Ocpp: add fallback methods to Enabled() (#15242)
This commit is contained in:
parent
7453d1219e
commit
77a3cf2861
2 changed files with 123 additions and 51 deletions
103
charger/ocpp.go
103
charger/ocpp.go
|
|
@ -26,6 +26,7 @@ type OCPP struct {
|
|||
idtag string
|
||||
phases int
|
||||
current float64
|
||||
enabled bool
|
||||
meterValuesSample string
|
||||
timeout time.Duration
|
||||
phaseSwitching bool
|
||||
|
|
@ -349,37 +350,95 @@ func (c *OCPP) wait(err error, rc chan error) error {
|
|||
|
||||
// Status implements the api.Charger interface
|
||||
func (c *OCPP) Status() (api.ChargeStatus, error) {
|
||||
if c.remoteStart {
|
||||
needtxn, err := c.conn.NeedsTransaction()
|
||||
if err != nil {
|
||||
return api.StatusNone, err
|
||||
}
|
||||
status, err := c.conn.Status()
|
||||
if err != nil {
|
||||
return api.StatusNone, err
|
||||
}
|
||||
|
||||
if needtxn {
|
||||
if c.conn.NeedsAuthentication() {
|
||||
if c.remoteStart {
|
||||
// lock the cable by starting remote transaction after vehicle connected
|
||||
if err := c.initTransaction(); err != nil {
|
||||
return api.StatusNone, err
|
||||
c.log.WARN.Printf("failed to start remote transaction: %v", err)
|
||||
}
|
||||
} else {
|
||||
// TODO: bring this status to UI
|
||||
c.log.WARN.Printf("waiting for local authentication")
|
||||
}
|
||||
}
|
||||
|
||||
return c.conn.Status()
|
||||
switch status {
|
||||
case
|
||||
core.ChargePointStatusAvailable, // "Available"
|
||||
core.ChargePointStatusUnavailable: // "Unavailable"
|
||||
return api.StatusA, nil
|
||||
case
|
||||
core.ChargePointStatusPreparing, // "Preparing"
|
||||
core.ChargePointStatusSuspendedEVSE, // "SuspendedEVSE"
|
||||
core.ChargePointStatusSuspendedEV, // "SuspendedEV"
|
||||
core.ChargePointStatusFinishing: // "Finishing"
|
||||
return api.StatusB, nil
|
||||
case
|
||||
core.ChargePointStatusCharging: // "Charging"
|
||||
return api.StatusC, nil
|
||||
case
|
||||
core.ChargePointStatusReserved, // "Reserved"
|
||||
core.ChargePointStatusFaulted: // "Faulted"
|
||||
return api.StatusF, fmt.Errorf("chargepoint status: %s", status)
|
||||
default:
|
||||
return api.StatusNone, fmt.Errorf("invalid chargepoint status: %s", status)
|
||||
}
|
||||
}
|
||||
|
||||
// Enabled implements the api.Charger interface
|
||||
func (c *OCPP) Enabled() (bool, error) {
|
||||
current, err := c.getCurrent()
|
||||
if s, err := c.conn.Status(); err == nil {
|
||||
switch s {
|
||||
case
|
||||
core.ChargePointStatusSuspendedEVSE:
|
||||
return false, nil
|
||||
case
|
||||
core.ChargePointStatusCharging,
|
||||
core.ChargePointStatusSuspendedEV:
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return current > 0, err
|
||||
// fallback to the "offered" measurands
|
||||
if c.hasMeasurement(types.MeasurandCurrentOffered) {
|
||||
if v, err := c.getMaxCurrent(); err == nil {
|
||||
return v > 0, nil
|
||||
}
|
||||
}
|
||||
if c.hasMeasurement(types.MeasurandPowerOffered) {
|
||||
if v, err := c.getMaxPower(); err == nil {
|
||||
return v > 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to querying the active charging profile schedule limit
|
||||
if v, err := c.getScheduleLimit(); err == nil {
|
||||
return v > 0, nil
|
||||
}
|
||||
|
||||
// fallback to cached value as last resort
|
||||
return c.enabled, nil
|
||||
}
|
||||
|
||||
// Enable implements the api.Charger interface
|
||||
func (c *OCPP) Enable(enable bool) error {
|
||||
var current float64
|
||||
if enable {
|
||||
current = c.current
|
||||
}
|
||||
|
||||
return c.setCurrent(current)
|
||||
err := c.setCurrent(current)
|
||||
if err == nil {
|
||||
// cache enabled state as last fallback option
|
||||
c.enabled = enable
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *OCPP) initTransaction() error {
|
||||
|
|
@ -421,15 +480,12 @@ func (c *OCPP) setCurrent(current float64) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// getCurrent returns the internal current offered by the chargepoint
|
||||
func (c *OCPP) getCurrent() (float64, error) {
|
||||
var current float64
|
||||
// getScheduleLimit queries the current or power limit the charge point is currently set to offer
|
||||
func (c *OCPP) getScheduleLimit() (float64, error) {
|
||||
const duration = 60 // duration of requested schedule in seconds
|
||||
|
||||
if c.hasMeasurement(types.MeasurandCurrentOffered) {
|
||||
return c.getMaxCurrent()
|
||||
}
|
||||
var limit float64
|
||||
|
||||
// fallback to GetCompositeSchedule request
|
||||
rc := make(chan error, 1)
|
||||
err := ocpp.Instance().GetCompositeSchedule(c.conn.ChargePoint().ID(), func(resp *smartcharging.GetCompositeScheduleConfirmation, err error) {
|
||||
if err == nil && resp != nil && resp.Status != smartcharging.GetCompositeScheduleStatusAccepted {
|
||||
|
|
@ -438,18 +494,19 @@ func (c *OCPP) getCurrent() (float64, error) {
|
|||
|
||||
if err == nil {
|
||||
if resp.ChargingSchedule != nil && len(resp.ChargingSchedule.ChargingSchedulePeriod) > 0 {
|
||||
current = resp.ChargingSchedule.ChargingSchedulePeriod[0].Limit
|
||||
// return first (current) period limit
|
||||
limit = resp.ChargingSchedule.ChargingSchedulePeriod[0].Limit
|
||||
} else {
|
||||
err = fmt.Errorf("invalid ChargingSchedule")
|
||||
}
|
||||
}
|
||||
|
||||
rc <- err
|
||||
}, c.conn.ID(), 1)
|
||||
}, c.conn.ID(), duration)
|
||||
|
||||
err = c.wait(err, rc)
|
||||
|
||||
return current, err
|
||||
return limit, err
|
||||
}
|
||||
|
||||
// createTxDefaultChargingProfile returns a TxDefaultChargingProfile with given current
|
||||
|
|
@ -506,6 +563,10 @@ func (c *OCPP) getMaxCurrent() (float64, error) {
|
|||
return c.conn.GetMaxCurrent()
|
||||
}
|
||||
|
||||
func (c *OCPP) getMaxPower() (float64, error) {
|
||||
return c.conn.GetMaxPower()
|
||||
}
|
||||
|
||||
// currentPower implements the api.Meter interface
|
||||
func (c *OCPP) currentPower() (float64, error) {
|
||||
return c.conn.CurrentPower()
|
||||
|
|
|
|||
|
|
@ -128,52 +128,36 @@ func (conn *Connector) TransactionID() (int, error) {
|
|||
return conn.txnId, nil
|
||||
}
|
||||
|
||||
func (conn *Connector) Status() (api.ChargeStatus, error) {
|
||||
// Status returns the unmapped charge point status
|
||||
func (conn *Connector) Status() (core.ChargePointStatus, error) {
|
||||
if !conn.cp.Connected() {
|
||||
return "", api.ErrTimeout
|
||||
}
|
||||
|
||||
conn.mu.Lock()
|
||||
defer conn.mu.Unlock()
|
||||
|
||||
res := api.StatusNone
|
||||
|
||||
if !conn.cp.Connected() {
|
||||
return res, api.ErrTimeout
|
||||
if conn.status == nil {
|
||||
return core.ChargePointStatusUnavailable, nil
|
||||
}
|
||||
|
||||
if conn.status.ErrorCode != core.NoError {
|
||||
return res, fmt.Errorf("%s: %s", conn.status.ErrorCode, conn.status.Info)
|
||||
return "", fmt.Errorf("%s: %s", conn.status.ErrorCode, conn.status.Info)
|
||||
}
|
||||
|
||||
switch conn.status.Status {
|
||||
case core.ChargePointStatusAvailable, // "Available"
|
||||
core.ChargePointStatusUnavailable: // "Unavailable"
|
||||
res = api.StatusA
|
||||
case
|
||||
core.ChargePointStatusPreparing, // "Preparing"
|
||||
core.ChargePointStatusSuspendedEVSE, // "SuspendedEVSE"
|
||||
core.ChargePointStatusSuspendedEV, // "SuspendedEV"
|
||||
core.ChargePointStatusFinishing: // "Finishing"
|
||||
res = api.StatusB
|
||||
case core.ChargePointStatusCharging: // "Charging"
|
||||
res = api.StatusC
|
||||
case core.ChargePointStatusReserved, // "Reserved"
|
||||
core.ChargePointStatusFaulted: // "Faulted"
|
||||
return api.StatusF, fmt.Errorf("chargepoint status: %s", conn.status.ErrorCode)
|
||||
default:
|
||||
return api.StatusNone, fmt.Errorf("invalid chargepoint status: %s", conn.status.Status)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return conn.status.Status, nil
|
||||
}
|
||||
|
||||
// NeedsTransaction checks if an initial RemoteStart of a transaction is required
|
||||
func (conn *Connector) NeedsTransaction() (bool, error) {
|
||||
// NeedsAuthentication checks if local authentication or an initial RemoteStartTransaction is required
|
||||
func (conn *Connector) NeedsAuthentication() bool {
|
||||
if !conn.cp.Connected() {
|
||||
return false, api.ErrTimeout
|
||||
return false
|
||||
}
|
||||
|
||||
conn.mu.Lock()
|
||||
defer conn.mu.Unlock()
|
||||
|
||||
return conn.txnId == 0 && conn.status.Status == core.ChargePointStatusPreparing, nil
|
||||
return conn.status != nil && conn.txnId == 0 && conn.status.Status == core.ChargePointStatusPreparing
|
||||
}
|
||||
|
||||
// isMeterTimeout checks if meter values are outdated.
|
||||
|
|
@ -182,6 +166,9 @@ func (conn *Connector) isMeterTimeout() bool {
|
|||
return conn.timeout > 0 && conn.clock.Since(conn.meterUpdated) > conn.timeout
|
||||
}
|
||||
|
||||
var _ api.CurrentGetter = (*Connector)(nil)
|
||||
|
||||
// GetMaxCurrent returns the maximum phase current the charge point is set to offer
|
||||
func (conn *Connector) GetMaxCurrent() (float64, error) {
|
||||
if !conn.cp.Connected() {
|
||||
return 0, api.ErrTimeout
|
||||
|
|
@ -203,6 +190,30 @@ func (conn *Connector) GetMaxCurrent() (float64, error) {
|
|||
return 0, api.ErrNotAvailable
|
||||
}
|
||||
|
||||
// GetMaxPower returns the maximum power the charge point is set to offer
|
||||
func (conn *Connector) GetMaxPower() (float64, error) {
|
||||
if !conn.cp.Connected() {
|
||||
return 0, api.ErrTimeout
|
||||
}
|
||||
|
||||
conn.mu.Lock()
|
||||
defer conn.mu.Unlock()
|
||||
|
||||
// fallthrough for last value on timeout when no transaction is running
|
||||
if conn.txnId != 0 && conn.isMeterTimeout() {
|
||||
return 0, api.ErrTimeout
|
||||
}
|
||||
|
||||
if m, ok := conn.measurements[types.MeasurandPowerOffered]; ok {
|
||||
f, err := strconv.ParseFloat(m.Value, 64)
|
||||
return scale(f, m.Unit), err
|
||||
}
|
||||
|
||||
return 0, api.ErrNotAvailable
|
||||
}
|
||||
|
||||
var _ api.Meter = (*Connector)(nil)
|
||||
|
||||
func (conn *Connector) CurrentPower() (float64, error) {
|
||||
if !conn.cp.Connected() {
|
||||
return 0, api.ErrTimeout
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue