diff --git a/include/villas/sample.h b/include/villas/sample.h index 17fac2f90..0770a7d03 100644 --- a/include/villas/sample.h +++ b/include/villas/sample.h @@ -26,16 +26,8 @@ #include #include #include -#include -#include -#include #include -/* "I" defined by complex.h collides with a define in OpenSSL */ -#undef I -#define J _Complex_I - - #include #include @@ -87,13 +79,17 @@ struct sample { struct timespec received; /**< The point in time when this data was received. */ } ts; - /** The values. */ - union { - double f; /**< Floating point values. */ - int64_t i; /**< Integer values. */ - bool b; /**< Boolean values. */ - float _Complex z; /**< Complex values. */ - } data[]; /**< Data is in host endianess! */ + /** The sample signal values. + * + * This variable length array (VLA) extends over the end of struct sample. + * Make sure that pointers to struct sample point to memory blocks of adequate size. + * Use the SAMPLE_LENGTH() macro to calculate the required size. + * + * Metadata describing the details of signal values (such as name, unit, data type and more) + * are stored in the struct sample::signals list. Each entry in this list corresponedents + * to an entry in the struct sample::data array. + */ + union signal_data data[]; }; #define SAMPLE_NON_POOL PTRDIFF_MIN diff --git a/include/villas/signal.h b/include/villas/signal.h index 55ec24004..312a6a12b 100644 --- a/include/villas/signal.h +++ b/include/villas/signal.h @@ -24,6 +24,12 @@ #pragma once #include +#include +#include +#include + +/* "I" defined by complex.h collides with a define in OpenSSL */ +#undef I #include @@ -36,12 +42,24 @@ struct list; struct node; struct mapping_entry; +/** A signal value. + * + * Data is in host endianess! + */ +union signal_data { + double f; /**< Floating point values. */ + int64_t i; /**< Integer values. */ + bool b; /**< Boolean values. */ + float _Complex z; /**< Complex values. */ +}; + enum signal_format { - SIGNAL_FORMAT_FLOAT = 0, - SIGNAL_FORMAT_INT = 1, - SIGNAL_FORMAT_BOOL = 2, - SIGNAL_FORMAT_COMPLEX = 3, - SIGNAL_FORMAT_UNKNOWN = -1 + SIGNAL_FORMAT_INVALID = 0, /**< Signal format is invalid. */ + SIGNAL_FORMAT_AUTO = 1, /**< Signal format is unknown. Try autodetection. */ + SIGNAL_FORMAT_FLOAT = 2, /**< See signal_data::f */ + SIGNAL_FORMAT_INT = 3, /**< See signal_data::i */ + SIGNAL_FORMAT_BOOL = 4, /**< See signal_data::b */ + SIGNAL_FORMAT_COMPLEX = 5 /**< See signal_data::z */ }; /** Signal descriptor. @@ -52,6 +70,8 @@ struct signal { char *name; /**< The name of the signal. */ char *unit; /**< The unit of the signal. */ + union signal_data init; /**< The initial value of the signal. */ + int enabled; atomic_int refcnt; /**< Reference counter. */ @@ -59,23 +79,53 @@ struct signal { enum signal_format format; }; +/** Initialize a signal with default values. */ int signal_init(struct signal *s); +/** Destroy a signal and release memory. */ +int signal_destroy(struct signal *s); + +/** Allocate memory for a new signal, and initialize it with provided values. */ +struct signal * signal_create(const char *name, const char *unit, enum signal_format fmt); + +/** Destroy and release memory of signal. */ +int signal_free(struct signal *s); + /** Increase reference counter. */ int signal_incref(struct signal *s); /** Decrease reference counter. */ int signal_decref(struct signal *s); -int signal_init_from_mapping(struct signal *s, const struct mapping_entry *me, unsigned index); - -int signal_destroy(struct signal *s); - +/** Parse signal description. */ int signal_parse(struct signal *s, json_t *cfg); -int signal_parse_list(struct list *list, json_t *cfg); +/** Initialize signal from a mapping_entry. */ +int signal_init_from_mapping(struct signal *s, const struct mapping_entry *me, unsigned index); -int signal_get_offset(const char *str, struct node *n); +int signal_list_parse(struct list *list, json_t *cfg); + +int signal_list_generate(struct list *list, unsigned len, enum signal_format fmt); + +void signal_list_dump(const struct list *list); + +enum signal_format signal_format_from_str(const char *str); + +const char * signal_format_to_str(enum signal_format fmt); + +enum signal_format signal_format_detect(const char *val); + +/** Convert signal data from one description/format to another. */ +void signal_data_convert(union signal_data *data, const struct signal *from, const struct signal *to); + +/** Print value of a signal to a character buffer. */ +int signal_data_snprint(const union signal_data *data, const struct signal *sig, char *buf, size_t len); + +int signal_data_parse_str(union signal_data *data, const struct signal *sig, const char *ptr, char **end); + +int signal_data_parse_json(union signal_data *data, const struct signal *sig, json_t *cfg); + +void signal_data_set(union signal_data *data, const struct signal *sig, double val); #ifdef __cplusplus } diff --git a/lib/signal.c b/lib/signal.c index 679cb5aee..356cc48b4 100644 --- a/lib/signal.c +++ b/lib/signal.c @@ -21,7 +21,7 @@ *********************************************************************************/ #include - +#include #include #include #include @@ -30,9 +30,11 @@ int signal_init(struct signal *s) { + s->enabled = true; + s->name = NULL; s->unit = NULL; - s->format = SIGNAL_FORMAT_UNKNOWN; + s->format = SIGNAL_FORMAT_AUTO; s->refcnt = ATOMIC_VAR_INIT(1); @@ -70,7 +72,7 @@ int signal_init_from_mapping(struct signal *s, const struct mapping_entry *me, u break; case MAPPING_TYPE_HEADER: - switch (me->hdr.type) { + switch (me->header.type) { case MAPPING_HEADER_TYPE_LENGTH: case MAPPING_HEADER_TYPE_SEQUENCE: s->format = SIGNAL_FORMAT_INT; @@ -83,8 +85,7 @@ int signal_init_from_mapping(struct signal *s, const struct mapping_entry *me, u break; case MAPPING_TYPE_DATA: - s->format = me->signal->format; - s->unit = me->signal->unit; + *s = *me->data.signal; break; } @@ -96,6 +97,46 @@ int signal_destroy(struct signal *s) if (s->name) free(s->name); + if (s->unit) + free(s->unit); + + return 0; +} + +struct signal * signal_create(const char *name, const char *unit, enum signal_format fmt) +{ + int ret; + struct signal *sig; + + sig = alloc(sizeof(struct signal)); + if (!sig) + return NULL; + + ret = signal_init(sig); + if (ret) + return NULL; + + if (name) + sig->name = strdup(name); + + if (unit) + sig->unit = strdup(unit); + + sig->format = fmt; + + return sig; +} + +int signal_free(struct signal *s) +{ + int ret; + + ret = signal_destroy(s); + if (ret) + return ret; + + free(s); + return 0; } @@ -110,7 +151,7 @@ int signal_decref(struct signal *s) /* Did we had the last reference? */ if (prev == 1) - signal_destroy(s); + signal_free(s); return prev - 1; } @@ -119,29 +160,47 @@ int signal_parse(struct signal *s, json_t *cfg) { int ret; json_error_t err; - const char *name; + json_t *json_init = NULL; + const char *name = NULL; const char *unit = NULL; + const char *format = NULL; - /* Default values */ - s->enabled = true; - - ret = json_unpack_ex(cfg, &err, 0, "{ s: s, s?: s, s?: b }", + ret = json_unpack_ex(cfg, &err, 0, "{ s?: s, s?: s, s?: s, s?: o, s?: b }", "name", &name, "unit", &unit, + "format", &format, + "init", &json_init, "enabled", &s->enabled ); if (ret) return -1; - s->name = strdup(name); - s->unit = unit - ? strdup(unit) - : NULL; + if (name) + s->name = strdup(name); + + if (unit) + s->unit = strdup(unit); + + if (format) { + s->format = signal_format_from_str(format); + if (s->format == SIGNAL_FORMAT_INVALID) + return -1; + } + + if (json_init) { + ret = signal_data_parse_json(&s->init, s, json_init); + if (ret) + return ret; + } + else + signal_data_set(&s->init, s, 0); return 0; } -int signal_parse_list(struct list *list, json_t *cfg) +/* Signal list */ + +int signal_list_parse(struct list *list, json_t *cfg) { int ret; struct signal *s; @@ -156,9 +215,13 @@ int signal_parse_list(struct list *list, json_t *cfg) if (!s) return -1; + ret = signal_init(s); + if (ret) + return ret; + ret = signal_parse(s, json_signal); if (ret) - return -1; + return ret; list_push(list, s); } @@ -166,23 +229,327 @@ int signal_parse_list(struct list *list, json_t *cfg) return 0; } -int signal_get_offsets(const char *str, struct list *sigs) +int signal_list_generate(struct list *list, unsigned len, enum signal_format fmt) { - int idx; - char *endptr; - struct signal *s; + for (int i = 0; i < len; i++) { + char name[32]; + snprintf(name, sizeof(name), "signal%d", i); - /* Lets try to find a signal with a matching name */ - if (1) { - s = list_lookup(sigs, str); - if (s) - return list_index(sigs, s); + struct signal *sig = signal_create(name, NULL, fmt); + if (!sig) + return -1; + + list_push(list, sig); } - /* Lets try to interpret the signal name as an index */ - idx = strtoul(str, &endptr, 10); - if (endptr == str + strlen(str)) - return idx; - - return -1; + return 0; +} + +void signal_list_dump(const struct list *list) +{ + info ("Signals:"); + + for (int i = 0; i < list_length(list); i++) { + struct signal *sig = list_at(list, i); + + if (sig->unit) + info(" %d: %s [%s] = %s", i, sig->name, sig->unit, signal_format_to_str(sig->format)); + else + info(" %d: %s = %s", i, sig->name, signal_format_to_str(sig->format)); + } +} + +/* Signal format */ + +enum signal_format signal_format_from_str(const char *str) +{ + if (!strcmp(str, "boolean")) + return SIGNAL_FORMAT_BOOL; + else if (!strcmp(str, "complex")) + return SIGNAL_FORMAT_COMPLEX; + else if (!strcmp(str, "float")) + return SIGNAL_FORMAT_FLOAT; + else if (!strcmp(str, "integer")) + return SIGNAL_FORMAT_INT; + else if (!strcmp(str, "auto")) + return SIGNAL_FORMAT_AUTO; + else + return SIGNAL_FORMAT_INVALID; +} + +const char * signal_format_to_str(enum signal_format fmt) +{ + switch (fmt) { + case SIGNAL_FORMAT_BOOL: + return "boolean"; + + case SIGNAL_FORMAT_COMPLEX: + return "complex"; + + case SIGNAL_FORMAT_FLOAT: + return "float"; + + case SIGNAL_FORMAT_INT: + return "integer"; + + case SIGNAL_FORMAT_AUTO: + return "auto"; + + case SIGNAL_FORMAT_INVALID: + return "invalid"; + } + + return NULL; +} + +enum signal_format signal_format_detect(const char *val) +{ + char *brk; + int len; + + debug(LOG_IO | 5, "Attempt to detect format of: %s", val); + + brk = strchr(val, 'i'); + if (brk) + return SIGNAL_FORMAT_COMPLEX; + + brk = strchr(val, '.'); + if (brk) + return SIGNAL_FORMAT_FLOAT; + + len = strlen(val); + if (len == 1 && (val[0] == '1' || val[0] == '0')) + return SIGNAL_FORMAT_BOOL; + + return SIGNAL_FORMAT_INT; +} + +/* Signal data */ + +void signal_data_set(union signal_data *data, const struct signal *sig, double val) +{ + switch (sig->format) { + case SIGNAL_FORMAT_BOOL: + data->b = val; + break; + + case SIGNAL_FORMAT_FLOAT: + data->f = val; + break; + + case SIGNAL_FORMAT_INT: + data->i = val; + break; + + case SIGNAL_FORMAT_COMPLEX: + data->z = val; + break; + + case SIGNAL_FORMAT_INVALID: + case SIGNAL_FORMAT_AUTO: + memset(data, 0, sizeof(union signal_data)); + break; + } +} + +void signal_data_convert(union signal_data *data, const struct signal *from, const struct signal *to) +{ + if (from == to) /* Nothing to do */ + return; + + switch (to->format) { + case SIGNAL_FORMAT_BOOL: + switch(from->format) { + case SIGNAL_FORMAT_BOOL: + data->b = data->b; + break; + + case SIGNAL_FORMAT_INT: + data->b = data->i; + break; + + case SIGNAL_FORMAT_FLOAT: + data->b = data->f; + break; + + case SIGNAL_FORMAT_COMPLEX: + data->b = creal(data->z); + break; + + default: { } + } + break; + + case SIGNAL_FORMAT_INT: + switch(from->format) { + case SIGNAL_FORMAT_BOOL: + data->i = data->b; + break; + + case SIGNAL_FORMAT_INT: + data->i = data->i; + break; + + case SIGNAL_FORMAT_FLOAT: + data->i = data->f; + break; + + case SIGNAL_FORMAT_COMPLEX: + data->i = creal(data->z); + break; + + default: { } + } + break; + + case SIGNAL_FORMAT_FLOAT: + switch(from->format) { + case SIGNAL_FORMAT_BOOL: + data->f = data->b; + break; + + case SIGNAL_FORMAT_INT: + data->f = data->i; + break; + + case SIGNAL_FORMAT_FLOAT: + data->f = data->f; + break; + + case SIGNAL_FORMAT_COMPLEX: + data->f = creal(data->z); + break; + + default: { } + } + break; + + case SIGNAL_FORMAT_COMPLEX: + switch(from->format) { + case SIGNAL_FORMAT_BOOL: + data->z = CMPLXF(data->b, 0); + break; + + case SIGNAL_FORMAT_INT: + data->z = CMPLXF(data->i, 0); + break; + + case SIGNAL_FORMAT_FLOAT: + data->z = CMPLXF(data->f, 0); + break; + + case SIGNAL_FORMAT_COMPLEX: + data->z = data->z; + break; + + default: { } + } + break; + + default: { } + } +} + +int signal_data_parse_str(union signal_data *data, const struct signal *sig, const char *ptr, char **end) +{ + switch (sig->format) { + case SIGNAL_FORMAT_FLOAT: + data->f = strtod(ptr, end); + break; + + case SIGNAL_FORMAT_INT: + data->i = strtol(ptr, end, 10); + break; + + case SIGNAL_FORMAT_BOOL: + data->b = strtol(ptr, end, 10); + break; + + case SIGNAL_FORMAT_COMPLEX: { + float real, imag; + + real = strtod(ptr, end); + if (*end == ptr) + return -1; + + ptr = *end; + + imag = strtod(ptr, end); + if (*end == ptr) + return -1; + + if (**end != 'i') + return -1; + + (*end)++; + + data->z = CMPLXF(real, imag); + break; + } + + case SIGNAL_FORMAT_AUTO: + case SIGNAL_FORMAT_INVALID: + return -1; + } + + return 0; +} + +int signal_data_parse_json(union signal_data *data, const struct signal *sig, json_t *cfg) +{ + int ret; + + switch (sig->format) { + case SIGNAL_FORMAT_FLOAT: + data->f = json_real_value(cfg); + break; + + case SIGNAL_FORMAT_INT: + data->i = json_integer_value(cfg); + break; + + case SIGNAL_FORMAT_BOOL: + data->b = json_boolean_value(cfg); + break; + + case SIGNAL_FORMAT_COMPLEX: { + double real, imag; + + ret = json_unpack(cfg, "{ s: F, s: F }", + "real", &real, + "imag", &imag + ); + if (ret) + return -2; + + data->z = CMPLXF(real, imag); + break; + } + + case SIGNAL_FORMAT_INVALID: + case SIGNAL_FORMAT_AUTO: + return -1; + } + + return 0; +} + +int signal_data_snprint(const union signal_data *data, const struct signal *sig, char *buf, size_t len) +{ + switch (sig->format) { + case SIGNAL_FORMAT_FLOAT: + return snprintf(buf, len, "%.6f", data->f); + + case SIGNAL_FORMAT_INT: + return snprintf(buf, len, "%" PRIi64, data->i); + + case SIGNAL_FORMAT_BOOL: + return snprintf(buf, len, "%u", data->b); + + case SIGNAL_FORMAT_COMPLEX: + return snprintf(buf, len, "%.6f%+.6fi", creal(data->z), cimag(data->z)); + + default: + return 0; + } }