Newer
Older
powermon_manager_sw / gui / gui_about.cpp
/* 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::hasWifi(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()