OpenCPN Partial API docs
comm_drv_n2k_serial.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: Implement comm_drv_n2k.h -- Nmea2000 serial driver.
5  * Author: David Register, Alec Leamas
6  *
7  ***************************************************************************
8  * Copyright (C) 2022 by David Register, Alec Leamas *
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  * This program is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18  * GNU General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU General Public License *
21  * along with this program; if not, write to the *
22  * Free Software Foundation, Inc., *
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24  **************************************************************************/
25 
26 // For compilers that support precompilation, includes "wx.h".
27 #include <wx/wxprec.h>
28 
29 #ifndef WX_PRECOMP
30 #include <wx/wx.h>
31 #endif // precompiled headers
32 
33 #include <vector>
34 #include <mutex> // std::mutex
35 #include <queue> // std::queue
36 
37 #include <wx/log.h>
38 
39 #include "model/comm_drv_n2k_serial.h"
40 #include "model/comm_navmsg_bus.h"
41 #include "model/comm_drv_registry.h"
42 #include "model/logger.h"
43 
44 #include <N2kMsg.h>
45 std::vector<unsigned char> BufferToActisenseFormat( tN2kMsg &msg);
46 
47 template <typename T>
49 public:
50  size_t size() {
51  std::lock_guard<std::mutex> lock(m_mutex);
52  return m_queque.size();
53  }
54 
55  bool empty() {
56  std::lock_guard<std::mutex> lock(m_mutex);
57  return m_queque.empty();
58  }
59 
60  const T& front() {
61  std::lock_guard<std::mutex> lock(m_mutex);
62  return m_queque.front();
63  }
64 
65  void push(const T& value) {
66  std::lock_guard<std::mutex> lock(m_mutex);
67  m_queque.push(value);
68  }
69 
70  void pop() {
71  std::lock_guard<std::mutex> lock(m_mutex);
72  m_queque.pop();
73  }
74 
75 private:
76  std::queue<T> m_queque;
77  mutable std::mutex m_mutex;
78 };
79 
80 template <class T>
81 class circular_buffer {
82 public:
83  explicit circular_buffer(size_t size)
84  : buf_(std::unique_ptr<T[]>(new T[size])), max_size_(size) {}
85 
86  void reset();
87  size_t capacity() const;
88  size_t size() const;
89 
90  bool empty() const {
91  // if head and tail are equal, we are empty
92  return (!full_ && (head_ == tail_));
93  }
94 
95  bool full() const {
96  // If tail is ahead the head by 1, we are full
97  return full_;
98  }
99 
100  void put(T item) {
101  std::lock_guard<std::mutex> lock(mutex_);
102  buf_[head_] = item;
103  if (full_) tail_ = (tail_ + 1) % max_size_;
104 
105  head_ = (head_ + 1) % max_size_;
106 
107  full_ = head_ == tail_;
108  }
109 
110  T get() {
111  std::lock_guard<std::mutex> lock(mutex_);
112 
113  if (empty()) return T();
114 
115  // Read data and advance the tail (we now have a free space)
116  auto val = buf_[tail_];
117  full_ = false;
118  tail_ = (tail_ + 1) % max_size_;
119 
120  return val;
121  }
122 
123 private:
124  std::mutex mutex_;
125  std::unique_ptr<T[]> buf_;
126  size_t head_ = 0;
127  size_t tail_ = 0;
128  const size_t max_size_;
129  bool full_ = 0;
130 };
131 
132 
133 class CommDriverN2KSerialEvent; // fwd
134 
135 class CommDriverN2KSerialThread : public wxThread {
136 public:
138  const wxString& PortName,
139  const wxString& strBaudRate);
140 
142  void* Entry();
143  bool SetOutMsg(const std::vector<unsigned char> &load);
144  void OnExit(void);
145 
146 private:
147 #ifndef __ANDROID__
148  serial::Serial m_serial;
149 #endif
150  void ThreadMessage(const wxString& msg);
151  bool OpenComPortPhysical(const wxString& com_name, int baud_rate);
152  void CloseComPortPhysical();
153  size_t WriteComPortPhysical(std::vector<unsigned char> msg);
154  size_t WriteComPortPhysical(unsigned char *msg, size_t length);
155  void SetGatewayOperationMode(void);
156 
157  CommDriverN2KSerial* m_pParentDriver;
158  wxString m_PortName;
159  wxString m_FullPortName;
160 
161  unsigned char* put_ptr;
162  unsigned char* tak_ptr;
163 
164  unsigned char* rx_buffer;
165 
166  int m_baud;
167  int m_n_timeout;
168 
170 
171 #ifdef __WXMSW__
172  HANDLE m_hSerialComm;
173  bool m_nl_found;
174 #endif
175 };
176 
178 wxDECLARE_EVENT(wxEVT_COMMDRIVER_N2K_SERIAL, CommDriverN2KSerialEvent);
179 
180 class CommDriverN2KSerialEvent : public wxEvent {
181 public:
182  CommDriverN2KSerialEvent(wxEventType commandType = wxEVT_NULL, int id = 0)
183  : wxEvent(id, commandType){};
185 
186  // accessors
187  void SetPayload(std::shared_ptr<std::vector<unsigned char>> data) {
188  m_payload = data;
189  }
190  std::shared_ptr<std::vector<unsigned char>> GetPayload() { return m_payload; }
191 
192  // required for sending with wxPostEvent()
193  wxEvent* Clone() const {
194  CommDriverN2KSerialEvent* newevent = new CommDriverN2KSerialEvent(*this);
195  newevent->m_payload = this->m_payload;
196  return newevent;
197  };
198 
199 private:
200  std::shared_ptr<std::vector<unsigned char>> m_payload;
201 };
202 
203 //========================================================================
204 /* commdriverN2KSerial implementation
205  * */
206 
207 wxDEFINE_EVENT(wxEVT_COMMDRIVER_N2K_SERIAL, CommDriverN2KSerialEvent);
208 
209 CommDriverN2KSerial::CommDriverN2KSerial(const ConnectionParams* params,
210  DriverListener& listener)
211  : CommDriverN2K(((ConnectionParams*)params)->GetStrippedDSPort()),
212  m_Thread_run_flag(-1),
213  m_bok(false),
214  m_portstring(params->GetDSPort()),
215  m_pSecondary_Thread(NULL),
216  m_params(*params),
217  m_listener(listener) {
218  m_BaudRate = wxString::Format("%i", params->Baudrate), SetSecThreadInActive();
219  m_manufacturers_code = 0;
220  m_got_mfg_code = false;
221  this->attributes["canAddress"] = std::string("-1");
222  this->attributes["userComment"] = params->UserComment.ToStdString();
223  this->attributes["ioDirection"] = std::string("IN/OUT");
224 
225  // Prepare the wxEventHandler to accept events from the actual hardware thread
226  Bind(wxEVT_COMMDRIVER_N2K_SERIAL, &CommDriverN2KSerial::handle_N2K_SERIAL_RAW,
227  this);
228 
229  Open();
230 
231  wxMilliSleep(100);
232  GetMfgCode();
233 
234 #if 0
235  // Testing TX of Heartbeat
236  wxSleep(1);
237 
238  tN2kMsg N2kMsg; // automatically sets destination 255
239  //SetHeartbeat(N2kMsg,2000,0);
240  //SetN2kPGN126993(N2kMsg, 2000, 0);
241  N2kMsg.SetPGN(126993L);
242  //N2kMsg.Priority=7;
243  N2kMsg.Source = 2;
244  N2kMsg.Destination = 133;
245  N2kMsg.Add2ByteUInt((uint16_t)(2000)); // Rate, msec
246 
247  N2kMsg.AddByte(0); //Status
248  N2kMsg.AddByte(0xff); // Reserved
249  N2kMsg.Add4ByteUInt(0xffffffff); // Reserved
250 
251  const std::vector<unsigned char> mv = BufferToActisenseFormat(N2kMsg);
252 
253  size_t len = mv.size();
254 
255  wxString comx = m_params.GetDSPort().AfterFirst(':');
256  std::string interface = comx.ToStdString();
257 
258  N2kName source_name(1234);
259  auto source_address = std::make_shared<NavAddr2000>(interface, source_name);
260  auto dest_address = std::make_shared<NavAddr2000>(interface, N2kMsg.Destination);
261 
262  auto message_to_send = std::make_shared<Nmea2000Msg>(126993L,
263  mv, source_address, 3);
264 
265  for(size_t i=0; i< mv.size(); i++){
266  printf("%02X ", mv.at(i));
267  }
268  printf("\n\n");
269 
270  SetTXPGN(126993);
271  wxSleep(1);
272 
273  SendMessage(message_to_send, dest_address);
274 
275  int yyp = 4;
276 #endif
277 
278 
279 }
280 
281 CommDriverN2KSerial::~CommDriverN2KSerial() {
282  Close();
283 }
284 
285 bool CommDriverN2KSerial::Open() {
286  wxString comx;
287  comx = m_params.GetDSPort().AfterFirst(':'); // strip "Serial:"
288 
289  comx =
290  comx.BeforeFirst(' '); // strip off any description provided by Windows
291 
292 #ifndef ANDROID
293  // Kick off the RX thread
294  SetSecondaryThread(new CommDriverN2KSerialThread(this, comx, m_BaudRate));
295  SetThreadRunFlag(1);
296  GetSecondaryThread()->Run();
297 #endif
298 
299  return true;
300 }
301 
302 void CommDriverN2KSerial::Close() {
303  wxLogMessage(
304  wxString::Format(_T("Closing N2K Driver %s"), m_portstring.c_str()));
305 
306  // Kill off the Secondary RX Thread if alive
307  if (m_pSecondary_Thread) {
308  if (m_bsec_thread_active) // Try to be sure thread object is still alive
309  {
310  wxLogMessage(_T("Stopping Secondary Thread"));
311 
312  m_Thread_run_flag = 0;
313  int tsec = 10;
314  while ((m_Thread_run_flag >= 0) && (tsec--)) wxSleep(1);
315 
316  wxString msg;
317  if (m_Thread_run_flag < 0)
318  msg.Printf(_T("Stopped in %d sec."), 10 - tsec);
319  else
320  msg.Printf(_T("Not Stopped after 10 sec."));
321  wxLogMessage(msg);
322  }
323 
324  m_pSecondary_Thread = NULL;
325  m_bsec_thread_active = false;
326  }
327 }
328 
330  CommDriverRegistry::GetInstance().Activate(shared_from_this());
331  // TODO: Read input data.
332 }
333 
334 bool CommDriverN2KSerial::SendMessage(std::shared_ptr<const NavMsg> msg,
335  std::shared_ptr<const NavAddr> addr) {
336 #ifndef ANDROID
337 
338  auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
339  std::vector<uint8_t> load = msg_n2k->payload;
340 
341  uint64_t _pgn = msg_n2k->PGN.pgn;
342  auto destination_address = std::static_pointer_cast<const NavAddr2000>(addr);
343 
344  tN2kMsg N2kMsg; // automatically sets destination 255
345  N2kMsg.SetPGN(_pgn);
346  N2kMsg.Priority = msg_n2k->priority;
347  if (destination_address)
348  N2kMsg.Destination = destination_address->address;
349 
350  for (size_t i=0 ; i < load.size(); i++)
351  N2kMsg.AddByte(load.at(i)); //data
352 
353  const std::vector<uint8_t> mv = BufferToActisenseFormat(N2kMsg);
354 
355 // printf("mv\n");
356 // for(size_t i=0; i< mv.size(); i++){
357 // printf("%02X ", mv.at(i));
358 // }
359 // printf("\n\n");
360 
361  if( GetSecondaryThread() ) {
362  if( IsSecThreadActive() )
363  {
364  int retry = 10;
365  while( retry ) {
366  if( GetSecondaryThread()->SetOutMsg(mv))
367  return true;
368  else
369  retry--;
370  }
371  return false; // could not send after several tries....
372  }
373  else
374  return false;
375  }
376 #endif
377  return true;
378 }
379 
380 void CommDriverN2KSerial::ProcessManagementPacket(std::vector<unsigned char> *payload) {
381 
382  if (payload->at(2) != 0xF2) { // hearbeat
383  //printf(" pl ");
384  //for (unsigned int i = 0; i < payload->size(); i++)
385  // printf("%02X ", payload->at(i));
386  //printf("\n");
387  }
388 
389  switch (payload->at(2)){
390  case 0x47:
391  m_bmg47_resp = true;
392  break;
393  case 0x01:
394  m_bmg01_resp = true;
395  break;
396  case 0x4B:
397  m_bmg4B_resp = true;
398  break;
399  case 0x041: {
400  m_bmg41_resp = true;
401  if (payload->at(3) == 0x02) { // ASCII device_common_name
402  std::string device_common_name;
403  for (unsigned int i = 0; i < 32; i++) {
404  device_common_name += payload->at(i + 14);
405  }
406  device_common_name += '\0';
407  m_device_common_name = device_common_name;
408  }
409  break;
410  }
411  case 0x042: {
412  m_bmg42_resp = true;
413  unsigned char name[8];
414  for (unsigned int i = 0; i < 8; i++)
415  name[i] = payload->at(i + 15);
416 
417  memcpy( (void *)&NAME, name, 8);
418  // Extract the manufacturers code
419  int *f1 = (int *)&NAME;
420  int f1d = *f1;
421  m_manufacturers_code = f1d >> 21;
422  break;
423  }
424 
425  default:
426  break;
427 
428  }
429 }
430 
431 
432 static uint64_t PayloadToName(const std::vector<unsigned char> payload) {
433  uint64_t name;
434  memcpy(&name, reinterpret_cast<const void*>(payload.data()), sizeof(name));
435  return name;
436 }
437 
438 
439 void CommDriverN2KSerial::handle_N2K_SERIAL_RAW(
440  CommDriverN2KSerialEvent& event) {
441  auto p = event.GetPayload();
442 
443  std::vector<unsigned char>* payload = p.get();
444 
445  if (payload->at(0) == 0xA0) {
446  ProcessManagementPacket(payload);
447  return;
448  }
449 
450  // extract PGN
451  uint64_t pgn = 0;
452  unsigned char* c = (unsigned char*)&pgn;
453  *c++ = payload->at(3);
454  *c++ = payload->at(4);
455  *c++ = payload->at(5);
456  // memcpy(&v, &data[3], 1);
457  //printf(" %ld\n", pgn);
458 
459  auto name = PayloadToName(*payload);
460  auto msg = std::make_shared<const Nmea2000Msg>(pgn, *payload, GetAddress(name));
461  auto msg_all = std::make_shared<const Nmea2000Msg>(1, *payload, GetAddress(name));
462 
463  m_listener.Notify(std::move(msg));
464  m_listener.Notify(std::move(msg_all));
465 
466 #if 0 // Debug output
467  size_t packetLength = (size_t)payload->at(1);
468  size_t vector_length = payload->size();
469 
470 
471  //if(pgn > 120000)
472  {
473  printf("Payload Length: %ld\n", vector_length);
474 
475  printf("PGN: %ld\n", pgn);
476 
477  for(size_t i=0; i< vector_length ; i++){
478  printf("%02X ", payload->at(i));
479  }
480  printf("\n\n");
481  }
482 #endif
483 }
484 
485 int CommDriverN2KSerial::GetMfgCode(){
486  unsigned char request_name[] = { 0x42};
487  int ni = SendMgmtMsg( request_name, sizeof(request_name), 0x41, 2000, &m_bmg42_resp);
488  if (ni)
489  return ni; // Not responding, return error so upper retries.
490  m_got_mfg_code = true;
491  return 0;
492 }
493 
494 int CommDriverN2KSerial::SendMgmtMsg( unsigned char *string, size_t string_size,
495  unsigned char cmd_code,
496  int timeout_msec, bool *response_flag) {
497 #ifndef ANDROID
498  // Make a message
499  int byteSum = 0;
500  uint8_t CheckSum;
501  std::vector <unsigned char> msg;
502 
503  msg.push_back(ESCAPE);
504  msg.push_back(STARTOFTEXT);
505  msg.push_back(0xA1);
506  byteSum += 0xA1;
507  msg.push_back(string_size); // payload length
508  byteSum += string_size;
509 
510  for (unsigned int i=0; i < string_size; i++){
511  if (string[i] == ESCAPE)
512  msg.push_back(string[i]);
513  msg.push_back(string[i]);
514  byteSum += string[i];
515  }
516 
517  // checksum
518  byteSum %= 256;
519  CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
520  msg.push_back(CheckSum);
521 
522  msg.push_back(ESCAPE);
523  msg.push_back(ENDOFTEXT);
524 
525  // send it out
526 
527  if (response_flag)
528  *response_flag = false; // prime the response detector
529 
530  // Send the msg
531  bool bsent = false;
532  bool not_done = true;
533  int ntry_outer = 10;
534  while ( not_done ){
535  if( GetSecondaryThread() && IsSecThreadActive() ) {
536  int retry = 10;
537  while( retry ) {
538  if( GetSecondaryThread()->SetOutMsg( msg )){
539  bsent = true;
540  not_done = false;
541  break;
542  }
543  else
544  retry--;
545  }
546  }
547  else {
548  wxMilliSleep(100);
549  if (ntry_outer-- <= 0)
550  not_done = false;
551  }
552  }
553 
554  if (!bsent)
555  return 1;
556 
557  bool bOK = false;
558  if (timeout_msec) {
559  int timeout = timeout_msec;
560  while (timeout > 0) {
561  wxYieldIfNeeded();
562  wxMilliSleep(100);
563  if (response_flag){
564  if (*response_flag){
565  bOK = true;
566  break;
567  }
568  }
569  timeout -= 100;
570  }
571  }
572  else
573  bOK = true;
574 
575  if(!bOK){
576  //printf( "***Err-1\n");
577  return 1;
578  }
579  //else
580  //printf("***OK-1 %d\n", timeout);
581 #endif
582  return 0;
583 }
584 
585 /* Copied from canboat Project
586  * https://github.com/canboat/canboat
587  *
588  * The following startup command reverse engineered from Actisense NMEAreader.
589  * It instructs the NGT1 to clear its PGN message TX list, thus it starts
590  * sending all PGNs.
591  */
592 static unsigned char NGT_STARTUP_SEQ[] = {
593  0x11, /* msg byte 1, meaning ? */
594  0x02, /* msg byte 2, meaning ? */
595  0x00 /* msg byte 3, meaning ? */
596 };
597 
598 int CommDriverN2KSerial::SetTXPGN(int pgn) {
599 
600  // Try to detect Actisense NGT-xx, has Mfg_code == 273
601 // if (m_got_mfg_code) {
602 // if (m_manufacturers_code != 273)
603 // return 0; // Not Actisense, no error
604 // }
605 
606  SendMgmtMsg( NGT_STARTUP_SEQ, sizeof(NGT_STARTUP_SEQ), 0x11, 0, NULL);
607 
608 
609 #if 0
610  // Enable PGN message
611  unsigned char request_enable[] = { 0x47,
612  0x00, 0x00, 0x00, //pgn
613  0x00, 0x01,
614  0xFF, 0xFF, 0xFF, 0xFF};
615 
616  int PGN = 0;
617  unsigned char* c = (unsigned char*)&pgn;
618  request_enable[1] = c[0];
619  request_enable[2] = c[1];
620  request_enable[3] = c[2];
621 
622  int aa = SendMgmtMsg( request_enable, sizeof(request_enable), 0x47, 2000, &m_bmg47_resp);
623 // if (aa)
624 // return -1;
625 
626  // Commit message
627  unsigned char request_commit[] = { 0x01 };
628  int bb = SendMgmtMsg( request_commit, sizeof(request_commit), 0x01, 2000, &m_bmg01_resp);
629 // if (bb)
630 // return -2;
631 
632 
633  // Activate message
634  unsigned char request_activate[] = { 0x4B };
635  int cc = SendMgmtMsg( request_activate, sizeof(request_activate), 0x4B, 2000, &m_bmg4B_resp);
636 // if (cc)
637 // return -3;
638 #endif
639 
640 
641  return 0;
642 }
643 
644 #ifndef __ANDROID__
645 
652 // Commonly used raw format is actually inherited from an old paketizing format:
653 // <10><02><application data><CRC (1)><10><03>
654 
655 // Actisense application data, from NGT-1 to PC
656 // <data code=93><length (1)><priority (1)><PGN (3)><destination(1)><source
657 // (1)><time (4)><len (1)><data (len)>
658 
659 // As applied to a real application data element, after extraction from packet
660 // format: 93 13 02 01 F8 01 FF 01 76 C2 52 00 08 08 70 EB 14 E8 8E 52 D2 BB 10
661 
662 // length (1): 0x13
663 // priority (1); 0x02
664 // PGN (3): 0x01 0xF8 0x01
665 // destination(1): 0xFF
666 // source (1): 0x01
667 // time (4): 0x76 0xC2 0x52 0x00
668 // len (1): 0x08
669 // data (len): 08 70 EB 14 E8 8E 52 D2
670 // packet CRC: 0xBB
671 
672 #define DS_RX_BUFFER_SIZE 4096
673 
674 CommDriverN2KSerialThread::CommDriverN2KSerialThread(
675  CommDriverN2KSerial* Launcher, const wxString& PortName,
676  const wxString& strBaudRate) {
677  m_pParentDriver = Launcher; // This thread's immediate "parent"
678 
679  m_PortName = PortName;
680  m_FullPortName = _T("Serial:") + PortName;
681 
682  rx_buffer = new unsigned char[DS_RX_BUFFER_SIZE + 1];
683 
684  put_ptr = rx_buffer; // local circular queue
685  tak_ptr = rx_buffer;
686 
687  m_baud = 9600; // default
688  long lbaud;
689  if (strBaudRate.ToLong(&lbaud)) m_baud = (int)lbaud;
690 
691  Create();
692 }
693 
694 CommDriverN2KSerialThread::~CommDriverN2KSerialThread(void) {
695  delete[] rx_buffer;
696 }
697 
698 void CommDriverN2KSerialThread::OnExit(void) {}
699 
700 bool CommDriverN2KSerialThread::OpenComPortPhysical(const wxString& com_name,
701  int baud_rate) {
702  try {
703  m_serial.setPort(com_name.ToStdString());
704  m_serial.setBaudrate(baud_rate);
705  m_serial.open();
706  m_serial.setTimeout(250, 250, 0, 250, 0);
707  } catch (std::exception&) {
708  // std::cerr << "Unhandled Exception while opening serial port: " <<
709  // e.what() << std::endl;
710  }
711  return m_serial.isOpen();
712 }
713 
714 void CommDriverN2KSerialThread::CloseComPortPhysical() {
715  try {
716  m_serial.close();
717  } catch (std::exception&) {
718  // std::cerr << "Unhandled Exception while closing serial port: " <<
719  // e.what() << std::endl;
720  }
721 }
722 
723 void CommDriverN2KSerialThread::SetGatewayOperationMode(void) {
724 
725  // For YDNU-02 device
726  // From Device User Manual
727  // Set the mode to "N2K"
728  unsigned char config_string[] = { 0x10, 0x02, 0xA1, 0x03, 0x11,
729  0x02, 0x00, 0x49, 0x10, 0x03};
730  //std::vector<byte>writeBuffer {DLE,STX,NGT_TX_CMD,0x03,0x11,0x02,0x00,0x49, DLE,ETX};
731 
732  WriteComPortPhysical(config_string, 10);
733 
734 }
735 
736 
737 void CommDriverN2KSerialThread::ThreadMessage(const wxString& msg) {
738  // Signal the main program thread
739  // OCPN_ThreadMessageEvent event(wxEVT_OCPN_THREADMSG, 0);
740  // event.SetSString(std::string(msg.mb_str()));
741  // if (gFrame) gFrame->GetEventHandler()->AddPendingEvent(event);
742 }
743 
744 size_t CommDriverN2KSerialThread::WriteComPortPhysical(std::vector<unsigned char> msg) {
745  if (m_serial.isOpen()) {
746  ssize_t status = 0;
747 #if 0
748  printf("X ");
749  for (size_t i = 0; i < msg.size(); i++) printf("%02X ", msg[i]);
750  printf("\n");
751 #endif
752  try {
753  status = m_serial.write((uint8_t*)msg.data(), msg.size());
754  } catch (std::exception& e) {
755  WARNING_LOG << "Unhandled Exception while writing to serial port: "
756  << e.what();
757  return -1;
758  }
759  return status;
760  } else {
761  return -1;
762  }
763 }
764 
765 size_t CommDriverN2KSerialThread::WriteComPortPhysical(unsigned char *msg, size_t length) {
766  if (m_serial.isOpen()) {
767  ssize_t status;
768  try {
769  status = m_serial.write((uint8_t*)msg, length);
770  } catch (std::exception&) {
771 // std::cerr << "Unhandled Exception while writing to serial port: " <<
772 // e.what() << std::endl;
773  return -1;
774  }
775  return status;
776  } else {
777  return -1;
778  }
779 }
780 
781 bool CommDriverN2KSerialThread::SetOutMsg(const std::vector<unsigned char> &msg)
782 {
783  if(out_que.size() < OUT_QUEUE_LENGTH){
784  out_que.push(msg);
785  return true;
786  }
787  return false;
788 }
789 
790 #ifndef __WXMSW__
791 void* CommDriverN2KSerialThread::Entry() {
792  bool not_done = true;
793  bool nl_found = false;
794  wxString msg;
795  uint8_t rdata[2000];
796  circular_buffer<uint8_t> circle(DS_RX_BUFFER_SIZE);
797  int ib = 0;
798 
799  // Request the com port from the comm manager
800  if (!OpenComPortPhysical(m_PortName, m_baud)) {
801  wxString msg(_T("NMEA input device open failed: "));
802  msg.Append(m_PortName);
803  ThreadMessage(msg);
804  // goto thread_exit; // This means we will not be trying to connect = The
805  // device must be connected when the thread is created. Does not seem to be
806  // needed/what we want as the reconnection logic is able to pick it up
807  // whenever it actually appears (Of course given it appears with the
808  // expected device name).
809  }
810  else {
811  wxMilliSleep(100);
812  SetGatewayOperationMode();
813  }
814 
815 
816  m_pParentDriver->SetSecThreadActive(); // I am alive
817 
818  // The main loop
819  static size_t retries = 0;
820 
821  bool bInMsg = false;
822  bool bGotESC = false;
823  bool bGotSOT = false;
824 
825  while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
826  if (TestDestroy()) not_done = false; // smooth exit
827 
828  uint8_t next_byte = 0;
829  int newdata = 0;
830  if (m_serial.isOpen()) {
831  try {
832  newdata = m_serial.read(rdata, 1000);
833  } catch (std::exception& e) {
834  // std::cerr << "Serial read exception: " << e.what() << std::endl;
835  if (10 < retries++) {
836  // We timed out waiting for the next character 10 times, let's close
837  // the port so that the reconnection logic kicks in and tries to fix
838  // our connection.
839  CloseComPortPhysical();
840  retries = 0;
841  }
842  }
843  } else {
844  // Reconnection logic. Let's try to reopen the port while waiting longer
845  // every time (until we simply keep trying every 2.5 seconds)
846  // std::cerr << "Serial port seems closed." << std::endl;
847  wxMilliSleep(250 * retries);
848  CloseComPortPhysical();
849  if (OpenComPortPhysical(m_PortName, m_baud)){
850  SetGatewayOperationMode();
851  retries = 0;
852  }
853  else if (retries < 10)
854  retries++;
855  }
856 
857  if (newdata > 0) {
858  for (int i = 0; i < newdata; i++) {
859  circle.put(rdata[i]);
860  }
861  }
862 
863  while (!circle.empty()) {
864  if (ib >= DS_RX_BUFFER_SIZE)
865  ib = 0;
866  uint8_t next_byte = circle.get();
867 
868  if (bInMsg) {
869  if (bGotESC) {
870  if (ESCAPE == next_byte) {
871  rx_buffer[ib++] = next_byte;
872  bGotESC = false;
873  }
874  }
875 
876  if (bGotESC && (ENDOFTEXT == next_byte)) {
877  // Process packet
878  // Copy the message into a std::vector
879 
880  auto buffer = std::make_shared<std::vector<unsigned char>>(rx_buffer, rx_buffer + ib);
881  std::vector<unsigned char>* vec = buffer.get();
882 
883  ib = 0;
884  //tak_ptr = tptr;
885  bInMsg = false;
886  bGotESC = false;
887 
888 // printf("raw ");
889 // for (unsigned int i = 0; i < vec->size(); i++)
890 // printf("%02X ", vec->at(i));
891 // printf("\n");
892 
893  // Message is finished
894  // Send the captured raw data vector pointer to the thread's "parent"
895  // thereby releasing the thread for further data capture
896  CommDriverN2KSerialEvent Nevent(wxEVT_COMMDRIVER_N2K_SERIAL, 0);
897  Nevent.SetPayload(buffer);
898  m_pParentDriver->AddPendingEvent(Nevent);
899 
900 
901  } else {
902  bGotESC = (next_byte == ESCAPE);
903 
904  if (!bGotESC) {
905  rx_buffer[ib++] = next_byte;
906  }
907  }
908  }
909 
910  else {
911  if (STARTOFTEXT == next_byte) {
912  bGotSOT = false;
913  if (bGotESC) {
914  bGotSOT = true;
915  }
916  } else {
917  bGotESC = (next_byte == ESCAPE);
918  if (bGotSOT) {
919  bGotSOT = false;
920  bInMsg = true;
921 
922  rx_buffer[ib++] = next_byte;
923  }
924  }
925  }
926  } // if newdata > 0
927 
928  // Check for any pending output message
929 #if 1
930  bool b_qdata = !out_que.empty();
931 
932  while (b_qdata) {
933  // Take a copy of message
934  std::vector<unsigned char> qmsg = out_que.front();
935  out_que.pop();
936 
937  if (static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
938  10 < retries++) {
939  // We failed to write the port 10 times, let's close the port so that
940  // the reconnection logic kicks in and tries to fix our connection.
941  retries = 0;
942  CloseComPortPhysical();
943  }
944 
945  b_qdata = !out_que.empty();
946  } // while b_qdata
947 
948 #endif
949  } // while ((not_done)
950 
951  // thread_exit:
952  CloseComPortPhysical();
953  m_pParentDriver->SetSecThreadInActive(); // I am dead
954  m_pParentDriver->m_Thread_run_flag = -1;
955 
956  return 0;
957 }
958 
959 #else
960 void* CommDriverN2KSerialThread::Entry() {
961  bool not_done = true;
962  bool nl_found = false;
963  wxString msg;
964  circular_buffer<uint8_t> circle(DS_RX_BUFFER_SIZE);
965 
966  // Request the com port from the comm manager
967  if (!OpenComPortPhysical(m_PortName, m_baud)) {
968  wxString msg(_T("NMEA input device open failed: "));
969  msg.Append(m_PortName);
970  ThreadMessage(msg);
971  // goto thread_exit; // This means we will not be trying to connect = The
972  // device must be connected when the thread is created. Does not seem to be
973  // needed/what we want as the reconnection logic is able to pick it up
974  // whenever it actually appears (Of course given it appears with the
975  // expected device name).
976  }
977  else {
978  SetGatewayOperationMode();
979  }
980 
981  m_pParentDriver->SetSecThreadActive(); // I am alive
982 
983  // The main loop
984  static size_t retries = 0;
985 
986  bool bInMsg = false;
987  bool bGotESC = false;
988  bool bGotSOT = false;
989 
990  while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
991  if (TestDestroy()) not_done = false; // smooth exit
992 
993  uint8_t next_byte = 0;
994  int newdata = -1;
995  uint8_t rdata[2000];
996 
997  if (m_serial.isOpen()) {
998  try {
999  newdata = m_serial.read(rdata, 200);
1000  } catch (std::exception& e) {
1001  // std::cerr << "Serial read exception: " << e.what() << std::endl;
1002  if (10 < retries++) {
1003  // We timed out waiting for the next character 10 times, let's close
1004  // the port so that the reconnection logic kicks in and tries to fix
1005  // our connection.
1006  CloseComPortPhysical();
1007  retries = 0;
1008  }
1009  }
1010  } else {
1011  // Reconnection logic. Let's try to reopen the port while waiting longer
1012  // every time (until we simply keep trying every 2.5 seconds)
1013  // std::cerr << "Serial port seems closed." << std::endl;
1014  wxMilliSleep(250 * retries);
1015  CloseComPortPhysical();
1016  if (OpenComPortPhysical(m_PortName, m_baud)){
1017  SetGatewayOperationMode();
1018  retries = 0;
1019  }
1020  else if (retries < 10)
1021  retries++;
1022  }
1023 
1024  if (newdata > 0) {
1025  for (int i = 0; i < newdata; i++) {
1026  circle.put(rdata[i]);
1027  }
1028  }
1029 
1030  while (!circle.empty()) {
1031  uint8_t next_byte = circle.get();
1032 
1033  if (1) {
1034  if (bInMsg) {
1035  if (bGotESC) {
1036  if (ESCAPE == next_byte) {
1037  *put_ptr++ = next_byte;
1038  if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1039  put_ptr = rx_buffer;
1040  bGotESC = false;
1041  } else if ( ENDOFTEXT == next_byte ) {
1042  // Process packet
1043  // Copy the message into a std::vector
1044 
1045  auto buffer = std::make_shared<std::vector<unsigned char>>();
1046  std::vector<unsigned char>* vec = buffer.get();
1047 
1048  unsigned char* tptr;
1049  tptr = tak_ptr;
1050 
1051  while ((tptr != put_ptr)) {
1052  vec->push_back(*tptr++);
1053  if ((tptr - rx_buffer) > DS_RX_BUFFER_SIZE) tptr = rx_buffer;
1054  }
1055 
1056  tak_ptr = tptr;
1057  bInMsg = false;
1058  bGotESC = false;
1059 
1060  // Message is finished
1061  // Send the captured raw data vector pointer to the thread's
1062  // "parent"
1063  // thereby releasing the thread for further data capture
1064  CommDriverN2KSerialEvent Nevent(wxEVT_COMMDRIVER_N2K_SERIAL, 0);
1065  Nevent.SetPayload(buffer);
1066  m_pParentDriver->AddPendingEvent(Nevent);
1067  } else if (next_byte == STARTOFTEXT) {
1068  put_ptr = rx_buffer;
1069  bGotESC = false;
1070  } else {
1071  put_ptr = rx_buffer;
1072  bInMsg = false;
1073  bGotESC = false;
1074  }
1075 
1076  } else {
1077  bGotESC = (next_byte == ESCAPE);
1078 
1079  if (!bGotESC) {
1080  *put_ptr++ = next_byte;
1081  if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1082  put_ptr = rx_buffer;
1083  }
1084  }
1085  }
1086 
1087  else {
1088  if (STARTOFTEXT == next_byte) {
1089  bGotSOT = false;
1090  if (bGotESC) {
1091  bGotSOT = true;
1092  }
1093  } else {
1094  bGotESC = (next_byte == ESCAPE);
1095  if (bGotSOT) {
1096  bGotSOT = false;
1097  bInMsg = true;
1098 
1099  *put_ptr++ = next_byte;
1100  if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1101  put_ptr = rx_buffer;
1102  }
1103  }
1104  }
1105  } // if newdata > 0
1106  } // while
1107 
1108  // Check for any pending output message
1109  bool b_qdata = !out_que.empty();
1110 
1111  while (b_qdata) {
1112  // Take a copy of message
1113  std::vector<unsigned char> qmsg = out_que.front();
1114  out_que.pop();
1115 
1116  if (static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
1117  10 < retries++) {
1118  // We failed to write the port 10 times, let's close the port so that
1119  // the reconnection logic kicks in and tries to fix our connection.
1120  retries = 0;
1121  CloseComPortPhysical();
1122  }
1123 
1124  b_qdata = !out_que.empty();
1125  } // while b_qdata
1126  } // while ((not_done)
1127 
1128  // thread_exit:
1129  CloseComPortPhysical();
1130  m_pParentDriver->SetSecThreadInActive(); // I am dead
1131  m_pParentDriver->m_Thread_run_flag = -1;
1132 
1133  return 0;
1134 }
1135 
1136 #endif // wxmsw Entry()
1137 
1138 #endif // Android
1139 
1140 
1141 //*****************************************************************************
1142 // Actisense Format:
1143 // <10><02><93><length (1)><priority (1)><PGN (3)><destination (1)><source (1)><time (4)><len (1)><data (len)><CRC (1)><10><03>
1144 #define MaxActisenseMsgBuf 400
1145 #define MsgTypeN2kTX 0x94
1146 
1147 void AddByteEscapedToBuf(unsigned char byteToAdd, uint8_t &idx, unsigned char *buf, int &byteSum);
1148 
1149 std::vector<unsigned char> BufferToActisenseFormat( tN2kMsg &msg){
1150  unsigned long _PGN=msg.PGN;
1151  uint8_t msgIdx=0;
1152  int byteSum = 0;
1153  uint8_t CheckSum;
1154  unsigned char ActisenseMsgBuf[MaxActisenseMsgBuf];
1155 
1156 
1157  ActisenseMsgBuf[msgIdx++]=ESCAPE;
1158  ActisenseMsgBuf[msgIdx++]=STARTOFTEXT;
1159  AddByteEscapedToBuf(MsgTypeN2kTX,msgIdx,ActisenseMsgBuf,byteSum);
1160  AddByteEscapedToBuf(msg.DataLen+6,msgIdx,ActisenseMsgBuf,byteSum); //length does not include escaped chars
1161 
1162  AddByteEscapedToBuf(msg.Priority,msgIdx,ActisenseMsgBuf,byteSum);
1163  AddByteEscapedToBuf(_PGN & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _PGN>>=8;
1164  AddByteEscapedToBuf(_PGN & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _PGN>>=8;
1165  AddByteEscapedToBuf(_PGN & 0xff,msgIdx,ActisenseMsgBuf,byteSum);
1166  AddByteEscapedToBuf(msg.Destination,msgIdx,ActisenseMsgBuf,byteSum);
1167 
1168 #if 0
1169  // For TX through Actisense compatible gateway, we skip "source" byte and msg time fields
1170  // Source
1171  AddByteEscapedToBuf(msg.Source,msgIdx,ActisenseMsgBuf,byteSum);
1172  // Time
1173  int _MsgTime = 0;
1174  AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1175  AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1176  AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1177  AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum);
1178 
1179 #endif
1180 
1181  AddByteEscapedToBuf(msg.DataLen,msgIdx,ActisenseMsgBuf,byteSum);
1182 
1183  for (int i = 0; i < msg.DataLen; i++)
1184  AddByteEscapedToBuf(msg.Data[i],msgIdx,ActisenseMsgBuf,byteSum);
1185  byteSum %= 256;
1186 
1187  CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
1188  ActisenseMsgBuf[msgIdx++]=CheckSum;
1189  if (CheckSum==ESCAPE) ActisenseMsgBuf[msgIdx++]=CheckSum;
1190 
1191  ActisenseMsgBuf[msgIdx++] = ESCAPE;
1192  ActisenseMsgBuf[msgIdx++] = ENDOFTEXT;
1193 
1194  std::vector<unsigned char> rv;
1195  for (unsigned int i=0 ; i < msgIdx; i++)
1196  rv.push_back(ActisenseMsgBuf[i]);
1197 
1198  return rv;
1199 }
void Activate() override
Register driver and possibly do other post-ctor steps.
void Activate(DriverPtr driver)
Add driver to list of active drivers.
Interface implemented by transport layer and possible other parties like test code which should handl...
Definition: comm_driver.h:47
virtual void Notify(std::shared_ptr< const NavMsg > message)=0
Handle a received message.
wxDEFINE_EVENT(REST_IO_EVT, ObservedEvt)
Event from IO thread to main.
N2k uses CAN which defines the basic properties of messages.
Definition: comm_navmsg.h:59