GPS Device
Loading...
Searching...
No Matches
datalog.c
Go to the documentation of this file.
1
11
12#include <stdbool.h>
13
14#include "datalog.h"
15#include "common.h"
16#include "configuration.h"
17#include "err.h"
18#include "init.h"
19#include "log.h"
20#include "modules.h"
21#include "cmd.h"
22#include "ff.h"
24#include "neo6.h"
25
39 char name[CONFIG_DATALOG_MAX_NAME_LEN];
40
43
45 uint32_t last_upd_time;
46
48 FATFS fs;
49
51 FIL fil;
52
54 FRESULT fr;
55
57 UINT bw;
58
60 UINT br;
61
63 bool in_use;
64
67};
68
76struct internal_state_s {
78 DATALOG_HANDLE_ts datalogs[CONFIG_DATALOG_MAX_OBJECTS];
79
81 uint32_t datalog_num;
82
85
88
90 bool initialized;
91
93 bool started;
94};
95
98
99/* ---- Forward declaration for command handler ---- */
100static ERR_te datalog_cmd_list_handler(uint32_t argc, char **argv);
101
110 {
111 .name = "list",
112 .help = "Lists active datalog objects, usage: datalog list",
113 .handler = datalog_cmd_list_handler
114 }
115};
116
125 .cmds_ptr = datalog_cmds,
126 .num_cmds = sizeof(datalog_cmds) / sizeof(datalog_cmds[0]),
127 .log_level_ptr = &internal_state.log_level,
128 .name = "datalog"
129};
130
135
138 ERR_te err;
139
140 if(internal_state.initialized) {
142 }
143
144 internal_state = (struct internal_state_s){ 0 };
145
146 internal_state.log_level = LOG_LEVEL_INFO;
148 internal_state.initialized = true;
149 internal_state.started = false;
150
151 init_log();
152 init_systick();
153 init_neo6();
154
156 if(err != ERR_OK) {
157 LOG_ERROR(
158 internal_state.subsys,
159 internal_state.log_level,
160 "datalog_init_subsys: cmd_register error"
161 );
162 return err;
163 }
164 LOG_INFO(
165 internal_state.subsys,
166 internal_state.log_level,
167 "datalog_init_subsys: subsys initialized"
168 );
169
170 return ERR_OK;
171}
172
175 if(internal_state.initialized) {
176 internal_state = (struct internal_state_s){ 0 };
177
179 }
180 else {
181 LOG_ERROR(
182 internal_state.subsys,
183 internal_state.log_level,
184 "datalog_deinit_subsys: subsys is not initialized"
185 );
186
188 }
189 LOG_INFO(
190 internal_state.subsys,
191 internal_state.log_level,
192 "datalog_deinit_subsys: subsys deinitialized"
193 );
194
195 return ERR_OK;
196}
197
200 if(internal_state.initialized && !internal_state.started) {
201 internal_state.started = true;
202
203 LOG_INFO(
204 internal_state.subsys,
205 internal_state.log_level,
206 "datalog_start_subsys: subsys started"
207 );
208 }
209 else {
210 LOG_ERROR(
211 internal_state.subsys,
212 internal_state.log_level,
213 "datalog_start_subsys: subsys not initialized or already started"
214 );
215
216 return ERR_UNKNOWN;
217 }
218
219 return ERR_OK;
220}
221
224 if(internal_state.initialized && internal_state.started) {
225 internal_state.started = false;
226
227 LOG_INFO(
228 internal_state.subsys,
229 internal_state.log_level,
230 "datalog_stop_subsys: subsys stopped"
231 );
232 }
233 else {
234 LOG_ERROR(
235 internal_state.subsys,
236 internal_state.log_level,
237 "datalog_stop_subsys: subsys not initialized or already already stopped"
238 );
239
240 return ERR_UNKNOWN;
241 }
242
243 return ERR_OK;
244}
245
247ERR_te datalog_init_handle(DATALOG_CFG_ts const *datalog_cfg, DATALOG_HANDLE_ts **datalog_handle_o) {
248 uint8_t free_index;
249
250 if(!internal_state.initialized) {
251 LOG_ERROR(
252 internal_state.subsys,
253 internal_state.log_level,
254 "datalog_init_handle: subsys not initialized"
255 );
256
258 }
259
260 if(internal_state.datalog_num == CONFIG_DATALOG_MAX_OBJECTS) {
262 internal_state.subsys,
263 internal_state.log_level,
264 "datalog_init_handle: subsystem out of memory space"
265 );
266
268 }
269
270 if(!datalog_cfg) {
271 LOG_ERROR(
272 internal_state.subsys,
273 internal_state.log_level,
274 "datalog_init_handle: invalid argument"
275 );
277 }
278
279 bool found = false;
280
281 for(uint32_t i = 0; i < CONFIG_DATALOG_MAX_OBJECTS; i++) {
282 if(!internal_state.datalogs[i].in_use) {
283 free_index = i;
284 found = true;
285 break;
286 }
287 }
288
289 if(!found)
291
292 // Mount filesystem
293 internal_state.datalogs[free_index].fr = f_mount(&internal_state.datalogs[free_index].fs, "0:", 1);
294 if (internal_state.datalogs[free_index].fr != FR_OK) {
296 }
297
298 // Create / open file
299 internal_state.datalogs[free_index].fr = f_open(&internal_state.datalogs[free_index].fil, "0:datalog.txt", FA_CREATE_ALWAYS | FA_WRITE);
300 if (internal_state.datalogs[free_index].fr != FR_OK) {
302 }
303
304 f_close(&internal_state.datalogs[free_index].fil);
305
306 str_cpy(
307 internal_state.datalogs[free_index].name,
308 datalog_cfg->name,
309 get_str_len(datalog_cfg->name) + 1
310 );
311 internal_state.datalogs[free_index].datalog_time = datalog_cfg->datalog_time;
312 internal_state.datalogs[free_index].in_use = true;
313
314 internal_state.datalog_num++;
315
316 *datalog_handle_o = &internal_state.datalogs[free_index];
317
318 LOG_INFO(
319 internal_state.subsys,
320 internal_state.log_level,
321 "datalog_init_handle: handle %s initialized",
322 internal_state.datalogs[free_index].name
323 );
324
325 return ERR_OK;
326}
327
330 if(!datalog_handle->initialized) {
331 LOG_ERROR(
332 internal_state.subsys,
333 internal_state.log_level,
334 "datalog_deinit_handle: handle not initialized"
335 );
336 return ERR_ILLEGAL_ACTION;
337 }
338
339 if(internal_state.datalog_num == 0) {
340 LOG_ERROR(
341 internal_state.subsys,
342 internal_state.log_level,
343 "datalog_deinit_handle: no such handle to deinitialize"
344 );
345
347 }
348
349 for(uint32_t i = 0; i < CONFIG_DATALOG_MAX_OBJECTS; i++) {
350 if(&internal_state.datalogs[i] == datalog_handle) {
351 uint8_t name_len = get_str_len(internal_state.datalogs[i].name) + 1;
352 char name[name_len];
353 str_cpy(name, internal_state.datalogs[i].name, name_len);
354
355 internal_state.datalogs[i] = (DATALOG_HANDLE_ts){ 0 };
356
357 internal_state.datalog_num--;
358
359 LOG_INFO(
360 internal_state.subsys,
361 internal_state.log_level,
362 "datalog_deinit_handle: handle %s deinitialized",
363 name
364 );
365
366 break;
367 }
368
369 if(i == CONFIG_DATALOG_MAX_OBJECTS - 1) {
370 LOG_ERROR(
371 internal_state.subsys,
372 internal_state.log_level,
373 "datalog_deinit_handle: no such handle to deinitialize"
374 );
375
377 }
378 }
379
380 return ERR_OK;
381}
382
385 uint32_t time = systick_get_ms();
386
387 // Time interval has not been reached yet, return
388 if(time - datalog_handle->last_upd_time < datalog_handle->datalog_time * 1000) {
389 return ERR_OK;
390 }
391
392 // Open file
393 datalog_handle->fr = f_open(&datalog_handle->fil, "0:datalog.txt", FA_OPEN_APPEND | FA_WRITE);
394 if (datalog_handle->fr != FR_OK) {
395 return ERR_UNKNOWN;
396 }
397
398 NEO6_INFO_ts *neo6_info = (void*)0;
399 neo6_get_info(&neo6_info);
400
401 // Set up time string
402 uint32_t time_len = get_str_len(neo6_info->time) + 2;
403 char time_str[time_len];
404 str_cpy(time_str, neo6_info->time, time_len - 2);
405 time_str[time_len - 2] = '\r';
406 time_str[time_len - 1] = '\n';
407
408 // Write time string to file
409 datalog_handle->fr = f_write(&datalog_handle->fil, time_str, time_len, &datalog_handle->bw);
410 if (datalog_handle->fr != FR_OK || datalog_handle->bw != time_len) {
411 f_close(&datalog_handle->fil);
412 return ERR_UNKNOWN;
413 }
414
415 // Set up latitude string
416 uint32_t lat_len = get_str_len(neo6_info->lat) + 2;
417 char lat_str[lat_len];
418 str_cpy(lat_str, neo6_info->lat, lat_len - 2);
419 lat_str[lat_len - 2] = '\r';
420 lat_str[lat_len - 1] = '\n';
421
422 // Write latitude string to file
423 datalog_handle->fr = f_write(&datalog_handle->fil, lat_str, lat_len, &datalog_handle->bw);
424 if (datalog_handle->fr != FR_OK || datalog_handle->bw != lat_len) {
425 f_close(&datalog_handle->fil);
426 return ERR_UNKNOWN;
427 }
428
429 // Set up longitude string
430 uint32_t lon_len = get_str_len(neo6_info->lon) + 2;
431 char lon_str[lon_len];
432 str_cpy(lon_str, neo6_info->lon, lon_len - 2);
433 lon_str[lon_len - 2] = '\r';
434 lon_str[lon_len - 1] = '\n';
435
436 // Write longitude string to file
437 datalog_handle->fr = f_write(&datalog_handle->fil, lon_str, lon_len, &datalog_handle->bw);
438 if (datalog_handle->fr != FR_OK || datalog_handle->bw != lon_len) {
439 f_close(&datalog_handle->fil);
440 return ERR_UNKNOWN;
441 }
442
443 // Set up orthometric height string
444 uint32_t ort_height_len = get_str_len(neo6_info->ort_height) + 2;
445 char ort_height_str[ort_height_len];
446 str_cpy(ort_height_str, neo6_info->ort_height, ort_height_len - 2);
447 ort_height_str[ort_height_len - 2] = '\r';
448 ort_height_str[ort_height_len - 1] = '\n';
449
450 // Write orthometric height string to file
451 datalog_handle->fr = f_write(&datalog_handle->fil, ort_height_str, ort_height_len, &datalog_handle->bw);
452 if (datalog_handle->fr != FR_OK || datalog_handle->bw != ort_height_len) {
453 f_close(&datalog_handle->fil);
454 return ERR_UNKNOWN;
455 }
456
457 // Set up movement speed string
458 uint32_t mov_speed_len = get_str_len(neo6_info->mov_speed) + 2;
459 char mov_speed_str[mov_speed_len];
460 str_cpy(mov_speed_str, neo6_info->mov_speed, mov_speed_len - 2);
461 mov_speed_str[mov_speed_len - 2] = '\r';
462 mov_speed_str[mov_speed_len - 1] = '\n';
463
464 // Write movement speed string to file
465 datalog_handle->fr = f_write(&datalog_handle->fil, mov_speed_str, mov_speed_len, &datalog_handle->bw);
466 if (datalog_handle->fr != FR_OK || datalog_handle->bw != mov_speed_len) {
467 f_close(&datalog_handle->fil);
468 return ERR_UNKNOWN;
469 }
470
471 // Set up movement direction string
472 uint32_t mov_dir_len = get_str_len(neo6_info->mov_dir) + 2;
473 char mov_dir_str[mov_dir_len];
474 str_cpy(mov_dir_str, neo6_info->mov_dir, mov_dir_len - 2);
475 mov_dir_str[mov_dir_len - 2] = '\r';
476 mov_dir_str[mov_dir_len - 1] = '\n';
477
478 // Write movement direction string to file
479 datalog_handle->fr = f_write(&datalog_handle->fil, mov_dir_str, mov_dir_len, &datalog_handle->bw);
480 if (datalog_handle->fr != FR_OK || datalog_handle->bw != mov_dir_len) {
481 f_close(&datalog_handle->fil);
482 return ERR_UNKNOWN;
483 }
484
485 datalog_handle->fr = f_write(&datalog_handle->fil, "\r\n", 2, &datalog_handle->bw);
486
487 f_close(&datalog_handle->fil);
488
489 datalog_handle->last_upd_time = systick_get_ms();
490
491 return ERR_OK;
492}
493
495
500
517static ERR_te datalog_cmd_list_handler(uint32_t argc, char **argv) {
518 if(argc != 2) {
519 LOG_ERROR(
520 internal_state.subsys,
521 internal_state.log_level,
522 "datalog_cmd_list_handler: invalid arguments"
523 );
525 }
526
527 for(uint32_t i = 0; i < CONFIG_DATALOG_MAX_OBJECTS; i++) {
528 if(internal_state.datalogs[i].in_use == true) {
529 LOG_INFO(
530 internal_state.subsys,
531 internal_state.log_level,
532 "%s",
533 internal_state.datalogs[i].name
534 );
535 }
536 }
537
538 return ERR_OK;
539}
540
static struct internal_state_s internal_state
Singleton instance of the SysTick driver internal state.
Arm Cortex-M4 SysTick driver public API.
Command subsystem public API.
Common utility module public API.
CMD_INFO_ts datalog_cmds[]
Table of CLI commands registered by the data log subsystem.
Definition datalog.c:109
CMD_CLIENT_INFO_ts datalog_cmd_client_info
Registration descriptor passed to the command subsystem.
Definition datalog.c:124
Data log module public API.
System-wide error code definitions.
ERR_te cmd_deregister(CMD_CLIENT_INFO_ts const *cmd_client_info)
Deregisters a client from the command subsystem.
Definition cmd.c:66
ERR_te cmd_register(CMD_CLIENT_INFO_ts *cmd_client_info)
Registers a client with the command subsystem.
Definition cmd.c:51
int str_cpy(char *str_to, const char *str_from, uint32_t len)
Copies a null-terminated string into a destination buffer.
Definition common.c:333
uint32_t get_str_len(char const *str)
Returns the length of a string, excluding the null terminator.
Definition common.c:22
uint32_t systick_get_ms(void)
Returns the number of milliseconds elapsed since SysTick was initialized.
static ERR_te datalog_cmd_list_handler(uint32_t argc, char **argv)
CLI handler for the "list" command. Logs the names of all active data log handles.
Definition datalog.c:517
ERR_te datalog_init_handle(DATALOG_CFG_ts const *datalog_cfg, DATALOG_HANDLE_ts **datalog_handle_o)
Initializes and registers a data log handle.
Definition datalog.c:247
ERR_te datalog_stop_subsys(void)
Stops the data log subsystem.
Definition datalog.c:223
ERR_te datalog_deinit_handle(DATALOG_HANDLE_ts const *datalog_handle)
Deinitializes a data log handle.
Definition datalog.c:329
ERR_te datalog_run_handle(DATALOG_HANDLE_ts *datalog_handle)
Writes a GPS data entry to the log file if the logging interval has elapsed.
Definition datalog.c:384
ERR_te datalog_start_subsys(void)
Starts the data log subsystem.
Definition datalog.c:199
ERR_te datalog_deinit_subsys(void)
Deinitializes the data log subsystem.
Definition datalog.c:174
ERR_te datalog_init_subsys(void)
Initializes the data log subsystem.
Definition datalog.c:137
struct datalog_handle_s DATALOG_HANDLE_ts
Opaque handle representing a data log instance.
Definition datalog.h:88
DATALOG_TIME_te
Logging interval in seconds between consecutive log entries.
Definition datalog.h:52
ERR_te
Standard return type used by all public API functions.
Definition err.h:35
@ ERR_UNINITIALZIED_OBJECT
Definition err.h:42
@ ERR_ILLEGAL_ACTION
Definition err.h:50
@ ERR_DEINITIALIZATION_FAILURE
Definition err.h:44
@ ERR_UNKNOWN
Definition err.h:37
@ ERR_OK
Definition err.h:36
@ ERR_MODULE_ALREADY_INITIALIZED
Definition err.h:54
@ ERR_NOT_ENOUGH_SPACE
Definition err.h:49
@ ERR_INITIALIZATION_FAILURE
Definition err.h:43
@ ERR_INVALID_ARGUMENT
Definition err.h:38
ERR_te init_log(void)
Initializes the logging subsystem.
Definition init.c:25
ERR_te init_neo6(void)
Initializes the NEO-6 GPS module.
Definition init.c:52
ERR_te init_systick(void)
Initializes the SysTick timer.
Definition init.c:40
#define LOG_CRITICAL(subsys, lvl, fmt,...)
Definition log.h:259
#define LOG_ERROR(subsys, lvl, fmt,...)
Definition log.h:258
#define LOG_INFO(subsys, lvl, fmt,...)
Definition log.h:255
LOG_LEVEL_te
Log severity levels, in ascending order of severity.
Definition log.h:63
@ LOG_LEVEL_INFO
Definition log.h:64
MODULES_te
Identifies a subsystem for use in logging and CLI output.
Definition modules.h:42
@ MODULES_DATALOG
Definition modules.h:50
ERR_te neo6_get_info(NEO6_INFO_ts **neo6_info_o)
Returns a pointer to the internal GPS data structure.
Definition neo6.c:430
Common initialization public API.
Log subsystem public API.
DATALOG_HANDLE_ts * datalog_handle
Definition main.c:66
System module identifier definitions.
NEO-6M GPS module public API.
Describes a subsystem client registering with the command module.
Definition cmd.h:92
Describes a single command exposed by a client.
Definition cmd.h:72
Configuration structure for initializing a data log handle.
Definition datalog.h:72
char name[CONFIG_SD_MAX_NAME_LEN]
Definition datalog.h:74
DATALOG_TIME_te datalog_time
Definition datalog.h:77
Holds all GPS data fields parsed from incoming NMEA sentences.
Definition neo6.h:85
char mov_dir[16]
Definition neo6.h:92
char lon[16]
Definition neo6.h:87
char ort_height[16]
Definition neo6.h:88
char time[16]
Definition neo6.h:90
char mov_speed[16]
Definition neo6.h:93
char lat[16]
Definition neo6.h:86
Internal structure representing a single data log instance.
Definition datalog.c:37
char name[CONFIG_DATALOG_MAX_NAME_LEN]
Definition datalog.c:39
uint32_t last_upd_time
Definition datalog.c:45
DATALOG_TIME_te datalog_time
Definition datalog.c:42
Internal state of the SysTick driver.
MODULES_te subsys
Definition button.c:95
uint32_t datalog_num
Definition datalog.c:81
DATALOG_HANDLE_ts datalogs[CONFIG_DATALOG_MAX_OBJECTS]
Definition datalog.c:78
LOG_LEVEL_te log_level
Definition button.c:92