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