/* Copyright (C) 2020 - 2024, Thornwave Labs Inc
* Written by Razvan Turiac <razvan.turiac@thornwave.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the “Software”), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
* Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <gui_about.h>
#include <gui_dfu_progress.h>
#include <version.h>
#include <wx/wx.h>
#include <wx/uri.h>
#include <wx/sstream.h>
#include <wx/mstream.h>
#include <wx/protocol/http.h>
#include <thread>
GuiAbout::GuiAbout(wxWindow *parent, const wxString &title, Powermon &powermon): wxDialog(parent, wxID_ANY, title), mPowermon(powermon)
{
//main window sizer
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(main_sizer);
wxString str;
str.Printf(wxT("%s v%X.%02X. (C) Thornwave Labs Inc"), g_version_tag, g_version_bcd >> 8, g_version_bcd & 0xFF);
main_sizer->Add(new wxStaticText(this, wxID_ANY, str), 0, wxALL | wxEXPAND, 5);
str.Printf(wxT("Device name: %s"), mPowermon.getLastDeviceInfo().name);
main_sizer->Add(new wxStaticText(this, wxID_ANY, str), 0, wxALL | wxEXPAND, 5);
str.Printf(wxT("Device model: %s"), Powermon::getHardwareString(mPowermon.getLastDeviceInfo().hardware_revision_bcd));
main_sizer->Add(new wxStaticText(this, wxID_ANY, str), 0, wxALL | wxEXPAND, 5);
str.Printf(wxT("Device serial: %016" PRIX64), mPowermon.getLastDeviceInfo().serial);
main_sizer->Add(new wxStaticText(this, wxID_ANY, str), 0, wxALL | wxEXPAND, 5);
uint16_t version = mPowermon.getLastDeviceInfo().firmware_version_bcd;
str.Printf(wxT("Firmware: v%X.%02X"), version >> 8, version & 0xFF);
main_sizer->Add(new wxStaticText(this, wxID_ANY, str), 0, wxALL | wxEXPAND, 5);
if (Powermon::hasNetwork(mPowermon.getLastDeviceInfo().hardware_revision_bcd))
str = wxString(wxT("Device IP Address: ")) + Powermon::getIpAddressString(mPowermon.getLastDeviceInfo().address);
else
str = wxString(wxT("Device Bluetooth Address: ")) + Powermon::getMacAddressString(mPowermon.getLastDeviceInfo().address);
main_sizer->Add(new wxStaticText(this, wxID_ANY, str), 0, wxALL | wxEXPAND, 5);
mStatusStatic = new wxStaticText(this, wxID_ANY, wxT("Checking for firmware update ..."));
main_sizer->AddSpacer(20);
main_sizer->Add(mStatusStatic, 0, wxALL | wxEXPAND, 5);
main_sizer->AddSpacer(20);
mUpdateButton = new wxButton(this, ID_BUTTON_UPDATE, wxT("Update"), wxDefaultPosition, wxSize(120, 40));
mUpdateButton->Enable(false);
main_sizer->Add(mUpdateButton, 0, wxALL | wxEXPAND, 5);
#ifdef DEBUG
main_sizer->AddSpacer(20);
mUpdateFromFileButton = new wxButton(this, ID_BUTTON_UPDATE_FROM_FILE, wxT("Update From File"), wxDefaultPosition, wxSize(120, 40));
main_sizer->Add(mUpdateFromFileButton, 0, wxALL | wxEXPAND, 5);
#endif
main_sizer->SetSizeHints(this);
Centre();
mFirmwareVersionBcd = 0;
std::thread download_thread([this](void)
{
//wxString str = Powermon::getUpdateFirmwareImageUrl(mPowermon.getLastDeviceInfo().hardware_revision_bcd, mPowermon.getLastDeviceInfo().firmware_version_bcd);
wxString str = wxT("https://files.thornwave.com/fwup/pmonw/pmonw_fw_dfu_dev.bin");
wxURI uri(str);
wxHTTP http;
http.SetHeader("Content-type", "text/html; charset=utf-8");
http.Connect(uri.GetServer());
wxInputStream* httpStream = http.GetInputStream(uri.GetPath());
if (http.GetError() == wxPROTO_NOERR)
{
wxMemoryOutputStream out;
httpStream->Read(out);
wxStreamBuffer* httpStreamBuffer = out.GetOutputStreamBuffer();
httpStreamBuffer->Seek(0, wxFromStart);
const size_t size = httpStreamBuffer->GetBufferSize();
mFirmwareImage.resize(size);
if (httpStreamBuffer->Read(mFirmwareImage.data(), size) == size)
mFirmwareVersionBcd = Powermon::checkFirmwareImage(mFirmwareImage.data(), mFirmwareImage.size(), mPowermon.getLastDeviceInfo().hardware_revision_bcd);
}
delete httpStream;
http.Close();
wxQueueEvent(this, new GuiEvent([this](const GuiEvent &event)
{
DownloadDone();
}));
});
download_thread.detach();
}
GuiAbout::~GuiAbout()
{
}
void GuiAbout::DownloadDone(void)
{
if (mFirmwareVersionBcd > 0
#ifndef DEBUG
&& mFirmwareVersionBcd > mPowermon.getLastDeviceInfo().firmware_version_bcd
#endif
)
{
wxString str;
str.Printf(wxT("Firmware update to version %X.%02X is available"), mFirmwareVersionBcd >> 8, mFirmwareVersionBcd & 0xFF);
mStatusStatic->SetLabel(str);
mUpdateButton->Enable(true);
}
else
{
mStatusStatic->SetLabel(wxT("No firmware update is available at the moment"));
}
mStatusStatic->Refresh();
}
void GuiAbout::UpdateFirmware(void)
{
GuiDFUProgress progress(this, wxT("Updating firmware ..."), mPowermon, mFirmwareImage);
const auto status = progress.ShowModal();
if (status == GuiDFUProgress::SUCCESS)
{
EndModal(wxID_OK);
}
else if (status == GuiDFUProgress::CANCELLED)
{
EndModal(wxID_CANCEL);
}
else if (status == GuiDFUProgress::FAILED)
{
wxMessageDialog msg(this, wxT("Firmware update error."), wxT("Error"), wxOK | wxSTAY_ON_TOP | wxCENTRE | wxICON_ERROR);
msg.ShowModal();
EndModal(wxID_CANCEL);
}
}
void GuiAbout::OnButtonUpdateClicked(wxCommandEvent &event)
{
wxMessageDialog msg(this, wxT("You are about to update the firmware in your PowerMon device. \n"
"Do not disconnect the power, sit back and relax."),
wxT("Confirmation"), wxOK | wxCANCEL | wxICON_QUESTION | wxSTAY_ON_TOP | wxCENTRE);
if (msg.ShowModal() == wxID_OK)
UpdateFirmware();
}
#ifdef DEBUG
void GuiAbout::OnButtonUpdateFromFileClicked(wxCommandEvent &event)
{
wxFileDialog dialog(this, wxT("Open DFU file"), "", "", "DFU files (*.bin|*.bin", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() == wxID_CANCEL)
return;
FILE* f = fopen(dialog.GetPath().mb_str(), "rb");
if (f == nullptr)
{
wxMessageDialog msg(this, wxT("Cannot open DFU file."), wxT("Error"), wxOK | wxSTAY_ON_TOP | wxCENTRE | wxICON_ERROR);
msg.ShowModal();
return;
}
if (fseek(f, 0, SEEK_END) != 0)
{
fclose(f);
wxMessageDialog msg(this, wxT("Cannot open DFU file."), wxT("Error"), wxOK | wxSTAY_ON_TOP | wxCENTRE | wxICON_ERROR);
msg.ShowModal();
return;
}
ssize_t fsize = ftell(f);
if (fsize < 0)
{
fclose(f);
wxMessageDialog msg(this, wxT("Cannot open DFU file."), wxT("Error"), wxOK | wxSTAY_ON_TOP | wxCENTRE | wxICON_ERROR);
msg.ShowModal();
return;
}
if (fsize == 0)
{
wxMessageDialog msg(this, wxT("DFU file is empty."), wxT("Error"), wxOK | wxSTAY_ON_TOP | wxCENTRE | wxICON_ERROR);
msg.ShowModal();
fclose(f);
return;
}
if (fseek(f, 0, SEEK_SET) != 0)
{
fclose(f);
wxMessageDialog msg(this, wxT("Cannot open DFU file."), wxT("Error"), wxOK | wxSTAY_ON_TOP | wxCENTRE | wxICON_ERROR);
msg.ShowModal();
return;
}
mFirmwareImage.resize(fsize);
if (fread(mFirmwareImage.data(), fsize, 1, f) != 1)
{
fclose(f);
wxMessageDialog msg(this, wxT("Cannot read DFU file."), wxT("Error"), wxOK | wxSTAY_ON_TOP | wxCENTRE | wxICON_ERROR);
msg.ShowModal();
return;
}
fclose(f);
mFirmwareVersionBcd = Powermon::checkFirmwareImage(mFirmwareImage.data(), mFirmwareImage.size(), mPowermon.getLastDeviceInfo().hardware_revision_bcd);
if (mFirmwareVersionBcd == 0)
{
wxMessageDialog msg(this, wxT("Invalid DFU file."), wxT("Error"), wxOK | wxSTAY_ON_TOP | wxCENTRE | wxICON_ERROR);
msg.ShowModal();
return;
}
wxMessageDialog msg(this, wxT("You are about to update the firmware in your PowerMon device. \n"
"Do not disconnect the power, sit back and relax."),
wxT("Confirmation"), wxOK | wxCANCEL | wxICON_QUESTION | wxSTAY_ON_TOP | wxCENTRE);
if (msg.ShowModal() == wxID_OK)
UpdateFirmware();
}
#endif
void GuiAbout::OnGuiEvent(GuiEvent &event)
{
event.process();
}
BEGIN_EVENT_TABLE(GuiAbout, wxDialog)
GUI_EVT(GuiAbout::OnGuiEvent)
EVT_BUTTON(ID_BUTTON_UPDATE, GuiAbout::OnButtonUpdateClicked)
#ifdef DEBUG
EVT_BUTTON(ID_BUTTON_UPDATE_FROM_FILE, GuiAbout::OnButtonUpdateFromFileClicked)
#endif
END_EVENT_TABLE()