Prepare files for public release

This commit is contained in:
2026-01-24 10:26:37 -05:00
parent 2f358c30e6
commit d89c636e2f
28 changed files with 2013 additions and 0 deletions

53
include/BlockingQueue.h Normal file
View File

@@ -0,0 +1,53 @@
//
// Created by Johnathon Slightham on 2025-07-10.
//
#ifndef BLOCKINGQUEUE_H
#define BLOCKINGQUEUE_H
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <optional>
#include <queue>
template <typename T> class BlockingQueue {
public:
explicit BlockingQueue(const size_t capacity) : m_capacity(capacity) {
}
// Enqueue with timeout. Returns true on success, false on timeout.
bool enqueue(T &&item, std::chrono::milliseconds max_wait) {
std::unique_lock lock(m_mutex);
if (!m_cond_not_full.wait_for(lock, max_wait,
[this]() { return m_queue.size() < m_capacity; })) {
return false;
}
m_queue.push(std::move(item));
m_cond_not_empty.notify_one();
return true;
}
// Dequeue with timeout. Returns optional<T> (empty on timeout).
std::optional<T> dequeue(std::chrono::milliseconds max_wait) {
std::unique_lock lock(m_mutex);
if (!m_cond_not_empty.wait_for(lock, max_wait, [this]() { return !m_queue.empty(); })) {
return std::nullopt;
}
T item = std::move(m_queue.front());
m_queue.pop();
m_cond_not_full.notify_one();
return item;
}
private:
std::queue<T> m_queue;
size_t m_capacity;
std::mutex m_mutex;
std::condition_variable m_cond_not_empty;
std::condition_variable m_cond_not_full;
};
#endif // BLOCKINGQUEUE_H

View File

@@ -0,0 +1,15 @@
//
// Created by Johnathon Slightham on 2025-06-10.
//
#ifndef INETWORKCLIENT_H
#define INETWORKCLIENT_H
class ICommunicationClient {
public:
virtual ~ICommunicationClient() = default;
virtual int init() = 0;
virtual int send_msg(void *sendbuff, uint32_t len) = 0;
};
#endif //INETWORKCLIENT_H

View File

@@ -0,0 +1,24 @@
//
// Created by Johnathon Slightham on 2025-06-10.
//
#ifndef IDISCOVERYSERVICE_H
#define IDISCOVERYSERVICE_H
#include <unordered_set>
#include "ICommunicationClient.h"
#include "mDNSRobotModule.h"
class IDiscoveryService {
public:
virtual ~IDiscoveryService() = default;
virtual std::unordered_set<uint8_t> find_modules(std::chrono::duration<double> wait_time) = 0;
virtual std::unordered_map<uint8_t, std::shared_ptr<ICommunicationClient>> get_lossy_clients(
const std::shared_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>> &rx_queue,
std::vector<uint8_t> &skip_modules) = 0;
virtual std::unordered_map<uint8_t, std::shared_ptr<ICommunicationClient>> get_lossless_clients(
const std::shared_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>> &rx_queue,
std::vector<uint8_t> &skip_modules) = 0;
};
#endif // IDISCOVERYSERVICE_H

54
include/TCPClient.h Normal file
View File

@@ -0,0 +1,54 @@
//
// Created by Johnathon Slightham on 2025-06-10.
//
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <thread>
#include <utility>
#include "ICommunicationClient.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define CLOSE_SOCKET closesocket
#pragma comment(lib, "ws2_32.lib")
typedef SOCKET socket_t;
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#define CLOSE_SOCKET close
typedef int socket_t;
#endif
#include "BlockingQueue.h"
class TCPClient final : public ICommunicationClient {
public:
TCPClient(std::string ip,
const std::shared_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>> &rx_queue)
: port{3000}, m_ip{std::move(ip)}, m_stop_flag(false),
m_thread(std::thread(&TCPClient::rx_thread, this)), m_rx_queue(rx_queue) {
}
~TCPClient() override;
int init() override;
int send_msg(void *sendbuff, uint32_t len) override;
private:
void deinit();
void rx_thread() const;
socket_t m_socket = -1;
int port;
bool m_initialized = false;
std::string m_ip;
std::atomic<bool> m_stop_flag;
std::thread m_thread;
std::shared_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>> m_rx_queue;
};
#endif // TCPCLIENT_H

53
include/UDPClient.h Normal file
View File

@@ -0,0 +1,53 @@
//
// Created by Johnathon Slightham on 2025-12-27.
//
#ifndef UDPCLIENT_H
#define UDPCLIENT_H
#include <thread>
#include <utility>
#include "ICommunicationClient.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define CLOSE_SOCKET closesocket
#pragma comment(lib, "ws2_32.lib")
typedef SOCKET socket_t;
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#define CLOSE_SOCKET close
typedef int socket_t;
#endif
#include "BlockingQueue.h"
class UDPClient final : public ICommunicationClient {
public:
UDPClient(std::string /* ip */,
const std::shared_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>> &rx_queue)
: m_stop_flag(false), m_thread(std::thread(&UDPClient::rx_thread, this)),
m_rx_queue(rx_queue) {
}
~UDPClient() override;
int init() override;
int send_msg(void *sendbuff, uint32_t len) override;
private:
void deinit();
void rx_thread() const;
socket_t m_tx_socket = -1;
socket_t m_rx_socket = -1;
bool m_initialized = false;
std::atomic<bool> m_stop_flag;
std::thread m_thread;
std::shared_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>> m_rx_queue;
};
#endif // UDPCLIENT_H

7
include/constants.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef CONSTANTS_H
#define CONSTANTS_H
constexpr auto PC_MODULE_ID = 1;
constexpr auto MAX_BUFFER_SIZE = 1024;
#endif // CONSTANTS_H

View File

@@ -0,0 +1,33 @@
//
// Created by Johnathon Slightham on 2025-06-30.
//
#ifndef MPIMESSAGEBUILDER_H
#define MPIMESSAGEBUILDER_H
#include <string>
#include <vector>
#include "../flatbuffers_generated/MPIMessage_generated.h"
#include "SerializedMessage.h"
#include "flatbuffers/flatbuffers.h"
namespace Flatbuffers {
class MPIMessageBuilder {
public:
MPIMessageBuilder() : builder_(1024) {
}
SerializedMessage build_mpi_message(Messaging::MessageType type, uint8_t sender,
uint8_t destination, uint16_t sequence_number,
bool is_durable, uint8_t tag,
const std::vector<uint8_t> &payload);
static const Messaging::MPIMessage *parse_mpi_message(const uint8_t *buffer);
private:
flatbuffers::FlatBufferBuilder builder_;
};
} // namespace Flatbuffers
#endif //MPIMESSAGEBUILDER_H

View File

@@ -0,0 +1,15 @@
//
// Created by Johnathon Slightham on 2025-07-05.
//
#ifndef SERIALIZEDMESSAGE_H
#define SERIALIZEDMESSAGE_H
namespace Flatbuffers {
struct SerializedMessage {
void *data;
size_t size;
};
} // namespace Flatbuffers
#endif //SERIALIZEDMESSAGE_H

View File

@@ -0,0 +1,188 @@
// automatically generated by the FlatBuffers compiler, do not modify
#ifndef FLATBUFFERS_GENERATED_MPIMESSAGE_MESSAGING_H_
#define FLATBUFFERS_GENERATED_MPIMESSAGE_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 MPIMessage;
struct MPIMessageBuilder;
enum MessageType : int8_t {
MessageType_BROADCAST = 0,
MessageType_PTP = 1,
MessageType_MIN = MessageType_BROADCAST,
MessageType_MAX = MessageType_PTP
};
inline const MessageType (&EnumValuesMessageType())[2] {
static const MessageType values[] = {MessageType_BROADCAST, MessageType_PTP};
return values;
}
inline const char *const *EnumNamesMessageType() {
static const char *const names[3] = {"BROADCAST", "PTP", nullptr};
return names;
}
inline const char *EnumNameMessageType(MessageType e) {
if (::flatbuffers::IsOutRange(e, MessageType_BROADCAST, MessageType_PTP))
return "";
const size_t index = static_cast<size_t>(e);
return EnumNamesMessageType()[index];
}
struct MPIMessage FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table {
typedef MPIMessageBuilder Builder;
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
VT_TYPE = 4,
VT_SENDER = 6,
VT_DESTINATION = 8,
VT_SEQUENCE_NUMBER = 10,
VT_IS_DURABLE = 12,
VT_LENGTH = 14,
VT_TAG = 16,
VT_PAYLOAD = 18
};
Messaging::MessageType type() const {
return static_cast<Messaging::MessageType>(GetField<int8_t>(VT_TYPE, 0));
}
uint8_t sender() const {
return GetField<uint8_t>(VT_SENDER, 0);
}
uint8_t destination() const {
return GetField<uint8_t>(VT_DESTINATION, 0);
}
uint16_t sequence_number() const {
return GetField<uint16_t>(VT_SEQUENCE_NUMBER, 0);
}
bool is_durable() const {
return GetField<uint8_t>(VT_IS_DURABLE, 0) != 0;
}
uint16_t length() const {
return GetField<uint16_t>(VT_LENGTH, 0);
}
uint8_t tag() const {
return GetField<uint8_t>(VT_TAG, 0);
}
const ::flatbuffers::Vector<uint8_t> *payload() const {
return GetPointer<const ::flatbuffers::Vector<uint8_t> *>(VT_PAYLOAD);
}
bool Verify(::flatbuffers::Verifier &verifier) const {
return VerifyTableStart(verifier) && VerifyField<int8_t>(verifier, VT_TYPE, 1) &&
VerifyField<uint8_t>(verifier, VT_SENDER, 1) &&
VerifyField<uint8_t>(verifier, VT_DESTINATION, 1) &&
VerifyField<uint16_t>(verifier, VT_SEQUENCE_NUMBER, 2) &&
VerifyField<uint8_t>(verifier, VT_IS_DURABLE, 1) &&
VerifyField<uint16_t>(verifier, VT_LENGTH, 2) &&
VerifyField<uint8_t>(verifier, VT_TAG, 1) && VerifyOffset(verifier, VT_PAYLOAD) &&
verifier.VerifyVector(payload()) && verifier.EndTable();
}
};
struct MPIMessageBuilder {
typedef MPIMessage Table;
::flatbuffers::FlatBufferBuilder &fbb_;
::flatbuffers::uoffset_t start_;
void add_type(Messaging::MessageType type) {
fbb_.AddElement<int8_t>(MPIMessage::VT_TYPE, static_cast<int8_t>(type), 0);
}
void add_sender(uint8_t sender) {
fbb_.AddElement<uint8_t>(MPIMessage::VT_SENDER, sender, 0);
}
void add_destination(uint8_t destination) {
fbb_.AddElement<uint8_t>(MPIMessage::VT_DESTINATION, destination, 0);
}
void add_sequence_number(uint16_t sequence_number) {
fbb_.AddElement<uint16_t>(MPIMessage::VT_SEQUENCE_NUMBER, sequence_number, 0);
}
void add_is_durable(bool is_durable) {
fbb_.AddElement<uint8_t>(MPIMessage::VT_IS_DURABLE, static_cast<uint8_t>(is_durable), 0);
}
void add_length(uint16_t length) {
fbb_.AddElement<uint16_t>(MPIMessage::VT_LENGTH, length, 0);
}
void add_tag(uint8_t tag) {
fbb_.AddElement<uint8_t>(MPIMessage::VT_TAG, tag, 0);
}
void add_payload(::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> payload) {
fbb_.AddOffset(MPIMessage::VT_PAYLOAD, payload);
}
explicit MPIMessageBuilder(::flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) {
start_ = fbb_.StartTable();
}
::flatbuffers::Offset<MPIMessage> Finish() {
const auto end = fbb_.EndTable(start_);
auto o = ::flatbuffers::Offset<MPIMessage>(end);
return o;
}
};
inline ::flatbuffers::Offset<MPIMessage>
CreateMPIMessage(::flatbuffers::FlatBufferBuilder &_fbb,
Messaging::MessageType type = Messaging::MessageType_BROADCAST, uint8_t sender = 0,
uint8_t destination = 0, uint16_t sequence_number = 0, bool is_durable = false,
uint16_t length = 0, uint8_t tag = 0,
::flatbuffers::Offset<::flatbuffers::Vector<uint8_t>> payload = 0) {
MPIMessageBuilder builder_(_fbb);
builder_.add_payload(payload);
builder_.add_length(length);
builder_.add_sequence_number(sequence_number);
builder_.add_tag(tag);
builder_.add_is_durable(is_durable);
builder_.add_destination(destination);
builder_.add_sender(sender);
builder_.add_type(type);
return builder_.Finish();
}
inline ::flatbuffers::Offset<MPIMessage>
CreateMPIMessageDirect(::flatbuffers::FlatBufferBuilder &_fbb,
Messaging::MessageType type = Messaging::MessageType_BROADCAST,
uint8_t sender = 0, uint8_t destination = 0, uint16_t sequence_number = 0,
bool is_durable = false, uint16_t length = 0, uint8_t tag = 0,
const std::vector<uint8_t> *payload = nullptr) {
auto payload__ = payload ? _fbb.CreateVector<uint8_t>(*payload) : 0;
return Messaging::CreateMPIMessage(_fbb, type, sender, destination, sequence_number, is_durable,
length, tag, payload__);
}
inline const Messaging::MPIMessage *GetMPIMessage(const void *buf) {
return ::flatbuffers::GetRoot<Messaging::MPIMessage>(buf);
}
inline const Messaging::MPIMessage *GetSizePrefixedMPIMessage(const void *buf) {
return ::flatbuffers::GetSizePrefixedRoot<Messaging::MPIMessage>(buf);
}
inline bool VerifyMPIMessageBuffer(::flatbuffers::Verifier &verifier) {
return verifier.VerifyBuffer<Messaging::MPIMessage>(nullptr);
}
inline bool VerifySizePrefixedMPIMessageBuffer(::flatbuffers::Verifier &verifier) {
return verifier.VerifySizePrefixedBuffer<Messaging::MPIMessage>(nullptr);
}
inline void FinishMPIMessageBuffer(::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<Messaging::MPIMessage> root) {
fbb.Finish(root);
}
inline void FinishSizePrefixedMPIMessageBuffer(::flatbuffers::FlatBufferBuilder &fbb,
::flatbuffers::Offset<Messaging::MPIMessage> root) {
fbb.FinishSizePrefixed(root);
}
} // namespace Messaging
#endif // FLATBUFFERS_GENERATED_MPIMESSAGE_MESSAGING_H_

View File

@@ -0,0 +1,286 @@
// 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_MIN = ModuleType_SPLITTER,
ModuleType_MAX = ModuleType_BATTERY
};
inline const ModuleType (&EnumValuesModuleType())[4] {
static const ModuleType values[] = {ModuleType_SPLITTER, ModuleType_SERVO_1,
ModuleType_DC_MOTOR, ModuleType_BATTERY};
return values;
}
inline const char *const *EnumNamesModuleType() {
static const char *const names[5] = {"SPLITTER", "SERVO_1", "DC_MOTOR", "BATTERY", nullptr};
return names;
}
inline const char *EnumNameModuleType(ModuleType e) {
if (::flatbuffers::IsOutRange(e, ModuleType_SPLITTER, ModuleType_BATTERY))
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_

59
include/librpc.h Normal file
View File

@@ -0,0 +1,59 @@
#ifndef RPC_LIBRARY_H
#define RPC_LIBRARY_H
#include <chrono>
#include <memory>
#include <shared_mutex>
#include <thread>
#include "BlockingQueue.h"
#include "constants.h"
#include "mDNSDiscoveryService.h"
constexpr auto RX_QUEUE_SIZE = 100;
struct SizeAndSource {
size_t bytes_written;
uint8_t sender;
};
class MessagingInterface {
public:
MessagingInterface()
: m_stop_flag(false), m_rx_thread(std::thread(&MessagingInterface::handle_recv, this)),
m_rx_queue(std::make_shared<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>>(
RX_QUEUE_SIZE)) {
#ifdef _WIN32
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif
// Initialization must be after call to WSAStartup
m_discovery_service = std::make_unique<mDNSDiscoveryService>();
}
~MessagingInterface();
int send(uint8_t *buffer, size_t size, uint8_t destination, uint8_t tag, bool durable);
int broadcast(uint8_t *buffer, size_t size, bool durable); // todo
std::optional<SizeAndSource> recv(uint8_t *buffer, size_t size, uint8_t tag);
int sendrecv(uint8_t *send_buffer, size_t send_size, uint8_t dest, uint8_t send_tag,
uint8_t *recv_buffer, size_t recv_size, uint8_t recv_tag); // todo
std::unordered_set<uint8_t> find_connected_modules(std::chrono::duration<double> scan_duration);
private:
void handle_recv();
uint16_t m_sequence_number = 0;
std::unordered_map<uint8_t, std::shared_ptr<ICommunicationClient>> m_id_to_lossless_client;
std::unordered_map<uint8_t, std::shared_ptr<ICommunicationClient>> m_id_to_lossy_client;
std::unordered_map<int, std::unique_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>>>
m_tag_to_queue_map;
std::unique_ptr<IDiscoveryService> m_discovery_service;
std::atomic<bool> m_stop_flag;
std::thread m_rx_thread;
std::shared_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>> m_rx_queue;
std::shared_mutex m_client_mutex;
std::shared_mutex m_scan_mutex;
};
#endif // RPC_LIBRARY_H

View File

@@ -0,0 +1,56 @@
//
// Created by Johnathon Slightham on 2025-06-10.
//
#ifndef MDNSDISCOVERYSERVICE_H
#define MDNSDISCOVERYSERVICE_H
#include <chrono>
#include <unordered_map>
#include "BlockingQueue.h"
#include "ICommunicationClient.h"
#include "IDiscoveryService.h"
#include "mDNSRobotModule.h"
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define CLOSE_SOCKET closesocket
#pragma comment(lib, "ws2_32.lib")
typedef SOCKET socket_t;
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#define CLOSE_SOCKET close
typedef int socket_t;
#endif
class mDNSDiscoveryService final : public IDiscoveryService {
public:
mDNSDiscoveryService();
~mDNSDiscoveryService() override;
std::unordered_set<uint8_t> find_modules(std::chrono::duration<double> wait_time) override;
std::unordered_map<uint8_t, std::shared_ptr<ICommunicationClient>> get_lossy_clients(
const std::shared_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>> &rx_queue,
std::vector<uint8_t> &skip_modules) override;
std::unordered_map<uint8_t, std::shared_ptr<ICommunicationClient>> get_lossless_clients(
const std::shared_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>> &rx_queue,
std::vector<uint8_t> &skip_modules) override;
private:
template <typename T>
std::unordered_map<uint8_t, std::shared_ptr<ICommunicationClient>> create_clients(
const std::shared_ptr<BlockingQueue<std::unique_ptr<std::vector<uint8_t>>>> &rx_queue,
std::vector<uint8_t> &skip_modules);
static void send_mdns_query(socket_t sock, const sockaddr_in &addr);
static std::optional<mDNSRobotModule> parse_response(uint8_t *buffer, int size);
static std::tuple<std::string, int> read_mdns_name(const uint8_t *buffer, int size, int ptr);
std::unordered_map<uint8_t, mDNSRobotModule> module_to_mdns{};
};
#endif // MDNSDISCOVERYSERVICE_H

19
include/mDNSRobotModule.h Normal file
View File

@@ -0,0 +1,19 @@
//
// Created by Johnathon Slightham on 2025-07-05.
//
#ifndef ROBOTMODULEINSTANCE_H
#define ROBOTMODULEINSTANCE_H
#include "flatbuffers_generated/RobotModule_generated.h"
#include <string>
struct mDNSRobotModule {
int id;
std::string ip;
std::string hostname;
ModuleType module_type;
std::vector<int> connected_module_ids;
};
#endif //ROBOTMODULEINSTANCE_H

24
include/util/ip.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef IP_UTIL_H
#define IP_UTIL_H
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define CLOSE_SOCKET closesocket
#pragma comment(lib, "ws2_32.lib")
typedef SOCKET socket_t;
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#define CLOSE_SOCKET close
typedef int socket_t;
#endif
bool is_valid_ipv4(const std::string &ip) {
struct in_addr addr;
return inet_pton(AF_INET, ip.c_str(), &addr) == 1;
}
#endif // IP_UTIL_H

32
include/util/log.h Normal file
View File

@@ -0,0 +1,32 @@
//
// Created by sligh on 2026-01-09.
//
#ifndef LOG_H
#define LOG_H
#define ERRBUF_SIZE 300
#include "spdlog/spdlog.h"
#ifdef _WIN32
void print_errno() {
char errbuf[ERRBUF_SIZE];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(), 0, errbuf, sizeof(errbuf),
NULL);
spdlog::error("{}", errbuf);
}
#else
#include <errno.h>
#include <string.h>
void print_errno() {
spdlog::error("{}", strerror(errno));
}
#endif
#endif //LOG_H

23
include/util/string.h Normal file
View File

@@ -0,0 +1,23 @@
//
// Created by Johnathon Slightham on 2025-07-05.
//
#ifndef STRING_H
#define STRING_H
#include <sstream>
#include <string>
#include <vector>
inline std::vector<std::string> split(const std::string &str, const char delimiter) {
std::vector<std::string> result;
std::stringstream ss(str);
std::string token;
while (std::getline(ss, token, delimiter)) {
result.push_back(token);
}
return result;
}
#endif //STRING_H