146 lines
3.2 KiB
Go
146 lines
3.2 KiB
Go
package ocpp
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"sync/atomic"
|
|
|
|
"github.com/evcc-io/evcc/util"
|
|
ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6"
|
|
)
|
|
|
|
type CS struct {
|
|
mu sync.Mutex
|
|
log *util.Logger
|
|
ocpp16.CentralSystem
|
|
cps map[string]*CP
|
|
init map[string]*sync.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 {
|
|
cs.log.ERROR.Println(err)
|
|
}
|
|
}
|
|
|
|
func (cs *CS) ChargepointByID(id string) (*CP, error) {
|
|
cs.mu.Lock()
|
|
defer cs.mu.Unlock()
|
|
|
|
cp, ok := cs.cps[id]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown charge point: %s", id)
|
|
}
|
|
if cp == nil {
|
|
return nil, fmt.Errorf("charge point not configured: %s", id)
|
|
}
|
|
return cp, nil
|
|
}
|
|
|
|
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
|
|
}
|
|
cs.mu.Unlock()
|
|
|
|
// serialise on chargepoint id
|
|
cpmu.Lock()
|
|
defer cpmu.Unlock()
|
|
|
|
// 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
|
|
}
|
|
|
|
return cp, init(cp)
|
|
}
|
|
|
|
// NewChargePoint implements ocpp16.ChargePointConnectionHandler
|
|
func (cs *CS) NewChargePoint(chargePoint ocpp16.ChargePointConnection) {
|
|
cs.mu.Lock()
|
|
defer cs.mu.Unlock()
|
|
|
|
// check for configured charge point
|
|
cp, ok := cs.cps[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 {
|
|
cp.connect(true)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// check for configured anonymous charge point
|
|
cp, ok = cs.cps[""]
|
|
if ok && cp != nil {
|
|
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, "")
|
|
|
|
cp.connect(true)
|
|
|
|
return
|
|
}
|
|
|
|
cs.log.WARN.Printf("unknown charge point connected: %s", chargePoint.ID())
|
|
|
|
// register unknown charge point
|
|
// when charge point setup is complete, it will eventually be associated with the connected id
|
|
cs.cps[chargePoint.ID()] = nil
|
|
}
|
|
|
|
// ChargePointDisconnected implements ocpp16.ChargePointConnectionHandler
|
|
func (cs *CS) ChargePointDisconnected(chargePoint ocpp16.ChargePointConnection) {
|
|
cs.log.DEBUG.Printf("charge point disconnected: %s", chargePoint.ID())
|
|
|
|
if cp, err := cs.ChargepointByID(chargePoint.ID()); err == nil {
|
|
cp.connect(false)
|
|
}
|
|
}
|