OpenCPN Partial API docs
All Classes Namespaces Functions Variables Pages
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 "multiplexer.h"
44
45#include "config_vars.h"
46#include "conn_params.h"
47#include "comm_drv_registry.h"
48#include "comm_drv_n0183_serial.h"
49#include "comm_drv_n0183_net.h"
50#include "comm_navmsg_bus.h"
51
52#ifdef __linux__
53#include "udev_rule_mgr.h"
54#endif
55
56extern bool g_b_legacy_input_filter_behaviour;
57
58wxDEFINE_EVENT(EVT_N0183_MUX, ObservedEvt);
59
60#ifdef HAVE_READLINK
61
62static std::string do_readlink(const char *link) {
63 // Strip possible Serial: or Usb: prefix:
64 const char *colon = strchr(link, ':');
65 const char *path = colon ? colon + 1 : link;
66
67 char target[PATH_MAX + 1] = {0};
68 int r = readlink(path, target, sizeof(target));
69 if (r == -1 && (errno == EINVAL || errno == ENOENT)) {
70 // Not a a symlink
71 return path;
72 }
73 if (r == -1) {
74 wxLogDebug("Error reading device link %s: %s", path, strerror(errno));
75 return path;
76 }
77 if (*target == '/') {
78 return target;
79 }
80 char buff[PATH_MAX + 1];
81 memcpy(buff, path, std::min(strlen(path) + 1, (size_t)PATH_MAX));
82 return std::string(dirname(buff)) + "/" + target;
83}
84
85static bool is_same_device(const char *port1, const char *port2) {
86 std::string dev1 = do_readlink(port1);
87 std::string dev2 = do_readlink(port2);
88 return dev1 == dev2;
89}
90
91#else // HAVE_READLINK
92
93static bool inline is_same_device(const char *port1, const char *port2) {
94 return strcmp(port1, port2) == 0;
95}
96
97#endif // HAVE_READLINK
98
99Multiplexer::Multiplexer(MuxLogCallbacks cb) : m_log_callbacks(cb) {
100 auto &msgbus = NavMsgBus::GetInstance();
101
102 m_listener_N0183_all.Listen(Nmea0183Msg::MessageKey("ALL"), this,
103 EVT_N0183_MUX);
104 Bind(EVT_N0183_MUX, [&](ObservedEvt ev) {
105 auto ptr = ev.GetSharedPtr();
106 auto n0183_msg = std::static_pointer_cast<const Nmea0183Msg>(ptr);
107 HandleN0183(n0183_msg);
108 });
109
110 if (g_GPS_Ident.IsEmpty()) g_GPS_Ident = wxT("Generic");
111}
112
113Multiplexer::~Multiplexer() {}
114
115void Multiplexer::LogOutputMessageColor(const wxString &msg,
116 const wxString &stream_name,
117 const wxString &color) {
118 if (m_log_callbacks.log_is_active()) {
119 wxDateTime now = wxDateTime::Now();
120 wxString ss;
121#ifndef __WXQT__ // Date/Time on Qt are broken, at least for android
122 ss = now.FormatISOTime();
123#endif
124 ss.Prepend(_T("--> "));
125 ss.Append(_T(" ("));
126 ss.Append(stream_name);
127 ss.Append(_T(") "));
128 ss.Append(msg);
129 ss.Prepend(color);
130
131 m_log_callbacks.log_message(ss.ToStdString());
132 }
133}
134
135void Multiplexer::LogOutputMessage(const wxString &msg, wxString stream_name,
136 bool b_filter) {
137 if (b_filter)
138 LogOutputMessageColor(msg, stream_name, _T("<CORAL>"));
139 else
140 LogOutputMessageColor(msg, stream_name, _T("<BLUE>"));
141}
142
143void Multiplexer::LogInputMessage(const wxString &msg,
144 const wxString &stream_name, bool b_filter,
145 bool b_error) {
146 if (m_log_callbacks.log_is_active()) {
147 wxDateTime now = wxDateTime::Now();
148 wxString ss;
149#ifndef __WXQT__ // Date/Time on Qt are broken, at least for android
150 ss = now.FormatISOTime();
151#endif
152 ss.Append(_T(" ("));
153 ss.Append(stream_name);
154 ss.Append(_T(") "));
155 ss.Append(msg);
156 if (b_error) {
157 ss.Prepend(_T("<RED>"));
158 } else {
159 if (b_filter)
160 if (g_b_legacy_input_filter_behaviour)
161 ss.Prepend(_T("<CORAL>"));
162 else
163 ss.Prepend(_T("<MAROON>"));
164 else
165 ss.Prepend(_T("<GREEN>"));
166 }
167 m_log_callbacks.log_message(ss.ToStdString());
168 }
169}
170
171void Multiplexer::HandleN0183(std::shared_ptr<const Nmea0183Msg> n0183_msg) {
172 // Find the driver that originated this message
173
174 const auto& drivers = CommDriverRegistry::GetInstance().GetDrivers();
175 auto source_driver = FindDriver(drivers, n0183_msg->source->iface);
176
177 wxString fmsg;
178 bool bpass_input_filter = true;
179
180 // Send to the Debug Window, if open
181 // Special formatting for non-printable characters helps debugging NMEA
182 // problems
183 if (m_log_callbacks.log_is_active()) {
184 std::string str = n0183_msg->payload;
185
186 // Get the params for the driver sending this message
187 ConnectionParams params;
188 auto drv_serial =
189 std::dynamic_pointer_cast<CommDriverN0183Serial>(source_driver);
190 if (drv_serial) {
191 params = drv_serial->GetParams();
192 } else {
193 auto drv_net = std::dynamic_pointer_cast<CommDriverN0183Net>(source_driver);
194 if (drv_net) {
195 params = drv_net->GetParams();
196 }
197 }
198
199 // Check to see if the message passes the source's input filter
200 bpass_input_filter = params.SentencePassesFilter(n0183_msg->payload.c_str(),
201 FILTER_INPUT);
202
203 bool b_error = false;
204 for (std::string::iterator it = str.begin(); it != str.end(); ++it) {
205 if (isprint(*it))
206 fmsg += *it;
207 else {
208 wxString bin_print;
209 bin_print.Printf(_T("<0x%02X>"), *it);
210 fmsg += bin_print;
211 if ((*it != 0x0a) && (*it != 0x0d)) b_error = true;
212 }
213 }
214
215 // FIXME (dave) Flag checksum errors, but fix and process the sentence anyway
216 //std::string goodMessage(message);
217 //bool checksumOK = CheckSumCheck(event.GetNMEAString());
218 //if (!checksumOK) {
219 //goodMessage = stream->FixChecksum(goodMessage);
220 //goodEvent->SetNMEAString(goodMessage);
221 //}
222
223
224 wxString port(n0183_msg->source->iface);
225 LogInputMessage(fmsg, port, !bpass_input_filter, b_error);
226 }
227
228 // Detect virtual driver, message comes from plugin API
229 // Set such source iface to "" for later test
230 std::string source_iface;
231 if (source_driver) // NULL for virtual driver
232 source_iface = source_driver->iface;
233
234
235 // Perform multiplexer output functions
236 for (auto& driver : drivers) {
237
238 if (driver->bus == NavAddr::Bus::N0183) {
239 ConnectionParams params;
240 auto drv_serial =
241 std::dynamic_pointer_cast<CommDriverN0183Serial>(driver);
242 if (drv_serial) {
243 params = drv_serial->GetParams();
244 } else {
245 auto drv_net = std::dynamic_pointer_cast<CommDriverN0183Net>(driver);
246 if (drv_net) {
247 params = drv_net->GetParams();
248 }
249 }
250
251 if ((g_b_legacy_input_filter_behaviour && !bpass_input_filter) ||
252 bpass_input_filter) {
253
254 // Allow re-transmit on same port (if type is SERIAL),
255 // or any any other NMEA0183 port supporting output
256 // But, do not echo to the source network interface. This will likely recurse...
257 if (params.Type == SERIAL || driver->iface != source_iface) {
258 if (params.IOSelect == DS_TYPE_INPUT_OUTPUT ||
259 params.IOSelect == DS_TYPE_OUTPUT)
260 {
261 bool bout_filter = true;
262 bool bxmit_ok = true;
263 if (params.SentencePassesFilter(n0183_msg->payload.c_str(),
264 FILTER_OUTPUT)) {
265 bxmit_ok = driver->SendMessage(n0183_msg,
266 std::make_shared<NavAddr0183>(driver->iface));
267 bout_filter = false;
268 }
269
270 // Send to the Debug Window, if open
271 if (!bout_filter) {
272 if (bxmit_ok)
273 LogOutputMessageColor(fmsg, driver->iface, _T("<BLUE>"));
274 else
275 LogOutputMessageColor(fmsg, driver->iface, _T("<RED>"));
276 } else
277 LogOutputMessageColor(fmsg, driver->iface, _T("<CORAL>"));
278 }
279 }
280 }
281 }
282 }
283}
284
const std::vector< DriverPtr > & GetDrivers()
static std::string MessageKey(const char *type="ALL")
Return key which should be used to listen to given message type.
Definition: comm_navmsg.h:272
Adds a std::shared<void> element to wxCommandEvent.
Definition: ocpn_plugin.h:1615