diff --git a/common/utils/T/tracer/logger/Makefile b/common/utils/T/tracer/logger/Makefile index 18f70e0bc56b303f1ed36d2fae81a12740687bfd..e5391008380ccb32410dcc92a3a0788bd3ad8a45 100644 --- a/common/utils/T/tracer/logger/Makefile +++ b/common/utils/T/tracer/logger/Makefile @@ -1,7 +1,7 @@ CC=gcc CFLAGS=-Wall -g -pthread -I.. -OBJS=logger.o textlog.o framelog.o ttilog.o timelog.o +OBJS=logger.o textlog.o framelog.o ttilog.o timelog.o ticklog.o logger.a: $(OBJS) ar cr logger.a $(OBJS) diff --git a/common/utils/T/tracer/logger/logger.h b/common/utils/T/tracer/logger/logger.h index ec04e60819029520074055fc648d6c1497d8c7bf..2c26f8dcb2aa9462f52f4bc7acfb53710417c4b7 100644 --- a/common/utils/T/tracer/logger/logger.h +++ b/common/utils/T/tracer/logger/logger.h @@ -11,6 +11,8 @@ logger *new_ttilog(void *event_handler, void *database, char *event_name, char *frame_varname, char *subframe_varname, char *data_varname, int convert_to_dB); logger *new_timelog(void *event_handler, void *database, char *event_name); +logger *new_ticklog(void *event_handler, void *database, + char *event_name, char *frame_name, char *subframe_name); void framelog_set_skip(logger *_this, int skip_delay); diff --git a/common/utils/T/tracer/logger/ticklog.c b/common/utils/T/tracer/logger/ticklog.c new file mode 100644 index 0000000000000000000000000000000000000000..b2462aabb642b7682216c48ec3b06e1e73e73164 --- /dev/null +++ b/common/utils/T/tracer/logger/ticklog.c @@ -0,0 +1,80 @@ +#include "logger.h" +#include "logger_defs.h" +#include "event.h" +#include "database.h" +#include "handler.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct ticklog { + struct logger common; + void *database; + int frame_arg; + int subframe_arg; +}; + +static void _event(void *p, event e) +{ + struct ticklog *l = p; + int i; + int frame; + int subframe; + + frame = e.e[l->frame_arg].i; + subframe = e.e[l->subframe_arg].i; + + for (i = 0; i < l->common.vsize; i++) + l->common.v[i]->append(l->common.v[i], e.sending_time, frame, subframe); +} + +logger *new_ticklog(event_handler *h, void *database, + char *event_name, char *frame_varname, char *subframe_varname) +{ + struct ticklog *ret; + int event_id; + database_event_format f; + int i; + + ret = calloc(1, sizeof(struct ticklog)); if (ret == NULL) abort(); + + ret->common.event_name = strdup(event_name); + if (ret->common.event_name == NULL) abort(); + ret->database = database; + + event_id = event_id_from_name(database, event_name); + + ret->common.handler_id = register_handler_function(h,event_id,_event,ret); + + f = get_format(database, event_id); + + /* look for frame and subframe args */ + ret->frame_arg = -1; + ret->subframe_arg = -1; + for (i = 0; i < f.count; i++) { + if (!strcmp(f.name[i], frame_varname)) ret->frame_arg = i; + if (!strcmp(f.name[i], subframe_varname)) ret->subframe_arg = i; + } + if (ret->frame_arg == -1) { + printf("%s:%d: frame argument '%s' not found in event '%s'\n", + __FILE__, __LINE__, frame_varname, event_name); + abort(); + } + if (ret->subframe_arg == -1) { + printf("%s:%d: subframe argument '%s' not found in event '%s'\n", + __FILE__, __LINE__, subframe_varname, event_name); + abort(); + } + if (strcmp(f.type[ret->frame_arg], "int") != 0) { + printf("%s:%d: argument '%s' has wrong type (should be 'int')\n", + __FILE__, __LINE__, frame_varname); + abort(); + } + if (strcmp(f.type[ret->subframe_arg], "int") != 0) { + printf("%s:%d: argument '%s' has wrong type (should be 'int')\n", + __FILE__, __LINE__, subframe_varname); + abort(); + } + + return ret; +} diff --git a/common/utils/T/tracer/view/Makefile b/common/utils/T/tracer/view/Makefile index 51b5114a0ede49d44a87e3df9d684417b9a7fe91..32b267ec24d60c25bf1650e5b60250579c6be871 100644 --- a/common/utils/T/tracer/view/Makefile +++ b/common/utils/T/tracer/view/Makefile @@ -1,7 +1,7 @@ CC=gcc -CFLAGS=-Wall -g -pthread -I.. +CFLAGS=-Wall -g -pthread -I.. -I../logger -OBJS=stdout.o textlist.o xy.o tti.o time.o +OBJS=stdout.o textlist.o xy.o tti.o time.o ticktime.o view.a: $(OBJS) ar cr view.a $(OBJS) diff --git a/common/utils/T/tracer/view/ticktime.c b/common/utils/T/tracer/view/ticktime.c new file mode 100644 index 0000000000000000000000000000000000000000..90ded4dd431ab0775c3209c30f0e37be8c79f9dd --- /dev/null +++ b/common/utils/T/tracer/view/ticktime.c @@ -0,0 +1,410 @@ +#include "view.h" +#include "../utils.h" +#include "logger.h" +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <string.h> + +/* TODO: some code is identical/almost identical to time.c, merge/factorize */ + +/****************************************************************************/ +/* tick timeview */ +/****************************************************************************/ + +struct plot { + struct timespec *tick; + int ticksize; + int tickmaxsize; + int tickstart; + int line; + int color; +}; + +struct ticktime { + view common; + gui *g; + widget *w; + float refresh_rate; + pthread_mutex_t lock; + struct plot *p; + int psize; + double pixel_length; /* unit: nanosecond (maximum 1 hour/pixel) */ + struct timespec latest_time; + struct timespec start_time; + int autoscroll; + struct timespec tick_latest_time; + int tick_latest_frame; + int tick_latest_subframe; + void *clock_ticktime; /* data for the clock tick, see below */ +}; + +/* TODO: put that function somewhere else (utils.c) */ +static struct timespec time_add(struct timespec a, struct timespec b) +{ + struct timespec ret; + ret.tv_sec = a.tv_sec + b.tv_sec; + ret.tv_nsec = a.tv_nsec + b.tv_nsec; + if (ret.tv_nsec > 1000000000) { + ret.tv_sec++; + ret.tv_nsec -= 1000000000; + } + return ret; +} + +/* TODO: put that function somewhere else (utils.c) */ +static struct timespec time_sub(struct timespec a, struct timespec b) +{ + struct timespec ret; + if (a.tv_nsec < b.tv_nsec) { + ret.tv_nsec = (int64_t)a.tv_nsec - (int64_t)b.tv_nsec + 1000000000; + ret.tv_sec = a.tv_sec - b.tv_sec - 1; + } else { + ret.tv_nsec = a.tv_nsec - b.tv_nsec; + ret.tv_sec = a.tv_sec - b.tv_sec; + } + return ret; +} + +/* TODO: put that function somewhere else (utils.c) */ +static struct timespec nano_to_time(int64_t n) +{ + struct timespec ret; + ret.tv_sec = n / 1000000000; + ret.tv_nsec = n % 1000000000; + return ret; +} + +/* TODO: put that function somewhere else (utils.c) */ +static int time_cmp(struct timespec a, struct timespec b) +{ + if (a.tv_sec < b.tv_sec) return -1; + if (a.tv_sec > b.tv_sec) return 1; + if (a.tv_nsec < b.tv_nsec) return -1; + if (a.tv_nsec > b.tv_nsec) return 1; + return 0; +} + +static int interval_empty(struct ticktime *this, int sub, + struct timespec start, struct timespec end) +{ + int a, b, mid; + int i; + + if (this->p[sub].ticksize == 0) return 1; + + /* look for a tick larger than start and smaller than end */ + a = 0; + b = this->p[sub].ticksize - 1; + while (b >= a) { + mid = (a+b) / 2; + i = (this->p[sub].tickstart + mid) % this->p[sub].ticksize; + if (time_cmp(this->p[sub].tick[i], start) < 0) a = mid + 1; + else if (time_cmp(this->p[sub].tick[i], end) > 0) b = mid - 1; + else return 0; + } + return 1; +} + +static void *ticktime_thread(void *_this) +{ + struct ticktime *this = _this; + int width; + int l; + int i; + struct timespec tstart; + struct timespec tnext; + struct plot *p; + int64_t pixel_length; + + while (1) { + if (pthread_mutex_lock(&this->lock)) abort(); + + timeline_get_width(this->g, this->w, &width); + timeline_clear_silent(this->g, this->w); + + /* TODO: optimize? */ + + /* use rounded pixel_length */ + pixel_length = this->pixel_length; + + if (this->autoscroll) { + tnext = time_add(this->latest_time, + (struct timespec){tv_sec:0,tv_nsec:1}); + tstart = time_sub(tnext, nano_to_time(pixel_length * width)); + this->start_time = tstart; + } else { + tstart = this->start_time; + tnext = time_add(tstart, nano_to_time(pixel_length * width)); + } + + for (l = 0; l < this->psize; l++) { + for (i = 0; i < width; i++) { + struct timespec tick_start, tick_end; + tick_start = time_add(tstart, nano_to_time(pixel_length * i)); + tick_end = time_add(tick_start, nano_to_time(pixel_length-1)); + if (interval_empty(this, l, tick_start, tick_end)) + continue; + p = &this->p[l]; + /* TODO: only one call */ + timeline_add_points_silent(this->g, this->w, p->line, p->color, &i, 1); + } + } + + widget_dirty(this->g, this->w); + + if (pthread_mutex_unlock(&this->lock)) abort(); + sleepms(1000 / this->refresh_rate); + } + + return 0; +} + +static void scroll(void *private, gui *g, + char *notification, widget *w, void *notification_data) +{ + struct ticktime *this = private; + int *d = notification_data; + int x = d[0]; + int key_modifiers = d[2]; + double mul = 1.2; + double pixel_length; + int64_t old_px_len_rounded; + struct timespec t; + int scroll_px; + int width; + + if (pthread_mutex_lock(&this->lock)) abort(); + + old_px_len_rounded = this->pixel_length; + + /* scroll if control+wheel, zoom otherwise */ + + if (key_modifiers & KEY_CONTROL) { + timeline_get_width(this->g, this->w, &width); + if (width < 2) width = 2; + scroll_px = 100; + if (scroll_px > width - 1) scroll_px = width - 1; + if (!strcmp(notification, "scrolldown")) + this->start_time = time_add(this->start_time, + nano_to_time(scroll_px * old_px_len_rounded)); + else + this->start_time = time_sub(this->start_time, + nano_to_time(scroll_px * old_px_len_rounded)); + goto end; + } + + if (!strcmp(notification, "scrollup")) mul = 1 / mul; + +again: + pixel_length = this->pixel_length * mul; + if (pixel_length < 1) pixel_length = 1; + if (pixel_length > (double)3600 * 1000000000) + pixel_length = (double)3600 * 1000000000; + + this->pixel_length = pixel_length; + + /* due to rounding, we may need to zoom by more than 1.2 with + * very close lookup, otherwise the user zooming command won't + * be visible (say length is 2.7, zoom in, new length is 2.25, + * and rounding is 2, same value, no change, no feedback to user => bad) + * TODO: make all this cleaner + */ + if (pixel_length != 1 && pixel_length != (double)3600 * 1000000000 && + (int64_t)pixel_length == old_px_len_rounded) + goto again; + + t = time_add(this->start_time, nano_to_time(x * old_px_len_rounded)); + this->start_time = time_sub(t, nano_to_time(x * (int64_t)pixel_length)); + +end: + if (pthread_mutex_unlock(&this->lock)) abort(); +} + +static void click(void *private, gui *g, + char *notification, widget *w, void *notification_data) +{ + struct ticktime *this = private; + int *d = notification_data; + int button = *d; + + if (button == 3) this->autoscroll = 0; + if (button == 1) this->autoscroll = 1; +} + +view *new_view_ticktime(float refresh_rate, gui *g, widget *w) +{ + struct ticktime *ret = calloc(1, sizeof(struct ticktime)); + if (ret == NULL) abort(); + + ret->refresh_rate = refresh_rate; + ret->g = g; + ret->w = w; + + ret->p = NULL; + ret->psize = 0; + + ret->autoscroll = 1; + + ret->tick_latest_time.tv_sec = 1; + + /* default pixel length: 10ms */ + ret->pixel_length = 10 * 1000000; + + register_notifier(g, "scrollup", w, scroll, ret); + register_notifier(g, "scrolldown", w, scroll, ret); + register_notifier(g, "click", w, click, ret); + + if (pthread_mutex_init(&ret->lock, NULL)) abort(); + + new_thread(ticktime_thread, ret); + + return (view *)ret; +} + +/****************************************************************************/ +/* subticktimeview */ +/****************************************************************************/ + +struct subticktime { + view common; + struct ticktime *parent; + int line; + int color; + int subview; +}; + +static void append(view *_this, struct timespec t, int frame, int subframe) +{ + struct subticktime *this = (struct subticktime *)_this; + struct ticktime *ticktime = this->parent; + struct plot *p = &ticktime->p[this->subview]; + int i; + struct timespec swap; + int64_t diff; + + if (pthread_mutex_lock(&ticktime->lock)) abort(); + + /* get time with respect to latest known tick time */ + diff = (frame*10 + subframe) - + (ticktime->tick_latest_frame*10 + ticktime->tick_latest_subframe); + if (diff > 1024*10/2) diff -= 1024*10; + else if (diff < -1024*10/2) diff += 1024*10; + if (diff < 0) + t = time_sub(ticktime->tick_latest_time, nano_to_time(-diff * 1000000)); + else + t = time_add(ticktime->tick_latest_time, nano_to_time(diff * 1000000)); + + if (p->ticksize < p->tickmaxsize) { + p->tick[p->ticksize] = t; + p->ticksize++; + } else { + p->tick[p->tickstart] = t; + p->tickstart = (p->tickstart + 1) % p->ticksize; + } + + /* due to adjustment of the time, array may not be ordered anymore */ + for (i = p->ticksize-2; i >= 0; i--) { + int prev = (p->tickstart + i) % p->ticksize; + int cur = (prev + 1) % p->ticksize; + if (time_cmp(p->tick[prev], p->tick[cur]) <= 0) break; + swap = p->tick[prev]; + p->tick[prev] = p->tick[cur]; + p->tick[cur] = swap; + } + + if (time_cmp(ticktime->latest_time, t) < 0) + ticktime->latest_time = t; + + if (pthread_mutex_unlock(&ticktime->lock)) abort(); +} + +view *new_subview_ticktime(view *_time, int line, int color, int size) +{ + struct ticktime *ticktime = (struct ticktime *)_time; + struct subticktime *ret = calloc(1, sizeof(struct subticktime)); + if (ret == NULL) abort(); + + ret->common.append = (void (*)(view *, ...))append; + + if (pthread_mutex_lock(&ticktime->lock)) abort(); + + ret->parent = ticktime; + ret->line = line; + ret->color = color; + ret->subview = ticktime->psize; + + ticktime->p = realloc(ticktime->p, + (ticktime->psize + 1) * sizeof(struct plot)); + if (ticktime->p == NULL) abort(); + ticktime->p[ticktime->psize].tick = calloc(size, sizeof(struct timespec)); + if (ticktime->p[ticktime->psize].tick == NULL) abort(); + ticktime->p[ticktime->psize].ticksize = 0; + ticktime->p[ticktime->psize].tickmaxsize = size; + ticktime->p[ticktime->psize].tickstart = 0; + ticktime->p[ticktime->psize].line = line; + ticktime->p[ticktime->psize].color = color; + + ticktime->psize++; + + if (pthread_mutex_unlock(&ticktime->lock)) abort(); + + return (view *)ret; +} + +/****************************************************************************/ +/* clock tick */ +/****************************************************************************/ + +struct clock_ticktime { + view common; + struct ticktime *parent; +}; + +static void clock_append(view *_this, struct timespec t, + int frame, int subframe) +{ + struct clock_ticktime *this = (struct clock_ticktime *)_this; + struct ticktime *tt = this->parent; + int64_t diff; + + if (subframe == 10) { subframe = 0; frame = (frame + 1) % 1024; } + + if (pthread_mutex_lock(&tt->lock)) abort(); + + /* get time relative to latest known tick time */ + /* In normal conditions diff is 1 but if the user pauses reception of events + * it may be anything. Let's take only positive values. + */ + diff = (frame*10 + subframe) - + (tt->tick_latest_frame*10 + tt->tick_latest_subframe); + if (diff < 0) diff += 1024*10; + tt->tick_latest_time = time_add(tt->tick_latest_time, + nano_to_time(diff * 1000000)); + tt->tick_latest_frame = frame; + tt->tick_latest_subframe = subframe; + + if (time_cmp(tt->latest_time, tt->tick_latest_time) < 0) + tt->latest_time = tt->tick_latest_time; + + if (pthread_mutex_unlock(&tt->lock)) abort(); +} + +void ticktime_set_tick(view *_ticktime, void *logger) +{ + struct ticktime *ticktime = (struct ticktime *)_ticktime; + struct clock_ticktime *n; + + if (pthread_mutex_lock(&ticktime->lock)) abort(); + + free(ticktime->clock_ticktime); + n = ticktime->clock_ticktime = calloc(1, sizeof(struct clock_ticktime)); + if (n == NULL) abort(); + + n->common.append = (void (*)(view *, ...))clock_append; + n->parent = ticktime; + + logger_add_view(logger, (view *)n); + + if (pthread_mutex_unlock(&ticktime->lock)) abort(); +} diff --git a/common/utils/T/tracer/view/view.h b/common/utils/T/tracer/view/view.h index 4b91291b1c3ffa6e2d7621d54f0e0727fe1801eb..cbc5e21b73d95fb3b15524977663a6054846fc5f 100644 --- a/common/utils/T/tracer/view/view.h +++ b/common/utils/T/tracer/view/view.h @@ -20,5 +20,8 @@ view *new_view_tti(float refresh_rate, gui *g, widget *w, view *new_view_time(int number_of_seconds, float refresh_rate, gui *g, widget *w); view *new_subview_time(view *time, int line, int color, int size); +view *new_view_ticktime(float refresh_rate, gui *g, widget *w); +view *new_subview_ticktime(view *ticktime, int line, int color, int size); +void ticktime_set_tick(view *ticktime, void *logger); #endif /* _VIEW_H_ */