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 auto* ppi = dynamic_cast<opencpn_plugin_18*>(pic->m_pplugin);
135 if (ppi) ppi->SetPluginMessage(id, body);
136 break;
137 }
138 default:
139 break;
140 }
141 }
142 }
143 }
144}
145
146void SendJSONMessageToAllPlugins(const wxString& message_id, wxJSONValue v) {
147 wxJSONWriter w(wxJSONWRITER_NO_LINEFEEDS | wxJSONWRITER_STYLED);
148 wxString out;
149 w.Write(v, out);
150 auto msg =
151 std::make_shared<PluginMsg>(message_id.ToStdString(), out.ToStdString());
152 SendMessageToAllPlugins(message_id, out);
153 wxLogDebug(message_id);
154 wxLogDebug(out);
155 LogMessage(msg, "Json message ");
156}
157
158void SendAISSentenceToAllPlugIns(const wxString& sentence) {
159 // decouple 'const wxString &' to keep interface.
160 wxString decouple_sentence(sentence);
161 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
162 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
163 PlugInContainer* pic = plugin_array->Item(i);
164 if (pic->m_enabled && pic->m_init_state) {
166 pic->m_pplugin->SetAISSentence(decouple_sentence);
167 }
168 }
169 auto msg =
170 std::make_shared<PluginMsg>("AIS", JoinLines(sentence.ToStdString()));
171 LogMessage(msg, "AIS data ");
172}
173
174void SendPositionFixToAllPlugIns(GenericPosDatEx* ppos) {
175 // Send basic position fix
177 pfix.Lat = ppos->kLat;
178 pfix.Lon = ppos->kLon;
179 pfix.Cog = ppos->kCog;
180 pfix.Sog = ppos->kSog;
181 pfix.Var = ppos->kVar;
182 pfix.FixTime = ppos->FixTime;
183 pfix.nSats = ppos->nSats;
184
185 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
186 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
187 PlugInContainer* pic = plugin_array->Item(i);
188 if (pic->m_enabled && pic->m_init_state) {
189 if (pic->m_cap_flag & WANTS_NMEA_EVENTS)
190 if (pic->m_pplugin) pic->m_pplugin->SetPositionFix(pfix);
191 }
192 }
193
194 // Send extended position fix to PlugIns at API 108 and later
196 pfix_ex.Lat = ppos->kLat;
197 pfix_ex.Lon = ppos->kLon;
198 pfix_ex.Cog = ppos->kCog;
199 pfix_ex.Sog = ppos->kSog;
200 pfix_ex.Var = ppos->kVar;
201 pfix_ex.FixTime = ppos->FixTime;
202 pfix_ex.nSats = ppos->nSats;
203 pfix_ex.Hdt = ppos->kHdt;
204 pfix_ex.Hdm = ppos->kHdm;
205
206 auto msg = std::make_shared<PluginMsg>("position-fix", MsgToString(pfix));
207 LogMessage(msg, "application ALL gnss-fix ");
208
209 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
210 PlugInContainer* pic = plugin_array->Item(i);
211 if (pic->m_enabled && pic->m_init_state) {
212 if (pic->m_cap_flag & WANTS_NMEA_EVENTS) {
213 switch (pic->m_api_version) {
214 case 108:
215 case 109:
216 case 110:
217 case 111:
218 case 112:
219 case 113:
220 case 114:
221 case 115:
222 case 116:
223 case 117:
224 case 118:
225 case 119: {
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 auto* ppi = dynamic_cast<opencpn_plugin_117*>(pic->m_pplugin);
265 if (ppi) ppi->SetActiveLegInfo(leg);
266 break;
267 }
268 default:
269 break;
270 }
271 }
272 }
273 }
274}
275
276bool SendMouseEventToPlugins(wxMouseEvent& event) {
277 bool bret = false;
278 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
279 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
280 PlugInContainer* pic = plugin_array->Item(i);
281 if (pic->m_enabled && pic->m_init_state) {
282 if (pic->m_cap_flag & WANTS_MOUSE_EVENTS) {
283 switch (pic->m_api_version) {
284 case 112:
285 case 113:
286 case 114:
287 case 115:
288 case 116:
289 case 117:
290 case 118:
291 case 119: {
292 auto* ppi = dynamic_cast<opencpn_plugin_112*>(pic->m_pplugin);
293 if (ppi && ppi->MouseEventHook(event)) bret = true;
294 break;
295 }
296 default:
297 break;
298 }
299 }
300 }
301 }
302 return bret;
303}
304
305bool SendKeyEventToPlugins(wxKeyEvent& event) {
306 bool bret = false;
307 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
308 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
309 PlugInContainer* pic = plugin_array->Item(i);
310 if (pic->m_enabled && pic->m_init_state) {
312 {
313 switch (pic->m_api_version) {
314 case 113:
315 case 114:
316 case 115:
317 case 116:
318 case 117:
319 case 118:
320 case 119: {
321 auto* ppi = dynamic_cast<opencpn_plugin_113*>(pic->m_pplugin);
322 if (ppi && ppi->KeyboardEventHook(event)) bret = true;
323 break;
324 }
325 default:
326 break;
327 }
328 }
329 }
330 }
331 }
332
333 return bret;
334}
335
336void SendPreShutdownHookToPlugins() {
337 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
338 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
339 PlugInContainer* pic = plugin_array->Item(i);
340 if (pic->m_enabled && pic->m_init_state) {
342 switch (pic->m_api_version) {
343 case 119: {
344 auto* ppi = dynamic_cast<opencpn_plugin_119*>(pic->m_pplugin);
345 if (ppi) ppi->PreShutdownHook();
346 break;
347 }
348 default:
349 break;
350 }
351 }
352 }
353 }
354}
355
356void SendCursorLatLonToAllPlugIns(double lat, double lon) {
357 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
358 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
359 PlugInContainer* pic = plugin_array->Item(i);
360 if (pic->m_enabled && pic->m_init_state) {
362 if (pic->m_pplugin) pic->m_pplugin->SetCursorLatLon(lat, lon);
363 }
364 }
365 auto msg = std::make_shared<PluginMsg>(
366 PluginMsg("Cursor-pos", Position(lat, lon).to_string()));
367 LogMessage(msg, "application ALL cursor-pos ");
368}
369
370void SendNMEASentenceToAllPlugIns(const wxString& sentence) {
371 // decouple 'const wxString &' to keep plugin interface.
372 wxString decouple_sentence(sentence);
373#ifndef __WXMSW__
374 // Set up a framework to catch (some) sigsegv faults from plugins.
375 sigaction(SIGSEGV, NULL, &sa_all_PIM_previous); // save existing
376 // action for this signal
377 struct sigaction temp;
378 sigaction(SIGSEGV, NULL, &temp); // inspect existing action for this signal
379
380 temp.sa_handler = catch_signals_PIM; // point to my handler
381 sigemptyset(&temp.sa_mask); // make the blocking set
382 // empty, so that all
383 // other signals will be
384 // unblocked during my handler
385 temp.sa_flags = 0;
386 sigaction(SIGSEGV, &temp, NULL);
387#endif
388 auto msg = std::make_shared<PluginMsg>("NMEA-msg", sentence.ToStdString());
389 LogMessage(msg, "internal ALL nmea-msg ");
390 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
391 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
392 PlugInContainer* pic = plugin_array->Item(i);
393 if (pic->m_enabled && pic->m_init_state) {
394 if (pic->m_cap_flag & WANTS_NMEA_SENTENCES) {
395#ifndef __WXMSW__
396 if (sigsetjmp(env_PIM, 1)) {
397 // Something in the "else" code block faulted.
398 // Probably safest to assume that all variables in this method are
399 // trash... So, simply clean up and return.
400 sigaction(SIGSEGV, &sa_all_PIM_previous, NULL);
401 // reset signal handler
402 return;
403 } else
404#endif
405 {
406 // volatile int *x = 0;
407 //*x = 0;
408 if (pic->m_pplugin)
409 pic->m_pplugin->SetNMEASentence(decouple_sentence);
410 }
411 }
412 }
413 }
414#ifndef __WXMSW__
415 sigaction(SIGSEGV, &sa_all_PIM_previous, NULL); // reset signal handler
416#endif
417}
418
419int GetJSONMessageTargetCount() {
420 int rv = 0;
421 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
422 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
423 PlugInContainer* pic = plugin_array->Item(i);
424 if (pic->m_enabled && pic->m_init_state &&
426 rv++;
427 }
428 return rv;
429}
430
431void SendVectorChartObjectInfo(const wxString& chart, const wxString& feature,
432 const wxString& objname, double& lat,
433 double& lon, double& scale, int& nativescale) {
434 wxString decouple_chart(chart);
435 wxString decouple_feature(feature);
436 wxString decouple_objname(objname);
437 auto plugin_array = PluginLoader::getInstance()->GetPlugInArray();
438 for (unsigned int i = 0; i < plugin_array->GetCount(); i++) {
439 PlugInContainer* pic = (*plugin_array)[i];
440 if (pic->m_enabled && pic->m_init_state) {
442 switch (pic->m_api_version) {
443 case 112:
444 case 113:
445 case 114:
446 case 115:
447 case 116:
448 case 117:
449 case 118:
450 case 119: {
451 auto* ppi = dynamic_cast<opencpn_plugin_112*>(pic->m_pplugin);
452 if (ppi)
453 ppi->SendVectorChartObjectInfo(decouple_chart, decouple_feature,
454 decouple_objname, lat, lon, scale,
455 nativescale);
456 break;
457 }
458 default:
459 break;
460 }
461 }
462 }
463 }
464}
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.
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