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