53static unsigned char NGT_STARTUP_SEQ[] = {
59static std::vector<unsigned char> BufferToActisenseFormat(tN2kMsg& msg);
66 const wxString& PortName,
67 const wxString& strBaudRate);
71 bool SetOutMsg(
const std::vector<unsigned char>& load);
79 void ThreadMessage(
const wxString& msg);
80 bool OpenComPortPhysical(
const wxString& com_name,
int baud_rate);
81 void CloseComPortPhysical();
82 size_t WriteComPortPhysical(std::vector<unsigned char> msg);
83 size_t WriteComPortPhysical(
unsigned char* msg,
size_t length);
84 void SetGatewayOperationMode();
88 wxString m_FullPortName;
90 unsigned char* put_ptr;
91 unsigned char* tak_ptr;
93 unsigned char* rx_buffer;
100 mutable std::mutex m_stats_mutex;
102 HANDLE m_hSerialComm;
113 : wxEvent(
id, commandType) {};
117 void SetPayload(std::shared_ptr<std::vector<unsigned char>> data) {
120 std::shared_ptr<std::vector<unsigned char>> GetPayload() {
return m_payload; }
123 wxEvent* Clone()
const {
125 newevent->m_payload = this->m_payload;
130 std::shared_ptr<std::vector<unsigned char>> m_payload;
142 m_Thread_run_flag(-1),
145 m_portstring(params->GetDSPort()),
146 m_pSecondary_Thread(NULL),
147 m_listener(listener),
148 m_stats_timer(*this, 2s),
150 m_BaudRate = wxString::Format(
"%i", params->Baudrate), SetSecThreadInActive();
151 m_manufacturers_code = 0;
152 m_got_mfg_code =
false;
153 this->attributes[
"canAddress"] = std::string(
"-1");
154 this->attributes[
"userComment"] = params->UserComment.ToStdString();
158 Bind(wxEVT_COMMDRIVER_N2K_SERIAL, &CommDriverN2KSerial::handle_N2K_SERIAL_RAW,
162 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
163 m_driver_stats.driver_iface = m_params.GetStrippedDSPort();
164 m_driver_stats.available =
false;
172 SendMgmtMsg(NGT_STARTUP_SEQ,
sizeof(NGT_STARTUP_SEQ), 0x11, 0, NULL);
175CommDriverN2KSerial::~CommDriverN2KSerial() { Close(); }
178 if (m_closing)
return m_driver_stats;
181 if (m_pSecondary_Thread)
182 return m_pSecondary_Thread->GetStats();
185 return m_driver_stats;
188bool CommDriverN2KSerial::Open() {
190 comx = m_params.GetDSPort().AfterFirst(
':');
193 comx.BeforeFirst(
' ');
199 GetSecondaryThread()->Run();
205void CommDriverN2KSerial::Close() {
206 wxLogMessage(wxString::Format(
"Closing N2K Driver %s", m_portstring.c_str()));
208 m_stats_timer.Stop();
212 if (m_pSecondary_Thread) {
213 if (m_bsec_thread_active)
215 wxLogMessage(
"Stopping Secondary Thread");
217 m_Thread_run_flag = 0;
219 while ((m_Thread_run_flag >= 0) && (tsec--)) wxSleep(1);
222 if (m_Thread_run_flag < 0)
223 msg.Printf(
"Stopped in %d sec.", 10 - tsec);
225 msg.Printf(
"Not Stopped after 10 sec.");
229 m_pSecondary_Thread = NULL;
230 m_bsec_thread_active =
false;
233static uint64_t PayloadToName(
const std::vector<unsigned char> payload) {
235 memcpy(&name,
reinterpret_cast<const void*
>(payload.data()),
sizeof(name));
239bool CommDriverN2KSerial::SendMessage(std::shared_ptr<const NavMsg> msg,
240 std::shared_ptr<const NavAddr> addr) {
241 if (m_closing)
return false;
242 if (!msg)
return false;
246 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
247 std::vector<uint8_t> load = msg_n2k->payload;
249 uint64_t _pgn = msg_n2k->PGN.pgn;
250 auto destination_address = std::static_pointer_cast<const NavAddr2000>(addr);
254 N2kMsg.Priority = msg_n2k->priority;
255 if (destination_address) N2kMsg.Destination = destination_address->address;
257 for (
size_t i = 0; i < load.size(); i++) N2kMsg.AddByte(load.at(i));
259 const std::vector<uint8_t> acti_pkg = BufferToActisenseFormat(N2kMsg);
262 std::vector<unsigned char> msg_payload;
263 for (
size_t i = 2; i < acti_pkg.size() - 2; i++)
264 msg_payload.push_back(acti_pkg[i]);
265 auto name = PayloadToName(load);
267 std::make_shared<const Nmea2000Msg>(_pgn, msg_payload, GetAddress(name)));
269 if (GetSecondaryThread()) {
270 if (IsSecThreadActive()) {
273 if (GetSecondaryThread()->SetOutMsg(acti_pkg))
286void CommDriverN2KSerial::ProcessManagementPacket(
287 std::vector<unsigned char>* payload) {
288 if (payload->at(2) != 0xF2) {
295 switch (payload->at(2)) {
307 if (payload->at(3) == 0x02) {
308 std::string device_common_name;
309 for (
unsigned int i = 0; i < 32; i++) {
310 device_common_name += payload->at(i + 14);
312 device_common_name +=
'\0';
313 m_device_common_name = device_common_name;
319 unsigned char name[8];
320 for (
unsigned int i = 0; i < 8; i++) name[i] = payload->at(i + 15);
322 memcpy((
void*)&NAME, name, 8);
324 int* f1 = (
int*)&NAME;
326 m_manufacturers_code = f1d >> 21;
335void CommDriverN2KSerial::handle_N2K_SERIAL_RAW(
337 auto p =
event.GetPayload();
339 std::vector<unsigned char>* payload = p.get();
341 if (payload->at(0) == 0xA0) {
342 ProcessManagementPacket(payload);
347 if (m_params.IOSelect != DS_TYPE_OUTPUT) {
350 unsigned char* c = (
unsigned char*)&pgn;
351 *c++ = payload->at(3);
352 *c++ = payload->at(4);
353 *c++ = payload->at(5);
355 auto name = PayloadToName(*payload);
357 std::make_shared<const Nmea2000Msg>(pgn, *payload, GetAddress(name));
359 m_listener.
Notify(std::move(msg));
363int CommDriverN2KSerial::GetMfgCode() {
364 unsigned char request_name[] = {0x42};
365 int ni = SendMgmtMsg(request_name,
sizeof(request_name), 0x41, 2000,
368 m_got_mfg_code =
true;
372int CommDriverN2KSerial::SendMgmtMsg(
unsigned char*
string,
size_t string_size,
373 unsigned char cmd_code,
int timeout_msec,
374 bool* response_flag) {
379 std::vector<unsigned char> msg;
381 msg.push_back(ESCAPE);
382 msg.push_back(STARTOFTEXT);
385 msg.push_back(string_size);
386 byteSum += string_size;
388 for (
unsigned int i = 0; i < string_size; i++) {
389 if (
string[i] == ESCAPE) msg.push_back(
string[i]);
390 msg.push_back(
string[i]);
391 byteSum +=
string[i];
396 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
397 msg.push_back(CheckSum);
399 msg.push_back(ESCAPE);
400 msg.push_back(ENDOFTEXT);
404 if (response_flag) *response_flag =
false;
408 bool not_done =
true;
411 if (GetSecondaryThread() && IsSecThreadActive()) {
414 if (GetSecondaryThread()->SetOutMsg(msg)) {
423 if (ntry_outer-- <= 0) not_done =
false;
427 if (!bsent)
return 1;
431 int timeout = timeout_msec;
432 while (timeout > 0) {
436 if (*response_flag) {
456int CommDriverN2KSerial::SetTXPGN(
int pgn) {
458 unsigned char request_enable[] = {0x47, 0x00, 0x00, 0x00,
459 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF};
462 unsigned char* c = (
unsigned char*)&pgn;
463 request_enable[1] = c[0];
464 request_enable[2] = c[1];
465 request_enable[3] = c[2];
467 int aa = SendMgmtMsg(request_enable,
sizeof(request_enable), 0x47, 2000,
472 unsigned char request_commit[] = {0x01};
473 int bb = SendMgmtMsg(request_commit,
sizeof(request_commit), 0x01, 2000,
477 unsigned char request_activate[] = {0x4B};
478 int cc = SendMgmtMsg(request_activate,
sizeof(request_activate), 0x4B, 2000,
483void CommDriverN2KSerial::AddTxPGN(
int pgn) {
484 auto it = std::find(pgn_tx_list.begin(), pgn_tx_list.end(), pgn);
485 if (it != pgn_tx_list.end())
489 pgn_tx_list.push_back(pgn);
521#define DS_RX_BUFFER_SIZE 4096
523CommDriverN2KSerialThread::CommDriverN2KSerialThread(
525 const wxString& strBaudRate)
526 : m_out_que(OUT_QUEUE_LENGTH) {
527 m_pParentDriver = Launcher;
529 m_PortName = PortName;
530 m_FullPortName =
"Serial:" + PortName;
539 if (strBaudRate.ToLong(&lbaud)) m_baud = (int)lbaud;
541 std::lock_guard lock(m_stats_mutex);
542 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
543 m_driver_stats.driver_iface = m_pParentDriver->m_params.GetStrippedDSPort();
544 m_driver_stats.available =
false;
550CommDriverN2KSerialThread::~CommDriverN2KSerialThread() {
delete[] rx_buffer; }
552void CommDriverN2KSerialThread::OnExit() {}
554DriverStats CommDriverN2KSerialThread::GetStats()
const {
555 std::lock_guard lock(m_stats_mutex);
556 return m_driver_stats;
559bool CommDriverN2KSerialThread::OpenComPortPhysical(
const wxString& com_name,
562 m_serial.
setPort(com_name.ToStdString());
566 }
catch (std::exception&) {
573void CommDriverN2KSerialThread::CloseComPortPhysical() {
576 }
catch (std::exception&) {
580 std::lock_guard lock(m_stats_mutex);
581 m_driver_stats.available =
false;
584void CommDriverN2KSerialThread::SetGatewayOperationMode() {
588 unsigned char config_string[] = {0x10, 0x02, 0xA1, 0x03, 0x11,
589 0x02, 0x00, 0x49, 0x10, 0x03};
593 WriteComPortPhysical(config_string, 10);
596void CommDriverN2KSerialThread::ThreadMessage(
const wxString& msg) {
603size_t CommDriverN2KSerialThread::WriteComPortPhysical(
604 std::vector<unsigned char> msg) {
605 return WriteComPortPhysical(msg.data(), msg.size());
608size_t CommDriverN2KSerialThread::WriteComPortPhysical(
unsigned char* msg,
610 if (!m_serial.
isOpen())
return 0;
612 size_t status = m_serial.
write((uint8_t*)msg, length);
615 }
catch (std::exception& e) {
616 DEBUG_LOG <<
"Unhandled Exception while writing to serial port: "
622bool CommDriverN2KSerialThread::SetOutMsg(
623 const std::vector<unsigned char>& msg) {
624 if (!m_out_que.
IsFull()) {
632void* CommDriverN2KSerialThread::Entry() {
633 bool not_done =
true;
634 bool nl_found =
false;
641 if (!OpenComPortPhysical(m_PortName, m_baud)) {
642 wxString msg(
"NMEA input device open failed: ");
643 msg.Append(m_PortName);
645 std::lock_guard lock(m_stats_mutex);
646 m_driver_stats.available =
false;
655 std::lock_guard lock(m_stats_mutex);
656 m_driver_stats.available =
true;
657 SetGatewayOperationMode();
660 m_pParentDriver->SetSecThreadActive();
663 static size_t retries = 0;
666 bool bGotESC =
false;
667 bool bGotSOT =
false;
669 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
670 if (TestDestroy()) not_done =
false;
672 uint8_t next_byte = 0;
676 newdata = m_serial.
read(rdata, 1000);
677 }
catch (std::exception& e) {
679 std::lock_guard lock(m_stats_mutex);
682 if (10 < retries++) {
686 CloseComPortPhysical();
694 wxMilliSleep(250 * retries);
695 CloseComPortPhysical();
696 if (OpenComPortPhysical(m_PortName, m_baud)) {
697 SetGatewayOperationMode();
698 std::lock_guard lock(m_stats_mutex);
699 m_driver_stats.available =
true;
701 }
else if (retries < 10) {
702 std::lock_guard lock(m_stats_mutex);
703 m_driver_stats.available =
false;
709 std::lock_guard lock(m_stats_mutex);
712 for (
int i = 0; i < newdata; i++) {
713 circle.Put(rdata[i]);
717 while (!circle.IsEmpty()) {
719 uint8_t next_byte = circle.Get();
723 if (ESCAPE == next_byte) {
724 rx_buffer[ib++] = next_byte;
729 if (bGotESC && (ENDOFTEXT == next_byte)) {
733 auto buffer = std::make_shared<std::vector<unsigned char>>(
734 rx_buffer, rx_buffer + ib);
735 std::vector<unsigned char>* vec = buffer.get();
751 Nevent.SetPayload(buffer);
752 m_pParentDriver->AddPendingEvent(Nevent);
755 bGotESC = (next_byte == ESCAPE);
758 rx_buffer[ib++] = next_byte;
764 if (STARTOFTEXT == next_byte) {
770 bGotESC = (next_byte == ESCAPE);
775 rx_buffer[ib++] = next_byte;
783 bool b_qdata = !m_out_que.
IsEmpty();
787 std::vector<unsigned char> qmsg = m_out_que.
Get();
789 if (
static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
794 CloseComPortPhysical();
797 b_qdata = !m_out_que.
IsEmpty();
804 CloseComPortPhysical();
805 m_pParentDriver->SetSecThreadInActive();
806 m_pParentDriver->m_Thread_run_flag = -1;
808 std::lock_guard lock(m_stats_mutex);
809 m_driver_stats.available =
false;
815void* CommDriverN2KSerialThread::Entry() {
816 bool not_done =
true;
817 bool nl_found =
false;
822 if (!OpenComPortPhysical(m_PortName, m_baud)) {
823 wxString msg(
"NMEA input device open failed: ");
824 msg.Append(m_PortName);
826 std::lock_guard lock(m_stats_mutex);
827 m_driver_stats.available =
false;
835 SetGatewayOperationMode();
836 std::lock_guard lock(m_stats_mutex);
837 m_driver_stats.available =
true;
840 m_pParentDriver->SetSecThreadActive();
843 static size_t retries = 0;
846 bool bGotESC =
false;
847 bool bGotSOT =
false;
849 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
850 if (TestDestroy()) not_done =
false;
852 uint8_t next_byte = 0;
858 newdata = m_serial.
read(rdata, 200);
859 }
catch (std::exception& e) {
861 if (10 < retries++) {
865 CloseComPortPhysical();
873 wxMilliSleep(250 * retries);
874 CloseComPortPhysical();
875 if (OpenComPortPhysical(m_PortName, m_baud)) {
876 std::lock_guard lock(m_stats_mutex);
877 m_driver_stats.available =
true;
878 SetGatewayOperationMode();
880 }
else if (retries < 10)
885 for (
int i = 0; i < newdata; i++) {
886 circle.Put(rdata[i]);
890 while (!circle.IsEmpty()) {
891 uint8_t next_byte = circle.Get();
896 if (ESCAPE == next_byte) {
897 *put_ptr++ = next_byte;
901 }
else if (ENDOFTEXT == next_byte) {
905 auto buffer = std::make_shared<std::vector<unsigned char>>();
906 std::vector<unsigned char>* vec = buffer.get();
911 while ((tptr != put_ptr)) {
912 vec->push_back(*tptr++);
925 Nevent.SetPayload(buffer);
926 m_pParentDriver->AddPendingEvent(Nevent);
927 std::lock_guard lock(m_stats_mutex);
928 m_driver_stats.
rx_count += vec->size();
929 }
else if (next_byte == STARTOFTEXT) {
939 bGotESC = (next_byte == ESCAPE);
942 *put_ptr++ = next_byte;
950 if (STARTOFTEXT == next_byte) {
956 bGotESC = (next_byte == ESCAPE);
961 *put_ptr++ = next_byte;
971 bool b_qdata = !m_out_que.
IsEmpty();
975 std::vector<unsigned char> qmsg = m_out_que.
Get();
977 if (
static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
982 CloseComPortPhysical();
984 std::lock_guard lock(m_stats_mutex);
985 m_driver_stats.
tx_count += qmsg.size();
990 CloseComPortPhysical();
991 m_pParentDriver->SetSecThreadInActive();
992 m_pParentDriver->m_Thread_run_flag = -1;
1005#define MaxActisenseMsgBuf 400
1006#define MsgTypeN2kTX 0x94
1008void AddByteEscapedToBuf(
unsigned char byteToAdd, uint8_t& idx,
1009 unsigned char* buf,
int& byteSum);
1011static std::vector<unsigned char> BufferToActisenseFormat(tN2kMsg& msg) {
1012 unsigned long _PGN = msg.PGN;
1016 unsigned char ActisenseMsgBuf[MaxActisenseMsgBuf];
1018 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1019 ActisenseMsgBuf[msgIdx++] = STARTOFTEXT;
1020 AddByteEscapedToBuf(MsgTypeN2kTX, msgIdx, ActisenseMsgBuf, byteSum);
1021 AddByteEscapedToBuf(msg.DataLen + 6, msgIdx, ActisenseMsgBuf,
1024 AddByteEscapedToBuf(msg.Priority, msgIdx, ActisenseMsgBuf, byteSum);
1025 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1027 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1029 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1030 AddByteEscapedToBuf(msg.Destination, msgIdx, ActisenseMsgBuf, byteSum);
1035 AddByteEscapedToBuf(msg.Source,msgIdx,ActisenseMsgBuf,byteSum);
1038 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1039 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1040 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1041 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum);
1045 AddByteEscapedToBuf(msg.DataLen, msgIdx, ActisenseMsgBuf, byteSum);
1047 for (
int i = 0; i < msg.DataLen; i++)
1048 AddByteEscapedToBuf(msg.Data[i], msgIdx, ActisenseMsgBuf, byteSum);
1051 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
1052 ActisenseMsgBuf[msgIdx++] = CheckSum;
1053 if (CheckSum == ESCAPE) ActisenseMsgBuf[msgIdx++] = CheckSum;
1055 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1056 ActisenseMsgBuf[msgIdx++] = ENDOFTEXT;
1058 std::vector<unsigned char> rv;
1059 for (
unsigned int i = 0; i < msgIdx; i++) rv.push_back(ActisenseMsgBuf[i]);
Fixed size, synchronized FIFO buffer.
bool IsEmpty() const noexcept
Return true if buffer is empty.
T Get()
Get item from buff; throw BufferError if empty.
void Put(const T &item)
Add item to buffer; throw BufferError if full.
bool IsFull() const noexcept
Return true if buffer is full.
DriverStats GetDriverStats() const override
Get the Driver Statistics.
Interface for handling incoming messages.
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.
Various input/output buffers.
#define DS_RX_BUFFER_SIZE
This thread manages reading the N2K data stream provided by some N2K gateways from the declared seria...
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.