33static const int kNotFound = -1;
37static void AddStr(std::vector<uint8_t>* data,
const std::string& text,
40 for (; i < text.size() && i < length; i++) data->push_back(text[i]);
41 for (; i < length; i++) data->push_back(0);
46 const wxString& port_name)
47 : wxThread(wxTHREAD_JOINABLE),
49 m_portName(port_name),
50 m_stopRequested(false) {
56 m_stopEvent = CreateEventW(
nullptr, TRUE, FALSE,
nullptr);
60 m_pendingWriteEvent = CreateEventW(
nullptr, FALSE, FALSE,
nullptr);
64CanableSerialThread::~CanableSerialThread() {
66 CloseHandle(m_stopEvent);
68 if (m_pendingWriteEvent) {
69 CloseHandle(m_pendingWriteEvent);
75void CanableSerialThread::RequestStop() {
76 m_stopRequested =
true;
78 SetEvent(m_stopEvent);
83bool CanableSerialThread::OpenPort() {
84 wxString serialPortName = m_portName;
86 if (!serialPortName.StartsWith(
"\\\\.\\")) {
87 serialPortName =
"\\\\.\\" + serialPortName;
92 CreateFileW(serialPortName.wc_str(), GENERIC_READ | GENERIC_WRITE, 0,
93 nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,
nullptr);
95 if (m_serialPortHandle == INVALID_HANDLE_VALUE) {
101 dcb.DCBlength =
sizeof(dcb);
103 if (!GetCommState(m_serialPortHandle, &dcb)) {
104 CloseHandle(m_serialPortHandle);
105 m_serialPortHandle = INVALID_HANDLE_VALUE;
111 dcb.BaudRate = CBR_115200;
113 dcb.Parity = NOPARITY;
114 dcb.StopBits = ONESTOPBIT;
116 dcb.fDtrControl = DTR_CONTROL_ENABLE;
117 dcb.fRtsControl = RTS_CONTROL_ENABLE;
118 SetCommState(m_serialPortHandle, &dcb);
121 COMMTIMEOUTS timeouts = {0};
122 timeouts.ReadIntervalTimeout = 10;
123 timeouts.ReadTotalTimeoutConstant = 100;
124 timeouts.ReadTotalTimeoutMultiplier = 0;
125 timeouts.WriteTotalTimeoutConstant = 50;
126 timeouts.WriteTotalTimeoutMultiplier = 0;
127 SetCommTimeouts(m_serialPortHandle, &timeouts);
130 ZeroMemory(&m_overlappedRead,
sizeof(m_overlappedRead));
131 m_overlappedRead.hEvent = CreateEventW(
nullptr, TRUE, FALSE,
nullptr);
133 ZeroMemory(&m_overlappedWrite,
sizeof(m_overlappedWrite));
134 m_overlappedWrite.hEvent = CreateEventW(
nullptr, TRUE, FALSE,
nullptr);
137 m_isReadPending =
false;
143void CanableSerialThread::ClosePort() {
144 if (m_overlappedRead.hEvent) {
145 CloseHandle(m_overlappedRead.hEvent);
146 m_overlappedRead.hEvent =
nullptr;
148 if (m_overlappedWrite.hEvent) {
149 CloseHandle(m_overlappedWrite.hEvent);
150 m_overlappedWrite.hEvent =
nullptr;
152 if (m_serialPortHandle != INVALID_HANDLE_VALUE) {
153 CloseHandle(m_serialPortHandle);
154 m_serialPortHandle = INVALID_HANDLE_VALUE;
159bool CanableSerialThread::WriteFrame(
const std::string& payload) {
160 ResetEvent(m_overlappedWrite.hEvent);
161 DWORD bytesWritten = 0;
162 BOOL result = WriteFile(m_serialPortHandle, payload.data(),
163 static_cast<DWORD
>(payload.size()), &bytesWritten,
168 return bytesWritten == payload.size();
171 if (GetLastError() != ERROR_IO_PENDING) {
177 DWORD wait_result = WaitForSingleObject(m_overlappedWrite.hEvent, 200);
178 if (wait_result != WAIT_OBJECT_0) {
179 CancelIoEx(m_serialPortHandle, &m_overlappedWrite);
183 DWORD overlappedBytesWritten = 0;
184 if (!GetOverlappedResult(m_serialPortHandle, &m_overlappedWrite,
185 &overlappedBytesWritten, FALSE)) {
189 return overlappedBytesWritten == payload.size();
195bool CanableSerialThread::DecodeSLCANFrame(
const std::string& line,
197 if (line.size() < 10) {
203 static_cast<uint32_t
>(strtoul(line.substr(1, 8).c_str(),
nullptr, 16)) &
206 int dlc = line[9] -
'0';
209 if (dlc < 0 || dlc > 8) {
213 frame->can_dlc =
static_cast<uint8_t
>(dlc);
215 std::memset(frame->data, 0,
sizeof(frame->data));
218 for (
int i = 0; i < dlc; i++) {
219 if (index + 2 > line.size()) {
223 frame->data[i] =
static_cast<uint8_t
>(
224 strtoul(line.substr(index, 2).c_str(),
nullptr, 16));
230void CanableSerialThread::ParseReceivedData(
231 const char* buf,
size_t len, std::vector<can_frame>* out_frames) {
236 for (
size_t i = 0; i < len; i++) {
240 m_hasStartDelimiter =
true;
241 m_partialLine.clear();
246 if (c ==
'\r' || c ==
'\n') {
247 if (c ==
'\r' && m_hasStartDelimiter && !m_partialLine.empty()) {
250 if (DecodeSLCANFrame(m_partialLine, &frame)) {
251 out_frames->push_back(frame);
255 m_hasStartDelimiter =
false;
256 m_partialLine.clear();
260 if (m_hasStartDelimiter) {
263 if (m_partialLine.size() > 64) {
264 m_hasStartDelimiter =
false;
265 m_partialLine.clear();
272void CanableSerialThread::QueueWrite(
const std::string& slcan_string) {
274 std::lock_guard<std::mutex> lock(m_transmitMutex);
275 m_transmitQueue.push_back(slcan_string);
278 if (m_pendingWriteEvent) {
279 SetEvent(m_pendingWriteEvent);
284void CanableSerialThread::DequeueWrite() {
285 std::vector<std::string> slcan_strings;
287 std::lock_guard<std::mutex> lock(m_transmitMutex);
288 if (m_transmitQueue.empty()) {
291 slcan_strings.swap(m_transmitQueue);
293 for (
const auto& slcan_string : slcan_strings) {
294 WriteFrame(slcan_string);
301void* CanableSerialThread::Entry() {
303 wxLogMessage(
"CANable: failed to open serial port %s", m_portName);
308 m_parent->m_driverStatistics.available =
true;
322 m_parent->SendAddressClaim(CommDriverN2KCanable::kDefaultSourceAddress);
325 char serialBuffer[4096];
326 std::vector<can_frame> frames;
329 if (!m_isReadPending) {
330 ResetEvent(m_overlappedRead.hEvent);
333 ReadFile(m_serialPortHandle, serialBuffer,
sizeof(serialBuffer),
334 &bytesRead, &m_overlappedRead);
339 ParseReceivedData(serialBuffer, bytesRead, &frames);
340 for (
const auto& frame : frames) {
343 m_parent->EncodeActisenseMessage(frame);
348 if (m_stopRequested) {
353 if (GetLastError() != ERROR_IO_PENDING) {
358 if (m_stopRequested) {
363 m_isReadPending =
true;
368 HANDLE handles[3] = {m_overlappedRead.hEvent, m_stopEvent,
369 m_pendingWriteEvent};
370 DWORD wait_result = WaitForMultipleObjects(3, handles, FALSE, 1000);
372 if (wait_result == WAIT_OBJECT_0) {
375 DWORD overlappedBytesRead = 0;
376 if (GetOverlappedResult(m_serialPortHandle, &m_overlappedRead,
377 &overlappedBytesRead, FALSE) &&
378 overlappedBytesRead > 0) {
380 ParseReceivedData(serialBuffer, overlappedBytesRead, &frames);
381 for (
const auto& frame : frames) {
382 m_parent->EncodeActisenseMessage(frame);
385 m_isReadPending =
false;
386 }
else if (wait_result == WAIT_OBJECT_0 + 1) {
390 CancelIoEx(m_serialPortHandle, &m_overlappedRead);
392 GetOverlappedResult(m_serialPortHandle, &m_overlappedRead, &dummy, TRUE);
393 m_isReadPending =
false;
395 }
else if (wait_result == WAIT_OBJECT_0 + 2) {
398 }
else if (wait_result == WAIT_TIMEOUT) {
403 CancelIoEx(m_serialPortHandle, &m_overlappedRead);
405 GetOverlappedResult(m_serialPortHandle, &m_overlappedRead, &dummy, TRUE);
406 m_isReadPending =
false;
411 if (m_stopRequested) {
412 if (m_isReadPending) {
413 CancelIoEx(m_serialPortHandle, &m_overlappedRead);
415 GetOverlappedResult(m_serialPortHandle, &m_overlappedRead, &dummy,
417 m_isReadPending =
false;
429void CanableSerialThread::OnExit() {}
435 m_connectionParameters(*params),
436 m_driverListener(listener),
437 m_portName(params->GetStrippedDSPort()),
438 m_statsTimer(*this, std::chrono::milliseconds(2000)) {
440 m_driverStatistics.driver_bus = NavAddr::Bus::N2000;
443 this->attributes[
"canAdapter"] = std::string(
"CANable Cantact (slcan)");
444 this->attributes[
"serialPort"] = m_portName.ToStdString();
445 this->attributes[
"bitrate"] = std::string(
"250000");
446 this->attributes[
"canAddress"] = std::to_string(kDefaultSourceAddress);
453 m_fastMessageMap = std::make_unique<FastMessageMap>();
456 m_startTime = std::chrono::steady_clock::now();
462CommDriverN2KCanable::~CommDriverN2KCanable() {
467void CommDriverN2KCanable::SetN2kName() {
469 m_deviceInformation.value.Name = 0;
474 wxString hostname = wxGetHostName();
475 std::string str(hostname.mb_str());
477 for (
char ch : str) hash = hash + (hash << 5) + ch + (ch << 7);
478 m_uniqueNumber = ((hash) ^ (hash >> 16)) & 0xffff;
482 m_deviceInformation.SetManufacturerCode(2046);
483 m_deviceInformation.SetUniqueNumber(m_uniqueNumber);
484 m_deviceInformation.SetDeviceFunction(130);
485 m_deviceInformation.SetDeviceClass(120);
486 m_deviceInformation.SetIndustryGroup(4);
487 m_deviceInformation.SetSystemInstance(0);
491void CommDriverN2KCanable::UpdateAttrCanAddress() {
492 this->attributes[
"canAddress"] = std::to_string(m_sourceAddress.load());
497 return m_driverStatistics;
501void CommDriverN2KCanable::Open() {
502 m_serialThread = std::make_unique<CanableSerialThread>(
this, m_portName);
503 m_serialThread->Run();
508void CommDriverN2KCanable::Close() {
509 m_isPortOpen =
false;
510 if (m_serialThread) {
511 m_serialThread->RequestStop();
512 m_serialThread->Wait();
513 m_serialThread.reset();
518bool CommDriverN2KCanable::WriteFrame(
const std::string& payload) {
520 if (!m_serialThread) {
523 m_serialThread->QueueWrite(payload);
528bool CommDriverN2KCanable::SendAddressClaim(
int proposed_source_address) {
530 m_sourceAddress.store(proposed_source_address);
531 UpdateAttrCanAddress();
533 std::vector<uint8_t> data;
534 uint32_t unique_and_mfg = m_deviceInformation.value.UnicNumberAndManCode;
536 data.push_back(unique_and_mfg & 0xFF);
537 data.push_back((unique_and_mfg >> 8) & 0xFF);
538 data.push_back((unique_and_mfg >> 16) & 0xFF);
539 data.push_back((unique_and_mfg >> 24) & 0xFF);
540 data.push_back(m_deviceInformation.value.DeviceInstance);
541 data.push_back(m_deviceInformation.value.DeviceFunction);
542 data.push_back(m_deviceInformation.value.DeviceClass);
543 data.push_back(m_deviceInformation.value.IndustryGroupAndSystemInstance);
545 std::vector<std::string> slcan_strings = FormatCanMessage(
546 6,
static_cast<uint8_t
>(proposed_source_address), 0xFF, 60928, data);
549 for (
const auto& slcan_string : slcan_strings) {
550 result = WriteFrame(slcan_string) && result;
556bool CommDriverN2KCanable::SendProductInfo() {
557 std::vector<uint8_t> payload;
558 payload.push_back(2100 & 0xFF);
559 payload.push_back((2100 >> 8) & 0xFF);
560 payload.push_back(0xEC);
561 payload.push_back(0x06);
563 AddStr(&payload,
"OpenCPN", 32);
564 AddStr(&payload, PACKAGE_VERSION, 32);
565 AddStr(&payload, PACKAGE_VERSION, 32);
566 AddStr(&payload, std::to_string(m_uniqueNumber), 32);
568 payload.push_back(0);
569 payload.push_back(0);
571 int source = m_sourceAddress.load();
573 if (source < 0 || source > 253) {
577 std::vector<std::string> slcan_strings =
578 FormatCanMessage(6,
static_cast<uint8_t
>(source), 0xFF, 126996, payload);
580 for (
const auto& slcan_string : slcan_strings) {
581 result = WriteFrame(slcan_string) && result;
589void CommDriverN2KCanable::ProcessMessages(
590 const std::shared_ptr<const Nmea2000Msg>& msg) {
592 uint8_t sourceAddress = m_sourceAddress.load();
595 if (msg->PGN.pgn == 59904 && msg->payload.size() > 15 &&
596 (msg->payload.at(6) ==
static_cast<uint8_t
>(sourceAddress) ||
597 msg->payload.at(6) == 0xFF)) {
598 unsigned long requested_pgn = 0;
599 requested_pgn =
static_cast<unsigned long>(msg->payload.at(15)) << 16;
600 requested_pgn +=
static_cast<unsigned long>(msg->payload.at(14)) << 8;
601 requested_pgn += msg->payload.at(13);
603 switch (requested_pgn) {
605 SendAddressClaim(sourceAddress);
615 }
else if (msg->PGN.pgn == 60928 && msg->payload.size() >= 21) {
617 if (msg->payload.at(7) ==
static_cast<uint8_t
>(sourceAddress)) {
618 uint64_t myName = m_deviceInformation.GetName();
619 uint64_t otherName = 0;
620 auto* p =
reinterpret_cast<uint8_t*
>(&otherName);
621 for (
unsigned int i = 0; i < 8; i++) {
622 *p++ = msg->payload.at(13 + i);
625 if (otherName < myName) {
629 if (sourceAddress > 253) {
631 wxLogMessage(
"Canable: Could not claim a valid address");
636 SendAddressClaim(sourceAddress);
647void CommDriverN2KCanable::EncodeActisenseMessage(
const can_frame& frame) {
649 int position = kNotFound;
650 bool isFinalMessage =
true;
652 if (header.IsFastMessage()) {
653 position = m_fastMessageMap->FindMatchingEntry(header, frame.data[0]);
654 if (position == kNotFound) {
655 position = m_fastMessageMap->AddNewEntry();
657 m_fastMessageMap->InsertEntry(header, frame.data, position);
660 m_fastMessageMap->AppendEntry(header, frame.data, position);
663 if (!isFinalMessage) {
668 std::vector<uint8_t> envelope;
669 auto currentTime = std::chrono::steady_clock::now();
670 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
671 currentTime - m_startTime);
673 envelope.push_back(N2K_RX_CMD);
674 if (header.IsFastMessage()) {
677 envelope.push_back(header.priority);
678 envelope.push_back(header.pgn & 0xFF);
679 envelope.push_back((header.pgn >> 8) & 0xFF);
680 envelope.push_back((header.pgn >> 16) & 0xFF);
681 envelope.push_back(header.destination);
682 envelope.push_back(header.source);
684 envelope.push_back(elapsed.count() & 0xFF);
685 envelope.push_back((elapsed.count() >> 8) & 0xFF);
686 envelope.push_back((elapsed.count() >> 16) & 0xFF);
687 envelope.push_back((elapsed.count() >> 24) & 0xFF);
690 envelope.push_back(e.
data[n]);
694 m_fastMessageMap->Remove(position);
699 envelope.push_back(0x13);
700 envelope.push_back(header.priority);
701 envelope.push_back(header.pgn & 0xFF);
702 envelope.push_back((header.pgn >> 8) & 0xFF);
703 envelope.push_back((header.pgn >> 16) & 0xFF);
704 envelope.push_back(header.destination);
705 envelope.push_back(header.source);
707 envelope.push_back(elapsed.count() & 0xFF);
708 envelope.push_back((elapsed.count() >> 8) & 0xFF);
709 envelope.push_back((elapsed.count() >> 16) & 0xFF);
710 envelope.push_back((elapsed.count() >> 24) & 0xFF);
711 envelope.push_back(CAN_MAX_DLEN);
712 for (
size_t n = 0; n < CAN_MAX_DLEN; n++) {
713 envelope.push_back(frame.data[n]);
718 for (
auto it : envelope) {
725 checksum = 256 - checksum;
727 envelope.push_back(checksum);
731 auto address = GetAddress(m_deviceInformation);
733 std::make_shared<const Nmea2000Msg>(header.pgn, envelope, address);
737 ProcessMessages(message);
740 m_driverListener.
Notify(message);
744 m_driverStatistics.
rx_count += envelope.size();
749std::string CommDriverN2KCanable::EncodeSLCANFrame(
750 uint32_t can_id,
const std::vector<uint8_t>& data) {
751 std::ostringstream os;
752 os <<
"T" << std::hex << std::uppercase;
755 os << (can_id & 0x1FFFFFFFu);
756 os << std::dec << data.size();
757 os << std::hex << std::uppercase;
758 for (uint8_t value : data) {
761 os << static_cast<int>(value);
769std::vector<std::string> CommDriverN2KCanable::FormatCanMessage(
770 uint8_t priority, uint8_t source, uint8_t destination, uint64_t pgn,
771 const std::vector<uint8_t>& data) {
772 std::vector<std::string> slcan_strings;
775 static_cast<uint32_t
>(BuildCanID(priority, source, destination, pgn));
777 if (!IsFastMessagePGN(pgn)) {
779 slcan_strings.push_back(EncodeSLCANFrame(can_id, data));
780 return slcan_strings;
786 std::lock_guard<std::mutex> lock(m_sequenceNumberMutex);
787 m_sequenceNumber = (m_sequenceNumber + 0x20) & 0xE0;
788 sequence = m_sequenceNumber;
792 size_t offset = std::min<size_t>(6, data.size());
794 std::vector<uint8_t> frame;
795 frame.push_back(
static_cast<uint8_t
>(sequence));
796 frame.push_back(
static_cast<uint8_t
>(data.size()));
797 frame.insert(frame.end(), data.begin(), data.begin() + offset);
798 frame.resize(8, 0xFF);
799 slcan_strings.push_back(EncodeSLCANFrame(can_id, frame));
804 while (offset < data.size()) {
805 std::vector<uint8_t> frame;
806 frame.push_back(
static_cast<uint8_t
>(sequence));
807 size_t n = std::min<size_t>(7, data.size() - offset);
808 frame.insert(frame.end(), data.begin() + offset, data.begin() + offset + n);
809 frame.resize(8, 0xFF);
810 slcan_strings.push_back(EncodeSLCANFrame(can_id, frame));
815 return slcan_strings;
819bool CommDriverN2KCanable::SendMessage(std::shared_ptr<const NavMsg> msg,
820 std::shared_ptr<const NavAddr> addr) {
829 int source = m_sourceAddress.load();
831 if (source < 0 || source > 253) {
835 auto n2kMessage = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
839 auto destinationAddress = std::static_pointer_cast<const NavAddr2000>(addr);
841 uint8_t destination = destinationAddress ? destinationAddress->address : 0xFF;
845 std::vector<std::string> slcan_strings =
846 FormatCanMessage(n2kMessage->priority,
static_cast<uint8_t
>(source),
847 destination, n2kMessage->PGN.pgn, n2kMessage->payload);
852 for (
const auto& slcan_string : slcan_strings) {
853 result = WriteFrame(slcan_string) && result;
857 m_driverStatistics.
tx_count += n2kMessage->payload.size();
DriverStats GetDriverStats() const override
Get the Driver Statistics.
Connection data container close to a POD struct.
std::string GetStrippedDSPort() const
Return port string with possible windows extra data removed, in some cases empty.
Interface for handling incoming messages.
virtual void Notify(std::shared_ptr< const NavMsg > message)=0
Handle a received message.
unsigned int expected_length
total data length from first frame
std::vector< unsigned char > data
Received data.
comm_drv_n2k_canable.h NMEA2000 driver for CANable "Contact" (and other SLCAN/LAWICEL-firmware) USB-C...
std::string PortDirectionToString(PortDirection pd)
Return textual representation for use in driver ioDirection attribute.
Driver statistics report.
unsigned tx_count
Number of bytes sent since program start.
unsigned rx_count
Number of bytes received since program start.