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 (GetPortType() != DS_TYPE_OUTPUT) {
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}
389
390void CommDriverN2KNet::OnSocketReadWatchdogTimer(wxTimerEvent& event) {
391 m_dog_value--;
392
393 if (m_dog_value <= 0) { // No receive in n seconds
394 if (GetParams().NoDataReconnect) {
395 // Reconnect on NO DATA is true, so try to reconnect now.
396 if (GetProtocol() == TCP) {
397 auto* tcp_socket = dynamic_cast<wxSocketClient*>(GetSock());
398 if (tcp_socket) tcp_socket->Close();
399
400 int n_reconnect_delay = wxMax(N_DOG_TIMEOUT - 2, 2);
401 wxLogMessage(wxString::Format(" Reconnection scheduled in %d seconds.",
402 n_reconnect_delay));
403 GetSocketTimer()->Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT);
404
405 // Stop DATA watchdog, will be restarted on successful connection.
406 GetSocketThreadWatchdogTimer()->Stop();
407 }
408 }
409 }
410}
411
412void CommDriverN2KNet::OnTimerSocket() {
413 // Attempt a connection
414 auto* tcp_socket = dynamic_cast<wxSocketClient*>(GetSock());
415 if (tcp_socket) {
416 if (tcp_socket->IsDisconnected()) {
417 wxLogDebug(" Attempting reconnection...");
418 SetBrxConnectEvent(false);
419 // Stop DATA watchdog, may be restarted on successful connection.
420 GetSocketThreadWatchdogTimer()->Stop();
421 tcp_socket->Connect(GetAddr(), FALSE);
422
423 // schedule another connection attempt, in case this one fails
424 int n_reconnect_delay = N_DOG_TIMEOUT;
425 GetSocketTimer()->Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT);
426 }
427 }
428}
429
430void CommDriverN2KNet::HandleResume() {
431 // Attempt a stop and restart of connection
432 wxSocketClient* tcp_socket = dynamic_cast<wxSocketClient*>(GetSock());
433 if (tcp_socket) {
434 GetSocketThreadWatchdogTimer()->Stop();
435
436 tcp_socket->Close();
437
438 // schedule reconnect attempt
439 int n_reconnect_delay = wxMax(N_DOG_TIMEOUT - 2, 2);
440 wxLogMessage(wxString::Format(" Reconnection scheduled in %d seconds.",
441 n_reconnect_delay));
442
443 GetSocketTimer()->Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT);
444 }
445}
446
447bool CommDriverN2KNet::SendMessage(std::shared_ptr<const NavMsg> msg,
448 std::shared_ptr<const NavAddr> addr) {
449 if (!msg) return false;
450 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
451 auto dest_addr_n2k = std::static_pointer_cast<const NavAddr2000>(addr);
452 return SendN2KNetwork(msg_n2k, dest_addr_n2k);
453}
454
455std::vector<unsigned char> CommDriverN2KNet::PrepareLogPayload(
456 const std::shared_ptr<const Nmea2000Msg>& msg,
457 const std::shared_ptr<const NavAddr2000>& addr) {
458 std::vector<unsigned char> data;
459 data.push_back(0x94);
460 data.push_back(0x13);
461 data.push_back(msg->priority);
462 data.push_back(msg->PGN.pgn & 0xFF);
463 data.push_back((msg->PGN.pgn >> 8) & 0xFF);
464 data.push_back((msg->PGN.pgn >> 16) & 0xFF);
465 data.push_back(addr->address);
466 data.push_back(addr->address);
467 for (size_t n = 0; n < msg->payload.size(); n++)
468 data.push_back(msg->payload[n]);
469 data.push_back(0x55); // CRC dummy, not checked
470 return data;
471}
472
473std::vector<unsigned char> CommDriverN2KNet::PushCompleteMsg(
474 const CanHeader& header, int position, const can_frame& frame) {
475 std::vector<unsigned char> data;
476 data.push_back(0x93);
477 data.push_back(0x13);
478 data.push_back(header.priority);
479 data.push_back(header.pgn & 0xFF);
480 data.push_back((header.pgn >> 8) & 0xFF);
481 data.push_back((header.pgn >> 16) & 0xFF);
482 data.push_back(header.destination);
483 data.push_back(header.source);
484 data.push_back(0xFF); // FIXME (dave) generate the time fields
485 data.push_back(0xFF);
486 data.push_back(0xFF);
487 data.push_back(0xFF);
488 data.push_back(CAN_MAX_DLEN); // nominally 8
489 for (size_t n = 0; n < CAN_MAX_DLEN; n++) data.push_back(frame.data[n]);
490 data.push_back(0x55); // CRC dummy, not checked
491 return data;
492}
493
494std::vector<unsigned char> CommDriverN2KNet::PushFastMsgFragment(
495 const CanHeader& header, int position) {
496 std::vector<unsigned char> data;
497 data.push_back(0x93);
498 data.push_back(fast_messages->entries[position].expected_length + 11);
499 data.push_back(header.priority);
500 data.push_back(header.pgn & 0xFF);
501 data.push_back((header.pgn >> 8) & 0xFF);
502 data.push_back((header.pgn >> 16) & 0xFF);
503 data.push_back(header.destination);
504 data.push_back(header.source);
505 data.push_back(0xFF); // FIXME (dave) Could generate the time fields
506 data.push_back(0xFF);
507 data.push_back(0xFF);
508 data.push_back(0xFF);
509 data.push_back(fast_messages->entries[position].expected_length);
510 for (size_t n = 0; n < fast_messages->entries[position].expected_length; n++)
511 data.push_back(fast_messages->entries[position].data[n]);
512 data.push_back(0x55); // CRC dummy
513 fast_messages->Remove(position);
514 return data;
515}
516
523void CommDriverN2KNet::HandleCanFrameInput(const can_frame& frame) {
524 int position = -1;
525 bool ready = true;
526
527 CanHeader header(frame);
528 if (header.IsFastMessage()) {
529 position = fast_messages->FindMatchingEntry(header, frame.data[0]);
530 if (position == kNotFound) {
531 // Not an existing fast message:
532 // If valid, create new entry and insert first frame
533 // First, sanity check the arriving frame.
534 // If it is not the first frame of a FastMessage, then discard it
535 // n.b. This should be considered a network error, or possibly a gateway
536 // error. Maybe as simple as a dropped starting frame....
537 if ((frame.data[0] & 0x1F) == 0) {
538 position = fast_messages->AddNewEntry();
539 ready = fast_messages->InsertEntry(header, frame.data, position);
540 } else
541 ready = false;
542 } else {
543 // An existing fast message entry is present, append the frame
544 ready = fast_messages->AppendEntry(header, frame.data, position);
545 }
546 }
547 if (ready) {
548 std::vector<unsigned char> vec;
549 if (position >= 0) {
550 // Re-assembled fast message
551 vec = PushFastMsgFragment(header, position);
552 } else {
553 // Single frame message
554 vec = PushCompleteMsg(header, position, frame);
555 }
556
557 // Intercept network management messages not used by OCPN navigation core.
558 if (HandleMgntMsg(header.pgn, vec)) return;
559
560 // Message is ready
561 CommDriverN2KNetEvent Nevent(wxEVT_COMMDRIVER_N2K_NET, 0);
562 auto payload = std::make_shared<std::vector<uint8_t>>(vec);
563 Nevent.SetPayload(payload);
564 AddPendingEvent(Nevent);
565 }
566}
567
568static bool isASCII(const std::vector<unsigned char>& packet) {
569 for (unsigned char c : packet) {
570 if (!isascii(c)) return false;
571 }
572 return true;
573}
574
575N2K_Format CommDriverN2KNet::DetectFormat(
576 const std::vector<unsigned char>& packet) {
577 // A simplistic attempt at identifying which of the various available
578 // on-wire (or air) formats being emitted by a configured
579 // Actisense N2k<->ethernet device.
580
581 if (isASCII(packet)) {
582 std::string payload = std::string(packet.begin(), packet.end());
583 if (payload.find("$PCDIN") != std::string::npos) {
584 return N2KFormat_SeaSmart;
585 } else if (payload.find("$MXPGN") != std::string::npos) {
586 // TODO: Due to the weird fragmentation observed with default settings of
587 // the wi-fi part, the payload does not always start with or even contain
588 // `$MXPGN`. We now lose the later.
589 return N2KFormat_MiniPlex;
590 } else if (std::find(packet.begin(), packet.end(), ':') != packet.end()) {
591 return N2KFormat_Actisense_RAW_ASCII;
592 } else {
593 return N2KFormat_Actisense_N2K_ASCII;
594 }
595 } else {
596 if (packet[2] == 0x95)
597 return N2KFormat_Actisense_RAW;
598 else if (packet[2] == 0xd0)
599 return N2KFormat_Actisense_N2K;
600 else if (packet[2] == 0x93)
601 return N2KFormat_Actisense_NGT;
602 }
603 return N2KFormat_Undefined;
604}
605
606bool CommDriverN2KNet::ProcessActisense_N2K(
607 const std::vector<unsigned char>& packet) {
608 // 1002 d0 1500ff0401f80900684c1b00a074eb14f89052d288 1003
609
610 std::vector<unsigned char> data;
611 bool bInMsg = false;
612 bool bGotESC = false;
613 bool bGotSOT = false;
614
615 while (!m_circle.IsEmpty()) {
616 uint8_t next_byte = m_circle.Get();
617
618 if (bInMsg) {
619 if (bGotESC) {
620 if (next_byte == ESCAPE) {
621 data.push_back(next_byte);
622 bGotESC = false;
623 } else if (next_byte == ENDOFTEXT) {
624 // Process packet
625 // first 3 bytes are: 1 byte for message type, 2 bytes for rest of
626 // message length
627 unsigned int msg_length =
628 (uint32_t)data[1] + ((uint32_t)data[2] << 8);
629
630 // As a sanity check, verify message length
631 if (msg_length == data.size() - 1) {
632 uint8_t destination = data[3];
633 uint8_t source = data[4];
634
635 uint8_t dprp = data[7];
636 uint8_t priority =
637 (dprp >> 2) & 7; // priority bits are 3,4,5th bit
638 uint8_t rAndDP = dprp & 3; // data page + reserved is first 2 bits
639
640 // PGN
641 uint8_t pduFormat = data[6]; // PF (PDU Format)
642 uint32_t pgn = (rAndDP << 16) + (pduFormat << 8);
643 if (pduFormat >=
644 240) // message is broadcast, PS contains group extension
645 pgn += data[5]; // +PS (PDU Specific)
646
647 // Create the OCPN payload
648 std::vector<uint8_t> o_payload;
649 o_payload.push_back(0x93);
650 o_payload.push_back(0x13);
651 o_payload.push_back(priority); // priority;
652 o_payload.push_back(pgn & 0xFF);
653 o_payload.push_back((pgn >> 8) & 0xFF);
654 o_payload.push_back((pgn >> 16) & 0xFF);
655 o_payload.push_back(destination); // destination;
656 o_payload.push_back(source); // source);
657 o_payload.push_back(0xFF); // FIXME (dave) generate the time fields
658 o_payload.push_back(0xFF);
659 o_payload.push_back(0xFF);
660 o_payload.push_back(0xFF);
661 o_payload.push_back(data.size());
662
663 // Data starts at offset 13
664 for (size_t n = 13; n < data.size() - 1; n++)
665 o_payload.push_back(data[n]);
666
667 o_payload.push_back(0x55); // CRC dummy, not checked
668
669 // Message is ready
670 CommDriverN2KNetEvent Nevent(wxEVT_COMMDRIVER_N2K_NET, 0);
671 auto n2k_payload =
672 std::make_shared<std::vector<uint8_t>>(o_payload);
673 Nevent.SetPayload(n2k_payload);
674 AddPendingEvent(Nevent);
675 }
676
677 // reset for next packet
678 bInMsg = false;
679 bGotESC = false;
680 data.clear();
681 } else if (next_byte == STARTOFTEXT) {
682 bGotESC = false;
683 data.clear();
684 } else {
685 data.clear();
686 bInMsg = false;
687 bGotESC = false;
688 }
689 } else {
690 bGotESC = (next_byte == ESCAPE);
691
692 if (!bGotESC) {
693 data.push_back(next_byte);
694 }
695 }
696 }
697
698 else {
699 if (STARTOFTEXT == next_byte) {
700 bGotSOT = false;
701 if (bGotESC) {
702 bGotSOT = true;
703 }
704 } else {
705 bGotESC = (next_byte == ESCAPE);
706 if (bGotSOT) {
707 bGotSOT = false;
708 bInMsg = true;
709
710 data.push_back(next_byte);
711 }
712 }
713 }
714 } // while
715
716 return true;
717}
718
719bool CommDriverN2KNet::ProcessActisense_RAW(
720 const std::vector<unsigned char>& packet) {
721 // 1002 95 0e15870402f8094b fc e6 20 00 00 ff ff 6f 1003
722
723 can_frame frame;
724
725 std::vector<unsigned char> data;
726 bool bInMsg = false;
727 bool bGotESC = false;
728 bool bGotSOT = false;
729
730 while (!m_circle.IsEmpty()) {
731 uint8_t next_byte = m_circle.Get();
732
733 if (bInMsg) {
734 if (bGotESC) {
735 if (next_byte == ESCAPE) {
736 data.push_back(next_byte);
737 bGotESC = false;
738 } else if (next_byte == ENDOFTEXT) {
739 // Process packet
740 // Create a can_frame, to assemble fast packets.
741
742 // As a sanity check, verify message length
743 if (data.size() >= 8) {
744 size_t dLen = data[1];
745
746 if (dLen + 3 == data.size()) {
747 // can_id
748 memcpy(&frame.can_id, &data.data()[4], 4);
749
750 // data
751 memcpy(&frame.data, &data.data()[8], 8);
752
753 HandleCanFrameInput(frame);
754
755 // reset for next packet
756 bInMsg = false;
757 bGotESC = false;
758 data.clear();
759 }
760 }
761 } else if (next_byte == STARTOFTEXT) {
762 bGotESC = false;
763 data.clear();
764 } else {
765 data.clear();
766 bInMsg = false;
767 bGotESC = false;
768 }
769 } else {
770 bGotESC = (next_byte == ESCAPE);
771
772 if (!bGotESC) {
773 data.push_back(next_byte);
774 }
775 }
776 }
777
778 else {
779 if (STARTOFTEXT == next_byte) {
780 bGotSOT = false;
781 if (bGotESC) {
782 bGotSOT = true;
783 }
784 } else {
785 bGotESC = (next_byte == ESCAPE);
786 if (bGotSOT) {
787 bGotSOT = false;
788 bInMsg = true;
789
790 data.push_back(next_byte);
791 }
792 }
793 }
794 } // while
795
796 return true;
797}
798
799bool CommDriverN2KNet::ProcessActisense_NGT(
800 const std::vector<unsigned char>& packet) {
801 std::vector<unsigned char> data;
802 bool bInMsg = false;
803 bool bGotESC = false;
804 bool bGotSOT = false;
805
806 while (!m_circle.IsEmpty()) {
807 uint8_t next_byte = m_circle.Get();
808
809 if (bInMsg) {
810 if (bGotESC) {
811 if (next_byte == ESCAPE) {
812 data.push_back(next_byte);
813 bGotESC = false;
814 } else if (next_byte == ENDOFTEXT) {
815 // Process packet
816 CommDriverN2KNetEvent Nevent(wxEVT_COMMDRIVER_N2K_NET, 0);
817 auto n2k_payload = std::make_shared<std::vector<uint8_t>>(data);
818 Nevent.SetPayload(n2k_payload);
819 AddPendingEvent(Nevent);
820
821 // reset for next packet
822 bInMsg = false;
823 bGotESC = false;
824 data.clear();
825 } else if (next_byte == STARTOFTEXT) {
826 bGotESC = false;
827 data.clear();
828 } else {
829 data.clear();
830 bInMsg = false;
831 bGotESC = false;
832 }
833 } else {
834 bGotESC = (next_byte == ESCAPE);
835
836 if (!bGotESC) {
837 data.push_back(next_byte);
838 }
839 }
840 }
841
842 else {
843 if (STARTOFTEXT == next_byte) {
844 bGotSOT = false;
845 if (bGotESC) {
846 bGotSOT = true;
847 }
848 } else {
849 bGotESC = (next_byte == ESCAPE);
850 if (bGotSOT) {
851 bGotSOT = false;
852 bInMsg = true;
853
854 data.push_back(next_byte);
855 }
856 }
857 }
858 } // while
859
860 return true;
861}
862
863bool CommDriverN2KNet::ProcessActisense_ASCII_RAW(
864 const std::vector<unsigned char>& packet) {
865 can_frame frame;
866
867 while (!m_circle.IsEmpty()) {
868 char b = m_circle.Get();
869 if ((b != 0x0a) && (b != 0x0d)) {
870 m_sentence += b;
871 }
872 if (b == 0x0a) { // end of sentence
873
874 // Extract a can_frame from ASCII stream
875 // printf("%s\n", m_sentence.c_str());
876
877 wxString ss(m_sentence.c_str());
878 m_sentence.clear();
879 wxStringTokenizer tkz(ss, " ");
880
881 // Discard first token
882 wxString token = tkz.GetNextToken(); // time stamp
883
884 token = tkz.GetNextToken(); // R/T
885 // Record the R/T flag, for use in device detect logic
886 m_TX_flag = token[0];
887
888 // can_id;
889 token = tkz.GetNextToken();
890 long canID;
891 token.ToLong(&canID, 16);
892 frame.can_id = canID;
893
894 // 8 data bytes, if present, 0 otherwise
895 unsigned char bytes[8];
896 memset(bytes, 0, 8);
897 for (unsigned int i = 0; i < 8; i++) {
898 if (tkz.HasMoreTokens()) {
899 token = tkz.GetNextToken();
900 long tui;
901 token.ToLong(&tui, 16);
902 bytes[i] = (uint8_t)tui;
903 }
904 }
905 memcpy(&frame.data, bytes, 8);
906 HandleCanFrameInput(frame);
907 }
908 }
909 return true;
910}
911
912bool CommDriverN2KNet::ProcessActisense_ASCII_N2K(
913 const std::vector<unsigned char>& packet) {
914 // A001001.732 04FF6 1FA03 C8FBA80329026400
915 std::string sentence;
916
917 while (!m_circle.IsEmpty()) {
918 char b = m_circle.Get();
919 if ((b != 0x0a) && (b != 0x0d)) {
920 sentence += b;
921 }
922 if (b == 0x0a) { // end of sentence
923
924 // Extract items
925 // printf("%s", sentence.c_str());
926
927 wxString ss(sentence.c_str());
928 wxStringTokenizer tkz(ss, " ");
929 sentence.clear(); // for next while loop
930
931 // skip timestamp
932 wxString time_header = tkz.GetNextToken();
933
934 wxString sprio_addr = tkz.GetNextToken();
935 long prio_addr;
936 sprio_addr.ToLong(&prio_addr, 16);
937 uint8_t priority = (uint8_t)prio_addr & 0X0F;
938 uint8_t destination = (uint8_t)(prio_addr >> 4) & 0X0FF;
939 uint8_t source = (uint8_t)(prio_addr >> 12) & 0X0FF;
940
941 // PGN
942 wxString sPGN = tkz.GetNextToken();
943 unsigned long PGN;
944 sPGN.ToULong(&PGN, 16);
945 // printf(" PGN: %ld\n", PGN);
946
947 // data field
948 wxString sdata = tkz.GetNextToken();
949 std::vector<uint8_t> data;
950 for (size_t i = 0; i < sdata.Length(); i += 2) {
951 long dv;
952 wxString stui = sdata.Mid(i, 2);
953 stui.ToLong(&dv, 16);
954 data.push_back((uint8_t)dv);
955 }
956
957 // Create the OCPN payload
958 std::vector<uint8_t> o_payload;
959 o_payload.push_back(0x93);
960 o_payload.push_back(0x13);
961 o_payload.push_back(priority); // priority;
962 o_payload.push_back(PGN & 0xFF);
963 o_payload.push_back((PGN >> 8) & 0xFF);
964 o_payload.push_back((PGN >> 16) & 0xFF);
965 o_payload.push_back(destination); // destination;
966 o_payload.push_back(source); // header.source);
967 o_payload.push_back(0xFF); // FIXME (dave) generate the time fields
968 o_payload.push_back(0xFF);
969 o_payload.push_back(0xFF);
970 o_payload.push_back(0xFF);
971 o_payload.push_back(data.size());
972 for (size_t n = 0; n < data.size(); n++) o_payload.push_back(data[n]);
973 o_payload.push_back(0x55); // CRC dummy, not checked
974
975 if (HandleMgntMsg(PGN, o_payload)) return false;
976
977 // Message is ready
978 CommDriverN2KNetEvent Nevent(wxEVT_COMMDRIVER_N2K_NET, 0);
979 auto n2k_payload = std::make_shared<std::vector<uint8_t>>(o_payload);
980 Nevent.SetPayload(n2k_payload);
981 AddPendingEvent(Nevent);
982 }
983 }
984 return true;
985}
986
987bool CommDriverN2KNet::ProcessSeaSmart(
988 const std::vector<unsigned char>& packet) {
989 while (!m_circle.IsEmpty()) {
990 char b = m_circle.Get();
991 if ((b != 0x0a) && (b != 0x0d)) {
992 m_sentence += b;
993 }
994 if (b == 0x0a) { // end of sentence
995
996 // Extract a can_frame from ASCII stream
997 // printf("%s\n", m_sentence.c_str());
998
999 wxString ss(m_sentence.c_str());
1000 m_sentence.clear();
1001 wxStringTokenizer tkz(ss, ",");
1002
1003 // Discard first token
1004 wxString token = tkz.GetNextToken(); // $PCDIN
1005 m_TX_flag = 'R';
1006
1007 token = tkz.GetNextToken(); // PGN
1008 unsigned long PGN;
1009 token.ToULong(&PGN, 16);
1010
1011 token = tkz.GetNextToken(); // Timestamp
1012 unsigned long timestamp;
1013 token.ToULong(&timestamp, 16);
1014
1015 token = tkz.GetNextToken(); // Source ID
1016 unsigned long source;
1017 token.ToULong(&source, 16);
1018
1019 token = tkz.GetNextToken(); // Payload + "*CRC_byte"
1020
1021 wxStringTokenizer datatkz(token, "*");
1022 wxString data = datatkz.GetNextToken();
1023
1024 // Create the OCPN payload
1025 std::vector<uint8_t> o_payload;
1026 o_payload.push_back(0x93);
1027 o_payload.push_back(0x13);
1028 o_payload.push_back(3); // priority hardcoded, missing in SeaSmart
1029 o_payload.push_back(PGN & 0xFF);
1030 o_payload.push_back((PGN >> 8) & 0xFF);
1031 o_payload.push_back((PGN >> 16) & 0xFF);
1032 o_payload.push_back(0xFF); // destination hardcoded, missing in SeaSmart
1033 o_payload.push_back((uint8_t)source); // header.source);
1034 o_payload.push_back(timestamp & 0xFF);
1035 o_payload.push_back((timestamp >> 8) & 0xFF);
1036 o_payload.push_back((timestamp >> 16) & 0xFF);
1037 o_payload.push_back((timestamp >> 24) & 0xFF);
1038 o_payload.push_back((uint8_t)data.Length() / 2);
1039 for (size_t i = 0; i < data.Length(); i += 2) {
1040 unsigned long dv;
1041 wxString sbyte = data.Mid(i, 2);
1042 sbyte.ToULong(&dv, 16);
1043 o_payload.push_back((uint8_t)dv);
1044 }
1045 o_payload.push_back(0x55); // CRC dummy, not checked
1046
1047 if (HandleMgntMsg(PGN, o_payload)) return false;
1048
1049 // Message is ready
1050 CommDriverN2KNetEvent Nevent(wxEVT_COMMDRIVER_N2K_NET, 0);
1051 auto n2k_payload = std::make_shared<std::vector<uint8_t>>(o_payload);
1052 Nevent.SetPayload(n2k_payload);
1053 AddPendingEvent(Nevent);
1054 }
1055 }
1056 return true;
1057}
1058
1059bool CommDriverN2KNet::ProcessMiniPlex(
1060 const std::vector<unsigned char>& packet) {
1061 /*
1062 $MXPGN – NMEA 2000 PGN Data
1063 This sentence transports NMEA 2000/CAN frames in NMEA 0183 format. The
1064 MiniPlex-3 will transmit this sentence with Talker ID “MX”. When sent to the
1065 MiniPlex-3, the Talker ID is ignored unless a routing entry exists for this
1066 sentence.
1067
1068 Format: $--PGN,pppppp,aaaa,c--c*hh<CR><LF>
1069
1070 pppppp: PGN of the NMEA 2000/CAN frame, 3-byte hexadecimal number. If the PGN
1071 is non-global, the lowest byte contains the destination address. aaaa:
1072 Attribute Word, a 16-bit hexadecimal number. This word contains the priority,
1073 the DLC code and then source/destination address of the frame, formatted as
1074 shown below:
1075
1076 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
1077 ----------------------------------------------------------------
1078 | S | Priority | DLC | Address |
1079 ----------------------------------------------------------------
1080
1081 S: Send bit. When an NMEA 2000/CAN frame is received, this bit is 0.
1082 To use the $MXPGN sentence to send an NMEA 2000/CAN frame, this bit must be 1.
1083 Priority: Frame priority. A value between 0 and 7, a lower value means higher
1084 priority. DLC: Data Length Code field, contains the size of the frame in bytes
1085 (1..8) or a Class 2 Transmission ID (9..15). Address: Depending on the Send
1086 bit, this field contains the Source Address (S=0) or the Destination Address
1087 (S=1) of the frame. c--c: Data field of the NMEA 2000/CAN frame, organised as
1088 one large number in hexadecimal notation from MSB to LSB. This is in
1089 accordance with “NMEA 2000 Appendix D”, chapter D.1, “Data Placement within
1090 the CAN Frame”. The size of this field depends on the DLC value and can be 1
1091 to 8 bytes (2 to 16 hexadecimal characters).
1092
1093 NMEA 2000 Reception
1094
1095 When the MiniPlex-3 converts an NMEA 2000/CAN frame into an $MXPGN sentence,
1096 the S bit in the Attribute field will be 0 and the Address field contains the
1097 source address of the frame. The destination address of the frame is either
1098 global or contained in the lower byte of the PGN, in accordance with the NMEA
1099 2000/ISO specification.
1100
1101 Notes:
1102
1103 Multiple messages can be delivered in a single packet
1104 It is not guaranteed that the whole message will be delivered in a single
1105 packet, actually it is common that the last message is split "anywhere" and
1106 continues in the next packet.
1107
1108 packet 1 payload
1109
1110 "$MXPGN,01F119,3816,FFFAAF01A3FDE301*14\r\n
1111 $MXPGN,01F201,2816,C50E0A19A0001A40*66\r\n
1112 $MXPGN,01F201,2816,6B4C0039058D8A41*15\r\n
1113 $MXPGN,01F201,2816,FFFFFFFFFF007542*1D\r\n
1114 $MXPGN,01F201,2816,FF7F7F0000000A43*6F\r\n
1115 $MXPGN,01F209,2816,2D002400ED0009A0*18\r\n
1116 $MXPGN,01F209,2816,FFFFFFFF002C00A1*10\r\n
1117 $MXPGN,01F213,6816,00B4F512020106C0*6E\r\n
1118 $MXPGN,01F214,6816,01FFFF7FFF04F801*12\r\n
1119 $MXPGN,01F214,6816,7EFFFF0009056400*65\r\n
1120 $MXPGN,"
1121
1122 packet 2 payload
1123
1124 "01F212,6816,185B560101010BC0*62\r\n
1125 $MXPGN,01F212,6816,FFFFFFFF00D700C1*1E\r\n
1126 $MXPGN,01FD06,5816,FF03F6749570C101*67\r\n
1127 $MXPGN,01FD07,5816,03F635B672F20401*1B\r\n"
1128
1129 packet 1
1130
1131 "$MXPGN,01F114,3816,FFFFF000D20212FF*1E\r\n
1132 $MXPGN,01F905,6816,0001000300005BC0*14\r\n
1133 $MXPGN,01F905,6816,6142010EE00007C1*67\r\n
1134 $MXPGN,01F905,6816,68206F74206B63C2*6F\r\n
1135 $MXPGN,01F905,6816,0D0001FF656D6FC3*16\r\n
1136 $MXPGN,01F905,6816,20747261745301C4*62\r\n
1137 $MXPGN,01F905,6816,4600746E696F70C5*6E\r\n
1138 $MXPGN,01F905,6816,020C84588023C3C6*6E\r\n
1139 $MXPGN,01F905,6816,6E727554011200C7*11\r\n
1140 $MXPGN,01F905,6816,65726F666562"
1141
1142 packet 2 payload
1143
1144 "20C8*1A\r\n
1145 $MXPGN,01F905,6816,CCA06B636F7220C9*1F\r\n
1146 $MXPGN,01F905,6816,030C85DF2023C4CA*1B\r\n
1147 $MXPGN,01F905,6816,656D6F48010600CB*19\r\n
1148 $MXPGN,01F905,6816,8765C023C65340CC*1B\r\n
1149 $MXPGN,01F905,6816,FFFFFFFFFFFF0CCD*66\r\n
1150 $MXPGN,01F10D,2816,FFFF0369FC97F901*16\r\n
1151 $MXPGN,01F112,2816,FD03C0FDF49B1A00*11\r\n
1152 $MXPGN,01F200,2816,FFFF7FFFFF43F800*10\r\n
1153 $MXPGN,01F205,2816,FF050D3A1D4CFC00*19\r\n"
1154 */
1155 while (!m_circle.IsEmpty()) {
1156 char b = m_circle.Get();
1157 if ((b != 0x0a) && (b != 0x0d)) {
1158 m_sentence += b;
1159 }
1160 if (b == 0x0a) { // end of sentence
1161
1162 // Extract a can_frame from ASCII stream
1163 // printf("%s\n", m_sentence.c_str());
1164
1165 wxString ss(m_sentence.c_str());
1166 m_sentence.clear();
1167 wxStringTokenizer tkz(ss, ",");
1168
1169 // Discard first token
1170 wxString token = tkz.GetNextToken(); // $MXPGN
1171 m_TX_flag = 'R';
1172
1173 token = tkz.GetNextToken(); // PGN
1174 unsigned long PGN;
1175 token.ToULong(&PGN, 16);
1176
1177 token = tkz.GetNextToken(); // Attribute compound field
1178 unsigned long attr;
1179 token.ToULong(&attr, 16);
1180 // Send Bit
1181 bool send_bit = (attr >> 15) != 0;
1182 // Priority
1183 uint8_t priority = (attr >> 12) & 0x07;
1184
1185 // dlc
1186 uint8_t dlc = (attr >> 8) & 0x0F;
1187
1188 // address
1189 uint8_t address = attr & 0xFF;
1190
1191 token = tkz.GetNextToken(); // Payload + "*CRC_byte"
1192
1193 wxStringTokenizer datatkz(token, "*");
1194 wxString data = datatkz.GetNextToken();
1195
1196 if (data.Length() >
1197 16) { // Payload can never exceed 8 bytes (=16 HEX characters)
1198 return false;
1199 }
1200
1201 can_frame frame;
1202 memset(&frame.data, 0, 8);
1203 for (size_t i = 0; i < data.Length(); i += 2) {
1204 unsigned long dv;
1205 wxString sbyte = data.Mid(data.Length() - i - 2, 2);
1206 sbyte.ToULong(&dv, 16);
1207 frame.data[i / 2] = ((uint8_t)dv);
1208 }
1209 frame.can_id = (uint32_t)BuildCanID(priority, address, 0xFF, PGN);
1210 HandleCanFrameInput(frame);
1211 }
1212 }
1213 return true;
1214}
1215
1216void CommDriverN2KNet::OnSocketEvent(wxSocketEvent& event) {
1217#define RD_BUF_SIZE 4096
1218 // can_frame frame;
1219
1220 switch (event.GetSocketEvent()) {
1221 case wxSOCKET_INPUT: {
1222 // TODO determine if the follwing SetFlags needs to be done at every
1223 // socket event or only once when socket is created, it it needs to be
1224 // done at all!
1225 // m_sock->SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK); // was
1226 // (wxSOCKET_NOWAIT);
1227
1228 // We use wxSOCKET_BLOCK to avoid Yield() reentrancy problems
1229 // if a long ProgressDialog is active, as in S57 SENC creation.
1230
1231 // Disable input event notifications to preclude re-entrancy on
1232 // non-blocking socket
1233 // m_sock->SetNotify(wxSOCKET_LOST_FLAG);
1234
1235 std::vector<unsigned char> data(RD_BUF_SIZE + 1);
1236 int newdata = 0;
1237 uint8_t next_byte = 0;
1238
1239 event.GetSocket()->Read(&data.front(), RD_BUF_SIZE);
1240 if (!event.GetSocket()->Error()) {
1241 m_driver_stats.available = true;
1242 size_t count = event.GetSocket()->LastCount();
1243 if (count) {
1244 if (1 /*FIXME !g_benableUDPNullHeader*/) {
1245 data[count] = 0;
1246 newdata = count;
1247 } else {
1248 // XXX FIXME: is it reliable?
1249 }
1250 }
1251 }
1252
1253 bool done = false;
1254 if (newdata > 0) {
1255 for (int i = 0; i < newdata; i++) {
1256 if (!m_circle.IsFull()) m_circle.Put(data[i]);
1257 // printf("%c", data.at(i));
1258 }
1259 }
1260 // Only invoke DetectFormat() on every tenth message:
1261 m_detect_count = (m_detect_count + 1) % 10;
1262 if (m_detect_count <= 0) m_n2k_format = DetectFormat(data);
1263
1264 switch (m_n2k_format) {
1265 case N2KFormat_Actisense_RAW_ASCII:
1266 ProcessActisense_ASCII_RAW(data);
1267 break;
1268 case N2KFormat_YD_RAW: // RX Byte compatible with Actisense ASCII RAW
1269 ProcessActisense_ASCII_RAW(data);
1270 break;
1271 case N2KFormat_Actisense_N2K_ASCII:
1272 ProcessActisense_ASCII_N2K(data);
1273 break;
1274 case N2KFormat_Actisense_N2K:
1275 ProcessActisense_N2K(data);
1276 break;
1277 case N2KFormat_Actisense_RAW:
1278 ProcessActisense_RAW(data);
1279 break;
1280 case N2KFormat_Actisense_NGT:
1281 ProcessActisense_NGT(data);
1282 break;
1283 case N2KFormat_SeaSmart:
1284 ProcessSeaSmart(data);
1285 break;
1286 case N2KFormat_MiniPlex:
1287 ProcessMiniPlex(data);
1288 break;
1289 case N2KFormat_Undefined:
1290 default:
1291 break;
1292 }
1293 // Check for any pending output message
1294 } // case
1295
1296 m_dog_value = N_DOG_TIMEOUT; // feed the dog
1297 break;
1298#if 1
1299
1300 case wxSOCKET_LOST: {
1301 m_driver_stats.available = false;
1302 if (GetProtocol() == TCP || GetProtocol() == GPSD) {
1303 if (GetBrxConnectEvent())
1304 wxLogMessage(wxString::Format("NetworkDataStream connection lost: %s",
1305 GetPort().c_str()));
1306 if (GetSockServer()) {
1307 GetSock()->Destroy();
1308 SetSock(nullptr);
1309 break;
1310 }
1311 wxDateTime now = wxDateTime::Now();
1312 wxTimeSpan since_connect(
1313 0, 0, 10); // ten secs assumed, if connect time is uninitialized
1314 if (GetConnectTime().IsValid()) since_connect = now - GetConnectTime();
1315
1316 int retry_time = 5000; // default
1317
1318 // If the socket has never connected, and it is a short interval since
1319 // the connect request then stretch the time a bit. This happens on
1320 // Windows if there is no dafault IP on any interface
1321
1322 if (!GetBrxConnectEvent() && (since_connect.GetSeconds() < 5))
1323 retry_time = 10000; // 10 secs
1324
1325 GetSocketThreadWatchdogTimer()->Stop();
1326 GetSocketTimer()->Start(
1327 retry_time, wxTIMER_ONE_SHOT); // Schedule a re-connect attempt
1328 }
1329 break;
1330 }
1331
1332 case wxSOCKET_CONNECTION: {
1333 m_driver_stats.available = true;
1334 if (GetProtocol() == GPSD) {
1335 // Sign up for watcher mode, Cooked NMEA
1336 // Note that SIRF devices will be converted by gpsd into
1337 // pseudo-NMEA
1338 char cmd[] = "?WATCH={\"class\":\"WATCH\", \"nmea\":true}";
1339 GetSock()->Write(cmd, strlen(cmd));
1340 } else if (GetProtocol() == TCP) {
1341 wxLogMessage(
1342 wxString::Format("TCP NetworkDataStream connection established: %s",
1343 GetPort().c_str()));
1344 m_dog_value = N_DOG_TIMEOUT; // feed the dog
1345 if (GetPortType() != DS_TYPE_OUTPUT) {
1347 if (GetParams().NoDataReconnect)
1348 GetSocketThreadWatchdogTimer()->Start(1000);
1349 }
1350 if (GetPortType() != DS_TYPE_INPUT && GetSock()->IsOk())
1351 (void)SetOutputSocketOptions(GetSock());
1352 GetSocketTimer()->Stop();
1353 SetBrxConnectEvent(true);
1354 }
1355
1356 SetConnectTime(wxDateTime::Now());
1357 break;
1358 }
1359#endif
1360 default:
1361 break;
1362 }
1363}
1364
1365void CommDriverN2KNet::OnServerSocketEvent(wxSocketEvent& event) {
1366 switch (event.GetSocketEvent()) {
1367 case wxSOCKET_CONNECTION: {
1368 m_driver_stats.available = true;
1369 SetSock(GetSockServer()->Accept(false));
1370
1371 if (GetSock()) {
1372 GetSock()->SetTimeout(2);
1373 // GetSock()->SetFlags(wxSOCKET_BLOCK);
1374 GetSock()->SetEventHandler(*this, DS_SOCKET_ID);
1375 int notify_flags = (wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG);
1376 if (GetPortType() != DS_TYPE_INPUT) {
1377 notify_flags |= wxSOCKET_OUTPUT_FLAG;
1378 (void)SetOutputSocketOptions(GetSock());
1379 }
1380 if (GetPortType() != DS_TYPE_OUTPUT)
1381 notify_flags |= wxSOCKET_INPUT_FLAG;
1382 GetSock()->SetNotify(notify_flags);
1383 GetSock()->Notify(true);
1384 }
1385
1386 break;
1387 }
1388
1389 default:
1390 break;
1391 }
1392}
1393
1394std::vector<unsigned char> MakeSimpleOutMsg(
1395 int data_format, int pgn, std::vector<unsigned char>& payload) {
1396 std::vector<unsigned char> out_vec;
1397
1398 switch (data_format) {
1399 case N2KFormat_YD_RAW:
1400 case N2KFormat_Actisense_RAW_ASCII: {
1401 // Craft the canID
1402 unsigned can_id = BuildCanID(6, 0xff, 0xff, pgn);
1403 std::stringstream ss;
1404 ss << std::setfill('0') << std::setw(8) << std::hex << can_id;
1405 for (unsigned char s : ss.str()) out_vec.push_back(s);
1406 out_vec.push_back(' ');
1407
1408 // Data payload
1409 std::string sspl;
1410 char tv[4];
1411 for (unsigned char d : payload) {
1412 snprintf(tv, 4, "%02X ", d);
1413 sspl += tv;
1414 }
1415 for (unsigned char s : sspl) out_vec.push_back(s);
1416
1417 // terminate
1418 out_vec.pop_back();
1419 out_vec.push_back(0x0d);
1420 out_vec.push_back(0x0a);
1421 break;
1422 }
1423 case N2KFormat_Actisense_N2K_ASCII: {
1424 // Create the time field
1425 wxDateTime now = wxDateTime::Now();
1426 wxString stime = now.Format("%H%M%S");
1427 stime += ".000 ";
1428 std::string sstime = stime.ToStdString();
1429 out_vec.push_back('A');
1430 for (unsigned char s : sstime) out_vec.push_back(s);
1431
1432 // src/dest/prio field
1433 wxString sdp;
1434 sdp.Printf("%02X%02X%1X ",
1435 1, // source
1436 (unsigned char)0xFF, 0x6);
1437 std::string ssdp = sdp.ToStdString();
1438 for (unsigned char s : ssdp) out_vec.push_back(s);
1439
1440 // PGN field
1441 wxString spgn;
1442 spgn.Printf("%05X ", pgn);
1443 std::string sspgn = spgn.ToStdString();
1444 for (unsigned char s : sspgn) out_vec.push_back(s);
1445
1446 // Data payload
1447 std::string sspl;
1448 char tv[3];
1449 for (unsigned char d : payload) {
1450 snprintf(tv, 3, "%02X", d);
1451 sspl += tv;
1452 }
1453 for (unsigned char s : sspl) out_vec.push_back(s);
1454
1455 // terminator
1456 out_vec.push_back(0x0d);
1457 out_vec.push_back(0x0a);
1458 break;
1459 }
1460 case N2KFormat_MiniPlex: {
1461 out_vec.push_back('$');
1462 out_vec.push_back('M');
1463 out_vec.push_back('X');
1464 out_vec.push_back('P');
1465 out_vec.push_back('G');
1466 out_vec.push_back('N');
1467 out_vec.push_back(',');
1468 // PGN field
1469 wxString spgn;
1470 spgn.Printf("%06X,", pgn);
1471 std::string sspgn = spgn.ToStdString();
1472 for (unsigned char c : sspgn) {
1473 out_vec.push_back(c);
1474 }
1475 // Attribute word
1476 uint16_t attr = 0;
1477
1478 attr |= ((uint16_t)0x06) << 12;
1479 attr |= ((uint16_t)payload.size()) << 8;
1480 attr |= (uint16_t)0xFF;
1481 attr |= 0x8000; // S bit set to 1
1482
1483 wxString sattr;
1484 sattr.Printf("%04X,", attr);
1485 std::string ssattr = sattr.ToStdString();
1486 for (unsigned char c : ssattr) {
1487 out_vec.push_back(c);
1488 }
1489 // Data payload
1490 char tv[3];
1491 for (auto rit = payload.rbegin(); rit != payload.rend(); ++rit) {
1492 snprintf(tv, 3, "%02X", *rit);
1493 out_vec.push_back(tv[0]);
1494 out_vec.push_back(tv[1]);
1495 }
1496 // CRC
1497 uint8_t crc = 0;
1498 for (auto ci = ++out_vec.begin(); ci != out_vec.end(); ci++) {
1499 crc ^= *ci;
1500 }
1501 out_vec.push_back('*');
1502 snprintf(tv, 3, "%02X", crc);
1503 out_vec.push_back(tv[0]);
1504 out_vec.push_back(tv[1]);
1505
1506 // term
1507 out_vec.push_back(0x0d);
1508 out_vec.push_back(0x0a);
1509 // DBG: std::cout << std::string(out_vec.begin(), out_vec.end()) <<
1510 // std::endl << std::flush;
1511 break;
1512 }
1513 default:
1514 break;
1515 }
1516 return out_vec;
1517}
1518
1519std::vector<std::vector<unsigned char>> CommDriverN2KNet::GetTxVector(
1520 const std::shared_ptr<const Nmea2000Msg>& msg,
1521 const std::shared_ptr<const NavAddr2000>& dest_addr) {
1522 std::vector<std::vector<unsigned char>> tx_vector;
1523
1524 // Branch based on detected network data format currently in use
1525 switch (m_n2k_format) {
1526 case N2KFormat_YD_RAW:
1527 break;
1528 case N2KFormat_Actisense_RAW_ASCII: {
1529 // 00:34:02.718 R 15FD0800 FF 00 01 CA 6F FF FF FF
1530 if (!IsFastMessagePGN(msg->PGN.pgn) && msg->payload.size() <= 8) {
1531 // Single packet message
1532 std::vector<unsigned char> header_vec;
1533 std::vector<unsigned char> out_vec;
1534
1535 // Craft the canID
1536 // No need to specify the source address
1537 // The TX frame will adopt the gateway's claimed N2K address.
1538 unsigned long can_id =
1539 BuildCanID(msg->priority, 0, dest_addr->address, msg->PGN.pgn);
1540
1541 std::stringstream ss;
1542 ss << std::setfill('0') << std::setw(8) << std::hex << can_id;
1543 for (unsigned char s : ss.str()) header_vec.push_back(s);
1544 header_vec.push_back(' ');
1545
1546 // constant header
1547 for (unsigned char s : header_vec) out_vec.push_back(s);
1548
1549 // single data packet
1550 std::string ssdata;
1551 for (unsigned int k = 0; k < msg->payload.size(); k++) {
1552 char tb[4];
1553 snprintf(tb, 4, "%02X ", msg->payload.data()[k]);
1554 ssdata += tb;
1555 }
1556 for (unsigned char s : ssdata) out_vec.push_back(s);
1557 out_vec.pop_back(); // drop the last space character
1558
1559 out_vec.push_back(0x0d); // terminate the string
1560 out_vec.push_back(0x0a);
1561
1562 tx_vector.push_back(out_vec);
1563 } else {
1564 std::vector<unsigned char> header_vec;
1565 std::vector<unsigned char> out_vec;
1566
1567 // No Need to create a timestamp or frame R/T indicator
1568#if 0
1569 // time header
1570 wxDateTime now = wxDateTime::Now();
1571 wxString stime = now.Format("%H:%M:%S");
1572 stime += ".000 ";
1573 std::string sstime = stime.ToStdString();
1574 for (unsigned char s : sstime) header_vec.push_back(s);
1575
1576 // Tx indicator
1577 header_vec.push_back('T');
1578 header_vec.push_back(' ');
1579#endif
1580
1581 // Craft the canID
1582 // No need to specify the source address
1583 // The TX frame will adopt the gateway's claimed N2K address.
1584 unsigned long can_id =
1585 BuildCanID(msg->priority, 0, dest_addr->address, msg->PGN.pgn);
1586 std::stringstream ss;
1587 ss << std::setfill('0') << std::setw(8) << std::hex << can_id;
1588 for (unsigned char s : ss.str()) header_vec.push_back(s);
1589 header_vec.push_back(' ');
1590
1591 // format the required number of short packets, in a loop
1592 int payload_size = msg->payload.size();
1593 unsigned char temp[8]; // {0,0,0,0,0,0,0,0};
1594 int cur = 0;
1595 int nframes =
1596 (payload_size > 6 ? (payload_size - 6 - 1) / 7 + 1 + 1 : 1);
1597 bool result = true;
1598 for (int i = 0; i < nframes && result; i++) {
1599 temp[0] = i | m_order; // frame counter
1600 if (i == 0) {
1601 temp[1] = msg->payload.size(); // total bytes in fast packet
1602 // send the first 6 bytes
1603 for (int j = 2; j < 8; j++) {
1604 temp[j] = msg->payload.data()[cur];
1605 cur++;
1606 }
1607 } else {
1608 int j = 1;
1609 // send the next 7 data bytes
1610 for (; j < 8 && cur < payload_size; j++) {
1611 temp[j] = msg->payload.data()[cur];
1612 cur++;
1613 }
1614 for (; j < 8; j++) {
1615 temp[j] = 0xff;
1616 }
1617 }
1618
1619 out_vec.clear();
1620
1621 // constant header
1622 for (unsigned char s : header_vec) out_vec.push_back(s);
1623
1624 // data, per packet
1625 std::string ssdata;
1626 for (unsigned int k = 0; k < 8; k++) {
1627 char tb[4];
1628 snprintf(tb, 4, "%02X ", temp[k]);
1629 ssdata += tb;
1630 }
1631 for (unsigned char s : ssdata) out_vec.push_back(s);
1632 out_vec.pop_back(); // drop the last space character
1633
1634 out_vec.push_back(0x0d); // terminate the string
1635 out_vec.push_back(0x0a);
1636
1637 tx_vector.push_back(out_vec);
1638 } // for loop
1639 }
1640 } break;
1641 case N2KFormat_Actisense_N2K_ASCII: {
1642 // Source: Actisense own documentation `NMEA 2000 ASCII Output
1643 // format.docx`
1644 //
1645 // Ahhmmss.ddd <SS><DD><P> <PPPPP> b0b1b2b3b4b5b6b7.....bn<CR><LF>
1646 // A = message is N2K or J1939 message
1647 // 173321.107 - time 17:33:21.107
1648 // <SS> - source address
1649 // <DD> - destination address
1650 // <P> - priority
1651 // <PPPPP> - PGN number
1652 // b0b1b2b3b4b5b6b7.....bn - data payload in hex. NB: ISO TP payload could
1653 // be up to 1786 bytes
1654 //
1655 // Example: `A173321.107 23FF7 1F513 012F3070002F30709F\n`
1656 // 1 2 3 4
1657
1658 std::vector<unsigned char> ovec;
1659
1660 // Create the time field
1661 wxDateTime now = wxDateTime::Now();
1662 wxString stime = now.Format("%H%M%S");
1663 stime += ".000 ";
1664 std::string sstime = stime.ToStdString();
1665 ovec.push_back('A');
1666 for (unsigned char s : sstime) ovec.push_back(s);
1667
1668 // src/dest/prio field
1669 wxString sdp;
1670 sdp.Printf("%02X%02X%1X ",
1671 1, // source
1672 (unsigned char)dest_addr->address,
1673 (unsigned char)msg->priority);
1674 std::string ssdp = sdp.ToStdString();
1675 for (unsigned char s : ssdp) ovec.push_back(s);
1676
1677 // PGN field
1678 wxString spgn;
1679 spgn.Printf("%05X ", (int)msg->PGN.pgn);
1680 std::string sspgn = spgn.ToStdString();
1681 for (unsigned char s : sspgn) ovec.push_back(s);
1682
1683 // Data payload
1684 std::string sspl;
1685 char tv[3];
1686 for (unsigned char d : msg->payload) {
1687 snprintf(tv, 3, "%02X", d);
1688 sspl += tv;
1689 }
1690 for (unsigned char s : sspl) ovec.push_back(s);
1691
1692 // term
1693 ovec.push_back(0x0d);
1694 ovec.push_back(0x0a);
1695
1696 // form the result
1697 tx_vector.push_back(ovec);
1698
1699 break;
1700 }
1701 case N2KFormat_MiniPlex: {
1702 std::vector<unsigned char> ovec;
1703 if (!IsFastMessagePGN(msg->PGN.pgn) && msg->payload.size() < 8) {
1704 // Single packet
1705 } else {
1706 size_t cur = 0;
1707 size_t nframes =
1708 (msg->payload.size() > 6 ? (msg->payload.size() - 6 - 1) / 7 + 1 + 1
1709 : 1);
1710 for (size_t i = 0; i < nframes; i++) {
1711 ovec.push_back('$');
1712 ovec.push_back('M');
1713 ovec.push_back('X');
1714 ovec.push_back('P');
1715 ovec.push_back('G');
1716 ovec.push_back('N');
1717 ovec.push_back(',');
1718 // PGN field
1719 wxString spgn;
1720 spgn.Printf("%06X,", (int)msg->PGN.pgn);
1721 std::string sspgn = spgn.ToStdString();
1722 for (unsigned char c : sspgn) {
1723 ovec.push_back(c);
1724 }
1725 // Attribute word
1726 uint16_t attr = 0;
1727 uint8_t len = 8;
1728 if (i == nframes - 1) {
1729 // TODO Check this
1730 // len = msg->payload.size() + 1 - 6 - (nframes - 2) * 7;
1731 }
1732
1733 attr |= ((uint16_t)((uint8_t)msg->priority & 0x07)) << 12;
1734 attr |= ((uint16_t)len) << 8;
1735 attr |= (uint16_t)dest_addr->address;
1736 attr |= 0x8000; // S bit set to 1
1737
1738 wxString sattr;
1739 sattr.Printf("%04X,", attr);
1740 std::string ssattr = sattr.ToStdString();
1741 for (unsigned char c : ssattr) {
1742 ovec.push_back(c);
1743 }
1744 // Data payload
1745 char tv[3];
1746 uint8_t databytes = i == 0 ? len - 2 : len - 1;
1747 std::vector<unsigned char> payload;
1748 for (uint8_t j = 0; j < databytes; j++) {
1749 payload.push_back(msg->payload[cur]);
1750 cur++;
1751 }
1752
1753 // Buffer the data to 7 bytes, if necessary, buffer at start.
1754 int psize = payload.size();
1755 while ((i > 0) && (psize < 7)) {
1756 ovec.push_back('F');
1757 ovec.push_back('F');
1758 psize++;
1759 }
1760
1761 // Buffer the actual payload bytes
1762 for (auto rit = payload.rbegin(); rit != payload.rend(); ++rit) {
1763 snprintf(tv, 3, "%02X", *rit);
1764 ovec.push_back(tv[0]);
1765 ovec.push_back(tv[1]);
1766 }
1767 if (i == 0) { // First frame contains the total payload length
1768 snprintf(tv, 3, "%02X", (uint8_t)msg->payload.size());
1769 ovec.push_back(tv[0]);
1770 ovec.push_back(tv[1]);
1771 }
1772 // frame counter
1773 snprintf(tv, 3, "%02X", (uint8_t)i | m_order);
1774 ovec.push_back(tv[0]);
1775 ovec.push_back(tv[1]);
1776
1777 // CRC
1778 uint8_t crc = 0;
1779 for (auto ci = ++ovec.begin(); ci != ovec.end(); ci++) {
1780 crc ^= *ci;
1781 }
1782 ovec.push_back('*');
1783 snprintf(tv, 3, "%02X", crc);
1784 ovec.push_back(tv[0]);
1785 ovec.push_back(tv[1]);
1786
1787 // term
1788 ovec.push_back(0x0d);
1789 ovec.push_back(0x0a);
1790
1791 // DBG: std::cout << std::string(ovec.begin(), ovec.end()) <<
1792 // std::endl << std::flush;
1793
1794 // form the result
1795 tx_vector.push_back(ovec);
1796 ovec.clear();
1797 }
1798 break;
1799 }
1800 }
1801 case N2KFormat_Actisense_N2K:
1802 case N2KFormat_Actisense_RAW:
1803 case N2KFormat_Actisense_NGT:
1804 case N2KFormat_SeaSmart:
1805 default:
1806 break;
1807 }
1808
1809 // update the fast message Sequence ID bits
1810 m_order = (m_order + 0x20) & 0xE0;
1811
1812 return tx_vector;
1813}
1814
1815bool CommDriverN2KNet::PrepareForTX() {
1816 // We need to determine several items before TX operations can commence.
1817 // 1. Is the gateway configured at my ip present, and if so, which of
1818 // the two supported gateways is it? (YDEN-type, or Actisense-type.
1819 // 2. If Actisense type, we need to infer the N2K source address it has
1820 // claimed, so that we can use that address for our TX operations.
1821
1822 // BASIC ASSUMPTION: There is (or has been) enough network traffic to
1823 // allow occurate determination of data format currently in use
1824
1825 // Step 1.1
1826 // If the detected data format is N2KFormat_Actisense_N2K_ASCII,
1827 // then we are clearly connected to an actisense device.
1828 // Nothing else need be done.
1829
1830 if (m_n2k_format == N2KFormat_Actisense_N2K_ASCII) return true;
1831
1832 // Step 1.2
1833 // If the detected data format is N2KFormat_MiniPlex,
1834 // then we are clearly connected to a MiniPlex.
1835 // Nothing else need be done.
1836
1837 if (m_n2k_format == N2KFormat_MiniPlex) return true;
1838
1839 // Step 1.2
1840 // If the detected data format is N2KFormat_SeaSmart,
1841 // then we can't transmit.
1842 if (m_n2k_format == N2KFormat_SeaSmart) return false;
1843
1844 // Step 2
1845
1846 // Assume that the gateway is YDEN type, RAW mode. Verify if true.
1847 // Logic: Actisense gateway will not respond to TX_FORMAT_YDEN,
1848 // so if we get sensible response, the gw must be YDEN type.
1849
1850 // Already tested and found available?
1851 if (m_TX_available)
1852 return true;
1853 else {
1854 // Send a broadcast request for PGN 126996, Product Information
1855 std::vector<unsigned char> payload;
1856 payload.push_back(0x14);
1857 payload.push_back(0xF0);
1858 payload.push_back(0x01);
1859
1860 std::vector<std::vector<unsigned char>> out_data;
1861 std::vector<unsigned char> msg_vec =
1862 MakeSimpleOutMsg(N2KFormat_YD_RAW, 59904, payload);
1863 out_data.push_back(msg_vec);
1864 SendSentenceNetwork(out_data);
1865
1866 // Wait some time, and study results
1867 m_prodinfo_timer.Start(200, true);
1868 }
1869
1870 // No acceptable TX device found
1871 return false;
1872}
1873
1874bool CommDriverN2KNet::SendN2KNetwork(
1875 const std::shared_ptr<const Nmea2000Msg>& msg,
1876 const std::shared_ptr<const NavAddr2000>& addr) {
1877 PrepareForTX();
1878
1879 std::vector<std::vector<unsigned char>> out_data = GetTxVector(msg, addr);
1880 SendSentenceNetwork(out_data);
1881 m_driver_stats.tx_count += msg->payload.size();
1882
1883 // Create internal message and notify upper layers
1884 std::vector<unsigned char> msg_payload = PrepareLogPayload(msg, addr);
1885 m_listener.Notify(
1886 std::make_shared<const Nmea2000Msg>(msg->PGN.pgn, msg_payload, addr));
1887
1888 return true;
1889};
1890
1891bool CommDriverN2KNet::SendSentenceNetwork(
1892 const std::vector<std::vector<unsigned char>>& payload) {
1893 if (m_txenter)
1894 return false; // do not allow recursion, could happen with non-blocking
1895 // sockets
1896 m_txenter++;
1897
1898 bool ret = true;
1899 wxDatagramSocket* udp_socket;
1900 switch (GetProtocol()) {
1901 case TCP:
1902 for (const std::vector<unsigned char>& v : payload) {
1903 if (GetSock() && GetSock()->IsOk()) {
1904 m_driver_stats.available = true;
1905 // printf("---%s", v.data());
1906 GetSock()->Write(v.data(), v.size());
1907 m_dog_value = N_DOG_TIMEOUT; // feed the dog
1908 if (GetSock()->Error()) {
1909 if (GetSockServer()) {
1910 GetSock()->Destroy();
1911 SetSock(nullptr);
1912 } else {
1913 auto* tcp_socket = dynamic_cast<wxSocketClient*>(GetSock());
1914 if (tcp_socket) tcp_socket->Close();
1915 if (!GetSocketTimer()->IsRunning())
1916 GetSocketTimer()->Start(
1917 5000, wxTIMER_ONE_SHOT); // schedule a reconnect
1918 GetSocketThreadWatchdogTimer()->Stop();
1919 }
1920 ret = false;
1921 }
1922 wxMilliSleep(2);
1923 } else {
1924 m_driver_stats.available = false;
1925 ret = false;
1926 }
1927 }
1928 break;
1929 case UDP:
1930#if 0
1931 udp_socket = dynamic_cast<wxDatagramSocket*>(GetTSock());
1932 if (udp_socket && udp_socket->IsOk()) {
1933 udp_socket->SendTo(GetAddr(), payload.mb_str(), payload.size());
1934 if (udp_socket->Error()) ret = false;
1935 } else
1936 ret = false;
1937#endif
1938 break;
1939
1940 case GPSD:
1941 default:
1942 ret = false;
1943 break;
1944 }
1945 m_txenter--;
1946 return ret;
1947}
1948
1949void CommDriverN2KNet::Close() {
1950 wxLogMessage(wxString::Format("Closing NMEA NetworkDataStream %s",
1951 GetNetPort().c_str()));
1952 m_stats_timer.Stop();
1953 // Kill off the TCP Socket if alive
1954 if (m_sock) {
1955 if (m_is_multicast)
1956 m_sock->SetOption(IPPROTO_IP, IP_DROP_MEMBERSHIP, &m_mrq_container->m_mrq,
1957 sizeof(m_mrq_container->m_mrq));
1958 m_sock->Notify(FALSE);
1959 m_sock->Destroy();
1960 m_driver_stats.available = false;
1961 }
1962
1963 if (m_tsock) {
1964 m_tsock->Notify(FALSE);
1965 m_tsock->Destroy();
1966 }
1967
1968 if (m_socket_server) {
1969 m_socket_server->Notify(FALSE);
1970 m_socket_server->Destroy();
1971 }
1972
1973 m_socket_timer.Stop();
1974 m_socketread_watchdog_timer.Stop();
1975}
1976
1977bool CommDriverN2KNet::SetOutputSocketOptions(wxSocketBase* tsock) {
1978 int ret;
1979
1980 // Disable nagle algorithm on outgoing connection
1981 // Doing this here rather than after the accept() is
1982 // pointless on platforms where TCP_NODELAY is
1983 // not inherited. However, none of OpenCPN's currently
1984 // supported platforms fall into that category.
1985
1986 int nagleDisable = 1;
1987 ret = tsock->SetOption(IPPROTO_TCP, TCP_NODELAY, &nagleDisable,
1988 sizeof(nagleDisable));
1989
1990 // Drastically reduce the size of the socket output buffer
1991 // so that when client goes away without properly closing, the stream will
1992 // quickly fill the output buffer, and thus fail the write() call
1993 // within a few seconds.
1994 unsigned long outbuf_size = 1024; // Smallest allowable value on Linux
1995 return (tsock->SetOption(SOL_SOCKET, SO_SNDBUF, &outbuf_size,
1996 sizeof(outbuf_size)) &&
1997 ret);
1998}
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.