25#if !defined(__linux__) || defined(__ANDROID__)
26#error "This file can only be compiled on Linux"
42#include <sys/socket.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->socket_can_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.socket_can_port.c_str());
213 m_stats_timer.Stop();
214 m_worker.StopThread();
217bool CommDriverN2KSocketCanImpl::SendAddressClaim(
int proposed_source_address) {
218 wxMutexLocker lock(m_TX_mutex);
220 int socket = GetWorker().GetSocket();
222 if (socket < 0)
return false;
225 memset(&frame, 0,
sizeof(frame));
227 uint64_t _pgn = 60928;
228 unsigned long canId = BuildCanID(6, proposed_source_address, 255, _pgn);
229 frame.can_id = canId | CAN_EFF_FLAG;
232 uint32_t b32_0 = node_name.value.UnicNumberAndManCode;
233 memcpy(&frame.data, &b32_0, 4);
235 unsigned char b81 = node_name.value.DeviceInstance;
236 memcpy(&frame.data[4], &b81, 1);
238 b81 = node_name.value.DeviceFunction;
239 memcpy(&frame.data[5], &b81, 1);
241 b81 = (node_name.value.DeviceClass);
242 memcpy(&frame.data[6], &b81, 1);
244 b81 = node_name.value.IndustryGroupAndSystemInstance;
245 memcpy(&frame.data[7], &b81, 1);
249 int sentbytes = write(socket, &frame,
sizeof(frame));
251 return (sentbytes == 16);
254void AddStr(std::vector<uint8_t>& vec, std::string str,
size_t max_len) {
256 for (i = 0; i < str.size(); i++) {
257 vec.push_back(str[i]);
260 for (; i < max_len; i++) {
265bool CommDriverN2KSocketCanImpl::SendProductInfo() {
267 std::vector<uint8_t> payload;
269 payload.push_back(2100 & 0xFF);
270 payload.push_back(2100 >> 8);
271 payload.push_back(0xEC);
272 payload.push_back(0x06);
274 std::string ModelID(
"OpenCPN");
275 AddStr(payload, ModelID, 32);
277 std::string ModelSWCode(PACKAGE_VERSION);
278 AddStr(payload, ModelSWCode, 32);
280 std::string ModelVersion(PACKAGE_VERSION);
281 AddStr(payload, ModelVersion, 32);
283 std::string ModelSerialCode(
284 std::to_string(m_unique_number));
285 AddStr(payload, ModelSerialCode, 32);
287 payload.push_back(0);
288 payload.push_back(0);
290 auto dest_addr = std::make_shared<const NavAddr2000>(
iface, 255);
294 auto msg = std::make_shared<const Nmea2000Msg>(_PGN, payload, dest_addr);
295 SendMessage(msg, dest_addr);
300bool CommDriverN2KSocketCanImpl::SendMessage(
301 std::shared_ptr<const NavMsg> msg, std::shared_ptr<const NavAddr> addr) {
302 if (!msg)
return false;
303 wxMutexLocker lock(m_TX_mutex);
306 if (m_source_address < 0)
return false;
308 if (m_source_address > 253)
311 int socket = GetWorker().GetSocket();
313 if (socket < 0)
return false;
316 memset(&frame, 0,
sizeof(frame));
318 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
319 std::vector<uint8_t> load = msg_n2k->payload;
321 uint64_t _pgn = msg_n2k->PGN.pgn;
322 auto destination_address = std::static_pointer_cast<const NavAddr2000>(addr);
324 unsigned long canId = BuildCanID(msg_n2k->priority, m_source_address,
325 destination_address->address, _pgn);
327 frame.can_id = canId | CAN_EFF_FLAG;
331 if (!IsFastMessagePGN(_pgn)) {
332 frame.can_dlc = load.size();
333 if (load.size() > 0) memcpy(&frame.data, load.data(), load.size());
335 sentbytes += write(socket, &frame,
sizeof(frame));
337 int sequence = (m_last_TX_sequence + 0x20) & 0xE0;
338 m_last_TX_sequence = sequence;
339 unsigned char* data_ptr = load.data();
340 int n_remaining = load.size();
344 frame.data[0] = sequence;
345 frame.data[1] = load.size();
346 int data_len_0 = wxMin(load.size(), 6);
347 memcpy(&frame.data[2], load.data(), data_len_0);
349 sentbytes += write(socket, &frame,
sizeof(frame));
351 data_ptr += data_len_0;
352 n_remaining -= data_len_0;
356 while (n_remaining > 0) {
358 frame.data[0] = sequence;
359 int data_len_n = wxMin(n_remaining, 7);
360 memcpy(&frame.data[1], data_ptr, data_len_n);
362 sentbytes += write(socket, &frame,
sizeof(frame));
364 data_ptr += data_len_n;
365 n_remaining -= data_len_n;
372 SetDriverStats(stats);
379CommDriverN2KSocketCAN::CommDriverN2KSocketCAN(
const ConnectionParams* params,
383 m_listener(listener),
384 m_stats_timer(*this, 2s),
386 m_portstring(params->GetDSPort()),
387 m_baudrate(wxString::Format(
"%i", params->baudrate)) {
388 this->attributes[
"canPort"] = params->socket_can_port.ToStdString();
389 this->attributes[
"canAddress"] = std::to_string(DEFAULT_N2K_SOURCE_ADDRESS);
390 this->attributes[
"userComment"] = params->user_comment.ToStdString();
391 this->attributes[
"ioDirection"] = std::string(
"IN/OUT");
393 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
397CommDriverN2KSocketCAN::~CommDriverN2KSocketCAN() {}
403 m_port_name(port_name.Clone()),
406 assert(m_parent_driver != 0);
409std::vector<unsigned char> Worker::PushCompleteMsg(
const CanHeader header,
411 const CanFrame frame) {
412 std::vector<unsigned char> data;
413 data.push_back(0x93);
414 data.push_back(0x13);
415 data.push_back(header.priority);
416 data.push_back(header.pgn & 0xFF);
417 data.push_back((header.pgn >> 8) & 0xFF);
418 data.push_back((header.pgn >> 16) & 0xFF);
419 data.push_back(header.destination);
420 data.push_back(header.source);
421 data.push_back(0xFF);
422 data.push_back(0xFF);
423 data.push_back(0xFF);
424 data.push_back(0xFF);
425 data.push_back(CAN_MAX_DLEN);
426 for (
size_t n = 0; n < CAN_MAX_DLEN; n++) data.push_back(frame.data[n]);
427 data.push_back(0x55);
431std::vector<unsigned char> Worker::PushFastMsgFragment(
const CanHeader& header,
433 std::vector<unsigned char> data;
434 data.push_back(0x93);
435 data.push_back(fast_messages[position].expected_length + 11);
436 data.push_back(header.priority);
437 data.push_back(header.pgn & 0xFF);
438 data.push_back((header.pgn >> 8) & 0xFF);
439 data.push_back((header.pgn >> 16) & 0xFF);
440 data.push_back(header.destination);
441 data.push_back(header.source);
442 data.push_back(0xFF);
443 data.push_back(0xFF);
444 data.push_back(0xFF);
445 data.push_back(0xFF);
446 data.push_back(fast_messages[position].expected_length);
447 for (
size_t n = 0; n < fast_messages[position].expected_length; n++)
448 data.push_back(fast_messages[position].data[n]);
449 data.push_back(0x55);
450 fast_messages.
Remove(position);
454void Worker::ThreadMessage(
const std::string& msg, wxLogLevel level) {
455 wxLogGeneric(level, wxString(msg.c_str()));
456 auto s = std::string(
"CommDriverN2KSocketCAN: ") + msg;
460void Worker::SocketMessage(
const std::string& msg,
const std::string& device) {
461 std::stringstream ss;
462 ss << msg << device <<
": " << strerror(errno);
463 ThreadMessage(ss.str());
472int Worker::InitSocket(
const std::string port_name) {
473 int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
475 SocketMessage(
"SocketCAN socket create failed: ", port_name);
480 struct ifreq if_request;
481 strcpy(if_request.ifr_name, port_name.c_str());
482 if (ioctl(sock, SIOCGIFINDEX, &if_request) < 0) {
483 SocketMessage(
"SocketCAN ioctl (SIOCGIFINDEX) failed: ", port_name);
489 struct sockaddr_can can_address;
490 can_address.can_family = AF_CAN;
491 can_address.can_ifindex = if_request.ifr_ifindex;
492 if (ioctl(sock, SIOCGIFFLAGS, &if_request) < 0) {
493 SocketMessage(
"SocketCAN socket IOCTL (SIOCGIFFLAGS) failed: ", port_name);
497 if (if_request.ifr_flags & IFF_UP) {
498 ThreadMessage(
"socketCan interface is UP");
500 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: ",
517 r = bind(sock, (
struct sockaddr*)&can_address,
sizeof(can_address));
519 SocketMessage(
"SocketCAN socket bind() failed: ", port_name);
524 stats.available =
true;
525 m_parent_driver->SetDriverStats(stats);
536void Worker::HandleInput(CanFrame frame) {
543 if (position == kNotFound) {
546 ready = fast_messages.
InsertEntry(header, frame.data, position);
549 ready = fast_messages.
AppendEntry(header, frame.data, position);
553 std::vector<unsigned char> vec;
556 vec = PushFastMsgFragment(header, position);
559 vec = PushCompleteMsg(header, position, frame);
562 auto src_addr = m_parent_driver->GetAddress(m_parent_driver->node_name);
563 auto msg = std::make_shared<const Nmea2000Msg>(header.pgn, vec, src_addr);
565 ProcessRxMessages(msg);
566 m_parent_driver->m_listener.
Notify(std::move(msg));
570 m_parent_driver->SetDriverStats(stats);
575void Worker::ProcessRxMessages(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
576 if (n2k_msg->PGN.pgn == 59904 &&
577 (n2k_msg->payload.at(6) == m_parent_driver->m_source_address ||
578 n2k_msg->payload.at(6) == 0xff)) {
579 unsigned long RequestedPGN = 0;
580 RequestedPGN = n2k_msg->payload.at(15) << 16;
581 RequestedPGN += n2k_msg->payload.at(14) << 8;
582 RequestedPGN += n2k_msg->payload.at(13);
584 switch (RequestedPGN) {
586 m_parent_driver->SendAddressClaim(m_parent_driver->m_source_address);
589 m_parent_driver->SendProductInfo();
596 else if (n2k_msg->PGN.pgn == 60928) {
598 if (n2k_msg->payload.at(7) == m_parent_driver->m_source_address) {
600 uint64_t my_name = m_parent_driver->node_name.GetName();
603 uint64_t his_name = 0;
604 unsigned char* p = (
unsigned char*)&his_name;
605 for (
unsigned int i = 0; i < 8; i++) *p++ = n2k_msg->payload.at(13 + i);
608 if (his_name < my_name) {
610 m_parent_driver->m_source_address++;
611 if (m_parent_driver->m_source_address > 253)
613 m_parent_driver->m_source_address = 254;
614 m_parent_driver->UpdateAttrCanAddress();
618 m_parent_driver->SendAddressClaim(m_parent_driver->m_source_address);
624void Worker::Entry() {
629 socket = InitSocket(m_port_name.ToStdString());
631 std::string msg(
"SocketCAN socket create failed: ");
632 ThreadMessage(msg + m_port_name.ToStdString());
639 if (m_parent_driver->SendAddressClaim(DEFAULT_N2K_SOURCE_ADDRESS)) {
640 m_parent_driver->m_source_address = DEFAULT_N2K_SOURCE_ADDRESS;
641 m_parent_driver->UpdateAttrCanAddress();
645 while (m_run_flag > 0) {
646 recvbytes = read(socket, &frame,
sizeof(frame));
647 if (recvbytes == -1) {
648 if (errno == EAGAIN || errno == EWOULDBLOCK)
continue;
650 wxLogWarning(
"can socket %s: fatal error %s", m_port_name.c_str(),
654 if (recvbytes != 16) {
655 wxLogWarning(
"can socket %s: bad frame size: %d (ignored)",
656 m_port_name.c_str(), recvbytes);
673bool Worker::StartThread() {
675 std::thread t(&Worker::Entry,
this);
680void Worker::StopThread() {
681 if (m_run_flag < 0) {
682 wxLogMessage(
"Attempt to stop already dead thread (ignored).");
685 wxLogMessage(
"Stopping Worker Thread");
689 while ((m_run_flag >= 0) && (tsec--)) wxSleep(1);
692 wxLogMessage(
"StopThread: Stopped in %d sec.", 10 - tsec);
694 wxLogWarning(
"StopThread: Not Stopped after 10 sec.");
const std::string iface
Physical device for 0183, else a unique string.
DriverStats GetDriverStats() const override
Get the Driver Statistics.
Local driver implementation, not visible outside this file.
obs::EventVar evt_driver_msg
Notified for messages from drivers.
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.
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.
Manages listening to an Observable instance.
Custom event class for OpenCPN's notification system.
Manages reading the N2K data stream provided by some N2K gateways from the declared serial port.
void Notify() override
Notify all listeners, no data supplied.
Low-level socketcan utility functions.
Low-level driver for socketcan devices (linux only).
Driver registration container, a singleton.
Raw messages layer, supports sending and recieving navmsg messages.
Global variables stored in configuration file.
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.