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