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 std::string error_msg;
234 if (n0183_msg->state == NavMsg::State::kCannotParse)
235 error_msg = _("Unparsable NMEA0183 message").ToStdString();
236 else if (n0183_msg->state == NavMsg::State::kBadChecksum)
237 error_msg = _("NMEA0183 checksum error").ToStdString();
238 bool is_filtered = n0183_msg->state == NavMsg::State::kFiltered;
239 bool cs_error = n0183_msg->state == NavMsg::State::kBadChecksum;
240 LogInputMessage(n0183_msg, is_filtered, cs_error, error_msg);
241
242 // Perform multiplexer output functions
243 for (auto &driver : CommDriverRegistry::GetInstance().GetDrivers()) {
244 if (!driver) continue;
245 if (driver->bus == NavAddr::Bus::N0183) {
246 auto *drv_n0183 = dynamic_cast<CommDriverN0183 *>(driver.get());
247 assert(drv_n0183);
248
249 bool passes_input_filter = n0183_msg->state != NavMsg::State::kFiltered;
250
251 ConnectionParams params_ = drv_n0183->GetParams();
252 std::shared_ptr<const Nmea0183Msg> msg = n0183_msg;
253 if ((m_legacy_input_filter_behaviour && !passes_input_filter) ||
254 passes_input_filter) {
255 // Allow re-transmit on same port (if type is SERIAL),
256 // or any other NMEA0183 port supporting output
257 // But, do not echo to the source network interface. This will
258 // likely recurse...
259 if ((!params_.DisableEcho && params_.Type == SERIAL) ||
260 driver->iface != n0183_msg->source->iface) {
261 if (params_.IOSelect == DS_TYPE_INPUT_OUTPUT ||
262 params_.IOSelect == DS_TYPE_OUTPUT) {
263 bool bout_filter = true;
264 bool bxmit_ok = true;
265 std::string id("XXXXX");
266 size_t comma_pos = n0183_msg->payload.find(',');
267 if (comma_pos != std::string::npos && comma_pos > 5)
268 id = n0183_msg->payload.substr(1, comma_pos - 1);
269 if (params_.SentencePassesFilter(n0183_msg->payload.c_str(),
270 FILTER_OUTPUT)) {
271 // Reset source address. It's const, so make a modified copy
272 auto null_addr = std::make_shared<NavAddr>();
273 msg = std::make_shared<Nmea0183Msg>(id, n0183_msg->payload,
274 null_addr);
275 bxmit_ok = driver->SendMessage(msg, null_addr);
276 bout_filter = false;
277 }
278
279 // Send to the Debug Window, if open
280 if (m_log_callbacks.log_is_active()) {
281 NavmsgStatus ns;
282 ns.direction = NavmsgStatus::Direction::kOutput;
283 if (bout_filter) {
284 ns.accepted = NavmsgStatus::Accepted::kFilteredDropped;
285 } else {
286 if (!bxmit_ok) ns.status = NavmsgStatus::State::kTxError;
287 }
288 auto logaddr = std::make_shared<NavAddr0183>(driver->iface);
289 auto logmsg = std::make_shared<Nmea0183Msg>(
290 id, n0183_msg->payload, logaddr);
291 LogOutputMessage(logmsg, ns);
292 }
293 }
294 }
295 }
296 }
297 }
298}
299
300bool Multiplexer::HandleN2kLog(
301 const std::shared_ptr<const Nmea2000Msg> &n2k_msg) {
302 if (!m_log_callbacks.log_is_active()) return false;
303
304 auto payload = n2k_msg->payload;
305 // extract PGN
306 unsigned int pgn = 0;
307 pgn += n2k_msg->payload.at(3);
308 pgn += n2k_msg->payload.at(4) << 8;
309 pgn += n2k_msg->payload.at(5) << 16;
310
311 // Input, or output?
312 if (payload.at(0) == 0x94) { // output
313 NavmsgStatus ns;
314 ns.direction = NavmsgStatus::Direction::kOutput;
315 LogOutputMessage(n2k_msg, ns);
316 } else { // input
317 // extract data source
318 std::string source = n2k_msg->source->to_string();
319
320 // extract source ID
321 unsigned char source_id = n2k_msg->payload.at(7);
322 char ss[4];
323 sprintf(ss, "%d", source_id);
324 std::string ident = std::string(ss);
325
326 if (pgn == m_last_pgn_logged) {
327 m_n2k_repeat_count++;
328 return false;
329 }
330 if (m_n2k_repeat_count) {
331 wxString repeat_log_msg;
332 repeat_log_msg.Printf("...Repeated %d times\n", m_n2k_repeat_count);
333 // LogInputMessage(repeat_log_msg, "N2000", false, false); FIXME(leamas)
334 m_n2k_repeat_count = 0;
335 }
336
337 wxString log_msg;
338 log_msg.Printf("PGN: %d Source: %s ID: %s Desc: %s\n", pgn, source, ident,
339 N2K_LogMessage_Detail(pgn).c_str());
340
341 LogInputMessage(n2k_msg, false, false);
342
343 m_last_pgn_logged = pgn;
344 }
345 return true;
346}
347
348void Multiplexer::OnNewMessageType() {
349 for (auto msg_key : NavMsgBus::GetInstance().GetActiveMessages()) {
350 if (m_listeners.find(msg_key) != m_listeners.end()) continue;
351 auto key_parts = ocpn::split(msg_key, "::");
352 switch (NavMsg::GetBusByKey(msg_key)) {
353 case NavAddr::Bus::N0183:
354 m_listeners[msg_key] =
355 ObsListener(RawKey(key_parts[1]), [&](ObservedEvt &ev) {
356 HandleN0183(UnpackEvtPointer<Nmea0183Msg>(ev));
357 });
358 break;
359
360 case NavAddr::Bus::N2000:
361 m_listeners[msg_key] =
362 ObsListener(RawKey(key_parts[1]), [&](ObservedEvt &ev) {
363 HandleN2kLog(UnpackEvtPointer<Nmea2000Msg>(ev));
364 });
365 break;
366
367 default:
368 break;
369 }
370 }
371}
NMEA0183 basic parsing common parts:
The global driver registry, a singleton.
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.
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