drivers/can_uvccm_win32/can_uvccm_win32.cpp
author etisserant
Mon, 02 Jul 2007 18:22:58 +0200
changeset 236 905677ed00f3
parent 145 e747d2e26af0
child 255 7b9f36dbfe5f
permissions -rw-r--r--
Full preliminary implementation of TPDO transmit type:
- SYNC (N) (1-240)
- RTR only + SYNC (252)
- RTR only (253)
- EVENT, with timer and inhibit time (254 and 255)

User app have to call sendPDOevent(d) to eventually signal mapped data changes.
Callbacks added to 0x140N, TPDO comm parameters for on the fly timers values change.
TestMasterSlave updated.
// can_uvccm_win32 adapter (http://www.gridconnect.com)
// driver for CanFestival-3 Win32 port
// Copyright (C) 2007 Leonid Tochinski, ChattenAssociates, Inc.


#include <sstream>
#include <iomanip>
#if 0  // change to 1 if you use boost
#include <boost/algorithm/string/case_conv.hpp>
#else
#include <algorithm>
#endif

extern "C" {
#include "can_driver.h"
}
class can_uvccm_win32
   {
   public:
      class error
        {
        };
      can_uvccm_win32(s_BOARD *board);
      ~can_uvccm_win32();
      bool send(const Message *m);
      bool receive(Message *m);
   private:
      bool open_rs232(int port = 1, int baud_rate = 57600);
      bool close_rs232();
      bool get_can_data(const char* can_cmd_buf, long& bufsize, Message* m);
      bool set_can_data(const Message& m, std::string& can_cmd);
   private:
      HANDLE m_port;
      HANDLE m_read_event;
      HANDLE m_write_event;
      std::string m_residual_buffer;
   };

can_uvccm_win32::can_uvccm_win32(s_BOARD *board) : m_port(INVALID_HANDLE_VALUE),
      m_read_event(0),
      m_write_event(0)
   {
   if (strcmp( board->baudrate, "125K") || !open_rs232(1))
      throw error();
   }

can_uvccm_win32::~can_uvccm_win32()
   {
   close_rs232();
   }

bool can_uvccm_win32::send(const Message *m)
   {
   if (m_port == INVALID_HANDLE_VALUE)
      return false;

   // build can_uvccm_win32 command string
   std::string can_cmd;
   set_can_data(*m, can_cmd);

   OVERLAPPED overlapped;
   ::memset(&overlapped, 0, sizeof overlapped);
   overlapped.hEvent = m_write_event;
   ::ResetEvent(overlapped.hEvent);

   unsigned long bytes_written = 0;
   ::WriteFile(m_port, can_cmd.c_str(), (unsigned long)can_cmd.size(), &bytes_written, &overlapped);
   // wait for write operation completion
   enum { WRITE_TIMEOUT = 1000 };
   ::WaitForSingleObject(overlapped.hEvent, WRITE_TIMEOUT);
   // get number of bytes written
   ::GetOverlappedResult(m_port, &overlapped, &bytes_written, FALSE);

   bool result = (bytes_written == can_cmd.size());

   return result;
   }


bool can_uvccm_win32::receive(Message *m)
   {
   if (m_port == INVALID_HANDLE_VALUE)
      return false;

   long res_buffer_size = (long)m_residual_buffer.size();
   bool result = get_can_data(m_residual_buffer.c_str(), res_buffer_size, m);
   if (result)
      {
      m_residual_buffer.erase(0, res_buffer_size);
      return true;
      }

   enum { READ_TIMEOUT = 500 };

   OVERLAPPED overlapped;
   ::memset(&overlapped, 0, sizeof overlapped);
   overlapped.hEvent = m_read_event;
   ::ResetEvent(overlapped.hEvent);
   unsigned long event_mask = 0;

   if (FALSE == ::WaitCommEvent(m_port, &event_mask, &overlapped) && ERROR_IO_PENDING == ::GetLastError())
      {
      if (WAIT_TIMEOUT == ::WaitForSingleObject(overlapped.hEvent, READ_TIMEOUT))
         return false;
      }

   // get number of bytes in the input que
   COMSTAT stat;
   ::memset(&stat, 0, sizeof stat);
   unsigned long errors = 0;
   ::ClearCommError(m_port, &errors, &stat);
   if (stat.cbInQue == 0)
      return false;
   char buffer[3000];

   unsigned long bytes_to_read = min(stat.cbInQue, sizeof (buffer));

   unsigned long bytes_read = 0;
   ::ReadFile(m_port, buffer, bytes_to_read, &bytes_read, &overlapped);
   // wait for read operation completion
   ::WaitForSingleObject(overlapped.hEvent, READ_TIMEOUT);
   // get number of bytes read
   ::GetOverlappedResult(m_port, &overlapped, &bytes_read, FALSE);
   result = false;
   if (bytes_read > 0)
      {
      m_residual_buffer.append(buffer, bytes_read);
      res_buffer_size = (long)m_residual_buffer.size();
      result = get_can_data(m_residual_buffer.c_str(), res_buffer_size, m);
      if (result)
         m_residual_buffer.erase(0, res_buffer_size);
      }
   return result;
   }

bool can_uvccm_win32::open_rs232(int port, int baud_rate)
   {
   if (m_port != INVALID_HANDLE_VALUE)
      return true;

   std::ostringstream device_name;
   device_name << "COM" << port;

   m_port = ::CreateFile(device_name.str().c_str(),
                         GENERIC_READ | GENERIC_WRITE,
                         0,   // exclusive access
                         NULL,   // no security
                         OPEN_EXISTING,
                         FILE_FLAG_OVERLAPPED,   // overlapped I/O
                         NULL); // null template

   // Check the returned handle for INVALID_HANDLE_VALUE and then set the buffer sizes.
   if (m_port == INVALID_HANDLE_VALUE)
      return false;

   //  SetCommMask(m_hCom,EV_RXCHAR|EV_TXEMPTY|EV_CTS|EV_DSR|EV_RLSD|EV_BREAK|EV_ERR|EV_RING); //
   ::SetCommMask(m_port, EV_RXFLAG);

   COMMTIMEOUTS timeouts;
   ::memset(&timeouts, 0, sizeof (timeouts));
   timeouts.ReadIntervalTimeout = -1;
   timeouts.ReadTotalTimeoutConstant = 0;
   timeouts.ReadTotalTimeoutMultiplier = 0;
   timeouts.WriteTotalTimeoutConstant = 5000;
   timeouts.WriteTotalTimeoutMultiplier = 0;
   SetCommTimeouts(m_port, &timeouts); //

   ::SetupComm(m_port, 1024, 512); // set buffer sizes

   // Port settings are specified in a Data Communication Block (DCB). The easiest way to initialize a DCB is to call GetCommState to fill in its default values, override the values that you want to change and then call SetCommState to set the values.
   DCB dcb;
   ::memset(&dcb, 0, sizeof (dcb));
   ::GetCommState(m_port, &dcb);
   dcb.BaudRate = baud_rate;
   dcb.ByteSize = 8;
   dcb.Parity = NOPARITY;
   dcb.StopBits = ONESTOPBIT;
   dcb.fAbortOnError = TRUE;
   dcb.EvtChar = 0x0A; // '\n' character
   ::SetCommState(m_port, &dcb);

   ::PurgeComm(m_port, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);

   m_read_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
   m_write_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);

   return true;
   }

bool can_uvccm_win32::close_rs232()
   {
   if (m_port != INVALID_HANDLE_VALUE)
      {
      ::PurgeComm(m_port, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
      ::CloseHandle(m_port);
      m_port = INVALID_HANDLE_VALUE;
      ::CloseHandle(m_read_event);
      m_read_event = 0;
      ::CloseHandle(m_write_event);
      m_write_event = 0;
      m_residual_buffer.clear();
      }
   return true;
   }

bool can_uvccm_win32::get_can_data(const char* can_cmd_buf, long& bufsize, Message* m)
   {
   if (bufsize < 5)
      {
      bufsize = 0;
      return false;
      }

   Message msg;
   ::memset(&msg, 0 , sizeof (msg));
   char colon = 0, type = 0, request = 0;
   std::istringstream buf(std::string(can_cmd_buf, bufsize));
   buf >> colon >> type >> std::hex >> msg.cob_id.w >> request;
   if (colon != ':' || (type != 'S' && type != 'X'))
      {
      bufsize = 0;
      return false;
      }
   if (request == 'N')
      {
      msg.rtr = 0;
      for (msg.len = 0; msg.len < 8; ++msg.len)
         {
         std::string data_byte_str;
         buf >> std::setw(2) >> data_byte_str;
         if (data_byte_str[0] == ';')
            break;
         long byte_val = -1;
         std::istringstream(data_byte_str) >> std::hex >> byte_val;
         if (byte_val == -1)
            {
            bufsize = 0;
            return false;
            }
         msg.data[msg.len] = (UNS8)byte_val;
         }
      if (msg.len == 8)
         {
         char semicolon = 0;
         buf >> semicolon;
         if (semicolon != ';')
            {
            bufsize = 0;
            return false;
            }
         }

      }
   else if (request == 'R')
      {
      msg.rtr = 1;
      buf >> msg.len;
      }
   else
      {
      bufsize = 0;
      return false;
      }

   bufsize = buf.tellg();

   *m = msg;
   return true;
   }

bool can_uvccm_win32::set_can_data(const Message& m, std::string& can_cmd)
   {
   // build can_uvccm_win32 command string
   std::ostringstream can_cmd_str;
   can_cmd_str << ":S" << std::hex << m.cob_id.w;
   if (m.rtr == 1)
      {
      can_cmd_str << 'R' << (long)m.len;
      }
   else
      {
      can_cmd_str << 'N';
      for (int i = 0; i < m.len; ++i)
         can_cmd_str << std::hex << std::setfill('0') << std::setw(2) << (long)m.data[i];
      }
   can_cmd_str << ';';
   can_cmd = can_cmd_str.str();
#ifdef BOOST_VERSION
   boost::to_upper(can_cmd);
#else
   std::transform(can_cmd.begin(),can_cmd.end(),can_cmd.begin(),::toupper);
#endif
   return true;
   }


//------------------------------------------------------------------------
extern "C"
   UNS8 canReceive_driver(CAN_HANDLE fd0, Message *m)
   {
   return (UNS8)(!(reinterpret_cast<can_uvccm_win32*>(fd0)->receive(m)));
   }

extern "C"
   UNS8 canSend_driver(CAN_HANDLE fd0, Message *m)
   {
   return (UNS8)reinterpret_cast<can_uvccm_win32*>(fd0)->send(m);
   }

extern "C"
   CAN_HANDLE canOpen_driver(s_BOARD *board)
   {
   try
      {
      return (CAN_HANDLE) new can_uvccm_win32(board);
      }
   catch (can_uvccm_win32::error&)
      {
      return NULL;
      }
   }

extern "C"
   int canClose_driver(CAN_HANDLE inst)
   {
   delete reinterpret_cast<can_uvccm_win32*>(inst);
   return 1;
   }