nico@215: nico@215:
nico@215: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: