pub mod msg;
pub mod ygw_server;
pub mod nodes {
#[cfg(feature = "ping")]
pub mod pingnode;
pub mod shellcmd;
pub mod tc_udp;
pub mod tm_udp;
#[cfg(feature = "serial")]
pub mod tmtc_serial;
#[cfg(feature = "socketcan")]
pub mod ygw_socketcan;
pub mod relay_node;
}
pub mod protobuf;
pub mod record_file;
pub mod recorder;
pub mod replay_server;
pub mod utc_converter;
use std::{io, sync::atomic::AtomicU32};
use async_trait::async_trait;
use msg::{Addr, YgwMessage, ACKNOWLEDGE_SENT_KEY, COMMAND_COMPLETE_KEY};
use protobuf::ygw::{command_ack::AckStatus, CommandId};
use thiserror::Error;
use tokio::{
sync::mpsc::{Receiver, Sender},
task::JoinError,
};
static PARAMETER_ID_GENERATOR: AtomicU32 = AtomicU32::new(0);
pub type Result<T> = std::result::Result<T, YgwError>;
#[derive(Error, Debug)]
pub enum YgwError {
#[error("{0}: {1}")]
IOError(String, std::io::Error),
#[error("Device access error: {0}")]
DeviceAccessError(String),
#[error("parse error: {0}")]
ParseError(String),
#[error("decoding error: {0}")]
DecodeError(String),
#[error("node {0} has closed its channel")]
TargetChannelClosed(u32),
#[error("server is shutting down")]
ServerShutdown,
#[error("error converting: {0} to {1}")]
ConversionError(String, String),
#[error("recording file full; max number of segments {0} reached")]
RecordingFileFull(u32),
#[error("recording file is corrupted:{0}")]
CorruptedRecordingFile(String),
#[error("command error: {0}")]
CommandError(String),
#[error("{0}")]
Generic(String),
#[error("{0}")]
TerminationError(String),
#[cfg(feature = "socketcan")]
#[error(transparent)]
SocketCanError(#[from] socketcan::Error),
#[error("Cannot resolve: {0}: {1}")]
Unresolvable(String, u16),
#[error("{0}")]
Other(#[from] Box<dyn std::error::Error + Send + Sync>),
}
impl From<io::Error> for YgwError {
fn from(err: io::Error) -> Self {
YgwError::IOError("".into(), err)
}
}
impl From<JoinError> for YgwError {
fn from(err: JoinError) -> Self {
YgwError::TerminationError(err.to_string())
}
}
#[async_trait]
pub trait YgwNode: Send {
fn properties(&self) -> &YgwLinkNodeProperties;
fn sub_links(&self) -> &[Link] {
&[]
}
async fn run(
mut self: Box<Self>,
node_id: u32,
tx: Sender<YgwMessage>,
mut rx: Receiver<YgwMessage>,
) -> Result<()>;
}
#[derive(Clone, Debug)]
pub struct YgwLinkNodeProperties {
pub name: String,
pub description: String,
pub tm_packet: bool,
pub tc: bool,
pub tm_frame: bool,
pub tc_frame: bool,
}
impl YgwLinkNodeProperties {
pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
Self {
name: name.into(),
description: description.into(),
tm_packet: false,
tc: false,
tm_frame: false,
tc_frame: false,
}
}
pub fn tm_packet(mut self, value: bool) -> Self {
self.tm_packet = value;
self
}
pub fn tc(mut self, value: bool) -> Self {
self.tc = value;
self
}
pub fn tm_frame(mut self, value: bool) -> Self {
self.tm_frame = value;
self
}
pub fn tc_frame(mut self, value: bool) -> Self {
self.tc_frame = value;
self
}
}
#[derive(Clone, Debug)]
pub struct Link {
pub id: u32,
pub props: YgwLinkNodeProperties,
}
impl Link {
fn to_proto(&self) -> protobuf::ygw::Link {
protobuf::ygw::Link {
id: self.id,
name: self.props.name.clone(),
description: Some(self.props.description.clone()),
tm_packet: if self.props.tm_packet {
Some(true)
} else {
None
},
tc: if self.props.tc { Some(true) } else { None },
tm_frame: if self.props.tm_frame {
Some(true)
} else {
None
},
tc_frame: if self.props.tc_frame {
Some(true)
} else {
None
},
}
}
}
pub struct LinkStatus {
addr: Addr,
inner: protobuf::ygw::LinkStatus,
}
impl LinkStatus {
pub fn new(addr: Addr) -> Self {
LinkStatus {
addr,
inner: protobuf::ygw::LinkStatus {
data_in_count: 0,
data_out_count: 0,
data_in_size: 0,
data_out_size: 0,
state: protobuf::ygw::LinkState::Ok as i32,
err: None,
},
}
}
pub fn data_in(&mut self, count: u64, size: u64) {
self.inner.data_in_count += count;
self.inner.data_in_size += size;
}
pub fn data_out(&mut self, count: u64, size: u64) {
self.inner.data_out_count += count;
self.inner.data_out_size += size;
}
pub fn change_state(&mut self, state: i32, err: Option<String>) {
self.inner.state = state;
self.inner.err = err;
}
pub fn state_ok(&mut self) {
self.inner.state = protobuf::ygw::LinkState::Ok as i32;
self.inner.err = None;
}
pub fn state_failed(&mut self, msg: String) {
self.inner.state = protobuf::ygw::LinkState::Failed as i32;
self.inner.err = Some(msg);
}
pub async fn send(&self, tx: &Sender<YgwMessage>) -> Result<()> {
tx.send(YgwMessage::LinkStatus(self.addr, self.inner.clone()))
.await
.map_err(|_| YgwError::ServerShutdown)
}
pub fn blocking_send(&self, tx: &Sender<YgwMessage>) -> Result<()> {
tx.blocking_send(YgwMessage::LinkStatus(self.addr, self.inner.clone()))
.map_err(|_| YgwError::ServerShutdown)
}
pub fn addr(&self) -> Addr {
self.addr
}
pub fn data_in_count(&self) -> u64 {
self.inner.data_in_count
}
pub fn data_out_count(&self) -> u64 {
self.inner.data_out_count
}
}
pub async fn ack_command(
tx: &mut Sender<YgwMessage>,
link_addr: Addr,
command_id: CommandId,
message: Option<String>,
) -> Result<()> {
let ack = protobuf::ygw::CommandAck {
command_id: command_id,
ack: AckStatus::Ok as i32,
key: ACKNOWLEDGE_SENT_KEY.into(),
time: protobuf::now(),
message: message,
return_pv: None,
};
tx.send(YgwMessage::TcAck(link_addr, ack))
.await
.map_err(|_| YgwError::ServerShutdown)
}
pub async fn nack_command(
tx: &mut Sender<YgwMessage>,
link_addr: Addr,
command_id: CommandId,
message: String,
) -> Result<()> {
let ack = protobuf::ygw::CommandAck {
command_id: command_id,
ack: AckStatus::Nok as i32,
key: ACKNOWLEDGE_SENT_KEY.into(),
time: protobuf::now(),
message: Some(message),
return_pv: None,
};
tx.send(YgwMessage::TcAck(link_addr, ack))
.await
.map_err(|_| YgwError::ServerShutdown)
}
pub async fn fail_command(
tx: &mut Sender<YgwMessage>,
link_addr: Addr,
command_id: CommandId,
message: String,
) -> Result<()> {
nack_command(tx, link_addr, command_id.clone(), message.clone()).await?;
let ack = protobuf::ygw::CommandAck {
command_id: command_id,
ack: AckStatus::Nok as i32,
key: COMMAND_COMPLETE_KEY.into(),
time: protobuf::now(),
message: Some(message),
return_pv: None,
};
tx.send(YgwMessage::TcAck(link_addr, ack))
.await
.map_err(|_| YgwError::ServerShutdown)
}
pub fn generate_pids(num_pids: u32) -> u32 {
PARAMETER_ID_GENERATOR.fetch_add(num_pids, std::sync::atomic::Ordering::Relaxed)
}
pub fn hex8(data: &[u8]) -> String {
let hex_strings: Vec<String> = data.iter().map(|x| format!("{:02X}", x)).collect();
hex_strings.join(" ")
}