diff --git a/charger/ocpp.go b/charger/ocpp.go index 0739d49441..516fd6a707 100644 --- a/charger/ocpp.go +++ b/charger/ocpp.go @@ -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) @@ -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) } diff --git a/charger/ocpp/connector.go b/charger/ocpp/connector.go index 409af091c0..3f24bb60b5 100644 --- a/charger/ocpp/connector.go +++ b/charger/ocpp/connector.go @@ -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 + + 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 + } + }) + + if cp.HasRemoteTriggerFeature { + // 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) { @@ -89,7 +110,9 @@ func (conn *Connector) WatchDog(timeout time.Duration) { conn.mu.Unlock() if update { - conn.TriggerMessageRequest(core.MeterValuesFeatureName) + if conn.cp.HasRemoteTriggerFeature { + conn.TriggerMessageRequest(core.MeterValuesFeatureName) + } } } } @@ -104,7 +127,9 @@ func (conn *Connector) Initialized() error { return nil case <-trigger: // try to trigger StatusNotification again as last resort - conn.TriggerMessageRequest(core.StatusNotificationFeatureName) + if conn.cp.HasRemoteTriggerFeature { + conn.TriggerMessageRequest(core.StatusNotificationFeatureName) + } case <-timeout: return api.ErrTimeout diff --git a/charger/ocpp/connector_test.go b/charger/ocpp/connector_test.go index ba0d80918e..95e7b9f275 100644 --- a/charger/ocpp/connector_test.go +++ b/charger/ocpp/connector_test.go @@ -23,6 +23,8 @@ type connTestSuite struct { } func (suite *connTestSuite) SetupTest() { + // setup instance + Instance() suite.cp = NewChargePoint(util.NewLogger("foo"), "abc") suite.conn, _ = NewConnector(util.NewLogger("foo"), 1, suite.cp, "") diff --git a/charger/ocpp/cp_setup.go b/charger/ocpp/cp_setup.go index afce47c88a..1dfa35da89 100644 --- a/charger/ocpp/cp_setup.go +++ b/charger/ocpp/cp_setup.go @@ -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 } diff --git a/charger/ocpp/cs.go b/charger/ocpp/cs.go index 93fe49a3e5..677e14ddf5 100644 --- a/charger/ocpp/cs.go +++ b/charger/ocpp/cs.go @@ -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 ®istration{status: make(map[int]*core.StatusNotificationRequest)} } type CS struct { @@ -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() } @@ -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 } @@ -129,8 +133,7 @@ func (cs *CS) NewChargePoint(chargePoint ocpp16.ChargePointConnection) { // update id cp.RegisterID(chargePoint.ID()) - - cs.regs[chargePoint.ID()].cp = cp + cs.regs[chargePoint.ID()] = reg delete(cs.regs, "") cp.connect(true) @@ -142,7 +145,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 diff --git a/charger/ocpp/cs_core.go b/charger/ocpp/cs_core.go index 5c0aa98b19..d56e7e325c 100644 --- a/charger/ocpp/cs_core.go +++ b/charger/ocpp/cs_core.go @@ -62,19 +62,18 @@ func (cs *CS) OnMeterValues(id string, request *core.MeterValuesRequest) (*core. } func (cs *CS) OnStatusNotification(id string, request *core.StatusNotificationRequest) (*core.StatusNotificationConfirmation, error) { - if cp, err := cs.ChargepointByID(id); err == nil { - return cp.OnStatusNotification(request) - } - cs.mu.Lock() - 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() } + cs.mu.Unlock() + + if cp, err := cs.ChargepointByID(id); err == nil { + return cp.OnStatusNotification(request) + } return new(core.StatusNotificationConfirmation), nil } diff --git a/charger/ocpp_test.go b/charger/ocpp_test.go index 2339c388f9..e425a2d419 100644 --- a/charger/ocpp_test.go +++ b/charger/ocpp_test.go @@ -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()) diff --git a/charger/ocpp_test_logger.go b/charger/ocpp_test_logger.go index ab6618a99b..b2dc7cb536 100644 --- a/charger/ocpp_test_logger.go +++ b/charger/ocpp_test_logger.go @@ -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...)) }