Freqman mem reduce (#1076)

-made frequency lists a widget to avoid a full frequency_list copy
-reduced FREQMAN_MAX_PER_FILE to a working limit
This commit is contained in:
gullradriel 2023-05-26 18:17:47 +02:00 committed by GitHub
parent 00667cecf9
commit 89e24cb358
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 286 additions and 56 deletions

View File

@ -218,6 +218,7 @@ set(CPPSRC
ui/ui_btngrid.cpp
ui/ui_receiver.cpp
ui/ui_rssi.cpp
ui/ui_freqlist.cpp
ui/ui_tv.cpp
ui/ui_spectrum.cpp
ui/ui_tabview.cpp

View File

@ -97,9 +97,11 @@ void FreqManBaseView::change_category(int32_t category_id) {
if (!load_freqman_file(file_list[categories[current_category_id].second], database))
error_ = ERROR_ACCESS;
else
else {
menu_view.set_db(database);
refresh_list();
}
}
void FreqManBaseView::refresh_list() {
if (!database.size()) {
@ -108,20 +110,6 @@ void FreqManBaseView::refresh_list() {
} else {
if (on_refresh_widgets)
on_refresh_widgets(false);
menu_view.clear();
for (size_t n = 0; n < database.size(); n++) {
menu_view.add_item({freqman_item_string(database[n], 30),
ui::Color::white(),
nullptr,
[this](KeyEvent) {
if (on_select_frequency)
on_select_frequency();
}});
}
menu_view.set_highlighted(0); // Refresh
}
}
@ -206,18 +194,13 @@ FrequencyLoadView::FrequencyLoadView(
// Resize menu view to fill screen
menu_view.set_parent_rect({0, 3 * 8, 240, 30 * 8});
// Just to allow exit on left
menu_view.on_left = [&nav, this]() {
nav.pop();
};
change_category(last_category_id);
refresh_list();
on_select_frequency = [&nav, this]() {
menu_view.on_select = [&nav, this](FreqManUIList&) {
nav_.pop();
auto& entry = database[menu_view.highlighted_index()];
auto& entry = database[menu_view.get_index()];
if (entry.type == RANGE) {
// User chose a frequency range entry
@ -235,14 +218,14 @@ FrequencyLoadView::FrequencyLoadView(
}
void FrequencyManagerView::on_edit_freq(rf::Frequency f) {
database[menu_view.highlighted_index()].frequency_a = f;
database[menu_view.get_index()].frequency_a = f;
save_freqman_file(file_list[categories[current_category_id].second], database);
refresh_list();
}
void FrequencyManagerView::on_edit_desc(NavigationView& nav) {
text_prompt(nav, desc_buffer, 28, [this](std::string& buffer) {
database[menu_view.highlighted_index()].description = buffer;
database[menu_view.get_index()].description = buffer;
refresh_list();
save_freqman_file(file_list[categories[current_category_id].second], database);
});
@ -258,7 +241,7 @@ void FrequencyManagerView::on_new_category(NavigationView& nav) {
}
void FrequencyManagerView::on_delete() {
database.erase(database.begin() + menu_view.highlighted_index());
database.erase(database.begin() + menu_view.get_index());
save_freqman_file(file_list[categories[current_category_id].second], database);
refresh_list();
}
@ -292,15 +275,10 @@ FrequencyManagerView::FrequencyManagerView(
&button_edit_desc,
&button_delete});
// Just to allow exit on left
menu_view.on_left = [&nav, this]() {
nav.pop();
};
change_category(last_category_id);
refresh_list();
on_select_frequency = [this]() {
menu_view.on_select = [this](FreqManUIList&) {
button_edit_freq.focus();
};
@ -310,14 +288,14 @@ FrequencyManagerView::FrequencyManagerView(
};
button_edit_freq.on_select = [this, &nav](Button&) {
auto new_view = nav.push<FrequencyKeypadView>(database[menu_view.highlighted_index()].frequency_a);
auto new_view = nav.push<FrequencyKeypadView>(database[menu_view.get_index()].frequency_a);
new_view->on_changed = [this](rf::Frequency f) {
on_edit_freq(f);
};
};
button_edit_desc.on_select = [this, &nav](Button&) {
desc_buffer = database[menu_view.highlighted_index()].description;
desc_buffer = database[menu_view.get_index()].description;
on_edit_desc(nav);
};

View File

@ -28,6 +28,7 @@
#include "ui_receiver.hpp"
#include "ui_textentry.hpp"
#include "freqman.hpp"
#include "ui_freqlist.hpp"
namespace ui {
@ -65,9 +66,9 @@ class FreqManBaseView : public View {
14,
{}};
MenuView menu_view{
{0, 3 * 8, 240, 23 * 8},
true};
FreqManUIList menu_view{
{0, 3 * 8, 240, 23 * 8}};
Text text_empty{
{7 * 8, 12 * 8, 16 * 8, 16},
"Empty category !",

View File

@ -36,7 +36,11 @@ void ReconView::clear_freqlist_for_ui_action() {
freqlist_cleared_for_ui_action = true;
// if in manual mode, there is enough memory to load freqman files, else we have to unload/reload
if (!manual_mode) {
frequency_list.clear();
// clear and shrink_to_fit are not enough to really start with a new, clean, empty vector
// swap is the only way to achieve a perfect memory liberation
std::vector<freqman_entry>().swap(frequency_list);
} else {
frequency_list.shrink_to_fit();
}
}
@ -687,7 +691,10 @@ ReconView::ReconView(NavigationView& nav)
} else {
audio::output::stop();
frequency_list.clear();
// clear and shrink_to_fit are not enough to really start with a new, clean, empty vector
// swap is the only way to achieve a perfect memory liberation
std::vector<freqman_entry>().swap(frequency_list);
freqman_entry manual_freq_entry;
def_step = step_mode.selected_index(); // max range val
@ -895,7 +902,9 @@ void ReconView::frequency_file_load(bool stop_all_before) {
audio::output::stop();
def_step = step_mode.selected_index(); // use def_step from manual selector
frequency_list.clear(); // clear the existing frequency list (expected behavior)
// clear and shrink_to_fit are not enough to really start with a new, clean, empty vector
// swap is the only way to achieve a perfect memory liberation
std::vector<freqman_entry>().swap(frequency_list); // clear the existing frequency list (expected behavior)
std::string file_input = input_file; // default recon mode
if (scanner_mode) {
file_input = output_file;

View File

@ -114,35 +114,40 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq
char* line_end;
std::string description;
rf::Frequency frequency_a, frequency_b;
char file_data[257];
const int read_buf_size = 128;
char file_data[read_buf_size + 1];
freqman_entry_type type;
freqman_index_t modulation = 0;
freqman_index_t bandwidth = 0;
freqman_index_t step = 0;
freqman_index_t tone = 0;
db.clear();
// these are not enough to really start with a new, clean, empty vector
// swap is the only way to achieve a perfect memory liberation
// db.clear();
// db.shrink_to_fit():
std::vector<freqman_entry>().swap(db);
auto result = freqman_file.open("FREQMAN/" + file_stem + ".TXT");
if (result.is_valid())
return false;
while (1) {
// Read a 256 bytes block from file
// Read a read_buf_size bytes block from file
freqman_file.seek(file_position);
memset(file_data, 0, 257);
auto read_size = freqman_file.read(file_data, 256);
memset(file_data, 0, read_buf_size + 1);
auto read_size = freqman_file.read(file_data, read_buf_size);
if (read_size.is_error())
return false; // Read error
file_position += 256;
file_position += read_buf_size;
// Reset line_start to beginning of buffer
line_start = file_data;
// If EOF reached, insert 0x0A after, in case the last line doesn't have a C/R
if (read_size.value() < 256)
if (read_size.value() < read_buf_size)
*(line_start + read_size.value()) = 0x0A;
// Look for complete lines in buffer
@ -222,8 +227,7 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq
pos += 2;
length = std::min(strcspn(pos, ",\x0A"), (size_t)FREQMAN_DESC_MAX_LEN);
description = string(pos, length);
} else
description = "-";
}
if ((type == SINGLE && load_freqs) || (type == RANGE && load_ranges) || (type == HAMRADIO && load_hamradios)) {
db.push_back({frequency_a, frequency_b, description, type, modulation, bandwidth, step, tone});
n++;
@ -231,14 +235,14 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq
}
line_start = line_end + 1;
if (line_start - file_data >= 256) break;
if (line_start - file_data >= read_buf_size) break;
}
if (read_size.value() != 256)
if (read_size.value() != read_buf_size)
break; // End of file
// Restart at beginning of last incomplete line
file_position -= (file_data + 256 - line_start);
file_position -= (file_data + read_buf_size - line_start);
}
/* populate implicitly specified modulation / bandwidth */

View File

@ -33,9 +33,9 @@
#include "ui_widget.hpp"
#define FREQMAN_DESC_MAX_LEN 24 // This is the number of characters that can be drawn in front of "R: TEXT..." before taking a full screen line
#define FREQMAN_MAX_PER_FILE 115 // Maximum of entries we can read. This is a hardware limit
#define FREQMAN_MAX_PER_FILE 60 // Maximum of entries we can read. This is a hardware limit
// It was tested and lowered to leave a bit of space to the caller
#define FREQMAN_MAX_PER_FILE_STR "115" // STRING OF FREQMAN_MAX_PER_FILE
#define FREQMAN_MAX_PER_FILE_STR "60" // STRING OF FREQMAN_MAX_PER_FILE
using namespace ui;
using namespace std;

View File

@ -0,0 +1,157 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_freqlist.hpp"
#include "baseband_api.hpp"
#include "utility.hpp"
#include <algorithm>
namespace ui {
FreqManUIList::FreqManUIList(
Rect parent_rect,
bool instant_exec)
: Widget{parent_rect},
instant_exec_{instant_exec} {
this->set_focusable(true);
}
void FreqManUIList::set_highlighted_index(int index) {
if ((unsigned)(current_index + index) >= freqlist_db.size())
return;
if (index < 0) {
index = 0;
if (current_index > 0)
current_index--;
}
if (index >= freqlist_nb_lines) {
index = freqlist_nb_lines - 1;
if ((unsigned)(current_index + index) < freqlist_db.size())
current_index++;
else
current_index = freqlist_db.size() - freqlist_nb_lines - 1;
}
highlighted_index = index;
}
uint8_t FreqManUIList::get_index() {
return current_index + highlighted_index;
}
void FreqManUIList::paint(Painter& painter) {
freqlist_nb_lines = 0;
const auto r = screen_rect();
uint8_t focused = has_focus();
const Rect r_widget_screen{r.left() + focused, r.top() + focused, r.width() - 2 * focused, r.height() - 2 * +focused};
painter.fill_rectangle(
r_widget_screen,
Color::black());
if (freqlist_db.size() == 0)
return;
uint8_t nb_lines = 0;
for (uint8_t it = current_index; it < freqlist_db.size(); it++) {
uint8_t line_height = (int)nb_lines * char_height;
if (line_height < (r.height() - char_height)) {
std::string description = freqman_item_string(freqlist_db[it], 30);
// line is within the widget
painter.draw_string(
{0, r.location().y() + (int)nb_lines * char_height},
style_default, description);
if (nb_lines == highlighted_index) {
const Rect r_highlighted_freq{0, r.location().y() + (int)nb_lines * char_height, 240, char_height};
painter.draw_rectangle(
r_highlighted_freq,
Color::white());
}
nb_lines++;
} else {
// rect is filled, we can break
break;
}
}
freqlist_nb_lines = nb_lines;
if (has_focus() || highlighted()) {
const Rect r_focus{r.left(), r.top(), r.width(), r.height()};
painter.draw_rectangle(
r_focus,
Color::white());
}
}
void FreqManUIList::set_db(freqman_db& db) {
freqlist_db = db;
current_index = 0;
highlighted_index = 0;
}
void FreqManUIList::on_focus() {
if (on_highlight)
on_highlight(*this);
}
bool FreqManUIList::on_key(const KeyEvent key) {
if (key == KeyEvent::Select) {
if (on_select) {
on_select(*this);
return true;
}
} else {
if (on_dir) {
return on_dir(*this, key);
}
}
return false;
}
bool FreqManUIList::on_touch(const TouchEvent event) {
switch (event.type) {
case TouchEvent::Type::Start:
set_highlighted(true);
set_dirty();
if (on_touch_press) {
on_touch_press(*this);
}
if (on_select && instant_exec_) {
on_select(*this);
}
return true;
case TouchEvent::Type::End:
set_highlighted(false);
set_dirty();
if (on_touch_release) {
on_touch_release(*this);
}
if (on_select && !instant_exec_) {
on_select(*this);
}
return true;
default:
return false;
}
}
bool FreqManUIList::on_encoder(EncoderEvent delta) {
set_highlighted_index((int)highlighted_index + delta);
set_dirty();
return true;
}
} /* namespace ui */

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_FREQLIST_H__
#define __UI_FREQLIST_H__
#include "ui.hpp"
#include "ui_font_fixed_5x8.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include "freqman.hpp"
#include <cstdint>
namespace ui {
class FreqManUIList : public Widget {
public:
std::function<void(FreqManUIList&)> on_select{};
std::function<void(FreqManUIList&)> on_touch_release{}; // Executed when releasing touch, after on_select.
std::function<void(FreqManUIList&)> on_touch_press{}; // Executed when touching, before on_select.
std::function<bool(FreqManUIList&, KeyEvent)> on_dir{};
std::function<void(FreqManUIList&)> on_highlight{};
FreqManUIList(Rect parent_rect, bool instant_exec); // instant_exec: Execute on_select when you touching instead of releasing
FreqManUIList(
Rect parent_rect)
: FreqManUIList{parent_rect, false} {
}
FreqManUIList()
: FreqManUIList{{}, {}} {
}
void paint(Painter& painter) override;
void on_focus() override;
bool on_key(const KeyEvent key) override;
bool on_touch(const TouchEvent event) override;
bool on_encoder(EncoderEvent delta) override;
bool set_highlighted_index(int index); // set highlighted_index and return capped highlighted_index value to set in caller
uint8_t get_index(); // return highlighed + index
void set_db(freqman_db& db);
private:
static constexpr Style style_default{
.font = font::fixed_8x16,
.background = Color::black(),
.foreground = Color::white(),
};
static constexpr int8_t char_height = 16;
bool instant_exec_{false};
freqman_db freqlist_db{};
int current_index{0};
int highlighted_index{0};
int freqlist_nb_lines{0};
};
} // namespace ui
#endif /*__UI_FREQLIST_H__*/