diff --git a/control_tab.c b/control_tab.c index 735a837..260aa4c 100644 --- a/control_tab.c +++ b/control_tab.c @@ -6,9 +6,7 @@ #include "tcpsocket.h" #include "sockaddr.h" -int tdc_parse_data(int fd); -void tdc_flush_vars(void); -void tdc_send_hello(int fd); +#include "tdc_store.h" enum { STAT_DISCONNECTED = 0, diff --git a/gtdc.c b/gtdc.c index c0f2c72..031fd66 100644 --- a/gtdc.c +++ b/gtdc.c @@ -1,14 +1,18 @@ #include #include +#include "tdc_store.h" +#include "variable_tab.h" + gint control_tab_init(GtkNotebook *notebook); -gint variable_tab_init(GtkNotebook *notebook); gint graph_tab_init(GtkNotebook *notebook); int main(int argc, char *argv[]) { gtk_init (&argc, &argv); + tdc_init(); + GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(window), " gTDC v0.1 "); gtk_window_set_default_size(GTK_WINDOW(window), 800, 600); diff --git a/tdc_store.c b/tdc_store.c new file mode 100644 index 0000000..111096d --- /dev/null +++ b/tdc_store.c @@ -0,0 +1,279 @@ +#include +#include +#include +#include + +#include + +#include "tdc_store.h" +#include "tdcproto.h" + +#include "variable_tab.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +static struct tdc_var * var_map[256]; +static GByteArray *stream; + +static void destroy_var(int id) +{ + g_free(var_map[id]); + var_map[id] = NULL; +} + +static void create_var(int id, uint32_t flags, char *name, int len) +{ + id &= 0xFF; + + if (var_map[id] != NULL) + destroy_var(id); + + var_map[id] = g_malloc0(sizeof(struct tdc_var) + len + 1); + var_map[id]->id = id; + var_map[id]->flags = flags; + + strncpy(var_map[id]->name, name, len); + var_map[id]->name[len] = '\0'; + + vartab_add_var(var_map[id]); +} + +static void update_var(int id, uint8_t *data, int len) +{ + memcpy(&(var_map[id & 0xFF]->data_uint8), data, len); +} + +#if 0 +static char * stream_print(void) +{ + int pos = 0, i = 0, j; + char *buf = g_malloc(stream->len * 4 + 64); + + while (pos < stream->len) { + i += sprintf(buf + i, "%04X: ", pos); + for (j = 0; j < 16; j++) { + if (pos + j < stream->len) + i += sprintf(buf + i, "%02X", stream->data[pos + j]); + else + i += sprintf(buf + i, " "); + + if (j % 2) + buf[i++] = ' '; + } + + for (j = 0; j < 16; j++) { + if (pos + j < stream->len) { + unsigned char val = stream->data[pos + j]; + if (val >= 0x20 && val < 0x80) + buf[i++] = val; + else + buf[i++] = '.'; + } else { + buf[i++] = ' '; + } + } + + pos += 16; + buf[i++] = '\r'; + buf[i++] = '\n'; + } + buf[i] = 0; + return buf; +} +#endif + +static int parse_size_helper(int available, + int packet_size, + int expected_base_size, + int packet_data_size, + const char *name) +{ + /* not enough data in buffer */ + if (available < expected_base_size) { + printf("received incomplete %s\n", name); + return 0; + } + + /* retest, after we know that packet_data_size is valid */ + if (available < (expected_base_size + packet_data_size)) { + printf("received incomplete %s\n", name); + return 0; + } + + /* packet has wrong size */ + if (packet_size != (expected_base_size + packet_data_size)) { + printf("received invalid %s\n", name); + return -1; + } + +// printf("received %s\n", name); + return packet_size; +} + +static int parse(int fd) +{ + int address = (stream->data[0] & TDC_ADDRMASK) >> 4; + + int remove = 1; + + switch (stream->data[0] & (TDC_REPLY | TDC_OPCODEMASK)) { + case TDC_HELLO: { + struct tdc_pkt_header *pkt = (struct tdc_pkt_header *)stream->data; + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_HELLO"); + /* do nothing, we'll never receive TDC_HELLOs */ + } break; + + case (TDC_HELLO | TDC_REPLY): { + struct tdc_hello_reply *pkt = (struct tdc_hello_reply *)stream->data; + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_HELLO REPLY"); + if (remove <= 0) + break; + + printf("ADDR%d: '%32s'\n", address, pkt->name); + + struct tdc_pkt_header pkt2; + pkt2.cmd = (address << 4) | TDC_GETVARS; + pkt2.size = sizeof(pkt2); + write(fd, &pkt2, pkt2.size); + + } break; + + case TDC_GETVARS: { + struct tdc_pkt_header *pkt = (struct tdc_pkt_header *)stream->data; + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_GETVARS"); + /* do nothing, we'll never receive TDC_GETVARSs */ + } break; + + case (TDC_GETVARS | TDC_REPLY): { + struct tdc_getvars_reply *pkt = (struct tdc_getvars_reply *)stream->data; + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), + pkt->name_len, "TDC_GETVARS REPLY"); + + if (remove <= 0) + break; + + create_var(pkt->id, pkt->flags, pkt->name, pkt->name_len); + printf("create_var(%d, 0x%x, '%s', %d)\n", + pkt->id, var_map[pkt->id]->flags, var_map[pkt->id]->name, pkt->name_len); + } break; + + case TDC_GETVALUE: { + struct tdc_getvalue_request *pkt = (struct tdc_getvalue_request *)stream->data; + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_GETVALUE"); + /* do nothing, we'll never receive TDC_GETVALUEs */ + } break; + + case (TDC_GETVALUE | TDC_REPLY): { + struct tdc_getvalue_reply *pkt = (struct tdc_getvalue_reply *)stream->data; + + /* check base packet */ + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_GETVALUE REPLY"); + if (remove <= 0) + break; + + if (var_map[pkt->id & 0xFF] == NULL) { + printf("unknown variable %d\n", pkt->id); + break; + } + + int varsize = (var_map[pkt->id & 0xFF]->flags & TDC_SIZEMASK); + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), varsize, "TDC_GETVALUE REPLY"); + // TODO: adjust remove on error (valid reply with invalid data size) + + update_var(pkt->id, pkt->data, varsize); + } break; + + case TDC_SETVALUE: { + struct tdc_setvalue_request *pkt = (struct tdc_setvalue_request *)stream->data; + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_SETVALUE"); + if (remove <= 0) + break; + + if (var_map[pkt->id & 0xFF] == NULL) { + printf("unknown variable %d\n", pkt->id); + break; + } + + int varsize = (var_map[pkt->id & 0xFF]->flags & TDC_SIZEMASK); + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), varsize, "TDC_GETVALUE REPLY"); + // TODO: adjust remove on error (valid reply with invalid data size) + + /* do nothing, we'll never receive TDC_SETVALUEs */ + } break; + + case (TDC_SETVALUE | TDC_REPLY): + break; + + case TDC_REQVALUES: { + struct tdc_reqvalues_request *pkt = (struct tdc_reqvalues_request *)stream->data; + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_REQVALUES"); + /* do nothing, we'll never receive TDC_REQVALUESs */ + } break; + + case (TDC_REQVALUES | TDC_REPLY): { + struct tdc_reqvalues_reply *pkt = (struct tdc_reqvalues_reply *)stream->data; + remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_REQVALUES"); + if (remove <= 0) + break; + + // TODO: output pkt->timestamp, pkt->cnt + } break; + + case TDC_TERMINAL: + case (TDC_TERMINAL | TDC_REPLY): + break; + } + + if (remove != 0) + g_byte_array_remove_range(stream, 0, (remove > 0) ? remove : -remove); + + return remove; +} + +int tdc_parse_data(int fd) +{ + char buf[256]; + int len = read(fd, buf, sizeof(buf)); + + if (len > 0) { + g_byte_array_append(stream, (guint8 *)buf, len); + + while (stream->len >= 2) { + if (parse(fd) == 0) + break; + } + } + + return len; +} + +void tdc_flush_vars(void) +{ + int i; + for (i = 0; i < ARRAY_SIZE(var_map); i++) { + if (var_map[i] == NULL) + continue; + + destroy_var(i); + } + + if (stream->len) + g_byte_array_remove_range(stream, 0, stream->len); +} + +void tdc_send_hello(int fd) +{ + int addr; + for (addr = 1; addr < 8; addr++) { + struct tdc_pkt_header pkt; + pkt.cmd = (addr << 4) | TDC_HELLO; + pkt.size = sizeof(pkt); + + write(fd, &pkt, pkt.size); + } +} + +void tdc_init(void) +{ + stream = g_byte_array_new(); +} diff --git a/tdc_store.h b/tdc_store.h new file mode 100644 index 0000000..5b893de --- /dev/null +++ b/tdc_store.h @@ -0,0 +1,25 @@ +#ifndef TDC_STORE_H_ +#define TDC_STORE_H_ + +#include + +struct tdc_var { + uint32_t id; + uint32_t flags; + union { + uint8_t data_uint8; + uint16_t data_uint16; + uint32_t data_uint32; + uint64_t data_uint64; + float data_float; + double data_double; + }; + char name[0]; +}; + +int tdc_parse_data(int fd); +void tdc_flush_vars(void); +void tdc_send_hello(int fd); +void tdc_init(void); + +#endif /* TDC_STORE_H_ */ diff --git a/tdcproto.h b/tdcproto.h new file mode 100644 index 0000000..71c8ad5 --- /dev/null +++ b/tdcproto.h @@ -0,0 +1,101 @@ +#ifndef TDCPROTO_H_ +#define TDCPROTO_H_ + +#include + +/* + * 0: this is a request (host -> board) + * 1: this is a reply (board -> host) + */ +#define TDC_DIR 0x80 +#define TDC_REPLY TDC_DIR + +/* + * TDC_DIR = 0: destination address + * TDC_DIR = 1: source address + */ +#define TDC_ADDRMASK 0x70 +#define TDC_ADDR0 0x00 // host (dynamic!, sends to interface of last hello) +#define TDC_ADDR1 0x10 // flightcontrol +#define TDC_ADDR2 0x20 // missioncontrol +#define TDC_ADDR3 0x30 // videocontrol +#define TDC_ADDR4 0x40 +#define TDC_ADDR5 0x50 +#define TDC_ADDR6 0x60 +#define TDC_ADDR7 0x70 + +#define TDC_OPCODEMASK 0x0F +#define TDC_HELLO 0x00 // sets the path/interface to the host, reply is a info string +#define TDC_GETVARS 0x01 // request variable names, many replies +#define TDC_GETVALUE 0x02 // get one value, one reply +#define TDC_SETVALUE 0x03 // set one value, no reply +#define TDC_REQVALUES 0x04 // registers a periodic update, timed replies +#define TDC_TERMINAL 0x05 // stdout data + +#define TDC_USERDATA 0x0F // user-defined data e.g. between boards + +struct tdc_pkt_header { + uint8_t cmd; // TDC_* + uint8_t size; // size including this header +} __attribute__ ((packed)); + +struct tdc_hello_reply { + uint8_t cmd; + uint8_t size; + char name[32]; // name of device, version string +} __attribute__ ((packed)); + +struct tdc_getvars_reply { + uint8_t cmd; + uint8_t size; + uint8_t id; // variable ID (max 256 vars / board) + uint32_t flags; // variable parameters (type, size, ro/rw) + uint8_t name_len; // size of variable name + char name[0]; // variable name, excluding '\0' +} __attribute__ ((packed)); + +struct tdc_getvalue_request { + uint8_t cmd; + uint8_t size; + uint8_t id; // variable ID (max 256 vars / board) +} __attribute__ ((packed)); + +struct tdc_getvalue_reply { + uint8_t cmd; + uint8_t size; + uint8_t id; // variable ID (max 256 vars / board) + uint8_t data[0]; // variable data 1-8 bytes +} __attribute__ ((packed)); + +struct tdc_setvalue_request { + uint8_t cmd; + uint8_t size; + uint8_t id; // variable ID (max 256 vars / board) + uint8_t data[0]; // variable data 1-8 bytes +} __attribute__ ((packed)); + +struct tdc_reqvalues_request { + uint8_t cmd; + uint8_t size; + uint16_t interval; // interval in ms + uint32_t varmap[8]; // bitmap of variables (32 * 8 = 256) +} __attribute__ ((packed)); + +struct tdc_reqvalues_reply { + uint8_t cmd; + uint8_t size; + uint32_t timestamp; // internal jiffie count + uint8_t cnt; // number of variables +} __attribute__ ((packed)); + +#define TDC_SIZEMASK 0x000F + +#define TDC_TYPEMASK 0x00F0 +#define TDC_UNSIGNED 0x0000 +#define TDC_SIGNED 0x0010 +#define TDC_FP 0x0020 +#define TDC_FIXED 0x0040 + +#define TDC_READONLY 0x0100 + +#endif /* TDCPROTO_H_ */ diff --git a/variable_tab.c b/variable_tab.c index 37dc53b..11b53b8 100644 --- a/variable_tab.c +++ b/variable_tab.c @@ -1,21 +1,48 @@ #include +#include "tdc_store.h" +#include "tdcproto.h" + enum { - COL_GRAPH = 0, - COL_VALUE, - COL_TYPE, - COL_NAME, + COL_ID = 0, /* UINT */ + COL_GRAPH, /* BOOLEAN */ + COL_VALUE, /* PTR?, with cell_data_func */ + COL_VALUE_EDIT, /* BOOLEAN */ + COL_TYPE, /* STRING */ + COL_NAME, /* STRING */ }; +static GtkListStore *list_store; + +static void cell_graph_toggle(GtkCellRendererToggle *cell, gchar *path_string, gpointer user_data) +{ + GtkTreeIter it; + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(user_data), &it, path_string); + + gboolean toggle; + + gtk_tree_model_get(GTK_TREE_MODEL(user_data), &it, + COL_GRAPH, &toggle, + -1); + + gtk_list_store_set(GTK_LIST_STORE(user_data), &it, + COL_GRAPH, !toggle, + -1); +} + +static void cell_value_edited(GtkCellRendererText *cell, gchar *path_string, gchar *new_text, gpointer user_data) +{ + printf("cell_value_edited\n"); +} + gint variable_tab_init(GtkNotebook *notebook) { - /* graph, value, type, name */ - GtkListStore *list_store = gtk_list_store_new(1, - G_TYPE_BOOLEAN, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING); + list_store = gtk_list_store_new(6, + G_TYPE_UINT, G_TYPE_BOOLEAN, + G_TYPE_STRING, G_TYPE_BOOLEAN, + G_TYPE_STRING, G_TYPE_STRING); - GtkWidget *view = gtk_tree_view_new(); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(list_store)); + GtkWidget *view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store)); g_object_unref(list_store); GtkTreeViewColumn *col; @@ -25,15 +52,22 @@ gint variable_tab_init(GtkNotebook *notebook) gtk_tree_view_column_set_title(col, "Graph"); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); renderer = gtk_cell_renderer_toggle_new(); + g_object_set(renderer, "activatable", TRUE, NULL); + g_signal_connect(renderer, "toggled", (GCallback)cell_graph_toggle, list_store); gtk_tree_view_column_pack_start(col, renderer, TRUE); gtk_tree_view_column_add_attribute(col, renderer, "active", COL_GRAPH); + col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "Value"); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); renderer = gtk_cell_renderer_text_new(); +// gtk_tree_view_column_set_cell_data_func(col, renderer, cell_value_func, NULL, NULL); + g_signal_connect(renderer, "edited", (GCallback)cell_value_edited, NULL); gtk_tree_view_column_pack_start(col, renderer, TRUE); gtk_tree_view_column_add_attribute(col, renderer, "text", COL_VALUE); + gtk_tree_view_column_add_attribute(col, renderer, "editable", COL_VALUE_EDIT); + col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "Type"); @@ -42,6 +76,7 @@ gint variable_tab_init(GtkNotebook *notebook) gtk_tree_view_column_pack_start(col, renderer, TRUE); gtk_tree_view_column_add_attribute(col, renderer, "text", COL_TYPE); + col = gtk_tree_view_column_new(); gtk_tree_view_column_set_title(col, "Name"); gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); @@ -49,6 +84,64 @@ gint variable_tab_init(GtkNotebook *notebook) gtk_tree_view_column_pack_start(col, renderer, TRUE); gtk_tree_view_column_add_attribute(col, renderer, "text", COL_NAME); + GtkWidget *scrollbar = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollbar), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_container_add(GTK_CONTAINER(scrollbar), view); + GtkWidget *label = gtk_label_new(" Variables "); - return gtk_notebook_append_page(GTK_NOTEBOOK(notebook), view, label); + return gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrollbar, label); +} + +void vartab_add_var(struct tdc_var *var) +{ + GtkTreeIter it; + gtk_list_store_append(list_store, &it); + + char typestr[16]; + + int size = (var->flags & TDC_SIZEMASK); + + switch (var->flags & TDC_TYPEMASK) { + case TDC_UNSIGNED: + snprintf(typestr, sizeof(typestr), "uint%d_t", size * 8); + break; + + case TDC_SIGNED: + snprintf(typestr, sizeof(typestr), "int%d_t", size * 8); + break; + + case TDC_FP: + if (size == sizeof(float)) + snprintf(typestr, sizeof(typestr), "float"); + + else if (size == sizeof(double)) + snprintf(typestr, sizeof(typestr), "double"); + + else + snprintf(typestr, sizeof(typestr), "float(%d)", size * 8); + break; + + case TDC_FIXED: + snprintf(typestr, sizeof(typestr), "fixed(%d)", size * 8); + break; + } + + gtk_list_store_set(list_store, &it, + COL_ID, var->id, + COL_GRAPH, FALSE, + COL_VALUE, "none", + COL_VALUE_EDIT, !(var->flags & TDC_READONLY), + COL_TYPE, typestr, + COL_NAME, var->name, + -1); +} + +void vartab_update_var(struct tdc_var *var) +{ + +} + +void vartab_remove_var(struct tdc_var *var) +{ + } diff --git a/variable_tab.h b/variable_tab.h new file mode 100644 index 0000000..3c1e5c3 --- /dev/null +++ b/variable_tab.h @@ -0,0 +1,13 @@ +#ifndef VARIABLE_TAB_H_ +#define VARIABLE_TAB_H_ + +#include +#include "tdc_store.h" + +gint variable_tab_init(GtkNotebook *notebook); + +void vartab_add_var(struct tdc_var *var); +void vartab_update_var(struct tdc_var *var); +void vartab_remove_var(struct tdc_var *var); + +#endif /* VARIABLE_TAB_H_ */