use std::collections::BTreeMap;
use std::path::PathBuf;
use std::time::Duration;
use crate::calc::{Constant, Sin};
use crate::peripheral::{AnalogIRev3, AnalogIRev4, DeimosDaqRev6};
use controller::context::ControllerCtx;
use deimos::calc::sequence_machine::{MachineCfg, ThreshOp, Timeout, Transition};
use deimos::calc::{Affine, Butter2, SequenceMachine};
use deimos::*;
use tracing::info;
fn main() {
let op_dir: PathBuf = "./software/deimos/examples".into();
let op_name_path = op_dir.join("op_name.tmp");
let op_name = std::fs::read_to_string(&op_name_path).unwrap_or_else(|_| {
format!(
"{:?}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos() as u64
)
});
std::fs::write(op_name_path, &op_name).unwrap();
let peripheral_plugins = None;
let rate_hz = 50.0;
let dt_ns = (1e9_f64 / rate_hz).ceil() as u32;
let mut ctx = ControllerCtx::default();
ctx.op_name = op_name;
ctx.dt_ns = dt_ns;
ctx.op_dir = op_dir.clone();
ctx.controller_loss_of_contact_limit = 10;
ctx.peripheral_loss_of_contact_limit = 10;
ctx.loss_of_contact_policy = LossOfContactPolicy::Reconnect(Some(Duration::from_secs(10)));
if rate_hz > 50.0 {
ctx.loop_method = LoopMethod::Performant;
} else {
ctx.loop_method = LoopMethod::Efficient;
}
let mut controller = Controller::new(ctx);
let scanned_peripherals = controller
.scan(10, &peripheral_plugins)
.expect("Failed to scan for peripherals");
info!("Scan found: {scanned_peripherals:?}\n");
controller.add_peripheral("p1", Box::new(AnalogIRev3 { serial_number: 1 }));
controller.add_peripheral("p2", Box::new(AnalogIRev3 { serial_number: 2 }));
controller.add_peripheral("p3", Box::new(AnalogIRev4 { serial_number: 1 }));
controller.add_peripheral("p4", Box::new(AnalogIRev4 { serial_number: 2 }));
controller.add_peripheral("p5", Box::new(AnalogIRev4 { serial_number: 3 }));
controller.add_peripheral("p6", Box::new(AnalogIRev4 { serial_number: 4 }));
controller.add_peripheral("p8", Box::new(DeimosDaqRev6 { serial_number: 5 }));
let timescale_dispatcher: Box<dyn Dispatcher> = TimescaleDbDispatcher::new(
"tsdb",
"/run/postgresql/", "jlogan",
"POSTGRES_PW",
Duration::from_nanos(1),
Duration::from_secs(60 * 60),
);
let csv_dispatcher: Box<dyn Dispatcher> = CsvDispatcher::new(50, dispatcher::Overflow::Wrap);
controller.add_dispatcher("tsdb", timescale_dispatcher);
controller.add_dispatcher("csv", csv_dispatcher);
let duty = Constant::new(0.5, true);
let freq = Sin::new(5.0, 0.25, 2500.0, 4000.0, true);
let freq1 = Sin::new(20.0, 0.25, 10.0, 200.0, true);
let dac1 = Sin::new(20.0, 0.0, 0.0, 2.5, true);
let dac2 = Sin::new(20.0, 5.0, 0.0, 2.5 / 25.7, true);
controller.add_calc("duty", duty);
controller.add_calc("freq0", freq);
controller.add_calc("freq1", freq1);
controller.add_calc("dac0", dac1);
controller.add_calc("dac1", dac2);
controller.set_peripheral_input_source("p1.pwm0_duty", "duty.y");
controller.set_peripheral_input_source("p1.pwm0_freq", "freq0.y");
controller.set_peripheral_input_source("p1.pwm1_duty", "duty.y");
controller.set_peripheral_input_source("p1.pwm1_freq", "freq1.y");
controller.set_peripheral_input_source("p1.pwm3_duty", "sequence_machine.duty");
controller.set_peripheral_input_source("p1.pwm3_freq", "freq0.y");
controller.set_peripheral_input_source("p8.pwm0_duty", "sequence_machine.duty");
controller.set_peripheral_input_source("p8.pwm0_freq", "freq0.y");
controller.set_peripheral_input_source("p8.dac0", "dac0.y");
controller.set_peripheral_input_source("p8.dac1", "dac1.y");
let sensed_current = Affine::new("p8_0_15V_1.y".to_string(), 1.0 / 1.5, 0.0, true);
controller.add_calc("p8_valve_current_A", sensed_current);
let sensed_current_filtered = Butter2::new("p8_valve_current_A.y".to_string(), 10.0, true);
controller.add_calc("p8_valve_current_filt_A", sensed_current_filtered);
let timeouts = BTreeMap::from([
("low".to_owned(), Timeout::Loop),
("abort".to_owned(), Timeout::Loop),
("high".to_owned(), Timeout::Transition("low".to_owned())),
]);
let transitions: BTreeMap<String, BTreeMap<String, Vec<Transition>>> = BTreeMap::from([
(
"low".to_owned(),
BTreeMap::from([(
"high".to_owned(),
vec![Transition::ConstantThresh(
"freq0.y".to_owned(),
ThreshOp::Gt { by: 0.0 },
3000.0,
)],
)]),
),
(
"high".to_owned(),
BTreeMap::from([(
"abort".to_owned(),
vec![
Transition::ConstantThresh(
"p8_tc_0.temperature_K".to_owned(),
ThreshOp::Gt { by: 0.0 },
350.0,
),
Transition::ConstantThresh(
"p8_tc_1.temperature_K".to_owned(),
ThreshOp::Gt { by: 0.0 },
350.0,
),
],
)]),
),
("abort".to_owned(), BTreeMap::from([])),
]);
let cfg = MachineCfg {
save_outputs: true,
entry: "low".to_owned(),
link_folder: Some("machine".to_owned()),
timeouts,
transitions,
};
let cfg_str = serde_json::to_string_pretty(&cfg).unwrap();
let machine_dir = op_dir.join("machine");
let fp = machine_dir.join("cfg.json");
std::fs::write(fp, cfg_str).unwrap();
let machine = SequenceMachine::load_folder(&machine_dir).unwrap();
controller.add_calc("sequence_machine", machine);
let serialized_controller = serde_json::to_string_pretty(&controller).unwrap();
let _: Controller = serde_json::from_str(&serialized_controller).unwrap();
println!("{}", controller.graphviz_dot());
let machine = SequenceMachine::load_folder(&machine_dir).unwrap();
println!("{}", machine.graphviz_dot());
info!("Starting controller");
controller.run(&peripheral_plugins, None).unwrap();
}