Compare commits

..

No commits in common. "659542f094ef1c36f091a0229882846f585632c8" and "b3f13767dcdce5bd6477c2cc91b927e8d26f63bc" have entirely different histories.

4 changed files with 41 additions and 467 deletions

View File

@ -1,95 +0,0 @@
#pragma once
template <typename DataType>
class Dequeue {
struct Node {
DataType data;
Node* next;
Node* previous;
Node(DataType data, Node* next, Node* previous):
data(data),
next(next),
previous(previous)
{}
};
int count = 0;
Node* head = nullptr;
Node* tail = nullptr;
public:
void pushFront(DataType data) {
auto* new_node = new Node(data, head, nullptr);
if (head != nullptr) {
head->previous = new_node;
}
if (tail == nullptr) {
tail = new_node;
}
head = new_node;
count++;
}
void pushBack(DataType data) {
auto* new_node = new Node(data, nullptr, tail);
if (head == nullptr) {
head = new_node;
}
if (tail != nullptr) {
tail->next = new_node;
}
tail = new_node;
count++;
}
void popFront() {
if (head != nullptr) {
bool is_last_node = (head == tail);
Node* node_to_delete = head;
head = node_to_delete->next;
if (is_last_node) {
tail = nullptr;
}
delete node_to_delete;
count--;
}
}
void popBack() {
if (tail != nullptr) {
bool is_last_node = (head == tail);
Node* node_to_delete = tail;
tail = node_to_delete->previous;
if (is_last_node) {
head = nullptr;
}
delete node_to_delete;
count--;
}
}
DataType back() const {
assert(tail != nullptr);
return tail->data;
}
DataType front() const {
assert(head != nullptr);
return head->data;
}
bool empty() const {
return head == nullptr;
}
int size() const { return count; }
};

View File

@ -1,156 +0,0 @@
#pragma once
template <typename DataType>
class LinkedList {
struct Node {
DataType data;
Node* next;
Node* previous;
Node(DataType data, Node* next, Node* previous):
data(data),
next(next),
previous(previous)
{}
};
int count = 0;
Node* head = nullptr;
Node* tail = nullptr;
public:
class Iterator {
Node *node = nullptr;
public:
Iterator(Node* node)
: node(node) {}
bool advance(size_t n) {
size_t i = 0;
if (n == 0) {
return true;
}
for (; node && (i < n); ++i) {
node = node->next;
}
return (i > 0);
}
DataType& operator* ()
{
return node->data;
}
DataType* operator-> ()
{
return &(node->data);
}
Iterator operator++ (int) {
assert(advance(1));
Iterator i(node);
return i;
}
bool operator==(const Iterator& right) const {
return node == right.node;
}
bool operator!=(const Iterator& right) const {
return node != right.node;
}
};
void pushFront(DataType data) {
auto* new_node = new Node(data, head, nullptr);
if (head != nullptr) {
head->previous = new_node;
}
if (tail == nullptr) {
tail = new_node;
}
head = new_node;
count++;
}
void pushBack(DataType data) {
auto* new_node = new Node(data, nullptr, tail);
if (head == nullptr) {
head = new_node;
}
if (tail != nullptr) {
tail->next = new_node;
}
tail = new_node;
count++;
}
void popFront() {
if (head != nullptr) {
bool is_last_node = (head == tail);
Node* node_to_delete = head;
head = node_to_delete->next;
if (is_last_node) {
tail = nullptr;
}
delete node_to_delete;
count--;
}
}
void popBack() {
if (tail != nullptr) {
bool is_last_node = (head == tail);
Node* node_to_delete = tail;
tail = node_to_delete->previous;
if (is_last_node) {
head = nullptr;
}
delete node_to_delete;
count--;
}
}
DataType back() const {
assert(tail != nullptr);
return tail->data;
}
DataType front() const {
assert(head != nullptr);
return head->data;
}
Iterator begin() const {
return Iterator(head);
}
Iterator end() const {
return Iterator(nullptr);
}
bool empty() const {
return head == nullptr;
}
int size() const { return count; }
DataType operator [] (int i) const {
auto iter = begin();
assert(iter.advance(i));
return *iter;
}
DataType& operator [] (int i) {
auto iter = begin();
assert(iter.advance(i));
return *iter;
}
};

View File

@ -1,37 +0,0 @@
#pragma once
#include <tt_hal_radio.h>
#include "Str.h"
#include "LinkedList.h"
class Preset {
public:
struct PresetItem {
RadioParameter parameter;
float value;
};
Str name;
Modulation modulation;
LinkedList<PresetItem> items;
Preset(const char* const name, Modulation modulation)
: name(name)
, modulation(modulation)
{}
virtual ~Preset() = default;
void addParameter(RadioParameter parameter, float value) {
items.pushBack({parameter, value});
}
LinkedList<PresetItem>::Iterator begin() {
return items.begin();
}
LinkedList<PresetItem>::Iterator end() {
return items.end();
}
};

View File

@ -1,36 +1,21 @@
#include "RadioSet.h"
#include "Str.h"
#include "LinkedList.h"
#include "Preset.h"
#include <cstdio>
#include <ctype.h>
#include <tt_lvgl_toolbar.h>
#include <tt_message_queue.h>
#include <tt_app_alertdialog.h>
#include <initializer_list>
#include "tt_app_alertdialog.h"
constexpr const char* TAG = "RadioSet";
template <typename Iterator>
Iterator next(Iterator i) {
return i++;
}
template <typename Iterator, typename Container>
bool is_last(Iterator i, const Container& c) {
return (i != c.end()) && (next(i) == c.end());
}
void crash(const char* const message) {
tt_app_alertdialog_start("RadioSet has crashed!", message, nullptr, 0);
}
void crashassert(bool assertion, const char* const message) {
if (!assertion) {
crash(message);
}
}
// Debug function which colors all children randomly
// TODO: Remove before flight
void clownvomit(lv_obj_t *obj) {
@ -132,12 +117,8 @@ static lv_obj_t* createGridDropdownInput(lv_obj_t *container, int row, const cha
struct ParameterInput {
static constexpr auto LV_STATE_INVALID = LV_STATE_USER_1;
typedef void (*Callback)(void* ctx);
const RadioHandle handle;
const RadioParameter param;
Callback userChangeCallback = nullptr;
void* userChangeCtx = nullptr;
static void apply_error_style(lv_obj_t* obj) {
static bool init = false;
@ -156,23 +137,9 @@ struct ParameterInput {
, param(param) {}
virtual ~ParameterInput() = default;
void onUserChange(Callback cb, void* ctx) {
userChangeCallback = cb;
userChangeCtx = ctx;
}
void emitUserChange() {
if (userChangeCallback) {
userChangeCallback(userChangeCtx);
}
}
virtual void storeToRadio() = 0;
virtual void updatePreview() = 0;
virtual void activate() = 0;
virtual void deactivate() = 0;
virtual void setValue(float value) = 0;
};
struct NumericParameterInput : public ParameterInput {
@ -232,18 +199,19 @@ struct NumericParameterInput : public ParameterInput {
lv_obj_add_event_cb(input, [](lv_event_t * e) {
NumericParameterInput* self = (NumericParameterInput*)lv_event_get_user_data(e);
self->storeToRadio();
self->emitUserChange();
}, LV_EVENT_VALUE_CHANGED, this);
}
void loadFromRadio() {
float value;
if (tt_hal_radio_get_parameter(handle, param, &value) == RADIO_PARAM_SUCCESS) {
setValue(value);
Str txt;
txt.appendf(fmt, value);
lv_textarea_set_text(input, txt.c_str());
}
}
virtual void storeToRadio() override {
void storeToRadio() {
float value;
if (sscanf(lv_textarea_get_text(input), "%f", &value) == 1) {
if (tt_hal_radio_set_parameter(handle, param, value) != RADIO_PARAM_SUCCESS) {
@ -265,12 +233,6 @@ struct NumericParameterInput : public ParameterInput {
virtual void deactivate() override {
lv_obj_add_state(input, LV_STATE_DISABLED);
}
virtual void setValue(float value) override {
Str txt;
txt.appendf(fmt, value);
lv_textarea_set_text(input, txt.c_str());
}
};
struct SliderParameterInput : public ParameterInput {
@ -306,6 +268,7 @@ struct SliderParameterInput : public ParameterInput {
lv_obj_set_size(slider, lv_pct(100), 10);
lv_slider_set_range(slider, min, max);
apply_error_style(slider);
loadFromRadio();
preview = lv_label_create(container);
lv_obj_set_grid_cell(preview,
@ -313,15 +276,12 @@ struct SliderParameterInput : public ParameterInput {
LV_GRID_ALIGN_CENTER, row, 1);
lv_obj_set_size(preview, lv_pct(100), LV_SIZE_CONTENT);
lv_obj_set_style_text_align(preview , LV_TEXT_ALIGN_LEFT, 0);
loadFromRadio();
updatePreview();
lv_obj_add_event_cb(slider, [](lv_event_t * e) {
lv_obj_t* slider = lv_event_get_target_obj(e);
SliderParameterInput* self = (SliderParameterInput*)lv_event_get_user_data(e);
self->updatePreview();
self->storeToRadio();
self->emitUserChange();
}, LV_EVENT_VALUE_CHANGED, this);
}
@ -329,11 +289,11 @@ struct SliderParameterInput : public ParameterInput {
void loadFromRadio() {
float value;
if (tt_hal_radio_get_parameter(handle, param, &value) == RADIO_PARAM_SUCCESS) {
setValue(value);
lv_slider_set_value(slider, value, LV_ANIM_ON);
}
}
virtual void storeToRadio() override {
void storeToRadio() {
if (tt_hal_radio_set_parameter(handle, param, lv_slider_get_value(slider)) != RADIO_PARAM_SUCCESS) {
lv_obj_add_state(slider, LV_STATE_INVALID);
} else {
@ -354,11 +314,6 @@ struct SliderParameterInput : public ParameterInput {
virtual void deactivate() override {
lv_obj_add_state(slider, LV_STATE_DISABLED);
}
virtual void setValue(float value) override {
lv_slider_set_value(slider, value, LV_ANIM_ON);
updatePreview();
}
};
@ -424,6 +379,7 @@ struct SliderSelectParameterInput : public ParameterInput {
lv_obj_set_size(slider, lv_pct(100), 10);
lv_slider_set_range(slider, 0, selectionsSize - 1);
apply_error_style(slider);
loadFromRadio();
preview = lv_label_create(container);
lv_obj_set_grid_cell(preview,
@ -433,15 +389,13 @@ struct SliderSelectParameterInput : public ParameterInput {
lv_obj_set_style_text_align(preview , LV_TEXT_ALIGN_LEFT, 0);
tt_hal_radio_get_parameter_unit_str(handle, param, unit, sizeof(unit));
loadFromRadio();
updatePreview();
lv_obj_add_event_cb(slider, [](lv_event_t * e) {
lv_obj_t* slider = lv_event_get_target_obj(e);
SliderSelectParameterInput* self = (SliderSelectParameterInput*)lv_event_get_user_data(e);
self->updatePreview();
self->storeToRadio();
self->emitUserChange();
}, LV_EVENT_VALUE_CHANGED, this);
}
@ -449,11 +403,11 @@ struct SliderSelectParameterInput : public ParameterInput {
void loadFromRadio() {
float value;
if (tt_hal_radio_get_parameter(handle, param, &value) == RADIO_PARAM_SUCCESS) {
setValue(value);
lv_slider_set_value(slider, get_selection_index(value), LV_ANIM_ON);
}
}
virtual void storeToRadio() override {
void storeToRadio() {
if (tt_hal_radio_set_parameter(handle, param, selections[lv_slider_get_value(slider)]) != RADIO_PARAM_SUCCESS) {
lv_obj_add_state(slider, LV_STATE_INVALID);
} else {
@ -475,11 +429,6 @@ struct SliderSelectParameterInput : public ParameterInput {
virtual void deactivate() override {
lv_obj_add_state(slider, LV_STATE_DISABLED);
}
virtual void setValue(float value) override {
lv_slider_set_value(slider, get_selection_index(value), LV_ANIM_ON);
updatePreview();
}
};
struct FlagParameterInput : public ParameterInput {
@ -515,18 +464,21 @@ struct FlagParameterInput : public ParameterInput {
lv_obj_t* slider = lv_event_get_target_obj(e);
FlagParameterInput* self = (FlagParameterInput*)lv_event_get_user_data(e);
self->storeToRadio();
self->emitUserChange();
}, LV_EVENT_VALUE_CHANGED, this);
}
void loadFromRadio() {
float value;
if (tt_hal_radio_get_parameter(handle, param, &value) == RADIO_PARAM_SUCCESS) {
setValue(value);
if (value != 0.0) {
lv_obj_add_state(input, LV_STATE_CHECKED);
} else {
lv_obj_clear_state(input, LV_STATE_CHECKED);
}
}
}
virtual void storeToRadio() override {
void storeToRadio() {
float value = lv_obj_has_state(input, LV_STATE_CHECKED) ? 1.0 : 0.0;
if (tt_hal_radio_set_parameter(handle, param, value) != RADIO_PARAM_SUCCESS) {
lv_obj_add_state(input, LV_STATE_INVALID);
@ -545,14 +497,6 @@ struct FlagParameterInput : public ParameterInput {
virtual void deactivate() override {
lv_obj_add_state(input, LV_STATE_DISABLED);
}
virtual void setValue(float value) override {
if (value != 0.0) {
lv_obj_add_state(input, LV_STATE_CHECKED);
} else {
lv_obj_clear_state(input, LV_STATE_CHECKED);
}
}
};
static ParameterInput* makeLoraInput(RadioHandle handle, const RadioParameter param, lv_obj_t* container, int row) {
@ -616,8 +560,6 @@ class SettingsView {
ParameterInput* paramInputs[MAX_PARAMS] = {0};
size_t paramsAvailableCount = 0;
LinkedList<Preset*> presets;
LinkedList<Preset*> presetsByModulation[MAX_MODEMS];
lv_obj_t* mainPanel = nullptr;
lv_obj_t* deviceForm = nullptr;
@ -625,16 +567,10 @@ class SettingsView {
lv_obj_t* radioSwitch = nullptr;
lv_obj_t* radioStateLabel = nullptr;
lv_obj_t* modemDropdown = nullptr;
lv_obj_t* modemPresetDropdown = nullptr;
lv_obj_t *propertiesForm = nullptr;
public:
void addPreset(Preset* preset) {
presets.pushBack(preset);
presetsByModulation[preset->modulation].pushBack(preset);
}
void queryRadios() {
DeviceId devices[MAX_RADIOS];
uint16_t device_count = 0;
@ -709,9 +645,8 @@ public:
char unit_buffer[32] = {0};
// Clean up any input
// Clean up any input, it's safe and this loop costs nothing'
for (size_t i = 0; i < MAX_PARAMS; ++i) {
// As this is a LUT, only some may be set
if (paramInputs[i]) {
delete paramInputs[i];
paramInputs[i] = nullptr;
@ -726,11 +661,7 @@ public:
auto status = tt_hal_radio_get_parameter(radioSelected, param, &value);
if (status == RADIO_PARAM_SUCCESS) {
auto input = makeParameterInput(radioSelected, param, modem, container, paramsAvailableCount);
input->onUserChange([](void* ctx) {
SettingsView* self = (SettingsView*)ctx;
self->onParameterInput();
}, this);
paramInputs[param] = input;
paramInputs[paramsAvailableCount] = input;
//lv_group_focus_obj(input);
paramsAvailable[paramsAvailableCount] = param;
paramsAvailableCount++;
@ -747,60 +678,7 @@ public:
lv_dropdown_set_selected(modemDropdown, modemIndex);
if (tt_hal_radio_set_modulation(radioSelected, modemsAvailable[modemIndex])) {
propertiesForm = initParameterFormGeneric(mainPanel, modemsAvailable[modemIndex]);
}
updatePresets();
}
void selectPreset(int presetIndex) {
// The first index is always "No preset" or "None available"
// Other indices are the presets for the current modulation + 1
auto modem = tt_hal_radio_get_modulation(radioSelected);
auto& presets = presetsByModulation[modem];
if ((presetIndex > 0) && ((presetIndex - 1) < presets.size())) {
auto preset = presets[presetIndex - 1];
for (auto iter = preset->items.begin(); iter != preset->items.end(); iter++) {
if (paramInputs[iter->parameter]) {
paramInputs[iter->parameter]->setValue(iter->value);
paramInputs[iter->parameter]->storeToRadio();
}
}
} else {
crash("Selected preset does not exist");
}
}
void onParameterInput() {
// As the user did an input, this makes any applied
// preset inconsistent, revert back to "None".
lv_dropdown_set_selected(modemPresetDropdown, 0);
}
void updatePresets() {
auto modemIndexConfigured = tt_hal_radio_get_modulation(radioSelected);
Str preset_list("Select...");
auto& presets = presetsByModulation[modemIndexConfigured];
if (!presets.empty()) {
preset_list.append("\n");
}
for (auto iter = presets.begin(); iter != presets.end(); iter++) {
auto place_sep = !is_last(iter, presets);
auto& name = (*iter)->name;
preset_list.append(name.c_str());
if (place_sep) {
preset_list.append("\n");
}
}
if (preset_list.empty()) {
lv_obj_add_state(modemPresetDropdown, LV_STATE_DISABLED);
lv_dropdown_set_options(modemPresetDropdown, "None");
} else {
lv_obj_clear_state(modemPresetDropdown, LV_STATE_DISABLED);
lv_dropdown_set_options(modemPresetDropdown, preset_list.c_str());
//clownvomit(propertiesForm);
}
}
@ -810,7 +688,7 @@ public:
}
radioSelected = radios[index];
crashassert(radioSelected, "Radio selected not allocated");
assert(radioSelected);
for (size_t i = 0; i < MAX_MODEMS; ++i) {
modemsAvailable[i] = MODULATION_NONE;
@ -888,7 +766,6 @@ public:
grid_row_size,
grid_row_size,
grid_row_size,
grid_row_size,
LV_GRID_TEMPLATE_LAST};
lv_obj_set_layout(container, LV_LAYOUT_GRID);
lv_obj_set_grid_dsc_array(container, lora_col_dsc, lora_row_dsc);
@ -916,8 +793,8 @@ public:
LV_GRID_ALIGN_CENTER, 1, 1);
lv_obj_set_size(radioStateLabel, lv_pct(100), LV_SIZE_CONTENT);
modemDropdown = createGridDropdownInput(container, 2, "Modulation", "None available");
modemPresetDropdown = createGridDropdownInput(container, 3, "Preset", "None");
modemDropdown = createGridDropdownInput(container, 2, "Modulation", "none available");
lv_obj_add_event_cb(modemDropdown, [](lv_event_t * e) {
SettingsView* self = (SettingsView*)lv_event_get_user_data(e);
@ -925,12 +802,6 @@ public:
self->selectModulation(lv_dropdown_get_selected(input));
}, LV_EVENT_VALUE_CHANGED, this);
lv_obj_add_event_cb(modemPresetDropdown, [](lv_event_t * e) {
SettingsView* self = (SettingsView*)lv_event_get_user_data(e);
lv_obj_t* input = lv_event_get_target_obj(e);
self->selectPreset(lv_dropdown_get_selected(input));
}, LV_EVENT_VALUE_CHANGED, this);
lv_obj_add_event_cb(radioSwitch, [](lv_event_t * e) {
lv_obj_t* input = lv_event_get_target_obj(e);
SettingsView* self = (SettingsView*)lv_event_get_user_data(e);
@ -989,22 +860,18 @@ public:
void activateConfig() {
lv_obj_clear_state(modemDropdown, LV_STATE_DISABLED);
lv_obj_clear_state(radioSwitch, LV_STATE_CHECKED);
lv_obj_clear_state(modemPresetDropdown, LV_STATE_DISABLED);
for (size_t i = 0; i < MAX_PARAMS; ++i) {
if (paramInputs[i]) {
paramInputs[i]->activate();
}
for (size_t i = 0; i < paramsAvailableCount; ++i) {
assert(paramInputs[i]);
paramInputs[i]->activate();
}
}
void deactivateConfig() {
lv_obj_add_state(radioSwitch, LV_STATE_CHECKED);
lv_obj_add_state(modemDropdown, LV_STATE_DISABLED);
lv_obj_add_state(modemPresetDropdown, LV_STATE_DISABLED);
for (size_t i = 0; i < MAX_PARAMS; ++i) {
if (paramInputs[i]) {
paramInputs[i]->deactivate();
}
for (size_t i = 0; i < paramsAvailableCount; ++i) {
assert(paramInputs[i]);
paramInputs[i]->deactivate();
}
}
@ -1019,13 +886,19 @@ public:
lv_obj_add_flag(mainPanel, (lv_obj_flag_t)(LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLL_ON_FOCUS));
auto* group = lv_group_get_default();
lv_group_add_obj(group, mainPanel);
//lv_obj_add_event_cb(btn, scrollbar_highlight, LV_EVENT_FOCUS, NULL);
//lv_obj_add_event_cb(btn, scrollbar_restore, LV_EVENT_DEFOCUSED, NULL);
// Create once (e.g. during init)
static lv_style_t style_scroll_focus;
lv_style_init(&style_scroll_focus);
lv_style_set_bg_color(&style_scroll_focus, lv_color_make(0x40,0xA0,0xFF));
lv_style_set_bg_opa(&style_scroll_focus, LV_OPA_COVER);
lv_style_set_border_width(&style_scroll_focus, 1);
lv_style_set_border_color(&style_scroll_focus, lv_theme_get_color_primary(nullptr));
lv_style_set_border_color(&style_scroll_focus, lv_color_black());
// Apply style targeted to the scrollbar part when the object is FOCUSED
// LV_PART_SCROLLBAR | LV_STATE_FOCUSED will ensure the style is used only while focused.
lv_obj_add_style(mainPanel, &style_scroll_focus, LV_PART_SCROLLBAR | LV_STATE_FOCUSED);
deviceForm = initDeviceForm(mainPanel);
@ -1068,17 +941,6 @@ void RadioSet::onShow(AppHandle appHandle, lv_obj_t* parent) {
lv_obj_remove_flag(wrapper, LV_OBJ_FLAG_SCROLLABLE);
settingsView = new SettingsView(wrapper);
auto presetMtEu868LongFast = new Preset("MT EU868 LongFast", MODULATION_LORA);
presetMtEu868LongFast->addParameter(RADIO_FREQUENCY, 869.525);
presetMtEu868LongFast->addParameter(RADIO_BANDWIDTH, 250.0);
presetMtEu868LongFast->addParameter(RADIO_SPREADFACTOR, 11);
presetMtEu868LongFast->addParameter(RADIO_CODINGRATE, 6);
presetMtEu868LongFast->addParameter(RADIO_SYNCWORD, 0x2B);
presetMtEu868LongFast->addParameter(RADIO_PREAMBLES, 16);
settingsView->addPreset(presetMtEu868LongFast);
settingsView->updatePresets();
}