From c4c0104772cd412a456338ac87813ce4f50f9cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 15 Dec 2017 00:35:02 +0100 Subject: [PATCH] more work on cleaning it up, added experimental QueryUnit cmd and mesage constants --- CMakeLists.txt | 2 + gex/TF_Integration.c | 2 + gex/gex_client.c | 123 +++++++++++++++++++++++++++++++++----- gex/gex_client.h | 27 +++++---- gex/gex_client_internal.h | 24 ++++++++ gex/gex_message_types.h | 23 +++++++ 6 files changed, 173 insertions(+), 28 deletions(-) create mode 100644 gex/gex_client_internal.h create mode 100644 gex/gex_message_types.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ac88997..244c77f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,8 @@ set(SOURCE_FILES gex/serial/serial.h gex/gex_client.c gex/gex_client.h + gex/gex_client_internal.h + gex/gex_message_types.h gex/utils/hexdump.c gex/utils/hexdump.h gex/TF_Integration.c diff --git a/gex/TF_Integration.c b/gex/TF_Integration.c index fd0ee09..df204d4 100644 --- a/gex/TF_Integration.c +++ b/gex/TF_Integration.c @@ -6,6 +6,8 @@ #include #include +#include "gex_client_internal.h" + void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len) { GexClient *gc = tf->userdata; diff --git a/gex/gex_client.c b/gex/gex_client.c index 052e13f..b662d5e 100644 --- a/gex/gex_client.c +++ b/gex/gex_client.c @@ -5,14 +5,18 @@ #include #include #include -#include +#include #include #include - #include "TinyFrame.h" + #include "gex_client.h" #include "serial.h" +#include "gex_client_internal.h" +#include "gex_message_types.h" +#include "utils/payload_parser.h" +/** Callback for ping */ TF_Result connectivityCheckCb(TinyFrame *tf, TF_Msg *msg) { GexClient *gex = tf->userdata; @@ -21,14 +25,74 @@ TF_Result connectivityCheckCb(TinyFrame *tf, TF_Msg *msg) return TF_CLOSE; } +/** Delete recursively all GEX callsign look-up table entries */ +static void destroy_unit_lookup(GexClient *gex) +{ + struct gex_name_lu *next = gex->ulu_head; + while (next != NULL) { + struct gex_name_lu *cur = next; + next = next->next; + free(cur); + } + gex->ulu_head = NULL; +} + +/** Get callsign for unit name */ +static uint8_t find_callsign_by_name(GexClient *gex, const char *name) +{ + struct gex_name_lu *next = gex->ulu_head; + while (next != NULL) { + if (strcmp(next->name, name) == 0) { + return next->callsign; + } + next = next->next; + } + return 0; +} + +/** Listener for the "list units" query response */ +TF_Result listUnitsCb(TinyFrame *tf, TF_Msg *msg) +{ + GexClient *gex = tf->userdata; + + destroy_unit_lookup(gex); + + PayloadParser pp = pp_start((uint8_t*)msg->data, msg->len, NULL); + uint8_t count = pp_u8(&pp); + char buf[100]; + struct gex_name_lu *tail = NULL; + for(int i=0; i< count; i++) { + uint8_t cs = pp_u8(&pp); + pp_string(&pp, buf, 100); + fprintf(stderr, "Available unit \"%s\" @ %d\n", buf, cs); + + // append + struct gex_name_lu *lu = malloc(sizeof(struct gex_name_lu)); + lu->next = NULL; + lu->type = "UNKNOWN"; + lu->name = strdup(buf); + lu->callsign = cs; + if (tail == NULL) { + gex->ulu_head = lu; + } else { + tail->next = lu; + } + tail = lu; + } + + return TF_CLOSE; +} + +/** Create a instance and connect */ GexClient *GEX_Init(const char *device, int timeout_ms) { assert(device != NULL); + // --- Init the struct --- GexClient *gex = calloc(1, sizeof(GexClient)); assert(gex != NULL); - // Open the device + // --- Open the device --- gex->acm_device = device; gex->acm_fd = serial_open(device, false, (timeout_ms + 50) / 100); if (gex->acm_fd == -1) { @@ -39,11 +103,8 @@ GexClient *GEX_Init(const char *device, int timeout_ms) gex->tf = TF_Init(TF_MASTER); gex->tf->userdata = gex; - // Test connectivity - TF_Msg msg; - TF_ClearMsg(&msg); - msg.type = 0x01; // TODO use constant - TF_Query(gex->tf, &msg, connectivityCheckCb, 0); + // --- Test connectivity --- + TF_QuerySimple(gex->tf, MSG_PING, /*pld*/ NULL, 0, /*cb*/ connectivityCheckCb, 0); GEX_Poll(gex); if (!gex->connected) { @@ -52,12 +113,14 @@ GexClient *GEX_Init(const char *device, int timeout_ms) return NULL; } - // TODO load and store unit callsigns + names + // --- populate callsign look-up table --- + TF_QuerySimple(gex->tf, MSG_LIST_UNITS, /*pld*/ NULL, 0, /*cb*/ listUnitsCb, 0); + GEX_Poll(gex); return gex; } - +/** Try to read from the serial port and process any received bytes with TF */ void GEX_Poll(GexClient *gex) { uint8_t pollbuffer[1024]; @@ -73,11 +136,41 @@ void GEX_Poll(GexClient *gex) } } +/** Free the struct */ +void GEX_DeInit(GexClient *gex) +{ + if (gex == NULL) return; + close(gex->acm_fd); + destroy_unit_lookup(gex); + TF_DeInit(gex->tf); + free(gex); +} -void GEX_DeInit(GexClient *gc) +/** Query a unit */ +void GEX_QueryUnit(GexClient *gex, + const char *unit, uint8_t cmd, + uint8_t *payload, uint32_t len, + TF_Listener listener) { - if (gc == NULL) return; - close(gc->acm_fd); - TF_DeInit(gc->tf); - free(gc); + uint8_t cs = find_callsign_by_name(gex, unit); + assert(cs != 0); + uint8_t *pld = malloc(len + 2); + assert(pld != NULL); + + pld[0] = cs; + pld[1] = cmd; + memcpy(pld+2, payload, len); + + TF_Msg msg; + TF_ClearMsg(&msg); + msg.type = MSG_UNIT_REQUEST; + msg.data = pld; + msg.len = (TF_LEN) (len + 2); + TF_Query(gex->tf, &msg, listener, 0); + free(pld); + + GEX_Poll(gex); } + +// TODO add Send command (no query) +// TODO add listener for spontaneous device reports with user configurable handler (per unit) diff --git a/gex/gex_client.h b/gex/gex_client.h index 1009c80..c98c7f4 100644 --- a/gex/gex_client.h +++ b/gex/gex_client.h @@ -10,13 +10,6 @@ #include #include "TinyFrame.h" -struct gex_client_ { - TinyFrame *tf; - const char *acm_device; - int acm_fd; - bool connected; -}; - typedef struct gex_client_ GexClient; /** @@ -37,15 +30,23 @@ void GEX_Poll(GexClient *gex); /** * Safely release all resources used up by GEX_Init() * - * @param gc - the allocated client structure + * @param gex - the allocated client structure */ -void GEX_DeInit(GexClient *gc); +void GEX_DeInit(GexClient *gex); -// --- Internal --- /** - * This is accessed by TF_WriteImpl(). - * To be removed once TF supports multiple instances, i.e. without globals + * Query a unit + * + * @param gex - client instance + * @param unit - unit name + * @param cmd - command (hex) + * @param payload - payload for the unit + * @param len - number of bytes in the payload + * @param listener - TF listener function called for the response */ -extern int gex_serial_fd; +void GEX_QueryUnit(GexClient *gex, + const char *unit, uint8_t cmd, + uint8_t *payload, uint32_t len, + TF_Listener listener); #endif //GEX_CLIENT_GEX_CLIENT_H diff --git a/gex/gex_client_internal.h b/gex/gex_client_internal.h new file mode 100644 index 0000000..e1c1502 --- /dev/null +++ b/gex/gex_client_internal.h @@ -0,0 +1,24 @@ +// +// Created by MightyPork on 2017/12/15. +// + +#ifndef GEX_CLIENT_GEX_CLIENT_INTERNAL_H +#define GEX_CLIENT_GEX_CLIENT_INTERNAL_H + +struct gex_name_lu { + char *name; + char *type; + uint8_t callsign; + struct gex_name_lu *next; +}; + +struct gex_client_ { + TinyFrame *tf; //!< TinyFrame instance + const char *acm_device; //!< Comport device name, might be used to reconnect + int acm_fd; //!< Open comport file descriptor + bool connected; //!< Flag that we're currently connected to the comport + + struct gex_name_lu *ulu_head; //!< Units look-up +}; + +#endif //GEX_CLIENT_GEX_CLIENT_INTERNAL_H diff --git a/gex/gex_message_types.h b/gex/gex_message_types.h new file mode 100644 index 0000000..2e2a60e --- /dev/null +++ b/gex/gex_message_types.h @@ -0,0 +1,23 @@ +// +// Created by MightyPork on 2017/12/15. +// + +#ifndef GEX_CLIENT_GEX_MESSAGE_TYPES_H +#define GEX_CLIENT_GEX_MESSAGE_TYPES_H + +/** + * Supported message types (TF_TYPE) + */ +enum TF_Types_ { + // General, low level + MSG_SUCCESS = 0x00, //!< Generic success response; used by default in all responses; payload is transaction-specific + MSG_PING = 0x01, //!< Ping request (or response), used to test connection + MSG_ERROR = 0x02, //!< Generic failure response (when a request fails to execute) + // Unit messages + MSG_UNIT_REQUEST = 0x10, //!< Command addressed to a particular unit + MSG_UNIT_REPORT = 0x11, //!< Spontaneous report from a unit + // System messages + MSG_LIST_UNITS = 0x20, //!< Get all unit call-signs and names +}; + +#endif //GEX_CLIENT_GEX_MESSAGE_TYPES_H