use crate::error::{CaError, CaResult};
use crate::server::record::{FieldDesc, ProcessOutcome, Record};
use crate::types::{DbFieldType, EpicsValue};
pub struct CompressRecord {
pub val: Vec<f64>,
pub nsam: i32, pub inp: String, pub alg: i16, pub n: i32, pub nuse: i32, pub off: i32, pub res: i16, pub balg: i16, accum: Vec<f64>,
}
impl Default for CompressRecord {
fn default() -> Self {
Self {
val: vec![0.0; 10],
nsam: 10,
inp: String::new(),
alg: 3, n: 1,
nuse: 0,
off: 0,
res: 0,
balg: 0,
accum: Vec::new(),
}
}
}
impl CompressRecord {
pub fn new(nsam: i32, alg: i16) -> Self {
Self {
val: vec![0.0; nsam as usize],
nsam,
alg,
..Default::default()
}
}
pub fn push_value(&mut self, input: f64) {
match self.alg {
3 => {
let idx = self.off as usize % self.nsam as usize;
self.val[idx] = input;
self.off += 1;
if (self.nuse as usize) < self.nsam as usize {
self.nuse += 1;
}
}
_ => {
self.accum.push(input);
if self.accum.len() >= self.n as usize {
let compressed = match self.alg {
0 => self.accum.iter().cloned().fold(f64::INFINITY, f64::min), 1 => self.accum.iter().cloned().fold(f64::NEG_INFINITY, f64::max), 2 => self.accum.iter().sum::<f64>() / self.accum.len() as f64, _ => 0.0,
};
let idx = self.off as usize % self.nsam as usize;
self.val[idx] = compressed;
self.off += 1;
if (self.nuse as usize) < self.nsam as usize {
self.nuse += 1;
}
self.accum.clear();
}
}
}
}
}
static COMPRESS_FIELDS: &[FieldDesc] = &[
FieldDesc {
name: "VAL",
dbf_type: DbFieldType::Double,
read_only: false,
},
FieldDesc {
name: "NSAM",
dbf_type: DbFieldType::Long,
read_only: true,
},
FieldDesc {
name: "ALG",
dbf_type: DbFieldType::Short,
read_only: false,
},
FieldDesc {
name: "N",
dbf_type: DbFieldType::Long,
read_only: false,
},
FieldDesc {
name: "OFF",
dbf_type: DbFieldType::Long,
read_only: true,
},
FieldDesc {
name: "NUSE",
dbf_type: DbFieldType::Long,
read_only: true,
},
FieldDesc {
name: "RES",
dbf_type: DbFieldType::Short,
read_only: false,
},
FieldDesc {
name: "BALG",
dbf_type: DbFieldType::Short,
read_only: false,
},
];
impl Record for CompressRecord {
fn record_type(&self) -> &'static str {
"compress"
}
fn process(&mut self) -> CaResult<ProcessOutcome> {
if self.res != 0 {
self.off = 0;
self.nuse = 0;
for v in &mut self.val {
*v = 0.0;
}
self.res = 0;
}
Ok(ProcessOutcome::complete())
}
fn get_field(&self, name: &str) -> Option<EpicsValue> {
match name {
"VAL" => Some(EpicsValue::DoubleArray(self.val.clone())),
"INP" => Some(EpicsValue::String(self.inp.clone())),
"NSAM" => Some(EpicsValue::Long(self.nsam)),
"NUSE" => Some(EpicsValue::Long(self.nuse)),
"RES" => Some(EpicsValue::Short(self.res)),
"BALG" => Some(EpicsValue::Short(self.balg)),
"ALG" => Some(EpicsValue::Short(self.alg)),
"N" => Some(EpicsValue::Long(self.n)),
"OFF" => Some(EpicsValue::Long(self.off)),
_ => None,
}
}
fn put_field(&mut self, name: &str, value: EpicsValue) -> CaResult<()> {
match name {
"VAL" => match value {
EpicsValue::DoubleArray(arr) => {
self.val = arr;
Ok(())
}
EpicsValue::Double(v) => {
self.push_value(v);
Ok(())
}
_ => Err(CaError::TypeMismatch("VAL".into())),
},
"ALG" => match value {
EpicsValue::Short(v) => {
self.alg = v;
Ok(())
}
_ => Err(CaError::TypeMismatch("ALG".into())),
},
"N" => match value {
EpicsValue::Long(v) => {
self.n = v;
Ok(())
}
_ => Err(CaError::TypeMismatch("N".into())),
},
"RES" => match value {
EpicsValue::Short(v) => {
self.res = v;
Ok(())
}
_ => Err(CaError::TypeMismatch("RES".into())),
},
"BALG" => match value {
EpicsValue::Short(v) => {
self.balg = v;
Ok(())
}
_ => Err(CaError::TypeMismatch("BALG".into())),
},
"NSAM" | "OFF" | "NUSE" => Err(CaError::ReadOnlyField(name.to_string())),
_ => Err(CaError::FieldNotFound(name.to_string())),
}
}
fn field_list(&self) -> &'static [FieldDesc] {
COMPRESS_FIELDS
}
fn primary_field(&self) -> &'static str {
"VAL"
}
}