From a8aa6ee03615d22eaf7412ad77ff5b1f67490447 Mon Sep 17 00:00:00 2001 From: Hakan Date: Wed, 29 Nov 2023 17:26:28 +0100 Subject: [PATCH 01/63] Meteo, fix visibility >_sign. User's UI notes fixed --- include/meteo_points.h | 1 + src/ais_decoder.cpp | 11 +++++++++-- src/ais_target_data.cpp | 26 +++++++++++++++++++------- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/include/meteo_points.h b/include/meteo_points.h index b466564ec4..afa624eb16 100644 --- a/include/meteo_points.h +++ b/include/meteo_points.h @@ -44,6 +44,7 @@ int airpress; // value+799 hPa NAN = 511(1310) int airpress_tend; // NAN = 3 double hor_vis; // NAN = 127(12.7) + bool hor_vis_GT; // Greater than double water_lev_dev; // Water level deviation (incl.tide) NAN = 30 double water_level; // Water level NAN = -32,768 int water_lev_trend; // NAN = 3 diff --git a/src/ais_decoder.cpp b/src/ais_decoder.cpp index 1f573adb9b..a2ff4e0198 100644 --- a/src/ais_decoder.cpp +++ b/src/ais_decoder.cpp @@ -3306,8 +3306,15 @@ bool AisDecoder::Parse_VDXBitstring(AisBitstring *bstr, 1 - 401 = 800 - 1200 hPa*/ ptd->met_data.airpress = bstr->GetInt(183, 9) + 799; ptd->met_data.airpress_tend = bstr->GetInt(192, 2); - ptd->met_data.hor_vis = bstr->GetInt(194, 8) / 10.; - //int MSB = bstr->GetInt(194, 8) < 0; + + int horVis = bstr->GetInt(194, 8); + if (horVis & 0x80u) { // if MSB = 1 + horVis &= 0x7F; // We print >x.x + ptd->met_data.hor_vis_GT = true; + } else + ptd->met_data.hor_vis_GT = false; + + ptd->met_data.hor_vis = horVis / 10.0; ptd->met_data.water_lev_dev = (bstr->GetInt(202, 12) / 100.) - 10.; ptd->met_data.water_lev_trend = bstr->GetInt(214, 2); diff --git a/src/ais_target_data.cpp b/src/ais_target_data.cpp index ebc2879d54..435a1e9805 100644 --- a/src/ais_target_data.cpp +++ b/src/ais_target_data.cpp @@ -314,6 +314,7 @@ AisTargetData::AisTargetData(AisTargetCallbacks cb ) : m_callbacks(cb) { met_data.airpress = 1310; met_data.airpress_tend = 3; met_data.hor_vis = 12.7; + met_data.hor_vis_GT = false; met_data.water_lev_dev = 4001 / 100 - 10; met_data.water_level = -32; met_data.water_lev_trend = 3; @@ -698,10 +699,11 @@ wxString AisTargetData::BuildQueryResult(void) { << rowEnd << rowStartH << _T("") << toSDMM(2, Lon); if (Class != AIS_METEO) html << rowEnd; else { - wxString meteoTime = wxString::Format( - "%s %02d:%02d", - _("Issued (UTC)"), met_data.hour, met_data.minute); - html << "" << meteoTime << rowEnd; + wxString meteoTime = + wxString::Format(" %02d:%02d", met_data.hour, met_data.minute); + html << " " + << _("Issued (UTC)") << "" << meteoTime + << "" << rowEnd; } } @@ -895,7 +897,7 @@ wxString AisTargetData::BuildQueryResult(void) { if (Class == AIS_METEO) { if (met_data.wind_kn < 122) { double userwindspeed = toUsrWindSpeed(met_data.wind_kn); - wxString wspeed = wxString::Format("%.0f %s %d%c", userwindspeed, getUsrWindSpeedUnit(), + wxString wspeed = wxString::Format("%0.1f %s %d%c", userwindspeed, getUsrWindSpeedUnit(), met_data.wind_dir, 0x00B0); double userwindgustspeed = toUsrWindSpeed(met_data.wind_gust_kn); @@ -946,7 +948,7 @@ wxString AisTargetData::BuildQueryResult(void) { if (met_data.wave_height < 24.6 || met_data.swell_height < 24.6) { double userwave = toUsrDepth(met_data.wave_height); wxString wave = - wxString::Format("%.1f %s %d%c %d %s", userwave, getUsrDepthUnit(), + wxString::Format("%.1f %s %d%c %d %s ", userwave, getUsrDepthUnit(), met_data.wave_dir, 0x00B0, met_data.wave_period, _("s")); if (met_data.wave_height >= 24.6) wave = wxEmptyString; @@ -1021,7 +1023,8 @@ wxString AisTargetData::BuildQueryResult(void) { double userVisDist = toUsrDistance(met_data.hor_vis); wxString horVis = - wxString::Format("%.1f %s", userVisDist, getUsrDistanceUnit()); + wxString::Format("%s%.1f %s", (met_data.hor_vis_GT ? ">" : ""), + userVisDist, getUsrDistanceUnit()); if (met_data.hor_vis >= 12.7) horVis = wxEmptyString; html << vertSpacer << rowStart << _("Precipitation") << _T("") @@ -1232,6 +1235,15 @@ wxString AisTargetData::GetRolloverString(void) { result << _("Air press"); result << wxString::Format(": %d hPa", met_data.airpress); } + + if (met_data.hor_vis < 12.) { + if (result.Len()) result << "\n"; + double userVisDist = toUsrDistance(met_data.hor_vis); + wxString horVis = + wxString::Format(": %s%.1f %s", (met_data.hor_vis_GT ? ">" : ""), + userVisDist, getUsrDistanceUnit()); + result << _("Visibility") << horVis; + } } return result; } From 384264c6cddb181714ae4ffa6febec45fa083c3c Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Mon, 20 Nov 2023 23:35:42 +0100 Subject: [PATCH 02/63] CMakeLists: Really use -O0 for Debug, update diagnostics. --- CMakeLists.txt | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 07e95c3c1d..cc1f6fef66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -379,16 +379,6 @@ if (CMAKE_VERSION VERSION_GREATER 3.9) set(CMAKE_CXX_CPPCHECK ${CPPCHECK_EXECUTABLE}) endif () endif () - -message(STATUS "Default compiler options:") -message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") -message(STATUS "CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}") -message(STATUS "CMAKE_CXX_FLAGS_MINSIZEREL: ${CMAKE_CXX_FLAGS_MINSIZEREL}") -message(STATUS "CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}") -message( - STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" -) - # ADD_COMPILE_OPTIONS( "-Wall" "-ansi" "-pedantic" "-Wno-variadic-macros" ) # TODO: Should we use -fno-stack-protector IF NOT DEBUGGING CFLAGS="-O2 # -march=native" @@ -403,8 +393,11 @@ if (NOT WIN32 AND NOT APPLE) "-Wno-deprecated-declarations" ) endif () - if (CMAKE_BUILD_TYPE MATCHES "Debug") - add_compile_options("-O0") + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang|GNU") + if (CMAKE_BUILD_TYPE MATCHES "Debug") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " -O0") + add_compile_options("-O0") + endif () endif () add_definitions(" -DPREFIX=\\\"${CMAKE_INSTALL_PREFIX}\\\"") @@ -511,6 +504,16 @@ if (QT_ANDROID) set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-soname,libgorp.so -Wl,--build-id") endif (QT_ANDROID) +message(STATUS "Final compiler options:") +message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") +message(STATUS "CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}") +message(STATUS "CMAKE_CXX_FLAGS_MINSIZEREL: ${CMAKE_CXX_FLAGS_MINSIZEREL}") +message(STATUS "CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}") +message( + STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" +) + + # # Installation directory setup From 818a5f19c8c7cbc77dd8e0ced944ede89a2aa6b8 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Mon, 20 Nov 2023 23:36:20 +0100 Subject: [PATCH 03/63] CMakeLists: Update standard to c++17 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cc1f6fef66..b3e0eac107 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -330,7 +330,7 @@ include(GetArch) getarch() message(STATUS "*** Host Build Architecture is ${ARCH}") -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) message(STATUS "Setting C++11 standard via cmake standard mechanism") if (NOT MSVC) set(OBJ_VISIBILITY "-fvisibility=hidden") From 8e83a4d2e07db9b972c5ee8a430ed9efa9ad5e28 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Tue, 21 Mar 2023 21:57:48 +0100 Subject: [PATCH 04/63] REST server et. al.: Introduce callback mechanics --- include/REST_server.h | 41 ++++++++++++++++++++-- include/REST_server_gui.h | 16 ++++++++- src/REST_server.cpp | 4 +-- src/REST_server_gui.cpp | 73 ++++++++++++++++++++++++++++++++++++++- src/ocpn_app.cpp | 6 ++-- 5 files changed, 132 insertions(+), 8 deletions(-) diff --git a/include/REST_server.h b/include/REST_server.h index d6a15965b3..f7c4697382 100644 --- a/include/REST_server.h +++ b/include/REST_server.h @@ -26,9 +26,10 @@ #ifndef _RESTSERVER_H #define _RESTSERVER_H +#include +#include #include #include -#include #include @@ -49,9 +50,43 @@ class RESTServerThread; // Internal class RESTServerEvent; // Internal class PINCreateDialog; +/** Abstract base class visible in callbacks. */ +class PinDialog { +public: + /** Create and show the dialog */ + virtual PinDialog* Initiate(const std::string& msg, + const std::string& text1) = 0; + + /** Close and destroy */ + virtual void DeInit() = 0; +}; + + +/** Callbacks invoked from PinDialog implementations. */ +class RestServerDlgCtx { +public: + std::function show_dialog; + std::function close_dialog; + std::function update_route_mgr; + + /** Run the "Accept Object" dialog, returns value from ShowModal(). */ + std::function run_accept_object_dlg; + + RestServerDlgCtx() + : show_dialog([]( + const std::string&, const std::string&)->PinDialog* { return 0; } ), + close_dialog([](PinDialog*) {}), + update_route_mgr([](){ }), + run_accept_object_dlg( + [](const std::string&, const std::string&) { return 0; }) {} +}; + + class RESTServer : public wxEvtHandler { public: - RESTServer(); + RESTServer(RestServerDlgCtx ctx); virtual ~RESTServer(); @@ -74,6 +109,7 @@ class RESTServer : public wxEvtHandler { return m_pSecondary_Thread; } void SetThreadRunFlag(int run) { m_Thread_run_flag = run; } + void UpdateRouteMgr() { m_dlg_ctx.update_route_mgr(); } std::string GetCertificateDirectory(){ return m_certificate_directory; } int m_Thread_run_flag; @@ -85,6 +121,7 @@ class RESTServer : public wxEvtHandler { bool LoadConfig( void ); bool SaveConfig( void ); + RestServerDlgCtx m_dlg_ctx; RESTServerThread* m_pSecondary_Thread; bool m_bsec_thread_active; std::string m_certificate_directory; diff --git a/include/REST_server_gui.h b/include/REST_server_gui.h index 7d051ffa0d..3aa6ed3775 100644 --- a/include/REST_server_gui.h +++ b/include/REST_server_gui.h @@ -25,10 +25,17 @@ #ifndef __RESTSERVERGUI_H__ #define __RESTSERVERGUI_H__ +#include +#include + +#include #include #include #include +#include "ocpn_frame.h" +#include "REST_server.h" + // Constants for Dialog #define ID_STGDIALOG 10005 #define SYMBOL_STG_STYLE \ @@ -80,7 +87,8 @@ class AcceptObjectDialog : public wxDialog { wxString m_checkbox1_msg; }; -class PINCreateDialog : public wxDialog { + +class PINCreateDialog : public PinDialog, public wxDialog { DECLARE_DYNAMIC_CLASS(PINCreateDialog) DECLARE_EVENT_TABLE() @@ -91,6 +99,12 @@ class PINCreateDialog : public wxDialog { long style); ~PINCreateDialog(); + static RestServerDlgCtx GetDlgCtx(); + + + PinDialog* Initiate(const std::string& msg, const std::string& text1); + void DeInit(); + bool Create(wxWindow* parent, wxWindowID id = SYMBOL_STG_IDNAME, const wxString& caption = SYMBOL_STG_TITLE, const wxString& hint = SYMBOL_STG_TITLE, diff --git a/src/REST_server.cpp b/src/REST_server.cpp index 603438011e..cd335151f3 100644 --- a/src/REST_server.cpp +++ b/src/REST_server.cpp @@ -142,8 +142,8 @@ wxDEFINE_EVENT(wxEVT_RESTFUL_SERVER, RESTServerEvent); /* RESTServer implementation * */ -RESTServer::RESTServer() - : m_Thread_run_flag(-1) +RESTServer::RESTServer(RestServerDlgCtx ctx) + : m_Thread_run_flag(-1), m_dlg_ctx(ctx) { m_PINCreateDialog = NULL; diff --git a/src/REST_server_gui.cpp b/src/REST_server_gui.cpp index 9df34549a9..65c6ee3ace 100644 --- a/src/REST_server_gui.cpp +++ b/src/REST_server_gui.cpp @@ -34,9 +34,64 @@ #include #include +#include "FontMgr.h" +#include "ocpn_frame.h" #include "REST_server.h" #include "REST_server_gui.h" -#include "FontMgr.h" +#include "routemanagerdialog.h" + +extern RouteManagerDialog *pRouteManagerDialog; +extern MyFrame* gFrame; + +static PinDialog* DisplayDlg(const std::string& msg, const std::string& txt1) { + auto dlg = new PINCreateDialog(dynamic_cast(gFrame), wxID_ANY, + _("OpenCPN Server Message"), + "", wxDefaultPosition, wxDefaultSize, + SYMBOL_STG_STYLE ); + dlg->SetMessage(msg); + dlg->SetText1Message(txt1); + dlg->Show(); + return dlg; +} + +static void CloseDlg(PinDialog* pin_dlg) { + if (pin_dlg) { + auto dlg = dynamic_cast(pin_dlg); + dlg->Close(); + dlg->Destroy(); + } +} + +static void UpdateRouteMgr() { + if( pRouteManagerDialog && pRouteManagerDialog->IsShown() ) { + pRouteManagerDialog->UpdateTrkListCtrl(); + pRouteManagerDialog->UpdateWptListCtrl(); + pRouteManagerDialog->UpdateRouteListCtrl(); + } +} + +static int RunAcceptObjectDlg(const std::string& msg, + const std::string& check1msg) { + AcceptObjectDialog dlg(NULL, wxID_ANY, _("OpenCPN Server Message"), + "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE, + msg, check1msg); + int result = dlg.ShowModal(); + return result; +} + +RestServerDlgCtx PINCreateDialog::GetDlgCtx() { + RestServerDlgCtx ctx; + ctx.show_dialog = + [](const std::string& msg, const std::string& text1) { + return DisplayDlg(msg, text1); }; + ctx.close_dialog = [](PinDialog* pin_dlg) { CloseDlg(pin_dlg); }; + ctx.update_route_mgr = []() { UpdateRouteMgr(); }; + ctx.run_accept_object_dlg = + [](const std::string& msg, const std::string& check1msg) { + return RunAcceptObjectDlg(msg, check1msg); }; + return ctx; +} + #ifdef __ANDROID__ #include "androidUTIL.h" @@ -185,6 +240,22 @@ PINCreateDialog::~PINCreateDialog() { } +PinDialog* PINCreateDialog::Initiate(const std::string& msg, + const std::string& text1) { + auto dlg = new PINCreateDialog(dynamic_cast(gFrame), + wxID_ANY, _("OpenCPN Server Message"), "", + wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE ); + dlg->SetMessage(msg); + dlg->SetText1Message(text1); + dlg->Show(); + return dlg; +} + +void PINCreateDialog::DeInit(){ + Close(); + Destroy(); +} + bool PINCreateDialog::Create(wxWindow* parent, wxWindowID id, const wxString& caption, const wxString& hint, const wxPoint& pos, const wxSize& size, long style) { diff --git a/src/ocpn_app.cpp b/src/ocpn_app.cpp index 1869f2463e..a2d21f1c52 100644 --- a/src/ocpn_app.cpp +++ b/src/ocpn_app.cpp @@ -132,6 +132,7 @@ #include "routemanagerdialog.h" #include "routeman.h" #include "RoutePropDlgImpl.h" +#include "REST_server_gui.h" #include "s52plib.h" #include "s57chart.h" #include "S57QueryDialog.h" @@ -1020,11 +1021,12 @@ void MyApp::OnActivateApp(wxActivateEvent &event) { event.Skip(); } + static wxStopWatch init_sw; -MyApp::MyApp() { +MyApp::MyApp() : m_RESTserver(PINCreateDialog::GetDlgCtx()) { #ifdef __linux__ -// Handle e. g., wayland default display -- see #1166. + // Handle e. g., wayland default display -- see #1166. if (wxGetEnv( "WAYLAND_DISPLAY", NULL)) setenv("GDK_BACKEND", "x11", 1); From 01fec16b09465ef0dc157c53e86c14982de68d6e Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Wed, 22 Mar 2023 18:15:39 +0100 Subject: [PATCH 05/63] REST server et al: Use the callback mechanisms --- include/REST_server.h | 23 +++++++-- src/REST_server.cpp | 102 +++++++++++----------------------------- src/REST_server_gui.cpp | 10 ++-- 3 files changed, 52 insertions(+), 83 deletions(-) diff --git a/include/REST_server.h b/include/REST_server.h index f7c4697382..51d8ebb6c8 100644 --- a/include/REST_server.h +++ b/include/REST_server.h @@ -32,6 +32,7 @@ #include #include +#include typedef enum RESTServerResult { RESULT_NO_ERROR = 0, @@ -61,6 +62,15 @@ class PinDialog { virtual void DeInit() = 0; }; +/** Returned status from RunAcceptObjectDlg. */ +struct AcceptObjectDlgResult { + int status; ///< return value from ShowModal() + bool check1_value; ///< As of GetCheck1Value() + + AcceptObjectDlgResult(): status(0), check1_value(false) {} + AcceptObjectDlgResult(int s, bool b): status(s), check1_value(b) {} +}; + /** Callbacks invoked from PinDialog implementations. */ class RestServerDlgCtx { @@ -71,8 +81,10 @@ class RestServerDlgCtx { std::function update_route_mgr; /** Run the "Accept Object" dialog, returns value from ShowModal(). */ - std::function run_accept_object_dlg; + std::function + run_accept_object_dlg; + std::function top_level_refresh; RestServerDlgCtx() : show_dialog([]( @@ -80,7 +92,10 @@ class RestServerDlgCtx { close_dialog([](PinDialog*) {}), update_route_mgr([](){ }), run_accept_object_dlg( - [](const std::string&, const std::string&) { return 0; }) {} + [](const wxString&, const wxString&) { + return AcceptObjectDlgResult(); }), + top_level_refresh([](){ }) + {} }; @@ -126,7 +141,7 @@ class RESTServer : public wxEvtHandler { bool m_bsec_thread_active; std::string m_certificate_directory; std::unordered_map m_key_map; - PINCreateDialog *m_PINCreateDialog; + PinDialog* m_pin_dialog; wxString m_sPIN; int m_dPIN; bool m_b_overwrite; diff --git a/src/REST_server.cpp b/src/REST_server.cpp index cd335151f3..141e9ef9bf 100644 --- a/src/REST_server.cpp +++ b/src/REST_server.cpp @@ -44,21 +44,15 @@ #include "mongoose.h" #include "config_vars.h" #include "cmdline.h" -#include "gui_lib.h" -#include "REST_server_gui.h" #include "pugixml.hpp" #include "route.h" #include "track.h" #include "routeman.h" #include "nav_object_database.h" -#ifndef CLIAPP -#include "routemanagerdialog.h" -#endif extern std::vector g_TrackList; -#ifndef CLIAPP -extern RouteManagerDialog* pRouteManagerDialog; -#endif +extern Routeman *g_pRouteMan; + Route *GPXLoadRoute1(pugi::xml_node &wpt_node, bool b_fullviz, bool b_layer, bool b_layerviz, int layer_id, bool b_change); @@ -73,8 +67,6 @@ bool InsertRouteA(Route *pTentRoute, NavObjectCollection1* navobj); bool InsertTrack(Track *pTentTrack, bool bApplyChanges = false); bool InsertWpt(RoutePoint *pWp, bool overwrite); -extern Routeman *g_pRouteMan; -extern MyFrame *gFrame; // Some global variables to handle thread syncronization int return_status; @@ -146,7 +138,7 @@ RESTServer::RESTServer(RestServerDlgCtx ctx) : m_Thread_run_flag(-1), m_dlg_ctx(ctx) { - m_PINCreateDialog = NULL; + m_pin_dialog = 0; // Prepare the wxEventHandler to accept events from the actual hardware thread Bind(wxEVT_RESTFUL_SERVER, &RESTServer::HandleServerMessage, @@ -294,11 +286,7 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { if (event.GetId() == ORS_CHUNK_LAST){ // Cancel existing dialog - if(m_PINCreateDialog){ - m_PINCreateDialog->Close(); - m_PINCreateDialog->Destroy(); - m_PINCreateDialog = NULL; - } + m_dlg_ctx.close_dialog(m_pin_dialog); // Close the temp file. if (m_tempUploadFilePath.size() && m_ul_stream.is_open()) @@ -309,8 +297,6 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { } -#ifndef CLIAPP - // Look up the api key in the hash map. std::string api_found; for (auto it : m_key_map){ @@ -334,19 +320,14 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { SaveConfig(); - m_PINCreateDialog = new PINCreateDialog((wxWindow *)gFrame, wxID_ANY, _("OpenCPN Server Message"), - "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE ); - wxString hmsg(event.m_source_peer.c_str()); hmsg += " "; hmsg += _("wants to send you new data.\nPlease enter the following PIN number on "); hmsg += wxString(event.m_source_peer.c_str()); hmsg += _(" to pair with this device.\n"); + m_pin_dialog = m_dlg_ctx.show_dialog(hmsg.ToStdString(), + m_sPIN.ToStdString()); - m_PINCreateDialog->SetMessage(hmsg); - m_PINCreateDialog->SetText1Message(m_sPIN); - - m_PINCreateDialog->Show(); return_status = RESTServerResult::RESULT_NEW_PIN_REQUESTED; std::lock_guard lock{mx}; @@ -365,7 +346,6 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { bool b_cont; b_cont = true; -#if 1 if (b_cont) {\ // Load the GPX file pugi::xml_document doc; @@ -386,21 +366,16 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { Route *duplicate = g_pRouteMan->FindRouteByGUID(pRoute->GetGUID()); if (duplicate){ if (!m_b_overwrite){ - AcceptObjectDialog dialog2(NULL, wxID_ANY, _("OpenCPN Server Message"), - "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE, - _("The received route already exists on this system.\nReplace?"), - _("Always replace objects?")); + auto result = m_dlg_ctx.run_accept_object_dlg( + _("The received route already exists on this system.\nReplace?"), + _("Always replace objects from this source?")); - dialog2.ShowModal(); - bool b_always = dialog2.GetCheck1Value(); - int result = dialog2.GetReturnCode(); - - if (result != ID_STG_OK){ + if (result.status != ID_STG_OK){ b_add = false; return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; } else{ - m_b_overwrite = b_always; + m_b_overwrite = result.check1_value; b_overwrite_one = true; SaveConfig(); } @@ -423,7 +398,7 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { return_stat = RESTServerResult::RESULT_NO_ERROR; else return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; - ((wxWindow *)gFrame)->Refresh(); + m_dlg_ctx.top_level_refresh(); } } } else if (!strcmp(object.name(), "trk")) { @@ -437,21 +412,16 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { Track *duplicate = g_pRouteMan->FindTrackByGUID(pRoute->m_GUID); if (duplicate){ if (!m_b_overwrite){ - AcceptObjectDialog dialog2(NULL, wxID_ANY, _("OpenCPN Server Message"), - "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE, - _("The received track already exists on this system.\nReplace?"), - _("Always replace objects?")); - - dialog2.ShowModal(); - bool b_always = dialog2.GetCheck1Value(); - int result = dialog2.GetReturnCode(); + auto result = m_dlg_ctx.run_accept_object_dlg( + _("The received track already exists on this system.\nReplace?"), + _("Always replace objects from this source?")); - if (result != ID_STG_OK){ + if (result.status != ID_STG_OK){ b_add = false; return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; } else{ - m_b_overwrite = b_always; + m_b_overwrite = result.check1_value; b_overwrite_one = true; SaveConfig(); } @@ -476,7 +446,7 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { return_stat = RESTServerResult::RESULT_NO_ERROR; else return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; - ((wxWindow *)gFrame)->Refresh(); + m_dlg_ctx.top_level_refresh(); } } } else if (!strcmp(object.name(), "wpt")) { @@ -492,22 +462,16 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { RoutePoint *duplicate = WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon); if (duplicate){ if (!m_b_overwrite){ - AcceptObjectDialog dialog2(NULL, wxID_ANY, _("OpenCPN Server Message"), - "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE, - _("The received waypoint already exists on this system.\nReplace?"), - _("Always replace objects?")); - - - dialog2.ShowModal(); - bool b_always = dialog2.GetCheck1Value(); - int result = dialog2.GetReturnCode(); + auto result = m_dlg_ctx.run_accept_object_dlg( + _("The received waypoint already exists on this system.\nReplace?"), + _("Always replace objects from this source?")); - if (result != ID_STG_OK){ + if (result.status != ID_STG_OK){ b_add = false; return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; } else{ - m_b_overwrite = b_always; + m_b_overwrite = result.check1_value; b_overwrite_one = true; SaveConfig(); } @@ -520,24 +484,18 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { return_stat = RESTServerResult::RESULT_NO_ERROR; else return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; - ((wxWindow *)gFrame)->Refresh(); + + m_dlg_ctx.top_level_refresh(); } } } } } } - else{ + else { return_stat = RESTServerResult::RESULT_OBJECT_REJECTED; - } -#else - // FIXME (leamas?) - // What should the CLI app do here? - return_stat = RESTServerResult::RESULT_GENERIC_ERROR; -#endif -#endif //0 return_status = return_stat; @@ -657,13 +615,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { printf("Reply: %d\n", return_status); mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); -#ifndef CLIAPP - if( pRouteManagerDialog && pRouteManagerDialog->IsShown() ) { - pRouteManagerDialog->UpdateTrkListCtrl(); - pRouteManagerDialog->UpdateWptListCtrl(); - pRouteManagerDialog->UpdateRouteListCtrl(); - } -#endif + parent->UpdateRouteMgr(); } } } diff --git a/src/REST_server_gui.cpp b/src/REST_server_gui.cpp index 65c6ee3ace..0a41fd3bcc 100644 --- a/src/REST_server_gui.cpp +++ b/src/REST_server_gui.cpp @@ -70,13 +70,14 @@ static void UpdateRouteMgr() { } } -static int RunAcceptObjectDlg(const std::string& msg, - const std::string& check1msg) { +static AcceptObjectDlgResult RunAcceptObjectDlg(const wxString& msg, + const wxString& check1msg) { AcceptObjectDialog dlg(NULL, wxID_ANY, _("OpenCPN Server Message"), "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE, msg, check1msg); int result = dlg.ShowModal(); - return result; + bool check1 = dlg.GetCheck1Value(); + return AcceptObjectDlgResult(result, check1); } RestServerDlgCtx PINCreateDialog::GetDlgCtx() { @@ -87,8 +88,9 @@ RestServerDlgCtx PINCreateDialog::GetDlgCtx() { ctx.close_dialog = [](PinDialog* pin_dlg) { CloseDlg(pin_dlg); }; ctx.update_route_mgr = []() { UpdateRouteMgr(); }; ctx.run_accept_object_dlg = - [](const std::string& msg, const std::string& check1msg) { + [](const wxString& msg, const wxString& check1msg) { return RunAcceptObjectDlg(msg, check1msg); }; + ctx.top_level_refresh = []() { dynamic_cast(gFrame)->Refresh(); }; return ctx; } From 6a3056ce1f094ef63190b1bfaf91216f67bf74d6 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 23 Mar 2023 11:04:27 +0100 Subject: [PATCH 06/63] REST_server.cpp etc. -> rest_server (Google GL) --- CMakeLists.txt | 8 ++++---- include/ocpn_app.h | 2 +- include/{REST_server.h => rest_server.h} | 0 include/{REST_server_gui.h => rest_server_gui.h} | 2 +- src/SendToGpsDlg.cpp | 2 +- src/ocpn_app.cpp | 2 +- src/peer_client.cpp | 2 +- src/{REST_server.cpp => rest_server.cpp} | 2 +- src/{REST_server_gui.cpp => rest_server_gui.cpp} | 4 +--- 9 files changed, 11 insertions(+), 13 deletions(-) rename include/{REST_server.h => rest_server.h} (100%) rename include/{REST_server_gui.h => rest_server_gui.h} (99%) rename src/{REST_server.cpp => rest_server.cpp} (99%) rename src/{REST_server_gui.cpp => rest_server_gui.cpp} (99%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b3e0eac107..0053158faf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -960,8 +960,8 @@ set( include/printtable.h include/priority_gui.h include/Quilt.h - include/REST_server.h - include/REST_server_gui.h + include/rest_server.h + include/rest_server_gui.h include/RolloverWin.h include/route.h include/route_gui.h @@ -1066,7 +1066,7 @@ set(MODEL_SRC ${CMAKE_SOURCE_DIR}/src/plugin_loader.cpp ${CMAKE_SOURCE_DIR}/src/plugin_paths.cpp ${CMAKE_SOURCE_DIR}/src/position_parser.cpp - ${CMAKE_SOURCE_DIR}/src/REST_server.cpp + ${CMAKE_SOURCE_DIR}/src/rest_server.cpp ${CMAKE_SOURCE_DIR}/src/route.cpp ${CMAKE_SOURCE_DIR}/src/route_point.cpp ${CMAKE_SOURCE_DIR}/src/routeman.cpp @@ -1145,7 +1145,7 @@ set( src/printtable.cpp src/priority_gui.cpp src/Quilt.cpp - src/REST_server_gui.cpp + src/rest_server_gui.cpp src/RolloverWin.cpp src/routemanagerdialog.cpp src/routeman_gui.cpp diff --git a/include/ocpn_app.h b/include/ocpn_app.h index 7efc41f10a..fd6cf9a6c0 100644 --- a/include/ocpn_app.h +++ b/include/ocpn_app.h @@ -37,7 +37,7 @@ #include #include "comm_bridge.h" -#include "REST_server.h" +#include "rest_server.h" class Track; diff --git a/include/REST_server.h b/include/rest_server.h similarity index 100% rename from include/REST_server.h rename to include/rest_server.h diff --git a/include/REST_server_gui.h b/include/rest_server_gui.h similarity index 99% rename from include/REST_server_gui.h rename to include/rest_server_gui.h index 3aa6ed3775..02cfc2a201 100644 --- a/include/REST_server_gui.h +++ b/include/rest_server_gui.h @@ -34,7 +34,7 @@ #include #include "ocpn_frame.h" -#include "REST_server.h" +#include "rest_server.h" // Constants for Dialog #define ID_STGDIALOG 10005 diff --git a/src/SendToGpsDlg.cpp b/src/SendToGpsDlg.cpp index a0b73a294c..d2b7f94f15 100644 --- a/src/SendToGpsDlg.cpp +++ b/src/SendToGpsDlg.cpp @@ -36,7 +36,7 @@ #include "conn_params.h" #include "OCPNPlatform.h" -#include "REST_server.h" +#include "rest_server.h" #include "route_gui.h" #include "route.h" #include "route_point_gui.h" diff --git a/src/ocpn_app.cpp b/src/ocpn_app.cpp index a2d21f1c52..a1e34cad2a 100644 --- a/src/ocpn_app.cpp +++ b/src/ocpn_app.cpp @@ -132,7 +132,7 @@ #include "routemanagerdialog.h" #include "routeman.h" #include "RoutePropDlgImpl.h" -#include "REST_server_gui.h" +#include "rest_server_gui.h" #include "s52plib.h" #include "s57chart.h" #include "S57QueryDialog.h" diff --git a/src/peer_client.cpp b/src/peer_client.cpp index 07a016ebe5..635a55d423 100644 --- a/src/peer_client.cpp +++ b/src/peer_client.cpp @@ -40,7 +40,7 @@ #include "FontMgr.h" #include "gui_lib.h" #include "nav_object_database.h" -#include "REST_server.h" +#include "rest_server.h" extern std::string PINtoRandomKeyString(int dpin); diff --git a/src/REST_server.cpp b/src/rest_server.cpp similarity index 99% rename from src/REST_server.cpp rename to src/rest_server.cpp index 141e9ef9bf..e75b1bcc1c 100644 --- a/src/REST_server.cpp +++ b/src/rest_server.cpp @@ -40,7 +40,7 @@ #include #include -#include "REST_server.h" +#include "rest_server.h" #include "mongoose.h" #include "config_vars.h" #include "cmdline.h" diff --git a/src/REST_server_gui.cpp b/src/rest_server_gui.cpp similarity index 99% rename from src/REST_server_gui.cpp rename to src/rest_server_gui.cpp index 0a41fd3bcc..497779b638 100644 --- a/src/REST_server_gui.cpp +++ b/src/rest_server_gui.cpp @@ -35,9 +35,7 @@ #include #include "FontMgr.h" -#include "ocpn_frame.h" -#include "REST_server.h" -#include "REST_server_gui.h" +#include "rest_server_gui.h" #include "routemanagerdialog.h" extern RouteManagerDialog *pRouteManagerDialog; From 9819f17412c6fa43896704f23516141e9d283b7f Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 23 Mar 2023 17:18:42 +0100 Subject: [PATCH 07/63] rest_server: Use callbacks and clean up - Don't use global g_bportable - Remove "if (true)" left over tests - Order of declarations, clang-format, etc. - Use static to limit visibility. --- CMakeLists.txt | 1 + include/rest_server.h | 67 +-- include/route_ctx_factory.h | 61 +++ src/ocpn_app.cpp | 5 +- src/peer_client.cpp | 2 - src/rest_server.cpp | 854 +++++++++++++++++------------------- 6 files changed, 509 insertions(+), 481 deletions(-) create mode 100644 include/route_ctx_factory.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0053158faf..8a3ef45f4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -964,6 +964,7 @@ set( include/rest_server_gui.h include/RolloverWin.h include/route.h + include/route_ctx_factory.h include/route_gui.h include/routemanagerdialog.h include/routeman.h diff --git a/include/rest_server.h b/include/rest_server.h index 51d8ebb6c8..5a703d05a7 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -34,6 +34,9 @@ #include #include +#include "route.h" +#include "track.h" + typedef enum RESTServerResult { RESULT_NO_ERROR = 0, RESULT_GENERIC_ERROR, @@ -48,9 +51,11 @@ enum { ORS_START_OF_SESSION, ORS_CHUNK_N, ORS_CHUNK_LAST }; enum { ID_STG_CANCEL = 10000, ID_STG_OK, ID_STG_CHECK1, ID_STG_CHOICE_COMM }; class RESTServerThread; // Internal -class RESTServerEvent; // Internal +class RESTServerEvent; // Internal class PINCreateDialog; +std::string PINtoRandomKeyString(int dpin); + /** Abstract base class visible in callbacks. */ class PinDialog { public: @@ -64,44 +69,56 @@ class PinDialog { /** Returned status from RunAcceptObjectDlg. */ struct AcceptObjectDlgResult { - int status; ///< return value from ShowModal() - bool check1_value; ///< As of GetCheck1Value() + int status; ///< return value from ShowModal() + bool check1_value; ///< As of GetCheck1Value() - AcceptObjectDlgResult(): status(0), check1_value(false) {} - AcceptObjectDlgResult(int s, bool b): status(s), check1_value(b) {} + AcceptObjectDlgResult() : status(0), check1_value(false) {} + AcceptObjectDlgResult(int s, bool b) : status(s), check1_value(b) {} }; - /** Callbacks invoked from PinDialog implementations. */ class RestServerDlgCtx { public: - std::function show_dialog; + std::function + show_dialog; std::function close_dialog; std::function update_route_mgr; /** Run the "Accept Object" dialog, returns value from ShowModal(). */ std::function - run_accept_object_dlg; + run_accept_object_dlg; std::function top_level_refresh; RestServerDlgCtx() - : show_dialog([]( - const std::string&, const std::string&)->PinDialog* { return 0; } ), + : show_dialog([](const std::string&, const std::string&) -> PinDialog* { + return 0; + }), close_dialog([](PinDialog*) {}), - update_route_mgr([](){ }), - run_accept_object_dlg( - [](const wxString&, const wxString&) { - return AcceptObjectDlgResult(); }), - top_level_refresh([](){ }) - {} + update_route_mgr([]() {}), + run_accept_object_dlg([](const wxString&, const wxString&) { + return AcceptObjectDlgResult(); + }), + top_level_refresh([]() {}) {} }; +/** Callbacks for handling routes and tracks. */ +class RouteCtx { +public: + std::function find_route_by_guid; + std::function find_track_by_guid; + std::function delete_route; + std::function delete_track; + RouteCtx() + : find_route_by_guid([](wxString) { return static_cast(0); }), + find_track_by_guid([](wxString) { return static_cast(0); }), + delete_route([](Route*) -> void {}), + delete_track([](Track*) -> void {}) {} +}; class RESTServer : public wxEvtHandler { public: - RESTServer(RestServerDlgCtx ctx); + RESTServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable); virtual ~RESTServer(); @@ -120,23 +137,23 @@ class RESTServer : public wxEvtHandler { void SetSecondaryThread(RESTServerThread* secondary_Thread) { m_pSecondary_Thread = secondary_Thread; } - RESTServerThread* GetSecondaryThread() { - return m_pSecondary_Thread; - } + RESTServerThread* GetSecondaryThread() { return m_pSecondary_Thread; } void SetThreadRunFlag(int run) { m_Thread_run_flag = run; } void UpdateRouteMgr() { m_dlg_ctx.update_route_mgr(); } - std::string GetCertificateDirectory(){ return m_certificate_directory; } + std::string GetCertificateDirectory() { return m_certificate_directory; } int m_Thread_run_flag; std::string m_cert_file; std::string m_key_file; private: - bool LoadConfig( void ); - bool SaveConfig( void ); + bool LoadConfig(void); + bool SaveConfig(void); RestServerDlgCtx m_dlg_ctx; + RouteCtx m_route_ctx; + bool& m_portable; RESTServerThread* m_pSecondary_Thread; bool m_bsec_thread_active; std::string m_certificate_directory; @@ -147,8 +164,6 @@ class RESTServer : public wxEvtHandler { bool m_b_overwrite; std::string m_tempUploadFilePath; std::ofstream m_ul_stream; - - }; #endif // guard diff --git a/include/route_ctx_factory.h b/include/route_ctx_factory.h new file mode 100644 index 0000000000..e9d9230f2b --- /dev/null +++ b/include/route_ctx_factory.h @@ -0,0 +1,61 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: Wrapper for creating a RouteCtx based on global vars + * Author: Alec Leamas + * + *************************************************************************** + * Copyright (C) 2023 by Alec Leamas + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ +#ifndef _ROUTE_CTX_FACTORY_H__ +#define _ROUTE_CTX_FACTORY_H__ + +#include + +#include "nav_object_database.h" +#include "routeman.h" +#include "track.h" + +extern Routeman *g_pRouteMan; +extern std::vector g_TrackList; + +RouteCtx RouteCtxFactory() { + RouteCtx ctx; + ctx.find_route_by_guid = + [](wxString guid) { + if (!g_pRouteMan) return static_cast(0); + return g_pRouteMan->FindRouteByGUID(guid); }; + ctx.find_track_by_guid = + [](wxString guid) { + if (!g_pRouteMan) return static_cast(0); + return g_pRouteMan->FindTrackByGUID(guid); }; + ctx.delete_route = + [](Route* route) { + if (!g_pRouteMan) return; + g_pRouteMan->DeleteRoute(route, NavObjectChanges::getInstance()); }; + ctx.delete_track = + [](Track* track) { + auto it = std::find(g_TrackList.begin(), g_TrackList.end(), track); + if (it != g_TrackList.end()) { + g_TrackList.erase(it); + } + delete track; + }; + return ctx; +} +#endif // _ROUTE_CTX_FACTORY_H__ diff --git a/src/ocpn_app.cpp b/src/ocpn_app.cpp index a1e34cad2a..fd0b0171a6 100644 --- a/src/ocpn_app.cpp +++ b/src/ocpn_app.cpp @@ -131,6 +131,8 @@ #include "route.h" #include "routemanagerdialog.h" #include "routeman.h" +#include "routeman_gui.h" +#include "route_ctx_factory.h" #include "RoutePropDlgImpl.h" #include "rest_server_gui.h" #include "s52plib.h" @@ -1024,7 +1026,8 @@ void MyApp::OnActivateApp(wxActivateEvent &event) { static wxStopWatch init_sw; -MyApp::MyApp() : m_RESTserver(PINCreateDialog::GetDlgCtx()) { +MyApp::MyApp() : m_RESTserver(PINCreateDialog::GetDlgCtx(), RouteCtxFactory(), + g_bportable) { #ifdef __linux__ // Handle e. g., wayland default display -- see #1166. diff --git a/src/peer_client.cpp b/src/peer_client.cpp index 635a55d423..ea601c1ae6 100644 --- a/src/peer_client.cpp +++ b/src/peer_client.cpp @@ -42,8 +42,6 @@ #include "nav_object_database.h" #include "rest_server.h" -extern std::string PINtoRandomKeyString(int dpin); - extern MyFrame *gFrame; wxString GetErrorText(int result){ diff --git a/src/rest_server.cpp b/src/rest_server.cpp index e75b1bcc1c..7f8037b919 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -23,97 +23,84 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -#include -#include -#include #include -#include +#include +#include +#include #include +#include +#include +#include #include #include #include #include -#include #include -#include -#include -#include +#include -#include "rest_server.h" -#include "mongoose.h" #include "config_vars.h" -#include "cmdline.h" +#include "mongoose.h" +#include "nav_object_database.h" #include "pugixml.hpp" +#include "rest_server.h" #include "route.h" #include "track.h" -#include "routeman.h" -#include "nav_object_database.h" -extern std::vector g_TrackList; -extern Routeman *g_pRouteMan; - -Route *GPXLoadRoute1(pugi::xml_node &wpt_node, bool b_fullviz, - bool b_layer, bool b_layerviz, int layer_id, - bool b_change); -Track *GPXLoadTrack1(pugi::xml_node &trk_node, bool b_fullviz, - bool b_layer, bool b_layerviz, int layer_id); -RoutePoint *GPXLoadWaypoint1(pugi::xml_node &wpt_node, - wxString def_symbol_name, wxString GUID, - bool b_fullviz, bool b_layer, - bool b_layerviz, int layer_id); +class RESTServerEvent; +wxDEFINE_EVENT(wxEVT_RESTFUL_SERVER, RESTServerEvent); -bool InsertRouteA(Route *pTentRoute, NavObjectCollection1* navobj); -bool InsertTrack(Track *pTentTrack, bool bApplyChanges = false); -bool InsertWpt(RoutePoint *pWp, bool overwrite); +Route* GPXLoadRoute1(pugi::xml_node& wpt_node, bool b_fullviz, bool b_layer, + bool b_layerviz, int layer_id, bool b_change); +Track* GPXLoadTrack1(pugi::xml_node& trk_node, bool b_fullviz, bool b_layer, + bool b_layerviz, int layer_id); +RoutePoint* GPXLoadWaypoint1(pugi::xml_node& wpt_node, wxString def_symbol_name, + wxString GUID, bool b_fullviz, bool b_layer, + bool b_layerviz, int layer_id); +bool InsertRouteA(Route* pTentRoute, NavObjectCollection1* navobj); +bool InsertTrack(Track* pTentTrack, bool bApplyChanges = false); +bool InsertWpt(RoutePoint* pWp, bool overwrite); // Some global variables to handle thread syncronization -int return_status; -std::condition_variable return_status_condition; -std::mutex mx; - - -class RESTServerThread : public wxThread { -public: - RESTServerThread(RESTServer* Launcher); - - ~RESTServerThread(void); - void* Entry(); - void OnExit(void); - -private: - RESTServer *m_pParent; - -}; - - -class RESTServerEvent; -wxDECLARE_EVENT(wxEVT_RESTFUL_SERVER, RESTServerEvent); +static int return_status; +static std::condition_variable return_status_condition; +static std::mutex mx; + +static const char* s_http_addr = "http://0.0.0.0:8000"; +static const char* s_https_addr = "https://0.0.0.0:8443"; +// Is this host a portable? Must use another port to avoid equal IP addres +// conflicts. +static const char* s_http_addr_portable = "http://0.0.0.0:8001"; +static const char* s_https_addr_portable = "https://0.0.0.0:8444"; + +static std::string server_ip; + +static unsigned long long PINtoRandomKey(int dpin) { + using namespace std; + linear_congruential_engine + engine; + engine.seed(dpin); + unsigned long long r = engine(); + return r; +} +/* Used by static function, breaks the ordering. Better off in separate file */ class RESTServerEvent : public wxEvent { public: - RESTServerEvent( - wxEventType commandType = wxEVT_RESTFUL_SERVER, int id = 0) + RESTServerEvent(wxEventType commandType = wxEVT_RESTFUL_SERVER, int id = 0) : wxEvent(id, commandType){}; ~RESTServerEvent(){}; // accessors - void SetPayload(std::shared_ptr data) { - m_payload = data; - } - void SetSource(std::string source){ - m_source_peer = source; - } - void SetAPIKey(std::string key){ - m_api_key = key; - } + void SetPayload(std::shared_ptr data) { m_payload = data; } + void SetSource(std::string source) { m_source_peer = source; } + void SetAPIKey(std::string key) { m_api_key = key; } std::shared_ptr GetPayload() { return m_payload; } // required for sending with wxPostEvent() wxEvent* Clone() const { - RESTServerEvent* newevent = - new RESTServerEvent(*this); + RESTServerEvent* newevent = new RESTServerEvent(*this); newevent->m_payload = this->m_payload; newevent->m_source_peer = this->m_source_peer; newevent->m_api_key = this->m_api_key; @@ -123,43 +110,238 @@ class RESTServerEvent : public wxEvent { std::shared_ptr m_payload; std::string m_source_peer; std::string m_api_key; -private: +}; +// We use the same event handler function for HTTP and HTTPS connections +// fn_data is NULL for plain HTTP, and non-NULL for HTTPS +static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { + RESTServer* parent = static_cast(fn_data); -}; + if (ev == MG_EV_ACCEPT /*&& fn_data != NULL*/) { + struct mg_tls_opts opts; + memset(&opts, 0, sizeof(mg_tls_opts)); + + opts.ca = NULL; //"cert.pem"; // Uncomment to enable two-way SSL + opts.cert = parent->m_cert_file.c_str(); // Certificate PEM file + opts.certkey = parent->m_key_file.c_str(); // The key PEM file + opts.ciphers = NULL; + mg_tls_init(c, &opts); + } else if (ev == MG_EV_TLS_HS) { // Think of this as "start of session" + if (parent) { + RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, ORS_START_OF_SESSION); + parent->AddPendingEvent(Nevent); + } + } else if (ev == MG_EV_HTTP_CHUNK) { + struct mg_http_message* hm = (struct mg_http_message*)ev_data; + if (mg_http_match_uri(hm, "/api/ping")) { + std::string api_key; + struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); + if (api_key_parm.len && api_key_parm.ptr) { + api_key = std::string(api_key_parm.ptr, api_key_parm.len); + } -wxDEFINE_EVENT(wxEVT_RESTFUL_SERVER, RESTServerEvent); + struct mg_str source = mg_http_var(hm->query, mg_str("source")); + + if (source.len) { + std::string source_peer(source.ptr, source.len); + + return_status = -1; + if (parent) { + RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, ORS_CHUNK_LAST); + Nevent.SetSource(source_peer); + Nevent.SetAPIKey(api_key); + parent->AddPendingEvent(Nevent); + } + + std::unique_lock lock{mx}; + while (return_status < 0) { // !predicate + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return_status_condition.wait(lock); + } + lock.unlock(); + } + mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); + } else if (mg_http_match_uri(hm, "/api/rx_object")) { + int MID = ORS_CHUNK_N; + + std::string api_key; + struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); + if (api_key_parm.len && api_key_parm.ptr) { + api_key = std::string(api_key_parm.ptr, api_key_parm.len); + } + + struct mg_str source = mg_http_var(hm->query, mg_str("source")); + + std::string xml_content; + if (hm->chunk.len) + xml_content = std::string(hm->chunk.ptr, hm->chunk.len); + else { + MID = ORS_CHUNK_LAST; + } + + mg_http_delete_chunk(c, hm); + + return_status = -1; + + if (source.len) { + std::string source_peer(source.ptr, source.len); + // printf("%s\n", xml_content.c_str()); + + // std::ofstream b_stream("bodyfile", std::fstream::out | + // std::fstream::binary); b_stream.write(hm->body.ptr, hm->body.len); + + if (parent) { + RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, MID); + if (xml_content.size()) { + auto buffer = std::make_shared(xml_content); + Nevent.SetPayload(buffer); + } + Nevent.SetSource(source_peer); + Nevent.SetAPIKey(api_key); + parent->AddPendingEvent(Nevent); + } + + if (MID == ORS_CHUNK_LAST) { + std::unique_lock lock{mx}; + while (return_status < 0) { // !predicate + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return_status_condition.wait(lock); + } + lock.unlock(); + + printf("Reply: %d\n", return_status); + mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); + parent->UpdateRouteMgr(); + } + } + } + + } else if (ev == MG_EV_HTTP_MSG) { +#if 0 + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + if (mg_http_match_uri(hm, "/api/ping")) { + std::string api_key; + struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); + if(api_key_parm.len && api_key_parm.ptr){ + api_key = std::string(api_key_parm.ptr, api_key_parm.len); + } + + + struct mg_str source = mg_http_var(hm->query, mg_str("source")); + + if(source.len) + { + std::string source_peer(source.ptr, source.len); + + return_status = -1; + + if (parent){ + RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, 0); + Nevent.SetSource(source_peer); + Nevent.SetAPIKey(api_key); + parent->AddPendingEvent(Nevent); + } + + std::unique_lock lock{mx}; + while (return_status < 0) { // !predicate + std::this_thread::sleep_for (std::chrono::milliseconds(100)); + return_status_condition.wait(lock); + } + lock.unlock(); + } + + mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); + } else if (mg_http_match_uri(hm, "/api/rx_object")) { + + std::string api_key; + struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); + if(api_key_parm.len && api_key_parm.ptr){ + api_key = std::string(api_key_parm.ptr, api_key_parm.len); + } + + + struct mg_str source = mg_http_var(hm->query, mg_str("source")); + + if(source.len && hm->body.len ) + { + std::string xml_content(hm->body.ptr, hm->body.len); + std::string source_peer(source.ptr, source.len); + + return_status = -1; + + if (parent){ + RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, 0); + auto buffer = std::make_shared(xml_content); + Nevent.SetPayload(buffer); + Nevent.SetSource(source_peer); + Nevent.SetAPIKey(api_key); + parent->AddPendingEvent(Nevent); + } + + std::unique_lock lock{mx}; + while (return_status < 0) { // !predicate + std::this_thread::sleep_for (std::chrono::milliseconds(100)); + return_status_condition.wait(lock); + } + lock.unlock(); + } + + mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); + } +#endif + } + (void)fn_data; +} + +std::string PINtoRandomKeyString(int dpin) { + unsigned long long pin = PINtoRandomKey(dpin); + char buffer[100]; + snprintf(buffer, sizeof(buffer) - 1, "%0llX", pin); + return std::string(buffer); +} + +class RESTServerThread : public wxThread { +public: + RESTServerThread(RESTServer* Launcher, bool& m_portable); + + ~RESTServerThread(void); + void* Entry(); + void OnExit(void); + +private: + RESTServer* m_pParent; + bool& m_portable; +}; //======================================================================== /* RESTServer implementation * */ -RESTServer::RESTServer(RestServerDlgCtx ctx) - : m_Thread_run_flag(-1), m_dlg_ctx(ctx) -{ - +RESTServer::RESTServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) + : m_Thread_run_flag(-1), + m_dlg_ctx(ctx), + m_route_ctx(route_ctx), + m_portable(portable) { m_pin_dialog = 0; // Prepare the wxEventHandler to accept events from the actual hardware thread - Bind(wxEVT_RESTFUL_SERVER, &RESTServer::HandleServerMessage, - this); - + Bind(wxEVT_RESTFUL_SERVER, &RESTServer::HandleServerMessage, this); } -RESTServer::~RESTServer() { } +RESTServer::~RESTServer() {} bool RESTServer::StartServer(std::string certificate_location) { - m_certificate_directory = certificate_location; - m_cert_file = m_certificate_directory + std::string("cert.pem"); // Certificate PEM file - m_key_file = m_certificate_directory + std::string("key.pem"); // The key PEM file - + m_cert_file = m_certificate_directory + + std::string("cert.pem"); // Certificate PEM file + m_key_file = + m_certificate_directory + std::string("key.pem"); // The key PEM file // Load persistent config info LoadConfig(); // Kick off the Server thread - SetSecondaryThread(new RESTServerThread(this)); + SetSecondaryThread(new RESTServerThread(this, m_portable)); SetThreadRunFlag(1); GetSecondaryThread()->Run(); @@ -167,11 +349,9 @@ bool RESTServer::StartServer(std::string certificate_location) { } void RESTServer::StopServer() { - wxLogMessage( - wxString::Format("Stopping REST service")); + wxLogMessage(wxString::Format("Stopping REST service")); - Unbind(wxEVT_RESTFUL_SERVER, &RESTServer::HandleServerMessage, - this); + Unbind(wxEVT_RESTFUL_SERVER, &RESTServer::HandleServerMessage, this); // Kill off the Secondary RX Thread if alive if (m_pSecondary_Thread) { @@ -199,14 +379,13 @@ void RESTServer::StopServer() { } } -bool RESTServer::LoadConfig( void ) -{ - if( TheBaseConfig() ) { +bool RESTServer::LoadConfig(void) { + if (TheBaseConfig()) { TheBaseConfig()->SetPath("/Settings/RESTServer"); wxString key_string; - TheBaseConfig()->Read("ServerKeys", &key_string ); + TheBaseConfig()->Read("ServerKeys", &key_string); wxStringTokenizer st(key_string, ";"); while (st.HasMoreTokens()) { wxString s1 = st.GetNextToken(); @@ -215,76 +394,61 @@ bool RESTServer::LoadConfig( void ) m_key_map[client_name.ToStdString()] = client_key.ToStdString(); } - TheBaseConfig()->Read("ServerOverwriteDuplicates", &m_b_overwrite, 0 ); - + TheBaseConfig()->Read("ServerOverwriteDuplicates", &m_b_overwrite, 0); } return true; } -bool RESTServer::SaveConfig( void ) -{ - if( TheBaseConfig() ) { - TheBaseConfig()->SetPath( "/Settings/RESTServer" ); +bool RESTServer::SaveConfig(void) { + if (TheBaseConfig()) { + TheBaseConfig()->SetPath("/Settings/RESTServer"); wxString key_string; - for (auto it : m_key_map){ - wxString item = it.first.c_str() + wxString(":") + it.second.c_str() + wxString(";"); + for (auto it : m_key_map) { + wxString item = + it.first.c_str() + wxString(":") + it.second.c_str() + wxString(";"); key_string += item; } - TheBaseConfig()->Write("ServerKeys", key_string ); - - TheBaseConfig()->Write("ServerOverwriteDuplicates", m_b_overwrite ); + TheBaseConfig()->Write("ServerKeys", key_string); + TheBaseConfig()->Write("ServerOverwriteDuplicates", m_b_overwrite); } return true; } -unsigned long long PINtoRandomKey( int dpin) { - std::linear_congruential_engine engine; - engine.seed( dpin ); - unsigned long long r = engine(); - return r; - -} - -std::string PINtoRandomKeyString( int dpin) { - unsigned long long pin = PINtoRandomKey(dpin); - char buffer[100]; - snprintf(buffer, sizeof(buffer)-1, "%0llX", pin); - return std::string(buffer); -} - void RESTServer::HandleServerMessage(RESTServerEvent& event) { - int return_stat = RESTServerResult::RESULT_GENERIC_ERROR; - if (event.GetId() == ORS_START_OF_SESSION){ + if (event.GetId() == ORS_START_OF_SESSION) { // Prepare a temp file to catch chuncks that might follow - m_tempUploadFilePath = wxFileName::CreateTempFileName("ocpn_tul").ToStdString(); + m_tempUploadFilePath = + wxFileName::CreateTempFileName("ocpn_tul").ToStdString(); - m_ul_stream.open(m_tempUploadFilePath.c_str(), std::ios::out | std::ios::trunc); + m_ul_stream.open(m_tempUploadFilePath.c_str(), + std::ios::out | std::ios::trunc); if (!m_ul_stream.is_open()) { - wxLogMessage("REST_server: Cannot open %s for write", m_tempUploadFilePath); - m_tempUploadFilePath.clear(); // reset for next time. + wxLogMessage("REST_server: Cannot open %s for write", + m_tempUploadFilePath); + m_tempUploadFilePath.clear(); // reset for next time. return; } return; } - if (event.GetId() == ORS_CHUNK_N){ + if (event.GetId() == ORS_CHUNK_N) { auto p = event.GetPayload(); - std::string *payload = p.get(); + std::string* payload = p.get(); - //printf("%s\n", payload->c_str()); - // Stream out to temp file + // printf("%s\n", payload->c_str()); + // Stream out to temp file if (m_tempUploadFilePath.size() && m_ul_stream.is_open()) { - m_ul_stream.write( payload->c_str(), payload->size()); + m_ul_stream.write(payload->c_str(), payload->size()); } return; } - if (event.GetId() == ORS_CHUNK_LAST){ + if (event.GetId() == ORS_CHUNK_LAST) { // Cancel existing dialog m_dlg_ctx.close_dialog(m_pin_dialog); @@ -293,20 +457,19 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { m_ul_stream.close(); // Server thread might be waiting for (return_status >= 0) on notify_one() - return_stat = RESTServerResult::RESULT_GENERIC_ERROR; // generic error - + return_stat = RESTServerResult::RESULT_GENERIC_ERROR; // generic error } // Look up the api key in the hash map. std::string api_found; - for (auto it : m_key_map){ - if (it.first == event.m_source_peer && it.second == event.m_api_key){ + for (auto it : m_key_map) { + if (it.first == event.m_source_peer && it.second == event.m_api_key) { api_found = it.second; break; } } - if (!api_found.size()){ + if (!api_found.size()) { // Need a new PIN confirmation m_dPIN = wxMin(rand() % 10000 + 1, 9999); m_sPIN.Printf("%04d", m_dPIN); @@ -319,14 +482,15 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { // And persist it SaveConfig(); - wxString hmsg(event.m_source_peer.c_str()); hmsg += " "; - hmsg += _("wants to send you new data.\nPlease enter the following PIN number on "); + hmsg += + _("wants to send you new data.\nPlease enter the following PIN number " + "on "); hmsg += wxString(event.m_source_peer.c_str()); hmsg += _(" to pair with this device.\n"); - m_pin_dialog = m_dlg_ctx.show_dialog(hmsg.ToStdString(), - m_sPIN.ToStdString()); + m_pin_dialog = + m_dlg_ctx.show_dialog(hmsg.ToStdString(), m_sPIN.ToStdString()); return_status = RESTServerResult::RESULT_NEW_PIN_REQUESTED; @@ -339,375 +503,161 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { return_status = RESTServerResult::RESULT_NO_ERROR; } - - - // GUI dialogs can go here.... bool b_cont; b_cont = true; - if (b_cont) {\ - // Load the GPX file + if (b_cont) { // Load the GPX file pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(m_tempUploadFilePath.c_str()); - if (result.status == pugi::status_ok){ - m_tempUploadFilePath.clear(); // empty for next time + if (result.status == pugi::status_ok) { + m_tempUploadFilePath.clear(); // empty for next time pugi::xml_node objects = doc.child("gpx"); for (pugi::xml_node object = objects.first_child(); object; - object = object.next_sibling()) { + object = object.next_sibling()) { if (!strcmp(object.name(), "rte")) { - Route *pRoute = NULL; + Route* pRoute = NULL; pRoute = GPXLoadRoute1(object, true, false, false, 0, true); // Check for duplicate GUID - if (g_pRouteMan){ - bool b_add = true; - bool b_overwrite_one = false; - Route *duplicate = g_pRouteMan->FindRouteByGUID(pRoute->GetGUID()); - if (duplicate){ - if (!m_b_overwrite){ - auto result = m_dlg_ctx.run_accept_object_dlg( - _("The received route already exists on this system.\nReplace?"), + bool b_add = true; + bool b_overwrite_one = false; + Route* duplicate = m_route_ctx.find_route_by_guid(pRoute->GetGUID()); + if (duplicate) { + if (!m_b_overwrite) { + auto result = m_dlg_ctx.run_accept_object_dlg( + _("The received route already exists on this " + "system.\nReplace?"), _("Always replace objects from this source?")); - if (result.status != ID_STG_OK){ - b_add = false; - return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; - } - else{ - m_b_overwrite = result.check1_value; - b_overwrite_one = true; - SaveConfig(); - } + if (result.status != ID_STG_OK) { + b_add = false; + return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; + } else { + m_b_overwrite = result.check1_value; + b_overwrite_one = true; + SaveConfig(); } + } - if (m_b_overwrite || b_overwrite_one){ - // Remove the existing duplicate route before adding new route - g_pRouteMan->DeleteRoute(duplicate, - NavObjectChanges::getInstance()); - } + if (m_b_overwrite || b_overwrite_one) { + // Remove the existing duplicate route before adding new route + m_route_ctx.delete_route(duplicate); } + } - if (b_add) { - // And here is the payoff.... + if (b_add) { + // And here is the payoff.... - // Add the route to the global list - NavObjectCollection1 pSet; + // Add the route to the global list + NavObjectCollection1 pSet; - if (InsertRouteA(pRoute, &pSet)) - return_stat = RESTServerResult::RESULT_NO_ERROR; - else - return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; - m_dlg_ctx.top_level_refresh(); - } + if (InsertRouteA(pRoute, &pSet)) + return_stat = RESTServerResult::RESULT_NO_ERROR; + else + return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; + m_dlg_ctx.top_level_refresh(); } } else if (!strcmp(object.name(), "trk")) { - Track *pRoute = NULL; + Track* pRoute = NULL; pRoute = GPXLoadTrack1(object, true, false, false, 0); // Check for duplicate GUID - if (g_pRouteMan){ - bool b_add = true; - bool b_overwrite_one = false; - - Track *duplicate = g_pRouteMan->FindTrackByGUID(pRoute->m_GUID); - if (duplicate){ - if (!m_b_overwrite){ - auto result = m_dlg_ctx.run_accept_object_dlg( - _("The received track already exists on this system.\nReplace?"), + bool b_add = true; + bool b_overwrite_one = false; + + Track* duplicate = m_route_ctx.find_track_by_guid(pRoute->m_GUID); + if (duplicate) { + if (!m_b_overwrite) { + auto result = m_dlg_ctx.run_accept_object_dlg( + _("The received track already exists on this " + "system.\nReplace?"), _("Always replace objects from this source?")); - if (result.status != ID_STG_OK){ - b_add = false; - return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; - } - else{ - m_b_overwrite = result.check1_value; - b_overwrite_one = true; - SaveConfig(); - } - } - - if (m_b_overwrite || b_overwrite_one){ - auto it = std::find(g_TrackList.begin(), g_TrackList.end(), duplicate); - if (it != g_TrackList.end()) { - g_TrackList.erase(it); - } - delete duplicate; + if (result.status != ID_STG_OK) { + b_add = false; + return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; + } else { + m_b_overwrite = result.check1_value; + b_overwrite_one = true; + SaveConfig(); } } + if (m_b_overwrite || b_overwrite_one) { + m_route_ctx.delete_track(duplicate); + } + } - if (b_add) { - // And here is the payoff.... + if (b_add) { + // And here is the payoff.... - // Add the route to the global list - NavObjectCollection1 pSet; + // Add the route to the global list + NavObjectCollection1 pSet; - if (InsertTrack(pRoute, false)) - return_stat = RESTServerResult::RESULT_NO_ERROR; - else - return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; - m_dlg_ctx.top_level_refresh(); - } + if (InsertTrack(pRoute, false)) + return_stat = RESTServerResult::RESULT_NO_ERROR; + else + return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; + m_dlg_ctx.top_level_refresh(); } } else if (!strcmp(object.name(), "wpt")) { - RoutePoint *pWp = NULL; + RoutePoint* pWp = NULL; pWp = GPXLoadWaypoint1(object, "circle", "", false, false, false, 0); - pWp->m_bIsolatedMark = true; // This is an isolated mark - // Check for duplicate GUID - if (g_pRouteMan){ - bool b_add = true; - bool b_overwrite_one = false; - - RoutePoint *duplicate = WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon); - if (duplicate){ - if (!m_b_overwrite){ - auto result = m_dlg_ctx.run_accept_object_dlg( - _("The received waypoint already exists on this system.\nReplace?"), + bool b_add = true; + bool b_overwrite_one = false; + + RoutePoint* duplicate = + WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon); + if (duplicate) { + if (!m_b_overwrite) { + auto result = m_dlg_ctx.run_accept_object_dlg( + _("The received waypoint already exists on this " + "system.\nReplace?"), _("Always replace objects from this source?")); - if (result.status != ID_STG_OK){ - b_add = false; - return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; - } - else{ - m_b_overwrite = result.check1_value; - b_overwrite_one = true; - SaveConfig(); - } + if (result.status != ID_STG_OK) { + b_add = false; + return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; + } else { + m_b_overwrite = result.check1_value; + b_overwrite_one = true; + SaveConfig(); } } + } - if (b_add) { - // And here is the payoff.... - if (InsertWpt(pWp, m_b_overwrite || b_overwrite_one)) - return_stat = RESTServerResult::RESULT_NO_ERROR; - else - return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; + if (b_add) { + // And here is the payoff.... + if (InsertWpt(pWp, m_b_overwrite || b_overwrite_one)) + return_stat = RESTServerResult::RESULT_NO_ERROR; + else + return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; - m_dlg_ctx.top_level_refresh(); - } + m_dlg_ctx.top_level_refresh(); } } } } - } - else { + } else { return_stat = RESTServerResult::RESULT_OBJECT_REJECTED; } - return_status = return_stat; std::lock_guard lock{mx}; return_status_condition.notify_one(); } - - static const char* s_http_addr = "http://0.0.0.0:8000"; // HTTP port - static const char* s_https_addr = "https://0.0.0.0:8443"; // HTTPS port - // Is this host a portable? Must use another port to avoid equal IP addres conflicts. - static const char* s_http_addr_portable = "http://0.0.0.0:8001"; // HTTP port - static const char* s_https_addr_portable = "https://0.0.0.0:8444"; // HTTPS port - - - -// We use the same event handler function for HTTP and HTTPS connections -// fn_data is NULL for plain HTTP, and non-NULL for HTTPS -static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { - RESTServer *parent = static_cast(fn_data); - - if (ev == MG_EV_ACCEPT /*&& fn_data != NULL*/) { - struct mg_tls_opts opts; - memset(&opts, 0, sizeof(mg_tls_opts)); - - opts.ca = NULL; //"cert.pem"; // Uncomment to enable two-way SSL - opts.cert = parent->m_cert_file.c_str(); // Certificate PEM file - opts.certkey = parent->m_key_file.c_str(); // The key PEM file - opts.ciphers = NULL; - mg_tls_init(c, &opts); - } else if(ev == MG_EV_TLS_HS){ // Think of this as "start of session" - if (parent){ - RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, ORS_START_OF_SESSION); - parent->AddPendingEvent(Nevent); - } - } else if (ev == MG_EV_HTTP_CHUNK){ - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - if (mg_http_match_uri(hm, "/api/ping")) { - std::string api_key; - struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); - if(api_key_parm.len && api_key_parm.ptr){ - api_key = std::string(api_key_parm.ptr, api_key_parm.len); - } - - - struct mg_str source = mg_http_var(hm->query, mg_str("source")); - - if(source.len) - { - std::string source_peer(source.ptr, source.len); - - return_status = -1; - if (parent){ - RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, ORS_CHUNK_LAST); - Nevent.SetSource(source_peer); - Nevent.SetAPIKey(api_key); - parent->AddPendingEvent(Nevent); - } - - std::unique_lock lock{mx}; - while (return_status < 0) { // !predicate - std::this_thread::sleep_for (std::chrono::milliseconds(100)); - return_status_condition.wait(lock); - } - lock.unlock(); - } - mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); - } else if (mg_http_match_uri(hm, "/api/rx_object")) { - int MID = ORS_CHUNK_N; - - std::string api_key; - struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); - if(api_key_parm.len && api_key_parm.ptr){ - api_key = std::string(api_key_parm.ptr, api_key_parm.len); - } - - struct mg_str source = mg_http_var(hm->query, mg_str("source")); - - std::string xml_content; - if (hm->chunk.len) - xml_content = std::string(hm->chunk.ptr, hm->chunk.len); - else { - MID = ORS_CHUNK_LAST; - } - - mg_http_delete_chunk(c, hm); - - return_status = -1; - - if(source.len) - { - std::string source_peer(source.ptr, source.len); - //printf("%s\n", xml_content.c_str()); - - //std::ofstream b_stream("bodyfile", std::fstream::out | std::fstream::binary); - //b_stream.write(hm->body.ptr, hm->body.len); - - - if (parent){ - RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, MID); - if (xml_content.size()) { - auto buffer = std::make_shared(xml_content); - Nevent.SetPayload(buffer); - } - Nevent.SetSource(source_peer); - Nevent.SetAPIKey(api_key); - parent->AddPendingEvent(Nevent); - } - - if (MID == ORS_CHUNK_LAST) { - std::unique_lock lock{mx}; - while (return_status < 0) { // !predicate - std::this_thread::sleep_for (std::chrono::milliseconds(100)); - return_status_condition.wait(lock); - } - lock.unlock(); - - printf("Reply: %d\n", return_status); - mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); - parent->UpdateRouteMgr(); - } - } - } - - } else if (ev == MG_EV_HTTP_MSG) { - #if 0 - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - if (mg_http_match_uri(hm, "/api/ping")) { - std::string api_key; - struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); - if(api_key_parm.len && api_key_parm.ptr){ - api_key = std::string(api_key_parm.ptr, api_key_parm.len); - } - - - struct mg_str source = mg_http_var(hm->query, mg_str("source")); - - if(source.len) - { - std::string source_peer(source.ptr, source.len); - - return_status = -1; - - if (parent){ - RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, 0); - Nevent.SetSource(source_peer); - Nevent.SetAPIKey(api_key); - parent->AddPendingEvent(Nevent); - } - - std::unique_lock lock{mx}; - while (return_status < 0) { // !predicate - std::this_thread::sleep_for (std::chrono::milliseconds(100)); - return_status_condition.wait(lock); - } - lock.unlock(); - } - - mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); - } else if (mg_http_match_uri(hm, "/api/rx_object")) { - - std::string api_key; - struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); - if(api_key_parm.len && api_key_parm.ptr){ - api_key = std::string(api_key_parm.ptr, api_key_parm.len); - } - - - struct mg_str source = mg_http_var(hm->query, mg_str("source")); - - if(source.len && hm->body.len ) - { - std::string xml_content(hm->body.ptr, hm->body.len); - std::string source_peer(source.ptr, source.len); - - return_status = -1; - - if (parent){ - RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, 0); - auto buffer = std::make_shared(xml_content); - Nevent.SetPayload(buffer); - Nevent.SetSource(source_peer); - Nevent.SetAPIKey(api_key); - parent->AddPendingEvent(Nevent); - } - - std::unique_lock lock{mx}; - while (return_status < 0) { // !predicate - std::this_thread::sleep_for (std::chrono::milliseconds(100)); - return_status_condition.wait(lock); - } - lock.unlock(); - } - - mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); - } - #endif - } - (void) fn_data; -} - -std::string server_ip; - -RESTServerThread::RESTServerThread(RESTServer* Launcher) { +RESTServerThread::RESTServerThread(RESTServer* Launcher, bool& portable) + : m_portable(portable) { m_pParent = Launcher; // This thread's immediate "parent" server_ip = s_https_addr; // If Portable use another port - if (g_bportable) { + if (m_portable) { server_ip = s_https_addr_portable; - wxString sip(server_ip); - wxLogMessage("Portable REST server IP: Port " + sip); + wxString sip(server_ip); + wxLogMessage("Portable REST server IP: Port " + sip); } Create(); @@ -719,21 +669,21 @@ void RESTServerThread::OnExit(void) {} void* RESTServerThread::Entry() { bool not_done = true; - m_pParent->SetSecThreadActive(); // I am alive + m_pParent->SetSecThreadActive(); // I am alive - struct mg_mgr mgr; // Event manager - mg_log_set(MG_LL_DEBUG); // Set log level - mg_mgr_init(&mgr); // Initialise event manager + struct mg_mgr mgr; // Event manager + mg_log_set(MG_LL_DEBUG); // Set log level + mg_mgr_init(&mgr); // Initialise event manager - mg_http_listen(&mgr, server_ip.c_str(), fn, m_pParent); // Create HTTPS listener - //mg_http_listen(&mgr, s_https_addr, fn, (void *) 1); // (HTTPS listener) + mg_http_listen(&mgr, server_ip.c_str(), fn, + m_pParent); // Create HTTPS listener + // mg_http_listen(&mgr, s_https_addr, fn, (void *) 1); // (HTTPS listener) - for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop + for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop mg_mgr_free(&mgr); - m_pParent->SetSecThreadInActive(); // I am dead + m_pParent->SetSecThreadInActive(); // I am dead m_pParent->m_Thread_run_flag = -1; return 0; } - From f7c4bf75da6f45a42790a682a26514aca9c32381 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Wed, 22 Mar 2023 22:18:15 +0100 Subject: [PATCH 08/63] garmin_wrapper et. al.: Add a callback. --- include/comm_n0183_output.h | 4 +- include/garmin_wrapper.h | 1 + include/n0183_ctx_factory.h | 8 ++++ src/comm_n0183_output.cpp | 1 + src/garmin_wrapper.cpp | 89 +++++++++++++++++-------------------- 5 files changed, 53 insertions(+), 50 deletions(-) diff --git a/include/comm_n0183_output.h b/include/comm_n0183_output.h index a14772f994..395c81bb4c 100644 --- a/include/comm_n0183_output.h +++ b/include/comm_n0183_output.h @@ -54,12 +54,14 @@ class N0183DlgCtx { std::function set_range; std::function pulse; std::function set_message; + std::function confirm_overwrite; N0183DlgCtx() : set_value([](int) {}), set_range([](int) {}), pulse([](void) {}), - set_message([](const std::string&) {}) {} + set_message([](const std::string&) {}), + confirm_overwrite([]() { return true; }) {} }; diff --git a/include/garmin_wrapper.h b/include/garmin_wrapper.h index 58395b1948..644b5c1697 100644 --- a/include/garmin_wrapper.h +++ b/include/garmin_wrapper.h @@ -31,6 +31,7 @@ #include #include "route.h" +//#include "navutil.h" #include "comm_n0183_output.h" /* Wrapped interface from higher level objects */ diff --git a/include/n0183_ctx_factory.h b/include/n0183_ctx_factory.h index 500a5c17a1..af3c298cd6 100644 --- a/include/n0183_ctx_factory.h +++ b/include/n0183_ctx_factory.h @@ -32,6 +32,13 @@ #include "SendToGpsDlg.h" #include "comm_n0183_output.h" +static bool ConfirmOverwrite() { + int r = OCPNMessageBox(NULL, _("Overwrite Garmin device route number 1?"), + _("OpenCPN Message"), + wxOK | wxCANCEL | wxICON_QUESTION); + return r == wxID_OK; +} + static N0183DlgCtx GetDialogCtx(SendToGpsDlg* dialog) { N0183DlgCtx dlg_ctx; @@ -49,6 +56,7 @@ static N0183DlgCtx GetDialogCtx(SendToGpsDlg* dialog) { dialog->GetProgressGauge()->Pulse(); }; dlg_ctx.set_message = [dialog](const std::string& s) { dialog->SetMessage(wxString(s)); }; + dlg_ctx.confirm_overwrite = []() { return ConfirmOverwrite(); }; return dlg_ctx; } diff --git a/src/comm_n0183_output.cpp b/src/comm_n0183_output.cpp index 48411f2acd..21dd3f0635 100644 --- a/src/comm_n0183_output.cpp +++ b/src/comm_n0183_output.cpp @@ -460,6 +460,7 @@ int SendRouteToGPS_N0183(Route* pr, const wxString& com_name, } dlg_ctx.set_value(40); + lret_val = Garmin_GPS_SendRoute(short_com, pr, dlg_ctx); if (lret_val != 1) { MESSAGE_LOG << "Error Sending Route to Garmin GPS on port: " << short_com diff --git a/src/garmin_wrapper.cpp b/src/garmin_wrapper.cpp index 2d646f8053..2a4b392eb7 100644 --- a/src/garmin_wrapper.cpp +++ b/src/garmin_wrapper.cpp @@ -27,10 +27,6 @@ #include "garmin_gps.h" #include "gpsserial.h" -#ifndef CLIAPP -#include "gui_lib.h" -#endif - #define GPS_DEBUG extern char last_error[]; @@ -259,62 +255,57 @@ int Garmin_GPS_SendRoute(const wxString &port_name, Route *pr, GPS_PWay *pprouteway; int32 npacks = GPS_A200_Get(port_name.mb_str(), &pprouteway); if (npacks < 0) return npacks; - dlg_ctx.set_value(40); - // Iterate on the packets, finding the first route number from [0..9] that - // is not present +// Iterate on the packets, finding the first route number from [0..9] that +// is not present - // An array of route numbers, set element to true as encountered - bool brn[10]; - for (int i = 0; i < 10; i++) brn[i] = false; +// An array of route numbers, set element to true as encountered +bool brn[10]; +for (int i = 0; i < 10; i++) brn[i] = false; - for (int ip = 0; ip < npacks; ip++) { - GPS_PWay pway = pprouteway[ip]; - if (pway->isrte) { - if ((pway->rte_num < 10)) brn[pway->rte_num] = true; - } - } +for (int ip = 0; ip < npacks; ip++) { + GPS_PWay pway = pprouteway[ip]; + if (pway->isrte) { + if ((pway->rte_num < 10)) brn[pway->rte_num] = true; + } +} - // Find the first candidate within [1..9] that is unused - bool bfound_empty = false; - for (int i = 1; i < 10; i++) { - if (brn[i] == false) { - route_number = i; - bfound_empty = true; - break; - } - } - GPS_Diag("Using route number: %d", route_number); - -#ifndef CLIAPP // FIXME (leamas) Use a callback - // Ask the user if it is all right to overwrite - if (!bfound_empty) { - int rv = OCPNMessageBox( - NULL, _("Overwrite Garmin device route number 1?"), - _("OpenCPN Message"), wxOK | wxCANCEL | wxICON_QUESTION); - if (rv != wxID_OK) return 0; - } -#endif +// Find the first candidate within [1..9] that is unused +bool bfound_empty = false; +for (int i = 1; i < 10; i++) { + if (brn[i] == false) { + route_number = i; + bfound_empty = true; + break; } +} +GPS_Diag("Using route number: %d", route_number); - // Based on the route transfer protocol create the array of transfer packets - GPS_SWay **ppway; - int elements = 0; - if (gps_route_transfer == pA201) - ppway = Garmin_GPS_Create_A201_Route(pr, route_number, &elements); - else - ppway = Garmin_GPS_Create_A200_Route(pr, route_number, &elements); +// Ask the user if it is all right to overwrite +if (!bfound_empty) { + if (!dlg_ctx.confirm_overwrite()) { + return 0; + } +} +} - // Transmit the Route to the GPS receiver - int xfer_result = GPS_Command_Send_Route(port_name.mb_str(), ppway, elements); - ret_val = xfer_result; +// Based on the route transfer protocol create the array of transfer packets +GPS_SWay **ppway; +int elements = 0; +if (gps_route_transfer == pA201) +ppway = Garmin_GPS_Create_A201_Route(pr, route_number, &elements); +else +ppway = Garmin_GPS_Create_A200_Route(pr, route_number, &elements); - // Free all the memory - for (int i = 0; i < elements; i++) GPS_Way_Del(&ppway[i]); +// Transmit the Route to the GPS receiver +int xfer_result = GPS_Command_Send_Route(port_name.mb_str(), ppway, elements); +ret_val = xfer_result; - free(ppway); +// Free all the memory +for (int i = 0; i < elements; i++) GPS_Way_Del(&ppway[i]); +free(ppway); dlg_ctx.set_value(80); VerifyPortClosed(); From 1f5b634e9811c770327d8d87d951c286fde08ba4 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 23 Mar 2023 07:17:49 +0100 Subject: [PATCH 09/63] routeman et. al.: Refactor callbacks. --- include/routeman.h | 27 +++++++++++++++++++++++---- include/routeman_gui.h | 3 +++ src/console.cpp | 2 +- src/ocpn_app.cpp | 2 +- src/routeman.cpp | 27 ++++++++------------------- src/routeman_gui.cpp | 27 +++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 25 deletions(-) diff --git a/include/routeman.h b/include/routeman.h index 68cdace6f1..1e14926af3 100644 --- a/include/routeman.h +++ b/include/routeman.h @@ -63,7 +63,7 @@ class markicon_description_list_type; WX_DEFINE_SORTED_ARRAY(MarkIcon *, SortedArrayOfMarkIcon); WX_DEFINE_ARRAY(MarkIcon *, ArrayOfMarkIcon); -// Callbacks for RoutePropDlg +/** Callbacks for RoutePropDlg */ struct RoutePropDlgCtx { std::function SetRouteAndUpdate; std::function SetEnroutePoint; @@ -75,6 +75,25 @@ struct RoutePropDlgCtx { { } }; +/** Routeman callbacks. */ + +struct RoutemanDlgCtx { + std::function confirm_delete_ais_mob; + std::function get_global_colour; + std::function show_with_fresh_fonts; + std::function clear_console_background; + std::function route_mgr_dlg_update_list_ctrl; + + RoutemanDlgCtx() + : confirm_delete_ais_mob([]() { return true; }), + get_global_colour([](wxString c) { return *wxBLACK; }), + show_with_fresh_fonts([]() { }), + clear_console_background([]() { }), + route_mgr_dlg_update_list_ctrl([]() { }) + {} +}; + + //---------------------------------------------------------------------------- // Routeman //---------------------------------------------------------------------------- @@ -84,8 +103,8 @@ class Routeman { friend class RoutemanGui; public: - Routeman(struct RoutePropDlgCtx ctx, - std::function RouteMgrDlgUpdateListCtrl, + Routeman(struct RoutePropDlgCtx prop_dlg_ctx, + struct RoutemanDlgCtx route_dlg_ctx, NmeaLog& nmea_log); ~Routeman(); @@ -189,7 +208,7 @@ friend class RoutemanGui; double m_arrival_min; int m_arrival_test; struct RoutePropDlgCtx m_prop_dlg_ctx; - std::function m_route_mgr_dlg_update_list_ctrl; + struct RoutemanDlgCtx m_route_dlg_ctx; NmeaLog& m_nmea_log; ObsListener msg_sent_listener; diff --git a/include/routeman_gui.h b/include/routeman_gui.h index 77ea87a1e0..2768668f18 100644 --- a/include/routeman_gui.h +++ b/include/routeman_gui.h @@ -32,10 +32,13 @@ class RoutemanGui { public: RoutemanGui(Routeman& routeman) : m_routeman(routeman) {} + static RoutemanDlgCtx GetDlgCtx(); + void DeleteAllTracks(); void DeleteTrack(Track *pTrack); bool UpdateProgress(); + private: void DoAdvance(void); diff --git a/src/console.cpp b/src/console.cpp index 39581c2021..0f44a7ceb9 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -170,7 +170,7 @@ static void InitRouteman() { struct RoutePropDlgCtx ctx; auto RouteMgrDlgUpdateListCtrl = [&]() {}; static NmeaLogDummy dummy_log; - g_pRouteMan = new Routeman(ctx, RouteMgrDlgUpdateListCtrl, dummy_log); + g_pRouteMan = new Routeman(ctx, RoutemanDlgCtx(), dummy_log); } namespace safe_mode { diff --git a/src/ocpn_app.cpp b/src/ocpn_app.cpp index fd0b0171a6..956335baf2 100644 --- a/src/ocpn_app.cpp +++ b/src/ocpn_app.cpp @@ -1281,7 +1281,7 @@ bool MyApp::OnInit() { pRouteManagerDialog->UpdateRouteListCtrl(); }; - g_pRouteMan = new Routeman(ctx, RouteMgrDlgUpdateListCtrl, + g_pRouteMan = new Routeman(ctx, RoutemanGui::GetDlgCtx(), NMEALogWindow::Get()); // Init the Selectable Route Items List diff --git a/src/routeman.cpp b/src/routeman.cpp index ed4b34042b..9f598442b6 100644 --- a/src/routeman.cpp +++ b/src/routeman.cpp @@ -52,25 +52,15 @@ #include "nav_object_database.h" #include "navutil_base.h" #include "navutil.h" -#include "ocpn_app.h" -#include "ocpn_frame.h" +#include "nmea_ctx_factory.h" +#include "nmea_log.h" #include "OCPNPlatform.h" #include "own_ship.h" -#include "pluginmanager.h" #include "route.h" -#include "routemanagerdialog.h" #include "routeman.h" -#include "RoutePropDlgImpl.h" -#include "styles.h" #include "svg_utils.h" #include "track.h" -#ifndef CLIAPP -#include "color_handler.h" -#include "concanv.h" -#include "gui_lib.h" -#endif - #ifdef __ANDROID__ #include "androidUTIL.h" #endif @@ -129,11 +119,12 @@ void appendOSDirSlash(wxString *pString); //-------------------------------------------------------------------------------- Routeman::Routeman(struct RoutePropDlgCtx ctx, - std::function dlg_update_list_ctrl, + struct RoutemanDlgCtx route_dlg_ctx, NmeaLog& nmea_log) - : m_NMEA0183(NmeaCtxFactory()), m_nmea_log(nmea_log) { - m_prop_dlg_ctx = ctx; - m_route_mgr_dlg_update_list_ctrl = dlg_update_list_ctrl; + : m_prop_dlg_ctx(ctx), + m_route_dlg_ctx(route_dlg_ctx), + m_NMEA0183(NmeaCtxFactory()), + m_nmea_log(nmea_log) { pActiveRoute = NULL; pActivePoint = NULL; pRouteActivatePoint = NULL; @@ -772,9 +763,7 @@ bool Routeman::DeleteRoute(Route *pRoute, NavObjectChanges* nav_obj_changes) { pSelect->DeleteAllSelectableRouteSegments(pRoute); pRouteList->DeleteObject(pRoute); - m_route_mgr_dlg_update_list_ctrl(); // Update the RouteManagerDialog - ///if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - /// pRouteManagerDialog->UpdateRouteListCtrl(); + m_route_dlg_ctx.route_mgr_dlg_update_list_ctrl(); // walk the route, tentatively deleting/marking points used only by this // route diff --git a/src/routeman_gui.cpp b/src/routeman_gui.cpp index 8cf4c3c99c..6b5a1f3f08 100644 --- a/src/routeman_gui.cpp +++ b/src/routeman_gui.cpp @@ -36,6 +36,7 @@ #include "ais_decoder.h" #include "chcanv.h" +#include "concanv.h" #include "routemanagerdialog.h" #include "routeman_gui.h" #include "route_point.h" @@ -59,6 +60,7 @@ extern MyFrame* gFrame; extern Select *pSelect; extern AisDecoder *g_pAIS; +extern ConsoleCanvas *console; extern std::vector g_TrackList; extern ActiveTrack* g_pActiveTrack; @@ -66,6 +68,31 @@ extern TrackPropDlg *pTrackPropDialog; extern RouteManagerDialog *pRouteManagerDialog; extern MyConfig *pConfig; +static bool ConfirmDeleteAisMob() { + int r = OCPNMessageBox(NULL, + _("You are trying to delete an active AIS MOB " + "route, are you REALLY sure?"), + _("OpenCPN Warning"), wxYES_NO); + + return r == wxID_YES; +} + +RoutemanDlgCtx RoutemanGui::GetDlgCtx() { + RoutemanDlgCtx ctx; + ctx.confirm_delete_ais_mob = []() { return ConfirmDeleteAisMob(); }; + ctx.get_global_colour = [](wxString c) { return GetGlobalColor(c); }; + ctx.show_with_fresh_fonts = []() { console->ShowWithFreshFonts(); }; + ctx.clear_console_background = [] () { + console->pCDI->ClearBackground(); + console->Show(false); }; + ctx.route_mgr_dlg_update_list_ctrl = []() { + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateRouteListCtrl(); + }; + return ctx; +} + + bool RoutemanGui::UpdateProgress() { bool bret_val = false; From bfbb5eb9a1500fd54743e9475c587e66947e73f9 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 23 Mar 2023 08:49:05 +0100 Subject: [PATCH 10/63] routeman et. al.: Use callbacks, decouple from GUI --- include/RoutePropDlg.h | 5 ++ include/routeman.h | 17 ++++--- src/RoutePropDlg.cpp | 31 ++++++++++++ src/console.cpp | 3 +- src/ocpn_app.cpp | 28 +---------- src/ocpn_frame.cpp | 5 +- src/routeman.cpp | 104 +++++++++++++++-------------------------- 7 files changed, 90 insertions(+), 103 deletions(-) diff --git a/include/RoutePropDlg.h b/include/RoutePropDlg.h index 9ccc3deb95..9e631bc177 100644 --- a/include/RoutePropDlg.h +++ b/include/RoutePropDlg.h @@ -44,9 +44,12 @@ #endif #include "observable_evtvar.h" +#include "routeman.h" /////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////////////// /// Class RoutePropDlg /////////////////////////////////////////////////////////////////////////////// @@ -170,6 +173,8 @@ class RoutePropDlg : public wxFrame { ~RoutePropDlg(); + static RoutePropDlgCtx GetDlgCtx(); + void m_hyperlink1OnContextMenu(wxMouseEvent& event) { m_hyperlink1->PopupMenu(m_menuLink, event.GetPosition()); } diff --git a/include/routeman.h b/include/routeman.h index 1e14926af3..f4833d9e95 100644 --- a/include/routeman.h +++ b/include/routeman.h @@ -65,13 +65,13 @@ WX_DEFINE_ARRAY(MarkIcon *, ArrayOfMarkIcon); /** Callbacks for RoutePropDlg */ struct RoutePropDlgCtx { - std::function SetRouteAndUpdate; - std::function SetEnroutePoint; - std::function Hide; + std::function set_route_and_update; + std::function set_enroute_point; + std::function hide; RoutePropDlgCtx() : - SetRouteAndUpdate([&](Route* r) {}), - SetEnroutePoint([&](Route* r, RoutePoint* rt) {}), - Hide([&](Route* r) {}) + set_route_and_update([&](Route* r) {}), + set_enroute_point([&](Route* r, RoutePoint* rt) {}), + hide([&](Route* r) {}) { } }; @@ -218,12 +218,14 @@ friend class RoutemanGui; // WayPointman //---------------------------------------------------------------------------- +typedef std::function GlobalColourFunc; + class WayPointman { friend class WayPointmanGui; public: - WayPointman(); + WayPointman(GlobalColourFunc colour_func); ~WayPointman(); wxBitmap *GetIconBitmap(const wxString &icon_key); bool GetIconPrescaled(const wxString &icon_key); @@ -278,6 +280,7 @@ friend class WayPointmanGui; int m_bitmapSizeForList; int m_iconListHeight; ColorScheme m_cs; + GlobalColourFunc m_get_global_colour; }; #endif // _ROUTEMAN_H__ diff --git a/src/RoutePropDlg.cpp b/src/RoutePropDlg.cpp index 49be3f59f7..c9d5d5cdf4 100644 --- a/src/RoutePropDlg.cpp +++ b/src/RoutePropDlg.cpp @@ -27,7 +27,9 @@ #include "ocpn_types.h" #include "routeman_gui.h" #include "routeman.h" +#include "routemanagerdialog.h" #include "RoutePropDlg.h" +#include "RoutePropDlgImpl.h" #include "styles.h" #if wxCHECK_VERSION(3, 1, 2) @@ -45,6 +47,35 @@ END_EVENT_TABLE() extern Routeman *g_pRouteMan; +extern RoutePropDlgImpl *pRoutePropDialog; +extern RouteManagerDialog *pRouteManagerDialog; + +RoutePropDlgCtx RoutePropDlg::GetDlgCtx() { + struct RoutePropDlgCtx ctx; + ctx.set_route_and_update = [&](Route* r) { + if (pRoutePropDialog && (pRoutePropDialog->IsShown())) { + pRoutePropDialog->SetRouteAndUpdate(r, true); + } + }; + ctx.set_enroute_point = [&](Route* r, RoutePoint* rt) { + if (pRoutePropDialog && pRoutePropDialog->IsShown()) { + if (pRoutePropDialog->GetRoute() == r) { + pRoutePropDialog->SetEnroutePoint(rt); + } + } + }; + ctx.hide = [&](Route* r) { + if (pRoutePropDialog && (pRoutePropDialog->IsShown()) && + (r == pRoutePropDialog->GetRoute())) { + pRoutePropDialog->Hide(); + } + }; + auto RouteMgrDlgUpdateListCtrl = [&]() { + if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) + pRouteManagerDialog->UpdateRouteListCtrl(); + }; + return ctx; +} RoutePropDlg::RoutePropDlg(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, diff --git a/src/console.cpp b/src/console.cpp index 0f44a7ceb9..471d5238db 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -251,7 +251,8 @@ class CliApp : public wxAppConsole { pSelect = new Select(); pRouteList = new RouteList; InitRouteman(); - pWayPointMan = new WayPointman(); + auto colour_func = [] (wxString c) { return *wxBLACK; }; + pWayPointMan = new WayPointman(colour_func); } void list_plugins() { diff --git a/src/ocpn_app.cpp b/src/ocpn_app.cpp index 956335baf2..d2149640cd 100644 --- a/src/ocpn_app.cpp +++ b/src/ocpn_app.cpp @@ -1257,32 +1257,8 @@ bool MyApp::OnInit() { // Init the Route Manager - struct RoutePropDlgCtx ctx; - ctx.SetRouteAndUpdate = [&](Route* r) { - if (pRoutePropDialog && (pRoutePropDialog->IsShown())) { - pRoutePropDialog->SetRouteAndUpdate(r, true); - } - }; - ctx.SetEnroutePoint = [&](Route* r, RoutePoint* rt) { - if (pRoutePropDialog && pRoutePropDialog->IsShown()) { - if (pRoutePropDialog->GetRoute() == r) { - pRoutePropDialog->SetEnroutePoint(rt); - } - } - }; - ctx.Hide = [&](Route* r) { - if (pRoutePropDialog && (pRoutePropDialog->IsShown()) && - (r == pRoutePropDialog->GetRoute())) { - pRoutePropDialog->Hide(); - } - }; - auto RouteMgrDlgUpdateListCtrl = [&]() { - if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) - pRouteManagerDialog->UpdateRouteListCtrl(); - }; - - g_pRouteMan = new Routeman(ctx, RoutemanGui::GetDlgCtx(), - NMEALogWindow::Get()); + g_pRouteMan = new Routeman(RoutePropDlg::GetDlgCtx(), RoutemanGui::GetDlgCtx(), + NMEALogWindow::Get()); // Init the Selectable Route Items List pSelect = new Select(); diff --git a/src/ocpn_frame.cpp b/src/ocpn_frame.cpp index 4792906ad5..9c1ffcecbe 100644 --- a/src/ocpn_frame.cpp +++ b/src/ocpn_frame.cpp @@ -66,8 +66,8 @@ #include "AISTargetQueryDialog.h" #include "CanvasConfig.h" #include "chartbase.h" -#include "chartdb.h" #include "chart_ctx_factory.h" +#include "chartdb.h" #include "chcanv.h" #include "cm93.h" #include "cmdline.h" @@ -4715,7 +4715,8 @@ void MyFrame::OnInitTimer(wxTimerEvent &event) { // Load the waypoints.. both of these routines are very slow to execute // which is why they have been to defered until here - pWayPointMan = new WayPointman(); + auto colour_func = [](wxString c) { return GetGlobalColor(c); }; + pWayPointMan = new WayPointman(colour_func); WayPointmanGui(*pWayPointMan).SetColorScheme(global_color_scheme, g_Platform->GetDisplayDPmm()); // Reload the ownship icon from UserIcons, if present diff --git a/src/routeman.cpp b/src/routeman.cpp index 9f598442b6..59097f9f45 100644 --- a/src/routeman.cpp +++ b/src/routeman.cpp @@ -22,6 +22,10 @@ * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ +#include +#include +#include + #include #include @@ -30,45 +34,35 @@ #include #include -#include #include #include #include -#include #include #include #include "ais_decoder.h" #include "base_platform.h" -#include "chcanv.h" #include "comm_n0183_output.h" #include "comm_vars.h" #include "config_vars.h" -#include "concanv.h" #include "cutil.h" #include "dychart.h" #include "georef.h" -#include "MarkIcon.h" #include "nav_object_database.h" #include "navutil_base.h" #include "navutil.h" #include "nmea_ctx_factory.h" -#include "nmea_log.h" -#include "OCPNPlatform.h" +//#include "nmea_log.h" +//#include "OCPNPlatform.h" #include "own_ship.h" #include "route.h" #include "routeman.h" -#include "svg_utils.h" #include "track.h" #ifdef __ANDROID__ #include "androidUTIL.h" #endif -#ifndef CLIAPP -extern ConsoleCanvas *console; -#endif - extern BasePlatform* g_BasePlatform; extern AisDecoder *g_pAIS; extern RouteList *pRouteList; @@ -121,9 +115,9 @@ void appendOSDirSlash(wxString *pString); Routeman::Routeman(struct RoutePropDlgCtx ctx, struct RoutemanDlgCtx route_dlg_ctx, NmeaLog& nmea_log) - : m_prop_dlg_ctx(ctx), + : m_NMEA0183(NmeaCtxFactory()), + m_prop_dlg_ctx(ctx), m_route_dlg_ctx(route_dlg_ctx), - m_NMEA0183(NmeaCtxFactory()), m_nmea_log(nmea_log) { pActiveRoute = NULL; pActivePoint = NULL; @@ -234,7 +228,7 @@ void Routeman::RemovePointFromRoute(RoutePoint *point, Route *route, //if (pRoutePropDialog && (pRoutePropDialog->IsShown())) { // pRoutePropDialog->SetRouteAndUpdate(route, true); //} - m_prop_dlg_ctx.SetRouteAndUpdate(route); + m_prop_dlg_ctx.set_route_and_update(route); } @@ -296,10 +290,7 @@ bool Routeman::ActivateRoute(Route *pRouteToActivate, RoutePoint *pStartPoint) { m_bDataValid = false; -#ifndef CLIAPP - console->ShowWithFreshFonts(); -#endif - + m_route_dlg_ctx.show_with_fresh_fonts(); return true; } @@ -374,7 +365,7 @@ bool Routeman::ActivateRoutePoint(Route *pA, RoutePoint *pRP_target) { /// pRoutePropDialog->SetEnroutePoint(pActivePoint); /// } /// } - m_prop_dlg_ctx.SetEnroutePoint(pA, pActivePoint); + m_prop_dlg_ctx.set_enroute_point(pA, pActivePoint); return true; } @@ -415,7 +406,7 @@ bool Routeman::ActivateNextPoint(Route *pr, bool skipped) { /// pRoutePropDialog->SetEnroutePoint(pActivePoint); /// } /// } - m_prop_dlg_ctx.SetEnroutePoint(pr, pActivePoint); + m_prop_dlg_ctx.set_enroute_point(pr, pActivePoint); json_msg.Notify(std::make_shared(v), "OCPN_WPT_ARRIVED"); return true; @@ -453,11 +444,7 @@ bool Routeman::DeactivateRoute(bool b_arrival) { pActivePoint = NULL; -#ifndef CLIAPP - console->pCDI->ClearBackground(); - console->Show(false); -#endif - + m_route_dlg_ctx.clear_console_background(); m_bDataValid = false; return true; @@ -729,19 +716,10 @@ bool Routeman::DoesRouteContainSharedPoints(Route *pRoute) { bool Routeman::DeleteRoute(Route *pRoute, NavObjectChanges* nav_obj_changes) { if (pRoute) { if (pRoute == pAISMOBRoute) { -#ifdef CLIAPP - pAISMOBRoute = NULL; -#else - int ret = OCPNMessageBox(NULL, - _("You are trying to delete an active AIS MOB " - "route, are you REALLY sure?"), - _("OpenCPN Warning"), wxYES_NO); - - if (ret == wxID_NO) + if (!m_route_dlg_ctx.confirm_delete_ais_mob()) { return false; - else - pAISMOBRoute = NULL; -#endif + } + pAISMOBRoute = 0; } ::wxBeginBusyCursor(); @@ -755,7 +733,7 @@ bool Routeman::DeleteRoute(Route *pRoute, NavObjectChanges* nav_obj_changes) { /// (pRoute == pRoutePropDialog->GetRoute())) { /// pRoutePropDialog->Hide(); /// } - m_prop_dlg_ctx.Hide(pRoute); + m_prop_dlg_ctx.hide(pRoute); nav_obj_changes->DeleteConfigRoute(pRoute); @@ -820,20 +798,11 @@ void Routeman::DeleteAllRoutes(NavObjectChanges* nav_obj_changes) { while (node) { Route *proute = node->GetData(); if (proute == pAISMOBRoute) { -#ifdef CLIAPP - pAISMOBRoute = NULL; -#else - ::wxEndBusyCursor(); - int ret = OCPNMessageBox(NULL, - _("You are trying to delete an active AIS MOB " - "route, are you REALLY sure?"), - _("OpenCPN Warning"), wxYES_NO); - if (ret == wxID_NO) - return; - else - pAISMOBRoute = NULL; + if (!m_route_dlg_ctx.confirm_delete_ais_mob()) { + return; + } + pAISMOBRoute = 0; ::wxBeginBusyCursor(); -#endif } node = node->GetNext(); @@ -848,9 +817,6 @@ void Routeman::DeleteAllRoutes(NavObjectChanges* nav_obj_changes) { ::wxEndBusyCursor(); } -#ifdef CLIAPP -wxColour GetGlobalColor(wxString name) { return *wxBLACK; } -#endif void Routeman::SetColorScheme(ColorScheme cs, double displayDPmm) { // Re-Create the pens and colors @@ -878,20 +844,23 @@ void Routeman::SetColorScheme(ColorScheme cs, double displayDPmm) { // Or in something like S-52 compliance m_pRoutePen = wxThePenList->FindOrCreatePen( - GetGlobalColor(_T("UINFB")), scaled_line_width, wxPENSTYLE_SOLID); + m_route_dlg_ctx.get_global_colour("UINFB"), scaled_line_width, + wxPENSTYLE_SOLID); m_pSelectedRoutePen = wxThePenList->FindOrCreatePen( - GetGlobalColor(_T("UINFO")), scaled_line_width, wxPENSTYLE_SOLID); + m_route_dlg_ctx.get_global_colour("UINFO"), scaled_line_width, + wxPENSTYLE_SOLID); m_pActiveRoutePen = wxThePenList->FindOrCreatePen( - GetGlobalColor(_T("UARTE")), scaled_line_width, wxPENSTYLE_SOLID); + m_route_dlg_ctx.get_global_colour("UARTE"), scaled_line_width, + wxPENSTYLE_SOLID); m_pTrackPen = wxThePenList->FindOrCreatePen( - GetGlobalColor(_T("CHMGD")), track_scaled_line_width, wxPENSTYLE_SOLID); - - m_pRouteBrush = wxTheBrushList->FindOrCreateBrush(GetGlobalColor(_T("UINFB")), - wxBRUSHSTYLE_SOLID); + m_route_dlg_ctx.get_global_colour("CHMGD"), track_scaled_line_width, + wxPENSTYLE_SOLID); + m_pRouteBrush = wxTheBrushList->FindOrCreateBrush( + m_route_dlg_ctx.get_global_colour("UINFB"), wxBRUSHSTYLE_SOLID); m_pSelectedRouteBrush = wxTheBrushList->FindOrCreateBrush( - GetGlobalColor(_T("UINFO")), wxBRUSHSTYLE_SOLID); + m_route_dlg_ctx.get_global_colour("UINFO"), wxBRUSHSTYLE_SOLID); m_pActiveRouteBrush = wxTheBrushList->FindOrCreateBrush( - GetGlobalColor(_T("PLRTE")), wxBRUSHSTYLE_SOLID); + m_route_dlg_ctx.get_global_colour("PLRTE"), wxBRUSHSTYLE_SOLID); } wxString Routeman::GetRouteReverseMessage(void) { @@ -942,7 +911,8 @@ void Routeman::ZeroCurrentXTEToActivePoint() { // WayPointman Implementation //-------------------------------------------------------------------------------- -WayPointman::WayPointman() { +WayPointman::WayPointman(GlobalColourFunc color_func) + : m_get_global_colour(color_func) { m_pWayPointList = new RoutePointList; pmarkicon_image_list = NULL; @@ -1365,7 +1335,7 @@ int WayPointman::GetIconImageListIndex(const wxBitmap *pbm) { int ym = xbmp.GetHeight() / 2; int dp = xm / 2; int width = wxMax(xm / 10, 2); - wxPen red(GetGlobalColor(_T( "URED" )), width); + wxPen red(m_get_global_colour("URED"), width); mdc.SetPen(red); mdc.DrawLine(xm - dp, ym - dp, xm + dp, ym + dp); mdc.DrawLine(xm - dp, ym + dp, xm + dp, ym - dp); @@ -1388,7 +1358,7 @@ int WayPointman::GetIconImageListIndex(const wxBitmap *pbm) { ym = fbmp.GetHeight() / 2; dp = xm / 2; width = wxMax(xm / 10, 2); - wxPen fred(GetGlobalColor(_T( "UGREN" )), width); + wxPen fred(m_get_global_colour("UGREN"), width); fmdc.SetPen(fred); fmdc.DrawLine(xm - dp, ym + dp, xm + dp, ym + dp); fmdc.SelectObject(wxNullBitmap); From f8293c048c0b8a2e16b81e4e068abc3f1aa71d48 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 23 Mar 2023 14:26:06 +0100 Subject: [PATCH 11/63] routeman: clean up includes --- src/routeman.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/routeman.cpp b/src/routeman.cpp index 59097f9f45..09888a5b72 100644 --- a/src/routeman.cpp +++ b/src/routeman.cpp @@ -26,18 +26,15 @@ #include #include -#include - #include #include #include -#include -#include +#include + #include #include #include -#include #include #include "ais_decoder.h" @@ -46,7 +43,6 @@ #include "comm_vars.h" #include "config_vars.h" #include "cutil.h" -#include "dychart.h" #include "georef.h" #include "nav_object_database.h" #include "navutil_base.h" From e6eb08f4faee5e0f8818ac62c41cc75a3b4c6dda Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Wed, 24 May 2023 23:32:30 +0200 Subject: [PATCH 12/63] rest_server: More general clean up, io thread communication updates - Move external defs to nav_object_database.h - Pick up som elate chnages from master - Streamline names according to Google G - Refactor a large method. - Re-design communication io thread -> main. --- include/nav_object_database.h | 17 + include/ocpn_app.h | 2 +- include/rest_server.h | 131 +++++--- src/comm_drv_signalk_net.cpp | 2 +- src/nav_object_database.cpp | 2 +- src/ocpn_app.cpp | 6 +- src/peer_client.cpp | 19 +- src/rest_server.cpp | 583 ++++++++++++++-------------------- src/rest_server_gui.cpp | 17 +- test/CMakeLists.txt | 2 +- 10 files changed, 382 insertions(+), 399 deletions(-) diff --git a/include/nav_object_database.h b/include/nav_object_database.h index 3f50e46a38..d2b9918558 100644 --- a/include/nav_object_database.h +++ b/include/nav_object_database.h @@ -82,6 +82,7 @@ class RoutePoint; #define RT_OUT_ACTION_UPD 1 << 3 #define RT_OUT_NO_RTPTS 1 << 4 +class NavObjectCollection1; // forward bool WptIsInRouteList(RoutePoint *pr); RoutePoint *WaypointExists(const wxString &name, double lat, double lon); @@ -92,6 +93,22 @@ Track *TrackExists(const wxString &guid); Route *FindRouteContainingWaypoint(RoutePoint *pWP); +Route *GPXLoadRoute1(pugi::xml_node &wpt_node, bool b_fullviz, bool b_layer, + bool b_layerviz, int layer_id, bool b_change); + +RoutePoint *GPXLoadWaypoint1(pugi::xml_node &wpt_node, wxString symbol_name, + wxString GUID, bool b_fullviz, bool b_layer, + bool b_layerviz, int layer_id); + + +bool InsertRouteA(Route* pTentRoute, NavObjectCollection1* navobj); +bool InsertTrack(Track* pTentTrack, bool bApplyChanges = false); +bool InsertWpt(RoutePoint* pWp, bool overwrite); + + +Track *GPXLoadTrack1(pugi::xml_node &trk_node, bool b_fullviz, + bool b_layer, bool b_layerviz, int layer_id); + class NavObjectCollection1 : public pugi::xml_document { public: NavObjectCollection1(); diff --git a/include/ocpn_app.h b/include/ocpn_app.h index fd6cf9a6c0..916860e6e2 100644 --- a/include/ocpn_app.h +++ b/include/ocpn_app.h @@ -68,7 +68,7 @@ class MyApp : public wxApp { wxSingleInstanceChecker* m_checker; CommBridge m_comm_bridge; - RESTServer m_RESTserver; + RestServer m_RESTserver; DECLARE_EVENT_TABLE() }; diff --git a/include/rest_server.h b/include/rest_server.h index 5a703d05a7..24bc080d11 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -22,39 +22,80 @@ * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ +/** + * \file + * + * Opencpn REST API + * + * Supports the following endpoints: + * + * GET /api/ping?api_key= + * Returns {"result": } + * + * POST /api/rx_object?api_key=&source=&force + * The source parameter is mandatory, ip address of originating + * peer. Message body contains xml-encoded data for one or + * more route(s), track(s) and/or waypoint(s). + * If "force" is present, the host object is unconditionally + * updated. If not, host may run a "OK to overwrite" dialog. + * Returns {"result": } + * + * GET /api/uid_exists?uid= + * Check if route or waypoint with given UID exists + * Returns {"result": } + * + * Authentication uses a pairing mechanism. When an unpaired device + * tries to connect, the API generates a random pincode which is + * sent to the connecting party where it is displayed to user. User + * must then input the pincode in the server-side GUI thus making + * sure he has physical access to the machine. + */ + + #ifndef _RESTSERVER_H #define _RESTSERVER_H +#include +#include #include #include #include +#include #include #include #include +#include "observable_evtvar.h" #include "route.h" #include "track.h" -typedef enum RESTServerResult { - RESULT_NO_ERROR = 0, - RESULT_GENERIC_ERROR, - RESULT_OBJECT_REJECTED, - RESULT_DUPLICATE_REJECTED, - RESULT_ROUTE_INSERT_ERROR, - RESULT_NEW_PIN_REQUESTED -} _RESTServerResult; +/** Return codes from HandleServerMessage. */ +enum class RestServerResult { + NoError = 0, + GenericError, + ObjectRejected, + DuplicateRejected, + RouteInsertError, + NewPinRequested, + Undefined +}; +/** Kind of messages sent from io thread to main code. */ enum { ORS_START_OF_SESSION, ORS_CHUNK_N, ORS_CHUNK_LAST }; enum { ID_STG_CANCEL = 10000, ID_STG_OK, ID_STG_CHECK1, ID_STG_CHOICE_COMM }; -class RESTServerThread; // Internal -class RESTServerEvent; // Internal -class PINCreateDialog; +/** \internal IO thread. */ +class RestServerThread; + +/** \internal Event sent from IO thread to main code. */ +class RestServerEvent; -std::string PINtoRandomKeyString(int dpin); +class PinCreateDialog; + +std::string PintoRandomKeyString(int dpin); /** Abstract base class visible in callbacks. */ class PinDialog { @@ -116,54 +157,70 @@ class RouteCtx { delete_track([](Track*) -> void {}) {} }; -class RESTServer : public wxEvtHandler { + +/** Server public interface. */ +class RestServer : public wxEvtHandler { +friend class RestServerObjectApp; public: - RESTServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable); + RestServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable); - virtual ~RESTServer(); + virtual ~RestServer(); - bool StartServer(std::string certificate_location); + + + bool StartServer(std::filesystem::path certificate_location); void StopServer(); - void HandleServerMessage(RESTServerEvent& event); + void HandleServerMessage(RestServerEvent& event); - // Secondary thread life toggle - // Used to inform launching object (this) to determine if the thread can - // be safely called or polled, e.g. wxThread->Destroy(); - void SetSecThreadActive(void) { m_bsec_thread_active = true; } - void SetSecThreadInActive(void) { m_bsec_thread_active = false; } - bool IsSecThreadActive() const { return m_bsec_thread_active; } + /** + * Secondary thread life toggle + * Used to inform launching object (this) to determine if the thread can + * be safely called or polled, e.g. wxThread->Destroy(); + */ - void SetSecondaryThread(RESTServerThread* secondary_Thread) { - m_pSecondary_Thread = secondary_Thread; - } - RESTServerThread* GetSecondaryThread() { return m_pSecondary_Thread; } - void SetThreadRunFlag(int run) { m_Thread_run_flag = run; } void UpdateRouteMgr() { m_dlg_ctx.update_route_mgr(); } std::string GetCertificateDirectory() { return m_certificate_directory; } - int m_Thread_run_flag; - std::string m_cert_file; std::string m_key_file; private: + class IoThread { + public: + IoThread(RestServer* parent, bool& m_portable); + virtual ~IoThread(void) {} + + + + void Entry(); + void Stop(); + + /** 1 -> running, 0 -> stop requested, -1 -> stopped. */ + std::atomic_int run_flag; + private: + bool& m_portable; + RestServer* m_parent; + }; + bool LoadConfig(void); bool SaveConfig(void); RestServerDlgCtx m_dlg_ctx; RouteCtx m_route_ctx; - bool& m_portable; - RESTServerThread* m_pSecondary_Thread; - bool m_bsec_thread_active; + std::string m_certificate_directory; std::unordered_map m_key_map; PinDialog* m_pin_dialog; - wxString m_sPIN; - int m_dPIN; - bool m_b_overwrite; - std::string m_tempUploadFilePath; + wxString m_pin; + RestServerThread* m_parent; + int m_dpin; + bool m_overwrite; + std::string m_tmp_upload_path; std::ofstream m_ul_stream; + std::thread m_thread; + bool m_portable; + IoThread m_io_thread; }; #endif // guard diff --git a/src/comm_drv_signalk_net.cpp b/src/comm_drv_signalk_net.cpp index 28606f4fb2..898855b29c 100644 --- a/src/comm_drv_signalk_net.cpp +++ b/src/comm_drv_signalk_net.cpp @@ -329,7 +329,7 @@ void CommDriverSignalKNet::CloseWebSocket() { wxMilliSleep(100); #if 0 - m_Thread_run_flag = 0; + m_thread_run_flag = 0; printf("sending delete\n"); m_wsThread->Delete(); wxMilliSleep(100); diff --git a/src/nav_object_database.cpp b/src/nav_object_database.cpp index 1744596b77..b4a4384bc9 100644 --- a/src/nav_object_database.cpp +++ b/src/nav_object_database.cpp @@ -1163,7 +1163,7 @@ bool InsertRouteA(Route *pTentRoute, NavObjectCollection1* navobj) { return bAddroute; } -bool InsertTrack(Track *pTentTrack, bool bApplyChanges = false) { +bool InsertTrack(Track *pTentTrack, bool bApplyChanges) { if (!pTentTrack) return false; bool bAddtrack = true; diff --git a/src/ocpn_app.cpp b/src/ocpn_app.cpp index d2149640cd..17954f00fd 100644 --- a/src/ocpn_app.cpp +++ b/src/ocpn_app.cpp @@ -40,6 +40,7 @@ #endif #include +#include #include #include @@ -1979,9 +1980,10 @@ bool MyApp::OnInit() { make_certificate(ipAddr, data_dir.ToStdString()); - m_RESTserver.StartServer(data_dir.ToStdString()); + m_RESTserver.StartServer(std::filesystem::path(data_dir.ToStdString())); - StartMDNSService(g_hostname.ToStdString(), "opencpn-object-control-service", 8000); + StartMDNSService(g_hostname.ToStdString(), + "opencpn-object-control-service", 8000); } return TRUE; } diff --git a/src/peer_client.cpp b/src/peer_client.cpp index ea601c1ae6..d5d55e701e 100644 --- a/src/peer_client.cpp +++ b/src/peer_client.cpp @@ -44,15 +44,15 @@ extern MyFrame *gFrame; -wxString GetErrorText(int result){ +wxString GetErrorText(RestServerResult result){ switch (result) { - case RESTServerResult::RESULT_GENERIC_ERROR: + case RestServerResult::GenericError: return _("Server Generic Error"); - case RESTServerResult::RESULT_OBJECT_REJECTED: + case RestServerResult::ObjectRejected: return _("Peer rejected object"); - case RESTServerResult::RESULT_DUPLICATE_REJECTED: + case RestServerResult::DuplicateRejected: return _("Peer rejected duplicate object"); - case RESTServerResult::RESULT_ROUTE_INSERT_ERROR: + case RestServerResult::RouteInsertError: return _("Peer internal error (insert)"); default: return _("Server Unknown Error"); @@ -249,7 +249,7 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, std::ve // Capture the result int result = root["result"].AsInt(); if (result > 0) { - if (result == RESULT_NEW_PIN_REQUESTED) { + if (result == static_cast(RestServerResult::NewPinRequested)) { // Show the dialog asking for PIN PINConfirmDialog dlg((wxWindow *)gFrame, wxID_ANY, _("OpenCPN Server Message"), @@ -266,14 +266,14 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, std::ve if (dlg.GetReturnCode() == ID_PCD_OK) { wxString PIN_tentative = dlg.GetText1Value().Trim().Trim(false); unsigned int dPIN = atoi(PIN_tentative.ToStdString().c_str()); - std::string new_api_key = PINtoRandomKeyString(dPIN);; + std::string new_api_key = PintoRandomKeyString(dPIN);; SaveClientKey(server_name, new_api_key); } else b_cancel = true; } - else if (result == RESULT_GENERIC_ERROR) + else if (result == static_cast(RestServerResult::GenericError)) apikey_ok = true; } else @@ -339,7 +339,8 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, std::ve // Capture the result int result = root["result"].AsInt(); if (result > 0) { - wxString error_text = GetErrorText(result); + wxString error_text = + GetErrorText(static_cast(result)); OCPNMessageDialog mdlg(NULL, error_text, wxString(_("OpenCPN Info")), wxICON_ERROR | wxOK); mdlg.ShowModal(); diff --git a/src/rest_server.cpp b/src/rest_server.cpp index 7f8037b919..dc73857081 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -35,11 +35,11 @@ #include #include #include -#include #include #include #include "config_vars.h" +#include "logger.h" #include "mongoose.h" #include "nav_object_database.h" #include "pugixml.hpp" @@ -47,23 +47,13 @@ #include "route.h" #include "track.h" -class RESTServerEvent; -wxDEFINE_EVENT(wxEVT_RESTFUL_SERVER, RESTServerEvent); +class RestServerEvent; +wxDEFINE_EVENT(wxEVT_RESTFUL_SERVER, RestServerEvent); -Route* GPXLoadRoute1(pugi::xml_node& wpt_node, bool b_fullviz, bool b_layer, - bool b_layerviz, int layer_id, bool b_change); -Track* GPXLoadTrack1(pugi::xml_node& trk_node, bool b_fullviz, bool b_layer, - bool b_layerviz, int layer_id); -RoutePoint* GPXLoadWaypoint1(pugi::xml_node& wpt_node, wxString def_symbol_name, - wxString GUID, bool b_fullviz, bool b_layer, - bool b_layerviz, int layer_id); - -bool InsertRouteA(Route* pTentRoute, NavObjectCollection1* navobj); -bool InsertTrack(Track* pTentTrack, bool bApplyChanges = false); -bool InsertWpt(RoutePoint* pWp, bool overwrite); +using namespace std::chrono_literals; // Some global variables to handle thread syncronization -static int return_status; +static RestServerResult return_status; static std::condition_variable return_status_condition; static std::mutex mx; @@ -85,12 +75,27 @@ static unsigned long long PINtoRandomKey(int dpin) { return r; } +// FIXME (leamas) "std::shared_ptr" makes no sense, nor does +// this event. Use a copyable struct + EventVar instead. +// struct RestServerEvt { +// const std::string m_payload; +// const std::string m_source_peer; +// const std::string m_api_key; +// RestServerEvt(const std::string& pl, const std::string& sp, +// const std::string& ak): +// m_payload(pl), m_source_peer(sp), m_api_key(ak) {} +// } +// RestServerEvt evt(payload, source_peer, api_key); +// EventVar OnRestSrvEvt; +// OnRestSrvEvt.Notify(std::shared_ptr(evt); +// auto evt = static_cast(OnRestSrvEvt.GetSharedPtr()); //not really... + /* Used by static function, breaks the ordering. Better off in separate file */ -class RESTServerEvent : public wxEvent { +class RestServerEvent : public wxEvent { public: - RESTServerEvent(wxEventType commandType = wxEVT_RESTFUL_SERVER, int id = 0) + RestServerEvent(wxEventType commandType = wxEVT_RESTFUL_SERVER, int id = 0) : wxEvent(id, commandType){}; - ~RESTServerEvent(){}; + ~RestServerEvent(){}; // accessors void SetPayload(std::shared_ptr data) { m_payload = data; } @@ -100,7 +105,7 @@ class RESTServerEvent : public wxEvent { // required for sending with wxPostEvent() wxEvent* Clone() const { - RESTServerEvent* newevent = new RESTServerEvent(*this); + RestServerEvent* newevent = new RestServerEvent(*this); newevent->m_payload = this->m_payload; newevent->m_source_peer = this->m_source_peer; newevent->m_api_key = this->m_api_key; @@ -112,10 +117,88 @@ class RESTServerEvent : public wxEvent { std::string m_api_key; }; +static void HandleRxObject(struct mg_connection* c, int ev, + struct mg_http_message* hm, RestServer* parent) { + int MID = ORS_CHUNK_N; + + std::string api_key; + + struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); + if (api_key_parm.len && api_key_parm.ptr) { + api_key = std::string(api_key_parm.ptr, api_key_parm.len); + } + struct mg_str source = mg_http_var(hm->query, mg_str("source")); + std::string xml_content; + if (hm->chunk.len) + xml_content = std::string(hm->chunk.ptr, hm->chunk.len); + else { + MID = ORS_CHUNK_LAST; + } + + mg_http_delete_chunk(c, hm); + + return_status = RestServerResult::Undefined; + + if (source.len) { + std::string source_peer(source.ptr, source.len); + + if (parent) { + auto evt = new RestServerEvent(wxEVT_RESTFUL_SERVER, MID); + if (xml_content.size()) { + auto buffer = std::make_shared(xml_content); + evt->SetPayload(buffer); + } + evt->SetSource(source_peer); + evt->SetAPIKey(api_key); + parent->QueueEvent(evt); + wxTheApp->ProcessPendingEvents(); +std::cout << "Sending event from thread, kind: " << MID << "\n"; + } + } + if (MID == ORS_CHUNK_LAST) { +std::cout << "IO thread: ORS_CHUNK_LAST: Waiting for status\n"; + std::unique_lock lock{mx}; + return_status_condition.wait(lock, [] { + return return_status != RestServerResult::Undefined; }); +std::cout << "After wait: Reply: " << static_cast(return_status) << "\n"; + mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); + parent->UpdateRouteMgr(); + } +} +static void HandlePing(struct mg_connection* c, int ev, + struct mg_http_message* hm, RestServer* parent) { + std::string api_key; + struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); + if (api_key_parm.len && api_key_parm.ptr) { + api_key = std::string(api_key_parm.ptr, api_key_parm.len); + } + + struct mg_str source = mg_http_var(hm->query, mg_str("source")); + + if (source.len) { + std::string source_peer(source.ptr, source.len); + + return_status = RestServerResult::Undefined; + if (parent) { + auto evt = new RestServerEvent(wxEVT_RESTFUL_SERVER, ORS_CHUNK_LAST); + evt->SetSource(source_peer); + evt->SetAPIKey(api_key); +std::cout << "API key: " << api_key << "\n"; + parent->QueueEvent(evt); + wxTheApp->ProcessPendingEvents(); + } + + std::unique_lock lock{mx}; + return_status_condition.wait(lock, [] { + return return_status != RestServerResult::Undefined; }); + } + mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); +} + // We use the same event handler function for HTTP and HTTPS connections // fn_data is NULL for plain HTTP, and non-NULL for HTTPS static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { - RESTServer* parent = static_cast(fn_data); + RestServer* parent = static_cast(fn_data); if (ev == MG_EV_ACCEPT /*&& fn_data != NULL*/) { struct mg_tls_opts opts; @@ -128,260 +211,87 @@ static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { mg_tls_init(c, &opts); } else if (ev == MG_EV_TLS_HS) { // Think of this as "start of session" if (parent) { - RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, ORS_START_OF_SESSION); - parent->AddPendingEvent(Nevent); + auto evt = + new RestServerEvent(wxEVT_RESTFUL_SERVER, ORS_START_OF_SESSION); + parent->QueueEvent(evt); + wxTheApp->ProcessPendingEvents(); } } else if (ev == MG_EV_HTTP_CHUNK) { struct mg_http_message* hm = (struct mg_http_message*)ev_data; if (mg_http_match_uri(hm, "/api/ping")) { - std::string api_key; - struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); - if (api_key_parm.len && api_key_parm.ptr) { - api_key = std::string(api_key_parm.ptr, api_key_parm.len); - } - - struct mg_str source = mg_http_var(hm->query, mg_str("source")); - - if (source.len) { - std::string source_peer(source.ptr, source.len); - - return_status = -1; - if (parent) { - RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, ORS_CHUNK_LAST); - Nevent.SetSource(source_peer); - Nevent.SetAPIKey(api_key); - parent->AddPendingEvent(Nevent); - } - - std::unique_lock lock{mx}; - while (return_status < 0) { // !predicate - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - return_status_condition.wait(lock); - } - lock.unlock(); - } - mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); + HandlePing(c, ev, hm, parent); } else if (mg_http_match_uri(hm, "/api/rx_object")) { - int MID = ORS_CHUNK_N; - - std::string api_key; - struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); - if (api_key_parm.len && api_key_parm.ptr) { - api_key = std::string(api_key_parm.ptr, api_key_parm.len); - } - - struct mg_str source = mg_http_var(hm->query, mg_str("source")); - - std::string xml_content; - if (hm->chunk.len) - xml_content = std::string(hm->chunk.ptr, hm->chunk.len); - else { - MID = ORS_CHUNK_LAST; - } - - mg_http_delete_chunk(c, hm); - - return_status = -1; - - if (source.len) { - std::string source_peer(source.ptr, source.len); - // printf("%s\n", xml_content.c_str()); - - // std::ofstream b_stream("bodyfile", std::fstream::out | - // std::fstream::binary); b_stream.write(hm->body.ptr, hm->body.len); - - if (parent) { - RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, MID); - if (xml_content.size()) { - auto buffer = std::make_shared(xml_content); - Nevent.SetPayload(buffer); - } - Nevent.SetSource(source_peer); - Nevent.SetAPIKey(api_key); - parent->AddPendingEvent(Nevent); - } - - if (MID == ORS_CHUNK_LAST) { - std::unique_lock lock{mx}; - while (return_status < 0) { // !predicate - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - return_status_condition.wait(lock); - } - lock.unlock(); - - printf("Reply: %d\n", return_status); - mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); - parent->UpdateRouteMgr(); - } - } + HandleRxObject(c, ev, hm, parent); } - - } else if (ev == MG_EV_HTTP_MSG) { -#if 0 - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - if (mg_http_match_uri(hm, "/api/ping")) { - std::string api_key; - struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); - if(api_key_parm.len && api_key_parm.ptr){ - api_key = std::string(api_key_parm.ptr, api_key_parm.len); - } - - - struct mg_str source = mg_http_var(hm->query, mg_str("source")); - - if(source.len) - { - std::string source_peer(source.ptr, source.len); - - return_status = -1; - - if (parent){ - RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, 0); - Nevent.SetSource(source_peer); - Nevent.SetAPIKey(api_key); - parent->AddPendingEvent(Nevent); - } - - std::unique_lock lock{mx}; - while (return_status < 0) { // !predicate - std::this_thread::sleep_for (std::chrono::milliseconds(100)); - return_status_condition.wait(lock); - } - lock.unlock(); - } - - mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); - } else if (mg_http_match_uri(hm, "/api/rx_object")) { - - std::string api_key; - struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); - if(api_key_parm.len && api_key_parm.ptr){ - api_key = std::string(api_key_parm.ptr, api_key_parm.len); - } - - - struct mg_str source = mg_http_var(hm->query, mg_str("source")); - - if(source.len && hm->body.len ) - { - std::string xml_content(hm->body.ptr, hm->body.len); - std::string source_peer(source.ptr, source.len); - - return_status = -1; - - if (parent){ - RESTServerEvent Nevent(wxEVT_RESTFUL_SERVER, 0); - auto buffer = std::make_shared(xml_content); - Nevent.SetPayload(buffer); - Nevent.SetSource(source_peer); - Nevent.SetAPIKey(api_key); - parent->AddPendingEvent(Nevent); - } - - std::unique_lock lock{mx}; - while (return_status < 0) { // !predicate - std::this_thread::sleep_for (std::chrono::milliseconds(100)); - return_status_condition.wait(lock); - } - lock.unlock(); - } - - mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); - } -#endif } - (void)fn_data; } -std::string PINtoRandomKeyString(int dpin) { +std::string PintoRandomKeyString(int dpin) { unsigned long long pin = PINtoRandomKey(dpin); char buffer[100]; snprintf(buffer, sizeof(buffer) - 1, "%0llX", pin); return std::string(buffer); } -class RESTServerThread : public wxThread { -public: - RESTServerThread(RESTServer* Launcher, bool& m_portable); - - ~RESTServerThread(void); - void* Entry(); - void OnExit(void); - -private: - RESTServer* m_pParent; - bool& m_portable; -}; //======================================================================== -/* RESTServer implementation +/* RestServer implementation * */ -RESTServer::RESTServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) - : m_Thread_run_flag(-1), - m_dlg_ctx(ctx), +RestServer::RestServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) + : m_dlg_ctx(ctx), m_route_ctx(route_ctx), - m_portable(portable) { + m_portable(portable), + m_io_thread(this, portable) { m_pin_dialog = 0; - // Prepare the wxEventHandler to accept events from the actual hardware thread - Bind(wxEVT_RESTFUL_SERVER, &RESTServer::HandleServerMessage, this); + //Bind(wxEVT_RESTFUL_SERVER, &RestServer::HandleServerMessage, this); + Bind(wxEVT_RESTFUL_SERVER, + [&](RestServerEvent& ev) { HandleServerMessage(ev); }); +} + +RestServer::~RestServer() { + //Unbind(wxEVT_RESTFUL_SERVER, &RestServer::HandleServerMessage, this); } -RESTServer::~RESTServer() {} +namespace fs = std::filesystem; -bool RESTServer::StartServer(std::string certificate_location) { - m_certificate_directory = certificate_location; - m_cert_file = m_certificate_directory + - std::string("cert.pem"); // Certificate PEM file - m_key_file = - m_certificate_directory + std::string("key.pem"); // The key PEM file +bool RestServer::StartServer(fs::path certificate_location) { + m_certificate_directory = certificate_location.string(); + m_cert_file = (certificate_location / "cert.pem").string(); + m_key_file = (certificate_location / "key.pem").string(); // Load persistent config info LoadConfig(); // Kick off the Server thread - SetSecondaryThread(new RESTServerThread(this, m_portable)); - SetThreadRunFlag(1); - GetSecondaryThread()->Run(); - + m_io_thread.run_flag = 1; + m_thread = std::thread([&] () {m_io_thread.Entry(); }); return true; } -void RESTServer::StopServer() { +void RestServer::StopServer() { wxLogMessage(wxString::Format("Stopping REST service")); - Unbind(wxEVT_RESTFUL_SERVER, &RESTServer::HandleServerMessage, this); - // Kill off the Secondary RX Thread if alive - if (m_pSecondary_Thread) { - m_pSecondary_Thread->Delete(); - - if (m_bsec_thread_active) // Try to be sure thread object is still alive - { - wxLogMessage("Stopping Secondary Thread"); - - m_Thread_run_flag = 0; - - int tsec = 10; - while ((m_Thread_run_flag >= 0) && (tsec--)) wxSleep(1); - - wxString msg; - if (m_Thread_run_flag < 0) - msg.Printf("Stopped in %d sec.", 10 - tsec); - else - msg.Printf("Not Stopped after 10 sec."); - wxLogMessage(msg); - } - - m_pSecondary_Thread = NULL; - m_bsec_thread_active = false; + if (m_thread.joinable()) { + wxLogMessage("Stopping io thread"); + m_io_thread.run_flag = 0; + + int msec = 10000; + while ((m_io_thread.run_flag >= 0) && (msec -= 100)) + std::this_thread::sleep_for(100ms); + if (m_io_thread.run_flag < 0) + MESSAGE_LOG << "Stopped in " << 10000 - msec << " milliseconds"; + else + MESSAGE_LOG << "Not Stopped after 10 sec."; + m_thread.join(); } } -bool RESTServer::LoadConfig(void) { +bool RestServer::LoadConfig(void) { if (TheBaseConfig()) { - TheBaseConfig()->SetPath("/Settings/RESTServer"); + TheBaseConfig()->SetPath("/Settings/RestServer"); wxString key_string; @@ -394,14 +304,14 @@ bool RESTServer::LoadConfig(void) { m_key_map[client_name.ToStdString()] = client_key.ToStdString(); } - TheBaseConfig()->Read("ServerOverwriteDuplicates", &m_b_overwrite, 0); + TheBaseConfig()->Read("ServerOverwriteDuplicates", &m_overwrite, 0); } return true; } -bool RESTServer::SaveConfig(void) { +bool RestServer::SaveConfig(void) { if (TheBaseConfig()) { - TheBaseConfig()->SetPath("/Settings/RESTServer"); + TheBaseConfig()->SetPath("/Settings/RestServer"); wxString key_string; for (auto it : m_key_map) { @@ -411,26 +321,37 @@ bool RESTServer::SaveConfig(void) { } TheBaseConfig()->Write("ServerKeys", key_string); - - TheBaseConfig()->Write("ServerOverwriteDuplicates", m_b_overwrite); + TheBaseConfig()->Write("ServerOverwriteDuplicates", m_overwrite); + TheBaseConfig()->Flush(); } return true; } -void RESTServer::HandleServerMessage(RESTServerEvent& event) { - int return_stat = RESTServerResult::RESULT_GENERIC_ERROR; +static void UpdateReturnStatus(RestServerResult result) { +std::cout << "Updating return_status: " << static_cast(result) << "..." + << std::flush; + { + std::lock_guard lock{mx}; + return_status = result; + } + return_status_condition.notify_one(); +std::cout << " done\n"; +} +void RestServer::HandleServerMessage(RestServerEvent& event) { + +std::cout << "Handling event, GetId() " << event.GetId() << "\n" << std::flush; if (event.GetId() == ORS_START_OF_SESSION) { // Prepare a temp file to catch chuncks that might follow - m_tempUploadFilePath = + m_tmp_upload_path = wxFileName::CreateTempFileName("ocpn_tul").ToStdString(); - m_ul_stream.open(m_tempUploadFilePath.c_str(), + m_ul_stream.open(m_tmp_upload_path.c_str(), std::ios::out | std::ios::trunc); if (!m_ul_stream.is_open()) { wxLogMessage("REST_server: Cannot open %s for write", - m_tempUploadFilePath); - m_tempUploadFilePath.clear(); // reset for next time. + m_tmp_upload_path); + m_tmp_upload_path.clear(); // reset for next time. return; } return; @@ -439,25 +360,26 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { if (event.GetId() == ORS_CHUNK_N) { auto p = event.GetPayload(); std::string* payload = p.get(); - +std::cout << "Got chunk: " << payload << "\n"; // printf("%s\n", payload->c_str()); // Stream out to temp file - if (m_tempUploadFilePath.size() && m_ul_stream.is_open()) { + if (m_tmp_upload_path.size() && m_ul_stream.is_open()) { m_ul_stream.write(payload->c_str(), payload->size()); } return; } if (event.GetId() == ORS_CHUNK_LAST) { +std::cout << "Processing ORS_CHUNK_LAST\n"; // Cancel existing dialog m_dlg_ctx.close_dialog(m_pin_dialog); // Close the temp file. - if (m_tempUploadFilePath.size() && m_ul_stream.is_open()) + if (m_tmp_upload_path.size() && m_ul_stream.is_open()) m_ul_stream.close(); - // Server thread might be waiting for (return_status >= 0) on notify_one() - return_stat = RESTServerResult::RESULT_GENERIC_ERROR; // generic error + // Io thread might be waiting for (return_status >= 0) on notify_one() + UpdateReturnStatus(RestServerResult::GenericError); } // Look up the api key in the hash map. @@ -471,15 +393,13 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { if (!api_found.size()) { // Need a new PIN confirmation - m_dPIN = wxMin(rand() % 10000 + 1, 9999); - m_sPIN.Printf("%04d", m_dPIN); + m_dpin = wxMin(rand() % 10000 + 1, 9999); + m_pin.Printf("%04d", m_dpin); - std::string new_api_key = PINtoRandomKeyString(m_dPIN); + std::string new_api_key = PintoRandomKeyString(m_dpin); - // Add new PIN to map + // Add new PIN to map and persist it m_key_map[event.m_source_peer] = new_api_key; - - // And persist it SaveConfig(); wxString hmsg(event.m_source_peer.c_str()); @@ -490,17 +410,12 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { hmsg += wxString(event.m_source_peer.c_str()); hmsg += _(" to pair with this device.\n"); m_pin_dialog = - m_dlg_ctx.show_dialog(hmsg.ToStdString(), m_sPIN.ToStdString()); - - return_status = RESTServerResult::RESULT_NEW_PIN_REQUESTED; - - std::lock_guard lock{mx}; - return_status_condition.notify_one(); + m_dlg_ctx.show_dialog(hmsg.ToStdString(), m_pin.ToStdString()); + UpdateReturnStatus(RestServerResult::NewPinRequested); return; - } else { - return_status = RESTServerResult::RESULT_NO_ERROR; + UpdateReturnStatus(RestServerResult::NoError); } // GUI dialogs can go here.... @@ -509,149 +424,141 @@ void RESTServer::HandleServerMessage(RESTServerEvent& event) { if (b_cont) { // Load the GPX file pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(m_tempUploadFilePath.c_str()); + pugi::xml_parse_result result = doc.load_file(m_tmp_upload_path.c_str()); if (result.status == pugi::status_ok) { - m_tempUploadFilePath.clear(); // empty for next time + m_tmp_upload_path.clear(); // empty for next time pugi::xml_node objects = doc.child("gpx"); for (pugi::xml_node object = objects.first_child(); object; object = object.next_sibling()) { if (!strcmp(object.name(), "rte")) { - Route* pRoute = NULL; - pRoute = GPXLoadRoute1(object, true, false, false, 0, true); + Route* route = NULL; + route = GPXLoadRoute1(object, true, false, false, 0, true); // Check for duplicate GUID - bool b_add = true; - bool b_overwrite_one = false; - Route* duplicate = m_route_ctx.find_route_by_guid(pRoute->GetGUID()); + bool add = true; + bool overwrite_one = false; + Route* duplicate = m_route_ctx.find_route_by_guid(route->GetGUID()); if (duplicate) { - if (!m_b_overwrite) { + if (!m_overwrite) { auto result = m_dlg_ctx.run_accept_object_dlg( _("The received route already exists on this " "system.\nReplace?"), - _("Always replace objects from this source?")); + _("Always replace objects?")); if (result.status != ID_STG_OK) { - b_add = false; - return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; + add = false; + UpdateReturnStatus(RestServerResult::DuplicateRejected); } else { - m_b_overwrite = result.check1_value; - b_overwrite_one = true; + m_overwrite = result.check1_value; + overwrite_one = true; SaveConfig(); } } - if (m_b_overwrite || b_overwrite_one) { + if (m_overwrite || overwrite_one) { // Remove the existing duplicate route before adding new route m_route_ctx.delete_route(duplicate); } } - if (b_add) { + if (add) { // And here is the payoff.... // Add the route to the global list NavObjectCollection1 pSet; - if (InsertRouteA(pRoute, &pSet)) - return_stat = RESTServerResult::RESULT_NO_ERROR; + if (InsertRouteA(route, &pSet)) + UpdateReturnStatus(RestServerResult::NoError); else - return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; + UpdateReturnStatus(RestServerResult::RouteInsertError); m_dlg_ctx.top_level_refresh(); } } else if (!strcmp(object.name(), "trk")) { - Track* pRoute = NULL; - pRoute = GPXLoadTrack1(object, true, false, false, 0); + Track* route = NULL; + route = GPXLoadTrack1(object, true, false, false, 0); // Check for duplicate GUID - bool b_add = true; - bool b_overwrite_one = false; + bool add = true; + bool overwrite_one = false; - Track* duplicate = m_route_ctx.find_track_by_guid(pRoute->m_GUID); + Track* duplicate = m_route_ctx.find_track_by_guid(route->m_GUID); if (duplicate) { - if (!m_b_overwrite) { + if (!m_overwrite) { auto result = m_dlg_ctx.run_accept_object_dlg( _("The received track already exists on this " "system.\nReplace?"), - _("Always replace objects from this source?")); + _("Always replace objects?")); if (result.status != ID_STG_OK) { - b_add = false; - return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; + add = false; + UpdateReturnStatus(RestServerResult::DuplicateRejected); } else { - m_b_overwrite = result.check1_value; - b_overwrite_one = true; + m_overwrite = result.check1_value; + overwrite_one = true; SaveConfig(); } } - if (m_b_overwrite || b_overwrite_one) { + if (m_overwrite || overwrite_one) { m_route_ctx.delete_track(duplicate); } } - if (b_add) { + if (add) { // And here is the payoff.... // Add the route to the global list NavObjectCollection1 pSet; - if (InsertTrack(pRoute, false)) - return_stat = RESTServerResult::RESULT_NO_ERROR; + if (InsertTrack(route, false)) + UpdateReturnStatus(RestServerResult::NoError); else - return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; + UpdateReturnStatus(RestServerResult::RouteInsertError); m_dlg_ctx.top_level_refresh(); } } else if (!strcmp(object.name(), "wpt")) { - RoutePoint* pWp = NULL; - pWp = GPXLoadWaypoint1(object, "circle", "", false, false, false, 0); + RoutePoint* rp = NULL; + rp = GPXLoadWaypoint1(object, "circle", "", false, false, false, 0); + rp->m_bIsolatedMark = true; // This is an isolated mark // Check for duplicate GUID - bool b_add = true; - bool b_overwrite_one = false; + bool add = true; + bool overwrite_one = false; RoutePoint* duplicate = - WaypointExists(pWp->GetName(), pWp->m_lat, pWp->m_lon); + WaypointExists(rp->GetName(), rp->m_lat, rp->m_lon); if (duplicate) { - if (!m_b_overwrite) { + if (!m_overwrite) { auto result = m_dlg_ctx.run_accept_object_dlg( _("The received waypoint already exists on this " "system.\nReplace?"), - _("Always replace objects from this source?")); + _("Always replace objects?")); if (result.status != ID_STG_OK) { - b_add = false; - return_stat = RESTServerResult::RESULT_DUPLICATE_REJECTED; + add = false; + UpdateReturnStatus(RestServerResult::DuplicateRejected); } else { - m_b_overwrite = result.check1_value; - b_overwrite_one = true; + m_overwrite = result.check1_value; + overwrite_one = true; SaveConfig(); } } } - - if (b_add) { + if (add) { // And here is the payoff.... - if (InsertWpt(pWp, m_b_overwrite || b_overwrite_one)) - return_stat = RESTServerResult::RESULT_NO_ERROR; + if (InsertWpt(rp, m_overwrite || overwrite_one)) + UpdateReturnStatus(RestServerResult::NoError); else - return_stat = RESTServerResult::RESULT_ROUTE_INSERT_ERROR; - + UpdateReturnStatus(RestServerResult::RouteInsertError); m_dlg_ctx.top_level_refresh(); } } } } } else { - return_stat = RESTServerResult::RESULT_OBJECT_REJECTED; + UpdateReturnStatus(RestServerResult::ObjectRejected); } - - return_status = return_stat; - - std::lock_guard lock{mx}; - return_status_condition.notify_one(); } -RESTServerThread::RESTServerThread(RESTServer* Launcher, bool& portable) - : m_portable(portable) { - m_pParent = Launcher; // This thread's immediate "parent" - +RestServer::IoThread::IoThread(RestServer* parent, bool& portable) + : m_portable(portable), m_parent(parent) { server_ip = s_https_addr; // If Portable use another port if (m_portable) { @@ -659,31 +566,21 @@ RESTServerThread::RESTServerThread(RESTServer* Launcher, bool& portable) wxString sip(server_ip); wxLogMessage("Portable REST server IP: Port " + sip); } - - Create(); } -RESTServerThread::~RESTServerThread(void) {} - -void RESTServerThread::OnExit(void) {} - -void* RESTServerThread::Entry() { +void RestServer::IoThread::Entry() { bool not_done = true; - m_pParent->SetSecThreadActive(); // I am alive - + run_flag = 1; struct mg_mgr mgr; // Event manager mg_log_set(MG_LL_DEBUG); // Set log level mg_mgr_init(&mgr); // Initialise event manager - mg_http_listen(&mgr, server_ip.c_str(), fn, - m_pParent); // Create HTTPS listener - // mg_http_listen(&mgr, s_https_addr, fn, (void *) 1); // (HTTPS listener) + // Create HTTPS listener +std::cout << "Listening on " << server_ip << "\n"; + mg_http_listen(&mgr, server_ip.c_str(), fn, m_parent); + // mg_http_listen(&mgr, s_https_addr, fn, (void *) 1); - for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop + while (run_flag > 0) mg_mgr_poll(&mgr, 200); // Infinite event loop mg_mgr_free(&mgr); - - m_pParent->SetSecThreadInActive(); // I am dead - m_pParent->m_Thread_run_flag = -1; - - return 0; + run_flag = -1; } diff --git a/src/rest_server_gui.cpp b/src/rest_server_gui.cpp index 497779b638..543905c92f 100644 --- a/src/rest_server_gui.cpp +++ b/src/rest_server_gui.cpp @@ -38,6 +38,11 @@ #include "rest_server_gui.h" #include "routemanagerdialog.h" +#ifdef __ANDROID__ +#include "androidUTIL.h" +#endif + + extern RouteManagerDialog *pRouteManagerDialog; extern MyFrame* gFrame; @@ -93,9 +98,6 @@ RestServerDlgCtx PINCreateDialog::GetDlgCtx() { } -#ifdef __ANDROID__ -#include "androidUTIL.h" -#endif IMPLEMENT_DYNAMIC_CLASS(AcceptObjectDialog, wxDialog) @@ -127,7 +129,7 @@ AcceptObjectDialog::AcceptObjectDialog(wxWindow* parent, wxWindowID id, AcceptObjectDialog::~AcceptObjectDialog() { delete m_OKButton; delete m_CancelButton; -#ifdef __OCPN__ANDROID__ +#ifdef __ANDROID__ androidEnableRotation(); #endif @@ -203,6 +205,9 @@ void AcceptObjectDialog::OnOKClick(wxCommandEvent& event) { void AcceptObjectDialog::OnCancelClick(wxCommandEvent& event) { EndModal(ID_STG_CANCEL); +#ifdef __ANDROID__ + androidDisableRotation(); +#endif } @@ -217,8 +222,12 @@ PINCreateDialog::PINCreateDialog() { m_OKButton = NULL; m_CancelButton = NULL; premtext = NULL; +#ifdef __ANDROID__ + androidEnableRotation(); +#endif } + PINCreateDialog::PINCreateDialog(wxWindow* parent, wxWindowID id, const wxString& caption, const wxString& hint, const wxPoint& pos, const wxSize& size, long style) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 29961680a1..a8636af5a6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.14) project(opencpn_tests) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) message(STATUS "Building tests") From b576d07444ffa6f313f81959dae7c333f64c3e8b Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 30 Nov 2023 10:00:04 +0100 Subject: [PATCH 13/63] tests: tests for old an new use cases - old functionality: ping, object, apikey parameter - new: writable, force parameter --- test/CMakeLists.txt | 18 +- test/rest-tests.cpp | 322 ++++++++++++++++++++++++++++++++ test/testdata/foo.gpx | 185 +++++++++++++++++++ test/testdata/opencpn.conf | 368 +++++++++++++++++++++++++++++++++++++ test/tests.cpp | 17 +- 5 files changed, 907 insertions(+), 3 deletions(-) create mode 100644 test/rest-tests.cpp create mode 100644 test/testdata/foo.gpx create mode 100644 test/testdata/opencpn.conf diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a8636af5a6..84f9a4472a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,17 +12,32 @@ set(PROJ_SRC ${PROJECT_SOURCE_DIR}/../src) set(SRC tests.cpp + rest-tests.cpp ${MODEL_SRC} ${CMAKE_SOURCE_DIR}/src/api_shim.cpp ${CMAKE_SOURCE_DIR}/src/base_platform.cpp + ${CMAKE_SOURCE_DIR}/src/certificates.cpp + ${CMAKE_SOURCE_DIR}/src/mDNS_query.cpp ) if (LINUX) list(APPEND SRC n2k_tests.cpp) endif () + add_executable(tests ${SRC}) -target_compile_definitions(tests PUBLIC CLIAPP USE_MOCK_DEFS) +target_compile_options(tests PUBLIC "-O0") +target_compile_definitions(tests + PUBLIC + CLIAPP USE_MOCK_DEFS CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}" + TESTDATA="${CMAKE_CURRENT_LIST_DIR}/testdata" +) + +find_program(CURL NAMES curl) +if (CURL) + target_compile_definitions(tests PUBLIC CURLPROG="${CURL}") +endif () + if (MSVC) target_link_libraries(tests PRIVATE setupapi.lib psapi.lib) endif () @@ -64,6 +79,7 @@ target_link_libraries(tests PRIVATE ocpn::gdal) target_link_libraries(tests PRIVATE ocpn::geoprim) target_link_libraries(tests PRIVATE ocpn::iso8211) target_link_libraries(tests PRIVATE ocpn::libarchive) +target_link_libraries(tests PRIVATE ocpn::mDNS) target_link_libraries(tests PRIVATE ocpn::mongoose) target_link_libraries(tests PRIVATE ocpn::N2KParser) target_link_libraries(tests PRIVATE ocpn::nmea0183) diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp new file mode 100644 index 0000000000..0555657c00 --- /dev/null +++ b/test/rest-tests.cpp @@ -0,0 +1,322 @@ +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "base_platform.h" +#include "certificates.h" +#include "config_vars.h" +#include "mDNS_query.h" +#include "observable_confvar.h" +#include "ocpn_types.h" +#include "rest_server.h" +#include "routeman.h" + +namespace fs = std::filesystem; +using namespace std::chrono_literals; + +extern WayPointman* pWayPointMan; +extern RouteList* pRouteList; +extern Select* pSelect; +extern BasePlatform* g_BasePlatform; + +static std::string s_result; +static int int_result0; + +static void ConfigSetup() { + const auto config_orig = fs::path(TESTDATA) / "opencpn.conf"; + const auto config_path = fs::path(CMAKE_BINARY_DIR) / "opencpn.conf"; + std::remove(config_path.string().c_str()); + fs::copy(config_orig, config_path); + InitBaseConfig(new wxFileConfig("", "", config_path.string())); +} + +static bool g_portable = false; +class RestServerApp : public wxAppConsole { +public: + RestServerApp(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) + : wxAppConsole(), + m_rest_server(ctx, route_ctx, portable) {} + + void Run() { + std::vector addresses = get_local_ipv4_addresses(); + auto local_address = addresses[0]; + fs::path dirpath(std::string(CMAKE_BINARY_DIR) + "/certs"); + if (!fs::exists(dirpath)) fs::create_directory(dirpath); + // Handle buggy make_certificate: + make_certificate(local_address, dirpath.string() + "/"); + m_rest_server.StartServer(dirpath.string()); + Work() ; + ProcessPendingEvents(); + m_rest_server.StopServer(); + } + +protected: + virtual void Work() { std::this_thread::sleep_for(50ms); } + +protected: + RestServer m_rest_server; +}; + +class RestServerPingApp : public RestServerApp { +public: + RestServerPingApp(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) + : RestServerApp(ctx, route_ctx, portable) {} + +protected: + void Work() { + auto path = fs::path(CMAKE_BINARY_DIR) / "curl-result"; + { + std::stringstream ss; + ss << CURLPROG << " --insecure -o " << path + << " https://localhost:8443/api/ping?source=1.2.3.4&apikey=bad"; + system(ss.str().c_str()); + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + std::ifstream f(path.string()); + std::string result; + std::getline(f, result); + EXPECT_EQ(result, "{\"result\": 5}"); // Bad api key + }{ + std::stringstream ss; + auto key = m_rest_server.m_key_map["1.2.3.4"]; + ss << CURLPROG << " --insecure -o " << path + << " \"https://localhost:8443/api/ping?source=1.2.3.4&apikey=" + << key << "\""; +std::cout << "running cmd: " << ss.str() << "\n"; + system(ss.str().c_str()); + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + std::ifstream f(path.string()); + std::string result; + std::getline(f, result); + EXPECT_EQ(result, "{\"result\": 0}"); // ok + } + } +}; + +class RestServerObjectApp : public RestServerApp { +public: + RestServerObjectApp(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) + : RestServerApp(ctx, route_ctx, portable) {} + +protected: + void Work() override { + auto outpath = fs::path(CMAKE_BINARY_DIR) / "curl-result"; + auto datapath = fs::path(TESTDATA) / "foo.gpx"; + { + // try to transfer route without api key + std::stringstream ss; + ss << CURLPROG << " --insecure --silent --data @" << datapath + << " -o " << outpath << " -H \"Content-Type: text/xml\"" + << " https://localhost:8443/api/rx_object?source=1.2.3.4"; + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + system(ss.str().c_str()); + std::ifstream f(outpath.string()); + std::string result; + std::getline(f, result); + EXPECT_EQ(result, "{\"result\": 5}"); // New pin required + } + { + // Try to transfer using api key set up above. + std::stringstream ss; + auto key = m_rest_server.m_key_map["1.2.3.4"]; + ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " + << outpath << " -H \"Content-Type: text/xml\"" + << " \"https://localhost:8443/api/rx_object?source=1.2.3.4" + << "&apikey=" << key << "\""; + + system(ss.str().c_str()); + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + std::ifstream f(outpath.string()); + std::string result; + std::getline(f, result); + EXPECT_EQ(result, "{\"result\": 0}"); // Ok + } + { + // Set "find duplicate guid" callback to return true; + m_rest_server.m_route_ctx.find_route_by_guid = [](wxString guid) { + auto r = new Route; + r->m_GUID = guid; + return r; + }; + // Set the user "accept overwrite" cb to return false + m_rest_server.m_dlg_ctx.run_accept_object_dlg = + [](const wxString&, const wxString&) { + return AcceptObjectDlgResult(ID_STG_CANCEL, true); }; + + // Try to transfer same object + std::stringstream ss; + auto key = m_rest_server.m_key_map["1.2.3.4"]; + ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " + << outpath << " -H \"Content-Type: text/xml\"" + << " \"https://localhost:8443/api/rx_object?source=1.2.3.4" + << "&apikey=" << key << "\""; + + system(ss.str().c_str()); + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + std::ifstream f(outpath.string()); + std::string result; + std::getline(f, result); + EXPECT_EQ(result, "{\"result\": 3}"); // Duplicate rejected + } + { + // Try to transfer same object using argument force + std::stringstream ss; + auto key = m_rest_server.m_key_map["1.2.3.4"]; + ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " + << outpath << " -H \"Content-Type: text/xml\"" + << " \"https://localhost:8443/api/rx_object?source=1.2.3.4" + << "&force=1&apikey=" << key << "\""; + system(ss.str().c_str()); +std::cout << "Running cmd: " << ss.str() << "\n"; + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + std::ifstream f(outpath.string()); + std::string result; + std::getline(f, result); + EXPECT_EQ(result, "{\"result\": 0}"); // Ok + } + } +}; +class RestCheckWriteApp : public RestServerApp { +public: + RestCheckWriteApp(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) + : RestServerApp(ctx, route_ctx, portable) {} + +protected: + + void Work() override { + + + auto datapath = fs::path(TESTDATA) / "foo.gpx"; + auto outpath = fs::path(CMAKE_BINARY_DIR) / "curl-result"; + { + std::stringstream ss; + auto key = m_rest_server.m_key_map["1.2.3.4"]; + ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " + << outpath << " -H \"Content-Type: text/xml\"" + << " \"https://localhost:8443/api/writable?source=1.2.3.4" + << "&apikey=" << key + << "&guid=6a76a7e6-dc39-4a7d-964e-1eff3462c06c\""; + + // Try check our standard object, bad api key + std::cout << "Running command; " << ss.str() << "\n"; + system(ss.str().c_str()); + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + std::ifstream f(outpath.string()); + std::string result; + std::getline(f, result); + EXPECT_EQ(result, "{\"result\": 5}"); // Ok + } + { + auto key = m_rest_server.m_key_map["1.2.3.4"]; + // Try check our standard object, fix the api key + key = m_rest_server.m_key_map["1.2.3.4"]; + std::stringstream ss; + ss.clear(); + ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " + << outpath << " -H \"Content-Type: text/xml\"" + << " \"https://localhost:8443/api/writable?source=1.2.3.4" + << "&apikey=" << key + << "&guid=6a76a7e6-dc39-4a7d-964e-1eff3462c06c\""; + ProcessPendingEvents(); +std::cout << "Running command; " << ss.str() << "\n"; + system(ss.str().c_str()); + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + std::ifstream f(outpath.string()); + std::string result; + std::getline(f, result); + EXPECT_EQ(result, "{\"result\": 0}"); // Ok + } + + { + // Set "find duplicate guid" callback to return true; + m_rest_server.m_route_ctx.find_route_by_guid = [](wxString guid) { + auto r = new Route; + r->m_GUID = guid; + return r; + }; + auto key = m_rest_server.m_key_map["1.2.3.4"]; + std::cout << "USing key: " << key << "\n"; + std::stringstream ss; + ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " + << outpath << " -H \"Content-Type: text/xml\"" + << " \"https://localhost:8443/api/writable?source=1.2.3.4" + << "&apikey=" << key + << "&guid=apikey6a76a7e6-dc39-4a7d-964e-1eff3462c06c\""; +std::cout << "Running apikey command; " << ss.str() << "\n"; + ProcessPendingEvents(); + system(ss.str().c_str()); + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + std::ifstream f(outpath.string()); + std::string result; + std::getline(f, result); + EXPECT_EQ(result, "{\"result\": 3}"); // Duplicate reject + } + } +}; +TEST(RestServer, start_stop) { + wxInitializer initializer; + ConfigSetup(); + RestServerDlgCtx dialog_ctx; + RouteCtx route_ctx; + RestServerApp app(dialog_ctx, route_ctx, g_portable); + app.Run(); + EXPECT_EQ(1, 1); +}; + +TEST(RestServer, Ping) { + wxInitializer initializer; + ConfigSetup(); + RestServerDlgCtx dialog_ctx; + RouteCtx route_ctx; + RestServerPingApp app(dialog_ctx, route_ctx, g_portable); + app.Run(); +}; + +TEST(RestServer, Object) { + wxInitializer initializer; + ConfigSetup(); + auto colour_func = [] (wxString c) { return *wxBLACK; }; + pWayPointMan = new WayPointman(colour_func); + pRouteList = new RouteList; + g_BasePlatform = new BasePlatform(); + pSelect = new Select(); + + RestServerDlgCtx dialog_ctx; + RouteCtx route_ctx; + RestServerObjectApp app(dialog_ctx, route_ctx, g_portable); + app.Run(); +} +TEST(RestServer, CheckWrite) { + wxInitializer initializer; + ConfigSetup(); + auto colour_func = [] (wxString c) { return *wxBLACK; }; + pWayPointMan = new WayPointman(colour_func); + pRouteList = new RouteList; + g_BasePlatform = new BasePlatform(); + pSelect = new Select(); + + RestServerDlgCtx dialog_ctx; + RouteCtx route_ctx; + RestCheckWriteApp app(dialog_ctx, route_ctx, g_portable); + app.Run(); +} diff --git a/test/testdata/foo.gpx b/test/testdata/foo.gpx new file mode 100644 index 0000000000..09ed869138 --- /dev/null +++ b/test/testdata/foo.gpx @@ -0,0 +1,185 @@ + + + + foo + + 6a76a7e6-dc39-4a7d-964e-1eff3462c06c + 1 + 6.00 + 2023-11-21T10:32:06Z + PC + + false + + + + + 001 + diamond + WPT + + 1baa5ba3-33cd-40f7-a1b7-25735a4563e3 + 1 + 0.050 + + + + + + + 002 + diamond + WPT + + 290519ca-61e0-4e43-96e5-2ddc0ff06a1c + 1 + 0.050 + + + + + + + 003 + diamond + WPT + + 752557f3-d477-44e1-8322-48de602eb16e + 1 + 0.050 + + + + + + + 004 + diamond + WPT + + 0fd5518a-c128-4997-ba58-6b873c946b56 + 1 + 0.050 + + + + + + + 005 + diamond + WPT + + 165e173b-3fa0-4179-a8aa-84f01a64684d + 1 + 0.050 + + + + + + + 006 + diamond + WPT + + 080075cb-8807-4224-af7b-06b926f8966c + 1 + 0.050 + + + + + + + 007 + diamond + WPT + + 6a46e49b-7f32-4e6c-b4da-c309510eabfa + 1 + 0.050 + + + + + + + 008 + diamond + WPT + + 01d95570-70b9-49cb-b398-ab881be982d7 + 1 + 0.050 + + + + + + + 009 + diamond + WPT + + 14deca45-56ba-49da-b7d6-a3800111ecd8 + 1 + 0.050 + + + + + + + 010 + diamond + WPT + + 656c2103-ddb5-4933-b623-6ec073e13824 + 1 + 0.050 + + + + + + + 011 + diamond + WPT + + 26a7e70d-f3e8-403b-a30d-ae1b6cd5d9a6 + 1 + 0.050 + + + + + + + 012 + diamond + WPT + + 6bb07ff3-aba0-4095-90c2-be5c65964218 + 1 + 0.050 + + + + + + + 013 + diamond + WPT + + 596b5cef-0748-4b42-8c2e-6fb732636cfc + 1 + 0.050 + + + + + + diff --git a/test/testdata/opencpn.conf b/test/testdata/opencpn.conf new file mode 100644 index 0000000000..9df6af93f2 --- /dev/null +++ b/test/testdata/opencpn.conf @@ -0,0 +1,368 @@ +[Settings] +CmdSoundString=/usr/bin/aplay %s +LastAppliedTemplate= +CompatOS= +CompatOsVersion= +ConfigVersionString=Version 5.9.0 Build 2023-11-15 +NavMessageShown=1 +InlandEcdis=0 +AndroidVersionCode=0 +UIexpert=0 +SpaceDropMark=0 +ShowStatusBar=1 +ShowMenuBar=0 +DefaultFontSize=0 +DefaultFontFacename= +Fullscreen=0 +ShowCompassWindow=1 +SetSystemTime=0 +ShowGrid=0 +PlayShipsBells=0 +SoundDeviceIndex=13 +FullscreenToolbar=1 +TransparentToolbar=0 +PermanentMOBIcon=0 +ShowLayers=1 +AutoAnchorDrop=0 +ShowChartOutlines=1 +ShowActiveRouteTotal=0 +ShowActiveRouteHighway=1 +SDMMFormat=0 +MostRecentGPSUploadConnection= +ShowChartBar=1 +GUIScaleFactor=0 +ChartObjectScaleFactor=0 +ShipScaleFactor=0 +ENCSoundingScaleFactor=0 +ENCTextScaleFactor=0 +ObjQueryAppendFilesExt=txt,rtf,png,html,gif,tif +CatalogCustomURL= +CatalogChannel= +FilterNMEA_Avg=0 +FilterNMEA_Sec=1 +TrackContinuous=0 +ShowTrue=1 +ShowMag=0 +UserMagVariation=0.00 +CM93DetailFactor=0 +CM93DetailZoomPosX=200 +CM93DetailZoomPosY=200 +ShowCM93DetailSlider=0 +SkewToNorthUp=0 +OpenGL=1 +DisableOpenGL=0 +SoftwareGL=0 +ShowFPS=0 +ZoomDetailFactor=0 +ZoomDetailFactorVector=0 +FogOnOverzoom=0 +OverzoomVectorScale=0 +OverzoomEmphasisBase=0 +PlusMinusZoomFactor=2 +MouseZoomSensitivity=1.3 +ShowMUIZoomButtons=1 +UseAcceleratedPanning=1 +GPUTextureCompression=1 +GPUTextureCompressionCaching=1 +GPUTextureDimension=512 +GPUTextureMemSize=64 +PolygonSmoothing=1 +LineSmoothing=1 +SmoothPanZoom=0 +CourseUpMode=0 +LookAheadMode=0 +COGUPAvgSeconds=15 +UseMagAPB=0 +OwnshipCOGPredictorMinutes=5 +OwnshipCOGPredictorWidth=3 +OwnshipHDTPredictorMiles=1 +OwnShipIconType=0 +OwnShipLength=0 +OwnShipWidth=0 +OwnShipGPSOffsetX=0 +OwnShipGPSOffsetY=0 +OwnShipMinSize=8 +OwnShipSogCogCalc=0 +OwnShipSogCogCalcDampSec=1 +ShowDirectRouteLine=0 +DirectRouteLineStyle=0 +DirectRouteLineColor=0 +RouteArrivalCircleRadius=0.05 +ChartQuilting=1 +NMEALogWindowSizeX=600 +NMEALogWindowSizeY=400 +NMEALogWindowPosX=10 +NMEALogWindowPosY=10 +PreserveScaleOnX=1 +StartWithTrackActive=0 +AutomaticDailyTracks=0 +TrackRotateAt=0 +TrackRotateTimeType=3 +HighlightTracks=1 +InitialStackIndex=0 +InitialdBIndex=-1 +NMEAAPBPrecision=3 +TalkerIdText=EC +ShowTrackPointTime=1 +AnchorWatch1GUID= +AnchorWatch2GUID= +ToolbarX=4 +ToolbarY=4 +iENCToolbarX=-1 +iENCToolbarY=-1 +GlobalToolbarConfig=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX +DistanceFormat=0 +SpeedFormat=0 +WindSpeedFormat=0 +ShowDepthUnits=1 +TemperatureFormat=0 +GPSIdent=Generic +UseGarminHostUpload=0 +MobileTouch=0 +ResponsiveGraphics=0 +EnableRolloverBlock=1 +AutoHideToolbar=0 +AutoHideToolbarSecs=0 +DisplaySizeMM=0 +DisplaySizeManual=0 +SelectionRadiusMM=2 +SelectionRadiusTouchMM=10 +PlanSpeed=6 +Locale= +LocaleOverride= +KeepNavobjBackups=5 +LegacyInputCOMPortFilterBehaviour=0 +AdvanceRouteWaypointOnArrivalOnly=0 +LiveETA=0 +DefaultBoatSpeed=6 +VisibleLayers= +InvisibleLayers= +VisNameInLayers= +InvisNameInLayers= +[Settings/GlobalState] +bShowS57Text=1 +bShowS57ImportantTextOnly=0 +nDisplayCategory=83 +nSymbolStyle=82 +nBoundaryStyle=78 +bShowSoundg=1 +bShowMeta=0 +bUseSCAMIN=1 +bShowAtonText=0 +bShowLightDescription=0 +bExtendLightSectors=1 +bDeClutterText=1 +bShowNationalText=1 +S52_MAR_SAFETY_CONTOUR=3 +S52_MAR_SHALLOW_CONTOUR=2 +S52_MAR_DEEP_CONTOUR=6 +S52_MAR_TWO_SHADES=0 +S52_DEPTH_UNIT_SHOW=0 +ZoomDetailFactorVector=3 +nColorScheme=1 +OwnShipLatLon=" 33.3580, -79.2820" +FrameWinX=1344 +FrameWinY=770 +FrameWinPosX=0 +FrameWinPosY=32 +FrameMax=0 +ClientPosX=0 +ClientPosY=32 +ClientSzX=1920 +ClientSzY=1048 +RoutePropSizeX=0 +RoutePropSizeY=0 +RoutePropPosX=0 +RoutePropPosY=0 +[Settings/WMM] +ShowIcon=1 +ShowLiveIcon=1 +ViewType=1 +ShowPlotOptions=1 +ShowAtCursor=1 +Opacity=255 +DialogPosX=20 +DialogPosY=20 +[Settings/WMM/Plot] +Declination=1 +DeclinationSpacing=10 +Inclination=0 +InclinationSpacing=10 +FieldStrength=0 +FieldStrengthSpacing=10000 +StepSize=6 +PoleAccuracy=2 +[Settings/Audio] +AISAlertSoundFile=/usr/local/share/opencpn/sounds/beep_ssl.wav +DSCAlertSoundFile=/usr/local/share/opencpn/sounds/phonering1.wav +SARTAlertSoundFile=/usr/local/share/opencpn/sounds/beep3.wav +AnchorAlarmSoundFile=/usr/local/share/opencpn/sounds/beep1.wav +bAIS_GCPA_AlertAudio=1 +bAIS_SART_AlertAudio=1 +bAIS_DSC_AlertAudio=1 +bAnchorAlertAudio=0 +[Settings/AIS] +bNoCPAMax=0 +NoCPAMaxNMi=20 +bCPAWarn=0 +CPAWarnNMi=2 +bTCPAMax=0 +TCPAMaxMinutes=30 +bMarkLostTargets=1 +MarkLost_Minutes=8 +bRemoveLostTargets=1 +RemoveLost_Minutes=10 +bShowCOGArrows=1 +bSyncCogPredictors=0 +CogArrowMinutes=6 +bShowTargetTracks=0 +TargetTracksMinutes=20 +bHideMooredTargets=0 +MooredTargetMaxSpeedKnots=0.2 +bAISAlertDialog=0 +bAISAlertAudio=0 +AISAlertAudioFile=/usr/local/share/opencpn/sounds/2bells.wav +bAISAlertSuppressMoored=0 +bShowAreaNotices=0 +bDrawAISSize=0 +bDrawAISRealtime=0 +AISRealtimeMinSpeedKnots=0.7 +bShowAISName=0 +ShowAISTargetNameScale=250000 +bWplIsAprsPositionReport=0 +WplSelAction=0 +AISCOGPredictorWidth=3 +bShowScaledTargets=0 +AISScaledNumber=10 +AISScaledNumberWeightSOG=50 +AISScaledNumberWeightCPA=60 +AISScaledNumberWeightTCPA=25 +AISScaledNumberWeightRange=75 +AISScaledNumberWeightSizeOfTarget=25 +AISScaledSizeMinimal=50 +AISShowScaled=0 +AlertDialogSizeX=200 +AlertDialogSizeY=200 +AlertDialogPosX=200 +AlertDialogPosY=200 +QueryDialogPosX=200 +QueryDialogPosY=200 +AISTargetListPerspective= +AISTargetListRange=40 +AISTargetListSortColumn=2 +bAISTargetListSortReverse=0 +AISTargetListColumnSpec= +AISTargetListColumnOrder= +S57QueryDialogSizeX=400 +S57QueryDialogSizeY=400 +S57QueryExtraDialogSizeX=400 +S57QueryExtraDialogSizeY=400 +bAISRolloverShowClass=0 +bAISRolloverShowCOG=0 +bAISRolloverShowCPA=0 +bAISAlertAckTimeout=0 +AlertAckTimeoutMinutes=0 +[Settings/NMEADataSource] +DataConnections= +[Settings/Others] +ShowRadarRings=0 +RadarRingsNumberVisible=0 +RadarRingsStep=1 +RadarRingsStepUnits=0 +RadarRingsColour=#FF0000 +WaypointUseScaMin=0 +WaypointScaMinValue=2147483646 +WaypointUseScaMinOverrule=0 +WaypointsShowName=1 +WaypointRangeRingsNumber=0 +WaypointRangeRingsStep=1 +WaypointRangeRingsStepUnits=0 +WaypointRangeRingsColour=#FF0000 +ConfirmObjectDeletion=1 +WaypointPreventDragging=0 +EnableZoomToCursor=0 +TrackIntervalSeconds=60 +TrackDeltaDistance=0.1 +TrackPrecision=2 +RouteLineWidth=2 +TrackLineWidth=2 +TrackLineColour=#F3E52F +DefaultWPIcon=triangle +DefaultRPIcon=diamond +[Settings/GTKFonts] +-2269c3b37e7=Dialog:Noto Sans 10:rgb(0, 0, 0) +-53e987a75e70b0f2=StatusBar:Noto Sans 12:rgb(0, 0, 0) +-f2cf98a8570c8c15=AIS Target Name:Noto Sans 12:rgb(0, 0, 0) +-f2cf97a62a33eea4=AISTargetAlert:Noto Sans 10:rgb(0, 0, 0) +-f2cf97a72b53eea9=AISTargetQuery:Noto Sans 10:rgb(0, 0, 0) +-6acb8fa51eb98d6a=ObjectQuery:Noto Sans 10:rgb(0, 0, 0) +-6a9a8a99fc76f901=RouteLegInfoRollover:Noto Sans 10:rgb(0, 0, 0) +-e4aa3309ff96d120=ExtendedTideIcon:Noto Sans 10:rgb(0, 0, 0) +-72cbbba56c43b85e=CurrentValue:Noto Sans 10:rgb(0, 0, 0) +-ecca82687bf7545b=Console Legend:Noto Sans 10:rgb(0, 255, 0) +-efd995057079369e=Console Value:Noto Sans 10:rgb(0, 255, 0) +-d3a5bf66cdfdc356=AISRollover:Noto Sans 10:rgb(0, 0, 0) +-e2188bcc19a263e7=TideCurrentGraphRollover:Noto Sans 10:rgb(0, 0, 0) +-4dc3cb5f3=Marks:Noto Sans 10:rgb(0, 0, 0) +-e8c3cba54cbe3a94=ChartTexts:Noto Sans 10:rgb(0, 0, 0) +-a9bf7eca9a7873=ToolTips:Noto Sans 10:rgb(0, 0, 0) +-09b97775=Menu:Noto Sans 10:rgb(0, 0, 0) +-8fcb4e4a997c74=GridText:Noto Sans 10:rgb(0, 0, 0) +-65bfeeeccc54cb74=WMM_Live_Overlay:Noto Sans 10:rgb(0, 0, 0) +[Settings/CommPriority] +PriorityPosition= +PriorityVelocity= +PriorityHeading= +PriorityVariation= +PrioritySatellites= +[PlugIns] +[PlugIns/libchartdldr_pi.so] +bEnabled=1 +[PlugIns/libwmm_pi.so] +bEnabled=1 +[Directories] +S57DataLocation= +InitChartDir=/home/mk/Documents +GPXIODir= +TCDataDir= +BasemapDir=/usr/local/share/opencpn/gshhs/ +pluginInstallDir= +WMMDataLocation=/usr/local/share/opencpn/plugins/wmm_pi/data/ +[Canvas] +CanvasConfig=0 +[Canvas/CanvasConfig1] +canvasVPLatLon=" 0.0000, 0.0000" +canvasVPScale=0.0003 +canvasVPRotation=0 +canvasInitialdBIndex=-1 +canvasbFollow=0 +ActiveChartGroup=0 +canvasToolbarConfig= +canvasShowToolbar=0 +canvasQuilt=1 +canvasShowGrid=0 +canvasShowOutlines=1 +canvasShowDepthUnits=1 +canvasShowAIS=1 +canvasAttenAIS=0 +canvasShowTides=0 +canvasShowCurrents=0 +canvasShowENCText=1 +canvasENCDisplayCategory=83 +canvasENCShowDepths=1 +canvasENCShowBuoyLabels=1 +canvasENCShowLightDescriptions=0 +canvasENCShowLights=1 +canvasENCShowVisibleSectorLights=97 +canvasENCShowAnchorInfo=0 +canvasENCShowDataQuality=0 +canvasCourseUp=0 +canvasHeadUp=0 +canvasLookahead=0 +canvasSizeX=1342 +canvasSizeY=707 +[AUI] +AUIPerspective=layout2|name=ChartCanvas;caption=;state=768;dir=5;layer=0;row=0;pos=0;prop=100000;bestw=5;besth=5;minw=268;minh=709;maxw=-1;maxh=-1;floatx=-1;floaty=-1;floatw=-1;floath=-1|dock_size(5,0,0)=270| +[TideCurrentDataSources] +tcds0=/usr/local/share/opencpn/tcdata/harmonics-dwf-20210110-free.tcd +tcds1=/usr/local/share/opencpn/tcdata/HARMONICS_NO_US.IDX diff --git a/test/tests.cpp b/test/tests.cpp index 9d4c2acc0e..759abcd32a 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,8 @@ #include "routeman.h" #include "select.h" +namespace fs = std::filesystem; + class AISTargetAlertDialog; class Multiplexer; @@ -141,6 +144,13 @@ AppMsg::Type s_apptype; auto shared_navaddr_none = std::make_shared(); wxLogStderr defaultLog; +static void ConfigSetup() { + const auto config_orig = fs::path(TESTDATA) / "opencpn.conf"; + const auto config_path = fs::path(CMAKE_BINARY_DIR) / "opencpn.conf"; + std::remove(config_path.string().c_str()); + fs::copy(config_orig, config_path); + InitBaseConfig(new wxFileConfig("", "", config_path.string())); +} class MsgCliApp : public wxAppConsole { public: @@ -337,9 +347,9 @@ class AppmsgCliApp : public wxAppConsole { using namespace std; #ifdef _MSC_VER -const static string kSEP("\\"); +const static std::string kSEP("\\"); #else -const static string kSEP("/"); +const static std::string kSEP("/"); #endif class GuernseyApp : public wxAppConsole { @@ -366,6 +376,7 @@ class GuernseyApp : public wxAppConsole { class PriorityApp : public wxAppConsole { public: PriorityApp(string inputfile) : wxAppConsole() { + ConfigSetup(); auto& msgbus = NavMsgBus::GetInstance(); string path(".."); path += kSEP + ".." + kSEP + "test" + kSEP + "testdata" + kSEP + inputfile; @@ -380,6 +391,7 @@ class PriorityApp : public wxAppConsole { class PriorityApp2 : public wxAppConsole { public: PriorityApp2(const char* msg1, const char* msg2) : wxAppConsole() { + ConfigSetup(); auto& msgbus = NavMsgBus::GetInstance(); CommBridge comm_bridge; comm_bridge.Initialize(); @@ -683,6 +695,7 @@ TEST(Position, ParseGGA) { } TEST(Priority, Framework) { + wxLog::SetActiveTarget(&defaultLog); PriorityApp app("stupan.se-10112-tcp.log.input"); EXPECT_NEAR(gLat, 57.6460, 0.001); From 7cc6a5307f2a03ffdfce06b0e85ae41c5c4024fb Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 30 Nov 2023 10:04:19 +0100 Subject: [PATCH 14/63] rest server: add api/writable, start-stop handling, misc - Add the "force" parameter. - Refactor io event handling. - Get rid of static variables - Clean up start-stop handling - Get rid of busy-wait for exit loop - make parent a compiler-checked reference - Add timeouts to all condition wait() rest_server: clean up --- include/rest_server.h | 154 ++++++--- src/rest_server.cpp | 749 +++++++++++++++++++----------------------- 2 files changed, 450 insertions(+), 453 deletions(-) diff --git a/include/rest_server.h b/include/rest_server.h index 24bc080d11..9111a85ff9 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -29,34 +29,44 @@ * * Supports the following endpoints: * - * GET /api/ping?api_key= - * Returns {"result": } - * - * POST /api/rx_object?api_key=&source=&force - * The source parameter is mandatory, ip address of originating - * peer. Message body contains xml-encoded data for one or - * more route(s), track(s) and/or waypoint(s). - * If "force" is present, the host object is unconditionally - * updated. If not, host may run a "OK to overwrite" dialog. - * Returns {"result": } - * - * GET /api/uid_exists?uid= - * Check if route or waypoint with given UID exists - * Returns {"result": } - * + * GET /api/ping?api_key=&source= + * Basic ping check, verifies api_key i. e., the pairing. + * Parameters: + * See below + * Returns + * {"result": } + * + * POST /api/rx_object?api_key=&source=&force=1 + * Upload a GPX route, track or waypoints. Parameters: + * - source= Mandatory, origin ip address or hostname + * - force=<1> if present, the host object is unconditionally + * updated. If not, host may run a "OK to overwrite" dialog. + * - api_key= Mandatory, as obtained when pairing, see below. + * Body: + * xml-encoded GPX data for one or more route(s), track(s) and/or + * waypoint(s) + * Returns: + * {"result": } + * + * GET /api/writable?guid= + * Check if route or waypoint with given is writable. + * Returns + * {"result": } + * * Authentication uses a pairing mechanism. When an unpaired device * tries to connect, the API generates a random pincode which is * sent to the connecting party where it is displayed to user. User * must then input the pincode in the server-side GUI thus making - * sure he has physical access to the machine. + * sure she has physical access to the machine. + * + * Result codes are as defined in RestServerResult. */ - - #ifndef _RESTSERVER_H #define _RESTSERVER_H #include +#include #include #include #include @@ -66,8 +76,10 @@ #include #include +#include // for wxSemaphore, std::semaphore is c++20 + -#include "observable_evtvar.h" +#include "pugixml.hpp" #include "route.h" #include "track.h" @@ -79,21 +91,43 @@ enum class RestServerResult { DuplicateRejected, RouteInsertError, NewPinRequested, - Undefined + Void }; /** Kind of messages sent from io thread to main code. */ enum { ORS_START_OF_SESSION, ORS_CHUNK_N, ORS_CHUNK_LAST }; +/** Dialog return codes. */ enum { ID_STG_CANCEL = 10000, ID_STG_OK, ID_STG_CHECK1, ID_STG_CHOICE_COMM }; -/** \internal IO thread. */ -class RestServerThread; +/** Data from IO thread to main */ +struct RestIoEvtData { + const enum class Cmd { Ping, Object, CheckWrite } cmd; + const std::string api_key; ///< Rest API parameter apikey + const std::string source; ///< Rest API parameter source + const bool force; ///< rest API parameter force + + const std::string payload; ///< GPX data for Object, Guid for CheckWrite + + /** Cmd::Object constructor. */ + RestIoEvtData(const std::string& key, const std::string& src, + const std::string& gpx_data, bool _force) + : RestIoEvtData(Cmd::Object, key, src, gpx_data, _force) {} -/** \internal Event sent from IO thread to main code. */ -class RestServerEvent; + /** Cmd::Ping constructor. */ + RestIoEvtData(const std::string& key, const std::string& src) + : RestIoEvtData(Cmd::Ping, key, src, "", false) {} -class PinCreateDialog; + /** Cmd::CheckWrite constructor. */ + RestIoEvtData(const std::string& key, const std::string& src, + const std::string& guid) + : RestIoEvtData(Cmd::CheckWrite, key, src, guid, false) {} + +private: + RestIoEvtData(Cmd c, const std::string& key, const std::string& src, + const std::string& _payload, bool _force) + : cmd(c), api_key(key), source(src), force(_force), payload(_payload) {} +}; std::string PintoRandomKeyString(int dpin); @@ -157,27 +191,22 @@ class RouteCtx { delete_track([](Track*) -> void {}) {} }; - /** Server public interface. */ class RestServer : public wxEvtHandler { -friend class RestServerObjectApp; + friend class RestServerObjectApp; // Unit test + friend class RestCheckWriteApp; // Unit test + friend class RestServerPingApp; // Unit test + public: RestServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable); virtual ~RestServer(); - - bool StartServer(std::filesystem::path certificate_location); void StopServer(); - void HandleServerMessage(RestServerEvent& event); - - /** - * Secondary thread life toggle - * Used to inform launching object (this) to determine if the thread can - * be safely called or polled, e.g. wxThread->Destroy(); - */ + void UpdateReturnStatus(RestServerResult r); + RestServerResult GetReturnStatus() { return return_status; } void UpdateRouteMgr() { m_dlg_ctx.update_route_mgr(); } @@ -185,41 +214,74 @@ friend class RestServerObjectApp; std::string m_cert_file; std::string m_key_file; + /** Guards return_status */ + std::mutex ret_mutex; + + /** Guards return_status */ + std::condition_variable return_status_condition; + + /** Binary exit synchronization, released when io thread exits. */ + wxSemaphore m_exit_sync; + private: class IoThread { public: - IoThread(RestServer* parent, bool& m_portable); + IoThread(RestServer& parent, const std::string& ip); virtual ~IoThread(void) {} + void Run(); + bool IsRunning() { return run_flag > 0; } - - void Entry(); + /** Request thread to stop asap. */ void Stop(); + /** Block until thread is stopped. */ + bool WaitUntilStopped(); + + private: /** 1 -> running, 0 -> stop requested, -1 -> stopped. */ std::atomic_int run_flag; - private: - bool& m_portable; - RestServer* m_parent; + RestServer& m_parent; + std::string m_server_ip; + std::thread m_thread; }; + /** + * Stores the api key for different ip addresses. Methods for + * serialize/deserialize config file format. + */ + class Apikeys: public std::unordered_map { + public: + static Apikeys Parse(const std::string& s); + std::string ToString() const; + }; + + bool LoadConfig(void); bool SaveConfig(void); + void HandleServerMessage(ObservedEvt& event); + + void HandleWaypoint(pugi::xml_node object, const RestIoEvtData& evt_data); + void HandleTrack(pugi::xml_node object, const RestIoEvtData& evt_data); + void HandleRoute(pugi::xml_node object, const RestIoEvtData& evt_data); + + bool CheckApiKey(const RestIoEvtData& evt_data); + RestServerDlgCtx m_dlg_ctx; RouteCtx m_route_ctx; + RestServerResult return_status; + std::string m_certificate_directory; - std::unordered_map m_key_map; + Apikeys m_key_map; PinDialog* m_pin_dialog; wxString m_pin; - RestServerThread* m_parent; int m_dpin; bool m_overwrite; - std::string m_tmp_upload_path; + std::string m_upload_path; std::ofstream m_ul_stream; std::thread m_thread; - bool m_portable; IoThread m_io_thread; }; diff --git a/src/rest_server.cpp b/src/rest_server.cpp index dc73857081..3aaab206a4 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -23,48 +23,35 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -#include -#include #include #include #include -#include -#include #include #include #include #include -#include #include #include "config_vars.h" #include "logger.h" #include "mongoose.h" #include "nav_object_database.h" -#include "pugixml.hpp" +#include "ocpn_utils.h" +#include "observable_evt.h" #include "rest_server.h" -#include "route.h" -#include "track.h" -class RestServerEvent; -wxDEFINE_EVENT(wxEVT_RESTFUL_SERVER, RestServerEvent); +/** Event from IO thread to main */ +wxDEFINE_EVENT(REST_IO_EVT, ObservedEvt); +namespace fs = std::filesystem; using namespace std::chrono_literals; -// Some global variables to handle thread syncronization -static RestServerResult return_status; -static std::condition_variable return_status_condition; -static std::mutex mx; - -static const char* s_http_addr = "http://0.0.0.0:8000"; -static const char* s_https_addr = "https://0.0.0.0:8443"; -// Is this host a portable? Must use another port to avoid equal IP addres -// conflicts. -static const char* s_http_addr_portable = "http://0.0.0.0:8001"; -static const char* s_https_addr_portable = "https://0.0.0.0:8444"; +static const char* const kHttpAddr = "http://0.0.0.0:8000"; +static const char* const kHttpsAddr = "http://0.0.0.0:8443"; -static std::string server_ip; +static const char* const kHttpPortableAddr = "http://0.0.0.0:8001"; +static const char* const kHttpsPortableAddr = "http://0.0.0.0:8444"; static unsigned long long PINtoRandomKey(int dpin) { using namespace std; @@ -75,124 +62,100 @@ static unsigned long long PINtoRandomKey(int dpin) { return r; } -// FIXME (leamas) "std::shared_ptr" makes no sense, nor does -// this event. Use a copyable struct + EventVar instead. -// struct RestServerEvt { -// const std::string m_payload; -// const std::string m_source_peer; -// const std::string m_api_key; -// RestServerEvt(const std::string& pl, const std::string& sp, -// const std::string& ak): -// m_payload(pl), m_source_peer(sp), m_api_key(ak) {} -// } -// RestServerEvt evt(payload, source_peer, api_key); -// EventVar OnRestSrvEvt; -// OnRestSrvEvt.Notify(std::shared_ptr(evt); -// auto evt = static_cast(OnRestSrvEvt.GetSharedPtr()); //not really... - -/* Used by static function, breaks the ordering. Better off in separate file */ -class RestServerEvent : public wxEvent { -public: - RestServerEvent(wxEventType commandType = wxEVT_RESTFUL_SERVER, int id = 0) - : wxEvent(id, commandType){}; - ~RestServerEvent(){}; - - // accessors - void SetPayload(std::shared_ptr data) { m_payload = data; } - void SetSource(std::string source) { m_source_peer = source; } - void SetAPIKey(std::string key) { m_api_key = key; } - std::shared_ptr GetPayload() { return m_payload; } - - // required for sending with wxPostEvent() - wxEvent* Clone() const { - RestServerEvent* newevent = new RestServerEvent(*this); - newevent->m_payload = this->m_payload; - newevent->m_source_peer = this->m_source_peer; - newevent->m_api_key = this->m_api_key; - return newevent; - }; - - std::shared_ptr m_payload; - std::string m_source_peer; - std::string m_api_key; -}; - -static void HandleRxObject(struct mg_connection* c, int ev, - struct mg_http_message* hm, RestServer* parent) { - int MID = ORS_CHUNK_N; +std::string PintoRandomKeyString(int dpin) { + unsigned long long pin = PINtoRandomKey(dpin); + char buffer[100]; + snprintf(buffer, sizeof(buffer) - 1, "%0llX", pin); + return std::string(buffer); +} - std::string api_key; +/** Extract a HTTP variable from query string. */ +static inline std::string HttpVarToString(const struct mg_str& query, + const char* var) { + std::string string; + struct mg_str mgs = mg_http_var(query, mg_str(var)); + if (mgs.len && mgs.ptr) string = std::string(mgs.ptr, mgs.len); + return string; +} - struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); - if (api_key_parm.len && api_key_parm.ptr) { - api_key = std::string(api_key_parm.ptr, api_key_parm.len); - } - struct mg_str source = mg_http_var(hm->query, mg_str("source")); +static void PostEvent(RestServer* parent, + std::shared_ptr evt_data, int id) { + auto evt = new ObservedEvt(REST_IO_EVT, id); + evt->SetSharedPtr(evt_data); + parent->QueueEvent(evt); + wxTheApp->ProcessPendingEvents(); +} + +static void HandleRxObject(struct mg_connection* c, struct mg_http_message* hm, + RestServer* parent) { + int MID = ORS_CHUNK_N; + + std::string api_key = HttpVarToString(hm->query, "apikey"); + std::string source = HttpVarToString(hm->query, "source"); + std::string force = HttpVarToString(hm->query, "force"); std::string xml_content; if (hm->chunk.len) xml_content = std::string(hm->chunk.ptr, hm->chunk.len); else { MID = ORS_CHUNK_LAST; } - mg_http_delete_chunk(c, hm); + parent->UpdateReturnStatus(RestServerResult::Void); - return_status = RestServerResult::Undefined; - - if (source.len) { - std::string source_peer(source.ptr, source.len); - - if (parent) { - auto evt = new RestServerEvent(wxEVT_RESTFUL_SERVER, MID); - if (xml_content.size()) { - auto buffer = std::make_shared(xml_content); - evt->SetPayload(buffer); - } - evt->SetSource(source_peer); - evt->SetAPIKey(api_key); - parent->QueueEvent(evt); - wxTheApp->ProcessPendingEvents(); -std::cout << "Sending event from thread, kind: " << MID << "\n"; - } + if (source.size()) { + assert(parent && "Null parent pointer"); + auto data_ptr = std::make_shared( + RestIoEvtData(api_key, source, xml_content, force.size())); + PostEvent(parent, data_ptr, MID); } if (MID == ORS_CHUNK_LAST) { -std::cout << "IO thread: ORS_CHUNK_LAST: Waiting for status\n"; - std::unique_lock lock{mx}; - return_status_condition.wait(lock, [] { - return return_status != RestServerResult::Undefined; }); -std::cout << "After wait: Reply: " << static_cast(return_status) << "\n"; - mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); + std::unique_lock lock{parent->ret_mutex}; + bool r = parent->return_status_condition.wait_for(lock, 10s, [&] { + return parent->GetReturnStatus() != RestServerResult::Void; + }); + if (!r) wxLogWarning("Timeout waiting for REST server condition"); + mg_http_reply(c, 200, "", "{\"result\": %d}\n", parent->GetReturnStatus()); parent->UpdateRouteMgr(); } } -static void HandlePing(struct mg_connection* c, int ev, - struct mg_http_message* hm, RestServer* parent) { - std::string api_key; - struct mg_str api_key_parm = mg_http_var(hm->query, mg_str("apikey")); - if (api_key_parm.len && api_key_parm.ptr) { - api_key = std::string(api_key_parm.ptr, api_key_parm.len); - } - struct mg_str source = mg_http_var(hm->query, mg_str("source")); - - if (source.len) { - std::string source_peer(source.ptr, source.len); - - return_status = RestServerResult::Undefined; - if (parent) { - auto evt = new RestServerEvent(wxEVT_RESTFUL_SERVER, ORS_CHUNK_LAST); - evt->SetSource(source_peer); - evt->SetAPIKey(api_key); -std::cout << "API key: " << api_key << "\n"; - parent->QueueEvent(evt); - wxTheApp->ProcessPendingEvents(); - } +static void HandlePing(struct mg_connection* c, struct mg_http_message* hm, + RestServer* parent) { + std::string api_key = HttpVarToString(hm->query, "apikey"); + std::string source = HttpVarToString(hm->query, "source"); + if (source.size()) { + assert(parent && "Null parent pointer"); + parent->UpdateReturnStatus(RestServerResult::Void); + auto data_ptr = + std::make_shared(RestIoEvtData(api_key, source)); + PostEvent(parent, data_ptr, ORS_CHUNK_LAST); + std::unique_lock lock{parent->ret_mutex}; + bool r = parent->return_status_condition.wait_for(lock, 10s, [&] { + return parent->GetReturnStatus() != RestServerResult::Void; + }); + if (!r) wxLogWarning("Timeout waiting for REST server condition"); + } + mg_http_reply(c, 200, "", "{\"result\": %d}\n", parent->GetReturnStatus()); +} - std::unique_lock lock{mx}; - return_status_condition.wait(lock, [] { - return return_status != RestServerResult::Undefined; }); +static void HandleWritable(struct mg_connection* c, struct mg_http_message* hm, + RestServer* parent) { + std::string apikey = HttpVarToString(hm->query, "apikey"); + std::string source = HttpVarToString(hm->query, "source"); + std::string guid = HttpVarToString(hm->query, "guid"); + if (source.size()) { + assert(parent && "Null parent pointer"); + parent->UpdateReturnStatus(RestServerResult::Void); + auto data_ptr = + std::make_shared(RestIoEvtData(apikey, source, guid)); + PostEvent(parent, data_ptr, ORS_CHUNK_LAST); + std::unique_lock lock{parent->ret_mutex}; + bool r = parent->return_status_condition.wait_for(lock, 10s, [&] { + return parent->GetReturnStatus() != RestServerResult::Void; + }); + if (!r) wxLogWarning("Timeout waiting for REST server condition"); } - mg_http_reply(c, 200, "", "{\"result\": %d}\n", return_status); + mg_http_reply(c, 200, "", "{\"result\": %d}\n", parent->GetReturnStatus()); } // We use the same event handler function for HTTP and HTTPS connections @@ -210,377 +173,349 @@ static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { opts.ciphers = NULL; mg_tls_init(c, &opts); } else if (ev == MG_EV_TLS_HS) { // Think of this as "start of session" - if (parent) { - auto evt = - new RestServerEvent(wxEVT_RESTFUL_SERVER, ORS_START_OF_SESSION); - parent->QueueEvent(evt); - wxTheApp->ProcessPendingEvents(); - } + PostEvent(parent, nullptr, ORS_START_OF_SESSION); } else if (ev == MG_EV_HTTP_CHUNK) { struct mg_http_message* hm = (struct mg_http_message*)ev_data; if (mg_http_match_uri(hm, "/api/ping")) { - HandlePing(c, ev, hm, parent); + HandlePing(c, hm, parent); } else if (mg_http_match_uri(hm, "/api/rx_object")) { - HandleRxObject(c, ev, hm, parent); + HandleRxObject(c, hm, parent); + } else if (mg_http_match_uri(hm, "/api/writable")) { + HandleWritable(c, hm, parent); } } } -std::string PintoRandomKeyString(int dpin) { - unsigned long long pin = PINtoRandomKey(dpin); - char buffer[100]; - snprintf(buffer, sizeof(buffer) - 1, "%0llX", pin); - return std::string(buffer); + +//======================================================================== +/* RestServer implementation */ + +RestServer::IoThread::IoThread(RestServer& parent, const std::string& ip) + : m_parent(parent), m_server_ip(ip) {} + +void RestServer::IoThread::Run() { + run_flag = 1; + struct mg_mgr mgr; // Event manager + mg_log_set(MG_LL_DEBUG); // Set log level + mg_mgr_init(&mgr); // Initialise event manager + + // Create HTTPS listener + MESSAGE_LOG << "Listening on " << m_server_ip << "\n"; + mg_http_listen(&mgr, m_server_ip.c_str(), fn, &m_parent); + + while (run_flag > 0) mg_mgr_poll(&mgr, 200); // Infinite event loop + mg_mgr_free(&mgr); + run_flag = -1; + m_parent.m_exit_sync.Post(); } +void RestServer::IoThread::Stop() { run_flag = 0; } -//======================================================================== -/* RestServer implementation - * */ +bool RestServer::IoThread::WaitUntilStopped() { + auto r = m_parent.m_exit_sync.WaitTimeout(10000); + if (r != wxSEMA_NO_ERROR) { + WARNING_LOG << "Semaphore error: " << r; + } + return r == wxSEMA_NO_ERROR; +} + +RestServer::Apikeys RestServer::Apikeys::Parse(const std::string& s) { + Apikeys apikeys; + auto ip_keys = ocpn::split(s.c_str(), ";"); + for (const auto& ip_key : ip_keys) { + auto words = ocpn::split(ip_key.c_str(), ":"); + if (words.size() != 2) continue; + if (apikeys.find(words[0]) == apikeys.end()) { + apikeys[words[0]] = words[1]; + } + } + return apikeys; +} +std::string RestServer::Apikeys::ToString() const { + std::stringstream ss; + for (const auto& it : *this) + ss << it.first << ":" << it.second << ";"; + return ss.str(); +} + + +void RestServer::UpdateReturnStatus(RestServerResult result) { + { + std::lock_guard lock{ret_mutex}; + return_status = result; + } + return_status_condition.notify_one(); +} RestServer::RestServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) - : m_dlg_ctx(ctx), + : m_exit_sync(0, 1), + m_dlg_ctx(ctx), m_route_ctx(route_ctx), - m_portable(portable), - m_io_thread(this, portable) { - m_pin_dialog = 0; - // Prepare the wxEventHandler to accept events from the actual hardware thread - //Bind(wxEVT_RESTFUL_SERVER, &RestServer::HandleServerMessage, this); - Bind(wxEVT_RESTFUL_SERVER, - [&](RestServerEvent& ev) { HandleServerMessage(ev); }); + m_pin_dialog(0), + m_io_thread(*this, portable ? kHttpsPortableAddr : kHttpsAddr) { + // Prepare the wxEventHandler to accept events from the io thread + Bind(REST_IO_EVT, &RestServer::HandleServerMessage, this); } RestServer::~RestServer() { - //Unbind(wxEVT_RESTFUL_SERVER, &RestServer::HandleServerMessage, this); + Unbind(REST_IO_EVT, &RestServer::HandleServerMessage, this); } -namespace fs = std::filesystem; - bool RestServer::StartServer(fs::path certificate_location) { m_certificate_directory = certificate_location.string(); m_cert_file = (certificate_location / "cert.pem").string(); m_key_file = (certificate_location / "key.pem").string(); - // Load persistent config info + // Load persistent config info and kick off the Server thread LoadConfig(); - - // Kick off the Server thread - m_io_thread.run_flag = 1; - m_thread = std::thread([&] () {m_io_thread.Entry(); }); + if (!m_thread.joinable()) { + m_thread = std::thread([&]() { m_io_thread.Run(); }); + } return true; } void RestServer::StopServer() { wxLogMessage(wxString::Format("Stopping REST service")); - // Kill off the Secondary RX Thread if alive + // Kill off the IO Thread if alive if (m_thread.joinable()) { wxLogMessage("Stopping io thread"); - m_io_thread.run_flag = 0; - - int msec = 10000; - while ((m_io_thread.run_flag >= 0) && (msec -= 100)) - std::this_thread::sleep_for(100ms); - if (m_io_thread.run_flag < 0) - MESSAGE_LOG << "Stopped in " << 10000 - msec << " milliseconds"; - else - MESSAGE_LOG << "Not Stopped after 10 sec."; + m_io_thread.Stop(); + m_io_thread.WaitUntilStopped(); m_thread.join(); } } bool RestServer::LoadConfig(void) { - if (TheBaseConfig()) { - TheBaseConfig()->SetPath("/Settings/RestServer"); - - wxString key_string; - TheBaseConfig()->Read("ServerKeys", &key_string); - wxStringTokenizer st(key_string, ";"); - while (st.HasMoreTokens()) { - wxString s1 = st.GetNextToken(); - wxString client_name = s1.BeforeFirst(':'); - wxString client_key = s1.AfterFirst(':'); + TheBaseConfig()->SetPath("/Settings/RestServer"); - m_key_map[client_name.ToStdString()] = client_key.ToStdString(); - } - TheBaseConfig()->Read("ServerOverwriteDuplicates", &m_overwrite, 0); - } + wxString key_string; + TheBaseConfig()->Read("ServerKeys", &key_string); + m_key_map = Apikeys::Parse(key_string.ToStdString()); + TheBaseConfig()->Read("ServerOverwriteDuplicates", &m_overwrite, 0); return true; } bool RestServer::SaveConfig(void) { - if (TheBaseConfig()) { - TheBaseConfig()->SetPath("/Settings/RestServer"); - - wxString key_string; - for (auto it : m_key_map) { - wxString item = - it.first.c_str() + wxString(":") + it.second.c_str() + wxString(";"); - key_string += item; - } - - TheBaseConfig()->Write("ServerKeys", key_string); - TheBaseConfig()->Write("ServerOverwriteDuplicates", m_overwrite); - TheBaseConfig()->Flush(); - } + TheBaseConfig()->SetPath("/Settings/RestServer"); + TheBaseConfig()->Write("ServerKeys", wxString(m_key_map.ToString())); + TheBaseConfig()->Write("ServerOverwriteDuplicates", m_overwrite); + TheBaseConfig()->Flush(); return true; } -static void UpdateReturnStatus(RestServerResult result) { -std::cout << "Updating return_status: " << static_cast(result) << "..." - << std::flush; - { - std::lock_guard lock{mx}; - return_status = result; +bool RestServer::CheckApiKey(const RestIoEvtData& evt_data) { + // Look up the api key in the hash map. + std::string api_found; + for (auto it : m_key_map) { + if (it.first == evt_data.source && it.second == evt_data.api_key) { + api_found = it.second; + break; + } } - return_status_condition.notify_one(); -std::cout << " done\n"; + if (api_found.size()) { + return true; + } + // Need a new PIN confirmation + m_dpin = wxMin(rand() % 10000 + 1, 9999); + m_pin.Printf("%04d", m_dpin); + std::string new_api_key = PintoRandomKeyString(m_dpin); + + // Add new PIN to map and persist it + m_key_map[evt_data.source] = new_api_key; + SaveConfig(); + + std::stringstream ss; + ss << evt_data.source << " " << _("wants to send you new data.") << "\n" + << _("Please enter the following PIN number on ") << evt_data.source + << " " << _("to pair with this device") << "\n"; + m_pin_dialog = m_dlg_ctx.show_dialog(ss.str(), m_pin.ToStdString()); + + return false; } -void RestServer::HandleServerMessage(RestServerEvent& event) { - -std::cout << "Handling event, GetId() " << event.GetId() << "\n" << std::flush; +void RestServer::HandleServerMessage(ObservedEvt& event) { if (event.GetId() == ORS_START_OF_SESSION) { // Prepare a temp file to catch chuncks that might follow - m_tmp_upload_path = - wxFileName::CreateTempFileName("ocpn_tul").ToStdString(); + m_upload_path = wxFileName::CreateTempFileName("ocpn_tul").ToStdString(); - m_ul_stream.open(m_tmp_upload_path.c_str(), - std::ios::out | std::ios::trunc); + m_ul_stream.open(m_upload_path.c_str(), std::ios::out | std::ios::trunc); if (!m_ul_stream.is_open()) { - wxLogMessage("REST_server: Cannot open %s for write", - m_tmp_upload_path); - m_tmp_upload_path.clear(); // reset for next time. + wxLogMessage("REST_server: Cannot open %s for write", m_upload_path); + m_upload_path.clear(); // reset for next time. return; } return; } + auto evt_data = UnpackEvtPointer(event); if (event.GetId() == ORS_CHUNK_N) { - auto p = event.GetPayload(); - std::string* payload = p.get(); -std::cout << "Got chunk: " << payload << "\n"; - // printf("%s\n", payload->c_str()); // Stream out to temp file - if (m_tmp_upload_path.size() && m_ul_stream.is_open()) { - m_ul_stream.write(payload->c_str(), payload->size()); + if (m_upload_path.size() && m_ul_stream.is_open()) { + m_ul_stream.write(evt_data->payload.c_str(), evt_data->payload.size()); } return; } if (event.GetId() == ORS_CHUNK_LAST) { -std::cout << "Processing ORS_CHUNK_LAST\n"; // Cancel existing dialog m_dlg_ctx.close_dialog(m_pin_dialog); // Close the temp file. - if (m_tmp_upload_path.size() && m_ul_stream.is_open()) - m_ul_stream.close(); + if (m_upload_path.size() && m_ul_stream.is_open()) m_ul_stream.close(); - // Io thread might be waiting for (return_status >= 0) on notify_one() + // Io thread might be waiting for return_status on notify_one() UpdateReturnStatus(RestServerResult::GenericError); } - // Look up the api key in the hash map. - std::string api_found; - for (auto it : m_key_map) { - if (it.first == event.m_source_peer && it.second == event.m_api_key) { - api_found = it.second; - break; - } - } - - if (!api_found.size()) { - // Need a new PIN confirmation - m_dpin = wxMin(rand() % 10000 + 1, 9999); - m_pin.Printf("%04d", m_dpin); - - std::string new_api_key = PintoRandomKeyString(m_dpin); - - // Add new PIN to map and persist it - m_key_map[event.m_source_peer] = new_api_key; - SaveConfig(); - - wxString hmsg(event.m_source_peer.c_str()); - hmsg += " "; - hmsg += - _("wants to send you new data.\nPlease enter the following PIN number " - "on "); - hmsg += wxString(event.m_source_peer.c_str()); - hmsg += _(" to pair with this device.\n"); - m_pin_dialog = - m_dlg_ctx.show_dialog(hmsg.ToStdString(), m_pin.ToStdString()); - + if (CheckApiKey(*evt_data)) { + UpdateReturnStatus(RestServerResult::NoError); + } else { UpdateReturnStatus(RestServerResult::NewPinRequested); return; - } else { - UpdateReturnStatus(RestServerResult::NoError); } - // GUI dialogs can go here.... - bool b_cont; - b_cont = true; - - if (b_cont) { // Load the GPX file - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(m_tmp_upload_path.c_str()); - if (result.status == pugi::status_ok) { - m_tmp_upload_path.clear(); // empty for next time - - pugi::xml_node objects = doc.child("gpx"); - for (pugi::xml_node object = objects.first_child(); object; - object = object.next_sibling()) { - if (!strcmp(object.name(), "rte")) { - Route* route = NULL; - route = GPXLoadRoute1(object, true, false, false, 0, true); - // Check for duplicate GUID - bool add = true; - bool overwrite_one = false; - Route* duplicate = m_route_ctx.find_route_by_guid(route->GetGUID()); - if (duplicate) { - if (!m_overwrite) { - auto result = m_dlg_ctx.run_accept_object_dlg( - _("The received route already exists on this " - "system.\nReplace?"), - _("Always replace objects?")); - - if (result.status != ID_STG_OK) { - add = false; - UpdateReturnStatus(RestServerResult::DuplicateRejected); - } else { - m_overwrite = result.check1_value; - overwrite_one = true; - SaveConfig(); - } - } - - if (m_overwrite || overwrite_one) { - // Remove the existing duplicate route before adding new route - m_route_ctx.delete_route(duplicate); - } - } - - if (add) { - // And here is the payoff.... - - // Add the route to the global list - NavObjectCollection1 pSet; - - if (InsertRouteA(route, &pSet)) - UpdateReturnStatus(RestServerResult::NoError); - else - UpdateReturnStatus(RestServerResult::RouteInsertError); - m_dlg_ctx.top_level_refresh(); - } - } else if (!strcmp(object.name(), "trk")) { - Track* route = NULL; - route = GPXLoadTrack1(object, true, false, false, 0); - // Check for duplicate GUID - bool add = true; - bool overwrite_one = false; - - Track* duplicate = m_route_ctx.find_track_by_guid(route->m_GUID); - if (duplicate) { - if (!m_overwrite) { - auto result = m_dlg_ctx.run_accept_object_dlg( - _("The received track already exists on this " - "system.\nReplace?"), - _("Always replace objects?")); - - if (result.status != ID_STG_OK) { - add = false; - UpdateReturnStatus(RestServerResult::DuplicateRejected); - } else { - m_overwrite = result.check1_value; - overwrite_one = true; - SaveConfig(); - } - } - if (m_overwrite || overwrite_one) { - m_route_ctx.delete_track(duplicate); - } - } - - if (add) { - // And here is the payoff.... - - // Add the route to the global list - NavObjectCollection1 pSet; - - if (InsertTrack(route, false)) - UpdateReturnStatus(RestServerResult::NoError); - else - UpdateReturnStatus(RestServerResult::RouteInsertError); - m_dlg_ctx.top_level_refresh(); - } - } else if (!strcmp(object.name(), "wpt")) { - RoutePoint* rp = NULL; - rp = GPXLoadWaypoint1(object, "circle", "", false, false, false, 0); - rp->m_bIsolatedMark = true; // This is an isolated mark - // Check for duplicate GUID - bool add = true; - bool overwrite_one = false; - - RoutePoint* duplicate = - WaypointExists(rp->GetName(), rp->m_lat, rp->m_lon); - if (duplicate) { - if (!m_overwrite) { - auto result = m_dlg_ctx.run_accept_object_dlg( - _("The received waypoint already exists on this " - "system.\nReplace?"), - _("Always replace objects?")); - - if (result.status != ID_STG_OK) { - add = false; - UpdateReturnStatus(RestServerResult::DuplicateRejected); - } else { - m_overwrite = result.check1_value; - overwrite_one = true; - SaveConfig(); - } - } - } - if (add) { - // And here is the payoff.... - if (InsertWpt(rp, m_overwrite || overwrite_one)) - UpdateReturnStatus(RestServerResult::NoError); - else - UpdateReturnStatus(RestServerResult::RouteInsertError); - m_dlg_ctx.top_level_refresh(); - } - } + if (evt_data->cmd == RestIoEvtData::Cmd::CheckWrite) { + auto guid = evt_data->payload; + auto dup = m_route_ctx.find_route_by_guid(guid); + if (!dup || evt_data->force || m_overwrite) { + UpdateReturnStatus(RestServerResult::NoError); + } else { + UpdateReturnStatus(RestServerResult::DuplicateRejected); + } + return; + } + // Load the GPX file + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(m_upload_path.c_str()); + if (result.status == pugi::status_ok) { + m_upload_path.clear(); // empty for next time + + pugi::xml_node objects = doc.child("gpx"); + for (pugi::xml_node object = objects.first_child(); object; + object = object.next_sibling()) { + if (!strcmp(object.name(), "rte")) { + HandleRoute(object, *evt_data); + } else if (!strcmp(object.name(), "trk")) { + HandleTrack(object, *evt_data); + } else if (!strcmp(object.name(), "wpt")) { + HandleWaypoint(object, *evt_data); } } - } else { - UpdateReturnStatus(RestServerResult::ObjectRejected); } } -RestServer::IoThread::IoThread(RestServer* parent, bool& portable) - : m_portable(portable), m_parent(parent) { - server_ip = s_https_addr; - // If Portable use another port - if (m_portable) { - server_ip = s_https_addr_portable; - wxString sip(server_ip); - wxLogMessage("Portable REST server IP: Port " + sip); +void RestServer::HandleRoute(pugi::xml_node object, + const RestIoEvtData& evt_data) { + Route* route = NULL; + route = GPXLoadRoute1(object, true, false, false, 0, true); + // Check for duplicate GUID + bool add = true; + bool overwrite_one = false; + Route* duplicate = m_route_ctx.find_route_by_guid(route->GetGUID()); + if (duplicate && !evt_data.force) { + if (!m_overwrite) { + auto result = m_dlg_ctx.run_accept_object_dlg( + _("The received route already exists on this system.\nReplace?"), + _("Always replace objects?")); + if (result.status != ID_STG_OK) { + add = false; + UpdateReturnStatus(RestServerResult::DuplicateRejected); + } else { + m_overwrite = result.check1_value; + overwrite_one = true; + SaveConfig(); + } + } + + if (m_overwrite || overwrite_one) { + // Remove the existing duplicate route before adding new route + m_route_ctx.delete_route(duplicate); + } + } + if (add) { + // Add the route to the global list + NavObjectCollection1 pSet; + + if (InsertRouteA(route, &pSet)) + UpdateReturnStatus(RestServerResult::NoError); + else + UpdateReturnStatus(RestServerResult::RouteInsertError); + m_dlg_ctx.top_level_refresh(); } } -void RestServer::IoThread::Entry() { - bool not_done = true; - run_flag = 1; - struct mg_mgr mgr; // Event manager - mg_log_set(MG_LL_DEBUG); // Set log level - mg_mgr_init(&mgr); // Initialise event manager +void RestServer::HandleTrack(pugi::xml_node object, + const RestIoEvtData& evt_data) { + Track* route = NULL; + route = GPXLoadTrack1(object, true, false, false, 0); + // Check for duplicate GUID + bool add = true; + bool overwrite_one = false; + + Track* duplicate = m_route_ctx.find_track_by_guid(route->m_GUID); + if (duplicate) { + if (!m_overwrite && !evt_data.force) { + auto result = m_dlg_ctx.run_accept_object_dlg( + _("The received track already exists on this system.\nReplace?"), + _("Always replace objects?")); + + if (result.status != ID_STG_OK) { + add = false; + UpdateReturnStatus(RestServerResult::DuplicateRejected); + } else { + m_overwrite = result.check1_value; + overwrite_one = true; + SaveConfig(); + } + } + if (m_overwrite || overwrite_one) { + m_route_ctx.delete_track(duplicate); + } + } + if (add) { + // Add the route to the global list + NavObjectCollection1 pSet; - // Create HTTPS listener -std::cout << "Listening on " << server_ip << "\n"; - mg_http_listen(&mgr, server_ip.c_str(), fn, m_parent); - // mg_http_listen(&mgr, s_https_addr, fn, (void *) 1); + if (InsertTrack(route, false)) + UpdateReturnStatus(RestServerResult::NoError); + else + UpdateReturnStatus(RestServerResult::RouteInsertError); + m_dlg_ctx.top_level_refresh(); + } +} - while (run_flag > 0) mg_mgr_poll(&mgr, 200); // Infinite event loop - mg_mgr_free(&mgr); - run_flag = -1; +void RestServer::HandleWaypoint(pugi::xml_node object, + const RestIoEvtData& evt_data) { + RoutePoint* rp = NULL; + rp = GPXLoadWaypoint1(object, "circle", "", false, false, false, 0); + rp->m_bIsolatedMark = true; // This is an isolated mark + // Check for duplicate GUID + bool add = true; + bool overwrite_one = false; + + RoutePoint* duplicate = WaypointExists(rp->GetName(), rp->m_lat, rp->m_lon); + if (duplicate) { + if (!m_overwrite && !evt_data.force) { + auto result = m_dlg_ctx.run_accept_object_dlg( + _("The received waypoint already exists on this system.\nReplace?"), + _("Always replace objects?")); + if (result.status != ID_STG_OK) { + add = false; + UpdateReturnStatus(RestServerResult::DuplicateRejected); + } else { + m_overwrite = result.check1_value; + overwrite_one = true; + SaveConfig(); + } + } + } + if (add) { + if (InsertWpt(rp, m_overwrite || overwrite_one || evt_data.force)) + UpdateReturnStatus(RestServerResult::NoError); + else + UpdateReturnStatus(RestServerResult::RouteInsertError); + m_dlg_ctx.top_level_refresh(); + } } From bcc7366525e76e534e406105cae3abfcdc6d6d38 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Fri, 24 Nov 2023 14:36:15 +0100 Subject: [PATCH 15/63] Add new pincode class --- CMakeLists.txt | 3 +++ include/pincode.h | 45 +++++++++++++++++++++++++++++++++++++++++++ src/pincode.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 1 + 4 files changed, 96 insertions(+) create mode 100644 include/pincode.h create mode 100644 src/pincode.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a3ef45f4b..e79a74fc58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -950,6 +950,7 @@ set( include/options.h include/own_ship.h include/piano.h + include/pincode.h include/plugin_cache.h include/plugin_blacklist.h include/plugin_handler.h @@ -1061,6 +1062,7 @@ set(MODEL_SRC ${CMAKE_SOURCE_DIR}/src/ocpn_plugin.cpp ${CMAKE_SOURCE_DIR}/src/ocpn_utils.cpp ${CMAKE_SOURCE_DIR}/src/own_ship.cpp + ${CMAKE_SOURCE_DIR}/src/pincode.cpp ${CMAKE_SOURCE_DIR}/src/plugin_blacklist.cpp ${CMAKE_SOURCE_DIR}/src/plugin_cache.cpp ${CMAKE_SOURCE_DIR}/src/plugin_handler.cpp @@ -3228,6 +3230,7 @@ if (NOT QT_ANDROID) target_link_libraries(opencpn-cmd PRIVATE ocpn::mongoose) target_link_libraries(opencpn-cmd PRIVATE ocpn::N2KParser) target_link_libraries(opencpn-cmd PRIVATE ocpn::nmea0183) + target_link_libraries(opencpn-cmd PRIVATE pico_sha2) target_link_libraries(opencpn-cmd PRIVATE ocpn::pugixml) target_link_libraries(opencpn-cmd PRIVATE ocpn::rapidjson) target_link_libraries(opencpn-cmd PRIVATE ocpn::s52plib) diff --git a/include/pincode.h b/include/pincode.h new file mode 100644 index 0000000000..085f3aa87e --- /dev/null +++ b/include/pincode.h @@ -0,0 +1,45 @@ + + /*************************************************************************** + * Copyright (C) 2023 Alec Leamas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ +#ifndef OPENCPN_INCLUDE_PINCODE_H_ +#define OPENCPN_INCLUDE_PINCODE_H_ + +#include +#include + +class Pincode { +public: + /** Create a new pincode based on a random value. */ + static Pincode Create(); + + /** Return numeric value: */ + uint64_t Get() const; + + /** Return value as string. */ + std::string ToString() const; + + /** Return a hashvalue string. */ + std::string Hash() const; + +private: + uint64_t m_value; + Pincode(uint64_t v) { m_value = v; } +}; + +#endif // OPENCPN_INCLUDE_PINCODE_H_ diff --git a/src/pincode.cpp b/src/pincode.cpp new file mode 100644 index 0000000000..3c862d2352 --- /dev/null +++ b/src/pincode.cpp @@ -0,0 +1,47 @@ + + +/*************************************************************************** + * Copyright (C) 2023 Alec Leamas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#include "pincode.h" + +#include +#include +#include + +#include "picosha2.h" + +Pincode Pincode::Create() { + srand(time(0)); + return Pincode(std::min(rand() % 10000 + 1, 9999)); +} + +uint64_t Pincode::Get() const { return m_value; } + +std::string Pincode::ToString() const { + std::stringstream ss; + ss << std::setw(4) << std::setfill('0') << m_value; + return ss.str(); +} + +std::string Pincode::Hash() const { + std::string hash_hex_str; + picosha2::hash256_hex_string(ToString(), hash_hex_str); + return hash_hex_str; +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 84f9a4472a..efe1d6c6ad 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -83,6 +83,7 @@ target_link_libraries(tests PRIVATE ocpn::mDNS) target_link_libraries(tests PRIVATE ocpn::mongoose) target_link_libraries(tests PRIVATE ocpn::N2KParser) target_link_libraries(tests PRIVATE ocpn::nmea0183) +target_link_libraries(tests PRIVATE pico_sha2) target_link_libraries(tests PRIVATE ocpn::pugixml) target_link_libraries(tests PRIVATE ocpn::rapidjson) target_link_libraries(tests PRIVATE ocpn::s52plib) From 5333eeb24e3da8b92e5e633904b7225b236aa21a Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Fri, 24 Nov 2023 16:36:13 +0100 Subject: [PATCH 16/63] rest_server: clean up --- include/pincode.h | 12 ++++++- include/rest_server.h | 61 +++++++++++++++++------------------ src/pincode.cpp | 43 +++++++++++++++++++++++-- src/rest_server.cpp | 75 +++++++++++++------------------------------ test/rest-tests.cpp | 45 ++++++++------------------ 5 files changed, 118 insertions(+), 118 deletions(-) diff --git a/include/pincode.h b/include/pincode.h index 085f3aa87e..f555828c37 100644 --- a/include/pincode.h +++ b/include/pincode.h @@ -23,11 +23,15 @@ #include #include +/** A random generated int value with accessors for string and hashcode. */ class Pincode { public: /** Create a new pincode based on a random value. */ static Pincode Create(); + /** Create a new pincode based on a known value. */ + Pincode(uint64_t v) { m_value = v; } + /** Return numeric value: */ uint64_t Get() const; @@ -37,9 +41,15 @@ class Pincode { /** Return a hashvalue string. */ std::string Hash() const; + /** Return a hashvalue as computed on 5.8 hosts. */ + std::string CompatHash(); + + /** convert numeric value to hash string. */ + static std::string IntToHash(uint64_t value); + private: uint64_t m_value; - Pincode(uint64_t v) { m_value = v; } + }; #endif // OPENCPN_INCLUDE_PINCODE_H_ diff --git a/include/rest_server.h b/include/rest_server.h index 9111a85ff9..32905e5f33 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -1,11 +1,7 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: - * Author: David Register, Alec Leamas - * - *************************************************************************** - * Copyright (C) 2022 by David Register, Alec Leamas * + + /*************************************************************************** + * Copyright (C) 2022 David Register * + * Copyright (C) 2022-2023 Alec Leamas * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -22,6 +18,7 @@ * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ + /** * \file * @@ -30,28 +27,28 @@ * Supports the following endpoints: * * GET /api/ping?api_key=&source= - * Basic ping check, verifies api_key i. e., the pairing. - * Parameters: - * See below - * Returns - * {"result": } + * Basic ping check, verifies api_key i. e., the pairing. + * Parameters: + * See below + * Returns + * {"result": } * * POST /api/rx_object?api_key=&source=&force=1 - * Upload a GPX route, track or waypoints. Parameters: - * - source= Mandatory, origin ip address or hostname - * - force=<1> if present, the host object is unconditionally - * updated. If not, host may run a "OK to overwrite" dialog. - * - api_key= Mandatory, as obtained when pairing, see below. - * Body: - * xml-encoded GPX data for one or more route(s), track(s) and/or - * waypoint(s) - * Returns: - * {"result": } + * Upload a GPX route, track or waypoints. Parameters: + * - source= Mandatory, origin ip address or hostname + * - force=<1> if present, the host object is unconditionally + * updated. If not, host may run a "OK to overwrite" dialog. + * - api_key= Mandatory, as obtained when pairing, see below. + * Body: + * xml-encoded GPX data for one or more route(s), track(s) and/or + * waypoint(s) + * Returns: + * {"result": } * * GET /api/writable?guid= - * Check if route or waypoint with given is writable. + * Check if route or waypoint with given is writable. * Returns - * {"result": } + * {"result": } * * Authentication uses a pairing mechanism. When an unpaired device * tries to connect, the API generates a random pincode which is @@ -80,10 +77,11 @@ #include "pugixml.hpp" +#include "pincode.h" #include "route.h" #include "track.h" -/** Return codes from HandleServerMessage. */ +/** Return codes from HandleServerMessage and eventually in the http response */ enum class RestServerResult { NoError = 0, GenericError, @@ -129,6 +127,7 @@ struct RestIoEvtData { : cmd(c), api_key(key), source(src), force(_force), payload(_payload) {} }; +/** Return hash code for numeric pin value. */ std::string PintoRandomKeyString(int dpin); /** Abstract base class visible in callbacks. */ @@ -144,8 +143,8 @@ class PinDialog { /** Returned status from RunAcceptObjectDlg. */ struct AcceptObjectDlgResult { - int status; ///< return value from ShowModal() - bool check1_value; ///< As of GetCheck1Value() + const int status; ///< return value from ShowModal() + const bool check1_value; ///< As of GetCheck1Value() AcceptObjectDlgResult() : status(0), check1_value(false) {} AcceptObjectDlgResult(int s, bool b) : status(s), check1_value(b) {} @@ -221,7 +220,7 @@ class RestServer : public wxEvtHandler { std::condition_variable return_status_condition; /** Binary exit synchronization, released when io thread exits. */ - wxSemaphore m_exit_sync; + wxSemaphore m_exit_sem; private: class IoThread { @@ -276,13 +275,13 @@ class RestServer : public wxEvtHandler { std::string m_certificate_directory; Apikeys m_key_map; PinDialog* m_pin_dialog; - wxString m_pin; - int m_dpin; + bool m_overwrite; std::string m_upload_path; std::ofstream m_ul_stream; std::thread m_thread; IoThread m_io_thread; + Pincode m_pincode; }; #endif // guard diff --git a/src/pincode.cpp b/src/pincode.cpp index 3c862d2352..c1137d1849 100644 --- a/src/pincode.cpp +++ b/src/pincode.cpp @@ -23,9 +23,44 @@ #include #include +#include #include #include "picosha2.h" +/** +// Need a new PIN confirmation + m_dPIN = wxMin(rand() % 10000 + 1, 9999); + m_sPIN.Printf("%04d", m_dPIN); + + std::string new_api_key = PINtoRandomKeyString(m_dPIN); + + +unsigned long long PINtoRandomKey( int dpin) { + std::linear_congruential_engine engine; + engine.seed( dpin ); + unsigned long long r = engine(); + return r; + +} + +std::string PINtoRandomKeyString( int dpin) { + unsigned long long pin = PINtoRandomKey(dpin); + char buffer[100]; + snprintf(buffer, sizeof(buffer)-1, "%0llX", pin); + return std::string(buffer); +} + + +**/ +std::string Pincode::CompatHash() { + std::linear_congruential_engine engine; + engine.seed(m_value); + unsigned long long compat_val = engine(); + char buffer[100]; + snprintf(buffer, sizeof(buffer)-1, "%0llX", compat_val); + return std::string(buffer); +} Pincode Pincode::Create() { srand(time(0)); @@ -43,5 +78,9 @@ std::string Pincode::ToString() const { std::string Pincode::Hash() const { std::string hash_hex_str; picosha2::hash256_hex_string(ToString(), hash_hex_str); - return hash_hex_str; -} \ No newline at end of file + return hash_hex_str.substr(0,12); +} + +std::string Pincode::IntToHash(uint64_t value) { + return Pincode(value).Hash(); +} diff --git a/src/rest_server.cpp b/src/rest_server.cpp index 3aaab206a4..b332703c04 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -1,11 +1,7 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: Implement RESTful server. - * Author: David Register, Alec Leamas - * - *************************************************************************** - * Copyright (C) 2022 by David Register, Alec Leamas * + + /************************************************************************** + * Copyright (C) 2022 David Register * + * Copyright (C) 2022-2023 Alec Leamas * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -23,6 +19,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ +/** \file Implement rest_server.h */ + #include #include #include @@ -53,20 +51,8 @@ static const char* const kHttpsAddr = "http://0.0.0.0:8443"; static const char* const kHttpPortableAddr = "http://0.0.0.0:8001"; static const char* const kHttpsPortableAddr = "http://0.0.0.0:8444"; -static unsigned long long PINtoRandomKey(int dpin) { - using namespace std; - linear_congruential_engine - engine; - engine.seed(dpin); - unsigned long long r = engine(); - return r; -} - std::string PintoRandomKeyString(int dpin) { - unsigned long long pin = PINtoRandomKey(dpin); - char buffer[100]; - snprintf(buffer, sizeof(buffer) - 1, "%0llX", pin); - return std::string(buffer); + return Pincode::IntToHash(dpin); } /** Extract a HTTP variable from query string. */ @@ -206,13 +192,13 @@ void RestServer::IoThread::Run() { while (run_flag > 0) mg_mgr_poll(&mgr, 200); // Infinite event loop mg_mgr_free(&mgr); run_flag = -1; - m_parent.m_exit_sync.Post(); + m_parent.m_exit_sem.Post(); } void RestServer::IoThread::Stop() { run_flag = 0; } bool RestServer::IoThread::WaitUntilStopped() { - auto r = m_parent.m_exit_sync.WaitTimeout(10000); + auto r = m_parent.m_exit_sem.WaitTimeout(10000); if (r != wxSEMA_NO_ERROR) { WARNING_LOG << "Semaphore error: " << r; } @@ -231,6 +217,7 @@ RestServer::Apikeys RestServer::Apikeys::Parse(const std::string& s) { } return apikeys; } + std::string RestServer::Apikeys::ToString() const { std::stringstream ss; for (const auto& it : *this) @@ -238,7 +225,6 @@ std::string RestServer::Apikeys::ToString() const { return ss.str(); } - void RestServer::UpdateReturnStatus(RestServerResult result) { { std::lock_guard lock{ret_mutex}; @@ -248,11 +234,12 @@ void RestServer::UpdateReturnStatus(RestServerResult result) { } RestServer::RestServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) - : m_exit_sync(0, 1), + : m_exit_sem(0, 1), m_dlg_ctx(ctx), m_route_ctx(route_ctx), m_pin_dialog(0), - m_io_thread(*this, portable ? kHttpsPortableAddr : kHttpsAddr) { + m_io_thread(*this, portable ? kHttpsPortableAddr : kHttpsAddr), + m_pincode(Pincode::Create()) { // Prepare the wxEventHandler to accept events from the io thread Bind(REST_IO_EVT, &RestServer::HandleServerMessage, this); } @@ -276,7 +263,6 @@ bool RestServer::StartServer(fs::path certificate_location) { void RestServer::StopServer() { wxLogMessage(wxString::Format("Stopping REST service")); - // Kill off the IO Thread if alive if (m_thread.joinable()) { wxLogMessage("Stopping io thread"); @@ -287,9 +273,7 @@ void RestServer::StopServer() { } bool RestServer::LoadConfig(void) { - TheBaseConfig()->SetPath("/Settings/RestServer"); - wxString key_string; TheBaseConfig()->Read("ServerKeys", &key_string); m_key_map = Apikeys::Parse(key_string.ToStdString()); @@ -306,23 +290,13 @@ bool RestServer::SaveConfig(void) { } bool RestServer::CheckApiKey(const RestIoEvtData& evt_data) { - // Look up the api key in the hash map. - std::string api_found; - for (auto it : m_key_map) { - if (it.first == evt_data.source && it.second == evt_data.api_key) { - api_found = it.second; - break; - } - } - if (api_found.size()) { - return true; + // Look up the api key in the hash map. If found, we are done. + if (m_key_map.find(evt_data.source) != m_key_map.end()) { + if (m_key_map[evt_data.source] == evt_data.api_key) return true; } - // Need a new PIN confirmation - m_dpin = wxMin(rand() % 10000 + 1, 9999); - m_pin.Printf("%04d", m_dpin); - std::string new_api_key = PintoRandomKeyString(m_dpin); - - // Add new PIN to map and persist it + // Need a new PIN confirmation, add it to map and persist + m_pincode = Pincode::Create(); + std::string new_api_key = m_pincode.Hash(); m_key_map[evt_data.source] = new_api_key; SaveConfig(); @@ -330,7 +304,7 @@ bool RestServer::CheckApiKey(const RestIoEvtData& evt_data) { ss << evt_data.source << " " << _("wants to send you new data.") << "\n" << _("Please enter the following PIN number on ") << evt_data.source << " " << _("to pair with this device") << "\n"; - m_pin_dialog = m_dlg_ctx.show_dialog(ss.str(), m_pin.ToStdString()); + m_pin_dialog = m_dlg_ctx.show_dialog(ss.str(), m_pincode.ToString()); return false; } @@ -359,10 +333,8 @@ void RestServer::HandleServerMessage(ObservedEvt& event) { } if (event.GetId() == ORS_CHUNK_LAST) { - // Cancel existing dialog + // Cancel existing dialog and close temp file m_dlg_ctx.close_dialog(m_pin_dialog); - - // Close the temp file. if (m_upload_path.size() && m_ul_stream.is_open()) m_ul_stream.close(); // Io thread might be waiting for return_status on notify_one() @@ -429,7 +401,7 @@ void RestServer::HandleRoute(pugi::xml_node object, } } - if (m_overwrite || overwrite_one) { + if (m_overwrite || overwrite_one || evt_data.force) { // Remove the existing duplicate route before adding new route m_route_ctx.delete_route(duplicate); } @@ -437,7 +409,6 @@ void RestServer::HandleRoute(pugi::xml_node object, if (add) { // Add the route to the global list NavObjectCollection1 pSet; - if (InsertRouteA(route, &pSet)) UpdateReturnStatus(RestServerResult::NoError); else @@ -470,7 +441,7 @@ void RestServer::HandleTrack(pugi::xml_node object, SaveConfig(); } } - if (m_overwrite || overwrite_one) { + if (m_overwrite || overwrite_one || evt_data.force) { m_route_ctx.delete_track(duplicate); } } diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index 0555657c00..ba0687c16d 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -57,15 +57,13 @@ class RestServerApp : public wxAppConsole { // Handle buggy make_certificate: make_certificate(local_address, dirpath.string() + "/"); m_rest_server.StartServer(dirpath.string()); - Work() ; + Work(); ProcessPendingEvents(); m_rest_server.StopServer(); } protected: virtual void Work() { std::this_thread::sleep_for(50ms); } - -protected: RestServer m_rest_server; }; @@ -94,7 +92,6 @@ class RestServerPingApp : public RestServerApp { ss << CURLPROG << " --insecure -o " << path << " \"https://localhost:8443/api/ping?source=1.2.3.4&apikey=" << key << "\""; -std::cout << "running cmd: " << ss.str() << "\n"; system(ss.str().c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); @@ -128,8 +125,7 @@ class RestServerObjectApp : public RestServerApp { std::string result; std::getline(f, result); EXPECT_EQ(result, "{\"result\": 5}"); // New pin required - } - { + } { // Try to transfer using api key set up above. std::stringstream ss; auto key = m_rest_server.m_key_map["1.2.3.4"]; @@ -137,7 +133,6 @@ class RestServerObjectApp : public RestServerApp { << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/rx_object?source=1.2.3.4" << "&apikey=" << key << "\""; - system(ss.str().c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); @@ -145,8 +140,7 @@ class RestServerObjectApp : public RestServerApp { std::string result; std::getline(f, result); EXPECT_EQ(result, "{\"result\": 0}"); // Ok - } - { + } { // Set "find duplicate guid" callback to return true; m_rest_server.m_route_ctx.find_route_by_guid = [](wxString guid) { auto r = new Route; @@ -165,7 +159,6 @@ class RestServerObjectApp : public RestServerApp { << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/rx_object?source=1.2.3.4" << "&apikey=" << key << "\""; - system(ss.str().c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); @@ -173,8 +166,7 @@ class RestServerObjectApp : public RestServerApp { std::string result; std::getline(f, result); EXPECT_EQ(result, "{\"result\": 3}"); // Duplicate rejected - } - { + } { // Try to transfer same object using argument force std::stringstream ss; auto key = m_rest_server.m_key_map["1.2.3.4"]; @@ -183,7 +175,6 @@ class RestServerObjectApp : public RestServerApp { << " \"https://localhost:8443/api/rx_object?source=1.2.3.4" << "&force=1&apikey=" << key << "\""; system(ss.str().c_str()); -std::cout << "Running cmd: " << ss.str() << "\n"; std::this_thread::sleep_for(50ms); ProcessPendingEvents(); std::ifstream f(outpath.string()); @@ -193,6 +184,7 @@ std::cout << "Running cmd: " << ss.str() << "\n"; } } }; + class RestCheckWriteApp : public RestServerApp { public: RestCheckWriteApp(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) @@ -202,19 +194,17 @@ class RestCheckWriteApp : public RestServerApp { void Work() override { - auto datapath = fs::path(TESTDATA) / "foo.gpx"; auto outpath = fs::path(CMAKE_BINARY_DIR) / "curl-result"; { std::stringstream ss; - auto key = m_rest_server.m_key_map["1.2.3.4"]; ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/writable?source=1.2.3.4" - << "&apikey=" << key + << "&apikey=" << "foobar" << "&guid=6a76a7e6-dc39-4a7d-964e-1eff3462c06c\""; - // Try check our standard object, bad api key + // Try check our standard object, bad api key std::cout << "Running command; " << ss.str() << "\n"; system(ss.str().c_str()); std::this_thread::sleep_for(50ms); @@ -222,21 +212,16 @@ class RestCheckWriteApp : public RestServerApp { std::ifstream f(outpath.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 5}"); // Ok - } - { - auto key = m_rest_server.m_key_map["1.2.3.4"]; + EXPECT_EQ(result, "{\"result\": 5}"); // New pin required + } { // Try check our standard object, fix the api key - key = m_rest_server.m_key_map["1.2.3.4"]; + auto key = m_rest_server.m_key_map["1.2.3.4"]; std::stringstream ss; - ss.clear(); ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/writable?source=1.2.3.4" << "&apikey=" << key << "&guid=6a76a7e6-dc39-4a7d-964e-1eff3462c06c\""; - ProcessPendingEvents(); -std::cout << "Running command; " << ss.str() << "\n"; system(ss.str().c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); @@ -244,25 +229,20 @@ std::cout << "Running command; " << ss.str() << "\n"; std::string result; std::getline(f, result); EXPECT_EQ(result, "{\"result\": 0}"); // Ok - } - - { - // Set "find duplicate guid" callback to return true; + } { + // Set "find duplicate guid" callback to return true; m_rest_server.m_route_ctx.find_route_by_guid = [](wxString guid) { auto r = new Route; r->m_GUID = guid; return r; }; auto key = m_rest_server.m_key_map["1.2.3.4"]; - std::cout << "USing key: " << key << "\n"; std::stringstream ss; ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/writable?source=1.2.3.4" << "&apikey=" << key << "&guid=apikey6a76a7e6-dc39-4a7d-964e-1eff3462c06c\""; -std::cout << "Running apikey command; " << ss.str() << "\n"; - ProcessPendingEvents(); system(ss.str().c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); @@ -273,6 +253,7 @@ std::cout << "Running apikey command; " << ss.str() << "\n"; } } }; + TEST(RestServer, start_stop) { wxInitializer initializer; ConfigSetup(); From e285c2cd1a4132a474799ad237795322a4f81fde Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Fri, 24 Nov 2023 18:16:39 +0100 Subject: [PATCH 17/63] tests: Fix platform issues, add 404 test --- CMakeLists.txt | 7 +++++++ include/rest_server.h | 14 ++++++++----- src/ocpn_app.cpp | 11 +++++++++-- src/rest_server.cpp | 2 +- test/CMakeLists.txt | 16 +++++++++++---- test/rest-tests.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++- test/tests.cpp | 10 +++++----- 7 files changed, 88 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e79a74fc58..d01574bb57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -465,6 +465,10 @@ if (MSVC) add_definitions(-D__MSVC__) add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_SECURE_NO_DEPRECATE) add_definitions(-DPSAPI_VERSION=1) + # https://developercommunity.visualstudio.com + # /t/error-c2872-byte-ambiguous-symbol/93889 + # https://shorturl.at/hmJSZ + add_definitions(-D_HAS_STD_BYTE=0) endif (MSVC) if (MSVC) @@ -504,6 +508,9 @@ if (QT_ANDROID) set(CMAKE_SHARED_LINKER_FLAGS "-Wl,-soname,libgorp.so -Wl,--build-id") endif (QT_ANDROID) +if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) + link_libraries(stdc++fs ) +endif () message(STATUS "Final compiler options:") message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") message(STATUS "CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}") diff --git a/include/rest_server.h b/include/rest_server.h index 32905e5f33..565a59f10e 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -64,13 +64,20 @@ #include #include -#include #include #include #include #include #include +#if defined(__GNUC__) && (__GNUC__ < 8) +#include +namespace fs = std::experimental::filesystem; +#else +#include +namespace fs = std::filesystem; +#endif + #include #include #include // for wxSemaphore, std::semaphore is c++20 @@ -201,7 +208,7 @@ class RestServer : public wxEvtHandler { virtual ~RestServer(); - bool StartServer(std::filesystem::path certificate_location); + bool StartServer(fs::path certificate_location); void StopServer(); void UpdateReturnStatus(RestServerResult r); @@ -209,7 +216,6 @@ class RestServer : public wxEvtHandler { void UpdateRouteMgr() { m_dlg_ctx.update_route_mgr(); } - std::string GetCertificateDirectory() { return m_certificate_directory; } std::string m_cert_file; std::string m_key_file; @@ -242,7 +248,6 @@ class RestServer : public wxEvtHandler { std::atomic_int run_flag; RestServer& m_parent; std::string m_server_ip; - std::thread m_thread; }; /** @@ -255,7 +260,6 @@ class RestServer : public wxEvtHandler { std::string ToString() const; }; - bool LoadConfig(void); bool SaveConfig(void); diff --git a/src/ocpn_app.cpp b/src/ocpn_app.cpp index 17954f00fd..ab7c5b0f2b 100644 --- a/src/ocpn_app.cpp +++ b/src/ocpn_app.cpp @@ -40,10 +40,17 @@ #endif #include -#include #include #include +#if defined(__GNUC__) && (__GNUC__ < 8) +#include +namespace fs = std::experimental::filesystem; +#else +#include +namespace fs = std::filesystem; +#endif + #ifdef __WXMSW__ #include #include @@ -1980,7 +1987,7 @@ bool MyApp::OnInit() { make_certificate(ipAddr, data_dir.ToStdString()); - m_RESTserver.StartServer(std::filesystem::path(data_dir.ToStdString())); + m_RESTserver.StartServer(fs::path(data_dir.ToStdString())); StartMDNSService(g_hostname.ToStdString(), "opencpn-object-control-service", 8000); diff --git a/src/rest_server.cpp b/src/rest_server.cpp index b332703c04..f261bb5311 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -42,7 +42,7 @@ /** Event from IO thread to main */ wxDEFINE_EVENT(REST_IO_EVT, ObservedEvt); -namespace fs = std::filesystem; + using namespace std::chrono_literals; static const char* const kHttpAddr = "http://0.0.0.0:8000"; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index efe1d6c6ad..1b1eed8e82 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,13 +12,17 @@ set(PROJ_SRC ${PROJECT_SOURCE_DIR}/../src) set(SRC tests.cpp - rest-tests.cpp ${MODEL_SRC} ${CMAKE_SOURCE_DIR}/src/api_shim.cpp ${CMAKE_SOURCE_DIR}/src/base_platform.cpp ${CMAKE_SOURCE_DIR}/src/certificates.cpp ${CMAKE_SOURCE_DIR}/src/mDNS_query.cpp ) + +if (CURL) + list(APPEND SRC rest-tests.cpp) +endif () + if (LINUX) list(APPEND SRC n2k_tests.cpp) endif () @@ -32,14 +36,18 @@ target_compile_definitions(tests CLIAPP USE_MOCK_DEFS CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}" TESTDATA="${CMAKE_CURRENT_LIST_DIR}/testdata" ) - -find_program(CURL NAMES curl) +if (MSVC) + set(CURL_HINTS C:\\mingw64\\bin C:\\ProgramData\\chocolatey\\bin) + find_program(CURL NAMES curl HINTS ${CURL_HINTS}) +endif () if (CURL) target_compile_definitions(tests PUBLIC CURLPROG="${CURL}") endif () if (MSVC) - target_link_libraries(tests PRIVATE setupapi.lib psapi.lib) + target_link_libraries(tests + PRIVATE setupapi.lib psapi.lib ${CMAKE_SOURCE_DIR}/cache/buildwin/iphlpapi.lib + ) endif () target_include_directories( tests diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index ba0687c16d..3cce397f73 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -20,10 +19,18 @@ #include "mDNS_query.h" #include "observable_confvar.h" #include "ocpn_types.h" +#include "ocpn_utils.h" #include "rest_server.h" #include "routeman.h" +#if defined(__GNUC__) && __GNUC__ == 7 +#include +namespace fs = std::experimental::filesystem; +#else +#include namespace fs = std::filesystem; +#endif + using namespace std::chrono_literals; extern WayPointman* pWayPointMan; @@ -103,6 +110,30 @@ class RestServerPingApp : public RestServerApp { } }; +class RestServer404App : public RestServerApp { +public: + RestServer404App(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) + : RestServerApp(ctx, route_ctx, portable) {} + +protected: + void Work() { + auto path = fs::path(CMAKE_BINARY_DIR) / "curl-result"; + + std::stringstream ss; + ss << CURLPROG << " --insecure --max-time 10 -I -o " << path + << " https://localhost:8443/api/pong"; + system(ss.str().c_str()); + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + std::ifstream f(path.string()); + std::string result; + std::getline(f, result); + auto words = ocpn::split(result.c_str(), " "); + EXPECT_EQ(words[1], "404"); // ok + } +}; + + class RestServerObjectApp : public RestServerApp { public: RestServerObjectApp(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) @@ -253,6 +284,7 @@ class RestCheckWriteApp : public RestServerApp { } } }; +#ifndef FLATPAK TEST(RestServer, start_stop) { wxInitializer initializer; @@ -273,6 +305,16 @@ TEST(RestServer, Ping) { app.Run(); }; +TEST(RestServer, Pong) { + wxInitializer initializer; + ConfigSetup(); + RestServerDlgCtx dialog_ctx; + RouteCtx route_ctx; + RestServer404App app(dialog_ctx, route_ctx, g_portable); + app.Run(); +}; + + TEST(RestServer, Object) { wxInitializer initializer; ConfigSetup(); @@ -301,3 +343,5 @@ TEST(RestServer, CheckWrite) { RestCheckWriteApp app(dialog_ctx, route_ctx, g_portable); app.Run(); } + +#endif // FLATPAK diff --git a/test/tests.cpp b/test/tests.cpp index 759abcd32a..07293246f3 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -356,9 +357,8 @@ class GuernseyApp : public wxAppConsole { public: GuernseyApp(vector& log) : wxAppConsole() { auto& msgbus = NavMsgBus::GetInstance(); - string path(".."); - path += kSEP + ".." + kSEP + "test" + kSEP + "testdata" + kSEP + - "Guernesey-1659560590623.input.txt"; + string path(TESTDATA); + path += kSEP + "Guernesey-1659560590623.input.txt"; auto driver = make_shared("test-output.txt", path, msgbus); listener.Listen(Nmea0183Msg("GPGLL"), this, EVT_FOO); Bind(EVT_FOO, [&log](ObservedEvt ev) { @@ -378,8 +378,8 @@ class PriorityApp : public wxAppConsole { PriorityApp(string inputfile) : wxAppConsole() { ConfigSetup(); auto& msgbus = NavMsgBus::GetInstance(); - string path(".."); - path += kSEP + ".." + kSEP + "test" + kSEP + "testdata" + kSEP + inputfile; + std::string path(TESTDATA); + path += kSEP + inputfile; auto driver = make_shared(inputfile + ".log", path, msgbus); CommBridge comm_bridge; comm_bridge.Initialize(); From 667c961257e9bafed55d466fc5df3777e433d831 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sun, 26 Nov 2023 15:27:09 +0100 Subject: [PATCH 18/63] peer_client: clang-format --- src/peer_client.cpp | 207 +++++++++++++++++++++----------------------- 1 file changed, 99 insertions(+), 108 deletions(-) diff --git a/src/peer_client.cpp b/src/peer_client.cpp index d5d55e701e..f4936232cd 100644 --- a/src/peer_client.cpp +++ b/src/peer_client.cpp @@ -23,7 +23,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ - #include #include @@ -42,9 +41,9 @@ #include "nav_object_database.h" #include "rest_server.h" -extern MyFrame *gFrame; +extern MyFrame* gFrame; -wxString GetErrorText(RestServerResult result){ +wxString GetErrorText(RestServerResult result) { switch (result) { case RestServerResult::GenericError: return _("Server Generic Error"); @@ -53,46 +52,44 @@ wxString GetErrorText(RestServerResult result){ case RestServerResult::DuplicateRejected: return _("Peer rejected duplicate object"); case RestServerResult::RouteInsertError: - return _("Peer internal error (insert)"); + return _("Peer internal error (insert)"); default: return _("Server Unknown Error"); } } -size_t wxcurl_string_write_UTF8(void* ptr, size_t size, size_t nmemb, void* pcharbuf) -{ - size_t iRealSize = size * nmemb; - wxCharBuffer* pStr = (wxCharBuffer*) pcharbuf; +size_t wxcurl_string_write_UTF8(void* ptr, size_t size, size_t nmemb, + void* pcharbuf) { + size_t iRealSize = size * nmemb; + wxCharBuffer* pStr = (wxCharBuffer*)pcharbuf; - if(pStr) - { + if (pStr) { #ifdef __WXMSW__ - wxString str1a = wxString(*pStr); - wxString str2 = wxString((const char*)ptr, wxConvUTF8, iRealSize); - *pStr = (str1a + str2).mb_str(); + wxString str1a = wxString(*pStr); + wxString str2 = wxString((const char*)ptr, wxConvUTF8, iRealSize); + *pStr = (str1a + str2).mb_str(); #else - wxString str = wxString(*pStr, wxConvUTF8) + wxString((const char*)ptr, wxConvUTF8, iRealSize); - *pStr = str.mb_str(wxConvUTF8); + wxString str = wxString(*pStr, wxConvUTF8) + + wxString((const char*)ptr, wxConvUTF8, iRealSize); + *pStr = str.mb_str(wxConvUTF8); #endif - } + } - return iRealSize; + return iRealSize; } - struct MemoryStruct { - char *memory; + char* memory; size_t size; }; -static size_t -WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) -{ +static size_t WriteMemoryCallback(void* contents, size_t size, size_t nmemb, + void* userp) { size_t realsize = size * nmemb; - struct MemoryStruct *mem = (struct MemoryStruct *)userp; + struct MemoryStruct* mem = (struct MemoryStruct*)userp; - char *ptr = (char *)realloc(mem->memory, mem->size + realsize + 1); - if(!ptr) { + char* ptr = (char*)realloc(mem->memory, mem->size + realsize + 1); + if (!ptr) { /* out of memory! */ printf("not enough memory (realloc returned NULL)\n"); return 0; @@ -108,41 +105,41 @@ WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) int navobj_transfer_progress; -int xfer_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, +int xfer_callback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { - if (ultotal == 0) { - navobj_transfer_progress = 0; - } else { - navobj_transfer_progress = 100 * ulnow / ultotal; - } - wxYield(); - return 0; - } - -long PostSendObjectMessage( std::string url, std::ostringstream &body, - MemoryStruct *response){ + if (ultotal == 0) { + navobj_transfer_progress = 0; + } else { + navobj_transfer_progress = 100 * ulnow / ultotal; + } + wxYield(); + return 0; +} +long PostSendObjectMessage(std::string url, std::ostringstream& body, + MemoryStruct* response) { long response_code = -1; navobj_transfer_progress = 0; CURL* c = curl_easy_init(); - curl_easy_setopt(c, CURLOPT_ENCODING, "identity"); // No encoding, plain ASCII + curl_easy_setopt(c, CURLOPT_ENCODING, + "identity"); // No encoding, plain ASCII curl_easy_setopt(c, CURLOPT_URL, url.c_str()); curl_easy_setopt(c, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(c, CURLOPT_SSL_VERIFYHOST, 0L); - int iSize = strlen(body.str().c_str()); + int iSize = strlen(body.str().c_str()); curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE, iSize); curl_easy_setopt(c, CURLOPT_COPYPOSTFIELDS, body.str().c_str()); curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(c, CURLOPT_WRITEDATA, (void *)response); + curl_easy_setopt(c, CURLOPT_WRITEDATA, (void*)response); curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(c, CURLOPT_XFERINFOFUNCTION, xfer_callback); CURLcode result = curl_easy_perform(c); navobj_transfer_progress = 0; - if(result == CURLE_OK) + if (result == CURLE_OK) curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &response_code); curl_easy_cleanup(c); @@ -150,14 +147,13 @@ long PostSendObjectMessage( std::string url, std::ostringstream &body, return response_code; } -std::string GetClientKey( std::string &server_name ) -{ +std::string GetClientKey(std::string& server_name) { if (TheBaseConfig()) { TheBaseConfig()->SetPath("/Settings/RESTClient"); wxString key_string; - TheBaseConfig()->Read("ServerKeys", &key_string ); + TheBaseConfig()->Read("ServerKeys", &key_string); wxStringTokenizer st(key_string, _T(";")); while (st.HasMoreTokens()) { wxString s1 = st.GetNextToken(); @@ -171,14 +167,13 @@ std::string GetClientKey( std::string &server_name ) return "1"; } -void SaveClientKey( std::string &server_name, std::string key ) -{ +void SaveClientKey(std::string& server_name, std::string key) { if (TheBaseConfig()) { TheBaseConfig()->SetPath("/Settings/RESTClient"); wxArrayString array; wxString key_string; - TheBaseConfig()->Read("ServerKeys", &key_string ); + TheBaseConfig()->Read("ServerKeys", &key_string); wxStringTokenizer st(key_string, _T(";")); while (st.HasMoreTokens()) { wxString s1 = st.GetNextToken(); @@ -186,41 +181,39 @@ void SaveClientKey( std::string &server_name, std::string key ) } bool b_updated = false; - for (unsigned int i=0; iWrite("ServerKeys", key_string_updated ); - + TheBaseConfig()->Write("ServerKeys", key_string_updated); } - return ; + return; } - - -int SendNavobjects(std::string dest_ip_address, std::string server_name, std::vector route, std::vector routepoint, std::vector track, bool overwrite) -{ - if(route.empty() && routepoint.empty() && track.empty()) - return -1; +int SendNavobjects(std::string dest_ip_address, std::string server_name, + std::vector route, + std::vector routepoint, + std::vector track, bool overwrite) { + if (route.empty() && routepoint.empty() && track.empty()) return -1; bool apikey_ok = false; bool b_cancel = false; std::ostringstream stream; @@ -235,25 +228,25 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, std::ve url += std::string("&apikey=") + api_key; struct MemoryStruct chunk; - chunk.memory = (char *)malloc(1); + chunk.memory = (char*)malloc(1); chunk.size = 0; - long response_code = PostSendObjectMessage( url, stream, &chunk); + long response_code = PostSendObjectMessage(url, stream, &chunk); - if(response_code == 200){ + if (response_code == 200) { wxString body(chunk.memory); - wxJSONValue root; + wxJSONValue root; wxJSONReader reader; - int numErrors = reader.Parse( body, &root ); + int numErrors = reader.Parse(body, &root); // Capture the result int result = root["result"].AsInt(); if (result > 0) { if (result == static_cast(RestServerResult::NewPinRequested)) { - // Show the dialog asking for PIN - PINConfirmDialog dlg((wxWindow *)gFrame, wxID_ANY, _("OpenCPN Server Message"), - "", wxDefaultPosition, wxDefaultSize, SYMBOL_PCD_STYLE ); + PINConfirmDialog dlg( + (wxWindow*)gFrame, wxID_ANY, _("OpenCPN Server Message"), "", + wxDefaultPosition, wxDefaultSize, SYMBOL_PCD_STYLE); wxString hmsg(_("The server ")); hmsg += _("needs a PIN.\nPlease enter the PIN number from "); @@ -266,24 +259,21 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, std::ve if (dlg.GetReturnCode() == ID_PCD_OK) { wxString PIN_tentative = dlg.GetText1Value().Trim().Trim(false); unsigned int dPIN = atoi(PIN_tentative.ToStdString().c_str()); - std::string new_api_key = PintoRandomKeyString(dPIN);; + std::string new_api_key = PintoRandomKeyString(dPIN); + ; SaveClientKey(server_name, new_api_key); - } - else + } else b_cancel = true; - } - else if (result == static_cast(RestServerResult::GenericError)) + } else if (result == static_cast(RestServerResult::GenericError)) apikey_ok = true; - } - else + } else apikey_ok = true; - } - else{ + } else { wxString err_msg; err_msg.Printf("Server HTTP response is: %ld", response_code); OCPNMessageDialog mdlg(NULL, err_msg, wxString(_("OpenCPN Info")), - wxICON_ERROR | wxOK); + wxICON_ERROR | wxOK); mdlg.ShowModal(); b_cancel = true; @@ -293,7 +283,7 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, std::ve return false; } // Get XML representation of object. - NavObjectCollection1 *pgpx = new NavObjectCollection1; + NavObjectCollection1* pgpx = new NavObjectCollection1; navobj_transfer_progress = 0; int total = route.size() + track.size() + routepoint.size(); int gpxgen = 0; @@ -326,37 +316,37 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, std::ve url += std::string("&apikey=") + api_key; struct MemoryStruct chunk; - chunk.memory = (char *)malloc(1); + chunk.memory = (char*)malloc(1); chunk.size = 0; - long response_code = PostSendObjectMessage( url, stream, &chunk); + long response_code = PostSendObjectMessage(url, stream, &chunk); - if(response_code == 200){ + if (response_code == 200) { wxString body(chunk.memory); - wxJSONValue root; + wxJSONValue root; wxJSONReader reader; - int numErrors = reader.Parse( body, &root ); + int numErrors = reader.Parse(body, &root); // Capture the result int result = root["result"].AsInt(); if (result > 0) { wxString error_text = GetErrorText(static_cast(result)); OCPNMessageDialog mdlg(NULL, error_text, wxString(_("OpenCPN Info")), - wxICON_ERROR | wxOK); + wxICON_ERROR | wxOK); mdlg.ShowModal(); b_cancel = true; } else { - OCPNMessageDialog mdlg(NULL, _("Objects successfully sent to peer OpenCPN instance."), wxString(_("OpenCPN Info")), - wxICON_INFORMATION | wxOK); + OCPNMessageDialog mdlg( + NULL, _("Objects successfully sent to peer OpenCPN instance."), + wxString(_("OpenCPN Info")), wxICON_INFORMATION | wxOK); mdlg.ShowModal(); b_cancel = true; } - } - else{ + } else { wxString err_msg; err_msg.Printf("Server HTTP response is: %ld", response_code); OCPNMessageDialog mdlg(NULL, err_msg, wxString(_("OpenCPN Info")), - wxICON_ERROR | wxOK); + wxICON_ERROR | wxOK); mdlg.ShowModal(); b_cancel = true; @@ -368,8 +358,8 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, std::ve IMPLEMENT_DYNAMIC_CLASS(PINConfirmDialog, wxDialog) BEGIN_EVENT_TABLE(PINConfirmDialog, wxDialog) - EVT_BUTTON(ID_PCD_CANCEL, PINConfirmDialog::OnCancelClick) - EVT_BUTTON(ID_PCD_OK, PINConfirmDialog::OnOKClick) +EVT_BUTTON(ID_PCD_CANCEL, PINConfirmDialog::OnCancelClick) +EVT_BUTTON(ID_PCD_OK, PINConfirmDialog::OnOKClick) END_EVENT_TABLE() PINConfirmDialog::PINConfirmDialog() { @@ -379,10 +369,11 @@ PINConfirmDialog::PINConfirmDialog() { } PINConfirmDialog::PINConfirmDialog(wxWindow* parent, wxWindowID id, - const wxString& caption, const wxString& hint, - const wxPoint& pos, const wxSize& size, long style) { + const wxString& caption, + const wxString& hint, const wxPoint& pos, + const wxSize& size, long style) { wxFont* pif = FontMgr::Get().GetFont(_T("Dialog")); - SetFont( *pif ); + SetFont(*pif); Create(parent, id, caption, hint, pos, size, style); } @@ -392,8 +383,9 @@ PINConfirmDialog::~PINConfirmDialog() { } bool PINConfirmDialog::Create(wxWindow* parent, wxWindowID id, - const wxString& caption, const wxString& hint, - const wxPoint& pos, const wxSize& size, long style) { + const wxString& caption, const wxString& hint, + const wxPoint& pos, const wxSize& size, + long style) { SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS); wxDialog::Create(parent, id, caption, pos, size, style); @@ -408,21 +400,20 @@ bool PINConfirmDialog::Create(wxWindow* parent, wxWindowID id, void PINConfirmDialog::CreateControls(const wxString& hint) { PINConfirmDialog* itemDialog1 = this; - wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL); - SetSizer(itemBoxSizer2); - + wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL); + SetSizer(itemBoxSizer2); // Add a reminder text box itemBoxSizer2->AddSpacer(20); - premtext = new wxStaticText( this, -1, "A loooooooooooooooooooooooooooooooooooooooooooooong line\n"); + premtext = new wxStaticText( + this, -1, "A loooooooooooooooooooooooooooooooooooooooooooooong line\n"); itemBoxSizer2->Add(premtext, 0, wxEXPAND | wxALL, 10); - m_pText1 = new wxTextCtrl(this, wxID_ANY, " ", - wxDefaultPosition, wxDefaultSize, wxTE_CENTRE); + m_pText1 = new wxTextCtrl(this, wxID_ANY, " ", wxDefaultPosition, + wxDefaultSize, wxTE_CENTRE); itemBoxSizer2->Add(m_pText1, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10); - // OK/Cancel/etc. wxBoxSizer* itemBoxSizer16 = new wxBoxSizer(wxHORIZONTAL); itemBoxSizer2->Add(itemBoxSizer16, 0, wxALIGN_RIGHT | wxALL, 5); @@ -431,20 +422,20 @@ void PINConfirmDialog::CreateControls(const wxString& hint) { wxDefaultPosition, wxDefaultSize, 0); itemBoxSizer16->Add(m_CancelButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - m_OKButton = new wxButton(itemDialog1, ID_PCD_OK, "OK", - wxDefaultPosition, wxDefaultSize, 0); + m_OKButton = new wxButton(itemDialog1, ID_PCD_OK, "OK", wxDefaultPosition, + wxDefaultSize, 0); itemBoxSizer16->Add(m_OKButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); m_OKButton->SetDefault(); } -void PINConfirmDialog::SetMessage(const wxString &msg) { +void PINConfirmDialog::SetMessage(const wxString& msg) { if (premtext) { premtext->SetLabel(msg); premtext->Refresh(true); } } -void PINConfirmDialog::SetText1Message(const wxString &msg) { +void PINConfirmDialog::SetText1Message(const wxString& msg) { m_pText1->ChangeValue(msg); m_pText1->Show(); GetSizer()->Fit(this); From 317e39a582ddfa5d19346b92680e594e8cb8cd9c Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 30 Nov 2023 10:39:38 +0100 Subject: [PATCH 19/63] peer client: First debug version, handles new and old servers --- src/peer_client.cpp | 106 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 7 deletions(-) diff --git a/src/peer_client.cpp b/src/peer_client.cpp index f4936232cd..fc7d4f0274 100644 --- a/src/peer_client.cpp +++ b/src/peer_client.cpp @@ -40,6 +40,7 @@ #include "gui_lib.h" #include "nav_object_database.h" #include "rest_server.h" +#include "semantic_vers.h" extern MyFrame* gFrame; @@ -117,7 +118,7 @@ int xfer_callback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, } long PostSendObjectMessage(std::string url, std::ostringstream& body, - MemoryStruct* response) { + MemoryStruct* response, bool timeout = false) { long response_code = -1; navobj_transfer_progress = 0; @@ -136,6 +137,8 @@ long PostSendObjectMessage(std::string url, std::ostringstream& body, curl_easy_setopt(c, CURLOPT_WRITEDATA, (void*)response); curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(c, CURLOPT_XFERINFOFUNCTION, xfer_callback); + if (timeout) curl_easy_setopt(c, CURLOPT_TIMEOUT, 5); + CURLcode result = curl_easy_perform(c); navobj_transfer_progress = 0; @@ -147,6 +150,22 @@ long PostSendObjectMessage(std::string url, std::ostringstream& body, return response_code; } +bool CheckApiKey(std::string url, MemoryStruct* response) { + long response_code = -1; + + CURL* c = curl_easy_init(); + curl_easy_setopt(c, CURLOPT_ENCODING, "identity"); // Encoding: plain ASCII + curl_easy_setopt(c, CURLOPT_URL, url.c_str()); + curl_easy_setopt(c, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(c, CURLOPT_SSL_VERIFYHOST, 0L); + CURLcode result = curl_easy_perform(c); + if (result == CURLE_OK) + curl_easy_getinfo(c, CURLINFO_RESPONSE_CODE, &response_code); + curl_easy_cleanup(c); + return response_code == 200; +} + + std::string GetClientKey(std::string& server_name) { if (TheBaseConfig()) { TheBaseConfig()->SetPath("/Settings/RESTClient"); @@ -209,6 +228,69 @@ void SaveClientKey(std::string& server_name, std::string key) { return; } + +SemanticVersion GetApiVersion(const std::string& dest_ip) { + std::string url(dest_ip); + url += "/api/get-version"; + + struct MemoryStruct chunk; + chunk.memory = (char*)malloc(1); + chunk.size = 0; + std::string buf; + std::ostringstream ostream(buf); + long response_code = PostSendObjectMessage(url, ostream, &chunk, true); + + if (response_code == 200) { + wxString body(chunk.memory); + wxJSONValue root; + wxJSONReader reader; + + int numErrors = reader.Parse(body, &root); + if (numErrors != 0) return SemanticVersion(-1, -1); + + wxString version = root["version"].AsString(); + return SemanticVersion::parse(version.ToStdString()); + } else { + return SemanticVersion(-1, -1); + } +} + + +RestServerResult CheckApiKey(const std::string& source, + const std::string& api_key, + const std::string& dest_ip) +{ + std::string url(dest_ip); + url += "/api/ping"; + url += std::string("?source=") + g_hostname; + url += std::string("&apikey=") + api_key; + + struct MemoryStruct chunk; + chunk.memory = (char*)malloc(1); + chunk.size = 0; + std::string buf; + std::ostringstream ostream(buf); + long response_code = PostSendObjectMessage(url, ostream, &chunk, true); + + if (response_code == 200) { + wxString body(chunk.memory); + wxJSONValue root; + wxJSONReader reader; + + int numErrors = reader.Parse(body, &root); + // Capture the result + int result = root["result"].AsInt(); + + if (result > 0) { + return RestServerResult::NewPinRequested; + } else { + return RestServerResult::Void; + } + } else { + return RestServerResult::Void; + } +} + int SendNavobjects(std::string dest_ip_address, std::string server_name, std::vector route, std::vector routepoint, @@ -237,7 +319,7 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, wxString body(chunk.memory); wxJSONValue root; wxJSONReader reader; - + int numErrors = reader.Parse(body, &root); // Capture the result int result = root["result"].AsInt(); @@ -259,12 +341,22 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, if (dlg.GetReturnCode() == ID_PCD_OK) { wxString PIN_tentative = dlg.GetText1Value().Trim().Trim(false); unsigned int dPIN = atoi(PIN_tentative.ToStdString().c_str()); - std::string new_api_key = PintoRandomKeyString(dPIN); - ; - - SaveClientKey(server_name, new_api_key); - } else + Pincode pincode(dPIN); + std::string api_key = pincode.Hash(); + SemanticVersion v = GetApiVersion(dest_ip_address); + RestServerResult result; + if (v.major >= 9) { + result = CheckApiKey(g_hostname.ToStdString(), api_key, + dest_ip_address); + } else { + api_key = pincode.CompatHash(); + result = CheckApiKey(g_hostname.ToStdString(), api_key, + dest_ip_address); + } + SaveClientKey(server_name, api_key); + } else { b_cancel = true; + } } else if (result == static_cast(RestServerResult::GenericError)) apikey_ok = true; } else From 856e70977df487225df554f37df3be5630de306a Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sun, 26 Nov 2023 04:42:09 +0100 Subject: [PATCH 20/63] rest server: Update comments, clang-format, clang-tidy diagnostics rest_server: Fix clang-tidy diagnostics rest server: clang-format, etc. rest_server: clean up, fix documentation --- include/rest_server.h | 212 +++++++++++++++++++++++------------------- src/rest_server.cpp | 128 ++++++++++++++----------- 2 files changed, 191 insertions(+), 149 deletions(-) diff --git a/include/rest_server.h b/include/rest_server.h index 565a59f10e..925a8d662a 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -1,5 +1,5 @@ - /*************************************************************************** +/*************************************************************************** * Copyright (C) 2022 David Register * * Copyright (C) 2022-2023 Alec Leamas * * * @@ -19,48 +19,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** - * \file - * - * Opencpn REST API - * - * Supports the following endpoints: - * - * GET /api/ping?api_key=&source= - * Basic ping check, verifies api_key i. e., the pairing. - * Parameters: - * See below - * Returns - * {"result": } - * - * POST /api/rx_object?api_key=&source=&force=1 - * Upload a GPX route, track or waypoints. Parameters: - * - source= Mandatory, origin ip address or hostname - * - force=<1> if present, the host object is unconditionally - * updated. If not, host may run a "OK to overwrite" dialog. - * - api_key= Mandatory, as obtained when pairing, see below. - * Body: - * xml-encoded GPX data for one or more route(s), track(s) and/or - * waypoint(s) - * Returns: - * {"result": } - * - * GET /api/writable?guid= - * Check if route or waypoint with given is writable. - * Returns - * {"result": } - * - * Authentication uses a pairing mechanism. When an unpaired device - * tries to connect, the API generates a random pincode which is - * sent to the connecting party where it is displayed to user. User - * must then input the pincode in the server-side GUI thus making - * sure she has physical access to the machine. - * - * Result codes are as defined in RestServerResult. - */ - -#ifndef _RESTSERVER_H -#define _RESTSERVER_H +#ifndef RESTSERVER_H_ +#define RESTSERVER_H_ #include #include @@ -75,13 +35,13 @@ namespace fs = std::experimental::filesystem; #else #include +#include namespace fs = std::filesystem; #endif #include #include -#include // for wxSemaphore, std::semaphore is c++20 - +#include // for wxSemaphore, std::semaphore is c++20 #include "pugixml.hpp" #include "pincode.h" @@ -112,30 +72,36 @@ struct RestIoEvtData { const std::string source; ///< Rest API parameter source const bool force; ///< rest API parameter force - const std::string payload; ///< GPX data for Object, Guid for CheckWrite + /** GPX data for Cmd::Object, Guid for Cmd::CheckWrite */ + const std::string payload; - /** Cmd::Object constructor. */ - RestIoEvtData(const std::string& key, const std::string& src, - const std::string& gpx_data, bool _force) - : RestIoEvtData(Cmd::Object, key, src, gpx_data, _force) {} + /** Create a Cmd::Object instance. */ + static RestIoEvtData CreateCmdData(const std::string& key, + const std::string& src, + const std::string& gpx_data, bool _force) { + return {Cmd::Object, key, src, gpx_data, _force}; + } - /** Cmd::Ping constructor. */ - RestIoEvtData(const std::string& key, const std::string& src) - : RestIoEvtData(Cmd::Ping, key, src, "", false) {} + /** Create a Cod::Ping instance: */ + static RestIoEvtData CreatePingData(const std::string& key, + const std::string& src) { + return {Cmd::Ping, key, src, "", false}; + } /** Cmd::CheckWrite constructor. */ - RestIoEvtData(const std::string& key, const std::string& src, - const std::string& guid) - : RestIoEvtData(Cmd::CheckWrite, key, src, guid, false) {} + static RestIoEvtData CreateChkWriteData(const std::string& key, + const std::string& src, + const std::string& guid) { + return {Cmd::CheckWrite, key, src, guid, false}; + } private: - RestIoEvtData(Cmd c, const std::string& key, const std::string& src, - const std::string& _payload, bool _force) - : cmd(c), api_key(key), source(src), force(_force), payload(_payload) {} + RestIoEvtData(Cmd c, std::string key, std::string src, std::string _payload, + bool _force); }; /** Return hash code for numeric pin value. */ -std::string PintoRandomKeyString(int dpin); +std::string PintoRandomKeyString(int pin); /** Abstract base class visible in callbacks. */ class PinDialog { @@ -148,12 +114,15 @@ class PinDialog { virtual void DeInit() = 0; }; -/** Returned status from RunAcceptObjectDlg. */ +/** Returned status from RunAcceptObjectDlg. */ struct AcceptObjectDlgResult { const int status; ///< return value from ShowModal() const bool check1_value; ///< As of GetCheck1Value() + /** default constructor, returns empty struct. */ AcceptObjectDlgResult() : status(0), check1_value(false) {} + + /** Create a struct with given values for status and check1_value. */ AcceptObjectDlgResult(int s, bool b) : status(s), check1_value(b) {} }; @@ -171,16 +140,7 @@ class RestServerDlgCtx { run_accept_object_dlg; std::function top_level_refresh; - RestServerDlgCtx() - : show_dialog([](const std::string&, const std::string&) -> PinDialog* { - return 0; - }), - close_dialog([](PinDialog*) {}), - update_route_mgr([]() {}), - run_accept_object_dlg([](const wxString&, const wxString&) { - return AcceptObjectDlgResult(); - }), - top_level_refresh([]() {}) {} + RestServerDlgCtx(); }; /** Callbacks for handling routes and tracks. */ @@ -190,49 +150,109 @@ class RouteCtx { std::function find_track_by_guid; std::function delete_route; std::function delete_track; - RouteCtx() - : find_route_by_guid([](wxString) { return static_cast(0); }), - find_track_by_guid([](wxString) { return static_cast(0); }), - delete_route([](Route*) -> void {}), - delete_track([](Track*) -> void {}) {} + RouteCtx(); +}; + +/** + * + * Opencpn REST API + * + * Supports the following endpoints: + * + * GET /api/ping?api_key=`` &source=`
+ * Basic ping check, verifies api_key i. e., the pairing. + * - Parameters: + * See below
+ * - Returns: + * {"result": ``} + * + * POST /api/rx_object?api_key=`` &source=``&force=1
+ * Upload a GPX route, track or waypoints. + * - Parameters: + * - source=`` Mandatory, origin ip address or hostname + * - force=`<1>` f present, the host object is unconditionally + * updated. If not, host may run a "OK to overwrite" dialog. + * - api_key=`` Mandatory, as obtained when pairing, see below. + * + * - Body: + * xml-encoded GPX data for one or more route(s), track(s) and/or + * waypoint(s)
+ * - Returns: + * {"result": ``} + * + * GET /api/writable?guid=<`guid>`
+ * Check if route or waypoint with given guid is writable.
+ * - Returns:: + * {"result": ``} + * + * Authentication uses a pairing mechanism. When an unpaired device + * tries to connect, the API generates a random pincode which is + * sent to the connecting party where it is displayed to user. User + * must then input the pincode in the server-side GUI thus making + * sure she has physical access to the machine. + * + * Result codes are as defined in RestServerResult. + */ +class AbstractRestServer { + /** Server public interface. */ + +public: + /** Start the server thread. */ + virtual bool StartServer(const fs::path& certificate_location) = 0; + + /** Stop server thread, blocks until completed. */ + virtual void StopServer() = 0; }; -/** Server public interface. */ -class RestServer : public wxEvtHandler { - friend class RestServerObjectApp; // Unit test - friend class RestCheckWriteApp; // Unit test - friend class RestServerPingApp; // Unit test +/** AbstractRestServer implementation and interface to underlying IO thread. */ +class RestServer : public AbstractRestServer, public wxEvtHandler { + friend class RestServerObjectApp; ///< Unit test hook + friend class RestCheckWriteApp; ///< Unit test hook + friend class RestServerPingApp; ///< Unit test hook public: RestServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable); - virtual ~RestServer(); + ~RestServer() override; + + /** Start the server thread. */ + bool StartServer(const fs::path& certificate_location) override; - bool StartServer(fs::path certificate_location); - void StopServer(); + /** Stop server thread, blocks until completed. */ + void StopServer() override; + /** IoThread interface.*/ void UpdateReturnStatus(RestServerResult r); + + /** IoThread interface. */ RestServerResult GetReturnStatus() { return return_status; } - void UpdateRouteMgr() { m_dlg_ctx.update_route_mgr(); } + /** IoThread interface. */ + void UpdateRouteMgr() const { m_dlg_ctx.update_route_mgr(); } + /** Semi-static storage used by IoThread C code. */ std::string m_cert_file; + + /** Semi-static storage used by IoThread C code. */ std::string m_key_file; - /** Guards return_status */ + /** IoThread interface: Guards return_status */ std::mutex ret_mutex; - /** Guards return_status */ + /** IoThread interface: Guards return_status */ std::condition_variable return_status_condition; - /** Binary exit synchronization, released when io thread exits. */ + /** + * IoThread interface: Binary exit synchronization, released when + * io thread exits. std::semaphore is C++20, hence wxSemaphore. + */ wxSemaphore m_exit_sem; private: class IoThread { public: - IoThread(RestServer& parent, const std::string& ip); - virtual ~IoThread(void) {} + IoThread(RestServer& parent, std::string ip); + virtual ~IoThread() = default; void Run(); bool IsRunning() { return run_flag > 0; } @@ -251,17 +271,17 @@ class RestServer : public wxEvtHandler { }; /** - * Stores the api key for different ip addresses. Methods for - * serialize/deserialize config file format. - */ - class Apikeys: public std::unordered_map { + * Stores the api key for different ip addresses. Methods for + * serialize/deserialize config file format. + */ + class Apikeys : public std::unordered_map { public: static Apikeys Parse(const std::string& s); std::string ToString() const; }; - bool LoadConfig(void); - bool SaveConfig(void); + bool LoadConfig(); + bool SaveConfig(); void HandleServerMessage(ObservedEvt& event); diff --git a/src/rest_server.cpp b/src/rest_server.cpp index f261bb5311..e514640927 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -1,5 +1,5 @@ - /************************************************************************** +/************************************************************************** * Copyright (C) 2022 David Register * * Copyright (C) 2022-2023 Alec Leamas * * * @@ -19,17 +19,16 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * **************************************************************************/ -/** \file Implement rest_server.h */ +/** \file rest_server.cpp Implement rest_server.h */ #include #include -#include +#include #include #include #include #include -#include #include "config_vars.h" #include "logger.h" @@ -42,7 +41,6 @@ /** Event from IO thread to main */ wxDEFINE_EVENT(REST_IO_EVT, ObservedEvt); - using namespace std::chrono_literals; static const char* const kHttpAddr = "http://0.0.0.0:8000"; @@ -51,9 +49,7 @@ static const char* const kHttpsAddr = "http://0.0.0.0:8443"; static const char* const kHttpPortableAddr = "http://0.0.0.0:8001"; static const char* const kHttpsPortableAddr = "http://0.0.0.0:8444"; -std::string PintoRandomKeyString(int dpin) { - return Pincode::IntToHash(dpin); -} +std::string PintoRandomKeyString(int pin) { return Pincode::IntToHash(pin); } /** Extract a HTTP variable from query string. */ static inline std::string HttpVarToString(const struct mg_str& query, @@ -65,7 +61,7 @@ static inline std::string HttpVarToString(const struct mg_str& query, } static void PostEvent(RestServer* parent, - std::shared_ptr evt_data, int id) { + const std::shared_ptr& evt_data, int id) { auto evt = new ObservedEvt(REST_IO_EVT, id); evt->SetSharedPtr(evt_data); parent->QueueEvent(evt); @@ -88,10 +84,11 @@ static void HandleRxObject(struct mg_connection* c, struct mg_http_message* hm, mg_http_delete_chunk(c, hm); parent->UpdateReturnStatus(RestServerResult::Void); - if (source.size()) { + if (!source.empty()) { assert(parent && "Null parent pointer"); - auto data_ptr = std::make_shared( - RestIoEvtData(api_key, source, xml_content, force.size())); + auto data_ptr = + std::make_shared(RestIoEvtData::CreateCmdData( + api_key, source, xml_content, !force.empty())); PostEvent(parent, data_ptr, MID); } if (MID == ORS_CHUNK_LAST) { @@ -109,11 +106,11 @@ static void HandlePing(struct mg_connection* c, struct mg_http_message* hm, RestServer* parent) { std::string api_key = HttpVarToString(hm->query, "apikey"); std::string source = HttpVarToString(hm->query, "source"); - if (source.size()) { + if (!source.empty()) { assert(parent && "Null parent pointer"); parent->UpdateReturnStatus(RestServerResult::Void); - auto data_ptr = - std::make_shared(RestIoEvtData(api_key, source)); + auto data_ptr = std::make_shared( + RestIoEvtData::CreatePingData(api_key, source)); PostEvent(parent, data_ptr, ORS_CHUNK_LAST); std::unique_lock lock{parent->ret_mutex}; bool r = parent->return_status_condition.wait_for(lock, 10s, [&] { @@ -129,11 +126,11 @@ static void HandleWritable(struct mg_connection* c, struct mg_http_message* hm, std::string apikey = HttpVarToString(hm->query, "apikey"); std::string source = HttpVarToString(hm->query, "source"); std::string guid = HttpVarToString(hm->query, "guid"); - if (source.size()) { + if (!source.empty()) { assert(parent && "Null parent pointer"); parent->UpdateReturnStatus(RestServerResult::Void); - auto data_ptr = - std::make_shared(RestIoEvtData(apikey, source, guid)); + auto data_ptr = std::make_shared( + RestIoEvtData::CreateChkWriteData(apikey, source, guid)); PostEvent(parent, data_ptr, ORS_CHUNK_LAST); std::unique_lock lock{parent->ret_mutex}; bool r = parent->return_status_condition.wait_for(lock, 10s, [&] { @@ -147,21 +144,21 @@ static void HandleWritable(struct mg_connection* c, struct mg_http_message* hm, // We use the same event handler function for HTTP and HTTPS connections // fn_data is NULL for plain HTTP, and non-NULL for HTTPS static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { - RestServer* parent = static_cast(fn_data); + auto parent = static_cast(fn_data); if (ev == MG_EV_ACCEPT /*&& fn_data != NULL*/) { - struct mg_tls_opts opts; - memset(&opts, 0, sizeof(mg_tls_opts)); + struct mg_tls_opts opts = {0}; + memset(&opts, 0, sizeof(mg_tls_opts)); // FIXME (leamas) - opts.ca = NULL; //"cert.pem"; // Uncomment to enable two-way SSL + opts.ca = nullptr; //"cert.pem"; // Uncomment to enable two-way SSL opts.cert = parent->m_cert_file.c_str(); // Certificate PEM file opts.certkey = parent->m_key_file.c_str(); // The key PEM file - opts.ciphers = NULL; + opts.ciphers = nullptr; mg_tls_init(c, &opts); } else if (ev == MG_EV_TLS_HS) { // Think of this as "start of session" PostEvent(parent, nullptr, ORS_START_OF_SESSION); } else if (ev == MG_EV_HTTP_CHUNK) { - struct mg_http_message* hm = (struct mg_http_message*)ev_data; + auto hm = (struct mg_http_message*)ev_data; if (mg_http_match_uri(hm, "/api/ping")) { HandlePing(c, hm, parent); } else if (mg_http_match_uri(hm, "/api/rx_object")) { @@ -172,16 +169,15 @@ static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { } } - //======================================================================== /* RestServer implementation */ -RestServer::IoThread::IoThread(RestServer& parent, const std::string& ip) - : m_parent(parent), m_server_ip(ip) {} +RestServer::IoThread::IoThread(RestServer& parent, std::string ip) + : run_flag(-1), m_parent(parent), m_server_ip(std::move(ip)) {} void RestServer::IoThread::Run() { run_flag = 1; - struct mg_mgr mgr; // Event manager + struct mg_mgr mgr = {0}; // Event manager mg_log_set(MG_LL_DEBUG); // Set log level mg_mgr_init(&mgr); // Initialise event manager @@ -215,13 +211,12 @@ RestServer::Apikeys RestServer::Apikeys::Parse(const std::string& s) { apikeys[words[0]] = words[1]; } } - return apikeys; + return apikeys; } std::string RestServer::Apikeys::ToString() const { std::stringstream ss; - for (const auto& it : *this) - ss << it.first << ":" << it.second << ";"; + for (const auto& it : *this) ss << it.first << ":" << it.second << ";"; return ss.str(); } @@ -235,9 +230,11 @@ void RestServer::UpdateReturnStatus(RestServerResult result) { RestServer::RestServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) : m_exit_sem(0, 1), - m_dlg_ctx(ctx), - m_route_ctx(route_ctx), - m_pin_dialog(0), + m_dlg_ctx(std::move(ctx)), + m_route_ctx(std::move(route_ctx)), + return_status(RestServerResult::Void), + m_pin_dialog(nullptr), + m_overwrite(false), m_io_thread(*this, portable ? kHttpsPortableAddr : kHttpsAddr), m_pincode(Pincode::Create()) { // Prepare the wxEventHandler to accept events from the io thread @@ -248,7 +245,7 @@ RestServer::~RestServer() { Unbind(REST_IO_EVT, &RestServer::HandleServerMessage, this); } -bool RestServer::StartServer(fs::path certificate_location) { +bool RestServer::StartServer(const fs::path& certificate_location) { m_certificate_directory = certificate_location.string(); m_cert_file = (certificate_location / "cert.pem").string(); m_key_file = (certificate_location / "key.pem").string(); @@ -272,16 +269,16 @@ void RestServer::StopServer() { } } -bool RestServer::LoadConfig(void) { +bool RestServer::LoadConfig() { TheBaseConfig()->SetPath("/Settings/RestServer"); wxString key_string; TheBaseConfig()->Read("ServerKeys", &key_string); m_key_map = Apikeys::Parse(key_string.ToStdString()); - TheBaseConfig()->Read("ServerOverwriteDuplicates", &m_overwrite, 0); + TheBaseConfig()->Read("ServerOverwriteDuplicates", &m_overwrite, false); return true; } -bool RestServer::SaveConfig(void) { +bool RestServer::SaveConfig() { TheBaseConfig()->SetPath("/Settings/RestServer"); TheBaseConfig()->Write("ServerKeys", wxString(m_key_map.ToString())); TheBaseConfig()->Write("ServerOverwriteDuplicates", m_overwrite); @@ -292,7 +289,7 @@ bool RestServer::SaveConfig(void) { bool RestServer::CheckApiKey(const RestIoEvtData& evt_data) { // Look up the api key in the hash map. If found, we are done. if (m_key_map.find(evt_data.source) != m_key_map.end()) { - if (m_key_map[evt_data.source] == evt_data.api_key) return true; + if (m_key_map[evt_data.source] == evt_data.api_key) return true; } // Need a new PIN confirmation, add it to map and persist m_pincode = Pincode::Create(); @@ -302,8 +299,8 @@ bool RestServer::CheckApiKey(const RestIoEvtData& evt_data) { std::stringstream ss; ss << evt_data.source << " " << _("wants to send you new data.") << "\n" - << _("Please enter the following PIN number on ") << evt_data.source - << " " << _("to pair with this device") << "\n"; + << _("Please enter the following PIN number on ") << evt_data.source << " " + << _("to pair with this device") << "\n"; m_pin_dialog = m_dlg_ctx.show_dialog(ss.str(), m_pincode.ToString()); return false; @@ -326,8 +323,8 @@ void RestServer::HandleServerMessage(ObservedEvt& event) { auto evt_data = UnpackEvtPointer(event); if (event.GetId() == ORS_CHUNK_N) { // Stream out to temp file - if (m_upload_path.size() && m_ul_stream.is_open()) { - m_ul_stream.write(evt_data->payload.c_str(), evt_data->payload.size()); + if (!m_upload_path.empty() && m_ul_stream.is_open()) { + m_ul_stream.write(evt_data->payload.c_str(), !evt_data->payload.empty()); } return; } @@ -335,7 +332,7 @@ void RestServer::HandleServerMessage(ObservedEvt& event) { if (event.GetId() == ORS_CHUNK_LAST) { // Cancel existing dialog and close temp file m_dlg_ctx.close_dialog(m_pin_dialog); - if (m_upload_path.size() && m_ul_stream.is_open()) m_ul_stream.close(); + if (!m_upload_path.empty() && m_ul_stream.is_open()) m_ul_stream.close(); // Io thread might be waiting for return_status on notify_one() UpdateReturnStatus(RestServerResult::GenericError); @@ -380,8 +377,7 @@ void RestServer::HandleServerMessage(ObservedEvt& event) { void RestServer::HandleRoute(pugi::xml_node object, const RestIoEvtData& evt_data) { - Route* route = NULL; - route = GPXLoadRoute1(object, true, false, false, 0, true); + Route* route = GPXLoadRoute1(object, true, false, false, 0, true); // Check for duplicate GUID bool add = true; bool overwrite_one = false; @@ -419,13 +415,12 @@ void RestServer::HandleRoute(pugi::xml_node object, void RestServer::HandleTrack(pugi::xml_node object, const RestIoEvtData& evt_data) { - Track* route = NULL; - route = GPXLoadTrack1(object, true, false, false, 0); + Track* track = GPXLoadTrack1(object, true, false, false, 0); // Check for duplicate GUID bool add = true; bool overwrite_one = false; - Track* duplicate = m_route_ctx.find_track_by_guid(route->m_GUID); + Track* duplicate = m_route_ctx.find_track_by_guid(track->m_GUID); if (duplicate) { if (!m_overwrite && !evt_data.force) { auto result = m_dlg_ctx.run_accept_object_dlg( @@ -446,10 +441,10 @@ void RestServer::HandleTrack(pugi::xml_node object, } } if (add) { - // Add the route to the global list + // Add the track to the global list NavObjectCollection1 pSet; - if (InsertTrack(route, false)) + if (InsertTrack(track, false)) UpdateReturnStatus(RestServerResult::NoError); else UpdateReturnStatus(RestServerResult::RouteInsertError); @@ -459,8 +454,8 @@ void RestServer::HandleTrack(pugi::xml_node object, void RestServer::HandleWaypoint(pugi::xml_node object, const RestIoEvtData& evt_data) { - RoutePoint* rp = NULL; - rp = GPXLoadWaypoint1(object, "circle", "", false, false, false, 0); + RoutePoint* rp = + GPXLoadWaypoint1(object, "circle", "", false, false, false, 0); rp->m_bIsolatedMark = true; // This is an isolated mark // Check for duplicate GUID bool add = true; @@ -490,3 +485,30 @@ void RestServer::HandleWaypoint(pugi::xml_node object, m_dlg_ctx.top_level_refresh(); } } + +RestIoEvtData::RestIoEvtData(RestIoEvtData::Cmd c, std::string key, + std::string src, std::string _payload, bool _force) + : cmd(c), + api_key(std::move(key)), + source(std::move(src)), + force(_force), + payload(std::move(_payload)) {} + +RestServerDlgCtx::RestServerDlgCtx() + : show_dialog([](const std::string&, const std::string&) -> PinDialog* { + return nullptr; + }), + close_dialog([](PinDialog*) {}), + update_route_mgr([]() {}), + run_accept_object_dlg([](const wxString&, const wxString&) { + return AcceptObjectDlgResult(); + }), + top_level_refresh([]() {}) {} + +RouteCtx::RouteCtx() + : find_route_by_guid( + [](const wxString&) { return static_cast(nullptr); }), + find_track_by_guid( + [](const wxString&) { return static_cast(nullptr); }), + delete_route([](Route*) -> void {}), + delete_track([](Track*) -> void {}) {} \ No newline at end of file From e3d82295555f6608890c27eb4e2ae7c06380c3ef Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Mon, 27 Nov 2023 08:26:59 +0100 Subject: [PATCH 21/63] rest_server: Handle bad urls. --- src/rest_server.cpp | 7 +++++-- test/rest-tests.cpp | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/rest_server.cpp b/src/rest_server.cpp index e514640927..524c773ab6 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -165,7 +165,10 @@ static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { HandleRxObject(c, hm, parent); } else if (mg_http_match_uri(hm, "/api/writable")) { HandleWritable(c, hm, parent); + } else { + mg_http_reply(c, 404, "", "url: not found"); } + } } @@ -324,7 +327,7 @@ void RestServer::HandleServerMessage(ObservedEvt& event) { if (event.GetId() == ORS_CHUNK_N) { // Stream out to temp file if (!m_upload_path.empty() && m_ul_stream.is_open()) { - m_ul_stream.write(evt_data->payload.c_str(), !evt_data->payload.empty()); + m_ul_stream.write(evt_data->payload.c_str(), evt_data->payload.size()); } return; } @@ -511,4 +514,4 @@ RouteCtx::RouteCtx() find_track_by_guid( [](const wxString&) { return static_cast(nullptr); }), delete_route([](Route*) -> void {}), - delete_track([](Track*) -> void {}) {} \ No newline at end of file + delete_track([](Track*) -> void {}) {} diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index 3cce397f73..e5759ccd84 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -120,7 +120,7 @@ class RestServer404App : public RestServerApp { auto path = fs::path(CMAKE_BINARY_DIR) / "curl-result"; std::stringstream ss; - ss << CURLPROG << " --insecure --max-time 10 -I -o " << path + ss << CURLPROG << " --insecure --max-time 3 -I -o " << path << " https://localhost:8443/api/pong"; system(ss.str().c_str()); std::this_thread::sleep_for(50ms); From 976ddc2762bb286021ef072b03802d22fe2680f0 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Mon, 27 Nov 2023 10:04:11 +0100 Subject: [PATCH 22/63] rest server: add new /api/get-version endpoint --- include/rest_server.h | 5 +++++ src/rest_server.cpp | 9 ++++++++- test/rest-tests.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/include/rest_server.h b/include/rest_server.h index 925a8d662a..a7d5e8bb8a 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -185,6 +185,11 @@ class RouteCtx { * - Returns:: * {"result": ``} * + * GET /api/get-version
+ * Return current server version string. Does not require api_key or source. + * - Returns (example:: + * {"version": "5.8.9" } + * * Authentication uses a pairing mechanism. When an unpaired device * tries to connect, the API generates a random pincode which is * sent to the connecting party where it is displayed to user. User diff --git a/src/rest_server.cpp b/src/rest_server.cpp index 524c773ab6..3271061c11 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -48,6 +48,9 @@ static const char* const kHttpsAddr = "http://0.0.0.0:8443"; static const char* const kHttpPortableAddr = "http://0.0.0.0:8001"; static const char* const kHttpsPortableAddr = "http://0.0.0.0:8444"; +static const char* const kVersionReply = R"""( +{ "version": "@version@" } +)"""; std::string PintoRandomKeyString(int pin) { return Pincode::IntToHash(pin); } @@ -150,7 +153,7 @@ static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { struct mg_tls_opts opts = {0}; memset(&opts, 0, sizeof(mg_tls_opts)); // FIXME (leamas) - opts.ca = nullptr; //"cert.pem"; // Uncomment to enable two-way SSL + opts.ca = nullptr; // "cert.pem"; // Uncomment to enable two-way SSL opts.cert = parent->m_cert_file.c_str(); // Certificate PEM file opts.certkey = parent->m_key_file.c_str(); // The key PEM file opts.ciphers = nullptr; @@ -165,6 +168,10 @@ static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { HandleRxObject(c, hm, parent); } else if (mg_http_match_uri(hm, "/api/writable")) { HandleWritable(c, hm, parent); + } else if (mg_http_match_uri(hm, "/api/get-version")) { + std::string reply(kVersionReply); + ocpn::replace(reply, "@version@", PACKAGE_VERSION); + mg_http_reply(c, 200, "", reply.c_str()); } else { mg_http_reply(c, 404, "", "url: not found"); } diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index e5759ccd84..8339c2ae62 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -6,9 +6,10 @@ #include #include -#include #include +#include #include +#include #include #include @@ -133,6 +134,35 @@ class RestServer404App : public RestServerApp { } }; +class RestServerVersionApp : public RestServerApp { +public: + RestServerVersionApp(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) + : RestServerApp(ctx, route_ctx, portable) {} + +protected: + void Work() { + auto path = fs::path(CMAKE_BINARY_DIR) / "curl-result"; + + { + std::stringstream ss; + ss << CURLPROG << " --insecure --max-time 3 -o " << path + << " https://localhost:8443/api/get-version"; + system(ss.str().c_str()); + } + std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); + std::ifstream f(path.string()); + std::stringstream ss; + ss << f.rdbuf(); + wxJSONValue root; + wxJSONReader reader; + std::string reply = ss.str(); + int errors = reader.Parse(reply, &root); + EXPECT_EQ(errors, 0); + wxString version = root["version"].AsString(); + EXPECT_EQ(version, PACKAGE_VERSION); + } +}; class RestServerObjectApp : public RestServerApp { public: @@ -314,6 +344,17 @@ TEST(RestServer, Pong) { app.Run(); }; +TEST(RestServer, Version) { + wxInitializer initializer; + ConfigSetup(); + RestServerDlgCtx dialog_ctx; + RouteCtx route_ctx; + RestServerVersionApp app(dialog_ctx, route_ctx, g_portable); + app.Run(); +}; + + + TEST(RestServer, Object) { wxInitializer initializer; From d0a95654937005ffd41193c23b3fda13feefb1b2 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 30 Nov 2023 11:02:10 +0100 Subject: [PATCH 23/63] rest-server: clean up --- include/rest_server.h | 63 +++++---------------------------------- include/rest_server_gui.h | 4 +-- src/rest_server.cpp | 47 +++++++++++++++++++++++++---- src/rest_server_gui.cpp | 15 ++-------- 4 files changed, 54 insertions(+), 75 deletions(-) diff --git a/include/rest_server.h b/include/rest_server.h index a7d5e8bb8a..fe3ffe364d 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -59,60 +59,11 @@ enum class RestServerResult { Void }; -/** Kind of messages sent from io thread to main code. */ -enum { ORS_START_OF_SESSION, ORS_CHUNK_N, ORS_CHUNK_LAST }; - /** Dialog return codes. */ enum { ID_STG_CANCEL = 10000, ID_STG_OK, ID_STG_CHECK1, ID_STG_CHOICE_COMM }; /** Data from IO thread to main */ -struct RestIoEvtData { - const enum class Cmd { Ping, Object, CheckWrite } cmd; - const std::string api_key; ///< Rest API parameter apikey - const std::string source; ///< Rest API parameter source - const bool force; ///< rest API parameter force - - /** GPX data for Cmd::Object, Guid for Cmd::CheckWrite */ - const std::string payload; - - /** Create a Cmd::Object instance. */ - static RestIoEvtData CreateCmdData(const std::string& key, - const std::string& src, - const std::string& gpx_data, bool _force) { - return {Cmd::Object, key, src, gpx_data, _force}; - } - - /** Create a Cod::Ping instance: */ - static RestIoEvtData CreatePingData(const std::string& key, - const std::string& src) { - return {Cmd::Ping, key, src, "", false}; - } - - /** Cmd::CheckWrite constructor. */ - static RestIoEvtData CreateChkWriteData(const std::string& key, - const std::string& src, - const std::string& guid) { - return {Cmd::CheckWrite, key, src, guid, false}; - } - -private: - RestIoEvtData(Cmd c, std::string key, std::string src, std::string _payload, - bool _force); -}; - -/** Return hash code for numeric pin value. */ -std::string PintoRandomKeyString(int pin); - -/** Abstract base class visible in callbacks. */ -class PinDialog { -public: - /** Create and show the dialog */ - virtual PinDialog* Initiate(const std::string& msg, - const std::string& text1) = 0; - - /** Close and destroy */ - virtual void DeInit() = 0; -}; +struct RestIoEvtData; /** Returned status from RunAcceptObjectDlg. */ struct AcceptObjectDlgResult { @@ -126,12 +77,14 @@ struct AcceptObjectDlgResult { AcceptObjectDlgResult(int s, bool b) : status(s), check1_value(b) {} }; -/** Callbacks invoked from PinDialog implementations. */ +/** Callbacks for handling dialogs and RouteManager updates */ class RestServerDlgCtx { public: - std::function + /** Run the "Server wants a pincode" dialog. */ + std::function show_dialog; - std::function close_dialog; + + /** Update Route manager after updates to underlying nav_object_database. */ std::function update_route_mgr; /** Run the "Accept Object" dialog, returns value from ShowModal(). */ @@ -140,6 +93,7 @@ class RestServerDlgCtx { run_accept_object_dlg; std::function top_level_refresh; + /** All dummy stubs constructor. */ RestServerDlgCtx(); }; @@ -199,7 +153,6 @@ class RouteCtx { * Result codes are as defined in RestServerResult. */ class AbstractRestServer { - /** Server public interface. */ public: /** Start the server thread. */ @@ -303,7 +256,7 @@ class RestServer : public AbstractRestServer, public wxEvtHandler { std::string m_certificate_directory; Apikeys m_key_map; - PinDialog* m_pin_dialog; + wxDialog* m_pin_dialog; bool m_overwrite; std::string m_upload_path; diff --git a/include/rest_server_gui.h b/include/rest_server_gui.h index 02cfc2a201..416b135a2b 100644 --- a/include/rest_server_gui.h +++ b/include/rest_server_gui.h @@ -88,7 +88,7 @@ class AcceptObjectDialog : public wxDialog { }; -class PINCreateDialog : public PinDialog, public wxDialog { +class PINCreateDialog : public wxDialog { DECLARE_DYNAMIC_CLASS(PINCreateDialog) DECLARE_EVENT_TABLE() @@ -102,7 +102,7 @@ class PINCreateDialog : public PinDialog, public wxDialog { static RestServerDlgCtx GetDlgCtx(); - PinDialog* Initiate(const std::string& msg, const std::string& text1); + wxDialog* Initiate(const std::string& msg, const std::string& text1); void DeInit(); bool Create(wxWindow* parent, wxWindowID id = SYMBOL_STG_IDNAME, diff --git a/src/rest_server.cpp b/src/rest_server.cpp index 3271061c11..5d310a12fb 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -52,6 +52,43 @@ static const char* const kVersionReply = R"""( { "version": "@version@" } )"""; +/** Kind of messages sent from io thread to main code. */ +enum { ORS_START_OF_SESSION, ORS_CHUNK_N, ORS_CHUNK_LAST }; + +struct RestIoEvtData { + const enum class Cmd { Ping, Object, CheckWrite } cmd; + const std::string api_key; ///< Rest API parameter apikey + const std::string source; ///< Rest API parameter source + const bool force; ///< rest API parameter force + + /** GPX data for Cmd::Object, Guid for Cmd::CheckWrite */ + const std::string payload; + + /** Create a Cmd::Object instance. */ + static RestIoEvtData CreateCmdData(const std::string& key, + const std::string& src, + const std::string& gpx_data, bool _force) { + return {Cmd::Object, key, src, gpx_data, _force}; + } + + /** Create a Cod::Ping instance: */ + static RestIoEvtData CreatePingData(const std::string& key, + const std::string& src) { + return {Cmd::Ping, key, src, "", false}; + } + + /** Cmd::CheckWrite constructor. */ + static RestIoEvtData CreateChkWriteData(const std::string& key, + const std::string& src, + const std::string& guid) { + return {Cmd::CheckWrite, key, src, guid, false}; + } + +private: + RestIoEvtData(Cmd c, std::string key, std::string src, std::string _payload, + bool _force); +}; + std::string PintoRandomKeyString(int pin) { return Pincode::IntToHash(pin); } /** Extract a HTTP variable from query string. */ @@ -251,9 +288,7 @@ RestServer::RestServer(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) Bind(REST_IO_EVT, &RestServer::HandleServerMessage, this); } -RestServer::~RestServer() { - Unbind(REST_IO_EVT, &RestServer::HandleServerMessage, this); -} +RestServer::~RestServer() { StopServer(); } bool RestServer::StartServer(const fs::path& certificate_location) { m_certificate_directory = certificate_location.string(); @@ -341,7 +376,8 @@ void RestServer::HandleServerMessage(ObservedEvt& event) { if (event.GetId() == ORS_CHUNK_LAST) { // Cancel existing dialog and close temp file - m_dlg_ctx.close_dialog(m_pin_dialog); + wxEvent* event = new wxCloseEvent; + wxQueueEvent(m_pin_dialog, event); if (!m_upload_path.empty() && m_ul_stream.is_open()) m_ul_stream.close(); // Io thread might be waiting for return_status on notify_one() @@ -505,10 +541,9 @@ RestIoEvtData::RestIoEvtData(RestIoEvtData::Cmd c, std::string key, payload(std::move(_payload)) {} RestServerDlgCtx::RestServerDlgCtx() - : show_dialog([](const std::string&, const std::string&) -> PinDialog* { + : show_dialog([](const std::string&, const std::string&) -> wxDialog* { return nullptr; }), - close_dialog([](PinDialog*) {}), update_route_mgr([]() {}), run_accept_object_dlg([](const wxString&, const wxString&) { return AcceptObjectDlgResult(); diff --git a/src/rest_server_gui.cpp b/src/rest_server_gui.cpp index 543905c92f..3794976607 100644 --- a/src/rest_server_gui.cpp +++ b/src/rest_server_gui.cpp @@ -46,7 +46,7 @@ extern RouteManagerDialog *pRouteManagerDialog; extern MyFrame* gFrame; -static PinDialog* DisplayDlg(const std::string& msg, const std::string& txt1) { +static wxDialog* DisplayDlg(const std::string& msg, const std::string& txt1) { auto dlg = new PINCreateDialog(dynamic_cast(gFrame), wxID_ANY, _("OpenCPN Server Message"), "", wxDefaultPosition, wxDefaultSize, @@ -57,14 +57,6 @@ static PinDialog* DisplayDlg(const std::string& msg, const std::string& txt1) { return dlg; } -static void CloseDlg(PinDialog* pin_dlg) { - if (pin_dlg) { - auto dlg = dynamic_cast(pin_dlg); - dlg->Close(); - dlg->Destroy(); - } -} - static void UpdateRouteMgr() { if( pRouteManagerDialog && pRouteManagerDialog->IsShown() ) { pRouteManagerDialog->UpdateTrkListCtrl(); @@ -88,7 +80,6 @@ RestServerDlgCtx PINCreateDialog::GetDlgCtx() { ctx.show_dialog = [](const std::string& msg, const std::string& text1) { return DisplayDlg(msg, text1); }; - ctx.close_dialog = [](PinDialog* pin_dlg) { CloseDlg(pin_dlg); }; ctx.update_route_mgr = []() { UpdateRouteMgr(); }; ctx.run_accept_object_dlg = [](const wxString& msg, const wxString& check1msg) { @@ -249,8 +240,8 @@ PINCreateDialog::~PINCreateDialog() { } -PinDialog* PINCreateDialog::Initiate(const std::string& msg, - const std::string& text1) { +wxDialog* PINCreateDialog::Initiate(const std::string& msg, + const std::string& text1) { auto dlg = new PINCreateDialog(dynamic_cast(gFrame), wxID_ANY, _("OpenCPN Server Message"), "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE ); From df2afbb945d9bf130f56c62c99767436e7a1ef48 Mon Sep 17 00:00:00 2001 From: ALec Leamas Date: Wed, 29 Nov 2023 17:37:51 +0100 Subject: [PATCH 24/63] test: rest-tests: Platform fixes, add "win-path-setup" target On windows, tests needs access to the runtime libraries, wxWidgets and others. This is problematic (as usual, sigh). Create a batch file which adds paths to wxWidgets and other stuff in the cache/ directory. After running the batch file tests should run. tests: more windows fixes rest-tests: clean up tests: enable some flatpak rest tests: tests: CMakeLists: Fix curl deps. tests: flatpak fixes tests: new windows fixes --- test/CMakeLists.txt | 48 +++++++++---- test/rest-tests.cpp | 162 ++++++++++++++++++++++++++------------------ test/tests.cpp | 1 + 3 files changed, 131 insertions(+), 80 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1b1eed8e82..ebc372827a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,31 +19,33 @@ set(SRC ${CMAKE_SOURCE_DIR}/src/mDNS_query.cpp ) -if (CURL) - list(APPEND SRC rest-tests.cpp) -endif () - if (LINUX) list(APPEND SRC n2k_tests.cpp) endif () +if (MSVC) + set(CURL_HINTS C:\\mingw64\\bin C:\\ProgramData\\chocolatey\\bin) +endif () +find_program(CURL NAMES curl HINTS ${CURL_HINTS}) + +if (CURL) + list(APPEND SRC rest-tests.cpp) +endif () add_executable(tests ${SRC}) -target_compile_options(tests PUBLIC "-O0") +if (CURL) + target_compile_definitions(tests PUBLIC CURLPROG="${CURL}") +endif () + +if (NOT MSVC) + target_compile_options(tests PUBLIC "-O0") +endif () target_compile_definitions(tests PUBLIC CLIAPP USE_MOCK_DEFS CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}" TESTDATA="${CMAKE_CURRENT_LIST_DIR}/testdata" ) -if (MSVC) - set(CURL_HINTS C:\\mingw64\\bin C:\\ProgramData\\chocolatey\\bin) - find_program(CURL NAMES curl HINTS ${CURL_HINTS}) -endif () -if (CURL) - target_compile_definitions(tests PUBLIC CURLPROG="${CURL}") -endif () - if (MSVC) target_link_libraries(tests PRIVATE setupapi.lib psapi.lib ${CMAKE_SOURCE_DIR}/cache/buildwin/iphlpapi.lib @@ -157,3 +159,23 @@ gtest_discover_tests(tests) add_test(NAME tests COMMAND tests) + +# Create a batch file which can be used to add paths to downloaded +# stuff so the tests runs on windows. Assumes things in top_dir\cache +# This is not that intelligent and needs manual updates when wxWidgets +# version is updated +if (MSVC) + set(_cache_dir ${CMAKE_SOURCE_DIR}\\\\cache) + set(_script_path ${CMAKE_BINARY_DIR}\\set_test_path.cmake) + set(_bat_path ${CMAKE_BINARY_DIR}\\\\set_test_path.bat) +set(_script +"file(WRITE ${_bat_path} \" +PATH=%PATH%\;${_cache_dir}\\\\buildwin +PATH=%PATH%\;${_cache_dir}\\\\wxWidgets-3.2.3\\\\lib\\\\vc14x_dll\") +") + file(WRITE ${_script_path} ${_script}) + add_custom_target(win-path-setup + COMMAND cmake -P "${_script_path}" + COMMAND cmake -E echo "Creating ${_bat_path}" + ) +endif () diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index 8339c2ae62..c50e50a3d5 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -24,14 +24,6 @@ #include "rest_server.h" #include "routeman.h" -#if defined(__GNUC__) && __GNUC__ == 7 -#include -namespace fs = std::experimental::filesystem; -#else -#include -namespace fs = std::filesystem; -#endif - using namespace std::chrono_literals; extern WayPointman* pWayPointMan; @@ -75,19 +67,32 @@ class RestServerApp : public wxAppConsole { RestServer m_rest_server; }; + +static std::string CmdString(const std::string s) { +#ifdef _MSC_VER + return std::string("\"") + s + "\""; +#else + return s; +#endif +} + + class RestServerPingApp : public RestServerApp { public: RestServerPingApp(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) : RestServerApp(ctx, route_ctx, portable) {} -protected: +protected: void Work() { auto path = fs::path(CMAKE_BINARY_DIR) / "curl-result"; { + std::this_thread::sleep_for(50ms); + + fs::path curl_prog(CURLPROG); std::stringstream ss; - ss << CURLPROG << " --insecure -o " << path - << " https://localhost:8443/api/ping?source=1.2.3.4&apikey=bad"; - system(ss.str().c_str()); + ss << curl_prog.make_preferred() << " --insecure -o " << path + << " \"https://localhost:8443/api/ping?source=1.2.3.4&apikey=bad\""; + system(CmdString(ss.str()).c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); std::ifstream f(path.string()); @@ -95,12 +100,13 @@ class RestServerPingApp : public RestServerApp { std::getline(f, result); EXPECT_EQ(result, "{\"result\": 5}"); // Bad api key }{ + fs::path curl_prog(CURLPROG); std::stringstream ss; auto key = m_rest_server.m_key_map["1.2.3.4"]; - ss << CURLPROG << " --insecure -o " << path - << " \"https://localhost:8443/api/ping?source=1.2.3.4&apikey=" - << key << "\""; - system(ss.str().c_str()); + ss << curl_prog.make_preferred() << " --insecure -o " << path + << " \"https://localhost:8443/api/ping?source=1.2.3.4&apikey=" << key + << "\""; + system(CmdString(ss.str()).c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); std::ifstream f(path.string()); @@ -116,15 +122,17 @@ class RestServer404App : public RestServerApp { RestServer404App(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) : RestServerApp(ctx, route_ctx, portable) {} -protected: +protected: void Work() { auto path = fs::path(CMAKE_BINARY_DIR) / "curl-result"; - + std::this_thread::sleep_for(50ms); + fs::path curl_prog(CURLPROG); std::stringstream ss; - ss << CURLPROG << " --insecure --max-time 3 -I -o " << path - << " https://localhost:8443/api/pong"; - system(ss.str().c_str()); + ss << curl_prog.make_preferred() << " --insecure --max-time 3 -I -o " + << path << " \"https://localhost:8443/api/pong\""; + system(CmdString(ss.str()).c_str()); std::this_thread::sleep_for(50ms); + ProcessPendingEvents(); std::ifstream f(path.string()); std::string result; @@ -139,15 +147,16 @@ class RestServerVersionApp : public RestServerApp { RestServerVersionApp(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) : RestServerApp(ctx, route_ctx, portable) {} -protected: +protected: void Work() { auto path = fs::path(CMAKE_BINARY_DIR) / "curl-result"; - + { + fs::path curl_prog(CURLPROG); std::stringstream ss; - ss << CURLPROG << " --insecure --max-time 3 -o " << path - << " https://localhost:8443/api/get-version"; - system(ss.str().c_str()); + ss << curl_prog.make_preferred() << " --insecure --max-time 3 -o " + << path << " \"https://localhost:8443/api/get-version\""; + system(CmdString(ss.str()).c_str()); } std::this_thread::sleep_for(50ms); ProcessPendingEvents(); @@ -169,32 +178,40 @@ class RestServerObjectApp : public RestServerApp { RestServerObjectApp(RestServerDlgCtx ctx, RouteCtx route_ctx, bool& portable) : RestServerApp(ctx, route_ctx, portable) {} -protected: +protected: void Work() override { + auto colour_func = [] (wxString c) { return *wxBLACK; }; + pWayPointMan = new WayPointman(colour_func); + pRouteList = new RouteList; + g_BasePlatform = new BasePlatform(); + pSelect = new Select(); + auto outpath = fs::path(CMAKE_BINARY_DIR) / "curl-result"; auto datapath = fs::path(TESTDATA) / "foo.gpx"; { // try to transfer route without api key + fs::path curl_prog(CURLPROG); std::stringstream ss; - ss << CURLPROG << " --insecure --silent --data @" << datapath - << " -o " << outpath << " -H \"Content-Type: text/xml\"" - << " https://localhost:8443/api/rx_object?source=1.2.3.4"; + ss << curl_prog.make_preferred() << " --insecure --silent --data @" + << datapath << " -o " << outpath << " -H \"Content-Type: text/xml\"" + << " \"https://localhost:8443/api/rx_object?source=1.2.3.4\""; + system(CmdString(ss.str()).c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); - system(ss.str().c_str()); std::ifstream f(outpath.string()); std::string result; std::getline(f, result); EXPECT_EQ(result, "{\"result\": 5}"); // New pin required } { // Try to transfer using api key set up above. + fs::path curl_prog(CURLPROG); std::stringstream ss; auto key = m_rest_server.m_key_map["1.2.3.4"]; - ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " - << outpath << " -H \"Content-Type: text/xml\"" + ss << curl_prog.make_preferred() << " --insecure --silent --data @" + << datapath << " -o " << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/rx_object?source=1.2.3.4" << "&apikey=" << key << "\""; - system(ss.str().c_str()); + system(CmdString(ss.str()).c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); std::ifstream f(outpath.string()); @@ -214,13 +231,14 @@ class RestServerObjectApp : public RestServerApp { return AcceptObjectDlgResult(ID_STG_CANCEL, true); }; // Try to transfer same object + fs::path curl_prog(CURLPROG); std::stringstream ss; auto key = m_rest_server.m_key_map["1.2.3.4"]; - ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " - << outpath << " -H \"Content-Type: text/xml\"" + ss << curl_prog.make_preferred() << " --insecure --silent --data @" + << datapath << " -o " << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/rx_object?source=1.2.3.4" << "&apikey=" << key << "\""; - system(ss.str().c_str()); + system(CmdString(ss.str()).c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); std::ifstream f(outpath.string()); @@ -229,14 +247,14 @@ class RestServerObjectApp : public RestServerApp { EXPECT_EQ(result, "{\"result\": 3}"); // Duplicate rejected } { // Try to transfer same object using argument force + fs::path curl_prog(CURLPROG); std::stringstream ss; auto key = m_rest_server.m_key_map["1.2.3.4"]; - ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " - << outpath << " -H \"Content-Type: text/xml\"" + ss << curl_prog.make_preferred() << " --insecure --silent --data @" + << datapath << " -o " << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/rx_object?source=1.2.3.4" << "&force=1&apikey=" << key << "\""; - system(ss.str().c_str()); - std::this_thread::sleep_for(50ms); + system(CmdString(ss.str()).c_str()); ProcessPendingEvents(); std::ifstream f(outpath.string()); std::string result; @@ -254,20 +272,25 @@ class RestCheckWriteApp : public RestServerApp { protected: void Work() override { + auto colour_func = [] (wxString c) { return *wxBLACK; }; + pWayPointMan = new WayPointman(colour_func); + pRouteList = new RouteList; + g_BasePlatform = new BasePlatform(); + pSelect = new Select(); + fs::path curl_prog(CURLPROG); auto datapath = fs::path(TESTDATA) / "foo.gpx"; auto outpath = fs::path(CMAKE_BINARY_DIR) / "curl-result"; { std::stringstream ss; - ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " - << outpath << " -H \"Content-Type: text/xml\"" + ss << curl_prog.make_preferred() << " --insecure --silent --data @" + << datapath << " -o " << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/writable?source=1.2.3.4" << "&apikey=" << "foobar" << "&guid=6a76a7e6-dc39-4a7d-964e-1eff3462c06c\""; // Try check our standard object, bad api key - std::cout << "Running command; " << ss.str() << "\n"; - system(ss.str().c_str()); + system(CmdString(ss.str()).c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); std::ifstream f(outpath.string()); @@ -278,12 +301,12 @@ class RestCheckWriteApp : public RestServerApp { // Try check our standard object, fix the api key auto key = m_rest_server.m_key_map["1.2.3.4"]; std::stringstream ss; - ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " - << outpath << " -H \"Content-Type: text/xml\"" + ss << curl_prog.make_preferred() << " --insecure --silent --data @" + << datapath << " -o " << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/writable?source=1.2.3.4" << "&apikey=" << key << "&guid=6a76a7e6-dc39-4a7d-964e-1eff3462c06c\""; - system(ss.str().c_str()); + system(CmdString(ss.str()).c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); std::ifstream f(outpath.string()); @@ -299,12 +322,12 @@ class RestCheckWriteApp : public RestServerApp { }; auto key = m_rest_server.m_key_map["1.2.3.4"]; std::stringstream ss; - ss << CURLPROG << " --insecure --silent --data @" << datapath << " -o " - << outpath << " -H \"Content-Type: text/xml\"" + ss << curl_prog.make_preferred() << " --insecure --silent --data @" + << datapath << " -o " << outpath << " -H \"Content-Type: text/xml\"" << " \"https://localhost:8443/api/writable?source=1.2.3.4" << "&apikey=" << key << "&guid=apikey6a76a7e6-dc39-4a7d-964e-1eff3462c06c\""; - system(ss.str().c_str()); + system(CmdString(ss.str()).c_str()); std::this_thread::sleep_for(50ms); ProcessPendingEvents(); std::ifstream f(outpath.string()); @@ -314,10 +337,11 @@ class RestCheckWriteApp : public RestServerApp { } } }; -#ifndef FLATPAK TEST(RestServer, start_stop) { +#ifdef _MSC_VER wxInitializer initializer; +#endif ConfigSetup(); RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; @@ -327,16 +351,23 @@ TEST(RestServer, start_stop) { }; TEST(RestServer, Ping) { + wxDisableAsserts(); +#ifdef _MSC_VER wxInitializer initializer; +#endif ConfigSetup(); RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; +std::cout << "About to run ping\n" << std::flush; RestServerPingApp app(dialog_ctx, route_ctx, g_portable); app.Run(); }; TEST(RestServer, Pong) { +#ifdef _MSC_VER wxInitializer initializer; +#endif + wxDisableAsserts(); ConfigSetup(); RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; @@ -345,7 +376,10 @@ TEST(RestServer, Pong) { }; TEST(RestServer, Version) { +#ifdef _MSC_VER wxInitializer initializer; +#endif + wxDisableAsserts(); ConfigSetup(); RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; @@ -353,36 +387,30 @@ TEST(RestServer, Version) { app.Run(); }; - - - TEST(RestServer, Object) { +#ifdef _MSC_VER wxInitializer initializer; +#endif + wxDisableAsserts(); ConfigSetup(); - auto colour_func = [] (wxString c) { return *wxBLACK; }; - pWayPointMan = new WayPointman(colour_func); - pRouteList = new RouteList; - g_BasePlatform = new BasePlatform(); - pSelect = new Select(); RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; RestServerObjectApp app(dialog_ctx, route_ctx, g_portable); app.Run(); } + TEST(RestServer, CheckWrite) { +#ifdef _MSC_VER wxInitializer initializer; +#endif + wxDisableAsserts(); ConfigSetup(); - auto colour_func = [] (wxString c) { return *wxBLACK; }; - pWayPointMan = new WayPointman(colour_func); - pRouteList = new RouteList; - g_BasePlatform = new BasePlatform(); - pSelect = new Select(); RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; RestCheckWriteApp app(dialog_ctx, route_ctx, g_portable); app.Run(); + delete g_BasePlatform; + g_BasePlatform = 0; } - -#endif // FLATPAK diff --git a/test/tests.cpp b/test/tests.cpp index 07293246f3..58a68c6ee7 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -412,6 +412,7 @@ class PriorityApp2 : public wxAppConsole { class AisApp : public wxAppConsole { public: AisApp(const char* type, const char* msg) : wxAppConsole() { + ConfigSetup(); SetAppName("opencpn_unittests"); g_BasePlatform = new BasePlatform(); pSelectAIS = new Select(); From 40df1b6f14546547ffee0137af1798450a4bc773 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 30 Nov 2023 11:01:54 +0100 Subject: [PATCH 25/63] rest-server_gui: clean up --- include/rest_server_gui.h | 3 +++ src/rest_server_gui.cpp | 15 +++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/include/rest_server_gui.h b/include/rest_server_gui.h index 416b135a2b..6976ee7bc1 100644 --- a/include/rest_server_gui.h +++ b/include/rest_server_gui.h @@ -55,6 +55,8 @@ class AcceptObjectDialog : public wxDialog { public: AcceptObjectDialog(); + AcceptObjectDialog(wxWindow* parent, const wxString& caption, + const wxString& msg1, const wxString msg2); AcceptObjectDialog(wxWindow* parent, wxWindowID id, const wxString& caption, const wxString& hint, const wxPoint& pos, const wxSize& size, long style, const wxString& msg1, const wxString& msg2); @@ -94,6 +96,7 @@ class PINCreateDialog : public wxDialog { public: PINCreateDialog(); + PINCreateDialog(wxWindow* parent, wxWindowID id, const wxString& caption, const wxString& hint, const wxPoint& pos, const wxSize& size, long style); diff --git a/src/rest_server_gui.cpp b/src/rest_server_gui.cpp index 3794976607..21c6a3012b 100644 --- a/src/rest_server_gui.cpp +++ b/src/rest_server_gui.cpp @@ -67,9 +67,7 @@ static void UpdateRouteMgr() { static AcceptObjectDlgResult RunAcceptObjectDlg(const wxString& msg, const wxString& check1msg) { - AcceptObjectDialog dlg(NULL, wxID_ANY, _("OpenCPN Server Message"), - "", wxDefaultPosition, wxDefaultSize, SYMBOL_STG_STYLE, - msg, check1msg); + AcceptObjectDialog dlg(NULL, _("OpenCPN Server Message"), msg, check1msg); int result = dlg.ShowModal(); bool check1 = dlg.GetCheck1Value(); return AcceptObjectDlgResult(result, check1); @@ -88,8 +86,6 @@ RestServerDlgCtx PINCreateDialog::GetDlgCtx() { return ctx; } - - IMPLEMENT_DYNAMIC_CLASS(AcceptObjectDialog, wxDialog) BEGIN_EVENT_TABLE(AcceptObjectDialog, wxDialog) @@ -103,6 +99,14 @@ AcceptObjectDialog::AcceptObjectDialog() { premtext = NULL; } +AcceptObjectDialog::AcceptObjectDialog(wxWindow* parent, + const wxString& caption, + const wxString& msg1, + const wxString msg2) + : AcceptObjectDialog(parent, 0, caption, "", wxDefaultPosition, + wxDefaultSize, SYMBOL_STG_STYLE, + msg1, msg2) {} + AcceptObjectDialog::AcceptObjectDialog(wxWindow* parent, wxWindowID id, const wxString& caption, const wxString& hint, const wxPoint& pos, const wxSize& size, long style, @@ -114,7 +118,6 @@ AcceptObjectDialog::AcceptObjectDialog(wxWindow* parent, wxWindowID id, #ifdef __ANDROID__ androidDisableRotation(); #endif - } AcceptObjectDialog::~AcceptObjectDialog() { From f7ab7fe56afc8867b8d58afe83f36595f442bfc8 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Tue, 28 Nov 2023 11:37:42 +0100 Subject: [PATCH 26/63] ocpn_app.cpp: clean up. --- src/ocpn_app.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/ocpn_app.cpp b/src/ocpn_app.cpp index ab7c5b0f2b..538e7a3335 100644 --- a/src/ocpn_app.cpp +++ b/src/ocpn_app.cpp @@ -43,14 +43,6 @@ #include #include -#if defined(__GNUC__) && (__GNUC__ < 8) -#include -namespace fs = std::experimental::filesystem; -#else -#include -namespace fs = std::filesystem; -#endif - #ifdef __WXMSW__ #include #include From b207947aebe56107e3f8c27f3dad1d25b3342b59 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 30 Nov 2023 21:26:38 +0100 Subject: [PATCH 27/63] flatpak: Don't link to host libraries --- flatpak/org.opencpn.OpenCPN.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flatpak/org.opencpn.OpenCPN.yaml b/flatpak/org.opencpn.OpenCPN.yaml index 86dd5782b9..f1a3ca35bb 100644 --- a/flatpak/org.opencpn.OpenCPN.yaml +++ b/flatpak/org.opencpn.OpenCPN.yaml @@ -137,10 +137,10 @@ modules: - -DOCPN_USE_SYSFS_PORTS=ON - -DBUILD_SHARED_LIBS=OFF - -DOCPN_TARGET_TUPLE=flatpak-x86_64;22.08;x86_64 - - -DCMAKE_FIND_ROOT_PATH=/app - - -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER - - -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH - - -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH + - -DCMAKE_FIND_ROOT_PATH=/app;/usr + - -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=BOTH + - -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY + - -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY build-options: build-args: - --share=network From d71c5377cf670bd03a0b99a2496acc67d47c37ca Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 30 Nov 2023 21:30:42 +0100 Subject: [PATCH 28/63] test: rest-tests: Handle slow to come up network interfaces --- test/rest-tests.cpp | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index c50e50a3d5..03365c6fa9 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -6,6 +6,14 @@ #include #include +#ifndef _WIN32 +#include +#include +#include +#include +#include +#endif + #include #include #include @@ -42,6 +50,31 @@ static void ConfigSetup() { InitBaseConfig(new wxFileConfig("", "", config_path.string())); } +#ifdef _WIN32 +#define GetLocalAddresses() get_local_ipv4_addresses() + +#else +static std::vector GetLocalAddresses() { + struct ifaddrs* ifAddrStruct = 0; + struct ifaddrs* ifa = 0; + void* tmpAddrPtr = 0; + std::vector retvals; + + getifaddrs(&ifAddrStruct); + for (ifa = ifAddrStruct; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) continue; + if (ifa->ifa_addr->sa_family == AF_INET) { + tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + char address_buffer[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, tmpAddrPtr, address_buffer, INET_ADDRSTRLEN); + retvals.push_back(address_buffer); + } + } + if (ifAddrStruct) freeifaddrs(ifAddrStruct); + return retvals; +} +#endif // _WIN32 + static bool g_portable = false; class RestServerApp : public wxAppConsole { public: @@ -50,7 +83,15 @@ class RestServerApp : public wxAppConsole { m_rest_server(ctx, route_ctx, portable) {} void Run() { - std::vector addresses = get_local_ipv4_addresses(); + std::vector addresses; + for (int i = 0; addresses.size() == 0 && i < 10; i++) { + std::this_thread::sleep_for(500ms); + addresses = GetLocalAddresses(); + } + if (!addresses.size()) { + std::cerr << "Cannot get local IP address(!)\n"; + return; + } auto local_address = addresses[0]; fs::path dirpath(std::string(CMAKE_BINARY_DIR) + "/certs"); if (!fs::exists(dirpath)) fs::create_directory(dirpath); From 8c49cdb4ad42b7c01bdc1b7ff08b4361eafdfe93 Mon Sep 17 00:00:00 2001 From: ALec Leamas Date: Thu, 30 Nov 2023 21:51:29 +0100 Subject: [PATCH 29/63] test: rest-tests: Clean upo configuration setup --- test/rest-tests.cpp | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index 03365c6fa9..665a5b3ffc 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -83,6 +83,7 @@ class RestServerApp : public wxAppConsole { m_rest_server(ctx, route_ctx, portable) {} void Run() { + ConfigSetup(); std::vector addresses; for (int i = 0; addresses.size() == 0 && i < 10; i++) { std::this_thread::sleep_for(500ms); @@ -380,10 +381,6 @@ class RestCheckWriteApp : public RestServerApp { }; TEST(RestServer, start_stop) { -#ifdef _MSC_VER - wxInitializer initializer; -#endif - ConfigSetup(); RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; RestServerApp app(dialog_ctx, route_ctx, g_portable); @@ -393,23 +390,14 @@ TEST(RestServer, start_stop) { TEST(RestServer, Ping) { wxDisableAsserts(); -#ifdef _MSC_VER - wxInitializer initializer; -#endif - ConfigSetup(); RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; -std::cout << "About to run ping\n" << std::flush; RestServerPingApp app(dialog_ctx, route_ctx, g_portable); app.Run(); }; TEST(RestServer, Pong) { -#ifdef _MSC_VER - wxInitializer initializer; -#endif wxDisableAsserts(); - ConfigSetup(); RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; RestServer404App app(dialog_ctx, route_ctx, g_portable); @@ -417,11 +405,7 @@ TEST(RestServer, Pong) { }; TEST(RestServer, Version) { -#ifdef _MSC_VER - wxInitializer initializer; -#endif wxDisableAsserts(); - ConfigSetup(); RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; RestServerVersionApp app(dialog_ctx, route_ctx, g_portable); @@ -429,12 +413,7 @@ TEST(RestServer, Version) { }; TEST(RestServer, Object) { -#ifdef _MSC_VER - wxInitializer initializer; -#endif wxDisableAsserts(); - ConfigSetup(); - RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; RestServerObjectApp app(dialog_ctx, route_ctx, g_portable); @@ -442,12 +421,7 @@ TEST(RestServer, Object) { } TEST(RestServer, CheckWrite) { -#ifdef _MSC_VER - wxInitializer initializer; -#endif wxDisableAsserts(); - ConfigSetup(); - RestServerDlgCtx dialog_ctx; RouteCtx route_ctx; RestCheckWriteApp app(dialog_ctx, route_ctx, g_portable); From 15c11ad1f8f1d593f134c1028ea401922031b61f Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Fri, 1 Dec 2023 10:14:25 +0100 Subject: [PATCH 30/63] rest server: Handle GPX parse errors. --- include/rest_server.h | 1 + src/rest_server.cpp | 6 ++++++ test/rest-tests.cpp | 10 +++++----- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/rest_server.h b/include/rest_server.h index fe3ffe364d..78b0c02c1f 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -53,6 +53,7 @@ enum class RestServerResult { NoError = 0, GenericError, ObjectRejected, + ObjectParseError, DuplicateRejected, RouteInsertError, NewPinRequested, diff --git a/src/rest_server.cpp b/src/rest_server.cpp index 5d310a12fb..bf969881f2 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -400,7 +400,11 @@ void RestServer::HandleServerMessage(ObservedEvt& event) { UpdateReturnStatus(RestServerResult::DuplicateRejected); } return; + } else if (evt_data->cmd == RestIoEvtData::Cmd::Ping) { + UpdateReturnStatus(RestServerResult::NoError); + return; } + // Load the GPX file pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(m_upload_path.c_str()); @@ -418,6 +422,8 @@ void RestServer::HandleServerMessage(ObservedEvt& event) { HandleWaypoint(object, *evt_data); } } + } else { + UpdateReturnStatus(RestServerResult::ObjectParseError); } } diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index 665a5b3ffc..a3a3d18b6a 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -140,7 +140,7 @@ class RestServerPingApp : public RestServerApp { std::ifstream f(path.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 5}"); // Bad api key + EXPECT_EQ(result, "{\"result\": 6}"); // Bad api key }{ fs::path curl_prog(CURLPROG); std::stringstream ss; @@ -243,7 +243,7 @@ class RestServerObjectApp : public RestServerApp { std::ifstream f(outpath.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 5}"); // New pin required + EXPECT_EQ(result, "{\"result\": 6}"); // New pin required } { // Try to transfer using api key set up above. fs::path curl_prog(CURLPROG); @@ -286,7 +286,7 @@ class RestServerObjectApp : public RestServerApp { std::ifstream f(outpath.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 3}"); // Duplicate rejected + EXPECT_EQ(result, "{\"result\": 4}"); // Duplicate rejected } { // Try to transfer same object using argument force fs::path curl_prog(CURLPROG); @@ -338,7 +338,7 @@ class RestCheckWriteApp : public RestServerApp { std::ifstream f(outpath.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 5}"); // New pin required + EXPECT_EQ(result, "{\"result\": 6}"); // New pin required } { // Try check our standard object, fix the api key auto key = m_rest_server.m_key_map["1.2.3.4"]; @@ -375,7 +375,7 @@ class RestCheckWriteApp : public RestServerApp { std::ifstream f(outpath.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 3}"); // Duplicate reject + EXPECT_EQ(result, "{\"result\": 4}"); // Duplicate reject } } }; From dd529fb8a7aaecbb00b136c09e51ecaa8f1a4704 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Fri, 1 Dec 2023 10:34:15 +0100 Subject: [PATCH 31/63] rest server: if-else -> switch(), clang-format switch() statements are generally better to handle enums than if-else, for example when adding new commands it gives the compiler a chance to spot possible loose ends. --- include/rest_server.h | 2 +- src/rest_server.cpp | 133 +++++++++++++++++++++--------------------- 2 files changed, 67 insertions(+), 68 deletions(-) diff --git a/include/rest_server.h b/include/rest_server.h index 78b0c02c1f..a24b77f706 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -114,7 +114,7 @@ class RouteCtx { * * Supports the following endpoints: * - * GET /api/ping?api_key=`` &source=`
+ * GET /api/ping?api_key=`` &source=``
* Basic ping check, verifies api_key i. e., the pairing. * - Parameters: * See below
diff --git a/src/rest_server.cpp b/src/rest_server.cpp index bf969881f2..36f35d4a54 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -190,7 +190,8 @@ static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { struct mg_tls_opts opts = {0}; memset(&opts, 0, sizeof(mg_tls_opts)); // FIXME (leamas) - opts.ca = nullptr; // "cert.pem"; // Uncomment to enable two-way SSL + opts.ca = + nullptr; // "cert.pem"; // Uncomment to enable two-way SSL opts.cert = parent->m_cert_file.c_str(); // Certificate PEM file opts.certkey = parent->m_key_file.c_str(); // The key PEM file opts.ciphers = nullptr; @@ -206,13 +207,12 @@ static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { } else if (mg_http_match_uri(hm, "/api/writable")) { HandleWritable(c, hm, parent); } else if (mg_http_match_uri(hm, "/api/get-version")) { - std::string reply(kVersionReply); - ocpn::replace(reply, "@version@", PACKAGE_VERSION); - mg_http_reply(c, 200, "", reply.c_str()); + std::string reply(kVersionReply); + ocpn::replace(reply, "@version@", PACKAGE_VERSION); + mg_http_reply(c, 200, "", reply.c_str()); } else { mg_http_reply(c, 404, "", "url: not found"); } - } } @@ -352,78 +352,77 @@ bool RestServer::CheckApiKey(const RestIoEvtData& evt_data) { } void RestServer::HandleServerMessage(ObservedEvt& event) { - if (event.GetId() == ORS_START_OF_SESSION) { - // Prepare a temp file to catch chuncks that might follow - m_upload_path = wxFileName::CreateTempFileName("ocpn_tul").ToStdString(); - - m_ul_stream.open(m_upload_path.c_str(), std::ios::out | std::ios::trunc); - if (!m_ul_stream.is_open()) { - wxLogMessage("REST_server: Cannot open %s for write", m_upload_path); - m_upload_path.clear(); // reset for next time. - return; - } - return; - } - auto evt_data = UnpackEvtPointer(event); - if (event.GetId() == ORS_CHUNK_N) { - // Stream out to temp file - if (!m_upload_path.empty() && m_ul_stream.is_open()) { - m_ul_stream.write(evt_data->payload.c_str(), evt_data->payload.size()); - } - return; - } - - if (event.GetId() == ORS_CHUNK_LAST) { - // Cancel existing dialog and close temp file - wxEvent* event = new wxCloseEvent; - wxQueueEvent(m_pin_dialog, event); - if (!m_upload_path.empty() && m_ul_stream.is_open()) m_ul_stream.close(); - - // Io thread might be waiting for return_status on notify_one() - UpdateReturnStatus(RestServerResult::GenericError); + switch (event.GetId()) { + case ORS_START_OF_SESSION: + // Prepare a temp file to catch chuncks that might follow + m_upload_path = wxFileName::CreateTempFileName("ocpn_tul").ToStdString(); + + m_ul_stream.open(m_upload_path.c_str(), std::ios::out | std::ios::trunc); + if (!m_ul_stream.is_open()) { + wxLogMessage("REST_server: Cannot open %s for write", m_upload_path); + m_upload_path.clear(); // reset for next time. + return; + } + return; + case ORS_CHUNK_N: + // Stream out to temp file + if (!m_upload_path.empty() && m_ul_stream.is_open()) { + m_ul_stream.write(evt_data->payload.c_str(), evt_data->payload.size()); + } + return; + case ORS_CHUNK_LAST: + // Cancel existing dialog and close temp file + wxEvent* event = new wxCloseEvent; + wxQueueEvent(m_pin_dialog, event); + if (!m_upload_path.empty() && m_ul_stream.is_open()) m_ul_stream.close(); + + // Io thread might be waiting for return_status on notify_one() + UpdateReturnStatus(RestServerResult::GenericError); + break; } - if (CheckApiKey(*evt_data)) { - UpdateReturnStatus(RestServerResult::NoError); - } else { + if (!CheckApiKey(*evt_data)) { UpdateReturnStatus(RestServerResult::NewPinRequested); return; } - if (evt_data->cmd == RestIoEvtData::Cmd::CheckWrite) { - auto guid = evt_data->payload; - auto dup = m_route_ctx.find_route_by_guid(guid); - if (!dup || evt_data->force || m_overwrite) { - UpdateReturnStatus(RestServerResult::NoError); - } else { - UpdateReturnStatus(RestServerResult::DuplicateRejected); + UpdateReturnStatus(RestServerResult::NoError); + switch (evt_data->cmd) { + case RestIoEvtData::Cmd::Ping: + return; + case RestIoEvtData::Cmd::CheckWrite: { + auto guid = evt_data->payload; + auto dup = m_route_ctx.find_route_by_guid(guid); + if (!dup || evt_data->force || m_overwrite) { + UpdateReturnStatus(RestServerResult::NoError); + } else { + UpdateReturnStatus(RestServerResult::DuplicateRejected); + } + return; } - return; - } else if (evt_data->cmd == RestIoEvtData::Cmd::Ping) { - UpdateReturnStatus(RestServerResult::NoError); - return; - } - - // Load the GPX file - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(m_upload_path.c_str()); - if (result.status == pugi::status_ok) { - m_upload_path.clear(); // empty for next time - - pugi::xml_node objects = doc.child("gpx"); - for (pugi::xml_node object = objects.first_child(); object; - object = object.next_sibling()) { - if (!strcmp(object.name(), "rte")) { - HandleRoute(object, *evt_data); - } else if (!strcmp(object.name(), "trk")) { - HandleTrack(object, *evt_data); - } else if (!strcmp(object.name(), "wpt")) { - HandleWaypoint(object, *evt_data); + case RestIoEvtData::Cmd::Object: { + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file(m_upload_path.c_str()); + if (result.status == pugi::status_ok) { + m_upload_path.clear(); // empty for next time + + pugi::xml_node objects = doc.child("gpx"); + for (pugi::xml_node object = objects.first_child(); object; + object = object.next_sibling()) { + if (!strcmp(object.name(), "rte")) { + HandleRoute(object, *evt_data); + } else if (!strcmp(object.name(), "trk")) { + HandleTrack(object, *evt_data); + } else if (!strcmp(object.name(), "wpt")) { + HandleWaypoint(object, *evt_data); + } + } + } else { + UpdateReturnStatus(RestServerResult::ObjectParseError); } + break; } - } else { - UpdateReturnStatus(RestServerResult::ObjectParseError); } } From 6c71226ccd6420245b19214783437f36a56dbc4f Mon Sep 17 00:00:00 2001 From: Hakan Date: Fri, 1 Dec 2023 22:21:42 +0100 Subject: [PATCH 32/63] AIS Meteo: Never show CPA or track --- src/ais_decoder.cpp | 9 +++++---- src/canvasMenu.cpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ais_decoder.cpp b/src/ais_decoder.cpp index a2ff4e0198..b3fb22b627 100644 --- a/src/ais_decoder.cpp +++ b/src/ais_decoder.cpp @@ -3345,8 +3345,6 @@ bool AisDecoder::Parse_VDXBitstring(AisBitstring *bstr, b_posn_report = true; ptd->PositionReportTicks = now.GetTicks(); ptd->b_nameValid = true; - ptd->b_show_AIS_CPA = false; - ptd->bCPA_Valid = false; parse_result = true; } @@ -3499,8 +3497,6 @@ bool AisDecoder::Parse_VDXBitstring(AisBitstring *bstr, b_posn_report = true; ptd->PositionReportTicks = now.GetTicks(); ptd->b_nameValid = true; - ptd->b_show_AIS_CPA = false; - ptd->bCPA_Valid = false; parse_result = true; } @@ -3843,6 +3839,11 @@ void AisDecoder::UpdateOneCPA(AisTargetData *ptarget) { ptarget->bCPA_Valid = false; return; } + // Ais Meteo is not a hard target in danger for collision + if (ptarget->Class == AIS_METEO) { + ptarget->bCPA_Valid = false; + return; + } // There can be no collision between ownship and itself.... // This can happen if AIVDO messages are received, and there is another diff --git a/src/canvasMenu.cpp b/src/canvasMenu.cpp index 4e6747e5ef..b2c03fe6d8 100644 --- a/src/canvasMenu.cpp +++ b/src/canvasMenu.cpp @@ -647,7 +647,7 @@ void CanvasMenuHandler::CanvasPopupMenu(int x, int y, int seltype) { MenuAppend1(menuAIS, ID_DEF_MENU_AIS_CPA, _("Show Target CPA")); } MenuAppend1(menuAIS, ID_DEF_MENU_AISTARGETLIST, _("Target List...")); - if (1 /*g_bAISShowTracks*/) { + if (myptarget->Class != AIS_METEO /*g_bAISShowTracks*/) { if (myptarget && myptarget->b_show_track) MenuAppend1(menuAIS, ID_DEF_MENU_AISSHOWTRACK, _("Hide Target Track")); From bc66e29fd78fd056469c52c5bb60d241ee972490 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Fri, 1 Dec 2023 12:13:46 +0100 Subject: [PATCH 33/63] misc: more clean up --- include/garmin_wrapper.h | 1 - include/rest_server.h | 49 +++++++++++++++++++++++---------------- src/comm_n0183_output.cpp | 1 - src/pincode.cpp | 24 ------------------- src/rest_server.cpp | 18 ++++++-------- test/rest-tests.cpp | 16 ++++++------- 6 files changed, 44 insertions(+), 65 deletions(-) diff --git a/include/garmin_wrapper.h b/include/garmin_wrapper.h index 644b5c1697..58395b1948 100644 --- a/include/garmin_wrapper.h +++ b/include/garmin_wrapper.h @@ -31,7 +31,6 @@ #include #include "route.h" -//#include "navutil.h" #include "comm_n0183_output.h" /* Wrapped interface from higher level objects */ diff --git a/include/rest_server.h b/include/rest_server.h index a24b77f706..1a2f2deabf 100644 --- a/include/rest_server.h +++ b/include/rest_server.h @@ -48,16 +48,21 @@ namespace fs = std::filesystem; #include "route.h" #include "track.h" -/** Return codes from HandleServerMessage and eventually in the http response */ +/** + * Return codes from HandleServerMessage and eventually in the http response + * Since they are transported as integers on the wire they cannot really be + * changed without breaking compatibility with older servers. Adding new types + * should be fine. + */ enum class RestServerResult { NoError = 0, - GenericError, - ObjectRejected, - ObjectParseError, - DuplicateRejected, - RouteInsertError, - NewPinRequested, - Void + GenericError = 1, + ObjectRejected = 2, + DuplicateRejected = 3, + RouteInsertError = 4, + NewPinRequested = 5, + ObjectParseError = 6, + Void = 100 }; /** Dialog return codes. */ @@ -112,22 +117,23 @@ class RouteCtx { * * Opencpn REST API * - * Supports the following endpoints: + * Supported endpoints: * * GET /api/ping?api_key=`` &source=``
* Basic ping check, verifies api_key i. e., the pairing. * - Parameters: - * See below
+ * - source=`` Mandatory, origin ip address or hostname. + * - api_key=`` Mandatory, as obtained when pairing, see below. * - Returns: - * {"result": ``} + * {"result": ``} * - * POST /api/rx_object?api_key=`` &source=``&force=1
- * Upload a GPX route, track or waypoints. + * POST /api/rx_object?api_key=``&source=``&force=1
+ * Upload GPX route(s), track(s) or waypoint(s). * - Parameters: - * - source=`` Mandatory, origin ip address or hostname - * - force=`<1>` f present, the host object is unconditionally - * updated. If not, host may run a "OK to overwrite" dialog. + * - source=`` Mandatory, origin ip address or hostname. * - api_key=`` Mandatory, as obtained when pairing, see below. + * - force=`<1>` if present, the host object is unconditionally + * updated. If not, host may run a "OK to overwrite" dialog. * * - Body: * xml-encoded GPX data for one or more route(s), track(s) and/or @@ -137,12 +143,17 @@ class RouteCtx { * * GET /api/writable?guid=<`guid>`
* Check if route or waypoint with given guid is writable.
- * - Returns:: + * - Parameters: + * - source=`` Mandatory, origin ip address or hostname. + * - api_key=`` Mandatory, as obtained when pairing, see below. + * - guid=`` Route, waypoint or track guid. + * - Returns: * {"result": ``} * * GET /api/get-version
* Return current server version string. Does not require api_key or source. - * - Returns (example:: + * - Parameters: None + * - Returns (example): * {"version": "5.8.9" } * * Authentication uses a pairing mechanism. When an unpaired device @@ -174,10 +185,8 @@ class RestServer : public AbstractRestServer, public wxEvtHandler { ~RestServer() override; - /** Start the server thread. */ bool StartServer(const fs::path& certificate_location) override; - /** Stop server thread, blocks until completed. */ void StopServer() override; /** IoThread interface.*/ diff --git a/src/comm_n0183_output.cpp b/src/comm_n0183_output.cpp index 21dd3f0635..48411f2acd 100644 --- a/src/comm_n0183_output.cpp +++ b/src/comm_n0183_output.cpp @@ -460,7 +460,6 @@ int SendRouteToGPS_N0183(Route* pr, const wxString& com_name, } dlg_ctx.set_value(40); - lret_val = Garmin_GPS_SendRoute(short_com, pr, dlg_ctx); if (lret_val != 1) { MESSAGE_LOG << "Error Sending Route to Garmin GPS on port: " << short_com diff --git a/src/pincode.cpp b/src/pincode.cpp index c1137d1849..ba883ddac6 100644 --- a/src/pincode.cpp +++ b/src/pincode.cpp @@ -27,31 +27,7 @@ #include #include "picosha2.h" -/** -// Need a new PIN confirmation - m_dPIN = wxMin(rand() % 10000 + 1, 9999); - m_sPIN.Printf("%04d", m_dPIN); - std::string new_api_key = PINtoRandomKeyString(m_dPIN); - - -unsigned long long PINtoRandomKey( int dpin) { - std::linear_congruential_engine engine; - engine.seed( dpin ); - unsigned long long r = engine(); - return r; - -} - -std::string PINtoRandomKeyString( int dpin) { - unsigned long long pin = PINtoRandomKey(dpin); - char buffer[100]; - snprintf(buffer, sizeof(buffer)-1, "%0llX", pin); - return std::string(buffer); -} - - -**/ std::string Pincode::CompatHash() { std::linear_congruential_engine engine; diff --git a/src/rest_server.cpp b/src/rest_server.cpp index 36f35d4a54..e58952c213 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -71,7 +71,7 @@ struct RestIoEvtData { return {Cmd::Object, key, src, gpx_data, _force}; } - /** Create a Cod::Ping instance: */ + /** Create a Cmd::Ping instance: */ static RestIoEvtData CreatePingData(const std::string& key, const std::string& src) { return {Cmd::Ping, key, src, "", false}; @@ -188,12 +188,9 @@ static void fn(struct mg_connection* c, int ev, void* ev_data, void* fn_data) { if (ev == MG_EV_ACCEPT /*&& fn_data != NULL*/) { struct mg_tls_opts opts = {0}; - memset(&opts, 0, sizeof(mg_tls_opts)); // FIXME (leamas) - - opts.ca = - nullptr; // "cert.pem"; // Uncomment to enable two-way SSL - opts.cert = parent->m_cert_file.c_str(); // Certificate PEM file - opts.certkey = parent->m_key_file.c_str(); // The key PEM file + opts.ca = nullptr; // "cert.pem" Uncomment to enable two-way SSL + opts.cert = parent->m_cert_file.c_str(); + opts.certkey = parent->m_key_file.c_str(); opts.ciphers = nullptr; mg_tls_init(c, &opts); } else if (ev == MG_EV_TLS_HS) { // Think of this as "start of session" @@ -304,10 +301,10 @@ bool RestServer::StartServer(const fs::path& certificate_location) { } void RestServer::StopServer() { - wxLogMessage(wxString::Format("Stopping REST service")); + wxLogDebug("Stopping REST service"); // Kill off the IO Thread if alive if (m_thread.joinable()) { - wxLogMessage("Stopping io thread"); + wxLogDebug("Stopping io thread"); m_io_thread.Stop(); m_io_thread.WaitUntilStopped(); m_thread.join(); @@ -373,8 +370,7 @@ void RestServer::HandleServerMessage(ObservedEvt& event) { return; case ORS_CHUNK_LAST: // Cancel existing dialog and close temp file - wxEvent* event = new wxCloseEvent; - wxQueueEvent(m_pin_dialog, event); + wxQueueEvent(m_pin_dialog, new wxCloseEvent); if (!m_upload_path.empty() && m_ul_stream.is_open()) m_ul_stream.close(); // Io thread might be waiting for return_status on notify_one() diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index a3a3d18b6a..6924e69c50 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -57,16 +57,16 @@ static void ConfigSetup() { static std::vector GetLocalAddresses() { struct ifaddrs* ifAddrStruct = 0; struct ifaddrs* ifa = 0; - void* tmpAddrPtr = 0; + void* tmp_addr = 0; std::vector retvals; getifaddrs(&ifAddrStruct); for (ifa = ifAddrStruct; ifa; ifa = ifa->ifa_next) { if (!ifa->ifa_addr) continue; if (ifa->ifa_addr->sa_family == AF_INET) { - tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + tmp_addr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; char address_buffer[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, tmpAddrPtr, address_buffer, INET_ADDRSTRLEN); + inet_ntop(AF_INET, tmp_addr, address_buffer, INET_ADDRSTRLEN); retvals.push_back(address_buffer); } } @@ -140,7 +140,7 @@ class RestServerPingApp : public RestServerApp { std::ifstream f(path.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 6}"); // Bad api key + EXPECT_EQ(result, "{\"result\": 5}"); // Bad api key }{ fs::path curl_prog(CURLPROG); std::stringstream ss; @@ -243,7 +243,7 @@ class RestServerObjectApp : public RestServerApp { std::ifstream f(outpath.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 6}"); // New pin required + EXPECT_EQ(result, "{\"result\": 5}"); // New pin required } { // Try to transfer using api key set up above. fs::path curl_prog(CURLPROG); @@ -286,7 +286,7 @@ class RestServerObjectApp : public RestServerApp { std::ifstream f(outpath.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 4}"); // Duplicate rejected + EXPECT_EQ(result, "{\"result\": 3}"); // Duplicate rejected } { // Try to transfer same object using argument force fs::path curl_prog(CURLPROG); @@ -338,7 +338,7 @@ class RestCheckWriteApp : public RestServerApp { std::ifstream f(outpath.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 6}"); // New pin required + EXPECT_EQ(result, "{\"result\": 5}"); // New pin required } { // Try check our standard object, fix the api key auto key = m_rest_server.m_key_map["1.2.3.4"]; @@ -375,7 +375,7 @@ class RestCheckWriteApp : public RestServerApp { std::ifstream f(outpath.string()); std::string result; std::getline(f, result); - EXPECT_EQ(result, "{\"result\": 4}"); // Duplicate reject + EXPECT_EQ(result, "{\"result\": 3}"); // Duplicate reject } } }; From 5cf8c67ee147b36cf3c70b182c0704c6af6ca7ea Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sat, 2 Dec 2023 16:47:11 +0100 Subject: [PATCH 34/63] ci: .circleci: Refactor filtered branches -- DRY fix --- .circleci/config.yml | 45 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b94b07b0eb..e7957208d0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -126,38 +126,33 @@ jobs: - /usr/local/lib - run: ci/generic-upload.sh +std-filters: &std-filters + filters: + branches: + only: + - master + - build + - flatpak + + workflows: version: 2 build_all: jobs: - build-bionic: - filters: - branches: - only: - - master + <<: *std-filters + - build-focal: - filters: - branches: - only: - - master + <<: *std-filters + - build-flatpak: - filters: - branches: - only: - - flatpak - - master + <<: *std-filters + - build-android-armhf: - filters: - branches: - only: - - master + <<: *std-filters + - build-macos-universal: - filters: - branches: - only: - - master + <<: *std-filters + - build-macos-intel-legacy: - filters: - branches: - only: - - master + <<: *std-filters From 17cb82d6b6705fdf74f4fe4bf8ecaa55d3d46ac5 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sat, 2 Dec 2023 16:54:42 +0100 Subject: [PATCH 35/63] ci: Add wxWidgets 3.2 to debian control file (#3507) Closes: #3507 --- ci/control | 84 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 25 deletions(-) diff --git a/ci/control b/ci/control index 7fb718c79e..2dd88588e1 100644 --- a/ci/control +++ b/ci/control @@ -1,44 +1,78 @@ Source: opencpn -Build-Depends: debhelper (>= 9), - cmake3 | cmake (>> 3.5.0), +Maintainer: Alec Leamas +Section: misc +Priority: optional +Build-Depends: debhelper-compat (>= 11), + cmake, + googletest (>= 1.11.0) | base-files, libarchive-dev, libbz2-dev, - libcairo2-dev, - libcurl4-openssl-dev, + libcurl4-gnutls-dev | libcurl4-openssl-dev, + libcxx-serial-dev | base-files (<< 12), + libdrm-dev, libelf-dev, libexif-dev, - libgdk-pixbuf2.0-dev, - libgl1-mesa-dev, - libglib2.0-dev, + libgdk-pixbuf-2.0-dev | base-files (<< 12), + libglew-dev, + libgtk-3-dev, + libjs-highlight.js, + libjs-mathjax, liblz4-dev, liblzma-dev, libpango1.0-dev, - libsndfile1-dev, - libssl-dev, libsqlite3-dev, + libssl-dev, libtinyxml-dev, libudev-dev, - libunarr-dev | base-files (<< 11), + libunarr-dev | base-files (<< 12), libusb-1.0-0-dev, - libglew-dev, - base-files (>=11) | libwxgtk3.0-dev, + libwxsvg-dev (>= 2:1.5.21) | base-files (<< 12), base-files (>=11) | libwxgtk3.0-0v5 | libwxgtk3.0-0, - libwxgtk3.0-gtk3-dev | base-files (<< 10), - base-files (>=10) | libwxgtk-webview3.0-dev, - libwxgtk-webview3.0-gtk3-dev | base-files (<< 11), - libwxsvg-dev | base-files (<< 11), + libwxgtk3.2-dev | libwxgtk3.0-gtk3-dev | base-files (<< 10), + libwxgtk-webview3.2-dev | libwxgtk-webview3.0-gtk3-dev | base-files (<< 11), + lsb-release, portaudio19-dev, rapidjson-dev - -Standards-Version: 4.3.0 +Standards-Version: 4.6.2 +Vcs-Browser: https://gitlab.com/leamas/opencpn +Vcs-Git: https://gitlab.com/leamas/opencpn.git -b debian/sid Homepage: https://opencpn.org +Rules-Requires-Root: no -Description: Packages needed to build opencpn on debian. - The Build-Depends field can be used to install dependencies - using something like: +Package: opencpn +Architecture: any +Depends: opencpn-data (>= ${source:Version}), + bzip2, + libjs-mathjax, + libjs-highlight.js, + ${shlibs:Depends}, + ${misc:Depends} +Recommends: wx3.0-i18n +Breaks: opencpn-plugins (<< 4.8.8~) +Replaces: opencpn-plugins (<< 4.8.8~) +Suggests: binutils +Description: Open Source Chartplotter and Marine GPS Navigation Software + Chart Plotter and Navigational software program for use underway + or as a planning tool. Developed by a team of active sailors using real + world conditions for program testing and refinement. + By default supports raster and vector formats like BSB and S63. Support for + many other formats are available in plugins. Other plugins provides + support for e. g., AIS, radar and weather maps. + Application has language support in 20+ languages. . - . sudo apt install devscripts equivs - . sudo mk-build-deps --install ci/control + This package contains programs, libraries and some support files. + +Package: opencpn-data +Architecture: all +Multi-Arch: foreign +Depends: ${misc:Depends} +Description: Open Source Chartplotter and Marine GPS Navigation Software (data) + Chart Plotter and Navigational software program for use underway + or as a planning tool. Developed by a team of active sailors using real + world conditions for program testing and refinement. + By default supports raster and vector formats like BSB and S63. Support for + many other formats are available in plugins. Other plugins provides + support for e. g., AIS, radar and weather maps. + Application has language support in 20+ languages. . - These packages are available in trusty+. The base-files - fallback is for optional packages available in later releases. + This package contains architecture independent data files. From 348242ec08b76c633977afae219cc3a6aa46ac04 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sat, 2 Dec 2023 16:56:18 +0100 Subject: [PATCH 36/63] ci: circleci: Add bookworm build (#3507) --- .circleci/config.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index e7957208d0..0337cb30bc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,20 @@ --- version: 2 jobs: + build-bookworm: + docker: + - image: debian:bookworm + auth: + username: $DOCKER_USER + password: $DOCKER_PW + environment: + - OCPN_TARGET: bookworm + steps: + - checkout + - run: cat /etc/apt/sources.list + - run: ci/generic-build-debian.sh + - run: ci/generic-upload.sh + build-bionic: docker: - image: circleci/buildpack-deps:bionic-scm @@ -14,6 +28,7 @@ jobs: - run: cat /etc/apt/sources.list - run: ci/generic-build-debian.sh - run: ci/generic-upload.sh + build-focal: docker: - image: circleci/buildpack-deps:focal-scm @@ -28,6 +43,7 @@ jobs: - run: cat /etc/apt/sources.list - run: ci/generic-build-debian.sh - run: ci/generic-upload.sh + build-flatpak: working_directory: ~/OpenCPN machine: @@ -139,6 +155,9 @@ workflows: version: 2 build_all: jobs: + - build-bookworm: + <<: *std-filters + - build-bionic: <<: *std-filters From 405522cb3f1dcad5ee3284cae75d0d9c1a87a331 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sat, 2 Dec 2023 17:31:27 +0100 Subject: [PATCH 37/63] ci/generic-build-debian: Fix long line --- ci/generic-build-debian.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ci/generic-build-debian.sh b/ci/generic-build-debian.sh index 6a5630b463..b8e088e79a 100755 --- a/ci/generic-build-debian.sh +++ b/ci/generic-build-debian.sh @@ -8,7 +8,10 @@ src_tree_root="$(dirname $(readlink -f $0))/.." sudo apt-get -qq update sudo apt-get install --yes --force-yes -q devscripts equivs -mk-build-deps "${src_tree_root}/ci/control" --install --root-cmd=sudo --remove --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes --force-yes" +mk-build-deps "${src_tree_root}/ci/control" \ + --install --root-cmd=sudo\ + --remove \ + --tool="apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes --force-yes" sudo apt-get --allow-unauthenticated --yes --force-yes install -f # Xenial finds webview header but not the library: From a7b11d7ca774cef03aab602601a6e71c244d1f6d Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sat, 2 Dec 2023 17:32:31 +0100 Subject: [PATCH 38/63] ci: circleci: Use custom debian images, focal -> jammy Existing buildpacks-deps images does not work with circleci due to missing git, sudo and /etc/apt/sources.list Update focal to latest build jammy so we build the latest and oldest version we support. --- .circleci/config.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0337cb30bc..58ca798f33 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,9 +1,13 @@ --- +# Buildpack images found at: https://hub.docker.com/_/buildpack-deps/ + version: 2 jobs: build-bookworm: docker: - - image: debian:bookworm + - image: leamas/debian-git:bookworm + # bookworm-scm fails on non-existing /etc/apt/sources.list + # - image: buildpack-deps:bookworm-scm auth: username: $DOCKER_USER password: $DOCKER_PW @@ -29,14 +33,16 @@ jobs: - run: ci/generic-build-debian.sh - run: ci/generic-upload.sh - build-focal: + build-jammy: docker: - - image: circleci/buildpack-deps:focal-scm + - image: leamas/ubuntu-git:jammy + # jammy-scm fails on missing sudo + # - image: buildpack-deps:jammy-scm auth: username: $DOCKER_USER password: $DOCKER_PW environment: - - OCPN_TARGET: focal-gtk3 + - OCPN_TARGET: jammy - CMAKE_BUILD_PARALLEL_LEVEL: 2 steps: - checkout @@ -161,7 +167,7 @@ workflows: - build-bionic: <<: *std-filters - - build-focal: + - build-jammy: <<: *std-filters - build-flatpak: From df23f39715ddc7e48264eeae2eb73c7552238a4d Mon Sep 17 00:00:00 2001 From: Pavel Kalian Date: Sun, 3 Dec 2023 19:43:00 -0300 Subject: [PATCH 39/63] Update wxWidgets in flatpak to 3.2.4 --- flatpak/org.opencpn.OpenCPN.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flatpak/org.opencpn.OpenCPN.yaml b/flatpak/org.opencpn.OpenCPN.yaml index 86dd5782b9..1bbb0c1739 100644 --- a/flatpak/org.opencpn.OpenCPN.yaml +++ b/flatpak/org.opencpn.OpenCPN.yaml @@ -107,8 +107,8 @@ modules: - name: wxGTK3 sources: - type: archive - url: https://github.com/wxWidgets/wxWidgets/releases/download/v3.2.3/wxWidgets-3.2.3.tar.bz2 - sha256: c170ab67c7e167387162276aea84e055ee58424486404bba692c401730d1a67a + url: https://github.com/wxWidgets/wxWidgets/releases/download/v3.2.4/wxWidgets-3.2.4.tar.bz2 + sha256: 0640e1ab716db5af2ecb7389dbef6138d7679261fbff730d23845ba838ca133e config-opts: - --with-gtk=3 - --with-opengl From 25d6ef13a87f6929c5184d7debce0798e010a0a1 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Mon, 4 Dec 2023 16:33:15 +0100 Subject: [PATCH 40/63] rest server: Only run ProcessPendingEvents in unit test. --- src/rest_server.cpp | 2 ++ test/CMakeLists.txt | 1 + 2 files changed, 3 insertions(+) diff --git a/src/rest_server.cpp b/src/rest_server.cpp index e58952c213..71f6126804 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -105,7 +105,9 @@ static void PostEvent(RestServer* parent, auto evt = new ObservedEvt(REST_IO_EVT, id); evt->SetSharedPtr(evt_data); parent->QueueEvent(evt); +#ifdef UNIT_TESTS wxTheApp->ProcessPendingEvents(); +#endif } static void HandleRxObject(struct mg_connection* c, struct mg_http_message* hm, diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ebc372827a..c06e1f84b9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -45,6 +45,7 @@ target_compile_definitions(tests PUBLIC CLIAPP USE_MOCK_DEFS CMAKE_BINARY_DIR="${CMAKE_BINARY_DIR}" TESTDATA="${CMAKE_CURRENT_LIST_DIR}/testdata" + UNIT_TESTS ) if (MSVC) target_link_libraries(tests From b7d2204a1d6e11878f33f0f85980ebc56972c7f1 Mon Sep 17 00:00:00 2001 From: Dave Date: Mon, 4 Dec 2023 11:32:05 -0500 Subject: [PATCH 41/63] Correct plugin chart (e.g. s63) rendering clip region setup. --- src/glChartCanvas.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/glChartCanvas.cpp b/src/glChartCanvas.cpp index 8d250faeef..ec0fd8da0b 100644 --- a/src/glChartCanvas.cpp +++ b/src/glChartCanvas.cpp @@ -3260,13 +3260,18 @@ void glChartCanvas::RenderQuiltViewGL(ViewPort &vp, ChartPlugInWrapper *ChPI = dynamic_cast(chart); if (ChPI) { + SetClipRegion(vp, get_region); RenderNoDTA(vp, get_region); ChPI->RenderRegionViewOnGLNoText(*m_pcontext, vp, rect_region, get_region); + DisableClipRegion(); + } else { + SetClipRegion(vp, get_region); RenderNoDTA(vp, get_region); chart->RenderRegionViewOnGL(*m_pcontext, vp, rect_region, get_region); + DisableClipRegion(); } } } From 4a0fccfd9a832aebb18d342d9aa7d783349d32d9 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Mon, 4 Dec 2023 21:31:02 +0100 Subject: [PATCH 42/63] rest server: clean up, bugfix --- src/rest_server.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/rest_server.cpp b/src/rest_server.cpp index 71f6126804..66e75439e9 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -48,9 +48,7 @@ static const char* const kHttpsAddr = "http://0.0.0.0:8443"; static const char* const kHttpPortableAddr = "http://0.0.0.0:8001"; static const char* const kHttpsPortableAddr = "http://0.0.0.0:8444"; -static const char* const kVersionReply = R"""( -{ "version": "@version@" } -)"""; +static const char* const kVersionReply = R"""( { "version": "@version@" })"""; /** Kind of messages sent from io thread to main code. */ enum { ORS_START_OF_SESSION, ORS_CHUNK_N, ORS_CHUNK_LAST }; @@ -77,7 +75,7 @@ struct RestIoEvtData { return {Cmd::Ping, key, src, "", false}; } - /** Cmd::CheckWrite constructor. */ + /** Create a Cmd::CheckWrite instance. */ static RestIoEvtData CreateChkWriteData(const std::string& key, const std::string& src, const std::string& guid) { @@ -374,9 +372,6 @@ void RestServer::HandleServerMessage(ObservedEvt& event) { // Cancel existing dialog and close temp file wxQueueEvent(m_pin_dialog, new wxCloseEvent); if (!m_upload_path.empty() && m_ul_stream.is_open()) m_ul_stream.close(); - - // Io thread might be waiting for return_status on notify_one() - UpdateReturnStatus(RestServerResult::GenericError); break; } From efb87552ab51161a360efde5656bfebc76efc932 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Mon, 4 Dec 2023 21:41:39 +0100 Subject: [PATCH 43/63] peer_client: bugfix --- src/peer_client.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/peer_client.cpp b/src/peer_client.cpp index fc7d4f0274..74997550b0 100644 --- a/src/peer_client.cpp +++ b/src/peer_client.cpp @@ -343,9 +343,9 @@ int SendNavobjects(std::string dest_ip_address, std::string server_name, unsigned int dPIN = atoi(PIN_tentative.ToStdString().c_str()); Pincode pincode(dPIN); std::string api_key = pincode.Hash(); - SemanticVersion v = GetApiVersion(dest_ip_address); RestServerResult result; - if (v.major >= 9) { + SemanticVersion v = GetApiVersion(dest_ip_address); + if (v >= SemanticVersion(5, 9)) { result = CheckApiKey(g_hostname.ToStdString(), api_key, dest_ip_address); } else { From be41fbd79ebe3771b665ba7152d8cb37915a0afe Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Tue, 5 Dec 2023 00:01:38 +0100 Subject: [PATCH 44/63] rest-server: Store old-style/new-style hashs depending on peer version. --- src/rest_server.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rest_server.cpp b/src/rest_server.cpp index 66e75439e9..784a2f9aa0 100644 --- a/src/rest_server.cpp +++ b/src/rest_server.cpp @@ -336,6 +336,9 @@ bool RestServer::CheckApiKey(const RestIoEvtData& evt_data) { // Need a new PIN confirmation, add it to map and persist m_pincode = Pincode::Create(); std::string new_api_key = m_pincode.Hash(); + if (evt_data.api_key.size() < 10) // client sends old-style keys + new_api_key = m_pincode.CompatHash(); + m_key_map[evt_data.source] = new_api_key; SaveConfig(); From a4c00510f1d2dd3c30773d51025429ec9e62f5f1 Mon Sep 17 00:00:00 2001 From: Hakan Date: Tue, 5 Dec 2023 08:55:26 +0100 Subject: [PATCH 45/63] Inscribe a W in the Meteo symbol --- src/ais.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/ais.cpp b/src/ais.cpp index 998184bb2b..529eb1ac5c 100644 --- a/src/ais.cpp +++ b/src/ais.cpp @@ -1262,12 +1262,27 @@ static void AISDrawTarget(AisTargetData *td, ocpnDC &dc, ViewPort &vp, dc.SetPen(wxPen(UBLCK, 1)); } - } else if (td->Class == AIS_METEO) { - wxPen target_pen(UBLCK, 2); - dc.SetPen(target_pen); + } else if (td->Class == AIS_METEO) { // Meteorologic + wxPen met(UBLCK,(wxMax(target_outline_pen.GetWidth(), 2.5))); + dc.SetPen(met); dc.SetBrush(wxBrush(UBLCK, wxBRUSHSTYLE_TRANSPARENT)); - dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1.8 * AIS_icon_diameter); - dc.StrokeCircle(TargetPoint.x, TargetPoint.y, 1); + double met_radius = 1.8 * AIS_icon_diameter; + dc.StrokeCircle(TargetPoint.x, TargetPoint.y, met_radius); + + /* Inscribed "W" in the circle. */ + dc.SetPen(wxPen(wxMax(target_outline_pen.GetWidth(), 1))); + //Left part + dc.StrokeLine(TargetPoint.x, TargetPoint.y - met_radius / 4, + TargetPoint.x - met_radius / 3, TargetPoint.y + met_radius / 2); + dc.StrokeLine( + TargetPoint.x - met_radius / 3, TargetPoint.y + met_radius / 2, + TargetPoint.x - met_radius / 2, TargetPoint.y - met_radius / 2); + // Right part + dc.StrokeLine(TargetPoint.x, TargetPoint.y - met_radius / 4, + TargetPoint.x + met_radius / 3, TargetPoint.y + met_radius / 2); + dc.StrokeLine( + TargetPoint.x + met_radius / 3, TargetPoint.y + met_radius / 2, + TargetPoint.x + met_radius / 2, TargetPoint.y - met_radius / 2); } else if (td->Class == AIS_ATON) { // Aid to Navigation AtoN_Diamond(dc, TargetPoint.x, TargetPoint.y, From cac7487d900ac258d64389ca1cd0a90be5236456 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 8 Dec 2023 15:50:09 -0500 Subject: [PATCH 46/63] Revert "Revert "plugin_loader: Fix bad typo leading to crash."" This reverts commit dc926a6e9ac8a525373a32e0fb425c4afdf6b7e6. --- src/plugin_loader.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/plugin_loader.cpp b/src/plugin_loader.cpp index 5735b83224..eca06954f3 100644 --- a/src/plugin_loader.cpp +++ b/src/plugin_loader.cpp @@ -324,8 +324,7 @@ void PluginLoader::NotifySetupOptionsPlugin(const PlugInData* pd) { case 117: case 118: { if (pic->m_pplugin) { - opencpn_plugin_19 *ppi = - dynamic_cast(pic->m_pplugin); + auto ppi = dynamic_cast(pic->m_pplugin); if (ppi) { ppi->OnSetupOptions(); auto loader = PluginLoader::getInstance(); From 999ab9c1bbaf05ff1fdf917345f8e803685425f4 Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 8 Dec 2023 23:31:54 -0500 Subject: [PATCH 47/63] Correct plugin API version cast logic. --- src/plugin_loader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin_loader.cpp b/src/plugin_loader.cpp index eca06954f3..90a54d563c 100644 --- a/src/plugin_loader.cpp +++ b/src/plugin_loader.cpp @@ -324,7 +324,7 @@ void PluginLoader::NotifySetupOptionsPlugin(const PlugInData* pd) { case 117: case 118: { if (pic->m_pplugin) { - auto ppi = dynamic_cast(pic->m_pplugin); + auto ppi = dynamic_cast(pic->m_pplugin); if (ppi) { ppi->OnSetupOptions(); auto loader = PluginLoader::getInstance(); From 4fe554f729f65359567506d460f9a4037550e16a Mon Sep 17 00:00:00 2001 From: Hakan Date: Fri, 8 Dec 2023 16:53:15 +0100 Subject: [PATCH 48/63] AIS_Meteo: Don't obscure prioritized targets --- src/AISTargetListDialog.cpp | 8 ++++---- src/OCPNListCtrl.cpp | 6 +++--- src/ais_decoder.cpp | 6 ++++++ src/ais_target_data.cpp | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/AISTargetListDialog.cpp b/src/AISTargetListDialog.cpp index d744d48fd4..9a87f65931 100644 --- a/src/AISTargetListDialog.cpp +++ b/src/AISTargetListDialog.cpp @@ -196,7 +196,7 @@ static int ItemCompare(AisTargetData *pAISTarget1, case tlCOG: { if ((t1->COG >= 360.0) || (t1->Class == AIS_ATON) || - (t1->Class == AIS_BASE)) + (t1->Class == AIS_BASE) || (t1->Class == AIS_METEO)) n1 = -1.0; else { int crs = wxRound(t1->COG); @@ -207,7 +207,7 @@ static int ItemCompare(AisTargetData *pAISTarget1, } if ((t2->COG >= 360.0) || (t2->Class == AIS_ATON) || - (t2->Class == AIS_BASE)) + (t2->Class == AIS_BASE) || (t2->Class == AIS_METEO)) n2 = -1.0; else { int crs = wxRound(t2->COG); @@ -223,13 +223,13 @@ static int ItemCompare(AisTargetData *pAISTarget1, case tlSOG: { if ((t1->SOG > 100.) || (t1->Class == AIS_ATON) || - (t1->Class == AIS_BASE)) + (t1->Class == AIS_BASE) || (t1->Class == AIS_METEO)) n1 = -1.0; else n1 = t1->SOG; if ((t2->SOG > 100.) || (t2->Class == AIS_ATON) || - (t2->Class == AIS_BASE)) + (t2->Class == AIS_BASE) || (t2->Class == AIS_METEO)) n2 = -1.0; else n2 = t2->SOG; diff --git a/src/OCPNListCtrl.cpp b/src/OCPNListCtrl.cpp index e60a8eaa69..cbb1b51d09 100644 --- a/src/OCPNListCtrl.cpp +++ b/src/OCPNListCtrl.cpp @@ -184,9 +184,9 @@ wxString OCPNListCtrl::GetTargetColumnData(AisTargetData* pAISTarget, } case tlSOG: { - if (((pAISTarget->SOG > 100.) && - !pAISTarget->b_SarAircraftPosnReport) || - (pAISTarget->Class == AIS_ATON) || (pAISTarget->Class == AIS_BASE)) + if (((pAISTarget->SOG > 100.) && !pAISTarget->b_SarAircraftPosnReport) || + (pAISTarget->Class == AIS_ATON) || (pAISTarget->Class == AIS_BASE) || + (pAISTarget->Class == AIS_METEO)) ret = _("-"); else ret.Printf(_T("%5.1f"), toUsrSpeed(pAISTarget->SOG)); diff --git a/src/ais_decoder.cpp b/src/ais_decoder.cpp index b3fb22b627..b7fcd82428 100644 --- a/src/ais_decoder.cpp +++ b/src/ais_decoder.cpp @@ -3338,6 +3338,9 @@ bool AisDecoder::Parse_VDXBitstring(AisBitstring *bstr, ptd->met_data.ice = bstr->GetInt(349, 2); ptd->Class = AIS_METEO; + ptd->COG = -1.; + ptd->HDG = 511; + ptd->SOG = -1.; ptd->b_NoTrack = true; ptd->b_show_track = false; ptd->b_positionDoubtful = false; @@ -3491,6 +3494,9 @@ bool AisDecoder::Parse_VDXBitstring(AisBitstring *bstr, if (ptd->b_positionOnceValid) { ptd->Class = AIS_METEO; + ptd->COG = -1.; + ptd->HDG = 511; + ptd->SOG = -1.; ptd->b_NoTrack = true; ptd->b_show_track = false; ptd->b_positionDoubtful = false; diff --git a/src/ais_target_data.cpp b/src/ais_target_data.cpp index 435a1e9805..23eb951150 100644 --- a/src/ais_target_data.cpp +++ b/src/ais_target_data.cpp @@ -1132,7 +1132,7 @@ wxString AisTargetData::GetRolloverString(void) { } if (g_bAISRolloverShowCOG && ((SOG <= 102.2) || b_SarAircraftPosnReport) && - ((Class != AIS_ATON) && (Class != AIS_BASE))) { + !((Class == AIS_ATON) || (Class == AIS_BASE) || (Class == AIS_METEO))) { if (result.Len()) result << _T("\n"); double speed_show = toUsrSpeed(SOG); From 6860c49a2edf02064a9a1bce2f3df457b9d75e91 Mon Sep 17 00:00:00 2001 From: Dave Date: Sat, 9 Dec 2023 22:21:47 -0500 Subject: [PATCH 49/63] Remove unnecessary call to ReloadPluginPanels() --- src/pluginmanager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pluginmanager.cpp b/src/pluginmanager.cpp index 5c50b14db0..009c731ef2 100644 --- a/src/pluginmanager.cpp +++ b/src/pluginmanager.cpp @@ -3973,7 +3973,6 @@ void CatalogMgrPanel::OnTarballButton(wxCommandEvent& event) { } LoadAllPlugIns(false); PluginHandler::getInstance()->SetInstalledMetadata(metadata); - m_PluginListPanel->ReloadPluginPanels(); wxString ws(_("Plugin")); ws += metadata.name + _(" successfully imported"); OCPNMessageBox(gFrame, ws, _("Installation complete"), From ef2f98aee9417fa9d2caa1d0f9000bacfa2b5f55 Mon Sep 17 00:00:00 2001 From: Pavel Kalian Date: Sun, 10 Dec 2023 11:30:32 -0300 Subject: [PATCH 50/63] Update Windows build dependencies to use wx 3.2.4 --- buildwin/win_deps.bat | 16 ++++++++-------- test/CMakeLists.txt | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/buildwin/win_deps.bat b/buildwin/win_deps.bat index 9652175b43..0a7dbd7a5a 100644 --- a/buildwin/win_deps.bat +++ b/buildwin/win_deps.bat @@ -24,22 +24,22 @@ wget --version >nul 2>&1 || choco install -y wget set "CACHE_DIR=%~dp0..\cache" if not exist !CACHE_DIR! (mkdir !CACHE_DIR!) set "GITHUB_DL=https://github.com/wxWidgets/wxWidgets/releases/download" -if not exist cache\wxWidgets-3.2.3 ( +if not exist cache\wxWidgets-3.2.4 ( :: wget -nv %GITHUB_DL%/v3.2.1/wxMSW-3.2.1_vc14x_Dev.7z :: 7z x -y -o%CACHE_DIR%\wxWidgets-3.2.1 wxMSW-3.2.1_vc14x_Dev.7z :: wget -nv %GITHUB_DL%/v3.2.1/wxWidgets-3.2.1-headers.7z :: 7z x -y -o%CACHE_DIR%\wxWidgets-3.2.1 wxWidgets-3.2.1-headers.7z :: wget -nv %GITHUB_DL%/v3.2.1/wxMSW-3.2.1_vc14x_ReleaseDLL.7z :: 7z x -y -o%CACHE_DIR%\wxWidgets-3.2.1 wxMSW-3.2.1_vc14x_ReleaseDLL.7z - wget -nv %GITHUB_DL%/v3.2.3/wxMSW-3.2.3_vc14x_Dev.7z - 7z x -y -o%CACHE_DIR%\wxWidgets-3.2.3 wxMSW-3.2.3_vc14x_Dev.7z - wget -nv %GITHUB_DL%/v3.2.3/wxWidgets-3.2.3-headers.7z - 7z x -y -o%CACHE_DIR%\wxWidgets-3.2.3 wxWidgets-3.2.3-headers.7z - wget -nv %GITHUB_DL%/v3.2.3/wxMSW-3.2.3_vc14x_ReleaseDLL.7z - 7z x -y -o%CACHE_DIR%\wxWidgets-3.2.3 wxMSW-3.2.3_vc14x_ReleaseDLL.7z + wget -nv %GITHUB_DL%/v3.2.4/wxMSW-3.2.4_vc14x_Dev.7z + 7z x -y -o%CACHE_DIR%\wxWidgets-3.2.4 wxMSW-3.2.4_vc14x_Dev.7z + wget -nv %GITHUB_DL%/v3.2.4/wxWidgets-3.2.4-headers.7z + 7z x -y -o%CACHE_DIR%\wxWidgets-3.2.4 wxWidgets-3.2.4-headers.7z + wget -nv %GITHUB_DL%/v3.2.4/wxMSW-3.2.4_vc14x_ReleaseDLL.7z + 7z x -y -o%CACHE_DIR%\wxWidgets-3.2.4 wxMSW-3.2.4_vc14x_ReleaseDLL.7z ) :: Create cache\wx-config.bat, paths to downloaded wxWidgets. -set "WXWIN=!CACHE_DIR!\wxWidgets-3.2.3" +set "WXWIN=!CACHE_DIR!\wxWidgets-3.2.4" echo set "wxWidgets_ROOT_DIR=%WXWIN%" > %CACHE_DIR%\wx-config.bat echo set "wxWidgets_LIB_DIR=%WXWIN%\lib\vc14x_dll" >> %CACHE_DIR%\wx-config.bat diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c06e1f84b9..6874f86372 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -172,7 +172,7 @@ if (MSVC) set(_script "file(WRITE ${_bat_path} \" PATH=%PATH%\;${_cache_dir}\\\\buildwin -PATH=%PATH%\;${_cache_dir}\\\\wxWidgets-3.2.3\\\\lib\\\\vc14x_dll\") +PATH=%PATH%\;${_cache_dir}\\\\wxWidgets-3.2.4\\\\lib\\\\vc14x_dll\") ") file(WRITE ${_script_path} ${_script}) add_custom_target(win-path-setup From bf32a17b5e55a89e6cda1027692b20c1f530f21e Mon Sep 17 00:00:00 2001 From: Dave Date: Sun, 10 Dec 2023 11:33:49 -0500 Subject: [PATCH 51/63] Protect ReloadPluginPanels() method from recursion using atomic_flag. --- include/pluginmanager.h | 4 ++-- src/pluginmanager.cpp | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/include/pluginmanager.h b/include/pluginmanager.h index 52daa3c654..7eacc27b51 100644 --- a/include/pluginmanager.h +++ b/include/pluginmanager.h @@ -32,11 +32,10 @@ #include #include - +#include #include "config.h" #include "ocpn_plugin.h" -// #include "chcanv.h" // for ViewPort #include "OCPN_Sound.h" #include "chartimg.h" #include "catalog_parser.h" @@ -439,6 +438,7 @@ class PluginListPanel : public wxScrolledWindow { PluginPanel* m_PluginSelected; wxString m_selectedName; int m_pluginSpacer; + std::atomic_flag m_is_loading; //!< recursive lock. }; /** Invokes client browser on plugin info_url when clicked. */ diff --git a/src/pluginmanager.cpp b/src/pluginmanager.cpp index 009c731ef2..1703fb762e 100644 --- a/src/pluginmanager.cpp +++ b/src/pluginmanager.cpp @@ -3973,6 +3973,7 @@ void CatalogMgrPanel::OnTarballButton(wxCommandEvent& event) { } LoadAllPlugIns(false); PluginHandler::getInstance()->SetInstalledMetadata(metadata); + m_PluginListPanel->ReloadPluginPanels(); wxString ws(_("Plugin")); ws += metadata.name + _(" successfully imported"); OCPNMessageBox(gFrame, ws, _("Installation complete"), @@ -4031,7 +4032,8 @@ END_EVENT_TABLE() PluginListPanel::PluginListPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size) : wxScrolledWindow(parent, id, pos, size, wxTAB_TRAVERSAL | wxVSCROLL), - m_PluginSelected(0) { + m_PluginSelected(0), + m_is_loading(ATOMIC_FLAG_INIT){ SetSizer(new wxBoxSizer(wxVERTICAL)); ReloadPluginPanels(); } @@ -4080,6 +4082,13 @@ static bool IsPluginLoaded(const std::string& name) { } void PluginListPanel::ReloadPluginPanels() { + if (m_is_loading.test_and_set()) { + // recursive call... + m_is_loading.clear(); + DEBUG_LOG << "LoadAllPlugins: recursive invocation"; + return; + } + auto plugins = PluginLoader::getInstance()->GetPlugInArray(); m_PluginItems.Clear(); @@ -4090,7 +4099,6 @@ void PluginListPanel::ReloadPluginPanels() { PluginPanel* pp = dynamic_cast(win); if (pp) win->Destroy(); } - GetSizer()->Clear(); Hide(); @@ -4122,8 +4130,9 @@ void PluginListPanel::ReloadPluginPanels() { Show(); Layout(); Refresh(true); - Scroll(0, 0); + + m_is_loading.clear(); } void PluginListPanel::AddPlugin(const PlugInData& pic) { From 35a8586d9711822a9bd1c29ee235f7a9a7433849 Mon Sep 17 00:00:00 2001 From: Dave Date: Sun, 10 Dec 2023 11:53:32 -0500 Subject: [PATCH 52/63] Further protect ReloadPluginPanels() against multiple re-entries. --- src/pluginmanager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pluginmanager.cpp b/src/pluginmanager.cpp index 1703fb762e..e4facd3a73 100644 --- a/src/pluginmanager.cpp +++ b/src/pluginmanager.cpp @@ -4084,7 +4084,6 @@ static bool IsPluginLoaded(const std::string& name) { void PluginListPanel::ReloadPluginPanels() { if (m_is_loading.test_and_set()) { // recursive call... - m_is_loading.clear(); DEBUG_LOG << "LoadAllPlugins: recursive invocation"; return; } From 3849a4f8e705d8fad9e1fff87053cc1d9bffbe2b Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sun, 10 Dec 2023 21:42:24 +0100 Subject: [PATCH 53/63] pluginmanager: Fix windows std::atomic_flag handling. --- src/pluginmanager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pluginmanager.cpp b/src/pluginmanager.cpp index e4facd3a73..d871d08e85 100644 --- a/src/pluginmanager.cpp +++ b/src/pluginmanager.cpp @@ -4032,8 +4032,8 @@ END_EVENT_TABLE() PluginListPanel::PluginListPanel(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size) : wxScrolledWindow(parent, id, pos, size, wxTAB_TRAVERSAL | wxVSCROLL), - m_PluginSelected(0), - m_is_loading(ATOMIC_FLAG_INIT){ + m_PluginSelected(0) { + m_is_loading.clear(); SetSizer(new wxBoxSizer(wxVERTICAL)); ReloadPluginPanels(); } From a0f8b48b9a6261cf62afe5db46becfca50c75841 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sun, 10 Dec 2023 09:39:10 +0100 Subject: [PATCH 54/63] tests: Add missing global --- test/tests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/tests.cpp b/test/tests.cpp index 58a68c6ee7..d56f02605d 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -28,6 +28,7 @@ #include "ocpn_types.h" #include "ocpn_plugin.h" #include "own_ship.h" +#include "S57ClassRegistrar.h" #include "routeman.h" #include "select.h" @@ -80,6 +81,7 @@ void* g_pi_manager = reinterpret_cast(1L); wxString g_compatOS = PKG_TARGET; wxString g_compatOsVersion = PKG_TARGET_VERSION; +S57ClassRegistrar *g_poRegistrar; Select* pSelect; double g_n_arrival_circle_radius; double g_PlanSpeed; From 4b781af7a642af4a2665770b40eb2033bcea58b0 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Fri, 8 Dec 2023 14:32:36 +0100 Subject: [PATCH 55/63] comm_n0183_output: Fix annoying compiler warning --- src/comm_n0183_output.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/comm_n0183_output.cpp b/src/comm_n0183_output.cpp index 48411f2acd..74bc0bc613 100644 --- a/src/comm_n0183_output.cpp +++ b/src/comm_n0183_output.cpp @@ -440,7 +440,7 @@ int SendRouteToGPS_N0183(Route* pr, const wxString& com_name, // structures // Retry 5 times, 1 sec cycle int n_try = 5; - int v_init; + int v_init = 0; while (n_try) { v_init = Garmin_GPS_Init(short_com); if (v_init >= 0) break; @@ -963,7 +963,7 @@ int SendWaypointToGPS_N0183(RoutePoint* prp, const wxString& com_name, // structures // Retry 5 times, 1 sec cycle int n_try = 5; - int v_init; + int v_init = 0; while (n_try) { v_init = Garmin_GPS_Init(short_com); if (v_init >= 0) break; From 7d8123485fceaf5614a746957ce1307141160928 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sat, 9 Dec 2023 22:37:58 +0100 Subject: [PATCH 56/63] s52plib: Clean up some dependencies on main opencpn (#3527) --- libs/s52plib/CMakeLists.txt | 13 +- {include => libs/s52plib/src}/color_types.h | 0 libs/s52plib/src/ocpn_plugin.h | 1838 +++++++++++++++++++ 3 files changed, 1845 insertions(+), 6 deletions(-) rename {include => libs/s52plib/src}/color_types.h (100%) create mode 100644 libs/s52plib/src/ocpn_plugin.h diff --git a/libs/s52plib/CMakeLists.txt b/libs/s52plib/CMakeLists.txt index 49cbdc1b3d..0da9a1c627 100644 --- a/libs/s52plib/CMakeLists.txt +++ b/libs/s52plib/CMakeLists.txt @@ -18,7 +18,8 @@ SET(SRC src/DepthFont.cpp src/mygeom.cpp src/Cs52_shaders.cpp - ) + src/color_types.h +) if (NOT wxWidgets_INCLUDE_DIRS) include(SimpleWxConfig) @@ -30,11 +31,11 @@ add_library(ocpn::s52plib ALIAS S52PLIB) set_property(TARGET S52PLIB PROPERTY COMPILE_FLAGS "${OBJ_VISIBILITY}") target_include_directories(S52PLIB PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) -target_include_directories(S52PLIB PRIVATE ${wxWidgets_INCLUDE_DIRS}) -target_include_directories(S52PLIB PRIVATE ../geoprim/src) -target_include_directories(S52PLIB PRIVATE ../pugixml) target_include_directories(S52PLIB PRIVATE ../../include) -target_include_directories(S52PLIB PRIVATE ../gdal/include) -target_include_directories(S52PLIB PRIVATE ../libtess2/Include) target_include_directories(S52PLIB PRIVATE ${CMAKE_BINARY_DIR}/include) +target_include_directories(S52PLIB PRIVATE ${wxWidgets_INCLUDE_DIRS}) +target_link_libraries(S52PLIB PRIVATE ocpn::geoprim) +target_link_libraries(S52PLIB PRIVATE ocpn::pugixml) +target_link_libraries(S52PLIB PRIVATE ocpn::gdal) +target_link_libraries(S52PLIB PRIVATE ocpn::tess2) diff --git a/include/color_types.h b/libs/s52plib/src/color_types.h similarity index 100% rename from include/color_types.h rename to libs/s52plib/src/color_types.h diff --git a/libs/s52plib/src/ocpn_plugin.h b/libs/s52plib/src/ocpn_plugin.h new file mode 100644 index 0000000000..6847dbe4e6 --- /dev/null +++ b/libs/s52plib/src/ocpn_plugin.h @@ -0,0 +1,1838 @@ +/*************************************************************************** + * + * Project: OpenCPN + * Purpose: PlugIn Object Definition/API + * Author: David Register + * + *************************************************************************** + * Copyright (C) 2010 by David S. Register * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + **************************************************************************/ + +#ifndef _PLUGIN_H_ +#define _PLUGIN_H_ + +#ifndef DECL_EXP +#if defined(__WXMSW__) || defined(__CYGWIN__) +#define DECL_EXP __declspec(dllexport) +#elif defined __GNUC__ && __GNUC__ >= 4 +#define DECL_EXP __attribute__((visibility("default"))) +#elif defined __WXOSX__ +#define DECL_EXP __attribute__((visibility("default"))) +#else +#define DECL_EXP +#endif +#endif + +#if defined(__WXMSW__) && defined(MAKING_PLUGIN) +#define DECL_IMP __declspec(dllimport) +#else +#define DECL_IMP +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef ocpnUSE_SVG +#include +#endif // ocpnUSE_SVG + +#include +#include +#include + +class wxGLContext; + +// This is the most modern API Version number +// It is expected that the API will remain downward compatible, meaning that +// PlugIns conforming to API Version less then the most modern will also +// be correctly supported. +#define API_VERSION_MAJOR 1 +#define API_VERSION_MINOR 18 + +// Fwd Definitions +class wxFileConfig; +class wxNotebook; +class wxFont; +class wxAuiManager; +class wxScrolledWindow; +class wxGLCanvas; + +//--------------------------------------------------------------------------------------------------------- +// +// Bitfield PlugIn Capabilites flag definition +// +//--------------------------------------------------------------------------------------------------------- +#define WANTS_OVERLAY_CALLBACK 0x00000001 +#define WANTS_CURSOR_LATLON 0x00000002 +#define WANTS_TOOLBAR_CALLBACK 0x00000004 +#define INSTALLS_TOOLBAR_TOOL 0x00000008 +#define WANTS_CONFIG 0x00000010 +#define INSTALLS_TOOLBOX_PAGE 0x00000020 +#define INSTALLS_CONTEXTMENU_ITEMS 0x00000040 +#define WANTS_NMEA_SENTENCES 0x00000080 +#define WANTS_NMEA_EVENTS 0x00000100 +#define WANTS_AIS_SENTENCES 0x00000200 +#define USES_AUI_MANAGER 0x00000400 +#define WANTS_PREFERENCES 0x00000800 +#define INSTALLS_PLUGIN_CHART 0x00001000 +#define WANTS_ONPAINT_VIEWPORT 0x00002000 +#define WANTS_PLUGIN_MESSAGING 0x00004000 +#define WANTS_OPENGL_OVERLAY_CALLBACK 0x00008000 +#define WANTS_DYNAMIC_OPENGL_OVERLAY_CALLBACK 0x00010000 +#define WANTS_LATE_INIT 0x00020000 +#define INSTALLS_PLUGIN_CHART_GL 0x00040000 +#define WANTS_MOUSE_EVENTS 0x00080000 +#define WANTS_VECTOR_CHART_OBJECT_INFO 0x00100000 +#define WANTS_KEYBOARD_EVENTS 0x00200000 + +//--------------------------------------------------------------------------------------------------------- +// +// Overlay priorities +// +//--------------------------------------------------------------------------------------------------------- +#define OVERLAY_LEGACY 0 +#define OVERLAY_OVER_SHIPS 64 +#define OVERLAY_OVER_EMBOSS 96 +#define OVERLAY_OVER_UI 128 + +//---------------------------------------------------------------------------------------------------------- +// Some PlugIn API interface object class definitions +//---------------------------------------------------------------------------------------------------------- +enum PI_ColorScheme { + PI_GLOBAL_COLOR_SCHEME_RGB, + PI_GLOBAL_COLOR_SCHEME_DAY, + PI_GLOBAL_COLOR_SCHEME_DUSK, + PI_GLOBAL_COLOR_SCHEME_NIGHT, + PI_N_COLOR_SCHEMES +}; + +class PlugIn_ViewPort { +public: + double clat; // center point + double clon; + double view_scale_ppm; + double skew; + double rotation; + + float chart_scale; // conventional chart displayed scale + + int pix_width; + int pix_height; + wxRect rv_rect; + bool b_quilt; + int m_projection_type; + + double lat_min, lat_max, lon_min, lon_max; + + bool bValid; // This VP is valid +}; + +class PlugIn_Position_Fix { +public: + double Lat; + double Lon; + double Cog; + double Sog; + double Var; // Variation, typically from RMC message + time_t FixTime; + int nSats; +}; + +class PlugIn_Position_Fix_Ex { +public: + double Lat; + double Lon; + double Cog; + double Sog; + double Var; // Variation, typically from RMC message + double Hdm; + double Hdt; + time_t FixTime; + int nSats; +}; + +class Plugin_Active_Leg_Info { +public: + double Xte; // Left side of the track -> negative XTE + double Btw; + double Dtw; + wxString wp_name; // Name of destination waypoint for active leg + bool arrival; // True when within arrival circle +}; + +// Describe AIS Alarm state +enum plugin_ais_alarm_type { + PI_AIS_NO_ALARM = 0, + PI_AIS_ALARM_SET, + PI_AIS_ALARM_ACKNOWLEDGED + +}; + +class PlugIn_AIS_Target { +public: + int MMSI; + int Class; + int NavStatus; + double SOG; + double COG; + double HDG; + double Lon; + double Lat; + int ROTAIS; + char CallSign[8]; // includes terminator + char ShipName[21]; + unsigned char ShipType; + int IMO; + + double Range_NM; + double Brg; + + // Per target collision parameters + bool bCPA_Valid; + double TCPA; // Minutes + double CPA; // Nautical Miles + + plugin_ais_alarm_type alarm_state; +}; + +// ChartType constants +typedef enum ChartTypeEnumPI { + PI_CHART_TYPE_UNKNOWN = 0, + PI_CHART_TYPE_DUMMY, + PI_CHART_TYPE_DONTCARE, + PI_CHART_TYPE_KAP, + PI_CHART_TYPE_GEO, + PI_CHART_TYPE_S57, + PI_CHART_TYPE_CM93, + PI_CHART_TYPE_CM93COMP, + PI_CHART_TYPE_PLUGIN +} _ChartTypeEnumPI; + +// ChartFamily constants +typedef enum ChartFamilyEnumPI { + PI_CHART_FAMILY_UNKNOWN = 0, + PI_CHART_FAMILY_RASTER, + PI_CHART_FAMILY_VECTOR, + PI_CHART_FAMILY_DONTCARE +} _ChartFamilyEnumPI; + +// Depth unit type enum +typedef enum ChartDepthUnitTypePI { + PI_DEPTH_UNIT_UNKNOWN, + PI_DEPTH_UNIT_FEET, + PI_DEPTH_UNIT_METERS, + PI_DEPTH_UNIT_FATHOMS +} _ChartDepthUnitTypePI; + +// Projection type enum +typedef enum OcpnProjTypePI { + PI_PROJECTION_UNKNOWN, + PI_PROJECTION_MERCATOR, + PI_PROJECTION_TRANSVERSE_MERCATOR, + PI_PROJECTION_POLYCONIC, + + PI_PROJECTION_ORTHOGRAPHIC, + PI_PROJECTION_POLAR, + PI_PROJECTION_STEREOGRAPHIC, + PI_PROJECTION_GNOMONIC, + PI_PROJECTION_EQUIRECTANGULAR +} _OcpnProjTypePI; + +typedef struct _ExtentPI { + double SLAT; + double WLON; + double NLAT; + double ELON; +} ExtentPI; + +// PlugInChartBase::Init() init_flags constants +#define PI_FULL_INIT 0 +#define PI_HEADER_ONLY 1 +#define PI_THUMB_ONLY 2 + +// ---------------------------------------------------------------------------- +// PlugInChartBase +// This class is the base class for Plug-able chart types +// ---------------------------------------------------------------------------- + +class DECL_EXP PlugInChartBase : public wxObject { +public: + // These methods Must be overriden in any derived class + PlugInChartBase(); + virtual ~PlugInChartBase(); + + virtual wxString GetFileSearchMask(void); + + virtual int Init(const wxString &full_path, int init_flags); + virtual void SetColorScheme(int cs, bool bApplyImmediate); + + virtual double GetNormalScaleMin(double canvas_scale_factor, + bool b_allow_overzoom); + virtual double GetNormalScaleMax(double canvas_scale_factor, + int canvas_width); + virtual double GetNearestPreferredScalePPM(double target_scale_ppm); + + virtual bool GetChartExtent(ExtentPI *pext); + + virtual wxBitmap &RenderRegionView(const PlugIn_ViewPort &VPoint, + const wxRegion &Region); + + virtual bool AdjustVP(PlugIn_ViewPort &vp_last, PlugIn_ViewPort &vp_proposed); + + virtual void GetValidCanvasRegion(const PlugIn_ViewPort &VPoint, + wxRegion *pValidRegion); + + virtual int GetCOVREntries() { return 0; } + virtual int GetCOVRTablePoints(int iTable) { return 0; } + virtual int GetCOVRTablenPoints(int iTable) { return 0; } + virtual float *GetCOVRTableHead(int iTable) { return (float *)NULL; } + + virtual wxBitmap *GetThumbnail(int tnx, int tny, int cs); + + // Accessors, need not be overridden in derived class if the member + // variables are maintained + virtual wxString GetFullPath() const { return m_FullPath; } + virtual ChartTypeEnumPI GetChartType() { return m_ChartType; } + virtual ChartFamilyEnumPI GetChartFamily() { return m_ChartFamily; } + virtual OcpnProjTypePI GetChartProjection() { return m_projection; } + virtual wxString GetName() { return m_Name; } + virtual wxString GetDescription() { return m_Description; } + virtual wxString GetID() { return m_ID; } + virtual wxString GetSE() { return m_SE; } + virtual wxString GetDepthUnits() { return m_DepthUnits; } + virtual wxString GetSoundingsDatum() { return m_SoundingsDatum; } + virtual wxString GetDatumString() { return m_datum_str; } + virtual wxString GetExtraInfo() { return m_ExtraInfo; } + virtual wxString GetPubDate() { return m_PubYear; } + virtual double GetChartErrorFactor() { return m_Chart_Error_Factor; } + virtual ChartDepthUnitTypePI GetDepthUnitId() { return m_depth_unit_id; } + virtual bool IsReadyToRender() { return m_bReadyToRender; } + virtual int GetNativeScale() { return m_Chart_Scale; }; + virtual double GetChartSkew() { return m_Chart_Skew; } + virtual wxDateTime GetEditionDate(void) { return m_EdDate; } + + // Methods pertaining to CHART_FAMILY_RASTER type PlugIn charts only + virtual void ComputeSourceRectangle(const PlugIn_ViewPort &vp, + wxRect *pSourceRect); + virtual double GetRasterScaleFactor(); + virtual bool GetChartBits(wxRect &source, unsigned char *pPix, int sub_samp); + virtual int GetSize_X(); + virtual int GetSize_Y(); + virtual void latlong_to_chartpix(double lat, double lon, double &pixx, + double &pixy); + virtual void chartpix_to_latlong(double pixx, double pixy, double *plat, + double *plon); + +protected: + ChartTypeEnumPI m_ChartType; + ChartFamilyEnumPI m_ChartFamily; + + wxString m_FullPath; + OcpnProjTypePI m_projection; + int m_Chart_Scale; + double m_Chart_Skew; + + wxDateTime m_EdDate; + bool m_bReadyToRender; + + wxString m_Name; + wxString m_Description; + wxString m_ID; + wxString m_SE; + wxString m_SoundingsDatum; + wxString m_datum_str; + wxString m_PubYear; + wxString m_DepthUnits; + wxString m_ExtraInfo; + + ChartDepthUnitTypePI m_depth_unit_id; + + double m_Chart_Error_Factor; +}; + +// Declare an array of PlugIn_AIS_Targets +WX_DEFINE_ARRAY_PTR(PlugIn_AIS_Target *, ArrayOfPlugIn_AIS_Targets); + +//---------------------------------------------------------------------------------------------------------- +// The Generic PlugIn Interface Class Definition +// +// This is a virtual class. +// opencpn PlugIns must derive from this class. +// There are two types of methods in this class +// a. Required...must be overridden and implemented by PlugIns +// b. Optional..may be overridden by PlugIns + +// PlugIns must implement optional method overrides consistent with their +// declared capabilities flag as returned by Init(). +//---------------------------------------------------------------------------------------------------------- +class DECL_EXP opencpn_plugin { +public: + opencpn_plugin(void *pmgr) {} + virtual ~opencpn_plugin(); + + // Public API to the PlugIn class + + // This group of methods is required, and will be called by the opencpn + // host opencpn PlugIns must implement this group + virtual int Init(void); // Return the PlugIn Capabilites flag + virtual bool DeInit(void); + + virtual int GetAPIVersionMajor(); + virtual int GetAPIVersionMinor(); + virtual int GetPlugInVersionMajor(); + virtual int GetPlugInVersionMinor(); + virtual wxBitmap *GetPlugInBitmap(); + + // These three methods should produce valid, meaningful strings always + // ---EVEN IF--- the PlugIn has not (yet) been initialized. + // They are used by the PlugInManager GUI + virtual wxString GetCommonName(); + virtual wxString GetShortDescription(); + virtual wxString GetLongDescription(); + + // This group is optional. + // PlugIns may override any of these methods as required + + virtual void SetDefaults( + void); // This will be called upon enabling a PlugIn via the user Dialog + // It gives a chance to setup any default options and behavior + + virtual int GetToolbarToolCount(void); + + virtual int GetToolboxPanelCount(void); + virtual void SetupToolboxPanel(int page_sel, wxNotebook *pnotebook); + virtual void OnCloseToolboxPanel(int page_sel, int ok_apply_cancel); + + virtual void ShowPreferencesDialog(wxWindow *parent); + + virtual bool RenderOverlay(wxMemoryDC *pmdc, PlugIn_ViewPort *vp); + virtual void SetCursorLatLon(double lat, double lon); + virtual void SetCurrentViewPort(PlugIn_ViewPort &vp); + + virtual void SetPositionFix(PlugIn_Position_Fix &pfix); + virtual void SetNMEASentence(wxString &sentence); + virtual void SetAISSentence(wxString &sentence); + + virtual void ProcessParentResize(int x, int y); + virtual void SetColorScheme(PI_ColorScheme cs); + + virtual void OnToolbarToolCallback(int id); + virtual void OnContextMenuItemCallback(int id); + + virtual void UpdateAuiStatus(void); + + virtual wxArrayString GetDynamicChartClassNameArray(void); +}; + +// the types of the class factories used to create PlugIn instances +typedef opencpn_plugin *create_t(void *); +typedef void destroy_t(opencpn_plugin *); + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Woverloaded-virtual" +#endif + +class DECL_EXP opencpn_plugin_16 : public opencpn_plugin { +public: + opencpn_plugin_16(void *pmgr); + virtual ~opencpn_plugin_16(); + + using opencpn_plugin::RenderOverlay; + + virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); + + virtual void SetPluginMessage(wxString &message_id, wxString &message_body); +}; + +class DECL_EXP opencpn_plugin_17 : public opencpn_plugin { +public: + opencpn_plugin_17(void *pmgr); + virtual ~opencpn_plugin_17(); + + using opencpn_plugin::RenderOverlay; + + virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); + virtual bool RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp); + + virtual void SetPluginMessage(wxString &message_id, wxString &message_body); +}; + +class DECL_EXP opencpn_plugin_18 : public opencpn_plugin { +public: + opencpn_plugin_18(void *pmgr); + virtual ~opencpn_plugin_18(); + + using opencpn_plugin::RenderOverlay; + + virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); + virtual bool RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp); + virtual void SetPluginMessage(wxString &message_id, wxString &message_body); + virtual void SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix); +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +class DECL_EXP opencpn_plugin_19 : public opencpn_plugin_18 { +public: + opencpn_plugin_19(void *pmgr); + virtual ~opencpn_plugin_19(); + + virtual void OnSetupOptions(void); +}; + +class DECL_EXP opencpn_plugin_110 : public opencpn_plugin_19 { +public: + opencpn_plugin_110(void *pmgr); + virtual ~opencpn_plugin_110(); + + virtual void LateInit(void); // If WANTS_LATE_INIT is returned by Init() +}; + +class DECL_EXP opencpn_plugin_111 : public opencpn_plugin_110 { +public: + opencpn_plugin_111(void *pmgr); + virtual ~opencpn_plugin_111(); +}; + +class DECL_EXP opencpn_plugin_112 : public opencpn_plugin_111 { +public: + opencpn_plugin_112(void *pmgr); + virtual ~opencpn_plugin_112(); + + virtual bool MouseEventHook(wxMouseEvent &event); + virtual void SendVectorChartObjectInfo(wxString &chart, wxString &feature, + wxString &objname, double lat, + double lon, double scale, + int nativescale); +}; + +class DECL_EXP opencpn_plugin_113 : public opencpn_plugin_112 { +public: + opencpn_plugin_113(void *pmgr); + virtual ~opencpn_plugin_113(); + + virtual bool KeyboardEventHook(wxKeyEvent &event); + virtual void OnToolbarToolDownCallback(int id); + virtual void OnToolbarToolUpCallback(int id); +}; + +class DECL_EXP opencpn_plugin_114 : public opencpn_plugin_113 { +public: + opencpn_plugin_114(void *pmgr); + virtual ~opencpn_plugin_114(); +}; + +class DECL_EXP opencpn_plugin_115 : public opencpn_plugin_114 { +public: + opencpn_plugin_115(void *pmgr); + virtual ~opencpn_plugin_115(); +}; + +class DECL_EXP opencpn_plugin_116 : public opencpn_plugin_115 { +public: + opencpn_plugin_116(void *pmgr); + virtual ~opencpn_plugin_116(); + virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, + PlugIn_ViewPort *vp, int canvasIndex); + virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, + int canvasIndex); + virtual void PrepareContextMenu(int canvasIndex); +}; + +class DECL_EXP opencpn_plugin_117 : public opencpn_plugin_116 { +public: + opencpn_plugin_117(void *pmgr); + /* + * Forms a semantic version together with GetPlugInVersionMajor() and + * GetPlugInVersionMinor(), see https://semver.org/ + */ + virtual int GetPlugInVersionPatch(); + + /** Post-release version part, extends the semver spec. */ + virtual int GetPlugInVersionPost(); + + /** Pre-release tag version part, see GetPlugInVersionPatch() */ + virtual const char *GetPlugInVersionPre(); + + /** Build version part see GetPlugInVersionPatch(). */ + virtual const char *GetPlugInVersionBuild(); + + /*Provide active leg data to plugins*/ + virtual void SetActiveLegInfo(Plugin_Active_Leg_Info &leg_info); +}; + +class DECL_EXP opencpn_plugin_118 : public opencpn_plugin_117 { +public: + opencpn_plugin_118(void *pmgr); + + using opencpn_plugin_116::RenderGLOverlayMultiCanvas; + using opencpn_plugin_116::RenderOverlayMultiCanvas; + + /// Render plugin overlay over chart canvas in OpenGL mode + /// + /// \param pcontext Pointer to the OpenGL context + /// \param vp Pointer to the Viewport + /// \param canvasIndex Index of the chart canvas, 0 for the first canvas + /// \param priority Priority, plugins only upgrading from older API versions + /// should draw only when priority is OVERLAY_LEGACY (0) + /// \return true if overlay was rendered, false otherwise +#ifdef _MSC_VER + virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, + int canvasIndex, int priority = -1); +#else + virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, + int canvasIndex, int priority); + + bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, + int canvas_ix) override { + return RenderGLOverlayMultiCanvas(pcontext, vp, canvas_ix, -1); + } +#endif + + + + /// Render plugin overlay over chart canvas in non-OpenGL mode + /// + /// \param dc Reference to the "device context" + /// \param vp Pointer to the Viewport + /// \param canvasIndex Index of the chart canvas, 0 for the first canvas + /// \param priority Priority, plugins only upgrading from older API versions + /// should draw only when priority is OVERLAY_LEGACY (0) + /// \return true if overlay was rendered, false otherwise +#ifdef _MSC_VER + virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, + int canvasIndex, int priority = -1); +#else + virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, int canvas_ix, + int priority); + bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, + int canvas_ix) override { + return RenderOverlayMultiCanvas(dc, vp, canvas_ix, -1); + } +#endif + +}; +//------------------------------------------------------------------ +// Route and Waypoint PlugIn support +// +//------------------------------------------------------------------ + +class DECL_EXP Plugin_Hyperlink { +public: + wxString DescrText; + wxString Link; + wxString Type; +}; + +WX_DECLARE_LIST(Plugin_Hyperlink, Plugin_HyperlinkList); + +class DECL_EXP PlugIn_Waypoint { +public: + PlugIn_Waypoint(); + PlugIn_Waypoint(double lat, double lon, const wxString &icon_ident, + const wxString &wp_name, const wxString &GUID = _T("")); + ~PlugIn_Waypoint(); + + double m_lat; + double m_lon; + + wxString m_GUID; + + wxString m_MarkName; + wxString m_MarkDescription; + wxDateTime m_CreateTime; + bool m_IsVisible; + + wxString m_IconName; + + Plugin_HyperlinkList *m_HyperlinkList; +}; + +WX_DECLARE_LIST(PlugIn_Waypoint, Plugin_WaypointList); + +class DECL_EXP PlugIn_Route { +public: + PlugIn_Route(void); + ~PlugIn_Route(void); + + wxString m_NameString; + wxString m_StartString; + wxString m_EndString; + wxString m_GUID; + + Plugin_WaypointList *pWaypointList; +}; + +class DECL_EXP PlugIn_Track { +public: + PlugIn_Track(void); + ~PlugIn_Track(void); + + wxString m_NameString; + wxString m_StartString; + wxString m_EndString; + wxString m_GUID; + + Plugin_WaypointList *pWaypointList; +}; + +//---------------------------------------------------------------------------------------------------------- +// The PlugIn CallBack API Definition +// +// The API back up to the PlugIn Manager +// PlugIns may call these static functions as necessary for system services +// +//---------------------------------------------------------------------------------------------------------- + +extern "C" DECL_EXP int InsertPlugInTool(wxString label, wxBitmap *bitmap, + wxBitmap *bmpRollover, wxItemKind kind, + wxString shortHelp, wxString longHelp, + wxObject *clientData, int position, + int tool_sel, opencpn_plugin *pplugin); +extern "C" DECL_EXP void RemovePlugInTool(int tool_id); +extern "C" DECL_EXP void SetToolbarToolViz( + int item, bool viz); // Temporarily change toolbar tool viz +extern "C" DECL_EXP void SetToolbarItemState(int item, bool toggle); +extern "C" DECL_EXP void SetToolbarToolBitmaps(int item, wxBitmap *bitmap, + wxBitmap *bmpRollover); + +extern "C" DECL_EXP int InsertPlugInToolSVG( + wxString label, wxString SVGfile, wxString SVGfileRollover, + wxString SVGfileToggled, wxItemKind kind, wxString shortHelp, + wxString longHelp, wxObject *clientData, int position, int tool_sel, + opencpn_plugin *pplugin); +extern "C" DECL_EXP void SetToolbarToolBitmapsSVG(int item, wxString SVGfile, + wxString SVGfileRollover, + wxString SVGfileToggled); + +extern "C" DECL_EXP int AddCanvasContextMenuItem(wxMenuItem *pitem, + opencpn_plugin *pplugin); +extern "C" DECL_EXP void RemoveCanvasContextMenuItem( + int item); // Fully remove this item +extern "C" DECL_EXP void SetCanvasContextMenuItemViz( + int item, bool viz); // Temporarily change context menu ptions +extern "C" DECL_EXP void SetCanvasContextMenuItemGrey(int item, bool grey); + +extern "C" DECL_EXP wxFileConfig *GetOCPNConfigObject(void); + +extern "C" DECL_EXP void RequestRefresh(wxWindow *); +extern "C" DECL_EXP bool GetGlobalColor(wxString colorName, wxColour *pcolour); + +extern "C" DECL_EXP void GetCanvasPixLL(PlugIn_ViewPort *vp, wxPoint *pp, + double lat, double lon); +extern "C" DECL_EXP void GetCanvasLLPix(PlugIn_ViewPort *vp, wxPoint p, + double *plat, double *plon); + +extern "C" DECL_EXP wxWindow *GetOCPNCanvasWindow(); + +extern "C" DECL_EXP wxFont *OCPNGetFont(wxString TextElement, int default_size); + +extern "C" DECL_EXP wxString *GetpSharedDataLocation(); + +extern "C" DECL_EXP ArrayOfPlugIn_AIS_Targets *GetAISTargetArray(void); + +extern "C" DECL_EXP wxAuiManager *GetFrameAuiManager(void); + +extern "C" DECL_EXP bool AddLocaleCatalog(wxString catalog); + +extern "C" DECL_EXP void PushNMEABuffer(wxString str); + +extern DECL_EXP wxXmlDocument GetChartDatabaseEntryXML(int dbIndex, + bool b_getGeom); + +extern DECL_EXP bool UpdateChartDBInplace(wxArrayString dir_array, + bool b_force_update, + bool b_ProgressDialog); +extern DECL_EXP wxArrayString GetChartDBDirArrayString(); + +extern "C" DECL_EXP void SendPluginMessage(wxString message_id, + wxString message_body); + +extern "C" DECL_EXP void DimeWindow(wxWindow *); + +extern "C" DECL_EXP void JumpToPosition(double lat, double lon, double scale); + +/* API 1.9 adds some common cartographic functions to avoid unnecessary code + * duplication */ +/* Study the original OpenCPN source (georef.c) for functional definitions */ + +extern "C" DECL_EXP void PositionBearingDistanceMercator_Plugin( + double lat, double lon, double brg, double dist, double *dlat, + double *dlon); +extern "C" DECL_EXP void DistanceBearingMercator_Plugin( + double lat0, double lon0, double lat1, double lon1, double *brg, + double *dist); +extern "C" DECL_EXP double DistGreatCircle_Plugin(double slat, double slon, + double dlat, double dlon); + +extern "C" DECL_EXP void toTM_Plugin(float lat, float lon, float lat0, + float lon0, double *x, double *y); +extern "C" DECL_EXP void fromTM_Plugin(double x, double y, double lat0, + double lon0, double *lat, double *lon); +extern "C" DECL_EXP void toSM_Plugin(double lat, double lon, double lat0, + double lon0, double *x, double *y); +extern "C" DECL_EXP void fromSM_Plugin(double x, double y, double lat0, + double lon0, double *lat, double *lon); +extern "C" DECL_EXP void toSM_ECC_Plugin(double lat, double lon, double lat0, + double lon0, double *x, double *y); +extern "C" DECL_EXP void fromSM_ECC_Plugin(double x, double y, double lat0, + double lon0, double *lat, + double *lon); + +extern "C" DECL_EXP bool DecodeSingleVDOMessage(const wxString &str, + PlugIn_Position_Fix_Ex *pos, + wxString *acc); +extern "C" DECL_EXP int GetChartbarHeight(void); +extern "C" DECL_EXP bool GetActiveRoutepointGPX(char *buffer, + unsigned int buffer_length); + +/* API 1.9 */ +typedef enum OptionsParentPI { + PI_OPTIONS_PARENT_DISPLAY, + PI_OPTIONS_PARENT_CONNECTIONS, + PI_OPTIONS_PARENT_CHARTS, + PI_OPTIONS_PARENT_SHIPS, + PI_OPTIONS_PARENT_UI, + PI_OPTIONS_PARENT_PLUGINS +} _OptionsParentPI; +extern DECL_EXP wxScrolledWindow *AddOptionsPage(OptionsParentPI parent, + wxString title); +extern DECL_EXP bool DeleteOptionsPage(wxScrolledWindow *page); + +/* API 1.10 */ + +/* API 1.10 adds some common functions to avoid unnecessary code duplication */ +/* Study the original OpenCPN source for functional definitions */ +extern "C" DECL_EXP double toUsrDistance_Plugin(double nm_distance, + int unit = -1); +extern "C" DECL_EXP double fromUsrDistance_Plugin(double usr_distance, + int unit = -1); +extern "C" DECL_EXP double toUsrSpeed_Plugin(double kts_speed, int unit = -1); +extern "C" DECL_EXP double fromUsrSpeed_Plugin(double usr_speed, int unit = -1); +extern "C" DECL_EXP double toUsrTemp_Plugin(double cel_temp, int unit = -1); +extern "C" DECL_EXP double fromUsrTemp_Plugin(double usr_temp, int unit = -1); +extern DECL_EXP wxString getUsrDistanceUnit_Plugin(int unit = -1); +extern DECL_EXP wxString getUsrSpeedUnit_Plugin(int unit = -1); +extern DECL_EXP wxString getUsrTempUnit_Plugin(int unit = -1); +extern DECL_EXP wxString GetNewGUID(); +extern "C" DECL_EXP bool PlugIn_GSHHS_CrossesLand(double lat1, double lon1, + double lat2, double lon2); +/** + * Start playing a sound file asynchronously. Supported formats depends + * on sound backend. + */ +extern DECL_EXP void PlugInPlaySound(wxString &sound_file); + +// API 1.10 Route and Waypoint Support +extern DECL_EXP wxBitmap *FindSystemWaypointIcon(wxString &icon_name); +extern DECL_EXP bool AddCustomWaypointIcon(wxBitmap *pimage, wxString key, + wxString description); + +extern DECL_EXP bool AddSingleWaypoint(PlugIn_Waypoint *pwaypoint, + bool b_permanent = true); +extern DECL_EXP bool DeleteSingleWaypoint(wxString &GUID); +extern DECL_EXP bool UpdateSingleWaypoint(PlugIn_Waypoint *pwaypoint); + +extern DECL_EXP bool AddPlugInRoute(PlugIn_Route *proute, + bool b_permanent = true); +extern DECL_EXP bool DeletePlugInRoute(wxString &GUID); +extern DECL_EXP bool UpdatePlugInRoute(PlugIn_Route *proute); + +extern DECL_EXP bool AddPlugInTrack(PlugIn_Track *ptrack, + bool b_permanent = true); +extern DECL_EXP bool DeletePlugInTrack(wxString &GUID); +extern DECL_EXP bool UpdatePlugInTrack(PlugIn_Track *ptrack); + +/* API 1.11 */ + +/* API 1.11 adds some more common functions to avoid unnecessary code + * duplication */ +wxColour DECL_EXP GetBaseGlobalColor(wxString colorName); +int DECL_EXP OCPNMessageBox_PlugIn(wxWindow *parent, const wxString &message, + const wxString &caption = _T("Message"), + int style = wxOK, int x = -1, int y = -1); + +extern DECL_EXP wxString toSDMM_PlugIn(int NEflag, double a, + bool hi_precision = true); + +extern "C" DECL_EXP wxString *GetpPrivateApplicationDataLocation(); +extern DECL_EXP wxString GetOCPN_ExePath(void); +extern "C" DECL_EXP wxString *GetpPlugInLocation(); +extern DECL_EXP wxString GetPlugInPath(opencpn_plugin *pplugin); + +extern "C" DECL_EXP int AddChartToDBInPlace(wxString &full_path, + bool b_RefreshCanvas); +extern "C" DECL_EXP int RemoveChartFromDBInPlace(wxString &full_path); +extern DECL_EXP wxString GetLocaleCanonicalName(); + +// API 1.11 adds access to S52 Presentation library +// Types + +// A flag field that defines the object capabilities passed by a chart to +// the S52 PLIB + +#define PLIB_CAPS_LINE_VBO 1 +#define PLIB_CAPS_LINE_BUFFER 1 << 1 +#define PLIB_CAPS_SINGLEGEO_BUFFER 1 << 2 +#define PLIB_CAPS_OBJSEGLIST 1 << 3 +#define PLIB_CAPS_OBJCATMUTATE 1 << 4 + +class PI_S57Obj; + +WX_DECLARE_LIST(PI_S57Obj, ListOfPI_S57Obj); + +// ---------------------------------------------------------------------------- +// PlugInChartBaseGL +// Derived from PlugInChartBase, add OpenGL Vector chart support +// ---------------------------------------------------------------------------- + +class DECL_EXP PlugInChartBaseGL : public PlugInChartBase { +public: + PlugInChartBaseGL(); + virtual ~PlugInChartBaseGL(); + + virtual int RenderRegionViewOnGL(const wxGLContext &glc, + const PlugIn_ViewPort &VPoint, + const wxRegion &Region, bool b_use_stencil); + + virtual ListOfPI_S57Obj *GetObjRuleListAtLatLon(float lat, float lon, + float select_radius, + PlugIn_ViewPort *VPoint); + virtual wxString CreateObjDescriptions(ListOfPI_S57Obj *obj_list); + + virtual int GetNoCOVREntries(); + virtual int GetNoCOVRTablePoints(int iTable); + virtual int GetNoCOVRTablenPoints(int iTable); + virtual float *GetNoCOVRTableHead(int iTable); +}; + +// ---------------------------------------------------------------------------- +// PlugInChartBaseGLPlus2 +// Derived from PlugInChartBaseGL, add additional chart management methods +// ---------------------------------------------------------------------------- + +class DECL_EXP PlugInChartBaseGLPlus2 : public PlugInChartBaseGL { +public: + PlugInChartBaseGLPlus2(); + virtual ~PlugInChartBaseGLPlus2(); + + virtual ListOfPI_S57Obj *GetLightsObjRuleListVisibleAtLatLon( + float lat, float lon, PlugIn_ViewPort *VPoint); +}; + +// ---------------------------------------------------------------------------- +// PlugInChartBaseExtended +// Derived from PlugInChartBase, add extended chart support methods +// ---------------------------------------------------------------------------- + +class DECL_EXP PlugInChartBaseExtended : public PlugInChartBase { +public: + PlugInChartBaseExtended(); + virtual ~PlugInChartBaseExtended(); + + virtual int RenderRegionViewOnGL(const wxGLContext &glc, + const PlugIn_ViewPort &VPoint, + const wxRegion &Region, bool b_use_stencil); + + virtual wxBitmap &RenderRegionViewOnDCNoText(const PlugIn_ViewPort &VPoint, + const wxRegion &Region); + virtual bool RenderRegionViewOnDCTextOnly(wxMemoryDC &dc, + const PlugIn_ViewPort &VPoint, + const wxRegion &Region); + + virtual int RenderRegionViewOnGLNoText(const wxGLContext &glc, + const PlugIn_ViewPort &VPoint, + const wxRegion &Region, + bool b_use_stencil); + + virtual int RenderRegionViewOnGLTextOnly(const wxGLContext &glc, + const PlugIn_ViewPort &VPoint, + const wxRegion &Region, + bool b_use_stencil); + + virtual ListOfPI_S57Obj *GetObjRuleListAtLatLon(float lat, float lon, + float select_radius, + PlugIn_ViewPort *VPoint); + virtual wxString CreateObjDescriptions(ListOfPI_S57Obj *obj_list); + + virtual int GetNoCOVREntries(); + virtual int GetNoCOVRTablePoints(int iTable); + virtual int GetNoCOVRTablenPoints(int iTable); + virtual float *GetNoCOVRTableHead(int iTable); + + virtual void ClearPLIBTextList(); +}; + +// ---------------------------------------------------------------------------- +// PlugInChartBaseExtendedPlus2 +// Derived from PlugInChartBaseExtended, add additional extended chart support +// methods +// ---------------------------------------------------------------------------- + +class DECL_EXP PlugInChartBaseExtendedPlus2 : public PlugInChartBaseExtended { +public: + PlugInChartBaseExtendedPlus2(); + virtual ~PlugInChartBaseExtendedPlus2(); + + virtual ListOfPI_S57Obj *GetLightsObjRuleListVisibleAtLatLon( + float lat, float lon, PlugIn_ViewPort *VPoint); +}; + +class wxArrayOfS57attVal; + +// name of the addressed look up table set (fifth letter) +typedef enum _PI_LUPname { + PI_SIMPLIFIED = 'L', // points + PI_PAPER_CHART = 'R', // points + PI_LINES = 'S', // lines + PI_PLAIN_BOUNDARIES = 'N', // areas + PI_SYMBOLIZED_BOUNDARIES = 'O', // areas + PI_LUPNAME_NUM = 5 +} PI_LUPname; + +// display category type +typedef enum _PI_DisCat { + PI_DISPLAYBASE = 'D', // + PI_STANDARD = 'S', // + PI_OTHER = 'O', // O for OTHER + PI_MARINERS_STANDARD = 'M', // Mariner specified + PI_MARINERS_OTHER, // value not defined + PI_DISP_CAT_NUM, // value not defined +} PI_DisCat; + +// Display Priority +typedef enum _PI_DisPrio { + PI_PRIO_NODATA = '0', // no data fill area pattern + PI_PRIO_GROUP1 = '1', // S57 group 1 filled areas + PI_PRIO_AREA_1 = '2', // superimposed areas + PI_PRIO_AREA_2 = '3', // superimposed areas also water features + PI_PRIO_SYMB_POINT = '4', // point symbol also land features + PI_PRIO_SYMB_LINE = '5', // line symbol also restricted areas + PI_PRIO_SYMB_AREA = '6', // area symbol also traffic areas + PI_PRIO_ROUTEING = '7', // routeing lines + PI_PRIO_HAZARDS = '8', // hazards + PI_PRIO_MARINERS = '9', // VRM, EBL, own ship + PI_PRIO_NUM = 10 // number of priority levels + +} PI_DisPrio; + +typedef enum PI_InitReturn { + PI_INIT_OK = 0, + PI_INIT_FAIL_RETRY, // Init failed, retry suggested + PI_INIT_FAIL_REMOVE, // Init failed, suggest remove from further use + PI_INIT_FAIL_NOERROR // Init failed, request no explicit error message +} _PI_InitReturn; + +class PI_line_segment_element { +public: + size_t vbo_offset; + size_t n_points; + int priority; + float lat_max; // segment bounding box + float lat_min; + float lon_max; + float lon_min; + int type; + void *private0; + + PI_line_segment_element *next; +}; + +class DECL_EXP PI_S57Obj { +public: + // Public Methods + PI_S57Obj(); + +public: + // Instance Data + char FeatureName[8]; + int Primitive_type; + + char *att_array; + wxArrayOfS57attVal *attVal; + int n_attr; + + int iOBJL; + int Index; + + double x; // for POINT + double y; + double z; + int npt; // number of points as needed by arrays + void *geoPt; // for LINE & AREA not described by PolyTessGeo + double *geoPtz; // an array[3] for MultiPoint, SM with Z, i.e. depth + double *geoPtMulti; // an array[2] for MultiPoint, lat/lon to make bbox + // of decomposed points + + void *pPolyTessGeo; + + double m_lat; // The lat/lon of the object's "reference" point + double m_lon; + + double chart_ref_lat; + double chart_ref_lon; + + double lat_min; + double lat_max; + double lon_min; + double lon_max; + + int Scamin; // SCAMIN attribute decoded during load + + bool bIsClone; + int nRef; // Reference counter, to signal OK for deletion + + bool bIsAton; // This object is an aid-to-navigation + bool bIsAssociable; // This object is DRGARE or DEPARE + + int m_n_lsindex; + int *m_lsindex_array; + int m_n_edge_max_points; + void *m_chart_context; + + PI_DisCat m_DisplayCat; + + void *S52_Context; + PI_S57Obj *child; // child list, used only for MultiPoint Soundings + + PI_S57Obj *next; // List linkage + + // This transform converts from object geometry + // to SM coordinates. + double x_rate; // These auxiliary transform coefficients are + double y_rate; // to be used in GetPointPix() and friends + double x_origin; // on a per-object basis if necessary + double y_origin; + + int auxParm0; // some per-object auxiliary parameters, used for OpenGL + int auxParm1; + int auxParm2; + int auxParm3; + + PI_line_segment_element *m_ls_list; + bool m_bcategory_mutable; + int m_DPRI; +}; + +wxString DECL_EXP PI_GetPLIBColorScheme(); +int DECL_EXP PI_GetPLIBDepthUnitInt(); +int DECL_EXP PI_GetPLIBSymbolStyle(); +int DECL_EXP PI_GetPLIBBoundaryStyle(); +int DECL_EXP PI_GetPLIBStateHash(); +double DECL_EXP PI_GetPLIBMarinerSafetyContour(); +bool DECL_EXP PI_GetObjectRenderBox(PI_S57Obj *pObj, double *lat_min, + double *lat_max, double *lon_min, + double *lon_max); +void DECL_EXP PI_UpdateContext(PI_S57Obj *pObj); + +bool DECL_EXP PI_PLIBObjectRenderCheck(PI_S57Obj *pObj, PlugIn_ViewPort *vp); +PI_LUPname DECL_EXP PI_GetObjectLUPName(PI_S57Obj *pObj); +PI_DisPrio DECL_EXP PI_GetObjectDisplayPriority(PI_S57Obj *pObj); +PI_DisCat DECL_EXP PI_GetObjectDisplayCategory(PI_S57Obj *pObj); +void DECL_EXP PI_PLIBSetLineFeaturePriority(PI_S57Obj *pObj, int prio); +void DECL_EXP PI_PLIBPrepareForNewRender(void); +void DECL_EXP PI_PLIBFreeContext(void *pContext); +void DECL_EXP PI_PLIBSetRenderCaps(unsigned int flags); + +bool DECL_EXP PI_PLIBSetContext(PI_S57Obj *pObj); + +int DECL_EXP PI_PLIBRenderObjectToDC(wxDC *pdc, PI_S57Obj *pObj, + PlugIn_ViewPort *vp); +int DECL_EXP PI_PLIBRenderAreaToDC(wxDC *pdc, PI_S57Obj *pObj, + PlugIn_ViewPort *vp, wxRect rect, + unsigned char *pixbuf); + +int DECL_EXP PI_PLIBRenderAreaToGL(const wxGLContext &glcc, PI_S57Obj *pObj, + PlugIn_ViewPort *vp, wxRect &render_rect); + +int DECL_EXP PI_PLIBRenderObjectToGL(const wxGLContext &glcc, PI_S57Obj *pObj, + PlugIn_ViewPort *vp, wxRect &render_rect); + +/* API 1.11 OpenGL Display List and vertex buffer object routines + + Effectively these two routines cancel each other so all + of the translation, scaling and rotation can be done by opengl. + + Display lists need only be built infrequently, but used in each frame + greatly accelerates the speed of rendering. This avoids costly calculations, + and also allows the vertexes to be stored in graphics memory. + + static int dl = 0; + glPushMatrix(); + PlugInMultMatrixViewport(current_viewport); + if(dl) + glCallList(dl); + else { + dl = glGenLists(1); + PlugInViewPort norm_viewport = current_viewport; + NormalizeViewPort(norm_viewport); + glNewList(dl, GL_COMPILE_AND_EXECUTE); + ... // use norm_viewport with GetCanvasLLPix here + glEndList(); + } + glPopMatrix(); + ... // use current_viewport with GetCanvasLLPix again +*/ + +extern DECL_EXP bool PlugInHasNormalizedViewPort(PlugIn_ViewPort *vp); +extern DECL_EXP void PlugInMultMatrixViewport(PlugIn_ViewPort *vp, + float lat = 0, float lon = 0); +extern DECL_EXP void PlugInNormalizeViewport(PlugIn_ViewPort *vp, float lat = 0, + float lon = 0); + +class wxPoint2DDouble; +extern "C" DECL_EXP void GetDoubleCanvasPixLL(PlugIn_ViewPort *vp, + wxPoint2DDouble *pp, double lat, + double lon); + +/* API 1.13 */ +/* API 1.13 adds some more common functions to avoid unnecessary code + * duplication */ + +extern DECL_EXP double fromDMM_Plugin(wxString sdms); +extern DECL_EXP void SetCanvasRotation(double rotation); +extern DECL_EXP void SetCanvasProjection(int projection); +extern DECL_EXP bool GetSingleWaypoint(wxString GUID, + PlugIn_Waypoint *pwaypoint); +extern DECL_EXP bool CheckEdgePan_PlugIn(int x, int y, bool dragging, + int margin, int delta); +extern DECL_EXP wxBitmap GetIcon_PlugIn(const wxString &name); +extern DECL_EXP void SetCursor_PlugIn(wxCursor *pPlugin_Cursor = NULL); +extern DECL_EXP wxFont *GetOCPNScaledFont_PlugIn(wxString TextElement, + int default_size = 0); +extern DECL_EXP wxFont GetOCPNGUIScaledFont_PlugIn(wxString item); +extern DECL_EXP double GetOCPNGUIToolScaleFactor_PlugIn(int GUIScaledFactor); +extern DECL_EXP double GetOCPNGUIToolScaleFactor_PlugIn(); +extern DECL_EXP float GetOCPNChartScaleFactor_Plugin(); +extern DECL_EXP wxColour GetFontColour_PlugIn(wxString TextElement); + +extern DECL_EXP double GetCanvasTilt(); +extern DECL_EXP void SetCanvasTilt(double tilt); + +/** + * Start playing a sound file asynchronously. Supported formats depends + * on sound backend. The deviceIx is only used on platforms using the + * portaudio sound backend where -1 indicates the default device. + */ +extern DECL_EXP bool PlugInPlaySoundEx(wxString &sound_file, + int deviceIndex = -1); +extern DECL_EXP void AddChartDirectory(wxString &path); +extern DECL_EXP void ForceChartDBUpdate(); +extern DECL_EXP void ForceChartDBRebuild(); + +extern DECL_EXP wxString GetWritableDocumentsDir(void); +extern DECL_EXP wxDialog *GetActiveOptionsDialog(); +extern DECL_EXP wxArrayString GetWaypointGUIDArray(void); +extern DECL_EXP wxArrayString GetIconNameArray(void); + +extern DECL_EXP bool AddPersistentFontKey(wxString TextElement); +extern DECL_EXP wxString GetActiveStyleName(); + +extern DECL_EXP wxBitmap GetBitmapFromSVGFile(wxString filename, + unsigned int width, + unsigned int height); +extern DECL_EXP bool IsTouchInterface_PlugIn(void); + +/* Platform optimized File/Dir selector dialogs */ +extern DECL_EXP int PlatformDirSelectorDialog(wxWindow *parent, + wxString *file_spec, + wxString Title, wxString initDir); + +extern DECL_EXP int PlatformFileSelectorDialog(wxWindow *parent, + wxString *file_spec, + wxString Title, wxString initDir, + wxString suggestedName, + wxString wildcard); + +/* OpenCPN HTTP File Download PlugIn Interface */ + +/* Various method Return Codes, etc */ +typedef enum _OCPN_DLStatus { + OCPN_DL_UNKNOWN = -1, + OCPN_DL_NO_ERROR = 0, + OCPN_DL_FAILED = 1, + OCPN_DL_ABORTED = 2, + OCPN_DL_USER_TIMEOUT = 4, + OCPN_DL_STARTED = 8 +} OCPN_DLStatus; + +typedef enum _OCPN_DLCondition { + OCPN_DL_EVENT_TYPE_UNKNOWN = -1, + OCPN_DL_EVENT_TYPE_START = 80, + OCPN_DL_EVENT_TYPE_PROGRESS = 81, + OCPN_DL_EVENT_TYPE_END = 82 +} OCPN_DLCondition; + +// Style definitions for Synchronous file download modal dialogs, if +// desired. Abstracted from wxCURL package +enum OCPN_DLDialogStyle { + OCPN_DLDS_ELAPSED_TIME = 0x0001, //!< The dialog shows the elapsed time. + OCPN_DLDS_ESTIMATED_TIME = + 0x0002, //!< The dialog shows the estimated total time. + OCPN_DLDS_REMAINING_TIME = 0x0004, //!< The dialog shows the remaining time. + OCPN_DLDS_SPEED = 0x0008, //!< The dialog shows the transfer speed. + OCPN_DLDS_SIZE = 0x0010, //!< The dialog shows the size of the resource to + //!< download/upload. + OCPN_DLDS_URL = + 0x0020, //!< The dialog shows the URL involved in the transfer. + + // styles related to the use of wxCurlConnectionSettingsDialog: + + OCPN_DLDS_CONN_SETTINGS_AUTH = + 0x0040, //!< The dialog allows the user to change the authentication + //!< settings. + OCPN_DLDS_CONN_SETTINGS_PORT = 0x0080, //!< The dialog allows the user to + //!< change the port for the transfer. + OCPN_DLDS_CONN_SETTINGS_PROXY = + 0x0100, //!< The dialog allows the user to change the proxy settings. + + OCPN_DLDS_CONN_SETTINGS_ALL = OCPN_DLDS_CONN_SETTINGS_AUTH | + OCPN_DLDS_CONN_SETTINGS_PORT | + OCPN_DLDS_CONN_SETTINGS_PROXY, + + OCPN_DLDS_SHOW_ALL = OCPN_DLDS_ELAPSED_TIME | OCPN_DLDS_ESTIMATED_TIME | + OCPN_DLDS_REMAINING_TIME | OCPN_DLDS_SPEED | + OCPN_DLDS_SIZE | OCPN_DLDS_URL | + OCPN_DLDS_CONN_SETTINGS_ALL, + + OCPN_DLDS_CAN_ABORT = 0x0200, //!< The transfer can be aborted by the user. + OCPN_DLDS_CAN_START = 0x0400, //!< The transfer won't start automatically. + //!< The user needs to start it. + OCPN_DLDS_CAN_PAUSE = 0x0800, //!< The transfer can be paused. + + OCPN_DLDS_AUTO_CLOSE = + 0x1000, //!< The dialog auto closes when transfer is complete. + + // by default all available features are enabled: + OCPN_DLDS_DEFAULT_STYLE = OCPN_DLDS_CAN_START | OCPN_DLDS_CAN_PAUSE | + OCPN_DLDS_CAN_ABORT | OCPN_DLDS_SHOW_ALL | + OCPN_DLDS_AUTO_CLOSE +}; + +#define ONLINE_CHECK_RETRY \ + 30 // Recheck the Internet connection availability every ONLINE_CHECK_RETRY s + +/* Synchronous (Blocking) download of a single file */ + +extern DECL_EXP _OCPN_DLStatus OCPN_downloadFile( + const wxString &url, const wxString &outputFile, const wxString &title, + const wxString &message, const wxBitmap &bitmap, wxWindow *parent, + long style, int timeout_secs); + +/* Asynchronous (Background) download of a single file */ + +extern DECL_EXP _OCPN_DLStatus +OCPN_downloadFileBackground(const wxString &url, const wxString &outputFile, + wxEvtHandler *handler, long *handle); + +extern DECL_EXP void OCPN_cancelDownloadFileBackground(long handle); + +/* Synchronous (Blocking) HTTP POST operation for small amounts of data */ + +extern DECL_EXP _OCPN_DLStatus OCPN_postDataHttp(const wxString &url, + const wxString ¶meters, + wxString &result, + int timeout_secs); + +/* Check whether connection to the Internet is working */ + +extern DECL_EXP bool OCPN_isOnline(); + +/* Supporting Event for Background downloading */ +/* OCPN_downloadEvent Definition */ + +/* PlugIn should be ready/able to handle this event after initiating a + * background file transfer + * + * The event as received should be parsed primarily by the getDLEventCondition() + * method. This will allow identification of download start, progress, and end + * states. + * + * Other accessor methods contain status, byte counts, etc. + * + * A PlugIn may safely destroy its EvtHandler after receipt of an + * OCPN_downloadEvent with getDLEventCondition == OCPN_DL_EVENT_TYPE_END + */ + +class DECL_EXP OCPN_downloadEvent : public wxEvent { +public: + OCPN_downloadEvent(wxEventType commandType = wxEVT_NULL, int id = 0); + ~OCPN_downloadEvent(); + + // accessors + _OCPN_DLStatus getDLEventStatus() { return m_stat; } + OCPN_DLCondition getDLEventCondition() { return m_condition; } + + void setDLEventStatus(_OCPN_DLStatus stat) { m_stat = stat; } + void setDLEventCondition(OCPN_DLCondition cond) { m_condition = cond; } + + void setTotal(long bytes) { m_totalBytes = bytes; } + void setTransferred(long bytes) { m_sofarBytes = bytes; } + long getTotal() { return m_totalBytes; } + long getTransferred() { return m_sofarBytes; } + + void setComplete(bool b_complete) { m_b_complete = b_complete; } + bool getComplete() { return m_b_complete; } + + // required for sending with wxPostEvent() + wxEvent *Clone() const; + +private: + OCPN_DLStatus m_stat; + OCPN_DLCondition m_condition; + + long m_totalBytes; + long m_sofarBytes; + bool m_b_complete; +}; + +// extern WXDLLIMPEXP_CORE const wxEventType wxEVT_DOWNLOAD_EVENT; + +#ifdef MAKING_PLUGIN +extern DECL_IMP wxEventType wxEVT_DOWNLOAD_EVENT; +#else +extern DECL_EXP wxEventType wxEVT_DOWNLOAD_EVENT; +#endif + +/* API 1.14 */ +/* API 1.14 adds some more common functions to avoid unnecessary code + * duplication */ + +bool LaunchDefaultBrowser_Plugin(wxString url); + +// API 1.14 Extra canvas Support + +/* Allow drawing of objects onto other OpenGL canvases */ +extern DECL_EXP void PlugInAISDrawGL(wxGLCanvas *glcanvas, + const PlugIn_ViewPort &vp); +extern DECL_EXP bool PlugInSetFontColor(const wxString TextElement, + const wxColour color); + +// API 1.15 +extern DECL_EXP double PlugInGetDisplaySizeMM(); + +// +extern DECL_EXP wxFont *FindOrCreateFont_PlugIn( + int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, + bool underline = false, const wxString &facename = wxEmptyString, + wxFontEncoding encoding = wxFONTENCODING_DEFAULT); + +extern DECL_EXP int PlugInGetMinAvailableGshhgQuality(); +extern DECL_EXP int PlugInGetMaxAvailableGshhgQuality(); + +extern DECL_EXP void PlugInHandleAutopilotRoute(bool enable); + +// API 1.16 +// +/** + * Return the plugin data directory for a given directory name. + * + * On Linux, the returned data path is an existing directory ending in + * "opencpn/plugins/" where the last part is the plugin_name + * argument. The prefix part is one of the directories listed in the + * environment variable XDG_DATA_DIRS, by default + * ~/.local/share:/usr/local/share:/usr/share. + * + * On other platforms, the returned value is GetSharedDataDir() + + * "/opencpn/plugins/" + plugin_name (with native path separators) + * if that path exists. + * + * Return "" if no existing directory is found. + */ +extern DECL_EXP wxString GetPluginDataDir(const char *plugin_name); + +extern DECL_EXP bool ShuttingDown(void); + +// Support for MUI MultiCanvas model + +extern DECL_EXP wxWindow *PluginGetFocusCanvas(); +extern DECL_EXP wxWindow *PluginGetOverlayRenderCanvas(); + +extern "C" DECL_EXP void CanvasJumpToPosition(wxWindow *canvas, double lat, + double lon, double scale); +extern "C" DECL_EXP int AddCanvasMenuItem(wxMenuItem *pitem, + opencpn_plugin *pplugin, + const char *name = ""); +extern "C" DECL_EXP void RemoveCanvasMenuItem( + int item, const char *name = ""); // Fully remove this item +extern "C" DECL_EXP void SetCanvasMenuItemViz( + int item, bool viz, + const char *name = ""); // Temporarily change context menu options +extern "C" DECL_EXP void SetCanvasMenuItemGrey(int item, bool grey, + const char *name = ""); + +// Extract waypoints, routes and tracks +extern DECL_EXP wxString GetSelectedWaypointGUID_Plugin(); +extern DECL_EXP wxString GetSelectedRouteGUID_Plugin(); +extern DECL_EXP wxString GetSelectedTrackGUID_Plugin(); + +extern DECL_EXP std::unique_ptr GetWaypoint_Plugin( + const wxString &); // doublon with GetSingleWaypoint +extern DECL_EXP std::unique_ptr GetRoute_Plugin(const wxString &); +extern DECL_EXP std::unique_ptr GetTrack_Plugin(const wxString &); + +extern DECL_EXP wxWindow *GetCanvasUnderMouse(); +extern DECL_EXP int GetCanvasIndexUnderMouse(); +// extern DECL_EXP std::vector GetCanvasArray(); +extern DECL_EXP wxWindow *GetCanvasByIndex(int canvasIndex); +extern DECL_EXP int GetCanvasCount(); +extern DECL_EXP bool CheckMUIEdgePan_PlugIn(int x, int y, bool dragging, + int margin, int delta, + int canvasIndex); +extern DECL_EXP void SetMUICursor_PlugIn(wxCursor *pCursor, int canvasIndex); + +// API 1.17 +// +extern DECL_EXP wxRect GetMasterToolbarRect(); + +enum SDDMFORMAT { + DEGREES_DECIMAL_MINUTES = 0, + DECIMAL_DEGREES, + DEGREES_MINUTES_SECONDS, + END_SDDMFORMATS +}; + +extern DECL_EXP int GetLatLonFormat(void); + +// API 1.17 +extern "C" DECL_EXP void ZeroXTE(); + +// Extended Waypoint manipulation API +class DECL_EXP PlugIn_Waypoint_Ex { +public: + PlugIn_Waypoint_Ex(); + PlugIn_Waypoint_Ex(double lat, double lon, const wxString &icon_ident, + const wxString &wp_name, const wxString &GUID = "", + const double ScaMin = 1e9, const bool bNameVisible = false, + const int nRanges = 0, const double RangeDistance = 1.0, + const wxColor RangeColor = wxColor(255, 0, 0)); + ~PlugIn_Waypoint_Ex(); + void InitDefaults(); + + bool GetFSStatus(); // return "free standing" status + // To be a "free standing waypoint"(FSWP), + // the RoutePoint will have been created by GUI dropping + // a point; by importing a waypoint in a GPX file or by + // the AddSingleWaypoint API. + + int GetRouteMembershipCount(); // Return the number of routes to which this + // WP belongs + + double m_lat; + double m_lon; + + wxString m_GUID; + + wxString m_MarkName; + wxString m_MarkDescription; + wxDateTime m_CreateTime; + bool IsVisible; + bool IsActive; + + double scamin; + bool b_useScamin; + bool IsNameVisible; + int nrange_rings; + double RangeRingSpace; + wxColour RangeRingColor; + + wxString IconName; + wxString IconDescription; + + Plugin_HyperlinkList *m_HyperlinkList; +}; + +WX_DECLARE_LIST(PlugIn_Waypoint_Ex, Plugin_WaypointExList); + +class DECL_EXP PlugIn_Route_Ex { +public: + PlugIn_Route_Ex(void); + ~PlugIn_Route_Ex(void); + + wxString m_NameString; + wxString m_StartString; + wxString m_EndString; + wxString m_GUID; + bool m_isActive; + bool m_isVisible; + wxString m_Description; + + + Plugin_WaypointExList *pWaypointList; +}; + +extern DECL_EXP wxArrayString GetRouteGUIDArray(void); +extern DECL_EXP wxArrayString GetTrackGUIDArray(void); + +extern DECL_EXP bool GetSingleWaypointEx(wxString GUID, + PlugIn_Waypoint_Ex *pwaypoint); + +extern DECL_EXP bool AddSingleWaypointEx(PlugIn_Waypoint_Ex *pwaypoint, + bool b_permanent = true); +extern DECL_EXP bool UpdateSingleWaypointEx(PlugIn_Waypoint_Ex *pwaypoint); + +extern DECL_EXP bool AddPlugInRouteEx(PlugIn_Route_Ex *proute, + bool b_permanent = true); +extern DECL_EXP bool UpdatePlugInRouteEx(PlugIn_Route_Ex *proute); + +extern DECL_EXP std::unique_ptr GetWaypointEx_Plugin( + const wxString &); +extern DECL_EXP std::unique_ptr GetRouteEx_Plugin( + const wxString &); + +extern DECL_EXP wxString +GetActiveWaypointGUID(void); // if no active waypoint, returns wxEmptyString +extern DECL_EXP wxString +GetActiveRouteGUID(void); // if no active route, returns wxEmptyString + +// API 1.18 + +// Scaled display support, as on some GTK3 and Mac Retina devices +extern DECL_EXP double OCPN_GetDisplayContentScaleFactor(); + +// Scaled display support, on Windows devices +extern DECL_EXP double OCPN_GetWinDIPScaleFactor(); + +// Comm Priority query support +extern DECL_EXP std::vector GetPriorityMaps(); +extern DECL_EXP std::vector GetActivePriorityIdentifiers(); + +extern DECL_EXP int GetGlobalWatchdogTimoutSeconds(); + +typedef enum _OBJECT_LAYER_REQ { + OBJECTS_ALL = 0, + OBJECTS_NO_LAYERS, + OBJECTS_ONLY_LAYERS +} OBJECT_LAYER_REQ; + +// FIXME (dave) Implement these +extern DECL_EXP wxArrayString GetRouteGUIDArray(OBJECT_LAYER_REQ req); +extern DECL_EXP wxArrayString GetTrackGUIDArray(OBJECT_LAYER_REQ req); +extern DECL_EXP wxArrayString GetWaypointGUIDArray(OBJECT_LAYER_REQ req); + +/** listen-notify interface */ + +/* Listening to messages. */ +class ObservableListener; + +/** The event used by notify/listen. */ +class ObservedEvt; + +// This is a verbatim copy from observable_evt.h, don't define twice. +#ifndef OBSERVABLE_EVT_H +#define OBSERVABLE_EVT_H + +wxDECLARE_EVENT(obsNOTIFY, ObservedEvt); + +/** Adds a std::shared element to wxCommandEvent. */ +class ObservedEvt : public wxCommandEvent { +public: + ObservedEvt(wxEventType commandType = obsNOTIFY, int id = 0) + : wxCommandEvent(commandType, id) {} + ObservedEvt(const ObservedEvt &event) : wxCommandEvent(event) { + this->m_shared_ptr = event.m_shared_ptr; + } + + wxEvent *Clone() const { return new ObservedEvt(*this); } + + std::shared_ptr GetSharedPtr() const { return m_shared_ptr; } + + void SetSharedPtr(std::shared_ptr p) { m_shared_ptr = p; } + +private: + std::shared_ptr m_shared_ptr; +}; + +#endif // OBSERVABLE_EVT_H + +class ObservableListener; + +/** Facade for NavAddr2000. */ +struct NMEA2000Id { + const uint64_t id; + NMEA2000Id(int value) : id(static_cast(value)){}; +}; + +extern DECL_EXP std::shared_ptr GetListener( + NMEA2000Id id, wxEventType ev, wxEvtHandler *handler); + +/** Facade for NavAddr0183. */ +struct NMEA0183Id { + const std::string id; + NMEA0183Id(const std::string &s) : id(s){}; +}; + +extern DECL_EXP std::shared_ptr GetListener( + NMEA0183Id id, wxEventType ev, wxEvtHandler *handler); + +/** Facade for NavAddrSignalK. */ +struct SignalkId { + const std::string id; + SignalkId(const std::string &s) : id(s){}; +}; + +extern DECL_EXP std::shared_ptr GetListener( + SignalkId id, wxEventType ev, wxEvtHandler *handler); + +/** Return payload in a received n2000 message of type id in ev. */ +extern DECL_EXP std::vector GetN2000Payload(NMEA2000Id id, + ObservedEvt ev); + +/** + * Get SignalK status payload after receiving a message. + * @return pointer to a wxJSONValue map object. Typical usage: + * + * auto ptr = GetSignalkPayload(ev); + * const auto msg = *std::static_pointer_cast(payload); + * + * The map contains the following entries: + * - "Data": the parsed json message + * - "ErrorCount": int, the number of parsing errors + * - "WarningCount": int, the number of parsing warnings + * - "Errors": list of strings, error messages. + * - "Warnings": list of strings, warning messages.. + * - "Context": string, message context + * - "ContextSelf": string, own ship context. + */ +std::shared_ptr GetSignalkPayload(ObservedEvt ev); + +/** + * Return source identifier (iface) of a received n2000 message of type id + * in ev. + */ +extern DECL_EXP std::string GetN2000Source(NMEA2000Id id, ObservedEvt ev); + +/** Return payload in a received n0183 message of type id in ev. */ +extern DECL_EXP std::string GetN0183Payload(NMEA0183Id id, ObservedEvt ev); + +/** Facade for BasicNavDataMsg. */ +struct NavDataId { + const int type; + NavDataId() : type(0) {} +}; + +extern DECL_EXP std::shared_ptr GetListener( + NavDataId id, wxEventType ev, wxEvtHandler *handler); + +/** Available decoded data for plugins. */ +struct PluginNavdata { + double lat; + double lon; + double sog; + double cog; + double var; + double hdt; + time_t time; +}; + +/** Return BasicNavDataMsg decoded data available in ev */ +extern DECL_EXP PluginNavdata GetEventNavdata(ObservedEvt ev); + +/** Plugin API supporting direct access to comm drivers for output purposes */ +/* + * Plugins may access comm ports for direct output. + * The general program flow for a plugin may look something like this + * pseudo-code: + * 1. Plugin will query OCPN core for a list of active comm drivers. + * 2. Plugin will inspect the list, and query OCPN core for driver + * attributes. + * 3. Plugin will select a comm driver with appropriate attributes for output. + * 4. Plugin will register a list of PGNs expected to be transmitted + * (N2K specific) + * 5. Plugin may then send a payload buffer to a specific comm driver for + * output as soon as possible. + * + * The mechanism for specifying a particular comm driver uses the notion of + * "handles". Each active comm driver has an associated opaque handle, managed + * by OCPN core. All references by a plugin to a driver are by means of its + * handle. Handles should be considered to be "opaque", meaning that the exact + * contents of the handle are of no specific value to the plugin, and only + * have meaning to the OCPN core management of drivers. + */ + +/** Definition of OCPN DriverHandle */ +typedef std::string DriverHandle; + +/** Error return values */ + +typedef enum CommDriverResult { + RESULT_COMM_NO_ERROR = 0, + RESULT_COMM_INVALID_HANDLE, + RESULT_COMM_INVALID_PARMS, + RESULT_COMM_TX_ERROR, + RESULT_COMM_REGISTER_GATEWAY_ERROR, + RESULT_COMM_REGISTER_PGN_ERROR +} _CommDriverResult; + +/** Query OCPN core for a list of active drivers */ +extern DECL_EXP std::vector GetActiveDrivers(); + +/** Query a specific driver for attributes */ +/* Driver attributes are available from OCPN core as a hash map of + * tag->attribute pairs. There is a defined set of common tags guaranteed + * for every driver. Both tags and attributes are defined as std::string. + * Here is the list of common tag-attribute pairs. + * + * Tag Attribute definition + * ---------- -------------------- + * "protocol" Comm bus device protocol, such as "NMEA0183", "NMEA2000" + * + * + */ + +/** Query driver attributes */ +extern DECL_EXP const std::unordered_map + GetAttributes(DriverHandle handle); + +/* Writing to a specific driver */ + +/* Comm drivers on bus protocols other than NMEA2000 may write directly to the + * port * using a simple call. The physical write operation will be queued, + * and executed in order as bandwidth allows. + * Return value is number of bytes queued for transmission. + */ +extern DECL_EXP CommDriverResult WriteCommDriver( + DriverHandle handle, + const std::shared_ptr > &payload); + +/** Send a PGN message to an NMEA2000 address. */ +extern DECL_EXP CommDriverResult WriteCommDriverN2K( + DriverHandle handle, int PGN, int destinationCANAddress, int priority, + const std::shared_ptr > &payload); + +/** + * Special NMEA2000 requirements + * NMEA2000 bus protocol device management requires that devices writing on + * the bus must inform all bus listeners of the specific PGNs that may be + * transmitted by this device. Once configured, this bus management process + * will be handled transparently by the OCPN core drivers. It is only + * necessary for plugins wishing to write to the NMEA2000 bus to register the + * specific PGNs that they anticipate using, with the selected driver. + */ +extern DECL_EXP CommDriverResult RegisterTXPGNs(DriverHandle handle, + std::vector &pgn_list); + + +#endif //_PLUGIN_H_ From 3c9a175494a9d1a9cda831e626dc7d6af01f3944 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sun, 10 Dec 2023 08:33:26 +0100 Subject: [PATCH 57/63] s52plib: Remove some main OpenCPN dependencies (#3527) --- libs/glshim/src/proxy/proxy.h | 1 - libs/s52plib/CMakeLists.txt | 3 +- libs/s52plib/src/mygeom.cpp | 2 - libs/s52plib/src/ocpn_plugin.h | 1838 -------------------------------- libs/s52plib/src/s52plibGL.h | 2 +- 5 files changed, 2 insertions(+), 1844 deletions(-) delete mode 100644 libs/s52plib/src/ocpn_plugin.h diff --git a/libs/glshim/src/proxy/proxy.h b/libs/glshim/src/proxy/proxy.h index 7b657c83b1..469fbf2bc1 100644 --- a/libs/glshim/src/proxy/proxy.h +++ b/libs/glshim/src/proxy/proxy.h @@ -4,7 +4,6 @@ #include #include "../gl/defines.h" #include "../config.h" -#include "config.h" #ifndef PROXY_H #define PROXY_H diff --git a/libs/s52plib/CMakeLists.txt b/libs/s52plib/CMakeLists.txt index 0da9a1c627..566e5a6e15 100644 --- a/libs/s52plib/CMakeLists.txt +++ b/libs/s52plib/CMakeLists.txt @@ -31,8 +31,7 @@ add_library(ocpn::s52plib ALIAS S52PLIB) set_property(TARGET S52PLIB PROPERTY COMPILE_FLAGS "${OBJ_VISIBILITY}") target_include_directories(S52PLIB PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) -target_include_directories(S52PLIB PRIVATE ../../include) -target_include_directories(S52PLIB PRIVATE ${CMAKE_BINARY_DIR}/include) +target_include_directories(S52PLIB PUBLIC ${PROJECT_SOURCE_DIR}/include) target_include_directories(S52PLIB PRIVATE ${wxWidgets_INCLUDE_DIRS}) target_link_libraries(S52PLIB PRIVATE ocpn::geoprim) target_link_libraries(S52PLIB PRIVATE ocpn::pugixml) diff --git a/libs/s52plib/src/mygeom.cpp b/libs/s52plib/src/mygeom.cpp index baa5d4262e..b863a8f55e 100644 --- a/libs/s52plib/src/mygeom.cpp +++ b/libs/s52plib/src/mygeom.cpp @@ -51,8 +51,6 @@ #include "wx/tokenzr.h" #include -#include "config.h" - #include "gdal/ogr_geometry.h" #include "cutil.h" diff --git a/libs/s52plib/src/ocpn_plugin.h b/libs/s52plib/src/ocpn_plugin.h deleted file mode 100644 index 6847dbe4e6..0000000000 --- a/libs/s52plib/src/ocpn_plugin.h +++ /dev/null @@ -1,1838 +0,0 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: PlugIn Object Definition/API - * Author: David Register - * - *************************************************************************** - * Copyright (C) 2010 by David S. Register * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - **************************************************************************/ - -#ifndef _PLUGIN_H_ -#define _PLUGIN_H_ - -#ifndef DECL_EXP -#if defined(__WXMSW__) || defined(__CYGWIN__) -#define DECL_EXP __declspec(dllexport) -#elif defined __GNUC__ && __GNUC__ >= 4 -#define DECL_EXP __attribute__((visibility("default"))) -#elif defined __WXOSX__ -#define DECL_EXP __attribute__((visibility("default"))) -#else -#define DECL_EXP -#endif -#endif - -#if defined(__WXMSW__) && defined(MAKING_PLUGIN) -#define DECL_IMP __declspec(dllimport) -#else -#define DECL_IMP -#endif - -#include -#include -#include -#include -#include -#include - -#ifdef ocpnUSE_SVG -#include -#endif // ocpnUSE_SVG - -#include -#include -#include - -class wxGLContext; - -// This is the most modern API Version number -// It is expected that the API will remain downward compatible, meaning that -// PlugIns conforming to API Version less then the most modern will also -// be correctly supported. -#define API_VERSION_MAJOR 1 -#define API_VERSION_MINOR 18 - -// Fwd Definitions -class wxFileConfig; -class wxNotebook; -class wxFont; -class wxAuiManager; -class wxScrolledWindow; -class wxGLCanvas; - -//--------------------------------------------------------------------------------------------------------- -// -// Bitfield PlugIn Capabilites flag definition -// -//--------------------------------------------------------------------------------------------------------- -#define WANTS_OVERLAY_CALLBACK 0x00000001 -#define WANTS_CURSOR_LATLON 0x00000002 -#define WANTS_TOOLBAR_CALLBACK 0x00000004 -#define INSTALLS_TOOLBAR_TOOL 0x00000008 -#define WANTS_CONFIG 0x00000010 -#define INSTALLS_TOOLBOX_PAGE 0x00000020 -#define INSTALLS_CONTEXTMENU_ITEMS 0x00000040 -#define WANTS_NMEA_SENTENCES 0x00000080 -#define WANTS_NMEA_EVENTS 0x00000100 -#define WANTS_AIS_SENTENCES 0x00000200 -#define USES_AUI_MANAGER 0x00000400 -#define WANTS_PREFERENCES 0x00000800 -#define INSTALLS_PLUGIN_CHART 0x00001000 -#define WANTS_ONPAINT_VIEWPORT 0x00002000 -#define WANTS_PLUGIN_MESSAGING 0x00004000 -#define WANTS_OPENGL_OVERLAY_CALLBACK 0x00008000 -#define WANTS_DYNAMIC_OPENGL_OVERLAY_CALLBACK 0x00010000 -#define WANTS_LATE_INIT 0x00020000 -#define INSTALLS_PLUGIN_CHART_GL 0x00040000 -#define WANTS_MOUSE_EVENTS 0x00080000 -#define WANTS_VECTOR_CHART_OBJECT_INFO 0x00100000 -#define WANTS_KEYBOARD_EVENTS 0x00200000 - -//--------------------------------------------------------------------------------------------------------- -// -// Overlay priorities -// -//--------------------------------------------------------------------------------------------------------- -#define OVERLAY_LEGACY 0 -#define OVERLAY_OVER_SHIPS 64 -#define OVERLAY_OVER_EMBOSS 96 -#define OVERLAY_OVER_UI 128 - -//---------------------------------------------------------------------------------------------------------- -// Some PlugIn API interface object class definitions -//---------------------------------------------------------------------------------------------------------- -enum PI_ColorScheme { - PI_GLOBAL_COLOR_SCHEME_RGB, - PI_GLOBAL_COLOR_SCHEME_DAY, - PI_GLOBAL_COLOR_SCHEME_DUSK, - PI_GLOBAL_COLOR_SCHEME_NIGHT, - PI_N_COLOR_SCHEMES -}; - -class PlugIn_ViewPort { -public: - double clat; // center point - double clon; - double view_scale_ppm; - double skew; - double rotation; - - float chart_scale; // conventional chart displayed scale - - int pix_width; - int pix_height; - wxRect rv_rect; - bool b_quilt; - int m_projection_type; - - double lat_min, lat_max, lon_min, lon_max; - - bool bValid; // This VP is valid -}; - -class PlugIn_Position_Fix { -public: - double Lat; - double Lon; - double Cog; - double Sog; - double Var; // Variation, typically from RMC message - time_t FixTime; - int nSats; -}; - -class PlugIn_Position_Fix_Ex { -public: - double Lat; - double Lon; - double Cog; - double Sog; - double Var; // Variation, typically from RMC message - double Hdm; - double Hdt; - time_t FixTime; - int nSats; -}; - -class Plugin_Active_Leg_Info { -public: - double Xte; // Left side of the track -> negative XTE - double Btw; - double Dtw; - wxString wp_name; // Name of destination waypoint for active leg - bool arrival; // True when within arrival circle -}; - -// Describe AIS Alarm state -enum plugin_ais_alarm_type { - PI_AIS_NO_ALARM = 0, - PI_AIS_ALARM_SET, - PI_AIS_ALARM_ACKNOWLEDGED - -}; - -class PlugIn_AIS_Target { -public: - int MMSI; - int Class; - int NavStatus; - double SOG; - double COG; - double HDG; - double Lon; - double Lat; - int ROTAIS; - char CallSign[8]; // includes terminator - char ShipName[21]; - unsigned char ShipType; - int IMO; - - double Range_NM; - double Brg; - - // Per target collision parameters - bool bCPA_Valid; - double TCPA; // Minutes - double CPA; // Nautical Miles - - plugin_ais_alarm_type alarm_state; -}; - -// ChartType constants -typedef enum ChartTypeEnumPI { - PI_CHART_TYPE_UNKNOWN = 0, - PI_CHART_TYPE_DUMMY, - PI_CHART_TYPE_DONTCARE, - PI_CHART_TYPE_KAP, - PI_CHART_TYPE_GEO, - PI_CHART_TYPE_S57, - PI_CHART_TYPE_CM93, - PI_CHART_TYPE_CM93COMP, - PI_CHART_TYPE_PLUGIN -} _ChartTypeEnumPI; - -// ChartFamily constants -typedef enum ChartFamilyEnumPI { - PI_CHART_FAMILY_UNKNOWN = 0, - PI_CHART_FAMILY_RASTER, - PI_CHART_FAMILY_VECTOR, - PI_CHART_FAMILY_DONTCARE -} _ChartFamilyEnumPI; - -// Depth unit type enum -typedef enum ChartDepthUnitTypePI { - PI_DEPTH_UNIT_UNKNOWN, - PI_DEPTH_UNIT_FEET, - PI_DEPTH_UNIT_METERS, - PI_DEPTH_UNIT_FATHOMS -} _ChartDepthUnitTypePI; - -// Projection type enum -typedef enum OcpnProjTypePI { - PI_PROJECTION_UNKNOWN, - PI_PROJECTION_MERCATOR, - PI_PROJECTION_TRANSVERSE_MERCATOR, - PI_PROJECTION_POLYCONIC, - - PI_PROJECTION_ORTHOGRAPHIC, - PI_PROJECTION_POLAR, - PI_PROJECTION_STEREOGRAPHIC, - PI_PROJECTION_GNOMONIC, - PI_PROJECTION_EQUIRECTANGULAR -} _OcpnProjTypePI; - -typedef struct _ExtentPI { - double SLAT; - double WLON; - double NLAT; - double ELON; -} ExtentPI; - -// PlugInChartBase::Init() init_flags constants -#define PI_FULL_INIT 0 -#define PI_HEADER_ONLY 1 -#define PI_THUMB_ONLY 2 - -// ---------------------------------------------------------------------------- -// PlugInChartBase -// This class is the base class for Plug-able chart types -// ---------------------------------------------------------------------------- - -class DECL_EXP PlugInChartBase : public wxObject { -public: - // These methods Must be overriden in any derived class - PlugInChartBase(); - virtual ~PlugInChartBase(); - - virtual wxString GetFileSearchMask(void); - - virtual int Init(const wxString &full_path, int init_flags); - virtual void SetColorScheme(int cs, bool bApplyImmediate); - - virtual double GetNormalScaleMin(double canvas_scale_factor, - bool b_allow_overzoom); - virtual double GetNormalScaleMax(double canvas_scale_factor, - int canvas_width); - virtual double GetNearestPreferredScalePPM(double target_scale_ppm); - - virtual bool GetChartExtent(ExtentPI *pext); - - virtual wxBitmap &RenderRegionView(const PlugIn_ViewPort &VPoint, - const wxRegion &Region); - - virtual bool AdjustVP(PlugIn_ViewPort &vp_last, PlugIn_ViewPort &vp_proposed); - - virtual void GetValidCanvasRegion(const PlugIn_ViewPort &VPoint, - wxRegion *pValidRegion); - - virtual int GetCOVREntries() { return 0; } - virtual int GetCOVRTablePoints(int iTable) { return 0; } - virtual int GetCOVRTablenPoints(int iTable) { return 0; } - virtual float *GetCOVRTableHead(int iTable) { return (float *)NULL; } - - virtual wxBitmap *GetThumbnail(int tnx, int tny, int cs); - - // Accessors, need not be overridden in derived class if the member - // variables are maintained - virtual wxString GetFullPath() const { return m_FullPath; } - virtual ChartTypeEnumPI GetChartType() { return m_ChartType; } - virtual ChartFamilyEnumPI GetChartFamily() { return m_ChartFamily; } - virtual OcpnProjTypePI GetChartProjection() { return m_projection; } - virtual wxString GetName() { return m_Name; } - virtual wxString GetDescription() { return m_Description; } - virtual wxString GetID() { return m_ID; } - virtual wxString GetSE() { return m_SE; } - virtual wxString GetDepthUnits() { return m_DepthUnits; } - virtual wxString GetSoundingsDatum() { return m_SoundingsDatum; } - virtual wxString GetDatumString() { return m_datum_str; } - virtual wxString GetExtraInfo() { return m_ExtraInfo; } - virtual wxString GetPubDate() { return m_PubYear; } - virtual double GetChartErrorFactor() { return m_Chart_Error_Factor; } - virtual ChartDepthUnitTypePI GetDepthUnitId() { return m_depth_unit_id; } - virtual bool IsReadyToRender() { return m_bReadyToRender; } - virtual int GetNativeScale() { return m_Chart_Scale; }; - virtual double GetChartSkew() { return m_Chart_Skew; } - virtual wxDateTime GetEditionDate(void) { return m_EdDate; } - - // Methods pertaining to CHART_FAMILY_RASTER type PlugIn charts only - virtual void ComputeSourceRectangle(const PlugIn_ViewPort &vp, - wxRect *pSourceRect); - virtual double GetRasterScaleFactor(); - virtual bool GetChartBits(wxRect &source, unsigned char *pPix, int sub_samp); - virtual int GetSize_X(); - virtual int GetSize_Y(); - virtual void latlong_to_chartpix(double lat, double lon, double &pixx, - double &pixy); - virtual void chartpix_to_latlong(double pixx, double pixy, double *plat, - double *plon); - -protected: - ChartTypeEnumPI m_ChartType; - ChartFamilyEnumPI m_ChartFamily; - - wxString m_FullPath; - OcpnProjTypePI m_projection; - int m_Chart_Scale; - double m_Chart_Skew; - - wxDateTime m_EdDate; - bool m_bReadyToRender; - - wxString m_Name; - wxString m_Description; - wxString m_ID; - wxString m_SE; - wxString m_SoundingsDatum; - wxString m_datum_str; - wxString m_PubYear; - wxString m_DepthUnits; - wxString m_ExtraInfo; - - ChartDepthUnitTypePI m_depth_unit_id; - - double m_Chart_Error_Factor; -}; - -// Declare an array of PlugIn_AIS_Targets -WX_DEFINE_ARRAY_PTR(PlugIn_AIS_Target *, ArrayOfPlugIn_AIS_Targets); - -//---------------------------------------------------------------------------------------------------------- -// The Generic PlugIn Interface Class Definition -// -// This is a virtual class. -// opencpn PlugIns must derive from this class. -// There are two types of methods in this class -// a. Required...must be overridden and implemented by PlugIns -// b. Optional..may be overridden by PlugIns - -// PlugIns must implement optional method overrides consistent with their -// declared capabilities flag as returned by Init(). -//---------------------------------------------------------------------------------------------------------- -class DECL_EXP opencpn_plugin { -public: - opencpn_plugin(void *pmgr) {} - virtual ~opencpn_plugin(); - - // Public API to the PlugIn class - - // This group of methods is required, and will be called by the opencpn - // host opencpn PlugIns must implement this group - virtual int Init(void); // Return the PlugIn Capabilites flag - virtual bool DeInit(void); - - virtual int GetAPIVersionMajor(); - virtual int GetAPIVersionMinor(); - virtual int GetPlugInVersionMajor(); - virtual int GetPlugInVersionMinor(); - virtual wxBitmap *GetPlugInBitmap(); - - // These three methods should produce valid, meaningful strings always - // ---EVEN IF--- the PlugIn has not (yet) been initialized. - // They are used by the PlugInManager GUI - virtual wxString GetCommonName(); - virtual wxString GetShortDescription(); - virtual wxString GetLongDescription(); - - // This group is optional. - // PlugIns may override any of these methods as required - - virtual void SetDefaults( - void); // This will be called upon enabling a PlugIn via the user Dialog - // It gives a chance to setup any default options and behavior - - virtual int GetToolbarToolCount(void); - - virtual int GetToolboxPanelCount(void); - virtual void SetupToolboxPanel(int page_sel, wxNotebook *pnotebook); - virtual void OnCloseToolboxPanel(int page_sel, int ok_apply_cancel); - - virtual void ShowPreferencesDialog(wxWindow *parent); - - virtual bool RenderOverlay(wxMemoryDC *pmdc, PlugIn_ViewPort *vp); - virtual void SetCursorLatLon(double lat, double lon); - virtual void SetCurrentViewPort(PlugIn_ViewPort &vp); - - virtual void SetPositionFix(PlugIn_Position_Fix &pfix); - virtual void SetNMEASentence(wxString &sentence); - virtual void SetAISSentence(wxString &sentence); - - virtual void ProcessParentResize(int x, int y); - virtual void SetColorScheme(PI_ColorScheme cs); - - virtual void OnToolbarToolCallback(int id); - virtual void OnContextMenuItemCallback(int id); - - virtual void UpdateAuiStatus(void); - - virtual wxArrayString GetDynamicChartClassNameArray(void); -}; - -// the types of the class factories used to create PlugIn instances -typedef opencpn_plugin *create_t(void *); -typedef void destroy_t(opencpn_plugin *); - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Woverloaded-virtual" -#endif - -class DECL_EXP opencpn_plugin_16 : public opencpn_plugin { -public: - opencpn_plugin_16(void *pmgr); - virtual ~opencpn_plugin_16(); - - using opencpn_plugin::RenderOverlay; - - virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); - - virtual void SetPluginMessage(wxString &message_id, wxString &message_body); -}; - -class DECL_EXP opencpn_plugin_17 : public opencpn_plugin { -public: - opencpn_plugin_17(void *pmgr); - virtual ~opencpn_plugin_17(); - - using opencpn_plugin::RenderOverlay; - - virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); - virtual bool RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp); - - virtual void SetPluginMessage(wxString &message_id, wxString &message_body); -}; - -class DECL_EXP opencpn_plugin_18 : public opencpn_plugin { -public: - opencpn_plugin_18(void *pmgr); - virtual ~opencpn_plugin_18(); - - using opencpn_plugin::RenderOverlay; - - virtual bool RenderOverlay(wxDC &dc, PlugIn_ViewPort *vp); - virtual bool RenderGLOverlay(wxGLContext *pcontext, PlugIn_ViewPort *vp); - virtual void SetPluginMessage(wxString &message_id, wxString &message_body); - virtual void SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix); -}; - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -class DECL_EXP opencpn_plugin_19 : public opencpn_plugin_18 { -public: - opencpn_plugin_19(void *pmgr); - virtual ~opencpn_plugin_19(); - - virtual void OnSetupOptions(void); -}; - -class DECL_EXP opencpn_plugin_110 : public opencpn_plugin_19 { -public: - opencpn_plugin_110(void *pmgr); - virtual ~opencpn_plugin_110(); - - virtual void LateInit(void); // If WANTS_LATE_INIT is returned by Init() -}; - -class DECL_EXP opencpn_plugin_111 : public opencpn_plugin_110 { -public: - opencpn_plugin_111(void *pmgr); - virtual ~opencpn_plugin_111(); -}; - -class DECL_EXP opencpn_plugin_112 : public opencpn_plugin_111 { -public: - opencpn_plugin_112(void *pmgr); - virtual ~opencpn_plugin_112(); - - virtual bool MouseEventHook(wxMouseEvent &event); - virtual void SendVectorChartObjectInfo(wxString &chart, wxString &feature, - wxString &objname, double lat, - double lon, double scale, - int nativescale); -}; - -class DECL_EXP opencpn_plugin_113 : public opencpn_plugin_112 { -public: - opencpn_plugin_113(void *pmgr); - virtual ~opencpn_plugin_113(); - - virtual bool KeyboardEventHook(wxKeyEvent &event); - virtual void OnToolbarToolDownCallback(int id); - virtual void OnToolbarToolUpCallback(int id); -}; - -class DECL_EXP opencpn_plugin_114 : public opencpn_plugin_113 { -public: - opencpn_plugin_114(void *pmgr); - virtual ~opencpn_plugin_114(); -}; - -class DECL_EXP opencpn_plugin_115 : public opencpn_plugin_114 { -public: - opencpn_plugin_115(void *pmgr); - virtual ~opencpn_plugin_115(); -}; - -class DECL_EXP opencpn_plugin_116 : public opencpn_plugin_115 { -public: - opencpn_plugin_116(void *pmgr); - virtual ~opencpn_plugin_116(); - virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, - PlugIn_ViewPort *vp, int canvasIndex); - virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, - int canvasIndex); - virtual void PrepareContextMenu(int canvasIndex); -}; - -class DECL_EXP opencpn_plugin_117 : public opencpn_plugin_116 { -public: - opencpn_plugin_117(void *pmgr); - /* - * Forms a semantic version together with GetPlugInVersionMajor() and - * GetPlugInVersionMinor(), see https://semver.org/ - */ - virtual int GetPlugInVersionPatch(); - - /** Post-release version part, extends the semver spec. */ - virtual int GetPlugInVersionPost(); - - /** Pre-release tag version part, see GetPlugInVersionPatch() */ - virtual const char *GetPlugInVersionPre(); - - /** Build version part see GetPlugInVersionPatch(). */ - virtual const char *GetPlugInVersionBuild(); - - /*Provide active leg data to plugins*/ - virtual void SetActiveLegInfo(Plugin_Active_Leg_Info &leg_info); -}; - -class DECL_EXP opencpn_plugin_118 : public opencpn_plugin_117 { -public: - opencpn_plugin_118(void *pmgr); - - using opencpn_plugin_116::RenderGLOverlayMultiCanvas; - using opencpn_plugin_116::RenderOverlayMultiCanvas; - - /// Render plugin overlay over chart canvas in OpenGL mode - /// - /// \param pcontext Pointer to the OpenGL context - /// \param vp Pointer to the Viewport - /// \param canvasIndex Index of the chart canvas, 0 for the first canvas - /// \param priority Priority, plugins only upgrading from older API versions - /// should draw only when priority is OVERLAY_LEGACY (0) - /// \return true if overlay was rendered, false otherwise -#ifdef _MSC_VER - virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, - int canvasIndex, int priority = -1); -#else - virtual bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, - int canvasIndex, int priority); - - bool RenderGLOverlayMultiCanvas(wxGLContext *pcontext, PlugIn_ViewPort *vp, - int canvas_ix) override { - return RenderGLOverlayMultiCanvas(pcontext, vp, canvas_ix, -1); - } -#endif - - - - /// Render plugin overlay over chart canvas in non-OpenGL mode - /// - /// \param dc Reference to the "device context" - /// \param vp Pointer to the Viewport - /// \param canvasIndex Index of the chart canvas, 0 for the first canvas - /// \param priority Priority, plugins only upgrading from older API versions - /// should draw only when priority is OVERLAY_LEGACY (0) - /// \return true if overlay was rendered, false otherwise -#ifdef _MSC_VER - virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, - int canvasIndex, int priority = -1); -#else - virtual bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, int canvas_ix, - int priority); - bool RenderOverlayMultiCanvas(wxDC &dc, PlugIn_ViewPort *vp, - int canvas_ix) override { - return RenderOverlayMultiCanvas(dc, vp, canvas_ix, -1); - } -#endif - -}; -//------------------------------------------------------------------ -// Route and Waypoint PlugIn support -// -//------------------------------------------------------------------ - -class DECL_EXP Plugin_Hyperlink { -public: - wxString DescrText; - wxString Link; - wxString Type; -}; - -WX_DECLARE_LIST(Plugin_Hyperlink, Plugin_HyperlinkList); - -class DECL_EXP PlugIn_Waypoint { -public: - PlugIn_Waypoint(); - PlugIn_Waypoint(double lat, double lon, const wxString &icon_ident, - const wxString &wp_name, const wxString &GUID = _T("")); - ~PlugIn_Waypoint(); - - double m_lat; - double m_lon; - - wxString m_GUID; - - wxString m_MarkName; - wxString m_MarkDescription; - wxDateTime m_CreateTime; - bool m_IsVisible; - - wxString m_IconName; - - Plugin_HyperlinkList *m_HyperlinkList; -}; - -WX_DECLARE_LIST(PlugIn_Waypoint, Plugin_WaypointList); - -class DECL_EXP PlugIn_Route { -public: - PlugIn_Route(void); - ~PlugIn_Route(void); - - wxString m_NameString; - wxString m_StartString; - wxString m_EndString; - wxString m_GUID; - - Plugin_WaypointList *pWaypointList; -}; - -class DECL_EXP PlugIn_Track { -public: - PlugIn_Track(void); - ~PlugIn_Track(void); - - wxString m_NameString; - wxString m_StartString; - wxString m_EndString; - wxString m_GUID; - - Plugin_WaypointList *pWaypointList; -}; - -//---------------------------------------------------------------------------------------------------------- -// The PlugIn CallBack API Definition -// -// The API back up to the PlugIn Manager -// PlugIns may call these static functions as necessary for system services -// -//---------------------------------------------------------------------------------------------------------- - -extern "C" DECL_EXP int InsertPlugInTool(wxString label, wxBitmap *bitmap, - wxBitmap *bmpRollover, wxItemKind kind, - wxString shortHelp, wxString longHelp, - wxObject *clientData, int position, - int tool_sel, opencpn_plugin *pplugin); -extern "C" DECL_EXP void RemovePlugInTool(int tool_id); -extern "C" DECL_EXP void SetToolbarToolViz( - int item, bool viz); // Temporarily change toolbar tool viz -extern "C" DECL_EXP void SetToolbarItemState(int item, bool toggle); -extern "C" DECL_EXP void SetToolbarToolBitmaps(int item, wxBitmap *bitmap, - wxBitmap *bmpRollover); - -extern "C" DECL_EXP int InsertPlugInToolSVG( - wxString label, wxString SVGfile, wxString SVGfileRollover, - wxString SVGfileToggled, wxItemKind kind, wxString shortHelp, - wxString longHelp, wxObject *clientData, int position, int tool_sel, - opencpn_plugin *pplugin); -extern "C" DECL_EXP void SetToolbarToolBitmapsSVG(int item, wxString SVGfile, - wxString SVGfileRollover, - wxString SVGfileToggled); - -extern "C" DECL_EXP int AddCanvasContextMenuItem(wxMenuItem *pitem, - opencpn_plugin *pplugin); -extern "C" DECL_EXP void RemoveCanvasContextMenuItem( - int item); // Fully remove this item -extern "C" DECL_EXP void SetCanvasContextMenuItemViz( - int item, bool viz); // Temporarily change context menu ptions -extern "C" DECL_EXP void SetCanvasContextMenuItemGrey(int item, bool grey); - -extern "C" DECL_EXP wxFileConfig *GetOCPNConfigObject(void); - -extern "C" DECL_EXP void RequestRefresh(wxWindow *); -extern "C" DECL_EXP bool GetGlobalColor(wxString colorName, wxColour *pcolour); - -extern "C" DECL_EXP void GetCanvasPixLL(PlugIn_ViewPort *vp, wxPoint *pp, - double lat, double lon); -extern "C" DECL_EXP void GetCanvasLLPix(PlugIn_ViewPort *vp, wxPoint p, - double *plat, double *plon); - -extern "C" DECL_EXP wxWindow *GetOCPNCanvasWindow(); - -extern "C" DECL_EXP wxFont *OCPNGetFont(wxString TextElement, int default_size); - -extern "C" DECL_EXP wxString *GetpSharedDataLocation(); - -extern "C" DECL_EXP ArrayOfPlugIn_AIS_Targets *GetAISTargetArray(void); - -extern "C" DECL_EXP wxAuiManager *GetFrameAuiManager(void); - -extern "C" DECL_EXP bool AddLocaleCatalog(wxString catalog); - -extern "C" DECL_EXP void PushNMEABuffer(wxString str); - -extern DECL_EXP wxXmlDocument GetChartDatabaseEntryXML(int dbIndex, - bool b_getGeom); - -extern DECL_EXP bool UpdateChartDBInplace(wxArrayString dir_array, - bool b_force_update, - bool b_ProgressDialog); -extern DECL_EXP wxArrayString GetChartDBDirArrayString(); - -extern "C" DECL_EXP void SendPluginMessage(wxString message_id, - wxString message_body); - -extern "C" DECL_EXP void DimeWindow(wxWindow *); - -extern "C" DECL_EXP void JumpToPosition(double lat, double lon, double scale); - -/* API 1.9 adds some common cartographic functions to avoid unnecessary code - * duplication */ -/* Study the original OpenCPN source (georef.c) for functional definitions */ - -extern "C" DECL_EXP void PositionBearingDistanceMercator_Plugin( - double lat, double lon, double brg, double dist, double *dlat, - double *dlon); -extern "C" DECL_EXP void DistanceBearingMercator_Plugin( - double lat0, double lon0, double lat1, double lon1, double *brg, - double *dist); -extern "C" DECL_EXP double DistGreatCircle_Plugin(double slat, double slon, - double dlat, double dlon); - -extern "C" DECL_EXP void toTM_Plugin(float lat, float lon, float lat0, - float lon0, double *x, double *y); -extern "C" DECL_EXP void fromTM_Plugin(double x, double y, double lat0, - double lon0, double *lat, double *lon); -extern "C" DECL_EXP void toSM_Plugin(double lat, double lon, double lat0, - double lon0, double *x, double *y); -extern "C" DECL_EXP void fromSM_Plugin(double x, double y, double lat0, - double lon0, double *lat, double *lon); -extern "C" DECL_EXP void toSM_ECC_Plugin(double lat, double lon, double lat0, - double lon0, double *x, double *y); -extern "C" DECL_EXP void fromSM_ECC_Plugin(double x, double y, double lat0, - double lon0, double *lat, - double *lon); - -extern "C" DECL_EXP bool DecodeSingleVDOMessage(const wxString &str, - PlugIn_Position_Fix_Ex *pos, - wxString *acc); -extern "C" DECL_EXP int GetChartbarHeight(void); -extern "C" DECL_EXP bool GetActiveRoutepointGPX(char *buffer, - unsigned int buffer_length); - -/* API 1.9 */ -typedef enum OptionsParentPI { - PI_OPTIONS_PARENT_DISPLAY, - PI_OPTIONS_PARENT_CONNECTIONS, - PI_OPTIONS_PARENT_CHARTS, - PI_OPTIONS_PARENT_SHIPS, - PI_OPTIONS_PARENT_UI, - PI_OPTIONS_PARENT_PLUGINS -} _OptionsParentPI; -extern DECL_EXP wxScrolledWindow *AddOptionsPage(OptionsParentPI parent, - wxString title); -extern DECL_EXP bool DeleteOptionsPage(wxScrolledWindow *page); - -/* API 1.10 */ - -/* API 1.10 adds some common functions to avoid unnecessary code duplication */ -/* Study the original OpenCPN source for functional definitions */ -extern "C" DECL_EXP double toUsrDistance_Plugin(double nm_distance, - int unit = -1); -extern "C" DECL_EXP double fromUsrDistance_Plugin(double usr_distance, - int unit = -1); -extern "C" DECL_EXP double toUsrSpeed_Plugin(double kts_speed, int unit = -1); -extern "C" DECL_EXP double fromUsrSpeed_Plugin(double usr_speed, int unit = -1); -extern "C" DECL_EXP double toUsrTemp_Plugin(double cel_temp, int unit = -1); -extern "C" DECL_EXP double fromUsrTemp_Plugin(double usr_temp, int unit = -1); -extern DECL_EXP wxString getUsrDistanceUnit_Plugin(int unit = -1); -extern DECL_EXP wxString getUsrSpeedUnit_Plugin(int unit = -1); -extern DECL_EXP wxString getUsrTempUnit_Plugin(int unit = -1); -extern DECL_EXP wxString GetNewGUID(); -extern "C" DECL_EXP bool PlugIn_GSHHS_CrossesLand(double lat1, double lon1, - double lat2, double lon2); -/** - * Start playing a sound file asynchronously. Supported formats depends - * on sound backend. - */ -extern DECL_EXP void PlugInPlaySound(wxString &sound_file); - -// API 1.10 Route and Waypoint Support -extern DECL_EXP wxBitmap *FindSystemWaypointIcon(wxString &icon_name); -extern DECL_EXP bool AddCustomWaypointIcon(wxBitmap *pimage, wxString key, - wxString description); - -extern DECL_EXP bool AddSingleWaypoint(PlugIn_Waypoint *pwaypoint, - bool b_permanent = true); -extern DECL_EXP bool DeleteSingleWaypoint(wxString &GUID); -extern DECL_EXP bool UpdateSingleWaypoint(PlugIn_Waypoint *pwaypoint); - -extern DECL_EXP bool AddPlugInRoute(PlugIn_Route *proute, - bool b_permanent = true); -extern DECL_EXP bool DeletePlugInRoute(wxString &GUID); -extern DECL_EXP bool UpdatePlugInRoute(PlugIn_Route *proute); - -extern DECL_EXP bool AddPlugInTrack(PlugIn_Track *ptrack, - bool b_permanent = true); -extern DECL_EXP bool DeletePlugInTrack(wxString &GUID); -extern DECL_EXP bool UpdatePlugInTrack(PlugIn_Track *ptrack); - -/* API 1.11 */ - -/* API 1.11 adds some more common functions to avoid unnecessary code - * duplication */ -wxColour DECL_EXP GetBaseGlobalColor(wxString colorName); -int DECL_EXP OCPNMessageBox_PlugIn(wxWindow *parent, const wxString &message, - const wxString &caption = _T("Message"), - int style = wxOK, int x = -1, int y = -1); - -extern DECL_EXP wxString toSDMM_PlugIn(int NEflag, double a, - bool hi_precision = true); - -extern "C" DECL_EXP wxString *GetpPrivateApplicationDataLocation(); -extern DECL_EXP wxString GetOCPN_ExePath(void); -extern "C" DECL_EXP wxString *GetpPlugInLocation(); -extern DECL_EXP wxString GetPlugInPath(opencpn_plugin *pplugin); - -extern "C" DECL_EXP int AddChartToDBInPlace(wxString &full_path, - bool b_RefreshCanvas); -extern "C" DECL_EXP int RemoveChartFromDBInPlace(wxString &full_path); -extern DECL_EXP wxString GetLocaleCanonicalName(); - -// API 1.11 adds access to S52 Presentation library -// Types - -// A flag field that defines the object capabilities passed by a chart to -// the S52 PLIB - -#define PLIB_CAPS_LINE_VBO 1 -#define PLIB_CAPS_LINE_BUFFER 1 << 1 -#define PLIB_CAPS_SINGLEGEO_BUFFER 1 << 2 -#define PLIB_CAPS_OBJSEGLIST 1 << 3 -#define PLIB_CAPS_OBJCATMUTATE 1 << 4 - -class PI_S57Obj; - -WX_DECLARE_LIST(PI_S57Obj, ListOfPI_S57Obj); - -// ---------------------------------------------------------------------------- -// PlugInChartBaseGL -// Derived from PlugInChartBase, add OpenGL Vector chart support -// ---------------------------------------------------------------------------- - -class DECL_EXP PlugInChartBaseGL : public PlugInChartBase { -public: - PlugInChartBaseGL(); - virtual ~PlugInChartBaseGL(); - - virtual int RenderRegionViewOnGL(const wxGLContext &glc, - const PlugIn_ViewPort &VPoint, - const wxRegion &Region, bool b_use_stencil); - - virtual ListOfPI_S57Obj *GetObjRuleListAtLatLon(float lat, float lon, - float select_radius, - PlugIn_ViewPort *VPoint); - virtual wxString CreateObjDescriptions(ListOfPI_S57Obj *obj_list); - - virtual int GetNoCOVREntries(); - virtual int GetNoCOVRTablePoints(int iTable); - virtual int GetNoCOVRTablenPoints(int iTable); - virtual float *GetNoCOVRTableHead(int iTable); -}; - -// ---------------------------------------------------------------------------- -// PlugInChartBaseGLPlus2 -// Derived from PlugInChartBaseGL, add additional chart management methods -// ---------------------------------------------------------------------------- - -class DECL_EXP PlugInChartBaseGLPlus2 : public PlugInChartBaseGL { -public: - PlugInChartBaseGLPlus2(); - virtual ~PlugInChartBaseGLPlus2(); - - virtual ListOfPI_S57Obj *GetLightsObjRuleListVisibleAtLatLon( - float lat, float lon, PlugIn_ViewPort *VPoint); -}; - -// ---------------------------------------------------------------------------- -// PlugInChartBaseExtended -// Derived from PlugInChartBase, add extended chart support methods -// ---------------------------------------------------------------------------- - -class DECL_EXP PlugInChartBaseExtended : public PlugInChartBase { -public: - PlugInChartBaseExtended(); - virtual ~PlugInChartBaseExtended(); - - virtual int RenderRegionViewOnGL(const wxGLContext &glc, - const PlugIn_ViewPort &VPoint, - const wxRegion &Region, bool b_use_stencil); - - virtual wxBitmap &RenderRegionViewOnDCNoText(const PlugIn_ViewPort &VPoint, - const wxRegion &Region); - virtual bool RenderRegionViewOnDCTextOnly(wxMemoryDC &dc, - const PlugIn_ViewPort &VPoint, - const wxRegion &Region); - - virtual int RenderRegionViewOnGLNoText(const wxGLContext &glc, - const PlugIn_ViewPort &VPoint, - const wxRegion &Region, - bool b_use_stencil); - - virtual int RenderRegionViewOnGLTextOnly(const wxGLContext &glc, - const PlugIn_ViewPort &VPoint, - const wxRegion &Region, - bool b_use_stencil); - - virtual ListOfPI_S57Obj *GetObjRuleListAtLatLon(float lat, float lon, - float select_radius, - PlugIn_ViewPort *VPoint); - virtual wxString CreateObjDescriptions(ListOfPI_S57Obj *obj_list); - - virtual int GetNoCOVREntries(); - virtual int GetNoCOVRTablePoints(int iTable); - virtual int GetNoCOVRTablenPoints(int iTable); - virtual float *GetNoCOVRTableHead(int iTable); - - virtual void ClearPLIBTextList(); -}; - -// ---------------------------------------------------------------------------- -// PlugInChartBaseExtendedPlus2 -// Derived from PlugInChartBaseExtended, add additional extended chart support -// methods -// ---------------------------------------------------------------------------- - -class DECL_EXP PlugInChartBaseExtendedPlus2 : public PlugInChartBaseExtended { -public: - PlugInChartBaseExtendedPlus2(); - virtual ~PlugInChartBaseExtendedPlus2(); - - virtual ListOfPI_S57Obj *GetLightsObjRuleListVisibleAtLatLon( - float lat, float lon, PlugIn_ViewPort *VPoint); -}; - -class wxArrayOfS57attVal; - -// name of the addressed look up table set (fifth letter) -typedef enum _PI_LUPname { - PI_SIMPLIFIED = 'L', // points - PI_PAPER_CHART = 'R', // points - PI_LINES = 'S', // lines - PI_PLAIN_BOUNDARIES = 'N', // areas - PI_SYMBOLIZED_BOUNDARIES = 'O', // areas - PI_LUPNAME_NUM = 5 -} PI_LUPname; - -// display category type -typedef enum _PI_DisCat { - PI_DISPLAYBASE = 'D', // - PI_STANDARD = 'S', // - PI_OTHER = 'O', // O for OTHER - PI_MARINERS_STANDARD = 'M', // Mariner specified - PI_MARINERS_OTHER, // value not defined - PI_DISP_CAT_NUM, // value not defined -} PI_DisCat; - -// Display Priority -typedef enum _PI_DisPrio { - PI_PRIO_NODATA = '0', // no data fill area pattern - PI_PRIO_GROUP1 = '1', // S57 group 1 filled areas - PI_PRIO_AREA_1 = '2', // superimposed areas - PI_PRIO_AREA_2 = '3', // superimposed areas also water features - PI_PRIO_SYMB_POINT = '4', // point symbol also land features - PI_PRIO_SYMB_LINE = '5', // line symbol also restricted areas - PI_PRIO_SYMB_AREA = '6', // area symbol also traffic areas - PI_PRIO_ROUTEING = '7', // routeing lines - PI_PRIO_HAZARDS = '8', // hazards - PI_PRIO_MARINERS = '9', // VRM, EBL, own ship - PI_PRIO_NUM = 10 // number of priority levels - -} PI_DisPrio; - -typedef enum PI_InitReturn { - PI_INIT_OK = 0, - PI_INIT_FAIL_RETRY, // Init failed, retry suggested - PI_INIT_FAIL_REMOVE, // Init failed, suggest remove from further use - PI_INIT_FAIL_NOERROR // Init failed, request no explicit error message -} _PI_InitReturn; - -class PI_line_segment_element { -public: - size_t vbo_offset; - size_t n_points; - int priority; - float lat_max; // segment bounding box - float lat_min; - float lon_max; - float lon_min; - int type; - void *private0; - - PI_line_segment_element *next; -}; - -class DECL_EXP PI_S57Obj { -public: - // Public Methods - PI_S57Obj(); - -public: - // Instance Data - char FeatureName[8]; - int Primitive_type; - - char *att_array; - wxArrayOfS57attVal *attVal; - int n_attr; - - int iOBJL; - int Index; - - double x; // for POINT - double y; - double z; - int npt; // number of points as needed by arrays - void *geoPt; // for LINE & AREA not described by PolyTessGeo - double *geoPtz; // an array[3] for MultiPoint, SM with Z, i.e. depth - double *geoPtMulti; // an array[2] for MultiPoint, lat/lon to make bbox - // of decomposed points - - void *pPolyTessGeo; - - double m_lat; // The lat/lon of the object's "reference" point - double m_lon; - - double chart_ref_lat; - double chart_ref_lon; - - double lat_min; - double lat_max; - double lon_min; - double lon_max; - - int Scamin; // SCAMIN attribute decoded during load - - bool bIsClone; - int nRef; // Reference counter, to signal OK for deletion - - bool bIsAton; // This object is an aid-to-navigation - bool bIsAssociable; // This object is DRGARE or DEPARE - - int m_n_lsindex; - int *m_lsindex_array; - int m_n_edge_max_points; - void *m_chart_context; - - PI_DisCat m_DisplayCat; - - void *S52_Context; - PI_S57Obj *child; // child list, used only for MultiPoint Soundings - - PI_S57Obj *next; // List linkage - - // This transform converts from object geometry - // to SM coordinates. - double x_rate; // These auxiliary transform coefficients are - double y_rate; // to be used in GetPointPix() and friends - double x_origin; // on a per-object basis if necessary - double y_origin; - - int auxParm0; // some per-object auxiliary parameters, used for OpenGL - int auxParm1; - int auxParm2; - int auxParm3; - - PI_line_segment_element *m_ls_list; - bool m_bcategory_mutable; - int m_DPRI; -}; - -wxString DECL_EXP PI_GetPLIBColorScheme(); -int DECL_EXP PI_GetPLIBDepthUnitInt(); -int DECL_EXP PI_GetPLIBSymbolStyle(); -int DECL_EXP PI_GetPLIBBoundaryStyle(); -int DECL_EXP PI_GetPLIBStateHash(); -double DECL_EXP PI_GetPLIBMarinerSafetyContour(); -bool DECL_EXP PI_GetObjectRenderBox(PI_S57Obj *pObj, double *lat_min, - double *lat_max, double *lon_min, - double *lon_max); -void DECL_EXP PI_UpdateContext(PI_S57Obj *pObj); - -bool DECL_EXP PI_PLIBObjectRenderCheck(PI_S57Obj *pObj, PlugIn_ViewPort *vp); -PI_LUPname DECL_EXP PI_GetObjectLUPName(PI_S57Obj *pObj); -PI_DisPrio DECL_EXP PI_GetObjectDisplayPriority(PI_S57Obj *pObj); -PI_DisCat DECL_EXP PI_GetObjectDisplayCategory(PI_S57Obj *pObj); -void DECL_EXP PI_PLIBSetLineFeaturePriority(PI_S57Obj *pObj, int prio); -void DECL_EXP PI_PLIBPrepareForNewRender(void); -void DECL_EXP PI_PLIBFreeContext(void *pContext); -void DECL_EXP PI_PLIBSetRenderCaps(unsigned int flags); - -bool DECL_EXP PI_PLIBSetContext(PI_S57Obj *pObj); - -int DECL_EXP PI_PLIBRenderObjectToDC(wxDC *pdc, PI_S57Obj *pObj, - PlugIn_ViewPort *vp); -int DECL_EXP PI_PLIBRenderAreaToDC(wxDC *pdc, PI_S57Obj *pObj, - PlugIn_ViewPort *vp, wxRect rect, - unsigned char *pixbuf); - -int DECL_EXP PI_PLIBRenderAreaToGL(const wxGLContext &glcc, PI_S57Obj *pObj, - PlugIn_ViewPort *vp, wxRect &render_rect); - -int DECL_EXP PI_PLIBRenderObjectToGL(const wxGLContext &glcc, PI_S57Obj *pObj, - PlugIn_ViewPort *vp, wxRect &render_rect); - -/* API 1.11 OpenGL Display List and vertex buffer object routines - - Effectively these two routines cancel each other so all - of the translation, scaling and rotation can be done by opengl. - - Display lists need only be built infrequently, but used in each frame - greatly accelerates the speed of rendering. This avoids costly calculations, - and also allows the vertexes to be stored in graphics memory. - - static int dl = 0; - glPushMatrix(); - PlugInMultMatrixViewport(current_viewport); - if(dl) - glCallList(dl); - else { - dl = glGenLists(1); - PlugInViewPort norm_viewport = current_viewport; - NormalizeViewPort(norm_viewport); - glNewList(dl, GL_COMPILE_AND_EXECUTE); - ... // use norm_viewport with GetCanvasLLPix here - glEndList(); - } - glPopMatrix(); - ... // use current_viewport with GetCanvasLLPix again -*/ - -extern DECL_EXP bool PlugInHasNormalizedViewPort(PlugIn_ViewPort *vp); -extern DECL_EXP void PlugInMultMatrixViewport(PlugIn_ViewPort *vp, - float lat = 0, float lon = 0); -extern DECL_EXP void PlugInNormalizeViewport(PlugIn_ViewPort *vp, float lat = 0, - float lon = 0); - -class wxPoint2DDouble; -extern "C" DECL_EXP void GetDoubleCanvasPixLL(PlugIn_ViewPort *vp, - wxPoint2DDouble *pp, double lat, - double lon); - -/* API 1.13 */ -/* API 1.13 adds some more common functions to avoid unnecessary code - * duplication */ - -extern DECL_EXP double fromDMM_Plugin(wxString sdms); -extern DECL_EXP void SetCanvasRotation(double rotation); -extern DECL_EXP void SetCanvasProjection(int projection); -extern DECL_EXP bool GetSingleWaypoint(wxString GUID, - PlugIn_Waypoint *pwaypoint); -extern DECL_EXP bool CheckEdgePan_PlugIn(int x, int y, bool dragging, - int margin, int delta); -extern DECL_EXP wxBitmap GetIcon_PlugIn(const wxString &name); -extern DECL_EXP void SetCursor_PlugIn(wxCursor *pPlugin_Cursor = NULL); -extern DECL_EXP wxFont *GetOCPNScaledFont_PlugIn(wxString TextElement, - int default_size = 0); -extern DECL_EXP wxFont GetOCPNGUIScaledFont_PlugIn(wxString item); -extern DECL_EXP double GetOCPNGUIToolScaleFactor_PlugIn(int GUIScaledFactor); -extern DECL_EXP double GetOCPNGUIToolScaleFactor_PlugIn(); -extern DECL_EXP float GetOCPNChartScaleFactor_Plugin(); -extern DECL_EXP wxColour GetFontColour_PlugIn(wxString TextElement); - -extern DECL_EXP double GetCanvasTilt(); -extern DECL_EXP void SetCanvasTilt(double tilt); - -/** - * Start playing a sound file asynchronously. Supported formats depends - * on sound backend. The deviceIx is only used on platforms using the - * portaudio sound backend where -1 indicates the default device. - */ -extern DECL_EXP bool PlugInPlaySoundEx(wxString &sound_file, - int deviceIndex = -1); -extern DECL_EXP void AddChartDirectory(wxString &path); -extern DECL_EXP void ForceChartDBUpdate(); -extern DECL_EXP void ForceChartDBRebuild(); - -extern DECL_EXP wxString GetWritableDocumentsDir(void); -extern DECL_EXP wxDialog *GetActiveOptionsDialog(); -extern DECL_EXP wxArrayString GetWaypointGUIDArray(void); -extern DECL_EXP wxArrayString GetIconNameArray(void); - -extern DECL_EXP bool AddPersistentFontKey(wxString TextElement); -extern DECL_EXP wxString GetActiveStyleName(); - -extern DECL_EXP wxBitmap GetBitmapFromSVGFile(wxString filename, - unsigned int width, - unsigned int height); -extern DECL_EXP bool IsTouchInterface_PlugIn(void); - -/* Platform optimized File/Dir selector dialogs */ -extern DECL_EXP int PlatformDirSelectorDialog(wxWindow *parent, - wxString *file_spec, - wxString Title, wxString initDir); - -extern DECL_EXP int PlatformFileSelectorDialog(wxWindow *parent, - wxString *file_spec, - wxString Title, wxString initDir, - wxString suggestedName, - wxString wildcard); - -/* OpenCPN HTTP File Download PlugIn Interface */ - -/* Various method Return Codes, etc */ -typedef enum _OCPN_DLStatus { - OCPN_DL_UNKNOWN = -1, - OCPN_DL_NO_ERROR = 0, - OCPN_DL_FAILED = 1, - OCPN_DL_ABORTED = 2, - OCPN_DL_USER_TIMEOUT = 4, - OCPN_DL_STARTED = 8 -} OCPN_DLStatus; - -typedef enum _OCPN_DLCondition { - OCPN_DL_EVENT_TYPE_UNKNOWN = -1, - OCPN_DL_EVENT_TYPE_START = 80, - OCPN_DL_EVENT_TYPE_PROGRESS = 81, - OCPN_DL_EVENT_TYPE_END = 82 -} OCPN_DLCondition; - -// Style definitions for Synchronous file download modal dialogs, if -// desired. Abstracted from wxCURL package -enum OCPN_DLDialogStyle { - OCPN_DLDS_ELAPSED_TIME = 0x0001, //!< The dialog shows the elapsed time. - OCPN_DLDS_ESTIMATED_TIME = - 0x0002, //!< The dialog shows the estimated total time. - OCPN_DLDS_REMAINING_TIME = 0x0004, //!< The dialog shows the remaining time. - OCPN_DLDS_SPEED = 0x0008, //!< The dialog shows the transfer speed. - OCPN_DLDS_SIZE = 0x0010, //!< The dialog shows the size of the resource to - //!< download/upload. - OCPN_DLDS_URL = - 0x0020, //!< The dialog shows the URL involved in the transfer. - - // styles related to the use of wxCurlConnectionSettingsDialog: - - OCPN_DLDS_CONN_SETTINGS_AUTH = - 0x0040, //!< The dialog allows the user to change the authentication - //!< settings. - OCPN_DLDS_CONN_SETTINGS_PORT = 0x0080, //!< The dialog allows the user to - //!< change the port for the transfer. - OCPN_DLDS_CONN_SETTINGS_PROXY = - 0x0100, //!< The dialog allows the user to change the proxy settings. - - OCPN_DLDS_CONN_SETTINGS_ALL = OCPN_DLDS_CONN_SETTINGS_AUTH | - OCPN_DLDS_CONN_SETTINGS_PORT | - OCPN_DLDS_CONN_SETTINGS_PROXY, - - OCPN_DLDS_SHOW_ALL = OCPN_DLDS_ELAPSED_TIME | OCPN_DLDS_ESTIMATED_TIME | - OCPN_DLDS_REMAINING_TIME | OCPN_DLDS_SPEED | - OCPN_DLDS_SIZE | OCPN_DLDS_URL | - OCPN_DLDS_CONN_SETTINGS_ALL, - - OCPN_DLDS_CAN_ABORT = 0x0200, //!< The transfer can be aborted by the user. - OCPN_DLDS_CAN_START = 0x0400, //!< The transfer won't start automatically. - //!< The user needs to start it. - OCPN_DLDS_CAN_PAUSE = 0x0800, //!< The transfer can be paused. - - OCPN_DLDS_AUTO_CLOSE = - 0x1000, //!< The dialog auto closes when transfer is complete. - - // by default all available features are enabled: - OCPN_DLDS_DEFAULT_STYLE = OCPN_DLDS_CAN_START | OCPN_DLDS_CAN_PAUSE | - OCPN_DLDS_CAN_ABORT | OCPN_DLDS_SHOW_ALL | - OCPN_DLDS_AUTO_CLOSE -}; - -#define ONLINE_CHECK_RETRY \ - 30 // Recheck the Internet connection availability every ONLINE_CHECK_RETRY s - -/* Synchronous (Blocking) download of a single file */ - -extern DECL_EXP _OCPN_DLStatus OCPN_downloadFile( - const wxString &url, const wxString &outputFile, const wxString &title, - const wxString &message, const wxBitmap &bitmap, wxWindow *parent, - long style, int timeout_secs); - -/* Asynchronous (Background) download of a single file */ - -extern DECL_EXP _OCPN_DLStatus -OCPN_downloadFileBackground(const wxString &url, const wxString &outputFile, - wxEvtHandler *handler, long *handle); - -extern DECL_EXP void OCPN_cancelDownloadFileBackground(long handle); - -/* Synchronous (Blocking) HTTP POST operation for small amounts of data */ - -extern DECL_EXP _OCPN_DLStatus OCPN_postDataHttp(const wxString &url, - const wxString ¶meters, - wxString &result, - int timeout_secs); - -/* Check whether connection to the Internet is working */ - -extern DECL_EXP bool OCPN_isOnline(); - -/* Supporting Event for Background downloading */ -/* OCPN_downloadEvent Definition */ - -/* PlugIn should be ready/able to handle this event after initiating a - * background file transfer - * - * The event as received should be parsed primarily by the getDLEventCondition() - * method. This will allow identification of download start, progress, and end - * states. - * - * Other accessor methods contain status, byte counts, etc. - * - * A PlugIn may safely destroy its EvtHandler after receipt of an - * OCPN_downloadEvent with getDLEventCondition == OCPN_DL_EVENT_TYPE_END - */ - -class DECL_EXP OCPN_downloadEvent : public wxEvent { -public: - OCPN_downloadEvent(wxEventType commandType = wxEVT_NULL, int id = 0); - ~OCPN_downloadEvent(); - - // accessors - _OCPN_DLStatus getDLEventStatus() { return m_stat; } - OCPN_DLCondition getDLEventCondition() { return m_condition; } - - void setDLEventStatus(_OCPN_DLStatus stat) { m_stat = stat; } - void setDLEventCondition(OCPN_DLCondition cond) { m_condition = cond; } - - void setTotal(long bytes) { m_totalBytes = bytes; } - void setTransferred(long bytes) { m_sofarBytes = bytes; } - long getTotal() { return m_totalBytes; } - long getTransferred() { return m_sofarBytes; } - - void setComplete(bool b_complete) { m_b_complete = b_complete; } - bool getComplete() { return m_b_complete; } - - // required for sending with wxPostEvent() - wxEvent *Clone() const; - -private: - OCPN_DLStatus m_stat; - OCPN_DLCondition m_condition; - - long m_totalBytes; - long m_sofarBytes; - bool m_b_complete; -}; - -// extern WXDLLIMPEXP_CORE const wxEventType wxEVT_DOWNLOAD_EVENT; - -#ifdef MAKING_PLUGIN -extern DECL_IMP wxEventType wxEVT_DOWNLOAD_EVENT; -#else -extern DECL_EXP wxEventType wxEVT_DOWNLOAD_EVENT; -#endif - -/* API 1.14 */ -/* API 1.14 adds some more common functions to avoid unnecessary code - * duplication */ - -bool LaunchDefaultBrowser_Plugin(wxString url); - -// API 1.14 Extra canvas Support - -/* Allow drawing of objects onto other OpenGL canvases */ -extern DECL_EXP void PlugInAISDrawGL(wxGLCanvas *glcanvas, - const PlugIn_ViewPort &vp); -extern DECL_EXP bool PlugInSetFontColor(const wxString TextElement, - const wxColour color); - -// API 1.15 -extern DECL_EXP double PlugInGetDisplaySizeMM(); - -// -extern DECL_EXP wxFont *FindOrCreateFont_PlugIn( - int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, - bool underline = false, const wxString &facename = wxEmptyString, - wxFontEncoding encoding = wxFONTENCODING_DEFAULT); - -extern DECL_EXP int PlugInGetMinAvailableGshhgQuality(); -extern DECL_EXP int PlugInGetMaxAvailableGshhgQuality(); - -extern DECL_EXP void PlugInHandleAutopilotRoute(bool enable); - -// API 1.16 -// -/** - * Return the plugin data directory for a given directory name. - * - * On Linux, the returned data path is an existing directory ending in - * "opencpn/plugins/" where the last part is the plugin_name - * argument. The prefix part is one of the directories listed in the - * environment variable XDG_DATA_DIRS, by default - * ~/.local/share:/usr/local/share:/usr/share. - * - * On other platforms, the returned value is GetSharedDataDir() + - * "/opencpn/plugins/" + plugin_name (with native path separators) - * if that path exists. - * - * Return "" if no existing directory is found. - */ -extern DECL_EXP wxString GetPluginDataDir(const char *plugin_name); - -extern DECL_EXP bool ShuttingDown(void); - -// Support for MUI MultiCanvas model - -extern DECL_EXP wxWindow *PluginGetFocusCanvas(); -extern DECL_EXP wxWindow *PluginGetOverlayRenderCanvas(); - -extern "C" DECL_EXP void CanvasJumpToPosition(wxWindow *canvas, double lat, - double lon, double scale); -extern "C" DECL_EXP int AddCanvasMenuItem(wxMenuItem *pitem, - opencpn_plugin *pplugin, - const char *name = ""); -extern "C" DECL_EXP void RemoveCanvasMenuItem( - int item, const char *name = ""); // Fully remove this item -extern "C" DECL_EXP void SetCanvasMenuItemViz( - int item, bool viz, - const char *name = ""); // Temporarily change context menu options -extern "C" DECL_EXP void SetCanvasMenuItemGrey(int item, bool grey, - const char *name = ""); - -// Extract waypoints, routes and tracks -extern DECL_EXP wxString GetSelectedWaypointGUID_Plugin(); -extern DECL_EXP wxString GetSelectedRouteGUID_Plugin(); -extern DECL_EXP wxString GetSelectedTrackGUID_Plugin(); - -extern DECL_EXP std::unique_ptr GetWaypoint_Plugin( - const wxString &); // doublon with GetSingleWaypoint -extern DECL_EXP std::unique_ptr GetRoute_Plugin(const wxString &); -extern DECL_EXP std::unique_ptr GetTrack_Plugin(const wxString &); - -extern DECL_EXP wxWindow *GetCanvasUnderMouse(); -extern DECL_EXP int GetCanvasIndexUnderMouse(); -// extern DECL_EXP std::vector GetCanvasArray(); -extern DECL_EXP wxWindow *GetCanvasByIndex(int canvasIndex); -extern DECL_EXP int GetCanvasCount(); -extern DECL_EXP bool CheckMUIEdgePan_PlugIn(int x, int y, bool dragging, - int margin, int delta, - int canvasIndex); -extern DECL_EXP void SetMUICursor_PlugIn(wxCursor *pCursor, int canvasIndex); - -// API 1.17 -// -extern DECL_EXP wxRect GetMasterToolbarRect(); - -enum SDDMFORMAT { - DEGREES_DECIMAL_MINUTES = 0, - DECIMAL_DEGREES, - DEGREES_MINUTES_SECONDS, - END_SDDMFORMATS -}; - -extern DECL_EXP int GetLatLonFormat(void); - -// API 1.17 -extern "C" DECL_EXP void ZeroXTE(); - -// Extended Waypoint manipulation API -class DECL_EXP PlugIn_Waypoint_Ex { -public: - PlugIn_Waypoint_Ex(); - PlugIn_Waypoint_Ex(double lat, double lon, const wxString &icon_ident, - const wxString &wp_name, const wxString &GUID = "", - const double ScaMin = 1e9, const bool bNameVisible = false, - const int nRanges = 0, const double RangeDistance = 1.0, - const wxColor RangeColor = wxColor(255, 0, 0)); - ~PlugIn_Waypoint_Ex(); - void InitDefaults(); - - bool GetFSStatus(); // return "free standing" status - // To be a "free standing waypoint"(FSWP), - // the RoutePoint will have been created by GUI dropping - // a point; by importing a waypoint in a GPX file or by - // the AddSingleWaypoint API. - - int GetRouteMembershipCount(); // Return the number of routes to which this - // WP belongs - - double m_lat; - double m_lon; - - wxString m_GUID; - - wxString m_MarkName; - wxString m_MarkDescription; - wxDateTime m_CreateTime; - bool IsVisible; - bool IsActive; - - double scamin; - bool b_useScamin; - bool IsNameVisible; - int nrange_rings; - double RangeRingSpace; - wxColour RangeRingColor; - - wxString IconName; - wxString IconDescription; - - Plugin_HyperlinkList *m_HyperlinkList; -}; - -WX_DECLARE_LIST(PlugIn_Waypoint_Ex, Plugin_WaypointExList); - -class DECL_EXP PlugIn_Route_Ex { -public: - PlugIn_Route_Ex(void); - ~PlugIn_Route_Ex(void); - - wxString m_NameString; - wxString m_StartString; - wxString m_EndString; - wxString m_GUID; - bool m_isActive; - bool m_isVisible; - wxString m_Description; - - - Plugin_WaypointExList *pWaypointList; -}; - -extern DECL_EXP wxArrayString GetRouteGUIDArray(void); -extern DECL_EXP wxArrayString GetTrackGUIDArray(void); - -extern DECL_EXP bool GetSingleWaypointEx(wxString GUID, - PlugIn_Waypoint_Ex *pwaypoint); - -extern DECL_EXP bool AddSingleWaypointEx(PlugIn_Waypoint_Ex *pwaypoint, - bool b_permanent = true); -extern DECL_EXP bool UpdateSingleWaypointEx(PlugIn_Waypoint_Ex *pwaypoint); - -extern DECL_EXP bool AddPlugInRouteEx(PlugIn_Route_Ex *proute, - bool b_permanent = true); -extern DECL_EXP bool UpdatePlugInRouteEx(PlugIn_Route_Ex *proute); - -extern DECL_EXP std::unique_ptr GetWaypointEx_Plugin( - const wxString &); -extern DECL_EXP std::unique_ptr GetRouteEx_Plugin( - const wxString &); - -extern DECL_EXP wxString -GetActiveWaypointGUID(void); // if no active waypoint, returns wxEmptyString -extern DECL_EXP wxString -GetActiveRouteGUID(void); // if no active route, returns wxEmptyString - -// API 1.18 - -// Scaled display support, as on some GTK3 and Mac Retina devices -extern DECL_EXP double OCPN_GetDisplayContentScaleFactor(); - -// Scaled display support, on Windows devices -extern DECL_EXP double OCPN_GetWinDIPScaleFactor(); - -// Comm Priority query support -extern DECL_EXP std::vector GetPriorityMaps(); -extern DECL_EXP std::vector GetActivePriorityIdentifiers(); - -extern DECL_EXP int GetGlobalWatchdogTimoutSeconds(); - -typedef enum _OBJECT_LAYER_REQ { - OBJECTS_ALL = 0, - OBJECTS_NO_LAYERS, - OBJECTS_ONLY_LAYERS -} OBJECT_LAYER_REQ; - -// FIXME (dave) Implement these -extern DECL_EXP wxArrayString GetRouteGUIDArray(OBJECT_LAYER_REQ req); -extern DECL_EXP wxArrayString GetTrackGUIDArray(OBJECT_LAYER_REQ req); -extern DECL_EXP wxArrayString GetWaypointGUIDArray(OBJECT_LAYER_REQ req); - -/** listen-notify interface */ - -/* Listening to messages. */ -class ObservableListener; - -/** The event used by notify/listen. */ -class ObservedEvt; - -// This is a verbatim copy from observable_evt.h, don't define twice. -#ifndef OBSERVABLE_EVT_H -#define OBSERVABLE_EVT_H - -wxDECLARE_EVENT(obsNOTIFY, ObservedEvt); - -/** Adds a std::shared element to wxCommandEvent. */ -class ObservedEvt : public wxCommandEvent { -public: - ObservedEvt(wxEventType commandType = obsNOTIFY, int id = 0) - : wxCommandEvent(commandType, id) {} - ObservedEvt(const ObservedEvt &event) : wxCommandEvent(event) { - this->m_shared_ptr = event.m_shared_ptr; - } - - wxEvent *Clone() const { return new ObservedEvt(*this); } - - std::shared_ptr GetSharedPtr() const { return m_shared_ptr; } - - void SetSharedPtr(std::shared_ptr p) { m_shared_ptr = p; } - -private: - std::shared_ptr m_shared_ptr; -}; - -#endif // OBSERVABLE_EVT_H - -class ObservableListener; - -/** Facade for NavAddr2000. */ -struct NMEA2000Id { - const uint64_t id; - NMEA2000Id(int value) : id(static_cast(value)){}; -}; - -extern DECL_EXP std::shared_ptr GetListener( - NMEA2000Id id, wxEventType ev, wxEvtHandler *handler); - -/** Facade for NavAddr0183. */ -struct NMEA0183Id { - const std::string id; - NMEA0183Id(const std::string &s) : id(s){}; -}; - -extern DECL_EXP std::shared_ptr GetListener( - NMEA0183Id id, wxEventType ev, wxEvtHandler *handler); - -/** Facade for NavAddrSignalK. */ -struct SignalkId { - const std::string id; - SignalkId(const std::string &s) : id(s){}; -}; - -extern DECL_EXP std::shared_ptr GetListener( - SignalkId id, wxEventType ev, wxEvtHandler *handler); - -/** Return payload in a received n2000 message of type id in ev. */ -extern DECL_EXP std::vector GetN2000Payload(NMEA2000Id id, - ObservedEvt ev); - -/** - * Get SignalK status payload after receiving a message. - * @return pointer to a wxJSONValue map object. Typical usage: - * - * auto ptr = GetSignalkPayload(ev); - * const auto msg = *std::static_pointer_cast(payload); - * - * The map contains the following entries: - * - "Data": the parsed json message - * - "ErrorCount": int, the number of parsing errors - * - "WarningCount": int, the number of parsing warnings - * - "Errors": list of strings, error messages. - * - "Warnings": list of strings, warning messages.. - * - "Context": string, message context - * - "ContextSelf": string, own ship context. - */ -std::shared_ptr GetSignalkPayload(ObservedEvt ev); - -/** - * Return source identifier (iface) of a received n2000 message of type id - * in ev. - */ -extern DECL_EXP std::string GetN2000Source(NMEA2000Id id, ObservedEvt ev); - -/** Return payload in a received n0183 message of type id in ev. */ -extern DECL_EXP std::string GetN0183Payload(NMEA0183Id id, ObservedEvt ev); - -/** Facade for BasicNavDataMsg. */ -struct NavDataId { - const int type; - NavDataId() : type(0) {} -}; - -extern DECL_EXP std::shared_ptr GetListener( - NavDataId id, wxEventType ev, wxEvtHandler *handler); - -/** Available decoded data for plugins. */ -struct PluginNavdata { - double lat; - double lon; - double sog; - double cog; - double var; - double hdt; - time_t time; -}; - -/** Return BasicNavDataMsg decoded data available in ev */ -extern DECL_EXP PluginNavdata GetEventNavdata(ObservedEvt ev); - -/** Plugin API supporting direct access to comm drivers for output purposes */ -/* - * Plugins may access comm ports for direct output. - * The general program flow for a plugin may look something like this - * pseudo-code: - * 1. Plugin will query OCPN core for a list of active comm drivers. - * 2. Plugin will inspect the list, and query OCPN core for driver - * attributes. - * 3. Plugin will select a comm driver with appropriate attributes for output. - * 4. Plugin will register a list of PGNs expected to be transmitted - * (N2K specific) - * 5. Plugin may then send a payload buffer to a specific comm driver for - * output as soon as possible. - * - * The mechanism for specifying a particular comm driver uses the notion of - * "handles". Each active comm driver has an associated opaque handle, managed - * by OCPN core. All references by a plugin to a driver are by means of its - * handle. Handles should be considered to be "opaque", meaning that the exact - * contents of the handle are of no specific value to the plugin, and only - * have meaning to the OCPN core management of drivers. - */ - -/** Definition of OCPN DriverHandle */ -typedef std::string DriverHandle; - -/** Error return values */ - -typedef enum CommDriverResult { - RESULT_COMM_NO_ERROR = 0, - RESULT_COMM_INVALID_HANDLE, - RESULT_COMM_INVALID_PARMS, - RESULT_COMM_TX_ERROR, - RESULT_COMM_REGISTER_GATEWAY_ERROR, - RESULT_COMM_REGISTER_PGN_ERROR -} _CommDriverResult; - -/** Query OCPN core for a list of active drivers */ -extern DECL_EXP std::vector GetActiveDrivers(); - -/** Query a specific driver for attributes */ -/* Driver attributes are available from OCPN core as a hash map of - * tag->attribute pairs. There is a defined set of common tags guaranteed - * for every driver. Both tags and attributes are defined as std::string. - * Here is the list of common tag-attribute pairs. - * - * Tag Attribute definition - * ---------- -------------------- - * "protocol" Comm bus device protocol, such as "NMEA0183", "NMEA2000" - * - * - */ - -/** Query driver attributes */ -extern DECL_EXP const std::unordered_map - GetAttributes(DriverHandle handle); - -/* Writing to a specific driver */ - -/* Comm drivers on bus protocols other than NMEA2000 may write directly to the - * port * using a simple call. The physical write operation will be queued, - * and executed in order as bandwidth allows. - * Return value is number of bytes queued for transmission. - */ -extern DECL_EXP CommDriverResult WriteCommDriver( - DriverHandle handle, - const std::shared_ptr > &payload); - -/** Send a PGN message to an NMEA2000 address. */ -extern DECL_EXP CommDriverResult WriteCommDriverN2K( - DriverHandle handle, int PGN, int destinationCANAddress, int priority, - const std::shared_ptr > &payload); - -/** - * Special NMEA2000 requirements - * NMEA2000 bus protocol device management requires that devices writing on - * the bus must inform all bus listeners of the specific PGNs that may be - * transmitted by this device. Once configured, this bus management process - * will be handled transparently by the OCPN core drivers. It is only - * necessary for plugins wishing to write to the NMEA2000 bus to register the - * specific PGNs that they anticipate using, with the selected driver. - */ -extern DECL_EXP CommDriverResult RegisterTXPGNs(DriverHandle handle, - std::vector &pgn_list); - - -#endif //_PLUGIN_H_ diff --git a/libs/s52plib/src/s52plibGL.h b/libs/s52plib/src/s52plibGL.h index 1294a02426..60401c94d5 100644 --- a/libs/s52plib/src/s52plibGL.h +++ b/libs/s52plib/src/s52plibGL.h @@ -31,7 +31,7 @@ #ifndef _S52PLIBGL_H_ #define _S52PLIBGL_H_ -#include "config.h" +//#include "config.h" #include #include From fea57e1f62d9ee7d8d64b727213a2c7442d4b834 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Sat, 9 Dec 2023 14:29:25 +0100 Subject: [PATCH 58/63] aisdecoder: Fix some strncat warnings Mostly non-alarming, but at least in one case the compiler flags an actual problem about not terminated strings --- src/ais_decoder.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ais_decoder.cpp b/src/ais_decoder.cpp index b3fb22b627..d38957e4b7 100644 --- a/src/ais_decoder.cpp +++ b/src/ais_decoder.cpp @@ -667,6 +667,7 @@ bool AisDecoder::HandleN2K_129041( std::shared_ptr n2k_msg ){ data.AtoNName[34] = 0; strncpy(pTargetData->ShipName, data.AtoNName, SHIP_NAME_LEN - 1); + pTargetData->ShipName[sizeof(pTargetData->ShipName) - 1] = '\0'; pTargetData->b_nameValid = true; pTargetData->MID = 124; // Indicates a name from n2k pTargetData->Class = AIS_ATON; @@ -739,8 +740,9 @@ bool AisDecoder::HandleN2K_129794( std::shared_ptr n2k_msg ){ //Populate the target_data pTargetData->MMSI = mmsi; - Name[sizeof(Name) - 1] = 0; strncpy(pTargetData->ShipName, Name, SHIP_NAME_LEN - 1); + pTargetData->ShipName[sizeof(pTargetData->ShipName) - 1] = '\0'; + Name[sizeof(Name) - 1] = 0; pTargetData->b_nameValid = true; pTargetData->MID = 124; // Indicates a name from n2k @@ -754,9 +756,11 @@ bool AisDecoder::HandleN2K_129794( std::shared_ptr n2k_msg ){ pTargetData->Draft = Draught; pTargetData->IMO = IMOnumber; strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1); + pTargetData->CallSign[sizeof(pTargetData->CallSign) - 1] = '\0'; pTargetData->ShipType = (unsigned char)VesselType; - Destination[sizeof(Destination) - 1] = 0; strncpy(pTargetData->Destination, Destination, DESTINATION_LEN - 1); + pTargetData->Destination[sizeof(pTargetData->Destination) - 1] = '\0'; + Destination[sizeof(Destination) - 1] = 0; if (!N2kIsNA(ETAdate) && !N2kIsNA(ETAtime)) { long secs = (ETAdate * 24 * 3600) + wxRound(ETAtime); @@ -870,6 +874,7 @@ bool AisDecoder::HandleN2K_129810( std::shared_ptr n2k_msg ){ pTargetData->DimC = Beam - PosRefStbd; pTargetData->DimD = PosRefStbd; strncpy(pTargetData->CallSign, Callsign, CALL_SIGN_LEN - 1); + pTargetData->CallSign[sizeof(pTargetData->CallSign) - 1] = '\0'; pTargetData->ShipType = (unsigned char)VesselType; pSelectAIS->DeleteSelectablePoint((void *)(long)mmsi, SELTYPE_AISTARGET); From 28a07b00ca4ea6aeb30be3da0ed6c0d473016b06 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Fri, 8 Dec 2023 12:22:45 +0100 Subject: [PATCH 59/63] src: Remove unused and unbuildable files. --- include/s57mgr.h | 63 ------ include/wificlient.h | 166 -------------- src/compasswin.cpp | 320 --------------------------- src/ocpnhelper.c | 102 --------- src/s57mgr.cpp | 277 ------------------------ src/scrollingdialog.cpp | 466 ---------------------------------------- src/wificlient.cpp | 452 -------------------------------------- 7 files changed, 1846 deletions(-) delete mode 100644 include/s57mgr.h delete mode 100644 include/wificlient.h delete mode 100644 src/compasswin.cpp delete mode 100644 src/ocpnhelper.c delete mode 100644 src/s57mgr.cpp delete mode 100644 src/scrollingdialog.cpp delete mode 100644 src/wificlient.cpp diff --git a/include/s57mgr.h b/include/s57mgr.h deleted file mode 100644 index 02ca7a1204..0000000000 --- a/include/s57mgr.h +++ /dev/null @@ -1,63 +0,0 @@ -/****************************************************************************** - * - * Project: OpenCPN - * Purpose: S57 Chart Manager - * Author: David Register - * - *************************************************************************** - * Copyright (C) 2010 by David S. Register * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** - * - */ - -#ifndef __S57MGR_H__ -#define __S57MGR_H__ - -#include "gdal/ogrsf_frmts.h" -//---------------------------------------------------------------------------- -// Fwd Definitions -//---------------------------------------------------------------------------- - -bool ddfrecord_test(); - -//---------------------------------------------------------------------------- -// s57mgr -//---------------------------------------------------------------------------- -class s57mgr { -public: - s57mgr(const wxString &csv_dir); - ~s57mgr(); - - bool GetChartExtent(char *pFullPath, Extent *pext); - int GetChartScale(char *pFullPath); - - bool GetChartFirstM_COVR(char *pFullPath, OGRDataSource **pDS, - OGRFeature **pFeature, OGRLayer **pLayer, - int &catcov); - - bool GetChartNextM_COVR(OGRDataSource *pDS, OGRLayer *pLayer, - OGRFeature *pLastFeature, OGRFeature **pFeature, - int &catcov); - - wxString *GetCSVDir() { return pcsv_locn; } - -private: - wxString *pcsv_locn; -}; - -#endif diff --git a/include/wificlient.h b/include/wificlient.h deleted file mode 100644 index 7421c18cad..0000000000 --- a/include/wificlient.h +++ /dev/null @@ -1,166 +0,0 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: wifi Client Data Object - * Author: David Register - * - *************************************************************************** - * Copyright (C) 2010 by David S. Register * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - **************************************************************************/ - -#ifndef __WIFICLIENT_H__ -#define __WIFICLIENT_H__ - -// Include wxWindows' headers - -#include "wx/wxprec.h" - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif // precompiled header - -#include "dychart.h" - -#include "wx/socket.h" - -#ifdef __WXMSW__ -#include -#endif - -//---------------------------------------------------------------------------- -// constants -//---------------------------------------------------------------------------- -#define WIFI_TRANSMIT_DATA 0x42 // This is the request code -#define WIFI_TRANSMIT_DATA_EXT 'D' // Extended request code - -#define NLOCALSTORE 4 -#define N_AGEDEATH 5 -// Class declarations - -// The MY_FILETIME structure is a 64-bit value -// representing the number of 100-nanosecond -// intervals since January 1, 1601 (UTC). -// This is the format used in the NMEA server data packet -// sigh.... - -typedef struct { - unsigned int low; - unsigned int high; -} WiFiMyFileTime; - -class MyFrame; - -// A local structure for managing station scanning -typedef struct { - char ESSID[64]; - int sig_quality; - int secure; - bool bisvalid; - int age; - -} wifi_local_scan_data; - -//---------------------------------------------------------------------------- -// WIFIWindow -//---------------------------------------------------------------------------- - -class WIFIWindow : public wxWindow { -public: - WIFIWindow(wxFrame *frame, const wxString &WiFiServerName); - ~WIFIWindow(); - - void GetSource(wxString &source); - - // Stop/Start the Socket Client - // Used to prevent async interrupts at critical times - void Pause(void); - void UnPause(void); - -private: - void OnPaint(wxPaintEvent &event); - void OnActivate(wxActivateEvent &event); - void OnSocketEvent(wxSocketEvent &event); - void OnTimer1(wxTimerEvent &event); - void OnCloseWindow(wxCloseEvent &event); - void wxDTToMyFileTime(wxDateTime *SDT, WiFiMyFileTime *pFileTime); - void MyFileTimeTowxDT(WiFiMyFileTime *pFileTime, wxDateTime *SDT); - - wxIPV4address addr; - wxSocketClient *m_sock; - bool m_busy; - wxTimer Timer1; - MyFrame *parent_frame; - bool m_bRX; - wxString *m_pdata_server_string; - int m_watchtick; - int m_scan_interval_msec; - bool m_timer_active; - - wifi_local_scan_data station_data[NLOCALSTORE]; - - DECLARE_EVENT_TABLE() -}; - -typedef struct _WIFI_DATA_MSG1 { - int msg; - long time; // UNIX 64 bit time - long time1; -} WIFI_DATA_MSG1; - -//------------------------------------------------------------------------------------------------------------- -// -// WiFi Server Data Definitions -// -//------------------------------------------------------------------------------------------------------------- - -// WiFi server produces messages composed of wifi_scan_data structures -// in a byte buffer, on 256 byte boundaries. -// This allows extension of the data structures without radical changes to -// server protocol - -typedef struct { - char ESSID[64]; - int sig_quality; - int secure; - int channel; - sockaddr ap_addr; - int key_flags; - unsigned char mode; -} wifi_scan_data; - -#define SERVER_PORT 3000 // the wifid tcp/ip socket server port - -#define WIFI_DOG_TIMEOUT 5 - -//------------------------------------------------------------------------------------------------------------- -// -// A simple thread to test host name resolution without blocking the main -// thread -// -//------------------------------------------------------------------------------------------------------------- -class WIFIDNSTestThread : public wxThread { -public: - WIFIDNSTestThread(const wxString &name_or_ip); - ~WIFIDNSTestThread(); - void *Entry(); - -private: - wxString *m_pip; -}; - -#endif diff --git a/src/compasswin.cpp b/src/compasswin.cpp deleted file mode 100644 index 25950cb0bb..0000000000 --- a/src/compasswin.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/****************************************************************************** - * - * Project: OpenCPN - * Purpose: OpenCPN Main wxWidgets Program - * Author: David Register - * - *************************************************************************** - * Copyright (C) 2010 by David S. Register * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** - * - */ -#include "wx/wxprec.h" -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif // precompiled headers -#include "ocpn_types.h" -#include "compasswin.h" -#include "comm_vars.h" -#include "chcanv.h" -#include "styles.h" -#include "wx28compat.h" - -BEGIN_EVENT_TABLE(ocpnFloatingCompassWindow, wxWindow) -EVT_PAINT(ocpnFloatingCompassWindow::OnPaint) - EVT_LEFT_DOWN(ocpnFloatingCompassWindow::MouseEvent) END_EVENT_TABLE() - - extern ocpnStyle::StyleManager* g_StyleManager; -extern bool bGPSValid; -extern bool g_bCourseUp; -extern bool g_bskew_comp; -extern MyFrame* gFrame; - -ocpnFloatingCompassWindow::ocpnFloatingCompassWindow(wxWindow* parent) { - m_pparent = parent; - long wstyle = wxNO_BORDER | wxFRAME_NO_TASKBAR | wxFRAME_SHAPED; -#ifdef __WXMAC__ - wstyle |= wxSTAY_ON_TOP; -#endif - wxDialog::Create(parent, -1, _T(""), wxPoint(0, 0), wxSize(-1, -1), wstyle); - - ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle(); - _img_compass = style->GetIcon(_T("CompassRose")); - _img_gpsRed = style->GetIcon(_T("gpsRed")); - - m_rose_angle = -999; // force a refresh when first used - - m_pStatBoxToolStaticBmp = NULL; - - m_scale = 1.0; - SetSize(m_scale * - ((_img_compass.GetWidth() + _img_gpsRed.GetWidth()) + - style->GetCompassLeftMargin() * 2 + style->GetToolSeparation()), - m_scale * (_img_compass.GetHeight() + style->GetCompassTopMargin() + - style->GetCompassBottomMargin())); - - m_xoffset = style->GetCompassXOffset(); - m_yoffset = style->GetCompassYOffset(); -} - -ocpnFloatingCompassWindow::~ocpnFloatingCompassWindow() { - delete m_pStatBoxToolStaticBmp; -} - -void ocpnFloatingCompassWindow::OnPaint(wxPaintEvent& event) { - int width, height; - GetClientSize(&width, &height); - wxPaintDC dc(this); - - dc.DrawBitmap(m_StatBmp, 0, 0, false); -} - -void ocpnFloatingCompassWindow::MouseEvent(wxMouseEvent& event) { - gFrame->ToggleCourseUp(); -} - -void ocpnFloatingCompassWindow::SetColorScheme(ColorScheme cs) { - wxColour back_color = GetGlobalColor(_T("GREY2")); - - // Set background - SetBackgroundColour(back_color); - ClearBackground(); - - UpdateStatus(true); -} - -void ocpnFloatingCompassWindow::UpdateStatus(bool bnew) { - if (bnew) m_lastgpsIconName.Clear(); // force an update to occur - - wxBitmap statbmp = CreateBmp(bnew); - if (statbmp.IsOk()) m_StatBmp = statbmp; - - Show(); - Refresh(false); -} - -void ocpnFloatingCompassWindow::SetScaleFactor(float factor) { - // qDebug() << m_scale << factor; - ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle(); - - if (factor > 0.1) - m_scale = factor; - else - m_scale = 1.0; - - SetSize(m_scale * - ((_img_compass.GetWidth() + _img_gpsRed.GetWidth()) + - style->GetCompassLeftMargin() * 2 + style->GetToolSeparation()), - m_scale * (_img_compass.GetHeight() + style->GetCompassTopMargin() + - style->GetCompassBottomMargin())); -} - -wxBitmap ocpnFloatingCompassWindow::CreateBmp(bool newColorScheme) { - wxString gpsIconName; - ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle(); - - // In order to draw a horizontal compass window when the toolbar is vertical, - // we need to save away the sizes and backgrounds for the two icons. - - static wxBitmap compassBg, gpsBg; - static wxSize toolsize; - static int topmargin, leftmargin, radius; - - if (!compassBg.IsOk() || newColorScheme) { - int orient = style->GetOrientation(); - style->SetOrientation(wxTB_HORIZONTAL); - if (style->HasBackground()) { - compassBg = style->GetNormalBG(); - style->DrawToolbarLineStart(compassBg); - compassBg = style->SetBitmapBrightness(compassBg); - gpsBg = style->GetNormalBG(); - style->DrawToolbarLineEnd(gpsBg); - gpsBg = style->SetBitmapBrightness(gpsBg); - - if (fabs(m_scale - 1.0) > 0.1) { - wxImage bg_img = compassBg.ConvertToImage(); - bg_img.Rescale(compassBg.GetWidth() * m_scale, - compassBg.GetHeight() * m_scale, wxIMAGE_QUALITY_NORMAL); - compassBg = wxBitmap(bg_img); - - bg_img = gpsBg.ConvertToImage(); - bg_img.Rescale(gpsBg.GetWidth() * m_scale, gpsBg.GetHeight() * m_scale, - wxIMAGE_QUALITY_NORMAL); - gpsBg = wxBitmap(bg_img); - } - } - - leftmargin = style->GetCompassLeftMargin(); - topmargin = style->GetCompassTopMargin(); - toolsize = style->GetToolSize(); - toolsize.x *= 2; - radius = style->GetCompassCornerRadius(); - - if (orient) style->SetOrientation(wxTB_VERTICAL); - } - - bool b_need_refresh = false; - - if (bGPSValid) { - if (g_bSatValid) { - gpsIconName = _T("gps3Bar"); - if (g_SatsInView <= 8) gpsIconName = _T("gps2Bar"); - if (g_SatsInView <= 4) gpsIconName = _T("gps1Bar"); - if (g_SatsInView < 0) gpsIconName = _T("gpsGry"); - - } else - gpsIconName = _T("gpsGrn"); - } else - gpsIconName = _T("gpsRed"); - - if (m_lastgpsIconName != gpsIconName) b_need_refresh = true; - - double rose_angle = -999.; - - if ((fabs(cc1->GetVPRotation()) > .01) || (fabs(cc1->GetVPSkew()) > .01)) { - rose_angle = -cc1->GetVPRotation(); - - if (!g_bCourseUp && !g_bskew_comp) rose_angle -= cc1->GetVPSkew(); - - } else - rose_angle = 0.; - - if (fabs(m_rose_angle - rose_angle) > .1) b_need_refresh = true; - - if (b_need_refresh) { - wxBitmap StatBmp; - - StatBmp.Create( - m_scale * - ((_img_compass.GetWidth() + _img_gpsRed.GetWidth()) + - style->GetCompassLeftMargin() * 2 + style->GetToolSeparation()), - m_scale * (_img_compass.GetHeight() + style->GetCompassTopMargin() + - style->GetCompassBottomMargin())); - - if (StatBmp.IsOk()) { - wxMemoryDC mdc; - mdc.SelectObject(StatBmp); - mdc.SetBackground( - wxBrush(GetGlobalColor(_T("GREY2")), wxBRUSHSTYLE_SOLID)); - mdc.Clear(); - - mdc.SetPen(wxPen(GetGlobalColor(_T("UITX1")), 1)); - mdc.SetBrush( - wxBrush(GetGlobalColor(_T("UITX1")), wxBRUSHSTYLE_TRANSPARENT)); - - mdc.DrawRoundedRectangle(0, 0, StatBmp.GetWidth(), StatBmp.GetHeight(), - m_scale * style->GetCompassCornerRadius()); - - wxPoint offset(style->GetCompassLeftMargin(), - style->GetCompassTopMargin()); - - wxBitmap iconBm; - - // Build Compass Rose, rotated... - wxBitmap BMPRose; - wxPoint after_rotate; - - if (g_bCourseUp) - BMPRose = style->GetIcon(_T("CompassRose")); - else - BMPRose = style->GetIcon(_T("CompassRoseBlue")); - - if ((fabs(cc1->GetVPRotation()) > .01) || - (fabs(cc1->GetVPSkew()) > .01) || (fabs(m_scale - 1.0) > 0.1)) { - int width = BMPRose.GetWidth() * m_scale; - int height = BMPRose.GetHeight() * m_scale; - - wxImage rose_img = BMPRose.ConvertToImage(); - - if (fabs(m_scale - 1.0) > 0.1) - rose_img.Rescale(width, height, wxIMAGE_QUALITY_NORMAL); - - if (fabs(rose_angle) > 0.01) { - wxPoint rot_ctr(width / 2, height / 2); - wxImage rot_image = - rose_img.Rotate(rose_angle, rot_ctr, true, &after_rotate); - BMPRose = wxBitmap(rot_image).GetSubBitmap( - wxRect(-after_rotate.x, -after_rotate.y, width, height)); - } else - BMPRose = wxBitmap(rose_img); - } - - if (style->HasBackground()) { - iconBm = MergeBitmaps(compassBg, BMPRose, wxSize(0, 0)); - } else { - iconBm = BMPRose; - } - - mdc.DrawBitmap(iconBm, offset); - offset.x += iconBm.GetWidth(); - - m_rose_angle = rose_angle; - - // GPS Icon - wxBitmap gicon = style->GetIcon(gpsIconName); - if (fabs(m_scale - 1.0) > 0.1) { - int width = gicon.GetWidth() * m_scale; - int height = gicon.GetHeight() * m_scale; - - wxImage gps_img = gicon.ConvertToImage(); - gps_img.Rescale(width, height, wxIMAGE_QUALITY_NORMAL); - gicon = wxBitmap(gps_img); - } - - if (style->HasBackground()) { - iconBm = MergeBitmaps(gpsBg, gicon, wxSize(0, 0)); - } else { - iconBm = gicon; - } - mdc.DrawBitmap(iconBm, offset); - mdc.SelectObject(wxNullBitmap); - m_lastgpsIconName = gpsIconName; - } - -#if !defined(__WXMAC__) && !defined(__OCPN__ANDROID__) - if (style->marginsInvisible) { - m_MaskBmp = wxBitmap(StatBmp.GetWidth(), StatBmp.GetHeight()); - wxMemoryDC sdc(m_MaskBmp); - sdc.SetBackground(*wxWHITE_BRUSH); - sdc.Clear(); - sdc.SetBrush(*wxBLACK_BRUSH); - sdc.SetPen(*wxBLACK_PEN); - sdc.DrawRoundedRectangle(wxPoint(leftmargin, topmargin), toolsize, - radius); - sdc.SelectObject(wxNullBitmap); - SetShape(wxRegion(m_MaskBmp, *wxWHITE, 0)); - } else if (radius) { - m_MaskBmp = wxBitmap(GetSize().x, GetSize().y); - wxMemoryDC sdc(m_MaskBmp); - sdc.SetBackground(*wxWHITE_BRUSH); - sdc.Clear(); - sdc.SetBrush(*wxBLACK_BRUSH); - sdc.SetPen(*wxBLACK_PEN); - sdc.DrawRoundedRectangle(0, 0, m_MaskBmp.GetWidth(), - m_MaskBmp.GetHeight(), radius); - sdc.SelectObject(wxNullBitmap); - SetShape(wxRegion(m_MaskBmp, *wxWHITE, 0)); - } -#endif - - return StatBmp; - } - - else - return wxNullBitmap; -} diff --git a/src/ocpnhelper.c b/src/ocpnhelper.c deleted file mode 100644 index 9a771b22c0..0000000000 --- a/src/ocpnhelper.c +++ /dev/null @@ -1,102 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by dsr * - * dsr@mshome.net * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int argc, char *argv[]) { - int option, debuglevel; - - int inF, ouF, bytes; - char line[512]; - - while ((option = getopt(argc, argv, "D:SBUV")) != -1) { - switch (option) { - case 'B': { - inF = open("/proc/tty/driver/usb-serial", O_RDONLY); - if (inF == -1) inF = open("/proc/tty/driver/usbserial", O_RDONLY); - - if (inF != -1) { - if ((ouF = open("/var/tmp/usbserial", O_WRONLY | O_CREAT | O_TRUNC, - 0777)) != -1) { - while ((bytes = read(inF, line, sizeof(line))) > 0) - write(ouF, line, bytes); - close(ouF); - } - close(inF); - } - break; - } - - case 'S': { - inF = open("/proc/tty/driver/serial", O_RDONLY); - - if (inF != -1) { - if ((ouF = open("/var/tmp/serial", O_WRONLY | O_CREAT | O_TRUNC, - 0777)) != -1) { - while ((bytes = read(inF, line, sizeof(line))) > 0) - write(ouF, line, bytes); - close(ouF); - } - close(inF); - } - break; - } - - case 'U': { - /* Kill the helper files */ - unlink("/var/tmp/usbserial"); - unlink("/var/tmp/serial"); - break; - } - - case 'D': { - debuglevel = (int)strtol(optarg, 0, 0); - break; - } - - case 'V': { - (void)printf("ocpnhelper %s\n", VERSION); - exit(0); - } - - case 'h': - case '?': - default: { - // usage(); - exit(0); - } - } - } - - return 0; -} diff --git a/src/s57mgr.cpp b/src/s57mgr.cpp deleted file mode 100644 index 77e8e18ce3..0000000000 --- a/src/s57mgr.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/****************************************************************************** - * - * Project: OpenCP - * Purpose: S57 Chart Manager - * Author: David Register - * - *************************************************************************** - * Copyright (C) 2010 by David S. Register * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - *************************************************************************** - * - * - */ - -#include "dychart.h" - -#include "s57mgr.h" - -#include "s57chart.h" - -#include "cpl_port.h" -#include "cpl_csv.h" - -#include "gdal.h" - -static wxString *pval; - -WX_DECLARE_STRING_HASH_MAP(wxString, EnvHash); - -static EnvHash env; - -//---------------------------------------------------------------------------------- -// mygetenv -// -// Replacement for posix getenv() which works for __WXMSW__ -// Todo Make this thing into a couple of string arrays to stop leakage -//---------------------------------------------------------------------------------- - -/* -extern "C" char *mygetenv(char *pvar) -{ - wxString key(pvar); - wxString test_val = env[key]; - if(test_val.Len()) - { - pval->Empty(); - pval->Append(wxString(test_val)); - return((char *)pval->mb_str()); - - } - else - { - wxString val; - wxGetEnv(key, &val); - env[key] = val; - pval->Empty(); - pval->Append(wxString(val)); - return((char *)pval->mb_str()); - } - -} -*/ - -//---------------------------------------------------------------------------------- -// s57mgr Implementation -//---------------------------------------------------------------------------------- - -s57mgr::s57mgr(const wxString &csv_dir) { -#ifdef __WXMSW__ - pval = new wxString; // initialize static string -#endif - - // MS Windows Build Note: - // In a .dll GDAL build, the following _putenv() call DOES NOT properly - // set the environment accessible to GDAL/OGR. So, S57 Reader options - // are not set AT ALL. Defaults will apply. - // See the README file - -#ifdef __WXMSW__ - wxString envs1("S57_CSV="); - envs1.Append(csv_dir); - _putenv(envs1.mb_str()); -#else - wxSetEnv("S57_CSV", csv_dir.mb_str()); -#endif - - // Set some S57 OGR Options thru environment variables - - // n.b. THERE IS A BUG in GDAL/OGR 1.2.0 wherein the sense of the flag - // UPDATES= is reversed. That is, anything other than UPDATES=APPLY selects - // update mode. Conversely, UPDATES=APPLY deselects updates. Fixed in - // GDAL 1.3.2, at least, maybe earlier?? Detect by GDALVersion check - - wxString set1, set2; - - set1 = "LNAM_REFS=ON"; - set1.Append(",SPLIT_MULTIPOINT=OFF"); - set1.Append(",ADD_SOUNDG_DEPTH=OFF"); - set1.Append(",PRESERVE_EMPTY_NUMBERS=OFF"); - set1.Append(",RETURN_PRIMITIVES=OFF"); - set1.Append(",RETURN_LINKAGES=OFF"); - - const char *version_string = GDALVersionInfo("VERSION_NUM"); - int ver_num = (int)CPLScanLong((char *)version_string, 4); - - if (ver_num < 1320) - set2 = ",UPDATES=BUGBUG"; // updates ENABLED - else - set2 = ",UPDATES=APPLY"; - - set1.Append(set2); - -#ifdef __WXMSW__ - wxString envs2("OGR_S57_OPTIONS="); - envs2.Append(set1); - _putenv(envs2.mb_str()); - -#else - wxSetEnv("OGR_S57_OPTIONS", set1.mb_str()); -#endif - - pcsv_locn = new wxString(csv_dir); - - // CPLSetConfigOption( "CPL_DEBUG", "ON"); - // CPLSetConfigOption( "CPL_LOG", "c:\\LOG"); - - RegisterOGRS57(); -} - -s57mgr::~s57mgr() { - delete pcsv_locn; - delete pval; - - // Close and release any csv file access elements, - // Particularly the s57objectclasses.csv used for s57 object query support - CSVDeaccess(NULL); -} - -//---------------------------------------------------------------------------------- -// Get First Chart M_COVR Object -// n.b. Caller owns the data source and the feature on success -//---------------------------------------------------------------------------------- -bool s57mgr::GetChartFirstM_COVR(char *pFullPath, OGRDataSource **pDS, - OGRFeature **pFeature, OGRLayer **pLayer, - int &catcov) { - OGRDataSource *poDS = OGRSFDriverRegistrar::Open(pFullPath); - - *pDS = poDS; // give to caller - - if (poDS == NULL) { - *pFeature = NULL; - return false; - } - - OGRLayer *pLay = poDS->GetLayerByName("M_COVR"); - *pLayer = pLay; // Give to caller - pLay->ResetReading(); - OGRFeature *objectDef = pLay->GetNextFeature(); - *pFeature = objectDef; // Give to caller - - if (objectDef) { - // Fetch the CATCOV attribute - for (int iField = 0; iField < objectDef->GetFieldCount(); iField++) { - if (objectDef->IsFieldSet(iField)) { - OGRFieldDefn *poFDefn = objectDef->GetDefnRef()->GetFieldDefn(iField); - if (!strcmp(poFDefn->GetNameRef(), "CATCOV")) - catcov = objectDef->GetFieldAsInteger(iField); - } - } - return true; - } - - else { - delete poDS; - *pDS = NULL; - return false; - } -} - -//---------------------------------------------------------------------------------- -// GetNext Chart M_COVR Object -// n.b. Caller still owns the data source and the feature on -// success -//---------------------------------------------------------------------------------- -bool s57mgr::GetChartNextM_COVR(OGRDataSource *pDS, OGRLayer *pLayer, - OGRFeature *pLastFeature, OGRFeature **pFeature, - int &catcov) { - if (pDS == NULL) return false; - - catcov = -1; - - int fid = pLastFeature->GetFID(); - - OGRFeature *objectDef = pLayer->GetFeature(fid + 1); - *pFeature = objectDef; // Give to caller - - if (objectDef) { - for (int iField = 0; iField < objectDef->GetFieldCount(); iField++) { - if (objectDef->IsFieldSet(iField)) { - OGRFieldDefn *poFDefn = objectDef->GetDefnRef()->GetFieldDefn(iField); - if (!strcmp(poFDefn->GetNameRef(), "CATCOV")) - catcov = objectDef->GetFieldAsInteger(iField); - } - } - return true; - } - return false; -} - -//---------------------------------------------------------------------------------- -// Get Chart Extents -//---------------------------------------------------------------------------------- -bool s57mgr::GetChartExtent(char *pFullPath, Extent *pext) { - // Fix this find extents of which?? layer?? - /* - OGRS57DataSource *poDS = new OGRS57DataSource; - poDS->Open(pFullPath, TRUE); - - if( poDS == NULL ) - return false; - - OGREnvelope Env; - S57Reader *poReader = poDS->GetModule(0); - poReader->GetExtent(&Env, true); - - pext->NLAT = Env.MaxY; - pext->ELON = Env.MaxX; - pext->SLAT = Env.MinY; - pext->WLON = Env.MinX; - - delete poDS; - */ - return true; -} - -//---------------------------------------------------------------------------------- -// Get Chart Scale -//---------------------------------------------------------------------------------- -int s57mgr::GetChartScale(char *pFullPath) { - DDFModule poModule; - - if (!poModule.Open(pFullPath)) { - return 0; - } - - DDFRecord *poRecord = poModule.ReadRecord(); - if (poRecord == NULL) { - poModule.Close(); - return 0; - } - - int scale = 1; - for (; poRecord != NULL; poRecord = poModule.ReadRecord()) { - if (poRecord->FindField("DSPM") != NULL) { - scale = poRecord->GetIntSubfield("DSPM", 0, "CSCL", 0); - break; - } - } - - poModule.Close(); - - return scale; -} diff --git a/src/scrollingdialog.cpp b/src/scrollingdialog.cpp deleted file mode 100644 index 7611d1b21b..0000000000 --- a/src/scrollingdialog.cpp +++ /dev/null @@ -1,466 +0,0 @@ -///////////////////////////////////////////////////////////////////////////// -// Name: scrollingdialog.cpp -// Purpose: wxScrollingDialog -// Author: Julian Smart -// Modified by: -// Created: 2007-12-11 -// Copyright: (c) Julian Smart -// Licence: wxWindows licence -///////////////////////////////////////////////////////////////////////////// - -#include "wx/wx.h" -#include "wx/module.h" -#include "wx/display.h" -#include "wx/bookctrl.h" - -#include "scrollingdialog.h" - -// Allow for caption size on wxWidgets < 2.9 -#if defined(__WXGTK__) && !wxCHECK_VERSION(2, 9, 0) -#define wxEXTRA_DIALOG_HEIGHT 30 -#else -#define wxEXTRA_DIALOG_HEIGHT 0 -#endif - -IMPLEMENT_CLASS(wxDialogLayoutAdapter, wxObject) - -/*! - * Dialog helper. This contains the extra members that in wxWidgets 3.0 will be - * in wxDialogBase. - */ - -wxDialogLayoutAdapter* wxDialogHelper::sm_layoutAdapter = NULL; -bool wxDialogHelper::sm_layoutAdaptation = true; - -void wxDialogHelper::Init() { - m_layoutAdaptationLevel = 3; - m_layoutLayoutAdaptationDone = FALSE; -} - -/// Do the adaptation -bool wxDialogHelper::DoLayoutAdaptation() { - if (GetLayoutAdapter()) - return GetLayoutAdapter()->DoLayoutAdaptation(this); - else - return false; -} - -/// Can we do the adaptation? -bool wxDialogHelper::CanDoLayoutAdaptation() { - return (GetLayoutAdaptation() && !m_layoutLayoutAdaptationDone && - GetLayoutAdaptationLevel() != 0 && GetLayoutAdapter() != NULL && - GetLayoutAdapter()->CanDoLayoutAdaptation(this)); -} - -/// Set scrolling adapter class, returning old adapter -wxDialogLayoutAdapter* wxDialogHelper::SetLayoutAdapter( - wxDialogLayoutAdapter* adapter) { - wxDialogLayoutAdapter* oldLayoutAdapter = sm_layoutAdapter; - sm_layoutAdapter = adapter; - return oldLayoutAdapter; -} - -/*! - * Standard adapter - */ - -IMPLEMENT_CLASS(wxStandardDialogLayoutAdapter, wxDialogLayoutAdapter) - -/// Indicate that adaptation should be done -bool wxStandardDialogLayoutAdapter::CanDoLayoutAdaptation( - wxDialogHelper* dialog) { - if (dialog->GetDialog()->GetSizer()) { - wxSize windowSize, displaySize; - return MustScroll(dialog->GetDialog(), windowSize, displaySize) != 0; - } else - return false; -} - -bool wxStandardDialogLayoutAdapter::DoLayoutAdaptation(wxDialogHelper* dialog) { - if (dialog->GetDialog()->GetSizer()) { - // The wxRTTI is wrong for wxNotebook in < 2.8.8 and 2.9, so use - // dynamic_cast instead -#if !wxCHECK_VERSION(2, 8, 8) || \ - (wxCHECK_VERSION(2, 9, 0) && !wxCHECK_VERSION(3, 0, 0)) - wxBookCtrlBase* bookContentWindow = - dynamic_cast(dialog->GetContentWindow()); -#else - wxBookCtrlBase* bookContentWindow = - wxDynamicCast(dialog->GetContentWindow(), wxBookCtrlBase); -#endif - - if (bookContentWindow) { - // If we have a book control, make all the pages (that use sizers) - // scrollable - wxWindowList windows; - for (size_t i = 0; i < bookContentWindow->GetPageCount(); i++) { - wxWindow* page = bookContentWindow->GetPage(i); - - wxScrolledWindow* scrolledWindow = - wxDynamicCast(page, wxScrolledWindow); - if (scrolledWindow) - windows.Append(scrolledWindow); - else if (!scrolledWindow && page->GetSizer()) { - // Create a scrolled window and reparent - scrolledWindow = new wxScrolledWindow( - page, wxID_ANY, wxDefaultPosition, wxDefaultSize, - wxTAB_TRAVERSAL | wxVSCROLL | wxHSCROLL | wxBORDER_NONE); - wxSizer* oldSizer = page->GetSizer(); - - wxSizer* newSizer = new wxBoxSizer(wxVERTICAL); - newSizer->Add(scrolledWindow, 1, wxEXPAND, 0); - - page->SetSizer(newSizer, false /* don't delete the old sizer */); - - scrolledWindow->SetSizer(oldSizer); - - ReparentControls(page, scrolledWindow, NULL); - - windows.Append(scrolledWindow); - } - } - - FitWithScrolling(dialog->GetDialog(), windows); - } else { - // If we have an arbitrary dialog, create a scrolling area for the main - // content, and a button sizer for the main buttons. - wxScrolledWindow* scrolledWindow = new wxScrolledWindow( - dialog->GetDialog(), wxID_ANY, wxDefaultPosition, wxDefaultSize, - wxTAB_TRAVERSAL | wxVSCROLL | wxHSCROLL | wxBORDER_NONE); - - int buttonSizerBorder = 0; - - // First try to find a wxStdDialogButtonSizer - wxSizer* buttonSizer = - FindButtonSizer(true /* find std button sizer */, dialog, - dialog->GetDialog()->GetSizer(), buttonSizerBorder); - - // Next try to find a wxBoxSizer containing the controls - if (!buttonSizer && dialog->GetLayoutAdaptationLevel() > 1) - buttonSizer = - FindButtonSizer(false /* find ordinary sizer */, dialog, - dialog->GetDialog()->GetSizer(), buttonSizerBorder); - - // If we still don't have a button sizer, collect any 'loose' buttons in - // the layout - if (!buttonSizer && dialog->GetLayoutAdaptationLevel() > 2) { - int count = 0; - wxStdDialogButtonSizer* stdButtonSizer = new wxStdDialogButtonSizer; - buttonSizer = stdButtonSizer; - - FindLooseButtons(dialog, stdButtonSizer, - dialog->GetDialog()->GetSizer(), count); - if (count > 0) - stdButtonSizer->Realize(); - else { - delete buttonSizer; - buttonSizer = NULL; - } - } - - if (buttonSizerBorder == 0) buttonSizerBorder = 5; - - ReparentControls(dialog->GetDialog(), scrolledWindow, buttonSizer); - - wxBoxSizer* newTopSizer = new wxBoxSizer(wxVERTICAL); - wxSizer* oldSizer = dialog->GetDialog()->GetSizer(); - - dialog->GetDialog()->SetSizer(newTopSizer, - false /* don't delete old sizer */); - - newTopSizer->Add(scrolledWindow, 1, wxEXPAND | wxALL, 0); - if (buttonSizer) - newTopSizer->Add(buttonSizer, 0, wxEXPAND | wxALL, buttonSizerBorder); - - scrolledWindow->SetSizer(oldSizer); - - FitWithScrolling(dialog->GetDialog(), scrolledWindow); - } - } - - dialog->SetLayoutAdaptationDone(true); - return true; -} - -/// Find and remove the button sizer, if any -wxSizer* wxStandardDialogLayoutAdapter::FindButtonSizer(bool stdButtonSizer, - wxDialogHelper* dialog, - wxSizer* sizer, - int& retBorder, - int accumlatedBorder) { - for (wxSizerItemList::compatibility_iterator node = - sizer->GetChildren().GetFirst(); - node; node = node->GetNext()) { - wxSizerItem* item = node->GetData(); - wxSizer* childSizer = item->GetSizer(); - - if (childSizer) { - int newBorder = accumlatedBorder; - if (item->GetFlag() & wxALL) newBorder += item->GetBorder(); - - if (stdButtonSizer) // find wxStdDialogButtonSizer - { - wxStdDialogButtonSizer* buttonSizer = - wxDynamicCast(childSizer, wxStdDialogButtonSizer); - if (buttonSizer) { - sizer->Detach(childSizer); - retBorder = newBorder; - return buttonSizer; - } - } else // find a horizontal box sizer containing standard buttons - { - wxBoxSizer* buttonSizer = wxDynamicCast(childSizer, wxBoxSizer); - if (buttonSizer && IsOrdinaryButtonSizer(dialog, buttonSizer)) { - sizer->Detach(childSizer); - retBorder = newBorder; - return buttonSizer; - } - } - - wxSizer* s = FindButtonSizer(stdButtonSizer, dialog, childSizer, - retBorder, newBorder); - if (s) return s; - } - } - return NULL; -} - -/// Check if this sizer contains standard buttons, and so can be repositioned in -/// the dialog -bool wxStandardDialogLayoutAdapter::IsOrdinaryButtonSizer( - wxDialogHelper* dialog, wxBoxSizer* sizer) { - if (sizer->GetOrientation() != wxHORIZONTAL) return false; - - for (wxSizerItemList::compatibility_iterator node = - sizer->GetChildren().GetFirst(); - node; node = node->GetNext()) { - wxSizerItem* item = node->GetData(); - wxButton* childButton = wxDynamicCast(item->GetWindow(), wxButton); - - if (childButton && IsStandardButton(dialog, childButton)) return true; - } - return false; -} - -/// Check if this is a standard button -bool wxStandardDialogLayoutAdapter::IsStandardButton(wxDialogHelper* dialog, - wxButton* button) { - wxWindowID id = button->GetId(); - - return (id == wxID_OK || id == wxID_CANCEL || id == wxID_YES || - id == wxID_NO || id == wxID_SAVE || id == wxID_APPLY || - id == wxID_HELP || id == wxID_CONTEXT_HELP || - dialog->IsUserButtonId(id)); -} - -/// Find 'loose' main buttons in the existing layout and add them to the -/// standard dialog sizer -bool wxStandardDialogLayoutAdapter::FindLooseButtons( - wxDialogHelper* dialog, wxStdDialogButtonSizer* buttonSizer, wxSizer* sizer, - int& count) { - wxSizerItemList::compatibility_iterator node = - sizer->GetChildren().GetFirst(); - while (node) { - wxSizerItemList::compatibility_iterator next = node->GetNext(); - wxSizerItem* item = node->GetData(); - wxSizer* childSizer = item->GetSizer(); - wxButton* childButton = wxDynamicCast(item->GetWindow(), wxButton); - - if (childButton && IsStandardButton(dialog, childButton)) { - sizer->Detach(childButton); - buttonSizer->AddButton(childButton); - count++; - } - - if (childSizer) FindLooseButtons(dialog, buttonSizer, childSizer, count); - - node = next; - } - return true; -} - -/// Reparent the controls to the scrolled window -void wxStandardDialogLayoutAdapter::ReparentControls(wxWindow* parent, - wxWindow* reparentTo, - wxSizer* buttonSizer) { - wxWindowList::compatibility_iterator node = parent->GetChildren().GetFirst(); - while (node) { - wxWindowList::compatibility_iterator next = node->GetNext(); - - wxWindow* win = node->GetData(); - - // Don't reparent the scrolled window or buttons in the button sizer - if (win != reparentTo && (!buttonSizer || !buttonSizer->GetItem(win))) { - win->Reparent(reparentTo); -#ifdef __WXMSW__ - // Restore correct tab order - ::SetWindowPos((HWND)win->GetHWND(), HWND_BOTTOM, -1, -1, -1, -1, - SWP_NOMOVE | SWP_NOSIZE); -#endif - } - - node = next; - } -} - -/// Find whether scrolling will be necessary for the dialog, returning -/// wxVERTICAL, wxHORIZONTAL or both -int wxStandardDialogLayoutAdapter::MustScroll(wxDialog* dialog, - wxSize& windowSize, - wxSize& displaySize) { - wxSize minWindowSize = dialog->GetSizer()->GetMinSize(); - windowSize = dialog->GetSize(); - windowSize = wxSize(wxMax(windowSize.x, minWindowSize.x), - wxMax(windowSize.y, minWindowSize.y)); - displaySize = - wxDisplay(wxDisplay::GetFromWindow(dialog)).GetClientArea().GetSize(); - - int flags = 0; - - if (windowSize.y >= (displaySize.y - wxEXTRA_DIALOG_HEIGHT)) - flags |= wxVERTICAL; - if (windowSize.x >= displaySize.x) flags |= wxHORIZONTAL; - - return flags; -} - -// A function to fit the dialog around its contents, and then adjust for screen -// size. If scrolled windows are passed, scrolling is enabled in the required -// orientation(s). -bool wxStandardDialogLayoutAdapter::FitWithScrolling(wxDialog* dialog, - wxWindowList& windows) { - wxSizer* sizer = dialog->GetSizer(); - if (!sizer) return false; - - sizer->SetSizeHints(dialog); - - wxSize windowSize, displaySize; - int scrollFlags = MustScroll(dialog, windowSize, displaySize); - int scrollBarSize = 20; - - if (scrollFlags) { - int scrollBarExtraX = 0, scrollBarExtraY = 0; - bool resizeHorizontally = (scrollFlags & wxHORIZONTAL) != 0; - bool resizeVertically = (scrollFlags & wxVERTICAL) != 0; - - if (windows.GetCount() != 0) { - // Allow extra for a scrollbar, assuming we resizing in one direction - // only. - if ((resizeVertically && !resizeHorizontally) && - (windowSize.x < (displaySize.x - scrollBarSize))) - scrollBarExtraX = scrollBarSize; - if ((resizeHorizontally && !resizeVertically) && - (windowSize.y < (displaySize.y - scrollBarSize))) - scrollBarExtraY = scrollBarSize; - } - - wxWindowList::compatibility_iterator node = windows.GetFirst(); - while (node) { - wxWindow* win = node->GetData(); - wxScrolledWindow* scrolledWindow = wxDynamicCast(win, wxScrolledWindow); - if (scrolledWindow) { - scrolledWindow->SetScrollRate(resizeHorizontally ? 10 : 0, - resizeVertically ? 10 : 0); - - if (scrolledWindow->GetSizer()) - scrolledWindow->GetSizer()->Fit(scrolledWindow); - } - - node = node->GetNext(); - } - - wxSize limitTo = windowSize + wxSize(scrollBarExtraX, scrollBarExtraY); - if (resizeVertically) limitTo.y = displaySize.y - wxEXTRA_DIALOG_HEIGHT; - if (resizeHorizontally) limitTo.x = displaySize.x; - - dialog->SetMinSize(limitTo); - dialog->SetSize(limitTo); - - dialog->SetSizeHints(limitTo.x, limitTo.y, dialog->GetMaxWidth(), - dialog->GetMaxHeight()); - } - - return true; -} - -// A function to fit the dialog around its contents, and then adjust for screen -// size. If a scrolled window is passed, scrolling is enabled in the required -// orientation(s). -bool wxStandardDialogLayoutAdapter::FitWithScrolling( - wxDialog* dialog, wxScrolledWindow* scrolledWindow) { - wxWindowList windows; - windows.Append(scrolledWindow); - return FitWithScrolling(dialog, windows); -} - -/*! - * Module to initialise standard adapter - */ - -class wxDialogLayoutAdapterModule : public wxModule { - DECLARE_DYNAMIC_CLASS(wxDialogLayoutAdapterModule) -public: - wxDialogLayoutAdapterModule() {} - virtual void OnExit() { delete wxDialogHelper::SetLayoutAdapter(NULL); } - virtual bool OnInit() { - wxDialogHelper::SetLayoutAdapter(new wxStandardDialogLayoutAdapter); - return true; - } -}; - -IMPLEMENT_DYNAMIC_CLASS(wxDialogLayoutAdapterModule, wxModule) - -/*! - * wxScrollingDialog - */ - -IMPLEMENT_CLASS(wxScrollingDialog, wxDialog) - -void wxScrollingDialog::Init() { wxDialogHelper::SetDialog(this); } - -bool wxScrollingDialog::Create(wxWindow* parent, int id, const wxString& title, - const wxPoint& pos, const wxSize& size, - long style) { - return wxDialog::Create(parent, id, title, pos, size, style); -} - -/// Override Show to rejig the control and sizer hierarchy if necessary -bool wxScrollingDialog::Show(bool show) { - if (CanDoLayoutAdaptation()) DoLayoutAdaptation(); - - return wxDialog::Show(show); -} - -/// Override ShowModal to rejig the control and sizer hierarchy if necessary -int wxScrollingDialog::ShowModal() { - if (CanDoLayoutAdaptation()) DoLayoutAdaptation(); - - return wxDialog::ShowModal(); -} - -/*! - * wxScrollingPropertySheetDialog - */ - -IMPLEMENT_DYNAMIC_CLASS(wxScrollingPropertySheetDialog, wxPropertySheetDialog) - -void wxScrollingPropertySheetDialog::Init() { wxDialogHelper::SetDialog(this); } - -/// Returns the content window -wxWindow* wxScrollingPropertySheetDialog::GetContentWindow() const { - return GetBookCtrl(); -} - -/// Override Show to rejig the control and sizer hierarchy if necessary -bool wxScrollingPropertySheetDialog::Show(bool show) { - if (CanDoLayoutAdaptation()) DoLayoutAdaptation(); - - return wxPropertySheetDialog::Show(show); -} - -/// Override ShowModal to rejig the control and sizer hierarchy if necessary -int wxScrollingPropertySheetDialog::ShowModal() { - if (CanDoLayoutAdaptation()) DoLayoutAdaptation(); - - return wxPropertySheetDialog::ShowModal(); -} diff --git a/src/wificlient.cpp b/src/wificlient.cpp deleted file mode 100644 index 9817b204dc..0000000000 --- a/src/wificlient.cpp +++ /dev/null @@ -1,452 +0,0 @@ -/*************************************************************************** - * - * Project: OpenCPN - * Purpose: NMEA Data Object - * Author: David Register - * - *************************************************************************** - * Copyright (C) 2010 by David S. Register * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - **************************************************************************/ - -#include "wx/wxprec.h" - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif // precompiled headers - -#include "dychart.h" - -#include -#include -#include - -#include "dychart.h" - -#include "wificlient.h" -#include "statwin.h" - -static int wifi_s_dns_test_flag; - -//------------------------------------------------------------------------------ -// WIFI Window Implementation -//------------------------------------------------------------------------------ -BEGIN_EVENT_TABLE(WIFIWindow, wxWindow) -EVT_PAINT(WIFIWindow::OnPaint) -EVT_ACTIVATE(WIFIWindow::OnActivate) -EVT_CLOSE(WIFIWindow::OnCloseWindow) - -EVT_SOCKET(WIFI_SOCKET_ID, WIFIWindow::OnSocketEvent) -EVT_TIMER(TIMER_WIFI1, WIFIWindow::OnTimer1) - -END_EVENT_TABLE() - -// A constructor -WIFIWindow::WIFIWindow(wxFrame *frame, const wxString &WiFiServerName) - : wxWindow(frame, wxID_ANY, wxPoint(20, 20), wxSize(5, 5), wxSIMPLE_BORDER) - -{ - parent_frame = (MyFrame *)frame; - m_sock = NULL; - - m_pdata_server_string = new wxString(WiFiServerName); - - m_watchtick = 0; - m_timer_active = false; - - // Decide upon Server source - wxString msg(_T("WiFi Server is....")); - msg.Append(*m_pdata_server_string); - wxLogMessage(msg); - - if (m_pdata_server_string->Contains(_T("TCP/IP"))) { - wxString WIFI_data_ip; - WIFI_data_ip = m_pdata_server_string->Mid(7); // extract the IP - - if (!WIFI_data_ip.IsEmpty()) { - // Create the socket - m_sock = new wxSocketClient(); - - // Setup the event handler and subscribe to most events - m_sock->SetEventHandler(*this, WIFI_SOCKET_ID); - - m_sock->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | - wxSOCKET_LOST_FLAG); - m_sock->Notify(TRUE); - - m_busy = FALSE; - - // Build the target address - - // n.b. Win98 - // wxIPV4address::Hostname() uses sockets function gethostbyname() for - // address resolution Implications...Either name target must exist in - // c:\windows\hosts, or - // a DNS server must be active on the network. - // If neither true, then wxIPV4address::Hostname() will block - // (forever?).... - // - // Workaround.... - // Use a thread to try the name lookup, in case it hangs - - WIFIDNSTestThread *ptest_thread = NULL; - ptest_thread = new WIFIDNSTestThread(WIFI_data_ip); - - ptest_thread->Run(); // Run the thread from ::Entry() - -// Sleep and loop for N seconds -#define SLEEP_TEST_SEC 2 - - for (int is = 0; is < SLEEP_TEST_SEC * 10; is++) { - wxMilliSleep(100); - if (wifi_s_dns_test_flag) break; - } - - if (!wifi_s_dns_test_flag) { - wxString msg(WIFI_data_ip); - msg.Prepend(_T("Could not resolve TCP/IP host '")); - msg.Append(_T("'\n Suggestion: Try 'xxx.xxx.xxx.xxx' notation")); - OCPNMessageDialog md(this, msg, _T("OpenCPN Message"), wxICON_ERROR); - md.ShowModal(); - - m_sock->Notify(FALSE); - m_sock->Destroy(); - - return; - } - - addr.Hostname(WIFI_data_ip); - addr.Service(SERVER_PORT); - - // It is considered safe to block GUI during socket IO, since WIFI data - // activity is infrequent - m_sock->SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK); - m_sock->Connect(addr, FALSE); // Non-blocking connect - - // Initialize local data stores - for (int ilocal = 0; ilocal < NLOCALSTORE; ilocal++) { - station_data[ilocal].bisvalid = false; - } - - Timer1.SetOwner(this, TIMER_WIFI1); - m_scan_interval_msec = 10000; - Timer1.Start(m_scan_interval_msec, wxTIMER_CONTINUOUS); - m_timer_active = true; - } // !Isempty() - } - - Hide(); -} - -WIFIWindow::~WIFIWindow() { delete m_pdata_server_string; } - -void WIFIWindow::OnCloseWindow(wxCloseEvent &event) { - // Kill off the WIFI Client Socket if alive - if (m_sock) { - m_sock->Notify(FALSE); - m_sock->Destroy(); - Timer1.Stop(); - } -} - -void WIFIWindow::GetSource(wxString &source) { - source = *m_pdata_server_string; -} - -void WIFIWindow::OnActivate(wxActivateEvent &event) {} - -void WIFIWindow::OnPaint(wxPaintEvent &event) { wxPaintDC dc(this); } - -void WIFIWindow::Pause(void) { - if (m_timer_active) Timer1.Stop(); - - if (m_sock) m_sock->Notify(FALSE); -} - -void WIFIWindow::UnPause(void) { - if (m_timer_active) Timer1.Start(m_scan_interval_msec, wxTIMER_CONTINUOUS); - - if (m_sock) m_sock->Notify(TRUE); -} - -/////////////////////////////// -void WIFIWindow::OnSocketEvent(wxSocketEvent &event) { - wifi_scan_data *pt; - unsigned char response_type; - - int i, ilocal; - unsigned char *pbuffer; - int *pcnt; - int cnt; - unsigned char buf[100]; - int pt_eaten[64]; - - if (event.GetSocketEvent() == wxSOCKET_INPUT) { - // Read the first 5 bytes of the reply, getting its total type and - // total length - m_sock->Read(buf, 5); - - // Read the rest - response_type = buf[0]; - int *pint = (int *)(&buf[1]); - int total_length = *pint; - - // get some memory to read the rest - pbuffer = (unsigned char *)malloc(total_length * sizeof(unsigned char)); - - m_sock->Read(pbuffer, total_length - 5); - - switch (response_type - 0x80) { - case 'D': - m_bRX = true; // reset watchdog - m_watchtick = 0; - - // Get the scan results station count - pcnt = (int *)&pbuffer[0]; - cnt = *pcnt; - - if (cnt > 64) cnt = 64; // be safe - - // Manage the data input - // Some setup - for (i = 0; i < cnt; i++) pt_eaten[i] = false; - - // First, check to see if any input station data is already present in - // local store If it is (ESSID matches), then simply update the signal - // quality, and refresh the age. Also, flag the fact that the input - // data has been eaten. - - for (i = 0; i < cnt; i++) { - pt = (wifi_scan_data *)(&pbuffer[( - sizeof(int) + i * 256)]); // skipping the first int - if (strlen(pt->ESSID)) { - for (int ilocal = 0; ilocal < NLOCALSTORE; ilocal++) { - if ((!strcmp(pt->ESSID, station_data[ilocal].ESSID)) && - (station_data[ilocal].bisvalid)) { - station_data[ilocal].sig_quality = pt->sig_quality; - station_data[ilocal].age = -1; - pt_eaten[i] = true; - } - } - } - } - - // Now, age the local store by one - for (ilocal = 0; ilocal < NLOCALSTORE; ilocal++) - if (station_data[ilocal].bisvalid) station_data[ilocal].age++; - - // and free any entries that are over the specified age - for (ilocal = 0; ilocal < NLOCALSTORE; ilocal++) { - if ((station_data[ilocal].bisvalid) && - (station_data[ilocal].age >= N_AGEDEATH)) { - station_data[ilocal].bisvalid = false; - station_data[ilocal].ESSID[0] = 0; - } - } - - // Now, check to see if any input data is un-eaten - // If found, then try to allocate to a local store item - for (i = 0; i < cnt; i++) { - if (pt_eaten[i] == false) { - pt = (wifi_scan_data *)(&pbuffer[(sizeof(int) + i * 256)]); - if (strlen(pt->ESSID)) { - for (ilocal = 0; ilocal < NLOCALSTORE; ilocal++) { - if (station_data[ilocal].bisvalid == false) { - strcpy(station_data[ilocal].ESSID, pt->ESSID); - station_data[ilocal].sig_quality = pt->sig_quality; - station_data[ilocal].secure = pt->secure; - station_data[ilocal].bisvalid = true; - station_data[ilocal].age = 0; - pt_eaten[i] = true; - break; - } - } - } - } - } - - // There may still be un-eaten input data at this point...... - // For now, ignore it. If it is real, it will appear as soon as - // something else dies - - // Finally, send the data to the display window - for (ilocal = 0; ilocal < NLOCALSTORE; ilocal++) { - if (station_data[ilocal].bisvalid) { - // g_ChartBarWin->pWiFi->SetStationQuality(ilocal, - // station_data[ilocal].sig_quality); - // g_ChartBarWin->pWiFi->SetStationSecureFlag(ilocal, - // station_data[ilocal].secure); - // g_ChartBarWin->pWiFi->SetStationAge(ilocal, - // station_data[ilocal].age); - } - // else - // g_ChartBarWin->pWiFi->SetStationQuality(ilocal, - // 0); - } - g_ChartBarWin->Refresh(true); - - break; - - case 'S': { - /* - StatusString = wxString(&buf[5]); - - // This may be useful later.... - fi_status_data *status = (wifi_status_data *)&buf[5]; - - memcpy(&connected_ap_mac_addr, &status->currently_connected_ap, - sizeof(struct sockaddr)); - - // Check for re-connect, if needed - if(StatusString.StartsWith("Not")) - { - if(s_do_reconnect) - { - time_t tnow = wxDateTime::GetTimeNow(); - last_connect_seconds = tnow - last_connect_time; - - do_reconnect(); - } - } - - m_statWindow->Refresh(); - */ - break; - } - - case 'R': { - /* - wxString wr(&buf[5]); - m_logWindow->WriteText(wr); - long ac_compass, ac_brg_commanded, ac_brg_current, ac_motor_dir; - - // Parse the Antenna Controller string - if(!strncmp((const char *)&buf[5], "ANTC", 4)) // valid - string - { - wxStringTokenizer tk(wr, wxT(":")); - - wxString token = tk.GetNextToken(); // skip ANTC - - token = tk.GetNextToken(); - token.ToLong(&ac_compass); // compass heading - - token = tk.GetNextToken(); - token.ToLong(&ac_brg_commanded); // last commanded - antenna bearing - - token = tk.GetNextToken(); - token.ToLong(&ac_brg_current); // current antenna - brg - - token = tk.GetNextToken(); - token.ToLong(&ac_motor_dir); // current motor - state - - s_ac_compass = ac_compass; - s_ac_brg_commanded = ac_brg_commanded; - s_ac_brg_current = ac_brg_current; - s_ac_motor_dir = ac_motor_dir; - - - m_antWindow->Refresh(); - } -*/ - break; - } - - case 'K': { - break; - } - - default: - break; - } // switch - - free(pbuffer); - - } // if - - event.Skip(); -} - -void WIFIWindow::OnTimer1(wxTimerEvent &event) { - Timer1.Stop(); - - if (m_sock->IsConnected()) { - // Keep a watchdog on received data - if (g_ChartBarWin) { - if (m_watchtick++ > WIFI_DOG_TIMEOUT) // nothing received recently - { - // g_ChartBarWin->pWiFi->SetServerStatus(false); - g_ChartBarWin->Refresh(true); - - // Try to totally reset the socket - m_sock->Destroy(); - - m_sock = new wxSocketClient(); - m_sock->SetEventHandler(*this, WIFI_SOCKET_ID); - - m_sock->SetNotify(wxSOCKET_CONNECTION_FLAG | wxSOCKET_INPUT_FLAG | - wxSOCKET_LOST_FLAG); - m_sock->Notify(TRUE); - m_sock->SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK); - - m_watchtick = 0; - } - // else - // g_ChartBarWin->pWiFi->SetServerStatus(true); - } - - unsigned char c = WIFI_TRANSMIT_DATA_EXT; // and call for more data - m_sock->Write(&c, 1); - } else // try to connect - { - if (g_ChartBarWin) { - // g_ChartBarWin->pWiFi->SetServerStatus(false); - g_ChartBarWin->Refresh(true); - } - m_sock->Connect(addr, FALSE); // Non-blocking connect - } - - m_bRX = false; - Timer1.Start(m_scan_interval_msec, wxTIMER_CONTINUOUS); -} - -//------------------------------------------------------------------------------------------------------------- -// -// A simple thread to test host name resolution without blocking the main -// thread -// -//------------------------------------------------------------------------------------------------------------- -WIFIDNSTestThread::WIFIDNSTestThread(const wxString &name_or_ip) { - m_pip = new wxString(name_or_ip); - - Create(); -} - -WIFIDNSTestThread::~WIFIDNSTestThread() { delete m_pip; } - -void *WIFIDNSTestThread::Entry() { - wifi_s_dns_test_flag = 0; - - wxIPV4address addr; - addr.Hostname(*m_pip); // this may block forever if DNS is not active - - wifi_s_dns_test_flag = 1; // came back OK - return NULL; -} From 9c0e4ac2c113db6f52eb424af0ce0e3282fcec31 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Fri, 8 Dec 2023 13:53:46 +0100 Subject: [PATCH 60/63] plugins: Fix bad include paths --- plugins/dashboard_pi/src/dashboard_pi.h | 2 +- plugins/dashboard_pi/src/instrument.h | 2 +- plugins/grib_pi/src/IsoLine.h | 2 +- plugins/grib_pi/src/grib_pi.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/dashboard_pi/src/dashboard_pi.h b/plugins/dashboard_pi/src/dashboard_pi.h index 351a559e2c..46807d6941 100644 --- a/plugins/dashboard_pi/src/dashboard_pi.h +++ b/plugins/dashboard_pi/src/dashboard_pi.h @@ -50,7 +50,7 @@ #include // wx2.9 #include -#include "../../../include/ocpn_plugin.h" +#include "ocpn_plugin.h" #ifdef __OCPN__ANDROID__ #include diff --git a/plugins/dashboard_pi/src/instrument.h b/plugins/dashboard_pi/src/instrument.h index 45b56afce7..70e8e8ab31 100644 --- a/plugins/dashboard_pi/src/instrument.h +++ b/plugins/dashboard_pi/src/instrument.h @@ -39,7 +39,7 @@ #endif // Required GetGlobalColor -#include "../../../include/ocpn_plugin.h" +#include "ocpn_plugin.h" #include #include // supplemental, for Mac diff --git a/plugins/grib_pi/src/IsoLine.h b/plugins/grib_pi/src/IsoLine.h index 6c98daf0b8..dba7de7e27 100644 --- a/plugins/grib_pi/src/IsoLine.h +++ b/plugins/grib_pi/src/IsoLine.h @@ -29,7 +29,7 @@ Dessin des donnĂ©es GRIB (avec QT) #include #include -#include "../../../include/ocpn_plugin.h" +#include "ocpn_plugin.h" #include "GribReader.h" diff --git a/plugins/grib_pi/src/grib_pi.h b/plugins/grib_pi/src/grib_pi.h index 2fd5ba94c7..ca680fdb48 100644 --- a/plugins/grib_pi/src/grib_pi.h +++ b/plugins/grib_pi/src/grib_pi.h @@ -40,7 +40,7 @@ #define MY_API_VERSION_MAJOR 1 #define MY_API_VERSION_MINOR 16 -#include "../../../include/ocpn_plugin.h" +#include "ocpn_plugin.h" #include "wx/jsonreader.h" #include "wx/jsonwriter.h" From c6aa20914ffba1fa2bd9fa59502257a0d8f77c2c Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Wed, 6 Dec 2023 14:25:12 +0100 Subject: [PATCH 61/63] plugin_loader: Bugfix, use dynamic test for being CLI app or not. remove one of the last #ifdef CLIAPP. Make sure also console clients are notified about plugin being loaded. --- src/plugin_loader.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plugin_loader.cpp b/src/plugin_loader.cpp index 90a54d563c..afa52acaea 100644 --- a/src/plugin_loader.cpp +++ b/src/plugin_loader.cpp @@ -533,14 +533,14 @@ bool PluginLoader::LoadPluginCandidate(const wxString& file_name, pic->m_enabled = false; enabled.Set(false); } -#ifndef CLIAPP - // The CLI has no graphics context, but plugins assumes there is. - if (pic->m_enabled) { - pic->m_cap_flag = pic->m_pplugin->Init(); - pic->m_init_state = true; - evt_load_plugin.Notify(pic); + if (dynamic_cast(wxAppConsole::GetInstance())) { + // The CLI has no graphics context, but plugins assumes there is. + if (pic->m_enabled) { + pic->m_cap_flag = pic->m_pplugin->Init(); + pic->m_init_state = true; + } } -#endif + evt_load_plugin.Notify(pic); wxLog::FlushActive(); std::string found_version; From b30a324f07b247c40cd49f6f73a206f206355fc2 Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 7 Dec 2023 19:22:50 +0100 Subject: [PATCH 62/63] BasePlatform: Split into AbstractPlatform + BasePlatform --- CMakeLists.txt | 5 ++- include/OCPNPlatform.h | 4 +-- include/base_platform.h | 28 ++++++++++----- include/cli_platform.h | 26 ++++++++++++++ src/base_platform.cpp | 79 +++++++++++++++-------------------------- src/chcanv.cpp | 8 ++--- src/cli_platform.cpp | 27 ++++++++++++++ src/cm93.cpp | 8 ++--- src/console.cpp | 6 ++-- src/glTexCache.cpp | 4 +-- src/ocpn_frame.cpp | 24 ++++++------- src/s57chart.cpp | 4 +-- test/n2k_tests.cpp | 8 ++--- test/rest-tests.cpp | 8 ++--- test/tests.cpp | 6 ++-- 15 files changed, 145 insertions(+), 100 deletions(-) create mode 100644 include/cli_platform.h create mode 100644 src/cli_platform.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d01574bb57..ea4f8ad623 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -465,6 +465,7 @@ if (MSVC) add_definitions(-D__MSVC__) add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_SECURE_NO_DEPRECATE) add_definitions(-DPSAPI_VERSION=1) + add_definitions(-D_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS=1) # https://developercommunity.visualstudio.com # /t/error-c2872-byte-ambiguous-symbol/93889 # https://shorturl.at/hmJSZ @@ -881,6 +882,7 @@ set( include/chart_ctx_factory.h include/chcanv.h include/ChInfoWin.h + include/cli_platform.h include/color_handler.h include/comm_ais.h include/comm_appmsg.h @@ -1031,6 +1033,7 @@ set(MODEL_SRC ${CMAKE_SOURCE_DIR}/src/catalog_handler.cpp ${CMAKE_SOURCE_DIR}/src/catalog_parser.cpp ${CMAKE_SOURCE_DIR}/src/chartdata_input_stream.cpp + ${CMAKE_SOURCE_DIR}/src/cli_platform.cpp ${CMAKE_SOURCE_DIR}/src/cmdline.cpp ${CMAKE_SOURCE_DIR}/src/comm_ais.cpp ${CMAKE_SOURCE_DIR}/src/comm_navmsg.cpp @@ -3206,7 +3209,7 @@ if (NOT QT_ANDROID) PRIVATE ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/test/include ) - target_compile_definitions(opencpn-cmd PUBLIC CLIAPP USE_MOCK_DEFS) + target_compile_definitions(opencpn-cmd PUBLIC USE_MOCK_DEFS) if (MSVC) target_link_libraries(opencpn-cmd PRIVATE setupapi.lib psapi.lib) endif () diff --git a/include/OCPNPlatform.h b/include/OCPNPlatform.h index 699f7bdb6f..df8da6df36 100644 --- a/include/OCPNPlatform.h +++ b/include/OCPNPlatform.h @@ -103,8 +103,8 @@ class OCPNPlatform : public BasePlatform { //-------------------------------------------------------------------------- // Platform Display Support //-------------------------------------------------------------------------- - static void ShowBusySpinner(void); - static void HideBusySpinner(void); + virtual void ShowBusySpinner(void); + virtual void HideBusySpinner(void); double getFontPointsperPixel(void); wxSize getDisplaySize(); double GetDisplaySizeMM(); diff --git a/include/base_platform.h b/include/base_platform.h index 40aafe7d1e..59332d7db7 100644 --- a/include/base_platform.h +++ b/include/base_platform.h @@ -65,10 +65,10 @@ struct OCPN_OSDetail { std::string osd_ID; }; -class BasePlatform { +class AbstractPlatform { public: - BasePlatform(); - virtual ~BasePlatform() {} + AbstractPlatform() = default; + virtual ~AbstractPlatform() = default; wxString& GetPrivateDataDir(); wxString* GetPluginDirPtr(); @@ -110,20 +110,20 @@ class BasePlatform { OCPN_OSDetail* GetOSDetail() { return m_osDetail; } void CloseLogFile(void); - bool InitializeLogFile(void); + virtual bool InitializeLogFile(void) = 0; wxString& GetLargeLogMessage(void) { return large_log_message; } FILE* GetLogFilePtr() { return flog; } wxString NormalizePath(const wxString& full_path); - virtual wxSize getDisplaySize(); - virtual double GetDisplaySizeMM(); - virtual double GetDisplayDPmm(); + virtual wxSize getDisplaySize() { return wxSize(); } + virtual double GetDisplaySizeMM() { return 1.0; } + virtual double GetDisplayDPmm() { return 1.0; } virtual unsigned int GetSelectRadiusPix(); double GetDisplayDIPMult(wxWindow *win); - void ShowBusySpinner(); - void HideBusySpinner(); + static void ShowBusySpinner(); + static void HideBusySpinner(); protected: bool DetectOSDetail(OCPN_OSDetail* detail); @@ -156,4 +156,14 @@ class BasePlatform { bool m_bdisableWindowsDisplayEnum; }; +class BasePlatform : public AbstractPlatform { +public: + BasePlatform(); + bool InitializeLogFile() override; + + wxSize getDisplaySize() override; + double GetDisplaySizeMM() override; + double GetDisplayDPmm() override; +}; + #endif // BASEPLATFORM_H diff --git a/include/cli_platform.h b/include/cli_platform.h new file mode 100644 index 0000000000..b97bbae41d --- /dev/null +++ b/include/cli_platform.h @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (C) 2019 Alec Leamas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +/** AbstractPlatform adapted for console/CLI usage */ +#include "base_platform.h" + +class CliPlatform : public AbstractPlatform { + bool InitializeLogFile(void) override; +}; + diff --git a/src/base_platform.cpp b/src/base_platform.cpp index 0cad1e6103..7151475977 100644 --- a/src/base_platform.cpp +++ b/src/base_platform.cpp @@ -107,7 +107,7 @@ static bool checkIfFlatpacked() { return id == "org.opencpn.OpenCPN"; } -static wxString ExpandPaths(wxString paths, BasePlatform* platform); +static wxString ExpandPaths(wxString paths, AbstractPlatform* platform); static wxString GetLinuxDataPath() { wxString dirs; @@ -131,7 +131,7 @@ static wxString GetLinuxDataPath() { return s; } -static wxString ExpandPaths(wxString paths, BasePlatform* platform) { +static wxString ExpandPaths(wxString paths, AbstractPlatform* platform) { wxStringTokenizer tokens(paths, ';'); wxString s = ""; while (tokens.HasMoreTokens()) { @@ -162,7 +162,7 @@ BasePlatform::BasePlatform() { // Per-Platform file/directory support //-------------------------------------------------------------------------- -wxStandardPaths& BasePlatform::GetStdPaths() { +wxStandardPaths& AbstractPlatform::GetStdPaths() { #ifndef __ANDROID__ return wxStandardPaths::Get(); #else @@ -171,7 +171,7 @@ wxStandardPaths& BasePlatform::GetStdPaths() { #endif } -wxString BasePlatform::NormalizePath(const wxString& full_path) { +wxString AbstractPlatform::NormalizePath(const wxString& full_path) { if (!g_bportable) { return full_path; } else { @@ -185,7 +185,7 @@ wxString BasePlatform::NormalizePath(const wxString& full_path) { } } -wxString& BasePlatform::GetHomeDir() { +wxString& AbstractPlatform::GetHomeDir() { if (m_homeDir.IsEmpty()) { // Establish a "home" location wxStandardPaths& std_path = GetStdPaths(); @@ -224,7 +224,7 @@ wxString& BasePlatform::GetHomeDir() { return m_homeDir; } -wxString& BasePlatform::GetExePath() { +wxString& AbstractPlatform::GetExePath() { if (m_exePath.IsEmpty()) { wxStandardPaths& std_path = GetStdPaths(); m_exePath = std_path.GetExecutablePath(); @@ -233,17 +233,17 @@ wxString& BasePlatform::GetExePath() { return m_exePath; } -wxString* BasePlatform::GetSharedDataDirPtr() { +wxString* AbstractPlatform::GetSharedDataDirPtr() { if (m_SData_Dir.IsEmpty()) GetSharedDataDir(); return &m_SData_Dir; } -wxString* BasePlatform::GetPrivateDataDirPtr() { +wxString* AbstractPlatform::GetPrivateDataDirPtr() { if (m_PrivateDataDir.IsEmpty()) GetPrivateDataDir(); return &m_PrivateDataDir; } -wxString& BasePlatform::GetSharedDataDir() { +wxString& AbstractPlatform::GetSharedDataDir() { if (m_SData_Dir.IsEmpty()) { // Establish a "shared data" location /* From the wxWidgets documentation... @@ -296,7 +296,7 @@ wxString GetPluginDataDir(const char* plugin_name) { return ""; } -wxString& BasePlatform::GetPrivateDataDir() { +wxString& AbstractPlatform::GetPrivateDataDir() { if (m_PrivateDataDir.IsEmpty()) { // Establish the prefix of the location of user specific data files wxStandardPaths& std_path = GetStdPaths(); @@ -336,7 +336,7 @@ wxString& BasePlatform::GetPrivateDataDir() { return m_PrivateDataDir; } -wxString BasePlatform::GetWinPluginBaseDir() { +wxString AbstractPlatform::GetWinPluginBaseDir() { if (g_winPluginDir != "") { wxLogMessage("winPluginDir: Using value from ini file."); wxFileName fn(g_winPluginDir); @@ -395,7 +395,7 @@ wxString BasePlatform::GetWinPluginBaseDir() { return winPluginDir; } -wxString& BasePlatform::GetPluginDir() { +wxString& AbstractPlatform::GetPluginDir() { if (m_PluginsDir.IsEmpty()) { wxStandardPaths& std_path = GetStdPaths(); @@ -420,12 +420,12 @@ wxString& BasePlatform::GetPluginDir() { return m_PluginsDir; } -wxString* BasePlatform::GetPluginDirPtr() { +wxString* AbstractPlatform::GetPluginDirPtr() { if (m_PluginsDir.IsEmpty()) GetPluginDir(); return &m_PluginsDir; } -bool BasePlatform::isPlatformCapable(int flag) { +bool AbstractPlatform::isPlatformCapable(int flag) { #ifndef __ANDROID__ return true; #else @@ -452,7 +452,7 @@ void appendOSDirSlash(wxString* pString) { if (pString->Last() != sep) pString->Append(sep); } -wxString BasePlatform::GetWritableDocumentsDir() { +wxString AbstractPlatform::GetWritableDocumentsDir() { wxString dir; #ifdef __ANDROID__ @@ -464,7 +464,7 @@ wxString BasePlatform::GetWritableDocumentsDir() { return dir; } -bool BasePlatform::DetectOSDetail(OCPN_OSDetail* detail) { +bool AbstractPlatform::DetectOSDetail(OCPN_OSDetail* detail) { if (!detail) return false; // We take some defaults from build-time definitions @@ -566,7 +566,7 @@ bool BasePlatform::DetectOSDetail(OCPN_OSDetail* detail) { return true; } -wxString& BasePlatform::GetConfigFileName() { +wxString& AbstractPlatform::GetConfigFileName() { if (m_config_file_name.IsEmpty()) { // Establish the location of the config file wxStandardPaths& std_path = GetStdPaths(); @@ -677,26 +677,20 @@ bool BasePlatform::InitializeLogFile(void) { if (wxLog::GetLogLevel() > wxLOG_User) wxLog::SetLogLevel(wxLOG_Info); -#if CLIAPP - wxLog::SetActiveTarget(new wxLogStderr); - wxLog::SetTimestamp(""); - wxLog::SetLogLevel(wxLOG_Warning); -#else g_logger = new OcpnLog(mlog_file.mb_str()); m_Oldlogger = wxLog::SetActiveTarget(g_logger); -#endif return true; } -void BasePlatform::CloseLogFile(void) { +void AbstractPlatform::CloseLogFile(void) { if (g_logger) { wxLog::SetActiveTarget(m_Oldlogger); delete g_logger; } } -wxString BasePlatform::GetPluginDataPath() { +wxString AbstractPlatform::GetPluginDataPath() { if (g_bportable) { wxString sep = wxFileName::GetPathSeparator(); wxString ret = GetPrivateDataDir() + sep + _T("plugins"); @@ -739,27 +733,22 @@ wxString BasePlatform::GetPluginDataPath() { #ifdef __ANDROID__ -void BasePlatform::ShowBusySpinner() { androidShowBusyIcon(); } -#elif defined(CLIAPP) -void BasePlatform::ShowBusySpinner() { } +void AbstractPlatform::ShowBusySpinner() { androidShowBusyIcon(); } #else -void BasePlatform::ShowBusySpinner() { ::wxBeginBusyCursor(); } +void AbstractPlatform::ShowBusySpinner() { ::wxBeginBusyCursor(); } #endif #ifdef __ANDROID__ -void BasePlatform::HideBusySpinner() { androidHideBusyIcon(); } -#elif defined(CLIAPP) -void BasePlatform::HideBusySpinner() { } +void AbstractPlatform::HideBusySpinner() { androidHideBusyIcon(); } #else -void BasePlatform::HideBusySpinner() { ::wxEndBusyCursor(); } +void AbstractPlatform::HideBusySpinner() { ::wxEndBusyCursor(); } #endif // getDisplaySize -#ifdef CLIAPP -wxSize BasePlatform::getDisplaySize() { return wxSize(); } -#elif defined(__ANDROID__) + +#if defined(__ANDROID__) wxSize BasePlatform::getDisplaySize() { return getAndroidDisplayDimensions(); } #else @@ -771,11 +760,6 @@ wxSize BasePlatform::getDisplaySize() { #endif // GetDisplaySizeMM - -#ifdef CLIAPP -double BasePlatform::GetDisplaySizeMM() { return 1.0; } - -#else double BasePlatform::GetDisplaySizeMM() { if (m_displaySizeMMOverride > 0) return m_displaySizeMMOverride; @@ -807,13 +791,8 @@ double BasePlatform::GetDisplaySizeMM() { wxLogDebug("Detected display size (horizontal): %d mm", (int)ret); return ret; } -#endif // CLIAPP - - -#ifdef CLIAPP -double BasePlatform::GetDisplayDPmm() { return 1.0; } -#elif defined(__ANDROID__) +#if defined(__ANDROID__) double BasePlatform::GetDisplayDPmm() { return getAndroidDPmm(); } #else @@ -824,7 +803,7 @@ double BasePlatform::GetDisplayDPmm() { #endif -double BasePlatform::GetDisplayDIPMult(wxWindow *win) { +double AbstractPlatform::GetDisplayDIPMult(wxWindow *win) { double rv = 1.0; #ifdef __WXMSW__ if (win) @@ -833,7 +812,7 @@ double BasePlatform::GetDisplayDIPMult(wxWindow *win) { return rv; } -unsigned int BasePlatform::GetSelectRadiusPix() { +unsigned int AbstractPlatform::GetSelectRadiusPix() { return GetDisplayDPmm() * (g_btouch ? g_selection_radius_touch_mm : g_selection_radius_mm); } @@ -913,7 +892,7 @@ bool GetSizeForDevID(wxString &TargetDevID, int *WidthMm, int *HeightMm) { return bRes; } -bool BasePlatform::GetWindowsMonitorSize(int *width, int *height) { +bool AbstractPlatform::GetWindowsMonitorSize(int *width, int *height) { bool bFoundDevice = true; if (m_monitorWidth < 10) { diff --git a/src/chcanv.cpp b/src/chcanv.cpp index 452edd3f7f..22bb7b9278 100644 --- a/src/chcanv.cpp +++ b/src/chcanv.cpp @@ -1481,7 +1481,7 @@ bool ChartCanvas::CheckGroup(int igroup) { void ChartCanvas::canvasChartsRefresh(int dbi_hint) { if (!ChartData) return; - OCPNPlatform::ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); double old_scale = GetVPScale(); InvalidateQuilt(); @@ -1545,7 +1545,7 @@ void ChartCanvas::canvasChartsRefresh(int dbi_hint) { SetCursor(wxCURSOR_ARROW); - OCPNPlatform::HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); } bool ChartCanvas::DoCanvasUpdate(void) { @@ -10840,7 +10840,7 @@ void ChartCanvas::OnPaint(wxPaintEvent &event) { bool busy = false; if (bvectorQuilt && (m_cache_vp.view_scale_ppm != VPoint.view_scale_ppm || m_cache_vp.rotation != VPoint.rotation)) { - OCPNPlatform::ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); busy = true; } @@ -10984,7 +10984,7 @@ void ChartCanvas::OnPaint(wxPaintEvent &event) { chart_get_all_region); } - if (busy) OCPNPlatform::HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); } diff --git a/src/cli_platform.cpp b/src/cli_platform.cpp new file mode 100644 index 0000000000..3a60aec770 --- /dev/null +++ b/src/cli_platform.cpp @@ -0,0 +1,27 @@ + /*************************************************************************** + * Copyright (C) 2019 Alec Leamas * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "cli_platform.h" + +bool CliPlatform::InitializeLogFile() { + wxLog::SetActiveTarget(new wxLogStderr); + wxLog::SetTimestamp(""); + wxLog::SetLogLevel(wxLOG_Warning); + return true; +} diff --git a/src/cm93.cpp b/src/cm93.cpp index 593c4d2de6..c0ec9033fe 100644 --- a/src/cm93.cpp +++ b/src/cm93.cpp @@ -2039,7 +2039,7 @@ void cm93chart::SetVPParms(const ViewPort &vpt) { // The cell is not in place, so go load it if (!bcell_is_in) { #ifndef __OCPN__ANDROID__ - OCPNPlatform::ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); #endif int cell_index = vpcells[i]; @@ -2102,7 +2102,7 @@ void cm93chart::SetVPParms(const ViewPort &vpt) { } } - OCPNPlatform::HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); } } } @@ -6402,9 +6402,9 @@ void CM93OffsetDialog::UpdateOffsets(void) { // Closing the current cell will record the offsets in the M_COVR cache // file Re-opening will then refresh the M_COVRs in the cover set - OCPNPlatform::ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); m_pcompchart->CloseandReopenCurrentSubchart(); - OCPNPlatform::HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); if (m_pparent) { m_pparent->Refresh(true); diff --git a/src/console.cpp b/src/console.cpp index 471d5238db..485de74b95 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -57,8 +57,8 @@ #include #include -#include "base_platform.h" #include "catalog_handler.h" +#include "cli_platform.h" #include "comm_appmsg_bus.h" #include "comm_driver.h" #include "comm_navmsg_bus.h" @@ -77,7 +77,7 @@ class AISTargetAlertDialog; class Multiplexer; class Select; -BasePlatform* g_BasePlatform = 0; +AbstractPlatform* g_BasePlatform = 0; void* g_pi_manager = reinterpret_cast(1L); wxString g_compatOS = PKG_TARGET; wxString g_compatOsVersion = PKG_TARGET_VERSION; @@ -245,7 +245,7 @@ class CliApp : public wxAppConsole { wxLog::SetTimestamp(""); wxLog::SetLogLevel(wxLOG_Warning); - g_BasePlatform = new BasePlatform(); + g_BasePlatform = new CliPlatform(); auto config_file = g_BasePlatform->GetConfigFileName(); InitBaseConfig(new wxFileConfig("", "", config_file)); pSelect = new Select(); diff --git a/src/glTexCache.cpp b/src/glTexCache.cpp index d395e97c76..c06fbb82aa 100644 --- a/src/glTexCache.cpp +++ b/src/glTexCache.cpp @@ -583,7 +583,7 @@ bool glTexFactory::BuildTexture(glTextureDescriptor *ptd, int base_level, } else { // COMPRESSED_BUFFER_OK == status if (m_newCatalog) { // it's an empty catalog or it's not used, odds it's going to be slow - OCPNPlatform::ShowBusySpinner(); + BasePlatform::ShowBusySpinner(); busy_shown = true; m_newCatalog = false; } @@ -631,7 +631,7 @@ bool glTexFactory::BuildTexture(glTextureDescriptor *ptd, int base_level, ptd->map_array[i] = 0; } - if (busy_shown) OCPNPlatform::HideBusySpinner(); + if (busy_shown) AbstractPlatform::HideBusySpinner(); return true; } diff --git a/src/ocpn_frame.cpp b/src/ocpn_frame.cpp index 9c1ffcecbe..240069b95d 100644 --- a/src/ocpn_frame.cpp +++ b/src/ocpn_frame.cpp @@ -403,9 +403,9 @@ void DeInitializeUserColors(void); void SetSystemColors(ColorScheme cs); static bool LoadAllPlugIns(bool load_enabled) { - g_Platform->ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); bool b = PluginLoader::getInstance()->LoadAllPlugIns(load_enabled); - g_Platform->HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); return b; } @@ -3825,7 +3825,7 @@ int MyFrame::DoOptionsDialog() { g_last_ChartScaleFactor = g_ChartScaleFactor; if (NULL == g_options) { - g_Platform->ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); int sx, sy; pConfig->SetPath("/Settings"); @@ -3839,7 +3839,7 @@ int MyFrame::DoOptionsDialog() { g_options = new options(optionsParent, -1, _("Options"), wxPoint(-1, -1), wxSize(sx, sy)); - g_Platform->HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); } // Set initial Chart Dir @@ -4463,7 +4463,7 @@ void MyFrame::RefreshCanvasOther(ChartCanvas *ccThis) { void MyFrame::ChartsRefresh() { if (!ChartData) return; - OCPNPlatform::ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); bool b_run = FrameTimer1.IsRunning(); @@ -4483,7 +4483,7 @@ void MyFrame::ChartsRefresh() { if (b_run) FrameTimer1.Start(TIMER_GFRAME_1, wxTIMER_CONTINUOUS); - OCPNPlatform::HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); } void MyFrame::InvalidateAllQuilts() { @@ -4521,7 +4521,7 @@ bool MyFrame::UpdateChartDatabaseInplace(ArrayOfCDI &DirArray, bool b_force, // delete pCurrentStack; // pCurrentStack = NULL; - OCPNPlatform::ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); wxGenericProgressDialog *pprog = nullptr; if (b_prog) { @@ -4569,7 +4569,7 @@ bool MyFrame::UpdateChartDatabaseInplace(ArrayOfCDI &DirArray, bool b_force, delete pprog; - OCPNPlatform::HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); pConfig->UpdateChartDirs(DirArray); @@ -4769,9 +4769,9 @@ void MyFrame::OnInitTimer(wxTimerEvent &event) { case 2: { if (m_initializing) break; m_initializing = true; - g_Platform->ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); PluginLoader::getInstance()->LoadAllPlugIns(true); - g_Platform->HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); // RequestNewToolbars(); RequestNewMasterToolbar(); // A Plugin (e.g. Squiddio) may have redefined some routepoint icons... @@ -6884,13 +6884,13 @@ void MyFrame::applySettingsString(wxString settings) { UpdateGPSCompassStatusBoxes(true); if (b_newToolbar) { - g_Platform->ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); SetAllToolbarScale(); RequestNewToolbars( true); // Force rebuild, to pick up bGUIexpert and scale settings. - g_Platform->HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); RequestNewMasterToolbar(true); } diff --git a/src/s57chart.cpp b/src/s57chart.cpp index c554cc5a42..fc8809d505 100644 --- a/src/s57chart.cpp +++ b/src/s57chart.cpp @@ -4090,11 +4090,11 @@ int s57chart::BuildSENCFile(const wxString &FullPath000, senc.setRefLocn(ref_lat, ref_lon); senc.SetLODMeters(m_LOD_meters); - OCPNPlatform::ShowBusySpinner(); + AbstractPlatform::ShowBusySpinner(); int ret = senc.createSenc200(FullPath000, SENCFileName, b_progress); - OCPNPlatform::HideBusySpinner(); + AbstractPlatform::HideBusySpinner(); if (ret == ERROR_INGESTING000) return BUILD_SENC_NOK_PERMANENT; diff --git a/test/n2k_tests.cpp b/test/n2k_tests.cpp index 49e7938aa9..595ec7fe3c 100644 --- a/test/n2k_tests.cpp +++ b/test/n2k_tests.cpp @@ -10,7 +10,7 @@ #include "config.h" -#include "base_platform.h" +#include "cli_platform.h" #include "cmdline.h" #include "comm_ais.h" #include "comm_appmsg_bus.h" @@ -133,7 +133,7 @@ extern float g_selection_radius_touch_mm; extern int g_nCOMPortCheck; extern bool g_benableUDPNullHeader; -extern BasePlatform* g_BasePlatform; +extern AbstractPlatform* g_BasePlatform; extern void* g_pi_manager; extern wxString g_compatOS; extern wxString g_compatOsVersion; @@ -188,7 +188,7 @@ class N2kTest: public testing::Test { wxAppConsole* app; virtual void SetUp() override { - g_BasePlatform = new BasePlatform(); + g_BasePlatform = new CliPlatform(); pSelectAIS = new Select(); pSelect = new Select(); g_pAIS = new AisDecoder(AisDecoderCallbacks()); @@ -274,7 +274,7 @@ class N2kRunLog : public wxAppConsole { wxAppConsole::OnInit(); //Observable::Clear(); - g_BasePlatform = new BasePlatform(); + g_BasePlatform = new CliPlatform(); delete pSelectAIS; pSelectAIS = new Select(); delete pSelect; diff --git a/test/rest-tests.cpp b/test/rest-tests.cpp index 6924e69c50..fdf41befda 100644 --- a/test/rest-tests.cpp +++ b/test/rest-tests.cpp @@ -22,8 +22,8 @@ #include -#include "base_platform.h" #include "certificates.h" +#include "cli_platform.h" #include "config_vars.h" #include "mDNS_query.h" #include "observable_confvar.h" @@ -37,7 +37,7 @@ using namespace std::chrono_literals; extern WayPointman* pWayPointMan; extern RouteList* pRouteList; extern Select* pSelect; -extern BasePlatform* g_BasePlatform; +extern AbstractPlatform* g_BasePlatform; static std::string s_result; static int int_result0; @@ -225,7 +225,7 @@ class RestServerObjectApp : public RestServerApp { auto colour_func = [] (wxString c) { return *wxBLACK; }; pWayPointMan = new WayPointman(colour_func); pRouteList = new RouteList; - g_BasePlatform = new BasePlatform(); + g_BasePlatform = new CliPlatform(); pSelect = new Select(); auto outpath = fs::path(CMAKE_BINARY_DIR) / "curl-result"; @@ -317,7 +317,7 @@ class RestCheckWriteApp : public RestServerApp { auto colour_func = [] (wxString c) { return *wxBLACK; }; pWayPointMan = new WayPointman(colour_func); pRouteList = new RouteList; - g_BasePlatform = new BasePlatform(); + g_BasePlatform = new CliPlatform(); pSelect = new Select(); fs::path curl_prog(CURLPROG); diff --git a/test/tests.cpp b/test/tests.cpp index d56f02605d..bfd9517b75 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -16,7 +16,7 @@ #include "ais_decoder.h" #include "ais_defs.h" -#include "base_platform.h" +#include "cli_platform.h" #include "comm_ais.h" #include "comm_appmsg_bus.h" #include "comm_bridge.h" @@ -76,7 +76,7 @@ float g_selection_radius_touch_mm; int g_nCOMPortCheck = 32; bool g_benableUDPNullHeader; -BasePlatform* g_BasePlatform = 0; +AbstractPlatform* g_BasePlatform = 0; void* g_pi_manager = reinterpret_cast(1L); wxString g_compatOS = PKG_TARGET; wxString g_compatOsVersion = PKG_TARGET_VERSION; @@ -416,7 +416,7 @@ class AisApp : public wxAppConsole { AisApp(const char* type, const char* msg) : wxAppConsole() { ConfigSetup(); SetAppName("opencpn_unittests"); - g_BasePlatform = new BasePlatform(); + g_BasePlatform = new CliPlatform(); pSelectAIS = new Select(); pSelect = new Select(); g_pAIS = new AisDecoder(AisDecoderCallbacks()); From a3805c45cfbcdd9fdb466a5f6ea8a8e695e5cd4c Mon Sep 17 00:00:00 2001 From: Alec Leamas Date: Thu, 7 Dec 2023 20:29:19 +0100 Subject: [PATCH 63/63] CmakeLists: Split SRCS into MODEL_SRC+ GUI_SRC --- CMakeLists.txt | 378 ++++++++++++++++++++++++++---------------------- src/console.cpp | 3 + 2 files changed, 205 insertions(+), 176 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea4f8ad623..e63966555e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -856,34 +856,15 @@ message(STATUS "*** Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "*** Will install to ${CMAKE_INSTALL_PREFIX} ***") set( - HDRS - include/AboutFrame.h - include/AboutFrameImpl.h - include/about.h + MODEL_HDRS include/ais_bitstring.h include/ais_decoder.h - include/ais.h - include/AISTargetAlertDialog.h include/ais_target_data.h - include/AISTargetListDialog.h - include/AISTargetQueryDialog.h - include/ais_info_gui.h - include/atomic_queue.h include/base_platform.h - include/canvasMenu.h include/catalog_handler.h - include/catalog_mgr.h include/catalog_parser.h - include/cat_settings.h include/chartdata_input_stream.h - include/chartdb.h - include/chartdbs.h - include/chartimg.h - include/chart_ctx_factory.h - include/chcanv.h - include/ChInfoWin.h include/cli_platform.h - include/color_handler.h include/comm_ais.h include/comm_appmsg.h include/comm_bridge.h @@ -904,14 +885,62 @@ set( include/comm_navmsg_bus.h include/comm_util.h include/comm_vars.h - include/compass.h - include/concanv.h include/config_vars.h include/conn_params.h - include/conn_params_panel.h include/cutil.h - include/DetailSlider.h include/downloader.h + include/garmin_protocol_mgr.h + include/geodesic.h + include/georef.h + include/hyperlink.h + include/logger.h + include/multiplexer.h + include/nav_object_database.h + include/navutil_base.h + include/ocpn_plugin.h + include/ocpn_types.h + include/ocpn_utils.h + include/own_ship.h + include/pincode.h + include/plugin_blacklist.h + include/plugin_handler.h + include/plugin_loader.h + include/plugin_paths.h + include/position_parser.h + include/rest_server.h + include/route.h + include/routeman.h + include/select.h + include/semantic_vers.h + include/ser_ports.h + include/track.h +) + +set( + GUI_HDRS + include/AboutFrame.h + include/AboutFrameImpl.h + include/about.h + include/ais.h + include/AISTargetAlertDialog.h + include/AISTargetListDialog.h + include/AISTargetQueryDialog.h + include/ais_info_gui.h + include/atomic_queue.h + include/canvasMenu.h + include/catalog_mgr.h + include/cat_settings.h + include/chartdb.h + include/chartdbs.h + include/chartimg.h + include/chart_ctx_factory.h + include/chcanv.h + include/ChInfoWin.h + include/color_handler.h + include/compass.h + include/concanv.h + include/conn_params_panel.h + include/DetailSlider.h include/download_mgr.h include/dsPortType.h include/emboss_data.h @@ -919,13 +948,9 @@ set( include/FontDesc.h include/FontMgr.h include/garmin_wrapper.h - include/garmin_protocol_mgr.h - include/geodesic.h - include/georef.h include/GoToPositionDialog.h include/gui_lib.h include/gshhs.h - include/hyperlink.h include/idents.h include/IDX_entry.h include/iENCToolbar.h @@ -933,16 +958,12 @@ set( include/Layer.h include/LinkPropDlg.h include/load_errors_dlg.h - include/logger.h include/meteo_points.h include/MarkIcon.h include/MarkInfo.h include/mbtiles.h - include/multiplexer.h - include/nav_object_database.h include/n0183_ctx_factory.h include/navutil.h - include/navutil_base.h include/NMEALogWindow.h include/nmea_ctx_factory.h include/ocpCursor.h @@ -951,33 +972,20 @@ set( include/OCPNListCtrl.h include/ocpn_pixel.h include/OCPNPlatform.h - include/ocpn_plugin.h include/ocpn_print.h include/OCPNRegion.h - include/ocpn_types.h - include/ocpn_utils.h include/options.h - include/own_ship.h include/piano.h - include/pincode.h include/plugin_cache.h - include/plugin_blacklist.h - include/plugin_handler.h - include/plugin_loader.h - include/plugin_paths.h include/pluginmanager.h - include/position_parser.h include/printtable.h include/priority_gui.h include/Quilt.h - include/rest_server.h include/rest_server_gui.h include/RolloverWin.h - include/route.h include/route_ctx_factory.h include/route_gui.h include/routemanagerdialog.h - include/routeman.h include/routeman_gui.h include/route_point.h include/route_point_gui.h @@ -990,13 +998,13 @@ set( include/S57QueryDialog.h include/S57Sector.h include/safe_mode.h - include/select.h include/select_item.h - include/semantic_vers.h include/SendToGpsDlg.h include/SendToPeerDlg.h + include/shaders.h include/Station_Data.h include/styles.h + include/svg_utils.h include/TCDataFactory.h include/TCDataSource.h include/TCDS_Ascii_Harmonic.h @@ -1008,7 +1016,6 @@ set( include/tide_time.h include/timers.h include/toolbar.h - include/track.h include/track_gui.h include/trackprintout.h include/TrackPropDlg.h @@ -1019,11 +1026,10 @@ set( include/usb_devices.h include/waypointman_gui.h include/viewport.h - include/shaders.h include/WindowDestroyListener.h - include/svg_utils.h ) + set(MODEL_SRC # Testable sources without GUI dependencies. ${CMAKE_SOURCE_DIR}/src/ais_bitstring.cpp @@ -1036,14 +1042,12 @@ set(MODEL_SRC ${CMAKE_SOURCE_DIR}/src/cli_platform.cpp ${CMAKE_SOURCE_DIR}/src/cmdline.cpp ${CMAKE_SOURCE_DIR}/src/comm_ais.cpp - ${CMAKE_SOURCE_DIR}/src/comm_navmsg.cpp - ${CMAKE_SOURCE_DIR}/src/comm_navmsg_bus.cpp - ${CMAKE_SOURCE_DIR}/src/comm_appmsg.cpp ${CMAKE_SOURCE_DIR}/src/comm_appmsg_bus.cpp + ${CMAKE_SOURCE_DIR}/src/comm_appmsg.cpp ${CMAKE_SOURCE_DIR}/src/comm_bridge.cpp ${CMAKE_SOURCE_DIR}/src/comm_decoder.cpp - ${CMAKE_SOURCE_DIR}/src/comm_drv_file.cpp ${CMAKE_SOURCE_DIR}/src/comm_drv_factory.cpp + ${CMAKE_SOURCE_DIR}/src/comm_drv_file.cpp ${CMAKE_SOURCE_DIR}/src/comm_drv_n0183.cpp ${CMAKE_SOURCE_DIR}/src/comm_drv_n0183_net.cpp ${CMAKE_SOURCE_DIR}/src/comm_drv_n0183_serial.cpp @@ -1052,13 +1056,15 @@ set(MODEL_SRC ${CMAKE_SOURCE_DIR}/src/comm_drv_registry.cpp ${CMAKE_SOURCE_DIR}/src/comm_drv_signalk.cpp ${CMAKE_SOURCE_DIR}/src/comm_drv_signalk_net.cpp - ${CMAKE_SOURCE_DIR}/src/comm_navmsg.cpp ${CMAKE_SOURCE_DIR}/src/comm_n0183_output.cpp + ${CMAKE_SOURCE_DIR}/src/comm_navmsg_bus.cpp + ${CMAKE_SOURCE_DIR}/src/comm_navmsg.cpp + ${CMAKE_SOURCE_DIR}/src/comm_navmsg.cpp + ${CMAKE_SOURCE_DIR}/src/comm_plugin_api.cpp ${CMAKE_SOURCE_DIR}/src/comm_util.cpp ${CMAKE_SOURCE_DIR}/src/comm_vars.cpp - ${CMAKE_SOURCE_DIR}/src/comm_plugin_api.cpp - ${CMAKE_SOURCE_DIR}/src/conn_params.cpp ${CMAKE_SOURCE_DIR}/src/config_vars.cpp + ${CMAKE_SOURCE_DIR}/src/conn_params.cpp ${CMAKE_SOURCE_DIR}/src/cutil.cpp ${CMAKE_SOURCE_DIR}/src/downloader.cpp ${CMAKE_SOURCE_DIR}/src/garmin_protocol_mgr.cpp @@ -1071,6 +1077,8 @@ set(MODEL_SRC ${CMAKE_SOURCE_DIR}/src/navutil_base.cpp ${CMAKE_SOURCE_DIR}/src/ocpn_plugin.cpp ${CMAKE_SOURCE_DIR}/src/ocpn_utils.cpp + ${CMAKE_SOURCE_DIR}/src/ogrs57datasource.cpp + ${CMAKE_SOURCE_DIR}/src/ogrs57layer.cpp ${CMAKE_SOURCE_DIR}/src/own_ship.cpp ${CMAKE_SOURCE_DIR}/src/pincode.cpp ${CMAKE_SOURCE_DIR}/src/plugin_blacklist.cpp @@ -1081,8 +1089,12 @@ set(MODEL_SRC ${CMAKE_SOURCE_DIR}/src/position_parser.cpp ${CMAKE_SOURCE_DIR}/src/rest_server.cpp ${CMAKE_SOURCE_DIR}/src/route.cpp - ${CMAKE_SOURCE_DIR}/src/route_point.cpp ${CMAKE_SOURCE_DIR}/src/routeman.cpp + ${CMAKE_SOURCE_DIR}/src/route_point.cpp + ${CMAKE_SOURCE_DIR}/src/s57classregistrar.cpp + ${CMAKE_SOURCE_DIR}/src/s57featuredefns.cpp + ${CMAKE_SOURCE_DIR}/src/s57reader.cpp + ${CMAKE_SOURCE_DIR}/src/s57RegistrarMgr.cpp ${CMAKE_SOURCE_DIR}/src/select.cpp ${CMAKE_SOURCE_DIR}/src/select_item.cpp ${CMAKE_SOURCE_DIR}/src/semantic_vers.cpp @@ -1091,117 +1103,124 @@ set(MODEL_SRC ) set( - SRCS - src/about.cpp - src/AboutFrame.cpp - src/AboutFrameImpl.cpp - src/ais.cpp - src/AISTargetAlertDialog.cpp - src/AISTargetListDialog.cpp - src/AISTargetQueryDialog.cpp - src/ais_info_gui.cpp - src/CanvasConfig.cpp - src/canvasMenu.cpp - src/CanvasOptions.cpp - src/catalog_mgr.cpp - src/cat_settings.cpp - src/certificates.cpp - src/chartdb.cpp - src/chartdbs.cpp - src/chartimg.cpp - src/chcanv.cpp - src/ChInfoWin.cpp - src/color_handler.cpp - src/compass.cpp - src/concanv.cpp - src/ConfigMgr.cpp - src/conn_params_panel.cpp - src/connections_dialog.cpp - src/DetailSlider.cpp - src/download_mgr.cpp - src/FlexHash.cpp - src/FontDesc.cpp - src/FontMgr.cpp - src/GoToPositionDialog.cpp - src/gshhs.cpp - src/gui_lib.cpp - src/IDX_entry.cpp - src/iENCToolbar.cpp - src/kml.cpp - src/Layer.cpp - src/LinkPropDlg.cpp - src/load_errors_dlg.cpp - src/logger.cpp - src/MarkInfo.cpp - src/mbtiles.cpp - src/mDNS_query.cpp - src/mDNS_service.cpp - src/MUIBar.cpp - src/navutil.cpp - src/NMEALogWindow.cpp - src/ocpCursor.cpp - src/ocpn_app.cpp - src/OCPN_AUIManager.cpp - src/ocpndc.cpp - src/ocpn_frame.cpp - src/OCPNListCtrl.cpp - src/ocpn_pixel.cpp - src/OCPNPlatform.cpp - src/ocpn_plugin.cpp - src/ocpn_print.cpp - src/OCPNRegion.cpp - src/options.cpp - src/peer_client.cpp - src/piano.cpp - src/pluginmanager.cpp - src/plugin_paths.cpp - src/printtable.cpp - src/priority_gui.cpp - src/Quilt.cpp - src/rest_server_gui.cpp - src/RolloverWin.cpp - src/routemanagerdialog.cpp - src/routeman_gui.cpp - src/route_gui.cpp - src/route_point_gui.cpp - src/routeprintout.cpp - src/RoutePropDlg.cpp - src/RoutePropDlgImpl.cpp - src/S57QueryDialog.cpp - src/safe_mode.cpp - src/SendToGpsDlg.cpp - src/SendToPeerDlg.cpp - src/Station_Data.cpp - src/styles.cpp - src/svg_utils.cpp - src/TCDataFactory.cpp - src/TCDataSource.cpp - src/TCDS_Ascii_Harmonic.cpp - src/TCDS_Binary_Harmonic.cpp - src/tcmgr.cpp - src/TCWin.cpp - src/thumbwin.cpp - src/toolbar.cpp - src/track_gui.cpp - src/trackprintout.cpp - src/TrackPropDlg.cpp - src/TTYScroll.cpp - src/TTYWindow.cpp - src/undo.cpp - src/update_mgr.cpp - src/waypointman_gui.cpp - src/viewport.cpp - src/shaders.cpp + GUI_SRC + ${CMAKE_SOURCE_DIR}/src/about.cpp + ${CMAKE_SOURCE_DIR}/src/AboutFrame.cpp + ${CMAKE_SOURCE_DIR}/src/AboutFrameImpl.cpp + ${CMAKE_SOURCE_DIR}/src/ais.cpp + ${CMAKE_SOURCE_DIR}/src/ais_info_gui.cpp + ${CMAKE_SOURCE_DIR}/src/AISTargetAlertDialog.cpp + ${CMAKE_SOURCE_DIR}/src/AISTargetListDialog.cpp + ${CMAKE_SOURCE_DIR}/src/AISTargetQueryDialog.cpp + ${CMAKE_SOURCE_DIR}/src/CanvasConfig.cpp + ${CMAKE_SOURCE_DIR}/src/canvasMenu.cpp + ${CMAKE_SOURCE_DIR}/src/CanvasOptions.cpp + ${CMAKE_SOURCE_DIR}/src/catalog_mgr.cpp + ${CMAKE_SOURCE_DIR}/src/cat_settings.cpp + ${CMAKE_SOURCE_DIR}/src/certificates.cpp + ${CMAKE_SOURCE_DIR}/src/chartdb.cpp + ${CMAKE_SOURCE_DIR}/src/chartdbs.cpp + ${CMAKE_SOURCE_DIR}/src/chartimg.cpp + ${CMAKE_SOURCE_DIR}/src/chcanv.cpp + ${CMAKE_SOURCE_DIR}/src/ChInfoWin.cpp + ${CMAKE_SOURCE_DIR}/src/cm93.cpp + ${CMAKE_SOURCE_DIR}/src/color_handler.cpp + ${CMAKE_SOURCE_DIR}/src/compass.cpp + ${CMAKE_SOURCE_DIR}/src/concanv.cpp + ${CMAKE_SOURCE_DIR}/src/ConfigMgr.cpp + ${CMAKE_SOURCE_DIR}/src/connections_dialog.cpp + ${CMAKE_SOURCE_DIR}/src/conn_params_panel.cpp + ${CMAKE_SOURCE_DIR}/src/DetailSlider.cpp + ${CMAKE_SOURCE_DIR}/src/download_mgr.cpp + ${CMAKE_SOURCE_DIR}/src/FlexHash.cpp + ${CMAKE_SOURCE_DIR}/src/FontDesc.cpp + ${CMAKE_SOURCE_DIR}/src/FontMgr.cpp + ${CMAKE_SOURCE_DIR}/src/glChartCanvas.cpp + ${CMAKE_SOURCE_DIR}/src/glTexCache.cpp + ${CMAKE_SOURCE_DIR}/src/glTextureDescriptor.cpp + ${CMAKE_SOURCE_DIR}/src/glTextureManager.cpp + ${CMAKE_SOURCE_DIR}/src/GoToPositionDialog.cpp + ${CMAKE_SOURCE_DIR}/src/gshhs.cpp + ${CMAKE_SOURCE_DIR}/src/gui_lib.cpp + ${CMAKE_SOURCE_DIR}/src/IDX_entry.cpp + ${CMAKE_SOURCE_DIR}/src/iENCToolbar.cpp + ${CMAKE_SOURCE_DIR}/src/kml.cpp + ${CMAKE_SOURCE_DIR}/src/Layer.cpp + ${CMAKE_SOURCE_DIR}/src/LinkPropDlg.cpp + ${CMAKE_SOURCE_DIR}/src/load_errors_dlg.cpp + ${CMAKE_SOURCE_DIR}/src/MarkInfo.cpp + ${CMAKE_SOURCE_DIR}/src/mbtiles.cpp + ${CMAKE_SOURCE_DIR}/src/mDNS_query.cpp + ${CMAKE_SOURCE_DIR}/src/mDNS_service.cpp + ${CMAKE_SOURCE_DIR}/src/MUIBar.cpp + ${CMAKE_SOURCE_DIR}/src/navutil.cpp + ${CMAKE_SOURCE_DIR}/src/NMEALogWindow.cpp + ${CMAKE_SOURCE_DIR}/src/ocpCursor.cpp + ${CMAKE_SOURCE_DIR}/src/ocpn_app.cpp + ${CMAKE_SOURCE_DIR}/src/OCPN_AUIManager.cpp + ${CMAKE_SOURCE_DIR}/src/ocpndc.cpp + ${CMAKE_SOURCE_DIR}/src/ocpn_frame.cpp + ${CMAKE_SOURCE_DIR}/src/OCPNListCtrl.cpp + ${CMAKE_SOURCE_DIR}/src/ocpn_pixel.cpp + ${CMAKE_SOURCE_DIR}/src/OCPNPlatform.cpp + ${CMAKE_SOURCE_DIR}/src/ocpn_print.cpp + ${CMAKE_SOURCE_DIR}/src/OCPNRegion.cpp + ${CMAKE_SOURCE_DIR}/src/options.cpp + ${CMAKE_SOURCE_DIR}/src/Osenc.cpp + ${CMAKE_SOURCE_DIR}/src/peer_client.cpp + ${CMAKE_SOURCE_DIR}/src/piano.cpp + ${CMAKE_SOURCE_DIR}/src/pluginmanager.cpp + ${CMAKE_SOURCE_DIR}/src/printtable.cpp + ${CMAKE_SOURCE_DIR}/src/priority_gui.cpp + ${CMAKE_SOURCE_DIR}/src/Quilt.cpp + ${CMAKE_SOURCE_DIR}/src/rest_server_gui.cpp + ${CMAKE_SOURCE_DIR}/src/RolloverWin.cpp + ${CMAKE_SOURCE_DIR}/src/route_gui.cpp + ${CMAKE_SOURCE_DIR}/src/routemanagerdialog.cpp + ${CMAKE_SOURCE_DIR}/src/routeman_gui.cpp + ${CMAKE_SOURCE_DIR}/src/route_point_gui.cpp + ${CMAKE_SOURCE_DIR}/src/routeprintout.cpp + ${CMAKE_SOURCE_DIR}/src/RoutePropDlg.cpp + ${CMAKE_SOURCE_DIR}/src/RoutePropDlgImpl.cpp + ${CMAKE_SOURCE_DIR}/src/s57chart.cpp + ${CMAKE_SOURCE_DIR}/src/s57obj.cpp + ${CMAKE_SOURCE_DIR}/src/S57QueryDialog.cpp + ${CMAKE_SOURCE_DIR}/src/safe_mode.cpp + ${CMAKE_SOURCE_DIR}/src/SencManager.cpp + ${CMAKE_SOURCE_DIR}/src/SendToGpsDlg.cpp + ${CMAKE_SOURCE_DIR}/src/SendToPeerDlg.cpp + ${CMAKE_SOURCE_DIR}/src/shaders.cpp + ${CMAKE_SOURCE_DIR}/src/Station_Data.cpp + ${CMAKE_SOURCE_DIR}/src/styles.cpp + ${CMAKE_SOURCE_DIR}/src/svg_utils.cpp + ${CMAKE_SOURCE_DIR}/src/TCDataFactory.cpp + ${CMAKE_SOURCE_DIR}/src/TCDataSource.cpp + ${CMAKE_SOURCE_DIR}/src/TCDS_Ascii_Harmonic.cpp + ${CMAKE_SOURCE_DIR}/src/TCDS_Binary_Harmonic.cpp + ${CMAKE_SOURCE_DIR}/src/tcmgr.cpp + ${CMAKE_SOURCE_DIR}/src/TCWin.cpp + ${CMAKE_SOURCE_DIR}/src/thumbwin.cpp + ${CMAKE_SOURCE_DIR}/src/toolbar.cpp + ${CMAKE_SOURCE_DIR}/src/track_gui.cpp + ${CMAKE_SOURCE_DIR}/src/trackprintout.cpp + ${CMAKE_SOURCE_DIR}/src/TrackPropDlg.cpp + ${CMAKE_SOURCE_DIR}/src/TTYScroll.cpp + ${CMAKE_SOURCE_DIR}/src/TTYWindow.cpp + ${CMAKE_SOURCE_DIR}/src/undo.cpp + ${CMAKE_SOURCE_DIR}/src/update_mgr.cpp + ${CMAKE_SOURCE_DIR}/src/viewport.cpp + ${CMAKE_SOURCE_DIR}/src/waypointman_gui.cpp + # ${CMAKE_SOURCE_DIR}/src/wificlient.cpp ) if (LINUX) - list(APPEND HDRS include/comm_drv_n2k_socketcan.h ) + list(APPEND MODEL_HDRS include/comm_drv_n2k_socketcan.h ) list(APPEND MODEL_SRC ${CMAKE_SOURCE_DIR}/src/comm_drv_n2k_socketcan.cpp) endif () -SET(HDRS ${HDRS} include/shaders.h) -SET(SRCS ${SRCS} src/shaders.cpp) +set(GUI_HDRS ${GUI_HDRS} include/shaders.h) +set(GUI_SRC ${GUI_SRC} src/shaders.cpp) if (OCPN_USE_GARMINHOST) message(STATUS "GarminHost is enabled.") @@ -1210,28 +1229,37 @@ if (OCPN_USE_GARMINHOST) endif () if (LINUX) - list(APPEND SRCS src/linux_devices.cpp src/udev_rule_mgr.cpp) - list(APPEND HDRS include/linux_devices.h include/udev_rule_mgr.h) + list(APPEND GUI_SRC src/linux_devices.cpp src/udev_rule_mgr.cpp) + list(APPEND GUI_HDRS include/linux_devices.h include/udev_rule_mgr.h) endif () if (APPLE) - list(APPEND HDRS include/macutils.h ) + list(APPEND MODEL_HDRS include/macutils.h ) list(APPEND MODEL_SRC ${CMAKE_SOURCE_DIR}/src/macutils.c) endif () if (LINUX AND OCPN_USE_CRASHREPORT) - list(APPEND HDRS include/crashprint.h) - list(APPEND SRCS src/crashprint.cpp) + list(APPEND GUI_HDRS include/crashprint.h) + list(APPEND GUI_SRC src/crashprint.cpp) +endif () + +if (QT_ANDROID) + set(GUI_HDRS ${GUI_HDRS} include/androidUTIL.h) + set(GUI_SRC ${GUI_SRC} src/androidUTIL.cpp) + set(GUI_SRC ${GUI_SRC} src/comm_drv_n0183_android_int.cpp) + set(GUI_SRC ${GUI_SRC} src/comm_drv_n0183_android_bt.cpp) endif () -IF(QT_ANDROID) - SET(HDRS ${HDRS} include/androidUTIL.h) - SET(SRCS ${SRCS} src/androidUTIL.cpp) - SET(SRCS ${SRCS} src/comm_drv_n0183_android_int.cpp) - SET(SRCS ${SRCS} src/comm_drv_n0183_android_bt.cpp) -ENDIF(QT_ANDROID) +set(SRCS ${GUI_SRC} ${MODEL_SRC}) + +set(HDRS ${MODEL_HDRS} ${GUI_HDRS}) + +# Temporary support for #3513, to be removed +file(WRITE ${CMAKE_BINARY_DIR}/gui-src.txt "${GUI_SRC}") +file(WRITE ${CMAKE_BINARY_DIR}/gui-headers.txt "${GUI_HDRS}") +file(WRITE ${CMAKE_BINARY_DIR}/model-hdrs.txt "${MODEL_HDRS}") +file(WRITE ${CMAKE_BINARY_DIR}/model-src.txt "${MODEL_SRC}") -set(SRCS ${SRCS} ${MODEL_SRC}) if (APPLE) add_executable(${PACKAGE_NAME} MACOSX_BUNDLE ${HDRS} ${SRCS}) @@ -3313,5 +3341,3 @@ if (QT_ANDROID) add_definitions(-DMG_ENABLE_OPENSSL) endif (QT_ANDROID) - -add_custom_target(print-model-files COMMAND cmake -E echo ${MODEL_SRC}) diff --git a/src/console.cpp b/src/console.cpp index 485de74b95..1064aa6049 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -70,6 +70,7 @@ #include "plugin_handler.h" #include "plugin_loader.h" #include "routeman.h" +#include "S57ClassRegistrar.h" #include "select.h" #include "track.h" @@ -118,6 +119,8 @@ float g_selection_radius_touch_mm; int g_nCOMPortCheck = 32; bool g_benableUDPNullHeader; +S57ClassRegistrar *g_poRegistrar; + std::vector g_TrackList; wxString AISTargetNameFileName; AISTargetAlertDialog* g_pais_alert_dialog_active;