OpenCPN Partial API docs
Loading...
Searching...
No Matches
comm_drv_n2k_canable.h
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2026 by twocanplugin@hotmail.com *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
27#ifndef _COMM_DRV_N2K_CANABLE_H
28#define _COMM_DRV_N2K_CANABLE_H
29
30// Actisense NGT-1 definitions
31// ASCII Control Characters
32#define DLE 0x10
33#define STX 0x02
34#define ETX 0x03
35#define ESC 0x1B
36
37// Actisense commands
38#define N2K_TX_CMD 0x92
39#define N2K_RX_CMD 0x93
40#define NGT_TX_CMD 0xA1
41#define NGT_RX_CMD 0xA3
42
43// Yacht Devices commands
44// Go figure....
45#define YD_TX_CMD 0x94
46
47#include <atomic>
48#include <cstdint>
49#include <memory>
50#include <mutex>
51#include <string>
52#include <vector>
53#include <chrono>
54
55#include <wx/wx.h>
56#include <wx/thread.h>
57
58#include <windows.h>
59
60#include "model/comm_driver.h"
61#include "model/comm_drv_n2k.h"
63#include "model/conn_params.h"
65#include "model/comm_can_util.h"
66#include "N2kMsg.h"
67
69
70// Note to self.
71// Worker thread perform the actual serial reads, SLCAN line parsing,
72// fast-packet reassembly, address-claim/product-info requests and delivery to
73// DriverListener::Notify() similar to Worker::HandleInput() in
74// comm_drv_n2k_socketcan.cpp
75// Outgoing SLCAN commands are queued from any thread, either this one,
76// for address-claim and product information frames, or the GUI thread
77// or ordinary SendMessage() traffic.
78
79class CanableSerialThread : public wxThread {
80public:
81 CanableSerialThread(CommDriverN2KCanable* parent, const wxString& port_name);
82 ~CanableSerialThread() override;
83
84 void* Entry() override;
85 void OnExit() override;
86
87 // Signals the worker thread to stop, and wakes it immediately even if
88 // it's currently blocked waiting on a pending overlapped read
89 void RequestStop();
90
91 // Thread-safe queue of fully-formed SLCAN commands for transmission
92 // Wakes the worker thread immediately via m_write_pending_event
93 void QueueWrite(const std::string& command);
94
95private:
96 bool OpenPort();
97 void ClosePort();
98 void ParseReceivedData(const char* buf, size_t len,
99 std::vector<can_frame>* out_frames);
100 bool DecodeSLCANFrame(const std::string& line, can_frame* frame);
101 void DequeueWrite();
102
103 // Overlapped write.
104 bool WriteFrame(const std::string& bytes);
105
106 CommDriverN2KCanable* m_parent;
107 wxString m_portName;
108
109 std::atomic<bool> m_stopRequested;
110
111 // Write queue & accompanying mutex
112 std::mutex m_transmitMutex;
113 std::vector<std::string> m_transmitQueue;
114
115 // Reassembly state for ParseReceivedData(), carried across successive reads
116 // since a SLCAN line may span more than one read.
117 std::string m_partialLine;
118 // If we have received the 'T' start delimiter
119 bool m_hasStartDelimiter = false;
120
121 // Serial port handle
122 HANDLE m_serialPortHandle = INVALID_HANDLE_VALUE;
123
124 // Overlapped I/O state. m_stop_event and m_write_pending_event are
125 // created in the constructor, independent of the port's own lifetime,
126 // so RequestStop()/QueueWrite() are always safe to call even if
127 // OpenPort() hasn't run (or failed) yet.
128 // m_read_overlapped.hEvent and m_write_overlapped.hEvent are created in
129 // OpenPort() and destroyed in ClosePort(), tied to the open handle,
130 // a fresh OVERLAPPED and event pair per open, matching the handle's own
131 // lifetime.
132 HANDLE m_stopEvent = nullptr;
133 HANDLE m_pendingWriteEvent = nullptr;
134 OVERLAPPED m_overlappedRead = {};
135 OVERLAPPED m_overlappedWrite = {};
136
137 // Flag while waiting on a pending read. Used to prevent issuing multiple
138 // reads
139 bool m_isReadPending = false;
140};
141
142// NMEA2000 communications driver for a CANable "Contact" (or other
143// SLCAN-firmware) USB-CAN adapter attached as a serial port.
144// Windows only as SLCAN devices support SocketCAN on Linux
146 friend class CanableSerialThread;
147
148public:
150 DriverListener& listener);
151 ~CommDriverN2KCanable() override;
152
153 void SetListener(DriverListener& l) override {}
154
155 bool SendMessage(std::shared_ptr<const NavMsg> msg,
156 std::shared_ptr<const NavAddr> addr) override;
157
158 void Open();
159 void Close();
160
161 // Queues a complete SLCAN frame.
162 bool WriteFrame(const std::string& bytes);
163
164 // Send PGN 60928 Address Claim.
165 // Mirrors CommDriverN2KSocketCanImpl::SendAddressClaim()
166 bool SendAddressClaim(int proposed_source_address);
167
168 // Send PGN 126996 Product Information
169 // Mirrors CommDriverN2KSocketCanImpl::SendProductInfo()
170 bool SendProductInfo();
171
172 // Persist our network address
173 void UpdateAttrCanAddress();
174
175 // Return "crude" statistics - populates the tick box in the connection
176 // dialog? Mirrors CommDriverN2KSocketCAN::GetDriverStats()
177 DriverStats GetDriverStats() const override;
178
179private:
180 // Encode message into Actisense format
181 void EncodeActisenseMessage(const can_frame& frame);
182
183 // Parses a limited set of NMEA Messages, namely PGN 60928 Address Claim
184 // and PGN 59904 ISO Requests
185 // Mirrors Worker::ProcessRxMessages() in comm_drv_n2k_socketcan.cpp.
186 // Called from HandleCanFrameInput(), so also always on the worker thread
187 void ProcessMessages(const std::shared_ptr<const Nmea2000Msg>& msg);
188
189 // Constructs our unique NMEA 2000 NAME
190 void SetN2kName();
191
192 // Encodes NMEA 2000 messages into the SLCAN format
193 std::string EncodeSLCANFrame(uint32_t can_id,
194 const std::vector<uint8_t>& data);
195
196 // Fragments Fast Messages if required
197 std::vector<std::string> FormatCanMessage(uint8_t priority, uint8_t source,
198 uint8_t destination, uint64_t pgn,
199 const std::vector<uint8_t>& data);
200
201 ConnectionParams m_connectionParameters;
202 DriverListener& m_driverListener;
203
204 wxString m_portName;
205
206 // Flag to indicate that the worker thread's port is actually open
207 bool m_isPortOpen = false;
208
209 // Basic statis, interface name, Tx & Rx counts
210 DriverStats m_driverStatistics;
211
212 // The fast message map - based on TwoCan's without any acknowledgemnt; the
213 // bastards
214 std::unique_ptr<FastMessageMap> m_fastMessageMap;
215
216 // The worker thread
217 std::unique_ptr<CanableSerialThread> m_serialThread;
218
219 // Guards m_sequenceNumber
220 std::mutex m_sequenceNumberMutex;
221
222 // Fast Message sequence number
223 int m_sequenceNumber = 0;
224
225 N2kName m_deviceInformation;
226 // Used for NMEA 2000 NAME and Product Information
227 int m_uniqueNumber = 1;
228
229 // Seems to be the OpenCPN default source address. See also
230 // comm_drv_n2k_socketcan's
231 static const int kDefaultSourceAddress = 72;
232 std::atomic<int> m_sourceAddress{kDefaultSourceAddress};
233
234 // Driver Stats are updated by a timer, not called by OpenCPN ?
235 StatsTimer m_statsTimer;
236
237 // Store the time when the serial listener initialized
238 // Used to generate the Actisense time stamps;
239 std::chrono::steady_clock::time_point m_startTime;
240};
241
242#endif
void SetListener(DriverListener &l) override
Set the entity which will receive incoming data.
DriverStats GetDriverStats() const override
Get the Driver Statistics.
Connection data container close to a POD struct.
Definition conn_params.h:76
Interface for handling incoming messages.
Definition comm_driver.h:50
Driver interface providing driver statistics.
Continuously report driver stats to CommDrvRegistry.evt_driver_stats.
Low-level socketcan utility functions.
Communication driver layer.
Nmea2000 driver.
Communication statistics infrastructure.
Raw messages layer, supports sending and recieving navmsg messages.
Connection parameters.
Driver statistics report.
N2k uses CAN which defines the basic properties of messages.
Definition comm_navmsg.h:70