OpenCPN Partial API docs
Loading...
Searching...
No Matches
navmsg_filter.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2025 Alec Leamas *
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#include <fstream>
25
26#include "model/base_platform.h"
27#include "model/navmsg_filter.h"
28
29#include "wx/jsonreader.h"
30#include "wx/jsonwriter.h"
31#include "wx/log.h"
32
33#include "std_filesystem.h"
34
35using Direction = NavmsgStatus::Direction;
36using State = NavmsgStatus::State;
37using Accepted = NavmsgStatus::Accepted;
38
39static const std::unordered_map<Accepted, std::string> StringByAccepted = {
40 {Accepted::kOk, "Ok"},
41 {Accepted::kFilteredNoOutput, "FilteredNoOutput"},
42 {Accepted::kFilteredDropped, "FilteredDropped"},
43 {Accepted::kNone, "None"}};
44
45std::string NavmsgStatus::AcceptedToString(Accepted a) {
46 try {
47 return StringByAccepted.at(a);
48 } catch (std::out_of_range& e) {
49 std::cout << "Error: " << e.what() << " :" << static_cast<int>(a) << "\n";
50 assert(false && "Bad Accepted state");
51 return ""; // For the compiler
52 }
53}
54
55Accepted NavmsgStatus::StringToAccepted(const std::string& s) {
56 for (auto& kv : StringByAccepted)
57 if (kv.second == s) return kv.first;
58 return Accepted::kNone;
59}
60
61// clang-format: off
62static const std::unordered_map<const char*, Direction> dir_map = {
63 {"input", Direction::kInput},
64 {"handled", Direction::kHandled},
65 {"output", Direction::kOutput},
66 {"internal", Direction::kInternal},
67 {"none", NavmsgStatus::Direction::kNone}};
68
69static const std::unordered_map<const char*, State> statemap = {
70 {"ok", State::kOk},
71 {"checksum-err", State::kChecksumError},
72 {"malformed", State::kMalformed},
73 {"tx-error", State::kTxError}};
74
75static const std::unordered_map<const char*, Accepted> acceptmap = {
76 {"Ok", Accepted::kOk},
77 {"FilteredDropped", Accepted::kFilteredDropped},
78 {"FilteredNoOutput", Accepted::kFilteredNoOutput},
79 {"None", Accepted::kNone}}; // clang-format: on
80
81static NavmsgStatus::Direction StringToDirection(const std::string s) {
82 // Transition from 5.12 beta which used "received" isf "handled"; to
83 // be removed
84 if (s == "received") return NavmsgStatus::Direction::kHandled;
85
86 for (auto kv : dir_map)
87 if (kv.first == s) return kv.second;
88 return NavmsgStatus::Direction::kNone;
89}
90
91static std::string DirectionToString(Direction direction) {
92 for (auto kv : dir_map)
93 if (kv.second == direction) return kv.first;
94 assert(false && "Illegal direction enumeration type (!)");
95 return ""; // for the compiler
96}
97
98static Accepted StringToAccepted(const std::string s) {
99 for (auto kv : acceptmap)
100 if (kv.first == s) return kv.second;
101 return Accepted::kNone;
102}
103
104static std::string AcceptedToString(Accepted accept) {
105 for (auto kv : acceptmap)
106 if (kv.second == accept) return kv.first;
107 assert(false && "Illegal direction enumeration type (!)");
108 return ""; // for the compiler
109}
110
111static State StringToState(const std::string s) {
112 for (auto kv : statemap)
113 if (kv.first == s) return kv.second;
114 return State::kNone;
115}
116
117static std::string StateToString(State state) {
118 for (auto kv : statemap)
119 if (kv.second == state) return kv.first;
120 assert(false && "Illegal direction enumeration type (!)");
121 return ""; // for the compiler
122}
123
124static void ParseBuses(NavmsgFilter& filter, wxJSONValue json_val) {
125 for (int i = 0; i < json_val.Size(); i++) {
126 auto str = json_val[i].AsString().ToStdString();
127 filter.buses.insert(NavAddr::StringToBus(str));
128 }
129}
130
131static void ParseDirections(NavmsgFilter& filter, wxJSONValue json_val) {
132 for (int i = 0; i < json_val.Size(); i++) {
133 auto str = json_val[i].AsString().ToStdString();
134 filter.directions.insert(StringToDirection(str));
135 }
136}
137
138static void ParseAccepted(NavmsgFilter& filter, wxJSONValue json_val) {
139 for (int i = 0; i < json_val.Size(); i++) {
140 auto str = json_val[i].AsString().ToStdString();
141 filter.accepted.insert(StringToAccepted(str));
142 }
143}
144
145static void ParseStatus(NavmsgFilter& filter, wxJSONValue json_val) {
146 for (int i = 0; i < json_val.Size(); i++) {
147 auto str = json_val[i].AsString().ToStdString();
148 filter.status.insert(StringToState(str));
149 }
150}
151
152static void ParseMsgFilter(NavmsgFilter& filter, wxJSONValue json_val) {
153 if (json_val.HasMember("blockedMsg")) {
154 auto val = json_val["blockedMsg"];
155 for (int i = 0; i < val.Size(); i++)
156 filter.exclude_msg.insert(val[i].AsString().ToStdString());
157 } else if (json_val.HasMember("allowedMsg")) {
158 auto val = json_val["allowedMsg"];
159 for (int i = 0; i < val.Size(); i++)
160 filter.include_msg.insert(val[i].AsString().ToStdString());
161 }
162}
163
164static void ParseInterfaces(NavmsgFilter& filter, wxJSONValue json_val) {
165 for (int i = 0; i < json_val.Size(); i++)
166 filter.interfaces.insert(json_val[i].AsString().ToStdString());
167}
168
169static void ParsePgn(NavmsgFilter& filter, wxJSONValue json_val) {
170 try {
171 for (int i = 0; i < json_val.Size(); i++)
172 filter.pgns.insert(std::stoi(json_val[i].AsString().ToStdString()));
173 } catch (...) {
174 return;
175 }
176}
177
178static void ParseSource(NavmsgFilter& filter, wxJSONValue json_val) {
179 try {
180 for (int i = 0; i < json_val.Size(); i++) {
181 filter.src_pgns.insert(std::stoi(json_val[i].AsString().ToStdString()));
182 }
183 } catch (...) {
184 return;
185 }
186}
187
188std::vector<NavmsgFilter> NavmsgFilter::GetFilters(const fs::path& dir) {
189 std::vector<NavmsgFilter> filters;
190 try {
191 for (auto& entry : fs::directory_iterator(dir)) {
192 auto filter = NavmsgFilter::Parse(entry);
193 if (filter.m_is_valid)
194 filters.push_back(filter);
195 else
196 wxLogWarning("Illegal system filter: %s",
197 entry.path().string().c_str());
198 }
199 } catch (...) {
200 wxLogWarning("Bad system filter path: %s", dir.string().c_str());
201 }
202 return filters;
203}
204
205std::vector<NavmsgFilter> NavmsgFilter::GetSystemFilters() {
206 fs::path dirpath(g_BasePlatform->GetSharedDataDir().ToStdString());
207 return NavmsgFilter::GetFilters(dirpath / "filters");
208}
209
210std::vector<NavmsgFilter> GetUserFilters() {
211 fs::path dirpath(g_BasePlatform->GetPrivateDataDir().ToStdString());
212 return NavmsgFilter::GetFilters(dirpath / "filters");
213}
214
215std::vector<NavmsgFilter> NavmsgFilter::GetAllFilters() {
216 std::vector<NavmsgFilter> filters = GetSystemFilters();
217 std::vector user_filters = GetUserFilters();
218 filters.insert(filters.end(), user_filters.begin(), user_filters.end());
219 return filters;
220}
221
223 const std::shared_ptr<const NavMsg>& msg) {
224 if (directions.size() > 0) {
225 if (directions.find(msg_status.direction) == directions.end()) return false;
226 }
227 if (status.size() > 0) {
228 if (status.find(msg_status.status) == status.end()) return false;
229 }
230 if (buses.size() > 0) {
231 if (buses.find(msg->bus) == buses.end()) return false;
232 }
233 if (include_msg.size() > 0) {
234 if (include_msg.find(msg->key()) == include_msg.end()) return false;
235 }
236 if (exclude_msg.size() > 0) {
237 if (exclude_msg.find(msg->key()) != exclude_msg.end()) return false;
238 }
239 if (interfaces.size() > 0) {
240 if (interfaces.find(msg->source->iface) == interfaces.end()) return false;
241 }
242 if (accepted.size() > 0) {
243 if (accepted.find(msg_status.accepted) == accepted.end()) return false;
244 }
245 auto n2k_msg = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
246 if (n2k_msg) {
247 if (pgns.size() > 0) {
248 if (pgns.find(n2k_msg->PGN) == pgns.end()) return false;
249 }
250 if (src_pgns.size() > 0) {
251 auto src = std::static_pointer_cast<const NavAddr2000>(msg->source);
252 if (src && src_pgns.find(src->name) == src_pgns.end()) return false;
253 }
254 }
255 return true;
256}
257
258NavmsgFilter NavmsgFilter::Parse(const fs::path& path) {
259 std::ifstream stream(path);
260 if (!stream.is_open()) return NavmsgFilter(false);
261 std::stringstream ss;
262 ss << stream.rdbuf();
263 return NavmsgFilter::Parse(ss.str());
264}
265
266NavmsgFilter NavmsgFilter::Parse(const std::string& string) {
267 wxJSONValue root;
268 wxJSONReader reader;
269 int err_count = reader.Parse(string, &root);
270 if (err_count > 0) {
271 wxLogWarning("Error parsing filter XML");
272 for (auto& e : reader.GetErrors())
273 wxLogWarning("Parse error: %s", e.c_str());
274 return NavmsgFilter(false);
275 }
276 NavmsgFilter filter;
277 filter.m_name = root["filter"]["name"].AsString();
278 filter.m_description = root["filter"]["description"].AsString();
279 if (root["filter"].HasMember("buses"))
280 ParseBuses(filter, root["filter"]["buses"]);
281 if (root["filter"].HasMember("accepted"))
282 ParseAccepted(filter, root["filter"]["accepted"]);
283 if (root["filter"].HasMember("status"))
284 ParseStatus(filter, root["filter"]["status"]);
285 if (root["filter"].HasMember("directions"))
286 ParseDirections(filter, root["filter"]["directions"]);
287 if (root["filter"].HasMember("msgFilter"))
288 ParseMsgFilter(filter, root["filter"]["msgFilter"]);
289 if (root["filter"].HasMember("interfaces"))
290 ParseInterfaces(filter, root["filter"]["interfaces"]);
291 if (root["filter"].HasMember("pgns"))
292 ParsePgn(filter, root["filter"]["pgns"]);
293 if (root["filter"].HasMember("src_pgns"))
294 ParseSource(filter, root["filter"]["src_pgns"]);
295 return filter;
296}
297
298std::string NavmsgFilter::to_string() const {
299 wxJSONValue root;
300 root["filter"]["name"] = m_name;
301 root["filter"]["description"] = m_description;
302 wxJSONValue& filter = root["filter"];
303
304 if (!buses.empty()) {
305 for (auto b : buses) filter["buses"].Append(NavAddr::BusToString(b));
306 };
307 if (!directions.empty()) {
308 for (auto d : directions) filter["directions"].Append(DirectionToString(d));
309 };
310 if (!accepted.empty()) {
311 for (auto a : accepted) filter["accepted"].Append(AcceptedToString(a));
312 };
313 if (!status.empty()) {
314 for (auto s : status) filter["status"].Append(StateToString(s));
315 };
316 if (!include_msg.empty()) {
317 for (auto m : include_msg) filter["msgFilter"]["allowedMsg"].Append(m);
318 } else if (!exclude_msg.empty()) {
319 for (auto m : exclude_msg) filter["msgFilter"]["blockedMsg"].Append(m);
320 }
321 if (!interfaces.empty()) {
322 for (auto i : interfaces) filter["interfaces"].Append(i);
323 }
324 if (!pgns.empty()) {
325 for (auto p : pgns) filter["pgns"].Append(p.to_string());
326 }
327 if (!src_pgns.empty()) {
328 for (auto p : src_pgns) filter["src_pgns"].Append(p.to_string());
329 }
330 wxJSONWriter writer;
331 wxString ws;
332 writer.Write(root, ws);
333 return ws.ToStdString();
334}
BasePlatform * g_BasePlatform
points to g_platform, handles brain-dead MS linker.
Basic platform specific support utilities without GUI deps.
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
bool Pass(NavmsgStatus status, const std::shared_ptr< const NavMsg > &msg)
Return true if message is not matched by filter.
static std::vector< NavmsgFilter > GetFilters(const fs::path &path)
Return list of pre-defined filters shipped with app, test hook.
static std::vector< NavmsgFilter > GetAllFilters()
Return list of all filters, system + user defined.
std::string to_string() const
Output parsable JSON string representation.
static NavmsgFilter Parse(const std::string &s)
Parse text as created by to_string().
static std::vector< NavmsgFilter > GetSystemFilters()
Return list of pre-defined filters shipped with application.
Representation of message status as determined by the multiplexer.
static Accepted StringToAccepted(const std::string &s)
Return Accepted value corresponding to argument s.
static std::string AcceptedToString(Accepted)
Return string representation of argument.
The JSON parser.
Definition jsonreader.h:50
const wxArrayString & GetErrors() const
Return a reference to the error message's array.
int Parse(const wxString &doc, wxJSONValue *val)
Parse the JSON document.
The JSON value class implementation.
Definition jsonval.h:84
int Size() const
Return the size of the array or map stored in this value.
Definition jsonval.cpp:1332
bool HasMember(unsigned index) const
Return TRUE if the object contains an element at the specified index.
Definition jsonval.cpp:1298
wxString AsString() const
Return the stored value as a wxWidget's string.
Definition jsonval.cpp:872
The JSON document writer.
Definition jsonwriter.h:50
void Write(const wxJSONValue &value, wxString &str)
Write the JSONvalue object to a JSON text.
Data monitor filter definitions.