52static unsigned char NGT_STARTUP_SEQ[] = {
58static std::vector<unsigned char> BufferToActisenseFormat(tN2kMsg& msg);
64 std::lock_guard<std::mutex> lock(m_mutex);
65 return m_queque.size();
69 std::lock_guard<std::mutex> lock(m_mutex);
70 return m_queque.empty();
74 std::lock_guard<std::mutex> lock(m_mutex);
75 return m_queque.front();
78 void push(
const T& value) {
79 std::lock_guard<std::mutex> lock(m_mutex);
84 std::lock_guard<std::mutex> lock(m_mutex);
89 std::queue<T> m_queque;
90 mutable std::mutex m_mutex;
97 : buf_(std::unique_ptr<T[]>(new T[size])), max_size_(size) {}
100 size_t capacity()
const;
105 return (!full_ && (head_ == tail_));
114 std::lock_guard<std::mutex> lock(mutex_);
116 if (full_) tail_ = (tail_ + 1) % max_size_;
118 head_ = (head_ + 1) % max_size_;
120 full_ = head_ == tail_;
124 std::lock_guard<std::mutex> lock(mutex_);
126 if (empty())
return T();
129 auto val = buf_[tail_];
131 tail_ = (tail_ + 1) % max_size_;
138 std::unique_ptr<T[]> buf_;
141 const size_t max_size_;
150 const wxString& PortName,
151 const wxString& strBaudRate);
155 bool SetOutMsg(
const std::vector<unsigned char>& load);
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();
172 wxString m_FullPortName;
174 unsigned char* put_ptr;
175 unsigned char* tak_ptr;
177 unsigned char* rx_buffer;
184 mutable std::mutex m_stats_mutex;
186 HANDLE m_hSerialComm;
197 : wxEvent(
id, commandType) {};
201 void SetPayload(std::shared_ptr<std::vector<unsigned char>> data) {
204 std::shared_ptr<std::vector<unsigned char>> GetPayload() {
return m_payload; }
207 wxEvent* Clone()
const {
209 newevent->m_payload = this->m_payload;
214 std::shared_ptr<std::vector<unsigned char>> m_payload;
226 m_Thread_run_flag(-1),
229 m_portstring(params->GetDSPort()),
230 m_pSecondary_Thread(NULL),
231 m_listener(listener),
232 m_stats_timer(*this, 2s),
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();
242 Bind(wxEVT_COMMDRIVER_N2K_SERIAL, &CommDriverN2KSerial::handle_N2K_SERIAL_RAW,
246 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
247 m_driver_stats.driver_iface = m_params.GetStrippedDSPort();
248 m_driver_stats.available =
false;
256 SendMgmtMsg(NGT_STARTUP_SEQ,
sizeof(NGT_STARTUP_SEQ), 0x11, 0, NULL);
259CommDriverN2KSerial::~CommDriverN2KSerial() { Close(); }
262 if (m_closing)
return m_driver_stats;
265 if (m_pSecondary_Thread)
266 return m_pSecondary_Thread->GetStats();
269 return m_driver_stats;
272bool CommDriverN2KSerial::Open() {
274 comx = m_params.GetDSPort().AfterFirst(
':');
277 comx.BeforeFirst(
' ');
283 GetSecondaryThread()->Run();
289void CommDriverN2KSerial::Close() {
290 wxLogMessage(wxString::Format(
"Closing N2K Driver %s", m_portstring.c_str()));
292 m_stats_timer.Stop();
296 if (m_pSecondary_Thread) {
297 if (m_bsec_thread_active)
299 wxLogMessage(
"Stopping Secondary Thread");
301 m_Thread_run_flag = 0;
303 while ((m_Thread_run_flag >= 0) && (tsec--)) wxSleep(1);
306 if (m_Thread_run_flag < 0)
307 msg.Printf(
"Stopped in %d sec.", 10 - tsec);
309 msg.Printf(
"Not Stopped after 10 sec.");
313 m_pSecondary_Thread = NULL;
314 m_bsec_thread_active =
false;
317static uint64_t PayloadToName(
const std::vector<unsigned char> payload) {
319 memcpy(&name,
reinterpret_cast<const void*
>(payload.data()),
sizeof(name));
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;
330 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
331 std::vector<uint8_t> load = msg_n2k->payload;
333 uint64_t _pgn = msg_n2k->PGN.pgn;
334 auto destination_address = std::static_pointer_cast<const NavAddr2000>(addr);
338 N2kMsg.Priority = msg_n2k->priority;
339 if (destination_address) N2kMsg.Destination = destination_address->address;
341 for (
size_t i = 0; i < load.size(); i++) N2kMsg.AddByte(load.at(i));
343 const std::vector<uint8_t> acti_pkg = BufferToActisenseFormat(N2kMsg);
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);
351 std::make_shared<const Nmea2000Msg>(1, msg_payload, GetAddress(name));
353 std::make_shared<const Nmea2000Msg>(_pgn, msg_payload, GetAddress(name));
356 m_listener.
Notify(std::move(msg_internal));
357 m_listener.
Notify(std::move(msg_all));
359 if (GetSecondaryThread()) {
360 if (IsSecThreadActive()) {
363 if (GetSecondaryThread()->SetOutMsg(acti_pkg))
376void CommDriverN2KSerial::ProcessManagementPacket(
377 std::vector<unsigned char>* payload) {
378 if (payload->at(2) != 0xF2) {
385 switch (payload->at(2)) {
397 if (payload->at(3) == 0x02) {
398 std::string device_common_name;
399 for (
unsigned int i = 0; i < 32; i++) {
400 device_common_name += payload->at(i + 14);
402 device_common_name +=
'\0';
403 m_device_common_name = device_common_name;
409 unsigned char name[8];
410 for (
unsigned int i = 0; i < 8; i++) name[i] = payload->at(i + 15);
412 memcpy((
void*)&NAME, name, 8);
414 int* f1 = (
int*)&NAME;
416 m_manufacturers_code = f1d >> 21;
425void CommDriverN2KSerial::handle_N2K_SERIAL_RAW(
427 auto p =
event.GetPayload();
429 std::vector<unsigned char>* payload = p.get();
431 if (payload->at(0) == 0xA0) {
432 ProcessManagementPacket(payload);
437 if (m_params.IOSelect != DS_TYPE_OUTPUT) {
440 unsigned char* c = (
unsigned char*)&pgn;
441 *c++ = payload->at(3);
442 *c++ = payload->at(4);
443 *c++ = payload->at(5);
445 auto name = PayloadToName(*payload);
447 std::make_shared<const Nmea2000Msg>(pgn, *payload, GetAddress(name));
449 m_listener.
Notify(std::move(msg));
453int CommDriverN2KSerial::GetMfgCode() {
454 unsigned char request_name[] = {0x42};
455 int ni = SendMgmtMsg(request_name,
sizeof(request_name), 0x41, 2000,
458 m_got_mfg_code =
true;
462int CommDriverN2KSerial::SendMgmtMsg(
unsigned char*
string,
size_t string_size,
463 unsigned char cmd_code,
int timeout_msec,
464 bool* response_flag) {
469 std::vector<unsigned char> msg;
471 msg.push_back(ESCAPE);
472 msg.push_back(STARTOFTEXT);
475 msg.push_back(string_size);
476 byteSum += string_size;
478 for (
unsigned int i = 0; i < string_size; i++) {
479 if (
string[i] == ESCAPE) msg.push_back(
string[i]);
480 msg.push_back(
string[i]);
481 byteSum +=
string[i];
486 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
487 msg.push_back(CheckSum);
489 msg.push_back(ESCAPE);
490 msg.push_back(ENDOFTEXT);
494 if (response_flag) *response_flag =
false;
498 bool not_done =
true;
501 if (GetSecondaryThread() && IsSecThreadActive()) {
504 if (GetSecondaryThread()->SetOutMsg(msg)) {
513 if (ntry_outer-- <= 0) not_done =
false;
517 if (!bsent)
return 1;
521 int timeout = timeout_msec;
522 while (timeout > 0) {
526 if (*response_flag) {
546int CommDriverN2KSerial::SetTXPGN(
int pgn) {
548 unsigned char request_enable[] = {0x47, 0x00, 0x00, 0x00,
549 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF};
552 unsigned char* c = (
unsigned char*)&pgn;
553 request_enable[1] = c[0];
554 request_enable[2] = c[1];
555 request_enable[3] = c[2];
557 int aa = SendMgmtMsg(request_enable,
sizeof(request_enable), 0x47, 2000,
562 unsigned char request_commit[] = {0x01};
563 int bb = SendMgmtMsg(request_commit,
sizeof(request_commit), 0x01, 2000,
567 unsigned char request_activate[] = {0x4B};
568 int cc = SendMgmtMsg(request_activate,
sizeof(request_activate), 0x4B, 2000,
573void CommDriverN2KSerial::AddTxPGN(
int pgn) {
574 auto it = std::find(pgn_tx_list.begin(), pgn_tx_list.end(), pgn);
575 if (it != pgn_tx_list.end())
579 pgn_tx_list.push_back(pgn);
611#define DS_RX_BUFFER_SIZE 4096
613CommDriverN2KSerialThread::CommDriverN2KSerialThread(
615 const wxString& strBaudRate) {
616 m_pParentDriver = Launcher;
618 m_PortName = PortName;
619 m_FullPortName =
"Serial:" + PortName;
628 if (strBaudRate.ToLong(&lbaud)) m_baud = (int)lbaud;
630 std::lock_guard lock(m_stats_mutex);
631 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
632 m_driver_stats.driver_iface = m_pParentDriver->m_params.GetStrippedDSPort();
633 m_driver_stats.available =
false;
639CommDriverN2KSerialThread::~CommDriverN2KSerialThread() {
delete[] rx_buffer; }
641void CommDriverN2KSerialThread::OnExit() {}
643DriverStats CommDriverN2KSerialThread::GetStats()
const {
644 std::lock_guard lock(m_stats_mutex);
645 return m_driver_stats;
648bool CommDriverN2KSerialThread::OpenComPortPhysical(
const wxString& com_name,
651 m_serial.
setPort(com_name.ToStdString());
655 }
catch (std::exception&) {
662void CommDriverN2KSerialThread::CloseComPortPhysical() {
665 }
catch (std::exception&) {
669 std::lock_guard lock(m_stats_mutex);
670 m_driver_stats.available =
false;
673void CommDriverN2KSerialThread::SetGatewayOperationMode() {
677 unsigned char config_string[] = {0x10, 0x02, 0xA1, 0x03, 0x11,
678 0x02, 0x00, 0x49, 0x10, 0x03};
682 WriteComPortPhysical(config_string, 10);
685void CommDriverN2KSerialThread::ThreadMessage(
const wxString& msg) {
692size_t CommDriverN2KSerialThread::WriteComPortPhysical(
693 std::vector<unsigned char> msg) {
694 return WriteComPortPhysical(msg.data(), msg.size());
697size_t CommDriverN2KSerialThread::WriteComPortPhysical(
unsigned char* msg,
699 if (!m_serial.
isOpen())
return 0;
701 size_t status = m_serial.
write((uint8_t*)msg, length);
704 }
catch (std::exception& e) {
705 DEBUG_LOG <<
"Unhandled Exception while writing to serial port: "
711bool CommDriverN2KSerialThread::SetOutMsg(
712 const std::vector<unsigned char>& msg) {
713 if (out_que.size() < OUT_QUEUE_LENGTH) {
721void* CommDriverN2KSerialThread::Entry() {
722 bool not_done =
true;
723 bool nl_found =
false;
730 if (!OpenComPortPhysical(m_PortName, m_baud)) {
731 wxString msg(
"NMEA input device open failed: ");
732 msg.Append(m_PortName);
734 std::lock_guard lock(m_stats_mutex);
735 m_driver_stats.available =
false;
744 std::lock_guard lock(m_stats_mutex);
745 m_driver_stats.available =
true;
746 SetGatewayOperationMode();
749 m_pParentDriver->SetSecThreadActive();
752 static size_t retries = 0;
755 bool bGotESC =
false;
756 bool bGotSOT =
false;
758 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
759 if (TestDestroy()) not_done =
false;
761 uint8_t next_byte = 0;
765 newdata = m_serial.
read(rdata, 1000);
766 }
catch (std::exception& e) {
768 std::lock_guard lock(m_stats_mutex);
771 if (10 < retries++) {
775 CloseComPortPhysical();
783 wxMilliSleep(250 * retries);
784 CloseComPortPhysical();
785 if (OpenComPortPhysical(m_PortName, m_baud)) {
786 SetGatewayOperationMode();
787 std::lock_guard lock(m_stats_mutex);
788 m_driver_stats.available =
true;
790 }
else if (retries < 10) {
791 std::lock_guard lock(m_stats_mutex);
792 m_driver_stats.available =
false;
798 std::lock_guard lock(m_stats_mutex);
801 for (
int i = 0; i < newdata; i++) {
802 circle.put(rdata[i]);
806 while (!circle.empty()) {
808 uint8_t next_byte = circle.get();
812 if (ESCAPE == next_byte) {
813 rx_buffer[ib++] = next_byte;
818 if (bGotESC && (ENDOFTEXT == next_byte)) {
822 auto buffer = std::make_shared<std::vector<unsigned char>>(
823 rx_buffer, rx_buffer + ib);
824 std::vector<unsigned char>* vec = buffer.get();
840 Nevent.SetPayload(buffer);
841 m_pParentDriver->AddPendingEvent(Nevent);
844 bGotESC = (next_byte == ESCAPE);
847 rx_buffer[ib++] = next_byte;
853 if (STARTOFTEXT == next_byte) {
859 bGotESC = (next_byte == ESCAPE);
864 rx_buffer[ib++] = next_byte;
872 bool b_qdata = !out_que.empty();
876 std::vector<unsigned char> qmsg = out_que.front();
879 if (
static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
884 CloseComPortPhysical();
887 b_qdata = !out_que.empty();
894 CloseComPortPhysical();
895 m_pParentDriver->SetSecThreadInActive();
896 m_pParentDriver->m_Thread_run_flag = -1;
898 std::lock_guard lock(m_stats_mutex);
899 m_driver_stats.available =
false;
905void* CommDriverN2KSerialThread::Entry() {
906 bool not_done =
true;
907 bool nl_found =
false;
912 if (!OpenComPortPhysical(m_PortName, m_baud)) {
913 wxString msg(
"NMEA input device open failed: ");
914 msg.Append(m_PortName);
916 std::lock_guard lock(m_stats_mutex);
917 m_driver_stats.available =
false;
925 SetGatewayOperationMode();
926 std::lock_guard lock(m_stats_mutex);
927 m_driver_stats.available =
true;
930 m_pParentDriver->SetSecThreadActive();
933 static size_t retries = 0;
936 bool bGotESC =
false;
937 bool bGotSOT =
false;
939 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
940 if (TestDestroy()) not_done =
false;
942 uint8_t next_byte = 0;
948 newdata = m_serial.
read(rdata, 200);
949 }
catch (std::exception& e) {
951 if (10 < retries++) {
955 CloseComPortPhysical();
963 wxMilliSleep(250 * retries);
964 CloseComPortPhysical();
965 if (OpenComPortPhysical(m_PortName, m_baud)) {
966 std::lock_guard lock(m_stats_mutex);
967 m_driver_stats.available =
true;
968 SetGatewayOperationMode();
970 }
else if (retries < 10)
975 for (
int i = 0; i < newdata; i++) {
976 circle.put(rdata[i]);
980 while (!circle.empty()) {
981 uint8_t next_byte = circle.get();
986 if (ESCAPE == next_byte) {
987 *put_ptr++ = next_byte;
991 }
else if (ENDOFTEXT == next_byte) {
995 auto buffer = std::make_shared<std::vector<unsigned char>>();
996 std::vector<unsigned char>* vec = buffer.get();
1001 while ((tptr != put_ptr)) {
1002 vec->push_back(*tptr++);
1015 Nevent.SetPayload(buffer);
1016 m_pParentDriver->AddPendingEvent(Nevent);
1017 std::lock_guard lock(m_stats_mutex);
1018 m_driver_stats.
rx_count += vec->size();
1019 }
else if (next_byte == STARTOFTEXT) {
1020 put_ptr = rx_buffer;
1023 put_ptr = rx_buffer;
1029 bGotESC = (next_byte == ESCAPE);
1032 *put_ptr++ = next_byte;
1034 put_ptr = rx_buffer;
1040 if (STARTOFTEXT == next_byte) {
1046 bGotESC = (next_byte == ESCAPE);
1051 *put_ptr++ = next_byte;
1053 put_ptr = rx_buffer;
1061 bool b_qdata = !out_que.empty();
1065 std::vector<unsigned char> qmsg = out_que.front();
1068 if (
static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
1073 CloseComPortPhysical();
1075 std::lock_guard lock(m_stats_mutex);
1076 m_driver_stats.
tx_count += qmsg.size();
1078 b_qdata = !out_que.empty();
1083 CloseComPortPhysical();
1084 m_pParentDriver->SetSecThreadInActive();
1085 m_pParentDriver->m_Thread_run_flag = -1;
1098#define MaxActisenseMsgBuf 400
1099#define MsgTypeN2kTX 0x94
1101void AddByteEscapedToBuf(
unsigned char byteToAdd, uint8_t& idx,
1102 unsigned char* buf,
int& byteSum);
1104static std::vector<unsigned char> BufferToActisenseFormat(tN2kMsg& msg) {
1105 unsigned long _PGN = msg.PGN;
1109 unsigned char ActisenseMsgBuf[MaxActisenseMsgBuf];
1111 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1112 ActisenseMsgBuf[msgIdx++] = STARTOFTEXT;
1113 AddByteEscapedToBuf(MsgTypeN2kTX, msgIdx, ActisenseMsgBuf, byteSum);
1114 AddByteEscapedToBuf(msg.DataLen + 6, msgIdx, ActisenseMsgBuf,
1117 AddByteEscapedToBuf(msg.Priority, msgIdx, ActisenseMsgBuf, byteSum);
1118 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1120 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1122 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1123 AddByteEscapedToBuf(msg.Destination, msgIdx, ActisenseMsgBuf, byteSum);
1128 AddByteEscapedToBuf(msg.Source,msgIdx,ActisenseMsgBuf,byteSum);
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); _MsgTime>>=8;
1134 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum);
1138 AddByteEscapedToBuf(msg.DataLen, msgIdx, ActisenseMsgBuf, byteSum);
1140 for (
int i = 0; i < msg.DataLen; i++)
1141 AddByteEscapedToBuf(msg.Data[i], msgIdx, ActisenseMsgBuf, byteSum);
1144 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
1145 ActisenseMsgBuf[msgIdx++] = CheckSum;
1146 if (CheckSum == ESCAPE) ActisenseMsgBuf[msgIdx++] = CheckSum;
1148 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1149 ActisenseMsgBuf[msgIdx++] = ENDOFTEXT;
1151 std::vector<unsigned char> rv;
1152 for (
unsigned int i = 0; i < msgIdx; i++) rv.push_back(ActisenseMsgBuf[i]);
DriverStats GetDriverStats() const override
Get the Driver Statistics.
Interface for handling incoming messages.
virtual void Notify(std::shared_ptr< const NavMsg > message)=0
Handle a received message.
Class that provides a portable serial port interface.
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...
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.