From 43d6757763117a8c3be6ffa0265a1d42d4f06dc1 Mon Sep 17 00:00:00 2001 From: Harald Toth Date: Fri, 11 Jul 2025 17:15:48 +0200 Subject: [PATCH 1/2] yaml id ranges but slightly broken --- config/dataflow.yaml | 18 +++++ util/src/dataflow.rs | 157 ++++++++++++++++++++++++++++++------------- 2 files changed, 129 insertions(+), 46 deletions(-) diff --git a/config/dataflow.yaml b/config/dataflow.yaml index d6be019..805b4f5 100644 --- a/config/dataflow.yaml +++ b/config/dataflow.yaml @@ -2405,6 +2405,24 @@ 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' + id-range-first: 0x950 + id-range-last: 0x963 + datapoint-size-bytes: 1 + datapoint-type: 'u8' + can-conversion: 'decode-temperature:u8->f32' + display-units: '°C' + store: + default: 0 + gs: + conversion: 'gs_2p_float:f32' commands: - name: 'SendHashes' diff --git a/util/src/dataflow.rs b/util/src/dataflow.rs index 4859cfa..e762c81 100644 --- a/util/src/dataflow.rs +++ b/util/src/dataflow.rs @@ -132,6 +132,22 @@ pub struct MessageProcessingSpec { pub can: CanSpec, pub fsm: Option, pub datapoint_conversion: Vec, + pub datapoints: Option, +} + +#[derive(Debug, serde::Deserialize)] +pub struct DatapointRangeConversionSpec { + pub name: String, + pub id_range_first: u16, + pub id_range_last: u16, + 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 +161,10 @@ pub enum CanSpec { #[serde(flatten)] comes_from_levi: Option, }, + Range { + id_range_first: u32, + id_range_last: u32, + }, } /// 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, name: String) -> String { + let mut code = String::new(); + if let Some(dpc) = &spec.datapoints { + for id_index in dpc.id_range_first..dpc.id_range_last + 1 { + let mut byte_index = 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;", + format!("{}_{}", name, id_index), + dpc.gs.conversion.procedure_suffix + ).unwrap(); + + byte_index += dpc.datapoint_size_bytes; + if byte_index == 8 { + break; + } else if byte_index > 8 { + panic!("Invalid size for datapoint {}_{}", name, id_index); + } + } + } + } + code +} + fn make_datapoint_parser(spec: &MessageProcessingSpec) -> String { let mut code = String::new(); for dpc in &spec.datapoint_conversion { @@ -476,6 +528,22 @@ pub fn collect_data_types(df: &DataflowSpec) -> crate::datatypes::Config { store: dpc.datapoint.store.clone(), }); } + if let Some(datapoints) = &mp.datapoints { + for (index, id) in + (datapoints.id_range_first..(datapoints.id_range_last + 1)).enumerate() + { + data_types.Datatype.push(crate::datatypes::Datatype { + id, + name: format!("{}_{}", &mp.name, 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 +691,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,57 +752,52 @@ 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(); - } - } - writeln!(&mut code, "_ => {{}}}}}}").unwrap(); - - writeln!(&mut code, "pub fn match_can_to_datatypes(id: u32) -> [Datatype; 8] {{ match id {{") - .unwrap(); - for mp in &df.message_processing { - if let CanSpec::Can2 { id, .. } = mp.can { - writeln!(&mut code, "{id} => [ ").unwrap(); - - let mut count = 0; - for dpc in &mp.datapoint_conversion { - write!(&mut code, "Datatype::{}, ", dpc.datapoint.name).unwrap(); - count += 1; - } - - loop { - if count < 8 { - write!(&mut code, "Datatype::DefaultDatatype, ").unwrap(); - } else { - break; + ) + .unwrap(); + }, + CanSpec::Range { id_range_first, id_range_last } => { + for id in id_range_first..(id_range_last + 1) { + writeln!( + &mut code, + "\t{id} => {{ + " + ) + .unwrap(); + + code.push_str(&make_datapoint_parser_range(mp, mp.name.clone())); + + //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(); } - count += 1; - } - - writeln!(&mut code, "],").unwrap(); + }, + _ => {}, } } - - writeln!( - &mut code, - "_ => [{}Datatype::DefaultDatatype],}}}}", - "Datatype::DefaultDatatype, ".repeat(7) - ) - .unwrap(); + writeln!(&mut code, "_ => {{}}}}}}").unwrap(); let mut can1commands = vec![]; let mut can2commands = vec![]; @@ -995,7 +1061,6 @@ END_IF } pub fn make_logging_pcb_code(_df: &DataflowSpec) -> String { - // TODO String::new() } From c349b76504ab4e9adc765cf1058d3743a1147849 Mon Sep 17 00:00:00 2001 From: Harald Toth Date: Fri, 11 Jul 2025 20:27:44 +0200 Subject: [PATCH 2/2] Fixed dataflow --- config/dataflow.yaml | 5 +- crates/lib/build.rs | 2 +- gs/station/build.rs | 2 +- util/src/bin/output_gs_frontend.rs | 5 +- util/src/bin/output_levi_beckhoff_code.rs | 3 +- util/src/dataflow.rs | 122 ++++++++++++++-------- 6 files changed, 86 insertions(+), 53 deletions(-) diff --git a/config/dataflow.yaml b/config/dataflow.yaml index 805b4f5..333e506 100644 --- a/config/dataflow.yaml +++ b/config/dataflow.yaml @@ -2413,12 +2413,11 @@ message-processing: datapoint-conversion: datapoints: name: 'BatteryTemp' - id-range-first: 0x950 - id-range-last: 0x963 datapoint-size-bytes: 1 datapoint-type: 'u8' - can-conversion: 'decode-temperature:u8->f32' + can-conversion: 'decode_temperature:u8->f32' display-units: '°C' + critical: false store: default: 0 gs: diff --git a/crates/lib/build.rs b/crates/lib/build.rs index dc35d74..588390a 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 b105a6c..edfe72f 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 5019fe5..54e8273 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 30ba9fb..cb822bb 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 e762c81..6b6c993 100644 --- a/util/src/dataflow.rs +++ b/util/src/dataflow.rs @@ -136,10 +136,9 @@ pub struct MessageProcessingSpec { } #[derive(Debug, serde::Deserialize)] +#[serde(rename_all = "kebab-case")] pub struct DatapointRangeConversionSpec { pub name: String, - pub id_range_first: u16, - pub id_range_last: u16, pub datapoint_size_bytes: u8, pub datapoint_type: Ty, pub can_conversion: CanConversionSpec, @@ -161,9 +160,10 @@ pub enum CanSpec { #[serde(flatten)] comes_from_levi: Option, }, + #[serde(rename_all = "kebab-case")] Range { - id_range_first: u32, - id_range_last: u32, + id_range_first: u16, + id_range_last: u16, }, } @@ -443,33 +443,33 @@ fn make_procedures(df: &DataflowSpec) -> String { code } -fn make_datapoint_parser_range(spec: &MessageProcessingSpec, name: String) -> String { +fn make_datapoint_parser_range(spec: &MessageProcessingSpec, can_index: usize) -> String { let mut code = String::new(); if let Some(dpc) = &spec.datapoints { - for id_index in dpc.id_range_first..dpc.id_range_last + 1 { - let mut byte_index = 0; - loop { - writeln!(&mut code, "let d = data[{byte_index}];").unwrap(); + 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, "let c = {}(d);", dpc.can_conversion.proc_name).unwrap(); - writeln!( - &mut code, - "f(Datapoint::new( - Datatype::{}, + writeln!( + &mut code, + "f(Datapoint::new( + Datatype::{}{}_{}, dump_{}(c), embassy_time::Instant::now().as_ticks(), )).await;", - format!("{}_{}", name, id_index), - dpc.gs.conversion.procedure_suffix - ).unwrap(); - - byte_index += dpc.datapoint_size_bytes; - if byte_index == 8 { - break; - } else if byte_index > 8 { - panic!("Invalid size for datapoint {}_{}", name, id_index); - } + 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), } } } @@ -529,19 +529,21 @@ pub fn collect_data_types(df: &DataflowSpec) -> crate::datatypes::Config { }); } if let Some(datapoints) = &mp.datapoints { - for (index, id) in - (datapoints.id_range_first..(datapoints.id_range_last + 1)).enumerate() - { - data_types.Datatype.push(crate::datatypes::Datatype { - id, - name: format!("{}_{}", &mp.name, 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(), - }); + 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(), + }); + } + } } } } @@ -692,8 +694,8 @@ fn apply_trim_8(data: [u8; 8], ctxt: &str) -> [u8; 0] { } }, _ => { - // I don't think the ranges should be converted to fsm events - } + // I don't think the ranges should be converted to fsm events + }, } } } @@ -771,7 +773,7 @@ fn apply_trim_8(data: [u8; 8], ctxt: &str) -> [u8; 0] { .unwrap(); }, CanSpec::Range { id_range_first, id_range_last } => { - for id in id_range_first..(id_range_last + 1) { + for (can_index, id) in (id_range_first..=id_range_last).enumerate() { writeln!( &mut code, "\t{id} => {{ @@ -779,7 +781,7 @@ fn apply_trim_8(data: [u8; 8], ctxt: &str) -> [u8; 0] { ) .unwrap(); - code.push_str(&make_datapoint_parser_range(mp, mp.name.clone())); + 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, @@ -799,6 +801,38 @@ fn apply_trim_8(data: [u8; 8], ctxt: &str) -> [u8; 0] { } writeln!(&mut code, "_ => {{}}}}}}").unwrap(); + 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 { + writeln!(&mut code, "{id} => [ ").unwrap(); + + let mut count = 0; + for dpc in &mp.datapoint_conversion { + write!(&mut code, "Datatype::{}, ", dpc.datapoint.name).unwrap(); + count += 1; + } + + loop { + if count < 8 { + write!(&mut code, "Datatype::DefaultDatatype, ").unwrap(); + } else { + break; + } + count += 1; + } + + writeln!(&mut code, "],").unwrap(); + } + } + + writeln!( + &mut code, + "_ => [{}Datatype::DefaultDatatype],}}}}", + "Datatype::DefaultDatatype, ".repeat(7) + ) + .unwrap(); + let mut can1commands = vec![]; let mut can2commands = vec![]; @@ -1060,9 +1094,7 @@ END_IF ) } -pub fn make_logging_pcb_code(_df: &DataflowSpec) -> String { - 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(); @@ -1203,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)?) }