/*
 *-----------------------------------------------------------------------------
 *  RLD Automation Copyright 1998 All Rights Reserved.
 *-----------------------------------------------------------------------------
 *
 * Module Name : fl_conv.c
 *
 * FactoryLink Task: FL_CONV
 *
 * Version/Rev : 1.00
 * Author/Date : Marcel Jordaan
 * Description : FactoryLink task.
 *
 *-----------------------------------------------------------------------------
 *
 *------------------------------  Revision Log  -------------------------------
 * Date     Time  Name    Function/Revision
 * -------- ----- ------- -----------------------------------------------------
 * 05/11/98 09:00 MJ      Creation
 *
 *
 *-----------------------------------------------------------------------------
 */

/*
 *-----------------------------------------------------------------------------
 *  Include files
 *-----------------------------------------------------------------------------
 */

#pragma warning( disable : 4996 )
#include "stdafx.h"

//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
//#include <ctype.h>
//#include <sys/timeb.h>
//#include <math.h>

//#include <flobj.h>

//#include "fl_conv.h"


/*
 *-----------------------------------------------------------------------------
 *  Global variables
 *-----------------------------------------------------------------------------
 */

MyFlTask          MyTask( TASK_NAME, TASK_DESC);
ConvertADRecord   *ADRecord = NULL;
ConvertDARecord   *DARecord = NULL;
ConvertAMHeader   *AMHeader = NULL;
FlTagList         *LastConvertDAOutput = NULL;


/*
 *-----------------------------------------------------------------------------
 *  Function bodies
 *-----------------------------------------------------------------------------
 */


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: int main( void)
 *
 * PUPRPOSE: Task main body
 *
 *-----------------------------------------------------------------------------
 */
int main( void)
{

  //initialise the FactoryLink part of the task
  if (MyTask.Init() != GOOD)
    return -1;

  //keep running untill we close down
  while (MyTask.Run() == GOOD)
    ;

  //do our things to clean up
  MyTask.Exit();

  return 0;
}


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void ADConversion( FlTag *tag, void *data)
 *
 * PUPRPOSE: Event function, Analog to Digital conversion
 *
 *-----------------------------------------------------------------------------
 */
void ADConversion( FlTag *tag, void *data)
{

  ConvertADRecord  *rec = (ConvertADRecord *)data;


  //do the conversion
  rec->Convert( tag);

  return;
} //ADConversion( FlTag *tag, void *data)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void DAConversion( FlTag *tag, void *data)
 *
 * PUPRPOSE: Event function, Digital to Analog conversion
 *
 *-----------------------------------------------------------------------------
 */
void DAConversion( FlTag *tag, void *data)
{

  ConvertDARecord  *rec = (ConvertDARecord *)data;


  //do the conversion
  rec->Convert( tag);

  return;
} //DAConversion( FlTag *tag, void *data)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void AMConversion( FlTag *tag, void *data)
 *
 * PUPRPOSE: Event function, Discrete to Text conversion
 *
 *-----------------------------------------------------------------------------
 */
void AMConversion( FlTag *tag, void *data)
{

  ConvertAMHeader  *rec = (ConvertAMHeader *)data;


  //do the conversion
  rec->Convert( tag);

  return;
} //ConvertAMHeader( FlTag *tag, void *data)



/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void  MyFlTask( char *Name, char *Desc)
 *
 * PUPRPOSE: Constructor.
 *
 *-----------------------------------------------------------------------------
 */
MyFlTask::MyFlTask( char *Name, char *Desc)
  : FlTask( Name, Desc)
{
  //do here our own stuff before calling the default constructor
}


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void  ctload(void)
 *
 * PUPRPOSE: Load task Configuration Table file.
 *
 *-----------------------------------------------------------------------------
 */
void MyFlTask::CTload()
{

  FlCTfile        CTfile;
  int             i;
  int             k;
  int             ct;
  int             fmt_cnt;
  char            *fmt_str;
  CT_AD_REC       *ad = NULL;
  CT_DA_REC       *da = NULL;
  CT_AM_HDR       *amh = NULL;
  CT_AM_REC       *amr = NULL;
  ConvertADRecord *CT_ADRecord = NULL;
  ConvertDARecord *CT_DARecord = NULL;
  ConvertAMHeader *CT_AMHeader = NULL;
  ConvertAMRecord *CT_AMRecord = NULL;


  //check for opened CT archive file
  if (!CTfile.IsOpen() ||!CTfile.NoTables())
  {

    //adjust message according to situation 
    if (!CTfile.NoTables())
      MyTask.RunTimeManager( FLS_ERROR, "NOTRIGGERS");
    else
      MyTask.RunTimeManager( FLS_ERROR, "NOCT");

    return;
  }

  //loop through the tables, processing each in turn
  for (ct = 0; ct < CTfile.NoTables(); ct++)
  {

    //read the index for this table
    if (CTfile.ReadIndex( ct) != GOOD)
      continue;

    //continue if there are no records
    if (!CTfile.NoRecords())
      continue;

    switch (CTfile.Type())
    {

      //parameter AD conversion
      case AD_CONV:

        //read all the records of the CT
        for (i = 0; i < CTfile.NoRecords(); i++)
        {

          //read the main AD record
          ad = (CT_AD_REC *)CTfile.ReadRecord( i);

          //allocate memory for the new object
          if (!(CT_ADRecord = new ConvertADRecord( ad, CT_ADRecord)))
            return;

          //get the first record in the list
          if (ADRecord == NULL)
            ADRecord = CT_ADRecord;

          //check tag dimension
          if (ad->Dim < 1) 
            ad->Dim = 1;

          if (ad->Dim > CT_ADRecord->In.Dimension( ad->In_name, ad->In_dim))
          {

            ad->Dim = CT_ADRecord->In.Dimension();
            RunTimeManager( FLS_ERROR, "TAGARRAY", ad->In_dim);
          }

          if (ad->Dim > CT_ADRecord->Out.Dimension( ad->Out_name, ad->Out_dim))
          {

            ad->Dim = CT_ADRecord->Out.Dimension();
            RunTimeManager( FLS_ERROR, "TAGARRAY", ad->Out_dim);
          }

          //add trigger: input tag
          MyTask.SetEvent( CT_ADRecord->In, ADConversion, CT_ADRecord);

          //run through all dimension tags
          for (k = 1; k < ad->Dim; k++)
          {

            ad->In.t_data++;
            ad->Out.t_data++;

            //allocate memory for the new object
            if (!(CT_ADRecord = new ConvertADRecord( ad, CT_ADRecord)))
              return;

            //add trigger: input tag
            MyTask.SetEvent( CT_ADRecord->In, ADConversion, CT_ADRecord);
          }
        }
        break;

      //parameter DA conversion
      case DA_CONV:

        //read all the records of the CT
        for (i = 0; i < CTfile.NoRecords(); i++)
        {

          //read the main DA record
          da = (CT_DA_REC *)CTfile.ReadRecord( i);

          //allocate memory for the new object
          if (!(CT_DARecord = new ConvertDARecord( da, CT_DARecord)))
            return;

          //get the first record in the list
          if (DARecord == NULL)
            DARecord = CT_DARecord;

          //check tag dimension
          if (da->Dim < 1)
            da->Dim = 1;

          if (da->Dim > CT_DARecord->In.Dimension( da->In_name, da->In_dim))
          {

            da->Dim = CT_DARecord->In.Dimension();
            RunTimeManager( FLS_ERROR, "TAGARRAY", ad->In_dim);
          }

          if (da->Dim > CT_DARecord->Out->Dimension( da->Out_name, da->Out_dim))
          {

            da->Dim = CT_DARecord->Out->Dimension();
            RunTimeManager( FLS_ERROR, "TAGARRAY", da->Out_dim);
          }

          //add trigger: input tag
          MyTask.SetEvent( CT_DARecord->In, DAConversion, CT_DARecord);

          //run through all dimension tags
          for (k = 1; k < da->Dim; k++)
          {

            da->In.t_data++;
            da->Out.t_data++;

            //allocate memory for the new object
            if (!(CT_DARecord = new ConvertDARecord( da, CT_DARecord)))
              return;

            //add trigger: input tag
            MyTask.SetEvent( CT_DARecord->In, DAConversion, CT_DARecord);
          }
        }
        break;

      //parameter AM conversion
      case AM_CONV:


        //read the main AM header
        amh = (CT_AM_HDR *)CTfile.ReadHeader();

        //allocate memory for the new object
        if (!(CT_AMHeader = new ConvertAMHeader( amh, CT_AMHeader)))
          return;

          //get the first record in the list
          if (AMHeader == NULL)
            AMHeader = CT_AMHeader;

        //check tag dimension
        if (amh->Dim < 1)
          amh->Dim = 1;

        if (amh->Dim > CT_AMHeader->In.Dimension( amh->In_name, amh->In_dim))
        {

          amh->Dim = CT_AMHeader->In.Dimension();
          RunTimeManager( FLS_ERROR, "TAGARRAY", amh->In_dim);
        }

        if (amh->Dim > CT_AMHeader->Out.Dimension( amh->Out_name, amh->Out_dim))
        {

          amh->Dim = CT_AMHeader->Out.Dimension();
          RunTimeManager( FLS_ERROR, "TAGARRAY", amh->Out_dim);
        }

        //read all the records of the CT
        for (k = 0, CT_AMRecord = NULL; k < CTfile.NoRecords(); k++)
        {

          //allocate the memory for the statics records
          if (!(CT_AMRecord = new ConvertAMRecord( (CT_AM_REC *)CTfile.ReadRecord( k), CT_AMRecord)))
            return;

          //chain the header and record
          if (!k)
            CT_AMHeader->SetList( CT_AMRecord);

          //check the format specifier string
          fmt_cnt = 0;
          fmt_str = CT_AMRecord->BufPointer();

          //count occurrence of format specifier
          while ((fmt_str = strchr( fmt_str, '%')) != NULL)
          {

             if (fmt_str[ 1] != '%')
               fmt_cnt++;
             else
               fmt_str = &fmt_str[ 1];

            fmt_str = &fmt_str[ 1];
          }

          if (fmt_cnt > 1)
          {

            RunTimeManager( FLS_ERROR, "CTFORMAT", CT_AMRecord->BufPointer());
            return;
          }
        }

        //retrieve a pointer to the first record
        CT_AMRecord = CT_AMHeader->GetList();

        //add trigger: input tag
        MyTask.SetEvent( CT_AMHeader->In, AMConversion, CT_AMHeader);

        //run through all dimension tags
        for (k = 1; k < amh->Dim; k++)
        {

          amh->In.t_data++;
          amh->Out.t_data++;

          //allocate memory for the new object
          if (!(CT_AMHeader = new ConvertAMHeader( amh, CT_AMHeader)))
            return;

          //add trigger: input tag
          CT_AMHeader->SetList( CT_AMRecord);
          MyTask.SetEvent( CT_AMHeader->In, AMConversion, CT_AMHeader);
        }
        break;
    }
  }
} //FlTask::CTload( void)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void FlTask::Starting( void)
 *
 * PURPOSE:  Every conversion is initialised
 *
 *-----------------------------------------------------------------------------
 */
void MyFlTask::Starting( void)
{

  ConvertADRecord *AD = ADRecord;
  ConvertDARecord *DA = DARecord;
  ConvertAMHeader *AM = AMHeader;


  fl_lock( FlTask::GetId());

  //AD converions
  if (AD)
  {

    //process all the headers
    do
    {

      //initialise all the records
      AD->Init();
    } while (AD = AD->Next());
  }

  //DA converions
  if (DA)
  {

    //process all the headers
    do
    {

      //initialise all the records
      DA->Init();
    } while (DA = DA->Next());
  }

  //AM converions
  if (AM)
  {

    //process all the headers
    do
    {

      //initialise all the records
      AM->Init();
    } while (AM = AM->Next());
  }

  fl_unlock( FlTask::GetId());

  return;
} //FlTask::Starting( void)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void FlTask::Stopping( void)
 *
 * PURPOSE:  Stopping actions for task
 *
 *-----------------------------------------------------------------------------
 */
void MyFlTask::Stopping( void)
{

  return;
} //FlTask::Stopping( void)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void ConvertADRecord::Convert( FlTag *input)
 *
 * PUPRPOSE: Convert the input value, AD conversion
 *
 *-----------------------------------------------------------------------------
 */
void ConvertADRecord::Convert( FlTag *input)
{

  u32     val;     //temporary value
  int     type = In.Type();


  //get the input value
  if (input)
  {

    val = *input;
    type = input->Type();
  }
  else
    val = In;

  //casting for digital tags
  if (type == FL_DIGITAL)
  {

    if (val)
      val = 0xffffffff;
  }

  //invert input value
  if (Invert) 
    val = ~val;

  //mask value
  val &= Mask;

  //mask ON
  if (Mon != (val & Mon))
    val = 0;

  //mask OFF
  if (val & Moff)
    val = 0;

  //adjust value for output tag type
  Out = val;

  //write output tag
  Out.Write();

  return;
} //ConvertADRecord::Convert( FlTag *input)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void ConvertADRecord::Init( void)
 *
 * PUPRPOSE: Initialisation for the AD object
 *
 *-----------------------------------------------------------------------------
 */
void ConvertADRecord::Init( void)
{

  //read the inout tag value
  In.Read();

  //do the first conversion
  Convert();

  return;
} //ConvertADRecord::Init( void)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void ConvertDARecord( CT_DA_REC *rec, ConvertDARecord *prev = NULL)
 *
 * PUPRPOSE: Constructor
 *
 *-----------------------------------------------------------------------------
 */
ConvertDARecord::ConvertDARecord( CT_DA_REC *rec, ConvertDARecord *prev)
                 : In( (rec) ? rec->In : BadTag)
{

  FlTagList    *Output;


  //get the inversion and bit number
  Invert = rec->Invert;
  Bit = rec->Bit;

  //linking of objects
  next = NULL;
  if (prev) prev->next = this;

  //check for existing output tag
  if (LastConvertDAOutput)
  {

    Output = LastConvertDAOutput->First();

    do
    {

      //output tag already defined
      if ((Output->Tag())->Compare( rec->Out))
      {

        Out = Output->Tag();
        return;
      }

    } while (Output = Output->Next());
  }
  
  //now we have a new output tag object, so create
  Out = new FlTag( rec->Out);
  LastConvertDAOutput = new FlTagList( Out, LastConvertDAOutput);

  //Don't loose the header of the list

} //ConvertDARecord::ConvertDARecord( CT_DA_REC *rec, ConvertDARecord *prev = NULL)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void ConvertDARecord::Convert( FlTag *input)
 *
 * PUPRPOSE: Convert the input value, AD conversion
 *
 *-----------------------------------------------------------------------------
 */
void ConvertDARecord::Convert( FlTag *input)
{

  u32     mask = 0x1; 
  u32     val  = 0;
  u32     temp;


  //get the input value
  if (input)
    val = *input;
  else
    val = In;

  //correct for digital
  if (val)
    val = 1L;
  else
    val = 0L;

  //invert input value
  if (Invert) 
  {

	  if (val)
      val = 0L;
    else
      val = 1L;
  }

  //shift value for output tag
  val <<= Bit;

  //update the output tag
  switch (Out->Type())
  {

    case FL_DIGITAL:

      if (val)
        *Out = 1;
      else
        *Out = 0;
      break;

    case FL_ANALOG:
    case FL_LANALOG:

      //get a copy of the value
      //Out->ReadNoClear();
      temp = *Out;

      //patch in the bit value
      mask <<= Bit;
      temp = temp & (~mask);
      temp |= val;
      *Out = temp;
      break;

    default:
      return;
  }

  //write output tag
  Out->ForcedWrite();
  //Out->Write();
 
  return;
} //ConvertDARecord::Convert( FlTag *input)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void ConvertDARecord::Init( void)
 *
 * PUPRPOSE: Initialisation for the DA object
 *
 *-----------------------------------------------------------------------------
 */
void ConvertDARecord::Init( void)
{

  //read the inout tag value
  In.Read();

  //do the first conversion
  Convert();

  return;
} //ConvertDARecord::Init( void)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void ConvertDARecord::Convert( FlTag *input)
 *
 * PUPRPOSE: Convert the input value, AM conversion
 *
 *-----------------------------------------------------------------------------
 */
void ConvertAMHeader::Convert( FlTag *input)
{

  FLP                 fl_val;
  FLP                 check;
  u16                 first = 1;
  char                no_text[] = "%f";
  char                temp[ 2*AM_LEN];
  char                *buf;
  u32                 val;
  ConvertAMRecord     *rec;
  TAG                 tag;


  /* 
   * Invert and mask are only allowed for digital, analog and longana tags 
   */
  switch (In.Type()) 
  {
    case FL_DIGITAL:

      //get the tag value
      if (input)
        val = *input;
      else
        val = In;

      //invert input value
      if (Invert)
      {
        
        if (val)
          val = 0;
        else
          val = 1;
      }

      //mask value
      val &= (Mask & 0x1);

      fl_val = val;
      break;
 
    case FL_ANALOG:
    case FL_LANALOG:

      //get the tag value
      if (input)
        val = *input;
      else
        val = In;

      //invert input value
      if (Invert)
        val = ~val;

      //mask value
      val &= Mask;

      fl_val = val;
      break;

    default:

      if (input)
        fl_val = *input;
      else
        fl_val = In;
      break;
  } //endswitch

  //find the output text
  buf = no_text;
  check = fl_val;
  rec = List;

  //loop through the value-text combinations
  while (rec)
  {

    if ((fl_val >= rec->Value()) && ((rec->Value() >= check) || first))
    {

      first = 0;
      buf = rec->BufPointer();
      check = rec->Value();
    }

    rec = rec->Next();
  }

  //build the message
  tag = In.Id();
  fl_dbfmtt( FlTask::GetId(), 2*AM_LEN, temp, buf, &tag);
  Out = temp;

  //write output tag
  Out.ForcedWrite();
  //Out.Write();

  return;
} //ConvertAMHeader::Convert( FlTag *input)


/*
 *-----------------------------------------------------------------------------
 * FUNCTION: void ConvertAMHeader::Init( void)
 *
 * PUPRPOSE: Initialisation for the AM object
 *
 *-----------------------------------------------------------------------------
 */
void ConvertAMHeader::Init( void)
{

  //read the inout tag value
  In.Read();

  //do the first conversion
  Convert();

  return;
} //ConvertAMHeader::Init( void)

