SELKIELogger  1.0.0
LoggerTime.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 "LoggerSignals.h"
24 #include "LoggerTime.h"
25 
34 void *timer_setup(void *ptargs) {
35  log_thread_args_t *args = (log_thread_args_t *)ptargs;
36  timer_params *timerInfo = (timer_params *)args->dParams;
37 
38  if (timerInfo->sourceName == NULL) {
39  if (timerInfo->sourceNum == SLSOURCE_TIMER) {
40  // Special case the default clock
41  timerInfo->sourceName = strdup("Internal");
42  } else {
43  log_error(args->pstate, "[Timer:Unknown] No source name specified!");
44  }
45  }
46  return NULL;
47 }
48 
61 void *timer_logging(void *ptargs) {
63  log_thread_args_t *args = (log_thread_args_t *)ptargs;
64  timer_params *timerInfo = (timer_params *)args->dParams;
65 
66  log_info(args->pstate, 1, "[Timer:%s] Logging thread started", args->tag);
67 
68  const int incr_nsec = (1E9 / timerInfo->frequency);
69  time_t lstamp = 0;
70  while (!shutdownFlag) {
71  struct timespec now = {0};
72  clock_gettime(CLOCK_MONOTONIC, &now);
73  // Millisecond precision timestamp, but arbitrary reference point
75  (1000 * now.tv_sec + now.tv_nsec / 1000000));
76  if (!queue_push(args->logQ, msg)) {
77  log_error(args->pstate, "[Timer:%s] Error pushing message to queue",
78  args->tag);
79  msg_destroy(msg);
80  args->returnCode = -1;
81  pthread_exit(&(args->returnCode));
82  }
83 
84  time_t nstamp = time(NULL);
85  if (nstamp != lstamp) {
86  // Unix Epoch referenced timestamp
87  msg_t *epoch_msg = msg_new_timestamp(timerInfo->sourceNum, 4, nstamp);
88  if (!queue_push(args->logQ, epoch_msg)) {
89  log_error(args->pstate,
90  "[Timer:%s] Error pushing message to queue", args->tag);
91  msg_destroy(epoch_msg);
92  args->returnCode = -1;
93  pthread_exit(&(args->returnCode));
94  }
95  lstamp = nstamp;
96  }
97 
98  struct timespec nextIter = now;
99  nextIter.tv_nsec += incr_nsec;
100  if (nextIter.tv_nsec >= 1E9) {
101  int nsec = nextIter.tv_nsec / 1000000000;
102  nextIter.tv_nsec = nextIter.tv_nsec % 1000000000;
103  nextIter.tv_sec += nsec;
104  }
105  nextIter.tv_nsec -= (nextIter.tv_nsec % incr_nsec);
106 
107  struct timespec target = {0};
108  clock_gettime(CLOCK_MONOTONIC, &now);
109  if (timespec_subtract(&target, &nextIter, &now)) {
110  // Target has passed! Update ASAP and continue
111  log_warning(args->pstate, "[Timer:%s] Deadline missed", args->tag);
112  continue;
113  } else {
114  nanosleep(&target, NULL);
115  }
116  }
117  pthread_exit(NULL);
118  return NULL; // Superfluous, as returning zero via pthread_exit above
119 }
120 
127 void *timer_shutdown(void *ptargs) {
128  log_thread_args_t *args = (log_thread_args_t *)ptargs;
129  timer_params *timerInfo = (timer_params *)args->dParams;
130 
131  if (timerInfo->sourceName) {
132  free(timerInfo->sourceName);
133  timerInfo->sourceName = NULL;
134  }
135  return NULL;
136 }
137 
146 void *timer_channels(void *ptargs) {
147  log_thread_args_t *args = (log_thread_args_t *)ptargs;
148  timer_params *timerInfo = (timer_params *)args->dParams;
149 
150  msg_t *m_sn = msg_new_string(timerInfo->sourceNum, SLCHAN_NAME,
151  strlen(timerInfo->sourceName), timerInfo->sourceName);
152 
153  if (!queue_push(args->logQ, m_sn)) {
154  log_error(args->pstate, "[Timer:%s] Error pushing channel name to queue",
155  args->tag);
156  msg_destroy(m_sn);
157  args->returnCode = -1;
158  pthread_exit(&(args->returnCode));
159  }
160 
161  strarray *channels = sa_new(5); // 0,1,2 and 4
162  sa_create_entry(channels, SLCHAN_NAME, 4, "Name");
163  sa_create_entry(channels, SLCHAN_MAP, 8, "Channels");
164  sa_create_entry(channels, SLCHAN_TSTAMP, 9, "Timestamp");
165  sa_create_entry(channels, 4, 5, "Epoch");
166 
167  msg_t *m_cmap = msg_new_string_array(timerInfo->sourceNum, SLCHAN_MAP, channels);
168 
169  if (!queue_push(args->logQ, m_cmap)) {
170  log_error(args->pstate, "[Timer:%s] Error pushing channel map to queue",
171  args->tag);
172  msg_destroy(m_cmap);
173  sa_destroy(channels);
174  free(channels);
175  args->returnCode = -1;
176  pthread_exit(&(args->returnCode));
177  }
178 
179  sa_destroy(channels);
180  free(channels);
181  return NULL;
182 }
183 
189  .logging = &timer_logging,
190  .shutdown = &timer_shutdown,
191  .channels = &timer_channels};
192  return cb;
193 }
194 
199  timer_params timer = {
201  .sourceName = NULL,
202  .frequency = DEFAULT_MARK_FREQUENCY,
203  };
204  return timer;
205 }
206 
213  if (lta->dParams) {
214  log_error(lta->pstate, "[Timer:%s] Refusing to reconfigure", lta->tag);
215  return false;
216  }
217 
218  timer_params *tp = calloc(1, sizeof(timer_params));
219  if (!tp) {
220  log_error(lta->pstate,
221  "[Timer:%s] Unable to allocate memory for device parameters", lta->tag);
222  return false;
223  }
224  (*tp) = timer_getParams();
225 
226  config_kv *t = NULL;
227  if ((t = config_get_key(s, "name"))) {
228  tp->sourceName = config_qstrdup(t->value);
229  } else {
230  // Must set a name, so nick the tag value
231  tp->sourceName = strdup(lta->tag);
232  }
233  t = NULL;
234 
235  if ((t = config_get_key(s, "frequency"))) {
236  errno = 0;
237  tp->frequency = strtol(t->value, NULL, 0);
238  if (errno) {
239  log_error(lta->pstate, "[Timer:%s] Error parsing frequency: %s", lta->tag,
240  strerror(errno));
241  free(tp);
242  return false;
243  }
244  if (tp->frequency <= 0) {
245  log_error(
246  lta->pstate,
247  "[Timer:%s] Invalid frequency requested (%d) - must be positive and non-zero",
248  lta->tag, tp->frequency);
249  free(tp);
250  return false;
251  }
252  }
253  t = NULL;
254 
255  if ((t = config_get_key(s, "sourcenum"))) {
256  errno = 0;
257  int sn = strtol(t->value, NULL, 0);
258  if (errno) {
259  log_error(lta->pstate, "[Timer:%s] Error parsing source number: %s",
260  lta->tag, strerror(errno));
261  free(tp);
262  return false;
263  }
264  if (sn < 0) {
265  log_error(lta->pstate, "[Timer:%s] Invalid source number (%s)", lta->tag,
266  t->value);
267  free(tp);
268  return false;
269  }
270  if (sn < 10) {
271  tp->sourceNum += sn;
272  } else {
273  tp->sourceNum = sn;
274  if (sn <= SLSOURCE_TIMER || sn > (SLSOURCE_TIMER + 0x0F)) {
275  log_warning(
276  lta->pstate,
277  "[Timer:%s] Unexpected Source ID number (0x%02x)- this may cause analysis problems",
278  lta->tag, sn);
279  }
280  }
281  }
282  t = NULL;
283  lta->dParams = tp;
284  return true;
285 }
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_t * msg_new_timestamp(const uint8_t source, const uint8_t type, const uint32_t ts)
Create a timestamp message.
Definition: messages.c:57
#define SLCHAN_TSTAMP
Source timestamp (milliseconds, arbitrary epoch)
Definition: sources.h:99
#define SLCHAN_MAP
Channel name map (excludes log channels)
Definition: sources.h:98
#define SLCHAN_NAME
Name of source device.
Definition: sources.h:97
#define SLSOURCE_TIMER
Local/Software timers.
Definition: sources.h:56
void * timer_logging(void *ptargs)
Generate timer message at requested frequency.
Definition: LoggerTime.c:61
timer_params timer_getParams()
Fill out default timer parameters.
Definition: LoggerTime.c:198
void * timer_shutdown(void *ptargs)
No shutdown required - does nothing.
Definition: LoggerTime.c:127
void * timer_setup(void *ptargs)
Check parameters, but no other setup required.
Definition: LoggerTime.c:34
bool timer_parseConfig(log_thread_args_t *lta, config_section *s)
Take a configuration section and parse parameters.
Definition: LoggerTime.c:212
void * timer_channels(void *ptargs)
Generate channel map and push to logging queue.
Definition: LoggerTime.c:146
device_callbacks timer_getCallbacks()
Fill out device callback functions for logging.
Definition: LoggerTime.c:187
atomic_bool shutdownFlag
Trigger clean software shutdown.
Definition: LoggerSignals.c:34
bool timespec_subtract(struct timespec *result, struct timespec *x, struct timespec *y)
Difference between timespecs (used for rate keeping)
Definition: Logger.c:900
#define DEFAULT_MARK_FREQUENCY
Default sample/marker frequency.
Definition: Logger.h.in:37
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
void sa_destroy(strarray *sa)
Destroy array and contents.
Definition: strarray.c:182
strarray * sa_new(int entries)
Allocate storage for a new array.
Definition: strarray.c:37
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
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
Queuable message.
Definition: messages.h:71
Array of strings.
Definition: strarray.h:43
Timer specific parameters.
Definition: LoggerTime.h:46
uint8_t sourceNum
Source ID for messages.
Definition: LoggerTime.h:47
char * sourceName
Name to report for this timer.
Definition: LoggerTime.h:48
int frequency
Aim to sample this many times per second.
Definition: LoggerTime.h:49