/* 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 <string.h>
#include <gui_app.h>
#include <gui_chart.h>
#include <model.h>
#include <math.h>
#include <resources/img_thornwave_icon_png.cpp>
GuiChart::GuiChart(wxWindow *parent, const wxString &title): wxFrame(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxRESIZE_BORDER | wxSYSTEM_MENU | wxCAPTION | wxCLOSE_BOX | wxCLIP_CHILDREN)
{
wxBitmap icon_bmp = wxBITMAP_PNG_FROM_DATA(img_thornwave_icon);
wxIcon icon;
icon.CopyFromBitmap(icon_bmp);
SetIcon(icon);
//main window sizer & panel
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
SetSizer(main_sizer);
mScopeDisplay = new ScopeDisplay(this, wxID_ANY, wxDefaultPosition, FromDIP(wxSize(800, 600)));
mScopeDisplay->setListener(this);
std::vector<uint64_t> scales;
//scales.push_back((uint64_t)1000 * 1000 * 30); //30 seconds / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 1); //1 minute / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 2); //2 minutes / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 5); //5 minutes / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 10); //10 minutes / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 20); //20 minutes / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 30); //30 minutes / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 60 * 1); //1 hour / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 60 * 2); //2 hours / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 60 * 6); //6 hours / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 60 * 12); //12 hours / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 60 * 24 * 1); //1 day / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 60 * 24 * 2); //2 day / div
scales.push_back((uint64_t)1000 * 1000 * 60 * 60 * 24 * 7); //7 day / div
mScopeDisplay->SetTimeScales(scales, 6);
main_sizer->Add(mScopeDisplay, 1, wxALL | wxEXPAND, FromDIP(6));
main_sizer->SetSizeHints(this);
Centre();
Bind(wxEVT_CLOSE_WINDOW, &GuiChart::OnClose, this);
mScopeDisplay->addWaveform(wxT("Voltage 1"), wxT("V"), wxT("V"), 5);
mScopeDisplay->addWaveform(wxT("Voltage 2"), wxT("V"), wxT("V"), 5);
mScopeDisplay->addWaveform(wxT("Current"), wxT("I"), wxT("A"), 5);
mScopeDisplay->addWaveform(wxT("Power"), wxT("P"), wxT("W"), 5);
mScopeDisplay->addWaveform(wxT("Temperature"), wxT("T"), wxT("\u00B0C"), 5);
mScopeDisplay->addWaveform(wxT("SoC"), wxT("%"), wxT("%"), 10);
}
GuiChart::~GuiChart()
{
}
void GuiChart::update(void)
{
const auto &log_data = Model::getInstance().logData;
mScopeDisplay->setMinTime((uint64_t)log_data.front().time * 1000000);
mScopeDisplay->setMaxTime((uint64_t)log_data.back().time * 1000000);
mScopeDisplay->EndTimePos();
}
void GuiChart::OnClose(wxCloseEvent &event)
{
Hide();
}
void GuiChart::getChartData(uint64_t start_time, uint64_t last_time, size_t point_count, std::vector<DataVector> &data)
{
const auto &log_data = Model::getInstance().logData;
for(auto &d: data)
d = std::vector<float>(point_count, NAN);
start_time = start_time / 1000000;
last_time = (last_time + 999999) / 1000000;
const uint32_t step = (last_time - start_time) / point_count;
uint64_t time = start_time + step;
uint32_t count = 0;
std::vector<float> avg(data.size(), 0);
std::vector<PowermonLogFile::Sample>::const_iterator it = std::find_if(log_data.begin(), log_data.end(), [start_time](const PowermonLogFile::Sample &sample)
{
return sample.time >= start_time;
});
uint32_t pos;
for(pos = 0; pos < point_count && (it != log_data.end()); it++)
{
if (it->time < time)
{
avg[0] += it->voltage1;
avg[1] += it->voltage2;
avg[2] += it->current;
avg[3] += it->power;
avg[4] += it->temperature;
avg[5] += (it->soc <= 100) ? it->soc : NAN;
count++;
}
else
{
if (count)
{
uint32_t i = 0;
for(auto &a: avg)
{
a /= count;
data[i++][pos] = a;
}
pos++;
}
avg[0] = it->voltage1;
avg[1] = it->voltage2;
avg[2] = it->current;
avg[3] = it->power;
avg[4] = it->temperature;
avg[5] = (it->soc <= 100) ? it->soc : NAN;
count = 1;
time += step;
}
}
}
BEGIN_EVENT_TABLE(GuiChart, wxFrame)
END_EVENT_TABLE()