[FL-2776] IR CLI Decode Command (#1692)

* Add decode option to Infrared app
* Implement saving results to file
* Refactor code
* Correct formatting
* Refactor code
* Better command line arguments handling
* Accept generic IR files
* Remove unneeded define

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Georgii Surkov 2022-09-03 13:46:07 +03:00 committed by GitHub
parent bd54c2b342
commit ed2c607dd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -3,6 +3,8 @@
#include <infrared.h> #include <infrared.h>
#include <infrared_worker.h> #include <infrared_worker.h>
#include <furi_hal_infrared.h> #include <furi_hal_infrared.h>
#include <flipper_format.h>
#include <toolbox/args.h>
#include "infrared_signal.h" #include "infrared_signal.h"
@ -10,6 +12,7 @@
static void infrared_cli_start_ir_rx(Cli* cli, string_t args); static void infrared_cli_start_ir_rx(Cli* cli, string_t args);
static void infrared_cli_start_ir_tx(Cli* cli, string_t args); static void infrared_cli_start_ir_tx(Cli* cli, string_t args);
static void infrared_cli_process_decode(Cli* cli, string_t args);
static const struct { static const struct {
const char* cmd; const char* cmd;
@ -17,6 +20,7 @@ static const struct {
} infrared_cli_commands[] = { } infrared_cli_commands[] = {
{.cmd = "rx", .process_function = infrared_cli_start_ir_rx}, {.cmd = "rx", .process_function = infrared_cli_start_ir_rx},
{.cmd = "tx", .process_function = infrared_cli_start_ir_tx}, {.cmd = "tx", .process_function = infrared_cli_start_ir_tx},
{.cmd = "decode", .process_function = infrared_cli_process_decode},
}; };
static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) { static void signal_received_callback(void* context, InfraredWorkerSignal* received_signal) {
@ -86,6 +90,7 @@ static void infrared_cli_print_usage(void) {
"\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n", "\tFrequency (%d - %d), Duty cycle (0 - 100), max 512 samples\r\n",
INFRARED_MIN_FREQUENCY, INFRARED_MIN_FREQUENCY,
INFRARED_MAX_FREQUENCY); INFRARED_MAX_FREQUENCY);
printf("\tir decode <input_file> [<output_file>]\r\n");
} }
static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) { static bool infrared_cli_parse_message(const char* str, InfraredSignal* signal) {
@ -162,6 +167,160 @@ static void infrared_cli_start_ir_tx(Cli* cli, string_t args) {
infrared_signal_free(signal); infrared_signal_free(signal);
} }
static bool
infrared_cli_save_signal(InfraredSignal* signal, FlipperFormat* file, const char* name) {
bool ret = infrared_signal_save(signal, file, name);
if(!ret) {
printf("Failed to save signal: \"%s\"\r\n", name);
}
return ret;
}
static bool infrared_cli_decode_raw_signal(
InfraredRawSignal* raw_signal,
InfraredDecoderHandler* decoder,
FlipperFormat* output_file,
const char* signal_name) {
InfraredSignal* signal = infrared_signal_alloc();
bool ret = false, level = true, is_decoded = false;
size_t i;
for(i = 0; i < raw_signal->timings_size; ++i) {
// TODO: Any infrared_check_decoder_ready() magic?
const InfraredMessage* message = infrared_decode(decoder, level, raw_signal->timings[i]);
if(message) {
is_decoded = true;
printf(
"Protocol: %s address: 0x%lX command: 0x%lX %s\r\n",
infrared_get_protocol_name(message->protocol),
message->address,
message->command,
(message->repeat ? "R" : ""));
if(output_file && !message->repeat) {
infrared_signal_set_message(signal, message);
if(!infrared_cli_save_signal(signal, output_file, signal_name)) break;
}
}
level = !level;
}
if(i == raw_signal->timings_size) {
if(!is_decoded && output_file) {
infrared_signal_set_raw_signal(
signal,
raw_signal->timings,
raw_signal->timings_size,
raw_signal->frequency,
raw_signal->duty_cycle);
ret = infrared_cli_save_signal(signal, output_file, signal_name);
} else {
ret = true;
}
}
infrared_reset_decoder(decoder);
infrared_signal_free(signal);
return ret;
}
static bool infrared_cli_decode_file(FlipperFormat* input_file, FlipperFormat* output_file) {
bool ret = false;
InfraredSignal* signal = infrared_signal_alloc();
InfraredDecoderHandler* decoder = infrared_alloc_decoder();
string_t tmp;
string_init(tmp);
while(infrared_signal_read(signal, input_file, tmp)) {
ret = false;
if(!infrared_signal_is_valid(signal)) {
printf("Invalid signal\r\n");
break;
}
if(!infrared_signal_is_raw(signal)) {
if(output_file &&
!infrared_cli_save_signal(signal, output_file, string_get_cstr(tmp))) {
break;
} else {
printf("Skipping decoded signal\r\n");
continue;
}
}
InfraredRawSignal* raw_signal = infrared_signal_get_raw_signal(signal);
printf("Raw signal: %s, %u samples\r\n", string_get_cstr(tmp), raw_signal->timings_size);
if(!infrared_cli_decode_raw_signal(raw_signal, decoder, output_file, string_get_cstr(tmp)))
break;
ret = true;
}
infrared_free_decoder(decoder);
infrared_signal_free(signal);
string_clear(tmp);
return ret;
}
static void infrared_cli_process_decode(Cli* cli, string_t args) {
UNUSED(cli);
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* input_file = flipper_format_buffered_file_alloc(storage);
FlipperFormat* output_file = NULL;
uint32_t version;
string_t tmp, header, input_path, output_path;
string_init(tmp);
string_init(header);
string_init(input_path);
string_init(output_path);
do {
if(!args_read_probably_quoted_string_and_trim(args, input_path)) {
printf("Wrong arguments.\r\n");
infrared_cli_print_usage();
break;
}
args_read_probably_quoted_string_and_trim(args, output_path);
if(!flipper_format_buffered_file_open_existing(input_file, string_get_cstr(input_path))) {
printf("Failed to open file for reading: \"%s\"\r\n", string_get_cstr(input_path));
break;
}
if(!flipper_format_read_header(input_file, header, &version) ||
(!string_start_with_str_p(header, "IR")) || version != 1) {
printf("Invalid or corrupted input file: \"%s\"\r\n", string_get_cstr(input_path));
break;
}
if(!string_empty_p(output_path)) {
printf("Writing output to file: \"%s\"\r\n", string_get_cstr(output_path));
output_file = flipper_format_file_alloc(storage);
}
if(output_file &&
!flipper_format_file_open_always(output_file, string_get_cstr(output_path))) {
printf("Failed to open file for writing: \"%s\"\r\n", string_get_cstr(output_path));
break;
}
if(output_file && !flipper_format_write_header(output_file, header, version)) {
printf("Failed to write to the output file: \"%s\"\r\n", string_get_cstr(output_path));
break;
}
if(!infrared_cli_decode_file(input_file, output_file)) {
break;
}
printf("File successfully decoded.\r\n");
} while(false);
string_clear(tmp);
string_clear(header);
string_clear(input_path);
string_clear(output_path);
flipper_format_free(input_file);
if(output_file) flipper_format_free(output_file);
furi_record_close(RECORD_STORAGE);
}
static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) { static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
UNUSED(context); UNUSED(context);
if(furi_hal_infrared_is_busy()) { if(furi_hal_infrared_is_busy()) {
@ -169,18 +328,15 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
return; return;
} }
string_t command;
string_init(command);
args_read_string_and_trim(args, command);
size_t i = 0; size_t i = 0;
for(; i < COUNT_OF(infrared_cli_commands); ++i) { for(; i < COUNT_OF(infrared_cli_commands); ++i) {
size_t size = strlen(infrared_cli_commands[i].cmd); size_t cmd_len = strlen(infrared_cli_commands[i].cmd);
bool cmd_found = !strncmp(string_get_cstr(args), infrared_cli_commands[i].cmd, size); if(!strncmp(string_get_cstr(command), infrared_cli_commands[i].cmd, cmd_len)) {
if(cmd_found) { break;
if(string_size(args) == size) {
break;
}
if(string_get_cstr(args)[size] == ' ') {
string_right(args, size + 1);
break;
}
} }
} }
@ -189,6 +345,8 @@ static void infrared_cli_start_ir(Cli* cli, string_t args, void* context) {
} else { } else {
infrared_cli_print_usage(); infrared_cli_print_usage();
} }
string_clear(command);
} }
void infrared_on_system_start() { void infrared_on_system_start() {
#ifdef SRV_CLI #ifdef SRV_CLI