Skip to content

Commit

Permalink
Merge pull request #13 from UOSupermileage/Integrate-Bar-Chart-Lap-Ef…
Browse files Browse the repository at this point in the history
…ficiency

Integrate bar chart lap efficiency
  • Loading branch information
jeremycote authored Aug 25, 2023
2 parents 5a3d6e5 + 1da0cfc commit a35a53b
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 99 deletions.
20 changes: 11 additions & 9 deletions Core/UI/Data/DataAggregator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
#include <variant>
#include "ApplicationTypes.h"
#include "DataAggregatorWrapperType.h"
#include "BarDataCollection.h"
#include "DataQueue.hpp"

#include "ObservedObject.hpp"
#include "ObservedDataQueue.hpp"

using namespace std;

Expand All @@ -23,18 +24,19 @@ using namespace std;
*/
class DataAggregator {
public:
explicit DataAggregator(uint8_t lapEfficiencySize): lapEfficiencies({BarDataCollection<watt_hour_t>(lapEfficiencySize)}) {

}

explicit DataAggregator(uint8_t motorVelocitiesSize, uint8_t batteryVoltagesSize, uint8_t lapEfficienciesSize, uint8_t lapTimesSize):
motorVelocities(motorVelocitiesSize),
batteryVoltages(batteryVoltagesSize),
lapEfficiencies(lapEfficienciesSize),
lapTimes(lapTimesSize) {}
/** The observed object that holds the motor RPM data. */
ObservedObject<velocity_t> motorRPM{0};
ObservedDataQueue<velocity_t> motorVelocities;
/** The observed object that holds the battery voltage data. */
ObservedObject<voltage_t> batteryVoltage{0};
ObservedDataQueue<voltage_t> batteryVoltages;
/** The observed object that holds the current lap time. */
ObservedObject<ms_t> lapTime{0};
ObservedDataQueue<ms_t> lapTimes;
/** The observed object that holds a collection of lap efficiencies. */
ObservedObject<BarDataCollection<watt_hour_t>> lapEfficiencies;
ObservedDataQueue<watt_hour_t> lapEfficiencies;
};

/** Returns a reference to the data aggregator object from a given wrapper.
Expand Down
13 changes: 7 additions & 6 deletions Core/UI/Data/DataAggregatorWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,25 @@
struct DataAggregatorWrapper {
DataAggregator aggregator;

explicit DataAggregatorWrapper(uint8_t lapEfficiencySize): aggregator(lapEfficiencySize) {}
explicit DataAggregatorWrapper(uint8_t motorVelocitiesSize, uint8_t batteryVoltagesSize, uint8_t lapEfficienciesSize, uint8_t lapTimesSize):
aggregator(motorVelocitiesSize, batteryVoltagesSize, lapEfficienciesSize, lapTimesSize) {}
};

DataAggregatorWrapper* DataAggregator_Create(uint8_t lapEfficiencySize) {
auto* wrapper = new DataAggregatorWrapper(lapEfficiencySize);
DataAggregatorWrapper* DataAggregator_Create(uint8_t motorVelocitiesSize, uint8_t batteryVoltagesSize, uint8_t lapEfficienciesSize, uint8_t lapTimesSize) {
auto* wrapper = new DataAggregatorWrapper(motorVelocitiesSize, batteryVoltagesSize, lapEfficienciesSize, lapTimesSize);
return wrapper;
}

void SetMotorRPM(DataAggregatorWrapper* wrapper, velocity_t rpm) {
wrapper->aggregator.motorRPM.set(rpm);
wrapper->aggregator.motorVelocities.add(rpm);
}

void SetBatteryVoltage(DataAggregatorWrapper* wrapper, voltage_t voltage) {
wrapper->aggregator.batteryVoltage.set(voltage);
wrapper->aggregator.batteryVoltages.add(voltage);
}

void SetLapTime(DataAggregatorWrapper* wrapper, ms_t time) {
wrapper->aggregator.lapTime.set(time);
wrapper->aggregator.lapTimes.add(time);
}

DataAggregator& DataAggregator_GetReference(DataAggregatorWrapper* wrapper) {
Expand Down
2 changes: 1 addition & 1 deletion Core/UI/Data/DataAggregatorWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ extern "C" {
* Creates a data aggregator wrapper object and returns a pointer to it.
* @return A pointer to the data aggregator wrapper object.
*/
DataAggregatorWrapper* DataAggregator_Create(uint8_t lapEfficiencySize);
DataAggregatorWrapper* DataAggregator_Create(uint8_t motorVelocitiesSize, uint8_t batteryVoltagesSize, uint8_t lapEfficienciesSize, uint8_t lapTimesSize);

/** @ingroup core-modules
* Sets the motor RPM data in the data aggregator object from a given wrapper.
Expand Down
73 changes: 16 additions & 57 deletions Core/UI/HomeView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,82 +40,41 @@ HomeView::HomeView(lv_obj_t* parent, HomeViewModel& viewModel) : View(parent, vi
lv_chart_set_type(lapTimeBarGraph, LV_CHART_TYPE_BAR);
lv_obj_set_size(lapTimeBarGraph, 200, 150);
lv_obj_center(lapTimeBarGraph);
lv_chart_set_range(lapTimeBarGraph, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
lv_chart_set_range(lapTimeBarGraph, LV_CHART_AXIS_SECONDARY_Y, 0, 400);
lv_chart_set_point_count(lapTimeBarGraph, 12);
lv_chart_set_range(lapTimeBarGraph, LV_CHART_AXIS_PRIMARY_Y, 0, 1010);
lv_chart_set_range(lapTimeBarGraph, LV_CHART_AXIS_SECONDARY_Y, 0, 1010);
// lv_chart_set_point_count(lapTimeBarGraph, 12);

/*Add ticks and label to every axis*/
lv_chart_set_axis_tick(lapTimeBarGraph, LV_CHART_AXIS_PRIMARY_X, 10, 5, 12, 3, true, 40);
lv_chart_set_axis_tick(lapTimeBarGraph, LV_CHART_AXIS_PRIMARY_Y, 10, 5, 6, 2, true, 50);
lv_chart_set_axis_tick(lapTimeBarGraph, LV_CHART_AXIS_SECONDARY_Y, 10, 5, 3, 4, true, 50);

/*Zoom in a little in X*/
lv_chart_set_zoom_x(lapTimeBarGraph, 800);
// lv_chart_set_zoom_x(lapTimeBarGraph, 800);

/*Add two data series*/
lv_chart_series_t * ser1 = lv_chart_add_series(lapTimeBarGraph, lv_palette_lighten(LV_PALETTE_GREEN, 2), LV_CHART_AXIS_PRIMARY_Y);

lv_coord_t * ser1_array = lv_chart_get_y_array(lapTimeBarGraph, ser1);
/*Directly set points on 'ser2'*/

lv_chart_set_next_value(lapTimeBarGraph , ser1, 31);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 66);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 89);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 63);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 56);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 32);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 35);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 57);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 85);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 22);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 58);

/* ser1_array[0] = 92;
ser1_array[1] = 71;
ser1_array[2] = 61;
ser1_array[3] = 15;
ser1_array[4] = 21;
ser1_array[5] = 35;
ser1_array[6] = 35;
ser1_array[7] = 58;
ser1_array[8] = 31;
ser1_array[9] = 53;
ser1_array[10] = 33;
ser1_array[11] = 73; */

/* ser1_array = lapEfficiencies.GetValues(); */

lv_chart_set_point_count(lapTimeBarGraph, viewModel.GetAggregator().lapTimes.getNumberOfElements());
lv_chart_set_ext_y_array(lapTimeBarGraph, ser1,
reinterpret_cast<lv_coord_t *>(viewModel.GetAggregator().lapTimes.getValues()));
lv_chart_refresh(lapTimeBarGraph); /*Required after direct set*/


viewModel.GetAggregator().batteryVoltage.addListener([this](const voltage_t& value) {
uint32_t n = value * 33 * 185 / 40960;
viewModel.GetAggregator().batteryVoltages.addListenerForLatest([this](const voltage_t& voltage) {
uint32_t n = voltage * 33 * 185 / 40960;
lv_label_set_text_fmt(batteryVoltageLabel, "%d.%d Volts", n / 10, n % 10);
});

viewModel.GetAggregator().motorRPM.addListener([this](const velocity_t& value) {
lv_label_set_text_fmt(motorRPMLabel, "%d RPM", value);
viewModel.GetAggregator().motorVelocities.addListenerForLatest([this](const velocity_t& velocity) {
lv_label_set_text_fmt(motorRPMLabel, "%d RPM", velocity);
});

viewModel.GetAggregator().lapTime.addListener([this](const ms_t& value) {
lv_label_set_text_fmt(lapTimeLabel, "%dm %ds", value / 60000, value / 1000);
viewModel.GetAggregator().lapTimes.addListenerForLatest([this](const ms_t& time) {
lv_label_set_text_fmt(lapTimeLabel, "%dm %ds", time / 60000, time / 1000);
});

viewModel.GetAggregator().lapEfficiencies.addListener([this, ser1](const BarDataCollection<watt_hour_t> &value) {
lv_chart_set_next_value(lapTimeBarGraph , ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
lv_chart_set_next_value(lapTimeBarGraph, ser1, 10);
viewModel.GetAggregator().lapTimes.addListener([this](const DataQueue<ms_t>& queue) {
lv_chart_set_point_count(lapTimeBarGraph, queue.getNumberOfElements());
lv_chart_refresh(lapTimeBarGraph);
});

});
}
4 changes: 2 additions & 2 deletions Core/UI/StatsView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ StatsView::StatsView(lv_obj_t* parent, StatsViewModel& viewModel) : View(parent,

lv_label_set_text_fmt(rpmLabel, "%d RPM", rpm);
};
combiner = new CombineLatest(callback, viewModel.GetAggregator().motorRPM,
viewModel.GetAggregator().batteryVoltage);
// combiner = new CombineLatest(callback, viewModel.GetAggregator().motorVelocities,
// viewModel.GetAggregator().batteryVoltages);
}

StatsView::~StatsView() {
Expand Down
66 changes: 51 additions & 15 deletions Core/UI/Utils/BarDataCollection.h → Core/UI/Utils/DataQueue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,31 @@
// Created by Jeremy Cote on 2023-08-18.
//

#ifndef UOSM_DASHBOARD_BARDATACOLLECTION_H
#define UOSM_DASHBOARD_BARDATACOLLECTION_H
#ifndef UOSM_DASHBOARD_DATAQUEUE_HPP
#define UOSM_DASHBOARD_DATAQUEUE_HPP

#include <stdexcept>

#include "lvgl/lvgl.h"

/** @ingroup core-ui-utils
* A class that aggregates the data to display on a bar chart.
* It can store any type T, but the values will be cast to lv_coord_t when displayed in a bar chart.
* Follows FIFO model.
*/
template<typename T>
class BarDataCollection {
class DataQueue {
private:
T** values;
T* values;
uint8_t largest;
uint8_t head;
uint8_t size;
public:
explicit BarDataCollection(uint8_t size): size(size), head(0) {
explicit DataQueue(uint8_t size): size(size), head(0) {
if (size <= 0) {
throw std::invalid_argument("Size must be at least 1");
}

values = new T*[size];
values = new T[size];
largest = 0;
}

/**
Expand All @@ -43,7 +44,26 @@ class BarDataCollection {
* Use to set the data source of a bar chart.
* @return a pointer to the underlying data source
*/
[[nodiscard]] T** getValues() const { return values; }
[[nodiscard]] T* getValues() const { return values; }

/**
* @return a pointer to the newest value added to the collection.
*/
[[nodiscard]] T getLatestValue() const noexcept(false) {
if (head == 0) {
throw std::out_of_range("DataQueue is empty.");
}

return values[head - 1];
}

[[nodiscard]] T getLargestValue() const noexcept(false) {
if (head == 0) {
throw std::out_of_range("DataQueue is empty.");
}

return values[largest];
}

/**
* Copy a value into the bar data collection.
Expand All @@ -52,14 +72,18 @@ class BarDataCollection {
*/
void add(T value) {
if (head == size) {
delete values[0];
for (uint8_t i = 1; i < size; i++) {
values[i - 1] = values[i];
}
head--;
}

values[head] = new T(value);
values[head] = value;

if (value > values[largest]) {
largest = head;
}

head++;
}

Expand All @@ -77,12 +101,24 @@ class BarDataCollection {

uint8_t i = head - 1;

if (values[i] != nullptr) {
delete values[i];
bool shouldScanForLargest = value < values[i];

values[i] = value;

for (int n = 0; n < head; n++) {
printf("%d: %d, ", n, values[n]);
}

values[i] = new T(value);
if (shouldScanForLargest) {
for (int n = 0; n < head; n++) {
if (values[n] > values[largest]) {
largest = n;
}
}
} else if (value > values[largest]) {
largest = i;
}
}
};

#endif //UOSM_DASHBOARD_BARDATACOLLECTION_H
#endif //UOSM_DASHBOARD_DATAQUEUE_HPP
71 changes: 71 additions & 0 deletions Core/UI/Utils/ObservedDataQueue.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// Created by Jeremy Cote on 2023-08-21.
//

#ifndef UOSM_DASHBOARD_SDL_OBSERVEDDATAQUEUE_HPP
#define UOSM_DASHBOARD_SDL_OBSERVEDDATAQUEUE_HPP

#include "DataQueue.hpp"
#include "ObservedObject.hpp"

/**
*
*/
template<typename T>
class ObservedDataQueue: public ObservedObject<DataQueue<T>> {
private:
DataQueue<T> queue;
public:
explicit ObservedDataQueue(uint8_t size): queue(size), ObservedObject<DataQueue<T>>(&queue, false) {}

/**
* Copy a value into the bar data collection. IMPORTANT: This creates a copy of the passed value.
* @param value to copy into the collection
*/
void add(T value) {
queue.add(value);
this->publish();
}

/**
* Update the last element of the collection. Useful if the latest value is still changing.
* If the collection is empty, this acts as a call to add
* @param value
*/
void update(T value) {
queue.update(value);
this->publish();
}

[[nodiscard]] uint8_t getSize() const { return queue.getSize(); }

/**
* @return the number of values stored in the collection.
*/
[[nodiscard]] uint8_t getNumberOfElements() const { return queue.getNumberOfElements(); }

/**
* Return the underlying data of the collection.
* Use to set the data source of a bar chart.
* @return a pointer to the underlying data source
*/
[[nodiscard]] T* getValues() const { return queue.getValues(); }

/**
* @return a pointer to the newest value added to the collection.
*/
[[nodiscard]] T getLatestValue() const noexcept(false) { return queue.getLatestValue(); }

/**
* Add a listener that only receives the latest value in the queue
* @param callback
* @return
*/
ObserverToken addListenerForLatest(std::function<void(const T&)> callback) {
return this->addListener([this, callback](const DataQueue<T>& q) {
callback(queue.getLatestValue());
});
}
};

#endif //UOSM_DASHBOARD_SDL_OBSERVEDDATAQUEUE_HPP
Loading

0 comments on commit a35a53b

Please sign in to comment.