mirror of
https://github.com/BotChain-Robots/control.git
synced 2026-03-09 16:22:26 +01:00
Prepare files for public release
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -39,3 +39,9 @@
|
||||
|
||||
# debug information files
|
||||
*.dwo
|
||||
|
||||
/build
|
||||
.DS_Store
|
||||
/cmake-build-debug
|
||||
/.cache
|
||||
**/.DS_Store
|
||||
|
||||
83
CMakeLists.txt
Normal file
83
CMakeLists.txt
Normal file
@@ -0,0 +1,83 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(control LANGUAGES C CXX)
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
|
||||
|
||||
find_package(Eigen3 REQUIRED)
|
||||
find_package(librpc REQUIRED)
|
||||
find_package(flatbuffers REQUIRED)
|
||||
find_package(spdlog REQUIRED)
|
||||
find_package(sentry REQUIRED)
|
||||
find_package(crashpad REQUIRED)
|
||||
|
||||
file(GLOB_RECURSE COMMON_SOURCES
|
||||
src/flatbuffers/*.cpp
|
||||
src/actuators/*.cpp
|
||||
src/Module.cpp
|
||||
src/Hub.cpp
|
||||
src/ModuleFactory.cpp
|
||||
src/libcontrol.cpp
|
||||
)
|
||||
|
||||
# C API Library
|
||||
if(WIN32)
|
||||
add_library(c_control SHARED
|
||||
src/lib_c_control.cpp
|
||||
${COMMON_SOURCES}
|
||||
control.def
|
||||
)
|
||||
target_compile_definitions(c_control PRIVATE CONTROL_EXPORTS)
|
||||
else()
|
||||
add_library(c_control SHARED
|
||||
src/lib_c_control.cpp
|
||||
${COMMON_SOURCES}
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(c_control PUBLIC include)
|
||||
target_link_libraries(c_control
|
||||
PRIVATE
|
||||
librpc::librpc
|
||||
Eigen3::Eigen
|
||||
spdlog::spdlog
|
||||
sentry-native::sentry-native
|
||||
sentry-crashpad::sentry-crashpad
|
||||
)
|
||||
set_property(TARGET c_control PROPERTY CXX_STANDARD 23)
|
||||
|
||||
install(TARGETS c_control
|
||||
EXPORT controlTargets
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
INCLUDES DESTINATION include
|
||||
)
|
||||
|
||||
# C++ API Library
|
||||
add_library(control SHARED
|
||||
${COMMON_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(control PUBLIC include)
|
||||
target_link_libraries(control
|
||||
PRIVATE
|
||||
librpc::librpc
|
||||
Eigen3::Eigen
|
||||
spdlog::spdlog
|
||||
sentry-native::sentry-native
|
||||
sentry-crashpad::sentry-crashpad
|
||||
)
|
||||
set_property(TARGET control PROPERTY CXX_STANDARD 23)
|
||||
|
||||
install(TARGETS control
|
||||
EXPORT controlTargets
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
INCLUDES DESTINATION include
|
||||
)
|
||||
|
||||
install(DIRECTORY include/ DESTINATION include)
|
||||
9
CMakeUserPresets.json
Normal file
9
CMakeUserPresets.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"version": 4,
|
||||
"vendor": {
|
||||
"conan": {}
|
||||
},
|
||||
"include": [
|
||||
"build/RelWithDebInfo/generators/CMakePresets.json"
|
||||
]
|
||||
}
|
||||
104
README.md
Normal file
104
README.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Control Library
|
||||
The Control library provides a high level interface to control BotChain devices.
|
||||
This builds separate C and C++ libraries, see `include/lib_c_control.h` and `include/libcontrol.h`.
|
||||
The library is managed by the [conan](https://conan.io) package manager, and is consumed by BotChain's user interface.
|
||||
The library uses the [RPC library](https://git.uwaterloo.ca/capstone-group2/rpc) to communicate with BotChain's modules.
|
||||
|
||||
The latest releases of the Control library can be found in our [artifactory](http://jslightham.com:8082).
|
||||
|
||||
## Platform Support
|
||||
- MacOS (Apple silicon)
|
||||
- MacOS (x86)
|
||||
- Ubuntu (x86)
|
||||
- Windows (x86)
|
||||
|
||||
## Setup
|
||||
### MacOS
|
||||
Install xcode command line tools (if you do not already have them)
|
||||
```
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
Install conan and dependencies
|
||||
```
|
||||
brew install conan cmake ninja clang-format@21
|
||||
```
|
||||
|
||||
Generate a conan profile
|
||||
```
|
||||
conan profile detect --force
|
||||
```
|
||||
|
||||
Follow the required artifactory setup.
|
||||
|
||||
### Ubuntu
|
||||
On newer versions of Ubuntu, the package manager is responsible for managing python packages. We use `pipx` to create a virtual environment.
|
||||
|
||||
Install `pipx` and dependencies
|
||||
```
|
||||
sudo apt install pipx cmake ninja-build
|
||||
```
|
||||
|
||||
Install clang-format version 21.
|
||||
|
||||
Install conan
|
||||
```
|
||||
pipx install conan
|
||||
```
|
||||
|
||||
Generate a conan profile
|
||||
```
|
||||
conan profile detect --force
|
||||
```
|
||||
|
||||
Follow the required artifactory setup.
|
||||
|
||||
### Required Artifactory Setup
|
||||
These instructions should only be followed after you have completed all setup steps for your platform.
|
||||
|
||||
Add the artifactory
|
||||
```
|
||||
conan remote add artifactory http://jslightham.com:8081/artifactory/api/conan/botchain
|
||||
```
|
||||
|
||||
### Additional Artifactory Setup (optional)
|
||||
These instructions should only be followed after you have completed all setup steps for your platform.
|
||||
|
||||
This is an optional section that is only required if you plan on uploading releases to the artifactory.
|
||||
|
||||
Add credentials to connect to the remote artifactory
|
||||
```
|
||||
conan remote login artifactory <username> -p <password>
|
||||
```
|
||||
|
||||
Contact Johnathon to get login credentials for the artifactory.
|
||||
|
||||
## Development
|
||||
```
|
||||
# Macos and Linux users can run,
|
||||
./build_control_library.sh
|
||||
|
||||
# Manual steps
|
||||
build_type=Release # set to your desired build type (Release, RelWithDebInfo, Debug, etc.)
|
||||
conan install . --build=missing --output-folder=. -s build_type="${build_type}"
|
||||
cmake -S . -B "./build/${build_type}" -DCMAKE_TOOLCHAIN_FILE="./build/${build_type}/generators/conan_toolchain.cmake" -DCMAKE_BUILD_TYPE="${build_type}"
|
||||
cmake --build "./build/${build_type}" --config "${build_type}"
|
||||
```
|
||||
|
||||
## Building For Release
|
||||
Create the package
|
||||
```
|
||||
conan create .
|
||||
```
|
||||
|
||||
Upload to the artifactory
|
||||
```
|
||||
conan upload libcontrol -r=artifactory
|
||||
```
|
||||
|
||||
## Crashpad
|
||||
To be able to use sentry, we need a crashpad executable.
|
||||
The executable can be taken out of the conan cache, ie,
|
||||
```
|
||||
find ~/.conan2 -type f -name "crashpad_handler" -perm /u=x,g=x,o=x 2>/dev/null | head -n 1
|
||||
```
|
||||
61
build_control_library.sh
Executable file
61
build_control_library.sh
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
function usage() {
|
||||
echo "Usage:"
|
||||
echo "${SCRIPT_NAME} [-b <build type>] [-h]"
|
||||
echo " -b | --build-type - The build type (ie. Release, Debug, RelWithDebInfo)"
|
||||
echo " -h | --help - Print usage"
|
||||
echo "Example:"
|
||||
echo "${SCRIPT_NAME} -b Release"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function parse_args() {
|
||||
while [ -n "${1}" ]; do
|
||||
case "${1}" in
|
||||
-h | --help)
|
||||
usage
|
||||
;;
|
||||
-b | --build-type)
|
||||
[ -n "${2}" ] || usage || echo "ERROR: Not enough parameters"
|
||||
build_type="${2}"
|
||||
shift 2
|
||||
;;
|
||||
-d | --disable-format)
|
||||
disable_format=true
|
||||
shift 1
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: Invalid parameter. Exiting..."
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
function check_pre_req() {
|
||||
if [ "${build_type}" != "Debug" ] && [ "${build_type}" != "Release" ] && [ "${build_type}" != "RelWithDebInfo" ]; then
|
||||
usage
|
||||
echo "ERROR: Build type must be one of: Release, Debug, RelWithDebInfo"
|
||||
fi
|
||||
}
|
||||
|
||||
SCRIPT_NAME="$(basename "${0}")"
|
||||
ROOT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||
|
||||
build_type=""
|
||||
disable_format=false
|
||||
parse_args "${@}"
|
||||
check_pre_req
|
||||
|
||||
if [ "$disable_format" != "true" ]; then
|
||||
echo "Formatting with clang-format..."
|
||||
find "${ROOT_DIR}" -iname '*.h' -o -iname '*.cpp' | xargs clang-format -i -style=file
|
||||
fi
|
||||
|
||||
echo "Building..."
|
||||
conan install "${ROOT_DIR}" --build=missing --output-folder="${ROOT_DIR}" -s build_type="${build_type}"
|
||||
cmake -S "${ROOT_DIR}" -B "${ROOT_DIR}/build/${build_type}" -DCMAKE_TOOLCHAIN_FILE="${ROOT_DIR}/build/${build_type}/generators/conan_toolchain.cmake" -DCMAKE_BUILD_TYPE="${build_type}"
|
||||
cmake --build "${ROOT_DIR}/build/${build_type}" --config "${build_type}"
|
||||
1
compile_commands.json
Symbolic link
1
compile_commands.json
Symbolic link
@@ -0,0 +1 @@
|
||||
build/RelWithDebInfo/compile_commands.json
|
||||
50
conanfile.py
Normal file
50
conanfile.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import os
|
||||
|
||||
from conan import ConanFile
|
||||
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
|
||||
from conan.tools.files import copy
|
||||
|
||||
|
||||
class MyLibraryConan(ConanFile):
|
||||
name = "libcontrol"
|
||||
version = "1.0.0"
|
||||
|
||||
settings = "os", "compiler", "build_type", "arch"
|
||||
options = {"shared": [True, False], "fPIC": [True, False]}
|
||||
default_options = {"shared": True, "fPIC": True}
|
||||
|
||||
exports_sources = "CMakeLists.txt", "src/*", "include/*"
|
||||
|
||||
def layout(self):
|
||||
cmake_layout(self)
|
||||
|
||||
def generate(self):
|
||||
deps = CMakeDeps(self)
|
||||
deps.generate()
|
||||
tc = CMakeToolchain(self)
|
||||
tc.variables["BUILD_SHARED_LIBS"] = self.options.shared
|
||||
tc.generate()
|
||||
|
||||
def build(self):
|
||||
cmake = CMake(self)
|
||||
cmake.configure()
|
||||
cmake.build()
|
||||
|
||||
def package(self):
|
||||
cmake = CMake(self)
|
||||
cmake.install()
|
||||
|
||||
def package_info(self):
|
||||
self.cpp_info.libs = ["control"]
|
||||
|
||||
def requirements(self):
|
||||
self.requires("flatbuffers/24.12.23")
|
||||
self.requires("librpc/1.1.6")
|
||||
self.requires("eigen/3.4.1")
|
||||
self.requires("spdlog/1.16.0")
|
||||
self.requires("sentry-native/0.12.2")
|
||||
self.requires("sentry-crashpad/0.6.5")
|
||||
|
||||
def configure(self):
|
||||
if self.settings.os == "Linux":
|
||||
self.options.fPIC = True
|
||||
8
control.def
Normal file
8
control.def
Normal file
@@ -0,0 +1,8 @@
|
||||
EXPORTS
|
||||
init
|
||||
cleanup
|
||||
send_angle_control
|
||||
get_configuration
|
||||
control_sentry_init
|
||||
control_sentry_set_app_info
|
||||
control_sentry_shutdown
|
||||
29
include/Hub.h
Normal file
29
include/Hub.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2026-01-13.
|
||||
//
|
||||
|
||||
#ifndef CONTROL_HUB_H
|
||||
#define CONTROL_HUB_H
|
||||
|
||||
#include "Module.h"
|
||||
#include "flatbuffers/SensorMessageBuilder.h"
|
||||
|
||||
class Hub final : public Module {
|
||||
|
||||
public:
|
||||
explicit Hub(uint8_t device_id) : Module(device_id) {};
|
||||
|
||||
Hub(uint8_t device_id, ModuleType module_type) : Module(device_id, module_type) {};
|
||||
|
||||
Hub(uint8_t device_id, ModuleType module_type, Messaging::ConnectionType connection_type,
|
||||
uint8_t leader)
|
||||
: Module(device_id, module_type, connection_type, leader) {};
|
||||
|
||||
double get_position() override;
|
||||
void actuate(double position) override;
|
||||
void actuate(double x, double y) override;
|
||||
std::vector<uint8_t> get_actuation_message() override;
|
||||
void update_sensor_data(const Flatbuffers::sensor_value &value) override;
|
||||
};
|
||||
|
||||
#endif // CONTROL_HUB_H
|
||||
66
include/Module.h
Normal file
66
include/Module.h
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-11-14.
|
||||
//
|
||||
|
||||
#ifndef CONTROL_MODULE_H
|
||||
#define CONTROL_MODULE_H
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
|
||||
#include "flatbuffers/SensorMessageBuilder.h"
|
||||
#include "flatbuffers_generated/RobotModule_generated.h"
|
||||
#include "flatbuffers_generated/TopologyMessage_generated.h"
|
||||
#include "librpc.h"
|
||||
|
||||
struct neighbour {
|
||||
uint8_t device_id;
|
||||
Orientation orientation;
|
||||
};
|
||||
|
||||
class Module {
|
||||
public:
|
||||
explicit Module(uint8_t device_id) : m_device_id(device_id) {};
|
||||
|
||||
Module(uint8_t device_id, ModuleType module_type)
|
||||
: m_device_id(device_id), m_module_type(module_type) {};
|
||||
|
||||
Module(uint8_t device_id, ModuleType module_type, Messaging::ConnectionType connection_type,
|
||||
uint8_t leader)
|
||||
: m_device_id(device_id), m_module_type(module_type), m_connection_type(connection_type),
|
||||
m_leader(leader) {};
|
||||
|
||||
std::vector<neighbour> get_neighbours();
|
||||
|
||||
uint8_t get_device_id();
|
||||
|
||||
ModuleType get_type();
|
||||
|
||||
Messaging::ConnectionType get_connection_type();
|
||||
|
||||
uint8_t get_leader();
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> get_last_updated_time();
|
||||
|
||||
virtual double get_position() = 0;
|
||||
virtual void actuate(double x) = 0;
|
||||
|
||||
// There are no modules with 2D actuation support, this is an example of how to implement it.
|
||||
virtual void actuate(double x, double y) = 0;
|
||||
|
||||
void update_module_metadata(const Messaging::TopologyMessage &message);
|
||||
|
||||
virtual std::vector<uint8_t> get_actuation_message() = 0;
|
||||
virtual void update_sensor_data(const Flatbuffers::sensor_value &value) = 0;
|
||||
|
||||
private:
|
||||
uint8_t m_device_id;
|
||||
ModuleType m_module_type;
|
||||
Messaging::ConnectionType m_connection_type;
|
||||
uint8_t m_leader;
|
||||
std::chrono::time_point<std::chrono::system_clock> m_last_updated;
|
||||
std::vector<neighbour> m_neighbours;
|
||||
std::shared_ptr<MessagingInterface> m_messaging_interface;
|
||||
};
|
||||
|
||||
#endif // CONTROL_MODULE_H
|
||||
14
include/ModuleFactory.h
Normal file
14
include/ModuleFactory.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef CONTROL_MODULEFACTORY_H
|
||||
#define CONTROL_MODULEFACTORY_H
|
||||
|
||||
#include "Module.h"
|
||||
#include "flatbuffers_generated/RobotModule_generated.h"
|
||||
|
||||
class ModuleFactory {
|
||||
public:
|
||||
static std::shared_ptr<Module>
|
||||
createModule(uint8_t device_id, ModuleType type,
|
||||
std::shared_ptr<MessagingInterface> &messaging_interface);
|
||||
};
|
||||
|
||||
#endif // CONTROL_MODULEFACTORY_H
|
||||
28
include/actuators/Actuator.h
Normal file
28
include/actuators/Actuator.h
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
#ifndef CONTROL_ACTUATOR_H
|
||||
#define CONTROL_ACTUATOR_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Module.h"
|
||||
#include "flatbuffers_generated/RobotModule_generated.h"
|
||||
#include "flatbuffers_generated/TopologyMessage_generated.h"
|
||||
|
||||
class Actuator : public Module {
|
||||
public:
|
||||
explicit Actuator(uint8_t device_id) : Module(device_id) {};
|
||||
|
||||
Actuator(uint8_t device_id, ModuleType module_type) : Module(device_id, module_type) {};
|
||||
|
||||
Actuator(uint8_t device_id, ModuleType module_type, Messaging::ConnectionType connection_type,
|
||||
uint8_t leader)
|
||||
: Module(device_id, module_type, connection_type, leader) {};
|
||||
|
||||
protected:
|
||||
virtual std::vector<uint8_t> get_actuation_message() = 0;
|
||||
|
||||
private:
|
||||
[[noreturn]] void actuator_tx_loop();
|
||||
};
|
||||
|
||||
#endif // CONTROL_MODULE_H
|
||||
37
include/actuators/BoundedPositionalActuator1D.h
Normal file
37
include/actuators/BoundedPositionalActuator1D.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-11-13.
|
||||
//
|
||||
|
||||
#ifndef CONTROL_BOUNDED1DPOSITIONALACTUATOR_H
|
||||
#define CONTROL_BOUNDED1DPOSITIONALACTUATOR_H
|
||||
|
||||
#include "actuators/Actuator.h"
|
||||
#include "flatbuffers/AngleControlMessageBuilder.h"
|
||||
#include "flatbuffers/SensorMessageBuilder.h"
|
||||
|
||||
class BoundedPositionalActuator1D : public Actuator {
|
||||
|
||||
public:
|
||||
BoundedPositionalActuator1D(uint8_t device_id, ModuleType type, double max_value,
|
||||
double min_value, double initial_position)
|
||||
: Actuator(device_id, type), m_target_position(initial_position), m_max_value(max_value),
|
||||
m_min_value(min_value),
|
||||
acm_builder(std::make_unique<Flatbuffers::AngleControlMessageBuilder>()) {
|
||||
}
|
||||
|
||||
double get_position() override;
|
||||
void actuate(double position) override;
|
||||
void actuate(double x, double y) override; // no-op
|
||||
|
||||
std::vector<uint8_t> get_actuation_message() override;
|
||||
void update_sensor_data(const Flatbuffers::sensor_value &value) override;
|
||||
|
||||
private:
|
||||
double m_current_position = 0;
|
||||
double m_target_position;
|
||||
double m_max_value;
|
||||
double m_min_value;
|
||||
std::unique_ptr<Flatbuffers::AngleControlMessageBuilder> acm_builder;
|
||||
};
|
||||
|
||||
#endif // CONTROL_1DPOSITIONALACTUATOR_H
|
||||
34
include/actuators/PositionalActuator1D.h
Normal file
34
include/actuators/PositionalActuator1D.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-11-13.
|
||||
//
|
||||
|
||||
#ifndef CONTROL_1DPOSITIONALACTUATOR_H
|
||||
#define CONTROL_1DPOSITIONALACTUATOR_H
|
||||
|
||||
#include "actuators/Actuator.h"
|
||||
#include "flatbuffers/AngleControlMessageBuilder.h"
|
||||
#include "flatbuffers/SensorMessageBuilder.h"
|
||||
|
||||
class PositionalActuator1D final : public Actuator {
|
||||
|
||||
public:
|
||||
PositionalActuator1D(uint8_t device_id, ModuleType type)
|
||||
: Actuator(device_id, type),
|
||||
m_acm_builder(std::make_unique<Flatbuffers::AngleControlMessageBuilder>()) {};
|
||||
|
||||
double get_position() override;
|
||||
void actuate(double position) override;
|
||||
void actuate(double x, double y) override; // no-op
|
||||
|
||||
std::vector<uint8_t> get_actuation_message() override;
|
||||
void update_sensor_data(const Flatbuffers::sensor_value &value) override;
|
||||
|
||||
private:
|
||||
void update_loop();
|
||||
double m_target_position = 0;
|
||||
double m_current_position = 0;
|
||||
double m_board_target_position = 0;
|
||||
std::unique_ptr<Flatbuffers::AngleControlMessageBuilder> m_acm_builder;
|
||||
};
|
||||
|
||||
#endif // CONTROL_1DPOSITIONALACTUATOR_H
|
||||
29
include/flatbuffers/AngleControlMessageBuilder.h
Normal file
29
include/flatbuffers/AngleControlMessageBuilder.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-06-30.
|
||||
//
|
||||
|
||||
#ifndef ANGLECONTROLMESSAGEBUILDER_H_
|
||||
#define ANGLECONTROLMESSAGEBUILDER_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "SerializedMessage.h"
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
#include "flatbuffers_generated/AngleControlMessage_generated.h"
|
||||
|
||||
namespace Flatbuffers {
|
||||
class AngleControlMessageBuilder {
|
||||
public:
|
||||
AngleControlMessageBuilder() : builder_(256) {
|
||||
}
|
||||
|
||||
SerializedMessage build_angle_control_message(int16_t angle);
|
||||
static const Messaging::AngleControlMessage *parse_angle_control_message(const uint8_t *buffer);
|
||||
|
||||
private:
|
||||
flatbuffers::FlatBufferBuilder builder_;
|
||||
};
|
||||
} // namespace Flatbuffers
|
||||
|
||||
#endif
|
||||
45
include/flatbuffers/RobotConfigurationBuilder.h
Normal file
45
include/flatbuffers/RobotConfigurationBuilder.h
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-07-25.
|
||||
//
|
||||
|
||||
#ifndef ROBOTCONFIGURATIONBUILDER_H
|
||||
#define ROBOTCONFIGURATIONBUILDER_H
|
||||
#include "SerializedMessage.h"
|
||||
#include <cstdint>
|
||||
#include <flatbuffers_generated/RobotConfiguration_generated.h>
|
||||
#include <flatbuffers_generated/RobotModule_generated.h>
|
||||
#include <vector>
|
||||
|
||||
namespace Flatbuffers {
|
||||
struct ModuleInstance {
|
||||
uint8_t id;
|
||||
ModuleType type;
|
||||
int angle;
|
||||
};
|
||||
|
||||
struct ModuleConnectionInstance {
|
||||
uint8_t from_module_id;
|
||||
uint8_t to_module_id;
|
||||
uint8_t from_socket;
|
||||
uint8_t to_socket;
|
||||
Orientation orientation;
|
||||
};
|
||||
|
||||
class RobotConfigurationBuilder {
|
||||
public:
|
||||
RobotConfigurationBuilder() : builder_(1024) {
|
||||
}
|
||||
|
||||
SerializedMessage
|
||||
build_robot_configuration(const std::vector<ModuleInstance> &modules,
|
||||
const std::vector<ModuleConnectionInstance> &connections);
|
||||
|
||||
static const Frontend::RobotConfiguration *
|
||||
parse_robot_configuration(const std::uint8_t *buffer);
|
||||
|
||||
private:
|
||||
flatbuffers::FlatBufferBuilder builder_;
|
||||
};
|
||||
} // namespace Flatbuffers
|
||||
|
||||
#endif // ROBOTCONFIGURATIONBUILDER_H
|
||||
54
include/flatbuffers/SensorMessageBuilder.h
Normal file
54
include/flatbuffers/SensorMessageBuilder.h
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-06-30.
|
||||
//
|
||||
|
||||
#ifndef SENSORMESSAGEBUILDER_H
|
||||
#define SENSORMESSAGEBUILDER_H
|
||||
|
||||
#include <variant>
|
||||
|
||||
#include "flatbuffers_generated/SensorMessage_generated.h"
|
||||
|
||||
namespace Flatbuffers {
|
||||
|
||||
struct target_angle {
|
||||
int16_t angle;
|
||||
};
|
||||
|
||||
struct current_angle {
|
||||
int16_t angle;
|
||||
};
|
||||
|
||||
typedef std::variant<target_angle, current_angle> sensor_value;
|
||||
|
||||
class SensorMessageBuilder {
|
||||
public:
|
||||
SensorMessageBuilder() : builder_(1024) {
|
||||
}
|
||||
|
||||
static const Messaging::SensorMessage *parse_sensor_message(const std::uint8_t *buffer);
|
||||
|
||||
template <typename T>
|
||||
static std::optional<sensor_value> build_sensor_value(Messaging::SensorValue type, T value) {
|
||||
switch (type) {
|
||||
case Messaging::SensorValue_TargetAngle: {
|
||||
const Messaging::TargetAngle *target =
|
||||
static_cast<const Messaging::TargetAngle *>(value);
|
||||
return target_angle{target->value()};
|
||||
}
|
||||
case Messaging::SensorValue_CurrentAngle: {
|
||||
const Messaging::CurrentAngle *current =
|
||||
static_cast<const Messaging::CurrentAngle *>(value);
|
||||
return current_angle{current->value()};
|
||||
}
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
flatbuffers::FlatBufferBuilder builder_;
|
||||
};
|
||||
} // namespace Flatbuffers
|
||||
|
||||
#endif // TOPOLOGYMESSAGEBUILDER_H
|
||||
17
include/flatbuffers/SerializedMessage.h
Normal file
17
include/flatbuffers/SerializedMessage.h
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-07-05.
|
||||
//
|
||||
|
||||
#ifndef SERIALIZEDMESSAGE_H
|
||||
#define SERIALIZEDMESSAGE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace Flatbuffers {
|
||||
struct SerializedMessage {
|
||||
void *data;
|
||||
size_t size;
|
||||
};
|
||||
} // namespace Flatbuffers
|
||||
|
||||
#endif // SERIALIZEDMESSAGE_H
|
||||
32
include/flatbuffers/TopologyMessageBuilder.h
Normal file
32
include/flatbuffers/TopologyMessageBuilder.h
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-06-30.
|
||||
//
|
||||
|
||||
#ifndef TOPOLOGYMESSAGEBUILDER_H
|
||||
#define TOPOLOGYMESSAGEBUILDER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "SerializedMessage.h"
|
||||
#include "flatbuffers_generated/TopologyMessage_generated.h"
|
||||
|
||||
namespace Flatbuffers {
|
||||
class TopologyMessageBuilder {
|
||||
public:
|
||||
TopologyMessageBuilder() : builder_(1024) {
|
||||
}
|
||||
|
||||
SerializedMessage build_topology_message(uint8_t module_id, ModuleType module_type,
|
||||
const std::vector<uint8_t> &channel_to_module,
|
||||
const std::vector<int8_t> &orientation_to_module);
|
||||
|
||||
static const Messaging::TopologyMessage *parse_topology_message(const uint8_t *buffer);
|
||||
|
||||
static bool is_valid_topology_message(const uint8_t *buffer, size_t size);
|
||||
|
||||
private:
|
||||
flatbuffers::FlatBufferBuilder builder_;
|
||||
};
|
||||
} // namespace Flatbuffers
|
||||
|
||||
#endif // TOPOLOGYMESSAGEBUILDER_H
|
||||
@@ -0,0 +1,86 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_ANGLECONTROLMESSAGE_MESSAGING_H_
|
||||
#define FLATBUFFERS_GENERATED_ANGLECONTROLMESSAGE_MESSAGING_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
// static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
// FLATBUFFERS_VERSION_MINOR == 2 &&
|
||||
// FLATBUFFERS_VERSION_REVISION == 10,
|
||||
//"Non-compatible flatbuffers version included");
|
||||
|
||||
namespace Messaging {
|
||||
|
||||
struct AngleControlMessage;
|
||||
struct AngleControlMessageBuilder;
|
||||
|
||||
struct AngleControlMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef AngleControlMessageBuilder Builder;
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_ANGLE = 4 };
|
||||
int16_t angle() const {
|
||||
return GetField<int16_t>(VT_ANGLE, 0);
|
||||
}
|
||||
bool Verify(::flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) && VerifyField<int16_t>(verifier, VT_ANGLE, 2) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct AngleControlMessageBuilder {
|
||||
typedef AngleControlMessage Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_angle(int16_t angle) {
|
||||
fbb_.AddElement<int16_t>(AngleControlMessage::VT_ANGLE, angle, 0);
|
||||
}
|
||||
explicit AngleControlMessageBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<AngleControlMessage> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<AngleControlMessage>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<AngleControlMessage>
|
||||
CreateAngleControlMessage(::flatbuffers::FlatBufferBuilder &_fbb, int16_t angle = 0) {
|
||||
AngleControlMessageBuilder builder_(_fbb);
|
||||
builder_.add_angle(angle);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline const Messaging::AngleControlMessage *GetAngleControlMessage(const void *buf) {
|
||||
return ::flatbuffers::GetRoot<Messaging::AngleControlMessage>(buf);
|
||||
}
|
||||
|
||||
inline const Messaging::AngleControlMessage *GetSizePrefixedAngleControlMessage(const void *buf) {
|
||||
return ::flatbuffers::GetSizePrefixedRoot<Messaging::AngleControlMessage>(buf);
|
||||
}
|
||||
|
||||
inline bool VerifyAngleControlMessageBuffer(::flatbuffers::Verifier &verifier) {
|
||||
return verifier.VerifyBuffer<Messaging::AngleControlMessage>(nullptr);
|
||||
}
|
||||
|
||||
inline bool VerifySizePrefixedAngleControlMessageBuffer(::flatbuffers::Verifier &verifier) {
|
||||
return verifier.VerifySizePrefixedBuffer<Messaging::AngleControlMessage>(nullptr);
|
||||
}
|
||||
|
||||
inline void
|
||||
FinishAngleControlMessageBuffer(::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Messaging::AngleControlMessage> root) {
|
||||
fbb.Finish(root);
|
||||
}
|
||||
|
||||
inline void FinishSizePrefixedAngleControlMessageBuffer(
|
||||
::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Messaging::AngleControlMessage> root) {
|
||||
fbb.FinishSizePrefixed(root);
|
||||
}
|
||||
|
||||
} // namespace Messaging
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_ANGLECONTROLMESSAGE_MESSAGING_H_
|
||||
201
include/flatbuffers_generated/RobotConfiguration_generated.h
Normal file
201
include/flatbuffers_generated/RobotConfiguration_generated.h
Normal file
@@ -0,0 +1,201 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_ROBOTCONFIGURATION_FRONTEND_H_
|
||||
#define FLATBUFFERS_GENERATED_ROBOTCONFIGURATION_FRONTEND_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
// static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
// FLATBUFFERS_VERSION_MINOR == 2 &&
|
||||
// FLATBUFFERS_VERSION_REVISION == 10,
|
||||
// "Non-compatible flatbuffers version included");
|
||||
|
||||
#include "RobotModule_generated.h"
|
||||
|
||||
namespace Frontend {
|
||||
|
||||
struct ModuleConnection;
|
||||
struct ModuleConnectionBuilder;
|
||||
|
||||
struct RobotConfiguration;
|
||||
struct RobotConfigurationBuilder;
|
||||
|
||||
struct ModuleConnection FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef ModuleConnectionBuilder Builder;
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_FROM_MODULE_ID = 4,
|
||||
VT_TO_MODULE_ID = 6,
|
||||
VT_FROM_SOCKET = 8,
|
||||
VT_TO_SOCKET = 10,
|
||||
VT_ORIENTATION = 12
|
||||
};
|
||||
uint8_t from_module_id() const {
|
||||
return GetField<uint8_t>(VT_FROM_MODULE_ID, 0);
|
||||
}
|
||||
uint8_t to_module_id() const {
|
||||
return GetField<uint8_t>(VT_TO_MODULE_ID, 0);
|
||||
}
|
||||
uint8_t from_socket() const {
|
||||
return GetField<uint8_t>(VT_FROM_SOCKET, 0);
|
||||
}
|
||||
uint8_t to_socket() const {
|
||||
return GetField<uint8_t>(VT_TO_SOCKET, 0);
|
||||
}
|
||||
Orientation orientation() const {
|
||||
return static_cast<Orientation>(GetField<int8_t>(VT_ORIENTATION, 0));
|
||||
}
|
||||
bool Verify(::flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) && VerifyField<uint8_t>(verifier, VT_FROM_MODULE_ID, 1) &&
|
||||
VerifyField<uint8_t>(verifier, VT_TO_MODULE_ID, 1) &&
|
||||
VerifyField<uint8_t>(verifier, VT_FROM_SOCKET, 1) &&
|
||||
VerifyField<uint8_t>(verifier, VT_TO_SOCKET, 1) &&
|
||||
VerifyField<int8_t>(verifier, VT_ORIENTATION, 1) && verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct ModuleConnectionBuilder {
|
||||
typedef ModuleConnection Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_from_module_id(uint8_t from_module_id) {
|
||||
fbb_.AddElement<uint8_t>(ModuleConnection::VT_FROM_MODULE_ID, from_module_id, 0);
|
||||
}
|
||||
void add_to_module_id(uint8_t to_module_id) {
|
||||
fbb_.AddElement<uint8_t>(ModuleConnection::VT_TO_MODULE_ID, to_module_id, 0);
|
||||
}
|
||||
void add_from_socket(uint8_t from_socket) {
|
||||
fbb_.AddElement<uint8_t>(ModuleConnection::VT_FROM_SOCKET, from_socket, 0);
|
||||
}
|
||||
void add_to_socket(uint8_t to_socket) {
|
||||
fbb_.AddElement<uint8_t>(ModuleConnection::VT_TO_SOCKET, to_socket, 0);
|
||||
}
|
||||
void add_orientation(Orientation orientation) {
|
||||
fbb_.AddElement<int8_t>(ModuleConnection::VT_ORIENTATION, static_cast<int8_t>(orientation),
|
||||
0);
|
||||
}
|
||||
explicit ModuleConnectionBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<ModuleConnection> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<ModuleConnection>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<ModuleConnection>
|
||||
CreateModuleConnection(::flatbuffers::FlatBufferBuilder &_fbb, uint8_t from_module_id = 0,
|
||||
uint8_t to_module_id = 0, uint8_t from_socket = 0, uint8_t to_socket = 0,
|
||||
Orientation orientation = Orientation_Deg0) {
|
||||
ModuleConnectionBuilder builder_(_fbb);
|
||||
builder_.add_orientation(orientation);
|
||||
builder_.add_to_socket(to_socket);
|
||||
builder_.add_from_socket(from_socket);
|
||||
builder_.add_to_module_id(to_module_id);
|
||||
builder_.add_from_module_id(from_module_id);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct RobotConfiguration FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef RobotConfigurationBuilder Builder;
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_MODULES = 4,
|
||||
VT_CONNECTIONS = 6
|
||||
};
|
||||
const ::flatbuffers::Vector<::flatbuffers::Offset<RobotModule>> *modules() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<RobotModule>> *>(
|
||||
VT_MODULES);
|
||||
}
|
||||
const ::flatbuffers::Vector<::flatbuffers::Offset<Frontend::ModuleConnection>> *
|
||||
connections() const {
|
||||
return GetPointer<
|
||||
const ::flatbuffers::Vector<::flatbuffers::Offset<Frontend::ModuleConnection>> *>(
|
||||
VT_CONNECTIONS);
|
||||
}
|
||||
bool Verify(::flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_MODULES) &&
|
||||
verifier.VerifyVector(modules()) && verifier.VerifyVectorOfTables(modules()) &&
|
||||
VerifyOffset(verifier, VT_CONNECTIONS) && verifier.VerifyVector(connections()) &&
|
||||
verifier.VerifyVectorOfTables(connections()) && verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct RobotConfigurationBuilder {
|
||||
typedef RobotConfiguration Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_modules(
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<RobotModule>>> modules) {
|
||||
fbb_.AddOffset(RobotConfiguration::VT_MODULES, modules);
|
||||
}
|
||||
void add_connections(::flatbuffers::Offset<
|
||||
::flatbuffers::Vector<::flatbuffers::Offset<Frontend::ModuleConnection>>>
|
||||
connections) {
|
||||
fbb_.AddOffset(RobotConfiguration::VT_CONNECTIONS, connections);
|
||||
}
|
||||
explicit RobotConfigurationBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<RobotConfiguration> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<RobotConfiguration>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<RobotConfiguration> CreateRobotConfiguration(
|
||||
::flatbuffers::FlatBufferBuilder &_fbb,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<RobotModule>>> modules = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<Frontend::ModuleConnection>>>
|
||||
connections = 0) {
|
||||
RobotConfigurationBuilder builder_(_fbb);
|
||||
builder_.add_connections(connections);
|
||||
builder_.add_modules(modules);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<RobotConfiguration> CreateRobotConfigurationDirect(
|
||||
::flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const std::vector<::flatbuffers::Offset<RobotModule>> *modules = nullptr,
|
||||
const std::vector<::flatbuffers::Offset<Frontend::ModuleConnection>> *connections = nullptr) {
|
||||
auto modules__ = modules ? _fbb.CreateVector<::flatbuffers::Offset<RobotModule>>(*modules) : 0;
|
||||
auto connections__ =
|
||||
connections
|
||||
? _fbb.CreateVector<::flatbuffers::Offset<Frontend::ModuleConnection>>(*connections)
|
||||
: 0;
|
||||
return Frontend::CreateRobotConfiguration(_fbb, modules__, connections__);
|
||||
}
|
||||
|
||||
inline const Frontend::RobotConfiguration *GetRobotConfiguration(const void *buf) {
|
||||
return ::flatbuffers::GetRoot<Frontend::RobotConfiguration>(buf);
|
||||
}
|
||||
|
||||
inline const Frontend::RobotConfiguration *GetSizePrefixedRobotConfiguration(const void *buf) {
|
||||
return ::flatbuffers::GetSizePrefixedRoot<Frontend::RobotConfiguration>(buf);
|
||||
}
|
||||
|
||||
inline bool VerifyRobotConfigurationBuffer(::flatbuffers::Verifier &verifier) {
|
||||
return verifier.VerifyBuffer<Frontend::RobotConfiguration>(nullptr);
|
||||
}
|
||||
|
||||
inline bool VerifySizePrefixedRobotConfigurationBuffer(::flatbuffers::Verifier &verifier) {
|
||||
return verifier.VerifySizePrefixedBuffer<Frontend::RobotConfiguration>(nullptr);
|
||||
}
|
||||
|
||||
inline void
|
||||
FinishRobotConfigurationBuffer(::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Frontend::RobotConfiguration> root) {
|
||||
fbb.Finish(root);
|
||||
}
|
||||
|
||||
inline void FinishSizePrefixedRobotConfigurationBuffer(
|
||||
::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Frontend::RobotConfiguration> root) {
|
||||
fbb.FinishSizePrefixed(root);
|
||||
}
|
||||
|
||||
} // namespace Frontend
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_ROBOTCONFIGURATION_FRONTEND_H_
|
||||
288
include/flatbuffers_generated/RobotModule_generated.h
Normal file
288
include/flatbuffers_generated/RobotModule_generated.h
Normal file
@@ -0,0 +1,288 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_ROBOTMODULE_H_
|
||||
#define FLATBUFFERS_GENERATED_ROBOTMODULE_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
// static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
// FLATBUFFERS_VERSION_MINOR == 2 &&
|
||||
// FLATBUFFERS_VERSION_REVISION == 10,
|
||||
// "Non-compatible flatbuffers version included");
|
||||
|
||||
struct MotorState;
|
||||
struct MotorStateBuilder;
|
||||
|
||||
struct RobotModule;
|
||||
struct RobotModuleBuilder;
|
||||
|
||||
enum ModuleType : int8_t {
|
||||
ModuleType_SPLITTER = 0,
|
||||
ModuleType_SERVO_1 = 1,
|
||||
ModuleType_DC_MOTOR = 2,
|
||||
ModuleType_BATTERY = 3,
|
||||
ModuleType_SERVO_2 = 4,
|
||||
ModuleType_MIN = ModuleType_SPLITTER,
|
||||
ModuleType_MAX = ModuleType_SERVO_2
|
||||
};
|
||||
|
||||
inline const ModuleType (&EnumValuesModuleType())[5] {
|
||||
static const ModuleType values[] = {ModuleType_SPLITTER, ModuleType_SERVO_1,
|
||||
ModuleType_DC_MOTOR, ModuleType_BATTERY,
|
||||
ModuleType_SERVO_2};
|
||||
return values;
|
||||
}
|
||||
|
||||
inline const char *const *EnumNamesModuleType() {
|
||||
static const char *const names[6] = {"SPLITTER", "SERVO_1", "DC_MOTOR",
|
||||
"BATTERY", "SERVO_2", nullptr};
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameModuleType(ModuleType e) {
|
||||
if (::flatbuffers::IsOutRange(e, ModuleType_SPLITTER, ModuleType_SERVO_2))
|
||||
return "";
|
||||
const size_t index = static_cast<size_t>(e);
|
||||
return EnumNamesModuleType()[index];
|
||||
}
|
||||
|
||||
enum Orientation : int8_t {
|
||||
Orientation_Deg0 = 0,
|
||||
Orientation_Deg90 = 1,
|
||||
Orientation_Deg180 = 2,
|
||||
Orientation_Deg270 = 3,
|
||||
Orientation_MIN = Orientation_Deg0,
|
||||
Orientation_MAX = Orientation_Deg270
|
||||
};
|
||||
|
||||
inline const Orientation (&EnumValuesOrientation())[4] {
|
||||
static const Orientation values[] = {Orientation_Deg0, Orientation_Deg90, Orientation_Deg180,
|
||||
Orientation_Deg270};
|
||||
return values;
|
||||
}
|
||||
|
||||
inline const char *const *EnumNamesOrientation() {
|
||||
static const char *const names[5] = {"Deg0", "Deg90", "Deg180", "Deg270", nullptr};
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameOrientation(Orientation e) {
|
||||
if (::flatbuffers::IsOutRange(e, Orientation_Deg0, Orientation_Deg270))
|
||||
return "";
|
||||
const size_t index = static_cast<size_t>(e);
|
||||
return EnumNamesOrientation()[index];
|
||||
}
|
||||
|
||||
enum ModuleState : uint8_t {
|
||||
ModuleState_NONE = 0,
|
||||
ModuleState_MotorState = 1,
|
||||
ModuleState_MIN = ModuleState_NONE,
|
||||
ModuleState_MAX = ModuleState_MotorState
|
||||
};
|
||||
|
||||
inline const ModuleState (&EnumValuesModuleState())[2] {
|
||||
static const ModuleState values[] = {ModuleState_NONE, ModuleState_MotorState};
|
||||
return values;
|
||||
}
|
||||
|
||||
inline const char *const *EnumNamesModuleState() {
|
||||
static const char *const names[3] = {"NONE", "MotorState", nullptr};
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameModuleState(ModuleState e) {
|
||||
if (::flatbuffers::IsOutRange(e, ModuleState_NONE, ModuleState_MotorState))
|
||||
return "";
|
||||
const size_t index = static_cast<size_t>(e);
|
||||
return EnumNamesModuleState()[index];
|
||||
}
|
||||
|
||||
template <typename T> struct ModuleStateTraits {
|
||||
static const ModuleState enum_value = ModuleState_NONE;
|
||||
};
|
||||
|
||||
template <> struct ModuleStateTraits<MotorState> {
|
||||
static const ModuleState enum_value = ModuleState_MotorState;
|
||||
};
|
||||
|
||||
bool VerifyModuleState(::flatbuffers::Verifier &verifier, const void *obj, ModuleState type);
|
||||
bool VerifyModuleStateVector(::flatbuffers::Verifier &verifier,
|
||||
const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values,
|
||||
const ::flatbuffers::Vector<uint8_t> *types);
|
||||
|
||||
struct MotorState FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef MotorStateBuilder Builder;
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_ANGLE = 4 };
|
||||
int32_t angle() const {
|
||||
return GetField<int32_t>(VT_ANGLE, 0);
|
||||
}
|
||||
bool Verify(::flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) && VerifyField<int32_t>(verifier, VT_ANGLE, 4) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct MotorStateBuilder {
|
||||
typedef MotorState Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_angle(int32_t angle) {
|
||||
fbb_.AddElement<int32_t>(MotorState::VT_ANGLE, angle, 0);
|
||||
}
|
||||
explicit MotorStateBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<MotorState> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<MotorState>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<MotorState> CreateMotorState(::flatbuffers::FlatBufferBuilder &_fbb,
|
||||
int32_t angle = 0) {
|
||||
MotorStateBuilder builder_(_fbb);
|
||||
builder_.add_angle(angle);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct RobotModule FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef RobotModuleBuilder Builder;
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_ID = 4,
|
||||
VT_MODULE_TYPE = 6,
|
||||
VT_CONFIGURATION_TYPE = 8,
|
||||
VT_CONFIGURATION = 10
|
||||
};
|
||||
uint8_t id() const {
|
||||
return GetField<uint8_t>(VT_ID, 0);
|
||||
}
|
||||
ModuleType module_type() const {
|
||||
return static_cast<ModuleType>(GetField<int8_t>(VT_MODULE_TYPE, 0));
|
||||
}
|
||||
ModuleState configuration_type() const {
|
||||
return static_cast<ModuleState>(GetField<uint8_t>(VT_CONFIGURATION_TYPE, 0));
|
||||
}
|
||||
const void *configuration() const {
|
||||
return GetPointer<const void *>(VT_CONFIGURATION);
|
||||
}
|
||||
template <typename T> const T *configuration_as() const;
|
||||
const MotorState *configuration_as_MotorState() const {
|
||||
return configuration_type() == ModuleState_MotorState
|
||||
? static_cast<const MotorState *>(configuration())
|
||||
: nullptr;
|
||||
}
|
||||
bool Verify(::flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) && VerifyField<uint8_t>(verifier, VT_ID, 1) &&
|
||||
VerifyField<int8_t>(verifier, VT_MODULE_TYPE, 1) &&
|
||||
VerifyField<uint8_t>(verifier, VT_CONFIGURATION_TYPE, 1) &&
|
||||
VerifyOffset(verifier, VT_CONFIGURATION) &&
|
||||
VerifyModuleState(verifier, configuration(), configuration_type()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
template <> inline const MotorState *RobotModule::configuration_as<MotorState>() const {
|
||||
return configuration_as_MotorState();
|
||||
}
|
||||
|
||||
struct RobotModuleBuilder {
|
||||
typedef RobotModule Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_id(uint8_t id) {
|
||||
fbb_.AddElement<uint8_t>(RobotModule::VT_ID, id, 0);
|
||||
}
|
||||
void add_module_type(ModuleType module_type) {
|
||||
fbb_.AddElement<int8_t>(RobotModule::VT_MODULE_TYPE, static_cast<int8_t>(module_type), 0);
|
||||
}
|
||||
void add_configuration_type(ModuleState configuration_type) {
|
||||
fbb_.AddElement<uint8_t>(RobotModule::VT_CONFIGURATION_TYPE,
|
||||
static_cast<uint8_t>(configuration_type), 0);
|
||||
}
|
||||
void add_configuration(::flatbuffers::Offset<void> configuration) {
|
||||
fbb_.AddOffset(RobotModule::VT_CONFIGURATION, configuration);
|
||||
}
|
||||
explicit RobotModuleBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<RobotModule> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<RobotModule>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<RobotModule>
|
||||
CreateRobotModule(::flatbuffers::FlatBufferBuilder &_fbb, uint8_t id = 0,
|
||||
ModuleType module_type = ModuleType_SPLITTER,
|
||||
ModuleState configuration_type = ModuleState_NONE,
|
||||
::flatbuffers::Offset<void> configuration = 0) {
|
||||
RobotModuleBuilder builder_(_fbb);
|
||||
builder_.add_configuration(configuration);
|
||||
builder_.add_configuration_type(configuration_type);
|
||||
builder_.add_module_type(module_type);
|
||||
builder_.add_id(id);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline bool VerifyModuleState(::flatbuffers::Verifier &verifier, const void *obj,
|
||||
ModuleState type) {
|
||||
switch (type) {
|
||||
case ModuleState_NONE: {
|
||||
return true;
|
||||
}
|
||||
case ModuleState_MotorState: {
|
||||
auto ptr = reinterpret_cast<const MotorState *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool
|
||||
VerifyModuleStateVector(::flatbuffers::Verifier &verifier,
|
||||
const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values,
|
||||
const ::flatbuffers::Vector<uint8_t> *types) {
|
||||
if (!values || !types)
|
||||
return !values && !types;
|
||||
if (values->size() != types->size())
|
||||
return false;
|
||||
for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
|
||||
if (!VerifyModuleState(verifier, values->Get(i), types->GetEnum<ModuleState>(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const RobotModule *GetRobotModule(const void *buf) {
|
||||
return ::flatbuffers::GetRoot<RobotModule>(buf);
|
||||
}
|
||||
|
||||
inline const RobotModule *GetSizePrefixedRobotModule(const void *buf) {
|
||||
return ::flatbuffers::GetSizePrefixedRoot<RobotModule>(buf);
|
||||
}
|
||||
|
||||
inline bool VerifyRobotModuleBuffer(::flatbuffers::Verifier &verifier) {
|
||||
return verifier.VerifyBuffer<RobotModule>(nullptr);
|
||||
}
|
||||
|
||||
inline bool VerifySizePrefixedRobotModuleBuffer(::flatbuffers::Verifier &verifier) {
|
||||
return verifier.VerifySizePrefixedBuffer<RobotModule>(nullptr);
|
||||
}
|
||||
|
||||
inline void FinishRobotModuleBuffer(::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<RobotModule> root) {
|
||||
fbb.Finish(root);
|
||||
}
|
||||
|
||||
inline void FinishSizePrefixedRobotModuleBuffer(::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<RobotModule> root) {
|
||||
fbb.FinishSizePrefixed(root);
|
||||
}
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_ROBOTMODULE_H_
|
||||
265
include/flatbuffers_generated/SensorMessage_generated.h
Normal file
265
include/flatbuffers_generated/SensorMessage_generated.h
Normal file
@@ -0,0 +1,265 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_
|
||||
#define FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
// static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
// FLATBUFFERS_VERSION_MINOR == 2 &&
|
||||
// FLATBUFFERS_VERSION_REVISION == 10,
|
||||
// "Non-compatible flatbuffers version included");
|
||||
|
||||
namespace Messaging {
|
||||
|
||||
struct TargetAngle;
|
||||
struct TargetAngleBuilder;
|
||||
|
||||
struct CurrentAngle;
|
||||
struct CurrentAngleBuilder;
|
||||
|
||||
struct SensorMessage;
|
||||
struct SensorMessageBuilder;
|
||||
|
||||
enum SensorValue : uint8_t {
|
||||
SensorValue_NONE = 0,
|
||||
SensorValue_TargetAngle = 1,
|
||||
SensorValue_CurrentAngle = 2,
|
||||
SensorValue_MIN = SensorValue_NONE,
|
||||
SensorValue_MAX = SensorValue_CurrentAngle
|
||||
};
|
||||
|
||||
inline const SensorValue (&EnumValuesSensorValue())[3] {
|
||||
static const SensorValue values[] = {SensorValue_NONE, SensorValue_TargetAngle,
|
||||
SensorValue_CurrentAngle};
|
||||
return values;
|
||||
}
|
||||
|
||||
inline const char *const *EnumNamesSensorValue() {
|
||||
static const char *const names[4] = {"NONE", "TargetAngle", "CurrentAngle", nullptr};
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameSensorValue(SensorValue e) {
|
||||
if (::flatbuffers::IsOutRange(e, SensorValue_NONE, SensorValue_CurrentAngle))
|
||||
return "";
|
||||
const size_t index = static_cast<size_t>(e);
|
||||
return EnumNamesSensorValue()[index];
|
||||
}
|
||||
|
||||
template <typename T> struct SensorValueTraits {
|
||||
static const SensorValue enum_value = SensorValue_NONE;
|
||||
};
|
||||
|
||||
template <> struct SensorValueTraits<Messaging::TargetAngle> {
|
||||
static const SensorValue enum_value = SensorValue_TargetAngle;
|
||||
};
|
||||
|
||||
template <> struct SensorValueTraits<Messaging::CurrentAngle> {
|
||||
static const SensorValue enum_value = SensorValue_CurrentAngle;
|
||||
};
|
||||
|
||||
bool VerifySensorValue(::flatbuffers::Verifier &verifier, const void *obj, SensorValue type);
|
||||
bool VerifySensorValueVector(::flatbuffers::Verifier &verifier,
|
||||
const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values,
|
||||
const ::flatbuffers::Vector<uint8_t> *types);
|
||||
|
||||
struct TargetAngle FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef TargetAngleBuilder Builder;
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_VALUE = 4 };
|
||||
int16_t value() const {
|
||||
return GetField<int16_t>(VT_VALUE, 0);
|
||||
}
|
||||
bool Verify(::flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) && VerifyField<int16_t>(verifier, VT_VALUE, 2) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct TargetAngleBuilder {
|
||||
typedef TargetAngle Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_value(int16_t value) {
|
||||
fbb_.AddElement<int16_t>(TargetAngle::VT_VALUE, value, 0);
|
||||
}
|
||||
explicit TargetAngleBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<TargetAngle> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<TargetAngle>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<TargetAngle> CreateTargetAngle(::flatbuffers::FlatBufferBuilder &_fbb,
|
||||
int16_t value = 0) {
|
||||
TargetAngleBuilder builder_(_fbb);
|
||||
builder_.add_value(value);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct CurrentAngle FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef CurrentAngleBuilder Builder;
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_VALUE = 4 };
|
||||
int16_t value() const {
|
||||
return GetField<int16_t>(VT_VALUE, 0);
|
||||
}
|
||||
bool Verify(::flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) && VerifyField<int16_t>(verifier, VT_VALUE, 2) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct CurrentAngleBuilder {
|
||||
typedef CurrentAngle Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_value(int16_t value) {
|
||||
fbb_.AddElement<int16_t>(CurrentAngle::VT_VALUE, value, 0);
|
||||
}
|
||||
explicit CurrentAngleBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<CurrentAngle> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<CurrentAngle>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<CurrentAngle>
|
||||
CreateCurrentAngle(::flatbuffers::FlatBufferBuilder &_fbb, int16_t value = 0) {
|
||||
CurrentAngleBuilder builder_(_fbb);
|
||||
builder_.add_value(value);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
struct SensorMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef SensorMessageBuilder Builder;
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_VALUES_TYPE = 4,
|
||||
VT_VALUES = 6
|
||||
};
|
||||
const ::flatbuffers::Vector<uint8_t> *values_type() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_VALUES_TYPE);
|
||||
}
|
||||
const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *>(VT_VALUES);
|
||||
}
|
||||
bool Verify(::flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_VALUES_TYPE) &&
|
||||
verifier.VerifyVector(values_type()) && VerifyOffset(verifier, VT_VALUES) &&
|
||||
verifier.VerifyVector(values()) &&
|
||||
VerifySensorValueVector(verifier, values(), values_type()) && verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct SensorMessageBuilder {
|
||||
typedef SensorMessage Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_values_type(::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> values_type) {
|
||||
fbb_.AddOffset(SensorMessage::VT_VALUES_TYPE, values_type);
|
||||
}
|
||||
void
|
||||
add_values(::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<void>>> values) {
|
||||
fbb_.AddOffset(SensorMessage::VT_VALUES, values);
|
||||
}
|
||||
explicit SensorMessageBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<SensorMessage> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<SensorMessage>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<SensorMessage> CreateSensorMessage(
|
||||
::flatbuffers::FlatBufferBuilder &_fbb,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> values_type = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<void>>> values = 0) {
|
||||
SensorMessageBuilder builder_(_fbb);
|
||||
builder_.add_values(values);
|
||||
builder_.add_values_type(values_type);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<SensorMessage>
|
||||
CreateSensorMessageDirect(::flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const std::vector<uint8_t> *values_type = nullptr,
|
||||
const std::vector<::flatbuffers::Offset<void>> *values = nullptr) {
|
||||
auto values_type__ = values_type ? _fbb.CreateVector<uint8_t>(*values_type) : 0;
|
||||
auto values__ = values ? _fbb.CreateVector<::flatbuffers::Offset<void>>(*values) : 0;
|
||||
return Messaging::CreateSensorMessage(_fbb, values_type__, values__);
|
||||
}
|
||||
|
||||
inline bool VerifySensorValue(::flatbuffers::Verifier &verifier, const void *obj,
|
||||
SensorValue type) {
|
||||
switch (type) {
|
||||
case SensorValue_NONE: {
|
||||
return true;
|
||||
}
|
||||
case SensorValue_TargetAngle: {
|
||||
auto ptr = reinterpret_cast<const Messaging::TargetAngle *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
case SensorValue_CurrentAngle: {
|
||||
auto ptr = reinterpret_cast<const Messaging::CurrentAngle *>(obj);
|
||||
return verifier.VerifyTable(ptr);
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool
|
||||
VerifySensorValueVector(::flatbuffers::Verifier &verifier,
|
||||
const ::flatbuffers::Vector<::flatbuffers::Offset<void>> *values,
|
||||
const ::flatbuffers::Vector<uint8_t> *types) {
|
||||
if (!values || !types)
|
||||
return !values && !types;
|
||||
if (values->size() != types->size())
|
||||
return false;
|
||||
for (::flatbuffers::uoffset_t i = 0; i < values->size(); ++i) {
|
||||
if (!VerifySensorValue(verifier, values->Get(i), types->GetEnum<SensorValue>(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline const Messaging::SensorMessage *GetSensorMessage(const void *buf) {
|
||||
return ::flatbuffers::GetRoot<Messaging::SensorMessage>(buf);
|
||||
}
|
||||
|
||||
inline const Messaging::SensorMessage *GetSizePrefixedSensorMessage(const void *buf) {
|
||||
return ::flatbuffers::GetSizePrefixedRoot<Messaging::SensorMessage>(buf);
|
||||
}
|
||||
|
||||
inline bool VerifySensorMessageBuffer(::flatbuffers::Verifier &verifier) {
|
||||
return verifier.VerifyBuffer<Messaging::SensorMessage>(nullptr);
|
||||
}
|
||||
|
||||
inline bool VerifySizePrefixedSensorMessageBuffer(::flatbuffers::Verifier &verifier) {
|
||||
return verifier.VerifySizePrefixedBuffer<Messaging::SensorMessage>(nullptr);
|
||||
}
|
||||
|
||||
inline void FinishSensorMessageBuffer(::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Messaging::SensorMessage> root) {
|
||||
fbb.Finish(root);
|
||||
}
|
||||
|
||||
inline void
|
||||
FinishSizePrefixedSensorMessageBuffer(::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Messaging::SensorMessage> root) {
|
||||
fbb.FinishSizePrefixed(root);
|
||||
}
|
||||
|
||||
} // namespace Messaging
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_SENSORMESSAGE_MESSAGING_H_
|
||||
203
include/flatbuffers_generated/TopologyMessage_generated.h
Normal file
203
include/flatbuffers_generated/TopologyMessage_generated.h
Normal file
@@ -0,0 +1,203 @@
|
||||
// automatically generated by the FlatBuffers compiler, do not modify
|
||||
|
||||
#ifndef FLATBUFFERS_GENERATED_TOPOLOGYMESSAGE_MESSAGING_H_
|
||||
#define FLATBUFFERS_GENERATED_TOPOLOGYMESSAGE_MESSAGING_H_
|
||||
|
||||
#include "flatbuffers/flatbuffers.h"
|
||||
|
||||
// Ensure the included flatbuffers.h is the same version as when this file was
|
||||
// generated, otherwise it may not be compatible.
|
||||
// static_assert(FLATBUFFERS_VERSION_MAJOR == 25 &&
|
||||
// FLATBUFFERS_VERSION_MINOR == 2 &&
|
||||
// FLATBUFFERS_VERSION_REVISION == 10,
|
||||
// "Non-compatible flatbuffers version included");
|
||||
|
||||
#include "RobotModule_generated.h"
|
||||
|
||||
namespace Messaging {
|
||||
|
||||
struct TopologyMessage;
|
||||
struct TopologyMessageBuilder;
|
||||
|
||||
enum ConnectionType : int8_t {
|
||||
ConnectionType_DIRECT = 0,
|
||||
ConnectionType_HOP = 1,
|
||||
ConnectionType_MIN = ConnectionType_DIRECT,
|
||||
ConnectionType_MAX = ConnectionType_HOP
|
||||
};
|
||||
|
||||
inline const ConnectionType (&EnumValuesConnectionType())[2] {
|
||||
static const ConnectionType values[] = {ConnectionType_DIRECT, ConnectionType_HOP};
|
||||
return values;
|
||||
}
|
||||
|
||||
inline const char *const *EnumNamesConnectionType() {
|
||||
static const char *const names[3] = {"DIRECT", "HOP", nullptr};
|
||||
return names;
|
||||
}
|
||||
|
||||
inline const char *EnumNameConnectionType(ConnectionType e) {
|
||||
if (::flatbuffers::IsOutRange(e, ConnectionType_DIRECT, ConnectionType_HOP))
|
||||
return "";
|
||||
const size_t index = static_cast<size_t>(e);
|
||||
return EnumNamesConnectionType()[index];
|
||||
}
|
||||
|
||||
struct TopologyMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
|
||||
typedef TopologyMessageBuilder Builder;
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_MODULE_ID = 4,
|
||||
VT_MODULE_TYPE = 6,
|
||||
VT_NUM_CHANNELS = 8,
|
||||
VT_CHANNEL_TO_MODULE = 10,
|
||||
VT_CHANNEL_TO_ORIENTATION = 12,
|
||||
VT_CONNECTION = 14,
|
||||
VT_LEADER = 16,
|
||||
VT_FIRMWARE = 18
|
||||
};
|
||||
uint8_t module_id() const {
|
||||
return GetField<uint8_t>(VT_MODULE_ID, 0);
|
||||
}
|
||||
ModuleType module_type() const {
|
||||
return static_cast<ModuleType>(GetField<int8_t>(VT_MODULE_TYPE, 0));
|
||||
}
|
||||
uint8_t num_channels() const {
|
||||
return GetField<uint8_t>(VT_NUM_CHANNELS, 0);
|
||||
}
|
||||
const ::flatbuffers::Vector<uint8_t> *channel_to_module() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_CHANNEL_TO_MODULE);
|
||||
}
|
||||
const ::flatbuffers::Vector<int8_t> *channel_to_orientation() const {
|
||||
return GetPointer<const ::flatbuffers::Vector<int8_t> *>(VT_CHANNEL_TO_ORIENTATION);
|
||||
}
|
||||
Messaging::ConnectionType connection() const {
|
||||
return static_cast<Messaging::ConnectionType>(GetField<int8_t>(VT_CONNECTION, 0));
|
||||
}
|
||||
uint8_t leader() const {
|
||||
return GetField<uint8_t>(VT_LEADER, 0);
|
||||
}
|
||||
const ::flatbuffers::String *firmware() const {
|
||||
return GetPointer<const ::flatbuffers::String *>(VT_FIRMWARE);
|
||||
}
|
||||
bool Verify(::flatbuffers::Verifier &verifier) const {
|
||||
return VerifyTableStart(verifier) && VerifyField<uint8_t>(verifier, VT_MODULE_ID, 1) &&
|
||||
VerifyField<int8_t>(verifier, VT_MODULE_TYPE, 1) &&
|
||||
VerifyField<uint8_t>(verifier, VT_NUM_CHANNELS, 1) &&
|
||||
VerifyOffset(verifier, VT_CHANNEL_TO_MODULE) &&
|
||||
verifier.VerifyVector(channel_to_module()) &&
|
||||
VerifyOffset(verifier, VT_CHANNEL_TO_ORIENTATION) &&
|
||||
verifier.VerifyVector(channel_to_orientation()) &&
|
||||
VerifyField<int8_t>(verifier, VT_CONNECTION, 1) &&
|
||||
VerifyField<uint8_t>(verifier, VT_LEADER, 1) &&
|
||||
VerifyOffset(verifier, VT_FIRMWARE) && verifier.VerifyString(firmware()) &&
|
||||
verifier.EndTable();
|
||||
}
|
||||
};
|
||||
|
||||
struct TopologyMessageBuilder {
|
||||
typedef TopologyMessage Table;
|
||||
::flatbuffers::FlatBufferBuilder &fbb_;
|
||||
::flatbuffers::uoffset_t start_;
|
||||
void add_module_id(uint8_t module_id) {
|
||||
fbb_.AddElement<uint8_t>(TopologyMessage::VT_MODULE_ID, module_id, 0);
|
||||
}
|
||||
void add_module_type(ModuleType module_type) {
|
||||
fbb_.AddElement<int8_t>(TopologyMessage::VT_MODULE_TYPE, static_cast<int8_t>(module_type),
|
||||
0);
|
||||
}
|
||||
void add_num_channels(uint8_t num_channels) {
|
||||
fbb_.AddElement<uint8_t>(TopologyMessage::VT_NUM_CHANNELS, num_channels, 0);
|
||||
}
|
||||
void
|
||||
add_channel_to_module(::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> channel_to_module) {
|
||||
fbb_.AddOffset(TopologyMessage::VT_CHANNEL_TO_MODULE, channel_to_module);
|
||||
}
|
||||
void add_channel_to_orientation(
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<int8_t>> channel_to_orientation) {
|
||||
fbb_.AddOffset(TopologyMessage::VT_CHANNEL_TO_ORIENTATION, channel_to_orientation);
|
||||
}
|
||||
void add_connection(Messaging::ConnectionType connection) {
|
||||
fbb_.AddElement<int8_t>(TopologyMessage::VT_CONNECTION, static_cast<int8_t>(connection), 0);
|
||||
}
|
||||
void add_leader(uint8_t leader) {
|
||||
fbb_.AddElement<uint8_t>(TopologyMessage::VT_LEADER, leader, 0);
|
||||
}
|
||||
void add_firmware(::flatbuffers::Offset<::flatbuffers::String> firmware) {
|
||||
fbb_.AddOffset(TopologyMessage::VT_FIRMWARE, firmware);
|
||||
}
|
||||
explicit TopologyMessageBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
|
||||
start_ = fbb_.StartTable();
|
||||
}
|
||||
::flatbuffers::Offset<TopologyMessage> Finish() {
|
||||
const auto end = fbb_.EndTable(start_);
|
||||
auto o = ::flatbuffers::Offset<TopologyMessage>(end);
|
||||
return o;
|
||||
}
|
||||
};
|
||||
|
||||
inline ::flatbuffers::Offset<TopologyMessage> CreateTopologyMessage(
|
||||
::flatbuffers::FlatBufferBuilder &_fbb, uint8_t module_id = 0,
|
||||
ModuleType module_type = ModuleType_SPLITTER, uint8_t num_channels = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> channel_to_module = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::Vector<int8_t>> channel_to_orientation = 0,
|
||||
Messaging::ConnectionType connection = Messaging::ConnectionType_DIRECT, uint8_t leader = 0,
|
||||
::flatbuffers::Offset<::flatbuffers::String> firmware = 0) {
|
||||
TopologyMessageBuilder builder_(_fbb);
|
||||
builder_.add_firmware(firmware);
|
||||
builder_.add_channel_to_orientation(channel_to_orientation);
|
||||
builder_.add_channel_to_module(channel_to_module);
|
||||
builder_.add_leader(leader);
|
||||
builder_.add_connection(connection);
|
||||
builder_.add_num_channels(num_channels);
|
||||
builder_.add_module_type(module_type);
|
||||
builder_.add_module_id(module_id);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
||||
inline ::flatbuffers::Offset<TopologyMessage>
|
||||
CreateTopologyMessageDirect(::flatbuffers::FlatBufferBuilder &_fbb, uint8_t module_id = 0,
|
||||
ModuleType module_type = ModuleType_SPLITTER, uint8_t num_channels = 0,
|
||||
const std::vector<uint8_t> *channel_to_module = nullptr,
|
||||
const std::vector<int8_t> *channel_to_orientation = nullptr,
|
||||
Messaging::ConnectionType connection = Messaging::ConnectionType_DIRECT,
|
||||
uint8_t leader = 0, const char *firmware = nullptr) {
|
||||
auto channel_to_module__ =
|
||||
channel_to_module ? _fbb.CreateVector<uint8_t>(*channel_to_module) : 0;
|
||||
auto channel_to_orientation__ =
|
||||
channel_to_orientation ? _fbb.CreateVector<int8_t>(*channel_to_orientation) : 0;
|
||||
auto firmware__ = firmware ? _fbb.CreateString(firmware) : 0;
|
||||
return Messaging::CreateTopologyMessage(_fbb, module_id, module_type, num_channels,
|
||||
channel_to_module__, channel_to_orientation__,
|
||||
connection, leader, firmware__);
|
||||
}
|
||||
|
||||
inline const Messaging::TopologyMessage *GetTopologyMessage(const void *buf) {
|
||||
return ::flatbuffers::GetRoot<Messaging::TopologyMessage>(buf);
|
||||
}
|
||||
|
||||
inline const Messaging::TopologyMessage *GetSizePrefixedTopologyMessage(const void *buf) {
|
||||
return ::flatbuffers::GetSizePrefixedRoot<Messaging::TopologyMessage>(buf);
|
||||
}
|
||||
|
||||
inline bool VerifyTopologyMessageBuffer(::flatbuffers::Verifier &verifier) {
|
||||
return verifier.VerifyBuffer<Messaging::TopologyMessage>(nullptr);
|
||||
}
|
||||
|
||||
inline bool VerifySizePrefixedTopologyMessageBuffer(::flatbuffers::Verifier &verifier) {
|
||||
return verifier.VerifySizePrefixedBuffer<Messaging::TopologyMessage>(nullptr);
|
||||
}
|
||||
|
||||
inline void FinishTopologyMessageBuffer(::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Messaging::TopologyMessage> root) {
|
||||
fbb.Finish(root);
|
||||
}
|
||||
|
||||
inline void
|
||||
FinishSizePrefixedTopologyMessageBuffer(::flatbuffers::FlatBufferBuilder &fbb,
|
||||
::flatbuffers::Offset<Messaging::TopologyMessage> root) {
|
||||
fbb.FinishSizePrefixed(root);
|
||||
}
|
||||
|
||||
} // namespace Messaging
|
||||
|
||||
#endif // FLATBUFFERS_GENERATED_TOPOLOGYMESSAGE_MESSAGING_H_
|
||||
26
include/lib_c_control.h
Normal file
26
include/lib_c_control.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef CONTROL_LIBRARY_H
|
||||
#define CONTROL_LIBRARY_H
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#ifdef CONTROL_EXPORTS
|
||||
#define LIB_API __declspec(dllexport)
|
||||
#else
|
||||
#define LIB_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define LIB_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
LIB_API void init();
|
||||
LIB_API void cleanup();
|
||||
LIB_API int send_angle_control(int module_id, int angle);
|
||||
LIB_API char *get_configuration(int *size_out);
|
||||
|
||||
LIB_API bool control_sentry_init(const char *dsn, const char *environment, const char *release);
|
||||
LIB_API void control_sentry_set_app_info(const char *app_name, const char *app_version,
|
||||
const char *build_number);
|
||||
LIB_API void control_sentry_shutdown(void);
|
||||
}
|
||||
|
||||
#endif // CONTROL_LIBRARY_H
|
||||
126
include/libcontrol.h
Normal file
126
include/libcontrol.h
Normal file
@@ -0,0 +1,126 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-11-13.
|
||||
//
|
||||
|
||||
#ifndef CONTROL_LIBCONTROL_H
|
||||
#define CONTROL_LIBCONTROL_H
|
||||
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "spdlog/logger.h"
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include "Module.h"
|
||||
#include "flatbuffers/RobotConfigurationBuilder.h"
|
||||
#include "librpc.h"
|
||||
|
||||
/**
|
||||
* \brief The root class of the BotChain control library.
|
||||
*
|
||||
* This class finds and connects to BotChain modules accessible to the PC.
|
||||
* Providing an interface to control actuator modules, and get sensor readings
|
||||
* from sensor modules.
|
||||
*/
|
||||
class RobotController {
|
||||
|
||||
public:
|
||||
/**
|
||||
* \brief Creates a new RobotController.
|
||||
*
|
||||
* Each RobotController will establish unique connections to accessible
|
||||
* BotChain modules.
|
||||
*/
|
||||
RobotController()
|
||||
: m_messaging_interface(std::make_unique<MessagingInterface>()),
|
||||
m_metadata_loop(std::thread(&RobotController::metadata_loop, this)),
|
||||
m_transmit_loop(std::thread(&RobotController::transmit_loop, this)),
|
||||
m_configuration_loop(std::thread(&RobotController::configuration_loop, this)),
|
||||
m_sensor_loop(std::thread(&RobotController::sensor_loop, this)),
|
||||
m_expiry_looop(std::thread(&RobotController::expiry_loop, this)) {
|
||||
m_logger = spdlog::basic_logger_mt("default_logger", "libcontrol.log");
|
||||
spdlog::flush_on(spdlog::level::info);
|
||||
spdlog::set_default_logger(m_logger);
|
||||
}
|
||||
|
||||
~RobotController();
|
||||
|
||||
/**
|
||||
* \brief Get a list of accessible modules
|
||||
*
|
||||
* Returns a std::vector containing robotic modules. This list includes
|
||||
* modules connected directly to the PC, as well as modules connected
|
||||
* through eachother. Sensor readings can be obtained, and actuation
|
||||
* commands can be sent by calling the "as" function for the correct
|
||||
* type (can be identified through the ModuleType).
|
||||
*/
|
||||
std::vector<std::weak_ptr<Module>> getModules();
|
||||
|
||||
/**
|
||||
* \brief Get a module by ID
|
||||
*
|
||||
* Returns a std::vector containing robotic modules. This list includes
|
||||
* modules connected directly to the PC, as well as modules connected
|
||||
* through eachother. Sensor readings can be obtained, and actuation
|
||||
* commands can be sent by calling the "as" function for the correct
|
||||
* type (can be identified through the ModuleType).
|
||||
*/
|
||||
std::optional<std::weak_ptr<Module>> getModule(uint8_t device_id);
|
||||
|
||||
/**
|
||||
* \brief Get a list of all connections.
|
||||
*
|
||||
* Returns a list containing all connections between modules.
|
||||
*/
|
||||
std::vector<Flatbuffers::ModuleConnectionInstance> getConnections();
|
||||
|
||||
/**
|
||||
* \brief Get a list of accessible modules.
|
||||
*
|
||||
* Returns a list containing ID and types of each module.
|
||||
*/
|
||||
std::vector<Flatbuffers::ModuleInstance> getModuleList();
|
||||
|
||||
/**
|
||||
* \brief Reset the list of modules.
|
||||
*
|
||||
* Reset the internal list containing known modules. Note: This list is
|
||||
* automatically updated, only call this function when you need to update the
|
||||
* list faster than the internal refresh logic.
|
||||
*/
|
||||
void resetModules();
|
||||
|
||||
/**
|
||||
* \brief Poll for devices accessible to the PC.
|
||||
*
|
||||
* Manually trigger a poll for devices accessible to the PC.
|
||||
*/
|
||||
void fetchDirectlyConnectedModules(bool block);
|
||||
|
||||
private:
|
||||
std::shared_ptr<spdlog::logger> m_logger;
|
||||
std::unordered_map<uint8_t, std::shared_ptr<Module>> m_id_to_module{};
|
||||
std::unordered_map<uint8_t, std::vector<Flatbuffers::ModuleConnectionInstance>>
|
||||
m_connection_map{};
|
||||
std::shared_mutex m_module_lock{};
|
||||
std::shared_mutex m_connection_lock{};
|
||||
std::shared_ptr<MessagingInterface> m_messaging_interface;
|
||||
std::atomic<bool> m_stop_thread{false}; // todo: make sure threads stop if we
|
||||
// dont get any messages (timeouts)
|
||||
std::thread m_metadata_loop;
|
||||
std::thread m_transmit_loop;
|
||||
std::thread m_configuration_loop;
|
||||
std::thread m_sensor_loop;
|
||||
std::thread m_expiry_looop;
|
||||
|
||||
void metadata_loop();
|
||||
void transmit_loop();
|
||||
void configuration_loop();
|
||||
void sensor_loop();
|
||||
void expiry_loop();
|
||||
};
|
||||
|
||||
#endif // CONTROL_LIBCONTROL_H
|
||||
50
include/util/Event.h
Normal file
50
include/util/Event.h
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
#ifndef CONTROL_EVENT_H
|
||||
#define CONTROL_EVENT_H
|
||||
|
||||
#include <functional>
|
||||
|
||||
class Event;
|
||||
|
||||
class CallbackHandle {
|
||||
public:
|
||||
CallbackHandle(Event *evt, int id) : evt(evt), id(id) {
|
||||
}
|
||||
~CallbackHandle();
|
||||
|
||||
private:
|
||||
Event *evt;
|
||||
int id;
|
||||
};
|
||||
|
||||
class Event {
|
||||
public:
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
CallbackHandle addListener(Callback cb) {
|
||||
int id = nextId++;
|
||||
callbacks[id] = std::move(cb);
|
||||
return CallbackHandle(this, id);
|
||||
}
|
||||
|
||||
void remove(int id) {
|
||||
callbacks.erase(id);
|
||||
}
|
||||
|
||||
void fire() {
|
||||
for (auto &kv : callbacks)
|
||||
kv.second();
|
||||
}
|
||||
|
||||
private:
|
||||
friend class CallbackHandle;
|
||||
std::unordered_map<int, Callback> callbacks;
|
||||
int nextId = 0;
|
||||
};
|
||||
|
||||
CallbackHandle::~CallbackHandle() {
|
||||
if (evt)
|
||||
evt->remove(id);
|
||||
}
|
||||
|
||||
#endif // CONTROL_EVENT_H
|
||||
18
include/util/Map.h
Normal file
18
include/util/Map.h
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
#ifndef CONTROL_MAP_H
|
||||
#define CONTROL_MAP_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
template <typename K, typename V, typename H>
|
||||
std::vector<V> map_to_values(const std::unordered_map<K, V, H> &map) {
|
||||
std::vector<V> out;
|
||||
out.reserve(map.size());
|
||||
for (auto const &[key, value] : map) {
|
||||
out.push_back(value);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif // CONTROL_MAP_H
|
||||
14
include/util/PairHash.h
Normal file
14
include/util/PairHash.h
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
#ifndef CONTROL_PAIRHASH_H
|
||||
#define CONTROL_PAIRHASH_H
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
// Custom hash function for std::pair
|
||||
template <typename T> struct pair_hash {
|
||||
std::size_t operator()(const std::pair<T, T> &p) const {
|
||||
return std::hash<T>()(p.first) ^ (std::hash<T>()(p.second) << 1);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONTROL_PAIRHASH_H
|
||||
13
include/util/Variant.h
Normal file
13
include/util/Variant.h
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
#ifndef VARIANT_H
|
||||
#define VARIANT_H
|
||||
|
||||
#include <variant> // NOLINT
|
||||
|
||||
template <class... Ts> struct overloaded : Ts... {
|
||||
using Ts::operator()...;
|
||||
};
|
||||
|
||||
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
#endif // VARIANT_H
|
||||
24
src/Hub.cpp
Normal file
24
src/Hub.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "Hub.h"
|
||||
#include "flatbuffers/SensorMessageBuilder.h"
|
||||
|
||||
double Hub::get_position() {
|
||||
// no-op
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Hub::actuate(double /* position */) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
void Hub::actuate(double /* x */, double /* y */) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Hub::get_actuation_message() {
|
||||
// no-op
|
||||
return {};
|
||||
}
|
||||
|
||||
void Hub::update_sensor_data(const Flatbuffers::sensor_value & /* value */) {
|
||||
// no-op
|
||||
}
|
||||
43
src/Module.cpp
Normal file
43
src/Module.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <ranges>
|
||||
|
||||
#include "Module.h"
|
||||
|
||||
std::vector<neighbour> Module::get_neighbours() {
|
||||
return m_neighbours;
|
||||
}
|
||||
|
||||
uint8_t Module::get_device_id() {
|
||||
return m_device_id;
|
||||
}
|
||||
|
||||
ModuleType Module::get_type() {
|
||||
return m_module_type;
|
||||
}
|
||||
|
||||
Messaging::ConnectionType Module::get_connection_type() {
|
||||
return m_connection_type;
|
||||
}
|
||||
|
||||
uint8_t Module::get_leader() {
|
||||
return m_leader;
|
||||
}
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> Module::get_last_updated_time() {
|
||||
return m_last_updated;
|
||||
}
|
||||
|
||||
void Module::update_module_metadata(const Messaging::TopologyMessage &message) {
|
||||
m_module_type = message.module_type();
|
||||
m_connection_type = message.connection();
|
||||
m_leader = message.leader();
|
||||
|
||||
m_last_updated = std::chrono::system_clock::now();
|
||||
|
||||
m_neighbours.clear();
|
||||
for (auto [id, ori] :
|
||||
std::views::zip(*message.channel_to_module(), *message.channel_to_orientation())) {
|
||||
m_neighbours.emplace_back(neighbour{id, static_cast<Orientation>(ori)});
|
||||
}
|
||||
}
|
||||
28
src/ModuleFactory.cpp
Normal file
28
src/ModuleFactory.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
#include "ModuleFactory.h"
|
||||
#include "Hub.h"
|
||||
#include "actuators/BoundedPositionalActuator1D.h"
|
||||
#include "actuators/PositionalActuator1D.h"
|
||||
#include "flatbuffers_generated/RobotModule_generated.h"
|
||||
|
||||
#define SERVO1_MAX_ANGLE 180
|
||||
#define SERVO1_MIN_ANGLE 0
|
||||
#define SERVO1_DEFAULT_ANGLE 90
|
||||
|
||||
std::shared_ptr<Module>
|
||||
ModuleFactory::createModule(uint8_t device_id, ModuleType type,
|
||||
std::shared_ptr<MessagingInterface> &messaging_interface) {
|
||||
switch (type) {
|
||||
case ModuleType_SPLITTER:
|
||||
return std::make_shared<Hub>(device_id, type);
|
||||
case ModuleType_BATTERY:
|
||||
return std::make_shared<Hub>(device_id, type);
|
||||
case ModuleType_SERVO_1:
|
||||
case ModuleType_SERVO_2:
|
||||
return std::make_shared<BoundedPositionalActuator1D>(
|
||||
device_id, type, SERVO1_MAX_ANGLE, SERVO1_MIN_ANGLE, SERVO1_DEFAULT_ANGLE);
|
||||
case ModuleType_DC_MOTOR:
|
||||
return std::make_shared<PositionalActuator1D>(device_id, type);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
43
src/actuators/BoundedPositionalActuator1D.cpp
Normal file
43
src/actuators/BoundedPositionalActuator1D.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <variant>
|
||||
|
||||
#include "actuators/BoundedPositionalActuator1D.h"
|
||||
#include "flatbuffers/SensorMessageBuilder.h"
|
||||
#include "util/Variant.h"
|
||||
|
||||
double BoundedPositionalActuator1D::get_position() {
|
||||
return m_current_position;
|
||||
}
|
||||
|
||||
void BoundedPositionalActuator1D::actuate(double position) {
|
||||
if (position < m_min_value || position > m_max_value) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_target_position = position;
|
||||
}
|
||||
|
||||
void BoundedPositionalActuator1D::actuate(double x, double y) {
|
||||
}
|
||||
|
||||
std::vector<uint8_t> BoundedPositionalActuator1D::get_actuation_message() {
|
||||
std::vector<uint8_t> message{};
|
||||
|
||||
if (m_target_position == m_current_position) {
|
||||
return message;
|
||||
}
|
||||
|
||||
auto [data, size] = acm_builder->build_angle_control_message(m_target_position);
|
||||
message.resize(size);
|
||||
memcpy(message.data(), data, size);
|
||||
return message;
|
||||
}
|
||||
|
||||
void BoundedPositionalActuator1D::update_sensor_data(const Flatbuffers::sensor_value &value) {
|
||||
std::visit(overloaded{
|
||||
[this](Flatbuffers::target_angle a) { m_current_position = a.angle; },
|
||||
[this](Flatbuffers::current_angle a) { m_current_position = a.angle; },
|
||||
},
|
||||
value);
|
||||
}
|
||||
36
src/actuators/PositionalActuator1D.cpp
Normal file
36
src/actuators/PositionalActuator1D.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "actuators/PositionalActuator1D.h"
|
||||
#include "flatbuffers/SensorMessageBuilder.h"
|
||||
#include "util/Variant.h"
|
||||
#include <cstring>
|
||||
|
||||
double PositionalActuator1D::get_position() {
|
||||
return m_current_position;
|
||||
}
|
||||
|
||||
void PositionalActuator1D::actuate(double position) {
|
||||
m_target_position = position;
|
||||
}
|
||||
|
||||
void PositionalActuator1D::actuate(double /* x */, double /* y */) {
|
||||
}
|
||||
|
||||
std::vector<uint8_t> PositionalActuator1D::get_actuation_message() {
|
||||
std::vector<uint8_t> message{};
|
||||
|
||||
if (m_target_position == m_board_target_position) {
|
||||
return message;
|
||||
}
|
||||
|
||||
auto [data, size] = m_acm_builder->build_angle_control_message(m_target_position);
|
||||
message.resize(size);
|
||||
memcpy(message.data(), data, size);
|
||||
return message;
|
||||
}
|
||||
|
||||
void PositionalActuator1D::update_sensor_data(const Flatbuffers::sensor_value &value) {
|
||||
std::visit(overloaded{
|
||||
[this](Flatbuffers::target_angle a) { m_board_target_position = a.angle; },
|
||||
[this](Flatbuffers::current_angle a) { m_current_position = a.angle; },
|
||||
},
|
||||
value);
|
||||
}
|
||||
22
src/flatbuffers/AngleControlMessageBuilder.cpp
Normal file
22
src/flatbuffers/AngleControlMessageBuilder.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-06-30.
|
||||
//
|
||||
|
||||
#include "flatbuffers/AngleControlMessageBuilder.h"
|
||||
|
||||
namespace Flatbuffers {
|
||||
const Messaging::AngleControlMessage *
|
||||
AngleControlMessageBuilder::parse_angle_control_message(const uint8_t *buffer) {
|
||||
return flatbuffers::GetRoot<Messaging::AngleControlMessage>(buffer);
|
||||
}
|
||||
|
||||
SerializedMessage AngleControlMessageBuilder::build_angle_control_message(const int16_t angle) {
|
||||
builder_.Clear();
|
||||
|
||||
const auto message = Messaging::CreateAngleControlMessage(builder_, angle);
|
||||
|
||||
builder_.Finish(message);
|
||||
|
||||
return {builder_.GetBufferPointer(), builder_.GetSize()};
|
||||
}
|
||||
} // namespace Flatbuffers
|
||||
49
src/flatbuffers/RobotConfigurationBuilder.cpp
Normal file
49
src/flatbuffers/RobotConfigurationBuilder.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-07-25.
|
||||
//
|
||||
|
||||
#include "flatbuffers/RobotConfigurationBuilder.h"
|
||||
|
||||
#include "flatbuffers/SerializedMessage.h"
|
||||
#include "flatbuffers_generated/RobotConfiguration_generated.h"
|
||||
|
||||
namespace Flatbuffers {
|
||||
|
||||
SerializedMessage RobotConfigurationBuilder::build_robot_configuration(
|
||||
const std::vector<ModuleInstance> &modules,
|
||||
const std::vector<ModuleConnectionInstance> &connections) {
|
||||
builder_.Clear();
|
||||
|
||||
std::vector<flatbuffers::Offset<RobotModule>> module_vector;
|
||||
std::vector<flatbuffers::Offset<Frontend::ModuleConnection>> connection_vector;
|
||||
|
||||
for (const auto &connection : connections) {
|
||||
connection_vector.push_back(Frontend::CreateModuleConnection(
|
||||
builder_, connection.from_module_id, connection.to_module_id, connection.from_socket,
|
||||
connection.to_socket, connection.orientation));
|
||||
}
|
||||
|
||||
for (const auto &module : modules) {
|
||||
// todo: this only works for motors right now
|
||||
auto motor_state = CreateMotorState(builder_, module.angle);
|
||||
const flatbuffers::Offset<void> config_union = motor_state.Union();
|
||||
module_vector.push_back(CreateRobotModule(builder_, module.id, module.type,
|
||||
ModuleState_MotorState, config_union));
|
||||
}
|
||||
|
||||
const auto connection_vector_fb = builder_.CreateVector(connection_vector);
|
||||
const auto module_vector_fb = builder_.CreateVector(module_vector);
|
||||
|
||||
const auto message =
|
||||
Frontend::CreateRobotConfiguration(builder_, module_vector_fb, connection_vector_fb);
|
||||
|
||||
builder_.Finish(message);
|
||||
|
||||
return {builder_.GetBufferPointer(), builder_.GetSize()};
|
||||
}
|
||||
|
||||
const Frontend::RobotConfiguration *
|
||||
RobotConfigurationBuilder::parse_robot_configuration(const std::uint8_t *buffer) {
|
||||
return flatbuffers::GetRoot<Frontend::RobotConfiguration>(buffer);
|
||||
}
|
||||
} // namespace Flatbuffers
|
||||
10
src/flatbuffers/SensorMessageBuilder.cpp
Normal file
10
src/flatbuffers/SensorMessageBuilder.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#include "flatbuffers/SensorMessageBuilder.h"
|
||||
#include "flatbuffers_generated/SensorMessage_generated.h"
|
||||
|
||||
namespace Flatbuffers {
|
||||
|
||||
const Messaging::SensorMessage *SensorMessageBuilder::parse_sensor_message(const uint8_t *buffer) {
|
||||
return flatbuffers::GetRoot<Messaging::SensorMessage>(buffer);
|
||||
}
|
||||
|
||||
} // namespace Flatbuffers
|
||||
38
src/flatbuffers/TopologyMessageBuilder.cpp
Normal file
38
src/flatbuffers/TopologyMessageBuilder.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-06-30.
|
||||
//
|
||||
|
||||
#include "flatbuffers/TopologyMessageBuilder.h"
|
||||
|
||||
#include "flatbuffers/SerializedMessage.h"
|
||||
|
||||
namespace Flatbuffers {
|
||||
SerializedMessage
|
||||
TopologyMessageBuilder::build_topology_message(const uint8_t module_id,
|
||||
const ModuleType module_type,
|
||||
const std::vector<uint8_t> &channel_to_module,
|
||||
const std::vector<int8_t> &orientation_to_module) {
|
||||
builder_.Clear();
|
||||
|
||||
const auto orientation_to_module_vector = builder_.CreateVector(orientation_to_module);
|
||||
const auto channel_to_module_vector = builder_.CreateVector(channel_to_module);
|
||||
|
||||
const auto message =
|
||||
Messaging::CreateTopologyMessage(builder_, module_id, module_type, channel_to_module.size(),
|
||||
channel_to_module_vector, orientation_to_module_vector);
|
||||
|
||||
builder_.Finish(message);
|
||||
|
||||
return {builder_.GetBufferPointer(), builder_.GetSize()};
|
||||
}
|
||||
|
||||
const Messaging::TopologyMessage *
|
||||
TopologyMessageBuilder::parse_topology_message(const uint8_t *buffer) {
|
||||
return flatbuffers::GetRoot<Messaging::TopologyMessage>(buffer);
|
||||
}
|
||||
|
||||
bool TopologyMessageBuilder::is_valid_topology_message(const uint8_t *buffer, size_t size) {
|
||||
flatbuffers::Verifier verifier(buffer, size);
|
||||
return Messaging::VerifyTopologyMessageBuffer(verifier);
|
||||
}
|
||||
} // namespace Flatbuffers
|
||||
102
src/lib_c_control.cpp
Normal file
102
src/lib_c_control.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "lib_c_control.h"
|
||||
#include "flatbuffers/AngleControlMessageBuilder.h"
|
||||
#include "flatbuffers/RobotConfigurationBuilder.h"
|
||||
#include "libcontrol.h"
|
||||
#include "sentry.h"
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
#define ACTUATOR_CONTROL_TAG 5
|
||||
#define TOPOLOGY_CMD_TAG 6
|
||||
#define MAX_RMT_CHANNELS 4
|
||||
|
||||
extern "C" {
|
||||
const auto robot_controller = std::make_unique<RobotController>();
|
||||
|
||||
const auto acm_builder = std::make_unique<Flatbuffers::AngleControlMessageBuilder>();
|
||||
|
||||
const auto robot_configuration_builder = std::make_unique<Flatbuffers::RobotConfigurationBuilder>();
|
||||
|
||||
LIB_API void init() {
|
||||
spdlog::info("[c_control] Initializing");
|
||||
robot_controller->fetchDirectlyConnectedModules(false);
|
||||
}
|
||||
|
||||
LIB_API void cleanup() {
|
||||
spdlog::info("[c_control] Cleanup");
|
||||
}
|
||||
|
||||
LIB_API int send_angle_control(int module_id, int angle) {
|
||||
if (const auto maybe_module = robot_controller->getModule(module_id)) {
|
||||
const auto module = (*maybe_module).lock();
|
||||
module->actuate(angle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LIB_API char *get_configuration(int *size_out) {
|
||||
std::vector<Flatbuffers::ModuleInstance> modules_vec{};
|
||||
std::vector<Flatbuffers::ModuleConnectionInstance> connections_vec{};
|
||||
|
||||
for (const auto &module : robot_controller->getModuleList()) {
|
||||
modules_vec.emplace_back(module);
|
||||
}
|
||||
|
||||
for (const auto &connection : robot_controller->getConnections()) {
|
||||
connections_vec.emplace_back(connection);
|
||||
}
|
||||
|
||||
const auto [data, size] =
|
||||
robot_configuration_builder->build_robot_configuration(modules_vec, connections_vec);
|
||||
*size_out = size;
|
||||
return reinterpret_cast<char *>(data);
|
||||
}
|
||||
|
||||
LIB_API bool control_sentry_init(const char *dsn, const char *environment, const char *release) {
|
||||
sentry_options_t *options = sentry_options_new();
|
||||
sentry_options_set_dsn(options, dsn);
|
||||
sentry_options_set_environment(options, environment);
|
||||
sentry_options_set_release(options, release);
|
||||
sentry_options_set_auto_session_tracking(options, 1);
|
||||
|
||||
sentry_init(options);
|
||||
return true;
|
||||
}
|
||||
|
||||
LIB_API void control_sentry_shutdown(void) {
|
||||
sentry_close();
|
||||
}
|
||||
|
||||
LIB_API void control_sentry_set_app_info(const char *app_name, const char *app_version,
|
||||
const char *build_number) {
|
||||
sentry_value_t app = sentry_value_new_object();
|
||||
|
||||
if (app_name && *app_name) {
|
||||
sentry_value_set_by_key(app, "name", sentry_value_new_string(app_name));
|
||||
}
|
||||
|
||||
if (app_version && *app_version) {
|
||||
sentry_value_set_by_key(app, "version", sentry_value_new_string(app_version));
|
||||
}
|
||||
|
||||
if (build_number && *build_number) {
|
||||
sentry_value_set_by_key(app, "build", sentry_value_new_string(build_number));
|
||||
}
|
||||
|
||||
sentry_set_context("app", app);
|
||||
|
||||
if (app_version && *app_version) {
|
||||
if (build_number && *build_number) {
|
||||
char release[256];
|
||||
snprintf(release, sizeof(release), "%s@%s+%s", app_name && *app_name ? app_name : "app",
|
||||
app_version, build_number);
|
||||
} else {
|
||||
// Example: mygame@1.2.3
|
||||
char release[256];
|
||||
snprintf(release, sizeof(release), "%s@%s", app_name && *app_name ? app_name : "app",
|
||||
app_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
249
src/libcontrol.cpp
Normal file
249
src/libcontrol.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
//
|
||||
// Created by Johnathon Slightham on 2025-11-13.
|
||||
//
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
|
||||
#include "flatbuffers_generated/SensorMessage_generated.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include "ModuleFactory.h"
|
||||
#include "flatbuffers/RobotConfigurationBuilder.h"
|
||||
#include "flatbuffers/SensorMessageBuilder.h"
|
||||
#include "flatbuffers/TopologyMessageBuilder.h"
|
||||
#include "flatbuffers_generated/TopologyMessage_generated.h"
|
||||
#include "libcontrol.h"
|
||||
#include "util/Map.h"
|
||||
|
||||
#define SCAN_DURATION_MS 2000
|
||||
#define ACTUATOR_CMD_TAG 5
|
||||
#define TOPOLOGY_CMD_TAG 6
|
||||
#define SENSOR_CMD_TAG 8
|
||||
#define NUM_CHANNELS 4
|
||||
#define TOPOLOGY_BUFFER_SIZE 1024
|
||||
#define SENSOR_BUFFER_SIZE 1024
|
||||
#define NETWORK_CONFIG_FETCH_RATE 3s
|
||||
#define MODULE_EXPIRE_TIME 3s
|
||||
#define CONTROL_MESSAGE_FREQUENCY 33ms
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
RobotController::~RobotController() {
|
||||
m_stop_thread = true;
|
||||
m_metadata_loop.join();
|
||||
m_transmit_loop.join();
|
||||
m_configuration_loop.join();
|
||||
m_sensor_loop.join();
|
||||
m_expiry_looop.join();
|
||||
}
|
||||
|
||||
std::vector<std::weak_ptr<Module>> RobotController::getModules() {
|
||||
std::vector<std::weak_ptr<Module>> out;
|
||||
std::shared_lock lock(m_module_lock);
|
||||
out.reserve(m_id_to_module.size());
|
||||
for (const auto m : map_to_values(m_id_to_module)) {
|
||||
out.emplace_back(m);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<Flatbuffers::ModuleConnectionInstance> RobotController::getConnections() {
|
||||
std::vector<Flatbuffers::ModuleConnectionInstance> out;
|
||||
std::shared_lock lock(m_connection_lock);
|
||||
|
||||
for (auto const &[_, value] : m_connection_map) {
|
||||
for (const auto conn : value) {
|
||||
out.push_back(conn);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<Flatbuffers::ModuleInstance> RobotController::getModuleList() {
|
||||
std::vector<Flatbuffers::ModuleInstance> out;
|
||||
std::shared_lock lock(m_module_lock);
|
||||
for (auto const &[key, value] : m_id_to_module) {
|
||||
out.push_back({key, value->get_type()});
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::optional<std::weak_ptr<Module>> RobotController::getModule(uint8_t device_id) {
|
||||
std::shared_lock lock(m_module_lock);
|
||||
if (m_id_to_module.contains(device_id)) {
|
||||
return m_id_to_module[device_id];
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void RobotController::resetModules() {
|
||||
std::unique_lock module_lock(m_module_lock);
|
||||
std::unique_lock conn_lock(m_connection_lock);
|
||||
m_id_to_module.erase(m_id_to_module.begin(), m_id_to_module.end());
|
||||
m_connection_map.erase(m_connection_map.begin(), m_connection_map.end());
|
||||
}
|
||||
|
||||
void RobotController::fetchDirectlyConnectedModules(bool block) {
|
||||
spdlog::info("[Control] Fetching modules from network");
|
||||
auto t = std::thread([&] {
|
||||
auto out = m_messaging_interface->find_connected_modules(
|
||||
std::chrono::milliseconds(SCAN_DURATION_MS));
|
||||
spdlog::info("[Control] Found {} modules on the network", out.size());
|
||||
});
|
||||
|
||||
if (block) {
|
||||
t.join();
|
||||
} else {
|
||||
t.detach();
|
||||
}
|
||||
}
|
||||
|
||||
void RobotController::metadata_loop() {
|
||||
unsigned char buf[TOPOLOGY_BUFFER_SIZE];
|
||||
const auto builder = std::make_unique<Flatbuffers::TopologyMessageBuilder>();
|
||||
while (!m_stop_thread) {
|
||||
if (auto result =
|
||||
m_messaging_interface->recv(buf, TOPOLOGY_BUFFER_SIZE, TOPOLOGY_CMD_TAG)) {
|
||||
const auto &[rx_size, from] = *result;
|
||||
|
||||
flatbuffers::Verifier verifier(buf, rx_size);
|
||||
if (!Messaging::VerifyTopologyMessageBuffer(verifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto metadata = builder->parse_topology_message(reinterpret_cast<uint8_t *>(buf));
|
||||
std::unique_lock lock(m_module_lock);
|
||||
if (!m_id_to_module.contains(metadata->module_id())) {
|
||||
spdlog::info("[Control] Creating module entry for {}", metadata->module_id());
|
||||
m_id_to_module.insert(
|
||||
{metadata->module_id(),
|
||||
ModuleFactory::createModule(metadata->module_id(), metadata->module_type(),
|
||||
m_messaging_interface)});
|
||||
}
|
||||
|
||||
m_id_to_module[metadata->module_id()]->update_module_metadata(*metadata);
|
||||
|
||||
std::unique_lock conn_lock(m_connection_lock);
|
||||
std::vector<Flatbuffers::ModuleConnectionInstance> conns;
|
||||
for (uint8_t i = 1; i < NUM_CHANNELS; i++) {
|
||||
if (metadata->channel_to_module()->Get(i) > 0) {
|
||||
conns.push_back(Flatbuffers::ModuleConnectionInstance{
|
||||
.from_module_id = metadata->module_id(),
|
||||
.to_module_id = metadata->channel_to_module()->Get(i),
|
||||
.from_socket = i,
|
||||
.to_socket = 0,
|
||||
.orientation =
|
||||
static_cast<Orientation>(metadata->channel_to_orientation()->Get(i))});
|
||||
}
|
||||
}
|
||||
|
||||
m_connection_map[metadata->module_id()] = conns;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RobotController::transmit_loop() {
|
||||
const auto builder = std::make_unique<Flatbuffers::TopologyMessageBuilder>();
|
||||
while (!m_stop_thread) {
|
||||
std::this_thread::sleep_for(CONTROL_MESSAGE_FREQUENCY);
|
||||
std::shared_lock lock(m_module_lock);
|
||||
for (const auto [id, module] : m_id_to_module) {
|
||||
auto out = module->get_actuation_message();
|
||||
|
||||
if (out.size() > 0) {
|
||||
m_messaging_interface->send(out.data(), out.size(), id, ACTUATOR_CMD_TAG, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RobotController::configuration_loop() {
|
||||
while (!m_stop_thread) {
|
||||
std::this_thread::sleep_for(NETWORK_CONFIG_FETCH_RATE);
|
||||
fetchDirectlyConnectedModules(true);
|
||||
}
|
||||
}
|
||||
|
||||
void RobotController::expiry_loop() {
|
||||
while (!m_stop_thread) {
|
||||
std::this_thread::sleep_for(MODULE_EXPIRE_TIME);
|
||||
|
||||
std::unordered_set<uint8_t> delete_modules{};
|
||||
|
||||
std::unique_lock module_lock(m_module_lock);
|
||||
std::unique_lock connection_lock(m_connection_lock);
|
||||
|
||||
for (auto it = m_id_to_module.begin(); it != m_id_to_module.end();) {
|
||||
if (it->second->get_last_updated_time() <
|
||||
std::chrono::system_clock::now() - MODULE_EXPIRE_TIME) {
|
||||
delete_modules.emplace(it->first);
|
||||
it = m_id_to_module.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// todo
|
||||
// Remove connections
|
||||
// for (auto it = m_connection_map.begin(); it != m_connection_map.end();) {
|
||||
// // Remove it->x connections
|
||||
// if (delete_modules.contains(it->first)) {
|
||||
// it = m_connection_map.erase(it);
|
||||
// } else {
|
||||
// ++it;
|
||||
// }
|
||||
|
||||
// // Remove x->it connections
|
||||
// for (auto it2 = it->second.begin(); it2 != it->second.end();) {
|
||||
// if (delete_modules.contains(it2->to_module_id)) {
|
||||
// it2 = it->second.erase(it2);
|
||||
// } else {
|
||||
// ++it2;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void RobotController::sensor_loop() {
|
||||
unsigned char buf[SENSOR_BUFFER_SIZE];
|
||||
const auto builder = std::make_unique<Flatbuffers::SensorMessageBuilder>();
|
||||
while (!m_stop_thread) {
|
||||
if (auto result = m_messaging_interface->recv(buf, SENSOR_BUFFER_SIZE, SENSOR_CMD_TAG)) {
|
||||
const auto &[rx_size, from] = *result;
|
||||
|
||||
flatbuffers::Verifier verifier(buf, rx_size);
|
||||
if (!Messaging::VerifySensorMessageBuffer(verifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto sensor_message =
|
||||
builder->parse_sensor_message(reinterpret_cast<uint8_t *>(buf));
|
||||
if (sensor_message->values()->size() != sensor_message->values_type()->size()) {
|
||||
spdlog::error(
|
||||
"[Control] Got a sensor message with different value ({}) and type ({}) sizes",
|
||||
sensor_message->values()->size(), sensor_message->values_type()->size());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::shared_lock module_lock(m_module_lock);
|
||||
if (!m_id_to_module.contains(from)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sensor_message->values()->size(); i++) {
|
||||
if (auto maybe_value = Flatbuffers::SensorMessageBuilder::build_sensor_value(
|
||||
static_cast<Messaging::SensorValue>(sensor_message->values_type()->Get(i)),
|
||||
sensor_message->values()->Get(i))) {
|
||||
m_id_to_module[from]->update_sensor_data(*maybe_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user