serial_w32.cpp
Go to the documentation of this file.
1 
9 /*****************************************************************************
10 ** Platform Check
11 *****************************************************************************/
12 
13 #include <ecl/config.hpp>
14 #ifdef ECL_IS_WIN32
15 
16 /*****************************************************************************
17 ** Includes
18 *****************************************************************************/
19 
21 #include "../../include/ecl/devices/serial_w32.hpp"
22 
23 /*****************************************************************************
24 ** Namespaces
25 *****************************************************************************/
26 
27 namespace ecl {
28 
29 /*****************************************************************************
30 ** Implementation [Serial][C&D]
31 *****************************************************************************/
32 
33 Serial::Serial(const std::string& port_name, const BaudRate &baud_rate, const DataBits &data_bits,
34  const StopBits &stop_bits, const Parity &parity ):
35  port(port_name), is_run(false), file_descriptor(INVALID_HANDLE_VALUE)
36 {
37  try {
38  open(port_name, baud_rate, data_bits, stop_bits, parity);
39  } catch ( StandardException &e ) {
40  throw StandardException(LOC,e);
41  }
42 }
43 
44 Serial::~Serial() {
45  close();
46 }
47 
48 /*****************************************************************************
49 ** Implementation [Serial][Open]
50 *****************************************************************************/
51 
52 void Serial::open(const std::string& port_name, const BaudRate &baud_rate, const DataBits &data_bits,
53  const StopBits &stop_bits, const Parity &parity ) {
54 
55  if ( open() ) {
56  close();
57  }
58 
59  if (strstr(port_name.c_str(), "\\\\.\\"))
60  port = port_name;
61  else
62  port = std::string("\\\\.\\") + port_name;
63 
64  /******************************************
65  * Reset Timeouts
66  ******************************************/
67  m_osRead.Offset = 0;
68  m_osRead.OffsetHigh = 0;
69  if (! (m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
70  throw StandardException(LOC, OpenError, "Serial port failed to open - could not configure offsets.");
71  }
72  m_osWrite.Offset = 0;
73  m_osWrite.OffsetHigh = 0;
74  if (! (m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
75  throw StandardException(LOC, OpenError, "Serial port failed to open - could not configure offsets.");
76  }
77 
78  /******************************************
79  * Open the port handle
80  ******************************************/
81  /*
82  * CreateFileA is the ascii function. CreateFileW is the unicode function.
83  * FILE_FLAG_OVERLAPPED - do not block operations (use events to return)
84  * If the above flag is not specified, it defaults to non-overlapped mode.
85  * When in non-overlapped mode you can't sit in a thread trying to read the
86  * port and simultaneously try to write from another.
87  */
88  file_descriptor = CreateFileA( port.c_str(),
89  GENERIC_READ | GENERIC_WRITE,
90  0,
91  NULL,
92  OPEN_EXISTING, // Serial ports already exist
93  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
94  NULL);
95 
96  /******************************************
97  * Open - error handling
98  ******************************************/
99  if (file_descriptor == INVALID_HANDLE_VALUE ) {
100  int error = GetLastError();
101  switch (error) {
102  case (ERROR_PATH_NOT_FOUND) : {
103  throw StandardException(LOC,NotFoundError);
104  break;
105  }
106  case (ERROR_FILE_NOT_FOUND) : {
107  throw StandardException(LOC,NotFoundError);
108  break;
109  }
110  default : {
111  throw StandardException(LOC,OpenError);
112  }
113  }
114  }
115 
116  /******************************************
117  ** Configuration
118  *******************************************/
119  static const int baud_rate_flags[] = { CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, CBR_4800, CBR_9600, CBR_19200, CBR_38400, CBR_57600, CBR_115200 };
120  static const int stop_bits_flags[] = { ONESTOPBIT, TWOSTOPBITS }; // Windoze has a ONE5STOPBITS as well
121  static const int parity_types_flags[] = { NOPARITY, ODDPARITY, EVENPARITY }; // There are others but we dont need
122  static const int data_bits_flags[] = { 5, 6, 7, 8 };
123 
124  /******************************************
125  * Initialise events to monitor
126  ******************************************/
127  // EV_RXCHAR : A character was received and placed in the input buffer.
128  SetCommMask( file_descriptor, EV_RXCHAR );
129 
130  /******************************************
131  * Initialise the input/output buffer's size
132  ******************************************/
133  SetupComm( file_descriptor, 2048, 2048 ); // 4096,4096
134 
135  /******************************************
136  * Clear
137  ******************************************/
138  PurgeComm( file_descriptor, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR );
139 
140  /******************************************
141  * Dont care about write timeouts (could be dangerous!?)
142  ******************************************/
143  COMMTIMEOUTS timeouts;
144  GetCommTimeouts( file_descriptor, &timeouts );
145  timeouts.WriteTotalTimeoutMultiplier = 0;
146  timeouts.WriteTotalTimeoutConstant = 0;
147  SetCommTimeouts( file_descriptor, &timeouts );
148 
149  /******************************************
150  * Setup the Configuration Structure
151  ******************************************/
152  DCB dcb;
153 
154  FillMemory(&dcb, sizeof(dcb), 0);
155 
156  // MS recommends just grabbing the current state and modifying the required parameters on the
157  // off chance the structure may change in the future.
158  if ( !GetCommState( file_descriptor, &dcb) ) {
159  throw StandardException(LOC,ConfigurationError,"There was an error retrieving the state of the port.");
160  };
161 
162  /******************************************
163  * Protocol Configuration
164  ******************************************/
165  dcb.DCBlength = sizeof(dcb);
166  dcb.BaudRate = baud_rate_flags[baud_rate];
167  dcb.ByteSize = data_bits_flags[data_bits];
168  dcb.Parity = parity_types_flags[parity];
169  dcb.StopBits = stop_bits_flags[stop_bits];
170  dcb.fNull= FALSE; // If true, discard null bytes if they are received
171 
172  // dcb.fAbortOnError = TRUE; // Need?
173 
174  /******************************************
175  * Flow Control
176  ******************************************/
177  dcb.fInX = FALSE; // XON/XOFF flow control during reception.
178  dcb.fOutX = FALSE; // XON/XOFF flow control during transmission.
179  dcb.fDtrControl = DTR_CONTROL_DISABLE; // Data-Terminal-Ready flow control
180  dcb.fRtsControl = RTS_CONTROL_DISABLE; // Request-To-Send flow control.
181 
182  dcb.fOutxCtsFlow = FALSE; // don't monitor the Clear-To-Send output flow control signal.
183  dcb.fOutxDsrFlow = FALSE; // don't monitor the Data-Set-Ready output flow control signal.
184 
185  // dcb.XonLim = 1000; // Number of bytes in the input buffer before flow control is activated to inhibit the sender.
186  // dcb.XoffLim = 1000; // Similar situation as above...
187  // dcb.XonChar = ASCII_XON;
188  // dcb.XoffChar = ASCII_XOFF;
189 
190  /******************************************
191  * Save and Handle Errors
192  ******************************************/
193  if (!SetCommState( file_descriptor, &dcb)) {
194  int error = GetLastError();
195  switch ( error ) {
196  case ( ERROR_INVALID_PARAMETER ) : {
197  throw StandardException(LOC,ConfigurationError,"A parameter was configured incorrectly (ERROR_INVALID_PARAMETER).");
198  break;
199  }
200  default: {
201  throw StandardException(LOC,ConfigurationError);
202  break;
203  }
204  }
205  }
206 
207  /******************************************
208  * Prepare thread for receiving event generated by communication port
209  ******************************************/
210  is_run = true;
211  event_receiver.cancel();
212  event_receiver.start(generateFunctionObject(event_proc, this));
213 
214  block(5000);
215  is_open = true;
216 }
217 
218 void Serial::close() {
219  if ( open() ) {
220 
221  // release thread for receiving event
222  is_run = false;
223  event_receiver.join();
224  event_receiver.cancel();
225 
226  if (file_descriptor != INVALID_HANDLE_VALUE) {
227  // These return values, but assume it works ok, not really critical.
228  SetCommMask(file_descriptor, 0);
229  PurgeComm(file_descriptor, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR );
230  CloseHandle(file_descriptor);
231  file_descriptor = INVALID_HANDLE_VALUE;
232  }
233 
234  is_open = false;
235  }
236 }
237 
238 /*****************************************************************************
239 ** Implementation [Serial][Threading Procedure]
240 *****************************************************************************/
241 void event_proc(void* arg) {
242  Serial* owner = (Serial*)arg;
243  if (!owner)
244  return;
245  while (owner->is_run) {
246  DWORD mask;
247  if (!WaitCommEvent(owner->file_descriptor, &mask, NULL)) {
248  if (0x000003e3 == GetLastError()) {
249  break;
250  }
251  }
252  }
253  owner->file_descriptor = INVALID_HANDLE_VALUE;
254  owner->is_open = false;
255 }
256 
257 /*****************************************************************************
258 ** Implementation [Serial][Writing]
259 *****************************************************************************/
260 
261 long Serial::write(const char &c) {
262  return write(&c, 1);
263 }
264 
265 long Serial::write(const char *s, unsigned long n) {
266  DWORD written, error, error_flags;
267  COMSTAT comstat;
268  int result;
269 
270  result = WriteFile( file_descriptor, s, n, &written, &m_osWrite);
271 
272  if (!result) {
273  if (GetLastError() == ERROR_IO_PENDING) {
274  while (!GetOverlappedResult(file_descriptor, &m_osWrite, &written, TRUE)) {
275  error = GetLastError();
276  if (error != ERROR_IO_INCOMPLETE) {
277  ClearCommError( file_descriptor, &error_flags, &comstat);
278  break;
279  }
280  }
281  } else {
282  written = 0;
283  ClearCommError(file_descriptor, &error_flags, &comstat);
284  }
285  }
286  ecl_assert_throw( written != 0, StandardException(LOC,WriteError) );
287  return written;
288 }
289 
290 /*****************************************************************************
291 ** Implementation [Serial][Reading Modes]
292 *****************************************************************************/
293 
294 void Serial::block(const long &timeout) {
295 
296  ecl_assert_throw( timeout >= 0, StandardException(LOC, InvalidInputError, "Serial port timeouts must be greater than 0ms.") );
297  COMMTIMEOUTS timeouts;
298  GetCommTimeouts( file_descriptor, &timeouts );
299 
300  // See http://msdn2.microsoft.com/en-us/library/ms885171.aspx for details
301  // about this combination of settings.
302  timeouts.ReadIntervalTimeout = MAXDWORD;
303  timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
304  timeouts.ReadTotalTimeoutConstant = static_cast<DWORD>(timeout);
305  SetCommTimeouts( file_descriptor, &timeouts );
306 }
307 
308 void Serial::unblock() {
309  COMMTIMEOUTS timeouts;
310  GetCommTimeouts( file_descriptor, &timeouts );
311 
312  // See http://msdn2.microsoft.com/en-us/library/ms885171.aspx for details
313  // about this combination of settings.
314  timeouts.ReadIntervalTimeout = MAXDWORD;
315  timeouts.ReadTotalTimeoutMultiplier = 0;
316  timeouts.ReadTotalTimeoutConstant = 0;
317  SetCommTimeouts( file_descriptor, &timeouts );
318 }
319 
320 /*****************************************************************************
321 ** Implementation [Serial][Reading]
322 *****************************************************************************/
323 long Serial::remaining() {
324  unsigned long error;
325  _COMSTAT status;
326  if ( ClearCommError(file_descriptor,&error,&status) ) {
327  return status.cbInQue;
328  } else {
329  return -1;
330  }
331 }
332 
333 long Serial::read(char &c) {
334  return read(&c,1);
335 }
336 
337 long Serial::read(char *s, const unsigned long &n)
338 {
339  COMSTAT comstat;
340  DWORD read=0, error, error_flags;
341  DWORD dwRes;
342 
343  /*********************
344  ** Windows Hack
345  **********************/
346  // If its blocking for a minimum number of characters,
347  /*********************
348  ** Reading Serial
349  **********************/
350  if (file_descriptor == INVALID_HANDLE_VALUE) {
351  return 0;
352  }
353 
354  if (!ReadFile( file_descriptor, s, n, &read, &m_osRead) ) {
355  error = GetLastError();
356 
357  if( error != ERROR_IO_PENDING ) {
358  if (error != ERROR_ACCESS_DENIED)
359  ClearCommError(file_descriptor, &error_flags, &comstat);
360 
361  return 0;
362  } else {
363  dwRes = WaitForSingleObject( m_osRead.hEvent, INFINITE );
364 
365  switch( dwRes ) {
366  case WAIT_OBJECT_0:
367  if( !GetOverlappedResult( file_descriptor, &m_osRead, &read, FALSE) ) {
368  ClearCommError(file_descriptor, &error_flags, &comstat);
369  return 0;
370  } else {
371  ClearCommError(file_descriptor, &error_flags, &comstat);
372  return read;
373  }
374  break;
375  default:
376  {
377  ClearCommError(file_descriptor, &error_flags, &comstat);
378  return 0;
379  }
380  break;
381  }
382  }
383  }
384  return read;
385 }
386 } // namespace ecl
387 
388 #endif /* ECL_IS_WIN32 */
ecl::Parity
Parity
Parity of the serial packet.
Definition: serial_parameters.hpp:83
ecl::DataBits
DataBits
Data bits used in a serial packet.
Definition: serial_parameters.hpp:60
config.hpp
ecl_assert_throw
#define ecl_assert_throw(expression, exception)
ecl::BaudRate
BaudRate
Serial connection baud rate.
Definition: serial_parameters.hpp:38
standard_exception.hpp
ecl
Embedded control libraries.
generateFunctionObject
NullaryFreeFunction< R > generateFunctionObject(R(*function)())
ecl::StopBits
StopBits
Stop bits used in a serial packet.
Definition: serial_parameters.hpp:72


ecl_devices
Author(s): Daniel Stonier
autogenerated on Wed Mar 2 2022 00:16:45