Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle dynamic extents #15

Merged
merged 10 commits into from
Aug 17, 2024
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
apt install -y apt-rdepends ros-${{ matrix.distribution }}-launch-ros
python3 -m venv ~/.venvs/dev
. ~/.venvs/dev/bin/activate
pip install colcon-ros lark
pip install lark
pip install colcon-lint

- name: build and test
Expand Down
61 changes: 41 additions & 20 deletions src/CameraNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,29 +440,35 @@ CameraNode::declareParameters()
// store control id with name
parameter_ids[id->name()] = id;

std::size_t extent;
if (info.min().numElements() != info.max().numElements())
throw std::runtime_error("minimum and maximum parameter array sizes do not match");

// check if the control can be mapped to a parameter
const rclcpp::ParameterType pv_type = cv_to_pv_type(id);
if (pv_type == rclcpp::ParameterType::PARAMETER_NOT_SET) {
RCLCPP_WARN_STREAM(get_logger(), "unsupported control '" << id->name() << "'");
continue;
}

// format type description
rcl_interfaces::msg::ParameterDescriptor param_descr;
try {
extent = get_extent(id);
const std::size_t extent = get_extent(id);
const bool scalar = (extent == 0);
const bool dynamic = (extent == libcamera::dynamic_extent);
const std::string cv_type_descr =
scalar ? "scalar" : "array[" + (dynamic ? std::string() : std::to_string(extent)) + "]";
param_descr.description =
std::to_string(id->type()) + " " + cv_type_descr + " range {" + info.min().toString() +
"}..{" + info.max().toString() + "}" +
(info.def().isNone() ? std::string {} : " (default: {" + info.def().toString() + "})");
}
catch (const std::runtime_error &e) {
// ignore
RCLCPP_WARN_STREAM(get_logger(), e.what());
continue;
}

// format type description
const std::string cv_descr =
std::to_string(id->type()) + " " +
std::string(extent > 1 ? "array[" + std::to_string(extent) + "]" : "scalar") + " range {" +
info.min().toString() + "}..{" + info.max().toString() + "}" +
(info.def().isNone() ? std::string {} : " (default: {" + info.def().toString() + "})");

if (info.min().numElements() != info.max().numElements())
throw std::runtime_error("minimum and maximum parameter array sizes do not match");

// clamp default ControlValue to min/max range and cast ParameterValue
const rclcpp::ParameterValue value = cv_to_pv(clamp(info.def(), info.min(), info.max()), extent);

// get smallest bounds for minimum and maximum set
rcl_interfaces::msg::IntegerRange range_int;
rcl_interfaces::msg::FloatingPointRange range_float;
Expand All @@ -484,18 +490,30 @@ CameraNode::declareParameters()
break;
}

rcl_interfaces::msg::ParameterDescriptor param_descr;
param_descr.description = cv_descr;
if (range_int.from_value != range_int.to_value)
param_descr.integer_range = {range_int};
if (range_float.from_value != range_float.to_value)
param_descr.floating_point_range = {range_float};

// clamp default ControlValue to min/max range and cast ParameterValue
rclcpp::ParameterValue value;
try {
value = cv_to_pv(clamp(info.def(), info.min(), info.max()));
}
catch (const invalid_conversion &e) {
RCLCPP_ERROR_STREAM(get_logger(), "unsupported control '"
<< id->name()
<< "' (type: " << std::to_string(info.def().type()) << "): "
<< e.what());
continue;
}

// declare parameters and set default or initial value
RCLCPP_DEBUG_STREAM(get_logger(),
"declare " << id->name() << " with default " << rclcpp::to_string(value));

if (value.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
declare_parameter(id->name(), cv_to_pv_type(id->type(), extent > 0), param_descr);
declare_parameter(id->name(), pv_type, param_descr);
}
else {
declare_parameter(id->name(), value, param_descr);
Expand Down Expand Up @@ -702,9 +720,12 @@ CameraNode::onParameterChange(const std::vector<rclcpp::Parameter> &parameters)
}

const std::size_t extent = get_extent(id);
if ((value.isArray() && (extent > 0)) && value.numElements() != extent) {
if (value.isArray() &&
(extent != libcamera::dynamic_extent) &&
(value.numElements() != extent))
{
result.successful = false;
result.reason = parameter.get_name() + ": parameter dimensions mismatch, expected " +
result.reason = parameter.get_name() + ": array dimensions mismatch, expected " +
std::to_string(extent) + ", got " + std::to_string(value.numElements());
return result;
}
Expand Down
20 changes: 20 additions & 0 deletions src/clamp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,17 @@ less(const libcamera::ControlValue &lhs, const libcamera::ControlValue &rhs)
return false;
}
}
else if (rhs.isArray()) {
// scalar-array comparison
const libcamera::Span<const T> vb = rhs.get<libcamera::Span<const T>>();
const T va = lhs.get<T>();
for (size_t i = 0; i < vb.size(); i++)
if (va < vb[i])
return true;
return false;
}
else {
assert(!lhs.isArray() && !rhs.isArray());
// scalar-scalar comparison
return lhs.get<T>() < rhs.get<T>();
}
Expand Down Expand Up @@ -211,7 +221,17 @@ greater(const libcamera::ControlValue &lhs, const libcamera::ControlValue &rhs)
return false;
}
}
else if (rhs.isArray()) {
// scalar-array comparison
const libcamera::Span<const T> vb = rhs.get<libcamera::Span<const T>>();
const T va = lhs.get<T>();
for (size_t i = 0; i < vb.size(); i++)
if (va > vb[i])
return true;
return false;
}
else {
assert(!lhs.isArray() && !rhs.isArray());
// scalar-scalar comparison
return lhs.get<T>() > rhs.get<T>();
}
Expand Down
39 changes: 19 additions & 20 deletions src/cv_to_pv.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "cv_to_pv.hpp"
#include "type_extent.hpp"
#include "types.hpp"
#include <cstdint>
#include <libcamera/base/span.h>
Expand All @@ -13,7 +14,7 @@

#define CASE_CONVERT(T) \
case libcamera::ControlType##T: \
return cv_to_pv(extract_value<ControlTypeMap<libcamera::ControlType##T>::type>(value), extent);
return cv_to_pv(extract_value<ControlTypeMap<libcamera::ControlType##T>::type>(value));

#define CASE_NONE(T) \
case libcamera::ControlType##T: \
Expand Down Expand Up @@ -48,7 +49,7 @@ template<typename T,
rclcpp::ParameterValue
cv_to_pv_array(const std::vector<T> & /*values*/)
{
throw std::runtime_error("ParameterValue only supported for arithmetic types");
throw invalid_conversion("ParameterValue only supported for arithmetic types");
}

template<
Expand All @@ -75,25 +76,23 @@ cv_to_pv_scalar(const libcamera::Size &size)

template<typename T>
rclcpp::ParameterValue
cv_to_pv(const std::vector<T> &values, const std::size_t &extent)
cv_to_pv(const std::vector<T> &values)
{
if ((values.size() > 1 && extent > 1) && (values.size() != extent))
throw std::runtime_error("type extent (" + std::to_string(extent) + ") and value size (" +
std::to_string(values.size()) + ") cannot be larger than 1 and differ");

if (values.size() > 1)
return cv_to_pv_array(values);
else if (values.size() == 1)
if (!extent)
return cv_to_pv_scalar(values[0]);
else
return cv_to_pv_array(std::vector<T>(extent, values[0]));
else
switch (values.size()) {
case 0:
// empty array
return rclcpp::ParameterValue();
case 1:
// single element (scalar)
return cv_to_pv_scalar(values[0]);
default:
// dynamic array
return cv_to_pv_array(values);
}
}

rclcpp::ParameterValue
cv_to_pv(const libcamera::ControlValue &value, const std::size_t &extent)
cv_to_pv(const libcamera::ControlValue &value)
{
switch (value.type()) {
CASE_NONE(None)
Expand All @@ -111,10 +110,10 @@ cv_to_pv(const libcamera::ControlValue &value, const std::size_t &extent)
}

rclcpp::ParameterType
cv_to_pv_type(const libcamera::ControlType &type, const bool is_array)
cv_to_pv_type(const libcamera::ControlId *const id)
{
if (!is_array) {
switch (type) {
if (get_extent(id) == 0) {
switch (id->type()) {
case libcamera::ControlType::ControlTypeNone:
return rclcpp::ParameterType::PARAMETER_NOT_SET;
case libcamera::ControlType::ControlTypeBool:
Expand All @@ -134,7 +133,7 @@ cv_to_pv_type(const libcamera::ControlType &type, const bool is_array)
}
}
else {
switch (type) {
switch (id->type()) {
case libcamera::ControlType::ControlTypeNone:
return rclcpp::ParameterType::PARAMETER_NOT_SET;
case libcamera::ControlType::ControlTypeBool:
Expand Down
11 changes: 9 additions & 2 deletions src/cv_to_pv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@
#include <rclcpp/parameter_value.hpp>


class invalid_conversion : public std::runtime_error
{
public:
explicit invalid_conversion(const std::string &msg) : std::runtime_error(msg) {}
};


rclcpp::ParameterValue
cv_to_pv(const libcamera::ControlValue &value, const std::size_t &extent);
cv_to_pv(const libcamera::ControlValue &value);

rclcpp::ParameterType
cv_to_pv_type(const libcamera::ControlType &type, const bool is_array);
cv_to_pv_type(const libcamera::ControlId *const id);
10 changes: 8 additions & 2 deletions src/type_extent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,20 @@ template<typename T, std::enable_if_t<!libcamera::details::is_span<T>::value, bo
std::size_t
get_extent(const libcamera::Control<T> &)
{
// return an extent of 0 for non-span types
return 0;
}

template<typename T, std::enable_if_t<libcamera::details::is_span<T>::value, bool> = true>
std::size_t
get_extent(const libcamera::Control<T> &)
{
return libcamera::Control<T>::type::extent;
// return the span extent, excluding 0
// This assumes that libcamera does not define control types
// with a fixed size span that does not hold any elements
constexpr std::size_t extent = libcamera::Control<T>::type::extent;
static_assert(extent != 0);
return extent;
}

#define IF(T) \
Expand All @@ -35,7 +41,7 @@ get_extent(const libcamera::Control<T> &)


std::size_t
get_extent(const libcamera::ControlId *id)
get_extent(const libcamera::ControlId *const id)
{
#if LIBCAMERA_VER_GE(0, 1, 0)
IF(AeEnable)
Expand Down
8 changes: 7 additions & 1 deletion src/type_extent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,11 @@ namespace libcamera
class ControlId;
}

/**
* @brief get the extent of a libcamera::ControlId
* @param id
* @return the extent of the control: 0 if the control is not a span,
* otherwise [1 ... libcamera::dynamic_extent]
*/
std::size_t
get_extent(const libcamera::ControlId *id);
get_extent(const libcamera::ControlId *const id);