diff --git a/config/dataflow.yaml b/config/dataflow.yaml index d6be0191..333e5067 100644 --- a/config/dataflow.yaml +++ b/config/dataflow.yaml @@ -2405,6 +2405,23 @@ message-processing: display-units: 'Bar' gs: conversion: 'gs_2p_float:f32' + - name: 'BatteryTemp' + can: + bus: range + id-range-first: 0x900 + id-range-last: 0x913 + datapoint-conversion: + datapoints: + name: 'BatteryTemp' + datapoint-size-bytes: 1 + datapoint-type: 'u8' + can-conversion: 'decode_temperature:u8->f32' + display-units: '°C' + critical: false + store: + default: 0 + gs: + conversion: 'gs_2p_float:f32' commands: - name: 'SendHashes' diff --git a/crates/lib/build.rs b/crates/lib/build.rs index dc35d744..588390af 100644 --- a/crates/lib/build.rs +++ b/crates/lib/build.rs @@ -99,7 +99,7 @@ fn main() -> Result<()> { let mut content = String::from("//@generated\n"); let df = std::fs::read_to_string(DATAFLOW_PATH)?; - let df = goose_utils::dataflow::parse_from(&df); + let df = goose_utils::dataflow::parse_from(&df)?; // content.push_str(&check_config(DATAFLOW_PATH, )?); content.push_str(&hash_config(CONFIG_PATH)?); diff --git a/gs/station/build.rs b/gs/station/build.rs index b105a6c2..edfe72fa 100644 --- a/gs/station/build.rs +++ b/gs/station/build.rs @@ -66,7 +66,7 @@ fn main() -> Result<()> { content.push_str(&configure_gs(&config)); content.push_str(&configure_gs_ips(&config.gs.ips, config.gs.port)); let df = fs::read_to_string(DATAFLOW_PATH)?; - let df = goose_utils::dataflow::parse_from(&df); + let df = goose_utils::dataflow::parse_from(&df)?; let dt = goose_utils::dataflow::collect_data_types(&df); let dt = generate_data_types_from_config(&dt, true)?; content.push_str(&dt); diff --git a/util/src/bin/output_gs_frontend.rs b/util/src/bin/output_gs_frontend.rs index 5019fe5b..54e82736 100644 --- a/util/src/bin/output_gs_frontend.rs +++ b/util/src/bin/output_gs_frontend.rs @@ -1,8 +1,9 @@ -fn main() { +fn main() -> anyhow::Result<()> { let df_path = std::env::args().nth(1).expect("No dataflow file provided"); let df = std::fs::read_to_string(df_path).expect("Failed to read dataflow file"); - let df = goose_utils::dataflow::parse_from(&df); + let df = goose_utils::dataflow::parse_from(&df)?; let output = goose_utils::dataflow::output_gs_frontend_code(&df); println!("{}", output); + Ok(()) } diff --git a/util/src/bin/output_levi_beckhoff_code.rs b/util/src/bin/output_levi_beckhoff_code.rs index 30ba9fb6..cb822bb6 100644 --- a/util/src/bin/output_levi_beckhoff_code.rs +++ b/util/src/bin/output_levi_beckhoff_code.rs @@ -1,8 +1,9 @@ -fn main() { +fn main() -> anyhow::Result<()> { let df_path = std::env::args().nth(1).expect("No dataflow file provided"); let df = std::fs::read_to_string(df_path).expect("Failed to read dataflow file"); let df = goose_utils::dataflow::parse_from(&df); let output = goose_utils::dataflow::make_levi_beckhoff_code(&df); println!("{}", output); + Ok(()) } diff --git a/util/src/dataflow.rs b/util/src/dataflow.rs index 4859cfa8..6b6c993b 100644 --- a/util/src/dataflow.rs +++ b/util/src/dataflow.rs @@ -132,6 +132,21 @@ pub struct MessageProcessingSpec { pub can: CanSpec, pub fsm: Option, pub datapoint_conversion: Vec, + pub datapoints: Option, +} + +#[derive(Debug, serde::Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct DatapointRangeConversionSpec { + pub name: String, + pub datapoint_size_bytes: u8, + pub datapoint_type: Ty, + pub can_conversion: CanConversionSpec, + pub display_units: String, + pub critical: bool, + pub limits: Option, + pub gs: DatapointConversionGsSpec, + pub store: Option, } #[derive(serde::Deserialize, Debug)] @@ -145,6 +160,11 @@ pub enum CanSpec { #[serde(flatten)] comes_from_levi: Option, }, + #[serde(rename_all = "kebab-case")] + Range { + id_range_first: u16, + id_range_last: u16, + }, } /// for auto-generation of the code for the levi Beckhoff pc. @@ -169,7 +189,6 @@ pub struct DatapointConversionSpec { pub display_units: Option, pub limits: Option, pub gs: DatapointConversionGsSpec, - pub severity: Option, #[serde(rename = "beckhoff")] pub comes_from_levi_info: Option, } @@ -424,6 +443,39 @@ fn make_procedures(df: &DataflowSpec) -> String { code } +fn make_datapoint_parser_range(spec: &MessageProcessingSpec, can_index: usize) -> String { + let mut code = String::new(); + if let Some(dpc) = &spec.datapoints { + let mut byte_index = 0; + let mut counter = 0; + loop { + writeln!(&mut code, "let d = data[{byte_index}];").unwrap(); + + writeln!(&mut code, "let c = {}(d);", dpc.can_conversion.proc_name).unwrap(); + + writeln!( + &mut code, + "f(Datapoint::new( + Datatype::{}{}_{}, + dump_{}(c), + embassy_time::Instant::now().as_ticks(), + )).await;", + dpc.name, can_index, counter, dpc.gs.conversion.procedure_suffix + ) + .unwrap(); + + byte_index += dpc.datapoint_size_bytes; + counter += 1; + match byte_index { + 0..8 => {}, + 8 => break, + _ => panic!("Invalid size for datapoint {}{}_{}", dpc.name, can_index, counter), + } + } + } + code +} + fn make_datapoint_parser(spec: &MessageProcessingSpec) -> String { let mut code = String::new(); for dpc in &spec.datapoint_conversion { @@ -476,6 +528,24 @@ pub fn collect_data_types(df: &DataflowSpec) -> crate::datatypes::Config { store: dpc.datapoint.store.clone(), }); } + if let Some(datapoints) = &mp.datapoints { + if let CanSpec::Range { id_range_first, id_range_last } = mp.can { + for (can_index, _can_id) in (id_range_first..=id_range_last).enumerate() { + for datatype_index in 0..8 / datapoints.datapoint_size_bytes { + data_types.Datatype.push(crate::datatypes::Datatype { + id: id_range_last + can_index as u16 * datatype_index as u16 + 1, + name: format!("{}{}_{}", &mp.name, can_index, datatype_index), + lower: datapoints.limits.as_ref().map(|l| l.lower).unwrap_or(Limit::No), // TODO: Check + upper: datapoints.limits.as_ref().map(|l| l.upper).unwrap_or(Limit::No), + critical: datapoints.critical, + display_units: Some(datapoints.display_units.clone()), + priority: None, + store: datapoints.store.clone(), + }); + } + } + } + } } for sd in &df.standard_datapoints { data_types.Datatype.push(crate::datatypes::Datatype { @@ -623,6 +693,9 @@ fn apply_trim_8(data: [u8; 8], ctxt: &str) -> [u8; 0] { ); } }, + _ => { + // I don't think the ranges should be converted to fsm events + }, } } } @@ -681,27 +754,54 @@ fn apply_trim_8(data: [u8; 8], ctxt: &str) -> [u8; 0] { writeln!(&mut code, "pub async fn parse_datapoints_can_2(id: u32, data: &[u8], mut f: F) where F: FnMut(Datapoint) -> Fut, Fut: Future {{ {proc} match id {{").unwrap(); for mp in &df.message_processing { - if let CanSpec::Can2 { id, .. } = mp.can { - writeln!( - &mut code, - "{id} => {{ + match mp.can { + CanSpec::Can2 { id, .. } => { + writeln!( + &mut code, + "\t{id} => {{ " - ) - .unwrap(); + ) + .unwrap(); - code.push_str(&make_datapoint_parser(mp)); + code.push_str(&make_datapoint_parser(mp)); - writeln!( - &mut code, - "}} + writeln!( + &mut code, + "}} " - ) - .unwrap(); + ) + .unwrap(); + }, + CanSpec::Range { id_range_first, id_range_last } => { + for (can_index, id) in (id_range_first..=id_range_last).enumerate() { + writeln!( + &mut code, + "\t{id} => {{ + " + ) + .unwrap(); + + code.push_str(&make_datapoint_parser_range(mp, can_index)); + + //TODO: Finish this (just take the same method and apply it to all datapoints in a for loop. + // Use index + mp.name to get the datapoint and index % 8 for the getter, + // use datapoint size to iterate over them. They will also have the same procedures for + // decoding, and use the same time stamp embassy_time::Instant::now().as_ticks() + + writeln!( + &mut code, + "}} + " + ) + .unwrap(); + } + }, + _ => {}, } } writeln!(&mut code, "_ => {{}}}}}}").unwrap(); - writeln!(&mut code, "pub fn match_can_to_datatypes(id: u32) -> [Datatype; 8] {{ match id {{") + writeln!(&mut code, "pub fn match_can_id_to_datatypes(id: u32) -> [Datatype; 8] {{ match id {{") .unwrap(); for mp in &df.message_processing { if let CanSpec::Can2 { id, .. } = mp.can { @@ -731,7 +831,7 @@ fn apply_trim_8(data: [u8; 8], ctxt: &str) -> [u8; 0] { "_ => [{}Datatype::DefaultDatatype],}}}}", "Datatype::DefaultDatatype, ".repeat(7) ) - .unwrap(); + .unwrap(); let mut can1commands = vec![]; let mut can2commands = vec![]; @@ -994,10 +1094,7 @@ END_IF ) } -pub fn make_logging_pcb_code(_df: &DataflowSpec) -> String { - // TODO - String::new() -} +pub fn make_logging_pcb_code(_df: &DataflowSpec) -> String { String::new() } pub fn make_gs_code(df: &DataflowSpec) -> String { let mut code = String::new(); @@ -1138,4 +1235,4 @@ export const NamedDatatypeValues = ["# code } -pub fn parse_from(data: &str) -> DataflowSpec { serde_yaml::from_str(data).unwrap() } +pub fn parse_from(data: &str) -> anyhow::Result { Ok(serde_yaml::from_str(data)?) }