OpenCPN Partial API docs
Loading...
Searching...
No Matches
multiplexer.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: NMEA Data Multiplexer Object
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
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"
50#include "model/comm_drv_n0183_android_bt.h"
52#include "model/nmea_log.h"
53
54wxDEFINE_EVENT(EVT_N0183_MUX, ObservedEvt);
55
56wxDEFINE_EVENT(EVT_N2K_129029, ObservedEvt);
57wxDEFINE_EVENT(EVT_N2K_129025, ObservedEvt);
58wxDEFINE_EVENT(EVT_N2K_129026, ObservedEvt);
59wxDEFINE_EVENT(EVT_N2K_127250, ObservedEvt);
60wxDEFINE_EVENT(EVT_N2K_129540, ObservedEvt);
61wxDEFINE_EVENT(EVT_N2K_ALL, ObservedEvt);
62
63Multiplexer *g_pMUX;
64
65Multiplexer::Multiplexer(MuxLogCallbacks cb, bool &filter_behaviour)
66 : m_log_callbacks(cb), m_legacy_input_filter_behaviour(filter_behaviour) {
67 m_listener_N0183_all.Listen(Nmea0183Msg::MessageKey("ALL"), this,
68 EVT_N0183_MUX);
69 Bind(EVT_N0183_MUX, [&](ObservedEvt ev) {
70 auto ptr = ev.GetSharedPtr();
71 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
72 HandleN0183(n0183_msg);
73 });
74
75 InitN2KCommListeners();
76 n_N2K_repeat = 0;
77
78 if (g_GPS_Ident.IsEmpty()) g_GPS_Ident = wxT("Generic");
79}
80
81Multiplexer::~Multiplexer() {}
82
83void Multiplexer::LogOutputMessage(const std::shared_ptr<const NavMsg> &msg,
84 NavmsgStatus ns) {
85 if (m_log_callbacks.log_is_active()) {
86 ns.direction = NavmsgStatus::Direction::kOutput;
87 Logline ll(msg, ns);
88 m_log_callbacks.log_message(ll);
89 }
90}
91
92void Multiplexer::LogInputMessage(const std::shared_ptr<const NavMsg> &msg,
93 bool is_filtered, bool is_error,
94 const wxString error_msg) {
95 if (m_log_callbacks.log_is_active()) {
96 NavmsgStatus ns;
97 ns.direction = NavmsgStatus::Direction::kReceived;
98 if (is_error) {
99 ns.status = NavmsgStatus::State::kChecksumError;
100 } else {
101 if (is_filtered) {
102 if (m_legacy_input_filter_behaviour) {
103 ns.accepted = NavmsgStatus::Accepted::kFilteredNoOutput;
104 } else {
105 ns.accepted = NavmsgStatus::Accepted::kFilteredDropped;
106 }
107 } else {
108 ns.accepted = NavmsgStatus::Accepted::kOk;
109 }
110 }
111 Logline ll(msg, ns);
112 ll.error_msg = error_msg;
113 m_log_callbacks.log_message(ll);
114 }
115}
116
117void Multiplexer::HandleN0183(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
118 // Find the driver that originated this message
119
120 const auto &drivers = CommDriverRegistry::GetInstance().GetDrivers();
121 auto &source_driver = FindDriver(drivers, n0183_msg->source->iface);
122
123 wxString fmsg;
124 bool bpass_input_filter = true;
125
126 // Send to the Debug Window, if open
127 // Special formatting for non-printable characters helps debugging NMEA
128 // problems
129 if (m_log_callbacks.log_is_active()) {
130 std::string str = n0183_msg->payload;
131
132 // Get the params for the driver sending this message
133 ConnectionParams params;
134 auto drv_serial =
135 dynamic_cast<CommDriverN0183Serial *>(source_driver.get());
136 if (drv_serial) {
137 params = drv_serial->GetParams();
138 } else {
139 auto drv_net = dynamic_cast<CommDriverN0183Net *>(source_driver.get());
140 if (drv_net) {
141 params = drv_net->GetParams();
142 }
143#ifdef __ANDROID__
144 else {
145 auto drv_bluetooth =
146 dynamic_cast<CommDriverN0183AndroidBT *>(source_driver.get());
147
148 if (drv_bluetooth) {
149 params = drv_bluetooth->GetParams();
150 }
151 }
152#endif
153 }
154
155 // Check to see if the message passes the source's input filter
156 bpass_input_filter =
157 params.SentencePassesFilter(n0183_msg->payload.c_str(), FILTER_INPUT);
158
159 bool b_error = false;
160 wxString error_msg;
161 for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
162 if (isprint(*it))
163 fmsg += *it;
164 else {
165 wxString bin_print;
166 bin_print.Printf(_T("<0x%02X>"), *it);
167 fmsg += bin_print;
168 if ((*it != 0x0a) && (*it != 0x0d)) {
169 b_error = true;
170 error_msg = _("Non-printable character in NMEA0183 message");
171 }
172 }
173 }
174
175 // FIXME (dave) Flag checksum errors, but fix and process the sentence
176 // anyway
177 // std::string goodMessage(message);
178 // bool checksumOK = CheckSumCheck(event.GetNMEAString());
179 // if (!checksumOK) {
180 // goodMessage = stream->FixChecksum(goodMessage);
181 // goodEvent->SetNMEAString(goodMessage);
182 //}
183
184 wxString port(n0183_msg->source->iface);
185 LogInputMessage(n0183_msg, !bpass_input_filter, b_error, error_msg);
186 }
187
188 // Detect virtual driver, message comes from plugin API
189 // Set such source iface to "" for later test
190 std::string source_iface;
191 if (source_driver) // NULL for virtual driver
192 source_iface = source_driver->iface;
193
194 // Perform multiplexer output functions
195 for (auto &driver : drivers) {
196 if (driver->bus == NavAddr::Bus::N0183) {
197 ConnectionParams params;
198 auto drv_serial = dynamic_cast<CommDriverN0183Serial *>(driver.get());
199 if (drv_serial) {
200 params = drv_serial->GetParams();
201 } else {
202 auto drv_net = dynamic_cast<CommDriverN0183Net *>(driver.get());
203 if (drv_net) {
204 params = drv_net->GetParams();
205 }
206#ifdef __ANDROID__
207 else {
208 auto drv_bluetooth =
209 dynamic_cast<CommDriverN0183AndroidBT *>(driver.get());
210 if (drv_bluetooth) {
211 params = drv_bluetooth->GetParams();
212 }
213 }
214#endif
215 }
216
217 std::shared_ptr<const Nmea0183Msg> msg = n0183_msg;
218 if ((m_legacy_input_filter_behaviour && !bpass_input_filter) ||
219 bpass_input_filter) {
220 // Allow re-transmit on same port (if type is SERIAL),
221 // or any other NMEA0183 port supporting output
222 // But, do not echo to the source network interface. This will likely
223 // recurse...
224 if ((!params.DisableEcho && params.Type == SERIAL) ||
225 driver->iface != source_iface) {
226 if (params.IOSelect == DS_TYPE_INPUT_OUTPUT ||
227 params.IOSelect == DS_TYPE_OUTPUT) {
228 bool bout_filter = true;
229 bool bxmit_ok = true;
230 std::string id("XXXXX");
231 size_t comma_pos = n0183_msg->payload.find(",");
232 if (comma_pos != std::string::npos && comma_pos > 5)
233 id = n0183_msg->payload.substr(1, comma_pos - 1);
234 if (params.SentencePassesFilter(n0183_msg->payload.c_str(),
235 FILTER_OUTPUT)) {
236 // Reset source address. It's const, so make a modified copy
237
238 auto null_addr = std::make_shared<NavAddr>();
239 msg = std::make_shared<Nmea0183Msg>(id, n0183_msg->payload,
240 null_addr);
241 bxmit_ok = driver->SendMessage(msg, null_addr);
242 bout_filter = false;
243 }
244 // Send to the Debug Window, if open
245 NavmsgStatus ns;
246 ns.direction = NavmsgStatus::Direction::kOutput;
247 if (bout_filter) {
248 ns.accepted = NavmsgStatus::Accepted::kFilteredDropped;
249 } else {
250 if (!bxmit_ok) ns.status = NavmsgStatus::State::kTxError;
251 }
252 auto logaddr = std::make_shared<NavAddr0183>(driver->iface);
253 auto logmsg =
254 std::make_shared<Nmea0183Msg>(id, n0183_msg->payload, logaddr);
255 LogOutputMessage(logmsg, ns);
256 }
257 }
258 }
259 }
260 }
261}
262
263void Multiplexer::InitN2KCommListeners() {
264 // Create a series of N2K listeners
265 // to allow minimal N2K Debug window logging
266
267 // All N2K
268 //----------------------------------
269 Nmea2000Msg n2k_msg_All(static_cast<uint64_t>(1));
270 listener_N2K_All.Listen(n2k_msg_All, this, EVT_N2K_ALL);
271 Bind(EVT_N2K_ALL, [&](ObservedEvt ev) {
272 HandleN2K_Log(UnpackEvtPointer<Nmea2000Msg>(ev));
273 });
274}
275
276bool Multiplexer::HandleN2K_Log(std::shared_ptr<const Nmea2000Msg> n2k_msg) {
277 if (!m_log_callbacks.log_is_active()) return false;
278
279 // extract PGN
280 unsigned int pgn = 0;
281 pgn += n2k_msg.get()->payload.at(3);
282 pgn += n2k_msg.get()->payload.at(4) << 8;
283 pgn += n2k_msg.get()->payload.at(5) << 16;
284
285 // extract data source
286 std::string source = n2k_msg.get()->source->to_string();
287
288 // extract source ID
289 unsigned char source_id = n2k_msg.get()->payload.at(7);
290 char ss[4];
291 sprintf(ss, "%d", source_id);
292 std::string ident = std::string(ss);
293
294 if (pgn == last_pgn_logged) {
295 n_N2K_repeat++;
296 return false;
297 } else {
298 if (n_N2K_repeat) {
299 wxString repeat_log_msg;
300 repeat_log_msg.Printf("...Repeated %d times\n", n_N2K_repeat);
301 // LogInputMessage(repeat_log_msg, "N2000", false, false); FIXME(leamas)
302 n_N2K_repeat = 0;
303 }
304 }
305
306 wxString log_msg;
307 log_msg.Printf("PGN: %d Source: %s ID: %s Desc: %s\n", pgn, source, ident,
308 N2K_LogMessage_Detail(pgn).c_str());
309
310 LogInputMessage(n2k_msg, false, false);
311
312 last_pgn_logged = pgn;
313 return true;
314}
315
316std::string Multiplexer::N2K_LogMessage_Detail(unsigned int pgn) {
317 std::string notused = "Not used by OCPN, maybe by Plugins";
318
319 switch (pgn) {
320 case 129029:
321 return "GNSS Position & DBoard: SAT System";
322 case 129025:
323 return "Position rapid";
324 case 129026:
325 return "COG/SOG rapid";
326 case 129038:
327 return "AIS Class A position report";
328 case 129039:
329 return "AIS Class B position report";
330 case 129041:
331 return "AIS Aids to Navigation (AtoN) Report";
332 case 129793:
333 return "AIS Base Station report";
334 case 129794:
335 return "AIS static data class A";
336 ;
337 case 129809:
338 return "AIS static data class B part A";
339 case 129810:
340 return "AIS static data class B part B";
341 case 127250:
342 return "Heading rapid";
343 case 129540:
344 return "GNSS Sats & DBoard: SAT Status";
345 //>> Dashboard
346 case 127245:
347 return "DBoard: Rudder data";
348 case 127257:
349 return "DBoard: Roll Pitch";
350 case 128259:
351 return "DBoard: Speed through water";
352 ;
353 case 128267:
354 return "DBoard: Depth Data";
355 case 128275:
356 return "DBoard: Distance log";
357 case 130306:
358 return "DBoard: Wind data";
359 case 130310:
360 return "DBoard: Envorinment data";
361 // Not used PGNs
362 case 126992:
363 return "System time. " + notused;
364 case 127233:
365 return "Man Overboard Notification. " + notused;
366 case 127237:
367 return "Heading/Track control. " + notused;
368 case 127251:
369 return "Rate of turn. " + notused;
370 case 127258:
371 return "Magnetic variation. " + notused;
372 case 127488:
373 return "Engine rapid param. " + notused;
374 case 127489:
375 return "Engine parameters dynamic. " + notused;
376 case 127493:
377 return "Transmission parameters dynamic. " + notused;
378 case 127497:
379 return "Trip Parameters, Engine. " + notused;
380 case 127501:
381 return "Binary status report. " + notused;
382 case 127505:
383 return "Fluid level. " + notused;
384 case 127506:
385 return "DC Detailed Status. " + notused;
386 case 127507:
387 return "Charger Status. " + notused;
388 case 127508:
389 return "Battery Status. " + notused;
390 case 127513:
391 return "Battery Configuration Status. " + notused;
392 case 128000:
393 return "Leeway. " + notused;
394 case 128776:
395 return "Windlass Control Status. " + notused;
396 case 128777:
397 return "Windlass Operating Status. " + notused;
398 case 128778:
399 return "Windlass Monitoring Status. " + notused;
400 case 129033:
401 return "Date,Time & Local offset. " + notused;
402 case 129539:
403 return "GNSS DOP data. " + notused;
404 case 129283:
405 return "Cross Track Error. " + notused;
406 case 129284:
407 return "Navigation info. " + notused;
408 case 129285:
409 return "Waypoint list. " + notused;
410 case 129802:
411 return "AIS Safety Related Broadcast Message. " + notused;
412 case 130074:
413 return "Waypoint list. " + notused;
414 case 130311:
415 return "Environmental parameters. " + notused;
416 case 130312:
417 return "Temperature. " + notused;
418 case 130313:
419 return "Humidity. " + notused;
420 case 130314:
421 return "Actual Pressure. " + notused;
422 case 130315:
423 return "Set Pressure. " + notused;
424 case 130316:
425 return "Temperature extended range. " + notused;
426 case 130323:
427 return "Meteorological Station Data. " + notused;
428 case 130576:
429 return "Trim Tab Position. " + notused;
430 case 130577:
431 return "Direction Data. " + notused;
432 default:
433 return "No description. Not used by OCPN, maybe passed to plugins";
434 }
435}
const std::vector< DriverPtr > & GetDrivers() const
void LogInputMessage(const std::shared_ptr< const NavMsg > &msg, bool is_filtered, bool is_error, const wxString error_msg="")
Logs an input message with context information.
Representation of message status as determined by the multiplexer.
static std::string MessageKey(const char *type="ALL")
Return key which should be used to listen to given message type.
See: https://github.com/OpenCPN/OpenCPN/issues/2729#issuecomment-1179506343.
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Custom event class for OpenCPN's notification system.
std::shared_ptr< const void > GetSharedPtr() const
Gets the event's payload data.
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.
Item in the log window.
Definition nmea_log.h:10