OpenCPN Partial API docs
Loading...
Searching...
No Matches
console.cpp
1/******************************************************************************
2 *
3 * Project: OpenCPN
4 *
5 * Purpose: Simple CLI application.
6 *
7 ***************************************************************************
8 * Copyright (C) 2022 Alec Leamas *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 ***************************************************************************
25 */
26
27#include "wx/wxprec.h"
28
29#ifndef WX_PRECOMP
30#include "wx/wx.h"
31#endif
32
33#include "config.h"
34
35#ifdef _WIN32
36#include <windows.h>
37#endif
38
39#include <algorithm>
40#include <chrono>
41#include <ctime>
42#include <iomanip>
43#include <iostream>
44#include <memory>
45
46#include "wx/wxprec.h"
47
48#ifndef WX_PRECOMP
49#include "wx/wx.h"
50#endif
51
52#include <wx/app.h>
53#include <wx/bitmap.h>
54#include <wx/cmdline.h>
55#include <wx/dynlib.h>
56#include <wx/fileconf.h>
57#include <wx/string.h>
58
59#include "base_platform.h"
60#include "catalog_handler.h"
61#include "comm_appmsg_bus.h"
62#include "comm_driver.h"
63#include "comm_navmsg_bus.h"
64#include "config_vars.h"
65#include "downloader.h"
66#include "observable_evtvar.h"
67#include "ocpn_utils.h"
68#include "plugin_handler.h"
69#include "plugin_loader.h"
70#include "routeman.h"
71#include "select.h"
72#include "track.h"
73
75class Multiplexer;
76class Select;
77
78BasePlatform* g_BasePlatform = 0;
79bool g_bportable = false;
80wxString g_winPluginDir;
81void* g_pi_manager = reinterpret_cast<void*>(1L);
82wxString g_compatOS = PKG_TARGET;
83wxString g_compatOsVersion = PKG_TARGET_VERSION;
84
85wxString g_catalog_custom_url;
86wxString g_catalog_channel;
87wxLog* g_logger;
88
89bool g_bAIS_ACK_Timeout;
90bool g_bAIS_CPA_Alert_Suppress_Moored;
91bool g_bCPAMax;
92bool g_bCPAWarn;
93bool g_bHideMoored;
94bool g_bTCPA_Max;
95double g_AckTimeout_Mins;
96double g_CPAMax_NM;
97double g_CPAWarn_NM;
98double g_ShowMoored_Kts;
99double g_TCPA_Max;
100bool g_bShowMag;
101bool g_bShowTrue;
102bool bGPSValid;
103bool g_bInlandEcdis;
104bool g_bRemoveLost;
105bool g_bMarkLost;
106bool g_bShowScaled;
107bool g_bAllowShowScaled;
108bool g_bAISRolloverShowCOG;
109bool g_bAISRolloverShowCPA;
110bool g_bAISShowTracks;
111bool g_bAISRolloverShowClass;
112bool g_bAIS_CPA_Alert;
113double g_RemoveLost_Mins;
114double g_MarkLost_Mins;
115double g_AISShowTracks_Mins;
116float g_selection_radius_mm;
117float g_selection_radius_touch_mm;
118int g_nCOMPortCheck = 32;
119bool g_benableUDPNullHeader;
120
121
122std::vector<Track*> g_TrackList;
123wxString AISTargetNameFileName;
124AISTargetAlertDialog* g_pais_alert_dialog_active;
125Route* pAISMOBRoute;
126int g_WplAction;
127Select* pSelectAIS;
128
129/* comm_bridge context. */
130
131int g_NMEAAPBPrecision;
132
133Select* pSelect;
134double g_n_arrival_circle_radius;
135double g_PlanSpeed;
136bool g_bTrackDaily;
137int g_trackFilterMax;
138wxString g_default_routepoint_icon;
139double g_TrackDeltaDistance;
140float g_fWaypointRangeRingsStep;
141float g_ChartScaleFactorExp;
142float g_MarkScaleFactorExp;
143wxString g_default_wp_icon;
144bool g_btouch;
145int g_iWaypointRangeRingsNumber;
146int g_iWaypointRangeRingsStepUnits;
147wxColour g_colourWaypointRangeRingsColour;
148bool g_bUseWptScaMin;
149int g_iWpt_ScaMin;
150int g_LayerIdx;
151bool g_bOverruleScaMin;
152int g_nTrackPrecision;
153bool g_bIsNewLayer;
154RouteList *pRouteList;
155WayPointman* pWayPointMan;
156int g_route_line_width;
157int g_track_line_width;
158RoutePoint* pAnchorWatchPoint1 = 0;
159RoutePoint* pAnchorWatchPoint2 = 0;
160bool g_bAllowShipToActive;
161wxRect g_blink_rect;
162bool g_bMagneticAPB;
163
164Routeman* g_pRouteMan;
165
166
167static void InitRouteman() {
168 struct RoutePropDlgCtx ctx;
169 auto RouteMgrDlgUpdateListCtrl = [&]() {};
170 g_pRouteMan = new Routeman(ctx, RouteMgrDlgUpdateListCtrl);
171}
172
173// navutil_base context
174int g_iDistanceFormat = 0;
175int g_iSDMMFormat = 0;
176int g_iSpeedFormat = 0;
177
178namespace safe_mode { bool get_mode() { return false; } }
179
180static const char* USAGE = R"""(
181Usage: opencpn-cli [options] <command>
182
183Options:
184
185 -v, --verbose:
186 Verbose logging.
187
188 -a, --abi <abi-spec>
189 Use non-default ABI. <abi-spec> is a string platform:version for
190 example 'ubuntu-gtk3-x86_64:20.04'.
191
192Commands:
193 load-plugin <plugin library file>:
194 Try to load a given library file, report possible errors.
195
196 install-plugin <plugin name>:
197 Download and install given plugin
198
199 uninstall-plugin <plugin name>:
200 Uninstall given plugin
201
202 list-plugins:
203 List name and version for all installed plugins
204
205 list-available:
206 List name and version for available plugins in catalog.
207
208 import-plugin <tarball file>
209 Import a given tarball file
210
211 print-abi:
212 print default ABI spec platform:version
213
214 update-catalog:
215 Download latest master catalog.
216
217)""";
218
219static const char *const DOWNLOAD_REPO_PROTO =
220 "https://raw.githubusercontent.com/OpenCPN/plugins/@branch@/"
221 "ocpn-plugins.xml";
222
223wxDEFINE_EVENT(EVT_FOO, wxCommandEvent);
224wxDEFINE_EVENT(EVT_BAR, wxCommandEvent);
225
226
227class CliApp : public wxAppConsole {
228public:
229 CliApp() : wxAppConsole() {
230 CheckBuildOptions(WX_BUILD_OPTIONS_SIGNATURE, "program");
231 SetAppName("opencpn");
232 }
233
234 void OnInitCmdLine(wxCmdLineParser& parser) override {
235 parser.AddOption("a", "abi", "abi:version e. g., \"ubuntu-x86_64:20.04\"");
236 parser.AddSwitch("v", "verbose", "Verbose logging");
237 parser.AddSwitch("h", "help", "Print help");
238 parser.AddParam("<command>",
239 wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
240 parser.AddParam("[arg]", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
241
242 wxLog::SetActiveTarget(new wxLogStderr);
243 wxLog::SetTimestamp("");
244 wxLog::SetLogLevel(wxLOG_Warning);
245
246 g_BasePlatform = new BasePlatform();
247 auto config_file = g_BasePlatform->GetConfigFileName();
248 InitBaseConfig(new wxFileConfig("", "", config_file));
249 pSelect = new Select();
250 pRouteList = new RouteList;
251 InitRouteman();
252 pWayPointMan = new WayPointman();
253 }
254
255 void list_plugins() {
256 using namespace std;
257 wxImage::AddHandler(new wxPNGHandler());
258 g_BasePlatform->GetSharedDataDir(); // See #2619
259 PluginLoader::getInstance()->LoadAllPlugIns(false);
260 auto plugins = PluginHandler::getInstance()->getInstalled();
261 for (const auto& p : plugins) {
262 if (p.version == "0.0") continue;
263 cout << left << setw(25) << p.name << p.version << "\n";
264 }
265 }
266
267 void list_available() {
268 using namespace std;
269 auto handler = PluginHandler::getInstance();
270 auto plugins = handler->getAvailable();
271 for (const auto& p : plugins) {
272 if (handler->isCompatible(p)) {
273 cout << left << setw(25) << p.name << p.version << "\n";
274 }
275 }
276 }
277
278 void uninstall_plugin(const std::string& plugin) {
279 using namespace std;
280 g_BasePlatform->GetSharedDataDir(); // See #2619
281 PluginLoader::getInstance()->LoadAllPlugIns(false);
282 auto plugins = PluginHandler::getInstance()->getInstalled();
283 vector<PluginMetadata> found;
284 copy_if(plugins.begin(), plugins.end(), back_inserter(found),
285 [plugin](const PluginMetadata& m) { return m.name == plugin; });
286 if (found.size() == 0) {
287 cerr << "No such plugin installed\n";
288 exit(2);
289 }
290 PluginHandler::getInstance()->uninstall(found[0].name);
291 }
292
293 void import_plugin(const std::string& tarball_path) {
294 PluginHandler::getInstance()->installPlugin(tarball_path);
295 exit(0);
296 }
297
298 void install_plugin(const std::string& plugin) {
299 using namespace std;
300 g_BasePlatform->GetSharedDataDir(); // See #2619
301 wxImage::AddHandler(new wxPNGHandler());
302 auto handler = PluginHandler::getInstance();
303 auto plugins = handler->getAvailable();
304 vector<PluginMetadata> found;
305 copy_if(plugins.begin(), plugins.end(), back_inserter(found),
306 [plugin, handler](const PluginMetadata& m) {
307 return m.name == plugin && handler->isCompatible(m); });
308 if (found.size() == 0) {
309 cerr << "No such plugin available\n";
310 exit(2);
311 }
312 Downloader downloader(found[0].tarball_url);
313 string path;
314 bool ok = downloader.download(path);
315 if (!ok) {
316 cerr << "Cannot download data from " << found[0].tarball_url << "\n";
317 exit(1);
318 }
319 PluginHandler::getInstance()->installPlugin(path);
320 remove(path.c_str());
321 exit(0);
322 }
323
324 bool load_plugin(const std::string& plugin) {
325 auto loader = PluginLoader::getInstance();
326 wxImage::AddHandler(new wxPNGHandler());
327 g_BasePlatform->GetSharedDataDir(); // See #2619
328 wxDEFINE_EVENT(EVT_FILE_NOTFOUND, wxCommandEvent);
329 ObservableListener file_notfound_listener;
330 file_notfound_listener.Listen(loader->evt_unreadable_plugin,
331 this, EVT_FILE_NOTFOUND);
332 Bind(EVT_FILE_NOTFOUND, [&](wxCommandEvent ev) {
333 std::cerr << "Cannot open file: " << ev.GetString() << "\n";
334 });
335
336 wxDEFINE_EVENT(EVT_BAD_VERSION, wxCommandEvent);
337 ObservableListener bad_version_listener;
338 bad_version_listener.Listen(loader->evt_version_incompatible_plugin,
339 this, EVT_BAD_VERSION);
340 Bind(EVT_BAD_VERSION, [&](wxCommandEvent ev) {
341 std::cerr << "Incompatible plugin version " << ev.GetString() << "\n";
342 });
343
344 auto container = loader->LoadPlugIn(plugin.c_str());
345 ProcessPendingEvents();
346 std::cout << (container ? "File loaded OK\n" : "Load error\n");
347 return container ? true : false;
348 }
349
350 void check_param_count(const wxCmdLineParser& parser, size_t count) {
351 if (parser.GetParamCount() < count) {
352 std::cerr << USAGE << "\n";
353 exit(1);
354 }
355 }
356
357 void update_catalog() {
358 std::string catalog(g_catalog_channel == "" ? "master" : g_catalog_channel);
359 std::string url(g_catalog_custom_url);
360 if (catalog != "custom") {
361 url = std::string(DOWNLOAD_REPO_PROTO);
362 ocpn::replace(url, "@branch@", catalog);
363 }
364 auto path = PluginHandler::getInstance()->getMetadataPath();
365 auto cat_handler = CatalogHandler::getInstance();
366 auto status = cat_handler->DownloadCatalog(path, url);
367 if (status != CatalogHandler::ServerStatus::OK) {
368 std::cout << "Cannot update catalog\n";
369 exit(1);
370 }
371 }
372
373 bool OnCmdLineParsed(wxCmdLineParser& parser) {
374 wxAppConsole::OnCmdLineParsed(parser);
375 if (argc == 1) {
376 std::cout << "OpenCPN CLI application. Use -h for help\n";
377 exit(0);
378 }
379 wxString option_val;
380 if (parser.Found("help")) {
381 std::cout << USAGE << "\n";
382 exit(0);
383 }
384 if (parser.Found("verbose")) wxLog::SetLogLevel(wxLOG_Debug);
385 if (parser.Found("abi", &option_val)) {
386 auto abi_vers = ocpn::split(option_val, ":");
387 g_compatOS = abi_vers[0];
388 if (abi_vers.size() > 1) g_compatOsVersion = abi_vers[1];
389 }
390 if (parser.GetParamCount() < 1) {
391 std::cerr << USAGE << "\n";
392 exit(1);
393 }
394 std::string command(parser.GetParam(0));
395 if (command == "load-plugin") {
396 check_param_count(parser, 2);
397 load_plugin(parser.GetParam(1).ToStdString());
398 }
399 else if (command == "uninstall-plugin") {
400 check_param_count(parser, 2);
401 uninstall_plugin(parser.GetParam(1).ToStdString());
402 }
403 else if (command == "import-plugin") {
404 check_param_count(parser, 2);
405 import_plugin(parser.GetParam(1).ToStdString());
406 }
407 else if (command == "install-plugin") {
408 check_param_count(parser, 2);
409 install_plugin(parser.GetParam(1).ToStdString());
410 }
411 else if (command == "list-plugins") {
412 check_param_count(parser, 1);
413 list_plugins();
414 }
415 else if (command == "list-available") {
416 check_param_count(parser, 1);
417 list_available();
418 }
419 else if (command == "print-abi") {
420 check_param_count(parser, 1);
421 std::cout << PKG_TARGET << ":" << PKG_TARGET_VERSION "\n";
422 }
423 else if (command == "update-catalog") {
424 check_param_count(parser, 1);
425 update_catalog();
426 }
427 else {
428 std::cerr << USAGE << "\n";
429 exit(2);
430 }
431 exit(0);
432 }
433};
434
435wxIMPLEMENT_APP_CONSOLE(CliApp);
Handle downloading of files from remote urls.
Definition: downloader.h:34
bool download(std::ostream *stream)
Download url into stream, return false on errors.
Definition: downloader.cpp:55
Keeps listening over it's lifespan, removes itself on destruction.
Definition: observable.h:143
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Definition: observable.cpp:98
const std::vector< PluginMetadata > getInstalled()
Return list of all installed plugins.
bool uninstall(const std::string plugin)
Uninstall an installed plugin.
bool installPlugin(PluginMetadata plugin)
Download and install a new, not installed plugin.
std::string getMetadataPath()
Return path to metadata XML file.
Definition: route.h:70
Definition: select.h:51
Safe mode handling.
Definition: console.cpp:178
Plugin metadata, reflects the xml format directly.