mirror of
https://github.com/halleysfifthinc/AVCLAN-Mockingboard.git
synced 2026-08-23 05:52:53 +00:00
Redesign (simplify) top-level avclan interface to use expected/nullable unique_ptr
- Peripheral::read/send now return `expected<unique_ptr<Frame>, Error>` for a unified success/error interface. - Peripheral::route returns `expected<unique_ptr<Frame>, Error>` to distinguish intent: (intentional) non-response vs unable to respond - Other functions with optional message semantics (handle,poll,react) return a unique_ptr whose ownership-state indicates response intent (i.e. send new message) Bundled necessary changes: - Switch Frame allocation from global to local (enabled via member function new/delete) - Add new header-library tl::expected to shim avr-libstdcpp Unrelated: Defensive `continue` added after `route`, to reduce Bus activity check latency Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+82
-42
@@ -4,16 +4,20 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "avclan.h"
|
||||
#include "bus.hpp"
|
||||
#include "device.hpp"
|
||||
#include "frame.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include "stdshim.hpp"
|
||||
|
||||
namespace avclan {
|
||||
|
||||
@@ -25,6 +29,14 @@ template <DeviceInterface... Devs> class Peripheral {
|
||||
public:
|
||||
using Error = detail::Error;
|
||||
|
||||
// lack of static reflection until C++26 limits our methods of doing a
|
||||
// compile-time collision check. Within Peripheral, we can at least assert no
|
||||
// collision with Devices *used in a particular instantiation* but it is not a
|
||||
// universal check against all Device enum values
|
||||
static_assert(((Devs::id != NoDevice) && ...),
|
||||
"a registered Device id collides with the NoDevice sentinel; "
|
||||
"update the sentinel value in avclan.h");
|
||||
|
||||
Peripheral(Bus &bus, uint16_t address) : bus{bus}, address_{address} {
|
||||
bus.init();
|
||||
(std::get<Devs>(devices_).init(), ...);
|
||||
@@ -43,32 +55,46 @@ public:
|
||||
Bus &get_bus() { return bus; }
|
||||
#endif
|
||||
|
||||
Error::Read read(Frame *in, Frame::Print print) {
|
||||
return bus.read(address_, in, print);
|
||||
expected<std::unique_ptr<Frame>, Error::Read>
|
||||
read(Frame::Print print = Frame::Print{}) {
|
||||
return bus.read(address_, print);
|
||||
};
|
||||
Error::Send send(Frame *out, Frame::Print print) {
|
||||
|
||||
expected<std::unique_ptr<Frame>, detail::SendError>
|
||||
send(std::unique_ptr<Frame> out, Frame::Print print = Frame::Print{}) {
|
||||
// To "forge" a controller_addr, instantiate a new/different Peripheral
|
||||
stamp<Sender>(out);
|
||||
stamp<Sender>(out.get());
|
||||
out->control = 0xF;
|
||||
return bus.send(out, print);
|
||||
auto err = bus.send(out.get(), print);
|
||||
if (err != Error::Send{0})
|
||||
return unexpected{
|
||||
detail::SendError{out->owning_device, out->reaction, err}};
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
#define PACK3(a, b, c) (((uint32_t)(a) << 16) | ((uint32_t)(b) << 8) | (c))
|
||||
|
||||
void route(const Frame *in, Frame *out) {
|
||||
// expected needed to distinguish don't vs can't respond
|
||||
expected<std::unique_ptr<Frame>, Error::Read> route(const Frame *in) {
|
||||
using enum Device;
|
||||
using enum Action;
|
||||
out->reaction = 0;
|
||||
|
||||
if (is_muted() || in->length < 3)
|
||||
return;
|
||||
return {};
|
||||
|
||||
std::unique_ptr<Frame> out(new (std::nothrow) Frame);
|
||||
if (!out) {
|
||||
puts("!! failed Frame alloc in route !!");
|
||||
return unexpected{Error::Read::POOL_EMPTY};
|
||||
}
|
||||
|
||||
// 0xFF placeholders are variant bytes filled by writing directly to
|
||||
// out->data[N] after memcpy.
|
||||
static const uint8_t lancheck_resp[] = {0x00, to_underlying(COMM_CTRL),
|
||||
to_underlying(LAN), 0xFF, 0xFF};
|
||||
|
||||
stamp<Recipient>(out);
|
||||
stamp<Recipient>(out.get());
|
||||
|
||||
const uint8_t *data = in->data;
|
||||
const uint8_t b0 = *data++;
|
||||
@@ -114,7 +140,7 @@ public:
|
||||
to_underlying(Advertise_Function)): {
|
||||
auto enable_d = [](auto &d, auto &out) { d.enable(out); };
|
||||
((Devs::id == static_cast<Device>(b3)
|
||||
? originate(std::get<Devs>(devices_), out, enable_d)
|
||||
? originate(std::get<Devs>(devices_), out.get(), enable_d)
|
||||
: void()),
|
||||
...);
|
||||
break;
|
||||
@@ -135,7 +161,7 @@ public:
|
||||
case PACK3(COMMUNICATION_V2, COMM_CTRL,
|
||||
to_underlying(List_Functions_Req)): {
|
||||
controller_ = in->controller_addr;
|
||||
stamp<Recipient>(out); // re-stamp now that controller_ is known
|
||||
stamp<Recipient>(out.get()); // re-stamp now that controller_ is known
|
||||
out->is_unicast = true;
|
||||
const uint8_t list_functions_resp[] = {
|
||||
0x00, to_underlying(COMM_CTRL), from,
|
||||
@@ -151,40 +177,54 @@ public:
|
||||
} else if (in->peripheral_addr == address_ && b0 == 0x00) {
|
||||
auto handle_d = [&](auto &d, auto &out) { d.handle(in, out); };
|
||||
((Devs::id == static_cast<Device>(b2)
|
||||
? originate(std::get<Devs>(devices_), out, handle_d)
|
||||
? originate(std::get<Devs>(devices_), out.get(), handle_d)
|
||||
: void()),
|
||||
...);
|
||||
}
|
||||
|
||||
if (out->reaction > 0)
|
||||
return out;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#undef PACK3
|
||||
|
||||
void react(Frame *out, Error::Send err) {
|
||||
if (((Devs::id == out->owning_device) || ...))
|
||||
((Devs::id == out->owning_device
|
||||
? void(std::get<Devs>(devices_).react(out, err))
|
||||
: void()),
|
||||
...);
|
||||
else
|
||||
out->reaction = 0;
|
||||
std::unique_ptr<Frame>
|
||||
react(expected<std::unique_ptr<Frame>, detail::SendError> exp) {
|
||||
const Device from =
|
||||
exp ? exp.value()->owning_device : exp.error().owning_device;
|
||||
|
||||
std::unique_ptr<Frame> next;
|
||||
((Devs::id == from &&
|
||||
(next = std::get<Devs>(devices_).react(std::move(exp)))) ||
|
||||
...);
|
||||
return next;
|
||||
}
|
||||
|
||||
bool pending() const { return (std::get<Devs>(devices_).pending() || ...); }
|
||||
|
||||
// Service ready devices in round-robin order
|
||||
bool emit(Frame *out) {
|
||||
auto does_emit = [&](DeviceInterface auto &dev) -> bool {
|
||||
std::unique_ptr<Frame> poll() {
|
||||
using U = std::unique_ptr<Frame>;
|
||||
auto does_emit = [&](DeviceInterface auto &dev) -> U {
|
||||
if (!dev.pending())
|
||||
return false;
|
||||
originate(dev, out, [](auto &d, auto &out) { d.emit(out); });
|
||||
dev.resolvepending();
|
||||
return true;
|
||||
return {};
|
||||
|
||||
U out(new (std::nothrow) Frame);
|
||||
if (!out) {
|
||||
puts("!! failed Frame alloc in poll !!");
|
||||
return {};
|
||||
}
|
||||
|
||||
originate(dev, out.get(), [](auto &d, auto &out) { d.emit(out); });
|
||||
return out;
|
||||
};
|
||||
|
||||
// Runtime tuple index helper
|
||||
auto does_index_emit = [&](std::size_t t) -> bool {
|
||||
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
|
||||
return (((Is == t) && does_emit(std::get<Is>(devices_))) || ...);
|
||||
auto does_index_emit = [&](std::size_t t) -> U {
|
||||
return [&]<std::size_t... Is>(std::index_sequence<Is...>) -> U {
|
||||
U out;
|
||||
((Is == t && (out = does_emit(std::get<Is>(devices_)))) || ...);
|
||||
return out;
|
||||
}(std::index_sequence_for<Devs...>{});
|
||||
};
|
||||
|
||||
@@ -195,16 +235,16 @@ public:
|
||||
static uint8_t rr_ = 0; // round-robin cursor
|
||||
const std::size_t start = rr_;
|
||||
for (std::size_t t = start; t < N; ++t) // [start, N)
|
||||
if (does_index_emit(t)) {
|
||||
if (auto out = does_index_emit(t)) {
|
||||
rr_ = (t + 1 == N) ? 0 : t + 1;
|
||||
return true;
|
||||
return out;
|
||||
}
|
||||
for (std::size_t t = 0; t < start; ++t) // [0, start); t+1 <= start < N
|
||||
if (does_index_emit(t)) {
|
||||
if (auto out = does_index_emit(t)) {
|
||||
rr_ = t + 1;
|
||||
return true;
|
||||
return out;
|
||||
}
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user