From be3f4fed7b863c8cdaabe32b61b65a8b3cd11355 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 17 May 2009 00:13:45 -0700 Subject: [PATCH] Add lots more aoview UI bits Logs data to files, displays current state in window. Signed-off-by: Keith Packard --- aoview/.gitignore | 2 + aoview/Makefile | 7 +- aoview/aoview.glade | 301 +++++++++++++++++-------------------- aoview/aoview.h | 44 +++++- aoview/aoview_dev.c | 41 +---- aoview/aoview_dev_dialog.c | 26 +--- aoview/aoview_log.c | 192 +++++++++++++++++++++++ aoview/aoview_main.c | 4 + aoview/aoview_monitor.c | 9 +- aoview/aoview_state.c | 43 ++++-- aoview/aoview_table.c | 62 ++++++++ aoview/aoview_util.c | 91 +++++++++++ 12 files changed, 583 insertions(+), 239 deletions(-) create mode 100644 aoview/.gitignore create mode 100644 aoview/aoview_log.c create mode 100644 aoview/aoview_table.c create mode 100644 aoview/aoview_util.c diff --git a/aoview/.gitignore b/aoview/.gitignore new file mode 100644 index 00000000..03c0c7da --- /dev/null +++ b/aoview/.gitignore @@ -0,0 +1,2 @@ +*.o +aoview diff --git a/aoview/Makefile b/aoview/Makefile index bdf7853c..253a160e 100644 --- a/aoview/Makefile +++ b/aoview/Makefile @@ -1,4 +1,4 @@ -MODULES=gtk+-2.0 libglade-2.0 +MODULES=gtk+-2.0 libglade-2.0 gconf-2.0 INCLUDES=$(shell pkg-config --cflags $(MODULES)) -I.. WARN= -Wall -Wpointer-arith -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -fno-strict-aliasing CFLAGS=$(INCLUDES) -O0 -g $(WARN) @@ -11,7 +11,10 @@ SRC = \ aoview_serial.c \ aoview_monitor.c \ aoview_state.c \ - aoview_convert.c + aoview_convert.c \ + aoview_log.c \ + aoview_table.c \ + aoview_util.c INC = \ aoview.h diff --git a/aoview/aoview.glade b/aoview/aoview.glade index e60f17f7..4b1566dc 100644 --- a/aoview/aoview.glade +++ b/aoview/aoview.glade @@ -158,6 +158,47 @@ + + + True + _Log + True + + + True + + + _New log + True + True + False + + + True + gtk-new + + + + + + + _Configure Log + True + True + False + + + + True + gtk-preferences + + + + + + + + True @@ -185,166 +226,12 @@ - + True - 7 - 2 - - - True - Altitude - - - - - True - Velocity - - - 1 - 2 - - - - - True - Acceleration - - - 2 - 3 - - - - - True - Latitude - - - 3 - 4 - - - - - True - Longitude - - - 4 - 5 - - - - - True - Range - - - 5 - 6 - - - - - True - Direction - - - 6 - 7 - - - - - True - 0m - True - True - - - 1 - 2 - - - - - True - 150m/s - True - - - 1 - 2 - 1 - 2 - - - - - True - 50m/s² - True - - - 1 - 2 - 2 - 3 - - - - - True - 45° 31' 24'' - True - - - 1 - 2 - 3 - 4 - - - - - True - 122° 40' 34''W - True - - - 1 - 2 - 4 - 5 - - - - - True - 500m - True - - - 1 - 2 - 5 - 6 - - - - - True - 45° - True - - - 1 - 2 - 6 - 7 - - + True + False + False + both 1 @@ -385,13 +272,15 @@ True end - - gtk-connect + + gtk-cancel + 1 True True True True True + False @@ -400,15 +289,70 @@ - + + gtk-connect + True + True + True + True + True + True + + + False + False + 1 + + + + + False + end + 0 + + + + + + + 5 + Configure Log Directory + dialog + False + select-folder + + + True + vertical + 2 + + + True + end + + gtk-cancel - 1 True True True - True True - + + + + False + False + 0 + + + + + gtk-ok + True + True + True + True + True + True False @@ -426,4 +370,31 @@ + + 5 + popup + normal + True + error + close + Cannot create log file + + + True + vertical + 2 + + + True + end + + + False + end + 0 + + + + + diff --git a/aoview/aoview.h b/aoview/aoview.h index 028b2f16..a3214128 100644 --- a/aoview/aoview.h +++ b/aoview/aoview.h @@ -18,8 +18,7 @@ #ifndef _AOVIEW_H_ #define _AOVIEW_H_ -#include -#include +#define _GNU_SOURCE #include #include @@ -33,6 +32,10 @@ #include #include +#include +#include +#include + struct usbdev { char *sys; char *tty; @@ -115,4 +118,41 @@ aoview_pres_to_altitude(int16_t pres); int16_t aoview_altitude_to_pres(int16_t alt); +char * +aoview_fullname (char *dir, char *file); + +char * +aoview_basename(char *file); + +GtkTreeViewColumn * +aoview_add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width); + +int +aoview_mkdir(char *dir); + +void +aoview_log_init(GladeXML *xml); + +void +aoview_log_set_serial(int serial); + +int +aoview_log_get_serial(void); + +void +aoview_log_printf(char *format, ...); + +void +aoview_table_start(void); + +void +aoview_table_add_row(char *label, char *format, ...); + +void +aoview_table_finish(void); + +void +aoview_table_init(GladeXML *xml); + + #endif /* _AOVIEW_H_ */ diff --git a/aoview/aoview_dev.c b/aoview/aoview_dev.c index 5bb3cc9f..a326ad73 100644 --- a/aoview/aoview_dev.c +++ b/aoview/aoview_dev.c @@ -19,30 +19,10 @@ #include #include -static char * -fullname (char *dir, char *file) -{ - char *new; - int dlen = strlen (dir); - int flen = strlen (file); - int slen = 0; - - if (dir[dlen-1] != '/') - slen = 1; - new = malloc (dlen + slen + flen + 1); - if (!new) - return 0; - strcpy(new, dir); - if (slen) - strcat (new, "/"); - strcat(new, file); - return new; -} - static char * load_string(char *dir, char *file) { - char *full = fullname(dir, file); + char *full = aoview_fullname(dir, file); char line[4096]; char *r; FILE *f; @@ -79,17 +59,6 @@ load_hex(char *dir, char *file) return i; } -static char * -basename(char *file) -{ - char *b; - - b = strrchr(file, '/'); - if (!b) - return file; - return b + 1; -} - static int dir_filter_tty(const struct dirent *d) { @@ -110,20 +79,20 @@ usb_tty(char *sys) int ntty; char *tty; - base = basename(sys); + base = aoview_basename(sys); num_configs = load_hex(sys, "bNumConfigurations"); num_interfaces = load_hex(sys, "bNumInterfaces"); for (config = 1; config <= num_configs; config++) { for (interface = 0; interface < num_interfaces; interface++) { sprintf(endpoint_base, "%s:%d.%d", base, config, interface); - endpoint_full = fullname(sys, endpoint_base); + endpoint_full = aoview_fullname(sys, endpoint_base); ntty = scandir(endpoint_full, &namelist, dir_filter_tty, alphasort); free(endpoint_full); if (ntty) { - tty = fullname("/dev", namelist[0]->d_name + 4); + tty = aoview_fullname("/dev", namelist[0]->d_name + 4); free(namelist); return tty; } @@ -196,7 +165,7 @@ aoview_usb_scan(struct usbdev ***devs_ret) if (!n) return 0; for (e = 0; e < n; e++) { - dir = fullname(USB_DEVICES, ents[e]->d_name); + dir = aoview_fullname(USB_DEVICES, ents[e]->d_name); dev = usb_scan_device(dir); free(dir); if (dev->idVendor == 0xfffe && dev->tty) { diff --git a/aoview/aoview_dev_dialog.c b/aoview/aoview_dev_dialog.c index f1165e12..00947404 100644 --- a/aoview/aoview_dev_dialog.c +++ b/aoview/aoview_dev_dialog.c @@ -17,26 +17,6 @@ #include "aoview.h" -static GtkTreeViewColumn * -add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width) -{ - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - - renderer = gtk_cell_renderer_text_new (); - g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL); - g_object_set(renderer, "width-chars", width, NULL); - column = gtk_tree_view_column_new_with_attributes (title, renderer, - "text", model_column, - NULL); - gtk_tree_view_column_set_resizable (column, FALSE); - gtk_tree_view_append_column (view, column); - - return column; -} - - - static void aoview_dev_dialog_map(GtkWidget *widget, gpointer data) { @@ -118,9 +98,9 @@ aoview_dev_dialog_init(GladeXML *xml) dev_list = GTK_TREE_VIEW(glade_xml_get_widget(xml, "dev_list")); assert(dev_list); - add_plain_text_column(dev_list, _("Product"), 0, 16); - add_plain_text_column(dev_list, _("Serial"), 1, 8); - add_plain_text_column(dev_list, _("Device"), 2, 13); + aoview_add_plain_text_column(dev_list, _("Product"), 0, 16); + aoview_add_plain_text_column(dev_list, _("Serial"), 1, 8); + aoview_add_plain_text_column(dev_list, _("Device"), 2, 13); dev_selection = gtk_tree_view_get_selection(dev_list); gtk_tree_selection_set_mode(dev_selection, GTK_SELECTION_SINGLE); diff --git a/aoview/aoview_log.c b/aoview/aoview_log.c new file mode 100644 index 00000000..623c5aa6 --- /dev/null +++ b/aoview/aoview_log.c @@ -0,0 +1,192 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "aoview.h" + +#define LOG_DIR_PATH "/apps/aoview/log_dir" +#define DEFAULT_LOG "AltOS" + +static char *aoview_log_dir; +static FILE *aoview_log_file; +static int aoview_log_serial; +static int aoview_log_sequence; +static GtkMessageDialog *log_fail_dialog; +static int aoview_log_failed; + +static void +aoview_log_save_conf(void) +{ + GConfClient *gconf_client; + + gconf_client = gconf_client_get_default(); + if (gconf_client) + { + gconf_client_set_string(gconf_client, + LOG_DIR_PATH, + aoview_log_dir, + NULL); + g_object_unref(G_OBJECT(gconf_client)); + } +} + +static void +aoview_log_configure(GtkWidget *widget, gpointer data) +{ + GtkFileChooser *chooser = data; + aoview_log_dir = gtk_file_chooser_get_filename(chooser); + aoview_log_save_conf(); + gtk_widget_hide(GTK_WIDGET(chooser)); +} + +static void +aoview_log_new(void) +{ + if (aoview_log_file) { + fclose(aoview_log_file); + aoview_log_file = NULL; + } + aoview_log_failed = 0; +} + +static void +aoview_log_new_item(GtkWidget *widget, gpointer data) +{ + aoview_log_new(); +} + +void +aoview_log_set_serial(int serial) +{ + aoview_log_serial = serial; +} + +int +aoview_log_get_serial(void) +{ + return aoview_log_serial; +} + +static void +aoview_log_open_failed(char *name) +{ + char *utf8_file; + utf8_file = g_filename_to_utf8(name, -1, NULL, NULL, NULL); + if (!utf8_file) + utf8_file = name; + gtk_message_dialog_format_secondary_text(log_fail_dialog, + "\"%s\"", utf8_file); + if (utf8_file != name) + g_free(utf8_file); + gtk_widget_show(GTK_WIDGET(log_fail_dialog)); + aoview_log_failed = 1; +} + +static void +aoview_log_start(void) +{ + if (!aoview_log_file) { + char base[50]; + struct tm tm; + time_t now; + char *full; + int r; + + now = time(NULL); + (void) localtime_r(&now, &tm); + aoview_mkdir(aoview_log_dir); + for (;;) { + sprintf(base, "%04d-%02d-%02d-serial-%03d-flight-%03d.log", + tm.tm_year + 1900, + tm.tm_mon + 1, + tm.tm_mday, + aoview_log_serial, + aoview_log_sequence); + full = aoview_fullname(aoview_log_dir, base); + r = access(full, F_OK); + if (r < 0) { + aoview_log_file = fopen(full, "w"); + if (!aoview_log_file) + aoview_log_open_failed(full); + else + setlinebuf(aoview_log_file); + free(full); + break; + } + free (full); + aoview_log_sequence++; + } + } +} + +void +aoview_log_printf(char *format, ...) +{ + va_list ap; + + if (aoview_log_failed) + return; + aoview_log_start(); + va_start(ap, format); + vfprintf(aoview_log_file, format, ap); + va_end(ap); +} + +void +aoview_log_init(GladeXML *xml) +{ + GConfClient *gconf_client; + char *log_dir = NULL; + GtkFileChooser *log_chooser_dialog; + GtkWidget *log_configure_ok; + GtkWidget *log_new; + + g_type_init(); + gconf_client = gconf_client_get_default(); + if (gconf_client) + { + log_dir = gconf_client_get_string(gconf_client, + LOG_DIR_PATH, + NULL); + g_object_unref(G_OBJECT(gconf_client)); + } + if (!log_dir) { + aoview_log_dir = aoview_fullname(getenv("HOME"), DEFAULT_LOG); + aoview_log_save_conf(); + } else { + aoview_log_dir = strdup(log_dir); + } + + log_chooser_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "log_chooser_dialog")); + assert(log_chooser_dialog); + gtk_file_chooser_set_filename(log_chooser_dialog, aoview_log_dir); + + log_configure_ok = glade_xml_get_widget(xml, "log_configure_ok"); + assert(log_configure_ok); + + g_signal_connect(G_OBJECT(log_configure_ok), "clicked", + G_CALLBACK(aoview_log_configure), + log_chooser_dialog); + + log_new = glade_xml_get_widget(xml, "ao_log_new"); + assert(log_new); + g_signal_connect(G_OBJECT(log_new), "activate", + G_CALLBACK(aoview_log_new_item), + NULL); + + log_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "log_fail_dialog")); + assert(log_fail_dialog); +} diff --git a/aoview/aoview_main.c b/aoview/aoview_main.c index 7906e8ac..9e32e639 100644 --- a/aoview/aoview_main.c +++ b/aoview/aoview_main.c @@ -69,6 +69,10 @@ int main(int argc, char **argv) aoview_state_init(xml); + aoview_log_init(xml); + + aoview_table_init(xml); + gtk_main(); return 0; diff --git a/aoview/aoview_monitor.c b/aoview/aoview_monitor.c index 1c9b7189..ba2e9df7 100644 --- a/aoview/aoview_monitor.c +++ b/aoview/aoview_monitor.c @@ -72,7 +72,8 @@ aoview_monitor_parse(char *line) int nword; struct aostate state; - printf ("%s\n", line); + if (aoview_log_get_serial()) + aoview_log_printf ("%s\n", line); for (nword = 0; nword < 64; nword++) { words[nword] = strtok_r(line, " \t\n", &saveptr); line = NULL; @@ -85,6 +86,9 @@ aoview_monitor_parse(char *line) return; aoview_parse_string(state.callsign, sizeof (state.callsign), words[1]); aoview_parse_int(&state.serial, words[3]); + if (!aoview_log_get_serial()) + aoview_log_set_serial(state.serial); + aoview_parse_int(&state.rssi, words[5]); aoview_parse_string(state.state, sizeof (state.state), words[9]); aoview_parse_int(&state.tick, words[10]); @@ -94,12 +98,15 @@ aoview_monitor_parse(char *line) aoview_parse_int(&state.batt, words[18]); aoview_parse_int(&state.drogue, words[20]); aoview_parse_int(&state.main, words[22]); + aoview_parse_int(&state.nsat, words[24]); if (strcmp (words[26], "unlocked") != 0 && nword >= 29) { + state.locked = 1; sscanf(words[26], "%d:%d:%d", &state.gps_time.hour, &state.gps_time.minute, &state.gps_time.second); aoview_parse_pos(&state.lat, words[27]); aoview_parse_pos(&state.lon, words[28]); sscanf(words[29], "%dm", &state.alt); } else { + state.locked = 0; state.gps_time.hour = state.gps_time.minute = state.gps_time.second = 0; state.lat = state.lon = 0; state.alt = 0; diff --git a/aoview/aoview_state.c b/aoview/aoview_state.c index efd49042..046ccc92 100644 --- a/aoview/aoview_state.c +++ b/aoview/aoview_state.c @@ -66,6 +66,9 @@ aoview_state_notify(struct aostate *state) int ticks; double dist; double bearing; + double temp; + double battery; + double drogue_sense, main_sense; if (!strcmp(state->state, "pad")) { if (npad < NUM_PAD_SAMPLES) { @@ -90,19 +93,39 @@ aoview_state_notify(struct aostate *state) velocity_change = (accel + prev_accel) / 2.0; ticks = state->tick - prev_tick; velocity -= velocity_change * (ticks / 100.0); + temp = ((state->temp / 32767.0 * 3.3) - 0.5) / 0.01; + battery = (state->batt / 32767.0 * 5.0); + drogue_sense = (state->drogue / 32767.0 * 15.0); + main_sense = (state->main / 32767.0 * 15.0); prev_accel = accel; prev_tick = state->tick; - printf ("Pad altitude: %dm\n", aoview_pres_to_altitude(pad_pres)); - printf ("AGL: %dm\n", altitude); - printf ("Acceleration: %gm/s²\n", accel); - printf ("Velocity: %gm/s\n", velocity); - printf ("Lat: %g\n", state->lat); - printf ("Lon: %g\n", state->lon); - printf ("GPS alt: %d\n", state->alt); - aoview_great_circle(pad_lat, pad_lon, state->lat, state->lon, - &dist, &bearing); - printf ("Course: %gkm %g°\n", dist, bearing); + aoview_table_start(); + aoview_table_add_row("RSSI", "%ddB", state->rssi); + aoview_table_add_row("Height", "%dm", altitude); + aoview_table_add_row("Acceleration", "%gm/s²", accel); + aoview_table_add_row("Velocity", "%gm/s", velocity); + aoview_table_add_row("Temperature", "%g°C", temp); + aoview_table_add_row("Battery", "%gV", battery); + aoview_table_add_row("Drogue", "%gV", drogue_sense); + aoview_table_add_row("Main", "%gV", main_sense); + aoview_table_add_row("Pad altitude", "%dm", aoview_pres_to_altitude(pad_pres)); + aoview_table_add_row("Satellites", "%d", state->nsat); + if (state->locked) { + aoview_table_add_row("Lat", "%g", state->lat); + aoview_table_add_row("Lon", "%g", state->lon); + aoview_table_add_row("GPS alt", "%d", state->alt); + aoview_table_add_row("GPS time", "%02d:%02d:%02d", + state->gps_time.hour, + state->gps_time.minute, + state->gps_time.second); + aoview_great_circle(pad_lat, pad_lon, state->lat, state->lon, + &dist, &bearing); + aoview_table_add_row("Course", "%gkm %g°", dist, bearing); + } else { + aoview_table_add_row("GPS", "unlocked"); + } + aoview_table_finish(); } void diff --git a/aoview/aoview_table.c b/aoview/aoview_table.c new file mode 100644 index 00000000..90deeb91 --- /dev/null +++ b/aoview/aoview_table.c @@ -0,0 +1,62 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "aoview.h" + +static GtkTreeView *dataview; +static GtkListStore *datalist; + +void +aoview_table_start(void) +{ + datalist = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); +} + +void +aoview_table_add_row(char *label, char *format, ...) +{ + char buf[1024]; + va_list ap; + GtkTreeIter iter; + + va_start(ap, format); + vsnprintf(buf, sizeof (buf), format, ap); + va_end(ap); + gtk_list_store_append(datalist, &iter); + gtk_list_store_set(datalist, &iter, + 0, label, + 1, buf, + -1); +} + +void +aoview_table_finish(void) +{ + gtk_tree_view_set_model(dataview, GTK_TREE_MODEL(datalist)); + g_object_unref(G_OBJECT(datalist)); + gtk_tree_view_columns_autosize(dataview); +} + +void +aoview_table_init(GladeXML *xml) +{ + dataview = GTK_TREE_VIEW(glade_xml_get_widget(xml, "dataview")); + assert(dataview); + + aoview_add_plain_text_column(dataview, "Field", 0, 16); + aoview_add_plain_text_column(dataview, "Value", 1, 32); +} diff --git a/aoview/aoview_util.c b/aoview/aoview_util.c new file mode 100644 index 00000000..6ea62ac9 --- /dev/null +++ b/aoview/aoview_util.c @@ -0,0 +1,91 @@ +/* + * Copyright © 2009 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "aoview.h" + +char * +aoview_fullname (char *dir, char *file) +{ + char *new; + int dlen = strlen (dir); + int flen = strlen (file); + int slen = 0; + + if (dir[dlen-1] != '/') + slen = 1; + new = malloc (dlen + slen + flen + 1); + if (!new) + return 0; + strcpy(new, dir); + if (slen) + strcat (new, "/"); + strcat(new, file); + return new; +} + +char * +aoview_basename(char *file) +{ + char *b; + + b = strrchr(file, '/'); + if (!b) + return file; + return b + 1; +} + +int +aoview_mkdir(char *dir) +{ + char *slash; + char *d; + char *part; + + d = dir; + for (;;) { + slash = strchr (d, '/'); + if (!slash) + slash = d + strlen(d); + if (!*slash) + break; + part = strndup(dir, slash - dir); + if (!access(part, F_OK)) + if (mkdir(part, 0777) < 0) + return -errno; + free(part); + d = slash + 1; + } + return 0; +} + +GtkTreeViewColumn * +aoview_add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + renderer = gtk_cell_renderer_text_new (); + g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL); + g_object_set(renderer, "width-chars", width, NULL); + column = gtk_tree_view_column_new_with_attributes (title, renderer, + "text", model_column, + NULL); + gtk_tree_view_column_set_resizable (column, FALSE); + gtk_tree_view_append_column (view, column); + + return column; +} -- 2.30.2