OpenCPN Partial API docs
Loading...
Searching...
No Matches
comm_drv_n2k_serial.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2022 by David Register *
3 * Copyright (C) 2022 Alec Leamas *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
25#include <mutex> // std::mutex
26#include <queue> // std::queue
27#include <vector>
28
29// For compilers that support precompilation, includes "wx.h".
30#include <wx/wxprec.h>
31#ifndef WX_PRECOMP
32#include <wx/wx.h>
33#endif
34
35#include <wx/log.h>
36
37#include "model/comm_buffers.h"
41#include "model/logger.h"
43
44#include "N2kMsg.h"
45
46/* Copied from canboat Project
47 * https://github.com/canboat/canboat
48 *
49 * The following startup command reverse engineered from Actisense NMEAreader.
50 * It instructs the NGT1 to clear its PGN message TX list, thus it starts
51 * sending all PGNs that it knows about.
52 */
53static unsigned char NGT_STARTUP_SEQ[] = {
54 0x11, /* msg byte 1, meaning ? */
55 0x02, /* msg byte 2, meaning ? */
56 0x00 /* msg byte 3, meaning ? */
57};
58
59static std::vector<unsigned char> BufferToActisenseFormat(tN2kMsg& msg);
60
61class CommDriverN2KSerialEvent; // fwd
62
63class CommDriverN2KSerialThread : public wxThread {
64public:
66 const wxString& PortName,
67 const wxString& strBaudRate);
68
70 void* Entry();
71 bool SetOutMsg(const std::vector<unsigned char>& load);
72 void OnExit();
73 DriverStats GetStats() const;
74
75private:
76#ifndef __ANDROID__
77 serial::Serial m_serial;
78#endif
79 void ThreadMessage(const wxString& msg);
80 bool OpenComPortPhysical(const wxString& com_name, int baud_rate);
81 void CloseComPortPhysical();
82 size_t WriteComPortPhysical(std::vector<unsigned char> msg);
83 size_t WriteComPortPhysical(unsigned char* msg, size_t length);
84 void SetGatewayOperationMode();
85
86 CommDriverN2KSerial* m_pParentDriver;
87 wxString m_PortName;
88 wxString m_FullPortName;
89
90 unsigned char* put_ptr;
91 unsigned char* tak_ptr;
92
93 unsigned char* rx_buffer;
94
95 int m_baud;
96 int m_n_timeout;
97
99 DriverStats m_driver_stats;
100 mutable std::mutex m_stats_mutex;
101#ifdef __WXMSW__
102 HANDLE m_hSerialComm;
103 bool m_nl_found;
104#endif
105};
106
108wxDECLARE_EVENT(wxEVT_COMMDRIVER_N2K_SERIAL, CommDriverN2KSerialEvent);
109
110class CommDriverN2KSerialEvent : public wxEvent {
111public:
112 CommDriverN2KSerialEvent(wxEventType commandType = wxEVT_NULL, int id = 0)
113 : wxEvent(id, commandType) {};
115
116 // accessors
117 void SetPayload(std::shared_ptr<std::vector<unsigned char>> data) {
118 m_payload = data;
119 }
120 std::shared_ptr<std::vector<unsigned char>> GetPayload() { return m_payload; }
121
122 // required for sending with wxPostEvent()
123 wxEvent* Clone() const {
125 newevent->m_payload = this->m_payload;
126 return newevent;
127 };
128
129private:
130 std::shared_ptr<std::vector<unsigned char>> m_payload;
131};
132
133//========================================================================
134/* commdriverN2KSerial implementation
135 * */
136
137wxDEFINE_EVENT(wxEVT_COMMDRIVER_N2K_SERIAL, CommDriverN2KSerialEvent);
138
139CommDriverN2KSerial::CommDriverN2KSerial(const ConnectionParams* params,
140 DriverListener& listener)
141 : CommDriverN2K(params->GetStrippedDSPort()),
142 m_Thread_run_flag(-1),
143 m_params(*params),
144 m_bok(false),
145 m_portstring(params->GetDSPort()),
146 m_pSecondary_Thread(NULL),
147 m_listener(listener),
148 m_stats_timer(*this, 2s),
149 m_closing(false) {
150 m_BaudRate = wxString::Format("%i", params->Baudrate), SetSecThreadInActive();
151 m_manufacturers_code = 0;
152 m_got_mfg_code = false;
153 this->attributes["canAddress"] = std::string("-1");
154 this->attributes["userComment"] = params->UserComment.ToStdString();
155 this->attributes["ioDirection"] = DsPortTypeToString(params->IOSelect);
156
157 // Prepare the wxEventHandler to accept events from the actual hardware thread
158 Bind(wxEVT_COMMDRIVER_N2K_SERIAL, &CommDriverN2KSerial::handle_N2K_SERIAL_RAW,
159 this);
160
161 // Dummy Driver Stats, may be polled before worker thread is active
162 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
163 m_driver_stats.driver_iface = m_params.GetStrippedDSPort();
164 m_driver_stats.available = false;
165
166 Open();
167
168 wxMilliSleep(100);
169 GetMfgCode();
170
171 // Initialize the device clearing all rx/tx filterx
172 SendMgmtMsg(NGT_STARTUP_SEQ, sizeof(NGT_STARTUP_SEQ), 0x11, 0, NULL);
173}
174
175CommDriverN2KSerial::~CommDriverN2KSerial() { Close(); }
176
178 if (m_closing) return m_driver_stats;
179
180#ifndef ANDROID
181 if (m_pSecondary_Thread)
182 return m_pSecondary_Thread->GetStats();
183 else
184#endif
185 return m_driver_stats;
186}
187
188bool CommDriverN2KSerial::Open() {
189 wxString comx;
190 comx = m_params.GetDSPort().AfterFirst(':'); // strip "Serial:"
191
192 comx =
193 comx.BeforeFirst(' '); // strip off any description provided by Windows
194
195#ifndef ANDROID
196 // Kick off the RX thread
197 SetSecondaryThread(new CommDriverN2KSerialThread(this, comx, m_BaudRate));
198 SetThreadRunFlag(1);
199 GetSecondaryThread()->Run();
200#endif
201
202 return true;
203}
204
205void CommDriverN2KSerial::Close() {
206 wxLogMessage(wxString::Format("Closing N2K Driver %s", m_portstring.c_str()));
207
208 m_stats_timer.Stop();
209 m_closing = true;
210
211 // Kill off the Secondary RX Thread if alive
212 if (m_pSecondary_Thread) {
213 if (m_bsec_thread_active) // Try to be sure thread object is still alive
214 {
215 wxLogMessage("Stopping Secondary Thread");
216
217 m_Thread_run_flag = 0;
218 int tsec = 10;
219 while ((m_Thread_run_flag >= 0) && (tsec--)) wxSleep(1);
220
221 wxString msg;
222 if (m_Thread_run_flag < 0)
223 msg.Printf("Stopped in %d sec.", 10 - tsec);
224 else
225 msg.Printf("Not Stopped after 10 sec.");
226 wxLogMessage(msg);
227 }
228
229 m_pSecondary_Thread = NULL;
230 m_bsec_thread_active = false;
231 }
232}
233static uint64_t PayloadToName(const std::vector<unsigned char> payload) {
234 uint64_t name;
235 memcpy(&name, reinterpret_cast<const void*>(payload.data()), sizeof(name));
236 return name;
237}
238
239bool CommDriverN2KSerial::SendMessage(std::shared_ptr<const NavMsg> msg,
240 std::shared_ptr<const NavAddr> addr) {
241 if (m_closing) return false;
242 if (!msg) return false;
243
244#ifndef ANDROID
245
246 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
247 std::vector<uint8_t> load = msg_n2k->payload;
248
249 uint64_t _pgn = msg_n2k->PGN.pgn;
250 auto destination_address = std::static_pointer_cast<const NavAddr2000>(addr);
251
252 tN2kMsg N2kMsg; // automatically sets destination 255
253 N2kMsg.SetPGN(_pgn);
254 N2kMsg.Priority = msg_n2k->priority;
255 if (destination_address) N2kMsg.Destination = destination_address->address;
256
257 for (size_t i = 0; i < load.size(); i++) N2kMsg.AddByte(load.at(i)); // data
258
259 const std::vector<uint8_t> acti_pkg = BufferToActisenseFormat(N2kMsg);
260
261 // Create the internal message and notify upper layers
262 std::vector<unsigned char> msg_payload;
263 for (size_t i = 2; i < acti_pkg.size() - 2; i++)
264 msg_payload.push_back(acti_pkg[i]);
265 auto name = PayloadToName(load);
266 m_listener.Notify(
267 std::make_shared<const Nmea2000Msg>(_pgn, msg_payload, GetAddress(name)));
268
269 if (GetSecondaryThread()) {
270 if (IsSecThreadActive()) {
271 int retry = 10;
272 while (retry) {
273 if (GetSecondaryThread()->SetOutMsg(acti_pkg))
274 return true;
275 else
276 retry--;
277 }
278 return false; // could not send after several tries....
279 } else
280 return false;
281 }
282#endif
283 return true;
284}
285
286void CommDriverN2KSerial::ProcessManagementPacket(
287 std::vector<unsigned char>* payload) {
288 if (payload->at(2) != 0xF2) { // hearbeat
289 // printf(" pl ");
290 // for (unsigned int i = 0; i < payload->size(); i++)
291 // printf("%02X ", payload->at(i));
292 // printf("\n");
293 }
294
295 switch (payload->at(2)) {
296 case 0x47:
297 m_bmg47_resp = true;
298 break;
299 case 0x01:
300 m_bmg01_resp = true;
301 break;
302 case 0x4B:
303 m_bmg4B_resp = true;
304 break;
305 case 0x041: {
306 m_bmg41_resp = true;
307 if (payload->at(3) == 0x02) { // ASCII device_common_name
308 std::string device_common_name;
309 for (unsigned int i = 0; i < 32; i++) {
310 device_common_name += payload->at(i + 14);
311 }
312 device_common_name += '\0';
313 m_device_common_name = device_common_name;
314 }
315 break;
316 }
317 case 0x042: {
318 m_bmg42_resp = true;
319 unsigned char name[8];
320 for (unsigned int i = 0; i < 8; i++) name[i] = payload->at(i + 15);
321
322 memcpy((void*)&NAME, name, 8);
323 // Extract the manufacturers code
324 int* f1 = (int*)&NAME;
325 int f1d = *f1;
326 m_manufacturers_code = f1d >> 21;
327 break;
328 }
329
330 default:
331 break;
332 }
333}
334
335void CommDriverN2KSerial::handle_N2K_SERIAL_RAW(
337 auto p = event.GetPayload();
338
339 std::vector<unsigned char>* payload = p.get();
340
341 if (payload->at(0) == 0xA0) {
342 ProcessManagementPacket(payload);
343 return;
344 }
345
346 // If port INPUT is not set, filter the mesage here
347 if (m_params.IOSelect != DS_TYPE_OUTPUT) {
348 // extract PGN
349 uint64_t pgn = 0;
350 unsigned char* c = (unsigned char*)&pgn;
351 *c++ = payload->at(3);
352 *c++ = payload->at(4);
353 *c++ = payload->at(5);
354
355 auto name = PayloadToName(*payload);
356 auto msg =
357 std::make_shared<const Nmea2000Msg>(pgn, *payload, GetAddress(name));
358
359 m_listener.Notify(std::move(msg));
360 }
361}
362
363int CommDriverN2KSerial::GetMfgCode() {
364 unsigned char request_name[] = {0x42};
365 int ni = SendMgmtMsg(request_name, sizeof(request_name), 0x41, 2000,
366 &m_bmg42_resp);
367 if (ni) return ni; // Not responding, return error so upper retries.
368 m_got_mfg_code = true;
369 return 0;
370}
371
372int CommDriverN2KSerial::SendMgmtMsg(unsigned char* string, size_t string_size,
373 unsigned char cmd_code, int timeout_msec,
374 bool* response_flag) {
375#ifndef ANDROID
376 // Make a message
377 int byteSum = 0;
378 uint8_t CheckSum;
379 std::vector<unsigned char> msg;
380
381 msg.push_back(ESCAPE);
382 msg.push_back(STARTOFTEXT);
383 msg.push_back(0xA1);
384 byteSum += 0xA1;
385 msg.push_back(string_size); // payload length
386 byteSum += string_size;
387
388 for (unsigned int i = 0; i < string_size; i++) {
389 if (string[i] == ESCAPE) msg.push_back(string[i]);
390 msg.push_back(string[i]);
391 byteSum += string[i];
392 }
393
394 // checksum
395 byteSum %= 256;
396 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
397 msg.push_back(CheckSum);
398
399 msg.push_back(ESCAPE);
400 msg.push_back(ENDOFTEXT);
401
402 // send it out
403
404 if (response_flag) *response_flag = false; // prime the response detector
405
406 // Send the msg
407 bool bsent = false;
408 bool not_done = true;
409 int ntry_outer = 10;
410 while (not_done) {
411 if (GetSecondaryThread() && IsSecThreadActive()) {
412 int retry = 10;
413 while (retry) {
414 if (GetSecondaryThread()->SetOutMsg(msg)) {
415 bsent = true;
416 not_done = false;
417 break;
418 } else
419 retry--;
420 }
421 } else {
422 wxMilliSleep(100);
423 if (ntry_outer-- <= 0) not_done = false;
424 }
425 }
426
427 if (!bsent) return 1;
428
429 bool bOK = false;
430 if (timeout_msec) {
431 int timeout = timeout_msec;
432 while (timeout > 0) {
433 wxYieldIfNeeded();
434 wxMilliSleep(100);
435 if (response_flag) {
436 if (*response_flag) {
437 bOK = true;
438 break;
439 }
440 }
441 timeout -= 100;
442 }
443 } else
444 bOK = true;
445
446 if (!bOK) {
447 // printf( "***Err-1\n");
448 return 1;
449 }
450 // else
451 // printf("***OK-1 %d\n", timeout);
452#endif
453 return 0;
454}
455
456int CommDriverN2KSerial::SetTXPGN(int pgn) {
457 // Enable PGN message
458 unsigned char request_enable[] = {0x47, 0x00, 0x00, 0x00, // pgn
459 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF};
460
461 int PGN = 0;
462 unsigned char* c = (unsigned char*)&pgn;
463 request_enable[1] = c[0];
464 request_enable[2] = c[1];
465 request_enable[3] = c[2];
466
467 int aa = SendMgmtMsg(request_enable, sizeof(request_enable), 0x47, 2000,
468 &m_bmg47_resp);
469 if (aa) return 0; // Probably YDNU-02 device
470
471 // Commit message
472 unsigned char request_commit[] = {0x01};
473 int bb = SendMgmtMsg(request_commit, sizeof(request_commit), 0x01, 2000,
474 &m_bmg01_resp);
475
476 // Activate message
477 unsigned char request_activate[] = {0x4B};
478 int cc = SendMgmtMsg(request_activate, sizeof(request_activate), 0x4B, 2000,
479 &m_bmg4B_resp);
480
481 return 0;
482}
483void CommDriverN2KSerial::AddTxPGN(int pgn) {
484 auto it = std::find(pgn_tx_list.begin(), pgn_tx_list.end(), pgn);
485 if (it != pgn_tx_list.end())
486 return;
487 else {
488 SetTXPGN(pgn);
489 pgn_tx_list.push_back(pgn);
490 }
491}
492
493#ifndef __ANDROID__
494
501// Commonly used raw format is actually inherited from an old paketizing format:
502// <10><02><application data><CRC (1)><10><03>
503
504// Actisense application data, from NGT-1 to PC
505// <data code=93><length (1)><priority (1)><PGN (3)><destination(1)><source
506// (1)><time (4)><len (1)><data (len)>
507
508// As applied to a real application data element, after extraction from packet
509// format: 93 13 02 01 F8 01 FF 01 76 C2 52 00 08 08 70 EB 14 E8 8E 52 D2 BB 10
510
511// length (1): 0x13
512// priority (1); 0x02
513// PGN (3): 0x01 0xF8 0x01
514// destination(1): 0xFF
515// source (1): 0x01
516// time (4): 0x76 0xC2 0x52 0x00
517// len (1): 0x08
518// data (len): 08 70 EB 14 E8 8E 52 D2
519// packet CRC: 0xBB
520
521#define DS_RX_BUFFER_SIZE 4096
522
523CommDriverN2KSerialThread::CommDriverN2KSerialThread(
524 CommDriverN2KSerial* Launcher, const wxString& PortName,
525 const wxString& strBaudRate)
526 : m_out_que(OUT_QUEUE_LENGTH) {
527 m_pParentDriver = Launcher; // This thread's immediate "parent"
528
529 m_PortName = PortName;
530 m_FullPortName = "Serial:" + PortName;
531
532 rx_buffer = new unsigned char[DS_RX_BUFFER_SIZE + 1];
533
534 put_ptr = rx_buffer; // local circular queue
535 tak_ptr = rx_buffer;
536
537 m_baud = 9600; // default
538 long lbaud;
539 if (strBaudRate.ToLong(&lbaud)) m_baud = (int)lbaud;
540 {
541 std::lock_guard lock(m_stats_mutex);
542 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
543 m_driver_stats.driver_iface = m_pParentDriver->m_params.GetStrippedDSPort();
544 m_driver_stats.available = false;
545 }
546
547 Create();
548}
549
550CommDriverN2KSerialThread::~CommDriverN2KSerialThread() { delete[] rx_buffer; }
551
552void CommDriverN2KSerialThread::OnExit() {}
553
554DriverStats CommDriverN2KSerialThread::GetStats() const {
555 std::lock_guard lock(m_stats_mutex);
556 return m_driver_stats;
557}
558
559bool CommDriverN2KSerialThread::OpenComPortPhysical(const wxString& com_name,
560 int baud_rate) {
561 try {
562 m_serial.setPort(com_name.ToStdString());
563 m_serial.setBaudrate(baud_rate);
564 m_serial.open();
565 m_serial.setTimeout(250, 250, 0, 250, 0);
566 } catch (std::exception&) {
567 // std::cerr << "Unhandled Exception while opening serial port: " <<
568 // e.what() << std::endl;
569 }
570 return m_serial.isOpen();
571}
572
573void CommDriverN2KSerialThread::CloseComPortPhysical() {
574 try {
575 m_serial.close();
576 } catch (std::exception&) {
577 // std::cerr << "Unhandled Exception while closing serial port: " <<
578 // e.what() << std::endl;
579 }
580 std::lock_guard lock(m_stats_mutex);
581 m_driver_stats.available = false;
582}
583
584void CommDriverN2KSerialThread::SetGatewayOperationMode() {
585 // For YDNU-02 device
586 // From Device User Manual
587 // Set the mode to "N2K"
588 unsigned char config_string[] = {0x10, 0x02, 0xA1, 0x03, 0x11,
589 0x02, 0x00, 0x49, 0x10, 0x03};
590 // std::vector<byte>writeBuffer {DLE,STX,NGT_TX_CMD,0x03,0x11,0x02,0x00,0x49,
591 // DLE,ETX};
592
593 WriteComPortPhysical(config_string, 10);
594}
595
596void CommDriverN2KSerialThread::ThreadMessage(const wxString& msg) {
597 // Signal the main program thread
598 // OCPN_ThreadMessageEvent event(wxEVT_OCPN_THREADMSG, 0);
599 // event.SetSString(std::string(msg.mb_str()));
600 // if (gFrame) gFrame->GetEventHandler()->AddPendingEvent(event);
601}
602
603size_t CommDriverN2KSerialThread::WriteComPortPhysical(
604 std::vector<unsigned char> msg) {
605 return WriteComPortPhysical(msg.data(), msg.size());
606}
607
608size_t CommDriverN2KSerialThread::WriteComPortPhysical(unsigned char* msg,
609 size_t length) {
610 if (!m_serial.isOpen()) return 0;
611 try {
612 size_t status = m_serial.write((uint8_t*)msg, length);
613 if (status) m_serial.flushOutput();
614 return status;
615 } catch (std::exception& e) {
616 DEBUG_LOG << "Unhandled Exception while writing to serial port: "
617 << e.what();
618 }
619 return 0;
620}
621
622bool CommDriverN2KSerialThread::SetOutMsg(
623 const std::vector<unsigned char>& msg) {
624 if (!m_out_que.IsFull()) {
625 m_out_que.Put(msg);
626 return true;
627 }
628 return false;
629}
630
631#ifndef __WXMSW__
632void* CommDriverN2KSerialThread::Entry() {
633 bool not_done = true;
634 bool nl_found = false;
635 wxString msg;
636 uint8_t rdata[2000];
638 int ib = 0;
639
640 // Request the com port from the comm manager
641 if (!OpenComPortPhysical(m_PortName, m_baud)) {
642 wxString msg("NMEA input device open failed: ");
643 msg.Append(m_PortName);
644 ThreadMessage(msg);
645 std::lock_guard lock(m_stats_mutex);
646 m_driver_stats.available = false;
647
648 // goto thread_exit; // This means we will not be trying to connect = The
649 // device must be connected when the thread is created. Does not seem to be
650 // needed/what we want as the reconnection logic is able to pick it up
651 // whenever it actually appears (Of course given it appears with the
652 // expected device name).
653 } else {
654 wxMilliSleep(100);
655 std::lock_guard lock(m_stats_mutex);
656 m_driver_stats.available = true;
657 SetGatewayOperationMode();
658 }
659
660 m_pParentDriver->SetSecThreadActive(); // I am alive
661
662 // The main loop
663 static size_t retries = 0;
664
665 bool bInMsg = false;
666 bool bGotESC = false;
667 bool bGotSOT = false;
668
669 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
670 if (TestDestroy()) not_done = false; // smooth exit
671
672 uint8_t next_byte = 0;
673 int newdata = 0;
674 if (m_serial.isOpen()) {
675 try {
676 newdata = m_serial.read(rdata, 1000);
677 } catch (std::exception& e) {
678 // std::cerr << "Serial read exception: " << e.what() << std::endl;
679 std::lock_guard lock(m_stats_mutex);
680 m_driver_stats.error_count++;
681
682 if (10 < retries++) {
683 // We timed out waiting for the next character 10 times, let's close
684 // the port so that the reconnection logic kicks in and tries to fix
685 // our connection.
686 CloseComPortPhysical();
687 retries = 0;
688 }
689 }
690 } else {
691 // Reconnection logic. Let's try to reopen the port while waiting longer
692 // every time (until we simply keep trying every 2.5 seconds)
693 // std::cerr << "Serial port seems closed." << std::endl;
694 wxMilliSleep(250 * retries);
695 CloseComPortPhysical();
696 if (OpenComPortPhysical(m_PortName, m_baud)) {
697 SetGatewayOperationMode();
698 std::lock_guard lock(m_stats_mutex);
699 m_driver_stats.available = true;
700 retries = 0;
701 } else if (retries < 10) {
702 std::lock_guard lock(m_stats_mutex);
703 m_driver_stats.available = false;
704 retries++;
705 }
706 }
707
708 if (newdata > 0) {
709 std::lock_guard lock(m_stats_mutex);
710 m_driver_stats.rx_count += newdata;
711
712 for (int i = 0; i < newdata; i++) {
713 circle.Put(rdata[i]);
714 }
715 }
716
717 while (!circle.IsEmpty()) {
718 if (ib >= DS_RX_BUFFER_SIZE) ib = 0;
719 uint8_t next_byte = circle.Get();
720
721 if (bInMsg) {
722 if (bGotESC) {
723 if (ESCAPE == next_byte) {
724 rx_buffer[ib++] = next_byte;
725 bGotESC = false;
726 }
727 }
728
729 if (bGotESC && (ENDOFTEXT == next_byte)) {
730 // Process packet
731 // Copy the message into a std::vector
732
733 auto buffer = std::make_shared<std::vector<unsigned char>>(
734 rx_buffer, rx_buffer + ib);
735 std::vector<unsigned char>* vec = buffer.get();
736
737 ib = 0;
738 // tak_ptr = tptr;
739 bInMsg = false;
740 bGotESC = false;
741
742 // printf("raw ");
743 // for (unsigned int i = 0; i < vec->size(); i++)
744 // printf("%02X ", vec->at(i));
745 // printf("\n");
746
747 // Message is finished
748 // Send the captured raw data vector pointer to the thread's "parent"
749 // thereby releasing the thread for further data capture
750 CommDriverN2KSerialEvent Nevent(wxEVT_COMMDRIVER_N2K_SERIAL, 0);
751 Nevent.SetPayload(buffer);
752 m_pParentDriver->AddPendingEvent(Nevent);
753
754 } else {
755 bGotESC = (next_byte == ESCAPE);
756
757 if (!bGotESC) {
758 rx_buffer[ib++] = next_byte;
759 }
760 }
761 }
762
763 else {
764 if (STARTOFTEXT == next_byte) {
765 bGotSOT = false;
766 if (bGotESC) {
767 bGotSOT = true;
768 }
769 } else {
770 bGotESC = (next_byte == ESCAPE);
771 if (bGotSOT) {
772 bGotSOT = false;
773 bInMsg = true;
774
775 rx_buffer[ib++] = next_byte;
776 }
777 }
778 }
779 } // if newdata > 0
780
781 // Check for any pending output message
782#if 1
783 bool b_qdata = !m_out_que.IsEmpty();
784
785 while (b_qdata) {
786 // Take a copy of message
787 std::vector<unsigned char> qmsg = m_out_que.Get();
788
789 if (static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
790 10 < retries++) {
791 // We failed to write the port 10 times, let's close the port so that
792 // the reconnection logic kicks in and tries to fix our connection.
793 retries = 0;
794 CloseComPortPhysical();
795 }
796
797 b_qdata = !m_out_que.IsEmpty();
798 } // while b_qdata
799
800#endif
801 } // while ((not_done)
802
803 // thread_exit:
804 CloseComPortPhysical();
805 m_pParentDriver->SetSecThreadInActive(); // I am dead
806 m_pParentDriver->m_Thread_run_flag = -1;
807
808 std::lock_guard lock(m_stats_mutex);
809 m_driver_stats.available = false;
810
811 return 0;
812}
813
814#else
815void* CommDriverN2KSerialThread::Entry() {
816 bool not_done = true;
817 bool nl_found = false;
818 wxString msg;
820
821 // Request the com port from the comm manager
822 if (!OpenComPortPhysical(m_PortName, m_baud)) {
823 wxString msg("NMEA input device open failed: ");
824 msg.Append(m_PortName);
825 ThreadMessage(msg);
826 std::lock_guard lock(m_stats_mutex);
827 m_driver_stats.available = false;
828
829 // goto thread_exit; // This means we will not be trying to connect = The
830 // device must be connected when the thread is created. Does not seem to be
831 // needed/what we want as the reconnection logic is able to pick it up
832 // whenever it actually appears (Of course given it appears with the
833 // expected device name).
834 } else {
835 SetGatewayOperationMode();
836 std::lock_guard lock(m_stats_mutex);
837 m_driver_stats.available = true;
838 }
839
840 m_pParentDriver->SetSecThreadActive(); // I am alive
841
842 // The main loop
843 static size_t retries = 0;
844
845 bool bInMsg = false;
846 bool bGotESC = false;
847 bool bGotSOT = false;
848
849 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
850 if (TestDestroy()) not_done = false; // smooth exit
851
852 uint8_t next_byte = 0;
853 int newdata = -1;
854 uint8_t rdata[2000];
855
856 if (m_serial.isOpen()) {
857 try {
858 newdata = m_serial.read(rdata, 200);
859 } catch (std::exception& e) {
860 // std::cerr << "Serial read exception: " << e.what() << std::endl;
861 if (10 < retries++) {
862 // We timed out waiting for the next character 10 times, let's close
863 // the port so that the reconnection logic kicks in and tries to fix
864 // our connection.
865 CloseComPortPhysical();
866 retries = 0;
867 }
868 }
869 } else {
870 // Reconnection logic. Let's try to reopen the port while waiting longer
871 // every time (until we simply keep trying every 2.5 seconds)
872 // std::cerr << "Serial port seems closed." << std::endl;
873 wxMilliSleep(250 * retries);
874 CloseComPortPhysical();
875 if (OpenComPortPhysical(m_PortName, m_baud)) {
876 std::lock_guard lock(m_stats_mutex);
877 m_driver_stats.available = true;
878 SetGatewayOperationMode();
879 retries = 0;
880 } else if (retries < 10)
881 retries++;
882 }
883
884 if (newdata > 0) {
885 for (int i = 0; i < newdata; i++) {
886 circle.Put(rdata[i]);
887 }
888 }
889
890 while (!circle.IsEmpty()) {
891 uint8_t next_byte = circle.Get();
892
893 if (1) {
894 if (bInMsg) {
895 if (bGotESC) {
896 if (ESCAPE == next_byte) {
897 *put_ptr++ = next_byte;
898 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
899 put_ptr = rx_buffer;
900 bGotESC = false;
901 } else if (ENDOFTEXT == next_byte) {
902 // Process packet
903 // Copy the message into a std::vector
904
905 auto buffer = std::make_shared<std::vector<unsigned char>>();
906 std::vector<unsigned char>* vec = buffer.get();
907
908 unsigned char* tptr;
909 tptr = tak_ptr;
910
911 while ((tptr != put_ptr)) {
912 vec->push_back(*tptr++);
913 if ((tptr - rx_buffer) > DS_RX_BUFFER_SIZE) tptr = rx_buffer;
914 }
915
916 tak_ptr = tptr;
917 bInMsg = false;
918 bGotESC = false;
919
920 // Message is finished
921 // Send the captured raw data vector pointer to the thread's
922 // "parent"
923 // thereby releasing the thread for further data capture
924 CommDriverN2KSerialEvent Nevent(wxEVT_COMMDRIVER_N2K_SERIAL, 0);
925 Nevent.SetPayload(buffer);
926 m_pParentDriver->AddPendingEvent(Nevent);
927 std::lock_guard lock(m_stats_mutex);
928 m_driver_stats.rx_count += vec->size();
929 } else if (next_byte == STARTOFTEXT) {
930 put_ptr = rx_buffer;
931 bGotESC = false;
932 } else {
933 put_ptr = rx_buffer;
934 bInMsg = false;
935 bGotESC = false;
936 }
937
938 } else {
939 bGotESC = (next_byte == ESCAPE);
940
941 if (!bGotESC) {
942 *put_ptr++ = next_byte;
943 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
944 put_ptr = rx_buffer;
945 }
946 }
947 }
948
949 else {
950 if (STARTOFTEXT == next_byte) {
951 bGotSOT = false;
952 if (bGotESC) {
953 bGotSOT = true;
954 }
955 } else {
956 bGotESC = (next_byte == ESCAPE);
957 if (bGotSOT) {
958 bGotSOT = false;
959 bInMsg = true;
960
961 *put_ptr++ = next_byte;
962 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
963 put_ptr = rx_buffer;
964 }
965 }
966 }
967 } // if newdata > 0
968 } // while
969
970 // Check for any pending output message
971 bool b_qdata = !m_out_que.IsEmpty();
972
973 while (!m_out_que.IsEmpty()) {
974 // Take a copy of message
975 std::vector<unsigned char> qmsg = m_out_que.Get();
976
977 if (static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
978 10 < retries++) {
979 // We failed to write the port 10 times, let's close the port so that
980 // the reconnection logic kicks in and tries to fix our connection.
981 retries = 0;
982 CloseComPortPhysical();
983 }
984 std::lock_guard lock(m_stats_mutex);
985 m_driver_stats.tx_count += qmsg.size();
986 }
987 } // while ((not_done)
988
989 // thread_exit:
990 CloseComPortPhysical();
991 m_pParentDriver->SetSecThreadInActive(); // I am dead
992 m_pParentDriver->m_Thread_run_flag = -1;
993
994 return 0;
995}
996
997#endif // wxmsw Entry()
998
999#endif // Android
1000
1001//*****************************************************************************
1002// Actisense Format:
1003// <10><02><93><length (1)><priority (1)><PGN (3)><destination (1)><source
1004// (1)><time (4)><len (1)><data (len)><CRC (1)><10><03>
1005#define MaxActisenseMsgBuf 400
1006#define MsgTypeN2kTX 0x94
1007
1008void AddByteEscapedToBuf(unsigned char byteToAdd, uint8_t& idx,
1009 unsigned char* buf, int& byteSum);
1010
1011static std::vector<unsigned char> BufferToActisenseFormat(tN2kMsg& msg) {
1012 unsigned long _PGN = msg.PGN;
1013 uint8_t msgIdx = 0;
1014 int byteSum = 0;
1015 uint8_t CheckSum;
1016 unsigned char ActisenseMsgBuf[MaxActisenseMsgBuf];
1017
1018 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1019 ActisenseMsgBuf[msgIdx++] = STARTOFTEXT;
1020 AddByteEscapedToBuf(MsgTypeN2kTX, msgIdx, ActisenseMsgBuf, byteSum);
1021 AddByteEscapedToBuf(msg.DataLen + 6, msgIdx, ActisenseMsgBuf,
1022 byteSum); // length does not include escaped chars
1023
1024 AddByteEscapedToBuf(msg.Priority, msgIdx, ActisenseMsgBuf, byteSum);
1025 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1026 _PGN >>= 8;
1027 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1028 _PGN >>= 8;
1029 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1030 AddByteEscapedToBuf(msg.Destination, msgIdx, ActisenseMsgBuf, byteSum);
1031
1032#if 0
1033 // For TX through Actisense compatible gateway, we skip "source" byte and msg time fields
1034 // Source
1035 AddByteEscapedToBuf(msg.Source,msgIdx,ActisenseMsgBuf,byteSum);
1036 // Time
1037 int _MsgTime = 0;
1038 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1039 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1040 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1041 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum);
1042
1043#endif
1044
1045 AddByteEscapedToBuf(msg.DataLen, msgIdx, ActisenseMsgBuf, byteSum);
1046
1047 for (int i = 0; i < msg.DataLen; i++)
1048 AddByteEscapedToBuf(msg.Data[i], msgIdx, ActisenseMsgBuf, byteSum);
1049 byteSum %= 256;
1050
1051 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
1052 ActisenseMsgBuf[msgIdx++] = CheckSum;
1053 if (CheckSum == ESCAPE) ActisenseMsgBuf[msgIdx++] = CheckSum;
1054
1055 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1056 ActisenseMsgBuf[msgIdx++] = ENDOFTEXT;
1057
1058 std::vector<unsigned char> rv;
1059 for (unsigned int i = 0; i < msgIdx; i++) rv.push_back(ActisenseMsgBuf[i]);
1060
1061 return rv;
1062}
Fixed size, synchronized FIFO buffer.
bool IsEmpty() const noexcept
Return true if buffer is empty.
T Get()
Get item from buff; throw BufferError if empty.
void Put(const T &item)
Add item to buffer; throw BufferError if full.
bool IsFull() const noexcept
Return true if buffer is full.
DriverStats GetDriverStats() const override
Get the Driver Statistics.
Interface for handling incoming messages.
Definition comm_driver.h:50
virtual void Notify(std::shared_ptr< const NavMsg > message)=0
Handle a received message.
Class that provides a portable serial port interface.
Definition serial.h:147
size_t read(uint8_t *buffer, size_t size)
Read a given amount of bytes from the serial port into a given buffer.
void setPort(const std::string &port)
Sets the serial port identifier.
size_t write(const uint8_t *data, size_t size)
Write a string to the serial port.
void setBaudrate(uint32_t baudrate)
Sets the baudrate for the serial port.
void close()
Closes the serial port.
void setTimeout(Timeout &timeout)
Sets the timeout for reads and writes using the Timeout struct.
void flushOutput()
Flush only the output buffer.
void open()
Opens the serial port as long as the port is set and the port isn't already open.
bool isOpen() const
Gets the open status of the serial port.
Various input/output buffers.
#define DS_RX_BUFFER_SIZE
This thread manages reading the N2K data stream provided by some N2K gateways from the declared seria...
Nmea2000 serial driver.
Driver registration container, a singleton.
Communication statistics infrastructure.
Raw messages layer, supports sending and recieving navmsg messages.
std::string DsPortTypeToString(dsPortType type)
Return textual representation for use in driver ioDirection attribute.
Enhanced logging interface on top of wx/log.h.
Driver statistics report.
unsigned tx_count
Number of bytes sent since program start.
unsigned rx_count
Number of bytes received since program start.
unsigned error_count
Number of detected errors since program start.