SELKIELogger  1.0.0
messages.c
1 /*
2  * Copyright (C) 2023 Swansea University
3  *
4  * This file is part of the SELKIELogger suite of tools.
5  *
6  * SELKIELogger is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation, either version 3 of the License, or (at your option)
9  * any later version.
10  *
11  * SELKIELogger is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this SELKIELogger product.
18  * If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include <errno.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "messages.h"
28 
38 msg_t *msg_new_float(const uint8_t source, const uint8_t type, const float val) {
39  msg_t *newmsg = calloc(1, sizeof(msg_t));
40  newmsg->source = source;
41  newmsg->type = type;
42  newmsg->dtype = MSG_FLOAT;
43  newmsg->length = 1;
44  newmsg->data.value = val;
45  return newmsg;
46 }
47 
57 msg_t *msg_new_timestamp(const uint8_t source, const uint8_t type, const uint32_t ts) {
58  msg_t *newmsg = calloc(1, sizeof(msg_t));
59  newmsg->source = source;
60  newmsg->type = type;
61  newmsg->dtype = MSG_TIMESTAMP;
62  newmsg->length = 1;
63  newmsg->data.timestamp = ts;
64  return newmsg;
65 }
66 
84 msg_t *msg_new_string(const uint8_t source, const uint8_t type, const size_t len, const char *str) {
85  msg_t *newmsg = calloc(1, sizeof(msg_t));
86  newmsg->source = source;
87  newmsg->type = type;
88  newmsg->dtype = MSG_STRING;
89  newmsg->length = len;
90  if (!str_update(&(newmsg->data.string), len, str)) {
91  // LCOV_EXCL_START
92  free(newmsg);
93  return NULL;
94  // LCOV_EXCL_STOP
95  }
96  return newmsg;
97 }
98 
116 msg_t *msg_new_string_array(const uint8_t source, const uint8_t type, const strarray *array) {
117  msg_t *newmsg = calloc(1, sizeof(msg_t));
118  newmsg->source = source;
119  newmsg->type = type;
120  newmsg->dtype = MSG_STRARRAY;
121  newmsg->length = array->entries;
122  if (!sa_copy(&(newmsg->data.names), array)) {
123  // LCOV_EXCL_START
124  free(newmsg);
125  return NULL;
126  // LCOV_EXCL_STOP
127  }
128  return newmsg;
129 }
130 
147 msg_t *msg_new_bytes(const uint8_t source, const uint8_t type, const size_t len, const uint8_t *bytes) {
148  msg_t *newmsg = calloc(1, sizeof(msg_t));
149  newmsg->source = source;
150  newmsg->type = type;
151  newmsg->dtype = MSG_BYTES;
152  newmsg->length = len;
153  newmsg->data.bytes = calloc(len, sizeof(uint8_t));
154  errno = 0;
155  memcpy(newmsg->data.bytes, bytes, len);
156  if (errno) {
157  free(newmsg->data.bytes);
158  free(newmsg);
159  return NULL;
160  }
161  return newmsg;
162 }
163 
176 msg_t *msg_new_float_array(const uint8_t source, const uint8_t type, const size_t entries, const float *array) {
177  msg_t *newmsg = calloc(1, sizeof(msg_t));
178  newmsg->source = source;
179  newmsg->type = type;
180  newmsg->dtype = MSG_NUMARRAY;
181  newmsg->length = entries;
182  newmsg->data.farray = calloc(entries, sizeof(float));
183  errno = 0;
184  memcpy(newmsg->data.farray, array, entries * sizeof(float));
185  if (errno) {
186  free(newmsg->data.farray);
187  free(newmsg);
188  return NULL;
189  }
190  return newmsg;
191 }
192 
201 char *msg_to_string(const msg_t *msg) {
202  if (msg == NULL) { return NULL; }
203 
204  char *out = NULL;
205  char *dstr = msg_data_to_string(msg);
206  int ar = asprintf(&out, "0x%02x:0x%02x %s", msg->source, msg->type, dstr);
207  free(dstr);
208 
209  if (ar >= 0) { return out; }
210 
211  return NULL;
212 }
213 
222 char *msg_data_to_string(const msg_t *msg) {
223  if (msg == NULL) { return NULL; }
224 
225  char *out = NULL;
226  int rv = 0;
227  switch (msg->dtype) {
228  case MSG_FLOAT:
229  rv = asprintf(&out, "%.6f", msg->data.value);
230  break;
231  case MSG_TIMESTAMP:
232  rv = asprintf(&out, "%09u", msg->data.timestamp);
233  break;
234  case MSG_STRING:
235  rv = asprintf(&out, "%*s", (int)msg->data.string.length, msg->data.string.data);
236  break;
237  case MSG_STRARRAY:
238  out = msg_data_sarr_to_string(msg);
239  if (out == NULL) { out = strdup("[String array - error converting to string]"); }
240  break;
241  case MSG_BYTES:
242  rv = asprintf(&out, "[Binary data, %zd bytes]", msg->length);
243  break;
244  case MSG_NUMARRAY:
245  out = msg_data_narr_to_string(msg);
246  if (out == NULL) { out = strdup("[Numeric array - error converting to string]"); }
247  break;
248  // LCOV_EXCL_START
249  case MSG_ERROR:
250  out = strdup("[Message flagged as error]");
251  break;
252  case MSG_UNDEF:
253  out = strdup("[Message flagged as uninitialised]");
254  break;
255  default:
256  out = strdup("[Message type unknown]");
257  break;
258  // LCOV_EXCL_STOP
259  }
260  if (rv < 0) {
261  // LCOV_EXCL_START
262  if (out) { free(out); }
263  return NULL;
264  // LCOV_EXCL_STOP
265  }
266  return out;
267 }
268 
280 char *msg_data_narr_to_string(const msg_t *msg) {
281  const size_t alen = 250;
282  char *out = calloc(alen, sizeof(char));
283  if (out == NULL) { return NULL; }
284  size_t pos = 0;
285  for (unsigned int i = 0; i < msg->length; i++) {
286  int l = snprintf(&(out[pos]), (alen - pos), "%.4f/", msg->data.farray[i]);
287  if (l < 0) {
288  // LCOV_EXCL_START
289  free(out);
290  return NULL;
291  // LCOV_EXCL_STOP
292  }
293  pos += l;
294  }
295  out[pos - 1] = '\0';
296  return out;
297 }
298 
310 char *msg_data_sarr_to_string(const msg_t *msg) {
311  size_t len = 0;
312  if ((msg == NULL) || (msg->data.names.entries <= 0)) { return NULL; }
313  for (int i = 0; i < msg->data.names.entries; i++) {
314  size_t l = msg->data.names.strings[i].length;
315  if ((l == 0) || (msg->data.names.strings[i].data == NULL)) {
316  len += 2;
317  } else {
318  len += l + 1;
319  }
320  }
321 
322  char *out = calloc(len, sizeof(char));
323  size_t pos = 0;
324  for (int i = 0; i < msg->data.names.entries; i++) {
325  int l = 0;
326  if ((msg->data.names.strings[i].length == 0) || (msg->data.names.strings[i].data == NULL)) {
327  l = snprintf(&(out[pos]), (len - pos), "-/");
328  } else {
329  l = snprintf(&(out[pos]), (len - pos), "%s/", msg->data.names.strings[i].data);
330  }
331  if (l < 0) {
332  free(out);
333  return NULL;
334  }
335  pos += l;
336  }
337  out[pos - 1] = '\0';
338  return out;
339 }
340 
349 void msg_destroy(msg_t *msg) {
350  if (msg == NULL) { return; }
351 
352  switch (msg->dtype) {
353  case MSG_ERROR:
354  case MSG_FLOAT:
355  case MSG_TIMESTAMP:
356  case MSG_UNDEF:
357  // No action required;
358  break;
359  case MSG_STRING:
360  str_destroy(&(msg->data.string));
361  break;
362  case MSG_STRARRAY:
363  sa_destroy(&(msg->data.names));
364  break;
365  case MSG_BYTES:
366  free(msg->data.bytes);
367  break;
368  case MSG_NUMARRAY:
369  free(msg->data.farray);
370  break;
371  // LCOV_EXCL_START
372  default:
373  fprintf(stderr, "Unhandled message type in msg_destroy!\n");
374  break;
375  // LCOV_EXCL_STOP
376  }
377  msg->length = 0;
378  msg->dtype = MSG_UNDEF;
379 }
msg_t * msg_new_string(const uint8_t source, const uint8_t type, const size_t len, const char *str)
Create a new message with a single string embedded.
Definition: messages.c:84
char * msg_data_to_string(const msg_t *msg)
Generate string representation of message data.
Definition: messages.c:222
char * msg_to_string(const msg_t *msg)
Generate string representation of message.
Definition: messages.c:201
void msg_destroy(msg_t *msg)
Destroy a message.
Definition: messages.c:349
msg_t * msg_new_string_array(const uint8_t source, const uint8_t type, const strarray *array)
Create a new message containing an array of strings.
Definition: messages.c:116
msg_t * msg_new_bytes(const uint8_t source, const uint8_t type, const size_t len, const uint8_t *bytes)
Create a new message containing raw binary data.
Definition: messages.c:147
char * msg_data_sarr_to_string(const msg_t *msg)
Represent string array as single string.
Definition: messages.c:310
msg_t * msg_new_timestamp(const uint8_t source, const uint8_t type, const uint32_t ts)
Create a timestamp message.
Definition: messages.c:57
char * msg_data_narr_to_string(const msg_t *msg)
Convert numerical array to string.
Definition: messages.c:280
msg_t * msg_new_float_array(const uint8_t source, const uint8_t type, const size_t entries, const float *array)
Create a new message containing an array of floating point data.
Definition: messages.c:176
msg_t * msg_new_float(const uint8_t source, const uint8_t type, const float val)
Create new message with a single numeric value.
Definition: messages.c:38
@ MSG_NUMARRAY
Array of floating point values.
Definition: messages.h:63
@ MSG_ERROR
An error code is returned in data.value.
Definition: messages.h:56
@ MSG_FLOAT
Generic numerical data.
Definition: messages.h:58
@ MSG_BYTES
Raw binary data.
Definition: messages.h:60
@ MSG_STRING
Single string.
Definition: messages.h:61
@ MSG_UNDEF
Undefined/Uninitialised message.
Definition: messages.h:57
@ MSG_STRARRAY
Array of strings.
Definition: messages.h:62
@ MSG_TIMESTAMP
Timestamp (milliseconds since defined epoch/event)
Definition: messages.h:59
void sa_destroy(strarray *sa)
Destroy array and contents.
Definition: strarray.c:182
bool sa_copy(strarray *dst, const strarray *src)
Copy an array of strings from src to dst.
Definition: strarray.c:76
bool str_update(string *str, const size_t len, const char *src)
Update an existing string from a character array of given length.
Definition: strarray.c:294
void str_destroy(string *str)
Destroy a string and free its contents.
Definition: strarray.c:323
Queuable message.
Definition: messages.h:71
size_t length
Data type dependent, see the msg_new functions.
Definition: messages.h:74
msg_data_t data
Embedded data.
Definition: messages.h:76
uint8_t type
Message type. Common types to be documented.
Definition: messages.h:73
uint8_t source
Maps to a specific sensor unit or data source.
Definition: messages.h:72
msg_dtype_t dtype
Embedded data type.
Definition: messages.h:75
Array of strings.
Definition: strarray.h:43
int entries
Maximum number of strings in array, set when calling sa_new()
Definition: strarray.h:44
string * strings
Simple array of string structures.
Definition: strarray.h:45
char * data
Character array, should be null terminated.
Definition: strarray.h:39
size_t length
This should include a terminating null byte where possible.
Definition: strarray.h:38
float * farray
Array of floats.
Definition: messages.h:51
string string
Single character array with length.
Definition: messages.h:49
uint32_t timestamp
Intended to represent millisecond level clock.
Definition: messages.h:47
float value
Generic numerical data.
Definition: messages.h:46
strarray names
Array of strings, intended for use to provide channel names.
Definition: messages.h:50
uint8_t * bytes
Our "raw" binary type.
Definition: messages.h:48