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