use prosa::core::{
adaptor::Adaptor,
error::ProcError,
msg::InternalMsg,
proc::{Proc, ProcBusParam as _, proc, proc_settings},
};
use serde::{Deserialize, Serialize};
use crate::{
adaptor::TeleinfoAdaptor,
observability::TeleinfoObservability,
teleinfo::{RateColor, RatePeriod, Teleinfo, TeleinfoFrame},
};
#[proc_settings]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TeleinfoSettings {
serial_path: Option<String>,
#[serde(default = "TeleinfoSettings::default_legacy")]
legacy: bool,
#[serde(default = "TeleinfoSettings::default_price_base")]
price_base: f64,
#[serde(default = "TeleinfoSettings::default_price_hc_hp")]
price_hc_hp: (f64, f64),
#[serde(default = "TeleinfoSettings::default_price_tempo")]
price_tempo: (f64, f64, f64, f64, f64, f64),
}
impl TeleinfoSettings {
fn default_legacy() -> bool {
false
}
fn default_price_base() -> f64 {
19.40f64
}
fn default_price_hc_hp() -> (f64, f64) {
(15.79f64, 20.65f64)
}
fn default_price_tempo() -> (f64, f64, f64, f64, f64, f64) {
(13.25f64, 16.12f64, 14.99f64, 18.71f64, 15.75f64, 70.60f64)
}
pub fn get_serial_path(&self) -> Result<String, tokio_serial::Error> {
if let Some(path) = &self.serial_path {
Ok(path.clone())
} else if let Some(info) = tokio_serial::available_ports()?.first() {
Ok(info.port_name.clone())
} else {
Err(tokio_serial::Error::new(
tokio_serial::ErrorKind::NoDevice,
"No serial port available for Teleinfo",
))
}
}
pub fn is_legacy(&self) -> bool {
self.legacy
}
pub fn get_price(&self, color: RateColor, period: RatePeriod) -> f64 {
match color {
RateColor::NONE => match period {
RatePeriod::TH => self.price_base,
RatePeriod::HC => self.price_hc_hp.0,
RatePeriod::HP => self.price_hc_hp.1,
_ => 0f64,
},
RateColor::BLUE => match period {
RatePeriod::HC => self.price_tempo.0,
RatePeriod::HP => self.price_tempo.1,
_ => 0f64,
},
RateColor::WHITE => match period {
RatePeriod::HC => self.price_tempo.2,
RatePeriod::HP => self.price_tempo.3,
_ => 0f64,
},
RateColor::RED => match period {
RatePeriod::HC => self.price_tempo.4,
RatePeriod::HP => self.price_tempo.5,
_ => 0f64,
},
}
}
}
#[proc_settings]
impl Default for TeleinfoSettings {
fn default() -> Self {
TeleinfoSettings {
serial_path: None,
legacy: Self::default_legacy(),
price_base: Self::default_price_base(),
price_hc_hp: Self::default_price_hc_hp(),
price_tempo: Self::default_price_tempo(),
}
}
}
#[proc(settings = TeleinfoSettings)]
pub struct TeleinfoProc {}
#[proc]
impl<A> Proc<A> for TeleinfoProc
where
A: Adaptor + TeleinfoAdaptor<M> + std::marker::Send,
{
async fn internal_run(&mut self) -> Result<(), Box<dyn ProcError + Send + Sync>> {
let mut serial = Teleinfo::new(&self.settings)?;
let mut adaptor = A::new(self)?;
self.proc.add_proc().await?;
let mut observability: Option<TeleinfoObservability> = None;
loop {
tokio::select! {
frame_ret = serial.read() => {
let frame = frame_ret?;
if let Some(observ) = &mut observability {
observ.process_teleinfo(&frame);
} else if let TeleinfoFrame::PRM(name) = &frame {
observability = Some(TeleinfoObservability::new(name.clone(), self))
} else if let TeleinfoFrame::ADCO(name) = &frame {
observability = Some(TeleinfoObservability::new(name.clone(), self))
}
adaptor.process_teleinfo(frame)?
},
Some(msg) = self.internal_rx_queue.recv() => {
match msg {
InternalMsg::Request(msg) => panic!(
"The teleinfo processor {} should not receive a request {:?}",
self.get_proc_id(),
msg
),
InternalMsg::Response(msg) => adaptor.process_response(msg)?,
InternalMsg::Error(err) => adaptor.process_error(err)?,
InternalMsg::Command(_) => todo!(),
InternalMsg::Config => todo!(),
InternalMsg::Service(table) => self.service = table,
InternalMsg::Shutdown => {
adaptor.terminate();
self.proc.remove_proc(None).await?;
return Ok(());
}
}
}
}
}
}
}