nico@215: nico@215: nico@215: CanFestival: drivers/can_uvccm_win32/can_uvccm_win32.cpp Source File nico@215: nico@215: nico@215: nico@215: nico@215:
nico@215:
nico@215:
nico@215:
nico@215: nico@215:

can_uvccm_win32.cpp

Go to the documentation of this file.
00001 // can_uvccm_win32 adapter (http://www.gridconnect.com)
nico@215: 00002 // driver for CanFestival-3 Win32 port
nico@215: 00003 // Copyright (C) 2007 Leonid Tochinski, ChattenAssociates, Inc.
nico@215: 00004 
nico@215: 00005 
nico@215: 00006 #include <sstream>
nico@215: 00007 #include <iomanip>
nico@215: 00008 #if 0  // change to 1 if you use boost
nico@215: 00009 #include <boost/algorithm/string/case_conv.hpp>
nico@215: 00010 #else
nico@215: 00011 #include <algorithm>
nico@215: 00012 #endif
nico@215: 00013 
nico@215: 00014 extern "C" {
nico@215: 00015 #include "can_driver.h"
nico@215: 00016 }
nico@215: 00017 class can_uvccm_win32
nico@215: 00018    {
nico@215: 00019    public:
nico@215: 00020       class error
nico@215: 00021         {
nico@215: 00022         };
etisserant@240: 00023       can_uvccm_win32(s_BOARD *board);
etisserant@240: 00024       ~can_uvccm_win32();
etisserant@240: 00025       bool send(const Message *m);
etisserant@240: 00026       bool receive(Message *m);
nico@215: 00027    private:
etisserant@240: 00028       bool open_rs232(int port = 1, int baud_rate = 57600);
etisserant@240: 00029       bool close_rs232();
etisserant@240: 00030       bool get_can_data(const char* can_cmd_buf, long& bufsize, Message* m);
etisserant@240: 00031       bool set_can_data(const Message& m, std::string& can_cmd);
nico@215: 00032    private:
etisserant@240: 00033       HANDLE m_port;
etisserant@240: 00034       HANDLE m_read_event;
etisserant@240: 00035       HANDLE m_write_event;
etisserant@240: 00036       std::string m_residual_buffer;
nico@215: 00037    };
nico@215: 00038 
etisserant@240: 00039 can_uvccm_win32::can_uvccm_win32(s_BOARD *board) : m_port(INVALID_HANDLE_VALUE),
nico@215: 00040       m_read_event(0),
nico@215: 00041       m_write_event(0)
nico@215: 00042    {
etisserant@240: 00043    if (strcmp( board->baudrate, "125K") || !open_rs232(1))
nico@215: 00044       throw error();
nico@215: 00045    }
nico@215: 00046 
etisserant@240: 00047 can_uvccm_win32::~can_uvccm_win32()
nico@215: 00048    {
etisserant@240: 00049    close_rs232();
nico@215: 00050    }
nico@215: 00051 
etisserant@240: 00052 bool can_uvccm_win32::send(const Message *m)
nico@215: 00053    {
etisserant@240: 00054    if (m_port == INVALID_HANDLE_VALUE)
nico@215: 00055       return false;
nico@215: 00056 
nico@215: 00057    // build can_uvccm_win32 command string
nico@215: 00058    std::string can_cmd;
etisserant@240: 00059    set_can_data(*m, can_cmd);
nico@215: 00060 
nico@215: 00061    OVERLAPPED overlapped;
nico@215: 00062    ::memset(&overlapped, 0, sizeof overlapped);
etisserant@240: 00063    overlapped.hEvent = m_write_event;
nico@215: 00064    ::ResetEvent(overlapped.hEvent);
nico@215: 00065 
nico@215: 00066    unsigned long bytes_written = 0;
etisserant@240: 00067    ::WriteFile(m_port, can_cmd.c_str(), (unsigned long)can_cmd.size(), &bytes_written, &overlapped);
nico@215: 00068    // wait for write operation completion
nico@215: 00069    enum { WRITE_TIMEOUT = 1000 };
nico@215: 00070    ::WaitForSingleObject(overlapped.hEvent, WRITE_TIMEOUT);
nico@215: 00071    // get number of bytes written
etisserant@240: 00072    ::GetOverlappedResult(m_port, &overlapped, &bytes_written, FALSE);
nico@215: 00073 
nico@215: 00074    bool result = (bytes_written == can_cmd.size());
nico@215: 00075 
nico@215: 00076    return result;
nico@215: 00077    }
nico@215: 00078 
nico@215: 00079 
etisserant@240: 00080 bool can_uvccm_win32::receive(Message *m)
nico@215: 00081    {
etisserant@240: 00082    if (m_port == INVALID_HANDLE_VALUE)
nico@215: 00083       return false;
nico@215: 00084 
etisserant@240: 00085    long res_buffer_size = (long)m_residual_buffer.size();
etisserant@240: 00086    bool result = get_can_data(m_residual_buffer.c_str(), res_buffer_size, m);
nico@215: 00087    if (result)
nico@215: 00088       {
etisserant@240: 00089       m_residual_buffer.erase(0, res_buffer_size);
nico@215: 00090       return true;
nico@215: 00091       }
nico@215: 00092 
nico@215: 00093    enum { READ_TIMEOUT = 500 };
nico@215: 00094 
nico@215: 00095    OVERLAPPED overlapped;
nico@215: 00096    ::memset(&overlapped, 0, sizeof overlapped);
etisserant@240: 00097    overlapped.hEvent = m_read_event;
nico@215: 00098    ::ResetEvent(overlapped.hEvent);
nico@215: 00099    unsigned long event_mask = 0;
nico@215: 00100 
etisserant@240: 00101    if (FALSE == ::WaitCommEvent(m_port, &event_mask, &overlapped) && ERROR_IO_PENDING == ::GetLastError())
nico@215: 00102       {
nico@215: 00103       if (WAIT_TIMEOUT == ::WaitForSingleObject(overlapped.hEvent, READ_TIMEOUT))
nico@215: 00104          return false;
nico@215: 00105       }
nico@215: 00106 
nico@215: 00107    // get number of bytes in the input que
nico@215: 00108    COMSTAT stat;
nico@215: 00109    ::memset(&stat, 0, sizeof stat);
nico@215: 00110    unsigned long errors = 0;
etisserant@240: 00111    ::ClearCommError(m_port, &errors, &stat);
nico@215: 00112    if (stat.cbInQue == 0)
nico@215: 00113       return false;
nico@215: 00114    char buffer[3000];
nico@215: 00115 
nico@215: 00116    unsigned long bytes_to_read = min(stat.cbInQue, sizeof (buffer));
nico@215: 00117 
nico@215: 00118    unsigned long bytes_read = 0;
etisserant@240: 00119    ::ReadFile(m_port, buffer, bytes_to_read, &bytes_read, &overlapped);
nico@215: 00120    // wait for read operation completion
nico@215: 00121    ::WaitForSingleObject(overlapped.hEvent, READ_TIMEOUT);
nico@215: 00122    // get number of bytes read
etisserant@240: 00123    ::GetOverlappedResult(m_port, &overlapped, &bytes_read, FALSE);
nico@215: 00124    result = false;
nico@215: 00125    if (bytes_read > 0)
nico@215: 00126       {
etisserant@240: 00127       m_residual_buffer.append(buffer, bytes_read);
etisserant@240: 00128       res_buffer_size = (long)m_residual_buffer.size();
etisserant@240: 00129       result = get_can_data(m_residual_buffer.c_str(), res_buffer_size, m);
nico@215: 00130       if (result)
etisserant@240: 00131          m_residual_buffer.erase(0, res_buffer_size);
nico@215: 00132       }
nico@215: 00133    return result;
nico@215: 00134    }
nico@215: 00135 
etisserant@240: 00136 bool can_uvccm_win32::open_rs232(int port, int baud_rate)
nico@215: 00137    {
etisserant@240: 00138    if (m_port != INVALID_HANDLE_VALUE)
nico@215: 00139       return true;
nico@215: 00140 
nico@215: 00141    std::ostringstream device_name;
nico@215: 00142    device_name << "COM" << port;
nico@215: 00143 
etisserant@240: 00144    m_port = ::CreateFile(device_name.str().c_str(),
nico@215: 00145                          GENERIC_READ | GENERIC_WRITE,
nico@215: 00146                          0,   // exclusive access
nico@215: 00147                          NULL,   // no security
nico@215: 00148                          OPEN_EXISTING,
nico@215: 00149                          FILE_FLAG_OVERLAPPED,   // overlapped I/O
nico@215: 00150                          NULL); // null template
nico@215: 00151 
nico@215: 00152    // Check the returned handle for INVALID_HANDLE_VALUE and then set the buffer sizes.
etisserant@240: 00153    if (m_port == INVALID_HANDLE_VALUE)
nico@215: 00154       return false;
nico@215: 00155 
nico@215: 00156    //  SetCommMask(m_hCom,EV_RXCHAR|EV_TXEMPTY|EV_CTS|EV_DSR|EV_RLSD|EV_BREAK|EV_ERR|EV_RING); //
etisserant@240: 00157    ::SetCommMask(m_port, EV_RXFLAG);
nico@215: 00158 
nico@215: 00159    COMMTIMEOUTS timeouts;
nico@215: 00160    ::memset(&timeouts, 0, sizeof (timeouts));
nico@215: 00161    timeouts.ReadIntervalTimeout = -1;
nico@215: 00162    timeouts.ReadTotalTimeoutConstant = 0;
nico@215: 00163    timeouts.ReadTotalTimeoutMultiplier = 0;
nico@215: 00164    timeouts.WriteTotalTimeoutConstant = 5000;
nico@215: 00165    timeouts.WriteTotalTimeoutMultiplier = 0;
etisserant@240: 00166    SetCommTimeouts(m_port, &timeouts); //
nico@215: 00167 
etisserant@240: 00168    ::SetupComm(m_port, 1024, 512); // set buffer sizes
nico@215: 00169 
nico@215: 00170    // 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.
nico@215: 00171    DCB dcb;
nico@215: 00172    ::memset(&dcb, 0, sizeof (dcb));
etisserant@240: 00173    ::GetCommState(m_port, &dcb);
nico@215: 00174    dcb.BaudRate = baud_rate;
nico@215: 00175    dcb.ByteSize = 8;
nico@215: 00176    dcb.Parity = NOPARITY;
nico@215: 00177    dcb.StopBits = ONESTOPBIT;
etisserant@240: 00178    dcb.fAbortOnError = TRUE;
nico@215: 00179    dcb.EvtChar = 0x0A; // '\n' character
etisserant@240: 00180    ::SetCommState(m_port, &dcb);
nico@215: 00181 
etisserant@240: 00182    ::PurgeComm(m_port, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
nico@215: 00183 
etisserant@240: 00184    m_read_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
etisserant@240: 00185    m_write_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
nico@215: 00186 
nico@215: 00187    return true;
nico@215: 00188    }
nico@215: 00189 
etisserant@240: 00190 bool can_uvccm_win32::close_rs232()
nico@215: 00191    {
etisserant@240: 00192    if (m_port != INVALID_HANDLE_VALUE)
nico@215: 00193       {
etisserant@240: 00194       ::PurgeComm(m_port, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
etisserant@240: 00195       ::CloseHandle(m_port);
etisserant@240: 00196       m_port = INVALID_HANDLE_VALUE;
etisserant@240: 00197       ::CloseHandle(m_read_event);
etisserant@240: 00198       m_read_event = 0;
etisserant@240: 00199       ::CloseHandle(m_write_event);
etisserant@240: 00200       m_write_event = 0;
etisserant@240: 00201       m_residual_buffer.clear();
nico@215: 00202       }
nico@215: 00203    return true;
nico@215: 00204    }
nico@215: 00205 
etisserant@240: 00206 bool can_uvccm_win32::get_can_data(const char* can_cmd_buf, long& bufsize, Message* m)
nico@215: 00207    {
nico@215: 00208    if (bufsize < 5)
nico@215: 00209       {
nico@215: 00210       bufsize = 0;
nico@215: 00211       return false;
nico@215: 00212       }
nico@215: 00213 
nico@215: 00214    Message msg;
nico@215: 00215    ::memset(&msg, 0 , sizeof (msg));
nico@215: 00216    char colon = 0, type = 0, request = 0;
nico@215: 00217    std::istringstream buf(std::string(can_cmd_buf, bufsize));
etisserant@240: 00218    buf >> colon >> type >> std::hex >> msg.cob_id.w >> request;
nico@215: 00219    if (colon != ':' || (type != 'S' && type != 'X'))
nico@215: 00220       {
nico@215: 00221       bufsize = 0;
nico@215: 00222       return false;
nico@215: 00223       }
nico@215: 00224    if (request == 'N')
nico@215: 00225       {
etisserant@240: 00226       msg.rtr = 0;
etisserant@240: 00227       for (msg.len = 0; msg.len < 8; ++msg.len)
nico@215: 00228          {
nico@215: 00229          std::string data_byte_str;
nico@215: 00230          buf >> std::setw(2) >> data_byte_str;
nico@215: 00231          if (data_byte_str[0] == ';')
nico@215: 00232             break;
nico@215: 00233          long byte_val = -1;
nico@215: 00234          std::istringstream(data_byte_str) >> std::hex >> byte_val;
nico@215: 00235          if (byte_val == -1)
nico@215: 00236             {
nico@215: 00237             bufsize = 0;
nico@215: 00238             return false;
nico@215: 00239             }
etisserant@240: 00240          msg.data[msg.len] = (UNS8)byte_val;
nico@215: 00241          }
etisserant@240: 00242       if (msg.len == 8)
nico@215: 00243          {
nico@215: 00244          char semicolon = 0;
nico@215: 00245          buf >> semicolon;
nico@215: 00246          if (semicolon != ';')
nico@215: 00247             {
nico@215: 00248             bufsize = 0;
nico@215: 00249             return false;
nico@215: 00250             }
nico@215: 00251          }
nico@215: 00252 
nico@215: 00253       }
nico@215: 00254    else if (request == 'R')
nico@215: 00255       {
etisserant@240: 00256       msg.rtr = 1;
etisserant@240: 00257       buf >> msg.len;
nico@215: 00258       }
nico@215: 00259    else
nico@215: 00260       {
nico@215: 00261       bufsize = 0;
nico@215: 00262       return false;
nico@215: 00263       }
nico@215: 00264 
nico@215: 00265    bufsize = buf.tellg();
nico@215: 00266 
nico@215: 00267    *m = msg;
nico@215: 00268    return true;
nico@215: 00269    }
nico@215: 00270 
etisserant@240: 00271 bool can_uvccm_win32::set_can_data(const Message& m, std::string& can_cmd)
nico@215: 00272    {
nico@215: 00273    // build can_uvccm_win32 command string
nico@215: 00274    std::ostringstream can_cmd_str;
etisserant@240: 00275    can_cmd_str << ":S" << std::hex << m.cob_id.w;
etisserant@240: 00276    if (m.rtr == 1)
nico@215: 00277       {
etisserant@240: 00278       can_cmd_str << 'R' << (long)m.len;
nico@215: 00279       }
nico@215: 00280    else
nico@215: 00281       {
nico@215: 00282       can_cmd_str << 'N';
etisserant@240: 00283       for (int i = 0; i < m.len; ++i)
etisserant@240: 00284          can_cmd_str << std::hex << std::setfill('0') << std::setw(2) << (long)m.data[i];
nico@215: 00285       }
nico@215: 00286    can_cmd_str << ';';
nico@215: 00287    can_cmd = can_cmd_str.str();
nico@215: 00288 #ifdef BOOST_VERSION
nico@215: 00289    boost::to_upper(can_cmd);
nico@215: 00290 #else
nico@215: 00291    std::transform(can_cmd.begin(),can_cmd.end(),can_cmd.begin(),::toupper);
nico@215: 00292 #endif
nico@215: 00293    return true;
nico@215: 00294    }
nico@215: 00295 
nico@215: 00296 
nico@215: 00297 //------------------------------------------------------------------------
nico@215: 00298 extern "C"
etisserant@240: 00299    UNS8 canReceive_driver(CAN_HANDLE fd0, Message *m)
nico@215: 00300    {
etisserant@240: 00301    return (UNS8)(!(reinterpret_cast<can_uvccm_win32*>(fd0)->receive(m)));
nico@215: 00302    }
nico@215: 00303 
nico@215: 00304 extern "C"
etisserant@240: 00305    UNS8 canSend_driver(CAN_HANDLE fd0, Message *m)
nico@215: 00306    {
etisserant@240: 00307    return (UNS8)reinterpret_cast<can_uvccm_win32*>(fd0)->send(m);
nico@215: 00308    }
nico@215: 00309 
nico@215: 00310 extern "C"
etisserant@240: 00311    CAN_HANDLE canOpen_driver(s_BOARD *board)
nico@215: 00312    {
nico@215: 00313    try
nico@215: 00314       {
etisserant@240: 00315       return (CAN_HANDLE) new can_uvccm_win32(board);
nico@215: 00316       }
nico@215: 00317    catch (can_uvccm_win32::error&)
nico@215: 00318       {
nico@215: 00319       return NULL;
nico@215: 00320       }
nico@215: 00321    }
nico@215: 00322 
nico@215: 00323 extern "C"
etisserant@240: 00324    int canClose_driver(CAN_HANDLE inst)
nico@215: 00325    {
nico@215: 00326    delete reinterpret_cast<can_uvccm_win32*>(inst);
nico@215: 00327    return 1;
nico@215: 00328    }
nico@215: 00329 
nico@215: 00330 
nico@215: 00331    
etisserant@240: 

Generated on Mon Jul 2 19:10:16 2007 for CanFestival by  nico@215: nico@215: doxygen 1.5.1
nico@215: nico@215: