Skip to content

Commit

Permalink
Ocpp: cache and re-use initial status (2nd attempt) (evcc-io#16885)
Browse files Browse the repository at this point in the history
andig authored Oct 26, 2024

Unverified

This commit is not signed, but one or more authors requires that any commit attributed to them is signed.
1 parent 5827307 commit 6f7489a
Showing 4 changed files with 89 additions and 59 deletions.
16 changes: 14 additions & 2 deletions charger/ocpp/cp_setup.go
Original file line number Diff line number Diff line change
@@ -158,8 +158,20 @@ func (cp *CP) Setup(meterValues string, meterInterval time.Duration) error {

// trigger status for all connectors
if cp.HasRemoteTriggerFeature {
if err := cp.TriggerMessageRequest(0, core.StatusNotificationFeatureName); err != nil {
cp.log.WARN.Printf("failed triggering StatusNotification: %v", err)
var ok bool

// apply cached status if available
instance.WithChargepointStatusByID(cp.id, func(status *core.StatusNotificationRequest) {
if _, err := cp.OnStatusNotification(status); err == nil {
ok = true
}
})

// only trigger if we don't already have a status
if !ok {
if err := cp.TriggerMessageRequest(0, core.StatusNotificationFeatureName); err != nil {
cp.log.WARN.Printf("failed triggering StatusNotification: %v", err)
}
}
}

119 changes: 64 additions & 55 deletions charger/ocpp/cs.go
Original file line number Diff line number Diff line change
@@ -8,45 +8,24 @@ import (

"github.com/evcc-io/evcc/util"
ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6"
"github.com/lorenzodonini/ocpp-go/ocpp1.6/core"
)

type registration struct {
mu sync.RWMutex
setup sync.RWMutex // serialises chargepoint setup
cp *CP // guarded by setup and CS mutexes
status *core.StatusNotificationRequest // guarded by mu mutex
}

type CS struct {
mu sync.Mutex
log *util.Logger
ocpp16.CentralSystem
cps map[string]*CP
init map[string]*sync.Mutex
mu sync.Mutex
log *util.Logger
regs map[string]*registration // guarded by mu mutex
txnId atomic.Int64
}

// Register registers a charge point with the central system.
// The charge point identified by id may already be connected in which case initial connection is triggered.
func (cs *CS) register(id string, new *CP) error {
cs.mu.Lock()
defer cs.mu.Unlock()

cp, ok := cs.cps[id]

// case 1: charge point neither registered nor physically connected
if !ok {
cs.cps[id] = new
return nil
}

// case 2: duplicate registration of id empty
if id == "" {
return errors.New("cannot have >1 charge point with empty station id")
}

// case 3: charge point not registered but physically already connected
if cp == nil {
cs.cps[id] = new
new.connect(true)
}

return nil
}

// errorHandler logs error channel
func (cs *CS) errorHandler(errC <-chan error) {
for err := range errC {
@@ -58,38 +37,67 @@ func (cs *CS) ChargepointByID(id string) (*CP, error) {
cs.mu.Lock()
defer cs.mu.Unlock()

cp, ok := cs.cps[id]
reg, ok := cs.regs[id]
if !ok {
return nil, fmt.Errorf("unknown charge point: %s", id)
}
if cp == nil {
if reg.cp == nil {
return nil, fmt.Errorf("charge point not configured: %s", id)
}
return cp, nil
return reg.cp, nil
}

func (cs *CS) WithChargepointStatusByID(id string, fun func(status *core.StatusNotificationRequest)) {
cs.mu.Lock()
defer cs.mu.Unlock()

if reg, ok := cs.regs[id]; ok {
reg.mu.RLock()
if reg.status != nil {
fun(reg.status)
}
reg.mu.RUnlock()
}
}

// RegisterChargepoint registers a charge point with the central system of returns an already registered charge point
func (cs *CS) RegisterChargepoint(id string, newfun func() *CP, init func(*CP) error) (*CP, error) {
cs.mu.Lock()
cpmu, ok := cs.init[id]
if !ok {
cpmu = new(sync.Mutex)
cs.init[id] = cpmu

// prepare shadow state
reg, registered := cs.regs[id]
if !registered {
reg = new(registration)
cs.regs[id] = reg
}
cs.mu.Unlock()

// serialise on chargepoint id
cpmu.Lock()
defer cpmu.Unlock()
reg.setup.Lock()
defer reg.setup.Unlock()

cp := reg.cp

cs.mu.Unlock()

// setup already completed?
if cp != nil {
// duplicate registration of id empty
if id == "" {
return nil, errors.New("cannot have >1 charge point with empty station id")
}

// already registered?
if cp, err := cs.ChargepointByID(id); err == nil {
return cp, nil
}

// first time- registration should not error
cp := newfun()
if err := cs.register(id, cp); err != nil {
return nil, err
// first time- create the charge point
cp = newfun()

cs.mu.Lock()
reg.cp = cp
cs.mu.Unlock()

if registered {
cp.connect(true)
}

return cp, init(cp)
@@ -101,28 +109,29 @@ func (cs *CS) NewChargePoint(chargePoint ocpp16.ChargePointConnection) {
defer cs.mu.Unlock()

// check for configured charge point
cp, ok := cs.cps[chargePoint.ID()]
reg, ok := cs.regs[chargePoint.ID()]
if ok {
cs.log.DEBUG.Printf("charge point connected: %s", chargePoint.ID())

// trigger initial connection if charge point is already setup
if cp != nil {
if cp := reg.cp; cp != nil {
cp.connect(true)
}

return
}

// check for configured anonymous charge point
cp, ok = cs.cps[""]
if ok && cp != nil {
reg, ok = cs.regs[""]
if ok && reg.cp != nil {
cp := reg.cp
cs.log.INFO.Printf("charge point connected, registering: %s", chargePoint.ID())

// update id
cp.RegisterID(chargePoint.ID())

cs.cps[chargePoint.ID()] = cp
delete(cs.cps, "")
cs.regs[chargePoint.ID()].cp = cp
delete(cs.regs, "")

cp.connect(true)

@@ -133,7 +142,7 @@ func (cs *CS) NewChargePoint(chargePoint ocpp16.ChargePointConnection) {

// register unknown charge point
// when charge point setup is complete, it will eventually be associated with the connected id
cs.cps[chargePoint.ID()] = nil
cs.regs[chargePoint.ID()] = new(registration)
}

// ChargePointDisconnected implements ocpp16.ChargePointConnectionHandler
10 changes: 10 additions & 0 deletions charger/ocpp/cs_core.go
Original file line number Diff line number Diff line change
@@ -66,6 +66,16 @@ func (cs *CS) OnStatusNotification(id string, request *core.StatusNotificationRe
return cp.OnStatusNotification(request)
}

cs.mu.Lock()
defer cs.mu.Unlock()

// cache status for future cp connection
if reg, ok := cs.regs[id]; ok {
reg.mu.Lock()
reg.status = request
reg.mu.Unlock()
}

return new(core.StatusNotificationConfirmation), nil
}

3 changes: 1 addition & 2 deletions charger/ocpp/instance.go
Original file line number Diff line number Diff line change
@@ -40,8 +40,7 @@ func Instance() *CS {

instance = &CS{
log: log,
cps: make(map[string]*CP),
init: make(map[string]*sync.Mutex),
regs: make(map[string]*registration),
CentralSystem: cs,
}
instance.txnId.Store(time.Now().UTC().Unix())

0 comments on commit 6f7489a

Please sign in to comment.