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->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;
165 ObservableListener listener_N2K_59904;
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_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->socketCAN_port.ToStdString();
389 this->attributes[
"canAddress"] = std::to_string(DEFAULT_N2K_SOURCE_ADDRESS);
390 this->attributes[
"userComment"] = params->UserComment.ToStdString();
391 this->attributes[
"ioDirection"] = std::string(
"IN/OUT");
393 m_driver_stats.driver_bus = NavAddr::Bus::N2000;
394 m_driver_stats.driver_iface = params->GetStrippedDSPort();
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;
457 CommDriverRegistry::GetInstance().
evt_driver_msg.Notify(level, s);
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 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);
671bool Worker::StartThread() {
673 std::thread t(&Worker::Entry,
this);
678void Worker::StopThread() {
679 if (m_run_flag < 0) {
680 wxLogMessage(
"Attempt to stop already dead thread (ignored).");
683 wxLogMessage(
"Stopping Worker Thread");
687 while ((m_run_flag >= 0) && (tsec--)) wxSleep(1);
690 wxLogMessage(
"StopThread: Stopped in %d sec.", 10 - tsec);
692 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.
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.
Custom event class for OpenCPN's notification system.
Manages reading the N2K data stream provided by some N2K gateways from the declared serial port.
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.