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