mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-28 16:59:47 -05:00
Improve File error handling. Massive effects...
API is somewhat stolen from Rust std::fs::File. Static factories didn't work out so well, though. Move semantics made my head explode. TODO: Still a lot of places where errors aren't handled, but it's an improvement...
This commit is contained in:
parent
d905c446bf
commit
682a1706a3
@ -126,25 +126,17 @@ static std::string true_heading(const TrueHeading value) {
|
||||
} /* namespace format */
|
||||
} /* namespace ais */
|
||||
|
||||
AISLogger::AISLogger(
|
||||
const std::string& file_path
|
||||
) : log_file { file_path }
|
||||
{
|
||||
}
|
||||
|
||||
void AISLogger::on_packet(const ais::Packet& packet) {
|
||||
// TODO: Unstuff here, not in baseband!
|
||||
if( log_file.is_open() ) {
|
||||
std::string entry;
|
||||
entry.reserve((packet.length() + 3) / 4);
|
||||
std::string entry;
|
||||
entry.reserve((packet.length() + 3) / 4);
|
||||
|
||||
for(size_t i=0; i<packet.length(); i+=4) {
|
||||
const auto nibble = packet.read(i, 4);
|
||||
entry += (nibble >= 10) ? ('W' + nibble) : ('0' + nibble);
|
||||
}
|
||||
|
||||
log_file.write_entry(packet.received_at(), entry);
|
||||
for(size_t i=0; i<packet.length(); i+=4) {
|
||||
const auto nibble = packet.read(i, 4);
|
||||
entry += (nibble >= 10) ? ('W' + nibble) : ('0' + nibble);
|
||||
}
|
||||
|
||||
log_file.write_entry(packet.received_at(), entry);
|
||||
}
|
||||
|
||||
void AISRecentEntry::update(const ais::Packet& packet) {
|
||||
@ -332,7 +324,10 @@ AISAppView::AISAppView(NavigationView&) {
|
||||
this->on_show_list();
|
||||
};
|
||||
|
||||
logger = std::make_unique<AISLogger>("ais.txt");
|
||||
logger = std::make_unique<AISLogger>();
|
||||
if( logger ) {
|
||||
logger->append("ais.txt");
|
||||
}
|
||||
}
|
||||
|
||||
AISAppView::~AISAppView() {
|
||||
|
@ -92,8 +92,10 @@ using AISRecentEntries = RecentEntries<ais::Packet, AISRecentEntry>;
|
||||
|
||||
class AISLogger {
|
||||
public:
|
||||
AISLogger(const std::string& file_path);
|
||||
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
void on_packet(const ais::Packet& packet);
|
||||
|
||||
private:
|
||||
|
@ -98,13 +98,8 @@ CaptureThread::~CaptureThread() {
|
||||
}
|
||||
}
|
||||
|
||||
Optional<std::string> CaptureThread::error() const {
|
||||
const auto error = writer->error();
|
||||
if( error.is_valid() ) {
|
||||
return { error.value().what() };
|
||||
} else {
|
||||
return { };
|
||||
}
|
||||
const Optional<File::Error>& CaptureThread::error() const {
|
||||
return last_error;
|
||||
}
|
||||
|
||||
void CaptureThread::check_fifo_isr() {
|
||||
@ -118,14 +113,15 @@ void CaptureThread::check_fifo_isr() {
|
||||
}
|
||||
}
|
||||
|
||||
msg_t CaptureThread::run() {
|
||||
Optional<File::Error> CaptureThread::run() {
|
||||
StreamOutput stream { &config };
|
||||
|
||||
while( !chThdShouldTerminate() ) {
|
||||
if( stream.available() ) {
|
||||
auto buffer = stream.get_buffer();
|
||||
if( !writer->write(buffer->data(), buffer->size()) ) {
|
||||
return false;
|
||||
auto write_result = writer->write(buffer->data(), buffer->size());
|
||||
if( write_result.is_error() ) {
|
||||
return write_result.error();
|
||||
}
|
||||
stream.release_buffer(buffer);
|
||||
} else {
|
||||
@ -133,5 +129,5 @@ msg_t CaptureThread::run() {
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return { };
|
||||
}
|
||||
|
@ -35,8 +35,7 @@
|
||||
|
||||
class Writer {
|
||||
public:
|
||||
virtual bool write(const void* const buffer, const size_t bytes) = 0;
|
||||
virtual Optional<std::filesystem::filesystem_error> error() const = 0;
|
||||
virtual File::Result<size_t> write(const void* const buffer, const size_t bytes) = 0;
|
||||
virtual ~Writer() = default;
|
||||
};
|
||||
|
||||
@ -53,21 +52,23 @@ public:
|
||||
return config;
|
||||
}
|
||||
|
||||
Optional<std::string> error() const;
|
||||
const Optional<File::Error>& error() const;
|
||||
|
||||
static void check_fifo_isr();
|
||||
|
||||
private:
|
||||
CaptureConfig config;
|
||||
std::unique_ptr<Writer> writer;
|
||||
Optional<File::Error> last_error;
|
||||
static Thread* thread;
|
||||
|
||||
static msg_t static_fn(void* arg) {
|
||||
auto obj = static_cast<CaptureThread*>(arg);
|
||||
return obj->run();
|
||||
obj->last_error = obj->run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
msg_t run();
|
||||
Optional<File::Error> run();
|
||||
};
|
||||
|
||||
#endif/*__CAPTURE_THREAD_H__*/
|
||||
|
@ -57,17 +57,9 @@ std::string commodity_type(CommodityType value) {
|
||||
|
||||
} /* namespace ert */
|
||||
|
||||
ERTLogger::ERTLogger(
|
||||
const std::string& file_path
|
||||
) : log_file { file_path }
|
||||
{
|
||||
}
|
||||
|
||||
void ERTLogger::on_packet(const ert::Packet& packet) {
|
||||
if( log_file.is_open() ) {
|
||||
const auto formatted = packet.symbols_formatted();
|
||||
log_file.write_entry(packet.received_at(), formatted.data + "/" + formatted.errors);
|
||||
}
|
||||
const auto formatted = packet.symbols_formatted();
|
||||
log_file.write_entry(packet.received_at(), formatted.data + "/" + formatted.errors);
|
||||
}
|
||||
|
||||
const ERTRecentEntry::Key ERTRecentEntry::invalid_key { };
|
||||
@ -148,7 +140,10 @@ ERTAppView::ERTAppView(NavigationView&) {
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
|
||||
logger = std::make_unique<ERTLogger>("ert.txt");
|
||||
logger = std::make_unique<ERTLogger>();
|
||||
if( logger ) {
|
||||
logger->append("ert.txt");
|
||||
}
|
||||
}
|
||||
|
||||
ERTAppView::~ERTAppView() {
|
||||
|
@ -87,8 +87,10 @@ struct ERTRecentEntry {
|
||||
|
||||
class ERTLogger {
|
||||
public:
|
||||
ERTLogger(const std::string& file_path);
|
||||
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
void on_packet(const ert::Packet& packet);
|
||||
|
||||
private:
|
||||
|
@ -28,99 +28,93 @@ static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected.");
|
||||
#define FR_DISK_FULL (0x100)
|
||||
#define FR_EOF (0x101)
|
||||
#define FR_BAD_SEEK (0x102)
|
||||
#define FR_UNEXPECTED (0x103)
|
||||
|
||||
File::File(
|
||||
const std::string& filename,
|
||||
openmode mode
|
||||
) : err { FR_OK }
|
||||
{
|
||||
BYTE fatfs_mode = 0;
|
||||
if( mode & openmode::in ) {
|
||||
fatfs_mode |= FA_READ;
|
||||
}
|
||||
if( mode & openmode::out ) {
|
||||
fatfs_mode |= FA_WRITE;
|
||||
}
|
||||
if( mode & openmode::trunc ) {
|
||||
fatfs_mode |= FA_CREATE_ALWAYS;
|
||||
}
|
||||
if( mode & openmode::ate ) {
|
||||
fatfs_mode |= FA_OPEN_ALWAYS;
|
||||
}
|
||||
|
||||
err = f_open(&f, filename.c_str(), fatfs_mode);
|
||||
if( err == FR_OK ) {
|
||||
if( mode & openmode::ate ) {
|
||||
err = f_lseek(&f, f_size(&f));
|
||||
if( err != FR_OK ) {
|
||||
Optional<File::Error> File::open_fatfs(const std::string& filename, BYTE mode) {
|
||||
auto result = f_open(&f, filename.c_str(), mode);
|
||||
if( result == FR_OK ) {
|
||||
if( mode & FA_OPEN_ALWAYS ) {
|
||||
const auto result = f_lseek(&f, f_size(&f));
|
||||
if( result != FR_OK ) {
|
||||
f_close(&f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( result == FR_OK ) {
|
||||
return { };
|
||||
} else {
|
||||
return { result };
|
||||
}
|
||||
}
|
||||
|
||||
Optional<File::Error> File::open(const std::string& filename) {
|
||||
return open_fatfs(filename, FA_READ);
|
||||
}
|
||||
|
||||
Optional<File::Error> File::append(const std::string& filename) {
|
||||
return open_fatfs(filename, FA_WRITE | FA_OPEN_ALWAYS);
|
||||
}
|
||||
|
||||
Optional<File::Error> File::create(const std::string& filename) {
|
||||
return open_fatfs(filename, FA_WRITE | FA_CREATE_ALWAYS);
|
||||
}
|
||||
|
||||
File::~File() {
|
||||
f_close(&f);
|
||||
}
|
||||
|
||||
bool File::read(void* const data, const size_t bytes_to_read) {
|
||||
if( err != FR_OK ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File::Result<size_t> File::read(void* const data, const size_t bytes_to_read) {
|
||||
UINT bytes_read = 0;
|
||||
err = f_read(&f, data, bytes_to_read, &bytes_read);
|
||||
if( bytes_read != bytes_to_read ) {
|
||||
err = FR_EOF;
|
||||
const auto result = f_read(&f, data, bytes_to_read, &bytes_read);
|
||||
if( result == FR_OK ) {
|
||||
return { static_cast<size_t>(bytes_read) };
|
||||
} else {
|
||||
return { static_cast<Error>(result) };
|
||||
}
|
||||
return (err == FR_OK);
|
||||
}
|
||||
|
||||
bool File::write(const void* const data, const size_t bytes_to_write) {
|
||||
if( err != FR_OK ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File::Result<size_t> File::write(const void* const data, const size_t bytes_to_write) {
|
||||
UINT bytes_written = 0;
|
||||
err = f_write(&f, data, bytes_to_write, &bytes_written);
|
||||
if( bytes_written != bytes_to_write ) {
|
||||
err = FR_DISK_FULL;
|
||||
const auto result = f_write(&f, data, bytes_to_write, &bytes_written);
|
||||
if( result == FR_OK ) {
|
||||
return { static_cast<size_t>(bytes_written) };
|
||||
} else {
|
||||
return { static_cast<Error>(result) };
|
||||
}
|
||||
return (err == FR_OK);
|
||||
}
|
||||
|
||||
uint64_t File::seek(const uint64_t new_position) {
|
||||
if( err != FR_OK ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File::Result<uint64_t> File::seek(const uint64_t new_position) {
|
||||
/* NOTE: Returns *old* position, not new position */
|
||||
const auto old_position = f_tell(&f);
|
||||
err = f_lseek(&f, new_position);
|
||||
if( err != FR_OK ) {
|
||||
f_close(&f);
|
||||
const auto result = f_lseek(&f, new_position);
|
||||
if( result != FR_OK ) {
|
||||
return { static_cast<Error>(result) };
|
||||
}
|
||||
if( f_tell(&f) != new_position ) {
|
||||
err = FR_BAD_SEEK;
|
||||
f_close(&f);
|
||||
return { static_cast<Error>(FR_BAD_SEEK) };
|
||||
}
|
||||
return old_position;
|
||||
return { static_cast<uint64_t>(old_position) };
|
||||
}
|
||||
|
||||
bool File::puts(const std::string& string) {
|
||||
File::Result<size_t> File::puts(const std::string& string) {
|
||||
const auto result = f_puts(string.c_str(), &f);
|
||||
if( result != (int)string.size() ) {
|
||||
err = FR_DISK_FULL;
|
||||
if( result >= 0 ) {
|
||||
return { static_cast<size_t>(result) };
|
||||
} else if( result == EOF ) {
|
||||
return { static_cast<Error>(f_error(&f)) };
|
||||
} else {
|
||||
return { static_cast<Error>(FR_UNEXPECTED) };
|
||||
}
|
||||
return (result >= 0);
|
||||
}
|
||||
|
||||
bool File::sync() {
|
||||
if( err != FR_OK ) {
|
||||
return false;
|
||||
Optional<File::Error> File::sync() {
|
||||
const auto result = f_sync(&f);
|
||||
if( result == FR_OK ) {
|
||||
return { };
|
||||
} else {
|
||||
return { result };
|
||||
}
|
||||
|
||||
err = f_sync(&f);
|
||||
return (err == FR_OK);
|
||||
}
|
||||
|
||||
static std::string find_last_file_matching_pattern(const std::string& pattern) {
|
||||
@ -181,7 +175,7 @@ namespace filesystem {
|
||||
|
||||
std::string filesystem_error::what() const {
|
||||
switch(err) {
|
||||
case FR_OK: return "";
|
||||
case FR_OK: return "ok";
|
||||
case FR_DISK_ERR: return "disk error";
|
||||
case FR_INT_ERR: return "insanity detected";
|
||||
case FR_NOT_READY: return "not ready";
|
||||
@ -204,6 +198,7 @@ std::string filesystem_error::what() const {
|
||||
case FR_EOF: return "end of file";
|
||||
case FR_DISK_FULL: return "disk full";
|
||||
case FR_BAD_SEEK: return "bad seek";
|
||||
case FR_UNEXPECTED: return "unexpected";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@
|
||||
|
||||
#include "ff.h"
|
||||
|
||||
#include "optional.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@ -37,9 +39,27 @@ namespace std {
|
||||
namespace filesystem {
|
||||
|
||||
struct filesystem_error {
|
||||
const uint32_t err;
|
||||
constexpr filesystem_error(
|
||||
) : err { FR_OK }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr filesystem_error(
|
||||
FRESULT fatfs_error
|
||||
) : err { fatfs_error }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr filesystem_error(
|
||||
unsigned int other_error
|
||||
) : err { other_error }
|
||||
{
|
||||
}
|
||||
|
||||
std::string what() const;
|
||||
|
||||
private:
|
||||
uint32_t err;
|
||||
};
|
||||
|
||||
using path = std::string;
|
||||
@ -109,51 +129,89 @@ space_info space(const path& p);
|
||||
|
||||
class File {
|
||||
public:
|
||||
enum openmode {
|
||||
app = 0x100,
|
||||
binary = 0x200,
|
||||
in = FA_READ,
|
||||
out = FA_WRITE,
|
||||
trunc = FA_CREATE_ALWAYS,
|
||||
ate = FA_OPEN_ALWAYS,
|
||||
using Error = std::filesystem::filesystem_error;
|
||||
|
||||
template<typename T>
|
||||
struct Result {
|
||||
enum class Type {
|
||||
Success,
|
||||
Error,
|
||||
} type;
|
||||
union {
|
||||
T value_;
|
||||
Error error_;
|
||||
};
|
||||
|
||||
bool is_ok() const {
|
||||
return type == Type::Success;
|
||||
}
|
||||
|
||||
bool is_error() const {
|
||||
return type == Type::Error;
|
||||
}
|
||||
|
||||
const T& value() {
|
||||
return value_;
|
||||
}
|
||||
|
||||
Error error() const {
|
||||
return error_;
|
||||
}
|
||||
|
||||
Result() = delete;
|
||||
|
||||
constexpr Result(
|
||||
T value
|
||||
) : type { Type::Success },
|
||||
value_ { value }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Result(
|
||||
Error error
|
||||
) : type { Type::Error },
|
||||
error_ { error }
|
||||
{
|
||||
}
|
||||
|
||||
~Result() {
|
||||
if( type == Type::Success ) {
|
||||
value_.~T();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
File(const std::string& filename, openmode mode);
|
||||
File() { };
|
||||
~File();
|
||||
|
||||
bool is_open() const {
|
||||
return err == FR_OK;
|
||||
}
|
||||
/* Prevent copies */
|
||||
File(const File&) = delete;
|
||||
File& operator=(const File&) = delete;
|
||||
|
||||
bool bad() const {
|
||||
return err != FR_OK;
|
||||
}
|
||||
// TODO: Return Result<>.
|
||||
Optional<Error> open(const std::string& filename);
|
||||
Optional<Error> append(const std::string& filename);
|
||||
Optional<Error> create(const std::string& filename);
|
||||
|
||||
std::filesystem::filesystem_error error() const {
|
||||
return { err };
|
||||
}
|
||||
Result<size_t> read(void* const data, const size_t bytes_to_read);
|
||||
Result<size_t> write(const void* const data, const size_t bytes_to_write);
|
||||
|
||||
bool read(void* const data, const size_t bytes_to_read);
|
||||
bool write(const void* const data, const size_t bytes_to_write);
|
||||
|
||||
uint64_t seek(const uint64_t new_position);
|
||||
Result<uint64_t> seek(const uint64_t new_position);
|
||||
|
||||
template<size_t N>
|
||||
bool write(const std::array<uint8_t, N>& data) {
|
||||
Result<size_t> write(const std::array<uint8_t, N>& data) {
|
||||
return write(data.data(), N);
|
||||
}
|
||||
|
||||
bool puts(const std::string& string);
|
||||
Result<size_t> puts(const std::string& string);
|
||||
|
||||
bool sync();
|
||||
// TODO: Return Result<>.
|
||||
Optional<Error> sync();
|
||||
|
||||
private:
|
||||
FIL f;
|
||||
uint32_t err;
|
||||
|
||||
Optional<Error> open_fatfs(const std::string& filename, BYTE mode);
|
||||
};
|
||||
|
||||
inline constexpr File::openmode operator|(File::openmode a, File::openmode b) {
|
||||
return File::openmode(static_cast<int>(a) | static_cast<int>(b));
|
||||
}
|
||||
|
||||
#endif/*__FILE_H__*/
|
||||
|
@ -23,21 +23,15 @@
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
LogFile::LogFile(
|
||||
const std::string& file_path
|
||||
) : file { file_path, File::openmode::out | File::openmode::ate }
|
||||
{
|
||||
}
|
||||
|
||||
bool LogFile::is_open() const {
|
||||
return file.is_open();
|
||||
}
|
||||
|
||||
bool LogFile::write_entry(const rtc::RTC& datetime, const std::string& entry) {
|
||||
File::Result<size_t> LogFile::write_entry(const rtc::RTC& datetime, const std::string& entry) {
|
||||
std::string timestamp = to_string_timestamp(datetime);
|
||||
return write(timestamp + " " + entry + "\r\n");
|
||||
}
|
||||
|
||||
bool LogFile::write(const std::string& message) {
|
||||
return file.puts(message) && file.sync();
|
||||
File::Result<size_t> LogFile::write(const std::string& message) {
|
||||
auto puts_result = file.puts(message);
|
||||
if( puts_result.is_ok() ) {
|
||||
file.sync();
|
||||
}
|
||||
return puts_result;
|
||||
}
|
||||
|
@ -31,16 +31,16 @@ using namespace lpc43xx;
|
||||
|
||||
class LogFile {
|
||||
public:
|
||||
LogFile(const std::string& file_path);
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
return file.append(filename);
|
||||
}
|
||||
|
||||
bool is_open() const;
|
||||
|
||||
bool write_entry(const rtc::RTC& datetime, const std::string& entry);
|
||||
File::Result<size_t> write_entry(const rtc::RTC& datetime, const std::string& entry);
|
||||
|
||||
private:
|
||||
File file;
|
||||
|
||||
bool write(const std::string& message);
|
||||
File::Result<size_t> write(const std::string& message);
|
||||
};
|
||||
|
||||
#endif/*__LOG_FILE_H__*/
|
||||
|
@ -51,22 +51,14 @@ std::string temperature(Temperature temperature) {
|
||||
|
||||
} /* namespace tpms */
|
||||
|
||||
TPMSLogger::TPMSLogger(
|
||||
const std::string& file_path
|
||||
) : log_file { file_path }
|
||||
{
|
||||
}
|
||||
|
||||
void TPMSLogger::on_packet(const tpms::Packet& packet, const uint32_t target_frequency) {
|
||||
const auto hex_formatted = packet.symbols_formatted();
|
||||
|
||||
if( log_file.is_open() ) {
|
||||
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
|
||||
const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10);
|
||||
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
|
||||
const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10);
|
||||
|
||||
std::string entry = tuning_frequency_str + " FSK 38.4 19.2 " + hex_formatted.data + "/" + hex_formatted.errors;
|
||||
log_file.write_entry(packet.received_at(), entry);
|
||||
}
|
||||
std::string entry = tuning_frequency_str + " FSK 38.4 19.2 " + hex_formatted.data + "/" + hex_formatted.errors;
|
||||
log_file.write_entry(packet.received_at(), entry);
|
||||
}
|
||||
|
||||
const TPMSRecentEntry::Key TPMSRecentEntry::invalid_key = { tpms::Reading::Type::None, 0 };
|
||||
@ -165,7 +157,10 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
|
||||
logger = std::make_unique<TPMSLogger>("tpms.txt");
|
||||
logger = std::make_unique<TPMSLogger>();
|
||||
if( logger ) {
|
||||
logger->append("tpms.txt");
|
||||
}
|
||||
}
|
||||
|
||||
TPMSAppView::~TPMSAppView() {
|
||||
|
@ -72,7 +72,9 @@ using TPMSRecentEntries = RecentEntries<tpms::Reading, TPMSRecentEntry>;
|
||||
|
||||
class TPMSLogger {
|
||||
public:
|
||||
TPMSLogger(const std::string& file_path);
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
void on_packet(const tpms::Packet& packet, const uint32_t target_frequency);
|
||||
|
||||
|
@ -86,7 +86,11 @@ void SystemStatusView::on_camera() {
|
||||
return;
|
||||
}
|
||||
|
||||
PNGWriter png { filename_stem + ".PNG" };
|
||||
PNGWriter png;
|
||||
auto create_error = png.create(filename_stem + ".PNG");
|
||||
if( create_error.is_valid() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i=0; i<320; i++) {
|
||||
std::array<ColorRGB888, 240> row;
|
||||
|
@ -34,26 +34,23 @@ using namespace portapack;
|
||||
|
||||
class FileWriter : public Writer {
|
||||
public:
|
||||
FileWriter(
|
||||
const std::string& filename
|
||||
) : file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc }
|
||||
{
|
||||
FileWriter() = default;
|
||||
|
||||
FileWriter(const FileWriter&) = delete;
|
||||
FileWriter& operator=(const FileWriter&) = delete;
|
||||
FileWriter(FileWriter&& file) = delete;
|
||||
FileWriter& operator=(FileWriter&&) = delete;
|
||||
|
||||
Optional<File::Error> create(const std::string& filename) {
|
||||
return file.create(filename);
|
||||
}
|
||||
|
||||
Optional<std::filesystem::filesystem_error> error() const override {
|
||||
if( file.bad() ) {
|
||||
return { file.error() };
|
||||
} else {
|
||||
return { };
|
||||
File::Result<size_t> write(const void* const buffer, const size_t bytes) override {
|
||||
auto write_result = file.write(buffer, bytes) ;
|
||||
if( write_result.is_ok() ) {
|
||||
bytes_written += write_result.value();
|
||||
}
|
||||
}
|
||||
|
||||
bool write(const void* const buffer, const size_t bytes) override {
|
||||
const auto success = file.write(buffer, bytes) ;
|
||||
if( success ) {
|
||||
bytes_written += bytes;
|
||||
}
|
||||
return success;
|
||||
return write_result;
|
||||
}
|
||||
|
||||
protected:
|
||||
@ -66,18 +63,33 @@ using RawFileWriter = FileWriter;
|
||||
class WAVFileWriter : public FileWriter {
|
||||
public:
|
||||
WAVFileWriter(
|
||||
const std::string& filename,
|
||||
size_t sampling_rate
|
||||
) : RawFileWriter { filename },
|
||||
header { sampling_rate }
|
||||
) : header { sampling_rate }
|
||||
{
|
||||
update_header();
|
||||
}
|
||||
|
||||
|
||||
WAVFileWriter(const WAVFileWriter&) = delete;
|
||||
WAVFileWriter& operator=(const WAVFileWriter&) = delete;
|
||||
WAVFileWriter(WAVFileWriter&&) = delete;
|
||||
WAVFileWriter& operator=(WAVFileWriter&&) = delete;
|
||||
|
||||
~WAVFileWriter() {
|
||||
update_header();
|
||||
}
|
||||
|
||||
Optional<File::Error> create(
|
||||
const std::string& filename
|
||||
) {
|
||||
const auto create_error = FileWriter::create(filename);
|
||||
if( create_error.is_valid() ) {
|
||||
return create_error;
|
||||
} else {
|
||||
update_header();
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
struct fmt_pcm_t {
|
||||
constexpr fmt_pcm_t(
|
||||
@ -132,7 +144,11 @@ private:
|
||||
|
||||
void update_header() {
|
||||
header.set_data_size(bytes_written);
|
||||
const auto old_position = file.seek(0);
|
||||
auto seek_result = file.seek(0);
|
||||
if( seek_result.is_error() ) {
|
||||
return;
|
||||
}
|
||||
const auto old_position = seek_result.value();
|
||||
file.write(&header, sizeof(header));
|
||||
file.seek(old_position);
|
||||
}
|
||||
@ -206,17 +222,39 @@ void RecordView::start() {
|
||||
std::unique_ptr<Writer> writer;
|
||||
switch(file_type) {
|
||||
case FileType::WAV:
|
||||
writer = std::make_unique<WAVFileWriter>(
|
||||
filename_stem + ".WAV",
|
||||
sampling_rate
|
||||
);
|
||||
{
|
||||
auto p = std::make_unique<WAVFileWriter>(
|
||||
sampling_rate
|
||||
);
|
||||
auto create_error = p->create(
|
||||
filename_stem + ".WAV"
|
||||
);
|
||||
if( create_error.is_valid() ) {
|
||||
report_error(create_error.value().what());
|
||||
} else {
|
||||
writer = std::move(p);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case FileType::RawS16:
|
||||
write_metadata_file(filename_stem + ".TXT");
|
||||
writer = std::make_unique<RawFileWriter>(
|
||||
filename_stem + ".C16"
|
||||
);
|
||||
{
|
||||
const auto metadata_file_error = write_metadata_file(filename_stem + ".TXT");
|
||||
if( metadata_file_error.is_valid() ) {
|
||||
report_error(metadata_file_error.value().what());
|
||||
return;
|
||||
}
|
||||
|
||||
auto p = std::make_unique<RawFileWriter>();
|
||||
auto create_error = p->create(
|
||||
filename_stem + ".C16"
|
||||
);
|
||||
if( create_error.is_valid() ) {
|
||||
report_error(create_error.value().what());
|
||||
} else {
|
||||
writer = std::move(p);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -224,19 +262,12 @@ void RecordView::start() {
|
||||
};
|
||||
|
||||
if( writer ) {
|
||||
const auto error = writer->error();
|
||||
if( error.is_valid() ) {
|
||||
report_error(error.value().what());
|
||||
} else {
|
||||
text_record_filename.set(filename_stem);
|
||||
button_record.set_bitmap(&bitmap_stop);
|
||||
capture_thread = std::make_unique<CaptureThread>(
|
||||
std::move(writer),
|
||||
write_size, buffer_count
|
||||
);
|
||||
}
|
||||
} else {
|
||||
report_error("file type");
|
||||
text_record_filename.set(filename_stem);
|
||||
button_record.set_bitmap(&bitmap_stop);
|
||||
capture_thread = std::make_unique<CaptureThread>(
|
||||
std::move(writer),
|
||||
write_size, buffer_count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,10 +278,22 @@ void RecordView::stop() {
|
||||
}
|
||||
}
|
||||
|
||||
void RecordView::write_metadata_file(const std::string& filename) {
|
||||
File file { filename, File::openmode::out | File::openmode::trunc };
|
||||
file.puts("sample_rate=" + to_string_dec_uint(sampling_rate) + "\n");
|
||||
file.puts("center_frequency=" + to_string_dec_uint(receiver_model.tuning_frequency()) + "\n");
|
||||
Optional<File::Error> RecordView::write_metadata_file(const std::string& filename) {
|
||||
File file;
|
||||
const auto create_error = file.create(filename);
|
||||
if( create_error.is_valid() ) {
|
||||
return create_error;
|
||||
} else {
|
||||
const auto puts_result1 = file.puts("sample_rate=" + to_string_dec_uint(sampling_rate) + "\n");
|
||||
if( puts_result1.is_error() ) {
|
||||
return { puts_result1.error() };
|
||||
}
|
||||
const auto puts_result2 = file.puts("center_frequency=" + to_string_dec_uint(receiver_model.tuning_frequency()) + "\n");
|
||||
if( puts_result2.is_error() ) {
|
||||
return { puts_result2.error() };
|
||||
}
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
void RecordView::on_tick_second() {
|
||||
@ -258,7 +301,7 @@ void RecordView::on_tick_second() {
|
||||
const auto error = capture_thread->error();
|
||||
if( error.is_valid() ) {
|
||||
stop();
|
||||
report_error(error.value());
|
||||
report_error(error.value().what());
|
||||
}
|
||||
const auto dropped_percent = std::min(99U, capture_thread->state().dropped_percent());
|
||||
const auto s = to_string_dec_uint(dropped_percent, 2, ' ') + "\%";
|
||||
|
@ -69,7 +69,7 @@ public:
|
||||
|
||||
private:
|
||||
void toggle();
|
||||
void write_metadata_file(const std::string& filename);
|
||||
Optional<File::Error> write_metadata_file(const std::string& filename);
|
||||
|
||||
void on_tick_second();
|
||||
|
||||
|
@ -134,22 +134,24 @@ private:
|
||||
return Result::FailHeap;
|
||||
}
|
||||
|
||||
File file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc };
|
||||
if( !file.is_open() ) {
|
||||
File file;
|
||||
auto file_create_error = file.create(filename);
|
||||
if( file_create_error.is_valid() ) {
|
||||
return Result::FailFileOpenWrite;
|
||||
}
|
||||
|
||||
lfsr_word_t v = 1;
|
||||
|
||||
const halrtcnt_t test_start = halGetCounterValue();
|
||||
while( !chThdShouldTerminate() && file.is_open() && (_stats.write_bytes < bytes_to_write) ) {
|
||||
while( !chThdShouldTerminate() && (_stats.write_bytes < bytes_to_write) ) {
|
||||
lfsr_fill(v,
|
||||
reinterpret_cast<lfsr_word_t*>(buffer->data()),
|
||||
sizeof(*buffer.get()) / sizeof(lfsr_word_t)
|
||||
);
|
||||
|
||||
const halrtcnt_t write_start = halGetCounterValue();
|
||||
if( !file.write(buffer->data(), buffer->size()) ) {
|
||||
const auto result_write = file.write(buffer->data(), buffer->size());
|
||||
if( result_write.is_error() ) {
|
||||
break;
|
||||
}
|
||||
const halrtcnt_t write_end = halGetCounterValue();
|
||||
@ -179,17 +181,19 @@ private:
|
||||
return Result::FailHeap;
|
||||
}
|
||||
|
||||
File file { filename, File::openmode::in | File::openmode::binary };
|
||||
if( !file.is_open() ) {
|
||||
File file;
|
||||
auto file_open_error = file.open(filename);
|
||||
if( file_open_error.is_valid() ) {
|
||||
return Result::FailFileOpenRead;
|
||||
}
|
||||
|
||||
lfsr_word_t v = 1;
|
||||
|
||||
const halrtcnt_t test_start = halGetCounterValue();
|
||||
while( !chThdShouldTerminate() && file.is_open() && (_stats.read_bytes < bytes_to_read) ) {
|
||||
while( !chThdShouldTerminate() && (_stats.read_bytes < bytes_to_read) ) {
|
||||
const halrtcnt_t read_start = halGetCounterValue();
|
||||
if( !file.read(buffer->data(), buffer->size()) ) {
|
||||
const auto result_read = file.read(buffer->data(), buffer->size());
|
||||
if( result_read.is_error() ) {
|
||||
break;
|
||||
}
|
||||
const halrtcnt_t read_end = halGetCounterValue();
|
||||
|
@ -49,10 +49,14 @@ static constexpr std::array<uint8_t, 12> png_iend { {
|
||||
0xae, 0x42, 0x60, 0x82, // CRC
|
||||
} };
|
||||
|
||||
PNGWriter::PNGWriter(
|
||||
Optional<File::Error> PNGWriter::create(
|
||||
const std::string& filename
|
||||
) : file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc }
|
||||
{
|
||||
) {
|
||||
const auto create_error = file.create(filename);
|
||||
if( create_error.is_valid() ) {
|
||||
return create_error;
|
||||
}
|
||||
|
||||
file.write(png_file_header);
|
||||
file.write(png_ihdr_screen_capture);
|
||||
|
||||
@ -63,6 +67,8 @@ PNGWriter::PNGWriter(
|
||||
|
||||
constexpr std::array<uint8_t, 2> zlib_header { 0x78, 0x01 }; // Zlib CM, CINFO, FLG.
|
||||
write_chunk_content(zlib_header);
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
PNGWriter::~PNGWriter() {
|
||||
|
@ -33,9 +33,10 @@
|
||||
|
||||
class PNGWriter {
|
||||
public:
|
||||
explicit PNGWriter(const std::string& filename);
|
||||
~PNGWriter();
|
||||
|
||||
Optional<File::Error> create(const std::string& filename);
|
||||
|
||||
void write_scanline(const std::array<ui::ColorRGB888, 240>& scanline);
|
||||
|
||||
private:
|
||||
|
Loading…
Reference in New Issue
Block a user