From d54ed4bd974e7443f67b222191d91fbdfa2befd9 Mon Sep 17 00:00:00 2001 From: Peyton-McKee Date: Sun, 25 Feb 2024 20:27:03 -0500 Subject: [PATCH] Add Length Checker --- oxy/RustSynth.py | 78 ++++++--- oxy/structs/CANMsg.py | 2 - src/decode_data.rs | 370 +++++++++++++++++++++++------------------- src/mqtt.rs | 1 - 4 files changed, 253 insertions(+), 198 deletions(-) diff --git a/oxy/RustSynth.py b/oxy/RustSynth.py index c7a2d40..227eb81 100644 --- a/oxy/RustSynth.py +++ b/oxy/RustSynth.py @@ -1,21 +1,28 @@ from structs.CANField import CANField from structs.CANMsg import CANMsg -from structs.Messages import Messages from structs.Result import Result -from structs.NetworkEncoding import CSV, SinglePoint from typing import List + class RustSynth: - ''' + """ A class to synthesize Rust from a given CANMsg spec. - ''' - - ignore_clippy: str = "#![allow(clippy::all)]\n" # Ignoring clippy for decode_data because it's autogenerated and has some unnecessary type casting to ensure correct types - decode_data_import: str = "use super::data::{Data,FormatData as fd, ProcessData as pd}; \n" # Importing the Data struct and the FormatData and ProcessData traits - - decode_return_type: str = "Vec::" # The return type of any decode function - decode_return_value: str = f" let result = vec![" # Initializing the result vector - decode_close: str = " ]; \n result\n}\n" # Returning the result vector and closing the function + """ + + ignore_clippy: str = ( + "#![allow(clippy::all)]\n" # Ignoring clippy for decode_data because it's autogenerated and has some unnecessary type casting to ensure correct types + ) + decode_data_import: str = ( + "use super::data::{Data,FormatData as fd, ProcessData as pd}; \n" # Importing the Data struct and the FormatData and ProcessData traits + ) + + decode_return_type: str = "Vec::" # The return type of any decode function + decode_return_value: str = ( + f" let result = vec![" # Initializing the result vector + ) + decode_close: str = ( + " ]; \n result\n}\n" # Returning the result vector and closing the function + ) decode_mock: str = """ pub fn decode_mock(_data: &[u8]) -> Vec:: { @@ -24,13 +31,19 @@ class RustSynth: ]; result } -""" # A mock decode function that is used for messages that don't have a decode function +""" # A mock decode function that is used for messages that don't have a decode function - master_mapping_import: str = "use super::decode_data::*; \nuse super::data::Data; \n" # Importing all the functions in decode_data.rs file and the Data struct + master_mapping_import: str = ( + "use super::decode_data::*; \nuse super::data::Data; \n" # Importing all the functions in decode_data.rs file and the Data struct + ) - master_mapping_signature: str = "pub fn get_message_info(id: &u32) -> MessageInfo { \n match id {" # The signature of the master_mapping function + master_mapping_signature: str = ( + "pub fn get_message_info(id: &u32) -> MessageInfo { \n match id {" # The signature of the master_mapping function + ) - master_mapping_closing: str = " _ => MessageInfo::new(decode_mock), \n }\n}" # The closing of the master_mapping function and the default case for the match statement that returns the mock decode function + master_mapping_closing: str = ( + " _ => MessageInfo::new(decode_mock), \n }\n}" # The closing of the master_mapping function and the default case for the match statement that returns the mock decode function + ) message_info = """ pub struct MessageInfo { @@ -44,7 +57,7 @@ class RustSynth: } } } -""" # The MessageInfo struct that is used to store the decode function for a given message +""" # The MessageInfo struct that is used to store the decode function for a given message # The main function of the RustSynth class. Takes a list of CANMsgs and returns a Result object that contains the synthesized Rust code for the decode_data.rs and master_mapping.rs files def parse_messages(self, msgs: List[CANMsg]) -> Result: @@ -66,33 +79,48 @@ def parse_messages(self, msgs: List[CANMsg]) -> Result: # Helper function that maps a given CANMsg to its decode function def map_msg_to_decoder(self, msg: CANMsg) -> str: - return f" {msg.id} => MessageInfo::new({self.function_name(msg.desc)}),\n" + return f" {msg.id} => MessageInfo::new({self.function_name(msg.desc)}),\n" # Helper function that synthesizes the decode function for a given CANMsg def synthesize(self, msg: CANMsg) -> str: signature: str = self.signature(msg.desc) + length_check: str = self.add_length_check(msg.networkEncoding[0].fields) generated_lines: list[str] = [] # Generate a line for each field in the message generated_lines += self.parse_network_encoding(msg) - total_list: list[str] = [signature, self.decode_return_value] + generated_lines + [self.decode_close] + total_list: list[str] = ( + [signature, length_check, self.decode_return_value] + + generated_lines + + [self.decode_close] + ) return "\n".join(total_list) - def parse_network_encoding(self, msg: CANMsg) -> list[str]: # Change return type to list[str] + def parse_network_encoding( + self, msg: CANMsg + ) -> list[str]: # Change return type to list[str] result = [] networkEncoding = msg.networkEncoding[0] if networkEncoding.id == "csv": result.append(f" {networkEncoding.start}") - result.append(f" {','.join(self.decode_field_value(field) for field in networkEncoding.fields)}") + result.append( + f" {','.join(self.decode_field_value(field) for field in networkEncoding.fields)}" + ) result.append(f" {networkEncoding.closing}") - result.append(f" , \"{networkEncoding.topic}\", \"{networkEncoding.unit}\")") + result.append( + f' , "{networkEncoding.topic}", "{networkEncoding.unit}")' + ) elif networkEncoding.id == "single_point": for field in networkEncoding.fields: result.append(f" {networkEncoding.start}") result.append(f" {self.decode_field_value(field)}") result.append(f" {networkEncoding.closing}") - result.append(f" , \"{field.name}\", \"{field.unit}\"), ") + result.append(f' , "{field.name}", "{field.unit}"), ') return result + def add_length_check(self, fields: List[CANField]) -> str: + fieldSize = sum(field.size for field in fields) + return f"if data.len() < {fieldSize} {{ return vec![]; }}" + def decode_field_value(self, field: CANField) -> str: return f"{self.format_data(field, self.parse_decoders(field))}" @@ -106,7 +134,7 @@ def signature(self, desc: str) -> str: # Helper function that generates a line the data struct for a given CANField value def finalize_line(self, topic: str, unit: str, val: str) -> str: - return f" Data::new({val}, \"{topic}\", \"{unit}\")," + return f' Data::new({val}, "{topic}", "{unit}"),' # Helper function that parses the decoders for a given CANField by applying the decoders to the data and casting the result to the final type of the CANField. def parse_decoders(self, field: CANField) -> str: @@ -116,14 +144,14 @@ def parse_decoders(self, field: CANField) -> str: base: str if field.size == 1: base = f"data[{field.index}]" - else : + else: base = f"&data[{field.index}..{field.index + field.size}]" for decoder in field.decodings: base = f"pd::{decoder.repr}({base} as {decoder.entry_type}, {decoder.bits})" return f"{base} as {field.final_type}" # Helper function that formats the data for a given CANField based off the format of the CANField if it exists, returns the decoded data otherwise - def format_data(self, field:CANField, decoded_data: str) -> str: + def format_data(self, field: CANField, decoded_data: str) -> str: cf = decoded_data if field.format: cf = f"fd::{field.format}({decoded_data})" diff --git a/oxy/structs/CANMsg.py b/oxy/structs/CANMsg.py index b199428..ac50c8b 100644 --- a/oxy/structs/CANMsg.py +++ b/oxy/structs/CANMsg.py @@ -1,8 +1,6 @@ from __future__ import annotations -from .CANField import CANField from dataclasses import dataclass from .NetworkEncoding import NetworkEncoding -from ruamel.yaml import Optional @dataclass class CANMsg: diff --git a/src/decode_data.rs b/src/decode_data.rs index 6612274..2f9ac86 100644 --- a/src/decode_data.rs +++ b/src/decode_data.rs @@ -1,207 +1,237 @@ #![allow(clippy::all)] -use super::data::{Data,FormatData as fd, ProcessData as pd}; +use super::data::{Data, FormatData as fd, ProcessData as pd}; -pub fn decode_mock(_data: &[u8]) -> Vec:: { - let result = vec![ - Data::new(vec![0.0], "Mock", "") - ]; +pub fn decode_mock(_data: &[u8]) -> Vec { + let result = vec![Data::new(vec![0.0], "Mock", "")]; result } -pub fn decode_accumulator_status(data: &[u8]) -> Vec:: { +pub fn decode_accumulator_status(data: &[u8]) -> Vec { + if data.len() < 8 { + return vec![]; + } let result = vec![ - Data::new(vec![ - fd::high_voltage(pd::big_endian(&data[0..2] as &[u8], 8) as f32) - ] - , "BMS/Pack/Voltage", "V"), - Data::new(vec![ - fd::current(pd::twos_comp(pd::big_endian(&data[2..4] as &[u8], 8) as u32, 16) as f32) - ] - , "BMS/Pack/Current", "A"), - Data::new(vec![ - pd::big_endian(&data[4..6] as &[u8], 8) as f32 - ] - , "BMS/Pack/Amp-hours", "Ah"), - Data::new(vec![ - data[6] as f32 - ] - , "BMS/Pack/SOC", "%"), - Data::new(vec![ - data[7] as f32 - ] - , "BMS/Pack/Health", "%"), - ]; + Data::new( + vec![fd::high_voltage( + pd::big_endian(&data[0..2] as &[u8], 8) as f32 + )], + "BMS/Pack/Voltage", + "V", + ), + Data::new( + vec![fd::current( + pd::twos_comp(pd::big_endian(&data[2..4] as &[u8], 8) as u32, 16) as f32, + )], + "BMS/Pack/Current", + "A", + ), + Data::new( + vec![pd::big_endian(&data[4..6] as &[u8], 8) as f32], + "BMS/Pack/Amp-hours", + "Ah", + ), + Data::new(vec![data[6] as f32], "BMS/Pack/SOC", "%"), + Data::new(vec![data[7] as f32], "BMS/Pack/Health", "%"), + ]; result } -pub fn decode_bms_status(data: &[u8]) -> Vec:: { +pub fn decode_bms_status(data: &[u8]) -> Vec { + if data.len() < 8 { + return vec![]; + } let result = vec![ - Data::new(vec![ - data[0] as f32 - ] - , "BMS/State", ""), - Data::new(vec![ - pd::little_endian(&data[1..5] as &[u8], 8) as f32 - ] - , "BMS/Faults", ""), - Data::new(vec![ - pd::twos_comp(data[5] as u32, 8) as f32 - ] - , "BMS/Temps/Average", "C"), - Data::new(vec![ - pd::twos_comp(data[6] as u32, 8) as f32 - ] - , "BMS/Temps/Internal", "C"), - Data::new(vec![ - data[7] as f32 - ] - , "BMS/Cells/BurningStatus", ""), - ]; + Data::new(vec![data[0] as f32], "BMS/State", ""), + Data::new( + vec![pd::little_endian(&data[1..5] as &[u8], 8) as f32], + "BMS/Faults", + "", + ), + Data::new( + vec![pd::twos_comp(data[5] as u32, 8) as f32], + "BMS/Temps/Average", + "C", + ), + Data::new( + vec![pd::twos_comp(data[6] as u32, 8) as f32], + "BMS/Temps/Internal", + "C", + ), + Data::new(vec![data[7] as f32], "BMS/Cells/BurningStatus", ""), + ]; result } -pub fn decode_shutdown_control(data: &[u8]) -> Vec:: { - let result = vec![ - Data::new(vec![ - data[0] as f32 - ] - , "BMS/Shutdown/MPE", ""), - ]; +pub fn decode_shutdown_control(data: &[u8]) -> Vec { + if data.len() < 1 { + return vec![]; + } + let result = vec![Data::new(vec![data[0] as f32], "BMS/Shutdown/MPE", "")]; result } -pub fn decode_cell_data(data: &[u8]) -> Vec:: { +pub fn decode_cell_data(data: &[u8]) -> Vec { + if data.len() < 10 { + return vec![]; + } let result = vec![ - Data::new(vec![ - fd::cell_voltage(pd::little_endian(&data[0..2] as &[u8], 8) as f32) - ] - , "BMS/Cells/Volts/High/Value", "V"), - Data::new(vec![ - pd::half(data[2] as u8, 4) as f32 - ] - , "BMS/Cells/Volts/High/Chip", ""), - Data::new(vec![ - pd::half(data[3] as u8, 0) as f32 - ] - , "BMS/Cells/Volts/High/Cell", ""), - Data::new(vec![ - fd::cell_voltage(pd::little_endian(&data[4..6] as &[u8], 8) as f32) - ] - , "BMS/Cells/Volts/Low/Value", "V"), - Data::new(vec![ - pd::half(data[6] as u8, 4) as f32 - ] - , "BMS/Cells/Volts/Low/Chip", ""), - Data::new(vec![ - pd::half(data[7] as u8, 0) as f32 - ] - , "BMS/Cells/Volts/Low/Cell", ""), - Data::new(vec![ - fd::cell_voltage(pd::little_endian(&data[8..10] as &[u8], 8) as f32) - ] - , "BMS/Cells/Volts/Avg/Value", "V"), - ]; + Data::new( + vec![fd::cell_voltage( + pd::little_endian(&data[0..2] as &[u8], 8) as f32 + )], + "BMS/Cells/Volts/High/Value", + "V", + ), + Data::new( + vec![pd::half(data[2] as u8, 4) as f32], + "BMS/Cells/Volts/High/Chip", + "", + ), + Data::new( + vec![pd::half(data[3] as u8, 0) as f32], + "BMS/Cells/Volts/High/Cell", + "", + ), + Data::new( + vec![fd::cell_voltage( + pd::little_endian(&data[4..6] as &[u8], 8) as f32 + )], + "BMS/Cells/Volts/Low/Value", + "V", + ), + Data::new( + vec![pd::half(data[6] as u8, 4) as f32], + "BMS/Cells/Volts/Low/Chip", + "", + ), + Data::new( + vec![pd::half(data[7] as u8, 0) as f32], + "BMS/Cells/Volts/Low/Cell", + "", + ), + Data::new( + vec![fd::cell_voltage( + pd::little_endian(&data[8..10] as &[u8], 8) as f32, + )], + "BMS/Cells/Volts/Avg/Value", + "V", + ), + ]; result } -pub fn decode_cell_temperatures(data: &[u8]) -> Vec:: { +pub fn decode_cell_temperatures(data: &[u8]) -> Vec { + if data.len() < 10 { + return vec![]; + } let result = vec![ - Data::new(vec![ - pd::twos_comp(pd::little_endian(&data[0..2] as &[u8], 8) as u32, 16) as f32 - ] - , "BMS/Cells/Temp/High/Value", "C"), - Data::new(vec![ - pd::half(data[2] as u8, 4) as f32 - ] - , "BMS/Cells/Temp/High/Cell", ""), - Data::new(vec![ - pd::half(data[3] as u8, 0) as f32 - ] - , "BMS/Cells/Temp/High/Chip", ""), - Data::new(vec![ - pd::twos_comp(pd::little_endian(&data[4..6] as &[u8], 8) as u32, 16) as f32 - ] - , "BMS/Cells/Temp/Low/Value", "C"), - Data::new(vec![ - pd::half(data[6] as u8, 4) as f32 - ] - , "BMS/Cells/Temp/Low/Cell", ""), - Data::new(vec![ - pd::half(data[7] as u8, 0) as f32 - ] - , "BMS/Cells/Temp/Low/Chip", ""), - Data::new(vec![ - pd::twos_comp(pd::little_endian(&data[8..10] as &[u8], 8) as u32, 16) as f32 - ] - , "BMS/Cells/Temp/Avg/Value", "C"), - ]; + Data::new( + vec![pd::twos_comp(pd::little_endian(&data[0..2] as &[u8], 8) as u32, 16) as f32], + "BMS/Cells/Temp/High/Value", + "C", + ), + Data::new( + vec![pd::half(data[2] as u8, 4) as f32], + "BMS/Cells/Temp/High/Cell", + "", + ), + Data::new( + vec![pd::half(data[3] as u8, 0) as f32], + "BMS/Cells/Temp/High/Chip", + "", + ), + Data::new( + vec![pd::twos_comp(pd::little_endian(&data[4..6] as &[u8], 8) as u32, 16) as f32], + "BMS/Cells/Temp/Low/Value", + "C", + ), + Data::new( + vec![pd::half(data[6] as u8, 4) as f32], + "BMS/Cells/Temp/Low/Cell", + "", + ), + Data::new( + vec![pd::half(data[7] as u8, 0) as f32], + "BMS/Cells/Temp/Low/Chip", + "", + ), + Data::new( + vec![pd::twos_comp(pd::little_endian(&data[8..10] as &[u8], 8) as u32, 16) as f32], + "BMS/Cells/Temp/Avg/Value", + "C", + ), + ]; result } -pub fn decode_segment_temperatures(data: &[u8]) -> Vec:: { +pub fn decode_segment_temperatures(data: &[u8]) -> Vec { + if data.len() < 4 { + return vec![]; + } let result = vec![ - Data::new(vec![ - pd::twos_comp(data[0] as u32, 8) as f32 - ] - , "BMS/Segment/Temp/1", "C"), - Data::new(vec![ - pd::twos_comp(data[1] as u32, 8) as f32 - ] - , "BMS/Segment/Temp/2", "C"), - Data::new(vec![ - pd::twos_comp(data[2] as u32, 8) as f32 - ] - , "BMS/Segment/Temp/3", "C"), - Data::new(vec![ - pd::twos_comp(data[3] as u32, 8) as f32 - ] - , "BMS/Segment/Temp/4", "C"), - ]; + Data::new( + vec![pd::twos_comp(data[0] as u32, 8) as f32], + "BMS/Segment/Temp/1", + "C", + ), + Data::new( + vec![pd::twos_comp(data[1] as u32, 8) as f32], + "BMS/Segment/Temp/2", + "C", + ), + Data::new( + vec![pd::twos_comp(data[2] as u32, 8) as f32], + "BMS/Segment/Temp/3", + "C", + ), + Data::new( + vec![pd::twos_comp(data[3] as u32, 8) as f32], + "BMS/Segment/Temp/4", + "C", + ), + ]; result } -pub fn decode_mpu_acceleromter(data: &[u8]) -> Vec:: { - let result = vec![ - Data::new(vec![ - fd::acceleration(pd::big_endian(&data[0..2] as &[u8], 8) as f32),fd::acceleration(pd::big_endian(&data[2..4] as &[u8], 8) as f32),fd::acceleration(pd::big_endian(&data[4..6] as &[u8], 8) as f32) - ] - , "MPU/Accel", "g") - ]; +pub fn decode_mpu_acceleromter(data: &[u8]) -> Vec { + if data.len() < 6 { + return vec![]; + } + let result = vec![Data::new( + vec![ + fd::acceleration(pd::big_endian(&data[0..2] as &[u8], 8) as f32), + fd::acceleration(pd::big_endian(&data[2..4] as &[u8], 8) as f32), + fd::acceleration(pd::big_endian(&data[4..6] as &[u8], 8) as f32), + ], + "MPU/Accel", + "g", + )]; result } -pub fn decode_mpu_status(data: &[u8]) -> Vec:: { +pub fn decode_mpu_status(data: &[u8]) -> Vec { + if data.len() < 4 { + return vec![]; + } let result = vec![ - Data::new(vec![ - data[0] as f32 - ] - , "MPU/State/Mode", ""), - Data::new(vec![ - data[1] as f32 - ] - , "MPU/State/Torque_Limit_Percentage", ""), - Data::new(vec![ - data[2] as f32 - ] - , "MPU/State/Regen_Strength", ""), - Data::new(vec![ - data[3] as f32 - ] - , "MPU/State/Traction_Control", ""), - ]; + Data::new(vec![data[0] as f32], "MPU/State/Mode", ""), + Data::new( + vec![data[1] as f32], + "MPU/State/Torque_Limit_Percentage", + "", + ), + Data::new(vec![data[2] as f32], "MPU/State/Regen_Strength", ""), + Data::new(vec![data[3] as f32], "MPU/State/Traction_Control", ""), + ]; result } -pub fn decode_wheel_state(data: &[u8]) -> Vec:: { +pub fn decode_wheel_state(data: &[u8]) -> Vec { + if data.len() < 2 { + return vec![]; + } let result = vec![ - Data::new(vec![ - data[0] as f32 - ] - , "WHEEL/Buttons/1", ""), - Data::new(vec![ - data[1] as f32 - ] - , "WHEEL/Buttons/2", ""), - ]; + Data::new(vec![data[0] as f32], "WHEEL/Buttons/1", ""), + Data::new(vec![data[1] as f32], "WHEEL/Buttons/2", ""), + ]; result } - diff --git a/src/mqtt.rs b/src/mqtt.rs index e8aee09..38d2266 100644 --- a/src/mqtt.rs +++ b/src/mqtt.rs @@ -116,7 +116,6 @@ impl MqttClient { .finalize(); if let Err(e) = client.connect(conn_opts) { println!("Unable to connect:\n\t{:?}", e); - process::exit(1); } } else { println!("Client not initialized, please set host first");