initial integration with telem viz
This commit is contained in:
@@ -5,10 +5,16 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
fern = { workspace = true }
|
||||
log = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
ctrlc = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
derive_more = {workspace = true, features = ["display"]}
|
||||
fern = { workspace = true }
|
||||
log = { workspace = true }
|
||||
postcard = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, optional = true, features = ["net"] }
|
||||
tokio-util = {workspace = true, optional = true}
|
||||
|
||||
[features]
|
||||
async = [ "dep:tokio", "dep:tokio-util" ]
|
||||
|
||||
@@ -50,6 +50,12 @@ pub struct CommandHeader<'a> {
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct OwnedCommandHeader {
|
||||
pub name: String,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
pub trait Command: Serialize + DeserializeOwned {}
|
||||
|
||||
impl Command for () {}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
use log::info;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
pub mod command;
|
||||
pub mod logger;
|
||||
pub mod on_drop;
|
||||
pub mod telemetry;
|
||||
pub mod udp;
|
||||
|
||||
@@ -12,10 +11,25 @@ pub mod udp;
|
||||
///
|
||||
/// # Errors
|
||||
/// If a system error occurred while trying to set the ctrl-c handler
|
||||
pub fn add_ctrlc_handler(flag: Arc<AtomicBool>) -> anyhow::Result<()> {
|
||||
pub fn add_ctrlc_handler_arc(
|
||||
flag: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||
) -> anyhow::Result<()> {
|
||||
ctrlc::set_handler(move || {
|
||||
info!("Shutdown Requested");
|
||||
flag.store(false, Ordering::Relaxed);
|
||||
flag.store(false, std::sync::atomic::Ordering::Relaxed);
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a ctrl-c handler which will set an atomic flag to `false` when ctrl-c is detected
|
||||
///
|
||||
/// # Errors
|
||||
/// If a system error occurred while trying to set the ctrl-c handler
|
||||
#[cfg(feature = "async")]
|
||||
pub fn add_ctrlc_handler_cancel(cancel: tokio_util::sync::CancellationToken) -> anyhow::Result<()> {
|
||||
ctrlc::set_handler(move || {
|
||||
info!("Shutdown Requested");
|
||||
cancel.cancel();
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use anyhow::Result;
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
use log::debug;
|
||||
use log::{LevelFilter, debug};
|
||||
use std::fs::create_dir_all;
|
||||
use std::str::FromStr;
|
||||
use std::{env, thread};
|
||||
@@ -46,6 +46,9 @@ pub fn setup_logger(package_name: &'static str) -> Result<()> {
|
||||
.chain(
|
||||
fern::Dispatch::new()
|
||||
.level(log_level)
|
||||
.level_for("tungstenite", LevelFilter::Warn)
|
||||
.level_for("tokio_tungstenite", LevelFilter::Warn)
|
||||
.level_for("api", LevelFilter::Info)
|
||||
.chain(std::io::stdout()),
|
||||
)
|
||||
.chain(fern::log_file(log_file.clone())?)
|
||||
|
||||
18
common/src/on_drop.rs
Normal file
18
common/src/on_drop.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use log::trace;
|
||||
use std::any::type_name;
|
||||
|
||||
pub struct OnDrop<F: FnMut()> {
|
||||
func: F,
|
||||
}
|
||||
|
||||
pub fn on_drop<F: FnMut()>(func: F) -> OnDrop<F> {
|
||||
trace!("on_drop<F={}>()", type_name::<F>());
|
||||
OnDrop { func }
|
||||
}
|
||||
|
||||
impl<F: FnMut()> Drop for OnDrop<F> {
|
||||
fn drop(&mut self) {
|
||||
trace!("OnDrop<F={}>::drop()", type_name::<F>());
|
||||
(self.func)();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use chrono::serde::ts_nanoseconds;
|
||||
use chrono::{DateTime, Utc};
|
||||
use derive_more::Display;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@@ -9,7 +10,7 @@ pub struct Telemetry {
|
||||
pub message: TelemetryMessage,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[derive(Copy, Clone, Debug, Display, Serialize, Deserialize)]
|
||||
pub enum SwitchBank {
|
||||
A,
|
||||
B,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::command::{Command, CommandHeader};
|
||||
use crate::udp::UdpRecvPostcardError::ExtraData;
|
||||
use crate::udp::UdpSendPostcardError::LengthMismatch;
|
||||
use log::error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::io::ErrorKind;
|
||||
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
||||
@@ -30,7 +29,7 @@ pub enum UdpSendPostcardError {
|
||||
}
|
||||
|
||||
pub trait UdpSocketExt {
|
||||
/// Receive a CBOR encoded message from this UDP Socket
|
||||
/// Receive a Postcard encoded message from this UDP Socket
|
||||
///
|
||||
/// # Errors
|
||||
/// An error that could have occurred while trying to receive this message.
|
||||
@@ -63,26 +62,51 @@ pub trait UdpSocketExt {
|
||||
) -> Result<(), UdpSendPostcardError>;
|
||||
}
|
||||
|
||||
fn recv_postcard_inner<'de, T: Deserialize<'de>>(
|
||||
result: std::io::Result<(usize, SocketAddr)>,
|
||||
buffer: &'de mut [u8],
|
||||
) -> Result<(T, SocketAddr), UdpRecvPostcardError> {
|
||||
match result {
|
||||
Ok((size, addr)) => match postcard::take_from_bytes::<T>(&buffer[..size]) {
|
||||
Ok((res, rem)) => {
|
||||
if !rem.is_empty() {
|
||||
return Err(ExtraData { amount: rem.len() });
|
||||
}
|
||||
Ok((res, addr))
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
},
|
||||
Err(err) => match err.kind() {
|
||||
ErrorKind::WouldBlock | ErrorKind::TimedOut => Err(UdpRecvPostcardError::NoData),
|
||||
_ => Err(err.into()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn send_inner(
|
||||
send_result: Result<usize, std::io::Error>,
|
||||
expected_size: usize,
|
||||
) -> Result<(), UdpSendPostcardError> {
|
||||
match send_result {
|
||||
Ok(size_sent) => {
|
||||
if expected_size != size_sent {
|
||||
return Err(LengthMismatch {
|
||||
expected: expected_size,
|
||||
actual: size_sent,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
impl UdpSocketExt for UdpSocket {
|
||||
fn recv_postcard<'de, T: Deserialize<'de>>(
|
||||
&self,
|
||||
buffer: &'de mut [u8],
|
||||
) -> Result<(T, SocketAddr), UdpRecvPostcardError> {
|
||||
match self.recv_from(buffer) {
|
||||
Ok((size, addr)) => match postcard::take_from_bytes::<T>(&buffer[..size]) {
|
||||
Ok((res, rem)) => {
|
||||
if !rem.is_empty() {
|
||||
return Err(ExtraData { amount: rem.len() });
|
||||
}
|
||||
Ok((res, addr))
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
},
|
||||
Err(err) => match err.kind() {
|
||||
ErrorKind::WouldBlock | ErrorKind::TimedOut => Err(UdpRecvPostcardError::NoData),
|
||||
_ => Err(err.into()),
|
||||
},
|
||||
}
|
||||
recv_postcard_inner(self.recv_from(buffer), buffer)
|
||||
}
|
||||
|
||||
fn send_postcard<T: Serialize + ?Sized, A: ToSocketAddrs>(
|
||||
@@ -91,24 +115,9 @@ impl UdpSocketExt for UdpSocket {
|
||||
buffer: &mut [u8],
|
||||
addr: A,
|
||||
) -> Result<(), UdpSendPostcardError> {
|
||||
match postcard::to_slice(data, buffer) {
|
||||
Ok(result) => {
|
||||
let size_encoded = result.len();
|
||||
match self.send_to(result, addr) {
|
||||
Ok(size_sent) => {
|
||||
if size_encoded != size_sent {
|
||||
return Err(LengthMismatch {
|
||||
expected: size_encoded,
|
||||
actual: size_sent,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
let result = postcard::to_slice(data, buffer)?;
|
||||
let size_encoded = result.len();
|
||||
send_inner(self.send_to(result, addr), size_encoded)
|
||||
}
|
||||
|
||||
fn send_command<T: Command, A: ToSocketAddrs>(
|
||||
@@ -120,21 +129,97 @@ impl UdpSocketExt for UdpSocket {
|
||||
let mut inner_buffer = [0u8; 512];
|
||||
let inner_buffer = postcard::to_slice(data, &mut inner_buffer)?;
|
||||
let mut buffer = [0u8; 512];
|
||||
let buffer = postcard::to_slice(
|
||||
self.send_postcard(
|
||||
&CommandHeader {
|
||||
name,
|
||||
data: inner_buffer,
|
||||
},
|
||||
&mut buffer,
|
||||
)?;
|
||||
let size_encoded = buffer.len();
|
||||
let size_sent = self.send_to(buffer, addr)?;
|
||||
if size_encoded != size_sent {
|
||||
return Err(LengthMismatch {
|
||||
expected: size_encoded,
|
||||
actual: size_sent,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
addr,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
pub mod tokio {
|
||||
use super::{
|
||||
Command, CommandHeader, Deserialize, Serialize, SocketAddr, UdpRecvPostcardError,
|
||||
UdpSendPostcardError, recv_postcard_inner, send_inner,
|
||||
};
|
||||
use ::tokio::net::ToSocketAddrs;
|
||||
use ::tokio::net::UdpSocket;
|
||||
|
||||
pub trait AsyncUdpSocketExt {
|
||||
/// Receive a Postcard encoded message from this UDP Socket
|
||||
///
|
||||
/// # Errors
|
||||
/// An error that could have occurred while trying to receive this message.
|
||||
/// If no data was received a `UdpRecvCborError::NoData` error would be returned.
|
||||
fn recv_postcard<'de, T: Deserialize<'de>>(
|
||||
&self,
|
||||
buffer: &'de mut [u8],
|
||||
) -> impl Future<Output = Result<(T, SocketAddr), UdpRecvPostcardError>>;
|
||||
|
||||
/// Send a CBOR encoded message to an address using this socket
|
||||
///
|
||||
/// # Errors
|
||||
/// An error that could have occurred while trying to send this message
|
||||
fn send_postcard<T: Serialize + ?Sized, A: ToSocketAddrs>(
|
||||
&self,
|
||||
data: &T,
|
||||
buffer: &mut [u8],
|
||||
addr: A,
|
||||
) -> impl Future<Output = Result<(), UdpSendPostcardError>>;
|
||||
|
||||
/// Send a command message to an address using this socket
|
||||
///
|
||||
/// # Errors
|
||||
/// An error that could have occurred while trying to send this message
|
||||
fn send_command<T: Command, A: ToSocketAddrs>(
|
||||
&self,
|
||||
name: &str,
|
||||
data: &T,
|
||||
addr: A,
|
||||
) -> impl Future<Output = Result<(), UdpSendPostcardError>>;
|
||||
}
|
||||
|
||||
impl AsyncUdpSocketExt for UdpSocket {
|
||||
async fn recv_postcard<'de, T: Deserialize<'de>>(
|
||||
&self,
|
||||
buffer: &'de mut [u8],
|
||||
) -> Result<(T, SocketAddr), UdpRecvPostcardError> {
|
||||
recv_postcard_inner(self.recv_from(buffer).await, buffer)
|
||||
}
|
||||
|
||||
async fn send_postcard<T: Serialize + ?Sized, A: ToSocketAddrs>(
|
||||
&self,
|
||||
data: &T,
|
||||
buffer: &mut [u8],
|
||||
addr: A,
|
||||
) -> Result<(), UdpSendPostcardError> {
|
||||
let result = postcard::to_slice(data, buffer)?;
|
||||
let size_encoded = result.len();
|
||||
send_inner(self.send_to(result, addr).await, size_encoded)
|
||||
}
|
||||
|
||||
async fn send_command<T: Command, A: ToSocketAddrs>(
|
||||
&self,
|
||||
name: &str,
|
||||
data: &T,
|
||||
addr: A,
|
||||
) -> Result<(), UdpSendPostcardError> {
|
||||
let mut inner_buffer = [0u8; 512];
|
||||
let inner_buffer = postcard::to_slice(data, &mut inner_buffer)?;
|
||||
let mut buffer = [0u8; 512];
|
||||
self.send_postcard(
|
||||
&CommandHeader {
|
||||
name,
|
||||
data: inner_buffer,
|
||||
},
|
||||
&mut buffer,
|
||||
addr,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user