use std::collections::BTreeMap;
use super::Peripheral;
use super::PeripheralId;
use crate::calc::{Affine, Calc, Constant, InverseAffine, RtdPt100, TcKtype};
use deimos_shared::states::OperatingMetrics;
use deimos_shared::peripherals::{analog_i_rev_2::*, model_numbers};
use serde::{Deserialize, Serialize};
#[cfg(feature = "python")]
use pyo3::prelude::*;
use crate::py_peripheral_methods;
#[derive(Serialize, Deserialize, Debug, Default)]
#[cfg_attr(feature = "python", pyclass)]
pub struct AnalogIRev2 {
pub serial_number: u64,
}
py_peripheral_methods!(AnalogIRev2);
#[typetag::serde]
impl Peripheral for AnalogIRev2 {
fn id(&self) -> PeripheralId {
PeripheralId {
model_number: model_numbers::ANALOG_I_REV_2_MODEL_NUMBER,
serial_number: self.serial_number,
}
}
fn input_names(&self) -> Vec<String> {
let mut names = Vec::new();
for i in 0..8 {
names.push(format!("pwm{i}_duty").to_owned())
}
for i in 0..8 {
names.push(format!("pwm{i}_freq").to_owned())
}
names
}
fn output_names(&self) -> Vec<String> {
let mut names = Vec::new();
for i in 0..20 {
names.push(format!("ain{i}").to_owned())
}
names
}
fn operating_roundtrip_input_size(&self) -> usize {
OperatingRoundtripInput::BYTE_LEN
}
fn operating_roundtrip_output_size(&self) -> usize {
OperatingRoundtripOutput::BYTE_LEN
}
fn emit_operating_roundtrip(
&self,
id: u64,
period_delta_ns: i64,
phase_delta_ns: i64,
inputs: &[f64],
bytes: &mut [u8],
) {
let mut pwm_duty_frac = [0_f32; 8];
let mut pwm_freq_hz = [0_u32; 8];
for i in 0..8 {
pwm_duty_frac[i] = (inputs[i] as f32).clamp(0.0, 1.0);
pwm_freq_hz[i] = inputs[i + 8].clamp(1.0, u32::MAX as f64) as u32;
}
OperatingRoundtripInput {
id,
period_delta_ns,
phase_delta_ns,
pwm_duty_frac,
pwm_freq_hz,
}
.write_bytes(bytes);
}
fn parse_operating_roundtrip(&self, bytes: &[u8], outputs: &mut [f64]) -> OperatingMetrics {
let n = self.operating_roundtrip_output_size();
let out = OperatingRoundtripOutput::read_bytes(&bytes[..n]);
let nout = out.adc_voltages.len();
for i in 0..nout {
outputs[i] = out.adc_voltages[i] as f64;
}
out.metrics
}
fn standard_calcs(&self, name: String) -> BTreeMap<String, Box<dyn Calc>> {
let mut calcs: BTreeMap<String, Box<dyn Calc>> = BTreeMap::new();
let board_temp = Constant::new(0.0 + 273.15, true);
calcs.insert(format!("{name}_board_temp_K"), board_temp);
let module_bus_voltage = Affine::new(format!("{name}.ain1"), 3.0, 0.0, true);
calcs.insert(format!("{name}_module_bus_voltage_V"), module_bus_voltage);
let milliamp_4_20_range = 2..=7;
let rtd_range = 8..=12;
let tc_range = 13..=17;
for i in milliamp_4_20_range {
let n = i - 1;
let input_name = format!("{name}.ain{i}");
let calc_name = format!("{name}_4_20_mA_{n}_A");
let slope = 100.0; calcs.insert(calc_name, InverseAffine::new(input_name, slope, 0.0, true));
}
for i in rtd_range {
let n = i - 7;
let input_name = format!("{name}.ain{i}");
let resistance_calc_name = format!("{name}_rtd_{n}_resistance_ohm");
let temperature_calc_name = format!("{name}_rtd_{n}_temp_K");
let slope = 250e-6 * 25.7;
let resistance_calc = InverseAffine::new(input_name, slope, 0.0, true);
let temperature_calc = RtdPt100::new(format!("{resistance_calc_name}.y"), true);
calcs.insert(resistance_calc_name, resistance_calc);
calcs.insert(temperature_calc_name, temperature_calc);
}
let i = 0;
let input_name = format!("{name}.ain{i}");
let resistance_calc_name = format!("{name}_rtd_cj_resistance_ohm");
let temperature_calc_name = format!("{name}_rtd_cj_temp_K");
let slope = 250e-6 * 25.7;
let resistance_calc = InverseAffine::new(input_name, slope, 0.0, true);
let temperature_calc = RtdPt100::new(format!("{resistance_calc_name}.y"), true);
calcs.insert(resistance_calc_name, resistance_calc);
calcs.insert(temperature_calc_name.clone(), temperature_calc);
let board_temp_name = temperature_calc_name;
for i in tc_range {
let n = i - 12;
let slope = 25.7;
let offset = 1.024;
let input_name = format!("{name}.ain{i}");
let voltage_calc_name = format!("{name}_tc_{n}_voltage_V");
let temperature_calc_name = format!("{name}_tc_{n}_temp_K");
let voltage_calc = InverseAffine::new(input_name, slope, offset, true);
let temperature_calc = TcKtype::new(
format!("{voltage_calc_name}.y"),
format!("{board_temp_name}.temperature_K"),
true,
);
calcs.insert(voltage_calc_name, voltage_calc);
calcs.insert(temperature_calc_name, temperature_calc);
}
calcs
}
}