Paparazzi UAS v7.1_unstable
Paparazzi is a free software Unmanned Aircraft System.
Loading...
Searching...
No Matches
sdlog_linux.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2026 Freek van Tienen <freek.v.tienen@gmail.com>
3 *
4 * This file is part of paparazzi.
5 *
6 * paparazzi is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * paparazzi is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with paparazzi; see the file COPYING. If not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
31
34#include "mcu_periph/sys_time.h"
35#include "generated/airframe.h"
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <stdarg.h>
40#include <string.h>
41#include <dirent.h>
42#include <errno.h>
43#include <sys/stat.h>
44#include <sys/types.h>
45
48#ifndef SDLOG_DIR
49#if SITL
50#define SDLOG_DIR NULL
51#else
52#define SDLOG_DIR "/data/ftp/internal_000/sdlog"
53#endif
54#endif
55
57#ifndef SDLOG_AUTO_FLUSH_PERIOD
58#if SITL
59#define SDLOG_AUTO_FLUSH_PERIOD 1
60#else
61#define SDLOG_AUTO_FLUSH_PERIOD 10
62#endif
63#endif
64
65#define SDLOG_MAX_FILES 4
66#define SDLOG_MAX_MSG_LEN 512
67
68static const char PPRZ_LOG_NAME[] = "pprzlog_";
69static const char PPRZ_LOG_DIR[] = "PPRZ";
70
73#ifndef SDLOG_CONF
74#define SDLOG_CONF TRUE
75#endif
76#if SDLOG_CONF
77#include "generated/aircraft_conf.h"
78#endif
81#if FLIGHTRECORDER_SDLOG
82static const char FLIGHTRECORDER_LOG_NAME[] = "fr_";
83static const char FR_LOG_DIR[] = "FLIGHT_RECORDER";
84FileDes flightRecorderLogFile = -1;
85#endif
86
89
92void usbStorage_enable_usb_storage(float e __attribute__((unused))) { usb_storage_status = 0; }
93
95static size_t sdlog_bytes = 0;
96static float sdlog_last_flush = 0;
97static enum {
103static char sdlog_filenames[68];
104
111static __thread struct sdlog_msg sdlog_msg;
112
113static FILE *sdlog_file(FileDes fd)
114{
116 return NULL;
117 }
118 return sdlog_files[fd];
119}
120
123{
124#ifdef EDQUOT
125 if (err == EDQUOT) {
126 return SDLOG_FSFULL;
127 }
128#endif
129 return (err == ENOSPC || err == EFBIG) ? SDLOG_FSFULL : SDLOG_FATFS_ERROR;
130}
131
132static void sdlog_write(FileDes fd, const uint8_t *data, size_t len)
133{
134 FILE *f = sdlog_file(fd);
135 if (f == NULL) {
136 return;
137 }
138 if (fwrite(data, 1, len, f) != len) {
141 return;
142 }
143 sdlog_bytes += len;
144
145 float now = get_sys_time_float();
147 sdlog_last_flush = now;
149 }
150}
151
152/* sdLog API */
153SdioError sdLogWriteLog(const FileDes fd, const char *fmt, ...)
154{
156 va_list ap;
157 va_start(ap, fmt);
158 int len = vsnprintf(buf, sizeof(buf), fmt, ap);
159 va_end(ap);
160 if (len < 0) {
162 }
163 if ((size_t)len >= sizeof(buf)) {
164 len = sizeof(buf) - 1;
165 }
167 return sdlog_error;
168}
169
170SdioError sdLogWriteRaw(const FileDes fd, const uint8_t *buffer, const size_t len)
171{
172 sdlog_write(fd, buffer, len);
173 return sdlog_error;
174}
175
177{
178 sdlog_write(fd, &value, 1);
179 return sdlog_error;
180}
181
183{
184 FILE *f = sdlog_file(fd);
185 if (f != NULL) {
186 fflush(f);
187 }
188 return sdlog_error;
189}
190
192{
193 for (int i = 0; i < SDLOG_MAX_FILES; i++) {
194 if (sdlog_files[i] != NULL) {
195 fflush(sdlog_files[i]);
196 }
197 }
198 return sdlog_error;
199}
200
202{
203 return sdlog_bytes;
204}
205
207{
208 return sdlog_error;
209}
210
211/* Generic device API */
212static int sdlog_check_free_space(struct chibios_sdlog *p, long *fd __attribute__((unused)), uint16_t len)
213{
214 if (sdlog_file(*(p->file)) == NULL || len > SDLOG_MAX_MSG_LEN) {
215 return 0;
216 }
217 sdlog_msg.len = 0;
218 return 1;
219}
220
221static void sdlog_put_buffer(struct chibios_sdlog *p __attribute__((unused)), long fd __attribute__((unused)),
222 const uint8_t *data, uint16_t len)
223{
225 return;
226 }
227 memcpy(&sdlog_msg.buf[sdlog_msg.len], data, len);
228 sdlog_msg.len += len;
229}
230
231static void sdlog_put_byte(struct chibios_sdlog *p, long fd, uint8_t byte)
232{
233 sdlog_put_buffer(p, fd, &byte, 1);
234}
235
236static void sdlog_send(struct chibios_sdlog *p, long fd __attribute__((unused)))
237{
239 sdlog_msg.len = 0;
240}
241
242static int null_function(struct chibios_sdlog *p __attribute__((unused))) { return 0; }
243static uint8_t null_byte_function(struct chibios_sdlog *p __attribute__((unused))) { return 0; }
244
245void chibios_sdlog_init(struct chibios_sdlog *sdlog, FileDes *file)
246{
247 sdlog->file = file;
248 sdlog->device.periph = (void *)(sdlog);
249 sdlog->device.check_free_space = (check_free_space_t) sdlog_check_free_space;
250 sdlog->device.put_byte = (put_byte_t) sdlog_put_byte;
251 sdlog->device.put_buffer = (put_buffer_t) sdlog_put_buffer;
252 sdlog->device.send_message = (send_message_t) sdlog_send;
253 sdlog->device.char_available = (char_available_t) null_function; // write only
254 sdlog->device.get_byte = (get_byte_t) null_byte_function; // write only
255}
256
257/* Files */
258
260static int sdlog_mkdir(const char *path)
261{
262 char tmp[256];
263 snprintf(tmp, sizeof(tmp), "%s", path);
264 for (char *c = tmp + 1; *c; c++) {
265 if (*c == '/') {
266 *c = '\0';
267 if (mkdir(tmp, 0755) != 0 && errno != EEXIST) {
268 return -1;
269 }
270 *c = '/';
271 }
272 }
273 if (mkdir(tmp, 0755) != 0 && errno != EEXIST) {
274 return -1;
275 }
276 return 0;
277}
278
280static int sdlog_next_number(const char *dir, const char *prefix)
281{
282 int next = 1;
283 size_t plen = strlen(prefix);
284 DIR *d = opendir(dir);
285 if (d == NULL) {
286 return next;
287 }
288 struct dirent *e;
289 while ((e = readdir(d)) != NULL) {
290 int num;
291 char ext[8];
292 if (strncmp(e->d_name, prefix, plen) != 0 ||
293 sscanf(e->d_name + plen, "%d.%7s", &num, ext) != 2 || strcmp(ext, "LOG") != 0) {
294 continue;
295 }
296 char path[512];
297 struct stat st;
298 snprintf(path, sizeof(path), "%s/%s", dir, e->d_name);
299 if (stat(path, &st) == 0 && st.st_size == 0) {
300 remove(path);
301 } else if (num >= next) {
302 next = num + 1;
303 }
304 }
305 closedir(d);
306 return next;
307}
308
310static FileDes sdlog_open(const char *base, const char *dir, const char *prefix, char *name, size_t name_len)
311{
312 FileDes fd = -1;
313 for (int i = 0; i < SDLOG_MAX_FILES; i++) {
314 if (sdlog_files[i] == NULL) {
315 fd = i;
316 break;
317 }
318 }
319 if (fd < 0) {
321 return -1;
322 }
323
324 char path[256];
325 // a truncated path would silently log into another folder
326 if (snprintf(path, sizeof(path), "%s/%s", base, dir) >= (int)sizeof(path) || sdlog_mkdir(path) != 0) {
328 return -1;
329 }
330
331 int num = sdlog_next_number(path, prefix);
332 if (num > 9999) {
334 return -1;
335 }
336 snprintf(name, name_len, "%s%04d.LOG", prefix, num);
337
338 char filename[512];
339 snprintf(filename, sizeof(filename), "%s/%s", path, name);
340 sdlog_files[fd] = fopen(filename, "wb");
341 if (sdlog_files[fd] == NULL) {
343 return -1;
344 }
345 printf("[sdlog] Logging to %s\n", filename);
346 return fd;
347}
348
349#if SDLOG_CONF
351static SdioError sdlog_write_conf(const char *base, const char *dir)
352{
353 char path[512], tmp[520];
354 struct stat st;
355 if (snprintf(path, sizeof(path), "%s/%s/%s", base, dir, AIRCRAFT_CONF_NAME) >= (int)sizeof(path)) {
356 return SDLOG_INTERNAL_ERROR; // path too long
357 }
358 if (stat(path, &st) == 0) {
359 return SDLOG_OK;
360 }
361 snprintf(tmp, sizeof(tmp), "%s.tmp", path);
362 FILE *f = fopen(tmp, "wb");
363 if (f == NULL) {
364 return SDLOG_FATFS_ERROR;
365 }
366 int err = 0;
367 if (fwrite(aircraft_conf_gz, 1, sizeof(aircraft_conf_gz), f) != sizeof(aircraft_conf_gz)) {
368 err = errno;
369 }
370 if (fclose(f) != 0 && err == 0) {
371 err = errno;
372 }
373 if (err != 0) {
374 remove(tmp);
375 return sdlog_errno_error(err);
376 }
377 if (rename(tmp, path) != 0) {
378 remove(tmp);
379 return SDLOG_FATFS_ERROR;
380 }
381 return SDLOG_OK;
382}
383#endif
384
385#if PREFLIGHT_CHECKS
387static struct preflight_check_t sdlog_pfc;
388
389static void sdlog_preflight(struct preflight_result_t *result)
390{
392 const char *cause = (sdlog_status == SDLOG_STOPPED) ? "not started" : sdlog_error_string(sdlog_error);
393#ifdef SDLOG_PREFLIGHT_ERROR
394 preflight_error(result, "SD log not running: %s", cause);
395#else
396 preflight_warning(result, "SD log not running: %s", cause);
397#endif
398 } else {
399 preflight_success(result, "SDLogger running");
400 }
401}
402#endif
403
404#if PERIODIC_TELEMETRY
411static void sdlog_report_status(struct transport_tx *trans, struct link_device *dev)
412{
413 static int reported = SDLOG_STOPPED;
414 int status = sdlog_status;
415 // only on a change: the start (stopped to running or error), and every time it goes into error
416 if (status == reported || status == SDLOG_STOPPED || (status == SDLOG_RUNNING && reported == SDLOG_ERROR)) {
417 reported = status;
418 return;
419 }
420 reported = status;
421 char msg[120];
422 int len;
423 if (status == SDLOG_ERROR) {
424 len = snprintf(msg, sizeof(msg), "SD log error: %s", sdlog_error_string(sdlog_error));
425 } else if (sdlog_conf_error != SDLOG_OK) {
426 len = snprintf(msg, sizeof(msg), "SD log: configuration not written: %s", sdlog_error_string(sdlog_conf_error));
427 } else {
428 return; // a normal start is not reported
429 }
430 if (len > 0) {
431 len = Min(len, (int)sizeof(msg) - 1);
432 pprz_msg_send_INFO_MSG(trans, dev, AC_ID, len, msg);
433 }
434}
435
436static void send_sdlog_status(struct transport_tx *trans, struct link_device *dev)
437{
438 sdlog_report_status(trans, dev);
442 char *filenames = sdlog_filenames;
443 if (strlen(filenames) == 0) {
444 filenames = "none";
445 }
446 pprz_msg_send_LOGGER_STATUS(trans, dev, AC_ID, &status, &err, &used, strlen(filenames), filenames);
447}
448#endif
449
450static void sdlog_atexit(void)
451{
453}
454
456{
457#if PERIODIC_TELEMETRY
459#endif
460#if PREFLIGHT_CHECKS
461 preflight_check_register(&sdlog_pfc, sdlog_preflight);
462#endif
463
465
466 char base[256];
467 const char *dir = SDLOG_DIR;
468 if (dir == NULL) {
469 const char *home = getenv("PAPARAZZI_HOME");
470 snprintf(base, sizeof(base), "%s/var/logs/sdlog/%s", home ? home : ".", AIRFRAME_NAME);
471 } else {
472 snprintf(base, sizeof(base), "%s", dir);
473 }
474
475 char name[32];
477 pprzLogFile = sdlog_open(base, PPRZ_LOG_DIR, PPRZ_LOG_NAME, name, sizeof(name));
478 snprintf(sdlog_filenames, sizeof(sdlog_filenames), "%s", pprzLogFile != -1 ? name : "");
479#if SDLOG_CONF
480 if (pprzLogFile != -1) {
481 sdlog_conf_error = sdlog_write_conf(base, PPRZ_LOG_DIR);
482 }
483#endif
484#if FLIGHTRECORDER_SDLOG
485 flightRecorderLogFile = sdlog_open(base, FR_LOG_DIR, FLIGHTRECORDER_LOG_NAME, name, sizeof(name));
486 if (flightRecorderLogFile != -1) {
487 size_t l = strlen(sdlog_filenames);
488 snprintf(sdlog_filenames + l, sizeof(sdlog_filenames) - l, "%s%s", l > 0 ? "," : "", name);
489 }
490#endif
491
492 if (sdlog_error == SDLOG_OK) {
494 } else {
495 printf("[sdlog] Could not open the log files in %s (error %d)\n", base, sdlog_error);
497 }
499 atexit(sdlog_atexit);
500}
501
502void sdlog_chibios_finish(const bool flush)
503{
504 pprzLogFile = -1;
505#if FLIGHTRECORDER_SDLOG
506 flightRecorderLogFile = -1;
507#endif
508 for (int i = 0; i < SDLOG_MAX_FILES; i++) {
509 if (sdlog_files[i] != NULL) {
510 if (flush) {
511 fflush(sdlog_files[i]);
512 }
513 fclose(sdlog_files[i]);
514 sdlog_files[i] = NULL;
515 }
516 }
518}
519
521{
522 uint8_t ac_id = pprzlink_get_DL_INFO_MSG_UP_ac_id(buf);
523 if (ac_id != AC_ID && ac_id != 0xFF) {
524 return;
525 }
526 uint8_t fd = pprzlink_get_DL_INFO_MSG_UP_fd(buf);
527 if (pprzLogFile != -1 && (fd == DEST_INFO_MSG_ALL || fd == DEST_INFO_MSG_PPRZLOG)) {
528 uint8_t len = pprzlink_get_INFO_MSG_UP_msg_length(buf);
529 uint8_t *msg = (uint8_t *) pprzlink_get_DL_INFO_MSG_UP_msg(buf);
532 }
533}
static uint8_t status
static struct uart_periph * dev
#define Min(x, y)
Definition esc_dshot.c:110
uint8_t msg[10]
Buffer used for general comunication over SPI (out buffer).
static float p[2][2]
static FILE * pprzLogFile
void preflight_error(struct preflight_result_t *result, const char *fmt,...)
Register a preflight error used inside the preflight checking functions.
void preflight_success(struct preflight_result_t *result, const char *fmt,...)
Register a preflight success used inside the preflight checking functions.
void preflight_warning(struct preflight_result_t *result, const char *fmt,...)
Register a preflight warning used inside the preflight checking functions.
void preflight_check_register(struct preflight_check_t *check, preflight_check_f func)
Register a preflight check and add it to the linked list.
int8_t FileDes
Definition sdLog.h:128
SdioError
Definition sdLog.h:111
@ SDLOG_NOCARD
Definition sdLog.h:113
@ SDLOG_LOGNUM_ERROR
Definition sdLog.h:123
@ SDLOG_FATFS_ERROR
Definition sdLog.h:114
@ SDLOG_FSFULL
Definition sdLog.h:116
@ SDLOG_OK
Definition sdLog.h:112
@ SDLOG_FDFULL
Definition sdLog.h:117
@ SDLOG_INTERNAL_ERROR
Definition sdLog.h:121
static void sdlog_send(struct chibios_sdlog *p, long fd)
static volatile SdioError sdlog_conf_error
Result of writing the configuration, reported to the GCS.
static void send_sdlog_status(struct transport_tx *trans, struct link_device *dev)
static int sdlog_check_free_space(struct chibios_sdlog *p, long *fd, uint16_t len)
#define SDLOG_AUTO_FLUSH_PERIOD
static const char PPRZ_LOG_DIR[]
@ SDLOG_STOPPED
@ SDLOG_ERROR
@ SDLOG_RUNNING
static const char PPRZ_LOG_NAME[]
#define DEST_INFO_MSG_PPRZLOG
FileDes * file
#define DEST_INFO_MSG_ALL
struct link_device device
Generic device interface.
static const char * sdlog_error_string(SdioError error)
Human readable cause of a logger error, for the messages to the GCS.
chibios_sdlog structure
static FILE * sdlog_files[SDLOG_MAX_FILES]
Definition sdlog_linux.c:94
static size_t sdlog_bytes
Definition sdlog_linux.c:95
static void sdlog_send(struct chibios_sdlog *p, long fd)
uint8_t buf[SDLOG_MAX_MSG_LEN]
static enum @245305264037150143222225356213155240245213073102 sdlog_status
static int null_function(struct chibios_sdlog *p)
static SdioError sdlog_errno_error(int err)
Error of a failed write or close: only a full disk is "SD card full".
uint16_t len
SdioError sdLogFlushAllLogs(void)
static FILE * sdlog_file(FileDes fd)
static char sdlog_filenames[68]
static void sdlog_write(FileDes fd, const uint8_t *data, size_t len)
#define SDLOG_MAX_MSG_LEN
Definition sdlog_linux.c:66
static void send_sdlog_status(struct transport_tx *trans, struct link_device *dev)
#define SDLOG_DIR
Directory where the logs are stored, the default in simulation is $PAPARAZZI_HOME/var/logs/sdlog/<AIR...
Definition sdlog_linux.c:52
SdioError sdLogWriteLog(const FileDes fd, const char *fmt,...)
static int sdlog_check_free_space(struct chibios_sdlog *p, long *fd, uint16_t len)
void logger_log_msg_up(uint8_t *buf)
static void sdlog_put_byte(struct chibios_sdlog *p, long fd, uint8_t byte)
#define SDLOG_MAX_FILES
Definition sdlog_linux.c:65
SdioError sdLogWriteByte(const FileDes fd, const uint8_t value)
static float sdlog_last_flush
Definition sdlog_linux.c:96
static SdioError sdlog_error
void usbStorage_enable_usb_storage(float e)
Definition sdlog_linux.c:92
static void sdlog_report_status(struct transport_tx *trans, struct link_device *dev)
Tell the GCS in words why the logger does not run (no SD card, card full, ...): at the start and ever...
static int sdlog_next_number(const char *dir, const char *prefix)
Remove the empty logs and get the next free log number.
void sdlog_chibios_finish(const bool flush)
SdioError sdLogGetStorageStatus(void)
SdioError sdLogWriteRaw(const FileDes fd, const uint8_t *buffer, const size_t len)
size_t sdLogGetNbBytesWrittenToStorage(void)
static int sdlog_mkdir(const char *path)
Create a directory and its parents.
static void sdlog_atexit(void)
SdioError sdLogFlushLog(const FileDes fd)
void chibios_sdlog_init(struct chibios_sdlog *sdlog, FileDes *file)
init chibios_sdlog structure
static FileDes sdlog_open(const char *base, const char *dir, const char *prefix, char *name, size_t name_len)
Open the next log file in base/dir, returns the descriptor or -1.
static void sdlog_put_buffer(struct chibios_sdlog *p, long fd, const uint8_t *data, uint16_t len)
static uint8_t null_byte_function(struct chibios_sdlog *p)
void sdlog_chibios_init(void)
Message under construction, one per thread so threads can't mix messages (the fd argument of the devi...
int fd
Definition serial.c:26
static const float dir[]
Architecture independent timing functions.
static float get_sys_time_float(void)
Get the time in seconds since startup.
Definition sys_time.h:174
int16_t register_periodic_telemetry(struct periodic_telemetry *_pt, uint16_t _id, telemetry_cb _cb)
Register a telemetry callback function.
Definition telemetry.c:51
Periodic telemetry system header (includes downlink utility and generated code).
#define DefaultPeriodic
Set default periodic telemetry.
Definition telemetry.h:66
float usb_storage_status
Definition usbStorage.c:48
unsigned short uint16_t
Typedef defining 16 bit unsigned short type.
unsigned int uint32_t
Typedef defining 32 bit unsigned int type.
unsigned char uint8_t
Typedef defining 8 bit unsigned char type.
uint16_t f
Camera baseline, in meters (i.e. horizontal distance between the two cameras of the stereo setup).
Definition wedgebug.c:204