Skip to content

Commit

Permalink
Add Length Checker
Browse files Browse the repository at this point in the history
  • Loading branch information
Peyton-McKee committed Feb 26, 2024
1 parent 6768b1e commit d54ed4b
Show file tree
Hide file tree
Showing 4 changed files with 253 additions and 198 deletions.
78 changes: 53 additions & 25 deletions oxy/RustSynth.py
Original file line number Diff line number Diff line change
@@ -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::<Data>" # 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::<Data>" # 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::<Data> {
Expand All @@ -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 {
Expand All @@ -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:
Expand All @@ -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))}"

Expand All @@ -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:
Expand All @@ -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})"
Expand Down
2 changes: 0 additions & 2 deletions oxy/structs/CANMsg.py
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
Loading

0 comments on commit d54ed4b

Please sign in to comment.