Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EEbus: upgrade stack to v2 #12407

Closed
wants to merge 19 commits into from
Closed
121 changes: 74 additions & 47 deletions charger/eebus.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,23 @@ import (
"sync"
"time"

"github.com/enbility/cemd/emobility"
"github.com/enbility/eebus-go/features"
cemdapi "github.com/enbility/cemd/api"
"github.com/enbility/cemd/ucevcc"
"github.com/enbility/cemd/ucevcem"
"github.com/enbility/cemd/ucevsecc"
"github.com/enbility/cemd/ucevsoc"
"github.com/enbility/cemd/ucopev"
eebusapi "github.com/enbility/eebus-go/api"
spineapi "github.com/enbility/spine-go/api"
"github.com/enbility/spine-go/model"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/charger/eebus"
"github.com/evcc-io/evcc/core/loadpoint"
"github.com/evcc-io/evcc/provider"
"github.com/evcc-io/evcc/util"
)

//go:generate mockgen -package charger -destination eebus_test_mock.go github.com/enbility/cemd/emobility EmobilityI
// go:generate mockgen -package charger -destination eebus_test_mock.go github.com/enbility/cemd/emobility EmobilityI

const (
maxIdRequestTimespan = time.Second * 120
Expand All @@ -29,14 +36,21 @@ type minMax struct {
}

type EEBus struct {
ski string
emobility emobility.EmobilityI
mux sync.Mutex
log *util.Logger

log *util.Logger
lp loadpoint.API
minMaxG func() (minMax, error)

communicationStandard emobility.EVCommunicationStandardType
ski string
entity spineapi.EntityRemoteInterface
ucEvseCC ucevsecc.UCEVSECCInterface // EVSE Commissioning and Configuration
ucEvCC ucevcc.UCEVCCInterface // EV Commissioning and Configuration
ucEvCem ucevcem.UCEVCEMInterface // EV Charging Electricity Measurement
ucEvOP ucopev.UCOPEVInterface // EV Overload Protection
ucEvSoc ucevsoc.UCEVSOCInterface // EV State Of Charge

communicationStandard string

expectedEnableUnpluggedState bool
current float64
Expand All @@ -51,8 +65,6 @@ type EEBus struct {
connected bool
connectedC chan bool
connectedTime time.Time

mux sync.Mutex
}

func init() {
Expand Down Expand Up @@ -91,11 +103,18 @@ func NewEEBus(ski, ip string, hasMeter, hasChargedEnergy bool) (api.Charger, err
ski: ski,
log: log,
connectedC: make(chan bool, 1),
communicationStandard: emobility.EVCommunicationStandardTypeUnknown,
communicationStandard: cemdapi.UCEVCCCommunicationStandardUnknown,
current: 6,
}

c.emobility = eebus.Instance.RegisterEVSE(ski, ip, c.onConnect, c.onDisconnect, nil)
c.entity = eebus.Instance.RegisterEVSE(ski, ip, c.onConnect, c.onDisconnect)

service := eebus.Instance.Service()
c.ucEvseCC = ucevsecc.NewUCEVSECC(service, c.ucCallback)
c.ucEvCC = ucevcc.NewUCEVCC(service, c.ucCallback)
c.ucEvCem = ucevcem.NewUCEVCEM(service, c.ucCallback)
c.ucEvOP = ucopev.NewUCOPEV(service, c.ucCallback)
c.ucEvSoc = ucevsoc.NewUCEVSOC(service, c.ucCallback)

andig marked this conversation as resolved.
Show resolved Hide resolved
c.minMaxG = provider.Cached(c.minMax, time.Second)

Expand Down Expand Up @@ -137,6 +156,10 @@ func (c *EEBus) onConnect(ski string) {
c.setConnected(true)
}

func (c *EEBus) ucCallback(ski string, device spineapi.DeviceRemoteInterface, entity spineapi.EntityRemoteInterface, event cemdapi.EventType) {
// TODO implement
}

func (c *EEBus) onDisconnect(ski string) {
c.log.TRACE.Println("disconnect ski:", ski)

Expand All @@ -146,7 +169,7 @@ func (c *EEBus) onDisconnect(ski string) {
}

func (c *EEBus) setDefaultValues() {
c.communicationStandard = emobility.EVCommunicationStandardTypeUnknown
c.communicationStandard = cemdapi.UCEVCCCommunicationStandardUnknown
c.lastIsChargingCheck = time.Now().Add(-time.Hour * 1)
c.lastIsChargingResult = false
}
Expand All @@ -167,7 +190,7 @@ func (c *EEBus) setConnected(connected bool) {
c.connected = connected
}

func (c *EEBus) isConnected() bool {
func (c *EEBus) isEVConnected() bool {
c.mux.Lock()
defer c.mux.Unlock()

Expand All @@ -177,9 +200,9 @@ func (c *EEBus) isConnected() bool {
var _ api.CurrentLimiter = (*EEBus)(nil)

func (c *EEBus) minMax() (minMax, error) {
minLimits, maxLimits, _, err := c.emobility.EVCurrentLimits()
minLimits, maxLimits, _, err := c.ucEvCC.CurrentLimits(c.entity)
if err != nil {
if err == features.ErrDataNotAvailable {
if err == eebusapi.ErrDataNotAvailable {
err = api.ErrNotAvailable
}
return minMax{}, err
Expand Down Expand Up @@ -218,11 +241,11 @@ func (c *EEBus) isCharging() bool { // d *communication.EVSEClientDataType
}

// The above doesn't (yet) work for built in meters, so check the EEBUS measurements also
currents, err := c.emobility.EVCurrentsPerPhase()
currents, err := c.ucEvCem.CurrentPerPhase(c.entity)
if err != nil {
return false
}
limitsMin, _, _, err := c.emobility.EVCurrentLimits()
limitsMin, _, _, err := c.ucEvCC.CurrentLimits(c.entity)
if err != nil || limitsMin == nil || len(limitsMin) == 0 {
return false
}
Expand All @@ -241,11 +264,11 @@ func (c *EEBus) isCharging() bool { // d *communication.EVSEClientDataType

// Status implements the api.Charger interface
func (c *EEBus) Status() (api.ChargeStatus, error) {
if !c.isConnected() {
if !c.isEVConnected() {
return api.StatusNone, api.ErrTimeout
}

if !c.emobility.EVConnected() {
if !c.ucEvCC.EVConnected(c.entity) {
c.expectedEnableUnpluggedState = false
c.evConnected = false
return api.StatusA, nil
Expand All @@ -256,23 +279,23 @@ func (c *EEBus) Status() (api.ChargeStatus, error) {
c.currentLimit = -1
}

currentState, err := c.emobility.EVCurrentChargeState()
currentState, err := c.ucEvCC.ChargeState(c.entity)
if err != nil {
return api.StatusNone, err
}

switch currentState {
case emobility.EVChargeStateTypeUnknown, emobility.EVChargeStateTypeUnplugged: // Unplugged
case cemdapi.EVChargeStateTypeUnknown, cemdapi.EVChargeStateTypeUnplugged: // Unplugged
c.expectedEnableUnpluggedState = false
return api.StatusA, nil
case emobility.EVChargeStateTypeFinished, emobility.EVChargeStateTypePaused: // Finished, Paused
case cemdapi.EVChargeStateTypeFinished, cemdapi.EVChargeStateTypePaused: // Finished, Paused
return api.StatusB, nil
case emobility.EVChargeStateTypeActive: // Active
case cemdapi.EVChargeStateTypeActive: // Active
if c.isCharging() {
return api.StatusC, nil
}
return api.StatusB, nil
case emobility.EVChargeStateTypeError: // Error
case cemdapi.EVChargeStateTypeError: // Error
return api.StatusF, nil
default:
return api.StatusNone, fmt.Errorf("%s properties unknown result: %s", c.ski, currentState)
Expand All @@ -293,7 +316,7 @@ func (c *EEBus) Enabled() (bool, error) {
return true, nil
}

limits, err := c.emobility.EVLoadControlObligationLimits()
limits, err := c.ucEvOP.LoadControlLimits(c.entity)
if err != nil {
// there are no overload protection limits available, e.g. because the data was not received yet
return true, nil
Expand Down Expand Up @@ -322,8 +345,8 @@ func (c *EEBus) Enable(enable bool) error {
// if we disable charging with a potential but not yet known communication standard ISO15118
// this would set allowed A value to be 0. And this would trigger ISO connections to switch to IEC!
if !enable {
comStandard, err := c.emobility.EVCommunicationStandard()
if err != nil || comStandard == emobility.EVCommunicationStandardTypeUnknown {
comStandard, err := c.ucEvCC.CommunicationStandard(c.entity)
if err != nil || comStandard == cemdapi.UCEVCCCommunicationStandardUnknown {
return api.ErrMustRetry
}
}
Expand All @@ -339,7 +362,7 @@ func (c *EEBus) Enable(enable bool) error {

// send current charging power limits to the EV
func (c *EEBus) writeCurrentLimitData(currents []float64) error {
comStandard, err := c.emobility.EVCommunicationStandard()
comStandard, err := c.ucEvCC.CommunicationStandard(c.entity)
if err != nil {
return err
}
Expand All @@ -350,8 +373,8 @@ func (c *EEBus) writeCurrentLimitData(currents []float64) error {
// wait for the car to go into sleep and plug it back in.
// So if there are currents smaller than 6A with unknown communication standard change them to 6A.
// Keep in mind that this will still confuse evcc as it thinks charging is stopped, but it hasn't yet.
if comStandard == emobility.EVCommunicationStandardTypeUnknown {
minLimits, _, _, err := c.emobility.EVCurrentLimits()
if comStandard == cemdapi.UCEVCCCommunicationStandardUnknown {
minLimits, _, _, err := c.ucEvCC.CurrentLimits(c.entity)
if err == nil {
for index, current := range currents {
if index < len(minLimits) && current < minLimits[index] {
Expand All @@ -363,7 +386,11 @@ func (c *EEBus) writeCurrentLimitData(currents []float64) error {

// Set overload protection limits and self consumption limits to identical values,
// so if the EV supports self consumption it will be used automatically.
if err = c.emobility.EVWriteLoadControlLimits(currents, currents); err == nil {
if _, err = c.ucEvOP.WriteLoadControlLimits(c.entity, []cemdapi.LoadLimitsPhase{
{Phase: model.ElectricalConnectionPhaseNameTypeA, IsActive: true, Value: currents[0]},
{Phase: model.ElectricalConnectionPhaseNameTypeB, IsActive: true, Value: currents[1]},
{Phase: model.ElectricalConnectionPhaseNameTypeC, IsActive: true, Value: currents[2]},
}); err == nil {
c.currentLimit = currents[0]
}

Expand All @@ -379,7 +406,7 @@ var _ api.ChargerEx = (*EEBus)(nil)

// MaxCurrentMillis implements the api.ChargerEx interface
func (c *EEBus) MaxCurrentMillis(current float64) error {
if !c.connected || !c.emobility.EVConnected() {
if !c.connected || !c.ucEvCC.EVConnected(c.entity) {
return errors.New("can't set new current as ev is unplugged")
}

Expand All @@ -401,16 +428,16 @@ func (c *EEBus) GetMaxCurrent() (float64, error) {

// CurrentPower implements the api.Meter interface
func (c *EEBus) currentPower() (float64, error) {
if !c.emobility.EVConnected() {
if !c.ucEvCC.EVConnected(c.entity) {
return 0, nil
}

connectedPhases, err := c.emobility.EVConnectedPhases()
connectedPhases, err := c.ucEvCem.PhasesConnected(c.entity)
if err != nil {
return 0, err
}

powers, err := c.emobility.EVPowerPerPhase()
powers, err := c.ucEvCem.PowerPerPhase(c.entity)
if err != nil {
return 0, err
}
Expand All @@ -428,11 +455,11 @@ func (c *EEBus) currentPower() (float64, error) {

// ChargedEnergy implements the api.ChargeRater interface
func (c *EEBus) chargedEnergy() (float64, error) {
if !c.emobility.EVConnected() {
if !c.ucEvCC.EVConnected(c.entity) {
return 0, nil
}

energy, err := c.emobility.EVChargedEnergy()
energy, err := c.ucEvCem.EnergyCharged(c.entity)
if err != nil {
return 0, err
}
Expand All @@ -442,13 +469,13 @@ func (c *EEBus) chargedEnergy() (float64, error) {

// Currents implements the api.PhaseCurrents interface
func (c *EEBus) currents() (float64, float64, float64, error) {
if !c.emobility.EVConnected() {
if !c.ucEvCC.EVConnected(c.entity) {
return 0, 0, 0, nil
}

res, err := c.emobility.EVCurrentsPerPhase()
res, err := c.ucEvCem.CurrentPerPhase(c.entity)
if err != nil {
if err == features.ErrDataNotAvailable {
if err == eebusapi.ErrDataNotAvailable {
err = api.ErrNotAvailable
}
return 0, 0, 0, err
Expand All @@ -466,18 +493,18 @@ var _ api.Identifier = (*EEBus)(nil)

// Identify implements the api.Identifier interface
func (c *EEBus) Identify() (string, error) {
if !c.isConnected() || !c.emobility.EVConnected() {
if !c.isEVConnected() || !c.ucEvCC.EVConnected(c.entity) {
return "", nil
}

if !c.emobility.EVConnected() {
if !c.ucEvCC.EVConnected(c.entity) {
return "", nil
}
if identification, _ := c.emobility.EVIdentification(); identification != "" {
return identification, nil
if identification, _ := c.ucEvCC.Identifications(c.entity); len(identification) > 0 {
return identification[0].Value, nil
}

if comStandard, _ := c.emobility.EVCommunicationStandard(); comStandard == emobility.EVCommunicationStandardTypeIEC61851 {
if comStandard, _ := c.ucEvCC.CommunicationStandard(c.entity); comStandard == "emobility.CommunicationStandardTypeIEC61851" {
return "", nil
}

Expand All @@ -492,11 +519,11 @@ var _ api.Battery = (*EEBus)(nil)

// Soc implements the api.Vehicle interface
func (c *EEBus) Soc() (float64, error) {
if socSupported, err := c.emobility.EVSoCSupported(); err != nil || !socSupported {
if socSupported, err := c.ucEvSoc.IsUseCaseSupported(c.entity); err != nil || !socSupported {
return 0, api.ErrNotAvailable
}

soc, err := c.emobility.EVSoC()
soc, err := c.ucEvSoc.StateOfCharge(c.entity)
if err != nil {
return 0, api.ErrNotAvailable
}
Expand Down
Loading