use crate::error::{CaError, CaResult};
use crate::server::record::{FieldDesc, Record};
use crate::types::{DbFieldType, EpicsValue};
pub struct WaveformRecord {
pub val: EpicsValue,
pub nelm: i32,
pub nord: i32,
pub ftvl: i16,
pub mpst: i16, pub apst: i16, pub hash: u32, pub busy: bool, pub egu: String,
pub hopr: f64,
pub lopr: f64,
pub prec: i16,
}
const MENU_FTYPE_DOUBLE: i16 = 10;
impl Default for WaveformRecord {
fn default() -> Self {
Self {
val: EpicsValue::DoubleArray(Vec::new()),
nelm: 1,
nord: 0,
ftvl: MENU_FTYPE_DOUBLE,
mpst: 0,
apst: 0,
hash: 0,
busy: false,
egu: String::new(),
hopr: 0.0,
lopr: 0.0,
prec: 0,
}
}
}
impl WaveformRecord {
pub fn new(nelm: i32, ftvl: DbFieldType) -> Self {
let (val, ftvl_idx) = match ftvl {
DbFieldType::Char => (EpicsValue::CharArray(vec![0; nelm as usize]), 1), DbFieldType::Short => (EpicsValue::ShortArray(vec![0; nelm as usize]), 3), DbFieldType::Long => (EpicsValue::LongArray(vec![0; nelm as usize]), 5), DbFieldType::Float => (EpicsValue::FloatArray(vec![0.0; nelm as usize]), 9), DbFieldType::Double => (EpicsValue::DoubleArray(vec![0.0; nelm as usize]), 10), _ => (EpicsValue::DoubleArray(vec![0.0; nelm as usize]), 10),
};
Self {
val,
nelm,
nord: 0,
ftvl: ftvl_idx,
..Default::default()
}
}
fn reallocate_val(&mut self) {
let n = self.nelm.max(0) as usize;
self.val = match self.ftvl {
1 | 2 => EpicsValue::CharArray(vec![0; n]), 3 | 4 => EpicsValue::ShortArray(vec![0; n]), 5 | 6 => EpicsValue::LongArray(vec![0; n]), 9 => EpicsValue::FloatArray(vec![0.0; n]), _ => EpicsValue::DoubleArray(vec![0.0; n]), };
self.nord = 0;
}
}
static WAVEFORM_FIELDS_CHAR: &[FieldDesc] = &[
FieldDesc {
name: "VAL",
dbf_type: DbFieldType::Char,
read_only: false,
},
FieldDesc {
name: "NELM",
dbf_type: DbFieldType::Long,
read_only: false,
},
FieldDesc {
name: "NORD",
dbf_type: DbFieldType::Long,
read_only: true,
},
FieldDesc {
name: "FTVL",
dbf_type: DbFieldType::Short,
read_only: false,
},
];
static WAVEFORM_FIELDS_SHORT: &[FieldDesc] = &[
FieldDesc {
name: "VAL",
dbf_type: DbFieldType::Short,
read_only: false,
},
FieldDesc {
name: "NELM",
dbf_type: DbFieldType::Long,
read_only: false,
},
FieldDesc {
name: "NORD",
dbf_type: DbFieldType::Long,
read_only: true,
},
FieldDesc {
name: "FTVL",
dbf_type: DbFieldType::Short,
read_only: false,
},
];
static WAVEFORM_FIELDS_LONG: &[FieldDesc] = &[
FieldDesc {
name: "VAL",
dbf_type: DbFieldType::Long,
read_only: false,
},
FieldDesc {
name: "NELM",
dbf_type: DbFieldType::Long,
read_only: false,
},
FieldDesc {
name: "NORD",
dbf_type: DbFieldType::Long,
read_only: true,
},
FieldDesc {
name: "FTVL",
dbf_type: DbFieldType::Short,
read_only: false,
},
];
static WAVEFORM_FIELDS_FLOAT: &[FieldDesc] = &[
FieldDesc {
name: "VAL",
dbf_type: DbFieldType::Float,
read_only: false,
},
FieldDesc {
name: "NELM",
dbf_type: DbFieldType::Long,
read_only: false,
},
FieldDesc {
name: "NORD",
dbf_type: DbFieldType::Long,
read_only: true,
},
FieldDesc {
name: "FTVL",
dbf_type: DbFieldType::Short,
read_only: false,
},
];
static WAVEFORM_FIELDS_DOUBLE: &[FieldDesc] = &[
FieldDesc {
name: "VAL",
dbf_type: DbFieldType::Double,
read_only: false,
},
FieldDesc {
name: "NELM",
dbf_type: DbFieldType::Long,
read_only: false,
},
FieldDesc {
name: "NORD",
dbf_type: DbFieldType::Long,
read_only: true,
},
FieldDesc {
name: "FTVL",
dbf_type: DbFieldType::Short,
read_only: false,
},
];
impl Record for WaveformRecord {
fn record_type(&self) -> &'static str {
"waveform"
}
fn get_field(&self, name: &str) -> Option<EpicsValue> {
match name {
"VAL" => {
let mut val = self.val.clone();
val.truncate(self.nord.max(0) as usize);
Some(val)
}
"NELM" => Some(EpicsValue::Long(self.nelm)),
"NORD" => Some(EpicsValue::Long(self.nord)),
"FTVL" => Some(EpicsValue::Short(self.ftvl)),
_ => None,
}
}
fn put_field(&mut self, name: &str, value: EpicsValue) -> CaResult<()> {
match name {
"VAL" => {
let value = match (&value, self.ftvl) {
(EpicsValue::String(s), 1 | 2) => EpicsValue::CharArray(s.as_bytes().to_vec()),
_ => value,
};
let nelm = self.nelm.max(0) as usize;
match value {
EpicsValue::CharArray(mut arr) => {
self.nord = arr.len() as i32;
arr.resize(nelm, 0);
self.val = EpicsValue::CharArray(arr);
}
EpicsValue::ShortArray(mut arr) => {
self.nord = arr.len() as i32;
arr.resize(nelm, 0);
self.val = EpicsValue::ShortArray(arr);
}
EpicsValue::LongArray(mut arr) => {
self.nord = arr.len() as i32;
arr.resize(nelm, 0);
self.val = EpicsValue::LongArray(arr);
}
EpicsValue::FloatArray(mut arr) => {
self.nord = arr.len() as i32;
arr.resize(nelm, 0.0);
self.val = EpicsValue::FloatArray(arr);
}
EpicsValue::DoubleArray(mut arr) => {
self.nord = arr.len() as i32;
arr.resize(nelm, 0.0);
self.val = EpicsValue::DoubleArray(arr);
}
other => {
self.nord = 1;
self.val = other;
}
}
Ok(())
}
"NELM" => {
if let EpicsValue::Long(n) = value {
if n <= 0 {
return Err(CaError::InvalidValue(format!(
"NELM must be positive, got {n}"
)));
}
self.nelm = n;
self.reallocate_val();
Ok(())
} else {
Err(CaError::InvalidValue(format!(
"NELM requires Long, got {value:?}"
)))
}
}
"FTVL" => {
if let EpicsValue::Short(v) = value {
self.ftvl = v;
self.reallocate_val();
Ok(())
} else {
Err(CaError::InvalidValue(format!(
"FTVL requires Short, got {value:?}"
)))
}
}
"NORD" => Err(CaError::ReadOnlyField(name.to_string())),
_ => Err(CaError::FieldNotFound(name.to_string())),
}
}
fn field_list(&self) -> &'static [FieldDesc] {
match self.ftvl {
1 | 2 => WAVEFORM_FIELDS_CHAR,
3 | 4 => WAVEFORM_FIELDS_SHORT,
5 | 6 => WAVEFORM_FIELDS_LONG,
9 => WAVEFORM_FIELDS_FLOAT,
_ => WAVEFORM_FIELDS_DOUBLE,
}
}
}