mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-08-15 00:27:40 +00:00
Add file reader (#1155)
* Add file reader * Add a simple test example of parsing settings. * Use new FileLineReader to parse Glass presets * Trim CRLF from Glass preset name
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
*/
|
||||
|
||||
#include "ui_looking_glass_app.hpp"
|
||||
#include "file_reader.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -519,70 +521,42 @@ GlassView::GlassView(
|
||||
|
||||
void GlassView::load_Presets() {
|
||||
File presets_file;
|
||||
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
presets_db.clear(); // Start with fresh db
|
||||
if (result.is_valid()) {
|
||||
presets_Default(); // There is no txt, store a default range
|
||||
} else {
|
||||
std::string line; // There is a txt file
|
||||
char one_char[1]; // Read it char by char
|
||||
for (size_t pointer = 0; pointer < presets_file.size(); pointer++) {
|
||||
presets_file.seek(pointer);
|
||||
presets_file.read(one_char, 1);
|
||||
if ((int)one_char[0] > 31) { // ascii space upwards
|
||||
line += one_char[0]; // Add it to the textline
|
||||
} else if (one_char[0] == '\n') { // New Line
|
||||
txtline_process(line); // make sense of this textline
|
||||
line.clear(); // Ready for next textline
|
||||
}
|
||||
auto error = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
presets_db.clear();
|
||||
|
||||
if (!error) {
|
||||
auto reader = FileLineReader(presets_file);
|
||||
for (const auto& line : reader) {
|
||||
if (line.length() == 0 || line[0] == '#')
|
||||
continue;
|
||||
|
||||
auto cols = split_string(line, ',');
|
||||
if (cols.size() != 3)
|
||||
continue;
|
||||
|
||||
// TODO: add some conversion helpers that take string_view.
|
||||
presets_db.emplace_back(preset_entry{
|
||||
std::stoi(std::string{cols[0]}),
|
||||
std::stoi(std::string{cols[1]}),
|
||||
trimr(std::string{cols[2]})});
|
||||
}
|
||||
if (line.length() > 0)
|
||||
txtline_process(line); // Last line had no newline at end ?
|
||||
if (!presets_db.size())
|
||||
presets_Default(); // no antenna on txt, use default
|
||||
}
|
||||
|
||||
// Couldn't load any from the file, load a default instead.
|
||||
if (presets_db.empty())
|
||||
presets_Default();
|
||||
|
||||
populate_Presets();
|
||||
}
|
||||
|
||||
void GlassView::txtline_process(std::string& line) {
|
||||
if (line.find("#") != std::string::npos)
|
||||
return; // Line is just a comment
|
||||
|
||||
size_t comma = line.find(","); // Get first comma position
|
||||
if (comma == std::string::npos)
|
||||
return; // No comma at all
|
||||
|
||||
size_t previous = 0;
|
||||
preset_entry new_preset;
|
||||
|
||||
new_preset.min = std::stoi(line.substr(0, comma));
|
||||
if (!new_preset.min)
|
||||
return; // No frequency!
|
||||
|
||||
previous = comma + 1;
|
||||
comma = line.find(",", previous); // Search for next delimiter
|
||||
if (comma == std::string::npos)
|
||||
return; // No comma at all
|
||||
|
||||
new_preset.max = std::stoi(line.substr(previous, comma - previous));
|
||||
if (!new_preset.max)
|
||||
return; // No frequency!
|
||||
|
||||
new_preset.label = line.substr(comma + 1);
|
||||
if (new_preset.label.size() == 0)
|
||||
return; // No label ?
|
||||
|
||||
presets_db.push_back(new_preset); // Add this preset.
|
||||
}
|
||||
|
||||
void GlassView::populate_Presets() {
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t entries;
|
||||
|
||||
for (preset_entry preset : presets_db) { // go thru all available presets
|
||||
for (const auto& preset : presets_db)
|
||||
entries.emplace_back(preset.label, entries.size());
|
||||
}
|
||||
|
||||
range_presets.set_options(entries);
|
||||
}
|
||||
|
||||
|
@@ -51,10 +51,7 @@ class SIGFRXView : public View {
|
||||
uint8_t last_channel;
|
||||
uint8_t detect_counter = 0;
|
||||
|
||||
RxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
3072000 /* sampling rate */
|
||||
};
|
||||
RxRadioState radio_state_{};
|
||||
|
||||
const uint16_t sigfrx_marks[18] = {
|
||||
10, 8, 0,
|
||||
|
@@ -115,8 +115,10 @@ bool TextViewer::on_encoder(EncoderEvent delta) {
|
||||
|
||||
if (cursor_.dir == ScrollDirection::Horizontal)
|
||||
updated = apply_scrolling_constraints(0, delta);
|
||||
else
|
||||
else {
|
||||
delta *= 16;
|
||||
updated = apply_scrolling_constraints(delta, 0);
|
||||
}
|
||||
|
||||
if (updated)
|
||||
redraw();
|
||||
|
155
firmware/application/file_reader.hpp
Normal file
155
firmware/application/file_reader.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Kyle Reed
|
||||
*
|
||||
* 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 __FILE_READER_HPP__
|
||||
#define __FILE_READER_HPP__
|
||||
|
||||
#include "file.hpp"
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
/* BufferType requires the following members
|
||||
* Size size()
|
||||
* Result<Size> read(void* data, Size bytes_to_read)
|
||||
* Result<Offset> seek(uint32_t offset)
|
||||
*/
|
||||
|
||||
/* Iterates lines in buffer split on '\n'.
|
||||
* NB: very basic iterator impl, don't try anything fancy with it. */
|
||||
template <typename BufferType>
|
||||
class BufferLineReader {
|
||||
public:
|
||||
struct iterator {
|
||||
bool operator!=(const iterator& other) {
|
||||
return this->pos_ != other.pos_ || this->reader_ != other.reader_;
|
||||
}
|
||||
|
||||
const std::string& operator*() {
|
||||
if (!cached_) {
|
||||
bool ok = reader_->read_line(*this);
|
||||
cached_ = true;
|
||||
|
||||
if (!ok) *this = reader_->end();
|
||||
}
|
||||
|
||||
return line_data_;
|
||||
}
|
||||
|
||||
iterator& operator++() {
|
||||
const auto size = reader_->size();
|
||||
|
||||
if (pos_ < size) {
|
||||
cached_ = false;
|
||||
pos_ += line_data_.length();
|
||||
}
|
||||
|
||||
if (pos_ >= size)
|
||||
*this = reader_->end();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
typename BufferType::Size pos_{};
|
||||
BufferLineReader* reader_{};
|
||||
bool cached_ = false;
|
||||
std::string line_data_{};
|
||||
};
|
||||
|
||||
BufferLineReader(BufferType& buffer)
|
||||
: buffer_{buffer} {}
|
||||
|
||||
iterator begin() { return {0, this}; }
|
||||
iterator end() { return {size(), this}; }
|
||||
|
||||
typename BufferType::Size size() const { return buffer_.size(); }
|
||||
|
||||
private:
|
||||
BufferType& buffer_;
|
||||
|
||||
bool read_line(iterator& it) {
|
||||
constexpr size_t buf_size = 0x80;
|
||||
char buf[buf_size];
|
||||
uint32_t offset = 0;
|
||||
|
||||
it.line_data_.resize(buf_size);
|
||||
buffer_.seek(it.pos_);
|
||||
|
||||
while (true) {
|
||||
auto read = buffer_.read(buf, buf_size);
|
||||
if (!read)
|
||||
return false;
|
||||
|
||||
// Find newline.
|
||||
auto len = 0u;
|
||||
for (; len < *read; ++len) {
|
||||
if (buf[len] == '\n') {
|
||||
++len;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reallocate if needed.
|
||||
if (offset + len >= it.line_data_.length())
|
||||
it.line_data_.resize(offset + len);
|
||||
|
||||
std::strncpy(&it.line_data_[offset], buf, len);
|
||||
offset += len;
|
||||
|
||||
if (len < buf_size)
|
||||
break;
|
||||
}
|
||||
|
||||
it.line_data_.resize(offset);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
using FileLineReader = BufferLineReader<File>;
|
||||
|
||||
/* Splits the string on the specified char and returns
|
||||
* a vector of string_views. NB: the lifetime of the
|
||||
* string to split must be maintained while the views
|
||||
* are used or they will dangle. */
|
||||
std::vector<std::string_view> split_string(std::string_view str, char c) {
|
||||
std::vector<std::string_view> cols;
|
||||
size_t start = 0;
|
||||
|
||||
while (start < str.length()) {
|
||||
auto it = str.find(c, start);
|
||||
|
||||
if (it == str.npos)
|
||||
break;
|
||||
|
||||
// TODO: allow empty?
|
||||
cols.emplace_back(&str[start], it - start);
|
||||
start = it + 1;
|
||||
}
|
||||
|
||||
if (start <= str.length() && !str.empty())
|
||||
cols.emplace_back(&str[start], str.length() - start);
|
||||
|
||||
return cols;
|
||||
}
|
||||
|
||||
#endif
|
@@ -281,14 +281,16 @@ double get_decimals(double num, int16_t mult, bool round) {
|
||||
return intnum;
|
||||
}
|
||||
|
||||
static const char* whitespace_str = " \t\r\n";
|
||||
|
||||
std::string trim(const std::string& str) {
|
||||
auto first = str.find_first_not_of(' ');
|
||||
auto last = str.find_last_not_of(' ');
|
||||
auto first = str.find_first_not_of(whitespace_str);
|
||||
auto last = str.find_last_not_of(whitespace_str);
|
||||
return str.substr(first, last - first);
|
||||
}
|
||||
|
||||
std::string trimr(std::string str) {
|
||||
size_t last = str.find_last_not_of(' ');
|
||||
std::string trimr(const std::string& str) {
|
||||
size_t last = str.find_last_not_of(whitespace_str);
|
||||
return (last != std::string::npos) ? str.substr(0, last + 1) : ""; // Remove the trailing spaces
|
||||
}
|
||||
|
||||
|
@@ -68,8 +68,8 @@ std::string to_string_file_size(uint32_t file_size);
|
||||
std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precision);
|
||||
double get_decimals(double num, int16_t mult, bool round = false); // euquiq added
|
||||
|
||||
std::string trim(const std::string& str); // Remove whitespace at ends.
|
||||
std::string trimr(std::string str); // Remove trailing spaces
|
||||
std::string trim(const std::string& str); // Remove whitespace at ends.
|
||||
std::string trimr(const std::string& str); // Remove trailing spaces
|
||||
std::string truncate(const std::string& str, size_t length);
|
||||
|
||||
#endif /*__STRING_FORMAT_H__*/
|
||||
|
Reference in New Issue
Block a user