|
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 |