26#if !defined(__linux__) || defined(__ANDROID__)
27#error "This file can only be compiled on Linux"
43#include <sys/socket.h>
51#include "model/comm_can_util.h"
52#include "model/comm_drv_n2k_socketcan.h"
55#include "model/config_vars.h"
57#define DEFAULT_N2K_SOURCE_ADDRESS 72
61static const int kNotFound = -1;
64static const int kSocketTimeoutSeconds = 2;
66typedef struct can_frame CanFrame;
69using namespace std::literals::chrono_literals;
101 int GetSocket() {
return m_socket; }
106 void ThreadMessage(
const std::string& msg, wxLogLevel l = wxLOG_Message);
108 int InitSocket(
const std::string port_name);
109 void SocketMessage(
const std::string& msg,
const std::string& device);
110 void HandleInput(CanFrame frame);
111 void ProcessRxMessages(std::shared_ptr<const Nmea2000Msg> n2k_msg);
113 std::vector<unsigned char> PushCompleteMsg(
const CanHeader header,
115 const CanFrame frame);
116 std::vector<unsigned char> PushFastMsgFragment(
const CanHeader& header,
120 const wxString m_port_name;
121 std::atomic<int> m_run_flag;
133 m_worker(
this, p->socketCAN_port),
134 m_source_address(-1),
135 m_last_TX_sequence(0) {
146 bool SendMessage(std::shared_ptr<const NavMsg> msg,
147 std::shared_ptr<const NavAddr> addr);
149 int DoAddressClaim();
150 bool SendAddressClaim(
int proposed_source_address);
151 bool SendProductInfo();
153 Worker& GetWorker() {
return m_worker; }
154 void UpdateAttrCanAddress();
159 int m_source_address;
160 int m_last_TX_sequence;
161 std::future<int> m_AddressClaimFuture;
166 bool HandleN2K_59904(std::shared_ptr<const Nmea2000Msg> n2k_msg);
171std::unique_ptr<CommDriverN2KSocketCAN> CommDriverN2KSocketCAN::Create(
173 return std::unique_ptr<CommDriverN2KSocketCAN>(
179void CommDriverN2KSocketCanImpl::SetN2K_Name() {
181 node_name.value.Name = 0;
186 std::string str(g_hostname.mb_str());
187 int len = str.size();
188 const char* ch = str.data();
189 for (
int i = 0; i < len; i++)
190 hash = hash + ((hash) << 5) + *(ch + i) + ((*(ch + i)) << 7);
191 m_unique_number = ((hash) ^ (hash >> 16)) & 0xffff;
193 node_name.SetManufacturerCode(2046);
194 node_name.SetUniqueNumber(m_unique_number);
195 node_name.SetDeviceFunction(130);
196 node_name.SetDeviceClass(120);
197 node_name.SetIndustryGroup(4);
198 node_name.SetSystemInstance(0);
201void CommDriverN2KSocketCanImpl::UpdateAttrCanAddress() {
202 this->attributes[
"canAddress"] = std::to_string(m_source_address);
205bool CommDriverN2KSocketCanImpl::Open() {
207 bool bws = m_worker.StartThread();
211void CommDriverN2KSocketCanImpl::Close() {
212 wxLogMessage(
"Closing N2K socketCAN: %s", m_params.socketCAN_port.c_str());
213 m_worker.StopThread();
216 auto& registry = CommDriverRegistry::GetInstance();
218 registry.Deactivate(me);
221bool CommDriverN2KSocketCanImpl::SendAddressClaim(
int proposed_source_address) {
222 wxMutexLocker lock(m_TX_mutex);
224 int socket = GetWorker().GetSocket();
226 if (socket < 0)
return false;
229 memset(&frame, 0,
sizeof(frame));
231 uint64_t _pgn = 60928;
232 unsigned long canId = BuildCanID(6, proposed_source_address, 255, _pgn);
233 frame.can_id = canId | CAN_EFF_FLAG;
236 uint32_t b32_0 = node_name.value.UnicNumberAndManCode;
237 memcpy(&frame.data, &b32_0, 4);
239 unsigned char b81 = node_name.value.DeviceInstance;
240 memcpy(&frame.data[4], &b81, 1);
242 b81 = node_name.value.DeviceFunction;
243 memcpy(&frame.data[5], &b81, 1);
245 b81 = (node_name.value.DeviceClass);
246 memcpy(&frame.data[6], &b81, 1);
248 b81 = node_name.value.IndustryGroupAndSystemInstance;
249 memcpy(&frame.data[7], &b81, 1);
253 int sentbytes = write(socket, &frame,
sizeof(frame));
255 return (sentbytes == 16);
258void AddStr(std::vector<uint8_t>& vec, std::string str,
size_t max_len) {
260 for (i = 0; i < str.size(); i++) {
261 vec.push_back(str[i]);
264 for (; i < max_len; i++) {
269bool CommDriverN2KSocketCanImpl::SendProductInfo() {
271 std::vector<uint8_t> payload;
273 payload.push_back(2100 & 0xFF);
274 payload.push_back(2100 >> 8);
275 payload.push_back(0xEC);
276 payload.push_back(0x06);
278 std::string ModelID(
"OpenCPN");
279 AddStr(payload, ModelID, 32);
281 std::string ModelSWCode(PACKAGE_VERSION);
282 AddStr(payload, ModelSWCode, 32);
284 std::string ModelVersion(PACKAGE_VERSION);
285 AddStr(payload, ModelVersion, 32);
287 std::string ModelSerialCode(
288 std::to_string(m_unique_number));
289 AddStr(payload, ModelSerialCode, 32);
291 payload.push_back(0);
292 payload.push_back(0);
294 auto dest_addr = std::make_shared<const NavAddr2000>(
iface, 255);
298 auto msg = std::make_shared<const Nmea2000Msg>(_PGN, payload, dest_addr);
299 SendMessage(msg, dest_addr);
304bool CommDriverN2KSocketCanImpl::SendMessage(
305 std::shared_ptr<const NavMsg> msg, std::shared_ptr<const NavAddr> addr) {
306 wxMutexLocker lock(m_TX_mutex);
309 if (m_source_address < 0)
return false;
311 if (m_source_address > 253)
314 int socket = GetWorker().GetSocket();
316 if (socket < 0)
return false;
319 memset(&frame, 0,
sizeof(frame));
321 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
322 std::vector<uint8_t> load = msg_n2k->payload;
324 uint64_t _pgn = msg_n2k->PGN.pgn;
325 auto destination_address = std::static_pointer_cast<const NavAddr2000>(addr);
327 unsigned long canId = BuildCanID(msg_n2k->priority, m_source_address,
328 destination_address->address, _pgn);
330 frame.can_id = canId | CAN_EFF_FLAG;
334 if (load.size() <= 8) {
335 frame.can_dlc = load.size();
336 if (load.size() > 0) memcpy(&frame.data, load.data(), load.size());
338 sentbytes += write(socket, &frame,
sizeof(frame));
340 int sequence = (m_last_TX_sequence + 0x20) & 0xE0;
341 m_last_TX_sequence = sequence;
342 unsigned char* data_ptr = load.data();
343 int n_remaining = load.size();
347 frame.data[0] = sequence;
348 frame.data[1] = load.size();
349 int data_len_0 = wxMin(load.size(), 6);
350 memcpy(&frame.data[2], load.data(), data_len_0);
352 sentbytes += write(socket, &frame,
sizeof(frame));
354 data_ptr += data_len_0;
355 n_remaining -= data_len_0;
359 while (n_remaining > 0) {
361 frame.data[0] = sequence;
362 int data_len_n = wxMin(n_remaining, 7);
363 memcpy(&frame.data[1], data_ptr, data_len_n);
365 sentbytes += write(socket, &frame,
sizeof(frame));
367 data_ptr += data_len_n;
368 n_remaining -= data_len_n;
375 SetDriverStats(stats);
382CommDriverN2KSocketCAN::CommDriverN2KSocketCAN(
const ConnectionParams* params,
386 m_listener(listener),
388 m_portstring(params->GetDSPort()),
389 m_baudrate(wxString::Format(
"%i", params->Baudrate)),
390 m_stats_timer(*this, 2s) {
391 this->attributes[
"canPort"] = params->socketCAN_port.ToStdString();
392 this->attributes[
"canAddress"] = std::to_string(DEFAULT_N2K_SOURCE_ADDRESS);
393 this->attributes[
"userComment"] = params->UserComment.ToStdString();
394 this->attributes[
"ioDirection"] = std::string(
"IN/OUT");
396 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
397 m_driver_stats.driver_iface = params->GetStrippedDSPort();
400CommDriverN2KSocketCAN::~CommDriverN2KSocketCAN() {}
406 m_port_name(port_name.Clone()),
409 assert(m_parent_driver != 0);
412std::vector<unsigned char> Worker::PushCompleteMsg(
const CanHeader header,
414 const CanFrame frame) {
415 std::vector<unsigned char> data;
416 data.push_back(0x93);
417 data.push_back(0x13);
418 data.push_back(header.priority);
419 data.push_back(header.pgn & 0xFF);
420 data.push_back((header.pgn >> 8) & 0xFF);
421 data.push_back((header.pgn >> 16) & 0xFF);
422 data.push_back(header.destination);
423 data.push_back(header.source);
424 data.push_back(0xFF);
425 data.push_back(0xFF);
426 data.push_back(0xFF);
427 data.push_back(0xFF);
428 data.push_back(CAN_MAX_DLEN);
429 for (
size_t n = 0; n < CAN_MAX_DLEN; n++) data.push_back(frame.data[n]);
430 data.push_back(0x55);
434std::vector<unsigned char> Worker::PushFastMsgFragment(
const CanHeader& header,
436 std::vector<unsigned char> data;
437 data.push_back(0x93);
438 data.push_back(fast_messages[position].expected_length + 11);
439 data.push_back(header.priority);
440 data.push_back(header.pgn & 0xFF);
441 data.push_back((header.pgn >> 8) & 0xFF);
442 data.push_back((header.pgn >> 16) & 0xFF);
443 data.push_back(header.destination);
444 data.push_back(header.source);
445 data.push_back(0xFF);
446 data.push_back(0xFF);
447 data.push_back(0xFF);
448 data.push_back(0xFF);
449 data.push_back(fast_messages[position].expected_length);
450 for (
size_t n = 0; n < fast_messages[position].expected_length; n++)
451 data.push_back(fast_messages[position].data[n]);
452 data.push_back(0x55);
453 fast_messages.
Remove(position);
457void Worker::ThreadMessage(
const std::string& msg, wxLogLevel level) {
458 wxLogGeneric(level, wxString(msg.c_str()));
459 auto s = std::string(
"CommDriverN2KSocketCAN: ") + msg;
463void Worker::SocketMessage(
const std::string& msg,
const std::string& device) {
464 std::stringstream ss;
465 ss << msg << device <<
": " << strerror(errno);
466 ThreadMessage(ss.str());
475int Worker::InitSocket(
const std::string port_name) {
476 int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
478 SocketMessage(
"SocketCAN socket create failed: ", port_name);
483 struct ifreq if_request;
484 strcpy(if_request.ifr_name, port_name.c_str());
485 if (ioctl(sock, SIOCGIFINDEX, &if_request) < 0) {
486 SocketMessage(
"SocketCAN ioctl (SIOCGIFINDEX) failed: ", port_name);
491 struct sockaddr_can can_address;
492 can_address.can_family = AF_CAN;
493 can_address.can_ifindex = if_request.ifr_ifindex;
494 if (ioctl(sock, SIOCGIFFLAGS, &if_request) < 0) {
495 SocketMessage(
"SocketCAN socket IOCTL (SIOCGIFFLAGS) failed: ", port_name);
498 if (if_request.ifr_flags & IFF_UP) {
499 ThreadMessage(
"socketCan interface is UP");
501 ThreadMessage(
"socketCan interface is NOT UP");
507 tv.tv_sec = kSocketTimeoutSeconds;
510 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (
const char*)&tv,
sizeof tv);
512 SocketMessage(
"SocketCAN setsockopt SO_RCVTIMEO failed on device: ",
516 r = bind(sock, (
struct sockaddr*)&can_address,
sizeof(can_address));
518 SocketMessage(
"SocketCAN socket bind() failed: ", port_name);
521 DriverStats stats = m_parent_driver->GetDriverStats();
522 stats.available =
true;
523 m_parent_driver->SetDriverStats(stats);
534void Worker::HandleInput(CanFrame frame) {
541 if (position == kNotFound) {
544 ready = fast_messages.
InsertEntry(header, frame.data, position);
547 ready = fast_messages.
AppendEntry(header, frame.data, position);
551 std::vector<unsigned char> vec;
554 vec = PushFastMsgFragment(header, position);
557 vec = PushCompleteMsg(header, position, frame);
560 auto src_addr = m_parent_driver->GetAddress(m_parent_driver->node_name);
561 auto msg = std::make_shared<const Nmea2000Msg>(header.pgn, vec, src_addr);
562 auto msg_all = std::make_shared<const Nmea2000Msg>(1, vec, src_addr);
564 ProcessRxMessages(msg);
565 m_parent_driver->m_listener.
Notify(std::move(msg));
566 m_parent_driver->m_listener.
Notify(std::move(msg_all));
568 DriverStats stats = m_parent_driver->GetDriverStats();
570 m_parent_driver->SetDriverStats(stats);
575void Worker::ProcessRxMessages(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
576 if (n2k_msg->PGN.pgn == 59904) {
577 unsigned long RequestedPGN = 0;
578 RequestedPGN = n2k_msg->payload.at(15) << 16;
579 RequestedPGN += n2k_msg->payload.at(14) << 8;
580 RequestedPGN += n2k_msg->payload.at(13);
582 switch (RequestedPGN) {
584 m_parent_driver->SendAddressClaim(m_parent_driver->m_source_address);
587 m_parent_driver->SendProductInfo();
594 else if (n2k_msg->PGN.pgn == 60928) {
596 if (n2k_msg->payload.at(7) == m_parent_driver->m_source_address) {
598 uint64_t my_name = m_parent_driver->node_name.GetName();
601 uint64_t his_name = 0;
602 unsigned char* p = (
unsigned char*)&his_name;
603 for (
unsigned int i = 0; i < 8; i++) *p++ = n2k_msg->payload.at(13 + i);
606 if (his_name < my_name) {
608 m_parent_driver->m_source_address++;
609 if (m_parent_driver->m_source_address > 253)
611 m_parent_driver->m_source_address = 254;
612 m_parent_driver->UpdateAttrCanAddress();
616 m_parent_driver->SendAddressClaim(m_parent_driver->m_source_address);
622void Worker::Entry() {
627 socket = InitSocket(m_port_name.ToStdString());
629 std::string msg(
"SocketCAN socket create failed: ");
630 ThreadMessage(msg + m_port_name.ToStdString());
637 if (m_parent_driver->SendAddressClaim(DEFAULT_N2K_SOURCE_ADDRESS)) {
638 m_parent_driver->m_source_address = DEFAULT_N2K_SOURCE_ADDRESS;
639 m_parent_driver->UpdateAttrCanAddress();
643 while (m_run_flag > 0) {
644 recvbytes = read(socket, &frame,
sizeof(frame));
645 if (recvbytes == -1) {
646 if (errno == EAGAIN || errno == EWOULDBLOCK)
continue;
648 wxLogWarning(
"can socket %s: fatal error %s", m_port_name.c_str(),
652 if (recvbytes != 16) {
653 wxLogWarning(
"can socket %s: bad frame size: %d (ignored)",
654 m_port_name.c_str(), recvbytes);
664bool Worker::StartThread() {
666 std::thread t(&Worker::Entry,
this);
671void Worker::StopThread() {
672 if (m_run_flag < 0) {
673 wxLogMessage(
"Attempt to stop already dead thread (ignored).");
676 wxLogMessage(
"Stopping Worker Thread");
680 while ((m_run_flag >= 0) && (tsec--)) wxSleep(1);
683 wxLogMessage(
"StopThread: Stopped in %d sec.", 10 - tsec);
685 wxLogWarning(
"StopThread: Not Stopped after 10 sec.");
const std::string iface
Physical device for 0183, else a unique string.
Local driver implementation, not visible outside this file.
EventVar evt_driver_msg
Notified for messages from drivers.
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.
const void Notify()
Notify all listeners, no data supplied.
Track fast message fragments eventually forming complete messages.
int AddNewEntry(void)
Allocate a new, fresh entry and return index to it.
void Remove(int pos)
Remove entry at pos.
bool AppendEntry(const CanHeader hdr, const unsigned char *data, int index)
Append fragment to existing multipart message.
int FindMatchingEntry(const CanHeader header, const unsigned char sid)
Setter.
bool InsertEntry(const CanHeader header, const unsigned char *data, int index)
Insert a new entry, first part of a multipart message.
Keeps listening over it's lifespan, removes itself on destruction.
Custom event class for OpenCPN's notification system.
Manages reading the N2K data stream provided by some N2K gateways from the declared serial port.
DriverPtr & FindDriver(const std::vector< DriverPtr > &drivers, const std::string &iface, const NavAddr::Bus _bus)
Search list of drivers for a driver with given interface string.
Driver registration container, a singleton.
Raw messages layer, supports sending and recieving navmsg messages.
Driver statistics report.
unsigned tx_count
Number of bytes sent since program start.
unsigned rx_count
Number of bytes received since program start.
N2k uses CAN which defines the basic properties of messages.