aboutsummaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorThomasintAnker <thomasintanker1@gmail.com>2024-06-24 14:59:56 +0200
committerThomasintAnker <thomasintanker1@gmail.com>2024-06-24 14:59:56 +0200
commita0c664908b9112306c5858ccb106d1a0e5555df7 (patch)
tree8ca77d1210d1683a97f4da131c6ffac8123d4375 /client
parent381149dd7a1f4d5f48dd5ac07186c73371ff3c04 (diff)
parentec7f5e970ed03acb33eb5dc3b67f3d52af52e6dc (diff)
merge main into wip/mc
Diffstat (limited to 'client')
-rw-r--r--client/cmd.cpp2
-rw-r--r--client/cmd.h55
-rw-r--r--client/i2c.cpp4
-rw-r--r--client/i2c.h29
-rw-r--r--client/parse.h14
-rw-r--r--client/readme.md32
-rw-r--r--client/rl.cpp17
-rw-r--r--client/rl.h43
-rw-r--r--client/sock.cpp2
-rw-r--r--client/sock.h43
-rw-r--r--client/xxd.h9
11 files changed, 209 insertions, 41 deletions
diff --git a/client/cmd.cpp b/client/cmd.cpp
index 10d53e3..062fefa 100644
--- a/client/cmd.cpp
+++ b/client/cmd.cpp
@@ -28,7 +28,7 @@ void cmd_test(char*) {
void cmd_help(char*) {
printf("List of available commands:\n");
for (size_t i = 0; i < cmds_length; i++) {
- struct cmd cmd = cmds[i];
+ cmd_t cmd = cmds[i];
printf(" %-*s", 10, cmd.name);
if (cmd.info != NULL)
printf(" %s", cmd.info);
diff --git a/client/cmd.h b/client/cmd.h
index 961ef89..4f77d50 100644
--- a/client/cmd.h
+++ b/client/cmd.h
@@ -1,17 +1,49 @@
#pragma once
+/**
+ * \ingroup pbc
+ * \defgroup pbc_cmd Commands
+ * \brief Commands within \ref pbc
+ *
+ * \note A manpage is available containing end-user usage instructions inside
+ * the \ref client folder in the source code repository. This page contains the
+ * internal code documentation for the commands defined in \c pbc.
+ *
+ * \{
+ */
+
#include <stddef.h>
-typedef void cmd_handle_t(char *);
-typedef char** cmd_complete_t(const char*, int, int);
+/**
+ * \internal
+ * \brief Command handler function
+ *
+ * \param line Remaining text after command name on command line
+ */
+typedef void cmd_handle_t(char * line);
+/**
+ * \internal
+ * \brief Command completion function
+ *
+ * \param text Current word to complete
+ * \param start Index in \c rl_line_buffer of cursor position
+ * \param end End index of \p text in \c rl_line_buffer
+ *
+ * \return Array of \c char* with suggestions. The array is terminated by a
+ * NULL pointer.
+ */
+typedef char** cmd_complete_t(const char* text, int start, int end);
-struct cmd {
- cmd_handle_t * handle;
- const char* name;
- const char* info;
- cmd_complete_t * complete;
-};
-typedef struct cmd cmd_t;
+/**
+ * \internal
+ * \brief Command definition struct
+ */
+typedef struct {
+ cmd_handle_t * handle; //!< Handler function (required)
+ const char* name; //!< Command name (required)
+ const char* info; //!< Command info (shown in help command) (optional = NULL)
+ cmd_complete_t * complete; //!< Completion function (optional = NULL)
+} cmd_t;
cmd_handle_t cmd_exit;
cmd_handle_t cmd_test;
@@ -23,6 +55,7 @@ cmd_handle_t cmd_skip;
cmd_handle_t cmd_dump;
cmd_complete_t cmd_dump_complete;
+//! Commands
static const cmd_t cmds[] = {
{
.handle = cmd_exit,
@@ -68,5 +101,9 @@ static const cmd_t cmds[] = {
},
#endif
};
+
+//! Number of commands defined in \c cmds
static const size_t cmds_length = sizeof(cmds) / sizeof(cmds[0]);
+/// \}
+
diff --git a/client/i2c.cpp b/client/i2c.cpp
index 78e5585..3655191 100644
--- a/client/i2c.cpp
+++ b/client/i2c.cpp
@@ -35,9 +35,9 @@ void i2c_send(uint16_t addr, const char * data, size_t data_size) {
static void i2c_handle_cmd_read(uint16_t, const char *, size_t);
-void i2c_recv(uint16_t addr, const char * data, size_t data_size) {
+void i2c_recv(const char * data, size_t data_size) {
if (i2c_dump_recv) {
- printf("[%s] addr(0x%02x) data(0x%02lx):\n", __FUNCTION__, addr, data_size);
+ printf("[%s] data(0x%02lx):\n", __FUNCTION__, data_size);
xxd(data, data_size);
}
}
diff --git a/client/i2c.h b/client/i2c.h
index f9f58f9..d2cfa9a 100644
--- a/client/i2c.h
+++ b/client/i2c.h
@@ -3,6 +3,33 @@
#include <stdint.h>
#include <stddef.h>
+/**
+ * \ingroup pbc
+ * \defgroup pbc_i2c I2C
+ * \brief I2C abstraction functions
+ * \{
+ */
+
+/**
+ * \brief Fake I2C send function
+ *
+ * This function sends an I2C message to the main controller over TCP using
+ * \ref i2ctcp.
+ *
+ * \param addr I2C address
+ * \param data Data to send
+ * \param data_size size of \p data
+ */
void i2c_send(uint16_t addr, const char * data, size_t data_size);
-void i2c_recv(uint16_t addr, const char * data, size_t data_size);
+/**
+ * \brief Fake I2C receive handler
+ *
+ * This function is called for I2C messages received by the main controller and
+ * forwarded to \ref pbc.
+ *
+ * \param data Received data
+ * \param data_size size of \p data
+ */
+void i2c_recv(const char * data, size_t data_size);
+/// \}
diff --git a/client/parse.h b/client/parse.h
index 94afe70..1beb714 100644
--- a/client/parse.h
+++ b/client/parse.h
@@ -2,11 +2,23 @@
#include <stddef.h>
+/**
+ * \ingroup pbc
+ * \defgroup pbc_parse Parse
+ * \brief Debug \c send command parser utilities
+ * \{
+ */
+
+//! Internal field separator (i.e. whitespace delimiter)
#define IFS " \t\n"
+//! Octal digit character set
#define SET_OCT "01234567"
+//! Decimal digit character set
#define SET_DEC "0123456789"
+//! Hexadecimal digit character set
#define SET_HEX SET_DEC"abcdefABCDEF"
+//! (Hexadecimal) byte string character set
#define SET_HEX_STR SET_HEX":"
/**
@@ -40,3 +52,5 @@ char* consume_token(char * token, const char * ifs);
*/
int strtodata(const char * str, char ** data, size_t * size);
+/// \}
+
diff --git a/client/readme.md b/client/readme.md
index fcde40d..b9e0b09 100644
--- a/client/readme.md
+++ b/client/readme.md
@@ -1,3 +1,6 @@
+\defgroup pbc pbc
+\brief Puzzle box client
+
# puzzle box client
This folder contains the source code for the puzzle box client (pbc). This is a
@@ -7,30 +10,21 @@ 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.
-## WIP TODO
-
-- cleanup
- - separate ../shared/pb/moddrv.c into a puzzle module specific and 'common' bit
- - use the common bit in i2c.cpp instead
- - cast to structs in ../shared/pb/moddrv.c
-- functionality
- - print pretty tree of connected puzzle modules
- - add enum to string functions in CLIENT ONLY
-
## 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
+- Debug: 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).
+PBC is a standard CMake project.
+
+## Using
+
+See ./pbc.1 for usage.
## Send data
@@ -45,8 +39,10 @@ send 0x39 68:65:6c:6c:6f 44 0x20 'world' 33
The data is concatenated, and may contain mixed types of literals
-## known bugs (TODO)
-- tab completion for `dump` seems to print garbage sometimes
-- the send command with an address but no data causes a segmentation fault
+## WIP TODO
+
+- add enum to string functions in CLIENT ONLY
+- bug: tab completion for `dump` seems to print garbage sometimes
+- bug: the send command with an address but no data causes a segmentation fault
diff --git a/client/rl.cpp b/client/rl.cpp
index fa44bf4..2e74e5f 100644
--- a/client/rl.cpp
+++ b/client/rl.cpp
@@ -57,7 +57,7 @@ static char* rl_completion_entries(const char *text, int state) {
if (state == 0) i = 0;
while (i < cmds_length) {
- struct cmd cmd = cmds[i];
+ cmd_t cmd = cmds[i];
i++;
if (strncmp(text, cmd.name, strlen(text)) == 0) {
return strdup(cmd.name);
@@ -119,24 +119,25 @@ int rl_word(const char * line, int cursor) {
return word;
}
+/// \internal
typedef struct {
const char * word;
- const char ** suggestions;
+ const char ** options;
} __rl_complete_list_data_t;
-char** rl_complete_list(const char * word, const char ** suggestions) {
+char** rl_complete_list(const char * word, const char ** options) {
__rl_complete_list_data_t data = {
.word = word,
- .suggestions = suggestions,
+ .options = options,
};
return rl_completion_matches((char *) &data, [](const char * text, int state) -> char * {
__rl_complete_list_data_t data = *(__rl_complete_list_data_t *) text;
static size_t i = 0;
if (state == 0) i = 0;
- while (data.suggestions[i] != NULL) {
- const char * suggestion = data.suggestions[i++];
- if (strncmp(data.word, suggestion, strlen(data.word)) == 0)
- return strdup(suggestion);
+ while (data.options[i] != NULL) {
+ const char * option = data.options[i++];
+ if (strncmp(data.word, option, strlen(data.word)) == 0)
+ return strdup(option);
}
return NULL;
});
diff --git a/client/rl.h b/client/rl.h
index ab31ddb..d2f8612 100644
--- a/client/rl.h
+++ b/client/rl.h
@@ -1,12 +1,53 @@
#pragma once
+/**
+ * \ingroup pbc
+ * \defgroup pbc_rl rl
+ * \brief GNU Readline related functions
+ * \{
+ */
+
+//! Reset color (ANSI sequence)
#define COLOR_OFF "\x1b[0m"
+//! Set font to bold (ANSI sequence)
#define COLOR_BOLD "\x1b[1m"
+//! Prompt text
#define CLI_PROMPT "(" COLOR_BOLD "pbc" COLOR_OFF ") "
+//! CLI entrypoint
int cli_main();
+
+/**
+ * \brief Print format string to stdout without disturbing the readline prompt
+ *
+ * This function saves and restores the current readline prompt before/after
+ * calling printf. This function is not required for commands that print output
+ * synchronously, as the prompt is only shown after a command handler
+ * completes.
+ */
void rl_printf(const char * fmt, ...);
+/**
+ * \brief Get the index of the word currently under the cursor
+ *
+ * \param line Command line contents
+ * \param cursor Index of cursor position
+ *
+ * This function returns the index of the word from an array made by splitting
+ * \p line on consecutive occurrences of \c IFS.
+ *
+ * \return Index of word
+ */
int rl_word(const char * line, int cursor);
-char ** rl_complete_list(const char * word, const char * suggestions[]);
+/**
+ * \brief Create a completion suggestion string array for readline
+ *
+ * \param word Word to complete
+ * \param options List of possible choices (NULL terminated array of strings)
+ *
+ * \return Suggestions matching \p word
+ */
+char ** rl_complete_list(const char * word, const char * options[]);
+
+/// \}
diff --git a/client/sock.cpp b/client/sock.cpp
index 3490586..e33a3dc 100644
--- a/client/sock.cpp
+++ b/client/sock.cpp
@@ -101,7 +101,7 @@ void PBSocket::sock_task() {
if (ret > 0) continue;
// message read completely!
- i2c_recv(input.addr, input.data, input.length);
+ i2c_recv(input.data, input.length);
free(input.data);
}
diff --git a/client/sock.h b/client/sock.h
index 0dee09e..792123e 100644
--- a/client/sock.h
+++ b/client/sock.h
@@ -3,29 +3,72 @@
#include <cstdint>
#include <thread>
+/**
+ * \ingroup pbc
+ * \defgroup pbc_sock Socket
+ * \brief TCP socket handling
+ * \{
+ */
+
+/**
+ * \brief Asynchronous puzzle box socket connection
+ * \note Once connected, this class will call \c i2c_recv() when a complete I2C
+ * message has been received
+ */
class PBSocket {
public:
PBSocket();
PBSocket(const char * addr, uint16_t port);
virtual ~PBSocket();
+ //! Configure target server
void set_server(const char * addr, uint16_t port);
+ //! Attempt to connect to server and start \c sock_task() in a thread
void sock_connect();
+ /**
+ * \brief Send data over the TCP connection
+ *
+ * \param buf Data to send
+ * \param buf_sz Size of \p buf in bytes
+ */
void send(const char * buf, size_t buf_sz);
private:
+ /**
+ * \brief Continously read from the TCP socket and read \ref i2ctcp messages
+ * using \c i2ctcp_read().
+ *
+ * Once a complete message has been parsed, \c i2c_recv() is called with the
+ * complete message. This message is automatically free'd after \c i2c_recv()
+ * returns.
+ *
+ * \note This function is run in a separate thread
+ */
void sock_task();
+ //! Close the socket
void sock_close();
+ //! Pointer to thread running \c sock_task()
std::thread* _thread = nullptr;
+ /**
+ * \brief IP address of server to connect to
+ *
+ * \note This member must contain an IP address, as no hostname resolution is
+ * done in pbc.
+ */
const char * _addr = NULL;
+ //! Port number of server to connect to
uint16_t _port = 0;
+ //! Unix file descriptor of opened socket
int _fd = -1;
};
+//! Singleton \c PBSocket instance
extern PBSocket* sock;
+/// \}
+
diff --git a/client/xxd.h b/client/xxd.h
index fb28bb1..ede9fff 100644
--- a/client/xxd.h
+++ b/client/xxd.h
@@ -7,10 +7,19 @@ extern "C" {
#endif
/**
+ * \ingroup pbc
+ * \defgroup pbc_xxd xxd
+ * \brief Utility hexdump
+ * \{
+ */
+
+/**
* \brief utility function that prints hexdump of data
*/
void xxd(const char * data, size_t size);
+/// \}
+
#ifdef __cplusplus
}
#endif