Public Access
The Storage App browses the SD card: folders first with sizes and dates, three sorts, one item at a time with a clipboard (c, x, v), rename, delete after counting what's inside, new folder, details. A listing holds 256 entries and says when a folder has more. FileOps does the card's work for the App and the console alike, one operation at a time on the storage task in turns of about 150 ms, so Logs and Captures are still written during a long copy. A copy shows progress, can be cancelled (what it wrote is taken back) and compares sizes after. The read-only rules are checked there: the firmware's top-level folders, /gemini/cache, and files being written (a Track, a Capture, an upload, today's IRC Logs). A listing reads the folder straight from FatFs: through the Arduino File, 329 entries took over two seconds. Viewers by type: text read a screen at a time whatever the file's size (logs open at the end), a hex dump, a Capture's packets as the LoRa Scanner lists them, a Track's summary, and an Update File checked as an install would check it, without writing anything. Tab shows any file as hex or text. Settings > Storage is gone: usage, Storage Clean-up and Erase are the App's Maintenance, behind a warning. The Storage Warning points there. The Clock sets the system time whatever its source, so files are dated correctly with a GNSS Fix alone (Q137). Console: cp, mv, mkdir, du, cancel; rm takes folders and follows the rules; ls shows dates; Debug Builds get `sd fill`. 424 host tests. Checked on the device: docs/milestones/F1.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
58 lines
1.9 KiB
C++
58 lines
1.9 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "loratap.h"
|
|
|
|
namespace roro::files {
|
|
|
|
// What the Storage App's viewers show of the files the firmware writes (F1, Q134).
|
|
|
|
// One row of a hex dump, eight bytes: "00010 4865 6c6c 6f2c 2077 Hello, w".
|
|
std::string hexRow(uint32_t offset, const uint8_t* data, size_t len);
|
|
|
|
// A Track (.gpx) read a piece at a time: its points, when it started and ended, how far it went.
|
|
class GpxSummary {
|
|
public:
|
|
void feed(const char* data, size_t len);
|
|
|
|
uint32_t points() const { return points_; }
|
|
int64_t start() const { return first_; } // UTC seconds, 0 if no point carried a time
|
|
int64_t end() const { return last_; }
|
|
double meters() const { return meters_; }
|
|
std::vector<std::string> lines() const; // for the screen
|
|
|
|
private:
|
|
void point(const std::string& element);
|
|
|
|
std::string carry_; // the part of a point cut by the end of a piece
|
|
uint32_t points_ = 0;
|
|
int64_t first_ = 0, last_ = 0;
|
|
double meters_ = 0, lat_ = 0, lon_ = 0;
|
|
};
|
|
|
|
// "1 h 02 min", "4 min 10 s", "12 s".
|
|
std::string formatDuration(int64_t seconds);
|
|
|
|
// A Capture (.pcap): the file's header, then one record after another.
|
|
struct PcapHeader {
|
|
bool ok = false;
|
|
uint32_t linkType = 0; // 270: LoRaTap, what the LoRa Scanner writes
|
|
};
|
|
constexpr size_t kPcapHeaderSize = 24, kPcapRecordSize = 16;
|
|
constexpr uint32_t kLinkLoraTap = 270;
|
|
PcapHeader parsePcapHeader(const uint8_t* data, size_t len);
|
|
|
|
struct PcapRecord {
|
|
uint32_t seconds = 0, micros = 0, length = 0; // length: the bytes that follow in the file
|
|
};
|
|
bool parsePcapRecord(const uint8_t* data, size_t len, PcapRecord& out); // false: not a record, stop there
|
|
|
|
// The LoRaTap header a packet starts with; false if `len` is too short for one.
|
|
bool parseLoraTap(const uint8_t* data, size_t len, lora::RxInfo& out);
|
|
|
|
} // namespace roro::files
|