/*
 ******************************************************************************
 *  Copyright 1998 DeltaLink bv. All Rights Reserved.
 ******************************************************************************
 *
 * File: sapi_x.c
 *
 * This module contains the communication with FactoryLink.
 *
 */
#ifdef WIN32
#pragma warning( disable : 4121 )

#define NOMSG										/* typedef MSG and associated routines */
#define _OLE2_H_								/* no ole */

#include  <windows.h>
#include  <process.h>

#undef ERROR

#endif


#include  <stdlib.h>
#include  <string.h>
#include  <stdio.h>
#include  <stdarg.h>


#include  <flib.h>						 /* FactoryLink definitions */
#include  <fl_utils.h>
#include  <g_utils.h>

#pragma warning( disable : 4214 )
#include  <imx.h>              /* Mailbox Communication Interface definitions */
#include  <imx_mt.h>           /* Mailbox Communication Interface definitions */
#include  <imx_fmt.h>          /* Mailbox Communication Interface definitions */
#pragma warning( default : 4214 )

#include  <protocol.h>				 /*  DeltaLink SAPI definitions */
#pragma warning( disable : 4115 )
#include  <sapi_s7.h>          /*  Siemens SAPI definitions */
#pragma warning( default : 4115 )
#include  <sapi.h>						 /*  DeltaLink SAPI definitions */

#include  <version.h>					 /*  DeltaLink SAPI definitions */

HWND	Hwnd;
ord16 Rcv_orderid;

/* defines for encoded data */

#define E_FLAG           'M'        /* merker */
#define E_DB             'D'        /* data block */
#define E_DX             'X'        /* extra data block */
#define E_TIMER          'T'        /* timer */
#define E_COUNTER        'Z'        /* counter */
#define E_OUTPUT         'A'        /* output */
#define E_INPUT          'E'        /* input */
#define E_PERIPHERAL     'P'        /* analog output */
#define E_EPERIPHERAL    'Q'        /* extra analog output */
#define E_SYSTEM         'S'        /* system words (BS) */

char s7_type[] = { 'D', 'M', 'E', 'A', 'P', 'Z', 'T', 'S', 'L', 'X', 'X', 'Q'};

#define E_BIT            'F'        /* bit */

#define E_BYTE           'B'        /* byte */
#define E_DL             'L'        /* left byte */
#define E_DR             'R'        /* right byte */
#define E_WORD           'W'        /* word */
#define E_LONG           'D'        /* long */

#define H1_DB      1             /* data block */
#define H1_FY      2             /* flag byte  */
#define H1_IY      3             /* input byte  */
#define H1_QY      4             /* output bytes  */
#define H1_PY      5             /* peripheral bytes  */
#define H1_CW      6             /* counter words  */
#define H1_TW      7             /* timer words  */
#define H1_BW      8             /* system words (BS)  */
#define H1_AW      9             /* absolute address words  */
#define H1_DX      10						 /* expanded data block words (S5-135U)  */
#define H1_DE      16						 /* external data block words (S5-150U) */
#define H1_PE      17            /* external peripheral bytes (S5-150U) */

char *S7_var[ ] = {
										"",
										"DB%d,B%d,%d",		/* DB */
										"MB%d,%d",				/* Flag Byte */
										"EB%d,%d",				/* Input Byte */
										"AB%d,%d",				/* Output Byte */
										"PA%d,%d",				/* Peripheral Output */
										"C%d,%d",					/* Counter */
										"T%d,%d",					/* Timer */
										"",
										"DB%d,X%d.%d",		/* DB */
										"MB%d.%d",				/* Flag Byte */
										"EB%d.%d",				/* Input Byte */
										"AB%d.%d",				/* Output Byte */
										"PA%d.%d",				/* Peripheral Output */
										"C%d.%d",					/* Counter */
										"T%d.%d",					/* Timer */
									};

#define X_OFFSET	8

int cp_write			( IMXDS * );
int cpu_write			( IMXDS * );

int find_global_ds_index( IMXDS *ds)
{
  int i;

  for (i= 0; i < Nr_ds; i++)
  {
    if (ds == Ds[i])
    {
      return i;
    }
  }
  return -1;
} //find_global_ds_index


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_dispatch
 *
 * PUPRPOSE: The SAPI DISPATCH function.
 *
 -----------------------------------------------------------------------------*/
int sapi_dispatch ( ord32 cp_descr )
{
	DEVICE  *device;
  //DS_INFO *ds_info;
  char    *value_array[1];
	int			fnc, i;
	ord16   ds_index,
          cref,
					order_id;
	u16 		error = 0;
	int32		max,
          result,
          len,
          r_id,
	        msg;

	/*
	 * retrieve event and command information
	 */
	msg = s7_receive ( cp_descr, &cref, &order_id );
  debug_log( &Task_win, "Windows message for descr: %ld; cref: %d; message = %ld ", 8, cp_descr, cref, msg );

  /* serach for the command */
	if ( search_device( cp_descr, cref, &device ) != GOOD )
	{
		device = NULL;
		return FL_ERROR;
	}

  /*
   * check what kind of message came in
   */
	switch ( msg )
	{
		case S7_NO_MSG:

      /* 
       * In case the connection of an incoming request was OK the initiate
       * a BRECV function
       */
#ifdef PASSIVE
      fnc = IMX_RCV;
			if ( ( fnc == IMX_RCV ) &&
           ( device->connect[ IMX_RCV ] == CS_CONNECTED ) &&
           ( !device->brcv[ IMX_RCV ] )
         )
			{
					/* we have a connection initiilaise just once the receive of data ... */

					if ( s7_brcv_init ( cp_descr,
															cref,
															( ord32)( IMX_RCV + 1 )) != S7_OK )
					{


            fl_enter_critical( FLS_SIGNAL );
            fl_status( &Task_win, "COM_INIT_RCV", FLS_ERROR, device->name, 
                       s7_last_detailed_err_no() );
            fl_leave_critical( FLS_SIGNAL );

						/* we can not receive, this is unacceptable */
						sapi_disconnect( device, IMX_RCV );

						return GOOD;
					}
					else
            device->brcv[ IMX_RCV ] = 1;
			}
#endif
			break;

		case S7_AWAIT_INITIATE_CNF:

			/* mark device as connected */
      if ( s7_get_await_initiate_cnf() != S7_OK )
      {

        fl_enter_critical( FLS_SIGNAL );
        fl_status( &Task_win, "COM_REJECT", FLS_ERROR, s7_last_detailed_err_no(), 
                   device->name);
        fl_leave_critical( FLS_SIGNAL );

				s7_abort( cp_descr, cref );
      }
			break;

		case S7_INITIATE_IND:

			debug_log( &Task_win, "Connection indication %s ", 2, device->name );
			sapi_connect_rsp( device );
			break;

		case S7_ABORT_IND:
			
			debug_log( &Task_win, "Abort indication %s, #%d ", 2, device->name, 
                 s7_last_detailed_err_no() );

			if ( s7_get_abort_ind() != S7_OK )
      {

        fl_enter_critical( FLS_SIGNAL );
        fl_status( &Task_win, "COM_ABORT", FLS_ERROR, s7_last_detailed_err_no(), 
                   device->name);
        fl_leave_critical( FLS_SIGNAL );
      }

			/* mark device as disconnected */
			device->connect = CS_DISCONNECTED;

      /*
       * also disable the connection so all incoming request will be bounced back to the decoder.
       */
      device->disable[ IMX_READ  ][ INTERNAL ] = 1;
      device->disable[ IMX_WRITE ][ INTERNAL ] = 1;

			/*
			 * reset the outstandig cyclic reads
			 */
      device->cyclic_count = 0;
      device->cyclic_pending[0] = 0;
      device->cyclic_pending[1] = 0;
      device->cyclic_head[0] = 0;
      device->cyclic_head[1] = 0;
      device->cyclic_tail[0] = 0;
      device->cyclic_tail[1] = 0;

      fl_enter_critical( FLS_SIGNAL );
      update_count( &Task_win, device, device->cyclic_count );
      fl_leave_critical( FLS_SIGNAL );

			/* send an IMX error back to the decoder */

			for ( i = 0; i < 2; i++)
			{
				if ( device->current[ i ] != -1 )
				{

					if( i == IMX_READ )
						Imx_Set_Type( Ds[ device->current[ i ] ] , IMX_READ_REQ );
					else
						Imx_Set_Type( Ds[ device->current[ i ] ] , IMX_WRITE_REQ );

          fl_enter_critical( FLS_SIGNAL );

					if (( error = (u16)Imx_Send_Error( &Thr_system,
																				Ds[ device->current[ i ] ], 
																				PD_DISCONNECTED )) != GOOD )
						fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, 
                       Imx_Get_Name( Ds[ device->current[ i ] ] ));

          fl_leave_critical( FLS_SIGNAL );

				}
			}

      fl_enter_critical( FLS_SIGNAL );
      update_status( &Task_win, device, PD_DISCONNECTED);
      fl_leave_critical( FLS_SIGNAL );

		  release_connection( device, IMX_READ );
		  release_connection( device, IMX_WRITE );
			break;

		case S7_INITIATE_CNF:

			error = (u16)sapi_connect_rsp( device );
			break;

		case S7_BRCV_IND:

			/*
			 * read the complete data block from the remote partner
			 * this is a fetch or an unsolicited........
			 */
      fnc = IMX_RCV;
      max = Sapi_buf.m_max;
      error = 0;

			if ( ( s7_get_brcv_ind( Sapi_buf.m_ptr,
														  max,
														  (ord32 *)&r_id,
														  (ord32 *)&len ) == S7_ERR ) )
			{

        error = s7_last_detailed_err_no();

        fl_enter_critical( FLS_SIGNAL );
        fl_status( &Task_win, "COM_RECV", FLS_ERROR, error, device->name);
        fl_leave_critical( FLS_SIGNAL );

				return GOOD;
			}

      Sapi_buf.m_len = ( u16 )len;

			/* check if any data has been read */
			if ( !Sapi_buf.m_len )
				error = PD_NO_DATA;

			/* get the function from the header */
			if( ( error = (u16)validate_H1_header( ( H1_HDR * )Sapi_buf.m_ptr, &fnc )) != 0 )
			{

        fl_enter_critical( FLS_SIGNAL );

				fl_status( &Task_win, "COM_READ", FLS_ERROR, error, 
                   Imx_Get_Name( Ds[ device->current[ IMX_READ ] ] ));

				/* send an IMX error back to the decoder */
				if (( error = (u16)Imx_Send_Error( &Thr_system, 
                                           Ds[ device->current[ fnc ] ],
                                           error )) != GOOD )
					fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, 
                     Imx_Get_Name( Ds[ device->current[ IMX_READ ] ]));

        fl_leave_critical( FLS_SIGNAL );
				return GOOD;
			}
			
			/* send data to decoder */
      /* NOTE: fnc is set to IMX_RCV, so all othe roptions are obsolete!!!!!! */
			switch ( fnc )
			{
        case IMX_READ:

          if( !error )
          {
            sapi_read_rsp( Ds[ device->current[ IMX_READ ] ] );
          }
          else
          {

            /* send an IMX error back to the decoder */
            fl_enter_critical( FLS_SIGNAL );

            if (( error = (u16)Imx_Send_Error( &Thr_system, Ds[ device->current[ IMX_READ ]], error )) != GOOD )
              fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( Ds[ device->current[ IMX_READ ]] ));

            fl_leave_critical( FLS_SIGNAL );
          }

          release_connection( device, IMX_READ );
          break;

				case IMX_WRITE:	

          if( !error )
          {
            sapi_write_rsp( Ds[ device->current[ IMX_WRITE ] ] );
          }
          else
          {

            /* send an IMX error back to the decoder */
            fl_enter_critical( FLS_SIGNAL );

            if (( error = (u16)Imx_Send_Error( &Thr_system, Ds[ device->current[ IMX_WRITE ]], error )) != GOOD )
              fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( Ds[ device->current[ IMX_WRITE ]] ));

            fl_leave_critical( FLS_SIGNAL );
          }

          release_connection( device, IMX_WRITE );

          break;
				case IMX_RCV:		

          sapi_recv_rsp( device );
          break;

				default:				
          break;
			}

			break;
	
		case S7_BSEND_CNF:
		
      fnc = IMX_WRITE;

			/* send confirmation */
			if ( s7_get_bsend_cnf() != S7_OK )
      {

        error = s7_last_detailed_err_no();

        fl_enter_critical( FLS_SIGNAL );

        /* send an IMX error back to the decoder */
        if (( error = (u16)Imx_Send_Error( &Thr_system, Ds[ device->current[ fnc ]], error )) != GOOD )
          fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( Ds[ device->current[ fnc ]] ));

        fl_leave_critical( FLS_SIGNAL );
      }
			break;

		case S7_READ_CNF:

			/*
			 * get the received data
			 */
			Sapi_buf.m_len = Sapi_buf.m_max - sizeof( IMXHDR );

			if ( ( s7_get_read_cnf( NULL, 
															&Sapi_buf.m_len, 
															Sapi_buf.m_ptr + sizeof( IMXHDR ) )) == S7_ERR )
			{
				error = s7_last_detailed_err_no();

        fl_enter_critical( FLS_SIGNAL );
				fl_status( &Task_win, "COM_READ", FLS_ERROR, error, device->name );
        fl_leave_critical( FLS_SIGNAL );
				/*return GOOD;*/
			}

			/*
			 * check if the received response is in sync with the pending request
			 */
			if ( device->current[ IMX_READ ] != order_id )
			{

        fl_enter_critical( FLS_SIGNAL );
        if (order_id >= 0)
          fl_status( &Task_win, "Read sync/time-out error %s", FLS_ERROR, 
                     Imx_Get_Name( Ds[ order_id] ));
        else
          fl_status( &Task_win, "Read sync/time-out error", FLS_ERROR);

        fl_leave_critical( FLS_SIGNAL );
			}

      if( !error )
      {
        sapi_read_rsp( Ds[ order_id ] );
        /*sapi_read_rsp( Ds[ device->current[ IMX_READ ] ] );*/
      }
      else
      {

        fl_enter_critical( FLS_SIGNAL );
        
        /* send an IMX error back to the decoder */
        if (( error = (u16)Imx_Send_Error( &Thr_system, Ds[ device->current[ IMX_READ ]], error )) != GOOD )
          fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( Ds[ device->current[ IMX_READ ]] ));

        fl_leave_critical( FLS_SIGNAL );
      }

      release_connection( device, IMX_READ );
  		break;

		case S7_WRITE_CNF:

			/*
			 * get the write confirmation
			 */
			if ( s7_get_write_cnf() != S7_OK )
      {
        error = s7_last_detailed_err_no();

        fl_enter_critical( FLS_SIGNAL );

        /* send an IMX error back to the decoder */
        if (( error = (u16)Imx_Send_Error( &Thr_system, Ds[ device->current[ IMX_WRITE ]], error )) != GOOD )
          fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( Ds[ device->current[ IMX_WRITE ]] ));

        fl_leave_critical( FLS_SIGNAL );
      }
			else
			{
				/*
				 * check if the received response is in sync with the pending request
				 */
				if ( device->current[ IMX_WRITE ] != order_id )
				{

          fl_enter_critical( FLS_SIGNAL );
          fl_status( &Task_win, "Write time-out/sync error %s", FLS_ERROR, Imx_Get_Name( Ds[ device->current[ IMX_WRITE ]] ));
          fl_leave_critical( FLS_SIGNAL );
				}

	      /*sapi_write_rsp( Ds[ device->current[ IMX_WRITE ] ] );*/
	      sapi_write_rsp( Ds[ order_id ] );
			}

      release_connection( device, IMX_WRITE );
			break;

    case S7_CYCL_READ_INIT_CNF:

      /*
       * This will be a critical section because of pointer manipulation
       */
      fl_enter_critical( FLS_SIGNAL );

			/*
			 * get the confirmation of the cyclic read initialisation
			 */
      if( ( result = s7_get_cycl_read_init_cnf()) != S7_OK )
      {

        /*
         * update the status of the device
         */
        update_status( &Task_win, device, PD_START_CYCLIC );
        ds_index = device->cyclic[ CSTART ][ device->cyclic_tail[ CSTART ]]->ds_index;

				fl_status( &Task_win, "S7_CYCL_READ_INIT_CNF", FLS_ERROR, s7_last_detailed_err_no(), Imx_Get_Name( Ds[ ds_index ] ) );

				if ( device->cyclic_tail[ CSTART ] != device->cyclic_head[ CSTART ] )
				{
					POINTER_UP( device->cyclic_tail[ CSTART ] );
					if (device->cyclic_pending[ CSTART ]) device->cyclic_pending[ CSTART ]--;
				}
      }
			else
			{
				/*
				 * start the cyclic read
				 */
				if( ( result = s7_cycl_read_start_req(	cp_descr,
																								cref,
																								order_id )) != S7_OK )
				{
          /*
           * update the status of the device
           */
          update_status( &Task_win, device, PD_START_CYCLIC );

          ds_index = device->cyclic[ CSTART ][ device->cyclic_tail[ CSTART ]]->ds_index;

          fl_status( &Task_win, "S7_CYCL_READ_START_REQ", FLS_ERROR, s7_last_detailed_err_no(), device->dataset[ds_index]->name);

					if ( device->cyclic_tail[ CSTART ] != device->cyclic_head[ CSTART ] )
					{
						POINTER_UP( device->cyclic_tail[ CSTART ] );
						device->cyclic_pending[ CSTART ]--;
					}
				}
			}

			/*
			 * if this cyclic request was not accepted try to set out others
			 */
			if ( result != S7_OK )
			{
				/*
				 * check if there are more cyclic read requests are pending on this device
				 */
				while ( device->cyclic_tail[ CSTART ] != device->cyclic_head[ CSTART ] )
				{
					result = sapi_start_cyclic ( &Task_win, device->cyclic[ CSTART ][ device->cyclic_tail[ CSTART ] ] );

					POINTER_UP( device->cyclic_tail[ CSTART ] );
					if (device->cyclic_pending[ CSTART ]) device->cyclic_pending[ CSTART ]--;

					if ( result == GOOD )
          {
						break;
          }
          else
          {
            /*
             * update the status of the device
             */
            update_status( &Task_win, device, PD_START_CYCLIC );

            ds_index = device->cyclic[ CSTART ][ device->cyclic_tail[ CSTART ]]->ds_index;

  					fl_status( &Task_win, "S7_CYCL_READ_START_REQ", FLS_ERROR, s7_last_detailed_err_no(), 
              device->dataset[ds_index]->name);
          }
				}
			}

			/* 
			 * check if there are stil requests pending
			 */
			if ( device->cyclic_tail[ CSTART ] == device->cyclic_head[ CSTART ] )
				device->cyclic_pending[ CSTART ] = 0;

      /*
       * end of the critical section
       */
      fl_leave_critical( FLS_SIGNAL );
			break;

    case S7_CYCL_READ_START_CNF:

      /*
       * This will be a critical section because of pointer manipulation
       */
      fl_enter_critical( FLS_SIGNAL );

      ds_index = device->cyclic[ CSTART ][ device->cyclic_tail[ CSTART ]]->ds_index;

			if ( device->cyclic_tail[ CSTART ] != device->cyclic_head[ CSTART ] )
			{
				POINTER_UP( device->cyclic_tail[ CSTART ] );
				if (device->cyclic_pending[ CSTART ]) device->cyclic_pending[ CSTART ]--;
			}

			/*
			 * get the start confirmation
			 */
      if( s7_get_cycl_read_start_cnf() != S7_OK )
      {

        /*
         * update the status of the device
         */
        update_status( &Task_win, device, PD_START_CYCLIC );

  			fl_status( &Task_win, "S7_CYCL_READ_START_CNF", FLS_ERROR, s7_last_detailed_err_no(), 
          device->dataset[ds_index]->name);
      }

      debug_log( &Task_win, "Cyclic read %s started", 2, device->dataset[ds_index]->name);

			/*
			 * update the number of resources in use
			 */
			device->cyclic_count++;

			update_count( &Task_win, device, device->cyclic_count );

			/*
			 * check if there are more cyclic read requests are pending on this device
			 */
			while ( device->cyclic_tail[ CSTART ] != device->cyclic_head[ CSTART ] )
			{

				result = sapi_start_cyclic ( &Task_win, device->cyclic[ CSTART ][ device->cyclic_tail[ CSTART ] ] );

				if ( result == GOOD )
        {
					break;
        }
				else
        {
          ds_index = device->cyclic[ CSTART ][ device->cyclic_tail[ CSTART ]]->ds_index;

  				fl_status( &Task_win, "S7_CYCL_READ_START_REQ", FLS_ERROR, s7_last_detailed_err_no(), 
                     device->dataset[ds_index]->name);

				  POINTER_UP( device->cyclic_tail[ CSTART ] );
					if (device->cyclic_pending[ CSTART ]) device->cyclic_pending[ CSTART ]--;

          /*
           * update the status of the device
           */
          update_status( &Task_win, device, PD_START_CYCLIC );
        }
			}

			/* 
			 * check if there are stil requests pending
			 */
			if ( device->cyclic_tail[ CSTART ] == device->cyclic_head[ CSTART ] )
				device->cyclic_pending[ CSTART ] = 0;

      /*
       * end of the critical section
       */
      fl_leave_critical( FLS_SIGNAL );

			break;

    case S7_CYCL_READ_IND:

      debug_log( &Task_win, "Cyclic data %s received (%d)", 2, 
                 Imx_Get_Name( Ds[ order_id ] ), order_id );

      /*
       * get the data of a cyclic read
       */
			value_array[0] = Sapi_buf.m_ptr + sizeof( IMXHDR );
  		Sapi_buf.m_len = Sapi_buf.m_max - sizeof( IMXHDR );

 			if ( s7_get_cycl_read_ind( ( void *)0, &error, &Sapi_buf.m_len, value_array ) != S7_OK )
      {

        fl_enter_critical( FLS_SIGNAL );
        fl_status( &Task_win, "S7_CYCL_READ_IND", FLS_ERROR, s7_last_detailed_err_no(), Imx_Get_Name( Ds[ order_id ] ));
        fl_leave_critical( FLS_SIGNAL );
        break;
      }

      /*
       * check also the result in the result array
       */
      if ( error != S7_RESULT_OK )
      {

        fl_enter_critical( FLS_SIGNAL );
        fl_status( &Task_win, "S7_CYCL_RESULT_IND", FLS_ERROR, error, Imx_Get_Name( Ds[ order_id ] ));
        fl_leave_critical( FLS_SIGNAL );
        break;
      }

      /*
       * send the received data to the ioxlator
       */
      sapi_cycl_read_rsp( Ds[ order_id ], device);
      break;

		case S7_CYCL_READ_DELETE_CNF:

      /*
       * This will be a critical section because of pointer manipulation
       */
      fl_enter_critical( FLS_SIGNAL );

      //ds_index = device->cyclic[ CSTOP ][ device->cyclic_tail[ CSTOP ]]->ds_index;

			if ( device->cyclic_tail[ CSTOP ] != device->cyclic_head[ CSTOP ] )
			{
				POINTER_UP( device->cyclic_tail[ CSTOP ] );
				if (device->cyclic_pending[ CSTOP ]) device->cyclic_pending[ CSTOP ]--;
			}

      /* stop the cyclic receive functionality of this dataset */
			if ( s7_get_cycl_read_delete_cnf() != S7_OK )
      {
        /*
         * update the status of the device
         */
        update_status( &Task_win, device, PD_STOP_CYCLIC );

        ds_index = device->cyclic[ CSTOP ][ device->cyclic_tail[ CSTOP ]]->ds_index;

  			fl_status( &Task_win, "S7_CYCL_READ_DELETE_CNF", FLS_ERROR, 
                   s7_last_detailed_err_no(), Imx_Get_Name( Ds[ ds_index ] ) );
      }
      else
			{
        debug_log( &Task_win, "Cyclic read %s stopped ", 2, Imx_Get_Name( Ds[ order_id ] ) );
			}

			/*
			 * update the number of resources in use
			 */
			device->cyclic_count--;

			update_count( &Task_win, device, device->cyclic_count );

			/*
			 * check if there are more cyclic read requests are pending on this device
			 */
			while ( device->cyclic_tail[ CSTOP ] != device->cyclic_head[ CSTOP ] )
			{
				result = sapi_stop_cyclic ( &Task_win, device->cyclic[ CSTOP ][ device->cyclic_tail[ CSTOP ] ] );

				if ( result == GOOD )
					break;
				else
        {
          /*
           * update the status of the device
           */
          update_status( &Task_win, device, PD_STOP_CYCLIC );

          ds_index = device->cyclic[ CSTOP ][ device->cyclic_tail[ CSTOP ]]->ds_index;

  			  fl_status( &Task_win, "S7_CYCL_READ_DELETE_CNF", FLS_ERROR, s7_last_detailed_err_no(), Imx_Get_Name( Ds[ ds_index ] ) );

				  POINTER_UP( device->cyclic_tail[ CSTOP ] );
					if (device->cyclic_pending[ CSTOP ]) device->cyclic_pending[ CSTOP ]--;
        }
			}

			/* 
			 * check if there are stil requests pending
			 */
			if ( device->cyclic_tail[ CSTOP ] == device->cyclic_head[ CSTOP ] )
				device->cyclic_pending[ CSTOP ] = 0;

      //ds_info = Imx_Get_Udata_Ptr( Ds[ ds_index ] );

      //if (ds_info->cyclic_read != 0)
      //{
      //  VAL tval;
      //  tval.ana = ds_info->cyclic_read;

      //  cyclic_read_task( &Task_win, ds_info, tval);
      //}

      /*
       * End of critical section
       */
      fl_leave_critical( FLS_SIGNAL );
      break;

		case S7_BRCV_CANCEL_IND:
		case S7_BRCV_STOP:
		case S7_VFD_USTATE_IND:
		case S7_VFD_STATE_CNF:

		default : 
			break;
	}

	return GOOD;
}


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_connect
 *
 * PUPRPOSE: The SAPI CONNECT function for the active connection.
 * the pc builds the connection
 -----------------------------------------------------------------------------*/
#pragma warning(disable:4100)
int sapi_connect( DEVICE *d, int fnc )
{

  #ifdef _COMSIM
  d->disable[ IMX_READ  ][ INTERNAL ] = 1;
  d->disable[ IMX_WRITE ][ INTERNAL ] = 1;

	return FL_ERROR;
  #endif

	/* reset the connect flag */
	/*d->connect = CS_DISCONNECTED;*/

	if ( d->board_id == -1 || 
       d->descr == -1 ||
       d->cref == -1)
		return FL_ERROR;
	
	/*
	 * send a connect request to the remote partner
	 */
	if ( d->connect == CS_DISCONNECTED )
	{

		debug_log( &Task_win, "Connect %s descr %d cref %d", 2, d->name, d->descr, d->cref );

		if( s7_initiate_req ( d->descr, d->cref ) != S7_OK )
			return s7_last_detailed_err_no();
	}

	/* set the connect flag to connecting */
	d->connect = CS_CONNECTING;

  #ifdef PASSIVE

	switch ( fnc )
	{
		case IMX_READ:
		case IMX_WRITE:

			/*
			 * send a connect request to the remote partner
			 */
			if ( d->connect[ fnc ] != CS_DISCONNECTED )
			{
				if( s7_initiate_req ( d->descr[ fnc], d->cref[ fnc ] ) != S7_OK )
				{
					return s7_last_detailed_err_no();
				}
			}

			/* set the connect flag to connecting */
			d->connect[ IMX_READ ] = CS_CONNECTING;
			d->connect[ IMX_WRITE ] = CS_CONNECTING;

			break;

		case IMX_RCV:

			/*
			 * wait for connection request form partner
			 */
			if( s7_await_initiate_req ( d->descr[ fnc], d->cref[ fnc ] ) != S7_OK )
      {
				return s7_last_detailed_err_no();
      }

			/* set the connect flag to connecting */
			d->connect[ fnc ] = CS_CONNECTING;

			break;
	}
  #endif

	return GOOD;
} //sapi_connect
#pragma warning(default:4100)


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_connect_rsp
 *
 * PUPRPOSE: The SAPI CONNECT response function.
 *
 -----------------------------------------------------------------------------*/
int sapi_connect_rsp( DEVICE *device )
{
	int i;


  /* reset first to disconnected */
	device->connect	= CS_DISCONNECTED;

  /*
   * receive the connect confirmation
   */
	if ( s7_get_initiate_cnf() == S7_OK )
  {

		debug_log( &Task_win, "Connect OK response %s ", 2, device->name );

    fl_enter_critical( FLS_SIGNAL );
    update_status( &Task_win, device, 0 );
    fl_leave_critical( FLS_SIGNAL );

    device->connect	= CS_CONNECTED;

    /*
     * check if the connection has to be enabled again
     */
		if( device->disable[ IMX_READ ][ USER ] == 0 )
      device->disable[ IMX_READ   ][ INTERNAL ] = 0;

		if( device->disable[ IMX_WRITE ][ USER ] == 0 )
      device->disable[ IMX_WRITE   ][ INTERNAL ] = 0;
  }
	else
  {
    /*
     * disable the connection on the device so all requests will be bounced back to the decoder
     */
    device->disable[ IMX_READ  ][ INTERNAL ] = 1;
    device->disable[ IMX_WRITE ][ INTERNAL ] = 1;

    fl_enter_critical( FLS_SIGNAL );
    fl_status( &Task_win, "COM_CONNECT", FLS_ERROR, s7_last_detailed_err_no() , 
               "READ/WRITE", device->name);

    update_status( &Task_win, device, PD_DISCONNECTED );
    fl_leave_critical( FLS_SIGNAL );

		return s7_last_detailed_err_no();
  }

	/*
	 * check the cyclic read to be started
	 */
	sapi_check_cyclic( device );

	/*
	 * inititiate a BRECV for handleling the unsolicited reveive
	 */
	if ( s7_brcv_init ( device->descr, device->cref,
											( ord32 )( IMX_RCV + 1 ) ) == S7_ERR )
	{

		/* we can not receive, this is unacceptable */
		sapi_disconnect( device, IMX_RCV );

		return s7_last_detailed_err_no();
	}

	/* 
	 * check if this is a CPU device, if so then use the BSEND and BRECV
	 * functions
   */
	if ( device->ctype == S7_CPU_DEVICE )
	{
		for( i = 0; i < 2; i++ )
		{
			if ( s7_brcv_init ( device->descr,
													device->cref,
													( ord32)( i + 1 ) ) == S7_ERR )
			{

				/* we can not receive, this is unacceptable */
				sapi_disconnect( device, i );

				return s7_last_detailed_err_no();
			}
		}
	}

	return GOOD;
} //sapi_connect_rsp


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_disconnect
 *
 * PUPRPOSE: The SAPI DISCONNECT function.
 *
 -----------------------------------------------------------------------------*/
#pragma warning(disable:4100)
int sapi_disconnect( DEVICE *d, int fnc )
{

	if ( d->connect == CS_DISCONNECTED )
		return GOOD;

#ifdef PASSIVE
	/*
	 * reset the connection flag
	 */
	if( fnc == IMX_READ || fnc == IMX_WRITE )
	{
		d->connect[ IMX_READ ] = CS_DISCONNECTED;
		d->connect[ IMX_WRITE ] = CS_DISCONNECTED;
	}
#endif

	d->connect = CS_DISCONNECTED;

  /*
	 * cancel receiving
	 */
	s7_brcv_stop( d->descr, d->cref, ( ord32 )( IMX_RCV + 1 ) );

	if ( d->ctype == S7_CPU_DEVICE )
	{
		s7_brcv_stop( d->descr, d->cref, ( ord32 )( IMX_READ + 1 ) );
		s7_brcv_stop( d->descr, d->cref, ( ord32 )( IMX_WRITE + 1 ) );
	}

	/*
	 * send a disconnect request to the remote partner
	 */
	s7_abort ( d->descr, d->cref );

	return GOOD;
} //sapi_disconnect
#pragma warning(default:4100)


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_shutdown
 *
 * PUPRPOSE: The SAPI SHUTDOWN function.
 *
 -----------------------------------------------------------------------------*/
int sapi_shutdown( )
{
	int i;


	/* shutdown the device, first check for disconnects */
	for ( i = 0; i < Nr_vfd; i++);
		s7_shut( Vfd[ i ].cp_descr );

	return GOOD;
} //sapi_shutdown


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_read
 *
 * PUPRPOSE: The SAPI READ-event function for fetching data. This will be a 
 *           separate thread running.
 *
 *					 The additional device information is not valid because we are using
 *					 one imx device for all configured devices.
 *
 -----------------------------------------------------------------------------*/
int sapi_read( IMX_DEVICE *imx_dev, int function )
{
	IMXDS								*ds;				
  DS_INFO							*ds_info;	
	struct S7_READ_PARA s7_para;
  int									error = 0;
	H1_HDR							*h1;
  ord16               gbl_idx;
//char *tmp=NULL;


  /*
   * loop around forever and handle all H1 read requests for this device
   */
  while ( !Imx_MT_Get_Stop() )
  {

    /*
     * wait for a dataset read command to come in
     */
    if ( Imx_MT_Wait( imx_dev, function, &ds ) != GOOD )
      break;

    /* check first if this is a IMX read request */
    if( Imx_Get_Type( ds ) != IMX_READ_REQ )
    {
      fl_enter_critical( FLS_SIGNAL );
      fl_status( &Task_win, "DBG_READ_TYPE", FLS_ERROR, Imx_Get_Name( ds));
      fl_leave_critical( FLS_SIGNAL );
			continue;
    }

//just crash
//tmp[0] = 5;

		/* retrieve the additional dataset information */
		ds_info = Imx_Get_Udata_Ptr( ds );

		/* lock the read connection for further use */
		if ( ( error = grab_connection( ds_info->device, IMX_READ )) != 0 )
		{
      fl_enter_critical( FLS_SIGNAL );

      fl_status( &Task_win, "Error sync reading %d, %s", FLS_ERROR, error, Imx_Get_Name( ds));

      if ( ( error = Imx_Send_Error( &Thr_system, ds, ( u16 )error )) != GOOD )
        fl_status( &Task_win, "IMX_SEND_ERROR1", FLS_ERROR, error, Imx_Get_Name( ds));

			fl_leave_critical( FLS_SIGNAL );

			continue;
		}
			
    /* set the current dataset on the device which is read now */
    gbl_idx = (ord16)find_global_ds_index( ds_info->device->dataset[ds_info->ds_index]);
    ds_info->device->current[ IMX_READ ] = gbl_idx;

		/* set the pointer to the start of the buffer to use */
		h1 = ( H1_HDR *)ds_info->device->buffer[ function ];

		/* Show which job is activated for logging and/or debugging */
		debug_log(&Task_win, "DBG_READ", 5, Imx_Get_Name( ds ), Imx_Get_Start( ds), Imx_Get_Len( ds));

		/* check if this functionality for this device is still enabled */
		//if ( ds_info->device->disable[ function ][ INTERNAL ] )
    if ( ds_info->device->disable[ function ][ USER ] )
		{

      /* send an IMX error back to the decoder */
      fl_enter_critical( FLS_SIGNAL );

      if (( error = Imx_Send_Error( &Thr_system, ds, PD_READ_DISABLED )) != GOOD )
        fl_status( &Task_win, "IMX_SEND_ERROR2", FLS_ERROR, error, Imx_Get_Name( ds));

      fl_leave_critical( FLS_SIGNAL );

      debug_log( &Task_win, "Read fnc disabled %s, %d", 2 , Imx_Get_Name( ds ), PD_READ_DISABLED );

		  release_connection( ds_info->device, IMX_READ );
			continue;
		}

		/*
		 * check if an error on the device exists and if the connection has to be rebuilt
		 */
		if ( ds_info->device->connect != CS_CONNECTED )
		{
      /* send an IMX error back to the decoder */
      fl_enter_critical( FLS_SIGNAL );

      /*fl_status( &Task_win, "COM_READ", FLS_ERROR, PD_DISCONNECTED, Imx_Get_Name( ds));*/

      if (( error = Imx_Send_Error( &Thr_system, ds, PD_DISCONNECTED )) != GOOD )
        fl_status( &Task_win, "IMX_SEND_ERROR3", FLS_ERROR, error, Imx_Get_Name( ds));

      fl_leave_critical( FLS_SIGNAL );

			/*
			 * try to set up a fetch connection with the PLC
			 */
      if ( ds_info->device->connect == CS_DISCONNECTED  )
      {
			  if( ( error = sapi_connect ( ds_info->device, function )) != 0 )
			  {
		      fl_enter_critical( FLS_SIGNAL );
			  fl_status( &Task_win, "COM_CONNECTDS", FLS_ERROR, error, "READ", 
				         ds_info->device->name, Imx_Get_Name( ds));
		      fl_leave_critical( FLS_SIGNAL );
			  }
      }

		  release_connection( ds_info->device, IMX_READ );

			continue;
		}

		if ( ds_info->device->ctype == S7_CPU_DEVICE )
		{

			/* copy the H1 header template */
			memcpy ( h1, &_H1_read, sizeof( H1_HDR ) );

			/* build the H1 header dataset structure */
			h1_set_type				 ( h1, ( char )ds_info->type );
			h1_set_dbno        ( h1, (uchar)ds_info->db_nr );
			h1_set_start       ( h1, Imx_Get_Start( ds ) );
			h1_set_length      ( h1, Imx_Get_Len( ds ) );

			if( ( error = s7_bsend_req ( ds_info->device->descr,
																	 ds_info->device->cref,
																	 gbl_idx,
																	 ( ord32 )( IMX_READ +1 ),
																	 h1,
																	 ( ord32 )sizeof( H1_HDR ))) != S7_OK )
			{

				fl_enter_critical( FLS_SIGNAL );
				fl_status( &Task_win, "COM_READ", FLS_ERROR, s7_last_detailed_err_no(), Imx_Get_Name( ds));

				/* send an IMX error back to the decoder */
				if (( error = Imx_Send_Error( &Thr_system, ds, s7_last_detailed_err_no() )) != GOOD )
					fl_status( &Task_win, "IMX_SEND_ERROR4", FLS_ERROR, error, Imx_Get_Name( ds));

	      fl_leave_critical( FLS_SIGNAL );

				release_connection( ds_info->device, IMX_READ );

				continue;
			}
		}
		else
		{

			/* build the read command on variable name */
			s7_para.access	 =	S7_ACCESS_SYMB_ADDRESS;

			if ( ds_info->type == H1_DB )
				sprintf( s7_para.var_name, S7_var[ ds_info->type ], ds_info->db_nr, Imx_Get_Start( ds ), Imx_Get_Len( ds ));
			else
				sprintf( s7_para.var_name, S7_var[ ds_info->type ], Imx_Get_Start( ds ), Imx_Get_Len( ds ));

      debug_log( &Task_win, "CP Read %s", 9 , s7_para.var_name );

      gbl_idx = (ord16)find_global_ds_index( ds_info->device->dataset[ds_info->ds_index]);
      //if (gbl_idx >= 0) {

			/* read the actual data from the external device */
			if( ( error = s7_read_req ( ds_info->device->descr,
																	ds_info->device->cref,
																	gbl_idx,
																	&s7_para )) != S7_OK )
			{

				fl_enter_critical( FLS_SIGNAL );
			
				fl_status( &Task_win, "COM_READ", FLS_ERROR, s7_last_detailed_err_no(), Imx_Get_Name( ds));

				/* send an IMX error back to the decoder */
				if (( error = Imx_Send_Error( &Thr_system, ds, s7_last_detailed_err_no() )) != GOOD )
					fl_status( &Task_win, "IMX_SEND_ERROR5", FLS_ERROR, error, Imx_Get_Name( ds));

	      fl_leave_critical( FLS_SIGNAL );

				release_connection( ds_info->device, IMX_READ );

				continue;
			}
		}
	}

  return GOOD;
} //sapi_read


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_read_rsp
 *
 * PUPRPOSE: The SAPI READ-event function with response. This function will be
 *           called from the sapi call back function. This is the read response
 *           with data, initiated by the remote partner.
 *int sapi_read_rsp( IMXDS *ds, ord16 order_id )
 -----------------------------------------------------------------------------*/
int sapi_read_rsp( IMXDS *ds )
{
  DS_INFO   *ds_info;
	FLMSG				buffer;	
	int				error;

  /* get the addtional dataset information */

  ds_info = Imx_Get_Udata_Ptr( (MT_IMXDS *)Imx_Get_Udata_Ptr( ds ));   
	
	/*
	 * read the data received without imxheader
	 */
	if ( ds_info->device->ctype == S7_CPU_DEVICE )
	{
		buffer.m_ptr = Sapi_buf.m_ptr + sizeof( H1_HDR ) - sizeof( IMXHDR );
		buffer.m_max = BUFFER_SIZE - sizeof( H1_HDR ) + sizeof( IMXHDR );

	  /* set the length of the received data */
		/* do a prepare for retrieving registered values */
		Imx_Ds_Prepare  ( ds, &buffer );
		Imx_Set_Type    ( ds, IMX_READ_RSP );
		Imx_Set_Len     ( ds, ( Sapi_buf.m_len - sizeof( H1_HDR )) / Imx_Get_Boundary( ds ) );
	}
	else
	{
		buffer.m_ptr = Sapi_buf.m_ptr;
		buffer.m_max = BUFFER_SIZE - sizeof( IMXHDR );

	  /* set the length of the received data */
		/* do a prepare for retrieving registered values */
		Imx_Ds_Prepare  ( ds, &buffer );
		Imx_Set_Type    ( ds, IMX_READ_RSP );
	  Imx_Set_Len     ( ds, Sapi_buf.m_len / Imx_Get_Boundary( ds ) );
	}
  
  debug_log( &Task_win, "DBG_READ_RSP", 3, Imx_Get_Name( ds ),
             ( int )Imx_Get_Start( ds ), ( int )Imx_Get_Len( ds ));

  fl_enter_critical( FLS_SIGNAL );

  /* send data to decoder */
  if( ( error = Imx_Send( &Thr_system, ds )) != GOOD )
    fl_status( &Task_win, "IMX_SENDREAD", FLS_ERROR, error, Imx_Get_Name( ds ));

  fl_leave_critical( FLS_SIGNAL );

  ds_info->device->current[ IMX_READ ] = -1;

	return error;
} //sapi_read_rsp


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_write
 *
 * PUPRPOSE: The SAPI event function for WRITING. This function will be called 
 *           from the FactoryLink thread.
 *
 -----------------------------------------------------------------------------*/
int sapi_write( IMX_DEVICE *imx_dev, int function )
{
	IMXDS				*ds;				
  DS_INFO     *ds_info;	
	int         error = 0;
  int         gbl_idx;


  /*
   * loop around forever and handle all H1 read requests for this device
   */
  while ( !Imx_MT_Get_Stop() )
  {

    /*
     * wait for a dataset read command to come in
     */
    if ( Imx_MT_Wait( imx_dev, function, &ds ) != GOOD )
      break;

    /* check first if this is a IMX read request */

    if( Imx_Get_Type( ds ) != IMX_WRITE_REQ )
    {
      debug_log( &Task_win, "DBG_WRITE_TYPE", 2, Imx_Get_Name( ds ));
			continue;
    }

		/* retrieve the additional dataset information */
		ds_info = Imx_Get_Udata_Ptr( ds );

		/* Show which job is activated for logging and/or debugging */
		debug_log(&Task_win, "DBG_WRITE", 5, Imx_Get_Name( ds ), Imx_Get_Start( ds), Imx_Get_Len( ds));

		if ( ( error = grab_connection( ds_info->device, IMX_WRITE )) != 0 )
		{
      fl_enter_critical( FLS_SIGNAL );

      fl_status( &Task_win, "Error sync writing %d, %s", FLS_ERROR, error, Imx_Get_Name( ds));

      if ( ( error = Imx_Send_Error( &Thr_system, ds, ( u16 )error )) != GOOD )
        fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( ds));

      fl_leave_critical( FLS_SIGNAL );
			continue;
		}

    /* set the current dataset on the device which is written now */
    gbl_idx = find_global_ds_index( ds_info->device->dataset[ds_info->ds_index]);
    ds_info->device->current[ IMX_WRITE ] = gbl_idx;

		/* check if this functionality for this device is still enabled */
		//if ( ds_info->device->disable[ function ][ INTERNAL ] )
    if ( ds_info->device->disable[ function ][ USER ] )
		{

      /* send an IMX error back to the decoder */
      fl_enter_critical( FLS_SIGNAL );

      if (( error = Imx_Send_Error( &Thr_system, ds, PD_WRITE_DISABLED )) != GOOD )
        fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( ds));

      fl_leave_critical( FLS_SIGNAL );

      debug_log( &Task_win, "Write fnc disabled %s, %d", 2 , Imx_Get_Name( ds ), PD_WRITE_DISABLED );

		  release_connection( ds_info->device, IMX_WRITE );
  		continue;
		}

		/*
		 * check if an error on the device exists and if the connection has to be rebuilt
		 */

		if ( ds_info->device->connect == CS_DISCONNECTED )
		{

      /* send an IMX error back to the decoder */
      fl_enter_critical( FLS_SIGNAL );

      /*fl_status( &Task_win, "COM_WRITE", FLS_ERROR, PD_DISCONNECTED, Imx_Get_Name( ds));*/

      if (( error = Imx_Send_Error( &Thr_system, ds, PD_DISCONNECTED )) != GOOD )
        fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( ds));

      fl_leave_critical( FLS_SIGNAL );

			/*
			 * try to set up a fetch connection with the PLC
			 */
      if ( ds_info->device->connect == CS_DISCONNECTED  )
      {
			  if( ( error = sapi_connect ( ds_info->device, function )) != 0 )
			  {
		      fl_enter_critical( FLS_SIGNAL );
          fl_status( &Task_win, "COM_CONNECTDS", FLS_ERROR, error, "WRITE", 
			         ds_info->device->name, Imx_Get_Name( ds ));
		      fl_leave_critical( FLS_SIGNAL );
			  }
      }

		  release_connection( ds_info->device, IMX_WRITE );
      continue;
		}

		/*
		 * call the right function for the type of connection
		 */
		if ( ds_info->device->ctype == S7_CP_DEVICE )
		{
			if ( cp_write( ds ) == FL_ERROR )
			  release_connection( ds_info->device, IMX_WRITE );
		}
		else
		{
			if ( cpu_write( ds ) == FL_ERROR )
			  release_connection( ds_info->device, IMX_WRITE );
		}
	}

	return GOOD;
} //sapi_write


/*-----------------------------------------------------------------------------
 * FUNCTION: cp_write
 *
 * PUPRPOSE: This function will be called in case this is a CP connection.
 *
 -----------------------------------------------------------------------------*/
int cp_write( IMXDS	*ds )
{
  DS_INFO *ds_info;
  char    *data;
  int     error;
  struct  S7_WRITE_PARA s7_para;
  ord16   gbl_idx;


	/* retrieve the additional dataset information */
	ds_info = Imx_Get_Udata_Ptr( ds );

	/* set the pointer to the start of the real data to use, IMX header is in front of this */
	data = Imx_Get_U_Buffer( ds );

	/* Show which job is activated for logging and/or debugging */

	/*debug_log( &Task_win, "DBG_WRITE", 2, 
							Imx_Get_Name  ( ds ),
							Imx_Get_Start ( ds ),
							Imx_Get_Len   ( ds ));*/


	/*
	 * check if the request is a block write or has to be changed to an
	 * encoded write.
	 */
	if ( Imx_Get_Handling( ds ) == IMX_NORMAL_WRITE )
	{

		/* first try to minimize the boundary of the element to be written */
		error = Imx_Ds_Evaluate_Write( ds );

		if( error != GOOD && error != IMX_BLOCK && error != IMX_X_WRITE )
		{
      fl_enter_critical( FLS_SIGNAL );

      fl_status( &Task_win, "COM_WRITE", FLS_ERROR, PD_NOT_ENCODE, Imx_Get_Name( ds ));

      /* send an IMX error back to the decoder */
      if (( error = Imx_Send_Error( &Thr_system, ds, PD_NOT_ENCODE )) != GOOD )
        fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( ds));

      fl_leave_critical( FLS_SIGNAL );

			return FL_ERROR;
		}

		if ( Imx_Get_CDE( ds ) !=  CDE_BLOCK )
		{
			if ( ds_info->type == H1_DB )
			{
				sprintf( s7_para.var_name, 
								 S7_var[ ds_info->type + X_OFFSET ], 
								 ds_info->db_nr, 
								 Imx_Get_Start( ds ), 
								 Imx_Get_CDE( ds ) - CDE_B0_15);
			}
			else
			{
				sprintf( s7_para.var_name, 
								 S7_var[ ds_info->type + X_OFFSET ], 
								 Imx_Get_Start( ds ), 
								 Imx_Get_CDE( ds ) - CDE_B0_15);
			}

			if( data[ 0 ] )
				s7_para.value[ 0 ] = 1;
			else
				s7_para.value[ 0 ] = 0;
		}
		else
		{
			if ( ds_info->type == H1_DB )
				sprintf( s7_para.var_name, 
								 S7_var[ ds_info->type ], 
								 ds_info->db_nr, 
								 Imx_Get_Start( ds ), 
								 Imx_Get_Len( ds ));
			else
				sprintf( s7_para.var_name, 
								 S7_var[ ds_info->type ], 
								 Imx_Get_Start( ds ), 
								 Imx_Get_Len( ds ));

			memcpy( s7_para.value, data, Imx_Get_Len( ds ) );
		}

		/* build the read command on variable name */
		s7_para.access			=	S7_ACCESS_SYMB_ADDRESS;
		s7_para.var_length	=	Imx_Get_Len( ds );

	}
	else
	{
		return FL_ERROR;
	}

  gbl_idx = (ord16)find_global_ds_index( ds_info->device->dataset[ds_info->ds_index]);

	/* write the actual data to the external device */
	if( ( error = s7_write_req ( ds_info->device->descr,
															 ds_info->device->cref,
															 gbl_idx ,
															 &s7_para,
															 NULL )) != S7_OK )
	{

    fl_enter_critical( FLS_SIGNAL );

    fl_status( &Task_win, "COM_WRITE", FLS_ERROR, s7_last_detailed_err_no(), Imx_Get_Name( ds ));

    /* send an IMX error back to the decoder */
    if (( error = Imx_Send_Error( &Thr_system, ds, s7_last_detailed_err_no() )) != GOOD )
      fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( ds ));

    fl_leave_critical( FLS_SIGNAL );

		return FL_ERROR;
	}

	return GOOD;
} //cp_write

/*-----------------------------------------------------------------------------
 * FUNCTION: cpu_write
 *
 * PUPRPOSE: This function will be called in case a CPU write has to be 
 *					 performed
 *
 -----------------------------------------------------------------------------*/
int cpu_write( IMXDS *ds )
{
  DS_INFO *ds_info;	
  char    *data,
          *temp;
  int     length = 0,
          error = 0;
  H1_HDR  *h1;
  ord16   gbl_idx;


	/* retrieve the additional dataset information */
	ds_info = Imx_Get_Udata_Ptr( ds );

  data = Imx_Get_U_Buffer( ds );
  h1	 = ( H1_HDR *)ds_info->device->buffer[ IMX_WRITE ];

	/*
	 * check if the request is a block write or has to be changed to an
	 * encoded write.
	 */
	if ( Imx_Get_Handling( ds ) == IMX_NORMAL_WRITE )
	{

		/* first try to minimize the boundary of the element to be written */
		error = Imx_Ds_Evaluate_Write( ds );

		if( error != GOOD && error != IMX_BLOCK && error != IMX_X_WRITE )
		{

      fl_enter_critical( FLS_SIGNAL );

      fl_status( &Task_win, "COM_WRITE", FLS_ERROR, PD_NOT_ENCODE, Imx_Get_Name( ds ));

      /* send an IMX error back to the decoder */
      if (( error = Imx_Send_Error( &Thr_system, ds, PD_NOT_ENCODE )) != GOOD )
        fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( ds));

      fl_leave_critical( FLS_SIGNAL );

			return FL_ERROR;
		}

		/*
		 * in case the write request could not be converted to a block then perform
		 * an exception write
		 */
		if ( Imx_Get_CDE( ds) != CDE_BLOCK )
		{
			Imx_Set_Handling( ds, IMX_ENCODED_WRITE );
		}
	}

	/* copy the H1 header template */
	memcpy ( h1, &_H1_write, sizeof( H1_HDR ) );

	/*
	 * the write request is in this case always a block write or encoded write
	 */
	if ( Imx_Get_Handling( ds ) == IMX_NORMAL_WRITE )
	{

		/* build the H1 dataset structure for block write */
		h1_set_type        ( h1, ( char )ds_info->type );
		h1_set_dbno        ( h1, ( uchar )ds_info->db_nr );
		h1_set_start       ( h1, Imx_Get_Start( ds ) );
		h1_set_length      ( h1, Imx_Get_Len( ds ) );

		/*
		 * copy the received data to the communication buffer
		 */
		temp = ( char *)h1 + sizeof( H1_HDR );
		memcpy( ( char *)h1 + sizeof( H1_HDR ),
						data,  
						length = ( Imx_Get_Len( ds ) * Imx_Get_Boundary( ds )) );
	}
	else
	{
		/*
		 * give the data to send to the device for the encoded write
		 */
		temp = ( char * )h1 + sizeof( H1_HDR );
		memcpy( ( char * )h1 + sizeof( H1_HDR ) + sizeof( ENCODE ),
						data,
						length = IMX_LONG_BND);

		length += sizeof( ENCODE );

		/*
		 * build the encode header
		 */
		if ( ( error = encode( ds_info->device, ds, ( ENCODE *)( ( char *)h1 + sizeof( H1_HDR )) ) ) != GOOD )
		{
      fl_enter_critical( FLS_SIGNAL );

      fl_status( &Task_win, "COM_WRITE", FLS_ERROR, PD_NOT_ENCODE, Imx_Get_Name( ds ));

      /* send an IMX error back to the decoder */
      if (( error = Imx_Send_Error( &Thr_system, ds, PD_NOT_ENCODE )) != GOOD )
        fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( ds));

      fl_leave_critical( FLS_SIGNAL );

			return FL_ERROR;
		}

		h1_set_type        ( h1, H1_DB );
		h1_set_dbno        ( h1, ( uchar )ds_info->device->encode );
		h1_set_start       ( h1, 2 );
		h1_set_length      ( h1, IMX_LONG_BND + sizeof( ENCODE ) );
	}

  length += sizeof( H1_HDR );

  gbl_idx = (ord16)find_global_ds_index( ds_info->device->dataset[ds_info->ds_index]);

	/* write the actual data to the external device */
	if( ( error = s7_bsend_req ( ds_info->device->descr,
															 ds_info->device->cref,
															 gbl_idx ,
															 ( ord32 )( IMX_WRITE + 1 ),
															 h1,
															 ( ord32 )length )) != S7_OK )
	{

    fl_enter_critical( FLS_SIGNAL );

    fl_status( &Task_win, "COM_WRITE", FLS_ERROR, s7_last_detailed_err_no(), Imx_Get_Name( ds ));

    /* send an IMX error back to the decoder */
    if (( error = Imx_Send_Error( &Thr_system, ds, s7_last_detailed_err_no() )) != GOOD )
      fl_status( &Task_win, "IMX_SEND_ERROR", FLS_ERROR, error, Imx_Get_Name( ds ));

    fl_leave_critical( FLS_SIGNAL );

		return FL_ERROR;
	}

	return GOOD;
} //cpu_write

 
/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_write_rsp
 *
 * PUPRPOSE: The SAPI WRITE-event function with response. This function will be
 *           called from the sapi call back function. This function handles the
 *					 the confirmation of a sapi bsend. This can be a confirmation on a 
 *           read and write connection.
 *
 -----------------------------------------------------------------------------*/
int sapi_write_rsp( IMXDS *ds )
{
  DS_INFO   *ds_info;
	ord16			error;


  /* get the additonal dataset information */
  ds_info = Imx_Get_Udata_Ptr( ( MT_IMXDS *)Imx_Get_Udata_Ptr( ds ));   

	/*
	 * generate a IMX write completion and send to decoder
	 */
  Imx_Set_Type( ds, IMX_WRITE_RSP );

  debug_log( &Task_win, "DBG_WRITE_RSP", 3, 
							 Imx_Get_Name( ds ), 
							 ds->start,
							 ds->len );

  fl_enter_critical( FLS_SIGNAL );

  if( ( error = (ord16)Imx_Send( &Thr_system, ds )) != GOOD )
  {
    fl_status( &Task_win, "IMX_SEND", FLS_ERROR, error, Imx_Get_Name( ds ));
  }

  fl_leave_critical( FLS_SIGNAL );
	return error;
} //sapi_write_rsp

/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_cycl_read_rsp
 *
 * PUPRPOSE: The SAPI event function for unsollicited receive with the cyclic read
 *           functionality. This function will be called from the sapi call back function.
 *
 -----------------------------------------------------------------------------*/
int sapi_cycl_read_rsp( IMXDS *ds, DEVICE  *device)
{
  int error;
  int length = Sapi_buf.m_len;

	/* Show wich job is activated for logging and/or debugging */
	debug_log( &Task_win, "Cyclic read data %s start %d length %d", 5, ds->name, 
             ds->start, Sapi_buf.m_len );

  if (device->disable[IMX_RCV][USER]) 
  {

	  debug_log( &Task_win, "Receive disabled for %s, ds %s; cyclic read data disregarded", 5, 
               device->name, ds->name);

    return GOOD;
  }

	Imx_Ds_Prepare  ( ds, &Sapi_buf );
	Imx_Set_Type    ( ds, IMX_RCV_RDY );

	/* set the length of the received data */
	Imx_Set_Len     ( ds, (u16)length );

  /*
   * send the unsolicited receive to the decoder
   */
  fl_enter_critical( FLS_SIGNAL );

	if( ( error = Imx_Send( &Thr_system, ds )) != GOOD )
	{
		fl_status( &Task_win, "IMX_SENDRCV", FLS_ERROR, error, ds->name);
	}

  fl_leave_critical( FLS_SIGNAL );

  return GOOD;
} //sapi_cycl_read_rsp
 

/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_recv_rsp( DEVICE *device )
 *
 * PUPRPOSE: The SAPI event function for unsollicited receive. This function will
 *					 be called from the sapi call back function.
 *
 -----------------------------------------------------------------------------*/
int sapi_recv_rsp( DEVICE *device )
{
	IMXDS			*ds=NULL;
	FLMSG			buffer;	
	int 			start;
	int				length;
  H1_HDR    *h1 = ( H1_HDR * )Sapi_buf.m_ptr;
	int       index = 0;
  int       error;


  /*
   * send an acknowledgement back to the sender
	 */
	if ( s7_bsend_req ( device->descr,
                      device->cref,
								      IMX_RCV,
                      ( ord32 )( IMX_RCV + 1 ),
								      &_H1_ack,
                      ( ord32 )sizeof( H1_HDR ) ) != S7_OK
                    )
	{
    fl_enter_critical( FLS_SIGNAL );
    fl_status( &Task_win, "COM_RECV", FLS_ERROR, s7_last_detailed_err_no(), device->name );
    fl_leave_critical( FLS_SIGNAL );
  }

  /* 
	 * Header is now ok, so find corresponding datasets, just for this device
	 */
	while ( ( index = sapi_ds_search( h1, device, index )) >= 0 )
	{

		/* get the dataset */
		ds = device->dataset[ index++ ];

		/* check if this functionality for this device is still enabled */
		if ( device->disable[ IMX_RCV ][ INTERNAL ] )
    {

      debug_log( &Task_win, "Rcv fnc disabled %s, %d", 2 , Imx_Get_Name( ds ), PD_RCV_DISABLED );
			return GOOD;
    }

		/* Show wich job is activated for logging and/or debugging */
		debug_log(	&Task_win, "DBG_RECV", 2, ds->name, ds->start, ds->len );

		/*
		 * set the start size of IMXHDR in front of the data in the buffer 
		 */
		start  = h1_get_start( h1 );
		length = h1_get_length( h1 );

		buffer.m_ptr = Sapi_buf.m_ptr + sizeof( H1_HDR ) - sizeof( IMXHDR );
		buffer.m_max = BUFFER_SIZE - sizeof( H1_HDR ) + sizeof( IMXHDR );

		Imx_Ds_Prepare  ( ds, &buffer );
		Imx_Set_Type    ( ds, IMX_RCV_RDY );

		/* set the length of the received data */
		Imx_Set_Start   ( ds, (u16)start );
        Imx_Set_Len     ( ds, (u16)length );

    /*
     * send the unsolicited receive to the decoder
     */
    fl_enter_critical( FLS_SIGNAL );

		if( ( error = Imx_Send( &Thr_system, ds )) != GOOD )
			fl_status( &Task_win, "IMX_SENDRCV", FLS_ERROR, error, ds->name);

    fl_leave_critical( FLS_SIGNAL );
	} 

  return GOOD;
} //sapi_recv_rsp


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_start_cyclic
 *
 * PURPOSE:  This function starts the cyclic read with the specified interval.
 *           The cyclic read functionality can be compared with the unsolicited
 *           receive functionality. The PLC will send spontanious with a certain
 *           interval the dataset to our system.
 *
 -----------------------------------------------------------------------------*/
int sapi_start_cyclic ( TASK_ID *task, DS_INFO *ds_info )
{
  IMXDS               *ds;
  ord16                 gbl_idx;
  struct S7_READ_PARA read_para;


  /* get the pointer to the registered dataset */
  //ds = Ds[ ds_info->ds_index ];
  ds = ds_info->device->dataset[ds_info->ds_index];

  /* check if the interval value is OK */
  if ( ds_info->cyclic_read == 0 )
  {
    fl_status( task, 
               "COM_CREAD", 
               FLS_ERROR, 
               PD_CREAD_INTERVAL, 
               ds_info->device->name,
               Imx_Get_Name( ds ) );

    return FL_ERROR;
  }


	/* build the read command on variable name */
	read_para.access	 =	S7_ACCESS_SYMB_ADDRESS;

	if ( ds_info->type == H1_DB )
		sprintf( read_para.var_name, S7_var[ ds_info->type ], ds_info->db_nr, ds->start, ds->len);
	else
		sprintf( read_para.var_name, S7_var[ ds_info->type ], ds->start, ds->len);

  gbl_idx = (ord16)find_global_ds_index( ds_info->device->dataset[ds_info->ds_index]);
  if (gbl_idx >= 0)
  {

  debug_log( task, "Start cyclic %s %s ", 4, ds->name, read_para.var_name );

	/* 	 * mark a cyclic request pending
	 */
	ds_info->device->cyclic_pending[ CSTART ] = 1;

  /*
   * start the cyclic read with the specified interval
   */
	/*if ( s7_cycl_read(	ds_info->device->descr,
										  ds_info->device->cref,
										  ds_info->ds_index,
										  ds_info->cyclic_read,
										  1,
										  ( struct S7_READ_PARA *)&read_para ) != S7_OK )*/
	if( s7_cycl_read_init_req( ds_info->device->descr,
														 ds_info->device->cref,
 														 gbl_idx,
														 ds_info->cyclic_read,
														 1,
														 ( struct S7_READ_PARA *)&read_para ) != S7_OK )
  {
    fl_status( task,
               "S7_CYCL_READ_INIT_REQ",
               FLS_ERROR,
               s7_last_detailed_err_no(),
               ds_info->device->name,
               ds->name );

    ds_info->device->cyclic_pending[ CSTART ] = 0;
    return s7_last_detailed_err_no();
  }
  }

  return GOOD;
} //sapi_start_cyclic


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_stop_cyclic
 *
 * PURPOSE:  This function stops the cylcic read functionality
 *
 -----------------------------------------------------------------------------*/
int sapi_stop_cyclic ( TASK_ID *task, DS_INFO *ds_info )
{
  IMXDS *ds;
  ord16   gbl_idx;


  /* get the pointer to the registered dataset */
  ds = ds_info->device->dataset[ds_info->ds_index];

  /* check if the cyclic read is active */
  if ( ds_info->cyclic_read == 0 )
    return GOOD;

  gbl_idx = (ord16)find_global_ds_index( ds_info->device->dataset[ds_info->ds_index]);
  if (gbl_idx < 0) return GOOD;  

  debug_log( task, "Stop cyclic %s", 2, ds->name);

	/* 
	 * mark a cyclic request pending
	 */
	ds_info->device->cyclic_pending[ CSTOP ] = 1;

	if ( s7_cycl_read_delete_req(	ds_info->device->descr,
										            ds_info->device->cref,
										            gbl_idx) != S7_OK )
  {

    fl_status( task, 
               "S7_CYCL_READ_DELETE_REQ", 
               FLS_ERROR, 
               s7_last_detailed_err_no(), 
               ds_info->device->name,
               ds->name );

    ds_info->device->cyclic_pending[ CSTOP ] = 0;
    return s7_last_detailed_err_no();
  }

  ds_info->cyclic_read = 0;

  return GOOD;
} //sapi_stop_cyclic


/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_check_cyclic
 *
 * PURPOSE: Check the status of the cyclic reads
 -----------------------------------------------------------------------------*/
int	sapi_check_cyclic( DEVICE *device )
{
	DS_INFO *ds_info;
	VAL     rate;
	int i;

  //show what we are doing
  debug_log( &Task_win, "Check cyclic reads for %s", 7, device->name);

	/*
	 * check all datasets if a cyclic read has to be started
	 */
	for ( i = 0; i < device->Nr_Datasets; i++ )
	{
    ds_info = Imx_Get_Udata_Ptr( (MT_IMXDS *)Imx_Get_Udata_Ptr( device->dataset[ i ] ));   

		rate.ana = ds_info->cyclic_read;

		if ( ds_info->cyclic_read )
		{

      fl_enter_critical( FLS_SIGNAL );
			cyclic_read_task( &Task_win, ds_info, rate );
      fl_leave_critical( FLS_SIGNAL );
		}
	}

	return 0;
} //sapi_check_cyclic


/*-----------------------------------------------------------------------------
 * FUNCTION: cyclic_read_task
 *
 * PURPOSE: Start or stop the cyclic read functionality
 -----------------------------------------------------------------------------*/
int cyclic_read_task( TASK_ID *task, DS_INFO *ds_info, VAL cyclic_rate )
{
  int previous_rate = ds_info->cyclic_read;
  //VAL tval;

	/*
	 * check first if the device is connected
	 */
  if ( ds_info->device->connect != CS_CONNECTED )
		return 0;


  /*
   * perform the right action on the cyclic function
   */
  if ( cyclic_rate.ana != 0 )
  {

    /* update the cyclic rate variable */
    //if ((ds_info->cyclic_read != cyclic_rate.ana) && ds_info->cyclic_read)
    //{

    //  tval.ana = 0;
    ds_info->cyclic_read = cyclic_rate.ana;
    //  cyclic_read_task( task, ds_info, tval);
    //  //return 0;
    //}

		/* check if the maximum number of pending requests has been reached */
		if ( ds_info->device->cyclic_pending[ CSTART ] == MAX_CYCLIC_REQ_PENDING )
		{
      update_status( task, ds_info->device, PD_MAX_CYCLIC );

			fl_status( task, "S7_CYCL_READ_MAX_REQ", FLS_ERROR );
      ds_info->cyclic_read = (ANA)previous_rate;
		}
		else
		{
			ds_info->device->cyclic[ CSTART ][ ds_info->device->cyclic_head[ CSTART ] ] = ds_info;

			POINTER_UP( ds_info->device->cyclic_head[ CSTART ] );

			/*
			 * Check if there is already an cyclic read request pending. If so then
			 * buffer the request
			 */
			if ( !ds_info->device->cyclic_pending[ CSTART ] )
			{
				if ( sapi_start_cyclic ( task, ds_info ) != GOOD )
				{

          //restore previous context
          POINTER_DOWN( ds_info->device->cyclic_head[ CSTART ] );
          ds_info->cyclic_read = (ANA)previous_rate;

					/*
					 * update the status tag of the device here
					 */
					update_status( task, ds_info->device, PD_START_CYCLIC );
				}
        //else
        //  ds_info->cyclic_read = cyclic_rate.ana;
			  //  ds_info->device->cyclic_pending[ CSTART ]++;
			}
		}
  }
  else
  {

		/* check if the maximum number of pending requests has been reached */
		if ( ds_info->device->cyclic_pending[ CSTOP ] == MAX_CYCLIC_REQ_PENDING )
		{
      update_status( task, ds_info->device, PD_MAX_CYCLIC );

			fl_status( task, "S7_CYCL_READ_MAX_STOP", FLS_ERROR );
		}
		else
		{

			/* buffer cyclic read request and set out after acknowledgements */
			ds_info->device->cyclic[ CSTOP ][ ds_info->device->cyclic_head[ CSTOP ] ] = ds_info;

			POINTER_UP( ds_info->device->cyclic_head[ CSTOP ] );

			if ( !ds_info->device->cyclic_pending[ CSTOP ] )
			{
				if ( sapi_stop_cyclic ( task, ds_info ) != GOOD )
				{

          POINTER_DOWN( ds_info->device->cyclic_head[ CSTOP ] );

					/*
					 * update the status tag of the device here
					 */
					update_status( task, ds_info->device, PD_STOP_CYCLIC );
				}
			}

			//ds_info->device->cyclic_pending[ CSTOP ]++;
		}
  }
  return 0;
} //cyclic_read_task


/*-----------------------------------------------------------------------------
 * FUNCTION: encode
 *
 * PURPOSE: This functions creates an encoded write message
 *
 -----------------------------------------------------------------------------*/
#pragma warning(disable:4100)
int encode ( DEVICE *device, IMXDS *ds, ENCODE *encode )
{
	DS_INFO		*ds_info;


	/* retrieve the additional dataset information */
	ds_info = Imx_Get_Udata_Ptr( ds );
																			
  /* mark a new incoming command for PLC */
  encode->chg_flag = 0xff;

  /*
   * set the org, type and address of the element depending on the type of PLC
   */
  if( ds_info->db_nr )
    encode->org     = s7_type[ ds_info->type -1 ];
  else
    encode->org     = (uchar)ds_info->type;

	S7_Set_Word_H2L( &encode->db_nr, ds_info->db_nr);
	S7_Set_Word_H2L( &encode->address, (short)Imx_Get_Start( ds ));

  /*
   * evaluate the code parameter
   */
  if ( Imx_Get_CDE( ds ) < CDE_NIBBLE )
	{                   
    encode->type = E_BIT;
    encode->bit  = ( uchar)( Imx_Get_CDE( ds ) - CDE_B0_15 );
  }
  else if ( Imx_Get_CDE( ds ) < CDE_BYTE )
	{
    return H1_NOT_ENCODE;
	}
  else if ( Imx_Get_CDE( ds ) < CDE_SIGNED )
	{
    switch ( Imx_Get_CDE( ds ) )
		{
      case CDE_BYTE:
      case CDE_UBYTE:
        if ( Imx_Get_Boundary( ds ) > 1 )
          encode->type = E_DR;
        else
          encode->type = E_BYTE;
        break;

      case CDE_BYTE + 8:
      case CDE_UBYTE + 8:
        encode->type = E_DL;
        break;

      default:
        return PD_NOT_ENCODE;
    }
  }
  else if( Imx_Get_CDE( ds ) < CDE_LONG )
	{
    switch ( Imx_Get_CDE( ds ) )
		{
      case CDE_SIGNED:
      case CDE_UNSIGNED:
        encode->type = E_WORD;
        break;

      default:
        return PD_NOT_ENCODE;
    }
  }
  else if ( Imx_Get_CDE( ds ) < CDE_DOUBLE_IEEE_FLT )
	{
    encode->type = E_LONG;
  }
  else
    return PD_NOT_ENCODE;

  #ifdef INTEL
  swab( ( char *)encode, ( char *)encode, sizeof( ENCODE ));
  #endif

  return 0;
} //encode
#pragma warning(default:4100)

/*-----------------------------------------------------------------------------
 * FUNCTION: search_device
 *
 * PUPRPOSE: The SAPI search command.
 *
 -----------------------------------------------------------------------------*/
int search_device( ord32 cp_descr, ord16 cref, DEVICE **device )
{
	int			i;

	/*
	 * search for the device being connected
	 */
	for ( i = 0; i < Nr_devices; i++ )
	{
		/*
		 * check if a connection has been on this device by
		 * comparing the connection reference
		 */
		if ( Device[ i ]->descr == cp_descr )
		{
			if ( Device[ i ]->cref == cref )
			{
				/* device found */

				*device = Device[ i ];

				return GOOD;
			}
		}
	}

	return FL_ERROR;
} //search_device

/*-----------------------------------------------------------------------------
 * FUNCTION: sapi_ds_search
 *
 * PUPRPOSE: The SAPI ds .globale data
 *
 -----------------------------------------------------------------------------*/
int sapi_ds_search( H1_HDR *h1, DEVICE *device, int start)
{
	DS_INFO			*ds_info;
	H1_HDR			h1_cmp;
	int         i;

  /*
	 * search for the dataset that is currently defined by us
	 */
	for( i = start; i < device->Nr_Datasets; i++ )
  {

    /* build the H1 dataset structure */
    ds_info = Imx_Get_Udata_Ptr( (MT_IMXDS *)Imx_Get_Udata_Ptr( device->dataset[ i ] ));   

		/* build the H1 dataset structure */
	  h1_set_type        ( &h1_cmp, ( char )ds_info->type );
		h1_set_dbno        ( &h1_cmp, ( uchar )ds_info->db_nr );
		h1_set_start       ( &h1_cmp, device->dataset[ i ]->start );
		h1_set_length      ( &h1_cmp, device->dataset[ i ]->len );

    if( Compare_Ds( &h1_cmp, h1 ) == GOOD )
		{
      return i;
		}
  }

	return -1;
} //sapi_ds_search


/*-----------------------------------------------------------------------------
 * FUNCTION: int Compare_Ds
 *
 * PURPOSE:
 *
 -----------------------------------------------------------------------------*/
int Compare_Ds ( H1_HDR *cmp, H1_HDR *org )
{
	int start,
			length;


  /* check first if the types match */
	/* opcode of h1 write request header is 03 */
	/* opcode of h1 write ack header is 04 */
  if ( h1_get_type( cmp ) == h1_get_type( org )  )
  {

    /* if the type is Data Baustein DB then the numbers must match */
    if ( (h1_get_type( org ) == H1_DB) && (h1_get_dbno( org ) != h1_get_dbno( cmp )))
      return FL_ERROR;

    /* check the addressing parameters */
    if ((h1_get_start( org )) < (h1_get_start( cmp)))
    {

      /*
       * check if received DataSet is in front of DataSet
       */
      if (((h1_get_start( cmp)) + (h1_get_length( cmp))) <= (h1_get_start( org)))
        return FL_ERROR;

      /*
       * end of received DataSet just overlaps beginning of DataSet
       */
      length = h1_get_length( org )  - ( h1_get_start( cmp ) - h1_get_start( org ) );

			if (length < 0)
				return FL_ERROR;

			h1_set_length( cmp, length );
    }
    else
    {
      /*
       * check if received dataset is behind dataset
       */
      if ((h1_get_start( org)) >= (h1_get_start( cmp)) + (h1_get_length( cmp)))
        return FL_ERROR;

      if ((h1_get_start( org)) + (h1_get_length( org)) <= (h1_get_start( cmp)) + (h1_get_length( cmp)))
      {
        start  = h1_get_start ( org );
        length = h1_get_length( org ) ;

				h1_set_start ( cmp, start );
				h1_set_length( cmp, length );
      }

      /*
       * first part of received DataSet overlaps end of DataSet 
       */
      else
      {
        start = h1_get_start( org ) ;
        length = ( h1_get_start( cmp ) + h1_get_length( cmp ) ) - h1_get_start( org );

				h1_set_start ( cmp, start );
				h1_set_length( cmp, length );
      }
    }
  }
	else
		return FL_ERROR;

  return GOOD;
} //Compare_Ds


/*-----------------------------------------------------------------------------
 * FUNCTION: int sapi_board_init
 *
 * PURPOSE: With this function the cp board is known to the driver. i.e.device name
 *
 -----------------------------------------------------------------------------*/
int sapi_board_init ( TASK_ID *task, DEVICE *dev )
{
	int32 s7_ret;
	ord16 count;
	ord16 ref;
	int		i = 0, vfd, 
				vfd_found,
				cref_found;
	char	buf[ MAX_TAG_NAME +MAX_DIM_LENGTH +2];
	char	vfd_name[ 20 ];


	/* first assume we don't have the correct board */
	dev->board_id = -1;
  
	/* search for the local CP used with this device */
	do
  {

		memset( buf, 0x00, MAX_TAG_NAME + MAX_DIM_LENGTH +2);

		if( ( s7_ret = s7_get_device(	( ord16 )i,
																  &count,
																  buf) ) != S7_OK )
			return s7_ret;

    debug_log( task, "Search for board %s, configured %s", 2, dev->board_name , buf );

 		/* compare name on device and actual board name */
		if ( !strcmp( buf, dev->board_name ) )
		{

			dev->board_id = i;

			debug_log( task, "Board found %s, configured %s", 2, dev->board_name , buf );

			break;
		}
		
		i++;

  } while ( i < count );

  /* check if the CP has been found */
	if ( dev->board_id == -1 )
		return PD_NOBOARD;

  /* initialize the connection parameters for this device */
	dev->connect = CS_DISCONNECTED;

	dev->cp_descr = ( ulong)-1L; 
	dev->cref     = ( ushort)-1;
	dev->descr    = ( ulong)-1L;

	/* 
   * Initialize the address space used by this device. This address space
   * is made of the combination local CP and VFD. When initialized, this
   * address space will be allocated for the SAPI driver.
   */

	i = 0;
	cref_found = 0;
  
  do
	{

    /* get the Virtual Domain names defined for this local CP */
		if( ( s7_ret = s7_get_vfd( dev->board_name,
															 ( ord16 )i,
															 &count,
															 vfd_name) ) != S7_OK )
		  return s7_ret;

    //there is no VFD on the specified board
    if (!count)
    {
      debug_log( task, "No VFD found on board '%s'", 2, dev->board_name);
      return PD_NO_VFD;
    }

		debug_log( task, "Search connection %s in VFD %s (#%d)", 2, 
               dev->connection, vfd_name, count );

		/*
		 * check if this VFD is already used
		 */
		for ( vfd_found = 0, vfd = 0; vfd < Nr_vfd; vfd++)
		{
			if ( !strcmp( Vfd[ vfd ].board, dev->board_name) &&
					 !strcmp( Vfd[ vfd ].name, vfd_name) )
			{
				vfd_found = 1;
				break;
			}
		}

		/* initialize the SAPI communication layer */
		if ( !vfd_found )
		{
			if( ( s7_ret = s7_init(	dev->board_name,
															vfd_name,
															&dev->cp_descr )) != S7_OK )
			{
				i++;
  			continue;
			}
		}
		else
		{
			dev->cp_descr = Vfd[ vfd ].cp_descr;
		}

		/*
		 * get the connection reference of the connection
		 */
		if( ( s7_ret = s7_get_cref(	dev->cp_descr,
																dev->connection,
																&ref )) == S7_OK )  
		{

			dev->cref  = ref;
			dev->descr = dev->cp_descr;

      if ( s7_mini_db_set( S7_MINI_DB_INIT_REQ_PDU_SIZE, "512" ) != S7_OK ) 
      {  
				fl_status( task, "PDUSIZE", FLS_ERROR);
      }

      if ( s7_mini_db_set( S7_MINI_DB_INIT_RSP_PDU_SIZE, "512" ) != S7_OK )
      {  
				fl_status( task, "PDUSIZE", FLS_ERROR);
      }

      debug_log( task, "Packet PDU size %s ", 2, s7_mini_db_get( S7_MINI_DB_INIT_CNF_PDU_SIZE ) );

			cref_found = 1;
			break;
		}

		i++;

		/*
		 * connection has not been found in this VFD, close this VFD and
		 * keep on searching
		 */
		if ( !vfd_found )
		{

			if( ( s7_ret = s7_shut(	dev->cp_descr )) != S7_OK )
			{
				return s7_ret;
			}
		}

	} while ( i < count );

	/*
	 * check if the connection has been found
	 */
	if ( !cref_found )
		return PD_NOCONNECTION;
	else
	{
		if ( !vfd_found )
		{
			/*
			 * update the found VFD's
			 */
			if( ( Vfd = ( VFD *)realloc( Vfd, sizeof( VFD ) * (Nr_vfd +1) )) == ( VFD *)0 )
			{
				fl_status( task, "NOMEMORY", FLS_ERROR);
				dl_exit_task( task, 1);
			}

			strcpy( Vfd[ Nr_vfd ].board, dev->board_name);
			strcpy( Vfd[ Nr_vfd ].name, vfd_name);
			Vfd[ Nr_vfd ].cp_descr = dev->cp_descr;

			Nr_vfd++;

			if( ( s7_ret = win_set_callback (	dev->cp_descr  )) != S7_OK )
				return s7_ret;
		}
	}

	/* initialize the SAPI communication layer */
	return GOOD;
} //sapi_board_init


/*-----------------------------------------------------------------------------
 * FUNCTION: int validate_H1_header( H1_HDR *)
 *
 * PURPOSE:  Checks the H1 header on validity    
 * 
 -----------------------------------------------------------------------------*/
int validate_H1_header( H1_HDR *h1, int *fnc )
{
	*fnc = 0;

  /* check first the the System Identifier */
  switch ( h1->opcode )
  {

    case H1_WRITE_REQUEST:
    case H1_READ_REQUEST:

      /* check first the fixed parameters in the header */
      if( h1->system_id1  != 'S'			 			 ||
          h1->system_id2  != '7'             ||
          h1->hdr_length  != sizeof( H1_HDR) ||
          h1->opcode_id   != H1_OPCODE_ID    ||
          h1->opcode_len  != H1_OPCODE_LEN   ||
          h1->org_id      != H1_ORG_ID       ||
          h1->org_len     != H1_ORG_LEN)
        return H1_ERR_PACKET;

      /* check the ORG type on validity for S7 blocks */

			switch ( h1->blk.type )
			{
				case H1_DB:             /* data block   */
				case H1_FY:             /* flag byte    */
				case H1_IY:             /* input byte   */
				case H1_QY:             /* output bytes */

				case H1_D20:            /* data block 256 - 511   */  
				case H1_D21:            /* data block 512 - 767   */  
				case H1_D22:            /* data block 768 - 1023  */ 
				case H1_D23:            /* data block 1024 - 1279 */
				case H1_D24:            /* data block 1280 - 1535 */
				case H1_D25:            /* data block 1536 - 1791 */
				case H1_D26:            /* data block 1792 - 2047 */
				case H1_D27:            /* data block 2048 - 2303 */
				case H1_D28:            /* data block 2304 - 2559 */
				case H1_D29:            /* data block 2560 - 2815 */
				case H1_D30:            /* data block 2816 - 3071 */
				case H1_D31:            /* data block 3072 - 3327 */
				case H1_D32:            /* data block 3328 - 3583 */
				case H1_D33:            /* data block 3584 - 3839 */
				case H1_D34:            /* data block 3840 - 4095 */
					break;

				default:
					return H1_ERR_PACKET;
			}
      break;

    case H1_READ_ACK:
    case H1_WRITE_ACK:

      if ( h1->system_id1  != 'S'								||
					 h1->system_id2  != '7'								||
           h1->hdr_length  != sizeof( H1_HDR)		||
           h1->opcode_id   != H1_OPCODE_ID			||
           h1->opcode_len  != H1_OPCODE_LEN			||
           h1->org_id      != ( char)H1_ACK_ID	||
           h1->org_len     != H1_ACK_LEN) 
        return H1_ERR_PACKET;

      if ( h1->blk.error ) 
        return PD_HEADER_ERROR + h1->blk.error;
      break;

    default:
      return H1_ERR_PACKET;
  }

	/*
	 * determine the functionality
	 */
	switch ( h1->opcode )
	{

		case H1_READ_ACK:
			*fnc = IMX_READ;
			break;

		case H1_WRITE_ACK:
			*fnc = IMX_WRITE;
			break;

    case H1_WRITE_REQUEST:
			*fnc = IMX_RCV;
			break;
	}

  return 0;
} //validate_H1_header


/*-----------------------------------------------------------------------------
 * FUNCTION: void  create_sem
 *
 * PUPRPOSE: create semaphore for synchronizing access to the read connection
 *           and for setting up unsolicited receive connections
 *
 -----------------------------------------------------------------------------*/
int create_sem( DEVICE *device )
{

  /*
   * create the semaphore for waiting on the read command
   */
  if ( ( device->grab[ IMX_READ] = CreateSemaphore( NULL, 1, 1, NULL)) == NULL )
    return -1;

  /*
   * create the semaphore for waiting on the write command
   */
  if ( ( device->grab[ IMX_WRITE] = CreateSemaphore( NULL, 1, 1, NULL)) == NULL )
    return -1;

  /*
   * create the semaphore for waiting on the listen command
   */
  if ( ( device->grab[ IMX_RCV] = CreateSemaphore( NULL, 1, 1, NULL)) == NULL )
    return -1;

  return 0;
} //create_sem


/*-----------------------------------------------------------------------------
 * FUNCTION: void  destroy_sem
 *
 * PUPRPOSE: create semaphore for synchronizing access to the read connection
 *           and for setting up unsolicited receive connections
 *
 -----------------------------------------------------------------------------*/
int destroy_sem( DEVICE *device )
{

  if (device->grab[ IMX_READ] != NULL)
    CloseHandle( device->grab[ IMX_READ] );

  if (device->grab[ IMX_WRITE] != NULL)
    CloseHandle( device->grab[ IMX_WRITE] );

  if (device->grab[ IMX_RCV] != NULL)
    CloseHandle( device->grab[ IMX_RCV] );

  return 0;
} //destroy_sem


/*-----------------------------------------------------------------------------
 * FUNCTION: grab_read_connection
 *
 * PUPRPOSE: get hold of the read connection
 *
 -----------------------------------------------------------------------------*/
int grab_connection( DEVICE *device, int function )
{
	int error;

  error = WaitForSingleObject( device->grab[ function ], SAPI_WAIT );

	switch ( error )
	{
		case WAIT_FAILED:
		case WAIT_ABANDONED:
			error = PD_SYNC_ERROR;
			break;

    case WAIT_OBJECT_0:
			error = 0;
			break;

    case WAIT_TIMEOUT:
 			error = PD_REQ_TIMEOUT;
			break;
	}

  return error;
} //grab_connection


/*-----------------------------------------------------------------------------
 * FUNCTION: release_read_connection
 *
 * PUPRPOSE: release the read connection for use by others
 *
 -----------------------------------------------------------------------------*/
int release_connection( DEVICE *device, int function )
{

  ReleaseSemaphore( device->grab[ function ], 1, NULL );
  return 0;
} //release_connection