OpenCPN Partial API docs
comm_drv_n2k_net.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: Implement comm_drv_n2k_net.h -- network nmea2K driver
5  * Author: David Register, Alec Leamas
6  *
7  ***************************************************************************
8  * Copyright (C) 2023 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 #ifdef __MINGW32__
27 #undef IPV6STRICT // mingw FTBS fix: missing struct ip_mreq
28 #include <ws2tcpip.h>
29 #include <windows.h>
30 #endif
31 
32 #ifdef __MSVC__
33 #include "winsock2.h"
34 #include <wx/msw/winundef.h>
35 #include <ws2tcpip.h>
36 #endif
37 
38 #include <wx/wxprec.h>
39 
40 #ifndef WX_PRECOMP
41 #include <wx/wx.h>
42 #endif // precompiled headers
43 
44 #include <wx/tokenzr.h>
45 #include <wx/datetime.h>
46 
47 #include <stdlib.h>
48 #include <math.h>
49 #include <time.h>
50 
51 #include "model/sys_events.h"
52 
53 #ifndef __WXMSW__
54 #include <arpa/inet.h>
55 #include <netinet/tcp.h>
56 #endif
57 
58 #include <vector>
59 #include <wx/socket.h>
60 #include <wx/log.h>
61 #include <wx/memory.h>
62 #include <wx/chartype.h>
63 #include <wx/wx.h>
64 #include <wx/sckaddr.h>
65 
66 
67 #include "model/comm_drv_n2k_net.h"
68 #include "model/comm_navmsg_bus.h"
69 #include "model/idents.h"
70 #include "model/comm_drv_registry.h"
71 
72 #define N_DOG_TIMEOUT 8
73 
74 static const int kNotFound = -1;
75 
76 class MrqContainer{
77 public:
78  struct ip_mreq m_mrq;
79  void SetMrqAddr(unsigned int addr) {
80  m_mrq.imr_multiaddr.s_addr = addr;
81  m_mrq.imr_interface.s_addr = INADDR_ANY;
82  }
83 };
84 
85 // circular_buffer implementation
86 
87 circular_buffer::circular_buffer(size_t size)
88  : buf_(std::unique_ptr<unsigned char[]>(new unsigned char[size])), max_size_(size) {}
89 
90 //void circular_buffer::reset()
91 //{}
92 
93 //size_t circular_buffer::capacity() const
94 //{}
95 
96 //size_t circular_buffer::size() const
97 //{}
98 
99 bool circular_buffer::empty() const {
100  // if head and tail are equal, we are empty
101  return (!full_ && (head_ == tail_));
102 }
103 
104 bool circular_buffer::full() const {
105  // If tail is ahead the head by 1, we are full
106  return full_;
107 }
108 
109 void circular_buffer::put(unsigned char item) {
110  std::lock_guard<std::mutex> lock(mutex_);
111  buf_[head_] = item;
112  if (full_) tail_ = (tail_ + 1) % max_size_;
113 
114  head_ = (head_ + 1) % max_size_;
115 
116  full_ = head_ == tail_;
117 }
118 
119 unsigned char circular_buffer::get() {
120  std::lock_guard<std::mutex> lock(mutex_);
121 
122  if (empty()) return 0;
123 
124  // Read data and advance the tail (we now have a free space)
125  auto val = buf_[tail_];
126  full_ = false;
127  tail_ = (tail_ + 1) % max_size_;
128 
129  return val;
130 }
131 
134  : priority('\0'), source('\0'), destination('\0'), pgn(-1) {};
135 
136 
137 
138 wxDEFINE_EVENT(wxEVT_COMMDRIVER_N2K_NET, CommDriverN2KNetEvent);
139 
141 wxDECLARE_EVENT(wxEVT_COMMDRIVER_N2K_NET, CommDriverN2KNetEvent);
142 
143 class CommDriverN2KNetEvent : public wxEvent {
144 public:
145  CommDriverN2KNetEvent(wxEventType commandType = wxEVT_NULL, int id = 0)
146  : wxEvent(id, commandType){};
148 
149  // accessors
150  void SetPayload(std::shared_ptr<std::vector<unsigned char>> data) {
151  m_payload = data;
152  }
153  std::shared_ptr<std::vector<unsigned char>> GetPayload() { return m_payload; }
154 
155  // required for sending with wxPostEvent()
156  wxEvent* Clone() const {
157  CommDriverN2KNetEvent* newevent = new CommDriverN2KNetEvent(*this);
158  newevent->m_payload = this->m_payload;
159  return newevent;
160  };
161 
162 private:
163  std::shared_ptr<std::vector<unsigned char>> m_payload;
164 };
165 
166 static uint64_t PayloadToName(const std::vector<unsigned char> payload) {
167  uint64_t name;
168  memcpy(&name, reinterpret_cast<const void*>(payload.data()), sizeof(name));
169  return name;
170 }
171 
172 //========================================================================
173 /* commdriverN2KNet implementation
174  * */
175 
176 #define TIMER_SOCKET_N2KNET 7339
177 
178 BEGIN_EVENT_TABLE(CommDriverN2KNet, wxEvtHandler)
179 EVT_TIMER(TIMER_SOCKET_N2KNET, CommDriverN2KNet::OnTimerSocket)
180 EVT_SOCKET(DS_SOCKET_ID, CommDriverN2KNet::OnSocketEvent)
181 EVT_SOCKET(DS_SERVERSOCKET_ID, CommDriverN2KNet::OnServerSocketEvent)
182 EVT_TIMER(TIMER_SOCKET_N2KNET + 1, CommDriverN2KNet::OnSocketReadWatchdogTimer)
183 END_EVENT_TABLE()
184 
185 // CommDriverN0183Net::CommDriverN0183Net() : CommDriverN0183() {}
186 
188  DriverListener& listener)
189  : CommDriverN2K(((ConnectionParams*)params)->GetStrippedDSPort()),
190  m_params(*params),
191  m_listener(listener),
192  m_net_port(wxString::Format("%i", params->NetworkPort)),
193  m_net_protocol(params->NetProtocol),
194  m_sock(NULL),
195  m_tsock(NULL),
196  m_socket_server(NULL),
197  m_is_multicast(false),
198  m_txenter(0),
199  m_portstring(params->GetDSPort()),
200  m_io_select(params->IOSelect),
201  m_connection_type(params->Type),
202  m_bok(false)
203 
204 {
205  m_addr.Hostname(params->NetworkAddress);
206  m_addr.Service(params->NetworkPort);
207 
208  m_socket_timer.SetOwner(this, TIMER_SOCKET_N2KNET);
209  m_socketread_watchdog_timer.SetOwner(this, TIMER_SOCKET_N2KNET + 1);
210  this->attributes["netAddress"] = params->NetworkAddress.ToStdString();
211  char port_char[10];
212  sprintf(port_char, "%d",params->NetworkPort);
213  this->attributes["netPort"] = std::string(port_char);
214  this->attributes["userComment"] = params->UserComment.ToStdString();
215  this->attributes["ioDirection"] = std::string("IN/OUT");
216 
217  // Prepare the wxEventHandler to accept events from the actual hardware thread
218  Bind(wxEVT_COMMDRIVER_N2K_NET, &CommDriverN2KNet::handle_N2K_MSG, this);
219 
220  m_mrq_container = new MrqContainer;
221  m_ib = 0;
222  m_bInMsg = false;
223  m_bGotESC = false;
224  m_bGotSOT = false;
225  rx_buffer = new unsigned char[RX_BUFFER_SIZE_NET + 1];
226  m_circle = new circular_buffer(RX_BUFFER_SIZE_NET);
227 
228  fast_messages = new FastMessageMap();
229  m_order = 0; // initialize the fast message order bits, for TX
230  m_n2k_format = N2KFormat_YD_RAW;
231 
232  // Establish the power events response
233  resume_listener.Init(SystemEvents::GetInstance().evt_resume,
234  [&](ObservedEvt&) { HandleResume(); });
235 
236  Open();
237 }
238 
239 CommDriverN2KNet::~CommDriverN2KNet() {
240  delete m_mrq_container;
241  delete[] rx_buffer;
242  delete m_circle;
243 
244  Close();
245 }
246 
247 typedef struct {
248  std::string Model_ID;
249  char RT_flag;
250 } product_info;
251 
252 std::unordered_map<uint8_t, product_info> prod_info_map;
253 
254 bool CommDriverN2KNet::HandleMgntMsg(uint64_t pgn, std::vector<unsigned char> &payload){
255  // Process a few N2K network management messages
256  auto name = PayloadToName(payload);
257  auto msg = std::make_shared<const Nmea2000Msg>(pgn, payload, GetAddress(name));
258 
259  bool b_handled = false;
260  switch(pgn){
261  case 126996: { // Product information
262  uint8_t src_addr = payload.at(7);
263  if(src_addr == 75) return false; // skip simulator mgnt messages
264  product_info pr_info;
265  pr_info.Model_ID = std::string((char *) &payload.data()[17], 32);
266  pr_info.RT_flag = m_TX_flag;
267 
268  prod_info_map[src_addr] = pr_info;
269  b_handled = true;
270  break;
271  }
272  case 59904: { // ISO request
273  uint8_t src_addr = payload.at(7);
274  b_handled = true;
275  break;
276  }
277 
278  default:
279  break;
280  }
281  return b_handled;
282 }
283 
284 void CommDriverN2KNet::handle_N2K_MSG(CommDriverN2KNetEvent& event) {
285  auto p = event.GetPayload();
286  std::vector<unsigned char>* payload = p.get();
287 
288  // extract PGN
289  uint64_t pgn = 0;
290  unsigned char* c = (unsigned char*)&pgn;
291  *c++ = payload->at(3);
292  *c++ = payload->at(4);
293  *c++ = payload->at(5);
294  // memcpy(&v, &data[3], 1);
295  //printf(" %ld\n", pgn);
296 
297  auto name = PayloadToName(*payload);
298  auto msg = std::make_shared<const Nmea2000Msg>(pgn, *payload, GetAddress(name));
299  auto msg_all = std::make_shared<const Nmea2000Msg>(1, *payload, GetAddress(name));
300 
301  m_listener.Notify(std::move(msg));
302  m_listener.Notify(std::move(msg_all));
303 }
304 
306  CommDriverRegistry::GetInstance().Activate(shared_from_this());
307  // TODO: Read input data.
308 }
309 
310 void CommDriverN2KNet::Open(void) {
311 #ifdef __UNIX__
312 #if wxCHECK_VERSION(3, 0, 0)
313  in_addr_t addr =
314  ((struct sockaddr_in*)GetAddr().GetAddressData())->sin_addr.s_addr;
315 #else
316  in_addr_t addr =
317  ((struct sockaddr_in*)GetAddr().GetAddress()->m_addr)->sin_addr.s_addr;
318 #endif
319 #else
320  unsigned int addr = inet_addr(GetAddr().IPAddress().mb_str());
321 #endif
322  // Create the socket
323  switch (m_net_protocol) {
324  case TCP: {
325  OpenNetworkTCP(addr);
326  break;
327  }
328  case UDP: {
329  OpenNetworkUDP(addr);
330  break;
331  }
332  default:
333  break;
334  }
335  SetOk(true);
336 }
337 
338 void CommDriverN2KNet::OpenNetworkUDP(unsigned int addr) {
339  if (GetPortType() != DS_TYPE_OUTPUT) {
340  // We need a local (bindable) address to create the Datagram receive socket
341  // Set up the receive socket
342  wxIPV4address conn_addr;
343  conn_addr.Service(GetNetPort());
344  conn_addr.AnyAddress();
345  SetSock(
346  new wxDatagramSocket(conn_addr, wxSOCKET_NOWAIT | wxSOCKET_REUSEADDR));
347 
348  // Test if address is IPv4 multicast
349  if ((ntohl(addr) & 0xf0000000) == 0xe0000000) {
350  SetMulticast(true);
351  m_mrq_container->SetMrqAddr(addr);
352  GetSock()->SetOption(IPPROTO_IP, IP_ADD_MEMBERSHIP, &m_mrq_container->m_mrq,
353  sizeof(m_mrq_container->m_mrq));
354  }
355 
356  GetSock()->SetEventHandler(*this, DS_SOCKET_ID);
357 
358  GetSock()->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG |
359  wxSOCKET_LOST_FLAG);
360  GetSock()->Notify(TRUE);
361  GetSock()->SetTimeout(1); // Short timeout
362  }
363 
364  // Set up another socket for transmit
365  if (GetPortType() != DS_TYPE_INPUT) {
366  wxIPV4address tconn_addr;
367  tconn_addr.Service(0); // use ephemeral out port
368  tconn_addr.AnyAddress();
369  SetTSock(
370  new wxDatagramSocket(tconn_addr, wxSOCKET_NOWAIT | wxSOCKET_REUSEADDR));
371  // Here would be the place to disable multicast loopback
372  // but for consistency with broadcast behaviour, we will
373  // instead rely on setting priority levels to ignore
374  // sentences read back that have just been transmitted
375  if ((!GetMulticast()) && (GetAddr().IPAddress().EndsWith(_T("255")))) {
376  int broadcastEnable = 1;
377  bool bam = GetTSock()->SetOption(
378  SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
379  }
380  }
381 
382  // In case the connection is lost before acquired....
383  SetConnectTime(wxDateTime::Now());
384 }
385 
386 void CommDriverN2KNet::OpenNetworkTCP(unsigned int addr) {
387  int isServer = ((addr == INADDR_ANY) ? 1 : 0);
388  wxLogMessage(wxString::Format(_T("Opening TCP Server %d"), isServer));
389 
390  if (isServer) {
391  SetSockServer(new wxSocketServer(GetAddr(), wxSOCKET_REUSEADDR));
392  } else {
393  SetSock(new wxSocketClient());
394  }
395 
396  if (isServer) {
397  GetSockServer()->SetEventHandler(*this, DS_SERVERSOCKET_ID);
398  GetSockServer()->SetNotify(wxSOCKET_CONNECTION_FLAG);
399  GetSockServer()->Notify(TRUE);
400  GetSockServer()->SetTimeout(1); // Short timeout
401  } else {
402  GetSock()->SetEventHandler(*this, DS_SOCKET_ID);
403  int notify_flags = (wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG);
404  if (GetPortType() != DS_TYPE_INPUT) notify_flags |= wxSOCKET_OUTPUT_FLAG;
405  if (GetPortType() != DS_TYPE_OUTPUT) notify_flags |= wxSOCKET_INPUT_FLAG;
406  GetSock()->SetNotify(notify_flags);
407  GetSock()->Notify(TRUE);
408  GetSock()->SetTimeout(1); // Short timeout
409 
410  SetBrxConnectEvent(false);
411  GetSocketTimer()->Start(100, wxTIMER_ONE_SHOT); // schedule a connection
412  }
413 
414  // In case the connection is lost before acquired....
415  SetConnectTime(wxDateTime::Now());
416 }
417 
418 
419 void CommDriverN2KNet::OnSocketReadWatchdogTimer(wxTimerEvent& event) {
420  m_dog_value--;
421 
422  if (m_dog_value <= 0) { // No receive in n seconds
423  if (GetParams().NoDataReconnect) {
424  // Reconnect on NO DATA is true, so try to reconnect now.
425  if (GetProtocol() == TCP) {
426  wxSocketClient* tcp_socket = dynamic_cast<wxSocketClient*>(GetSock());
427  if (tcp_socket)
428  tcp_socket->Close();
429 
430  int n_reconnect_delay = wxMax(N_DOG_TIMEOUT - 2, 2);
431  wxLogMessage(wxString::Format(" Reconnection scheduled in %d seconds.",
432  n_reconnect_delay));
433  GetSocketTimer()->Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT);
434 
435  // Stop DATA watchdog, will be restarted on successful connection.
436  GetSocketThreadWatchdogTimer()->Stop();
437  }
438  }
439  }
440 }
441 
442 void CommDriverN2KNet::OnTimerSocket() {
443  // Attempt a connection
444  wxSocketClient* tcp_socket = dynamic_cast<wxSocketClient*>(GetSock());
445  if (tcp_socket) {
446  if (tcp_socket->IsDisconnected()) {
447  wxLogDebug(" Attempting reconnection...");
448  SetBrxConnectEvent(false);
449  // Stop DATA watchdog, may be restarted on successful connection.
450  GetSocketThreadWatchdogTimer()->Stop();
451  tcp_socket->Connect(GetAddr(), FALSE);
452 
453  // schedule another connection attempt, in case this one fails
454  int n_reconnect_delay = N_DOG_TIMEOUT;
455  GetSocketTimer()->Start(n_reconnect_delay * 1000,
456  wxTIMER_ONE_SHOT);
457  }
458  }
459 }
460 
461 
462 void CommDriverN2KNet::HandleResume() {
463 
464  // Attempt a stop and restart of connection
465  wxSocketClient* tcp_socket = dynamic_cast<wxSocketClient*>(GetSock());
466  if (tcp_socket) {
467  GetSocketThreadWatchdogTimer()->Stop();
468 
469  tcp_socket->Close();
470 
471  // schedule reconnect attempt
472  int n_reconnect_delay = wxMax(N_DOG_TIMEOUT-2, 2);
473  wxLogMessage(wxString::Format(" Reconnection scheduled in %d seconds.", n_reconnect_delay));
474 
475  GetSocketTimer()->Start(n_reconnect_delay * 1000,
476  wxTIMER_ONE_SHOT);
477  }
478 }
479 
480 bool CommDriverN2KNet::SendMessage(std::shared_ptr<const NavMsg> msg,
481  std::shared_ptr<const NavAddr> addr) {
482  auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
483  auto dest_addr_n2k = std::static_pointer_cast<const NavAddr2000>(addr);
484  return SendN2KNetwork(msg_n2k, dest_addr_n2k);
485 }
486 
487 std::vector<unsigned char> CommDriverN2KNet::PushCompleteMsg(const CanHeader header,
488  int position,
489  const can_frame frame) {
490  std::vector<unsigned char> data;
491  data.push_back(0x93);
492  data.push_back(0x13);
493  data.push_back(header.priority);
494  data.push_back(header.pgn & 0xFF);
495  data.push_back((header.pgn >> 8) & 0xFF);
496  data.push_back((header.pgn >> 16) & 0xFF);
497  data.push_back(header.destination);
498  data.push_back(header.source);
499  data.push_back(0xFF); // FIXME (dave) generate the time fields
500  data.push_back(0xFF);
501  data.push_back(0xFF);
502  data.push_back(0xFF);
503  data.push_back(CAN_MAX_DLEN); // nominally 8
504  for (size_t n = 0; n < CAN_MAX_DLEN; n++) data.push_back(frame.data[n]);
505  data.push_back(0x55); // CRC dummy, not checked
506  return data;
507 }
508 
509 std::vector<unsigned char> CommDriverN2KNet::PushFastMsgFragment(const CanHeader& header,
510  int position) {
511  std::vector<unsigned char> data;
512  data.push_back(0x93);
513  data.push_back(fast_messages->entries[position].expected_length + 11);
514  data.push_back(header.priority);
515  data.push_back(header.pgn & 0xFF);
516  data.push_back((header.pgn >> 8) & 0xFF);
517  data.push_back((header.pgn >> 16) & 0xFF);
518  data.push_back(header.destination);
519  data.push_back(header.source);
520  data.push_back(0xFF); // FIXME (dave) Could generate the time fields
521  data.push_back(0xFF);
522  data.push_back(0xFF);
523  data.push_back(0xFF);
524  data.push_back(fast_messages->entries[position].expected_length);
525  for (size_t n = 0; n < fast_messages->entries[position].expected_length; n++)
526  data.push_back(fast_messages->entries[position].data[n]);
527  data.push_back(0x55); // CRC dummy
528  fast_messages->Remove(position);
529  return data;
530 }
531 
538 void CommDriverN2KNet::HandleCanFrameInput(can_frame frame) {
539  int position = -1;
540  bool ready = true;
541 
542  CanHeader header(frame);
543  if (header.IsFastMessage()) {
544  position = fast_messages->FindMatchingEntry(header, frame.data[0]);
545  if (position == kNotFound) {
546  // Not an existing fast message:
547  // If valid, create new entry and insert first frame
548  // First, sanity check the arriving frame.
549  // If it is not the first frame of a FastMessage, then discard it
550  // n.b. This should be considered a network error, or possibly a gateway
551  // error. Maybe as simple as a dropped starting frame....
552  if ((frame.data[0] & 0x1F) == 0) {
553  position = fast_messages->AddNewEntry();
554  ready = fast_messages->InsertEntry(header, frame.data, position);
555  }
556  else
557  ready = false;
558  } else {
559  // An existing fast message entry is present, append the frame
560  ready = fast_messages->AppendEntry(header, frame.data, position);
561  }
562  }
563  if (ready) {
564  std::vector<unsigned char> vec;
565  if (position >= 0) {
566  // Re-assembled fast message
567  vec = PushFastMsgFragment(header, position);
568  } else {
569  // Single frame message
570  vec = PushCompleteMsg(header, position, frame);
571  }
572 
573  // Intercept network management messages not used by OCPN navigation core.
574  if (HandleMgntMsg( header.pgn, vec))
575  return;
576 
577 
578  // Message is ready
579  CommDriverN2KNetEvent Nevent(wxEVT_COMMDRIVER_N2K_NET, 0);
580  auto payload = std::make_shared<std::vector<uint8_t> >(vec);
581  Nevent.SetPayload(payload);
582  AddPendingEvent(Nevent);
583 
584  }
585 }
586 
587 bool isASCII(std::vector<unsigned char> packet) {
588  for (unsigned char c : packet) {
589  if (!isascii(c)) return false;
590  }
591  return true;
592 }
593 
594 N2K_Format CommDriverN2KNet::DetectFormat(std::vector<unsigned char> packet) {
595 
596  // A simplistic attempt at identifying which of the various available
597  // on-wire (or air) formats being emitted by a configured
598  // Actisense N2k<->ethernet device.
599 
600  if (isASCII(packet)) {
601  std::string payload = std::string(packet.begin(), packet.end());
602  if (payload.find("$PCDIN") != std::string::npos) {
603  return N2KFormat_SeaSmart;
604  } else if (payload.find("$MXPGN") != std::string::npos) {
605  // TODO: Due to the weird fragmentation observed with default settings of the wi-fi part,
606  // the payload does not always start with or even contain `$MXPGN`. We now lose the later.
607  return N2KFormat_MiniPlex;
608  } else if (std::find(packet.begin(), packet.end(), ':') != packet.end()) {
609  return N2KFormat_Actisense_RAW_ASCII;
610  } else {
611  return N2KFormat_Actisense_N2K_ASCII;
612  }
613  }
614  else {
615  if (packet[2] == 0x95)
616  return N2KFormat_Actisense_RAW;
617  else if (packet[2] == 0xd0)
618  return N2KFormat_Actisense_N2K;
619  else if (packet[2] == 0x93)
620  return N2KFormat_Actisense_NGT;
621  }
622  return N2KFormat_Undefined;
623 }
624 
625 bool CommDriverN2KNet::ProcessActisense_N2K(std::vector<unsigned char> packet) {
626 
627  //1002 d0 1500ff0401f80900684c1b00a074eb14f89052d288 1003
628 
629  std::vector<unsigned char> data;
630  bool bInMsg = false;
631  bool bGotESC = false;
632  bool bGotSOT = false;
633 
634  while (!m_circle->empty()) {
635  uint8_t next_byte = m_circle->get();
636 
637  if (bInMsg) {
638  if (bGotESC) {
639  if ( next_byte == ESCAPE ) {
640  data.push_back(next_byte);
641  bGotESC = false;
642  } else if ( next_byte == ENDOFTEXT ) {
643  // Process packet
644  // first 3 bytes are: 1 byte for message type, 2 bytes for rest of message length
645  unsigned int msg_length = (uint32_t)data[1] + ((uint32_t)data[2]<<8);
646 
647  // As a sanity check, verify message length
648  if (msg_length == data.size()-1) {
649  uint8_t destination = data[3];
650  uint8_t source = data[4];
651 
652  uint8_t dprp = data[7];
653  uint8_t priority = (dprp >> 2) & 7; // priority bits are 3,4,5th bit
654  uint8_t rAndDP = dprp & 3; // data page + reserved is first 2 bits
655 
656  // PGN
657  uint8_t pduFormat = data[6]; // PF (PDU Format)
658  uint32_t pgn = (rAndDP << 16) + (pduFormat << 8);
659  if (pduFormat >=
660  240) // message is broadcast, PS contains group extension
661  pgn += data[5]; // +PS (PDU Specific)
662 
663  // Create the OCPN payload
664  std::vector<uint8_t> o_payload;
665  o_payload.push_back(0x93);
666  o_payload.push_back(0x13);
667  o_payload.push_back(priority); // priority;
668  o_payload.push_back(pgn & 0xFF);
669  o_payload.push_back((pgn >> 8) & 0xFF);
670  o_payload.push_back((pgn >> 16) & 0xFF);
671  o_payload.push_back(destination); // destination;
672  o_payload.push_back(source); // source);
673  o_payload.push_back(0xFF); // FIXME (dave) generate the time fields
674  o_payload.push_back(0xFF);
675  o_payload.push_back(0xFF);
676  o_payload.push_back(0xFF);
677  o_payload.push_back(data.size());
678 
679  // Data starts at offset 13
680  for (size_t n = 13; n < data.size() - 1; n++)
681  o_payload.push_back(data[n]);
682 
683  o_payload.push_back(0x55); // CRC dummy, not checked
684 
685  // Message is ready
686  CommDriverN2KNetEvent Nevent(wxEVT_COMMDRIVER_N2K_NET, 0);
687  auto n2k_payload = std::make_shared<std::vector<uint8_t>>(o_payload);
688  Nevent.SetPayload(n2k_payload);
689  AddPendingEvent(Nevent);
690  }
691 
692  // reset for next packet
693  bInMsg = false;
694  bGotESC = false;
695  data.clear();
696  } else if (next_byte == STARTOFTEXT) {
697  bGotESC = false;
698  data.clear();
699  } else {
700  data.clear();
701  bInMsg = false;
702  bGotESC = false;
703  }
704  } else {
705  bGotESC = (next_byte == ESCAPE);
706 
707  if (!bGotESC) {
708  data.push_back(next_byte);
709  }
710  }
711  }
712 
713  else {
714  if (STARTOFTEXT == next_byte) {
715  bGotSOT = false;
716  if (bGotESC) {
717  bGotSOT = true;
718  }
719  } else {
720  bGotESC = (next_byte == ESCAPE);
721  if (bGotSOT) {
722  bGotSOT = false;
723  bInMsg = true;
724 
725  data.push_back(next_byte);
726  }
727  }
728  }
729  } // while
730 
731  return true;
732 }
733 
734 bool CommDriverN2KNet::ProcessActisense_RAW(std::vector<unsigned char> packet) {
735  //1002 95 0e15870402f8094b fc e6 20 00 00 ff ff 6f 1003
736 
737  can_frame frame;
738 
739  std::vector<unsigned char> data;
740  bool bInMsg = false;
741  bool bGotESC = false;
742  bool bGotSOT = false;
743 
744  while (!m_circle->empty()) {
745  uint8_t next_byte = m_circle->get();
746 
747  if (bInMsg) {
748  if (bGotESC) {
749  if ( next_byte == ESCAPE ) {
750  data.push_back(next_byte);
751  bGotESC = false;
752  } else if ( next_byte == ENDOFTEXT ) {
753  // Process packet
754  // Create a can_frame, to assemble fast packets.
755 
756  // As a sanity check, verify message length
757  if (data.size() >= 8) {
758  size_t dLen = data[1];
759 
760  if (dLen+3 == data.size()) {
761 
762  // can_id
763  memcpy(&frame.can_id, &data.data()[4], 4);
764 
765  // data
766  memcpy(&frame.data, &data.data()[8], 8);
767 
768  HandleCanFrameInput(frame);
769 
770  // reset for next packet
771  bInMsg = false;
772  bGotESC = false;
773  data.clear();
774  }
775  }
776  } else if (next_byte == STARTOFTEXT) {
777  bGotESC = false;
778  data.clear();
779  } else {
780  data.clear();
781  bInMsg = false;
782  bGotESC = false;
783  }
784  } else {
785  bGotESC = (next_byte == ESCAPE);
786 
787  if (!bGotESC) {
788  data.push_back(next_byte);
789  }
790  }
791  }
792 
793  else {
794  if (STARTOFTEXT == next_byte) {
795  bGotSOT = false;
796  if (bGotESC) {
797  bGotSOT = true;
798  }
799  } else {
800  bGotESC = (next_byte == ESCAPE);
801  if (bGotSOT) {
802  bGotSOT = false;
803  bInMsg = true;
804 
805  data.push_back(next_byte);
806  }
807  }
808  }
809  } // while
810 
811  return true;
812 }
813 
814 bool CommDriverN2KNet::ProcessActisense_NGT(std::vector<unsigned char> packet) {
815  std::vector<unsigned char> data;
816  bool bInMsg = false;
817  bool bGotESC = false;
818  bool bGotSOT = false;
819 
820  while (!m_circle->empty()) {
821  uint8_t next_byte = m_circle->get();
822 
823  if (bInMsg) {
824  if (bGotESC) {
825  if ( next_byte == ESCAPE ) {
826  data.push_back(next_byte);
827  bGotESC = false;
828  } else if ( next_byte == ENDOFTEXT ) {
829  // Process packet
830  CommDriverN2KNetEvent Nevent(wxEVT_COMMDRIVER_N2K_NET, 0);
831  auto n2k_payload = std::make_shared<std::vector<uint8_t>>(data);
832  Nevent.SetPayload(n2k_payload);
833  AddPendingEvent(Nevent);
834 
835  // reset for next packet
836  bInMsg = false;
837  bGotESC = false;
838  data.clear();
839  } else if (next_byte == STARTOFTEXT) {
840  bGotESC = false;
841  data.clear();
842  } else {
843  data.clear();
844  bInMsg = false;
845  bGotESC = false;
846  }
847  } else {
848  bGotESC = (next_byte == ESCAPE);
849 
850  if (!bGotESC) {
851  data.push_back(next_byte);
852  }
853  }
854  }
855 
856  else {
857  if (STARTOFTEXT == next_byte) {
858  bGotSOT = false;
859  if (bGotESC) {
860  bGotSOT = true;
861  }
862  } else {
863  bGotESC = (next_byte == ESCAPE);
864  if (bGotSOT) {
865  bGotSOT = false;
866  bInMsg = true;
867 
868  data.push_back(next_byte);
869  }
870  }
871  }
872  } // while
873 
874  return true;
875 }
876 
877 bool CommDriverN2KNet::ProcessActisense_ASCII_RAW(std::vector<unsigned char> packet) {
878  can_frame frame;
879 
880  while (!m_circle->empty()) {
881  char b = m_circle->get();
882  if ((b != 0x0a) && (b != 0x0d)) {
883  m_sentence += b;
884  }
885  if (b == 0x0a) { // end of sentence
886 
887  // Extract a can_frame from ASCII stream
888  //printf("%s\n", m_sentence.c_str());
889 
890  wxString ss(m_sentence.c_str());
891  m_sentence.clear();
892  wxStringTokenizer tkz(ss, " ");
893 
894  // Discard first token
895  wxString token = tkz.GetNextToken(); // time stamp
896 
897  token = tkz.GetNextToken(); // R/T
898  // Record the R/T flag, for use in device detect logic
899  m_TX_flag = token[0];
900 
901  // can_id;
902  token = tkz.GetNextToken();
903  long canID;
904  token.ToLong(&canID, 16);
905  frame.can_id = canID;
906 
907  // 8 data bytes, if present, 0 otherwise
908  unsigned char bytes[8];
909  memset(bytes, 0, 8);
910  for (unsigned int i = 0; i < 8; i++) {
911  if (tkz.HasMoreTokens()) {
912  token = tkz.GetNextToken();
913  long tui;
914  token.ToLong(&tui, 16);
915  bytes[i] = (uint8_t)tui;
916  }
917  }
918  memcpy(&frame.data, bytes, 8);
919  HandleCanFrameInput(frame);
920  }
921  }
922  return true;
923 }
924 
925 bool CommDriverN2KNet::ProcessActisense_ASCII_N2K(std::vector<unsigned char> packet) {
926  // A001001.732 04FF6 1FA03 C8FBA80329026400
927  std::string sentence;
928 
929  while (!m_circle->empty()) {
930  char b = m_circle->get();
931  if ((b != 0x0a) && (b != 0x0d)) {
932  sentence += b;
933  }
934  if (b == 0x0a) { // end of sentence
935 
936  // Extract items
937  //printf("%s", sentence.c_str());
938 
939  wxString ss(sentence.c_str());
940  wxStringTokenizer tkz(ss, " ");
941  sentence.clear(); // for next while loop
942 
943  // skip timestamp
944  wxString time_header = tkz.GetNextToken();
945 
946  wxString sprio_addr = tkz.GetNextToken();
947  long prio_addr;
948  sprio_addr.ToLong(&prio_addr, 16);
949  uint8_t priority = (uint8_t)prio_addr & 0X0F;
950  uint8_t destination = (uint8_t)(prio_addr >> 4) & 0X0FF;
951  uint8_t source = (uint8_t)(prio_addr >> 12) & 0X0FF;
952 
953 
954  // PGN
955  wxString sPGN = tkz.GetNextToken();
956  unsigned long PGN;
957  sPGN.ToULong(&PGN, 16);
958  //printf(" PGN: %ld\n", PGN);
959 
960  // data field
961  wxString sdata = tkz.GetNextToken();
962  std::vector<uint8_t> data;
963  for (size_t i = 0; i < sdata.Length(); i += 2) {
964  long dv;
965  wxString stui = sdata.Mid(i, 2);
966  stui.ToLong(&dv, 16);
967  data.push_back((uint8_t)dv);
968  }
969 
970  // Create the OCPN payload
971  std::vector<uint8_t> o_payload;
972  o_payload.push_back(0x93);
973  o_payload.push_back(0x13);
974  o_payload.push_back(priority); //priority;
975  o_payload.push_back(PGN & 0xFF);
976  o_payload.push_back((PGN >> 8) & 0xFF);
977  o_payload.push_back((PGN >> 16) & 0xFF);
978  o_payload.push_back(destination); //destination;
979  o_payload.push_back(source); // header.source);
980  o_payload.push_back(0xFF); // FIXME (dave) generate the time fields
981  o_payload.push_back(0xFF);
982  o_payload.push_back(0xFF);
983  o_payload.push_back(0xFF);
984  o_payload.push_back(data.size());
985  for (size_t n = 0; n < data.size(); n++) o_payload.push_back(data[n]);
986  o_payload.push_back(0x55); // CRC dummy, not checked
987 
988  if (HandleMgntMsg(PGN, o_payload))
989  return false;
990 
991  // Message is ready
992  CommDriverN2KNetEvent Nevent(wxEVT_COMMDRIVER_N2K_NET, 0);
993  auto n2k_payload = std::make_shared<std::vector<uint8_t>>(o_payload);
994  Nevent.SetPayload(n2k_payload);
995  AddPendingEvent(Nevent);
996  }
997  }
998  return true;
999 }
1000 
1001 bool CommDriverN2KNet::ProcessSeaSmart(std::vector<unsigned char> packet) {
1002  while (!m_circle->empty()) {
1003  char b = m_circle->get();
1004  if ((b != 0x0a) && (b != 0x0d)) {
1005  m_sentence += b;
1006  }
1007  if (b == 0x0a) { // end of sentence
1008 
1009  // Extract a can_frame from ASCII stream
1010  //printf("%s\n", m_sentence.c_str());
1011 
1012  wxString ss(m_sentence.c_str());
1013  m_sentence.clear();
1014  wxStringTokenizer tkz(ss, ",");
1015 
1016  // Discard first token
1017  wxString token = tkz.GetNextToken(); // $PCDIN
1018  m_TX_flag = 'R';
1019 
1020  token = tkz.GetNextToken(); // PGN
1021  unsigned long PGN;
1022  token.ToULong(&PGN, 16);
1023 
1024  token = tkz.GetNextToken(); // Timestamp
1025  unsigned long timestamp;
1026  token.ToULong(&timestamp, 16);
1027 
1028  token = tkz.GetNextToken(); // Source ID
1029  unsigned long source;
1030  token.ToULong(&source, 16);
1031 
1032  token = tkz.GetNextToken(); // Payload + "*CRC_byte"
1033 
1034  wxStringTokenizer datatkz(token, "*");
1035  wxString data = datatkz.GetNextToken();
1036 
1037  // Create the OCPN payload
1038  std::vector<uint8_t> o_payload;
1039  o_payload.push_back(0x93);
1040  o_payload.push_back(0x13);
1041  o_payload.push_back(3); //priority hardcoded, missing in SeaSmart
1042  o_payload.push_back(PGN & 0xFF);
1043  o_payload.push_back((PGN >> 8) & 0xFF);
1044  o_payload.push_back((PGN >> 16) & 0xFF);
1045  o_payload.push_back(0xFF); //destination hardcoded, missing in SeaSmart
1046  o_payload.push_back((uint8_t)source); // header.source);
1047  o_payload.push_back(timestamp & 0xFF);
1048  o_payload.push_back((timestamp >> 8) & 0xFF);
1049  o_payload.push_back((timestamp >> 16) & 0xFF);
1050  o_payload.push_back((timestamp >> 24) & 0xFF);
1051  o_payload.push_back((uint8_t)data.Length()/2);
1052  for (size_t i = 0; i < data.Length(); i += 2) {
1053  unsigned long dv;
1054  wxString sbyte = data.Mid(i, 2);
1055  sbyte.ToULong(&dv, 16);
1056  o_payload.push_back((uint8_t)dv);
1057  }
1058  o_payload.push_back(0x55); // CRC dummy, not checked
1059 
1060  if (HandleMgntMsg(PGN, o_payload))
1061  return false;
1062 
1063  // Message is ready
1064  CommDriverN2KNetEvent Nevent(wxEVT_COMMDRIVER_N2K_NET, 0);
1065  auto n2k_payload = std::make_shared<std::vector<uint8_t>>(o_payload);
1066  Nevent.SetPayload(n2k_payload);
1067  AddPendingEvent(Nevent);
1068  }
1069  }
1070  return true;
1071 }
1072 
1073 bool CommDriverN2KNet::ProcessMiniPlex(std::vector<unsigned char> packet) {
1074  /*
1075  $MXPGN – NMEA 2000 PGN Data
1076  This sentence transports NMEA 2000/CAN frames in NMEA 0183 format. The MiniPlex-3 will transmit this
1077  sentence with Talker ID “MX”. When sent to the MiniPlex-3, the Talker ID is ignored unless a routing entry
1078  exists for this sentence.
1079 
1080  Format: $--PGN,pppppp,aaaa,c--c*hh<CR><LF>
1081 
1082  pppppp: PGN of the NMEA 2000/CAN frame, 3-byte hexadecimal number. If the PGN is non-global, the
1083  lowest byte contains the destination address.
1084  aaaa: Attribute Word, a 16-bit hexadecimal number. This word contains the priority, the DLC code and
1085  then source/destination address of the frame, formatted as shown below:
1086 
1087  15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
1088  ----------------------------------------------------------------
1089  | S | Priority | DLC | Address |
1090  ----------------------------------------------------------------
1091 
1092  S: Send bit. When an NMEA 2000/CAN frame is received, this bit is 0.
1093  To use the $MXPGN sentence to send an NMEA 2000/CAN frame, this bit must be 1.
1094  Priority: Frame priority. A value between 0 and 7, a lower value means higher priority.
1095  DLC: Data Length Code field, contains the size of the frame in bytes (1..8) or a Class 2
1096  Transmission ID (9..15).
1097  Address: Depending on the Send bit, this field contains the Source Address (S=0) or the
1098  Destination Address (S=1) of the frame.
1099  c--c: Data field of the NMEA 2000/CAN frame, organised as one large number in hexadecimal
1100  notation from MSB to LSB. This is in accordance with “NMEA 2000 Appendix D”, chapter D.1,
1101  “Data Placement within the CAN Frame”.
1102  The size of this field depends on the DLC value and can be 1 to 8 bytes (2 to 16 hexadecimal
1103  characters).
1104 
1105  NMEA 2000 Reception
1106 
1107  When the MiniPlex-3 converts an NMEA 2000/CAN frame into an $MXPGN sentence, the S bit in the
1108  Attribute field will be 0 and the Address field contains the source address of the frame. The destination
1109  address of the frame is either global or contained in the lower byte of the PGN, in accordance with the
1110  NMEA 2000/ISO specification.
1111 
1112  Notes:
1113 
1114  Multiple messages can be delivered in a single packet
1115  It is not guaranteed that the whole message will be delivered in a single packet, actually it is common
1116  that the last message is split "anywhere" and continues in the next packet.
1117 
1118  packet 1 payload
1119 
1120  "$MXPGN,01F119,3816,FFFAAF01A3FDE301*14\r\n
1121  $MXPGN,01F201,2816,C50E0A19A0001A40*66\r\n
1122  $MXPGN,01F201,2816,6B4C0039058D8A41*15\r\n
1123  $MXPGN,01F201,2816,FFFFFFFFFF007542*1D\r\n
1124  $MXPGN,01F201,2816,FF7F7F0000000A43*6F\r\n
1125  $MXPGN,01F209,2816,2D002400ED0009A0*18\r\n
1126  $MXPGN,01F209,2816,FFFFFFFF002C00A1*10\r\n
1127  $MXPGN,01F213,6816,00B4F512020106C0*6E\r\n
1128  $MXPGN,01F214,6816,01FFFF7FFF04F801*12\r\n
1129  $MXPGN,01F214,6816,7EFFFF0009056400*65\r\n
1130  $MXPGN,"
1131 
1132  packet 2 payload
1133 
1134  "01F212,6816,185B560101010BC0*62\r\n
1135  $MXPGN,01F212,6816,FFFFFFFF00D700C1*1E\r\n
1136  $MXPGN,01FD06,5816,FF03F6749570C101*67\r\n
1137  $MXPGN,01FD07,5816,03F635B672F20401*1B\r\n"
1138 
1139  packet 1
1140 
1141  "$MXPGN,01F114,3816,FFFFF000D20212FF*1E\r\n
1142  $MXPGN,01F905,6816,0001000300005BC0*14\r\n
1143  $MXPGN,01F905,6816,6142010EE00007C1*67\r\n
1144  $MXPGN,01F905,6816,68206F74206B63C2*6F\r\n
1145  $MXPGN,01F905,6816,0D0001FF656D6FC3*16\r\n
1146  $MXPGN,01F905,6816,20747261745301C4*62\r\n
1147  $MXPGN,01F905,6816,4600746E696F70C5*6E\r\n
1148  $MXPGN,01F905,6816,020C84588023C3C6*6E\r\n
1149  $MXPGN,01F905,6816,6E727554011200C7*11\r\n
1150  $MXPGN,01F905,6816,65726F666562"
1151 
1152  packet 2 payload
1153 
1154  "20C8*1A\r\n
1155  $MXPGN,01F905,6816,CCA06B636F7220C9*1F\r\n
1156  $MXPGN,01F905,6816,030C85DF2023C4CA*1B\r\n
1157  $MXPGN,01F905,6816,656D6F48010600CB*19\r\n
1158  $MXPGN,01F905,6816,8765C023C65340CC*1B\r\n
1159  $MXPGN,01F905,6816,FFFFFFFFFFFF0CCD*66\r\n
1160  $MXPGN,01F10D,2816,FFFF0369FC97F901*16\r\n
1161  $MXPGN,01F112,2816,FD03C0FDF49B1A00*11\r\n
1162  $MXPGN,01F200,2816,FFFF7FFFFF43F800*10\r\n
1163  $MXPGN,01F205,2816,FF050D3A1D4CFC00*19\r\n"
1164  */
1165  while (!m_circle->empty()) {
1166  char b = m_circle->get();
1167  if ((b != 0x0a) && (b != 0x0d)) {
1168  m_sentence += b;
1169  }
1170  if (b == 0x0a) { // end of sentence
1171 
1172  // Extract a can_frame from ASCII stream
1173  //printf("%s\n", m_sentence.c_str());
1174 
1175  wxString ss(m_sentence.c_str());
1176  m_sentence.clear();
1177  wxStringTokenizer tkz(ss, ",");
1178 
1179  // Discard first token
1180  wxString token = tkz.GetNextToken(); // $MXPGN
1181  m_TX_flag = 'R';
1182 
1183  token = tkz.GetNextToken(); // PGN
1184  unsigned long PGN;
1185  token.ToULong(&PGN, 16);
1186 
1187  token = tkz.GetNextToken(); // Attribute compound field
1188  unsigned long attr;
1189  token.ToULong(&attr, 16);
1190  // Send Bit
1191  bool send_bit = (attr >> 15) != 0;
1192  // Priority
1193  uint8_t priority = (attr >> 12) & 0x07;
1194 
1195  // dlc
1196  uint8_t dlc = (attr >> 8) & 0x0F;
1197 
1198  //address
1199  uint8_t address = attr & 0xFF;
1200 
1201  token = tkz.GetNextToken(); // Payload + "*CRC_byte"
1202 
1203  wxStringTokenizer datatkz(token, "*");
1204  wxString data = datatkz.GetNextToken();
1205 
1206  if (data.Length() > 16) { // Payload can never exceed 8 bytes (=16 HEX characters)
1207  return false;
1208  }
1209 
1210  can_frame frame;
1211  memset(&frame.data, 0, 8);
1212  for (size_t i = 0; i < data.Length(); i += 2) {
1213  unsigned long dv;
1214  wxString sbyte = data.Mid(data.Length() - i - 2, 2);
1215  sbyte.ToULong(&dv, 16);
1216  frame.data[i/2] = ((uint8_t)dv);
1217  }
1218  frame.can_id = (uint32_t)BuildCanID(priority, address, 0xFF, PGN);
1219  HandleCanFrameInput(frame);
1220  }
1221  }
1222  return true;
1223 }
1224 
1225 void CommDriverN2KNet::OnSocketEvent(wxSocketEvent& event) {
1226 #define RD_BUF_SIZE \
1227  4096
1228  //can_frame frame;
1229 
1230  switch (event.GetSocketEvent()) {
1231  case wxSOCKET_INPUT: {
1232  // TODO determine if the follwing SetFlags needs to be done at every
1233  // socket event or only once when socket is created, it it needs to be
1234  // done at all!
1235  // m_sock->SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK); // was
1236  // (wxSOCKET_NOWAIT);
1237 
1238  // We use wxSOCKET_BLOCK to avoid Yield() reentrancy problems
1239  // if a long ProgressDialog is active, as in S57 SENC creation.
1240 
1241  // Disable input event notifications to preclude re-entrancy on
1242  // non-blocking socket
1243  // m_sock->SetNotify(wxSOCKET_LOST_FLAG);
1244 
1245  std::vector<unsigned char> data(RD_BUF_SIZE + 1);
1246  int newdata = 0;
1247  uint8_t next_byte = 0;
1248 
1249  event.GetSocket()->Read(&data.front(), RD_BUF_SIZE);
1250  if (!event.GetSocket()->Error()) {
1251  size_t count = event.GetSocket()->LastCount();
1252  if (count) {
1253  if (1 /*FIXME !g_benableUDPNullHeader*/) {
1254  data[count] = 0;
1255  newdata = count;
1256  } else {
1257  // XXX FIXME: is it reliable?
1258  }
1259  }
1260  }
1261 
1262  bool done = false;
1263  if (newdata > 0) {
1264  for (int i = 0; i < newdata; i++) {
1265  m_circle->put(data[i]);
1266  //printf("%c", data.at(i));
1267  }
1268  }
1269 
1270  m_n2k_format = DetectFormat(data);
1271 
1272  switch (m_n2k_format) {
1273  case N2KFormat_Actisense_RAW_ASCII:
1274  ProcessActisense_ASCII_RAW(data);
1275  break;
1276  case N2KFormat_YD_RAW: // RX Byte compatible with Actisense ASCII RAW
1277  ProcessActisense_ASCII_RAW(data);
1278  break;
1279  case N2KFormat_Actisense_N2K_ASCII:
1280  ProcessActisense_ASCII_N2K(data);
1281  break;
1282  case N2KFormat_Actisense_N2K:
1283  ProcessActisense_N2K(data);
1284  break;
1285  case N2KFormat_Actisense_RAW:
1286  ProcessActisense_RAW(data);
1287  break;
1288  case N2KFormat_Actisense_NGT:
1289  ProcessActisense_NGT(data);
1290  break;
1291  case N2KFormat_SeaSmart:
1292  ProcessSeaSmart(data);
1293  break;
1294  case N2KFormat_MiniPlex:
1295  ProcessMiniPlex(data);
1296  break;
1297  case N2KFormat_Undefined:
1298  default:
1299  break;
1300  }
1301  // Check for any pending output message
1302  } // case
1303 
1304  m_dog_value = N_DOG_TIMEOUT; // feed the dog
1305  break;
1306 #if 1
1307 
1308  case wxSOCKET_LOST: {
1309  if (GetProtocol() == TCP || GetProtocol() == GPSD) {
1310  if (GetBrxConnectEvent())
1311  wxLogMessage(wxString::Format(
1312  _T("NetworkDataStream connection lost: %s"), GetPort().c_str()));
1313  if (GetSockServer()) {
1314  GetSock()->Destroy();
1315  SetSock(NULL);
1316  break;
1317  }
1318  wxDateTime now = wxDateTime::Now();
1319  wxTimeSpan since_connect(
1320  0, 0, 10); // ten secs assumed, if connect time is uninitialized
1321  if (GetConnectTime().IsValid()) since_connect = now - GetConnectTime();
1322 
1323  int retry_time = 5000; // default
1324 
1325  // If the socket has never connected, and it is a short interval since
1326  // the connect request then stretch the time a bit. This happens on
1327  // Windows if there is no dafault IP on any interface
1328 
1329  if (!GetBrxConnectEvent() && (since_connect.GetSeconds() < 5))
1330  retry_time = 10000; // 10 secs
1331 
1332  GetSocketThreadWatchdogTimer()->Stop();
1333  GetSocketTimer()->Start(
1334  retry_time, wxTIMER_ONE_SHOT); // Schedule a re-connect attempt
1335  }
1336  break;
1337  }
1338 
1339  case wxSOCKET_CONNECTION: {
1340  if (GetProtocol() == GPSD) {
1341  // Sign up for watcher mode, Cooked NMEA
1342  // Note that SIRF devices will be converted by gpsd into
1343  // pseudo-NMEA
1344  char cmd[] = "?WATCH={\"class\":\"WATCH\", \"nmea\":true}";
1345  GetSock()->Write(cmd, strlen(cmd));
1346  } else if (GetProtocol() == TCP) {
1347  wxLogMessage(wxString::Format(
1348  _T("TCP NetworkDataStream connection established: %s"),
1349  GetPort().c_str()));
1350  m_dog_value = N_DOG_TIMEOUT; // feed the dog
1351  if (GetPortType() != DS_TYPE_OUTPUT) {
1353  if (GetParams().NoDataReconnect)
1354  GetSocketThreadWatchdogTimer()->Start(1000);
1355  }
1356  if (GetPortType() != DS_TYPE_INPUT && GetSock()->IsOk())
1357  (void)SetOutputSocketOptions(GetSock());
1358  GetSocketTimer()->Stop();
1359  SetBrxConnectEvent(true);
1360  }
1361 
1362  SetConnectTime(wxDateTime::Now());
1363  break;
1364  }
1365 #endif
1366  default:
1367  break;
1368  }
1369 }
1370 
1371 void CommDriverN2KNet::OnServerSocketEvent(wxSocketEvent& event) {
1372  switch (event.GetSocketEvent()) {
1373  case wxSOCKET_CONNECTION: {
1374  SetSock(GetSockServer()->Accept(false));
1375 
1376  if (GetSock()) {
1377  GetSock()->SetTimeout(2);
1378  // GetSock()->SetFlags(wxSOCKET_BLOCK);
1379  GetSock()->SetEventHandler(*this, DS_SOCKET_ID);
1380  int notify_flags = (wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG);
1381  if (GetPortType() != DS_TYPE_INPUT) {
1382  notify_flags |= wxSOCKET_OUTPUT_FLAG;
1383  (void)SetOutputSocketOptions(GetSock());
1384  }
1385  if (GetPortType() != DS_TYPE_OUTPUT)
1386  notify_flags |= wxSOCKET_INPUT_FLAG;
1387  GetSock()->SetNotify(notify_flags);
1388  GetSock()->Notify(true);
1389  }
1390 
1391  break;
1392  }
1393 
1394  default:
1395  break;
1396  }
1397 }
1398 
1399 std::vector<unsigned char> MakeSimpleOutMsg(
1400  int data_format, int pgn, std::vector<unsigned char>& payload) {
1401  std::vector<unsigned char> out_vec;
1402 
1403  switch (data_format) {
1404  case N2KFormat_YD_RAW:
1405  case N2KFormat_Actisense_RAW_ASCII: {
1406  // Craft the canID
1407  unsigned can_id = BuildCanID(6, 0xff, 0xff, pgn);
1408  wxString scan_id;
1409  scan_id.Printf("%08X", can_id);
1410  std::string sscan_id = scan_id.ToStdString();
1411  for (unsigned char s : sscan_id) out_vec.push_back(s);
1412  out_vec.push_back(' ');
1413 
1414  // Data payload
1415  std::string sspl;
1416  char tv[4];
1417  for (unsigned char d : payload) {
1418  snprintf(tv, 4, "%02X ", d);
1419  sspl += tv;
1420  }
1421  for (unsigned char s : sspl) out_vec.push_back(s);
1422 
1423  // terminate
1424  out_vec.push_back(0x0d);
1425  out_vec.push_back(0x0a);
1426  break;
1427  }
1428  case N2KFormat_Actisense_N2K_ASCII: {
1429  // Create the time field
1430  wxDateTime now = wxDateTime::Now();
1431  wxString stime = now.Format("%H%M%S");
1432  stime += ".000 ";
1433  std::string sstime = stime.ToStdString();
1434  out_vec.push_back('A');
1435  for (unsigned char s : sstime) out_vec.push_back(s);
1436 
1437  // src/dest/prio field
1438  wxString sdp;
1439  sdp.Printf("%02X%02X%1X ",
1440  1, // source
1441  (unsigned char)0xFF, 0x6);
1442  std::string ssdp = sdp.ToStdString();
1443  for (unsigned char s : ssdp) out_vec.push_back(s);
1444 
1445  // PGN field
1446  wxString spgn;
1447  spgn.Printf("%05X ", pgn);
1448  std::string sspgn = spgn.ToStdString();
1449  for (unsigned char s : sspgn) out_vec.push_back(s);
1450 
1451  // Data payload
1452  std::string sspl;
1453  char tv[3];
1454  for (unsigned char d : payload) {
1455  snprintf(tv, 3, "%02X", d);
1456  sspl += tv;
1457  }
1458  for (unsigned char s : sspl) out_vec.push_back(s);
1459 
1460  // terminator
1461  out_vec.push_back(0x0d);
1462  out_vec.push_back(0x0a);
1463  break;
1464  }
1465  case N2KFormat_MiniPlex: {
1466  out_vec.push_back('$');
1467  out_vec.push_back('M');
1468  out_vec.push_back('X');
1469  out_vec.push_back('P');
1470  out_vec.push_back('G');
1471  out_vec.push_back('N');
1472  out_vec.push_back(',');
1473  // PGN field
1474  wxString spgn;
1475  spgn.Printf("%06X,", pgn);
1476  std::string sspgn = spgn.ToStdString();
1477  for (unsigned char c : sspgn) {
1478  out_vec.push_back(c);
1479  }
1480  // Attribute word
1481  uint16_t attr = 0;
1482  uint8_t len = 8;
1483 
1484  attr |= ((uint16_t)0x06) << 12;
1485  attr |= ((uint16_t)payload.size()) << 8;
1486  attr |= (uint16_t)0xFF;
1487  attr |= 0x8000; // S bit set to 1
1488 
1489  wxString sattr;
1490  sattr.Printf("%04X,", attr);
1491  std::string ssattr = sattr.ToStdString();
1492  for (unsigned char c : ssattr) {
1493  out_vec.push_back(c);
1494  }
1495  // Data payload
1496  char tv[3];
1497  for (auto rit = payload.rbegin(); rit != payload.rend(); ++rit) {
1498  snprintf(tv, 3, "%02X", *rit);
1499  out_vec.push_back(tv[0]);
1500  out_vec.push_back(tv[1]);
1501  }
1502  // CRC
1503  uint8_t crc = 0;
1504  for (auto ci = ++out_vec.begin(); ci != out_vec.end(); ci++) {
1505  crc ^= *ci;
1506  }
1507  out_vec.push_back('*');
1508  snprintf(tv, 3, "%02X", crc);
1509  out_vec.push_back(tv[0]);
1510  out_vec.push_back(tv[1]);
1511 
1512  // term
1513  out_vec.push_back(0x0d);
1514  out_vec.push_back(0x0a);
1515  //DBG: std::cout << std::string(out_vec.begin(), out_vec.end()) << std::endl << std::flush;
1516  break;
1517  }
1518  default:
1519  break;
1520  }
1521  return out_vec;
1522 }
1523 
1524 std::vector<std::vector<unsigned char>> CommDriverN2KNet::GetTxVector(const std::shared_ptr<const Nmea2000Msg> &msg,
1525  std::shared_ptr<const NavAddr2000> dest_addr) {
1526  std::vector<std::vector<unsigned char>> tx_vector;
1527 
1528  // Branch based on detected network data format currently in use
1529  switch(m_n2k_format) {
1530  case N2KFormat_YD_RAW:
1531  break;
1532  case N2KFormat_Actisense_RAW_ASCII: {
1533  // 00:34:02.718 R 15FD0800 FF 00 01 CA 6F FF FF FF
1534  if (!IsFastMessagePGN(msg->PGN.pgn) && msg->payload.size() < 8) {
1535  // Single packet
1536  } else {
1537  std::vector<unsigned char> header_vec;
1538  std::vector<unsigned char> out_vec;
1539 
1540  // No Need to create a timestamp or frame R/T indicator
1541 #if 0
1542  // time header
1543  wxDateTime now = wxDateTime::Now();
1544  wxString stime = now.Format("%H:%M:%S");
1545  stime += ".000 ";
1546  std::string sstime = stime.ToStdString();
1547  for (unsigned char s : sstime) header_vec.push_back(s);
1548 
1549  // Tx indicator
1550  header_vec.push_back('T');
1551  header_vec.push_back(' ');
1552 #endif
1553 
1554  // Craft the canID
1555  // No need to specify the source address
1556  // The TX frame will adopt the gateway's claimed N2K address.
1557  unsigned long can_id =
1558  BuildCanID(msg->priority, 0, dest_addr->address, msg->PGN.pgn);
1559  wxString scan_id;
1560  scan_id.Printf("%08X", can_id);
1561  std::string sscan_id = scan_id.ToStdString();
1562  for (unsigned char s : sscan_id) header_vec.push_back(s);
1563  header_vec.push_back(' ');
1564 
1565  // format the required number of short packets, in a loop
1566  int payload_size = msg->payload.size();
1567  unsigned char temp[8]; // {0,0,0,0,0,0,0,0};
1568  int cur = 0;
1569  int nframes =
1570  (payload_size > 6 ? (payload_size - 6 - 1) / 7 + 1 + 1 : 1);
1571  bool result = true;
1572  for (int i = 0; i < nframes && result; i++) {
1573  temp[0] = i | m_order; //frame counter
1574  if (i == 0) {
1575  temp[1] = msg->payload.size(); // total bytes in fast packet
1576  // send the first 6 bytes
1577  for (int j = 2; j < 8; j++) {
1578  temp[j] = msg->payload.data()[cur];
1579  cur++;
1580  }
1581  } else {
1582  int j = 1;
1583  // send the next 7 data bytes
1584  for (; j < 8 && cur < payload_size; j++) {
1585  temp[j] = msg->payload.data()[cur];
1586  cur++;
1587  }
1588  for (; j < 8; j++) {
1589  temp[j] = 0xff;
1590  }
1591  }
1592 
1593  out_vec.clear();
1594 
1595  // constant header
1596  for (unsigned char s : header_vec) out_vec.push_back(s);
1597 
1598  // data, per packet
1599  std::string ssdata;
1600  for (unsigned int k = 0; k < 8; k++) {
1601  char tb[4];
1602  snprintf(tb, 4, "%02X ", temp[k]);
1603  ssdata += tb;
1604  }
1605  for (unsigned char s : ssdata) out_vec.push_back(s);
1606  out_vec.pop_back(); // drop the last space character
1607 
1608  out_vec.push_back(0x0d); // terminate the string
1609  out_vec.push_back(0x0a);
1610 
1611  //for (char s :out_vec)
1612  //printf( "%c", s);
1613 
1614  tx_vector.push_back(out_vec);
1615  } // for loop
1616  }
1617  }
1618  break;
1619  case N2KFormat_Actisense_N2K_ASCII: {
1620  // Source: Actisense own documentation `NMEA 2000 ASCII Output format.docx`
1621  //
1622  // Ahhmmss.ddd <SS><DD><P> <PPPPP> b0b1b2b3b4b5b6b7.....bn<CR><LF>
1623  // A = message is N2K or J1939 message
1624  // 173321.107 - time 17:33:21.107
1625  // <SS> - source address
1626  // <DD> - destination address
1627  // <P> - priority
1628  // <PPPPP> - PGN number
1629  // b0b1b2b3b4b5b6b7.....bn - data payload in hex. NB: ISO TP payload could be up to 1786 bytes
1630  //
1631  // Example: `A173321.107 23FF7 1F513 012F3070002F30709F\n`
1632  // 1 2 3 4
1633 
1634  std::vector<unsigned char> ovec;
1635 
1636  // Create the time field
1637  wxDateTime now = wxDateTime::Now();
1638  wxString stime = now.Format("%H%M%S");
1639  stime += ".000 ";
1640  std::string sstime = stime.ToStdString();
1641  ovec.push_back('A');
1642  for (unsigned char s : sstime) ovec.push_back(s);
1643 
1644  // src/dest/prio field
1645  wxString sdp;
1646  sdp.Printf( "%02X%02X%1X ",
1647  1, // source
1648  (unsigned char)dest_addr->address,
1649  (unsigned char)msg->priority);
1650  std::string ssdp = sdp.ToStdString();
1651  for (unsigned char s : ssdp) ovec.push_back(s);
1652 
1653  // PGN field
1654  wxString spgn;
1655  spgn.Printf("%05X ", (int)msg->PGN.pgn);
1656  std::string sspgn = spgn.ToStdString();
1657  for (unsigned char s : sspgn) ovec.push_back(s);
1658 
1659  // Data payload
1660  std::string sspl;
1661  char tv[3];
1662  for (unsigned char d : msg->payload){
1663  snprintf(tv, 3, "%02X", d);
1664  sspl += tv;
1665  }
1666  for (unsigned char s : sspl) ovec.push_back(s);
1667 
1668  // term
1669  ovec.push_back(0x0d);
1670  ovec.push_back(0x0a);
1671 
1672  //form the result
1673  tx_vector.push_back(ovec);
1674 
1675  break;
1676  }
1677  case N2KFormat_MiniPlex: {
1678  std::vector<unsigned char> ovec;
1679  if (!IsFastMessagePGN(msg->PGN.pgn) && msg->payload.size() < 8) {
1680  // Single packet
1681  } else {
1682  size_t cur = 0;
1683  size_t nframes = (msg->payload.size() > 6 ? (msg->payload.size() - 6 - 1) / 7 + 1 + 1 : 1);
1684  for (size_t i = 0; i < nframes; i++) {
1685  ovec.push_back('$');
1686  ovec.push_back('M');
1687  ovec.push_back('X');
1688  ovec.push_back('P');
1689  ovec.push_back('G');
1690  ovec.push_back('N');
1691  ovec.push_back(',');
1692  // PGN field
1693  wxString spgn;
1694  spgn.Printf("%06X,", (int)msg->PGN.pgn);
1695  std::string sspgn = spgn.ToStdString();
1696  for (unsigned char c : sspgn) {
1697  ovec.push_back(c);
1698  }
1699  // Attribute word
1700  uint16_t attr = 0;
1701  uint8_t len = 8;
1702  if (i == nframes - 1) {
1703  len = msg->payload.size() + 1 - 6 - (nframes - 2) * 7;
1704  }
1705  attr |= ((uint16_t)((uint8_t)msg->priority & 0x07)) << 12;
1706  attr |= ((uint16_t)len) << 8;
1707  attr |= (uint16_t)dest_addr->address;
1708  attr |= 0x8000; // S bit set to 1
1709 
1710  wxString sattr;
1711  sattr.Printf("%04X,", attr);
1712  std::string ssattr = sattr.ToStdString();
1713  for (unsigned char c : ssattr) {
1714  ovec.push_back(c);
1715  }
1716  // Data payload
1717  char tv[3];
1718  uint8_t databytes = i == 0 ? len - 2 : len - 1;
1719  std::vector<unsigned char> payload;
1720  for (uint8_t j = 0; j < databytes; j++) {
1721  payload.push_back(msg->payload[cur]);
1722  cur++;
1723  }
1724  for (auto rit = payload.rbegin(); rit != payload.rend(); ++rit) {
1725  snprintf(tv, 3, "%02X", *rit);
1726  ovec.push_back(tv[0]);
1727  ovec.push_back(tv[1]);
1728  }
1729  if (i == 0) { // First frame contains the total payload length
1730  snprintf(tv, 3, "%02X", (uint8_t)msg->payload.size());
1731  ovec.push_back(tv[0]);
1732  ovec.push_back(tv[1]);
1733  }
1734  //frame counter
1735  snprintf(tv, 3, "%02X", (uint8_t)i | m_order);
1736  ovec.push_back(tv[0]);
1737  ovec.push_back(tv[1]);
1738 
1739  // CRC
1740  uint8_t crc = 0;
1741  for (auto ci = ++ovec.begin(); ci != ovec.end(); ci++) {
1742  crc ^= *ci;
1743  }
1744  ovec.push_back('*');
1745  snprintf(tv, 3, "%02X", crc);
1746  ovec.push_back(tv[0]);
1747  ovec.push_back(tv[1]);
1748 
1749  // term
1750  ovec.push_back(0x0d);
1751  ovec.push_back(0x0a);
1752 
1753  //DBG: std::cout << std::string(ovec.begin(), ovec.end()) << std::endl << std::flush;
1754 
1755  //form the result
1756  tx_vector.push_back(ovec);
1757  ovec.clear();
1758  }
1759  m_order += 16;
1760  break;
1761  }
1762  }
1763  case N2KFormat_Actisense_N2K:
1764  break;
1765  case N2KFormat_Actisense_RAW:
1766  break;
1767  case N2KFormat_Actisense_NGT:
1768  break;
1769  case N2KFormat_SeaSmart:
1770  break;
1771  default:
1772  break;
1773  }
1774 
1775  m_order += 16; // update the fast message order bits
1776 
1777  return tx_vector;
1778 }
1779 
1780 bool CommDriverN2KNet::PrepareForTX() {
1781 
1782  // We need to determine several items before TX operations can commence.
1783  // 1. Is the gateway configured at my ip present, and if so, which of
1784  // the two supported gateways is it? (YDEN-type, or Actisense-type.
1785  // 2. If Actisense type, we need to infer the N2K source address it has
1786  // claimed, so that we can use that address for our TX operations.
1787 
1788  // BASIC ASSUMPTION: There is (or has been) enough network traffic to
1789  // allow occurate determination of data format currently in use
1790 
1791  bool b_found = false;
1792 
1793  // Step 1.1
1794  // If the detected data format is N2KFormat_Actisense_N2K_ASCII,
1795  // then we are clearly connected to an actisense device.
1796  // Nothing else need be done.
1797 
1798  if (m_n2k_format == N2KFormat_Actisense_N2K_ASCII)
1799  return true;
1800 
1801  // Step 1.2
1802  // If the detected data format is N2KFormat_MiniPlex,
1803  // then we are clearly connected to a MiniPlex.
1804  // Nothing else need be done.
1805 
1806  if (m_n2k_format == N2KFormat_MiniPlex)
1807  return true;
1808 
1809 
1810  // Step 1.2
1811  // If the detected data format is N2KFormat_SeaSmart,
1812  // then we can't transmit.
1813  if (m_n2k_format == N2KFormat_SeaSmart)
1814  return false;
1815 
1816  // Step 2
1817 
1818  // Assume that the gateway is YDEN type, RAW mode. Verify if true.
1819  // Logic: Actisense gateway will not respond to TX_FORMAT_YDEN,
1820  // so if we get sensible response, the gw must be YDEN type.
1821 
1822  prod_info_map.clear();
1823 
1824  // Send a broadcast request for PGN 126996, Product Information
1825  std::vector<unsigned char> payload;
1826  payload.push_back(0x14);
1827  payload.push_back(0xF0);
1828  payload.push_back(0x01);
1829 
1830  std::vector<std::vector<unsigned char>> out_data;
1831  std::vector<unsigned char> msg_vec = MakeSimpleOutMsg( N2KFormat_YD_RAW, 59904, payload);
1832  out_data.push_back(msg_vec);
1833  SendSentenceNetwork(out_data);
1834 
1835  // Wait some time, and study results
1836  wxMilliSleep(200);
1837  wxYield();
1838 
1839  // Check the results of the PGN 126996 capture
1840  for (const auto& [key, value] : prod_info_map){
1841  auto prod_info = value;
1842  if (prod_info.Model_ID.find("YDEN") != std::string::npos) {
1843  // Found a YDEN device
1844  // If this configured port is actually connector to YDEN,
1845  // then the device will have marked the received TCP packet
1846  // with "T" indicator. Check it.
1847  if (prod_info.RT_flag == 'T')
1848  b_found = true;
1849  break;
1850  }
1851  }
1852 
1853  if( b_found)
1854  return true;
1855 
1856  // No acceptable TX device found
1857  return false;
1858 }
1859 
1860 
1861 
1862 
1863 bool CommDriverN2KNet::SendN2KNetwork(std::shared_ptr<const Nmea2000Msg> &msg,
1864  std::shared_ptr<const NavAddr2000> addr) {
1865  PrepareForTX();
1866 
1867  std::vector<std::vector<unsigned char>> out_data = GetTxVector(msg, addr);
1868  SendSentenceNetwork(out_data);
1869  return true;
1870 };
1871 
1872 
1873 bool CommDriverN2KNet::SendSentenceNetwork(std::vector<std::vector<unsigned char>> payload) {
1874  if (m_txenter)
1875  return false; // do not allow recursion, could happen with non-blocking
1876  // sockets
1877  m_txenter++;
1878 
1879  bool ret = true;
1880  wxDatagramSocket* udp_socket;
1881  switch (GetProtocol()) {
1882  case TCP:
1883  for (std::vector<unsigned char> &v : payload ) {
1884  if (GetSock() && GetSock()->IsOk()) {
1885  //printf("---%s", v.data());
1886  GetSock()->Write(v.data(), v.size());
1887  if (GetSock()->Error()) {
1888  if (GetSockServer()) {
1889  GetSock()->Destroy();
1890  SetSock(NULL);
1891  } else {
1892  wxSocketClient* tcp_socket =
1893  dynamic_cast<wxSocketClient*>(GetSock());
1894  if (tcp_socket) tcp_socket->Close();
1895  if (!GetSocketTimer()->IsRunning())
1896  GetSocketTimer()->Start(
1897  5000, wxTIMER_ONE_SHOT); // schedule a reconnect
1898  GetSocketThreadWatchdogTimer()->Stop();
1899  }
1900  ret = false;
1901  }
1902  wxMilliSleep(200);
1903  } else
1904  ret = false;
1905  }
1906  break;
1907  case UDP:
1908 #if 0
1909  udp_socket = dynamic_cast<wxDatagramSocket*>(GetTSock());
1910  if (udp_socket && udp_socket->IsOk()) {
1911  udp_socket->SendTo(GetAddr(), payload.mb_str(), payload.size());
1912  if (udp_socket->Error()) ret = false;
1913  } else
1914  ret = false;
1915 #endif
1916  break;
1917 
1918  case GPSD:
1919  default:
1920  ret = false;
1921  break;
1922  }
1923  m_txenter--;
1924  return ret;
1925 }
1926 
1927 void CommDriverN2KNet::Close() {
1928  wxLogMessage(wxString::Format(_T("Closing NMEA NetworkDataStream %s"),
1929  GetNetPort().c_str()));
1930  // Kill off the TCP Socket if alive
1931  if (m_sock) {
1932  if (m_is_multicast)
1933  m_sock->SetOption(IPPROTO_IP, IP_DROP_MEMBERSHIP, &m_mrq_container->m_mrq,
1934  sizeof(m_mrq_container->m_mrq));
1935  m_sock->Notify(FALSE);
1936  m_sock->Destroy();
1937  }
1938 
1939  if (m_tsock) {
1940  m_tsock->Notify(FALSE);
1941  m_tsock->Destroy();
1942  }
1943 
1944  if (m_socket_server) {
1945  m_socket_server->Notify(FALSE);
1946  m_socket_server->Destroy();
1947  }
1948 
1949  m_socket_timer.Stop();
1950  m_socketread_watchdog_timer.Stop();
1951 }
1952 
1953 bool CommDriverN2KNet::SetOutputSocketOptions(wxSocketBase* tsock) {
1954  int ret;
1955 
1956  // Disable nagle algorithm on outgoing connection
1957  // Doing this here rather than after the accept() is
1958  // pointless on platforms where TCP_NODELAY is
1959  // not inherited. However, none of OpenCPN's currently
1960  // supported platforms fall into that category.
1961 
1962  int nagleDisable = 1;
1963  ret = tsock->SetOption(IPPROTO_TCP, TCP_NODELAY, &nagleDisable,
1964  sizeof(nagleDisable));
1965 
1966  // Drastically reduce the size of the socket output buffer
1967  // so that when client goes away without properly closing, the stream will
1968  // quickly fill the output buffer, and thus fail the write() call
1969  // within a few seconds.
1970  unsigned long outbuf_size = 1024; // Smallest allowable value on Linux
1971  return (tsock->SetOption(SOL_SOCKET, SO_SNDBUF, &outbuf_size,
1972  sizeof(outbuf_size)) &&
1973  ret);
1974 }
CAN v2.0 29 bit header as used by NMEA 2000.
Definition: comm_can_util.h:64
CanHeader()
CAN v2.0 29 bit header as used by NMEA 2000.
bool IsFastMessage() const
Return true if header reflects a multipart fast message.
void OnSocketEvent(wxSocketEvent &event)
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.
Track fast message fragments eventually forming complete messages.
Definition: comm_can_util.h:80
int AddNewEntry(void)
Allocate a new, fresh entry and return index to it.
void Remove(int pos)
Remove entry at pos.
bool AppendEntry(const CanHeader hdr, const unsigned char *data, int index)
Append fragment to existing multipart message.
int FindMatchingEntry(const CanHeader header, const unsigned char sid)
Setter.
bool InsertEntry(const CanHeader header, const unsigned char *data, int index)
Insert a new entry, first part of a multipart message.
Adds a std::shared<void> element to wxCommandEvent.
Definition: ocpn_plugin.h:1652
wxDEFINE_EVENT(REST_IO_EVT, ObservedEvt)
Event from IO thread to main.