/* 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()