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