STM32G474RB firmware for solar buck converter with MPPT, CC control, Vfly compensation, and adaptive deadtime. Includes Textual TUI debug console for real-time telemetry, parameter tuning, and SQLite logging. Added pyproject.toml for uv: `cd code64 && uv run debug-console` Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
103 lines
2.9 KiB
Python
103 lines
2.9 KiB
Python
"""Editable parameter rows per controller group."""
|
|
from textual.app import ComposeResult
|
|
from textual.containers import Vertical, Horizontal
|
|
from textual.widgets import Static, Input, Button
|
|
|
|
from ..protocol import ParamDef, PARAMS, build_param_write
|
|
|
|
|
|
class ParamRow(Horizontal):
|
|
"""Single parameter row with label, current value, input, and send button."""
|
|
|
|
DEFAULT_CSS = """
|
|
ParamRow {
|
|
height: auto;
|
|
width: 100%;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
ParamRow .param-label {
|
|
width: 16;
|
|
height: 1;
|
|
padding: 0 1;
|
|
}
|
|
ParamRow .param-value {
|
|
width: 14;
|
|
height: 1;
|
|
padding: 0 1;
|
|
color: $success;
|
|
}
|
|
ParamRow Input {
|
|
width: 1fr;
|
|
}
|
|
ParamRow Button {
|
|
width: 6;
|
|
min-width: 6;
|
|
}
|
|
"""
|
|
|
|
def __init__(self, param: ParamDef, send_callback):
|
|
super().__init__()
|
|
self.param = param
|
|
self._send_callback = send_callback
|
|
|
|
def compose(self) -> ComposeResult:
|
|
yield Static(self.param.name, classes="param-label")
|
|
yield Static("---", id=f"val_{self.param.id:02x}", classes="param-value")
|
|
yield Input(placeholder="new", id=f"input_{self.param.id:02x}")
|
|
yield Button("Go", id=f"btn_{self.param.id:02x}", variant="primary")
|
|
|
|
def set_current_value(self, value: float):
|
|
fmt = self.param.fmt
|
|
label = self.query_one(f"#val_{self.param.id:02x}", Static)
|
|
label.update(f"{value:{fmt}}")
|
|
|
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
inp = self.query_one(Input)
|
|
try:
|
|
value = float(inp.value)
|
|
except ValueError:
|
|
return
|
|
frame = build_param_write(self.param.id, self.param.ptype, value)
|
|
self._send_callback(frame)
|
|
inp.value = ""
|
|
|
|
def on_input_submitted(self, event: Input.Submitted) -> None:
|
|
try:
|
|
value = float(event.value)
|
|
except ValueError:
|
|
return
|
|
frame = build_param_write(self.param.id, self.param.ptype, value)
|
|
self._send_callback(frame)
|
|
event.input.value = ""
|
|
|
|
|
|
class ParamGroup(Vertical):
|
|
"""A group of parameter rows for one controller."""
|
|
|
|
DEFAULT_CSS = """
|
|
ParamGroup {
|
|
width: 100%;
|
|
height: auto;
|
|
padding: 0 1;
|
|
margin: 0 0 1 0;
|
|
}
|
|
"""
|
|
|
|
def __init__(self, group_name: str, send_callback):
|
|
super().__init__()
|
|
self.group_name = group_name
|
|
self._send_callback = send_callback
|
|
self._params = [p for p in PARAMS if p.group == group_name]
|
|
|
|
def compose(self) -> ComposeResult:
|
|
yield Static(f"[bold]{self.group_name} CONTROLLER[/bold]")
|
|
for p in self._params:
|
|
yield ParamRow(p, self._send_callback)
|
|
|
|
def update_param(self, param_id: int, value: float):
|
|
for row in self.query(ParamRow):
|
|
if row.param.id == param_id:
|
|
row.set_current_value(value)
|
|
return
|