OpenCPN Partial API docs
Loading...
Searching...
No Matches
navmsg_filter.cpp
Go to the documentation of this file.
1
2/***************************************************************************
3 * Copyright (C) 2025 Alec Leamas *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 **************************************************************************/
20
26#include <fstream>
27
28#include "model/base_platform.h"
29#include "model/navmsg_filter.h"
30
31#include "wx/jsonreader.h"
32#include "wx/jsonwriter.h"
33#include "wx/log.h"
34
35#include "std_filesystem.h"
36
37using Direction = NavmsgStatus::Direction;
38using State = NavmsgStatus::State;
39using Accepted = NavmsgStatus::Accepted;
40
41static const std::unordered_map<Accepted, std::string> StringByAccepted = {
42 {Accepted::kOk, "Ok"},
43 {Accepted::kFilteredNoOutput, "FilteredNoOutput"},
44 {Accepted::kFilteredDropped, "FilteredDropped"},
45 {Accepted::kNone, "None"}};
46
47std::string NavmsgStatus::AcceptedToString(Accepted a) {
48 try {
49 return StringByAccepted.at(a);
50 } catch (std::out_of_range& e) {
51 std::cout << "Error: " << e.what() << " :" << static_cast<int>(a) << "\n";
52 assert(false && "Bad Accepted state");
53 return ""; // For the compiler
54 }
55}
56
57Accepted NavmsgStatus::StringToAccepted(const std::string& s) {
58 for (auto& kv : StringByAccepted)
59 if (kv.second == s) return kv.first;
60 return Accepted::kNone;
61}
62
63// clang-format: off
64static const std::unordered_map<const char*, Direction> dir_map = {
65 {"input", Direction::kInput},
66 {"handled", Direction::kHandled},
67 {"output", Direction::kOutput},
68 {"internal", Direction::kInternal},
69 {"none", NavmsgStatus::Direction::kNone}};
70
71static const std::unordered_map<const char*, State> statemap = {
72 {"ok", State::kOk},
73 {"checksum-err", State::kChecksumError},
74 {"malformed", State::kMalformed},
75 {"tx-error", State::kTxError}};
76
77static const std::unordered_map<const char*, Accepted> acceptmap = {
78 {"Ok", Accepted::kOk},
79 {"FilteredDropped", Accepted::kFilteredDropped},
80 {"FilteredNoOutput", Accepted::kFilteredNoOutput},
81 {"None", Accepted::kNone}}; // clang-format: on
82
83static NavmsgStatus::Direction StringToDirection(const std::string s) {
84 // Transition from 5.12 beta which used "received" isf "handled"; to
85 // be removed
86 if (s == "received") return NavmsgStatus::Direction::kHandled;
87
88 for (auto kv : dir_map)
89 if (kv.first == s) return kv.second;
90 return NavmsgStatus::Direction::kNone;
91}
92
93static std::string DirectionToString(Direction direction) {
94 for (auto kv : dir_map)
95 if (kv.second == direction) return kv.first;
96 assert(false && "Illegal direction enumeration type (!)");
97 return ""; // for the compiler
98}
99
100static Accepted StringToAccepted(const std::string s) {
101 for (auto kv : acceptmap)
102 if (kv.first == s) return kv.second;
103 return Accepted::kNone;
104}
105
106static std::string AcceptedToString(Accepted accept) {
107 for (auto kv : acceptmap)
108 if (kv.second == accept) return kv.first;
109 assert(false && "Illegal direction enumeration type (!)");
110 return ""; // for the compiler
111}
112
113static State StringToState(const std::string s) {
114 for (auto kv : statemap)
115 if (kv.first == s) return kv.second;
116 return State::kNone;
117}
118
119static std::string StateToString(State state) {
120 for (auto kv : statemap)
121 if (kv.second == state) return kv.first;
122 assert(false && "Illegal direction enumeration type (!)");
123 return ""; // for the compiler
124}
125
126static void ParseBuses(NavmsgFilter& filter, wxJSONValue json_val) {
127 for (int i = 0; i < json_val.Size(); i++) {
128 auto str = json_val[i].AsString().ToStdString();
129 filter.buses.insert(NavAddr::StringToBus(str));
130 }
131}
132
133static void ParseDirections(NavmsgFilter& filter, wxJSONValue json_val) {
134 for (int i = 0; i < json_val.Size(); i++) {
135 auto str = json_val[i].AsString().ToStdString();
136 filter.directions.insert(StringToDirection(str));
137 }
138}
139
140static void ParseAccepted(NavmsgFilter& filter, wxJSONValue json_val) {
141 for (int i = 0; i < json_val.Size(); i++) {
142 auto str = json_val[i].AsString().ToStdString();
143 filter.accepted.insert(StringToAccepted(str));
144 }
145}
146
147static void ParseStatus(NavmsgFilter& filter, wxJSONValue json_val) {
148 for (int i = 0; i < json_val.Size(); i++) {
149 auto str = json_val[i].AsString().ToStdString();
150 filter.status.insert(StringToState(str));
151 }
152}
153
154static void ParseMsgFilter(NavmsgFilter& filter, wxJSONValue json_val) {
155 if (json_val.HasMember("blockedMsg")) {
156 auto val = json_val["blockedMsg"];
157 for (int i = 0; i < val.Size(); i++)
158 filter.exclude_msg.insert(val[i].AsString().ToStdString());
159 } else if (json_val.HasMember("allowedMsg")) {
160 auto val = json_val["allowedMsg"];
161 for (int i = 0; i < val.Size(); i++)
162 filter.include_msg.insert(val[i].AsString().ToStdString());
163 }
164}
165
166static void ParseInterfaces(NavmsgFilter& filter, wxJSONValue json_val) {
167 for (int i = 0; i < json_val.Size(); i++)
168 filter.interfaces.insert(json_val[i].AsString().ToStdString());
169}
170
171static void ParsePgn(NavmsgFilter& filter, wxJSONValue json_val) {
172 try {
173 for (int i = 0; i < json_val.Size(); i++)
174 filter.pgns.insert(std::stoi(json_val[i].AsString().ToStdString()));
175 } catch (...) {
176 return;
177 }
178}
179
180static void ParseSource(NavmsgFilter& filter, wxJSONValue json_val) {
181 try {
182 for (int i = 0; i < json_val.Size(); i++) {
183 filter.src_pgns.insert(std::stoi(json_val[i].AsString().ToStdString()));
184 }
185 } catch (...) {
186 return;
187 }
188}
189
190std::vector<NavmsgFilter> NavmsgFilter::GetFilters(const fs::path& dir) {
191 std::vector<NavmsgFilter> filters;
192 try {
193 for (auto& entry : fs::directory_iterator(dir)) {
194 auto filter = NavmsgFilter::Parse(entry);
195 if (filter.m_is_valid)
196 filters.push_back(filter);
197 else
198 wxLogWarning("Illegal system filter: %s",
199 entry.path().string().c_str());
200 }
201 } catch (...) {
202 wxLogWarning("Bad system filter path: %s", dir.string().c_str());
203 }
204 return filters;
205}
206
207std::vector<NavmsgFilter> NavmsgFilter::GetSystemFilters() {
208 fs::path dirpath(g_BasePlatform->GetSharedDataDir().ToStdString());
209 return NavmsgFilter::GetFilters(dirpath / "filters");
210}
211
212std::vector<NavmsgFilter> GetUserFilters() {
213 fs::path dirpath(g_BasePlatform->GetPrivateDataDir().ToStdString());
214 return NavmsgFilter::GetFilters(dirpath / "filters");
215}
216
217std::vector<NavmsgFilter> NavmsgFilter::GetAllFilters() {
218 std::vector<NavmsgFilter> filters = GetSystemFilters();
219 std::vector user_filters = GetUserFilters();
220 filters.insert(filters.end(), user_filters.begin(), user_filters.end());
221 return filters;
222}
223
225 const std::shared_ptr<const NavMsg>& msg) {
226 if (directions.size() > 0) {
227 if (directions.find(msg_status.direction) == directions.end()) return false;
228 }
229 if (status.size() > 0) {
230 if (status.find(msg_status.status) == status.end()) return false;
231 }
232 if (buses.size() > 0) {
233 if (buses.find(msg->bus) == buses.end()) return false;
234 }
235 if (include_msg.size() > 0) {
236 if (include_msg.find(msg->key()) == include_msg.end()) return false;
237 }
238 if (exclude_msg.size() > 0) {
239 if (exclude_msg.find(msg->key()) != exclude_msg.end()) return false;
240 }
241 if (interfaces.size() > 0) {
242 if (interfaces.find(msg->source->iface) == interfaces.end()) return false;
243 }
244 if (accepted.size() > 0) {
245 if (accepted.find(msg_status.accepted) == accepted.end()) return false;
246 }
247 auto n2k_msg = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
248 if (n2k_msg) {
249 if (pgns.size() > 0) {
250 if (pgns.find(n2k_msg->PGN) == pgns.end()) return false;
251 }
252 if (src_pgns.size() > 0) {
253 auto src = std::static_pointer_cast<const NavAddr2000>(msg->source);
254 if (src && src_pgns.find(src->name) == src_pgns.end()) return false;
255 }
256 }
257 return true;
258}
259
260NavmsgFilter NavmsgFilter::Parse(const fs::path& path) {
261 std::ifstream stream(path);
262 if (!stream.is_open()) return NavmsgFilter(false);
263 std::stringstream ss;
264 ss << stream.rdbuf();
265 return NavmsgFilter::Parse(ss.str());
266}
267
268NavmsgFilter NavmsgFilter::Parse(const std::string& string) {
269 wxJSONValue root;
270 wxJSONReader reader;
271 int err_count = reader.Parse(string, &root);
272 if (err_count > 0) {
273 wxLogWarning("Error parsing filter XML");
274 for (auto& e : reader.GetErrors())
275 wxLogWarning("Parse error: %s", e.c_str());
276 return NavmsgFilter(false);
277 }
278 NavmsgFilter filter;
279 filter.m_name = root["filter"]["name"].AsString();
280 filter.m_description = root["filter"]["description"].AsString();
281 if (root["filter"].HasMember("buses"))
282 ParseBuses(filter, root["filter"]["buses"]);
283 if (root["filter"].HasMember("accepted"))
284 ParseAccepted(filter, root["filter"]["accepted"]);
285 if (root["filter"].HasMember("status"))
286 ParseStatus(filter, root["filter"]["status"]);
287 if (root["filter"].HasMember("directions"))
288 ParseDirections(filter, root["filter"]["directions"]);
289 if (root["filter"].HasMember("msgFilter"))
290 ParseMsgFilter(filter, root["filter"]["msgFilter"]);
291 if (root["filter"].HasMember("interfaces"))
292 ParseInterfaces(filter, root["filter"]["interfaces"]);
293 if (root["filter"].HasMember("pgns"))
294 ParsePgn(filter, root["filter"]["pgns"]);
295 if (root["filter"].HasMember("src_pgns"))
296 ParseSource(filter, root["filter"]["src_pgns"]);
297 return filter;
298}
299
300std::string NavmsgFilter::to_string() const {
301 wxJSONValue root;
302 root["filter"]["name"] = m_name;
303 root["filter"]["description"] = m_description;
304 wxJSONValue& filter = root["filter"];
305
306 if (!buses.empty()) {
307 for (auto b : buses) filter["buses"].Append(NavAddr::BusToString(b));
308 };
309 if (!directions.empty()) {
310 for (auto d : directions) filter["directions"].Append(DirectionToString(d));
311 };
312 if (!accepted.empty()) {
313 for (auto a : accepted) filter["accepted"].Append(AcceptedToString(a));
314 };
315 if (!status.empty()) {
316 for (auto s : status) filter["status"].Append(StateToString(s));
317 };
318 if (!include_msg.empty()) {
319 for (auto m : include_msg) filter["msgFilter"]["allowedMsg"].Append(m);
320 } else if (!exclude_msg.empty()) {
321 for (auto m : exclude_msg) filter["msgFilter"]["blockedMsg"].Append(m);
322 }
323 if (!interfaces.empty()) {
324 for (auto i : interfaces) filter["interfaces"].Append(i);
325 }
326 if (!pgns.empty()) {
327 for (auto p : pgns) filter["pgns"].Append(p.to_string());
328 }
329 if (!src_pgns.empty()) {
330 for (auto p : src_pgns) filter["src_pgns"].Append(p.to_string());
331 }
332 wxJSONWriter writer;
333 wxString ws;
334 writer.Write(root, ws);
335 return ws.ToStdString();
336}
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.