From e0a46a2769435f9d9274d4e180d5becb2f77fd24 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 3 Jan 2025 17:58:29 -0500 Subject: [PATCH 1/4] FPP Rediscover and KeepOpen --- xLights/TabSetup.cpp | 7 ++ xLights/controllers/FPP.h | 5 + xLights/controllers/FPPConnectDialog.cpp | 114 ++++++++++++++++++----- xLights/controllers/FPPConnectDialog.h | 28 +++--- xLights/wxsmith/FPPConnectDialog.wxs | 19 +++- xLights/xLightsMain.h | 1 + 6 files changed, 137 insertions(+), 37 deletions(-) diff --git a/xLights/TabSetup.cpp b/xLights/TabSetup.cpp index 02206fb189..bcd6d3a549 100644 --- a/xLights/TabSetup.cpp +++ b/xLights/TabSetup.cpp @@ -70,6 +70,7 @@ const long xLightsFrame::ID_NETWORK_ACTIVEXLIGHTS = wxNewId(); const long xLightsFrame::ID_NETWORK_INACTIVE = wxNewId(); const long xLightsFrame::ID_NETWORK_DELETE = wxNewId(); const long xLightsFrame::ID_NETWORK_UNLINKFROMBASE = wxNewId(); +const long xLightsFrame::ID_NETWORK_UPLOADOUTPUT = wxNewId(); #pragma region Show Directory void xLightsFrame::OnMenuMRU(wxCommandEvent& event) { @@ -2198,6 +2199,8 @@ void xLightsFrame::OnListControllersItemRClick(wxListEvent& event) { bool allSelectedControllersFromBase = std::all_of(selectedControllers.begin(), selectedControllers.end(), [](const Controller* controller) { return controller->IsFromBase(); }); bool enableActivateMenuItems = selectedControllers.size() > 0 && !anySelectedControllersFromBase; bool enableUnlinkFromBaseMenuItem = selectedControllers.size() > 0 && allSelectedControllersFromBase; + bool anySelectedControllersSupportUpload = std::any_of(selectedControllers.begin(), selectedControllers.end(), [](const Controller* controller) { return controller->SupportsUpload(); }); + bool enableUploadMenuItem = selectedControllers.size() == 1 && anySelectedControllersSupportUpload; mnu.Append(ID_NETWORK_ADDETHERNET, ethernet)->Enable(ButtonAddControllerSerial->IsEnabled()); mnu.Append(ID_NETWORK_ADDNULL, "Insert NULL")->Enable(ButtonAddControllerSerial->IsEnabled()); @@ -2207,6 +2210,7 @@ void xLightsFrame::OnListControllersItemRClick(wxListEvent& event) { mnu.Append(ID_NETWORK_INACTIVE, "Inactivate")->Enable(ButtonAddControllerSerial->IsEnabled() && enableActivateMenuItems); mnu.Append(ID_NETWORK_DELETE, "Delete")->Enable(ButtonAddControllerSerial->IsEnabled()); mnu.Append(ID_NETWORK_UNLINKFROMBASE, "Unlink from Base Show Folder")->Enable(ButtonAddControllerSerial->IsEnabled() && enableUnlinkFromBaseMenuItem); + mnu.Append(ID_NETWORK_UPLOADOUTPUT, "Upload Output")->Enable(ButtonAddControllerSerial->IsEnabled() && enableUploadMenuItem); mnu.Connect(wxEVT_MENU, (wxObjectEventFunction)&xLightsFrame::OnListControllerPopup, nullptr, this); PopupMenu(&mnu); @@ -2274,6 +2278,9 @@ void xLightsFrame::OnListControllerPopup(wxCommandEvent& event) { _outputModelManager.AddASAPWork(OutputModelManager::WORK_UPDATE_NETWORK_LIST, "OnListControllerPopup:DELETE"); _outputModelManager.AddLayoutTabWork(OutputModelManager::WORK_CALCULATE_START_CHANNELS, "OnListControllerPopup:DELETE"); } + else if (id == ID_NETWORK_UPLOADOUTPUT) { + OnButtonUploadOutputClick(event); + } } #pragma endregion diff --git a/xLights/controllers/FPP.h b/xLights/controllers/FPP.h index 3828f06b2b..ab03094be2 100644 --- a/xLights/controllers/FPP.h +++ b/xLights/controllers/FPP.h @@ -67,6 +67,7 @@ class FPP : public BaseController std::string controllerVendor; std::string controllerModel; std::string controllerVariant; + bool upload; wxWindow *parent = nullptr; void setProgress(FPPUploadProgressDialog*d, wxGauge *g) { progressDialog = d; progress = g; } @@ -239,3 +240,7 @@ static inline bool sortByIP(const FPP* i, const FPP* j) { return i->ipAddress < j->ipAddress; } + +static inline bool sortByUpload(const FPP* i, const FPP* j) { + return i->upload < j->upload; +} diff --git a/xLights/controllers/FPPConnectDialog.cpp b/xLights/controllers/FPPConnectDialog.cpp index 68cf8e7c5b..1e32de21f4 100644 --- a/xLights/controllers/FPPConnectDialog.cpp +++ b/xLights/controllers/FPPConnectDialog.cpp @@ -38,17 +38,19 @@ #include //(*IdInit(FPPConnectDialog) -const long FPPConnectDialog::ID_SCROLLEDWINDOW1 = wxNewId(); -const long FPPConnectDialog::ID_STATICTEXT1 = wxNewId(); -const long FPPConnectDialog::ID_CHOICE_FILTER = wxNewId(); -const long FPPConnectDialog::ID_STATICTEXT2 = wxNewId(); -const long FPPConnectDialog::ID_CHOICE_FOLDER = wxNewId(); -const long FPPConnectDialog::ID_STATICTEXT3 = wxNewId(); -const long FPPConnectDialog::ID_PANEL2 = wxNewId(); -const long FPPConnectDialog::ID_PANEL1 = wxNewId(); -const long FPPConnectDialog::ID_SPLITTERWINDOW1 = wxNewId(); -const long FPPConnectDialog::ID_BUTTON1 = wxNewId(); -const long FPPConnectDialog::ID_BUTTON_Upload = wxNewId(); +const wxWindowID FPPConnectDialog::ID_SCROLLEDWINDOW1 = wxNewId(); +const wxWindowID FPPConnectDialog::ID_STATICTEXT1 = wxNewId(); +const wxWindowID FPPConnectDialog::ID_CHOICE_FILTER = wxNewId(); +const wxWindowID FPPConnectDialog::ID_STATICTEXT2 = wxNewId(); +const wxWindowID FPPConnectDialog::ID_CHOICE_FOLDER = wxNewId(); +const wxWindowID FPPConnectDialog::ID_STATICTEXT3 = wxNewId(); +const wxWindowID FPPConnectDialog::ID_PANEL2 = wxNewId(); +const wxWindowID FPPConnectDialog::ID_PANEL1 = wxNewId(); +const wxWindowID FPPConnectDialog::ID_SPLITTERWINDOW1 = wxNewId(); +const wxWindowID FPPConnectDialog::ID_BUTTON1 = wxNewId(); +const wxWindowID FPPConnectDialog::ID_BUTTON2 = wxNewId(); +const wxWindowID FPPConnectDialog::ID_CHECKBOX1 = wxNewId(); +const wxWindowID FPPConnectDialog::ID_BUTTON_Upload = wxNewId(); //*) const long FPPConnectDialog::ID_MNU_SELECTALL = wxNewId(); @@ -62,6 +64,7 @@ static const long ID_POPUP_MNU_SORT_NAME = wxNewId(); static const long ID_POPUP_MNU_SORT_IP = wxNewId(); static const long ID_POPUP_MNU_SELECT_ALL = wxNewId(); static const long ID_POPUP_MNU_DESELECT_ALL = wxNewId(); +static const long ID_POPUP_MNU_SORT_UPLOAD = wxNewId(); static const long ID_POPUP_MNU_CAPE_SELECT_ALL = wxNewId(); static const long ID_POPUP_MNU_CAPE_DESELECT_ALL = wxNewId(); static const long ID_POPUP_MNU_MEDIA_DESELECT_ALL = wxNewId(); @@ -138,13 +141,18 @@ FPPConnectDialog::FPPConnectDialog(wxWindow* parent, OutputManager* outputManage Panel1->SetSizer(FlexGridSizer2); SplitterWindow1->SplitHorizontally(FPPInstanceList, Panel1); FlexGridSizer1->Add(SplitterWindow1, 1, wxALL|wxEXPAND, 5); - FlexGridSizer4 = new wxFlexGridSizer(0, 4, 0, 0); + FlexGridSizer4 = new wxFlexGridSizer(0, 6, 0, 0); FlexGridSizer4->AddGrowableCol(1); FlexGridSizer4->AddGrowableRow(0); AddFPPButton = new wxButton(this, ID_BUTTON1, _("Add FPP"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON1")); FlexGridSizer4->Add(AddFPPButton, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); + ReDiscover = new wxButton(this, ID_BUTTON2, _("Re-Discover"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON2")); + FlexGridSizer4->Add(ReDiscover, 1, wxALL, 5); StaticText3 = new wxStaticText(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, _T("wxID_ANY")); FlexGridSizer4->Add(StaticText3, 1, wxALL|wxEXPAND, 5); + KeepWinOpen = new wxCheckBox(this, ID_CHECKBOX1, _("Keep Open"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_CHECKBOX1")); + KeepWinOpen->SetValue(false); + FlexGridSizer4->Add(KeepWinOpen, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); Button_Upload = new wxButton(this, ID_BUTTON_Upload, _("Upload"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("ID_BUTTON_Upload")); FlexGridSizer4->Add(Button_Upload, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5); cancelButton = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, _T("wxID_CANCEL")); @@ -156,6 +164,7 @@ FPPConnectDialog::FPPConnectDialog(wxWindow* parent, OutputManager* outputManage Connect(ID_CHOICE_FILTER, wxEVT_COMMAND_CHOICE_SELECTED, (wxObjectEventFunction)&FPPConnectDialog::OnChoiceFilterSelect); Connect(ID_CHOICE_FOLDER, wxEVT_COMMAND_CHOICE_SELECTED, (wxObjectEventFunction)&FPPConnectDialog::OnChoiceFolderSelect); Connect(ID_BUTTON1, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&FPPConnectDialog::OnAddFPPButtonClick); + Connect(ID_BUTTON2, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&FPPConnectDialog::OnFPPReDiscoverClick); Connect(ID_BUTTON_Upload, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction)&FPPConnectDialog::OnButton_UploadClick); Connect(wxID_ANY, wxEVT_CLOSE_WINDOW, (wxObjectEventFunction)&FPPConnectDialog::OnClose); //*) @@ -406,25 +415,31 @@ void FPPConnectDialog::UploadPopupMenu(wxContextMenuEvent& event) { wxMenu mnu; mnu.Append(ID_POPUP_MNU_SELECT_ALL, "Select All"); mnu.Append(ID_POPUP_MNU_DESELECT_ALL, "Deselect All"); + mnu.Append(ID_POPUP_MNU_SORT_UPLOAD, "Sort"); mnu.Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(FPPConnectDialog::OnUploadPopupClick), NULL, this); PopupMenu(&mnu); } void FPPConnectDialog::OnUploadPopupClick(wxCommandEvent& event) { - int row = 0; - for (const auto& inst : instances) { - std::string l = inst->hostName + " - " + inst->ipAddress; - if (inst->fppType == FPP_TYPE::FPP) { - if (inst->supportedForFPPConnect()) { - std::string rowStr = std::to_string(row); - if (event.GetId() == ID_POPUP_MNU_SELECT_ALL) { - SetCheckValue(CHECK_COL + rowStr, true); - } else if (event.GetId() == ID_POPUP_MNU_DESELECT_ALL) { - SetCheckValue(CHECK_COL + rowStr, false); + if (event.GetId() == ID_POPUP_MNU_SORT_UPLOAD) { + instances.sort(sortByUpload); + PopulateFPPInstanceList(); + } else { + int row = 0; + for (const auto& inst : instances) { + std::string l = inst->hostName + " - " + inst->ipAddress; + if (inst->fppType == FPP_TYPE::FPP) { + if (inst->supportedForFPPConnect()) { + std::string rowStr = std::to_string(row); + if (event.GetId() == ID_POPUP_MNU_SELECT_ALL) { + SetCheckValue(CHECK_COL + rowStr, true); + } else if (event.GetId() == ID_POPUP_MNU_DESELECT_ALL) { + SetCheckValue(CHECK_COL + rowStr, false); + } } } + row++; } - row++; } } @@ -468,7 +483,6 @@ void FPPConnectDialog::PopulateFPPInstanceList(wxProgressDialog *prgs) { for (const auto& inst : instances) { std::string rowStr = std::to_string(row); wxCheckBox *doUploadCheckbox = new wxCheckBox(FPPInstanceList, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, CHECK_COL + rowStr); - doUploadCheckbox->SetValue(true); FPPInstanceSizer->Add(doUploadCheckbox, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1); std::string l = inst->hostName + " - " + inst->ipAddress; @@ -544,7 +558,6 @@ void FPPConnectDialog::PopulateFPPInstanceList(wxProgressDialog *prgs) { prgs->Pulse("Probing information from " + l); } wxCheckBox *CheckBox1 = new wxCheckBox(FPPInstanceList, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator, MEDIA_COL + rowStr); - CheckBox1->SetValue(inst->mode != "remote"); FPPInstanceSizer->Add(CheckBox1, 1, wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 1); wxChoice* Choice1 = new wxChoice(FPPInstanceList, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, 0, 0, wxDefaultValidator, MODELS_COL + rowStr); wxFont font = Choice1->GetFont(); @@ -1005,9 +1018,12 @@ void FPPConnectDialog::OnButton_UploadClick(wxCommandEvent& event) if (!c) { SaveSettings(); + auto kwo = this->KeepWinOpen->Get3StateValue(); + if (kwo != wxCHK_CHECKED) { #ifndef _DEBUG EndDialog(wxID_CLOSE); #endif + } } Button_Upload->Enable(true); AddFPPButton->Enable(true); @@ -1458,6 +1474,7 @@ void FPPConnectDialog::ApplySavedHostSettings() int lval; if (config->Read("FPPConnectUpload_" + keyPostfx, &bval)) { SetCheckValue(CHECK_COL + rowStr, bval); + inst->upload = bval; } if (config->Read("FPPConnectUploadFSEQType_" + keyPostfx, &lval)) { SetChoiceValueIndex(FSEQ_COL + rowStr, lval); @@ -1499,6 +1516,53 @@ void FPPConnectDialog::SequenceListPopup(wxTreeListEvent& event) PopupMenu(&mnu); } +void FPPConnectDialog::OnFPPReDiscoverClick(wxCommandEvent& event) { + int curSize = instances.size(); + std::list add; + + wxProgressDialog prgs("Discovering FPP Instances", + "Discovering FPP Instances", 100, this); + prgs.Pulse("Discovering FPP Instances"); + + std::string fppConnectIP = ""; + prgs.Show(); + instances = FPP::GetInstances(this, _outputManager); + + if (curSize < instances.size()) { + int cur = 0; + for (const auto& fpp : instances) { + if (cur >= curSize) { + prgs.Pulse("Gathering configuration for " + fpp->hostName + " - " + fpp->ipAddress); + fpp->AuthenticateAndUpdateVersions(); + fpp->probePixelControllerType(); + } + cur++; + } + + instances.sort(sortByIP); + // it worked, we found some new instances, record this + wxConfigBase* config = wxConfigBase::Get(); + wxString ip; + config->Read("FPPConnectForcedIPs", &ip); + if (locationSortCol == "name") { + instances.sort(sortByName); + } else { + instances.sort([this](const FPP* a, const FPP* b) { + std::vector aComponents = SplitIP(a->ipAddress); + std::vector bComponents = SplitIP(b->ipAddress); + for (size_t i = 0; i < aComponents.size() && i < bComponents.size(); ++i) { + if (aComponents[i] != bComponents[i]) + return aComponents[i] < bComponents[i]; + } + return aComponents.size() < bComponents.size(); + }); + } + PopulateFPPInstanceList(); + } + + prgs.Hide(); +} + void FPPConnectDialog::OnAddFPPButtonClick(wxCommandEvent& event) { wxTextEntryDialog dlg(this, "Find FPP Instance", "Enter IP address or hostname for FPP Instance"); diff --git a/xLights/controllers/FPPConnectDialog.h b/xLights/controllers/FPPConnectDialog.h index 9535f95964..1670acff14 100644 --- a/xLights/controllers/FPPConnectDialog.h +++ b/xLights/controllers/FPPConnectDialog.h @@ -6,6 +6,7 @@ //(*Headers(FPPConnectDialog) #include +#include #include #include #include @@ -36,6 +37,8 @@ class FPPConnectDialog: public wxDialog //(*Declarations(FPPConnectDialog) wxButton* AddFPPButton; wxButton* Button_Upload; + wxButton* ReDiscover; + wxCheckBox* KeepWinOpen; wxChoice* ChoiceFilter; wxChoice* ChoiceFolder; wxFlexGridSizer* FPPInstanceSizer; @@ -53,17 +56,19 @@ class FPPConnectDialog: public wxDialog protected: //(*Identifiers(FPPConnectDialog) - static const long ID_SCROLLEDWINDOW1; - static const long ID_STATICTEXT1; - static const long ID_CHOICE_FILTER; - static const long ID_STATICTEXT2; - static const long ID_CHOICE_FOLDER; - static const long ID_STATICTEXT3; - static const long ID_PANEL2; - static const long ID_PANEL1; - static const long ID_SPLITTERWINDOW1; - static const long ID_BUTTON1; - static const long ID_BUTTON_Upload; + static const wxWindowID ID_SCROLLEDWINDOW1; + static const wxWindowID ID_STATICTEXT1; + static const wxWindowID ID_CHOICE_FILTER; + static const wxWindowID ID_STATICTEXT2; + static const wxWindowID ID_CHOICE_FOLDER; + static const wxWindowID ID_STATICTEXT3; + static const wxWindowID ID_PANEL2; + static const wxWindowID ID_PANEL1; + static const wxWindowID ID_SPLITTERWINDOW1; + static const wxWindowID ID_BUTTON1; + static const wxWindowID ID_BUTTON2; + static const wxWindowID ID_CHECKBOX1; + static const wxWindowID ID_BUTTON_Upload; //*) static const long ID_MNU_SELECTALL; @@ -83,6 +88,7 @@ class FPPConnectDialog: public wxDialog void OnClose(wxCloseEvent& event); void SequenceListPopup(wxTreeListEvent& event); void OnAddFPPButtonClick(wxCommandEvent& event); + void OnFPPReDiscoverClick(wxCommandEvent& event); void OnChoiceFolderSelect(wxCommandEvent& event); void OnChoiceFilterSelect(wxCommandEvent& event); void HostSortMenu(wxContextMenuEvent& event); diff --git a/xLights/wxsmith/FPPConnectDialog.wxs b/xLights/wxsmith/FPPConnectDialog.wxs index 7f83e777ae..2550a765c8 100644 --- a/xLights/wxsmith/FPPConnectDialog.wxs +++ b/xLights/wxsmith/FPPConnectDialog.wxs @@ -96,7 +96,7 @@ - 4 + 6 1 0 @@ -108,6 +108,15 @@ 5 + + + + + + wxALL + 5 + + @@ -116,6 +125,14 @@ 5 + + + + + wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + 5 + + diff --git a/xLights/xLightsMain.h b/xLights/xLightsMain.h index 2851c2c6c7..8745175592 100755 --- a/xLights/xLightsMain.h +++ b/xLights/xLightsMain.h @@ -1878,6 +1878,7 @@ private : static const long ID_NETWORK_UNLINKFROMBASE; static const long ID_NETWORK_INACTIVE; static const long ID_NETWORK_DELETE; + static const long ID_NETWORK_UPLOADOUTPUT; #define isRandom(ctl) isRandom_(ctl, #ctl) //(buttonState[std::string(ctl->GetName())] == Random) From d3164911553c30b1e5a5d99f04dde81ac7ddc47b Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 4 Jan 2025 13:31:35 -0500 Subject: [PATCH 2/4] case-insensitive hostname sort --- xLights/controllers/FPP.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/xLights/controllers/FPP.h b/xLights/controllers/FPP.h index ab03094be2..10a721a18b 100644 --- a/xLights/controllers/FPP.h +++ b/xLights/controllers/FPP.h @@ -233,7 +233,15 @@ static inline int case_insensitive_match(std::string s1, std::string s2) static inline bool sortByName(const FPP* i, const FPP* j) { - return i->hostName < j->hostName; + std::string lowerI = i->hostName; + std::string lowerJ = j->hostName; + + std::transform(lowerI.begin(), lowerI.end(), lowerI.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::transform(lowerJ.begin(), lowerJ.end(), lowerJ.begin(), + [](unsigned char c) { return std::tolower(c); }); + + return lowerI < lowerJ; } static inline bool sortByIP(const FPP* i, const FPP* j) From 09aaf200ab6296577b067e669d65856b7e437635 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 6 Jan 2025 09:06:39 -0500 Subject: [PATCH 3/4] if uuid is avail use that for settings --- xLights/controllers/FPP.cpp | 2 + xLights/controllers/FPP.h | 2 +- xLights/controllers/FPPConnectDialog.cpp | 61 +++++++++++++----------- xLights/controllers/FPPConnectDialog.h | 1 + 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/xLights/controllers/FPP.cpp b/xLights/controllers/FPP.cpp index b7bf572f7b..93f69e6872 100644 --- a/xLights/controllers/FPP.cpp +++ b/xLights/controllers/FPP.cpp @@ -3845,6 +3845,7 @@ void FPP::MapToFPPInstances(Discovery &discovery, std::list &instances, Ou FPP *fpp = new FPP(res->ip, res->proxy, res->pixelControllerType); fpp->ipAddress = res->ip;//not needed, in constructor fpp->hostName = res->hostname; + fpp->uuid = res->uuid; fpp->description = res->description; fpp->platform = res->platform; fpp->model = res->platformModel; @@ -3874,6 +3875,7 @@ void FPP::MapToFPPInstances(Discovery &discovery, std::list &instances, Ou } else { setIfEmpty(fpp->proxy, res->proxy); setIfEmpty(fpp->hostName, res->hostname); + setIfEmpty(fpp->uuid, res->uuid); setIfEmpty(fpp->description, res->description); setIfEmpty(fpp->platform, res->platform); setIfEmpty(fpp->model, res->platformModel); diff --git a/xLights/controllers/FPP.h b/xLights/controllers/FPP.h index 10a721a18b..0d4936015c 100644 --- a/xLights/controllers/FPP.h +++ b/xLights/controllers/FPP.h @@ -250,5 +250,5 @@ static inline bool sortByIP(const FPP* i, const FPP* j) } static inline bool sortByUpload(const FPP* i, const FPP* j) { - return i->upload < j->upload; + return i->upload > j->upload; } diff --git a/xLights/controllers/FPPConnectDialog.cpp b/xLights/controllers/FPPConnectDialog.cpp index d507db2a24..3348e849e4 100644 --- a/xLights/controllers/FPPConnectDialog.cpp +++ b/xLights/controllers/FPPConnectDialog.cpp @@ -1520,11 +1520,8 @@ void FPPConnectDialog::SaveSettings(bool onlyInsts) int row = 0; for (const auto& inst : instances) { std::string rowStr = std::to_string(row); - wxString keyPostfx = inst->ipAddress; - keyPostfx.Replace(":", "_"); - keyPostfx.Replace("/", "_"); - keyPostfx.Replace("\\", "_"); - keyPostfx.Replace(".", "_"); + wxString keyPostfx = (inst->uuid.empty() ? inst->ipAddress : inst->uuid); + keyPostfx = Fixitup(keyPostfx); config->Write("FPPConnectUpload_" + keyPostfx, GetCheckValue(CHECK_COL + rowStr)); config->Write("FPPConnectUploadMedia_" + keyPostfx, GetCheckValue(MEDIA_COL + rowStr)); config->Write("FPPConnectUploadFSEQType_" + keyPostfx, GetChoiceValueIndex(FSEQ_COL + rowStr)); @@ -1537,52 +1534,59 @@ void FPPConnectDialog::SaveSettings(bool onlyInsts) config->Flush(); } +wxString FPPConnectDialog::Fixitup(wxString val) { + val.Replace(":", "_"); + val.Replace("/", "_"); + val.Replace("\\", "_"); + val.Replace(".", "_"); + return val; +} + void FPPConnectDialog::ApplySavedHostSettings() { - /* - static const std::string CHECK_COL = "ID_UPLOAD_"; - static const std::string FSEQ_COL = "ID_FSEQTYPE_"; - static const std::string MEDIA_COL = "ID_MEDIA_"; - static const std::string MODELS_COL = "ID_MODELS_"; - static const std::string UDP_COL = "ID_UDPOUT_"; - static const std::string PLAYLIST_COL = "ID_PLAYLIST_"; - static const std::string UPLOAD_CONTROLLER_COL = "ID_CONTROLLER_"; - */ - - wxConfigBase* config = wxConfigBase::Get(); if (config != nullptr) { int row = 0; for (const auto& inst : instances) { std::string rowStr = std::to_string(row); - wxString keyPostfx = inst->ipAddress; - keyPostfx.Replace(":", "_"); - keyPostfx.Replace("/", "_"); - keyPostfx.Replace("\\", "_"); - keyPostfx.Replace(".", "_"); bool bval; int lval; - if (config->Read("FPPConnectUpload_" + keyPostfx, &bval)) { + if (config->Read("FPPConnectUpload_" + Fixitup(inst->uuid), &bval)) { + SetCheckValue(CHECK_COL + rowStr, bval); + inst->upload = bval; + } else if (config->Read("FPPConnectUpload_" + Fixitup(inst->ipAddress), &bval)) { SetCheckValue(CHECK_COL + rowStr, bval); inst->upload = bval; } - if (config->Read("FPPConnectUploadFSEQType_" + keyPostfx, &lval)) { + if (config->Read("FPPConnectUploadFSEQType_" + Fixitup(inst->uuid), &lval)) { SetChoiceValueIndex(FSEQ_COL + rowStr, lval); + } else if (config->Read("FPPConnectUploadFSEQType_" + Fixitup(inst->ipAddress), &lval)) { + SetCheckValue(FSEQ_COL + rowStr, lval); } - if (config->Read("FPPConnectUploadMedia_" + keyPostfx, &bval)) { + if (config->Read("FPPConnectUploadMedia_" + Fixitup(inst->uuid), &bval)) { + SetCheckValue(MEDIA_COL + rowStr, bval); + } else if (config->Read("FPPConnectUploadMedia_" + Fixitup(inst->ipAddress), &bval)) { SetCheckValue(MEDIA_COL + rowStr, bval); } - if (config->Read("FPPConnectUploadModels_" + keyPostfx, &lval)) { + if (config->Read("FPPConnectUploadModels_" + Fixitup(inst->uuid), &lval)) { SetChoiceValueIndex(MODELS_COL + rowStr, lval); + } else if (config->Read("FPPConnectUploadModels_" + Fixitup(inst->ipAddress), &lval)) { + SetCheckValue(MODELS_COL + rowStr, lval); } - if (config->Read("FPPConnectUploadUDPOut_" + keyPostfx, &lval)) { + if (config->Read("FPPConnectUploadUDPOut_" + Fixitup(inst->uuid), &lval)) { SetChoiceValueIndex(UDP_COL + rowStr, lval); + } else if (config->Read("FPPConnectUploadUDPOut_" + Fixitup(inst->ipAddress), &lval)) { + SetCheckValue(UDP_COL + rowStr, lval); } - if (config->Read("FPPConnectUploadPixelOut_" + keyPostfx, &bval)) { + if (config->Read("FPPConnectUploadPixelOut_" + Fixitup(inst->uuid), &bval)) { + SetCheckValue(UPLOAD_CONTROLLER_COL + rowStr, bval); + } else if (config->Read("FPPConnectUploadPixelOut_" + Fixitup(inst->ipAddress), &bval)) { SetCheckValue(UPLOAD_CONTROLLER_COL + rowStr, bval); } - if (config->Read("FPPConnectUploadProxy_" + keyPostfx, &bval)) { + if (config->Read("FPPConnectUploadProxy_" + Fixitup(inst->uuid), &bval)) { + SetCheckValue(PROXY_COL + rowStr, bval); + } else if (config->Read("FPPConnectUploadProxy_" + Fixitup(inst->ipAddress), &bval)) { SetCheckValue(PROXY_COL + rowStr, bval); } row++; @@ -1590,7 +1594,6 @@ void FPPConnectDialog::ApplySavedHostSettings() } } - void FPPConnectDialog::OnClose(wxCloseEvent& event) { EndDialog(0); diff --git a/xLights/controllers/FPPConnectDialog.h b/xLights/controllers/FPPConnectDialog.h index 341902884d..aa0a5cab6c 100644 --- a/xLights/controllers/FPPConnectDialog.h +++ b/xLights/controllers/FPPConnectDialog.h @@ -28,6 +28,7 @@ class FPPConnectDialog: public wxDialog { void SaveSettings(bool onlyInsts = false); void ApplySavedHostSettings(); + wxString Fixitup(wxString val); public: From 2f2fb7c0706bf2320d786691a3d5134f7ee5a539 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 8 Jan 2025 22:18:06 -0500 Subject: [PATCH 4/4] batchrender->selseq, fseq upload logging --- xLights/controllers/FPP.cpp | 8 ++++- xLights/controllers/FPP.h | 1 + xLights/controllers/FPPConnectDialog.cpp | 46 ++++++++++++++++++++---- xLights/controllers/FPPConnectDialog.h | 2 +- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/xLights/controllers/FPP.cpp b/xLights/controllers/FPP.cpp index 93f69e6872..f8315d2f37 100644 --- a/xLights/controllers/FPP.cpp +++ b/xLights/controllers/FPP.cpp @@ -778,8 +778,10 @@ bool FPP::uploadFile(const std::string &utfFilename, const std::string &file) { logger_base.debug("Renaming done."); } } + logger_base.debug(utfFilename + " upload complete to " + this->hostName + " (" + this->ipAddress + "). Bytes sent:" + std::to_string(data->totalWritten) + "."); } else { messages.push_back("ERROR Uploading file: " + utfFilename + " Response Code: " + std::to_string(response_code)); + faileduploads.push_back(filename); logger_base.warn("Did not get 200 response code: %d", response_code); } @@ -840,7 +842,8 @@ int progress_callback(void *clientp, void prepareCurlForMulti(V7ProgressStruct *ps) { static log4cpp::Category& logger_curl = log4cpp::Category::getInstance(std::string("log_curl")); - + static log4cpp::Category& logger_base = log4cpp::Category::getInstance(std::string("log_base")); + constexpr uint64_t BLOCK_SIZE = 16*1024*1024; CurlManager::CurlPrivateData *cpd = nullptr; CURL *curl = CurlManager::INSTANCE.createCurl(ps->fullUrl, &cpd, true); @@ -869,6 +872,7 @@ void prepareCurlForMulti(V7ProgressStruct *ps) { if (read != remaining) { logger_curl.info("ERROR Uploading file: " + ps->filename + " Could not read source file."); ps->instance->messages.push_back("ERROR Uploading file: " + ps->filename + " Could not read source file."); + ps->instance->faileduploads.push_back(ps->filename); } std::string contentSizeHeader = "Content-Length: " + std::to_string(remaining); headers = curl_slist_append(headers, contentSizeHeader.c_str()); @@ -898,6 +902,7 @@ void prepareCurlForMulti(V7ProgressStruct *ps) { ++ps->errorCount; } else if (response_code != 200) { ps->instance->messages.push_back("ERROR Uploading file: " + ps->filename + ". Response code: " + std::to_string(response_code)); + ps->instance->faileduploads.push_back(ps->filename); cancelled = true; } else { ps->offset += remaining; @@ -905,6 +910,7 @@ void prepareCurlForMulti(V7ProgressStruct *ps) { uint64_t pct = (ps->offset * 1000) / ps->length; cancelled |= ps->instance->updateProgress(pct, false); if (cancelled || ps->offset >= ps->length) { + logger_base.debug(ps->filename + " upload complete to " + ps->instance->hostName + " (" + ps->instance->ipAddress + "). Bytes sent:" + std::to_string(ps->length) + "."); delete ps; } else { prepareCurlForMulti(ps); diff --git a/xLights/controllers/FPP.h b/xLights/controllers/FPP.h index 0d4936015c..6c794dafd2 100644 --- a/xLights/controllers/FPP.h +++ b/xLights/controllers/FPP.h @@ -75,6 +75,7 @@ class FPP : public BaseController std::list messages; + std::list faileduploads; int defaultConnectTimeout = 2000; std::map GetExpansionPorts(ControllerCaps* caps) const; diff --git a/xLights/controllers/FPPConnectDialog.cpp b/xLights/controllers/FPPConnectDialog.cpp index 3348e849e4..7fbced5269 100644 --- a/xLights/controllers/FPPConnectDialog.cpp +++ b/xLights/controllers/FPPConnectDialog.cpp @@ -67,6 +67,7 @@ static const long ID_POPUP_MNU_MEDIA_DESELECT_ALL = wxNewId(); static const long ID_POPUP_MNU_SELECT_HIGH = wxNewId(); static const long ID_POPUP_MNU_DESELECT_HIGH = wxNewId(); static const long ID_POPUP_MNU_SELECT_BATCH = wxNewId(); +static const long ID_POPUP_MNU_SELECT_FAILED = wxNewId(); static const long ID_POPUP_MNU_SELECT_SUBNET = wxNewId(); wxString locationSortCol = "ip"; @@ -710,7 +711,11 @@ void FPPConnectDialog::OnPopup(wxCommandEvent &event) { int id = event.GetId(); if (ID_POPUP_MNU_SELECT_BATCH == id) { - SelectBatchRenderSeq(); + SequenceSelector("BatchRendererItemList"); + return; + } + if (ID_POPUP_MNU_SELECT_FAILED == id) { + SequenceSelector("FPPConnectFailedList"); return; } wxTreeListItem item = CheckListBox_Sequences->GetFirstItem(); @@ -731,22 +736,22 @@ void FPPConnectDialog::OnPopup(wxCommandEvent &event) UpdateSeqCount(); } -void FPPConnectDialog::SelectBatchRenderSeq() { +void FPPConnectDialog::SequenceSelector(const std::string regexKey) { wxConfigBase* config = wxConfigBase::Get(); if (nullptr == config) { return; } wxString itcsv; - config->Read("BatchRendererItemList", &itcsv, ""); + config->Read(regexKey, &itcsv, ""); if (!itcsv.IsEmpty()) { - auto const& savedBatchItems = wxSplit(itcsv, ','); + auto const& list = wxSplit(itcsv, ','); xLightsFrame* frame = static_cast(GetParent()); wxString const& showDirectory = frame->GetShowDirectory(); wxString const& fseqDirectory = frame->GetFseqDirectory(); - if (savedBatchItems.empty()) { + if (list.empty()) { return; } auto uitem = CheckListBox_Sequences->GetFirstItem(); @@ -757,8 +762,8 @@ void FPPConnectDialog::SelectBatchRenderSeq() { } uitem = CheckListBox_Sequences->GetNextItem(uitem); } - for (auto const& bat_seq : savedBatchItems) { - auto const& xsqName = showDirectory + wxFileName::GetPathSeparator() + bat_seq; + for (auto const& seq : list) { + auto const& xsqName = showDirectory + wxFileName::GetPathSeparator() + seq; wxFileName fseqFile(xsqName); fseqFile.SetExt("fseq"); @@ -1069,6 +1074,14 @@ void FPPConnectDialog::OnButton_UploadClick(wxCommandEvent& event) Button_Upload->Enable(false); AddFPPButton->Enable(false); + wxConfigBase* config = wxConfigBase::Get(); + if (config != nullptr) { + config->Write("FPPConnectFailedList", ""); + } + config->Flush(); + for (const auto& inst : instances) { + inst->faileduploads.clear(); + } std::vector doUpload(instances.size()); int row = 0; @@ -1394,6 +1407,7 @@ void FPPConnectDialog::doUpload(FPPUploadProgressDialog *prgs, std::vector std::string messages; + wxString failedUploadsList; for (const auto& inst : instances) { std::string rowStr = std::to_string(row); if (inst->fppType == FPP_TYPE::FPP) { @@ -1418,8 +1432,25 @@ void FPPConnectDialog::doUpload(FPPUploadProgressDialog *prgs, std::vector messages += "\n"; } } + if (!inst->faileduploads.empty()) { + for (auto& m : inst->faileduploads) { + if (failedUploadsList.empty()) { + failedUploadsList = m; + } else { + failedUploadsList += ","; + failedUploadsList += m; + } + } + } row++; } + if (!failedUploadsList.empty()) { + wxConfigBase* config = wxConfigBase::Get(); + if (config != nullptr) { + config->Write("FPPConnectFailedList", failedUploadsList); + } + config->Flush(); + }; xLightsFrame* xlframe = static_cast(GetParent()); if (messages != "") { xlframe->SetStatusText("FPP Connect Upload had errors or warnings", 0); @@ -1607,6 +1638,7 @@ void FPPConnectDialog::SequenceListPopup(wxTreeListEvent& event) mnu.Append(ID_POPUP_MNU_SELECT_HIGH, "Select Highlighted"); mnu.Append(ID_POPUP_MNU_DESELECT_HIGH, "Deselect Highlighted"); mnu.Append(ID_POPUP_MNU_SELECT_BATCH, "Select Batch Render"); + mnu.Append(ID_POPUP_MNU_SELECT_FAILED, "Select Failed Uploads"); mnu.Connect(wxEVT_MENU, (wxObjectEventFunction)&FPPConnectDialog::OnPopup, nullptr, this); PopupMenu(&mnu); } diff --git a/xLights/controllers/FPPConnectDialog.h b/xLights/controllers/FPPConnectDialog.h index aa0a5cab6c..6920b2ab13 100644 --- a/xLights/controllers/FPPConnectDialog.h +++ b/xLights/controllers/FPPConnectDialog.h @@ -126,7 +126,7 @@ class FPPConnectDialog: public wxDialog void doUpload(FPPUploadProgressDialog *prgs, std::vector doUpload); std::vector SplitIP(const wxString& ip) const; - void SelectBatchRenderSeq(); + void SequenceSelector(const std::string regexKey); void SelectIPsWithSubnet(); DECLARE_EVENT_TABLE()