// MyFlTask.cpp: implementation of the MyFlTask class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "stdarg.h"
#include "MyFlTask.h"
#include "Registration.h"
#include "math.h"
#include "hklm.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//static varibales for MyFlTask
//error codes which change the runtime manager status to error
int MyFlTask::m_iRunTimeError[ MAX_ERROR];   
int MyFlTask::m_iPreviousState;


/////////////////////////////////////////////////////////////////////////////
// Description: Thread function for the 'read' and 'write' functionality of the
// driver. The thread receives commands from an IO-translator, the imx-library
// is used to receive these commands. The command is dispatched to the appropiate 
// function of the device: read or write. the imx-library delivers an imx-dataset 
// with the command, this datasets belongs to a device.
// 
// Parameter: *ImxDevice - Pointer to imx device structure, this strucuture is 
// defined in the imx-library. 
// Parameter: function - functionality of the thread: read (FNC_READ) or write
// (FNC_WRITE).
// 
// Returns: on exit of the thread zero is returned.
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ReadThreadFunction( IMX_DEVICE *ImxDevice, int function)
{

  CDeviceObject  *device = NULL;
  IMXDS          *ds = NULL;
  char           buf[ 2*sizeof( DEV_STATUS)];
  FLMSG          msgbuf;
  CDatasetObject *dataset;
  IMXSYSTEM      imxsys;


  //initialize the buffer for IMX
  msgbuf.m_ptr = buf;
  msgbuf.m_max = 2*sizeof( DEV_STATUS);

  //retrieve the pointer to the additional device information
  device = (CDeviceObject *)Imx_MT_Get_DevInfo( ImxDevice);

  //loop around forever and handle all IM-BAS read/write requests for this device
  while ( !Imx_MT_Get_Stop() )
  {

    //wait for a dataset read/write command
    if ( Imx_MT_Wait( ImxDevice, function, &ds ) != GOOD )
      break;

    //check first the type of the command, this should only be read or write
    switch (Imx_Get_Type( ds))
    {

      //read request, read data from device and send response to Io-xlator
      case IMX_READ_REQ:

        //retrieve the additional dataset information
        dataset = (CDatasetObject *)Imx_Get_Udata_Ptr( ds );

        //check dataset type
        if (dataset && (dataset->GetType() == 999))
        {

          device->GetDeviceStatus( buf+sizeof( IMXHDR));

          //do a prepare for retrieving registered values
          Imx_Ds_Prepare  ( ds, &msgbuf );
          Imx_Set_Type    ( ds, IMX_READ_RSP );

          //set the length of the received data
          Imx_Set_Start( ds, 0);
          Imx_Set_Len( ds, sizeof( DEV_STATUS) / Imx_Get_Boundary( ds));

          //send data to decoder
          LockRTDB( false);

          //fill imx structure with correct task id
          imxsys.task_id = GetId();

          //send imx message to translator
          Imx_Send( &imxsys, ds);
          ReleaseRTDB();
        }
        else
          device->Read( ds);

        break;

      //write request, send data to device and send response to Io-xlator
      case IMX_WRITE_REQ:

        //retrieve the additional dataset information
        dataset = (CDatasetObject *)Imx_Get_Udata_Ptr( ds );

        //check dataset type
        if (dataset && (dataset->GetType() == 999))
        {

          //reset total counters
          device->ResetTotalCounters();
        }
        else
          device->Write( ds);
        break;

      default: break;; //unkown and not processed command
    }
  }

  //thread ends
  return 0;
} //ReadThreadFunction


struct _recvdata
{

  SOCKET sock;
  SOCKADDR_IN m_wAddress;
  MyFlTask *theTask;
  CWinThread *m_Thread;
};

/////////////////////////////////////////////////////////////////////////////
// Descripton: Thread function for the 'receive' functionality of the driver.
// The parameters passed with this function identify a connection with a re-
// mote subsystem. We have to wait for the first alive sign before we can 
// identify the subsystem and search a corresponding devic eobject.
//
// Parameters: pParam - Array of two pointers, the first pointer points to 
// SOCKET variable, and the second pointer pojnts to an address structure. The
// memory fro the data structure is allocated prior to creation of the thread,
// this memory is not released by the thread that created this thread, before
// this function exits, the memory should be released.
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
unsigned int MyFlTask::ReceiveThreadFunction( LPVOID pParam)
{

  //the parameter is a pointer to a _recvdata strucuture, 
  //this strucuture includes a socket, address specification
  //and a pointer to the task object
  struct _recvdata *ConnData = (struct _recvdata *)pParam;
  char buffer[ IMX_BUFFER_SIZE]; //physical buffer for read/write operations
  char imxbuf[ IMX_BUFFER_SIZE]; //physical buffer for imx responses
  int m_iConnected = DEVICE_NOT_CONNECTED; //connection info
  CDeviceObject *device = NULL;
  int start = 0, length = 0;
  int error = 0;


  ConnData->theTask->CodeTrace( _T("THREADSTART"), 4, EVENTLOG_INFORMATION_TYPE, 
                              TRACE_TEXT, NULL, 0, ConnData->theTask->GetThreadDelay());

  //before we start the thread is delayed, no message are received
  Sleep( ConnData->theTask->GetThreadDelay());

  //clear input buffer, don't TM will not survive this action
  //ConnData->theTask->ImBasPurgeRX( ConnData->sock);

  //physical connectin is present, wait for life sign to establish 
  //connection on application level
  short standby = 0;
  while (m_iConnected != DEVICE_CONNECTED)
  {

    //the first lifesign is received without time restriction
    //this is not needed, cause the remote will disconnect
    int type = 0;
    if (!ConnData->theTask->ImBasRecvMessage( ConnData->sock, (char *)buffer, &type, device))
    {

      //check for life sign message
      if (type == DS_LIFESIGN) 
      {

        //find our device 
        CString DevId = ConnData->theTask->ImBasGetSystemId( buffer, PROT_LIFESIGN_MSG);

        //lock the device list, only this thread can search and update devices for
        //incoming connections
        ConnData->theTask->DeviceListLock();

        for (int i = 0; i < ConnData->theTask->DeviceCount(); i++)
        {

          //lifesign received, device present; we have a connection
          if (DevId == ConnData->theTask->GetDevice( i)->GetSubId())
          {

            //device is found
            device = ConnData->theTask->GetDevice( i);

            //do not accept a second connection on the device
            if (device->GetSocket() != INVALID_SOCKET) 
            {
            
              //if device is stored in array, remve it
              device->GetDriver()->RemoveReceiveSocket( device->GetSocket());

              //disconnect the present connection and proceed 
              device->Disconnect();
              //break; //breaking will save the previous connection
            }

            //stop processing lifesign if the driver is in standby mode
            if (standby = (short)device->GetDriver()->StandbyTag) break;

            device->SetSocket( ConnData->sock); 
            device->SetTime();

            if (!(error = device->theTask->ImBasSendMessage( device, (char *)buffer, type + 100)))
            {
              m_iConnected = DEVICE_CONNECTED;

              device->theTask->CodeTrace( _T("CONNECTION_PRESENT"), 3, EVENTLOG_INFORMATION_TYPE, 
                                          TRACE_TEXT, NULL, 0, device->GetName());

              device->SetReceiveThread( ConnData->m_Thread);
            }
            else
              device->theTask->CodeTrace( _T("DEVICE_TXERR"), 3, EVENTLOG_INFORMATION_TYPE, 
                                          TRACE_TEXT, NULL, 0, error);

            device->SetProtocolError( 0); //clear protocol errors

            //write status to device
            device->UpdateConnection( m_iConnected);

            //connect only one device
            break;
          }
        }

        //release device list
        ConnData->theTask->DeviceListRelease();
      }

      if (error) break; //on errors disconnect TCP/IP connection
      if (standby) continue; //standby mode, device will disconnect
    }

    //sleep if we have to continue
    if (m_iConnected != DEVICE_CONNECTED)
      //ConnData->theTask->FlSleep( 500);
    {

      int idx = -1;

      //code trace
      if (device)
      {

        device->theTask->CodeTrace( _T("TCP_DISCONNECT"), 3, EVENTLOG_INFORMATION_TYPE, 
                                    TRACE_TEXT, NULL, 0, device->GetName());

        //update socket count in main thread
        idx = ConnData->theTask->GetDriver()->FindReceiveSocket( device->GetSocket());

        //disconnect and exit thread
        device->Disconnect();
      }
      else
      {

        ConnData->theTask->CodeTrace( _T("THREAD_DISCONNECT"), 3, EVENTLOG_INFORMATION_TYPE, 
                                      TRACE_TEXT, NULL, 0);

        //disconnect and exit thread
        closesocket( ConnData->sock);

        //update socket count in main thread
        //idx = ConnData->theTask->GetDriver()->FindReceiveSocket( ConnData->sock);
      }

      //mark socket as invalid
      if (device) 
      {

        ConnData->theTask->DeviceListLock();
        device->SetSocket( INVALID_SOCKET); 
        device->SetProtocolError( 0); //clear protocol errors
        ConnData->theTask->DeviceListRelease();
        device->ClearReceiveThread();
      }

      //update socket count in main thread
      ConnData->theTask->GetDriver()->RemoveReceiveSocket( ConnData->sock);

      //exit the receive thread if the connection is lost
      //free the memory from the connection data
      delete pParam;
      
      return 0;
    }
  }

  //we have an application connection, receive data and process
  while (!ConnData->theTask->IsListenCanceled() && (m_iConnected == DEVICE_CONNECTED) && !Imx_MT_Get_Stop())
  {

    FLMSG reply;
    int type = 0;
    int DataError;
    device->SetProtocolError( 0);
    int error = ConnData->theTask->ImBasRecvMessage( device->GetSocket(), (char *)buffer, &type, device);
    int len = 0;
    int end = 0;

    reply.m_ptr = &imxbuf[ PROT_HEADER_LEN - sizeof( IMXHDR)];
    reply.m_max = IMX_BUFFER_SIZE - PROT_HEADER_LEN;
    reply.m_len = 0;

    //final check if somebody interfering with our connection...
    if (device->GetSocket() != ConnData->sock)
      error = ERR_SOCKET_HANDLE;

    if (device->GetCurrentImxDataset() == NULL)
    {

      //dataset is required for commands, but not for lifesigns or exceptions
      if ((type != DS_LIFESIGN) && (type != DS_EXCEPTION))
        error = ERR_IMXDS_INVALID;
    }
  
    //check if response is valid
    if (error)
    {

      //give rtm error message
      m_iConnected = DEVICE_DISCONNECTING;
      device->UpdateConnection( m_iConnected);

      //code trace
      device->theTask->CodeTrace( _T("TCP_DISCONNECT_ERR"), 3, EVENTLOG_INFORMATION_TYPE, 
                                  TRACE_TEXT, NULL, 0, device->GetName(), error);

      //update socket count in main thread
      //int idx = device->GetDriver()->FindReceiveSocket( device->GetSocket());
      int sock = device->GetSocket();

      //disconnect and exit thread
      device->Disconnect();

      //check for pending commands
      for (int i = 0; i < 2; i++)
        if (device->GetCmdPending( i))
        {

          //Imx error response, response is only end if a ds is present on the device
          if (device->GetCurrentImxDataset())
            device->theTask->ImxSendError( device->GetCurrentImxDataset(), 
                                           i, ERR_DEVICE_NOT_CONNECTED, device);

          //preserve imx dataset
          device->LockImxDataset();

          //release read request
          device->SetCmdPending( i, 0);

          //release lock on imx dataset
          device->ReleaseImxDataset();
        }

      ConnData->theTask->DeviceListLock();

      //update connection status
      m_iConnected = DEVICE_NOT_CONNECTED;
      device->UpdateConnection( m_iConnected);

      device->GetDriver()->RemoveReceiveSocket( sock);

      //mark socket as invalid
      device->SetSocket( INVALID_SOCKET);

      ConnData->theTask->DeviceListRelease();

      device->ClearReceiveThread();

      //exit the receive thread if the connection is lost
      //free the memory from the connection data
      delete pParam;

      return 0;
    }

    //the meber function GetCurrentImxDataset() of the 'device' will now always
    //return a valid ds, there is no need to check this again, if there is no 
    //read or write registered GetCmdPendig should return zero...

    //check if a valid message is received
    if (!type && device->GetProtocolerror())
    {

      //check for pending commands
      for (int i = 0; i < 2; i++)
        if (device->GetCmdPending( i))
        {

          //Imx error response
          if (device->GetCurrentImxDataset() != NULL)
            device->theTask->ImxSendError( device->GetCurrentImxDataset(), 
                                           i, device->GetProtocolerror(), device);

          //preserve imx dataset
          device->LockImxDataset();

          //release read request
          device->SetCmdPending( i, 0);

          //release lock on imx dataset
          device->ReleaseImxDataset();
        }

      device->SetProtocolError( 0); //clear protocol errors
      continue; //continue with while loop
    }

    //data lenght of message
    len = device->theTask->ImBasGetDataLength( buffer, PROT_HEADER_LEN);

    //code trace
    device->theTask->CodeTrace( _T("MESSAGE_RX"), 3, EVENTLOG_INFORMATION_TYPE, 
                                TRACE_TEXT, NULL, 0, device->GetName(), type);

    //preserve imx dataset
    device->LockImxDataset();

    //Imx_Set_Len( (device->GetCurrentImxDataset()), len);
    //if (device->GetCurrentImxDataset())
    //  device->theTask->CodeTrace( _T("%s, ds=%d, len=%d"), 3, EVENTLOG_INFORMATION_TYPE, 
    //                              TRACE_TEXT, NULL, 0, device->GetName(), Imx_Get_Len( (device->GetCurrentImxDataset())), len);

    //handle the message
    switch (type)
    {

      case DS_LIFESIGN: //life sign

        //only reply when in normal mode
        if ((short)device->GetDriver()->StandbyTag) break;

        //check time between two conscutive lifesigns
        if (device->theTask->UseLifesignTimeout())
        {

          //check the timeout interval
          if (device->GetTime( true) > (device->GetAliveTime() + device->GetAliveTime()/5))
          {

            //update status tag
            device->UpdateStatus( ERR_LIFESIGN_TIMEOUT);

            //update runtime manager
            device->theTask->MyRunTimeManager( ERR_LIFESIGN_TIMEOUT, FLS_ERROR, _T("LIFESIGN_TIMEOUT"), 
                                               device->GetName(), device->GetAliveTime());

            //mark device as disconnecting, now we get out of the while loop
            if (device->theTask->DisconnectLifesignLoss())
              m_iConnected = DEVICE_DISCONNECTING;

            //update device status
            device->UpdateConnection( m_iConnected);
          }
        }

        //update receive time
        device->SetTime();

        //send reply
        error = device->theTask->ImBasSendMessage( device, NULL, type + 100);
        break;    

      case DS_COMMAND_BOOL:

        //check for pending write
        if (device->GetCmdPending( FNC_WRITE))
        {

          //check for limit errors, and reply to translator
          if (((CDatasetObject *)Imx_Get_Udata_Ptr( device->GetCurrentImxDataset()))->GetErrorOnLimit())
            device->theTask->ImxSendError( device->GetCurrentImxDataset(), FNC_WRITE, ERR_LIMIT_SET, device);
          else //reply to translator
            device->theTask->ImxSend( device->GetCurrentImxDataset(), IMX_WRITE_RSP, device);

          //mark write ready, release semaphore
          device->SetCmdPending( FNC_WRITE, 0);
        }
        else
        {
         
          //update runtime manger
          MyRunTimeManager( ERR_UNEXPECTED_MSG, FLS_ERROR, _T("DEV_UNEXPECTEDMSG"), device->GetName(), type);

          //unexpected message, error on device
          device->UpdateStatus( ERR_UNEXPECTED_MSG); 
        }
        break;

      case DS_PARAMETER_BOOL:

        if ((device->GetCmdPending( FNC_READ)) || (device->GetCmdPending( FNC_WRITE)))
        {

          //get the dataset definition and calculate the number of packets
          CDatasetObject *dataset = (CDatasetObject *)Imx_Get_Udata_Ptr( device->GetCurrentImxDataset());

          int rep = device->theTask->GetPacketCount( Imx_Get_Len( device->GetCurrentImxDataset()), dataset->GetType());
          //int dlen = 0;

          //first packet initialise start, size and data area          
          if (!device->GetResponseCount())
          {

            //reset start and length of dataset
            start = Imx_Get_Start( device->GetCurrentImxDataset());
            length = 0;

            //initialize the imx data to their default values
            device->theTask->ImBasInitImxData( imxbuf, IMX_BUFFER_SIZE - PROT_HEADER_LEN,
                                               device->theTask->ImBasGetDataType( buffer, IMX_BUFFER_SIZE));
          }

          //convert data to imx data layout
          DataError = device->theTask->ImBasData2Imx( imxbuf, buffer, device->GetCurrentImxDataset(),
                                                      start, &length);

          //save conversion and interpretation errors
          if (DataError) device->SetProtocolError( DataError);

          //update start and length of the received data, this start and length
          //is used for the imx dataset
//          if (!device->GetResponseCount())
//          {

//            imxstart = start;
//            imxlength = length;
//          }
//          else 
//          {

            //calculate end address
//            end = ((imxstart + imxlength) - (start + length)) > 0 ? (imxstart + imxlength): (start + length);

//            imxstart = start < imxstart ? start : imxstart;
//            imxlength = end - imxstart;
//          }

          if (device->GetResponseCount( 1) >= rep)
          {

            //prepare for retrieving registered values
            Imx_Ds_Prepare( device->GetCurrentImxDataset(), &reply);

            Imx_Set_Start( device->GetCurrentImxDataset(), start);
            if (Imx_Get_Len( device->GetCurrentImxDataset()) < length)
              Imx_Set_Len( device->GetCurrentImxDataset(), length);

            if (device->GetCmdPending( FNC_READ))
            {

              //reply to translator
              if (device->GetProtocolerror())
                device->theTask->ImxSendError( device->GetCurrentImxDataset(), FNC_READ, device->GetProtocolerror(), device);
              else
                device->theTask->ImxSend( device->GetCurrentImxDataset(), IMX_READ_RSP, device);
            }
            else
            {

              int myL = len > 0 ? 20: 0;
              int myX = device->theTask->ImBasGetDataCount( buffer, myL);
              device->theTask->CodeTrace( _T("%s, ds=%d, len=%d"), 3, EVENTLOG_INFORMATION_TYPE, 
                                  TRACE_TEXT, NULL, 0, device->GetName(), Imx_Get_Len( (device->GetCurrentImxDataset())), myX);

              myL = Imx_Get_Len( device->GetCurrentImxDataset());
              Imx_Set_Len( device->GetCurrentImxDataset(), myX);

              //send unsolicited data to IO-translator              
              device->theTask->ImxSend( device->GetCurrentImxDataset(), IMX_RCV_RDY, device, true);
              Imx_Set_Len( device->GetCurrentImxDataset(), myL);

              //check for limit errors, and reply to translator
              if (((CDatasetObject *)Imx_Get_Udata_Ptr( device->GetCurrentImxDataset()))->GetErrorOnLimit())
                device->SetProtocolError( ERR_LIMIT_SET);

              if (device->GetProtocolerror())
                device->theTask->ImxSendError( device->GetCurrentImxDataset(), FNC_WRITE, device->GetProtocolerror(), device);
              else //reply to translator
                device->theTask->ImxSend( device->GetCurrentImxDataset(), IMX_WRITE_RSP, device);
            }

            //mark write ready, release semaphore
            if (device->GetCmdPending( FNC_READ)) device->SetCmdPending( FNC_READ, 0);
            else device->SetCmdPending( FNC_WRITE, 0);
          }
        }
        else
        {
         
          //update runtime manger
          MyRunTimeManager( ERR_UNEXPECTED_MSG, FLS_ERROR, _T("DEV_UNEXPECTEDMSG"), device->GetName(), type);

          device->UpdateStatus( ERR_UNEXPECTED_MSG); //unexpected message, error on device
        }
        break;

      case DS_STATUS_BOOL:
      case DS_STATINFO_BOOL:

        if (device->GetCmdPending( FNC_READ))
        {

          //get the dataset definition and calculate th enumber of packets
          CDatasetObject *dataset = (CDatasetObject *)Imx_Get_Udata_Ptr( device->GetCurrentImxDataset());

          int rep = device->theTask->GetPacketCount( Imx_Get_Len( device->GetCurrentImxDataset()), dataset->GetType());
          //int dlen = 0;

          //first packet initialise start, size and data area          
          if (!device->GetResponseCount())
          {

            //reset start and length of dataset
            start = Imx_Get_Start( device->GetCurrentImxDataset());
            length = 0;

            //initialize the imx data to their default values
            device->theTask->ImBasInitImxData( imxbuf, IMX_BUFFER_SIZE - PROT_HEADER_LEN,
                                               device->theTask->ImBasGetDataType( buffer, IMX_BUFFER_SIZE));
          }

          //convert data to imx data layout
          DataError = device->theTask->ImBasData2Imx( imxbuf, buffer, device->GetCurrentImxDataset(), start, &length);

          //save conversion and interpretation errors
          if (DataError) device->SetProtocolError( DataError);

          //update start and length of the received data, this start and length
          //is used for the imx dataset
          //if (!device->GetResponseCount()) imxstart = start;
          //else imxstart = start < imxstart ? start : imxstart;
          //imxlength += length; //length always increases

          if (device->GetResponseCount( 1) >= rep)
          {

            //prepare for retrieving registered values
            Imx_Ds_Prepare( device->GetCurrentImxDataset(), &reply);

            Imx_Set_Start( device->GetCurrentImxDataset(), start);
            if (Imx_Get_Len( device->GetCurrentImxDataset()) < length)
              Imx_Set_Len( device->GetCurrentImxDataset(), length);

            //reply to translator
            if (device->GetProtocolerror())
              device->theTask->ImxSendError( device->GetCurrentImxDataset(), FNC_READ, device->GetProtocolerror(), device);
            else
              device->theTask->ImxSend( device->GetCurrentImxDataset(), IMX_READ_RSP, device);

            //mark write ready, release semaphore
            device->SetCmdPending( FNC_READ, 0);
          }
        }
        else
        {
         
          //update runtime manger
          MyRunTimeManager( ERR_UNEXPECTED_MSG, FLS_ERROR, _T("DEV_UNEXPECTEDMSG"), device->GetName(), type);

          device->UpdateStatus( ERR_UNEXPECTED_MSG); //unexpected message, error on device
        }
        break;

      case DS_EXCEPTION:
      {
        device->SetReplySequence( device->theTask->ImBasGetSequence( buffer, PROT_HEADER_LEN));

        //exception message received, find dataset
        for (int i = 0, sendonce = 0; i < device->GetDatasetCount(); i++)
        {

          //reply to exception datasets
          if (device->GetDataSet( i)->GetType() == DS_EXCEPTION)
          {

            reply.m_ptr = &buffer[ PROT_HEADER_LEN - sizeof( IMXHDR)];

            //convert from UTC to local time, time is asumed to be of the 
            //form: hhmmss, that is six bytes.
            //conversion depends on the time format of the device
            if (!device->GetTimeFormat()) 
              device->theTask->ImBasUtc2Local( &buffer[ PROT_HEADER_LEN]);
            else
              device->theTask->ImBasLocal2Utc( &buffer[ PROT_HEADER_LEN]);

            CString tmp = _T("");
            int offset = device->theTask->GetExceptionSize( 0);

            //strip spaces from equipment
            for (int k = 0; k < device->theTask->GetExceptionSize( 1); k++)
                tmp.Insert( k+1, buffer[ PROT_HEADER_LEN + k + offset]);

            if (device->theTask->GetSpaceTrimLeft())tmp.TrimLeft();
            if (device->theTask->GetSpaceTrimRight()) tmp.TrimRight();

            //clear data buffer
            memset( &buffer[ PROT_HEADER_LEN + offset], 0, device->theTask->GetExceptionSize( 1));
            for (k = 0; k < tmp.GetLength(); k++)
              buffer[ PROT_HEADER_LEN + k + offset] = tmp.GetAt( k);

            tmp.Empty();

            //strip space from description
            offset = device->theTask->GetExceptionSize( 0) + device->theTask->GetExceptionSize( 1) + device->theTask->GetExceptionSize( 2);
            for (k = 0; k < device->theTask->GetExceptionSize( 3); k++)
                tmp.Insert( k+1, buffer[ PROT_HEADER_LEN + k + offset]);

            if (device->theTask->GetSpaceTrimLeft())tmp.TrimLeft();
            if (device->theTask->GetSpaceTrimRight()) tmp.TrimRight();

            //clear data buffer
            memset( &buffer[ PROT_HEADER_LEN + offset], 0, device->theTask->GetExceptionSize( 1));
            for (k = 0; k < tmp.GetLength(); k++)
              buffer[ PROT_HEADER_LEN + k + offset] = tmp.GetAt( k);

            //prepare for retrieving registered values
            Imx_Ds_Prepare( device->GetDataSet( i)->GetImxDataset(), &reply);

            Imx_Set_Start( device->GetDataSet( i)->GetImxDataset(), 0);
            Imx_Set_Len( device->GetDataSet( i)->GetImxDataset(), 
                         device->theTask->ImBasGetExceptionLength());

            //reply to translator
            device->theTask->ImxSend( device->GetDataSet( i)->GetImxDataset(), IMX_RCV_RDY, device);
            //reply.m_ptr = &imxbuf[ PROT_HEADER_LEN - sizeof( IMXHDR)];

            //send only one reply to TM
            if (sendonce == 0)
            {

              //send reply message to TM
              error = device->theTask->ImBasSendMessage( device, (char *)buffer, DSW_EXCEPTION);

              //sending failed, send error and completion to IOX
              if (error)
                device->theTask->MyRunTimeManager( error, FLS_ERROR, _T("DEVICE_READ_ERROR"), 
                                                   (char *)((LPCTSTR)device->GetName()), 
                                                   (char *)((LPCTSTR)device->GetDataSet( i)->GetName()),
                                                   error);
              sendonce = 1;
            }
          }
        }
        break;
      }
      case DSW_COMMAND_BOOL: //command confirmation
      case DSW_COMMAND_INT: //command confirmation
      case DSW_COMMAND_LONG: //command confirmation
      case DSW_COMMAND_REAL: //command confirmation
      case DSW_COMMAND_CHAR: //command confirmation

        //send reply
        error = device->theTask->ImBasSendMessage( device, (char *)buffer, type + 100);

        //check for pending write
        if (device->GetCmdPending( FNC_WRITE))
        {

          //check for limit errors, and reply to translator
          if (((CDatasetObject *)Imx_Get_Udata_Ptr( device->GetCurrentImxDataset()))->GetErrorOnLimit())
            device->theTask->ImxSendError( device->GetCurrentImxDataset(), FNC_WRITE, ERR_LIMIT_SET, device);
          else //reply to translator
            device->theTask->ImxSend( device->GetCurrentImxDataset(), IMX_WRITE_RSP, device);

          //mark write ready, release semaphore
          device->SetCmdPending( FNC_WRITE, 0);
        }
        else
        {
         
          //update runtime manger
          MyRunTimeManager( ERR_UNEXPECTED_MSG, FLS_ERROR, _T("DEV_UNEXPECTEDMSG"), device->GetName(), type);

          device->UpdateStatus( ERR_UNEXPECTED_MSG); //unexpected message, error on device
        }
        break;
    } //end if switch (type) 

    //release use of imx dataset
    device->ReleaseImxDataset();
  } //end of while (!ConnData->theTask->IsListenCanceled() && (m_iConnected == DEVICE_CONNECTED))

  //mark device as disconnecting
  m_iConnected = DEVICE_DISCONNECTING;
  device->UpdateConnection( m_iConnected);

  //code trace
  device->theTask->CodeTrace( _T("TCP_DISCONNECT"), 3, EVENTLOG_INFORMATION_TYPE, 
                              TRACE_TEXT, NULL, 0, device->GetName());

  //update socket count in main thread
  //int idx = device->GetDriver()->FindReceiveSocket( device->GetSocket());
  SOCKET sock = device->GetSocket();

  //disconnect and exit thread
  device->Disconnect();

  //check for pending commands
  for (int i = 0; i < 2; i++)
    if (device->GetCmdPending( i))
    {

      //Imx error response
      device->theTask->ImxSendError( device->GetCurrentImxDataset(), 
                                     i, ERR_DEVICE_NOT_CONNECTED, device);

      //preserve imx dataset
      device->LockImxDataset();

      //release read request
      device->SetCmdPending( i, 0);

      //release lock on imx dataset
      device->ReleaseImxDataset();
    }

  ConnData->theTask->DeviceListLock();

  m_iConnected = DEVICE_NOT_CONNECTED;
  device->UpdateConnection( m_iConnected);

  //reset errors on this device
  device->SetProtocolError( 0);

  ConnData->theTask->DeviceListRelease();

  device->GetDriver()->RemoveReceiveSocket( sock);

  //mark socket as invalid
  device->SetSocket( INVALID_SOCKET);

  device->ClearReceiveThread();

  //free the memory from the connection data
  delete pParam;

  return 0;
} //ReceiveThreadFunction


/////////////////////////////////////////////////////////////////////////////
// Descripton: Listen thread for accepting requests.
// 
// Parameters: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
unsigned int MyFlTask::MyListenThread( LPVOID pParam)
{

  CDriverObject *driver = (CDriverObject *)pParam;
  int error = 0;
  SOCKADDR_IN m_wAddress;

  //fill in local address
  m_wAddress.sin_family = AF_INET;

  //no local address specified ,all addresses qualify
  m_wAddress.sin_addr.s_addr = INADDR_ANY;

  //local address is specified on the driver object, first check
  //if the specified address is a valid TCP/IP address, if not
  //assume a name
  if (driver->GetIP().GetLength())
  {


    //check ip-address for punctuation
    if ((m_wAddress.sin_addr.s_addr = inet_addr( driver->GetIP())) == INADDR_NONE)
    {

      struct HOSTENT *haddr = (HOSTENT *)gethostbyname( driver->GetIP());

      //get ip-address from hostname
      if (!(gethostbyname( driver->GetIP())))
        m_wAddress.sin_addr.s_addr = INADDR_ANY;
      else
      {

        //hostname found, assume it is our name so we can use the IP-addres
        //m_wAddress.sin_addr.s_addr = INADDR_ANY;

        m_wAddress.sin_addr.s_addr = *((unsigned long *)gethostbyname( driver->GetIP())->h_addr);
      }
    }
  }

  //fill in the local port number
  m_wAddress.sin_port = htons( driver->GetLocalPort());

  //show the used network address inet_ntoa
  driver->theTask->CodeTrace( _T("TCP_ADDRESS"), 3, EVENTLOG_INFORMATION_TYPE, 
                              TRACE_TEXT, NULL, 0, inet_ntoa( m_wAddress.sin_addr), 
                              driver->GetLocalPort());

  //start with creation of a socket
  error = driver->CreateListenSocket();

  //bind the socket to the local port
  if (!error)
    error = bind( driver->GetListenSocket(), 
                  (SOCKADDR *)&m_wAddress, 
                  sizeof( m_wAddress));

  //thread is active untill a shutdown is received
  while (true)
  {

    //check if we still accept connections
    if ((short)driver->DisconnectTag)
    {

      //close socket, and rejected incoming requests
      driver->theTask->FlSleep( 100);
      continue;
    }

    //error handling
    if (error)
    {

      //check socket
      error = WSAGetLastError();

      if (driver->theTask->IsListenCanceled()) break;

      //RTM shows the error code
      MyRunTimeManager( error, FLS_ERROR, _T("LISTEN_ERROR"), error);

      //there is an error, wait for some time
      driver->theTask->FlSleep( 500);

      //start with creation of a socket
      error = driver->CreateListenSocket();

      //bind the socket to the local port
      if (!error)
        error = bind( driver->GetListenSocket(), 
                      (SOCKADDR *)&m_wAddress, 
                      sizeof( m_wAddress));
    }

    //set out a listen
    if (error = listen( driver->GetListenSocket(), SOMAXCONN)) continue;

    //save the socket and address i a structure and pass the variable to the thread
    struct _recvdata *tmp = new struct _recvdata;

    //continue if memory allocation failed
    if (!tmp) continue;

    //wait for incoming connection requests
    tmp->sock = accept( driver->GetListenSocket(), (struct sockaddr *)&tmp->m_wAddress, NULL);

    if (tmp->sock == INVALID_SOCKET)
    {
      error = -1;
      delete tmp;
      continue;
    }
    
    //check if we still accept connections
    if ((short)driver->DisconnectTag)
    {

      //close socket, and rejected incoming requests
      closesocket( tmp->sock);
      delete tmp;
      continue;
    }

    driver->theTask->CodeTrace( _T("CONNECTION_ACCEPTED"), 3, EVENTLOG_INFORMATION_TYPE, 
                                TRACE_TEXT, NULL, 0, driver->GetLocalPort(), tmp->sock);

    //set the task pointer
    tmp->theTask = driver->theTask;

    //evaluate the connection and start a receive thread
    CWinThread *m_RxThread = new CWinThread( ReceiveThreadFunction, (void *)tmp);

    if (m_RxThread == NULL)
    {

      delete tmp;
      error = ERR_RX_THREAD_CREATION;
      continue;
    }

    //auto clean up
    m_RxThread->m_bAutoDelete = true;

    //register socket in array
    //int idx = driver->AddReceiveSocket( tmp->sock);
    driver->AddReceiveSocket( tmp->sock);
    tmp->m_Thread = m_RxThread;

    //start the thread
    if (!m_RxThread->CreateThread()) driver->RemoveReceiveSocket( tmp->sock);;
  }

  //main thread has requested a stop, the main thread will shutdown
  return 0;
} //MyListenThread


/////////////////////////////////////////////////////////////////////////////
// Description: The 'EmptyEvent' funtion is an event function for FactoryLink,
// however no action is done. Basicly the standard event function stores the
// new tag value in the tag object, and that's all we need.
// 
// Parameter: *tag - Pointer to a tag object, this is the tag object with 
// which the event function is registerd.
// Parameter: *data - pointer to a user data structure. User is responsible 
// for the structure, the pointer is tied to the event by registering the tag, 
// event and data pointer.
//
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void EmptyEvent( FlTag *tag, void *data)
{

  CDriverObject *driver = (CDriverObject *)data;


  //empty function, after this function call, the tag object is updated with
  //the new value. This value is read by processing the event, here no action 
  //is needed.
  if (driver)
    driver->theTask->CodeTrace( _T("TAG_EVENT"), 4, EVENTLOG_INFORMATION_TYPE, 
                                TRACE_TEXT, NULL, 0, tag->Id().t_type, 
                                tag->Id().t_data);
} //EmptyEvent


/////////////////////////////////////////////////////////////////////////////
// Description: This event function handles the disconnect request from the 
// FactoryLink kernel: a digital or analog tag. If the value changes from zero
// to non-zero all connections to susbsystems are closed, and new incoming 
// connection requests are rejected during the non-zero value of the tag.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void DisconnectEvent( FlTag *tag, void *data)
{

  CDriverObject *driver = (CDriverObject *)data;


  if (driver)
    driver->theTask->CodeTrace( _T("DISCONNECT_EVENT"), 4, EVENTLOG_INFORMATION_TYPE, 
                                TRACE_TEXT, NULL, 0, (short)tag->Value().ana);

  //tag vlaue is ON: disconnect all devices
  if ((short)tag->Value().ana)
  {

    // do not wait until all receive threads have ended
    if (driver) driver->theTask->CancelReceiving( false);
  }
} //DisconnectEvent


/////////////////////////////////////////////////////////////////////////////
// Descripton: 
// 
// Parameters: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void ImxEvent( FlTag *tag, void *data)
{

  struct _imxdata *idat = (struct _imxdata *)data;


  //call the function
  idat->imx_func( idat->data, tag->Value());
 
}


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// MyFlTask::MyFlTask( char *Name, char *Desc)
// 
// 
//////////////////////////////////////////////////////////////////////
MyFlTask::MyFlTask( char *Name, char *Desc)
  : FlTask( Name, Desc)
{

  //clear Imx variables
  memset( (char *)&m_sImxSystem, 0, sizeof( IMXSYSTEM));

  m_ListenThread = NULL;
  m_bStopListening = false;

  //default protocol version
  m_sProtocolVersion = _T("02");

  //defualt number of IOX messages in mbx
  m_iIoxMaxMbxMsg = 100;

  //default ini settings
  m_sDestination = _T("BC");
  m_sSource = _T("IM");
  m_iTerminator = 10;
  m_sDelimiter = ";";
  m_sLifeSeq = _T("00");
  m_iExceptionSize[ 0] = 6;
  m_iExceptionSize[ 1] = 15;
  m_iExceptionSize[ 2] = 1;
  m_iExceptionSize[ 3] =50;
  m_iExcLocalTime = 0;

  m_iAcceptIllegalException = 0;
  m_iResponseTimeout = 5000;

  m_sIdLifeSign = _T("00");     //00
  m_sIdCommand = _T("01");      //01
  m_sIdParameter = _T("02");    //02
  m_sIdSetParameter = _T("03"); //03
  m_sIdStatus = _T("11");       //11
  m_sIdStatistical = _T("12");  //12
  m_sIdException = _T("21");    //21

  //space trimming is disabled by default
  m_iSpaceTrimLeft = 0;
  m_iSpaceTrimRight = 0;

  //non existent defualt value
  m_iNonExistentValue = 0;

  //no debugging of reals
  m_iDebugInfoForReals = 0;
  m_iThreadDelaySec = 0; //no delay
  m_iFirstLifeSign = 30;

  //no error on setting a limit
  m_iErrorOnLimit = 0;

  //create a semaphore for exclusive usae of deice list
  m_semDeviceList = CreateSemaphore( NULL, 1, 1, NULL);

  for( int i = 0; i < MAX_ERROR; i++)
    m_iRunTimeError[ i] = 1;

  m_iPreviousState = -1;
}


//////////////////////////////////////////////////////////////////////
// void MyFlTask::ExitApplication()
// 
// Something serious went wrong......, just exit application right now.
//////////////////////////////////////////////////////////////////////
void MyFlTask::ExitApplication( char *Xlate, ...)
{

  va_list      vl;


  //get the argument pointer
  va_start( vl, Xlate);

  //execute the FL utility function to write the RTM tags
  if (Xlate) MyRunTimeManager( -1, FLS_ERROR, Xlate, va_arg( vl, char *));

  va_end( vl);

  //continue with normal stopping of the task
  Stopping();

  //all things for FL done, quit now
  exit(-1);
}

// Take each desktop and add it to the listbox.
//BOOL CALLBACK MyDesktopEnumProc(LPTSTR name, LPARAM listbox)
//{
//	HWND hwndList = (HWND)listbox;
//	SendMessage(hwndList, LB_ADDSTRING, 0,
//		reinterpret_cast<LPARAM>(name));
//	return TRUE;
//}

//////////////////////////////////////////////////////////////////////
// void MyFlTask::Registration()
// 
// Registraiton window if task is not registered
//////////////////////////////////////////////////////////////////////
void MyFlTask::Registration()
{
#ifdef MyLicensActive

  HDESK hdesk = OpenDesktop( "Default", 0, FALSE, DESKTOP_SWITCHDESKTOP);

  if (hdesk != NULL)
  {
  
    //open registration box only for the default desktop 
    CRegistration dlg( TASK_NUMBER);

    //check if applicaiton is registrated
    if (!dlg.IsRegistered()) 
    {

      //switch always to the default desktop
      if (SwitchDesktop(hdesk))
      {

        //show registration window
        dlg.DoModal();

        //SwitchDesktop(hprev);
        CloseDesktop(hdesk);
      }
    }
  }


/*

DWORD dwGuiThreadId = 0; 
 
int 
UserMessageBox( 
    RPC_BINDING_HANDLE h, 
    LPSTR lpszWindowStation, 
    LPSTR lpszDesktop, 
    LPSTR lpszText, 
    LPSTR lpszTitle, 
    UINT fuStyle) 
{ 
        DWORD dwThreadId; 
    HWINSTA hwinstaSave; 
    HDESK hdeskSave; 
    HWINSTA hwinstaUser; 
    HDESK hdeskUser; 
    HWND hwndOwner
    int result; 
 
    // Ensure connection to service window station and desktop, and 
    // save their handles. 

//    hwndOwner = GetDesktopWindow(); 
    hwinstaSave = GetProcessWindowStation(); 
    dwThreadId = GetCurrentThreadId(); 
    hdeskSave = GetThreadDesktop(dwThreadId); 
 
    // Impersonate the client and connect to the User's 
    // window station and desktop. 

    RpcImpersonateClient(h); 
    hwinstaUser = OpenWindowStation(lpszWindowStation, FALSE, MAXIMUM_ALLOWED); 
    if (hwinstaUser == NULL) 
    { 
        RpcRevertToSelf(); 
        return 0; 
    } 
    SetProcessWindowStation(hwinstaUser); 
    hdeskUser = OpenDesktop(lpszDesktop, 0, FALSE, MAXIMUM_ALLOWED); 
    RpcRevertToSelf(); 
    if (hdeskUser == NULL) 
    { 
        SetProcessWindowStation(hwinstaSave); 
        CloseWindowStation(hwinstaUser); 
        return 0; 
    } 
    SetThreadDesktop(hdeskUser); 
 
    // Display message box. 

    dwGuiThreadId = dwThreadId; 
    result = MessageBox(NULL, lpszText, lpszTitle, fuStyle); 
    dwGuiThreadId = 0; 
 
    // Restore window station and desktop. 

    SetThreadDesktop(hdeskSave); 
    SetProcessWindowStation(hwinstaSave); 
    CloseDesktop(hdeskUser); 
    CloseWindowStation(hwinstaUser); 
 
    return result; 
} 

*/
#endif
}

  
//////////////////////////////////////////////////////////////////////
// void MyFlTask::ReadSettings()
// 
// Read settings from the task ini file and store the values in the task
// object.
//////////////////////////////////////////////////////////////////////
BOOL MyFlTask::ReadSettings()
{

  CString sSection(_T("Debug"));


  MyRunTimeManager( 0, FLS_STARTING, "INILOAD", GetIniFileName());

  //read the maximum nuber of messages for translaotro mailboxes
  sSection = _T("Translator");
  m_iIoxMaxMbxMsg = GetPrivateProfileInteger( sSection, _T("MaxMessage"), 100);

  if (GetPrivateProfileInteger( sSection, _T("UseBlackBox"), 1) != 0) 
    hLibBlackBox = LoadLibrary( "BlackBox.dll");
  else
    hLibBlackBox = NULL;
  
  //go to next section
  sSection = _T("Protocol");

  //load source and destination id's
  m_sSource = GetPrivateProfileString( sSection, _T("Source"), _T("IM"));
  if (m_sSource.GetLength() < 2) m_sSource = _T("IM");
  if (m_sSource.GetLength() > 2) m_sSource = m_sSource.Left( 2);

  m_sDestination = GetPrivateProfileString( sSection, _T("Destination"), _T("BC"));
  if (m_sDestination.GetLength() < 2) m_sDestination = _T("BC");
  if (m_sDestination.GetLength() > 2) m_sDestination = m_sDestination.Left( 2);

  //load termintor character
  m_iTerminator = GetPrivateProfileInteger( sSection, _T("Terminator"), 10);

  //load delimiter character
  m_sDelimiter = GetPrivateProfileString( sSection, _T("Delimiter"), _T(";"));
  if (m_sDelimiter.GetLength() != 2) m_sDelimiter = _T(";");

  //load version
  m_sProtocolVersion = GetPrivateProfileString( sSection, _T("Version"), _T("02"));
  if (m_sProtocolVersion.GetLength() < 2) m_sProtocolVersion = _T("02");
  if (m_sProtocolVersion.GetLength() > 2) m_sProtocolVersion = m_sProtocolVersion.Left( 2);

  //load sequence number for life signs
  m_sLifeSeq = GetPrivateProfileString( sSection, _T("LifeSeq"), _T("00"));
  if (m_sLifeSeq.GetLength() < 2) m_sLifeSeq = _T("00");
  if (m_sLifeSeq.GetLength() > 2) m_sLifeSeq = m_sLifeSeq.Left( 2);

  //load settings for exception message
  m_iExceptionSize[ 0] = GetPrivateProfileInteger( sSection, _T("ExceptionByteLength0"), 6);
  m_iExceptionSize[ 1] = GetPrivateProfileInteger( sSection, _T("ExceptionByteLength1"), 15);
  m_iExceptionSize[ 2] = GetPrivateProfileInteger( sSection, _T("ExceptionByteLength2"), 1);
  m_iExceptionSize[ 3] = GetPrivateProfileInteger( sSection, _T("ExceptionByteLength3"), 50);
  m_iExceptionSize[ 4] = GetPrivateProfileInteger( sSection, _T("ExceptionByteLength4"), 0);
  m_iExceptionSize[ 5] = GetPrivateProfileInteger( sSection, _T("ExceptionByteLength5"), 0);
  m_iExceptionSize[ 6] = GetPrivateProfileInteger( sSection, _T("ExceptionByteLength6"), 0);
  m_iExceptionSize[ 7] = GetPrivateProfileInteger( sSection, _T("ExceptionByteLength7"), 0);
  m_iExceptionSize[ 8] = GetPrivateProfileInteger( sSection, _T("ExceptionByteLength8"), 0);
  m_iExceptionSize[ 9] = GetPrivateProfileInteger( sSection, _T("ExceptionByteLength9"), 0);

  m_iExcLocalTime = GetPrivateProfileInteger( sSection, _T("ExceptionUseLocalTime"), 1);

  //trim leading and/or trailing spaces from strings
  m_iSpaceTrimLeft = GetPrivateProfileInteger( sSection, _T("StringTrimLeft"), 0);
  m_iSpaceTrimRight = GetPrivateProfileInteger( sSection, _T("StringTrimRight"), 0);

  if (GetPrivateProfileInteger( sSection, _T("StringTrimBothSides"), 0))
    m_iSpaceTrimLeft = m_iSpaceTrimRight = 1;

  //read default value for numerical elements in case the TM reports them as non-existent
  m_iNonExistentValue = GetPrivateProfileInteger( sSection, _T("NonExistentValue"), 0);

  //read requested behaviour for protocol errors: to disconnect or not
  m_iDisconnectProtocolErrors = GetPrivateProfileInteger( sSection, _T("DisconnectProtocolErrors"), 1);

  m_iUseLifesignTimeout = GetPrivateProfileInteger( sSection, _T("UseLifesignTimeout"), 1);
  m_iDisconnectLifesignLoss = GetPrivateProfileInteger( sSection, _T("DisconnectLifesignLoss"), 0);

  //load command identifiers
  m_sIdLifeSign = GetPrivateProfileString( sSection, _T("IdLifeSign"), _T("00"));
  if (m_sIdLifeSign.GetLength() < 2) m_sIdLifeSign = _T("00");
  if (m_sIdLifeSign.GetLength() > 2) m_sIdLifeSign = m_sIdLifeSign.Left( 2);

  m_sIdCommand = GetPrivateProfileString( sSection, _T("IdCommand"), _T("01"));
  if (m_sIdCommand.GetLength() < 2) m_sIdCommand = _T("01");
  if (m_sIdCommand.GetLength() > 2) m_sIdCommand = m_sIdCommand.Left( 2);

  m_sIdParameter = GetPrivateProfileString( sSection, _T("IdParameter"), _T("02"));
  if (m_sIdParameter.GetLength() < 2) m_sIdParameter = _T("02");
  if (m_sIdParameter.GetLength() > 2) m_sIdParameter = m_sIdParameter.Left( 2);

  m_sIdSetParameter = GetPrivateProfileString( sSection, _T("IdSetParameter"), _T("03"));
  if (m_sIdSetParameter.GetLength() < 2) m_sIdSetParameter = _T("03");
  if (m_sIdSetParameter.GetLength() > 2) m_sIdSetParameter = m_sIdSetParameter.Left( 2);

  m_sIdStatus = GetPrivateProfileString( sSection, _T("IdStatus"), _T("11"));
  if (m_sIdStatus.GetLength() < 2) m_sIdStatus = _T("11");
  if (m_sIdStatus.GetLength() > 2) m_sIdStatus = m_sIdStatus.Left( 2);

  m_sIdStatistical = GetPrivateProfileString( sSection, _T("IdStatistical"), _T("12"));
  if (m_sIdStatistical.GetLength() < 2) m_sIdStatistical = _T("12");
  if (m_sIdStatistical.GetLength() > 2) m_sIdStatistical = m_sIdStatistical.Left( 2);

  m_sIdException = GetPrivateProfileString( sSection, _T("IdException"), _T("21"));
  if (m_sIdException.GetLength() < 2) m_sIdException = _T("21");
  if (m_sIdException.GetLength() > 2) m_sIdException = m_sIdException.Left( 2);

  m_iDebugInfoForReals = GetPrivateProfileInteger( sSection, _T("DebugInfoForReals"), 0);

  m_iThreadDelaySec = GetPrivateProfileInteger( sSection, _T("ThreadDelaySec"), 0);

  m_iErrorOnLimit = GetPrivateProfileInteger( sSection, _T("ErrorOnDataLimit"), 0);

  m_iFirstLifeSign = GetPrivateProfileInteger( sSection, _T("FirstLifeSign"), 30);

  m_iAcceptIllegalException = GetPrivateProfileInteger( sSection, _T("AllowInvalidExceptionLength"), 0);
  m_iResponseTimeout = GetPrivateProfileInteger( sSection, _T("ResponseTimeout"), 5000);

  //go to next section
  sSection = _T("RunTimeManagerError");

  //load error code passing to the runtime manager
  CString StrError = _T("Error%d");
  for( int i = 0; i < MAX_ERROR; i++)
  {

    StrError.Format( _T("Error%d"), i);
    m_iRunTimeError[ i] = GetPrivateProfileInteger( sSection, StrError, 1);
  }

  //return to starting phase
  MyRunTimeManager( 0, FLS_STARTING, "START");
  return true;
} //ReadSettings

//////////////////////////////////////////////////////////////////////
// Overloaded functions
//////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////
// void MyFlTask::CTload()
// 
// 
//////////////////////////////////////////////////////////////////////
void MyFlTask::CTload()
{

  FlCTfile         CTfile;
  int              i, ct;
  CT_DRIVER        *driver_info;
  CT_DEVICE        *device_info;
  CT_DATASET       *dataset_info;
  CDriverObject    *driverobject = NULL;
  CDeviceObject    *deviceobject;
  CDatasetObject   *datasetobject;


  //mark previous state as starting
  m_iPreviousState = FLS_STARTING;

  //first load settings from the ini-file
  ReadSettings();

  //check for opened CT archive file
  if (!CTfile.IsOpen() || (CTfile.NoTables() < 2))
  {

    //adjust message according to situation 
    if (CTfile.IsOpen()) ExitApplication( _T("NOTRIGGERS"));
    else ExitApplication( _T("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;

    switch (CTfile.Type())
    {

      //global definitions for the driver, only the first record is used
      //all other records are read and stored but will never be used....
      case CTID_DRIVER:

        //read the main record, this is the first record
        driver_info = (CT_DRIVER *)CTfile.ReadRecord( 0);

        //generate an error message if there are no records
        if (!driver_info)
          ExitApplication( _T("NOHEADER"), _T("Driver"));

        //allocate memory for the new object
        if (!(driverobject = new CDriverObject( driver_info, this)))
          ExitApplication( _T("NOMEMORY"));

        //add element to list
        m_aDriver.Add( (CDriverObject *)driverobject);

        //add standby tag as a trigger
        if (driverobject->StandbyTag.IsValid())
        {

          SetEvent( driverobject->StandbyTag, EmptyEvent, driverobject);

          //force change bits for standby tag to ON
          SetChangeBits( &driverobject->StandbyTag);
        }

        //add disconnect tag as a trigger
        if (driverobject->DisconnectTag.IsValid())
        {

          SetEvent( driverobject->DisconnectTag, DisconnectEvent, driverobject);

          //force change bits for disconnect tag to ON
          SetChangeBits( &driverobject->DisconnectTag);
        }
        break;

      //load devcies, included are the datasets for th edevice
      case CTID_DEVICE:

        //read the header of the CT, this is the device
        device_info = (CT_DEVICE *)CTfile.ReadHeader();

        //allocate memory for the new object
        if (!(deviceobject = new CDeviceObject( device_info, this)))
          ExitApplication( _T("NOMEMORY"));;

        //add element to list
        m_aDevice.Add( deviceobject);

        //relation between driver object and device
        deviceobject->SetDriver( driverobject);

        //add triggers: 3 disable tag's, during processing only the new is sotred
        //for later use
        //force change bits for standby and disconnect tag to ON

        //read disable
        if (deviceobject->ReadDisableTag.IsValid())
        {

          SetEvent( deviceobject->ReadDisableTag, EmptyEvent, deviceobject);
          SetChangeBits( &deviceobject->ReadDisableTag);
        }

        //write disable
        if (deviceobject->WriteDisableTag.IsValid())
        {

          SetEvent( deviceobject->WriteDisableTag, EmptyEvent, deviceobject);
          SetChangeBits( &deviceobject->WriteDisableTag);
        }

        //receive disable
        if (deviceobject->ReadDisableTag.IsValid())
        {

          SetEvent( deviceobject->ReceiveDisableTag, EmptyEvent, deviceobject);
          SetChangeBits( &deviceobject->ReceiveDisableTag);
        }
         
        //read all the records of the CT, the recipients
        for (i = 0; i < CTfile.NoRecords(); i++)
        {

          //read the record
          dataset_info = (CT_DATASET *)CTfile.ReadRecord( i);

          //allocate memory for the new object
          if (!(datasetobject = new CDatasetObject( dataset_info, this)))
            ExitApplication( _T("NOMEMORY"));;

          //check address info
          if (datasetobject->GetStart() > PROT_MAX_LENGTH)
          {

            datasetobject->SetStart( PROT_MAX_LENGTH);
            MyRunTimeManager( ERR_DS_ADJUSTED, FLS_ERROR, _T("ADJUST_DS"), datasetobject->GetName(), 
                              datasetobject->GetStart(), datasetobject->GetLength());
          }

          if(datasetobject->GetStart() + datasetobject->GetLength() > PROT_MAX_LENGTH)
          {

            datasetobject->SetLength( PROT_MAX_LENGTH + 1 - datasetobject->GetStart());
            MyRunTimeManager( ERR_DS_ADJUSTED, FLS_ERROR, _T("ADJUST_DS"), datasetobject->GetName(), 
                              datasetobject->GetStart(), datasetobject->GetLength());
          }

          if (datasetobject->GetLength() > PROT_MAX_SIZE)
          {

            datasetobject->SetLength( PROT_MAX_SIZE);
            MyRunTimeManager( ERR_DS_ADJUSTED, FLS_ERROR, _T("ADJUST_DS"), datasetobject->GetName(), 
                              datasetobject->GetStart(), datasetobject->GetLength());
          }

          //add element to list in device object
          deviceobject->AddDataSet( datasetobject);
        }
        break;
    }
  }
}

  
//////////////////////////////////////////////////////////////////////
// void MyFlTask::Starting()
// 
// Application is starting, initialise our interface with FactoryLink.
//////////////////////////////////////////////////////////////////////
void MyFlTask::Starting()
{


  //call original function
  FlTask::Starting();

  //this is a driver, so force polling mode if we have
  //only 1 task id
  if (!Is2TaskIdDriver()) ForcePollingMode();
  else ForceEventMode();

  //check registration
  Registration();

  //assume normal start up
  m_iPreviousState = FLS_ACTIVE;

  //startup winsock, exit application when this fails
  if (!FlTask::WSAStartUp()) ExitApplication( NULL);

  //check if the command line has a definition for the local station id
  //this parameter is passed to the task with the cmd line option: -LSx
  CString sLocalStation = GetCmdLineOption( _T("LS"));

  //initialise the IMX library
  Imx_Sys_Set_Task_Id( &m_sImxSystem, GetMainId());
  Imx_Sys_Set_Station_Id( &m_sImxSystem, atoi( sLocalStation));
  Imx_Sys_Set_Mbx( &m_sImxSystem, m_aDriver.GetAt( 0)->MailboxTag.Id());
  Imx_Sys_Set_Functions( &m_sImxSystem, IMX_READ_FNC|IMX_WRITE_FNC|IMX_RCV_FNC|IMX_QUERY_FNC);

  //initialise the IMX MT library, IMX library is initialized from this call
  int imx_init = Imx_MT_Init( &m_sImxSystem);

  //quit if initialisation failed, continuing will give unpredictable errors
  if (imx_init != GOOD)
    ExitApplication( _T("IMX_INIT"), imx_init);

  //create the device structures for IMX, and link to device object
  for (int i = 0; i < m_aDevice.GetSize(); i++)
  {

    CDeviceObject *device = m_aDevice.GetAt( i);
    
    device->ImxDevice = (IMX_DEVICE *)calloc( 1, sizeof( IMX_DEVICE));
    
    if (device->ImxDevice == NULL) 
      ExitApplication( _T("NO_MEMORY"));

    //update connection state and tag
    device->UpdateConnection( DEVICE_NOT_CONNECTED);

    //set additional device information for IMX
    Imx_MT_Set_DevInfo( device->ImxDevice, device);

    //set the thread functions for every functionality
    Imx_MT_Set_Thread( device->ImxDevice, IMX_MT_READ, ReadThreadFunction);
    Imx_MT_Set_Thread( device->ImxDevice, IMX_MT_WRITE, NULL);
    //Imx_MT_Set_Thread( device->ImxDevice, IMX_MT_RCV, ReceiveThreadFunction);
    Imx_MT_Set_Thread( device->ImxDevice, IMX_MT_RCV, NULL);

    //clear communication buffers
    device->ClearBuffer();

    //allocate communication buffers for IMX MT
    FLMSG imx_buf;

    imx_buf.m_len = 0;
    imx_buf.m_max = IMX_BUFFER_SIZE;

    imx_buf.m_ptr = device->GetBuffer( FNC_READ);
    Imx_MT_Set_Buffer( device->ImxDevice, &imx_buf, FNC_READ);

    imx_buf.m_ptr = device->GetBuffer( FNC_WRITE);
    Imx_MT_Set_Buffer( device->ImxDevice, &imx_buf, FNC_WRITE);

    imx_buf.m_ptr = device->GetBuffer( FNC_RECV);
    Imx_MT_Set_Buffer( device->ImxDevice, &imx_buf, FNC_RECV);

    //temporary dataset, contents are copied with IMX functions and macros
    IMXDS t_ds;

    //now process all the datasets of this device
    for (int k = 0; k < device->GetDatasetCount(); k++)
    {

      CDatasetObject *dataset = device->GetDataSet( k);

      //find duplicate dataset control tags
      for (int a = 0; a <= i; a++)
      {

        CDeviceObject *dev = m_aDevice.GetAt( a);

        int c = a < i ? dev->GetDatasetCount() : k;

        for (int b = 0; b < c; b++)
        {

          CDatasetObject *ds = dev->GetDataSet( b);

          if ((ds->DataSetTag.Id().t_type == dataset->DataSetTag.Id().t_type) &&
              (ds->DataSetTag.Id().t_data == dataset->DataSetTag.Id().t_data))
            ExitApplication( _T("IMX_UNIQUE"), dataset->GetName());
        }
      }

      memset( &t_ds, 0x00, sizeof( IMXDS));

      //io translator mailbox
      Imx_Set_Snd_Mbx( &t_ds, dataset->IOxMailboxTag.Id());

      //datset control tag
      Imx_Set_Ds_Ctrl( &t_ds, dataset->DataSetTag.Id());

      //name of the datset control tag
      Imx_Set_Name( &t_ds, dataset->GetName());

      //additional dataset information
      Imx_Set_Udata_Ptr( &t_ds, dataset);

      //set registration buffer
      Imx_Set_R_Buffer( &t_ds, device->GetRegistrationBuffer());
      Imx_Set_T_Buffer( &t_ds, device->GetRegistrationBuffer());

      //dataset information which does not depend on the message type
      Imx_Set_Bnd_Dir( &t_ds, IMX_LOW_2_HIGH);
      Imx_Set_Bit_Dir( &t_ds, IMX_LOW_2_HIGH);

      //start and length of the dataset
      Imx_Set_Start( &t_ds, dataset->GetStart());

      switch (dataset->GetType())
      {

        case DS_COMMAND_BOOL:
        case DS_PARAMETER_BOOL: 
        case DS_STATUS_BOOL: 
        case DS_STATINFO_BOOL: 
        case DS_STATDEL_BOOL:   Imx_Set_Boundary( &t_ds, IMX_BYTE_BND);
                                Imx_Set_Len( &t_ds, dataset->GetLength());
                                break;
        
        case DS_COMMAND_CHAR:
        case DS_PARAMETER_CHAR: 
        case DS_STATUS_CHAR:
        case DS_STATINFO_CHAR: 
        case DS_STATDEL_CHAR:   Imx_Set_Boundary( &t_ds, IMX_LONG_BND);
                                Imx_Set_Bnd_Dir( &t_ds, IMX_HIGH_2_LOW);
                                Imx_Set_Bit_Dir( &t_ds, IMX_HIGH_2_LOW);
                                //every string is 20 characters
                                Imx_Set_Len( &t_ds, dataset->GetLength() * PROT_STRING_SIZE/IMX_LONG_BND);
                                Imx_Set_Start( &t_ds, dataset->GetStart() * PROT_STRING_SIZE/IMX_LONG_BND);
                                break;

        case DS_EXCEPTION:      Imx_Set_Boundary( &t_ds, IMX_BYTE_BND);
                                Imx_Set_Len( &t_ds, ImBasGetExceptionLength());
                                break;
        
        case DS_COMMAND_INT:
        case DS_PARAMETER_INT:  
        case DS_STATUS_INT: 
        case DS_STATINFO_INT: 
        case DS_STATDEL_INT:    Imx_Set_Boundary( &t_ds, IMX_WORD_BND);
                                Imx_Set_Len( &t_ds, dataset->GetLength());
                                break;

        case DS_COMMAND_LONG:
        case DS_PARAMETER_LONG:  
        case DS_STATUS_LONG: 
        case DS_STATINFO_LONG: 
        case DS_STATDEL_LONG:   Imx_Set_Boundary( &t_ds, IMX_LONG_BND);
                                Imx_Set_Len( &t_ds, dataset->GetLength());
                                break;

        case DS_COMMAND_REAL:
        case DS_PARAMETER_REAL:  
        case DS_STATUS_REAL: 
        case DS_STATINFO_REAL: 
        case DS_STATDEL_REAL:   Imx_Set_Boundary( &t_ds, IMX_DBL_BND);
                                Imx_Set_Len( &t_ds, dataset->GetLength());
                                break;

        default:                Imx_Set_Boundary( &t_ds, IMX_BYTE_BND); 
                                Imx_Set_Len( &t_ds, dataset->GetLength());
                                break;
      }

      //bit offset
      Imx_Set_Bit_Offset( &t_ds, 0);

      //finally register the dataset with the imx library
      IMXDS *ImxDataset = Imx_MT_Ds_Register( &t_ds, device->ImxDevice);

      if (ImxDataset == NULL) 
        ExitApplication( _T("IMX_REGISTER"), dataset->GetName());

      //save the imx-dataset
      dataset->SetImxDataset( ImxDataset);
    }

    //create IMX threads
    Imx_MT_CreateThread( device->ImxDevice);
  }

  TAG *imx_tag = NULL;
  void **imx_data;
  unsigned long imx_num;
  int (**imx_fnc)( void *, VAL);

  //add imx events to our event list
  Imx_Event_Retrieve( &imx_tag, &imx_data, &imx_num, &imx_fnc);  

  //find the tag object, this a mailbox or dataset tag
  //next add the event, with the imx event function
  for (i = 0; i < imx_num; i++)
  {

    //first check the mailbox tag
    if ((imx_tag[ i].t_type == m_aDriver.GetAt( 0)->MailboxTag.Id().t_type) &&
        (imx_tag[ i].t_data == m_aDriver.GetAt( 0)->MailboxTag.Id().t_data))
    {

      //fill event data strucuture
      m_aDriver.GetAt( 0)->SetImxData( *imx_fnc[ i], imx_data[ i]);

      //add event
      SetEvent( m_aDriver.GetAt( 0)->MailboxTag, 
                ImxEvent, 
                m_aDriver.GetAt( 0)->GetImxData());
    }
    else
    {

      for( int k = 0; k < m_aDevice.GetSize(); k++)
        for( int m = 0; m < m_aDevice.GetAt( k)->GetDatasetCount(); m++)
        {

          CDatasetObject *dataset = m_aDevice.GetAt( k)->GetDataSet( m);

          if ((imx_tag[ i].t_type == dataset->DataSetTag.Id().t_type) &&
              (imx_tag[ i].t_data == dataset->DataSetTag.Id().t_data))
          {

            //fill event data strucuture
            dataset->SetImxData( *imx_fnc[ i], imx_data[ i]);

            //add event
            SetEvent( dataset->DataSetTag, ImxEvent, dataset->GetImxData());
          }
        }
    }
  }

  //start listen thread for accepting connection requests
  //start a thread, on completion we have the value to return to FL
  if (m_ListenThread == NULL)
  {

    //listen thread is only started if a driver is defined
    if (m_aDriver.GetSize())
    {

      m_ListenThread = new CWinThread( MyListenThread, (void *)m_aDriver.GetAt(0));
      m_ListenThread->m_bAutoDelete = FALSE;

      //start the thread
      m_ListenThread->CreateThread();
    }
  }
}


//////////////////////////////////////////////////////////////////////
// void MyFlTask::Stopping()
// 
// 
//////////////////////////////////////////////////////////////////////
void MyFlTask::Stopping()
{

  CodeTrace( _T("CANC_LIST"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0);

  //wait for the listen thread to end
  CancelListening();

  //wait until all receive threads have ended
  CodeTrace( _T("CANC_RECV"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0);
  CancelReceiving();

  //Exit IMX MT library
  Imx_MT_Exit();

  //destroy any remaining thread
  for (int i = 0; i < m_aDevice.GetSize(); i++)
  {

    CDeviceObject *device = m_aDevice.GetAt( i);

  
    Imx_MT_KillThreads( device->ImxDevice);
  }

  //walk through the driver list and delete the objects, currently there is only one
  for (i = 0; i < m_aDriver.GetSize(); i++)
    delete m_aDriver.ElementAt( i);

  //clear the driver list
  m_aDriver.RemoveAll();

  //walk through the device list and delete the datasets
  for (i = 0; i < m_aDevice.GetSize(); i++)
  {

    CDeviceObject *device = m_aDevice.ElementAt( i);

    device->RemoveAllDatasets();

    //release memory for IMX_DEVICE structure
    free( device->ImxDevice);

    delete device;
  }

  //clear the smtp list
  m_aDevice.RemoveAll();

  //cleanup use of winsock stack
  FlTask::WSACleanUp();

  //call original function
  FlTask::Stopping();
}


/////////////////////////////////////////////////////////////////////////////
// Descripton: In between checking for changed tags, which are input or 
// triggers for the task, commands or jobs are inspected for completion.
// 
// Parameters: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::Polling()
{


  //call the original one, it does some sleeping...
  FlTask::Polling();
}



/////////////////////////////////////////////////////////////////////////////
// Descripton: Calling this function will send a stop request to the thread, 
// which is listening for connection requests from BAS control subsystems.
// The function will block untill the thread has ended.
// 
// Parameters: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::CancelListening()
{


  DWORD exitcode = STILL_ACTIVE;
  int Remaining = MAX_THREAD_STOPTIME;
  long mysleep = 10;


  //wait until the thread ends
  while (m_ListenThread)
  {

    //cancel the listen thread, done by closing the listen socket
    CDriverObject *driver = m_aDriver.GetAt( 0);

    //tell thread to stop
    m_bStopListening = true;

    //close the listen socket
    if (driver->GetListenSocket() != INVALID_SOCKET)
      driver->CloseListenSocket();

    if (!GetExitCodeThread( m_ListenThread->m_hThread, &exitcode))
    {

      exitcode = 0; //error assume no exit code
      int err = GetLastError();
    }

    if (exitcode == STILL_ACTIVE)
    {
      FlSleep( mysleep); //thread active, wait and check again

      Remaining -= mysleep;

      //kill the thread if it is still running after waiting some time
      if (Remaining <= 0)
      {

        TerminateThread( m_ListenThread->m_hThread, -1);
        //return;
      }
    }
    else
    {

      //thread has ended, release memory, destroy thread
      delete m_ListenThread;
      m_ListenThread = NULL;
    }
  }
} //CancelListening


/////////////////////////////////////////////////////////////////////////////
// Descripton: Perform a read operation on the device for the specified
// dataset. After reading the data, the result being data or an error code is 
// sent to the IO-xlator task.
// 
// Parameters: ds - The IMX dataset for which a read operation is performed.
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CDeviceObject::Read( IMXDS *ds)
{


  //the read can only be successfull if there is already a connection with
  //the remote device
  if ((IsConnected() != DEVICE_CONNECTED) || ((int)ReadDisableTag) ||
      ((int)GetDriver()->StandbyTag))
  {

    CString Xlate = _T("DRIVER_STANDBY");
    int error = ERR_DRIVER_STANDBY;

    if (IsConnected() != DEVICE_CONNECTED) //device not connected
    {

     Xlate = _T("DEVICE_NOT_CONNECTED");
     error = ERR_DEVICE_NOT_CONNECTED;
    }

    if ((int)ReadDisableTag) //read functionality disabled
    {

      Xlate = _T("DEVICE_READ_DISABLED");
      error = ERR_DEVICE_READ_DISABLED;
    }

    //send error response to the translator task
    theTask->MyRunTimeManager( error, FLS_ERROR, (char *)((LPCTSTR)Xlate), 
                               (char *)((LPCTSTR)GetName()));

    //Imx error response
    theTask->ImxSendError( ds, FNC_READ, error, this);

    return;
  }

  //there is a connection, and function is not disabled
  //build TX-buffer for command, and send data to remote device
  //save the dataset on the object
  SetCurrentImxDataset( ds);

  char *data = Imx_Get_U_Buffer( ds);
  CDatasetObject *dataset = (CDatasetObject *)Imx_Get_Udata_Ptr( ds);

  //code trace message
  theTask->CodeTrace( _T("DEVICE_READ"), 3, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT,NULL, 0, 
                      (char *)((LPCTSTR)GetName()), (char *)((LPCTSTR)dataset->GetName()));

  //ready to send request to device, mark device waiting for read response
  SetCmdPending( FNC_READ, 1);

  //send the message
  if (int error = theTask->ImBasSendMessage( this, data, dataset->GetType()))
  {

    //release read request
    SetCmdPending( FNC_READ, 0);

    //sending failed, send error and completion to IOX
    theTask->MyRunTimeManager( error, FLS_ERROR, _T("DEVICE_READ_ERROR"), (char *)((LPCTSTR)GetName()), 
                               (char *)((LPCTSTR)dataset->GetName()), error);

    //Imx error response
    theTask->ImxSendError( ds, FNC_READ, error, this);
  }
  else //wait for the recieve thread if there are no send errors
  {

    //wait for read to complete
    if (GetCmdPending( FNC_READ, theTask->GetResponseTimeout()))
    {
        //waited to long, give erro message
        theTask->MyRunTimeManager( ERR_TIMEOUT, FLS_ERROR, _T("DEVICE_READ_ERROR"), 
                                   (char *)((LPCTSTR)GetName()), 
                                   (char *)((LPCTSTR)dataset->GetName()), ERR_TIMEOUT);

        //Imx error response
        theTask->ImxSendError( ds, FNC_READ, ERR_TIMEOUT, this);
        if (this) this->SetProtocolError( 0);

        //preserve imx dataset
        LockImxDataset();

        //release read request
        SetCmdPending( FNC_READ, 0);

        //release lock on imx dataset
        ReleaseImxDataset();
    }
  }
} //Read


/////////////////////////////////////////////////////////////////////////////
// Descripton: Perform a write operation on the device for the specified
// dataset. After writing the data, the result success or an error code is 
// sent to the IO-xlator task.
// 
// Parameters: ds - The IMX dataset for which a write operation is performed.
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CDeviceObject::Write( IMXDS *ds)
{

  //the write can only be successfull if there is already a connection with
  //the remote device
  if ((IsConnected() != DEVICE_CONNECTED) || ((int)ReadDisableTag) ||
      ((int)GetDriver()->StandbyTag))
  {

    CString Xlate = _T("DRIVER_STANDBY");
    int error = ERR_DRIVER_STANDBY;

    if (IsConnected() != DEVICE_CONNECTED) //device not connected
    {

     Xlate = _T("DEVICE_NOT_CONNECTED");
     error = ERR_DEVICE_NOT_CONNECTED;
    }

    if ((int)ReadDisableTag) //read functionality disabled
    {

      Xlate = _T("DEVICE_READ_DISABLED");
      error = ERR_DEVICE_READ_DISABLED;
    }

    //send error response to the translator task
    theTask->MyRunTimeManager( error, FLS_ERROR, (char *)((LPCTSTR)Xlate), 
                               (char *)((LPCTSTR)GetName()));

    //Imx error response
    theTask->ImxSendError( ds, FNC_WRITE, error, this);
    return;
  }

  //there is a connection, and function is not disabled
  //build TX-buffer for command, and send data to remote device
  //save the dataset on the object
  SetCurrentImxDataset( ds);

  char *data = Imx_Get_U_Buffer( ds);
  CDatasetObject *dataset = (CDatasetObject *)Imx_Get_Udata_Ptr( ds);

  if (Imx_Get_Handling( ds) == IMX_NORMAL_WRITE)
  {
  
    //first try to minimalize the boundary of the element to be written
    int fail = Imx_Ds_Evaluate_Write( ds);

    if ((fail != GOOD) && (fail != IMX_BLOCK) && (fail != IMX_X_WRITE))
    {

      //send error response to the translator task
      theTask->MyRunTimeManager( fail, FLS_ERROR, _T("DEVICE_WRITE_ERROR"), 
                                 (char *)((LPCTSTR)GetName()), 
                                 (char *)((LPCTSTR)dataset->GetName()), fail);

      //Imx error response
      theTask->ImxSendError( ds, FNC_WRITE, fail, this);
      return;
    }
    int mytest = Imx_Get_CDE( ds);
    if (Imx_Get_CDE( ds) != CDE_BLOCK)
    {

      //exception write < boundary: not supported
      //send error response to the translator task
      theTask->MyRunTimeManager( ERR_EXCEPTION_WRITE, FLS_ERROR, _T("DEVICE_WRITE_ERROR"), 
                                 (char *)((LPCTSTR)GetName()), 
                                 (char *)((LPCTSTR)dataset->GetName()), ERR_EXCEPTION_WRITE);

      //Imx error response
      theTask->ImxSendError( ds, FNC_WRITE, ERR_EXCEPTION_WRITE, this);
      return;
    }
  }
  else
  {

    //encoded write is not supported
    //send error response to the translator task
    theTask->MyRunTimeManager( ERR_ENCODED_WRITE, FLS_ERROR, _T("DEVICE_WRITE_ERROR"), 
                               (char *)((LPCTSTR)GetName()), 
                               (char *)((LPCTSTR)dataset->GetName()), ERR_ENCODED_WRITE);

    //Imx error response
    theTask->ImxSendError( ds, FNC_WRITE, ERR_ENCODED_WRITE, this);
    return;
  }

  //normal block write, code trace message
  theTask->CodeTrace( _T("DEVICE_WRITE"), 3, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT,NULL, 0, 
                      (char *)((LPCTSTR)GetName()), (char *)((LPCTSTR)dataset->GetName()));
  
  int corr = (dataset->GetType() % 10 == 4) ? 5 : 1;
  int maxorg = corr * (dataset->GetStart() + dataset->GetLength());
  int maxds = Imx_Get_Start( ds) + Imx_Get_Len( ds);
  int startorg = corr * dataset->GetStart();
  int startds = Imx_Get_Start( ds);

  //check if addressing is in range with dataset definition
  if ((maxds > maxorg) || (startds < startorg))
  {

    //send error response to the translator task
    theTask->MyRunTimeManager( ERR_BW_ADDRESS, FLS_ERROR, _T("DEVICE_WRITE_ERROR"), 
                               (char *)((LPCTSTR)GetName()), 
                               (char *)((LPCTSTR)dataset->GetName()), ERR_BW_ADDRESS);

    //Imx error response
    theTask->ImxSendError( ds, FNC_WRITE, ERR_BW_ADDRESS, this);
    return;
  }

  //request send to device with suxccess, mark device for reading
  SetCmdPending( FNC_WRITE, 1);

  //send the message
  if (int error = theTask->ImBasSendMessage( this, data, dataset->GetType() + 100))
  {

    //release read request
    SetCmdPending( FNC_WRITE, 0);

    //sending failed, send error and completion to IOX
    theTask->MyRunTimeManager( error, FLS_ERROR, _T("DEVICE_WRITE_ERROR"), (char *)((LPCTSTR)GetName()), 
                               (char *)((LPCTSTR)dataset->GetName()), error);

    //Imx error response
    theTask->ImxSendError( ds, FNC_WRITE, error, this);
  }
  else
  {

    //wait for read to complete
    if (GetCmdPending( FNC_WRITE, theTask->GetResponseTimeout()))
    {
        //waited to long, give error message
        theTask->MyRunTimeManager( ERR_TIMEOUT, FLS_ERROR, _T("DEVICE_WRITE_ERROR"), 
                                 GetName(), 
                                 dataset->GetName(), 
                                 ERR_TIMEOUT);

        //Imx error response
        theTask->ImxSendError( ds, FNC_WRITE, ERR_TIMEOUT, this);
        if (this) this->SetProtocolError( 0);

        //preserve imx dataset
        LockImxDataset();

        //release read request
        SetCmdPending( FNC_WRITE, 0);

        //release lock on imx dataset
        ReleaseImxDataset();
    }
  }
} //Write


/////////////////////////////////////////////////////////////////////////////
// Descripton: Create a listen socket for the driver, all connections are 
// accepted on this socket. The creation of the socket includes the following:
// socket creation for TCP/IP, setting the options for shutdown
// 
// Parameters: 
// 
// Returns: The function returns an error (not equal to zero) or success as 
// a zero value.
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int CDriverObject::CreateListenSocket()
{

  //if there is a socket return without an error
  if (m_sListen != INVALID_SOCKET) closesocket( m_sListen);

  //open the listen socket
  if ((m_sListen = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    return ERR_SOCKET_CREATION; //return error code
  else //set the options we need
  {

    struct linger lng;

    //turn linger on, but without timeout
    lng.l_onoff = true;
    lng.l_linger = 0;

    setsockopt( m_sListen, SOL_SOCKET, SO_LINGER, (char *)&lng, sizeof( struct linger));

    //socket is in blocking mode
    ioctlsocket( m_sListen, FIONBIO, NULL);
  }

  //success
  return 0;
}


/////////////////////////////////////////////////////////////////////////////
// Descripton: Check the given message for protocol errors, message up to the 
// given length is check for errors.
// 
// Parameters: *buffer - Buffer in which the received data si stored, this data
// is checked for protocol errors.
// MsgLength - The full lenght of th emessage, or the length of the buffer to 
// check for protocol errors.
// MsgType - The message type of the received data, the data is check if it con-
// tains the same message type identification.
//
// Returns: If there are no protocol errors found in 'buffer' zero is returned, 
// otherwise an error code is returned.
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ImBasCheckRxBuffer(char *buffer, int MsgLength, int MsgType)
{

  //first the fixed items are checked, it is assumed that we are
  //dealing with an incoming message

  if (MsgLength < 2) return 0;

  //source field in header
  //if ((buffer[ 0] != 'B') || (buffer[ 1] != 'C')) return ERR_INVALID_SOURCE;
  if ((buffer[ 0] != m_sDestination[ 0]) || (buffer[ 1] != m_sDestination[ 1])) return ERR_INVALID_SOURCE;

  if (MsgLength < 4) return 0;

  //destination field in header
  //if ((buffer[ 2] != 'I') || (buffer[ 3] != 'M')) return ERR_INVALID_DESTINATION;
  if ((buffer[ 2] != m_sSource[ 0]) || (buffer[ 3] != m_sSource[ 1])) return ERR_INVALID_DESTINATION;

  if (MsgLength < 6) return 0;

/*
  m_sIdLifeSign = _T("00");     //00
  m_sIdCommand = _T("01");      //01
  m_sIdParameter = _T("02");    //02
  m_sIdSetParameter = _T("03"); //03
  m_sIdStatus = _T("11");       //11
  m_sIdStatistical = _T("12");  //12
  m_sIdException = _T("21");    //21
*/

  //find the data length of the message
  int len = ImBasGetDataLength( buffer, MsgLength);
  int dlen;
  int i;

  //message types
  switch (MsgType)
  {

    case DS_COMMAND_BOOL:
    case DS_COMMAND_INT:
    case DS_COMMAND_LONG:
    case DS_COMMAND_REAL:
    case DS_COMMAND_CHAR:
      if ((buffer[ 4] != m_sIdCommand[ 0]) || (buffer[ 5] != m_sIdCommand[ 1])) 
        return ERR_INVALID_TYPE;  
      break;

    case DS_PARAMETER_BOOL:
    case DS_PARAMETER_INT:
    case DS_PARAMETER_LONG:
    case DS_PARAMETER_REAL:
    case DS_PARAMETER_CHAR:
      if ((buffer[ 4] != m_sIdParameter[ 0]) || ((buffer[ 5] != m_sIdParameter[ 1]) && (buffer[ 5] != m_sIdSetParameter[ 1]))) 
        return ERR_INVALID_TYPE;  
      break;

    case DS_STATUS_BOOL:
    case DS_STATUS_INT:
    case DS_STATUS_LONG:
    case DS_STATUS_REAL:
    case DS_STATUS_CHAR:
      if ((buffer[ 4] != m_sIdStatus[ 0]) || (buffer[ 5] != m_sIdStatus[ 1])) 
        return ERR_INVALID_TYPE;
      break;

    case DS_STATINFO_BOOL:
    case DS_STATDEL_BOOL:
    case DS_STATINFO_INT:
    case DS_STATDEL_INT:
    case DS_STATINFO_LONG:
    case DS_STATDEL_LONG:
    case DS_STATINFO_REAL:
    case DS_STATDEL_REAL:
    case DS_STATINFO_CHAR:
    case DS_STATDEL_CHAR:
      if ((buffer[ 4] != m_sIdStatistical[ 0]) || (buffer[ 5] != m_sIdStatistical[ 1]))
        return ERR_INVALID_TYPE;  
      break;

    case DS_EXCEPTION:
      if ((buffer[ 4] != m_sIdException[ 0]) || (buffer[ 5] != m_sIdException[ 1]))
        return ERR_INVALID_TYPE;  

      //check the datalength
      for ( i = 0, dlen = 0; i < 10; i++)
        dlen += m_iExceptionSize[ i];
      //invalid lifesign lengths are sometimes accepted, ndicating the max length 
      //for the description is exceeded, but message is accepted.

      if ((len != dlen) && !m_iAcceptIllegalException) return ERR_DATA_LENGTH;
      break;

    case DS_LIFESIGN:
      if ((buffer[ 4] != m_sIdLifeSign[ 0]) || (buffer[ 5] != m_sIdLifeSign[ 1]))
        return ERR_INVALID_TYPE;  
      break;

    default: return ERR_INVALID_TYPE;
  }

  if (MsgLength < 8) return 0;

  //version number
  if (strncmp( &buffer[ 6], (LPCTSTR)m_sProtocolVersion, 2))
    return ERR_INVALID_VERSION;

  if (MsgLength < 10) return 0;

  //check sequence number
  if (MsgType == DS_LIFESIGN)
  {

    if ((buffer[ 8] != '0') || (buffer[ 9] != '0'))
      return ERR_INVALID_SEQUENCE;  
  }

  if ((len >= 0) && (MsgLength > (len + PROT_HEADER_LEN)))
  {

    if (buffer[ len + PROT_HEADER_LEN] != ImBasGetTerminator())
      return ERR_INVALID_TERMINATOR;
  }

  return 0;
}


/////////////////////////////////////////////////////////////////////////////
// Descripton: Retrieve the datalength from the data buffer
// 
// Parameters: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ImBasGetDataLength( char *buf, int length)
{

  //if length data is not included in buffer return negtive
  if (length < 13) return -1;

  //get length
  CString len = _T("000");
  len.SetAt( 0, buf[ 10]);
  len.SetAt( 1, buf[ 11]);
  len.SetAt( 2, buf[ 12]);

  return atoi( len);
}

/////////////////////////////////////////////////////////////////////////////
// Descripton: Retrieve the datalength from the data buffer
// 
// Parameters: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ImBasGetDataCount( char *buf, int length)
{
  int i  = 1;
  //if length data is not included in buffer return negtive
  if (length < 18) return -1;

  //get length
  CString len = _T("00");
  len.SetAt( 0, buf[ 16]);
  len.SetAt( 1, buf[ 17]);

  if (buf[15] == 'S') i = 5;
  return (i * atoi( len));
} //ImBasGetDataCount


/////////////////////////////////////////////////////////////////////////////
// Descripton: Retrieve the datalength from the data buffer
// 
// Parameters: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ImBasGetSequence( char *buf, int length)
{

  //if length data is not included in buffer return negtive
  if (length < 10) return -1;

  //get length
  CString len = _T("00");
  len.SetAt( 0, buf[ 8]);
  len.SetAt( 1, buf[ 9]);

  return atoi( len);
}


/////////////////////////////////////////////////////////////////////////////
// Description: Retrieve the message type from the data buffer
// 
// Parameter: *buf - Character buffer in which th emessage is received.
// Parameter: length - Length of the character buffer 'buf'.
// 
// Returns: The type of the message as an integer, note that the value is derived
// from the received message and therefor only the base (or default) type of the 
// message is returned. These types are only the boolean data types for the message.
// 
// Comment: 
//
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ImBasGetType( char *buf, int length)
{

  //if length data is not included in buffer return negtive
  if (length < 6) return -1;

  //get type
  CString type = _T("00");
  type.SetAt( 0, buf[ 4]);
  type.SetAt( 1, buf[ 5]);
  
  switch (atoi( type))
  {

    case  0: return DS_LIFESIGN;       //life sign
    case  1: return DS_COMMAND_BOOL;   //command
    case  2: return DS_PARAMETER_BOOL; //parameter data
    case  3: return DS_PARAMETER_BOOL; //parameter data
    case 11: return DS_STATUS_BOOL;    //status 
    case 12: return DS_STATINFO_BOOL;  //statistical info
    case 21: return DS_EXCEPTION;      //exception
    default: break;
  }

  return -1; //return invalid message type
}


/////////////////////////////////////////////////////////////////////////////
// Description: Retrieve the message type from the data buffer
// 
// Parameter: *buf - Character buffer in which th emessage is received.
// Parameter: length - Length of the character buffer 'buf'.
// 
// Returns: The data as a character, the format is the sdame as in the IM-BAS
// protocol, 'B" for boolean, 'I' for integer, 'L' for long, 'R' for float and
// 'S' for string.
// 
// Comment: 
//
/////////////////////////////////////////////////////////////////////////////
char MyFlTask::ImBasGetDataType( char *buf, int length)
{

  //if length data is not included in buffer return negtive
  if (length < PROT_HEADER_LEN+1) return -1;

  //get datatype
  return buf[ 15];
} //ImBasGetDataType


/////////////////////////////////////////////////////////////////////////////
// Descripton: Retrieve system id form data buffer
// 
// Parameters: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
CString MyFlTask::ImBasGetSystemId( char *buf, int length)
{

  CString id = _T("  ");


  //if id is not included in buffer return empty string
  if (length < 15) return id;

  //get length
  id.SetAt( 0, buf[ 13]);
  id.SetAt( 1, buf[ 14]);

  return id;
}

/////////////////////////////////////////////////////////////////////////////
// Descripton: Receive a message on the specified socket. Receiving a message 
// starts with receiving a header of 15 bytes, if the strucutur of these 15 
// bytes is recognized as a header, the additional data is read followed by 
// the message terminator
// 
// Parameters: sock - the socket for which a message will be received.
// buffer - Character buffer, define dby the caller, to hold the complete
// message, including header and terminator.
// type - Ouput parameter, in case a message is received this parameter is
// filled with message type. The parameter is an integer pointer
// 
// Returns: The function returns with a message or an error code. The return
// value is zero for success and is the error number if there is a failure.
// a failure indicates an error on Winsock level, the receive failed. Normally
// this is caused by the remote partner who closed the connection, or an in-
// ternal stop request, which caused the socket to close.
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ImBasRecvMessage( SOCKET sock, char *buffer, int *type, 
                                CDeviceObject *device)
{

  int rlen = 0;
  int error;
  FD_SET rfds;
  TIMEVAL t = { 0, 0};
  TIMEVAL *tval = NULL;

  if (device)
  {
    //timeout value is configured response time, plus 20%
    t.tv_sec = device->GetAliveTime() + (device->GetAliveTime() / 5);

    //error generation after the timeout is needed to notify the driver of
    //communication loss, life signs are not received within the configured
    //time period, so ther emust be already a connection before timeouts are 
    //usefull
    if (device->IsConnected() && device->theTask->UseLifesignTimeout()) tval = &t;
  }
  else
  {

    //first lifesign, use default timeout
    t.tv_sec = GetFirstLifeSign();

    tval = &t;
  }

  //start receiving a header
  while (rlen < PROT_HEADER_LEN)
  {

    //wait for data
    FD_ZERO( &rfds);
    FD_SET( sock, &rfds);

    error = select( 0, &rfds, NULL, NULL, tval);

    if ((error == SOCKET_ERROR) || (error == 0))
    {

      //timeout reached, update device status
      if (error == 0)
      {

        if (device)
        {
          //update status tag
          device->UpdateStatus( ERR_LIFESIGN_TIMEOUT);

          //update connection tag
          device->UpdateConnection( DEVICE_TCPIP_ONLY);

          //update runtime manager
          MyRunTimeManager( ERR_LIFESIGN_TIMEOUT, FLS_ERROR, _T("LIFESIGN_TIMEOUT"), 
                            device->GetName(), device->GetAliveTime());

          //exit or continue?
          if (device->theTask->DisconnectLifesignLoss()) return ERR_RX_FAILED;
          else continue;
        }
        else
        {

          //update runtime manager
          MyRunTimeManager( ERR_LIFESIGN_TIMEOUT, FLS_ERROR, _T("LIFESIGN_TIMEOUT"), 
                            Xlate("UNKNOWN_DEV"), t.tv_sec);
          //first timeout, no device return with RX error
          return ERR_RX_FAILED;
        }
      }

      //trace the winsock error
      if ((error == SOCKET_ERROR) && device)
        CodeTrace( _T("SOCKET_ERROR"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, 
                   WSAGetLastError(), device->GetName());

      return ERR_RX_FAILED; //read error
    }

    //check for stop condition
    if (Imx_MT_Get_Stop()) return ERR_RX_FAILED;

    //inhibit send actions
    if (device) device->Lock();

    //receive the header
    error = recv( sock, &buffer[ rlen], PROT_HEADER_LEN, 0);

    //release device for send actions
    if (device) device->Release();

    //evaluate the received data
    if (!error || (error == SOCKET_ERROR ))
    {

      //build device name for logging purposes
      CString DevName = (device) ? device->GetName() : Xlate( "UNKNOWN_DEV");

      if (!error)
        CodeTrace( _T("SOCKET_CLOSED"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, DevName);
      else
        CodeTrace( _T("SOCKET_ERROR"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, WSAGetLastError(), DevName);

      return ERR_RX_FAILED;
    }
    else 
      rlen += error; //number of received bytes
  }

  //evaluate the header, assume header is ok and extract message type
  *type = ImBasGetType( buffer, PROT_HEADER_LEN);

  //now check if everything is ok, including the expected message type
  if (error = ImBasCheckRxBuffer( buffer, PROT_HEADER_LEN, *type))
  {

    //code trace for invalid protocol data
    CodeTrace( _T("DEVICE_INVRX"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, buffer, PROT_HEADER_LEN, error);

    //save protocol error on device
    if (device) device->SetProtocolError( error);

    //read data untill terminator is received
    int check = rlen > 0 ? rlen - 1: 0;
    if (!rlen) buffer[ rlen] = '0';

    while (buffer[ check] != ImBasGetTerminator())
    {

      //wait for data
      FD_ZERO( &rfds);
      FD_SET( sock, &rfds);
      t.tv_sec = 0;
      t.tv_usec = 0;
      error = select( 0, &rfds, NULL, NULL, &t);
      
      if (!error || error == SOCKET_ERROR)
      {

        //build device name for logging purposes
        CString DevName = (device) ? device->GetName() : Xlate( "UNKNOWN_DEVID", ImBasGetSystemId( buffer, PROT_HEADER_LEN));

        //trace the winsock error
        if (error == SOCKET_ERROR)
          CodeTrace( _T("SOCKET_ERROR"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, WSAGetLastError());
        else
          CodeTrace( _T("RCV_TIMEOUT"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, DevName);

        break;
      }

      recv( sock, &buffer[ rlen++], 1, 0);
      check = rlen - 1;
    } 

    //code trace for invalid protocol data
    //if (rlen > 1)
    //  CodeTrace( _T("DEVICE_INVRX"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, buffer, rlen-1, ERR_RECVLEN);

    rlen = 0; //invalid header, try again

    //give a value for message type: unknown type
    *type = 0;
    if (m_iDisconnectProtocolErrors) return ERR_RX_FAILED;
    else return 0;
  }

  //header is received and is looking fine, receive data
  while (rlen < (PROT_HEADER_LEN + ImBasGetDataLength( buffer, PROT_HEADER_LEN) + 1))
  {

    //wait for data
    FD_ZERO( &rfds);
    FD_SET( sock, &rfds);
    select( 0, &rfds, NULL, NULL, &t);

    //receive data
    error = recv( sock, &buffer[ rlen], PROT_HEADER_LEN + ImBasGetDataLength( buffer, PROT_HEADER_LEN) + 1 - rlen, 0);

    //evaluate the received data
    if (!error || (error == SOCKET_ERROR ))
    {

      //build device name for logging purposes
      CString DevName = (device) ? device->GetName() : Xlate( "UNKNOWN_DEV");

      if (!error)
        CodeTrace( _T("SOCKET_CLOSED"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, device->GetName());
      else
        CodeTrace( _T("SOCKET_ERROR"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, WSAGetLastError(), device->GetName());

      return ERR_RX_FAILED;
    }
    else 
      rlen += error; //number of received bytes
  }

  CodeTrace( _T("DEVICE_RX"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, buffer, rlen);

  //check received message, this time only for terminator...
  if (error = ImBasCheckRxBuffer( buffer, rlen, *type))
  {

    //save the protocol error on the device
    if (device) device->SetProtocolError( error);

    //invalidate the message type
    *type = 0;

    if (m_iDisconnectProtocolErrors) return error;
  }

   //done, all data received
   return 0;
} //ImBasRecvMessage


/////////////////////////////////////////////////////////////////////////////
// Descripton: Send a message to the remote station
// 
// Parameters: 
// 
// Returns: 
// 
// Comment: 
//calculate the maximum response size, split the request if the response 
//exceeds the limit of 999 data byte
//for (int x = 0; x < dataset->GetLength() / theTask->ImBasMaxLength( dataset->GetType()) + 1; x++)
//;
// 
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ImBasSendMessage( CDeviceObject *device, char *buffer, int type)
{

  //calculate datalength for dataset and fill data buffer
  int start = 0;
  int length = 0;
  char data[ IMX_BUFFER_SIZE];
  int rep = 1;
  int corr = 1; 

  //we need a device,if there is none don't send anything
  if (!device) return ERR_TX_FAILED; //error code is general

  CodeTrace( _T("SEND_MESSAGE"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, device->GetName(), type);

  //correction factor for packet count
  switch (type)
  {
  
    case DS_COMMAND_CHAR:
    case DS_PARAMETER_CHAR:
    case DS_STATUS_CHAR:
    case DS_STATINFO_CHAR:
    case DS_STATDEL_CHAR: 
    case DSW_COMMAND_CHAR:
    case DSW_PARAMETER_CHAR:
    case DSW_STATUS_CHAR:
    case DSW_STATINFO_CHAR:
    case DSW_STATDEL_CHAR:

    case DS_EXCEPTION:
    case DSW_EXCEPTION: //response send, end of command

      //correction only needed if there is an active imx-dataset
      if (device->GetCurrentImxDataset())
      {

        //see if we need a correction factor fo the number of bytes to send
        IMXDS *t = device->GetCurrentImxDataset();
        corr = t->boundary;
        corr = PROT_STRING_SIZE/Imx_Get_Boundary(device->GetCurrentImxDataset()); 
        corr = PROT_STRING_SIZE/t->boundary;
      }

    case DS_LIFESIGN:
    case DSW_LIFESIGN:

    case DS_COMMAND_BOOL:
    case DS_COMMAND_INT:
    case DS_COMMAND_LONG:
    case DS_COMMAND_REAL:
    case DS_PARAMETER_BOOL:
    case DS_PARAMETER_INT:
    case DS_PARAMETER_LONG:
    case DS_PARAMETER_REAL:
    case DS_STATUS_BOOL:
    case DS_STATUS_INT:
    case DS_STATUS_LONG:
    case DS_STATUS_REAL:
    case DS_STATINFO_BOOL:
    case DS_STATINFO_INT:
    case DS_STATINFO_LONG:
    case DS_STATINFO_REAL:
    case DS_STATDEL_BOOL:
    case DS_STATDEL_INT:
    case DS_STATDEL_LONG:
    case DS_STATDEL_REAL:

    case DSW_COMMAND_BOOL:
    case DSW_COMMAND_INT:
    case DSW_COMMAND_LONG:
    case DSW_COMMAND_REAL:
    case DSW_PARAMETER_BOOL:
    case DSW_PARAMETER_INT:
    case DSW_PARAMETER_LONG:
    case DSW_PARAMETER_REAL:
    case DSW_STATUS_BOOL:
    case DSW_STATUS_INT:
    case DSW_STATUS_LONG:
    case DSW_STATUS_REAL:
    case DSW_STATINFO_BOOL:
    case DSW_STATINFO_INT:
    case DSW_STATINFO_LONG:
    case DSW_STATINFO_REAL:
    case DSW_STATDEL_BOOL:
    case DSW_STATDEL_INT:
    case DSW_STATDEL_LONG:
    case DSW_STATDEL_REAL:

    //case DSW_EXCEPTION: //response send, end of command
      break;
    
    default:
      //the command issued is unknown
      return ERR_CMD_UNKNOWN;
  }
  
  if (device->GetCurrentImxDataset())
  {

    //data = buffer ? buffer: Imx_Get_U_Buffer( device->GetCurrentImxDataset());
    rep = GetPacketCount( Imx_Get_Len( device->GetCurrentImxDataset()), type);

    CodeTrace( _T("REPETION_TX"), 5, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, 
               rep, (char *)((LPCTSTR)device->GetName()));

  }
  //else
  //  data = new char[ IMX_BUFFER_SIZE];

  //memory allocation error
//  if (!data) return ERR_MEMORY_ALLOCATION;

  if (rep < 1) rep = 1;

  //send all the message packets
  for (int i = 0; i < rep; i++)
  {

    if (device->GetCurrentImxDataset())
    {

      int len = (Imx_Get_Len( device->GetCurrentImxDataset()) / corr) - (i * ImBasMaxLength( type));
      len = len > ImBasMaxLength( type) ? ImBasMaxLength( type) : len;

      start = Imx_Get_Start( device->GetCurrentImxDataset()) / corr;
      start += (i * ImBasMaxLength( type));
      length = len;
    }  

    //clear send buffer
    memset( data, (int)' ', IMX_BUFFER_SIZE);

    //build data part of send buffer
    int datalen = ImBasImx2Data( data, type, start, &length, device);

    //check for invalid type
    if (datalen < 0)
      return ERR_INVALID_ACTION;

    //build the buffer with data, start with the header
    ImBasBuildHeader( data, device, &datalen, type);

    //datalength is updated with header length
    datalen += PROT_LIFESIGN_MSG;

    //inhibit recv actions
    CodeTrace( _T("SEND_LOCK"), 5, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, device->GetName());
    device->Lock();

    //send the data
    int slen = 0;
    if (datalen != (slen = send( device->GetSocket(), data, datalen, 0)))
    {

      CodeTrace( _T("DEVICE_TXLEN"), 5, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, 
                 datalen, slen, (char *)((LPCTSTR)device->GetName()));

      //release device for recv actions
      device->Release();

      //something went wrong, error code is general send error
      return ERR_TX_FAILED;
    }
    else //code trace message
    {

      CodeTrace( _T("DEVICE_TXLEN"), 5, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, 
                 datalen, slen, (char *)((LPCTSTR)device->GetName()));
      CodeTrace( _T("DEVICE_TX"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, data, datalen, 
                         (char *)((LPCTSTR)device->GetName()));
    }

    //release device for recv actions
    device->Release();
  }
    
  return 0;
} //ImBasSendMessage


/////////////////////////////////////////////////////////////////////////////
// Descripton: Send an response to the translator, use our own defined task
// id(s), overrule the one defined with imx.
// This function check if the mailbox for sending messages has not more then 
// the maximum allowed number of messages. The user is responsible for selecting
// a maximum number of mbx messages, which will not overload the system (system
// wide settings are set with command line options of the run-tiume manager 
// task (-m option).
// 
// Parameters: 
// *ds - Pointer to an IMXDS structure, which is the currently processed
//       dataset.
// Function - Integer specifying the type of the response for the IO-translator.
//            The response type can be FNC_READ (0), FNC_WRITE (1) or FNC_RECV (2).
// *dev - Pointer to the device object associated with the specified IMX dataset, see
// the first parameter '*ds'.
// SaveError - Integer parameter with value false (0) or true (any value except 0), 
//             if the value is true any existing error condition on the task is saved
//             and the task status remains red if there is a previous error, otherwise
//             the task status will green, indicatin error free operation.
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::ImxSend(IMXDS *ds, int Function, CDeviceObject *dev, int SaveError)
{

  IMXSYSTEM imxsys;
  int error = 0;


  Imx_Set_Type( ds, Function);

  //remove protocol error from the device
  if (!SaveError && dev) dev->SetProtocolError( 0);

  //check all devices for errors
  for( int i = 0; i < DeviceCount(); i++)
    error = GetDevice( i)->GetProtocolerror() ? error+1: error;

  LockRTDB( false);

  //set running state of the task
  if (!error) MyRunTimeManager( -1, FLS_ACTIVE, NULL);

  //fill imx structure with correct task id
  memcpy( &imxsys, &m_sImxSystem, sizeof( IMXSYSTEM));
  imxsys.task_id = GetId();

  //check for the allowed maximum number of messages in the mailbox tag
  unsigned int iMbxCount = 0;
  //errors in retrieving the number of messages are ignored, vlaue will
  //be zero, but if there is an error, there will be more errors and this
  //error will never happen on its own.....
  Imx_Q_Num_Mbx( &imxsys, Imx_Get_Snd_Mbx( ds), &iMbxCount); 

  //printf( "\nImxSend: Current = %d, Max = %d", iMbxCount, m_iIoxMaxMbxMsg);

  if (iMbxCount <= m_iIoxMaxMbxMsg)
  {
    //send imx message to translator
    if ((error = Imx_Send( &imxsys, ds)) != GOOD)
      MyRunTimeManager( error, FLS_ERROR, _T("IMX_SEND"), error, Imx_Get_Name( ds));
  }
  else
    MyRunTimeManager( error, FLS_ERROR, _T("IMX_SEND"), (int)ERR_IOX_MAX_MESSAGE, Imx_Get_Name( ds));

  ReleaseRTDB();
} //ImxSend


/////////////////////////////////////////////////////////////////////////////
// Descripton: Send an error response to the translator, use our own defined
// task id(s), overrule th eone defined with imx.
// 
// Parameters: *ds - Pointer to an IMXDS structure, which is the currently 
// processed dataset.
// func - Functionality on the dataset which reported an error
// code - error code for the failed functionality.
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::ImxSendError( IMXDS *ds, int func, int code, CDeviceObject *dev)
{

  IMXSYSTEM imxsys;
  int error = 0;


  //remove limit errors
  CDatasetObject *dsobject = ds ? (CDatasetObject *)Imx_Get_Udata_Ptr( ds): NULL;
  if (dsobject) dsobject->GetErrorOnLimit();

  //save the error on the device
  if (dev) dev->SetProtocolError( code);

  LockRTDB( false);

  if (dev) dev->UpdateStatus( code);

  //fill imx structure with correct task id
  memcpy( &imxsys, &m_sImxSystem, sizeof( IMXSYSTEM));
  imxsys.task_id = GetId();

  //message for run time manager
  switch (func)
  {

    case 0: //read
      MyRunTimeManager( code, FLS_ERROR, _T("IMX_READ_ERROR"), code, Imx_Get_Name( ds));
      break;

    case 1: //write
      MyRunTimeManager( code, FLS_ERROR, _T("IMX_WRITE_ERROR"), code, Imx_Get_Name( ds));
      break;

    case 2: //receive
      MyRunTimeManager( code, FLS_ERROR, _T("IMX_RECV_ERROR"), code, Imx_Get_Name( ds));
      break;
  }

  //check for the allowed maximum number of messages in the mailbox tag
  unsigned int iMbxCount = 0;
  //errors in retrieving the number of messages are ignored, vlaue will
  //be zero, but if there is an error, there will be more errors and this
  //error will never happen on its own.....
  Imx_Q_Num_Mbx( &imxsys, Imx_Get_Snd_Mbx( ds), &iMbxCount); 

  //printf( "\nImxSendError: Current = %d, Max = %d", iMbxCount, m_iIoxMaxMbxMsg);

  if (iMbxCount <= m_iIoxMaxMbxMsg)
  {

    if ((error = Imx_Send_Error( &imxsys, ds, code)) != GOOD)
      MyRunTimeManager( error, FLS_ERROR, _T("IMX_SEND_ERROR"), error, Imx_Get_Name( ds));
  }
  else
    MyRunTimeManager( error, FLS_ERROR, _T("IMX_SEND"), (int)ERR_IOX_MAX_MESSAGE, Imx_Get_Name( ds));

  ReleaseRTDB();

  return;
} //ImxSendError


/////////////////////////////////////////////////////////////////////////////
// Descripton: Build header strucuture for message with device settings for the
// specified message type
// 
// Parameters: buffer - Pointer to buffer to fill with header, the header length 
//                      is 15 bytes, user needs to supply a buffer which is large
//                      enough.
//             device - Pointer to device object, settings for the header are 
//                      stored on the device object.
//             datalen - This is an integer telling how many databytes will be 
//                       included in the message.
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::ImBasBuildHeader( char *buffer, CDeviceObject *device, 
                                 int *datalen, int type)
{

  //device object is reauired
  if (!device) return;

  //fill bytes 0 and 1 with source id
  buffer[ 0] = m_sSource[ 0];
  buffer[ 1] = m_sSource[ 1];

  //fill bytes 2 and 3 with destination id
  buffer[ 2] = m_sDestination[ 0];
  buffer[ 3] = m_sDestination[ 1];

  //fill bytes 4 and 5 with message number
  switch (type)
  {

    case DS_COMMAND_BOOL:      
    case DS_COMMAND_INT:      
    case DS_COMMAND_LONG:      
    case DS_COMMAND_REAL:      
    case DS_COMMAND_CHAR:     
      device->IncSequenceNr();
      buffer[ 4] = m_sIdCommand[ 0]; 
      buffer[ 5] = m_sIdCommand[ 1]; break;

    case DSW_COMMAND_BOOL:      
    case DSW_COMMAND_INT:      
    case DSW_COMMAND_LONG:      
    case DSW_COMMAND_REAL:      
    case DSW_COMMAND_CHAR:    
      device->IncSequenceNr();
      buffer[ 4] = m_sIdCommand[ 0]; 
      buffer[ 5] = m_sIdCommand[ 1]; break;

    case DS_PARAMETER_BOOL:
    case DS_PARAMETER_INT: 
    case DS_PARAMETER_LONG:
    case DS_PARAMETER_REAL:
    case DS_PARAMETER_CHAR:   
      device->IncSequenceNr();
      buffer[ 4] = m_sIdParameter[ 0]; 
      buffer[ 5] = m_sIdParameter[ 1]; break;

    case DSW_PARAMETER_BOOL:
    case DSW_PARAMETER_INT: 
    case DSW_PARAMETER_LONG:
    case DSW_PARAMETER_REAL:
    case DSW_PARAMETER_CHAR: 
      device->IncSequenceNr();
      buffer[ 4] = m_sIdSetParameter[ 0]; 
      buffer[ 5] = m_sIdSetParameter[ 1]; break;

    case DS_STATUS_BOOL:
    case DS_STATUS_INT:
    case DS_STATUS_LONG:
    case DS_STATUS_REAL:
    case DS_STATUS_CHAR:     
      device->IncSequenceNr();
      buffer[ 4] = m_sIdStatus[ 0]; 
      buffer[ 5] = m_sIdStatus[ 1]; break;

    case DSW_STATUS_BOOL:
    case DSW_STATUS_INT:
    case DSW_STATUS_LONG:
    case DSW_STATUS_REAL:
    case DSW_STATUS_CHAR:    
      device->IncSequenceNr();
      buffer[ 4] = m_sIdStatus[ 0]; 
      buffer[ 5] = m_sIdStatus[ 1]; break;

    case DS_STATINFO_BOOL:
    case DS_STATINFO_INT:
    case DS_STATINFO_LONG:
    case DS_STATINFO_REAL:
    case DS_STATINFO_CHAR:   
      device->IncSequenceNr();
      buffer[ 4] = m_sIdStatistical[ 0]; 
      buffer[ 5] = m_sIdStatistical[ 1]; break;

    case DSW_STATINFO_BOOL:
    case DSW_STATINFO_INT:
    case DSW_STATINFO_LONG:
    case DSW_STATINFO_REAL:
    case DSW_STATINFO_CHAR:  
      device->IncSequenceNr();
      buffer[ 4] = m_sIdStatistical[ 0]; 
      buffer[ 5] = m_sIdStatistical[ 1]; break;

    case DS_STATDEL_BOOL:
    case DS_STATDEL_INT:
    case DS_STATDEL_LONG:
    case DS_STATDEL_REAL:
    case DS_STATDEL_CHAR:    
      device->IncSequenceNr();
      buffer[ 4] = m_sIdStatistical[ 0]; 
      buffer[ 5] = m_sIdStatistical[ 1]; break;

    case DSW_STATDEL_BOOL:
    case DSW_STATDEL_INT:
    case DSW_STATDEL_LONG:
    case DSW_STATDEL_REAL:
    case DSW_STATDEL_CHAR:   
      device->IncSequenceNr();
      buffer[ 4] = m_sIdStatistical[ 0]; 
      buffer[ 5] = m_sIdStatistical[ 1]; break;

    case DS_EXCEPTION:
    case DSW_EXCEPTION:      
      buffer[ 4] = m_sIdException[ 0]; 
      buffer[ 5] = m_sIdException[ 1]; break;

    default:       
    case DSW_LIFESIGN:
    case DS_LIFESIGN:        
      buffer[ 4] = m_sIdLifeSign[ 0]; 
      buffer[ 5] = m_sIdLifeSign[ 1]; break;
  }

  //fill bytes 6 and 7 with version
  buffer[ 6] = m_sProtocolVersion[ 0];
  buffer[ 7] = m_sProtocolVersion[ 1];

  //fill bytes 8 and 9 with sequence number
  if ((type == DS_LIFESIGN) ||(type == DSW_LIFESIGN))
    buffer[ 8] = buffer[ 9] = '0';
  else
    if (type == DSW_EXCEPTION) 
      sprintf( &buffer[8], _T("%02d"), device->GetReplySequence());
    else 
      sprintf( &buffer[8], _T("%02d"), device->GetSequenceNr());

  //fill bytes 10, 11 and 12 with data length
  if (*datalen > 999) *datalen = 999;
  sprintf( &buffer[ 10], _T("%03d"), *datalen);

  //fill bytes 13 and 14 with subsystem id
  buffer[ 13] = device->GetSubId()[ 0];
  buffer[ 14] = device->GetSubId()[ 1];

  //add terminator
  buffer[ PROT_HEADER_LEN + *datalen] = ImBasGetTerminator();
} //ImBasBuildHeader


/////////////////////////////////////////////////////////////////////////////
// Descripton: Convert Imx data to dat strucutres used in the IM-BAS protocol.
// If the conversion fails, e.g the requested message type is not implemented
// in the protocol, a value of -1 is returned. Otherwise the length in bytes 
// of the data buffer is returned.
// 
// Parameters: buffer - pointer to a data buffer i which the message is stored.
// type - The type of the message to convert
// start - Start address of the data
// length - umber of addresses to read/write.
// 
// Returns: The number of bytes which are in the buffer ready for sending on
// the network. If this value is -1, an error has occured, and one should not
// send the message.
// 
// Comment: 
// Imx_Get_Word( IMXDS *ds, i16 *data) {
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ImBasImx2Data( char *buf, int type, int start, 
                             int *length, CDeviceObject *device) //IMXDS *ds)
{

  int retlen = 0;
  int i, k;
  int dslen;
  char *buffer = NULL;//[ IMX_BUFFER_SIZE];
  char del = '0';
  int startds = 0;
  IMXDS *ds = (device) ? device->GetCurrentImxDataset(): NULL;
  CDatasetObject *dataset = NULL;
  char tmpbuf[20];


  //get registered dataset
  if (ds) dataset = (CDatasetObject *)Imx_Get_Udata_Ptr( ds);

  //update buf pointer for header
  buf += PROT_HEADER_LEN;

  //check length of dataset
  if (*length > 99) *length = 99;

  if (ds)
    buffer = Imx_Get_U_Buffer( ds);

  //fill buffer with data, filling depends on type of dataset
  switch (type)
  {

    case DSW_COMMAND_BOOL:
    case DSW_PARAMETER_BOOL:

      if (!ds) return 0;
      startds = Imx_Get_Start( ds);

      buf[ 0] = 'B'; //field type
      sprintf( &buf[ 1], "%02d", *length);
      dslen = *length;
      
      for (i = 0, k = 0; i < *length; i++)
      {

        if ((dataset == NULL) ||   
            ((dataset != NULL) && (dataset->FindAddressIndex(start + i) != 0)))
        {
          //set field index
          sprintf( &buf[ 3 + k*6], "%04d", start + i);

          //assume boundary is byte
          buf[ 7 + k*6] = (buffer[ i]) ? '1': '0';
          buf[ 8 + k++*6] = ImBasGetDelimiter(); //';';
        }
        else //skip address, adjust length with one element
        {

          sprintf( &tmpbuf[ 1], "%02d", --dslen);
          buf[ 1] = tmpbuf[ 1];
          buf[ 2] = tmpbuf[ 2];
        }
      }

      retlen = (dslen * 6) + 3; //length of data
      break;

    case DSW_COMMAND_INT:
    case DSW_PARAMETER_INT:

      if (!ds) return 0;
      startds = Imx_Get_Start( ds) / 2;

      //fill request buffer
      buf[ 0] = 'I';

      sprintf( &buf[ 1], "%02d", *length);
      dslen = *length;

      for (i = 0, k = 0; i < *length; i++)
      {

        if ((dataset == NULL) ||   
            ((dataset != NULL) && (dataset->FindAddressIndex(start + i) != 0)))
        {

          sprintf( &buf[ 3 + k*8], "%04d", start + i);

          //assume boundary is word
          int x = Imx_Get_Word( ds, (u16 *)&buffer[ 2*i]);

          //check value
          if (device && ((x < 0) || (x > 999))) dataset->SetErrorOnLimit();
          if (x > 999) x = 999;
          if (x < 0) x = 0;

          sprintf( &buf[ 7 + k*8], "%03d", x);
          buf[ 10 + k++*8] = ImBasGetDelimiter(); //';';
        }
        else //skip address, adjust length with one element
        {

          sprintf( &tmpbuf[ 1], "%02d", --dslen);
          buf[ 1] = tmpbuf[ 1];
          buf[ 2] = tmpbuf[ 2];
        }
      }

      retlen = (dslen * 8) + 3;
      break;

    case DSW_COMMAND_LONG:
    case DSW_PARAMETER_LONG:

      if (!ds) return 0;
      startds = Imx_Get_Start( ds) / 4;

      //fill request buffer
      buf[ 0] = 'L';

      sprintf( &buf[ 1], "%02d", *length);
      dslen = *length;
      
      for (i = 0, k = 0; i < *length; i++)
      {

        if ((dataset == NULL) ||   
            ((dataset != NULL) && (dataset->FindAddressIndex(start + i) != 0)))
        {

          sprintf( &buf[ 3 + k*11], "%04d", start + i);

          //assume boundary is long
          long x = Imx_Get_Long( ds, (u32 *)&buffer[ 4*i]);

          //check value
          if (device && ((x < 0) || (x > 999999))) dataset->SetErrorOnLimit();
          if (x > 999999) x = 999999;
          if (x < 0) x = 0;

          sprintf( &buf[ 7 + k*11], "%06d", x);
          buf[ 13 + k++*11] = ImBasGetDelimiter(); //';';
        }
        else //skip address, adjust length with one element
        {

          sprintf( &tmpbuf[ 1], "%02d", --dslen);
          buf[ 1] = tmpbuf[ 1];
          buf[ 2] = tmpbuf[ 2];
        }
      }

      retlen = (dslen * 11) + 3;
      break;

    case DSW_COMMAND_REAL:
    case DSW_PARAMETER_REAL:

      if (!ds) return 0;
      startds = Imx_Get_Start( ds) / IMX_DBL_BND;

      retlen = (*length * 22) + 3;

      //fill request buffer
      buf[ 0] = 'R';

      sprintf( &buf[ 1], "%02d", *length);
      dslen = *length;

      for (i = 0, k = 0; i < *length; i++)
      {

        if ((dataset == NULL) ||   
            ((dataset != NULL) && (dataset->FindAddressIndex(start + i) != 0)))
        {

          sprintf( &buf[ 3 + k*22], "%04d", start + i);

          //assume boundary is double, elements are double's
          double x = Imx_Get_Double( ds, (double *)&buffer[ IMX_DBL_BND*i]);

          //check value
          if (device && ((x < 0) || (x > 99999999.99999999))) dataset->SetErrorOnLimit();
          if (x > 99999999.99999999) x = 99999999.99999999;
          if (x < 0) x = 0;

          sprintf( &buf[ 7 + k*22], "%017.8f", (double)x);
          buf[ 24 + k++*22] = ImBasGetDelimiter(); //';';
        }
        else //skip address, adjust length with one element
        {

          sprintf( &tmpbuf[ 1], "%02d", --dslen);
          buf[ 1] = tmpbuf[ 1];
          buf[ 2] = tmpbuf[ 2];
        }
      }
      break;

    case DSW_COMMAND_CHAR:
    case DSW_PARAMETER_CHAR:

      if (!ds) return 0;
      startds = Imx_Get_Start( ds) / (PROT_STRING_SIZE/IMX_LONG_BND);

      //fill request buffer
      buf[ 0] = 'S';

      sprintf( &buf[ 1], "%02d", *length);
      dslen = *length;

      //every string is supposed to be 20 characters long
      for (i = 0, k = 0; i < *length; i++)
      {

        if ((dataset == NULL) ||   
            ((dataset != NULL) && (dataset->FindAddressIndex(start + i) != 0)))
        {

          sprintf( &buf[ 3 + i*25], "%04d", start + i);

          //assume boundary is byte
          for (int x = 0; x < PROT_STRING_SIZE; x++)
          {
            if (buffer[ (i+start-startds)*PROT_STRING_SIZE + x] == 0)
              buf[ 7 + i*25 + x] = ' '; //trailing spaces
            else
              buf[ 7 + i*25 + x] = buffer[ (i+start-startds)*PROT_STRING_SIZE + x];
          }
       
          buf[ 7 + i*25 + PROT_STRING_SIZE] = ImBasGetDelimiter(); //';';
        }
        else //skip address, adjust length with one element
        {

          sprintf( &tmpbuf[ 1], "%02d", --dslen);
          buf[ 1] = tmpbuf[ 1];
          buf[ 2] = tmpbuf[ 2];
        }
      }

      retlen = (dslen * 25) + 3;
      break;

    case DS_PARAMETER_BOOL:
    case DS_PARAMETER_INT:
    case DS_PARAMETER_LONG:
    case DS_PARAMETER_REAL:
    case DS_PARAMETER_CHAR:

      //fill request buffer
      if (type == DS_PARAMETER_BOOL) buf[ 0] = 'B';
      if (type == DS_PARAMETER_INT)  buf[ 0] = 'I';
      if (type == DS_PARAMETER_LONG) buf[ 0] = 'L';
      if (type == DS_PARAMETER_REAL) buf[ 0] = 'R';
      if (type == DS_PARAMETER_CHAR) buf[ 0] = 'S';

      sprintf( &buf[ 1], "%02d", *length);
      dslen = *length;

      for (i = 0, k = 0; i < *length; i++)
      {

        if ((dataset == NULL) ||   
            ((dataset != NULL) && (dataset->FindAddressIndex(start + i) != 0)))
        {

          sprintf( &buf[ 3 + k*5], "%04d", start + i);

          //assume boundary is byte
          buf[ 7 + k++*5] = ImBasGetDelimiter(); //';';
        }
        else //skip address, adjust length with one element
        {

          sprintf( &tmpbuf[ 1], "%02d", --dslen);
          buf[ 1] = tmpbuf[ 1];
          buf[ 2] = tmpbuf[ 2];
        }
      }

      retlen = (dslen * 5) + 3;
      break;

    case DS_STATUS_BOOL:
    case DS_STATUS_INT:
    case DS_STATUS_LONG:
    case DS_STATUS_REAL:
    case DS_STATUS_CHAR:
      retlen = 0;
      break;

    case DS_STATDEL_BOOL:
    case DS_STATDEL_INT:
    case DS_STATDEL_LONG:
    case DS_STATDEL_REAL:
    case DS_STATDEL_CHAR: del = '1';
    case DS_STATINFO_BOOL:
    case DS_STATINFO_INT:
    case DS_STATINFO_LONG:
    case DS_STATINFO_REAL:
    case DS_STATINFO_CHAR:

      //fill request buffer
      if ((type == DS_STATDEL_BOOL) || (type == DS_STATINFO_BOOL)) buf[ 0] = 'B';
      if ((type == DS_STATDEL_INT)  || (type == DS_STATINFO_INT))  buf[ 0] = 'I';
      if ((type == DS_STATDEL_LONG) || (type == DS_STATINFO_LONG)) buf[ 0] = 'L';
      if ((type == DS_STATDEL_REAL) || (type == DS_STATINFO_REAL)) buf[ 0] = 'R';
      if ((type == DS_STATDEL_CHAR) || (type == DS_STATINFO_CHAR)) buf[ 0] = 'S';

      sprintf( &buf[ 1], "%02d", *length);
      dslen = *length;

      for (i = 0, k = 0; i < *length; i++)
      {

        if ((dataset == NULL) ||   
            ((dataset != NULL) && (dataset->FindAddressIndex(start + i) != 0)))
        {

          sprintf( &buf[ 3 + k*5], "%04d", start + i);

          //assume boundary is byte
          buf[ 7 + k++*5] = ImBasGetDelimiter(); //';';
        }
        else //skip address, adjust length with one element
        {

          sprintf( &tmpbuf[ 1], "%02d", --dslen);
          buf[ 1] = tmpbuf[ 1];
          buf[ 2] = tmpbuf[ 2];
        }
      }

      retlen = (dslen * 5) + 4;

      //erase indicator
      buf[ retlen-1] = del;
      break;

    case DSW_LIFESIGN:
    case DSW_EXCEPTION:
      retlen = 0;
      break;

    default:       
      retlen = -1;
      break;
  }

  //copy internal buffer to user buffer, doing so 
  //allows the user to specify 1 buffer for input/output
  //if (retlen > 0) memcpy( &buffer[ PROT_HEADER_LEN], buf, retlen);

  return retlen; //return data length
} //ImBasImx2Data


/////////////////////////////////////////////////////////////////////////////
// Descripton: Im-bas protocl data  converson to imx data. The raw data in 'source'
// is converted to IMX format and stored in 'dest'. These buffers may overlap or be 
// the same memory location.
// 
// Parameter: 
// *dest - pointer to destination databuffer, this is the converted 
//         data in imx-format.
// *source - pointer to source databauffer, this is the raw data in 
//           IM-BAS format.
// *ds - pointer to an imx dataset structure.
// *datalen -
// start - Start address of the data, determined before calling this function.
//         This address can be the same as the start address of the dataset, but will
//         not be if the complete dataset is received in more then one TM-buffer.
// *length - Output parameter of the function, the actual length of the received 
//           data is stored.
//
// Returns: Error code, or success as a zero value.
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ImBasData2Imx( char *dest, char *source, IMXDS *ds, int start,
                             int *length)
{

  int dlen = ImBasGetDataLength( source, PROT_HEADER_LEN); //get the data length
  int dtype = 0;
  int dsize = 0;
  //use temp buffer, this allows source and destination to overlap
  char buf[ IMX_BUFFER_SIZE]; 
  char tmp[ 100];
  short itmp = Imx_Get_Boundary( ds);
  int dslength = Imx_Get_Len( ds);
  long ltmp;
  double dtmp;
  CString str;
  int mystart = 0x7fffffff;
  int mymax = 0;
  int DataType = (CDatasetObject *)Imx_Get_Udata_Ptr( ds) ? ((CDatasetObject *)Imx_Get_Udata_Ptr( ds))->GetType() % 10 : -1;

  
  //get the data type
  switch (source[ PROT_HEADER_LEN])
  {

    case 'B': dtype = IMX_BYTE_BND; dsize = 1; break;
    case 'I': dtype = IMX_WORD_BND; dsize = 3; break;
    case 'L': dtype = IMX_LONG_BND; dsize = 6; break;
    case 'R': dtype = IMX_DBL_BND; dsize = 17; break;
    case 'S': dtype = PROT_STRING_SIZE; dsize = PROT_STRING_SIZE; 

              //make sure the boundary is long
              if (Imx_Get_Boundary( ds) != IMX_LONG_BND)
                Imx_Set_Boundary( ds, IMX_LONG_BND);
              break;
  }

  //check if data types match
  switch (DataType)
  {

    case 0: //boolean data
      if (source[ PROT_HEADER_LEN] != 'B') return ERR_DATATYPE_INV;
      break;
       
    case 1: //integer data
      if (source[ PROT_HEADER_LEN] != 'I') return ERR_DATATYPE_INV;
      break;

    case 2: //long data
      if (source[ PROT_HEADER_LEN] != 'L') return ERR_DATATYPE_INV;
      break;

    case 3: //real data
      if (source[ PROT_HEADER_LEN] != 'R') return ERR_DATATYPE_INV;
      break;

    case 4: //string data
      if (source[ PROT_HEADER_LEN] != 'S') return ERR_DATATYPE_INV;
      break;

    default: return ERR_UNKNOWN_DATATYPE;
  }

  //calculate the number of elements, return if there is no data received
  if ((dlen = (dlen - 3) / (dsize + 5)) <= 0) return *length = 0;

  //start with a minimum length of 1 element
  if (*length <= 0) *length = 1 * (dtype / Imx_Get_Boundary( ds));

  //initialise the internal buffer to default values
  ImBasInitImxData( buf, IMX_BUFFER_SIZE, source[ PROT_HEADER_LEN]);

  //find the length of the dataset and convert the data
  for (int i = 0; i < dlen; i++)
  {

    strncpy( tmp, &source[ PROT_HEADER_LEN + 3 + (i * (5 + dsize))], PROT_INDEX_SIZE); 
    tmp[ PROT_INDEX_SIZE] = '\0';

    int idx = atoi( tmp); //address
    ltmp = idx * (dtype / (int)Imx_Get_Boundary( ds));

    //if the address lower then requested, skip and continue
    if (start > ((idx * dtype) / Imx_Get_Boundary( ds))) continue;

    if (((idx * dtype) / Imx_Get_Boundary( ds)) - start >= dslength)
      continue;

    //calculated start address must be above start of dataset
    ltmp = ltmp < start ? start : ltmp; 
    mystart = ltmp < mystart ? ltmp : mystart;

    //max address in received data
    mymax = ltmp > mymax ? ltmp : mymax;

    *length = (ltmp - start + (dtype / Imx_Get_Boundary( ds))) > *length ? 
      ltmp - start + (dtype / Imx_Get_Boundary( ds)) : *length;


    switch (source[ PROT_HEADER_LEN])
    {

      case 'B':
        buf[ (idx - start) * dtype] = source[ PROT_HEADER_LEN + 7 + (i * (5 + dsize))] - '0';
        break;

      case 'I':
        strncpy( tmp, &source[ PROT_HEADER_LEN + 7 + (i * (5 + dsize))], dsize);
        tmp[ dsize] = '\0';
        itmp = atoi( tmp);
        Imx_Set_Word( ds, (u16 *)&buf[ (idx - start) * Imx_Get_Boundary( ds)], (u16 *)&itmp);
        break;

      case 'L':
        strncpy( tmp, &source[ PROT_HEADER_LEN + 7 + (i * (5 + dsize))], dsize);
        tmp[ dsize] = '\0';
        ltmp = atol( tmp);
        Imx_Set_Long( ds, (u32 *)&buf[ (idx - start) * dtype], (u32 *)&ltmp);
        break;

      case 'R':
        strncpy( tmp, &source[ PROT_HEADER_LEN + 7 + (i * (5 + dsize))], dsize);
        tmp[ dsize] = '\0';
        dtmp = atof( tmp);

        if (DebugForReals())
          CodeTrace( "REAL_INFO", 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, tmp, dtmp);

        Imx_Set_Double( ds, (double *)&buf[ (idx - start) * dtype], (double *)&dtmp);
        break;

      case 'S':
        strncpy( tmp, &source[ PROT_HEADER_LEN + 7 + (i * (5 + dsize))], dsize);
        tmp[ dsize] = '\0'; //copy string to tmp

        //do the trimming of spaces
        str = tmp;
        if (GetSpaceTrimLeft())str.TrimLeft();
        if (GetSpaceTrimRight()) str.TrimRight();

        int xstart = start * Imx_Get_Boundary( ds);

        //copy the string to the imx buffer
        int ix = (idx * (dtype)) - xstart + str.GetLength();
        memset( &buf[ (idx * dtype) - xstart + str.GetLength()], 0, dsize - str.GetLength());
        strncpy( &buf[ (idx * dtype) - xstart], str, str.GetLength()); 
        break;
    }
  }

  //copy the buffer
  if (dlen > 0)
  {

    //received no data fro dataset
    if ((mystart == 0x7fffffff) || (mystart < start)) 
      return ERR_INVALID_ADR;

    //quit if data does not fit in the buffer
    if (mystart - start < 0) 
      return ERR_INVALID_ADR;

    //if (PROT_HEADER_LEN + mystart - start + (dtype * dlen) > IMX_BUFFER_SIZE) return ERR_INVALID_ADR;
    if ((mymax - start + dtype / Imx_Get_Boundary( ds)) * Imx_Get_Boundary( ds) > IMX_BUFFER_SIZE)
      return ERR_INVALID_ADR;

    //preserve the space for the IM-BAS header
    memcpy( &dest[ PROT_HEADER_LEN + (mystart - start)*Imx_Get_Boundary( ds)], 
            &buf[ (mystart - start) * Imx_Get_Boundary( ds)], 
            (mymax - start + dtype / Imx_Get_Boundary( ds)) * Imx_Get_Boundary( ds));
            //dtype * dlen);
  }

  return 0;
} //ImBasData2Imx


/////////////////////////////////////////////////////////////////////////////
// Descripton: Calculate the maximum length of a message, for which the response
// size will less then 999 data bytes. 
// 
// Parameters: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::ImBasMaxLength( int type)
{


  //calculate the maximum length for response size <= 999 data bytes
  switch (type)
  {
  
    case DS_COMMAND_BOOL:
    case DS_COMMAND_INT:
    case DS_PARAMETER_BOOL:
    case DS_PARAMETER_INT:
    case DS_STATUS_BOOL:
    case DS_STATUS_INT:
    case DS_STATINFO_BOOL:
    case DS_STATINFO_INT:
    case DS_STATDEL_BOOL:
    case DS_STATDEL_INT:
    case DS_EXCEPTION:
    case DS_LIFESIGN: return 99;

    case DS_COMMAND_LONG:
    case DS_PARAMETER_LONG:
    case DS_STATUS_LONG:
    case DS_STATINFO_LONG:
    case DS_STATDEL_LONG: return 90;

    case DS_COMMAND_REAL:
    case DS_PARAMETER_REAL:
    case DS_STATUS_REAL:
    case DS_STATINFO_REAL:
    case DS_STATDEL_REAL: return 45;

    case DS_COMMAND_CHAR:
    case DS_PARAMETER_CHAR:
    case DS_STATUS_CHAR:
    case DS_STATINFO_CHAR:
    case DS_STATDEL_CHAR: return 39;

    case DSW_COMMAND_BOOL:
    case DSW_COMMAND_INT:
    case DSW_PARAMETER_BOOL:
    case DSW_PARAMETER_INT:
    case DSW_STATUS_BOOL:
    case DSW_STATUS_INT:
    case DSW_STATINFO_BOOL:
    case DSW_STATINFO_INT:
    case DSW_STATDEL_BOOL:
    case DSW_STATDEL_INT:
    case DSW_EXCEPTION:
    case DSW_LIFESIGN: return 99;

    case DSW_COMMAND_LONG:
    case DSW_PARAMETER_LONG:
    case DSW_STATUS_LONG:
    case DSW_STATINFO_LONG:
    case DSW_STATDEL_LONG: return 90;

    case DSW_COMMAND_REAL:
    case DSW_PARAMETER_REAL:
    case DSW_STATUS_REAL:
    case DSW_STATINFO_REAL:
    case DSW_STATDEL_REAL: return 45;

    case DSW_COMMAND_CHAR:
    case DSW_PARAMETER_CHAR:
    case DSW_STATUS_CHAR:
    case DSW_STATINFO_CHAR:
    case DSW_STATDEL_CHAR: return 39;
  }

  return 0;
}


/////////////////////////////////////////////////////////////////////////////
// Description: The function updates the connection state. This connection state 
// is registered for every device, and used to update the a tag which is configured
// for the device to represent the actual conneciton state.
// If this tag is a message tag, three translations are used to convert the state
// from an integer value to a string: DEVICE_NOT_CONNECTED --> CONN_LISTENING = 
// 'Listening', DEVICE_CONNECTED --> CONN_LIFESIGN = 'Connected' and DEVICE_DISCONNECTING
// --> CONN_DICONNECT = 'Disconnecting'.
// 
// Parameter: state - Connection state of the device, this can be: DEVICE_NOT_CONNECTED,
// DEVICE_CONNECTED or DEVICE_DISCONNECTING.

// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CDeviceObject::UpdateConnection( int state)
{ 

  CString xl[5] = {_T("CONN_LISTENING"), _T("CONN_DISCONNECT"), _T("CONN_CONNECT"), 
                   _T("CONN_TCPIP_ONLY"), _T("CONN_LIFESIGN")};


  //update internal state
  m_iConnected = state;
  MyStatus.Connection = m_iConnected;

  //update connection info for device status
  if (state == DEVICE_CONNECTED)
  {

    //get the local IP-address and port number
    struct sockaddr_in sa;
    int len = sizeof( struct sockaddr_in);
    int pret;

    pret = getsockname( GetSocket(), (struct sockaddr *)&sa, &len);
    if (!pret)
    {

      strcpy( MyStatus.LocalIP, inet_ntoa( sa.sin_addr));
      MyStatus.LocalPort = ntohs( sa.sin_port);
    }

    //get the remote IP-address and port number
    len = sizeof( struct sockaddr_in);
    pret = getpeername( GetSocket(), (struct sockaddr *)&sa, &len);
    if (!pret)
    {

      strcpy( MyStatus.RemoteIP, inet_ntoa( sa.sin_addr));
      MyStatus.RemotePort = ntohs( sa.sin_port);
    }
  }

  //convert value to tag value
  if (ConnectionTag.Type() != FL_MESSAGE) 
  {
  
    if (ConnectionTag.Type() == FL_DIGITAL)   
    {

      //tag type is dgitial, convert analog connection status to ON/OFF
      switch( state)
      {

        case DEVICE_CONNECTED:
          state = 1; //connection status is ON
          break;
        default:
          state = 0; //connection status is OFF
          break;
      }
    }

    ConnectionTag = state;
  }
  else 
    ConnectionTag = (char *)((LPCTSTR)theTask->Xlate( xl[ state]));

  //write the 'connection' tag
  ConnectionTag.Write();
}


/////////////////////////////////////////////////////////////////////////////
// Description: Update the status tag of the device
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CDeviceObject::UpdateStatus( int status)
{

  //convert value to tag value
  StatusTag = status;
  
  //write the 'connection' tag
  StatusTag.Write();
} //UpdateStatus


/////////////////////////////////////////////////////////////////////////////
// Description: This function is caled when the driver is stopping, and will
// close all the receive sockets. A receive thread exits when its socket is
// closed, and updates the count of registered sockets for the driver. 
// This function will block until all the receive threads have ended and the 
// number of registered receive sockets is zero.
// Note that this function should be called after calling the CancelListen 
// function. If receive threads are closed before th elisten is cancelled the
// TM will try to reconnect, and succeed with the connection because the listen 
// thread will accept the connection.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::CancelReceiving( int wait)
{

  int MaxWaitTime = 5000;  //wait 10 seconds at most
  int SleepTime = 10;


  //stop all driver parts
  for (int i = 0; i < m_aDriver.GetSize(); i++)
  {

    CDriverObject *driver = m_aDriver.GetAt( i);
    CodeTrace( _T("RECV_COUNT"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, driver->GetReceiveSocketCount());

    //close all sockets for driver object
    driver->CloseAllReceiveSockets();

    //wait until all 
    for (int i = 0; i < MaxWaitTime/SleepTime; i++)
    {

      if (driver->GetReceiveSocketCount()) FlSleep( 10);
      else break;
    }

    CodeTrace( _T("RECV_COUNT"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, driver->GetReceiveSocketCount());
    int ret;

    //FlSleep( 5000);

    //timeout occurred?
    if (driver->GetReceiveSocketCount())
    {

      //there is a timeout, and there can be still threads active,
      //threads which do have a connection are terminated
      for (int i = 0; i < m_aDevice.GetSize(); i++)
        if (m_aDevice.GetAt( i)->GetReceiveThread())
        {

          ret = TerminateThread( m_aDevice.GetAt( i)->GetReceiveThread(), -1);
          if (!ret) ret = GetLastError();
          CodeTrace( _T("TERM_RECV"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, m_aDevice.GetAt( i)->GetName(), ret);
        }
    }
  }
} //CancelReceiving


/////////////////////////////////////////////////////////////////////////////
// Description: Get the number of TCP/IP packets for the specified dataset type
// and length of the imx dataset. IMX  specifies a length of a dataset, the
// message type determines how many data bytes are used for one field index, the 
// function returns the number of packets the dirver has to send, to limit the 
// reply to a maximum of 999 data bytes.
// 
// Parameter: len - IMX dataset length.
// Parameter: type - IM-BAS protocl message type.
// 
// Returns: The number of packets for the specified message type with the given 
// length.
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int MyFlTask::GetPacketCount(int len, int type)
{

  int corr = 1; //correction factor, only used for strings


  switch (type)
  {
  
    case DS_COMMAND_CHAR:
    case DS_PARAMETER_CHAR:
    case DS_STATUS_CHAR:
    case DS_STATINFO_CHAR:
    case DS_STATDEL_CHAR: 
    case DSW_COMMAND_CHAR:
    case DSW_PARAMETER_CHAR:
    case DSW_STATUS_CHAR:
    case DSW_STATINFO_CHAR:
    case DSW_STATDEL_CHAR: corr = PROT_STRING_SIZE/IMX_LONG_BND; break;
  }

  int rep = (len / corr) / ImBasMaxLength( type);
  int rest = (len / corr) % ImBasMaxLength( type);
  if (rest != 0) rep++; //the minimum value for rep is 1

  return rep;
} //GetPacketCount


/////////////////////////////////////////////////////////////////////////////
// Description: 
// 
// Parameter: *buf - Pointer to buffer to initialize. Value to initialise is 
// set in the ini-file of the IM-BAS driver, and defaults to zero.
// Parameter: len - length of the buffer to initialise, the length is given in
// bytes.
// Parameter: type - The type of the dataset, this type defines the boundary 
// and data-type of the the dataset. With this type the converson of the initial
// value is known: from integer to the data-type of the dataset. Normally the type
// is directly retrieved form the received message an dhas on the values: 'B', 'I', 
// 'L', 'R' or 'S'.
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::ImBasInitImxData(char *buf, int len, char type)
{

  int step = 1;
  char value[4];
  void *p = value;


  //default is byte stream
  value[0] = (char)GetNonExistentValue();

  switch (type)
  {

    case 'I':
      step = 2; 
      *((int *)value) = (int)GetNonExistentValue();
      break;

    case 'L': 
      step = 4; 
      *((long *)value) = (long)GetNonExistentValue();
      break;

    case 'R':
      step = 4; 
      *((float *)value) = (float)GetNonExistentValue();
      break;

    case 'S':
      value[0] = 0;
      break;
  }

  //fill buffer
  for (int i = 0; i < len; i += step)
  {

    for( int k = 0; k < step; k++)
      buf[ i + k] = value[ k];
  }
} //ImBasInitImxData


/////////////////////////////////////////////////////////////////////////////
// Description: Convert from UTC to local time, time is asumed to be of the 
// form: hhmmss, that is six bytes. The ini-file of the IM-BAS driver has a 
// key., which tells the driver to convert time from UTC to local (or not). The
// time converted is the time in an exception message.
// Just befor th etime th edate is assumed to be of the form: YYYYMMDD
// 
// Parameter: *buf - Buffer in which a six byte time is stored of the form:
// hhmmss. This time is converted from UTC to local time.
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::ImBasUtc2Local(char *buf)
{

  //first check if conversion is needed
  if (!ImBasUseLocalTime()) return;

  //conversion is needed, build utc time strucuture
  SYSTEMTIME TmUTC, TmLocal;

  TmUTC.wYear = (buf[0] - '0') * 1000 + (buf[1] - '0') * 100 + (buf[2] - '0') * 10 + buf[3] - '0';
  TmUTC.wMonth = (buf[4] - '0') * 10 + buf[5] - '0';
  TmUTC.wDay = (buf[6] - '0') * 10 + buf[7] - '0';
  TmUTC.wHour = (buf[8] - '0') * 10 + buf[9] - '0';
  TmUTC.wMinute = (buf[10] - '0') * 10 + buf[11] - '0';
  TmUTC.wSecond = (buf[12] - '0') * 10 + buf[13] - '0';
  TmUTC.wMilliseconds = 0;
  TmUTC.wDayOfWeek = -1; //undefined

  //convert utc --> local
  SystemTimeToTzSpecificLocalTime( NULL, &TmUTC, &TmLocal);

  buf[0] = TmLocal.wYear / 1000;
  buf[1] = (TmLocal.wYear % 1000) / 100;
  buf[2] = (TmLocal.wYear % 100) / 10;
  buf[3] = TmLocal.wYear % 10;
  buf[4] = TmLocal.wMonth / 10;
  buf[5] = TmLocal.wMonth % 10;
  buf[6] = TmLocal.wDay / 10;
  buf[7] = TmLocal.wDay % 10;
  buf[8] = TmLocal.wHour / 10;
  buf[9] = TmLocal.wHour % 10;
  buf[10] = TmLocal.wMinute / 10;
  buf[11] = TmLocal.wMinute % 10;
  buf[12] = TmLocal.wSecond / 10;
  buf[13] = TmLocal.wSecond % 10;

  //make ascii characters
  for (int i = 0; i < 14; i++)
    buf[ i] += '0';
} //ImBasUtc2Local


/////////////////////////////////////////////////////////////////////////////
// Description: Convert from local time to UTC, time is asumed to be of the 
// form: hhmmss, that is six bytes. The ini-file of the IM-BAS driver has a 
// key. Which tells the driver to convert time from UTC to local (or not). The
// time converted is the time in an exception message.
// Just befor the time the date is assumed to be of the form: YYYYMMDD
// 
// Parameter: *buf - Buffer in which a six byte time is stored of the form:
// hhmmss. This time is converted from UTC to local time.
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::ImBasLocal2Utc(char *buf)
{

  //first check if conversion is needed
  if (ImBasUseLocalTime()) return;

  //conversion is needed, build local time strucuture
  SYSTEMTIME TmUTC, TmLocal;
  FILETIME FtUTC, FtLocal;
  //TIME_ZONE_INFORMATION TmZone;

  TmLocal.wYear = (buf[0] - '0') * 1000 + (buf[1] - '0') * 100 + (buf[2] - '0') * 10 + buf[3] - '0';
  TmLocal.wMonth = (buf[4] - '0') * 10 + buf[5] - '0';
  TmLocal.wDay = (buf[6] - '0') * 10 + buf[7] - '0';
  TmLocal.wHour = (buf[8] - '0') * 10 + buf[9] - '0';
  TmLocal.wMinute = (buf[10] - '0') * 10 + buf[11] - '0';
  TmLocal.wSecond = (buf[12] - '0') * 10 + buf[13] - '0';
  TmLocal.wMilliseconds = 0;
  TmLocal.wDayOfWeek = -1; //undefined

  //get local time zone
  //GetTimeZoneInformation( &TmZone);

  //convert local --> utc
  SystemTimeToFileTime( &TmLocal, &FtLocal);
  LocalFileTimeToFileTime( &FtLocal, &FtUTC);
  FileTimeToSystemTime( &FtUTC, &TmUTC);
  //TzSpecificLocalTimeToSystemTime( &TmZone, &TmLocal, &TmUTC);

  buf[0] = TmUTC.wYear / 1000;
  buf[1] = (TmUTC.wYear % 1000) / 100;
  buf[2] = (TmUTC.wYear % 100) / 10;
  buf[3] = TmUTC.wYear % 10;
  buf[4] = TmUTC.wMonth / 10;
  buf[5] = TmUTC.wMonth % 10;
  buf[6] = TmUTC.wDay / 10;
  buf[7] = TmUTC.wDay % 10;
  buf[8] = TmUTC.wHour / 10;
  buf[9] = TmUTC.wHour % 10;
  buf[10] = TmUTC.wMinute / 10;
  buf[11] = TmUTC.wMinute % 10;
  buf[12] = TmUTC.wSecond / 10;
  buf[13] = TmUTC.wSecond % 10;

  //make ascii characters
  for (int i = 0; i < 14; i++)
    buf[ i] += '0';
} //ImBasLocal2Utc


/////////////////////////////////////////////////////////////////////////////
// Description: Clear RX-buffer, after the function call no message are left
// in the input buffer.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::ImBasPurgeRX( SOCKET sock)
{

  int error;
  FD_SET rfds;
  TIMEVAL t = { 0, 0};
  TIMEVAL *tval = NULL;
  char buffer = 0;


  //read all characters from input buffer
  while (buffer != ImBasGetTerminator())
  {

    //wait for data
    FD_ZERO( &rfds);
    FD_SET( sock, &rfds);
    t.tv_sec = 0;
    t.tv_usec = 0;
    error = select( 0, &rfds, NULL, NULL, &t);
      
    if (!error || error == SOCKET_ERROR)
    {

      //trace the winsock error
      if (error == SOCKET_ERROR)
        CodeTrace( _T("SOCKET_ERROR"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, WSAGetLastError());

      break;
    }

    recv( sock, &buffer, 1, 0);
  } 
} //ImBasPurgeRX



/////////////////////////////////////////////////////////////////////////////
// Description: Runtime manamger update function, error status is only set if
// the error code is not blocked for displaying the error status. The error
// status is blocked in the ini file of the module.
// 
// Parameter: 
// 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void MyFlTask::MyRunTimeManager( int ErrorCode, int Mode, char *Xlate, ...)
{

  va_list vl;
  char    buf[ 2*MAX_MSG];
  char    *b = Xlate ? buf : NULL;


  //check the mode, and reset to normal running if error is blocked
  if ((ErrorCode > 0) && (ErrorCode <= MAX_ERROR))
  {

    //check if error message is serious or just a warning
    if (!m_iRunTimeError[ ErrorCode]) 
    {

      Mode = (m_iPreviousState > FLS_INACTIVE) ? m_iPreviousState : FLS_ACTIVE;

   	  //give only message if the new mode will be error and not running
	    if ((Mode == FLS_ACTIVE) && (m_iPreviousState == FLS_ERROR)) return;
    }
  }
  else
  {

    //keep current state
    if (ErrorCode == 0) Mode = m_iPreviousState;
  }

  //save previous mode
  m_iPreviousState = Mode;

  //get the argument pointer
  va_start( vl, Xlate);

  if (Xlate != NULL) vsprintf( buf, fl_xlate( Xlate), vl);
  RunTimeManager( Mode, b);

  va_end( vl);
} //MyRunTimeManager


/////////////////////////////////////////////////////////////////////////////
// Description: Check if a data value has limit restrictions set to it's value.
// Calling this function will reset the error condition if present.
// 
// Parameter: 
// 
// Returns: A value not equal to zero is returned in case one or more data values
// are change to their lower or upper limiot before sending them to the IM.
// Otherwise zero is returned.
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
int CDatasetObject::GetErrorOnLimit()
{
  
	int x = theTask->GetErrorOnLimit() ? m_iLimitErrorActive: 0;
	m_iLimitErrorActive = 0; 
  return x;
}


/////////////////////////////////////////////////////////////////////////////
// Description: Activate error on dataset caused by limiting a data value.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CDatasetObject::SetErrorOnLimit() 
{ 

  m_iLimitErrorActive = -1;
}


/////////////////////////////////////////////////////////////////////////////
// Description: Calculate actual dataset length, by using the range definition.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CDatasetObject::EvaluateRange() 
{ 

  int iLen;
  int i;
  int s;
  int t;
  int k;
  char *b;

  //remove leading and trailing spaces
  m_sRange.TrimRight();
  m_sRange.TrimLeft();
  iLen = m_sRange.GetLength();

  //clear any previous array
  if (m_iRange) free( m_iRange);
  m_iRange = NULL;
  m_iRangeLength = 0;
  b = m_sRange.GetBuffer(1);

  for( i = 0, s = -1, t = -1; i <= iLen; i++)
  {

    if ((b[i] >= '0') && (b[i] <= '9'))
    {
      if (s < 0) s = 0;
      s = s * 10 + (b[i] - '0');
    }
    else
    {

      //there is a range if the char is '-'
      if (b[i] == '-')
      {

        t = s;
        s = -1;
        continue;
      }

      if ((t > -1) && (s > t))
      {

        m_iRange = (short *)realloc( m_iRange, sizeof(short) * (m_iRangeLength + (s - t + 1)));       

        for( k = 0; k < (s - t + 1); k++)
          m_iRange[ m_iRangeLength++] = t + k;

        s = -1;
        t = -1;
      }
      else
      {

        if (s < 0) continue;
        //add only one short or id
        m_iRange = (short *)realloc( m_iRange, sizeof(short) * (++m_iRangeLength));
        m_iRange[ m_iRangeLength - 1] = s;
        s = -1;
        t = -1;
      }
    }
  }

  //allocate for start and length of dataset
  if (m_iRange == NULL) 
  {

    m_iRangeLength = GetLength();
    m_iRange = (short *)realloc( m_iRange, sizeof(short) * GetLength());

    for (i = 0; i < GetLength(); i++)
      m_iRange[ i] = GetStart() + i;
  }
} //EvaluateRange


/////////////////////////////////////////////////////////////////////////////
// Description: Reset all the device information.
// 
// Parameter: 
// 
// Returns: 
// 
// Comment: 
// 
/////////////////////////////////////////////////////////////////////////////
void CDeviceObject::ClearStatusVariables()
{

  //initialise statusstructure
  strcpy( MyStatus.Device, Name);
  strcpy( MyStatus.LocalIP, "0.0.0.0");
  strcpy( MyStatus.RemoteIP, "0.0.0.0");
  MyStatus.LocalPort = 0;
  MyStatus.RemotePort = 0;
  MyStatus.Connection = 0;
  for (int i = 0; i < 3; i++)
  {

    MyStatus.Count[ i] = 0;
    MyStatus.DataTime[ i] = 0;
    MyStatus.WaitTime[ i] = 0;
  }
}


////////////////////////////////////////////////////////////////////////////////
//Description: Lock a deivce object, access to the object is granted to one 
//thread only. Device-lock is released with the function 'Device->Release'.
//
//Parameter: 
//
//Returns:
//
//Comment:
//
////////////////////////////////////////////////////////////////////////////////
void CDeviceObject::Lock()
{
  theTask->CodeTrace( _T("SEND_LOCK"), 5, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, GetName());
  if (m_semWinSock) WaitForSingleObject( m_semWinSock, INFINITE);
}


////////////////////////////////////////////////////////////////////////////////
//Description: Releaese a locked device, a device is locked with the function
//'Device->Lock', after locking only one thread is granted access to the device.
//This function releases a lock set on the device, note that any thread can 
//release a lock set on the device. If a device needs to wait untill a present
//lock is released (or no lock is placed on the deivce) the function 'Device->Lock'
//should be used.
//
//Parameter: 
//
//Returns:
//
//Comment:
//
////////////////////////////////////////////////////////////////////////////////
void CDeviceObject::Release()
{

  theTask->CodeTrace( _T("RECV_LOCK"), 5, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, GetName());
  if (m_semWinSock) ReleaseSemaphore( m_semWinSock, 1, NULL);
}

////////////////////////////////////////////////////////////////////////////////
//Description: 
//
//Parameter: 
//
//Returns:
//
//Comment:
//
////////////////////////////////////////////////////////////////////////////////
CDeviceObject::~CDeviceObject()
{

  theTask->CodeTrace( _T("DEV_DESTRUCT"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, GetName());

  //close and reset all semaphore handles  
  for (int i = 0; i < FNC_COUNT; i++)
  {
    CloseHandle( m_semFunc[ i]);
    //m_semFunc[ i] = NULL;
  }

  CloseHandle( m_semWinSock);
  CloseHandle( m_semImxDataset);

  //m_semWinSock = NULL;
  //m_semImxDataset = NULL;
} //CDeviceObject::~CDeviceObject()


////////////////////////////////////////////////////////////////////////////////
//Description: 
//
//Parameter: 
//
//Returns:
//
//Comment:
//
////////////////////////////////////////////////////////////////////////////////
void CDriverObject::CloseAllReceiveSockets()
{ 

  LockSockList();

  for (int i = 0; i < GetReceiveSocketCount(); i++)
  {
    closesocket( m_aRecvSockets.GetAt( i));
    theTask->CodeTrace( _T("CLOSE_SOCK"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, m_aRecvSockets.GetAt( i), GetReceiveSocketCount());
  }

  ReleaseSockList();
} //CDriverObject::CloseAllReceiveSockets()


////////////////////////////////////////////////////////////////////////////////
//Description: 
//
//Parameter: 
//
//Returns:
//
//Comment:
//
////////////////////////////////////////////////////////////////////////////////
void CDriverObject::RemoveReceiveSocket( SOCKET sock)
{
  int ret = -1;


  LockSockList();

  int idx = FindReceiveSocket( sock, 0);

  theTask->CodeTrace( _T("REM_SOCK"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, sock, idx);
  if ((idx >= 0) && (idx < m_aRecvSockets.GetSize()))
    m_aRecvSockets.RemoveAt( idx);

  m_aRecvSockets.FreeExtra();

  ReleaseSockList();
} //CDriverObject::RemoveReceiveSocket( SOCKET sock)


////////////////////////////////////////////////////////////////////////////////
//Description: 
//
//Parameter: 
//
//Returns:
//
//Comment:
//
////////////////////////////////////////////////////////////////////////////////
int CDriverObject::AddReceiveSocket( SOCKET sock)
{

  int ret = -1;


  LockSockList();
  ret = m_aRecvSockets.Add( sock);
  theTask->CodeTrace( _T("ADD_SOCK"), 4, EVENTLOG_INFORMATION_TYPE, TRACE_TEXT, NULL, 0, sock, ret);
  ReleaseSockList();
  return ret;
} //CDriverObject::AddReceiveSocket( SOCKET sock)


////////////////////////////////////////////////////////////////////////////////
//Description: 
//
//Parameter: 
//
//Returns:
//
//Comment:
//
////////////////////////////////////////////////////////////////////////////////
void CDeviceObject::Disconnect() 
{
  
  theTask->CodeTrace( _T("DISCONN_SOCK"), 3, EVENTLOG_INFORMATION_TYPE, 
                      TRACE_TEXT, NULL, 0, GetSocket(), (LPCTSTR)GetName());
  closesocket( GetSocket());
  ClearStatusVariables();
  SetSocket( INVALID_SOCKET);};


////////////////////////////////////////////////////////////////////////////////
//Description: 
//
//Parameter: 
//
//Returns:
//
//Comment:
//
////////////////////////////////////////////////////////////////////////////////
int CDeviceObject::GetTimeFormat()
{

  return m_iTimeStamp;
}

////////////////////////////////////////////////////////////////////////////////
//Description: 
//
//Parameter: 
//
//Returns:
//
//Comment:
//
////////////////////////////////////////////////////////////////////////////////
int MyFlTask::GetResponseTimeout()
{

  return m_iResponseTimeout;
}
