diff --git a/CMakeLists.txt b/CMakeLists.txt index 244c77f..b0119c9 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_helpers.c + gex/gex_helpers.h gex/gex_client_internal.h gex/gex_message_types.h gex/utils/hexdump.c diff --git a/gex/gex_client.c b/gex/gex_client.c index 1f65b04..8d61b69 100644 --- a/gex/gex_client.c +++ b/gex/gex_client.c @@ -14,10 +14,11 @@ #include "serial.h" #include "gex_client_internal.h" #include "gex_message_types.h" +#include "gex_helpers.h" #include "utils/payload_parser.h" /** Callback for ping */ -TF_Result connectivityCheckCb(TinyFrame *tf, TF_Msg *msg) +static TF_Result connectivity_check_lst(TinyFrame *tf, TF_Msg *msg) { GexClient *gex = tf->userdata; gex->connected = true; @@ -25,36 +26,31 @@ 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) +/** Callback for ping */ +static TF_Result unit_report_lst(TinyFrame *tf, TF_Msg *msg) { - 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; -} - + GexClient *gex = tf->userdata; -/** 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; + // payload must start by callsign and report type. + if (msg->len < 2) goto done; + uint8_t callsign = msg->data[0]; + uint8_t rpt_type = msg->data[1]; + + struct gex_unit_lu *lu = find_unit_by_callsign(gex, callsign); + if (lu && lu->report_handler) { + lu->report_handler(gex, lu->name, rpt_type, + msg->data+2, (uint32_t) (msg->len - 2)); + } else if (gex->fallback_report_handler) { + gex->fallback_report_handler(gex, (lu ? lu->name : "UNKNOWN"), rpt_type, + msg->data+2, (uint32_t) (msg->len - 2)); } - return 0; -} +done: + return TF_STAY; +} /** Listener for the "list units" query response */ -TF_Result listUnitsCb(TinyFrame *tf, TF_Msg *msg) +static TF_Result list_units_lst(TinyFrame *tf, TF_Msg *msg) { GexClient *gex = tf->userdata; @@ -63,18 +59,19 @@ TF_Result listUnitsCb(TinyFrame *tf, TF_Msg *msg) 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); + struct gex_unit_lu *tail = NULL; + for(int i = 0; i < count; i++) { + uint8_t callsign = pp_u8(&pp); pp_string(&pp, buf, 100); - fprintf(stderr, "Available unit \"%s\" @ %d\n", buf, cs); + fprintf(stderr, "Available unit \"%s\" @ %d\n", buf, callsign); // append - struct gex_name_lu *lu = malloc(sizeof(struct gex_name_lu)); + struct gex_unit_lu *lu = malloc(sizeof(struct gex_unit_lu)); lu->next = NULL; lu->type = "UNKNOWN"; lu->name = strdup(buf); - lu->callsign = cs; + lu->callsign = callsign; + lu->report_handler = NULL; if (tail == NULL) { gex->ulu_head = lu; } else { @@ -86,6 +83,22 @@ TF_Result listUnitsCb(TinyFrame *tf, TF_Msg *msg) return TF_CLOSE; } +/** Bind report listener */ +void GEX_OnReport(GexClient *gex, const char *unit_name, GEX_ReportListener lst) +{ + if (!unit_name) { + gex->fallback_report_handler = lst; + } + else { + struct gex_unit_lu *lu = find_unit_by_name(gex, unit_name); + if (!lu) { + fprintf(stderr, "No unit named \"%s\"!", unit_name); + } + else { + lu->report_handler = lst; + } + } +} /** Create a instance and connect */ GexClient *GEX_Init(const char *device, int timeout_ms) @@ -108,7 +121,7 @@ GexClient *GEX_Init(const char *device, int timeout_ms) gex->tf->userdata = gex; // --- Test connectivity --- - TF_QuerySimple(gex->tf, MSG_PING, /*pld*/ NULL, 0, /*cb*/ connectivityCheckCb, 0); + TF_QuerySimple(gex->tf, MSG_PING, /*pld*/ NULL, 0, /*cb*/ connectivity_check_lst, 0); GEX_Poll(gex); if (!gex->connected) { @@ -118,9 +131,11 @@ GexClient *GEX_Init(const char *device, int timeout_ms) } // --- populate callsign look-up table --- - TF_QuerySimple(gex->tf, MSG_LIST_UNITS, /*pld*/ NULL, 0, /*cb*/ listUnitsCb, 0); + TF_QuerySimple(gex->tf, MSG_LIST_UNITS, /*pld*/ NULL, 0, /*cb*/ list_units_lst, 0); GEX_Poll(gex); + TF_AddTypeListener(gex->tf, MSG_UNIT_REPORT, unit_report_lst); + return gex; } @@ -159,14 +174,14 @@ void GEX_Query(GexClient *gex, uint8_t *payload, uint32_t len, TF_Listener listener) { - uint8_t cs = find_callsign_by_name(gex, unit); - assert(cs != 0); + uint8_t callsign = find_callsign_by_name(gex, unit); + assert(callsign != 0); uint8_t *pld = malloc(len + 2); assert(pld != NULL); // prefix the actual payload with the callsign and command bytes. // TODO provide TF API for sending the payload externally in smaller chunks? Will avoid the malloc here - pld[0] = cs; + pld[0] = callsign; pld[1] = cmd; memcpy(pld+2, payload, len); @@ -217,5 +232,3 @@ void GEX_Send(GexClient *gex, { GEX_Query(gex, unit, cmd, payload, len, NULL); } - -// 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 5c12593..7b65a9a 100644 --- a/gex/gex_client.h +++ b/gex/gex_client.h @@ -12,6 +12,18 @@ typedef struct gex_client_ GexClient; +/** Callback for spontaneous reports from units */ +typedef void (*GEX_ReportListener)(GexClient *gex, const char *unit, uint8_t code, const uint8_t *payload, uint32_t len); + +/** + * Bind a report listener + * + * @param gex - client + * @param unit_name - name of the listened for unit, NULL to bind a fallback listener + * @param lst - the listener + */ +void GEX_OnReport(GexClient *gex, const char *unit_name, GEX_ReportListener lst); + /** * Initialize the GEX client * diff --git a/gex/gex_client_internal.h b/gex/gex_client_internal.h index adf8d4d..48d1153 100644 --- a/gex/gex_client_internal.h +++ b/gex/gex_client_internal.h @@ -5,11 +5,12 @@ #ifndef GEX_CLIENT_GEX_CLIENT_INTERNAL_H #define GEX_CLIENT_GEX_CLIENT_INTERNAL_H -struct gex_name_lu { +struct gex_unit_lu { char *name; //!< Unit name char *type; //!< Unit type uint8_t callsign; //!< Unit callsign byte - struct gex_name_lu *next; //!< Pointer to the next entry in this linked list, or NULL if none + GEX_ReportListener report_handler; //!< Report handling function + struct gex_unit_lu *next; //!< Pointer to the next entry in this linked list, or NULL if none }; struct gex_client_ { @@ -23,7 +24,9 @@ struct gex_client_ { TF_Msg sync_query_response; //!< response message, copied here uint8_t sync_query_buffer[TF_MAX_PAYLOAD_RX]; //!< buffer for the rx payload to be copied here - struct gex_name_lu *ulu_head; //!< Units look-up linked list + GEX_ReportListener fallback_report_handler; + + struct gex_unit_lu *ulu_head; //!< Units look-up linked list }; #endif //GEX_CLIENT_GEX_CLIENT_INTERNAL_H diff --git a/gex/gex_helpers.c b/gex/gex_helpers.c new file mode 100644 index 0000000..55bfb63 --- /dev/null +++ b/gex/gex_helpers.c @@ -0,0 +1,51 @@ +// +// Created by MightyPork on 2017/12/19. +// + +#include "gex_helpers.h" +#include "gex_client_internal.h" + +/** Delete recursively all GEX callsign look-up table entries */ +void destroy_unit_lookup(GexClient *gex) +{ + struct gex_unit_lu *next = gex->ulu_head; + while (next != NULL) { + struct gex_unit_lu *cur = next; + next = next->next; + free(cur); + } + gex->ulu_head = NULL; +} + +/** Get lookup entry for unit name */ +struct gex_unit_lu *find_unit_by_callsign(GexClient *gex, uint8_t callsign) +{ + struct gex_unit_lu *next = gex->ulu_head; + while (next != NULL) { + if (next->callsign == callsign) { + return next; + } + next = next->next; + } + return NULL; +} + +/** Get lookup entry for unit name */ +struct gex_unit_lu *find_unit_by_name(GexClient *gex, const char *name) +{ + struct gex_unit_lu *next = gex->ulu_head; + while (next != NULL) { + if (strcmp(next->name, name) == 0) { + return next; + } + next = next->next; + } + return NULL; +} + +/** Get callsign for unit name */ +uint8_t find_callsign_by_name(GexClient *gex, const char *name) +{ + struct gex_unit_lu *lu = find_unit_by_name(gex, name); + return (uint8_t) ((lu == NULL) ? 0 : lu->callsign); +} diff --git a/gex/gex_helpers.h b/gex/gex_helpers.h new file mode 100644 index 0000000..c1605e9 --- /dev/null +++ b/gex/gex_helpers.h @@ -0,0 +1,23 @@ +// +// Created by MightyPork on 2017/12/19. +// + +#ifndef GEX_CLIENT_GEX_HELPERS_H +#define GEX_CLIENT_GEX_HELPERS_H + +#include "gex_client_internal.h" +#include "gex_client.h" + +/** Delete recursively all GEX callsign look-up table entries */ +void destroy_unit_lookup(GexClient *gex); + +/** Get lookup entry for unit name */ +struct gex_unit_lu *find_unit_by_callsign(GexClient *gex, uint8_t callsign); + +/** Get lookup entry for unit name */ +struct gex_unit_lu *find_unit_by_name(GexClient *gex, const char *name); + +/** Get callsign for unit name */ +uint8_t find_callsign_by_name(GexClient *gex, const char *name); + +#endif //GEX_CLIENT_GEX_HELPERS_H