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, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 **************************************************************************/
20
26#include <setjmp.h>
27
28#include <wx/event.h>
29#include <wx/jsonval.h>
30#include <wx/jsonreader.h>
31#include <wx/jsonwriter.h>
32
33#include "model/comm_appmsg.h"
35#include "model/gui.h"
36#include "model/nmea_log.h"
37#include "model/plugin_comm.h"
38#include "model/plugin_loader.h"
39#include "model/ocpn_utils.h"
40
41#include "ocpn_plugin.h"
42
43#ifndef _WIN32
44
45static struct sigaction sa_all_PIM_previous;
46static sigjmp_buf env_PIM;
47
48static void catch_signals_PIM(int signo) {
49 switch (signo) {
50 case SIGSEGV:
51 siglongjmp(env_PIM, 1); // jump back to the setjmp() point
52 break;
53
54 default:
55 break;
56 }
57}
58
59#endif
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 wxString& message_id,
96 const wxString& message_body) {
97 auto msg = std::make_shared<PluginMsg>(
98 PluginMsg(message_id.ToStdString(), message_body.ToStdString()));
99 NavMsgBus::GetInstance().Notify(msg);
100
101 // decouple 'const wxString &' and 'wxString &' to keep API
102 wxString id(message_id);
103 wxString body(message_body);
104
105 LogMessage(msg);
106 // LogMessage(std::string("internal ALL ") + msg->to_string()); FIXME/leamas
107
108 for (auto pic : *PluginLoader::GetInstance()->GetPlugInArray()) {
109 if (pic->m_enabled && pic->m_init_state) {
110 if (pic->m_cap_flag & WANTS_PLUGIN_MESSAGING) {
111 switch (pic->m_api_version) {
112 case 106: {
113 auto* ppi = dynamic_cast<opencpn_plugin_16*>(pic->m_pplugin);
114 if (ppi) ppi->SetPluginMessage(id, body);
115 break;
116 }
117 case 107: {
118 auto* ppi = dynamic_cast<opencpn_plugin_17*>(pic->m_pplugin);
119 if (ppi) ppi->SetPluginMessage(id, body);
120 break;
121 }
122 case 108:
123 case 109:
124 case 110:
125 case 111:
126 case 112:
127 case 113:
128 case 114:
129 case 115:
130 case 116:
131 case 117:
132 case 118:
133 case 119:
134 case 120:
135 case 121: {
136 auto* ppi = dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
137 if (ppi) ppi->SetPluginMessage(id, body);
138 break;
139 }
140 default:
141 break;
142 }
143 }
144 }
145 }
146}
147
148void SendJSONMessageToAllPlugins(const wxString& message_id, wxJSONValue v) {
149 wxJSONWriter w(wxJSONWRITER_NO_LINEFEEDS | wxJSONWRITER_STYLED);
150 wxString out;
151 w.Write(v, out);
152 auto msg =
153 std::make_shared<PluginMsg>(message_id.ToStdString(), out.ToStdString());
154 SendMessageToAllPlugins(message_id, out);
155 wxLogDebug(message_id);
156 wxLogDebug(out);
157 LogMessage(msg, "Json message ");
158}
159
160void SendAISSentenceToAllPlugIns(const wxString& sentence) {
161 // decouple 'const wxString &' to keep interface.
162 wxString decouple_sentence(sentence);
163 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
164 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
165 PlugInContainer* pic = plugin_array->Item(i);
166 if (pic->m_enabled && pic->m_init_state) {
168 pic->m_pplugin->SetAISSentence(decouple_sentence);
169 }
170 }
171 auto msg =
172 std::make_shared<PluginMsg>("AIS", JoinLines(sentence.ToStdString()));
173 LogMessage(msg, "AIS data ");
174}
175
176void SendPositionFixToAllPlugIns(GenericPosDatEx* ppos) {
177 // Send basic position fix
179 pfix.Lat = ppos->kLat;
180 pfix.Lon = ppos->kLon;
181 pfix.Cog = ppos->kCog;
182 pfix.Sog = ppos->kSog;
183 pfix.Var = ppos->kVar;
184 pfix.FixTime = ppos->FixTime;
185 pfix.nSats = ppos->nSats;
186
187 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
188 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
189 PlugInContainer* pic = plugin_array->Item(i);
190 if (pic->m_enabled && pic->m_init_state) {
191 if (pic->m_cap_flag & WANTS_NMEA_EVENTS)
192 if (pic->m_pplugin) pic->m_pplugin->SetPositionFix(pfix);
193 }
194 }
195
196 // Send extended position fix to PlugIns at API 108 and later
198 pfix_ex.Lat = ppos->kLat;
199 pfix_ex.Lon = ppos->kLon;
200 pfix_ex.Cog = ppos->kCog;
201 pfix_ex.Sog = ppos->kSog;
202 pfix_ex.Var = ppos->kVar;
203 pfix_ex.FixTime = ppos->FixTime;
204 pfix_ex.nSats = ppos->nSats;
205 pfix_ex.Hdt = ppos->kHdt;
206 pfix_ex.Hdm = ppos->kHdm;
207
208 auto msg = std::make_shared<PluginMsg>("position-fix", MsgToString(pfix));
209 LogMessage(msg, "application ALL gnss-fix ");
210
211 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
212 PlugInContainer* pic = plugin_array->Item(i);
213 if (pic->m_enabled && pic->m_init_state) {
214 if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
215 switch (pic->m_api_version) {
216 case 108:
217 case 109:
218 case 110:
219 case 111:
220 case 112:
221 case 113:
222 case 114:
223 case 115:
224 case 116:
225 case 117:
226 case 118:
227 case 119:
228 case 120:
229 case 121: {
230 auto* ppi = dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
231 if (ppi) ppi->SetPositionFixEx(pfix_ex);
232 break;
233 }
234 default:
235 break;
236 }
237 }
238 }
239 }
240}
241
242void SendActiveLegInfoToAllPlugIns(const ActiveLegDat* leg_info) {
244 leg.Btw = leg_info->Btw;
245 leg.Dtw = leg_info->Dtw;
246 leg.wp_name = leg_info->wp_name;
247 leg.Xte = leg_info->Xte;
248 leg.arrival = leg_info->arrival;
249 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
250 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
251 PlugInContainer* pic = plugin_array->Item(i);
252 if (pic->m_enabled && pic->m_init_state) {
253 if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
254 switch (pic->m_api_version) {
255 case 108:
256 case 109:
257 case 110:
258 case 111:
259 case 112:
260 case 113:
261 case 114:
262 case 115:
263 case 116:
264 break;
265 case 117:
266 case 118:
267 case 119:
268 case 120:
269 case 121: {
270 auto* ppi = dynamic_cast<opencpn_plugin_117*>(pic->m_pplugin);
271 if (ppi) ppi->SetActiveLegInfo(leg);
272 break;
273 }
274 default:
275 break;
276 }
277 }
278 }
279 }
280}
281
282bool SendMouseEventToPlugins(wxMouseEvent& event) {
283 bool bret = false;
284 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
285 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
286 PlugInContainer* pic = plugin_array->Item(i);
287 if (pic->m_enabled && pic->m_init_state) {
288 if (pic->m_cap_flag & WANTS_MOUSE_EVENTS) {
289 switch (pic->m_api_version) {
290 case 112:
291 case 113:
292 case 114:
293 case 115:
294 case 116:
295 case 117:
296 case 118:
297 case 119:
298 case 120:
299 case 121: {
300 auto* ppi = dynamic_cast<opencpn_plugin_112*>(pic->m_pplugin);
301 if (ppi && ppi->MouseEventHook(event)) bret = true;
302 break;
303 }
304 default:
305 break;
306 }
307 }
308 }
309 }
310 return bret;
311}
312
313bool SendKeyEventToPlugins(wxKeyEvent& event) {
314 bool bret = false;
315 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
316 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
317 PlugInContainer* pic = plugin_array->Item(i);
318 if (pic->m_enabled && pic->m_init_state) {
320 {
321 switch (pic->m_api_version) {
322 case 113:
323 case 114:
324 case 115:
325 case 116:
326 case 117:
327 case 118:
328 case 119:
329 case 120:
330 case 121: {
331 auto* ppi = dynamic_cast<opencpn_plugin_113*>(pic->m_pplugin);
332 if (ppi && ppi->KeyboardEventHook(event)) bret = true;
333 break;
334 }
335 default:
336 break;
337 }
338 }
339 }
340 }
341 }
342
343 return bret;
344}
345
346void SendPreShutdownHookToPlugins() {
347 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
348 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
349 PlugInContainer* pic = plugin_array->Item(i);
350 if (pic->m_enabled && pic->m_init_state) {
352 switch (pic->m_api_version) {
353 case 119:
354 case 120:
355 case 121: {
356 auto* ppi = dynamic_cast<opencpn_plugin_119*>(pic->m_pplugin);
357 if (ppi) ppi->PreShutdownHook();
358 break;
359 }
360 default:
361 break;
362 }
363 }
364 }
365 }
366}
367
368void SendCursorLatLonToAllPlugIns(double lat, double lon) {
369 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
370 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
371 PlugInContainer* pic = plugin_array->Item(i);
372 if (pic->m_enabled && pic->m_init_state) {
374 if (pic->m_pplugin) pic->m_pplugin->SetCursorLatLon(lat, lon);
375 }
376 }
377 auto msg = std::make_shared<PluginMsg>(
378 PluginMsg("Cursor-pos", Position(lat, lon).to_string()));
379 LogMessage(msg, "application ALL cursor-pos ");
380}
381
382void SendNMEASentenceToAllPlugIns(const wxString& sentence) {
383 // decouple 'const wxString &' to keep plugin interface.
384 wxString decouple_sentence(sentence);
385#ifndef __WXMSW__
386 // Set up a framework to catch (some) sigsegv faults from plugins.
387 sigaction(SIGSEGV, NULL, &sa_all_PIM_previous); // save existing
388 // action for this signal
389 struct sigaction temp;
390 sigaction(SIGSEGV, NULL, &temp); // inspect existing action for this signal
391
392 temp.sa_handler = catch_signals_PIM; // point to my handler
393 sigemptyset(&temp.sa_mask); // make the blocking set
394 // empty, so that all
395 // other signals will be
396 // unblocked during my handler
397 temp.sa_flags = 0;
398 sigaction(SIGSEGV, &temp, NULL);
399#endif
400 auto msg = std::make_shared<PluginMsg>("NMEA-msg", sentence.ToStdString());
401 LogMessage(msg, "internal ALL nmea-msg ");
402 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
403 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
404 PlugInContainer* pic = plugin_array->Item(i);
405 if (pic->m_enabled && pic->m_init_state) {
406 if (pic->m_cap_flag & WANTS_NMEA_SENTENCES) {
407#ifndef __WXMSW__
408 if (sigsetjmp(env_PIM, 1)) {
409 // Something in the "else" code block faulted.
410 // Probably safest to assume that all variables in this method are
411 // trash... So, simply clean up and return.
412 sigaction(SIGSEGV, &sa_all_PIM_previous, NULL);
413 // reset signal handler
414 return;
415 } else
416#endif
417 {
418 // volatile int *x = 0;
419 //*x = 0;
420 if (pic->m_pplugin)
421 pic->m_pplugin->SetNMEASentence(decouple_sentence);
422 }
423 }
424 }
425 }
426#ifndef __WXMSW__
427 sigaction(SIGSEGV, &sa_all_PIM_previous, NULL); // reset signal handler
428#endif
429}
430
431int GetJSONMessageTargetCount() {
432 int rv = 0;
433 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
434 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
435 PlugInContainer* pic = plugin_array->Item(i);
436 if (pic->m_enabled && pic->m_init_state &&
438 rv++;
439 }
440 return rv;
441}
442
443void SendVectorChartObjectInfo(const wxString& chart, const wxString& feature,
444 const wxString& objname, double& lat,
445 double& lon, double& scale, int& nativescale) {
446 wxString decouple_chart(chart);
447 wxString decouple_feature(feature);
448 wxString decouple_objname(objname);
449 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
450 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
451 PlugInContainer* pic = (*plugin_array)[i];
452 if (pic->m_enabled && pic->m_init_state) {
454 switch (pic->m_api_version) {
455 case 112:
456 case 113:
457 case 114:
458 case 115:
459 case 116:
460 case 117:
461 case 118:
462 case 119:
463 case 120:
464 case 121: {
465 auto* ppi = dynamic_cast<opencpn_plugin_112*>(pic->m_pplugin);
466 if (ppi)
467 ppi->SendVectorChartObjectInfo(decouple_chart, decouple_feature,
468 decouple_objname, lat, lon, scale,
469 nativescale);
470 break;
471 }
472 default:
473 break;
474 }
475 }
476 }
477 }
478}
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:72
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.
The JSON value class implementation.
Definition jsonval.h:84
The JSON document writer.
Definition jsonwriter.h:50
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:91
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:74
double kCog
Course over ground in degrees.
Definition ocpn_types.h:92
double kHdt
True heading in degrees.
Definition ocpn_types.h:117
int nSats
Number of satellites used in the fix.
Definition ocpn_types.h:132
double kHdm
Magnetic heading in degrees.
Definition ocpn_types.h:110
time_t FixTime
UTC time of fix.
Definition ocpn_types.h:124
double kLat
Latitude in decimal degrees.
Definition ocpn_types.h:81
double kSog
Speed over ground in knots.
Definition ocpn_types.h:98
double kVar
Magnetic variation in degrees.
Definition ocpn_types.h:104
double kLon
Longitude in decimal degrees.
Definition ocpn_types.h:89
Item in the log window.
Definition nmea_log.h:34