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"] = std::string(
"IN/OUT");
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(); }
307DriverStats CommDriverN2KSerial::GetDriverStats()
const {
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);
484 unsigned char* c = (
unsigned char*)&pgn;
485 *c++ = payload->at(3);
486 *c++ = payload->at(4);
487 *c++ = payload->at(5);
491 auto name = PayloadToName(*payload);
493 std::make_shared<const Nmea2000Msg>(pgn, *payload, GetAddress(name));
495 std::make_shared<const Nmea2000Msg>(1, *payload, GetAddress(name));
497 m_listener.
Notify(std::move(msg));
498 m_listener.
Notify(std::move(msg_all));
501 size_t packetLength = (size_t)payload->at(1);
502 size_t vector_length = payload->size();
507 printf(
"Payload Length: %ld\n", vector_length);
509 printf(
"PGN: %ld\n", pgn);
511 for(
size_t i=0; i< vector_length ; i++){
512 printf(
"%02X ", payload->at(i));
519int CommDriverN2KSerial::GetMfgCode() {
520 unsigned char request_name[] = {0x42};
521 int ni = SendMgmtMsg(request_name,
sizeof(request_name), 0x41, 2000,
524 m_got_mfg_code =
true;
528int CommDriverN2KSerial::SendMgmtMsg(
unsigned char*
string,
size_t string_size,
529 unsigned char cmd_code,
int timeout_msec,
530 bool* response_flag) {
535 std::vector<unsigned char> msg;
537 msg.push_back(ESCAPE);
538 msg.push_back(STARTOFTEXT);
541 msg.push_back(string_size);
542 byteSum += string_size;
544 for (
unsigned int i = 0; i < string_size; i++) {
545 if (
string[i] == ESCAPE) msg.push_back(
string[i]);
546 msg.push_back(
string[i]);
547 byteSum +=
string[i];
552 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
553 msg.push_back(CheckSum);
555 msg.push_back(ESCAPE);
556 msg.push_back(ENDOFTEXT);
560 if (response_flag) *response_flag =
false;
564 bool not_done =
true;
567 if (GetSecondaryThread() && IsSecThreadActive()) {
570 if (GetSecondaryThread()->SetOutMsg(msg)) {
579 if (ntry_outer-- <= 0) not_done =
false;
583 if (!bsent)
return 1;
587 int timeout = timeout_msec;
588 while (timeout > 0) {
592 if (*response_flag) {
612int CommDriverN2KSerial::SetTXPGN(
int pgn) {
614 unsigned char request_enable[] = {0x47, 0x00, 0x00, 0x00,
615 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF};
618 unsigned char* c = (
unsigned char*)&pgn;
619 request_enable[1] = c[0];
620 request_enable[2] = c[1];
621 request_enable[3] = c[2];
623 int aa = SendMgmtMsg(request_enable,
sizeof(request_enable), 0x47, 2000,
628 unsigned char request_commit[] = {0x01};
629 int bb = SendMgmtMsg(request_commit,
sizeof(request_commit), 0x01, 2000,
633 unsigned char request_activate[] = {0x4B};
634 int cc = SendMgmtMsg(request_activate,
sizeof(request_activate), 0x4B, 2000,
639void CommDriverN2KSerial::AddTxPGN(
int pgn) {
640 auto it = std::find(pgn_tx_list.begin(), pgn_tx_list.end(), pgn);
641 if (it != pgn_tx_list.end())
645 pgn_tx_list.push_back(pgn);
677#define DS_RX_BUFFER_SIZE 4096
679CommDriverN2KSerialThread::CommDriverN2KSerialThread(
681 const wxString& strBaudRate) {
682 m_pParentDriver = Launcher;
684 m_PortName = PortName;
685 m_FullPortName = _T(
"Serial:") + PortName;
687 rx_buffer =
new unsigned char[DS_RX_BUFFER_SIZE + 1];
694 if (strBaudRate.ToLong(&lbaud)) m_baud = (int)lbaud;
696 std::lock_guard lock(m_stats_mutex);
697 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
698 m_driver_stats.driver_iface = m_pParentDriver->m_params.GetStrippedDSPort();
699 m_driver_stats.available =
false;
705CommDriverN2KSerialThread::~CommDriverN2KSerialThread(
void) {
709void CommDriverN2KSerialThread::OnExit(
void) {}
711DriverStats CommDriverN2KSerialThread::GetStats()
const {
712 std::lock_guard lock(m_stats_mutex);
713 return m_driver_stats;
716bool CommDriverN2KSerialThread::OpenComPortPhysical(
const wxString& com_name,
719 m_serial.
setPort(com_name.ToStdString());
723 }
catch (std::exception&) {
730void CommDriverN2KSerialThread::CloseComPortPhysical() {
733 }
catch (std::exception&) {
737 std::lock_guard lock(m_stats_mutex);
738 m_driver_stats.available =
false;
741void CommDriverN2KSerialThread::SetGatewayOperationMode(
void) {
745 unsigned char config_string[] = {0x10, 0x02, 0xA1, 0x03, 0x11,
746 0x02, 0x00, 0x49, 0x10, 0x03};
750 WriteComPortPhysical(config_string, 10);
753void CommDriverN2KSerialThread::ThreadMessage(
const wxString& msg) {
760size_t CommDriverN2KSerialThread::WriteComPortPhysical(
761 std::vector<unsigned char> msg) {
762 return WriteComPortPhysical(msg.data(), msg.size());
765size_t CommDriverN2KSerialThread::WriteComPortPhysical(
unsigned char* msg,
767 if (!m_serial.
isOpen())
return 0;
769 size_t status = m_serial.
write((uint8_t*)msg, length);
772 }
catch (std::exception& e) {
773 DEBUG_LOG <<
"Unhandled Exception while writing to serial port: "
779bool CommDriverN2KSerialThread::SetOutMsg(
780 const std::vector<unsigned char>& msg) {
781 if (out_que.size() < OUT_QUEUE_LENGTH) {
789void* CommDriverN2KSerialThread::Entry() {
790 bool not_done =
true;
791 bool nl_found =
false;
798 if (!OpenComPortPhysical(m_PortName, m_baud)) {
799 wxString msg(_T(
"NMEA input device open failed: "));
800 msg.Append(m_PortName);
802 std::lock_guard lock(m_stats_mutex);
803 m_driver_stats.available =
false;
812 std::lock_guard lock(m_stats_mutex);
813 m_driver_stats.available =
true;
814 SetGatewayOperationMode();
817 m_pParentDriver->SetSecThreadActive();
820 static size_t retries = 0;
823 bool bGotESC =
false;
824 bool bGotSOT =
false;
826 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
827 if (TestDestroy()) not_done =
false;
829 uint8_t next_byte = 0;
833 newdata = m_serial.
read(rdata, 1000);
834 }
catch (std::exception& e) {
836 std::lock_guard lock(m_stats_mutex);
839 if (10 < retries++) {
843 CloseComPortPhysical();
851 wxMilliSleep(250 * retries);
852 CloseComPortPhysical();
853 if (OpenComPortPhysical(m_PortName, m_baud)) {
854 SetGatewayOperationMode();
855 std::lock_guard lock(m_stats_mutex);
856 m_driver_stats.available =
true;
858 }
else if (retries < 10) {
859 std::lock_guard lock(m_stats_mutex);
860 m_driver_stats.available =
false;
866 std::lock_guard lock(m_stats_mutex);
869 for (
int i = 0; i < newdata; i++) {
870 circle.put(rdata[i]);
874 while (!circle.empty()) {
875 if (ib >= DS_RX_BUFFER_SIZE) ib = 0;
876 uint8_t next_byte = circle.get();
880 if (ESCAPE == next_byte) {
881 rx_buffer[ib++] = next_byte;
886 if (bGotESC && (ENDOFTEXT == next_byte)) {
890 auto buffer = std::make_shared<std::vector<unsigned char>>(
891 rx_buffer, rx_buffer + ib);
892 std::vector<unsigned char>* vec = buffer.get();
908 Nevent.SetPayload(buffer);
909 m_pParentDriver->AddPendingEvent(Nevent);
912 bGotESC = (next_byte == ESCAPE);
915 rx_buffer[ib++] = next_byte;
921 if (STARTOFTEXT == next_byte) {
927 bGotESC = (next_byte == ESCAPE);
932 rx_buffer[ib++] = next_byte;
940 bool b_qdata = !out_que.empty();
944 std::vector<unsigned char> qmsg = out_que.front();
947 if (
static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
952 CloseComPortPhysical();
955 b_qdata = !out_que.empty();
962 CloseComPortPhysical();
963 m_pParentDriver->SetSecThreadInActive();
964 m_pParentDriver->m_Thread_run_flag = -1;
966 std::lock_guard lock(m_stats_mutex);
967 m_driver_stats.available =
false;
973void* CommDriverN2KSerialThread::Entry() {
974 bool not_done =
true;
975 bool nl_found =
false;
980 if (!OpenComPortPhysical(m_PortName, m_baud)) {
981 wxString msg(_T(
"NMEA input device open failed: "));
982 msg.Append(m_PortName);
990 SetGatewayOperationMode();
993 m_pParentDriver->SetSecThreadActive();
996 static size_t retries = 0;
999 bool bGotESC =
false;
1000 bool bGotSOT =
false;
1002 while ((not_done) && (m_pParentDriver->m_Thread_run_flag > 0)) {
1003 if (TestDestroy()) not_done =
false;
1005 uint8_t next_byte = 0;
1007 uint8_t rdata[2000];
1011 newdata = m_serial.
read(rdata, 200);
1012 }
catch (std::exception& e) {
1014 if (10 < retries++) {
1018 CloseComPortPhysical();
1026 wxMilliSleep(250 * retries);
1027 CloseComPortPhysical();
1028 if (OpenComPortPhysical(m_PortName, m_baud)) {
1029 SetGatewayOperationMode();
1031 }
else if (retries < 10)
1036 for (
int i = 0; i < newdata; i++) {
1037 circle.put(rdata[i]);
1041 while (!circle.empty()) {
1042 uint8_t next_byte = circle.get();
1047 if (ESCAPE == next_byte) {
1048 *put_ptr++ = next_byte;
1049 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1050 put_ptr = rx_buffer;
1052 }
else if (ENDOFTEXT == next_byte) {
1056 auto buffer = std::make_shared<std::vector<unsigned char>>();
1057 std::vector<unsigned char>* vec = buffer.get();
1059 unsigned char* tptr;
1062 while ((tptr != put_ptr)) {
1063 vec->push_back(*tptr++);
1064 if ((tptr - rx_buffer) > DS_RX_BUFFER_SIZE) tptr = rx_buffer;
1076 Nevent.SetPayload(buffer);
1077 m_pParentDriver->AddPendingEvent(Nevent);
1078 }
else if (next_byte == STARTOFTEXT) {
1079 put_ptr = rx_buffer;
1082 put_ptr = rx_buffer;
1088 bGotESC = (next_byte == ESCAPE);
1091 *put_ptr++ = next_byte;
1092 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1093 put_ptr = rx_buffer;
1099 if (STARTOFTEXT == next_byte) {
1105 bGotESC = (next_byte == ESCAPE);
1110 *put_ptr++ = next_byte;
1111 if ((put_ptr - rx_buffer) > DS_RX_BUFFER_SIZE)
1112 put_ptr = rx_buffer;
1120 bool b_qdata = !out_que.empty();
1124 std::vector<unsigned char> qmsg = out_que.front();
1127 if (
static_cast<size_t>(-1) == WriteComPortPhysical(qmsg) &&
1132 CloseComPortPhysical();
1135 b_qdata = !out_que.empty();
1140 CloseComPortPhysical();
1141 m_pParentDriver->SetSecThreadInActive();
1142 m_pParentDriver->m_Thread_run_flag = -1;
1155#define MaxActisenseMsgBuf 400
1156#define MsgTypeN2kTX 0x94
1158void AddByteEscapedToBuf(
unsigned char byteToAdd, uint8_t& idx,
1159 unsigned char* buf,
int& byteSum);
1161std::vector<unsigned char> BufferToActisenseFormat(tN2kMsg& msg) {
1162 unsigned long _PGN = msg.PGN;
1166 unsigned char ActisenseMsgBuf[MaxActisenseMsgBuf];
1168 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1169 ActisenseMsgBuf[msgIdx++] = STARTOFTEXT;
1170 AddByteEscapedToBuf(MsgTypeN2kTX, msgIdx, ActisenseMsgBuf, byteSum);
1171 AddByteEscapedToBuf(msg.DataLen + 6, msgIdx, ActisenseMsgBuf,
1174 AddByteEscapedToBuf(msg.Priority, msgIdx, ActisenseMsgBuf, byteSum);
1175 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1177 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1179 AddByteEscapedToBuf(_PGN & 0xff, msgIdx, ActisenseMsgBuf, byteSum);
1180 AddByteEscapedToBuf(msg.Destination, msgIdx, ActisenseMsgBuf, byteSum);
1185 AddByteEscapedToBuf(msg.Source,msgIdx,ActisenseMsgBuf,byteSum);
1188 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1189 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1190 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum); _MsgTime>>=8;
1191 AddByteEscapedToBuf(_MsgTime & 0xff,msgIdx,ActisenseMsgBuf,byteSum);
1195 AddByteEscapedToBuf(msg.DataLen, msgIdx, ActisenseMsgBuf, byteSum);
1197 for (
int i = 0; i < msg.DataLen; i++)
1198 AddByteEscapedToBuf(msg.Data[i], msgIdx, ActisenseMsgBuf, byteSum);
1201 CheckSum = (uint8_t)((byteSum == 0) ? 0 : (256 - byteSum));
1202 ActisenseMsgBuf[msgIdx++] = CheckSum;
1203 if (CheckSum == ESCAPE) ActisenseMsgBuf[msgIdx++] = CheckSum;
1205 ActisenseMsgBuf[msgIdx++] = ESCAPE;
1206 ActisenseMsgBuf[msgIdx++] = ENDOFTEXT;
1208 std::vector<unsigned char> rv;
1209 for (
unsigned int i = 0; i < msgIdx; i++) rv.push_back(ActisenseMsgBuf[i]);
Interface implemented by transport layer and possible other parties like test code which should handl...
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 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.