157 lines
4.2 KiB
Go
157 lines
4.2 KiB
Go
package ocpp
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/lorenzodonini/ocpp-go/ocpp1.6/core"
|
|
"github.com/lorenzodonini/ocpp-go/ocpp1.6/types"
|
|
)
|
|
|
|
// timestampValid returns false if status timestamps are outdated
|
|
func (conn *Connector) timestampValid(t time.Time) bool {
|
|
// reject if expired
|
|
if conn.clock.Since(t) > Timeout {
|
|
return false
|
|
}
|
|
|
|
// assume having a timestamp is better than not
|
|
if conn.status.Timestamp == nil {
|
|
return true
|
|
}
|
|
|
|
// reject older values than we already have
|
|
return !t.Before(conn.status.Timestamp.Time)
|
|
}
|
|
|
|
func (conn *Connector) OnStatusNotification(request *core.StatusNotificationRequest) (*core.StatusNotificationConfirmation, error) {
|
|
conn.mu.Lock()
|
|
defer conn.mu.Unlock()
|
|
|
|
if conn.status == nil {
|
|
conn.status = request
|
|
close(conn.statusC) // signal initial status received
|
|
} else if request.Timestamp == nil || conn.timestampValid(request.Timestamp.Time) {
|
|
conn.status = request
|
|
} else {
|
|
conn.log.TRACE.Printf("ignoring status: %s < %s", request.Timestamp.Time, conn.status.Timestamp)
|
|
}
|
|
|
|
if conn.isWaitingForAuth() {
|
|
if conn.remoteIdTag != "" {
|
|
conn.RemoteStartTransactionRequest(conn.remoteIdTag)
|
|
} else {
|
|
conn.log.DEBUG.Printf("waiting for local authentication")
|
|
}
|
|
}
|
|
|
|
return new(core.StatusNotificationConfirmation), nil
|
|
}
|
|
|
|
func getSampleKey(s types.SampledValue) types.Measurand {
|
|
if s.Phase != "" {
|
|
return s.Measurand + types.Measurand("."+string(s.Phase))
|
|
}
|
|
|
|
return s.Measurand
|
|
}
|
|
|
|
func (conn *Connector) OnMeterValues(request *core.MeterValuesRequest) (*core.MeterValuesConfirmation, error) {
|
|
conn.mu.Lock()
|
|
defer conn.mu.Unlock()
|
|
|
|
if request.TransactionId != nil && *request.TransactionId > 0 &&
|
|
conn.txnId == 0 && conn.status != nil &&
|
|
(conn.status.Status == core.ChargePointStatusCharging ||
|
|
conn.status.Status == core.ChargePointStatusSuspendedEV ||
|
|
conn.status.Status == core.ChargePointStatusSuspendedEVSE) {
|
|
conn.log.DEBUG.Printf("recovered transaction: %d", *request.TransactionId)
|
|
conn.txnId = *request.TransactionId
|
|
}
|
|
|
|
for _, meterValue := range sortByAge(request.MeterValue) {
|
|
if meterValue.Timestamp == nil {
|
|
// this should be done before the sorting, but lets assume either all or no sample has a timestamp
|
|
meterValue.Timestamp = types.NewDateTime(conn.clock.Now())
|
|
}
|
|
|
|
// ignore old meter value requests
|
|
if !meterValue.Timestamp.Time.Before(conn.meterUpdated) {
|
|
for _, sample := range meterValue.SampledValue {
|
|
sample.Value = strings.TrimSpace(sample.Value)
|
|
conn.measurements[getSampleKey(sample)] = sample
|
|
conn.meterUpdated = meterValue.Timestamp.Time
|
|
}
|
|
}
|
|
}
|
|
|
|
return new(core.MeterValuesConfirmation), nil
|
|
}
|
|
|
|
func (conn *Connector) OnStartTransaction(request *core.StartTransactionRequest) (*core.StartTransactionConfirmation, error) {
|
|
conn.mu.Lock()
|
|
defer conn.mu.Unlock()
|
|
|
|
conn.txnId = int(instance.txnId.Add(1))
|
|
conn.idTag = request.IdTag
|
|
|
|
res := &core.StartTransactionConfirmation{
|
|
IdTagInfo: &types.IdTagInfo{
|
|
Status: types.AuthorizationStatusAccepted,
|
|
},
|
|
TransactionId: conn.txnId,
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (conn *Connector) assumeMeterStopped() {
|
|
conn.meterUpdated = conn.clock.Now()
|
|
|
|
if _, ok := conn.measurements[types.MeasurandPowerActiveImport]; ok {
|
|
conn.measurements[types.MeasurandPowerActiveImport] = types.SampledValue{
|
|
Value: "0",
|
|
Unit: types.UnitOfMeasureW,
|
|
}
|
|
}
|
|
|
|
for phase := 1; phase <= 3; phase++ {
|
|
// phase powers
|
|
for _, suffix := range []types.Measurand{"", "-N"} {
|
|
key := getPhaseKey(types.MeasurandPowerActiveImport, phase) + suffix
|
|
if _, ok := conn.measurements[key]; ok {
|
|
conn.measurements[key] = types.SampledValue{
|
|
Value: "0",
|
|
Unit: types.UnitOfMeasureW,
|
|
}
|
|
}
|
|
}
|
|
|
|
// phase currents
|
|
key := getPhaseKey(types.MeasurandCurrentImport, phase)
|
|
if _, ok := conn.measurements[key]; ok {
|
|
conn.measurements[key] = types.SampledValue{
|
|
Value: "0",
|
|
Unit: types.UnitOfMeasureA,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (conn *Connector) OnStopTransaction(request *core.StopTransactionRequest) (*core.StopTransactionConfirmation, error) {
|
|
conn.mu.Lock()
|
|
defer conn.mu.Unlock()
|
|
|
|
conn.txnId = 0
|
|
conn.idTag = ""
|
|
|
|
res := &core.StopTransactionConfirmation{
|
|
IdTagInfo: &types.IdTagInfo{
|
|
Status: types.AuthorizationStatusAccepted, // accept
|
|
},
|
|
}
|
|
|
|
conn.assumeMeterStopped()
|
|
|
|
return res, nil
|
|
}
|