/**************************************************************************** * * Copyright (c) 2013-2023 PX4 Development Team. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name PX4 nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /** * @file dataman.cpp * DATAMANAGER driver. * * @author Jean Cyr * @author Lorenz Meier * @author Julian Oes * @author Thomas Gubler * @author David Sidrane */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dataman.h" __BEGIN_DECLS __EXPORT int dataman_main(int argc, char *argv[]); __END_DECLS static constexpr int TASK_STACK_SIZE = 1420; #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE /* Private File based Operations */ static ssize_t _file_write(dm_item_t item, unsigned index, const void *buf, size_t count); static ssize_t _file_read(dm_item_t item, unsigned index, void *buf, size_t count); static int _file_clear(dm_item_t item); static int _file_initialize(unsigned max_offset); static void _file_shutdown(); #endif /* Private Ram based Operations */ static ssize_t _ram_write(dm_item_t item, unsigned index, const void *buf, size_t count); static ssize_t _ram_read(dm_item_t item, unsigned index, void *buf, size_t count); static int _ram_clear(dm_item_t item); static int _ram_initialize(unsigned max_offset); static void _ram_shutdown(); typedef struct dm_operations_t { ssize_t (*write)(dm_item_t item, unsigned index, const void *buf, size_t count); ssize_t (*read)(dm_item_t item, unsigned index, void *buf, size_t count); int (*clear)(dm_item_t item); int (*initialize)(unsigned max_offset); void (*shutdown)(); int (*wait)(px4_sem_t *sem); } dm_operations_t; #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE static constexpr dm_operations_t dm_file_operations = { .write = _file_write, .read = _file_read, .clear = _file_clear, .initialize = _file_initialize, .shutdown = _file_shutdown, .wait = px4_sem_wait, }; #endif static constexpr dm_operations_t dm_ram_operations = { .write = _ram_write, .read = _ram_read, .clear = _ram_clear, .initialize = _ram_initialize, .shutdown = _ram_shutdown, .wait = px4_sem_wait, }; static const dm_operations_t *g_dm_ops; static struct { union { struct { int fd; } file; struct { uint8_t *data; uint8_t *data_end; } ram; }; bool running; bool silence = false; } dm_operations_data; /* Usage statistics */ static unsigned g_func_counts[DM_NUMBER_OF_FUNCS]; #define DM_SECTOR_HDR_SIZE 4 /* data manager per item header overhead */ /* Table of the len of each item type including HDR size */ static constexpr size_t g_per_item_size_with_hdr[DM_KEY_NUM_KEYS] = { g_per_item_size[DM_KEY_SAFE_POINTS_0] + DM_SECTOR_HDR_SIZE, g_per_item_size[DM_KEY_SAFE_POINTS_1] + DM_SECTOR_HDR_SIZE, g_per_item_size[DM_KEY_SAFE_POINTS_STATE] + DM_SECTOR_HDR_SIZE, g_per_item_size[DM_KEY_FENCE_POINTS_0] + DM_SECTOR_HDR_SIZE, g_per_item_size[DM_KEY_FENCE_POINTS_1] + DM_SECTOR_HDR_SIZE, g_per_item_size[DM_KEY_FENCE_POINTS_STATE] + DM_SECTOR_HDR_SIZE, g_per_item_size[DM_KEY_WAYPOINTS_OFFBOARD_0] + DM_SECTOR_HDR_SIZE, g_per_item_size[DM_KEY_WAYPOINTS_OFFBOARD_1] + DM_SECTOR_HDR_SIZE, g_per_item_size[DM_KEY_MISSION_STATE] + DM_SECTOR_HDR_SIZE, g_per_item_size[DM_KEY_COMPAT] + DM_SECTOR_HDR_SIZE }; /* Table of offset for index 0 of each item type */ static unsigned int g_key_offsets[DM_KEY_NUM_KEYS]; static uint8_t dataman_clients_count = 1; static perf_counter_t _dm_read_perf{nullptr}; static perf_counter_t _dm_write_perf{nullptr}; #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE /* The data manager store file handle and file name */ static const char *default_device_path = PX4_STORAGEDIR "/dataman"; static char *k_data_manager_device_path = nullptr; #endif static enum { BACKEND_NONE = 0, BACKEND_FILE, BACKEND_RAM, BACKEND_LAST } backend = BACKEND_NONE; static px4_sem_t g_init_sema; static bool g_task_should_exit; /**< if true, dataman task should exit */ /* Work queue management functions */ static bool is_running() { return dm_operations_data.running; } /* Calculate the offset in file of specific item */ static int calculate_offset(dm_item_t item, unsigned index) { /* Make sure the item type is valid */ if (item >= DM_KEY_NUM_KEYS) { return -1; } /* Make sure the index for this item type is valid */ if (index >= g_per_item_max_index[item]) { return -1; } /* Calculate and return the item index based on type and index */ return g_key_offsets[item] + (index * g_per_item_size_with_hdr[item]); } /* Each data item is stored as follows * * byte 0: Length of user data item * byte 1: Unused (previously persistence of this data item) * byte 2: Unused (for future use) * byte 3: Unused (for future use) * byte DM_SECTOR_HDR_SIZE... : data item value * * The total size must not exceed g_per_item_max_index[item] */ /* write to the data manager RAM buffer */ static ssize_t _ram_write(dm_item_t item, unsigned index, const void *buf, size_t count) { if (item >= DM_KEY_NUM_KEYS) { return -1; } /* Get the offset for this item */ int offset = calculate_offset(item, index); /* If item type or index out of range, return error */ if (offset < 0) { return -1; } /* Make sure caller has not given us more data than we can handle */ if (count > (g_per_item_size_with_hdr[item] - DM_SECTOR_HDR_SIZE)) { return -E2BIG; } uint8_t *buffer = &dm_operations_data.ram.data[offset]; if (buffer > dm_operations_data.ram.data_end) { return -1; } /* Write out the data, prefixed with length */ buffer[0] = count; buffer[1] = 0; buffer[2] = 0; buffer[3] = 0; if (count > 0) { memcpy(buffer + DM_SECTOR_HDR_SIZE, buf, count); } /* All is well... return the number of user data written */ return count; } #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE /* write to the data manager file */ static ssize_t _file_write(dm_item_t item, unsigned index, const void *buf, size_t count) { if (item >= DM_KEY_NUM_KEYS) { return -1; } unsigned char buffer[g_per_item_size_with_hdr[item]]; /* Get the offset for this item */ const int offset = calculate_offset(item, index); /* If item type or index out of range, return error */ if (offset < 0) { return -1; } /* Make sure caller has not given us more data than we can handle */ if (count > (g_per_item_size_with_hdr[item] - DM_SECTOR_HDR_SIZE)) { return -E2BIG; } /* Write out the data, prefixed with length */ buffer[0] = count; buffer[1] = 0; buffer[2] = 0; buffer[3] = 0; if (count > 0) { memcpy(buffer + DM_SECTOR_HDR_SIZE, buf, count); } count += DM_SECTOR_HDR_SIZE; bool write_success = false; for (int i = 0; i < 2; i++) { int ret_seek = lseek(dm_operations_data.file.fd, offset, SEEK_SET); if (ret_seek < 0) { PX4_ERR("file write lseek failed %d", errno); continue; } if (ret_seek != offset) { PX4_ERR("file write lseek failed, incorrect offset %d vs %d", ret_seek, offset); continue; } int ret_write = write(dm_operations_data.file.fd, buffer, count); if (ret_write < 0) { PX4_ERR("file write failed %d", errno); continue; } if (ret_write != (ssize_t)count) { PX4_ERR("file write failed, wrote %d bytes, expected %zu", ret_write, count); continue; } else { write_success = true; break; } } if (!write_success) { return -1; } /* Make sure data is written to physical media */ fsync(dm_operations_data.file.fd); /* All is well... return the number of user data written */ return count - DM_SECTOR_HDR_SIZE; } #endif /* Retrieve from the data manager RAM buffer*/ static ssize_t _ram_read(dm_item_t item, unsigned index, void *buf, size_t count) { if (item >= DM_KEY_NUM_KEYS) { return -1; } /* Get the offset for this item */ int offset = calculate_offset(item, index); /* If item type or index out of range, return error */ if (offset < 0) { return -1; } /* Make sure the caller hasn't asked for more data than we can handle */ if (count > (g_per_item_size_with_hdr[item] - DM_SECTOR_HDR_SIZE)) { return -E2BIG; } /* Read the prefix and data */ uint8_t *buffer = &dm_operations_data.ram.data[offset]; if (buffer > dm_operations_data.ram.data_end) { return -1; } /* See if we got data */ if (buffer[0] > 0) { /* We got more than requested!!! */ if (buffer[0] > count) { return -1; } /* Looks good, copy it to the caller's buffer */ memcpy(buf, buffer + DM_SECTOR_HDR_SIZE, buffer[0]); } /* Return the number of bytes of caller data read */ return buffer[0]; } #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE /* Retrieve from the data manager file */ static ssize_t _file_read(dm_item_t item, unsigned index, void *buf, size_t count) { if (item >= DM_KEY_NUM_KEYS) { return -1; } unsigned char buffer[g_per_item_size_with_hdr[item]]; /* Get the offset for this item */ int offset = calculate_offset(item, index); /* If item type or index out of range, return error */ if (offset < 0) { return -1; } /* Make sure the caller hasn't asked for more data than we can handle */ if (count > (g_per_item_size_with_hdr[item] - DM_SECTOR_HDR_SIZE)) { return -E2BIG; } int len = -1; bool read_success = false; for (int i = 0; i < 2; i++) { int ret_seek = lseek(dm_operations_data.file.fd, offset, SEEK_SET); if ((ret_seek < 0) && !dm_operations_data.silence) { PX4_ERR("file read lseek failed %d", errno); continue; } if ((ret_seek != offset) && !dm_operations_data.silence) { PX4_ERR("file read lseek failed, incorrect offset %d vs %d", ret_seek, offset); continue; } /* Read the prefix and data */ len = read(dm_operations_data.file.fd, buffer, count + DM_SECTOR_HDR_SIZE); /* Check for read error */ if (len >= 0) { read_success = true; break; } else { if (!dm_operations_data.silence) { PX4_ERR("file read failed %d", errno); } } } if (!read_success) { return -1; } /* A zero length entry is a empty entry */ if (len == 0) { buffer[0] = 0; } /* See if we got data */ if (buffer[0] > 0) { /* We got more than requested!!! */ if (buffer[0] > count) { return -1; } /* Looks good, copy it to the caller's buffer */ memcpy(buf, buffer + DM_SECTOR_HDR_SIZE, buffer[0]); } else { memset(buf, 0, count); } /* Return the number of bytes of caller data read */ return buffer[0]; } #endif static int _ram_clear(dm_item_t item) { if (item >= DM_KEY_NUM_KEYS) { return -1; } /* Get the offset of 1st item of this type */ int offset = calculate_offset(item, 0); /* Check for item type out of range */ if (offset < 0) { return -1; } int result = 0; /* Clear all items of this type */ for (int i = 0; (unsigned)i < g_per_item_max_index[item]; i++) { uint8_t *buf = &dm_operations_data.ram.data[offset]; if (buf > dm_operations_data.ram.data_end) { result = -1; break; } buf[0] = 0; offset += g_per_item_size_with_hdr[item]; } return result; } #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE static int _file_clear(dm_item_t item) { if (item >= DM_KEY_NUM_KEYS) { return -1; } /* Get the offset of 1st item of this type */ int offset = calculate_offset(item, 0); /* Check for item type out of range */ if (offset < 0) { return -1; } int result = 0; /* Clear all items of this type */ for (int i = 0; (unsigned)i < g_per_item_max_index[item]; i++) { char buf[1]; if (lseek(dm_operations_data.file.fd, offset, SEEK_SET) != offset) { result = -1; break; } /* Avoid SD flash wear by only doing writes where necessary */ if (read(dm_operations_data.file.fd, buf, 1) < 1) { break; } /* If item has length greater than 0 it needs to be overwritten */ if (buf[0]) { if (lseek(dm_operations_data.file.fd, offset, SEEK_SET) != offset) { result = -1; break; } buf[0] = 0; if (write(dm_operations_data.file.fd, buf, 1) != 1) { result = -1; break; } } offset += g_per_item_size_with_hdr[item]; } /* Make sure data is actually written to physical media */ fsync(dm_operations_data.file.fd); return result; } #endif #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE static int _file_initialize(unsigned max_offset) { const bool file_existed = (access(k_data_manager_device_path, F_OK) == 0); /* Open or create the data manager file */ dm_operations_data.file.fd = open(k_data_manager_device_path, O_RDWR | O_CREAT | O_BINARY, PX4_O_MODE_666); if (dm_operations_data.file.fd < 0) { PX4_WARN("Could not open data manager file %s", k_data_manager_device_path); px4_sem_post(&g_init_sema); /* Don't want to hang startup */ return -1; } if ((unsigned)lseek(dm_operations_data.file.fd, max_offset, SEEK_SET) != max_offset) { close(dm_operations_data.file.fd); PX4_WARN("Could not seek data manager file %s", k_data_manager_device_path); px4_sem_post(&g_init_sema); /* Don't want to hang startup */ return -1; } dataman_compat_s compat_state{}; dm_operations_data.silence = true; g_dm_ops->read(DM_KEY_COMPAT, 0, &compat_state, sizeof(compat_state)); dm_operations_data.silence = false; if (!file_existed || (compat_state.key != DM_COMPAT_KEY)) { /* Write current compat info */ compat_state.key = DM_COMPAT_KEY; int ret = g_dm_ops->write(DM_KEY_COMPAT, 0, &compat_state, sizeof(compat_state)); if (ret != sizeof(compat_state)) { PX4_ERR("Failed writing compat: %d", ret); } for (uint32_t item = DM_KEY_SAFE_POINTS_0; item <= DM_KEY_MISSION_STATE; ++item) { g_dm_ops->clear((dm_item_t)item); } mission_s mission{}; mission.timestamp = hrt_absolute_time(); mission.mission_dataman_id = DM_KEY_WAYPOINTS_OFFBOARD_0; mission.count = 0; mission.current_seq = 0; mission.mission_id = 0u; mission.geofence_id = 0u; mission.safe_points_id = 0u; mission_stats_entry_s stats; stats.num_items = 0; stats.opaque_id = 0; g_dm_ops->write(DM_KEY_MISSION_STATE, 0, reinterpret_cast(&mission), sizeof(mission_s)); g_dm_ops->write(DM_KEY_FENCE_POINTS_STATE, 0, reinterpret_cast(&stats), sizeof(mission_stats_entry_s)); g_dm_ops->write(DM_KEY_SAFE_POINTS_STATE, 0, reinterpret_cast(&stats), sizeof(mission_stats_entry_s)); } dm_operations_data.running = true; return 0; } #endif static int _ram_initialize(unsigned max_offset) { /* In memory */ dm_operations_data.ram.data = (uint8_t *)malloc(max_offset); if (dm_operations_data.ram.data == nullptr) { PX4_WARN("Could not allocate %u bytes of memory", max_offset); px4_sem_post(&g_init_sema); /* Don't want to hang startup */ return -1; } memset(dm_operations_data.ram.data, 0, max_offset); dm_operations_data.ram.data_end = &dm_operations_data.ram.data[max_offset - 1]; dm_operations_data.running = true; return 0; } #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE static void _file_shutdown() { close(dm_operations_data.file.fd); dm_operations_data.running = false; } #endif static void _ram_shutdown() { free(dm_operations_data.ram.data); dm_operations_data.running = false; } static int task_main(int argc, char *argv[]) { /* Dataman can use disk or RAM */ switch (backend) { #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE case BACKEND_FILE: g_dm_ops = &dm_file_operations; break; #endif case BACKEND_RAM: g_dm_ops = &dm_ram_operations; break; default: PX4_WARN("No valid backend set."); return -1; } /* Initialize global variables */ g_key_offsets[0] = 0; for (int i = 0; i < ((int)DM_KEY_NUM_KEYS - 1); i++) { g_key_offsets[i + 1] = g_key_offsets[i] + (g_per_item_max_index[i] * g_per_item_size_with_hdr[i]); } unsigned max_offset = g_key_offsets[DM_KEY_NUM_KEYS - 1] + (g_per_item_max_index[DM_KEY_NUM_KEYS - 1] * g_per_item_size_with_hdr[DM_KEY_NUM_KEYS - 1]); for (unsigned i = 0; i < DM_NUMBER_OF_FUNCS; i++) { g_func_counts[i] = 0; } g_task_should_exit = false; uORB::Publication dataman_response_pub{ORB_ID(dataman_response)}; const int dataman_request_sub = orb_subscribe(ORB_ID(dataman_request)); if (dataman_request_sub < 0) { PX4_ERR("Failed to subscribe (%i)", errno); } _dm_read_perf = perf_alloc(PC_ELAPSED, MODULE_NAME": read"); _dm_write_perf = perf_alloc(PC_ELAPSED, MODULE_NAME": write"); int ret = g_dm_ops->initialize(max_offset); if (ret) { g_task_should_exit = true; goto end; } switch (backend) { #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE case BACKEND_FILE: PX4_INFO("data manager file '%s' size is %u bytes", k_data_manager_device_path, max_offset); break; #endif case BACKEND_RAM: PX4_INFO("data manager RAM size is %u bytes", max_offset); break; default: break; } px4_pollfd_struct_t fds; fds.fd = dataman_request_sub; fds.events = POLLIN; /* Tell startup that the worker thread has completed its initialization */ px4_sem_post(&g_init_sema); /* Start the endless loop, waiting for then processing work requests */ while (true) { ret = px4_poll(&fds, 1, 1000); if (ret > 0) { bool updated = false; orb_check(dataman_request_sub, &updated); if (updated) { dataman_request_s request; orb_copy(ORB_ID(dataman_request), dataman_request_sub, &request); dataman_response_s response{}; response.client_id = request.client_id; response.request_type = request.request_type; response.item = request.item; response.index = request.index; response.status = dataman_response_s::STATUS_FAILURE_NO_DATA; ssize_t result; switch (request.request_type) { case DM_GET_ID: if (dataman_clients_count < UINT8_MAX) { response.client_id = dataman_clients_count++; /* Send the timestamp of the request over the data buffer so that the "dataman client" * can distinguish whether the request was made by it. */ memcpy(response.data, &request.timestamp, sizeof(hrt_abstime)); } else { PX4_ERR("Max Dataman clients reached!"); } break; case DM_WRITE: g_func_counts[DM_WRITE]++; perf_begin(_dm_write_perf); result = g_dm_ops->write(static_cast(request.item), request.index, &(request.data), request.data_length); perf_end(_dm_write_perf); if (result > 0) { response.status = dataman_response_s::STATUS_SUCCESS; } else { response.status = dataman_response_s::STATUS_FAILURE_WRITE_FAILED; } break; case DM_READ: g_func_counts[DM_READ]++; perf_begin(_dm_read_perf); result = g_dm_ops->read(static_cast(request.item), request.index, &(response.data), request.data_length); perf_end(_dm_read_perf); if (result >= 0) { response.status = dataman_response_s::STATUS_SUCCESS; } else { response.status = dataman_response_s::STATUS_FAILURE_READ_FAILED; } break; case DM_CLEAR: g_func_counts[DM_CLEAR]++; result = g_dm_ops->clear(static_cast(request.item)); if (result == 0) { response.status = dataman_response_s::STATUS_SUCCESS; } else { response.status = dataman_response_s::STATUS_FAILURE_CLEAR_FAILED; } break; default: break; } response.timestamp = hrt_absolute_time(); dataman_response_pub.publish(response); } } /* time to go???? */ if (g_task_should_exit) { break; } } orb_unsubscribe(dataman_request_sub); g_dm_ops->shutdown(); end: backend = BACKEND_NONE; perf_free(_dm_read_perf); _dm_read_perf = nullptr; perf_free(_dm_write_perf); _dm_write_perf = nullptr; return 0; } static int start() { px4_sem_init(&g_init_sema, 1, 0); /* g_init_sema use case is a signal */ px4_sem_setprotocol(&g_init_sema, SEM_PRIO_NONE); /* start the worker thread with low priority for disk IO */ if (px4_task_spawn_cmd("dataman", SCHED_DEFAULT, SCHED_PRIORITY_DEFAULT - 10, PX4_STACK_ADJUSTED(TASK_STACK_SIZE), task_main, nullptr) < 0) { px4_sem_destroy(&g_init_sema); PX4_ERR("task start failed"); return -1; } /* wait for the thread to actually initialize */ px4_sem_wait(&g_init_sema); px4_sem_destroy(&g_init_sema); return 0; } static void status() { /* display usage statistics */ PX4_INFO("Writes %u", g_func_counts[DM_WRITE]); PX4_INFO("Reads %u", g_func_counts[DM_READ]); PX4_INFO("Clears %u", g_func_counts[DM_CLEAR]); perf_print_counter(_dm_read_perf); perf_print_counter(_dm_write_perf); } static void stop() { /* Tell the worker task to shut down */ g_task_should_exit = true; } static void usage() { PRINT_MODULE_DESCRIPTION( R"DESCR_STR( ### Description Module to provide persistent storage for the rest of the system in form of a simple database through a C API. Multiple backends are supported depending on the board: - a file (eg. on the SD card) - RAM (this is obviously not persistent) It is used to store structured data of different types: mission waypoints, mission state and geofence polygons. Each type has a specific type and a fixed maximum amount of storage items, so that fast random access is possible. ### Implementation Reading and writing a single item is always atomic. )DESCR_STR"); PRINT_MODULE_USAGE_NAME("dataman", "system"); PRINT_MODULE_USAGE_COMMAND("start"); #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE PRINT_MODULE_USAGE_PARAM_STRING('f', nullptr, "", "Storage file", true); #endif PRINT_MODULE_USAGE_PARAM_FLAG('r', "Use RAM backend (NOT persistent)", true); #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE PRINT_MODULE_USAGE_PARAM_COMMENT("The options -f and -r are mutually exclusive. If nothing is specified, a file 'dataman' is used"); #endif PRINT_MODULE_USAGE_DEFAULT_COMMANDS(); } static int backend_check() { if (backend != BACKEND_NONE) { PX4_WARN("-f and -r are mutually exclusive"); usage(); return -1; } return 0; } int dataman_main(int argc, char *argv[]) { if (argc < 2) { usage(); return -1; } if (!strcmp(argv[1], "start")) { if (is_running()) { PX4_WARN("dataman already running"); return -1; } int ch; int dmoptind = 1; const char *dmoptarg = nullptr; /* jump over start and look at options first */ while ((ch = px4_getopt(argc, argv, "f:r", &dmoptind, &dmoptarg)) != EOF) { switch (ch) { case 'f': if (backend_check()) { return -1; } #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE backend = BACKEND_FILE; k_data_manager_device_path = strdup(dmoptarg); PX4_INFO("dataman file set to: %s", k_data_manager_device_path); #else backend = BACKEND_RAM; PX4_WARN("dataman does not support persistent storage. Falling back to RAM."); #endif break; case 'r': if (backend_check()) { return -1; } backend = BACKEND_RAM; break; //no break default: usage(); return -1; } } if (backend == BACKEND_NONE) { #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE backend = BACKEND_FILE; k_data_manager_device_path = strdup(default_device_path); #else backend = BACKEND_RAM; #endif } start(); if (!is_running()) { PX4_ERR("dataman start failed"); #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE free(k_data_manager_device_path); k_data_manager_device_path = nullptr; #endif return -1; } return 0; } /* Worker thread should be running for all other commands */ if (!is_running()) { PX4_WARN("dataman worker thread not running"); usage(); return -1; } if (!strcmp(argv[1], "stop")) { stop(); #ifdef CONFIG_DATAMAN_PERSISTENT_STORAGE free(k_data_manager_device_path); k_data_manager_device_path = nullptr; #endif } else if (!strcmp(argv[1], "status")) { status(); } else { usage(); return -1; } return 0; } static_assert(sizeof(dataman_request_s::data) == sizeof(dataman_response_s::data), "request and response data are not the same size"); static_assert(sizeof(dataman_response_s::data) >= MISSION_SAFE_POINT_SIZE, "mission_item_s can't fit in the response data"); static_assert(sizeof(dataman_response_s::data) >= MISSION_FENCE_POINT_SIZE, "mission_fance_point_s can't fit in the response data"); static_assert(sizeof(dataman_response_s::data) >= MISSION_ITEM_SIZE, "mission_item_s can't fit in the response data"); static_assert(sizeof(dataman_response_s::data) >= MISSION_SIZE, "mission_s can't fit in the response data"); static_assert(sizeof(dataman_response_s::data) >= DATAMAN_COMPAT_SIZE, "dataman_compat_s can't fit in the response data"); static_assert(sizeof(dataman_response_s::data) >= sizeof(hrt_abstime), "hrt_abstime can't fit in the response data");