Skip to content

Commit 6f7489a

Browse files
authored
Ocpp: cache and re-use initial status (2nd attempt) (#16885)
1 parent 5827307 commit 6f7489a

File tree

4 files changed

+89
-59
lines changed

4 files changed

+89
-59
lines changed

charger/ocpp/cp_setup.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,20 @@ func (cp *CP) Setup(meterValues string, meterInterval time.Duration) error {
158158

159159
// trigger status for all connectors
160160
if cp.HasRemoteTriggerFeature {
161-
if err := cp.TriggerMessageRequest(0, core.StatusNotificationFeatureName); err != nil {
162-
cp.log.WARN.Printf("failed triggering StatusNotification: %v", err)
161+
var ok bool
162+
163+
// apply cached status if available
164+
instance.WithChargepointStatusByID(cp.id, func(status *core.StatusNotificationRequest) {
165+
if _, err := cp.OnStatusNotification(status); err == nil {
166+
ok = true
167+
}
168+
})
169+
170+
// only trigger if we don't already have a status
171+
if !ok {
172+
if err := cp.TriggerMessageRequest(0, core.StatusNotificationFeatureName); err != nil {
173+
cp.log.WARN.Printf("failed triggering StatusNotification: %v", err)
174+
}
163175
}
164176
}
165177

charger/ocpp/cs.go

+64-55
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,24 @@ import (
88

99
"github.com/evcc-io/evcc/util"
1010
ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6"
11+
"github.com/lorenzodonini/ocpp-go/ocpp1.6/core"
1112
)
1213

14+
type registration struct {
15+
mu sync.RWMutex
16+
setup sync.RWMutex // serialises chargepoint setup
17+
cp *CP // guarded by setup and CS mutexes
18+
status *core.StatusNotificationRequest // guarded by mu mutex
19+
}
20+
1321
type CS struct {
14-
mu sync.Mutex
15-
log *util.Logger
1622
ocpp16.CentralSystem
17-
cps map[string]*CP
18-
init map[string]*sync.Mutex
23+
mu sync.Mutex
24+
log *util.Logger
25+
regs map[string]*registration // guarded by mu mutex
1926
txnId atomic.Int64
2027
}
2128

22-
// Register registers a charge point with the central system.
23-
// The charge point identified by id may already be connected in which case initial connection is triggered.
24-
func (cs *CS) register(id string, new *CP) error {
25-
cs.mu.Lock()
26-
defer cs.mu.Unlock()
27-
28-
cp, ok := cs.cps[id]
29-
30-
// case 1: charge point neither registered nor physically connected
31-
if !ok {
32-
cs.cps[id] = new
33-
return nil
34-
}
35-
36-
// case 2: duplicate registration of id empty
37-
if id == "" {
38-
return errors.New("cannot have >1 charge point with empty station id")
39-
}
40-
41-
// case 3: charge point not registered but physically already connected
42-
if cp == nil {
43-
cs.cps[id] = new
44-
new.connect(true)
45-
}
46-
47-
return nil
48-
}
49-
5029
// errorHandler logs error channel
5130
func (cs *CS) errorHandler(errC <-chan error) {
5231
for err := range errC {
@@ -58,38 +37,67 @@ func (cs *CS) ChargepointByID(id string) (*CP, error) {
5837
cs.mu.Lock()
5938
defer cs.mu.Unlock()
6039

61-
cp, ok := cs.cps[id]
40+
reg, ok := cs.regs[id]
6241
if !ok {
6342
return nil, fmt.Errorf("unknown charge point: %s", id)
6443
}
65-
if cp == nil {
44+
if reg.cp == nil {
6645
return nil, fmt.Errorf("charge point not configured: %s", id)
6746
}
68-
return cp, nil
47+
return reg.cp, nil
6948
}
7049

50+
func (cs *CS) WithChargepointStatusByID(id string, fun func(status *core.StatusNotificationRequest)) {
51+
cs.mu.Lock()
52+
defer cs.mu.Unlock()
53+
54+
if reg, ok := cs.regs[id]; ok {
55+
reg.mu.RLock()
56+
if reg.status != nil {
57+
fun(reg.status)
58+
}
59+
reg.mu.RUnlock()
60+
}
61+
}
62+
63+
// RegisterChargepoint registers a charge point with the central system of returns an already registered charge point
7164
func (cs *CS) RegisterChargepoint(id string, newfun func() *CP, init func(*CP) error) (*CP, error) {
7265
cs.mu.Lock()
73-
cpmu, ok := cs.init[id]
74-
if !ok {
75-
cpmu = new(sync.Mutex)
76-
cs.init[id] = cpmu
66+
67+
// prepare shadow state
68+
reg, registered := cs.regs[id]
69+
if !registered {
70+
reg = new(registration)
71+
cs.regs[id] = reg
7772
}
78-
cs.mu.Unlock()
7973

8074
// serialise on chargepoint id
81-
cpmu.Lock()
82-
defer cpmu.Unlock()
75+
reg.setup.Lock()
76+
defer reg.setup.Unlock()
77+
78+
cp := reg.cp
79+
80+
cs.mu.Unlock()
81+
82+
// setup already completed?
83+
if cp != nil {
84+
// duplicate registration of id empty
85+
if id == "" {
86+
return nil, errors.New("cannot have >1 charge point with empty station id")
87+
}
8388

84-
// already registered?
85-
if cp, err := cs.ChargepointByID(id); err == nil {
8689
return cp, nil
8790
}
8891

89-
// first time- registration should not error
90-
cp := newfun()
91-
if err := cs.register(id, cp); err != nil {
92-
return nil, err
92+
// first time- create the charge point
93+
cp = newfun()
94+
95+
cs.mu.Lock()
96+
reg.cp = cp
97+
cs.mu.Unlock()
98+
99+
if registered {
100+
cp.connect(true)
93101
}
94102

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

103111
// check for configured charge point
104-
cp, ok := cs.cps[chargePoint.ID()]
112+
reg, ok := cs.regs[chargePoint.ID()]
105113
if ok {
106114
cs.log.DEBUG.Printf("charge point connected: %s", chargePoint.ID())
107115

108116
// trigger initial connection if charge point is already setup
109-
if cp != nil {
117+
if cp := reg.cp; cp != nil {
110118
cp.connect(true)
111119
}
112120

113121
return
114122
}
115123

116124
// check for configured anonymous charge point
117-
cp, ok = cs.cps[""]
118-
if ok && cp != nil {
125+
reg, ok = cs.regs[""]
126+
if ok && reg.cp != nil {
127+
cp := reg.cp
119128
cs.log.INFO.Printf("charge point connected, registering: %s", chargePoint.ID())
120129

121130
// update id
122131
cp.RegisterID(chargePoint.ID())
123132

124-
cs.cps[chargePoint.ID()] = cp
125-
delete(cs.cps, "")
133+
cs.regs[chargePoint.ID()].cp = cp
134+
delete(cs.regs, "")
126135

127136
cp.connect(true)
128137

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

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

139148
// ChargePointDisconnected implements ocpp16.ChargePointConnectionHandler

charger/ocpp/cs_core.go

+10
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ func (cs *CS) OnStatusNotification(id string, request *core.StatusNotificationRe
6666
return cp.OnStatusNotification(request)
6767
}
6868

69+
cs.mu.Lock()
70+
defer cs.mu.Unlock()
71+
72+
// cache status for future cp connection
73+
if reg, ok := cs.regs[id]; ok {
74+
reg.mu.Lock()
75+
reg.status = request
76+
reg.mu.Unlock()
77+
}
78+
6979
return new(core.StatusNotificationConfirmation), nil
7080
}
7181

charger/ocpp/instance.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ func Instance() *CS {
4040

4141
instance = &CS{
4242
log: log,
43-
cps: make(map[string]*CP),
44-
init: make(map[string]*sync.Mutex),
43+
regs: make(map[string]*registration),
4544
CentralSystem: cs,
4645
}
4746
instance.txnId.Store(time.Now().UTC().Unix())

0 commit comments

Comments
 (0)