Skip to content

Commit

Permalink
added code generation for simulation (#168)
Browse files Browse the repository at this point in the history
* added sim code generation

* JSON definitions for most of YAML (still in progress)

* remove sneaky quotes

* created struct for constructor params

---------

Co-authored-by: Jack Rubacha <[email protected]>
  • Loading branch information
tszwinglitw and jr1221 authored Oct 16, 2024
1 parent 357727f commit 3b6642e
Show file tree
Hide file tree
Showing 11 changed files with 1,197 additions and 579 deletions.
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

0 comments on commit 3b6642e

Please sign in to comment.