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 <iomanip>
26#include <sstream>
27#include <vector>
28
29#include <stdlib.h>
30#include <math.h>
31#include <time.h>
32
33#ifdef __MINGW32__
34#undef IPV6STRICT // mingw FTBS fix: missing struct ip_mreq
35#include <ws2tcpip.h>
36#include <windows.h>
37#endif
38
39#ifdef __MSVC__
40#include <winsock2.h>
41#include <wx/msw/winundef.h>
42#include <ws2tcpip.h>
43#endif
44
45#ifndef _WIN32
46#include <arpa/inet.h>
47#include <netinet/tcp.h>
48#endif
49
50#include <wx/wxprec.h>
51#ifndef WX_PRECOMP
52#include <wx/wx.h>
53#endif
54
55#include <wx/tokenzr.h>
56#include <wx/datetime.h>
57
58#include <wx/socket.h>
59#include <wx/log.h>
60#include <wx/memory.h>
61#include <wx/chartype.h>
62#include <wx/wx.h>
63#include <wx/sckaddr.h>
64
67#include "model/idents.h"
69#include "model/sys_events.h"
70
71#define N_DOG_TIMEOUT 8
72
73using namespace std::literals::chrono_literals;
74
75static const int kNotFound = -1;
76
77class MrqContainer {
78public:
79 struct ip_mreq m_mrq;
80 void SetMrqAddr(unsigned int addr) {
81 m_mrq.imr_multiaddr.s_addr = addr;
82 m_mrq.imr_interface.s_addr = INADDR_ANY;
83 }
84};
85
88 : priority('\0'), source('\0'), destination('\0'), pgn(-1) {};
89
90wxDEFINE_EVENT(wxEVT_COMMDRIVER_N2K_NET, CommDriverN2KNetEvent);
91
93wxDECLARE_EVENT(wxEVT_COMMDRIVER_N2K_NET, CommDriverN2KNetEvent);
94
95class CommDriverN2KNetEvent : public wxEvent {
96public:
97 CommDriverN2KNetEvent(wxEventType commandType = wxEVT_NULL, int id = 0)
98 : wxEvent(id, commandType) {};
100
101 // accessors
102 void SetPayload(std::shared_ptr<std::vector<unsigned char>> data) {
103 m_payload = data;
104 }
105 std::shared_ptr<std::vector<unsigned char>> GetPayload() { return m_payload; }
106
107 // required for sending with wxPostEvent()
108 wxEvent* Clone() const {
109 CommDriverN2KNetEvent* newevent = new CommDriverN2KNetEvent(*this);
110 newevent->m_payload = this->m_payload;
111 return newevent;
112 };
113
114private:
115 std::shared_ptr<std::vector<unsigned char>> m_payload;
116};
117
118static uint64_t PayloadToName(const std::vector<unsigned char> payload) {
119 uint64_t name;
120 memcpy(&name, reinterpret_cast<const void*>(payload.data()), sizeof(name));
121 return name;
122}
123
124//========================================================================
125/* commdriverN2KNet implementation
126 * */
127
128#define TIMER_SOCKET_N2KNET 7339
129
130BEGIN_EVENT_TABLE(CommDriverN2KNet, wxEvtHandler)
131EVT_TIMER(TIMER_SOCKET_N2KNET, CommDriverN2KNet::OnTimerSocket)
132EVT_SOCKET(DS_SOCKET_ID, CommDriverN2KNet::OnSocketEvent)
133EVT_SOCKET(DS_SERVERSOCKET_ID, CommDriverN2KNet::OnServerSocketEvent)
134EVT_TIMER(TIMER_SOCKET_N2KNET + 1, CommDriverN2KNet::OnSocketReadWatchdogTimer)
135END_EVENT_TABLE()
136
137// CommDriverN0183Net::CommDriverN0183Net() : CommDriverN0183() {}
138
140 DriverListener& listener)
141 : CommDriverN2K(params->GetStrippedDSPort()),
142 m_params(*params),
143 m_listener(listener),
144 m_stats_timer(*this, 2s),
145 m_net_port(wxString::Format("%i", params->NetworkPort)),
146 m_net_protocol(params->NetProtocol),
147 m_sock(NULL),
148 m_tsock(NULL),
149 m_socket_server(NULL),
150 m_is_multicast(false),
151 m_txenter(0),
152 m_portstring(params->GetDSPort()),
153 m_io_select(params->IOSelect),
154 m_connection_type(params->Type),
155 m_bok(false),
156 m_circle(RX_BUFFER_SIZE_NET),
157 m_TX_available(false),
158 m_detect_count(-1) {
159 m_addr.Hostname(params->NetworkAddress);
160 m_addr.Service(params->NetworkPort);
161
162 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
163 m_driver_stats.driver_iface = params->GetStrippedDSPort();
164
165 m_socket_timer.SetOwner(this, TIMER_SOCKET_N2KNET);
166 m_socketread_watchdog_timer.SetOwner(this, TIMER_SOCKET_N2KNET + 1);
167 this->attributes["netAddress"] = params->NetworkAddress.ToStdString();
168 char port_char[10];
169 sprintf(port_char, "%d", params->NetworkPort);
170 this->attributes["netPort"] = std::string(port_char);
171 this->attributes["userComment"] = params->UserComment.ToStdString();
172 this->attributes["ioDirection"] = DsPortTypeToString(params->IOSelect);
173
174 // Prepare the wxEventHandler to accept events from the actual hardware thread
175 Bind(wxEVT_COMMDRIVER_N2K_NET, &CommDriverN2KNet::handle_N2K_MSG, this);
176
177 m_prodinfo_timer.Connect(
178 wxEVT_TIMER, wxTimerEventHandler(CommDriverN2KNet::OnProdInfoTimer), NULL,
179 this);
180
181 m_mrq_container = new MrqContainer;
182 m_bInMsg = false;
183 m_bGotESC = false;
184 m_bGotSOT = false;
185 rx_buffer = new unsigned char[RX_BUFFER_SIZE_NET + 1];
186
187 fast_messages = new FastMessageMap();
188 m_order = 0; // initialize the fast message sequence ID bits, for TX
189 m_n2k_format = N2KFormat_YD_RAW;
190
191 // Establish the power events response
192 resume_listener.Init(SystemEvents::GetInstance().evt_resume,
193 [&](ObservedEvt&) { HandleResume(); });
194
195 Open();
196}
197
198CommDriverN2KNet::~CommDriverN2KNet() {
199 delete m_mrq_container;
200 delete[] rx_buffer;
201
202 Close();
203}
204
205typedef struct {
206 std::string Model_ID;
207 char RT_flag;
209
210std::unordered_map<uint8_t, product_info> prod_info_map;
211
212bool CommDriverN2KNet::HandleMgntMsg(
213 uint64_t pgn, const std::vector<unsigned char>& payload) {
214 // Process a few N2K network management messages
215 auto name = PayloadToName(payload);
216 auto msg =
217 std::make_shared<const Nmea2000Msg>(pgn, payload, GetAddress(name));
218
219 bool b_handled = false;
220 switch (pgn) {
221 case 126996: { // Product information
222 uint8_t src_addr = payload.at(7);
223 if (src_addr == 75) return false; // skip simulator mgnt messages
224 product_info pr_info;
225 pr_info.Model_ID = std::string((char*)&payload.data()[17], 32);
226 pr_info.RT_flag = m_TX_flag;
227
228 prod_info_map[src_addr] = pr_info;
229 b_handled = true;
230 break;
231 }
232 case 59904: { // ISO request
233 uint8_t src_addr = payload.at(7);
234 b_handled = true;
235 break;
236 }
237 default:
238 break;
239 }
240 return b_handled;
241}
242
243void CommDriverN2KNet::OnProdInfoTimer(wxTimerEvent& ev) {
244 // Check the results of the PGN 126996 capture
245 bool b_found = false;
246 for (const auto& [key, value] : prod_info_map) {
247 auto prod_info = value;
248 if (prod_info.Model_ID.find("YDEN") != std::string::npos) {
249 // Found a YDEN device
250 // If this configured port is actually connector to YDEN,
251 // then the device will have marked the received TCP packet
252 // with "T" indicator. Check it.
253 if (prod_info.RT_flag == 'T') b_found = true;
254 break;
255 }
256 }
257
258 if (b_found) m_TX_available = true;
259 prod_info_map.clear();
260}
261
262void CommDriverN2KNet::handle_N2K_MSG(CommDriverN2KNetEvent& event) {
263 auto p = event.GetPayload();
264 std::vector<unsigned char>* payload = p.get();
265
266 // extract PGN
267 uint64_t pgn = 0;
268 unsigned char* c = (unsigned char*)&pgn;
269 *c++ = payload->at(3);
270 *c++ = payload->at(4);
271 *c++ = payload->at(5);
272 // memcpy(&v, &data[3], 1);
273 // printf(" %ld\n", pgn);
274
275 auto name = PayloadToName(*payload);
276 auto msg =
277 std::make_shared<const Nmea2000Msg>(pgn, *payload, GetAddress(name));
278 m_driver_stats.rx_count += payload->size();
279 m_listener.Notify(std::move(msg));
280}
281
282void CommDriverN2KNet::Open() {
283#ifdef __UNIX__
284#if wxCHECK_VERSION(3, 0, 0)
285 in_addr_t addr =
286 ((struct sockaddr_in*)GetAddr().GetAddressData())->sin_addr.s_addr;
287#else
288 in_addr_t addr =
289 ((struct sockaddr_in*)GetAddr().GetAddress()->m_addr)->sin_addr.s_addr;
290#endif
291#else
292 unsigned int addr = inet_addr(GetAddr().IPAddress().mb_str());
293#endif
294 // Create the socket
295 switch (m_net_protocol) {
296 case TCP: {
297 OpenNetworkTCP(addr);
298 break;
299 }
300 case UDP: {
301 OpenNetworkUDP(addr);
302 break;
303 }
304 default:
305 break;
306 }
307 SetOk(true);
308}
309
310void CommDriverN2KNet::OpenNetworkUDP(unsigned int addr) {
311 if (GetPortType() != DS_TYPE_OUTPUT) {
312 // We need a local (bindable) address to create the Datagram receive socket
313 // Set up the receive socket
314 wxIPV4address conn_addr;
315 conn_addr.Service(GetNetPort());
316 conn_addr.AnyAddress();
317 SetSock(
318 new wxDatagramSocket(conn_addr, wxSOCKET_NOWAIT | wxSOCKET_REUSEADDR));
319
320 // Test if address is IPv4 multicast
321 if ((ntohl(addr) & 0xf0000000) == 0xe0000000) {
322 SetMulticast(true);
323 m_mrq_container->SetMrqAddr(addr);
324 GetSock()->SetOption(IPPROTO_IP, IP_ADD_MEMBERSHIP,
325 &m_mrq_container->m_mrq,
326 sizeof(m_mrq_container->m_mrq));
327 }
328
329 GetSock()->SetEventHandler(*this, DS_SOCKET_ID);
330
331 GetSock()->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG |
332 wxSOCKET_LOST_FLAG);
333 GetSock()->Notify(TRUE);
334 GetSock()->SetTimeout(1); // Short timeout
335 m_driver_stats.available = true;
336 }
337
338 // Set up another socket for transmit
339 if (GetPortType() != DS_TYPE_INPUT) {
340 wxIPV4address tconn_addr;
341 tconn_addr.Service(0); // use ephemeral out port
342 tconn_addr.AnyAddress();
343 SetTSock(
344 new wxDatagramSocket(tconn_addr, wxSOCKET_NOWAIT | wxSOCKET_REUSEADDR));
345 // Here would be the place to disable multicast loopback
346 // but for consistency with broadcast behaviour, we will
347 // instead rely on setting priority levels to ignore
348 // sentences read back that have just been transmitted
349 if ((!GetMulticast()) && (GetAddr().IPAddress().EndsWith("255"))) {
350 int broadcastEnable = 1;
351 bool bam = GetTSock()->SetOption(
352 SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
353 }
354 m_driver_stats.available = true;
355 }
356
357 // In case the connection is lost before acquired....
358 SetConnectTime(wxDateTime::Now());
359}
360
361void CommDriverN2KNet::OpenNetworkTCP(unsigned int addr) {
362 int isServer = ((addr == INADDR_ANY) ? 1 : 0);
363 wxLogMessage(wxString::Format("Opening TCP Server %d", isServer));
364
365 if (isServer) {
366 SetSockServer(new wxSocketServer(GetAddr(), wxSOCKET_REUSEADDR));
367 } else {
368 SetSock(new wxSocketClient());
369 }
370
371 if (isServer) {
372 GetSockServer()->SetEventHandler(*this, DS_SERVERSOCKET_ID);
373 GetSockServer()->SetNotify(wxSOCKET_CONNECTION_FLAG);
374 GetSockServer()->Notify(TRUE);
375 GetSockServer()->SetTimeout(1); // Short timeout
376 } else {
377 GetSock()->SetEventHandler(*this, DS_SOCKET_ID);
378 int notify_flags = (wxSOCKET_CONNECTION_FLAG | wxSOCKET_LOST_FLAG);
379 if (GetPortType() != DS_TYPE_INPUT) notify_flags |= wxSOCKET_OUTPUT_FLAG;
380 if (GetPortType() != DS_TYPE_OUTPUT) notify_flags |= wxSOCKET_INPUT_FLAG;
381 GetSock()->SetNotify(notify_flags);
382 GetSock()->Notify(TRUE);
383 GetSock()->SetTimeout(1); // Short timeout
384
385 SetBrxConnectEvent(false);
386 GetSocketTimer()->Start(100, wxTIMER_ONE_SHOT); // schedule a connection
387 }
388
389 // In case the connection is lost before acquired....
390 SetConnectTime(wxDateTime::Now());
391}
392
393void CommDriverN2KNet::OnSocketReadWatchdogTimer(wxTimerEvent& event) {
394 m_dog_value--;
395
396 if (m_dog_value <= 0) { // No receive in n seconds
397 if (GetParams().NoDataReconnect) {
398 // Reconnect on NO DATA is true, so try to reconnect now.
399 if (GetProtocol() == TCP) {
400 wxSocketClient* tcp_socket = dynamic_cast<wxSocketClient*>(GetSock());
401 if (tcp_socket) tcp_socket->Close();
402
403 int n_reconnect_delay = wxMax(N_DOG_TIMEOUT - 2, 2);
404 wxLogMessage(wxString::Format(" Reconnection scheduled in %d seconds.",
405 n_reconnect_delay));
406 GetSocketTimer()->Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT);
407
408 // Stop DATA watchdog, will be restarted on successful connection.
409 GetSocketThreadWatchdogTimer()->Stop();
410 }
411 }
412 }
413}
414
415void CommDriverN2KNet::OnTimerSocket() {
416 // Attempt a connection
417 wxSocketClient* tcp_socket = dynamic_cast<wxSocketClient*>(GetSock());
418 if (tcp_socket) {
419 if (tcp_socket->IsDisconnected()) {
420 wxLogDebug(" Attempting reconnection...");
421 SetBrxConnectEvent(false);
422 // Stop DATA watchdog, may be restarted on successful connection.
423 GetSocketThreadWatchdogTimer()->Stop();
424 tcp_socket->Connect(GetAddr(), FALSE);
425
426 // schedule another connection attempt, in case this one fails
427 int n_reconnect_delay = N_DOG_TIMEOUT;
428 GetSocketTimer()->Start(n_reconnect_delay * 1000, wxTIMER_ONE_SHOT);
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 % 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 = false;
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(NULL);
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 uint8_t len = 8;
1481
1482 attr |= ((uint16_t)0x06) << 12;
1483 attr |= ((uint16_t)payload.size()) << 8;
1484 attr |= (uint16_t)0xFF;
1485 attr |= 0x8000; // S bit set to 1
1486
1487 wxString sattr;
1488 sattr.Printf("%04X,", attr);
1489 std::string ssattr = sattr.ToStdString();
1490 for (unsigned char c : ssattr) {
1491 out_vec.push_back(c);
1492 }
1493 // Data payload
1494 char tv[3];
1495 for (auto rit = payload.rbegin(); rit != payload.rend(); ++rit) {
1496 snprintf(tv, 3, "%02X", *rit);
1497 out_vec.push_back(tv[0]);
1498 out_vec.push_back(tv[1]);
1499 }
1500 // CRC
1501 uint8_t crc = 0;
1502 for (auto ci = ++out_vec.begin(); ci != out_vec.end(); ci++) {
1503 crc ^= *ci;
1504 }
1505 out_vec.push_back('*');
1506 snprintf(tv, 3, "%02X", crc);
1507 out_vec.push_back(tv[0]);
1508 out_vec.push_back(tv[1]);
1509
1510 // term
1511 out_vec.push_back(0x0d);
1512 out_vec.push_back(0x0a);
1513 // DBG: std::cout << std::string(out_vec.begin(), out_vec.end()) <<
1514 // std::endl << std::flush;
1515 break;
1516 }
1517 default:
1518 break;
1519 }
1520 return out_vec;
1521}
1522
1523std::vector<std::vector<unsigned char>> CommDriverN2KNet::GetTxVector(
1524 const std::shared_ptr<const Nmea2000Msg>& msg,
1525 const std::shared_ptr<const NavAddr2000>& dest_addr) {
1526 std::vector<std::vector<unsigned char>> tx_vector;
1527
1528 // Branch based on detected network data format currently in use
1529 switch (m_n2k_format) {
1530 case N2KFormat_YD_RAW:
1531 break;
1532 case N2KFormat_Actisense_RAW_ASCII: {
1533 // 00:34:02.718 R 15FD0800 FF 00 01 CA 6F FF FF FF
1534 if (!IsFastMessagePGN(msg->PGN.pgn) && msg->payload.size() <= 8) {
1535 // Single packet message
1536 std::vector<unsigned char> header_vec;
1537 std::vector<unsigned char> out_vec;
1538
1539 // Craft the canID
1540 // No need to specify the source address
1541 // The TX frame will adopt the gateway's claimed N2K address.
1542 unsigned long can_id =
1543 BuildCanID(msg->priority, 0, dest_addr->address, msg->PGN.pgn);
1544
1545 std::stringstream ss;
1546 ss << std::setfill('0') << std::setw(8) << std::hex << can_id;
1547 for (unsigned char s : ss.str()) header_vec.push_back(s);
1548 header_vec.push_back(' ');
1549
1550 // constant header
1551 for (unsigned char s : header_vec) out_vec.push_back(s);
1552
1553 // single data packet
1554 std::string ssdata;
1555 for (unsigned int k = 0; k < msg->payload.size(); k++) {
1556 char tb[4];
1557 snprintf(tb, 4, "%02X ", msg->payload.data()[k]);
1558 ssdata += tb;
1559 }
1560 for (unsigned char s : ssdata) out_vec.push_back(s);
1561 out_vec.pop_back(); // drop the last space character
1562
1563 out_vec.push_back(0x0d); // terminate the string
1564 out_vec.push_back(0x0a);
1565
1566 tx_vector.push_back(out_vec);
1567 } else {
1568 std::vector<unsigned char> header_vec;
1569 std::vector<unsigned char> out_vec;
1570
1571 // No Need to create a timestamp or frame R/T indicator
1572#if 0
1573 // time header
1574 wxDateTime now = wxDateTime::Now();
1575 wxString stime = now.Format("%H:%M:%S");
1576 stime += ".000 ";
1577 std::string sstime = stime.ToStdString();
1578 for (unsigned char s : sstime) header_vec.push_back(s);
1579
1580 // Tx indicator
1581 header_vec.push_back('T');
1582 header_vec.push_back(' ');
1583#endif
1584
1585 // Craft the canID
1586 // No need to specify the source address
1587 // The TX frame will adopt the gateway's claimed N2K address.
1588 unsigned long can_id =
1589 BuildCanID(msg->priority, 0, dest_addr->address, msg->PGN.pgn);
1590 std::stringstream ss;
1591 ss << std::setfill('0') << std::setw(8) << std::hex << can_id;
1592 for (unsigned char s : ss.str()) header_vec.push_back(s);
1593 header_vec.push_back(' ');
1594
1595 // format the required number of short packets, in a loop
1596 int payload_size = msg->payload.size();
1597 unsigned char temp[8]; // {0,0,0,0,0,0,0,0};
1598 int cur = 0;
1599 int nframes =
1600 (payload_size > 6 ? (payload_size - 6 - 1) / 7 + 1 + 1 : 1);
1601 bool result = true;
1602 for (int i = 0; i < nframes && result; i++) {
1603 temp[0] = i | m_order; // frame counter
1604 if (i == 0) {
1605 temp[1] = msg->payload.size(); // total bytes in fast packet
1606 // send the first 6 bytes
1607 for (int j = 2; j < 8; j++) {
1608 temp[j] = msg->payload.data()[cur];
1609 cur++;
1610 }
1611 } else {
1612 int j = 1;
1613 // send the next 7 data bytes
1614 for (; j < 8 && cur < payload_size; j++) {
1615 temp[j] = msg->payload.data()[cur];
1616 cur++;
1617 }
1618 for (; j < 8; j++) {
1619 temp[j] = 0xff;
1620 }
1621 }
1622
1623 out_vec.clear();
1624
1625 // constant header
1626 for (unsigned char s : header_vec) out_vec.push_back(s);
1627
1628 // data, per packet
1629 std::string ssdata;
1630 for (unsigned int k = 0; k < 8; k++) {
1631 char tb[4];
1632 snprintf(tb, 4, "%02X ", temp[k]);
1633 ssdata += tb;
1634 }
1635 for (unsigned char s : ssdata) out_vec.push_back(s);
1636 out_vec.pop_back(); // drop the last space character
1637
1638 out_vec.push_back(0x0d); // terminate the string
1639 out_vec.push_back(0x0a);
1640
1641 tx_vector.push_back(out_vec);
1642 } // for loop
1643 }
1644 } break;
1645 case N2KFormat_Actisense_N2K_ASCII: {
1646 // Source: Actisense own documentation `NMEA 2000 ASCII Output
1647 // format.docx`
1648 //
1649 // Ahhmmss.ddd <SS><DD><P> <PPPPP> b0b1b2b3b4b5b6b7.....bn<CR><LF>
1650 // A = message is N2K or J1939 message
1651 // 173321.107 - time 17:33:21.107
1652 // <SS> - source address
1653 // <DD> - destination address
1654 // <P> - priority
1655 // <PPPPP> - PGN number
1656 // b0b1b2b3b4b5b6b7.....bn - data payload in hex. NB: ISO TP payload could
1657 // be up to 1786 bytes
1658 //
1659 // Example: `A173321.107 23FF7 1F513 012F3070002F30709F\n`
1660 // 1 2 3 4
1661
1662 std::vector<unsigned char> ovec;
1663
1664 // Create the time field
1665 wxDateTime now = wxDateTime::Now();
1666 wxString stime = now.Format("%H%M%S");
1667 stime += ".000 ";
1668 std::string sstime = stime.ToStdString();
1669 ovec.push_back('A');
1670 for (unsigned char s : sstime) ovec.push_back(s);
1671
1672 // src/dest/prio field
1673 wxString sdp;
1674 sdp.Printf("%02X%02X%1X ",
1675 1, // source
1676 (unsigned char)dest_addr->address,
1677 (unsigned char)msg->priority);
1678 std::string ssdp = sdp.ToStdString();
1679 for (unsigned char s : ssdp) ovec.push_back(s);
1680
1681 // PGN field
1682 wxString spgn;
1683 spgn.Printf("%05X ", (int)msg->PGN.pgn);
1684 std::string sspgn = spgn.ToStdString();
1685 for (unsigned char s : sspgn) ovec.push_back(s);
1686
1687 // Data payload
1688 std::string sspl;
1689 char tv[3];
1690 for (unsigned char d : msg->payload) {
1691 snprintf(tv, 3, "%02X", d);
1692 sspl += tv;
1693 }
1694 for (unsigned char s : sspl) ovec.push_back(s);
1695
1696 // term
1697 ovec.push_back(0x0d);
1698 ovec.push_back(0x0a);
1699
1700 // form the result
1701 tx_vector.push_back(ovec);
1702
1703 break;
1704 }
1705 case N2KFormat_MiniPlex: {
1706 std::vector<unsigned char> ovec;
1707 if (!IsFastMessagePGN(msg->PGN.pgn) && msg->payload.size() < 8) {
1708 // Single packet
1709 } else {
1710 size_t cur = 0;
1711 size_t nframes =
1712 (msg->payload.size() > 6 ? (msg->payload.size() - 6 - 1) / 7 + 1 + 1
1713 : 1);
1714 for (size_t i = 0; i < nframes; i++) {
1715 ovec.push_back('$');
1716 ovec.push_back('M');
1717 ovec.push_back('X');
1718 ovec.push_back('P');
1719 ovec.push_back('G');
1720 ovec.push_back('N');
1721 ovec.push_back(',');
1722 // PGN field
1723 wxString spgn;
1724 spgn.Printf("%06X,", (int)msg->PGN.pgn);
1725 std::string sspgn = spgn.ToStdString();
1726 for (unsigned char c : sspgn) {
1727 ovec.push_back(c);
1728 }
1729 // Attribute word
1730 uint16_t attr = 0;
1731 uint8_t len = 8;
1732 if (i == nframes - 1) {
1733 // TODO Check this
1734 // len = msg->payload.size() + 1 - 6 - (nframes - 2) * 7;
1735 }
1736
1737 attr |= ((uint16_t)((uint8_t)msg->priority & 0x07)) << 12;
1738 attr |= ((uint16_t)len) << 8;
1739 attr |= (uint16_t)dest_addr->address;
1740 attr |= 0x8000; // S bit set to 1
1741
1742 wxString sattr;
1743 sattr.Printf("%04X,", attr);
1744 std::string ssattr = sattr.ToStdString();
1745 for (unsigned char c : ssattr) {
1746 ovec.push_back(c);
1747 }
1748 // Data payload
1749 char tv[3];
1750 uint8_t databytes = i == 0 ? len - 2 : len - 1;
1751 std::vector<unsigned char> payload;
1752 for (uint8_t j = 0; j < databytes; j++) {
1753 payload.push_back(msg->payload[cur]);
1754 cur++;
1755 }
1756
1757 // Buffer the data to 7 bytes, if necessary, buffer at start.
1758 int psize = payload.size();
1759 while ((i > 0) && (psize < 7)) {
1760 ovec.push_back('F');
1761 ovec.push_back('F');
1762 psize++;
1763 }
1764
1765 // Buffer the actual payload bytes
1766 for (auto rit = payload.rbegin(); rit != payload.rend(); ++rit) {
1767 snprintf(tv, 3, "%02X", *rit);
1768 ovec.push_back(tv[0]);
1769 ovec.push_back(tv[1]);
1770 }
1771 if (i == 0) { // First frame contains the total payload length
1772 snprintf(tv, 3, "%02X", (uint8_t)msg->payload.size());
1773 ovec.push_back(tv[0]);
1774 ovec.push_back(tv[1]);
1775 }
1776 // frame counter
1777 snprintf(tv, 3, "%02X", (uint8_t)i | m_order);
1778 ovec.push_back(tv[0]);
1779 ovec.push_back(tv[1]);
1780
1781 // CRC
1782 uint8_t crc = 0;
1783 for (auto ci = ++ovec.begin(); ci != ovec.end(); ci++) {
1784 crc ^= *ci;
1785 }
1786 ovec.push_back('*');
1787 snprintf(tv, 3, "%02X", crc);
1788 ovec.push_back(tv[0]);
1789 ovec.push_back(tv[1]);
1790
1791 // term
1792 ovec.push_back(0x0d);
1793 ovec.push_back(0x0a);
1794
1795 // DBG: std::cout << std::string(ovec.begin(), ovec.end()) <<
1796 // std::endl << std::flush;
1797
1798 // form the result
1799 tx_vector.push_back(ovec);
1800 ovec.clear();
1801 }
1802 break;
1803 }
1804 }
1805 case N2KFormat_Actisense_N2K:
1806 break;
1807 case N2KFormat_Actisense_RAW:
1808 break;
1809 case N2KFormat_Actisense_NGT:
1810 break;
1811 case N2KFormat_SeaSmart:
1812 break;
1813 default:
1814 break;
1815 }
1816
1817 // update the fast message Sequence ID bits
1818 m_order = (m_order + 0x20) & 0xE0;
1819
1820 return tx_vector;
1821}
1822
1823bool CommDriverN2KNet::PrepareForTX() {
1824 // We need to determine several items before TX operations can commence.
1825 // 1. Is the gateway configured at my ip present, and if so, which of
1826 // the two supported gateways is it? (YDEN-type, or Actisense-type.
1827 // 2. If Actisense type, we need to infer the N2K source address it has
1828 // claimed, so that we can use that address for our TX operations.
1829
1830 // BASIC ASSUMPTION: There is (or has been) enough network traffic to
1831 // allow occurate determination of data format currently in use
1832
1833 bool b_found = false;
1834
1835 // Step 1.1
1836 // If the detected data format is N2KFormat_Actisense_N2K_ASCII,
1837 // then we are clearly connected to an actisense device.
1838 // Nothing else need be done.
1839
1840 if (m_n2k_format == N2KFormat_Actisense_N2K_ASCII) return true;
1841
1842 // Step 1.2
1843 // If the detected data format is N2KFormat_MiniPlex,
1844 // then we are clearly connected to a MiniPlex.
1845 // Nothing else need be done.
1846
1847 if (m_n2k_format == N2KFormat_MiniPlex) return true;
1848
1849 // Step 1.2
1850 // If the detected data format is N2KFormat_SeaSmart,
1851 // then we can't transmit.
1852 if (m_n2k_format == N2KFormat_SeaSmart) return false;
1853
1854 // Step 2
1855
1856 // Assume that the gateway is YDEN type, RAW mode. Verify if true.
1857 // Logic: Actisense gateway will not respond to TX_FORMAT_YDEN,
1858 // so if we get sensible response, the gw must be YDEN type.
1859
1860 // Already tested and found available?
1861 if (m_TX_available)
1862 return true;
1863 else {
1864 // Send a broadcast request for PGN 126996, Product Information
1865 std::vector<unsigned char> payload;
1866 payload.push_back(0x14);
1867 payload.push_back(0xF0);
1868 payload.push_back(0x01);
1869
1870 std::vector<std::vector<unsigned char>> out_data;
1871 std::vector<unsigned char> msg_vec =
1872 MakeSimpleOutMsg(N2KFormat_YD_RAW, 59904, payload);
1873 out_data.push_back(msg_vec);
1874 SendSentenceNetwork(out_data);
1875
1876 // Wait some time, and study results
1877 m_prodinfo_timer.Start(200, true);
1878 }
1879
1880 // No acceptable TX device found
1881 return false;
1882}
1883
1884bool CommDriverN2KNet::SendN2KNetwork(
1885 const std::shared_ptr<const Nmea2000Msg>& msg,
1886 const std::shared_ptr<const NavAddr2000>& addr) {
1887 PrepareForTX();
1888
1889 std::vector<std::vector<unsigned char>> out_data = GetTxVector(msg, addr);
1890 SendSentenceNetwork(out_data);
1891 m_driver_stats.tx_count += msg->payload.size();
1892
1893 // Create internal message and notify upper layers
1894 std::vector<unsigned char> msg_payload = PrepareLogPayload(msg, addr);
1895 m_listener.Notify(
1896 std::make_shared<const Nmea2000Msg>(msg->PGN.pgn, msg_payload, addr));
1897
1898 return true;
1899};
1900
1901bool CommDriverN2KNet::SendSentenceNetwork(
1902 const std::vector<std::vector<unsigned char>>& payload) {
1903 if (m_txenter)
1904 return false; // do not allow recursion, could happen with non-blocking
1905 // sockets
1906 m_txenter++;
1907
1908 bool ret = true;
1909 wxDatagramSocket* udp_socket;
1910 switch (GetProtocol()) {
1911 case TCP:
1912 for (const std::vector<unsigned char>& v : payload) {
1913 if (GetSock() && GetSock()->IsOk()) {
1914 m_driver_stats.available = true;
1915 // printf("---%s", v.data());
1916 GetSock()->Write(v.data(), v.size());
1917 m_dog_value = N_DOG_TIMEOUT; // feed the dog
1918 if (GetSock()->Error()) {
1919 if (GetSockServer()) {
1920 GetSock()->Destroy();
1921 SetSock(NULL);
1922 } else {
1923 wxSocketClient* tcp_socket =
1924 dynamic_cast<wxSocketClient*>(GetSock());
1925 if (tcp_socket) tcp_socket->Close();
1926 if (!GetSocketTimer()->IsRunning())
1927 GetSocketTimer()->Start(
1928 5000, wxTIMER_ONE_SHOT); // schedule a reconnect
1929 GetSocketThreadWatchdogTimer()->Stop();
1930 }
1931 ret = false;
1932 }
1933 wxMilliSleep(2);
1934 } else {
1935 m_driver_stats.available = false;
1936 ret = false;
1937 }
1938 }
1939 break;
1940 case UDP:
1941#if 0
1942 udp_socket = dynamic_cast<wxDatagramSocket*>(GetTSock());
1943 if (udp_socket && udp_socket->IsOk()) {
1944 udp_socket->SendTo(GetAddr(), payload.mb_str(), payload.size());
1945 if (udp_socket->Error()) ret = false;
1946 } else
1947 ret = false;
1948#endif
1949 break;
1950
1951 case GPSD:
1952 default:
1953 ret = false;
1954 break;
1955 }
1956 m_txenter--;
1957 return ret;
1958}
1959
1960void CommDriverN2KNet::Close() {
1961 wxLogMessage(wxString::Format("Closing NMEA NetworkDataStream %s",
1962 GetNetPort().c_str()));
1963 m_stats_timer.Stop();
1964 // Kill off the TCP Socket if alive
1965 if (m_sock) {
1966 if (m_is_multicast)
1967 m_sock->SetOption(IPPROTO_IP, IP_DROP_MEMBERSHIP, &m_mrq_container->m_mrq,
1968 sizeof(m_mrq_container->m_mrq));
1969 m_sock->Notify(FALSE);
1970 m_sock->Destroy();
1971 m_driver_stats.available = false;
1972 }
1973
1974 if (m_tsock) {
1975 m_tsock->Notify(FALSE);
1976 m_tsock->Destroy();
1977 }
1978
1979 if (m_socket_server) {
1980 m_socket_server->Notify(FALSE);
1981 m_socket_server->Destroy();
1982 }
1983
1984 m_socket_timer.Stop();
1985 m_socketread_watchdog_timer.Stop();
1986}
1987
1988bool CommDriverN2KNet::SetOutputSocketOptions(wxSocketBase* tsock) {
1989 int ret;
1990
1991 // Disable nagle algorithm on outgoing connection
1992 // Doing this here rather than after the accept() is
1993 // pointless on platforms where TCP_NODELAY is
1994 // not inherited. However, none of OpenCPN's currently
1995 // supported platforms fall into that category.
1996
1997 int nagleDisable = 1;
1998 ret = tsock->SetOption(IPPROTO_TCP, TCP_NODELAY, &nagleDisable,
1999 sizeof(nagleDisable));
2000
2001 // Drastically reduce the size of the socket output buffer
2002 // so that when client goes away without properly closing, the stream will
2003 // quickly fill the output buffer, and thus fail the write() call
2004 // within a few seconds.
2005 unsigned long outbuf_size = 1024; // Smallest allowable value on Linux
2006 return (tsock->SetOption(SOL_SOCKET, SO_SNDBUF, &outbuf_size,
2007 sizeof(outbuf_size)) &&
2008 ret);
2009}
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.
Driver registration container, a singleton.
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.