SELKIELogger  1.0.0
MPSerial.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 <fcntl.h>
23 #include <stdbool.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <termios.h>
29 #include <time.h>
30 #include <unistd.h>
31 
32 #include "MPSerial.h"
33 #include "MPTypes.h"
34 #include <msgpack.h>
35 
43 int mp_openConnection(const char *port, const int baudRate) {
44  return openSerialConnection(port, baudRate);
45 }
46 
52 void mp_closeConnection(int handle) {
53  close(handle);
54 }
55 
66 bool mp_readMessage(int handle, msg_t *out) {
67  static uint8_t buf[MP_SERIAL_BUFF];
68  static int index = 0; // Current read position
69  static int hw = 0; // Current array end
70  return mp_readMessage_buf(handle, out, buf, &index, &hw);
71 }
72 
101 bool mp_readMessage_buf(int handle, msg_t *out, uint8_t buf[MP_SERIAL_BUFF], int *index, int *hw) {
102  int ti = 0;
103  if (out == NULL || (*index) < 0 || (*hw) < 0 || buf == NULL) { return false; }
104  if ((*hw) < MP_SERIAL_BUFF - 1) {
105  errno = 0;
106  ti = read(handle, &(buf[(*hw)]), MP_SERIAL_BUFF - (*hw));
107  if (ti >= 0) {
108  (*hw) += ti;
109  } else {
110  if (errno != EAGAIN) {
111  fprintf(stderr, "Unexpected error while reading from serial port (handle ID: 0x%02x)\n",
112  handle);
113  fprintf(stderr, "read returned \"%s\" in readMessage\n", strerror(errno));
114  out->dtype = MSG_ERROR;
115  out->data.value = 0xAA;
116  return false;
117  }
118  }
119  }
120 
121  // Check buf[index] is valid ID
122  while (!(buf[(*index)] == MP_SYNC_BYTE1) && (*index) < (*hw)) {
123  (*index)++; // Current byte cannot be start of a message, so advance
124  }
125  if ((*index) == (*hw)) {
126  if ((*hw) > 0 && (*index) > 0) {
127  // Move data from index back to zero position
128  memmove(buf, &(buf[(*index)]), MP_SERIAL_BUFF - (*index));
129  (*hw) -= (*index);
130  (*index) = 0;
131  }
132  // fprintf(stderr, "Buffer empty - returning\n");
133  out->dtype = MSG_ERROR;
134  out->data.value = 0xFF;
135  if (ti == 0) { out->data.value = 0xFD; }
136  return false;
137  }
138 
139  if (((*hw) - (*index)) < 8) {
140  // Not enough data for any valid message, come back later
141  out->dtype = MSG_ERROR;
142  out->data.value = 0xFF;
143  if (ti == 0) { out->data.value = 0xFD; }
144  return false;
145  }
146 
147  if (buf[(*index) + 1] != MP_SYNC_BYTE2) {
148  // Found first sync byte, but second not valid
149  // Advance the index so we skip this message and go back around
150  (*index)++;
151  out->dtype = MSG_ERROR;
152  out->data.value = 0xFF;
153  return false;
154  }
155 
156  // We now know we have a good candidate for a valid MessagePacked message
157  msgpack_unpacker unpack;
158  if (!msgpack_unpacker_init(&unpack, 64 + (*hw) - (*index))) {
159  out->dtype = MSG_ERROR;
160  out->data.value = 0xAA;
161  fprintf(stderr, "Unable to initialise msgpack unpacker\n");
162  return false;
163  }
164  const size_t upInitialOffset = unpack.off;
165  memcpy(msgpack_unpacker_buffer(&unpack), &(buf[(*index)]), (*hw) - (*index));
166  msgpack_unpacker_buffer_consumed(&unpack, (*hw) - (*index));
167 
168  msgpack_unpacked mpupd = {0};
169  {
170  msgpack_unpack_return rs = msgpack_unpacker_next(&unpack, &mpupd);
171  switch (rs) {
172  case MSGPACK_UNPACK_SUCCESS:
173  // Will continue after the switch
174  break;
175  case MSGPACK_UNPACK_CONTINUE:
176  // Need more data
177  out->dtype = MSG_ERROR;
178  out->data.value = 0xFF;
179  msgpack_unpacker_destroy(&unpack);
180  // Could still be a good message, so do not advance index
181  return false;
182 
183  case MSGPACK_UNPACK_PARSE_ERROR:
184  default:
185  // Treat any unknown status as an error
186  out->dtype = MSG_ERROR;
187  out->data.value = 0xFF;
188  msgpack_unpacker_destroy(&unpack);
189  (*index)++; // Assume bad message, so advance 1 byte
190  // further into buffer
191  return false;
192  }
193 
194  if (mpupd.data.type != MSGPACK_OBJECT_ARRAY || mpupd.data.via.array.size != 4) {
195  msgpack_unpacked_destroy(&mpupd);
196  out->dtype = MSG_ERROR;
197  out->data.value = 0xFF;
198  msgpack_unpacker_destroy(&unpack);
199  (*index)++;
200  return false;
201  }
202  }
203 
204  // So at this point we have a 4 element array unpacked in mpupd.data
205  // This is msgpack_object_array with two members:
206  // - size, which we've verified (4)
207  // - ptr, which is an array of msgpack_objects
208  // Give the array we're interested in a shorter name
209  msgpack_object *inArr = mpupd.data.via.array.ptr;
210 
211  // Verify our marker byte
212  if (inArr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER || inArr[0].via.u64 != MP_SYNC_BYTE2) {
213  msgpack_unpacked_destroy(&mpupd);
214  out->dtype = MSG_ERROR;
215  out->data.value = 0xFF;
216  msgpack_unpacker_destroy(&unpack);
217  (*index)++;
218  return false;
219  }
220 
221  // Next should be our Source ID
222  if (inArr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER || inArr[1].via.u64 >= 128) {
223  msgpack_unpacked_destroy(&mpupd);
224  out->dtype = MSG_ERROR;
225  out->data.value = 0xFF;
226  msgpack_unpacker_destroy(&unpack);
227  (*index)++;
228  return false;
229  }
230  out->source = inArr[1].via.u64;
231 
232  // Then our Channel ID
233  if (inArr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER || inArr[2].via.u64 >= 128) {
234  msgpack_unpacked_destroy(&mpupd);
235  out->dtype = MSG_ERROR;
236  out->data.value = 0xFF;
237  msgpack_unpacker_destroy(&unpack);
238  (*index)++;
239  return false;
240  }
241  out->type = inArr[2].via.u64;
242 
243  // Now for the really fun bit, dealing with whatever input data this message has
244  // Our valid types are:
245  // MSG_FLOAT -> MSGPACK_OBJECT_FLOAT
246  // MSG_TIMESTAMP -> MSGPACK_OBECT_POSITIVE_INTEGER
247  // MSG_STRING -> MSGPACK_OBJECT_STR
248  // MSG_STRARRAY -> MSGPACK_OBJECT_ARRAY (of MSG_PACK_OBJECT_STRs)
249  // MSG_NUMARRAY -> MSGPACK_OBJECT_ARRAY (of MSG_PACK_OBJECT_FLOAT32)
250  bool valid = false;
251  switch (inArr[3].type) {
252  case MSGPACK_OBJECT_FLOAT32:
253  case MSGPACK_OBJECT_FLOAT64: // Shouldn't happen, but we can accept it
254  out->dtype = MSG_FLOAT;
255  out->data.value = inArr[3].via.f64;
256  valid = true;
257  break;
258  case MSGPACK_OBJECT_POSITIVE_INTEGER:
259  out->dtype = MSG_TIMESTAMP;
260  out->data.timestamp = inArr[3].via.u64;
261  valid = true;
262  break;
263  case MSGPACK_OBJECT_STR:
264  out->dtype = MSG_STRING;
265  out->length = inArr[3].via.str.size;
266  valid = str_update(&(out->data.string), inArr[3].via.str.size, inArr[3].via.str.ptr);
267  break;
268  case MSGPACK_OBJECT_ARRAY:
269  // Switch based on first item type
270  switch (inArr[3].via.array.ptr[0].type) {
271  case MSGPACK_OBJECT_STR:
272  out->dtype = MSG_STRARRAY;
273  valid = mp_unpack_strarray(&(out->data.names), &(inArr[3].via.array));
274  break;
275  case MSGPACK_OBJECT_FLOAT32:
276  case MSGPACK_OBJECT_FLOAT64:
277  out->dtype = MSG_NUMARRAY;
278  out->length = mp_unpack_numarray(&(out->data.farray), &(inArr[3].via.array));
279  if (out->length > 0) { valid = true; }
280  break;
281  default:
282  valid = false;
283  break;
284  }
285  break;
286  case MSGPACK_OBJECT_BIN:
287  out->dtype = MSG_BYTES;
288  out->length = inArr[3].via.bin.size;
289  out->data.bytes = malloc(out->length);
290  if (out->data.bytes == NULL) {
291  valid = false;
292  break;
293  }
294  memcpy(out->data.bytes, inArr[3].via.bin.ptr, out->length);
295  valid = true;
296  break;
297  default:
298  valid = false;
299  break;
300  }
301 
302  (*index) += (unpack.off - upInitialOffset);
303  msgpack_unpacked_destroy(&mpupd);
304  msgpack_unpacker_destroy(&unpack);
305 
306  if ((*hw) > 0) {
307  // Move data from index back to zero position
308  memmove(buf, &(buf[(*index)]), MP_SERIAL_BUFF - (*index));
309  (*hw) -= (*index);
310  (*index) = 0;
311  }
312  if (valid == false) {
313  out->dtype = MSG_ERROR;
314  out->data.value = 0xFF; // Invalid message
315  // Index already incremented and reset above
316  }
317  return valid;
318 }
319 
330 bool mp_packMessage(msgpack_sbuffer *sbuf, const msg_t *out) {
331  msgpack_packer pack = {0};
332  msgpack_sbuffer_init(sbuf);
333  msgpack_packer_init(&pack, sbuf, msgpack_sbuffer_write);
334  msgpack_pack_array(&pack, 4); // MP_SYNC_BYTE1
335  msgpack_pack_int(&pack, MP_SYNC_BYTE2);
336  msgpack_pack_int(&pack, out->source);
337  msgpack_pack_int(&pack, out->type);
338  size_t sl = 0;
339  switch (out->dtype) {
340  case MSG_FLOAT:
341  msgpack_pack_float(&pack, out->data.value);
342  break;
343 
344  case MSG_TIMESTAMP:
345  msgpack_pack_uint32(&pack, out->data.timestamp);
346  break;
347 
348  case MSG_BYTES:
349  msgpack_pack_bin(&pack, out->length);
350  msgpack_pack_bin_body(&pack, out->data.bytes, out->length);
351  break;
352 
353  case MSG_STRING:
354  sl = out->data.string.length;
355  if (strlen(out->data.string.data) < sl) { sl = strlen(out->data.string.data); }
356  msgpack_pack_str(&pack, sl);
357  msgpack_pack_str_body(&pack, out->data.string.data, sl);
358  break;
359 
360  case MSG_STRARRAY:
361  mp_pack_strarray(&pack, &(out->data.names));
362  break;
363  case MSG_NUMARRAY:
364  mp_pack_numarray(&pack, out->length, out->data.farray);
365  break;
366  case MSG_ERROR:
367  case MSG_UNDEF:
368  default:
369  msgpack_sbuffer_destroy(sbuf);
370  return false;
371  }
372  return true;
373 }
374 
382 bool mp_writeMessage(int handle, const msg_t *out) {
383  msgpack_sbuffer sbuf;
384  if (!mp_packMessage(&sbuf, out)) { return false; }
385  int ret = write(handle, sbuf.data, sbuf.size);
386  msgpack_sbuffer_destroy(&sbuf);
387  return (ret == (ssize_t) sbuf.size);
388 }
389 
397 bool mp_writeData(int handle, const msg_t *out) {
398  size_t sl = 0;
399  switch (out->dtype) {
400  case MSG_FLOAT:
401  return (write(handle, &(out->data.value), sizeof(out->data.value)) == sizeof(out->data.value));
402 
403  case MSG_TIMESTAMP:
404  return (write(handle, &(out->data.timestamp), sizeof(out->data.timestamp)) ==
405  (ssize_t) sizeof(out->data.timestamp));
406 
407  case MSG_BYTES:
408  return (write(handle, out->data.bytes, out->length) == (ssize_t) out->length);
409 
410  case MSG_STRING:
411  sl = out->data.string.length;
412  if (strlen(out->data.string.data) < sl) { sl = strlen(out->data.string.data); }
413  return (write(handle, &(out->data.string.data), sl) == (ssize_t) sl);
414 
415  case MSG_STRARRAY:
416  for (int ix = 0; ix < out->data.names.entries; ix++) {
417  sl = out->data.names.strings[ix].length;
418  if (sl > 0 && strlen(out->data.names.strings[ix].data) < sl) {
419  sl = strlen(out->data.names.strings[ix].data);
420  }
421  if (!(write(handle, out->data.names.strings[ix].data, sl) == (ssize_t) sl)) { return false; }
422  }
423  return true;
424 
425  case MSG_NUMARRAY:
426  sl = sizeof(out->data.farray[0]) * out->length;
427  return (write(handle, out->data.farray, sl) == ((ssize_t) sl));
428 
429  case MSG_ERROR:
430  case MSG_UNDEF:
431  default:
432  return false;
433  }
434 
435  return false;
436 }
437 
446 void mp_pack_strarray(msgpack_packer *pack, const strarray *sa) {
447  msgpack_pack_array(pack, sa->entries);
448  for (int ix = 0; ix < sa->entries; ix++) {
449  string *s = &(sa->strings[ix]);
450  size_t sl = s->length;
451  if (sl > 0 && strlen(s->data) < sl) { sl = strlen(s->data); }
452  msgpack_pack_str(pack, sl);
453  msgpack_pack_str_body(pack, s->data, sl);
454  }
455 }
456 
466 void mp_pack_numarray(msgpack_packer *pack, const size_t entries, const float *fa) {
467  msgpack_pack_array(pack, entries);
468  for (unsigned int ix = 0; ix < entries; ix++) {
469  msgpack_pack_float(pack, fa[ix]);
470  }
471 }
472 
483 bool mp_unpack_strarray(strarray *sa, msgpack_object_array *obj) {
484  const int nEntries = obj->size;
485  sa_destroy(sa); // Just in case
486  sa->entries = nEntries;
487  sa->strings = calloc(nEntries, sizeof(string));
488 
489  if (sa->strings == NULL) { return false; }
490 
491  for (int ix = 0; ix < nEntries; ix++) {
492  if (obj->ptr[ix].type != MSGPACK_OBJECT_STR) {
493  sa_destroy(sa);
494  return false;
495  }
496  if (!sa_create_entry(sa, ix, obj->ptr[ix].via.str.size, obj->ptr[ix].via.str.ptr)) {
497  sa_destroy(sa);
498  return false;
499  }
500  }
501  return true;
502 }
503 
514 size_t mp_unpack_numarray(float **fa, msgpack_object_array *obj) {
515  const int nEntries = obj->size;
516  (*fa) = calloc(nEntries, sizeof(float));
517 
518  if ((*fa) == NULL) { return -1; }
519 
520  for (int ix = 0; ix < nEntries; ix++) {
521  if (!((obj->ptr[ix].type == MSGPACK_OBJECT_FLOAT32) || (obj->ptr[ix].type == MSGPACK_OBJECT_FLOAT64))) {
522  free((*fa));
523  (*fa) = NULL;
524  return -1;
525  }
526  (*fa)[ix] = obj->ptr[ix].via.f64;
527  }
528  return nEntries;
529 }
@ 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
int openSerialConnection(const char *port, const int baudRate)
Open a serial connection at a given baud rate.
Definition: serial.c:44
void mp_pack_numarray(msgpack_packer *pack, const size_t entries, const float *fa)
Pack numeric/floating point array.
Definition: MPSerial.c:466
void mp_pack_strarray(msgpack_packer *pack, const strarray *sa)
Pack string array.
Definition: MPSerial.c:446
#define MP_SYNC_BYTE2
MP Serial synchronisation byte 2.
Definition: MPTypes.h:46
int mp_openConnection(const char *port, const int baudRate)
Set up a connection to the specified port.
Definition: MPSerial.c:43
bool mp_readMessage_buf(int handle, msg_t *out, uint8_t buf[MP_SERIAL_BUFF], int *index, int *hw)
Read data from handle, and parse message if able.
Definition: MPSerial.c:101
bool mp_readMessage(int handle, msg_t *out)
Static wrapper around mp_readMessage_buf.
Definition: MPSerial.c:66
bool mp_unpack_strarray(strarray *sa, msgpack_object_array *obj)
Unpack msgpack array into string array.
Definition: MPSerial.c:483
#define MP_SERIAL_BUFF
Default serial buffer allocation size.
Definition: MPSerial.h:41
void mp_closeConnection(int handle)
Close existing connection.
Definition: MPSerial.c:52
#define MP_SYNC_BYTE1
MP Serial synchronisation byte 1.
Definition: MPTypes.h:40
bool mp_writeData(int handle, const msg_t *out)
Send message data (only!) to attached device.
Definition: MPSerial.c:397
bool mp_packMessage(msgpack_sbuffer *sbuf, const msg_t *out)
Pack a message into a buffer.
Definition: MPSerial.c:330
bool mp_writeMessage(int handle, const msg_t *out)
Send message to attached device.
Definition: MPSerial.c:382
size_t mp_unpack_numarray(float **fa, msgpack_object_array *obj)
Allocate array of floats and unpack a msgpack array into it.
Definition: MPSerial.c:514
void sa_destroy(strarray *sa)
Destroy array and contents.
Definition: strarray.c:182
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
bool sa_create_entry(strarray *array, const int index, const size_t len, const char *src)
Create an string in a given position from a character array and length.
Definition: strarray.c:149
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