SELKIELogger  1.0.0
LoggerN2K.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 "LoggerN2K.h"
24 #include "LoggerSignals.h"
25 
26 #include "N2KMessages.h"
27 
36 void *n2k_setup(void *ptargs) {
37  log_thread_args_t *args = (log_thread_args_t *)ptargs;
38  n2k_params *n2kInfo = (n2k_params *)args->dParams;
39 
40  n2kInfo->handle = n2k_openConnection(n2kInfo->portName, n2kInfo->baudRate);
41  if (n2kInfo->handle < 0) {
42  log_error(args->pstate, "[N2K:%s] Unable to open a connection", args->tag);
43  args->returnCode = -1;
44  return NULL;
45  }
46 
47  log_info(args->pstate, 2, "[N2K:%s] Connected", args->tag);
48  args->returnCode = 0;
49  return NULL;
50 }
51 
62 void *n2k_logging(void *ptargs) {
64  log_thread_args_t *args = (log_thread_args_t *)ptargs;
65  n2k_params *n2kInfo = (n2k_params *)args->dParams;
66 
67  log_info(args->pstate, 1, "[N2K:%s] Logging thread started", args->tag);
68 
69  uint8_t *buf = calloc(N2K_BUFF, sizeof(uint8_t));
70  size_t n2k_index = 0;
71  size_t n2k_hw = 0;
72  while (!shutdownFlag) {
73  n2k_act_message out = {0};
74  if (n2k_act_readMessage_buf(n2kInfo->handle, &out, buf, &n2k_index, &n2k_hw)) {
75  bool handled = false;
76 
77  if (out.PGN == 129025) {
78  double lat = 0;
79  double lon = 0;
80  if (n2k_129025_values(&out, &lat, &lon)) {
81  msg_t *rm = NULL;
82  rm = msg_new_float(n2kInfo->sourceNum, N2KCHAN_LAT, lat);
83  if (!queue_push(args->logQ, rm)) {
84  log_error(
85  args->pstate,
86  "[N2K:%s] Error pushing message to queue",
87  args->tag);
88  msg_destroy(rm);
89  args->returnCode = -1;
90  pthread_exit(&(args->returnCode));
91  }
92  rm = NULL; // Discard pointer as message is now "owned" by
93  // queue
94  rm = msg_new_float(n2kInfo->sourceNum, N2KCHAN_LON, lon);
95  if (!queue_push(args->logQ, rm)) {
96  log_error(
97  args->pstate,
98  "[N2K:%s] Error pushing message to queue",
99  args->tag);
100  msg_destroy(rm);
101  args->returnCode = -1;
102  pthread_exit(&(args->returnCode));
103  }
104  } else {
105  log_warning(
106  args->pstate,
107  "[N2K:%s] Failed to decode message (PGN %d, Source %d)",
108  args->tag, out.PGN, out.src);
109  }
110  }
111  if (!handled) {
112  size_t mlen = 0;
113  uint8_t *rd = NULL;
114  if (!n2k_act_to_bytes(&out, &rd, &mlen)) {
115  log_warning(
116  args->pstate,
117  "[N2K:%s] Unable to serialise message (PGN %d, Source %d)",
118  args->tag, out.PGN, out.src);
119  if (rd) { free(rd); }
120  continue;
121  }
122  msg_t *rm = NULL;
123  rm = msg_new_bytes(n2kInfo->sourceNum, N2KCHAN_RAW, mlen, rd);
124  if (!queue_push(args->logQ, rm)) {
125  log_error(args->pstate,
126  "[N2K:%s] Error pushing message to queue",
127  args->tag);
128  msg_destroy(rm);
129  args->returnCode = -1;
130  pthread_exit(&(args->returnCode));
131  }
132  }
133  // Do not destroy or free message here
134  // After pushing it to the queue, it is the responsibility of the
135  // consumer to dispose of it after use.
136  } else {
137  if (!(out.priority == 0xFF || out.priority == 0xFD ||
138  out.priority == 0xEE)) {
139  // 0xFF, 0xFD and 0xEE are used to signal recoverable
140  // states that resulted in no valid message.
141  //
142  // 0xFF and 0xFD indicate an out of data error, which is
143  // not a problem for serial monitoring, but might indicate
144  // EOF when reading from file
145  //
146  // 0xEE indicates an invalid message following valid sync
147  // bytes
148  log_error(args->pstate,
149  "[N2K:%s] Error signalled from n2k_readMessage_buf",
150  args->tag);
151  args->returnCode = -2;
152  free(buf);
153  pthread_exit(&(args->returnCode));
154  }
155  // We've already exited (via pthread_exit) for error
156  // cases, so at this point sleep briefly and wait for
157  // more data
158  usleep(SERIAL_SLEEP);
159  }
160  if (out.data) { free(out.data); }
161 
162  if (n2k_hw == 1024) { log_error(args->pstate, "[N2K:%s] Buffer full", args->tag); }
163 
164  if (n2k_hw == 1024 && n2k_index == 0) {
165  log_error(args->pstate, "[N2K:%s] Ignoring first 100 bytes", args->tag);
166  n2k_index += 100; // Leave memory juggling to the other functions
167  }
168  }
169  free(buf);
170  log_info(args->pstate, 1, "[N2K:%s] Logging thread exiting", args->tag);
171  pthread_exit(NULL);
172  return NULL; // Superfluous, as returning zero via pthread_exit above
173 }
174 
181 void *n2k_shutdown(void *ptargs) {
182  log_thread_args_t *args = (log_thread_args_t *)ptargs;
183  n2k_params *n2kInfo = (n2k_params *)args->dParams;
184 
185  if (n2kInfo->handle >= 0) { // Admittedly 0 is unlikely
186  n2k_closeConnection(n2kInfo->handle);
187  }
188  n2kInfo->handle = -1;
189  if (n2kInfo->sourceName) {
190  free(n2kInfo->sourceName);
191  n2kInfo->sourceName = NULL;
192  }
193  if (n2kInfo->portName) {
194  free(n2kInfo->portName);
195  n2kInfo->portName = NULL;
196  }
197  return NULL;
198 }
199 
208 void *n2k_channels(void *ptargs) {
209  log_thread_args_t *args = (log_thread_args_t *)ptargs;
210  n2k_params *n2kInfo = (n2k_params *)args->dParams;
211 
212  msg_t *m_sn = msg_new_string(n2kInfo->sourceNum, SLCHAN_NAME, strlen(n2kInfo->sourceName),
213  n2kInfo->sourceName);
214 
215  if (!queue_push(args->logQ, m_sn)) {
216  log_error(args->pstate, "[N2K:%s] Error pushing channel name to queue", args->tag);
217  msg_destroy(m_sn);
218  args->returnCode = -1;
219  pthread_exit(&(args->returnCode));
220  }
221 
222  strarray *channels = sa_new(6);
223  sa_create_entry(channels, N2KCHAN_NAME, 4, "Name");
224  sa_create_entry(channels, N2KCHAN_MAP, 8, "Channels");
225  sa_create_entry(channels, N2KCHAN_TSTAMP, 9, "Timestamp");
226  sa_create_entry(channels, N2KCHAN_RAW, 8, "Raw N2K");
227  sa_create_entry(channels, N2KCHAN_LAT, 9, "Latitude");
228  sa_create_entry(channels, N2KCHAN_LON, 9, "Longitude");
229 
230  msg_t *m_cmap = msg_new_string_array(n2kInfo->sourceNum, SLCHAN_MAP, channels);
231 
232  if (!queue_push(args->logQ, m_cmap)) {
233  log_error(args->pstate, "[N2K:%s] Error pushing channel map to queue", args->tag);
234  msg_destroy(m_cmap);
235  sa_destroy(channels);
236  free(channels);
237  args->returnCode = -1;
238  pthread_exit(&(args->returnCode));
239  }
240 
241  sa_destroy(channels);
242  free(channels);
243  return NULL;
244 }
245 
251  .logging = &n2k_logging,
252  .shutdown = &n2k_shutdown,
253  .channels = &n2k_channels};
254  return cb;
255 }
256 
261  n2k_params gp = {
262  .portName = NULL, .sourceNum = SLSOURCE_N2K, .baudRate = 115200, .handle = -1};
263  return gp;
264 }
265 
272  if (lta->dParams) {
273  log_error(lta->pstate, "[N2K:%s] Refusing to reconfigure", lta->tag);
274  return false;
275  }
276 
277  n2k_params *nmp = calloc(1, sizeof(n2k_params));
278  if (!nmp) {
279  log_error(lta->pstate, "[N2K:%s] Unable to allocate memory for device parameters",
280  lta->tag);
281  return false;
282  }
283  (*nmp) = n2k_getParams();
284 
285  config_kv *t = NULL;
286 
287  if ((t = config_get_key(s, "name"))) {
288  nmp->sourceName = config_qstrdup(t->value);
289  } else {
290  // Must set a name, so nick the tag value
291  nmp->sourceName = strdup(lta->tag);
292  }
293  t = NULL;
294 
295  if ((t = config_get_key(s, "port"))) { nmp->portName = config_qstrdup(t->value); }
296  t = NULL;
297 
298  if ((t = config_get_key(s, "baud"))) {
299  errno = 0;
300  nmp->baudRate = strtol(t->value, NULL, 0);
301  if (errno) {
302  log_error(lta->pstate, "[N2K:%s] Error parsing baud rate: %s", lta->tag,
303  strerror(errno));
304  free(nmp);
305  return false;
306  }
307  }
308  t = NULL;
309 
310  if ((t = config_get_key(s, "sourcenum"))) {
311  errno = 0;
312  int sn = strtol(t->value, NULL, 0);
313  if (errno) {
314  log_error(lta->pstate, "[N2K:%s] Error parsing source number: %s",
315  lta->tag, strerror(errno));
316  free(nmp);
317  return false;
318  }
319  if (sn < 0) {
320  log_error(lta->pstate, "[N2K:%s] Invalid source number (%s)", lta->tag,
321  t->value);
322  free(nmp);
323  return false;
324  }
325  if (sn < 10) {
326  nmp->sourceNum += sn;
327  } else {
328  nmp->sourceNum = sn;
329  if (sn < SLSOURCE_N2K || sn > (SLSOURCE_N2K + 0x0F)) {
330  log_warning(
331  lta->pstate,
332  "[N2K:%s] Unexpected Source ID number (0x%02x)- this may cause analysis problems",
333  lta->tag, sn);
334  }
335  }
336  }
337  t = NULL;
338  lta->dParams = nmp;
339  return true;
340 }
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.
#define N2K_BUFF
Default serial buffer allocation size.
Definition: N2KConnection.h:35
void n2k_closeConnection(int handle)
Close N2K serial connection.
Definition: N2KConnection.c:46
int n2k_openConnection(const char *device, const int baud)
Open connection to an N2K serial device.
Definition: N2KConnection.c:38
bool n2k_act_readMessage_buf(int handle, n2k_act_message *out, uint8_t buf[N2K_BUFF], size_t *index, size_t *hw)
Read data from handle, and parse message if able.
Definition: N2KConnection.c:85
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_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
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
bool n2k_129025_values(const n2k_act_message *n, double *lat, double *lon)
Extract values from PGN 129025: Device position.
Definition: N2KMessages.c:320
bool n2k_act_to_bytes(const n2k_act_message *act, uint8_t **out, size_t *len)
Convert N2K message to a series of bytes compatible with ACT gateway devices.
Definition: N2KTypes.c:38
#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 N2KCHAN_RAW
Raw data (recorded unmodified)
Definition: LoggerN2K.h:49
bool n2k_parseConfig(log_thread_args_t *lta, config_section *s)
Take a configuration section and parse parameters.
Definition: LoggerN2K.c:271
void * n2k_channels(void *ptargs)
N2K Channel map.
Definition: LoggerN2K.c:208
void * n2k_setup(void *ptargs)
N2K Setup.
Definition: LoggerN2K.c:36
#define N2KCHAN_NAME
Source name.
Definition: LoggerN2K.h:46
#define N2KCHAN_MAP
Channel map.
Definition: LoggerN2K.h:47
#define N2KCHAN_LON
Longitude (from GPS)
Definition: LoggerN2K.h:51
void * n2k_shutdown(void *ptargs)
N2K Shutdown.
Definition: LoggerN2K.c:181
n2k_params n2k_getParams()
Fill out default N2K parameters.
Definition: LoggerN2K.c:260
void * n2k_logging(void *ptargs)
N2K logging (with pthread function signature)
Definition: LoggerN2K.c:62
#define N2KCHAN_LAT
Latitude (from GPS)
Definition: LoggerN2K.h:50
device_callbacks n2k_getCallbacks()
Fill out device callback functions for logging.
Definition: LoggerN2K.c:249
#define N2KCHAN_TSTAMP
Timestamps.
Definition: LoggerN2K.h:48
#define SLSOURCE_N2K
N2K bus.
Definition: sources.h:66
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
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
uint8_t priority
N2K Message priority value.
Definition: N2KTypes.h:83
uint32_t PGN
24 bit PGN identifier
Definition: N2KTypes.h:84
uint8_t * data
Message payload.
Definition: N2KTypes.h:89
uint8_t src
Message source.
Definition: N2KTypes.h:86
N2K Device specific parameters.
Definition: LoggerN2K.h:54
char * portName
Target port name.
Definition: LoggerN2K.h:55
uint8_t sourceNum
Source ID for messages.
Definition: LoggerN2K.h:57
char * sourceName
User defined name for this source.
Definition: LoggerN2K.h:56
int baudRate
Baud rate for operations.
Definition: LoggerN2K.h:58
int handle
Handle for currently opened device.
Definition: LoggerN2K.h:59
Array of strings.
Definition: strarray.h:43