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 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 wxString& message_id, wxJSONValue v) {
148 wxJSONWriter w(wxJSONWRITER_NO_LINEFEEDS | wxJSONWRITER_STYLED);
149 wxString out;
150 w.Write(v, out);
151 auto msg =
152 std::make_shared<PluginMsg>(message_id.ToStdString(), out.ToStdString());
153 SendMessageToAllPlugins(message_id, out);
154 wxLogDebug(message_id);
155 wxLogDebug(out);
156 LogMessage(msg, "Json message ");
157}
158
159void SendAISSentenceToAllPlugIns(const wxString& sentence) {
160 // decouple 'const wxString &' to keep interface.
161 wxString decouple_sentence(sentence);
162 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
163 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
164 PlugInContainer* pic = plugin_array->Item(i);
165 if (pic->m_enabled && pic->m_init_state) {
167 pic->m_pplugin->SetAISSentence(decouple_sentence);
168 }
169 }
170 auto msg =
171 std::make_shared<PluginMsg>("AIS", JoinLines(sentence.ToStdString()));
172 LogMessage(msg, "AIS data ");
173}
174
175void SendPositionFixToAllPlugIns(GenericPosDatEx* ppos) {
176 // Send basic position fix
178 pfix.Lat = ppos->kLat;
179 pfix.Lon = ppos->kLon;
180 pfix.Cog = ppos->kCog;
181 pfix.Sog = ppos->kSog;
182 pfix.Var = ppos->kVar;
183 pfix.FixTime = ppos->FixTime;
184 pfix.nSats = ppos->nSats;
185
186 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
187 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
188 PlugInContainer* pic = plugin_array->Item(i);
189 if (pic->m_enabled && pic->m_init_state) {
190 if (pic->m_cap_flag & WANTS_NMEA_EVENTS)
191 if (pic->m_pplugin) pic->m_pplugin->SetPositionFix(pfix);
192 }
193 }
194
195 // Send extended position fix to PlugIns at API 108 and later
197 pfix_ex.Lat = ppos->kLat;
198 pfix_ex.Lon = ppos->kLon;
199 pfix_ex.Cog = ppos->kCog;
200 pfix_ex.Sog = ppos->kSog;
201 pfix_ex.Var = ppos->kVar;
202 pfix_ex.FixTime = ppos->FixTime;
203 pfix_ex.nSats = ppos->nSats;
204 pfix_ex.Hdt = ppos->kHdt;
205 pfix_ex.Hdm = ppos->kHdm;
206
207 auto msg = std::make_shared<PluginMsg>("position-fix", MsgToString(pfix));
208 LogMessage(msg, "application ALL gnss-fix ");
209
210 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
211 PlugInContainer* pic = plugin_array->Item(i);
212 if (pic->m_enabled && pic->m_init_state) {
213 if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
214 switch (pic->m_api_version) {
215 case 108:
216 case 109:
217 case 110:
218 case 111:
219 case 112:
220 case 113:
221 case 114:
222 case 115:
223 case 116:
224 case 117:
225 case 118:
226 case 119:
227 case 120: {
228 auto* ppi = dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
229 if (ppi) ppi->SetPositionFixEx(pfix_ex);
230 break;
231 }
232 default:
233 break;
234 }
235 }
236 }
237 }
238}
239
240void SendActiveLegInfoToAllPlugIns(const ActiveLegDat* leg_info) {
242 leg.Btw = leg_info->Btw;
243 leg.Dtw = leg_info->Dtw;
244 leg.wp_name = leg_info->wp_name;
245 leg.Xte = leg_info->Xte;
246 leg.arrival = leg_info->arrival;
247 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
248 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
249 PlugInContainer* pic = plugin_array->Item(i);
250 if (pic->m_enabled && pic->m_init_state) {
251 if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
252 switch (pic->m_api_version) {
253 case 108:
254 case 109:
255 case 110:
256 case 111:
257 case 112:
258 case 113:
259 case 114:
260 case 115:
261 case 116:
262 break;
263 case 117:
264 case 118:
265 case 119:
266 case 120: {
267 auto* ppi = dynamic_cast<opencpn_plugin_117*>(pic->m_pplugin);
268 if (ppi) ppi->SetActiveLegInfo(leg);
269 break;
270 }
271 default:
272 break;
273 }
274 }
275 }
276 }
277}
278
279bool SendMouseEventToPlugins(wxMouseEvent& event) {
280 bool bret = false;
281 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
282 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
283 PlugInContainer* pic = plugin_array->Item(i);
284 if (pic->m_enabled && pic->m_init_state) {
285 if (pic->m_cap_flag & WANTS_MOUSE_EVENTS) {
286 switch (pic->m_api_version) {
287 case 112:
288 case 113:
289 case 114:
290 case 115:
291 case 116:
292 case 117:
293 case 118:
294 case 119:
295 case 120: {
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 auto* ppi = dynamic_cast<opencpn_plugin_113*>(pic->m_pplugin);
327 if (ppi && ppi->KeyboardEventHook(event)) bret = true;
328 break;
329 }
330 default:
331 break;
332 }
333 }
334 }
335 }
336 }
337
338 return bret;
339}
340
341void SendPreShutdownHookToPlugins() {
342 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
343 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
344 PlugInContainer* pic = plugin_array->Item(i);
345 if (pic->m_enabled && pic->m_init_state) {
347 switch (pic->m_api_version) {
348 case 119:
349 case 120: {
350 auto* ppi = dynamic_cast<opencpn_plugin_119*>(pic->m_pplugin);
351 if (ppi) ppi->PreShutdownHook();
352 break;
353 }
354 default:
355 break;
356 }
357 }
358 }
359 }
360}
361
362void SendCursorLatLonToAllPlugIns(double lat, double lon) {
363 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
364 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
365 PlugInContainer* pic = plugin_array->Item(i);
366 if (pic->m_enabled && pic->m_init_state) {
368 if (pic->m_pplugin) pic->m_pplugin->SetCursorLatLon(lat, lon);
369 }
370 }
371 auto msg = std::make_shared<PluginMsg>(
372 PluginMsg("Cursor-pos", Position(lat, lon).to_string()));
373 LogMessage(msg, "application ALL cursor-pos ");
374}
375
376void SendNMEASentenceToAllPlugIns(const wxString& sentence) {
377 // decouple 'const wxString &' to keep plugin interface.
378 wxString decouple_sentence(sentence);
379#ifndef __WXMSW__
380 // Set up a framework to catch (some) sigsegv faults from plugins.
381 sigaction(SIGSEGV, NULL, &sa_all_PIM_previous); // save existing
382 // action for this signal
383 struct sigaction temp;
384 sigaction(SIGSEGV, NULL, &temp); // inspect existing action for this signal
385
386 temp.sa_handler = catch_signals_PIM; // point to my handler
387 sigemptyset(&temp.sa_mask); // make the blocking set
388 // empty, so that all
389 // other signals will be
390 // unblocked during my handler
391 temp.sa_flags = 0;
392 sigaction(SIGSEGV, &temp, NULL);
393#endif
394 auto msg = std::make_shared<PluginMsg>("NMEA-msg", sentence.ToStdString());
395 LogMessage(msg, "internal ALL nmea-msg ");
396 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
397 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
398 PlugInContainer* pic = plugin_array->Item(i);
399 if (pic->m_enabled && pic->m_init_state) {
400 if (pic->m_cap_flag & WANTS_NMEA_SENTENCES) {
401#ifndef __WXMSW__
402 if (sigsetjmp(env_PIM, 1)) {
403 // Something in the "else" code block faulted.
404 // Probably safest to assume that all variables in this method are
405 // trash... So, simply clean up and return.
406 sigaction(SIGSEGV, &sa_all_PIM_previous, NULL);
407 // reset signal handler
408 return;
409 } else
410#endif
411 {
412 // volatile int *x = 0;
413 //*x = 0;
414 if (pic->m_pplugin)
415 pic->m_pplugin->SetNMEASentence(decouple_sentence);
416 }
417 }
418 }
419 }
420#ifndef __WXMSW__
421 sigaction(SIGSEGV, &sa_all_PIM_previous, NULL); // reset signal handler
422#endif
423}
424
425int GetJSONMessageTargetCount() {
426 int rv = 0;
427 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
428 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
429 PlugInContainer* pic = plugin_array->Item(i);
430 if (pic->m_enabled && pic->m_init_state &&
432 rv++;
433 }
434 return rv;
435}
436
437void SendVectorChartObjectInfo(const wxString& chart, const wxString& feature,
438 const wxString& objname, double& lat,
439 double& lon, double& scale, int& nativescale) {
440 wxString decouple_chart(chart);
441 wxString decouple_feature(feature);
442 wxString decouple_objname(objname);
443 auto plugin_array = PluginLoader::GetInstance()->GetPlugInArray();
444 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
445 PlugInContainer* pic = (*plugin_array)[i];
446 if (pic->m_enabled && pic->m_init_state) {
448 switch (pic->m_api_version) {
449 case 112:
450 case 113:
451 case 114:
452 case 115:
453 case 116:
454 case 117:
455 case 118:
456 case 119:
457 case 120: {
458 auto* ppi = dynamic_cast<opencpn_plugin_112*>(pic->m_pplugin);
459 if (ppi)
460 ppi->SendVectorChartObjectInfo(decouple_chart, decouple_feature,
461 decouple_objname, lat, lon, scale,
462 nativescale);
463 break;
464 }
465 default:
466 break;
467 }
468 }
469 }
470 }
471}
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.
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.
double Lat
Latitude in decimal degrees.
double Hdm
Heading magnetic in degrees.
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.
Basic position fix information.
double Cog
Course over ground in degrees.
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.
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 begins shutdown sequence.
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.
virtual void SetPositionFix(PlugIn_Position_Fix &pfix)
Updates plugin with current position fix data.
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.
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:90
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:10