Refactor charger handler part 2 (#482)

This commit is contained in:
andig 2020-11-26 16:51:38 +01:00 • committed by GitHub
parent 4f06e6948d
commit 10b206a912
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 245 additions and 880 deletions

View file

@ -1,171 +0,0 @@
package core
import (
"fmt"
"time"
"github.com/andig/evcc/api"
"github.com/andig/evcc/util"
evbus "github.com/asaskevich/EventBus"
"github.com/benbjohnson/clock"
)
//go:generate mockgen -package mock -destination ../mock/mock_chargerhandler.go github.com/andig/evcc/core Handler
// Handler is the charger handler responsible for enabled state, target current and guard durations
type Handler interface {
Prepare()
Sync()
Enabled() bool
Status() (api.ChargeStatus, error)
TargetCurrent() int64
Ramp(int64, bool) error
}
// HandlerConfig contains the public configuration for the ChargerHandler
type HandlerConfig struct {
MinCurrent int64 // PV mode: start current Min+PV mode: min current
MaxCurrent int64 // Max allowed current. Physically ensured by the charge controller
GuardDuration time.Duration // charger enable/disable minimum holding time
}
// ChargerHandler handles steering of the charger state and allowed current
type ChargerHandler struct {
clock clock.Clock // mockable time
bus evbus.Bus // event bus
log *util.Logger
charger api.Charger // Charger
HandlerConfig // public configuration
enabled bool // Charger enabled state
targetCurrent int64 // Charger target current
// contactor switch guard
guardUpdated time.Time // charger enabled/disabled timestamp
}
// Status returns charger status
func (lp *ChargerHandler) Status() (api.ChargeStatus, error) {
return lp.charger.Status()
}
// Enabled returns handler enabled state
func (lp *ChargerHandler) Enabled() bool {
return lp.enabled
}
// TargetCurrent returns handler target current
func (lp *ChargerHandler) TargetCurrent() int64 {
return lp.targetCurrent
}
// Prepare synchronizes initial charger enabled state and current
func (lp *ChargerHandler) Prepare() {
// read initial enabled state
enabled, err := lp.charger.Enabled()
if err == nil {
lp.enabled = enabled
lp.log.INFO.Printf("charger %sd", status[lp.enabled])
// prevent immediately disabling charger
if lp.enabled {
lp.guardUpdated = lp.clock.Now()
}
} else {
lp.log.ERROR.Printf("charger error: %v", err)
}
// set current to known value
if err = lp.setTargetCurrent(lp.MinCurrent); err != nil {
lp.log.ERROR.Println(err)
}
lp.bus.Publish(evChargeCurrent, lp.MinCurrent)
}
// Sync synchronizes charger settings to expected state
func (lp *ChargerHandler) Sync() {
enabled, err := lp.charger.Enabled()
if err == nil && enabled != lp.enabled {
lp.log.WARN.Printf("sync enabled state to %s", status[lp.enabled])
err = lp.charger.Enable(lp.enabled)
}
if err != nil {
lp.log.ERROR.Printf("charge controller error: %v", err)
}
}
// chargerEnable switches charging on or off. Minimum cycle duration is guaranteed.
func (lp *ChargerHandler) chargerEnable(enable bool) error {
if remaining := (lp.GuardDuration - lp.clock.Since(lp.guardUpdated)).Truncate(time.Second); remaining > 0 {
lp.log.DEBUG.Printf("charger %s - contactor delay %v", status[enable], remaining)
return nil
}
if lp.enabled != enable {
if err := lp.charger.Enable(enable); err != nil {
return fmt.Errorf("charge controller error: %v", err)
}
lp.enabled = enable // cache
lp.log.INFO.Printf("charger %s", status[enable])
lp.guardUpdated = lp.clock.Now()
} else {
lp.log.DEBUG.Printf("charger %s", status[enable])
}
// if not enabled, current will be reduced to 0 in handler
lp.bus.Publish(evChargeCurrent, lp.MinCurrent)
return nil
}
// setTargetCurrent guards setting current against changing to identical value
// and violating MaxCurrent
func (lp *ChargerHandler) setTargetCurrent(targetCurrent int64) error {
target := clamp(targetCurrent, lp.MinCurrent, lp.MaxCurrent)
if lp.targetCurrent != target {
lp.log.DEBUG.Printf("set charge current: %dA", target)
if err := lp.charger.MaxCurrent(target); err != nil {
return fmt.Errorf("charge controller error: %v", err)
}
lp.targetCurrent = target // cache
}
// if not enabled, current will be reduced to 0 in handler
lp.bus.Publish(evChargeCurrent, target)
return nil
}
// Ramp performs ramping charger current up and down where targetCurrent=0
// signals disabled state
func (lp *ChargerHandler) Ramp(targetCurrent int64, force bool) error {
// reset guard updated
if force {
lp.guardUpdated = time.Time{}
}
// if targetCurrent == 0 disable
if targetCurrent == 0 {
err := lp.chargerEnable(false)
if err == nil && lp.enabled && lp.targetCurrent > lp.MinCurrent {
err = lp.setTargetCurrent(lp.MinCurrent)
}
return err
}
// else set targetCurrent and optionally enable
err := lp.setTargetCurrent(targetCurrent)
if err == nil && !lp.enabled {
err = lp.chargerEnable(true)
}
return err
}

View file

@ -1,347 +0,0 @@
package core
import (
"testing"
"time"
"github.com/andig/evcc/api"
"github.com/andig/evcc/mock"
"github.com/andig/evcc/util"
evbus "github.com/asaskevich/EventBus"
"github.com/benbjohnson/clock"
"github.com/golang/mock/gomock"
)
const (
minA int64 = 6
maxA int64 = 16
guardDuration = 5 * time.Minute
dt = time.Hour
)
func newChargerHandler(clock clock.Clock, mc api.Charger) *ChargerHandler {
h := &ChargerHandler{
log: util.NewLogger("foo"),
clock: clock,
bus: evbus.New(),
charger: mc,
HandlerConfig: HandlerConfig{
MinCurrent: minA,
MaxCurrent: maxA,
GuardDuration: guardDuration,
},
}
// prepare charger and set guardUpdated
mc.(*mock.MockCharger).EXPECT().Enabled().Return(true, nil)
mc.(*mock.MockCharger).EXPECT().MaxCurrent(int64(6)).Return(nil)
h.Prepare()
return h
}
// test here to ensure loadpoint defaults are valid
func TestNewChargerHandler(t *testing.T) {
// LoadPoint contains ChargerHandler configuration
r := NewLoadPoint(util.NewLogger("foo"))
if r.MinCurrent != minA {
t.Errorf("expected %v, got %v", minA, r.MinCurrent)
}
if r.MaxCurrent != maxA {
t.Errorf("expected %v, got %v", maxA, r.MaxCurrent)
}
if r.GuardDuration != guardDuration {
t.Errorf("expected %v, got %v", guardDuration, r.GuardDuration)
}
}
func TestEnable(t *testing.T) {
tc := []struct {
enabled bool
dt time.Duration
enable bool
targetCurrent int64
expect func(*mock.MockCharger)
}{
// any test with current != 0 or min will fail
{false, 0, false, 0, func(mc *mock.MockCharger) {
// nop
}},
{false, 0, true, 0, func(mc *mock.MockCharger) {
// nop
}},
{false, dt, true, 0, func(mc *mock.MockCharger) {
mc.EXPECT().Enable(true).Return(nil)
}},
{false, 0, true, minA, func(mc *mock.MockCharger) {
// nop
}},
{false, dt, true, minA, func(mc *mock.MockCharger) {
mc.EXPECT().Enable(true).Return(nil)
}},
{true, 0, false, minA, func(mc *mock.MockCharger) {
// nop
}},
{true, dt, false, minA, func(mc *mock.MockCharger) {
mc.EXPECT().Enable(false).Return(nil)
}},
{true, 0, true, minA, func(mc *mock.MockCharger) {
// nop
}},
{true, dt, true, minA, func(mc *mock.MockCharger) {
// nop
}},
}
for _, tc := range tc {
ctrl := gomock.NewController(t)
mc := mock.NewMockCharger(ctrl)
t.Log(tc)
clock := clock.NewMock()
r := newChargerHandler(clock, mc)
r.enabled = tc.enabled
r.targetCurrent = tc.targetCurrent
tc.expect(mc)
clock.Add(tc.dt)
if err := r.chargerEnable(tc.enable); err != nil {
t.Error(err)
}
ctrl.Finish()
}
}
func TestSetCurrent(t *testing.T) {
tc := []struct {
targetCurrentI, targetCurrent, targetCurrentO int64
expect func(*mock.MockCharger)
}{
{0, 0, minA, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(minA).Return(nil)
}},
{minA, minA, minA, func(mc *mock.MockCharger) {
// we are at min: current call omitted
}},
{minA, 0, minA, func(mc *mock.MockCharger) {
// we are at min: current call omitted
}},
{minA, maxA, maxA, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(maxA).Return(nil)
}},
{maxA, maxA, maxA, func(mc *mock.MockCharger) {
// we are at min: current call omitted
}},
{minA, 2 * maxA, maxA, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(maxA).Return(nil)
}},
}
for _, tc := range tc {
ctrl := gomock.NewController(t)
mc := mock.NewMockCharger(ctrl)
t.Log(tc)
clock := clock.NewMock()
r := newChargerHandler(clock, mc)
r.targetCurrent = tc.targetCurrentI
tc.expect(mc)
if err := r.setTargetCurrent(tc.targetCurrent); err != nil {
t.Error(err)
}
if r.targetCurrent != tc.targetCurrentO {
t.Errorf("targetCurrent: expected %d, got %d", tc.targetCurrentO, r.targetCurrent)
}
ctrl.Finish()
}
}
func TestRampOn(t *testing.T) {
tc := []struct {
enabledI bool
targetCurrentI, targetCurrent int64
dt time.Duration
expect func(*mock.MockCharger)
}{
// off at zero: set min
{false, 0, minA, 0, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(minA).Return(nil)
// guard duration
}},
{false, 0, minA, dt, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(minA).Return(nil)
mc.EXPECT().Enable(true).Return(nil)
}},
// off at max: set min
{false, maxA, minA, 0, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(minA).Return(nil)
// guard duration
}},
{false, maxA, minA, dt, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(minA).Return(nil)
mc.EXPECT().Enable(true).Return(nil)
}},
// off at min: set on
{false, minA, minA, 0, func(mc *mock.MockCharger) {
// we are at min: current call omitted
// guard duration
}},
{false, minA, minA, dt, func(mc *mock.MockCharger) {
// we are at min: current call omitted
mc.EXPECT().Enable(true).Return(nil)
}},
// on at min, set min: set min
{true, minA, minA, 0, func(mc *mock.MockCharger) {
// we are at min: current call omitted
// we are enabled: enable call omitted
}},
// on at max, set min: set min
{true, maxA, minA, 0, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(minA).Return(nil)
// we are enabled: enable call omitted
}},
}
for _, tc := range tc {
ctrl := gomock.NewController(t)
mc := mock.NewMockCharger(ctrl)
t.Log(tc)
clock := clock.NewMock()
r := newChargerHandler(clock, mc)
r.enabled = tc.enabledI
r.targetCurrent = tc.targetCurrentI
tc.expect(mc)
clock.Add(tc.dt)
if err := r.Ramp(tc.targetCurrent, false); err != nil {
t.Error(err)
}
ctrl.Finish()
}
}
func TestRampOff(t *testing.T) {
tc := []struct {
enabledI bool
targetCurrentI int64
dt time.Duration
expect func(*mock.MockCharger)
}{
// off at zero
{false, 0, 0, func(mc *mock.MockCharger) {
// we are off: enable call omitted
}},
// off at min
{false, minA, 0, func(mc *mock.MockCharger) {
// we are off: enable call omitted
}},
// off at max
{false, maxA, 0, func(mc *mock.MockCharger) {
// we are off: enable call omitted
}},
// on at min, disable
{true, minA, 0, func(mc *mock.MockCharger) {
// guard duration
}},
// on at min, set disable
{true, minA, dt, func(mc *mock.MockCharger) {
// we are at min: current call omitted
mc.EXPECT().Enable(false).Return(nil)
}},
// on at max, set disable
{true, maxA, 0, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(minA).Return(nil)
// guard duration
}},
// on at max, set disable
{true, maxA, dt, func(mc *mock.MockCharger) {
mc.EXPECT().Enable(false).Return(nil)
}},
}
for _, tc := range tc {
ctrl := gomock.NewController(t)
mc := mock.NewMockCharger(ctrl)
t.Log(tc)
clock := clock.NewMock()
r := newChargerHandler(clock, mc)
r.enabled = tc.enabledI
r.targetCurrent = tc.targetCurrentI
tc.expect(mc)
clock.Add(tc.dt)
if err := r.Ramp(0, false); err != nil {
t.Error(err)
}
ctrl.Finish()
}
}
func TestRampUpDown(t *testing.T) {
tc := []struct {
targetCurrentI, targetCurrent int64
expect func(*mock.MockCharger)
}{
// no change at 0: nop
{0, 0, func(mc *mock.MockCharger) {
// nop
}},
// no change at min: nop
{minA, minA, func(mc *mock.MockCharger) {
// nop
}},
// at min: set <min
{minA, minA - 100, func(mc *mock.MockCharger) {
// nop
}},
// at min: set max
{minA, maxA, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(maxA).Return(nil)
}},
// at max: set min
{maxA, minA, func(mc *mock.MockCharger) {
mc.EXPECT().MaxCurrent(minA).Return(nil)
}},
// at max: set >max
{maxA, maxA + 100, func(mc *mock.MockCharger) {
// nop
}},
}
for _, tc := range tc {
ctrl := gomock.NewController(t)
mc := mock.NewMockCharger(ctrl)
t.Log(tc)
clock := clock.NewMock()
h := newChargerHandler(clock, mc)
h.enabled = true
h.targetCurrent = tc.targetCurrentI
tc.expect(mc)
if err := h.Ramp(tc.targetCurrent, false); err != nil {
t.Error(err)
}
ctrl.Finish()
}
}

View file

@ -73,9 +73,15 @@ type LoadPoint struct {
}
Enable, Disable ThresholdConfig
handler Handler
HandlerConfig `mapstructure:",squash"` // handle charger state and current
MinCurrent int64 // PV mode: start current Min+PV mode: min current
MaxCurrent int64 // Max allowed current. Physically ensured by the charger
GuardDuration time.Duration // charger enable/disable minimum holding time
enabled bool // Charger enabled state
maxCurrent int64 // Charger current limit
guardUpdated time.Time // Charger enabled/disabled timestamp
charger api.Charger
chargeTimer api.ChargeTimer
chargeRater api.ChargeRater
@ -139,21 +145,13 @@ func NewLoadPointFromConfig(log *util.Logger, cp configProvider, other map[strin
if lp.ChargerRef == "" {
return nil, errors.New("missing charger")
}
charger := cp.Charger(lp.ChargerRef)
lp.configureChargerType(charger)
lp.charger = cp.Charger(lp.ChargerRef)
lp.configureChargerType(lp.charger)
if lp.Enable.Threshold > lp.Disable.Threshold {
log.WARN.Printf("PV mode enable threshold (%.0fW) is larger than disable threshold (%.0fW)", lp.Enable.Threshold, lp.Disable.Threshold)
}
lp.handler = &ChargerHandler{
log: lp.log,
clock: lp.clock,
bus: lp.bus,
charger: charger,
HandlerConfig: lp.HandlerConfig,
}
return lp, nil
}
@ -163,17 +161,15 @@ func NewLoadPoint(log *util.Logger) *LoadPoint {
bus := evbus.New()
lp := &LoadPoint{
log: log, // logger
clock: clock, // mockable time
bus: bus, // event bus
Mode: api.ModeOff,
Phases: 1,
status: api.StatusNone,
HandlerConfig: HandlerConfig{
MinCurrent: 6, // A
MaxCurrent: 16, // A
GuardDuration: 5 * time.Minute,
},
log: log, // logger
clock: clock, // mockable time
bus: bus, // event bus
Mode: api.ModeOff,
Phases: 1,
status: api.StatusNone,
MinCurrent: 6, // A
MaxCurrent: 16, // A
GuardDuration: 5 * time.Minute,
}
return lp
@ -301,7 +297,7 @@ func (lp *LoadPoint) evChargeCurrentHandler(current int64) {
func (lp *LoadPoint) evChargeCurrentWrappedMeterHandler(current int64) {
power := float64(current*lp.Phases) * Voltage
if !lp.handler.Enabled() || lp.status != api.StatusC {
if !lp.enabled || lp.status != api.StatusC {
// if disabled we cannot be charging
power = 0
}
@ -357,8 +353,56 @@ func (lp *LoadPoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even
lp.setActiveVehicle(lp.vehicles[0])
}
// read initial charger state to prevent immediately disabling charger
if enabled, err := lp.charger.Enabled(); err == nil {
if lp.enabled = enabled; enabled {
lp.guardUpdated = lp.clock.Now()
}
} else {
lp.log.ERROR.Printf("charger error: %v", err)
}
// prepare charger status
lp.handler.Prepare()
lp.setLimit(lp.MinCurrent, false)
}
func (lp *LoadPoint) syncCharger() {
enabled, err := lp.charger.Enabled()
if err == nil && enabled != lp.enabled {
lp.log.WARN.Println("charger out of sync")
err = lp.charger.Enable(lp.enabled)
}
if err != nil {
lp.log.ERROR.Printf("charger error: %v", err)
}
}
func (lp *LoadPoint) setLimit(maxCurrent int64, force bool) (err error) {
// set current
if maxCurrent != lp.maxCurrent && maxCurrent >= lp.MinCurrent {
if err = lp.charger.MaxCurrent(maxCurrent); err == nil {
lp.maxCurrent = maxCurrent
lp.bus.Publish(evChargeCurrent, maxCurrent)
}
}
// set enabled
if enabled := maxCurrent != 0; enabled != lp.enabled && err == nil {
if remaining := (lp.GuardDuration - lp.clock.Since(lp.guardUpdated)).Truncate(time.Second); remaining > 0 && !force {
lp.log.DEBUG.Printf("charger %s - contactor delay %v", status[enabled], remaining)
return nil
}
if err = lp.charger.Enable(enabled); err == nil {
lp.enabled = enabled
lp.guardUpdated = lp.clock.Now()
lp.log.DEBUG.Printf("charger %s", status[enabled])
lp.bus.Publish(evChargeCurrent, maxCurrent)
}
}
return err
}
// connected returns the EVs connection state
@ -475,7 +519,7 @@ func (lp *LoadPoint) findActiveVehicle() {
// updateChargerStatus updates charger status and detects car connected/disconnected events
func (lp *LoadPoint) updateChargerStatus() error {
status, err := lp.handler.Status()
status, err := lp.charger.Status()
if err != nil {
return err
}
@ -503,7 +547,7 @@ func (lp *LoadPoint) updateChargerStatus() error {
}
// update whenever there is a state change
lp.bus.Publish(evChargeCurrent, lp.handler.TargetCurrent())
lp.bus.Publish(evChargeCurrent, lp.maxCurrent)
}
return nil
@ -551,7 +595,7 @@ func (lp *LoadPoint) pvDisableTimer() {
// pvMaxCurrent calculates the maximum target current for PV mode
func (lp *LoadPoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64) int64 {
// calculate target charge current from delta power and actual current
effectiveCurrent := lp.handler.TargetCurrent()
effectiveCurrent := lp.maxCurrent
if lp.status != api.StatusC {
effectiveCurrent = 0
}
@ -577,9 +621,7 @@ func (lp *LoadPoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64) int64
}
// read only once to simplify testing
enabled := lp.handler.Enabled()
if mode == api.ModePV && enabled && targetCurrent < lp.MinCurrent {
if mode == api.ModePV && lp.enabled && targetCurrent < lp.MinCurrent {
// kick off disable sequence
if sitePower >= lp.Disable.Threshold {
lp.log.DEBUG.Printf("site power %.0fW >= disable threshold %.0fW", sitePower, lp.Disable.Threshold)
@ -604,7 +646,7 @@ func (lp *LoadPoint) pvMaxCurrent(mode api.ChargeMode, sitePower float64) int64
return lp.MinCurrent
}
if mode == api.ModePV && !enabled {
if mode == api.ModePV && !lp.enabled {
// kick off enable sequence
if targetCurrent >= lp.MinCurrent ||
(lp.Enable.Threshold != 0 && sitePower <= lp.Enable.Threshold) {
@ -717,7 +759,7 @@ func (lp *LoadPoint) Update(sitePower float64) {
lp.updateChargeMeter()
// update ChargeRater here to make sure initial meter update is caught
lp.bus.Publish(evChargeCurrent, lp.handler.TargetCurrent())
lp.bus.Publish(evChargeCurrent, lp.maxCurrent)
lp.bus.Publish(evChargePower, lp.chargePower)
// update progress and soc before status is updated
@ -725,7 +767,7 @@ func (lp *LoadPoint) Update(sitePower float64) {
// read and publish status
if err := lp.updateChargerStatus(); err != nil {
lp.log.ERROR.Printf("charge controller error: %v", err)
lp.log.ERROR.Printf("charger error: %v", err)
return
}
@ -739,9 +781,7 @@ func (lp *LoadPoint) Update(sitePower float64) {
lp.publishSoC()
// sync settings with charger
if lp.status != api.StatusA {
lp.handler.Sync()
}
lp.syncCharger()
// phase detection
lp.detectPhases()
@ -757,14 +797,14 @@ func (lp *LoadPoint) Update(sitePower float64) {
case !lp.connected():
// always disable charger if not connected
// https://github.com/andig/evcc/issues/105
err = lp.handler.Ramp(0, false)
err = lp.setLimit(0, false)
case lp.targetSocReached():
var targetCurrent int64 // zero disables
if lp.climateActive() {
targetCurrent = lp.MinCurrent
}
err = lp.handler.Ramp(targetCurrent, true)
err = lp.setLimit(targetCurrent, true)
// OCPP
case lp.remoteControlled(RemoteHardDisable):
@ -772,14 +812,14 @@ func (lp *LoadPoint) Update(sitePower float64) {
fallthrough
case mode == api.ModeOff:
err = lp.handler.Ramp(0, true)
err = lp.setLimit(0, true)
case lp.minSocNotReached():
err = lp.handler.Ramp(lp.MaxCurrent, true)
err = lp.setLimit(lp.MaxCurrent, true)
lp.pvDisableTimer() // let PV mode disable immediately afterwards
case mode == api.ModeNow:
err = lp.handler.Ramp(lp.MaxCurrent, true)
err = lp.setLimit(lp.MaxCurrent, true)
case mode == api.ModeMinPV || mode == api.ModePV:
targetCurrent := lp.pvMaxCurrent(mode, sitePower)
@ -798,7 +838,7 @@ func (lp *LoadPoint) Update(sitePower float64) {
required = true
}
err = lp.handler.Ramp(targetCurrent, required)
err = lp.setLimit(targetCurrent, required)
}
// effective disabled status

View file

@ -15,8 +15,8 @@ import (
)
const (
lpMinCurrent int64 = 6
lpMaxCurrent int64 = 16
minA int64 = 6
maxA int64 = 16
)
type Null struct{}
@ -34,6 +34,8 @@ func (n *Null) ChargingTime() (time.Duration, error) {
}
func attachListeners(t *testing.T, lp *LoadPoint) {
Voltage = 230 // V
uiChan := make(chan util.Param)
pushChan := make(chan push.Event)
lpChan := make(chan *LoadPoint)
@ -58,6 +60,11 @@ func attachListeners(t *testing.T, lp *LoadPoint) {
}
}()
if charger, ok := lp.charger.(*mock.MockCharger); ok && charger != nil {
charger.EXPECT().Enabled().Return(true, nil)
charger.EXPECT().MaxCurrent(lp.MinCurrent).Return(nil)
}
lp.Prepare(uiChan, pushChan, lpChan)
}
@ -67,10 +74,10 @@ func TestNew(t *testing.T) {
if lp.Phases != 1 {
t.Errorf("Phases %v", lp.Phases)
}
if lp.MinCurrent != lpMinCurrent {
if lp.MinCurrent != minA {
t.Errorf("MinCurrent %v", lp.MinCurrent)
}
if lp.MaxCurrent != lpMaxCurrent {
if lp.MaxCurrent != maxA {
t.Errorf("MaxCurrent %v", lp.MaxCurrent)
}
if lp.status != api.StatusNone {
@ -81,60 +88,51 @@ func TestNew(t *testing.T) {
}
}
func TestUpdate(t *testing.T) {
func TestUpdatePowerZero(t *testing.T) {
tc := []struct {
status api.ChargeStatus
mode api.ChargeMode
expect func(h *mock.MockHandler)
expect func(h *mock.MockCharger)
}{
{api.StatusA, api.ModeOff, func(h *mock.MockHandler) {
h.EXPECT().Ramp(int64(0), false)
{api.StatusA, api.ModeOff, func(h *mock.MockCharger) {
h.EXPECT().Enable(false)
}},
{api.StatusA, api.ModeNow, func(h *mock.MockHandler) {
h.EXPECT().Ramp(int64(0), false)
{api.StatusA, api.ModeNow, func(h *mock.MockCharger) {
h.EXPECT().Enable(false)
}},
{api.StatusA, api.ModeMinPV, func(h *mock.MockHandler) {
h.EXPECT().Ramp(int64(0), false)
{api.StatusA, api.ModeMinPV, func(h *mock.MockCharger) {
h.EXPECT().Enable(false)
}},
{api.StatusA, api.ModePV, func(h *mock.MockHandler) {
h.EXPECT().Ramp(int64(0), false) // zero since update called with 0
// h.EXPECT().Enabled().Return(false) // short-circuited due to status != C
{api.StatusA, api.ModePV, func(h *mock.MockCharger) {
h.EXPECT().Enable(false) // zero since update called with 0
}},
{api.StatusB, api.ModeOff, func(h *mock.MockHandler) {
h.EXPECT().Ramp(int64(0), true)
{api.StatusB, api.ModeOff, func(h *mock.MockCharger) {
h.EXPECT().Enable(false)
}},
{api.StatusB, api.ModeNow, func(h *mock.MockHandler) {
h.EXPECT().Ramp(lpMaxCurrent, true)
{api.StatusB, api.ModeNow, func(h *mock.MockCharger) {
h.EXPECT().MaxCurrent(maxA) // true
}},
{api.StatusB, api.ModeMinPV, func(h *mock.MockHandler) {
// min since update called with 0
// force = false due to pv mode climater check
h.EXPECT().Ramp(lpMinCurrent, false)
{api.StatusB, api.ModeMinPV, func(h *mock.MockCharger) {
// MaxCurrent omitted since identical value
}},
{api.StatusB, api.ModePV, func(h *mock.MockHandler) {
{api.StatusB, api.ModePV, func(h *mock.MockCharger) {
// zero since update called with 0
// force = false due to pv mode climater check
h.EXPECT().Ramp(int64(0), false)
// h.EXPECT().Enabled().Return(false) // short-circuited due to status != C
h.EXPECT().Enable(false)
}},
{api.StatusC, api.ModeOff, func(h *mock.MockHandler) {
h.EXPECT().Ramp(int64(0), true)
{api.StatusC, api.ModeOff, func(h *mock.MockCharger) {
h.EXPECT().Enable(false)
}},
{api.StatusC, api.ModeNow, func(h *mock.MockHandler) {
h.EXPECT().Ramp(lpMaxCurrent, true)
{api.StatusC, api.ModeNow, func(h *mock.MockCharger) {
h.EXPECT().MaxCurrent(maxA) // true
}},
{api.StatusC, api.ModeMinPV, func(h *mock.MockHandler) {
// min since update called with 0
// force = false due to pv mode climater check
h.EXPECT().Ramp(lpMinCurrent, false)
{api.StatusC, api.ModeMinPV, func(h *mock.MockCharger) {
// MaxCurrent omitted since identical value
}},
{api.StatusC, api.ModePV, func(h *mock.MockHandler) {
// zero since update called with 0
// force = false due to pv mode climater check
h.EXPECT().Ramp(int64(0), false)
h.EXPECT().Enabled().Return(false)
{api.StatusC, api.ModePV, func(h *mock.MockCharger) {
// omitted since PV balanced
}},
}
@ -143,43 +141,34 @@ func TestUpdate(t *testing.T) {
clck := clock.NewMock()
ctrl := gomock.NewController(t)
handler := mock.NewMockHandler(ctrl)
charger := mock.NewMockCharger(ctrl)
lp := &LoadPoint{
log: util.NewLogger("foo"),
bus: evbus.New(),
clock: clck,
charger: charger,
chargeMeter: &Null{}, // silence nil panics
chargeRater: &Null{}, // silence nil panics
chargeTimer: &Null{}, // silence nil panics
HandlerConfig: HandlerConfig{
MinCurrent: lpMinCurrent,
MaxCurrent: lpMaxCurrent,
},
handler: handler,
status: tc.status, // no status change
MinCurrent: minA,
MaxCurrent: maxA,
Phases: 1,
status: tc.status, // no status change
}
handler.EXPECT().Prepare().Return()
attachListeners(t, lp)
handler.EXPECT().Status().Return(tc.status, nil)
handler.EXPECT().TargetCurrent().Return(int64(0))
if tc.status != api.StatusA {
handler.EXPECT().Sync()
if tc.mode == api.ModeMinPV || tc.mode == api.ModePV {
handler.EXPECT().TargetCurrent().Return(int64(0))
}
}
// initial status
charger.EXPECT().Status().Return(tc.status, nil)
charger.EXPECT().Enabled().Return(true, nil)
if tc.expect != nil {
tc.expect(handler)
tc.expect(charger)
}
lp.Mode = tc.mode
lp.Update(0)
lp.Update(0) // sitePower 0
ctrl.Finish()
}
@ -209,7 +198,7 @@ func TestPVHysteresisForStatusC(t *testing.T) {
{-6 * 100 * 10, 0, 0},
{-6 * 100 * 10, 1, 0},
{-6 * 100 * 10, dt - 1, 0},
{-6 * 100 * 10, dt + 1, lpMinCurrent},
{-6 * 100 * 10, dt + 1, minA},
}},
// keep disabled when threshold not configured
{false, 0, 0, []se{
@ -230,34 +219,34 @@ func TestPVHysteresisForStatusC(t *testing.T) {
{-500, 0, 0},
{-500, 1, 0},
{-500, dt - 1, 0},
{-500, dt + 1, lpMinCurrent},
{-500, dt + 1, minA},
}},
// keep enabled at max
{true, 500, 0, []se{
{-16 * 100 * 10, 0, lpMaxCurrent},
{-16 * 100 * 10, 1, lpMaxCurrent},
{-16 * 100 * 10, dt - 1, lpMaxCurrent},
{-16 * 100 * 10, dt + 1, lpMaxCurrent},
{-16 * 100 * 10, 0, maxA},
{-16 * 100 * 10, 1, maxA},
{-16 * 100 * 10, dt - 1, maxA},
{-16 * 100 * 10, dt + 1, maxA},
}},
// keep enabled at min
{true, 500, 0, []se{
{-6 * 100 * 10, 0, lpMinCurrent},
{-6 * 100 * 10, 1, lpMinCurrent},
{-6 * 100 * 10, dt - 1, lpMinCurrent},
{-6 * 100 * 10, dt + 1, lpMinCurrent},
{-6 * 100 * 10, 0, minA},
{-6 * 100 * 10, 1, minA},
{-6 * 100 * 10, dt - 1, minA},
{-6 * 100 * 10, dt + 1, minA},
}},
// keep enabled at min (negative threshold)
{true, 0, 500, []se{
{-500, 0, lpMinCurrent},
{-500, 1, lpMinCurrent},
{-500, dt - 1, lpMinCurrent},
{-500, dt + 1, lpMinCurrent},
{-500, 0, minA},
{-500, 1, minA},
{-500, dt - 1, minA},
{-500, dt + 1, minA},
}},
// disable when threshold met
{true, 0, 500, []se{
{500, 0, lpMinCurrent},
{500, 1, lpMinCurrent},
{500, dt - 1, lpMinCurrent},
{500, 0, minA},
{500, 1, minA},
{500, dt - 1, minA},
{500, dt + 1, 0},
}},
// reset enable timer when threshold not met while timer active
@ -267,7 +256,7 @@ func TestPVHysteresisForStatusC(t *testing.T) {
{-499, dt - 1, 0}, // should reset timer
{-500, dt + 1, 0}, // new begin of timer
{-500, 2*dt - 2, 0},
{-500, 2*dt - 1, lpMinCurrent},
{-500, 2*dt - 1, minA},
}},
// reset enable timer when threshold not met while timer active and threshold not configured
{false, 0, 0, []se{
@ -275,16 +264,16 @@ func TestPVHysteresisForStatusC(t *testing.T) {
{-6 * 100 * 10, dt + 1, 0},
{-6 * 100 * 10, dt + 2, 0},
{-6 * 100 * 10, 2 * dt, 0},
{-6 * 100 * 10, 2*dt + 2, lpMinCurrent},
{-6 * 100 * 10, 2*dt + 2, minA},
}},
// reset disable timer when threshold not met while timer active
{true, 0, 500, []se{
{500, 0, lpMinCurrent},
{500, 1, lpMinCurrent},
{499, dt - 1, lpMinCurrent}, // reset timer
{500, dt + 1, lpMinCurrent}, // within reset timer duration
{500, 2*dt - 2, lpMinCurrent}, // still within reset timer duration
{500, 2*dt - 1, 0}, // reset timer elapsed
{500, 0, minA},
{500, 1, minA},
{499, dt - 1, minA}, // reset timer
{500, dt + 1, minA}, // within reset timer duration
{500, 2*dt - 2, minA}, // still within reset timer duration
{500, 2*dt - 1, 0}, // reset timer elapsed
}},
}
@ -293,18 +282,16 @@ func TestPVHysteresisForStatusC(t *testing.T) {
clck := clock.NewMock()
ctrl := gomock.NewController(t)
handler := mock.NewMockHandler(ctrl)
charger := mock.NewMockCharger(ctrl)
Voltage = 100
lp := &LoadPoint{
log: util.NewLogger("foo"),
clock: clck,
HandlerConfig: HandlerConfig{
MinCurrent: lpMinCurrent,
MaxCurrent: lpMaxCurrent,
},
handler: handler,
Phases: 10,
log: util.NewLogger("foo"),
clock: clck,
charger: charger,
MinCurrent: minA,
MaxCurrent: maxA,
Phases: 10,
Enable: ThresholdConfig{
Threshold: tc.enable,
Delay: dt,
@ -324,9 +311,9 @@ func TestPVHysteresisForStatusC(t *testing.T) {
clck.Set(start.Add(se.delay))
// maxCurrent will read actual current and enabled state in PV mode
handler.EXPECT().TargetCurrent().Return(int64(0))
handler.EXPECT().Enabled().Return(tc.enabled)
// charger.EXPECT().Enabled().Return(tc.enabled, nil)
lp.enabled = tc.enabled
current := lp.pvMaxCurrent(api.ModePV, se.site)
if current != se.current {
@ -341,25 +328,20 @@ func TestPVHysteresisForStatusC(t *testing.T) {
func TestPVHysteresisForStatusOtherThanC(t *testing.T) {
clck := clock.NewMock()
ctrl := gomock.NewController(t)
handler := mock.NewMockHandler(ctrl)
Voltage = 100
lp := &LoadPoint{
log: util.NewLogger("foo"),
clock: clck,
HandlerConfig: HandlerConfig{
MinCurrent: lpMinCurrent,
MaxCurrent: lpMaxCurrent,
},
handler: handler,
Phases: 10,
log: util.NewLogger("foo"),
clock: clck,
MinCurrent: minA,
MaxCurrent: maxA,
Phases: 10,
}
// not connected, test PV mode logic short-circuited
lp.status = api.StatusA
// maxCurrent will read actual current in PV mode
handler.EXPECT().TargetCurrent().Return(int64(0))
// maxCurrent will read enabled state in PV mode
sitePower := -float64(minA*lp.Phases)*Voltage + 1 // 1W below min power
@ -375,7 +357,7 @@ func TestPVHysteresisForStatusOtherThanC(t *testing.T) {
func TestDisableAndEnableAtTargetSoC(t *testing.T) {
clock := clock.NewMock()
ctrl := gomock.NewController(t)
handler := mock.NewMockHandler(ctrl)
charger := mock.NewMockCharger(ctrl)
vehicle := mock.NewMockVehicle(ctrl)
// wrap vehicle with estimator
@ -383,17 +365,15 @@ func TestDisableAndEnableAtTargetSoC(t *testing.T) {
socEstimator := wrapper.NewSocEstimator(util.NewLogger("foo"), vehicle, false)
lp := &LoadPoint{
log: util.NewLogger("foo"),
bus: evbus.New(),
clock: clock,
chargeMeter: &Null{}, // silence nil panics
chargeRater: &Null{}, // silence nil panics
chargeTimer: &Null{}, // silence nil panics
HandlerConfig: HandlerConfig{
MinCurrent: lpMinCurrent,
MaxCurrent: lpMaxCurrent,
},
handler: handler,
log: util.NewLogger("foo"),
bus: evbus.New(),
clock: clock,
charger: charger,
chargeMeter: &Null{}, // silence nil panics
chargeRater: &Null{}, // silence nil panics
chargeTimer: &Null{}, // silence nil panics
MinCurrent: minA,
MaxCurrent: maxA,
vehicle: vehicle, // needed for targetSoC check
socEstimator: socEstimator, // instead of vehicle: vehicle,
status: api.StatusC,
@ -403,43 +383,39 @@ func TestDisableAndEnableAtTargetSoC(t *testing.T) {
},
}
handler.EXPECT().Prepare().Return()
attachListeners(t, lp)
// charging below target
handler.EXPECT().TargetCurrent().Return(int64(6))
handler.EXPECT().Status().Return(api.StatusC, nil)
lp.enabled = true
lp.maxCurrent = minA
t.Log("charging below target")
vehicle.EXPECT().ChargeState().Return(85.0, nil)
handler.EXPECT().Sync().Return()
handler.EXPECT().Ramp(int64(16), true).Return(nil)
charger.EXPECT().Status().Return(api.StatusC, nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().MaxCurrent(maxA).Return(nil)
lp.Update(500)
// charging above target deactivates charger
t.Log("charging above target deactivates charger")
clock.Add(5 * time.Minute)
handler.EXPECT().TargetCurrent().Return(int64(16))
handler.EXPECT().Status().Return(api.StatusC, nil)
vehicle.EXPECT().ChargeState().Return(90.0, nil)
handler.EXPECT().Sync().Return()
handler.EXPECT().Ramp(int64(0), true).Return(nil) // true due to immediately handling climate requests
charger.EXPECT().Status().Return(api.StatusC, nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().Enable(false).Return(nil)
lp.Update(500)
// deactivated charger changes status to B
t.Log("deactivated charger changes status to B")
clock.Add(5 * time.Minute)
handler.EXPECT().TargetCurrent().Return(int64(0))
handler.EXPECT().Status().Return(api.StatusB, nil)
handler.EXPECT().TargetCurrent().Return(int64(0)) // once more for status changes
vehicle.EXPECT().ChargeState().Return(95.0, nil)
handler.EXPECT().Sync().Return()
handler.EXPECT().Ramp(int64(0), true).Return(nil) // true due to immediately handling climate requests
charger.EXPECT().Status().Return(api.StatusB, nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
lp.Update(-5000)
// soc has fallen below target
t.Log("soc has fallen below target")
clock.Add(5 * time.Minute)
handler.EXPECT().TargetCurrent().Return(int64(0))
handler.EXPECT().Status().Return(api.StatusB, nil)
vehicle.EXPECT().ChargeState().Return(85.0, nil)
handler.EXPECT().Sync().Return()
handler.EXPECT().Ramp(int64(16), true).Return(nil) // TODO don't treat this as forced change
charger.EXPECT().Status().Return(api.StatusB, nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().Enable(true).Return(nil)
lp.Update(-5000)
ctrl.Finish()
@ -448,21 +424,19 @@ func TestDisableAndEnableAtTargetSoC(t *testing.T) {
func TestSetModeAndSocAtDisconnect(t *testing.T) {
clock := clock.NewMock()
ctrl := gomock.NewController(t)
handler := mock.NewMockHandler(ctrl)
charger := mock.NewMockCharger(ctrl)
lp := &LoadPoint{
log: util.NewLogger("foo"),
bus: evbus.New(),
clock: clock,
charger: charger,
chargeMeter: &Null{}, // silence nil panics
chargeRater: &Null{}, // silence nil panics
chargeTimer: &Null{}, // silence nil panics
HandlerConfig: HandlerConfig{
MinCurrent: lpMinCurrent,
MaxCurrent: lpMaxCurrent,
},
handler: handler,
status: api.StatusC,
MinCurrent: minA,
MaxCurrent: maxA,
status: api.StatusC,
OnDisconnect: struct {
Mode api.ChargeMode `mapstructure:"mode"` // Charge mode to apply when car disconnected
TargetSoC int `mapstructure:"targetSoC"` // Target SoC to apply when car disconnected
@ -472,21 +446,23 @@ func TestSetModeAndSocAtDisconnect(t *testing.T) {
},
}
handler.EXPECT().Prepare().Return()
attachListeners(t, lp)
lp.enabled = true
lp.maxCurrent = minA
lp.Mode = api.ModeNow
handler.EXPECT().TargetCurrent().Return(int64(6))
handler.EXPECT().Status().Return(api.StatusC, nil)
handler.EXPECT().Sync().Return()
handler.EXPECT().Ramp(int64(16), true).Return(nil)
t.Log("charging at min")
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().Status().Return(api.StatusC, nil)
charger.EXPECT().MaxCurrent(maxA).Return(nil)
lp.Update(500)
t.Log("switch off when disconnected")
clock.Add(5 * time.Minute)
handler.EXPECT().TargetCurrent().Return(int64(16))
handler.EXPECT().Status().Return(api.StatusA, nil)
handler.EXPECT().TargetCurrent().Return(int64(0)) // once more for status changes
handler.EXPECT().Ramp(int64(0), false).Return(nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().Status().Return(api.StatusA, nil)
charger.EXPECT().Enable(false).Return(nil)
lp.Update(-3000)
if lp.Mode != api.ModeOff {
@ -519,91 +495,74 @@ func cacheExpecter(t *testing.T, lp *LoadPoint) (*util.Cache, func(key string, v
func TestChargedEnergyAtDisconnect(t *testing.T) {
clock := clock.NewMock()
ctrl := gomock.NewController(t)
handler := mock.NewMockHandler(ctrl)
charger := mock.NewMockCharger(ctrl)
rater := mock.NewMockChargeRater(ctrl)
lp := &LoadPoint{
log: util.NewLogger("foo"),
bus: evbus.New(),
clock: clock,
charger: charger,
chargeMeter: &Null{}, // silence nil panics
chargeRater: rater,
chargeTimer: &Null{}, // silence nil panics
HandlerConfig: HandlerConfig{
MinCurrent: lpMinCurrent,
MaxCurrent: lpMaxCurrent,
},
handler: handler,
status: api.StatusC,
MinCurrent: minA,
MaxCurrent: maxA,
status: api.StatusC,
}
lp.Mode = api.ModeNow
handler.EXPECT().Prepare().Return()
attachListeners(t, lp)
lp.enabled = true
lp.maxCurrent = maxA
lp.Mode = api.ModeNow
// attach cache for verifying values
_, expectCache := cacheExpecter(t, lp)
// start charging at 0 kWh
handler.EXPECT().TargetCurrent().Return(int64(6))
t.Log("start charging at 0 kWh")
rater.EXPECT().ChargedEnergy().Return(0.0, nil)
handler.EXPECT().Status().Return(api.StatusC, nil)
handler.EXPECT().Sync().Return()
handler.EXPECT().Ramp(int64(16), true).Return(nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().Status().Return(api.StatusC, nil)
lp.Update(-1)
// at 1:00h charging at 5 kWh
t.Log("at 1:00h charging at 5 kWh")
clock.Add(time.Hour)
handler.EXPECT().TargetCurrent().Return(int64(16))
rater.EXPECT().ChargedEnergy().Return(5.0, nil)
handler.EXPECT().Status().Return(api.StatusC, nil)
handler.EXPECT().Sync().Return()
// handler.EXPECT().TargetCurrent().Return(int64(0)) // once more for status changes
handler.EXPECT().Ramp(int64(16), true).Return(nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().Status().Return(api.StatusC, nil)
lp.Update(-1)
expectCache("chargedEnergy", 5000.0)
// at 1:00h stop charging at 5 kWh
t.Log("at 1:00h stop charging at 5 kWh")
clock.Add(time.Second)
handler.EXPECT().TargetCurrent().Return(int64(16))
rater.EXPECT().ChargedEnergy().Return(5.0, nil)
handler.EXPECT().Status().Return(api.StatusB, nil)
handler.EXPECT().Sync().Return()
handler.EXPECT().TargetCurrent().Return(int64(0)) // once more for status changes
handler.EXPECT().Ramp(int64(16), true).Return(nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().Status().Return(api.StatusB, nil)
lp.Update(-1)
expectCache("chargedEnergy", 5000.0)
// at 1:00h restart charging at 5 kWh
t.Log("at 1:00h restart charging at 5 kWh")
clock.Add(time.Second)
handler.EXPECT().TargetCurrent().Return(int64(16))
rater.EXPECT().ChargedEnergy().Return(5.0, nil)
handler.EXPECT().Status().Return(api.StatusC, nil)
handler.EXPECT().Sync().Return()
handler.EXPECT().TargetCurrent().Return(int64(0)) // once more for status changes
handler.EXPECT().Ramp(int64(16), true).Return(nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().Status().Return(api.StatusC, nil)
lp.Update(-1)
expectCache("chargedEnergy", 5000.0)
// at 1:30h continue charging at 7.5 kWh
t.Log("at 1:30h continue charging at 7.5 kWh")
clock.Add(30 * time.Minute)
handler.EXPECT().TargetCurrent().Return(int64(16))
rater.EXPECT().ChargedEnergy().Return(7.5, nil)
handler.EXPECT().Status().Return(api.StatusC, nil)
handler.EXPECT().Sync().Return()
// handler.EXPECT().TargetCurrent().Return(int64(0)) // once more for status changes
handler.EXPECT().Ramp(int64(16), true).Return(nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().Status().Return(api.StatusC, nil)
lp.Update(-1)
expectCache("chargedEnergy", 7500.0)
// at 2:00h stop charging at 10 kWh
t.Log("at 2:00h stop charging at 10 kWh")
clock.Add(30 * time.Minute)
handler.EXPECT().TargetCurrent().Return(int64(16))
rater.EXPECT().ChargedEnergy().Return(10.0, nil)
handler.EXPECT().Status().Return(api.StatusB, nil)
handler.EXPECT().Sync().Return()
handler.EXPECT().TargetCurrent().Return(int64(0)) // once more for status changes
handler.EXPECT().Ramp(int64(16), true).Return(nil)
charger.EXPECT().Enabled().Return(lp.enabled, nil)
charger.EXPECT().Status().Return(api.StatusB, nil)
lp.Update(-1)
expectCache("chargedEnergy", 10000.0)

View file

@ -150,11 +150,10 @@ func (site *Site) DumpConfig() {
lp.log.INFO.Printf(" mode: %s", lp.GetMode())
charger := lp.handler.(*ChargerHandler).charger
_, power := charger.(api.Meter)
_, energy := charger.(api.MeterEnergy)
_, currents := charger.(api.MeterCurrent)
_, timer := charger.(api.ChargeTimer)
_, power := lp.charger.(api.Meter)
_, energy := lp.charger.(api.MeterEnergy)
_, currents := lp.charger.(api.MeterCurrent)
_, timer := lp.charger.(api.ChargeTimer)
lp.log.INFO.Printf(" charger: power %s energy %s currents %s timer %s",
presence[power],

View file

@ -1,115 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/andig/evcc/core (interfaces: Handler)
// Package mock is a generated GoMock package.
package mock
import (
api "github.com/andig/evcc/api"
gomock "github.com/golang/mock/gomock"
reflect "reflect"
)
// MockHandler is a mock of Handler interface
type MockHandler struct {
ctrl *gomock.Controller
recorder *MockHandlerMockRecorder
}
// MockHandlerMockRecorder is the mock recorder for MockHandler
type MockHandlerMockRecorder struct {
mock *MockHandler
}
// NewMockHandler creates a new mock instance
func NewMockHandler(ctrl *gomock.Controller) *MockHandler {
mock := &MockHandler{ctrl: ctrl}
mock.recorder = &MockHandlerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockHandler) EXPECT() *MockHandlerMockRecorder {
return m.recorder
}
// Enabled mocks base method
func (m *MockHandler) Enabled() bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Enabled")
ret0, _ := ret[0].(bool)
return ret0
}
// Enabled indicates an expected call of Enabled
func (mr *MockHandlerMockRecorder) Enabled() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Enabled", reflect.TypeOf((*MockHandler)(nil).Enabled))
}
// Prepare mocks base method
func (m *MockHandler) Prepare() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Prepare")
}
// Prepare indicates an expected call of Prepare
func (mr *MockHandlerMockRecorder) Prepare() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prepare", reflect.TypeOf((*MockHandler)(nil).Prepare))
}
// Ramp mocks base method
func (m *MockHandler) Ramp(arg0 int64, arg1 bool) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Ramp", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// Ramp indicates an expected call of Ramp
func (mr *MockHandlerMockRecorder) Ramp(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ramp", reflect.TypeOf((*MockHandler)(nil).Ramp), arg0, arg1)
}
// Status mocks base method
func (m *MockHandler) Status() (api.ChargeStatus, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Status")
ret0, _ := ret[0].(api.ChargeStatus)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Status indicates an expected call of Status
func (mr *MockHandlerMockRecorder) Status() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockHandler)(nil).Status))
}
// Sync mocks base method
func (m *MockHandler) Sync() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Sync")
}
// Sync indicates an expected call of Sync
func (mr *MockHandlerMockRecorder) Sync() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sync", reflect.TypeOf((*MockHandler)(nil).Sync))
}
// TargetCurrent mocks base method
func (m *MockHandler) TargetCurrent() int64 {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "TargetCurrent")
ret0, _ := ret[0].(int64)
return ret0
}
// TargetCurrent indicates an expected call of TargetCurrent
func (mr *MockHandlerMockRecorder) TargetCurrent() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TargetCurrent", reflect.TypeOf((*MockHandler)(nil).TargetCurrent))
}