/**
 * \brief Example of usage of IQI's control DLL
 * \copyright 2013-2020, IQ2 Development GmbH, Germany. All rights reserved.
 */

#ifdef _MSC_VER

#ifdef _DEBUG
#pragma comment(lib, "iqcomm_dbg")
#else
#pragma comment(lib, "iqcomm")
#endif       

#endif

// neither EXPORT_API nor IMPORT_API means to use code just as a source code in some project
//#define EXPORT_API // to use header files to export functions as dll
#define IMPORT_API // to use header files to import functions from dll

#include "iqi_define.h"
#include "iqi_mst.h"
#include "iqi_dev.h"
#include "iqi_ethernet.h"

#define CHK_EQUAL(exp) \
    do { \
        if (!(exp)) return ERR_INTERNAL; \
    } while(0)

#define CHK_BYTE_SEQ_EQUAL(seq1, len1, seq2, len2, i) \
    do { \
		CHK_EQUAL(len1 == len2); \
		for (i = 0; i < len1; i++) \
			CHK_EQUAL(seq1[i] == seq2[i]); \
    } while(0)


static mst_StatusT to_mst_status(uchar08 data)
{
	mst_StatusT mst_status;

	mst_status.State = data & 0x07;
	mst_status.PDOutInvalid = (data >> 3) & 0x01;
	mst_status.PDInInvalid = (data >> 4) & 0x01;
	mst_status.Event = (data >> 5) & 0x01;
	mst_status.OD = (data >> 6) & 0x01;
	mst_status.PDOutSet = (data >> 7) & 0x01;

	return mst_status;
}

int16 example_master(int16 iPortNo, char08 *cpErrMsg)
{ 
  mst_ConfigT config;
  mst_StateT state;
  uchar08 pdIn[MAX_PD_BUF_LEN];
  int16 pdInSize;
  uchar08 pdOut[MAX_PD_BUF_LEN];
  uchar08 od[MAX_OD_BUF_LEN];
  uchar08 recv_buffer[256];
  int16 odSize;
  uint16 uiISDUErr = 0;
  EventQualifierT eventQualifier;
  uchar08 ucEventQualifier;
  uint16 uiEventCode;
    
  // Read IO-Link Master configuration from iqInterface
  CHK_RETURN(mst_GetConfig(iPortNo, &config, cpErrMsg));
  printf("Version of iqStack Master V%04X\n", config.stackVersion);
  printf("Config cycle time operate %.1f ms\n", (double)config.cycleTimeOperate/10);
  printf("Config rest of cycle time operate %.1f ms\n", (double)config.restOfCycleTimeOperate/10);
  printf("Config revision ID V%02X\n", (uchar08)(config.revisionID));
  printf("Inspection level %d\n", config.inspectionLevel);
  printf("Config device vendor ID %d\n", config.deviceVendorID);
  printf("Config device ID %d\n", config.deviceID);
  printf("Config device function ID %d\n", config.deviceFunctionID);
  printf("Config device serial number %s\n", config.deviceSerialNumber);
  printf("Config device serial number length %d\n", config.deviceSerialNumberLen);
  printf("Real current (or last) SDCI baudrate COM%d\n", config.realBaudrate + 1);
  printf("DS activation state %d\n", (uchar08)(config.dsActivState));
  printf("DS upload enable flag %s\n", config.dsUploadEnable ? "TRUE" : "FALSE");
  printf("DS download enable flag %s\n", config.dsDownloadEnable ? "TRUE" : "FALSE");
  // set IO-Link Master configuration to config
  // config.cycleTimeOperate = 50; // 5 ms
  // ...
  // Write IO-Link Master configuration to iqInterface 
  CHK_RETURN(mst_SetConfig(iPortNo, &config, cpErrMsg));

  // try to set operating mode to Preoperate and
  // wait for state changing to given state.
  // The achieved state is returned as a parameter state.
  // (see comment for mst_StateT in iqi_mst.h)
  CHK_RETURN(mst_SetOperatingMode(iPortNo, MST_OPERATING_MODE_PREOPERATE, 
                                  MST_STATE_PREOPERATE, recv_buffer, cpErrMsg));
  state = (mst_StateT)recv_buffer[0];
  printf("go to preoperate: state %d\n", state);

  // try to set operating mode to Operate
  CHK_RETURN(mst_SetOperatingMode(iPortNo, MST_OPERATING_MODE_OPERATE, 
                                  MST_STATE_OPERATE, recv_buffer, cpErrMsg));
  state = (mst_StateT)recv_buffer[0];
  printf("go to operate: state %d\n", state);

  // Get iqInterface master status and input process data from device
  // (see comment for mst_StatusT in iqi_mst.h)
  printf("read master status\n");
  ASSIGN_RETURN(pdInSize, mst_GetStatus(iPortNo, recv_buffer,
                                        pdIn, 32, cpErrMsg));  

  pdOut[0] = 0x00;
  pdOut[1] = 0x00;
  // Set process data output
  printf("set pd out\n");
  CHK_RETURN(mst_SetPDValue(iPortNo, pdOut, 2, cpErrMsg));  

  // Set process data output valid
  printf("set pd out valid\n");
  CHK_RETURN(mst_SetPDValidity(iPortNo, TRUE, cpErrMsg));  

  // try to set operating mode to Inactive (Fallback)
  CHK_RETURN(mst_SetOperatingMode(iPortNo, MST_OPERATING_MODE_INACTIVE, 
                                  MST_STATE_INACTIVE, recv_buffer, cpErrMsg));
  state = (mst_StateT)recv_buffer[0];
  printf("go to inactive: state %d\n", state);

  // try to set operating mode to Auto (permanent seach of IO-Link device)
  CHK_RETURN(mst_SetOperatingMode(iPortNo, MST_OPERATING_MODE_AUTO, 
                                  MST_STATE_OPERATE, recv_buffer, cpErrMsg));
  state = (mst_StateT)recv_buffer[0];
  printf("go to auto: state %d\n", state);

  // read ISDU data (device serial number)
  // possible ISDU is saved in uiISDUErr
  printf("read device serial number\n");
  ASSIGN_RETURN(odSize, mst_ReadOD(iPortNo, 0x0015, 0x00, od, 255, &uiISDUErr, cpErrMsg));  
  
#if 0
  // write ISDU data (reset device command)
  // possible ISDU is saved in uiISDUErr
  od[0] = 0x80;
  printf("reset device\n");
  CHK_RETURN(mst_WriteOD(iPortNo, 0x0002, 0x00, od, 1, &uiISDUErr, cpErrMsg));  
#endif

  //raise comlost event by power cycle
  CHK_RETURN(mst_PowerControl(iPortNo, 0, cpErrMsg));
  Sleep(1000);
  CHK_RETURN(mst_PowerControl(iPortNo, 1, cpErrMsg));

  // wait and read comlost event
  CHK_RETURN(mst_ReadEvent(iPortNo, &ucEventQualifier, &uiEventCode, cpErrMsg));
  eventQualifier.Byte = ucEventQualifier;
  printf("read event instance %d, source %d, type %d, mode %d and code 0x%04X\n", 
    eventQualifier.Fields.Instance, eventQualifier.Fields.Source, 
    eventQualifier.Fields.Type, eventQualifier.Fields.Mode, uiEventCode);
  
  // set operating mode to inactive to stop auto scaning mode of IO-Link device
  CHK_RETURN(mst_SetOperatingMode(iPortNo, MST_OPERATING_MODE_INACTIVE, 
                                  MST_STATE_INACTIVE, recv_buffer, cpErrMsg));
  state = (mst_StateT)recv_buffer[0];
  printf("go to inactive: state %d\n", state);

  // disconnect iqInterface and close comport
  CHK_RETURN(mst_Disconnect(iPortNo, cpErrMsg));

  return ERR_NONE;
}

int16 example_master_comport(uchar08 mst_port, char08 *cpErrMsg)
{
  int16 iPortNo;
  uchar08 ucPortNoStart;
  uchar08 ucPortNoFinish;

  if (mst_port == 0)
  {
    ucPortNoStart = 1;
    ucPortNoFinish = 255;
  }
  else
  {
    ucPortNoStart = (uchar08)mst_port;
    ucPortNoFinish = (uchar08)mst_port;
  }

  printf("Test generic master over comport\n");

  // Search iqInterface between comport numbers ucPortNoStart and ucPortNoFinish
  // and try to connect it as IO-Link master.
  ASSIGN_RETURN(iPortNo, mst_Connect(ucPortNoStart, ucPortNoFinish, cpErrMsg));
  printf("iqInterface Master found on comport %d\n", iPortNo);

  return example_master(iPortNo, cpErrMsg);
}

// ethernet connection is tested only in networks with standart configuration 
// (TCP/IPv4, dhcp, auto ip assignment etc.)
// iqInteraface in generic device mode can be managed the same way over ethernet TCP connection,
// using dev_EthernetConnect instead of mst_EthernetConnect
int16 example_master_ethernet(char08 *cpErrMsg)
{
  enet_ConnectionT connections[5];
  uchar08 ucFoundConnectionNumber = 0;
  int16 iPortNo;
  
  printf("Test generic master over ethernet\n");

  // startup winsock2 library, very important to call before start of ethernet connection usage
  CHK_RETURN(enet_Startup(cpErrMsg));

  // search iqInterface in all availiable IPv4 networks 
  // (takes time, optional, if ip address of iqInterface is unknown)
  // UDP broadcast message to the port 30718 is used
  CHK_RETURN(enet_Search(connections, 5, &ucFoundConnectionNumber, 500, cpErrMsg));
    
  // ip and port can be set for manual connection without comm_EthernetSearch
  printf("iqInterface IP Address: %s\n", connections[0].cpIqInterfaceIpAddr); // IPv4, string in decimal format like "194.56.12.3"
  printf("iqInterface TCP port: %d\n", connections[0].uiPort);                // default 10001
  
  // MAC address, serial number and pc local ip are just information returned by comm_EthernetSearch, 
  // which can be ignored in case of manual connection
  printf("iqInterface MAC Address: %s\n", connections[0].cpIqInterfaceMacAddr);
  printf("iqInterface serial number: %I64u\n", connections[0].ul64SerialNumber); // can be found on the case/box of iqInterface
  printf("PC local IP Address: %s\n", connections[0].cpLocalIpAddr);            
  
  // returns virtual port 'iPortNo' (connection handle)
  // which can be used in all other operations the same way as for comport
  ASSIGN_RETURN(iPortNo, mst_EthernetConnect(&(connections[0]), 2000, cpErrMsg));
  
  CHK_RETURN(example_master(iPortNo, cpErrMsg));
  
  // free winsock2 library resources
  CHK_RETURN(enet_Cleanup(cpErrMsg));

  return ERR_NONE;
}

// This is example of full ISDU control from generic device side.
// If dev_ConfigT.ISDURspType = OD_RSP_TYPE_MANUAL is set by dev_SetConfig(),
// ISDU requests from master can be waited on PC with dev_GetODRequest()
// and responded with custom data from PC using dev_ODResponse().
// The configured (saved in iqInterface) ISDU parameters are not used thereby.
int16 example_isdu_manual_request_response(int16 mst_port, int16 dev_port, 
                                           dev_ConfigT * config, char08 *cpErrMsg)
{
  int i;
  int16 res;
  uint16 uiIndex;
  uchar08 ucSubindex;
  uchar08 ucODType;
  uchar08 od1[2], od2[2];
  uchar08 recv_buffer[256];
  uint16 uiISDUErr = 0;

  config->ISDURspType = OD_RSP_TYPE_MANUAL;
  printf("Configure IO-Link generic device to response ISDU manually with data from PC\n");
  CHK_RETURN(dev_SetConfig(dev_port, config, cpErrMsg));

  // master goes to operate
  printf("go to operate\n");
  CHK_RETURN(mst_SetOperatingMode(mst_port, MST_OPERATING_MODE_OPERATE, 
                                  MST_STATE_OPERATE, recv_buffer, cpErrMsg));

  // ISDU write ok example
  od1[0] = 0x43;
  od1[1] = 0x21;
  printf("start write index 0x0015 subindex 0 with 2-byte value 0x%02X, 0x%02X on master side\n", od1[0], od1[1]);
  CHK_RETURN(mst_StartWriteOD(mst_port, 0x0015, 0x00, od1, 2, cpErrMsg));

  ASSIGN_RETURN(res, dev_GetODRequest(dev_port, &ucODType, &uiIndex, &ucSubindex, od2, 2, &uiISDUErr, cpErrMsg));
  printf("ISDU request type %d index 0x%04X subindex 0x%02X value 0x%02X, 0x%02X (size %d) is received on device side\n", 
    ucODType, uiIndex, ucSubindex, od2[0], od2[1], res);
  CHK_BYTE_SEQ_EQUAL(od1, 2, od2, res, i);

  printf("send ok response from device\n");
  dev_ODResponse(dev_port, ucODType, 0, 0, 0, cpErrMsg);

  printf("wait od response on master side\n");
  CHK_RETURN(mst_WaitODRsp(mst_port, cpErrMsg));
  CHK_RETURN(mst_GetWriteODRsp(mst_port, &uiISDUErr, cpErrMsg));
  printf("ISDU write ok response is received on master side\n");

  // ISDU write error example
  od1[0] = 0x43;
  od1[1] = 0x21;
  printf("start write index 0x0015 subindex 0 with 2-byte value 0x%02X, 0x%02X on master side\n", od1[0], od1[1]);
  CHK_RETURN(mst_StartWriteOD(mst_port, 0x0015, 0x00, od1, 2, cpErrMsg));

  ASSIGN_RETURN(res, dev_GetODRequest(dev_port, &ucODType, &uiIndex, &ucSubindex, od2, 2, &uiISDUErr, cpErrMsg));
  printf("ISDU request type %d index 0x%04X subindex 0x%02X value 0x%02X, 0x%02X (size %d) is received on device side\n", 
    ucODType, uiIndex, ucSubindex, od2[0], od2[1], res);
  CHK_BYTE_SEQ_EQUAL(od1, 2, od2, res, i);


  printf("send error 0x%04X response from device\n", 0x8011);
  dev_ODResponse(dev_port, DEV_ODREQ_TYPE_WRITE_ERR, 0, 0, 0x8011, cpErrMsg);

  printf("wait od response on master side\n");
  CHK_RETURN(mst_WaitODRsp(mst_port, cpErrMsg));
  uiISDUErr = 0;
  if (mst_GetWriteODRsp(mst_port, &uiISDUErr, cpErrMsg) != ERR_OPERATION)
  {
    printf("test failed: master have not received isdu error\n");
    return ERR_OPERATION;
  }
  printf("ISDU write response with error 0x%04X is received on master side\n", uiISDUErr);
  CHK_EQUAL(uiISDUErr == 0x8011);

  // ISDU read ok example
  printf("start read index 0x0015 subindex 0\n");
  CHK_RETURN(mst_StartReadOD(mst_port, 0x0015, 0x00, cpErrMsg));

  ASSIGN_RETURN(res, dev_GetODRequest(dev_port, &ucODType, &uiIndex, &ucSubindex, od2, 2, &uiISDUErr, cpErrMsg));
  printf("ISDU request type %d index 0x%04X subindex 0x%02X is received on device side\n", 
    ucODType, uiIndex, ucSubindex);

  od1[0] = 0x12;
  od1[1] = 0x34;
  printf("send ok response from device with value 0x%02X, 0x%02X\n", od1[0], od1[1]);
  dev_ODResponse(dev_port, ucODType, od1, 2, 0, cpErrMsg);

  printf("wait od response on master side\n");
  CHK_RETURN(mst_WaitODRsp(mst_port, cpErrMsg));
  ASSIGN_RETURN(res, mst_GetReadODRsp(mst_port, od2, 2, &uiISDUErr, cpErrMsg));
  printf("ISDU read ok response with value 0x%02X, 0x%02X (size %d) is received on master side\n", od2[0], od2[1], res);
  CHK_BYTE_SEQ_EQUAL(od1, 2, od2, res, i);

  // ISDU read error example
  printf("start read index 0x0015 subindex 0\n");
  CHK_RETURN(mst_StartReadOD(mst_port, 0x0015, 0x00, cpErrMsg));

  ASSIGN_RETURN(res, dev_GetODRequest(dev_port, &ucODType, &uiIndex, &ucSubindex, od2, 2, &uiISDUErr, cpErrMsg));
  printf("ISDU request type %d index 0x%04X subindex 0x%02X is received on device side\n", 
    ucODType, uiIndex, ucSubindex);

  printf("send error 0x%04X response from device\n", 0x8011);
  dev_ODResponse(dev_port, DEV_ODREQ_TYPE_READ_ERR, 0, 0, 0x8011, cpErrMsg);

  printf("wait od response on master side\n");
  CHK_RETURN(mst_WaitODRsp(mst_port, cpErrMsg));
  uiISDUErr = 0;
  if (mst_GetReadODRsp(mst_port, od2, 2, &uiISDUErr, cpErrMsg) != ERR_OPERATION)
  {
    printf("test failed: master have not received isdu error\n");
    return ERR_OPERATION;
  }
  printf("ISDU write response with error 0x%04X is received on master side\n", uiISDUErr);
  CHK_EQUAL(uiISDUErr == 0x8011);

  // master goes to inactive
  printf("master sends fallback\n");
  CHK_RETURN(mst_SetOperatingMode(mst_port, MST_OPERATING_MODE_INACTIVE, 
                                  MST_OPERATING_MODE_INACTIVE, recv_buffer, cpErrMsg));

  return ERR_NONE;  
}

// iqInterface generic device should be connected to iqInterface Master
// to perform this test
int16 example_generic_device(int16 master_port, int16 device_port, char08 *cpErrMsg)
{
  int i;
  uchar08 ucPortNoStart;
  uchar08 ucPortNoFinish;
  dev_ConfigT config, chkConfig;
  dev_StatusT status;
  dev_StateT state;
  uchar08 recv_buffer[256];
  uchar08 pdInMst[MAX_PD_BUF_LEN], pdInDev[MAX_PD_BUF_LEN];
  int16 pdOutSize;
  int16 pdInSize;
  uchar08 pdOutMst[MAX_PD_BUF_LEN], pdOutDev[MAX_PD_BUF_LEN];
  uchar08 od1[MAX_OD_BUF_LEN], od2[MAX_OD_BUF_LEN];
  int16 odSize;
  dev_ISDU_FlagT ISDUFlag;
  uint16 uiISDUErr = 0;
  EventQualifierT eventQualifier;
  uchar08 ucEventQualifierMst, ucEventQualifierDev;
  uint16 uiEventCodeMst, uiEventCodeDev;
  int16 mst_port, dev_port;

  if (master_port == 0)
  {
    ucPortNoStart = 1;
    ucPortNoFinish = 20;
  }
  else
  {
    ucPortNoStart = (uchar08)master_port;
    ucPortNoFinish = (uchar08)master_port;
  }

  printf("Test generic device over comport\n");

  // connect iqInterface Master to test iqInterface generic device
  ASSIGN_RETURN(mst_port, mst_Connect(ucPortNoStart, ucPortNoFinish, cpErrMsg));
  printf("Connect to iqInterface Master on comport %d\n", mst_port);
  if (master_port != 0) CHK_EQUAL(mst_port == master_port);

  if (device_port == 0)
  {
    ucPortNoStart = 1;
    ucPortNoFinish = 20;
  }
  else
  {
    ucPortNoStart = (uchar08)device_port;
    ucPortNoFinish = (uchar08)device_port;
  }

  // Search iqInterface generic device between comport numbers ucPortNoStart and ucPortNoFinish
  // and try to connect it as IO-Link master.
  ASSIGN_RETURN(dev_port, dev_Connect(ucPortNoStart, ucPortNoFinish, cpErrMsg));
  printf("iqInterface generic device found on comport %d\n", dev_port);
  if (device_port != 0) CHK_EQUAL(dev_port == device_port);

  printf("Read IO-Link generic device configuration from iqInterface\n");
  CHK_RETURN(dev_GetConfig(dev_port, &config, cpErrMsg));
  printf("Version of iqStack Device V%04X\n", config.stackVersion);
  printf("Master cycle time in last operate %.1f ms\n", (double)config.masterCycleTime/10);
  printf("Min cycle time operate %.1f ms\n", (double)config.minCycleTime/10);
  printf("Revision ID V%02X\n", (uchar08)(config.revisionID));
  printf("ISDU is %ssupported\n", config.isISDUSupported == TRUE ? "" : "not ");
  printf("On-request data size preoperate %d bytes\n", config.ODSizePreoperate);
  printf("On-request data size operate %d bytes\n", config.ODSizeOperate);
  printf("Process data input size %d %s\n", config.PDInLength, config.isPDInLengthBytes == TRUE ? "bytes" : "bits");
  printf("Process data output size %d %s\n", config.PDOutLength, config.isPDOutLengthBytes == TRUE ? "bytes" : "bits");
  printf("Generic device vendor ID %d\n", config.deviceVendorID);
  printf("Generic device ID %d\n", config.deviceID);
  printf("Supported baudrate COM%d\n", config.supportedBaudrate + 1);
  printf("ISDU response %s\n", config.ISDURspType == OD_RSP_TYPE_AT_ONCE ? "immediately" : "after confirmation from PC" );
  // set IO-Link generic device configuration to config
  config.minCycleTime = 50; // 5 ms
  config.revisionID = IOL_REV_ID_V1_1;
  config.isISDUSupported = TRUE;
  config.ODSizePreoperate = 2;
  config.ODSizeOperate = 2;
  config.isPDInLengthBytes = FALSE;
  config.PDInLength = 16; // 16 bit
  config.isPDOutLengthBytes = TRUE;
  config.PDOutLength = 4; // 4 bytes
  config.deviceVendorID = 123;
  config.deviceID = 123456;
  config.supportedBaudrate = IOL_BAUDRATE_COM2;
  config.ISDURspType = OD_RSP_TYPE_AT_ONCE; // set OD_RSP_TYPE_AUTO or OD_RSP_TYPE_MANUAL to use dev_GetODRequest/dev_ODResponse, see also dev_ISDURspTypeT
  printf("Write IO-Link generic device configuration to iqInterface\n");
  CHK_RETURN(dev_SetConfig(dev_port, &config, cpErrMsg));
  CHK_RETURN(dev_GetConfig(dev_port, &chkConfig, cpErrMsg));
  CHK_EQUAL(chkConfig.minCycleTime == 50); // 5 ms
  CHK_EQUAL(chkConfig.revisionID == IOL_REV_ID_V1_1);
  CHK_EQUAL(chkConfig.isISDUSupported == TRUE);
  CHK_EQUAL(chkConfig.ODSizePreoperate == 2);
  CHK_EQUAL(chkConfig.ODSizeOperate == 2);
  CHK_EQUAL(chkConfig.isPDInLengthBytes == FALSE);
  CHK_EQUAL(chkConfig.PDInLength == 16); // 16 bit
  CHK_EQUAL(chkConfig.isPDOutLengthBytes == TRUE);
  CHK_EQUAL(chkConfig.PDOutLength == 4); // 4 bytes
  CHK_EQUAL(chkConfig.deviceVendorID == 123);
  CHK_EQUAL(chkConfig.deviceID == 123456);
  CHK_EQUAL(chkConfig.supportedBaudrate == IOL_BAUDRATE_COM2);
  CHK_EQUAL(chkConfig.ISDURspType == OD_RSP_TYPE_AT_ONCE);

  printf("clear ISDU structure in generic device\n");
  CHK_RETURN(dev_RemoveAllISDU(dev_port, cpErrMsg));
    
  ISDUFlag.Data = 0;
  ISDUFlag.Bits.IsRead = TRUE;
  ISDUFlag.Bits.IsWrite = TRUE;
  od1[0] = 0x12;
  od1[1] = 0x34;
  printf("add ISDU parameter on index 0x0015 subindex 0 with 2-byte value 0x%02X, 0x%02X and r/w access\n", od1[0], od1[1]);
  CHK_RETURN(dev_AddISDU(dev_port, ISDUFlag.Data, 0x0015, 0, od1, 2, cpErrMsg));

  // master goes to preoperate
  printf("go to preoperate\n");
  CHK_RETURN(mst_SetOperatingMode(mst_port, MST_OPERATING_MODE_PREOPERATE, 
                                  MST_STATE_PREOPERATE, recv_buffer, cpErrMsg));
    
  // Get iqInterface generic device status (output process data is absent in preoperate)
  // (see comment for dev_StatusT in iqi_dev.h)
  ASSIGN_RETURN(pdOutSize, dev_GetStatus(dev_port, (uchar08*)&status, 
                                         pdOutDev, 32, cpErrMsg));
  state = (dev_StateT)status.State;
  printf("generic device state %d\n", state);
  CHK_EQUAL(state == DEV_STATE_PREOPERATE);
  CHK_EQUAL(pdOutSize == 4);

  // master goes to operate
  printf("go to operate\n");
  CHK_RETURN(mst_SetOperatingMode(mst_port, MST_OPERATING_MODE_OPERATE, 
                                  MST_STATE_OPERATE, recv_buffer, cpErrMsg));

  pdOutMst[0] = 0x12;
  pdOutMst[1] = 0x34;
  pdOutMst[2] = 0x56;
  pdOutMst[3] = 0x78;
  printf("set pd out 0x%01X, 0x%01X, 0x%01X, 0x%01X\n", pdOutMst[0], pdOutMst[1], pdOutMst[2], pdOutMst[3]);
  CHK_RETURN(mst_SetPDValue(mst_port, pdOutMst, 4, cpErrMsg));  

  printf("set pd out valid\n");
  CHK_RETURN(mst_SetPDValidity(mst_port, TRUE, cpErrMsg));
  
  // Get iqInterface generic device status and output process data from master
  // (see comment for dev_StatusT in iqi_dev.h)
  ASSIGN_RETURN(pdOutSize, dev_GetStatus(dev_port, (uchar08*)&status, 
                                         pdOutDev, 32, cpErrMsg));
  state = (dev_StateT)status.State;
  printf("generic device state %d\n", state);
  printf("pd out is 0x%01X, 0x%01X, 0x%01X, 0x%01X (%svalid)\n", 
    pdOutDev[0], pdOutDev[1], pdOutDev[2], pdOutDev[3], status.PDOutInvalid == TRUE ? "not " : "");
  CHK_BYTE_SEQ_EQUAL(pdOutMst, 4, pdOutDev, pdOutSize, i);


  pdInDev[0] = 0x12;
  pdInDev[1] = 0x34;
  printf("set pd in valid and value 0x%01X, 0x%01X\n", pdInDev[0], pdInDev[1]);
  CHK_RETURN(dev_SetPDIn(dev_port, pdInDev, 2, TRUE, cpErrMsg));

  ASSIGN_RETURN(pdInSize, mst_GetStatus(mst_port, recv_buffer,
                                        pdInMst, 32, cpErrMsg));

  mst_StatusT mst_status = to_mst_status(recv_buffer[0]);
  printf("pd in is 0x%01X, 0x%01X (%svalid)\n", 
    pdInMst[0], pdInMst[1], mst_status.PDInInvalid == TRUE ? "not " : "");
  CHK_BYTE_SEQ_EQUAL(pdInDev, 2, pdInMst, pdInSize, i);
    
  eventQualifier.Byte = 0;
  eventQualifier.Fields.Instance = EVENT_INSTANCE_APPL;
  eventQualifier.Fields.Source = EVENT_SOURCE_REMOTE;
  eventQualifier.Fields.Type = EVENT_TYPE_NOTIFICATION;
  eventQualifier.Fields.Mode = EVENT_MODE_SINGLE_SHOT;
  ucEventQualifierDev = eventQualifier.Byte;
  uiEventCodeDev = 0x1234;
  printf("generic device sends event instance %d, source %d, type %d, mode %d, code 0x%04X\n", 
    eventQualifier.Fields.Instance, eventQualifier.Fields.Source, 
    eventQualifier.Fields.Type, eventQualifier.Fields.Mode, uiEventCodeDev);
  CHK_RETURN(dev_SendEvent(dev_port, eventQualifier.Byte, uiEventCodeDev, cpErrMsg));

  CHK_RETURN(mst_ReadEvent(mst_port, &ucEventQualifierMst, &uiEventCodeMst, cpErrMsg));
  eventQualifier.Byte = ucEventQualifierMst;
  printf("master receives      event instance %d, source %d, type %d, mode %d, code 0x%04X\n", 
    eventQualifier.Fields.Instance, eventQualifier.Fields.Source, 
    eventQualifier.Fields.Type, eventQualifier.Fields.Mode, uiEventCodeMst);
  CHK_EQUAL(ucEventQualifierDev == ucEventQualifierMst);
  CHK_EQUAL(uiEventCodeDev == uiEventCodeMst);

  od1[0] = 0x43;
  od1[1] = 0x21;
  printf("write index 0x0015 subindex 0 with 2-byte value 0x%02X, 0x%02X\n", od1[0], od1[1]);
  CHK_RETURN(mst_WriteOD(mst_port, 0x0015, 0x00, od1, 2, &uiISDUErr, cpErrMsg));
  
  ASSIGN_RETURN(odSize, dev_GetISDUValue(dev_port, 0x0015, 0x00, od2, 2, cpErrMsg));
  printf("get ISDU value index 0x0015 subindex 0: 0x%02X, 0x%02X\n", od2[0], od2[1]);
  CHK_BYTE_SEQ_EQUAL(od1, 2, od2, odSize, i);

  od1[0] = 0x12;
  od1[1] = 0x34;
  printf("set ISDU value index 0x0015 subindex 0: 0x%02X, 0x%02X\n", od1[0], od1[1]);
  CHK_RETURN(dev_SetISDUValue(dev_port, 0x0015, 0x00, od1, 2, cpErrMsg));
    
  ASSIGN_RETURN(odSize, mst_ReadOD(mst_port, 0x0015, 0x00, od2, 2, &uiISDUErr, cpErrMsg));
  printf("read index 0x0015 subindex 0 value 0x%02X, 0x%02X\n", od2[0], od2[1]);
  CHK_BYTE_SEQ_EQUAL(od1, 2, od2, odSize, i);

  printf("receive upload request event because of dev_SetISDUValue\n");
  CHK_RETURN(mst_ReadEvent(mst_port, &ucEventQualifierMst, &uiEventCodeMst, cpErrMsg));
  eventQualifier.Byte = ucEventQualifierMst;
  printf("master receives      event instance %d, source %d, type %d, mode %d, code 0x%04X\n", 
    eventQualifier.Fields.Instance, eventQualifier.Fields.Source, 
    eventQualifier.Fields.Type, eventQualifier.Fields.Mode, uiEventCodeMst);
  CHK_EQUAL(uiEventCodeMst == 0xFF91);

  odSize = mst_ReadOD(mst_port, 0x0016, 0x00, od1, 2, &uiISDUErr, cpErrMsg);
  printf("read index 0x0016 subindex 0: ISDU error 0x%04X\n", uiISDUErr);
  CHK_EQUAL(uiISDUErr == 0x8011);

  // master goes to inactive
  printf("master sends fallback\n");
  CHK_RETURN(mst_SetOperatingMode(mst_port, MST_OPERATING_MODE_INACTIVE, 
                                  MST_OPERATING_MODE_INACTIVE, recv_buffer, cpErrMsg));
  ASSIGN_RETURN(pdOutSize, dev_GetStatus(dev_port, recv_buffer,
                                         pdOutDev, 32, cpErrMsg));
  state = (dev_StateT)recv_buffer[0];
  printf("generic device state %d\n", state);
  CHK_EQUAL(state == DEV_STATE_INACTIVE);

  CHK_RETURN(example_isdu_manual_request_response(mst_port, dev_port, &config, cpErrMsg));

  // disconnect iqInterface and close comport
  CHK_RETURN(dev_Disconnect(dev_port, cpErrMsg));

  // disconnect master iqInterface
  CHK_RETURN(mst_Disconnect(mst_port, cpErrMsg));

  return ERR_NONE;
}

int16 example_master_reboot(uchar08 mst_port, char08 *cpErrMsg)
{
	int16 iPortNo;
	uchar08 ucPortNoStart;
	uchar08 ucPortNoFinish;

	if (mst_port == 0)
	{
		ucPortNoStart = 1;
		ucPortNoFinish = 20;
	}
	else
	{
		ucPortNoStart = (uchar08)mst_port;
		ucPortNoFinish = (uchar08)mst_port;
	}

	printf("reboot device\n");

	// Search iqInterface between comport numbers ucPortNoStart and ucPortNoFinish
	// and try to connect it as IO-Link master.
	ASSIGN_RETURN(iPortNo, mst_Connect(ucPortNoStart, ucPortNoFinish, cpErrMsg));
	printf("iqInterface Master found on comport %d\n", iPortNo);

	return mst_Reboot(mst_port, cpErrMsg);
}

int16 example_master_parameters_reset(uchar08 mst_port, char08 *cpErrMsg)
{
	int16 iPortNo;
	uchar08 ucPortNoStart;
	uchar08 ucPortNoFinish;

	if (mst_port == 0)
	{
		ucPortNoStart = 1;
		ucPortNoFinish = 20;
	}
	else
	{
		ucPortNoStart = (uchar08)mst_port;
		ucPortNoFinish = (uchar08)mst_port;
	}

	printf("reboot device\n");

	// Search iqInterface between comport numbers ucPortNoStart and ucPortNoFinish
	// and try to connect it as IO-Link master.
	ASSIGN_RETURN(iPortNo, mst_Connect(ucPortNoStart, ucPortNoFinish, cpErrMsg));
	printf("iqInterface Master found on comport %d\n", iPortNo);

	return mst_ResetParameters2Default(mst_port, cpErrMsg);
}


//#define RUN_READ_BLOB_DATA 1

#ifdef RUN_READ_BLOB_DATA

#define DATA_BUFFER_SIZE 256
//#define RUN_READ_BLOB_DATA 1
uchar08 buffer[DATA_BUFFER_SIZE] = { 0 };
#define READ_BLOB_ID -4096

int16 example_read_write_blob_data(uchar08 mst_port, char08 *cpErrMsg)
{
	int16 iPortNo;
	uchar08 ucPortNoStart;
	uchar08 ucPortNoFinish;

	if (mst_port == 0)
	{
		ucPortNoStart = 1;
		ucPortNoFinish = 0xFF;
	}
	else
	{
		ucPortNoStart = (uchar08)mst_port;
		ucPortNoFinish = (uchar08)mst_port;
	}

	printf("reboot device\n");

	// Search iqInterface between comport numbers ucPortNoStart and ucPortNoFinish
	// and try to connect it as IO-Link master.
	ASSIGN_RETURN(iPortNo, mst_Connect(ucPortNoStart, ucPortNoFinish, cpErrMsg));
	printf("iqInterface Master found on comport %d\n", iPortNo);

	// try to set operating mode to Operate
	uchar08 ucState;
	CHK_RETURN(mst_SetOperatingMode(iPortNo, MST_OPERATING_MODE_OPERATE,
		MST_STATE_OPERATE, &ucState, cpErrMsg));
	mst_StateT state = (mst_StateT)ucState;
	printf("go to operate: state %d\n", state);

	// Get iqInterface master status and input process data from device
	// (see comment for mst_StatusT in iqi_mst.h)
	printf("read master status\n");
	mst_StatusT status;
	uchar08 pdIn[MAX_PD_BUF_LEN];
	int16 pdInSize;
	ASSIGN_RETURN(pdInSize, mst_GetStatus(iPortNo, (uchar08*)&status,
		pdIn, 32, cpErrMsg));

	//read vendor name
	uint16 uiISDUErr = 0;
	uchar08 buffer[MAX_OD_BUF_LEN] = {0};
	mst_ReadOD(iPortNo, 0x0010, 0x00, buffer, MAX_OD_BUF_LEN, &uiISDUErr, cpErrMsg);
	printf("Vendor name: %s\n", buffer);

	memset(buffer, 0, DATA_BUFFER_SIZE);
	ulong32 recvDataSize;
	printf("readind BLOB id: %d\n", READ_BLOB_ID);
	int16 result = mst_ReadBlob(iPortNo, READ_BLOB_ID, buffer, DATA_BUFFER_SIZE, &recvDataSize, cpErrMsg);

	if (result == ERR_NONE)
	{
		printf("Received data for BLOB id: %d\n", READ_BLOB_ID);
		for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
		{
			printf("0x%X ", buffer[byteIndex]);
			if ((byteIndex != 0) && (byteIndex % 16) == 0)
			{
				printf("\n");
			}
		}
		printf("\n");
	}
	else
	{
		return result;
	}

	//write BLOB data 
	uchar08* write_buffer = malloc(recvDataSize);

	for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
	{
		write_buffer[byteIndex] ^= buffer[byteIndex];
		//write_buffer[byteIndex] = buffer[byteIndex];
	}

	printf("Write BLOB id: %d\n", -READ_BLOB_ID);
	for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
	{
		printf("0x%X ", write_buffer[byteIndex]);
		if ((byteIndex != 0) && (byteIndex % 16) == 0)
		{
			printf("\n");
		}
	}
	printf("\n");
	result = mst_WriteBlob(iPortNo, -READ_BLOB_ID, write_buffer, recvDataSize, cpErrMsg);

	if (result < ERR_NONE)
	{
		return result;
	}

	//test written data
	memset(buffer, 0, DATA_BUFFER_SIZE);
	printf("readind BLOB id: %d\n", READ_BLOB_ID);
	result = mst_ReadBlob(iPortNo, READ_BLOB_ID, buffer, DATA_BUFFER_SIZE, &recvDataSize, cpErrMsg);

	if (result < ERR_NONE)
	{
		return result;
	}

	//validate data

	for (unsigned int byteIndex = 0; byteIndex < recvDataSize; byteIndex++)
	{
		if (write_buffer[byteIndex] != buffer[byteIndex])
		{
			snprintf(cpErrMsg, MAX_ERR_STR_LEN, MAX_ERR_STR_LEN,
				"written data is not equal as read data index %d, wrote: %X, read: %x \n",
				byteIndex, write_buffer[byteIndex], buffer[byteIndex]);
			return ERR_OPERATION;
		}
	}

	printf("Written and read data are equal\n");

	free(write_buffer);

	return result;
}

#endif //RUN_READ_BLOB_DATA

//#define TEST_FIRMWARE_UPDATE 1

#ifdef TEST_FIRMWARE_UPDATE

int16 example_firmware_update(uchar08 mst_port, char08 *cpErrMsg)
{
	int16 result = ERR_CUSTOM;
	int16 iPortNo;
	uchar08 ucPortNoStart;
	uchar08 ucPortNoFinish;

	if (mst_port == 0)
	{
		ucPortNoStart = 1;
		ucPortNoFinish = 0xFF;
	}
	else
	{
		ucPortNoStart = (uchar08)mst_port;
		ucPortNoFinish = (uchar08)mst_port;
	}

	// Search iqInterface between comport numbers ucPortNoStart and ucPortNoFinish
	// and try to connect it as IO-Link master.
	ASSIGN_RETURN(iPortNo, mst_Connect(ucPortNoStart, ucPortNoFinish, cpErrMsg));
	printf("iqInterface Master found on comport %d\n", iPortNo);

	// try to set operating mode to Operate
	uchar08 ucState;
	CHK_RETURN(mst_SetOperatingMode(iPortNo, MST_OPERATING_MODE_OPERATE,
		MST_STATE_OPERATE, &ucState, cpErrMsg));
	mst_StateT state = (mst_StateT)ucState;
	printf("go to operate: state %d\n", state);

	// Get iqInterface master status and input process data from device
	// (see comment for mst_StatusT in iqi_mst.h)
	printf("read master status\n");
	mst_StatusT status;
	uchar08 pdIn[MAX_PD_BUF_LEN];
	int16 pdInSize;
	ASSIGN_RETURN(pdInSize, mst_GetStatus(iPortNo, (uchar08*)&status,
		pdIn, 32, cpErrMsg));

	FILE* fp;
	errno_t err;

	err = fopen_s(&fp, "c:\\temp\\IQ2 Development-V1.1.3.0EMULATE_MAXREFDES42-20190307-IOLFW1.0\\fw_application_binary.bin", "r");
	fseek(fp, 0, SEEK_END);
	long ulFileSize = ftell(fp);
	fseek(fp, 0, SEEK_SET);

	uchar08* file_buffer = malloc(ulFileSize);
	fread(file_buffer, 1, ulFileSize, fp);
	fclose(fp);

	result = mst_FirmwareUpdate(iPortNo,
			"BOOTLOAD_FW",
			"1234",
			file_buffer,
			ulFileSize,
			3,
			cpErrMsg);
	free(file_buffer);

	if (result < ERR_NONE)
	{
		return result;
	}
	return result;
}

#endif //TEST_FIRMWARE_UPDATE

void main(int argc, char **argv)
{
  char08 cpErrMsg[MAX_ERR_STR_LEN];
  uchar08 mst_comport_num = 0;
  uchar08 dev_comport_num = 0;
  const char08 errMessage[] = "Please input not zero comport number of iqInterface master and generic device as command line arguments";
  
#if 0 //use autodetection serial
  if (argc <= 1)
  {
    if (example_master_ethernet(cpErrMsg) == ERR_NONE)
      printf("Test ok");
    else
      printf("Failed: %s", cpErrMsg);
    return;
  }
  else if (argc < 3)
  {
    printf(errMessage);
    return;
  }

  mst_comport_num = atoi(argv[1]);
  dev_comport_num = atoi(argv[2]);
  if (mst_comport_num == 0 || dev_comport_num == 0)
  {
    printf(errMessage);
    return;
  }
#endif

  if (example_master_comport(mst_comport_num, cpErrMsg) == ERR_NONE &&
      example_generic_device(mst_comport_num, dev_comport_num, cpErrMsg) == ERR_NONE)
    printf("Test ok");
  else
    printf("Failed: %s", cpErrMsg);

#ifdef RUN_REBOOT

  if (example_master_reboot(mst_comport_num, cpErrMsg) == ERR_NONE)
  {
	  printf("Device rebooted");
  }
  else
  {
	  printf("Failed: %s", cpErrMsg);
  }
#endif

#ifdef RUN_PARAMETERS_RESET

  if (example_master_parameters_reset(mst_comport_num, cpErrMsg) == ERR_NONE)
  {
	  printf("Device rebooted");
  }
  else
  {
	  printf("Failed: %s", cpErrMsg);
  }
#endif

#ifdef RUN_READ_BLOB_DATA

	  if (example_read_write_blob_data(mst_comport_num, cpErrMsg) >= ERR_NONE)
	  {
		  printf("BLOB read/write data was successfull");
	  }
	  else
	  {
		  printf("Failed: %s", cpErrMsg);
	  }
#endif

#ifdef TEST_FIRMWARE_UPDATE
	  if (example_firmware_update(mst_comport_num, cpErrMsg) >= ERR_NONE)
	  {
		  printf("Firmware update succeded");
	  }
	  else
	  {
		  printf("Failed: %s", cpErrMsg);
	  }
#endif
	  printf("\n");
	  system("pause");
}
