/**************************************************************************** * * Copyright (c) 2012-2015 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 uORB.cpp * A lightweight object broker. */ #include "uORB.h" #include "uORBManager.hpp" #include "uORBCommon.hpp" #include #include #include #ifdef __PX4_NUTTX #include #endif static uORB::DeviceMaster *g_dev = nullptr; int uorb_start(void) { if (g_dev != nullptr) { PX4_WARN("already loaded"); /* user wanted to start uorb, its already running, no error */ return 0; } if (!uORB::Manager::initialize()) { PX4_ERR("uorb manager alloc failed"); return -ENOMEM; } #if !defined(__PX4_NUTTX) || defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) /* create the driver */ g_dev = uORB::Manager::get_instance()->get_device_master(); if (g_dev == nullptr) { return -errno; } #endif return OK; } int uorb_status(void) { #if !defined(__PX4_NUTTX) || defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) if (g_dev != nullptr) { g_dev->printStatistics(); } else { PX4_INFO("uorb is not running"); } #else boardctl(ORBIOCDEVMASTERCMD, ORB_DEVMASTER_STATUS); #endif return OK; } int uorb_top(char **topic_filter, int num_filters) { #if !defined(__PX4_NUTTX) || defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__) if (g_dev != nullptr) { g_dev->showTop(topic_filter, num_filters); } else { PX4_INFO("uorb is not running"); } #else boardctl(ORBIOCDEVMASTERCMD, ORB_DEVMASTER_TOP); #endif return OK; } orb_advert_t orb_advertise(const struct orb_metadata *meta, const void *data) { return uORB::Manager::get_instance()->orb_advertise(meta, data); } orb_advert_t orb_advertise_queue(const struct orb_metadata *meta, const void *data, unsigned int queue_size) { return uORB::Manager::get_instance()->orb_advertise(meta, data, queue_size); } orb_advert_t orb_advertise_multi(const struct orb_metadata *meta, const void *data, int *instance) { return uORB::Manager::get_instance()->orb_advertise_multi(meta, data, instance); } orb_advert_t orb_advertise_multi_queue(const struct orb_metadata *meta, const void *data, int *instance, unsigned int queue_size) { return uORB::Manager::get_instance()->orb_advertise_multi(meta, data, instance, queue_size); } int orb_unadvertise(orb_advert_t handle) { return uORB::Manager::get_instance()->orb_unadvertise(handle); } int orb_publish(const struct orb_metadata *meta, orb_advert_t handle, const void *data) { return uORB::Manager::get_instance()->orb_publish(meta, handle, data); } int orb_subscribe(const struct orb_metadata *meta) { return uORB::Manager::get_instance()->orb_subscribe(meta); } int orb_subscribe_multi(const struct orb_metadata *meta, unsigned instance) { return uORB::Manager::get_instance()->orb_subscribe_multi(meta, instance); } int orb_unsubscribe(int handle) { return uORB::Manager::get_instance()->orb_unsubscribe(handle); } int orb_copy(const struct orb_metadata *meta, int handle, void *buffer) { return uORB::Manager::get_instance()->orb_copy(meta, handle, buffer); } int orb_check(int handle, bool *updated) { return uORB::Manager::get_instance()->orb_check(handle, updated); } int orb_exists(const struct orb_metadata *meta, int instance) { return uORB::Manager::get_instance()->orb_exists(meta, instance); } int orb_group_count(const struct orb_metadata *meta) { unsigned instance = 0; while (uORB::Manager::get_instance()->orb_exists(meta, instance) == OK) { ++instance; }; return instance; } int orb_set_interval(int handle, unsigned interval) { return uORB::Manager::get_instance()->orb_set_interval(handle, interval); } int orb_get_interval(int handle, unsigned *interval) { return uORB::Manager::get_instance()->orb_get_interval(handle, interval); } const char *orb_get_c_type(unsigned char short_type) { // this matches with the uorb o_fields generator switch (short_type) { case 0x82: return "int8_t"; case 0x83: return "int16_t"; case 0x84: return "int32_t"; case 0x85: return "int64_t"; case 0x86: return "uint8_t"; case 0x87: return "uint16_t"; case 0x88: return "uint32_t"; case 0x89: return "uint64_t"; case 0x8a: return "float"; case 0x8b: return "double"; case 0x8c: return "bool"; case 0x8d: return "char"; } return nullptr; } void orb_print_message_internal(const orb_metadata *meta, const void *data, bool print_topic_name) { if (print_topic_name) { PX4_INFO_RAW(" %s\n", meta->o_name); } const hrt_abstime now = hrt_absolute_time(); hrt_abstime topic_timestamp = 0; const uint8_t *data_ptr = (const uint8_t *)data; int data_offset = 0; for (int format_idx = 0; meta->o_fields[format_idx] != 0;) { const char *end_field = strchr(meta->o_fields + format_idx, ';'); if (!end_field) { PX4_ERR("Format error in %s", meta->o_fields); return; } const char *c_type = orb_get_c_type(meta->o_fields[format_idx]); const int end_field_idx = end_field - meta->o_fields; int array_idx = -1; int field_name_idx = -1; for (int field_idx = format_idx; field_idx != end_field_idx; ++field_idx) { if (meta->o_fields[field_idx] == '[') { array_idx = field_idx + 1; } else if (meta->o_fields[field_idx] == ' ') { field_name_idx = field_idx + 1; break; } } int array_size = 1; if (array_idx >= 0) { array_size = strtol(meta->o_fields + array_idx, nullptr, 10); } char field_name[80]; size_t field_name_len = end_field_idx - field_name_idx; if (field_name_len >= sizeof(field_name)) { PX4_ERR("field name too long %s (max: %u)", meta->o_fields, (unsigned)sizeof(field_name)); return; } memcpy(field_name, meta->o_fields + field_name_idx, field_name_len); field_name[field_name_len] = '\0'; if (c_type) { // built-in type bool dont_print = false; // handle special cases if (strncmp(field_name, "_padding", 8) == 0) { dont_print = true; } else if (strcmp(c_type, "char") == 0 && array_size > 1) { // string PX4_INFO_RAW(" %s: \"%.*s\"\n", field_name, array_size, (char *)(data_ptr + data_offset)); dont_print = true; } if (!dont_print) { PX4_INFO_RAW(" %s: ", field_name); } if (!dont_print && array_size > 1) { PX4_INFO_RAW("["); } const int previous_data_offset = data_offset; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" // the caller ensures data is aligned for (int i = 0; i < array_size; ++i) { if (strcmp(c_type, "int8_t") == 0) { if (!dont_print) { PX4_INFO_RAW("%" PRIi8, *(int8_t *)(data_ptr + data_offset)); } data_offset += sizeof(int8_t); } else if (strcmp(c_type, "int16_t") == 0) { if (!dont_print) { PX4_INFO_RAW("%" PRIi16, *(int16_t *)(data_ptr + data_offset)); } data_offset += sizeof(int16_t); } else if (strcmp(c_type, "int32_t") == 0) { if (!dont_print) { PX4_INFO_RAW("%" PRIi32, *(int32_t *)(data_ptr + data_offset)); } data_offset += sizeof(int32_t); } else if (strcmp(c_type, "int64_t") == 0) { if (!dont_print) { PX4_INFO_RAW("%" PRIi64, *(int64_t *)(data_ptr + data_offset)); } data_offset += sizeof(int64_t); } else if (strcmp(c_type, "uint8_t") == 0) { if (!dont_print) { PX4_INFO_RAW("%" PRIu8, *(uint8_t *)(data_ptr + data_offset)); } data_offset += sizeof(uint8_t); } else if (strcmp(c_type, "uint16_t") == 0) { if (!dont_print) { PX4_INFO_RAW("%" PRIu16, *(uint16_t *)(data_ptr + data_offset)); } data_offset += sizeof(uint16_t); } else if (strcmp(c_type, "uint32_t") == 0) { if (!dont_print) { PX4_INFO_RAW("%" PRIu32, *(uint32_t *)(data_ptr + data_offset)); } data_offset += sizeof(uint32_t); } else if (strcmp(c_type, "uint64_t") == 0) { if (!dont_print) { PX4_INFO_RAW("%" PRIu64, *(uint64_t *)(data_ptr + data_offset)); } data_offset += sizeof(uint64_t); } else if (strcmp(c_type, "float") == 0) { if (!dont_print) { PX4_INFO_RAW("%.4f", (double) * (float *)(data_ptr + data_offset)); } data_offset += sizeof(float); } else if (strcmp(c_type, "double") == 0) { if (!dont_print) { PX4_INFO_RAW("%.4f", *(double *)(data_ptr + data_offset)); } data_offset += sizeof(double); } else if (strcmp(c_type, "bool") == 0) { if (!dont_print) { PX4_INFO_RAW("%s", *(bool *)(data_ptr + data_offset) ? "True" : "False"); } data_offset += sizeof(bool); } else if (strcmp(c_type, "char") == 0) { if (!dont_print) { PX4_INFO_RAW("%i", (int) * (char *)(data_ptr + data_offset)); } data_offset += sizeof(char); } else { PX4_ERR("unknown type: %s", c_type); return; } if (!dont_print && i < array_size - 1) { PX4_INFO_RAW(", "); } } if (!dont_print && array_size > 1) { PX4_INFO_RAW("]"); } // handle special cases if (array_size == 1) { if (strcmp(c_type, "uint64_t") == 0 && strcmp(field_name, "timestamp") == 0) { topic_timestamp = *(uint64_t *)(data_ptr + previous_data_offset); if (topic_timestamp != 0) { PX4_INFO_RAW(" (%.6f seconds ago)", (double)((now - topic_timestamp) / 1e6f)); } } else if (strcmp(c_type, "uint64_t") == 0 && strcmp(field_name, "timestamp_sample") == 0) { hrt_abstime timestamp = *(uint64_t *)(data_ptr + previous_data_offset); if (topic_timestamp != 0 && timestamp != 0) { PX4_INFO_RAW(" (%i us before timestamp)", (int)(topic_timestamp - timestamp)); } } else if (strstr(field_name, "flags") != nullptr) { // bitfield unsigned field_size = 0; unsigned long value = 0; if (strcmp(c_type, "uint8_t") == 0) { field_size = sizeof(uint8_t); value = *(uint8_t *)(data_ptr + previous_data_offset); } else if (strcmp(c_type, "uint16_t") == 0) { field_size = sizeof(uint16_t); value = *(uint16_t *)(data_ptr + previous_data_offset); } else if (strcmp(c_type, "uint32_t") == 0) { field_size = sizeof(uint32_t); value = *(uint32_t *)(data_ptr + previous_data_offset); } if (field_size > 0) { PX4_INFO_RAW(" (0b"); for (int i = (field_size * 8) - 1; i >= 0; i--) { PX4_INFO_RAW("%lu%s", (value >> i) & 1, ((unsigned)i < (field_size * 8) - 1 && i % 4 == 0 && i > 0) ? "'" : ""); } PX4_INFO_RAW(")"); } } else if (strcmp(c_type, "uint32_t") == 0 && strstr(field_name, "device_id") != nullptr) { // Device ID uint32_t device_id = *(uint32_t *)(data_ptr + previous_data_offset); char device_id_buffer[80]; device::Device::device_id_print_buffer(device_id_buffer, sizeof(device_id_buffer), device_id); PX4_INFO_RAW(" (%s)", device_id_buffer); } } else if (array_size == 4 && strcmp(c_type, "float") == 0 && (strcmp(field_name, "q") == 0 || strncmp(field_name, "q_", 2) == 0)) { // attitude float *attitude = (float *)(data_ptr + previous_data_offset); matrix::Eulerf euler{matrix::Quatf{attitude}}; PX4_INFO_RAW(" (Roll: %.1f deg, Pitch: %.1f deg, Yaw: %.1f deg)", (double)math::degrees(euler(0)), (double)math::degrees(euler(1)), (double)math::degrees(euler(2))); } #pragma GCC diagnostic pop PX4_INFO_RAW("\n"); } else { // extract the topic name char topic_name[80]; const size_t topic_name_len = array_size > 1 ? array_idx - format_idx - 1 : field_name_idx - format_idx - 1; if (topic_name_len >= sizeof(topic_name)) { PX4_ERR("topic name too long in %s (max: %u)", meta->o_name, (unsigned)sizeof(topic_name)); return; } memcpy(topic_name, meta->o_fields + format_idx, topic_name_len); topic_name[topic_name_len] = '\0'; // find the metadata const orb_metadata *const *topics = orb_get_topics(); const orb_metadata *found_topic = nullptr; for (size_t i = 0; i < orb_topics_count(); i++) { if (strcmp(topics[i]->o_name, topic_name) == 0) { found_topic = topics[i]; break; } } if (!found_topic) { PX4_ERR("Topic %s did not match any known topics", topic_name); return; } // print recursively for (int i = 0; i < array_size; ++i) { PX4_INFO_RAW(" %s", field_name); if (array_size > 1) { PX4_INFO_RAW("[%i]", i); } PX4_INFO_RAW(" (%s):\n", topic_name); orb_print_message_internal(found_topic, data_ptr + data_offset, false); data_offset += found_topic->o_size; } } format_idx = end_field_idx + 1; } }