From f4868604384908a7477cbb4b544c6ee7aac2a883 Mon Sep 17 00:00:00 2001 From: lonkaars Date: Mon, 27 May 2024 08:23:21 +0200 Subject: quickly implement some pseudo handlers for the remaining commands --- client/cmd.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'client/cmd.h') diff --git a/client/cmd.h b/client/cmd.h index 9d20328..30bcbeb 100644 --- a/client/cmd.h +++ b/client/cmd.h @@ -40,21 +40,21 @@ static const struct cmd cmds[] = { .name = "send", .info = "[debug] send raw message", }, - // { - // .handle = cmd_status, - // .name = "status", - // .info = "show global puzzle box state (main controller state)", - // }, - // { - // .handle = cmd_reset, - // .name = "reset", - // .info = "reset entire game state", - // }, - // { - // .handle = cmd_ls, - // .name = "ls", - // .info = "list connected puzzle modules", - // }, + { + .handle = cmd_status, + .name = "status", + .info = "show global puzzle box state (main controller state)", + }, + { + .handle = cmd_reset, + .name = "reset", + .info = "reset entire game state", + }, + { + .handle = cmd_ls, + .name = "ls", + .info = "list connected puzzle modules", + }, }; static const size_t cmds_length = sizeof(cmds) / sizeof(cmds[0]); -- cgit v1.2.3 From b6abd84b9930ab398f0402058e56a480e80799cc Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Tue, 28 May 2024 11:53:06 +0200 Subject: update readmes --- client/CMakeLists.txt | 3 +++ client/cmd.cpp | 1 + client/cmd.h | 14 ++++++++------ client/readme.md | 22 ++++++++++++++++++++++ readme.md | 39 +++++++++++++++++++++++++++------------ 5 files changed, 61 insertions(+), 18 deletions(-) (limited to 'client/cmd.h') diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 73c703d..57a2447 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -3,7 +3,10 @@ cmake_minimum_required(VERSION 3.29) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) + +# enable debug features set(CMAKE_BUILD_TYPE Debug) +add_compile_definitions(DEBUG) project(puzzlebox_client C CXX) diff --git a/client/cmd.cpp b/client/cmd.cpp index a26de13..ab101e9 100644 --- a/client/cmd.cpp +++ b/client/cmd.cpp @@ -91,3 +91,4 @@ void cmd_ls(char*) { }; i2c_send(BUSADDR_MAIN, msg, sizeof(msg)); } + diff --git a/client/cmd.h b/client/cmd.h index 30bcbeb..932f3a2 100644 --- a/client/cmd.h +++ b/client/cmd.h @@ -14,10 +14,10 @@ struct cmd { cmd_fn_t cmd_exit; cmd_fn_t cmd_test; cmd_fn_t cmd_help; -cmd_fn_t cmd_send; cmd_fn_t cmd_status; cmd_fn_t cmd_reset; cmd_fn_t cmd_ls; +cmd_fn_t cmd_send; static const struct cmd cmds[] = { { @@ -35,11 +35,6 @@ static const struct cmd cmds[] = { .name = "help", .info = "show this help", }, - { - .handle = cmd_send, - .name = "send", - .info = "[debug] send raw message", - }, { .handle = cmd_status, .name = "status", @@ -55,6 +50,13 @@ static const struct cmd cmds[] = { .name = "ls", .info = "list connected puzzle modules", }, +#ifdef DEBUG + { + .handle = cmd_send, + .name = "send", + .info = "[debug] send raw message", + }, +#endif }; static const size_t cmds_length = sizeof(cmds) / sizeof(cmds[0]); diff --git a/client/readme.md b/client/readme.md index 1b4cc34..ea3e034 100644 --- a/client/readme.md +++ b/client/readme.md @@ -1,5 +1,27 @@ # puzzle box client +This folder contains the source code for the puzzle box client (pbc). This is a +desktop application that communicates with the main controller over TCP to +send/receive I2C messages. This application is not only used by a +game operator to control and monitor the state of a puzzle box, but is also a +useful debugging tool when developing puzzle modules, as it allows you to send +arbitrary data over the puzzle bus. + +## Features + +- List detected puzzle modules +- Reset puzzle modules (individually or all to reset the box) +- Skip puzzle modules (individually or all) +- Request puzzle box state + +Debug only: +- Send arbitrary messages + +## Building + +PBC is a standard CMake project, but a [makefile](./makefile) is provided for +convenience (still requires CMake and Ninja are installed). + ## Send data ``` diff --git a/readme.md b/readme.md index 7802f5c..ac703b7 100644 --- a/readme.md +++ b/readme.md @@ -1,25 +1,34 @@ -# puzzle box +# Puzzle box -Avans University of Applied Sciences project puzzle box. +This repository contains the source code for the puzzle framework designed and +implemented during the 2023-2024 run of the Puzzlebox project. This year's run +of the project consists of only software students, and was developed using the +hardware from the 21-22 run of the project. -## tidyness +Improved hardware was designed but not realised during the 22-23 run of the +project. This hardware is recommended for future groups participating in the +project. The software in this repository should be easily portable to various +other microcontrollers, and a recommendation is made in the [design +document](docs/design.adoc). + +## Tidyness Please keep this repository tidy by being aware of the following conventions! -### folder structure +### Folder structure |folder|contains| |-|-| |`/client`|Desktop PC application for controlling the puzzle box |`/docs`|Project documentation in AsciiDoc(tor) format +|`/i2ctcp`|I2C over TCP protocol functions (used by main and client) |`/lib`|Libraries (tracked as [submodules](#submodules)) |`/main`|Main controller (RPi pico) software -|`/proto`|Puzzle bus TCP protocol functions (used by main and client) |`/puzzle/`|Puzzle sources, each puzzle has its own subdirectory -|`/shared`|Auxiliary shared code +|`/shared`|Shared code |`/test`|Unit test framework (currently unutilized) -### code style +### Code style An `.editorconfig` file is provided in this repository. Please install the [EditorConfig](https://editorconfig.org/) plugin for your text editor of choice @@ -28,7 +37,7 @@ to automatically use these. Currently, no linter/formatter is configured for maintaining consistent code style. -## submodules +## Submodules This repository tracks (most) dependencies via git submodules. @@ -40,6 +49,7 @@ git submodule update --init --recursive --depth 1 until your problems go away. + + +## ESP SDK setup -## ESP -1. Install ESP-IDF extension in vscode +1. Install ESP-IDF extension in Visual Studio Code 2. Install using 'express' option 3. Install ESP-IDF v5.2.1 (release version) -4. For windows: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/windows-setup.html#get-started-windows-first-steps -5. For Linux: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/linux-macos-setup.html#get-started-linux-macos-first-steps + + Additional help: + - [For windows](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/windows-setup.html#get-started-windows-first-steps) + - [For Linux](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/linux-macos-setup.html#get-started-linux-macos-first-steps) + -- cgit v1.2.3 From 81cbd31a1a6a0e521ab262492245ec109c25ec8b Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Wed, 29 May 2024 12:09:19 +0200 Subject: sort out completion functions --- client/cmd.cpp | 14 +++++++++++++- client/cmd.h | 33 +++++++++++++++++++++------------ client/parse.cpp | 9 +++------ client/pbc.1 | 0 client/rl.cpp | 25 ++++++++++++++++++++++++- client/rl.h | 2 +- 6 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 client/pbc.1 (limited to 'client/cmd.h') diff --git a/client/cmd.cpp b/client/cmd.cpp index ab101e9..2871daf 100644 --- a/client/cmd.cpp +++ b/client/cmd.cpp @@ -1,9 +1,11 @@ #include #include +#include #include #include "cmd.h" #include "i2ctcpv1.h" +#include "rl.h" #include "sock.h" #include "parse.h" @@ -35,7 +37,8 @@ void cmd_help(char*) { printf( "\n" - "You can also use the TAB key to autocomplete commands\n" + "See man pbc(1) for more info about specific commands\n" + "Hint: you can use the TAB key to autocomplete commands\n" ); } @@ -83,6 +86,15 @@ void cmd_reset(char*) { i2c_send(BUSADDR_MAIN, msg, sizeof(msg)); } +void cmd_skip(char*) { + const char msg[] = { + PB_CMD_WRITE, + 0x00, + PB_GS_SOLVED, + }; + i2c_send(BUSADDR_MAIN, msg, sizeof(msg)); +} + void cmd_ls(char*) { return; const char msg[] = { diff --git a/client/cmd.h b/client/cmd.h index 932f3a2..9c58fb6 100644 --- a/client/cmd.h +++ b/client/cmd.h @@ -2,24 +2,28 @@ #include -typedef void cmd_fn_t(char *); +typedef void cmd_handle_t(char *); +typedef char** cmd_complete_t(const char*, int, int); struct cmd { - void (* handle)(char *); + cmd_handle_t * handle; const char* name; const char* info; - // TODO: tab completion function? + cmd_complete_t * complete; }; +typedef struct cmd cmd_t; -cmd_fn_t cmd_exit; -cmd_fn_t cmd_test; -cmd_fn_t cmd_help; -cmd_fn_t cmd_status; -cmd_fn_t cmd_reset; -cmd_fn_t cmd_ls; -cmd_fn_t cmd_send; +cmd_handle_t cmd_exit; +cmd_handle_t cmd_test; +cmd_handle_t cmd_help; +cmd_complete_t cmd_help_complete; +cmd_handle_t cmd_status; +cmd_handle_t cmd_reset; +cmd_handle_t cmd_ls; +cmd_handle_t cmd_send; +cmd_handle_t cmd_skip; -static const struct cmd cmds[] = { +static const cmd_t cmds[] = { { .handle = cmd_exit, .name = "exit", @@ -43,7 +47,12 @@ static const struct cmd cmds[] = { { .handle = cmd_reset, .name = "reset", - .info = "reset entire game state", + .info = "set game state to 'idle' for one or more puzzle modules", + }, + { + .handle = cmd_skip, + .name = "skip", + .info = "set game state to 'solved' for one or more puzzle modules", }, { .handle = cmd_ls, diff --git a/client/parse.cpp b/client/parse.cpp index f31e802..56d1137 100644 --- a/client/parse.cpp +++ b/client/parse.cpp @@ -38,8 +38,7 @@ static int parse_string(const char * str, char * data, size_t * offset) { } static int parse_hexstr(const char * str, char * data, size_t * offset) { - const char* ifs = IFS; - size_t len = strcspn(str, ifs); + size_t len = strcspn(str, IFS); int i = 0; // check if token contains at least one colon @@ -74,8 +73,7 @@ static int parse_hexstr(const char * str, char * data, size_t * offset) { } static int parse_number(const char * str, char * data, size_t * offset) { - const char* ifs = IFS; - size_t len = strcspn(str, ifs); + size_t len = strcspn(str, IFS); int i = 0; int base = 10; bool bytestring = false; @@ -142,12 +140,11 @@ static int parse_number(const char * str, char * data, size_t * offset) { } static int _strtodata_main(const char * str, char* data, size_t * offset) { - const char* ifs = IFS; size_t len = strlen(str); int i, run; for (i = 0; i < len; i += run) { - i += strspn(&str[i], ifs); // skip whitespace + i += strspn(&str[i], IFS); // skip whitespace if (str[i] == '\0') break; // end of string if ((run = parse_string(str + i, data, offset)) > 0) continue; diff --git a/client/pbc.1 b/client/pbc.1 new file mode 100644 index 0000000..e69de29 diff --git a/client/rl.cpp b/client/rl.cpp index 3f93e99..2fdd356 100644 --- a/client/rl.cpp +++ b/client/rl.cpp @@ -38,6 +38,7 @@ void rl_printf(const char *fmt, ...) { } static void cli_cmd(char* cmd) { + cmd += strspn(cmd, IFS); // skip leading whitespace char* line = consume_token(cmd, IFS); for (size_t i = 0; i < cmds_length; i++) { @@ -66,9 +67,31 @@ static char* rl_completion_entries(const char *text, int state) { return NULL; } +static char** rl_attempted_completion(const char * text, int start, int end) { + // do not suggest filenames + rl_attempted_completion_over = 1; + + // if first word in line buffer -> complete commands from cmds[] + size_t cmd_start = strspn(rl_line_buffer, IFS); + if (start == cmd_start) + return rl_completion_matches(text, rl_completion_entries); + + // else, check specialized completion functions + size_t cmd_len = strcspn(rl_line_buffer + cmd_start, IFS); + for (size_t i = 0; i < cmds_length; i++) { + cmd_t cmd = cmds[i]; + if (cmd.complete == NULL) continue; + if (strncmp(cmd.name, rl_line_buffer + cmd_start, cmd_len) != 0) continue; + return cmd.complete(rl_line_buffer, start, end); + } + + // else, no completion available + return NULL; +} + int cli_main() { char* input = NULL; - rl_completion_entry_function = rl_completion_entries; + rl_attempted_completion_function = rl_attempted_completion; while (1) { if (input != NULL) free(input); diff --git a/client/rl.h b/client/rl.h index 503225f..5e80d1a 100644 --- a/client/rl.h +++ b/client/rl.h @@ -6,5 +6,5 @@ #define CLI_PROMPT "(" COLOR_BOLD "pbc" COLOR_OFF ") " int cli_main(); -void rl_printf(const char *fmt, ...); +void rl_printf(const char * fmt, ...); -- cgit v1.2.3 From 439859f1133fb88e64df31acaa1b2845e36ba348 Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Wed, 29 May 2024 15:07:30 +0200 Subject: add documentation + string escape --- client/cmd.cpp | 18 +++++------ client/cmd.h | 18 ++++------- client/parse.cpp | 17 ++++++++-- client/pbc.1 | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 24 deletions(-) (limited to 'client/cmd.h') diff --git a/client/cmd.cpp b/client/cmd.cpp index 2871daf..a6242af 100644 --- a/client/cmd.cpp +++ b/client/cmd.cpp @@ -67,15 +67,15 @@ void cmd_send(char* addr_str) { free(data); } -void cmd_status(char*) { - const char msg[] = { - PB_CMD_READ, - 0x00, // addr 0 = global state - }; - i2c_send(BUSADDR_MAIN, msg, sizeof(msg)); - // NOTE: the reply handler will automatically print the state once it's - // received -} +// void cmd_status(char*) { +// const char msg[] = { +// PB_CMD_READ, +// 0x00, // addr 0 = global state +// }; +// i2c_send(BUSADDR_MAIN, msg, sizeof(msg)); +// // NOTE: the reply handler will automatically print the state once it's +// // received +// } void cmd_reset(char*) { const char msg[] = { diff --git a/client/cmd.h b/client/cmd.h index 9c58fb6..7fefe98 100644 --- a/client/cmd.h +++ b/client/cmd.h @@ -17,7 +17,6 @@ cmd_handle_t cmd_exit; cmd_handle_t cmd_test; cmd_handle_t cmd_help; cmd_complete_t cmd_help_complete; -cmd_handle_t cmd_status; cmd_handle_t cmd_reset; cmd_handle_t cmd_ls; cmd_handle_t cmd_send; @@ -29,21 +28,11 @@ static const cmd_t cmds[] = { .name = "exit", .info = "exit pbc", }, - { - .handle = cmd_test, - .name = "test", - .info = "send a test puzbus message", - }, { .handle = cmd_help, .name = "help", .info = "show this help", }, - { - .handle = cmd_status, - .name = "status", - .info = "show global puzzle box state (main controller state)", - }, { .handle = cmd_reset, .name = "reset", @@ -57,7 +46,7 @@ static const cmd_t cmds[] = { { .handle = cmd_ls, .name = "ls", - .info = "list connected puzzle modules", + .info = "list connected puzzle modules and their state", }, #ifdef DEBUG { @@ -65,6 +54,11 @@ static const cmd_t cmds[] = { .name = "send", .info = "[debug] send raw message", }, + { + .handle = cmd_test, + .name = "test", + .info = "[debug] send a test puzbus message", + }, #endif }; static const size_t cmds_length = sizeof(cmds) / sizeof(cmds[0]); diff --git a/client/parse.cpp b/client/parse.cpp index 56d1137..16f0781 100644 --- a/client/parse.cpp +++ b/client/parse.cpp @@ -6,7 +6,6 @@ #include "parse.h" static int parse_string(const char * str, char * data, size_t * offset) { - char closing = str[0]; char escape = false; int i = 0; size_t len = strlen(str); @@ -21,15 +20,27 @@ static int parse_string(const char * str, char * data, size_t * offset) { default: return -i; } + char closing = str[i]; for (i = 1; i < len && str[i] != '\0'; i++, *offset += 1) { char c = str[i]; - // TODO: handle escaped characters - if (c == closing) return i + 1; // +1 for closing quote + if (escape && c == '\\') { + char x = str[i + 1]; + if (x == '0') c = '\0'; + else if (x == 't') c = '\t'; + else if (x == 'n') c = '\n'; + else if (x == 'r') c = '\r'; + else if (x == '\\') c = '\\'; + else if (x == '\"') c = '\"'; + else if (x == '\'') c = '\''; + else break; + i++; + } + if (data != NULL) data[*offset] = c; } diff --git a/client/pbc.1 b/client/pbc.1 index e69de29..f5a2198 100644 --- a/client/pbc.1 +++ b/client/pbc.1 @@ -0,0 +1,94 @@ +\# vim: ft=groff +.de I2C +I\*{2\*}C +.. +.TH pbc 1 +.SH NAME +pbc \- puzzle box client +.SH SYNPOSIS +pbc [port] +.SH DESCRIPTION +Connect to a puzzle box at the IPv4 address specified by \fIaddr\fP and +optionally port specified by \fIport\fP. The default port is 9191. Once +connected, a +.MR readline 3 -based +CLI is started, and commands can be sent. +.SH COMMANDS +.TP +exit +Disconnect from the puzzle box and exit pbc. This command takes no arguments. +.TP +help +Print a list of available commands with descriptions. This command takes no +arguments. +.TP +ls +List all puzzle modules, their state, and the combined state of all puzzle +modules (global state of the main controller). +.TP +reset [mod ...] +Set the main controller or specific puzzle module's global state to \fIidle\fP. +If no modules are specified, the main controller's state is updated. One or +more modules can be specified to update them at once. +.TP +skip [mod ...] +Set the main controller or specific puzzle module's global state to +\fIsolved\fP. If no modules are specified, the main controller's state is +updated. One or more modules can be specified to update them at once. +.SH DEBUG COMMANDS +The commands detailed under this section are only available in version of pbc +compiled with debug support. +.TP +send +Send arbitrary data specified by \fIdata\fP to the +.I2C +address specified by \fIaddr\fP. \fIdata\fP may consist of multiple arguments +separated by IFS, in which case the arguments are concatenated. +.TP +test +Send a test command containing the ASCII string "Hello world!" to +.I2C +address 0x39. This command takes no arguments. +.SH DATA FORMATS +.TP +number +Numbers can be specified as decimal or hexadecimal using a "0x" prefix. All +numbers are unsigned. Decimal literals are always cast to 8-bit integers, while +hexadecimal literals are cast to the smallest type that will fit the specified +number. Numbers are always sent as little endian. + +Examples: 0 123 255 0x10 0x1245 0xdeadBEEF +.TP +hexstr +Hexadecimal string literals are specified by hexadecimal bytes separated by +colons. Each byte must be exactly 2 hexadecimal characters long and followed by +a colon (except for the last byte). The minimum length of a hexstr is 2 bytes, +as it must include at least a single colon. + +Examples: de:ad:be:ef 00:00 +.TP +string +A string literal starts and ends with a single quote. All characters within +this literal are sent as-is, and no escaping is possible. + +Examples: 'Hello world!' 'string' ' hello ' + +When double quotes are used instead of single quotes, the following escape +sequences are recognised and replaced with special characters: + +\\0 -> 0x00 (null) +.br +\\t -> 0x09 (tab) +.br +\\n -> 0x0a (newline) +.br +\\r -> 0x0d (carriage return) +.br +\\\\ -> 0x5c (backslash) +.br +\\" -> 0x22 (double quote) +.br +\\' -> 0x27 (single quote) + +Examples: "Hello world!\\0" "foo\\nbar" + -- cgit v1.2.3 From d1f5291f82f5e13db756adc919883e310cc537de Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Thu, 30 May 2024 14:02:51 +0200 Subject: implement message dumping --- client/cmd.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++----------- client/cmd.h | 9 ++++++++- client/rl.cpp | 14 +++++++++++++- client/rl.h | 1 + client/sock.cpp | 13 ++++++++++++- 5 files changed, 76 insertions(+), 14 deletions(-) (limited to 'client/cmd.h') diff --git a/client/cmd.cpp b/client/cmd.cpp index b7adfae..e3c9eb9 100644 --- a/client/cmd.cpp +++ b/client/cmd.cpp @@ -43,7 +43,7 @@ void cmd_help(char*) { ); } -void cmd_send(char* addr_str) { +void cmd_send(char * addr_str) { char* data_str = consume_token(addr_str, IFS); char* end; @@ -68,16 +68,6 @@ void cmd_send(char* addr_str) { free(data); } -// void cmd_status(char*) { -// const char msg[] = { -// PB_CMD_READ, -// 0x00, // addr 0 = global state -// }; -// i2c_send(BUSADDR_MAIN, msg, sizeof(msg)); -// // NOTE: the reply handler will automatically print the state once it's -// // received -// } - void cmd_reset(char*) { const char msg[] = { PB_CMD_WRITE, @@ -105,3 +95,44 @@ void cmd_ls(char*) { i2c_send(BUSADDR_MAIN, msg, sizeof(msg)); } +extern bool i2c_dump_send; +extern bool i2c_dump_recv; +const char * dump_modes[] = { + "none", + "send", + "recv", + "both", + NULL, +}; +void cmd_dump(char * mode) { + consume_token(mode, IFS); + mode += strspn(mode, IFS); + + for (int i = 0; dump_modes[i] != NULL; i++) { + if (strcmp(mode, dump_modes[i]) == 0) { + i2c_dump_send = (i >> 0) & 1; + i2c_dump_recv = (i >> 1) & 1; + return; + } + } + + printf("mode \"%s\" unknown\n", mode); +} +char** cmd_dump_complete(const char * text, int begin, int end) { + int word = rl_word(rl_line_buffer, begin); + if (word != 1) return NULL; + + return rl_completion_matches(text, [](const char * text, int state) -> char * { + static size_t i = 0; + if (state == 0) i = 0; + + while (dump_modes[i] != NULL) { + const char * mode = dump_modes[i++]; + if (strncmp(text, mode, strlen(text)) == 0) + return strdup(mode); + } + return NULL; + }); + + return NULL; +} diff --git a/client/cmd.h b/client/cmd.h index 7fefe98..961ef89 100644 --- a/client/cmd.h +++ b/client/cmd.h @@ -16,11 +16,12 @@ typedef struct cmd cmd_t; cmd_handle_t cmd_exit; cmd_handle_t cmd_test; cmd_handle_t cmd_help; -cmd_complete_t cmd_help_complete; cmd_handle_t cmd_reset; cmd_handle_t cmd_ls; cmd_handle_t cmd_send; cmd_handle_t cmd_skip; +cmd_handle_t cmd_dump; +cmd_complete_t cmd_dump_complete; static const cmd_t cmds[] = { { @@ -59,6 +60,12 @@ static const cmd_t cmds[] = { .name = "test", .info = "[debug] send a test puzbus message", }, + { + .handle = cmd_dump, + .name = "dump", + .info = "[debug] dump sent or received messages", + .complete = cmd_dump_complete, + }, #endif }; static const size_t cmds_length = sizeof(cmds) / sizeof(cmds[0]); diff --git a/client/rl.cpp b/client/rl.cpp index 2fdd356..b8113aa 100644 --- a/client/rl.cpp +++ b/client/rl.cpp @@ -82,7 +82,7 @@ static char** rl_attempted_completion(const char * text, int start, int end) { cmd_t cmd = cmds[i]; if (cmd.complete == NULL) continue; if (strncmp(cmd.name, rl_line_buffer + cmd_start, cmd_len) != 0) continue; - return cmd.complete(rl_line_buffer, start, end); + return cmd.complete(text, start, end); } // else, no completion available @@ -107,3 +107,15 @@ int cli_main() { return EXIT_SUCCESS; } +int rl_word(const char * line, int cursor) { + int word = -1; + for (int i = 0; line[i] != '\0';) { + i += strspn(line + i, IFS); + int len = strcspn(line + i, IFS); + word++; + i += len; + if (i > cursor) break; + } + return word; +} + diff --git a/client/rl.h b/client/rl.h index 5e80d1a..c3bf2c7 100644 --- a/client/rl.h +++ b/client/rl.h @@ -7,4 +7,5 @@ int cli_main(); void rl_printf(const char * fmt, ...); +int rl_word(const char * line, int cursor); diff --git a/client/sock.cpp b/client/sock.cpp index 2d5787d..f5bd564 100644 --- a/client/sock.cpp +++ b/client/sock.cpp @@ -13,6 +13,7 @@ #include "i2ctcpv1.h" #include "sock.h" #include "rl.h" +#include "xxd.h" using std::logic_error; using std::thread; @@ -105,6 +106,9 @@ void PBSocket::sock_task() { sock_close(); } +bool i2c_dump_send = false; +bool i2c_dump_recv = true; + void i2c_send(uint16_t addr, const char * data, size_t data_size) { i2ctcp_msg_t msg = { .addr = addr, @@ -117,9 +121,16 @@ void i2c_send(uint16_t addr, const char * data, size_t data_size) { if (!i2ctcp_write(&msg, &packed, &size)) return; sock->send(packed, size); + if (i2c_dump_send) { + printf("[i2c send] data(0x%02lx):\n", data_size); + xxd(data, data_size); + } } void i2c_recv(uint16_t addr, const char * data, size_t data_size) { - rl_printf("[0x%02x]: %.*s\n", addr, data_size, data); + if (i2c_dump_recv) { + printf("[i2c recv] data(0x%02lx):\n", data_size); + xxd(data, data_size); + } } -- cgit v1.2.3