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