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

Ocpp: cache and re-use initial status (3rd attempt) #16908

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions charger/ocpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ func NewOCPP(id string, connector int, idTag string,
) (*OCPP, error) {
log := util.NewLogger(fmt.Sprintf("%s-%d", lo.CoalesceOrEmpty(id, "ocpp"), connector))

log.DEBUG.Printf("registering %s:%d", id, connector)

cp, err := ocpp.Instance().RegisterChargepoint(id,
func() *ocpp.CP {
return ocpp.NewChargePoint(log, id)
Expand All @@ -164,6 +166,8 @@ func NewOCPP(id string, connector int, idTag string,
return nil, err
}

log.DEBUG.Printf("connected %s:%d", id, connector)

if cp.NumberOfConnectors > 0 && connector > cp.NumberOfConnectors {
return nil, fmt.Errorf("invalid connector: %d", connector)
}
Expand Down
25 changes: 23 additions & 2 deletions charger/ocpp/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,30 @@ func NewConnector(log *util.Logger, id int, cp *CP, idTag string) (*Connector, e
remoteIdTag: idTag,
}

err := cp.registerConnector(id, conn)
if err := cp.registerConnector(id, conn); err != nil {
return nil, err
}

// trigger status for all connectors
if cp.HasRemoteTriggerFeature {
var ok bool

// apply cached status if available
instance.WithConnectorStatus(cp.ID(), 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)
}
}
}

return conn, err
return conn, nil
}

func (conn *Connector) TestClock(clock clock.Clock) {
Expand Down
19 changes: 0 additions & 19 deletions charger/ocpp/cp_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,25 +156,6 @@ func (cp *CP) Setup(meterValues string, meterInterval time.Duration) error {
cp.log.DEBUG.Printf("failed configuring %s: %v", KeyWebSocketPingInterval, err)
}

// trigger status for all connectors
if cp.HasRemoteTriggerFeature {
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)
}
}
}

return nil
}

Expand Down
20 changes: 12 additions & 8 deletions charger/ocpp/cs.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ import (

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
setup sync.RWMutex // serialises chargepoint setup
cp *CP // guarded by setup and CS mutexes
status map[int]*core.StatusNotificationRequest // guarded by mu mutex
}

func newRegistration() *registration {
return &registration{status: make(map[int]*core.StatusNotificationRequest)}
}

type CS struct {
Expand Down Expand Up @@ -47,14 +51,14 @@ func (cs *CS) ChargepointByID(id string) (*CP, error) {
return reg.cp, nil
}

func (cs *CS) WithChargepointStatusByID(id string, fun func(status *core.StatusNotificationRequest)) {
func (cs *CS) WithConnectorStatus(id string, connector int, 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)
if status, ok := reg.status[connector]; ok {
fun(status)
}
reg.mu.RUnlock()
}
Expand All @@ -67,7 +71,7 @@ func (cs *CS) RegisterChargepoint(id string, newfun func() *CP, init func(*CP) e
// prepare shadow state
reg, registered := cs.regs[id]
if !registered {
reg = new(registration)
reg = newRegistration()
cs.regs[id] = reg
}

Expand Down Expand Up @@ -142,7 +146,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.regs[chargePoint.ID()] = new(registration)
cs.regs[chargePoint.ID()] = newRegistration()
}

// ChargePointDisconnected implements ocpp16.ChargePointConnectionHandler
Expand Down
4 changes: 2 additions & 2 deletions charger/ocpp/cs_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ func (cs *CS) OnStatusNotification(id string, request *core.StatusNotificationRe
defer cs.mu.Unlock()

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

Expand Down
4 changes: 2 additions & 2 deletions charger/ocpp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ type ocppTestSuite struct {
clock *clock.Mock
}

func (suite *ocppTestSuite) SetupSuite() {
func (suite *ocppTestSuite) SetupTest() {
ocpp.Timeout = 5 * time.Second
ocppj.SetLogger(new(ocppLogger))

// setup cs so we can overwrite logger afterwards
_ = ocpp.Instance()
ocppj.SetLogger(&ocppLogger{suite.T()})

suite.clock = clock.NewMock()
suite.NotNil(ocpp.Instance())
Expand Down
21 changes: 9 additions & 12 deletions charger/ocpp_test_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,24 @@ package charger

import (
"fmt"
"testing"
"time"
)

type ocppLogger struct {
t *testing.T
}
type ocppLogger struct{}

func print(t *testing.T, s string) {
t.Log((time.Now().Format(time.DateTime)), s)
func print(s string) {
fmt.Println(time.Now().Format(time.DateTime), s)
}

func (l *ocppLogger) Debug(args ...interface{}) { print(l.t, fmt.Sprint(args...)) }
func (l *ocppLogger) Debug(args ...interface{}) { print(fmt.Sprint(args...)) }
func (l *ocppLogger) Debugf(format string, args ...interface{}) {
print(l.t, fmt.Sprintf(format, args...))
print(fmt.Sprintf(format, args...))
}
func (l *ocppLogger) Info(args ...interface{}) { print(l.t, fmt.Sprint(args...)) }
func (l *ocppLogger) Info(args ...interface{}) { print(fmt.Sprint(args...)) }
func (l *ocppLogger) Infof(format string, args ...interface{}) {
print(l.t, fmt.Sprintf(format, args...))
print(fmt.Sprintf(format, args...))
}
func (l *ocppLogger) Error(args ...interface{}) { print(l.t, fmt.Sprint(args...)) }
func (l *ocppLogger) Error(args ...interface{}) { print(fmt.Sprint(args...)) }
func (l *ocppLogger) Errorf(format string, args ...interface{}) {
print(l.t, fmt.Sprintf(format, args...))
print(fmt.Sprintf(format, args...))
}
Loading