176 lines
4.3 KiB
C++

#pragma once
#include "../Device.h"
#include <Tactility/EventFlag.h>
#include <Tactility/Lock.h>
#include <Tactility/Mutex.h>
#include <Tactility/Thread.h>
#include <deque>
#include <utility>
namespace tt::hal::radio {
struct RxPacket {
std::vector<uint8_t> data;
float rssi;
float snr;
};
struct TxPacket {
std::vector<uint8_t> data;
uint32_t address; // FSK only
};
class RadioDevice : public Device {
public:
enum class Modulation {
Fsk,
LoRa,
LrFhss
};
enum class Parameter {
Power,
Frequency,
Bandwidth,
SpreadFactor,
CodingRate,
SyncWord,
PreambleLength,
FrequencyDeviation,
DataRate,
AddressWidth
};
typedef int RxSubscriptionId;
typedef int TxId;
enum class State {
PendingOn,
On,
Error,
PendingOff,
Off
};
enum class TransmissionState {
Queued,
PendingTransmit,
Transmitted,
Timeout,
Error
};
using TxStateCallback = std::function<void(TxId id, TransmissionState state)>;
protected:
struct TxItem {
TxId id;
TxPacket packet;
TxStateCallback callback;
};
private:
struct RxSubscription {
RxSubscriptionId id;
std::shared_ptr<std::function<void(Device::Id id, const RxPacket&)>> onData;
};
std::string threadName;
size_t threadSize;
State state;
EventFlag events;
Mutex mutex = Mutex(Mutex::Type::Recursive);
std::unique_ptr<Thread> _Nullable thread;
bool threadInterrupted = false;
std::vector<RxSubscription> rxSubscriptions;
std::deque<TxItem> txQueue;
TxId lastTxId = 0;
RxSubscriptionId lastRxSubscriptionId = 0;
protected:
static constexpr auto RADIO_TERMINATE_BIT = BIT0;
static constexpr auto RADIO_TRANSMIT_QUEUED_BIT = BIT1;
static constexpr auto RADIO_RECEIVED_BIT = BIT2;
static constexpr auto RADIO_TRANSMITTED_BIT = BIT3;
virtual int32_t threadMain(const Modulation modulation) = 0;
Mutex &getMutex() { return mutex; }
EventFlag &getEventFlag() { return events; }
bool isThreadInterrupted() const;
void setState(State newState);
size_t getTxQueueSize() const {
auto lock = mutex.asScopedLock();
lock.lock();
const auto size = txQueue.size();
return size;
}
TxItem popNextQueuedTx() {
auto lock = mutex.asScopedLock();
lock.lock();
auto tx = std::move(txQueue.front());
txQueue.pop_front();
return tx;
}
void publishRx(const RxPacket& packet);
public:
explicit RadioDevice(const std::string& threadName, const size_t threadSize)
: threadName(threadName)
, threadSize(threadSize)
, state(State::Off) {}
~RadioDevice() override = default;
Type getType() const override { return Type::Radio; }
virtual bool configure(const Parameter parameter, const float value) = 0;
virtual bool isCapableOf(const Modulation modulation) = 0;
bool start(const Modulation modulation);
bool stop();
TxId transmit(const TxPacket& packet, TxStateCallback callback) {
auto lock = mutex.asScopedLock();
lock.lock();
const auto txId = lastTxId;
txQueue.push_back(TxItem{.id = txId, .packet = packet, .callback = callback});
callback(txId, TransmissionState::Queued);
lastTxId++;
getEventFlag().set(RADIO_TRANSMIT_QUEUED_BIT);
return txId;
}
RxSubscriptionId subscribeRx(const std::function<void(Device::Id id, const RxPacket&)>& onData) {
auto lock = mutex.asScopedLock();
lock.lock();
rxSubscriptions.push_back({
.id = ++lastRxSubscriptionId,
.onData = std::make_shared<std::function<void(Device::Id, const RxPacket&)>>(onData)
});
return lastRxSubscriptionId;
}
void unsubscribeRx(RxSubscriptionId subscriptionId) {
auto lock = mutex.asScopedLock();
lock.lock();
std::erase_if(rxSubscriptions, [subscriptionId](auto& subscription) { return subscription.id == subscriptionId; });
}
State getState() const;
};
const char* toString(RadioDevice::Modulation modulation);
const char* toString(RadioDevice::Parameter parameter);
}