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