drivers/can_uvccm_win32/can_uvccm_win32.cpp
author fbeaulier
Tue, 16 Aug 2011 14:15:52 +0200
changeset 663 70fc3603e36f
parent 631 08b6b903f84a
child 706 7c35870a66ea
permissions -rw-r--r--
timers_unix.c : remove sigint and sigterm catch
sdo : Allow multiple servers
The sdo transfer struct is not anymore referenced by server's node id but by
client or server number in the OD. Node id is not relevant in SDO transfert.
/*
This file is part of CanFestival, a library implementing CanOpen Stack. 

CanFestival Copyright (C): Edouard TISSERANT and Francis DUPIN
CanFestival Win32 port Copyright (C) 2007 Leonid Tochinski, ChattenAssociates, Inc.

See COPYING file for copyrights details.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

// can_uvccm_win32 adapter (http://www.gridconnect.com)
// driver for CanFestival-3 Win32 port

#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 true;

   // 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 false;
   }


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 >> 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;
   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 const *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;
   }

extern "C"
	UNS8 canChangeBaudRate_driver( CAN_HANDLE fd, char* baud)
	{
	return 0;
	}