Skip to content

Latest commit

Β 

History

History
147 lines (118 loc) Β· 5.56 KB

File metadata and controls

147 lines (118 loc) Β· 5.56 KB

Core Domain: Site, Loadpoint, and the Control Loop

Object Hierarchy

Site (orchestrator β€” core/site.go)
β”œβ”€β”€ Meters: Grid, PV[], Battery[], Auxiliary[], External[]
β”œβ”€β”€ Tariffs: Grid, FeedIn, CO2, Solar
β”œβ”€β”€ Coordinator (vehicle <-> loadpoint assignment)
β”œβ”€β”€ Prioritizer (power allocation fairness)
└── Loadpoints[] (core/loadpoint.go)
    β”œβ”€β”€ Charger      (api.Charger β€” hardware controller)
    β”œβ”€β”€ Vehicle      (api.Vehicle β€” EV battery state via cloud API)
    β”œβ”€β”€ ChargeMeter  (api.Meter β€” AC power at charger)
    └── Circuit      (optional β€” electrical domain limits)

Key Interfaces (api/api.go)

Meter

  • Meter β€” CurrentPower() (float64, error) β€” watts
  • MeterEnergy β€” TotalEnergy() (float64, error) β€” kWh
  • PhaseCurrents / PhaseVoltages / PhasePowers β€” per-phase readings

Battery

  • Battery β€” Soc() (float64, error) β€” 0-100%
  • BatteryCapacity β€” kWh
  • BatteryController β€” set charge/discharge/hold mode

Charger

  • Charger β€” Status(), Enabled(), Enable(bool), MaxCurrent(int64)
  • ChargerEx β€” milliamp-precision current via MaxCurrentMillis(float64)
  • PhaseSwitcher β€” Phases1p3p(int) error
  • ChargeRater β€” ChargedEnergy() (float64, error)
  • ChargeTimer β€” ChargeDuration() (time.Duration, error)

Vehicle

  • Vehicle β€” Soc(), Capacity(), Identifiers(), Phases(), OnIdentified()
  • VehicleRange, VehicleOdometer, VehicleClimater, VehicleFinishTimer, VehiclePosition
  • ChargeController β€” remote start/stop on vehicle
  • CurrentLimiter β€” GetMinMaxCurrent() for vehicle-side current limits
  • CurrentController β€” some vehicles (Tesla, Fiat) also implement MaxCurrent() to set charge current from the vehicle side

Charge Modes

Mode Behavior
OFF Disabled (unless welcome charge)
NOW Max current immediately
MINPV Min current when PV surplus; fast if cheap tariff
PV Ramp current proportional to available solar

Charge States (IEC 61851)

  • A β€” not connected
  • B β€” connected, not charging
  • C β€” connected, charging

The Control Loop (Site.update β€” runs every N seconds)

1. Update all meters (grid, PV, battery, aux)
2. For each loadpoint: UpdateChargePowerAndCurrents()
3. Calculate site power balance:
   sitePower = gridPower + batteryPower + excessDCPower
             + residualPower - auxPower - flexiblePower
4. Apply battery priority rules (prioritySoc, bufferSoc)
5. Get tariff rates
6. For EACH loadpoint: Update(sitePower, ...)
   β”œβ”€β”€ Read charger status
   β”œβ”€β”€ Detect/identify vehicle
   β”œβ”€β”€ Check plan requirements (minSOC, target time)
   β”œβ”€β”€ Check limits (limitSOC, limitEnergy)
   β”œβ”€β”€ MODE switch -> calculate target current
   β”œβ”€β”€ Cap at maxCurrent, respect circuit limits
   β”œβ”€β”€ Send MaxCurrent() to charger
   └── Record metrics
7. Push updates to WebSocket + metrics

The loop is stateless per cycle: always re-reads actual state, calculates optimal current, sends single command. Resilient to restarts and missed updates.

PV Surplus Charging (pvMaxCurrent in core/loadpoint.go)

1. Read effective min/max current limits
2. Reduce sitePower by battery boost power
3. Consider phase switching (1p <-> 3p) if supported
4. deltaCurrent = powerToCurrent(-sitePower, activePhases)
   targetCurrent = effectiveCurrent + deltaCurrent
5. Below minCurrent -> start disable timer (default 3 min)
6. Surplus returns -> start enable timer (default 1 min)
7. Cap at maxCurrent

Battery Priority Rules

Setting Effect
prioritySoc Below this: battery charges first, EV gets 0
bufferSoc Above this: EV can draw from battery reserves
bufferStartSoc Above this: EV charging can begin even if importing

Effective Price Calculation

greenShare = (max(pvPower,0) + max(batteryPower,0)) / totalChargePower
effectivePrice = gridPrice * (1 - greenShare) + feedInPrice * greenShare

Concurrency Model

  • Site owns RWMutex for its state (meters, battery, tariffs)
  • Loadpoint owns RWMutex for its state (charger, vehicle, current)
  • Coordinator owns RWMutex for vehicle <-> loadpoint tracking
  • No global locks β€” ordering prevents deadlocks

Channels

Channel Scope Buffer Purpose
valueChan Site Unbounded (chanx.NewUnboundedChan) State changes -> DB + UI (ordering)
lpUpdateChan Site 1 Early loadpoint update requests
pushChan Loadpoint Buffered User notifications

Tariff Integration

Types: TariffUsageGrid, TariffUsageFeedIn, TariffUsageCo2, TariffUsagePlanner, TariffUsageSolar

Smart Features

  • Cheap-tariff override β€” rate below threshold -> fast charge
  • Smart feed-in β€” feed-in rate above threshold -> prioritize export
  • Planner (core/planner/planner.go) β€” finds cheapest time slots for target SOC/energy by deadline
    • optimalPlan() β€” cheapest non-contiguous slots
    • continuousPlan() β€” cheapest continuous window (fallback)

Key File Locations

  • api/api.go β€” all core interfaces
  • core/site.go β€” Site orchestrator + control loop
  • core/loadpoint.go β€” Loadpoint state machine (pvMaxCurrent, mode switch)
  • core/site_battery.go β€” battery priority logic
  • core/site_tariffs.go β€” tariff integration
  • core/planner/planner.go β€” charge time optimization
  • core/prioritizer/prioritizer.go β€” power allocation across loadpoints
  • core/circuit/circuit.go β€” electrical domain limits