px4-firmware/platforms/common/uORB/uORB.cpp

506 lines
14 KiB
C++

/****************************************************************************
*
* 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 <lib/drivers/device/Device.hpp>
#include <matrix/Quaternion.hpp>
#include <mathlib/mathlib.h>
#ifdef __PX4_NUTTX
#include <sys/boardctl.h>
#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;
uint64_t 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);
} else if (strcmp(c_type, "uint64_t") == 0) {
field_size = sizeof(uint64_t);
value = *(uint64_t *)(data_ptr + previous_data_offset);
}
if (field_size > 0 && value != 0) {
PX4_INFO_RAW(" (0b");
bool got_set_bit = false;
for (int i = (field_size * 8) - 1; i >= 0; i--) {
unsigned current_bit = (value >> i) & 1;
got_set_bit |= current_bit;
if (got_set_bit) {
PX4_INFO_RAW("%u%s", current_bit, ((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;
}
}