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 if (!msg) return false;
327
328#ifndef ANDROID
329
330 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
331 std::vector<uint8_t> load = msg_n2k->payload;
332
333 uint64_t _pgn = msg_n2k->PGN.pgn;
334 auto destination_address = std::static_pointer_cast<const NavAddr2000>(addr);
335
336 tN2kMsg N2kMsg; // automatically sets destination 255
337 N2kMsg.SetPGN(_pgn);
338 N2kMsg.Priority = msg_n2k->priority;
339 if (destination_address) N2kMsg.Destination = destination_address->address;
340
341 for (size_t i = 0; i < load.size(); i++) N2kMsg.AddByte(load.at(i)); // data
342
343 const std::vector<uint8_t> acti_pkg = BufferToActisenseFormat(N2kMsg);
344
345 // Create the internal message and notify upper layers
346 std::vector<unsigned char> msg_payload;
347 for (size_t i = 2; i < acti_pkg.size() - 2; i++)
348 msg_payload.push_back(acti_pkg[i]);
349 auto name = PayloadToName(load);
350 m_listener.Notify(
351 std::make_shared<const Nmea2000Msg>(_pgn, msg_payload, GetAddress(name)));
352
353 if (GetSecondaryThread()) {
354 if (IsSecThreadActive()) {
355 int retry = 10;
356 while (retry) {
357 if (GetSecondaryThread()->SetOutMsg(acti_pkg))
358 return true;
359 else
360 retry--;
361 }
362 return false; // could not send after several tries....
363 } else
364 return false;
365 }
366#endif
367 return true;
368}
369
370void CommDriverN2KSerial::ProcessManagementPacket(
371 std::vector<unsigned char>* payload) {
372 if (payload->at(2) != 0xF2) { // hearbeat
373 // printf(" pl ");
374 // for (unsigned int i = 0; i < payload->size(); i++)
375 // printf("%02X ", payload->at(i));
376 // printf("\n");
377 }
378
379 switch (payload->at(2)) {
380 case 0x47:
381 m_bmg47_resp = true;
382 break;
383 case 0x01:
384 m_bmg01_resp = true;
385 break;
386 case 0x4B:
387 m_bmg4B_resp = true;
388 break;
389 case 0x041: {
390 m_bmg41_resp = true;
391 if (payload->at(3) == 0x02) { // ASCII device_common_name
392 std::string device_common_name;
393 for (unsigned int i = 0; i < 32; i++) {
394 device_common_name += payload->at(i + 14);
395 }
396 device_common_name += '\0';
397 m_device_common_name = device_common_name;
398 }
399 break;
400 }
401 case 0x042: {
402 m_bmg42_resp = true;
403 unsigned char name[8];
404 for (unsigned int i = 0; i < 8; i++) name[i] = payload->at(i + 15);
405
406 memcpy((void*)&NAME, name, 8);
407 // Extract the manufacturers code
408 int* f1 = (int*)&NAME;
409 int f1d = *f1;
410 m_manufacturers_code = f1d >> 21;
411 break;
412 }
413
414 default:
415 break;
416 }
417}
418
419void CommDriverN2KSerial::handle_N2K_SERIAL_RAW(
421 auto p = event.GetPayload();
422
423 std::vector<unsigned char>* payload = p.get();
424
425 if (payload->at(0) == 0xA0) {
426 ProcessManagementPacket(payload);
427 return;
428 }
429
430 // If port INPUT is not set, filter the mesage here
431 if (m_params.IOSelect != DS_TYPE_OUTPUT) {
432 // extract PGN
433 uint64_t pgn = 0;
434 unsigned char* c = (unsigned char*)&pgn;
435 *c++ = payload->at(3);
436 *c++ = payload->at(4);
437 *c++ = payload->at(5);
438
439 auto name = PayloadToName(*payload);
440 auto msg =
441 std::make_shared<const Nmea2000Msg>(pgn, *payload, GetAddress(name));
442
443 m_listener.Notify(std::move(msg));
444 }
445}
446
447int CommDriverN2KSerial::GetMfgCode() {
448 unsigned char request_name[] = {0x42};
449 int ni = SendMgmtMsg(request_name, sizeof(request_name), 0x41, 2000,
450 &m_bmg42_resp);
451 if (ni) return ni; // Not responding, return error so upper retries.
452 m_got_mfg_code = true;
453 return 0;
454}
455
456int CommDriverN2KSerial::SendMgmtMsg(unsigned char* string, size_t string_size,
457 unsigned char cmd_code, int timeout_msec,
458 bool* response_flag) {
459#ifndef ANDROID
460 // Make a message
461 int byteSum = 0;
462 uint8_t CheckSum;
463 std::vector<unsigned char> msg;
464
465 msg.push_back(ESCAPE);
466 msg.push_back(STARTOFTEXT);
467 msg.push_back(0xA1);
468 byteSum += 0xA1;
469 msg.push_back(string_size); // payload length
470 byteSum += string_size;
471
472 for (unsigned int i = 0; i < string_size; i++) {
473 if (string[i] == ESCAPE) msg.push_back(string[i]);
474 msg.push_back(string[i]);
475 byteSum += string[i];
476 }
477
478 // checksum
479 byteSum %= 256;
480 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
481 msg.push_back(CheckSum);
482
483 msg.push_back(ESCAPE);
484 msg.push_back(ENDOFTEXT);
485
486 // send it out
487
488 if (response_flag) *response_flag = false; // prime the response detector
489
490 // Send the msg
491 bool bsent = false;
492 bool not_done = true;
493 int ntry_outer = 10;
494 while (not_done) {
495 if (GetSecondaryThread() && IsSecThreadActive()) {
496 int retry = 10;
497 while (retry) {
498 if (GetSecondaryThread()->SetOutMsg(msg)) {
499 bsent = true;
500 not_done = false;
501 break;
502 } else
503 retry--;
504 }
505 } else {
506 wxMilliSleep(100);
507 if (ntry_outer-- <= 0) not_done = false;
508 }
509 }
510
511 if (!bsent) return 1;
512
513 bool bOK = false;
514 if (timeout_msec) {
515 int timeout = timeout_msec;
516 while (timeout > 0) {
517 wxYieldIfNeeded();
518 wxMilliSleep(100);
519 if (response_flag) {
520 if (*response_flag) {
521 bOK = true;
522 break;
523 }
524 }
525 timeout -= 100;
526 }
527 } else
528 bOK = true;
529
530 if (!bOK) {
531 // printf( "***Err-1\n");
532 return 1;
533 }
534 // else
535 // printf("***OK-1 %d\n", timeout);
536#endif
537 return 0;
538}
539
540int CommDriverN2KSerial::SetTXPGN(int pgn) {
541 // Enable PGN message
542 unsigned char request_enable[] = {0x47, 0x00, 0x00, 0x00, // pgn
543 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF};
544
545 int PGN = 0;
546 unsigned char* c = (unsigned char*)&pgn;
547 request_enable[1] = c[0];
548 request_enable[2] = c[1];
549 request_enable[3] = c[2];
550
551 int aa = SendMgmtMsg(request_enable, sizeof(request_enable), 0x47, 2000,
552 &m_bmg47_resp);
553 if (aa) return 0; // Probably YDNU-02 device
554
555 // Commit message
556 unsigned char request_commit[] = {0x01};
557 int bb = SendMgmtMsg(request_commit, sizeof(request_commit), 0x01, 2000,
558 &m_bmg01_resp);
559
560 // Activate message
561 unsigned char request_activate[] = {0x4B};
562 int cc = SendMgmtMsg(request_activate, sizeof(request_activate), 0x4B, 2000,
563 &m_bmg4B_resp);
564
565 return 0;
566}
567void CommDriverN2KSerial::AddTxPGN(int pgn) {
568 auto it = std::find(pgn_tx_list.begin(), pgn_tx_list.end(), pgn);
569 if (it != pgn_tx_list.end())
570 return;
571 else {
572 SetTXPGN(pgn);
573 pgn_tx_list.push_back(pgn);
574 }
575}
576
577#ifndef __ANDROID__
578
585// Commonly used raw format is actually inherited from an old paketizing format:
586// <10><02><application data><CRC (1)><10><03>
587
588// Actisense application data, from NGT-1 to PC
589// <data code=93><length (1)><priority (1)><PGN (3)><destination(1)><source
590// (1)><time (4)><len (1)><data (len)>
591
592// As applied to a real application data element, after extraction from packet
593// format: 93 13 02 01 F8 01 FF 01 76 C2 52 00 08 08 70 EB 14 E8 8E 52 D2 BB 10
594
595// length (1): 0x13
596// priority (1); 0x02
597// PGN (3): 0x01 0xF8 0x01
598// destination(1): 0xFF
599// source (1): 0x01
600// time (4): 0x76 0xC2 0x52 0x00
601// len (1): 0x08
602// data (len): 08 70 EB 14 E8 8E 52 D2
603// packet CRC: 0xBB
604
605#define DS_RX_BUFFER_SIZE 4096
606
607CommDriverN2KSerialThread::CommDriverN2KSerialThread(
608 CommDriverN2KSerial* Launcher, const wxString& PortName,
609 const wxString& strBaudRate) {
610 m_pParentDriver = Launcher; // This thread's immediate "parent"
611
612 m_PortName = PortName;
613 m_FullPortName = "Serial:" + PortName;
614
615 rx_buffer = new unsigned char[DS_RX_BUFFER_SIZE + 1];
616
617 put_ptr = rx_buffer; // local circular queue
618 tak_ptr = rx_buffer;
619
620 m_baud = 9600; // default
621 long lbaud;
622 if (strBaudRate.ToLong(&lbaud)) m_baud = (int)lbaud;
623 {
624 std::lock_guard lock(m_stats_mutex);
625 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
626 m_driver_stats.driver_iface = m_pParentDriver->m_params.GetStrippedDSPort();
627 m_driver_stats.available = false;
628 }
629
630 Create();
631}
632
633CommDriverN2KSerialThread::~CommDriverN2KSerialThread() { delete[] rx_buffer; }
634
635void CommDriverN2KSerialThread::OnExit() {}
636
637DriverStats CommDriverN2KSerialThread::GetStats() const {
638 std::lock_guard lock(m_stats_mutex);
639 return m_driver_stats;
640}
641
642bool CommDriverN2KSerialThread::OpenComPortPhysical(const wxString& com_name,
643 int baud_rate) {
644 try {
645 m_serial.setPort(com_name.ToStdString());
646 m_serial.setBaudrate(baud_rate);
647 m_serial.open();
648 m_serial.setTimeout(250, 250, 0, 250, 0);
649 } catch (std::exception&) {
650 // std::cerr << "Unhandled Exception while opening serial port: " <<
651 // e.what() << std::endl;
652 }
653 return m_serial.isOpen();
654}
655
656void CommDriverN2KSerialThread::CloseComPortPhysical() {
657 try {
658 m_serial.close();
659 } catch (std::exception&) {
660 // std::cerr << "Unhandled Exception while closing serial port: " <<
661 // e.what() << std::endl;
662 }
663 std::lock_guard lock(m_stats_mutex);
664 m_driver_stats.available = false;
665}
666
667void CommDriverN2KSerialThread::SetGatewayOperationMode() {
668 // For YDNU-02 device
669 // From Device User Manual
670 // Set the mode to "N2K"
671 unsigned char config_string[] = {0x10, 0x02, 0xA1, 0x03, 0x11,
672 0x02, 0x00, 0x49, 0x10, 0x03};
673 // std::vector<byte>writeBuffer {DLE,STX,NGT_TX_CMD,0x03,0x11,0x02,0x00,0x49,
674 // DLE,ETX};
675
676 WriteComPortPhysical(config_string, 10);
677}
678
679void CommDriverN2KSerialThread::ThreadMessage(const wxString& msg) {
680 // Signal the main program thread
681 // OCPN_ThreadMessageEvent event(wxEVT_OCPN_THREADMSG, 0);
682 // event.SetSString(std::string(msg.mb_str()));
683 // if (gFrame) gFrame->GetEventHandler()->AddPendingEvent(event);
684}
685
686size_t CommDriverN2KSerialThread::WriteComPortPhysical(
687 std::vector<unsigned char> msg) {
688 return WriteComPortPhysical(msg.data(), msg.size());
689}
690
691size_t CommDriverN2KSerialThread::WriteComPortPhysical(unsigned char* msg,
692 size_t length) {
693 if (!m_serial.isOpen()) return 0;
694 try {
695 size_t status = m_serial.write((uint8_t*)msg, length);
696 if (status) m_serial.flushOutput();
697 return status;
698 } catch (std::exception& e) {
699 DEBUG_LOG << "Unhandled Exception while writing to serial port: "
700 << e.what();
701 }
702 return 0;
703}
704
705bool CommDriverN2KSerialThread::SetOutMsg(
706 const std::vector<unsigned char>& msg) {
707 if (out_que.size() < OUT_QUEUE_LENGTH) {
708 out_que.push(msg);
709 return true;
710 }
711 return false;
712}
713
714#ifndef __WXMSW__
715void* CommDriverN2KSerialThread::Entry() {
716 bool not_done = true;
717 bool nl_found = false;
718 wxString msg;
719 uint8_t rdata[2000];
721 int ib = 0;
722
723 // Request the com port from the comm manager
724 if (!OpenComPortPhysical(m_PortName, m_baud)) {
725 wxString msg("NMEA input device open failed: ");
726 msg.Append(m_PortName);
727 ThreadMessage(msg);
728 std::lock_guard lock(m_stats_mutex);
729 m_driver_stats.available = false;
730
731 // goto thread_exit; // This means we will not be trying to connect = The
732 // device must be connected when the thread is created. Does not seem to be
733 // needed/what we want as the reconnection logic is able to pick it up
734 // whenever it actually appears (Of course given it appears with the
735 // expected device name).
736 } else {
737 wxMilliSleep(100);
738 std::lock_guard lock(m_stats_mutex);
739 m_driver_stats.available = true;
740 SetGatewayOperationMode();
741 }
742
743 m_pParentDriver->SetSecThreadActive(); // I am alive
744
745 // The main loop
746 static size_t retries = 0;
747
748 bool bInMsg = false;
749 bool bGotESC = false;
750 bool bGotSOT = false;
751
752 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
753 if (TestDestroy()) not_done = false; // smooth exit
754
755 uint8_t next_byte = 0;
756 int newdata = 0;
757 if (m_serial.isOpen()) {
758 try {
759 newdata = m_serial.read(rdata, 1000);
760 } catch (std::exception& e) {
761 // std::cerr << "Serial read exception: " << e.what() << std::endl;
762 std::lock_guard lock(m_stats_mutex);
763 m_driver_stats.error_count++;
764
765 if (10 < retries++) {
766 // We timed out waiting for the next character 10 times, let's close
767 // the port so that the reconnection logic kicks in and tries to fix
768 // our connection.
769 CloseComPortPhysical();
770 retries = 0;
771 }
772 }
773 } else {
774 // Reconnection logic. Let's try to reopen the port while waiting longer
775 // every time (until we simply keep trying every 2.5 seconds)
776 // std::cerr << "Serial port seems closed." << std::endl;
777 wxMilliSleep(250 * retries);
778 CloseComPortPhysical();
779 if (OpenComPortPhysical(m_PortName, m_baud)) {
780 SetGatewayOperationMode();
781 std::lock_guard lock(m_stats_mutex);
782 m_driver_stats.available = true;
783 retries = 0;
784 } else if (retries < 10) {
785 std::lock_guard lock(m_stats_mutex);
786 m_driver_stats.available = false;
787 retries++;
788 }
789 }
790
791 if (newdata > 0) {
792 std::lock_guard lock(m_stats_mutex);
793 m_driver_stats.rx_count += newdata;
794
795 for (int i = 0; i < newdata; i++) {
796 circle.put(rdata[i]);
797 }
798 }
799
800 while (!circle.empty()) {
801 if (ib >= DS_RX_BUFFER_SIZE) ib = 0;
802 uint8_t next_byte = circle.get();
803
804 if (bInMsg) {
805 if (bGotESC) {
806 if (ESCAPE == next_byte) {
807 rx_buffer[ib++] = next_byte;
808 bGotESC = false;
809 }
810 }
811
812 if (bGotESC && (ENDOFTEXT == next_byte)) {
813 // Process packet
814 // Copy the message into a std::vector
815
816 auto buffer = std::make_shared<std::vector<unsigned char>>(
817 rx_buffer, rx_buffer + ib);
818 std::vector<unsigned char>* vec = buffer.get();
819
820 ib = 0;
821 // tak_ptr = tptr;
822 bInMsg = false;
823 bGotESC = false;
824
825 // printf("raw ");
826 // for (unsigned int i = 0; i < vec->size(); i++)
827 // printf("%02X ", vec->at(i));
828 // printf("\n");
829
830 // Message is finished
831 // Send the captured raw data vector pointer to the thread's "parent"
832 // thereby releasing the thread for further data capture
833 CommDriverN2KSerialEvent Nevent(wxEVT_COMMDRIVER_N2K_SERIAL, 0);
834 Nevent.SetPayload(buffer);
835 m_pParentDriver->AddPendingEvent(Nevent);
836
837 } else {
838 bGotESC = (next_byte == ESCAPE);
839
840 if (!bGotESC) {
841 rx_buffer[ib++] = next_byte;
842 }
843 }
844 }
845
846 else {
847 if (STARTOFTEXT == next_byte) {
848 bGotSOT = false;
849 if (bGotESC) {
850 bGotSOT = true;
851 }
852 } else {
853 bGotESC = (next_byte == ESCAPE);
854 if (bGotSOT) {
855 bGotSOT = false;
856 bInMsg = true;
857
858 rx_buffer[ib++] = next_byte;
859 }
860 }
861 }
862 } // if newdata > 0
863
864 // Check for any pending output message
865#if 1
866 bool b_qdata = !out_que.empty();
867
868 while (b_qdata) {
869 // Take a copy of message
870 std::vector<unsigned char> qmsg = out_que.front();
871 out_que.pop();
872
873 if (static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
874 10 < retries++) {
875 // We failed to write the port 10 times, let's close the port so that
876 // the reconnection logic kicks in and tries to fix our connection.
877 retries = 0;
878 CloseComPortPhysical();
879 }
880
881 b_qdata = !out_que.empty();
882 } // while b_qdata
883
884#endif
885 } // while ((not_done)
886
887 // thread_exit:
888 CloseComPortPhysical();
889 m_pParentDriver->SetSecThreadInActive(); // I am dead
890 m_pParentDriver->m_Thread_run_flag = -1;
891
892 std::lock_guard lock(m_stats_mutex);
893 m_driver_stats.available = false;
894
895 return 0;
896}
897
898#else
899void* CommDriverN2KSerialThread::Entry() {
900 bool not_done = true;
901 bool nl_found = false;
902 wxString msg;
904
905 // Request the com port from the comm manager
906 if (!OpenComPortPhysical(m_PortName, m_baud)) {
907 wxString msg("NMEA input device open failed: ");
908 msg.Append(m_PortName);
909 ThreadMessage(msg);
910 std::lock_guard lock(m_stats_mutex);
911 m_driver_stats.available = false;
912
913 // goto thread_exit; // This means we will not be trying to connect = The
914 // device must be connected when the thread is created. Does not seem to be
915 // needed/what we want as the reconnection logic is able to pick it up
916 // whenever it actually appears (Of course given it appears with the
917 // expected device name).
918 } else {
919 SetGatewayOperationMode();
920 std::lock_guard lock(m_stats_mutex);
921 m_driver_stats.available = true;
922 }
923
924 m_pParentDriver->SetSecThreadActive(); // I am alive
925
926 // The main loop
927 static size_t retries = 0;
928
929 bool bInMsg = false;
930 bool bGotESC = false;
931 bool bGotSOT = false;
932
933 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
934 if (TestDestroy()) not_done = false; // smooth exit
935
936 uint8_t next_byte = 0;
937 int newdata = -1;
938 uint8_t rdata[2000];
939
940 if (m_serial.isOpen()) {
941 try {
942 newdata = m_serial.read(rdata, 200);
943 } catch (std::exception& e) {
944 // std::cerr << "Serial read exception: " << e.what() << std::endl;
945 if (10 < retries++) {
946 // We timed out waiting for the next character 10 times, let's close
947 // the port so that the reconnection logic kicks in and tries to fix
948 // our connection.
949 CloseComPortPhysical();
950 retries = 0;
951 }
952 }
953 } else {
954 // Reconnection logic. Let's try to reopen the port while waiting longer
955 // every time (until we simply keep trying every 2.5 seconds)
956 // std::cerr << "Serial port seems closed." << std::endl;
957 wxMilliSleep(250 * retries);
958 CloseComPortPhysical();
959 if (OpenComPortPhysical(m_PortName, m_baud)) {
960 std::lock_guard lock(m_stats_mutex);
961 m_driver_stats.available = true;
962 SetGatewayOperationMode();
963 retries = 0;
964 } else if (retries < 10)
965 retries++;
966 }
967
968 if (newdata > 0) {
969 for (int i = 0; i < newdata; i++) {
970 circle.put(rdata[i]);
971 }
972 }
973
974 while (!circle.empty()) {
975 uint8_t next_byte = circle.get();
976
977 if (1) {
978 if (bInMsg) {
979 if (bGotESC) {
980 if (ESCAPE == next_byte) {
981 *put_ptr++ = next_byte;
982 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
983 put_ptr = rx_buffer;
984 bGotESC = false;
985 } else if (ENDOFTEXT == next_byte) {
986 // Process packet
987 // Copy the message into a std::vector
988
989 auto buffer = std::make_shared<std::vector<unsigned char>>();
990 std::vector<unsigned char>* vec = buffer.get();
991
992 unsigned char* tptr;
993 tptr = tak_ptr;
994
995 while ((tptr != put_ptr)) {
996 vec->push_back(*tptr++);
997 if ((tptr - rx_buffer) > DS_RX_BUFFER_SIZE) tptr = rx_buffer;
998 }
999
1000 tak_ptr = tptr;
1001 bInMsg = false;
1002 bGotESC = false;
1003
1004 // Message is finished
1005 // Send the captured raw data vector pointer to the thread's
1006 // "parent"
1007 // thereby releasing the thread for further data capture
1008 CommDriverN2KSerialEvent Nevent(wxEVT_COMMDRIVER_N2K_SERIAL, 0);
1009 Nevent.SetPayload(buffer);
1010 m_pParentDriver->AddPendingEvent(Nevent);
1011 std::lock_guard lock(m_stats_mutex);
1012 m_driver_stats.rx_count += vec->size();
1013 } else if (next_byte == STARTOFTEXT) {
1014 put_ptr = rx_buffer;
1015 bGotESC = false;
1016 } else {
1017 put_ptr = rx_buffer;
1018 bInMsg = false;
1019 bGotESC = false;
1020 }
1021
1022 } else {
1023 bGotESC = (next_byte == ESCAPE);
1024
1025 if (!bGotESC) {
1026 *put_ptr++ = next_byte;
1027 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1028 put_ptr = rx_buffer;
1029 }
1030 }
1031 }
1032
1033 else {
1034 if (STARTOFTEXT == next_byte) {
1035 bGotSOT = false;
1036 if (bGotESC) {
1037 bGotSOT = true;
1038 }
1039 } else {
1040 bGotESC = (next_byte == ESCAPE);
1041 if (bGotSOT) {
1042 bGotSOT = false;
1043 bInMsg = true;
1044
1045 *put_ptr++ = next_byte;
1046 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1047 put_ptr = rx_buffer;
1048 }
1049 }
1050 }
1051 } // if newdata > 0
1052 } // while
1053
1054 // Check for any pending output message
1055 bool b_qdata = !out_que.empty();
1056
1057 while (b_qdata) {
1058 // Take a copy of message
1059 std::vector<unsigned char> qmsg = out_que.front();
1060 out_que.pop();
1061
1062 if (static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
1063 10 < retries++) {
1064 // We failed to write the port 10 times, let's close the port so that
1065 // the reconnection logic kicks in and tries to fix our connection.
1066 retries = 0;
1067 CloseComPortPhysical();
1068 }
1069 std::lock_guard lock(m_stats_mutex);
1070 m_driver_stats.tx_count += qmsg.size();
1071
1072 b_qdata = !out_que.empty();
1073 } // while b_qdata
1074 } // while ((not_done)
1075
1076 // thread_exit:
1077 CloseComPortPhysical();
1078 m_pParentDriver->SetSecThreadInActive(); // I am dead
1079 m_pParentDriver->m_Thread_run_flag = -1;
1080
1081 return 0;
1082}
1083
1084#endif // wxmsw Entry()
1085
1086#endif // Android
1087
1088//*****************************************************************************
1089// Actisense Format:
1090// <10><02><93><length (1)><priority (1)><PGN (3)><destination (1)><source
1091// (1)><time (4)><len (1)><data (len)><CRC (1)><10><03>
1092#define MaxActisenseMsgBuf 400
1093#define MsgTypeN2kTX 0x94
1094
1095void AddByteEscapedToBuf(unsigned char byteToAdd, uint8_t& idx,
1096 unsigned char* buf, int& byteSum);
1097
1098static std::vector<unsigned char> BufferToActisenseFormat(tN2kMsg& msg) {
1099 unsigned long _PGN = msg.PGN;
1100 uint8_t msgIdx = 0;
1101 int byteSum = 0;
1102 uint8_t CheckSum;
1103 unsigned char ActisenseMsgBuf[MaxActisenseMsgBuf];
1104
1105 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1106 ActisenseMsgBuf[msgIdx++] = STARTOFTEXT;
1107 AddByteEscapedToBuf(MsgTypeN2kTX, msgIdx, ActisenseMsgBuf, byteSum);
1108 AddByteEscapedToBuf(msg.DataLen + 6, msgIdx, ActisenseMsgBuf,
1109 byteSum); // length does not include escaped chars
1110
1111 AddByteEscapedToBuf(msg.Priority, msgIdx, ActisenseMsgBuf, byteSum);
1112 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1113 _PGN >>= 8;
1114 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1115 _PGN >>= 8;
1116 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1117 AddByteEscapedToBuf(msg.Destination, msgIdx, ActisenseMsgBuf, byteSum);
1118
1119#if 0
1120 // For TX through Actisense compatible gateway, we skip "source" byte and msg time fields
1121 // Source
1122 AddByteEscapedToBuf(msg.Source,msgIdx,ActisenseMsgBuf,byteSum);
1123 // Time
1124 int _MsgTime = 0;
1125 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1126 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1127 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1128 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum);
1129
1130#endif
1131
1132 AddByteEscapedToBuf(msg.DataLen, msgIdx, ActisenseMsgBuf, byteSum);
1133
1134 for (int i = 0; i < msg.DataLen; i++)
1135 AddByteEscapedToBuf(msg.Data[i], msgIdx, ActisenseMsgBuf, byteSum);
1136 byteSum %= 256;
1137
1138 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
1139 ActisenseMsgBuf[msgIdx++] = CheckSum;
1140 if (CheckSum == ESCAPE) ActisenseMsgBuf[msgIdx++] = CheckSum;
1141
1142 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1143 ActisenseMsgBuf[msgIdx++] = ENDOFTEXT;
1144
1145 std::vector<unsigned char> rv;
1146 for (unsigned int i = 0; i < msgIdx; i++) rv.push_back(ActisenseMsgBuf[i]);
1147
1148 return rv;
1149}
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.