drivers/can_uvccm_win32/can_uvccm_win32.cpp
changeset 145 e747d2e26af0
child 255 7b9f36dbfe5f
equal deleted inserted replaced
144:3ebf16150b2e 145:e747d2e26af0
       
     1 // can_uvccm_win32 adapter (http://www.gridconnect.com)
       
     2 // driver for CanFestival-3 Win32 port
       
     3 // Copyright (C) 2007 Leonid Tochinski, ChattenAssociates, Inc.
       
     4 
       
     5 
       
     6 #include <sstream>
       
     7 #include <iomanip>
       
     8 #if 0  // change to 1 if you use boost
       
     9 #include <boost/algorithm/string/case_conv.hpp>
       
    10 #else
       
    11 #include <algorithm>
       
    12 #endif
       
    13 
       
    14 extern "C" {
       
    15 #include "can_driver.h"
       
    16 }
       
    17 class can_uvccm_win32
       
    18    {
       
    19    public:
       
    20       class error
       
    21         {
       
    22         };
       
    23       can_uvccm_win32(s_BOARD *board);
       
    24       ~can_uvccm_win32();
       
    25       bool send(const Message *m);
       
    26       bool receive(Message *m);
       
    27    private:
       
    28       bool open_rs232(int port = 1, int baud_rate = 57600);
       
    29       bool close_rs232();
       
    30       bool get_can_data(const char* can_cmd_buf, long& bufsize, Message* m);
       
    31       bool set_can_data(const Message& m, std::string& can_cmd);
       
    32    private:
       
    33       HANDLE m_port;
       
    34       HANDLE m_read_event;
       
    35       HANDLE m_write_event;
       
    36       std::string m_residual_buffer;
       
    37    };
       
    38 
       
    39 can_uvccm_win32::can_uvccm_win32(s_BOARD *board) : m_port(INVALID_HANDLE_VALUE),
       
    40       m_read_event(0),
       
    41       m_write_event(0)
       
    42    {
       
    43    if (strcmp( board->baudrate, "125K") || !open_rs232(1))
       
    44       throw error();
       
    45    }
       
    46 
       
    47 can_uvccm_win32::~can_uvccm_win32()
       
    48    {
       
    49    close_rs232();
       
    50    }
       
    51 
       
    52 bool can_uvccm_win32::send(const Message *m)
       
    53    {
       
    54    if (m_port == INVALID_HANDLE_VALUE)
       
    55       return false;
       
    56 
       
    57    // build can_uvccm_win32 command string
       
    58    std::string can_cmd;
       
    59    set_can_data(*m, can_cmd);
       
    60 
       
    61    OVERLAPPED overlapped;
       
    62    ::memset(&overlapped, 0, sizeof overlapped);
       
    63    overlapped.hEvent = m_write_event;
       
    64    ::ResetEvent(overlapped.hEvent);
       
    65 
       
    66    unsigned long bytes_written = 0;
       
    67    ::WriteFile(m_port, can_cmd.c_str(), (unsigned long)can_cmd.size(), &bytes_written, &overlapped);
       
    68    // wait for write operation completion
       
    69    enum { WRITE_TIMEOUT = 1000 };
       
    70    ::WaitForSingleObject(overlapped.hEvent, WRITE_TIMEOUT);
       
    71    // get number of bytes written
       
    72    ::GetOverlappedResult(m_port, &overlapped, &bytes_written, FALSE);
       
    73 
       
    74    bool result = (bytes_written == can_cmd.size());
       
    75 
       
    76    return result;
       
    77    }
       
    78 
       
    79 
       
    80 bool can_uvccm_win32::receive(Message *m)
       
    81    {
       
    82    if (m_port == INVALID_HANDLE_VALUE)
       
    83       return false;
       
    84 
       
    85    long res_buffer_size = (long)m_residual_buffer.size();
       
    86    bool result = get_can_data(m_residual_buffer.c_str(), res_buffer_size, m);
       
    87    if (result)
       
    88       {
       
    89       m_residual_buffer.erase(0, res_buffer_size);
       
    90       return true;
       
    91       }
       
    92 
       
    93    enum { READ_TIMEOUT = 500 };
       
    94 
       
    95    OVERLAPPED overlapped;
       
    96    ::memset(&overlapped, 0, sizeof overlapped);
       
    97    overlapped.hEvent = m_read_event;
       
    98    ::ResetEvent(overlapped.hEvent);
       
    99    unsigned long event_mask = 0;
       
   100 
       
   101    if (FALSE == ::WaitCommEvent(m_port, &event_mask, &overlapped) && ERROR_IO_PENDING == ::GetLastError())
       
   102       {
       
   103       if (WAIT_TIMEOUT == ::WaitForSingleObject(overlapped.hEvent, READ_TIMEOUT))
       
   104          return false;
       
   105       }
       
   106 
       
   107    // get number of bytes in the input que
       
   108    COMSTAT stat;
       
   109    ::memset(&stat, 0, sizeof stat);
       
   110    unsigned long errors = 0;
       
   111    ::ClearCommError(m_port, &errors, &stat);
       
   112    if (stat.cbInQue == 0)
       
   113       return false;
       
   114    char buffer[3000];
       
   115 
       
   116    unsigned long bytes_to_read = min(stat.cbInQue, sizeof (buffer));
       
   117 
       
   118    unsigned long bytes_read = 0;
       
   119    ::ReadFile(m_port, buffer, bytes_to_read, &bytes_read, &overlapped);
       
   120    // wait for read operation completion
       
   121    ::WaitForSingleObject(overlapped.hEvent, READ_TIMEOUT);
       
   122    // get number of bytes read
       
   123    ::GetOverlappedResult(m_port, &overlapped, &bytes_read, FALSE);
       
   124    result = false;
       
   125    if (bytes_read > 0)
       
   126       {
       
   127       m_residual_buffer.append(buffer, bytes_read);
       
   128       res_buffer_size = (long)m_residual_buffer.size();
       
   129       result = get_can_data(m_residual_buffer.c_str(), res_buffer_size, m);
       
   130       if (result)
       
   131          m_residual_buffer.erase(0, res_buffer_size);
       
   132       }
       
   133    return result;
       
   134    }
       
   135 
       
   136 bool can_uvccm_win32::open_rs232(int port, int baud_rate)
       
   137    {
       
   138    if (m_port != INVALID_HANDLE_VALUE)
       
   139       return true;
       
   140 
       
   141    std::ostringstream device_name;
       
   142    device_name << "COM" << port;
       
   143 
       
   144    m_port = ::CreateFile(device_name.str().c_str(),
       
   145                          GENERIC_READ | GENERIC_WRITE,
       
   146                          0,   // exclusive access
       
   147                          NULL,   // no security
       
   148                          OPEN_EXISTING,
       
   149                          FILE_FLAG_OVERLAPPED,   // overlapped I/O
       
   150                          NULL); // null template
       
   151 
       
   152    // Check the returned handle for INVALID_HANDLE_VALUE and then set the buffer sizes.
       
   153    if (m_port == INVALID_HANDLE_VALUE)
       
   154       return false;
       
   155 
       
   156    //  SetCommMask(m_hCom,EV_RXCHAR|EV_TXEMPTY|EV_CTS|EV_DSR|EV_RLSD|EV_BREAK|EV_ERR|EV_RING); //
       
   157    ::SetCommMask(m_port, EV_RXFLAG);
       
   158 
       
   159    COMMTIMEOUTS timeouts;
       
   160    ::memset(&timeouts, 0, sizeof (timeouts));
       
   161    timeouts.ReadIntervalTimeout = -1;
       
   162    timeouts.ReadTotalTimeoutConstant = 0;
       
   163    timeouts.ReadTotalTimeoutMultiplier = 0;
       
   164    timeouts.WriteTotalTimeoutConstant = 5000;
       
   165    timeouts.WriteTotalTimeoutMultiplier = 0;
       
   166    SetCommTimeouts(m_port, &timeouts); //
       
   167 
       
   168    ::SetupComm(m_port, 1024, 512); // set buffer sizes
       
   169 
       
   170    // 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.
       
   171    DCB dcb;
       
   172    ::memset(&dcb, 0, sizeof (dcb));
       
   173    ::GetCommState(m_port, &dcb);
       
   174    dcb.BaudRate = baud_rate;
       
   175    dcb.ByteSize = 8;
       
   176    dcb.Parity = NOPARITY;
       
   177    dcb.StopBits = ONESTOPBIT;
       
   178    dcb.fAbortOnError = TRUE;
       
   179    dcb.EvtChar = 0x0A; // '\n' character
       
   180    ::SetCommState(m_port, &dcb);
       
   181 
       
   182    ::PurgeComm(m_port, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
       
   183 
       
   184    m_read_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
       
   185    m_write_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
       
   186 
       
   187    return true;
       
   188    }
       
   189 
       
   190 bool can_uvccm_win32::close_rs232()
       
   191    {
       
   192    if (m_port != INVALID_HANDLE_VALUE)
       
   193       {
       
   194       ::PurgeComm(m_port, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
       
   195       ::CloseHandle(m_port);
       
   196       m_port = INVALID_HANDLE_VALUE;
       
   197       ::CloseHandle(m_read_event);
       
   198       m_read_event = 0;
       
   199       ::CloseHandle(m_write_event);
       
   200       m_write_event = 0;
       
   201       m_residual_buffer.clear();
       
   202       }
       
   203    return true;
       
   204    }
       
   205 
       
   206 bool can_uvccm_win32::get_can_data(const char* can_cmd_buf, long& bufsize, Message* m)
       
   207    {
       
   208    if (bufsize < 5)
       
   209       {
       
   210       bufsize = 0;
       
   211       return false;
       
   212       }
       
   213 
       
   214    Message msg;
       
   215    ::memset(&msg, 0 , sizeof (msg));
       
   216    char colon = 0, type = 0, request = 0;
       
   217    std::istringstream buf(std::string(can_cmd_buf, bufsize));
       
   218    buf >> colon >> type >> std::hex >> msg.cob_id.w >> request;
       
   219    if (colon != ':' || (type != 'S' && type != 'X'))
       
   220       {
       
   221       bufsize = 0;
       
   222       return false;
       
   223       }
       
   224    if (request == 'N')
       
   225       {
       
   226       msg.rtr = 0;
       
   227       for (msg.len = 0; msg.len < 8; ++msg.len)
       
   228          {
       
   229          std::string data_byte_str;
       
   230          buf >> std::setw(2) >> data_byte_str;
       
   231          if (data_byte_str[0] == ';')
       
   232             break;
       
   233          long byte_val = -1;
       
   234          std::istringstream(data_byte_str) >> std::hex >> byte_val;
       
   235          if (byte_val == -1)
       
   236             {
       
   237             bufsize = 0;
       
   238             return false;
       
   239             }
       
   240          msg.data[msg.len] = (UNS8)byte_val;
       
   241          }
       
   242       if (msg.len == 8)
       
   243          {
       
   244          char semicolon = 0;
       
   245          buf >> semicolon;
       
   246          if (semicolon != ';')
       
   247             {
       
   248             bufsize = 0;
       
   249             return false;
       
   250             }
       
   251          }
       
   252 
       
   253       }
       
   254    else if (request == 'R')
       
   255       {
       
   256       msg.rtr = 1;
       
   257       buf >> msg.len;
       
   258       }
       
   259    else
       
   260       {
       
   261       bufsize = 0;
       
   262       return false;
       
   263       }
       
   264 
       
   265    bufsize = buf.tellg();
       
   266 
       
   267    *m = msg;
       
   268    return true;
       
   269    }
       
   270 
       
   271 bool can_uvccm_win32::set_can_data(const Message& m, std::string& can_cmd)
       
   272    {
       
   273    // build can_uvccm_win32 command string
       
   274    std::ostringstream can_cmd_str;
       
   275    can_cmd_str << ":S" << std::hex << m.cob_id.w;
       
   276    if (m.rtr == 1)
       
   277       {
       
   278       can_cmd_str << 'R' << (long)m.len;
       
   279       }
       
   280    else
       
   281       {
       
   282       can_cmd_str << 'N';
       
   283       for (int i = 0; i < m.len; ++i)
       
   284          can_cmd_str << std::hex << std::setfill('0') << std::setw(2) << (long)m.data[i];
       
   285       }
       
   286    can_cmd_str << ';';
       
   287    can_cmd = can_cmd_str.str();
       
   288 #ifdef BOOST_VERSION
       
   289    boost::to_upper(can_cmd);
       
   290 #else
       
   291    std::transform(can_cmd.begin(),can_cmd.end(),can_cmd.begin(),::toupper);
       
   292 #endif
       
   293    return true;
       
   294    }
       
   295 
       
   296 
       
   297 //------------------------------------------------------------------------
       
   298 extern "C"
       
   299    UNS8 canReceive_driver(CAN_HANDLE fd0, Message *m)
       
   300    {
       
   301    return (UNS8)(!(reinterpret_cast<can_uvccm_win32*>(fd0)->receive(m)));
       
   302    }
       
   303 
       
   304 extern "C"
       
   305    UNS8 canSend_driver(CAN_HANDLE fd0, Message *m)
       
   306    {
       
   307    return (UNS8)reinterpret_cast<can_uvccm_win32*>(fd0)->send(m);
       
   308    }
       
   309 
       
   310 extern "C"
       
   311    CAN_HANDLE canOpen_driver(s_BOARD *board)
       
   312    {
       
   313    try
       
   314       {
       
   315       return (CAN_HANDLE) new can_uvccm_win32(board);
       
   316       }
       
   317    catch (can_uvccm_win32::error&)
       
   318       {
       
   319       return NULL;
       
   320       }
       
   321    }
       
   322 
       
   323 extern "C"
       
   324    int canClose_driver(CAN_HANDLE inst)
       
   325    {
       
   326    delete reinterpret_cast<can_uvccm_win32*>(inst);
       
   327    return 1;
       
   328    }
       
   329 
       
   330 
       
   331