Skip to content

Commit 21e7356

Browse files
authored
Fix connect/disconnect handlers (#266)
1 parent b7be4dd commit 21e7356

File tree

2 files changed

+143
-15
lines changed

2 files changed

+143
-15
lines changed

core/loadpoint.go

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,8 @@ func (lp *LoadPoint) Prepare(uiChan chan<- util.Param, pushChan chan<- push.Even
333333
// event handlers
334334
_ = lp.bus.Subscribe(evChargeStart, lp.evChargeStartHandler)
335335
_ = lp.bus.Subscribe(evChargeStop, lp.evChargeStopHandler)
336+
_ = lp.bus.Subscribe(evVehicleConnect, lp.evVehicleConnectHandler)
337+
_ = lp.bus.Subscribe(evVehicleDisconnect, lp.evVehicleDisconnectHandler)
336338

337339
// publish initial values
338340
lp.Lock()
@@ -355,8 +357,8 @@ func (lp *LoadPoint) targetSocReached(socCharge, targetSoC float64) bool {
355357
return targetSoC > 0 && targetSoC < 100 && socCharge >= targetSoC
356358
}
357359

358-
// updateChargeStatus updates car status and detects car connected/disconnected events
359-
func (lp *LoadPoint) updateChargeStatus() error {
360+
// updateChargerStatus updates car status and detects car connected/disconnected events
361+
func (lp *LoadPoint) updateChargerStatus() error {
360362
status, err := lp.handler.Status()
361363
if err != nil {
362364
return err
@@ -372,14 +374,11 @@ func (lp *LoadPoint) updateChargeStatus() error {
372374
lp.bus.Publish(evVehicleConnect)
373375
}
374376

375-
// start/stop charging cycle - handle before disconnect to update energy
377+
// changed to C - start/stop charging cycle - handle before disconnect to update energy
376378
if lp.charging = status == api.StatusC; lp.charging {
377379
lp.bus.Publish(evChargeStart)
378-
} else {
379-
// omit initial stop event before started
380-
if prevStatus != api.StatusNone {
381-
lp.bus.Publish(evChargeStop)
382-
}
380+
} else if prevStatus == api.StatusC {
381+
lp.bus.Publish(evChargeStop)
383382
}
384383

385384
// changed to A - disconnected
@@ -602,7 +601,7 @@ func (lp *LoadPoint) Update(sitePower float64) {
602601
lp.publishSoC()
603602

604603
// read and publish status
605-
if err := retry.Do(lp.updateChargeStatus, retryOptions...); err != nil {
604+
if err := lp.updateChargerStatus(); err != nil {
606605
lp.log.ERROR.Printf("charge controller error: %v", err)
607606
return
608607
}

core/loadpoint_test.go

Lines changed: 135 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,32 @@ func (n *Null) ChargingTime() (time.Duration, error) {
3232
return 0, nil
3333
}
3434

35-
func attachListeners(lp *LoadPoint) {
35+
func attachListeners(t *testing.T, lp *LoadPoint) {
3636
uiChan := make(chan util.Param)
3737
pushChan := make(chan push.Event)
38+
lpChan := make(chan *LoadPoint)
3839

40+
log := false
3941
go func() {
4042
for {
4143
select {
42-
case <-uiChan:
43-
case <-pushChan:
44+
case v := <-uiChan:
45+
if log {
46+
t.Log(v)
47+
}
48+
case v := <-pushChan:
49+
if log {
50+
t.Log(v)
51+
}
52+
case v := <-lpChan:
53+
if log {
54+
t.Log(v)
55+
}
4456
}
4557
}
4658
}()
4759

48-
lp.uiChan = uiChan
49-
lp.pushChan = pushChan
60+
lp.Prepare(uiChan, pushChan, lpChan)
5061
}
5162

5263
func TestNew(t *testing.T) {
@@ -143,7 +154,8 @@ func TestUpdate(t *testing.T) {
143154
status: tc.status, // no status change
144155
}
145156

146-
attachListeners(lp)
157+
handler.EXPECT().Prepare().Return()
158+
attachListeners(t, lp)
147159

148160
handler.EXPECT().Status().Return(tc.status, nil)
149161
handler.EXPECT().TargetCurrent().Return(int64(0))
@@ -373,3 +385,120 @@ func TestRemainingChargeDuration(t *testing.T) {
373385
t.Error("wrong remaining charge duration")
374386
}
375387
}
388+
389+
func TestDisableAndEnableAtTargetSoC(t *testing.T) {
390+
clock := clock.NewMock()
391+
ctrl := gomock.NewController(t)
392+
handler := mock.NewMockHandler(ctrl)
393+
vehicle := mock.NewMockVehicle(ctrl)
394+
395+
lp := &LoadPoint{
396+
log: util.NewLogger("foo"),
397+
bus: evbus.New(),
398+
clock: clock,
399+
chargeMeter: &Null{}, //silence nil panics
400+
chargeRater: &Null{}, //silence nil panics
401+
chargeTimer: &Null{}, //silence nil panics
402+
HandlerConfig: HandlerConfig{
403+
MinCurrent: lpMinCurrent,
404+
MaxCurrent: lpMaxCurrent,
405+
},
406+
handler: handler,
407+
vehicle: vehicle,
408+
status: api.StatusC,
409+
TargetSoC: 90,
410+
}
411+
412+
handler.EXPECT().Prepare().Return()
413+
attachListeners(t, lp)
414+
415+
// charging below target
416+
lp.Mode = api.ModeNow
417+
handler.EXPECT().TargetCurrent().Return(int64(6))
418+
handler.EXPECT().Status().Return(api.StatusC, nil)
419+
vehicle.EXPECT().ChargeState().Return(85.0, nil)
420+
handler.EXPECT().SyncEnabled().Return()
421+
handler.EXPECT().Ramp(int64(16), true).Return(nil)
422+
lp.Update(500)
423+
424+
// charging above target deactivates charger
425+
clock.Add(5 * time.Minute)
426+
handler.EXPECT().TargetCurrent().Return(int64(16))
427+
handler.EXPECT().Status().Return(api.StatusC, nil)
428+
vehicle.EXPECT().ChargeState().Return(90.0, nil)
429+
handler.EXPECT().SyncEnabled().Return()
430+
handler.EXPECT().Ramp(int64(0)).Return(nil)
431+
lp.Update(500)
432+
433+
// deactivated charger changes status to B
434+
clock.Add(5 * time.Minute)
435+
handler.EXPECT().TargetCurrent().Return(int64(0))
436+
handler.EXPECT().Status().Return(api.StatusB, nil)
437+
handler.EXPECT().TargetCurrent().Return(int64(0)) // once more for status changes
438+
vehicle.EXPECT().ChargeState().Return(95.0, nil)
439+
handler.EXPECT().SyncEnabled().Return()
440+
handler.EXPECT().Ramp(int64(0)).Return(nil)
441+
lp.Update(-5000)
442+
443+
// soc has fallen below target
444+
clock.Add(5 * time.Minute)
445+
handler.EXPECT().TargetCurrent().Return(int64(0))
446+
handler.EXPECT().Status().Return(api.StatusB, nil)
447+
vehicle.EXPECT().ChargeState().Return(85.0, nil)
448+
handler.EXPECT().SyncEnabled().Return()
449+
handler.EXPECT().Ramp(int64(16), true).Return(nil) // TODO don't treat this as forced change
450+
lp.Update(-5000)
451+
452+
ctrl.Finish()
453+
}
454+
455+
func TestSetModeAndSocAtDisconnect(t *testing.T) {
456+
clock := clock.NewMock()
457+
ctrl := gomock.NewController(t)
458+
handler := mock.NewMockHandler(ctrl)
459+
460+
lp := &LoadPoint{
461+
log: util.NewLogger("foo"),
462+
bus: evbus.New(),
463+
clock: clock,
464+
chargeMeter: &Null{}, //silence nil panics
465+
chargeRater: &Null{}, //silence nil panics
466+
chargeTimer: &Null{}, //silence nil panics
467+
HandlerConfig: HandlerConfig{
468+
MinCurrent: lpMinCurrent,
469+
MaxCurrent: lpMaxCurrent,
470+
},
471+
handler: handler,
472+
status: api.StatusC,
473+
OnDisconnect: struct {
474+
Mode api.ChargeMode `mapstructure:"mode"` // Charge mode to apply when car disconnected
475+
TargetSoC int `mapstructure:"targetSoC"` // Target SoC to apply when car disconnected
476+
}{
477+
Mode: api.ModeOff,
478+
TargetSoC: 70,
479+
},
480+
}
481+
482+
handler.EXPECT().Prepare().Return()
483+
attachListeners(t, lp)
484+
485+
lp.Mode = api.ModeNow
486+
handler.EXPECT().TargetCurrent().Return(int64(6))
487+
handler.EXPECT().Status().Return(api.StatusC, nil)
488+
handler.EXPECT().SyncEnabled().Return()
489+
handler.EXPECT().Ramp(int64(16), true).Return(nil)
490+
lp.Update(500)
491+
492+
clock.Add(5 * time.Minute)
493+
handler.EXPECT().TargetCurrent().Return(int64(16))
494+
handler.EXPECT().Status().Return(api.StatusA, nil)
495+
handler.EXPECT().TargetCurrent().Return(int64(0)) // once more for status changes
496+
handler.EXPECT().Ramp(int64(0)).Return(nil)
497+
lp.Update(-3000)
498+
499+
if lp.Mode != api.ModeOff {
500+
t.Error("unexpected mode", lp.Mode)
501+
}
502+
503+
ctrl.Finish()
504+
}

0 commit comments

Comments
 (0)