OpenCPN Partial API docs
Loading...
Searching...
No Matches
multiplexer.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2010 by David S. Register *
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
24#ifdef __MSVC__
25#include "winsock2.h"
26#include <wx/msw/winundef.h>
27#endif
28
29#ifdef HAVE_LIBGEN_H
30#include <libgen.h>
31#endif
32
33#if defined(HAVE_READLINK) && !defined(HAVE_LIBGEN_H)
34#error Using readlink(3) requires libgen.h which cannot be found.
35#endif
36
37#include "config.h"
38
39#include <wx/wx.h>
40
41#include "model/multiplexer.h"
42
43#include "model/config_vars.h"
44#include "model/conn_params.h"
49#include "model/nmea_log.h"
50
51Multiplexer *g_pMUX;
52
54class RawKey : public KeyProvider {
55public:
56 explicit RawKey(const std::string &key) : m_key(key) {}
57 [[nodiscard]] std::string GetKey() const override { return m_key; }
58
59private:
60 std::string m_key;
61};
62
63static std::string N2K_LogMessage_Detail(unsigned int pgn) {
64 std::string not_used = "Not used by OCPN, maybe by Plugins";
65
66 switch (pgn) {
67 case 129029:
68 return "GNSS Position & DBoard: SAT System";
69 case 129025:
70 return "Position rapid";
71 case 129026:
72 return "COG/SOG rapid";
73 case 129038:
74 return "AIS Class A position report";
75 case 129039:
76 return "AIS Class B position report";
77 case 129041:
78 return "AIS Aids to Navigation (AtoN) Report";
79 case 129793:
80 return "AIS Base Station report";
81 case 129794:
82 return "AIS static data class A";
83 ;
84 case 129809:
85 return "AIS static data class B part A";
86 case 129810:
87 return "AIS static data class B part B";
88 case 127250:
89 return "Heading rapid";
90 case 129540:
91 return "GNSS Sats & DBoard: SAT Status";
92 //>> Dashboard
93 case 127245:
94 return "DBoard: Rudder data";
95 case 127257:
96 return "DBoard: Roll Pitch";
97 case 128259:
98 return "DBoard: Speed through water";
99 ;
100 case 128267:
101 return "DBoard: Depth Data";
102 case 128275:
103 return "DBoard: Distance log";
104 case 130306:
105 return "DBoard: Wind data";
106 case 130310:
107 return "DBoard: Environment data";
108 // Not used PGNs
109 case 126992:
110 return "System time. " + not_used;
111 case 127233:
112 return "Man Overboard Notification. " + not_used;
113 case 127237:
114 return "Heading/Track control. " + not_used;
115 case 127251:
116 return "Rate of turn. " + not_used;
117 case 127258:
118 return "Magnetic variation. " + not_used;
119 case 127488:
120 return "Engine rapid param. " + not_used;
121 case 127489:
122 return "Engine parameters dynamic. " + not_used;
123 case 127493:
124 return "Transmission parameters dynamic. " + not_used;
125 case 127497:
126 return "Trip Parameters, Engine. " + not_used;
127 case 127501:
128 return "Binary status report. " + not_used;
129 case 127505:
130 return "Fluid level. " + not_used;
131 case 127506:
132 return "DC Detailed Status. " + not_used;
133 case 127507:
134 return "Charger Status. " + not_used;
135 case 127508:
136 return "Battery Status. " + not_used;
137 case 127513:
138 return "Battery Configuration Status. " + not_used;
139 case 128000:
140 return "Leeway. " + not_used;
141 case 128776:
142 return "Windlass Control Status. " + not_used;
143 case 128777:
144 return "Windlass Operating Status. " + not_used;
145 case 128778:
146 return "Windlass Monitoring Status. " + not_used;
147 case 129033:
148 return "Date,Time & Local offset. " + not_used;
149 case 129539:
150 return "GNSS DOP data. " + not_used;
151 case 129283:
152 return "Cross Track Error. " + not_used;
153 case 129284:
154 return "Navigation info. " + not_used;
155 case 129285:
156 return "Waypoint list. " + not_used;
157 case 129802:
158 return "AIS Safety Related Broadcast Message. " + not_used;
159 case 130074:
160 return "Waypoint list. " + not_used;
161 case 130311:
162 return "Environmental parameters. " + not_used;
163 case 130312:
164 return "Temperature. " + not_used;
165 case 130313:
166 return "Humidity. " + not_used;
167 case 130314:
168 return "Actual Pressure. " + not_used;
169 case 130315:
170 return "Set Pressure. " + not_used;
171 case 130316:
172 return "Temperature extended range. " + not_used;
173 case 130323:
174 return "Meteorological Station Data. " + not_used;
175 case 130576:
176 return "Trim Tab Position. " + not_used;
177 case 130577:
178 return "Direction Data. " + not_used;
179 default:
180 return "No description. Not used by OCPN, maybe passed to plugins";
181 }
182}
183
184Multiplexer::Multiplexer(const MuxLogCallbacks &cb, bool &filter_behaviour)
185
186 : m_log_callbacks(cb),
187 m_legacy_input_filter_behaviour(filter_behaviour),
188 m_new_msgtype_lstnr(NavMsgBus::GetInstance().new_msg_event,
189 [&](ObservedEvt &) { OnNewMessageType(); }),
190 m_n2k_repeat_count(0),
191 m_last_pgn_logged(0) {
192 if (g_GPS_Ident.IsEmpty()) g_GPS_Ident = "Generic";
193}
194
195Multiplexer::~Multiplexer() = default;
196
197void Multiplexer::LogOutputMessage(const std::shared_ptr<const NavMsg> &msg,
198 NavmsgStatus ns) const {
199 if (m_log_callbacks.log_is_active()) {
200 ns.direction = NavmsgStatus::Direction::kOutput;
201 Logline ll(msg, ns);
202 m_log_callbacks.log_message(ll);
203 }
204}
205
206void Multiplexer::LogInputMessage(const std::shared_ptr<const NavMsg> &msg,
207 bool is_filtered, bool is_error,
208 const wxString &error_msg) const {
209 if (m_log_callbacks.log_is_active()) {
210 NavmsgStatus ns;
211 ns.direction = NavmsgStatus::Direction::kHandled;
212 if (is_error) {
213 ns.status = NavmsgStatus::State::kChecksumError;
214 } else {
215 if (is_filtered) {
216 if (m_legacy_input_filter_behaviour) {
217 ns.accepted = NavmsgStatus::Accepted::kFilteredNoOutput;
218 } else {
219 ns.accepted = NavmsgStatus::Accepted::kFilteredDropped;
220 }
221 } else {
222 ns.accepted = NavmsgStatus::Accepted::kOk;
223 }
224 }
225 Logline ll(msg, ns);
226 ll.error_msg = error_msg;
227 m_log_callbacks.log_message(ll);
228 }
229}
230
231void Multiplexer::HandleN0183(
232 const std::shared_ptr<const Nmea0183Msg> &n0183_msg) const {
233 // Find the driver that originated this message
234 const auto &drivers = CommDriverRegistry::GetInstance().GetDrivers();
235 auto &source_driver = FindDriver(drivers, n0183_msg->source->iface);
236 if (!source_driver) {
237 // might be a message from a "virtual" plugin.
238 if ((n0183_msg->source->iface != "virtual")) {
239 return;
240 }
241 }
242
243 std::string error_msg;
244 if (n0183_msg->state == NavMsg::State::kCannotParse)
245 error_msg = _("Unparsable NMEA0183 message").ToStdString();
246 else if (n0183_msg->state == NavMsg::State::kBadChecksum)
247 error_msg = _("NMEA0183 checksum error").ToStdString();
248 bool is_filtered = n0183_msg->state == NavMsg::State::kFiltered;
249 bool cs_error = n0183_msg->state == NavMsg::State::kBadChecksum;
250 LogInputMessage(n0183_msg, is_filtered, cs_error, error_msg);
251
252 // Detect virtual driver, message comes from plugin API
253 // Set such source iface to "" for later test
254 std::string source_iface;
255 if (source_driver) // NULL for virtual driver
256 source_iface = source_driver->iface;
257
258 // Perform multiplexer output functions
259 for (auto &driver : drivers) {
260 if (!driver) continue;
261 if (driver->bus == NavAddr::Bus::N0183) {
262 auto *drv_n0183 = dynamic_cast<CommDriverN0183 *>(driver.get());
263 assert(drv_n0183);
264
265 bool passes_input_filter = n0183_msg->state != NavMsg::State::kFiltered;
266
267 ConnectionParams params_ = drv_n0183->GetParams();
268 std::shared_ptr<const Nmea0183Msg> msg = n0183_msg;
269 if ((m_legacy_input_filter_behaviour && !passes_input_filter) ||
270 passes_input_filter) {
271 // Allow re-transmit on same port (if type is SERIAL),
272 // or any other NMEA0183 port supporting output
273 // But, do not echo to the source network interface. This will
274 // likely recurse...
275 if ((!params_.DisableEcho && params_.Type == SERIAL) ||
276 driver->iface != source_iface) {
277 if (params_.IOSelect == DS_TYPE_INPUT_OUTPUT ||
278 params_.IOSelect == DS_TYPE_OUTPUT) {
279 bool bout_filter = true;
280 bool bxmit_ok = true;
281 std::string id("XXXXX");
282 size_t comma_pos = n0183_msg->payload.find(',');
283 if (comma_pos != std::string::npos && comma_pos > 5)
284 id = n0183_msg->payload.substr(1, comma_pos - 1);
285 if (params_.SentencePassesFilter(n0183_msg->payload.c_str(),
286 FILTER_OUTPUT)) {
287 // Reset source address. It's const, so make a modified copy
288 auto null_addr = std::make_shared<NavAddr>();
289 msg = std::make_shared<Nmea0183Msg>(id, n0183_msg->payload,
290 null_addr);
291 bxmit_ok = driver->SendMessage(msg, null_addr);
292 bout_filter = false;
293 }
294
295 // Send to the Debug Window, if open
296 if (m_log_callbacks.log_is_active()) {
297 NavmsgStatus ns;
298 ns.direction = NavmsgStatus::Direction::kOutput;
299 if (bout_filter) {
300 ns.accepted = NavmsgStatus::Accepted::kFilteredDropped;
301 } else {
302 if (!bxmit_ok) ns.status = NavmsgStatus::State::kTxError;
303 }
304 auto logaddr = std::make_shared<NavAddr0183>(driver->iface);
305 auto logmsg = std::make_shared<Nmea0183Msg>(
306 id, n0183_msg->payload, logaddr);
307 LogOutputMessage(logmsg, ns);
308 }
309 }
310 }
311 }
312 }
313 }
314}
315
316bool Multiplexer::HandleN2kLog(
317 const std::shared_ptr<const Nmea2000Msg> &n2k_msg) {
318 if (!m_log_callbacks.log_is_active()) return false;
319
320 auto payload = n2k_msg->payload;
321 // extract PGN
322 unsigned int pgn = 0;
323 pgn += n2k_msg->payload.at(3);
324 pgn += n2k_msg->payload.at(4) << 8;
325 pgn += n2k_msg->payload.at(5) << 16;
326
327 // Input, or output?
328 if (payload.at(0) == 0x94) { // output
329 NavmsgStatus ns;
330 ns.direction = NavmsgStatus::Direction::kOutput;
331 LogOutputMessage(n2k_msg, ns);
332 } else { // input
333 // extract data source
334 std::string source = n2k_msg->source->to_string();
335
336 // extract source ID
337 unsigned char source_id = n2k_msg->payload.at(7);
338 char ss[4];
339 sprintf(ss, "%d", source_id);
340 std::string ident = std::string(ss);
341
342 if (pgn == m_last_pgn_logged) {
343 m_n2k_repeat_count++;
344 return false;
345 }
346 if (m_n2k_repeat_count) {
347 wxString repeat_log_msg;
348 repeat_log_msg.Printf("...Repeated %d times\n", m_n2k_repeat_count);
349 // LogInputMessage(repeat_log_msg, "N2000", false, false); FIXME(leamas)
350 m_n2k_repeat_count = 0;
351 }
352
353 wxString log_msg;
354 log_msg.Printf("PGN: %d Source: %s ID: %s Desc: %s\n", pgn, source, ident,
355 N2K_LogMessage_Detail(pgn).c_str());
356
357 LogInputMessage(n2k_msg, false, false);
358
359 m_last_pgn_logged = pgn;
360 }
361 return true;
362}
363
364void Multiplexer::OnNewMessageType() {
365 for (auto msg_key : NavMsgBus::GetInstance().GetActiveMessages()) {
366 if (m_listeners.find(msg_key) != m_listeners.end()) continue;
367 auto key_parts = ocpn::split(msg_key, "::");
368 switch (NavMsg::GetBusByKey(msg_key)) {
369 case NavAddr::Bus::N0183:
370 m_listeners[msg_key] =
371 ObsListener(RawKey(key_parts[1]), [&](ObservedEvt &ev) {
372 HandleN0183(UnpackEvtPointer<Nmea0183Msg>(ev));
373 });
374 break;
375
376 case NavAddr::Bus::N2000:
377 m_listeners[msg_key] =
378 ObsListener(RawKey(key_parts[1]), [&](ObservedEvt &ev) {
379 HandleN2kLog(UnpackEvtPointer<Nmea2000Msg>(ev));
380 });
381 break;
382
383 default:
384 break;
385 }
386 }
387}
NMEA0183 basic parsing common parts:
const std::vector< DriverPtr > & GetDrivers() const
Interface implemented by classes which listens.
Definition observable.h:64
Handle logging and forwarding of incoming n0183/n2k messages.
Definition multiplexer.h:56
void LogInputMessage(const std::shared_ptr< const NavMsg > &msg, bool is_filtered, bool is_error, const wxString &error_msg="") const
Logs an input message with context information.
The raw message layer, a singleton.
static NavAddr::Bus GetBusByKey(const std::string &key)
Return bus corresponding to given key.
Representation of message status as determined by the multiplexer.
Define an action to be performed when a KeyProvider is notified.
Definition observable.h:257
Custom event class for OpenCPN's notification system.
KeyProvider wrapper for a plain key string.
std::string GetKey() const override
Get the Key object from the Key Provider.
NMEA0183 over IP driver.
NMEA0183 serial driver.
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.
Global variables stored in configuration file.
Connection parameters.
Multiplexer class and helpers.
std::vector< std::string > split(const char *token_string, const std::string &delimiter)
Return vector of items in s separated by delimiter.
Basic DataMonitor logging interface: LogLine (reflects a line in the log) and NmeaLog,...
Item in the log window.
Definition nmea_log.h:32