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