Ocpp: request list of required configuration keys (#4687)

This commit is contained in:
andig 2022-10-02 15:15:01 +02:00 • committed by GitHub
parent edb635faa3
commit 04d198a4e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 178 deletions

View file

@ -22,14 +22,13 @@ const statusTimeout = 30 * time.Second
// OCPP charger implementation
type OCPP struct {
log *util.Logger
cp *ocpp.CP
connector int
idtag string
phases int
current float64
meterValuesSample string
phaseSwitchingSupported bool
log *util.Logger
cp *ocpp.CP
connector int
idtag string
phases int
current float64
meterValuesSample string
}
func init() {
@ -123,69 +122,68 @@ func NewOCPP(id string, connector int, idtag string, meterValues string, meterIn
var (
rc = make(chan error, 1)
options []core.ConfigurationKey
meterSampleInterval time.Duration
)
keys := []string{
ocpp.KeyNumberOfConnectors,
ocpp.KeyMeterValuesSampledData,
ocpp.KeyMeterValueSampleInterval,
ocpp.KeyChargeProfileMaxStackLevel,
ocpp.KeyChargingScheduleAllowedChargingRateUnit,
ocpp.KeyChargingScheduleMaxPeriods,
ocpp.KeyConnectorSwitch3to1PhaseSupported,
ocpp.KeyMaxChargingProfilesInstalled,
}
// configured id may be empty, use registered id below
err := ocpp.Instance().GetConfiguration(cp.ID(), func(resp *core.GetConfigurationConfirmation, err error) {
if err != nil {
rc <- err
return
}
if err == nil {
// sort config options for printing
sort.Slice(resp.ConfigurationKey, func(i, j int) bool {
return resp.ConfigurationKey[i].Key < resp.ConfigurationKey[j].Key
})
options = resp.ConfigurationKey
rw := map[bool]string{false: "r/w", true: "r/o"}
// sort config options for printing
sort.Slice(options, func(i, j int) bool {
return options[i].Key < options[j].Key
})
rw := map[bool]string{false: "r/w", true: "r/o"}
for _, opt := range options {
c.log.TRACE.Printf("%s (%s): %s", opt.Key, rw[opt.Readonly], *opt.Value)
switch opt.Key {
case ocpp.KeyMeterValuesSampledData:
c.meterValuesSample = *opt.Value
case ocpp.KeyMeterValueSampleInterval:
meterValuesSampleInterval, err := strconv.Atoi(*opt.Value)
if err != nil {
rc <- err
return
}
meterSampleInterval = time.Duration(meterValuesSampleInterval) * time.Second
case string(ocpp.KeyConnectorSwitch3to1PhaseSupported):
// Detection of 1 phase charging/switching support
b, err := strconv.ParseBool(*opt.Value)
if err != nil {
rc <- err
return
for _, opt := range resp.ConfigurationKey {
if opt.Value == nil {
c.log.ERROR.Printf("%s (%s): %s", opt.Key, rw[opt.Readonly], "nil")
continue
}
c.phaseSwitchingSupported = b
c.log.TRACE.Printf("%s (%s): %s", opt.Key, rw[opt.Readonly], *opt.Value)
switch opt.Key {
case ocpp.KeyNumberOfConnectors:
var val int
if val, err = strconv.Atoi(*opt.Value); err == nil && c.connector > val {
err = fmt.Errorf("connector %d exceeds max available connectors: %d", c.connector, val)
}
case ocpp.KeyMeterValuesSampledData:
c.meterValuesSample = *opt.Value
case ocpp.KeyMeterValueSampleInterval:
var val int
if val, err = strconv.Atoi(*opt.Value); err == nil {
meterSampleInterval = time.Duration(val) * time.Second
}
}
if err != nil {
break
}
}
}
rc <- nil
}, []string{})
rc <- err
}, keys)
if err := c.wait(err, rc); err != nil {
return nil, err
}
if err := cp.DetectCapabilities(options); err != nil {
return nil, err
}
// check supported connectors of charge point
if supported := cp.GetNumberOfSupportedConnectors(); c.connector > supported {
return nil, fmt.Errorf("configured connector is not available, max available connectors %d", supported)
}
if meterValues != "" {
if err := c.configure(ocpp.KeyMeterValuesSampledData, meterValues); err != nil {
return nil, err

View file

@ -3,7 +3,6 @@ package ocpp
import (
"fmt"
"strconv"
"strings"
"sync"
"time"
@ -13,38 +12,22 @@ import (
"github.com/lorenzodonini/ocpp-go/ocpp1.6/types"
)
// Meter Profile Key
const (
// Core profile keys
KeyNumberOfConnectors = "NumberOfConnectors"
// Meter profile keys
KeyMeterValuesSampledData = "MeterValuesSampledData"
KeyMeterValueSampleInterval = "MeterValueSampleInterval"
// Smart Charging profile keys
KeyChargeProfileMaxStackLevel = "ChargeProfileMaxStackLevel"
KeyChargingScheduleAllowedChargingRateUnit = "ChargingScheduleAllowedChargingRateUnit"
KeyChargingScheduleMaxPeriods = "ChargingScheduleMaxPeriods"
KeyConnectorSwitch3to1PhaseSupported = "ConnectorSwitch3to1PhaseSupported"
KeyMaxChargingProfilesInstalled = "MaxChargingProfilesInstalled"
)
type SmartchargingChargeProfileKey string
// Smart Charging Profile Key
const (
KeyChargeProfileMaxStackLevel SmartchargingChargeProfileKey = "ChargeProfileMaxStackLevel"
KeyChargingScheduleAllowedChargingRateUnit SmartchargingChargeProfileKey = "ChargingScheduleAllowedChargingRateUnit"
KeyChargingScheduleMaxPeriods SmartchargingChargeProfileKey = "ChargingScheduleMaxPeriods"
KeyConnectorSwitch3to1PhaseSupported SmartchargingChargeProfileKey = "ConnectorSwitch3to1PhaseSupported"
KeyMaxChargingProfilesInstalled SmartchargingChargeProfileKey = "MaxChargingProfilesInstalled"
)
type smartChargingProfile struct {
// Max StackLevel of a ChargingProfile. The number defined also indicates the max allowed
// number of installed charging scheduls per Charging Profile Purpose
ChargeProfileMaxStackLevel int
// A list of supported quantities for use in a ChargingSchedule.
// Allowed values: 'Current' and 'Power'
ChargingScheduleAllowedChargingRateUnit []string
// Maximum number of periods that may be defined per ChargingSchedule
ChargingScheduleMaxPeriods int
// Defines if this Charge Point support switching from 3 to 1 phase during a charging session.
ConnectorSwitch3to1PhaseSupported bool
// Maximum number of Charging profiles instsalled at a time.
MaxChargingProfilesInstalled int
}
type CP struct {
mu sync.Mutex
log *util.Logger
@ -60,9 +43,6 @@ type CP struct {
meterUpdated time.Time
measurements map[string]types.SampledValue
supportedNumberOfConnectors int
smartChargingCapabilities smartChargingProfile
txnCount int // change initial value to the last known global transaction. Needs persistence
txnId int
}
@ -148,99 +128,6 @@ func (cp *CP) WatchDog(timeout time.Duration) {
}()
}
func (cp *CP) DetectCapabilities(opts []core.ConfigurationKey) error {
options := make(map[string]core.ConfigurationKey)
for _, opt := range opts {
options[opt.Key] = opt
}
var err error
if cp.supportedNumberOfConnectors, err = parseIntOption("NumberOfConnectors", options); err != nil {
return err
}
if cp.smartChargingCapabilities, err = detectSmartChargingCapabilities(options); err != nil {
return err
}
return nil
}
func (cp *CP) GetNumberOfSupportedConnectors() int {
return cp.supportedNumberOfConnectors
}
func detectSmartChargingCapabilities(options map[string]core.ConfigurationKey) (smartChargingProfile, error) {
var profile smartChargingProfile
{ // required
val, err := parseIntOption(KeyChargeProfileMaxStackLevel, options)
if err != nil {
return profile, err
}
profile.ChargeProfileMaxStackLevel = val
}
{ // required
val, err := parseIntOption(KeyChargingScheduleMaxPeriods, options)
if err != nil {
return profile, err
}
profile.ChargingScheduleMaxPeriods = val
}
{ // required
val, err := parseIntOption(KeyMaxChargingProfilesInstalled, options)
if err != nil {
return profile, err
}
profile.MaxChargingProfilesInstalled = val
}
{ // required
opt, found := options[string(KeyChargingScheduleAllowedChargingRateUnit)]
if !found || opt.Value == nil {
return profile, fmt.Errorf("smart charging key '%s' not found", KeyChargingScheduleAllowedChargingRateUnit)
}
vals := strings.Split(*opt.Value, ",")
profile.ChargingScheduleAllowedChargingRateUnit = append(profile.ChargingScheduleAllowedChargingRateUnit, vals...)
}
{ // optional
var supported bool
if opt, ok := options[string(KeyConnectorSwitch3to1PhaseSupported)]; ok {
var err error
supported, err = strconv.ParseBool(*opt.Value)
if err != nil {
return profile, fmt.Errorf("invalid value for key: %s", opt.Key)
}
}
profile.ConnectorSwitch3to1PhaseSupported = supported
}
return profile, nil
}
func parseIntOption(key SmartchargingChargeProfileKey, options map[string]core.ConfigurationKey) (int, error) {
opt, found := options[string(key)]
if !found || opt.Value == nil {
return 0, fmt.Errorf("smart charging key '%s' not found", key)
}
val, err := strconv.Atoi(*opt.Value)
if err != nil {
return 0, fmt.Errorf("invalid value for key: %s", key)
}
return val, nil
}
// TransactionID returns the current transaction id
func (cp *CP) TransactionID() int {
cp.mu.Lock()