OpenCPN Partial API docs
Loading...
Searching...
No Matches
plugin_comm.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2022 by David Register *
3 * Copyright (C) 2022 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, see <https://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
25#include <setjmp.h>
26
27#include <wx/event.h>
28#include <wx/jsonval.h>
29#include <wx/jsonreader.h>
30#include <wx/jsonwriter.h>
31
32#include "model/comm_appmsg.h"
34#include "model/gui.h"
35#include "model/nmea_log.h"
36#include "model/plugin_comm.h"
37#include "model/plugin_loader.h"
38#include "model/ocpn_utils.h"
39
40#include "ocpn_plugin.h"
41
42#ifndef _WIN32
43
44static struct sigaction sa_all_PIM_previous;
45static sigjmp_buf env_PIM;
46
47static void catch_signals_PIM(int signo) {
48 switch (signo) {
49 case SIGSEGV:
50 siglongjmp(env_PIM, 1); // jump back to the setjmp() point
51 break;
52
53 default:
54 break;
55 }
56}
57
58#endif
59
60static std::string PosItem(const std::string what, double item) {
61 std::stringstream ss;
62 ss << " " << what << " " << std::setprecision(3) << item;
63 return ss.str();
64}
65
66static std::string MsgToString(PlugIn_Position_Fix fix) {
67 std::stringstream ss;
68 ss << Position(fix.Lat, fix.Lon).to_string() << " " << PosItem("Cog", fix.Cog)
69 << PosItem("Sog", fix.Sog) << " " << PosItem("Var", fix.Var)
70 << " Nsats: " << fix.nSats;
71 return ss.str();
72}
73
74static std::string JoinLines(const std::string lines) {
75 std::istringstream is(lines);
76 std::string line;
77 std::string output;
78 while (std::getline(is, line)) output += line + " ";
79 return output.substr(0, output.size() - 1);
80}
81
82static void LogMessage(const std::shared_ptr<const NavMsg>& message,
83 const std::string prefix = "") {
84 auto w = wxWindow::FindWindowByName(kDataMonitorWindowName);
85 auto log = dynamic_cast<NmeaLog*>(w);
86 if (log) {
87 NavmsgStatus ns;
88 ns.direction = NavmsgStatus::Direction::kInternal;
89 Logline ll(message, ns);
90 ll.prefix = prefix;
91 log->Add(ll);
92 }
93}
94
95void SendMessageToAllPlugins(const std::string& message_id,
96 const std::string& message_body) {
97 auto msg = std::make_shared<PluginMsg>(PluginMsg(message_id, message_body));
98 NavMsgBus::GetInstance().Notify(msg);
99
100 // decouple 'const wxString &' and 'wxString &' to keep API
101 wxString id(message_id);
102 wxString body(message_body);
103
104 LogMessage(msg);
105 // LogMessage(std::string("internal ALL ") + msg->to_string()); FIXME/leamas
106
107 for (auto pic : *PluginLoader::GetInstance()->GetPlugInArray()) {
108 if (pic->m_enabled && pic->m_init_state) {
109 if (pic->m_cap_flag & WANTS_PLUGIN_MESSAGING) {
110 switch (pic->m_api_version) {
111 case 106: {
112 auto* ppi = dynamic_cast<opencpn_plugin_16*>(pic->m_pplugin);
113 if (ppi) ppi->SetPluginMessage(id, body);
114 break;
115 }
116 case 107: {
117 auto* ppi = dynamic_cast<opencpn_plugin_17*>(pic->m_pplugin);
118 if (ppi) ppi->SetPluginMessage(id, body);
119 break;
120 }
121 case 108:
122 case 109:
123 case 110:
124 case 111:
125 case 112:
126 case 113:
127 case 114:
128 case 115:
129 case 116:
130 case 117:
131 case 118:
132 case 119:
133 case 120:
134 case 121: {
135 auto* ppi = dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
136 if (ppi) ppi->SetPluginMessage(id, body);
137 break;
138 }
139 default:
140 break;
141 }
142 }
143 }
144 }
145}
146
147void SendJsonMessageToAllPlugins(const std::string& message_id,
148 const nlohmann::json& v) {
149 std::string out = v.dump();
150 auto msg = std::make_shared<PluginMsg>(message_id, out);
151 SendMessageToAllPlugins(message_id, out);
152 wxLogDebug(wxString(message_id) + " " + out);
153 LogMessage(msg, "Json message ");
154}
155
156void SendAISSentenceToAllPlugIns(const wxString& sentence) {
157 // decouple 'const wxString &' to keep interface.
158 wxString decouple_sentence(sentence);
159 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
160 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
161 PlugInContainer* pic = plugin_array->Item(i);
162 if (pic->m_enabled && pic->m_init_state) {
164 pic->m_pplugin->SetAISSentence(decouple_sentence);
165 }
166 }
167 auto msg =
168 std::make_shared<PluginMsg>("AIS", JoinLines(sentence.ToStdString()));
169 LogMessage(msg, "AIS data ");
170}
171
172void SendPositionFixToAllPlugIns(GenericPosDatEx* ppos) {
173 // Send basic position fix
175 pfix.Lat = ppos->kLat;
176 pfix.Lon = ppos->kLon;
177 pfix.Cog = ppos->kCog;
178 pfix.Sog = ppos->kSog;
179 pfix.Var = ppos->kVar;
180 pfix.FixTime = ppos->FixTime;
181 pfix.nSats = ppos->nSats;
182
183 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
184 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
185 PlugInContainer* pic = plugin_array->Item(i);
186 if (pic->m_enabled && pic->m_init_state) {
187 if (pic->m_cap_flag & WANTS_NMEA_EVENTS)
188 if (pic->m_pplugin) pic->m_pplugin->SetPositionFix(pfix);
189 }
190 }
191
192 // Send extended position fix to PlugIns at API 108 and later
194 pfix_ex.Lat = ppos->kLat;
195 pfix_ex.Lon = ppos->kLon;
196 pfix_ex.Cog = ppos->kCog;
197 pfix_ex.Sog = ppos->kSog;
198 pfix_ex.Var = ppos->kVar;
199 pfix_ex.FixTime = ppos->FixTime;
200 pfix_ex.nSats = ppos->nSats;
201 pfix_ex.Hdt = ppos->kHdt;
202 pfix_ex.Hdm = ppos->kHdm;
203
204 auto msg = std::make_shared<PluginMsg>("position-fix", MsgToString(pfix));
205 LogMessage(msg, "application ALL gnss-fix ");
206
207 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
208 PlugInContainer* pic = plugin_array->Item(i);
209 if (pic->m_enabled && pic->m_init_state) {
210 if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
211 switch (pic->m_api_version) {
212 case 108:
213 case 109:
214 case 110:
215 case 111:
216 case 112:
217 case 113:
218 case 114:
219 case 115:
220 case 116:
221 case 117:
222 case 118:
223 case 119:
224 case 120:
225 case 121: {
226 auto* ppi = dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
227 if (ppi) ppi->SetPositionFixEx(pfix_ex);
228 break;
229 }
230 default:
231 break;
232 }
233 }
234 }
235 }
236}
237
238void SendActiveLegInfoToAllPlugIns(const ActiveLegDat* leg_info) {
240 leg.Btw = leg_info->Btw;
241 leg.Dtw = leg_info->Dtw;
242 leg.wp_name = leg_info->wp_name;
243 leg.Xte = leg_info->Xte;
244 leg.arrival = leg_info->arrival;
245 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
246 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
247 PlugInContainer* pic = plugin_array->Item(i);
248 if (pic->m_enabled && pic->m_init_state) {
249 if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
250 switch (pic->m_api_version) {
251 case 108:
252 case 109:
253 case 110:
254 case 111:
255 case 112:
256 case 113:
257 case 114:
258 case 115:
259 case 116:
260 break;
261 case 117:
262 case 118:
263 case 119:
264 case 120:
265 case 121: {
266 auto* ppi = dynamic_cast<opencpn_plugin_117*>(pic->m_pplugin);
267 if (ppi) ppi->SetActiveLegInfo(leg);
268 break;
269 }
270 default:
271 break;
272 }
273 }
274 }
275 }
276}
277
278bool SendMouseEventToPlugins(wxMouseEvent& event) {
279 bool bret = false;
280 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
281 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
282 PlugInContainer* pic = plugin_array->Item(i);
283 if (pic->m_enabled && pic->m_init_state) {
284 if (pic->m_cap_flag & WANTS_MOUSE_EVENTS) {
285 switch (pic->m_api_version) {
286 case 112:
287 case 113:
288 case 114:
289 case 115:
290 case 116:
291 case 117:
292 case 118:
293 case 119:
294 case 120:
295 case 121: {
296 auto* ppi = dynamic_cast<opencpn_plugin_112*>(pic->m_pplugin);
297 if (ppi && ppi->MouseEventHook(event)) bret = true;
298 break;
299 }
300 default:
301 break;
302 }
303 }
304 }
305 }
306 return bret;
307}
308
309bool SendKeyEventToPlugins(wxKeyEvent& event) {
310 bool bret = false;
311 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
312 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
313 PlugInContainer* pic = plugin_array->Item(i);
314 if (pic->m_enabled && pic->m_init_state) {
316 {
317 switch (pic->m_api_version) {
318 case 113:
319 case 114:
320 case 115:
321 case 116:
322 case 117:
323 case 118:
324 case 119:
325 case 120:
326 case 121: {
327 auto* ppi = dynamic_cast<opencpn_plugin_113*>(pic->m_pplugin);
328 if (ppi && ppi->KeyboardEventHook(event)) bret = true;
329 break;
330 }
331 default:
332 break;
333 }
334 }
335 }
336 }
337 }
338
339 return bret;
340}
341
342void SendPreShutdownHookToPlugins() {
343 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
344 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
345 PlugInContainer* pic = plugin_array->Item(i);
346 if (pic->m_enabled && pic->m_init_state) {
348 switch (pic->m_api_version) {
349 case 119:
350 case 120:
351 case 121: {
352 auto* ppi = dynamic_cast<opencpn_plugin_119*>(pic->m_pplugin);
353 if (ppi) ppi->PreShutdownHook();
354 break;
355 }
356 default:
357 break;
358 }
359 }
360 }
361 }
362}
363
364void SendCursorLatLonToAllPlugIns(double lat, double lon) {
365 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
366 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
367 PlugInContainer* pic = plugin_array->Item(i);
368 if (pic->m_enabled && pic->m_init_state) {
370 if (pic->m_pplugin) pic->m_pplugin->SetCursorLatLon(lat, lon);
371 }
372 }
373 auto msg = std::make_shared<PluginMsg>(
374 PluginMsg("Cursor-pos", Position(lat, lon).to_string()));
375 LogMessage(msg, "application ALL cursor-pos ");
376}
377
378void SendNMEASentenceToAllPlugIns(const wxString& sentence) {
379 // decouple 'const wxString &' to keep plugin interface.
380 wxString decouple_sentence(sentence);
381#ifndef __WXMSW__
382 // Set up a framework to catch (some) sigsegv faults from plugins.
383 sigaction(SIGSEGV, NULL, &sa_all_PIM_previous); // save existing
384 // action for this signal
385 struct sigaction temp;
386 sigaction(SIGSEGV, NULL, &temp); // inspect existing action for this signal
387
388 temp.sa_handler = catch_signals_PIM; // point to my handler
389 sigemptyset(&temp.sa_mask); // make the blocking set
390 // empty, so that all
391 // other signals will be
392 // unblocked during my handler
393 temp.sa_flags = 0;
394 sigaction(SIGSEGV, &temp, NULL);
395#endif
396 auto msg = std::make_shared<PluginMsg>("NMEA-msg", sentence.ToStdString());
397 LogMessage(msg, "internal ALL nmea-msg ");
398 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
399 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
400 PlugInContainer* pic = plugin_array->Item(i);
401 if (pic->m_enabled && pic->m_init_state) {
402 if (pic->m_cap_flag & WANTS_NMEA_SENTENCES) {
403#ifndef __WXMSW__
404 if (sigsetjmp(env_PIM, 1)) {
405 // Something in the "else" code block faulted.
406 // Probably safest to assume that all variables in this method are
407 // trash... So, simply clean up and return.
408 sigaction(SIGSEGV, &sa_all_PIM_previous, NULL);
409 // reset signal handler
410 return;
411 } else
412#endif
413 {
414 // volatile int *x = 0;
415 //*x = 0;
416 if (pic->m_pplugin)
417 pic->m_pplugin->SetNMEASentence(decouple_sentence);
418 }
419 }
420 }
421 }
422#ifndef __WXMSW__
423 sigaction(SIGSEGV, &sa_all_PIM_previous, NULL); // reset signal handler
424#endif
425}
426
427int GetJSONMessageTargetCount() {
428 int rv = 0;
429 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
430 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
431 PlugInContainer* pic = plugin_array->Item(i);
432 if (pic->m_enabled && pic->m_init_state &&
434 rv++;
435 }
436 return rv;
437}
438
439void SendVectorChartObjectInfo(const wxString& chart, const wxString& feature,
440 const wxString& objname, double& lat,
441 double& lon, double& scale, int& nativescale) {
442 wxString decouple_chart(chart);
443 wxString decouple_feature(feature);
444 wxString decouple_objname(objname);
445 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
446 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
447 PlugInContainer* pic = (*plugin_array)[i];
448 if (pic->m_enabled && pic->m_init_state) {
450 switch (pic->m_api_version) {
451 case 112:
452 case 113:
453 case 114:
454 case 115:
455 case 116:
456 case 117:
457 case 118:
458 case 119:
459 case 120:
460 case 121: {
461 auto* ppi = dynamic_cast<opencpn_plugin_112*>(pic->m_pplugin);
462 if (ppi)
463 ppi->SendVectorChartObjectInfo(decouple_chart, decouple_feature,
464 decouple_objname, lat, lon, scale,
465 nativescale);
466 break;
467 }
468 default:
469 break;
470 }
471 }
472 }
473 }
474}
void Notify(std::shared_ptr< const NavMsg > message)
Accept message received by driver, make it available for upper layers.
Representation of message status as determined by the multiplexer.
NMEA Log Interface.
Definition nmea_log.h:70
Data for a loaded plugin, including dl-loaded library.
int m_cap_flag
PlugIn Capabilities descriptor.
Extended position fix information.
int nSats
Number of satellites used in the fix.
double Var
Magnetic variation in degrees, typically from RMC message.
double Cog
Course over ground in degrees [0-360).
double Lat
Latitude in decimal degrees.
double Hdm
Heading magnetic in degrees [0-360).
time_t FixTime
UTC time of fix.
double Lon
Longitude in decimal degrees.
double Sog
Speed over ground in knots.
double Hdt
Heading true in degrees [0-360).
Basic position fix information.
double Cog
Course over ground in degrees [0-360).
double Sog
Speed over ground in knots.
time_t FixTime
UTC time of fix as time_t value.
double Lat
Latitude in decimal degrees.
int nSats
Number of satellites used in the fix.
double Var
Magnetic variation in degrees, typically from RMC message.
double Lon
Longitude in decimal degrees.
PluginLoader is a backend module without any direct GUI functionality.
const ArrayOfPlugIns * GetPlugInArray()
Return list of currently loaded plugins.
A plugin to plugin json message over the REST interface.
Information about the currently active route leg.
double Dtw
Distance to waypoint in nautical miles.
wxString wp_name
Name of destination waypoint for the active leg.
double Xte
Cross track error in nautical miles, negative values indicate left side of track.
double Btw
Bearing to waypoint in degrees true.
bool arrival
True when vessel is within the arrival circle of the destination waypoint.
std::string to_string() const
Return utf string like 65°25,11N 21°12,01E.
virtual void SendVectorChartObjectInfo(wxString &chart, wxString &feature, wxString &objname, double lat, double lon, double scale, int nativescale)
Receives vector chart object information.
virtual void PreShutdownHook()
Called just before OpenCPN exits.
virtual void SetPluginMessage(wxString &message_id, wxString &message_body)
Receives plugin-to-plugin messages.
virtual void SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix)
Updates plugin with extended position fix data at regular intervals.
virtual void SetPositionFix(PlugIn_Position_Fix &pfix)
Updates plugin with current position fix data at regular intervals.
virtual void SetNMEASentence(wxString &sentence)
Receive all NMEA 0183 sentences from OpenCPN.
virtual void SetAISSentence(wxString &sentence)
Receive all AIS sentences from OpenCPN.
virtual void SetCursorLatLon(double lat, double lon)
Receives cursor lat/lon position updates.
Decoded messages definitions.
Raw messages layer, supports sending and recieving navmsg messages.
Hooks into gui available in model.
Basic DataMonitor logging interface: LogLine (reflects a line in the log) and NmeaLog,...
PlugIn Object Definition/API.
#define WANTS_NMEA_EVENTS
Receive decoded NMEA events with parsed data.
#define WANTS_VECTOR_CHART_OBJECT_INFO
Receive information about vector chart objects.
#define WANTS_AIS_SENTENCES
Receive AIS target information and updates.
#define WANTS_KEYBOARD_EVENTS
Receive keyboard events from main window.
#define WANTS_NMEA_SENTENCES
Receive raw NMEA 0183 sentences from all active ports.
#define WANTS_MOUSE_EVENTS
Receive mouse events (clicks, movement, etc).
#define WANTS_PRESHUTDOWN_HOOK
Receive notification just before OpenCPN shutdown.
#define WANTS_PLUGIN_MESSAGING
Enable message passing between plugins.
#define WANTS_CURSOR_LATLON
Receive updates when cursor moves over chart.
Definition ocpn_plugin.h:95
Miscellaneous utilities, many of which string related.
void SendNMEASentenceToAllPlugIns(const wxString &sentence)
Distribute a NMEA 0183 sentence to all plugins that have registered interest by setting the WANTS_NME...
Tools to send data to plugins.
Low level code to load plugins from disk, notably the PluginLoader class.
A generic position and navigation data structure.
Definition ocpn_types.h:68
double kCog
Course over ground in degrees.
Definition ocpn_types.h:86
double kHdt
True heading in degrees.
Definition ocpn_types.h:111
int nSats
Number of satellites used in the fix.
Definition ocpn_types.h:126
double kHdm
Magnetic heading in degrees.
Definition ocpn_types.h:104
time_t FixTime
UTC time of fix.
Definition ocpn_types.h:118
double kLat
Latitude in decimal degrees.
Definition ocpn_types.h:75
double kSog
Speed over ground in knots.
Definition ocpn_types.h:92
double kVar
Magnetic variation in degrees.
Definition ocpn_types.h:98
double kLon
Longitude in decimal degrees.
Definition ocpn_types.h:83
Item in the log window.
Definition nmea_log.h:32