Add distance sensor and display

This commit is contained in:
2026-03-03 21:11:34 -05:00
parent f09e367d53
commit e67d44a536
16 changed files with 288 additions and 27 deletions

View File

@@ -16,6 +16,7 @@ find_package(crashpad REQUIRED)
file(GLOB_RECURSE COMMON_SOURCES file(GLOB_RECURSE COMMON_SOURCES
src/flatbuffers/*.cpp src/flatbuffers/*.cpp
src/actuators/*.cpp src/actuators/*.cpp
src/sensors/*.cpp
src/Module.cpp src/Module.cpp
src/Hub.cpp src/Hub.cpp
src/ModuleFactory.cpp src/ModuleFactory.cpp

View File

@@ -7,7 +7,7 @@ from conan.tools.files import copy
class MyLibraryConan(ConanFile): class MyLibraryConan(ConanFile):
name = "libcontrol" name = "libcontrol"
version = "1.0.2" version = "1.0.3"
settings = "os", "compiler", "build_type", "arch" settings = "os", "compiler", "build_type", "arch"
options = {"shared": [True, False], "fPIC": [True, False]} options = {"shared": [True, False], "fPIC": [True, False]}
@@ -40,7 +40,7 @@ class MyLibraryConan(ConanFile):
def requirements(self): def requirements(self):
self.requires("flatbuffers/24.12.23") self.requires("flatbuffers/24.12.23")
self.requires("librpc/1.1.7") self.requires("librpc/1.1.8")
self.requires("eigen/3.4.1") self.requires("eigen/3.4.1")
self.requires("spdlog/1.16.0") self.requires("spdlog/1.16.0")
self.requires("sentry-native/0.12.2") self.requires("sentry-native/0.12.2")

View File

@@ -2,6 +2,8 @@ EXPORTS
init init
cleanup cleanup
send_angle_control send_angle_control
send_string_control
get_distance_control
get_configuration get_configuration
control_sentry_init control_sentry_init
control_sentry_set_app_info control_sentry_set_app_info

4
examples/display/build.sh Executable file
View File

@@ -0,0 +1,4 @@
conan install . --build=missing --output-folder=build -s build_type=Release
cmake -S . -B "build" -DCMAKE_TOOLCHAIN_FILE="build/conan_toolchain.cmake" -DCMAKE_BUILD_TYPE="Release"
cmake --build "./build" --config "Release"

View File

@@ -45,6 +45,7 @@ class Module {
// Not all modules implement all actuation/sensor values, some are no-ops // Not all modules implement all actuation/sensor values, some are no-ops
virtual double get_position(); virtual double get_position();
virtual double get_distance();
virtual std::string get_text(); virtual std::string get_text();
virtual void actuate(double x); virtual void actuate(double x);
virtual void actuate(const std::string &text); virtual void actuate(const std::string &text);
@@ -53,6 +54,7 @@ class Module {
void update_module_metadata(const Messaging::TopologyMessage &message); void update_module_metadata(const Messaging::TopologyMessage &message);
virtual std::vector<uint8_t> get_actuation_message() = 0; virtual std::vector<uint8_t> get_actuation_message() = 0;
virtual void update_sensor_data(const Flatbuffers::sensor_value &value) = 0; virtual void update_sensor_data(const Flatbuffers::sensor_value &value) = 0;
private: private:

View File

@@ -24,7 +24,11 @@ struct current_text {
std::string text; std::string text;
}; };
typedef std::variant<target_angle, current_angle, current_text> sensor_value; struct distance {
float distance;
};
typedef std::variant<target_angle, current_angle, current_text, distance> sensor_value;
class SensorMessageBuilder { class SensorMessageBuilder {
public: public:
@@ -51,6 +55,10 @@ class SensorMessageBuilder {
static_cast<const Messaging::CurrentText *>(value); static_cast<const Messaging::CurrentText *>(value);
return current_text{current->value()->str()}; return current_text{current->value()->str()};
} }
case Messaging::SensorValue_Distance: {
const Messaging::Distance *current = static_cast<const Messaging::Distance *>(value);
return distance{current->value()};
}
default: default:
return std::nullopt; return std::nullopt;
} }

View File

@@ -1,5 +1,6 @@
// automatically generated by the FlatBuffers compiler, do not modify // automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_ #ifndef FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_
#define FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_ #define FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_
@@ -8,9 +9,9 @@
// Ensure the included flatbuffers.h is the same version as when this file was // Ensure the included flatbuffers.h is the same version as when this file was
// generated, otherwise it may not be compatible. // generated, otherwise it may not be compatible.
// static_assert(FLATBUFFERS_VERSION_MAJOR == 25 && // static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
// FLATBUFFERS_VERSION_MINOR == 2 && // FLATBUFFERS_VERSION_MINOR == 2 &&
// FLATBUFFERS_VERSION_REVISION == 10, // FLATBUFFERS_VERSION_REVISION == 10,
// "Non-compatible flatbuffers version included"); // "Non-compatible flatbuffers version included");
namespace Messaging { namespace Messaging {
@@ -23,6 +24,15 @@ struct CurrentTextBuilder;
struct CurrentAngle; struct CurrentAngle;
struct CurrentAngleBuilder; struct CurrentAngleBuilder;
struct Distance;
struct DistanceBuilder;
struct Temperature;
struct TemperatureBuilder;
struct Position;
struct PositionBuilder;
struct SensorMessage; struct SensorMessage;
struct SensorMessageBuilder; struct SensorMessageBuilder;
@@ -31,24 +41,29 @@ enum SensorValue : uint8_t {
SensorValue_TargetAngle = 1, SensorValue_TargetAngle = 1,
SensorValue_CurrentAngle = 2, SensorValue_CurrentAngle = 2,
SensorValue_CurrentText = 3, SensorValue_CurrentText = 3,
SensorValue_Distance = 4,
SensorValue_Temperature = 5,
SensorValue_Position = 6,
SensorValue_MIN = SensorValue_NONE, SensorValue_MIN = SensorValue_NONE,
SensorValue_MAX = SensorValue_CurrentText SensorValue_MAX = SensorValue_Position
}; };
inline const SensorValue (&EnumValuesSensorValue())[4] { inline const SensorValue (&EnumValuesSensorValue())[7] {
static const SensorValue values[] = {SensorValue_NONE, SensorValue_TargetAngle, static const SensorValue values[] = {SensorValue_NONE, SensorValue_TargetAngle,
SensorValue_CurrentAngle, SensorValue_CurrentText}; SensorValue_CurrentAngle, SensorValue_CurrentText,
SensorValue_Distance, SensorValue_Temperature,
SensorValue_Position};
return values; return values;
} }
inline const char *const *EnumNamesSensorValue() { inline const char *const *EnumNamesSensorValue() {
static const char *const names[5] = {"NONE", "TargetAngle", "CurrentAngle", "CurrentText", static const char *const names[8] = {"NONE", "TargetAngle", "CurrentAngle", "CurrentText",
nullptr}; "Distance", "Temperature", "Position", nullptr};
return names; return names;
} }
inline const char *EnumNameSensorValue(SensorValue e) { inline const char *EnumNameSensorValue(SensorValue e) {
if (::flatbuffers::IsOutRange(e, SensorValue_NONE, SensorValue_CurrentText)) if (::flatbuffers::IsOutRange(e, SensorValue_NONE, SensorValue_Position))
return ""; return "";
const size_t index = static_cast<size_t>(e); const size_t index = static_cast<size_t>(e);
return EnumNamesSensorValue()[index]; return EnumNamesSensorValue()[index];
@@ -70,6 +85,18 @@ template <> struct SensorValueTraits<Messaging::CurrentText> {
static const SensorValue enum_value = SensorValue_CurrentText; static const SensorValue enum_value = SensorValue_CurrentText;
}; };
template <> struct SensorValueTraits<Messaging::Distance> {
static const SensorValue enum_value = SensorValue_Distance;
};
template <> struct SensorValueTraits<Messaging::Temperature> {
static const SensorValue enum_value = SensorValue_Temperature;
};
template <> struct SensorValueTraits<Messaging::Position> {
static const SensorValue enum_value = SensorValue_Position;
};
bool VerifySensorValue(::flatbuffers::Verifier &verifier, const void *obj, SensorValue type); bool VerifySensorValue(::flatbuffers::Verifier &verifier, const void *obj, SensorValue type);
bool VerifySensorValueVector(::flatbuffers::Verifier &verifier, bool VerifySensorValueVector(::flatbuffers::Verifier &verifier,
const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values, const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values,
@@ -190,6 +217,134 @@ CreateCurrentAngle(::flatbuffers::FlatBufferBuilder &_fbb, int16_t value = 0) {
return builder_.Finish(); return builder_.Finish();
} }
struct Distance FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef DistanceBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_VALUE = 4 };
float value() const {
return GetField<float>(VT_VALUE, 0.0f);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) && VerifyField<float>(verifier, VT_VALUE, 4) &&
verifier.EndTable();
}
};
struct DistanceBuilder {
typedef Distance Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_value(float value) {
fbb_.AddElement<float>(Distance::VT_VALUE, value, 0.0f);
}
explicit DistanceBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<Distance> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<Distance>(end);
return o;
}
};
inline ::flatbuffers::Offset<Distance> CreateDistance(::flatbuffers::FlatBufferBuilder &_fbb,
float value = 0.0f) {
DistanceBuilder builder_(_fbb);
builder_.add_value(value);
return builder_.Finish();
}
struct Temperature FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef TemperatureBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_VALUE = 4 };
float value() const {
return GetField<float>(VT_VALUE, 0.0f);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) && VerifyField<float>(verifier, VT_VALUE, 4) &&
verifier.EndTable();
}
};
struct TemperatureBuilder {
typedef Temperature Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_value(float value) {
fbb_.AddElement<float>(Temperature::VT_VALUE, value, 0.0f);
}
explicit TemperatureBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<Temperature> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<Temperature>(end);
return o;
}
};
inline ::flatbuffers::Offset<Temperature> CreateTemperature(::flatbuffers::FlatBufferBuilder &_fbb,
float value = 0.0f) {
TemperatureBuilder builder_(_fbb);
builder_.add_value(value);
return builder_.Finish();
}
struct Position FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef PositionBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_HEADING = 4,
VT_PITCH = 6,
VT_ROLL = 8
};
float heading() const {
return GetField<float>(VT_HEADING, 0.0f);
}
float pitch() const {
return GetField<float>(VT_PITCH, 0.0f);
}
float roll() const {
return GetField<float>(VT_ROLL, 0.0f);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) && VerifyField<float>(verifier, VT_HEADING, 4) &&
VerifyField<float>(verifier, VT_PITCH, 4) &&
VerifyField<float>(verifier, VT_ROLL, 4) && verifier.EndTable();
}
};
struct PositionBuilder {
typedef Position Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_heading(float heading) {
fbb_.AddElement<float>(Position::VT_HEADING, heading, 0.0f);
}
void add_pitch(float pitch) {
fbb_.AddElement<float>(Position::VT_PITCH, pitch, 0.0f);
}
void add_roll(float roll) {
fbb_.AddElement<float>(Position::VT_ROLL, roll, 0.0f);
}
explicit PositionBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<Position> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<Position>(end);
return o;
}
};
inline ::flatbuffers::Offset<Position> CreatePosition(::flatbuffers::FlatBufferBuilder &_fbb,
float heading = 0.0f, float pitch = 0.0f,
float roll = 0.0f) {
PositionBuilder builder_(_fbb);
builder_.add_roll(roll);
builder_.add_pitch(pitch);
builder_.add_heading(heading);
return builder_.Finish();
}
struct SensorMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { struct SensorMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef SensorMessageBuilder Builder; typedef SensorMessageBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
@@ -268,6 +423,18 @@ inline bool VerifySensorValue(::flatbuffers::Verifier &verifier, const void *obj
auto ptr = reinterpret_cast<const Messaging::CurrentText *>(obj); auto ptr = reinterpret_cast<const Messaging::CurrentText *>(obj);
return verifier.VerifyTable(ptr); return verifier.VerifyTable(ptr);
} }
case SensorValue_Distance: {
auto ptr = reinterpret_cast<const Messaging::Distance *>(obj);
return verifier.VerifyTable(ptr);
}
case SensorValue_Temperature: {
auto ptr = reinterpret_cast<const Messaging::Temperature *>(obj);
return verifier.VerifyTable(ptr);
}
case SensorValue_Position: {
auto ptr = reinterpret_cast<const Messaging::Position *>(obj);
return verifier.VerifyTable(ptr);
}
default: default:
return true; return true;
} }

View File

@@ -17,6 +17,8 @@ extern "C" {
LIB_API void init(); LIB_API void init();
LIB_API void cleanup(); LIB_API void cleanup();
LIB_API int send_angle_control(int module_id, int angle); LIB_API int send_angle_control(int module_id, int angle);
LIB_API int send_string_control(int module_id, const char *string);
LIB_API double get_distance_control(int module_id);
LIB_API char *get_configuration(int *size_out); LIB_API char *get_configuration(int *size_out);
LIB_API bool remote_call_c(uint8_t function_tag, uint8_t module, const uint8_t *params, LIB_API bool remote_call_c(uint8_t function_tag, uint8_t module, const uint8_t *params,

View File

@@ -0,0 +1,23 @@
#ifndef CONTROL_DISTANCESENSOR_H
#define CONTROL_DISTANCESENSOR_H
#include "Module.h"
#include "flatbuffers/SensorMessageBuilder.h"
#include "util/Variant.h"
class DistanceSensor : public Module {
public:
DistanceSensor(uint8_t device_id, ModuleType type) : Module(device_id, type) {
}
double get_distance() override;
std::vector<uint8_t> get_actuation_message() override;
void update_sensor_data(const Flatbuffers::sensor_value &value) override;
private:
double m_current_distance = 0.0;
};
#endif // CONTROL_DISTANCESENSOR_H

View File

@@ -52,6 +52,11 @@ std::string Module::get_text() {
return ""; return "";
} }
double Module::get_distance() {
// no-op
return 0.0;
}
void Module::actuate(double /* position */) { void Module::actuate(double /* position */) {
// no-op // no-op
} }

View File

@@ -15,6 +15,13 @@ ModuleFactory::createModule(uint8_t device_id, ModuleType type,
std::shared_ptr<MessagingInterface> &messaging_interface) { std::shared_ptr<MessagingInterface> &messaging_interface) {
switch (type) { switch (type) {
case ModuleType_SPLITTER: case ModuleType_SPLITTER:
case ModuleType_SPLITTER_2:
case ModuleType_SPLITTER_3:
case ModuleType_SPLITTER_4:
case ModuleType_SPLITTER_5:
case ModuleType_SPLITTER_6:
case ModuleType_SPLITTER_7:
case ModuleType_SPLITTER_8:
return std::make_shared<Hub>(device_id, type); return std::make_shared<Hub>(device_id, type);
case ModuleType_BATTERY: case ModuleType_BATTERY:
return std::make_shared<Hub>(device_id, type); return std::make_shared<Hub>(device_id, type);

View File

@@ -32,10 +32,9 @@ std::vector<uint8_t> BoundedPositionalActuator1D::get_actuation_message() {
} }
void BoundedPositionalActuator1D::update_sensor_data(const Flatbuffers::sensor_value &value) { void BoundedPositionalActuator1D::update_sensor_data(const Flatbuffers::sensor_value &value) {
std::visit(overloaded{ std::visit(overloaded{[this](Flatbuffers::target_angle a) { m_current_position = a.angle; },
[this](Flatbuffers::target_angle a) { m_current_position = a.angle; }, [this](Flatbuffers::current_angle a) { m_current_position = a.angle; },
[this](Flatbuffers::current_angle a) { m_current_position = a.angle; }, [this](Flatbuffers::current_text /*t*/) {},
[this](Flatbuffers::current_text /*t*/) {}, [this](Flatbuffers::distance /*d*/) {}},
},
value); value);
} }

View File

@@ -25,10 +25,9 @@ std::vector<uint8_t> OledActuator::get_actuation_message() {
} }
void OledActuator::update_sensor_data(const Flatbuffers::sensor_value &value) { void OledActuator::update_sensor_data(const Flatbuffers::sensor_value &value) {
std::visit(overloaded{ std::visit(overloaded{[this](Flatbuffers::target_angle /* a */) {},
[this](Flatbuffers::target_angle /* a */) {}, [this](Flatbuffers::current_angle /* a */) {},
[this](Flatbuffers::current_angle /* a */) {}, [this](Flatbuffers::current_text t) { m_current_text = t.text; },
[this](Flatbuffers::current_text t) { m_current_text = t.text; }, [this](Flatbuffers::distance /*d*/) {}},
},
value); value);
} }

View File

@@ -25,10 +25,10 @@ std::vector<uint8_t> PositionalActuator1D::get_actuation_message() {
} }
void PositionalActuator1D::update_sensor_data(const Flatbuffers::sensor_value &value) { void PositionalActuator1D::update_sensor_data(const Flatbuffers::sensor_value &value) {
std::visit(overloaded{ std::visit(
[this](Flatbuffers::target_angle a) { m_board_target_position = a.angle; }, overloaded{[this](Flatbuffers::target_angle a) { m_board_target_position = a.angle; },
[this](Flatbuffers::current_angle a) { m_current_position = a.angle; }, [this](Flatbuffers::current_angle a) { m_current_position = a.angle; },
[this](Flatbuffers::current_text /*t*/) {}, [this](Flatbuffers::current_text /*t*/) {},
}, [this](Flatbuffers::distance /*d*/) {}},
value); value);
} }

View File

@@ -39,6 +39,30 @@ LIB_API int send_angle_control(int module_id, int angle) {
return 0; return 0;
} }
LIB_API int send_string_control(int module_id, const char *string) {
if (const auto maybe_module = robot_controller->getModule(module_id)) {
const auto module = (*maybe_module).lock();
if (module) {
module->actuate(std::string(string));
} else {
spdlog::warn("[c_control] send_angle_control: module {} has expired", module_id);
}
}
return 0;
}
LIB_API double get_distance_control(int module_id) {
if (const auto maybe_module = robot_controller->getModule(module_id)) {
const auto module = (*maybe_module).lock();
if (module) {
return module->get_distance();
} else {
spdlog::warn("[c_control] send_angle_control: module {} has expired", module_id);
}
}
return 0.0;
}
LIB_API char *get_configuration(int *size_out) { LIB_API char *get_configuration(int *size_out) {
std::vector<Flatbuffers::ModuleInstance> modules_vec{}; std::vector<Flatbuffers::ModuleInstance> modules_vec{};
std::vector<Flatbuffers::ModuleConnectionInstance> connections_vec{}; std::vector<Flatbuffers::ModuleConnectionInstance> connections_vec{};

View File

@@ -0,0 +1,18 @@
#include "sensors/DistanceSensor.h"
double DistanceSensor::get_distance() {
return m_current_distance;
}
std::vector<uint8_t> DistanceSensor::get_actuation_message() {
return {};
}
void DistanceSensor::update_sensor_data(const Flatbuffers::sensor_value &value) {
std::visit(overloaded{[this](Flatbuffers::target_angle /*a*/) {},
[this](Flatbuffers::current_angle /*a*/) {},
[this](Flatbuffers::current_text /*t*/) {},
[this](Flatbuffers::distance d) { m_current_distance = d.distance; }},
value);
}