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