use std::time::Instant;
use epics_base_rs::error::CaResult;
use epics_base_rs::server::device_support::{DeviceReadOutcome, DeviceSupport};
use epics_base_rs::server::record::Record;
use crate::records::epid::EpidRecord;
pub struct EpidSoftDeviceSupport;
impl Default for EpidSoftDeviceSupport {
fn default() -> Self {
Self::new()
}
}
impl EpidSoftDeviceSupport {
pub fn new() -> Self {
Self
}
pub fn do_pid(epid: &mut EpidRecord) {
let pcval = epid.cval;
let setp = epid.val;
let cval = epid.cval;
let ctp = epid.ct;
let ct = Instant::now();
let dt = ct.duration_since(ctp).as_secs_f64();
if dt < epid.mdt {
return;
}
let kp = epid.kp;
let ki = epid.ki;
let kd = epid.kd;
let ep = epid.err;
let mut oval = epid.oval;
let mut p = epid.p;
let mut i = epid.i;
let mut d = epid.d;
let mut e = 0.0;
match epid.fmod {
0 => {
e = setp - cval;
let de = e - ep;
p = kp * e;
let di = kp * ki * e * dt;
if epid.fbon != 0 {
if epid.fbop == 0 {
i = epid.oval;
} else {
if (oval > epid.drvl && oval < epid.drvh)
|| (oval >= epid.drvh && di < 0.0)
|| (oval <= epid.drvl && di > 0.0)
{
i += di;
if i < epid.drvl {
i = epid.drvl;
}
if i > epid.drvh {
i = epid.drvh;
}
}
}
}
if ki == 0.0 {
i = 0.0;
}
d = if dt > 0.0 { kp * kd * (de / dt) } else { 0.0 };
oval = p + i + d;
}
1 => {
if epid.fbon != 0 {
if epid.fbop == 0 {
oval = epid.oval;
} else {
e = cval - pcval;
let sign = if d > 0.0 { 1.0 } else { -1.0 };
let sign = if (kp > 0.0 && e < 0.0) || (kp < 0.0 && e > 0.0) {
-sign
} else {
sign
};
d = kp * sign;
oval = epid.oval + d;
}
}
}
_ => {
tracing::warn!("Invalid feedback mode {} in epid record", epid.fmod);
}
}
if oval > epid.drvh {
oval = epid.drvh;
}
if oval < epid.drvl {
oval = epid.drvl;
}
epid.ct = ct;
epid.dt = dt;
epid.err = e;
epid.cval = cval;
if epid.odel == 0.0 || (epid.oval - oval).abs() > epid.odel {
epid.oval = oval;
}
epid.p = p;
epid.i = i;
epid.d = d;
epid.fbop = epid.fbon;
}
}
impl DeviceSupport for EpidSoftDeviceSupport {
fn dtyp(&self) -> &str {
"Epid Soft"
}
fn read(&mut self, record: &mut dyn Record) -> CaResult<DeviceReadOutcome> {
let epid = record
.as_any_mut()
.and_then(|a| a.downcast_mut::<EpidRecord>())
.expect("EpidSoftDeviceSupport requires an EpidRecord");
Self::do_pid(epid);
Ok(DeviceReadOutcome::computed())
}
fn write(&mut self, _record: &mut dyn Record) -> CaResult<()> {
Ok(())
}
}