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 {"received", Direction::kReceived},
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 for (auto kv : dir_map)
85 if (kv.first == s) return kv.second;
86 return NavmsgStatus::Direction::kNone;
87}
88
89static std::string DirectionToString(Direction direction) {
90 for (auto kv : dir_map)
91 if (kv.second == direction) return kv.first;
92 assert(false && "Illegal direction enumeration type (!)");
93 return ""; // for the compiler
94}
95
96static Accepted StringToAccepted(const std::string s) {
97 for (auto kv : acceptmap)
98 if (kv.first == s) return kv.second;
99 return Accepted::kNone;
100}
101
102static std::string AcceptedToString(Accepted accept) {
103 for (auto kv : acceptmap)
104 if (kv.second == accept) return kv.first;
105 assert(false && "Illegal direction enumeration type (!)");
106 return ""; // for the compiler
107}
108
109static State StringToState(const std::string s) {
110 for (auto kv : statemap)
111 if (kv.first == s) return kv.second;
112 return State::kNone;
113}
114
115static std::string StateToString(State state) {
116 for (auto kv : statemap)
117 if (kv.second == state) return kv.first;
118 assert(false && "Illegal direction enumeration type (!)");
119 return ""; // for the compiler
120}
121
122static void ParseBuses(NavmsgFilter& filter, wxJSONValue json_val) {
123 for (int i = 0; i < json_val.Size(); i++) {
124 auto str = json_val[i].AsString().ToStdString();
125 filter.buses.insert(NavAddr::StringToBus(str));
126 }
127}
128
129static void ParseDirections(NavmsgFilter& filter, wxJSONValue json_val) {
130 for (int i = 0; i < json_val.Size(); i++) {
131 auto str = json_val[i].AsString().ToStdString();
132 filter.directions.insert(StringToDirection(str));
133 }
134}
135
136static void ParseAccepted(NavmsgFilter& filter, wxJSONValue json_val) {
137 for (int i = 0; i < json_val.Size(); i++) {
138 auto str = json_val[i].AsString().ToStdString();
139 filter.accepted.insert(StringToAccepted(str));
140 }
141}
142
143static void ParseStatus(NavmsgFilter& filter, wxJSONValue json_val) {
144 for (int i = 0; i < json_val.Size(); i++) {
145 auto str = json_val[i].AsString().ToStdString();
146 filter.status.insert(StringToState(str));
147 }
148}
149
150static void ParseMsgFilter(NavmsgFilter& filter, wxJSONValue json_val) {
151 if (json_val.HasMember("blockedMsg")) {
152 auto val = json_val["blockedMsg"];
153 for (int i = 0; i < val.Size(); i++)
154 filter.exclude_msg.insert(val[i].AsString().ToStdString());
155 } else if (json_val.HasMember("allowedMsg")) {
156 auto val = json_val["allowedMsg"];
157 for (int i = 0; i < val.Size(); i++)
158 filter.include_msg.insert(val[i].AsString().ToStdString());
159 }
160}
161
162static void ParseInterfaces(NavmsgFilter& filter, wxJSONValue json_val) {
163 for (int i = 0; i < json_val.Size(); i++)
164 filter.interfaces.insert(json_val[i].AsString().ToStdString());
165}
166
167static void ParsePgn(NavmsgFilter& filter, wxJSONValue json_val) {
168 try {
169 for (int i = 0; i < json_val.Size(); i++)
170 filter.pgns.insert(std::stoi(json_val[i].AsString().ToStdString()));
171 } catch (...) {
172 return;
173 }
174}
175
176static void ParseSource(NavmsgFilter& filter, wxJSONValue json_val) {
177 try {
178 for (int i = 0; i < json_val.Size(); i++) {
179 filter.src_pgns.insert(std::stoi(json_val[i].AsString().ToStdString()));
180 }
181 } catch (...) {
182 return;
183 }
184}
185
186std::vector<NavmsgFilter> NavmsgFilter::GetFilters(const fs::path& dir) {
187 std::vector<NavmsgFilter> filters;
188 try {
189 for (auto& entry : fs::directory_iterator(dir)) {
190 auto filter = NavmsgFilter::Parse(entry);
191 if (filter.m_is_valid)
192 filters.push_back(filter);
193 else
194 wxLogWarning("Illegal system filter: %s",
195 entry.path().string().c_str());
196 }
197 } catch (...) {
198 wxLogWarning("Bad system filter path: %s", dir.string().c_str());
199 }
200 return filters;
201}
202
203std::vector<NavmsgFilter> NavmsgFilter::GetSystemFilters() {
204 fs::path dirpath(g_BasePlatform->GetSharedDataDir().ToStdString());
205 return NavmsgFilter::GetFilters(dirpath / "filters");
206}
207
208std::vector<NavmsgFilter> GetUserFilters() {
209 fs::path dirpath(g_BasePlatform->GetPrivateDataDir().ToStdString());
210 return NavmsgFilter::GetFilters(dirpath / "filters");
211}
212
213std::vector<NavmsgFilter> NavmsgFilter::GetAllFilters() {
214 std::vector<NavmsgFilter> filters = GetSystemFilters();
215 std::vector user_filters = GetUserFilters();
216 filters.insert(filters.end(), user_filters.begin(), user_filters.end());
217 return filters;
218}
219
221 const std::shared_ptr<const NavMsg>& msg) {
222 if (directions.size() > 0) {
223 if (directions.find(msg_status.direction) == directions.end()) return false;
224 }
225 if (status.size() > 0) {
226 if (status.find(msg_status.status) == status.end()) return false;
227 }
228 if (buses.size() > 0) {
229 if (buses.find(msg->bus) == buses.end()) return false;
230 }
231 if (include_msg.size() > 0) {
232 if (include_msg.find(msg->key()) == include_msg.end()) return false;
233 }
234 if (exclude_msg.size() > 0) {
235 if (exclude_msg.find(msg->key()) != exclude_msg.end()) return false;
236 }
237 if (interfaces.size() > 0) {
238 if (interfaces.find(msg->source->iface) == interfaces.end()) return false;
239 }
240 if (accepted.size() > 0) {
241 if (accepted.find(msg_status.accepted) == accepted.end()) return false;
242 }
243 auto n2k_msg = std::dynamic_pointer_cast<const Nmea2000Msg>(msg);
244 if (n2k_msg) {
245 if (pgns.size() > 0) {
246 if (pgns.find(n2k_msg->PGN) == pgns.end()) return false;
247 }
248 if (src_pgns.size() > 0) {
249 auto src = std::static_pointer_cast<const NavAddr2000>(msg->source);
250 if (src && src_pgns.find(src->name) == src_pgns.end()) return false;
251 }
252 }
253 return true;
254}
255
256NavmsgFilter NavmsgFilter::Parse(const fs::path& path) {
257 std::ifstream stream(path);
258 if (!stream.is_open()) return NavmsgFilter(false);
259 std::stringstream ss;
260 ss << stream.rdbuf();
261 return NavmsgFilter::Parse(ss.str());
262}
263
264NavmsgFilter NavmsgFilter::Parse(const std::string& string) {
265 wxJSONValue root;
266 wxJSONReader reader;
267 int err_count = reader.Parse(string, &root);
268 if (err_count > 0) {
269 wxLogWarning("Error parsing filter XML");
270 for (auto& e : reader.GetErrors())
271 wxLogWarning("Parse error: %s", e.c_str());
272 return NavmsgFilter(false);
273 }
274 NavmsgFilter filter;
275 filter.m_name = root["filter"]["name"].AsString();
276 filter.m_description = root["filter"]["description"].AsString();
277 if (root["filter"].HasMember("buses"))
278 ParseBuses(filter, root["filter"]["buses"]);
279 if (root["filter"].HasMember("accepted"))
280 ParseAccepted(filter, root["filter"]["accepted"]);
281 if (root["filter"].HasMember("status"))
282 ParseStatus(filter, root["filter"]["status"]);
283 if (root["filter"].HasMember("directions"))
284 ParseDirections(filter, root["filter"]["directions"]);
285 if (root["filter"].HasMember("msgFilter"))
286 ParseMsgFilter(filter, root["filter"]["msgFilter"]);
287 if (root["filter"].HasMember("interfaces"))
288 ParseInterfaces(filter, root["filter"]["interfaces"]);
289 if (root["filter"].HasMember("pgns"))
290 ParsePgn(filter, root["filter"]["pgns"]);
291 if (root["filter"].HasMember("src_pgns"))
292 ParseSource(filter, root["filter"]["src_pgns"]);
293 return filter;
294}
295
296std::string NavmsgFilter::to_string() const {
297 wxJSONValue root;
298 root["filter"]["name"] = m_name;
299 root["filter"]["description"] = m_description;
300 wxJSONValue& filter = root["filter"];
301
302 if (!buses.empty()) {
303 for (auto b : buses) filter["buses"].Append(NavAddr::BusToString(b));
304 };
305 if (!directions.empty()) {
306 for (auto d : directions) filter["directions"].Append(DirectionToString(d));
307 };
308 if (!accepted.empty()) {
309 for (auto a : accepted) filter["accepted"].Append(AcceptedToString(a));
310 };
311 if (!status.empty()) {
312 for (auto s : status) filter["status"].Append(StateToString(s));
313 };
314 if (!include_msg.empty()) {
315 for (auto m : include_msg) filter["msgFilter"]["allowedMsg"].Append(m);
316 } else if (!exclude_msg.empty()) {
317 for (auto m : exclude_msg) filter["msgFilter"]["blockedMsg"].Append(m);
318 }
319 if (!interfaces.empty()) {
320 for (auto i : interfaces) filter["interfaces"].Append(i);
321 }
322 if (!pgns.empty()) {
323 for (auto p : pgns) filter["pgns"].Append(p.to_string());
324 }
325 if (!src_pgns.empty()) {
326 for (auto p : src_pgns) filter["src_pgns"].Append(p.to_string());
327 }
328 wxJSONWriter writer;
329 wxString ws;
330 writer.Write(root, ws);
331 return ws.ToStdString();
332}
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.