can_uvccm_win32.cpp

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

Generated on Fri Jun 8 08:51:38 2007 for CanFestival by  doxygen 1.5.1