Mack charger settings permanent

This commit is contained in:
2026-07-15 10:46:50 +02:00
parent 48c7587550
commit 0f20ec0758
+101 -3
View File
@@ -52,12 +52,82 @@ func NewChargerController(host string, pool *pgxpool.Pool) *ChargerController {
params: ChargingParams{Mode: ModeOff},
status: "initializing",
}
// Always restore RFID-gated idle state on startup. nmo/acs settings persist
// on the charger hardware across app restarts, so we must reset them explicitly.
go c.initSafeState()
c.initParamsSchema(context.Background())
go c.startup()
return c
}
// startup runs once after construction. If an active charging mode was
// persisted (e.g. solar_battery at 90% SOC), it restores the goroutine
// without touching the charger hardware — charging simply continues.
// Only when mode is off do we reset the charger to RFID-gated safe state,
// since that hardware state (acs=0, nmo=true, frc=2) persists across reboots.
func (c *ChargerController) startup() {
ctx := context.Background()
p, err := c.loadParams(ctx)
if err != nil {
log.Printf("charger ctrl: load params on startup: %v", err)
p = ChargingParams{Mode: ModeOff}
}
if p.Mode == ModeOff {
c.initSafeState()
return
}
log.Printf("charger ctrl: restoring mode=%s (max=%dA, minSOC=%d%%) after restart", p.Mode, p.MaxAmp, p.MinBatterySoc)
if err := c.SetParams(p); err != nil {
log.Printf("charger ctrl: restore params: %v", err)
c.initSafeState()
}
}
func (c *ChargerController) initParamsSchema(ctx context.Context) {
_, err := c.pool.Exec(ctx, `
CREATE TABLE IF NOT EXISTS charger_params (
id INT PRIMARY KEY DEFAULT 1,
mode TEXT NOT NULL DEFAULT 'off',
max_amp INT NOT NULL DEFAULT 16,
min_battery_soc INT NOT NULL DEFAULT 0,
hysteresis INT NOT NULL DEFAULT 0,
target_kwh DOUBLE PRECISION NOT NULL DEFAULT 0,
target_soc INT NOT NULL DEFAULT 0,
phases INT NOT NULL DEFAULT 0
);
INSERT INTO charger_params (id, mode) VALUES (1, 'off') ON CONFLICT DO NOTHING;
`)
if err != nil {
log.Printf("charger ctrl: schema init: %v", err)
}
}
func (c *ChargerController) saveParams(ctx context.Context, p ChargingParams) error {
_, err := c.pool.Exec(ctx, `
INSERT INTO charger_params
(id, mode, max_amp, min_battery_soc, hysteresis, target_kwh, target_soc, phases)
VALUES (1, $1, $2, $3, $4, $5, $6, $7)
ON CONFLICT (id) DO UPDATE SET
mode = EXCLUDED.mode,
max_amp = EXCLUDED.max_amp,
min_battery_soc = EXCLUDED.min_battery_soc,
hysteresis = EXCLUDED.hysteresis,
target_kwh = EXCLUDED.target_kwh,
target_soc = EXCLUDED.target_soc,
phases = EXCLUDED.phases
`, string(p.Mode), p.MaxAmp, p.MinBatterySoc, p.Hysteresis, p.TargetKwh, p.TargetSoc, p.Phases)
return err
}
func (c *ChargerController) loadParams(ctx context.Context) (ChargingParams, error) {
var p ChargingParams
var mode string
err := c.pool.QueryRow(ctx, `
SELECT mode, max_amp, min_battery_soc, hysteresis, target_kwh, target_soc, phases
FROM charger_params WHERE id = 1
`).Scan(&mode, &p.MaxAmp, &p.MinBatterySoc, &p.Hysteresis, &p.TargetKwh, &p.TargetSoc, &p.Phases)
p.Mode = ChargingMode(mode)
return p, err
}
func (c *ChargerController) initSafeState() {
if err := setChargerFrc(c.host, 1); err != nil {
log.Printf("charger ctrl: init: %v", err)
@@ -85,6 +155,10 @@ func (c *ChargerController) SetParams(p ChargingParams) error {
p.Phases = 3
}
if err := c.saveParams(context.Background(), p); err != nil {
log.Printf("charger ctrl: save params: %v", err)
}
c.mu.Lock()
if c.cancel != nil {
c.cancel()
@@ -286,6 +360,24 @@ func (c *ChargerController) adjust(ctx context.Context) {
return
}
// car=3 with alw=true: the CP line is stuck in IEC state B even though
// authorization was already granted (alw=true, frc=2). Simply re-setting
// frc=2 every tick has no effect — a full charger reset is required to
// force a fresh IEC 61851 handshake.
if st.Car == 3 && st.Alw {
c.mu.RLock()
paused := c.battPaused
c.mu.RUnlock()
if !paused {
c.setStatus("resetting — car stuck in IEC state B")
if err := c.enableCharging(params.MaxAmp); err != nil {
log.Printf("charger ctrl: car=3 stuck: %v", err)
c.setStatus("reset failed: " + err.Error())
}
}
return
}
if params.TargetKwh > 0 || params.TargetSoc > 0 {
if params.TargetSoc > 0 && st.Soc > 0 && st.Soc >= params.TargetSoc {
c.stopForTarget(fmt.Sprintf("car SOC %d%% reached", params.TargetSoc))
@@ -445,6 +537,9 @@ func (c *ChargerController) stopOnDisconnect() {
c.cancel = nil
}
c.mu.Unlock()
if err := c.saveParams(context.Background(), ChargingParams{Mode: ModeOff}); err != nil {
log.Printf("charger ctrl: save disconnect params: %v", err)
}
c.setStatus("off — car disconnected")
}
@@ -468,6 +563,9 @@ func (c *ChargerController) stopForTarget(reason string) {
c.cancel = nil
}
c.mu.Unlock()
if err := c.saveParams(context.Background(), ChargingParams{Mode: ModeOff}); err != nil {
log.Printf("charger ctrl: save target-stop params: %v", err)
}
c.setStatus(reason)
}