/**********************************************************************/ 
/*                                                                    */ 
/*         File Name: ReadTagName.cpp                                 */ 
/*            Device: IBM PC compatible, Windows API32                */ 
/*         Author(s): Walter Borst (bow), Borst Automation            */ 
/*                                                                    */ 
/* Description                                                        */ 
/* Example to show how to implemented communication with a hart       */ 
/* device using a com port of the pc.                                 */ 
/*                                                                    */ 
/* Revision History                                                   */ 
/* ================                                                   */ 
/* date:      name:  version:  description:                           */ 
/* may-06     bow    00000     module created                         */ 
/*                                                                    */ 
/* Warranty Disclaimer                                                */ 
/* ===================                                                */ 
/* This software paket is supplied with NO WARRANTIES. Borst          */
/* Automation and Walter Borst expressly disclaims any warranty for   */
/* the software paket. This software paket and related documents are  */
/* provided "AS IS"; without warranty of any kind, expressed or       */
/* implied. This includes implied warranties of fitness for a         */
/* particular purpose. All risk arising out of use of this paket      */
/* remains with the user. By using this software paket, the user      */
/* agrees that no event shall Borst Automation or Walter Borst make   */
/* responsible or liable for damages whatsoever. This includes,       */
/* without limitation, damages for loss of business profits, loss due */
/* to business interruption, loss of business information, or any     */
/* other pecuniary loss, arising out of the use of or the inability   */
/* to use this software paket.                                        */
/**********************************************************************/
/**********************************************************************/ 
/* Include Section                                                    */ 
/**********************************************************************/ 
#define USECOMM
#include "stdafx.h"
#include <windows.h>
#include <io.h>
#include <memory.h>

/**********************************************************************/ 
/* Local Functions Prototypes                                         */ 
/**********************************************************************/ 
static bool openComPort(const char * pcComPort);
static bool connectToDevice(unsigned char ucAddr);
static bool readTagName(void);
static bool waitForResponse(unsigned char ucExpectedDataLen);
static void closeComPort(HANDLE hComPort);

/**********************************************************************/ 
/* Local Variables                                                    */ 
/**********************************************************************/ 
static unsigned char m_ucUniqueId[5] = { 0x00, 0x00, 0x00, 0x00, 0x00 };
static HANDLE          m_hComFile = INVALID_HANDLE_VALUE;
static unsigned char  m_ucTagName[9];
static unsigned char m_ucTxBuffer[255];
static unsigned char m_ucRxBuffer[255];
static unsigned char   m_ucRcvLen;
static const char     m_acComPort[] = { 'C','O','M','1',0x00 };

/**********************************************************************/ 
/* Local Constants Definitions                                        */ 
/**********************************************************************/ 
#define WAIT_PREAMBLE  0
#define WAIT_DELIMITER 1
#define READING_DATA   2

/**********************************************************************/ 
/* Global Functions                                                   */ 
/**********************************************************************/ 
   /****/
int main(int argc, char* argv[])
{  /****/
  printf("Opening "); printf(&m_acComPort[0]); printf("\n");
  if(openComPort(&m_acComPort[0]))
  {
    /* Connect to device with address 0 */
    if(connectToDevice(0))
    {
      printf("Connected to a device with Mfr Id: %d", m_ucRxBuffer[7]);
      printf(" and Dev Id: %d\n", m_ucRxBuffer[8]);
      printf("Reading tag name...\n");
      if(readTagName())
      {
        printf("The tag name of the device is ");
        printf((const char *)&m_ucTagName[0]);
        printf(".\n");
      }
      else
      {
        printf("Failed to read tag name of the device!\n");
      }
    }
    else
    {
      printf("Connection failed! No device response.\n");
    }
  }
  else
  {
  printf("Could not open com port!\n");
  }
  printf("Press return to exit!\n");
  getchar();
  if(m_hComFile != INVALID_HANDLE_VALUE)
  { 
    closeComPort(m_hComFile);
  }
  return 0;
}
/**********************************************************************/ 
/* Local Functions                                                    */ 
/**********************************************************************/ 
           /***********/
static bool openComPort(const char * pcComPort)
{          /***********/
  COMMTIMEOUTS   strCommTimeOuts;
  DCB                        dcb;

  m_hComFile =
  CreateFile(&m_acComPort[0],
             GENERIC_READ | GENERIC_WRITE,
             0,                            //exclusive access
             NULL,                         //no security attribs
             OPEN_EXISTING,
             FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
             NULL
            );
  if(m_hComFile == INVALID_HANDLE_VALUE)
  {
    return FALSE;
  }
  // get any early notifications
  SetCommMask(m_hComFile,EV_RXCHAR);
  // setup device buffers
  SetupComm(m_hComFile,256,256);
  // purge any information in the buffer
  PurgeComm(m_hComFile, 
            PURGE_TXABORT | PURGE_RXABORT |
            PURGE_TXCLEAR | PURGE_RXCLEAR 
           );
  // set up for overlapped I/O
  strCommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF;
  strCommTimeOuts.ReadTotalTimeoutMultiplier = 0;
  strCommTimeOuts.ReadTotalTimeoutConstant = 1000;
  strCommTimeOuts.WriteTotalTimeoutMultiplier = 0;
  strCommTimeOuts.WriteTotalTimeoutConstant = 1000;
  SetCommTimeouts(m_hComFile,&strCommTimeOuts);
  // Set the com port parameter
  GetCommState(m_hComFile,&dcb) ;
  dcb.BaudRate = CBR_1200;
  dcb.ByteSize = DATABITS_8;
  dcb.Parity = ODDPARITY;
  dcb.StopBits = ONESTOPBIT;
  dcb.fBinary = TRUE;
  dcb.fParity = TRUE;
  dcb.fErrorChar = FALSE;
  dcb.ErrorChar = (char) 0;
  dcb.fAbortOnError = FALSE;
  dcb.fDtrControl = DTR_CONTROL_DISABLE;
  dcb.fRtsControl = RTS_CONTROL_DISABLE;
  if (SetCommState(m_hComFile,&dcb)==FALSE)
  {
    CloseHandle(m_hComFile);  
    return FALSE;
  }
  // Switch off carrier
  EscapeCommFunction(m_hComFile,CLRRTS);
  EscapeCommFunction(m_hComFile,SETDTR);
  Sleep(10);
  return TRUE;
}
           /***************/
static bool connectToDevice(unsigned char ucAddr)
{          /***************/
  DWORD              dwWritten = 0;
  OVERLAPPED           osWrite;
  COMSTAT           strComStat;
  DWORD           dwErrorFlags = 0;

  // Initialize structures
  memset(&osWrite,0,sizeof(osWrite));
  memset(&strComStat,0,sizeof(strComStat));
  // Send: FF FF FF FF FF|02|80|000|000||82
  memcpy(&m_ucTxBuffer[0], "\xff\xff\xff\xff\xff\x02\x80\x00\x00",9);
  // Insert Address
  m_ucTxBuffer[6] |= ucAddr;
  // Build bcb
  m_ucTxBuffer[9] = m_ucTxBuffer[5] ^ m_ucTxBuffer[6] ^
                    m_ucTxBuffer[7] ^ m_ucTxBuffer[8];
  // Switch on carrier
  EscapeCommFunction(m_hComFile,SETRTS);
  EscapeCommFunction(m_hComFile,CLRDTR);
  // Wait until carrier is stable
  Sleep(2);
  // Send the data    
  ClearCommError(m_hComFile, &dwErrorFlags, &strComStat);
  WriteFile(m_hComFile, &m_ucTxBuffer[0], 10, &dwWritten, &osWrite); 
  // Wait until Tx is completed
  // 10 Bytes * 11 Bits * 1/1200 + 10 ms safety margin
  Sleep(102);
  // Switch off carrier
  ClearCommError(m_hComFile, &dwErrorFlags, &strComStat);
  EscapeCommFunction(m_hComFile,CLRRTS);
  EscapeCommFunction(m_hComFile,SETDTR);
  //Wait for the response of 19 bytes length
  if(waitForResponse(19))
  {
    // Store Unique Id
    m_ucUniqueId[0] = m_ucRxBuffer[7];
    m_ucUniqueId[1] = m_ucRxBuffer[8];
    m_ucUniqueId[2] = m_ucRxBuffer[15];
    m_ucUniqueId[3] = m_ucRxBuffer[16];
    m_ucUniqueId[4] = m_ucRxBuffer[17];
    return TRUE;
  }
  return FALSE;
}
           /***********/
static bool readTagName(void)
{          /***********/
  DWORD              dwWritten = 0;
  OVERLAPPED           osWrite;
  COMSTAT           strComStat;
  DWORD           dwErrorFlags = 0;
  unsigned char              e;
  bool                 bRepeat;

  // Initialize structures
  memset(&osWrite,0,sizeof(osWrite));
  memset(&strComStat,0,sizeof(strComStat));
  // Send: FF FF FF FF FF|82|80|00|00|00|00|13|00|BCB
  memcpy(&m_ucTxBuffer[0], "\xff\xff\xff\xff\xff\x82\x80\x00\x00\x00\x00\x0d\x00",13);
  // Insert Unique Id
  m_ucTxBuffer[6] |= m_ucUniqueId[0] & 0x3f;
  m_ucTxBuffer[7] = m_ucUniqueId[1];
  m_ucTxBuffer[8] = m_ucUniqueId[2];
  m_ucTxBuffer[9] = m_ucUniqueId[3];
  m_ucTxBuffer[10] = m_ucUniqueId[4];
  // Build bcb
  m_ucTxBuffer[13] = m_ucTxBuffer[5] ^ m_ucTxBuffer[6] ^
                     m_ucTxBuffer[7] ^ m_ucTxBuffer[8] ^
                     m_ucTxBuffer[9] ^ m_ucTxBuffer[10] ^
                     m_ucTxBuffer[11] ^ m_ucTxBuffer[12];
  do
  { bRepeat = FALSE;
    // Switch on carrier
    EscapeCommFunction(m_hComFile,SETRTS);
    EscapeCommFunction(m_hComFile,CLRDTR);
    // Wait until carrier is stable
    Sleep(2);
    // Send the data    
    ClearCommError(m_hComFile, &dwErrorFlags, &strComStat);
    WriteFile(m_hComFile, &m_ucTxBuffer[0], 14, &dwWritten, &osWrite); 
    // Wait until Tx is completed
    // 14 Bytes * 11 Bits * 1/1200 + 10 ms safety margin
    Sleep(138);
    // Switch off carrier
    ClearCommError(m_hComFile, &dwErrorFlags, &strComStat);
    EscapeCommFunction(m_hComFile,CLRRTS);
    EscapeCommFunction(m_hComFile,SETDTR);
    //Wait for the response of 32 bytes length
    if(waitForResponse(32))
    { unsigned char ucIdx = 0;
      unsigned char    uc;

      // Decode Tag Name
      for(e = 10; e < 16; e += 3)
      {
        uc = ((m_ucRxBuffer[e    ] >> 2) & 0x3F);
        if (uc < 0x20)
          uc += 0x40;
        m_ucTagName[ucIdx++] = uc;
        uc = ((m_ucRxBuffer[e    ] << 4) & 0x3F) | 
             ((m_ucRxBuffer[e + 1] >> 4) & 0x3F);
        if (uc < 0x20)
          uc += 0x40;
        m_ucTagName[ucIdx++] = uc;
        uc = ((m_ucRxBuffer[e + 1] << 2) & 0x3F) | 
             ((m_ucRxBuffer[e + 2] >> 6) & 0x3F);
        if (uc < 0x20)
          uc += 0x40;
        m_ucTagName[ucIdx++] = uc;
        uc = m_ucRxBuffer[e + 2] & 0x3F;
        if (uc < 0x20)
          uc += 0x40;
        m_ucTagName[ucIdx++] = uc;
      }
      return TRUE;
    }
    if((m_ucRcvLen == 0x0b) && (m_ucRxBuffer[8] == 32))
    { // Repeat service if device is busy
      bRepeat = TRUE;
      printf("Device Busy detected!\n");
    }
    else
    {
      return FALSE;
    }
  }
  while(bRepeat);
  return FALSE;
}
           /***************/
static bool waitForResponse(unsigned char ucExpectedDataLen)
{          /***************/
  DWORD               dwLength;
  OVERLAPPED            osRead;
  COMSTAT           strComStat;
  DWORD           dwErrorFlags = 0;
  unsigned char    ucRcvStatus;
  unsigned char       ucBuffer[255];
  unsigned char      ucTimeOut;
  unsigned char              e;

  // Initialize structures
  memset(&osRead,0,sizeof(osRead));
  memset(&strComStat,0,sizeof(strComStat));
  // Note 9.16 ms per expected octet 
  ucTimeOut = (unsigned char)((ucExpectedDataLen * 9.16 + 300.0f) / 10.0f);
  m_ucRcvLen = 0;
  ucRcvStatus = WAIT_PREAMBLE;
  // We are waiting for the time out time or until
  // we have got the number of expected characters
  while((m_ucRcvLen < ucExpectedDataLen) && (ucTimeOut))
  {
    ClearCommError(m_hComFile, &dwErrorFlags, &strComStat);
    dwLength = min(255, strComStat.cbInQue);
    ReadFile(m_hComFile, &ucBuffer[0], dwLength, &dwLength, &osRead);
    if(dwLength)
    {
      // Data was received
      for(e = 0; e < dwLength; e++)
      {
        switch(ucRcvStatus)
        {
          case WAIT_PREAMBLE:
            if(ucBuffer[e] == 0xff)
            { // Preamble byte
              ucRcvStatus = WAIT_DELIMITER;
            }
            break;
          case WAIT_DELIMITER:
            if(ucBuffer[e] == 0xff)
            { // Preamble byte do nothing
            }
            else
            {
              if((ucBuffer[e] &0x3f) == 0x06)
              { //Delimiter begin recording data
                m_ucRxBuffer[0] = ucBuffer[e];
                m_ucRcvLen++;
                ucRcvStatus = READING_DATA;
              }
              else
              { // Garbidge wait for next preamble
                ucRcvStatus = WAIT_PREAMBLE;
              }
            }
            break;
          case READING_DATA:
            m_ucRxBuffer[m_ucRcvLen++] = ucBuffer[e];
            break;
        }
      }
    } // if dwLength
    ucTimeOut--;
    Sleep(10); 
  } //while 
  if(m_ucRcvLen < ucExpectedDataLen)
  {
    return FALSE;
  }
  return TRUE;
}
           /************/
static void closeComPort(HANDLE hComPort)
{          /************/
  if(hComPort != INVALID_HANDLE_VALUE)
  { 
    CloseHandle(hComPort);
  }
}
