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