Ares LoRa Serial Driver Core
Loading...
Searching...
No Matches
ares_lora_serial.hpp
Go to the documentation of this file.
1
10
11#ifndef ARES_ARES_LORA_SERIAL_HPP
12#define ARES_ARES_LORA_SERIAL_HPP
13
15#include <ares/data-structures/queue.hpp>
16#include <ares/serial/serial.hpp>
17#include <ares/synchronization/semaphore.hpp>
18#include <ares/synchronization/spinlock.hpp>
19#include <ares/work-q/task.hpp>
20#include <ares/work-q/work_q.hpp>
21#include <atomic>
22#include <chrono>
23#include <exception>
24#include <functional>
25#include <future>
26#include <mutex>
27#include <pybind11/pybind11.h>
28#include <random>
29#include <string>
30#include <utility>
31
32namespace py = pybind11;
33using namespace std::chrono_literals;
34
39class AresTimeoutError : public std::exception {
40 public:
45 explicit AresTimeoutError(std::string msg) : _msg(std::move(msg)) {}
46
51 [[nodiscard]] const char *what() const noexcept override {
52 return _msg.c_str();
53 }
54
55 private:
56 std::string _msg;
57};
58
64struct AresSerialConfigs {
65 AresSerialConfigs() = default;
66
71 explicit AresSerialConfigs(const py::kwargs &kwargs);
72
76 std::string port;
77
81 std::chrono::milliseconds serial_timeout = 100ms;
82
86 std::chrono::milliseconds response_timeout = 2000ms;
87
91 std::chrono::milliseconds rx_period = 100ms;
92
96 bool master = false;
97
103 std::function<void(int64_t, uint64_t, uint16_t, bool, uint8_t, uint16_t)>
104 start_callback = nullptr;
105
110 std::function<void(uint16_t, bool, bool)> heartbeat_callback = nullptr;
111
116 std::function<void(uint16_t)> claim_callback = nullptr;
117
122 std::function<void(uint16_t, uint16_t, uint8_t, uint8_t,
123 const std::string &)>
124 log_callback = nullptr;
125
129 double alpha = 1.0;
130
134 double beta = 2.0;
135
140 std::function<void(uint8_t, uint16_t, uint16_t)> packet_rx_callback =
141 nullptr;
142
147 std::function<void(uint32_t)> packet_tx_callback = nullptr;
148};
149
155struct AresLoraConfig {
156 AresLoraConfig() = default;
157
162 explicit AresLoraConfig(const py::kwargs &kwargs);
163
167 uint32_t frequency = 0;
168
172 uint16_t preamble_length = 0;
173
177 uint8_t bandwidth = 0;
178
182 uint8_t datarate = 0;
183
187 uint8_t coding_rate = 0;
188
192 int8_t tx_power = 0;
193
198 [[nodiscard]] AresFrame generate_frame() const;
199};
200
207 public:
212 explicit AresSerial(const AresSerialConfigs &configs);
213
219
226 int setting_set(uint16_t id, uint32_t value);
227
233 py::tuple setting_get(uint16_t id);
234
244 int send_start(int64_t sec, uint64_t nsec, uint16_t id, bool broadcast);
245
251 int lora_config(const AresLoraConfig &config);
252
257 void set_response_timeout(const std::chrono::milliseconds &timeout);
258
263 [[nodiscard]] std::chrono::milliseconds get_response_timeout() const;
264
271 py::tuple led(uint8_t id, uint8_t state);
272
279 int send_heartbeat(bool ready, uint8_t tx_cnt);
280
299 py::tuple send_log(const std::string &log_msg, bool broadcast,
300 uint8_t tx_cnt, uint16_t id);
301
307 py::tuple version();
308
313 void set_logging_level(uint32_t level);
314
318 void start();
319
323 void stop();
324
325 private:
326 Serial::Serial _serial;
327 WorkQ _work_q;
328 SpinLock _command_lock;
329 std::exception_ptr _exception;
330
331 void _check_crash();
332
333 std::chrono::milliseconds _response_timeout;
334 std::chrono::milliseconds _rx_period;
335
336 void _process_frames_helper();
337 void _process_frames();
338
339 void _process_rx_buffer(std::vector<uint8_t> &buf);
340 void _read_serial_helper();
341 void _read_serial();
342
343 struct AresResponse {
344 enum ResponseType {
345 COMMAND_SPECIFIC, // Command specific response (example: setting
346 // command)
347 ACK, // AresFrameAckErrorCode
348 BAD_FRAME, // AresFrameFramingError
349 };
350
351 ResponseType type;
353 };
354
355 void _publish_response(const AresFrame::Decoded &frame);
356 void _send_frame(const std::vector<uint8_t> &tx);
357 AresResponse _send_frame(AresFrame &frame,
358 const std::chrono::milliseconds &timeout);
359 void _send_multi_frame(AresFrame &frame,
360 const std::chrono::milliseconds &timeout,
361 std::vector<AresResponse> &responses);
362 AresResponse _wait_response(const std::chrono::milliseconds &timeout);
363 AresResponse
364 _wait_response_timeout(const std::chrono::milliseconds &timeout);
365 AresResponse _wait_response_forever();
366
367 std::atomic_bool _tasks_running = false;
368
369 Task<void()> _rx_task;
370 ares::bounded_queue<AresFrame::Decoded, 10, true> _frame_q;
371
372 Task<void()> _processing_task;
373 ares::bounded_queue<AresResponse> _response_queue;
374
375 std::recursive_mutex _serial_lock;
376
377 std::function<void(int64_t, uint64_t, uint16_t, bool, uint8_t, uint16_t)>
378 _start_callback = nullptr;
379 void _start_event(const AresFrame::Start &start_frame) const;
380
381 static void _handle_bad_frame(const AresResponse &response);
382
383 struct HeartbeatWork {
384 HeartbeatWork(work_handler_t handler, AresSerial *ctx)
385 : work(std::move(handler)), obj(ctx) {}
386 ~HeartbeatWork() { work.work_flush(); }
387 Work work;
388 AresSerial *obj;
389 uint16_t id = 0;
390 ares::semaphore<> sem{};
391 };
392
393 bool _master;
394 uint16_t _claimed_host = 0;
395 std::function<void(uint16_t, bool, bool)> _heartbeat_callback = nullptr;
396 void _heartbeat_event(const AresFrame::Heartbeat &heartbeat);
397 static void _heartbeat_handler(Work *work);
398 HeartbeatWork _heartbeat_work;
399 int _heartbeat_claim_host(uint16_t destination_id);
400
401 std::function<void(uint16_t)> _claim_callback = nullptr;
402 void _claim_event(const AresFrame::Claim &claim);
403
404 SpinLock _log_spinlock;
405 uint16_t _log_id = 0;
406 ares::bounded_queue<AresFrame::LogAck> _log_ack_signal;
407 void _log_ack_event(const AresFrame::LogAck &ack);
408 bool _log_ack_event_wait(const std::chrono::milliseconds &timeout,
409 size_t part, size_t num_parts, uint16_t id);
410 void _send_log_frame_directed(AresFrame &frame,
411 const std::chrono::milliseconds &ack_timeout,
412 size_t max_attempts,
413 std::vector<AresResponse> &responses,
414 uint16_t target);
415 std::gamma_distribution<double> _mac_backoff;
416 std::random_device _rd;
417 std::mt19937 _generator;
418 void _handle_ack(uint16_t target, bool acked);
419 std::function<void(uint16_t, uint16_t, uint8_t, uint8_t,
420 const std::string &msg)>
421 _log_callback = nullptr;
422 void _log_event(const AresFrame::Log &log) const;
423
424 static py::tuple _decode_version(uint32_t version_num);
425
426 static void _debug_event(const AresFrame::Dbg &msg);
427
428 std::function<void(uint8_t, uint16_t, uint16_t)> _pkt_rx_cb = nullptr;
429 void _packet_rx_event(const AresFrame::PktRx &msg) const;
430
431 std::function<void(uint32_t)> _pkt_tx_cb = nullptr;
432 void _packet_tx_event(const AresFrame::PktTx &msg) const;
433};
434
435#endif // ARES_ARES_LORA_SERIAL_HPP
Ares Frame Library for host computers.
Definition ares_frame.hpp:54
std::variant< std::monostate, Setting, AckErrorCode, FramingError, Led, Version > ResponseTypes
Definition ares_frame.hpp:482
py::tuple setting_get(uint16_t id)
AresSerial(const AresSerialConfigs &configs)
int send_heartbeat(bool ready, uint8_t tx_cnt)
void stop()
std::chrono::milliseconds get_response_timeout() const
int setting_set(uint16_t id, uint32_t value)
void set_response_timeout(const std::chrono::milliseconds &timeout)
int lora_config(const AresLoraConfig &config)
py::tuple led(uint8_t id, uint8_t state)
py::tuple version()
void start()
int send_start(int64_t sec, uint64_t nsec, uint16_t id, bool broadcast)
void set_logging_level(uint32_t level)
py::tuple send_log(const std::string &log_msg, bool broadcast, uint8_t tx_cnt, uint16_t id)
AresTimeoutError(std::string msg)
Definition ares_lora_serial.hpp:45
const char * what() const noexcept override
Definition ares_lora_serial.hpp:51
Definition ares_lora_serial.hpp:155
AresLoraConfig(const py::kwargs &kwargs)
uint8_t datarate
Definition ares_lora_serial.hpp:182
uint32_t frequency
Definition ares_lora_serial.hpp:167
AresFrame generate_frame() const
uint8_t bandwidth
Definition ares_lora_serial.hpp:177
uint8_t coding_rate
Definition ares_lora_serial.hpp:187
int8_t tx_power
Definition ares_lora_serial.hpp:192
uint16_t preamble_length
Definition ares_lora_serial.hpp:172
Definition ares_lora_serial.hpp:64
std::function< void(uint32_t)> packet_tx_callback
Definition ares_lora_serial.hpp:147
std::chrono::milliseconds response_timeout
Definition ares_lora_serial.hpp:86
std::function< void(uint16_t, uint16_t, uint8_t, uint8_t, const std::string &)> log_callback
Definition ares_lora_serial.hpp:124
double beta
Definition ares_lora_serial.hpp:134
std::function< void(uint8_t, uint16_t, uint16_t)> packet_rx_callback
Definition ares_lora_serial.hpp:140
std::function< void(uint16_t)> claim_callback
Definition ares_lora_serial.hpp:116
std::string port
Definition ares_lora_serial.hpp:76
std::function< void(int64_t, uint64_t, uint16_t, bool, uint8_t, uint16_t)> start_callback
Definition ares_lora_serial.hpp:104
std::chrono::milliseconds serial_timeout
Definition ares_lora_serial.hpp:81
double alpha
Definition ares_lora_serial.hpp:129
std::chrono::milliseconds rx_period
Definition ares_lora_serial.hpp:91
bool master
Definition ares_lora_serial.hpp:96
std::function< void(uint16_t, bool, bool)> heartbeat_callback
Definition ares_lora_serial.hpp:110
AresSerialConfigs(const py::kwargs &kwargs)