Add split RAW files ability

This commit is contained in:
DerSkythe 2022-10-10 20:20:56 +04:00
parent 016ebd3afc
commit 52361b4adf
2 changed files with 491 additions and 15 deletions

View File

@ -1,7 +1,10 @@
#include "subghz_history.h" #include "subghz_history.h"
#include "subghz_history_private.h" #include "subghz_history_private.h"
#include <lib/subghz/receiver.h> #include <lib/subghz/receiver.h>
#include <toolbox/path.h>
#include <flipper_format/flipper_format_i.h> #include <flipper_format/flipper_format_i.h>
#include "flipper_format_stream_i.h"
#include <inttypes.h>
#define SUBGHZ_HISTORY_MAX 60 #define SUBGHZ_HISTORY_MAX 60
@ -11,10 +14,12 @@
*/ */
#define SUBGHZ_HISTORY_TMP_DIR EXT_PATH("subghz/tmp_history") #define SUBGHZ_HISTORY_TMP_DIR EXT_PATH("subghz/tmp_history")
#define SUBGHZ_HISTORY_TMP_EXTENSION ".tmp" #define SUBGHZ_HISTORY_TMP_EXTENSION ".tmp"
#define SUBGHZ_HISTORY_TMP_SIGNAL_MAX_LEVEL_DURATION 700 #define SUBGHZ_HISTORY_TMP_SIGNAL_MAX 700
#define SUBGHZ_HISTORY_TMP_SIGNAL_MIN_LEVEL_DURATION 100 #define SUBGHZ_HISTORY_TMP_SIGNAL_MIN 100
#define SUBGHZ_HISTORY_TMP_REMOVE_FILES true #define SUBGHZ_HISTORY_TMP_REMOVE_FILES true
#define SUBGHZ_HISTORY_TMP_RAW_KEY "RAW_Data" #define SUBGHZ_HISTORY_TMP_RAW_KEY "RAW_Data"
#define MAX_LINE 500
const size_t buffer_size = 32;
#define TAG "SubGhzHistory" #define TAG "SubGhzHistory"
@ -436,7 +441,7 @@ bool subghz_history_add_to_history(
#ifdef FURI_DEBUG #ifdef FURI_DEBUG
FURI_LOG_I(TAG, "Save temp file: %s", furi_string_get_cstr(dir_path)); FURI_LOG_I(TAG, "Save temp file: %s", furi_string_get_cstr(dir_path));
#endif #endif
if(!subghz_history_tmp_write_file_split(instance, item, dir_path)) { if(!subghz_history_tmp_write_file_split(instance, item, furi_string_get_cstr(dir_path))) {
// Plan B! // Plan B!
subghz_history_tmp_write_file_full(instance, item, dir_path); subghz_history_tmp_write_file_full(instance, item, dir_path);
} }
@ -455,19 +460,405 @@ bool subghz_history_add_to_history(
return true; return true;
} }
static inline bool is_space_playground(char c) {
return c == ' ' || c == '\t' || c == flipper_format_eolr;
}
bool subghz_history_stream_read_valid_key(Stream* stream, FuriString* key) {
furi_string_reset(key);
uint8_t buffer[buffer_size];
bool found = false;
bool error = false;
bool accumulate = true;
bool new_line = true;
while(true) {
size_t was_read = stream_read(stream, buffer, buffer_size);
if(was_read == 0) break;
for(size_t i = 0; i < was_read; i++) {
uint8_t data = buffer[i];
if(data == flipper_format_eoln) {
// EOL found, clean data, start accumulating data and set the new_line flag
furi_string_reset(key);
accumulate = true;
new_line = true;
} else if(data == flipper_format_eolr) {
// ignore
} else if(data == flipper_format_comment && new_line) {
// if there is a comment character and we are at the beginning of a new line
// do not accumulate comment data and reset the new_line flag
accumulate = false;
new_line = false;
} else if(data == flipper_format_delimiter) {
if(new_line) {
// we are on a "new line" and found the delimiter
// this can only be if we have previously found some kind of key, so
// clear the data, set the flag that we no longer want to accumulate data
// and reset the new_line flag
furi_string_reset(key);
accumulate = false;
new_line = false;
} else {
// parse the delimiter only if we are accumulating data
if(accumulate) {
// we found the delimiter, move the rw pointer to the delimiter location
// and signal that we have found something
if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {
error = true;
break;
}
found = true;
break;
}
}
} else {
// just new symbol, reset the new_line flag
new_line = false;
if(accumulate) {
// and accumulate data if we want
furi_string_push_back(key, data);
}
}
}
if(found || error) break;
}
return found;
}
bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) {
bool found = false;
FuriString* read_key;
read_key = furi_string_alloc();
while(!stream_eof(stream)) {
if(subghz_history_stream_read_valid_key(stream, read_key)) {
if(furi_string_cmp_str(read_key, key) == 0) {
if(!stream_seek(stream, 2, StreamOffsetFromCurrent)) {
break;
}
found = true;
break;
} else if(strict_mode) {
found = false;
break;
}
}
}
furi_string_free(read_key);
return found;
}
bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last) {
enum { LeadingSpace, ReadValue, TrailingSpace } state = LeadingSpace;
const size_t buffer_size = 32;
uint8_t buffer[buffer_size];
bool result = false;
bool error = false;
furi_string_reset(value);
while(true) {
size_t was_read = stream_read(stream, buffer, buffer_size);
if(was_read == 0) {
if(state != LeadingSpace && stream_eof(stream)) {
result = true;
*last = true;
} else {
error = true;
}
}
for(uint16_t i = 0; i < was_read; i++) {
const uint8_t data = buffer[i];
if(state == LeadingSpace) {
if(is_space_playground(data)) {
continue;
} else if(data == flipper_format_eoln) {
stream_seek(stream, i - was_read, StreamOffsetFromCurrent);
error = true;
break;
} else {
state = ReadValue;
furi_string_push_back(value, data);
}
} else if(state == ReadValue) {
if(is_space_playground(data)) {
state = TrailingSpace;
} else if(data == flipper_format_eoln) {
if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {
error = true;
} else {
result = true;
*last = true;
}
break;
} else {
furi_string_push_back(value, data);
}
} else if(state == TrailingSpace) {
if(is_space_playground(data)) {
continue;
} else if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) {
error = true;
} else {
*last = (data == flipper_format_eoln);
result = true;
}
break;
}
}
if(error || result) break;
}
return result;
}
bool subghz_history_read_int32(Stream* stream, int32_t* _data, const uint16_t data_size) {
bool result = false;
result = true;
FuriString* value;
value = furi_string_alloc();
for(size_t i = 0; i < data_size; i++) {
bool last = false;
result = subghz_history_stream_read_value(stream, value, &last);
if(result) {
int scan_values = 0;
int32_t* data = _data;
scan_values = sscanf(furi_string_get_cstr(value), "%" PRIi32, &data[i]);
if(scan_values != 1) {
result = false;
break;
}
} else {
break;
}
if(last && ((i + 1) != data_size)) {
result = false;
break;
}
}
furi_string_free(value);
return result;
}
uint32_t subghz_history_rand_range(uint32_t min, uint32_t max) {
// size of range, inclusive
const uint32_t length_of_range = max - min + 1;
// add n so that we don't return a number below our range
return (uint32_t)(rand() % length_of_range + min);
}
bool subghz_history_write_file_noise(
Stream* file,
bool is_negative_start,
size_t current_position,
bool empty_line) {
size_t was_write = 0;
if(empty_line) {
was_write = stream_write_format(file, "%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY);
if(was_write <= 0) {
FURI_LOG_E(TAG, "Can't write key!");
return false;
}
}
int8_t first;
int8_t second;
if(is_negative_start) {
first = -1;
second = 1;
} else {
first = 1;
second = -1;
}
while(current_position < MAX_LINE) {
was_write = stream_write_format(
file,
"%ld %ld ",
subghz_history_rand_range(
SUBGHZ_HISTORY_TMP_SIGNAL_MIN, SUBGHZ_HISTORY_TMP_SIGNAL_MAX) *
first,
subghz_history_rand_range(
SUBGHZ_HISTORY_TMP_SIGNAL_MIN, SUBGHZ_HISTORY_TMP_SIGNAL_MAX) *
second);
if(was_write <= 0) {
FURI_LOG_E(TAG, "Can't write random values!");
return false;
}
current_position += was_write;
}
// Step back to write \n instead of space
size_t offset = stream_tell(file);
if(stream_seek(file, offset - 1, StreamOffsetFromCurrent)) {
FURI_LOG_E(TAG, "Step back failed!");
return false;
}
return stream_write_char(file, flipper_format_eoln) > 0;
}
bool subghz_history_write_file_data(
Stream* src,
Stream* file,
bool* is_negative_start,
size_t* current_position) {
size_t offset_file = 0;
bool result = false;
int32_t value = 0;
do {
if(!subghz_history_read_int32(src, &value, 1)) {
result = true;
break;
}
offset_file = stream_tell(file);
stream_write_format(file, "%ld ", value);
*current_position += stream_tell(file) - offset_file;
if(*current_position > MAX_LINE) {
if((is_negative_start && value > 0) || (!is_negative_start && value < 0)) {
// Align values
continue;
}
if(stream_write_format(file, "\n%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY) == 0) {
FURI_LOG_E(TAG, "Can't write new line!");
result = false;
break;
}
*current_position = 0;
}
} while(true);
*is_negative_start = value < 0;
return result;
}
bool subghz_history_tmp_write_file_split( bool subghz_history_tmp_write_file_split(
SubGhzHistory* instance, SubGhzHistory* instance,
void* current_item, void* current_item,
FuriString* dir_path) { const char* dir_path) {
UNUSED(instance); furi_assert(instance);
UNUSED(current_item);
UNUSED(dir_path);
/*furi_assert(instance);
furi_assert(current_item); furi_assert(current_item);
furi_assert(dir_path);*/ furi_assert(dir_path);
//SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item; #ifdef FURI_DEBUG
FURI_LOG_I(TAG, "Save temp file splitted: %s", dir_path);
#endif
SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item;
return false; uint8_t buffer[buffer_size];
Stream* src = flipper_format_get_raw_stream(item->flipper_string);
stream_rewind(src);
FlipperFormat* flipper_format_file = flipper_format_file_alloc(instance->storage);
bool result = false;
FuriString* temp_str = furi_string_alloc();
do {
if(storage_file_exists(instance->storage, dir_path) &&
storage_common_remove(instance->storage, dir_path) != FSE_OK) {
FURI_LOG_E(TAG, "Can't delete old file!");
break;
}
path_extract_dirname(dir_path, temp_str);
FS_Error fs_result =
storage_common_mkdir(instance->storage, furi_string_get_cstr(temp_str));
if(fs_result != FSE_OK && fs_result != FSE_EXIST) {
FURI_LOG_E(TAG, "Can't create dir!");
break;
}
result = flipper_format_file_open_always(flipper_format_file, dir_path);
if(!result) {
FURI_LOG_E(TAG, "Can't open file for write!");
break;
}
Stream* file = flipper_format_get_raw_stream(flipper_format_file);
if(!subghz_history_stream_seek_to_key(src, SUBGHZ_HISTORY_TMP_RAW_KEY, false)) {
FURI_LOG_E(TAG, "Can't find key!");
break;
}
bool is_negative_start = false;
bool found = false;
size_t offset_start;
offset_start = stream_tell(src);
// Check for negative value at the start and end to align file by correct values
size_t was_read = stream_read(src, buffer, 1);
if(was_read <= 0) {
FURI_LOG_E(TAG, "Can't obtain first mark!");
break;
}
is_negative_start = buffer[0] == '-';
// Ready to write stream to file
size_t current_position;
stream_rewind(src);
current_position = stream_copy(src, file, offset_start);
if(current_position != offset_start) {
FURI_LOG_E(TAG, "Invalid copy header data from one stream to another!");
break;
}
found = true;
current_position = 0;
if(!subghz_history_write_file_noise(file, is_negative_start, current_position, false)) {
FURI_LOG_E(TAG, "Add start noise failed!");
break;
}
if(stream_write_format(file, "%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY) == 0) {
FURI_LOG_E(TAG, "Can't write new line!");
result = false;
break;
}
if(!subghz_history_write_file_data(src, file, &is_negative_start, &current_position)) {
FURI_LOG_E(TAG, "Split by lines failed!");
break;
}
if(!subghz_history_write_file_noise(file, is_negative_start, current_position, false)) {
FURI_LOG_E(TAG, "Add end noise failed!");
break;
}
if(!subghz_history_write_file_noise(file, is_negative_start, 0, true)) {
FURI_LOG_E(TAG, "Add end noise failed!");
break;
}
result = found;
} while(false);
flipper_format_file_close(flipper_format_file);
flipper_format_free(flipper_format_file);
furi_string_free(temp_str);
return result;
} }
void subghz_history_tmp_write_file_full( void subghz_history_tmp_write_file_full(

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "subghz_history.h" #include "subghz_history.h"
#include <toolbox/stream/stream.h>
/** /**
* @brief Generate filename like 000.tmp * @brief Generate filename like 000.tmp
@ -52,7 +53,7 @@ void subghz_history_item_free(void* current_item);
void subghz_history_clean_item_array(SubGhzHistory* instance); void subghz_history_clean_item_array(SubGhzHistory* instance);
/** /**
* @brief Write temp file fully, without spliting * @brief Write temp file fully, without splitting
* *
* @param instance - SubGhzHistory* * @param instance - SubGhzHistory*
* @param current_item - SubGhzHistoryItem* * @param current_item - SubGhzHistoryItem*
@ -64,15 +65,99 @@ void subghz_history_tmp_write_file_full(
FuriString* dir_path); FuriString* dir_path);
/** /**
* @brief Write temp splited to lines * @brief Write temp spited to lines
* *
* @param instance - SubGhzHistory* * @param instance - SubGhzHistory*
* @param current_item - SubGhzHistoryItem* * @param current_item - SubGhzHistoryItem*
* @param dir_path - full path to file * @param dir_path - full path to file
* @return true - file saved * @return true - file saved
* @return false - error occured * @return false - error occurred
*/ */
bool subghz_history_tmp_write_file_split( bool subghz_history_tmp_write_file_split(
SubGhzHistory* instance, SubGhzHistory* instance,
void* current_item, void* current_item,
FuriString* dir_path); const char* dir_path);
/**
* @brief generate random value
*
* @param min - min value
* @param max - max value
* @return uint32_t
*/
uint32_t subghz_history_rand_range(uint32_t min, uint32_t max);
/**
* @brief write random noise signals to file applying to max line value
*
* @param file - Stream*
* @param is_negative_start - first value is negative or positive?
* @param current_position - 0 if started from begining
* @param empty_line - add RAW_Data to this line
* @return true
* @return false
*/
bool subghz_history_write_file_noise(
Stream* file,
bool is_negative_start,
size_t current_position,
bool empty_line);
/**
* @brief taken from flipper_format_stream_read_value_line but takes only one int32 value
*
* @param stream - Stream*
* @param _data - int32_t* output data
* @param data_size - size of data
* @return true
* @return false
*/
bool subghz_history_read_int32(Stream* stream, int32_t* _data, const uint16_t data_size);
/**
* @brief write payload to file spliting by lines
*
* @param src - Stream* of source
* @param file - Stream* of file
* @param is_negative_start - first value is negative or positive?
* @param current_position - by default is 0 but in this value returned last position of payload
* @return true
* @return false
*/
bool subghz_history_write_file_data(
Stream* src,
Stream* file,
bool* is_negative_start,
size_t* current_position);
/**
* @brief taken from flipper_format_stream_read_valid_key
*
* @param stream - Stream*
* @param key - FuriString* output value
* @return true
* @return false
*/
bool subghz_history_stream_read_valid_key(Stream* stream, FuriString* key);
/**
* @brief taken from flipper_format_stream_seek_to_key
*
* @param stream - Stream*
* @param key - key
* @param strict_mode - false
* @return true
* @return false
*/
bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode);
/**
* @brief taken from flipper_format_stream_read_value
*
* @param stream - Stream*
* @param value - FuriString* output value
* @param last - return position is last flag
* @return true
* @return false
*/
bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last);