163 lines
4.4 KiB
Go
163 lines
4.4 KiB
Go
package core
|
|
|
|
import (
|
|
"github.com/evcc-io/evcc/api"
|
|
"github.com/evcc-io/evcc/core/keys"
|
|
)
|
|
|
|
// setPhasesConfigured sets the default phase configuration
|
|
func (lp *Loadpoint) setPhasesConfigured(phases int) {
|
|
lp.phasesConfigured = phases
|
|
lp.publish(keys.PhasesConfigured, lp.phasesConfigured)
|
|
lp.settings.SetInt(keys.PhasesConfigured, int64(lp.phasesConfigured))
|
|
|
|
// configured phases are actual phases for non-1p3p charger
|
|
// for 1p3p charger, configuration does not mean that the physical state has changed, so don't touch it
|
|
if !lp.hasPhaseSwitching() {
|
|
lp.setPhases(phases)
|
|
}
|
|
}
|
|
|
|
// SetPhases sets the number of enabled phases without modifying the charger
|
|
func (lp *Loadpoint) SetPhases(phases int) {
|
|
lp.Lock()
|
|
defer lp.Unlock()
|
|
lp.setPhases(phases)
|
|
}
|
|
|
|
// setPhases sets the number of enabled phases without modifying the charger
|
|
func (lp *Loadpoint) setPhases(phases int) {
|
|
if lp.phases != phases {
|
|
lp.phases = phases
|
|
|
|
// reset timer to disabled state
|
|
lp.resetPhaseTimer()
|
|
|
|
// measure phases after switching
|
|
lp.resetMeasuredPhases()
|
|
}
|
|
}
|
|
|
|
// ResetMeasuredPhases resets measured phases to unknown on vehicle disconnect, phase switch or phase api call
|
|
func (lp *Loadpoint) ResetMeasuredPhases() {
|
|
lp.Lock()
|
|
defer lp.Unlock()
|
|
lp.resetMeasuredPhases()
|
|
}
|
|
|
|
// resetMeasuredPhases resets measured phases to unknown on vehicle disconnect, phase switch or phase api call
|
|
func (lp *Loadpoint) resetMeasuredPhases() {
|
|
lp.measuredPhases = 0
|
|
lp.publish(keys.PhasesActive, lp.activePhases())
|
|
}
|
|
|
|
// GetMeasuredPhases provides synchronized access to measuredPhases
|
|
func (lp *Loadpoint) GetMeasuredPhases() int {
|
|
lp.RLock()
|
|
defer lp.RUnlock()
|
|
return lp.getMeasuredPhases()
|
|
}
|
|
|
|
// getMeasuredPhases provides synchronized access to measuredPhases
|
|
func (lp *Loadpoint) getMeasuredPhases() int {
|
|
return lp.measuredPhases
|
|
}
|
|
|
|
// assume 3p for switchable charger during startup
|
|
const unknownPhases = 3
|
|
|
|
func expect(phases int) int {
|
|
if phases > 0 {
|
|
return phases
|
|
}
|
|
return unknownPhases
|
|
}
|
|
|
|
// ActivePhases returns the number of expectedly active phases for the meter.
|
|
// If unknown for 1p3p chargers during startup it will assume 3p.
|
|
func (lp *Loadpoint) ActivePhases() int {
|
|
lp.Lock()
|
|
defer lp.Unlock()
|
|
return lp.activePhases()
|
|
}
|
|
|
|
// activePhases returns the number of expectedly active phases for the meter.
|
|
// If unknown for 1p3p chargers during startup it will assume 3p.
|
|
func (lp *Loadpoint) activePhases() int {
|
|
physical := lp.phases
|
|
vehicle := lp.getVehiclePhases()
|
|
measured := lp.getMeasuredPhases()
|
|
charger := lp.getChargerPhysicalPhases()
|
|
|
|
active := min(expect(vehicle), expect(physical), expect(measured), expect(charger))
|
|
|
|
// sanity check - we should not assume less active phases than actually measured
|
|
if measured > 0 && active < measured {
|
|
lp.log.WARN.Printf("phase mismatch between %dp measured for %dp vehicle and %dp charger", measured, vehicle, physical)
|
|
}
|
|
|
|
return active
|
|
}
|
|
|
|
// MinActivePhases returns the minimum number of active phases for the loadpoint.
|
|
func (lp *Loadpoint) MinActivePhases() int {
|
|
lp.RLock()
|
|
defer lp.RUnlock()
|
|
return lp.minActivePhases()
|
|
}
|
|
|
|
// minActivePhases returns the minimum number of active phases for the loadpoint.
|
|
func (lp *Loadpoint) minActivePhases() int {
|
|
if lp.hasPhaseSwitching() || lp.phasesConfigured == 1 {
|
|
return 1
|
|
}
|
|
|
|
return lp.maxActivePhases()
|
|
}
|
|
|
|
// MaxActivePhases returns the maximum number of active phases for the loadpoint.
|
|
func (lp *Loadpoint) MaxActivePhases() int {
|
|
lp.RLock()
|
|
defer lp.RUnlock()
|
|
return lp.maxActivePhases()
|
|
}
|
|
|
|
// maxActivePhases returns the maximum number of active phases for the loadpoint.
|
|
func (lp *Loadpoint) maxActivePhases() int {
|
|
physical := lp.phases
|
|
measured := lp.getMeasuredPhases()
|
|
vehicle := lp.getVehiclePhases()
|
|
charger := lp.getChargerPhysicalPhases()
|
|
|
|
// during 1p or unknown config, 1p measured is not a restriction
|
|
if physical <= 1 || vehicle == 1 || charger == 1 {
|
|
measured = 0
|
|
}
|
|
|
|
// if 1p3p supported then assume configured limit or 3p
|
|
if lp.hasPhaseSwitching() {
|
|
physical = lp.phasesConfigured
|
|
}
|
|
|
|
return min(expect(vehicle), expect(physical), expect(measured), expect(charger))
|
|
}
|
|
|
|
func (lp *Loadpoint) getVehiclePhases() int {
|
|
if v := lp.GetVehicle(); v != nil {
|
|
return v.Phases()
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func (lp *Loadpoint) getChargerPhysicalPhases() int {
|
|
if cc, ok := api.Cap[api.PhaseDescriber](lp.charger); ok {
|
|
return cc.Phases()
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func (lp *Loadpoint) hasPhaseSwitching() bool {
|
|
return api.HasCap[api.PhaseSwitcher](lp.charger)
|
|
}
|