Mack charger settings permanent
This commit is contained in:
@@ -52,12 +52,82 @@ func NewChargerController(host string, pool *pgxpool.Pool) *ChargerController {
|
|||||||
params: ChargingParams{Mode: ModeOff},
|
params: ChargingParams{Mode: ModeOff},
|
||||||
status: "initializing",
|
status: "initializing",
|
||||||
}
|
}
|
||||||
// Always restore RFID-gated idle state on startup. nmo/acs settings persist
|
c.initParamsSchema(context.Background())
|
||||||
// on the charger hardware across app restarts, so we must reset them explicitly.
|
go c.startup()
|
||||||
go c.initSafeState()
|
|
||||||
return c
|
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() {
|
func (c *ChargerController) initSafeState() {
|
||||||
if err := setChargerFrc(c.host, 1); err != nil {
|
if err := setChargerFrc(c.host, 1); err != nil {
|
||||||
log.Printf("charger ctrl: init: %v", err)
|
log.Printf("charger ctrl: init: %v", err)
|
||||||
@@ -85,6 +155,10 @@ func (c *ChargerController) SetParams(p ChargingParams) error {
|
|||||||
p.Phases = 3
|
p.Phases = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.saveParams(context.Background(), p); err != nil {
|
||||||
|
log.Printf("charger ctrl: save params: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
if c.cancel != nil {
|
if c.cancel != nil {
|
||||||
c.cancel()
|
c.cancel()
|
||||||
@@ -286,6 +360,24 @@ func (c *ChargerController) adjust(ctx context.Context) {
|
|||||||
return
|
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.TargetKwh > 0 || params.TargetSoc > 0 {
|
||||||
if params.TargetSoc > 0 && st.Soc > 0 && st.Soc >= params.TargetSoc {
|
if params.TargetSoc > 0 && st.Soc > 0 && st.Soc >= params.TargetSoc {
|
||||||
c.stopForTarget(fmt.Sprintf("car SOC %d%% reached", params.TargetSoc))
|
c.stopForTarget(fmt.Sprintf("car SOC %d%% reached", params.TargetSoc))
|
||||||
@@ -445,6 +537,9 @@ func (c *ChargerController) stopOnDisconnect() {
|
|||||||
c.cancel = nil
|
c.cancel = nil
|
||||||
}
|
}
|
||||||
c.mu.Unlock()
|
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")
|
c.setStatus("off — car disconnected")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -468,6 +563,9 @@ func (c *ChargerController) stopForTarget(reason string) {
|
|||||||
c.cancel = nil
|
c.cancel = nil
|
||||||
}
|
}
|
||||||
c.mu.Unlock()
|
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)
|
c.setStatus(reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user