39#include "model/comm_drv_n2k_serial.h"
54static unsigned char NGT_STARTUP_SEQ[] = {
60std::vector<unsigned char> BufferToActisenseFormat(tN2kMsg& msg);
66 std::lock_guard<std::mutex> lock(m_mutex);
67 return m_queque.size();
71 std::lock_guard<std::mutex> lock(m_mutex);
72 return m_queque.empty();
76 std::lock_guard<std::mutex> lock(m_mutex);
77 return m_queque.front();
80 void push(
const T& value) {
81 std::lock_guard<std::mutex> lock(m_mutex);
86 std::lock_guard<std::mutex> lock(m_mutex);
91 std::queue<T> m_queque;
92 mutable std::mutex m_mutex;
99 : buf_(std::unique_ptr<T[]>(new T[size])), max_size_(size) {}
102 size_t capacity()
const;
107 return (!full_ && (head_ == tail_));
116 std::lock_guard<std::mutex> lock(mutex_);
118 if (full_) tail_ = (tail_ + 1) % max_size_;
120 head_ = (head_ + 1) % max_size_;
122 full_ = head_ == tail_;
126 std::lock_guard<std::mutex> lock(mutex_);
128 if (empty())
return T();
131 auto val = buf_[tail_];
133 tail_ = (tail_ + 1) % max_size_;
140 std::unique_ptr<T[]> buf_;
143 const size_t max_size_;
152 const wxString& PortName,
153 const wxString& strBaudRate);
157 bool SetOutMsg(
const std::vector<unsigned char>& load);
165 void ThreadMessage(
const wxString& msg);
166 bool OpenComPortPhysical(
const wxString& com_name,
int baud_rate);
167 void CloseComPortPhysical();
168 size_t WriteComPortPhysical(std::vector<unsigned char> msg);
169 size_t WriteComPortPhysical(
unsigned char* msg,
size_t length);
170 void SetGatewayOperationMode(
void);
174 wxString m_FullPortName;
176 unsigned char* put_ptr;
177 unsigned char* tak_ptr;
179 unsigned char* rx_buffer;
186 mutable std::mutex m_stats_mutex;
188 HANDLE m_hSerialComm;
199 : wxEvent(
id, commandType) {};
203 void SetPayload(std::shared_ptr<std::vector<unsigned char>> data) {
206 std::shared_ptr<std::vector<unsigned char>> GetPayload() {
return m_payload; }
209 wxEvent* Clone()
const {
211 newevent->m_payload = this->m_payload;
216 std::shared_ptr<std::vector<unsigned char>> m_payload;
228 m_Thread_run_flag(-1),
231 m_portstring(params->GetDSPort()),
232 m_pSecondary_Thread(NULL),
233 m_listener(listener),
234 m_stats_timer(*this, 2s),
236 m_BaudRate = wxString::Format(
"%i", params->Baudrate), SetSecThreadInActive();
237 m_manufacturers_code = 0;
238 m_got_mfg_code =
false;
239 this->attributes[
"canAddress"] = std::string(
"-1");
240 this->attributes[
"userComment"] = params->UserComment.ToStdString();
241 this->attributes[
"ioDirection"] = DsPortTypeToString(params->IOSelect);
244 Bind(wxEVT_COMMDRIVER_N2K_SERIAL, &CommDriverN2KSerial::handle_N2K_SERIAL_RAW,
248 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
249 m_driver_stats.driver_iface = m_params.GetStrippedDSPort();
250 m_driver_stats.available =
false;
258 SendMgmtMsg(NGT_STARTUP_SEQ,
sizeof(NGT_STARTUP_SEQ), 0x11, 0, NULL);
267 N2kMsg.SetPGN(126993L);
270 N2kMsg.Destination = 133;
271 N2kMsg.Add2ByteUInt((uint16_t)(2000));
274 N2kMsg.AddByte(0xff);
275 N2kMsg.Add4ByteUInt(0xffffffff);
277 const std::vector<unsigned char> mv = BufferToActisenseFormat(N2kMsg);
279 size_t len = mv.size();
281 wxString comx = m_params.GetDSPort().AfterFirst(
':');
282 std::string
interface = comx.ToStdString();
285 auto source_address = std::make_shared<NavAddr2000>(interface, source_name);
286 auto dest_address = std::make_shared<NavAddr2000>(interface, N2kMsg.Destination);
288 auto message_to_send = std::make_shared<Nmea2000Msg>(126993L,
289 mv, source_address, 3);
291 for(
size_t i=0; i< mv.size(); i++){
292 printf(
"%02X ", mv.at(i));
299 SendMessage(message_to_send, dest_address);
305CommDriverN2KSerial::~CommDriverN2KSerial() { Close(); }
308 if (m_closing)
return m_driver_stats;
311 if (m_pSecondary_Thread)
312 return m_pSecondary_Thread->GetStats();
315 return m_driver_stats;
318bool CommDriverN2KSerial::Open() {
320 comx = m_params.GetDSPort().AfterFirst(
':');
323 comx.BeforeFirst(
' ');
329 GetSecondaryThread()->Run();
335void CommDriverN2KSerial::Close() {
337 wxString::Format(_T(
"Closing N2K Driver %s"), m_portstring.c_str()));
339 m_stats_timer.Stop();
343 if (m_pSecondary_Thread) {
344 if (m_bsec_thread_active)
346 wxLogMessage(_T(
"Stopping Secondary Thread"));
348 m_Thread_run_flag = 0;
350 while ((m_Thread_run_flag >= 0) && (tsec--)) wxSleep(1);
353 if (m_Thread_run_flag < 0)
354 msg.Printf(_T(
"Stopped in %d sec."), 10 - tsec);
356 msg.Printf(_T(
"Not Stopped after 10 sec."));
360 m_pSecondary_Thread = NULL;
361 m_bsec_thread_active =
false;
364static uint64_t PayloadToName(
const std::vector<unsigned char> payload) {
366 memcpy(&name,
reinterpret_cast<const void*
>(payload.data()),
sizeof(name));
370bool CommDriverN2KSerial::SendMessage(std::shared_ptr<const NavMsg> msg,
371 std::shared_ptr<const NavAddr> addr) {
372 if (m_closing)
return false;
376 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
377 std::vector<uint8_t> load = msg_n2k->payload;
379 uint64_t _pgn = msg_n2k->PGN.pgn;
380 auto destination_address = std::static_pointer_cast<const NavAddr2000>(addr);
384 N2kMsg.Priority = msg_n2k->priority;
385 if (destination_address) N2kMsg.Destination = destination_address->address;
387 for (
size_t i = 0; i < load.size(); i++) N2kMsg.AddByte(load.at(i));
389 const std::vector<uint8_t> acti_pkg = BufferToActisenseFormat(N2kMsg);
392 std::vector<unsigned char> msg_payload;
393 for (
size_t i = 2; i < acti_pkg.size() - 2; i++)
394 msg_payload.push_back(acti_pkg[i]);
395 auto name = PayloadToName(load);
397 std::make_shared<const Nmea2000Msg>(1, msg_payload, GetAddress(name));
399 std::make_shared<const Nmea2000Msg>(_pgn, msg_payload, GetAddress(name));
402 m_listener.
Notify(std::move(msg_internal));
403 m_listener.
Notify(std::move(msg_all));
405 if (GetSecondaryThread()) {
406 if (IsSecThreadActive()) {
409 if (GetSecondaryThread()->SetOutMsg(acti_pkg))
422void CommDriverN2KSerial::ProcessManagementPacket(
423 std::vector<unsigned char>* payload) {
424 if (payload->at(2) != 0xF2) {
431 switch (payload->at(2)) {
443 if (payload->at(3) == 0x02) {
444 std::string device_common_name;
445 for (
unsigned int i = 0; i < 32; i++) {
446 device_common_name += payload->at(i + 14);
448 device_common_name +=
'\0';
449 m_device_common_name = device_common_name;
455 unsigned char name[8];
456 for (
unsigned int i = 0; i < 8; i++) name[i] = payload->at(i + 15);
458 memcpy((
void*)&NAME, name, 8);
460 int* f1 = (
int*)&NAME;
462 m_manufacturers_code = f1d >> 21;
471void CommDriverN2KSerial::handle_N2K_SERIAL_RAW(
473 auto p =
event.GetPayload();
475 std::vector<unsigned char>* payload = p.get();
477 if (payload->at(0) == 0xA0) {
478 ProcessManagementPacket(payload);
483 if (m_params.IOSelect != DS_TYPE_OUTPUT) {
486 unsigned char* c = (
unsigned char*)&pgn;
487 *c++ = payload->at(3);
488 *c++ = payload->at(4);
489 *c++ = payload->at(5);
491 auto name = PayloadToName(*payload);
493 std::make_shared<const Nmea2000Msg>(pgn, *payload, GetAddress(name));
495 m_listener.
Notify(std::move(msg));
499int CommDriverN2KSerial::GetMfgCode() {
500 unsigned char request_name[] = {0x42};
501 int ni = SendMgmtMsg(request_name,
sizeof(request_name), 0x41, 2000,
504 m_got_mfg_code =
true;
508int CommDriverN2KSerial::SendMgmtMsg(
unsigned char*
string,
size_t string_size,
509 unsigned char cmd_code,
int timeout_msec,
510 bool* response_flag) {
515 std::vector<unsigned char> msg;
517 msg.push_back(ESCAPE);
518 msg.push_back(STARTOFTEXT);
521 msg.push_back(string_size);
522 byteSum += string_size;
524 for (
unsigned int i = 0; i < string_size; i++) {
525 if (
string[i] == ESCAPE) msg.push_back(
string[i]);
526 msg.push_back(
string[i]);
527 byteSum +=
string[i];
532 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
533 msg.push_back(CheckSum);
535 msg.push_back(ESCAPE);
536 msg.push_back(ENDOFTEXT);
540 if (response_flag) *response_flag =
false;
544 bool not_done =
true;
547 if (GetSecondaryThread() && IsSecThreadActive()) {
550 if (GetSecondaryThread()->SetOutMsg(msg)) {
559 if (ntry_outer-- <= 0) not_done =
false;
563 if (!bsent)
return 1;
567 int timeout = timeout_msec;
568 while (timeout > 0) {
572 if (*response_flag) {
592int CommDriverN2KSerial::SetTXPGN(
int pgn) {
594 unsigned char request_enable[] = {0x47, 0x00, 0x00, 0x00,
595 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF};
598 unsigned char* c = (
unsigned char*)&pgn;
599 request_enable[1] = c[0];
600 request_enable[2] = c[1];
601 request_enable[3] = c[2];
603 int aa = SendMgmtMsg(request_enable,
sizeof(request_enable), 0x47, 2000,
608 unsigned char request_commit[] = {0x01};
609 int bb = SendMgmtMsg(request_commit,
sizeof(request_commit), 0x01, 2000,
613 unsigned char request_activate[] = {0x4B};
614 int cc = SendMgmtMsg(request_activate,
sizeof(request_activate), 0x4B, 2000,
619void CommDriverN2KSerial::AddTxPGN(
int pgn) {
620 auto it = std::find(pgn_tx_list.begin(), pgn_tx_list.end(), pgn);
621 if (it != pgn_tx_list.end())
625 pgn_tx_list.push_back(pgn);
657#define DS_RX_BUFFER_SIZE 4096
659CommDriverN2KSerialThread::CommDriverN2KSerialThread(
661 const wxString& strBaudRate) {
662 m_pParentDriver = Launcher;
664 m_PortName = PortName;
665 m_FullPortName = _T(
"Serial:") + PortName;
667 rx_buffer =
new unsigned char[DS_RX_BUFFER_SIZE + 1];
674 if (strBaudRate.ToLong(&lbaud)) m_baud = (int)lbaud;
676 std::lock_guard lock(m_stats_mutex);
677 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
678 m_driver_stats.driver_iface = m_pParentDriver->m_params.GetStrippedDSPort();
679 m_driver_stats.available =
false;
685CommDriverN2KSerialThread::~CommDriverN2KSerialThread(
void) {
689void CommDriverN2KSerialThread::OnExit(
void) {}
691DriverStats CommDriverN2KSerialThread::GetStats()
const {
692 std::lock_guard lock(m_stats_mutex);
693 return m_driver_stats;
696bool CommDriverN2KSerialThread::OpenComPortPhysical(
const wxString& com_name,
699 m_serial.
setPort(com_name.ToStdString());
703 }
catch (std::exception&) {
710void CommDriverN2KSerialThread::CloseComPortPhysical() {
713 }
catch (std::exception&) {
717 std::lock_guard lock(m_stats_mutex);
718 m_driver_stats.available =
false;
721void CommDriverN2KSerialThread::SetGatewayOperationMode(
void) {
725 unsigned char config_string[] = {0x10, 0x02, 0xA1, 0x03, 0x11,
726 0x02, 0x00, 0x49, 0x10, 0x03};
730 WriteComPortPhysical(config_string, 10);
733void CommDriverN2KSerialThread::ThreadMessage(
const wxString& msg) {
740size_t CommDriverN2KSerialThread::WriteComPortPhysical(
741 std::vector<unsigned char> msg) {
742 return WriteComPortPhysical(msg.data(), msg.size());
745size_t CommDriverN2KSerialThread::WriteComPortPhysical(
unsigned char* msg,
747 if (!m_serial.
isOpen())
return 0;
749 size_t status = m_serial.
write((uint8_t*)msg, length);
752 }
catch (std::exception& e) {
753 DEBUG_LOG <<
"Unhandled Exception while writing to serial port: "
759bool CommDriverN2KSerialThread::SetOutMsg(
760 const std::vector<unsigned char>& msg) {
761 if (out_que.size() < OUT_QUEUE_LENGTH) {
769void* CommDriverN2KSerialThread::Entry() {
770 bool not_done =
true;
771 bool nl_found =
false;
778 if (!OpenComPortPhysical(m_PortName, m_baud)) {
779 wxString msg(_T(
"NMEA input device open failed: "));
780 msg.Append(m_PortName);
782 std::lock_guard lock(m_stats_mutex);
783 m_driver_stats.available =
false;
792 std::lock_guard lock(m_stats_mutex);
793 m_driver_stats.available =
true;
794 SetGatewayOperationMode();
797 m_pParentDriver->SetSecThreadActive();
800 static size_t retries = 0;
803 bool bGotESC =
false;
804 bool bGotSOT =
false;
806 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
807 if (TestDestroy()) not_done =
false;
809 uint8_t next_byte = 0;
813 newdata = m_serial.
read(rdata, 1000);
814 }
catch (std::exception& e) {
816 std::lock_guard lock(m_stats_mutex);
819 if (10 < retries++) {
823 CloseComPortPhysical();
831 wxMilliSleep(250 * retries);
832 CloseComPortPhysical();
833 if (OpenComPortPhysical(m_PortName, m_baud)) {
834 SetGatewayOperationMode();
835 std::lock_guard lock(m_stats_mutex);
836 m_driver_stats.available =
true;
838 }
else if (retries < 10) {
839 std::lock_guard lock(m_stats_mutex);
840 m_driver_stats.available =
false;
846 std::lock_guard lock(m_stats_mutex);
849 for (
int i = 0; i < newdata; i++) {
850 circle.put(rdata[i]);
854 while (!circle.empty()) {
855 if (ib >= DS_RX_BUFFER_SIZE) ib = 0;
856 uint8_t next_byte = circle.get();
860 if (ESCAPE == next_byte) {
861 rx_buffer[ib++] = next_byte;
866 if (bGotESC && (ENDOFTEXT == next_byte)) {
870 auto buffer = std::make_shared<std::vector<unsigned char>>(
871 rx_buffer, rx_buffer + ib);
872 std::vector<unsigned char>* vec = buffer.get();
888 Nevent.SetPayload(buffer);
889 m_pParentDriver->AddPendingEvent(Nevent);
892 bGotESC = (next_byte == ESCAPE);
895 rx_buffer[ib++] = next_byte;
901 if (STARTOFTEXT == next_byte) {
907 bGotESC = (next_byte == ESCAPE);
912 rx_buffer[ib++] = next_byte;
920 bool b_qdata = !out_que.empty();
924 std::vector<unsigned char> qmsg = out_que.front();
927 if (
static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
932 CloseComPortPhysical();
935 b_qdata = !out_que.empty();
942 CloseComPortPhysical();
943 m_pParentDriver->SetSecThreadInActive();
944 m_pParentDriver->m_Thread_run_flag = -1;
946 std::lock_guard lock(m_stats_mutex);
947 m_driver_stats.available =
false;
953void* CommDriverN2KSerialThread::Entry() {
954 bool not_done =
true;
955 bool nl_found =
false;
960 if (!OpenComPortPhysical(m_PortName, m_baud)) {
961 wxString msg(_T(
"NMEA input device open failed: "));
962 msg.Append(m_PortName);
964 std::lock_guard lock(m_stats_mutex);
965 m_driver_stats.available =
false;
973 SetGatewayOperationMode();
974 std::lock_guard lock(m_stats_mutex);
975 m_driver_stats.available =
true;
978 m_pParentDriver->SetSecThreadActive();
981 static size_t retries = 0;
984 bool bGotESC =
false;
985 bool bGotSOT =
false;
987 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
988 if (TestDestroy()) not_done =
false;
990 uint8_t next_byte = 0;
996 newdata = m_serial.
read(rdata, 200);
997 }
catch (std::exception& e) {
999 if (10 < retries++) {
1003 CloseComPortPhysical();
1011 wxMilliSleep(250 * retries);
1012 CloseComPortPhysical();
1013 if (OpenComPortPhysical(m_PortName, m_baud)) {
1014 std::lock_guard lock(m_stats_mutex);
1015 m_driver_stats.available =
true;
1016 SetGatewayOperationMode();
1018 }
else if (retries < 10)
1023 for (
int i = 0; i < newdata; i++) {
1024 circle.put(rdata[i]);
1028 while (!circle.empty()) {
1029 uint8_t next_byte = circle.get();
1034 if (ESCAPE == next_byte) {
1035 *put_ptr++ = next_byte;
1036 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1037 put_ptr = rx_buffer;
1039 }
else if (ENDOFTEXT == next_byte) {
1043 auto buffer = std::make_shared<std::vector<unsigned char>>();
1044 std::vector<unsigned char>* vec = buffer.get();
1046 unsigned char* tptr;
1049 while ((tptr != put_ptr)) {
1050 vec->push_back(*tptr++);
1051 if ((tptr - rx_buffer) > DS_RX_BUFFER_SIZE) tptr = rx_buffer;
1063 Nevent.SetPayload(buffer);
1064 m_pParentDriver->AddPendingEvent(Nevent);
1065 std::lock_guard lock(m_stats_mutex);
1066 m_driver_stats.
rx_count += vec->size();
1067 }
else if (next_byte == STARTOFTEXT) {
1068 put_ptr = rx_buffer;
1071 put_ptr = rx_buffer;
1077 bGotESC = (next_byte == ESCAPE);
1080 *put_ptr++ = next_byte;
1081 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1082 put_ptr = rx_buffer;
1088 if (STARTOFTEXT == next_byte) {
1094 bGotESC = (next_byte == ESCAPE);
1099 *put_ptr++ = next_byte;
1100 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1101 put_ptr = rx_buffer;
1109 bool b_qdata = !out_que.empty();
1113 std::vector<unsigned char> qmsg = out_que.front();
1116 if (
static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
1121 CloseComPortPhysical();
1123 std::lock_guard lock(m_stats_mutex);
1124 m_driver_stats.
tx_count += qmsg.size();
1126 b_qdata = !out_que.empty();
1131 CloseComPortPhysical();
1132 m_pParentDriver->SetSecThreadInActive();
1133 m_pParentDriver->m_Thread_run_flag = -1;
1146#define MaxActisenseMsgBuf 400
1147#define MsgTypeN2kTX 0x94
1149void AddByteEscapedToBuf(
unsigned char byteToAdd, uint8_t& idx,
1150 unsigned char* buf,
int& byteSum);
1152std::vector<unsigned char> BufferToActisenseFormat(tN2kMsg& msg) {
1153 unsigned long _PGN = msg.PGN;
1157 unsigned char ActisenseMsgBuf[MaxActisenseMsgBuf];
1159 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1160 ActisenseMsgBuf[msgIdx++] = STARTOFTEXT;
1161 AddByteEscapedToBuf(MsgTypeN2kTX, msgIdx, ActisenseMsgBuf, byteSum);
1162 AddByteEscapedToBuf(msg.DataLen + 6, msgIdx, ActisenseMsgBuf,
1165 AddByteEscapedToBuf(msg.Priority, msgIdx, ActisenseMsgBuf, byteSum);
1166 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1168 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1170 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1171 AddByteEscapedToBuf(msg.Destination, msgIdx, ActisenseMsgBuf, byteSum);
1176 AddByteEscapedToBuf(msg.Source,msgIdx,ActisenseMsgBuf,byteSum);
1179 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1180 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1181 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1182 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum);
1186 AddByteEscapedToBuf(msg.DataLen, msgIdx, ActisenseMsgBuf, byteSum);
1188 for (
int i = 0; i < msg.DataLen; i++)
1189 AddByteEscapedToBuf(msg.Data[i], msgIdx, ActisenseMsgBuf, byteSum);
1192 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
1193 ActisenseMsgBuf[msgIdx++] = CheckSum;
1194 if (CheckSum == ESCAPE) ActisenseMsgBuf[msgIdx++] = CheckSum;
1196 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1197 ActisenseMsgBuf[msgIdx++] = ENDOFTEXT;
1199 std::vector<unsigned char> rv;
1200 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.
Driver registration container, a singleton.
Communication statistics infrastructure.
Raw messages layer, supports sending and recieving navmsg messages.
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.
N2k uses CAN which defines the basic properties of messages.