From c09448ea69402d91bc1ce06c11ebce33faca77ca Mon Sep 17 00:00:00 2001 From: harry Date: Thu, 12 Dec 2024 15:39:41 -0500 Subject: [PATCH 01/10] Added serde deny_unknown_fields attribute to CAN structs --- libs/calypso-cangen/src/can_types.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/calypso-cangen/src/can_types.rs b/libs/calypso-cangen/src/can_types.rs index 242e3aa..e551951 100644 --- a/libs/calypso-cangen/src/can_types.rs +++ b/libs/calypso-cangen/src/can_types.rs @@ -10,6 +10,7 @@ use serde::Deserialize; * Class representing a CAN message */ #[derive(Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct CANMsg { pub id: String, pub desc: String, @@ -23,6 +24,7 @@ pub struct CANMsg { * Class representing a NetField of a CAN message */ #[derive(Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct NetField { pub name: String, pub unit: String, @@ -36,6 +38,7 @@ pub struct NetField { * Class representing a CAN point of a NetField */ #[derive(Deserialize, Debug)] +#[serde(deny_unknown_fields)] pub struct CANPoint { pub size: usize, pub signed: Option, @@ -46,7 +49,7 @@ pub struct CANPoint { } #[derive(Deserialize, Debug)] -#[serde(untagged)] +#[serde(untagged, deny_unknown_fields)] pub enum Sim { SimSweep { min: f32, From 2e2637cf2e9d280f267c771c7ec65d7c1c5ceb89 Mon Sep 17 00:00:00 2001 From: harry Date: Thu, 12 Dec 2024 15:40:21 -0500 Subject: [PATCH 02/10] Moved CANGEN_SPEC_PATH to calypso-cangen lib --- libs/calypso-cangen/src/lib.rs | 6 ++++++ libs/daedalus/src/lib.rs | 13 +++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/libs/calypso-cangen/src/lib.rs b/libs/calypso-cangen/src/lib.rs index e294d79..57974f9 100644 --- a/libs/calypso-cangen/src/lib.rs +++ b/libs/calypso-cangen/src/lib.rs @@ -1,3 +1,9 @@ pub mod can_gen_decode; pub mod can_gen_encode; pub mod can_types; +/** + * Path to CAN spec JSON files + * Used by all daedalus macros + * Filepath is relative to project root (i.e. /Calypso) + */ +const CANGEN_SPEC_PATH: &str = "./Embedded-Base/cangen/can-messages"; diff --git a/libs/daedalus/src/lib.rs b/libs/daedalus/src/lib.rs index 7077889..6e6b27b 100644 --- a/libs/daedalus/src/lib.rs +++ b/libs/daedalus/src/lib.rs @@ -13,13 +13,6 @@ use std::io::Read; use std::path::PathBuf; use std::str::FromStr; -/** - * Path to CAN spec JSON files - * Used by all daedalus macros - * Filepath is relative to project root (i.e. /Calypso) - */ -const DAEDALUS_CANGEN_SPEC_PATH: &str = "./Embedded-Base/cangen/can-messages"; - /** * Macro to generate all the code for decode_data.rs * - Generates prelude, phf map, and all decode functions @@ -43,7 +36,7 @@ pub fn gen_decode_data(_item: TokenStream) -> TokenStream { }; let mut __decode_map_entries = ProcMacro2TokenStream::new(); - match fs::read_dir(DAEDALUS_CANGEN_SPEC_PATH) { + match fs::read_dir(CANGEN_SPEC_PATH) { Ok(__entries) => { for __entry in __entries { match __entry { @@ -168,7 +161,7 @@ pub fn gen_encode_data(_item: TokenStream) -> TokenStream { let mut __encode_key_list_entries = ProcMacro2TokenStream::new(); let mut __encode_key_list_size: usize = 0; - match fs::read_dir(DAEDALUS_CANGEN_SPEC_PATH) { + match fs::read_dir(CANGEN_SPEC_PATH) { Ok(__entries) => { for __entry in __entries { match __entry { @@ -330,7 +323,7 @@ pub fn gen_simulate_data(_item: TokenStream) -> TokenStream { }; let mut __simulate_function_body = quote! {}; - match fs::read_dir(DAEDALUS_CANGEN_SPEC_PATH) { + match fs::read_dir(CANGEN_SPEC_PATH) { Ok(__entries) => { for __entry in __entries { match __entry { From 224faf19530bc7df608fa8d7919c6e3cda2c3aa4 Mon Sep 17 00:00:00 2001 From: harry Date: Thu, 12 Dec 2024 17:25:40 -0500 Subject: [PATCH 03/10] Added JSON validation to build.rs (details in libs/calypso-cangen/src/validate.rs) Added CANSpecError enum to calypso-cangen --- Cargo.lock | 52 +++++++--- build.rs | 12 +++ libs/calypso-cangen/Cargo.toml | 2 + libs/calypso-cangen/src/lib.rs | 1 + libs/calypso-cangen/src/validate.rs | 145 ++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 15 deletions(-) create mode 100644 libs/calypso-cangen/src/validate.rs diff --git a/Cargo.lock b/Cargo.lock index 2058be9..7f9f184 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,6 +136,8 @@ dependencies = [ "proc-macro2", "quote", "serde", + "serde_json", + "thiserror 2.0.6", ] [[package]] @@ -184,7 +186,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -345,7 +347,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -561,7 +563,7 @@ dependencies = [ "libc", "log", "paho-mqtt-sys", - "thiserror", + "thiserror 1.0.50", ] [[package]] @@ -604,7 +606,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -660,7 +662,7 @@ checksum = "0bcc343da15609eaecd65f8aa76df8dc4209d325131d8219358c0aaaebab0bf6" dependencies = [ "once_cell", "protobuf-support", - "thiserror", + "thiserror 1.0.50", ] [[package]] @@ -675,7 +677,7 @@ dependencies = [ "protobuf-parse", "regex", "tempfile", - "thiserror", + "thiserror 1.0.50", ] [[package]] @@ -690,7 +692,7 @@ dependencies = [ "protobuf", "protobuf-support", "tempfile", - "thiserror", + "thiserror 1.0.50", "which", ] @@ -700,7 +702,7 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0766e3675a627c327e4b3964582594b0e8741305d628a98a5de75a1d15f99b9" dependencies = [ - "thiserror", + "thiserror 1.0.50", ] [[package]] @@ -816,7 +818,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -862,7 +864,7 @@ dependencies = [ "nb", "neli", "nix", - "thiserror", + "thiserror 1.0.50", ] [[package]] @@ -884,9 +886,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -912,7 +914,16 @@ version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.50", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] @@ -923,7 +934,18 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -1046,5 +1068,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] diff --git a/build.rs b/build.rs index eebb270..82e5f3b 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,6 @@ +use calypso_cangen::validate::* +use std::process; + /* Prebuild script */ fn main() { println!("cargo:rerun-if-changed=Embedded-Base"); @@ -12,4 +15,13 @@ fn main() { // Specify output directory relative to Cargo output directory. .out_dir("src") .run_from_script(); + + // Validate CAN spec + let can_spec_errors: Result<(), Vec> = validate_all_spec(); + if !can_spec_errors.is_empty() { + for error in can_spec_errors { + eprintln!("CAN spec error: {}", error); + } + process::exit(1); + } } diff --git a/libs/calypso-cangen/Cargo.toml b/libs/calypso-cangen/Cargo.toml index 51165b5..27d1f1e 100644 --- a/libs/calypso-cangen/Cargo.toml +++ b/libs/calypso-cangen/Cargo.toml @@ -7,3 +7,5 @@ edition = "2021" proc-macro2.workspace = true quote.workspace = true serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +thiserror = "2.0.6" diff --git a/libs/calypso-cangen/src/lib.rs b/libs/calypso-cangen/src/lib.rs index 57974f9..ef1eb31 100644 --- a/libs/calypso-cangen/src/lib.rs +++ b/libs/calypso-cangen/src/lib.rs @@ -1,6 +1,7 @@ pub mod can_gen_decode; pub mod can_gen_encode; pub mod can_types; +pub mod validate; /** * Path to CAN spec JSON files * Used by all daedalus macros diff --git a/libs/calypso-cangen/src/validate.rs b/libs/calypso-cangen/src/validate.rs new file mode 100644 index 0000000..683ec3d --- /dev/null +++ b/libs/calypso-cangen/src/validate.rs @@ -0,0 +1,145 @@ +use crate::can_types::*; +use crate::CANGEN_SPEC_PATH; +use std::fs; +use std::io::Read; +use std::path::PathBuf; +use thiserror::Error; + +/** + * JSON spec error enum + */ +#[derive(Error, Debug)] +pub enum CANSpecError { + #[error("Message {0} description ({1}) contains illegal characters. Message descriptions may only contain letters and spaces.")] + MessageDescIllegalChars(String, String), + + #[error("{0} totals to {1} bits. NetField totals should be byte-aligned (bit size should be a power of 2).")] + FieldTotalByteMisalignment(String, usize), + + #[error("Sim frequencies ({1}, {2}) for NetField {0} do not add to 1. Sim enum frequencies must add up to 1.")] + FieldSimEnumFrequencySum(String, f32, f32), + + #[error("Signed point {0} of NetField {1} is {2} bits. Signed messages must be 8, 16, or 32 bits.")] + PointSignedBitCount(usize, String, usize), + + #[error("Little-endian point {0} of NetField {1} is {2} bits. Little-endian messages must be 8, 16, or 32 bits.")] + PointLittleEndianBitCount(usize, String, usize), + + #[error("IEEE754 float point {0} of NetField {1} is {2} bits, instead of 32 bits.")] + PointFloatBitCount(usize, String, usize), +} + +/** + * Validate all CAN spec files in CANGEN_SPEC_PATH + */ +pub fn validate_all_spec() -> Result<(), Vec> { + match fs::read_dir(CANGEN_SPEC_PATH) { + Ok(__entries) => { + let mut __all_errors = Vec::new(); + for __entry in __entries { + match __entry { + Ok(__entry) => { + let __path = __entry.path(); + if __path.is_file() && __path.extension().map_or(false, |ext| ext == "json") + { + match validate_spec_file(__path.clone()) { + Ok(()) => {}, + Err(__file_errors) => __all_errors.extend(__file_errors), + }; + } + } + Err(__err) => err.to_compile_error().into() + } + } + + if __all_errors.is_empty() { + Ok(()) + } else { + Err(__all_errors) + } + } + Err(__err) => err.to_compile_error().into() + } +} + +/** + * Validate a CAN spec file + */ +fn validate_spec_file(_path: PathBuf) -> Result<(), Vec> { + match fs::File::open(_path) { + Ok(mut _file) => { + let mut _contents = String::new(); + let _ = _file.read_to_string(&mut _contents); + let mut _errors = Vec::new(); + let _msgs: Vec = serde_json::from_str(&_contents).unwrap(); + for _msg in _msgs { + match validate_msg(_msg) { + Ok(()) => {}, + Err(_msg_errors) => _errors.extend(_msg_errors) + }; + } + + if _errors.is_empty() { + Ok(()) + } else { + _errors + } + } + Err(err) => err.to_compile_error().into() + } +} + +/** + * Validate a CANMsg + */ +fn validate_msg(_msg: CANMsg) -> Result<(), Vec> { + let _errors = Vec::new(); + + // Check description contains legal chars + let _desc = _msg.desc.clone(); + if !_desc.chars().all(|c| c.is_alphabetic() || c.is_whitespace()) { + _errors.push(CANSpecError::MessageDescIllegalChars(_msg.id.clone(), _desc)); + } + + for _field in _msg.fields { + _name = _field.name.clone(); + + // Check Sim enum frequencies add to 1 + if let Some(SimEnum {f1, f2}) = _field.sim && f1 + f2 != 1.0 { + _errors.push(CANSpecError::FieldSimEnumFrequencySum(_name.clone(), f1, f2)); + } + + // Sum bit count of points for checks + let mut _bit_count: usize = 0; + + for (_i, _point) in _field.points.iter().enumerate() { + _bit_count += _point.size; + + // Check signed point bit count + if let Some(true) = _point.signed && ![8, 16, 32].contains(&_point.size) { + _errors.push(CANSpecError::PointSignedBitCount(_i, _name.clone(), _point.size)); + } + + // Check little endian point bit count + if let Some("little") = _point.endianness && ![8, 16, 32].contains(&_point.size) { + _errors.push(CANSpecError::PointLittleEndianBitCount(_i, _name.clone(), _point.size)); + } + + // Check IEEE754 f32 point bit count + if let Some(true) = _point.ieee754_f32 && _point.size != 32 { + _errors.push(CANSpecError::PointFloatBitCount(_i, _name.clone(), _point.size)); + } + } + + // Check field total alignment + if _bit_count % 2 != 0 { + _errors.push(CANSpecError::FieldTotalByteMisalignment(_name.clone(), _bit_count)); + } + } + + if _errors.is_empty() { + Ok(()) + } else { + Err(_errors) + } +} From d5233c4118c10a3def5665a1995affb613a9bb13 Mon Sep 17 00:00:00 2001 From: harry Date: Thu, 12 Dec 2024 20:45:36 -0500 Subject: [PATCH 04/10] Working prototype JSON validation check in build.rs, needs work (either on error or spec side, not sure yet) --- .gitmodules | 1 + Cargo.toml | 1 + Embedded-Base | 2 +- build.rs | 18 ++-- libs/calypso-cangen/src/can_gen_encode.rs | 4 +- libs/calypso-cangen/src/can_types.rs | 2 +- libs/calypso-cangen/src/lib.rs | 2 +- libs/calypso-cangen/src/validate.rs | 102 ++++++++++++++++------ libs/daedalus/src/lib.rs | 1 + 9 files changed, 91 insertions(+), 42 deletions(-) diff --git a/.gitmodules b/.gitmodules index f18e99b..529ad8d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "Embedded-Base"] path = Embedded-Base url = ../Embedded-Base + branch = main diff --git a/Cargo.toml b/Cargo.toml index 4f3dc1e..6b8ce6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ daedalus = { path = "./libs/daedalus" } [build-dependencies] protobuf-codegen = "3.5.1" +calypso-cangen = { path = "./libs/calypso-cangen" } [profile.release] lto = true diff --git a/Embedded-Base b/Embedded-Base index 5d4b634..c2061ae 160000 --- a/Embedded-Base +++ b/Embedded-Base @@ -1 +1 @@ -Subproject commit 5d4b634132fa9dc44e4f9496c265c6e214715075 +Subproject commit c2061ae62cf670cdb6fb6ac9c966f3ec74ffc916 diff --git a/build.rs b/build.rs index 82e5f3b..d08e960 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,4 @@ -use calypso_cangen::validate::* +use calypso_cangen::validate::*; use std::process; /* Prebuild script */ @@ -15,13 +15,15 @@ fn main() { // Specify output directory relative to Cargo output directory. .out_dir("src") .run_from_script(); - - // Validate CAN spec - let can_spec_errors: Result<(), Vec> = validate_all_spec(); - if !can_spec_errors.is_empty() { - for error in can_spec_errors { - eprintln!("CAN spec error: {}", error); + + // Validate CAN spec + match validate_all_spec() { + Ok(()) => {} + Err(errors) => { + for error in errors { + eprintln!("CAN spec error: {}", error); + } + process::exit(1); } - process::exit(1); } } diff --git a/libs/calypso-cangen/src/can_gen_encode.rs b/libs/calypso-cangen/src/can_gen_encode.rs index 03ec9f3..2caaf66 100644 --- a/libs/calypso-cangen/src/can_gen_encode.rs +++ b/libs/calypso-cangen/src/can_gen_encode.rs @@ -101,8 +101,8 @@ impl CANGenEncode for CANPoint { } _ => quote! {}, }; - let default_value: f32 = match self.default_value { - Some(default_value) => default_value, + let default_value: f32 = match self.default { + Some(default) => default, _ => 0f32, }; let float_final = quote! { diff --git a/libs/calypso-cangen/src/can_types.rs b/libs/calypso-cangen/src/can_types.rs index e551951..bfdb6a2 100644 --- a/libs/calypso-cangen/src/can_types.rs +++ b/libs/calypso-cangen/src/can_types.rs @@ -44,7 +44,7 @@ pub struct CANPoint { pub signed: Option, pub endianness: Option, pub format: Option, - pub default_value: Option, + pub default: Option, pub ieee754_f32: Option, } diff --git a/libs/calypso-cangen/src/lib.rs b/libs/calypso-cangen/src/lib.rs index ef1eb31..7cbf95b 100644 --- a/libs/calypso-cangen/src/lib.rs +++ b/libs/calypso-cangen/src/lib.rs @@ -7,4 +7,4 @@ pub mod validate; * Used by all daedalus macros * Filepath is relative to project root (i.e. /Calypso) */ -const CANGEN_SPEC_PATH: &str = "./Embedded-Base/cangen/can-messages"; +pub const CANGEN_SPEC_PATH: &str = "./Embedded-Base/cangen/can-messages"; diff --git a/libs/calypso-cangen/src/validate.rs b/libs/calypso-cangen/src/validate.rs index 683ec3d..07faea0 100644 --- a/libs/calypso-cangen/src/validate.rs +++ b/libs/calypso-cangen/src/validate.rs @@ -18,13 +18,15 @@ pub enum CANSpecError { #[error("Sim frequencies ({1}, {2}) for NetField {0} do not add to 1. Sim enum frequencies must add up to 1.")] FieldSimEnumFrequencySum(String, f32, f32), - - #[error("Signed point {0} of NetField {1} is {2} bits. Signed messages must be 8, 16, or 32 bits.")] + + #[error( + "Signed point {0} of NetField {1} is {2} bits. Signed messages must be 8, 16, or 32 bits." + )] PointSignedBitCount(usize, String, usize), #[error("Little-endian point {0} of NetField {1} is {2} bits. Little-endian messages must be 8, 16, or 32 bits.")] PointLittleEndianBitCount(usize, String, usize), - + #[error("IEEE754 float point {0} of NetField {1} is {2} bits, instead of 32 bits.")] PointFloatBitCount(usize, String, usize), } @@ -43,12 +45,12 @@ pub fn validate_all_spec() -> Result<(), Vec> { if __path.is_file() && __path.extension().map_or(false, |ext| ext == "json") { match validate_spec_file(__path.clone()) { - Ok(()) => {}, + Ok(()) => {} Err(__file_errors) => __all_errors.extend(__file_errors), }; } } - Err(__err) => err.to_compile_error().into() + Err(_) => eprintln!("Error opening file"), } } @@ -58,7 +60,10 @@ pub fn validate_all_spec() -> Result<(), Vec> { Err(__all_errors) } } - Err(__err) => err.to_compile_error().into() + Err(_) => { + eprintln!("Could not read from directory"); + Ok(()) + } } } @@ -74,66 +79,105 @@ fn validate_spec_file(_path: PathBuf) -> Result<(), Vec> { let _msgs: Vec = serde_json::from_str(&_contents).unwrap(); for _msg in _msgs { match validate_msg(_msg) { - Ok(()) => {}, - Err(_msg_errors) => _errors.extend(_msg_errors) + Ok(()) => {} + Err(_msg_errors) => _errors.extend(_msg_errors), }; } if _errors.is_empty() { Ok(()) } else { - _errors + Err(_errors) } } - Err(err) => err.to_compile_error().into() - } + Err(_) => { + eprintln!("Error opening file"); + Ok(()) + } + } } /** * Validate a CANMsg */ fn validate_msg(_msg: CANMsg) -> Result<(), Vec> { - let _errors = Vec::new(); + let mut _errors = Vec::new(); - // Check description contains legal chars + // Check description contains legal chars let _desc = _msg.desc.clone(); - if !_desc.chars().all(|c| c.is_alphabetic() || c.is_whitespace()) { - _errors.push(CANSpecError::MessageDescIllegalChars(_msg.id.clone(), _desc)); + if !_desc + .chars() + .all(|c| c.is_alphabetic() || c.is_whitespace()) + { + _errors.push(CANSpecError::MessageDescIllegalChars( + _msg.id.clone(), + _desc, + )); } for _field in _msg.fields { - _name = _field.name.clone(); + let _name = _field.name.clone(); // Check Sim enum frequencies add to 1 - if let Some(SimEnum {f1, f2}) = _field.sim && f1 + f2 != 1.0 { - _errors.push(CANSpecError::FieldSimEnumFrequencySum(_name.clone(), f1, f2)); + if let Some(Sim::SimEnum { options }) = _field.sim { + options.iter().for_each(|opt| { + if opt[0] + opt[1] != 1.0 { + _errors.push(CANSpecError::FieldSimEnumFrequencySum( + _name.clone(), + opt[0], + opt[1], + )); + } + }) } - + // Sum bit count of points for checks let mut _bit_count: usize = 0; for (_i, _point) in _field.points.iter().enumerate() { _bit_count += _point.size; - // Check signed point bit count - if let Some(true) = _point.signed && ![8, 16, 32].contains(&_point.size) { - _errors.push(CANSpecError::PointSignedBitCount(_i, _name.clone(), _point.size)); + // Check signed point bit count + if let Some(true) = _point.signed { + if _point.size != 8 && _point.size != 16 && _point.size != 32 { + _errors.push(CANSpecError::PointSignedBitCount( + _i, + _name.clone(), + _point.size, + )); + } } - - // Check little endian point bit count - if let Some("little") = _point.endianness && ![8, 16, 32].contains(&_point.size) { - _errors.push(CANSpecError::PointLittleEndianBitCount(_i, _name.clone(), _point.size)); + + // Check little endian point bit count + // TODO fix + if let Some(ref s) = _point.endianness { + if s == "little" && _point.size != 8 && _point.size != 16 && _point.size != 32 { + _errors.push(CANSpecError::PointLittleEndianBitCount( + _i, + _name.clone(), + _point.size, + )); + } } // Check IEEE754 f32 point bit count - if let Some(true) = _point.ieee754_f32 && _point.size != 32 { - _errors.push(CANSpecError::PointFloatBitCount(_i, _name.clone(), _point.size)); + if let Some(true) = _point.ieee754_f32 { + if _point.size != 32 { + _errors.push(CANSpecError::PointFloatBitCount( + _i, + _name.clone(), + _point.size, + )); + } } } // Check field total alignment if _bit_count % 2 != 0 { - _errors.push(CANSpecError::FieldTotalByteMisalignment(_name.clone(), _bit_count)); + _errors.push(CANSpecError::FieldTotalByteMisalignment( + _name.clone(), + _bit_count, + )); } } diff --git a/libs/daedalus/src/lib.rs b/libs/daedalus/src/lib.rs index 6e6b27b..844b892 100644 --- a/libs/daedalus/src/lib.rs +++ b/libs/daedalus/src/lib.rs @@ -5,6 +5,7 @@ extern crate serde_json; use calypso_cangen::can_gen_decode::*; use calypso_cangen::can_gen_encode::*; use calypso_cangen::can_types::*; +use calypso_cangen::CANGEN_SPEC_PATH; use proc_macro::TokenStream; use proc_macro2::TokenStream as ProcMacro2TokenStream; use quote::{format_ident, quote}; From 0f8a2b3e593280218e64cc574d09fbfcfced36fc Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 16 Dec 2024 16:30:07 -0500 Subject: [PATCH 05/10] Added new error types, fixed some existing error types, added colored terminal output --- build.rs | 3 +- libs/calypso-cangen/src/validate.rs | 100 ++++++++++++++++++---------- 2 files changed, 67 insertions(+), 36 deletions(-) diff --git a/build.rs b/build.rs index d08e960..7ce53c1 100644 --- a/build.rs +++ b/build.rs @@ -21,7 +21,8 @@ fn main() { Ok(()) => {} Err(errors) => { for error in errors { - eprintln!("CAN spec error: {}", error); + // The \x1b[...m is an ANSI escape sequence for colored terminal output + println!("\x1b[31;1mCAN spec error:\x1b[0m {}", error); } process::exit(1); } diff --git a/libs/calypso-cangen/src/validate.rs b/libs/calypso-cangen/src/validate.rs index 07faea0..d7eff79 100644 --- a/libs/calypso-cangen/src/validate.rs +++ b/libs/calypso-cangen/src/validate.rs @@ -13,11 +13,14 @@ pub enum CANSpecError { #[error("Message {0} description ({1}) contains illegal characters. Message descriptions may only contain letters and spaces.")] MessageDescIllegalChars(String, String), - #[error("{0} totals to {1} bits. NetField totals should be byte-aligned (bit size should be a power of 2).")] - FieldTotalByteMisalignment(String, usize), + #[error("Message {0} totals to {1} bits. Message totals should be byte-aligned (bit size should be a power of 2).")] + MessageTotalByteMisalignment(String, usize), - #[error("Sim frequencies ({1}, {2}) for NetField {0} do not add to 1. Sim enum frequencies must add up to 1.")] - FieldSimEnumFrequencySum(String, f32, f32), + #[error("Sim frequencies for NetField {0} add to {1}. Sim enum frequencies must add up to 1.")] + FieldSimEnumFrequencySum(String, f32), + + #[error("Point {0} of NetField {1} is {2} bits. The maximum size for a point is 32 bits.")] + PointSizeOverMax(usize, String, usize), #[error( "Signed point {0} of NetField {1} is {2} bits. Signed messages must be 8, 16, or 32 bits." @@ -27,17 +30,23 @@ pub enum CANSpecError { #[error("Little-endian point {0} of NetField {1} is {2} bits. Little-endian messages must be 8, 16, or 32 bits.")] PointLittleEndianBitCount(usize, String, usize), + #[error("Point {0} of NetField {1} specifies endianness and is {2} bits. Points with <=8 bits should not specify endianness.")] + PointSmallSizeEndianness(usize, String, usize), + #[error("IEEE754 float point {0} of NetField {1} is {2} bits, instead of 32 bits.")] PointFloatBitCount(usize, String, usize), + + #[error(transparent)] // Pass-through for IO error + IOError(#[from] std::io::Error), } /** * Validate all CAN spec files in CANGEN_SPEC_PATH */ pub fn validate_all_spec() -> Result<(), Vec> { + let mut __all_errors = Vec::new(); match fs::read_dir(CANGEN_SPEC_PATH) { Ok(__entries) => { - let mut __all_errors = Vec::new(); for __entry in __entries { match __entry { Ok(__entry) => { @@ -50,7 +59,7 @@ pub fn validate_all_spec() -> Result<(), Vec> { }; } } - Err(_) => eprintln!("Error opening file"), + Err(__err) => __all_errors.push(__err.into()), } } @@ -60,9 +69,9 @@ pub fn validate_all_spec() -> Result<(), Vec> { Err(__all_errors) } } - Err(_) => { - eprintln!("Could not read from directory"); - Ok(()) + Err(__err) => { + __all_errors.push(__err.into()); + Err(__all_errors) } } } @@ -71,11 +80,11 @@ pub fn validate_all_spec() -> Result<(), Vec> { * Validate a CAN spec file */ fn validate_spec_file(_path: PathBuf) -> Result<(), Vec> { + let mut _errors = Vec::new(); match fs::File::open(_path) { Ok(mut _file) => { let mut _contents = String::new(); let _ = _file.read_to_string(&mut _contents); - let mut _errors = Vec::new(); let _msgs: Vec = serde_json::from_str(&_contents).unwrap(); for _msg in _msgs { match validate_msg(_msg) { @@ -90,9 +99,9 @@ fn validate_spec_file(_path: PathBuf) -> Result<(), Vec> { Err(_errors) } } - Err(_) => { - eprintln!("Error opening file"); - Ok(()) + Err(_err) => { + _errors.push(_err.into()); + Err(_errors) } } } @@ -103,11 +112,14 @@ fn validate_spec_file(_path: PathBuf) -> Result<(), Vec> { fn validate_msg(_msg: CANMsg) -> Result<(), Vec> { let mut _errors = Vec::new(); + // Sum bit count of points for checks + let mut _bit_count: usize = 0; + // Check description contains legal chars let _desc = _msg.desc.clone(); if !_desc .chars() - .all(|c| c.is_alphabetic() || c.is_whitespace()) + .all(|c| c.is_alphabetic() || c.is_whitespace() || c == '_') { _errors.push(CANSpecError::MessageDescIllegalChars( _msg.id.clone(), @@ -120,23 +132,34 @@ fn validate_msg(_msg: CANMsg) -> Result<(), Vec> { // Check Sim enum frequencies add to 1 if let Some(Sim::SimEnum { options }) = _field.sim { - options.iter().for_each(|opt| { - if opt[0] + opt[1] != 1.0 { - _errors.push(CANSpecError::FieldSimEnumFrequencySum( - _name.clone(), - opt[0], - opt[1], - )); - } - }) + let mut _sim_total: f32 = 0.0; + options.iter().for_each(|opt| { _sim_total += opt[1]; }); + if _sim_total != 1.0 { + _errors.push(CANSpecError::FieldSimEnumFrequencySum( + _name.clone(), + _sim_total, + )); + } } - // Sum bit count of points for checks - let mut _bit_count: usize = 0; + let _send = match _field.send { + Some(false) => false, + _ => true + }; for (_i, _point) in _field.points.iter().enumerate() { _bit_count += _point.size; + // Check that point size is at most 32 bits + if _point.size > 32 && _send { + _errors.push(CANSpecError::PointSizeOverMax( + _i, + _name.clone(), + _point.size, + )); + continue; + } + // Check signed point bit count if let Some(true) = _point.signed { if _point.size != 8 && _point.size != 16 && _point.size != 32 { @@ -148,10 +171,17 @@ fn validate_msg(_msg: CANMsg) -> Result<(), Vec> { } } - // Check little endian point bit count - // TODO fix if let Some(ref s) = _point.endianness { - if s == "little" && _point.size != 8 && _point.size != 16 && _point.size != 32 { + // Check that small points don't specify endianness + if _point.size <= 8 { + _errors.push(CANSpecError::PointSmallSizeEndianness( + _i, + _name.clone(), + _point.size, + )); + } + // Check little endian point bit count + else if s == "little" && _point.size != 8 && _point.size != 16 && _point.size != 32 { _errors.push(CANSpecError::PointLittleEndianBitCount( _i, _name.clone(), @@ -171,14 +201,14 @@ fn validate_msg(_msg: CANMsg) -> Result<(), Vec> { } } } + } - // Check field total alignment - if _bit_count % 2 != 0 { - _errors.push(CANSpecError::FieldTotalByteMisalignment( - _name.clone(), - _bit_count, - )); - } + // Check message total alignment + if _bit_count % 8 != 0 { + _errors.push(CANSpecError::MessageTotalByteMisalignment( + _msg.id.clone(), + _bit_count, + )); } if _errors.is_empty() { From da3147f24417a1f4bdcbba7bcaf0565fa009b0b8 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 16 Dec 2024 16:31:12 -0500 Subject: [PATCH 06/10] Fixed message decription error output --- libs/calypso-cangen/src/validate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/calypso-cangen/src/validate.rs b/libs/calypso-cangen/src/validate.rs index d7eff79..ff95a9f 100644 --- a/libs/calypso-cangen/src/validate.rs +++ b/libs/calypso-cangen/src/validate.rs @@ -10,7 +10,7 @@ use thiserror::Error; */ #[derive(Error, Debug)] pub enum CANSpecError { - #[error("Message {0} description ({1}) contains illegal characters. Message descriptions may only contain letters and spaces.")] + #[error("Message {0} description ({1}) contains illegal characters. Message descriptions may only contain letters and whitespace (_ included).")] MessageDescIllegalChars(String, String), #[error("Message {0} totals to {1} bits. Message totals should be byte-aligned (bit size should be a power of 2).")] From 39a1e3f2d8ac311a532e140149e184555c158cd3 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 16 Dec 2024 16:37:02 -0500 Subject: [PATCH 07/10] Fixed sim enum frequency check --- libs/calypso-cangen/src/validate.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/calypso-cangen/src/validate.rs b/libs/calypso-cangen/src/validate.rs index ff95a9f..d721381 100644 --- a/libs/calypso-cangen/src/validate.rs +++ b/libs/calypso-cangen/src/validate.rs @@ -130,11 +130,11 @@ fn validate_msg(_msg: CANMsg) -> Result<(), Vec> { for _field in _msg.fields { let _name = _field.name.clone(); - // Check Sim enum frequencies add to 1 + // Check Sim enum frequencies add to 1 (roughly, f32s are approximate) if let Some(Sim::SimEnum { options }) = _field.sim { let mut _sim_total: f32 = 0.0; options.iter().for_each(|opt| { _sim_total += opt[1]; }); - if _sim_total != 1.0 { + if (_sim_total - 1.0).abs() > 0.00001 { _errors.push(CANSpecError::FieldSimEnumFrequencySum( _name.clone(), _sim_total, From 77959c86c300503c3c2a7eab692a97884ff19ba7 Mon Sep 17 00:00:00 2001 From: harry Date: Mon, 16 Dec 2024 17:03:32 -0500 Subject: [PATCH 08/10] Made MessageTotalByteMisalignment error message more descriptive by including message description --- libs/calypso-cangen/src/validate.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libs/calypso-cangen/src/validate.rs b/libs/calypso-cangen/src/validate.rs index d721381..b0fcee2 100644 --- a/libs/calypso-cangen/src/validate.rs +++ b/libs/calypso-cangen/src/validate.rs @@ -13,8 +13,8 @@ pub enum CANSpecError { #[error("Message {0} description ({1}) contains illegal characters. Message descriptions may only contain letters and whitespace (_ included).")] MessageDescIllegalChars(String, String), - #[error("Message {0} totals to {1} bits. Message totals should be byte-aligned (bit size should be a power of 2).")] - MessageTotalByteMisalignment(String, usize), + #[error("Message {0} ({1}) totals to {2} bits. Message totals should be byte-aligned (bit size should be a power of 2).")] + MessageTotalByteMisalignment(String, String, usize), #[error("Sim frequencies for NetField {0} add to {1}. Sim enum frequencies must add up to 1.")] FieldSimEnumFrequencySum(String, f32), @@ -23,11 +23,11 @@ pub enum CANSpecError { PointSizeOverMax(usize, String, usize), #[error( - "Signed point {0} of NetField {1} is {2} bits. Signed messages must be 8, 16, or 32 bits." + "Signed point {0} of NetField {1} is {2} bits. Signed points must be 8, 16, or 32 bits." )] PointSignedBitCount(usize, String, usize), - #[error("Little-endian point {0} of NetField {1} is {2} bits. Little-endian messages must be 8, 16, or 32 bits.")] + #[error("Little-endian point {0} of NetField {1} is {2} bits. Little-endian points must be 8, 16, or 32 bits.")] PointLittleEndianBitCount(usize, String, usize), #[error("Point {0} of NetField {1} specifies endianness and is {2} bits. Points with <=8 bits should not specify endianness.")] @@ -207,6 +207,7 @@ fn validate_msg(_msg: CANMsg) -> Result<(), Vec> { if _bit_count % 8 != 0 { _errors.push(CANSpecError::MessageTotalByteMisalignment( _msg.id.clone(), + _msg.desc.clone(), _bit_count, )); } From a9efe2628d1ee9e0a57f9c941b404f70d73f20e0 Mon Sep 17 00:00:00 2001 From: harry Date: Fri, 20 Dec 2024 20:12:18 -0500 Subject: [PATCH 09/10] Updated submodule --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 529ad8d..cd564eb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "Embedded-Base"] path = Embedded-Base - url = ../Embedded-Base + url = github.com/Northeastern-Electric-Racing/Embedded-Base.git branch = main From 2300677a678ece7a4af1ce65c47f143dce649d7a Mon Sep 17 00:00:00 2001 From: harry Date: Sat, 21 Dec 2024 14:47:46 -0500 Subject: [PATCH 10/10] Submodule https :p --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index cd564eb..8193e62 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "Embedded-Base"] path = Embedded-Base - url = github.com/Northeastern-Electric-Racing/Embedded-Base.git + url = https://github.com/Northeastern-Electric-Racing/Embedded-Base.git branch = main