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