Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added code generation for simulation #168

Merged
merged 10 commits into from
Oct 16, 2024
4 changes: 4 additions & 0 deletions cangen/CANField.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ class NetField:
points: list[CANPoint]
send: bool = True
topic_append: bool = False
sim_min: Optional[float] = -1.0
sim_max: Optional[float] = -1.0
sim_inc_min: Optional[float] = -1.0
sim_inc_max: Optional[float] = -1.0
1 change: 1 addition & 0 deletions cangen/CANMsg.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class CANMsg:
id: str # Hex value of CAN ID, i.e. `0x88`
desc: str # Brief name of CAN message, used for generating function names
fields: list[NetField] # List of CAN fields in the message
sim_freq: Optional[int] # Frequency to simulate this message at, in ms

@dataclass
class EncodableCANMsg(CANMsg):
Expand Down
4 changes: 3 additions & 1 deletion cangen/Result.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ class Result:
encode_data: str
encode_master_mapping: str
format_data: str
simulate_data: str

def __init__(self, decode_data: str, master_mapping: str, encode_data: str, encode_master_mapping: str, format_data: str):
def __init__(self, decode_data: str, master_mapping: str, encode_data: str, encode_master_mapping: str, format_data: str, simulate_data: str = ""):
self.decode_data = decode_data
self.decode_master_mapping = master_mapping
self.encode_data = encode_data
self.encode_master_mapping = encode_master_mapping
self.format_data = format_data
self.simulate_data = simulate_data
72 changes: 71 additions & 1 deletion cangen/RustSynthFromJSON.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from cangen.Result import Result
from typing import List, Dict, Optional
import math
import re
import logging

class RustSynthFromJSON:
"""
Expand All @@ -13,7 +15,8 @@ def parse_messages(self, msgs: List[Dict]) -> Result:
returns a Result object that contains the synthesized Rust code for the
decode_data.rs and master_mapping.rs files
"""
result = Result("", "", "", "", "")

result = Result("", "", "", "", "", "")
result.decode_data += RustSnippets.ignore_clippy
result.decode_data += RustSnippets.decode_data_import
result.decode_data += RustSnippets.decode_mock
Expand Down Expand Up @@ -49,6 +52,7 @@ def parse_messages(self, msgs: List[Dict]) -> Result:

result.format_data = RustSnippets.format_impl

result.simulate_data = self.create_simulate_data(msgs)

return result

Expand Down Expand Up @@ -343,6 +347,72 @@ def encode_data_inst(self, msg: Dict) -> str:
is_ext: {str(msg['is_ext'] if 'is_ext' in msg else 'false').lower()},
}}"""

def create_simulate_data(self, msgs: List[Dict]) -> str:
def sanitize_fnname(name):
name = re.sub(r'\W|^(?=\d)', '_', name)
name = name.lower()
return name

sim_funcbody = ""
# process each CAN message
for msg in msgs:
if ('sim_freq' in msg.keys()):
id = msg['id']
desc = msg['desc']
sim_freq = msg['sim_freq']
net_fields = msg['fields']
for netfield in net_fields:
# check if sim_min, sim_max, sim_inc_max, sim_inc_min, points are present
if ("sim_min" not in netfield.keys() or
"sim_max" not in netfield.keys() or
"sim_inc_max" not in netfield.keys() or
"sim_inc_min" not in netfield.keys()):
continue

component_name = netfield['name']
component_var = sanitize_fnname(component_name)
unit = netfield['unit']
sim_min = netfield['sim_min']
sim_max = netfield['sim_max']
sim_inc_max = netfield['sim_inc_max']
sim_inc_min = netfield['sim_inc_min']
n_canpoints = len(netfield['points'])

new_component = f"""
let {component_var}_attr: SimulatedComponentAttr = SimulatedComponentAttr {{
sim_min: {float(sim_min)},
sim_max: {float(sim_max)},
sim_inc_min: {float(sim_inc_min)},
sim_inc_max: {float(sim_inc_max)},
sim_freq: {float(sim_freq)},
n_canpoints: {n_canpoints},
id: "{id}".to_string(),
}};


let {component_var} = SimulatedComponent::new(
"{component_name}".to_string(),
"{unit}".to_string(),
{component_var}_attr,
);
simulatable_messages.push({component_var});
"""
sim_funcbody += new_component


sim_fnblock = f"""
#![allow(clippy::all)]
use crate::simulatable_message::{{SimulatedComponent, SimulatedComponentAttr}};
pub fn create_simulated_components() -> Vec<SimulatedComponent> {{
let mut simulatable_messages: Vec<SimulatedComponent> = Vec::new();

{sim_funcbody}

simulatable_messages
}}
"""
return sim_fnblock



class RustSnippets:
Expand Down
Loading
Loading