fix(hw_defs): improve template error reporting in code generator (#7190)
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import jinja2
|
||||
import pydantic
|
||||
|
||||
import legacy_names
|
||||
import json_index
|
||||
|
||||
from models import KeyEnum
|
||||
from models import HardwareDefinition, KeyEnum
|
||||
|
||||
MAIN_CONTROL_LUT = {
|
||||
# 2 Gimbal radios
|
||||
@@ -17,61 +20,83 @@ MAIN_CONTROL_LUT = {
|
||||
"TH": {"str": "TH", "local": "STR_SURFACE_NAMES1"},
|
||||
}
|
||||
|
||||
# def eprint(*args, **kwargs):
|
||||
# from sys import stderr
|
||||
# print(*args, file=stderr, **kwargs)
|
||||
|
||||
def eprint(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
|
||||
def is_ext_input(input):
|
||||
if input.get("type") != "FLEX":
|
||||
if str(getattr(input, "type", "")) != "FLEX":
|
||||
return False
|
||||
|
||||
name = input.get("name")
|
||||
if name and name.startswith("EXT"):
|
||||
return True
|
||||
|
||||
return False
|
||||
name = str(getattr(input, "name", ""))
|
||||
return name.startswith("EXT")
|
||||
|
||||
|
||||
def generate_from_template(json_filename, template_filename, target):
|
||||
with open(json_filename) as json_file:
|
||||
with open(template_filename) as template_file:
|
||||
root_obj = json.load(json_file)
|
||||
raw_data = json_file.read()
|
||||
|
||||
adc_inputs = root_obj.get("adc_inputs")
|
||||
adc_index = json_index.build_adc_index(adc_inputs)
|
||||
adc_gpios = json_index.build_adc_gpio_port_index(adc_inputs)
|
||||
# Validate using Pydantic model
|
||||
try:
|
||||
hw_def = HardwareDefinition.from_json(raw_data)
|
||||
except pydantic.ValidationError as e:
|
||||
eprint(f"ERROR validating '{json_filename}' (target={target}):")
|
||||
for err in e.errors():
|
||||
loc = " -> ".join(str(l) for l in err["loc"])
|
||||
eprint(f" {loc}: {err['msg']}")
|
||||
sys.exit(1)
|
||||
|
||||
switches = root_obj.get("switches")
|
||||
switch_gpios = json_index.build_switch_gpio_port_index(switches)
|
||||
# Load remaining fields not yet in the Pydantic model
|
||||
root_obj = json.loads(raw_data)
|
||||
|
||||
keys = root_obj.get("keys")
|
||||
key_gpios = json_index.build_key_gpio_port_index(keys)
|
||||
adc_index = json_index.build_adc_index(hw_def.adc_inputs)
|
||||
adc_gpios = json_index.build_adc_gpio_port_index(hw_def.adc_inputs)
|
||||
switch_gpios = json_index.build_switch_gpio_port_index(hw_def.switches)
|
||||
key_gpios = json_index.build_key_gpio_port_index(hw_def.keys)
|
||||
|
||||
trims = root_obj.get("trims")
|
||||
trim_gpios = json_index.build_trim_gpio_port_index(trims)
|
||||
trims = root_obj.get("trims")
|
||||
trim_gpios = json_index.build_trim_gpio_port_index(trims)
|
||||
|
||||
legacy_inputs = legacy_names.inputs_by_target(target)
|
||||
legacy_inputs = legacy_names.inputs_by_target(target)
|
||||
|
||||
env = jinja2.Environment(lstrip_blocks=True, trim_blocks=True)
|
||||
template_dir = os.path.dirname(os.path.abspath(template_filename))
|
||||
template_name = os.path.basename(template_filename)
|
||||
|
||||
env.tests["ext_input"] = is_ext_input
|
||||
env = jinja2.Environment(
|
||||
loader=jinja2.FileSystemLoader(template_dir),
|
||||
lstrip_blocks=True,
|
||||
trim_blocks=True,
|
||||
)
|
||||
|
||||
template_str = template_file.read()
|
||||
template = env.from_string(template_str)
|
||||
env.tests["ext_input"] = is_ext_input
|
||||
|
||||
key_index = list([str(i) for i in KeyEnum])
|
||||
template = env.get_template(template_name)
|
||||
|
||||
print(
|
||||
template.render(
|
||||
root_obj,
|
||||
adc_index=adc_index,
|
||||
adc_gpios=adc_gpios,
|
||||
switch_gpios=switch_gpios,
|
||||
key_gpios=key_gpios,
|
||||
trim_gpios=trim_gpios,
|
||||
legacy_inputs=legacy_inputs,
|
||||
main_labels=MAIN_CONTROL_LUT,
|
||||
key_index=key_index,
|
||||
)
|
||||
)
|
||||
key_index = list([str(i) for i in KeyEnum])
|
||||
|
||||
context = dict(
|
||||
adc_inputs=hw_def.adc_inputs,
|
||||
switches=hw_def.switches,
|
||||
keys=hw_def.keys,
|
||||
trims=trims,
|
||||
adc_index=adc_index,
|
||||
adc_gpios=adc_gpios,
|
||||
switch_gpios=switch_gpios,
|
||||
key_gpios=key_gpios,
|
||||
trim_gpios=trim_gpios,
|
||||
legacy_inputs=legacy_inputs,
|
||||
main_labels=MAIN_CONTROL_LUT,
|
||||
key_index=key_index,
|
||||
)
|
||||
|
||||
try:
|
||||
print(template.render(context))
|
||||
except jinja2.UndefinedError as e:
|
||||
eprint(f"ERROR rendering template '{template_name}'"
|
||||
f" with json='{json_filename}', target='{target}':")
|
||||
eprint(f" {e}")
|
||||
none_keys = [k for k, v in context.items() if v is None]
|
||||
if none_keys:
|
||||
eprint(f" None-valued context keys: {none_keys}")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#
|
||||
# These methods are used to build helper indexes on JSON structures
|
||||
# These methods are used to build helper indexes on data structures
|
||||
#
|
||||
def build_adc_index(adc_inputs):
|
||||
i = 0
|
||||
index = {}
|
||||
for adc_input in adc_inputs["inputs"]:
|
||||
name = adc_input["name"]
|
||||
index[name] = i
|
||||
for adc_input in adc_inputs.inputs:
|
||||
index[str(adc_input.name)] = i
|
||||
i = i + 1
|
||||
|
||||
return index
|
||||
@@ -22,11 +21,11 @@ def append_to_index(d, key, val):
|
||||
def build_adc_gpio_port_index(adc_inputs):
|
||||
i = 0
|
||||
gpios = {}
|
||||
for adc_input in adc_inputs["inputs"]:
|
||||
if adc_input["adc"] == "SPI":
|
||||
for adc_input in adc_inputs.inputs:
|
||||
if str(adc_input.adc) == "SPI":
|
||||
continue
|
||||
|
||||
gpio = adc_input["gpio"]
|
||||
gpio = getattr(adc_input, "gpio", None)
|
||||
if gpio is None:
|
||||
i = i + 1
|
||||
continue
|
||||
@@ -34,7 +33,7 @@ def build_adc_gpio_port_index(adc_inputs):
|
||||
if gpio not in gpios:
|
||||
gpios[gpio] = []
|
||||
|
||||
pin = {"pin": adc_input["pin"], "idx": i}
|
||||
pin = {"pin": adc_input.pin, "idx": i}
|
||||
|
||||
gpios[gpio].append(pin)
|
||||
i = i + 1
|
||||
@@ -45,22 +44,22 @@ def build_adc_gpio_port_index(adc_inputs):
|
||||
def build_switch_gpio_port_index(switches):
|
||||
gpios = {}
|
||||
for switch in switches:
|
||||
sw_type = switch["type"]
|
||||
sw_type = str(switch.type)
|
||||
|
||||
if sw_type == "2POS" or sw_type == "FSWITCH":
|
||||
gpio = switch.get("gpio")
|
||||
pin = switch.get("pin")
|
||||
gpio = switch.gpio
|
||||
pin = switch.pin
|
||||
if gpio is not None and pin is not None:
|
||||
append_to_index(gpios, gpio, pin)
|
||||
|
||||
elif sw_type == "3POS":
|
||||
gpio_high = switch.get("gpio_high")
|
||||
pin_high = switch.get("pin_high")
|
||||
gpio_high = switch.gpio_high
|
||||
pin_high = switch.pin_high
|
||||
if gpio_high is not None and pin_high is not None:
|
||||
append_to_index(gpios, gpio_high, pin_high)
|
||||
|
||||
gpio_low = switch.get("gpio_low")
|
||||
pin_low = switch.get("pin_low")
|
||||
gpio_low = switch.gpio_low
|
||||
pin_low = switch.pin_low
|
||||
if gpio_low is not None and pin_low is not None:
|
||||
append_to_index(gpios, gpio_low, pin_low)
|
||||
|
||||
@@ -88,6 +87,6 @@ def build_trim_gpio_port_index(trims):
|
||||
def build_key_gpio_port_index(keys):
|
||||
gpios = {}
|
||||
for key in keys:
|
||||
append_to_index(gpios, key["gpio"], key["pin"])
|
||||
append_to_index(gpios, key.gpio, key.pin)
|
||||
|
||||
return gpios
|
||||
return gpios
|
||||
|
||||
@@ -26,6 +26,11 @@ class ADC(BaseModel):
|
||||
dma_stream: Optional[str] = None
|
||||
dma_stream_irq: Optional[str] = None
|
||||
dma_stream_irq_handler: Optional[str] = None
|
||||
# SPI ADC fields
|
||||
gpio_pin_sck: Optional[str] = None
|
||||
gpio_pin_miso: Optional[str] = None
|
||||
gpio_pin_mosi: Optional[str] = None
|
||||
gpio_pin_cs: Optional[str] = None
|
||||
|
||||
|
||||
StickEnum = StrEnum(
|
||||
@@ -75,6 +80,7 @@ InputType = StrEnum(
|
||||
"VBAT",
|
||||
"RTC_BAT",
|
||||
"RAW",
|
||||
"LUX",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -100,6 +106,7 @@ class StickInput(BaseModel):
|
||||
pin: Optional[str] = None
|
||||
channel: Optional[Union[str, int]] = None
|
||||
inverted: Optional[bool] = False
|
||||
pwm_channel: Optional[int] = None
|
||||
|
||||
|
||||
class FlexInput(BaseModel):
|
||||
@@ -110,7 +117,7 @@ class FlexInput(BaseModel):
|
||||
pin: Optional[str] = None
|
||||
channel: Optional[Union[str, int]] = None
|
||||
inverted: Optional[bool] = False
|
||||
default: Optional[FlexType] = FlexType.NONE
|
||||
default: Optional[FlexType] = None
|
||||
label: Optional[str] = None
|
||||
short_label: Optional[str] = None
|
||||
|
||||
@@ -150,7 +157,16 @@ class RTCBatInput(BaseModel):
|
||||
channel: str
|
||||
|
||||
|
||||
Input = Union[StickInput, FlexInput, SwitchInput, RawInput, VBatInput, RTCBatInput]
|
||||
class LuxInput(BaseModel):
|
||||
name: Literal["LUX"]
|
||||
type: Literal["LUX"]
|
||||
adc: ADCNameType
|
||||
gpio: Optional[str] = None
|
||||
pin: Optional[str] = None
|
||||
channel: Optional[Union[str, int]] = None
|
||||
|
||||
|
||||
Input = Union[StickInput, FlexInput, SwitchInput, RawInput, VBatInput, RTCBatInput, LuxInput]
|
||||
|
||||
|
||||
class ADCInputs(BaseModel):
|
||||
@@ -230,6 +246,8 @@ class Switch(BaseModel):
|
||||
default: Optional[SwitchTypeEnum] = SwitchTypeEnum.NONE
|
||||
flags: Optional[int] = 0
|
||||
inverted: Optional[bool] = False
|
||||
is_cfs: Optional[bool] = False
|
||||
cfs_idx: Optional[int] = None
|
||||
adc_input: Optional[str] = None
|
||||
gpio: Optional[str] = None
|
||||
pin: Optional[str] = None
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// This file has been generated from the target's JSON hardware description
|
||||
//
|
||||
|
||||
{% set pwm_inputs = adc_inputs.inputs | selectattr('pwm_channel', 'defined') | list %}
|
||||
{% set pwm_inputs = adc_inputs.inputs | selectattr('type', '==', 'STICK') | selectattr('pwm_channel', 'ne', none) | list %}
|
||||
static const stick_pwm_input_t _PWM_inputs[] = {
|
||||
{% for input in pwm_inputs %}
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user