diff --git a/charger/ocpp.go b/charger/ocpp.go index 24a853346..61ce6afc7 100644 --- a/charger/ocpp.go +++ b/charger/ocpp.go @@ -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 diff --git a/charger/ocpp/cp.go b/charger/ocpp/cp.go index 6d38b9d15..d1df8fbdc 100644 --- a/charger/ocpp/cp.go +++ b/charger/ocpp/cp.go @@ -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()