26 #if !defined(__linux__) || defined(__ANDROID__)
27 #error "This file can only be compiled on Linux"
41 #include <serial/serial.h>
42 #include <sys/ioctl.h>
43 #include <sys/socket.h>
47 #include <wx/string.h>
49 #include <wx/thread.h>
51 #include "model/comm_can_util.h"
52 #include "model/comm_drv_n2k_socketcan.h"
53 #include "model/comm_drv_registry.h"
54 #include "model/comm_navmsg_bus.h"
55 #include "model/config_vars.h"
57 #define DEFAULT_N2K_SOURCE_ADDRESS 72
61 static const int kNotFound = -1;
64 static const int kSocketTimeoutSeconds = 2;
66 typedef struct can_frame CanFrame;
100 int GetSocket(){
return m_socket;}
105 void ThreadMessage(
const std::string& msg, wxLogLevel l = wxLOG_Message);
107 int InitSocket(
const std::string port_name);
108 void SocketMessage(
const std::string& msg,
const std::string& device);
109 void HandleInput(CanFrame frame);
110 void ProcessRxMessages(std::shared_ptr<const Nmea2000Msg> n2k_msg);
112 std::vector<unsigned char> PushCompleteMsg(
const CanHeader header,
114 const CanFrame frame);
115 std::vector<unsigned char> PushFastMsgFragment(
const CanHeader& header,
119 const wxString m_port_name;
120 std::atomic<int> m_run_flag;
132 m_source_address(-1), m_last_TX_sequence(0){
143 bool SendMessage(std::shared_ptr<const NavMsg> msg,
144 std::shared_ptr<const NavAddr> addr);
146 int DoAddressClaim();
147 bool SendAddressClaim(
int proposed_source_address);
148 bool SendProductInfo();
150 Worker& GetWorker(){
return m_worker; }
151 void UpdateAttrCanAddress();
156 int m_source_address;
157 int m_last_TX_sequence;
158 std::future<int> m_AddressClaimFuture;
163 bool HandleN2K_59904( std::shared_ptr<const Nmea2000Msg> n2k_msg );
170 std::shared_ptr<CommDriverN2KSocketCAN> CommDriverN2KSocketCAN::Create(
172 return std::shared_ptr<CommDriverN2KSocketCAN>(
180 void CommDriverN2KSocketCanImpl::SetN2K_Name() {
182 node_name.value.Name = 0;
187 std::string str(g_hostname.mb_str());
188 int len = str.size();
189 const char* ch = str.data();
190 for (
int i = 0; i < len; i++)
191 hash = hash + ((hash) << 5) + *(ch + i) + ((*(ch + i)) << 7);
192 m_unique_number = ((hash) ^ (hash >> 16)) & 0xffff;
194 node_name.SetManufacturerCode(2046);
195 node_name.SetUniqueNumber(m_unique_number);
196 node_name.SetDeviceFunction(130);
197 node_name.SetDeviceClass(120);
198 node_name.SetIndustryGroup(4);
199 node_name.SetSystemInstance(0);
202 void CommDriverN2KSocketCanImpl::UpdateAttrCanAddress() {
203 this->attributes[
"canAddress"] = std::to_string(m_source_address);
207 bool CommDriverN2KSocketCanImpl::Open() {
210 bool bws = m_worker.StartThread();
215 void CommDriverN2KSocketCanImpl::Close() {
216 wxLogMessage(
"Closing N2K socketCAN: %s", m_params.socketCAN_port.c_str());
217 m_worker.StopThread();
220 auto& registry = CommDriverRegistry::GetInstance();
221 auto me = FindDriver(registry.GetDrivers(),
iface, bus);
222 registry.Deactivate(me);
226 bool CommDriverN2KSocketCanImpl::SendAddressClaim(
int proposed_source_address) {
228 wxMutexLocker lock(m_TX_mutex);
230 int socket = GetWorker().GetSocket();
236 memset(&frame, 0,
sizeof(frame));
238 uint64_t _pgn = 60928;
239 unsigned long canId = BuildCanID(6, proposed_source_address, 255, _pgn);
240 frame.can_id = canId | CAN_EFF_FLAG;
243 uint32_t b32_0 = node_name.value.UnicNumberAndManCode;
244 memcpy(&frame.data, &b32_0, 4);
246 unsigned char b81 = node_name.value.DeviceInstance;
247 memcpy(&frame.data[4], &b81, 1);
249 b81 = node_name.value.DeviceFunction;
250 memcpy(&frame.data[5], &b81, 1);
252 b81 = (node_name.value.DeviceClass);
253 memcpy(&frame.data[6], &b81, 1);
255 b81 = node_name.value.IndustryGroupAndSystemInstance;
256 memcpy(&frame.data[7], &b81, 1);
260 int sentbytes = write(socket, &frame,
sizeof(frame));
262 return (sentbytes == 16);
265 void AddStr( std::vector<uint8_t> &vec, std::string str,
size_t max_len) {
267 for (i = 0; i<str.size(); i++) {
268 vec.push_back(str[i]);;
270 for (; i<max_len; i++) {
275 bool CommDriverN2KSocketCanImpl::SendProductInfo() {
278 std::vector<uint8_t> payload;
280 payload.push_back(2100 & 0xFF);
281 payload.push_back(2100 >> 8);
282 payload.push_back(0xEC);
283 payload.push_back(0x06);
285 std::string ModelID(
"OpenCPN");
286 AddStr(payload, ModelID, 32);
288 std::string ModelSWCode(PACKAGE_VERSION);
289 AddStr(payload, ModelSWCode, 32);
291 std::string ModelVersion(PACKAGE_VERSION);
292 AddStr(payload, ModelVersion, 32);
294 std::string ModelSerialCode(std::to_string(m_unique_number));
295 AddStr(payload, ModelSerialCode, 32);
297 payload.push_back(0);
298 payload.push_back(0);
300 auto dest_addr = std::make_shared<const NavAddr2000>(
iface, 255);
304 auto msg = std::make_shared<const Nmea2000Msg>(_PGN, payload, dest_addr);
305 SendMessage(msg, dest_addr);
310 bool CommDriverN2KSocketCanImpl::SendMessage(std::shared_ptr<const NavMsg> msg,
311 std::shared_ptr<const NavAddr> addr) {
313 wxMutexLocker lock(m_TX_mutex);
316 if ( m_source_address < 0)
319 if ( m_source_address > 253)
322 int socket = GetWorker().GetSocket();
328 memset(&frame, 0,
sizeof(frame));
330 auto msg_n2k = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
331 std::vector<uint8_t> load = msg_n2k->payload;
333 uint64_t _pgn = msg_n2k->PGN.pgn;
334 auto destination_address = std::static_pointer_cast<const NavAddr2000>(addr);
337 unsigned long canId = BuildCanID(msg_n2k->priority, m_source_address,
338 destination_address->address, _pgn);
340 frame.can_id = canId | CAN_EFF_FLAG;
342 if (load.size() <= 8){
343 frame.can_dlc = load.size();
345 memcpy(&frame.data, load.data(), load.size());
347 int sentbytes = write(socket, &frame,
sizeof(frame));
350 int sequence = (m_last_TX_sequence + 0x20) & 0xE0;
351 m_last_TX_sequence = sequence;
352 unsigned char *data_ptr = load.data();
353 int n_remaining = load.size();
357 frame.data[0] = sequence;
358 frame.data[1] = load.size();
359 int data_len_0 = wxMin(load.size(), 6);
360 memcpy(&frame.data[2], load.data(), data_len_0);
362 int sentbytes0 = write(socket, &frame,
sizeof(frame));
364 data_ptr += data_len_0;
365 n_remaining -= data_len_0;
369 while (n_remaining > 0){
371 frame.data[0] = sequence;
372 int data_len_n = wxMin(n_remaining, 7);
373 memcpy(&frame.data[1], data_ptr, data_len_n);
375 int sentbytesn = write(socket, &frame,
sizeof(frame));
377 data_ptr += data_len_n;
378 n_remaining -= data_len_n;
391 CommDriverN2KSocketCAN::CommDriverN2KSocketCAN(
const ConnectionParams* params,
395 m_listener(listener),
397 m_portstring(params->GetDSPort()),
398 m_baudrate(wxString::Format(
"%i", params->Baudrate))
400 this->attributes[
"canPort"] = params->socketCAN_port.ToStdString();
401 this->attributes[
"canAddress"] = std::to_string(DEFAULT_N2K_SOURCE_ADDRESS);
402 this->attributes[
"userComment"] = params->UserComment.ToStdString();
403 this->attributes[
"ioDirection"] = std::string(
"IN/OUT");
407 CommDriverN2KSocketCAN::~CommDriverN2KSocketCAN() {}
410 CommDriverRegistry::GetInstance().
Activate(shared_from_this());
418 m_port_name(port_name.Clone()),
421 assert(m_parent_driver != 0);
424 std::vector<unsigned char> Worker::PushCompleteMsg(
const CanHeader header,
426 const CanFrame frame) {
427 std::vector<unsigned char> data;
428 data.push_back(0x93);
429 data.push_back(0x13);
430 data.push_back(header.priority);
431 data.push_back(header.pgn & 0xFF);
432 data.push_back((header.pgn >> 8) & 0xFF);
433 data.push_back((header.pgn >> 16) & 0xFF);
434 data.push_back(header.destination);
435 data.push_back(header.source);
436 data.push_back(0xFF);
437 data.push_back(0xFF);
438 data.push_back(0xFF);
439 data.push_back(0xFF);
440 data.push_back(CAN_MAX_DLEN);
441 for (
size_t n = 0; n < CAN_MAX_DLEN; n++) data.push_back(frame.data[n]);
442 data.push_back(0x55);
446 std::vector<unsigned char> Worker::PushFastMsgFragment(
const CanHeader& header,
448 std::vector<unsigned char> data;
449 data.push_back(0x93);
450 data.push_back(fast_messages[position].expected_length + 11);
451 data.push_back(header.priority);
452 data.push_back(header.pgn & 0xFF);
453 data.push_back((header.pgn >> 8) & 0xFF);
454 data.push_back((header.pgn >> 16) & 0xFF);
455 data.push_back(header.destination);
456 data.push_back(header.source);
457 data.push_back(0xFF);
458 data.push_back(0xFF);
459 data.push_back(0xFF);
460 data.push_back(0xFF);
461 data.push_back(fast_messages[position].expected_length);
462 for (
size_t n = 0; n < fast_messages[position].expected_length; n++)
463 data.push_back(fast_messages[position].data[n]);
464 data.push_back(0x55);
465 fast_messages.
Remove(position);
469 void Worker::ThreadMessage(
const std::string& msg, wxLogLevel level) {
470 wxLogGeneric(level, wxString(msg.c_str()));
471 auto s = std::string(
"CommDriverN2KSocketCAN: ") + msg;
475 void Worker::SocketMessage(
const std::string& msg,
const std::string& device) {
476 std::stringstream ss;
477 ss << msg << device <<
": " << strerror(errno);
478 ThreadMessage(ss.str());
487 int Worker::InitSocket(
const std::string port_name) {
488 int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
490 SocketMessage(
"SocketCAN socket create failed: ", port_name);
495 struct ifreq if_request;
496 strcpy(if_request.ifr_name, port_name.c_str());
497 if (ioctl(sock, SIOCGIFINDEX, &if_request) < 0) {
498 SocketMessage(
"SocketCAN ioctl (SIOCGIFINDEX) failed: ", port_name);
503 struct sockaddr_can can_address;
504 can_address.can_family = AF_CAN;
505 can_address.can_ifindex = if_request.ifr_ifindex;
506 if (ioctl(sock, SIOCGIFFLAGS, &if_request) < 0) {
507 SocketMessage(
"SocketCAN socket IOCTL (SIOCGIFFLAGS) failed: ", port_name);
510 if (if_request.ifr_flags & IFF_UP) {
511 ThreadMessage(
"socketCan interface is UP");
513 ThreadMessage(
"socketCan interface is NOT UP");
519 tv.tv_sec = kSocketTimeoutSeconds;
522 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (
const char*)&tv,
sizeof tv);
524 SocketMessage(
"SocketCAN setsockopt SO_RCVTIMEO failed on device: ",
528 r = bind(sock, (
struct sockaddr*)&can_address,
sizeof(can_address));
530 SocketMessage(
"SocketCAN socket bind() failed: ", port_name);
542 void Worker::HandleInput(CanFrame frame) {
549 if (position == kNotFound) {
552 ready = fast_messages.
InsertEntry(header, frame.data, position);
555 ready = fast_messages.
AppendEntry(header, frame.data, position);
559 std::vector<unsigned char> vec;
562 vec = PushFastMsgFragment(header, position);
565 vec = PushCompleteMsg(header, position, frame);
568 auto src_addr = m_parent_driver->GetAddress(m_parent_driver->node_name);
569 auto msg = std::make_shared<const Nmea2000Msg>(header.pgn, vec, src_addr);
570 auto msg_all = std::make_shared<const Nmea2000Msg>(1, vec, src_addr);
572 ProcessRxMessages(msg);
573 m_parent_driver->m_listener.
Notify(std::move(msg));
574 m_parent_driver->m_listener.
Notify(std::move(msg_all));
580 void Worker::ProcessRxMessages(std::shared_ptr<const Nmea2000Msg> n2k_msg){
582 if(n2k_msg->PGN.pgn == 59904 ){
583 unsigned long RequestedPGN = 0;
584 RequestedPGN = n2k_msg->payload.at(15) << 16;
585 RequestedPGN += n2k_msg->payload.at(14) << 8;
586 RequestedPGN += n2k_msg->payload.at(13);
588 switch (RequestedPGN){
590 m_parent_driver->SendAddressClaim(m_parent_driver->m_source_address);
593 m_parent_driver->SendProductInfo();
600 else if(n2k_msg->PGN.pgn == 60928 ){
602 if (n2k_msg->payload.at(7) == m_parent_driver->m_source_address){
604 uint64_t my_name = m_parent_driver->node_name.GetName();
607 uint64_t his_name = 0;
608 unsigned char *p = (
unsigned char *)&his_name;
609 for (
unsigned int i=0 ; i < 8 ; i++)
610 *p++ = n2k_msg->payload.at(13 + i);
613 if (his_name < my_name){
615 m_parent_driver->m_source_address++;
616 if ( m_parent_driver->m_source_address > 253)
618 m_parent_driver->m_source_address = 254;
619 m_parent_driver->UpdateAttrCanAddress();
623 m_parent_driver->SendAddressClaim(m_parent_driver->m_source_address);
630 void Worker::Entry() {
635 socket = InitSocket(m_port_name.ToStdString());
637 std::string msg(
"SocketCAN socket create failed: ");
638 ThreadMessage(msg + m_port_name.ToStdString());
645 if (m_parent_driver->SendAddressClaim(DEFAULT_N2K_SOURCE_ADDRESS)){
646 m_parent_driver->m_source_address = DEFAULT_N2K_SOURCE_ADDRESS;
647 m_parent_driver->UpdateAttrCanAddress();
652 while (m_run_flag > 0) {
653 recvbytes = read(socket, &frame,
sizeof(frame));
654 if (recvbytes == -1) {
655 if (errno == EAGAIN || errno == EWOULDBLOCK)
continue;
657 wxLogWarning(
"can socket %s: fatal error %s", m_port_name.c_str(),
661 if (recvbytes != 16) {
662 wxLogWarning(
"can socket %s: bad frame size: %d (ignored)",
663 m_port_name.c_str(), recvbytes);
673 bool Worker::StartThread() {
675 std::thread t(&Worker::Entry,
this);
680 void 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.
void Activate() override
Register driver and possibly do other post-ctor steps.
Local driver implementation, not visible outside this file.
EventVar evt_driver_msg
Notified for messages from drivers.
void Activate(DriverPtr driver)
Add driver to list of active 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.
Adds a std::shared<void> element to wxCommandEvent.
Manages reading the N2K data stream provided by some N2K gateways from the declared serial port.
wxDEFINE_EVENT(REST_IO_EVT, ObservedEvt)
Event from IO thread to main.
N2k uses CAN which defines the basic properties of messages.