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