w.i.p. refactor

master
Ondřej Hruška 7 years ago
parent 7afa485724
commit 79f6f64514
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 2
      CMakeLists.txt
  2. 114
      gex/gex_client.c
  3. 53
      gex/gex_client.h
  4. 35
      gex/gex_defines.h
  5. 14
      gex/gex_helpers.c
  6. 4
      gex/gex_helpers.h
  7. 16
      gex/gex_internal.h
  8. 126
      gex/gex_unit.c
  9. 10
      gex/gex_unit.h

@ -24,7 +24,7 @@ set(SOURCE_FILES
gex/protocol/TinyFrame.c
gex/protocol/TinyFrame.h
gex/utils/type_coerce.h
gex/gex_defines.h)
gex/gex_defines.h gex/gex_unit.c gex/gex_unit.h)
include_directories(
gex

@ -36,13 +36,30 @@ static TF_Result unit_report_lst(TinyFrame *tf, TF_Msg *msg)
uint8_t callsign = msg->data[0];
uint8_t rpt_type = msg->data[1];
struct gex_unit_lu *lu = gex_find_unit_by_callsign(gex, callsign);
struct gex_unit *lu = gex_find_unit_by_callsign(gex, callsign);
// NULL object pattern - we use a fake unit if no unit matched.
GexUnit fbu = {
.callsign = 0,
.report_handler = NULL,
.type = "NONE",
.name = "FALLBACK",
.gex = gex, // gex must be available here - this is why we can't have this static or const.
.next = NULL,
};
GexMsg gexMsg = {
.session = msg->frame_id,
.payload = (uint8_t *) (msg->data + 2),
.len = (uint32_t) (msg->len - 2),
.type = rpt_type,
.unit = (lu == NULL) ? fbu : lu,
};
if (lu && lu->report_handler) {
lu->report_handler(gex, lu->name, rpt_type,
msg->data+2, (uint32_t) (msg->len - 2));
lu->report_handler(gexMsg);
} 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));
gex->fallback_report_handler(gexMsg);
}
done:
@ -59,14 +76,14 @@ static TF_Result list_units_lst(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_unit_lu *tail = NULL;
struct gex_unit *tail = NULL;
for(int i = 0; i < count; i++) {
uint8_t callsign = pp_u8(&pp);
pp_string(&pp, buf, 100);
fprintf(stderr, "- Found unit \"%s\" @ callsign %d\n", buf, callsign);
// append
struct gex_unit_lu *lu = malloc(sizeof(struct gex_unit_lu));
struct gex_unit *lu = malloc(sizeof(struct gex_unit));
lu->next = NULL;
lu->type = "UNKNOWN";
lu->name = strdup(buf);
@ -84,19 +101,13 @@ static TF_Result list_units_lst(TinyFrame *tf, TF_Msg *msg)
}
/** Bind report listener */
void GEX_OnReport(GexClient *gex, const char *unit_name, GEX_ReportListener lst)
void GEX_OnReport(GexClient *gex, GexUnit *unit, GexEventListener lst)
{
if (!unit_name) {
if (!unit) {
gex->fallback_report_handler = lst;
}
else {
struct gex_unit_lu *lu = gex_find_unit_by_name(gex, unit_name);
if (!lu) {
fprintf(stderr, "No unit named \"%s\", can't bind listener!", unit_name);
}
else {
lu->report_handler = lst;
}
unit->report_handler = lst;
}
}
@ -142,7 +153,6 @@ GexClient *GEX_Init(const char *device, int timeout_ms)
return gex;
}
/** Try to read from the serial port and process any received bytes with TF */
void GEX_Poll(GexClient *gex)
{
@ -150,16 +160,14 @@ void GEX_Poll(GexClient *gex)
assert(gex != NULL);
ssize_t len = read(gex->acm_fd, pollbuffer, TF_MAX_PAYLOAD_RX);
ssize_t len = read(gex->acm_fd, pollbuffer, TF_MAX_PAYLOAD_RX); // TODO wait only for expect amount?
if (len < 0) {
fprintf(stderr, "ERROR %d in GEX Poll: %s\n", errno, strerror(errno));
} else {
//hexDump("Received", pollbuffer, (uint32_t) len);
TF_Accept(gex->tf, pollbuffer, (size_t) len);
}
}
/** Free the struct */
void GEX_DeInit(GexClient *gex)
{
@ -169,69 +177,3 @@ void GEX_DeInit(GexClient *gex)
TF_DeInit(gex->tf);
free(gex);
}
/** Query a unit */
void GEX_Query(GexClient *gex,
const char *unit, uint8_t cmd,
uint8_t *payload, uint32_t len,
TF_Listener listener)
{
uint8_t callsign = gex_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] = callsign;
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);
if (NULL != listener) {
GEX_Poll(gex);
}
}
/** listener for the synchronous query functionality */
static TF_Result sync_query_lst(TinyFrame *tf, TF_Msg *msg)
{
GexClient *gex = tf->userdata;
// clone the message
memcpy(&gex->sync_query_response, msg, sizeof(TF_Msg));
// clone the buffer
if (msg->len > 0) memcpy(gex->sync_query_buffer, msg->data, msg->len);
// re-link the buffer
gex->sync_query_response.data = gex->sync_query_buffer;
gex->sync_query_ok = true;
}
/** Query a unit. The response is expected to be relatively short. */
TF_Msg *GEX_SyncQuery(GexClient *gex,
const char *unit, uint8_t cmd,
uint8_t *payload, uint32_t len)
{
gex->sync_query_ok = false;
memset(&gex->sync_query_response, 0, sizeof(TF_Msg));
GEX_Query(gex, unit, cmd, payload, len, sync_query_lst);
return gex->sync_query_ok ? &gex->sync_query_response : NULL;
}
/** Command a unit (same like query, but without listener and without polling) */
void GEX_Send(GexClient *gex,
const char *unit, uint8_t cmd,
uint8_t *payload, uint32_t len)
{
GEX_Query(gex, unit, cmd, payload, len, NULL);
}

@ -12,13 +12,15 @@
#include "gex_defines.h"
/**
* Bind a report listener
* Bind a report listener. The listener is called with a message object when
* a spontaneous report is received. If no known unit matched the report,
* a dummy unit is provided to avoid NULL access.
*
* @param gex - client
* @param unit_name - name of the listened for unit, NULL to bind a fallback listener
* @param unit - the handled unit, NULL to bind a fallback listener (fallback may receive events from all unhandled units)
* @param lst - the listener
*/
void GEX_OnReport(GexClient *gex, const char *unit_name, GEX_ReportListener lst);
void GEX_OnReport(GexClient *gex, GexUnit *unit, GexEventListener lst);
/**
* Initialize the GEX client
@ -42,49 +44,4 @@ void GEX_Poll(GexClient *gex);
*/
void GEX_DeInit(GexClient *gex);
/**
* 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
*/
void GEX_Query(GexClient *gex,
const char *unit, uint8_t cmd,
uint8_t *payload, uint32_t len,
TF_Listener listener);
/**
* Query a unit and return the response. The resulting message (including payload) is
* copied to a internal holder variable. Do not attempt to free it!
*
* @attention Calling `GEX_SyncQuery` destroys the previous sync query message and payload.
*
* @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
* @return a clone of the response, or NULL if none arrived in time.
*/
TF_Msg *GEX_SyncQuery(GexClient *gex,
const char *unit, uint8_t cmd,
uint8_t *payload, uint32_t len);
/**
* Send a command with response no listener
*
* @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
*/
void GEX_Send(GexClient *gex,
const char *unit, uint8_t cmd,
uint8_t *payload, uint32_t len);
#endif //GEX_CLIENT_GEX_CLIENT_H

@ -8,9 +8,40 @@
#include <stdint.h>
#include <stdbool.h>
typedef struct gex_client_ GexClient;
typedef TF_ID GexSession;
typedef struct gex_client GexClient;
typedef struct gex_unit GexUnit;
typedef struct gex_msg GexMsg;
/** 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);
typedef void (*GexEventListener)(GexMsg msg);
/**
* GEX message, used e.g. as a return value to static query.
* Contains all needed information to lead a multi-part dialogue.
*/
struct gex_msg {
GexUnit *unit;
uint8_t *payload;
uint32_t len;
GexSession session;
union {
uint8_t cmd;
uint8_t type;
};
};
/**
* GEX unit instance, allocated based on configuration read from the GEX board.
*/
struct gex_unit {
GexClient *gex; //!< Client instance
char *name; //!< Unit name (loaded, malloc'd)
char *type; //!< Unit type (loaded, malloc'd)
uint8_t callsign; //!< Unit callsign
GexEventListener report_handler; //!< Report handling function
struct gex_unit *next; //!< Pointer to the next entry in this linked list, or NULL if none
};
#endif //GEX_CLIENT_GEX_DEFINES_H

@ -10,9 +10,9 @@
/** Delete recursively all GEX callsign look-up table entries */
void gex_destroy_unit_lookup(GexClient *gex)
{
struct gex_unit_lu *next = gex->ulu_head;
struct gex_unit *next = gex->ulu_head;
while (next != NULL) {
struct gex_unit_lu *cur = next;
struct gex_unit *cur = next;
next = next->next;
free(cur);
}
@ -20,9 +20,9 @@ void gex_destroy_unit_lookup(GexClient *gex)
}
/** Get lookup entry for unit name */
struct gex_unit_lu *gex_find_unit_by_callsign(GexClient *gex, uint8_t callsign)
struct gex_unit *gex_find_unit_by_callsign(GexClient *gex, uint8_t callsign)
{
struct gex_unit_lu *next = gex->ulu_head;
struct gex_unit *next = gex->ulu_head;
while (next != NULL) {
if (next->callsign == callsign) {
return next;
@ -33,9 +33,9 @@ struct gex_unit_lu *gex_find_unit_by_callsign(GexClient *gex, uint8_t callsign)
}
/** Get lookup entry for unit name */
struct gex_unit_lu *gex_find_unit_by_name(GexClient *gex, const char *name)
struct gex_unit *gex_find_unit_by_name(GexClient *gex, const char *name)
{
struct gex_unit_lu *next = gex->ulu_head;
struct gex_unit *next = gex->ulu_head;
while (next != NULL) {
if (strcmp(next->name, name) == 0) {
return next;
@ -48,6 +48,6 @@ struct gex_unit_lu *gex_find_unit_by_name(GexClient *gex, const char *name)
/** Get callsign for unit name */
uint8_t gex_find_callsign_by_name(GexClient *gex, const char *name)
{
struct gex_unit_lu *lu = gex_find_unit_by_name(gex, name);
struct gex_unit *lu = gex_find_unit_by_name(gex, name);
return (uint8_t) ((lu == NULL) ? 0 : lu->callsign);
}

@ -12,10 +12,10 @@
void gex_destroy_unit_lookup(GexClient *gex);
/** Get lookup entry for unit name */
struct gex_unit_lu *gex_find_unit_by_callsign(GexClient *gex, uint8_t callsign);
struct gex_unit *gex_find_unit_by_callsign(GexClient *gex, uint8_t callsign);
/** Get lookup entry for unit name */
struct gex_unit_lu *gex_find_unit_by_name(GexClient *gex, const char *name);
struct gex_unit *gex_find_unit_by_name(GexClient *gex, const char *name);
/** Get callsign for unit name */
uint8_t gex_find_callsign_by_name(GexClient *gex, const char *name);

@ -9,15 +9,7 @@
#include <stdbool.h>
#include "gex_client.h"
struct gex_unit_lu {
char *name; //!< Unit name
char *type; //!< Unit type
uint8_t callsign; //!< Unit callsign byte
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_ {
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
@ -25,12 +17,12 @@ struct gex_client_ {
// synchronous query "hacks"
bool sync_query_ok; //!< flag that the query response was received
TF_Msg sync_query_response; //!< response message, copied here
GexMsg 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
GEX_ReportListener fallback_report_handler;
GexEventListener fallback_report_handler;
struct gex_unit_lu *ulu_head; //!< Units look-up linked list
struct gex_unit *ulu_head; //!< Units look-up linked list
};
#endif //GEX_CLIENT_GEX_CLIENT_INTERNAL_H

@ -0,0 +1,126 @@
//
// Created by MightyPork on 2017/12/19.
//
#include <malloc.h>
#include <assert.h>
#include "gex_defines.h"
#include "gex_helpers.h"
#include "gex_message_types.h"
/**
* Low level query
*
* @param unit - GEX unit to address. The unit is available in userdata1 of the response message, if any
* @param cmd - command byte
* @param payload - payload buffer
* @param len - payload length
* @param lst - TF listener to handle the response, can be NULL
* @param userdata2 userdata2 argument for the TF listener's message
*/
static void GEX_LL_Query(GexUnit *unit, uint8_t cmd, uint8_t *payload, uint32_t len, TF_Listener lst, void *userdata2)
{
GexClient *gex = unit->gex;
uint8_t callsign = gex_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] = callsign;
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);
msg.userdata = unit;
msg.userdata2 = userdata2;
TF_Query(gex->tf, &msg, lst, 0);
}
free(pld);
}
/** Send with no listener, don't wait for response */
void GEX_Send(GexUnit *unit, uint8_t cmd, uint8_t *payload, uint32_t len)
{
GEX_LL_Query(unit, cmd, payload, len, NULL, NULL);
}
/** listener for the synchronous query functionality */
static TF_Result sync_query_lst(TinyFrame *tf, TF_Msg *msg)
{
GexClient *gex = tf->userdata;
// clone the message
gex->sync_query_response.len = msg->len;
gex->sync_query_response.session = msg->frame_id;
gex->sync_query_response.unit = msg->userdata;
gex->sync_query_response.type = msg->type;
// clone the buffer
if (msg->len > 0) memcpy(gex->sync_query_buffer, msg->data, msg->len);
// re-link the buffer
gex->sync_query_response.payload = gex->sync_query_buffer;
gex->sync_query_ok = true;
}
/** Static query */
GexMsg GEX_Query(GexUnit *unit, uint8_t cmd, uint8_t *payload, uint32_t len)
{
GexClient *gex = unit->gex;
gex->sync_query_ok = false;
// Default response that will be used if nothing is received
gex->sync_query_response.unit = unit;
gex->sync_query_response.session = 0;
gex->sync_query_response.type = MSG_ERROR;
sprintf((char *) gex->sync_query_buffer, "TIMEOUT");
gex->sync_query_response.len = (uint32_t) strlen("TIMEOUT");
gex->sync_query_response.payload = gex->sync_query_buffer;
GEX_LL_Query(unit, cmd, payload, len, sync_query_lst, NULL);
GEX_Poll(gex);
if (!gex->sync_query_ok) {
fprintf(stderr, "No response to query of unit %s!", unit->name);
}
return gex->sync_query_response;
}
/** listener for the synchronous query functionality */
static TF_Result async_query_lst(TinyFrame *tf, TF_Msg *msg)
{
GexMsg gexMsg;
// clone the message
gexMsg.len = msg->len;
gexMsg.session = msg->frame_id;
gexMsg.unit = msg->userdata; // gex is accessible via the unit
gexMsg.type = msg->type;
gexMsg.payload = (uint8_t *) msg->data;
GexEventListener lst = msg->userdata2;
assert(lst != NULL);
lst(gexMsg);
}
/** Sync query, without poll */
void GEX_QueryAsync(GexUnit *unit, uint8_t cmd, uint8_t *payload, uint32_t len, GexEventListener lst)
{
GexClient *gex = unit->gex;
gex->sync_query_ok = false;
memset(&gex->sync_query_response, 0, sizeof(GexMsg));
// Default response that will be used if nothing is received
gex->sync_query_response.type = MSG_ERROR;
sprintf((char *) gex->sync_query_buffer, "TIMEOUT");
gex->sync_query_response.len = (uint32_t) strlen("TIMEOUT");
GEX_LL_Query(unit, cmd, payload, len, async_query_lst, lst);
}

@ -0,0 +1,10 @@
//
// Created by MightyPork on 2017/12/19.
//
#ifndef GEX_CLIENT_GEX_UNIT_H
#define GEX_CLIENT_GEX_UNIT_H
#endif //GEX_CLIENT_GEX_UNIT_H
Loading…
Cancel
Save