canserial-peak.cpp
Go to the documentation of this file.
1 //======================================================================
14 //======================================================================
15 
16 #include "sdhlibrary_settings.h"
17 
18 //----------------------------------------------------------------------
19 // System Includes - include with <>
20 //----------------------------------------------------------------------
21 
22 #include <fcntl.h>
23 //#include <termios.h>
24 #include <stdio.h>
25 #if ! SDH_USE_VCC
26 # include <unistd.h>
27 #endif
28 #include <iostream>
29 #include <exception>
30 #include <stdarg.h>
31 #include <assert.h>
32 
33 //----------------------------------------------------------------------
34 // Project Includes - include with ""
35 //----------------------------------------------------------------------
36 
37 #include "canserial-peak.h"
38 #include "simpletime.h"
39 #include "util.h"
40 #if defined( OSNAME_LINUX )
41 # include <libpcan.h>
42 
43 #elif defined( OSNAME_CYGWIN )
44 // on cygwin Pcan_usb.h includes windef.h which defines macros named max/min which the compiler confuses with max/min templates
45 // but defining NOMINMAX prevents those evil macros from being defined
46 # define NOMINMAX
47 # include <windows.h>
48 # include <Pcan_usb.h>
49 
50 #elif SDH_USE_VCC
51 # include <windows.h>
52 # include <Pcan_usb.h>
53 
54 #else
55 # error "FIXME: support for PEAK CAN devices in other systems than linux/cygwin is not provided yet!"
56  // e.g. the include header from peak is named libpcan.h on linux and peak_usb.h on windows
57 #endif
58 
59 //----------------------------------------------------------------------
60 // Defines, enums, unions, structs,
61 //----------------------------------------------------------------------
62 
64 
66 // the Linux version of the PEAK library uses handles,
67 // while the cygwin/windows version does not. So use a macro
68 // to keep the code somewhat clearer
69 #if defined( OSNAME_LINUX )
70 # define USE_HANDLES( H_ ) (H_),
71 # define USE_HANDLE( H_ ) (H_)
72 typedef HANDLE PCAN_HANDLE;
74 
75 #else
76 # define USE_HANDLES( H_ )
77 # define USE_HANDLE( H_ )
78 typedef void* PCAN_HANDLE; // dummy definition
80 #endif
82 
92 #define SDH_CANSERIAL_PEAK_DEBUG 1
93 
98 #if SDH_CANSERIAL_PEAK_DEBUG
99 # define DBG( ... ) \
100  do { \
101  __VA_ARGS__; \
102  } while (0)
103 #else
104 # define DBG( ... )
105 #endif
106 
107 //----------------------------------------------------------------------
108 // Global variables
109 //----------------------------------------------------------------------
110 
111 
112 //----------------------------------------------------------------------
113 // Function and class member implementation (function definitions)
114 //----------------------------------------------------------------------
115 
116 using namespace std;
117 
119 
121 
127 {
128 public:
131 
132 #if defined( OSNAME_LINUX )
133  int timeout_us;
134 #else
135  // The cygwin/windows version of the PEAK library/driver cannot handle timeouts...
136 #endif
137 
144 #if defined( OSNAME_LINUX )
145  TPCANRdMsg m_cmsg;
146 # define M_CMSG_MSG() m_cmsg.Msg
147 #else
148  TPCANMsg m_cmsg;
149 # define M_CMSG_MSG() m_cmsg
150 #endif
151  int m_cmsg_next;
153 
155  DWORD rc;
156 };
158 
159 
160 cCANSerial_PEAK::cCANSerial_PEAK( unsigned long _baudrate, double _timeout, int _id_read, int _id_write, const char *device )
161 {
162  pimpl = NULL;
163  assert( sizeof( PCAN_HANDLE ) == sizeof( tDeviceHandle) ); // if this fails, please adjust the tDeviceHandle type
164 
165  if ( _timeout < 0.0 )
166  throw new cCANSerial_PEAKException( cMsg( "Invalid timeout %f (must be >= 0)", _timeout ) );
167 
168  pimpl = new cCANSerial_PEAK_Internal();
169  pimpl->peak_handle = (PCAN_HANDLE) NULL;
170  baudrate = _baudrate;
171  SetTimeout( _timeout );
172  id_read = _id_read;
173  id_write = _id_write;
174  strncpy(m_device, device, 64);
175 
176  ungetch_valid = false;
177 }
178 //----------------------------------------------------------------------
179 
180 cCANSerial_PEAK::cCANSerial_PEAK( PCAN_HANDLE _peak_handle, double _timeout, int _id_read, int _id_write )
181 {
182  pimpl = NULL;
183  if ( _timeout < 0.0 )
184  throw new cCANSerial_PEAKException( cMsg( "Invalid timeout %f (must be >= 0)", _timeout ) );
185 
186 #if defined( OSNAME_LINUX )
187  if ( _peak_handle == NULL )
188  throw new cCANSerial_PEAKException( cMsg( "Cannot reuse invalid PEAK CAN handle" ) );
189 #endif
190 
191  pimpl = new cCANSerial_PEAK_Internal();
192  pimpl->peak_handle = _peak_handle;
193  baudrate = 0;
194  SetTimeout( _timeout );
195  id_read = _id_read;
196  id_write = _id_write;
197 
198  ungetch_valid = false;
199 }
200 //----------------------------------------------------------------------
201 
203 {
204  if ( pimpl )
205  delete pimpl;
206 }
207 //----------------------------------------------------------------------
208 
209 char const* PEAK_strerror( DWORD rc )
210 {
211  switch (rc)
212  {
213  DEFINE_TO_CASECOMMAND( CAN_ERR_OK);
214  DEFINE_TO_CASECOMMAND( CAN_ERR_XMTFULL );
215  DEFINE_TO_CASECOMMAND( CAN_ERR_OVERRUN );
216  DEFINE_TO_CASECOMMAND( CAN_ERR_BUSLIGHT );
217  DEFINE_TO_CASECOMMAND( CAN_ERR_BUSHEAVY );
218  DEFINE_TO_CASECOMMAND( CAN_ERR_BUSOFF );
219  DEFINE_TO_CASECOMMAND( CAN_ERR_QRCVEMPTY );
220  DEFINE_TO_CASECOMMAND( CAN_ERR_QOVERRUN );
221  DEFINE_TO_CASECOMMAND( CAN_ERR_QXMTFULL );
222  DEFINE_TO_CASECOMMAND( CAN_ERR_REGTEST );
223  DEFINE_TO_CASECOMMAND( CAN_ERR_NOVXD );
224  DEFINE_TO_CASECOMMAND( CAN_ERR_RESOURCE );
225  DEFINE_TO_CASECOMMAND( CAN_ERR_ILLPARAMTYPE );
226  DEFINE_TO_CASECOMMAND( CAN_ERR_ILLPARAMVAL );
227  default:
228  return "unknown";
229  }
230 }
231 //-----------------------------------------------------------------
232 
233 
235 {
236  if (pimpl->peak_handle == NULL)
237  {
238  // only open if we're not reusing an existing handle:
239  DBG( dbg << "Opening PEAK CAN baudrate: " << baudrate << ", id_read: 0x" << std::hex << id_read << ", id_write: 0x" << id_write << std::dec << "\n" );
240 
241 #if defined( OSNAME_LINUX )
242  pimpl->peak_handle = LINUX_CAN_Open((char*) m_device, O_RDWR );//| O_NONBLOCK);
243  if (pimpl->peak_handle == NULL)
244  {
245  pimpl->rc = nGetLastError();
246  // open failed, so ensure that handle is invalid
247  pimpl->peak_handle = NULL;
248  throw new cCANSerial_PEAKException( cMsg( "Could not open PEAK CAN device \"%s\": %s", m_device, GetLastErrorMessage() ) );
249  }
250 #else
251  pimpl->peak_handle = (PCAN_HANDLE) 1; // nothing more to do on cygwin/windows
252 #endif
253 
254  pimpl->rc = CAN_Init( USE_HANDLES( pimpl->peak_handle ) WORD(BaudrateToBaudrateCode(baudrate)), CAN_INIT_TYPE_ST);
255  if ( pimpl->rc )
256  {
257 #if ! defined( OSNAME_LINUX )
258  pimpl->peak_handle = NULL;
259 #endif
260  throw new cCANSerial_PEAKException( cMsg( "Could not set baudrate to %lu on Peak CAN device \"%s\": %s",
261  baudrate, m_device, GetLastErrorMessage() ) );
262  }
263 
264  pimpl->rc = CAN_ResetFilter( USE_HANDLE(pimpl->peak_handle) );
265  if ( pimpl->rc )
266  {
267 #if ! defined( OSNAME_LINUX )
268  pimpl->peak_handle = NULL;
269 #endif
270  throw new cCANSerial_PEAKException( cMsg( "Could not reset CAN ID 0x%03x on Peak CAN device \"%s\": %s",
271  (unsigned int) id_read, m_device, GetLastErrorMessage() ) );
272  }
273 
274  pimpl->rc = CAN_MsgFilter( USE_HANDLES(pimpl->peak_handle) DWORD(id_read), DWORD(id_read), MSGTYPE_STANDARD);
275  if ( pimpl->rc )
276  {
277 #if ! defined( OSNAME_LINUX )
278  pimpl->peak_handle = NULL;
279 #endif
280  throw new cCANSerial_PEAKException( cMsg( "Could not add CAN ID 0x%03x on Peak CAN device \"%s\": %s",
281  (unsigned int) id_read, m_device, GetLastErrorMessage() ) );
282  }
283  }
284  // (re)init member data:
285  pimpl->M_CMSG_MSG().LEN = 0;
286  pimpl->m_cmsg_next = 0;
287 }
288 //----------------------------------------------------------------------
289 
290 
292  throw()
293 {
294  return ( pimpl->peak_handle!=NULL );
295 }
296 //----------------------------------------------------------------------
297 
298 
300 {
301  if ( pimpl->peak_handle == NULL )
302  throw new cCANSerial_PEAKException( cMsg( "Could not close un-opened device" ) );
303 
304  CAN_Close( USE_HANDLE( pimpl->peak_handle ) );
305  pimpl->peak_handle = NULL;
306 }
307 //----------------------------------------------------------------------
308 
309 int cCANSerial_PEAK::BaudrateToBaudrateCode( unsigned long baudrate )
310 {
311  switch (baudrate)
312  {
313  case 1000000: return CAN_BAUD_1M;
314  case 800000: return CAN_BAUD_500K;
315  case 500000: return CAN_BAUD_500K;
316  case 250000: return CAN_BAUD_250K;
317  case 125000: return CAN_BAUD_125K;
318  case 100000: return CAN_BAUD_100K;
319  case 50000: return CAN_BAUD_50K;
320  case 20000: return CAN_BAUD_20K;
321  case 10000: return CAN_BAUD_10K;
322  case 5000: return CAN_BAUD_5K;
323  }
324 
325  throw new cCANSerial_PEAKException( cMsg( "Invalid baudrate %ld", baudrate ) );
326 }
327 //----------------------------------------------------------------------
328 
329 int cCANSerial_PEAK::write( char const *ptr, int len )
330 {
331  assert( pimpl->peak_handle != NULL );
332 
333  //cerr << "in cCANSerial_PEAK::write\n"; cerr.flush();
334  if ( len == 0 )
335  len = int( strlen( ptr ) );
336 
337  //cerr << "sending " << len << " bytes <" << ptr << "> to CAN device\n"; cerr.flush();
338 
339  // calculate number of CMSGS needed (max 8 data bytes per CMSG)
340  Int32 len_cmsgs = len/8 + (((len%8)!=0) ? 1 : 0);
341 
342  //---------------------
343  TPCANMsg cmsg;
344  int j;
345  for ( int i=0; i < len_cmsgs; i++)
346  {
347  // prepare message to send:
348  cmsg.ID = DWORD(id_write);
349  cmsg.LEN = min( 8, len-i*8 );
350  cmsg.MSGTYPE = MSGTYPE_STANDARD;
351  for ( j=0; j<cmsg.LEN; j++ )
352  cmsg.DATA[ j ] = *(ptr++);
353  //-----
354 
355  //-----
356  // now send the cmsgs and check return values
357 #if defined( OSNAME_LINUX )
358  pimpl->rc = LINUX_CAN_Write_Timeout(pimpl->peak_handle, &cmsg, pimpl->timeout_us );
359 #else
360  pimpl->rc = CAN_Write( &cmsg );
361 #endif
362  if ( pimpl->rc )
363  {
364  throw new cCANSerial_PEAKException( cMsg( "Could not write message %d/%d on PEAK CAN device \"%s\": %s",
365  i, (int)len_cmsgs, m_device, GetLastErrorMessage() ) );
366  }
367  DBG( dbg << "cCANSerial_PEAK::write wrote CAN frame ID:0x" << std::hex << cmsg.ID << " LEN=" << int(cmsg.LEN) << " DATA (hex):" << cHexByteString( (char const*) cmsg.DATA, cmsg.LEN ) << " bytes_written:" << (i*8+cmsg.LEN) << "/" << len << "\n" );
368 
369  }
370  return len;
371 }
372 //----------------------------------------------------------------------
373 
374 
375 ssize_t cCANSerial_PEAK::Read( void *_data, ssize_t size, long r_timeout_us, bool return_on_less_data )
376 {
377  assert( pimpl->peak_handle != NULL );
378 
379  char* data = (char*) _data;
380 
381 #if ! defined( OSNAME_LINUX )
382  // in windows (cygwin and VCC) we must implement our own timeout mechanism
383  cSimpleTime start_time;
384 #endif
385 
386  //---------------------
387  int bytes_read = 0;
388 
389  do
390  {
391  //------
392  // first: copy remaining, not yet returned bytes from a previous canRead call to data
393  for ( ; pimpl->m_cmsg_next < pimpl->M_CMSG_MSG().LEN && bytes_read < size; pimpl->m_cmsg_next++, bytes_read++ )
394  *data++ = pimpl->M_CMSG_MSG().DATA[ pimpl->m_cmsg_next ];
395  //------
396 
397  if ( bytes_read < size )
398  {
399  //-----
400  // if necessary read one more messages with blocking call
401 
402  pimpl->M_CMSG_MSG().LEN = 0;
403  pimpl->m_cmsg_next = 0;
404 #if defined( OSNAME_LINUX )
405  //dbg << __func__ << " r_timeout_us=" << r_timeout_us << "\n";
406  if ( r_timeout_us == 0 )
407  {
408  //pimpl->rc = LINUX_CAN_Read( pimpl->peak_handle, &(pimpl->m_cmsg) ); // will sometimes hang the process here!!!
409  if ( return_on_less_data )
410  {
411  //dbg << "polling read\n";
412  pimpl->rc = LINUX_CAN_Read_Timeout( pimpl->peak_handle, &(pimpl->m_cmsg), 0 ); // use polling
413  }
414  else
415  {
416  //dbg << "blocking read\n";
417  pimpl->rc = LINUX_CAN_Read_Timeout( pimpl->peak_handle, &(pimpl->m_cmsg), -1 ); // use blocking read
418  }
419  }
420  else
421  {
422  //dbg << "timeout read" << r_timeout_us << "us\n";
423  pimpl->rc = LINUX_CAN_Read_Timeout( pimpl->peak_handle, &(pimpl->m_cmsg), r_timeout_us );
424  }
425 #else
426  pimpl->rc = CAN_Read( &(pimpl->m_cmsg) );
427 #endif
428  //-----
429 
430  //-----
431  // check the received message:
432 
433  // simple check: return code of the Read call:
434  //dbg << "pimpl->rc=" << pimpl->rc << "\n";
435  if (pimpl->rc==0xFFFFFFFF)
436  {
437  // ANOTE: although undocumented this seems to be the "no data available" answer when timeout=0
438 
439  //dbg << "***Ignoring error rc=-1 r_timeout_us=" << r_timeout_us <<" return_on_less_data=" << return_on_less_data << " errno=" << errno << "=\"" << strerror( errno ) << "\"\n";
440  continue;
441  }
442 
443  if (pimpl->rc > 0)
444  {
445  pimpl->M_CMSG_MSG().LEN = 0;
446 
447  //if ( (r_timeout_us == 0 || return_on_less_data) && pimpl->rc == CAN_ERR_QRCVEMPTY )
448  if ( pimpl->rc == CAN_ERR_QRCVEMPTY )
449  {
450  // no error, just no more data available
451 #if ! defined( OSNAME_LINUX )
452  if ( return_on_less_data && bytes_read > 0 )
453  {
454  //DBG( dbg << "cCANSerial_PEAK::Read1 took " << start_time.Elapsed_us() << "us\n" );
455  return bytes_read;
456  }
457 
458  if ( r_timeout_us == 0 || start_time.Elapsed_us() < r_timeout_us)
459  continue;
460 #endif
461  //DBG( dbg << "cCANSerial_PEAK::Read2 took " << start_time.Elapsed_us() << "us\n" );
462  return bytes_read;
463  }
464  throw new cCANSerial_PEAKException( cMsg( "Could not read CAN messages from CAN Peak device \"%s\": %s",
465  m_device, GetLastErrorMessage() ) );
466  }
467 
468  // check the actual type of the returned message:
469  if ( pimpl->M_CMSG_MSG().MSGTYPE != MSGTYPE_STANDARD )
470  {
471  pimpl->M_CMSG_MSG().LEN = 0;
472  if ( pimpl->M_CMSG_MSG().MSGTYPE == MSGTYPE_EXTENDED || pimpl->M_CMSG_MSG().MSGTYPE == MSGTYPE_RTR )
473  {
474  cerr << "Ignoring invalid CAN message of type " << pimpl->M_CMSG_MSG().MSGTYPE << "\n"; cerr.flush();
475  continue;
476  }
477  // its a MSGTYPE_STATUS indicating an error
478  // so the actual error code is in the data bytes in big endian
479  pimpl->rc = (DWORD(pimpl->M_CMSG_MSG().DATA[0])<<24) | (DWORD(pimpl->M_CMSG_MSG().DATA[1])<<16) | (DWORD(pimpl->M_CMSG_MSG().DATA[2])<<8) | DWORD(pimpl->M_CMSG_MSG().DATA[3]);
480  throw new cCANSerial_PEAKException( cMsg( "Error frame from CAN Peak device \"%s\": %s",
481  m_device, GetLastErrorMessage() ) );
482  }
483 
484  // check the ID:
485  if ( pimpl->M_CMSG_MSG().ID != DWORD(id_read) )
486  {
487  pimpl->M_CMSG_MSG().LEN = 0;
488  throw new cCANSerial_PEAKException( cMsg( "Invalid CAN ID 0x%03x received, expected 0x%03x",
489  (unsigned int) pimpl->M_CMSG_MSG().ID, (unsigned int) id_read ) );
490  }
491 
492  // finally copy received bytes to user data:
493  for ( ; pimpl->m_cmsg_next < pimpl->M_CMSG_MSG().LEN && bytes_read < size; pimpl->m_cmsg_next++, bytes_read++ )
494  *data++ = pimpl->M_CMSG_MSG().DATA[ pimpl->m_cmsg_next ];
495 
496  DBG( dbg << "cCANSerial_PEAK::Read read CAN frame ID:0x" << std::hex << pimpl->M_CMSG_MSG().ID << " LEN=" << int(pimpl->M_CMSG_MSG().LEN) << " DATA (hex):" << cHexByteString( (char const*) pimpl->M_CMSG_MSG().DATA, pimpl->M_CMSG_MSG().LEN ) << " bytes_read:" << bytes_read << "/" << size << "\n" );
497  //DBG( dbg << "cCANSerial_PEAK::Read read CAN frame ID:0x" << std::hex << pimpl->M_CMSG_MSG().ID << " LEN=" << int(pimpl->M_CMSG_MSG().LEN) << " DATA (hex):" << cHexByteString( (char const*) pimpl->M_CMSG_MSG().DATA, pimpl->M_CMSG_MSG().LEN ) << " bytes_read:" << bytes_read << "/" << size << " us_elapsed:" << start_time.Elapsed_us() << "\n" );
498  }
499  } while ( bytes_read < size && !return_on_less_data );
500 
501  //DBG( dbg << "cCANSerial_PEAK::Read 3 took " << start_time.Elapsed_us() << "us\n" );
502  return bytes_read;
503 }
504 //----------------------------------------------------------------------
505 
506 
507 void cCANSerial_PEAK::SetTimeout( double _timeout )
508 {
509  cSerialBase::SetTimeout( _timeout );
510 #if defined( OSNAME_LINUX )
511  pimpl->timeout_us = int( _timeout * 1E6 );
512 #endif
513 }
514 //----------------------------------------------------------------------
515 
516 
518 {
519  static char return_msg[512];
520 
521  snprintf( return_msg, 511, "error 0x%x = %d = \"%s\"", (int) dw, (int) dw, PEAK_strerror( (DWORD) pimpl->rc ) );
522  return return_msg;
523 }
524 //----------------------------------------------------------------------
525 
527 {
528  return (tErrorCode) pimpl->rc;
529 }
530 //----------------------------------------------------------------------
531 
533 {
534  return tDeviceHandle( pimpl->peak_handle );
535 }
536 //----------------------------------------------------------------------
537 
538 //======================================================================
539 /*
540  Here are some settings for the emacs/xemacs editor (and can be safely ignored):
541  (e.g. to explicitely set C++ mode for *.h header files)
542 
543  Local Variables:
544  mode:C++
545  mode:ELSE
546  End:
547  */
548 //======================================================================
Interface of auxilliary utility functions for SDHLibrary-CPP.
Interface of class #SDH::cCANSerial_PEAK, class to access CAN bus via PEAK card on cygwin/linux...
#define NULL
Definition: getopt1.c:56
dummy class for (debug) stream output of bytes as list of hex values
Definition: dbg.h:329
#define USE_HANDLES(H_)
long Elapsed_us(void) const
Return time in micro seconds elapsed between the time stored in the object and now.
Definition: simpletime.h:125
PCAN_HANDLE peak_handle
the internal handle to the driver
virtual void SetTimeout(double _timeout)
set the timeout for next readline() calls (negative value means: no timeout, wait for ever) ...
Definition: serialbase.h:151
int tErrorCode
type of the error code, DWORD on windows and int on Linux/cygwin
Definition: serialbase.h:233
Derived exception class for low-level CAN PEAK related exceptions.
int write(char const *ptr, int len=0)
Write data to a previously opened port.
#define NAMESPACE_SDH_START
~cCANSerial_PEAK()
destructor: clean up
Interface of auxilliary utility functions for SDHLibrary-CPP.
int BaudrateToBaudrateCode(unsigned long baudrate)
Translate a baudrate given as unsigned long into a baudrate code for struct termios.
#define USE_HANDLE(H_)
virtual tErrorCode GetErrorNumber()
ssize_t Read(void *data, ssize_t size, long timeout_us, bool return_on_less_data)
bool IsOpen(void)
Return true if interface to CAN PEAK is open.
#define USING_NAMESPACE_SDH
UInt16 size
Definition: dsa.h:269
void SetTimeout(double _timeout)
set the timeout for next readline() calls (negative value means: no timeout, wait for ever) ...
cCANSerial_PEAK(cCANSerial_PEAK const &other)
private copy constructor without implementation, since copying of cCANSerial_PEAK objects makes no se...
virtual char const * GetErrorMessage(tErrorCode dw)
char const * PEAK_strerror(DWORD rc)
#define NAMESPACE_SDH_END
This file contains settings to make the SDHLibrary compile on differen systems:
DWORD rc
last return code of calls to Peak functions
Very simple class to measure elapsed time.
Definition: simpletime.h:84
#define DBG(...)
void Close(void)
Close the previously opened CAN PEAK interface port.
Class for short, fixed maximum length text messages.
Definition: sdhexception.h:77
void * PCAN_HANDLE
Linux libpcan uses HANDLE where Windows Pcan_usb.h uses no handle at all:
#define DEFINE_TO_CASECOMMAND(_c)
Definition: util.h:103
internal hardware specific implementation details of the lowlevel PEAK CAN interface ...
tDeviceHandle GetHandle()
int32_t Int32
signed integer, size 4 Byte (32 Bit)
Definition: basisdef.h:64
NAMESPACE_SDH_START typedef void * tDeviceHandle
generic device handle for CAN devices
Definition: serialbase.h:64


sdhlibrary_cpp
Author(s): Dirk Osswald
autogenerated on Sun Aug 18 2019 03:42:20