SELKIELogger  1.0.0
LoggerMP.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 "Logger.h"
22 
23 #include "LoggerMP.h"
24 #include "LoggerSignals.h"
25 
33 void *mp_setup(void *ptargs) {
34  log_thread_args_t *args = (log_thread_args_t *)ptargs;
35  mp_params *mpInfo = (mp_params *)args->dParams;
36 
37  mpInfo->handle = mp_openConnection(mpInfo->portName, mpInfo->baudRate);
38  if (mpInfo->handle < 0) {
39  log_error(args->pstate, "[MP:%s] Unable to open a connection", args->tag);
40  args->returnCode = -1;
41  return NULL;
42  }
43 
44  log_info(args->pstate, 2, "[MP:%s] Connected", args->tag);
45  args->returnCode = 0;
46  return NULL;
47 }
48 
57 void *mp_logging(void *ptargs) {
59  log_thread_args_t *args = (log_thread_args_t *)ptargs;
60  mp_params *mpInfo = (mp_params *)args->dParams;
61 
62  log_info(args->pstate, 1, "[MP:%s] Logging thread started", args->tag);
63 
64  uint8_t *buf = calloc(MP_SERIAL_BUFF, sizeof(uint8_t));
65  int mp_index = 0;
66  int mp_hw = 0;
67  while (!shutdownFlag) {
68  // Needs to be on the heap as we'll be queuing it
69  msg_t *out = calloc(1, sizeof(msg_t));
70  if (mp_readMessage_buf(mpInfo->handle, out, buf, &mp_index, &mp_hw)) {
71  if (!queue_push(args->logQ, out)) {
72  log_error(args->pstate, "[MP:%s] Error pushing message to queue",
73  args->tag);
74  msg_destroy(out);
75  args->returnCode = -1;
76  pthread_exit(&(args->returnCode));
77  }
78 
79  if (out->type == SLCHAN_NAME) {
80  if (out->dtype != MSG_STRING) {
82  args->pstate,
83  "[MP:%s] Unexpected message type (0x%02x) for source name (Source ID: 0x{%02x})",
84  args->tag, out->dtype, out->source);
85  continue;
86  }
87 
88  if (mpInfo->csource > 0 && mpInfo->csource != out->source) {
90  args->pstate,
91  "[MP:%s] Received source ID (0x%02x) does not match cached value (0x%02x) - multiple devices on a single input not currently supported!",
92  args->tag, out->source, mpInfo->csource);
93  }
94  mpInfo->csource = out->source;
95 
96  if (mpInfo->cname) {
97  free(mpInfo->cname);
98  mpInfo->cname = NULL;
99  }
100  mpInfo->cname = strdup(out->data.string.data);
101  } else if (out->type == SLCHAN_MAP) {
102  if (mpInfo->csource > 0 && mpInfo->csource != out->source) {
103  log_warning(
104  args->pstate,
105  "[MP:%s] Received source ID (0x%02x) does not match cached value (0x%02x) - multiple devices on a single input not currently supported!",
106  args->tag, out->source, mpInfo->csource);
107  }
108  mpInfo->csource = out->source;
109 
110  if (!sa_copy(&mpInfo->cmap, &(out->data.names))) {
111  log_error(args->pstate,
112  "[MP:%s] Error caching channel map", args->tag);
113  // Not destroying "out", as already queued
114  args->returnCode = -1;
115  pthread_exit(&(args->returnCode));
116  }
117  }
118  // After pushing it to the queue, it is the responsibility of the
119  // consumer to dispose of it after use.
120  } else {
121  if (out->dtype == MSG_ERROR &&
122  !(out->data.value == 0xFF || out->data.value == 0xFD ||
123  out->data.value == 0xEE)) {
124  // 0xFF, 0xFD and 0xEE are used to signal recoverable
125  // states that resulted in no valid message.
126  //
127  // 0xFF and 0xFD indicate an out of data error, which is
128  // not a problem for serial monitoring, but might indicate
129  // EOF when reading from file
130  //
131  // 0xEE indicates an invalid message following valid sync
132  // bytes
133  log_error(args->pstate,
134  "[MP:%s] Error signalled from mp_readMessage_buf",
135  args->tag);
136  free(buf);
137  free(out);
138  args->returnCode = -2;
139  pthread_exit(&(args->returnCode));
140  }
141  // out was allocated but not pushed to the queue, so free it here.
142  msg_destroy(out);
143  free(out);
144 
145  // We've already exited (via pthread_exit) for error
146  // cases, so at this point sleep briefly and wait for
147  // more data
148  usleep(SERIAL_SLEEP);
149  }
150  }
151  free(buf);
152  pthread_exit(NULL);
153  return NULL; // Superfluous, as returning zero via pthread_exit above
154 }
155 
164 void *mp_channels(void *ptargs) {
165  log_thread_args_t *args = (log_thread_args_t *)ptargs;
166  mp_params *mpInfo = (mp_params *)args->dParams;
167  if (mpInfo->csource == 0) {
168  // No device ID yet
169  return NULL;
170  }
171 
172  if (mpInfo->cname != NULL) {
173  msg_t *out = msg_new_string(mpInfo->csource, SLCHAN_NAME, strlen(mpInfo->cname),
174  mpInfo->cname);
175 
176  if (!queue_push(args->logQ, out)) {
177  log_error(args->pstate, "[MP:%s] Error pushing source name to queue",
178  args->tag);
179  msg_destroy(out);
180  args->returnCode = -1;
181  pthread_exit(&(args->returnCode));
182  }
183  }
184 
185  if (mpInfo->cmap.entries > 0) {
186  msg_t *out = msg_new_string_array(mpInfo->csource, SLCHAN_MAP, &mpInfo->cmap);
187 
188  if (!queue_push(args->logQ, out)) {
189  log_error(args->pstate, "[MP:%s] Error pushing channel map to queue",
190  args->tag);
191  msg_destroy(out);
192  args->returnCode = -1;
193  pthread_exit(&(args->returnCode));
194  }
195  }
196 
197  return NULL;
198 }
199 
206 void *mp_shutdown(void *ptargs) {
207  log_thread_args_t *args = (log_thread_args_t *)ptargs;
208  mp_params *mpInfo = (mp_params *)args->dParams;
209 
210  if (mpInfo->handle >= 0) { // Admittedly 0 is unlikely
211  mp_closeConnection(mpInfo->handle);
212  }
213  mpInfo->handle = -1;
214  if (mpInfo->portName) {
215  free(mpInfo->portName);
216  mpInfo->portName = NULL;
217  }
218  return NULL;
219 }
220 
226  .logging = &mp_logging,
227  .shutdown = &mp_shutdown,
228  .channels = &mp_channels};
229  return cb;
230 }
231 
236  mp_params mp = {.portName = NULL,
237  .baudRate = 115200,
238  .handle = -1,
239  .csource = 0,
240  .cname = NULL,
241  .cmap = {0}};
242  return mp;
243 }
244 
251  if (lta->dParams) {
252  log_error(lta->pstate, "[MP:%s] Refusing to reconfigure", lta->tag);
253  return false;
254  }
255 
256  mp_params *mp = calloc(1, sizeof(mp_params));
257  if (!mp) {
258  log_error(lta->pstate, "[MP:%s] Unable to allocate memory for device parameters",
259  lta->tag);
260  return false;
261  }
262 
263  (*mp) = mp_getParams();
264 
265  config_kv *t = NULL;
266  if ((t = config_get_key(s, "port"))) { mp->portName = config_qstrdup(t->value); }
267  t = NULL;
268 
269  if ((t = config_get_key(s, "baud"))) {
270  errno = 0;
271  mp->baudRate = strtol(t->value, NULL, 0);
272  if (errno) {
273  log_error(lta->pstate, "[MP:%s] Error parsing baud rate: %s", lta->tag,
274  strerror(errno));
275  free(mp);
276  return false;
277  }
278  }
279  t = NULL;
280 
281  lta->dParams = mp;
282  return true;
283 }
char * config_qstrdup(const char *c)
Duplicate string, stripping optional leading/trailing quote marks.
Definition: LoggerConfig.c:271
config_kv * config_get_key(const config_section *cs, const char *kn)
Find configugration key within specific section, by name.
Definition: LoggerConfig.c:204
void signalHandlersBlock(void)
Block signals that we have handlers for.
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
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_ERROR
An error code is returned in data.value.
Definition: messages.h:56
@ MSG_STRING
Single string.
Definition: messages.h:61
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
#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 SLCHAN_MAP
Channel name map (excludes log channels)
Definition: sources.h:98
#define SLCHAN_NAME
Name of source device.
Definition: sources.h:97
void * mp_logging(void *ptargs)
MP source main logging loop.
Definition: LoggerMP.c:57
void * mp_shutdown(void *ptargs)
MP source shutdown.
Definition: LoggerMP.c:206
void * mp_setup(void *ptargs)
MP connection setup.
Definition: LoggerMP.c:33
device_callbacks mp_getCallbacks()
Fill out device callback functions for logging.
Definition: LoggerMP.c:224
bool mp_parseConfig(log_thread_args_t *lta, config_section *s)
Take a configuration section and parse parameters.
Definition: LoggerMP.c:250
mp_params mp_getParams()
Fill out default MP source parameters.
Definition: LoggerMP.c:235
void * mp_channels(void *ptargs)
Push device information from cache to queue.
Definition: LoggerMP.c:164
atomic_bool shutdownFlag
Trigger clean software shutdown.
Definition: LoggerSignals.c:34
#define SERIAL_SLEEP
Default serial wait time.
Definition: Logger.h.in:51
void log_info(const program_state *s, const int level, const char *format,...)
Output formatted information message at a given level.
Definition: logging.c:125
void log_warning(const program_state *s, const char *format,...)
Output formatted warning message.
Definition: logging.c:86
void log_error(const program_state *s, const char *format,...)
Output formatted error message.
Definition: logging.c:47
bool queue_push(msgqueue *queue, msg_t *msg)
Add a message to the tail of the queue.
Definition: queue.c:103
bool sa_copy(strarray *dst, const strarray *src)
Copy an array of strings from src to dst.
Definition: strarray.c:76
Represent a key=value pair.
Definition: LoggerConfig.h:42
char * value
Configuration item value.
Definition: LoggerConfig.h:44
Configuration file section.
Definition: LoggerConfig.h:54
Device specific function information.
Definition: Logger.h.in:72
device_fn startup
Called serially at startup, opens devices etc.
Definition: Logger.h.in:73
Logging thread information.
Definition: Logger.h.in:86
msgqueue * logQ
Main message queue. Pushed to by threads, consumed by main()
Definition: Logger.h.in:89
char * tag
Tag/source name for messages etc.
Definition: Logger.h.in:87
void * dParams
Device/Thread specific data.
Definition: Logger.h.in:92
program_state * pstate
Current program state, used for logging.
Definition: Logger.h.in:90
int returnCode
Thread return code (output)
Definition: Logger.h.in:93
MP Source device specific parameters.
Definition: LoggerMP.h:46
int baudRate
Baud rate for operations (currently unused)
Definition: LoggerMP.h:48
strarray cmap
Cache latest channel map.
Definition: LoggerMP.h:52
int handle
Handle for currently opened device.
Definition: LoggerMP.h:49
char * portName
Target port name.
Definition: LoggerMP.h:47
uint8_t csource
Cache source ID.
Definition: LoggerMP.h:50
char * cname
Cache latest device name.
Definition: LoggerMP.h:51
Queuable message.
Definition: messages.h:71
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
int entries
Maximum number of strings in array, set when calling sa_new()
Definition: strarray.h:44
char * data
Character array, should be null terminated.
Definition: strarray.h:39
string string
Single character array with length.
Definition: messages.h:49
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