diff --git a/.travis.yml b/.travis.yml index 85a981c3..2d09cb56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,16 +20,16 @@ jobs: - go test ./... - stage: publish_latest script: - - docker build -t ldonini/ocpp1.6-central-system:latest -f example/cs/Dockerfile . - - docker build -t ldonini/ocpp1.6-charge-point:latest -f example/cp/Dockerfile . + - docker build -t ldonini/ocpp1.6-central-system:latest -f example/cs1.6/Dockerfile . + - docker build -t ldonini/ocpp1.6-charge-point:latest -f example/cp1.6/Dockerfile . - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - docker push ldonini/ocpp1.6-central-system:latest - docker push ldonini/ocpp1.6-charge-point:latest if: branch == master - stage: release script: - - docker build -t "ldonini/ocpp1.6-central-system:$TRAVIS_TAG" -f example/cs/Dockerfile . - - docker build -t "ldonini/ocpp1.6-charge-point:$TRAVIS_TAG" -f example/cp/Dockerfile . + - docker build -t "ldonini/ocpp1.6-central-system:$TRAVIS_TAG" -f example/cs1.6/Dockerfile . + - docker build -t "ldonini/ocpp1.6-charge-point:$TRAVIS_TAG" -f example/cp1.6/Dockerfile . - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - docker push "ldonini/ocpp1.6-central-system:$TRAVIS_TAG" - docker push "ldonini/ocpp1.6-charge-point:$TRAVIS_TAG" diff --git a/README.md b/README.md index 8bb205f4..9c91201d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Planned milestones and features: **Note: The library is still a WIP, therefore expect some APIs to change.** -## Usage +## OCPP 1.6 Usage Go version 1.11+ is required. diff --git a/docker-compose.yml b/docker-compose.yml index 07e86fd1..5adbc416 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ services: central_system: build: context: . - dockerfile: ./example/cs/Dockerfile + dockerfile: example/cs1.6/Dockerfile image: ldonini/ocpp1.6-central-system:latest container_name: central-system ports: @@ -16,7 +16,7 @@ services: - CENTRAL_SYSTEM_URL=ws://central_system:8887 build: context: . - dockerfile: ./example/cp/Dockerfile + dockerfile: example/cp1.6/Dockerfile image: ldonini/ocpp1.6-charge-point:latest container_name: charge-point networks: diff --git a/example/cp/charge_point_sim.go b/example/cp/charge_point_sim.go deleted file mode 100644 index 434b9db8..00000000 --- a/example/cp/charge_point_sim.go +++ /dev/null @@ -1,335 +0,0 @@ -package main - -import ( - "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" - log "github.com/sirupsen/logrus" - "os" - "time" -) - -type ConnectorInfo struct { - status ocpp16.ChargePointStatus - availability ocpp16.AvailabilityType - currentTransaction int - currentReservation int -} - -type ChargePointHandler struct { - status ocpp16.ChargePointStatus - connectors map[int]*ConnectorInfo - errorCode ocpp16.ChargePointErrorCode - configuration map[string]ocpp16.ConfigurationKey - meterValue int - localAuthList []ocpp16.AuthorizationData - localAuthListVersion int -} - -var chargePoint ocpp16.ChargePoint - -// Core profile callbacks -func (handler *ChargePointHandler) OnChangeAvailability(request *ocpp16.ChangeAvailabilityRequest) (confirmation *ocpp16.ChangeAvailabilityConfirmation, err error) { - handler.connectors[request.ConnectorId].availability = request.Type - return ocpp16.NewChangeAvailabilityConfirmation(ocpp16.AvailabilityStatusAccepted), nil -} - -func (handler *ChargePointHandler) OnChangeConfiguration(request *ocpp16.ChangeConfigurationRequest) (confirmation *ocpp16.ChangeConfigurationConfirmation, err error) { - configKey, ok := handler.configuration[request.Key] - if !ok { - return ocpp16.NewChangeConfigurationConfirmation(ocpp16.ConfigurationStatusNotSupported), nil - } else if configKey.Readonly { - return ocpp16.NewChangeConfigurationConfirmation(ocpp16.ConfigurationStatusRejected), nil - } - configKey.Value = request.Value - handler.configuration[request.Key] = configKey - return ocpp16.NewChangeConfigurationConfirmation(ocpp16.ConfigurationStatusAccepted), nil -} - -func (handler *ChargePointHandler) OnClearCache(request *ocpp16.ClearCacheRequest) (confirmation *ocpp16.ClearCacheConfirmation, err error) { - return ocpp16.NewClearCacheConfirmation(ocpp16.ClearCacheStatusAccepted), nil -} - -func (handler *ChargePointHandler) OnDataTransfer(request *ocpp16.DataTransferRequest) (confirmation *ocpp16.DataTransferConfirmation, err error) { - logDefault(request.GetFeatureName()).Infof("data transfer [Vendor: %v Message: %v]: %v", request.VendorId, request.MessageId, request.Data) - return ocpp16.NewDataTransferConfirmation(ocpp16.DataTransferStatusAccepted), nil -} - -func (handler *ChargePointHandler) OnGetConfiguration(request *ocpp16.GetConfigurationRequest) (confirmation *ocpp16.GetConfigurationConfirmation, err error) { - var resultKeys []ocpp16.ConfigurationKey - var unknownKeys []string - for _, key := range request.Key { - configKey, ok := handler.configuration[key] - if !ok { - unknownKeys = append(unknownKeys, configKey.Value) - } else { - resultKeys = append(resultKeys, configKey) - } - } - conf := ocpp16.NewGetConfigurationConfirmation(resultKeys) - conf.UnknownKey = unknownKeys - return conf, nil -} - -func (handler *ChargePointHandler) OnRemoteStartTransaction(request *ocpp16.RemoteStartTransactionRequest) (confirmation *ocpp16.RemoteStartTransactionConfirmation, err error) { - connector, ok := handler.connectors[request.ConnectorId] - if !ok { - return ocpp16.NewRemoteStartTransactionConfirmation(ocpp16.RemoteStartStopStatusRejected), nil - } else if connector.availability != ocpp16.AvailabilityTypeOperative || connector.status != ocpp16.ChargePointStatusAvailable || connector.currentTransaction > 0 { - return ocpp16.NewRemoteStartTransactionConfirmation(ocpp16.RemoteStartStopStatusRejected), nil - } - logDefault(request.GetFeatureName()).Infof("started transaction %v on connector %v", connector.currentTransaction, request.ConnectorId) - connector.currentTransaction = request.ConnectorId - return ocpp16.NewRemoteStartTransactionConfirmation(ocpp16.RemoteStartStopStatusAccepted), nil -} - -func (handler *ChargePointHandler) OnRemoteStopTransaction(request *ocpp16.RemoteStopTransactionRequest) (confirmation *ocpp16.RemoteStopTransactionConfirmation, err error) { - for key, val := range handler.connectors { - if val.currentTransaction == request.TransactionId { - logDefault(request.GetFeatureName()).Infof("stopped transaction %v on connector %v", val.currentTransaction, key) - val.currentTransaction = 0 - val.currentReservation = 0 - val.status = ocpp16.ChargePointStatusAvailable - return ocpp16.NewRemoteStopTransactionConfirmation(ocpp16.RemoteStartStopStatusAccepted), nil - } - } - return ocpp16.NewRemoteStopTransactionConfirmation(ocpp16.RemoteStartStopStatusRejected), nil -} - -func (handler *ChargePointHandler) OnReset(request *ocpp16.ResetRequest) (confirmation *ocpp16.ResetConfirmation, err error) { - //TODO: stop all ongoing transactions - return ocpp16.NewResetConfirmation(ocpp16.ResetStatusAccepted), nil -} - -func (handler *ChargePointHandler) OnUnlockConnector(request *ocpp16.UnlockConnectorRequest) (confirmation *ocpp16.UnlockConnectorConfirmation, err error) { - _, ok := handler.connectors[request.ConnectorId] - if !ok { - return ocpp16.NewUnlockConnectorConfirmation(ocpp16.UnlockStatusNotSupported), nil - } - return ocpp16.NewUnlockConnectorConfirmation(ocpp16.UnlockStatusUnlocked), nil -} - -// Local authorization list profile callbacks -func (handler *ChargePointHandler) OnGetLocalListVersion(request *ocpp16.GetLocalListVersionRequest) (confirmation *ocpp16.GetLocalListVersionConfirmation, err error) { - return ocpp16.NewGetLocalListVersionConfirmation(handler.localAuthListVersion), nil -} - -func (handler *ChargePointHandler) OnSendLocalList(request *ocpp16.SendLocalListRequest) (confirmation *ocpp16.SendLocalListConfirmation, err error) { - if request.ListVersion <= handler.localAuthListVersion { - return ocpp16.NewSendLocalListConfirmation(ocpp16.UpdateStatusVersionMismatch), nil - } - if request.UpdateType == ocpp16.UpdateTypeFull { - handler.localAuthList = request.LocalAuthorizationList - handler.localAuthListVersion = request.ListVersion - } else if request.UpdateType == ocpp16.UpdateTypeDifferential { - handler.localAuthList = append(handler.localAuthList, request.LocalAuthorizationList...) - handler.localAuthListVersion = request.ListVersion - } - return ocpp16.NewSendLocalListConfirmation(ocpp16.UpdateStatusAccepted), nil -} - -// Firmware management profile callbacks -func (handler *ChargePointHandler) OnGetDiagnostics(request *ocpp16.GetDiagnosticsRequest) (confirmation *ocpp16.GetDiagnosticsConfirmation, err error) { - return ocpp16.NewGetDiagnosticsConfirmation(), nil - //TODO: perform diagnostics upload out-of-band -} - -func (handler *ChargePointHandler) OnUpdateFirmware(request *ocpp16.UpdateFirmwareRequest) (confirmation *ocpp16.UpdateFirmwareConfirmation, err error) { - return ocpp16.NewUpdateFirmwareConfirmation(), nil - //TODO: download new firmware out-of-band -} - -// Remote trigger profile callbacks -func (handler *ChargePointHandler) OnTriggerMessage(request *ocpp16.TriggerMessageRequest) (confirmation *ocpp16.TriggerMessageConfirmation, err error) { - switch request.RequestedMessage { - case ocpp16.BootNotificationFeatureName: - //TODO: schedule boot notification message - break - case ocpp16.DiagnosticsStatusNotificationFeatureName: - //TODO: schedule diagnostics status notification message - break - case ocpp16.FirmwareStatusNotificationFeatureName: - //TODO: schedule firmware status notification message - break - case ocpp16.HeartbeatFeatureName: - //TODO: schedule heartbeat message - break - case ocpp16.MeterValuesFeatureName: - //TODO: schedule meter values message - break - //TODO: schedule status notification message - case ocpp16.StatusNotificationFeatureName: - break - default: - return ocpp16.NewTriggerMessageConfirmation(ocpp16.TriggerMessageStatusNotImplemented), nil - } - return ocpp16.NewTriggerMessageConfirmation(ocpp16.TriggerMessageStatusAccepted), nil -} - -// Reservation profile callbacks -func (handler *ChargePointHandler) OnReserveNow(request *ocpp16.ReserveNowRequest) (confirmation *ocpp16.ReserveNowConfirmation, err error) { - connector := handler.connectors[request.ConnectorId] - if connector == nil { - return ocpp16.NewReserveNowConfirmation(ocpp16.ReservationStatusUnavailable), nil - } else if connector.status != ocpp16.ChargePointStatusAvailable { - return ocpp16.NewReserveNowConfirmation(ocpp16.ReservationStatusOccupied), nil - } - connector.currentReservation = request.ReservationId - go updateStatus(handler, request.ConnectorId, ocpp16.ChargePointStatusReserved) - // TODO: automatically remove reservation after expiryDate - return ocpp16.NewReserveNowConfirmation(ocpp16.ReservationStatusAccepted), nil -} - -func (handler *ChargePointHandler) OnCancelReservation(request *ocpp16.CancelReservationRequest) (confirmation *ocpp16.CancelReservationConfirmation, err error) { - for k,v := range handler.connectors { - if v.currentReservation == request.ReservationId { - v.currentReservation = 0 - if v.status == ocpp16.ChargePointStatusReserved { - go updateStatus(handler, k, ocpp16.ChargePointStatusAvailable) - } - return ocpp16.NewCancelReservationConfirmation(ocpp16.CancelReservationStatusAccepted), nil - } - } - return ocpp16.NewCancelReservationConfirmation(ocpp16.CancelReservationStatusRejected), nil -} - -// Smart charging profile callbacks -func (handler *ChargePointHandler) OnSetChargingProfile(request *ocpp16.SetChargingProfileRequest) (confirmation *ocpp16.SetChargingProfileConfirmation, err error) { - //TODO: handle logic - return ocpp16.NewSetChargingProfileConfirmation(ocpp16.ChargingProfileStatusNotImplemented), nil -} - -func (handler *ChargePointHandler) OnClearChargingProfile(request *ocpp16.ClearChargingProfileRequest) (confirmation *ocpp16.ClearChargingProfileConfirmation, err error) { - //TODO: handle logic - return ocpp16.NewClearChargingProfileConfirmation(ocpp16.ClearChargingProfileStatusUnknown), nil -} - -func (handler *ChargePointHandler) OnGetCompositeSchedule(request *ocpp16.GetCompositeScheduleRequest) (confirmation *ocpp16.GetCompositeScheduleConfirmation, err error) { - //TODO: handle logic - return ocpp16.NewGetCompositeScheduleConfirmation(ocpp16.GetCompositeScheduleStatusRejected), nil -} - -func checkError(err error) { - if err != nil { - log.Fatal(err) - } -} - -func getExpiryDate(info *ocpp16.IdTagInfo) string { - if info.ExpiryDate != nil { - return fmt.Sprintf("authorized until %v", info.ExpiryDate.String()) - } - return "" -} - -func updateStatus(stateHandler *ChargePointHandler, connector int, status ocpp16.ChargePointStatus) { - if connector == 0 { - stateHandler.status = status - } else { - stateHandler.connectors[connector].status = status - } - statusConfirmation, err := chargePoint.StatusNotification(connector, ocpp16.NoError, status) - checkError(err) - if connector == 0 { - logDefault(statusConfirmation.GetFeatureName()).Infof("status for all connectors updated to %v", status) - } else { - logDefault(statusConfirmation.GetFeatureName()).Infof("status for connector %v updated to %v", connector, status) - } -} - -func exampleRoutine(chargePoint ocpp16.ChargePoint, stateHandler *ChargePointHandler) { - dummyClientIdTag := "12345" - chargingConnector := 1 - // Boot - bootConf, err := chargePoint.BootNotification("model1", "vendor1") - checkError(err) - logDefault(bootConf.GetFeatureName()).Infof("status: %v, interval: %v, current time: %v", bootConf.Status, bootConf.Interval, bootConf.CurrentTime.String()) - // Notify connector status - updateStatus(stateHandler, 0, ocpp16.ChargePointStatusAvailable) - // Wait for some time ... - time.Sleep(5 * time.Second) - // Simulate charging for connector 1 - authConf, err := chargePoint.Authorize(dummyClientIdTag) - checkError(err) - logDefault(authConf.GetFeatureName()).Infof("status: %v %v", authConf.IdTagInfo.Status, getExpiryDate(authConf.IdTagInfo)) - // Update connector status - updateStatus(stateHandler, chargingConnector, ocpp16.ChargePointStatusPreparing) - // Start transaction - startConf, err := chargePoint.StartTransaction(chargingConnector, dummyClientIdTag, stateHandler.meterValue, ocpp16.NewDateTime(time.Now())) - checkError(err) - logDefault(startConf.GetFeatureName()).Infof("status: %v, transaction %v %v", startConf.IdTagInfo.Status, startConf.TransactionId, getExpiryDate(startConf.IdTagInfo)) - stateHandler.connectors[chargingConnector].currentTransaction = startConf.TransactionId - // Update connector status - updateStatus(stateHandler, chargingConnector, ocpp16.ChargePointStatusCharging) - // Periodically send meter values - for i := 0; i < 5; i++ { - time.Sleep(5 * time.Second) - stateHandler.meterValue += 10 - sampledValue := ocpp16.SampledValue{Value: fmt.Sprintf("%v", stateHandler.meterValue), Unit: ocpp16.UnitOfMeasureWh, Format: ocpp16.ValueFormatRaw, Measurand: ocpp16.MeasurandEnergyActiveExportRegister, Context: ocpp16.ReadingContextSamplePeriodic, Location: ocpp16.LocationOutlet} - meterValue := ocpp16.MeterValue{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{sampledValue}} - meterConf, err := chargePoint.MeterValues(chargingConnector, []ocpp16.MeterValue{meterValue}) - checkError(err) - logDefault(meterConf.GetFeatureName()).Infof("sent updated %v", sampledValue.Measurand) - } - stateHandler.meterValue += 2 - // Stop charging for connector 1 - updateStatus(stateHandler, chargingConnector, ocpp16.ChargePointStatusFinishing) - stopConf, err := chargePoint.StopTransaction(stateHandler.meterValue, ocpp16.NewDateTime(time.Now()), startConf.TransactionId, func(request *ocpp16.StopTransactionRequest) { - sampledValue := ocpp16.SampledValue{Value: fmt.Sprintf("%v", stateHandler.meterValue), Unit: ocpp16.UnitOfMeasureWh, Format: ocpp16.ValueFormatRaw, Measurand: ocpp16.MeasurandEnergyActiveExportRegister, Context: ocpp16.ReadingContextSamplePeriodic, Location: ocpp16.LocationOutlet} - meterValue := ocpp16.MeterValue{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{sampledValue}} - request.TransactionData = []ocpp16.MeterValue{meterValue} - request.Reason = ocpp16.ReasonEVDisconnected - }) - checkError(err) - logDefault(stopConf.GetFeatureName()).Infof("transaction %v stopped", startConf.TransactionId) - // Update connector status - updateStatus(stateHandler, chargingConnector, ocpp16.ChargePointStatusAvailable) -} - -// Start function -func main() { - // Parse arguments from env variables - id, ok := os.LookupEnv("CLIENT_ID") - if !ok { - log.Print("Usage:\n\tocppClientId\n\tocppServerUrl") - return - } - csUrl, ok := os.LookupEnv("CENTRAL_SYSTEM_URL") - if !ok { - log.Print("Usage:\n\tocppClientId\n\tocppServerUrl") - return - } - // Create a default OCPP 1.6 charge point - chargePoint = ocpp16.NewChargePoint(id, nil, nil) - // Set a handler for all callback functions - connectors := map[int]*ConnectorInfo{ - 1: {status: ocpp16.ChargePointStatusAvailable, availability: ocpp16.AvailabilityTypeOperative, currentTransaction: 0}, - } - handler := &ChargePointHandler{ - status: ocpp16.ChargePointStatusAvailable, - connectors: connectors, - configuration: map[string]ocpp16.ConfigurationKey{}, - errorCode: ocpp16.NoError, - localAuthList: []ocpp16.AuthorizationData{}, - localAuthListVersion: 0} - chargePoint.SetChargePointCoreListener(handler) - // Connects to central system - err := chargePoint.Start(csUrl) - if err != nil { - log.Println(err) - } else { - log.Infof("connected to central system at %v", csUrl) - exampleRoutine(chargePoint, handler) - // Disconnect - chargePoint.Stop() - log.Infof("disconnected from central system") - } -} - -func init() { - log.SetLevel(log.InfoLevel) -} - -// Utility functions -func logDefault(feature string) *log.Entry { - return log.WithField("message", feature) -} diff --git a/example/cp/Dockerfile b/example/cp1.6/Dockerfile similarity index 89% rename from example/cp/Dockerfile rename to example/cp1.6/Dockerfile index e48c9e07..78e9c38c 100644 --- a/example/cp/Dockerfile +++ b/example/cp1.6/Dockerfile @@ -9,7 +9,7 @@ COPY . . # Fetch dependencies. RUN go mod download # Build the binary. -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /go/bin/charge_point example/cp/charge_point_sim.go +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /go/bin/charge_point example/cp1.6/charge_point_sim.go ############################ # STEP 2 build a small image diff --git a/example/cp1.6/charge_point_sim.go b/example/cp1.6/charge_point_sim.go new file mode 100644 index 00000000..8cba4664 --- /dev/null +++ b/example/cp1.6/charge_point_sim.go @@ -0,0 +1,342 @@ +package main + +import ( + "fmt" + ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/localauth" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/remotetrigger" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/reservation" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" + log "github.com/sirupsen/logrus" + "os" + "time" +) + +type ConnectorInfo struct { + status core.ChargePointStatus + availability core.AvailabilityType + currentTransaction int + currentReservation int +} + +type ChargePointHandler struct { + status core.ChargePointStatus + connectors map[int]*ConnectorInfo + errorCode core.ChargePointErrorCode + configuration map[string]core.ConfigurationKey + meterValue int + localAuthList []localauth.AuthorizationData + localAuthListVersion int +} + +var chargePoint ocpp16.ChargePoint + +// Core profile callbacks +func (handler *ChargePointHandler) OnChangeAvailability(request *core.ChangeAvailabilityRequest) (confirmation *core.ChangeAvailabilityConfirmation, err error) { + handler.connectors[request.ConnectorId].availability = request.Type + return core.NewChangeAvailabilityConfirmation(core.AvailabilityStatusAccepted), nil +} + +func (handler *ChargePointHandler) OnChangeConfiguration(request *core.ChangeConfigurationRequest) (confirmation *core.ChangeConfigurationConfirmation, err error) { + configKey, ok := handler.configuration[request.Key] + if !ok { + return core.NewChangeConfigurationConfirmation(core.ConfigurationStatusNotSupported), nil + } else if configKey.Readonly { + return core.NewChangeConfigurationConfirmation(core.ConfigurationStatusRejected), nil + } + configKey.Value = request.Value + handler.configuration[request.Key] = configKey + return core.NewChangeConfigurationConfirmation(core.ConfigurationStatusAccepted), nil +} + +func (handler *ChargePointHandler) OnClearCache(request *core.ClearCacheRequest) (confirmation *core.ClearCacheConfirmation, err error) { + return core.NewClearCacheConfirmation(core.ClearCacheStatusAccepted), nil +} + +func (handler *ChargePointHandler) OnDataTransfer(request *core.DataTransferRequest) (confirmation *core.DataTransferConfirmation, err error) { + logDefault(request.GetFeatureName()).Infof("data transfer [Vendor: %v Message: %v]: %v", request.VendorId, request.MessageId, request.Data) + return core.NewDataTransferConfirmation(core.DataTransferStatusAccepted), nil +} + +func (handler *ChargePointHandler) OnGetConfiguration(request *core.GetConfigurationRequest) (confirmation *core.GetConfigurationConfirmation, err error) { + var resultKeys []core.ConfigurationKey + var unknownKeys []string + for _, key := range request.Key { + configKey, ok := handler.configuration[key] + if !ok { + unknownKeys = append(unknownKeys, configKey.Value) + } else { + resultKeys = append(resultKeys, configKey) + } + } + conf := core.NewGetConfigurationConfirmation(resultKeys) + conf.UnknownKey = unknownKeys + return conf, nil +} + +func (handler *ChargePointHandler) OnRemoteStartTransaction(request *core.RemoteStartTransactionRequest) (confirmation *core.RemoteStartTransactionConfirmation, err error) { + connector, ok := handler.connectors[request.ConnectorId] + if !ok { + return core.NewRemoteStartTransactionConfirmation(types.RemoteStartStopStatusRejected), nil + } else if connector.availability != core.AvailabilityTypeOperative || connector.status != core.ChargePointStatusAvailable || connector.currentTransaction > 0 { + return core.NewRemoteStartTransactionConfirmation(types.RemoteStartStopStatusRejected), nil + } + logDefault(request.GetFeatureName()).Infof("started transaction %v on connector %v", connector.currentTransaction, request.ConnectorId) + connector.currentTransaction = request.ConnectorId + return core.NewRemoteStartTransactionConfirmation(types.RemoteStartStopStatusAccepted), nil +} + +func (handler *ChargePointHandler) OnRemoteStopTransaction(request *core.RemoteStopTransactionRequest) (confirmation *core.RemoteStopTransactionConfirmation, err error) { + for key, val := range handler.connectors { + if val.currentTransaction == request.TransactionId { + logDefault(request.GetFeatureName()).Infof("stopped transaction %v on connector %v", val.currentTransaction, key) + val.currentTransaction = 0 + val.currentReservation = 0 + val.status = core.ChargePointStatusAvailable + return core.NewRemoteStopTransactionConfirmation(types.RemoteStartStopStatusAccepted), nil + } + } + return core.NewRemoteStopTransactionConfirmation(types.RemoteStartStopStatusRejected), nil +} + +func (handler *ChargePointHandler) OnReset(request *core.ResetRequest) (confirmation *core.ResetConfirmation, err error) { + //TODO: stop all ongoing transactions + return core.NewResetConfirmation(core.ResetStatusAccepted), nil +} + +func (handler *ChargePointHandler) OnUnlockConnector(request *core.UnlockConnectorRequest) (confirmation *core.UnlockConnectorConfirmation, err error) { + _, ok := handler.connectors[request.ConnectorId] + if !ok { + return core.NewUnlockConnectorConfirmation(core.UnlockStatusNotSupported), nil + } + return core.NewUnlockConnectorConfirmation(core.UnlockStatusUnlocked), nil +} + +// Local authorization list profile callbacks +func (handler *ChargePointHandler) OnGetLocalListVersion(request *localauth.GetLocalListVersionRequest) (confirmation *localauth.GetLocalListVersionConfirmation, err error) { + return localauth.NewGetLocalListVersionConfirmation(handler.localAuthListVersion), nil +} + +func (handler *ChargePointHandler) OnSendLocalList(request *localauth.SendLocalListRequest) (confirmation *localauth.SendLocalListConfirmation, err error) { + if request.ListVersion <= handler.localAuthListVersion { + return localauth.NewSendLocalListConfirmation(localauth.UpdateStatusVersionMismatch), nil + } + if request.UpdateType == localauth.UpdateTypeFull { + handler.localAuthList = request.LocalAuthorizationList + handler.localAuthListVersion = request.ListVersion + } else if request.UpdateType == localauth.UpdateTypeDifferential { + handler.localAuthList = append(handler.localAuthList, request.LocalAuthorizationList...) + handler.localAuthListVersion = request.ListVersion + } + return localauth.NewSendLocalListConfirmation(localauth.UpdateStatusAccepted), nil +} + +// Firmware management profile callbacks +func (handler *ChargePointHandler) OnGetDiagnostics(request *firmware.GetDiagnosticsRequest) (confirmation *firmware.GetDiagnosticsConfirmation, err error) { + return firmware.NewGetDiagnosticsConfirmation(), nil + //TODO: perform diagnostics upload out-of-band +} + +func (handler *ChargePointHandler) OnUpdateFirmware(request *firmware.UpdateFirmwareRequest) (confirmation *firmware.UpdateFirmwareConfirmation, err error) { + return firmware.NewUpdateFirmwareConfirmation(), nil + //TODO: download new firmware out-of-band +} + +// Remote trigger profile callbacks +func (handler *ChargePointHandler) OnTriggerMessage(request *remotetrigger.TriggerMessageRequest) (confirmation *remotetrigger.TriggerMessageConfirmation, err error) { + switch request.RequestedMessage { + case core.BootNotificationFeatureName: + //TODO: schedule boot notification message + break + case firmware.DiagnosticsStatusNotificationFeatureName: + //TODO: schedule diagnostics status notification message + break + case firmware.FirmwareStatusNotificationFeatureName: + //TODO: schedule firmware status notification message + break + case core.HeartbeatFeatureName: + //TODO: schedule heartbeat message + break + case core.MeterValuesFeatureName: + //TODO: schedule meter values message + break + //TODO: schedule status notification message + case core.StatusNotificationFeatureName: + break + default: + return remotetrigger.NewTriggerMessageConfirmation(remotetrigger.TriggerMessageStatusNotImplemented), nil + } + return remotetrigger.NewTriggerMessageConfirmation(remotetrigger.TriggerMessageStatusAccepted), nil +} + +// Reservation profile callbacks +func (handler *ChargePointHandler) OnReserveNow(request *reservation.ReserveNowRequest) (confirmation *reservation.ReserveNowConfirmation, err error) { + connector := handler.connectors[request.ConnectorId] + if connector == nil { + return reservation.NewReserveNowConfirmation(reservation.ReservationStatusUnavailable), nil + } else if connector.status != core.ChargePointStatusAvailable { + return reservation.NewReserveNowConfirmation(reservation.ReservationStatusOccupied), nil + } + connector.currentReservation = request.ReservationId + go updateStatus(handler, request.ConnectorId, core.ChargePointStatusReserved) + // TODO: automatically remove reservation after expiryDate + return reservation.NewReserveNowConfirmation(reservation.ReservationStatusAccepted), nil +} + +func (handler *ChargePointHandler) OnCancelReservation(request *reservation.CancelReservationRequest) (confirmation *reservation.CancelReservationConfirmation, err error) { + for k, v := range handler.connectors { + if v.currentReservation == request.ReservationId { + v.currentReservation = 0 + if v.status == core.ChargePointStatusReserved { + go updateStatus(handler, k, core.ChargePointStatusAvailable) + } + return reservation.NewCancelReservationConfirmation(reservation.CancelReservationStatusAccepted), nil + } + } + return reservation.NewCancelReservationConfirmation(reservation.CancelReservationStatusRejected), nil +} + +// Smart charging profile callbacks +func (handler *ChargePointHandler) OnSetChargingProfile(request *smartcharging.SetChargingProfileRequest) (confirmation *smartcharging.SetChargingProfileConfirmation, err error) { + //TODO: handle logic + return smartcharging.NewSetChargingProfileConfirmation(smartcharging.ChargingProfileStatusNotImplemented), nil +} + +func (handler *ChargePointHandler) OnClearChargingProfile(request *smartcharging.ClearChargingProfileRequest) (confirmation *smartcharging.ClearChargingProfileConfirmation, err error) { + //TODO: handle logic + return smartcharging.NewClearChargingProfileConfirmation(smartcharging.ClearChargingProfileStatusUnknown), nil +} + +func (handler *ChargePointHandler) OnGetCompositeSchedule(request *smartcharging.GetCompositeScheduleRequest) (confirmation *smartcharging.GetCompositeScheduleConfirmation, err error) { + //TODO: handle logic + return smartcharging.NewGetCompositeScheduleConfirmation(smartcharging.GetCompositeScheduleStatusRejected), nil +} + +func checkError(err error) { + if err != nil { + log.Fatal(err) + } +} + +func getExpiryDate(info *types.IdTagInfo) string { + if info.ExpiryDate != nil { + return fmt.Sprintf("authorized until %v", info.ExpiryDate.String()) + } + return "" +} + +func updateStatus(stateHandler *ChargePointHandler, connector int, status core.ChargePointStatus) { + if connector == 0 { + stateHandler.status = status + } else { + stateHandler.connectors[connector].status = status + } + statusConfirmation, err := chargePoint.StatusNotification(connector, core.NoError, status) + checkError(err) + if connector == 0 { + logDefault(statusConfirmation.GetFeatureName()).Infof("status for all connectors updated to %v", status) + } else { + logDefault(statusConfirmation.GetFeatureName()).Infof("status for connector %v updated to %v", connector, status) + } +} + +func exampleRoutine(chargePoint ocpp16.ChargePoint, stateHandler *ChargePointHandler) { + dummyClientIdTag := "12345" + chargingConnector := 1 + // Boot + bootConf, err := chargePoint.BootNotification("model1", "vendor1") + checkError(err) + logDefault(bootConf.GetFeatureName()).Infof("status: %v, interval: %v, current time: %v", bootConf.Status, bootConf.Interval, bootConf.CurrentTime.String()) + // Notify connector status + updateStatus(stateHandler, 0, core.ChargePointStatusAvailable) + // Wait for some time ... + time.Sleep(5 * time.Second) + // Simulate charging for connector 1 + authConf, err := chargePoint.Authorize(dummyClientIdTag) + checkError(err) + logDefault(authConf.GetFeatureName()).Infof("status: %v %v", authConf.IdTagInfo.Status, getExpiryDate(authConf.IdTagInfo)) + // Update connector status + updateStatus(stateHandler, chargingConnector, core.ChargePointStatusPreparing) + // Start transaction + startConf, err := chargePoint.StartTransaction(chargingConnector, dummyClientIdTag, stateHandler.meterValue, types.NewDateTime(time.Now())) + checkError(err) + logDefault(startConf.GetFeatureName()).Infof("status: %v, transaction %v %v", startConf.IdTagInfo.Status, startConf.TransactionId, getExpiryDate(startConf.IdTagInfo)) + stateHandler.connectors[chargingConnector].currentTransaction = startConf.TransactionId + // Update connector status + updateStatus(stateHandler, chargingConnector, core.ChargePointStatusCharging) + // Periodically send meter values + for i := 0; i < 5; i++ { + time.Sleep(5 * time.Second) + stateHandler.meterValue += 10 + sampledValue := types.SampledValue{Value: fmt.Sprintf("%v", stateHandler.meterValue), Unit: types.UnitOfMeasureWh, Format: types.ValueFormatRaw, Measurand: types.MeasurandEnergyActiveExportRegister, Context: types.ReadingContextSamplePeriodic, Location: types.LocationOutlet} + meterValue := types.MeterValue{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{sampledValue}} + meterConf, err := chargePoint.MeterValues(chargingConnector, []types.MeterValue{meterValue}) + checkError(err) + logDefault(meterConf.GetFeatureName()).Infof("sent updated %v", sampledValue.Measurand) + } + stateHandler.meterValue += 2 + // Stop charging for connector 1 + updateStatus(stateHandler, chargingConnector, core.ChargePointStatusFinishing) + stopConf, err := chargePoint.StopTransaction(stateHandler.meterValue, types.NewDateTime(time.Now()), startConf.TransactionId, func(request *core.StopTransactionRequest) { + sampledValue := types.SampledValue{Value: fmt.Sprintf("%v", stateHandler.meterValue), Unit: types.UnitOfMeasureWh, Format: types.ValueFormatRaw, Measurand: types.MeasurandEnergyActiveExportRegister, Context: types.ReadingContextSamplePeriodic, Location: types.LocationOutlet} + meterValue := types.MeterValue{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{sampledValue}} + request.TransactionData = []types.MeterValue{meterValue} + request.Reason = core.ReasonEVDisconnected + }) + checkError(err) + logDefault(stopConf.GetFeatureName()).Infof("transaction %v stopped", startConf.TransactionId) + // Update connector status + updateStatus(stateHandler, chargingConnector, core.ChargePointStatusAvailable) +} + +// Start function +func main() { + // Parse arguments from env variables + id, ok := os.LookupEnv("CLIENT_ID") + if !ok { + log.Print("Usage:\n\tocppClientId\n\tocppServerUrl") + return + } + csUrl, ok := os.LookupEnv("CENTRAL_SYSTEM_URL") + if !ok { + log.Print("Usage:\n\tocppClientId\n\tocppServerUrl") + return + } + // Create a default OCPP 1.6 charge point + chargePoint = ocpp16.NewChargePoint(id, nil, nil) + // Set a handler for all callback functions + connectors := map[int]*ConnectorInfo{ + 1: {status: core.ChargePointStatusAvailable, availability: core.AvailabilityTypeOperative, currentTransaction: 0}, + } + handler := &ChargePointHandler{ + status: core.ChargePointStatusAvailable, + connectors: connectors, + configuration: map[string]core.ConfigurationKey{}, + errorCode: core.NoError, + localAuthList: []localauth.AuthorizationData{}, + localAuthListVersion: 0} + chargePoint.SetChargePointCoreHandler(handler) + // Connects to central system + err := chargePoint.Start(csUrl) + if err != nil { + log.Println(err) + } else { + log.Infof("connected to central system at %v", csUrl) + exampleRoutine(chargePoint, handler) + // Disconnect + chargePoint.Stop() + log.Infof("disconnected from central system") + } +} + +func init() { + log.SetLevel(log.InfoLevel) +} + +// Utility functions +func logDefault(feature string) *log.Entry { + return log.WithField("message", feature) +} diff --git a/example/cs/Dockerfile b/example/cs1.6/Dockerfile similarity index 89% rename from example/cs/Dockerfile rename to example/cs1.6/Dockerfile index 4620d6f0..d6798263 100644 --- a/example/cs/Dockerfile +++ b/example/cs1.6/Dockerfile @@ -9,7 +9,7 @@ COPY . . # Fetch dependencies. RUN go mod download # Build the binary. -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /go/bin/central_system example/cs/central_system_sim.go +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /go/bin/central_system example/cs1.6/central_system_sim.go ############################ # STEP 2 build a small image diff --git a/example/cs/central_system_sim.go b/example/cs1.6/central_system_sim.go similarity index 72% rename from example/cs/central_system_sim.go rename to example/cs1.6/central_system_sim.go index b6fbff4e..82641b0b 100644 --- a/example/cs/central_system_sim.go +++ b/example/cs1.6/central_system_sim.go @@ -3,6 +3,9 @@ package main import ( "fmt" ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" log "github.com/sirupsen/logrus" "os" "strconv" @@ -23,8 +26,8 @@ var ( // Charge Point state type TransactionInfo struct { id int - startTime *ocpp16.DateTime - endTime *ocpp16.DateTime + startTime *types.DateTime + endTime *types.DateTime startMeter int endMeter int connectorId int @@ -36,7 +39,7 @@ func (ti *TransactionInfo) hasTransactionEnded() bool { } type ConnectorInfo struct { - status ocpp16.ChargePointStatus + status core.ChargePointStatus currentTransaction int } @@ -45,12 +48,12 @@ func (ci *ConnectorInfo) hasTransactionInProgress() bool { } type ChargePointState struct { - status ocpp16.ChargePointStatus - diagnosticsStatus ocpp16.DiagnosticsStatus - firmwareStatus ocpp16.FirmwareStatus + status core.ChargePointStatus + diagnosticsStatus firmware.DiagnosticsStatus + firmwareStatus firmware.FirmwareStatus connectors map[int]*ConnectorInfo // No assumptions about the # of connectors transactions map[int]*TransactionInfo - errorCode ocpp16.ChargePointErrorCode + errorCode core.ChargePointErrorCode } func (cps *ChargePointState) getConnector(id int) *ConnectorInfo { @@ -67,34 +70,34 @@ type CentralSystemHandler struct { } // Core profile callbacks -func (handler *CentralSystemHandler) OnAuthorize(chargePointId string, request *ocpp16.AuthorizeRequest) (confirmation *ocpp16.AuthorizeConfirmation, err error) { +func (handler *CentralSystemHandler) OnAuthorize(chargePointId string, request *core.AuthorizeRequest) (confirmation *core.AuthorizeConfirmation, err error) { logDefault(chargePointId, request.GetFeatureName()).Infof("client authorized") - return ocpp16.NewAuthorizationConfirmation(ocpp16.NewIdTagInfo(ocpp16.AuthorizationStatusAccepted)), nil + return core.NewAuthorizationConfirmation(types.NewIdTagInfo(types.AuthorizationStatusAccepted)), nil } -func (handler *CentralSystemHandler) OnBootNotification(chargePointId string, request *ocpp16.BootNotificationRequest) (confirmation *ocpp16.BootNotificationConfirmation, err error) { +func (handler *CentralSystemHandler) OnBootNotification(chargePointId string, request *core.BootNotificationRequest) (confirmation *core.BootNotificationConfirmation, err error) { logDefault(chargePointId, request.GetFeatureName()).Infof("boot confirmed") - return ocpp16.NewBootNotificationConfirmation(ocpp16.NewDateTime(time.Now()), defaultHeartbeatInterval, ocpp16.RegistrationStatusAccepted), nil + return core.NewBootNotificationConfirmation(types.NewDateTime(time.Now()), defaultHeartbeatInterval, core.RegistrationStatusAccepted), nil } -func (handler *CentralSystemHandler) OnDataTransfer(chargePointId string, request *ocpp16.DataTransferRequest) (confirmation *ocpp16.DataTransferConfirmation, err error) { +func (handler *CentralSystemHandler) OnDataTransfer(chargePointId string, request *core.DataTransferRequest) (confirmation *core.DataTransferConfirmation, err error) { logDefault(chargePointId, request.GetFeatureName()).Infof("received data %d", request.Data) - return ocpp16.NewDataTransferConfirmation(ocpp16.DataTransferStatusAccepted), nil + return core.NewDataTransferConfirmation(core.DataTransferStatusAccepted), nil } -func (handler *CentralSystemHandler) OnHeartbeat(chargePointId string, request *ocpp16.HeartbeatRequest) (confirmation *ocpp16.HeartbeatConfirmation, err error) { - return ocpp16.NewHeartbeatConfirmation(ocpp16.NewDateTime(time.Now())), nil +func (handler *CentralSystemHandler) OnHeartbeat(chargePointId string, request *core.HeartbeatRequest) (confirmation *core.HeartbeatConfirmation, err error) { + return core.NewHeartbeatConfirmation(types.NewDateTime(time.Now())), nil } -func (handler *CentralSystemHandler) OnMeterValues(chargePointId string, request *ocpp16.MeterValuesRequest) (confirmation *ocpp16.MeterValuesConfirmation, err error) { +func (handler *CentralSystemHandler) OnMeterValues(chargePointId string, request *core.MeterValuesRequest) (confirmation *core.MeterValuesConfirmation, err error) { logDefault(chargePointId, request.GetFeatureName()).Infof("received meter values for connector %v, transaction %v. Meter values:\n", request.ConnectorId, request.TransactionId) for _, mv := range request.MeterValue { logDefault(chargePointId, request.GetFeatureName()).Printf("\t %v", mv) } - return ocpp16.NewMeterValuesConfirmation(), nil + return core.NewMeterValuesConfirmation(), nil } -func (handler *CentralSystemHandler) OnStatusNotification(chargePointId string, request *ocpp16.StatusNotificationRequest) (confirmation *ocpp16.StatusNotificationConfirmation, err error) { +func (handler *CentralSystemHandler) OnStatusNotification(chargePointId string, request *core.StatusNotificationRequest) (confirmation *core.StatusNotificationConfirmation, err error) { info, ok := handler.chargePoints[chargePointId] if !ok { return nil, fmt.Errorf("unknown charge point %v", chargePointId) @@ -108,10 +111,10 @@ func (handler *CentralSystemHandler) OnStatusNotification(chargePointId string, info.status = request.Status logDefault(chargePointId, request.GetFeatureName()).Infof("all connectors updated status to %v", request.Status) } - return ocpp16.NewStatusNotificationConfirmation(), nil + return core.NewStatusNotificationConfirmation(), nil } -func (handler *CentralSystemHandler) OnStartTransaction(chargePointId string, request *ocpp16.StartTransactionRequest) (confirmation *ocpp16.StartTransactionConfirmation, err error) { +func (handler *CentralSystemHandler) OnStartTransaction(chargePointId string, request *core.StartTransactionRequest) (confirmation *core.StartTransactionConfirmation, err error) { info, ok := handler.chargePoints[chargePointId] if !ok { return nil, fmt.Errorf("unknown charge point %v", chargePointId) @@ -131,10 +134,10 @@ func (handler *CentralSystemHandler) OnStartTransaction(chargePointId string, re info.transactions[transaction.id] = transaction //TODO: check billable clients logDefault(chargePointId, request.GetFeatureName()).Infof("started transaction %v for connector %v", transaction.id, transaction.connectorId) - return ocpp16.NewStartTransactionConfirmation(ocpp16.NewIdTagInfo(ocpp16.AuthorizationStatusAccepted), transaction.id), nil + return core.NewStartTransactionConfirmation(types.NewIdTagInfo(types.AuthorizationStatusAccepted), transaction.id), nil } -func (handler *CentralSystemHandler) OnStopTransaction(chargePointId string, request *ocpp16.StopTransactionRequest) (confirmation *ocpp16.StopTransactionConfirmation, err error) { +func (handler *CentralSystemHandler) OnStopTransaction(chargePointId string, request *core.StopTransactionRequest) (confirmation *core.StopTransactionConfirmation, err error) { info, ok := handler.chargePoints[chargePointId] if !ok { return nil, fmt.Errorf("unknown charge point %v", chargePointId) @@ -151,28 +154,28 @@ func (handler *CentralSystemHandler) OnStopTransaction(chargePointId string, req for _, mv := range request.TransactionData { logDefault(chargePointId, request.GetFeatureName()).Printf("\t %v", mv) } - return ocpp16.NewStopTransactionConfirmation(), nil + return core.NewStopTransactionConfirmation(), nil } // Firmware management callbacks -func (handler *CentralSystemHandler) OnDiagnosticsStatusNotification(chargePointId string, request *ocpp16.DiagnosticsStatusNotificationRequest) (confirmation *ocpp16.DiagnosticsStatusNotificationConfirmation, err error) { +func (handler *CentralSystemHandler) OnDiagnosticsStatusNotification(chargePointId string, request *firmware.DiagnosticsStatusNotificationRequest) (confirmation *firmware.DiagnosticsStatusNotificationConfirmation, err error) { info, ok := handler.chargePoints[chargePointId] if !ok { return nil, fmt.Errorf("unknown charge point %v", chargePointId) } info.diagnosticsStatus = request.Status logDefault(chargePointId, request.GetFeatureName()).Infof("updated diagnostics status to %v", request.Status) - return ocpp16.NewDiagnosticsStatusNotificationConfirmation(), nil + return firmware.NewDiagnosticsStatusNotificationConfirmation(), nil } -func (handler *CentralSystemHandler) OnFirmwareStatusNotification(chargePointId string, request *ocpp16.FirmwareStatusNotificationRequest) (confirmation *ocpp16.FirmwareStatusNotificationConfirmation, err error) { +func (handler *CentralSystemHandler) OnFirmwareStatusNotification(chargePointId string, request *firmware.FirmwareStatusNotificationRequest) (confirmation *firmware.FirmwareStatusNotificationConfirmation, err error) { info, ok := handler.chargePoints[chargePointId] if !ok { return nil, fmt.Errorf("unknown charge point %v", chargePointId) } info.firmwareStatus = request.Status logDefault(chargePointId, request.GetFeatureName()).Infof("updated firmware status to %v", request.Status) - return &ocpp16.FirmwareStatusNotificationConfirmation{}, nil + return &firmware.FirmwareStatusNotificationConfirmation{}, nil } // No callbacks for Local Auth management, Reservation, Remote trigger or Smart Charging profile on central system @@ -190,7 +193,7 @@ func main() { log.WithField("client", chargePointId).Info("charge point disconnected") delete(handler.chargePoints, chargePointId) }) - centralSystem.SetCentralSystemCoreListener(handler) + centralSystem.SetCentralSystemCoreHandler(handler) var listenPort = defaultListenPort if len(args) > 0 { port, err := strconv.Atoi(args[0]) diff --git a/ocpp/ocpp.go b/ocpp/ocpp.go index f5f68e99..8dac2ee6 100644 --- a/ocpp/ocpp.go +++ b/ocpp/ocpp.go @@ -1,3 +1,5 @@ +// Open Charge Point Protocol (OCPP) is a standard open protocol for communication between Charge Points and a Central System and is designed to accommodate any type of charging technique. +// This package contains the base interfaces used for the OCPP 1.6 and OCPP 2.0 implementation. package ocpp import ( @@ -6,28 +8,40 @@ import ( "reflect" ) +// Feature represents a single functionality, associated to a unique name. +// Every feature describes a Request and a Response message, specified in the OCPP protocol. type Feature interface { + // Returns the unique name of the feature. GetFeatureName() string + // Returns the type of the request message. GetRequestType() reflect.Type - GetConfirmationType() reflect.Type + // Returns the type of the response message. + GetResponseType() reflect.Type } +// Request message type Request interface { + // Returns the unique name of the feature, to which this request belongs to. GetFeatureName() string } -type Confirmation interface { +// Response message +type Response interface { + // Returns the unique name of the feature, to which this request belongs to. GetFeatureName() string } +// ErrorCode defines a common code name for an error. type ErrorCode string +// Error wraps an OCPP error, containing an ErrorCode, a Description and the ID of the message. type Error struct { Code ErrorCode Description string MessageId string } +// Creates a new OCPP Error. func NewError(errorCode ErrorCode, description string, messageId string) *Error { return &Error{Code: errorCode, Description: description, MessageId: messageId} } @@ -37,11 +51,17 @@ func (err *Error) Error() string { } // -------------------- Profile -------------------- + +// Profile defines a specific set of features, grouped by functionality. +// +// Some vendor may want to keep the protocol as slim as possible, and only support some feature profiles. +// This can easily be achieved by only registering certain profiles, while remaining compliant with the specifications. type Profile struct { Name string Features map[string]Feature } +// Creates a new profile, identified by a name and a set of features. func NewProfile(name string, features ...Feature) *Profile { profile := Profile{Name: name, Features: make(map[string]Feature)} for _, feature := range features { @@ -50,19 +70,25 @@ func NewProfile(name string, features ...Feature) *Profile { return &profile } +// Adds a feature to the profile. func (p *Profile) AddFeature(feature Feature) { p.Features[feature.GetFeatureName()] = feature } +// SupportsFeature returns true if a feature matching the the passed name is registered with this profile, false otherwise. func (p *Profile) SupportsFeature(name string) bool { _, ok := p.Features[name] return ok } +// Retrieves a feature, identified by a unique name. +// Returns nil in case the feature is not registered with this profile. func (p *Profile) GetFeature(name string) Feature { return p.Features[name] } +// ParseRequest checks whether a feature is supported and passes the rawRequest message to the requestParser function. +// The type of the request message is passed to the requestParser function, which has to perform type assertion. func (p *Profile) ParseRequest(featureName string, rawRequest interface{}, requestParser func(raw interface{}, requestType reflect.Type) (Request, error)) (Request, error) { feature, ok := p.Features[featureName] if !ok { @@ -72,11 +98,13 @@ func (p *Profile) ParseRequest(featureName string, rawRequest interface{}, reque return requestParser(rawRequest, requestType) } -func (p *Profile) ParseConfirmation(featureName string, rawConfirmation interface{}, confirmationParser func(raw interface{}, confirmationType reflect.Type) (Confirmation, error)) (Confirmation, error) { +// ParseRequest checks whether a feature is supported and passes the rawResponse message to the responseParser function. +// The type of the response message is passed to the responseParser function, which has to perform type assertion. +func (p *Profile) ParseResponse(featureName string, rawResponse interface{}, responseParser func(raw interface{}, responseType reflect.Type) (Response, error)) (Response, error) { feature, ok := p.Features[featureName] if !ok { return nil, errors2.Errorf("Feature %s not found", featureName) } - confirmationType := feature.GetConfirmationType() - return confirmationParser(rawConfirmation, confirmationType) + responseType := feature.GetResponseType() + return responseParser(rawResponse, responseType) } diff --git a/ocpp1.6/central_system.go b/ocpp1.6/central_system.go new file mode 100644 index 00000000..00ef7ecf --- /dev/null +++ b/ocpp1.6/central_system.go @@ -0,0 +1,508 @@ +package ocpp16 + +import ( + "fmt" + "github.com/lorenzodonini/ocpp-go/ocpp" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/localauth" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/remotetrigger" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/reservation" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" + "github.com/lorenzodonini/ocpp-go/ocppj" + log "github.com/sirupsen/logrus" +) + +type centralSystem struct { + server *ocppj.Server + coreHandler core.CentralSystemCoreHandler + localAuthListHandler localauth.CentralSystemLocalAuthListHandler + firmwareHandler firmware.CentralSystemFirmwareManagementHandler + reservationHandler reservation.CentralSystemReservationHandler + remoteTriggerHandler remotetrigger.CentralSystemRemoteTriggerHandler + smartChargingHandler smartcharging.CentralSystemSmartChargingHandler + callbacks map[string]func(confirmation ocpp.Response, err error) +} + +func (cs *centralSystem) ChangeAvailability(clientId string, callback func(confirmation *core.ChangeAvailabilityConfirmation, err error), connectorId int, availabilityType core.AvailabilityType, props ...func(request *core.ChangeAvailabilityRequest)) error { + request := core.NewChangeAvailabilityRequest(connectorId, availabilityType) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*core.ChangeAvailabilityConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) ChangeConfiguration(clientId string, callback func(confirmation *core.ChangeConfigurationConfirmation, err error), key string, value string, props ...func(request *core.ChangeConfigurationRequest)) error { + request := core.NewChangeConfigurationRequest(key, value) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*core.ChangeConfigurationConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) ClearCache(clientId string, callback func(confirmation *core.ClearCacheConfirmation, err error), props ...func(*core.ClearCacheRequest)) error { + request := core.NewClearCacheRequest() + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*core.ClearCacheConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) DataTransfer(clientId string, callback func(confirmation *core.DataTransferConfirmation, err error), vendorId string, props ...func(request *core.DataTransferRequest)) error { + request := core.NewDataTransferRequest(vendorId) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*core.DataTransferConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) GetConfiguration(clientId string, callback func(confirmation *core.GetConfigurationConfirmation, err error), keys []string, props ...func(request *core.GetConfigurationRequest)) error { + request := core.NewGetConfigurationRequest(keys) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*core.GetConfigurationConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) RemoteStartTransaction(clientId string, callback func(*core.RemoteStartTransactionConfirmation, error), idTag string, props ...func(*core.RemoteStartTransactionRequest)) error { + request := core.NewRemoteStartTransactionRequest(idTag) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*core.RemoteStartTransactionConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) RemoteStopTransaction(clientId string, callback func(*core.RemoteStopTransactionConfirmation, error), transactionId int, props ...func(request *core.RemoteStopTransactionRequest)) error { + request := core.NewRemoteStopTransactionRequest(transactionId) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*core.RemoteStopTransactionConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) Reset(clientId string, callback func(*core.ResetConfirmation, error), resetType core.ResetType, props ...func(request *core.ResetRequest)) error { + request := core.NewResetRequest(resetType) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*core.ResetConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) UnlockConnector(clientId string, callback func(*core.UnlockConnectorConfirmation, error), connectorId int, props ...func(*core.UnlockConnectorRequest)) error { + request := core.NewUnlockConnectorRequest(connectorId) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*core.UnlockConnectorConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) GetLocalListVersion(clientId string, callback func(*localauth.GetLocalListVersionConfirmation, error), props ...func(request *localauth.GetLocalListVersionRequest)) error { + request := localauth.NewGetLocalListVersionRequest() + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*localauth.GetLocalListVersionConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) SendLocalList(clientId string, callback func(*localauth.SendLocalListConfirmation, error), version int, updateType localauth.UpdateType, props ...func(request *localauth.SendLocalListRequest)) error { + request := localauth.NewSendLocalListRequest(version, updateType) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*localauth.SendLocalListConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) GetDiagnostics(clientId string, callback func(*firmware.GetDiagnosticsConfirmation, error), location string, props ...func(request *firmware.GetDiagnosticsRequest)) error { + request := firmware.NewGetDiagnosticsRequest(location) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*firmware.GetDiagnosticsConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) UpdateFirmware(clientId string, callback func(*firmware.UpdateFirmwareConfirmation, error), location string, retrieveDate *types.DateTime, props ...func(request *firmware.UpdateFirmwareRequest)) error { + request := firmware.NewUpdateFirmwareRequest(location, retrieveDate) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*firmware.UpdateFirmwareConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) ReserveNow(clientId string, callback func(*reservation.ReserveNowConfirmation, error), connectorId int, expiryDate *types.DateTime, idTag string, reservationId int, props ...func(request *reservation.ReserveNowRequest)) error { + request := reservation.NewReserveNowRequest(connectorId, expiryDate, idTag, reservationId) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*reservation.ReserveNowConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) CancelReservation(clientId string, callback func(*reservation.CancelReservationConfirmation, error), reservationId int, props ...func(request *reservation.CancelReservationRequest)) error { + request := reservation.NewCancelReservationRequest(reservationId) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*reservation.CancelReservationConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) TriggerMessage(clientId string, callback func(*remotetrigger.TriggerMessageConfirmation, error), requestedMessage remotetrigger.MessageTrigger, props ...func(request *remotetrigger.TriggerMessageRequest)) error { + request := remotetrigger.NewTriggerMessageRequest(requestedMessage) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*remotetrigger.TriggerMessageConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) SetChargingProfile(clientId string, callback func(*smartcharging.SetChargingProfileConfirmation, error), connectorId int, chargingProfile *types.ChargingProfile, props ...func(request *smartcharging.SetChargingProfileRequest)) error { + request := smartcharging.NewSetChargingProfileRequest(connectorId, chargingProfile) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*smartcharging.SetChargingProfileConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) ClearChargingProfile(clientId string, callback func(*smartcharging.ClearChargingProfileConfirmation, error), props ...func(request *smartcharging.ClearChargingProfileRequest)) error { + request := smartcharging.NewClearChargingProfileRequest() + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*smartcharging.ClearChargingProfileConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) GetCompositeSchedule(clientId string, callback func(*smartcharging.GetCompositeScheduleConfirmation, error), connectorId int, duration int, props ...func(request *smartcharging.GetCompositeScheduleRequest)) error { + request := smartcharging.NewGetCompositeScheduleRequest(connectorId, duration) + for _, fn := range props { + fn(request) + } + genericCallback := func(confirmation ocpp.Response, protoError error) { + if confirmation != nil { + callback(confirmation.(*smartcharging.GetCompositeScheduleConfirmation), protoError) + } else { + callback(nil, protoError) + } + } + return cs.SendRequestAsync(clientId, request, genericCallback) +} + +func (cs *centralSystem) SetCentralSystemCoreHandler(listener core.CentralSystemCoreHandler) { + cs.coreHandler = listener +} + +func (cs *centralSystem) SetLocalAuthListHandler(listener localauth.CentralSystemLocalAuthListHandler) { + cs.localAuthListHandler = listener +} + +func (cs *centralSystem) SetFirmwareManagementHandler(listener firmware.CentralSystemFirmwareManagementHandler) { + cs.firmwareHandler = listener +} + +func (cs *centralSystem) SetReservationHandler(listener reservation.CentralSystemReservationHandler) { + cs.reservationHandler = listener +} + +func (cs *centralSystem) SetRemoteTriggerHandler(listener remotetrigger.CentralSystemRemoteTriggerHandler) { + cs.remoteTriggerHandler = listener +} + +func (cs *centralSystem) SetSmartChargingHandler(listener smartcharging.CentralSystemSmartChargingHandler) { + cs.smartChargingHandler = listener +} + +func (cs *centralSystem) SetNewChargePointHandler(handler func(chargePointId string)) { + cs.server.SetNewClientHandler(handler) +} + +func (cs *centralSystem) SetChargePointDisconnectedHandler(handler func(chargePointId string)) { + cs.server.SetDisconnectedClientHandler(handler) +} + +func (cs *centralSystem) SendRequestAsync(clientId string, request ocpp.Request, callback func(confirmation ocpp.Response, err error)) error { + featureName := request.GetFeatureName() + if _, found := cs.server.GetProfileForFeature(featureName); !found { + return fmt.Errorf("feature %v is unsupported on central system (missing profile), cannot send request", featureName) + } + switch featureName { + case core.ChangeAvailabilityFeatureName, core.ChangeConfigurationFeatureName, core.ClearCacheFeatureName, core.DataTransferFeatureName, core.GetConfigurationFeatureName, core.RemoteStartTransactionFeatureName, core.RemoteStopTransactionFeatureName, core.ResetFeatureName, core.UnlockConnectorFeatureName, + localauth.GetLocalListVersionFeatureName, localauth.SendLocalListFeatureName, + firmware.GetDiagnosticsFeatureName, firmware.UpdateFirmwareFeatureName, + reservation.ReserveNowFeatureName, reservation.CancelReservationFeatureName, + remotetrigger.TriggerMessageFeatureName, + smartcharging.SetChargingProfileFeatureName, smartcharging.ClearChargingProfileFeatureName, smartcharging.GetCompositeScheduleFeatureName: + default: + return fmt.Errorf("unsupported action %v on central system, cannot send request", featureName) + } + cs.callbacks[clientId] = callback + err := cs.server.SendRequest(clientId, request) + if err != nil { + delete(cs.callbacks, clientId) + return err + } + return nil +} + +func (cs *centralSystem) Start(listenPort int, listenPath string) { + cs.server.Start(listenPort, listenPath) +} + +func (cs *centralSystem) sendResponse(chargePointId string, confirmation ocpp.Response, err error, requestId string) { + if confirmation != nil { + err := cs.server.SendResponse(chargePointId, requestId, confirmation) + if err != nil { + //TODO: handle error somehow + log.Print(err) + } + } else { + err := cs.server.SendError(chargePointId, requestId, ocppj.ProtocolError, "Couldn't generate valid confirmation", nil) + if err != nil { + log.WithFields(log.Fields{ + "client": chargePointId, + "request": requestId, + }).Errorf("unknown error %v while replying to message with CallError", err) + } + } +} + +func (cs *centralSystem) notImplementedError(chargePointId string, requestId string, action string) { + log.Warnf("Cannot handle call %v from charge point %v. Sending CallError instead", requestId, chargePointId) + err := cs.server.SendError(chargePointId, requestId, ocppj.NotImplemented, fmt.Sprintf("no handler for action %v implemented", action), nil) + if err != nil { + log.WithFields(log.Fields{ + "client": chargePointId, + "request": requestId, + }).Errorf("unknown error %v while replying to message with CallError", err) + } +} + +func (cs *centralSystem) notSupportedError(chargePointId string, requestId string, action string) { + log.Warnf("Cannot handle call %v from charge point %v. Sending CallError instead", requestId, chargePointId) + err := cs.server.SendError(chargePointId, requestId, ocppj.NotSupported, fmt.Sprintf("unsupported action %v on central system", action), nil) + if err != nil { + log.WithFields(log.Fields{ + "client": chargePointId, + "request": requestId, + }).Errorf("unknown error %v while replying to message with CallError", err) + } +} + +func (cs *centralSystem) handleIncomingRequest(chargePointId string, request ocpp.Request, requestId string, action string) { + profile, found := cs.server.GetProfileForFeature(action) + // Check whether action is supported and a listener for it exists + if !found { + cs.notImplementedError(chargePointId, requestId, action) + return + } else { + switch profile.Name { + case core.ProfileName: + if cs.coreHandler == nil { + cs.notSupportedError(chargePointId, requestId, action) + return + } + case localauth.ProfileName: + if cs.localAuthListHandler == nil { + cs.notSupportedError(chargePointId, requestId, action) + return + } + case firmware.ProfileName: + if cs.firmwareHandler == nil { + cs.notSupportedError(chargePointId, requestId, action) + return + } + case reservation.ProfileName: + if cs.reservationHandler == nil { + cs.notSupportedError(chargePointId, requestId, action) + return + } + case remotetrigger.ProfileName: + if cs.remoteTriggerHandler == nil { + cs.notSupportedError(chargePointId, requestId, action) + return + } + case smartcharging.ProfileName: + if cs.smartChargingHandler == nil { + cs.notSupportedError(chargePointId, requestId, action) + return + } + } + } + var confirmation ocpp.Response = nil + var err error = nil + // Execute in separate goroutine, so the caller goroutine is available + go func() { + switch action { + case core.BootNotificationFeatureName: + confirmation, err = cs.coreHandler.OnBootNotification(chargePointId, request.(*core.BootNotificationRequest)) + case core.AuthorizeFeatureName: + confirmation, err = cs.coreHandler.OnAuthorize(chargePointId, request.(*core.AuthorizeRequest)) + case core.DataTransferFeatureName: + confirmation, err = cs.coreHandler.OnDataTransfer(chargePointId, request.(*core.DataTransferRequest)) + case core.HeartbeatFeatureName: + confirmation, err = cs.coreHandler.OnHeartbeat(chargePointId, request.(*core.HeartbeatRequest)) + case core.MeterValuesFeatureName: + confirmation, err = cs.coreHandler.OnMeterValues(chargePointId, request.(*core.MeterValuesRequest)) + case core.StartTransactionFeatureName: + confirmation, err = cs.coreHandler.OnStartTransaction(chargePointId, request.(*core.StartTransactionRequest)) + case core.StopTransactionFeatureName: + confirmation, err = cs.coreHandler.OnStopTransaction(chargePointId, request.(*core.StopTransactionRequest)) + case core.StatusNotificationFeatureName: + confirmation, err = cs.coreHandler.OnStatusNotification(chargePointId, request.(*core.StatusNotificationRequest)) + case firmware.DiagnosticsStatusNotificationFeatureName: + confirmation, err = cs.firmwareHandler.OnDiagnosticsStatusNotification(chargePointId, request.(*firmware.DiagnosticsStatusNotificationRequest)) + case firmware.FirmwareStatusNotificationFeatureName: + confirmation, err = cs.firmwareHandler.OnFirmwareStatusNotification(chargePointId, request.(*firmware.FirmwareStatusNotificationRequest)) + default: + cs.notSupportedError(chargePointId, requestId, action) + return + } + cs.sendResponse(chargePointId, confirmation, err, requestId) + }() +} + +func (cs *centralSystem) handleIncomingConfirmation(chargePointId string, confirmation ocpp.Response, requestId string) { + if callback, ok := cs.callbacks[chargePointId]; ok { + delete(cs.callbacks, chargePointId) + callback(confirmation, nil) + } else { + log.WithFields(log.Fields{ + "client": chargePointId, + "request": requestId, + }).Errorf("no handler available for Call Result of type %v", confirmation.GetFeatureName()) + } +} + +func (cs *centralSystem) handleIncomingError(chargePointId string, err *ocpp.Error, details interface{}) { + if callback, ok := cs.callbacks[chargePointId]; ok { + delete(cs.callbacks, chargePointId) + callback(nil, err) + } else { + log.WithFields(log.Fields{ + "client": chargePointId, + "request": err.MessageId, + }).Errorf("no handler available for Call Error %v", err.Code) + } +} diff --git a/ocpp1.6/charge_point.go b/ocpp1.6/charge_point.go new file mode 100644 index 00000000..5f34ad17 --- /dev/null +++ b/ocpp1.6/charge_point.go @@ -0,0 +1,356 @@ +package ocpp16 + +import ( + "fmt" + "github.com/lorenzodonini/ocpp-go/ocpp" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/localauth" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/remotetrigger" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/reservation" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" + "github.com/lorenzodonini/ocpp-go/ocppj" + log "github.com/sirupsen/logrus" +) + +type chargePoint struct { + client *ocppj.Client + coreHandler core.ChargePointCoreHandler + localAuthListHandler localauth.ChargePointLocalAuthListHandler + firmwareHandler firmware.ChargePointFirmwareManagementHandler + reservationHandler reservation.ChargePointReservationHandler + remoteTriggerHandler remotetrigger.ChargePointRemoteTriggerHandler + smartChargingHandler smartcharging.ChargePointSmartChargingHandler + confirmationHandler chan ocpp.Response + errorHandler chan error +} + +func (cp *chargePoint) BootNotification(chargePointModel string, chargePointVendor string, props ...func(request *core.BootNotificationRequest)) (*core.BootNotificationConfirmation, error) { + request := core.NewBootNotificationRequest(chargePointModel, chargePointVendor) + for _, fn := range props { + fn(request) + } + confirmation, err := cp.SendRequest(request) + if err != nil { + return nil, err + } else { + return confirmation.(*core.BootNotificationConfirmation), err + } +} + +func (cp *chargePoint) Authorize(idTag string, props ...func(request *core.AuthorizeRequest)) (*core.AuthorizeConfirmation, error) { + request := core.NewAuthorizationRequest(idTag) + for _, fn := range props { + fn(request) + } + confirmation, err := cp.SendRequest(request) + if err != nil { + return nil, err + } else { + return confirmation.(*core.AuthorizeConfirmation), err + } +} + +func (cp *chargePoint) DataTransfer(vendorId string, props ...func(request *core.DataTransferRequest)) (*core.DataTransferConfirmation, error) { + request := core.NewDataTransferRequest(vendorId) + for _, fn := range props { + fn(request) + } + confirmation, err := cp.SendRequest(request) + if err != nil { + return nil, err + } else { + return confirmation.(*core.DataTransferConfirmation), err + } +} + +func (cp *chargePoint) Heartbeat(props ...func(request *core.HeartbeatRequest)) (*core.HeartbeatConfirmation, error) { + request := core.NewHeartbeatRequest() + for _, fn := range props { + fn(request) + } + confirmation, err := cp.SendRequest(request) + if err != nil { + return nil, err + } else { + return confirmation.(*core.HeartbeatConfirmation), err + } +} + +func (cp *chargePoint) MeterValues(connectorId int, meterValues []types.MeterValue, props ...func(request *core.MeterValuesRequest)) (*core.MeterValuesConfirmation, error) { + request := core.NewMeterValuesRequest(connectorId, meterValues) + for _, fn := range props { + fn(request) + } + confirmation, err := cp.SendRequest(request) + if err != nil { + return nil, err + } else { + return confirmation.(*core.MeterValuesConfirmation), err + } +} + +func (cp *chargePoint) StartTransaction(connectorId int, idTag string, meterStart int, timestamp *types.DateTime, props ...func(request *core.StartTransactionRequest)) (*core.StartTransactionConfirmation, error) { + request := core.NewStartTransactionRequest(connectorId, idTag, meterStart, timestamp) + for _, fn := range props { + fn(request) + } + confirmation, err := cp.SendRequest(request) + if err != nil { + return nil, err + } else { + return confirmation.(*core.StartTransactionConfirmation), err + } +} + +func (cp *chargePoint) StopTransaction(meterStop int, timestamp *types.DateTime, transactionId int, props ...func(request *core.StopTransactionRequest)) (*core.StopTransactionConfirmation, error) { + request := core.NewStopTransactionRequest(meterStop, timestamp, transactionId) + for _, fn := range props { + fn(request) + } + confirmation, err := cp.SendRequest(request) + if err != nil { + return nil, err + } else { + return confirmation.(*core.StopTransactionConfirmation), err + } +} + +func (cp *chargePoint) StatusNotification(connectorId int, errorCode core.ChargePointErrorCode, status core.ChargePointStatus, props ...func(request *core.StatusNotificationRequest)) (*core.StatusNotificationConfirmation, error) { + request := core.NewStatusNotificationRequest(connectorId, errorCode, status) + for _, fn := range props { + fn(request) + } + confirmation, err := cp.SendRequest(request) + if err != nil { + return nil, err + } else { + return confirmation.(*core.StatusNotificationConfirmation), err + } +} + +func (cp *chargePoint) DiagnosticsStatusNotification(status firmware.DiagnosticsStatus, props ...func(request *firmware.DiagnosticsStatusNotificationRequest)) (*firmware.DiagnosticsStatusNotificationConfirmation, error) { + request := firmware.NewDiagnosticsStatusNotificationRequest(status) + for _, fn := range props { + fn(request) + } + confirmation, err := cp.SendRequest(request) + if err != nil { + return nil, err + } else { + return confirmation.(*firmware.DiagnosticsStatusNotificationConfirmation), err + } +} + +func (cp *chargePoint) FirmwareStatusNotification(status firmware.FirmwareStatus, props ...func(request *firmware.FirmwareStatusNotificationRequest)) (*firmware.FirmwareStatusNotificationConfirmation, error) { + request := firmware.NewFirmwareStatusNotificationRequest(status) + for _, fn := range props { + fn(request) + } + confirmation, err := cp.SendRequest(request) + if err != nil { + return nil, err + } else { + return confirmation.(*firmware.FirmwareStatusNotificationConfirmation), err + } +} + +func (cp *chargePoint) SetChargePointCoreHandler(listener core.ChargePointCoreHandler) { + cp.coreHandler = listener +} + +func (cp *chargePoint) SetLocalAuthListHandler(listener localauth.ChargePointLocalAuthListHandler) { + cp.localAuthListHandler = listener +} + +func (cp *chargePoint) SetFirmwareManagementHandler(listener firmware.ChargePointFirmwareManagementHandler) { + cp.firmwareHandler = listener +} + +func (cp *chargePoint) SetReservationHandler(listener reservation.ChargePointReservationHandler) { + cp.reservationHandler = listener +} + +func (cp *chargePoint) SetRemoteTriggerHandler(listener remotetrigger.ChargePointRemoteTriggerHandler) { + cp.remoteTriggerHandler = listener +} + +func (cp *chargePoint) SetSmartChargingHandler(listener smartcharging.ChargePointSmartChargingHandler) { + cp.smartChargingHandler = listener +} + +func (cp *chargePoint) SendRequest(request ocpp.Request) (ocpp.Response, error) { + featureName := request.GetFeatureName() + if _, found := cp.client.GetProfileForFeature(featureName); !found { + return nil, fmt.Errorf("feature %v is unsupported on charge point (missing profile), cannot send request", featureName) + } + err := cp.client.SendRequest(request) + if err != nil { + return nil, err + } + //TODO: timeouts + select { + case confirmation := <-cp.confirmationHandler: + return confirmation, nil + case err = <-cp.errorHandler: + return nil, err + } +} + +func (cp *chargePoint) SendRequestAsync(request ocpp.Request, callback func(confirmation ocpp.Response, err error)) error { + featureName := request.GetFeatureName() + if _, found := cp.client.GetProfileForFeature(featureName); !found { + return fmt.Errorf("feature %v is unsupported on charge point (missing profile), cannot send request", featureName) + } + switch featureName { + case core.AuthorizeFeatureName, core.BootNotificationFeatureName, core.DataTransferFeatureName, core.HeartbeatFeatureName, core.MeterValuesFeatureName, core.StartTransactionFeatureName, core.StopTransactionFeatureName, core.StatusNotificationFeatureName, + firmware.DiagnosticsStatusNotificationFeatureName, firmware.FirmwareStatusNotificationFeatureName: + break + default: + return fmt.Errorf("unsupported action %v on charge point, cannot send request", featureName) + } + err := cp.client.SendRequest(request) + if err == nil { + // Retrieve result asynchronously + go func() { + select { + case confirmation := <-cp.confirmationHandler: + callback(confirmation, nil) + case protoError := <-cp.errorHandler: + callback(nil, protoError) + } + }() + } + return err +} + +func (cp *chargePoint) sendResponse(confirmation ocpp.Response, err error, requestId string) { + if confirmation != nil { + err := cp.client.SendResponse(requestId, confirmation) + if err != nil { + log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) + //TODO: handle error somehow + } + } else { + err = cp.client.SendError(requestId, ocppj.ProtocolError, err.Error(), nil) + if err != nil { + log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) + } + } +} + +func (cp *chargePoint) Start(centralSystemUrl string) error { + // TODO: implement auto-reconnect logic + return cp.client.Start(centralSystemUrl) +} + +func (cp *chargePoint) Stop() { + cp.client.Stop() +} + +func (cp *chargePoint) notImplementedError(requestId string, action string) { + log.WithField("request", requestId).Errorf("cannot handle Call from central system. Sending CallError instead") + err := cp.client.SendError(requestId, ocppj.NotImplemented, fmt.Sprintf("no handler for action %v implemented", action), nil) + if err != nil { + log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) + } +} + +func (cp *chargePoint) notSupportedError(requestId string, action string) { + log.WithField("request", requestId).Errorf("cannot handle call from central system. Sending CallError instead") + err := cp.client.SendError(requestId, ocppj.NotSupported, fmt.Sprintf("unsupported action %v on charge point", action), nil) + if err != nil { + log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) + } +} + +func (cp *chargePoint) handleIncomingRequest(request ocpp.Request, requestId string, action string) { + profile, found := cp.client.GetProfileForFeature(action) + // Check whether action is supported and a listener for it exists + if !found { + cp.notImplementedError(requestId, action) + return + } else { + switch profile.Name { + case core.ProfileName: + if cp.coreHandler == nil { + cp.notSupportedError(requestId, action) + return + } + case localauth.ProfileName: + if cp.localAuthListHandler == nil { + cp.notSupportedError(requestId, action) + return + } + case firmware.ProfileName: + if cp.firmwareHandler == nil { + cp.notSupportedError(requestId, action) + return + } + case reservation.ProfileName: + if cp.reservationHandler == nil { + cp.notSupportedError(requestId, action) + return + } + case remotetrigger.ProfileName: + if cp.remoteTriggerHandler == nil { + cp.notSupportedError(requestId, action) + return + } + case smartcharging.ProfileName: + if cp.smartChargingHandler == nil { + cp.notSupportedError(requestId, action) + return + } + } + } + // Process request + var confirmation ocpp.Response = nil + cp.client.GetProfileForFeature(action) + var err error = nil + switch action { + case core.ChangeAvailabilityFeatureName: + confirmation, err = cp.coreHandler.OnChangeAvailability(request.(*core.ChangeAvailabilityRequest)) + case core.ChangeConfigurationFeatureName: + confirmation, err = cp.coreHandler.OnChangeConfiguration(request.(*core.ChangeConfigurationRequest)) + case core.ClearCacheFeatureName: + confirmation, err = cp.coreHandler.OnClearCache(request.(*core.ClearCacheRequest)) + case core.DataTransferFeatureName: + confirmation, err = cp.coreHandler.OnDataTransfer(request.(*core.DataTransferRequest)) + case core.GetConfigurationFeatureName: + confirmation, err = cp.coreHandler.OnGetConfiguration(request.(*core.GetConfigurationRequest)) + case core.RemoteStartTransactionFeatureName: + confirmation, err = cp.coreHandler.OnRemoteStartTransaction(request.(*core.RemoteStartTransactionRequest)) + case core.RemoteStopTransactionFeatureName: + confirmation, err = cp.coreHandler.OnRemoteStopTransaction(request.(*core.RemoteStopTransactionRequest)) + case core.ResetFeatureName: + confirmation, err = cp.coreHandler.OnReset(request.(*core.ResetRequest)) + case core.UnlockConnectorFeatureName: + confirmation, err = cp.coreHandler.OnUnlockConnector(request.(*core.UnlockConnectorRequest)) + case localauth.GetLocalListVersionFeatureName: + confirmation, err = cp.localAuthListHandler.OnGetLocalListVersion(request.(*localauth.GetLocalListVersionRequest)) + case localauth.SendLocalListFeatureName: + confirmation, err = cp.localAuthListHandler.OnSendLocalList(request.(*localauth.SendLocalListRequest)) + case firmware.GetDiagnosticsFeatureName: + confirmation, err = cp.firmwareHandler.OnGetDiagnostics(request.(*firmware.GetDiagnosticsRequest)) + case firmware.UpdateFirmwareFeatureName: + confirmation, err = cp.firmwareHandler.OnUpdateFirmware(request.(*firmware.UpdateFirmwareRequest)) + case reservation.ReserveNowFeatureName: + confirmation, err = cp.reservationHandler.OnReserveNow(request.(*reservation.ReserveNowRequest)) + case reservation.CancelReservationFeatureName: + confirmation, err = cp.reservationHandler.OnCancelReservation(request.(*reservation.CancelReservationRequest)) + case remotetrigger.TriggerMessageFeatureName: + confirmation, err = cp.remoteTriggerHandler.OnTriggerMessage(request.(*remotetrigger.TriggerMessageRequest)) + case smartcharging.SetChargingProfileFeatureName: + confirmation, err = cp.smartChargingHandler.OnSetChargingProfile(request.(*smartcharging.SetChargingProfileRequest)) + case smartcharging.ClearChargingProfileFeatureName: + confirmation, err = cp.smartChargingHandler.OnClearChargingProfile(request.(*smartcharging.ClearChargingProfileRequest)) + case smartcharging.GetCompositeScheduleFeatureName: + confirmation, err = cp.smartChargingHandler.OnGetCompositeSchedule(request.(*smartcharging.GetCompositeScheduleRequest)) + default: + cp.notSupportedError(requestId, action) + return + } + cp.sendResponse(confirmation, err, requestId) +} diff --git a/ocpp1.6/authorize.go b/ocpp1.6/core/authorize.go similarity index 86% rename from ocpp1.6/authorize.go rename to ocpp1.6/core/authorize.go index 6725bc36..3a9874ad 100644 --- a/ocpp1.6/authorize.go +++ b/ocpp1.6/core/authorize.go @@ -1,11 +1,14 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "reflect" ) // -------------------- Authorize (CP -> CS) -------------------- +const AuthorizeFeatureName = "Authorize" + // The field definition of the Authorize request payload sent by the Charge Point to the Central System. type AuthorizeRequest struct { IdTag string `json:"idTag" validate:"required,max=20"` @@ -14,7 +17,7 @@ type AuthorizeRequest struct { // This field definition of the Authorize confirmation payload, sent by the Charge Point to the Central System in response to an AuthorizeRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. type AuthorizeConfirmation struct { - IdTagInfo *IdTagInfo `json:"idTagInfo" validate:"required"` + IdTagInfo *types.IdTagInfo `json:"idTagInfo" validate:"required"` } // Before the owner of an electric vehicle can start or stop charging, the Charge Point has to authorize the operation. @@ -33,7 +36,7 @@ func (f AuthorizeFeature) GetRequestType() reflect.Type { return reflect.TypeOf(AuthorizeRequest{}) } -func (f AuthorizeFeature) GetConfirmationType() reflect.Type { +func (f AuthorizeFeature) GetResponseType() reflect.Type { return reflect.TypeOf(AuthorizeConfirmation{}) } @@ -51,6 +54,6 @@ func NewAuthorizationRequest(idTag string) *AuthorizeRequest { } // Creates a new AuthorizeConfirmation. There are no optional fields for this message. -func NewAuthorizationConfirmation(idTagInfo *IdTagInfo) *AuthorizeConfirmation { +func NewAuthorizationConfirmation(idTagInfo *types.IdTagInfo) *AuthorizeConfirmation { return &AuthorizeConfirmation{IdTagInfo: idTagInfo} } diff --git a/ocpp1.6/boot_notification.go b/ocpp1.6/core/boot_notification.go similarity index 90% rename from ocpp1.6/boot_notification.go rename to ocpp1.6/core/boot_notification.go index eb6d6da4..e1b150f0 100644 --- a/ocpp1.6/boot_notification.go +++ b/ocpp1.6/core/boot_notification.go @@ -1,12 +1,15 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Boot Notification (CP -> CS) -------------------- +const BootNotificationFeatureName = "BootNotification" + // Result of registration in response to a BootNotification request. type RegistrationStatus string @@ -42,7 +45,7 @@ type BootNotificationRequest struct { // This field definition of the BootNotification confirmation payload, sent by the Central System to the Charge Point in response to a BootNotificationRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. type BootNotificationConfirmation struct { - CurrentTime *DateTime `json:"currentTime" validate:"required"` + CurrentTime *types.DateTime `json:"currentTime" validate:"required"` Interval int `json:"interval" validate:"gte=0"` Status RegistrationStatus `json:"status" validate:"required,registrationStatus"` } @@ -65,7 +68,7 @@ func (f BootNotificationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(BootNotificationRequest{}) } -func (f BootNotificationFeature) GetConfirmationType() reflect.Type { +func (f BootNotificationFeature) GetResponseType() reflect.Type { return reflect.TypeOf(BootNotificationConfirmation{}) } @@ -83,10 +86,10 @@ func NewBootNotificationRequest(chargePointModel string, chargePointVendor strin } // Creates a new BootNotificationConfirmation. There are no optional fields for this message. -func NewBootNotificationConfirmation(currentTime *DateTime, interval int, status RegistrationStatus) *BootNotificationConfirmation { +func NewBootNotificationConfirmation(currentTime *types.DateTime, interval int, status RegistrationStatus) *BootNotificationConfirmation { return &BootNotificationConfirmation{CurrentTime: currentTime, Interval: interval, Status: status} } func init() { - _ = Validate.RegisterValidation("registrationStatus", isValidRegistrationStatus) + _ = types.Validate.RegisterValidation("registrationStatus", isValidRegistrationStatus) } diff --git a/ocpp1.6/change_availability.go b/ocpp1.6/core/change_availability.go similarity index 90% rename from ocpp1.6/change_availability.go rename to ocpp1.6/core/change_availability.go index c56cf148..b0ce5be8 100644 --- a/ocpp1.6/change_availability.go +++ b/ocpp1.6/core/change_availability.go @@ -1,12 +1,15 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Change Availability (CS -> CP) -------------------- +const ChangeAvailabilityFeatureName = "ChangeAvailability" + // Requested availability change in ChangeAvailabilityRequest. type AvailabilityType string @@ -71,7 +74,7 @@ func (f ChangeAvailabilityFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ChangeAvailabilityRequest{}) } -func (f ChangeAvailabilityFeature) GetConfirmationType() reflect.Type { +func (f ChangeAvailabilityFeature) GetResponseType() reflect.Type { return reflect.TypeOf(ChangeAvailabilityConfirmation{}) } @@ -94,6 +97,6 @@ func NewChangeAvailabilityConfirmation(status AvailabilityStatus) *ChangeAvailab } func init() { - _ = Validate.RegisterValidation("availabilityType", isValidAvailabilityType) - _ = Validate.RegisterValidation("availabilityStatus", isValidAvailabilityStatus) + _ = types.Validate.RegisterValidation("availabilityType", isValidAvailabilityType) + _ = types.Validate.RegisterValidation("availabilityStatus", isValidAvailabilityStatus) } diff --git a/ocpp1.6/change_configuration.go b/ocpp1.6/core/change_configuration.go similarity index 93% rename from ocpp1.6/change_configuration.go rename to ocpp1.6/core/change_configuration.go index 29c21da3..a0e250fd 100644 --- a/ocpp1.6/change_configuration.go +++ b/ocpp1.6/core/change_configuration.go @@ -1,12 +1,15 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Change Configuration (CS -> CP) -------------------- +const ChangeConfigurationFeatureName = "ChangeConfiguration" + // Status in ChangeConfigurationConfirmation. type ConfigurationStatus string @@ -59,7 +62,7 @@ func (f ChangeConfigurationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ChangeConfigurationRequest{}) } -func (f ChangeConfigurationFeature) GetConfirmationType() reflect.Type { +func (f ChangeConfigurationFeature) GetResponseType() reflect.Type { return reflect.TypeOf(ChangeConfigurationConfirmation{}) } @@ -82,5 +85,5 @@ func NewChangeConfigurationConfirmation(status ConfigurationStatus) *ChangeConfi } func init() { - _ = Validate.RegisterValidation("configurationStatus", isValidConfigurationStatus) + _ = types.Validate.RegisterValidation("configurationStatus", isValidConfigurationStatus) } diff --git a/ocpp1.6/clear_cache.go b/ocpp1.6/core/clear_cache.go similarity index 90% rename from ocpp1.6/clear_cache.go rename to ocpp1.6/core/clear_cache.go index 80f9b034..495741aa 100644 --- a/ocpp1.6/clear_cache.go +++ b/ocpp1.6/core/clear_cache.go @@ -1,12 +1,15 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Clear Cache (CS -> CP) -------------------- +const ClearCacheFeatureName = "ClearCache" + // Status returned in response to ClearCacheRequest. type ClearCacheStatus string @@ -49,7 +52,7 @@ func (f ClearCacheFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ClearCacheRequest{}) } -func (f ClearCacheFeature) GetConfirmationType() reflect.Type { +func (f ClearCacheFeature) GetResponseType() reflect.Type { return reflect.TypeOf(ClearCacheConfirmation{}) } @@ -72,5 +75,5 @@ func NewClearCacheConfirmation(status ClearCacheStatus) *ClearCacheConfirmation } func init() { - _ = Validate.RegisterValidation("cacheStatus", isValidClearCacheStatus) + _ = types.Validate.RegisterValidation("cacheStatus", isValidClearCacheStatus) } diff --git a/ocpp1.6/core.go b/ocpp1.6/core/core.go similarity index 69% rename from ocpp1.6/core.go rename to ocpp1.6/core/core.go index 873e5b55..cf6e0d98 100644 --- a/ocpp1.6/core.go +++ b/ocpp1.6/core/core.go @@ -1,29 +1,12 @@ -package ocpp16 +// Contains the Basic Charge Point functionality comparable with OCPP 1.5. +package core import ( "github.com/lorenzodonini/ocpp-go/ocpp" ) -const ( - BootNotificationFeatureName = "BootNotification" - AuthorizeFeatureName = "Authorize" - ChangeAvailabilityFeatureName = "ChangeAvailability" - ChangeConfigurationFeatureName = "ChangeConfiguration" - DataTransferFeatureName = "DataTransfer" - GetConfigurationFeatureName = "GetConfiguration" - ClearCacheFeatureName = "ClearCache" - HeartbeatFeatureName = "Heartbeat" - MeterValuesFeatureName = "MeterValues" - RemoteStartTransactionFeatureName = "RemoteStartTransaction" - RemoteStopTransactionFeatureName = "RemoteStopTransaction" - ResetFeatureName = "Reset" - StartTransactionFeatureName = "StartTransaction" - StopTransactionFeatureName = "StopTransaction" - StatusNotificationFeatureName = "StatusNotification" - UnlockConnectorFeatureName = "UnlockConnector" -) - -type CentralSystemCoreListener interface { +// Needs to be implemented by Central systems for handling messages part of the OCPP 1.6 Core profile. +type CentralSystemCoreHandler interface { OnAuthorize(chargePointId string, request *AuthorizeRequest) (confirmation *AuthorizeConfirmation, err error) OnBootNotification(chargePointId string, request *BootNotificationRequest) (confirmation *BootNotificationConfirmation, err error) OnDataTransfer(chargePointId string, request *DataTransferRequest) (confirmation *DataTransferConfirmation, err error) @@ -34,7 +17,8 @@ type CentralSystemCoreListener interface { OnStopTransaction(chargePointId string, request *StopTransactionRequest) (confirmation *StopTransactionConfirmation, err error) } -type ChargePointCoreListener interface { +// Needs to be implemented by Charge points for handling messages part of the OCPP 1.6 Core profile. +type ChargePointCoreHandler interface { OnChangeAvailability(request *ChangeAvailabilityRequest) (confirmation *ChangeAvailabilityConfirmation, err error) OnChangeConfiguration(request *ChangeConfigurationRequest) (confirmation *ChangeConfigurationConfirmation, err error) OnClearCache(request *ClearCacheRequest) (confirmation *ClearCacheConfirmation, err error) @@ -46,10 +30,12 @@ type ChargePointCoreListener interface { OnUnlockConnector(request *UnlockConnectorRequest) (confirmation *UnlockConnectorConfirmation, err error) } -var CoreProfileName = "core" +// THe profile name +var ProfileName = "core" -var CoreProfile = ocpp.NewProfile( - CoreProfileName, +// Provides support for Basic Charge Point functionality comparable with OCPP 1.5. +var Profile = ocpp.NewProfile( + ProfileName, BootNotificationFeature{}, AuthorizeFeature{}, ChangeAvailabilityFeature{}, diff --git a/ocpp1.6/data_transfer.go b/ocpp1.6/core/data_transfer.go similarity index 91% rename from ocpp1.6/data_transfer.go rename to ocpp1.6/core/data_transfer.go index 94c833a3..8e56e231 100644 --- a/ocpp1.6/data_transfer.go +++ b/ocpp1.6/core/data_transfer.go @@ -1,12 +1,15 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Data Transfer (CP -> CS / CS -> CP) -------------------- +const DataTransferFeatureName = "DataTransfer" + // Status in DataTransferConfirmation messages. type DataTransferStatus string @@ -53,7 +56,7 @@ func (f DataTransferFeature) GetRequestType() reflect.Type { return reflect.TypeOf(DataTransferRequest{}) } -func (f DataTransferFeature) GetConfirmationType() reflect.Type { +func (f DataTransferFeature) GetResponseType() reflect.Type { return reflect.TypeOf(DataTransferConfirmation{}) } @@ -76,5 +79,5 @@ func NewDataTransferConfirmation(status DataTransferStatus) *DataTransferConfirm } func init() { - _ = Validate.RegisterValidation("dataTransferStatus", isValidDataTransferStatus) + _ = types.Validate.RegisterValidation("dataTransferStatus", isValidDataTransferStatus) } diff --git a/ocpp1.6/get_configuration.go b/ocpp1.6/core/get_configuration.go similarity index 95% rename from ocpp1.6/get_configuration.go rename to ocpp1.6/core/get_configuration.go index 2198e1dc..b6bad092 100644 --- a/ocpp1.6/get_configuration.go +++ b/ocpp1.6/core/get_configuration.go @@ -1,4 +1,4 @@ -package ocpp16 +package core import ( "reflect" @@ -6,6 +6,8 @@ import ( // -------------------- Get Configuration (CS -> CP) -------------------- +const GetConfigurationFeatureName = "GetConfiguration" + // Contains information about a specific configuration key. It is returned in GetConfigurationConfirmation type ConfigurationKey struct { Key string `json:"key" validate:"required,max=50"` @@ -43,7 +45,7 @@ func (f GetConfigurationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetConfigurationRequest{}) } -func (f GetConfigurationFeature) GetConfirmationType() reflect.Type { +func (f GetConfigurationFeature) GetResponseType() reflect.Type { return reflect.TypeOf(GetConfigurationConfirmation{}) } diff --git a/ocpp1.6/heartbeat.go b/ocpp1.6/core/heartbeat.go similarity index 84% rename from ocpp1.6/heartbeat.go rename to ocpp1.6/core/heartbeat.go index 8969cd01..a49406cb 100644 --- a/ocpp1.6/heartbeat.go +++ b/ocpp1.6/core/heartbeat.go @@ -1,12 +1,15 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Heartbeat (CP -> CS) -------------------- +const HeartbeatFeatureName = "Heartbeat" + // The field definition of the Heartbeat request payload sent by the Charge Point to the Central System. type HeartbeatRequest struct { } @@ -14,7 +17,7 @@ type HeartbeatRequest struct { // This field definition of the Heartbeat confirmation payload, sent by the Central System to the Charge Point in response to a HeartbeatRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. type HeartbeatConfirmation struct { - CurrentTime *DateTime `json:"currentTime" validate:"required"` + CurrentTime *types.DateTime `json:"currentTime" validate:"required"` } // To let the Central System know that a Charge Point is still connected, a Charge Point sends a heartbeat after a configurable time interval. @@ -34,7 +37,7 @@ func (f HeartbeatFeature) GetRequestType() reflect.Type { return reflect.TypeOf(HeartbeatRequest{}) } -func (f HeartbeatFeature) GetConfirmationType() reflect.Type { +func (f HeartbeatFeature) GetResponseType() reflect.Type { return reflect.TypeOf(HeartbeatConfirmation{}) } @@ -52,17 +55,17 @@ func NewHeartbeatRequest() *HeartbeatRequest { } // Creates a new HeartbeatConfirmation, containing all required fields. -func NewHeartbeatConfirmation(currentTime *DateTime) *HeartbeatConfirmation { +func NewHeartbeatConfirmation(currentTime *types.DateTime) *HeartbeatConfirmation { return &HeartbeatConfirmation{CurrentTime: currentTime} } func validateHeartbeatConfirmation(sl validator.StructLevel) { confirmation := sl.Current().Interface().(HeartbeatConfirmation) - if dateTimeIsNull(confirmation.CurrentTime) { + if types.DateTimeIsNull(confirmation.CurrentTime) { sl.ReportError(confirmation.CurrentTime, "CurrentTime", "currentTime", "required", "") } } func init() { - Validate.RegisterStructValidation(validateHeartbeatConfirmation, HeartbeatConfirmation{}) + types.Validate.RegisterStructValidation(validateHeartbeatConfirmation, HeartbeatConfirmation{}) } diff --git a/ocpp1.6/meter_values.go b/ocpp1.6/core/meter_values.go similarity index 90% rename from ocpp1.6/meter_values.go rename to ocpp1.6/core/meter_values.go index c81c0439..d461c954 100644 --- a/ocpp1.6/meter_values.go +++ b/ocpp1.6/core/meter_values.go @@ -1,16 +1,19 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "reflect" ) // -------------------- Heartbeat (CP -> CS) -------------------- +const MeterValuesFeatureName = "MeterValues" + // The field definition of the MeterValues request payload sent by the Charge Point to the Central System. type MeterValuesRequest struct { - ConnectorId int `json:"connectorId" validate:"gte=0"` - TransactionId int `json:"reservationId,omitempty"` - MeterValue []MeterValue `json:"meterValue" validate:"required,min=1,dive"` + ConnectorId int `json:"connectorId" validate:"gte=0"` + TransactionId int `json:"reservationId,omitempty"` + MeterValue []types.MeterValue `json:"meterValue" validate:"required,min=1,dive"` } // This field definition of the Authorize confirmation payload, sent by the Charge Point to the Central System in response to an AuthorizeRequest. @@ -50,7 +53,7 @@ func (f MeterValuesFeature) GetRequestType() reflect.Type { return reflect.TypeOf(MeterValuesRequest{}) } -func (f MeterValuesFeature) GetConfirmationType() reflect.Type { +func (f MeterValuesFeature) GetResponseType() reflect.Type { return reflect.TypeOf(MeterValuesConfirmation{}) } @@ -63,7 +66,7 @@ func (c MeterValuesConfirmation) GetFeatureName() string { } // Creates a new MeterValuesRequest, containing all required fields. Optional fields may be set afterwards. -func NewMeterValuesRequest(connectorId int, meterValues []MeterValue) *MeterValuesRequest { +func NewMeterValuesRequest(connectorId int, meterValues []types.MeterValue) *MeterValuesRequest { return &MeterValuesRequest{ConnectorId: connectorId, MeterValue: meterValues} } diff --git a/ocpp1.6/remote_start_transaction.go b/ocpp1.6/core/remote_start_transaction.go similarity index 86% rename from ocpp1.6/remote_start_transaction.go rename to ocpp1.6/core/remote_start_transaction.go index 67755dc9..5bd93111 100644 --- a/ocpp1.6/remote_start_transaction.go +++ b/ocpp1.6/core/remote_start_transaction.go @@ -1,22 +1,25 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "reflect" ) // -------------------- Remote Start Transaction (CS -> CP) -------------------- +const RemoteStartTransactionFeatureName = "RemoteStartTransaction" + // The field definition of the RemoteStartTransaction request payload sent by the Central System to the Charge Point. type RemoteStartTransactionRequest struct { - ConnectorId int `json:"connectorId,omitempty" validate:"omitempty,gt=0"` - IdTag string `json:"idTag" validate:"required,max=20"` - ChargingProfile *ChargingProfile `json:"chargingProfile,omitempty"` + ConnectorId int `json:"connectorId,omitempty" validate:"omitempty,gt=0"` + IdTag string `json:"idTag" validate:"required,max=20"` + ChargingProfile *types.ChargingProfile `json:"chargingProfile,omitempty"` } // This field definition of the RemoteStartTransaction confirmation payload, sent by the Charge Point to the Central System in response to a RemoteStartTransactionRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. type RemoteStartTransactionConfirmation struct { - Status RemoteStartStopStatus `json:"status" validate:"required,remoteStartStopStatus"` + Status types.RemoteStartStopStatus `json:"status" validate:"required,remoteStartStopStatus"` } // Central System can request a Charge Point to start a transaction by sending a RemoteStartTransactionRequest. @@ -49,7 +52,7 @@ func (f RemoteStartTransactionFeature) GetRequestType() reflect.Type { return reflect.TypeOf(RemoteStartTransactionRequest{}) } -func (f RemoteStartTransactionFeature) GetConfirmationType() reflect.Type { +func (f RemoteStartTransactionFeature) GetResponseType() reflect.Type { return reflect.TypeOf(RemoteStartTransactionConfirmation{}) } @@ -67,6 +70,6 @@ func NewRemoteStartTransactionRequest(idTag string) *RemoteStartTransactionReque } // Creates a new RemoteStartTransactionConfirmation, containing all required fields. There are no optional fields for this message. -func NewRemoteStartTransactionConfirmation(status RemoteStartStopStatus) *RemoteStartTransactionConfirmation { +func NewRemoteStartTransactionConfirmation(status types.RemoteStartStopStatus) *RemoteStartTransactionConfirmation { return &RemoteStartTransactionConfirmation{Status: status} } diff --git a/ocpp1.6/remote_stop_transaction.go b/ocpp1.6/core/remote_stop_transaction.go similarity index 86% rename from ocpp1.6/remote_stop_transaction.go rename to ocpp1.6/core/remote_stop_transaction.go index 465eefba..a17429b2 100644 --- a/ocpp1.6/remote_stop_transaction.go +++ b/ocpp1.6/core/remote_stop_transaction.go @@ -1,11 +1,14 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "reflect" ) // -------------------- Remote Stop Transaction (CS -> CP) -------------------- +const RemoteStopTransactionFeatureName = "RemoteStopTransaction" + // The field definition of the RemoteStopTransaction request payload sent by the Central System to the Charge Point. type RemoteStopTransactionRequest struct { TransactionId int `json:"transactionId" validate:"gte=0"` @@ -14,7 +17,7 @@ type RemoteStopTransactionRequest struct { // This field definition of the RemoteStopTransaction confirmation payload, sent by the Charge Point to the Central System in response to a RemoteStopTransactionRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. type RemoteStopTransactionConfirmation struct { - Status RemoteStartStopStatus `json:"status" validate:"required,remoteStartStopStatus"` + Status types.RemoteStartStopStatus `json:"status" validate:"required,remoteStartStopStatus"` } // Central System can request a Charge Point to stop a transaction by sending a RemoteStopTransactionRequest to Charge Point with the identifier of the transaction. @@ -34,7 +37,7 @@ func (f RemoteStopTransactionFeature) GetRequestType() reflect.Type { return reflect.TypeOf(RemoteStopTransactionRequest{}) } -func (f RemoteStopTransactionFeature) GetConfirmationType() reflect.Type { +func (f RemoteStopTransactionFeature) GetResponseType() reflect.Type { return reflect.TypeOf(RemoteStopTransactionConfirmation{}) } @@ -52,6 +55,6 @@ func NewRemoteStopTransactionRequest(transactionId int) *RemoteStopTransactionRe } // Creates a new RemoteStopTransactionConfirmation, containing all required fields. There are no optional fields for this message. -func NewRemoteStopTransactionConfirmation(status RemoteStartStopStatus) *RemoteStopTransactionConfirmation { +func NewRemoteStopTransactionConfirmation(status types.RemoteStartStopStatus) *RemoteStopTransactionConfirmation { return &RemoteStopTransactionConfirmation{Status: status} } diff --git a/ocpp1.6/reset.go b/ocpp1.6/core/reset.go similarity index 92% rename from ocpp1.6/reset.go rename to ocpp1.6/core/reset.go index 74a366a8..ae035f36 100644 --- a/ocpp1.6/reset.go +++ b/ocpp1.6/core/reset.go @@ -1,12 +1,15 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Reset (CS -> CP) -------------------- +const ResetFeatureName = "Reset" + // Type of reset requested by ResetRequest. type ResetType string @@ -71,7 +74,7 @@ func (f ResetFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ResetRequest{}) } -func (f ResetFeature) GetConfirmationType() reflect.Type { +func (f ResetFeature) GetResponseType() reflect.Type { return reflect.TypeOf(ResetConfirmation{}) } @@ -94,6 +97,6 @@ func NewResetConfirmation(status ResetStatus) *ResetConfirmation { } func init() { - _ = Validate.RegisterValidation("resetType", isValidResetType) - _ = Validate.RegisterValidation("resetStatus", isValidResetStatus) + _ = types.Validate.RegisterValidation("resetType", isValidResetType) + _ = types.Validate.RegisterValidation("resetStatus", isValidResetStatus) } diff --git a/ocpp1.6/start_transaction.go b/ocpp1.6/core/start_transaction.go similarity index 77% rename from ocpp1.6/start_transaction.go rename to ocpp1.6/core/start_transaction.go index 7c82e7ac..68df407b 100644 --- a/ocpp1.6/start_transaction.go +++ b/ocpp1.6/core/start_transaction.go @@ -1,24 +1,27 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "reflect" ) // -------------------- Start Transaction (CP -> CS) -------------------- +const StartTransactionFeatureName = "StartTransaction" + // This field definition of the StartTransactionRequest payload sent by the Charge Point to the Central System. type StartTransactionRequest struct { - ConnectorId int `json:"connectorId" validate:"gt=0"` - IdTag string `json:"idTag" validate:"required,max=20"` - MeterStart int `json:"meterStart" validate:"gte=0"` - ReservationId int `json:"reservationId,omitempty"` - Timestamp *DateTime `json:"timestamp" validate:"required"` + ConnectorId int `json:"connectorId" validate:"gt=0"` + IdTag string `json:"idTag" validate:"required,max=20"` + MeterStart int `json:"meterStart" validate:"gte=0"` + ReservationId int `json:"reservationId,omitempty"` + Timestamp *types.DateTime `json:"timestamp" validate:"required"` } // This field definition of the StartTransactionConfirmation payload sent by the Central System to the Charge Point in response to a StartTransactionRequest. type StartTransactionConfirmation struct { - IdTagInfo *IdTagInfo `json:"idTagInfo" validate:"required"` - TransactionId int `json:"transactionId" validate:"gte=0"` + IdTagInfo *types.IdTagInfo `json:"idTagInfo" validate:"required"` + TransactionId int `json:"transactionId" validate:"gte=0"` } // The Charge Point SHALL send a StartTransactionRequest to the Central System to inform about a transaction that has been started. @@ -40,7 +43,7 @@ func (f StartTransactionFeature) GetRequestType() reflect.Type { return reflect.TypeOf(StartTransactionRequest{}) } -func (f StartTransactionFeature) GetConfirmationType() reflect.Type { +func (f StartTransactionFeature) GetResponseType() reflect.Type { return reflect.TypeOf(StartTransactionConfirmation{}) } @@ -53,11 +56,11 @@ func (c StartTransactionConfirmation) GetFeatureName() string { } // Creates a new StartTransaction request. All signature parameters are required fields. Optional fields may be set directly on the created request. -func NewStartTransactionRequest(connectorId int, idTag string, meterStart int, timestamp *DateTime) *StartTransactionRequest { +func NewStartTransactionRequest(connectorId int, idTag string, meterStart int, timestamp *types.DateTime) *StartTransactionRequest { return &StartTransactionRequest{ConnectorId: connectorId, IdTag: idTag, MeterStart: meterStart, Timestamp: timestamp} } // Creates a new StartTransaction confirmation. All signature parameters are required fields. There are no optional fields for this message. -func NewStartTransactionConfirmation(idTagInfo *IdTagInfo, transactionId int) *StartTransactionConfirmation { +func NewStartTransactionConfirmation(idTagInfo *types.IdTagInfo, transactionId int) *StartTransactionConfirmation { return &StartTransactionConfirmation{IdTagInfo: idTagInfo, TransactionId: transactionId} } diff --git a/ocpp1.6/status_notification.go b/ocpp1.6/core/status_notification.go similarity index 94% rename from ocpp1.6/status_notification.go rename to ocpp1.6/core/status_notification.go index 3f671d04..4e2efdc9 100644 --- a/ocpp1.6/status_notification.go +++ b/ocpp1.6/core/status_notification.go @@ -1,12 +1,15 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Status Notification (CP -> CS) -------------------- +const StatusNotificationFeatureName = "StatusNotification" + // Charge Point status reported in StatusNotificationRequest. type ChargePointErrorCode string @@ -69,7 +72,7 @@ type StatusNotificationRequest struct { ErrorCode ChargePointErrorCode `json:"errorCode" validate:"required,chargePointErrorCode"` Info string `json:"info,omitempty" validate:"max=50"` Status ChargePointStatus `json:"status" validate:"required,chargePointStatus"` - Timestamp DateTime `json:"timestamp,omitempty"` + Timestamp types.DateTime `json:"timestamp,omitempty"` VendorId string `json:"vendorId,omitempty" validate:"max=255"` VendorErrorCode string `json:"vendorErrorCode,omitempty" validate:"max=50"` } @@ -105,7 +108,7 @@ func (f StatusNotificationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(StatusNotificationRequest{}) } -func (f StatusNotificationFeature) GetConfirmationType() reflect.Type { +func (f StatusNotificationFeature) GetResponseType() reflect.Type { return reflect.TypeOf(StatusNotificationConfirmation{}) } @@ -129,6 +132,6 @@ func NewStatusNotificationConfirmation() *StatusNotificationConfirmation { } func init() { - _ = Validate.RegisterValidation("chargePointErrorCode", isValidChargePointErrorCode) - _ = Validate.RegisterValidation("chargePointStatus", isValidChargePointStatus) + _ = types.Validate.RegisterValidation("chargePointErrorCode", isValidChargePointErrorCode) + _ = types.Validate.RegisterValidation("chargePointStatus", isValidChargePointStatus) } diff --git a/ocpp1.6/stop_transaction.go b/ocpp1.6/core/stop_transaction.go similarity index 85% rename from ocpp1.6/stop_transaction.go rename to ocpp1.6/core/stop_transaction.go index 9f0428b9..2ca5bd20 100644 --- a/ocpp1.6/stop_transaction.go +++ b/ocpp1.6/core/stop_transaction.go @@ -1,12 +1,15 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Stop Transaction (CP -> CS) -------------------- +const StopTransactionFeatureName = "StopTransaction" + // Reason for stopping a transaction in StopTransactionRequest. type Reason string @@ -36,18 +39,18 @@ func isValidReason(fl validator.FieldLevel) bool { // The field definition of the StopTransaction request payload sent by the Charge Point to the Central System. type StopTransactionRequest struct { - IdTag string `json:"idTag,omitempty" validate:"max=20"` - MeterStop int `json:"meterStop" validate:"gte=0"` - Timestamp *DateTime `json:"timestamp" validate:"required"` - TransactionId int `json:"transactionId" validate:"gte=0"` - Reason Reason `json:"reason,omitempty" validate:"omitempty,reason"` - TransactionData []MeterValue `json:"transactionData,omitempty" validate:"omitempty,dive"` + IdTag string `json:"idTag,omitempty" validate:"max=20"` + MeterStop int `json:"meterStop" validate:"gte=0"` + Timestamp *types.DateTime `json:"timestamp" validate:"required"` + TransactionId int `json:"transactionId" validate:"gte=0"` + Reason Reason `json:"reason,omitempty" validate:"omitempty,reason"` + TransactionData []types.MeterValue `json:"transactionData,omitempty" validate:"omitempty,dive"` } // This field definition of the StopTransaction confirmation payload, sent by the Central System to the Charge Point in response to a StopTransactionRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. type StopTransactionConfirmation struct { - IdTagInfo *IdTagInfo `json:"idTagInfo,omitempty" validate:"omitempty"` + IdTagInfo *types.IdTagInfo `json:"idTagInfo,omitempty" validate:"omitempty"` } // When a transaction is stopped, the Charge Point SHALL send a StopTransactionRequest, notifying to the Central System that the transaction has stopped. @@ -80,7 +83,7 @@ func (f StopTransactionFeature) GetRequestType() reflect.Type { return reflect.TypeOf(StopTransactionRequest{}) } -func (f StopTransactionFeature) GetConfirmationType() reflect.Type { +func (f StopTransactionFeature) GetResponseType() reflect.Type { return reflect.TypeOf(StopTransactionConfirmation{}) } @@ -94,7 +97,7 @@ func (c StopTransactionConfirmation) GetFeatureName() string { // Creates a new StopTransactionRequest, containing all required fields. // Optional fields may be set directly on the created request. -func NewStopTransactionRequest(meterStop int, timestamp *DateTime, transactionId int) *StopTransactionRequest { +func NewStopTransactionRequest(meterStop int, timestamp *types.DateTime, transactionId int) *StopTransactionRequest { return &StopTransactionRequest{MeterStop: meterStop, Timestamp: timestamp, TransactionId: transactionId} } @@ -105,5 +108,5 @@ func NewStopTransactionConfirmation() *StopTransactionConfirmation { //TODO: advanced validation func init() { - _ = Validate.RegisterValidation("reason", isValidReason) + _ = types.Validate.RegisterValidation("reason", isValidReason) } diff --git a/ocpp1.6/unlock_connector.go b/ocpp1.6/core/unlock_connector.go similarity index 92% rename from ocpp1.6/unlock_connector.go rename to ocpp1.6/core/unlock_connector.go index d956776c..5d3d764c 100644 --- a/ocpp1.6/unlock_connector.go +++ b/ocpp1.6/core/unlock_connector.go @@ -1,12 +1,15 @@ -package ocpp16 +package core import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Unlock Connector (CS -> CP) -------------------- +const UnlockConnectorFeatureName = "UnlockConnector" + // Status in response to UnlockConnectorRequest. type UnlockStatus string @@ -55,7 +58,7 @@ func (f UnlockConnectorFeature) GetRequestType() reflect.Type { return reflect.TypeOf(UnlockConnectorRequest{}) } -func (f UnlockConnectorFeature) GetConfirmationType() reflect.Type { +func (f UnlockConnectorFeature) GetResponseType() reflect.Type { return reflect.TypeOf(UnlockConnectorConfirmation{}) } @@ -78,5 +81,5 @@ func NewUnlockConnectorConfirmation(status UnlockStatus) *UnlockConnectorConfirm } func init() { - _ = Validate.RegisterValidation("unlockStatus", isValidUnlockStatus) + _ = types.Validate.RegisterValidation("unlockStatus", isValidUnlockStatus) } diff --git a/ocpp1.6/diagnostics_status_notification.go b/ocpp1.6/firmware/diagnostics_status_notification.go similarity index 90% rename from ocpp1.6/diagnostics_status_notification.go rename to ocpp1.6/firmware/diagnostics_status_notification.go index 5a7b5f01..5dcfb81f 100644 --- a/ocpp1.6/diagnostics_status_notification.go +++ b/ocpp1.6/firmware/diagnostics_status_notification.go @@ -1,12 +1,15 @@ -package ocpp16 +package firmware import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Diagnostics Status Notification (CP -> CS) -------------------- +const DiagnosticsStatusNotificationFeatureName = "DiagnosticsStatusNotification" + // Status reported in DiagnosticsStatusNotificationRequest. type DiagnosticsStatus string @@ -49,7 +52,7 @@ func (f DiagnosticsStatusNotificationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(DiagnosticsStatusNotificationRequest{}) } -func (f DiagnosticsStatusNotificationFeature) GetConfirmationType() reflect.Type { +func (f DiagnosticsStatusNotificationFeature) GetResponseType() reflect.Type { return reflect.TypeOf(DiagnosticsStatusNotificationConfirmation{}) } @@ -72,5 +75,5 @@ func NewDiagnosticsStatusNotificationConfirmation() *DiagnosticsStatusNotificati } func init() { - _ = Validate.RegisterValidation("diagnosticsStatus", isValidDiagnosticsStatus) + _ = types.Validate.RegisterValidation("diagnosticsStatus", isValidDiagnosticsStatus) } diff --git a/ocpp1.6/firmware.go b/ocpp1.6/firmware/firmware.go similarity index 56% rename from ocpp1.6/firmware.go rename to ocpp1.6/firmware/firmware.go index 01d72cbd..743fc5da 100644 --- a/ocpp1.6/firmware.go +++ b/ocpp1.6/firmware/firmware.go @@ -1,29 +1,28 @@ -package ocpp16 +// Contains support for firmware update management and diagnostic log file download. +package firmware -import "github.com/lorenzodonini/ocpp-go/ocpp" - -const ( - GetDiagnosticsFeatureName = "GetDiagnostics" - DiagnosticsStatusNotificationFeatureName = "DiagnosticsStatusNotification" - FirmwareStatusNotificationFeatureName = "FirmwareStatusNotification" - UpdateFirmwareFeatureName = "UpdateFirmware" +import ( + "github.com/lorenzodonini/ocpp-go/ocpp" ) -type CentralSystemFirmwareManagementListener interface { +// Needs to be implemented by Central systems for handling messages part of the OCPP 1.6 FirmwareManagement profile. +type CentralSystemFirmwareManagementHandler interface { OnDiagnosticsStatusNotification(chargePointId string, request *DiagnosticsStatusNotificationRequest) (confirmation *DiagnosticsStatusNotificationConfirmation, err error) OnFirmwareStatusNotification(chargePointId string, request *FirmwareStatusNotificationRequest) (confirmation *FirmwareStatusNotificationConfirmation, err error) } -type ChargePointFirmwareManagementListener interface { +// Needs to be implemented by Charge points for handling messages part of the OCPP 1.6 FirmwareManagement profile. +type ChargePointFirmwareManagementHandler interface { OnGetDiagnostics(request *GetDiagnosticsRequest) (confirmation *GetDiagnosticsConfirmation, err error) OnUpdateFirmware(request *UpdateFirmwareRequest) (confirmation *UpdateFirmwareConfirmation, err error) } -const FirmwareManagementProfileName = "firmwareManagement" +// The profile name +const ProfileName = "firmwareManagement" // Provides support for firmware update management and diagnostic log file download. -var FirmwareManagementProfile = ocpp.NewProfile( - FirmwareManagementProfileName, +var Profile = ocpp.NewProfile( + ProfileName, GetDiagnosticsFeature{}, DiagnosticsStatusNotificationFeature{}, FirmwareStatusNotificationFeature{}, diff --git a/ocpp1.6/firmware_status_notification.go b/ocpp1.6/firmware/firmware_status_notification.go similarity index 91% rename from ocpp1.6/firmware_status_notification.go rename to ocpp1.6/firmware/firmware_status_notification.go index 9e4365b8..d85557b0 100644 --- a/ocpp1.6/firmware_status_notification.go +++ b/ocpp1.6/firmware/firmware_status_notification.go @@ -1,12 +1,15 @@ -package ocpp16 +package firmware import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Firmware Status Notification (CP -> CS) -------------------- +const FirmwareStatusNotificationFeatureName = "FirmwareStatusNotification" + // Status reported in FirmwareStatusNotificationRequest. type FirmwareStatus string @@ -53,7 +56,7 @@ func (f FirmwareStatusNotificationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(FirmwareStatusNotificationRequest{}) } -func (f FirmwareStatusNotificationFeature) GetConfirmationType() reflect.Type { +func (f FirmwareStatusNotificationFeature) GetResponseType() reflect.Type { return reflect.TypeOf(FirmwareStatusNotificationConfirmation{}) } @@ -76,5 +79,5 @@ func NewFirmwareStatusNotificationConfirmation() *FirmwareStatusNotificationConf } func init() { - _ = Validate.RegisterValidation("firmwareStatus", isValidFirmwareStatus) + _ = types.Validate.RegisterValidation("firmwareStatus", isValidFirmwareStatus) } diff --git a/ocpp1.6/get_diagnostics.go b/ocpp1.6/firmware/get_diagnostics.go similarity index 81% rename from ocpp1.6/get_diagnostics.go rename to ocpp1.6/firmware/get_diagnostics.go index 0f592f02..cb218ca6 100644 --- a/ocpp1.6/get_diagnostics.go +++ b/ocpp1.6/firmware/get_diagnostics.go @@ -1,18 +1,21 @@ -package ocpp16 +package firmware import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "reflect" ) // -------------------- Get Diagnostics (CS -> CP) -------------------- +const GetDiagnosticsFeatureName = "GetDiagnostics" + // The field definition of the GetDiagnostics request payload sent by the Central System to the Charge Point. type GetDiagnosticsRequest struct { - Location string `json:"location" validate:"required,uri"` - Retries int `json:"retries" validate:"gte=0"` //TODO: pointer? - RetryInterval int `json:"retryInterval" validate:"gte=0"` //TODO: pointer? - StartTime *DateTime `json:"startTime,omitempty"` - EndTime *DateTime `json:"endTime,omitempty"` + Location string `json:"location" validate:"required,uri"` + Retries int `json:"retries" validate:"gte=0"` //TODO: pointer? + RetryInterval int `json:"retryInterval" validate:"gte=0"` //TODO: pointer? + StartTime *types.DateTime `json:"startTime,omitempty"` + EndTime *types.DateTime `json:"endTime,omitempty"` } // This field definition of the GetDiagnostics confirmation payload, sent by the Charge Point to the Central System in response to a GetDiagnosticsRequest. @@ -38,7 +41,7 @@ func (f GetDiagnosticsFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetDiagnosticsRequest{}) } -func (f GetDiagnosticsFeature) GetConfirmationType() reflect.Type { +func (f GetDiagnosticsFeature) GetResponseType() reflect.Type { return reflect.TypeOf(GetDiagnosticsConfirmation{}) } diff --git a/ocpp1.6/update_firmware.go b/ocpp1.6/firmware/update_firmware.go similarity index 81% rename from ocpp1.6/update_firmware.go rename to ocpp1.6/firmware/update_firmware.go index 9bd2f007..fb31c570 100644 --- a/ocpp1.6/update_firmware.go +++ b/ocpp1.6/firmware/update_firmware.go @@ -1,17 +1,20 @@ -package ocpp16 +package firmware import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "reflect" ) // -------------------- Get Diagnostics (CS -> CP) -------------------- +const UpdateFirmwareFeatureName = "UpdateFirmware" + // The field definition of the UpdateFirmware request payload sent by the Central System to the Charge Point. type UpdateFirmwareRequest struct { - Location string `json:"location" validate:"required,uri"` - Retries int `json:"retries" validate:"gte=0"` //TODO: pointer? - RetrieveDate *DateTime `json:"retrieveDate" validate:"required"` - RetryInterval int `json:"retryInterval" validate:"gte=0"` //TODO: pointer? + Location string `json:"location" validate:"required,uri"` + Retries int `json:"retries" validate:"gte=0"` //TODO: pointer? + RetrieveDate *types.DateTime `json:"retrieveDate" validate:"required"` + RetryInterval int `json:"retryInterval" validate:"gte=0"` //TODO: pointer? } // This field definition of the UpdateFirmware confirmation payload, sent by the Charge Point to the Central System in response to a UpdateFirmwareRequest. @@ -37,7 +40,7 @@ func (f UpdateFirmwareFeature) GetRequestType() reflect.Type { return reflect.TypeOf(UpdateFirmwareRequest{}) } -func (f UpdateFirmwareFeature) GetConfirmationType() reflect.Type { +func (f UpdateFirmwareFeature) GetResponseType() reflect.Type { return reflect.TypeOf(UpdateFirmwareConfirmation{}) } @@ -50,7 +53,7 @@ func (c UpdateFirmwareConfirmation) GetFeatureName() string { } // Creates a new UpdateFirmwareRequest, which doesn't contain any required or optional fields. -func NewUpdateFirmwareRequest(location string, retrieveDate *DateTime) *UpdateFirmwareRequest { +func NewUpdateFirmwareRequest(location string, retrieveDate *types.DateTime) *UpdateFirmwareRequest { return &UpdateFirmwareRequest{Location: location, RetrieveDate: retrieveDate} } diff --git a/ocpp1.6/local_auth_list.go b/ocpp1.6/local_auth_list.go deleted file mode 100644 index e580cf2a..00000000 --- a/ocpp1.6/local_auth_list.go +++ /dev/null @@ -1,23 +0,0 @@ -package ocpp16 - -import "github.com/lorenzodonini/ocpp-go/ocpp" - -const ( - GetLocalListVersionFeatureName = "GetLocalListVersion" - SendLocalListFeatureName = "SendLocalList" -) - -type CentralSystemLocalAuthListListener interface { -} - -type ChargePointLocalAuthListListener interface { - OnGetLocalListVersion(request *GetLocalListVersionRequest) (confirmation *GetLocalListVersionConfirmation, err error) - OnSendLocalList(request *SendLocalListRequest) (confirmation *SendLocalListConfirmation, err error) -} - -const LocalAuthListProfileName = "localAuthList" - -var LocalAuthListProfile = ocpp.NewProfile( - LocalAuthListProfileName, - GetLocalListVersionFeature{}, - SendLocalListFeature{}) diff --git a/ocpp1.6/get_local_list_version.go b/ocpp1.6/localauth/get_local_list_version.go similarity index 93% rename from ocpp1.6/get_local_list_version.go rename to ocpp1.6/localauth/get_local_list_version.go index c62f7691..b08bfbd3 100644 --- a/ocpp1.6/get_local_list_version.go +++ b/ocpp1.6/localauth/get_local_list_version.go @@ -1,4 +1,4 @@ -package ocpp16 +package localauth import ( "reflect" @@ -6,6 +6,8 @@ import ( // -------------------- Get Local List Version (CS -> CP) -------------------- +const GetLocalListVersionFeatureName = "GetLocalListVersion" + // The field definition of the GetLocalListVersion request payload sent by the Central System to the Charge Point. type GetLocalListVersionRequest struct { } @@ -31,7 +33,7 @@ func (f GetLocalListVersionFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetLocalListVersionRequest{}) } -func (f GetLocalListVersionFeature) GetConfirmationType() reflect.Type { +func (f GetLocalListVersionFeature) GetResponseType() reflect.Type { return reflect.TypeOf(GetLocalListVersionConfirmation{}) } diff --git a/ocpp1.6/localauth/local_auth_list.go b/ocpp1.6/localauth/local_auth_list.go new file mode 100644 index 00000000..a87abf25 --- /dev/null +++ b/ocpp1.6/localauth/local_auth_list.go @@ -0,0 +1,23 @@ +// Contains features to manage the local authorization list in Charge Points. +package localauth + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by Central systems for handling messages part of the OCPP 1.6 LocalAuthList profile. +type CentralSystemLocalAuthListHandler interface { +} + +// Needs to be implemented by Charge points for handling messages part of the OCPP 1.6 LocalAuthList profile. +type ChargePointLocalAuthListHandler interface { + OnGetLocalListVersion(request *GetLocalListVersionRequest) (confirmation *GetLocalListVersionConfirmation, err error) + OnSendLocalList(request *SendLocalListRequest) (confirmation *SendLocalListConfirmation, err error) +} + +// The profile name +const ProfileName = "localAuthList" + +// Provides support for managing the local authorization list in Charge Points. +var Profile = ocpp.NewProfile( + ProfileName, + GetLocalListVersionFeature{}, + SendLocalListFeature{}) diff --git a/ocpp1.6/send_local_list.go b/ocpp1.6/localauth/send_local_list.go similarity index 89% rename from ocpp1.6/send_local_list.go rename to ocpp1.6/localauth/send_local_list.go index f186119c..e3bb827c 100644 --- a/ocpp1.6/send_local_list.go +++ b/ocpp1.6/localauth/send_local_list.go @@ -1,11 +1,15 @@ -package ocpp16 +package localauth import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Send Local List (CS -> CP) -------------------- + +const SendLocalListFeatureName = "SendLocalList" + type UpdateType string type UpdateStatus string @@ -39,8 +43,8 @@ func isValidUpdateStatus(fl validator.FieldLevel) bool { } type AuthorizationData struct { - IdTag string `json:"idTag" validate:"required,max=20"` - IdTagInfo *IdTagInfo `json:"idTagInfo,omitempty"` //TODO: validate required if update type is Full + IdTag string `json:"idTag" validate:"required,max=20"` + IdTagInfo *types.IdTagInfo `json:"idTagInfo,omitempty"` //TODO: validate required if update type is Full } // The field definition of the SendLocalList request payload sent by the Central System to the Charge Point. @@ -77,7 +81,7 @@ func (f SendLocalListFeature) GetRequestType() reflect.Type { return reflect.TypeOf(SendLocalListRequest{}) } -func (f SendLocalListFeature) GetConfirmationType() reflect.Type { +func (f SendLocalListFeature) GetResponseType() reflect.Type { return reflect.TypeOf(SendLocalListConfirmation{}) } @@ -100,7 +104,7 @@ func NewSendLocalListConfirmation(status UpdateStatus) *SendLocalListConfirmatio } func init() { - _ = Validate.RegisterValidation("updateStatus", isValidUpdateStatus) - _ = Validate.RegisterValidation("updateType", isValidUpdateType) + _ = types.Validate.RegisterValidation("updateStatus", isValidUpdateStatus) + _ = types.Validate.RegisterValidation("updateType", isValidUpdateType) //TODO: validation for SendLocalListMaxLength } diff --git a/ocpp1.6/remote_trigger.go b/ocpp1.6/remote_trigger.go deleted file mode 100644 index 81c3d39e..00000000 --- a/ocpp1.6/remote_trigger.go +++ /dev/null @@ -1,20 +0,0 @@ -package ocpp16 - -import "github.com/lorenzodonini/ocpp-go/ocpp" - -const ( - TriggerMessageFeatureName = "TriggerMessage" -) - -type CentralSystemRemoteTriggerListener interface { -} - -type ChargePointRemoteTriggerListener interface { - OnTriggerMessage(request *TriggerMessageRequest) (confirmation *TriggerMessageConfirmation, err error) -} - -const RemoteTriggerProfileName = "RemoteTrigger" - -var RemoteTriggerProfile = ocpp.NewProfile( - RemoteTriggerProfileName, - TriggerMessageFeature{}) diff --git a/ocpp1.6/remotetrigger/remote_trigger.go b/ocpp1.6/remotetrigger/remote_trigger.go new file mode 100644 index 00000000..d64a658b --- /dev/null +++ b/ocpp1.6/remotetrigger/remote_trigger.go @@ -0,0 +1,21 @@ +// Contains support for remote triggering of Charge Point initiated messages. +package remotetrigger + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by Central systems for handling messages part of the OCPP 1.6 RemoteTrigger profile. +type CentralSystemRemoteTriggerHandler interface { +} + +// Needs to be implemented by Charge points for handling messages part of the OCPP 1.6 RemoteTrigger profile. +type ChargePointRemoteTriggerHandler interface { + OnTriggerMessage(request *TriggerMessageRequest) (confirmation *TriggerMessageConfirmation, err error) +} + +// The profile name +const ProfileName = "RemoteTrigger" + +// Provides support for remote triggering of Charge Point initiated messages. +var Profile = ocpp.NewProfile( + ProfileName, + TriggerMessageFeature{}) diff --git a/ocpp1.6/trigger_message.go b/ocpp1.6/remotetrigger/trigger_message.go similarity index 89% rename from ocpp1.6/trigger_message.go rename to ocpp1.6/remotetrigger/trigger_message.go index 2be4e0a8..4bbde06e 100644 --- a/ocpp1.6/trigger_message.go +++ b/ocpp1.6/remotetrigger/trigger_message.go @@ -1,12 +1,17 @@ -package ocpp16 +package remotetrigger import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Trigger Message (CS -> CP) -------------------- +const TriggerMessageFeatureName = "TriggerMessage" + // Status reported in TriggerMessageConfirmation. type TriggerMessageStatus string @@ -32,7 +37,7 @@ func isValidTriggerMessageStatus(fl validator.FieldLevel) bool { func isValidMessageTrigger(fl validator.FieldLevel) bool { trigger := MessageTrigger(fl.Field().String()) switch trigger { - case BootNotificationFeatureName, DiagnosticsStatusNotificationFeatureName, FirmwareStatusNotificationFeatureName, HeartbeatFeatureName, MeterValuesFeatureName, StatusNotificationFeatureName: + case core.BootNotificationFeatureName, firmware.DiagnosticsStatusNotificationFeatureName, firmware.FirmwareStatusNotificationFeatureName, core.HeartbeatFeatureName, core.MeterValuesFeatureName, core.StatusNotificationFeatureName: return true default: return false @@ -82,7 +87,7 @@ func (f TriggerMessageFeature) GetRequestType() reflect.Type { return reflect.TypeOf(TriggerMessageRequest{}) } -func (f TriggerMessageFeature) GetConfirmationType() reflect.Type { +func (f TriggerMessageFeature) GetResponseType() reflect.Type { return reflect.TypeOf(TriggerMessageConfirmation{}) } @@ -105,6 +110,6 @@ func NewTriggerMessageConfirmation(status TriggerMessageStatus) *TriggerMessageC } func init() { - _ = Validate.RegisterValidation("triggerMessageStatus", isValidTriggerMessageStatus) - _ = Validate.RegisterValidation("messageTrigger", isValidMessageTrigger) + _ = types.Validate.RegisterValidation("triggerMessageStatus", isValidTriggerMessageStatus) + _ = types.Validate.RegisterValidation("messageTrigger", isValidMessageTrigger) } diff --git a/ocpp1.6/reservation.go b/ocpp1.6/reservation.go deleted file mode 100644 index ac97c3e9..00000000 --- a/ocpp1.6/reservation.go +++ /dev/null @@ -1,23 +0,0 @@ -package ocpp16 - -import "github.com/lorenzodonini/ocpp-go/ocpp" - -const ( - CancelReservationFeatureName = "CancelReservation" - ReserveNowFeatureName = "ReserveNow" -) - -type CentralSystemReservationListener interface { -} - -type ChargePointReservationListener interface { - OnReserveNow(request *ReserveNowRequest) (confirmation *ReserveNowConfirmation, err error) - OnCancelReservation(request *CancelReservationRequest) (confirmation *CancelReservationConfirmation, err error) -} - -const ReservationProfileName = "reservation" - -var ReservationProfile = ocpp.NewProfile( - ReservationProfileName, - ReserveNowFeature{}, - CancelReservationFeature{}) diff --git a/ocpp1.6/cancel_reservation.go b/ocpp1.6/reservation/cancel_reservation.go similarity index 89% rename from ocpp1.6/cancel_reservation.go rename to ocpp1.6/reservation/cancel_reservation.go index f3d48912..1ddd804a 100644 --- a/ocpp1.6/cancel_reservation.go +++ b/ocpp1.6/reservation/cancel_reservation.go @@ -1,12 +1,15 @@ -package ocpp16 +package reservation import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Cancel Reservation (CS -> CP) -------------------- +const CancelReservationFeatureName = "CancelReservation" + // Status reported in CancelReservationConfirmation. type CancelReservationStatus string @@ -49,7 +52,7 @@ func (f CancelReservationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(CancelReservationRequest{}) } -func (f CancelReservationFeature) GetConfirmationType() reflect.Type { +func (f CancelReservationFeature) GetResponseType() reflect.Type { return reflect.TypeOf(CancelReservationConfirmation{}) } @@ -72,5 +75,5 @@ func NewCancelReservationConfirmation(status CancelReservationStatus) *CancelRes } func init() { - _ = Validate.RegisterValidation("cancelReservationStatus", isValidCancelReservationStatus) + _ = types.Validate.RegisterValidation("cancelReservationStatus", isValidCancelReservationStatus) } diff --git a/ocpp1.6/reservation/reservation.go b/ocpp1.6/reservation/reservation.go new file mode 100644 index 00000000..023faa58 --- /dev/null +++ b/ocpp1.6/reservation/reservation.go @@ -0,0 +1,23 @@ +// Contains support for reservation of a Charge Point. +package reservation + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by Central systems for handling messages part of the OCPP 1.6 Reservation profile. +type CentralSystemReservationHandler interface { +} + +// Needs to be implemented by Charge points for handling messages part of the OCPP 1.6 Reservation profile. +type ChargePointReservationHandler interface { + OnReserveNow(request *ReserveNowRequest) (confirmation *ReserveNowConfirmation, err error) + OnCancelReservation(request *CancelReservationRequest) (confirmation *CancelReservationConfirmation, err error) +} + +// The profile name +const ProfileName = "reservation" + +// Provides support for for reservation of a Charge Point. +var Profile = ocpp.NewProfile( + ProfileName, + ReserveNowFeature{}, + CancelReservationFeature{}) diff --git a/ocpp1.6/reserve_now.go b/ocpp1.6/reservation/reserve_now.go similarity index 85% rename from ocpp1.6/reserve_now.go rename to ocpp1.6/reservation/reserve_now.go index b9b6cc5c..6a58e932 100644 --- a/ocpp1.6/reserve_now.go +++ b/ocpp1.6/reservation/reserve_now.go @@ -1,12 +1,15 @@ -package ocpp16 +package reservation import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Reserve Now (CS -> CP) -------------------- +const ReserveNowFeatureName = "ReserveNow" + // Status reported in ReserveNowConfirmation. type ReservationStatus string @@ -30,11 +33,11 @@ func isValidReservationStatus(fl validator.FieldLevel) bool { // The field definition of the ReserveNow request payload sent by the Central System to the Charge Point. type ReserveNowRequest struct { - ConnectorId int `json:"connectorId" validate:"gte=0"` - ExpiryDate *DateTime `json:"expiryDate" validate:"required"` - IdTag string `json:"idTag" validate:"required,max=20"` - ParentIdTag string `json:"parentIdTag,omitempty" validate:"max=20"` - ReservationId int `json:"reservationId" validate:"gte=0"` + ConnectorId int `json:"connectorId" validate:"gte=0"` + ExpiryDate *types.DateTime `json:"expiryDate" validate:"required"` + IdTag string `json:"idTag" validate:"required,max=20"` + ParentIdTag string `json:"parentIdTag,omitempty" validate:"max=20"` + ReservationId int `json:"reservationId" validate:"gte=0"` } // This field definition of the ReserveNow confirmation payload, sent by the Charge Point to the Central System in response to a ReserveNowRequest. @@ -67,7 +70,7 @@ func (f ReserveNowFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ReserveNowRequest{}) } -func (f ReserveNowFeature) GetConfirmationType() reflect.Type { +func (f ReserveNowFeature) GetResponseType() reflect.Type { return reflect.TypeOf(ReserveNowConfirmation{}) } @@ -80,7 +83,7 @@ func (c ReserveNowConfirmation) GetFeatureName() string { } // Creates a new ReserveNowRequest, containing all required fields. Optional fields may be set afterwards. -func NewReserveNowRequest(connectorId int, expiryDate *DateTime, idTag string, reservationId int) *ReserveNowRequest { +func NewReserveNowRequest(connectorId int, expiryDate *types.DateTime, idTag string, reservationId int) *ReserveNowRequest { return &ReserveNowRequest{ConnectorId: connectorId, ExpiryDate: expiryDate, IdTag: idTag, ReservationId: reservationId} } @@ -90,5 +93,5 @@ func NewReserveNowConfirmation(status ReservationStatus) *ReserveNowConfirmation } func init() { - _ = Validate.RegisterValidation("reservationStatus", isValidReservationStatus) + _ = types.Validate.RegisterValidation("reservationStatus", isValidReservationStatus) } diff --git a/ocpp1.6/smart_charging.go b/ocpp1.6/smart_charging.go deleted file mode 100644 index 560a45c7..00000000 --- a/ocpp1.6/smart_charging.go +++ /dev/null @@ -1,26 +0,0 @@ -package ocpp16 - -import "github.com/lorenzodonini/ocpp-go/ocpp" - -const ( - SetChargingProfileFeatureName = "SetChargingProfile" - ClearChargingProfileFeatureName = "ClearChargingProfile" - GetCompositeScheduleFeatureName = "GetCompositeSchedule" -) - -type CentralSystemSmartChargingListener interface { -} - -type ChargePointSmartChargingListener interface { - OnSetChargingProfile(request *SetChargingProfileRequest) (confirmation *SetChargingProfileConfirmation, err error) - OnClearChargingProfile(request *ClearChargingProfileRequest) (confirmation *ClearChargingProfileConfirmation, err error) - OnGetCompositeSchedule(request *GetCompositeScheduleRequest) (confirmation *GetCompositeScheduleConfirmation, err error) -} - -const SmartChargingProfileName = "SmartCharging" - -var SmartChargingProfile = ocpp.NewProfile( - SmartChargingProfileName, - SetChargingProfileFeature{}, - ClearChargingProfileFeature{}, - GetCompositeScheduleFeature{}) diff --git a/ocpp1.6/clear_charging_profile.go b/ocpp1.6/smartcharging/clear_charging_profile.go similarity index 78% rename from ocpp1.6/clear_charging_profile.go rename to ocpp1.6/smartcharging/clear_charging_profile.go index 3d4d07a8..3679a6cf 100644 --- a/ocpp1.6/clear_charging_profile.go +++ b/ocpp1.6/smartcharging/clear_charging_profile.go @@ -1,12 +1,15 @@ -package ocpp16 +package smartcharging import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Clear Charging Profile (CS -> CP) -------------------- +const ClearChargingProfileFeatureName = "ClearChargingProfile" + // Status reported in ClearChargingProfileConfirmation. type ClearChargingProfileStatus string @@ -27,10 +30,10 @@ func isValidClearChargingProfileStatus(fl validator.FieldLevel) bool { // The field definition of the ClearChargingProfile request payload sent by the Central System to the Charge Point. type ClearChargingProfileRequest struct { - Id int `json:"id,omitempty" validate:"gte=0"` - ConnectorId int `json:"connectorId,omitempty" validate:"gte=0"` //TODO: handle 0 case with pointer? - ChargingProfilePurpose ChargingProfilePurposeType `json:"chargingProfilePurpose,omitempty" validate:"omitempty,chargingProfilePurpose"` - StackLevel int `json:"stackLevel,omitempty" validate:"omitempty,gt=0"` + Id int `json:"id,omitempty" validate:"gte=0"` + ConnectorId int `json:"connectorId,omitempty" validate:"gte=0"` //TODO: handle 0 case with pointer? + ChargingProfilePurpose types.ChargingProfilePurposeType `json:"chargingProfilePurpose,omitempty" validate:"omitempty,chargingProfilePurpose"` + StackLevel int `json:"stackLevel,omitempty" validate:"omitempty,gt=0"` } // This field definition of the ClearChargingProfile confirmation payload, sent by the Charge Point to the Central System in response to a ClearChargingProfileRequest. @@ -54,7 +57,7 @@ func (f ClearChargingProfileFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ClearChargingProfileRequest{}) } -func (f ClearChargingProfileFeature) GetConfirmationType() reflect.Type { +func (f ClearChargingProfileFeature) GetResponseType() reflect.Type { return reflect.TypeOf(ClearChargingProfileConfirmation{}) } @@ -77,5 +80,5 @@ func NewClearChargingProfileConfirmation(status ClearChargingProfileStatus) *Cle } func init() { - _ = Validate.RegisterValidation("clearChargingProfileStatus", isValidClearChargingProfileStatus) + _ = types.Validate.RegisterValidation("clearChargingProfileStatus", isValidClearChargingProfileStatus) } diff --git a/ocpp1.6/get_composite_schedule.go b/ocpp1.6/smartcharging/get_composite_schedule.go similarity index 81% rename from ocpp1.6/get_composite_schedule.go rename to ocpp1.6/smartcharging/get_composite_schedule.go index 3b029c71..363d1dc4 100644 --- a/ocpp1.6/get_composite_schedule.go +++ b/ocpp1.6/smartcharging/get_composite_schedule.go @@ -1,12 +1,15 @@ -package ocpp16 +package smartcharging import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Get Composite Schedule (CS -> CP) -------------------- +const GetCompositeScheduleFeatureName = "GetCompositeSchedule" + // Status reported in GetCompositeScheduleConfirmation. type GetCompositeScheduleStatus string @@ -27,9 +30,9 @@ func isValidGetCompositeScheduleStatus(fl validator.FieldLevel) bool { // The field definition of the GetCompositeSchedule request payload sent by the Central System to the Charge Point. type GetCompositeScheduleRequest struct { - ConnectorId int `json:"connectorId" validate:"gte=0"` - Duration int `json:"duration" validate:"gte=0"` - ChargingRateUnit ChargingRateUnitType `json:"chargingRateUnit,omitempty" validate:"omitempty,chargingRateUnit"` + ConnectorId int `json:"connectorId" validate:"gte=0"` + Duration int `json:"duration" validate:"gte=0"` + ChargingRateUnit types.ChargingRateUnitType `json:"chargingRateUnit,omitempty" validate:"omitempty,chargingRateUnit"` } // This field definition of the GetCompositeSchedule confirmation payload, sent by the Charge Point to the Central System in response to a GetCompositeScheduleRequest. @@ -37,8 +40,8 @@ type GetCompositeScheduleRequest struct { type GetCompositeScheduleConfirmation struct { Status GetCompositeScheduleStatus `json:"status" validate:"required,compositeScheduleStatus"` ConnectorId int `json:"connectorId,omitempty" validate:"omitempty,gt=0"` - ScheduleStart *DateTime `json:"scheduleStart,omitempty"` - ChargingSchedule *ChargingSchedule `json:"chargingSchedule,omitempty" validate:"omitempty"` + ScheduleStart *types.DateTime `json:"scheduleStart,omitempty"` + ChargingSchedule *types.ChargingSchedule `json:"chargingSchedule,omitempty" validate:"omitempty"` } // The CSMS requests the Charging Station to report the Composite Charging Schedule by sending a GetCompositeScheduleRequest. @@ -56,7 +59,7 @@ func (f GetCompositeScheduleFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetCompositeScheduleRequest{}) } -func (f GetCompositeScheduleFeature) GetConfirmationType() reflect.Type { +func (f GetCompositeScheduleFeature) GetResponseType() reflect.Type { return reflect.TypeOf(GetCompositeScheduleConfirmation{}) } @@ -79,5 +82,5 @@ func NewGetCompositeScheduleConfirmation(status GetCompositeScheduleStatus) *Get } func init() { - _ = Validate.RegisterValidation("compositeScheduleStatus", isValidGetCompositeScheduleStatus) + _ = types.Validate.RegisterValidation("compositeScheduleStatus", isValidGetCompositeScheduleStatus) } diff --git a/ocpp1.6/set_charging_profile.go b/ocpp1.6/smartcharging/set_charging_profile.go similarity index 85% rename from ocpp1.6/set_charging_profile.go rename to ocpp1.6/smartcharging/set_charging_profile.go index 2071ae2b..6d55efbd 100644 --- a/ocpp1.6/set_charging_profile.go +++ b/ocpp1.6/smartcharging/set_charging_profile.go @@ -1,12 +1,15 @@ -package ocpp16 +package smartcharging import ( + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Set Charging Profile (CS -> CP) -------------------- +const SetChargingProfileFeatureName = "SetChargingProfile" + // Status reported in SetChargingProfileConfirmation. type ChargingProfileStatus string @@ -28,8 +31,8 @@ func isValidChargingProfileStatus(fl validator.FieldLevel) bool { // The field definition of the SetChargingProfile request payload sent by the Central System to the Charge Point. type SetChargingProfileRequest struct { - ConnectorId int `json:"connectorId" validate:"gte=0"` - ChargingProfile *ChargingProfile `json:"chargingProfile" validate:"required"` + ConnectorId int `json:"connectorId" validate:"gte=0"` + ChargingProfile *types.ChargingProfile `json:"chargingProfile" validate:"required"` } // This field definition of the SetChargingProfile confirmation payload, sent by the Charge Point to the Central System in response to a SetChargingProfileRequest. @@ -52,7 +55,7 @@ func (f SetChargingProfileFeature) GetRequestType() reflect.Type { return reflect.TypeOf(SetChargingProfileRequest{}) } -func (f SetChargingProfileFeature) GetConfirmationType() reflect.Type { +func (f SetChargingProfileFeature) GetResponseType() reflect.Type { return reflect.TypeOf(SetChargingProfileConfirmation{}) } @@ -65,7 +68,7 @@ func (c SetChargingProfileConfirmation) GetFeatureName() string { } // Creates a new SetChargingProfileRequest, containing all required fields. There are no optional fields for this message. -func NewSetChargingProfileRequest(connectorId int, chargingProfile *ChargingProfile) *SetChargingProfileRequest { +func NewSetChargingProfileRequest(connectorId int, chargingProfile *types.ChargingProfile) *SetChargingProfileRequest { return &SetChargingProfileRequest{ConnectorId: connectorId, ChargingProfile: chargingProfile} } @@ -75,5 +78,5 @@ func NewSetChargingProfileConfirmation(status ChargingProfileStatus) *SetChargin } func init() { - _ = Validate.RegisterValidation("chargingProfileStatus", isValidChargingProfileStatus) + _ = types.Validate.RegisterValidation("chargingProfileStatus", isValidChargingProfileStatus) } diff --git a/ocpp1.6/smartcharging/smart_charging.go b/ocpp1.6/smartcharging/smart_charging.go new file mode 100644 index 00000000..24792a01 --- /dev/null +++ b/ocpp1.6/smartcharging/smart_charging.go @@ -0,0 +1,25 @@ +// Contains support for basic Smart Charging, for instance using control pilot. +package smartcharging + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by Central systems for handling messages part of the OCPP 1.6 SmartCharging profile. +type CentralSystemSmartChargingHandler interface { +} + +// Needs to be implemented by Charge points for handling messages part of the OCPP 1.6 SmartCharging profile. +type ChargePointSmartChargingHandler interface { + OnSetChargingProfile(request *SetChargingProfileRequest) (confirmation *SetChargingProfileConfirmation, err error) + OnClearChargingProfile(request *ClearChargingProfileRequest) (confirmation *ClearChargingProfileConfirmation, err error) + OnGetCompositeSchedule(request *GetCompositeScheduleRequest) (confirmation *GetCompositeScheduleConfirmation, err error) +} + +// The profile name +const ProfileName = "SmartCharging" + +// Provides support for basic Smart Charging, for instance using control pilot. +var Profile = ocpp.NewProfile( + ProfileName, + SetChargingProfileFeature{}, + ClearChargingProfileFeature{}, + GetCompositeScheduleFeature{}) diff --git a/ocpp1.6/types/datetime.go b/ocpp1.6/types/datetime.go new file mode 100644 index 00000000..8a221701 --- /dev/null +++ b/ocpp1.6/types/datetime.go @@ -0,0 +1,83 @@ +package types + +import ( + "encoding/json" + "strings" + "time" +) + +// ISO8601 time format, assuming Zulu timestamp. +const ISO8601 = "2006-01-02T15:04:05Z" + +// DateTimeFormat to be used for all OCPP messages. +// +// The default dateTime format is RFC3339. +// Change this if another format is desired. +var DateTimeFormat = time.RFC3339 + +// DateTime wraps a time.Time struct, allowing for improved dateTime JSON compatibility. +type DateTime struct { + time.Time +} + +// Creates a new DateTime struct, embedding a time.Time struct. +func NewDateTime(time time.Time) *DateTime { + return &DateTime{Time: time} +} + +func (dt *DateTime) UnmarshalJSON(input []byte) error { + strInput := string(input) + strInput = strings.Trim(strInput, `"`) + if DateTimeFormat == "" { + defaultTime := time.Time{} + err := json.Unmarshal(input, defaultTime) + if err != nil { + return err + } + dt.Time = defaultTime.Local() + } else { + newTime, err := time.Parse(DateTimeFormat, strInput) + if err != nil { + return err + } + dt.Time = newTime.Local() + } + return nil +} + +func (dt *DateTime) MarshalJSON() ([]byte, error) { + if DateTimeFormat == "" { + return json.Marshal(dt.Time) + } + timeStr := dt.FormatTimestamp() + return json.Marshal(timeStr) +} + +// Formats the UTC timestamp using the DateTimeFormat setting. +// This function is used during JSON marshaling as well. +func (dt *DateTime) FormatTimestamp() string { + return dt.UTC().Format(DateTimeFormat) +} + +func FormatTimestamp(t time.Time) string { + return t.UTC().Format(DateTimeFormat) +} + +// DateTime Validation + +func DateTimeIsNull(dateTime *DateTime) bool { + return dateTime != nil && dateTime.IsZero() +} + +func validateDateTimeGt(dateTime *DateTime, than time.Time) bool { + return dateTime != nil && dateTime.After(than) +} + +func validateDateTimeNow(dateTime DateTime) bool { + dur := time.Now().Sub(dateTime.Time).Minutes() + return dur < 1 +} + +func validateDateTimeLt(dateTime DateTime, than time.Time) bool { + return dateTime.Before(than) +} diff --git a/ocpp1.6/common.go b/ocpp1.6/types/types.go similarity index 92% rename from ocpp1.6/common.go rename to ocpp1.6/types/types.go index 93b73480..f91cabf7 100644 --- a/ocpp1.6/common.go +++ b/ocpp1.6/types/types.go @@ -1,56 +1,15 @@ -package ocpp16 +// Contains common and shared data types between OCPP 1.6 messages. +package types import ( - "encoding/json" "github.com/lorenzodonini/ocpp-go/ocppj" "gopkg.in/go-playground/validator.v9" - "strings" - "time" ) const ( - ISO8601 = "2006-01-02T15:04:05Z" V16Subprotocol = "ocpp1.6" ) -type DateTime struct { - time.Time -} - -func NewDateTime(time time.Time) *DateTime { - return &DateTime{Time: time} -} - -var DateTimeFormat = ISO8601 - -func (dt *DateTime) UnmarshalJSON(input []byte) error { - strInput := string(input) - strInput = strings.Trim(strInput, `"`) - if DateTimeFormat == "" { - defaultTime := time.Time{} - err := json.Unmarshal(input, defaultTime) - if err != nil { - return err - } - dt.Time = defaultTime - } else { - newTime, err := time.Parse(DateTimeFormat, strInput) - if err != nil { - return err - } - dt.Time = newTime - } - return nil -} - -func (dt *DateTime) MarshalJSON() ([]byte, error) { - if DateTimeFormat == "" { - return json.Marshal(dt.Time) - } - timeStr := dt.Time.Format(DateTimeFormat) - return json.Marshal(timeStr) -} - type PropertyViolation struct { error Property string @@ -354,24 +313,6 @@ type MeterValue struct { SampledValue []SampledValue `json:"sampledValue" validate:"required,min=1,dive"` } -// DateTime Validation -func dateTimeIsNull(dateTime *DateTime) bool { - return dateTime != nil && dateTime.IsZero() -} - -func validateDateTimeGt(dateTime *DateTime, than time.Time) bool { - return dateTime != nil && dateTime.After(than) -} - -func validateDateTimeNow(dateTime DateTime) bool { - dur := time.Now().Sub(dateTime.Time).Minutes() - return dur < 1 -} - -func validateDateTimeLt(dateTime DateTime, than time.Time) bool { - return dateTime.Before(than) -} - // Initialize validator var Validate = ocppj.Validate diff --git a/ocpp1.6/v16.go b/ocpp1.6/v16.go index 4f25f5ed..fb13c15a 100644 --- a/ocpp1.6/v16.go +++ b/ocpp1.6/v16.go @@ -1,413 +1,100 @@ +// The package contains an implementation of the OCPP 1.6 communication protocol between a Charge Point and a Central System in an EV charging infrastructure. package ocpp16 import ( - "fmt" "github.com/gorilla/websocket" "github.com/lorenzodonini/ocpp-go/ocpp" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/localauth" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/remotetrigger" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/reservation" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/lorenzodonini/ocpp-go/ocppj" "github.com/lorenzodonini/ocpp-go/ws" log "github.com/sirupsen/logrus" ) // -------------------- v1.6 Charge Point -------------------- -type ChargePoint interface { - // Messages - BootNotification(chargePointModel string, chargePointVendor string, props ...func(request *BootNotificationRequest)) (*BootNotificationConfirmation, error) - Authorize(idTag string, props ...func(request *AuthorizeRequest)) (*AuthorizeConfirmation, error) - DataTransfer(vendorId string, props ...func(request *DataTransferRequest)) (*DataTransferConfirmation, error) - Heartbeat(props ...func(request *HeartbeatRequest)) (*HeartbeatConfirmation, error) - MeterValues(connectorId int, meterValues []MeterValue, props ...func(request *MeterValuesRequest)) (*MeterValuesConfirmation, error) - StartTransaction(connectorId int, idTag string, meterStart int, timestamp *DateTime, props ...func(request *StartTransactionRequest)) (*StartTransactionConfirmation, error) - StopTransaction(meterStop int, timestamp *DateTime, transactionId int, props ...func(request *StopTransactionRequest)) (*StopTransactionConfirmation, error) - StatusNotification(connectorId int, errorCode ChargePointErrorCode, status ChargePointStatus, props ...func(request *StatusNotificationRequest)) (*StatusNotificationConfirmation, error) - DiagnosticsStatusNotification(status DiagnosticsStatus, props ...func(request *DiagnosticsStatusNotificationRequest)) (*DiagnosticsStatusNotificationConfirmation, error) - FirmwareStatusNotification(status FirmwareStatus, props ...func(request *FirmwareStatusNotificationRequest)) (*FirmwareStatusNotificationConfirmation, error) - - // Logic - SetChargePointCoreListener(listener ChargePointCoreListener) - SetLocalAuthListListener(listener ChargePointLocalAuthListListener) - SetFirmwareManagementListener(listener ChargePointFirmwareManagementListener) - SetReservationListener(listener ChargePointReservationListener) - SetRemoteTriggerListener(listener ChargePointRemoteTriggerListener) - SetSmartChargingListener(listener ChargePointSmartChargingListener) - SendRequest(request ocpp.Request) (ocpp.Confirmation, error) - SendRequestAsync(request ocpp.Request, callback func(confirmation ocpp.Confirmation, protoError error)) error - Start(centralSystemUrl string) error - Stop() -} - -type chargePoint struct { - chargePoint *ocppj.ChargePoint - coreListener ChargePointCoreListener - localAuthListListener ChargePointLocalAuthListListener - firmwareListener ChargePointFirmwareManagementListener - reservationListener ChargePointReservationListener - remoteTriggerListener ChargePointRemoteTriggerListener - smartChargingListener ChargePointSmartChargingListener - confirmationListener chan ocpp.Confirmation - errorListener chan error -} - -// Sends a BootNotificationRequest to the central system, along with information about the charge point. -func (cp *chargePoint) BootNotification(chargePointModel string, chargePointVendor string, props ...func(request *BootNotificationRequest)) (*BootNotificationConfirmation, error) { - request := NewBootNotificationRequest(chargePointModel, chargePointVendor) - for _, fn := range props { - fn(request) - } - confirmation, err := cp.SendRequest(request) - if err != nil { - return nil, err - } else { - return confirmation.(*BootNotificationConfirmation), err - } -} - -// Requests explicit authorization to the central system, provided a valid IdTag (typically the client's). The central system may either authorize or reject the client. -func (cp *chargePoint) Authorize(idTag string, props ...func(request *AuthorizeRequest)) (*AuthorizeConfirmation, error) { - request := NewAuthorizationRequest(idTag) - for _, fn := range props { - fn(request) - } - confirmation, err := cp.SendRequest(request) - if err != nil { - return nil, err - } else { - return confirmation.(*AuthorizeConfirmation), err - } -} - -// Starts a custom data transfer request. Every vendor may implement their own proprietary logic for this message. -func (cp *chargePoint) DataTransfer(vendorId string, props ...func(request *DataTransferRequest)) (*DataTransferConfirmation, error) { - request := NewDataTransferRequest(vendorId) - for _, fn := range props { - fn(request) - } - confirmation, err := cp.SendRequest(request) - if err != nil { - return nil, err - } else { - return confirmation.(*DataTransferConfirmation), err - } -} - -// Notifies the central system that the charge point is still online. The central system's response is used for time synchronization purposes. It is recommended to perform this operation once every 24 hours. -func (cp *chargePoint) Heartbeat(props ...func(request *HeartbeatRequest)) (*HeartbeatConfirmation, error) { - request := NewHeartbeatRequest() - for _, fn := range props { - fn(request) - } - confirmation, err := cp.SendRequest(request) - if err != nil { - return nil, err - } else { - return confirmation.(*HeartbeatConfirmation), err - } -} - -// Sends a batch of collected meter values to the central system, for billing and analysis. May be done periodically during ongoing transactions. -func (cp *chargePoint) MeterValues(connectorId int, meterValues []MeterValue, props ...func(request *MeterValuesRequest)) (*MeterValuesConfirmation, error) { - request := NewMeterValuesRequest(connectorId, meterValues) - for _, fn := range props { - fn(request) - } - confirmation, err := cp.SendRequest(request) - if err != nil { - return nil, err - } else { - return confirmation.(*MeterValuesConfirmation), err - } -} - -// Requests to start a transaction for a specific connector. The central system will verify the client's IdTag and either accept or reject the transaction. -func (cp *chargePoint) StartTransaction(connectorId int, idTag string, meterStart int, timestamp *DateTime, props ...func(request *StartTransactionRequest)) (*StartTransactionConfirmation, error) { - request := NewStartTransactionRequest(connectorId, idTag, meterStart, timestamp) - for _, fn := range props { - fn(request) - } - confirmation, err := cp.SendRequest(request) - if err != nil { - return nil, err - } else { - return confirmation.(*StartTransactionConfirmation), err - } -} - -// Stops an ongoing transaction. Typically a batch of meter values is passed along with this message. -func (cp *chargePoint) StopTransaction(meterStop int, timestamp *DateTime, transactionId int, props ...func(request *StopTransactionRequest)) (*StopTransactionConfirmation, error) { - request := NewStopTransactionRequest(meterStop, timestamp, transactionId) - for _, fn := range props { - fn(request) - } - confirmation, err := cp.SendRequest(request) - if err != nil { - return nil, err - } else { - return confirmation.(*StopTransactionConfirmation), err - } -} - -// Notifies the central system of a status update. This may apply to the entire charge point or to a single connector. -func (cp *chargePoint) StatusNotification(connectorId int, errorCode ChargePointErrorCode, status ChargePointStatus, props ...func(request *StatusNotificationRequest)) (*StatusNotificationConfirmation, error) { - request := NewStatusNotificationRequest(connectorId, errorCode, status) - for _, fn := range props { - fn(request) - } - confirmation, err := cp.SendRequest(request) - if err != nil { - return nil, err - } else { - return confirmation.(*StatusNotificationConfirmation), err - } -} - -// Notifies the central system of a status change in the upload of diagnostics data. -func (cp *chargePoint) DiagnosticsStatusNotification(status DiagnosticsStatus, props ...func(request *DiagnosticsStatusNotificationRequest)) (*DiagnosticsStatusNotificationConfirmation, error) { - request := NewDiagnosticsStatusNotificationRequest(status) - for _, fn := range props { - fn(request) - } - confirmation, err := cp.SendRequest(request) - if err != nil { - return nil, err - } else { - return confirmation.(*DiagnosticsStatusNotificationConfirmation), err - } -} - -// Notifies the central system of a status change during the download of a new firmware version. -func (cp *chargePoint) FirmwareStatusNotification(status FirmwareStatus, props ...func(request *FirmwareStatusNotificationRequest)) (*FirmwareStatusNotificationConfirmation, error) { - request := NewFirmwareStatusNotificationRequest(status) - for _, fn := range props { - fn(request) - } - confirmation, err := cp.SendRequest(request) - if err != nil { - return nil, err - } else { - return confirmation.(*FirmwareStatusNotificationConfirmation), err - } -} - -// Registers a handler for incoming core profile messages -func (cp *chargePoint) SetChargePointCoreListener(listener ChargePointCoreListener) { - cp.coreListener = listener -} - -// Registers a handler for incoming local authorization profile messages -func (cp *chargePoint) SetLocalAuthListListener(listener ChargePointLocalAuthListListener) { - cp.localAuthListListener = listener -} - -// Registers a handler for incoming firmware management profile messages -func (cp *chargePoint) SetFirmwareManagementListener(listener ChargePointFirmwareManagementListener) { - cp.firmwareListener = listener -} - -// Registers a handler for incoming reservation profile messages -func (cp *chargePoint) SetReservationListener(listener ChargePointReservationListener) { - cp.reservationListener = listener -} - -// Registers a handler for incoming remote trigger profile messages -func (cp *chargePoint) SetRemoteTriggerListener(listener ChargePointRemoteTriggerListener) { - cp.remoteTriggerListener = listener -} - -// Registers a handler for incoming smart charging profile messages -func (cp *chargePoint) SetSmartChargingListener(listener ChargePointSmartChargingListener) { - cp.smartChargingListener = listener -} - -// Sends a request to the central system. -// The central system will respond with a confirmation, or with an error if the request was invalid or could not be processed. -// In case of network issues (i.e. the remote host couldn't be reached), the function also returns an error. -// The request is synchronous blocking. -func (cp *chargePoint) SendRequest(request ocpp.Request) (ocpp.Confirmation, error) { - // TODO: check for supported feature - err := cp.chargePoint.SendRequest(request) - if err != nil { - return nil, err - } - //TODO: timeouts - select { - case confirmation := <-cp.confirmationListener: - return confirmation, nil - case err = <-cp.errorListener: - return nil, err - } -} -// Sends an asynchronous request to the central system. -// The central system will respond with a confirmation messages, or with an error if the request was invalid or could not be processed. -// This result is propagated via a callback, called asynchronously. -// In case of network issues (i.e. the remote host couldn't be reached), the function returns an error directly. In this case, the callback is never called. -func (cp *chargePoint) SendRequestAsync(request ocpp.Request, callback func(confirmation ocpp.Confirmation, err error)) error { - switch request.GetFeatureName() { - case AuthorizeFeatureName, BootNotificationFeatureName, DataTransferFeatureName, HeartbeatFeatureName, MeterValuesFeatureName, StartTransactionFeatureName, StopTransactionFeatureName, StatusNotificationFeatureName, - DiagnosticsStatusNotificationFeatureName, FirmwareStatusNotificationFeatureName: - break - default: - return fmt.Errorf("unsupported action %v on charge point, cannot send request", request.GetFeatureName()) - } - err := cp.chargePoint.SendRequest(request) - if err == nil { - // Retrieve result asynchronously - go func() { - select { - case confirmation := <-cp.confirmationListener: - callback(confirmation, nil) - case protoError := <-cp.errorListener: - callback(nil, protoError) - } - }() - } - return err -} - -func (cp *chargePoint) sendResponse(confirmation ocpp.Confirmation, err error, requestId string) { - if confirmation != nil { - err := cp.chargePoint.SendConfirmation(requestId, confirmation) - if err != nil { - log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) - //TODO: handle error somehow - } - } else { - err = cp.chargePoint.SendError(requestId, ocppj.ProtocolError, err.Error(), nil) - if err != nil { - log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) - } - } -} - -// Connects to the central system and starts the charge point routine. -// The function doesn't block and returns right away, after having attempted to open a connection to the central system. -// If the connection couldn't be opened, an error is returned. +// A Charge Point represents the physical system where an EV can be charged. +// You can instantiate a default Charge Point struct by calling NewClient. // -// Optional client options must be set before calling this function. Refer to NewChargePoint. +// The logic for incoming messages needs to be implemented, and the message handlers need to be registered with the charge point: +// handler := &ChargePointHandler{} +// client.SetChargePointCoreHandler(handler) +// Refer to the ChargePointCoreHandler, ChargePointFirmwareHandler, ChargePointLocalAuthListHandler, ChargePointReservationHandler, ChargePointRemoteTriggerHandler and ChargePointSmartChargingHandlers interfaces for the implementation requirements. // -// No auto-reconnect logic is implemented as of now, but is planned for the future. -func (cp *chargePoint) Start(centralSystemUrl string) error { - // TODO: implement auto-reconnect logic - return cp.chargePoint.Start(centralSystemUrl) -} - -// Stops the charge point routine, disconnecting it from the central system. -// Any pending requests are discarded. -func (cp *chargePoint) Stop() { - cp.chargePoint.Stop() -} - -func (cp *chargePoint) notImplementedError(requestId string, action string) { - log.WithField("request", requestId).Errorf("cannot handle call from central system. Sending CallError instead") - err := cp.chargePoint.SendError(requestId, ocppj.NotImplemented, fmt.Sprintf("no handler for action %v implemented", action), nil) - if err != nil { - log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) - } -} - -func (cp *chargePoint) notSupportedError(requestId string, action string) { - log.WithField("request", requestId).Errorf("cannot handle call from central system. Sending CallError instead") - err := cp.chargePoint.SendError(requestId, ocppj.NotSupported, fmt.Sprintf("unsupported action %v on charge point", action), nil) - if err != nil { - log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) - } -} - -func (cp *chargePoint) handleIncomingRequest(request ocpp.Request, requestId string, action string) { - profile, found := cp.chargePoint.GetProfileForFeature(action) - // Check whether action is supported and a listener for it exists - if !found { - cp.notImplementedError(requestId, action) - return - } else { - switch profile.Name { - case CoreProfileName: - if cp.coreListener == nil { - cp.notSupportedError(requestId, action) - return - } - case LocalAuthListProfileName: - if cp.localAuthListListener == nil { - cp.notSupportedError(requestId, action) - return - } - case FirmwareManagementProfileName: - if cp.firmwareListener == nil { - cp.notSupportedError(requestId, action) - return - } - case ReservationProfileName: - if cp.reservationListener == nil { - cp.notSupportedError(requestId, action) - return - } - case RemoteTriggerProfileName: - if cp.remoteTriggerListener == nil { - cp.notSupportedError(requestId, action) - return - } - case SmartChargingProfileName: - if cp.smartChargingListener == nil { - cp.notSupportedError(requestId, action) - return - } - } - } - // Process request - var confirmation ocpp.Confirmation = nil - cp.chargePoint.GetProfileForFeature(action) - var err error = nil - switch action { - case ChangeAvailabilityFeatureName: - confirmation, err = cp.coreListener.OnChangeAvailability(request.(*ChangeAvailabilityRequest)) - case ChangeConfigurationFeatureName: - confirmation, err = cp.coreListener.OnChangeConfiguration(request.(*ChangeConfigurationRequest)) - case ClearCacheFeatureName: - confirmation, err = cp.coreListener.OnClearCache(request.(*ClearCacheRequest)) - case DataTransferFeatureName: - confirmation, err = cp.coreListener.OnDataTransfer(request.(*DataTransferRequest)) - case GetConfigurationFeatureName: - confirmation, err = cp.coreListener.OnGetConfiguration(request.(*GetConfigurationRequest)) - case RemoteStartTransactionFeatureName: - confirmation, err = cp.coreListener.OnRemoteStartTransaction(request.(*RemoteStartTransactionRequest)) - case RemoteStopTransactionFeatureName: - confirmation, err = cp.coreListener.OnRemoteStopTransaction(request.(*RemoteStopTransactionRequest)) - case ResetFeatureName: - confirmation, err = cp.coreListener.OnReset(request.(*ResetRequest)) - case UnlockConnectorFeatureName: - confirmation, err = cp.coreListener.OnUnlockConnector(request.(*UnlockConnectorRequest)) - case GetLocalListVersionFeatureName: - confirmation, err = cp.localAuthListListener.OnGetLocalListVersion(request.(*GetLocalListVersionRequest)) - case SendLocalListFeatureName: - confirmation, err = cp.localAuthListListener.OnSendLocalList(request.(*SendLocalListRequest)) - case GetDiagnosticsFeatureName: - confirmation, err = cp.firmwareListener.OnGetDiagnostics(request.(*GetDiagnosticsRequest)) - case UpdateFirmwareFeatureName: - confirmation, err = cp.firmwareListener.OnUpdateFirmware(request.(*UpdateFirmwareRequest)) - case ReserveNowFeatureName: - confirmation, err = cp.reservationListener.OnReserveNow(request.(*ReserveNowRequest)) - case CancelReservationFeatureName: - confirmation, err = cp.reservationListener.OnCancelReservation(request.(*CancelReservationRequest)) - case TriggerMessageFeatureName: - confirmation, err = cp.remoteTriggerListener.OnTriggerMessage(request.(*TriggerMessageRequest)) - case SetChargingProfileFeatureName: - confirmation, err = cp.smartChargingListener.OnSetChargingProfile(request.(*SetChargingProfileRequest)) - case ClearChargingProfileFeatureName: - confirmation, err = cp.smartChargingListener.OnClearChargingProfile(request.(*ClearChargingProfileRequest)) - case GetCompositeScheduleFeatureName: - confirmation, err = cp.smartChargingListener.OnGetCompositeSchedule(request.(*GetCompositeScheduleRequest)) - default: - cp.notSupportedError(requestId, action) - return - } - cp.sendResponse(confirmation, err, requestId) +// A charge point can be started and stopped using the Start and Stop functions. +// While running, messages can be sent to the Central system by calling the Charge point's functions, e.g. +// bootConf, err := client.BootNotification("model1", "vendor1") +// +// All messages are synchronous blocking, and return either the response from the Central system or an error. +// To send asynchronous messages and avoid blocking the calling thread, refer to SendRequestAsync. +type ChargePoint interface { + // Sends a BootNotificationRequest to the central system, along with information about the charge point. + BootNotification(chargePointModel string, chargePointVendor string, props ...func(request *core.BootNotificationRequest)) (*core.BootNotificationConfirmation, error) + // Requests explicit authorization to the central system, provided a valid IdTag (typically the client's). The central system may either authorize or reject the client. + Authorize(idTag string, props ...func(request *core.AuthorizeRequest)) (*core.AuthorizeConfirmation, error) + // Starts a custom data transfer request. Every vendor may implement their own proprietary logic for this message. + DataTransfer(vendorId string, props ...func(request *core.DataTransferRequest)) (*core.DataTransferConfirmation, error) + // Notifies the central system that the charge point is still online. The central system's response is used for time synchronization purposes. It is recommended to perform this operation once every 24 hours. + Heartbeat(props ...func(request *core.HeartbeatRequest)) (*core.HeartbeatConfirmation, error) + // Sends a batch of collected meter values to the central system, for billing and analysis. May be done periodically during ongoing transactions. + MeterValues(connectorId int, meterValues []types.MeterValue, props ...func(request *core.MeterValuesRequest)) (*core.MeterValuesConfirmation, error) + // Requests to start a transaction for a specific connector. The central system will verify the client's IdTag and either accept or reject the transaction. + StartTransaction(connectorId int, idTag string, meterStart int, timestamp *types.DateTime, props ...func(request *core.StartTransactionRequest)) (*core.StartTransactionConfirmation, error) + // Stops an ongoing transaction. Typically a batch of meter values is passed along with this message. + StopTransaction(meterStop int, timestamp *types.DateTime, transactionId int, props ...func(request *core.StopTransactionRequest)) (*core.StopTransactionConfirmation, error) + // Notifies the central system of a status update. This may apply to the entire charge point or to a single connector. + StatusNotification(connectorId int, errorCode core.ChargePointErrorCode, status core.ChargePointStatus, props ...func(request *core.StatusNotificationRequest)) (*core.StatusNotificationConfirmation, error) + // Notifies the central system of a status change in the upload of diagnostics data. + DiagnosticsStatusNotification(status firmware.DiagnosticsStatus, props ...func(request *firmware.DiagnosticsStatusNotificationRequest)) (*firmware.DiagnosticsStatusNotificationConfirmation, error) + // Notifies the central system of a status change during the download of a new firmware version. + FirmwareStatusNotification(status firmware.FirmwareStatus, props ...func(request *firmware.FirmwareStatusNotificationRequest)) (*firmware.FirmwareStatusNotificationConfirmation, error) + + // Registers a handler for incoming core profile messages + SetChargePointCoreHandler(listener core.ChargePointCoreHandler) + // Registers a handler for incoming local authorization profile messages + SetLocalAuthListHandler(listener localauth.ChargePointLocalAuthListHandler) + // Registers a handler for incoming firmware management profile messages + SetFirmwareManagementHandler(listener firmware.ChargePointFirmwareManagementHandler) + // Registers a handler for incoming reservation profile messages + SetReservationHandler(listener reservation.ChargePointReservationHandler) + // Registers a handler for incoming remote trigger profile messages + SetRemoteTriggerHandler(listener remotetrigger.ChargePointRemoteTriggerHandler) + // Registers a handler for incoming smart charging profile messages + SetSmartChargingHandler(listener smartcharging.ChargePointSmartChargingHandler) + // Sends a request to the central system. + // The central system will respond with a confirmation, or with an error if the request was invalid or could not be processed. + // In case of network issues (i.e. the remote host couldn't be reached), the function also returns an error. + // + // The request is synchronous blocking. + SendRequest(request ocpp.Request) (ocpp.Response, error) + // Sends an asynchronous request to the central system. + // The central system will respond with a confirmation messages, or with an error if the request was invalid or could not be processed. + // This result is propagated via a callback, called asynchronously. + // In case of network issues (i.e. the remote host couldn't be reached), the function returns an error directly. In this case, the callback is never called. + SendRequestAsync(request ocpp.Request, callback func(confirmation ocpp.Response, protoError error)) error + // Connects to the central system and starts the charge point routine. + // The function doesn't block and returns right away, after having attempted to open a connection to the central system. + // If the connection couldn't be opened, an error is returned. + // + // Optional client options must be set before calling this function. Refer to NewClient. + // + // No auto-reconnect logic is implemented as of now, but is planned for the future. + Start(centralSystemUrl string) error + // Stops the charge point routine, disconnecting it from the central system. + // Any pending requests are discarded. + Stop() } // Creates a new OCPP 1.6 charge point client. // The id parameter is required to uniquely identify the charge point. // // The dispatcher and client parameters may be omitted, in order to use a default configuration: -// chargePoint := NewChargePoint("someUniqueId", nil, nil) +// client := NewClient("someUniqueId", nil, nil) // // Additional networking parameters (e.g. TLS or proxy configuration) may be passed, by creating a custom client. // Here is an example for a client using TLS configuration with a self-signed certificate: @@ -420,629 +107,156 @@ func (cp *chargePoint) handleIncomingRequest(request ocpp.Request, requestId str // if !ok { // log.Fatal("couldn't parse PEM certificate") // } -// cp := NewChargePoint("someUniqueId", nil, ws.NewTLSClient(&tls.Config{ +// cp := NewClient("someUniqueId", nil, ws.NewTLSClient(&tls.Config{ // RootCAs: certPool, // }) // // For more advanced options, or if a customer networking/occpj layer is required, -// please refer to ocppj.ChargePoint and ws.WsClient. -func NewChargePoint(id string, dispatcher *ocppj.ChargePoint, client ws.WsClient) ChargePoint { +// please refer to ocppj.Client and ws.WsClient. +func NewChargePoint(id string, dispatcher *ocppj.Client, client ws.WsClient) ChargePoint { if client == nil { client = ws.NewClient() } - client.AddOption(func (dialer *websocket.Dialer) { + client.AddOption(func(dialer *websocket.Dialer) { // Look for v1.6 subprotocol and add it, if not found alreadyExists := false for _, proto := range dialer.Subprotocols { - if proto == V16Subprotocol { + if proto == types.V16Subprotocol { alreadyExists = true break } } if !alreadyExists { - dialer.Subprotocols = append(dialer.Subprotocols, V16Subprotocol) + dialer.Subprotocols = append(dialer.Subprotocols, types.V16Subprotocol) } }) if dispatcher == nil { - dispatcher = ocppj.NewChargePoint(id, client, CoreProfile, LocalAuthListProfile, FirmwareManagementProfile, ReservationProfile, RemoteTriggerProfile, SmartChargingProfile) + dispatcher = ocppj.NewClient(id, client, core.Profile, localauth.Profile, firmware.Profile, reservation.Profile, remotetrigger.Profile, smartcharging.Profile) } - cp := chargePoint{chargePoint: dispatcher, confirmationListener: make(chan ocpp.Confirmation), errorListener: make(chan error)} - cp.chargePoint.SetConfirmationHandler(func(confirmation ocpp.Confirmation, requestId string) { - cp.confirmationListener <- confirmation + cp := chargePoint{client: dispatcher, confirmationHandler: make(chan ocpp.Response), errorHandler: make(chan error)} + cp.client.SetResponseHandler(func(confirmation ocpp.Response, requestId string) { + cp.confirmationHandler <- confirmation }) - cp.chargePoint.SetErrorHandler(func(err *ocpp.Error, details interface{}) { - cp.errorListener <- err + cp.client.SetErrorHandler(func(err *ocpp.Error, details interface{}) { + cp.errorHandler <- err }) - cp.chargePoint.SetRequestHandler(cp.handleIncomingRequest) + cp.client.SetRequestHandler(cp.handleIncomingRequest) return &cp } // -------------------- v1.6 Central System -------------------- -type CentralSystem interface { - // Messages - ChangeAvailability(clientId string, callback func(*ChangeAvailabilityConfirmation, error), connectorId int, availabilityType AvailabilityType, props ...func(*ChangeAvailabilityRequest)) error - ChangeConfiguration(clientId string, callback func(*ChangeConfigurationConfirmation, error), key string, value string, props ...func(*ChangeConfigurationRequest)) error - ClearCache(clientId string, callback func(*ClearCacheConfirmation, error), props ...func(*ClearCacheRequest)) error - DataTransfer(clientId string, callback func(*DataTransferConfirmation, error), vendorId string, props ...func(*DataTransferRequest)) error - GetConfiguration(clientId string, callback func(*GetConfigurationConfirmation, error), keys []string, props ...func(*GetConfigurationRequest)) error - RemoteStartTransaction(clientId string, callback func(*RemoteStartTransactionConfirmation, error), idTag string, props ...func(*RemoteStartTransactionRequest)) error - RemoteStopTransaction(clientId string, callback func(*RemoteStopTransactionConfirmation, error), transactionId int, props ...func(request *RemoteStopTransactionRequest)) error - Reset(clientId string, callback func(*ResetConfirmation, error), resetType ResetType, props ...func(*ResetRequest)) error - UnlockConnector(clientId string, callback func(*UnlockConnectorConfirmation, error), connectorId int, props ...func(*UnlockConnectorRequest)) error - GetLocalListVersion(clientId string, callback func(*GetLocalListVersionConfirmation, error), props ...func(request *GetLocalListVersionRequest)) error - SendLocalList(clientId string, callback func(*SendLocalListConfirmation, error), version int, updateType UpdateType, props ...func(request *SendLocalListRequest)) error - GetDiagnostics(clientId string, callback func(*GetDiagnosticsConfirmation, error), location string, props ...func(request *GetDiagnosticsRequest)) error - UpdateFirmware(clientId string, callback func(*UpdateFirmwareConfirmation, error), location string, retrieveDate *DateTime, props ...func(request *UpdateFirmwareRequest)) error - ReserveNow(clientId string, callback func(*ReserveNowConfirmation, error), connectorId int, expiryDate *DateTime, idTag string, reservationId int, props ...func(request *ReserveNowRequest)) error - CancelReservation(clientId string, callback func(*CancelReservationConfirmation, error), reservationId int, props ...func(request *CancelReservationRequest)) error - TriggerMessage(clientId string, callback func(*TriggerMessageConfirmation, error), requestedMessage MessageTrigger, props ...func(request *TriggerMessageRequest)) error - SetChargingProfile(clientId string, callback func(*SetChargingProfileConfirmation, error), connectorId int, chargingProfile *ChargingProfile, props ...func(request *SetChargingProfileRequest)) error - ClearChargingProfile(clientId string, callback func(*ClearChargingProfileConfirmation, error), props ...func(request *ClearChargingProfileRequest)) error - GetCompositeSchedule(clientId string, callback func(*GetCompositeScheduleConfirmation, error), connectorId int, duration int, props ...func(request *GetCompositeScheduleRequest)) error - // Logic - SetCentralSystemCoreListener(listener CentralSystemCoreListener) - SetLocalAuthListListener(listener CentralSystemLocalAuthListListener) - SetFirmwareManagementListener(listener CentralSystemFirmwareManagementListener) - SetReservationListener(listener CentralSystemReservationListener) - SetRemoteTriggerListener(listener CentralSystemRemoteTriggerListener) - SetSmartChargingListener(listener CentralSystemSmartChargingListener) +// A Central System manages Charge Points and has the information for authorizing users for using its Charge Points. +// You can instantiate a default Central System struct by calling the NewServer function. +// +// The logic for handling incoming messages needs to be implemented, and the message handlers need to be registered with the central system: +// handler := &CentralSystemHandler{} +// server.SetCentralSystemCoreHandler(handler) +// // Refer to the CentralSystemCoreHandler, CentralSystemFirmwareHandler, CentralSystemLocalAuthListHandler, CentralSystemReservationHandler, CentralSystemRemoteTriggerHandler and CentralSystemSmartChargingHandlers interfaces for the implementation requirements. +// +// A Central system can be started by using the Start function. +// To be notified of incoming (dis)connections from charge points refer to the SetNewClientHandler and SetChargePointDisconnectedHandler functions. +// +// While running, messages can be sent to a charge point by calling the Central system's functions, e.g.: +// callback := func(conf *ChangeAvailabilityConfirmation, err error) { +// // handle the response... +// } +// changeAvailabilityConf, err := server.ChangeAvailability("cs0001", callback, 1, AvailabilityTypeOperative) +// All messages are sent asynchronously and do not block the caller. +type CentralSystem interface { + // Instructs a charge point to change its availability. The target availability can be set for a single connector of for the whole charge point. + ChangeAvailability(clientId string, callback func(*core.ChangeAvailabilityConfirmation, error), connectorId int, availabilityType core.AvailabilityType, props ...func(*core.ChangeAvailabilityRequest)) error + // Changes the configuration of a charge point, by setting a specific key-value pair. + // The configuration key must be supported by the target charge point, in order for the configuration to be accepted. + ChangeConfiguration(clientId string, callback func(*core.ChangeConfigurationConfirmation, error), key string, value string, props ...func(*core.ChangeConfigurationRequest)) error + // Instructs the charge point to clear its current authorization cache. All authorization saved locally will be invalidated. + ClearCache(clientId string, callback func(*core.ClearCacheConfirmation, error), props ...func(*core.ClearCacheRequest)) error + // Starts a custom data transfer request. Every vendor may implement their own proprietary logic for this message. + DataTransfer(clientId string, callback func(*core.DataTransferConfirmation, error), vendorId string, props ...func(*core.DataTransferRequest)) error + // Retrieves the configuration values for the provided configuration keys. + GetConfiguration(clientId string, callback func(*core.GetConfigurationConfirmation, error), keys []string, props ...func(*core.GetConfigurationRequest)) error + // Instructs a charge point to start a transaction for a specified client on a provided connector. + // Depending on the configuration, an explicit authorization message may still be required, before the transaction can start. + RemoteStartTransaction(clientId string, callback func(*core.RemoteStartTransactionConfirmation, error), idTag string, props ...func(*core.RemoteStartTransactionRequest)) error + // Instructs a charge point to stop an ongoing transaction, given the transaction's ID. + RemoteStopTransaction(clientId string, callback func(*core.RemoteStopTransactionConfirmation, error), transactionId int, props ...func(request *core.RemoteStopTransactionRequest)) error + // Forces a charge point to perform an internal hard or soft reset. In both cases, all ongoing transactions are stopped. + Reset(clientId string, callback func(*core.ResetConfirmation, error), resetType core.ResetType, props ...func(*core.ResetRequest)) error + // Attempts to unlock a specific connector on a charge point. Used for remote support purposes. + UnlockConnector(clientId string, callback func(*core.UnlockConnectorConfirmation, error), connectorId int, props ...func(*core.UnlockConnectorRequest)) error + // Queries the current version of the local authorization list from a charge point. + GetLocalListVersion(clientId string, callback func(*localauth.GetLocalListVersionConfirmation, error), props ...func(request *localauth.GetLocalListVersionRequest)) error + // Sends or updates a local authorization list on a charge point. Versioning rules must be followed. + SendLocalList(clientId string, callback func(*localauth.SendLocalListConfirmation, error), version int, updateType localauth.UpdateType, props ...func(request *localauth.SendLocalListRequest)) error + // Requests diagnostics data from a charge point. The data will be uploaded out-of-band to the provided URL location. + GetDiagnostics(clientId string, callback func(*firmware.GetDiagnosticsConfirmation, error), location string, props ...func(request *firmware.GetDiagnosticsRequest)) error + // Instructs the charge point to download and install a new firmware version. The firmware binary will be downloaded out-of-band from the provided URL location. + UpdateFirmware(clientId string, callback func(*firmware.UpdateFirmwareConfirmation, error), location string, retrieveDate *types.DateTime, props ...func(request *firmware.UpdateFirmwareRequest)) error + // Instructs the charge point to reserve a connector for a specific IdTag (client). The connector, or the entire charge point, will be reserved until the provided expiration time. + ReserveNow(clientId string, callback func(*reservation.ReserveNowConfirmation, error), connectorId int, expiryDate *types.DateTime, idTag string, reservationId int, props ...func(request *reservation.ReserveNowRequest)) error + // Cancels a previously reserved charge point or connector, given the reservation ID. + CancelReservation(clientId string, callback func(*reservation.CancelReservationConfirmation, error), reservationId int, props ...func(request *reservation.CancelReservationRequest)) error + // Instructs a charge point to send a specific message to the central system. This is used for forcefully triggering status updates, when the last known state is either too old or not clear to the central system. + TriggerMessage(clientId string, callback func(*remotetrigger.TriggerMessageConfirmation, error), requestedMessage remotetrigger.MessageTrigger, props ...func(request *remotetrigger.TriggerMessageRequest)) error + // Sends a smart charging profile to a charge point. Refer to the smart charging documentation for more information. + SetChargingProfile(clientId string, callback func(*smartcharging.SetChargingProfileConfirmation, error), connectorId int, chargingProfile *types.ChargingProfile, props ...func(request *smartcharging.SetChargingProfileRequest)) error + // Removes one or more charging profiles from a charge point. + ClearChargingProfile(clientId string, callback func(*smartcharging.ClearChargingProfileConfirmation, error), props ...func(request *smartcharging.ClearChargingProfileRequest)) error + // Queries a charge point to the composite smart charging schedules and rules for a specified time interval. + GetCompositeSchedule(clientId string, callback func(*smartcharging.GetCompositeScheduleConfirmation, error), connectorId int, duration int, props ...func(request *smartcharging.GetCompositeScheduleRequest)) error + + // Registers a handler for incoming core profile messages. + SetCentralSystemCoreHandler(listener core.CentralSystemCoreHandler) + // Registers a handler for incoming local authorization profile messages. + SetLocalAuthListHandler(listener localauth.CentralSystemLocalAuthListHandler) + // Registers a handler for incoming firmware management profile messages. + SetFirmwareManagementHandler(listener firmware.CentralSystemFirmwareManagementHandler) + // Registers a handler for incoming reservation profile messages. + SetReservationHandler(listener reservation.CentralSystemReservationHandler) + // Registers a handler for incoming remote trigger profile messages. + SetRemoteTriggerHandler(listener remotetrigger.CentralSystemRemoteTriggerHandler) + // Registers a handler for incoming smart charging profile messages. + SetSmartChargingHandler(listener smartcharging.CentralSystemSmartChargingHandler) + // Registers a handler for new incoming charge point connections. SetNewChargePointHandler(handler func(chargePointId string)) + // Registers a handler for charge point disconnections. SetChargePointDisconnectedHandler(handler func(chargePointId string)) - SendRequestAsync(clientId string, request ocpp.Request, callback func(ocpp.Confirmation, error)) error + // Sends an asynchronous request to the charge point. + // The charge point will respond with a confirmation message, or with an error if the request was invalid or could not be processed. + // This result is propagated via a callback, called asynchronously. + // In case of network issues (i.e. the remote host couldn't be reached), the function returns an error directly. In this case, the callback is never called. + SendRequestAsync(clientId string, request ocpp.Request, callback func(ocpp.Response, error)) error + // Starts running the central system on the specified port and URL. + // The central system runs as a daemon and handles incoming charge point connections and messages. + // + // The function blocks forever, so it is suggested to wrap it in a goroutine, in case other functionality needs to be executed on the main program thread. Start(listenPort int, listenPath string) } -type centralSystem struct { - centralSystem *ocppj.CentralSystem - coreListener CentralSystemCoreListener - localAuthListListener CentralSystemLocalAuthListListener - firmwareListener CentralSystemFirmwareManagementListener - reservationListener CentralSystemReservationListener - remoteTriggerListener CentralSystemRemoteTriggerListener - smartChargingListener CentralSystemSmartChargingListener - callbacks map[string]func(confirmation ocpp.Confirmation, err error) -} - -// Instructs a charge point to change its availability. The target availability can be set for a single connector of for the whole charge point. -func (cs *centralSystem) ChangeAvailability(clientId string, callback func(confirmation *ChangeAvailabilityConfirmation, err error), connectorId int, availabilityType AvailabilityType, props ...func(request *ChangeAvailabilityRequest)) error { - request := NewChangeAvailabilityRequest(connectorId, availabilityType) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ChangeAvailabilityConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Changes the configuration of a charge point, by setting a specific key-value pair. -// The configuration key must be supported by the target charge point, in order for the configuration to be accepted. -func (cs *centralSystem) ChangeConfiguration(clientId string, callback func(confirmation *ChangeConfigurationConfirmation, err error), key string, value string, props ...func(request *ChangeConfigurationRequest)) error { - request := NewChangeConfigurationRequest(key, value) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ChangeConfigurationConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Instructs the charge point to clear its current authorization cache. All authorization saved locally will be invalidated. -func (cs *centralSystem) ClearCache(clientId string, callback func(confirmation *ClearCacheConfirmation, err error), props ...func(*ClearCacheRequest)) error { - request := NewClearCacheRequest() - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ClearCacheConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Starts a custom data transfer request. Every vendor may implement their own proprietary logic for this message. -func (cs *centralSystem) DataTransfer(clientId string, callback func(confirmation *DataTransferConfirmation, err error), vendorId string, props ...func(request *DataTransferRequest)) error { - request := NewDataTransferRequest(vendorId) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*DataTransferConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Retrieves the configuration values for the provided configuration keys. -func (cs *centralSystem) GetConfiguration(clientId string, callback func(confirmation *GetConfigurationConfirmation, err error), keys []string, props ...func(request *GetConfigurationRequest)) error { - request := NewGetConfigurationRequest(keys) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetConfigurationConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Instructs a charge point to start a transaction for a specified client on a provided connector. -// Depending on the configuration, an explicit authorization message may still be required, before the transaction can start. -func (cs *centralSystem) RemoteStartTransaction(clientId string, callback func(*RemoteStartTransactionConfirmation, error), idTag string, props ...func(*RemoteStartTransactionRequest)) error { - request := NewRemoteStartTransactionRequest(idTag) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*RemoteStartTransactionConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Instructs a charge point to stop an ongoing transaction, given the transaction's ID. -func (cs *centralSystem) RemoteStopTransaction(clientId string, callback func(*RemoteStopTransactionConfirmation, error), transactionId int, props ...func(request *RemoteStopTransactionRequest)) error { - request := NewRemoteStopTransactionRequest(transactionId) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*RemoteStopTransactionConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Forces a charge point to perform an internal hard or soft reset. In both cases, all ongoing transactions are stopped. -func (cs *centralSystem) Reset(clientId string, callback func(*ResetConfirmation, error), resetType ResetType, props ...func(request *ResetRequest)) error { - request := NewResetRequest(resetType) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ResetConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Attempts to unlock a specific connector on a charge point. Used for remote support purposes. -func (cs *centralSystem) UnlockConnector(clientId string, callback func(*UnlockConnectorConfirmation, error), connectorId int, props ...func(*UnlockConnectorRequest)) error { - request := NewUnlockConnectorRequest(connectorId) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*UnlockConnectorConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Queries the current version of the local authorization list from a charge point. -func (cs *centralSystem) GetLocalListVersion(clientId string, callback func(*GetLocalListVersionConfirmation, error), props ...func(request *GetLocalListVersionRequest)) error { - request := NewGetLocalListVersionRequest() - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetLocalListVersionConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Sends or updates a local authorization list on a charge point. Versioning rules must be followed. -func (cs *centralSystem) SendLocalList(clientId string, callback func(*SendLocalListConfirmation, error), version int, updateType UpdateType, props ...func(request *SendLocalListRequest)) error { - request := NewSendLocalListRequest(version, updateType) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*SendLocalListConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Requests diagnostics data from a charge point. The data will be uploaded out-of-band to the provided URL location. -func (cs *centralSystem) GetDiagnostics(clientId string, callback func(*GetDiagnosticsConfirmation, error), location string, props ...func(request *GetDiagnosticsRequest)) error { - request := NewGetDiagnosticsRequest(location) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetDiagnosticsConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Instructs the charge point to download and install a new firmware version. The firmware binary will be downloaded out-of-band from the provided URL location. -func (cs *centralSystem) UpdateFirmware(clientId string, callback func(*UpdateFirmwareConfirmation, error), location string, retrieveDate *DateTime, props ...func(request *UpdateFirmwareRequest)) error { - request := NewUpdateFirmwareRequest(location, retrieveDate) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*UpdateFirmwareConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Instructs the charge point to reserve a connector for a specific IdTag (client). The connector, or the entire charge point, will be reserved until the provided expiration time. -func (cs *centralSystem) ReserveNow(clientId string, callback func(*ReserveNowConfirmation, error), connectorId int, expiryDate *DateTime, idTag string, reservationId int, props ...func(request *ReserveNowRequest)) error { - request := NewReserveNowRequest(connectorId, expiryDate, idTag, reservationId) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ReserveNowConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Cancels a previously reserved charge point or connector, given the reservation Id. -func (cs *centralSystem) CancelReservation(clientId string, callback func(*CancelReservationConfirmation, error), reservationId int, props ...func(request *CancelReservationRequest)) error { - request := NewCancelReservationRequest(reservationId) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*CancelReservationConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Instructs a charge point to send a specific message to the central system. This is used for forcefully triggering status updates, when the last known state is either too old or not clear to the central system. -func (cs *centralSystem) TriggerMessage(clientId string, callback func(*TriggerMessageConfirmation, error), requestedMessage MessageTrigger, props ...func(request *TriggerMessageRequest)) error { - request := NewTriggerMessageRequest(requestedMessage) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*TriggerMessageConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Sends a smart charging profile to a charge point. Refer to the smart charging documentation for more information. -func (cs *centralSystem) SetChargingProfile(clientId string, callback func(*SetChargingProfileConfirmation, error), connectorId int, chargingProfile *ChargingProfile, props ...func(request *SetChargingProfileRequest)) error { - request := NewSetChargingProfileRequest(connectorId, chargingProfile) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*SetChargingProfileConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Removes one or more charging profiles from a charge point. -func (cs *centralSystem) ClearChargingProfile(clientId string, callback func(*ClearChargingProfileConfirmation, error), props ...func(request *ClearChargingProfileRequest)) error { - request := NewClearChargingProfileRequest() - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ClearChargingProfileConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Queries a charge point to the composite smart charging schedules and rules for a specified time interval. -func (cs *centralSystem) GetCompositeSchedule(clientId string, callback func(*GetCompositeScheduleConfirmation, error), connectorId int, duration int, props ...func(request *GetCompositeScheduleRequest)) error { - request := NewGetCompositeScheduleRequest(connectorId, duration) - for _, fn := range props { - fn(request) - } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetCompositeScheduleConfirmation), protoError) - } else { - callback(nil, protoError) - } - } - return cs.SendRequestAsync(clientId, request, genericCallback) -} - -// Registers a handler for incoming core profile messages. -func (cs *centralSystem) SetCentralSystemCoreListener(listener CentralSystemCoreListener) { - cs.coreListener = listener -} - -// Registers a handler for incoming local authorization profile messages. -func (cs *centralSystem) SetLocalAuthListListener(listener CentralSystemLocalAuthListListener) { - cs.localAuthListListener = listener -} - -// Registers a handler for incoming firmware management profile messages. -func (cs *centralSystem) SetFirmwareManagementListener(listener CentralSystemFirmwareManagementListener) { - cs.firmwareListener = listener -} - -// Registers a handler for incoming reservation profile messages. -func (cs *centralSystem) SetReservationListener(listener CentralSystemReservationListener) { - cs.reservationListener = listener -} - -// Registers a handler for incoming remote trigger profile messages. -func (cs *centralSystem) SetRemoteTriggerListener(listener CentralSystemRemoteTriggerListener) { - cs.remoteTriggerListener = listener -} - -// Registers a handler for incoming smart charging profile messages. -func (cs *centralSystem) SetSmartChargingListener(listener CentralSystemSmartChargingListener) { - cs.smartChargingListener = listener -} - -// Registers a handler for new incoming charge point connections. -func (cs *centralSystem) SetNewChargePointHandler(handler func(chargePointId string)) { - cs.centralSystem.SetNewChargePointHandler(handler) -} - -// Registers a handler for charge point disconnections. -func (cs *centralSystem) SetChargePointDisconnectedHandler(handler func(chargePointId string)) { - cs.centralSystem.SetDisconnectedChargePointHandler(handler) -} - -// Sends an asynchronous request to the charge point. -// The charge point will respond with a confirmation message, or with an error if the request was invalid or could not be processed. -// This result is propagated via a callback, called asynchronously. -// In case of network issues (i.e. the remote host couldn't be reached), the function returns an error directly. In this case, the callback is never called. -func (cs *centralSystem) SendRequestAsync(clientId string, request ocpp.Request, callback func(confirmation ocpp.Confirmation, err error)) error { - switch request.GetFeatureName() { - case ChangeAvailabilityFeatureName, ChangeConfigurationFeatureName, ClearCacheFeatureName, DataTransferFeatureName, GetConfigurationFeatureName, RemoteStartTransactionFeatureName, RemoteStopTransactionFeatureName, ResetFeatureName, UnlockConnectorFeatureName, - GetLocalListVersionFeatureName, SendLocalListFeatureName, - GetDiagnosticsFeatureName, UpdateFirmwareFeatureName, - ReserveNowFeatureName, CancelReservationFeatureName, - TriggerMessageFeatureName, - SetChargingProfileFeatureName, ClearChargingProfileFeatureName, GetCompositeScheduleFeatureName: - default: - return fmt.Errorf("unsupported action %v on central system, cannot send request", request.GetFeatureName()) - } - cs.callbacks[clientId] = callback - err := cs.centralSystem.SendRequest(clientId, request) - if err != nil { - delete(cs.callbacks, clientId) - return err - } - return nil -} - -// Starts running the central system on the specified port and URL. -// The central system runs as a daemon and handles incoming charge point connections and messages. - -// The function blocks forever, so it is suggested to wrap it in a goroutine, in case other functionality needs to be executed on the main program thread. -func (cs *centralSystem) Start(listenPort int, listenPath string) { - cs.centralSystem.Start(listenPort, listenPath) -} - -func (cs *centralSystem) sendResponse(chargePointId string, confirmation ocpp.Confirmation, err error, requestId string) { - if confirmation != nil { - err := cs.centralSystem.SendConfirmation(chargePointId, requestId, confirmation) - if err != nil { - //TODO: handle error somehow - log.Print(err) - } - } else { - err := cs.centralSystem.SendError(chargePointId, requestId, ocppj.ProtocolError, "Couldn't generate valid confirmation", nil) - if err != nil { - log.WithFields(log.Fields{ - "client": chargePointId, - "request": requestId, - }).Errorf("unknown error %v while replying to message with CallError", err) - } - } -} - -func (cs *centralSystem) notImplementedError(chargePointId string, requestId string, action string) { - log.Warnf("Cannot handle call %v from charge point %v. Sending CallError instead", requestId, chargePointId) - err := cs.centralSystem.SendError(chargePointId, requestId, ocppj.NotImplemented, fmt.Sprintf("no handler for action %v implemented", action), nil) - if err != nil { - log.WithFields(log.Fields{ - "client": chargePointId, - "request": requestId, - }).Errorf("unknown error %v while replying to message with CallError", err) - } -} - -func (cs *centralSystem) notSupportedError(chargePointId string, requestId string, action string) { - log.Warnf("Cannot handle call %v from charge point %v. Sending CallError instead", requestId, chargePointId) - err := cs.centralSystem.SendError(chargePointId, requestId, ocppj.NotSupported, fmt.Sprintf("unsupported action %v on central system", action), nil) - if err != nil { - log.WithFields(log.Fields{ - "client": chargePointId, - "request": requestId, - }).Errorf("unknown error %v while replying to message with CallError", err) - } -} - -func (cs *centralSystem) handleIncomingRequest(chargePointId string, request ocpp.Request, requestId string, action string) { - profile, found := cs.centralSystem.GetProfileForFeature(action) - // Check whether action is supported and a listener for it exists - if !found { - cs.notImplementedError(chargePointId, requestId, action) - return - } else { - switch profile.Name { - case CoreProfileName: - if cs.coreListener == nil { - cs.notSupportedError(chargePointId, requestId, action) - return - } - case LocalAuthListProfileName: - if cs.localAuthListListener == nil { - cs.notSupportedError(chargePointId, requestId, action) - return - } - case FirmwareManagementProfileName: - if cs.firmwareListener == nil { - cs.notSupportedError(chargePointId, requestId, action) - return - } - case ReservationProfileName: - if cs.reservationListener == nil { - cs.notSupportedError(chargePointId, requestId, action) - return - } - case RemoteTriggerProfileName: - if cs.remoteTriggerListener == nil { - cs.notSupportedError(chargePointId, requestId, action) - return - } - case SmartChargingProfileName: - if cs.smartChargingListener == nil { - cs.notSupportedError(chargePointId, requestId, action) - return - } - } - } - var confirmation ocpp.Confirmation = nil - var err error = nil - // Execute in separate goroutine, so the caller goroutine is available - go func() { - switch action { - case BootNotificationFeatureName: - confirmation, err = cs.coreListener.OnBootNotification(chargePointId, request.(*BootNotificationRequest)) - case AuthorizeFeatureName: - confirmation, err = cs.coreListener.OnAuthorize(chargePointId, request.(*AuthorizeRequest)) - case DataTransferFeatureName: - confirmation, err = cs.coreListener.OnDataTransfer(chargePointId, request.(*DataTransferRequest)) - case HeartbeatFeatureName: - confirmation, err = cs.coreListener.OnHeartbeat(chargePointId, request.(*HeartbeatRequest)) - case MeterValuesFeatureName: - confirmation, err = cs.coreListener.OnMeterValues(chargePointId, request.(*MeterValuesRequest)) - case StartTransactionFeatureName: - confirmation, err = cs.coreListener.OnStartTransaction(chargePointId, request.(*StartTransactionRequest)) - case StopTransactionFeatureName: - confirmation, err = cs.coreListener.OnStopTransaction(chargePointId, request.(*StopTransactionRequest)) - case StatusNotificationFeatureName: - confirmation, err = cs.coreListener.OnStatusNotification(chargePointId, request.(*StatusNotificationRequest)) - case DiagnosticsStatusNotificationFeatureName: - confirmation, err = cs.firmwareListener.OnDiagnosticsStatusNotification(chargePointId, request.(*DiagnosticsStatusNotificationRequest)) - case FirmwareStatusNotificationFeatureName: - confirmation, err = cs.firmwareListener.OnFirmwareStatusNotification(chargePointId, request.(*FirmwareStatusNotificationRequest)) - default: - cs.notSupportedError(chargePointId, requestId, action) - return - } - cs.sendResponse(chargePointId, confirmation, err, requestId) - }() -} - -func (cs *centralSystem) handleIncomingConfirmation(chargePointId string, confirmation ocpp.Confirmation, requestId string) { - if callback, ok := cs.callbacks[chargePointId]; ok { - delete(cs.callbacks, chargePointId) - callback(confirmation, nil) - } else { - log.WithFields(log.Fields{ - "client": chargePointId, - "request": requestId, - }).Errorf("no handler available for Call Result of type %v", confirmation.GetFeatureName()) - } -} - -func (cs *centralSystem) handleIncomingError(chargePointId string, err *ocpp.Error, details interface{}) { - if callback, ok := cs.callbacks[chargePointId]; ok { - delete(cs.callbacks, chargePointId) - callback(nil, err) - } else { - log.WithFields(log.Fields{ - "client": chargePointId, - "request": err.MessageId, - }).Errorf("no handler available for Call Error %v", err.Code) - } -} - // Creates a new OCPP 1.6 central system. // // The dispatcher and client parameters may be omitted, in order to use a default configuration: -// chargePoint := NewCentralSystem(nil, nil) +// client := NewServer(nil, nil) // // It is recommended to use the default configuration, unless a custom networking / ocppj layer is required. // The default dispatcher supports all OCPP 1.6 profiles out-of-the-box. // // If you need a TLS server, you may use the following: -// cs := NewCentralSystem(nil, ws.NewTLSServer("certificatePath", "privateKeyPath")) -func NewCentralSystem(dispatcher *ocppj.CentralSystem, server ws.WsServer) CentralSystem { +// cs := NewServer(nil, ws.NewTLSServer("certificatePath", "privateKeyPath")) +func NewCentralSystem(dispatcher *ocppj.Server, server ws.WsServer) CentralSystem { if server == nil { server = ws.NewServer() } - server.AddSupportedSubprotocol(V16Subprotocol) + server.AddSupportedSubprotocol(types.V16Subprotocol) if dispatcher == nil { - dispatcher = ocppj.NewCentralSystem(server, CoreProfile, LocalAuthListProfile, FirmwareManagementProfile, ReservationProfile, RemoteTriggerProfile, SmartChargingProfile) + dispatcher = ocppj.NewServer(server, core.Profile, localauth.Profile, firmware.Profile, reservation.Profile, remotetrigger.Profile, smartcharging.Profile) } cs := centralSystem{ - centralSystem: dispatcher, - callbacks: map[string]func(confirmation ocpp.Confirmation, err error){}} - cs.centralSystem.SetRequestHandler(cs.handleIncomingRequest) - cs.centralSystem.SetConfirmationHandler(cs.handleIncomingConfirmation) - cs.centralSystem.SetErrorHandler(cs.handleIncomingError) + server: dispatcher, + callbacks: map[string]func(confirmation ocpp.Response, err error){}} + cs.server.SetRequestHandler(cs.handleIncomingRequest) + cs.server.SetResponseHandler(cs.handleIncomingConfirmation) + cs.server.SetErrorHandler(cs.handleIncomingError) return &cs } diff --git a/ocpp1.6_test/authorize_test.go b/ocpp1.6_test/authorize_test.go index 66ce6abd..19e86261 100644 --- a/ocpp1.6_test/authorize_test.go +++ b/ocpp1.6_test/authorize_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -12,9 +13,9 @@ import ( func (suite *OcppV16TestSuite) TestAuthorizeRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.AuthorizeRequest{IdTag: "12345"}, true}, - {ocpp16.AuthorizeRequest{}, false}, - {ocpp16.AuthorizeRequest{IdTag: ">20.................."}, false}, + {core.AuthorizeRequest{IdTag: "12345"}, true}, + {core.AuthorizeRequest{}, false}, + {core.AuthorizeRequest{IdTag: ">20.................."}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -22,9 +23,9 @@ func (suite *OcppV16TestSuite) TestAuthorizeRequestValidation() { func (suite *OcppV16TestSuite) TestAuthorizeConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.AuthorizeConfirmation{IdTagInfo: &ocpp16.IdTagInfo{ExpiryDate: ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "00000", Status: ocpp16.AuthorizationStatusAccepted}}, true}, - {ocpp16.AuthorizeConfirmation{IdTagInfo: &ocpp16.IdTagInfo{Status: "invalidAuthorizationStatus"}}, false}, - {ocpp16.AuthorizeConfirmation{}, false}, + {core.AuthorizeConfirmation{IdTagInfo: &types.IdTagInfo{ExpiryDate: types.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "00000", Status: types.AuthorizationStatusAccepted}}, true}, + {core.AuthorizeConfirmation{IdTagInfo: &types.IdTagInfo{Status: "invalidAuthorizationStatus"}}, false}, + {core.AuthorizeConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -36,11 +37,11 @@ func (suite *OcppV16TestSuite) TestAuthorizeE2EMocked() { wsUrl := "someUrl" idTag := "tag1" parentIdTag := "parentTag1" - status := ocpp16.AuthorizationStatusAccepted - expiryDate := ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"idTag":"%v"}]`, messageId, ocpp16.AuthorizeFeatureName, idTag) - responseJson := fmt.Sprintf(`[3,"%v",{"idTagInfo":{"expiryDate":"%v","parentIdTag":"%v","status":"%v"}}]`, messageId, expiryDate.Time.Format(ocpp16.ISO8601), parentIdTag, status) - authorizeConfirmation := ocpp16.NewAuthorizationConfirmation(&ocpp16.IdTagInfo{ExpiryDate: expiryDate, ParentIdTag: parentIdTag, Status: status}) + status := types.AuthorizationStatusAccepted + expiryDate := types.NewDateTime(time.Now().Add(time.Hour * 8)) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"idTag":"%v"}]`, messageId, core.AuthorizeFeatureName, idTag) + responseJson := fmt.Sprintf(`[3,"%v",{"idTagInfo":{"expiryDate":"%v","parentIdTag":"%v","status":"%v"}}]`, messageId, expiryDate.FormatTimestamp(), parentIdTag, status) + authorizeConfirmation := core.NewAuthorizationConfirmation(&types.IdTagInfo{ExpiryDate: expiryDate, ParentIdTag: parentIdTag, Status: status}) requestRaw := []byte(requestJson) responseRaw := []byte(responseJson) channel := NewMockWebSocket(wsId) @@ -64,7 +65,7 @@ func (suite *OcppV16TestSuite) TestAuthorizeE2EMocked() { func (suite *OcppV16TestSuite) TestAuthorizeInvalidEndpoint() { messageId := defaultMessageId idTag := "tag1" - authorizeRequest := ocpp16.NewAuthorizationRequest(idTag) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"idTag":"%v"}]`, messageId, ocpp16.AuthorizeFeatureName, idTag) + authorizeRequest := core.NewAuthorizationRequest(idTag) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"idTag":"%v"}]`, messageId, core.AuthorizeFeatureName, idTag) testUnsupportedRequestFromCentralSystem(suite, authorizeRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/boot_notification_test.go b/ocpp1.6_test/boot_notification_test.go index 9261d9a9..3b11ce3c 100644 --- a/ocpp1.6_test/boot_notification_test.go +++ b/ocpp1.6_test/boot_notification_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -12,19 +13,19 @@ import ( func (suite *OcppV16TestSuite) TestBootNotificationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test"}, true}, - {ocpp16.BootNotificationRequest{ChargeBoxSerialNumber: "test", ChargePointModel: "test", ChargePointSerialNumber: "number", ChargePointVendor: "test", FirmwareVersion: "version", Iccid: "test", Imsi: "test"}, true}, - {ocpp16.BootNotificationRequest{ChargeBoxSerialNumber: "test", ChargePointSerialNumber: "number", ChargePointVendor: "test", FirmwareVersion: "version", Iccid: "test", Imsi: "test"}, false}, - {ocpp16.BootNotificationRequest{ChargeBoxSerialNumber: "test", ChargePointModel: "test", ChargePointSerialNumber: "number", FirmwareVersion: "version", Iccid: "test", Imsi: "test"}, false}, - {ocpp16.BootNotificationRequest{ChargeBoxSerialNumber: ">25.......................", ChargePointModel: "test", ChargePointVendor: "test"}, false}, - {ocpp16.BootNotificationRequest{ChargePointModel: ">20..................", ChargePointVendor: "test"}, false}, - {ocpp16.BootNotificationRequest{ChargePointModel: "test", ChargePointSerialNumber: ">25.......................", ChargePointVendor: "test"}, false}, - {ocpp16.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: ">20.................."}, false}, - {ocpp16.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test", FirmwareVersion: ">50................................................"}, false}, - {ocpp16.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test", Iccid: ">20.................."}, false}, - {ocpp16.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test", Imsi: ">20.................."}, false}, - {ocpp16.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test", MeterSerialNumber: ">25......................."}, false}, - {ocpp16.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test", MeterType: ">25......................."}, false}, + {core.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test"}, true}, + {core.BootNotificationRequest{ChargeBoxSerialNumber: "test", ChargePointModel: "test", ChargePointSerialNumber: "number", ChargePointVendor: "test", FirmwareVersion: "version", Iccid: "test", Imsi: "test"}, true}, + {core.BootNotificationRequest{ChargeBoxSerialNumber: "test", ChargePointSerialNumber: "number", ChargePointVendor: "test", FirmwareVersion: "version", Iccid: "test", Imsi: "test"}, false}, + {core.BootNotificationRequest{ChargeBoxSerialNumber: "test", ChargePointModel: "test", ChargePointSerialNumber: "number", FirmwareVersion: "version", Iccid: "test", Imsi: "test"}, false}, + {core.BootNotificationRequest{ChargeBoxSerialNumber: ">25.......................", ChargePointModel: "test", ChargePointVendor: "test"}, false}, + {core.BootNotificationRequest{ChargePointModel: ">20..................", ChargePointVendor: "test"}, false}, + {core.BootNotificationRequest{ChargePointModel: "test", ChargePointSerialNumber: ">25.......................", ChargePointVendor: "test"}, false}, + {core.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: ">20.................."}, false}, + {core.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test", FirmwareVersion: ">50................................................"}, false}, + {core.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test", Iccid: ">20.................."}, false}, + {core.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test", Imsi: ">20.................."}, false}, + {core.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test", MeterSerialNumber: ">25......................."}, false}, + {core.BootNotificationRequest{ChargePointModel: "test", ChargePointVendor: "test", MeterType: ">25......................."}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -32,14 +33,14 @@ func (suite *OcppV16TestSuite) TestBootNotificationRequestValidation() { func (suite *OcppV16TestSuite) TestBootNotificationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.BootNotificationConfirmation{CurrentTime: ocpp16.NewDateTime(time.Now()), Interval: 60, Status: ocpp16.RegistrationStatusAccepted}, true}, - {ocpp16.BootNotificationConfirmation{CurrentTime: ocpp16.NewDateTime(time.Now()), Interval: 60, Status: ocpp16.RegistrationStatusPending}, true}, - {ocpp16.BootNotificationConfirmation{CurrentTime: ocpp16.NewDateTime(time.Now()), Interval: 60, Status: ocpp16.RegistrationStatusRejected}, true}, - {ocpp16.BootNotificationConfirmation{CurrentTime: ocpp16.NewDateTime(time.Now()), Status: ocpp16.RegistrationStatusAccepted}, true}, - {ocpp16.BootNotificationConfirmation{CurrentTime: ocpp16.NewDateTime(time.Now()), Interval: -1, Status: ocpp16.RegistrationStatusRejected}, false}, - {ocpp16.BootNotificationConfirmation{CurrentTime: ocpp16.NewDateTime(time.Now()), Interval: 60, Status: "invalidRegistrationStatus"}, false}, - {ocpp16.BootNotificationConfirmation{CurrentTime: ocpp16.NewDateTime(time.Now()), Interval: 60}, false}, - {ocpp16.BootNotificationConfirmation{Interval: 60, Status: ocpp16.RegistrationStatusAccepted}, false}, + {core.BootNotificationConfirmation{CurrentTime: types.NewDateTime(time.Now()), Interval: 60, Status: core.RegistrationStatusAccepted}, true}, + {core.BootNotificationConfirmation{CurrentTime: types.NewDateTime(time.Now()), Interval: 60, Status: core.RegistrationStatusPending}, true}, + {core.BootNotificationConfirmation{CurrentTime: types.NewDateTime(time.Now()), Interval: 60, Status: core.RegistrationStatusRejected}, true}, + {core.BootNotificationConfirmation{CurrentTime: types.NewDateTime(time.Now()), Status: core.RegistrationStatusAccepted}, true}, + {core.BootNotificationConfirmation{CurrentTime: types.NewDateTime(time.Now()), Interval: -1, Status: core.RegistrationStatusRejected}, false}, + {core.BootNotificationConfirmation{CurrentTime: types.NewDateTime(time.Now()), Interval: 60, Status: "invalidRegistrationStatus"}, false}, + {core.BootNotificationConfirmation{CurrentTime: types.NewDateTime(time.Now()), Interval: 60}, false}, + {core.BootNotificationConfirmation{Interval: 60, Status: core.RegistrationStatusAccepted}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -52,11 +53,11 @@ func (suite *OcppV16TestSuite) TestBootNotificationE2EMocked() { interval := 60 chargePointModel := "model1" chargePointVendor := "ABL" - registrationStatus := ocpp16.RegistrationStatusAccepted - currentTime := ocpp16.NewDateTime(time.Now()) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"chargePointModel":"%v","chargePointVendor":"%v"}]`, messageId, ocpp16.BootNotificationFeatureName, chargePointModel, chargePointVendor) - responseJson := fmt.Sprintf(`[3,"%v",{"currentTime":"%v","interval":%v,"status":"%v"}]`, messageId, currentTime.Time.Format(ocpp16.ISO8601), interval, registrationStatus) - bootNotificationConfirmation := ocpp16.NewBootNotificationConfirmation(currentTime, interval, registrationStatus) + registrationStatus := core.RegistrationStatusAccepted + currentTime := types.NewDateTime(time.Now()) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"chargePointModel":"%v","chargePointVendor":"%v"}]`, messageId, core.BootNotificationFeatureName, chargePointModel, chargePointVendor) + responseJson := fmt.Sprintf(`[3,"%v",{"currentTime":"%v","interval":%v,"status":"%v"}]`, messageId, currentTime.FormatTimestamp(), interval, registrationStatus) + bootNotificationConfirmation := core.NewBootNotificationConfirmation(currentTime, interval, registrationStatus) channel := NewMockWebSocket(wsId) coreListener := MockCentralSystemCoreListener{} @@ -79,7 +80,7 @@ func (suite *OcppV16TestSuite) TestBootNotificationInvalidEndpoint() { messageId := defaultMessageId chargePointModel := "model1" chargePointVendor := "ABL" - bootNotificationRequest := ocpp16.NewBootNotificationRequest(chargePointModel, chargePointVendor) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"chargePointModel":"%v","chargePointVendor":"%v"}]`, messageId, ocpp16.BootNotificationFeatureName, chargePointModel, chargePointVendor) + bootNotificationRequest := core.NewBootNotificationRequest(chargePointModel, chargePointVendor) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"chargePointModel":"%v","chargePointVendor":"%v"}]`, messageId, core.BootNotificationFeatureName, chargePointModel, chargePointVendor) testUnsupportedRequestFromCentralSystem(suite, bootNotificationRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/cancel_reservation_test.go b/ocpp1.6_test/cancel_reservation_test.go index ae91250e..ade6b2e6 100644 --- a/ocpp1.6_test/cancel_reservation_test.go +++ b/ocpp1.6_test/cancel_reservation_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/reservation" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,9 +11,9 @@ import ( func (suite *OcppV16TestSuite) TestCancelReservationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.CancelReservationRequest{ReservationId: 42}, true}, - {ocpp16.CancelReservationRequest{}, true}, - {ocpp16.CancelReservationRequest{ReservationId: -1}, false}, + {reservation.CancelReservationRequest{ReservationId: 42}, true}, + {reservation.CancelReservationRequest{}, true}, + {reservation.CancelReservationRequest{ReservationId: -1}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -21,9 +21,9 @@ func (suite *OcppV16TestSuite) TestCancelReservationRequestValidation() { func (suite *OcppV16TestSuite) TestCancelReservationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.CancelReservationConfirmation{Status: ocpp16.CancelReservationStatusAccepted}, true}, - {ocpp16.CancelReservationConfirmation{Status: "invalidCancelReservationStatus"}, false}, - {ocpp16.CancelReservationConfirmation{}, false}, + {reservation.CancelReservationConfirmation{Status: reservation.CancelReservationStatusAccepted}, true}, + {reservation.CancelReservationConfirmation{Status: "invalidCancelReservationStatus"}, false}, + {reservation.CancelReservationConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -34,27 +34,27 @@ func (suite *OcppV16TestSuite) TestCancelReservationE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" reservationId := 42 - status := ocpp16.CancelReservationStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"reservationId":%v}]`, messageId, ocpp16.CancelReservationFeatureName, reservationId) + status := reservation.CancelReservationStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"reservationId":%v}]`, messageId, reservation.CancelReservationFeatureName, reservationId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - cancelReservationConfirmation := ocpp16.NewCancelReservationConfirmation(status) + cancelReservationConfirmation := reservation.NewCancelReservationConfirmation(status) channel := NewMockWebSocket(wsId) reservationListener := MockChargePointReservationListener{} reservationListener.On("OnCancelReservation", mock.Anything).Return(cancelReservationConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp16.CancelReservationRequest) + request, ok := args.Get(0).(*reservation.CancelReservationRequest) assert.True(t, ok) assert.Equal(t, reservationId, request.ReservationId) }) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - suite.chargePoint.SetReservationListener(reservationListener) + suite.chargePoint.SetReservationHandler(reservationListener) // Run Test suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.CancelReservation(wsId, func(confirmation *ocpp16.CancelReservationConfirmation, err error) { + err = suite.centralSystem.CancelReservation(wsId, func(confirmation *reservation.CancelReservationConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -68,7 +68,7 @@ func (suite *OcppV16TestSuite) TestCancelReservationE2EMocked() { func (suite *OcppV16TestSuite) TestCancelReservationInvalidEndpoint() { messageId := defaultMessageId reservationId := 42 - cancelReservationRequest := ocpp16.NewCancelReservationRequest(reservationId) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"reservationId":%v}]`, messageId, ocpp16.CancelReservationFeatureName, reservationId) + cancelReservationRequest := reservation.NewCancelReservationRequest(reservationId) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"reservationId":%v}]`, messageId, reservation.CancelReservationFeatureName, reservationId) testUnsupportedRequestFromChargePoint(suite, cancelReservationRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/change_availability_test.go b/ocpp1.6_test/change_availability_test.go index 5149ec7b..53e19011 100644 --- a/ocpp1.6_test/change_availability_test.go +++ b/ocpp1.6_test/change_availability_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -10,12 +10,12 @@ import ( func (suite *OcppV16TestSuite) TestChangeAvailabilityRequestValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp16.ChangeAvailabilityRequest{ConnectorId: 0, Type: ocpp16.AvailabilityTypeOperative}, true}, - {ocpp16.ChangeAvailabilityRequest{ConnectorId: 0, Type: ocpp16.AvailabilityTypeInoperative}, true}, - {ocpp16.ChangeAvailabilityRequest{ConnectorId: 0}, false}, - {ocpp16.ChangeAvailabilityRequest{Type: ocpp16.AvailabilityTypeOperative}, true}, - {ocpp16.ChangeAvailabilityRequest{Type: "invalidAvailabilityType"}, false}, - {ocpp16.ChangeAvailabilityRequest{ConnectorId: -1, Type: ocpp16.AvailabilityTypeOperative}, false}, + {core.ChangeAvailabilityRequest{ConnectorId: 0, Type: core.AvailabilityTypeOperative}, true}, + {core.ChangeAvailabilityRequest{ConnectorId: 0, Type: core.AvailabilityTypeInoperative}, true}, + {core.ChangeAvailabilityRequest{ConnectorId: 0}, false}, + {core.ChangeAvailabilityRequest{Type: core.AvailabilityTypeOperative}, true}, + {core.ChangeAvailabilityRequest{Type: "invalidAvailabilityType"}, false}, + {core.ChangeAvailabilityRequest{ConnectorId: -1, Type: core.AvailabilityTypeOperative}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -23,11 +23,11 @@ func (suite *OcppV16TestSuite) TestChangeAvailabilityRequestValidation() { func (suite *OcppV16TestSuite) TestChangeAvailabilityConfirmationValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp16.ChangeAvailabilityConfirmation{Status: ocpp16.AvailabilityStatusAccepted}, true}, - {ocpp16.ChangeAvailabilityConfirmation{Status: ocpp16.AvailabilityStatusRejected}, true}, - {ocpp16.ChangeAvailabilityConfirmation{Status: ocpp16.AvailabilityStatusScheduled}, true}, - {ocpp16.ChangeAvailabilityConfirmation{Status: "invalidAvailabilityStatus"}, false}, - {ocpp16.ChangeAvailabilityConfirmation{}, false}, + {core.ChangeAvailabilityConfirmation{Status: core.AvailabilityStatusAccepted}, true}, + {core.ChangeAvailabilityConfirmation{Status: core.AvailabilityStatusRejected}, true}, + {core.ChangeAvailabilityConfirmation{Status: core.AvailabilityStatusScheduled}, true}, + {core.ChangeAvailabilityConfirmation{Status: "invalidAvailabilityStatus"}, false}, + {core.ChangeAvailabilityConfirmation{}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -39,11 +39,11 @@ func (suite *OcppV16TestSuite) TestChangeAvailabilityE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" connectorId := 1 - availabilityType := ocpp16.AvailabilityTypeOperative - status := ocpp16.AvailabilityStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"type":"%v"}]`, messageId, ocpp16.ChangeAvailabilityFeatureName, connectorId, availabilityType) + availabilityType := core.AvailabilityTypeOperative + status := core.AvailabilityStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"type":"%v"}]`, messageId, core.ChangeAvailabilityFeatureName, connectorId, availabilityType) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - changeAvailabilityConfirmation := ocpp16.NewChangeAvailabilityConfirmation(status) + changeAvailabilityConfirmation := core.NewChangeAvailabilityConfirmation(status) channel := NewMockWebSocket(wsId) // Setting handlers coreListener := MockChargePointCoreListener{} @@ -55,7 +55,7 @@ func (suite *OcppV16TestSuite) TestChangeAvailabilityE2EMocked() { err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.ChangeAvailability(wsId, func(confirmation *ocpp16.ChangeAvailabilityConfirmation, err error) { + err = suite.centralSystem.ChangeAvailability(wsId, func(confirmation *core.ChangeAvailabilityConfirmation, err error) { assert.NotNil(t, confirmation) assert.Nil(t, err) assert.Equal(t, status, confirmation.Status) @@ -69,8 +69,8 @@ func (suite *OcppV16TestSuite) TestChangeAvailabilityE2EMocked() { func (suite *OcppV16TestSuite) TestChangeAvailabilityInvalidEndpoint() { messageId := defaultMessageId connectorId := 1 - availabilityType := ocpp16.AvailabilityTypeOperative - changeAvailabilityRequest := ocpp16.NewChangeAvailabilityRequest(connectorId, availabilityType) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"type":"%v"}]`, messageId, ocpp16.ChangeAvailabilityFeatureName, connectorId, availabilityType) + availabilityType := core.AvailabilityTypeOperative + changeAvailabilityRequest := core.NewChangeAvailabilityRequest(connectorId, availabilityType) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"type":"%v"}]`, messageId, core.ChangeAvailabilityFeatureName, connectorId, availabilityType) testUnsupportedRequestFromChargePoint(suite, changeAvailabilityRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/change_configuration_test.go b/ocpp1.6_test/change_configuration_test.go index 21d86857..3b39d8ed 100644 --- a/ocpp1.6_test/change_configuration_test.go +++ b/ocpp1.6_test/change_configuration_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,12 +11,12 @@ import ( func (suite *OcppV16TestSuite) TestChangeConfigurationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.ChangeConfigurationRequest{Key: "someKey", Value: "someValue"}, true}, - {ocpp16.ChangeConfigurationRequest{Key: "someKey"}, false}, - {ocpp16.ChangeConfigurationRequest{Value: "someValue"}, false}, - {ocpp16.ChangeConfigurationRequest{}, false}, - {ocpp16.ChangeConfigurationRequest{Key: ">50................................................", Value: "someValue"}, false}, - {ocpp16.ChangeConfigurationRequest{Key: "someKey", Value: ">500................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."}, false}, + {core.ChangeConfigurationRequest{Key: "someKey", Value: "someValue"}, true}, + {core.ChangeConfigurationRequest{Key: "someKey"}, false}, + {core.ChangeConfigurationRequest{Value: "someValue"}, false}, + {core.ChangeConfigurationRequest{}, false}, + {core.ChangeConfigurationRequest{Key: ">50................................................", Value: "someValue"}, false}, + {core.ChangeConfigurationRequest{Key: "someKey", Value: ">500................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -24,11 +24,11 @@ func (suite *OcppV16TestSuite) TestChangeConfigurationRequestValidation() { func (suite *OcppV16TestSuite) TestChangeConfigurationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.ChangeConfigurationConfirmation{Status: ocpp16.ConfigurationStatusAccepted}, true}, - {ocpp16.ChangeConfigurationConfirmation{Status: ocpp16.ConfigurationStatusRejected}, true}, - {ocpp16.ChangeConfigurationConfirmation{Status: ocpp16.ConfigurationStatusRebootRequired}, true}, - {ocpp16.ChangeConfigurationConfirmation{Status: ocpp16.ConfigurationStatusNotSupported}, true}, - {ocpp16.ChangeConfigurationConfirmation{Status: "invalidConfigurationStatus"}, false}, + {core.ChangeConfigurationConfirmation{Status: core.ConfigurationStatusAccepted}, true}, + {core.ChangeConfigurationConfirmation{Status: core.ConfigurationStatusRejected}, true}, + {core.ChangeConfigurationConfirmation{Status: core.ConfigurationStatusRebootRequired}, true}, + {core.ChangeConfigurationConfirmation{Status: core.ConfigurationStatusNotSupported}, true}, + {core.ChangeConfigurationConfirmation{Status: "invalidConfigurationStatus"}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -40,10 +40,10 @@ func (suite *OcppV16TestSuite) TestChangeConfigurationE2EMocked() { wsUrl := "someUrl" key := "someKey" value := "someValue" - status := ocpp16.ConfigurationStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"key":"%v","value":"%v"}]`, messageId, ocpp16.ChangeConfigurationFeatureName, key, value) + status := core.ConfigurationStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"key":"%v","value":"%v"}]`, messageId, core.ChangeConfigurationFeatureName, key, value) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - changeConfigurationConfirmation := ocpp16.NewChangeConfigurationConfirmation(status) + changeConfigurationConfirmation := core.NewChangeConfigurationConfirmation(status) channel := NewMockWebSocket(wsId) coreListener := MockChargePointCoreListener{} @@ -55,7 +55,7 @@ func (suite *OcppV16TestSuite) TestChangeConfigurationE2EMocked() { err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.ChangeConfiguration(wsId, func(confirmation *ocpp16.ChangeConfigurationConfirmation, err error) { + err = suite.centralSystem.ChangeConfiguration(wsId, func(confirmation *core.ChangeConfigurationConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -70,7 +70,7 @@ func (suite *OcppV16TestSuite) TestChangeConfigurationInvalidEndpoint() { messageId := defaultMessageId key := "someKey" value := "someValue" - changeConfigurationRequest := ocpp16.NewChangeConfigurationRequest(key, value) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"key":"%v","value":"%v"}]`, messageId, ocpp16.ChangeConfigurationFeatureName, key, value) + changeConfigurationRequest := core.NewChangeConfigurationRequest(key, value) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"key":"%v","value":"%v"}]`, messageId, core.ChangeConfigurationFeatureName, key, value) testUnsupportedRequestFromChargePoint(suite, changeConfigurationRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/clear_cache_test.go b/ocpp1.6_test/clear_cache_test.go index 263b3a40..d37c44c8 100644 --- a/ocpp1.6_test/clear_cache_test.go +++ b/ocpp1.6_test/clear_cache_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,7 +11,7 @@ import ( func (suite *OcppV16TestSuite) TestClearCacheRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.ClearCacheRequest{}, true}, + {core.ClearCacheRequest{}, true}, } ExecuteGenericTestTable(t, requestTable) } @@ -19,10 +19,10 @@ func (suite *OcppV16TestSuite) TestClearCacheRequestValidation() { func (suite *OcppV16TestSuite) TestClearCacheConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.ClearCacheConfirmation{Status: ocpp16.ClearCacheStatusAccepted}, true}, - {ocpp16.ClearCacheConfirmation{Status: ocpp16.ClearCacheStatusRejected}, true}, - {ocpp16.ClearCacheConfirmation{Status: "invalidClearCacheStatus"}, false}, - {ocpp16.ClearCacheConfirmation{}, false}, + {core.ClearCacheConfirmation{Status: core.ClearCacheStatusAccepted}, true}, + {core.ClearCacheConfirmation{Status: core.ClearCacheStatusRejected}, true}, + {core.ClearCacheConfirmation{Status: "invalidClearCacheStatus"}, false}, + {core.ClearCacheConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -32,10 +32,10 @@ func (suite *OcppV16TestSuite) TestClearCacheE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - status := ocpp16.ClearCacheStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, ocpp16.ClearCacheFeatureName) + status := core.ClearCacheStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, core.ClearCacheFeatureName) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - clearCacheConfirmation := ocpp16.NewClearCacheConfirmation(status) + clearCacheConfirmation := core.NewClearCacheConfirmation(status) channel := NewMockWebSocket(wsId) coreListener := MockChargePointCoreListener{} @@ -47,7 +47,7 @@ func (suite *OcppV16TestSuite) TestClearCacheE2EMocked() { err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.ClearCache(wsId, func(confirmation *ocpp16.ClearCacheConfirmation, err error) { + err = suite.centralSystem.ClearCache(wsId, func(confirmation *core.ClearCacheConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -60,7 +60,7 @@ func (suite *OcppV16TestSuite) TestClearCacheE2EMocked() { func (suite *OcppV16TestSuite) TestClearCacheInvalidEndpoint() { messageId := defaultMessageId - clearCacheRequest := ocpp16.NewClearCacheRequest() - requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, ocpp16.ClearCacheFeatureName) + clearCacheRequest := core.NewClearCacheRequest() + requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, core.ClearCacheFeatureName) testUnsupportedRequestFromChargePoint(suite, clearCacheRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/clear_charging_profile_test.go b/ocpp1.6_test/clear_charging_profile_test.go index 40eeb97f..6432f907 100644 --- a/ocpp1.6_test/clear_charging_profile_test.go +++ b/ocpp1.6_test/clear_charging_profile_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,15 +12,15 @@ import ( func (suite *OcppV16TestSuite) TestClearChargingProfileRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.ClearChargingProfileRequest{Id: 1, ConnectorId: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, StackLevel: 1}, true}, - {ocpp16.ClearChargingProfileRequest{Id: 1, ConnectorId: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile}, true}, - {ocpp16.ClearChargingProfileRequest{Id: 1, ConnectorId: 1}, true}, - {ocpp16.ClearChargingProfileRequest{Id: 1}, true}, - {ocpp16.ClearChargingProfileRequest{}, true}, - {ocpp16.ClearChargingProfileRequest{ConnectorId: -1}, false}, - {ocpp16.ClearChargingProfileRequest{Id: -1}, false}, - {ocpp16.ClearChargingProfileRequest{ChargingProfilePurpose: "invalidChargingProfilePurposeType"}, false}, - {ocpp16.ClearChargingProfileRequest{StackLevel: -1}, false}, + {smartcharging.ClearChargingProfileRequest{Id: 1, ConnectorId: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, StackLevel: 1}, true}, + {smartcharging.ClearChargingProfileRequest{Id: 1, ConnectorId: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile}, true}, + {smartcharging.ClearChargingProfileRequest{Id: 1, ConnectorId: 1}, true}, + {smartcharging.ClearChargingProfileRequest{Id: 1}, true}, + {smartcharging.ClearChargingProfileRequest{}, true}, + {smartcharging.ClearChargingProfileRequest{ConnectorId: -1}, false}, + {smartcharging.ClearChargingProfileRequest{Id: -1}, false}, + {smartcharging.ClearChargingProfileRequest{ChargingProfilePurpose: "invalidChargingProfilePurposeType"}, false}, + {smartcharging.ClearChargingProfileRequest{StackLevel: -1}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -27,9 +28,9 @@ func (suite *OcppV16TestSuite) TestClearChargingProfileRequestValidation() { func (suite *OcppV16TestSuite) TestClearChargingProfileConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.ClearChargingProfileConfirmation{Status: ocpp16.ClearChargingProfileStatusAccepted}, true}, - {ocpp16.ClearChargingProfileConfirmation{Status: "invalidClearChargingProfileStatus"}, false}, - {ocpp16.ClearChargingProfileConfirmation{}, false}, + {smartcharging.ClearChargingProfileConfirmation{Status: smartcharging.ClearChargingProfileStatusAccepted}, true}, + {smartcharging.ClearChargingProfileConfirmation{Status: "invalidClearChargingProfileStatus"}, false}, + {smartcharging.ClearChargingProfileConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -41,18 +42,18 @@ func (suite *OcppV16TestSuite) TestClearChargingProfileE2EMocked() { wsUrl := "someUrl" chargingProfileId := 1 connectorId := 1 - chargingProfilePurpose := ocpp16.ChargingProfilePurposeChargePointMaxProfile + chargingProfilePurpose := types.ChargingProfilePurposeChargePointMaxProfile stackLevel := 1 - status := ocpp16.ClearChargingProfileStatusAccepted + status := smartcharging.ClearChargingProfileStatusAccepted requestJson := fmt.Sprintf(`[2,"%v","%v",{"id":%v,"connectorId":%v,"chargingProfilePurpose":"%v","stackLevel":%v}]`, - messageId, ocpp16.ClearChargingProfileFeatureName, chargingProfileId, connectorId, chargingProfilePurpose, stackLevel) + messageId, smartcharging.ClearChargingProfileFeatureName, chargingProfileId, connectorId, chargingProfilePurpose, stackLevel) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - ClearChargingProfileConfirmation := ocpp16.NewClearChargingProfileConfirmation(status) + ClearChargingProfileConfirmation := smartcharging.NewClearChargingProfileConfirmation(status) channel := NewMockWebSocket(wsId) smartChargingListener := MockChargePointSmartChargingListener{} smartChargingListener.On("OnClearChargingProfile", mock.Anything).Return(ClearChargingProfileConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp16.ClearChargingProfileRequest) + request, ok := args.Get(0).(*smartcharging.ClearChargingProfileRequest) assert.True(t, ok) assert.NotNil(t, request) assert.Equal(t, chargingProfileId, request.Id) @@ -62,20 +63,20 @@ func (suite *OcppV16TestSuite) TestClearChargingProfileE2EMocked() { }) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - suite.chargePoint.SetSmartChargingListener(smartChargingListener) + suite.chargePoint.SetSmartChargingHandler(smartChargingListener) // Run Test suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.ClearChargingProfile(wsId, func(confirmation *ocpp16.ClearChargingProfileConfirmation, err error) { + err = suite.centralSystem.ClearChargingProfile(wsId, func(confirmation *smartcharging.ClearChargingProfileConfirmation, err error) { if !assert.Nil(t, err) || !assert.NotNil(t, confirmation) { resultChannel <- false return } assert.Equal(t, status, confirmation.Status) resultChannel <- true - }, func(request *ocpp16.ClearChargingProfileRequest) { + }, func(request *smartcharging.ClearChargingProfileRequest) { request.Id = chargingProfileId request.ConnectorId = connectorId request.ChargingProfilePurpose = chargingProfilePurpose @@ -91,10 +92,10 @@ func (suite *OcppV16TestSuite) TestClearChargingProfileInvalidEndpoint() { connectorId := 1 chargingProfileId := 1 stackLevel := 1 - chargingProfilePurpose := ocpp16.ChargingProfilePurposeChargePointMaxProfile + chargingProfilePurpose := types.ChargingProfilePurposeChargePointMaxProfile requestJson := fmt.Sprintf(`[2,"%v","%v",{"id":%v,"connectorId":%v,"chargingProfilePurpose":"%v","stackLevel":%v}]`, - messageId, ocpp16.ClearChargingProfileFeatureName, chargingProfileId, connectorId, chargingProfilePurpose, stackLevel) - clearChargingProfileRequest := ocpp16.NewClearChargingProfileRequest() + messageId, smartcharging.ClearChargingProfileFeatureName, chargingProfileId, connectorId, chargingProfilePurpose, stackLevel) + clearChargingProfileRequest := smartcharging.NewClearChargingProfileRequest() clearChargingProfileRequest.Id = chargingProfileId clearChargingProfileRequest.ConnectorId = connectorId clearChargingProfileRequest.ChargingProfilePurpose = chargingProfilePurpose diff --git a/ocpp1.6_test/common_test.go b/ocpp1.6_test/common_test.go index c9c365b3..d6badbf8 100644 --- a/ocpp1.6_test/common_test.go +++ b/ocpp1.6_test/common_test.go @@ -1,23 +1,23 @@ package ocpp16_test import ( - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "time" ) // Test func (suite *OcppV16TestSuite) TestIdTagInfoValidation() { var testTable = []GenericTestEntry{ - {ocpp16.IdTagInfo{ExpiryDate: ocpp16.NewDateTime(time.Now()), ParentIdTag: "00000", Status: ocpp16.AuthorizationStatusAccepted}, true}, - {ocpp16.IdTagInfo{ExpiryDate: ocpp16.NewDateTime(time.Now()), Status: ocpp16.AuthorizationStatusAccepted}, true}, - {ocpp16.IdTagInfo{Status: ocpp16.AuthorizationStatusAccepted}, true}, - {ocpp16.IdTagInfo{Status: ocpp16.AuthorizationStatusBlocked}, true}, - {ocpp16.IdTagInfo{Status: ocpp16.AuthorizationStatusExpired}, true}, - {ocpp16.IdTagInfo{Status: ocpp16.AuthorizationStatusInvalid}, true}, - {ocpp16.IdTagInfo{Status: ocpp16.AuthorizationStatusConcurrentTx}, true}, - {ocpp16.IdTagInfo{Status: "invalidAuthorizationStatus"}, false}, - {ocpp16.IdTagInfo{}, false}, - {ocpp16.IdTagInfo{ExpiryDate: ocpp16.NewDateTime(time.Now()), ParentIdTag: ">20..................", Status: ocpp16.AuthorizationStatusAccepted}, false}, + {types.IdTagInfo{ExpiryDate: types.NewDateTime(time.Now()), ParentIdTag: "00000", Status: types.AuthorizationStatusAccepted}, true}, + {types.IdTagInfo{ExpiryDate: types.NewDateTime(time.Now()), Status: types.AuthorizationStatusAccepted}, true}, + {types.IdTagInfo{Status: types.AuthorizationStatusAccepted}, true}, + {types.IdTagInfo{Status: types.AuthorizationStatusBlocked}, true}, + {types.IdTagInfo{Status: types.AuthorizationStatusExpired}, true}, + {types.IdTagInfo{Status: types.AuthorizationStatusInvalid}, true}, + {types.IdTagInfo{Status: types.AuthorizationStatusConcurrentTx}, true}, + {types.IdTagInfo{Status: "invalidAuthorizationStatus"}, false}, + {types.IdTagInfo{}, false}, + {types.IdTagInfo{ExpiryDate: types.NewDateTime(time.Now()), ParentIdTag: ">20..................", Status: types.AuthorizationStatusAccepted}, false}, } ExecuteGenericTestTable(suite.T(), testTable) } @@ -25,52 +25,52 @@ func (suite *OcppV16TestSuite) TestIdTagInfoValidation() { func (suite *OcppV16TestSuite) TestChargingSchedulePeriodValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp16.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: 3}, true}, - {ocpp16.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0}, true}, - {ocpp16.ChargingSchedulePeriod{StartPeriod: 0}, true}, - {ocpp16.ChargingSchedulePeriod{}, true}, - {ocpp16.ChargingSchedulePeriod{StartPeriod: 0, Limit: -1.0}, false}, - {ocpp16.ChargingSchedulePeriod{StartPeriod: -1, Limit: 10.0}, false}, - {ocpp16.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: -1}, false}, + {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: 3}, true}, + {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0}, true}, + {types.ChargingSchedulePeriod{StartPeriod: 0}, true}, + {types.ChargingSchedulePeriod{}, true}, + {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: -1.0}, false}, + {types.ChargingSchedulePeriod{StartPeriod: -1, Limit: 10.0}, false}, + {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: -1}, false}, } ExecuteGenericTestTable(t, testTable) } func (suite *OcppV16TestSuite) TestChargingScheduleValidation() { t := suite.T() - chargingSchedulePeriods := make([]ocpp16.ChargingSchedulePeriod, 2) - chargingSchedulePeriods[0] = ocpp16.NewChargingSchedulePeriod(0, 10.0) - chargingSchedulePeriods[1] = ocpp16.NewChargingSchedulePeriod(100, 8.0) + chargingSchedulePeriods := make([]types.ChargingSchedulePeriod, 2) + chargingSchedulePeriods[0] = types.NewChargingSchedulePeriod(0, 10.0) + chargingSchedulePeriods[1] = types.NewChargingSchedulePeriod(100, 8.0) var testTable = []GenericTestEntry{ - {ocpp16.ChargingSchedule{Duration: 0, StartSchedule: ocpp16.NewDateTime(time.Now()), ChargingRateUnit: ocpp16.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: 1.0}, true}, - {ocpp16.ChargingSchedule{Duration: 0, ChargingRateUnit: ocpp16.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: 1.0}, true}, - {ocpp16.ChargingSchedule{Duration: 0, ChargingRateUnit: ocpp16.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods}, true}, - {ocpp16.ChargingSchedule{Duration: 0, ChargingRateUnit: ocpp16.ChargingRateUnitWatts}, false}, - {ocpp16.ChargingSchedule{Duration: 0, ChargingSchedulePeriod: chargingSchedulePeriods}, false}, - {ocpp16.ChargingSchedule{Duration: -1, StartSchedule: ocpp16.NewDateTime(time.Now()), ChargingRateUnit: ocpp16.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: 1.0}, false}, - {ocpp16.ChargingSchedule{Duration: 0, StartSchedule: ocpp16.NewDateTime(time.Now()), ChargingRateUnit: ocpp16.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: -1.0}, false}, - {ocpp16.ChargingSchedule{Duration: 0, StartSchedule: ocpp16.NewDateTime(time.Now()), ChargingRateUnit: ocpp16.ChargingRateUnitWatts, ChargingSchedulePeriod: make([]ocpp16.ChargingSchedulePeriod, 0), MinChargingRate: 1.0}, false}, - {ocpp16.ChargingSchedule{Duration: -1, StartSchedule: ocpp16.NewDateTime(time.Now()), ChargingRateUnit: "invalidChargeRateUnit", ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: 1.0}, false}, + {types.ChargingSchedule{Duration: 0, StartSchedule: types.NewDateTime(time.Now()), ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: 1.0}, true}, + {types.ChargingSchedule{Duration: 0, ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: 1.0}, true}, + {types.ChargingSchedule{Duration: 0, ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods}, true}, + {types.ChargingSchedule{Duration: 0, ChargingRateUnit: types.ChargingRateUnitWatts}, false}, + {types.ChargingSchedule{Duration: 0, ChargingSchedulePeriod: chargingSchedulePeriods}, false}, + {types.ChargingSchedule{Duration: -1, StartSchedule: types.NewDateTime(time.Now()), ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: 1.0}, false}, + {types.ChargingSchedule{Duration: 0, StartSchedule: types.NewDateTime(time.Now()), ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: -1.0}, false}, + {types.ChargingSchedule{Duration: 0, StartSchedule: types.NewDateTime(time.Now()), ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: make([]types.ChargingSchedulePeriod, 0), MinChargingRate: 1.0}, false}, + {types.ChargingSchedule{Duration: -1, StartSchedule: types.NewDateTime(time.Now()), ChargingRateUnit: "invalidChargeRateUnit", ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: 1.0}, false}, } ExecuteGenericTestTable(t, testTable) } func (suite *OcppV16TestSuite) TestChargingProfileValidation() { t := suite.T() - chargingSchedule := ocpp16.NewChargingSchedule(ocpp16.ChargingRateUnitWatts, ocpp16.NewChargingSchedulePeriod(0, 10.0), ocpp16.NewChargingSchedulePeriod(100, 8.0)) + chargingSchedule := types.NewChargingSchedule(types.ChargingRateUnitWatts, types.NewChargingSchedulePeriod(0, 10.0), types.NewChargingSchedulePeriod(100, 8.0)) var testTable = []GenericTestEntry{ - {ocpp16.ChargingProfile{ChargingProfileId: 1, TransactionId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: ocpp16.ChargingProfileKindAbsolute, RecurrencyKind: ocpp16.RecurrencyKindDaily, ValidFrom: ocpp16.NewDateTime(time.Now()), ValidTo: ocpp16.NewDateTime(time.Now().Add(8 * time.Hour)), ChargingSchedule: chargingSchedule}, true}, - {ocpp16.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: ocpp16.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, true}, - {ocpp16.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: ocpp16.ChargingProfileKindAbsolute}, false}, - {ocpp16.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, ChargingSchedule: chargingSchedule}, false}, - {ocpp16.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfileKind: ocpp16.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, - {ocpp16.ChargingProfile{ChargingProfileId: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: ocpp16.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, - {ocpp16.ChargingProfile{StackLevel: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: ocpp16.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, true}, - {ocpp16.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: "invalidChargingProfileKind", ChargingSchedule: chargingSchedule}, false}, - {ocpp16.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: "invalidChargingProfilePurpose", ChargingProfileKind: ocpp16.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, - {ocpp16.ChargingProfile{ChargingProfileId: 1, StackLevel: 0, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: ocpp16.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, - {ocpp16.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: ocpp16.ChargingProfileKindAbsolute, RecurrencyKind: "invalidRecurrencyKind", ChargingSchedule: chargingSchedule}, false}, - {ocpp16.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp16.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: ocpp16.ChargingProfileKindAbsolute, ChargingSchedule: ocpp16.NewChargingSchedule(ocpp16.ChargingRateUnitWatts)}, false}, + {types.ChargingProfile{ChargingProfileId: 1, TransactionId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, RecurrencyKind: types.RecurrencyKindDaily, ValidFrom: types.NewDateTime(time.Now()), ValidTo: types.NewDateTime(time.Now().Add(8 * time.Hour)), ChargingSchedule: chargingSchedule}, true}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, true}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, true}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: "invalidChargingProfileKind", ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: "invalidChargingProfilePurpose", ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 0, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, RecurrencyKind: "invalidRecurrencyKind", ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargePointMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: types.NewChargingSchedule(types.ChargingRateUnitWatts)}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -78,30 +78,30 @@ func (suite *OcppV16TestSuite) TestChargingProfileValidation() { func (suite *OcppV16TestSuite) TestSampledValueValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp16.SampledValue{Value: "value", Context: ocpp16.ReadingContextTransactionEnd, Format: ocpp16.ValueFormatRaw, Measurand: ocpp16.MeasurandPowerActiveExport, Phase: ocpp16.PhaseL2, Location: ocpp16.LocationBody, Unit: ocpp16.UnitOfMeasureKW}, true}, - {ocpp16.SampledValue{Value: "value", Context: ocpp16.ReadingContextTransactionEnd, Format: ocpp16.ValueFormatRaw, Measurand: ocpp16.MeasurandPowerActiveExport, Phase: ocpp16.PhaseL2, Location: ocpp16.LocationBody}, true}, - {ocpp16.SampledValue{Value: "value", Context: ocpp16.ReadingContextTransactionEnd, Format: ocpp16.ValueFormatRaw, Measurand: ocpp16.MeasurandPowerActiveExport, Phase: ocpp16.PhaseL2}, true}, - {ocpp16.SampledValue{Value: "value", Context: ocpp16.ReadingContextTransactionEnd, Format: ocpp16.ValueFormatRaw, Measurand: ocpp16.MeasurandPowerActiveExport}, true}, - {ocpp16.SampledValue{Value: "value", Context: ocpp16.ReadingContextTransactionEnd, Format: ocpp16.ValueFormatRaw}, true}, - {ocpp16.SampledValue{Value: "value", Context: ocpp16.ReadingContextTransactionEnd}, true}, - {ocpp16.SampledValue{Value: "value"}, true}, - {ocpp16.SampledValue{Value: "value", Context: "invalidContext"}, false}, - {ocpp16.SampledValue{Value: "value", Format: "invalidFormat"}, false}, - {ocpp16.SampledValue{Value: "value", Measurand: "invalidMeasurand"}, false}, - {ocpp16.SampledValue{Value: "value", Phase: "invalidPhase"}, false}, - {ocpp16.SampledValue{Value: "value", Location: "invalidLocation"}, false}, - {ocpp16.SampledValue{Value: "value", Unit: "invalidUnit"}, false}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd, Format: types.ValueFormatRaw, Measurand: types.MeasurandPowerActiveExport, Phase: types.PhaseL2, Location: types.LocationBody, Unit: types.UnitOfMeasureKW}, true}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd, Format: types.ValueFormatRaw, Measurand: types.MeasurandPowerActiveExport, Phase: types.PhaseL2, Location: types.LocationBody}, true}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd, Format: types.ValueFormatRaw, Measurand: types.MeasurandPowerActiveExport, Phase: types.PhaseL2}, true}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd, Format: types.ValueFormatRaw, Measurand: types.MeasurandPowerActiveExport}, true}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd, Format: types.ValueFormatRaw}, true}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd}, true}, + {types.SampledValue{Value: "value"}, true}, + {types.SampledValue{Value: "value", Context: "invalidContext"}, false}, + {types.SampledValue{Value: "value", Format: "invalidFormat"}, false}, + {types.SampledValue{Value: "value", Measurand: "invalidMeasurand"}, false}, + {types.SampledValue{Value: "value", Phase: "invalidPhase"}, false}, + {types.SampledValue{Value: "value", Location: "invalidLocation"}, false}, + {types.SampledValue{Value: "value", Unit: "invalidUnit"}, false}, } ExecuteGenericTestTable(t, testTable) } func (suite *OcppV16TestSuite) TestMeterValueValidation() { var testTable = []GenericTestEntry{ - {ocpp16.MeterValue{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{{Value: "value"}, {Value: "value2", Unit: ocpp16.UnitOfMeasureKW}}}, true}, - {ocpp16.MeterValue{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{{Value: "value"}}}, true}, - {ocpp16.MeterValue{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{}}, false}, - {ocpp16.MeterValue{Timestamp: ocpp16.NewDateTime(time.Now())}, false}, - {ocpp16.MeterValue{SampledValue: []ocpp16.SampledValue{{Value: "value"}}}, false}, + {types.MeterValue{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: "value"}, {Value: "value2", Unit: types.UnitOfMeasureKW}}}, true}, + {types.MeterValue{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: "value"}}}, true}, + {types.MeterValue{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{}}, false}, + {types.MeterValue{Timestamp: types.NewDateTime(time.Now())}, false}, + {types.MeterValue{SampledValue: []types.SampledValue{{Value: "value"}}}, false}, } ExecuteGenericTestTable(suite.T(), testTable) } diff --git a/ocpp1.6_test/data_transfer_test.go b/ocpp1.6_test/data_transfer_test.go index 14d3ad88..4e638919 100644 --- a/ocpp1.6_test/data_transfer_test.go +++ b/ocpp1.6_test/data_transfer_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,12 +11,12 @@ import ( func (suite *OcppV16TestSuite) TestDataTransferRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.DataTransferRequest{VendorId: "12345"}, true}, - {ocpp16.DataTransferRequest{VendorId: "12345", MessageId: "6789"}, true}, - {ocpp16.DataTransferRequest{VendorId: "12345", MessageId: "6789", Data: "mockData"}, true}, - {ocpp16.DataTransferRequest{}, false}, - {ocpp16.DataTransferRequest{VendorId: ">255............................................................................................................................................................................................................................................................"}, false}, - {ocpp16.DataTransferRequest{VendorId: "12345", MessageId: ">50................................................"}, false}, + {core.DataTransferRequest{VendorId: "12345"}, true}, + {core.DataTransferRequest{VendorId: "12345", MessageId: "6789"}, true}, + {core.DataTransferRequest{VendorId: "12345", MessageId: "6789", Data: "mockData"}, true}, + {core.DataTransferRequest{}, false}, + {core.DataTransferRequest{VendorId: ">255............................................................................................................................................................................................................................................................"}, false}, + {core.DataTransferRequest{VendorId: "12345", MessageId: ">50................................................"}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -24,12 +24,12 @@ func (suite *OcppV16TestSuite) TestDataTransferRequestValidation() { func (suite *OcppV16TestSuite) TestDataTransferConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.DataTransferConfirmation{Status: ocpp16.DataTransferStatusAccepted}, true}, - {ocpp16.DataTransferConfirmation{Status: ocpp16.DataTransferStatusRejected}, true}, - {ocpp16.DataTransferConfirmation{Status: ocpp16.DataTransferStatusUnknownMessageId}, true}, - {ocpp16.DataTransferConfirmation{Status: ocpp16.DataTransferStatusUnknownVendorId}, true}, - {ocpp16.DataTransferConfirmation{Status: "invalidDataTransferStatus"}, false}, - {ocpp16.DataTransferConfirmation{Status: ocpp16.DataTransferStatusAccepted, Data: "mockData"}, true}, + {core.DataTransferConfirmation{Status: core.DataTransferStatusAccepted}, true}, + {core.DataTransferConfirmation{Status: core.DataTransferStatusRejected}, true}, + {core.DataTransferConfirmation{Status: core.DataTransferStatusUnknownMessageId}, true}, + {core.DataTransferConfirmation{Status: core.DataTransferStatusUnknownVendorId}, true}, + {core.DataTransferConfirmation{Status: "invalidDataTransferStatus"}, false}, + {core.DataTransferConfirmation{Status: core.DataTransferStatusAccepted, Data: "mockData"}, true}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -40,10 +40,10 @@ func (suite *OcppV16TestSuite) TestDataTransferFromChargePointE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" vendorId := "vendor1" - status := ocpp16.DataTransferStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"vendorId":"%v"}]`, messageId, ocpp16.DataTransferFeatureName, vendorId) + status := core.DataTransferStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"vendorId":"%v"}]`, messageId, core.DataTransferFeatureName, vendorId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - dataTransferConfirmation := ocpp16.NewDataTransferConfirmation(status) + dataTransferConfirmation := core.NewDataTransferConfirmation(status) channel := NewMockWebSocket(wsId) coreListener := MockCentralSystemCoreListener{} @@ -66,10 +66,10 @@ func (suite *OcppV16TestSuite) TestDataTransferFromCentralSystemE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" vendorId := "vendor1" - status := ocpp16.DataTransferStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"vendorId":"%v"}]`, messageId, ocpp16.DataTransferFeatureName, vendorId) + status := core.DataTransferStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"vendorId":"%v"}]`, messageId, core.DataTransferFeatureName, vendorId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - dataTransferConfirmation := ocpp16.NewDataTransferConfirmation(status) + dataTransferConfirmation := core.NewDataTransferConfirmation(status) channel := NewMockWebSocket(wsId) coreListener := MockChargePointCoreListener{} @@ -81,7 +81,7 @@ func (suite *OcppV16TestSuite) TestDataTransferFromCentralSystemE2EMocked() { err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.DataTransfer(wsId, func(confirmation *ocpp16.DataTransferConfirmation, err error) { + err = suite.centralSystem.DataTransfer(wsId, func(confirmation *core.DataTransferConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) diff --git a/ocpp1.6_test/diagnostics_status_notification_test.go b/ocpp1.6_test/diagnostics_status_notification_test.go index 84cb2b2e..0b256716 100644 --- a/ocpp1.6_test/diagnostics_status_notification_test.go +++ b/ocpp1.6_test/diagnostics_status_notification_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,9 +11,9 @@ import ( func (suite *OcppV16TestSuite) TestDiagnosticsStatusNotificationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.DiagnosticsStatusNotificationRequest{Status: ocpp16.DiagnosticsStatusUploaded}, true}, - {ocpp16.DiagnosticsStatusNotificationRequest{}, false}, - {ocpp16.DiagnosticsStatusNotificationRequest{Status: "invalidDiagnosticsStatus"}, false}, + {firmware.DiagnosticsStatusNotificationRequest{Status: firmware.DiagnosticsStatusUploaded}, true}, + {firmware.DiagnosticsStatusNotificationRequest{}, false}, + {firmware.DiagnosticsStatusNotificationRequest{Status: "invalidDiagnosticsStatus"}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -21,7 +21,7 @@ func (suite *OcppV16TestSuite) TestDiagnosticsStatusNotificationRequestValidatio func (suite *OcppV16TestSuite) TestDiagnosticsStatusNotificationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.DiagnosticsStatusNotificationConfirmation{}, true}, + {firmware.DiagnosticsStatusNotificationConfirmation{}, true}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -31,20 +31,20 @@ func (suite *OcppV16TestSuite) TestDiagnosticsStatusNotificationE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - status := ocpp16.DiagnosticsStatusUploaded - requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v"}]`, messageId, ocpp16.DiagnosticsStatusNotificationFeatureName, status) + status := firmware.DiagnosticsStatusUploaded + requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v"}]`, messageId, firmware.DiagnosticsStatusNotificationFeatureName, status) responseJson := fmt.Sprintf(`[3,"%v",{}]`, messageId) - diagnosticsStatusNotificationConfirmation := ocpp16.NewDiagnosticsStatusNotificationConfirmation() + diagnosticsStatusNotificationConfirmation := firmware.NewDiagnosticsStatusNotificationConfirmation() channel := NewMockWebSocket(wsId) firmwareListener := MockCentralSystemFirmwareManagementListener{} firmwareListener.On("OnDiagnosticsStatusNotification", mock.AnythingOfType("string"), mock.Anything).Return(diagnosticsStatusNotificationConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(1).(*ocpp16.DiagnosticsStatusNotificationRequest) + request, ok := args.Get(1).(*firmware.DiagnosticsStatusNotificationRequest) assert.True(t, ok) assert.Equal(t, status, request.Status) }) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - suite.centralSystem.SetFirmwareManagementListener(firmwareListener) + suite.centralSystem.SetFirmwareManagementHandler(firmwareListener) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) // Run Test suite.centralSystem.Start(8887, "somePath") @@ -57,8 +57,8 @@ func (suite *OcppV16TestSuite) TestDiagnosticsStatusNotificationE2EMocked() { func (suite *OcppV16TestSuite) TestDiagnosticsStatusNotificationInvalidEndpoint() { messageId := defaultMessageId - status := ocpp16.DiagnosticsStatusUploaded - diagnosticsStatusRequest := ocpp16.NewDiagnosticsStatusNotificationRequest(status) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v"}]`, messageId, ocpp16.DiagnosticsStatusNotificationFeatureName, status) + status := firmware.DiagnosticsStatusUploaded + diagnosticsStatusRequest := firmware.NewDiagnosticsStatusNotificationRequest(status) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v"}]`, messageId, firmware.DiagnosticsStatusNotificationFeatureName, status) testUnsupportedRequestFromCentralSystem(suite, diagnosticsStatusRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/firmware_status_notification_test.go b/ocpp1.6_test/firmware_status_notification_test.go index 47a611ca..5118a152 100644 --- a/ocpp1.6_test/firmware_status_notification_test.go +++ b/ocpp1.6_test/firmware_status_notification_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,9 +11,9 @@ import ( func (suite *OcppV16TestSuite) TestFirmwareStatusNotificationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.FirmwareStatusNotificationRequest{Status: ocpp16.FirmwareStatusDownloaded}, true}, - {ocpp16.FirmwareStatusNotificationRequest{}, false}, - {ocpp16.FirmwareStatusNotificationRequest{Status: "invalidFirmwareStatus"}, false}, + {firmware.FirmwareStatusNotificationRequest{Status: firmware.FirmwareStatusDownloaded}, true}, + {firmware.FirmwareStatusNotificationRequest{}, false}, + {firmware.FirmwareStatusNotificationRequest{Status: "invalidFirmwareStatus"}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -21,7 +21,7 @@ func (suite *OcppV16TestSuite) TestFirmwareStatusNotificationRequestValidation() func (suite *OcppV16TestSuite) TestFirmwareStatusNotificationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.FirmwareStatusNotificationConfirmation{}, true}, + {firmware.FirmwareStatusNotificationConfirmation{}, true}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -31,20 +31,20 @@ func (suite *OcppV16TestSuite) TestFirmwareStatusNotificationE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - status := ocpp16.FirmwareStatusDownloaded - requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v"}]`, messageId, ocpp16.FirmwareStatusNotificationFeatureName, status) + status := firmware.FirmwareStatusDownloaded + requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v"}]`, messageId, firmware.FirmwareStatusNotificationFeatureName, status) responseJson := fmt.Sprintf(`[3,"%v",{}]`, messageId) - firmwareStatusNotificationConfirmation := ocpp16.NewFirmwareStatusNotificationConfirmation() + firmwareStatusNotificationConfirmation := firmware.NewFirmwareStatusNotificationConfirmation() channel := NewMockWebSocket(wsId) firmwareListener := MockCentralSystemFirmwareManagementListener{} firmwareListener.On("OnFirmwareStatusNotification", mock.AnythingOfType("string"), mock.Anything).Return(firmwareStatusNotificationConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(1).(*ocpp16.FirmwareStatusNotificationRequest) + request, ok := args.Get(1).(*firmware.FirmwareStatusNotificationRequest) assert.True(t, ok) assert.Equal(t, status, request.Status) }) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - suite.centralSystem.SetFirmwareManagementListener(firmwareListener) + suite.centralSystem.SetFirmwareManagementHandler(firmwareListener) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) // Run Test suite.centralSystem.Start(8887, "somePath") @@ -57,8 +57,8 @@ func (suite *OcppV16TestSuite) TestFirmwareStatusNotificationE2EMocked() { func (suite *OcppV16TestSuite) TestFirmwareStatusNotificationInvalidEndpoint() { messageId := defaultMessageId - status := ocpp16.FirmwareStatusDownloaded - firmwareStatusRequest := ocpp16.NewFirmwareStatusNotificationRequest(status) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v"}]`, messageId, ocpp16.FirmwareStatusNotificationFeatureName, status) + status := firmware.FirmwareStatusDownloaded + firmwareStatusRequest := firmware.NewFirmwareStatusNotificationRequest(status) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v"}]`, messageId, firmware.FirmwareStatusNotificationFeatureName, status) testUnsupportedRequestFromCentralSystem(suite, firmwareStatusRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/get_composite_schedule_test.go b/ocpp1.6_test/get_composite_schedule_test.go index 62851178..2b61ea09 100644 --- a/ocpp1.6_test/get_composite_schedule_test.go +++ b/ocpp1.6_test/get_composite_schedule_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -12,29 +13,29 @@ import ( func (suite *OcppV16TestSuite) TestGetCompositeScheduleRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.GetCompositeScheduleRequest{ConnectorId: 1, Duration: 600, ChargingRateUnit: ocpp16.ChargingRateUnitWatts}, true}, - {ocpp16.GetCompositeScheduleRequest{ConnectorId: 1, Duration: 600}, true}, - {ocpp16.GetCompositeScheduleRequest{ConnectorId: 1}, true}, - {ocpp16.GetCompositeScheduleRequest{}, true}, - {ocpp16.GetCompositeScheduleRequest{ConnectorId: -1, Duration: 600, ChargingRateUnit: ocpp16.ChargingRateUnitWatts}, false}, - {ocpp16.GetCompositeScheduleRequest{ConnectorId: 1, Duration: -1, ChargingRateUnit: ocpp16.ChargingRateUnitWatts}, false}, - {ocpp16.GetCompositeScheduleRequest{ConnectorId: 1, Duration: 600, ChargingRateUnit: "invalidChargingRateUnit"}, false}, + {smartcharging.GetCompositeScheduleRequest{ConnectorId: 1, Duration: 600, ChargingRateUnit: types.ChargingRateUnitWatts}, true}, + {smartcharging.GetCompositeScheduleRequest{ConnectorId: 1, Duration: 600}, true}, + {smartcharging.GetCompositeScheduleRequest{ConnectorId: 1}, true}, + {smartcharging.GetCompositeScheduleRequest{}, true}, + {smartcharging.GetCompositeScheduleRequest{ConnectorId: -1, Duration: 600, ChargingRateUnit: types.ChargingRateUnitWatts}, false}, + {smartcharging.GetCompositeScheduleRequest{ConnectorId: 1, Duration: -1, ChargingRateUnit: types.ChargingRateUnitWatts}, false}, + {smartcharging.GetCompositeScheduleRequest{ConnectorId: 1, Duration: 600, ChargingRateUnit: "invalidChargingRateUnit"}, false}, } ExecuteGenericTestTable(t, requestTable) } func (suite *OcppV16TestSuite) TestGetCompositeScheduleConfirmationValidation() { t := suite.T() - chargingSchedule := ocpp16.NewChargingSchedule(ocpp16.ChargingRateUnitWatts, ocpp16.NewChargingSchedulePeriod(0, 10.0)) + chargingSchedule := types.NewChargingSchedule(types.ChargingRateUnitWatts, types.NewChargingSchedulePeriod(0, 10.0)) var confirmationTable = []GenericTestEntry{ - {ocpp16.GetCompositeScheduleConfirmation{Status: ocpp16.GetCompositeScheduleStatusAccepted, ConnectorId: 1, ScheduleStart: ocpp16.NewDateTime(time.Now()), ChargingSchedule: chargingSchedule}, true}, - {ocpp16.GetCompositeScheduleConfirmation{Status: ocpp16.GetCompositeScheduleStatusAccepted, ConnectorId: 1, ScheduleStart: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.GetCompositeScheduleConfirmation{Status: ocpp16.GetCompositeScheduleStatusAccepted, ConnectorId: 1}, true}, - {ocpp16.GetCompositeScheduleConfirmation{Status: ocpp16.GetCompositeScheduleStatusAccepted}, true}, - {ocpp16.GetCompositeScheduleConfirmation{}, false}, - {ocpp16.GetCompositeScheduleConfirmation{Status: "invalidGetCompositeScheduleStatus"}, false}, - {ocpp16.GetCompositeScheduleConfirmation{Status: ocpp16.GetCompositeScheduleStatusAccepted, ConnectorId: -1}, false}, - {ocpp16.GetCompositeScheduleConfirmation{Status: ocpp16.GetCompositeScheduleStatusAccepted, ConnectorId: 1, ChargingSchedule: ocpp16.NewChargingSchedule(ocpp16.ChargingRateUnitWatts)}, false}, + {smartcharging.GetCompositeScheduleConfirmation{Status: smartcharging.GetCompositeScheduleStatusAccepted, ConnectorId: 1, ScheduleStart: types.NewDateTime(time.Now()), ChargingSchedule: chargingSchedule}, true}, + {smartcharging.GetCompositeScheduleConfirmation{Status: smartcharging.GetCompositeScheduleStatusAccepted, ConnectorId: 1, ScheduleStart: types.NewDateTime(time.Now())}, true}, + {smartcharging.GetCompositeScheduleConfirmation{Status: smartcharging.GetCompositeScheduleStatusAccepted, ConnectorId: 1}, true}, + {smartcharging.GetCompositeScheduleConfirmation{Status: smartcharging.GetCompositeScheduleStatusAccepted}, true}, + {smartcharging.GetCompositeScheduleConfirmation{}, false}, + {smartcharging.GetCompositeScheduleConfirmation{Status: "invalidGetCompositeScheduleStatus"}, false}, + {smartcharging.GetCompositeScheduleConfirmation{Status: smartcharging.GetCompositeScheduleStatusAccepted, ConnectorId: -1}, false}, + {smartcharging.GetCompositeScheduleConfirmation{Status: smartcharging.GetCompositeScheduleStatusAccepted, ConnectorId: 1, ChargingSchedule: types.NewChargingSchedule(types.ChargingRateUnitWatts)}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -45,18 +46,18 @@ func (suite *OcppV16TestSuite) TestGetCompositeScheduleE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" connectorId := 1 - chargingRateUnit := ocpp16.ChargingRateUnitWatts + chargingRateUnit := types.ChargingRateUnitWatts startPeriod := 0 limit := 10.0 duration := 600 - status := ocpp16.GetCompositeScheduleStatusAccepted - scheduleStart := ocpp16.NewDateTime(time.Now()) - chargingSchedule := ocpp16.NewChargingSchedule(chargingRateUnit, ocpp16.NewChargingSchedulePeriod(startPeriod, limit)) + status := smartcharging.GetCompositeScheduleStatusAccepted + scheduleStart := types.NewDateTime(time.Now()) + chargingSchedule := types.NewChargingSchedule(chargingRateUnit, types.NewChargingSchedulePeriod(startPeriod, limit)) requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"duration":%v,"chargingRateUnit":"%v"}]`, - messageId, ocpp16.GetCompositeScheduleFeatureName, connectorId, duration, chargingRateUnit) + messageId, smartcharging.GetCompositeScheduleFeatureName, connectorId, duration, chargingRateUnit) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v","connectorId":%v,"scheduleStart":"%v","chargingSchedule":{"chargingRateUnit":"%v","chargingSchedulePeriod":[{"startPeriod":%v,"limit":%v}]}}]`, - messageId, status, connectorId, scheduleStart.Format(ocpp16.ISO8601), chargingRateUnit, startPeriod, limit) - getCompositeScheduleConfirmation := ocpp16.NewGetCompositeScheduleConfirmation(status) + messageId, status, connectorId, scheduleStart.FormatTimestamp(), chargingRateUnit, startPeriod, limit) + getCompositeScheduleConfirmation := smartcharging.NewGetCompositeScheduleConfirmation(status) getCompositeScheduleConfirmation.ChargingSchedule = chargingSchedule getCompositeScheduleConfirmation.ScheduleStart = scheduleStart getCompositeScheduleConfirmation.ConnectorId = connectorId @@ -64,7 +65,7 @@ func (suite *OcppV16TestSuite) TestGetCompositeScheduleE2EMocked() { smartChargingListener := MockChargePointSmartChargingListener{} smartChargingListener.On("OnGetCompositeSchedule", mock.Anything).Return(getCompositeScheduleConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp16.GetCompositeScheduleRequest) + request, ok := args.Get(0).(*smartcharging.GetCompositeScheduleRequest) assert.True(t, ok) assert.NotNil(t, request) assert.Equal(t, connectorId, request.ConnectorId) @@ -73,19 +74,19 @@ func (suite *OcppV16TestSuite) TestGetCompositeScheduleE2EMocked() { }) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - suite.chargePoint.SetSmartChargingListener(smartChargingListener) + suite.chargePoint.SetSmartChargingHandler(smartChargingListener) // Run Test suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.GetCompositeSchedule(wsId, func(confirmation *ocpp16.GetCompositeScheduleConfirmation, err error) { + err = suite.centralSystem.GetCompositeSchedule(wsId, func(confirmation *smartcharging.GetCompositeScheduleConfirmation, err error) { if !assert.Nil(t, err) || !assert.NotNil(t, confirmation) { resultChannel <- false } else { assert.Equal(t, status, confirmation.Status) assert.Equal(t, connectorId, confirmation.ConnectorId) - assert.Equal(t, scheduleStart.Format(ocpp16.ISO8601), confirmation.ScheduleStart.Format(ocpp16.ISO8601)) + assert.Equal(t, scheduleStart.FormatTimestamp(), confirmation.ScheduleStart.FormatTimestamp()) assert.Equal(t, chargingSchedule.ChargingRateUnit, confirmation.ChargingSchedule.ChargingRateUnit) assert.Equal(t, chargingSchedule.Duration, confirmation.ChargingSchedule.Duration) assert.Equal(t, chargingSchedule.MinChargingRate, confirmation.ChargingSchedule.MinChargingRate) @@ -96,7 +97,7 @@ func (suite *OcppV16TestSuite) TestGetCompositeScheduleE2EMocked() { assert.Equal(t, chargingSchedule.ChargingSchedulePeriod[0].NumberPhases, confirmation.ChargingSchedule.ChargingSchedulePeriod[0].NumberPhases) resultChannel <- true } - }, connectorId, duration, func(request *ocpp16.GetCompositeScheduleRequest) { + }, connectorId, duration, func(request *smartcharging.GetCompositeScheduleRequest) { request.ChargingRateUnit = chargingRateUnit }) assert.Nil(t, err) @@ -107,10 +108,10 @@ func (suite *OcppV16TestSuite) TestGetCompositeScheduleE2EMocked() { func (suite *OcppV16TestSuite) TestGetCompositeScheduleInvalidEndpoint() { messageId := defaultMessageId connectorId := 1 - chargingRateUnit := ocpp16.ChargingRateUnitWatts + chargingRateUnit := types.ChargingRateUnitWatts duration := 600 requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"duration":%v,"chargingRateUnit":"%v"}]`, - messageId, ocpp16.GetCompositeScheduleFeatureName, connectorId, duration, chargingRateUnit) - GetCompositeScheduleRequest := ocpp16.NewGetCompositeScheduleRequest(connectorId, duration) + messageId, smartcharging.GetCompositeScheduleFeatureName, connectorId, duration, chargingRateUnit) + GetCompositeScheduleRequest := smartcharging.NewGetCompositeScheduleRequest(connectorId, duration) testUnsupportedRequestFromChargePoint(suite, GetCompositeScheduleRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/get_configuration_test.go b/ocpp1.6_test/get_configuration_test.go index 36407e41..cd3ffb9f 100644 --- a/ocpp1.6_test/get_configuration_test.go +++ b/ocpp1.6_test/get_configuration_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,12 +11,12 @@ import ( func (suite *OcppV16TestSuite) TestGetConfigurationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.GetConfigurationRequest{Key: []string{"key1", "key2"}}, true}, - {ocpp16.GetConfigurationRequest{Key: []string{"key1", "key2", "key3", "key4", "key5", "key6"}}, true}, - {ocpp16.GetConfigurationRequest{Key: []string{"key1", "key2", "key2"}}, false}, - {ocpp16.GetConfigurationRequest{}, false}, - {ocpp16.GetConfigurationRequest{Key: []string{}}, false}, - {ocpp16.GetConfigurationRequest{Key: []string{">50................................................"}}, false}, + {core.GetConfigurationRequest{Key: []string{"key1", "key2"}}, true}, + {core.GetConfigurationRequest{Key: []string{"key1", "key2", "key3", "key4", "key5", "key6"}}, true}, + {core.GetConfigurationRequest{Key: []string{"key1", "key2", "key2"}}, false}, + {core.GetConfigurationRequest{}, false}, + {core.GetConfigurationRequest{Key: []string{}}, false}, + {core.GetConfigurationRequest{Key: []string{">50................................................"}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -24,14 +24,14 @@ func (suite *OcppV16TestSuite) TestGetConfigurationRequestValidation() { func (suite *OcppV16TestSuite) TestGetConfigurationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.GetConfigurationConfirmation{ConfigurationKey: []ocpp16.ConfigurationKey{{Key: "key1", Readonly: true, Value: "value1"}}}, true}, - {ocpp16.GetConfigurationConfirmation{ConfigurationKey: []ocpp16.ConfigurationKey{{Key: "key1", Readonly: true, Value: "value1"}, {Key: "key2", Readonly: false, Value: "value2"}}}, true}, - {ocpp16.GetConfigurationConfirmation{ConfigurationKey: []ocpp16.ConfigurationKey{{Key: "key1", Readonly: true, Value: "value1"}}, UnknownKey: []string{"keyX"}}, true}, - {ocpp16.GetConfigurationConfirmation{ConfigurationKey: []ocpp16.ConfigurationKey{{Key: "key1", Readonly: false, Value: "value1"}}, UnknownKey: []string{"keyX", "keyY"}}, true}, - {ocpp16.GetConfigurationConfirmation{UnknownKey: []string{"keyX"}}, true}, - {ocpp16.GetConfigurationConfirmation{UnknownKey: []string{">50................................................"}}, false}, - {ocpp16.GetConfigurationConfirmation{ConfigurationKey: []ocpp16.ConfigurationKey{{Key: ">50................................................", Readonly: true, Value: "value1"}}}, false}, - {ocpp16.GetConfigurationConfirmation{ConfigurationKey: []ocpp16.ConfigurationKey{{Key: "key1", Readonly: true, Value: ">500................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."}}}, false}, + {core.GetConfigurationConfirmation{ConfigurationKey: []core.ConfigurationKey{{Key: "key1", Readonly: true, Value: "value1"}}}, true}, + {core.GetConfigurationConfirmation{ConfigurationKey: []core.ConfigurationKey{{Key: "key1", Readonly: true, Value: "value1"}, {Key: "key2", Readonly: false, Value: "value2"}}}, true}, + {core.GetConfigurationConfirmation{ConfigurationKey: []core.ConfigurationKey{{Key: "key1", Readonly: true, Value: "value1"}}, UnknownKey: []string{"keyX"}}, true}, + {core.GetConfigurationConfirmation{ConfigurationKey: []core.ConfigurationKey{{Key: "key1", Readonly: false, Value: "value1"}}, UnknownKey: []string{"keyX", "keyY"}}, true}, + {core.GetConfigurationConfirmation{UnknownKey: []string{"keyX"}}, true}, + {core.GetConfigurationConfirmation{UnknownKey: []string{">50................................................"}}, false}, + {core.GetConfigurationConfirmation{ConfigurationKey: []core.ConfigurationKey{{Key: ">50................................................", Readonly: true, Value: "value1"}}}, false}, + {core.GetConfigurationConfirmation{ConfigurationKey: []core.ConfigurationKey{{Key: "key1", Readonly: true, Value: ">500................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."}}}, false}, //{ocpp16.GetConfigurationConfirmation{ConfigurationKey: []ocpp16.ConfigurationKey{{Key: "key1", Readonly: true, Value: "value1"}, {Key: "key1", Readonly: false, Value: "value2"}}}, false}, } //TODO: additional test cases TBD. See get_configuration.go @@ -46,13 +46,13 @@ func (suite *OcppV16TestSuite) TestGetConfigurationE2EMocked() { key1 := "key1" key2 := "key2" requestKeys := []string{key1, key2} - resultKey1 := ocpp16.ConfigurationKey{Key: key1, Readonly: true, Value: "someValue"} - resultKey2 := ocpp16.ConfigurationKey{Key: key1, Readonly: true, Value: "someOtherValue"} - resultKeys := []ocpp16.ConfigurationKey{resultKey1, resultKey2} + resultKey1 := core.ConfigurationKey{Key: key1, Readonly: true, Value: "someValue"} + resultKey2 := core.ConfigurationKey{Key: key1, Readonly: true, Value: "someOtherValue"} + resultKeys := []core.ConfigurationKey{resultKey1, resultKey2} unknownKeys := []string{"keyX", "keyY"} - requestJson := fmt.Sprintf(`[2,"%v","%v",{"key":["%v","%v"]}]`, messageId, ocpp16.GetConfigurationFeatureName, key1, key2) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"key":["%v","%v"]}]`, messageId, core.GetConfigurationFeatureName, key1, key2) responseJson := fmt.Sprintf(`[3,"%v",{"configurationKey":[{"key":"%v","readonly":%v,"value":"%v"},{"key":"%v","readonly":%v,"value":"%v"}],"unknownKey":["%v","%v"]}]`, messageId, resultKey1.Key, resultKey1.Readonly, resultKey1.Value, resultKey2.Key, resultKey2.Readonly, resultKey2.Value, unknownKeys[0], unknownKeys[1]) - getConfigurationConfirmation := ocpp16.NewGetConfigurationConfirmation(resultKeys) + getConfigurationConfirmation := core.NewGetConfigurationConfirmation(resultKeys) getConfigurationConfirmation.UnknownKey = unknownKeys channel := NewMockWebSocket(wsId) @@ -65,7 +65,7 @@ func (suite *OcppV16TestSuite) TestGetConfigurationE2EMocked() { err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.GetConfiguration(wsId, func(confirmation *ocpp16.GetConfigurationConfirmation, err error) { + err = suite.centralSystem.GetConfiguration(wsId, func(confirmation *core.GetConfigurationConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, unknownKeys, confirmation.UnknownKey) @@ -82,7 +82,7 @@ func (suite *OcppV16TestSuite) TestGetConfigurationInvalidEndpoint() { key1 := "key1" key2 := "key2" requestKeys := []string{key1, key2} - getConfigurationRequest := ocpp16.NewGetConfigurationRequest(requestKeys) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"key":["%v","%v"]}]`, messageId, ocpp16.GetConfigurationFeatureName, key1, key2) + getConfigurationRequest := core.NewGetConfigurationRequest(requestKeys) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"key":["%v","%v"]}]`, messageId, core.GetConfigurationFeatureName, key1, key2) testUnsupportedRequestFromChargePoint(suite, getConfigurationRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/get_diagnostics_test.go b/ocpp1.6_test/get_diagnostics_test.go index b0e3b84f..f38453a3 100644 --- a/ocpp1.6_test/get_diagnostics_test.go +++ b/ocpp1.6_test/get_diagnostics_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -12,15 +13,15 @@ import ( func (suite *OcppV16TestSuite) TestGetDiagnosticsRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.GetDiagnosticsRequest{Location: "ftp:some/path", Retries: 10, RetryInterval: 10, StartTime: ocpp16.NewDateTime(time.Now()), EndTime: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.GetDiagnosticsRequest{Location: "ftp:some/path", Retries: 10, RetryInterval: 10, StartTime: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.GetDiagnosticsRequest{Location: "ftp:some/path", Retries: 10, RetryInterval: 10}, true}, - {ocpp16.GetDiagnosticsRequest{Location: "ftp:some/path", Retries: 10}, true}, - {ocpp16.GetDiagnosticsRequest{Location: "ftp:some/path"}, true}, - {ocpp16.GetDiagnosticsRequest{}, false}, - {ocpp16.GetDiagnosticsRequest{Location: "invalidUri"}, false}, - {ocpp16.GetDiagnosticsRequest{Location: "ftp:some/path", Retries: -1}, false}, - {ocpp16.GetDiagnosticsRequest{Location: "ftp:some/path", RetryInterval: -1}, false}, + {firmware.GetDiagnosticsRequest{Location: "ftp:some/path", Retries: 10, RetryInterval: 10, StartTime: types.NewDateTime(time.Now()), EndTime: types.NewDateTime(time.Now())}, true}, + {firmware.GetDiagnosticsRequest{Location: "ftp:some/path", Retries: 10, RetryInterval: 10, StartTime: types.NewDateTime(time.Now())}, true}, + {firmware.GetDiagnosticsRequest{Location: "ftp:some/path", Retries: 10, RetryInterval: 10}, true}, + {firmware.GetDiagnosticsRequest{Location: "ftp:some/path", Retries: 10}, true}, + {firmware.GetDiagnosticsRequest{Location: "ftp:some/path"}, true}, + {firmware.GetDiagnosticsRequest{}, false}, + {firmware.GetDiagnosticsRequest{Location: "invalidUri"}, false}, + {firmware.GetDiagnosticsRequest{Location: "ftp:some/path", Retries: -1}, false}, + {firmware.GetDiagnosticsRequest{Location: "ftp:some/path", RetryInterval: -1}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -28,10 +29,10 @@ func (suite *OcppV16TestSuite) TestGetDiagnosticsRequestValidation() { func (suite *OcppV16TestSuite) TestGetDiagnosticsConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.GetDiagnosticsConfirmation{FileName: "someFileName"}, true}, - {ocpp16.GetDiagnosticsConfirmation{FileName: ""}, true}, - {ocpp16.GetDiagnosticsConfirmation{}, true}, - {ocpp16.GetDiagnosticsConfirmation{FileName: ">255............................................................................................................................................................................................................................................................"}, false}, + {firmware.GetDiagnosticsConfirmation{FileName: "someFileName"}, true}, + {firmware.GetDiagnosticsConfirmation{FileName: ""}, true}, + {firmware.GetDiagnosticsConfirmation{}, true}, + {firmware.GetDiagnosticsConfirmation{FileName: ">255............................................................................................................................................................................................................................................................"}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -45,26 +46,26 @@ func (suite *OcppV16TestSuite) TestGetDiagnosticsE2EMocked() { fileName := "diagnostics.json" retries := 10 retryInterval := 600 - startTime := ocpp16.NewDateTime(time.Now().Add(-10 * time.Hour * 24)) - endTime := ocpp16.NewDateTime(time.Now()) + startTime := types.NewDateTime(time.Now().Add(-10 * time.Hour * 24)) + endTime := types.NewDateTime(time.Now()) requestJson := fmt.Sprintf(`[2,"%v","%v",{"location":"%v","retries":%v,"retryInterval":%v,"startTime":"%v","endTime":"%v"}]`, - messageId, ocpp16.GetDiagnosticsFeatureName, location, retries, retryInterval, startTime.Format(ocpp16.ISO8601), endTime.Format(ocpp16.ISO8601)) + messageId, firmware.GetDiagnosticsFeatureName, location, retries, retryInterval, startTime.FormatTimestamp(), endTime.FormatTimestamp()) responseJson := fmt.Sprintf(`[3,"%v",{"fileName":"%v"}]`, messageId, fileName) - getDiagnosticsConfirmation := ocpp16.NewGetDiagnosticsConfirmation() + getDiagnosticsConfirmation := firmware.NewGetDiagnosticsConfirmation() getDiagnosticsConfirmation.FileName = fileName channel := NewMockWebSocket(wsId) firmwareListener := MockChargePointFirmwareManagementListener{} firmwareListener.On("OnGetDiagnostics", mock.Anything).Return(getDiagnosticsConfirmation, nil) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - suite.chargePoint.SetFirmwareManagementListener(firmwareListener) + suite.chargePoint.SetFirmwareManagementHandler(firmwareListener) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) // Run Test suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.GetDiagnostics(wsId, func(confirmation *ocpp16.GetDiagnosticsConfirmation, err error) { + err = suite.centralSystem.GetDiagnostics(wsId, func(confirmation *firmware.GetDiagnosticsConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) if confirmation != nil { @@ -73,7 +74,7 @@ func (suite *OcppV16TestSuite) TestGetDiagnosticsE2EMocked() { } else { resultChannel <- false } - }, location, func(request *ocpp16.GetDiagnosticsRequest) { + }, location, func(request *firmware.GetDiagnosticsRequest) { request.RetryInterval = retryInterval request.Retries = retries request.StartTime = startTime @@ -91,10 +92,10 @@ func (suite *OcppV16TestSuite) TestGetDiagnosticsInvalidEndpoint() { location := "ftp:some/path" retries := 10 retryInterval := 600 - startTime := ocpp16.NewDateTime(time.Now().Add(-10 * time.Hour * 24)) - endTime := ocpp16.NewDateTime(time.Now()) - localListVersionRequest := ocpp16.NewGetDiagnosticsRequest(location) + startTime := types.NewDateTime(time.Now().Add(-10 * time.Hour * 24)) + endTime := types.NewDateTime(time.Now()) + localListVersionRequest := firmware.NewGetDiagnosticsRequest(location) requestJson := fmt.Sprintf(`[2,"%v","%v",{"location":"%v","retries":%v,"retryInterval":%v,"startTime":"%v","endTime":"%v"}]`, - messageId, ocpp16.GetDiagnosticsFeatureName, location, retries, retryInterval, startTime.Format(ocpp16.ISO8601), endTime.Format(ocpp16.ISO8601)) + messageId, firmware.GetDiagnosticsFeatureName, location, retries, retryInterval, startTime.FormatTimestamp(), endTime.FormatTimestamp()) testUnsupportedRequestFromChargePoint(suite, localListVersionRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/get_local_list_version_test.go b/ocpp1.6_test/get_local_list_version_test.go index 2991c03a..596f02b8 100644 --- a/ocpp1.6_test/get_local_list_version_test.go +++ b/ocpp1.6_test/get_local_list_version_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/localauth" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,7 +11,7 @@ import ( func (suite *OcppV16TestSuite) TestGetLocalListVersionRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.GetLocalListVersionRequest{}, true}, + {localauth.GetLocalListVersionRequest{}, true}, } ExecuteGenericTestTable(t, requestTable) } @@ -19,11 +19,11 @@ func (suite *OcppV16TestSuite) TestGetLocalListVersionRequestValidation() { func (suite *OcppV16TestSuite) TestGetLocalListVersionConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.GetLocalListVersionConfirmation{ListVersion: 1}, true}, - {ocpp16.GetLocalListVersionConfirmation{ListVersion: 0}, true}, - {ocpp16.GetLocalListVersionConfirmation{}, true}, - {ocpp16.GetLocalListVersionConfirmation{ListVersion: -1}, true}, - {ocpp16.GetLocalListVersionConfirmation{ListVersion: -2}, false}, + {localauth.GetLocalListVersionConfirmation{ListVersion: 1}, true}, + {localauth.GetLocalListVersionConfirmation{ListVersion: 0}, true}, + {localauth.GetLocalListVersionConfirmation{}, true}, + {localauth.GetLocalListVersionConfirmation{ListVersion: -1}, true}, + {localauth.GetLocalListVersionConfirmation{ListVersion: -2}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -34,22 +34,22 @@ func (suite *OcppV16TestSuite) TestGetLocalListVersionE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" listVersion := 1 - requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, ocpp16.GetLocalListVersionFeatureName) + requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, localauth.GetLocalListVersionFeatureName) responseJson := fmt.Sprintf(`[3,"%v",{"listVersion":%v}]`, messageId, listVersion) - localListVersionConfirmation := ocpp16.NewGetLocalListVersionConfirmation(listVersion) + localListVersionConfirmation := localauth.NewGetLocalListVersionConfirmation(listVersion) channel := NewMockWebSocket(wsId) localAuthListListener := MockChargePointLocalAuthListListener{} localAuthListListener.On("OnGetLocalListVersion", mock.Anything).Return(localListVersionConfirmation, nil) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - suite.chargePoint.SetLocalAuthListListener(localAuthListListener) + suite.chargePoint.SetLocalAuthListHandler(localAuthListListener) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) // Run Test suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.GetLocalListVersion(wsId, func(confirmation *ocpp16.GetLocalListVersionConfirmation, err error) { + err = suite.centralSystem.GetLocalListVersion(wsId, func(confirmation *localauth.GetLocalListVersionConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) if confirmation != nil { @@ -68,7 +68,7 @@ func (suite *OcppV16TestSuite) TestGetLocalListVersionE2EMocked() { func (suite *OcppV16TestSuite) TestGetLocalListVersionInvalidEndpoint() { messageId := defaultMessageId - localListVersionRequest := ocpp16.NewGetLocalListVersionRequest() - requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, ocpp16.GetLocalListVersionFeatureName) + localListVersionRequest := localauth.NewGetLocalListVersionRequest() + requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, localauth.GetLocalListVersionFeatureName) testUnsupportedRequestFromChargePoint(suite, localListVersionRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/heartbeat_test.go b/ocpp1.6_test/heartbeat_test.go index 4d7f3a29..ea5a3af0 100644 --- a/ocpp1.6_test/heartbeat_test.go +++ b/ocpp1.6_test/heartbeat_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -12,7 +13,7 @@ import ( func (suite *OcppV16TestSuite) TestHeartbeatRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.HeartbeatRequest{}, true}, + {core.HeartbeatRequest{}, true}, } ExecuteGenericTestTable(t, requestTable) } @@ -20,8 +21,8 @@ func (suite *OcppV16TestSuite) TestHeartbeatRequestValidation() { func (suite *OcppV16TestSuite) TestHeartbeatConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.HeartbeatConfirmation{CurrentTime: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.HeartbeatConfirmation{}, false}, + {core.HeartbeatConfirmation{CurrentTime: types.NewDateTime(time.Now())}, true}, + {core.HeartbeatConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -31,10 +32,10 @@ func (suite *OcppV16TestSuite) TestHeartbeatE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - currentTime := ocpp16.NewDateTime(time.Now()) - requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, ocpp16.HeartbeatFeatureName) - responseJson := fmt.Sprintf(`[3,"%v",{"currentTime":"%v"}]`, messageId, currentTime.Time.Format(ocpp16.ISO8601)) - heartbeatConfirmation := ocpp16.NewHeartbeatConfirmation(currentTime) + currentTime := types.NewDateTime(time.Now()) + requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, core.HeartbeatFeatureName) + responseJson := fmt.Sprintf(`[3,"%v",{"currentTime":"%v"}]`, messageId, currentTime.FormatTimestamp()) + heartbeatConfirmation := core.NewHeartbeatConfirmation(currentTime) channel := NewMockWebSocket(wsId) coreListener := MockCentralSystemCoreListener{} @@ -53,7 +54,7 @@ func (suite *OcppV16TestSuite) TestHeartbeatE2EMocked() { func (suite *OcppV16TestSuite) TestHeartbeatInvalidEndpoint() { messageId := defaultMessageId - heartbeatRequest := ocpp16.NewHeartbeatRequest() - requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, ocpp16.HeartbeatFeatureName) + heartbeatRequest := core.NewHeartbeatRequest() + requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, core.HeartbeatFeatureName) testUnsupportedRequestFromCentralSystem(suite, heartbeatRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/meter_values_test.go b/ocpp1.6_test/meter_values_test.go index b34f5b5e..06091dd0 100644 --- a/ocpp1.6_test/meter_values_test.go +++ b/ocpp1.6_test/meter_values_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -11,20 +12,20 @@ import ( // Test func (suite *OcppV16TestSuite) TestMeterValuesRequestValidation() { var requestTable = []GenericTestEntry{ - {ocpp16.MeterValuesRequest{ConnectorId: 1, TransactionId: 1, MeterValue: []ocpp16.MeterValue{{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{{Value: "value"}}}}}, true}, - {ocpp16.MeterValuesRequest{ConnectorId: 1, MeterValue: []ocpp16.MeterValue{{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{{Value: "value"}}}}}, true}, - {ocpp16.MeterValuesRequest{MeterValue: []ocpp16.MeterValue{{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{{Value: "value"}}}}}, true}, - {ocpp16.MeterValuesRequest{ConnectorId: -1, MeterValue: []ocpp16.MeterValue{{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{{Value: "value"}}}}}, false}, - {ocpp16.MeterValuesRequest{ConnectorId: 1, MeterValue: []ocpp16.MeterValue{}}, false}, - {ocpp16.MeterValuesRequest{ConnectorId: 1}, false}, - {ocpp16.MeterValuesRequest{ConnectorId: 1, MeterValue: []ocpp16.MeterValue{{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{}}}}, false}, + {core.MeterValuesRequest{ConnectorId: 1, TransactionId: 1, MeterValue: []types.MeterValue{{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: "value"}}}}}, true}, + {core.MeterValuesRequest{ConnectorId: 1, MeterValue: []types.MeterValue{{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: "value"}}}}}, true}, + {core.MeterValuesRequest{MeterValue: []types.MeterValue{{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: "value"}}}}}, true}, + {core.MeterValuesRequest{ConnectorId: -1, MeterValue: []types.MeterValue{{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: "value"}}}}}, false}, + {core.MeterValuesRequest{ConnectorId: 1, MeterValue: []types.MeterValue{}}, false}, + {core.MeterValuesRequest{ConnectorId: 1}, false}, + {core.MeterValuesRequest{ConnectorId: 1, MeterValue: []types.MeterValue{{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{}}}}, false}, } ExecuteGenericTestTable(suite.T(), requestTable) } func (suite *OcppV16TestSuite) TestMeterValuesConfirmationValidation() { var confirmationTable = []GenericTestEntry{ - {ocpp16.MeterValuesConfirmation{}, true}, + {core.MeterValuesConfirmation{}, true}, } ExecuteGenericTestTable(suite.T(), confirmationTable) } @@ -36,17 +37,17 @@ func (suite *OcppV16TestSuite) TestMeterValuesE2EMocked() { wsUrl := "someUrl" connectorId := 1 mockValue := "value" - mockUnit := ocpp16.UnitOfMeasureKW - meterValues := []ocpp16.MeterValue{{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{{Value: mockValue, Unit: mockUnit}}}} - timestamp := ocpp16.DateTime{Time: time.Now()} - requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"meterValue":[{"timestamp":"%v","sampledValue":[{"value":"%v","unit":"%v"}]}]}]`, messageId, ocpp16.MeterValuesFeatureName, connectorId, timestamp.Format(ocpp16.ISO8601), mockValue, mockUnit) + mockUnit := types.UnitOfMeasureKW + meterValues := []types.MeterValue{{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: mockValue, Unit: mockUnit}}}} + timestamp := types.DateTime{Time: time.Now()} + requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"meterValue":[{"timestamp":"%v","sampledValue":[{"value":"%v","unit":"%v"}]}]}]`, messageId, core.MeterValuesFeatureName, connectorId, timestamp.FormatTimestamp(), mockValue, mockUnit) responseJson := fmt.Sprintf(`[3,"%v",{}]`, messageId) - meterValuesConfirmation := ocpp16.NewMeterValuesConfirmation() + meterValuesConfirmation := core.NewMeterValuesConfirmation() channel := NewMockWebSocket(wsId) coreListener := MockCentralSystemCoreListener{} coreListener.On("OnMeterValues", mock.AnythingOfType("string"), mock.Anything).Return(meterValuesConfirmation, nil).Run(func(args mock.Arguments) { - request := args.Get(1).(*ocpp16.MeterValuesRequest) + request := args.Get(1).(*core.MeterValuesRequest) assert.Equal(t, connectorId, request.ConnectorId) assert.Equal(t, 1, len(request.MeterValue)) mv := request.MeterValue[0] @@ -71,10 +72,10 @@ func (suite *OcppV16TestSuite) TestMeterValuesInvalidEndpoint() { messageId := defaultMessageId connectorId := 1 mockValue := "value" - mockUnit := ocpp16.UnitOfMeasureKW - timestamp := ocpp16.DateTime{Time: time.Now()} - meterValues := []ocpp16.MeterValue{{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{{Value: mockValue, Unit: mockUnit}}}} - meterValuesRequest := ocpp16.NewMeterValuesRequest(connectorId, meterValues) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"meterValue":[{"timestamp":"%v","sampledValue":[{"value":"%v","unit":"%v"}]}]}]`, messageId, ocpp16.MeterValuesFeatureName, connectorId, timestamp.Format(ocpp16.ISO8601), mockValue, mockUnit) + mockUnit := types.UnitOfMeasureKW + timestamp := types.DateTime{Time: time.Now()} + meterValues := []types.MeterValue{{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: mockValue, Unit: mockUnit}}}} + meterValuesRequest := core.NewMeterValuesRequest(connectorId, meterValues) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"meterValue":[{"timestamp":"%v","sampledValue":[{"value":"%v","unit":"%v"}]}]}]`, messageId, core.MeterValuesFeatureName, connectorId, timestamp.FormatTimestamp(), mockValue, mockUnit) testUnsupportedRequestFromCentralSystem(suite, meterValuesRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/ocpp16_test.go b/ocpp1.6_test/ocpp16_test.go index e5e59490..947d328b 100644 --- a/ocpp1.6_test/ocpp16_test.go +++ b/ocpp1.6_test/ocpp16_test.go @@ -4,6 +4,13 @@ import ( "fmt" "github.com/lorenzodonini/ocpp-go/ocpp" ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/localauth" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/remotetrigger" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/reservation" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/lorenzodonini/ocpp-go/ocppj" "github.com/lorenzodonini/ocpp-go/ws" "github.com/sirupsen/logrus" @@ -123,7 +130,7 @@ func (f MockFeature) GetRequestType() reflect.Type { return reflect.TypeOf(MockRequest{}) } -func (f MockFeature) GetConfirmationType() reflect.Type { +func (f MockFeature) GetResponseType() reflect.Type { return reflect.TypeOf(MockConfirmation{}) } @@ -148,51 +155,51 @@ type MockCentralSystemCoreListener struct { mock.Mock } -func (coreListener MockCentralSystemCoreListener) OnAuthorize(chargePointId string, request *ocpp16.AuthorizeRequest) (confirmation *ocpp16.AuthorizeConfirmation, err error) { +func (coreListener MockCentralSystemCoreListener) OnAuthorize(chargePointId string, request *core.AuthorizeRequest) (confirmation *core.AuthorizeConfirmation, err error) { args := coreListener.MethodCalled("OnAuthorize", chargePointId, request) - conf := args.Get(0).(*ocpp16.AuthorizeConfirmation) + conf := args.Get(0).(*core.AuthorizeConfirmation) return conf, args.Error(1) } -func (coreListener MockCentralSystemCoreListener) OnBootNotification(chargePointId string, request *ocpp16.BootNotificationRequest) (confirmation *ocpp16.BootNotificationConfirmation, err error) { +func (coreListener MockCentralSystemCoreListener) OnBootNotification(chargePointId string, request *core.BootNotificationRequest) (confirmation *core.BootNotificationConfirmation, err error) { args := coreListener.MethodCalled("OnBootNotification", chargePointId, request) - conf := args.Get(0).(*ocpp16.BootNotificationConfirmation) + conf := args.Get(0).(*core.BootNotificationConfirmation) return conf, args.Error(1) } -func (coreListener MockCentralSystemCoreListener) OnDataTransfer(chargePointId string, request *ocpp16.DataTransferRequest) (confirmation *ocpp16.DataTransferConfirmation, err error) { +func (coreListener MockCentralSystemCoreListener) OnDataTransfer(chargePointId string, request *core.DataTransferRequest) (confirmation *core.DataTransferConfirmation, err error) { args := coreListener.MethodCalled("OnDataTransfer", chargePointId, request) - conf := args.Get(0).(*ocpp16.DataTransferConfirmation) + conf := args.Get(0).(*core.DataTransferConfirmation) return conf, args.Error(1) } -func (coreListener MockCentralSystemCoreListener) OnHeartbeat(chargePointId string, request *ocpp16.HeartbeatRequest) (confirmation *ocpp16.HeartbeatConfirmation, err error) { +func (coreListener MockCentralSystemCoreListener) OnHeartbeat(chargePointId string, request *core.HeartbeatRequest) (confirmation *core.HeartbeatConfirmation, err error) { args := coreListener.MethodCalled("OnHeartbeat", chargePointId, request) - conf := args.Get(0).(*ocpp16.HeartbeatConfirmation) + conf := args.Get(0).(*core.HeartbeatConfirmation) return conf, args.Error(1) } -func (coreListener MockCentralSystemCoreListener) OnMeterValues(chargePointId string, request *ocpp16.MeterValuesRequest) (confirmation *ocpp16.MeterValuesConfirmation, err error) { +func (coreListener MockCentralSystemCoreListener) OnMeterValues(chargePointId string, request *core.MeterValuesRequest) (confirmation *core.MeterValuesConfirmation, err error) { args := coreListener.MethodCalled("OnMeterValues", chargePointId, request) - conf := args.Get(0).(*ocpp16.MeterValuesConfirmation) + conf := args.Get(0).(*core.MeterValuesConfirmation) return conf, args.Error(1) } -func (coreListener MockCentralSystemCoreListener) OnStartTransaction(chargePointId string, request *ocpp16.StartTransactionRequest) (confirmation *ocpp16.StartTransactionConfirmation, err error) { +func (coreListener MockCentralSystemCoreListener) OnStartTransaction(chargePointId string, request *core.StartTransactionRequest) (confirmation *core.StartTransactionConfirmation, err error) { args := coreListener.MethodCalled("OnStartTransaction", chargePointId, request) - conf := args.Get(0).(*ocpp16.StartTransactionConfirmation) + conf := args.Get(0).(*core.StartTransactionConfirmation) return conf, args.Error(1) } -func (coreListener MockCentralSystemCoreListener) OnStatusNotification(chargePointId string, request *ocpp16.StatusNotificationRequest) (confirmation *ocpp16.StatusNotificationConfirmation, err error) { +func (coreListener MockCentralSystemCoreListener) OnStatusNotification(chargePointId string, request *core.StatusNotificationRequest) (confirmation *core.StatusNotificationConfirmation, err error) { args := coreListener.MethodCalled("OnStatusNotification", chargePointId, request) - conf := args.Get(0).(*ocpp16.StatusNotificationConfirmation) + conf := args.Get(0).(*core.StatusNotificationConfirmation) return conf, args.Error(1) } -func (coreListener MockCentralSystemCoreListener) OnStopTransaction(chargePointId string, request *ocpp16.StopTransactionRequest) (confirmation *ocpp16.StopTransactionConfirmation, err error) { +func (coreListener MockCentralSystemCoreListener) OnStopTransaction(chargePointId string, request *core.StopTransactionRequest) (confirmation *core.StopTransactionConfirmation, err error) { args := coreListener.MethodCalled("OnStopTransaction", chargePointId, request) - conf := args.Get(0).(*ocpp16.StopTransactionConfirmation) + conf := args.Get(0).(*core.StopTransactionConfirmation) return conf, args.Error(1) } @@ -201,57 +208,57 @@ type MockChargePointCoreListener struct { mock.Mock } -func (coreListener MockChargePointCoreListener) OnChangeAvailability(request *ocpp16.ChangeAvailabilityRequest) (confirmation *ocpp16.ChangeAvailabilityConfirmation, err error) { +func (coreListener MockChargePointCoreListener) OnChangeAvailability(request *core.ChangeAvailabilityRequest) (confirmation *core.ChangeAvailabilityConfirmation, err error) { args := coreListener.MethodCalled("OnChangeAvailability", request) - conf := args.Get(0).(*ocpp16.ChangeAvailabilityConfirmation) + conf := args.Get(0).(*core.ChangeAvailabilityConfirmation) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnDataTransfer(request *ocpp16.DataTransferRequest) (confirmation *ocpp16.DataTransferConfirmation, err error) { +func (coreListener MockChargePointCoreListener) OnDataTransfer(request *core.DataTransferRequest) (confirmation *core.DataTransferConfirmation, err error) { args := coreListener.MethodCalled("OnDataTransfer", request) - conf := args.Get(0).(*ocpp16.DataTransferConfirmation) + conf := args.Get(0).(*core.DataTransferConfirmation) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnChangeConfiguration(request *ocpp16.ChangeConfigurationRequest) (confirmation *ocpp16.ChangeConfigurationConfirmation, err error) { +func (coreListener MockChargePointCoreListener) OnChangeConfiguration(request *core.ChangeConfigurationRequest) (confirmation *core.ChangeConfigurationConfirmation, err error) { args := coreListener.MethodCalled("OnChangeConfiguration", request) - conf := args.Get(0).(*ocpp16.ChangeConfigurationConfirmation) + conf := args.Get(0).(*core.ChangeConfigurationConfirmation) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnClearCache(request *ocpp16.ClearCacheRequest) (confirmation *ocpp16.ClearCacheConfirmation, err error) { +func (coreListener MockChargePointCoreListener) OnClearCache(request *core.ClearCacheRequest) (confirmation *core.ClearCacheConfirmation, err error) { args := coreListener.MethodCalled("OnClearCache", request) - conf := args.Get(0).(*ocpp16.ClearCacheConfirmation) + conf := args.Get(0).(*core.ClearCacheConfirmation) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnGetConfiguration(request *ocpp16.GetConfigurationRequest) (confirmation *ocpp16.GetConfigurationConfirmation, err error) { +func (coreListener MockChargePointCoreListener) OnGetConfiguration(request *core.GetConfigurationRequest) (confirmation *core.GetConfigurationConfirmation, err error) { args := coreListener.MethodCalled("OnGetConfiguration", request) - conf := args.Get(0).(*ocpp16.GetConfigurationConfirmation) + conf := args.Get(0).(*core.GetConfigurationConfirmation) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnReset(request *ocpp16.ResetRequest) (confirmation *ocpp16.ResetConfirmation, err error) { +func (coreListener MockChargePointCoreListener) OnReset(request *core.ResetRequest) (confirmation *core.ResetConfirmation, err error) { args := coreListener.MethodCalled("OnReset", request) - conf := args.Get(0).(*ocpp16.ResetConfirmation) + conf := args.Get(0).(*core.ResetConfirmation) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnUnlockConnector(request *ocpp16.UnlockConnectorRequest) (confirmation *ocpp16.UnlockConnectorConfirmation, err error) { +func (coreListener MockChargePointCoreListener) OnUnlockConnector(request *core.UnlockConnectorRequest) (confirmation *core.UnlockConnectorConfirmation, err error) { args := coreListener.MethodCalled("OnUnlockConnector", request) - conf := args.Get(0).(*ocpp16.UnlockConnectorConfirmation) + conf := args.Get(0).(*core.UnlockConnectorConfirmation) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnRemoteStartTransaction(request *ocpp16.RemoteStartTransactionRequest) (confirmation *ocpp16.RemoteStartTransactionConfirmation, err error) { +func (coreListener MockChargePointCoreListener) OnRemoteStartTransaction(request *core.RemoteStartTransactionRequest) (confirmation *core.RemoteStartTransactionConfirmation, err error) { args := coreListener.MethodCalled("OnRemoteStartTransaction", request) - conf := args.Get(0).(*ocpp16.RemoteStartTransactionConfirmation) + conf := args.Get(0).(*core.RemoteStartTransactionConfirmation) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnRemoteStopTransaction(request *ocpp16.RemoteStopTransactionRequest) (confirmation *ocpp16.RemoteStopTransactionConfirmation, err error) { +func (coreListener MockChargePointCoreListener) OnRemoteStopTransaction(request *core.RemoteStopTransactionRequest) (confirmation *core.RemoteStopTransactionConfirmation, err error) { args := coreListener.MethodCalled("OnRemoteStopTransaction", request) - conf := args.Get(0).(*ocpp16.RemoteStopTransactionConfirmation) + conf := args.Get(0).(*core.RemoteStopTransactionConfirmation) return conf, args.Error(1) } @@ -265,15 +272,15 @@ type MockChargePointLocalAuthListListener struct { mock.Mock } -func (localAuthListListener MockChargePointLocalAuthListListener) OnGetLocalListVersion(request *ocpp16.GetLocalListVersionRequest) (confirmation *ocpp16.GetLocalListVersionConfirmation, err error) { +func (localAuthListListener MockChargePointLocalAuthListListener) OnGetLocalListVersion(request *localauth.GetLocalListVersionRequest) (confirmation *localauth.GetLocalListVersionConfirmation, err error) { args := localAuthListListener.MethodCalled("OnGetLocalListVersion", request) - conf := args.Get(0).(*ocpp16.GetLocalListVersionConfirmation) + conf := args.Get(0).(*localauth.GetLocalListVersionConfirmation) return conf, args.Error(1) } -func (localAuthListListener MockChargePointLocalAuthListListener) OnSendLocalList(request *ocpp16.SendLocalListRequest) (confirmation *ocpp16.SendLocalListConfirmation, err error) { +func (localAuthListListener MockChargePointLocalAuthListListener) OnSendLocalList(request *localauth.SendLocalListRequest) (confirmation *localauth.SendLocalListConfirmation, err error) { args := localAuthListListener.MethodCalled("OnSendLocalList", request) - conf := args.Get(0).(*ocpp16.SendLocalListConfirmation) + conf := args.Get(0).(*localauth.SendLocalListConfirmation) return conf, args.Error(1) } @@ -282,15 +289,15 @@ type MockCentralSystemFirmwareManagementListener struct { mock.Mock } -func (firmwareListener MockCentralSystemFirmwareManagementListener) OnDiagnosticsStatusNotification(chargePointId string, request *ocpp16.DiagnosticsStatusNotificationRequest) (confirmation *ocpp16.DiagnosticsStatusNotificationConfirmation, err error) { +func (firmwareListener MockCentralSystemFirmwareManagementListener) OnDiagnosticsStatusNotification(chargePointId string, request *firmware.DiagnosticsStatusNotificationRequest) (confirmation *firmware.DiagnosticsStatusNotificationConfirmation, err error) { args := firmwareListener.MethodCalled("OnDiagnosticsStatusNotification", chargePointId, request) - conf := args.Get(0).(*ocpp16.DiagnosticsStatusNotificationConfirmation) + conf := args.Get(0).(*firmware.DiagnosticsStatusNotificationConfirmation) return conf, args.Error(1) } -func (firmwareListener MockCentralSystemFirmwareManagementListener) OnFirmwareStatusNotification(chargePointId string, request *ocpp16.FirmwareStatusNotificationRequest) (confirmation *ocpp16.FirmwareStatusNotificationConfirmation, err error) { +func (firmwareListener MockCentralSystemFirmwareManagementListener) OnFirmwareStatusNotification(chargePointId string, request *firmware.FirmwareStatusNotificationRequest) (confirmation *firmware.FirmwareStatusNotificationConfirmation, err error) { args := firmwareListener.MethodCalled("OnFirmwareStatusNotification", chargePointId, request) - conf := args.Get(0).(*ocpp16.FirmwareStatusNotificationConfirmation) + conf := args.Get(0).(*firmware.FirmwareStatusNotificationConfirmation) return conf, args.Error(1) } @@ -299,15 +306,15 @@ type MockChargePointFirmwareManagementListener struct { mock.Mock } -func (firmwareListener MockChargePointFirmwareManagementListener) OnGetDiagnostics(request *ocpp16.GetDiagnosticsRequest) (confirmation *ocpp16.GetDiagnosticsConfirmation, err error) { +func (firmwareListener MockChargePointFirmwareManagementListener) OnGetDiagnostics(request *firmware.GetDiagnosticsRequest) (confirmation *firmware.GetDiagnosticsConfirmation, err error) { args := firmwareListener.MethodCalled("OnGetDiagnostics", request) - conf := args.Get(0).(*ocpp16.GetDiagnosticsConfirmation) + conf := args.Get(0).(*firmware.GetDiagnosticsConfirmation) return conf, args.Error(1) } -func (firmwareListener MockChargePointFirmwareManagementListener) OnUpdateFirmware(request *ocpp16.UpdateFirmwareRequest) (confirmation *ocpp16.UpdateFirmwareConfirmation, err error) { +func (firmwareListener MockChargePointFirmwareManagementListener) OnUpdateFirmware(request *firmware.UpdateFirmwareRequest) (confirmation *firmware.UpdateFirmwareConfirmation, err error) { args := firmwareListener.MethodCalled("OnUpdateFirmware", request) - conf := args.Get(0).(*ocpp16.UpdateFirmwareConfirmation) + conf := args.Get(0).(*firmware.UpdateFirmwareConfirmation) return conf, args.Error(1) } @@ -321,15 +328,15 @@ type MockChargePointReservationListener struct { mock.Mock } -func (reservationListener MockChargePointReservationListener) OnReserveNow(request *ocpp16.ReserveNowRequest) (confirmation *ocpp16.ReserveNowConfirmation, err error) { +func (reservationListener MockChargePointReservationListener) OnReserveNow(request *reservation.ReserveNowRequest) (confirmation *reservation.ReserveNowConfirmation, err error) { args := reservationListener.MethodCalled("OnReserveNow", request) - conf := args.Get(0).(*ocpp16.ReserveNowConfirmation) + conf := args.Get(0).(*reservation.ReserveNowConfirmation) return conf, args.Error(1) } -func (reservationListener MockChargePointReservationListener) OnCancelReservation(request *ocpp16.CancelReservationRequest) (confirmation *ocpp16.CancelReservationConfirmation, err error) { +func (reservationListener MockChargePointReservationListener) OnCancelReservation(request *reservation.CancelReservationRequest) (confirmation *reservation.CancelReservationConfirmation, err error) { args := reservationListener.MethodCalled("OnCancelReservation", request) - conf := args.Get(0).(*ocpp16.CancelReservationConfirmation) + conf := args.Get(0).(*reservation.CancelReservationConfirmation) return conf, args.Error(1) } @@ -343,9 +350,9 @@ type MockChargePointRemoteTriggerListener struct { mock.Mock } -func (remoteTriggerListener MockChargePointRemoteTriggerListener) OnTriggerMessage(request *ocpp16.TriggerMessageRequest) (confirmation *ocpp16.TriggerMessageConfirmation, err error) { +func (remoteTriggerListener MockChargePointRemoteTriggerListener) OnTriggerMessage(request *remotetrigger.TriggerMessageRequest) (confirmation *remotetrigger.TriggerMessageConfirmation, err error) { args := remoteTriggerListener.MethodCalled("OnTriggerMessage", request) - conf := args.Get(0).(*ocpp16.TriggerMessageConfirmation) + conf := args.Get(0).(*remotetrigger.TriggerMessageConfirmation) return conf, args.Error(1) } @@ -359,21 +366,21 @@ type MockChargePointSmartChargingListener struct { mock.Mock } -func (smartChargingListener MockChargePointSmartChargingListener) OnSetChargingProfile(request *ocpp16.SetChargingProfileRequest) (confirmation *ocpp16.SetChargingProfileConfirmation, err error) { +func (smartChargingListener MockChargePointSmartChargingListener) OnSetChargingProfile(request *smartcharging.SetChargingProfileRequest) (confirmation *smartcharging.SetChargingProfileConfirmation, err error) { args := smartChargingListener.MethodCalled("OnSetChargingProfile", request) - conf := args.Get(0).(*ocpp16.SetChargingProfileConfirmation) + conf := args.Get(0).(*smartcharging.SetChargingProfileConfirmation) return conf, args.Error(1) } -func (smartChargingListener MockChargePointSmartChargingListener) OnClearChargingProfile(request *ocpp16.ClearChargingProfileRequest) (confirmation *ocpp16.ClearChargingProfileConfirmation, err error) { +func (smartChargingListener MockChargePointSmartChargingListener) OnClearChargingProfile(request *smartcharging.ClearChargingProfileRequest) (confirmation *smartcharging.ClearChargingProfileConfirmation, err error) { args := smartChargingListener.MethodCalled("OnClearChargingProfile", request) - conf := args.Get(0).(*ocpp16.ClearChargingProfileConfirmation) + conf := args.Get(0).(*smartcharging.ClearChargingProfileConfirmation) return conf, args.Error(1) } -func (smartChargingListener MockChargePointSmartChargingListener) OnGetCompositeSchedule(request *ocpp16.GetCompositeScheduleRequest) (confirmation *ocpp16.GetCompositeScheduleConfirmation, err error) { +func (smartChargingListener MockChargePointSmartChargingListener) OnGetCompositeSchedule(request *smartcharging.GetCompositeScheduleRequest) (confirmation *smartcharging.GetCompositeScheduleConfirmation, err error) { args := smartChargingListener.MethodCalled("OnGetCompositeSchedule", request) - conf := args.Get(0).(*ocpp16.GetCompositeScheduleConfirmation) + conf := args.Get(0).(*smartcharging.GetCompositeScheduleConfirmation) return conf, args.Error(1) } @@ -432,12 +439,12 @@ type expectedChargePointOptions struct { forwardWrittenMessage bool } -func setupDefaultCentralSystemHandlers(suite *OcppV16TestSuite, coreListener ocpp16.CentralSystemCoreListener, options expectedCentralSystemOptions) { +func setupDefaultCentralSystemHandlers(suite *OcppV16TestSuite, coreListener core.CentralSystemCoreHandler, options expectedCentralSystemOptions) { t := suite.T() suite.centralSystem.SetNewChargePointHandler(func(chargePointId string) { assert.Equal(t, options.clientId, chargePointId) }) - suite.centralSystem.SetCentralSystemCoreListener(coreListener) + suite.centralSystem.SetCentralSystemCoreHandler(coreListener) suite.mockWsServer.On("Start", mock.AnythingOfType("int"), mock.AnythingOfType("string")).Return(options.startReturnArgument) suite.mockWsServer.On("Write", mock.AnythingOfType("string"), mock.Anything).Return(options.writeReturnArgument).Run(func(args mock.Arguments) { clientId := args.String(0) @@ -456,9 +463,9 @@ func setupDefaultCentralSystemHandlers(suite *OcppV16TestSuite, coreListener ocp }) } -func setupDefaultChargePointHandlers(suite *OcppV16TestSuite, coreListener ocpp16.ChargePointCoreListener, options expectedChargePointOptions) { +func setupDefaultChargePointHandlers(suite *OcppV16TestSuite, coreListener core.ChargePointCoreHandler, options expectedChargePointOptions) { t := suite.T() - suite.chargePoint.SetChargePointCoreListener(coreListener) + suite.chargePoint.SetChargePointCoreHandler(coreListener) suite.mockWsClient.On("Start", mock.AnythingOfType("string")).Return(options.startReturnArgument).Run(func(args mock.Arguments) { u := args.String(0) assert.Equal(t, fmt.Sprintf("%s/%s", options.serverUrl, options.clientId), u) @@ -482,8 +489,8 @@ func setupDefaultChargePointHandlers(suite *OcppV16TestSuite, coreListener ocpp1 }) } -func assertDateTimeEquality(t *testing.T, expected ocpp16.DateTime, actual ocpp16.DateTime) { - assert.Equal(t, expected.Time.Format(ocpp16.ISO8601), actual.Time.Format(ocpp16.ISO8601)) +func assertDateTimeEquality(t *testing.T, expected types.DateTime, actual types.DateTime) { + assert.Equal(t, expected.FormatTimestamp(), actual.FormatTimestamp()) } func testUnsupportedRequestFromChargePoint(suite *OcppV16TestSuite, request ocpp.Request, requestJson string, messageId string) { @@ -511,7 +518,7 @@ func testUnsupportedRequestFromChargePoint(suite *OcppV16TestSuite, request ocpp err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) // Run request test - err = suite.chargePoint.SendRequestAsync(request, func(confirmation ocpp.Confirmation, err error) { + err = suite.chargePoint.SendRequestAsync(request, func(confirmation ocpp.Response, err error) { t.Fail() }) assert.Error(t, err) @@ -548,7 +555,7 @@ func testUnsupportedRequestFromCentralSystem(suite *OcppV16TestSuite, request oc err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) // Run request test - err = suite.centralSystem.SendRequestAsync(wsId, request, func(confirmation ocpp.Confirmation, err error) { + err = suite.centralSystem.SendRequestAsync(wsId, request, func(confirmation ocpp.Response, err error) { t.Fail() }) assert.Error(t, err) @@ -570,14 +577,14 @@ type RequestTestEntry struct { } type ConfirmationTestEntry struct { - Confirmation ocpp.Confirmation + Confirmation ocpp.Response ExpectedValid bool } // TODO: pass expected error value for improved validation and error message func ExecuteGenericTestTable(t *testing.T, testTable []GenericTestEntry) { for _, testCase := range testTable { - err := ocpp16.Validate.Struct(testCase.Element) + err := types.Validate.Struct(testCase.Element) if err != nil { assert.Equal(t, testCase.ExpectedValid, false, err.Error()) } else { @@ -589,8 +596,8 @@ func ExecuteGenericTestTable(t *testing.T, testTable []GenericTestEntry) { // ---------------------- TESTS ---------------------- type OcppV16TestSuite struct { suite.Suite - ocppjChargePoint *ocppj.ChargePoint - ocppjCentralSystem *ocppj.CentralSystem + ocppjChargePoint *ocppj.Client + ocppjCentralSystem *ocppj.Server mockWsServer *MockWebsocketServer mockWsClient *MockWebsocketClient chargePoint ocpp16.ChargePoint @@ -609,18 +616,18 @@ func (testGenerator *TestRandomIdGenerator) generateId() string { var defaultMessageId = "1234" func (suite *OcppV16TestSuite) SetupTest() { - coreProfile := ocpp16.CoreProfile - localAuthListProfile := ocpp16.LocalAuthListProfile - firmwareProfile := ocpp16.FirmwareManagementProfile - reservationProfile := ocpp16.ReservationProfile - remoteTriggerProfile := ocpp16.RemoteTriggerProfile - smartChargingProfile := ocpp16.SmartChargingProfile + coreProfile := core.Profile + localAuthListProfile := localauth.Profile + firmwareProfile := firmware.Profile + reservationProfile := reservation.Profile + remoteTriggerProfile := remotetrigger.Profile + smartChargingProfile := smartcharging.Profile mockClient := MockWebsocketClient{} mockServer := MockWebsocketServer{} suite.mockWsClient = &mockClient suite.mockWsServer = &mockServer - suite.ocppjChargePoint = ocppj.NewChargePoint("test_id", suite.mockWsClient, coreProfile, localAuthListProfile, firmwareProfile, reservationProfile, remoteTriggerProfile, smartChargingProfile) - suite.ocppjCentralSystem = ocppj.NewCentralSystem(suite.mockWsServer, coreProfile, localAuthListProfile, firmwareProfile, reservationProfile, remoteTriggerProfile, smartChargingProfile) + suite.ocppjChargePoint = ocppj.NewClient("test_id", suite.mockWsClient, coreProfile, localAuthListProfile, firmwareProfile, reservationProfile, remoteTriggerProfile, smartChargingProfile) + suite.ocppjCentralSystem = ocppj.NewServer(suite.mockWsServer, coreProfile, localAuthListProfile, firmwareProfile, reservationProfile, remoteTriggerProfile, smartChargingProfile) suite.chargePoint = ocpp16.NewChargePoint("test_id", suite.ocppjChargePoint, suite.mockWsClient) suite.centralSystem = ocpp16.NewCentralSystem(suite.ocppjCentralSystem, suite.mockWsServer) suite.messageIdGenerator = TestRandomIdGenerator{generator: func() string { diff --git a/ocpp1.6_test/remote_start_transaction_test.go b/ocpp1.6_test/remote_start_transaction_test.go index b961138a..6e8a3848 100644 --- a/ocpp1.6_test/remote_start_transaction_test.go +++ b/ocpp1.6_test/remote_start_transaction_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -10,15 +11,15 @@ import ( // Test func (suite *OcppV16TestSuite) TestRemoteStartTransactionRequestValidation() { t := suite.T() - chargingSchedule := ocpp16.NewChargingSchedule(ocpp16.ChargingRateUnitWatts, ocpp16.NewChargingSchedulePeriod(0, 10.0)) - chargingProfile := ocpp16.NewChargingProfile(1, 1, ocpp16.ChargingProfilePurposeChargePointMaxProfile, ocpp16.ChargingProfileKindAbsolute, chargingSchedule) + chargingSchedule := types.NewChargingSchedule(types.ChargingRateUnitWatts, types.NewChargingSchedulePeriod(0, 10.0)) + chargingProfile := types.NewChargingProfile(1, 1, types.ChargingProfilePurposeChargePointMaxProfile, types.ChargingProfileKindAbsolute, chargingSchedule) var requestTable = []GenericTestEntry{ - {ocpp16.RemoteStartTransactionRequest{IdTag: "12345", ConnectorId: 1, ChargingProfile: chargingProfile}, true}, - {ocpp16.RemoteStartTransactionRequest{IdTag: "12345", ConnectorId: 1}, true}, - {ocpp16.RemoteStartTransactionRequest{IdTag: "12345"}, true}, - {ocpp16.RemoteStartTransactionRequest{IdTag: "12345", ConnectorId: -1}, false}, - {ocpp16.RemoteStartTransactionRequest{}, false}, - {ocpp16.RemoteStartTransactionRequest{IdTag: ">20..................", ConnectorId: 1}, false}, + {core.RemoteStartTransactionRequest{IdTag: "12345", ConnectorId: 1, ChargingProfile: chargingProfile}, true}, + {core.RemoteStartTransactionRequest{IdTag: "12345", ConnectorId: 1}, true}, + {core.RemoteStartTransactionRequest{IdTag: "12345"}, true}, + {core.RemoteStartTransactionRequest{IdTag: "12345", ConnectorId: -1}, false}, + {core.RemoteStartTransactionRequest{}, false}, + {core.RemoteStartTransactionRequest{IdTag: ">20..................", ConnectorId: 1}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -26,10 +27,10 @@ func (suite *OcppV16TestSuite) TestRemoteStartTransactionRequestValidation() { func (suite *OcppV16TestSuite) TestRemoteStartTransactionConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.RemoteStartTransactionConfirmation{Status: ocpp16.RemoteStartStopStatusAccepted}, true}, - {ocpp16.RemoteStartTransactionConfirmation{Status: ocpp16.RemoteStartStopStatusRejected}, true}, - {ocpp16.RemoteStartTransactionConfirmation{Status: "invalidRemoteStartTransactionStatus"}, false}, - {ocpp16.RemoteStartTransactionConfirmation{}, false}, + {core.RemoteStartTransactionConfirmation{Status: types.RemoteStartStopStatusAccepted}, true}, + {core.RemoteStartTransactionConfirmation{Status: types.RemoteStartStopStatusRejected}, true}, + {core.RemoteStartTransactionConfirmation{Status: "invalidRemoteStartTransactionStatus"}, false}, + {core.RemoteStartTransactionConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -43,17 +44,17 @@ func (suite *OcppV16TestSuite) TestRemoteStartTransactionE2EMocked() { connectorId := 1 chargingProfileId := 1 stackLevel := 1 - chargingProfilePurpose := ocpp16.ChargingProfilePurposeChargePointMaxProfile - chargingProfileKind := ocpp16.ChargingProfileKindAbsolute - chargingRateUnit := ocpp16.ChargingRateUnitWatts + chargingProfilePurpose := types.ChargingProfilePurposeChargePointMaxProfile + chargingProfileKind := types.ChargingProfileKindAbsolute + chargingRateUnit := types.ChargingRateUnitWatts startPeriod := 0 limit := 10.0 - status := ocpp16.RemoteStartStopStatusAccepted - chargingSchedule := ocpp16.NewChargingSchedule(chargingRateUnit, ocpp16.NewChargingSchedulePeriod(startPeriod, limit)) - chargingProfile := ocpp16.NewChargingProfile(chargingProfileId, stackLevel, chargingProfilePurpose, chargingProfileKind, chargingSchedule) + status := types.RemoteStartStopStatusAccepted + chargingSchedule := types.NewChargingSchedule(chargingRateUnit, types.NewChargingSchedulePeriod(startPeriod, limit)) + chargingProfile := types.NewChargingProfile(chargingProfileId, stackLevel, chargingProfilePurpose, chargingProfileKind, chargingSchedule) requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"idTag":"%v","chargingProfile":{"chargingProfileId":%v,"stackLevel":%v,"chargingProfilePurpose":"%v","chargingProfileKind":"%v","chargingSchedule":{"chargingRateUnit":"%v","chargingSchedulePeriod":[{"startPeriod":%v,"limit":%v}]}}}]`, messageId, - ocpp16.RemoteStartTransactionFeatureName, + core.RemoteStartTransactionFeatureName, connectorId, idTag, chargingProfileId, @@ -64,7 +65,7 @@ func (suite *OcppV16TestSuite) TestRemoteStartTransactionE2EMocked() { startPeriod, limit) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - RemoteStartTransactionConfirmation := ocpp16.NewRemoteStartTransactionConfirmation(status) + RemoteStartTransactionConfirmation := core.NewRemoteStartTransactionConfirmation(status) channel := NewMockWebSocket(wsId) coreListener := MockChargePointCoreListener{} @@ -76,12 +77,12 @@ func (suite *OcppV16TestSuite) TestRemoteStartTransactionE2EMocked() { err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.RemoteStartTransaction(wsId, func(confirmation *ocpp16.RemoteStartTransactionConfirmation, err error) { + err = suite.centralSystem.RemoteStartTransaction(wsId, func(confirmation *core.RemoteStartTransactionConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) resultChannel <- true - }, idTag, func(request *ocpp16.RemoteStartTransactionRequest) { + }, idTag, func(request *core.RemoteStartTransactionRequest) { request.ConnectorId = connectorId request.ChargingProfile = chargingProfile }) @@ -93,10 +94,10 @@ func (suite *OcppV16TestSuite) TestRemoteStartTransactionE2EMocked() { func (suite *OcppV16TestSuite) TestRemoteStartTransactionInvalidEndpoint() { messageId := defaultMessageId idTag := "12345" - remoteStartTransactionRequest := ocpp16.NewRemoteStartTransactionRequest(idTag) + remoteStartTransactionRequest := core.NewRemoteStartTransactionRequest(idTag) requestJson := fmt.Sprintf(`[2,"%v","%v",{"idTag":"%v"}]`, messageId, - ocpp16.RemoteStartTransactionFeatureName, + core.RemoteStartTransactionFeatureName, idTag) testUnsupportedRequestFromChargePoint(suite, remoteStartTransactionRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/remote_stop_transaction_test.go b/ocpp1.6_test/remote_stop_transaction_test.go index 5daae996..9df3ac5e 100644 --- a/ocpp1.6_test/remote_stop_transaction_test.go +++ b/ocpp1.6_test/remote_stop_transaction_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,9 +12,9 @@ import ( func (suite *OcppV16TestSuite) TestRemoteStopTransactionRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.RemoteStopTransactionRequest{TransactionId: 1}, true}, - {ocpp16.RemoteStopTransactionRequest{}, true}, - {ocpp16.RemoteStopTransactionRequest{TransactionId: -1}, false}, + {core.RemoteStopTransactionRequest{TransactionId: 1}, true}, + {core.RemoteStopTransactionRequest{}, true}, + {core.RemoteStopTransactionRequest{TransactionId: -1}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -21,10 +22,10 @@ func (suite *OcppV16TestSuite) TestRemoteStopTransactionRequestValidation() { func (suite *OcppV16TestSuite) TestRemoteStopTransactionConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.RemoteStopTransactionConfirmation{Status: ocpp16.RemoteStartStopStatusAccepted}, true}, - {ocpp16.RemoteStopTransactionConfirmation{Status: ocpp16.RemoteStartStopStatusRejected}, true}, - {ocpp16.RemoteStopTransactionConfirmation{Status: "invalidRemoteStopTransactionStatus"}, false}, - {ocpp16.RemoteStopTransactionConfirmation{}, false}, + {core.RemoteStopTransactionConfirmation{Status: types.RemoteStartStopStatusAccepted}, true}, + {core.RemoteStopTransactionConfirmation{Status: types.RemoteStartStopStatusRejected}, true}, + {core.RemoteStopTransactionConfirmation{Status: "invalidRemoteStopTransactionStatus"}, false}, + {core.RemoteStopTransactionConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -35,10 +36,10 @@ func (suite *OcppV16TestSuite) TestRemoteStopTransactionE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" transactionId := 1 - status := ocpp16.RemoteStartStopStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"transactionId":%v}]`, messageId, ocpp16.RemoteStopTransactionFeatureName, transactionId) + status := types.RemoteStartStopStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"transactionId":%v}]`, messageId, core.RemoteStopTransactionFeatureName, transactionId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - RemoteStopTransactionConfirmation := ocpp16.NewRemoteStopTransactionConfirmation(status) + RemoteStopTransactionConfirmation := core.NewRemoteStopTransactionConfirmation(status) channel := NewMockWebSocket(wsId) coreListener := MockChargePointCoreListener{} @@ -50,7 +51,7 @@ func (suite *OcppV16TestSuite) TestRemoteStopTransactionE2EMocked() { err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.RemoteStopTransaction(wsId, func(confirmation *ocpp16.RemoteStopTransactionConfirmation, err error) { + err = suite.centralSystem.RemoteStopTransaction(wsId, func(confirmation *core.RemoteStopTransactionConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -64,7 +65,7 @@ func (suite *OcppV16TestSuite) TestRemoteStopTransactionE2EMocked() { func (suite *OcppV16TestSuite) TestRemoteStopTransactionInvalidEndpoint() { messageId := defaultMessageId transactionId := 1 - RemoteStopTransactionRequest := ocpp16.NewRemoteStopTransactionRequest(transactionId) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"transactionId":%v}]`, messageId, ocpp16.RemoteStopTransactionFeatureName, transactionId) + RemoteStopTransactionRequest := core.NewRemoteStopTransactionRequest(transactionId) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"transactionId":%v}]`, messageId, core.RemoteStopTransactionFeatureName, transactionId) testUnsupportedRequestFromChargePoint(suite, RemoteStopTransactionRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/reserve_now_test.go b/ocpp1.6_test/reserve_now_test.go index 9fa57eb2..c9524991 100644 --- a/ocpp1.6_test/reserve_now_test.go +++ b/ocpp1.6_test/reserve_now_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/reservation" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -12,16 +13,16 @@ import ( func (suite *OcppV16TestSuite) TestReserveNowRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.ReserveNowRequest{ConnectorId: 1, ExpiryDate: ocpp16.NewDateTime(time.Now()), IdTag: "12345", ReservationId: 42, ParentIdTag: "9999"}, true}, - {ocpp16.ReserveNowRequest{ConnectorId: 1, ExpiryDate: ocpp16.NewDateTime(time.Now()), IdTag: "12345", ReservationId: 42}, true}, - {ocpp16.ReserveNowRequest{ExpiryDate: ocpp16.NewDateTime(time.Now()), IdTag: "12345", ReservationId: 42}, true}, - {ocpp16.ReserveNowRequest{ConnectorId: 1, ExpiryDate: ocpp16.NewDateTime(time.Now()), IdTag: "12345"}, true}, - {ocpp16.ReserveNowRequest{ConnectorId: 1, ExpiryDate: ocpp16.NewDateTime(time.Now())}, false}, - {ocpp16.ReserveNowRequest{ConnectorId: 1, IdTag: "12345"}, false}, - {ocpp16.ReserveNowRequest{ConnectorId: -1, ExpiryDate: ocpp16.NewDateTime(time.Now()), IdTag: "12345", ReservationId: 42}, false}, - {ocpp16.ReserveNowRequest{ConnectorId: 1, ExpiryDate: ocpp16.NewDateTime(time.Now()), IdTag: "12345", ReservationId: -1}, false}, - {ocpp16.ReserveNowRequest{ConnectorId: 1, ExpiryDate: ocpp16.NewDateTime(time.Now()), IdTag: ">20.................."}, false}, - {ocpp16.ReserveNowRequest{ConnectorId: 1, ExpiryDate: ocpp16.NewDateTime(time.Now()), IdTag: "12345", ReservationId: 42, ParentIdTag: ">20.................."}, false}, + {reservation.ReserveNowRequest{ConnectorId: 1, ExpiryDate: types.NewDateTime(time.Now()), IdTag: "12345", ReservationId: 42, ParentIdTag: "9999"}, true}, + {reservation.ReserveNowRequest{ConnectorId: 1, ExpiryDate: types.NewDateTime(time.Now()), IdTag: "12345", ReservationId: 42}, true}, + {reservation.ReserveNowRequest{ExpiryDate: types.NewDateTime(time.Now()), IdTag: "12345", ReservationId: 42}, true}, + {reservation.ReserveNowRequest{ConnectorId: 1, ExpiryDate: types.NewDateTime(time.Now()), IdTag: "12345"}, true}, + {reservation.ReserveNowRequest{ConnectorId: 1, ExpiryDate: types.NewDateTime(time.Now())}, false}, + {reservation.ReserveNowRequest{ConnectorId: 1, IdTag: "12345"}, false}, + {reservation.ReserveNowRequest{ConnectorId: -1, ExpiryDate: types.NewDateTime(time.Now()), IdTag: "12345", ReservationId: 42}, false}, + {reservation.ReserveNowRequest{ConnectorId: 1, ExpiryDate: types.NewDateTime(time.Now()), IdTag: "12345", ReservationId: -1}, false}, + {reservation.ReserveNowRequest{ConnectorId: 1, ExpiryDate: types.NewDateTime(time.Now()), IdTag: ">20.................."}, false}, + {reservation.ReserveNowRequest{ConnectorId: 1, ExpiryDate: types.NewDateTime(time.Now()), IdTag: "12345", ReservationId: 42, ParentIdTag: ">20.................."}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -29,9 +30,9 @@ func (suite *OcppV16TestSuite) TestReserveNowRequestValidation() { func (suite *OcppV16TestSuite) TestReserveNowConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.ReserveNowConfirmation{Status: ocpp16.ReservationStatusAccepted}, true}, - {ocpp16.ReserveNowConfirmation{Status: "invalidReserveNowStatus"}, false}, - {ocpp16.ReserveNowConfirmation{}, false}, + {reservation.ReserveNowConfirmation{Status: reservation.ReservationStatusAccepted}, true}, + {reservation.ReserveNowConfirmation{Status: "invalidReserveNowStatus"}, false}, + {reservation.ReserveNowConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -45,38 +46,38 @@ func (suite *OcppV16TestSuite) TestReserveNowE2EMocked() { parentIdTag := "00000" connectorId := 1 reservationId := 42 - expiryDate := ocpp16.NewDateTime(time.Now()) - status := ocpp16.ReservationStatusAccepted + expiryDate := types.NewDateTime(time.Now()) + status := reservation.ReservationStatusAccepted requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"expiryDate":"%v","idTag":"%v","parentIdTag":"%v","reservationId":%v}]`, - messageId, ocpp16.ReserveNowFeatureName, connectorId, expiryDate.Format(ocpp16.ISO8601), idTag, parentIdTag, reservationId) + messageId, reservation.ReserveNowFeatureName, connectorId, expiryDate.FormatTimestamp(), idTag, parentIdTag, reservationId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - ReserveNowConfirmation := ocpp16.NewReserveNowConfirmation(status) + ReserveNowConfirmation := reservation.NewReserveNowConfirmation(status) channel := NewMockWebSocket(wsId) reservationListener := MockChargePointReservationListener{} reservationListener.On("OnReserveNow", mock.Anything).Return(ReserveNowConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp16.ReserveNowRequest) + request, ok := args.Get(0).(*reservation.ReserveNowRequest) assert.True(t, ok) assert.Equal(t, connectorId, request.ConnectorId) - assert.Equal(t, expiryDate.Format(ocpp16.ISO8601), request.ExpiryDate.Format(ocpp16.ISO8601)) + assert.Equal(t, expiryDate.FormatTimestamp(), request.ExpiryDate.FormatTimestamp()) assert.Equal(t, idTag, request.IdTag) assert.Equal(t, parentIdTag, request.ParentIdTag) assert.Equal(t, reservationId, request.ReservationId) }) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - suite.chargePoint.SetReservationListener(reservationListener) + suite.chargePoint.SetReservationHandler(reservationListener) // Run Test suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.ReserveNow(wsId, func(confirmation *ocpp16.ReserveNowConfirmation, err error) { + err = suite.centralSystem.ReserveNow(wsId, func(confirmation *reservation.ReserveNowConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) resultChannel <- true - }, connectorId, expiryDate, idTag, reservationId, func(request *ocpp16.ReserveNowRequest) { + }, connectorId, expiryDate, idTag, reservationId, func(request *reservation.ReserveNowRequest) { request.ParentIdTag = parentIdTag }) assert.Nil(t, err) @@ -90,10 +91,10 @@ func (suite *OcppV16TestSuite) TestReserveNowInvalidEndpoint() { parentIdTag := "00000" connectorId := 1 reservationId := 42 - expiryDate := ocpp16.NewDateTime(time.Now()) - reserveNowRequest := ocpp16.NewReserveNowRequest(connectorId, expiryDate, idTag, reservationId) + expiryDate := types.NewDateTime(time.Now()) + reserveNowRequest := reservation.NewReserveNowRequest(connectorId, expiryDate, idTag, reservationId) reserveNowRequest.ParentIdTag = parentIdTag requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"expiryDate":"%v","idTag":"%v","parentIdTag":"%v","reservationId":%v}]`, - messageId, ocpp16.ReserveNowFeatureName, connectorId, expiryDate.Format(ocpp16.ISO8601), idTag, parentIdTag, reservationId) + messageId, reservation.ReserveNowFeatureName, connectorId, expiryDate.FormatTimestamp(), idTag, parentIdTag, reservationId) testUnsupportedRequestFromChargePoint(suite, reserveNowRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/reset_test.go b/ocpp1.6_test/reset_test.go index 8d7d9d02..55a9aec8 100644 --- a/ocpp1.6_test/reset_test.go +++ b/ocpp1.6_test/reset_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -10,10 +10,10 @@ import ( func (suite *OcppV16TestSuite) TestResetRequestValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp16.ResetRequest{Type: ocpp16.ResetTypeHard}, true}, - {ocpp16.ResetRequest{Type: ocpp16.ResetTypeSoft}, true}, - {ocpp16.ResetRequest{Type: "invalidResetType"}, false}, - {ocpp16.ResetRequest{}, false}, + {core.ResetRequest{Type: core.ResetTypeHard}, true}, + {core.ResetRequest{Type: core.ResetTypeSoft}, true}, + {core.ResetRequest{Type: "invalidResetType"}, false}, + {core.ResetRequest{}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -21,10 +21,10 @@ func (suite *OcppV16TestSuite) TestResetRequestValidation() { func (suite *OcppV16TestSuite) TestResetConfirmationValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp16.ResetConfirmation{Status: ocpp16.ResetStatusAccepted}, true}, - {ocpp16.ResetConfirmation{Status: ocpp16.ResetStatusRejected}, true}, - {ocpp16.ResetConfirmation{Status: "invalidResetStatus"}, false}, - {ocpp16.ResetConfirmation{}, false}, + {core.ResetConfirmation{Status: core.ResetStatusAccepted}, true}, + {core.ResetConfirmation{Status: core.ResetStatusRejected}, true}, + {core.ResetConfirmation{Status: "invalidResetStatus"}, false}, + {core.ResetConfirmation{}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -35,11 +35,11 @@ func (suite *OcppV16TestSuite) TestResetE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - resetType := ocpp16.ResetTypeSoft - status := ocpp16.ResetStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"type":"%v"}]`, messageId, ocpp16.ResetFeatureName, resetType) + resetType := core.ResetTypeSoft + status := core.ResetStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"type":"%v"}]`, messageId, core.ResetFeatureName, resetType) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - resetConfirmation := ocpp16.NewResetConfirmation(status) + resetConfirmation := core.NewResetConfirmation(status) channel := NewMockWebSocket(wsId) // Setting handlers coreListener := MockChargePointCoreListener{} @@ -51,7 +51,7 @@ func (suite *OcppV16TestSuite) TestResetE2EMocked() { err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.Reset(wsId, func(confirmation *ocpp16.ResetConfirmation, err error) { + err = suite.centralSystem.Reset(wsId, func(confirmation *core.ResetConfirmation, err error) { assert.NotNil(t, confirmation) assert.Nil(t, err) assert.Equal(t, status, confirmation.Status) @@ -64,8 +64,8 @@ func (suite *OcppV16TestSuite) TestResetE2EMocked() { func (suite *OcppV16TestSuite) TestResetInvalidEndpoint() { messageId := defaultMessageId - resetType := ocpp16.ResetTypeSoft - resetRequest := ocpp16.NewResetRequest(resetType) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"type":"%v"}]`, messageId, ocpp16.ResetFeatureName, resetType) + resetType := core.ResetTypeSoft + resetRequest := core.NewResetRequest(resetType) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"type":"%v"}]`, messageId, core.ResetFeatureName, resetType) testUnsupportedRequestFromChargePoint(suite, resetRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/send_local_list_test.go b/ocpp1.6_test/send_local_list_test.go index 989168e7..69834d93 100644 --- a/ocpp1.6_test/send_local_list_test.go +++ b/ocpp1.6_test/send_local_list_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/localauth" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -11,27 +12,27 @@ import ( // Test func (suite *OcppV16TestSuite) TestSendLocalListRequestValidation() { t := suite.T() - localAuthEntry := ocpp16.AuthorizationData{IdTag: "12345", IdTagInfo: &ocpp16.IdTagInfo{ - ExpiryDate: ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)), + localAuthEntry := localauth.AuthorizationData{IdTag: "12345", IdTagInfo: &types.IdTagInfo{ + ExpiryDate: types.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "000000", - Status: ocpp16.AuthorizationStatusAccepted, + Status: types.AuthorizationStatusAccepted, }} - invalidAuthEntry := ocpp16.AuthorizationData{IdTag: "12345", IdTagInfo: &ocpp16.IdTagInfo{ - ExpiryDate: ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)), + invalidAuthEntry := localauth.AuthorizationData{IdTag: "12345", IdTagInfo: &types.IdTagInfo{ + ExpiryDate: types.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "000000", Status: "invalidStatus", }} var requestTable = []GenericTestEntry{ - {ocpp16.SendLocalListRequest{UpdateType: ocpp16.UpdateTypeDifferential, ListVersion: 1, LocalAuthorizationList: []ocpp16.AuthorizationData{localAuthEntry}}, true}, - {ocpp16.SendLocalListRequest{UpdateType: ocpp16.UpdateTypeDifferential, ListVersion: 1, LocalAuthorizationList: []ocpp16.AuthorizationData{}}, true}, - {ocpp16.SendLocalListRequest{UpdateType: ocpp16.UpdateTypeDifferential, ListVersion: 1}, true}, - {ocpp16.SendLocalListRequest{UpdateType: ocpp16.UpdateTypeDifferential, ListVersion: 0}, true}, - {ocpp16.SendLocalListRequest{UpdateType: ocpp16.UpdateTypeDifferential}, true}, - {ocpp16.SendLocalListRequest{UpdateType: ocpp16.UpdateTypeDifferential, ListVersion: -1}, false}, - {ocpp16.SendLocalListRequest{UpdateType: ocpp16.UpdateTypeDifferential, ListVersion: 1, LocalAuthorizationList: []ocpp16.AuthorizationData{invalidAuthEntry}}, false}, - {ocpp16.SendLocalListRequest{UpdateType: "invalidUpdateType", ListVersion: 1}, false}, - {ocpp16.SendLocalListRequest{ListVersion: 1}, false}, - {ocpp16.SendLocalListRequest{}, false}, + {localauth.SendLocalListRequest{UpdateType: localauth.UpdateTypeDifferential, ListVersion: 1, LocalAuthorizationList: []localauth.AuthorizationData{localAuthEntry}}, true}, + {localauth.SendLocalListRequest{UpdateType: localauth.UpdateTypeDifferential, ListVersion: 1, LocalAuthorizationList: []localauth.AuthorizationData{}}, true}, + {localauth.SendLocalListRequest{UpdateType: localauth.UpdateTypeDifferential, ListVersion: 1}, true}, + {localauth.SendLocalListRequest{UpdateType: localauth.UpdateTypeDifferential, ListVersion: 0}, true}, + {localauth.SendLocalListRequest{UpdateType: localauth.UpdateTypeDifferential}, true}, + {localauth.SendLocalListRequest{UpdateType: localauth.UpdateTypeDifferential, ListVersion: -1}, false}, + {localauth.SendLocalListRequest{UpdateType: localauth.UpdateTypeDifferential, ListVersion: 1, LocalAuthorizationList: []localauth.AuthorizationData{invalidAuthEntry}}, false}, + {localauth.SendLocalListRequest{UpdateType: "invalidUpdateType", ListVersion: 1}, false}, + {localauth.SendLocalListRequest{ListVersion: 1}, false}, + {localauth.SendLocalListRequest{}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -39,9 +40,9 @@ func (suite *OcppV16TestSuite) TestSendLocalListRequestValidation() { func (suite *OcppV16TestSuite) TestSendLocalListConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.SendLocalListConfirmation{Status: ocpp16.UpdateStatusAccepted}, true}, - {ocpp16.SendLocalListConfirmation{Status: "invalidStatus"}, false}, - {ocpp16.SendLocalListConfirmation{}, false}, + {localauth.SendLocalListConfirmation{Status: localauth.UpdateStatusAccepted}, true}, + {localauth.SendLocalListConfirmation{Status: "invalidStatus"}, false}, + {localauth.SendLocalListConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -52,30 +53,30 @@ func (suite *OcppV16TestSuite) TestSendLocalListE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" listVersion := 1 - updateType := ocpp16.UpdateTypeDifferential - status := ocpp16.UpdateStatusAccepted - localAuthEntry := ocpp16.AuthorizationData{IdTag: "12345", IdTagInfo: &ocpp16.IdTagInfo{ - ExpiryDate: ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)), + updateType := localauth.UpdateTypeDifferential + status := localauth.UpdateStatusAccepted + localAuthEntry := localauth.AuthorizationData{IdTag: "12345", IdTagInfo: &types.IdTagInfo{ + ExpiryDate: types.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "parentTag", - Status: ocpp16.AuthorizationStatusAccepted, + Status: types.AuthorizationStatusAccepted, }} requestJson := fmt.Sprintf(`[2,"%v","%v",{"listVersion":%v,"localAuthorizationList":[{"idTag":"%v","idTagInfo":{"expiryDate":"%v","parentIdTag":"%v","status":"%v"}}],"updateType":"%v"}]`, - messageId, ocpp16.SendLocalListFeatureName, listVersion, localAuthEntry.IdTag, localAuthEntry.IdTagInfo.ExpiryDate.Format(ocpp16.ISO8601), localAuthEntry.IdTagInfo.ParentIdTag, localAuthEntry.IdTagInfo.Status, updateType) + messageId, localauth.SendLocalListFeatureName, listVersion, localAuthEntry.IdTag, localAuthEntry.IdTagInfo.ExpiryDate.FormatTimestamp(), localAuthEntry.IdTagInfo.ParentIdTag, localAuthEntry.IdTagInfo.Status, updateType) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - sendLocalListConfirmation := ocpp16.NewSendLocalListConfirmation(status) + sendLocalListConfirmation := localauth.NewSendLocalListConfirmation(status) channel := NewMockWebSocket(wsId) localAuthListListener := MockChargePointLocalAuthListListener{} localAuthListListener.On("OnSendLocalList", mock.Anything).Return(sendLocalListConfirmation, nil) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - suite.chargePoint.SetLocalAuthListListener(localAuthListListener) + suite.chargePoint.SetLocalAuthListHandler(localAuthListListener) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) // Run Test suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.SendLocalList(wsId, func(confirmation *ocpp16.SendLocalListConfirmation, err error) { + err = suite.centralSystem.SendLocalList(wsId, func(confirmation *localauth.SendLocalListConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) if confirmation != nil { @@ -84,8 +85,8 @@ func (suite *OcppV16TestSuite) TestSendLocalListE2EMocked() { } else { resultChannel <- false } - }, listVersion, updateType, func(request *ocpp16.SendLocalListRequest) { - request.LocalAuthorizationList = []ocpp16.AuthorizationData{localAuthEntry} + }, listVersion, updateType, func(request *localauth.SendLocalListRequest) { + request.LocalAuthorizationList = []localauth.AuthorizationData{localAuthEntry} }) assert.Nil(t, err) if err == nil { @@ -97,14 +98,14 @@ func (suite *OcppV16TestSuite) TestSendLocalListE2EMocked() { func (suite *OcppV16TestSuite) TestSendLocalListInvalidEndpoint() { messageId := defaultMessageId listVersion := 1 - updateType := ocpp16.UpdateTypeDifferential - localAuthEntry := ocpp16.AuthorizationData{IdTag: "12345", IdTagInfo: &ocpp16.IdTagInfo{ - ExpiryDate: ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)), + updateType := localauth.UpdateTypeDifferential + localAuthEntry := localauth.AuthorizationData{IdTag: "12345", IdTagInfo: &types.IdTagInfo{ + ExpiryDate: types.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "parentTag", - Status: ocpp16.AuthorizationStatusAccepted, + Status: types.AuthorizationStatusAccepted, }} requestJson := fmt.Sprintf(`[2,"%v","%v",{"listVersion":%v,"localAuthorizationList":[{"idTag":"%v","idTagInfo":{"expiryDate":"%v","parentIdTag":"%v","status":"%v"}}],"updateType":"%v"}]`, - messageId, ocpp16.SendLocalListFeatureName, listVersion, localAuthEntry.IdTag, localAuthEntry.IdTagInfo.ExpiryDate.Format(ocpp16.ISO8601), localAuthEntry.IdTagInfo.ParentIdTag, localAuthEntry.IdTagInfo.Status, updateType) - localListVersionRequest := ocpp16.NewSendLocalListRequest(listVersion, updateType) + messageId, localauth.SendLocalListFeatureName, listVersion, localAuthEntry.IdTag, localAuthEntry.IdTagInfo.ExpiryDate.FormatTimestamp(), localAuthEntry.IdTagInfo.ParentIdTag, localAuthEntry.IdTagInfo.Status, updateType) + localListVersionRequest := localauth.NewSendLocalListRequest(listVersion, updateType) testUnsupportedRequestFromChargePoint(suite, localListVersionRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/set_charging_profile_test.go b/ocpp1.6_test/set_charging_profile_test.go index da7a1822..00301d1e 100644 --- a/ocpp1.6_test/set_charging_profile_test.go +++ b/ocpp1.6_test/set_charging_profile_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -10,14 +11,14 @@ import ( // Test func (suite *OcppV16TestSuite) TestSetChargingProfileRequestValidation() { t := suite.T() - chargingSchedule := ocpp16.NewChargingSchedule(ocpp16.ChargingRateUnitWatts, ocpp16.NewChargingSchedulePeriod(0, 10.0)) - chargingProfile := ocpp16.NewChargingProfile(1, 1, ocpp16.ChargingProfilePurposeChargePointMaxProfile, ocpp16.ChargingProfileKindAbsolute, chargingSchedule) + chargingSchedule := types.NewChargingSchedule(types.ChargingRateUnitWatts, types.NewChargingSchedulePeriod(0, 10.0)) + chargingProfile := types.NewChargingProfile(1, 1, types.ChargingProfilePurposeChargePointMaxProfile, types.ChargingProfileKindAbsolute, chargingSchedule) var requestTable = []GenericTestEntry{ - {ocpp16.SetChargingProfileRequest{ConnectorId: 1, ChargingProfile: chargingProfile}, true}, - {ocpp16.SetChargingProfileRequest{ChargingProfile: chargingProfile}, true}, - {ocpp16.SetChargingProfileRequest{}, false}, - {ocpp16.SetChargingProfileRequest{ConnectorId: 1}, false}, - {ocpp16.SetChargingProfileRequest{ConnectorId: -1, ChargingProfile: chargingProfile}, false}, + {smartcharging.SetChargingProfileRequest{ConnectorId: 1, ChargingProfile: chargingProfile}, true}, + {smartcharging.SetChargingProfileRequest{ChargingProfile: chargingProfile}, true}, + {smartcharging.SetChargingProfileRequest{}, false}, + {smartcharging.SetChargingProfileRequest{ConnectorId: 1}, false}, + {smartcharging.SetChargingProfileRequest{ConnectorId: -1, ChargingProfile: chargingProfile}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -25,9 +26,9 @@ func (suite *OcppV16TestSuite) TestSetChargingProfileRequestValidation() { func (suite *OcppV16TestSuite) TestSetChargingProfileConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.SetChargingProfileConfirmation{Status: ocpp16.ChargingProfileStatusAccepted}, true}, - {ocpp16.SetChargingProfileConfirmation{Status: "invalidChargingProfileStatus"}, false}, - {ocpp16.SetChargingProfileConfirmation{}, false}, + {smartcharging.SetChargingProfileConfirmation{Status: smartcharging.ChargingProfileStatusAccepted}, true}, + {smartcharging.SetChargingProfileConfirmation{Status: "invalidChargingProfileStatus"}, false}, + {smartcharging.SetChargingProfileConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -40,17 +41,17 @@ func (suite *OcppV16TestSuite) TestSetChargingProfileE2EMocked() { connectorId := 1 chargingProfileId := 1 stackLevel := 1 - chargingProfilePurpose := ocpp16.ChargingProfilePurposeChargePointMaxProfile - chargingProfileKind := ocpp16.ChargingProfileKindAbsolute - chargingRateUnit := ocpp16.ChargingRateUnitWatts + chargingProfilePurpose := types.ChargingProfilePurposeChargePointMaxProfile + chargingProfileKind := types.ChargingProfileKindAbsolute + chargingRateUnit := types.ChargingRateUnitWatts startPeriod := 0 limit := 10.0 - status := ocpp16.ChargingProfileStatusAccepted - chargingSchedule := ocpp16.NewChargingSchedule(chargingRateUnit, ocpp16.NewChargingSchedulePeriod(startPeriod, limit)) - chargingProfile := ocpp16.NewChargingProfile(chargingProfileId, stackLevel, chargingProfilePurpose, chargingProfileKind, chargingSchedule) + status := smartcharging.ChargingProfileStatusAccepted + chargingSchedule := types.NewChargingSchedule(chargingRateUnit, types.NewChargingSchedulePeriod(startPeriod, limit)) + chargingProfile := types.NewChargingProfile(chargingProfileId, stackLevel, chargingProfilePurpose, chargingProfileKind, chargingSchedule) requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"chargingProfile":{"chargingProfileId":%v,"stackLevel":%v,"chargingProfilePurpose":"%v","chargingProfileKind":"%v","chargingSchedule":{"chargingRateUnit":"%v","chargingSchedulePeriod":[{"startPeriod":%v,"limit":%v}]}}}]`, messageId, - ocpp16.SetChargingProfileFeatureName, + smartcharging.SetChargingProfileFeatureName, connectorId, chargingProfileId, stackLevel, @@ -60,19 +61,19 @@ func (suite *OcppV16TestSuite) TestSetChargingProfileE2EMocked() { startPeriod, limit) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - SetChargingProfileConfirmation := ocpp16.NewSetChargingProfileConfirmation(status) + SetChargingProfileConfirmation := smartcharging.NewSetChargingProfileConfirmation(status) channel := NewMockWebSocket(wsId) smartChargingListener := MockChargePointSmartChargingListener{} smartChargingListener.On("OnSetChargingProfile", mock.Anything).Return(SetChargingProfileConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp16.SetChargingProfileRequest) + request, ok := args.Get(0).(*smartcharging.SetChargingProfileRequest) assert.True(t, ok) assert.NotNil(t, request) assert.Equal(t, connectorId, request.ConnectorId) assert.Equal(t, chargingProfileId, request.ChargingProfile.ChargingProfileId) assert.Equal(t, chargingProfileKind, request.ChargingProfile.ChargingProfileKind) assert.Equal(t, chargingProfilePurpose, request.ChargingProfile.ChargingProfilePurpose) - assert.Equal(t, ocpp16.RecurrencyKindType(""), request.ChargingProfile.RecurrencyKind) + assert.Equal(t, types.RecurrencyKindType(""), request.ChargingProfile.RecurrencyKind) assert.Equal(t, stackLevel, request.ChargingProfile.StackLevel) assert.Equal(t, 0, request.ChargingProfile.TransactionId) assert.Nil(t, request.ChargingProfile.ValidFrom) @@ -88,13 +89,13 @@ func (suite *OcppV16TestSuite) TestSetChargingProfileE2EMocked() { }) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - suite.chargePoint.SetSmartChargingListener(smartChargingListener) + suite.chargePoint.SetSmartChargingHandler(smartChargingListener) // Run Test suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.SetChargingProfile(wsId, func(confirmation *ocpp16.SetChargingProfileConfirmation, err error) { + err = suite.centralSystem.SetChargingProfile(wsId, func(confirmation *smartcharging.SetChargingProfileConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -110,16 +111,16 @@ func (suite *OcppV16TestSuite) TestSetChargingProfileInvalidEndpoint() { connectorId := 1 chargingProfileId := 1 stackLevel := 1 - chargingProfilePurpose := ocpp16.ChargingProfilePurposeChargePointMaxProfile - chargingProfileKind := ocpp16.ChargingProfileKindAbsolute - chargingRateUnit := ocpp16.ChargingRateUnitWatts + chargingProfilePurpose := types.ChargingProfilePurposeChargePointMaxProfile + chargingProfileKind := types.ChargingProfileKindAbsolute + chargingRateUnit := types.ChargingRateUnitWatts startPeriod := 0 limit := 10.0 - chargingSchedule := ocpp16.NewChargingSchedule(chargingRateUnit, ocpp16.NewChargingSchedulePeriod(startPeriod, limit)) - chargingProfile := ocpp16.NewChargingProfile(chargingProfileId, stackLevel, chargingProfilePurpose, chargingProfileKind, chargingSchedule) + chargingSchedule := types.NewChargingSchedule(chargingRateUnit, types.NewChargingSchedulePeriod(startPeriod, limit)) + chargingProfile := types.NewChargingProfile(chargingProfileId, stackLevel, chargingProfilePurpose, chargingProfileKind, chargingSchedule) requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"chargingProfile":{"chargingProfileId":%v,"stackLevel":%v,"chargingProfilePurpose":"%v","chargingProfileKind":"%v","chargingSchedule":{"chargingRateUnit":"%v","chargingSchedulePeriod":[{"startPeriod":%v,"limit":%v}]}}}]`, messageId, - ocpp16.SetChargingProfileFeatureName, + smartcharging.SetChargingProfileFeatureName, connectorId, chargingProfileId, stackLevel, @@ -128,6 +129,6 @@ func (suite *OcppV16TestSuite) TestSetChargingProfileInvalidEndpoint() { chargingRateUnit, startPeriod, limit) - SetChargingProfileRequest := ocpp16.NewSetChargingProfileRequest(connectorId, chargingProfile) + SetChargingProfileRequest := smartcharging.NewSetChargingProfileRequest(connectorId, chargingProfile) testUnsupportedRequestFromChargePoint(suite, SetChargingProfileRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/start_transaction_test.go b/ocpp1.6_test/start_transaction_test.go index 3f1a72c4..ba41a669 100644 --- a/ocpp1.6_test/start_transaction_test.go +++ b/ocpp1.6_test/start_transaction_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -12,15 +13,15 @@ import ( func (suite *OcppV16TestSuite) TestStartTransactionRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.StartTransactionRequest{ConnectorId: 1, IdTag: "12345", MeterStart: 100, ReservationId: 42, Timestamp: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.StartTransactionRequest{ConnectorId: 1, IdTag: "12345", MeterStart: 100, Timestamp: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.StartTransactionRequest{ConnectorId: 1, IdTag: "12345", Timestamp: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.StartTransactionRequest{ConnectorId: 0, IdTag: "12345", MeterStart: 100, Timestamp: ocpp16.NewDateTime(time.Now())}, false}, - {ocpp16.StartTransactionRequest{ConnectorId: -1, IdTag: "12345", MeterStart: 100, Timestamp: ocpp16.NewDateTime(time.Now())}, false}, - {ocpp16.StartTransactionRequest{ConnectorId: 1, IdTag: ">20..................", MeterStart: 100, Timestamp: ocpp16.NewDateTime(time.Now())}, false}, - {ocpp16.StartTransactionRequest{IdTag: "12345", MeterStart: 100, Timestamp: ocpp16.NewDateTime(time.Now())}, false}, - {ocpp16.StartTransactionRequest{ConnectorId: 1, MeterStart: 100, Timestamp: ocpp16.NewDateTime(time.Now())}, false}, - {ocpp16.StartTransactionRequest{ConnectorId: 1, IdTag: "12345", MeterStart: 100}, false}, + {core.StartTransactionRequest{ConnectorId: 1, IdTag: "12345", MeterStart: 100, ReservationId: 42, Timestamp: types.NewDateTime(time.Now())}, true}, + {core.StartTransactionRequest{ConnectorId: 1, IdTag: "12345", MeterStart: 100, Timestamp: types.NewDateTime(time.Now())}, true}, + {core.StartTransactionRequest{ConnectorId: 1, IdTag: "12345", Timestamp: types.NewDateTime(time.Now())}, true}, + {core.StartTransactionRequest{ConnectorId: 0, IdTag: "12345", MeterStart: 100, Timestamp: types.NewDateTime(time.Now())}, false}, + {core.StartTransactionRequest{ConnectorId: -1, IdTag: "12345", MeterStart: 100, Timestamp: types.NewDateTime(time.Now())}, false}, + {core.StartTransactionRequest{ConnectorId: 1, IdTag: ">20..................", MeterStart: 100, Timestamp: types.NewDateTime(time.Now())}, false}, + {core.StartTransactionRequest{IdTag: "12345", MeterStart: 100, Timestamp: types.NewDateTime(time.Now())}, false}, + {core.StartTransactionRequest{ConnectorId: 1, MeterStart: 100, Timestamp: types.NewDateTime(time.Now())}, false}, + {core.StartTransactionRequest{ConnectorId: 1, IdTag: "12345", MeterStart: 100}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -28,10 +29,10 @@ func (suite *OcppV16TestSuite) TestStartTransactionRequestValidation() { func (suite *OcppV16TestSuite) TestStartTransactionConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.StartTransactionConfirmation{IdTagInfo: &ocpp16.IdTagInfo{ExpiryDate: ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "00000", Status: ocpp16.AuthorizationStatusAccepted}, TransactionId: 10}, true}, - {ocpp16.StartTransactionConfirmation{IdTagInfo: &ocpp16.IdTagInfo{ExpiryDate: ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "00000", Status: ocpp16.AuthorizationStatusAccepted}}, true}, - {ocpp16.StartTransactionConfirmation{IdTagInfo: &ocpp16.IdTagInfo{Status: "invalidAuthorizationStatus"}, TransactionId: 10}, false}, - {ocpp16.StartTransactionConfirmation{TransactionId: 10}, false}, + {core.StartTransactionConfirmation{IdTagInfo: &types.IdTagInfo{ExpiryDate: types.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "00000", Status: types.AuthorizationStatusAccepted}, TransactionId: 10}, true}, + {core.StartTransactionConfirmation{IdTagInfo: &types.IdTagInfo{ExpiryDate: types.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "00000", Status: types.AuthorizationStatusAccepted}}, true}, + {core.StartTransactionConfirmation{IdTagInfo: &types.IdTagInfo{Status: "invalidAuthorizationStatus"}, TransactionId: 10}, false}, + {core.StartTransactionConfirmation{TransactionId: 10}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -45,21 +46,21 @@ func (suite *OcppV16TestSuite) TestStartTransactionE2EMocked() { meterStart := 100 reservationId := 42 connectorId := 1 - timestamp := ocpp16.NewDateTime(time.Now()) + timestamp := types.NewDateTime(time.Now()) parentIdTag := "parentTag1" - status := ocpp16.AuthorizationStatusAccepted - expiryDate := ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)) + status := types.AuthorizationStatusAccepted + expiryDate := types.NewDateTime(time.Now().Add(time.Hour * 8)) transactionId := 16 - requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"idTag":"%v","meterStart":%v,"reservationId":%v,"timestamp":"%v"}]`, messageId, ocpp16.StartTransactionFeatureName, connectorId, idTag, meterStart, reservationId, timestamp.Format(ocpp16.ISO8601)) - responseJson := fmt.Sprintf(`[3,"%v",{"idTagInfo":{"expiryDate":"%v","parentIdTag":"%v","status":"%v"},"transactionId":%v}]`, messageId, expiryDate.Time.Format(ocpp16.ISO8601), parentIdTag, status, transactionId) - startTransactionConfirmation := ocpp16.NewStartTransactionConfirmation(&ocpp16.IdTagInfo{ExpiryDate: expiryDate, ParentIdTag: parentIdTag, Status: status}, transactionId) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"idTag":"%v","meterStart":%v,"reservationId":%v,"timestamp":"%v"}]`, messageId, core.StartTransactionFeatureName, connectorId, idTag, meterStart, reservationId, timestamp.FormatTimestamp()) + responseJson := fmt.Sprintf(`[3,"%v",{"idTagInfo":{"expiryDate":"%v","parentIdTag":"%v","status":"%v"},"transactionId":%v}]`, messageId, expiryDate.FormatTimestamp(), parentIdTag, status, transactionId) + startTransactionConfirmation := core.NewStartTransactionConfirmation(&types.IdTagInfo{ExpiryDate: expiryDate, ParentIdTag: parentIdTag, Status: status}, transactionId) requestRaw := []byte(requestJson) responseRaw := []byte(responseJson) channel := NewMockWebSocket(wsId) coreListener := MockCentralSystemCoreListener{} coreListener.On("OnStartTransaction", mock.AnythingOfType("string"), mock.Anything).Return(startTransactionConfirmation, nil).Run(func(args mock.Arguments) { - request := args.Get(1).(*ocpp16.StartTransactionRequest) + request := args.Get(1).(*core.StartTransactionRequest) assert.Equal(t, connectorId, request.ConnectorId) assert.Equal(t, idTag, request.IdTag) assert.Equal(t, meterStart, request.MeterStart) @@ -72,7 +73,7 @@ func (suite *OcppV16TestSuite) TestStartTransactionE2EMocked() { suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) - confirmation, err := suite.chargePoint.StartTransaction(connectorId, idTag, meterStart, timestamp, func(request *ocpp16.StartTransactionRequest) { + confirmation, err := suite.chargePoint.StartTransaction(connectorId, idTag, meterStart, timestamp, func(request *core.StartTransactionRequest) { request.ReservationId = reservationId }) assert.Nil(t, err) @@ -88,8 +89,8 @@ func (suite *OcppV16TestSuite) TestStartTransactionInvalidEndpoint() { meterStart := 100 reservationId := 42 connectorId := 1 - timestamp := ocpp16.NewDateTime(time.Now()) - authorizeRequest := ocpp16.NewStartTransactionRequest(connectorId, idTag, meterStart, timestamp) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"idTag":"%v","meterStart":%v,"reservationId":%v,"timestamp":"%v"}]`, messageId, ocpp16.StartTransactionFeatureName, connectorId, idTag, meterStart, reservationId, timestamp.Format(ocpp16.ISO8601)) + timestamp := types.NewDateTime(time.Now()) + authorizeRequest := core.NewStartTransactionRequest(connectorId, idTag, meterStart, timestamp) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"idTag":"%v","meterStart":%v,"reservationId":%v,"timestamp":"%v"}]`, messageId, core.StartTransactionFeatureName, connectorId, idTag, meterStart, reservationId, timestamp.FormatTimestamp()) testUnsupportedRequestFromCentralSystem(suite, authorizeRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/status_notification_test.go b/ocpp1.6_test/status_notification_test.go index 1a1395e2..2588cdbe 100644 --- a/ocpp1.6_test/status_notification_test.go +++ b/ocpp1.6_test/status_notification_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -12,17 +13,17 @@ import ( func (suite *OcppV16TestSuite) TestStatusNotificationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.StatusNotificationRequest{ConnectorId: 0, ErrorCode: ocpp16.NoError, Info: "mockInfo", Status: ocpp16.ChargePointStatusAvailable, Timestamp: ocpp16.DateTime{Time: time.Now().Add(-1 * time.Hour)}, VendorId: "mockId", VendorErrorCode: "mockErrorCode"}, true}, - {ocpp16.StatusNotificationRequest{ConnectorId: 0, ErrorCode: ocpp16.NoError, Status: ocpp16.ChargePointStatusAvailable}, true}, - {ocpp16.StatusNotificationRequest{ErrorCode: ocpp16.NoError, Status: ocpp16.ChargePointStatusAvailable}, true}, - {ocpp16.StatusNotificationRequest{ConnectorId: -1, ErrorCode: ocpp16.NoError, Status: ocpp16.ChargePointStatusAvailable}, false}, - {ocpp16.StatusNotificationRequest{ConnectorId: 0, Status: ocpp16.ChargePointStatusAvailable}, false}, - {ocpp16.StatusNotificationRequest{ConnectorId: 0, ErrorCode: ocpp16.NoError}, false}, - {ocpp16.StatusNotificationRequest{ConnectorId: 0, ErrorCode: "invalidErrorCode", Status: ocpp16.ChargePointStatusAvailable}, false}, - {ocpp16.StatusNotificationRequest{ConnectorId: 0, ErrorCode: ocpp16.NoError, Status: "invalidChargePointStatus"}, false}, - {ocpp16.StatusNotificationRequest{ConnectorId: 0, ErrorCode: ocpp16.NoError, Info: ">50................................................", Status: ocpp16.ChargePointStatusAvailable}, false}, - {ocpp16.StatusNotificationRequest{ConnectorId: 0, ErrorCode: ocpp16.NoError, VendorErrorCode: ">50................................................", Status: ocpp16.ChargePointStatusAvailable}, false}, - {ocpp16.StatusNotificationRequest{ConnectorId: 0, ErrorCode: ocpp16.NoError, VendorId: ">255............................................................................................................................................................................................................................................................", Status: ocpp16.ChargePointStatusAvailable}, false}, + {core.StatusNotificationRequest{ConnectorId: 0, ErrorCode: core.NoError, Info: "mockInfo", Status: core.ChargePointStatusAvailable, Timestamp: types.DateTime{Time: time.Now().Add(-1 * time.Hour)}, VendorId: "mockId", VendorErrorCode: "mockErrorCode"}, true}, + {core.StatusNotificationRequest{ConnectorId: 0, ErrorCode: core.NoError, Status: core.ChargePointStatusAvailable}, true}, + {core.StatusNotificationRequest{ErrorCode: core.NoError, Status: core.ChargePointStatusAvailable}, true}, + {core.StatusNotificationRequest{ConnectorId: -1, ErrorCode: core.NoError, Status: core.ChargePointStatusAvailable}, false}, + {core.StatusNotificationRequest{ConnectorId: 0, Status: core.ChargePointStatusAvailable}, false}, + {core.StatusNotificationRequest{ConnectorId: 0, ErrorCode: core.NoError}, false}, + {core.StatusNotificationRequest{ConnectorId: 0, ErrorCode: "invalidErrorCode", Status: core.ChargePointStatusAvailable}, false}, + {core.StatusNotificationRequest{ConnectorId: 0, ErrorCode: core.NoError, Status: "invalidChargePointStatus"}, false}, + {core.StatusNotificationRequest{ConnectorId: 0, ErrorCode: core.NoError, Info: ">50................................................", Status: core.ChargePointStatusAvailable}, false}, + {core.StatusNotificationRequest{ConnectorId: 0, ErrorCode: core.NoError, VendorErrorCode: ">50................................................", Status: core.ChargePointStatusAvailable}, false}, + {core.StatusNotificationRequest{ConnectorId: 0, ErrorCode: core.NoError, VendorId: ">255............................................................................................................................................................................................................................................................", Status: core.ChargePointStatusAvailable}, false}, //{ocpp16.StatusNotificationRequest{ConnectorId: 0, ErrorCode: ocpp16.NoError, Info: "mockInfo", Status: ocpp16.ChargePointStatusAvailable, Timestamp: ocpp16.DateTime{Time: time.Now().Add(1 * time.Hour)}, VendorId: "mockId", VendorErrorCode: "mockErrorCode"}, false}, } ExecuteGenericTestTable(t, requestTable) @@ -31,7 +32,7 @@ func (suite *OcppV16TestSuite) TestStatusNotificationRequestValidation() { func (suite *OcppV16TestSuite) TestStatusNotificationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.StatusNotificationConfirmation{}, true}, + {core.StatusNotificationConfirmation{}, true}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -42,20 +43,20 @@ func (suite *OcppV16TestSuite) TestStatusNotificationE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" connectorId := 1 - timestamp := ocpp16.DateTime{Time: time.Now().Add(-1 * time.Hour)} - status := ocpp16.ChargePointStatusAvailable - cpErrorCode := ocpp16.NoError + timestamp := types.DateTime{Time: time.Now().Add(-1 * time.Hour)} + status := core.ChargePointStatusAvailable + cpErrorCode := core.NoError info := "mockInfo" vendorId := "mockVendorId" vendorErrorCode := "mockErrorCode" - requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"errorCode":"%v","info":"%v","status":"%v","timestamp":"%v","vendorId":"%v","vendorErrorCode":"%v"}]`, messageId, ocpp16.StatusNotificationFeatureName, connectorId, cpErrorCode, info, status, timestamp.Format(ocpp16.ISO8601), vendorId, vendorErrorCode) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"errorCode":"%v","info":"%v","status":"%v","timestamp":"%v","vendorId":"%v","vendorErrorCode":"%v"}]`, messageId, core.StatusNotificationFeatureName, connectorId, cpErrorCode, info, status, timestamp.FormatTimestamp(), vendorId, vendorErrorCode) responseJson := fmt.Sprintf(`[3,"%v",{}]`, messageId) - statusNotificationConfirmation := ocpp16.NewStatusNotificationConfirmation() + statusNotificationConfirmation := core.NewStatusNotificationConfirmation() channel := NewMockWebSocket(wsId) coreListener := MockCentralSystemCoreListener{} coreListener.On("OnStatusNotification", mock.AnythingOfType("string"), mock.Anything).Return(statusNotificationConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(1).(*ocpp16.StatusNotificationRequest) + request, ok := args.Get(1).(*core.StatusNotificationRequest) assert.True(t, ok) assert.Equal(t, connectorId, request.ConnectorId) assert.Equal(t, cpErrorCode, request.ErrorCode) @@ -71,7 +72,7 @@ func (suite *OcppV16TestSuite) TestStatusNotificationE2EMocked() { suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) - confirmation, err := suite.chargePoint.StatusNotification(connectorId, cpErrorCode, status, func(request *ocpp16.StatusNotificationRequest) { + confirmation, err := suite.chargePoint.StatusNotification(connectorId, cpErrorCode, status, func(request *core.StatusNotificationRequest) { request.Timestamp = timestamp request.Info = info request.VendorId = vendorId @@ -84,17 +85,17 @@ func (suite *OcppV16TestSuite) TestStatusNotificationE2EMocked() { func (suite *OcppV16TestSuite) TestStatusNotificationInvalidEndpoint() { messageId := defaultMessageId connectorId := 1 - timestamp := ocpp16.DateTime{Time: time.Now().Add(-1 * time.Hour)} - status := ocpp16.ChargePointStatusAvailable - cpErrorCode := ocpp16.NoError + timestamp := types.DateTime{Time: time.Now().Add(-1 * time.Hour)} + status := core.ChargePointStatusAvailable + cpErrorCode := core.NoError info := "mockInfo" vendorId := "mockVendorId" vendorErrorCode := "mockErrorCode" - statusNotificationRequest := ocpp16.NewStatusNotificationRequest(connectorId, cpErrorCode, status) + statusNotificationRequest := core.NewStatusNotificationRequest(connectorId, cpErrorCode, status) statusNotificationRequest.Info = info statusNotificationRequest.Timestamp = timestamp statusNotificationRequest.VendorId = vendorId statusNotificationRequest.VendorErrorCode = vendorErrorCode - requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"errorCode":"%v","info":"%v","status":"%v","timestamp":"%v","vendorId":"%v","vendorErrorCode":"%v"}]`, messageId, ocpp16.StatusNotificationFeatureName, connectorId, cpErrorCode, info, status, timestamp.Format(ocpp16.ISO8601), vendorId, vendorErrorCode) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v,"errorCode":"%v","info":"%v","status":"%v","timestamp":"%v","vendorId":"%v","vendorErrorCode":"%v"}]`, messageId, core.StatusNotificationFeatureName, connectorId, cpErrorCode, info, status, timestamp.FormatTimestamp(), vendorId, vendorErrorCode) testUnsupportedRequestFromCentralSystem(suite, statusNotificationRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/stop_transaction_test.go b/ocpp1.6_test/stop_transaction_test.go index b28caaff..167d97b8 100644 --- a/ocpp1.6_test/stop_transaction_test.go +++ b/ocpp1.6_test/stop_transaction_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -11,20 +12,20 @@ import ( // Test func (suite *OcppV16TestSuite) TestStopTransactionRequestValidation() { t := suite.T() - transactionData := []ocpp16.MeterValue{{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{{Value: "value"}}}} + transactionData := []types.MeterValue{{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: "value"}}}} var requestTable = []GenericTestEntry{ - {ocpp16.StopTransactionRequest{IdTag: "12345", MeterStop: 100, Timestamp: ocpp16.NewDateTime(time.Now()), TransactionId: 1, Reason: ocpp16.ReasonEVDisconnected, TransactionData: transactionData}, true}, - {ocpp16.StopTransactionRequest{IdTag: "12345", MeterStop: 100, Timestamp: ocpp16.NewDateTime(time.Now()), TransactionId: 1, Reason: ocpp16.ReasonEVDisconnected, TransactionData: []ocpp16.MeterValue{}}, true}, - {ocpp16.StopTransactionRequest{IdTag: "12345", MeterStop: 100, Timestamp: ocpp16.NewDateTime(time.Now()), TransactionId: 1, Reason: ocpp16.ReasonEVDisconnected}, true}, - {ocpp16.StopTransactionRequest{IdTag: "12345", MeterStop: 100, Timestamp: ocpp16.NewDateTime(time.Now()), TransactionId: 1}, true}, - {ocpp16.StopTransactionRequest{MeterStop: 100, Timestamp: ocpp16.NewDateTime(time.Now()), TransactionId: 1}, true}, - {ocpp16.StopTransactionRequest{MeterStop: 100, Timestamp: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.StopTransactionRequest{Timestamp: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.StopTransactionRequest{MeterStop: 100}, false}, - {ocpp16.StopTransactionRequest{IdTag: "12345", MeterStop: 100, Timestamp: ocpp16.NewDateTime(time.Now()), TransactionId: 1, Reason: "invalidReason"}, false}, - {ocpp16.StopTransactionRequest{IdTag: ">20..................", MeterStop: 100, Timestamp: ocpp16.NewDateTime(time.Now()), TransactionId: 1}, false}, - {ocpp16.StopTransactionRequest{MeterStop: -1, Timestamp: ocpp16.NewDateTime(time.Now()), TransactionId: 1}, false}, - {ocpp16.StopTransactionRequest{MeterStop: 100, Timestamp: ocpp16.NewDateTime(time.Now()), TransactionId: 1, TransactionData: []ocpp16.MeterValue{{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{}}}}, false}, + {core.StopTransactionRequest{IdTag: "12345", MeterStop: 100, Timestamp: types.NewDateTime(time.Now()), TransactionId: 1, Reason: core.ReasonEVDisconnected, TransactionData: transactionData}, true}, + {core.StopTransactionRequest{IdTag: "12345", MeterStop: 100, Timestamp: types.NewDateTime(time.Now()), TransactionId: 1, Reason: core.ReasonEVDisconnected, TransactionData: []types.MeterValue{}}, true}, + {core.StopTransactionRequest{IdTag: "12345", MeterStop: 100, Timestamp: types.NewDateTime(time.Now()), TransactionId: 1, Reason: core.ReasonEVDisconnected}, true}, + {core.StopTransactionRequest{IdTag: "12345", MeterStop: 100, Timestamp: types.NewDateTime(time.Now()), TransactionId: 1}, true}, + {core.StopTransactionRequest{MeterStop: 100, Timestamp: types.NewDateTime(time.Now()), TransactionId: 1}, true}, + {core.StopTransactionRequest{MeterStop: 100, Timestamp: types.NewDateTime(time.Now())}, true}, + {core.StopTransactionRequest{Timestamp: types.NewDateTime(time.Now())}, true}, + {core.StopTransactionRequest{MeterStop: 100}, false}, + {core.StopTransactionRequest{IdTag: "12345", MeterStop: 100, Timestamp: types.NewDateTime(time.Now()), TransactionId: 1, Reason: "invalidReason"}, false}, + {core.StopTransactionRequest{IdTag: ">20..................", MeterStop: 100, Timestamp: types.NewDateTime(time.Now()), TransactionId: 1}, false}, + {core.StopTransactionRequest{MeterStop: -1, Timestamp: types.NewDateTime(time.Now()), TransactionId: 1}, false}, + {core.StopTransactionRequest{MeterStop: 100, Timestamp: types.NewDateTime(time.Now()), TransactionId: 1, TransactionData: []types.MeterValue{{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{}}}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -32,9 +33,9 @@ func (suite *OcppV16TestSuite) TestStopTransactionRequestValidation() { func (suite *OcppV16TestSuite) TestStopTransactionConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.StopTransactionConfirmation{IdTagInfo: &ocpp16.IdTagInfo{ExpiryDate: ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "00000", Status: ocpp16.AuthorizationStatusAccepted}}, true}, - {ocpp16.StopTransactionConfirmation{}, true}, - {ocpp16.StopTransactionConfirmation{IdTagInfo: &ocpp16.IdTagInfo{Status: "invalidAuthorizationStatus"}}, false}, + {core.StopTransactionConfirmation{IdTagInfo: &types.IdTagInfo{ExpiryDate: types.NewDateTime(time.Now().Add(time.Hour * 8)), ParentIdTag: "00000", Status: types.AuthorizationStatusAccepted}}, true}, + {core.StopTransactionConfirmation{}, true}, + {core.StopTransactionConfirmation{IdTagInfo: &types.IdTagInfo{Status: "invalidAuthorizationStatus"}}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -46,25 +47,25 @@ func (suite *OcppV16TestSuite) TestStopTransactionE2EMocked() { wsUrl := "someUrl" idTag := "tag1" mockValue := "value" - mockUnit := ocpp16.UnitOfMeasureKW + mockUnit := types.UnitOfMeasureKW meterStop := 100 transactionId := 42 - timestamp := ocpp16.NewDateTime(time.Now()) - meterValues := []ocpp16.MeterValue{{Timestamp: ocpp16.NewDateTime(time.Now()), SampledValue: []ocpp16.SampledValue{{Value: mockValue, Unit: mockUnit}}}} + timestamp := types.NewDateTime(time.Now()) + meterValues := []types.MeterValue{{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: mockValue, Unit: mockUnit}}}} parentIdTag := "parentTag1" - status := ocpp16.AuthorizationStatusAccepted - expiryDate := ocpp16.NewDateTime(time.Now().Add(time.Hour * 8)) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"idTag":"%v","meterStop":%v,"timestamp":"%v","transactionId":%v,"transactionData":[{"timestamp":"%v","sampledValue":[{"value":"%v","unit":"%v"}]}]}]`, messageId, ocpp16.StopTransactionFeatureName, idTag, meterStop, timestamp.Format(ocpp16.ISO8601), transactionId, timestamp.Format(ocpp16.ISO8601), mockValue, mockUnit) - responseJson := fmt.Sprintf(`[3,"%v",{"idTagInfo":{"expiryDate":"%v","parentIdTag":"%v","status":"%v"}}]`, messageId, expiryDate.Format(ocpp16.ISO8601), parentIdTag, status) - stopTransactionConfirmation := ocpp16.NewStopTransactionConfirmation() - stopTransactionConfirmation.IdTagInfo = &ocpp16.IdTagInfo{ExpiryDate: expiryDate, ParentIdTag: parentIdTag, Status: status} + status := types.AuthorizationStatusAccepted + expiryDate := types.NewDateTime(time.Now().Add(time.Hour * 8)) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"idTag":"%v","meterStop":%v,"timestamp":"%v","transactionId":%v,"transactionData":[{"timestamp":"%v","sampledValue":[{"value":"%v","unit":"%v"}]}]}]`, messageId, core.StopTransactionFeatureName, idTag, meterStop, timestamp.FormatTimestamp(), transactionId, timestamp.FormatTimestamp(), mockValue, mockUnit) + responseJson := fmt.Sprintf(`[3,"%v",{"idTagInfo":{"expiryDate":"%v","parentIdTag":"%v","status":"%v"}}]`, messageId, expiryDate.FormatTimestamp(), parentIdTag, status) + stopTransactionConfirmation := core.NewStopTransactionConfirmation() + stopTransactionConfirmation.IdTagInfo = &types.IdTagInfo{ExpiryDate: expiryDate, ParentIdTag: parentIdTag, Status: status} requestRaw := []byte(requestJson) responseRaw := []byte(responseJson) channel := NewMockWebSocket(wsId) coreListener := MockCentralSystemCoreListener{} coreListener.On("OnStopTransaction", mock.AnythingOfType("string"), mock.Anything).Return(stopTransactionConfirmation, nil).Run(func(args mock.Arguments) { - request := args.Get(1).(*ocpp16.StopTransactionRequest) + request := args.Get(1).(*core.StopTransactionRequest) assert.Equal(t, meterStop, request.MeterStop) assert.Equal(t, transactionId, request.TransactionId) assert.Equal(t, idTag, request.IdTag) @@ -83,7 +84,7 @@ func (suite *OcppV16TestSuite) TestStopTransactionE2EMocked() { suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) - confirmation, err := suite.chargePoint.StopTransaction(meterStop, timestamp, transactionId, func(request *ocpp16.StopTransactionRequest) { + confirmation, err := suite.chargePoint.StopTransaction(meterStop, timestamp, transactionId, func(request *core.StopTransactionRequest) { request.IdTag = idTag request.TransactionData = meterValues }) @@ -98,12 +99,12 @@ func (suite *OcppV16TestSuite) TestStopTransactionInvalidEndpoint() { messageId := defaultMessageId idTag := "tag1" mockValue := "value" - mockUnit := ocpp16.UnitOfMeasureKW + mockUnit := types.UnitOfMeasureKW meterStop := 100 transactionId := 42 - timestamp := ocpp16.NewDateTime(time.Now()) - stopTransactionRequest := ocpp16.NewStopTransactionRequest(meterStop, timestamp, transactionId) + timestamp := types.NewDateTime(time.Now()) + stopTransactionRequest := core.NewStopTransactionRequest(meterStop, timestamp, transactionId) stopTransactionRequest.IdTag = idTag - requestJson := fmt.Sprintf(`[2,"%v","%v",{"idTag":"%v","meterStop":%v,"timestamp":"%v","transactionId":%v,"transactionData":[{"timestamp":"%v","sampledValue":[{"value":"%v","unit":"%v"}]}]}]`, messageId, ocpp16.StopTransactionFeatureName, idTag, meterStop, timestamp.Format(ocpp16.ISO8601), transactionId, timestamp.Format(ocpp16.ISO8601), mockValue, mockUnit) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"idTag":"%v","meterStop":%v,"timestamp":"%v","transactionId":%v,"transactionData":[{"timestamp":"%v","sampledValue":[{"value":"%v","unit":"%v"}]}]}]`, messageId, core.StopTransactionFeatureName, idTag, meterStop, timestamp.FormatTimestamp(), transactionId, timestamp.FormatTimestamp(), mockValue, mockUnit) testUnsupportedRequestFromCentralSystem(suite, stopTransactionRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/trigger_message_test.go b/ocpp1.6_test/trigger_message_test.go index cca3e2e4..2e057173 100644 --- a/ocpp1.6_test/trigger_message_test.go +++ b/ocpp1.6_test/trigger_message_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/remotetrigger" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -11,11 +12,11 @@ import ( func (suite *OcppV16TestSuite) TestTriggerMessageRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.TriggerMessageRequest{RequestedMessage: ocpp16.StatusNotificationFeatureName, ConnectorId: 1}, true}, - {ocpp16.TriggerMessageRequest{RequestedMessage: ocpp16.StatusNotificationFeatureName}, true}, - {ocpp16.TriggerMessageRequest{}, false}, - {ocpp16.TriggerMessageRequest{RequestedMessage: ocpp16.StatusNotificationFeatureName, ConnectorId: -1}, false}, - {ocpp16.TriggerMessageRequest{RequestedMessage: ocpp16.StartTransactionFeatureName}, false}, + {remotetrigger.TriggerMessageRequest{RequestedMessage: core.StatusNotificationFeatureName, ConnectorId: 1}, true}, + {remotetrigger.TriggerMessageRequest{RequestedMessage: core.StatusNotificationFeatureName}, true}, + {remotetrigger.TriggerMessageRequest{}, false}, + {remotetrigger.TriggerMessageRequest{RequestedMessage: core.StatusNotificationFeatureName, ConnectorId: -1}, false}, + {remotetrigger.TriggerMessageRequest{RequestedMessage: core.StartTransactionFeatureName}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -23,9 +24,9 @@ func (suite *OcppV16TestSuite) TestTriggerMessageRequestValidation() { func (suite *OcppV16TestSuite) TestTriggerMessageConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.TriggerMessageConfirmation{Status: ocpp16.TriggerMessageStatusAccepted}, true}, - {ocpp16.TriggerMessageConfirmation{Status: "invalidTriggerMessageStatus"}, false}, - {ocpp16.TriggerMessageConfirmation{}, false}, + {remotetrigger.TriggerMessageConfirmation{Status: remotetrigger.TriggerMessageStatusAccepted}, true}, + {remotetrigger.TriggerMessageConfirmation{Status: "invalidTriggerMessageStatus"}, false}, + {remotetrigger.TriggerMessageConfirmation{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -36,34 +37,34 @@ func (suite *OcppV16TestSuite) TestTriggerMessageE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" connectorId := 1 - requestedMessage := ocpp16.MessageTrigger(ocpp16.StatusNotificationFeatureName) - status := ocpp16.TriggerMessageStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestedMessage":"%v","connectorId":%v}]`, messageId, ocpp16.TriggerMessageFeatureName, requestedMessage, connectorId) + requestedMessage := remotetrigger.MessageTrigger(core.StatusNotificationFeatureName) + status := remotetrigger.TriggerMessageStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestedMessage":"%v","connectorId":%v}]`, messageId, remotetrigger.TriggerMessageFeatureName, requestedMessage, connectorId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - TriggerMessageConfirmation := ocpp16.NewTriggerMessageConfirmation(status) + TriggerMessageConfirmation := remotetrigger.NewTriggerMessageConfirmation(status) channel := NewMockWebSocket(wsId) remoteTriggerListener := MockChargePointRemoteTriggerListener{} remoteTriggerListener.On("OnTriggerMessage", mock.Anything).Return(TriggerMessageConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp16.TriggerMessageRequest) + request, ok := args.Get(0).(*remotetrigger.TriggerMessageRequest) assert.True(t, ok) assert.Equal(t, requestedMessage, request.RequestedMessage) assert.Equal(t, connectorId, request.ConnectorId) }) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - suite.chargePoint.SetRemoteTriggerListener(remoteTriggerListener) + suite.chargePoint.SetRemoteTriggerHandler(remoteTriggerListener) // Run Test suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.TriggerMessage(wsId, func(confirmation *ocpp16.TriggerMessageConfirmation, err error) { + err = suite.centralSystem.TriggerMessage(wsId, func(confirmation *remotetrigger.TriggerMessageConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) resultChannel <- true - }, requestedMessage, func(request *ocpp16.TriggerMessageRequest) { + }, requestedMessage, func(request *remotetrigger.TriggerMessageRequest) { request.ConnectorId = connectorId }) assert.Nil(t, err) @@ -74,8 +75,8 @@ func (suite *OcppV16TestSuite) TestTriggerMessageE2EMocked() { func (suite *OcppV16TestSuite) TestTriggerMessageInvalidEndpoint() { messageId := defaultMessageId connectorId := 1 - requestedMessage := ocpp16.MessageTrigger(ocpp16.StatusNotificationFeatureName) - TriggerMessageRequest := ocpp16.NewTriggerMessageRequest(requestedMessage) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestedMessage":"%v","connectorId":%v}]`, messageId, ocpp16.TriggerMessageFeatureName, requestedMessage, connectorId) + requestedMessage := remotetrigger.MessageTrigger(core.StatusNotificationFeatureName) + TriggerMessageRequest := remotetrigger.NewTriggerMessageRequest(requestedMessage) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestedMessage":"%v","connectorId":%v}]`, messageId, remotetrigger.TriggerMessageFeatureName, requestedMessage, connectorId) testUnsupportedRequestFromChargePoint(suite, TriggerMessageRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/unlock_connector_test.go b/ocpp1.6_test/unlock_connector_test.go index 970ecb2a..c9455313 100644 --- a/ocpp1.6_test/unlock_connector_test.go +++ b/ocpp1.6_test/unlock_connector_test.go @@ -2,7 +2,7 @@ package ocpp16_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -10,9 +10,9 @@ import ( func (suite *OcppV16TestSuite) TestUnlockConnectorRequestValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp16.UnlockConnectorRequest{ConnectorId: 1}, true}, - {ocpp16.UnlockConnectorRequest{ConnectorId: -1}, false}, - {ocpp16.UnlockConnectorRequest{}, false}, + {core.UnlockConnectorRequest{ConnectorId: 1}, true}, + {core.UnlockConnectorRequest{ConnectorId: -1}, false}, + {core.UnlockConnectorRequest{}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -20,9 +20,9 @@ func (suite *OcppV16TestSuite) TestUnlockConnectorRequestValidation() { func (suite *OcppV16TestSuite) TestUnlockConnectorConfirmationValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp16.UnlockConnectorConfirmation{Status: ocpp16.UnlockStatusUnlocked}, true}, - {ocpp16.UnlockConnectorConfirmation{Status: "invalidUnlockStatus"}, false}, - {ocpp16.UnlockConnectorConfirmation{}, false}, + {core.UnlockConnectorConfirmation{Status: core.UnlockStatusUnlocked}, true}, + {core.UnlockConnectorConfirmation{Status: "invalidUnlockStatus"}, false}, + {core.UnlockConnectorConfirmation{}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -34,10 +34,10 @@ func (suite *OcppV16TestSuite) TestUnlockConnectorE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" connectorId := 1 - status := ocpp16.UnlockStatusUnlocked - requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v}]`, messageId, ocpp16.UnlockConnectorFeatureName, connectorId) + status := core.UnlockStatusUnlocked + requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v}]`, messageId, core.UnlockConnectorFeatureName, connectorId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - unlockConnectorConfirmation := ocpp16.NewUnlockConnectorConfirmation(status) + unlockConnectorConfirmation := core.NewUnlockConnectorConfirmation(status) channel := NewMockWebSocket(wsId) // Setting handlers coreListener := MockChargePointCoreListener{} @@ -49,7 +49,7 @@ func (suite *OcppV16TestSuite) TestUnlockConnectorE2EMocked() { err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.UnlockConnector(wsId, func(confirmation *ocpp16.UnlockConnectorConfirmation, err error) { + err = suite.centralSystem.UnlockConnector(wsId, func(confirmation *core.UnlockConnectorConfirmation, err error) { assert.NotNil(t, confirmation) assert.Nil(t, err) assert.Equal(t, status, confirmation.Status) @@ -63,7 +63,7 @@ func (suite *OcppV16TestSuite) TestUnlockConnectorE2EMocked() { func (suite *OcppV16TestSuite) TestUnlockConnectorInvalidEndpoint() { messageId := defaultMessageId connectorId := 1 - changeAvailabilityRequest := ocpp16.NewUnlockConnectorRequest(connectorId) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v}]`, messageId, ocpp16.UnlockConnectorFeatureName, connectorId) + changeAvailabilityRequest := core.NewUnlockConnectorRequest(connectorId) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"connectorId":%v}]`, messageId, core.UnlockConnectorFeatureName, connectorId) testUnsupportedRequestFromChargePoint(suite, changeAvailabilityRequest, requestJson, messageId) } diff --git a/ocpp1.6_test/update_firmware_test.go b/ocpp1.6_test/update_firmware_test.go index 29c25753..efe014e7 100644 --- a/ocpp1.6_test/update_firmware_test.go +++ b/ocpp1.6_test/update_firmware_test.go @@ -2,7 +2,8 @@ package ocpp16_test import ( "fmt" - ocpp16 "github.com/lorenzodonini/ocpp-go/ocpp1.6" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "time" @@ -12,14 +13,14 @@ import ( func (suite *OcppV16TestSuite) TestUpdateFirmwareRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp16.UpdateFirmwareRequest{Location: "ftp:some/path", Retries: 10, RetryInterval: 10, RetrieveDate: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.UpdateFirmwareRequest{Location: "ftp:some/path", Retries: 10, RetrieveDate: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.UpdateFirmwareRequest{Location: "ftp:some/path", RetrieveDate: ocpp16.NewDateTime(time.Now())}, true}, - {ocpp16.UpdateFirmwareRequest{}, false}, - {ocpp16.UpdateFirmwareRequest{Location: "ftp:some/path"}, false}, - {ocpp16.UpdateFirmwareRequest{Location: "invalidUri", RetrieveDate: ocpp16.NewDateTime(time.Now())}, false}, - {ocpp16.UpdateFirmwareRequest{Location: "ftp:some/path", Retries: -1, RetrieveDate: ocpp16.NewDateTime(time.Now())}, false}, - {ocpp16.UpdateFirmwareRequest{Location: "ftp:some/path", RetryInterval: -1, RetrieveDate: ocpp16.NewDateTime(time.Now())}, false}, + {firmware.UpdateFirmwareRequest{Location: "ftp:some/path", Retries: 10, RetryInterval: 10, RetrieveDate: types.NewDateTime(time.Now())}, true}, + {firmware.UpdateFirmwareRequest{Location: "ftp:some/path", Retries: 10, RetrieveDate: types.NewDateTime(time.Now())}, true}, + {firmware.UpdateFirmwareRequest{Location: "ftp:some/path", RetrieveDate: types.NewDateTime(time.Now())}, true}, + {firmware.UpdateFirmwareRequest{}, false}, + {firmware.UpdateFirmwareRequest{Location: "ftp:some/path"}, false}, + {firmware.UpdateFirmwareRequest{Location: "invalidUri", RetrieveDate: types.NewDateTime(time.Now())}, false}, + {firmware.UpdateFirmwareRequest{Location: "ftp:some/path", Retries: -1, RetrieveDate: types.NewDateTime(time.Now())}, false}, + {firmware.UpdateFirmwareRequest{Location: "ftp:some/path", RetryInterval: -1, RetrieveDate: types.NewDateTime(time.Now())}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -27,7 +28,7 @@ func (suite *OcppV16TestSuite) TestUpdateFirmwareRequestValidation() { func (suite *OcppV16TestSuite) TestUpdateFirmwareConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp16.UpdateFirmwareConfirmation{}, true}, + {firmware.UpdateFirmwareConfirmation{}, true}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -40,24 +41,24 @@ func (suite *OcppV16TestSuite) TestUpdateFirmwareE2EMocked() { location := "ftp:some/path" retries := 10 retryInterval := 600 - retrieveDate := ocpp16.NewDateTime(time.Now()) + retrieveDate := types.NewDateTime(time.Now()) requestJson := fmt.Sprintf(`[2,"%v","%v",{"location":"%v","retries":%v,"retrieveDate":"%v","retryInterval":%v}]`, - messageId, ocpp16.UpdateFirmwareFeatureName, location, retries, retrieveDate.Format(ocpp16.ISO8601), retryInterval) + messageId, firmware.UpdateFirmwareFeatureName, location, retries, retrieveDate.FormatTimestamp(), retryInterval) responseJson := fmt.Sprintf(`[3,"%v",{}]`, messageId) - updateFirmwareConfirmation := ocpp16.NewUpdateFirmwareConfirmation() + updateFirmwareConfirmation := firmware.NewUpdateFirmwareConfirmation() channel := NewMockWebSocket(wsId) firmwareListener := MockChargePointFirmwareManagementListener{} firmwareListener.On("OnUpdateFirmware", mock.Anything).Return(updateFirmwareConfirmation, nil) setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - suite.chargePoint.SetFirmwareManagementListener(firmwareListener) + suite.chargePoint.SetFirmwareManagementHandler(firmwareListener) setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) // Run Test suite.centralSystem.Start(8887, "somePath") err := suite.chargePoint.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.centralSystem.UpdateFirmware(wsId, func(confirmation *ocpp16.UpdateFirmwareConfirmation, err error) { + err = suite.centralSystem.UpdateFirmware(wsId, func(confirmation *firmware.UpdateFirmwareConfirmation, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) if confirmation == nil || err != nil { @@ -65,7 +66,7 @@ func (suite *OcppV16TestSuite) TestUpdateFirmwareE2EMocked() { } else { resultChannel <- true } - }, location, retrieveDate, func(request *ocpp16.UpdateFirmwareRequest) { + }, location, retrieveDate, func(request *firmware.UpdateFirmwareRequest) { request.RetryInterval = retryInterval request.Retries = retries }) @@ -81,9 +82,9 @@ func (suite *OcppV16TestSuite) TestUpdateFirmwareInvalidEndpoint() { location := "ftp:some/path" retries := 10 retryInterval := 600 - retrieveDate := ocpp16.NewDateTime(time.Now()) - localListVersionRequest := ocpp16.NewUpdateFirmwareRequest(location, retrieveDate) + retrieveDate := types.NewDateTime(time.Now()) + localListVersionRequest := firmware.NewUpdateFirmwareRequest(location, retrieveDate) requestJson := fmt.Sprintf(`[2,"%v","%v",{"location":"%v","retries":%v,"retrieveDate":"%v","retryInterval":%v}]`, - messageId, ocpp16.UpdateFirmwareFeatureName, location, retries, retrieveDate.Format(ocpp16.ISO8601), retryInterval) + messageId, firmware.UpdateFirmwareFeatureName, location, retries, retrieveDate.FormatTimestamp(), retryInterval) testUnsupportedRequestFromChargePoint(suite, localListVersionRequest, requestJson, messageId) } diff --git a/ocpp2.0/authorization/authorization.go b/ocpp2.0/authorization/authorization.go new file mode 100644 index 00000000..0872d724 --- /dev/null +++ b/ocpp2.0/authorization/authorization.go @@ -0,0 +1,24 @@ +// The authorization functional block contains OCPP 2.0 authorization-related features. It contains different ways of authorizing a user, online and/or offline . +package authorization + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Authorization profile. +type CSMSHandler interface { + // OnAuthorize is called on the CSMS whenever an AuthorizeRequest is received from a charging station. + OnAuthorize(chargingStationID string, request *AuthorizeRequest) (confirmation *AuthorizeResponse, err error) +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Authorization profile. +type ChargingStationHandler interface { + // OnClearCache is called on a charging station whenever a ClearCacheRequest is received from the CSMS. + OnClearCache(request *ClearCacheRequest) (confirmation *ClearCacheResponse, err error) +} + +const ProfileName = "authorization" + +var Profile = ocpp.NewProfile( + ProfileName, + AuthorizeFeature{}, + ClearCacheFeature{}, + ) diff --git a/ocpp2.0/authorize.go b/ocpp2.0/authorization/authorize.go similarity index 50% rename from ocpp2.0/authorize.go rename to ocpp2.0/authorization/authorize.go index 69295f44..1879c453 100644 --- a/ocpp2.0/authorize.go +++ b/ocpp2.0/authorization/authorize.go @@ -1,28 +1,31 @@ -package ocpp2 +package authorization import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "reflect" ) // -------------------- Authorize (CS -> CSMS) -------------------- +const AuthorizeFeatureName = "Authorize" + // The field definition of the Authorize request payload sent by the Charging Station to the CSMS. type AuthorizeRequest struct { - EvseID []int `json:"evseId,omitempty"` - IdToken IdToken `json:"idToken" validate:"required"` - CertificateHashData []OCSPRequestDataType `json:"15118CertificateHashData,omitempty" validate:"max=4"` + EvseID []int `json:"evseId,omitempty"` + IdToken types.IdToken `json:"idToken" validate:"required"` + CertificateHashData []types.OCSPRequestDataType `json:"15118CertificateHashData,omitempty" validate:"max=4"` } -// This field definition of the Authorize confirmation payload, sent by the Charging Station to the CSMS in response to an AuthorizeRequest. +// This field definition of the Authorize response payload, sent by the Charging Station to the CSMS in response to an AuthorizeRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type AuthorizeConfirmation struct { - CertificateStatus CertificateStatus `json:"certificateStatus,omitempty" validate:"omitempty,certificateStatus"` - EvseID []int `json:"evseId,omitempty"` - IdTokenInfo IdTokenInfo `json:"idTokenInfo" validate:"required"` +type AuthorizeResponse struct { + CertificateStatus types.CertificateStatus `json:"certificateStatus,omitempty" validate:"omitempty,certificateStatus"` + EvseID []int `json:"evseId,omitempty"` + IdTokenInfo types.IdTokenInfo `json:"idTokenInfo" validate:"required"` } // Before the owner of an electric vehicle can start or stop charging, the Charging Station has to authorize the operation. -// Upon receipt of an AuthorizeRequest, the CSMS SHALL respond with an AuthorizeConfirmation. +// Upon receipt of an AuthorizeRequest, the CSMS SHALL respond with an AuthorizeResponse. // This response payload SHALL indicate whether or not the idTag is accepted by the CSMS. // If the CSMS accepts the idToken then the response payload MUST include an authorization status value indicating acceptance or a reason for rejection. // @@ -39,24 +42,24 @@ func (f AuthorizeFeature) GetRequestType() reflect.Type { return reflect.TypeOf(AuthorizeRequest{}) } -func (f AuthorizeFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(AuthorizeConfirmation{}) +func (f AuthorizeFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(AuthorizeResponse{}) } func (r AuthorizeRequest) GetFeatureName() string { return AuthorizeFeatureName } -func (c AuthorizeConfirmation) GetFeatureName() string { +func (c AuthorizeResponse) GetFeatureName() string { return AuthorizeFeatureName } // Creates a new AuthorizeRequest, containing all required fields. There are no optional fields for this message. -func NewAuthorizationRequest(idToken string, tokenType IdTokenType) *AuthorizeRequest { - return &AuthorizeRequest{IdToken: IdToken{IdToken: idToken, Type: tokenType}} +func NewAuthorizationRequest(idToken string, tokenType types.IdTokenType) *AuthorizeRequest { + return &AuthorizeRequest{IdToken: types.IdToken{IdToken: idToken, Type: tokenType}} } -// Creates a new AuthorizeConfirmation. There are no optional fields for this message. -func NewAuthorizationConfirmation(idTokenInfo IdTokenInfo) *AuthorizeConfirmation { - return &AuthorizeConfirmation{IdTokenInfo: idTokenInfo} +// Creates a new AuthorizeResponse. There are no optional fields for this message. +func NewAuthorizationResponse(idTokenInfo types.IdTokenInfo) *AuthorizeResponse { + return &AuthorizeResponse{IdTokenInfo: idTokenInfo} } diff --git a/ocpp2.0/clear_cache.go b/ocpp2.0/authorization/clear_cache.go similarity index 68% rename from ocpp2.0/clear_cache.go rename to ocpp2.0/authorization/clear_cache.go index e3110a5b..dad86b17 100644 --- a/ocpp2.0/clear_cache.go +++ b/ocpp2.0/authorization/clear_cache.go @@ -1,12 +1,15 @@ -package ocpp2 +package authorization import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Clear Cache (CSMS -> CS) -------------------- +const ClearCacheFeatureName = "ClearCache" + // Status returned in response to ClearCacheRequest. type ClearCacheStatus string @@ -29,15 +32,15 @@ func isValidClearCacheStatus(fl validator.FieldLevel) bool { type ClearCacheRequest struct { } -// This field definition of the ClearCache confirmation payload, sent by the Charging Station to the CSMS in response to a ClearCacheRequest. +// This field definition of the ClearCache response payload, sent by the Charging Station to the CSMS in response to a ClearCacheRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type ClearCacheConfirmation struct { +type ClearCacheResponse struct { Status ClearCacheStatus `json:"status" validate:"required,cacheStatus"` } // CSMS can request a Charging Station to clear its Authorization Cache. // The CSMS SHALL send a ClearCacheRequest payload for clearing the Charging Station’s Authorization Cache. -// Upon receipt of a ClearCacheRequest, the Charging Station SHALL respond with a ClearCacheConfirmation payload. +// Upon receipt of a ClearCacheRequest, the Charging Station SHALL respond with a ClearCacheResponse payload. // The response payload SHALL indicate whether the Charging Station was able to clear its Authorization Cache. type ClearCacheFeature struct{} @@ -49,15 +52,15 @@ func (f ClearCacheFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ClearCacheRequest{}) } -func (f ClearCacheFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(ClearCacheConfirmation{}) +func (f ClearCacheFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(ClearCacheResponse{}) } func (r ClearCacheRequest) GetFeatureName() string { return ClearCacheFeatureName } -func (c ClearCacheConfirmation) GetFeatureName() string { +func (c ClearCacheResponse) GetFeatureName() string { return ClearCacheFeatureName } @@ -66,11 +69,11 @@ func NewClearCacheRequest() *ClearCacheRequest { return &ClearCacheRequest{} } -// Creates a new ClearCacheConfirmation, containing all required fields. There are no optional fields for this message. -func NewClearCacheConfirmation(status ClearCacheStatus) *ClearCacheConfirmation { - return &ClearCacheConfirmation{Status: status} +// Creates a new ClearCacheResponse, containing all required fields. There are no optional fields for this message. +func NewClearCacheResponse(status ClearCacheStatus) *ClearCacheResponse { + return &ClearCacheResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("cacheStatus", isValidClearCacheStatus) + _ = types.Validate.RegisterValidation("cacheStatus", isValidClearCacheStatus) } diff --git a/ocpp2.0/availability/availability.go b/ocpp2.0/availability/availability.go new file mode 100644 index 00000000..33defcfe --- /dev/null +++ b/ocpp2.0/availability/availability.go @@ -0,0 +1,22 @@ +// The availability functional block contains OCPP 2.0 features for notifying the CSMS of availability and status changes. +// A CSMS can also instruct a charging station to change its availability. +package availability + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Availability profile. +type CSMSHandler interface { +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Availability profile. +type ChargingStationHandler interface { + // OnChangeAvailability is called on a charging station whenever a ChangeAvailabilityRequest is received from the CSMS. + OnChangeAvailability(request *ChangeAvailabilityRequest) (confirmation *ChangeAvailabilityResponse, err error) +} + +const ProfileName = "availability" + +var Profile = ocpp.NewProfile( + ProfileName, + ChangeAvailabilityFeature{}, + ) diff --git a/ocpp2.0/change_availability.go b/ocpp2.0/availability/change_availability.go similarity index 75% rename from ocpp2.0/change_availability.go rename to ocpp2.0/availability/change_availability.go index 906e5766..b8696020 100644 --- a/ocpp2.0/change_availability.go +++ b/ocpp2.0/availability/change_availability.go @@ -1,12 +1,15 @@ -package ocpp2 +package availability import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Change Availability (CSMS -> CS) -------------------- +const ChangeAvailabilityFeatureName = "ChangeAvailability" + // Requested availability change in ChangeAvailabilityRequest. type OperationalStatus string @@ -50,9 +53,9 @@ type ChangeAvailabilityRequest struct { OperationalStatus OperationalStatus `json:"operationalStatus" validate:"required,operationalStatus"` } -// This field definition of the ChangeAvailability confirmation payload, sent by the Charging Station to the CSMS in response to a ChangeAvailabilityRequest. +// This field definition of the ChangeAvailability response payload, sent by the Charging Station to the CSMS in response to a ChangeAvailabilityRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type ChangeAvailabilityConfirmation struct { +type ChangeAvailabilityResponse struct { Status ChangeAvailabilityStatus `json:"status" validate:"required,changeAvailabilityStatus"` } @@ -71,15 +74,15 @@ func (f ChangeAvailabilityFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ChangeAvailabilityRequest{}) } -func (f ChangeAvailabilityFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(ChangeAvailabilityConfirmation{}) +func (f ChangeAvailabilityFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(ChangeAvailabilityResponse{}) } func (r ChangeAvailabilityRequest) GetFeatureName() string { return ChangeAvailabilityFeatureName } -func (c ChangeAvailabilityConfirmation) GetFeatureName() string { +func (c ChangeAvailabilityResponse) GetFeatureName() string { return ChangeAvailabilityFeatureName } @@ -88,12 +91,12 @@ func NewChangeAvailabilityRequest(evseID int, operationalStatus OperationalStatu return &ChangeAvailabilityRequest{EvseID: evseID, OperationalStatus: operationalStatus} } -// Creates a new ChangeAvailabilityConfirmation, containing all required fields. There are no optional fields for this message. -func NewChangeAvailabilityConfirmation(status ChangeAvailabilityStatus) *ChangeAvailabilityConfirmation { - return &ChangeAvailabilityConfirmation{Status: status} +// Creates a new ChangeAvailabilityResponse, containing all required fields. There are no optional fields for this message. +func NewChangeAvailabilityResponse(status ChangeAvailabilityStatus) *ChangeAvailabilityResponse { + return &ChangeAvailabilityResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("operationalStatus", isValidOperationalStatus) - _ = Validate.RegisterValidation("changeAvailabilityStatus", isValidChangeAvailabilityStatus) + _ = types.Validate.RegisterValidation("operationalStatus", isValidOperationalStatus) + _ = types.Validate.RegisterValidation("changeAvailabilityStatus", isValidChangeAvailabilityStatus) } diff --git a/ocpp2.0/charging_station.go b/ocpp2.0/charging_station.go index 86ec4ae2..72140374 100644 --- a/ocpp2.0/charging_station.go +++ b/ocpp2.0/charging_station.go @@ -3,267 +3,241 @@ package ocpp2 import ( "fmt" "github.com/lorenzodonini/ocpp-go/ocpp" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/authorization" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/availability" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/data" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/diagnostics" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/display" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/iso15118" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/localauth" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/meter" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/provisioning" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/remotecontrol" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/reservation" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/security" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/tariffcost" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/transactions" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/lorenzodonini/ocpp-go/ocppj" log "github.com/sirupsen/logrus" ) type chargingStation struct { - client *ocppj.ChargePoint - messageHandler ChargingStationHandler - confirmationListener chan ocpp.Confirmation - errorListener chan error + client *ocppj.Client + securityHandler security.ChargingStationHandler + provisioningHandler provisioning.ChargingStationHandler + authorizationHandler authorization.ChargingStationHandler + localAuthListHandler localauth.ChargingStationHandler + transactionsHandler transactions.ChargingStationHandler + remoteControlHandler remotecontrol.ChargingStationHandler + availabilityHandler availability.ChargingStationHandler + reservationHandler reservation.ChargingStationHandler + tariffCostHandler tariffcost.ChargingStationHandler + meterHandler meter.ChargingStationHandler + smartChargingHandler smartcharging.ChargingStationHandler + firmwareHandler firmware.ChargingStationHandler + iso15118Handler iso15118.ChargingStationHandler + diagnosticsHandler diagnostics.ChargingStationHandler + displayHandler display.ChargingStationHandler + dataHandler data.ChargingStationHandler + responseHandler chan ocpp.Response + errorHandler chan error } -func (cp *chargingStation) BootNotification(reason BootReason, model string, chargePointVendor string, props ...func(request *BootNotificationRequest)) (*BootNotificationConfirmation, error) { - request := NewBootNotificationRequest(reason, model, chargePointVendor) +func (cs *chargingStation) BootNotification(reason provisioning.BootReason, model string, vendor string, props ...func(request *provisioning.BootNotificationRequest)) (*provisioning.BootNotificationResponse, error) { + request := provisioning.NewBootNotificationRequest(reason, model, vendor) for _, fn := range props { fn(request) } - confirmation, err := cp.SendRequest(request) + response, err := cs.SendRequest(request) if err != nil { return nil, err } else { - return confirmation.(*BootNotificationConfirmation), err + return response.(*provisioning.BootNotificationResponse), err } } -func (cp *chargingStation) Authorize(idToken string, tokenType IdTokenType, props ...func(request *AuthorizeRequest)) (*AuthorizeConfirmation, error) { - request := NewAuthorizationRequest(idToken, tokenType) +func (cs *chargingStation) Authorize(idToken string, tokenType types.IdTokenType, props ...func(request *authorization.AuthorizeRequest)) (*authorization.AuthorizeResponse, error) { + request := authorization.NewAuthorizationRequest(idToken, tokenType) for _, fn := range props { fn(request) } - confirmation, err := cp.SendRequest(request) + response, err := cs.SendRequest(request) if err != nil { return nil, err } else { - return confirmation.(*AuthorizeConfirmation), err + return response.(*authorization.AuthorizeResponse), err } } -func (cp *chargingStation) ClearedChargingLimit(chargingLimitSource ChargingLimitSourceType, props ...func(request *ClearedChargingLimitRequest)) (*ClearedChargingLimitConfirmation, error) { - request := NewClearedChargingLimitRequest(chargingLimitSource) +func (cs *chargingStation) ClearedChargingLimit(chargingLimitSource types.ChargingLimitSourceType, props ...func(request *smartcharging.ClearedChargingLimitRequest)) (*smartcharging.ClearedChargingLimitResponse, error) { + request := smartcharging.NewClearedChargingLimitRequest(chargingLimitSource) for _, fn := range props { fn(request) } - confirmation, err := cp.SendRequest(request) + response, err := cs.SendRequest(request) if err != nil { return nil, err } else { - return confirmation.(*ClearedChargingLimitConfirmation), err + return response.(*smartcharging.ClearedChargingLimitResponse), err } } -// Starts a custom data transfer request. Every vendor may implement their own proprietary logic for this message. -func (cp *chargingStation) DataTransfer(vendorId string, props ...func(request *DataTransferRequest)) (*DataTransferConfirmation, error) { - request := NewDataTransferRequest(vendorId) +func (cs *chargingStation) DataTransfer(vendorId string, props ...func(request *data.DataTransferRequest)) (*data.DataTransferResponse, error) { + request := data.NewDataTransferRequest(vendorId) for _, fn := range props { fn(request) } - confirmation, err := cp.SendRequest(request) + response, err := cs.SendRequest(request) if err != nil { return nil, err } else { - return confirmation.(*DataTransferConfirmation), err + return response.(*data.DataTransferResponse), err } } -func (cp *chargingStation) FirmwareStatusNotification(status FirmwareStatus, requestID int, props ...func(request *FirmwareStatusNotificationRequest)) (*FirmwareStatusNotificationConfirmation, error) { - request := NewFirmwareStatusNotificationRequest(status, requestID) +func (cs *chargingStation) FirmwareStatusNotification(status firmware.FirmwareStatus, requestID int, props ...func(request *firmware.FirmwareStatusNotificationRequest)) (*firmware.FirmwareStatusNotificationResponse, error) { + request := firmware.NewFirmwareStatusNotificationRequest(status, requestID) for _, fn := range props { fn(request) } - confirmation, err := cp.SendRequest(request) + response, err := cs.SendRequest(request) if err != nil { return nil, err } else { - return confirmation.(*FirmwareStatusNotificationConfirmation), err + return response.(*firmware.FirmwareStatusNotificationResponse), err } } -func (cp *chargingStation) Get15118EVCertificate(schemaVersion string, exiRequest string, props ...func(request *Get15118EVCertificateRequest)) (*Get15118EVCertificateConfirmation, error) { - request := NewGet15118EVCertificateRequest(schemaVersion, exiRequest) +func (cs *chargingStation) Get15118EVCertificate(schemaVersion string, exiRequest string, props ...func(request *iso15118.Get15118EVCertificateRequest)) (*iso15118.Get15118EVCertificateResponse, error) { + request := iso15118.NewGet15118EVCertificateRequest(schemaVersion, exiRequest) for _, fn := range props { fn(request) } - confirmation, err := cp.SendRequest(request) + response, err := cs.SendRequest(request) if err != nil { return nil, err } else { - return confirmation.(*Get15118EVCertificateConfirmation), err + return response.(*iso15118.Get15118EVCertificateResponse), err } } -func (cp *chargingStation) GetCertificateStatus(ocspRequestData OCSPRequestDataType, props ...func(request *GetCertificateStatusRequest)) (*GetCertificateStatusConfirmation, error) { - request := NewGetCertificateStatusRequest(ocspRequestData) +func (cs *chargingStation) GetCertificateStatus(ocspRequestData types.OCSPRequestDataType, props ...func(request *iso15118.GetCertificateStatusRequest)) (*iso15118.GetCertificateStatusResponse, error) { + request := iso15118.NewGetCertificateStatusRequest(ocspRequestData) for _, fn := range props { fn(request) } - confirmation, err := cp.SendRequest(request) + response, err := cs.SendRequest(request) if err != nil { return nil, err } else { - return confirmation.(*GetCertificateStatusConfirmation), err + return response.(*iso15118.GetCertificateStatusResponse), err } } -// -//// Notifies the central system that the charge point is still online. The central system's response is used for time synchronization purposes. It is recommended to perform this operation once every 24 hours. -//func (cp *chargingStation) Heartbeat(props ...func(request *HeartbeatRequest)) (*HeartbeatConfirmation, error) { -// request := NewHeartbeatRequest() -// for _, fn := range props { -// fn(request) -// } -// confirmation, err := cp.SendRequest(request) -// if err != nil { -// return nil, err -// } else { -// return confirmation.(*HeartbeatConfirmation), err -// } -//} -// -//// Sends a batch of collected meter values to the central system, for billing and analysis. May be done periodically during ongoing transactions. -//func (cp *chargingStation) MeterValues(connectorId int, meterValues []MeterValue, props ...func(request *MeterValuesRequest)) (*MeterValuesConfirmation, error) { -// request := NewMeterValuesRequest(connectorId, meterValues) -// for _, fn := range props { -// fn(request) -// } -// confirmation, err := cp.SendRequest(request) -// if err != nil { -// return nil, err -// } else { -// return confirmation.(*MeterValuesConfirmation), err -// } -//} -// -//// Requests to start a transaction for a specific connector. The central system will verify the client's IdTag and either accept or reject the transaction. -//func (cp *chargingStation) StartTransaction(connectorId int, idTag string, meterStart int, timestamp *DateTime, props ...func(request *StartTransactionRequest)) (*StartTransactionConfirmation, error) { -// request := NewStartTransactionRequest(connectorId, idTag, meterStart, timestamp) -// for _, fn := range props { -// fn(request) -// } -// confirmation, err := cp.SendRequest(request) -// if err != nil { -// return nil, err -// } else { -// return confirmation.(*StartTransactionConfirmation), err -// } -//} -// -//// Stops an ongoing transaction. Typically a batch of meter values is passed along with this message. -//func (cp *chargingStation) StopTransaction(meterStop int, timestamp *DateTime, transactionId int, props ...func(request *StopTransactionRequest)) (*StopTransactionConfirmation, error) { -// request := NewStopTransactionRequest(meterStop, timestamp, transactionId) -// for _, fn := range props { -// fn(request) -// } -// confirmation, err := cp.SendRequest(request) -// if err != nil { -// return nil, err -// } else { -// return confirmation.(*StopTransactionConfirmation), err -// } -//} -// -//// Notifies the central system of a status update. This may apply to the entire charge point or to a single connector. -//func (cp *chargingStation) StatusNotification(connectorId int, errorCode ChargePointErrorCode, status ChargePointStatus, props ...func(request *StatusNotificationRequest)) (*StatusNotificationConfirmation, error) { -// request := NewStatusNotificationRequest(connectorId, errorCode, status) -// for _, fn := range props { -// fn(request) -// } -// confirmation, err := cp.SendRequest(request) -// if err != nil { -// return nil, err -// } else { -// return confirmation.(*StatusNotificationConfirmation), err -// } -//} -// -//// Notifies the central system of a status change in the upload of diagnostics data. -//func (cp *chargingStation) DiagnosticsStatusNotification(status DiagnosticsStatus, props ...func(request *DiagnosticsStatusNotificationRequest)) (*DiagnosticsStatusNotificationConfirmation, error) { -// request := NewDiagnosticsStatusNotificationRequest(status) -// for _, fn := range props { -// fn(request) -// } -// confirmation, err := cp.SendRequest(request) -// if err != nil { -// return nil, err -// } else { -// return confirmation.(*DiagnosticsStatusNotificationConfirmation), err -// } -//} -// -//// Notifies the central system of a status change during the download of a new firmware version. -//func (cp *chargingStation) FirmwareStatusNotification(status FirmwareStatus, props ...func(request *FirmwareStatusNotificationRequest)) (*FirmwareStatusNotificationConfirmation, error) { -// request := NewFirmwareStatusNotificationRequest(status) -// for _, fn := range props { -// fn(request) -// } -// confirmation, err := cp.SendRequest(request) -// if err != nil { -// return nil, err -// } else { -// return confirmation.(*FirmwareStatusNotificationConfirmation), err -// } -//} - -func (cp *chargingStation) SetMessageHandler(handler ChargingStationHandler) { - cp.messageHandler = handler -} - -//// Registers a handler for incoming local authorization profile messages -//func (cp *chargingStation) SetLocalAuthListListener(listener ChargePointLocalAuthListListener) { -// cp.localAuthListListener = listener -//} -// -//// Registers a handler for incoming firmware management profile messages -//func (cp *chargingStation) SetFirmwareManagementListener(listener ChargePointFirmwareManagementListener) { -// cp.firmwareListener = listener -//} -// -//// Registers a handler for incoming reservation profile messages -//func (cp *chargingStation) SetReservationListener(listener ChargePointReservationListener) { -// cp.reservationListener = listener -//} -// -//// Registers a handler for incoming remote trigger profile messages -//func (cp *chargingStation) SetRemoteTriggerListener(listener ChargePointRemoteTriggerListener) { -// cp.remoteTriggerListener = listener -//} -// -//// Registers a handler for incoming smart charging profile messages -//func (cp *chargingStation) SetSmartChargingListener(listener ChargePointSmartChargingListener) { -// cp.smartChargingListener = listener -//} - -func (cp *chargingStation) SendRequest(request ocpp.Request) (ocpp.Confirmation, error) { - // TODO: check for supported feature - err := cp.client.SendRequest(request) +func (cs *chargingStation) SetSecurityHandler(handler security.ChargingStationHandler) { + cs.securityHandler = handler +} + +func (cs *chargingStation) SetProvisioningHandler(handler provisioning.ChargingStationHandler) { + cs.provisioningHandler = handler +} + +func (cs *chargingStation) SetAuthorizationHandler(handler authorization.ChargingStationHandler) { + cs.authorizationHandler = handler +} + +func (cs *chargingStation) SetLocalAuthListHandler(handler localauth.ChargingStationHandler) { + cs.localAuthListHandler = handler +} + +func (cs *chargingStation) SetTransactionsHandler(handler transactions.ChargingStationHandler) { + cs.transactionsHandler = handler +} + +func (cs *chargingStation) SetRemoteControlHandler(handler remotecontrol.ChargingStationHandler) { + cs.remoteControlHandler = handler +} + +func (cs *chargingStation) SetAvailabilityHandler(handler availability.ChargingStationHandler) { + cs.availabilityHandler = handler +} + +func (cs *chargingStation) SetReservationHandler(handler reservation.ChargingStationHandler) { + cs.reservationHandler = handler +} + +func (cs *chargingStation) SetTariffCostHandler(handler tariffcost.ChargingStationHandler) { + cs.tariffCostHandler = handler +} + +func (cs *chargingStation) SetMeterHandler(handler meter.ChargingStationHandler) { + cs.meterHandler = handler +} + +func (cs *chargingStation) SetSmartChargingHandler(handler smartcharging.ChargingStationHandler) { + cs.smartChargingHandler = handler +} + +func (cs *chargingStation) SetFirmwareHandler(handler firmware.ChargingStationHandler) { + cs.firmwareHandler = handler +} + +func (cs *chargingStation) SetISO15118Handler(handler iso15118.ChargingStationHandler) { + cs.iso15118Handler = handler +} + +func (cs *chargingStation) SetDiagnosticsHandler(handler diagnostics.ChargingStationHandler) { + cs.diagnosticsHandler = handler +} + +func (cs *chargingStation) SetDisplayHandler(handler display.ChargingStationHandler) { + cs.displayHandler = handler +} + +func (cs *chargingStation) SetDataHandler(handler data.ChargingStationHandler) { + cs.dataHandler = handler +} + +func (cs *chargingStation) SendRequest(request ocpp.Request) (ocpp.Response, error) { + featureName := request.GetFeatureName() + if _, found := cs.client.GetProfileForFeature(featureName); !found { + return nil, fmt.Errorf("feature %v is unsupported on charging station (missing profile), cannot send request", featureName) + } + err := cs.client.SendRequest(request) if err != nil { return nil, err } //TODO: timeouts select { - case confirmation := <-cp.confirmationListener: - return confirmation, nil - case err = <-cp.errorListener: + case response := <-cs.responseHandler: + return response, nil + case err = <-cs.errorHandler: return nil, err } } -func (cp *chargingStation) SendRequestAsync(request ocpp.Request, callback func(confirmation ocpp.Confirmation, err error)) error { - switch request.GetFeatureName() { - case AuthorizeFeatureName, BootNotificationFeatureName, ClearedChargingLimitFeatureName, DataTransferFeatureName, FirmwareStatusNotificationFeatureName, Get15118EVCertificateFeatureName, GetCertificateStatusFeatureName: +func (cs *chargingStation) SendRequestAsync(request ocpp.Request, callback func(response ocpp.Response, err error)) error { + featureName := request.GetFeatureName() + if _, found := cs.client.GetProfileForFeature(featureName); !found { + return fmt.Errorf("feature %v is unsupported on charging station (missing profile), cannot send request", featureName) + } + switch featureName { + case authorization.AuthorizeFeatureName, provisioning.BootNotificationFeatureName, smartcharging.ClearedChargingLimitFeatureName, data.DataTransferFeatureName, firmware.FirmwareStatusNotificationFeatureName, iso15118.Get15118EVCertificateFeatureName, iso15118.GetCertificateStatusFeatureName: break default: - return fmt.Errorf("unsupported action %v on charge point, cannot send request", request.GetFeatureName()) + return fmt.Errorf("unsupported action %v on charging station, cannot send request", featureName) } - err := cp.client.SendRequest(request) + err := cs.client.SendRequest(request) if err == nil { // Retrieve result asynchronously go func() { select { - case confirmation := <-cp.confirmationListener: - callback(confirmation, nil) - case protoError := <-cp.errorListener: + case response := <-cs.responseHandler: + callback(response, nil) + case protoError := <-cs.errorHandler: callback(nil, protoError) } }() @@ -271,164 +245,171 @@ func (cp *chargingStation) SendRequestAsync(request ocpp.Request, callback func( return err } -func (cp *chargingStation) sendResponse(confirmation ocpp.Confirmation, err error, requestId string) { - if confirmation != nil { - err := cp.client.SendConfirmation(requestId, confirmation) +func (cs *chargingStation) sendResponse(response ocpp.Response, err error, requestId string) { + if response != nil { + err := cs.client.SendResponse(requestId, response) if err != nil { log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) //TODO: handle error somehow } } else { - err = cp.client.SendError(requestId, ocppj.ProtocolError, err.Error(), nil) + err = cs.client.SendError(requestId, ocppj.ProtocolError, err.Error(), nil) if err != nil { log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) } } } -func (cp *chargingStation) Start(csmsUrl string) error { +func (cs *chargingStation) Start(csmsUrl string) error { // TODO: implement auto-reconnect logic - return cp.client.Start(csmsUrl) + return cs.client.Start(csmsUrl) } -func (cp *chargingStation) Stop() { - cp.client.Stop() +func (cs *chargingStation) Stop() { + cs.client.Stop() } -func (cp *chargingStation) notImplementedError(requestId string, action string) { - log.WithField("request", requestId).Errorf("cannot handle call from central system. Sending CallError instead") - err := cp.client.SendError(requestId, ocppj.NotImplemented, fmt.Sprintf("no handler for action %v implemented", action), nil) +func (cs *chargingStation) notImplementedError(requestId string, action string) { + log.WithField("request", requestId).Errorf("cannot handle call from CSMS. Sending CallError instead") + err := cs.client.SendError(requestId, ocppj.NotImplemented, fmt.Sprintf("no handler for action %v implemented", action), nil) if err != nil { log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) } } -func (cp *chargingStation) notSupportedError(requestId string, action string) { - log.WithField("request", requestId).Errorf("cannot handle call from central system. Sending CallError instead") - err := cp.client.SendError(requestId, ocppj.NotSupported, fmt.Sprintf("unsupported action %v on charge point", action), nil) +func (cs *chargingStation) notSupportedError(requestId string, action string) { + log.WithField("request", requestId).Errorf("cannot handle call from CSMS. Sending CallError instead") + err := cs.client.SendError(requestId, ocppj.NotSupported, fmt.Sprintf("unsupported action %v on charging station", action), nil) if err != nil { log.WithField("request", requestId).Errorf("unknown error %v while replying to message with CallError", err) } } -func (cp *chargingStation) handleIncomingRequest(request ocpp.Request, requestId string, action string) { - profile, found := cp.client.GetProfileForFeature(action) +func (cs *chargingStation) handleIncomingRequest(request ocpp.Request, requestId string, action string) { + profile, found := cs.client.GetProfileForFeature(action) // Check whether action is supported and a listener for it exists if !found { - cp.notImplementedError(requestId, action) + cs.notImplementedError(requestId, action) return } else { + supported := true switch profile.Name { - case CoreProfileName: - if cp.messageHandler == nil { - cp.notSupportedError(requestId, action) - return + case authorization.ProfileName: + if cs.authorizationHandler == nil { + supported = false + } + case availability.ProfileName: + if cs.availabilityHandler == nil { + supported = false + } + case data.ProfileName: + if cs.dataHandler == nil { + supported = false + } + case diagnostics.ProfileName: + if cs.diagnosticsHandler == nil { + supported = false + } + case display.ProfileName: + if cs.displayHandler == nil { + supported = false } - //case LocalAuthListProfileName: - // if cp.localAuthListListener == nil { - // cp.notSupportedError(requestId, action) - // return - // } - //case FirmwareManagementProfileName: - // if cp.firmwareListener == nil { - // cp.notSupportedError(requestId, action) - // return - // } - //case ReservationProfileName: - // if cp.reservationListener == nil { - // cp.notSupportedError(requestId, action) - // return - // } - //case RemoteTriggerProfileName: - // if cp.remoteTriggerListener == nil { - // cp.notSupportedError(requestId, action) - // return - // } - //case SmartChargingProfileName: - // if cp.smartChargingListener == nil { - // cp.notSupportedError(requestId, action) - // return - // } + case firmware.ProfileName: + if cs.firmwareHandler == nil { + supported = false + } + case iso15118.ProfileName: + if cs.iso15118Handler == nil { + supported = false + } + case localauth.ProfileName: + if cs.localAuthListHandler == nil { + supported = false + } + case meter.ProfileName: + if cs.meterHandler == nil { + supported = false + } + case provisioning.ProfileName: + if cs.provisioningHandler == nil { + supported = false + } + case remotecontrol.ProfileName: + if cs.remoteControlHandler == nil { + supported = false + } + case reservation.ProfileName: + if cs.reservationHandler == nil { + supported = false + } + case security.ProfileName: + if cs.securityHandler == nil { + supported = false + } + case smartcharging.ProfileName: + if cs.smartChargingHandler == nil { + supported = false + } + case tariffcost.ProfileName: + if cs.tariffCostHandler == nil { + supported = false + } + case transactions.ProfileName: + if cs.transactionsHandler == nil { + supported = false + } + } + if !supported { + cs.notSupportedError(requestId, action) + return } } // Process request - var confirmation ocpp.Confirmation = nil - cp.client.GetProfileForFeature(action) + var response ocpp.Response = nil + cs.client.GetProfileForFeature(action) var err error = nil switch action { - case CancelReservationFeatureName: - confirmation, err = cp.messageHandler.OnCancelReservation(request.(*CancelReservationRequest)) - case CertificateSignedFeatureName: - confirmation, err = cp.messageHandler.OnCertificateSigned(request.(*CertificateSignedRequest)) - case ChangeAvailabilityFeatureName: - confirmation, err = cp.messageHandler.OnChangeAvailability(request.(*ChangeAvailabilityRequest)) - //case ChangeConfigurationFeatureName: - // confirmation, err = cp.messageHandler.OnChangeConfiguration(request.(*ChangeConfigurationRequest)) - case ClearCacheFeatureName: - confirmation, err = cp.messageHandler.OnClearCache(request.(*ClearCacheRequest)) - case ClearChargingProfileFeatureName: - confirmation, err = cp.messageHandler.OnClearChargingProfile(request.(*ClearChargingProfileRequest)) - case ClearDisplayFeatureName: - confirmation, err = cp.messageHandler.OnClearDisplay(request.(*ClearDisplayRequest)) - case ClearVariableMonitoringFeatureName: - confirmation, err = cp.messageHandler.OnClearVariableMonitoring(request.(*ClearVariableMonitoringRequest)) - case CostUpdatedFeatureName: - confirmation, err = cp.messageHandler.OnCostUpdated(request.(*CostUpdatedRequest)) - case CustomerInformationFeatureName: - confirmation, err = cp.messageHandler.OnCustomerInformation(request.(*CustomerInformationRequest)) - case DataTransferFeatureName: - confirmation, err = cp.messageHandler.OnDataTransfer(request.(*DataTransferRequest)) - case DeleteCertificateFeatureName: - confirmation, err = cp.messageHandler.OnDeleteCertificate(request.(*DeleteCertificateRequest)) - case GetBaseReportFeatureName: - confirmation, err = cp.messageHandler.OnGetBaseReport(request.(*GetBaseReportRequest)) - case GetChargingProfilesFeatureName: - confirmation, err = cp.messageHandler.OnGetChargingProfiles(request.(*GetChargingProfilesRequest)) - case GetCompositeScheduleFeatureName: - confirmation, err = cp.messageHandler.OnGetCompositeSchedule(request.(*GetCompositeScheduleRequest)) - case GetDisplayMessagesFeatureName: - confirmation, err = cp.messageHandler.OnGetDisplayMessages(request.(*GetDisplayMessagesRequest)) - case GetInstalledCertificateIdsFeatureName: - confirmation, err = cp.messageHandler.OnGetInstalledCertificateIds(request.(*GetInstalledCertificateIdsRequest)) - case GetLocalListVersionFeatureName: - confirmation, err = cp.messageHandler.OnGetLocalListVersion(request.(*GetLocalListVersionRequest)) - case GetLogFeatureName: - confirmation, err = cp.messageHandler.OnGetLog(request.(*GetLogRequest)) - case GetMonitoringReportFeatureName: - confirmation, err = cp.messageHandler.OnGetMonitoringReport(request.(*GetMonitoringReportRequest)) - //case GetConfigurationFeatureName: - // confirmation, err = cp.messageHandler.OnGetConfiguration(request.(*GetConfigurationRequest)) - //case RemoteStartTransactionFeatureName: - // confirmation, err = cp.messageHandler.OnRemoteStartTransaction(request.(*RemoteStartTransactionRequest)) - //case RemoteStopTransactionFeatureName: - // confirmation, err = cp.messageHandler.OnRemoteStopTransaction(request.(*RemoteStopTransactionRequest)) - //case ResetFeatureName: - // confirmation, err = cp.messageHandler.OnReset(request.(*ResetRequest)) - //case UnlockConnectorFeatureName: - // confirmation, err = cp.messageHandler.OnUnlockConnector(request.(*UnlockConnectorRequest)) - //case GetLocalListVersionFeatureName: - // confirmation, err = cp.localAuthListListener.OnGetLocalListVersion(request.(*GetLocalListVersionRequest)) - //case SendLocalListFeatureName: - // confirmation, err = cp.localAuthListListener.OnSendLocalList(request.(*SendLocalListRequest)) - //case GetDiagnosticsFeatureName: - // confirmation, err = cp.firmwareListener.OnGetDiagnostics(request.(*GetDiagnosticsRequest)) - //case UpdateFirmwareFeatureName: - // confirmation, err = cp.firmwareListener.OnUpdateFirmware(request.(*UpdateFirmwareRequest)) - //case ReserveNowFeatureName: - // confirmation, err = cp.reservationListener.OnReserveNow(request.(*ReserveNowRequest)) - //case CancelReservationFeatureName: - // confirmation, err = cp.reservationListener.OnCancelReservation(request.(*CancelReservationRequest)) - //case TriggerMessageFeatureName: - // confirmation, err = cp.remoteTriggerListener.OnTriggerMessage(request.(*TriggerMessageRequest)) - //case SetChargingProfileFeatureName: - // confirmation, err = cp.smartChargingListener.OnSetChargingProfile(request.(*SetChargingProfileRequest)) - //case ClearChargingProfileFeatureName: - // confirmation, err = cp.smartChargingListener.OnClearChargingProfile(request.(*ClearChargingProfileRequest)) - //case GetCompositeScheduleFeatureName: - // confirmation, err = cp.smartChargingListener.OnGetCompositeSchedule(request.(*GetCompositeScheduleRequest)) + case reservation.CancelReservationFeatureName: + response, err = cs.reservationHandler.OnCancelReservation(request.(*reservation.CancelReservationRequest)) + case security.CertificateSignedFeatureName: + response, err = cs.securityHandler.OnCertificateSigned(request.(*security.CertificateSignedRequest)) + case availability.ChangeAvailabilityFeatureName: + response, err = cs.availabilityHandler.OnChangeAvailability(request.(*availability.ChangeAvailabilityRequest)) + case authorization.ClearCacheFeatureName: + response, err = cs.authorizationHandler.OnClearCache(request.(*authorization.ClearCacheRequest)) + case smartcharging.ClearChargingProfileFeatureName: + response, err = cs.smartChargingHandler.OnClearChargingProfile(request.(*smartcharging.ClearChargingProfileRequest)) + case display.ClearDisplayFeatureName: + response, err = cs.displayHandler.OnClearDisplay(request.(*display.ClearDisplayRequest)) + case diagnostics.ClearVariableMonitoringFeatureName: + response, err = cs.diagnosticsHandler.OnClearVariableMonitoring(request.(*diagnostics.ClearVariableMonitoringRequest)) + case tariffcost.CostUpdatedFeatureName: + response, err = cs.tariffCostHandler.OnCostUpdated(request.(*tariffcost.CostUpdatedRequest)) + case diagnostics.CustomerInformationFeatureName: + response, err = cs.diagnosticsHandler.OnCustomerInformation(request.(*diagnostics.CustomerInformationRequest)) + case data.DataTransferFeatureName: + response, err = cs.dataHandler.OnDataTransfer(request.(*data.DataTransferRequest)) + case iso15118.DeleteCertificateFeatureName: + response, err = cs.iso15118Handler.OnDeleteCertificate(request.(*iso15118.DeleteCertificateRequest)) + case provisioning.GetBaseReportFeatureName: + response, err = cs.provisioningHandler.OnGetBaseReport(request.(*provisioning.GetBaseReportRequest)) + case smartcharging.GetChargingProfilesFeatureName: + response, err = cs.smartChargingHandler.OnGetChargingProfiles(request.(*smartcharging.GetChargingProfilesRequest)) + case smartcharging.GetCompositeScheduleFeatureName: + response, err = cs.smartChargingHandler.OnGetCompositeSchedule(request.(*smartcharging.GetCompositeScheduleRequest)) + case display.GetDisplayMessagesFeatureName: + response, err = cs.displayHandler.OnGetDisplayMessages(request.(*display.GetDisplayMessagesRequest)) + case iso15118.GetInstalledCertificateIdsFeatureName: + response, err = cs.iso15118Handler.OnGetInstalledCertificateIds(request.(*iso15118.GetInstalledCertificateIdsRequest)) + case localauth.GetLocalListVersionFeatureName: + response, err = cs.localAuthListHandler.OnGetLocalListVersion(request.(*localauth.GetLocalListVersionRequest)) + case diagnostics.GetLogFeatureName: + response, err = cs.diagnosticsHandler.OnGetLog(request.(*diagnostics.GetLogRequest)) + case diagnostics.GetMonitoringReportFeatureName: + response, err = cs.diagnosticsHandler.OnGetMonitoringReport(request.(*diagnostics.GetMonitoringReportRequest)) default: - cp.notSupportedError(requestId, action) + cs.notSupportedError(requestId, action) return } - cp.sendResponse(confirmation, err, requestId) + cs.sendResponse(response, err, requestId) } diff --git a/ocpp2.0/csms.go b/ocpp2.0/csms.go index c3acdff9..18587402 100644 --- a/ocpp2.0/csms.go +++ b/ocpp2.0/csms.go @@ -3,30 +3,56 @@ package ocpp2 import ( "fmt" "github.com/lorenzodonini/ocpp-go/ocpp" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/authorization" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/availability" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/data" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/diagnostics" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/display" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/iso15118" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/localauth" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/meter" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/provisioning" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/remotecontrol" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/reservation" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/security" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/tariffcost" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/transactions" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/lorenzodonini/ocpp-go/ocppj" log "github.com/sirupsen/logrus" ) type csms struct { - server *ocppj.CentralSystem - coreListener CSMSHandler - //localAuthListListener CentralSystemLocalAuthListListener - //firmwareListener CentralSystemFirmwareManagementListener - //reservationListener CentralSystemReservationListener - //remoteTriggerListener CentralSystemRemoteTriggerListener - //smartChargingListener CentralSystemSmartChargingListener - callbacks map[string]func(confirmation ocpp.Confirmation, err error) -} - -// Cancels a previously reserved charge point or connector, given the reservation Id. -func (cs *csms) CancelReservation(clientId string, callback func(*CancelReservationConfirmation, error), reservationId int, props ...func(request *CancelReservationRequest)) error { - request := NewCancelReservationRequest(reservationId) + server *ocppj.Server + securityHandler security.CSMSHandler + provisioningHandler provisioning.CSMSHandler + authorizationHandler authorization.CSMSHandler + localAuthListHandler localauth.CSMSHandler + transactionsHandler transactions.CSMSHandler + remoteControlHandler remotecontrol.CSMSHandler + availabilityHandler availability.CSMSHandler + reservationHandler reservation.CSMSHandler + tariffCostHandler tariffcost.CSMSHandler + meterHandler meter.CSMSHandler + smartChargingHandler smartcharging.CSMSHandler + firmwareHandler firmware.CSMSHandler + iso15118Handler iso15118.CSMSHandler + diagnosticsHandler diagnostics.CSMSHandler + displayHandler display.CSMSHandler + dataHandler data.CSMSHandler + callbacks map[string]func(response ocpp.Response, err error) +} + +func (cs *csms) CancelReservation(clientId string, callback func(*reservation.CancelReservationResponse, error), reservationId int, props ...func(request *reservation.CancelReservationRequest)) error { + request := reservation.NewCancelReservationRequest(reservationId) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*CancelReservationConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*reservation.CancelReservationResponse), protoError) } else { callback(nil, protoError) } @@ -34,15 +60,14 @@ func (cs *csms) CancelReservation(clientId string, callback func(*CancelReservat return cs.SendRequestAsync(clientId, request, genericCallback) } -// Sends a new certificate (chain) to the charging station. -func (cs *csms) CertificateSigned(clientId string, callback func(*CertificateSignedConfirmation, error), certificate []string, props ...func(*CertificateSignedRequest)) error { - request := NewCertificateSignedRequest(certificate) +func (cs *csms) CertificateSigned(clientId string, callback func(*security.CertificateSignedResponse, error), certificate []string, props ...func(*security.CertificateSignedRequest)) error { + request := security.NewCertificateSignedRequest(certificate) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*CertificateSignedConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*security.CertificateSignedResponse), protoError) } else { callback(nil, protoError) } @@ -50,15 +75,14 @@ func (cs *csms) CertificateSigned(clientId string, callback func(*CertificateSig return cs.SendRequestAsync(clientId, request, genericCallback) } -// Instructs a charge point to change its availability. The target availability can be set for a single evse of for the whole charging station. -func (cs *csms) ChangeAvailability(clientId string, callback func(confirmation *ChangeAvailabilityConfirmation, err error), evseID int, operationalStatus OperationalStatus, props ...func(request *ChangeAvailabilityRequest)) error { - request := NewChangeAvailabilityRequest(evseID, operationalStatus) +func (cs *csms) ChangeAvailability(clientId string, callback func(*availability.ChangeAvailabilityResponse, error), evseID int, operationalStatus availability.OperationalStatus, props ...func(request *availability.ChangeAvailabilityRequest)) error { + request := availability.NewChangeAvailabilityRequest(evseID, operationalStatus) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ChangeAvailabilityConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*availability.ChangeAvailabilityResponse), protoError) } else { callback(nil, protoError) } @@ -66,32 +90,14 @@ func (cs *csms) ChangeAvailability(clientId string, callback func(confirmation * return cs.SendRequestAsync(clientId, request, genericCallback) } -// -//// Changes the configuration of a charge point, by setting a specific key-value pair. -//// The configuration key must be supported by the target charge point, in order for the configuration to be accepted. -//func (cs *server) ChangeConfiguration(clientId string, callback func(confirmation *ChangeConfigurationConfirmation, err error), key string, value string, props ...func(request *ChangeConfigurationRequest)) error { -// request := NewChangeConfigurationRequest(key, value) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*ChangeConfigurationConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} - -func (cs *csms) ClearCache(clientId string, callback func(confirmation *ClearCacheConfirmation, err error), props ...func(*ClearCacheRequest)) error { - request := NewClearCacheRequest() +func (cs *csms) ClearCache(clientId string, callback func(*authorization.ClearCacheResponse, error), props ...func(*authorization.ClearCacheRequest)) error { + request := authorization.NewClearCacheRequest() for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ClearCacheConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*authorization.ClearCacheResponse), protoError) } else { callback(nil, protoError) } @@ -99,15 +105,14 @@ func (cs *csms) ClearCache(clientId string, callback func(confirmation *ClearCac return cs.SendRequestAsync(clientId, request, genericCallback) } -// Removes one or more charging profiles from a charging station. -func (cs *csms) ClearChargingProfile(clientId string, callback func(*ClearChargingProfileConfirmation, error), props ...func(request *ClearChargingProfileRequest)) error { - request := NewClearChargingProfileRequest() +func (cs *csms) ClearChargingProfile(clientId string, callback func(*smartcharging.ClearChargingProfileResponse, error), props ...func(request *smartcharging.ClearChargingProfileRequest)) error { + request := smartcharging.NewClearChargingProfileRequest() for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ClearChargingProfileConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*smartcharging.ClearChargingProfileResponse), protoError) } else { callback(nil, protoError) } @@ -115,14 +120,14 @@ func (cs *csms) ClearChargingProfile(clientId string, callback func(*ClearChargi return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) ClearDisplay(clientId string, callback func(*ClearDisplayConfirmation, error), id int, props ...func(*ClearDisplayRequest)) error { - request := NewClearDisplayRequest(id) +func (cs *csms) ClearDisplay(clientId string, callback func(*display.ClearDisplayResponse, error), id int, props ...func(*display.ClearDisplayRequest)) error { + request := display.NewClearDisplayRequest(id) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ClearDisplayConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*display.ClearDisplayResponse), protoError) } else { callback(nil, protoError) } @@ -130,14 +135,14 @@ func (cs *csms) ClearDisplay(clientId string, callback func(*ClearDisplayConfirm return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) ClearVariableMonitoring(clientId string, callback func(*ClearVariableMonitoringConfirmation, error), id []int, props ...func(*ClearVariableMonitoringRequest)) error { - request := NewClearVariableMonitoringRequest(id) +func (cs *csms) ClearVariableMonitoring(clientId string, callback func(*diagnostics.ClearVariableMonitoringResponse, error), id []int, props ...func(*diagnostics.ClearVariableMonitoringRequest)) error { + request := diagnostics.NewClearVariableMonitoringRequest(id) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*ClearVariableMonitoringConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*diagnostics.ClearVariableMonitoringResponse), protoError) } else { callback(nil, protoError) } @@ -145,14 +150,14 @@ func (cs *csms) ClearVariableMonitoring(clientId string, callback func(*ClearVar return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) CostUpdated(clientId string, callback func(*CostUpdatedConfirmation, error), totalCost float64, transactionId string, props ...func(*CostUpdatedRequest)) error { - request := NewCostUpdatedRequest(totalCost, transactionId) +func (cs *csms) CostUpdated(clientId string, callback func(*tariffcost.CostUpdatedResponse, error), totalCost float64, transactionId string, props ...func(*tariffcost.CostUpdatedRequest)) error { + request := tariffcost.NewCostUpdatedRequest(totalCost, transactionId) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*CostUpdatedConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*tariffcost.CostUpdatedResponse), protoError) } else { callback(nil, protoError) } @@ -160,14 +165,14 @@ func (cs *csms) CostUpdated(clientId string, callback func(*CostUpdatedConfirmat return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) CustomerInformation(clientId string, callback func(*CustomerInformationConfirmation, error), requestId int, report bool, clear bool, props ...func(*CustomerInformationRequest)) error { - request := NewCustomerInformationRequest(requestId, report, clear) +func (cs *csms) CustomerInformation(clientId string, callback func(*diagnostics.CustomerInformationResponse, error), requestId int, report bool, clear bool, props ...func(*diagnostics.CustomerInformationRequest)) error { + request := diagnostics.NewCustomerInformationRequest(requestId, report, clear) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*CustomerInformationConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*diagnostics.CustomerInformationResponse), protoError) } else { callback(nil, protoError) } @@ -175,15 +180,14 @@ func (cs *csms) CustomerInformation(clientId string, callback func(*CustomerInfo return cs.SendRequestAsync(clientId, request, genericCallback) } -// Starts a custom data transfer request. Every vendor may implement their own proprietary logic for this message. -func (cs *csms) DataTransfer(clientId string, callback func(confirmation *DataTransferConfirmation, err error), vendorId string, props ...func(request *DataTransferRequest)) error { - request := NewDataTransferRequest(vendorId) +func (cs *csms) DataTransfer(clientId string, callback func(*data.DataTransferResponse, error), vendorId string, props ...func(request *data.DataTransferRequest)) error { + request := data.NewDataTransferRequest(vendorId) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*DataTransferConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*data.DataTransferResponse), protoError) } else { callback(nil, protoError) } @@ -191,14 +195,14 @@ func (cs *csms) DataTransfer(clientId string, callback func(confirmation *DataTr return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) DeleteCertificate(clientId string, callback func(*DeleteCertificateConfirmation, error), data CertificateHashData, props ...func(*DeleteCertificateRequest)) error { - request := NewDeleteCertificateRequest(data) +func (cs *csms) DeleteCertificate(clientId string, callback func(*iso15118.DeleteCertificateResponse, error), data types.CertificateHashData, props ...func(*iso15118.DeleteCertificateRequest)) error { + request := iso15118.NewDeleteCertificateRequest(data) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*DeleteCertificateConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*iso15118.DeleteCertificateResponse), protoError) } else { callback(nil, protoError) } @@ -206,14 +210,14 @@ func (cs *csms) DeleteCertificate(clientId string, callback func(*DeleteCertific return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) GetBaseReport(clientId string, callback func(*GetBaseReportConfirmation, error), requestId int, reportBase ReportBaseType, props ...func(*GetBaseReportRequest)) error { - request := NewGetBaseReportRequest(requestId, reportBase) +func (cs *csms) GetBaseReport(clientId string, callback func(*provisioning.GetBaseReportResponse, error), requestId int, reportBase provisioning.ReportBaseType, props ...func(*provisioning.GetBaseReportRequest)) error { + request := provisioning.NewGetBaseReportRequest(requestId, reportBase) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetBaseReportConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*provisioning.GetBaseReportResponse), protoError) } else { callback(nil, protoError) } @@ -221,14 +225,14 @@ func (cs *csms) GetBaseReport(clientId string, callback func(*GetBaseReportConfi return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) GetChargingProfiles(clientId string, callback func(*GetChargingProfilesConfirmation, error), chargingProfile ChargingProfileCriterion, props ...func(*GetChargingProfilesRequest)) error { - request := NewGetChargingProfilesRequest(chargingProfile) +func (cs *csms) GetChargingProfiles(clientId string, callback func(*smartcharging.GetChargingProfilesResponse, error), chargingProfile smartcharging.ChargingProfileCriterion, props ...func(*smartcharging.GetChargingProfilesRequest)) error { + request := smartcharging.NewGetChargingProfilesRequest(chargingProfile) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetChargingProfilesConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*smartcharging.GetChargingProfilesResponse), protoError) } else { callback(nil, protoError) } @@ -236,14 +240,14 @@ func (cs *csms) GetChargingProfiles(clientId string, callback func(*GetChargingP return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) GetCompositeSchedule(clientId string, callback func(*GetCompositeScheduleConfirmation, error), duration int, evseId int, props ...func(*GetCompositeScheduleRequest)) error { - request := NewGetCompositeScheduleRequest(duration, evseId) +func (cs *csms) GetCompositeSchedule(clientId string, callback func(*smartcharging.GetCompositeScheduleResponse, error), duration int, evseId int, props ...func(*smartcharging.GetCompositeScheduleRequest)) error { + request := smartcharging.NewGetCompositeScheduleRequest(duration, evseId) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetCompositeScheduleConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*smartcharging.GetCompositeScheduleResponse), protoError) } else { callback(nil, protoError) } @@ -251,14 +255,14 @@ func (cs *csms) GetCompositeSchedule(clientId string, callback func(*GetComposit return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) GetDisplayMessages(clientId string, callback func(*GetDisplayMessagesConfirmation, error), requestId int, props ...func(*GetDisplayMessagesRequest)) error { - request := NewGetDisplayMessagesRequest(requestId) +func (cs *csms) GetDisplayMessages(clientId string, callback func(*display.GetDisplayMessagesResponse, error), requestId int, props ...func(*display.GetDisplayMessagesRequest)) error { + request := display.NewGetDisplayMessagesRequest(requestId) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetDisplayMessagesConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*display.GetDisplayMessagesResponse), protoError) } else { callback(nil, protoError) } @@ -266,14 +270,14 @@ func (cs *csms) GetDisplayMessages(clientId string, callback func(*GetDisplayMes return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) GetInstalledCertificateIds(clientId string, callback func(*GetInstalledCertificateIdsConfirmation, error), typeOfCertificate CertificateUse, props ...func(*GetInstalledCertificateIdsRequest)) error { - request := NewGetInstalledCertificateIdsRequest(typeOfCertificate) +func (cs *csms) GetInstalledCertificateIds(clientId string, callback func(*iso15118.GetInstalledCertificateIdsResponse, error), typeOfCertificate types.CertificateUse, props ...func(*iso15118.GetInstalledCertificateIdsRequest)) error { + request := iso15118.NewGetInstalledCertificateIdsRequest(typeOfCertificate) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetInstalledCertificateIdsConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*iso15118.GetInstalledCertificateIdsResponse), protoError) } else { callback(nil, protoError) } @@ -281,14 +285,14 @@ func (cs *csms) GetInstalledCertificateIds(clientId string, callback func(*GetIn return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) GetLocalListVersion(clientId string, callback func(*GetLocalListVersionConfirmation, error), props ...func(*GetLocalListVersionRequest)) error { - request := NewGetLocalListVersionRequest() +func (cs *csms) GetLocalListVersion(clientId string, callback func(*localauth.GetLocalListVersionResponse, error), props ...func(*localauth.GetLocalListVersionRequest)) error { + request := localauth.NewGetLocalListVersionRequest() for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetLocalListVersionConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*localauth.GetLocalListVersionResponse), protoError) } else { callback(nil, protoError) } @@ -296,14 +300,14 @@ func (cs *csms) GetLocalListVersion(clientId string, callback func(*GetLocalList return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) GetLog(clientId string, callback func(*GetLogConfirmation, error), logType LogType, requestID int, logParameters LogParameters, props ...func(*GetLogRequest)) error { - request := NewGetLogRequest(logType, requestID, logParameters) +func (cs *csms) GetLog(clientId string, callback func(*diagnostics.GetLogResponse, error), logType diagnostics.LogType, requestID int, logParameters diagnostics.LogParameters, props ...func(*diagnostics.GetLogRequest)) error { + request := diagnostics.NewGetLogRequest(logType, requestID, logParameters) for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetLogConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*diagnostics.GetLogResponse), protoError) } else { callback(nil, protoError) } @@ -311,14 +315,14 @@ func (cs *csms) GetLog(clientId string, callback func(*GetLogConfirmation, error return cs.SendRequestAsync(clientId, request, genericCallback) } -func (cs *csms) GetMonitoringReport(clientId string, callback func(*GetMonitoringReportConfirmation, error), props ...func(*GetMonitoringReportRequest)) error { - request := NewGetMonitoringReportRequest() +func (cs *csms) GetMonitoringReport(clientId string, callback func(*diagnostics.GetMonitoringReportResponse, error), props ...func(*diagnostics.GetMonitoringReportRequest)) error { + request := diagnostics.NewGetMonitoringReportRequest() for _, fn := range props { fn(request) } - genericCallback := func(confirmation ocpp.Confirmation, protoError error) { - if confirmation != nil { - callback(confirmation.(*GetMonitoringReportConfirmation), protoError) + genericCallback := func(response ocpp.Response, protoError error) { + if response != nil { + callback(response.(*diagnostics.GetMonitoringReportResponse), protoError) } else { callback(nil, protoError) } @@ -326,265 +330,88 @@ func (cs *csms) GetMonitoringReport(clientId string, callback func(*GetMonitorin return cs.SendRequestAsync(clientId, request, genericCallback) } -// -//// Retrieves the configuration values for the provided configuration keys. -//func (cs *server) GetConfiguration(clientId string, callback func(confirmation *GetConfigurationConfirmation, err error), keys []string, props ...func(request *GetConfigurationRequest)) error { -// request := NewGetConfigurationRequest(keys) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*GetConfigurationConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Instructs a charge point to start a transaction for a specified client on a provided connector. -//// Depending on the configuration, an explicit authorization message may still be required, before the transaction can start. -//func (cs *server) RemoteStartTransaction(clientId string, callback func(*RemoteStartTransactionConfirmation, error), idTag string, props ...func(*RemoteStartTransactionRequest)) error { -// request := NewRemoteStartTransactionRequest(idTag) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*RemoteStartTransactionConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Instructs a charge point to stop an ongoing transaction, given the transaction's ID. -//func (cs *server) RemoteStopTransaction(clientId string, callback func(*RemoteStopTransactionConfirmation, error), transactionId int, props ...func(request *RemoteStopTransactionRequest)) error { -// request := NewRemoteStopTransactionRequest(transactionId) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*RemoteStopTransactionConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Forces a charge point to perform an internal hard or soft reset. In both cases, all ongoing transactions are stopped. -//func (cs *server) Reset(clientId string, callback func(*ResetConfirmation, error), resetType ResetType, props ...func(request *ResetRequest)) error { -// request := NewResetRequest(resetType) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*ResetConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Attempts to unlock a specific connector on a charge point. Used for remote support purposes. -//func (cs *server) UnlockConnector(clientId string, callback func(*UnlockConnectorConfirmation, error), connectorId int, props ...func(*UnlockConnectorRequest)) error { -// request := NewUnlockConnectorRequest(connectorId) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*UnlockConnectorConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Queries the current version of the local authorization list from a charge point. -//func (cs *server) GetLocalListVersion(clientId string, callback func(*GetLocalListVersionConfirmation, error), props ...func(request *GetLocalListVersionRequest)) error { -// request := NewGetLocalListVersionRequest() -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*GetLocalListVersionConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Sends or updates a local authorization list on a charge point. Versioning rules must be followed. -//func (cs *server) SendLocalList(clientId string, callback func(*SendLocalListConfirmation, error), version int, updateType UpdateType, props ...func(request *SendLocalListRequest)) error { -// request := NewSendLocalListRequest(version, updateType) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*SendLocalListConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Requests diagnostics data from a charge point. The data will be uploaded out-of-band to the provided URL location. -//func (cs *server) GetDiagnostics(clientId string, callback func(*GetDiagnosticsConfirmation, error), location string, props ...func(request *GetDiagnosticsRequest)) error { -// request := NewGetDiagnosticsRequest(location) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*GetDiagnosticsConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Instructs the charge point to download and install a new firmware version. The firmware binary will be downloaded out-of-band from the provided URL location. -//func (cs *server) UpdateFirmware(clientId string, callback func(*UpdateFirmwareConfirmation, error), location string, retrieveDate *DateTime, props ...func(request *UpdateFirmwareRequest)) error { -// request := NewUpdateFirmwareRequest(location, retrieveDate) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*UpdateFirmwareConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Instructs the charge point to reserve a connector for a specific IdTag (client). The connector, or the entire charge point, will be reserved until the provided expiration time. -//func (cs *server) ReserveNow(clientId string, callback func(*ReserveNowConfirmation, error), connectorId int, expiryDate *DateTime, idTag string, reservationId int, props ...func(request *ReserveNowRequest)) error { -// request := NewReserveNowRequest(connectorId, expiryDate, idTag, reservationId) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*ReserveNowConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Instructs a charge point to send a specific message to the central system. This is used for forcefully triggering status updates, when the last known state is either too old or not clear to the central system. -//func (cs *server) TriggerMessage(clientId string, callback func(*TriggerMessageConfirmation, error), requestedMessage MessageTrigger, props ...func(request *TriggerMessageRequest)) error { -// request := NewTriggerMessageRequest(requestedMessage) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*TriggerMessageConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Sends a smart charging profile to a charge point. Refer to the smart charging documentation for more information. -//func (cs *server) SetChargingProfile(clientId string, callback func(*SetChargingProfileConfirmation, error), connectorId int, chargingProfile *ChargingProfile, props ...func(request *SetChargingProfileRequest)) error { -// request := NewSetChargingProfileRequest(connectorId, chargingProfile) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*SetChargingProfileConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} -// -//// Queries a charge point to the composite smart charging schedules and rules for a specified time interval. -//func (cs *server) GetCompositeSchedule(clientId string, callback func(*GetCompositeScheduleConfirmation, error), connectorId int, duration int, props ...func(request *GetCompositeScheduleRequest)) error { -// request := NewGetCompositeScheduleRequest(connectorId, duration) -// for _, fn := range props { -// fn(request) -// } -// genericCallback := func(confirmation ocpp.Confirmation, protoError error) { -// if confirmation != nil { -// callback(confirmation.(*GetCompositeScheduleConfirmation), protoError) -// } else { -// callback(nil, protoError) -// } -// } -// return cs.SendRequestAsync(clientId, request, genericCallback) -//} - -func (cs *csms) SetMessageHandler(handler CSMSHandler) { - cs.coreListener = handler -} - -// Registers a handler for incoming local authorization profile messages. -//func (cs *server) SetLocalAuthListListener(listener CentralSystemLocalAuthListListener) { -// cs.localAuthListListener = listener -//} -// -//// Registers a handler for incoming firmware management profile messages. -//func (cs *server) SetFirmwareManagementListener(listener CentralSystemFirmwareManagementListener) { -// cs.firmwareListener = listener -//} -// -//// Registers a handler for incoming reservation profile messages. -//func (cs *server) SetReservationListener(listener CentralSystemReservationListener) { -// cs.reservationListener = listener -//} -// -//// Registers a handler for incoming remote trigger profile messages. -//func (cs *server) SetRemoteTriggerListener(listener CentralSystemRemoteTriggerListener) { -// cs.remoteTriggerListener = listener -//} -// -//// Registers a handler for incoming smart charging profile messages. -//func (cs *server) SetSmartChargingListener(listener CentralSystemSmartChargingListener) { -// cs.smartChargingListener = listener -//} - -func (cs *csms) SetNewChargingStationHandler(handler func(chargePointId string)) { - cs.server.SetNewChargePointHandler(handler) -} - -func (cs *csms) SetChargingStationDisconnectedHandler(handler func(chargePointId string)) { - cs.server.SetDisconnectedChargePointHandler(handler) -} - -func (cs *csms) SendRequestAsync(clientId string, request ocpp.Request, callback func(confirmation ocpp.Confirmation, err error)) error { - switch request.GetFeatureName() { - case CancelReservationFeatureName, CertificateSignedFeatureName, ChangeAvailabilityFeatureName, ClearCacheFeatureName, ClearChargingProfileFeatureName, ClearDisplayFeatureName, ClearVariableMonitoringFeatureName, CostUpdatedFeatureName, CustomerInformationFeatureName, DataTransferFeatureName, DeleteCertificateFeatureName, GetBaseReportFeatureName, GetChargingProfilesFeatureName, GetCompositeScheduleFeatureName, GetDisplayMessagesFeatureName, GetInstalledCertificateIdsFeatureName, GetLocalListVersionFeatureName, GetLogFeatureName, GetMonitoringReportFeatureName: +func (cs *csms) SetSecurityHandler(handler security.CSMSHandler) { + cs.securityHandler = handler +} + +func (cs *csms) SetProvisioningHandler(handler provisioning.CSMSHandler) { + cs.provisioningHandler = handler +} + +func (cs *csms) SetAuthorizationHandler(handler authorization.CSMSHandler) { + cs.authorizationHandler = handler +} + +func (cs *csms) SetLocalAuthListHandler(handler localauth.CSMSHandler) { + cs.localAuthListHandler = handler +} + +func (cs *csms) SetTransactionsHandler(handler transactions.CSMSHandler) { + cs.transactionsHandler = handler +} + +func (cs *csms) SetRemoteControlHandler(handler remotecontrol.CSMSHandler) { + cs.remoteControlHandler = handler +} + +func (cs *csms) SetAvailabilityHandler(handler availability.CSMSHandler) { + cs.availabilityHandler = handler +} + +func (cs *csms) SetReservationHandler(handler reservation.CSMSHandler) { + cs.reservationHandler = handler +} + +func (cs *csms) SetTariffCostHandler(handler tariffcost.CSMSHandler) { + cs.tariffCostHandler = handler +} + +func (cs *csms) SetMeterHandler(handler meter.CSMSHandler) { + cs.meterHandler = handler +} + +func (cs *csms) SetSmartChargingHandler(handler smartcharging.CSMSHandler) { + cs.smartChargingHandler = handler +} + +func (cs *csms) SetFirmwareHandler(handler firmware.CSMSHandler) { + cs.firmwareHandler = handler +} + +func (cs *csms) SetISO15118Handler(handler iso15118.CSMSHandler) { + cs.iso15118Handler = handler +} + +func (cs *csms) SetDiagnosticsHandler(handler diagnostics.CSMSHandler) { + cs.diagnosticsHandler = handler +} + +func (cs *csms) SetDisplayHandler(handler display.CSMSHandler) { + cs.displayHandler = handler +} + +func (cs *csms) SetDataHandler(handler data.CSMSHandler) { + cs.dataHandler = handler +} + +func (cs *csms) SetNewChargingStationHandler(handler func(chargingStationID string)) { + cs.server.SetNewClientHandler(handler) +} + +func (cs *csms) SetChargingStationDisconnectedHandler(handler func(chargingStationID string)) { + cs.server.SetDisconnectedClientHandler(handler) +} + +func (cs *csms) SendRequestAsync(clientId string, request ocpp.Request, callback func(response ocpp.Response, err error)) error { + featureName := request.GetFeatureName() + if _, found := cs.server.GetProfileForFeature(featureName); !found { + return fmt.Errorf("feature %v is unsupported on CSMS (missing profile), cannot send request", featureName) + } + switch featureName { + case reservation.CancelReservationFeatureName, security.CertificateSignedFeatureName, availability.ChangeAvailabilityFeatureName, authorization.ClearCacheFeatureName, smartcharging.ClearChargingProfileFeatureName, display.ClearDisplayFeatureName, diagnostics.ClearVariableMonitoringFeatureName, tariffcost.CostUpdatedFeatureName, diagnostics.CustomerInformationFeatureName, data.DataTransferFeatureName, iso15118.DeleteCertificateFeatureName, provisioning.GetBaseReportFeatureName, smartcharging.GetChargingProfilesFeatureName, smartcharging.GetCompositeScheduleFeatureName, display.GetDisplayMessagesFeatureName, iso15118.GetInstalledCertificateIdsFeatureName, localauth.GetLocalListVersionFeatureName, diagnostics.GetLogFeatureName, diagnostics.GetMonitoringReportFeatureName: break - //case ChangeConfigurationFeatureName, DataTransferFeatureName, GetConfigurationFeatureName, RemoteStartTransactionFeatureName, RemoteStopTransactionFeatureName, ResetFeatureName, UnlockConnectorFeatureName, - // GetLocalListVersionFeatureName, SendLocalListFeatureName, - // GetDiagnosticsFeatureName, UpdateFirmwareFeatureName, - // ReserveNowFeatureName, - // TriggerMessageFeatureName, - // SetChargingProfileFeatureName, ClearChargingProfileFeatureName, GetCompositeScheduleFeatureName: default: - return fmt.Errorf("unsupported action %v on central system, cannot send request", request.GetFeatureName()) + return fmt.Errorf("unsupported action %v on CSMS, cannot send request", featureName) } cs.callbacks[clientId] = callback err := cs.server.SendRequest(clientId, request) @@ -599,146 +426,172 @@ func (cs *csms) Start(listenPort int, listenPath string) { cs.server.Start(listenPort, listenPath) } -func (cs *csms) sendResponse(chargePointId string, confirmation ocpp.Confirmation, err error, requestId string) { - if confirmation != nil { - err := cs.server.SendConfirmation(chargePointId, requestId, confirmation) +func (cs *csms) sendResponse(chargingStationID string, response ocpp.Response, err error, requestId string) { + if response != nil { + err := cs.server.SendResponse(chargingStationID, requestId, response) if err != nil { //TODO: handle error somehow log.Print(err) } } else { - err := cs.server.SendError(chargePointId, requestId, ocppj.ProtocolError, "Couldn't generate valid confirmation", nil) + err := cs.server.SendError(chargingStationID, requestId, ocppj.ProtocolError, "Couldn't generate valid response", nil) if err != nil { log.WithFields(log.Fields{ - "client": chargePointId, + "client": chargingStationID, "request": requestId, }).Errorf("unknown error %v while replying to message with CallError", err) } } } -func (cs *csms) notImplementedError(chargePointId string, requestId string, action string) { - log.Warnf("Cannot handle call %v from charge point %v. Sending CallError instead", requestId, chargePointId) - err := cs.server.SendError(chargePointId, requestId, ocppj.NotImplemented, fmt.Sprintf("no handler for action %v implemented", action), nil) +func (cs *csms) notImplementedError(chargingStationID string, requestId string, action string) { + log.Warnf("Cannot handle call %v from charging station %v. Sending CallError instead", requestId, chargingStationID) + err := cs.server.SendError(chargingStationID, requestId, ocppj.NotImplemented, fmt.Sprintf("no handler for action %v implemented", action), nil) if err != nil { log.WithFields(log.Fields{ - "client": chargePointId, + "client": chargingStationID, "request": requestId, }).Errorf("unknown error %v while replying to message with CallError", err) } } -func (cs *csms) notSupportedError(chargePointId string, requestId string, action string) { - log.Warnf("Cannot handle call %v from charge point %v. Sending CallError instead", requestId, chargePointId) - err := cs.server.SendError(chargePointId, requestId, ocppj.NotSupported, fmt.Sprintf("unsupported action %v on central system", action), nil) +func (cs *csms) notSupportedError(chargingStationID string, requestId string, action string) { + log.Warnf("Cannot handle call %v from charging station %v. Sending CallError instead", requestId, chargingStationID) + err := cs.server.SendError(chargingStationID, requestId, ocppj.NotSupported, fmt.Sprintf("unsupported action %v on CSMS", action), nil) if err != nil { log.WithFields(log.Fields{ - "client": chargePointId, + "client": chargingStationID, "request": requestId, }).Errorf("unknown error %v while replying to message with CallError", err) } } -func (cs *csms) handleIncomingRequest(chargePointId string, request ocpp.Request, requestId string, action string) { +func (cs *csms) handleIncomingRequest(chargingStationID string, request ocpp.Request, requestId string, action string) { profile, found := cs.server.GetProfileForFeature(action) // Check whether action is supported and a listener for it exists if !found { - cs.notImplementedError(chargePointId, requestId, action) + cs.notImplementedError(chargingStationID, requestId, action) return } else { + supported := true switch profile.Name { - case CoreProfileName: - if cs.coreListener == nil { - cs.notSupportedError(chargePointId, requestId, action) - return + case authorization.ProfileName: + if cs.authorizationHandler == nil { + supported = false + } + case availability.ProfileName: + if cs.availabilityHandler == nil { + supported = false + } + case data.ProfileName: + if cs.dataHandler == nil { + supported = false + } + case diagnostics.ProfileName: + if cs.diagnosticsHandler == nil { + supported = false } - //case LocalAuthListProfileName: - // if cs.localAuthListListener == nil { - // cs.notSupportedError(chargePointId, requestId, action) - // return - // } - //case FirmwareManagementProfileName: - // if cs.firmwareListener == nil { - // cs.notSupportedError(chargePointId, requestId, action) - // return - // } - //case ReservationProfileName: - // if cs.reservationListener == nil { - // cs.notSupportedError(chargePointId, requestId, action) - // return - // } - //case RemoteTriggerProfileName: - // if cs.remoteTriggerListener == nil { - // cs.notSupportedError(chargePointId, requestId, action) - // return - // } - //case SmartChargingProfileName: - // if cs.smartChargingListener == nil { - // cs.notSupportedError(chargePointId, requestId, action) - // return - // } + case display.ProfileName: + if cs.displayHandler == nil { + supported = false + } + case firmware.ProfileName: + if cs.firmwareHandler == nil { + supported = false + } + case iso15118.ProfileName: + if cs.iso15118Handler == nil { + supported = false + } + case localauth.ProfileName: + if cs.localAuthListHandler == nil { + supported = false + } + case meter.ProfileName: + if cs.meterHandler == nil { + supported = false + } + case provisioning.ProfileName: + if cs.provisioningHandler == nil { + supported = false + } + case remotecontrol.ProfileName: + if cs.remoteControlHandler == nil { + supported = false + } + case reservation.ProfileName: + if cs.reservationHandler == nil { + supported = false + } + case security.ProfileName: + if cs.securityHandler == nil { + supported = false + } + case smartcharging.ProfileName: + if cs.smartChargingHandler == nil { + supported = false + } + case tariffcost.ProfileName: + if cs.tariffCostHandler == nil { + supported = false + } + case transactions.ProfileName: + if cs.transactionsHandler == nil { + supported = false + } + } + if !supported { + cs.notSupportedError(chargingStationID, requestId, action) + return } } - var confirmation ocpp.Confirmation = nil + var response ocpp.Response = nil var err error = nil // Execute in separate goroutine, so the caller goroutine is available go func() { switch action { - case BootNotificationFeatureName: - confirmation, err = cs.coreListener.OnBootNotification(chargePointId, request.(*BootNotificationRequest)) - case AuthorizeFeatureName: - confirmation, err = cs.coreListener.OnAuthorize(chargePointId, request.(*AuthorizeRequest)) - case ClearedChargingLimitFeatureName: - confirmation, err = cs.coreListener.OnClearedChargingLimit(chargePointId, request.(*ClearedChargingLimitRequest)) - case DataTransferFeatureName: - confirmation, err = cs.coreListener.OnDataTransfer(chargePointId, request.(*DataTransferRequest)) - case FirmwareStatusNotificationFeatureName: - confirmation, err = cs.coreListener.OnFirmwareStatusNotification(chargePointId, request.(*FirmwareStatusNotificationRequest)) - case Get15118EVCertificateFeatureName: - confirmation, err = cs.coreListener.OnGet15118EVCertificate(chargePointId, request.(*Get15118EVCertificateRequest)) - case GetCertificateStatusFeatureName: - confirmation, err = cs.coreListener.OnGetCertificateStatus(chargePointId, request.(*GetCertificateStatusRequest)) - //case HeartbeatFeatureName: - // confirmation, err = cs.messageHandler.OnHeartbeat(chargePointId, request.(*HeartbeatRequest)) - //case MeterValuesFeatureName: - // confirmation, err = cs.messageHandler.OnMeterValues(chargePointId, request.(*MeterValuesRequest)) - //case StartTransactionFeatureName: - // confirmation, err = cs.messageHandler.OnStartTransaction(chargePointId, request.(*StartTransactionRequest)) - //case StopTransactionFeatureName: - // confirmation, err = cs.messageHandler.OnStopTransaction(chargePointId, request.(*StopTransactionRequest)) - //case StatusNotificationFeatureName: - // confirmation, err = cs.messageHandler.OnStatusNotification(chargePointId, request.(*StatusNotificationRequest)) - //case DiagnosticsStatusNotificationFeatureName: - // confirmation, err = cs.firmwareListener.OnDiagnosticsStatusNotification(chargePointId, request.(*DiagnosticsStatusNotificationRequest)) - //case FirmwareStatusNotificationFeatureName: - // confirmation, err = cs.firmwareListener.OnFirmwareStatusNotification(chargePointId, request.(*FirmwareStatusNotificationRequest)) + case provisioning.BootNotificationFeatureName: + response, err = cs.provisioningHandler.OnBootNotification(chargingStationID, request.(*provisioning.BootNotificationRequest)) + case authorization.AuthorizeFeatureName: + response, err = cs.authorizationHandler.OnAuthorize(chargingStationID, request.(*authorization.AuthorizeRequest)) + case smartcharging.ClearedChargingLimitFeatureName: + response, err = cs.smartChargingHandler.OnClearedChargingLimit(chargingStationID, request.(*smartcharging.ClearedChargingLimitRequest)) + case data.DataTransferFeatureName: + response, err = cs.dataHandler.OnDataTransfer(chargingStationID, request.(*data.DataTransferRequest)) + case firmware.FirmwareStatusNotificationFeatureName: + response, err = cs.firmwareHandler.OnFirmwareStatusNotification(chargingStationID, request.(*firmware.FirmwareStatusNotificationRequest)) + case iso15118.Get15118EVCertificateFeatureName: + response, err = cs.iso15118Handler.OnGet15118EVCertificate(chargingStationID, request.(*iso15118.Get15118EVCertificateRequest)) + case iso15118.GetCertificateStatusFeatureName: + response, err = cs.iso15118Handler.OnGetCertificateStatus(chargingStationID, request.(*iso15118.GetCertificateStatusRequest)) default: - cs.notSupportedError(chargePointId, requestId, action) + cs.notSupportedError(chargingStationID, requestId, action) return } - cs.sendResponse(chargePointId, confirmation, err, requestId) + cs.sendResponse(chargingStationID, response, err, requestId) }() } -func (cs *csms) handleIncomingConfirmation(chargePointId string, confirmation ocpp.Confirmation, requestId string) { - if callback, ok := cs.callbacks[chargePointId]; ok { - delete(cs.callbacks, chargePointId) - callback(confirmation, nil) +func (cs *csms) handleIncomingResponse(chargingStationID string, response ocpp.Response, requestId string) { + if callback, ok := cs.callbacks[chargingStationID]; ok { + delete(cs.callbacks, chargingStationID) + callback(response, nil) } else { log.WithFields(log.Fields{ - "client": chargePointId, + "client": chargingStationID, "request": requestId, - }).Errorf("no handler available for Call Result of type %v", confirmation.GetFeatureName()) + }).Errorf("no handler available for Call Result of type %v", response.GetFeatureName()) } } -func (cs *csms) handleIncomingError(chargePointId string, err *ocpp.Error, details interface{}) { - if callback, ok := cs.callbacks[chargePointId]; ok { - delete(cs.callbacks, chargePointId) +func (cs *csms) handleIncomingError(chargingStationID string, err *ocpp.Error, details interface{}) { + if callback, ok := cs.callbacks[chargingStationID]; ok { + delete(cs.callbacks, chargingStationID) callback(nil, err) } else { + //TODO: print details log.WithFields(log.Fields{ - "client": chargePointId, + "client": chargingStationID, "request": err.MessageId, }).Errorf("no handler available for Call Error %v", err.Code) } diff --git a/ocpp2.0/data/data.go b/ocpp2.0/data/data.go new file mode 100644 index 00000000..15dcd04b --- /dev/null +++ b/ocpp2.0/data/data.go @@ -0,0 +1,23 @@ +// The data transfer functional block enables parties to add custom commands and extensions to OCPP 2.0. +package data + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Data transfer profile. +type CSMSHandler interface { + // OnDataTransfer is called on the CSMS whenever a DataTransferRequest is received from a charging station. + OnDataTransfer(chargingStationID string, request *DataTransferRequest) (confirmation *DataTransferResponse, err error) +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Data transfer profile. +type ChargingStationHandler interface { + // OnDataTransfer is called on a charging station whenever a DataTransferRequest is received from the CSMS. + OnDataTransfer(request *DataTransferRequest) (confirmation *DataTransferResponse, err error) +} + +const ProfileName = "data" + +var Profile = ocpp.NewProfile( + ProfileName, + DataTransferFeature{}, + ) diff --git a/ocpp2.0/data_transfer.go b/ocpp2.0/data/data_transfer.go similarity index 73% rename from ocpp2.0/data_transfer.go rename to ocpp2.0/data/data_transfer.go index afc5e53d..3264c64e 100644 --- a/ocpp2.0/data_transfer.go +++ b/ocpp2.0/data/data_transfer.go @@ -1,13 +1,16 @@ -package ocpp2 +package data import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Data Transfer (CS -> CSMS / CSMS -> CS) -------------------- -// Status in DataTransferConfirmation messages. +const DataTransferFeatureName = "DataTransfer" + +// Status in DataTransferResponse messages. type DataTransferStatus string const ( @@ -34,9 +37,9 @@ type DataTransferRequest struct { VendorId string `json:"vendorId" validate:"required,max=255"` } -// This field definition of the DataTransfer confirmation payload, sent by an endpoint in response to a DataTransferRequest, coming from the other endpoint. +// This field definition of the DataTransfer response payload, sent by an endpoint in response to a DataTransferRequest, coming from the other endpoint. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type DataTransferConfirmation struct { +type DataTransferResponse struct { Status DataTransferStatus `json:"status" validate:"required,dataTransferStatus"` Data interface{} `json:"data,omitempty"` } @@ -53,15 +56,15 @@ func (f DataTransferFeature) GetRequestType() reflect.Type { return reflect.TypeOf(DataTransferRequest{}) } -func (f DataTransferFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(DataTransferConfirmation{}) +func (f DataTransferFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(DataTransferResponse{}) } func (r DataTransferRequest) GetFeatureName() string { return DataTransferFeatureName } -func (c DataTransferConfirmation) GetFeatureName() string { +func (c DataTransferResponse) GetFeatureName() string { return DataTransferFeatureName } @@ -70,11 +73,11 @@ func NewDataTransferRequest(vendorId string) *DataTransferRequest { return &DataTransferRequest{VendorId: vendorId} } -// Creates a new DataTransferConfirmation. Optional fields may be set afterwards. -func NewDataTransferConfirmation(status DataTransferStatus) *DataTransferConfirmation { - return &DataTransferConfirmation{Status: status} +// Creates a new DataTransferResponse. Optional fields may be set afterwards. +func NewDataTransferResponse(status DataTransferStatus) *DataTransferResponse { + return &DataTransferResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("dataTransferStatus", isValidDataTransferStatus) + _ = types.Validate.RegisterValidation("dataTransferStatus", isValidDataTransferStatus) } diff --git a/ocpp2.0/clear_variable_monitoring.go b/ocpp2.0/diagnostics/clear_variable_monitoring.go similarity index 69% rename from ocpp2.0/clear_variable_monitoring.go rename to ocpp2.0/diagnostics/clear_variable_monitoring.go index 290d1391..a081c6c3 100644 --- a/ocpp2.0/clear_variable_monitoring.go +++ b/ocpp2.0/diagnostics/clear_variable_monitoring.go @@ -1,12 +1,15 @@ -package ocpp2 +package diagnostics import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Clear Variable Monitoring (CSMS -> CP) -------------------- +const ClearVariableMonitoringFeatureName = "ClearVariableMonitoring" + // Status contained inside a ClearMonitoringResult struct. type ClearMonitoringStatus string @@ -36,15 +39,15 @@ type ClearVariableMonitoringRequest struct { ID []int `json:"id" validate:"required,min=1,dive,gte=0"` // List of the monitors to be cleared, identified by their Id. } -// This field definition of the ClearVariableMonitoring confirmation payload, sent by the Charging Station to the CSMS in response to a ClearVariableMonitoringRequest. +// This field definition of the ClearVariableMonitoring response payload, sent by the Charging Station to the CSMS in response to a ClearVariableMonitoringRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type ClearVariableMonitoringConfirmation struct { +type ClearVariableMonitoringResponse struct { ClearMonitoringResult []ClearMonitoringResult `json:"clearMonitoringResult" validate:"required,min=1,dive"` // List of result statuses per monitor. } // The CSMS asks the Charging Station to clear a display message that has been configured in the Charging Station to be cleared/removed. // The Charging station checks for a message with the requested ID and removes it. -// The Charging station then responds with a ClearVariableMonitoringConfirmation. The response payload indicates whether the Charging Station was able to remove the message from display or not. +// The Charging station then responds with a ClearVariableMonitoringResponse. The response payload indicates whether the Charging Station was able to remove the message from display or not. type ClearVariableMonitoringFeature struct{} func (f ClearVariableMonitoringFeature) GetFeatureName() string { @@ -55,15 +58,15 @@ func (f ClearVariableMonitoringFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ClearVariableMonitoringRequest{}) } -func (f ClearVariableMonitoringFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(ClearVariableMonitoringConfirmation{}) +func (f ClearVariableMonitoringFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(ClearVariableMonitoringResponse{}) } func (r ClearVariableMonitoringRequest) GetFeatureName() string { return ClearVariableMonitoringFeatureName } -func (c ClearVariableMonitoringConfirmation) GetFeatureName() string { +func (c ClearVariableMonitoringResponse) GetFeatureName() string { return ClearVariableMonitoringFeatureName } @@ -72,11 +75,11 @@ func NewClearVariableMonitoringRequest(id []int) *ClearVariableMonitoringRequest return &ClearVariableMonitoringRequest{ID: id} } -// Creates a new ClearVariableMonitoringConfirmation, containing all required fields. There are no optional fields for this message. -func NewClearVariableMonitoringConfirmation(result []ClearMonitoringResult) *ClearVariableMonitoringConfirmation { - return &ClearVariableMonitoringConfirmation{ClearMonitoringResult: result} +// Creates a new ClearVariableMonitoringResponse, containing all required fields. There are no optional fields for this message. +func NewClearVariableMonitoringResponse(result []ClearMonitoringResult) *ClearVariableMonitoringResponse { + return &ClearVariableMonitoringResponse{ClearMonitoringResult: result} } func init() { - _ = Validate.RegisterValidation("clearMonitoringStatus", isValidClearMonitoringStatus) + _ = types.Validate.RegisterValidation("clearMonitoringStatus", isValidClearMonitoringStatus) } diff --git a/ocpp2.0/customer_information.go b/ocpp2.0/diagnostics/customer_information.go similarity index 60% rename from ocpp2.0/customer_information.go rename to ocpp2.0/diagnostics/customer_information.go index 9d2064c6..a7b4899a 100644 --- a/ocpp2.0/customer_information.go +++ b/ocpp2.0/diagnostics/customer_information.go @@ -1,12 +1,15 @@ -package ocpp2 +package diagnostics import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Customer Information (CSMS -> CS) -------------------- +const CustomerInformationFeatureName = "CustomerInformation" + // Status returned in response to CustomerInformationRequest. type CustomerInformationStatus string @@ -28,23 +31,23 @@ func isValidCustomerInformationStatus(fl validator.FieldLevel) bool { // The field definition of the CustomerInformation request payload sent by the CSMS to the Charging Station. type CustomerInformationRequest struct { - RequestID int `json:"requestId" validate:"gte=0"` - Report bool `json:"report"` - Clear bool `json:"clear"` - CustomerIdentifier string `json:"customerIdentifier,omitempty" validate:"max=64"` - IdToken *IdToken `json:"idToken,omitempty" validate:"omitempty,dive"` - CustomerCertificate *CertificateHashData `json:"customerCertificate,omitempty" validate:"omitempty,dive"` + RequestID int `json:"requestId" validate:"gte=0"` + Report bool `json:"report"` + Clear bool `json:"clear"` + CustomerIdentifier string `json:"customerIdentifier,omitempty" validate:"max=64"` + IdToken *types.IdToken `json:"idToken,omitempty" validate:"omitempty,dive"` + CustomerCertificate *types.CertificateHashData `json:"customerCertificate,omitempty" validate:"omitempty,dive"` } -// This field definition of the CustomerInformation confirmation payload, sent by the Charging Station to the CSMS in response to a CustomerInformationRequest. +// This field definition of the CustomerInformation response payload, sent by the Charging Station to the CSMS in response to a CustomerInformationRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type CustomerInformationConfirmation struct { +type CustomerInformationResponse struct { Status CustomerInformationStatus `json:"status" validate:"required,customerInformationStatus"` } // CSMS can request a Charging Station to clear its Authorization Cache. // The CSMS SHALL send a CustomerInformationRequest payload for clearing the Charging Station’s Authorization Cache. -// Upon receipt of a CustomerInformationRequest, the Charging Station SHALL respond with a CustomerInformationConfirmation payload. +// Upon receipt of a CustomerInformationRequest, the Charging Station SHALL respond with a CustomerInformationResponse payload. // The response payload SHALL indicate whether the Charging Station was able to clear its Authorization Cache. type CustomerInformationFeature struct{} @@ -56,15 +59,15 @@ func (f CustomerInformationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(CustomerInformationRequest{}) } -func (f CustomerInformationFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(CustomerInformationConfirmation{}) +func (f CustomerInformationFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(CustomerInformationResponse{}) } func (r CustomerInformationRequest) GetFeatureName() string { return CustomerInformationFeatureName } -func (c CustomerInformationConfirmation) GetFeatureName() string { +func (c CustomerInformationResponse) GetFeatureName() string { return CustomerInformationFeatureName } @@ -73,11 +76,11 @@ func NewCustomerInformationRequest(requestId int, report bool, clear bool) *Cust return &CustomerInformationRequest{RequestID: requestId, Report: report, Clear: clear} } -// Creates a new CustomerInformationConfirmation, containing all required fields. There are no optional fields for this message. -func NewCustomerInformationConfirmation(status CustomerInformationStatus) *CustomerInformationConfirmation { - return &CustomerInformationConfirmation{Status: status} +// Creates a new CustomerInformationResponse, containing all required fields. There are no optional fields for this message. +func NewCustomerInformationResponse(status CustomerInformationStatus) *CustomerInformationResponse { + return &CustomerInformationResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("customerInformationStatus", isValidCustomerInformationStatus) + _ = types.Validate.RegisterValidation("customerInformationStatus", isValidCustomerInformationStatus) } diff --git a/ocpp2.0/diagnostics/diagnostics.go b/ocpp2.0/diagnostics/diagnostics.go new file mode 100644 index 00000000..886d9fc8 --- /dev/null +++ b/ocpp2.0/diagnostics/diagnostics.go @@ -0,0 +1,30 @@ +// The diagnostics functional block contains OCPP 2.0 features than enable remote diagnostics of problems with a charging station. +package diagnostics + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Diagnostics profile. +type CSMSHandler interface { +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Diagnostics profile. +type ChargingStationHandler interface { + // OnClearVariableMonitoring is called on a charging station whenever a ClearVariableMonitoringRequest is received from the CSMS. + OnClearVariableMonitoring(request *ClearVariableMonitoringRequest) (confirmation *ClearVariableMonitoringResponse, err error) + // OnCustomerInformation is called on a charging station whenever a CustomerInformationRequest is received from the CSMS. + OnCustomerInformation(request *CustomerInformationRequest) (confirmation *CustomerInformationResponse, err error) + // OnGetLog is called on a charging station whenever a GetLogRequest is received from the CSMS. + OnGetLog(request *GetLogRequest) (confirmation *GetLogResponse, err error) + // OnGetMonitoringReport is called on a charging station whenever a GetMonitoringReportRequest is received from the CSMS. + OnGetMonitoringReport(request *GetMonitoringReportRequest) (confirmation *GetMonitoringReportResponse, err error) +} + +const ProfileName = "diagnostics" + +var Profile = ocpp.NewProfile( + ProfileName, + ClearVariableMonitoringFeature{}, + CustomerInformationFeature{}, + GetLogFeature{}, + GetMonitoringReportFeature{}, + ) diff --git a/ocpp2.0/get_log.go b/ocpp2.0/diagnostics/get_log.go similarity index 76% rename from ocpp2.0/get_log.go rename to ocpp2.0/diagnostics/get_log.go index e87cb1ea..31cd461d 100644 --- a/ocpp2.0/get_log.go +++ b/ocpp2.0/diagnostics/get_log.go @@ -1,16 +1,19 @@ -package ocpp2 +package diagnostics import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Get Log (CSMS -> CS) -------------------- +const GetLogFeatureName = "GetLog" + // LogType represents the type of log file that the Charging Station should send. It is used in GetLogRequest. type LogType string -// LogStatus represents the status returned by a Charging Station in a GetLogConfirmation. +// LogStatus represents the status returned by a Charging Station in a GetLogResponse. type LogStatus string const ( @@ -43,9 +46,9 @@ func isValidLogStatus(fl validator.FieldLevel) bool { // LogParameters specifies the requested log and the location to which the log should be sent. It is used in GetLogRequest. type LogParameters struct { - RemoteLocation string `json:"remoteLocation" validate:"required,max=512,url"` - OldestTimestamp *DateTime `json:"oldestTimestamp,omitempty" validate:"omitempty"` - LatestTimestamp *DateTime `json:"latestTimestamp,omitempty" validate:"omitempty"` + RemoteLocation string `json:"remoteLocation" validate:"required,max=512,url"` + OldestTimestamp *types.DateTime `json:"oldestTimestamp,omitempty" validate:"omitempty"` + LatestTimestamp *types.DateTime `json:"latestTimestamp,omitempty" validate:"omitempty"` } // The field definition of the GetLog request payload sent by the CSMS to the Charging Station. @@ -57,16 +60,16 @@ type GetLogRequest struct { Log LogParameters `json:"log" validate:"required"` } -// This field definition of the GetLog confirmation payload, sent by the Charging Station to the CSMS in response to a GetLogRequest. +// This field definition of the GetLog response payload, sent by the Charging Station to the CSMS in response to a GetLogRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type GetLogConfirmation struct { +type GetLogResponse struct { Status LogStatus `json:"status" validate:"required,logStatus"` // This field indicates whether the Charging Station was able to accept the request. Filename string `json:"filename,omitempty" validate:"omitempty,max=256"` // This contains the name of the log file that will be uploaded. This field is not present when no logging information is available. } // The CSO may trigger the CSMS to request a report from a Charging Station. // The CSMS shall then request a Charging Station to send a predefined report as defined in ReportBase. -// The Charging Station responds with GetLogConfirmation. +// The Charging Station responds with GetLogResponse. // The result will be returned asynchronously in one or more NotifyReportRequest messages (one for each report part). type GetLogFeature struct{} @@ -78,15 +81,15 @@ func (f GetLogFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetLogRequest{}) } -func (f GetLogFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(GetLogConfirmation{}) +func (f GetLogFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(GetLogResponse{}) } func (r GetLogRequest) GetFeatureName() string { return GetLogFeatureName } -func (c GetLogConfirmation) GetFeatureName() string { +func (c GetLogResponse) GetFeatureName() string { return GetLogFeatureName } @@ -95,12 +98,12 @@ func NewGetLogRequest(logType LogType, requestID int, logParameters LogParameter return &GetLogRequest{LogType: logType, RequestID: requestID, Log: logParameters} } -// Creates a new GetLogConfirmation, containing all required fields. Optional fields may be set afterwards. -func NewGetLogConfirmation(status LogStatus) *GetLogConfirmation { - return &GetLogConfirmation{Status: status} +// Creates a new GetLogResponse, containing all required fields. Optional fields may be set afterwards. +func NewGetLogResponse(status LogStatus) *GetLogResponse { + return &GetLogResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("logType", isValidLogType) - _ = Validate.RegisterValidation("logStatus", isValidLogStatus) + _ = types.Validate.RegisterValidation("logType", isValidLogType) + _ = types.Validate.RegisterValidation("logStatus", isValidLogStatus) } diff --git a/ocpp2.0/get_monitoring_report.go b/ocpp2.0/diagnostics/get_monitoring_report.go similarity index 56% rename from ocpp2.0/get_monitoring_report.go rename to ocpp2.0/diagnostics/get_monitoring_report.go index a37017ab..9a9cec90 100644 --- a/ocpp2.0/get_monitoring_report.go +++ b/ocpp2.0/diagnostics/get_monitoring_report.go @@ -1,12 +1,15 @@ -package ocpp2 +package diagnostics import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Get Monitoring Report (CSMS -> CS) -------------------- +const GetMonitoringReportFeatureName = "GetMonitoringReport" + // Monitoring criteria contained in GetMonitoringReportRequest. type MonitoringCriteriaType string @@ -28,15 +31,15 @@ func isValidMonitoringCriteriaType(fl validator.FieldLevel) bool { // The field definition of the GetMonitoringReport request payload sent by the CSMS to the Charging Station. type GetMonitoringReportRequest struct { - RequestID *int `json:"requestId,omitempty" validate:"omitempty,gte=0"` // The Id of the request. - MonitoringCriteria []MonitoringCriteriaType `json:"monitoringCriteria,omitempty" validate:"omitempty,max=3,dive,monitoringCriteria"` // This field contains criteria for components for which a monitoring report is requested. - ComponentVariable []ComponentVariable `json:"componentVariable,omitempty" validate:"omitempty,dive"` // This field specifies the components and variables for which a monitoring report is requested. + RequestID *int `json:"requestId,omitempty" validate:"omitempty,gte=0"` // The Id of the request. + MonitoringCriteria []MonitoringCriteriaType `json:"monitoringCriteria,omitempty" validate:"omitempty,max=3,dive,monitoringCriteria"` // This field contains criteria for components for which a monitoring report is requested. + ComponentVariable []types.ComponentVariable `json:"componentVariable,omitempty" validate:"omitempty,dive"` // This field specifies the components and variables for which a monitoring report is requested. } -// This field definition of the GetMonitoringReport confirmation payload, sent by the Charging Station to the CSMS in response to a GetMonitoringReportRequest. +// This field definition of the GetMonitoringReport response payload, sent by the Charging Station to the CSMS in response to a GetMonitoringReportRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type GetMonitoringReportConfirmation struct { - Status GenericDeviceModelStatus `json:"status" validate:"required,genericDeviceModelStatus"` // This field indicates whether the Charging Station was able to accept the request. +type GetMonitoringReportResponse struct { + Status types.GenericDeviceModelStatus `json:"status" validate:"required,genericDeviceModelStatus"` // This field indicates whether the Charging Station was able to accept the request. } // A CSMS can request the Charging Station to send a report about configured monitoring settings per component and variable. @@ -54,15 +57,15 @@ func (f GetMonitoringReportFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetMonitoringReportRequest{}) } -func (f GetMonitoringReportFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(GetMonitoringReportConfirmation{}) +func (f GetMonitoringReportFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(GetMonitoringReportResponse{}) } func (r GetMonitoringReportRequest) GetFeatureName() string { return GetMonitoringReportFeatureName } -func (c GetMonitoringReportConfirmation) GetFeatureName() string { +func (c GetMonitoringReportResponse) GetFeatureName() string { return GetMonitoringReportFeatureName } @@ -71,11 +74,11 @@ func NewGetMonitoringReportRequest() *GetMonitoringReportRequest { return &GetMonitoringReportRequest{} } -// Creates a new GetMonitoringReportConfirmation, containing all required fields. There are no optional fields for this message. -func NewGetMonitoringReportConfirmation(status GenericDeviceModelStatus) *GetMonitoringReportConfirmation { - return &GetMonitoringReportConfirmation{Status: status} +// Creates a new GetMonitoringReportResponse, containing all required fields. There are no optional fields for this message. +func NewGetMonitoringReportResponse(status types.GenericDeviceModelStatus) *GetMonitoringReportResponse { + return &GetMonitoringReportResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("monitoringCriteria", isValidMonitoringCriteriaType) + _ = types.Validate.RegisterValidation("monitoringCriteria", isValidMonitoringCriteriaType) } diff --git a/ocpp2.0/clear_display.go b/ocpp2.0/display/clear_display.go similarity index 64% rename from ocpp2.0/clear_display.go rename to ocpp2.0/display/clear_display.go index 1d2753e3..2476bb1e 100644 --- a/ocpp2.0/clear_display.go +++ b/ocpp2.0/display/clear_display.go @@ -1,12 +1,15 @@ -package ocpp2 +package display import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Clear Display (CSMS -> CS) -------------------- +const ClearDisplayFeatureName = "ClearDisplay" + // Status returned in response to ClearDisplayRequest. type ClearMessageStatus string @@ -30,15 +33,15 @@ type ClearDisplayRequest struct { ID int `json:"id" validate:"required,gte=0"` } -// This field definition of the ClearDisplay confirmation payload, sent by the Charging Station to the CSMS in response to a ClearDisplayRequest. +// This field definition of the ClearDisplay response payload, sent by the Charging Station to the CSMS in response to a ClearDisplayRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type ClearDisplayConfirmation struct { +type ClearDisplayResponse struct { Status ClearMessageStatus `json:"status" validate:"required,clearMessageStatus"` } // The CSMS asks the Charging Station to clear a display message that has been configured in the Charging Station to be cleared/removed. // The Charging station checks for a message with the requested ID and removes it. -// The Charging station then responds with a ClearDisplayConfirmation. The response payload indicates whether the Charging Station was able to remove the message from display or not. +// The Charging station then responds with a ClearDisplayResponse. The response payload indicates whether the Charging Station was able to remove the message from display or not. type ClearDisplayFeature struct{} func (f ClearDisplayFeature) GetFeatureName() string { @@ -49,15 +52,15 @@ func (f ClearDisplayFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ClearDisplayRequest{}) } -func (f ClearDisplayFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(ClearDisplayConfirmation{}) +func (f ClearDisplayFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(ClearDisplayResponse{}) } func (r ClearDisplayRequest) GetFeatureName() string { return ClearDisplayFeatureName } -func (c ClearDisplayConfirmation) GetFeatureName() string { +func (c ClearDisplayResponse) GetFeatureName() string { return ClearDisplayFeatureName } @@ -66,11 +69,11 @@ func NewClearDisplayRequest(id int) *ClearDisplayRequest { return &ClearDisplayRequest{ID: id} } -// Creates a new ClearDisplayConfirmation, containing all required fields. There are no optional fields for this message. -func NewClearDisplayConfirmation(status ClearMessageStatus) *ClearDisplayConfirmation { - return &ClearDisplayConfirmation{Status: status} +// Creates a new ClearDisplayResponse, containing all required fields. There are no optional fields for this message. +func NewClearDisplayResponse(status ClearMessageStatus) *ClearDisplayResponse { + return &ClearDisplayResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("clearMessageStatus", isValidClearMessageStatus) + _ = types.Validate.RegisterValidation("clearMessageStatus", isValidClearMessageStatus) } diff --git a/ocpp2.0/display/display.go b/ocpp2.0/display/display.go new file mode 100644 index 00000000..92c2048a --- /dev/null +++ b/ocpp2.0/display/display.go @@ -0,0 +1,24 @@ +// The display functional block contains OCPP 2.0 features for managing message that get displayed on a charging station. +package display + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Display profile. +type CSMSHandler interface { +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Display profile. +type ChargingStationHandler interface { + // OnClearDisplay is called on a charging station whenever a ClearDisplayRequest is received from the CSMS. + OnClearDisplay(request *ClearDisplayRequest) (confirmation *ClearDisplayResponse, err error) + // OnGetDisplayMessages is called on a charging station whenever a GetDisplayMessagesRequest is received from the CSMS. + OnGetDisplayMessages(request *GetDisplayMessagesRequest) (confirmation *GetDisplayMessagesResponse, err error) +} + +const ProfileName = "display" + +var Profile = ocpp.NewProfile( + ProfileName, + ClearDisplayFeature{}, + GetDisplayMessagesFeature{}, + ) diff --git a/ocpp2.0/get_display_messages.go b/ocpp2.0/display/get_display_messages.go similarity index 79% rename from ocpp2.0/get_display_messages.go rename to ocpp2.0/display/get_display_messages.go index a8c4fd10..46b6d434 100644 --- a/ocpp2.0/get_display_messages.go +++ b/ocpp2.0/display/get_display_messages.go @@ -1,12 +1,15 @@ -package ocpp2 +package display import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Get Display Messages (CSMS -> CS) -------------------- +const GetDisplayMessagesFeatureName = "GetDisplayMessages" + // Priority with which a message should be displayed on a Charging Station. // Used within a GetDisplayMessagesRequest. type MessagePriority string @@ -15,7 +18,7 @@ type MessagePriority string // Used within a GetDisplayMessagesRequest. type MessageState string -// MessageStatus represents the status of the request, used in a GetDisplayMessagesConfirmation. +// MessageStatus represents the status of the request, used in a GetDisplayMessagesResponse. type MessageStatus string const ( @@ -68,9 +71,9 @@ type GetDisplayMessagesRequest struct { ID []int `json:"id,omitempty" validate:"omitempty,dive,gte=0"` } -// This field definition of the GetDisplayMessages confirmation payload, sent by the Charging Station to the CSMS in response to a GetDisplayMessagesRequest. +// This field definition of the GetDisplayMessages response payload, sent by the Charging Station to the CSMS in response to a GetDisplayMessagesRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type GetDisplayMessagesConfirmation struct { +type GetDisplayMessagesResponse struct { Status MessageStatus `json:"status" validate:"required,messageStatus"` } @@ -92,15 +95,15 @@ func (f GetDisplayMessagesFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetDisplayMessagesRequest{}) } -func (f GetDisplayMessagesFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(GetDisplayMessagesConfirmation{}) +func (f GetDisplayMessagesFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(GetDisplayMessagesResponse{}) } func (r GetDisplayMessagesRequest) GetFeatureName() string { return GetDisplayMessagesFeatureName } -func (c GetDisplayMessagesConfirmation) GetFeatureName() string { +func (c GetDisplayMessagesResponse) GetFeatureName() string { return GetDisplayMessagesFeatureName } @@ -109,13 +112,13 @@ func NewGetDisplayMessagesRequest(requestId int) *GetDisplayMessagesRequest { return &GetDisplayMessagesRequest{RequestID: requestId} } -// Creates a new GetDisplayMessagesConfirmation, containing all required fields. There are no optional fields for this message. -func NewGetDisplayMessagesConfirmation(status MessageStatus) *GetDisplayMessagesConfirmation { - return &GetDisplayMessagesConfirmation{Status: status} +// Creates a new GetDisplayMessagesResponse, containing all required fields. There are no optional fields for this message. +func NewGetDisplayMessagesResponse(status MessageStatus) *GetDisplayMessagesResponse { + return &GetDisplayMessagesResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("messagePriority", isValidMessagePriority) - _ = Validate.RegisterValidation("messageState", isValidMessageState) - _ = Validate.RegisterValidation("messageStatus", isValidMessageStatus) + _ = types.Validate.RegisterValidation("messagePriority", isValidMessagePriority) + _ = types.Validate.RegisterValidation("messageState", isValidMessageState) + _ = types.Validate.RegisterValidation("messageStatus", isValidMessageStatus) } diff --git a/ocpp2.0/firmware/firmware.go b/ocpp2.0/firmware/firmware.go new file mode 100644 index 00000000..b3b98f8b --- /dev/null +++ b/ocpp2.0/firmware/firmware.go @@ -0,0 +1,21 @@ +// The firmware functional block contains OCPP 2.0 features that enable firmware updates on a charging station. +package firmware + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Firmware profile. +type CSMSHandler interface { + // OnFirmwareStatusNotification is called on the CSMS whenever a FirmwareStatusNotificationRequest is received from a charging station. + OnFirmwareStatusNotification(chargingStationID string, request *FirmwareStatusNotificationRequest) (confirmation *FirmwareStatusNotificationResponse, err error) +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Firmware profile. +type ChargingStationHandler interface { +} + +const ProfileName = "firmware" + +var Profile = ocpp.NewProfile( + ProfileName, + FirmwareStatusNotificationFeature{}, + ) diff --git a/ocpp2.0/firmware_status_notification.go b/ocpp2.0/firmware/firmware_status_notification.go similarity index 75% rename from ocpp2.0/firmware_status_notification.go rename to ocpp2.0/firmware/firmware_status_notification.go index a1d99023..dbdd23c0 100644 --- a/ocpp2.0/firmware_status_notification.go +++ b/ocpp2.0/firmware/firmware_status_notification.go @@ -1,12 +1,15 @@ -package ocpp2 +package firmware import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Firmware Status Notification (CS -> CSMS) -------------------- +const FirmwareStatusNotificationFeatureName = "FirmwareStatusNotification" + // Status reported in FirmwareStatusNotificationRequest. type FirmwareStatus string @@ -36,9 +39,9 @@ type FirmwareStatusNotificationRequest struct { RequestID int `json:"requestId" validate:"gte=0"` } -// This field definition of the FirmwareStatusNotification confirmation payload, sent by the CSMS to the Charging Station in response to a FirmwareStatusNotificationRequest. +// This field definition of the FirmwareStatusNotification response payload, sent by the CSMS to the Charging Station in response to a FirmwareStatusNotificationRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type FirmwareStatusNotificationConfirmation struct { +type FirmwareStatusNotificationResponse struct { } // The Charging Station sends a notification to inform the CSMS about the progress of the downloading and installation of a firmware update. @@ -54,15 +57,15 @@ func (f FirmwareStatusNotificationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(FirmwareStatusNotificationRequest{}) } -func (f FirmwareStatusNotificationFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(FirmwareStatusNotificationConfirmation{}) +func (f FirmwareStatusNotificationFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(FirmwareStatusNotificationResponse{}) } func (r FirmwareStatusNotificationRequest) GetFeatureName() string { return FirmwareStatusNotificationFeatureName } -func (c FirmwareStatusNotificationConfirmation) GetFeatureName() string { +func (c FirmwareStatusNotificationResponse) GetFeatureName() string { return FirmwareStatusNotificationFeatureName } @@ -71,11 +74,11 @@ func NewFirmwareStatusNotificationRequest(status FirmwareStatus, requestId int) return &FirmwareStatusNotificationRequest{Status: status, RequestID: requestId} } -// Creates a new FirmwareStatusNotificationConfirmation, which doesn't contain any required or optional fields. -func NewFirmwareStatusNotificationConfirmation() *FirmwareStatusNotificationConfirmation { - return &FirmwareStatusNotificationConfirmation{} +// Creates a new FirmwareStatusNotificationResponse, which doesn't contain any required or optional fields. +func NewFirmwareStatusNotificationResponse() *FirmwareStatusNotificationResponse { + return &FirmwareStatusNotificationResponse{} } func init() { - _ = Validate.RegisterValidation("firmwareStatus", isValidFirmwareStatus) + _ = types.Validate.RegisterValidation("firmwareStatus", isValidFirmwareStatus) } diff --git a/ocpp2.0/delete_certificate.go b/ocpp2.0/iso15118/delete_certificate.go similarity index 63% rename from ocpp2.0/delete_certificate.go rename to ocpp2.0/iso15118/delete_certificate.go index 5b0eb448..b2364ee1 100644 --- a/ocpp2.0/delete_certificate.go +++ b/ocpp2.0/iso15118/delete_certificate.go @@ -1,12 +1,15 @@ -package ocpp2 +package iso15118 import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Clear Display (CSMS -> CS) -------------------- +const DeleteCertificateFeatureName = "DeleteCertificate" + // Status returned in response to DeleteCertificateRequest. type DeleteCertificateStatus string @@ -28,12 +31,12 @@ func isValidDeleteCertificateStatus(fl validator.FieldLevel) bool { // The field definition of the DeleteCertificate request payload sent by the CSMS to the Charging Station. type DeleteCertificateRequest struct { - CertificateHashData CertificateHashData `json:"certificateHashData" validate:"required"` + CertificateHashData types.CertificateHashData `json:"certificateHashData" validate:"required"` } -// This field definition of the DeleteCertificate confirmation payload, sent by the Charging Station to the CSMS in response to a DeleteCertificateRequest. +// This field definition of the DeleteCertificate response payload, sent by the Charging Station to the CSMS in response to a DeleteCertificateRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type DeleteCertificateConfirmation struct { +type DeleteCertificateResponse struct { Status DeleteCertificateStatus `json:"status" validate:"required,deleteCertificateStatus"` } @@ -49,28 +52,28 @@ func (f DeleteCertificateFeature) GetRequestType() reflect.Type { return reflect.TypeOf(DeleteCertificateRequest{}) } -func (f DeleteCertificateFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(DeleteCertificateConfirmation{}) +func (f DeleteCertificateFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(DeleteCertificateResponse{}) } func (r DeleteCertificateRequest) GetFeatureName() string { return DeleteCertificateFeatureName } -func (c DeleteCertificateConfirmation) GetFeatureName() string { +func (c DeleteCertificateResponse) GetFeatureName() string { return DeleteCertificateFeatureName } // Creates a new DeleteCertificateRequest, containing all required fields. There are no optional fields for this message. -func NewDeleteCertificateRequest(certificateHashData CertificateHashData) *DeleteCertificateRequest { +func NewDeleteCertificateRequest(certificateHashData types.CertificateHashData) *DeleteCertificateRequest { return &DeleteCertificateRequest{CertificateHashData: certificateHashData} } -// Creates a new DeleteCertificateConfirmation, containing all required fields. There are no optional fields for this message. -func NewDeleteCertificateConfirmation(status DeleteCertificateStatus) *DeleteCertificateConfirmation { - return &DeleteCertificateConfirmation{Status: status} +// Creates a new DeleteCertificateResponse, containing all required fields. There are no optional fields for this message. +func NewDeleteCertificateResponse(status DeleteCertificateStatus) *DeleteCertificateResponse { + return &DeleteCertificateResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("deleteCertificateStatus", isValidDeleteCertificateStatus) + _ = types.Validate.RegisterValidation("deleteCertificateStatus", isValidDeleteCertificateStatus) } diff --git a/ocpp2.0/get_15118ev_certificate.go b/ocpp2.0/iso15118/get_15118ev_certificate.go similarity index 54% rename from ocpp2.0/get_15118ev_certificate.go rename to ocpp2.0/iso15118/get_15118ev_certificate.go index a2ae42d7..7c372cdb 100644 --- a/ocpp2.0/get_15118ev_certificate.go +++ b/ocpp2.0/iso15118/get_15118ev_certificate.go @@ -1,11 +1,14 @@ -package ocpp2 +package iso15118 import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "reflect" ) // -------------------- Get 15118EV Certificate (CS -> CSMS) -------------------- +const Get15118EVCertificateFeatureName = "Get15118EVCertificate" + // Contains an X.509 certificate chain, each first DER encoded into binary, and then base64 encoded. type CertificateChain struct { Certificate string `json:"certificate" validate:"required,max=800"` @@ -18,13 +21,13 @@ type Get15118EVCertificateRequest struct { ExiRequest string `json:"exiRequest" validate:"required,max=5500"` } -// This field definition of the Get15118EVCertificate confirmation payload, sent by the CSMS to the Charging Station in response to a Get15118EVCertificateRequest. +// This field definition of the Get15118EVCertificate response payload, sent by the CSMS to the Charging Station in response to a Get15118EVCertificateRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type Get15118EVCertificateConfirmation struct { - Status Certificate15118EVStatus `json:"status" validate:"required,15118EVCertificate"` - ExiResponse string `json:"exiResponse" validate:"required,max=5500"` - ContractSignatureCertificateChain CertificateChain `json:"contractSignatureCertificateChain" validate:"required"` - SaProvisioningCertificateChain CertificateChain `json:"saProvisioningCertificateChain" validate:"required"` +type Get15118EVCertificateResponse struct { + Status types.Certificate15118EVStatus `json:"status" validate:"required,15118EVCertificate"` + ExiResponse string `json:"exiResponse" validate:"required,max=5500"` + ContractSignatureCertificateChain CertificateChain `json:"contractSignatureCertificateChain" validate:"required"` + SaProvisioningCertificateChain CertificateChain `json:"saProvisioningCertificateChain" validate:"required"` } // An EV connected to a Charging Station may request a new certificate. @@ -40,15 +43,15 @@ func (f Get15118EVCertificateFeature) GetRequestType() reflect.Type { return reflect.TypeOf(Get15118EVCertificateRequest{}) } -func (f Get15118EVCertificateFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(Get15118EVCertificateConfirmation{}) +func (f Get15118EVCertificateFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(Get15118EVCertificateResponse{}) } func (r Get15118EVCertificateRequest) GetFeatureName() string { return Get15118EVCertificateFeatureName } -func (c Get15118EVCertificateConfirmation) GetFeatureName() string { +func (c Get15118EVCertificateResponse) GetFeatureName() string { return Get15118EVCertificateFeatureName } @@ -57,9 +60,9 @@ func NewGet15118EVCertificateRequest(schemaVersion string, exiRequest string) *G return &Get15118EVCertificateRequest{SchemaVersion: schemaVersion, ExiRequest: exiRequest} } -// Creates a new Get15118EVCertificateConfirmation, containing all required fields. -func NewGet15118EVCertificateConfirmation(status Certificate15118EVStatus, exiResponse string, contractSignatureCertificateChain CertificateChain, saProvisioningCertificateChain CertificateChain) *Get15118EVCertificateConfirmation { - return &Get15118EVCertificateConfirmation{Status: status, ExiResponse: exiResponse, ContractSignatureCertificateChain: contractSignatureCertificateChain, SaProvisioningCertificateChain: saProvisioningCertificateChain} +// Creates a new Get15118EVCertificateResponse, containing all required fields. +func NewGet15118EVCertificateResponse(status types.Certificate15118EVStatus, exiResponse string, contractSignatureCertificateChain CertificateChain, saProvisioningCertificateChain CertificateChain) *Get15118EVCertificateResponse { + return &Get15118EVCertificateResponse{Status: status, ExiResponse: exiResponse, ContractSignatureCertificateChain: contractSignatureCertificateChain, SaProvisioningCertificateChain: saProvisioningCertificateChain} } func init() { diff --git a/ocpp2.0/get_certificate_status.go b/ocpp2.0/iso15118/get_certificate_status.go similarity index 55% rename from ocpp2.0/get_certificate_status.go rename to ocpp2.0/iso15118/get_certificate_status.go index 7ba8ce07..320f0e5e 100644 --- a/ocpp2.0/get_certificate_status.go +++ b/ocpp2.0/iso15118/get_certificate_status.go @@ -1,21 +1,24 @@ -package ocpp2 +package iso15118 import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "reflect" ) // -------------------- Get Certificate Status (CS -> CSMS) -------------------- +const GetCertificateStatusFeatureName = "GetCertificateStatus" + // The field definition of the GetCertificateStatus request payload sent by the Charging Station to the CSMS. type GetCertificateStatusRequest struct { - OcspRequestData OCSPRequestDataType `json:"ocspRequestData" validate:"required"` + OcspRequestData types.OCSPRequestDataType `json:"ocspRequestData" validate:"required"` } -// This field definition of the GetCertificateStatus confirmation payload, sent by the CSMS to the Charging Station in response to a GetCertificateStatusRequest. +// This field definition of the GetCertificateStatus response payload, sent by the CSMS to the Charging Station in response to a GetCertificateStatusRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type GetCertificateStatusConfirmation struct { - Status GenericStatus `json:"status" validate:"required,genericStatus"` - OcspResult string `json:"ocspResult,omitempty" validate:"omitempty,max=5500"` +type GetCertificateStatusResponse struct { + Status types.GenericStatus `json:"status" validate:"required,genericStatus"` + OcspResult string `json:"ocspResult,omitempty" validate:"omitempty,max=5500"` } // For 15118 certificate installation on EVs, the Charging Station requests the CSMS to provide the OCSP certificate @@ -33,24 +36,24 @@ func (f GetCertificateStatusFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetCertificateStatusRequest{}) } -func (f GetCertificateStatusFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(GetCertificateStatusConfirmation{}) +func (f GetCertificateStatusFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(GetCertificateStatusResponse{}) } func (r GetCertificateStatusRequest) GetFeatureName() string { return GetCertificateStatusFeatureName } -func (c GetCertificateStatusConfirmation) GetFeatureName() string { +func (c GetCertificateStatusResponse) GetFeatureName() string { return GetCertificateStatusFeatureName } // Creates a new GetCertificateStatusRequest, containing all required fields. There are no optional fields for this message. -func NewGetCertificateStatusRequest(ocspRequestData OCSPRequestDataType) *GetCertificateStatusRequest { +func NewGetCertificateStatusRequest(ocspRequestData types.OCSPRequestDataType) *GetCertificateStatusRequest { return &GetCertificateStatusRequest{OcspRequestData: ocspRequestData} } -// Creates a new GetCertificateStatusConfirmation, containing all required fields. Optional fields may be set afterwards. -func NewGetCertificateStatusConfirmation(status GenericStatus) *GetCertificateStatusConfirmation { - return &GetCertificateStatusConfirmation{Status: status} +// Creates a new GetCertificateStatusResponse, containing all required fields. Optional fields may be set afterwards. +func NewGetCertificateStatusResponse(status types.GenericStatus) *GetCertificateStatusResponse { + return &GetCertificateStatusResponse{Status: status} } diff --git a/ocpp2.0/get_installed_certificate_ids.go b/ocpp2.0/iso15118/get_installed_certificate_ids.go similarity index 63% rename from ocpp2.0/get_installed_certificate_ids.go rename to ocpp2.0/iso15118/get_installed_certificate_ids.go index 28c65378..9c170b60 100644 --- a/ocpp2.0/get_installed_certificate_ids.go +++ b/ocpp2.0/iso15118/get_installed_certificate_ids.go @@ -1,12 +1,15 @@ -package ocpp2 +package iso15118 import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Get Installed Certificate IDs (CSMS -> CS) -------------------- +const GetInstalledCertificateIdsFeatureName = "GetInstalledCertificateIds" + // Status returned in response to GetInstalledCertificateIdsRequest, that indicates whether certificate signing has been accepted or rejected. type GetInstalledCertificateStatus string @@ -27,13 +30,13 @@ func isValidGetInstalledCertificateStatus(fl validator.FieldLevel) bool { // The field definition of the GetInstalledCertificateIdsRequest PDU sent by the CSMS to the Charging Station. type GetInstalledCertificateIdsRequest struct { - TypeOfCertificate CertificateUse `json:"typeOfCertificate" validate:"required,certificateUse"` + TypeOfCertificate types.CertificateUse `json:"typeOfCertificate" validate:"required,certificateUse"` } -// The field definition of the GetInstalledCertificateIdsResponse payload sent by the Charging Station to the CSMS in response to a GetInstalledCertificateIdsRequest. -type GetInstalledCertificateIdsConfirmation struct { +// The field definition of the GetInstalledCertificateIds response payload sent by the Charging Station to the CSMS in response to a GetInstalledCertificateIdsRequest. +type GetInstalledCertificateIdsResponse struct { Status GetInstalledCertificateStatus `json:"status" validate:"required,getInstalledCertificateStatus"` - CertificateHashData []CertificateHashData `json:"certificateHashData,omitempty" validate:"omitempty,dive"` + CertificateHashData []types.CertificateHashData `json:"certificateHashData,omitempty" validate:"omitempty,dive"` } // To facilitate the management of the Charging Station’s installed certificates, a method of retrieving the installed certificates is provided. @@ -49,28 +52,28 @@ func (f GetInstalledCertificateIdsFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetInstalledCertificateIdsRequest{}) } -func (f GetInstalledCertificateIdsFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(GetInstalledCertificateIdsConfirmation{}) +func (f GetInstalledCertificateIdsFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(GetInstalledCertificateIdsResponse{}) } func (r GetInstalledCertificateIdsRequest) GetFeatureName() string { return GetInstalledCertificateIdsFeatureName } -func (c GetInstalledCertificateIdsConfirmation) GetFeatureName() string { +func (c GetInstalledCertificateIdsResponse) GetFeatureName() string { return GetInstalledCertificateIdsFeatureName } // Creates a new GetInstalledCertificateIdsRequest, containing all required fields. There are no optional fields for this message. -func NewGetInstalledCertificateIdsRequest(typeOfCertificate CertificateUse) *GetInstalledCertificateIdsRequest { +func NewGetInstalledCertificateIdsRequest(typeOfCertificate types.CertificateUse) *GetInstalledCertificateIdsRequest { return &GetInstalledCertificateIdsRequest{TypeOfCertificate: typeOfCertificate} } -// Creates a new ChangeAvailabilityConfirmation, containing all required fields. Additional optional fields may be set afterwards. -func NewGetInstalledCertificateIdsConfirmation(status GetInstalledCertificateStatus) *GetInstalledCertificateIdsConfirmation { - return &GetInstalledCertificateIdsConfirmation{Status: status} +// Creates a new NewGetInstalledCertificateIdsResponse, containing all required fields. Additional optional fields may be set afterwards. +func NewGetInstalledCertificateIdsResponse(status GetInstalledCertificateStatus) *GetInstalledCertificateIdsResponse { + return &GetInstalledCertificateIdsResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("getInstalledCertificateStatus", isValidGetInstalledCertificateStatus) + _ = types.Validate.RegisterValidation("getInstalledCertificateStatus", isValidGetInstalledCertificateStatus) } diff --git a/ocpp2.0/iso15118/iso_15118.go b/ocpp2.0/iso15118/iso_15118.go new file mode 100644 index 00000000..5315f6ba --- /dev/null +++ b/ocpp2.0/iso15118/iso_15118.go @@ -0,0 +1,34 @@ +// The ISO 15118 functional block contains OCPP 2.0 features that allow: +// +// - communication between EV and an EVSE +// +// - support for certificate-based authentication and authorization at the charging station, i.e. plug and charge +package iso15118 + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 ISO 15118 profile. +type CSMSHandler interface { + // OnGet15118EVCertificate is called on the CSMS whenever a Get15118EVCertificateRequest is received from a charging station. + OnGet15118EVCertificate(chargingStationID string, request *Get15118EVCertificateRequest) (confirmation *Get15118EVCertificateResponse, err error) + // OnGetCertificateStatus is called on the CSMS whenever a GetCertificateStatusRequest is received from a charging station. + OnGetCertificateStatus(chargingStationID string, request *GetCertificateStatusRequest) (confirmation *GetCertificateStatusResponse, err error) +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 ISO 15118 profile. +type ChargingStationHandler interface { + // OnDeleteCertificate is called on a charging station whenever a DeleteCertificateRequest is received from the CSMS. + OnDeleteCertificate(request *DeleteCertificateRequest) (confirmation *DeleteCertificateResponse, err error) + // OnGetInstalledCertificateIds is called on a charging station whenever a GetInstalledCertificateIdsRequest is received from the CSMS. + OnGetInstalledCertificateIds(request *GetInstalledCertificateIdsRequest) (confirmation *GetInstalledCertificateIdsResponse, err error) +} + +const ProfileName = "iso15118" + +var Profile = ocpp.NewProfile( + ProfileName, + DeleteCertificateFeature{}, + Get15118EVCertificateFeature{}, + GetCertificateStatusFeature{}, + GetInstalledCertificateIdsFeature{}, + ) diff --git a/ocpp2.0/get_local_list_version.go b/ocpp2.0/localauth/get_local_list_version.go similarity index 66% rename from ocpp2.0/get_local_list_version.go rename to ocpp2.0/localauth/get_local_list_version.go index e9658de7..bfcb57b5 100644 --- a/ocpp2.0/get_local_list_version.go +++ b/ocpp2.0/localauth/get_local_list_version.go @@ -1,4 +1,4 @@ -package ocpp2 +package localauth import ( "reflect" @@ -6,13 +6,15 @@ import ( // -------------------- Get Local List Version (CSMS -> CS) -------------------- +const GetLocalListVersionFeatureName = "GetLocalListVersion" + // The field definition of the GetLocalListVersion request payload sent by the CSMS to the Charging Station. type GetLocalListVersionRequest struct { } -// This field definition of the GetLocalListVersion confirmation payload, sent by the Charging Station to the CSMS in response to a GetLocalListVersionRequest. +// This field definition of the GetLocalListVersion response payload, sent by the Charging Station to the CSMS in response to a GetLocalListVersionRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type GetLocalListVersionConfirmation struct { +type GetLocalListVersionResponse struct { VersionNumber int `json:"versionNumber" validate:"gte=0"` } @@ -29,15 +31,15 @@ func (f GetLocalListVersionFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetLocalListVersionRequest{}) } -func (f GetLocalListVersionFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(GetLocalListVersionConfirmation{}) +func (f GetLocalListVersionFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(GetLocalListVersionResponse{}) } func (r GetLocalListVersionRequest) GetFeatureName() string { return GetLocalListVersionFeatureName } -func (c GetLocalListVersionConfirmation) GetFeatureName() string { +func (c GetLocalListVersionResponse) GetFeatureName() string { return GetLocalListVersionFeatureName } @@ -46,7 +48,7 @@ func NewGetLocalListVersionRequest() *GetLocalListVersionRequest { return &GetLocalListVersionRequest{} } -// Creates a new GetLocalListVersionConfirmation, containing all required fields. There are no optional fields for this message. -func NewGetLocalListVersionConfirmation(version int) *GetLocalListVersionConfirmation { - return &GetLocalListVersionConfirmation{VersionNumber: version} +// Creates a new GetLocalListVersionResponse, containing all required fields. There are no optional fields for this message. +func NewGetLocalListVersionResponse(version int) *GetLocalListVersionResponse { + return &GetLocalListVersionResponse{VersionNumber: version} } diff --git a/ocpp2.0/localauth/local_auth_list.go b/ocpp2.0/localauth/local_auth_list.go new file mode 100644 index 00000000..0f22633f --- /dev/null +++ b/ocpp2.0/localauth/local_auth_list.go @@ -0,0 +1,22 @@ +// The Local authorization list functional block contains OCPP 2.0 features for synchronizing local authorization lists between CSMS and charging station. +// Local lists are used for offline and generally optimized authorization. +package localauth + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Local Authorization List profile. +type CSMSHandler interface { +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Local Authorization List profile. +type ChargingStationHandler interface { + // OnGetLocalListVersion is called on a charging station whenever a GetLocalListVersionRequest is received from the CSMS. + OnGetLocalListVersion(request *GetLocalListVersionRequest) (confirmation *GetLocalListVersionResponse, err error) +} + +const ProfileName = "localAuthList" + +var Profile = ocpp.NewProfile( + ProfileName, + GetLocalListVersionFeature{}, + ) diff --git a/ocpp2.0/meter/meter.go b/ocpp2.0/meter/meter.go new file mode 100644 index 00000000..22310a7e --- /dev/null +++ b/ocpp2.0/meter/meter.go @@ -0,0 +1,17 @@ +// The Meter values functional block contains OCPP 2.0 features for sending meter values to the CSMS. +package meter + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Meter values profile. +type CSMSHandler interface { +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Meter values profile. +type ChargingStationHandler interface { +} + +const ProfileName = "meter" + +var Profile = ocpp.NewProfile( + ProfileName) diff --git a/ocpp2.0/boot_notification.go b/ocpp2.0/provisioning/boot_notification.go similarity index 80% rename from ocpp2.0/boot_notification.go rename to ocpp2.0/provisioning/boot_notification.go index 082e0d6b..d8ab2639 100644 --- a/ocpp2.0/boot_notification.go +++ b/ocpp2.0/provisioning/boot_notification.go @@ -1,12 +1,15 @@ -package ocpp2 +package provisioning import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Boot Notification (CS -> CSMS) -------------------- +const BootNotificationFeatureName = "BootNotification" + // Result of registration in response to a BootNotification request. type RegistrationStatus string @@ -69,10 +72,10 @@ type BootNotificationRequest struct { ChargingStation ChargingStationType `json:"chargingStation" validate:"required,dive"` } -// The field definition of the BootNotification confirmation payload, sent by the CSMS to the Charging Station in response to a BootNotificationRequest. +// The field definition of the BootNotification response payload, sent by the CSMS to the Charging Station in response to a BootNotificationRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type BootNotificationConfirmation struct { - CurrentTime *DateTime `json:"currentTime" validate:"required"` +type BootNotificationResponse struct { + CurrentTime *types.DateTime `json:"currentTime" validate:"required"` Interval int `json:"interval" validate:"gte=0"` Status RegistrationStatus `json:"status" validate:"required,registrationStatus"` } @@ -81,7 +84,7 @@ type BootNotificationConfirmation struct { // The CSMS SHALL respond to indicate whether it will accept the Charging Station. // Between the physical power-on/reboot and the successful completion of a BootNotification, where CSMS returns Accepted or Pending, the Charging Station SHALL NOT send any other request to the CSMS. // -// When the CSMS responds with a BootNotificationConfirmation with a status Accepted, the Charging Station will adjust the heartbeat +// When the CSMS responds with a BootNotificationResponse with a status Accepted, the Charging Station will adjust the heartbeat // interval in accordance with the interval from the response PDU and it is RECOMMENDED to synchronize its internal clock with the supplied CSMS’s current time. // // If that interval value is zero, the Charging Station chooses a waiting interval on its own, in a way that avoids flooding the CSMS with requests. @@ -98,15 +101,15 @@ func (f BootNotificationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(BootNotificationRequest{}) } -func (f BootNotificationFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(BootNotificationConfirmation{}) +func (f BootNotificationFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(BootNotificationResponse{}) } func (r BootNotificationRequest) GetFeatureName() string { return BootNotificationFeatureName } -func (c BootNotificationConfirmation) GetFeatureName() string { +func (c BootNotificationResponse) GetFeatureName() string { return BootNotificationFeatureName } @@ -115,12 +118,12 @@ func NewBootNotificationRequest(reason BootReason, model string, vendorName stri return &BootNotificationRequest{Reason: reason, ChargingStation: ChargingStationType{Model: model, VendorName: vendorName}} } -// Creates a new BootNotificationConfirmation. There are no optional fields for this message. -func NewBootNotificationConfirmation(currentTime *DateTime, interval int, status RegistrationStatus) *BootNotificationConfirmation { - return &BootNotificationConfirmation{CurrentTime: currentTime, Interval: interval, Status: status} +// Creates a new BootNotificationResponse. There are no optional fields for this message. +func NewBootNotificationResponse(currentTime *types.DateTime, interval int, status RegistrationStatus) *BootNotificationResponse { + return &BootNotificationResponse{CurrentTime: currentTime, Interval: interval, Status: status} } func init() { - _ = Validate.RegisterValidation("registrationStatus", isValidRegistrationStatus) - _ = Validate.RegisterValidation("bootReason", isValidBootReason) + _ = types.Validate.RegisterValidation("registrationStatus", isValidRegistrationStatus) + _ = types.Validate.RegisterValidation("bootReason", isValidBootReason) } diff --git a/ocpp2.0/get_base_report.go b/ocpp2.0/provisioning/get_base_report.go similarity index 68% rename from ocpp2.0/get_base_report.go rename to ocpp2.0/provisioning/get_base_report.go index a0d3ed64..68152edc 100644 --- a/ocpp2.0/get_base_report.go +++ b/ocpp2.0/provisioning/get_base_report.go @@ -1,12 +1,15 @@ -package ocpp2 +package provisioning import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Get Base Report (CSMS -> CS) -------------------- +const GetBaseReportFeatureName = "GetBaseReport" + // Requested availability change in GetBaseReportRequest. type ReportBaseType string @@ -32,15 +35,15 @@ type GetBaseReportRequest struct { ReportBase ReportBaseType `json:"reportBase" validate:"required,reportBaseType"` } -// This field definition of the GetBaseReport confirmation payload, sent by the Charging Station to the CSMS in response to a GetBaseReportRequest. +// This field definition of the GetBaseReport response payload, sent by the Charging Station to the CSMS in response to a GetBaseReportRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type GetBaseReportConfirmation struct { - Status GenericDeviceModelStatus `json:"status" validate:"required,genericDeviceModelStatus"` +type GetBaseReportResponse struct { + Status types.GenericDeviceModelStatus `json:"status" validate:"required,genericDeviceModelStatus"` } // The CSO may trigger the CSMS to request a report from a Charging Station. // The CSMS shall then request a Charging Station to send a predefined report as defined in ReportBase. -// The Charging Station responds with GetBaseReportConfirmation. +// The Charging Station responds with GetBaseReportResponse. // The result will be returned asynchronously in one or more NotifyReportRequest messages (one for each report part). type GetBaseReportFeature struct{} @@ -52,15 +55,15 @@ func (f GetBaseReportFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetBaseReportRequest{}) } -func (f GetBaseReportFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(GetBaseReportConfirmation{}) +func (f GetBaseReportFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(GetBaseReportResponse{}) } func (r GetBaseReportRequest) GetFeatureName() string { return GetBaseReportFeatureName } -func (c GetBaseReportConfirmation) GetFeatureName() string { +func (c GetBaseReportResponse) GetFeatureName() string { return GetBaseReportFeatureName } @@ -69,11 +72,11 @@ func NewGetBaseReportRequest(requestID int, reportBase ReportBaseType) *GetBaseR return &GetBaseReportRequest{RequestID: requestID, ReportBase: reportBase} } -// Creates a new GetBaseReportConfirmation, containing all required fields. There are no optional fields for this message. -func NewGetBaseReportConfirmation(status GenericDeviceModelStatus) *GetBaseReportConfirmation { - return &GetBaseReportConfirmation{Status: status} +// Creates a new GetBaseReportResponse, containing all required fields. There are no optional fields for this message. +func NewGetBaseReportResponse(status types.GenericDeviceModelStatus) *GetBaseReportResponse { + return &GetBaseReportResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("reportBaseType", isValidReportBaseType) + _ = types.Validate.RegisterValidation("reportBaseType", isValidReportBaseType) } diff --git a/ocpp2.0/provisioning/provisioning.go b/ocpp2.0/provisioning/provisioning.go new file mode 100644 index 00000000..3110cdd6 --- /dev/null +++ b/ocpp2.0/provisioning/provisioning.go @@ -0,0 +1,26 @@ +// The provisioning functional block contains features that help a CSO to provision their Charging Stations, allowing them on their network and retrieving configuration information from these Charging Stations. +// Additionally, it contains features for retrieving information about the configuration of Charging Stations, make changes to the configuration, resetting it etc. +package provisioning + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Provisioning profile. +type CSMSHandler interface { + // OnBootNotification is called on the CSMS whenever a BootNotificationRequest is received from a charging station. + OnBootNotification(chargingStationID string, request *BootNotificationRequest) (confirmation *BootNotificationResponse, err error) +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Provisioning profile. +type ChargingStationHandler interface { + // OnGetBaseReport is called on a charging station whenever a GetBaseReportRequest is received from the CSMS. + OnGetBaseReport(request *GetBaseReportRequest) (confirmation *GetBaseReportResponse, err error) +} + +const ProfileName = "provisioning" + +var Profile = ocpp.NewProfile( + ProfileName, + BootNotificationFeature{}, + GetBaseReportFeature{}, + // SetVariables +) diff --git a/ocpp2.0/remotecontrol/remote_control.go b/ocpp2.0/remotecontrol/remote_control.go new file mode 100644 index 00000000..725b2318 --- /dev/null +++ b/ocpp2.0/remotecontrol/remote_control.go @@ -0,0 +1,17 @@ +// The Remote control functional block contains OCPP 2.0 features for remote-control management from the CSMS. +package remotecontrol + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Remote control profile. +type CSMSHandler interface { +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Remote control profile. +type ChargingStationHandler interface { +} + +const ProfileName = "remoteControl" + +var Profile = ocpp.NewProfile( + ProfileName) diff --git a/ocpp2.0/cancel_reservation.go b/ocpp2.0/reservation/cancel_reservation.go similarity index 68% rename from ocpp2.0/cancel_reservation.go rename to ocpp2.0/reservation/cancel_reservation.go index 01b8b2c9..eb1e8c77 100644 --- a/ocpp2.0/cancel_reservation.go +++ b/ocpp2.0/reservation/cancel_reservation.go @@ -1,13 +1,16 @@ -package ocpp2 +package reservation import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Cancel Reservation (CSMS -> CS) -------------------- -// Status reported in CancelReservationConfirmation. +const CancelReservationFeatureName = "CancelReservation" + +// Status reported in CancelReservationResponse. type CancelReservationStatus string const ( @@ -30,9 +33,9 @@ type CancelReservationRequest struct { ReservationId int `json:"reservationId" validate:"gte=0"` } -// This field definition of the CancelReservation confirmation payload, sent by the Charging Station to the CSMS in response to a CancelReservationRequest. +// This field definition of the CancelReservation response payload, sent by the Charging Station to the CSMS in response to a CancelReservationRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type CancelReservationConfirmation struct { +type CancelReservationResponse struct { Status CancelReservationStatus `json:"status" validate:"required,cancelReservationStatus"` } @@ -49,15 +52,15 @@ func (f CancelReservationFeature) GetRequestType() reflect.Type { return reflect.TypeOf(CancelReservationRequest{}) } -func (f CancelReservationFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(CancelReservationConfirmation{}) +func (f CancelReservationFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(CancelReservationResponse{}) } func (r CancelReservationRequest) GetFeatureName() string { return CancelReservationFeatureName } -func (c CancelReservationConfirmation) GetFeatureName() string { +func (c CancelReservationResponse) GetFeatureName() string { return CancelReservationFeatureName } @@ -66,11 +69,11 @@ func NewCancelReservationRequest(reservationId int) *CancelReservationRequest { return &CancelReservationRequest{ReservationId: reservationId} } -// Creates a new CancelReservationConfirmation, containing all required fields. There are no optional fields for this message. -func NewCancelReservationConfirmation(status CancelReservationStatus) *CancelReservationConfirmation { - return &CancelReservationConfirmation{Status: status} +// Creates a new CancelReservationResponse, containing all required fields. There are no optional fields for this message. +func NewCancelReservationResponse(status CancelReservationStatus) *CancelReservationResponse { + return &CancelReservationResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("cancelReservationStatus", isValidCancelReservationStatus) + _ = types.Validate.RegisterValidation("cancelReservationStatus", isValidCancelReservationStatus) } diff --git a/ocpp2.0/reservation/reservation.go b/ocpp2.0/reservation/reservation.go new file mode 100644 index 00000000..9298322b --- /dev/null +++ b/ocpp2.0/reservation/reservation.go @@ -0,0 +1,23 @@ +// The reservation functional block contains OCPP 2.0 features that enable EV drivers to make and manage reservations of charging stations. +package reservation + +import ( + "github.com/lorenzodonini/ocpp-go/ocpp" +) + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Reservation profile. +type CSMSHandler interface { +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Reservation profile. +type ChargingStationHandler interface { + // OnCancelReservation is called on a charging station whenever a CancelReservationRequest is received from the CSMS. + OnCancelReservation(request *CancelReservationRequest) (confirmation *CancelReservationResponse, err error) +} + +const ProfileName = "reservation" + +var Profile = ocpp.NewProfile( + ProfileName, + CancelReservationFeature{}, + ) diff --git a/ocpp2.0/certificate_signed.go b/ocpp2.0/security/certificate_signed.go similarity index 69% rename from ocpp2.0/certificate_signed.go rename to ocpp2.0/security/certificate_signed.go index 8ac83884..c8f283d3 100644 --- a/ocpp2.0/certificate_signed.go +++ b/ocpp2.0/security/certificate_signed.go @@ -1,12 +1,15 @@ -package ocpp2 +package security import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Certificate Signed (CSMS -> CS) -------------------- +const CertificateSignedFeatureName = "CertificateSigned" + // Status returned in response to CertificateSignedRequest, that indicates whether certificate signing has been accepted or rejected. type CertificateSignedStatus string @@ -27,12 +30,12 @@ func isValidCertificateSignedStatus(fl validator.FieldLevel) bool { // The field definition of the CertificateSignedRequest PDU sent by the CSMS to the Charging Station. type CertificateSignedRequest struct { - Cert []string `json:"cert" validate:"required,min=1,dive,max=800"` - TypeOfCertificate CertificateSigningUse `json:"typeOfCertificate,omitempty" validate:"omitempty,certificateSigningUse"` + Cert []string `json:"cert" validate:"required,min=1,dive,max=800"` + TypeOfCertificate types.CertificateSigningUse `json:"typeOfCertificate,omitempty" validate:"omitempty,certificateSigningUse"` } // The field definition of the CertificateSignedResponse payload sent by the Charging Station to the CSMS in response to a CertificateSignedRequest. -type CertificateSignedConfirmation struct { +type CertificateSignedResponse struct { Status CertificateSignedStatus `json:"status" validate:"required,certificateSignedStatus"` } @@ -48,15 +51,15 @@ func (f CertificateSignedFeature) GetRequestType() reflect.Type { return reflect.TypeOf(CertificateSignedRequest{}) } -func (f CertificateSignedFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(CertificateSignedConfirmation{}) +func (f CertificateSignedFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(CertificateSignedResponse{}) } func (r CertificateSignedRequest) GetFeatureName() string { return CertificateSignedFeatureName } -func (c CertificateSignedConfirmation) GetFeatureName() string { +func (c CertificateSignedResponse) GetFeatureName() string { return CertificateSignedFeatureName } @@ -65,11 +68,11 @@ func NewCertificateSignedRequest(certificate []string) *CertificateSignedRequest return &CertificateSignedRequest{Cert: certificate} } -// Creates a new CertificateSignedConfirmation, containing all required fields. There are no optional fields for this message. -func NewCertificateSignedConfirmation(status CertificateSignedStatus) *CertificateSignedConfirmation { - return &CertificateSignedConfirmation{Status: status} +// Creates a new CertificateSignedResponse, containing all required fields. There are no optional fields for this message. +func NewCertificateSignedResponse(status CertificateSignedStatus) *CertificateSignedResponse { + return &CertificateSignedResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("certificateSignedStatus", isValidCertificateSignedStatus) + _ = types.Validate.RegisterValidation("certificateSignedStatus", isValidCertificateSignedStatus) } diff --git a/ocpp2.0/security/security.go b/ocpp2.0/security/security.go new file mode 100644 index 00000000..26ad6a83 --- /dev/null +++ b/ocpp2.0/security/security.go @@ -0,0 +1,23 @@ +// The security functional block contains OCPP 2.0 features aimed at providing E2E security between a CSMS and a Charging station. +package security + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Security profile. +type CSMSHandler interface { +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Security profile. +type ChargingStationHandler interface { + // OnCertificateSigned is called on a charging station whenever a CertificateSignedRequest is received from the CSMS. + OnCertificateSigned(request *CertificateSignedRequest) (response *CertificateSignedResponse, err error) +} + +const ProfileName = "security" + +var Profile = ocpp.NewProfile( + ProfileName, + CertificateSignedFeature{}, + +// SetVariables +) diff --git a/ocpp2.0/clear_charging_profile.go b/ocpp2.0/smartcharging/clear_charging_profile.go similarity index 62% rename from ocpp2.0/clear_charging_profile.go rename to ocpp2.0/smartcharging/clear_charging_profile.go index 17caed45..f08cd296 100644 --- a/ocpp2.0/clear_charging_profile.go +++ b/ocpp2.0/smartcharging/clear_charging_profile.go @@ -1,13 +1,16 @@ -package ocpp2 +package smartcharging import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Clear Charging Profile (CSMS -> CS) -------------------- -// Status reported in ClearChargingProfileConfirmation. +const ClearChargingProfileFeatureName = "ClearChargingProfile" + +// Status reported in ClearChargingProfileResponse. type ClearChargingProfileStatus string const ( @@ -16,9 +19,9 @@ const ( ) type ClearChargingProfileType struct { - ID int `json:"id,omitempty" validate:"gte=0"` - ChargingProfilePurpose ChargingProfilePurposeType `json:"chargingProfilePurpose,omitempty" validate:"omitempty,chargingProfilePurpose"` - StackLevel int `json:"stackLevel,omitempty" validate:"omitempty,gt=0"` + ID int `json:"id,omitempty" validate:"gte=0"` + ChargingProfilePurpose types.ChargingProfilePurposeType `json:"chargingProfilePurpose,omitempty" validate:"omitempty,chargingProfilePurpose"` + StackLevel int `json:"stackLevel,omitempty" validate:"omitempty,gt=0"` } func isValidClearChargingProfileStatus(fl validator.FieldLevel) bool { @@ -37,9 +40,9 @@ type ClearChargingProfileRequest struct { ChargingProfile *ClearChargingProfileType `json:"chargingProfile,omitempty" validate:"omitempty,dive"` } -// This field definition of the ClearChargingProfile confirmation payload, sent by the Charging Station to the CSMS in response to a ClearChargingProfileRequest. +// This field definition of the ClearChargingProfile response payload, sent by the Charging Station to the CSMS in response to a ClearChargingProfileRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type ClearChargingProfileConfirmation struct { +type ClearChargingProfileResponse struct { Status ClearChargingProfileStatus `json:"status" validate:"required,clearChargingProfileStatus"` } @@ -47,7 +50,7 @@ type ClearChargingProfileConfirmation struct { // it SHALL send a ClearChargingProfileRequest. // The CSMS can use this message to clear (remove) either a specific charging profile (denoted by id) or a selection of // charging profiles that match with the values of the optional connectorId, stackLevel and chargingProfilePurpose fields. -// The Charging Station SHALL respond with a ClearChargingProfileConfirmation payload specifying whether it was able to process the request. +// The Charging Station SHALL respond with a ClearChargingProfileResponse payload specifying whether it was able to process the request. type ClearChargingProfileFeature struct{} func (f ClearChargingProfileFeature) GetFeatureName() string { @@ -58,15 +61,15 @@ func (f ClearChargingProfileFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ClearChargingProfileRequest{}) } -func (f ClearChargingProfileFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(ClearChargingProfileConfirmation{}) +func (f ClearChargingProfileFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(ClearChargingProfileResponse{}) } func (r ClearChargingProfileRequest) GetFeatureName() string { return ClearChargingProfileFeatureName } -func (c ClearChargingProfileConfirmation) GetFeatureName() string { +func (c ClearChargingProfileResponse) GetFeatureName() string { return ClearChargingProfileFeatureName } @@ -75,11 +78,11 @@ func NewClearChargingProfileRequest() *ClearChargingProfileRequest { return &ClearChargingProfileRequest{} } -// Creates a new ClearChargingProfileConfirmation, containing all required fields. There are no optional fields for this message. -func NewClearChargingProfileConfirmation(status ClearChargingProfileStatus) *ClearChargingProfileConfirmation { - return &ClearChargingProfileConfirmation{Status: status} +// Creates a new ClearChargingProfileResponse, containing all required fields. There are no optional fields for this message. +func NewClearChargingProfileResponse(status ClearChargingProfileStatus) *ClearChargingProfileResponse { + return &ClearChargingProfileResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("clearChargingProfileStatus", isValidClearChargingProfileStatus) + _ = types.Validate.RegisterValidation("clearChargingProfileStatus", isValidClearChargingProfileStatus) } diff --git a/ocpp2.0/cleared_charging_limit.go b/ocpp2.0/smartcharging/cleared_charging_limit.go similarity index 57% rename from ocpp2.0/cleared_charging_limit.go rename to ocpp2.0/smartcharging/cleared_charging_limit.go index a9e5bdf9..b6c23750 100644 --- a/ocpp2.0/cleared_charging_limit.go +++ b/ocpp2.0/smartcharging/cleared_charging_limit.go @@ -1,20 +1,23 @@ -package ocpp2 +package smartcharging import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "reflect" ) // -------------------- Cleared Charging Limit (CS -> CSMS) -------------------- +const ClearedChargingLimitFeatureName = "ClearedChargingLimit" + // The field definition of the ClearedChargingLimit request payload sent by the Charging Station to the CSMS. type ClearedChargingLimitRequest struct { - ChargingLimitSource ChargingLimitSourceType `json:"chargingLimitSource" validate:"required,chargingLimitSource"` - EvseID *int `json:"evseId,omitempty" validate:"omitempty,gte=0"` + ChargingLimitSource types.ChargingLimitSourceType `json:"chargingLimitSource" validate:"required,chargingLimitSource"` + EvseID *int `json:"evseId,omitempty" validate:"omitempty,gte=0"` } -// This field definition of the ClearedChargingLimit confirmation payload, sent by the CSMS to the Charging Station in response to a ClearedChargingLimitRequest. +// This field definition of the ClearedChargingLimit response payload, sent by the CSMS to the Charging Station in response to a ClearedChargingLimitRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type ClearedChargingLimitConfirmation struct { +type ClearedChargingLimitResponse struct { } // When an external control system sends a signal to release a previously imposed charging limit to a Charging Station, @@ -32,24 +35,24 @@ func (f ClearedChargingLimitFeature) GetRequestType() reflect.Type { return reflect.TypeOf(ClearedChargingLimitRequest{}) } -func (f ClearedChargingLimitFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(ClearedChargingLimitConfirmation{}) +func (f ClearedChargingLimitFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(ClearedChargingLimitResponse{}) } func (r ClearedChargingLimitRequest) GetFeatureName() string { return ClearedChargingLimitFeatureName } -func (c ClearedChargingLimitConfirmation) GetFeatureName() string { +func (c ClearedChargingLimitResponse) GetFeatureName() string { return ClearedChargingLimitFeatureName } // Creates a new ClearedChargingLimitRequest, containing all required fields. Optional fields may be set afterwards. -func NewClearedChargingLimitRequest(chargingLimitSource ChargingLimitSourceType) *ClearedChargingLimitRequest { +func NewClearedChargingLimitRequest(chargingLimitSource types.ChargingLimitSourceType) *ClearedChargingLimitRequest { return &ClearedChargingLimitRequest{ChargingLimitSource: chargingLimitSource} } -// Creates a new ClearedChargingLimitConfirmation, which doesn't contain any required or optional fields. -func NewClearedChargingLimitConfirmation() *ClearedChargingLimitConfirmation { - return &ClearedChargingLimitConfirmation{} +// Creates a new ClearedChargingLimitResponse, which doesn't contain any required or optional fields. +func NewClearedChargingLimitResponse() *ClearedChargingLimitResponse { + return &ClearedChargingLimitResponse{} } diff --git a/ocpp2.0/get_charging_profiles.go b/ocpp2.0/smartcharging/get_charging_profiles.go similarity index 63% rename from ocpp2.0/get_charging_profiles.go rename to ocpp2.0/smartcharging/get_charging_profiles.go index 20b54f6f..e810ab97 100644 --- a/ocpp2.0/get_charging_profiles.go +++ b/ocpp2.0/smartcharging/get_charging_profiles.go @@ -1,13 +1,16 @@ -package ocpp2 +package smartcharging import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Get Charging Profiles (CSMS -> Charging Station) -------------------- -// Status reported in GetChargingProfilesConfirmation. +const GetChargingProfilesFeatureName = "GetChargingProfiles" + +// Status reported in GetChargingProfilesResponse. type GetChargingProfileStatus string const ( @@ -28,10 +31,10 @@ func isValidGetChargingProfileStatus(fl validator.FieldLevel) bool { // ChargingProfileCriterion specifies the charging profile within a GetChargingProfilesRequest. // A ChargingProfile consists of ChargingSchedule, describing the amount of power or current that can be delivered per time interval. type ChargingProfileCriterion struct { - ChargingProfilePurpose ChargingProfilePurposeType `json:"chargingProfilePurpose,omitempty" validate:"omitempty,chargingProfilePurpose"` - StackLevel *int `json:"stackLevel,omitempty" validate:"omitempty,gte=0"` - ChargingProfileID []int `json:"chargingProfileId,omitempty" validate:"omitempty,dive,gte=0"` // This field SHALL NOT contain more ids than set in ChargingProfileEntries.maxLimit - ChargingLimitSource []ChargingLimitSourceType `json:"chargingLimitSource,omitempty" validate:"omitempty,max=4,dive,chargingLimitSource"` + ChargingProfilePurpose types.ChargingProfilePurposeType `json:"chargingProfilePurpose,omitempty" validate:"omitempty,chargingProfilePurpose"` + StackLevel *int `json:"stackLevel,omitempty" validate:"omitempty,gte=0"` + ChargingProfileID []int `json:"chargingProfileId,omitempty" validate:"omitempty,dive,gte=0"` // This field SHALL NOT contain more ids than set in ChargingProfileEntries.maxLimit + ChargingLimitSource []types.ChargingLimitSourceType `json:"chargingLimitSource,omitempty" validate:"omitempty,max=4,dive,chargingLimitSource"` } // The field definition of the GetChargingProfiles request payload sent by the CSMS to the Charging Station. @@ -41,9 +44,9 @@ type GetChargingProfilesRequest struct { ChargingProfile ChargingProfileCriterion `json:"chargingProfile" validate:"required"` } -// This field definition of the GetChargingProfiles confirmation payload, sent by the Charging Station to the CSMS in response to a GetChargingProfilesRequest. +// This field definition of the GetChargingProfiles response payload, sent by the Charging Station to the CSMS in response to a GetChargingProfilesRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type GetChargingProfilesConfirmation struct { +type GetChargingProfilesResponse struct { Status GetChargingProfileStatus `json:"status" validate:"required,getChargingProfileStatus"` } @@ -60,15 +63,15 @@ func (f GetChargingProfilesFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetChargingProfilesRequest{}) } -func (f GetChargingProfilesFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(GetChargingProfilesConfirmation{}) +func (f GetChargingProfilesFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(GetChargingProfilesResponse{}) } func (r GetChargingProfilesRequest) GetFeatureName() string { return GetChargingProfilesFeatureName } -func (c GetChargingProfilesConfirmation) GetFeatureName() string { +func (c GetChargingProfilesResponse) GetFeatureName() string { return GetChargingProfilesFeatureName } @@ -77,11 +80,11 @@ func NewGetChargingProfilesRequest(chargingProfile ChargingProfileCriterion) *Ge return &GetChargingProfilesRequest{ChargingProfile: chargingProfile} } -// Creates a new GetChargingProfilesConfirmation, containing all required fields. There are no optional fields for this message. -func NewGetChargingProfilesConfirmation(status GetChargingProfileStatus) *GetChargingProfilesConfirmation { - return &GetChargingProfilesConfirmation{Status: status} +// Creates a new GetChargingProfilesResponse, containing all required fields. There are no optional fields for this message. +func NewGetChargingProfilesResponse(status GetChargingProfileStatus) *GetChargingProfilesResponse { + return &GetChargingProfilesResponse{Status: status} } func init() { - _ = Validate.RegisterValidation("getChargingProfileStatus", isValidGetChargingProfileStatus) + _ = types.Validate.RegisterValidation("getChargingProfileStatus", isValidGetChargingProfileStatus) } diff --git a/ocpp2.0/get_composite_schedule.go b/ocpp2.0/smartcharging/get_composite_schedule.go similarity index 60% rename from ocpp2.0/get_composite_schedule.go rename to ocpp2.0/smartcharging/get_composite_schedule.go index b02221f1..f101c7b3 100644 --- a/ocpp2.0/get_composite_schedule.go +++ b/ocpp2.0/smartcharging/get_composite_schedule.go @@ -1,13 +1,16 @@ -package ocpp2 +package smartcharging import ( + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "gopkg.in/go-playground/validator.v9" "reflect" ) // -------------------- Get Composite Schedule (CSMS -> CS) -------------------- -// Status reported in GetCompositeScheduleConfirmation. +const GetCompositeScheduleFeatureName = "GetCompositeSchedule" + +// Status reported in GetCompositeScheduleResponse. type GetCompositeScheduleStatus string const ( @@ -26,28 +29,28 @@ func isValidGetCompositeScheduleStatus(fl validator.FieldLevel) bool { } type CompositeSchedule struct { - StartDateTime *DateTime `json:"startDateTime,omitempty" validate:"omitempty"` - ChargingSchedule *ChargingSchedule `json:"chargingSchedule,omitempty" validate:"omitempty"` + StartDateTime *types.DateTime `json:"startDateTime,omitempty" validate:"omitempty"` + ChargingSchedule *types.ChargingSchedule `json:"chargingSchedule,omitempty" validate:"omitempty"` } // The field definition of the GetCompositeSchedule request payload sent by the CSMS to the Charging System. type GetCompositeScheduleRequest struct { - Duration int `json:"duration" validate:"gte=0"` - ChargingRateUnit ChargingRateUnitType `json:"chargingRateUnit,omitempty" validate:"omitempty,chargingRateUnit"` - EvseID int `json:"evseId" validate:"gte=0"` + Duration int `json:"duration" validate:"gte=0"` + ChargingRateUnit types.ChargingRateUnitType `json:"chargingRateUnit,omitempty" validate:"omitempty,chargingRateUnit"` + EvseID int `json:"evseId" validate:"gte=0"` } -// This field definition of the GetCompositeSchedule confirmation payload, sent by the Charging System to the CSMS in response to a GetCompositeScheduleRequest. +// This field definition of the GetCompositeSchedule response payload, sent by the Charging System to the CSMS in response to a GetCompositeScheduleRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type GetCompositeScheduleConfirmation struct { +type GetCompositeScheduleResponse struct { Status GetCompositeScheduleStatus `json:"status" validate:"required,getCompositeScheduleStatus"` EvseID int `json:"evseId" validate:"gte=0"` Schedule *CompositeSchedule `json:"schedule,omitempty" validate:"omitempty"` } // The CSMS MAY request the Charging System to report the Composite Charging Schedule by sending a GetCompositeScheduleRequest. -// The Charging System SHALL calculate the Composite Charging Schedule intervals, from the moment the request payload is received: Time X, up to X + Duration, and send them in the GetCompositeScheduleConfirmation to the CSMS. -// The reported schedule, in the GetCompositeScheduleConfirmation payload, is the result of the calculation of all active schedules and possible local limits present in the Charging System. +// The Charging System SHALL calculate the Composite Charging Schedule intervals, from the moment the request payload is received: Time X, up to X + Duration, and send them in the GetCompositeScheduleResponse to the CSMS. +// The reported schedule, in the GetCompositeScheduleResponse payload, is the result of the calculation of all active schedules and possible local limits present in the Charging System. // If the ConnectorId in the request is set to '0', the Charging System SHALL report the total expected power or current the Charging System expects to consume from the grid during the requested time period. // If the Charging System is not able to report the requested schedule, for instance if the connectorId is unknown, it SHALL respond with a status Rejected. type GetCompositeScheduleFeature struct{} @@ -60,15 +63,15 @@ func (f GetCompositeScheduleFeature) GetRequestType() reflect.Type { return reflect.TypeOf(GetCompositeScheduleRequest{}) } -func (f GetCompositeScheduleFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(GetCompositeScheduleConfirmation{}) +func (f GetCompositeScheduleFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(GetCompositeScheduleResponse{}) } func (r GetCompositeScheduleRequest) GetFeatureName() string { return GetCompositeScheduleFeatureName } -func (c GetCompositeScheduleConfirmation) GetFeatureName() string { +func (c GetCompositeScheduleResponse) GetFeatureName() string { return GetCompositeScheduleFeatureName } @@ -77,11 +80,11 @@ func NewGetCompositeScheduleRequest(duration int, evseId int) *GetCompositeSched return &GetCompositeScheduleRequest{Duration: duration, EvseID: evseId} } -// Creates a new GetCompositeScheduleConfirmation, containing all required fields. Optional fields may be set afterwards. -func NewGetCompositeScheduleConfirmation(status GetCompositeScheduleStatus, evseId int) *GetCompositeScheduleConfirmation { - return &GetCompositeScheduleConfirmation{Status: status, EvseID: evseId} +// Creates a new GetCompositeScheduleResponse, containing all required fields. Optional fields may be set afterwards. +func NewGetCompositeScheduleResponse(status GetCompositeScheduleStatus, evseId int) *GetCompositeScheduleResponse { + return &GetCompositeScheduleResponse{Status: status, EvseID: evseId} } func init() { - _ = Validate.RegisterValidation("getCompositeScheduleStatus", isValidGetCompositeScheduleStatus) + _ = types.Validate.RegisterValidation("getCompositeScheduleStatus", isValidGetCompositeScheduleStatus) } diff --git a/ocpp2.0/smartcharging/smart_charging.go b/ocpp2.0/smartcharging/smart_charging.go new file mode 100644 index 00000000..dba94e6e --- /dev/null +++ b/ocpp2.0/smartcharging/smart_charging.go @@ -0,0 +1,32 @@ +// The Smart charging functional block contains OCPP 2.0 features that enable the CSO (or a third party) to influence the charging current/power transferred during a transaction, or set limits to the amount of current/power a Charging Station can draw from the grid. +package smartcharging + +import ( + "github.com/lorenzodonini/ocpp-go/ocpp" +) + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Smart charging profile. +type CSMSHandler interface { + // OnClearedChargingLimit is called on the CSMS whenever a ClearedChargingLimitRequest is received from a charging station. + OnClearedChargingLimit(chargingStationID string, request *ClearedChargingLimitRequest) (confirmation *ClearedChargingLimitResponse, err error) +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Smart charging profile. +type ChargingStationHandler interface { + // OnClearChargingProfile is called on a charging station whenever a ClearChargingProfileRequest is received from the CSMS. + OnClearChargingProfile(request *ClearChargingProfileRequest) (confirmation *ClearChargingProfileResponse, err error) + // OnGetChargingProfiles is called on a charging station whenever a GetChargingProfilesRequest is received from the CSMS. + OnGetChargingProfiles(request *GetChargingProfilesRequest) (confirmation *GetChargingProfilesResponse, err error) + // OnGetCompositeSchedule is called on a charging station whenever a GetCompositeScheduleRequest is received from the CSMS. + OnGetCompositeSchedule(request *GetCompositeScheduleRequest) (confirmation *GetCompositeScheduleResponse, err error) +} + +const ProfileName = "smartCharging" + +var Profile = ocpp.NewProfile( + ProfileName, + ClearChargingProfileFeature{}, + ClearedChargingLimitFeature{}, + GetChargingProfilesFeature{}, + GetCompositeScheduleFeature{}, + ) diff --git a/ocpp2.0/cost_updated.go b/ocpp2.0/tariffcost/cost_updated.go similarity index 72% rename from ocpp2.0/cost_updated.go rename to ocpp2.0/tariffcost/cost_updated.go index 188290ea..7ac901f8 100644 --- a/ocpp2.0/cost_updated.go +++ b/ocpp2.0/tariffcost/cost_updated.go @@ -1,4 +1,4 @@ -package ocpp2 +package tariffcost import ( "reflect" @@ -6,15 +6,17 @@ import ( // -------------------- Cost Updated (CSMS -> CS) -------------------- +const CostUpdatedFeatureName = "CostUpdated" + // The field definition of the CostUpdated request payload sent by the CSMS to the Charging Station. type CostUpdatedRequest struct { TotalCost float64 `json:"totalCost" validate:"required"` TransactionID string `json:"transactionId" validate:"required,max=36"` } -// This field definition of the CostUpdated confirmation payload, sent by the Charging Station to the CSMS in response to a CostUpdatedRequest. +// This field definition of the CostUpdated response payload, sent by the Charging Station to the CSMS in response to a CostUpdatedRequest. // In case the request was invalid, or couldn't be processed, an error will be sent instead. -type CostUpdatedConfirmation struct { +type CostUpdatedResponse struct { } // The driver wants to know how much the running total cost is, updated at a relevant interval, while a transaction is ongoing. @@ -30,15 +32,15 @@ func (f CostUpdatedFeature) GetRequestType() reflect.Type { return reflect.TypeOf(CostUpdatedRequest{}) } -func (f CostUpdatedFeature) GetConfirmationType() reflect.Type { - return reflect.TypeOf(CostUpdatedConfirmation{}) +func (f CostUpdatedFeature) GetResponseType() reflect.Type { + return reflect.TypeOf(CostUpdatedResponse{}) } func (r CostUpdatedRequest) GetFeatureName() string { return CostUpdatedFeatureName } -func (c CostUpdatedConfirmation) GetFeatureName() string { +func (c CostUpdatedResponse) GetFeatureName() string { return CostUpdatedFeatureName } @@ -47,7 +49,7 @@ func NewCostUpdatedRequest(totalCost float64, transactionID string) *CostUpdated return &CostUpdatedRequest{TotalCost: totalCost, TransactionID: transactionID} } -// Creates a new CostUpdatedConfirmation, which doesn't contain any required or optional fields. -func NewCostUpdatedConfirmation() *CostUpdatedConfirmation { - return &CostUpdatedConfirmation{} +// Creates a new CostUpdatedResponse, which doesn't contain any required or optional fields. +func NewCostUpdatedResponse() *CostUpdatedResponse { + return &CostUpdatedResponse{} } diff --git a/ocpp2.0/tariffcost/tariff_cost.go b/ocpp2.0/tariffcost/tariff_cost.go new file mode 100644 index 00000000..a1c9ffd6 --- /dev/null +++ b/ocpp2.0/tariffcost/tariff_cost.go @@ -0,0 +1,21 @@ +// The authorization functional block contains OCPP 2.0 features that show tariff and costs to an EV driver, when supported by the charging station. +package tariffcost + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Tariff and cost profile. +type CSMSHandler interface { +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Tariff and cost profile. +type ChargingStationHandler interface { + // OnCostUpdated is called on a charging station whenever a CostUpdatedRequest is received from the CSMS. + OnCostUpdated(request *CostUpdatedRequest) (confirmation *CostUpdatedResponse, err error) +} + +const ProfileName = "tariffCost" + +var Profile = ocpp.NewProfile( + ProfileName, + CostUpdatedFeature{}, + ) diff --git a/ocpp2.0/transactions/transactions.go b/ocpp2.0/transactions/transactions.go new file mode 100644 index 00000000..79f9716d --- /dev/null +++ b/ocpp2.0/transactions/transactions.go @@ -0,0 +1,17 @@ +// The transactions functional block contains OCPP 2.0 features related to OCPP transactions. +package transactions + +import "github.com/lorenzodonini/ocpp-go/ocpp" + +// Needs to be implemented by a CSMS for handling messages part of the OCPP 2.0 Transactions profile. +type CSMSHandler interface { +} + +// Needs to be implemented by Charging stations for handling messages part of the OCPP 2.0 Transactions profile. +type ChargingStationHandler interface { +} + +const ProfileName = "transactions" + +var Profile = ocpp.NewProfile( + ProfileName) diff --git a/ocpp2.0/types/datetime.go b/ocpp2.0/types/datetime.go new file mode 100644 index 00000000..e4015a27 --- /dev/null +++ b/ocpp2.0/types/datetime.go @@ -0,0 +1,80 @@ +package types + +import ( + "encoding/json" + "strings" + "time" +) + +// DateTimeFormat to be used for all OCPP messages. +// +// The default dateTime format is RFC3339. +// Change this if another format is desired. +var DateTimeFormat = time.RFC3339 + +// DateTime wraps a time.Time struct, allowing for improved dateTime JSON compatibility. +type DateTime struct { + time.Time +} + +// Creates a new DateTime struct, embedding a time.Time struct. +func NewDateTime(time time.Time) *DateTime { + return &DateTime{Time: time} +} + +func (dt *DateTime) UnmarshalJSON(input []byte) error { + strInput := string(input) + strInput = strings.Trim(strInput, `"`) + if DateTimeFormat == "" { + defaultTime := time.Time{} + err := json.Unmarshal(input, defaultTime) + if err != nil { + return err + } + dt.Time = defaultTime.Local() + } else { + newTime, err := time.Parse(DateTimeFormat, strInput) + if err != nil { + return err + } + dt.Time = newTime.Local() + } + return nil +} + +func (dt *DateTime) MarshalJSON() ([]byte, error) { + if DateTimeFormat == "" { + return json.Marshal(dt.Time) + } + timeStr := FormatTimestamp(dt.Time) + return json.Marshal(timeStr) +} + +// Formats the UTC timestamp using the DateTimeFormat setting. +// This function is used during JSON marshaling as well. +func (dt *DateTime) FormatTimestamp() string { + return dt.UTC().Format(DateTimeFormat) +} + +func FormatTimestamp(t time.Time) string { + return t.UTC().Format(DateTimeFormat) +} + +// DateTime Validation + +func dateTimeIsNull(dateTime *DateTime) bool { + return dateTime != nil && dateTime.IsZero() +} + +func validateDateTimeGt(dateTime *DateTime, than time.Time) bool { + return dateTime != nil && dateTime.After(than) +} + +func validateDateTimeNow(dateTime DateTime) bool { + dur := time.Now().Sub(dateTime.Time).Minutes() + return dur < 1 +} + +func validateDateTimeLt(dateTime DateTime, than time.Time) bool { + return dateTime.Before(than) +} diff --git a/ocpp2.0/common.go b/ocpp2.0/types/types.go similarity index 95% rename from ocpp2.0/common.go rename to ocpp2.0/types/types.go index d55ff4e7..3fdd7141 100644 --- a/ocpp2.0/common.go +++ b/ocpp2.0/types/types.go @@ -1,59 +1,15 @@ -package ocpp2 +// Contains common and shared data types between OCPP 2.0 messages. +package types import ( - "encoding/json" "github.com/lorenzodonini/ocpp-go/ocppj" "gopkg.in/go-playground/validator.v9" - "strings" - "time" ) const ( V2Subprotocol = "ocpp2.0" ) -type DateTime struct { - time.Time -} - -func NewDateTime(time time.Time) *DateTime { - return &DateTime{Time: time} -} - -var DateTimeFormat = time.RFC3339 - -func (dt *DateTime) UnmarshalJSON(input []byte) error { - strInput := string(input) - strInput = strings.Trim(strInput, `"`) - if DateTimeFormat == "" { - defaultTime := time.Time{} - err := json.Unmarshal(input, defaultTime) - if err != nil { - return err - } - dt.Time = defaultTime.Local() - } else { - newTime, err := time.Parse(DateTimeFormat, strInput) - if err != nil { - return err - } - dt.Time = newTime - } - return nil -} - -func (dt *DateTime) MarshalJSON() ([]byte, error) { - if DateTimeFormat == "" { - return json.Marshal(dt.Time) - } - timeStr := FormatTimestamp(dt.Time) - return json.Marshal(timeStr) -} - -func FormatTimestamp(t time.Time) string { - return t.UTC().Format(DateTimeFormat) -} - type PropertyViolation struct { error Property string @@ -630,24 +586,6 @@ type MeterValue struct { SampledValue []SampledValue `json:"sampledValue" validate:"required,min=1,dive"` } -// DateTime Validation -func dateTimeIsNull(dateTime *DateTime) bool { - return dateTime != nil && dateTime.IsZero() -} - -func validateDateTimeGt(dateTime *DateTime, than time.Time) bool { - return dateTime != nil && dateTime.After(than) -} - -func validateDateTimeNow(dateTime DateTime) bool { - dur := time.Now().Sub(dateTime.Time).Minutes() - return dur < 1 -} - -func validateDateTimeLt(dateTime DateTime, than time.Time) bool { - return dateTime.Before(than) -} - // Validator used for validating all OCPP 2.0 messages. // Any additional custom validations must be added to this object for automatic validation. var Validate = ocppj.Validate diff --git a/ocpp2.0/v2.go b/ocpp2.0/v2.go index 40b5f358..a133006f 100644 --- a/ocpp2.0/v2.go +++ b/ocpp2.0/v2.go @@ -4,6 +4,23 @@ package ocpp2 import ( "github.com/gorilla/websocket" "github.com/lorenzodonini/ocpp-go/ocpp" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/authorization" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/availability" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/data" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/diagnostics" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/display" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/iso15118" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/localauth" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/meter" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/provisioning" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/remotecontrol" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/reservation" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/security" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/tariffcost" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/transactions" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/lorenzodonini/ocpp-go/ocppj" "github.com/lorenzodonini/ocpp-go/ws" log "github.com/sirupsen/logrus" @@ -14,10 +31,14 @@ import ( // A Charging Station represents the physical system where an EV can be charged. // You can instantiate a default Charging Station struct by calling NewChargingStation. // -// The logic for incoming messages needs to be implemented, and the message handler has to be registered with the charging station: -// handler := &ChargingStationHandler{} -// centralStation.SetMessageHandler(handler) -// Refer to the ChargingStationHandler interface for the implementation requirements. +// The logic for incoming messages needs to be implemented, and message handlers need to be registered with the charging station: +// handler := &ChargingStationHandler{} // Custom struct +// chargingStation.SetAuthorizationHandler(handler) +// chargingStation.SetProvisioningHandler(handler) +// // set more handlers... +// Refer to the ChargingStationHandler interface of each profile for the implementation requirements. +// +// If a handler for a profile is not set, the OCPP library will reply to incoming messages for that profile with a NotImplemented error. // // A charging station can be started and stopped using the Start and Stop functions. // While running, messages can be sent to the CSMS by calling the Charging Station's functions, e.g. @@ -27,42 +48,64 @@ import ( // To send asynchronous messages and avoid blocking the calling thread, refer to SendRequestAsync. type ChargingStation interface { // Sends a BootNotificationRequest to the CSMS, along with information about the charging station. - BootNotification(reason BootReason, model string, chargePointVendor string, props ...func(request *BootNotificationRequest)) (*BootNotificationConfirmation, error) + BootNotification(reason provisioning.BootReason, model string, chargePointVendor string, props ...func(request *provisioning.BootNotificationRequest)) (*provisioning.BootNotificationResponse, error) // Requests explicit authorization to the CSMS, provided a valid IdToken (typically the customer's). The CSMS may either authorize or reject the token. - Authorize(idToken string, tokenType IdTokenType, props ...func(request *AuthorizeRequest)) (*AuthorizeConfirmation, error) + Authorize(idToken string, tokenType types.IdTokenType, props ...func(request *authorization.AuthorizeRequest)) (*authorization.AuthorizeResponse, error) // Notifies the CSMS, that a previously set charging limit was cleared. - ClearedChargingLimit(chargingLimitSource ChargingLimitSourceType, props ...func(request *ClearedChargingLimitRequest)) (*ClearedChargingLimitConfirmation, error) - // Performs a custom data transfer to the CSMS. The message payload is not pre-defined and must be supported by the CSMS. - DataTransfer(vendorId string, props ...func(request *DataTransferRequest)) (*DataTransferConfirmation, error) + ClearedChargingLimit(chargingLimitSource types.ChargingLimitSourceType, props ...func(request *smartcharging.ClearedChargingLimitRequest)) (*smartcharging.ClearedChargingLimitResponse, error) + // Performs a custom data transfer to the CSMS. The message payload is not pre-defined and must be supported by the CSMS. Every vendor may implement their own proprietary logic for this message. + DataTransfer(vendorId string, props ...func(request *data.DataTransferRequest)) (*data.DataTransferResponse, error) // Notifies the CSMS of a status change during a firmware update procedure (download, installation). - FirmwareStatusNotification(status FirmwareStatus, requestID int, props ...func(request *FirmwareStatusNotificationRequest)) (*FirmwareStatusNotificationConfirmation, error) + FirmwareStatusNotification(status firmware.FirmwareStatus, requestID int, props ...func(request *firmware.FirmwareStatusNotificationRequest)) (*firmware.FirmwareStatusNotificationResponse, error) // Requests a new certificate, required for an ISO 15118 EV, from the CSMS. - Get15118EVCertificate(schemaVersion string, exiRequest string, props ...func(request *Get15118EVCertificateRequest)) (*Get15118EVCertificateConfirmation, error) + Get15118EVCertificate(schemaVersion string, exiRequest string, props ...func(request *iso15118.Get15118EVCertificateRequest)) (*iso15118.Get15118EVCertificateResponse, error) // Requests the CSMS to provide OCSP certificate status for the charging station's 15118 certificates. - GetCertificateStatus(ocspRequestData OCSPRequestDataType, props ...func(request *GetCertificateStatusRequest)) (*GetCertificateStatusConfirmation, error) - //Heartbeat(props ...func(request *HeartbeatRequest)) (*HeartbeatConfirmation, error) - //MeterValues(connectorId int, meterValues []MeterValue, props ...func(request *MeterValuesRequest)) (*MeterValuesConfirmation, error) - //StartTransaction(connectorId int, idTag string, meterStart int, timestamp *DateTime, props ...func(request *StartTransactionRequest)) (*StartTransactionConfirmation, error) - //StopTransaction(meterStop int, timestamp *DateTime, transactionId int, props ...func(request *StopTransactionRequest)) (*StopTransactionConfirmation, error) - //StatusNotification(connectorId int, errorCode ChargePointErrorCode, status ChargePointStatus, props ...func(request *StatusNotificationRequest)) (*StatusNotificationConfirmation, error) - //DiagnosticsStatusNotification(status DiagnosticsStatus, props ...func(request *DiagnosticsStatusNotificationRequest)) (*DiagnosticsStatusNotificationConfirmation, error) - //FirmwareStatusNotification(status FirmwareStatus, props ...func(request *FirmwareStatusNotificationRequest)) (*FirmwareStatusNotificationConfirmation, error) + GetCertificateStatus(ocspRequestData types.OCSPRequestDataType, props ...func(request *iso15118.GetCertificateStatusRequest)) (*iso15118.GetCertificateStatusResponse, error) - // SetMessageHandler sets a handler for incoming messages from the CSMS. - // Refer to ChargingStationHandler for info on how to handle the callbacks. - SetMessageHandler(handler ChargingStationHandler) + // Registers a handler for incoming security profile messages + SetSecurityHandler(handler security.ChargingStationHandler) + // Registers a handler for incoming provisioning profile messages + SetProvisioningHandler(handler provisioning.ChargingStationHandler) + // Registers a handler for incoming authorization profile messages + SetAuthorizationHandler(handler authorization.ChargingStationHandler) + // Registers a handler for incoming local authorization list profile messages + SetLocalAuthListHandler(handler localauth.ChargingStationHandler) + // Registers a handler for incoming transactions profile messages + SetTransactionsHandler(handler transactions.ChargingStationHandler) + // Registers a handler for incoming remote control profile messages + SetRemoteControlHandler(handler remotecontrol.ChargingStationHandler) + // Registers a handler for incoming availability profile messages + SetAvailabilityHandler(handler availability.ChargingStationHandler) + // Registers a handler for incoming reservation profile messages + SetReservationHandler(handler reservation.ChargingStationHandler) + // Registers a handler for incoming tariff and cost profile messages + SetTariffCostHandler(handler tariffcost.ChargingStationHandler) + // Registers a handler for incoming meter profile messages + SetMeterHandler(handler meter.ChargingStationHandler) + // Registers a handler for incoming smart charging messages + SetSmartChargingHandler(handler smartcharging.ChargingStationHandler) + // Registers a handler for incoming firmware management messages + SetFirmwareHandler(handler firmware.ChargingStationHandler) + // Registers a handler for incoming ISO15118 management messages + SetISO15118Handler(handler iso15118.ChargingStationHandler) + // Registers a handler for incoming diagnostics messages + SetDiagnosticsHandler(handler diagnostics.ChargingStationHandler) + // Registers a handler for incoming display messages + SetDisplayHandler(handler display.ChargingStationHandler) + // Registers a handler for incoming data transfer messages + SetDataHandler(handler data.ChargingStationHandler) // Sends a request to the CSMS. // The CSMS will respond with a confirmation, or with an error if the request was invalid or could not be processed. // In case of network issues (i.e. the remote host couldn't be reached), the function also returns an error. // // The request is synchronous blocking. - SendRequest(request ocpp.Request) (ocpp.Confirmation, error) + SendRequest(request ocpp.Request) (ocpp.Response, error) // Sends an asynchronous request to the CSMS. // The CSMS will respond with a confirmation message, or with an error if the request was invalid or could not be processed. // This result is propagated via a callback, called asynchronously. // // In case of network issues (i.e. the remote host couldn't be reached), the function returns an error directly. In this case, the callback is never invoked. - SendRequestAsync(request ocpp.Request, callback func(confirmation ocpp.Confirmation, protoError error)) error + SendRequestAsync(request ocpp.Request, callback func(confirmation ocpp.Response, protoError error)) error // Connects to the CSMS and starts the charging station routine. // The function doesn't block and returns right away, after having attempted to open a connection to the CSMS. // If the connection couldn't be opened, an error is returned. @@ -93,13 +136,13 @@ type ChargingStation interface { // if !ok { // log.Fatal("couldn't parse PEM certificate") // } -// cp := NewChargingStation("someUniqueId", nil, ws.NewTLSClient(&tls.Config{ +// cs := NewChargingStation("someUniqueId", nil, ws.NewTLSClient(&tls.Config{ // RootCAs: certPool, // }) // // For more advanced options, or if a custom networking/occpj layer is required, -// please refer to ocppj.ChargingStation and ws.WsClient. -func NewChargingStation(id string, dispatcher *ocppj.ChargePoint, client ws.WsClient) ChargingStation { +// please refer to ocppj.Client and ws.WsClient. +func NewChargingStation(id string, dispatcher *ocppj.Client, client ws.WsClient) ChargingStation { if client == nil { client = ws.NewClient() } @@ -107,107 +150,138 @@ func NewChargingStation(id string, dispatcher *ocppj.ChargePoint, client ws.WsCl // Look for v2.0 subprotocol and add it, if not found alreadyExists := false for _, proto := range dialer.Subprotocols { - if proto == V2Subprotocol { + if proto == types.V2Subprotocol { alreadyExists = true break } } if !alreadyExists { - dialer.Subprotocols = append(dialer.Subprotocols, V2Subprotocol) + dialer.Subprotocols = append(dialer.Subprotocols, types.V2Subprotocol) } }) if dispatcher == nil { - dispatcher = ocppj.NewChargePoint(id, client, CoreProfile) + dispatcher = ocppj.NewClient(id, client, authorization.Profile, availability.Profile, data.Profile, diagnostics.Profile, display.Profile, firmware.Profile, iso15118.Profile, localauth.Profile, meter.Profile, provisioning.Profile, remotecontrol.Profile, reservation.Profile, security.Profile, smartcharging.Profile, tariffcost.Profile, transactions.Profile) } - cp := chargingStation{client: dispatcher, confirmationListener: make(chan ocpp.Confirmation), errorListener: make(chan error)} - cp.client.SetConfirmationHandler(func(confirmation ocpp.Confirmation, requestId string) { - cp.confirmationListener <- confirmation + cs := chargingStation{client: dispatcher, responseHandler: make(chan ocpp.Response), errorHandler: make(chan error)} + cs.client.SetResponseHandler(func(confirmation ocpp.Response, requestId string) { + cs.responseHandler <- confirmation }) - cp.client.SetErrorHandler(func(err *ocpp.Error, details interface{}) { - cp.errorListener <- err + cs.client.SetErrorHandler(func(err *ocpp.Error, details interface{}) { + cs.errorHandler <- err }) - cp.client.SetRequestHandler(cp.handleIncomingRequest) - return &cp + cs.client.SetRequestHandler(cs.handleIncomingRequest) + return &cs } // -------------------- v2.0 CSMS -------------------- -// A Charging Station represents the physical system where an EV can be charged. - // A Charging Station Management System (CSMS) manages Charging Stations and has the information for authorizing Management Users for using its Charging Stations. // You can instantiate a default CSMS struct by calling the NewCSMS function. // -// The logic for handling incoming messages needs to be implemented, and the message handler has to be registered with the CSMS: -// handler := &CSMSHandler{} -// csms.SetMessageHandler(handler) -// Refer to the CSMSHandler interface for the implementation requirements. +// The logic for handling incoming messages needs to be implemented, and message handlers need to be registered with the CSMS: +// handler := &CSMSHandler{} // Custom struct +// csms.SetAuthorizationHandler(handler) +// csms.SetProvisioningHandler(handler) +// // set more handlers... +// Refer to the CSMSHandler interface of each profile for the implementation requirements. +// +// If a handler for a profile is not set, the OCPP library will reply to incoming messages for that profile with a NotImplemented error. // -// A CSMS station can be started by using the Start function. +// A CSMS can be started by using the Start function. // To be notified of incoming (dis)connections from charging stations refer to the SetNewChargingStationHandler and SetChargingStationDisconnectedHandler functions. // -// While running, messages can be sent to a Charging Station by calling the CSMS's, e.g.: -// callback := func(conf *ClearDisplayConfirmation, err error) { +// While running, messages can be sent to a Charging Station by calling the CSMS's functions, e.g.: +// callback := func(conf *ClearDisplayResponse, err error) { // // handle the response... // } // clearDisplayConf, err := csms.ClearDisplay("cs0001", callback, 10) // All messages are sent asynchronously and do not block the caller. type CSMS interface { // Cancel a pending reservation, provided the reservationId, on a charging station. - CancelReservation(clientId string, callback func(*CancelReservationConfirmation, error), reservationId int, props ...func(*CancelReservationRequest)) error - // The CSMS installs a new certificate, signed by the CA, on the charging station. This typically follows a SignCertificate message, initiated by the charging station. - CertificateSigned(clientId string, callback func(*CertificateSignedConfirmation, error), certificate []string, props ...func(*CertificateSignedRequest)) error + CancelReservation(clientId string, callback func(*reservation.CancelReservationResponse, error), reservationId int, props ...func(*reservation.CancelReservationRequest)) error + // The CSMS installs a new certificate (chain), signed by the CA, on the charging station. This typically follows a SignCertificate message, initiated by the charging station. + CertificateSigned(clientId string, callback func(*security.CertificateSignedResponse, error), certificate []string, props ...func(*security.CertificateSignedRequest)) error // Instructs a charging station to change its availability to the desired operational status. - ChangeAvailability(clientId string, callback func(*ChangeAvailabilityConfirmation, error), evseID int, operationalStatus OperationalStatus, props ...func(*ChangeAvailabilityRequest)) error + ChangeAvailability(clientId string, callback func(*availability.ChangeAvailabilityResponse, error), evseID int, operationalStatus availability.OperationalStatus, props ...func(*availability.ChangeAvailabilityRequest)) error // Instructs a charging station to clear its current authorization cache. All authorization saved locally will be invalidated. - ClearCache(clientId string, callback func(*ClearCacheConfirmation, error), props ...func(*ClearCacheRequest)) error + ClearCache(clientId string, callback func(*authorization.ClearCacheResponse, error), props ...func(*authorization.ClearCacheRequest)) error // Instructs a charging station to clear some or all charging profiles, previously sent to the charging station. - ClearChargingProfile(clientId string, callback func(*ClearChargingProfileConfirmation, error), props ...func(request *ClearChargingProfileRequest)) error + ClearChargingProfile(clientId string, callback func(*smartcharging.ClearChargingProfileResponse, error), props ...func(request *smartcharging.ClearChargingProfileRequest)) error // Removes a specific display message, currently configured in a charging station. - ClearDisplay(clientId string, callback func(*ClearDisplayConfirmation, error), id int, props ...func(*ClearDisplayRequest)) error + ClearDisplay(clientId string, callback func(*display.ClearDisplayResponse, error), id int, props ...func(*display.ClearDisplayRequest)) error // Removes one or more monitoring settings from a charging station for the given variable IDs. - ClearVariableMonitoring(clientId string, callback func(*ClearVariableMonitoringConfirmation, error), id []int, props ...func(*ClearVariableMonitoringRequest)) error + ClearVariableMonitoring(clientId string, callback func(*diagnostics.ClearVariableMonitoringResponse, error), id []int, props ...func(*diagnostics.ClearVariableMonitoringRequest)) error // Instructs a charging station to display the updated current total cost of an ongoing transaction. - CostUpdated(clientId string, callback func(*CostUpdatedConfirmation, error), totalCost float64, transactionId string, props ...func(*CostUpdatedRequest)) error + CostUpdated(clientId string, callback func(*tariffcost.CostUpdatedResponse, error), totalCost float64, transactionId string, props ...func(*tariffcost.CostUpdatedRequest)) error // Instructs a charging station to send one or more reports, containing raw customer information. - CustomerInformation(clientId string, callback func(*CustomerInformationConfirmation, error), requestId int, report bool, clear bool, props ...func(*CustomerInformationRequest)) error - // Performs a custom data transfer to a charging station. The message payload is not pre-defined and must be supported by the charging station. - DataTransfer(clientId string, callback func(*DataTransferConfirmation, error), vendorId string, props ...func(*DataTransferRequest)) error + CustomerInformation(clientId string, callback func(*diagnostics.CustomerInformationResponse, error), requestId int, report bool, clear bool, props ...func(*diagnostics.CustomerInformationRequest)) error + // Performs a custom data transfer to a charging station. The message payload is not pre-defined and must be supported by the charging station. Every vendor may implement their own proprietary logic for this message. + DataTransfer(clientId string, callback func(*data.DataTransferResponse, error), vendorId string, props ...func(*data.DataTransferRequest)) error // Deletes a previously installed certificate on a charging station. - DeleteCertificate(clientId string, callback func(*DeleteCertificateConfirmation, error), data CertificateHashData, props ...func(*DeleteCertificateRequest)) error + DeleteCertificate(clientId string, callback func(*iso15118.DeleteCertificateResponse, error), data types.CertificateHashData, props ...func(*iso15118.DeleteCertificateRequest)) error // Requests a report from a charging station. The charging station will asynchronously send the report in chunks using NotifyReportRequest messages. - GetBaseReport(clientId string, callback func(*GetBaseReportConfirmation, error), requestId int, reportBase ReportBaseType, props ...func(*GetBaseReportRequest)) error + GetBaseReport(clientId string, callback func(*provisioning.GetBaseReportResponse, error), requestId int, reportBase provisioning.ReportBaseType, props ...func(*provisioning.GetBaseReportRequest)) error // Request a charging station to report some or all installed charging profiles. The charging station will report these asynchronously using ReportChargingProfiles messages. - GetChargingProfiles(clientId string, callback func(*GetChargingProfilesConfirmation, error), chargingProfile ChargingProfileCriterion, props ...func(*GetChargingProfilesRequest)) error + GetChargingProfiles(clientId string, callback func(*smartcharging.GetChargingProfilesResponse, error), chargingProfile smartcharging.ChargingProfileCriterion, props ...func(*smartcharging.GetChargingProfilesRequest)) error // Requests a charging station to report the composite charging schedule for the indicated duration and evseID. - GetCompositeSchedule(clientId string, callback func(*GetCompositeScheduleConfirmation, error), duration int, evseId int, props ...func(*GetCompositeScheduleRequest)) error + GetCompositeSchedule(clientId string, callback func(*smartcharging.GetCompositeScheduleResponse, error), duration int, evseId int, props ...func(*smartcharging.GetCompositeScheduleRequest)) error // Retrieves all messages currently configured on a charging station. - GetDisplayMessages(clientId string, callback func(*GetDisplayMessagesConfirmation, error), requestId int, props ...func(*GetDisplayMessagesRequest)) error + GetDisplayMessages(clientId string, callback func(*display.GetDisplayMessagesResponse, error), requestId int, props ...func(*display.GetDisplayMessagesRequest)) error // Retrieves all installed certificates on a charging station. - GetInstalledCertificateIds(clientId string, callback func(*GetInstalledCertificateIdsConfirmation, error), typeOfCertificate CertificateUse, props ...func(*GetInstalledCertificateIdsRequest)) error + GetInstalledCertificateIds(clientId string, callback func(*iso15118.GetInstalledCertificateIdsResponse, error), typeOfCertificate types.CertificateUse, props ...func(*iso15118.GetInstalledCertificateIdsRequest)) error // Queries a charging station for version number of the Local Authorization List. - GetLocalListVersion(clientId string, callback func(*GetLocalListVersionConfirmation, error), props ...func(*GetLocalListVersionRequest)) error + GetLocalListVersion(clientId string, callback func(*localauth.GetLocalListVersionResponse, error), props ...func(*localauth.GetLocalListVersionRequest)) error // Instructs a charging station to upload a diagnostics or security logfile to the CSMS. - GetLog(clientId string, callback func(*GetLogConfirmation, error), logType LogType, requestID int, logParameters LogParameters, props ...func(*GetLogRequest)) error + GetLog(clientId string, callback func(*diagnostics.GetLogResponse, error), logType diagnostics.LogType, requestID int, logParameters diagnostics.LogParameters, props ...func(*diagnostics.GetLogRequest)) error // Requests a report about configured monitoring settings per component and variable from a charging station. The reports will be uploaded asynchronously using NotifyMonitoringReport messages. - GetMonitoringReport(clientId string, callback func(*GetMonitoringReportConfirmation, error), props ...func(*GetMonitoringReportRequest)) error + GetMonitoringReport(clientId string, callback func(*diagnostics.GetMonitoringReportResponse, error), props ...func(*diagnostics.GetMonitoringReportRequest)) error //GetConfiguration(clientId string, callback func(*GetConfigurationConfirmation, error), keys []string, props ...func(*GetConfigurationRequest)) error //RemoteStartTransaction(clientId string, callback func(*RemoteStartTransactionConfirmation, error), idTag string, props ...func(*RemoteStartTransactionRequest)) error //RemoteStopTransaction(clientId string, callback func(*RemoteStopTransactionConfirmation, error), transactionId int, props ...func(request *RemoteStopTransactionRequest)) error //Reset(clientId string, callback func(*ResetConfirmation, error), resetType ResetType, props ...func(*ResetRequest)) error //UnlockConnector(clientId string, callback func(*UnlockConnectorConfirmation, error), connectorId int, props ...func(*UnlockConnectorRequest)) error - //GetLocalListVersion(clientId string, callback func(*GetLocalListVersionConfirmation, error), props ...func(request *GetLocalListVersionRequest)) error + //GetLocalListVersion(clientId string, callback func(*GetLocalListVersionResponse, error), props ...func(request *GetLocalListVersionRequest)) error //SendLocalList(clientId string, callback func(*SendLocalListConfirmation, error), version int, updateType UpdateType, props ...func(request *SendLocalListRequest)) error //GetDiagnostics(clientId string, callback func(*GetDiagnosticsConfirmation, error), location string, props ...func(request *GetDiagnosticsRequest)) error //UpdateFirmware(clientId string, callback func(*UpdateFirmwareConfirmation, error), location string, retrieveDate *DateTime, props ...func(request *UpdateFirmwareRequest)) error //ReserveNow(clientId string, callback func(*ReserveNowConfirmation, error), connectorId int, expiryDate *DateTime, idTag string, reservationId int, props ...func(request *ReserveNowRequest)) error - //CancelReservation(clientId string, callback func(*CancelReservationConfirmation, error), reservationId int, props ...func(request *CancelReservationRequest)) error + //CancelReservation(clientId string, callback func(*CancelReservationResponse, error), reservationId int, props ...func(request *CancelReservationRequest)) error //TriggerMessage(clientId string, callback func(*TriggerMessageConfirmation, error), requestedMessage MessageTrigger, props ...func(request *TriggerMessageRequest)) error //SetChargingProfile(clientId string, callback func(*SetChargingProfileConfirmation, error), connectorId int, chargingProfile *ChargingProfile, props ...func(request *SetChargingProfileRequest)) error - //GetCompositeSchedule(clientId string, callback func(*GetCompositeScheduleConfirmation, error), connectorId int, duration int, props ...func(request *GetCompositeScheduleRequest)) error + //GetCompositeSchedule(clientId string, callback func(*GetCompositeScheduleResponse, error), connectorId int, duration int, props ...func(request *GetCompositeScheduleRequest)) error - // SetMessageHandler sets a handler for incoming messages from the Charging station. - // Refer to CSMSHandler for info on how to handle the callbacks. - SetMessageHandler(handler CSMSHandler) + // Registers a handler for incoming security profile messages. + SetSecurityHandler(handler security.CSMSHandler) + // Registers a handler for incoming provisioning profile messages. + SetProvisioningHandler(handler provisioning.CSMSHandler) + // Registers a handler for incoming authorization profile messages. + SetAuthorizationHandler(handler authorization.CSMSHandler) + // Registers a handler for incoming local authorization list profile messages. + SetLocalAuthListHandler(handler localauth.CSMSHandler) + // Registers a handler for incoming transactions profile messages + SetTransactionsHandler(handler transactions.CSMSHandler) + // Registers a handler for incoming remote control profile messages + SetRemoteControlHandler(handler remotecontrol.CSMSHandler) + // Registers a handler for incoming availability profile messages + SetAvailabilityHandler(handler availability.CSMSHandler) + // Registers a handler for incoming reservation profile messages + SetReservationHandler(handler reservation.CSMSHandler) + // Registers a handler for incoming tariff and cost profile messages + SetTariffCostHandler(handler tariffcost.CSMSHandler) + // Registers a handler for incoming meter profile messages + SetMeterHandler(handler meter.CSMSHandler) + // Registers a handler for incoming smart charging messages + SetSmartChargingHandler(handler smartcharging.CSMSHandler) + // Registers a handler for incoming firmware management messages + SetFirmwareHandler(handler firmware.CSMSHandler) + // Registers a handler for incoming ISO15118 management messages + SetISO15118Handler(handler iso15118.CSMSHandler) + // Registers a handler for incoming diagnostics messages + SetDiagnosticsHandler(handler diagnostics.CSMSHandler) + // Registers a handler for incoming display messages + SetDisplayHandler(handler display.CSMSHandler) + // Registers a handler for incoming data transfer messages + SetDataHandler(handler data.CSMSHandler) // Registers a handler for new incoming Charging station connections. SetNewChargingStationHandler(handler func(chargePointId string)) // Registers a handler for Charging station disconnections. @@ -216,7 +290,7 @@ type CSMS interface { // The charging station will respond with a confirmation message, or with an error if the request was invalid or could not be processed. // This result is propagated via a callback, called asynchronously. // In case of network issues (i.e. the remote host couldn't be reached), the function returns an error directly. In this case, the callback is never invoked. - SendRequestAsync(clientId string, request ocpp.Request, callback func(ocpp.Confirmation, error)) error + SendRequestAsync(clientId string, request ocpp.Request, callback func(ocpp.Response, error)) error // Starts running the CSMS on the specified port and URL. // The central system runs as a daemon and handles incoming charge point connections and messages. @@ -227,26 +301,26 @@ type CSMS interface { // Creates a new OCPP 2.0 CSMS. // // The dispatcher and client parameters may be omitted, in order to use a default configuration: -// chargingStation := NewCSMS(nil, nil) +// csms := NewCSMS(nil, nil) // // It is recommended to use the default configuration, unless a custom networking / ocppj layer is required. -// The default dispatcher supports all OCPP 2.0 features out-of-the-box. +// The default dispatcher supports all implemented OCPP 2.0 features out-of-the-box. // // If you need a TLS server, you may use the following: -// cs := NewCSMS(nil, ws.NewTLSServer("certificatePath", "privateKeyPath")) -func NewCSMS(dispatcher *ocppj.CentralSystem, server ws.WsServer) CSMS { +// csms := NewCSMS(nil, ws.NewTLSServer("certificatePath", "privateKeyPath")) +func NewCSMS(dispatcher *ocppj.Server, server ws.WsServer) CSMS { if server == nil { server = ws.NewServer() } - server.AddSupportedSubprotocol(V2Subprotocol) + server.AddSupportedSubprotocol(types.V2Subprotocol) if dispatcher == nil { - dispatcher = ocppj.NewCentralSystem(server, CoreProfile) + dispatcher = ocppj.NewServer(server, authorization.Profile, availability.Profile, data.Profile, diagnostics.Profile, display.Profile, firmware.Profile, iso15118.Profile, localauth.Profile, meter.Profile, provisioning.Profile, remotecontrol.Profile, reservation.Profile, security.Profile, smartcharging.Profile, tariffcost.Profile, transactions.Profile) } cs := csms{ server: dispatcher, - callbacks: map[string]func(confirmation ocpp.Confirmation, err error){}} + callbacks: map[string]func(confirmation ocpp.Response, err error){}} cs.server.SetRequestHandler(cs.handleIncomingRequest) - cs.server.SetConfirmationHandler(cs.handleIncomingConfirmation) + cs.server.SetResponseHandler(cs.handleIncomingResponse) cs.server.SetErrorHandler(cs.handleIncomingError) return &cs } diff --git a/ocpp2.0/v2_handlers.go b/ocpp2.0/v2_handlers.go deleted file mode 100644 index 5d4e5135..00000000 --- a/ocpp2.0/v2_handlers.go +++ /dev/null @@ -1,155 +0,0 @@ -package ocpp2 - -import ( - "github.com/lorenzodonini/ocpp-go/ocpp" -) - -const ( - BootNotificationFeatureName = "BootNotification" - AuthorizeFeatureName = "Authorize" - CancelReservationFeatureName = "CancelReservation" - CertificateSignedFeatureName = "CertificateSigned" - ChangeAvailabilityFeatureName = "ChangeAvailability" - ClearCacheFeatureName = "ClearCache" - ClearDisplayFeatureName = "ClearDisplay" - ClearChargingProfileFeatureName = "ClearChargingProfile" - ClearedChargingLimitFeatureName = "ClearedChargingLimit" - ClearVariableMonitoringFeatureName = "ClearVariableMonitoring" - CostUpdatedFeatureName = "CostUpdated" - CustomerInformationFeatureName = "CustomerInformation" - DataTransferFeatureName = "DataTransfer" - DeleteCertificateFeatureName = "DeleteCertificate" - FirmwareStatusNotificationFeatureName = "FirmwareStatusNotification" - Get15118EVCertificateFeatureName = "Get15118EVCertificate" - GetBaseReportFeatureName = "GetBaseReport" - GetCertificateStatusFeatureName = "GetCertificateStatus" - GetChargingProfilesFeatureName = "GetChargingProfiles" - GetCompositeScheduleFeatureName = "GetCompositeSchedule" - GetDisplayMessagesFeatureName = "GetDisplayMessages" - GetInstalledCertificateIdsFeatureName = "GetInstalledCertificateIds" - GetLocalListVersionFeatureName = "GetLocalListVersion" - GetLogFeatureName = "GetLog" - GetMonitoringReportFeatureName = "GetMonitoringReport" - // GetConfigurationFeatureName = "GetConfiguration" - // HeartbeatFeatureName = "Heartbeat" - // MeterValuesFeatureName = "MeterValues" - // RemoteStartTransactionFeatureName = "RemoteStartTransaction" - // RemoteStopTransactionFeatureName = "RemoteStopTransaction" - // ResetFeatureName = "Reset" - // StartTransactionFeatureName = "StartTransaction" - // StopTransactionFeatureName = "StopTransaction" - // StatusNotificationFeatureName = "StatusNotification" - // UnlockConnectorFeatureName = "UnlockConnector" - //SetChargingProfileFeatureName = "SetChargingProfile" - //GetCompositeScheduleFeatureName = "GetCompositeSchedule" -) - -type CSMSHandler interface { - // OnCancelReservation is called on the CSMS whenever an AuthorizeRequest is received from a charging station. - OnAuthorize(chargingStationID string, request *AuthorizeRequest) (confirmation *AuthorizeConfirmation, err error) - // OnBootNotification is called on the CSMS whenever a BootNotificationRequest is received from a charging station. - OnBootNotification(chargingStationID string, request *BootNotificationRequest) (confirmation *BootNotificationConfirmation, err error) - // OnClearedChargingLimit is called on the CSMS whenever a ClearedChargingLimitRequest is received from a charging station. - OnClearedChargingLimit(chargingStationID string, request *ClearedChargingLimitRequest) (confirmation *ClearedChargingLimitConfirmation, err error) - // OnDataTransfer is called on the CSMS whenever a DataTransferRequest is received from a charging station. - OnDataTransfer(chargingStationID string, request *DataTransferRequest) (confirmation *DataTransferConfirmation, err error) - // OnFirmwareStatusNotification is called on the CSMS whenever a FirmwareStatusNotificationRequest is received from a charging station. - OnFirmwareStatusNotification(chargingStationID string, request *FirmwareStatusNotificationRequest) (confirmation *FirmwareStatusNotificationConfirmation, err error) - // OnGet15118EVCertificate is called on the CSMS whenever a Get15118EVCertificateRequest is received from a charging station. - OnGet15118EVCertificate(chargingStationID string, request *Get15118EVCertificateRequest) (confirmation *Get15118EVCertificateConfirmation, err error) - // OnGetCertificateStatus is called on the CSMS whenever a GetCertificateStatusRequest is received from a charging station. - OnGetCertificateStatus(chargingStationID string, request *GetCertificateStatusRequest) (confirmation *GetCertificateStatusConfirmation, err error) - // OnHeartbeat(chargePointId string, request *HeartbeatRequest) (confirmation *HeartbeatConfirmation, err error) - // OnMeterValues(chargePointId string, request *MeterValuesRequest) (confirmation *MeterValuesConfirmation, err error) - // OnStatusNotification(chargePointId string, request *StatusNotificationRequest) (confirmation *StatusNotificationConfirmation, err error) - // OnStartTransaction(chargePointId string, request *StartTransactionRequest) (confirmation *StartTransactionConfirmation, err error) - // OnStopTransaction(chargePointId string, request *StopTransactionRequest) (confirmation *StopTransactionConfirmation, err error) -} - -type ChargingStationHandler interface { - // OnCancelReservation is called on a charging station whenever a CancelReservationRequest is received from the CSMS. - OnCancelReservation(request *CancelReservationRequest) (confirmation *CancelReservationConfirmation, err error) - // OnCertificateSigned is called on a charging station whenever a CertificateSignedRequest is received from the CSMS. - OnCertificateSigned(request *CertificateSignedRequest) (confirmation *CertificateSignedConfirmation, err error) - // OnChangeAvailability is called on a charging station whenever a ChangeAvailabilityRequest is received from the CSMS. - OnChangeAvailability(request *ChangeAvailabilityRequest) (confirmation *ChangeAvailabilityConfirmation, err error) - // OnClearCache is called on a charging station whenever a ClearCacheRequest is received from the CSMS. - OnClearCache(request *ClearCacheRequest) (confirmation *ClearCacheConfirmation, err error) - // OnClearDisplay is called on a charging station whenever a ClearDisplayRequest is received from the CSMS. - OnClearDisplay(request *ClearDisplayRequest) (confirmation *ClearDisplayConfirmation, err error) - // OnClearChargingProfile is called on a charging station whenever a ClearChargingProfileRequest is received from the CSMS. - OnClearChargingProfile(request *ClearChargingProfileRequest) (confirmation *ClearChargingProfileConfirmation, err error) - // OnClearVariableMonitoring is called on a charging station whenever a ClearVariableMonitoringRequest is received from the CSMS. - OnClearVariableMonitoring(request *ClearVariableMonitoringRequest) (confirmation *ClearVariableMonitoringConfirmation, err error) - // OnCostUpdated is called on a charging station whenever a CostUpdatedRequest is received from the CSMS. - OnCostUpdated(request *CostUpdatedRequest) (confirmation *CostUpdatedConfirmation, err error) - // OnCustomerInformation is called on a charging station whenever a CustomerInformationRequest is received from the CSMS. - OnCustomerInformation(request *CustomerInformationRequest) (confirmation *CustomerInformationConfirmation, err error) - // OnDataTransfer is called on a charging station whenever a DataTransferRequest is received from the CSMS. - OnDataTransfer(request *DataTransferRequest) (confirmation *DataTransferConfirmation, err error) - // OnDeleteCertificate is called on a charging station whenever a DeleteCertificateRequest is received from the CSMS. - OnDeleteCertificate(request *DeleteCertificateRequest) (confirmation *DeleteCertificateConfirmation, err error) - // OnGetBaseReport is called on a charging station whenever a GetBaseReportRequest is received from the CSMS. - OnGetBaseReport(request *GetBaseReportRequest) (confirmation *GetBaseReportConfirmation, err error) - // OnGetChargingProfiles is called on a charging station whenever a GetChargingProfilesRequest is received from the CSMS. - OnGetChargingProfiles(request *GetChargingProfilesRequest) (confirmation *GetChargingProfilesConfirmation, err error) - // OnGetCompositeSchedule is called on a charging station whenever a GetCompositeScheduleRequest is received from the CSMS. - OnGetCompositeSchedule(request *GetCompositeScheduleRequest) (confirmation *GetCompositeScheduleConfirmation, err error) - // OnGetDisplayMessages is called on a charging station whenever a GetDisplayMessagesRequest is received from the CSMS. - OnGetDisplayMessages(request *GetDisplayMessagesRequest) (confirmation *GetDisplayMessagesConfirmation, err error) - // OnGetInstalledCertificateIds is called on a charging station whenever a GetInstalledCertificateIdsRequest is received from the CSMS. - OnGetInstalledCertificateIds(request *GetInstalledCertificateIdsRequest) (confirmation *GetInstalledCertificateIdsConfirmation, err error) - // OnGetLocalListVersion is called on a charging station whenever a GetLocalListVersionRequest is received from the CSMS. - OnGetLocalListVersion(request *GetLocalListVersionRequest) (confirmation *GetLocalListVersionConfirmation, err error) - // OnGetLog is called on a charging station whenever a GetLogRequest is received from the CSMS. - OnGetLog(request *GetLogRequest) (confirmation *GetLogConfirmation, err error) - // OnGetMonitoringReport is called on a charging station whenever a GetMonitoringReportRequest is received from the CSMS. - OnGetMonitoringReport(request *GetMonitoringReportRequest) (confirmation *GetMonitoringReportConfirmation, err error) - // OnGetConfiguration(request *GetConfigurationRequest) (confirmation *GetConfigurationConfirmation, err error) - // OnRemoteStartTransaction(request *RemoteStartTransactionRequest) (confirmation *RemoteStartTransactionConfirmation, err error) - // OnRemoteStopTransaction(request *RemoteStopTransactionRequest) (confirmation *RemoteStopTransactionConfirmation, err error) - // OnReset(request *ResetRequest) (confirmation *ResetConfirmation, err error) - // OnUnlockConnector(request *UnlockConnectorRequest) (confirmation *UnlockConnectorConfirmation, err error) -} - -var CoreProfileName = "core" - -var CoreProfile = ocpp.NewProfile( - CoreProfileName, - BootNotificationFeature{}, - AuthorizeFeature{}, - CancelReservationFeature{}, - CertificateSignedFeature{}, - ChangeAvailabilityFeature{}, - ClearCacheFeature{}, - ClearDisplayFeature{}, - ClearChargingProfileFeature{}, - ClearedChargingLimitFeature{}, - ClearVariableMonitoringFeature{}, - CostUpdatedFeature{}, - CustomerInformationFeature{}, - DataTransferFeature{}, - DeleteCertificateFeature{}, - FirmwareStatusNotificationFeature{}, - Get15118EVCertificateFeature{}, - GetBaseReportFeature{}, - GetCertificateStatusFeature{}, - GetChargingProfilesFeature{}, - GetCompositeScheduleFeature{}, - GetDisplayMessagesFeature{}, - GetInstalledCertificateIdsFeature{}, - GetLocalListVersionFeature{}, - GetLogFeature{}, - GetMonitoringReportFeature{}, - -//GetConfigurationFeature{}, -//HeartbeatFeature{}, -//MeterValuesFeature{}, -//RemoteStartTransactionFeature{}, -//RemoteStopTransactionFeature{}, -//StartTransactionFeature{}, -//StopTransactionFeature{}, -//StatusNotificationFeature{}, -//ResetFeature{}, -//UnlockConnectorFeature{} -) diff --git a/ocpp2.0_test/authorize_test.go b/ocpp2.0_test/authorize_test.go index e594f941..b92d09cf 100644 --- a/ocpp2.0_test/authorize_test.go +++ b/ocpp2.0_test/authorize_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/authorization" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,15 +13,15 @@ import ( func (suite *OcppV2TestSuite) TestAuthorizeRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.AuthorizeRequest{EvseID: []int{4,2}, IdToken: ocpp2.IdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: []ocpp2.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}, CertificateHashData: []ocpp2.OCSPRequestDataType{{SerialNumber: "serial0", HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash0", IssuerKeyHash: "hash1", ResponderURL: "www.someurl.com"}}}, true}, - {ocpp2.AuthorizeRequest{EvseID: []int{4,2}, IdToken: ocpp2.IdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: []ocpp2.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}}, true}, - {ocpp2.AuthorizeRequest{IdToken: ocpp2.IdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: []ocpp2.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}, CertificateHashData: []ocpp2.OCSPRequestDataType{{SerialNumber: "serial0", HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash0", IssuerKeyHash: "hash1", ResponderURL: "www.someurl.com"}}}, true}, - {ocpp2.AuthorizeRequest{IdToken: ocpp2.IdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: []ocpp2.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}, CertificateHashData: []ocpp2.OCSPRequestDataType{}}, true}, - {ocpp2.AuthorizeRequest{EvseID: []int{}, IdToken: ocpp2.IdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: []ocpp2.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}}, true}, - {ocpp2.AuthorizeRequest{}, false}, - {ocpp2.AuthorizeRequest{IdToken: ocpp2.IdToken{Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: []ocpp2.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}}, false}, - {ocpp2.AuthorizeRequest{IdToken: ocpp2.IdToken{Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: []ocpp2.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}, CertificateHashData: []ocpp2.OCSPRequestDataType{{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash0", IssuerKeyHash: "hash1"}}}, false}, - {ocpp2.AuthorizeRequest{IdToken: ocpp2.IdToken{Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: []ocpp2.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}, CertificateHashData: []ocpp2.OCSPRequestDataType{{SerialNumber: "s0", HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "h0", IssuerKeyHash: "h0.1"},{SerialNumber: "s1", HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "h1", IssuerKeyHash: "h1.1"},{SerialNumber: "s2", HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "h2", IssuerKeyHash: "h2.1"},{SerialNumber: "s3", HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "h3", IssuerKeyHash: "h3.1"},{SerialNumber: "s4", HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "h4", IssuerKeyHash: "h4.1"}}}, false}, + {authorization.AuthorizeRequest{EvseID: []int{4, 2}, IdToken: types.IdToken{IdToken: "1234", Type: types.IdTokenTypeKeyCode, AdditionalInfo: []types.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}, CertificateHashData: []types.OCSPRequestDataType{{SerialNumber: "serial0", HashAlgorithm: types.SHA256, IssuerNameHash: "hash0", IssuerKeyHash: "hash1", ResponderURL: "www.someurl.com"}}}, true}, + {authorization.AuthorizeRequest{EvseID: []int{4, 2}, IdToken: types.IdToken{IdToken: "1234", Type: types.IdTokenTypeKeyCode, AdditionalInfo: []types.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}}, true}, + {authorization.AuthorizeRequest{IdToken: types.IdToken{IdToken: "1234", Type: types.IdTokenTypeKeyCode, AdditionalInfo: []types.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}, CertificateHashData: []types.OCSPRequestDataType{{SerialNumber: "serial0", HashAlgorithm: types.SHA256, IssuerNameHash: "hash0", IssuerKeyHash: "hash1", ResponderURL: "www.someurl.com"}}}, true}, + {authorization.AuthorizeRequest{IdToken: types.IdToken{IdToken: "1234", Type: types.IdTokenTypeKeyCode, AdditionalInfo: []types.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}, CertificateHashData: []types.OCSPRequestDataType{}}, true}, + {authorization.AuthorizeRequest{EvseID: []int{}, IdToken: types.IdToken{IdToken: "1234", Type: types.IdTokenTypeKeyCode, AdditionalInfo: []types.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}}, true}, + {authorization.AuthorizeRequest{}, false}, + {authorization.AuthorizeRequest{IdToken: types.IdToken{Type: types.IdTokenTypeKeyCode, AdditionalInfo: []types.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}}, false}, + {authorization.AuthorizeRequest{IdToken: types.IdToken{Type: types.IdTokenTypeKeyCode, AdditionalInfo: []types.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}, CertificateHashData: []types.OCSPRequestDataType{{HashAlgorithm: types.SHA256, IssuerNameHash: "hash0", IssuerKeyHash: "hash1"}}}, false}, + {authorization.AuthorizeRequest{IdToken: types.IdToken{Type: types.IdTokenTypeKeyCode, AdditionalInfo: []types.AdditionalInfo{{AdditionalIdToken: "0000", Type: "someType"}}}, CertificateHashData: []types.OCSPRequestDataType{{SerialNumber: "s0", HashAlgorithm: types.SHA256, IssuerNameHash: "h0", IssuerKeyHash: "h0.1"}, {SerialNumber: "s1", HashAlgorithm: types.SHA256, IssuerNameHash: "h1", IssuerKeyHash: "h1.1"}, {SerialNumber: "s2", HashAlgorithm: types.SHA256, IssuerNameHash: "h2", IssuerKeyHash: "h2.1"}, {SerialNumber: "s3", HashAlgorithm: types.SHA256, IssuerNameHash: "h3", IssuerKeyHash: "h3.1"}, {SerialNumber: "s4", HashAlgorithm: types.SHA256, IssuerNameHash: "h4", IssuerKeyHash: "h4.1"}}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -28,12 +29,12 @@ func (suite *OcppV2TestSuite) TestAuthorizeRequestValidation() { func (suite *OcppV2TestSuite) TestAuthorizeConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.AuthorizeConfirmation{CertificateStatus: ocpp2.CertificateStatusAccepted, EvseID: []int{4,2}, IdTokenInfo: ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted}}, true}, - {ocpp2.AuthorizeConfirmation{CertificateStatus: ocpp2.CertificateStatusAccepted, IdTokenInfo: ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted}}, true}, - {ocpp2.AuthorizeConfirmation{IdTokenInfo: ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted}}, true}, - {ocpp2.AuthorizeConfirmation{}, false}, - {ocpp2.AuthorizeConfirmation{CertificateStatus:"invalidCertificateStatus", EvseID: []int{4,2}, IdTokenInfo: ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted}}, false}, - {ocpp2.AuthorizeConfirmation{CertificateStatus:"invalidCertificateStatus", EvseID: []int{4,2}, IdTokenInfo: ocpp2.IdTokenInfo{Status: "invalidTokenInfoStatus"}}, false}, + {authorization.AuthorizeResponse{CertificateStatus: types.CertificateStatusAccepted, EvseID: []int{4, 2}, IdTokenInfo: types.IdTokenInfo{Status: types.AuthorizationStatusAccepted}}, true}, + {authorization.AuthorizeResponse{CertificateStatus: types.CertificateStatusAccepted, IdTokenInfo: types.IdTokenInfo{Status: types.AuthorizationStatusAccepted}}, true}, + {authorization.AuthorizeResponse{IdTokenInfo: types.IdTokenInfo{Status: types.AuthorizationStatusAccepted}}, true}, + {authorization.AuthorizeResponse{}, false}, + {authorization.AuthorizeResponse{CertificateStatus: "invalidCertificateStatus", EvseID: []int{4, 2}, IdTokenInfo: types.IdTokenInfo{Status: types.AuthorizationStatusAccepted}}, false}, + {authorization.AuthorizeResponse{CertificateStatus: "invalidCertificateStatus", EvseID: []int{4, 2}, IdTokenInfo: types.IdTokenInfo{Status: "invalidTokenInfoStatus"}}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -43,26 +44,26 @@ func (suite *OcppV2TestSuite) TestAuthorizeE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - evseIds := []int{4,2} - additionalInfo := ocpp2.AdditionalInfo{AdditionalIdToken: "at1", Type: "some"} - idToken := ocpp2.IdToken{IdToken: "tok1", Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: []ocpp2.AdditionalInfo{additionalInfo}} - certHashData := ocpp2.OCSPRequestDataType{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "h0", IssuerKeyHash: "h0.1", SerialNumber: "s0", ResponderURL: "http://www.test.org"} - status := ocpp2.AuthorizationStatusAccepted - certificateStatus := ocpp2.CertificateStatusAccepted + evseIds := []int{4, 2} + additionalInfo := types.AdditionalInfo{AdditionalIdToken: "at1", Type: "some"} + idToken := types.IdToken{IdToken: "tok1", Type: types.IdTokenTypeKeyCode, AdditionalInfo: []types.AdditionalInfo{additionalInfo}} + certHashData := types.OCSPRequestDataType{HashAlgorithm: types.SHA256, IssuerNameHash: "h0", IssuerKeyHash: "h0.1", SerialNumber: "s0", ResponderURL: "http://www.test.org"} + status := types.AuthorizationStatusAccepted + certificateStatus := types.CertificateStatusAccepted requestJson := fmt.Sprintf(`[2,"%v","%v",{"evseId":[%v,%v],"idToken":{"idToken":"%v","type":"%v","additionalInfo":[{"additionalIdToken":"%v","type":"%v"}]},"15118CertificateHashData":[{"hashAlgorithm":"%v","issuerNameHash":"%v","issuerKeyHash":"%v","serialNumber":"%v","responderURL":"%v"}]}]`, - messageId, ocpp2.AuthorizeFeatureName, evseIds[0], evseIds[1], idToken.IdToken, idToken.Type, additionalInfo.AdditionalIdToken, additionalInfo.Type, certHashData.HashAlgorithm, certHashData.IssuerNameHash, certHashData.IssuerKeyHash, certHashData.SerialNumber, certHashData.ResponderURL) + messageId, authorization.AuthorizeFeatureName, evseIds[0], evseIds[1], idToken.IdToken, idToken.Type, additionalInfo.AdditionalIdToken, additionalInfo.Type, certHashData.HashAlgorithm, certHashData.IssuerNameHash, certHashData.IssuerKeyHash, certHashData.SerialNumber, certHashData.ResponderURL) responseJson := fmt.Sprintf(`[3,"%v",{"certificateStatus":"%v","evseId":[%v,%v],"idTokenInfo":{"status":"%v"}}]`, messageId, certificateStatus, evseIds[0], evseIds[1], status) - authorizeConfirmation := ocpp2.NewAuthorizationConfirmation(ocpp2.IdTokenInfo{Status: status}) + authorizeConfirmation := authorization.NewAuthorizationResponse(types.IdTokenInfo{Status: status}) authorizeConfirmation.EvseID = evseIds authorizeConfirmation.CertificateStatus = certificateStatus requestRaw := []byte(requestJson) responseRaw := []byte(responseJson) channel := NewMockWebSocket(wsId) - coreListener := MockCentralSystemCoreListener{} - coreListener.On("OnAuthorize", mock.AnythingOfType("string"), mock.Anything).Return(authorizeConfirmation, nil).Run(func(args mock.Arguments) { - request := args.Get(1).(*ocpp2.AuthorizeRequest) + handler := MockCSMSAuthorizationHandler{} + handler.On("OnAuthorize", mock.AnythingOfType("string"), mock.Anything).Return(authorizeConfirmation, nil).Run(func(args mock.Arguments) { + request := args.Get(1).(*authorization.AuthorizeRequest) require.Len(t, request.EvseID, 2) assert.Equal(t, evseIds[0], request.EvseID[0]) assert.Equal(t, evseIds[1], request.EvseID[1]) @@ -78,16 +79,16 @@ func (suite *OcppV2TestSuite) TestAuthorizeE2EMocked() { assert.Equal(t, certHashData.SerialNumber, request.CertificateHashData[0].SerialNumber) assert.Equal(t, certHashData.ResponderURL, request.CertificateHashData[0].ResponderURL) }) - setupDefaultCentralSystemHandlers(suite, coreListener, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: responseRaw, forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: requestRaw, forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: responseRaw, forwardWrittenMessage: true}, handler) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: requestRaw, forwardWrittenMessage: true}) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) - confirmation, err := suite.chargePoint.Authorize(idToken.IdToken, idToken.Type, func(request *ocpp2.AuthorizeRequest) { - request.IdToken.AdditionalInfo = []ocpp2.AdditionalInfo{additionalInfo} + confirmation, err := suite.chargingStation.Authorize(idToken.IdToken, idToken.Type, func(request *authorization.AuthorizeRequest) { + request.IdToken.AdditionalInfo = []types.AdditionalInfo{additionalInfo} request.EvseID = evseIds - request.CertificateHashData = []ocpp2.OCSPRequestDataType{certHashData} + request.CertificateHashData = []types.OCSPRequestDataType{certHashData} }) require.Nil(t, err) require.NotNil(t, confirmation) @@ -100,12 +101,12 @@ func (suite *OcppV2TestSuite) TestAuthorizeE2EMocked() { func (suite *OcppV2TestSuite) TestAuthorizeInvalidEndpoint() { messageId := defaultMessageId - evseIds := []int{4,2} - additionalInfo := ocpp2.AdditionalInfo{AdditionalIdToken: "at1", Type: "some"} - idToken := ocpp2.IdToken{IdToken: "tok1", Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: []ocpp2.AdditionalInfo{additionalInfo}} - certHashData := ocpp2.OCSPRequestDataType{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "h0", IssuerKeyHash: "h0.1", SerialNumber: "s0", ResponderURL: "http://www.test.org"} - authorizeRequest := ocpp2.NewAuthorizationRequest(idToken.IdToken, idToken.Type) + evseIds := []int{4, 2} + additionalInfo := types.AdditionalInfo{AdditionalIdToken: "at1", Type: "some"} + idToken := types.IdToken{IdToken: "tok1", Type: types.IdTokenTypeKeyCode, AdditionalInfo: []types.AdditionalInfo{additionalInfo}} + certHashData := types.OCSPRequestDataType{HashAlgorithm: types.SHA256, IssuerNameHash: "h0", IssuerKeyHash: "h0.1", SerialNumber: "s0", ResponderURL: "http://www.test.org"} + authorizeRequest := authorization.NewAuthorizationRequest(idToken.IdToken, idToken.Type) requestJson := fmt.Sprintf(`[2,"%v","%v",{"evseId":[%v,%v],"idToken":{"idToken":"%v","type":"%v","additionalInfo":[{"additionalIdToken":"%v","type":"%v"}]},"15118CertificateHashData":[{"hashAlgorithm":"%v","issuerNameHash":"%v","issuerKeyHash":"%v","serialNumber":"%v","responderURL":"%v"}]}]`, - messageId, ocpp2.AuthorizeFeatureName, evseIds[0], evseIds[1], idToken.IdToken, idToken.Type, additionalInfo.AdditionalIdToken, additionalInfo.Type, certHashData.HashAlgorithm, certHashData.IssuerNameHash, certHashData.IssuerKeyHash, certHashData.SerialNumber, certHashData.ResponderURL) + messageId, authorization.AuthorizeFeatureName, evseIds[0], evseIds[1], idToken.IdToken, idToken.Type, additionalInfo.AdditionalIdToken, additionalInfo.Type, certHashData.HashAlgorithm, certHashData.IssuerNameHash, certHashData.IssuerKeyHash, certHashData.SerialNumber, certHashData.ResponderURL) testUnsupportedRequestFromCentralSystem(suite, authorizeRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/boot_notification_test.go b/ocpp2.0_test/boot_notification_test.go index 62e1ae67..f63ba1c7 100644 --- a/ocpp2.0_test/boot_notification_test.go +++ b/ocpp2.0_test/boot_notification_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/provisioning" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -13,23 +14,23 @@ import ( func (suite *OcppV2TestSuite) TestBootNotificationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test", FirmwareVersion: "version", Modem: &ocpp2.ModemType{Iccid: "test", Imsi: "test"}}}, true}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test", FirmwareVersion: "version", Modem: &ocpp2.ModemType{Iccid: "test"}}}, true}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test", FirmwareVersion: "version", Modem: &ocpp2.ModemType{Imsi: "test"}}}, true}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test", FirmwareVersion: "version", Modem: &ocpp2.ModemType{}}}, true}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test", FirmwareVersion: "version"}}, true}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test"}}, true}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{Model: "test", VendorName: "test"}}, true}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{Model: "test"}}, false}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{VendorName: "test"}}, false}, - {ocpp2.BootNotificationRequest{ChargingStation: ocpp2.ChargingStationType{Model: "test", VendorName: "test"}}, false}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{Model: ">20..................", VendorName: "test"}}, false}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{Model: "test", VendorName: ">50................................................"}}, false}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{SerialNumber: ">20..................", Model: "test", VendorName: "test"}}, false}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{Model: "test", VendorName: "test", FirmwareVersion: ">50................................................"}}, false}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{Model: "test", VendorName: "test", Modem: &ocpp2.ModemType{Iccid: ">20.................."}}}, false}, - {ocpp2.BootNotificationRequest{Reason: ocpp2.BootReasonPowerUp, ChargingStation: ocpp2.ChargingStationType{Model: "test", VendorName: "test", Modem: &ocpp2.ModemType{Imsi: ">20.................."}}}, false}, - {ocpp2.BootNotificationRequest{Reason: "invalidReason", ChargingStation: ocpp2.ChargingStationType{Model: "test", VendorName: "test"}}, false}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test", FirmwareVersion: "version", Modem: &provisioning.ModemType{Iccid: "test", Imsi: "test"}}}, true}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test", FirmwareVersion: "version", Modem: &provisioning.ModemType{Iccid: "test"}}}, true}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test", FirmwareVersion: "version", Modem: &provisioning.ModemType{Imsi: "test"}}}, true}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test", FirmwareVersion: "version", Modem: &provisioning.ModemType{}}}, true}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test", FirmwareVersion: "version"}}, true}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{SerialNumber: "number", Model: "test", VendorName: "test"}}, true}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{Model: "test", VendorName: "test"}}, true}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{Model: "test"}}, false}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{VendorName: "test"}}, false}, + {provisioning.BootNotificationRequest{ChargingStation: provisioning.ChargingStationType{Model: "test", VendorName: "test"}}, false}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{Model: ">20..................", VendorName: "test"}}, false}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{Model: "test", VendorName: ">50................................................"}}, false}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{SerialNumber: ">20..................", Model: "test", VendorName: "test"}}, false}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{Model: "test", VendorName: "test", FirmwareVersion: ">50................................................"}}, false}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{Model: "test", VendorName: "test", Modem: &provisioning.ModemType{Iccid: ">20.................."}}}, false}, + {provisioning.BootNotificationRequest{Reason: provisioning.BootReasonPowerUp, ChargingStation: provisioning.ChargingStationType{Model: "test", VendorName: "test", Modem: &provisioning.ModemType{Imsi: ">20.................."}}}, false}, + {provisioning.BootNotificationRequest{Reason: "invalidReason", ChargingStation: provisioning.ChargingStationType{Model: "test", VendorName: "test"}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -37,12 +38,12 @@ func (suite *OcppV2TestSuite) TestBootNotificationRequestValidation() { func (suite *OcppV2TestSuite) TestBootNotificationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.BootNotificationConfirmation{CurrentTime: ocpp2.NewDateTime(time.Now()), Interval: 60, Status: ocpp2.RegistrationStatusAccepted}, true}, - {ocpp2.BootNotificationConfirmation{CurrentTime: ocpp2.NewDateTime(time.Now()), Status: ocpp2.RegistrationStatusAccepted}, true}, - {ocpp2.BootNotificationConfirmation{CurrentTime: ocpp2.NewDateTime(time.Now()), Interval: -1, Status: ocpp2.RegistrationStatusAccepted}, false}, - {ocpp2.BootNotificationConfirmation{CurrentTime: ocpp2.NewDateTime(time.Now()), Interval: 60, Status: "invalidRegistrationStatus"}, false}, - {ocpp2.BootNotificationConfirmation{CurrentTime: ocpp2.NewDateTime(time.Now()), Interval: 60}, false}, - {ocpp2.BootNotificationConfirmation{Interval: 60, Status: ocpp2.RegistrationStatusAccepted}, false}, + {provisioning.BootNotificationResponse{CurrentTime: types.NewDateTime(time.Now()), Interval: 60, Status: provisioning.RegistrationStatusAccepted}, true}, + {provisioning.BootNotificationResponse{CurrentTime: types.NewDateTime(time.Now()), Status: provisioning.RegistrationStatusAccepted}, true}, + {provisioning.BootNotificationResponse{CurrentTime: types.NewDateTime(time.Now()), Interval: -1, Status: provisioning.RegistrationStatusAccepted}, false}, + {provisioning.BootNotificationResponse{CurrentTime: types.NewDateTime(time.Now()), Interval: 60, Status: "invalidRegistrationStatus"}, false}, + {provisioning.BootNotificationResponse{CurrentTime: types.NewDateTime(time.Now()), Interval: 60}, false}, + {provisioning.BootNotificationResponse{Interval: 60, Status: provisioning.RegistrationStatusAccepted}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -53,43 +54,44 @@ func (suite *OcppV2TestSuite) TestBootNotificationE2EMocked() { messageId := "1234" wsUrl := "someUrl" interval := 60 - reason := ocpp2.BootReasonPowerUp + reason := provisioning.BootReasonPowerUp chargePointModel := "model1" chargePointVendor := "ABL" - registrationStatus := ocpp2.RegistrationStatusAccepted - currentTime := ocpp2.NewDateTime(time.Now()) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"reason":"%v","chargingStation":{"model":"%v","vendorName":"%v"}}]`, messageId, ocpp2.BootNotificationFeatureName, reason, chargePointModel, chargePointVendor) - responseJson := fmt.Sprintf(`[3,"%v",{"currentTime":"%v","interval":%v,"status":"%v"}]`, messageId, ocpp2.FormatTimestamp(currentTime.Time), interval, registrationStatus) - bootNotificationConfirmation := ocpp2.NewBootNotificationConfirmation(currentTime, interval, registrationStatus) + registrationStatus := provisioning.RegistrationStatusAccepted + currentTime := types.NewDateTime(time.Now()) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"reason":"%v","chargingStation":{"model":"%v","vendorName":"%v"}}]`, messageId, provisioning.BootNotificationFeatureName, reason, chargePointModel, chargePointVendor) + responseJson := fmt.Sprintf(`[3,"%v",{"currentTime":"%v","interval":%v,"status":"%v"}]`, messageId, currentTime.FormatTimestamp(), interval, registrationStatus) + fmt.Println(responseJson) + bootNotificationConfirmation := provisioning.NewBootNotificationResponse(currentTime, interval, registrationStatus) channel := NewMockWebSocket(wsId) - coreListener := MockCentralSystemCoreListener{} - coreListener.On("OnBootNotification", mock.AnythingOfType("string"), mock.Anything).Return(bootNotificationConfirmation, nil).Run(func(args mock.Arguments) { - request := args.Get(1).(*ocpp2.BootNotificationRequest) + handler := MockCSMSProvisioningHandler{} + handler.On("OnBootNotification", mock.AnythingOfType("string"), mock.Anything).Return(bootNotificationConfirmation, nil).Run(func(args mock.Arguments) { + request := args.Get(1).(*provisioning.BootNotificationRequest) assert.Equal(t, reason, request.Reason) assert.Equal(t, chargePointVendor, request.ChargingStation.VendorName) assert.Equal(t, chargePointModel, request.ChargingStation.Model) }) - setupDefaultCentralSystemHandlers(suite, coreListener, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) // Run test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) - confirmation, err := suite.chargePoint.BootNotification(reason, chargePointModel, chargePointVendor) + confirmation, err := suite.chargingStation.BootNotification(reason, chargePointModel, chargePointVendor) require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, registrationStatus, confirmation.Status) assert.Equal(t, interval, confirmation.Interval) - assertDateTimeEquality(t, *currentTime, *confirmation.CurrentTime) + assertDateTimeEquality(t, currentTime, confirmation.CurrentTime) } func (suite *OcppV2TestSuite) TestBootNotificationInvalidEndpoint() { messageId := defaultMessageId chargePointModel := "model1" chargePointVendor := "ABL" - reason := ocpp2.BootReasonPowerUp - bootNotificationRequest := ocpp2.NewBootNotificationRequest(reason, chargePointModel, chargePointVendor) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"reason":"%v","chargingStation":{"model":"%v","vendorName":"%v"}}]`, messageId, ocpp2.BootNotificationFeatureName, reason, chargePointModel, chargePointVendor) + reason := provisioning.BootReasonPowerUp + bootNotificationRequest := provisioning.NewBootNotificationRequest(reason, chargePointModel, chargePointVendor) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"reason":"%v","chargingStation":{"model":"%v","vendorName":"%v"}}]`, messageId, provisioning.BootNotificationFeatureName, reason, chargePointModel, chargePointVendor) testUnsupportedRequestFromCentralSystem(suite, bootNotificationRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/cancel_reservation_test.go b/ocpp2.0_test/cancel_reservation_test.go index 16f926cf..1985a21a 100644 --- a/ocpp2.0_test/cancel_reservation_test.go +++ b/ocpp2.0_test/cancel_reservation_test.go @@ -2,7 +2,7 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/reservation" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,9 +12,9 @@ import ( func (suite *OcppV2TestSuite) TestCancelReservationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.CancelReservationRequest{ReservationId: 42}, true}, - {ocpp2.CancelReservationRequest{}, true}, - {ocpp2.CancelReservationRequest{ReservationId: -1}, false}, + {reservation.CancelReservationRequest{ReservationId: 42}, true}, + {reservation.CancelReservationRequest{}, true}, + {reservation.CancelReservationRequest{ReservationId: -1}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -22,9 +22,9 @@ func (suite *OcppV2TestSuite) TestCancelReservationRequestValidation() { func (suite *OcppV2TestSuite) TestCancelReservationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.CancelReservationConfirmation{Status: ocpp2.CancelReservationStatusAccepted}, true}, - {ocpp2.CancelReservationConfirmation{Status: "invalidCancelReservationStatus"}, false}, - {ocpp2.CancelReservationConfirmation{}, false}, + {reservation.CancelReservationResponse{Status: reservation.CancelReservationStatusAccepted}, true}, + {reservation.CancelReservationResponse{Status: "invalidCancelReservationStatus"}, false}, + {reservation.CancelReservationResponse{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -35,26 +35,26 @@ func (suite *OcppV2TestSuite) TestCancelReservationE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" reservationId := 42 - status := ocpp2.CancelReservationStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"reservationId":%v}]`, messageId, ocpp2.CancelReservationFeatureName, reservationId) + status := reservation.CancelReservationStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"reservationId":%v}]`, messageId, reservation.CancelReservationFeatureName, reservationId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - cancelReservationConfirmation := ocpp2.NewCancelReservationConfirmation(status) + cancelReservationConfirmation := reservation.NewCancelReservationResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnCancelReservation", mock.Anything).Return(cancelReservationConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.CancelReservationRequest) + handler := MockChargingStationReservationHandler{} + handler.On("OnCancelReservation", mock.Anything).Return(cancelReservationConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*reservation.CancelReservationRequest) require.True(t, ok) assert.Equal(t, reservationId, request.ReservationId) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.CancelReservation(wsId, func(confirmation *ocpp2.CancelReservationConfirmation, err error) { + err = suite.csms.CancelReservation(wsId, func(confirmation *reservation.CancelReservationResponse, err error) { require.Nil(t, err) assert.NotNil(t, confirmation) require.Equal(t, status, confirmation.Status) @@ -68,7 +68,7 @@ func (suite *OcppV2TestSuite) TestCancelReservationE2EMocked() { func (suite *OcppV2TestSuite) TestCancelReservationInvalidEndpoint() { messageId := defaultMessageId reservationId := 42 - cancelReservationRequest := ocpp2.NewCancelReservationRequest(reservationId) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"reservationId":%v}]`, messageId, ocpp2.CancelReservationFeatureName, reservationId) - testUnsupportedRequestFromChargePoint(suite, cancelReservationRequest, requestJson, messageId) + cancelReservationRequest := reservation.NewCancelReservationRequest(reservationId) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"reservationId":%v}]`, messageId, reservation.CancelReservationFeatureName, reservationId) + testUnsupportedRequestFromChargingStation(suite, cancelReservationRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/certificate_signed_test.go b/ocpp2.0_test/certificate_signed_test.go index 152c8e77..9587405d 100644 --- a/ocpp2.0_test/certificate_signed_test.go +++ b/ocpp2.0_test/certificate_signed_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/security" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -11,12 +12,12 @@ import ( func (suite *OcppV2TestSuite) TestCertificateSignedRequestValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp2.CertificateSignedRequest{Cert: []string{"sampleCert"}, TypeOfCertificate: ocpp2.ChargingStationCert}, true}, - {ocpp2.CertificateSignedRequest{Cert: []string{"sampleCert"}}, true}, - {ocpp2.CertificateSignedRequest{Cert: []string{}}, false}, - {ocpp2.CertificateSignedRequest{}, false}, - {ocpp2.CertificateSignedRequest{Cert: []string{">800............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."}}, false}, - {ocpp2.CertificateSignedRequest{Cert: []string{"sampleCert"}, TypeOfCertificate: "invalidCertificateType"}, false}, + {security.CertificateSignedRequest{Cert: []string{"sampleCert"}, TypeOfCertificate: types.ChargingStationCert}, true}, + {security.CertificateSignedRequest{Cert: []string{"sampleCert"}}, true}, + {security.CertificateSignedRequest{Cert: []string{}}, false}, + {security.CertificateSignedRequest{}, false}, + {security.CertificateSignedRequest{Cert: []string{">800............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."}}, false}, + {security.CertificateSignedRequest{Cert: []string{"sampleCert"}, TypeOfCertificate: "invalidCertificateType"}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -24,10 +25,10 @@ func (suite *OcppV2TestSuite) TestCertificateSignedRequestValidation() { func (suite *OcppV2TestSuite) TestCertificateSignedConfirmationValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp2.CertificateSignedConfirmation{Status: ocpp2.CertificateSignedStatusAccepted}, true}, - {ocpp2.CertificateSignedConfirmation{Status: ocpp2.CertificateSignedStatusRejected}, true}, - {ocpp2.CertificateSignedConfirmation{Status: "invalidCertificateSignedStatus"}, false}, - {ocpp2.CertificateSignedConfirmation{}, false}, + {security.CertificateSignedResponse{Status: security.CertificateSignedStatusAccepted}, true}, + {security.CertificateSignedResponse{Status: security.CertificateSignedStatusRejected}, true}, + {security.CertificateSignedResponse{Status: "invalidCertificateSignedStatus"}, false}, + {security.CertificateSignedResponse{}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -39,34 +40,34 @@ func (suite *OcppV2TestSuite) TestCertificateSignedE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" certificate := "someX509Certificate" - certificateType := ocpp2.ChargingStationCert - status := ocpp2.CertificateSignedStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"cert":["%v"],"typeOfCertificate":"%v"}]`, messageId, ocpp2.CertificateSignedFeatureName, certificate, certificateType) + certificateType := types.ChargingStationCert + status := security.CertificateSignedStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"cert":["%v"],"typeOfCertificate":"%v"}]`, messageId, security.CertificateSignedFeatureName, certificate, certificateType) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - certificateSignedConfirmation := ocpp2.NewCertificateSignedConfirmation(status) + certificateSignedConfirmation := security.NewCertificateSignedResponse(status) channel := NewMockWebSocket(wsId) // Setting handlers - coreListener := MockChargePointCoreListener{} - coreListener.On("OnCertificateSigned", mock.Anything).Return(certificateSignedConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.CertificateSignedRequest) + handler := MockChargingStationSecurityHandler{} + handler.On("OnCertificateSigned", mock.Anything).Return(certificateSignedConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*security.CertificateSignedRequest) require.True(t, ok) require.Len(t, request.Cert, 1) assert.Equal(t, certificate, request.Cert[0]) assert.Equal(t, certificateType, request.TypeOfCertificate) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.CertificateSigned(wsId, func(confirmation *ocpp2.CertificateSignedConfirmation, err error) { + err = suite.csms.CertificateSigned(wsId, func(confirmation *security.CertificateSignedResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) resultChannel <- true - }, []string{certificate}, func(request *ocpp2.CertificateSignedRequest) { + }, []string{certificate}, func(request *security.CertificateSignedRequest) { request.TypeOfCertificate = certificateType }) require.Nil(t, err) @@ -77,9 +78,9 @@ func (suite *OcppV2TestSuite) TestCertificateSignedE2EMocked() { func (suite *OcppV2TestSuite) TestCertificateSignedInvalidEndpoint() { messageId := defaultMessageId certificate := "someX509Certificate" - certificateType := ocpp2.ChargingStationCert - certificateSignedRequest := ocpp2.NewCertificateSignedRequest([]string{certificate}) + certificateType := types.ChargingStationCert + certificateSignedRequest := security.NewCertificateSignedRequest([]string{certificate}) certificateSignedRequest.TypeOfCertificate = certificateType - requestJson := fmt.Sprintf(`[2,"%v","%v",{"cert":["%v"],"typeOfCertificate":"%v"}]`, messageId, ocpp2.CertificateSignedFeatureName, certificate, certificateType) - testUnsupportedRequestFromChargePoint(suite, certificateSignedRequest, requestJson, messageId) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"cert":["%v"],"typeOfCertificate":"%v"}]`, messageId, security.CertificateSignedFeatureName, certificate, certificateType) + testUnsupportedRequestFromChargingStation(suite, certificateSignedRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/change_availability_test.go b/ocpp2.0_test/change_availability_test.go index 58615abc..dee266e7 100644 --- a/ocpp2.0_test/change_availability_test.go +++ b/ocpp2.0_test/change_availability_test.go @@ -2,7 +2,7 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/availability" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -11,12 +11,12 @@ import ( func (suite *OcppV2TestSuite) TestChangeAvailabilityRequestValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp2.ChangeAvailabilityRequest{EvseID: 0, OperationalStatus: ocpp2.OperationalStatusOperative}, true}, - {ocpp2.ChangeAvailabilityRequest{EvseID: 0, OperationalStatus: ocpp2.OperationalStatusInoperative}, true}, - {ocpp2.ChangeAvailabilityRequest{EvseID: 0}, false}, - {ocpp2.ChangeAvailabilityRequest{OperationalStatus: ocpp2.OperationalStatusOperative}, true}, - {ocpp2.ChangeAvailabilityRequest{OperationalStatus: "invalidAvailabilityType"}, false}, - {ocpp2.ChangeAvailabilityRequest{EvseID: -1, OperationalStatus: ocpp2.OperationalStatusOperative}, false}, + {availability.ChangeAvailabilityRequest{EvseID: 0, OperationalStatus: availability.OperationalStatusOperative}, true}, + {availability.ChangeAvailabilityRequest{EvseID: 0, OperationalStatus: availability.OperationalStatusInoperative}, true}, + {availability.ChangeAvailabilityRequest{EvseID: 0}, false}, + {availability.ChangeAvailabilityRequest{OperationalStatus: availability.OperationalStatusOperative}, true}, + {availability.ChangeAvailabilityRequest{OperationalStatus: "invalidAvailabilityType"}, false}, + {availability.ChangeAvailabilityRequest{EvseID: -1, OperationalStatus: availability.OperationalStatusOperative}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -24,11 +24,11 @@ func (suite *OcppV2TestSuite) TestChangeAvailabilityRequestValidation() { func (suite *OcppV2TestSuite) TestChangeAvailabilityConfirmationValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp2.ChangeAvailabilityConfirmation{Status: ocpp2.ChangeAvailabilityStatusAccepted}, true}, - {ocpp2.ChangeAvailabilityConfirmation{Status: ocpp2.ChangeAvailabilityStatusRejected}, true}, - {ocpp2.ChangeAvailabilityConfirmation{Status: ocpp2.ChangeAvailabilityStatusScheduled}, true}, - {ocpp2.ChangeAvailabilityConfirmation{Status: "invalidAvailabilityStatus"}, false}, - {ocpp2.ChangeAvailabilityConfirmation{}, false}, + {availability.ChangeAvailabilityResponse{Status: availability.ChangeAvailabilityStatusAccepted}, true}, + {availability.ChangeAvailabilityResponse{Status: availability.ChangeAvailabilityStatusRejected}, true}, + {availability.ChangeAvailabilityResponse{Status: availability.ChangeAvailabilityStatusScheduled}, true}, + {availability.ChangeAvailabilityResponse{Status: "invalidAvailabilityStatus"}, false}, + {availability.ChangeAvailabilityResponse{}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -40,28 +40,28 @@ func (suite *OcppV2TestSuite) TestChangeAvailabilityE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" evseID := 1 - operationalStatus := ocpp2.OperationalStatusOperative - status := ocpp2.ChangeAvailabilityStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"evseId":%v,"operationalStatus":"%v"}]`, messageId, ocpp2.ChangeAvailabilityFeatureName, evseID, operationalStatus) + operationalStatus := availability.OperationalStatusOperative + status := availability.ChangeAvailabilityStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"evseId":%v,"operationalStatus":"%v"}]`, messageId, availability.ChangeAvailabilityFeatureName, evseID, operationalStatus) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - changeAvailabilityConfirmation := ocpp2.NewChangeAvailabilityConfirmation(status) + changeAvailabilityConfirmation := availability.NewChangeAvailabilityResponse(status) channel := NewMockWebSocket(wsId) // Setting handlers - coreListener := MockChargePointCoreListener{} - coreListener.On("OnChangeAvailability", mock.Anything).Return(changeAvailabilityConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.ChangeAvailabilityRequest) + handler := MockChargingStationAvailabilityHandler{} + handler.On("OnChangeAvailability", mock.Anything).Return(changeAvailabilityConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*availability.ChangeAvailabilityRequest) require.True(t, ok) assert.Equal(t, evseID, request.EvseID) assert.Equal(t, operationalStatus, request.OperationalStatus) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.ChangeAvailability(wsId, func(confirmation *ocpp2.ChangeAvailabilityConfirmation, err error) { + err = suite.csms.ChangeAvailability(wsId, func(confirmation *availability.ChangeAvailabilityResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -75,8 +75,8 @@ func (suite *OcppV2TestSuite) TestChangeAvailabilityE2EMocked() { func (suite *OcppV2TestSuite) TestChangeAvailabilityInvalidEndpoint() { messageId := defaultMessageId evseID := 1 - operationalStatus := ocpp2.OperationalStatusOperative - changeAvailabilityRequest := ocpp2.NewChangeAvailabilityRequest(evseID, operationalStatus) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"evseId":%v,"operationalStatus":"%v"}]`, messageId, ocpp2.ChangeAvailabilityFeatureName, evseID, operationalStatus) - testUnsupportedRequestFromChargePoint(suite, changeAvailabilityRequest, requestJson, messageId) + operationalStatus := availability.OperationalStatusOperative + changeAvailabilityRequest := availability.NewChangeAvailabilityRequest(evseID, operationalStatus) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"evseId":%v,"operationalStatus":"%v"}]`, messageId, availability.ChangeAvailabilityFeatureName, evseID, operationalStatus) + testUnsupportedRequestFromChargingStation(suite, changeAvailabilityRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/clear_cache_test.go b/ocpp2.0_test/clear_cache_test.go index a3798d8e..eb5340e9 100644 --- a/ocpp2.0_test/clear_cache_test.go +++ b/ocpp2.0_test/clear_cache_test.go @@ -2,7 +2,7 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/authorization" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,7 +12,7 @@ import ( func (suite *OcppV2TestSuite) TestClearCacheRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.ClearCacheRequest{}, true}, + {authorization.ClearCacheRequest{}, true}, } ExecuteGenericTestTable(t, requestTable) } @@ -20,10 +20,10 @@ func (suite *OcppV2TestSuite) TestClearCacheRequestValidation() { func (suite *OcppV2TestSuite) TestClearCacheConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.ClearCacheConfirmation{Status: ocpp2.ClearCacheStatusAccepted}, true}, - {ocpp2.ClearCacheConfirmation{Status: ocpp2.ClearCacheStatusRejected}, true}, - {ocpp2.ClearCacheConfirmation{Status: "invalidClearCacheStatus"}, false}, - {ocpp2.ClearCacheConfirmation{}, false}, + {authorization.ClearCacheResponse{Status: authorization.ClearCacheStatusAccepted}, true}, + {authorization.ClearCacheResponse{Status: authorization.ClearCacheStatusRejected}, true}, + {authorization.ClearCacheResponse{Status: "invalidClearCacheStatus"}, false}, + {authorization.ClearCacheResponse{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -33,22 +33,22 @@ func (suite *OcppV2TestSuite) TestClearCacheE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - status := ocpp2.ClearCacheStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, ocpp2.ClearCacheFeatureName) + status := authorization.ClearCacheStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, authorization.ClearCacheFeatureName) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - clearCacheConfirmation := ocpp2.NewClearCacheConfirmation(status) + clearCacheConfirmation := authorization.NewClearCacheResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnClearCache", mock.Anything).Return(clearCacheConfirmation, nil) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + handler := MockChargingStationAuthorizationHandler{} + handler.On("OnClearCache", mock.Anything).Return(clearCacheConfirmation, nil) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.ClearCache(wsId, func(confirmation *ocpp2.ClearCacheConfirmation, err error) { + err = suite.csms.ClearCache(wsId, func(confirmation *authorization.ClearCacheResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -61,7 +61,7 @@ func (suite *OcppV2TestSuite) TestClearCacheE2EMocked() { func (suite *OcppV2TestSuite) TestClearCacheInvalidEndpoint() { messageId := defaultMessageId - clearCacheRequest := ocpp2.NewClearCacheRequest() - requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, ocpp2.ClearCacheFeatureName) - testUnsupportedRequestFromChargePoint(suite, clearCacheRequest, requestJson, messageId) + clearCacheRequest := authorization.NewClearCacheRequest() + requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, authorization.ClearCacheFeatureName) + testUnsupportedRequestFromChargingStation(suite, clearCacheRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/clear_charging_profile_test.go b/ocpp2.0_test/clear_charging_profile_test.go index f7221c22..4fa03ca3 100644 --- a/ocpp2.0_test/clear_charging_profile_test.go +++ b/ocpp2.0_test/clear_charging_profile_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,16 +13,16 @@ import ( func (suite *OcppV2TestSuite) TestClearChargingProfileRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.ClearChargingProfileRequest{EvseID: newInt(1), ChargingProfile: &ocpp2.ClearChargingProfileType{ID: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, StackLevel: 1}}, true}, - {ocpp2.ClearChargingProfileRequest{EvseID: newInt(1), ChargingProfile: &ocpp2.ClearChargingProfileType{ID: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile}}, true}, - {ocpp2.ClearChargingProfileRequest{EvseID: newInt(1), ChargingProfile: &ocpp2.ClearChargingProfileType{ID: 1}}, true}, - {ocpp2.ClearChargingProfileRequest{ChargingProfile: &ocpp2.ClearChargingProfileType{ID: 1}}, true}, - {ocpp2.ClearChargingProfileRequest{ChargingProfile: &ocpp2.ClearChargingProfileType{}}, true}, - {ocpp2.ClearChargingProfileRequest{}, true}, - {ocpp2.ClearChargingProfileRequest{EvseID: newInt(-1)}, false}, - {ocpp2.ClearChargingProfileRequest{ChargingProfile: &ocpp2.ClearChargingProfileType{ID: -1}}, false}, - {ocpp2.ClearChargingProfileRequest{ChargingProfile: &ocpp2.ClearChargingProfileType{ChargingProfilePurpose: "invalidChargingProfilePurposeType"}}, false}, - {ocpp2.ClearChargingProfileRequest{ChargingProfile: &ocpp2.ClearChargingProfileType{StackLevel: -1}}, false}, + {smartcharging.ClearChargingProfileRequest{EvseID: newInt(1), ChargingProfile: &smartcharging.ClearChargingProfileType{ID: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, StackLevel: 1}}, true}, + {smartcharging.ClearChargingProfileRequest{EvseID: newInt(1), ChargingProfile: &smartcharging.ClearChargingProfileType{ID: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile}}, true}, + {smartcharging.ClearChargingProfileRequest{EvseID: newInt(1), ChargingProfile: &smartcharging.ClearChargingProfileType{ID: 1}}, true}, + {smartcharging.ClearChargingProfileRequest{ChargingProfile: &smartcharging.ClearChargingProfileType{ID: 1}}, true}, + {smartcharging.ClearChargingProfileRequest{ChargingProfile: &smartcharging.ClearChargingProfileType{}}, true}, + {smartcharging.ClearChargingProfileRequest{}, true}, + {smartcharging.ClearChargingProfileRequest{EvseID: newInt(-1)}, false}, + {smartcharging.ClearChargingProfileRequest{ChargingProfile: &smartcharging.ClearChargingProfileType{ID: -1}}, false}, + {smartcharging.ClearChargingProfileRequest{ChargingProfile: &smartcharging.ClearChargingProfileType{ChargingProfilePurpose: "invalidChargingProfilePurposeType"}}, false}, + {smartcharging.ClearChargingProfileRequest{ChargingProfile: &smartcharging.ClearChargingProfileType{StackLevel: -1}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -29,9 +30,9 @@ func (suite *OcppV2TestSuite) TestClearChargingProfileRequestValidation() { func (suite *OcppV2TestSuite) TestClearChargingProfileConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.ClearChargingProfileConfirmation{Status: ocpp2.ClearChargingProfileStatusAccepted}, true}, - {ocpp2.ClearChargingProfileConfirmation{Status: "invalidClearChargingProfileStatus"}, false}, - {ocpp2.ClearChargingProfileConfirmation{}, false}, + {smartcharging.ClearChargingProfileResponse{Status: smartcharging.ClearChargingProfileStatusAccepted}, true}, + {smartcharging.ClearChargingProfileResponse{Status: "invalidClearChargingProfileStatus"}, false}, + {smartcharging.ClearChargingProfileResponse{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -43,18 +44,18 @@ func (suite *OcppV2TestSuite) TestClearChargingProfileE2EMocked() { wsUrl := "someUrl" chargingProfileId := 1 evseID := 1 - chargingProfilePurpose := ocpp2.ChargingProfilePurposeChargingStationMaxProfile + chargingProfilePurpose := types.ChargingProfilePurposeChargingStationMaxProfile stackLevel := 1 - status := ocpp2.ClearChargingProfileStatusAccepted + status := smartcharging.ClearChargingProfileStatusAccepted requestJson := fmt.Sprintf(`[2,"%v","%v",{"evseId":%v,"chargingProfile":{"id":%v,"chargingProfilePurpose":"%v","stackLevel":%v}}]`, - messageId, ocpp2.ClearChargingProfileFeatureName, evseID, chargingProfileId, chargingProfilePurpose, stackLevel) + messageId, smartcharging.ClearChargingProfileFeatureName, evseID, chargingProfileId, chargingProfilePurpose, stackLevel) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - clearChargingProfileConfirmation := ocpp2.NewClearChargingProfileConfirmation(status) + clearChargingProfileConfirmation := smartcharging.NewClearChargingProfileResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnClearChargingProfile", mock.Anything).Return(clearChargingProfileConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.ClearChargingProfileRequest) + handler := MockChargingStationSmartChargingHandler{} + handler.On("OnClearChargingProfile", mock.Anything).Return(clearChargingProfileConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*smartcharging.ClearChargingProfileRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, evseID, *request.EvseID) @@ -62,21 +63,21 @@ func (suite *OcppV2TestSuite) TestClearChargingProfileE2EMocked() { assert.Equal(t, chargingProfilePurpose, request.ChargingProfile.ChargingProfilePurpose) assert.Equal(t, stackLevel, request.ChargingProfile.StackLevel) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.ClearChargingProfile(wsId, func(confirmation *ocpp2.ClearChargingProfileConfirmation, err error) { + err = suite.csms.ClearChargingProfile(wsId, func(confirmation *smartcharging.ClearChargingProfileResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) resultChannel <- true - }, func(request *ocpp2.ClearChargingProfileRequest) { + }, func(request *smartcharging.ClearChargingProfileRequest) { request.EvseID = &evseID - request.ChargingProfile = &ocpp2.ClearChargingProfileType{ID: chargingProfileId, ChargingProfilePurpose: chargingProfilePurpose, StackLevel: stackLevel} + request.ChargingProfile = &smartcharging.ClearChargingProfileType{ID: chargingProfileId, ChargingProfilePurpose: chargingProfilePurpose, StackLevel: stackLevel} }) require.Nil(t, err) result := <-resultChannel @@ -88,11 +89,11 @@ func (suite *OcppV2TestSuite) TestClearChargingProfileInvalidEndpoint() { evseID := 1 chargingProfileId := 1 stackLevel := 1 - chargingProfilePurpose := ocpp2.ChargingProfilePurposeChargingStationMaxProfile + chargingProfilePurpose := types.ChargingProfilePurposeChargingStationMaxProfile requestJson := fmt.Sprintf(`[2,"%v","%v",{"evseId":%v,"chargingProfile":{"id":%v,"chargingProfilePurpose":"%v","stackLevel":%v}}]`, - messageId, ocpp2.ClearChargingProfileFeatureName, evseID, chargingProfileId, chargingProfilePurpose, stackLevel) - clearChargingProfileRequest := ocpp2.NewClearChargingProfileRequest() + messageId, smartcharging.ClearChargingProfileFeatureName, evseID, chargingProfileId, chargingProfilePurpose, stackLevel) + clearChargingProfileRequest := smartcharging.NewClearChargingProfileRequest() clearChargingProfileRequest.EvseID = &evseID - clearChargingProfileRequest.ChargingProfile = &ocpp2.ClearChargingProfileType{ID: chargingProfileId, ChargingProfilePurpose: chargingProfilePurpose, StackLevel: stackLevel} - testUnsupportedRequestFromChargePoint(suite, clearChargingProfileRequest, requestJson, messageId) + clearChargingProfileRequest.ChargingProfile = &smartcharging.ClearChargingProfileType{ID: chargingProfileId, ChargingProfilePurpose: chargingProfilePurpose, StackLevel: stackLevel} + testUnsupportedRequestFromChargingStation(suite, clearChargingProfileRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/clear_display_test.go b/ocpp2.0_test/clear_display_test.go index da7b3492..121ac3d4 100644 --- a/ocpp2.0_test/clear_display_test.go +++ b/ocpp2.0_test/clear_display_test.go @@ -2,7 +2,7 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/display" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,9 +12,9 @@ import ( func (suite *OcppV2TestSuite) TestClearDisplayRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.ClearDisplayRequest{ID: 42}, true}, - {ocpp2.ClearDisplayRequest{}, false}, - {ocpp2.ClearDisplayRequest{ID: -1}, false}, + {display.ClearDisplayRequest{ID: 42}, true}, + {display.ClearDisplayRequest{}, false}, + {display.ClearDisplayRequest{ID: -1}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -22,10 +22,10 @@ func (suite *OcppV2TestSuite) TestClearDisplayRequestValidation() { func (suite *OcppV2TestSuite) TestClearDisplayConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.ClearDisplayConfirmation{Status: ocpp2.ClearMessageStatusAccepted}, true}, - {ocpp2.ClearDisplayConfirmation{Status: ocpp2.ClearMessageStatusUnknown}, true}, - {ocpp2.ClearDisplayConfirmation{Status: "invalidClearMessageStatus"}, false}, - {ocpp2.ClearDisplayConfirmation{}, false}, + {display.ClearDisplayResponse{Status: display.ClearMessageStatusAccepted}, true}, + {display.ClearDisplayResponse{Status: display.ClearMessageStatusUnknown}, true}, + {display.ClearDisplayResponse{Status: "invalidClearMessageStatus"}, false}, + {display.ClearDisplayResponse{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -36,27 +36,27 @@ func (suite *OcppV2TestSuite) TestClearDisplayE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" displayMessageId := 42 - status := ocpp2.ClearMessageStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"id":%v}]`, messageId, ocpp2.ClearDisplayFeatureName, displayMessageId) + status := display.ClearMessageStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"id":%v}]`, messageId, display.ClearDisplayFeatureName, displayMessageId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - clearDisplayConfirmation := ocpp2.NewClearDisplayConfirmation(status) + clearDisplayConfirmation := display.NewClearDisplayResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnClearDisplay", mock.Anything).Return(clearDisplayConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.ClearDisplayRequest) + handler := MockChargingStationDisplayHandler{} + handler.On("OnClearDisplay", mock.Anything).Return(clearDisplayConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*display.ClearDisplayRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, displayMessageId, request.ID) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.ClearDisplay(wsId, func(confirmation *ocpp2.ClearDisplayConfirmation, err error) { + err = suite.csms.ClearDisplay(wsId, func(confirmation *display.ClearDisplayResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -70,7 +70,7 @@ func (suite *OcppV2TestSuite) TestClearDisplayE2EMocked() { func (suite *OcppV2TestSuite) TestClearDisplayInvalidEndpoint() { messageId := defaultMessageId displayMessageId := 42 - clearDisplayRequest := ocpp2.NewClearDisplayRequest(displayMessageId) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"id":%v}]`, messageId, ocpp2.ClearDisplayFeatureName, displayMessageId) - testUnsupportedRequestFromChargePoint(suite, clearDisplayRequest, requestJson, messageId) + clearDisplayRequest := display.NewClearDisplayRequest(displayMessageId) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"id":%v}]`, messageId, display.ClearDisplayFeatureName, displayMessageId) + testUnsupportedRequestFromChargingStation(suite, clearDisplayRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/clear_variable_monitoring_test.go b/ocpp2.0_test/clear_variable_monitoring_test.go index c6d269c0..48e7cd84 100644 --- a/ocpp2.0_test/clear_variable_monitoring_test.go +++ b/ocpp2.0_test/clear_variable_monitoring_test.go @@ -2,7 +2,7 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/diagnostics" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,11 +12,11 @@ import ( func (suite *OcppV2TestSuite) TestClearVariableMonitoringRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.ClearVariableMonitoringRequest{ID: []int{0, 2, 15}}, true}, - {ocpp2.ClearVariableMonitoringRequest{ID: []int{0}}, true}, - {ocpp2.ClearVariableMonitoringRequest{ID: []int{}}, false}, - {ocpp2.ClearVariableMonitoringRequest{}, false}, - {ocpp2.ClearVariableMonitoringRequest{ID: []int{-1}}, false}, + {diagnostics.ClearVariableMonitoringRequest{ID: []int{0, 2, 15}}, true}, + {diagnostics.ClearVariableMonitoringRequest{ID: []int{0}}, true}, + {diagnostics.ClearVariableMonitoringRequest{ID: []int{}}, false}, + {diagnostics.ClearVariableMonitoringRequest{}, false}, + {diagnostics.ClearVariableMonitoringRequest{ID: []int{-1}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -24,12 +24,12 @@ func (suite *OcppV2TestSuite) TestClearVariableMonitoringRequestValidation() { func (suite *OcppV2TestSuite) TestClearVariableMonitoringConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.ClearVariableMonitoringConfirmation{ClearMonitoringResult: []ocpp2.ClearMonitoringResult{{ID: 2, Status: ocpp2.ClearMonitoringStatusAccepted}}}, true}, - {ocpp2.ClearVariableMonitoringConfirmation{ClearMonitoringResult: []ocpp2.ClearMonitoringResult{{ID: 2}}}, false}, - {ocpp2.ClearVariableMonitoringConfirmation{ClearMonitoringResult: []ocpp2.ClearMonitoringResult{}}, false}, - {ocpp2.ClearVariableMonitoringConfirmation{}, false}, - {ocpp2.ClearVariableMonitoringConfirmation{ClearMonitoringResult: []ocpp2.ClearMonitoringResult{{ID: -1, Status: ocpp2.ClearMonitoringStatusAccepted}}}, false}, - {ocpp2.ClearVariableMonitoringConfirmation{ClearMonitoringResult: []ocpp2.ClearMonitoringResult{{ID: 2, Status: "invalidClearMonitoringStatus"}}}, false}, + {diagnostics.ClearVariableMonitoringResponse{ClearMonitoringResult: []diagnostics.ClearMonitoringResult{{ID: 2, Status: diagnostics.ClearMonitoringStatusAccepted}}}, true}, + {diagnostics.ClearVariableMonitoringResponse{ClearMonitoringResult: []diagnostics.ClearMonitoringResult{{ID: 2}}}, false}, + {diagnostics.ClearVariableMonitoringResponse{ClearMonitoringResult: []diagnostics.ClearMonitoringResult{}}, false}, + {diagnostics.ClearVariableMonitoringResponse{}, false}, + {diagnostics.ClearVariableMonitoringResponse{ClearMonitoringResult: []diagnostics.ClearMonitoringResult{{ID: -1, Status: diagnostics.ClearMonitoringStatusAccepted}}}, false}, + {diagnostics.ClearVariableMonitoringResponse{ClearMonitoringResult: []diagnostics.ClearMonitoringResult{{ID: 2, Status: "invalidClearMonitoringStatus"}}}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -39,31 +39,31 @@ func (suite *OcppV2TestSuite) TestClearVariableMonitoringE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - ids := []int{1,2} - result1 := ocpp2.ClearMonitoringResult{ID: 1, Status: ocpp2.ClearMonitoringStatusAccepted} - result2 := ocpp2.ClearMonitoringResult{ID: 2, Status: ocpp2.ClearMonitoringStatusNotFound} - requestJson := fmt.Sprintf(`[2,"%v","%v",{"id":[%v,%v]}]`, messageId, ocpp2.ClearVariableMonitoringFeatureName, ids[0], ids[1]) + ids := []int{1, 2} + result1 := diagnostics.ClearMonitoringResult{ID: 1, Status: diagnostics.ClearMonitoringStatusAccepted} + result2 := diagnostics.ClearMonitoringResult{ID: 2, Status: diagnostics.ClearMonitoringStatusNotFound} + requestJson := fmt.Sprintf(`[2,"%v","%v",{"id":[%v,%v]}]`, messageId, diagnostics.ClearVariableMonitoringFeatureName, ids[0], ids[1]) responseJson := fmt.Sprintf(`[3,"%v",{"clearMonitoringResult":[{"id":%v,"status":"%v"},{"id":%v,"status":"%v"}]}]`, messageId, result1.ID, result1.Status, result2.ID, result2.Status) - clearVariableMonitoringConfirmation := ocpp2.NewClearVariableMonitoringConfirmation([]ocpp2.ClearMonitoringResult{result1, result2}) + clearVariableMonitoringConfirmation := diagnostics.NewClearVariableMonitoringResponse([]diagnostics.ClearMonitoringResult{result1, result2}) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnClearVariableMonitoring", mock.Anything).Return(clearVariableMonitoringConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.ClearVariableMonitoringRequest) + handler := MockChargingStationDiagnosticsHandler{} + handler.On("OnClearVariableMonitoring", mock.Anything).Return(clearVariableMonitoringConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*diagnostics.ClearVariableMonitoringRequest) require.True(t, ok) require.NotNil(t, request) require.Len(t, request.ID, 2) assert.Equal(t, ids[0], request.ID[0]) assert.Equal(t, ids[1], request.ID[1]) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.ClearVariableMonitoring(wsId, func(confirmation *ocpp2.ClearVariableMonitoringConfirmation, err error) { + err = suite.csms.ClearVariableMonitoring(wsId, func(confirmation *diagnostics.ClearVariableMonitoringResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) require.Len(t, confirmation.ClearMonitoringResult, 2) @@ -80,8 +80,8 @@ func (suite *OcppV2TestSuite) TestClearVariableMonitoringE2EMocked() { func (suite *OcppV2TestSuite) TestClearVariableMonitoringInvalidEndpoint() { messageId := defaultMessageId - ids := []int{1,2} - clearVariableMonitoringRequest := ocpp2.NewClearVariableMonitoringRequest(ids) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"id":[%v,%v]}]`, messageId, ocpp2.ClearVariableMonitoringFeatureName, ids[0], ids[1]) - testUnsupportedRequestFromChargePoint(suite, clearVariableMonitoringRequest, requestJson, messageId) + ids := []int{1, 2} + clearVariableMonitoringRequest := diagnostics.NewClearVariableMonitoringRequest(ids) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"id":[%v,%v]}]`, messageId, diagnostics.ClearVariableMonitoringFeatureName, ids[0], ids[1]) + testUnsupportedRequestFromChargingStation(suite, clearVariableMonitoringRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/cleared_charging_limit_test.go b/ocpp2.0_test/cleared_charging_limit_test.go index abe17cd6..8b17c3e6 100644 --- a/ocpp2.0_test/cleared_charging_limit_test.go +++ b/ocpp2.0_test/cleared_charging_limit_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,11 +13,11 @@ import ( func (suite *OcppV2TestSuite) TestClearedChargingLimitRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.ClearedChargingLimitRequest{ChargingLimitSource: ocpp2.ChargingLimitSourceEMS, EvseID: newInt(0)}, true}, - {ocpp2.ClearedChargingLimitRequest{ChargingLimitSource: ocpp2.ChargingLimitSourceEMS}, true}, - {ocpp2.ClearedChargingLimitRequest{}, false}, - {ocpp2.ClearedChargingLimitRequest{ChargingLimitSource: ocpp2.ChargingLimitSourceEMS, EvseID: newInt(-1)}, false}, - {ocpp2.ClearedChargingLimitRequest{ChargingLimitSource: "invalidChargingLimitSource"}, false}, + {smartcharging.ClearedChargingLimitRequest{ChargingLimitSource: types.ChargingLimitSourceEMS, EvseID: newInt(0)}, true}, + {smartcharging.ClearedChargingLimitRequest{ChargingLimitSource: types.ChargingLimitSourceEMS}, true}, + {smartcharging.ClearedChargingLimitRequest{}, false}, + {smartcharging.ClearedChargingLimitRequest{ChargingLimitSource: types.ChargingLimitSourceEMS, EvseID: newInt(-1)}, false}, + {smartcharging.ClearedChargingLimitRequest{ChargingLimitSource: "invalidChargingLimitSource"}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -24,7 +25,7 @@ func (suite *OcppV2TestSuite) TestClearedChargingLimitRequestValidation() { func (suite *OcppV2TestSuite) TestClearedChargingLimitConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.ClearedChargingLimitConfirmation{}, true}, + {smartcharging.ClearedChargingLimitResponse{}, true}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -34,28 +35,28 @@ func (suite *OcppV2TestSuite) TestClearedChargingLimitE2EMocked() { wsId := "test_id" messageId := "1234" wsUrl := "someUrl" - chargingLimitSource := ocpp2.ChargingLimitSourceEMS + chargingLimitSource := types.ChargingLimitSourceEMS evseID := 42 - requestJson := fmt.Sprintf(`[2,"%v","%v",{"chargingLimitSource":"%v","evseId":%v}]`, messageId, ocpp2.ClearedChargingLimitFeatureName, chargingLimitSource, evseID) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"chargingLimitSource":"%v","evseId":%v}]`, messageId, smartcharging.ClearedChargingLimitFeatureName, chargingLimitSource, evseID) responseJson := fmt.Sprintf(`[3,"%v",{}]`, messageId) - clearedChargingLimitConfirmation := ocpp2.NewClearedChargingLimitConfirmation() + clearedChargingLimitConfirmation := smartcharging.NewClearedChargingLimitResponse() channel := NewMockWebSocket(wsId) - coreListener := MockCentralSystemCoreListener{} - coreListener.On("OnClearedChargingLimit", mock.AnythingOfType("string"), mock.Anything).Return(clearedChargingLimitConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(1).(*ocpp2.ClearedChargingLimitRequest) + handler := MockCSMSSmartChargingHandler{} + handler.On("OnClearedChargingLimit", mock.AnythingOfType("string"), mock.Anything).Return(clearedChargingLimitConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(1).(*smartcharging.ClearedChargingLimitRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, chargingLimitSource, request.ChargingLimitSource) assert.Equal(t, evseID, *request.EvseID) }) - setupDefaultCentralSystemHandlers(suite, coreListener, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) // Run test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) - confirmation, err := suite.chargePoint.ClearedChargingLimit(chargingLimitSource, func(request *ocpp2.ClearedChargingLimitRequest) { + confirmation, err := suite.chargingStation.ClearedChargingLimit(chargingLimitSource, func(request *smartcharging.ClearedChargingLimitRequest) { request.EvseID = newInt(evseID) }) require.Nil(t, err) @@ -64,10 +65,10 @@ func (suite *OcppV2TestSuite) TestClearedChargingLimitE2EMocked() { func (suite *OcppV2TestSuite) TestClearedChargingLimitInvalidEndpoint() { messageId := defaultMessageId - chargingLimitSource := ocpp2.ChargingLimitSourceEMS + chargingLimitSource := types.ChargingLimitSourceEMS evseID := 42 - clearedChargingLimitRequest := ocpp2.NewClearedChargingLimitRequest(chargingLimitSource) + clearedChargingLimitRequest := smartcharging.NewClearedChargingLimitRequest(chargingLimitSource) clearedChargingLimitRequest.EvseID = newInt(evseID) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"chargingLimitSource":"%v","evseId":%v}]`, messageId, ocpp2.ClearedChargingLimitFeatureName, chargingLimitSource, evseID) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"chargingLimitSource":"%v","evseId":%v}]`, messageId, smartcharging.ClearedChargingLimitFeatureName, chargingLimitSource, evseID) testUnsupportedRequestFromCentralSystem(suite, clearedChargingLimitRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/common_test.go b/ocpp2.0_test/common_test.go index e1a1223c..6757f976 100644 --- a/ocpp2.0_test/common_test.go +++ b/ocpp2.0_test/common_test.go @@ -1,44 +1,44 @@ package ocpp2_test import ( - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "time" ) // Utility functions -func newInt(i int) *int{ +func newInt(i int) *int { return &i } -func newFloat(f float64) *float64{ +func newFloat(f float64) *float64 { return &f } // Test func (suite *OcppV2TestSuite) TestIdTokenInfoValidation() { var testTable = []GenericTestEntry{ - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeCentral}, PersonalMessage: &ocpp2.MessageContent{Format: ocpp2.MessageFormatUTF8, Language: "en", Content: "random"}}, true}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeCentral}, PersonalMessage: &ocpp2.MessageContent{Format: ocpp2.MessageFormatUTF8, Content: "random"}}, true}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeCentral}}, true}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2"}, true}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1"}, true}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1}, true}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now())}, true}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted}, true}, - {ocpp2.IdTokenInfo{}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeCentral}, PersonalMessage: &ocpp2.MessageContent{Format: "invalidFormat", Language: "en", Content: "random"}}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeCentral}, PersonalMessage: &ocpp2.MessageContent{Format: ocpp2.MessageFormatUTF8, Language: "en", Content: ">512............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."}}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeCentral}, PersonalMessage: &ocpp2.MessageContent{Format: ocpp2.MessageFormatUTF8, Language: "en"}}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeCentral}, PersonalMessage: &ocpp2.MessageContent{}}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{IdToken: "1234", Type: "invalidTokenType"}}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{Type: ocpp2.IdTokenTypeCentral}}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{IdToken: "1234"}}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &ocpp2.GroupIdToken{}}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: ">8......."}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 1, Language1: ">8.......", Language2: "l2"}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: -10}, false}, - {ocpp2.IdTokenInfo{Status: ocpp2.AuthorizationStatusAccepted, CacheExpiryDateTime: ocpp2.NewDateTime(time.Now()), ChargingPriority: 10}, false}, - {ocpp2.IdTokenInfo{Status: "invalidAuthStatus"}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{IdToken: "1234", Type: types.IdTokenTypeCentral}, PersonalMessage: &types.MessageContent{Format: types.MessageFormatUTF8, Language: "en", Content: "random"}}, true}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{IdToken: "1234", Type: types.IdTokenTypeCentral}, PersonalMessage: &types.MessageContent{Format: types.MessageFormatUTF8, Content: "random"}}, true}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{IdToken: "1234", Type: types.IdTokenTypeCentral}}, true}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2"}, true}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1"}, true}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1}, true}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now())}, true}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted}, true}, + {types.IdTokenInfo{}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{IdToken: "1234", Type: types.IdTokenTypeCentral}, PersonalMessage: &types.MessageContent{Format: "invalidFormat", Language: "en", Content: "random"}}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{IdToken: "1234", Type: types.IdTokenTypeCentral}, PersonalMessage: &types.MessageContent{Format: types.MessageFormatUTF8, Language: "en", Content: ">512............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."}}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{IdToken: "1234", Type: types.IdTokenTypeCentral}, PersonalMessage: &types.MessageContent{Format: types.MessageFormatUTF8, Language: "en"}}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{IdToken: "1234", Type: types.IdTokenTypeCentral}, PersonalMessage: &types.MessageContent{}}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{IdToken: "1234", Type: "invalidTokenType"}}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{Type: types.IdTokenTypeCentral}}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{IdToken: "1234"}}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: "l2", GroupIdToken: &types.GroupIdToken{}}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: "l1", Language2: ">8......."}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 1, Language1: ">8.......", Language2: "l2"}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: -10}, false}, + {types.IdTokenInfo{Status: types.AuthorizationStatusAccepted, CacheExpiryDateTime: types.NewDateTime(time.Now()), ChargingPriority: 10}, false}, + {types.IdTokenInfo{Status: "invalidAuthStatus"}, false}, } ExecuteGenericTestTable(suite.T(), testTable) } @@ -46,32 +46,32 @@ func (suite *OcppV2TestSuite) TestIdTokenInfoValidation() { func (suite *OcppV2TestSuite) TestChargingSchedulePeriodValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp2.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: newInt(3)}, true}, - {ocpp2.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0}, true}, - {ocpp2.ChargingSchedulePeriod{StartPeriod: 0}, true}, - {ocpp2.ChargingSchedulePeriod{}, true}, - {ocpp2.ChargingSchedulePeriod{StartPeriod: 0, Limit: -1.0}, false}, - {ocpp2.ChargingSchedulePeriod{StartPeriod: -1, Limit: 10.0}, false}, - {ocpp2.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: newInt(-1)}, false}, + {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: newInt(3)}, true}, + {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0}, true}, + {types.ChargingSchedulePeriod{StartPeriod: 0}, true}, + {types.ChargingSchedulePeriod{}, true}, + {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: -1.0}, false}, + {types.ChargingSchedulePeriod{StartPeriod: -1, Limit: 10.0}, false}, + {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: newInt(-1)}, false}, } ExecuteGenericTestTable(t, testTable) } func (suite *OcppV2TestSuite) TestChargingScheduleValidation() { t := suite.T() - chargingSchedulePeriods := make([]ocpp2.ChargingSchedulePeriod, 2) - chargingSchedulePeriods[0] = ocpp2.NewChargingSchedulePeriod(0, 10.0) - chargingSchedulePeriods[1] = ocpp2.NewChargingSchedulePeriod(100, 8.0) + chargingSchedulePeriods := make([]types.ChargingSchedulePeriod, 2) + chargingSchedulePeriods[0] = types.NewChargingSchedulePeriod(0, 10.0) + chargingSchedulePeriods[1] = types.NewChargingSchedulePeriod(100, 8.0) var testTable = []GenericTestEntry{ - {ocpp2.ChargingSchedule{Duration: newInt(0), StartSchedule: ocpp2.NewDateTime(time.Now()), ChargingRateUnit: ocpp2.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: newFloat(1.0)}, true}, - {ocpp2.ChargingSchedule{Duration: newInt(0), ChargingRateUnit: ocpp2.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: newFloat(1.0)}, true}, - {ocpp2.ChargingSchedule{Duration: newInt(0), ChargingRateUnit: ocpp2.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods}, true}, - {ocpp2.ChargingSchedule{Duration: newInt(0), ChargingRateUnit: ocpp2.ChargingRateUnitWatts}, false}, - {ocpp2.ChargingSchedule{Duration: newInt(0), ChargingSchedulePeriod: chargingSchedulePeriods}, false}, - {ocpp2.ChargingSchedule{Duration: newInt(-1), StartSchedule: ocpp2.NewDateTime(time.Now()), ChargingRateUnit: ocpp2.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: newFloat(1.0)}, false}, - {ocpp2.ChargingSchedule{Duration: newInt(0), StartSchedule: ocpp2.NewDateTime(time.Now()), ChargingRateUnit: ocpp2.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: newFloat(-1.0)}, false}, - {ocpp2.ChargingSchedule{Duration: newInt(0), StartSchedule: ocpp2.NewDateTime(time.Now()), ChargingRateUnit: ocpp2.ChargingRateUnitWatts, ChargingSchedulePeriod: make([]ocpp2.ChargingSchedulePeriod, 0), MinChargingRate: newFloat(1.0)}, false}, - {ocpp2.ChargingSchedule{Duration: newInt(-1), StartSchedule: ocpp2.NewDateTime(time.Now()), ChargingRateUnit: "invalidChargeRateUnit", ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: newFloat(1.0)}, false}, + {types.ChargingSchedule{Duration: newInt(0), StartSchedule: types.NewDateTime(time.Now()), ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: newFloat(1.0)}, true}, + {types.ChargingSchedule{Duration: newInt(0), ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: newFloat(1.0)}, true}, + {types.ChargingSchedule{Duration: newInt(0), ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods}, true}, + {types.ChargingSchedule{Duration: newInt(0), ChargingRateUnit: types.ChargingRateUnitWatts}, false}, + {types.ChargingSchedule{Duration: newInt(0), ChargingSchedulePeriod: chargingSchedulePeriods}, false}, + {types.ChargingSchedule{Duration: newInt(-1), StartSchedule: types.NewDateTime(time.Now()), ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: newFloat(1.0)}, false}, + {types.ChargingSchedule{Duration: newInt(0), StartSchedule: types.NewDateTime(time.Now()), ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: newFloat(-1.0)}, false}, + {types.ChargingSchedule{Duration: newInt(0), StartSchedule: types.NewDateTime(time.Now()), ChargingRateUnit: types.ChargingRateUnitWatts, ChargingSchedulePeriod: make([]types.ChargingSchedulePeriod, 0), MinChargingRate: newFloat(1.0)}, false}, + {types.ChargingSchedule{Duration: newInt(-1), StartSchedule: types.NewDateTime(time.Now()), ChargingRateUnit: "invalidChargeRateUnit", ChargingSchedulePeriod: chargingSchedulePeriods, MinChargingRate: newFloat(1.0)}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -79,42 +79,42 @@ func (suite *OcppV2TestSuite) TestChargingScheduleValidation() { func (suite *OcppV2TestSuite) TestComponentVariableValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1", Instance: "instance1", EVSE: &ocpp2.EVSE{ID: 2, ConnectorID: newInt(2)}}, Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}}, true}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1", Instance: "instance1", EVSE: &ocpp2.EVSE{ID: 2}}, Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}}, true}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1", EVSE: &ocpp2.EVSE{ID: 2}}, Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}}, true}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1", EVSE: &ocpp2.EVSE{ID: 2}}, Variable: ocpp2.Variable{ Name: "variable1"}}, true}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1", EVSE: &ocpp2.EVSE{}}, Variable: ocpp2.Variable{ Name: "variable1"}}, true}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1"}, Variable: ocpp2.Variable{ Name: "variable1"}}, true}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1"}, Variable: ocpp2.Variable{}}, false}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{}, Variable: ocpp2.Variable{ Name: "variable1"}}, false}, - {ocpp2.ComponentVariable{Variable: ocpp2.Variable{ Name: "variable1"}}, false}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1"}}, false}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: ">50................................................", Instance: "instance1", EVSE: &ocpp2.EVSE{ID: 2, ConnectorID: newInt(2)}}, Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}}, false}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1", Instance: ">50................................................", EVSE: &ocpp2.EVSE{ID: 2, ConnectorID: newInt(2)}}, Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}}, false}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1", Instance: "instance1", EVSE: &ocpp2.EVSE{ID: 2, ConnectorID: newInt(2)}}, Variable: ocpp2.Variable{ Name: ">50................................................", Instance: "instance1"}}, false}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1", Instance: "instance1", EVSE: &ocpp2.EVSE{ID: 2, ConnectorID: newInt(2)}}, Variable: ocpp2.Variable{ Name: "variable1", Instance: ">50................................................"}}, false}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1", Instance: "instance1", EVSE: &ocpp2.EVSE{ID: 2, ConnectorID: newInt(-2)}}, Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}}, false}, - {ocpp2.ComponentVariable{Component: ocpp2.Component{ Name: "component1", Instance: "instance1", EVSE: &ocpp2.EVSE{ID: -2, ConnectorID: newInt(2)}}, Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}}, false}, + {types.ComponentVariable{Component: types.Component{Name: "component1", Instance: "instance1", EVSE: &types.EVSE{ID: 2, ConnectorID: newInt(2)}}, Variable: types.Variable{Name: "variable1", Instance: "instance1"}}, true}, + {types.ComponentVariable{Component: types.Component{Name: "component1", Instance: "instance1", EVSE: &types.EVSE{ID: 2}}, Variable: types.Variable{Name: "variable1", Instance: "instance1"}}, true}, + {types.ComponentVariable{Component: types.Component{Name: "component1", EVSE: &types.EVSE{ID: 2}}, Variable: types.Variable{Name: "variable1", Instance: "instance1"}}, true}, + {types.ComponentVariable{Component: types.Component{Name: "component1", EVSE: &types.EVSE{ID: 2}}, Variable: types.Variable{Name: "variable1"}}, true}, + {types.ComponentVariable{Component: types.Component{Name: "component1", EVSE: &types.EVSE{}}, Variable: types.Variable{Name: "variable1"}}, true}, + {types.ComponentVariable{Component: types.Component{Name: "component1"}, Variable: types.Variable{Name: "variable1"}}, true}, + {types.ComponentVariable{Component: types.Component{Name: "component1"}, Variable: types.Variable{}}, false}, + {types.ComponentVariable{Component: types.Component{}, Variable: types.Variable{Name: "variable1"}}, false}, + {types.ComponentVariable{Variable: types.Variable{Name: "variable1"}}, false}, + {types.ComponentVariable{Component: types.Component{Name: "component1"}}, false}, + {types.ComponentVariable{Component: types.Component{Name: ">50................................................", Instance: "instance1", EVSE: &types.EVSE{ID: 2, ConnectorID: newInt(2)}}, Variable: types.Variable{Name: "variable1", Instance: "instance1"}}, false}, + {types.ComponentVariable{Component: types.Component{Name: "component1", Instance: ">50................................................", EVSE: &types.EVSE{ID: 2, ConnectorID: newInt(2)}}, Variable: types.Variable{Name: "variable1", Instance: "instance1"}}, false}, + {types.ComponentVariable{Component: types.Component{Name: "component1", Instance: "instance1", EVSE: &types.EVSE{ID: 2, ConnectorID: newInt(2)}}, Variable: types.Variable{Name: ">50................................................", Instance: "instance1"}}, false}, + {types.ComponentVariable{Component: types.Component{Name: "component1", Instance: "instance1", EVSE: &types.EVSE{ID: 2, ConnectorID: newInt(2)}}, Variable: types.Variable{Name: "variable1", Instance: ">50................................................"}}, false}, + {types.ComponentVariable{Component: types.Component{Name: "component1", Instance: "instance1", EVSE: &types.EVSE{ID: 2, ConnectorID: newInt(-2)}}, Variable: types.Variable{Name: "variable1", Instance: "instance1"}}, false}, + {types.ComponentVariable{Component: types.Component{Name: "component1", Instance: "instance1", EVSE: &types.EVSE{ID: -2, ConnectorID: newInt(2)}}, Variable: types.Variable{Name: "variable1", Instance: "instance1"}}, false}, } ExecuteGenericTestTable(t, testTable) } func (suite *OcppV2TestSuite) TestChargingProfileValidation() { t := suite.T() - chargingSchedule := ocpp2.NewChargingSchedule(ocpp2.ChargingRateUnitWatts, ocpp2.NewChargingSchedulePeriod(0, 10.0), ocpp2.NewChargingSchedulePeriod(100, 8.0)) + chargingSchedule := types.NewChargingSchedule(types.ChargingRateUnitWatts, types.NewChargingSchedulePeriod(0, 10.0), types.NewChargingSchedulePeriod(100, 8.0)) var testTable = []GenericTestEntry{ - {ocpp2.ChargingProfile{ChargingProfileId: 1, TransactionId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: ocpp2.ChargingProfileKindAbsolute, RecurrencyKind: ocpp2.RecurrencyKindDaily, ValidFrom: ocpp2.NewDateTime(time.Now()), ValidTo: ocpp2.NewDateTime(time.Now().Add(8 * time.Hour)), ChargingSchedule: chargingSchedule}, true}, - {ocpp2.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: ocpp2.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, true}, - {ocpp2.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: ocpp2.ChargingProfileKindAbsolute}, false}, - {ocpp2.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, ChargingSchedule: chargingSchedule}, false}, - {ocpp2.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfileKind: ocpp2.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, - {ocpp2.ChargingProfile{ChargingProfileId: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: ocpp2.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, - {ocpp2.ChargingProfile{StackLevel: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: ocpp2.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, true}, - {ocpp2.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: "invalidChargingProfileKind", ChargingSchedule: chargingSchedule}, false}, - {ocpp2.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: "invalidChargingProfilePurpose", ChargingProfileKind: ocpp2.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, - {ocpp2.ChargingProfile{ChargingProfileId: 1, StackLevel: 0, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: ocpp2.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, - {ocpp2.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: ocpp2.ChargingProfileKindAbsolute, RecurrencyKind: "invalidRecurrencyKind", ChargingSchedule: chargingSchedule}, false}, - {ocpp2.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: ocpp2.ChargingProfileKindAbsolute, ChargingSchedule: ocpp2.NewChargingSchedule(ocpp2.ChargingRateUnitWatts)}, false}, + {types.ChargingProfile{ChargingProfileId: 1, TransactionId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, RecurrencyKind: types.RecurrencyKindDaily, ValidFrom: types.NewDateTime(time.Now()), ValidTo: types.NewDateTime(time.Now().Add(8 * time.Hour)), ChargingSchedule: chargingSchedule}, true}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, true}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, true}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: "invalidChargingProfileKind", ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: "invalidChargingProfilePurpose", ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 0, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, RecurrencyKind: "invalidRecurrencyKind", ChargingSchedule: chargingSchedule}, false}, + {types.ChargingProfile{ChargingProfileId: 1, StackLevel: 1, ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, ChargingProfileKind: types.ChargingProfileKindAbsolute, ChargingSchedule: types.NewChargingSchedule(types.ChargingRateUnitWatts)}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -122,30 +122,30 @@ func (suite *OcppV2TestSuite) TestChargingProfileValidation() { func (suite *OcppV2TestSuite) TestSampledValueValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp2.SampledValue{Value: "value", Context: ocpp2.ReadingContextTransactionEnd, Format: ocpp2.ValueFormatRaw, Measurand: ocpp2.MeasurandPowerActiveExport, Phase: ocpp2.PhaseL2, Location: ocpp2.LocationBody, Unit: ocpp2.UnitOfMeasureKW}, true}, - {ocpp2.SampledValue{Value: "value", Context: ocpp2.ReadingContextTransactionEnd, Format: ocpp2.ValueFormatRaw, Measurand: ocpp2.MeasurandPowerActiveExport, Phase: ocpp2.PhaseL2, Location: ocpp2.LocationBody}, true}, - {ocpp2.SampledValue{Value: "value", Context: ocpp2.ReadingContextTransactionEnd, Format: ocpp2.ValueFormatRaw, Measurand: ocpp2.MeasurandPowerActiveExport, Phase: ocpp2.PhaseL2}, true}, - {ocpp2.SampledValue{Value: "value", Context: ocpp2.ReadingContextTransactionEnd, Format: ocpp2.ValueFormatRaw, Measurand: ocpp2.MeasurandPowerActiveExport}, true}, - {ocpp2.SampledValue{Value: "value", Context: ocpp2.ReadingContextTransactionEnd, Format: ocpp2.ValueFormatRaw}, true}, - {ocpp2.SampledValue{Value: "value", Context: ocpp2.ReadingContextTransactionEnd}, true}, - {ocpp2.SampledValue{Value: "value"}, true}, - {ocpp2.SampledValue{Value: "value", Context: "invalidContext"}, false}, - {ocpp2.SampledValue{Value: "value", Format: "invalidFormat"}, false}, - {ocpp2.SampledValue{Value: "value", Measurand: "invalidMeasurand"}, false}, - {ocpp2.SampledValue{Value: "value", Phase: "invalidPhase"}, false}, - {ocpp2.SampledValue{Value: "value", Location: "invalidLocation"}, false}, - {ocpp2.SampledValue{Value: "value", Unit: "invalidUnit"}, false}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd, Format: types.ValueFormatRaw, Measurand: types.MeasurandPowerActiveExport, Phase: types.PhaseL2, Location: types.LocationBody, Unit: types.UnitOfMeasureKW}, true}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd, Format: types.ValueFormatRaw, Measurand: types.MeasurandPowerActiveExport, Phase: types.PhaseL2, Location: types.LocationBody}, true}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd, Format: types.ValueFormatRaw, Measurand: types.MeasurandPowerActiveExport, Phase: types.PhaseL2}, true}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd, Format: types.ValueFormatRaw, Measurand: types.MeasurandPowerActiveExport}, true}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd, Format: types.ValueFormatRaw}, true}, + {types.SampledValue{Value: "value", Context: types.ReadingContextTransactionEnd}, true}, + {types.SampledValue{Value: "value"}, true}, + {types.SampledValue{Value: "value", Context: "invalidContext"}, false}, + {types.SampledValue{Value: "value", Format: "invalidFormat"}, false}, + {types.SampledValue{Value: "value", Measurand: "invalidMeasurand"}, false}, + {types.SampledValue{Value: "value", Phase: "invalidPhase"}, false}, + {types.SampledValue{Value: "value", Location: "invalidLocation"}, false}, + {types.SampledValue{Value: "value", Unit: "invalidUnit"}, false}, } ExecuteGenericTestTable(t, testTable) } func (suite *OcppV2TestSuite) TestMeterValueValidation() { var testTable = []GenericTestEntry{ - {ocpp2.MeterValue{Timestamp: ocpp2.NewDateTime(time.Now()), SampledValue: []ocpp2.SampledValue{{Value: "value"}, {Value: "value2", Unit: ocpp2.UnitOfMeasureKW}}}, true}, - {ocpp2.MeterValue{Timestamp: ocpp2.NewDateTime(time.Now()), SampledValue: []ocpp2.SampledValue{{Value: "value"}}}, true}, - {ocpp2.MeterValue{Timestamp: ocpp2.NewDateTime(time.Now()), SampledValue: []ocpp2.SampledValue{}}, false}, - {ocpp2.MeterValue{Timestamp: ocpp2.NewDateTime(time.Now())}, false}, - {ocpp2.MeterValue{SampledValue: []ocpp2.SampledValue{{Value: "value"}}}, false}, + {types.MeterValue{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: "value"}, {Value: "value2", Unit: types.UnitOfMeasureKW}}}, true}, + {types.MeterValue{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{{Value: "value"}}}, true}, + {types.MeterValue{Timestamp: types.NewDateTime(time.Now()), SampledValue: []types.SampledValue{}}, false}, + {types.MeterValue{Timestamp: types.NewDateTime(time.Now())}, false}, + {types.MeterValue{SampledValue: []types.SampledValue{{Value: "value"}}}, false}, } ExecuteGenericTestTable(suite.T(), testTable) } diff --git a/ocpp2.0_test/cost_updated_test.go b/ocpp2.0_test/cost_updated_test.go index 66c49d25..a10129d5 100644 --- a/ocpp2.0_test/cost_updated_test.go +++ b/ocpp2.0_test/cost_updated_test.go @@ -2,7 +2,7 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/tariffcost" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,11 +12,11 @@ import ( func (suite *OcppV2TestSuite) TestCostUpdatedRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.CostUpdatedRequest{TotalCost: 24.6, TransactionID: "1234"}, true}, - {ocpp2.CostUpdatedRequest{TotalCost: 24.6}, false}, - {ocpp2.CostUpdatedRequest{TransactionID: "1234"}, false}, - {ocpp2.CostUpdatedRequest{}, false}, - {ocpp2.CostUpdatedRequest{TotalCost: 24.6, TransactionID: ">36.................................."}, false}, + {tariffcost.CostUpdatedRequest{TotalCost: 24.6, TransactionID: "1234"}, true}, + {tariffcost.CostUpdatedRequest{TotalCost: 24.6}, false}, + {tariffcost.CostUpdatedRequest{TransactionID: "1234"}, false}, + {tariffcost.CostUpdatedRequest{}, false}, + {tariffcost.CostUpdatedRequest{TotalCost: 24.6, TransactionID: ">36.................................."}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -24,7 +24,7 @@ func (suite *OcppV2TestSuite) TestCostUpdatedRequestValidation() { func (suite *OcppV2TestSuite) TestCostUpdatedConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.CostUpdatedConfirmation{}, true}, + {tariffcost.CostUpdatedResponse{}, true}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -36,27 +36,27 @@ func (suite *OcppV2TestSuite) TestCostUpdatedE2EMocked() { wsUrl := "someUrl" totalCost := 24.6 transactionId := "1234" - requestJson := fmt.Sprintf(`[2,"%v","%v",{"totalCost":%v,"transactionId":"%v"}]`, messageId, ocpp2.CostUpdatedFeatureName, totalCost, transactionId) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"totalCost":%v,"transactionId":"%v"}]`, messageId, tariffcost.CostUpdatedFeatureName, totalCost, transactionId) responseJson := fmt.Sprintf(`[3,"%v",{}]`, messageId) - costUpdatedConfirmation := ocpp2.NewCostUpdatedConfirmation() + costUpdatedConfirmation := tariffcost.NewCostUpdatedResponse() channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnCostUpdated", mock.Anything).Return(costUpdatedConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.CostUpdatedRequest) + handler := MockChargingStationTariffCostHandler{} + handler.On("OnCostUpdated", mock.Anything).Return(costUpdatedConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*tariffcost.CostUpdatedRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, totalCost, request.TotalCost) assert.Equal(t, transactionId, request.TransactionID) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.CostUpdated(wsId, func(confirmation *ocpp2.CostUpdatedConfirmation, err error) { + err = suite.csms.CostUpdated(wsId, func(confirmation *tariffcost.CostUpdatedResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) resultChannel <- true @@ -70,7 +70,7 @@ func (suite *OcppV2TestSuite) TestCostUpdatedInvalidEndpoint() { messageId := defaultMessageId totalCost := 24.6 transactionId := "1234" - costUpdatedRequest := ocpp2.NewCostUpdatedRequest(totalCost, transactionId) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"totalCost":%v,"transactionId":"%v"}]`, messageId, ocpp2.CostUpdatedFeatureName, totalCost, transactionId) - testUnsupportedRequestFromChargePoint(suite, costUpdatedRequest, requestJson, messageId) + costUpdatedRequest := tariffcost.NewCostUpdatedRequest(totalCost, transactionId) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"totalCost":%v,"transactionId":"%v"}]`, messageId, tariffcost.CostUpdatedFeatureName, totalCost, transactionId) + testUnsupportedRequestFromChargingStation(suite, costUpdatedRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/customer_information_test.go b/ocpp2.0_test/customer_information_test.go index 9bc366f9..13a36ecc 100644 --- a/ocpp2.0_test/customer_information_test.go +++ b/ocpp2.0_test/customer_information_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/diagnostics" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,18 +13,18 @@ import ( func (suite *OcppV2TestSuite) TestCustomerInformationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, CustomerIdentifier: "0001", IdToken: &ocpp2.IdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: nil}, CustomerCertificate: &ocpp2.CertificateHashData{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"}}, true}, - {ocpp2.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, CustomerIdentifier: "0001", IdToken: &ocpp2.IdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeKeyCode, AdditionalInfo: nil}}, true}, - {ocpp2.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, CustomerIdentifier: "0001"}, true}, - {ocpp2.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true}, true}, - {ocpp2.CustomerInformationRequest{RequestID: 42, Report: true}, true}, - {ocpp2.CustomerInformationRequest{RequestID: 42, Clear: true}, true}, - {ocpp2.CustomerInformationRequest{Report: true, Clear: true}, true}, - {ocpp2.CustomerInformationRequest{}, true}, - {ocpp2.CustomerInformationRequest{RequestID: -1, Report: true, Clear: true}, false}, - {ocpp2.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, CustomerIdentifier: ">64.............................................................."}, false}, - {ocpp2.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, IdToken: &ocpp2.IdToken{IdToken: "1234", Type: "invalidTokenType", AdditionalInfo: nil}}, false}, - {ocpp2.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, CustomerCertificate: &ocpp2.CertificateHashData{HashAlgorithm: "invalidHasAlgorithm", IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"}}, false}, + {diagnostics.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, CustomerIdentifier: "0001", IdToken: &types.IdToken{IdToken: "1234", Type: types.IdTokenTypeKeyCode, AdditionalInfo: nil}, CustomerCertificate: &types.CertificateHashData{HashAlgorithm: types.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"}}, true}, + {diagnostics.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, CustomerIdentifier: "0001", IdToken: &types.IdToken{IdToken: "1234", Type: types.IdTokenTypeKeyCode, AdditionalInfo: nil}}, true}, + {diagnostics.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, CustomerIdentifier: "0001"}, true}, + {diagnostics.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true}, true}, + {diagnostics.CustomerInformationRequest{RequestID: 42, Report: true}, true}, + {diagnostics.CustomerInformationRequest{RequestID: 42, Clear: true}, true}, + {diagnostics.CustomerInformationRequest{Report: true, Clear: true}, true}, + {diagnostics.CustomerInformationRequest{}, true}, + {diagnostics.CustomerInformationRequest{RequestID: -1, Report: true, Clear: true}, false}, + {diagnostics.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, CustomerIdentifier: ">64.............................................................."}, false}, + {diagnostics.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, IdToken: &types.IdToken{IdToken: "1234", Type: "invalidTokenType", AdditionalInfo: nil}}, false}, + {diagnostics.CustomerInformationRequest{RequestID: 42, Report: true, Clear: true, CustomerCertificate: &types.CertificateHashData{HashAlgorithm: "invalidHasAlgorithm", IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -31,9 +32,9 @@ func (suite *OcppV2TestSuite) TestCustomerInformationRequestValidation() { func (suite *OcppV2TestSuite) TestCustomerInformationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.CustomerInformationConfirmation{Status: ocpp2.CustomerInformationStatusAccepted}, true}, - {ocpp2.CustomerInformationConfirmation{}, false}, - {ocpp2.CustomerInformationConfirmation{Status: "invalidCustomerInformationStatus"}, false}, + {diagnostics.CustomerInformationResponse{Status: diagnostics.CustomerInformationStatusAccepted}, true}, + {diagnostics.CustomerInformationResponse{}, false}, + {diagnostics.CustomerInformationResponse{Status: "invalidCustomerInformationStatus"}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -47,18 +48,18 @@ func (suite *OcppV2TestSuite) TestCustomerInformationE2EMocked() { report := true clear := true customerId := "0001" - idToken := ocpp2.IdToken{IdToken: "1234", Type: ocpp2.IdTokenTypeKeyCode} - customerCertificate := ocpp2.CertificateHashData{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"} - status := ocpp2.CustomerInformationStatusAccepted + idToken := types.IdToken{IdToken: "1234", Type: types.IdTokenTypeKeyCode} + customerCertificate := types.CertificateHashData{HashAlgorithm: types.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"} + status := diagnostics.CustomerInformationStatusAccepted requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"report":%v,"clear":%v,"customerIdentifier":"%v","idToken":{"idToken":"%v","type":"%v"},"customerCertificate":{"hashAlgorithm":"%v","issuerNameHash":"%v","issuerKeyHash":"%v","serialNumber":"%v"}}]`, - messageId, ocpp2.CustomerInformationFeatureName, requestId, report, clear, customerId, idToken.IdToken, idToken.Type, customerCertificate.HashAlgorithm, customerCertificate.IssuerNameHash, customerCertificate.IssuerKeyHash, customerCertificate.SerialNumber) + messageId, diagnostics.CustomerInformationFeatureName, requestId, report, clear, customerId, idToken.IdToken, idToken.Type, customerCertificate.HashAlgorithm, customerCertificate.IssuerNameHash, customerCertificate.IssuerKeyHash, customerCertificate.SerialNumber) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - customerInformationConfirmation := ocpp2.NewCustomerInformationConfirmation(status) + customerInformationConfirmation := diagnostics.NewCustomerInformationResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnCustomerInformation", mock.Anything).Return(customerInformationConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.CustomerInformationRequest) + handler := MockChargingStationDiagnosticsHandler{} + handler.On("OnCustomerInformation", mock.Anything).Return(customerInformationConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*diagnostics.CustomerInformationRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, requestId, request.RequestID) @@ -68,19 +69,19 @@ func (suite *OcppV2TestSuite) TestCustomerInformationE2EMocked() { require.NotNil(t, request.IdToken) require.NotNil(t, request.CustomerCertificate) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.CustomerInformation(wsId, func(confirmation *ocpp2.CustomerInformationConfirmation, err error) { + err = suite.csms.CustomerInformation(wsId, func(confirmation *diagnostics.CustomerInformationResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) require.Equal(t, status, confirmation.Status) resultChannel <- true - }, requestId, report, clear, func(request *ocpp2.CustomerInformationRequest) { + }, requestId, report, clear, func(request *diagnostics.CustomerInformationRequest) { request.CustomerIdentifier = customerId request.IdToken = &idToken request.CustomerCertificate = &customerCertificate @@ -96,7 +97,7 @@ func (suite *OcppV2TestSuite) TestCustomerInformationInvalidEndpoint() { report := true clear := true customerId := "0001" - requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"report":%v,"clear":%v,"customerIdentifier":"%v"}]`, messageId, ocpp2.CustomerInformationFeatureName, requestId, report, clear, customerId) - customerInformationRequest := ocpp2.NewCustomerInformationRequest(requestId, report, clear) - testUnsupportedRequestFromChargePoint(suite, customerInformationRequest, requestJson, messageId) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"report":%v,"clear":%v,"customerIdentifier":"%v"}]`, messageId, diagnostics.CustomerInformationFeatureName, requestId, report, clear, customerId) + customerInformationRequest := diagnostics.NewCustomerInformationRequest(requestId, report, clear) + testUnsupportedRequestFromChargingStation(suite, customerInformationRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/data_transfer_test.go b/ocpp2.0_test/data_transfer_test.go index fbc87616..b418fa22 100644 --- a/ocpp2.0_test/data_transfer_test.go +++ b/ocpp2.0_test/data_transfer_test.go @@ -2,7 +2,7 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/data" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,12 +12,12 @@ import ( func (suite *OcppV2TestSuite) TestDataTransferRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.DataTransferRequest{VendorId: "12345"}, true}, - {ocpp2.DataTransferRequest{VendorId: "12345", MessageId: "6789"}, true}, - {ocpp2.DataTransferRequest{VendorId: "12345", MessageId: "6789", Data: "mockData"}, true}, - {ocpp2.DataTransferRequest{}, false}, - {ocpp2.DataTransferRequest{VendorId: ">255............................................................................................................................................................................................................................................................"}, false}, - {ocpp2.DataTransferRequest{VendorId: "12345", MessageId: ">50................................................"}, false}, + {data.DataTransferRequest{VendorId: "12345"}, true}, + {data.DataTransferRequest{VendorId: "12345", MessageId: "6789"}, true}, + {data.DataTransferRequest{VendorId: "12345", MessageId: "6789", Data: "mockData"}, true}, + {data.DataTransferRequest{}, false}, + {data.DataTransferRequest{VendorId: ">255............................................................................................................................................................................................................................................................"}, false}, + {data.DataTransferRequest{VendorId: "12345", MessageId: ">50................................................"}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -25,12 +25,12 @@ func (suite *OcppV2TestSuite) TestDataTransferRequestValidation() { func (suite *OcppV2TestSuite) TestDataTransferConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.DataTransferConfirmation{Status: ocpp2.DataTransferStatusAccepted}, true}, - {ocpp2.DataTransferConfirmation{Status: ocpp2.DataTransferStatusRejected}, true}, - {ocpp2.DataTransferConfirmation{Status: ocpp2.DataTransferStatusUnknownMessageId}, true}, - {ocpp2.DataTransferConfirmation{Status: ocpp2.DataTransferStatusUnknownVendorId}, true}, - {ocpp2.DataTransferConfirmation{Status: "invalidDataTransferStatus"}, false}, - {ocpp2.DataTransferConfirmation{Status: ocpp2.DataTransferStatusAccepted, Data: "mockData"}, true}, + {data.DataTransferResponse{Status: data.DataTransferStatusAccepted}, true}, + {data.DataTransferResponse{Status: data.DataTransferStatusRejected}, true}, + {data.DataTransferResponse{Status: data.DataTransferStatusUnknownMessageId}, true}, + {data.DataTransferResponse{Status: data.DataTransferStatusUnknownVendorId}, true}, + {data.DataTransferResponse{Status: "invalidDataTransferStatus"}, false}, + {data.DataTransferResponse{Status: data.DataTransferStatusAccepted, Data: "mockData"}, true}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -41,26 +41,26 @@ func (suite *OcppV2TestSuite) TestDataTransferFromChargePointE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" vendorId := "vendor1" - status := ocpp2.DataTransferStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"vendorId":"%v"}]`, messageId, ocpp2.DataTransferFeatureName, vendorId) + status := data.DataTransferStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"vendorId":"%v"}]`, messageId, data.DataTransferFeatureName, vendorId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - dataTransferConfirmation := ocpp2.NewDataTransferConfirmation(status) + dataTransferConfirmation := data.NewDataTransferResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockCentralSystemCoreListener{} - coreListener.On("OnDataTransfer", mock.AnythingOfType("string"), mock.Anything).Return(dataTransferConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(1).(*ocpp2.DataTransferRequest) + handler := MockCSMSDataHandler{} + handler.On("OnDataTransfer", mock.AnythingOfType("string"), mock.Anything).Return(dataTransferConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(1).(*data.DataTransferRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, vendorId, request.VendorId) }) - setupDefaultCentralSystemHandlers(suite, coreListener, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) assert.Nil(t, err) - confirmation, err := suite.chargePoint.DataTransfer(vendorId) + confirmation, err := suite.chargingStation.DataTransfer(vendorId) assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -72,27 +72,27 @@ func (suite *OcppV2TestSuite) TestDataTransferFromCentralSystemE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" vendorId := "vendor1" - status := ocpp2.DataTransferStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"vendorId":"%v"}]`, messageId, ocpp2.DataTransferFeatureName, vendorId) + status := data.DataTransferStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"vendorId":"%v"}]`, messageId, data.DataTransferFeatureName, vendorId) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - dataTransferConfirmation := ocpp2.NewDataTransferConfirmation(status) + dataTransferConfirmation := data.NewDataTransferResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnDataTransfer", mock.Anything).Return(dataTransferConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.DataTransferRequest) + handler := MockChargingStationDataHandler{} + handler.On("OnDataTransfer", mock.Anything).Return(dataTransferConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*data.DataTransferRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, vendorId, request.VendorId) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.DataTransfer(wsId, func(confirmation *ocpp2.DataTransferConfirmation, err error) { + err = suite.csms.DataTransfer(wsId, func(confirmation *data.DataTransferResponse, err error) { assert.Nil(t, err) assert.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) diff --git a/ocpp2.0_test/delete_certificate_test.go b/ocpp2.0_test/delete_certificate_test.go index a9e30636..1d47ad27 100644 --- a/ocpp2.0_test/delete_certificate_test.go +++ b/ocpp2.0_test/delete_certificate_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/iso15118" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,9 +13,9 @@ import ( func (suite *OcppV2TestSuite) TestDeleteCertificateRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.DeleteCertificateRequest{CertificateHashData: ocpp2.CertificateHashData{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"}}, true}, - {ocpp2.DeleteCertificateRequest{}, false}, - {ocpp2.DeleteCertificateRequest{CertificateHashData: ocpp2.CertificateHashData{HashAlgorithm: "invalidHashAlgorithm", IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"}}, false}, + {iso15118.DeleteCertificateRequest{CertificateHashData: types.CertificateHashData{HashAlgorithm: types.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"}}, true}, + {iso15118.DeleteCertificateRequest{}, false}, + {iso15118.DeleteCertificateRequest{CertificateHashData: types.CertificateHashData{HashAlgorithm: "invalidHashAlgorithm", IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -22,11 +23,11 @@ func (suite *OcppV2TestSuite) TestDeleteCertificateRequestValidation() { func (suite *OcppV2TestSuite) TestDeleteCertificateConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.DeleteCertificateConfirmation{Status: ocpp2.DeleteCertificateStatusAccepted}, true}, - {ocpp2.DeleteCertificateConfirmation{Status: ocpp2.DeleteCertificateStatusFailed}, true}, - {ocpp2.DeleteCertificateConfirmation{Status: ocpp2.DeleteCertificateStatusNotFound}, true}, - {ocpp2.DeleteCertificateConfirmation{Status: "invalidDeleteCertificateStatus"}, false}, - {ocpp2.DeleteCertificateConfirmation{}, false}, + {iso15118.DeleteCertificateResponse{Status: iso15118.DeleteCertificateStatusAccepted}, true}, + {iso15118.DeleteCertificateResponse{Status: iso15118.DeleteCertificateStatusFailed}, true}, + {iso15118.DeleteCertificateResponse{Status: iso15118.DeleteCertificateStatusNotFound}, true}, + {iso15118.DeleteCertificateResponse{Status: "invalidDeleteCertificateStatus"}, false}, + {iso15118.DeleteCertificateResponse{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -36,17 +37,17 @@ func (suite *OcppV2TestSuite) TestDeleteCertificateE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - certificateHashData := ocpp2.CertificateHashData{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"} - status := ocpp2.DeleteCertificateStatusAccepted + certificateHashData := types.CertificateHashData{HashAlgorithm: types.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"} + status := iso15118.DeleteCertificateStatusAccepted requestJson := fmt.Sprintf(`[2,"%v","%v",{"certificateHashData":{"hashAlgorithm":"%v","issuerNameHash":"%v","issuerKeyHash":"%v","serialNumber":"%v"}}]`, - messageId, ocpp2.DeleteCertificateFeatureName, certificateHashData.HashAlgorithm, certificateHashData.IssuerNameHash, certificateHashData.IssuerKeyHash, certificateHashData.SerialNumber) + messageId, iso15118.DeleteCertificateFeatureName, certificateHashData.HashAlgorithm, certificateHashData.IssuerNameHash, certificateHashData.IssuerKeyHash, certificateHashData.SerialNumber) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - deleteCertificateConfirmation := ocpp2.NewDeleteCertificateConfirmation(status) + deleteCertificateConfirmation := iso15118.NewDeleteCertificateResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnDeleteCertificate", mock.Anything).Return(deleteCertificateConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.DeleteCertificateRequest) + handler := MockChargingStationIso15118Handler{} + handler.On("OnDeleteCertificate", mock.Anything).Return(deleteCertificateConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*iso15118.DeleteCertificateRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, certificateHashData.HashAlgorithm, request.CertificateHashData.HashAlgorithm) @@ -54,14 +55,14 @@ func (suite *OcppV2TestSuite) TestDeleteCertificateE2EMocked() { assert.Equal(t, certificateHashData.IssuerKeyHash, request.CertificateHashData.IssuerKeyHash) assert.Equal(t, certificateHashData.SerialNumber, request.CertificateHashData.SerialNumber) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.DeleteCertificate(wsId, func(confirmation *ocpp2.DeleteCertificateConfirmation, err error) { + err = suite.csms.DeleteCertificate(wsId, func(confirmation *iso15118.DeleteCertificateResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -74,9 +75,9 @@ func (suite *OcppV2TestSuite) TestDeleteCertificateE2EMocked() { func (suite *OcppV2TestSuite) TestDeleteCertificateInvalidEndpoint() { messageId := defaultMessageId - certificateHashData := ocpp2.CertificateHashData{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"} - deleteCertificateRequest := ocpp2.NewDeleteCertificateRequest(certificateHashData) + certificateHashData := types.CertificateHashData{HashAlgorithm: types.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0"} + deleteCertificateRequest := iso15118.NewDeleteCertificateRequest(certificateHashData) requestJson := fmt.Sprintf(`[2,"%v","%v",{"certificateHashData":{"hashAlgorithm":"%v","issuerNameHash":"%v","issuerKeyHash":"%v","serialNumber":"%v"}}]`, - messageId, ocpp2.DeleteCertificateFeatureName, certificateHashData.HashAlgorithm, certificateHashData.IssuerNameHash, certificateHashData.IssuerKeyHash, certificateHashData.SerialNumber) - testUnsupportedRequestFromChargePoint(suite, deleteCertificateRequest, requestJson, messageId) + messageId, iso15118.DeleteCertificateFeatureName, certificateHashData.HashAlgorithm, certificateHashData.IssuerNameHash, certificateHashData.IssuerKeyHash, certificateHashData.SerialNumber) + testUnsupportedRequestFromChargingStation(suite, deleteCertificateRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/firmware_status_notification_test.go b/ocpp2.0_test/firmware_status_notification_test.go index a6ca344e..88bd94eb 100644 --- a/ocpp2.0_test/firmware_status_notification_test.go +++ b/ocpp2.0_test/firmware_status_notification_test.go @@ -2,7 +2,7 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/firmware" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,12 +12,12 @@ import ( func (suite *OcppV2TestSuite) TestFirmwareStatusNotificationRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.FirmwareStatusNotificationRequest{Status: ocpp2.FirmwareStatusDownloaded, RequestID: 42}, true}, - {ocpp2.FirmwareStatusNotificationRequest{Status: ocpp2.FirmwareStatusDownloaded}, true}, - {ocpp2.FirmwareStatusNotificationRequest{RequestID: 42}, false}, - {ocpp2.FirmwareStatusNotificationRequest{}, false}, - {ocpp2.FirmwareStatusNotificationRequest{Status: ocpp2.FirmwareStatusDownloaded, RequestID: -1}, false}, - {ocpp2.FirmwareStatusNotificationRequest{Status: "invalidFirmwareStatus"}, false}, + {firmware.FirmwareStatusNotificationRequest{Status: firmware.FirmwareStatusDownloaded, RequestID: 42}, true}, + {firmware.FirmwareStatusNotificationRequest{Status: firmware.FirmwareStatusDownloaded}, true}, + {firmware.FirmwareStatusNotificationRequest{RequestID: 42}, false}, + {firmware.FirmwareStatusNotificationRequest{}, false}, + {firmware.FirmwareStatusNotificationRequest{Status: firmware.FirmwareStatusDownloaded, RequestID: -1}, false}, + {firmware.FirmwareStatusNotificationRequest{Status: "invalidFirmwareStatus"}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -25,7 +25,7 @@ func (suite *OcppV2TestSuite) TestFirmwareStatusNotificationRequestValidation() func (suite *OcppV2TestSuite) TestFirmwareStatusNotificationConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.FirmwareStatusNotificationConfirmation{}, true}, + {firmware.FirmwareStatusNotificationResponse{}, true}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -35,36 +35,36 @@ func (suite *OcppV2TestSuite) TestFirmwareStatusNotificationE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - status := ocpp2.FirmwareStatusDownloaded + status := firmware.FirmwareStatusDownloaded requestID := 42 - requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v","requestId":%v}]`, messageId, ocpp2.FirmwareStatusNotificationFeatureName, status, requestID) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v","requestId":%v}]`, messageId, firmware.FirmwareStatusNotificationFeatureName, status, requestID) responseJson := fmt.Sprintf(`[3,"%v",{}]`, messageId) - firmwareStatusNotificationConfirmation := ocpp2.NewFirmwareStatusNotificationConfirmation() + firmwareStatusNotificationConfirmation := firmware.NewFirmwareStatusNotificationResponse() channel := NewMockWebSocket(wsId) - coreListener := MockCentralSystemCoreListener{} - coreListener.On("OnFirmwareStatusNotification", mock.AnythingOfType("string"), mock.Anything).Return(firmwareStatusNotificationConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(1).(*ocpp2.FirmwareStatusNotificationRequest) + handler := MockCSMSFirmwareHandler{} + handler.On("OnFirmwareStatusNotification", mock.AnythingOfType("string"), mock.Anything).Return(firmwareStatusNotificationConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(1).(*firmware.FirmwareStatusNotificationRequest) require.True(t, ok) assert.Equal(t, status, request.Status) assert.Equal(t, requestID, request.RequestID) }) - setupDefaultCentralSystemHandlers(suite, coreListener, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) - confirmation, err := suite.chargePoint.FirmwareStatusNotification(status, requestID) + confirmation, err := suite.chargingStation.FirmwareStatusNotification(status, requestID) assert.Nil(t, err) assert.NotNil(t, confirmation) } func (suite *OcppV2TestSuite) TestFirmwareStatusNotificationInvalidEndpoint() { messageId := defaultMessageId - status := ocpp2.FirmwareStatusDownloaded + status := firmware.FirmwareStatusDownloaded requestID := 42 - firmwareStatusRequest := ocpp2.NewFirmwareStatusNotificationRequest(status, requestID) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v","requestId":%v}]`, messageId, ocpp2.FirmwareStatusNotificationFeatureName, status, requestID) + firmwareStatusRequest := firmware.NewFirmwareStatusNotificationRequest(status, requestID) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"status":"%v","requestId":%v}]`, messageId, firmware.FirmwareStatusNotificationFeatureName, status, requestID) testUnsupportedRequestFromCentralSystem(suite, firmwareStatusRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/get_15118ev_certificate_test.go b/ocpp2.0_test/get_15118ev_certificate_test.go index aaed4979..afc7ab58 100644 --- a/ocpp2.0_test/get_15118ev_certificate_test.go +++ b/ocpp2.0_test/get_15118ev_certificate_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/iso15118" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,11 +13,11 @@ import ( func (suite *OcppV2TestSuite) TestGet15118EVCertificateRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.Get15118EVCertificateRequest{SchemaVersion: "1.0", ExiRequest: "deadbeef"}, true}, - {ocpp2.Get15118EVCertificateRequest{SchemaVersion: "1.0"}, false}, - {ocpp2.Get15118EVCertificateRequest{ExiRequest: "deadbeef"}, false}, - {ocpp2.Get15118EVCertificateRequest{}, false}, - {ocpp2.Get15118EVCertificateRequest{SchemaVersion: ">50................................................", ExiRequest: "deadbeef"}, false}, + {iso15118.Get15118EVCertificateRequest{SchemaVersion: "1.0", ExiRequest: "deadbeef"}, true}, + {iso15118.Get15118EVCertificateRequest{SchemaVersion: "1.0"}, false}, + {iso15118.Get15118EVCertificateRequest{ExiRequest: "deadbeef"}, false}, + {iso15118.Get15118EVCertificateRequest{}, false}, + {iso15118.Get15118EVCertificateRequest{SchemaVersion: ">50................................................", ExiRequest: "deadbeef"}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -24,17 +25,17 @@ func (suite *OcppV2TestSuite) TestGet15118EVCertificateRequestValidation() { func (suite *OcppV2TestSuite) TestGet15118EVCertificateConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.Get15118EVCertificateConfirmation{Status: ocpp2.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode"}, SaProvisioningCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode2"}}, true}, - {ocpp2.Get15118EVCertificateConfirmation{Status: ocpp2.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode", ChildCertificate: []string{"c1", "c2", "c3", "c4"}}, SaProvisioningCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode2"}}, true}, - {ocpp2.Get15118EVCertificateConfirmation{Status: ocpp2.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode"}}, false}, - {ocpp2.Get15118EVCertificateConfirmation{Status: ocpp2.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", SaProvisioningCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode2"}}, false}, - {ocpp2.Get15118EVCertificateConfirmation{Status: ocpp2.Certificate15188EVStatusAccepted, ContractSignatureCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode"}, SaProvisioningCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode2"}}, false}, - {ocpp2.Get15118EVCertificateConfirmation{ExiResponse: "deadbeef", ContractSignatureCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode"}, SaProvisioningCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode2"}}, false}, - {ocpp2.Get15118EVCertificateConfirmation{}, false}, - {ocpp2.Get15118EVCertificateConfirmation{Status: "invalidCertificateStatus", ExiResponse: "deadbeef", ContractSignatureCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode"}, SaProvisioningCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode2"}}, false}, - {ocpp2.Get15118EVCertificateConfirmation{Status: ocpp2.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode"}, SaProvisioningCertificateChain: ocpp2.CertificateChain{}}, false}, - {ocpp2.Get15118EVCertificateConfirmation{Status: ocpp2.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode", ChildCertificate: []string{"c1", "c2", "c3", "c4", "c5"}}, SaProvisioningCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode2"}}, false}, - {ocpp2.Get15118EVCertificateConfirmation{Status: ocpp2.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode", ChildCertificate: []string{"c1", "c2", "c3", ""}}, SaProvisioningCertificateChain: ocpp2.CertificateChain{Certificate: "deadcode2"}}, false}, + {iso15118.Get15118EVCertificateResponse{Status: types.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: iso15118.CertificateChain{Certificate: "deadcode"}, SaProvisioningCertificateChain: iso15118.CertificateChain{Certificate: "deadcode2"}}, true}, + {iso15118.Get15118EVCertificateResponse{Status: types.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: iso15118.CertificateChain{Certificate: "deadcode", ChildCertificate: []string{"c1", "c2", "c3", "c4"}}, SaProvisioningCertificateChain: iso15118.CertificateChain{Certificate: "deadcode2"}}, true}, + {iso15118.Get15118EVCertificateResponse{Status: types.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: iso15118.CertificateChain{Certificate: "deadcode"}}, false}, + {iso15118.Get15118EVCertificateResponse{Status: types.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", SaProvisioningCertificateChain: iso15118.CertificateChain{Certificate: "deadcode2"}}, false}, + {iso15118.Get15118EVCertificateResponse{Status: types.Certificate15188EVStatusAccepted, ContractSignatureCertificateChain: iso15118.CertificateChain{Certificate: "deadcode"}, SaProvisioningCertificateChain: iso15118.CertificateChain{Certificate: "deadcode2"}}, false}, + {iso15118.Get15118EVCertificateResponse{ExiResponse: "deadbeef", ContractSignatureCertificateChain: iso15118.CertificateChain{Certificate: "deadcode"}, SaProvisioningCertificateChain: iso15118.CertificateChain{Certificate: "deadcode2"}}, false}, + {iso15118.Get15118EVCertificateResponse{}, false}, + {iso15118.Get15118EVCertificateResponse{Status: "invalidCertificateStatus", ExiResponse: "deadbeef", ContractSignatureCertificateChain: iso15118.CertificateChain{Certificate: "deadcode"}, SaProvisioningCertificateChain: iso15118.CertificateChain{Certificate: "deadcode2"}}, false}, + {iso15118.Get15118EVCertificateResponse{Status: types.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: iso15118.CertificateChain{Certificate: "deadcode"}, SaProvisioningCertificateChain: iso15118.CertificateChain{}}, false}, + {iso15118.Get15118EVCertificateResponse{Status: types.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: iso15118.CertificateChain{Certificate: "deadcode", ChildCertificate: []string{"c1", "c2", "c3", "c4", "c5"}}, SaProvisioningCertificateChain: iso15118.CertificateChain{Certificate: "deadcode2"}}, false}, + {iso15118.Get15118EVCertificateResponse{Status: types.Certificate15188EVStatusAccepted, ExiResponse: "deadbeef", ContractSignatureCertificateChain: iso15118.CertificateChain{Certificate: "deadcode", ChildCertificate: []string{"c1", "c2", "c3", ""}}, SaProvisioningCertificateChain: iso15118.CertificateChain{Certificate: "deadcode2"}}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -44,32 +45,32 @@ func (suite *OcppV2TestSuite) TestGet15118EVCertificateE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - status := ocpp2.Certificate15188EVStatusAccepted + status := types.Certificate15188EVStatusAccepted schemaVersion := "1.0" exiRequest := "deadbeef" exiResponse := "deadbeef2" - contractSignatureCertificateChain := ocpp2.CertificateChain{Certificate: "deadcode"} - saProvisioningCertificateChain := ocpp2.CertificateChain{Certificate: "deadcode2"} - requestJson := fmt.Sprintf(`[2,"%v","%v",{"15118SchemaVersion":"%v","exiRequest":"%v"}]`, messageId, ocpp2.Get15118EVCertificateFeatureName, schemaVersion, exiRequest) + contractSignatureCertificateChain := iso15118.CertificateChain{Certificate: "deadcode"} + saProvisioningCertificateChain := iso15118.CertificateChain{Certificate: "deadcode2"} + requestJson := fmt.Sprintf(`[2,"%v","%v",{"15118SchemaVersion":"%v","exiRequest":"%v"}]`, messageId, iso15118.Get15118EVCertificateFeatureName, schemaVersion, exiRequest) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v","exiResponse":"%v","contractSignatureCertificateChain":{"certificate":"%v"},"saProvisioningCertificateChain":{"certificate":"%v"}}]`, messageId, status, exiResponse, contractSignatureCertificateChain.Certificate, saProvisioningCertificateChain.Certificate) - get15118EVCertificateConfirmation := ocpp2.NewGet15118EVCertificateConfirmation(status, exiResponse, contractSignatureCertificateChain, saProvisioningCertificateChain) + get15118EVCertificateConfirmation := iso15118.NewGet15118EVCertificateResponse(status, exiResponse, contractSignatureCertificateChain, saProvisioningCertificateChain) channel := NewMockWebSocket(wsId) - coreListener := MockCentralSystemCoreListener{} - coreListener.On("OnGet15118EVCertificate", mock.AnythingOfType("string"), mock.Anything).Return(get15118EVCertificateConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(1).(*ocpp2.Get15118EVCertificateRequest) + handler := MockCSMSIso15118Handler{} + handler.On("OnGet15118EVCertificate", mock.AnythingOfType("string"), mock.Anything).Return(get15118EVCertificateConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(1).(*iso15118.Get15118EVCertificateRequest) require.True(t, ok) assert.Equal(t, schemaVersion, request.SchemaVersion) assert.Equal(t, exiRequest, request.ExiRequest) }) - setupDefaultCentralSystemHandlers(suite, coreListener, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) - confirmation, err := suite.chargePoint.Get15118EVCertificate(schemaVersion, exiRequest) + confirmation, err := suite.chargingStation.Get15118EVCertificate(schemaVersion, exiRequest) require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -82,7 +83,7 @@ func (suite *OcppV2TestSuite) TestGet15118EVCertificateInvalidEndpoint() { messageId := defaultMessageId schemaVersion := "1.0" exiRequest := "deadbeef" - get15118EVCertificateRequest := ocpp2.NewGet15118EVCertificateRequest(schemaVersion, exiRequest) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"15118SchemaVersion":"%v","exiRequest":"%v"}]`, messageId, ocpp2.Get15118EVCertificateFeatureName, schemaVersion, exiRequest) + get15118EVCertificateRequest := iso15118.NewGet15118EVCertificateRequest(schemaVersion, exiRequest) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"15118SchemaVersion":"%v","exiRequest":"%v"}]`, messageId, iso15118.Get15118EVCertificateFeatureName, schemaVersion, exiRequest) testUnsupportedRequestFromCentralSystem(suite, get15118EVCertificateRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/get_base_report_test.go b/ocpp2.0_test/get_base_report_test.go index 60a78b10..6d642527 100644 --- a/ocpp2.0_test/get_base_report_test.go +++ b/ocpp2.0_test/get_base_report_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/provisioning" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,12 +13,12 @@ import ( func (suite *OcppV2TestSuite) TestGetBaseReportRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.GetBaseReportRequest{RequestID: 42, ReportBase: ocpp2.ReportTypeConfigurationInventory}, true}, - {ocpp2.GetBaseReportRequest{ReportBase: ocpp2.ReportTypeConfigurationInventory}, true}, - {ocpp2.GetBaseReportRequest{RequestID: 42}, false}, - {ocpp2.GetBaseReportRequest{}, false}, - {ocpp2.GetBaseReportRequest{RequestID: -1, ReportBase: ocpp2.ReportTypeConfigurationInventory}, false}, - {ocpp2.GetBaseReportRequest{RequestID: 42, ReportBase: "invalidReportType"}, false}, + {provisioning.GetBaseReportRequest{RequestID: 42, ReportBase: provisioning.ReportTypeConfigurationInventory}, true}, + {provisioning.GetBaseReportRequest{ReportBase: provisioning.ReportTypeConfigurationInventory}, true}, + {provisioning.GetBaseReportRequest{RequestID: 42}, false}, + {provisioning.GetBaseReportRequest{}, false}, + {provisioning.GetBaseReportRequest{RequestID: -1, ReportBase: provisioning.ReportTypeConfigurationInventory}, false}, + {provisioning.GetBaseReportRequest{RequestID: 42, ReportBase: "invalidReportType"}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -25,9 +26,9 @@ func (suite *OcppV2TestSuite) TestGetBaseReportRequestValidation() { func (suite *OcppV2TestSuite) TestGetBaseReportConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.GetBaseReportConfirmation{Status: ocpp2.GenericDeviceModelStatusAccepted}, true}, - {ocpp2.GetBaseReportConfirmation{Status: "invalidDeviceModelStatus"}, false}, - {ocpp2.GetBaseReportConfirmation{}, false}, + {provisioning.GetBaseReportResponse{Status: types.GenericDeviceModelStatusAccepted}, true}, + {provisioning.GetBaseReportResponse{Status: "invalidDeviceModelStatus"}, false}, + {provisioning.GetBaseReportResponse{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -38,29 +39,29 @@ func (suite *OcppV2TestSuite) TestGetBaseReportE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" requestID := 42 - reportBase := ocpp2.ReportTypeConfigurationInventory - status := ocpp2.GenericDeviceModelStatusAccepted - requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"reportBase":"%v"}]`, messageId, ocpp2.GetBaseReportFeatureName, requestID, reportBase) + reportBase := provisioning.ReportTypeConfigurationInventory + status := types.GenericDeviceModelStatusAccepted + requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"reportBase":"%v"}]`, messageId, provisioning.GetBaseReportFeatureName, requestID, reportBase) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - getBaseReportConfirmation := ocpp2.NewGetBaseReportConfirmation(status) + getBaseReportConfirmation := provisioning.NewGetBaseReportResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnGetBaseReport", mock.Anything).Return(getBaseReportConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.GetBaseReportRequest) + handler := MockChargingStationProvisioningHandler{} + handler.On("OnGetBaseReport", mock.Anything).Return(getBaseReportConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*provisioning.GetBaseReportRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, requestID, request.RequestID) assert.Equal(t, reportBase, request.ReportBase) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.GetBaseReport(wsId, func(confirmation *ocpp2.GetBaseReportConfirmation, err error) { + err = suite.csms.GetBaseReport(wsId, func(confirmation *provisioning.GetBaseReportResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -74,8 +75,8 @@ func (suite *OcppV2TestSuite) TestGetBaseReportE2EMocked() { func (suite *OcppV2TestSuite) TestGetBaseReportInvalidEndpoint() { messageId := defaultMessageId requestID := 42 - reportBase := ocpp2.ReportTypeConfigurationInventory - getBaseReportRequest := ocpp2.NewGetBaseReportRequest(requestID, reportBase) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"reportBase":"%v"}]`, messageId, ocpp2.GetBaseReportFeatureName, requestID, reportBase) - testUnsupportedRequestFromChargePoint(suite, getBaseReportRequest, requestJson, messageId) + reportBase := provisioning.ReportTypeConfigurationInventory + getBaseReportRequest := provisioning.NewGetBaseReportRequest(requestID, reportBase) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"reportBase":"%v"}]`, messageId, provisioning.GetBaseReportFeatureName, requestID, reportBase) + testUnsupportedRequestFromChargingStation(suite, getBaseReportRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/get_certificate_status_test.go b/ocpp2.0_test/get_certificate_status_test.go index bf178473..0f124e2b 100644 --- a/ocpp2.0_test/get_certificate_status_test.go +++ b/ocpp2.0_test/get_certificate_status_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/iso15118" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,9 +13,9 @@ import ( func (suite *OcppV2TestSuite) TestGetCertificateStatusRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.GetCertificateStatusRequest{OcspRequestData: ocpp2.OCSPRequestDataType{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0", ResponderURL: "http://someUrl"}}, true}, - {ocpp2.GetCertificateStatusRequest{}, false}, - {ocpp2.GetCertificateStatusRequest{OcspRequestData: ocpp2.OCSPRequestDataType{HashAlgorithm: "invalidHashAlgorithm", IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0", ResponderURL: "http://someUrl"}}, false}, + {iso15118.GetCertificateStatusRequest{OcspRequestData: types.OCSPRequestDataType{HashAlgorithm: types.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0", ResponderURL: "http://someUrl"}}, true}, + {iso15118.GetCertificateStatusRequest{}, false}, + {iso15118.GetCertificateStatusRequest{OcspRequestData: types.OCSPRequestDataType{HashAlgorithm: "invalidHashAlgorithm", IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0", ResponderURL: "http://someUrl"}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -22,11 +23,11 @@ func (suite *OcppV2TestSuite) TestGetCertificateStatusRequestValidation() { func (suite *OcppV2TestSuite) TestGetCertificateStatusConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.GetCertificateStatusConfirmation{Status: ocpp2.GenericStatusAccepted, OcspResult: "deadbeef"}, true}, - {ocpp2.GetCertificateStatusConfirmation{Status: ocpp2.GenericStatusAccepted}, true}, - {ocpp2.GetCertificateStatusConfirmation{Status: ocpp2.GenericStatusRejected}, true}, - {ocpp2.GetCertificateStatusConfirmation{Status: "invalidGenericStatus"}, false}, - {ocpp2.GetCertificateStatusConfirmation{}, false}, + {iso15118.GetCertificateStatusResponse{Status: types.GenericStatusAccepted, OcspResult: "deadbeef"}, true}, + {iso15118.GetCertificateStatusResponse{Status: types.GenericStatusAccepted}, true}, + {iso15118.GetCertificateStatusResponse{Status: types.GenericStatusRejected}, true}, + {iso15118.GetCertificateStatusResponse{Status: "invalidGenericStatus"}, false}, + {iso15118.GetCertificateStatusResponse{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -36,19 +37,19 @@ func (suite *OcppV2TestSuite) TestGetCertificateStatusE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - ocspData := ocpp2.OCSPRequestDataType{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0", ResponderURL: "http://someUrl"} + ocspData := types.OCSPRequestDataType{HashAlgorithm: types.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0", ResponderURL: "http://someUrl"} ocspResult := "deadbeef" - status := ocpp2.GenericStatusAccepted + status := types.GenericStatusAccepted requestJson := fmt.Sprintf(`[2,"%v","%v",{"ocspRequestData":{"hashAlgorithm":"%v","issuerNameHash":"%v","issuerKeyHash":"%v","serialNumber":"%v","responderURL":"%v"}}]`, - messageId, ocpp2.GetCertificateStatusFeatureName, ocspData.HashAlgorithm, ocspData.IssuerNameHash, ocspData.IssuerKeyHash, ocspData.SerialNumber, ocspData.ResponderURL) + messageId, iso15118.GetCertificateStatusFeatureName, ocspData.HashAlgorithm, ocspData.IssuerNameHash, ocspData.IssuerKeyHash, ocspData.SerialNumber, ocspData.ResponderURL) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v","ocspResult":"%v"}]`, messageId, status, ocspResult) - getCertificateStatusConfirmation := ocpp2.NewGetCertificateStatusConfirmation(status) + getCertificateStatusConfirmation := iso15118.NewGetCertificateStatusResponse(status) getCertificateStatusConfirmation.OcspResult = ocspResult channel := NewMockWebSocket(wsId) - coreListener := MockCentralSystemCoreListener{} - coreListener.On("OnGetCertificateStatus", mock.AnythingOfType("string"), mock.Anything).Return(getCertificateStatusConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(1).(*ocpp2.GetCertificateStatusRequest) + handler := MockCSMSIso15118Handler{} + handler.On("OnGetCertificateStatus", mock.AnythingOfType("string"), mock.Anything).Return(getCertificateStatusConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(1).(*iso15118.GetCertificateStatusRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, ocspData.HashAlgorithm, request.OcspRequestData.HashAlgorithm) @@ -57,13 +58,13 @@ func (suite *OcppV2TestSuite) TestGetCertificateStatusE2EMocked() { assert.Equal(t, ocspData.SerialNumber, request.OcspRequestData.SerialNumber) assert.Equal(t, ocspData.ResponderURL, request.OcspRequestData.ResponderURL) }) - setupDefaultCentralSystemHandlers(suite, coreListener, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) - confirmation, err := suite.chargePoint.GetCertificateStatus(ocspData) + confirmation, err := suite.chargingStation.GetCertificateStatus(ocspData) require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -72,9 +73,9 @@ func (suite *OcppV2TestSuite) TestGetCertificateStatusE2EMocked() { func (suite *OcppV2TestSuite) TestGetCertificateStatusInvalidEndpoint() { messageId := defaultMessageId - ocspData := ocpp2.OCSPRequestDataType{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0", ResponderURL: "http://someUrl"} - getCertificateStatusRequest := ocpp2.NewGetCertificateStatusRequest(ocspData) + ocspData := types.OCSPRequestDataType{HashAlgorithm: types.SHA256, IssuerNameHash: "hash00", IssuerKeyHash: "hash01", SerialNumber: "serial0", ResponderURL: "http://someUrl"} + getCertificateStatusRequest := iso15118.NewGetCertificateStatusRequest(ocspData) requestJson := fmt.Sprintf(`[2,"%v","%v",{"ocspRequestData":{"hashAlgorithm":"%v","issuerNameHash":"%v","issuerKeyHash":"%v","serialNumber":"%v","responderURL":"%v"}}]`, - messageId, ocpp2.GetCertificateStatusFeatureName, ocspData.HashAlgorithm, ocspData.IssuerNameHash, ocspData.IssuerKeyHash, ocspData.SerialNumber, ocspData.ResponderURL) + messageId, iso15118.GetCertificateStatusFeatureName, ocspData.HashAlgorithm, ocspData.IssuerNameHash, ocspData.IssuerKeyHash, ocspData.SerialNumber, ocspData.ResponderURL) testUnsupportedRequestFromCentralSystem(suite, getCertificateStatusRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/get_charging_profiles_test.go b/ocpp2.0_test/get_charging_profiles_test.go index 9d9f888f..f308033c 100644 --- a/ocpp2.0_test/get_charging_profiles_test.go +++ b/ocpp2.0_test/get_charging_profiles_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -11,26 +12,26 @@ import ( // Test func (suite *OcppV2TestSuite) TestGetChargingProfilesRequestValidation() { t := suite.T() - validChargingProfileCriterion := ocpp2.ChargingProfileCriterion{ - ChargingProfilePurpose: ocpp2.ChargingProfilePurposeTxDefaultProfile, + validChargingProfileCriterion := smartcharging.ChargingProfileCriterion{ + ChargingProfilePurpose: types.ChargingProfilePurposeTxDefaultProfile, StackLevel: newInt(2), - ChargingProfileID: []int{1,2}, - ChargingLimitSource: []ocpp2.ChargingLimitSourceType{ocpp2.ChargingLimitSourceEMS}, + ChargingProfileID: []int{1, 2}, + ChargingLimitSource: []types.ChargingLimitSourceType{types.ChargingLimitSourceEMS}, } var requestTable = []GenericTestEntry{ - {ocpp2.GetChargingProfilesRequest{RequestID: newInt(42), EvseID: newInt(1), ChargingProfile: validChargingProfileCriterion}, true}, - {ocpp2.GetChargingProfilesRequest{RequestID: newInt(42), ChargingProfile: validChargingProfileCriterion}, true}, - {ocpp2.GetChargingProfilesRequest{EvseID: newInt(1), ChargingProfile: validChargingProfileCriterion}, true}, - {ocpp2.GetChargingProfilesRequest{ChargingProfile: validChargingProfileCriterion}, true}, - {ocpp2.GetChargingProfilesRequest{ChargingProfile: ocpp2.ChargingProfileCriterion{}}, true}, - {ocpp2.GetChargingProfilesRequest{}, true}, - {ocpp2.GetChargingProfilesRequest{RequestID: newInt(42), EvseID: newInt(-1), ChargingProfile: validChargingProfileCriterion}, false}, - {ocpp2.GetChargingProfilesRequest{RequestID: newInt(-1), EvseID: newInt(1), ChargingProfile: validChargingProfileCriterion}, false}, - {ocpp2.GetChargingProfilesRequest{ChargingProfile: ocpp2.ChargingProfileCriterion{ChargingProfilePurpose: "invalidChargingProfilePurpose", StackLevel: newInt(2), ChargingProfileID: []int{1,2}, ChargingLimitSource:[]ocpp2.ChargingLimitSourceType{ocpp2.ChargingLimitSourceEMS}}}, false}, - {ocpp2.GetChargingProfilesRequest{ChargingProfile: ocpp2.ChargingProfileCriterion{ChargingProfilePurpose: ocpp2.ChargingProfilePurposeTxDefaultProfile, StackLevel: newInt(-1), ChargingProfileID: []int{1,2}, ChargingLimitSource:[]ocpp2.ChargingLimitSourceType{ocpp2.ChargingLimitSourceEMS}}}, false}, - {ocpp2.GetChargingProfilesRequest{ChargingProfile: ocpp2.ChargingProfileCriterion{ChargingProfilePurpose: ocpp2.ChargingProfilePurposeTxDefaultProfile, StackLevel: newInt(2), ChargingProfileID: []int{1,2}, ChargingLimitSource:[]ocpp2.ChargingLimitSourceType{ocpp2.ChargingLimitSourceEMS, ocpp2.ChargingLimitSourceCSO, ocpp2.ChargingLimitSourceSO, ocpp2.ChargingLimitSourceOther, ocpp2.ChargingLimitSourceEMS}}}, false}, - {ocpp2.GetChargingProfilesRequest{ChargingProfile: ocpp2.ChargingProfileCriterion{ChargingProfilePurpose: ocpp2.ChargingProfilePurposeTxDefaultProfile, StackLevel: newInt(2), ChargingProfileID: []int{1,2}, ChargingLimitSource:[]ocpp2.ChargingLimitSourceType{"invalidChargingLimitSource"}}}, false}, - {ocpp2.GetChargingProfilesRequest{ChargingProfile: ocpp2.ChargingProfileCriterion{ChargingProfilePurpose: ocpp2.ChargingProfilePurposeTxDefaultProfile, StackLevel: newInt(2), ChargingProfileID: []int{-1}, ChargingLimitSource:[]ocpp2.ChargingLimitSourceType{ocpp2.ChargingLimitSourceEMS}}}, false}, + {smartcharging.GetChargingProfilesRequest{RequestID: newInt(42), EvseID: newInt(1), ChargingProfile: validChargingProfileCriterion}, true}, + {smartcharging.GetChargingProfilesRequest{RequestID: newInt(42), ChargingProfile: validChargingProfileCriterion}, true}, + {smartcharging.GetChargingProfilesRequest{EvseID: newInt(1), ChargingProfile: validChargingProfileCriterion}, true}, + {smartcharging.GetChargingProfilesRequest{ChargingProfile: validChargingProfileCriterion}, true}, + {smartcharging.GetChargingProfilesRequest{ChargingProfile: smartcharging.ChargingProfileCriterion{}}, true}, + {smartcharging.GetChargingProfilesRequest{}, true}, + {smartcharging.GetChargingProfilesRequest{RequestID: newInt(42), EvseID: newInt(-1), ChargingProfile: validChargingProfileCriterion}, false}, + {smartcharging.GetChargingProfilesRequest{RequestID: newInt(-1), EvseID: newInt(1), ChargingProfile: validChargingProfileCriterion}, false}, + {smartcharging.GetChargingProfilesRequest{ChargingProfile: smartcharging.ChargingProfileCriterion{ChargingProfilePurpose: "invalidChargingProfilePurpose", StackLevel: newInt(2), ChargingProfileID: []int{1, 2}, ChargingLimitSource: []types.ChargingLimitSourceType{types.ChargingLimitSourceEMS}}}, false}, + {smartcharging.GetChargingProfilesRequest{ChargingProfile: smartcharging.ChargingProfileCriterion{ChargingProfilePurpose: types.ChargingProfilePurposeTxDefaultProfile, StackLevel: newInt(-1), ChargingProfileID: []int{1, 2}, ChargingLimitSource: []types.ChargingLimitSourceType{types.ChargingLimitSourceEMS}}}, false}, + {smartcharging.GetChargingProfilesRequest{ChargingProfile: smartcharging.ChargingProfileCriterion{ChargingProfilePurpose: types.ChargingProfilePurposeTxDefaultProfile, StackLevel: newInt(2), ChargingProfileID: []int{1, 2}, ChargingLimitSource: []types.ChargingLimitSourceType{types.ChargingLimitSourceEMS, types.ChargingLimitSourceCSO, types.ChargingLimitSourceSO, types.ChargingLimitSourceOther, types.ChargingLimitSourceEMS}}}, false}, + {smartcharging.GetChargingProfilesRequest{ChargingProfile: smartcharging.ChargingProfileCriterion{ChargingProfilePurpose: types.ChargingProfilePurposeTxDefaultProfile, StackLevel: newInt(2), ChargingProfileID: []int{1, 2}, ChargingLimitSource: []types.ChargingLimitSourceType{"invalidChargingLimitSource"}}}, false}, + {smartcharging.GetChargingProfilesRequest{ChargingProfile: smartcharging.ChargingProfileCriterion{ChargingProfilePurpose: types.ChargingProfilePurposeTxDefaultProfile, StackLevel: newInt(2), ChargingProfileID: []int{-1}, ChargingLimitSource: []types.ChargingLimitSourceType{types.ChargingLimitSourceEMS}}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -38,10 +39,10 @@ func (suite *OcppV2TestSuite) TestGetChargingProfilesRequestValidation() { func (suite *OcppV2TestSuite) TestGetChargingProfilesConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.GetChargingProfilesConfirmation{Status: ocpp2.GetChargingProfileStatusAccepted}, true}, - {ocpp2.GetChargingProfilesConfirmation{Status: ocpp2.GetChargingProfileStatusNoProfiles}, true}, - {ocpp2.GetChargingProfilesConfirmation{Status: "invalidGetChargingProfilesStatus"}, false}, - {ocpp2.GetChargingProfilesConfirmation{}, false}, + {smartcharging.GetChargingProfilesResponse{Status: smartcharging.GetChargingProfileStatusAccepted}, true}, + {smartcharging.GetChargingProfilesResponse{Status: smartcharging.GetChargingProfileStatusNoProfiles}, true}, + {smartcharging.GetChargingProfilesResponse{Status: "invalidGetChargingProfilesStatus"}, false}, + {smartcharging.GetChargingProfilesResponse{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -53,23 +54,23 @@ func (suite *OcppV2TestSuite) TestGetChargingProfilesE2EMocked() { wsUrl := "someUrl" requestID := 42 evseID := 1 - chargingProfileCriterion := ocpp2.ChargingProfileCriterion{ - ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, + chargingProfileCriterion := smartcharging.ChargingProfileCriterion{ + ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, StackLevel: newInt(1), - ChargingProfileID: []int{1,2}, - ChargingLimitSource: []ocpp2.ChargingLimitSourceType{ocpp2.ChargingLimitSourceEMS}, + ChargingProfileID: []int{1, 2}, + ChargingLimitSource: []types.ChargingLimitSourceType{types.ChargingLimitSourceEMS}, } - status := ocpp2.GetChargingProfileStatusAccepted + status := smartcharging.GetChargingProfileStatusAccepted requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"evseId":%v,"chargingProfile":{"chargingProfilePurpose":"%v","stackLevel":%v,"chargingProfileId":[%v,%v],"chargingLimitSource":["%v"]}}]`, - messageId, ocpp2.GetChargingProfilesFeatureName, requestID, evseID, chargingProfileCriterion.ChargingProfilePurpose, *chargingProfileCriterion.StackLevel, chargingProfileCriterion.ChargingProfileID[0], chargingProfileCriterion.ChargingProfileID[1], chargingProfileCriterion.ChargingLimitSource[0]) + messageId, smartcharging.GetChargingProfilesFeatureName, requestID, evseID, chargingProfileCriterion.ChargingProfilePurpose, *chargingProfileCriterion.StackLevel, chargingProfileCriterion.ChargingProfileID[0], chargingProfileCriterion.ChargingProfileID[1], chargingProfileCriterion.ChargingLimitSource[0]) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - getChargingProfilesConfirmation := ocpp2.NewGetChargingProfilesConfirmation(status) + getChargingProfilesConfirmation := smartcharging.NewGetChargingProfilesResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnGetChargingProfiles", mock.Anything).Return(getChargingProfilesConfirmation, nil).Run(func(args mock.Arguments) { + handler := MockChargingStationSmartChargingHandler{} + handler.On("OnGetChargingProfiles", mock.Anything).Return(getChargingProfilesConfirmation, nil).Run(func(args mock.Arguments) { // Assert request message contents - request, ok := args.Get(0).(*ocpp2.GetChargingProfilesRequest) + request, ok := args.Get(0).(*smartcharging.GetChargingProfilesRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, requestID, *request.RequestID) @@ -82,25 +83,25 @@ func (suite *OcppV2TestSuite) TestGetChargingProfilesE2EMocked() { require.Len(t, request.ChargingProfile.ChargingLimitSource, len(chargingProfileCriterion.ChargingLimitSource)) assert.Equal(t, chargingProfileCriterion.ChargingLimitSource[0], request.ChargingProfile.ChargingLimitSource[0]) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.GetChargingProfiles(wsId, func(confirmation *ocpp2.GetChargingProfilesConfirmation, err error) { + err = suite.csms.GetChargingProfiles(wsId, func(confirmation *smartcharging.GetChargingProfilesResponse, err error) { // Assert confirmation message contents require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) resultChannel <- true }, - chargingProfileCriterion, - func(request *ocpp2.GetChargingProfilesRequest) { - request.EvseID = &evseID - request.RequestID = &requestID - }) + chargingProfileCriterion, + func(request *smartcharging.GetChargingProfilesRequest) { + request.EvseID = &evseID + request.RequestID = &requestID + }) require.Nil(t, err) result := <-resultChannel assert.True(t, result) @@ -110,16 +111,16 @@ func (suite *OcppV2TestSuite) TestGetChargingProfilesInvalidEndpoint() { messageId := defaultMessageId requestID := 42 evseID := 1 - chargingProfileCriterion := ocpp2.ChargingProfileCriterion{ - ChargingProfilePurpose: ocpp2.ChargingProfilePurposeChargingStationMaxProfile, + chargingProfileCriterion := smartcharging.ChargingProfileCriterion{ + ChargingProfilePurpose: types.ChargingProfilePurposeChargingStationMaxProfile, StackLevel: newInt(1), - ChargingProfileID: []int{1,2}, - ChargingLimitSource: []ocpp2.ChargingLimitSourceType{ocpp2.ChargingLimitSourceEMS}, + ChargingProfileID: []int{1, 2}, + ChargingLimitSource: []types.ChargingLimitSourceType{types.ChargingLimitSourceEMS}, } requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"evseId":%v,"chargingProfile":{"chargingProfilePurpose":"%v","stackLevel":%v,"chargingProfileId":[%v,%v],"chargingLimitSource":["%v"]}}]`, - messageId, ocpp2.GetChargingProfilesFeatureName, requestID, evseID, chargingProfileCriterion.ChargingProfilePurpose, *chargingProfileCriterion.StackLevel, chargingProfileCriterion.ChargingProfileID[0], chargingProfileCriterion.ChargingProfileID[1], chargingProfileCriterion.ChargingLimitSource[0]) - getChargingProfilesRequest := ocpp2.NewGetChargingProfilesRequest(chargingProfileCriterion) + messageId, smartcharging.GetChargingProfilesFeatureName, requestID, evseID, chargingProfileCriterion.ChargingProfilePurpose, *chargingProfileCriterion.StackLevel, chargingProfileCriterion.ChargingProfileID[0], chargingProfileCriterion.ChargingProfileID[1], chargingProfileCriterion.ChargingLimitSource[0]) + getChargingProfilesRequest := smartcharging.NewGetChargingProfilesRequest(chargingProfileCriterion) getChargingProfilesRequest.EvseID = &evseID getChargingProfilesRequest.RequestID = &requestID - testUnsupportedRequestFromChargePoint(suite, getChargingProfilesRequest, requestJson, messageId) + testUnsupportedRequestFromChargingStation(suite, getChargingProfilesRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/get_composite_schedule_test.go b/ocpp2.0_test/get_composite_schedule_test.go index d48c3c0a..e48120d7 100644 --- a/ocpp2.0_test/get_composite_schedule_test.go +++ b/ocpp2.0_test/get_composite_schedule_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -13,36 +14,36 @@ import ( func (suite *OcppV2TestSuite) TestGetCompositeScheduleRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.GetCompositeScheduleRequest{Duration: 600, EvseID: 1, ChargingRateUnit: ocpp2.ChargingRateUnitWatts}, true}, - {ocpp2.GetCompositeScheduleRequest{Duration: 600, EvseID: 1}, true}, - {ocpp2.GetCompositeScheduleRequest{EvseID: 1}, true}, - {ocpp2.GetCompositeScheduleRequest{}, true}, - {ocpp2.GetCompositeScheduleRequest{Duration: 600, EvseID: -1, ChargingRateUnit: ocpp2.ChargingRateUnitWatts}, false}, - {ocpp2.GetCompositeScheduleRequest{Duration: -1, EvseID: 1, ChargingRateUnit: ocpp2.ChargingRateUnitWatts}, false}, - {ocpp2.GetCompositeScheduleRequest{Duration: 600, EvseID: 1, ChargingRateUnit: "invalidChargingRateUnit"}, false}, + {smartcharging.GetCompositeScheduleRequest{Duration: 600, EvseID: 1, ChargingRateUnit: types.ChargingRateUnitWatts}, true}, + {smartcharging.GetCompositeScheduleRequest{Duration: 600, EvseID: 1}, true}, + {smartcharging.GetCompositeScheduleRequest{EvseID: 1}, true}, + {smartcharging.GetCompositeScheduleRequest{}, true}, + {smartcharging.GetCompositeScheduleRequest{Duration: 600, EvseID: -1, ChargingRateUnit: types.ChargingRateUnitWatts}, false}, + {smartcharging.GetCompositeScheduleRequest{Duration: -1, EvseID: 1, ChargingRateUnit: types.ChargingRateUnitWatts}, false}, + {smartcharging.GetCompositeScheduleRequest{Duration: 600, EvseID: 1, ChargingRateUnit: "invalidChargingRateUnit"}, false}, } ExecuteGenericTestTable(t, requestTable) } func (suite *OcppV2TestSuite) TestGetCompositeScheduleConfirmationValidation() { t := suite.T() - chargingSchedule := ocpp2.NewChargingSchedule(ocpp2.ChargingRateUnitWatts, ocpp2.NewChargingSchedulePeriod(0, 10.0)) + chargingSchedule := types.NewChargingSchedule(types.ChargingRateUnitWatts, types.NewChargingSchedulePeriod(0, 10.0)) chargingSchedule.Duration = newInt(600) chargingSchedule.MinChargingRate = newFloat(6.0) - chargingSchedule.StartSchedule = ocpp2.NewDateTime(time.Now()) - compositeSchedule := ocpp2.CompositeSchedule{ - StartDateTime: ocpp2.NewDateTime(time.Now()), + chargingSchedule.StartSchedule = types.NewDateTime(time.Now()) + compositeSchedule := smartcharging.CompositeSchedule{ + StartDateTime: types.NewDateTime(time.Now()), ChargingSchedule: chargingSchedule, } var confirmationTable = []GenericTestEntry{ - {ocpp2.GetCompositeScheduleConfirmation{Status: ocpp2.GetCompositeScheduleStatusAccepted, EvseID: 1, Schedule: &compositeSchedule}, true}, - {ocpp2.GetCompositeScheduleConfirmation{Status: ocpp2.GetCompositeScheduleStatusAccepted, EvseID: 1, Schedule: &ocpp2.CompositeSchedule{}}, true}, - {ocpp2.GetCompositeScheduleConfirmation{Status: ocpp2.GetCompositeScheduleStatusAccepted, EvseID: 1}, true}, - {ocpp2.GetCompositeScheduleConfirmation{Status: ocpp2.GetCompositeScheduleStatusAccepted}, true}, - {ocpp2.GetCompositeScheduleConfirmation{}, false}, - {ocpp2.GetCompositeScheduleConfirmation{Status: "invalidGetCompositeScheduleStatus"}, false}, - {ocpp2.GetCompositeScheduleConfirmation{Status: ocpp2.GetCompositeScheduleStatusAccepted, EvseID: -1}, false}, - {ocpp2.GetCompositeScheduleConfirmation{Status: ocpp2.GetCompositeScheduleStatusAccepted, EvseID: 1, Schedule: &ocpp2.CompositeSchedule{StartDateTime: ocpp2.NewDateTime(time.Now()), ChargingSchedule: ocpp2.NewChargingSchedule("invalidChargingRateUnit")}}, false}, + {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, EvseID: 1, Schedule: &compositeSchedule}, true}, + {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, EvseID: 1, Schedule: &smartcharging.CompositeSchedule{}}, true}, + {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, EvseID: 1}, true}, + {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted}, true}, + {smartcharging.GetCompositeScheduleResponse{}, false}, + {smartcharging.GetCompositeScheduleResponse{Status: "invalidGetCompositeScheduleStatus"}, false}, + {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, EvseID: -1}, false}, + {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, EvseID: 1, Schedule: &smartcharging.CompositeSchedule{StartDateTime: types.NewDateTime(time.Now()), ChargingSchedule: types.NewChargingSchedule("invalidChargingRateUnit")}}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -53,49 +54,49 @@ func (suite *OcppV2TestSuite) TestGetCompositeScheduleE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" evseID := 1 - chargingRateUnit := ocpp2.ChargingRateUnitWatts + chargingRateUnit := types.ChargingRateUnitWatts duration := 600 - status := ocpp2.GetCompositeScheduleStatusAccepted - scheduleStart := ocpp2.NewDateTime(time.Now()) - chargingSchedulePeriod := ocpp2.NewChargingSchedulePeriod(0, 10.0) + status := smartcharging.GetCompositeScheduleStatusAccepted + scheduleStart := types.NewDateTime(time.Now()) + chargingSchedulePeriod := types.NewChargingSchedulePeriod(0, 10.0) chargingSchedulePeriod.NumberPhases = newInt(3) - chargingSchedule := ocpp2.NewChargingSchedule(chargingRateUnit, chargingSchedulePeriod) + chargingSchedule := types.NewChargingSchedule(chargingRateUnit, chargingSchedulePeriod) chargingSchedule.Duration = newInt(600) - chargingSchedule.StartSchedule = ocpp2.NewDateTime(time.Now()) + chargingSchedule.StartSchedule = types.NewDateTime(time.Now()) chargingSchedule.MinChargingRate = newFloat(6.0) - compositeSchedule := ocpp2.CompositeSchedule{StartDateTime: scheduleStart, ChargingSchedule: chargingSchedule} + compositeSchedule := smartcharging.CompositeSchedule{StartDateTime: scheduleStart, ChargingSchedule: chargingSchedule} requestJson := fmt.Sprintf(`[2,"%v","%v",{"duration":%v,"chargingRateUnit":"%v","evseId":%v}]`, - messageId, ocpp2.GetCompositeScheduleFeatureName, duration, chargingRateUnit, evseID) + messageId, smartcharging.GetCompositeScheduleFeatureName, duration, chargingRateUnit, evseID) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v","evseId":%v,"schedule":{"startDateTime":"%v","chargingSchedule":{"startSchedule":"%v","duration":%v,"chargingRateUnit":"%v","minChargingRate":%v,"chargingSchedulePeriod":[{"startPeriod":%v,"limit":%v,"numberPhases":%v}]}}}]`, - messageId, status, evseID, ocpp2.FormatTimestamp(compositeSchedule.StartDateTime.Time), ocpp2.FormatTimestamp(chargingSchedule.StartSchedule.Time), *chargingSchedule.Duration, chargingSchedule.ChargingRateUnit, *chargingSchedule.MinChargingRate, chargingSchedulePeriod.StartPeriod, chargingSchedulePeriod.Limit, *chargingSchedulePeriod.NumberPhases) - getCompositeScheduleConfirmation := ocpp2.NewGetCompositeScheduleConfirmation(status, evseID) + messageId, status, evseID, compositeSchedule.StartDateTime.FormatTimestamp(), chargingSchedule.StartSchedule.FormatTimestamp(), *chargingSchedule.Duration, chargingSchedule.ChargingRateUnit, *chargingSchedule.MinChargingRate, chargingSchedulePeriod.StartPeriod, chargingSchedulePeriod.Limit, *chargingSchedulePeriod.NumberPhases) + getCompositeScheduleConfirmation := smartcharging.NewGetCompositeScheduleResponse(status, evseID) getCompositeScheduleConfirmation.Schedule = &compositeSchedule channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnGetCompositeSchedule", mock.Anything).Return(getCompositeScheduleConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.GetCompositeScheduleRequest) + handler := MockChargingStationSmartChargingHandler{} + handler.On("OnGetCompositeSchedule", mock.Anything).Return(getCompositeScheduleConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*smartcharging.GetCompositeScheduleRequest) assert.True(t, ok) assert.NotNil(t, request) assert.Equal(t, duration, request.Duration) assert.Equal(t, chargingRateUnit, request.ChargingRateUnit) assert.Equal(t, evseID, request.EvseID) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.GetCompositeSchedule(wsId, func(confirmation *ocpp2.GetCompositeScheduleConfirmation, err error) { + err = suite.csms.GetCompositeSchedule(wsId, func(confirmation *smartcharging.GetCompositeScheduleResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) assert.Equal(t, evseID, confirmation.EvseID) require.NotNil(t, confirmation.Schedule) require.NotNil(t, confirmation.Schedule.StartDateTime) - assert.Equal(t, ocpp2.FormatTimestamp(compositeSchedule.StartDateTime.Time), ocpp2.FormatTimestamp(confirmation.Schedule.StartDateTime.Time)) + assert.Equal(t, compositeSchedule.StartDateTime.FormatTimestamp(), confirmation.Schedule.StartDateTime.FormatTimestamp()) require.NotNil(t, confirmation.Schedule.ChargingSchedule) assert.Equal(t, chargingSchedule.ChargingRateUnit, confirmation.Schedule.ChargingSchedule.ChargingRateUnit) require.NotNil(t, confirmation.Schedule.ChargingSchedule.Duration) @@ -103,14 +104,14 @@ func (suite *OcppV2TestSuite) TestGetCompositeScheduleE2EMocked() { require.NotNil(t, confirmation.Schedule.ChargingSchedule.MinChargingRate) assert.Equal(t, *chargingSchedule.MinChargingRate, *confirmation.Schedule.ChargingSchedule.MinChargingRate) require.NotNil(t, confirmation.Schedule.ChargingSchedule.StartSchedule) - assert.Equal(t, ocpp2.FormatTimestamp(chargingSchedule.StartSchedule.Time), ocpp2.FormatTimestamp(confirmation.Schedule.ChargingSchedule.StartSchedule.Time)) + assert.Equal(t, chargingSchedule.StartSchedule.FormatTimestamp(), confirmation.Schedule.ChargingSchedule.StartSchedule.FormatTimestamp()) require.Len(t, confirmation.Schedule.ChargingSchedule.ChargingSchedulePeriod, len(chargingSchedule.ChargingSchedulePeriod)) assert.Equal(t, chargingSchedule.ChargingSchedulePeriod[0].Limit, confirmation.Schedule.ChargingSchedule.ChargingSchedulePeriod[0].Limit) assert.Equal(t, chargingSchedule.ChargingSchedulePeriod[0].StartPeriod, confirmation.Schedule.ChargingSchedule.ChargingSchedulePeriod[0].StartPeriod) require.NotNil(t, confirmation.Schedule.ChargingSchedule.ChargingSchedulePeriod[0].NumberPhases) assert.Equal(t, *chargingSchedule.ChargingSchedulePeriod[0].NumberPhases, *confirmation.Schedule.ChargingSchedule.ChargingSchedulePeriod[0].NumberPhases) resultChannel <- true - }, duration, evseID, func(request *ocpp2.GetCompositeScheduleRequest) { + }, duration, evseID, func(request *smartcharging.GetCompositeScheduleRequest) { request.ChargingRateUnit = chargingRateUnit }) assert.Nil(t, err) @@ -121,11 +122,11 @@ func (suite *OcppV2TestSuite) TestGetCompositeScheduleE2EMocked() { func (suite *OcppV2TestSuite) TestGetCompositeScheduleInvalidEndpoint() { messageId := defaultMessageId evseID := 1 - chargingRateUnit := ocpp2.ChargingRateUnitWatts + chargingRateUnit := types.ChargingRateUnitWatts duration := 600 requestJson := fmt.Sprintf(`[2,"%v","%v",{"duration":%v,"chargingRateUnit":"%v","evseId":%v}]`, - messageId, ocpp2.GetCompositeScheduleFeatureName, duration, chargingRateUnit, evseID) - getCompositeScheduleRequest := ocpp2.NewGetCompositeScheduleRequest(evseID, duration) + messageId, smartcharging.GetCompositeScheduleFeatureName, duration, chargingRateUnit, evseID) + getCompositeScheduleRequest := smartcharging.NewGetCompositeScheduleRequest(evseID, duration) getCompositeScheduleRequest.ChargingRateUnit = chargingRateUnit - testUnsupportedRequestFromChargePoint(suite, getCompositeScheduleRequest, requestJson, messageId) + testUnsupportedRequestFromChargingStation(suite, getCompositeScheduleRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/get_display_messages_test.go b/ocpp2.0_test/get_display_messages_test.go index 5e4ebcb6..36c5e67b 100644 --- a/ocpp2.0_test/get_display_messages_test.go +++ b/ocpp2.0_test/get_display_messages_test.go @@ -2,7 +2,7 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/display" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,17 +12,17 @@ import ( func (suite *OcppV2TestSuite) TestGetDisplayMessagesRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.GetDisplayMessagesRequest{RequestID: 1, Priority: ocpp2.MessagePriorityAlwaysFront, State: ocpp2.MessageStateCharging, ID: []int{2, 3}}, true}, - {ocpp2.GetDisplayMessagesRequest{RequestID: 1, Priority: ocpp2.MessagePriorityAlwaysFront, State: ocpp2.MessageStateCharging, ID: []int{}}, true}, - {ocpp2.GetDisplayMessagesRequest{RequestID: 1, Priority: ocpp2.MessagePriorityAlwaysFront, State: ocpp2.MessageStateCharging}, true}, - {ocpp2.GetDisplayMessagesRequest{RequestID: 1, Priority: ocpp2.MessagePriorityAlwaysFront}, true}, - {ocpp2.GetDisplayMessagesRequest{RequestID: 1, State: ocpp2.MessageStateCharging}, true}, - {ocpp2.GetDisplayMessagesRequest{RequestID: 1}, true}, - {ocpp2.GetDisplayMessagesRequest{}, true}, - {ocpp2.GetDisplayMessagesRequest{RequestID: -1}, false}, - {ocpp2.GetDisplayMessagesRequest{RequestID: 1, Priority: "invalidMessagePriority", State: ocpp2.MessageStateCharging, ID: []int{2, 3}}, false}, - {ocpp2.GetDisplayMessagesRequest{RequestID: 1, Priority: ocpp2.MessagePriorityAlwaysFront, State: "invalidMessageState", ID: []int{2, 3}}, false}, - {ocpp2.GetDisplayMessagesRequest{RequestID: 1, Priority: ocpp2.MessagePriorityAlwaysFront, State: ocpp2.MessageStateCharging, ID: []int{-2, 3}}, false}, + {display.GetDisplayMessagesRequest{RequestID: 1, Priority: display.MessagePriorityAlwaysFront, State: display.MessageStateCharging, ID: []int{2, 3}}, true}, + {display.GetDisplayMessagesRequest{RequestID: 1, Priority: display.MessagePriorityAlwaysFront, State: display.MessageStateCharging, ID: []int{}}, true}, + {display.GetDisplayMessagesRequest{RequestID: 1, Priority: display.MessagePriorityAlwaysFront, State: display.MessageStateCharging}, true}, + {display.GetDisplayMessagesRequest{RequestID: 1, Priority: display.MessagePriorityAlwaysFront}, true}, + {display.GetDisplayMessagesRequest{RequestID: 1, State: display.MessageStateCharging}, true}, + {display.GetDisplayMessagesRequest{RequestID: 1}, true}, + {display.GetDisplayMessagesRequest{}, true}, + {display.GetDisplayMessagesRequest{RequestID: -1}, false}, + {display.GetDisplayMessagesRequest{RequestID: 1, Priority: "invalidMessagePriority", State: display.MessageStateCharging, ID: []int{2, 3}}, false}, + {display.GetDisplayMessagesRequest{RequestID: 1, Priority: display.MessagePriorityAlwaysFront, State: "invalidMessageState", ID: []int{2, 3}}, false}, + {display.GetDisplayMessagesRequest{RequestID: 1, Priority: display.MessagePriorityAlwaysFront, State: display.MessageStateCharging, ID: []int{-2, 3}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -30,10 +30,10 @@ func (suite *OcppV2TestSuite) TestGetDisplayMessagesRequestValidation() { func (suite *OcppV2TestSuite) TestGetDisplayMessagesConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.GetDisplayMessagesConfirmation{Status: ocpp2.MessageStatusAccepted}, true}, - {ocpp2.GetDisplayMessagesConfirmation{Status: ocpp2.MessageStatusUnknown}, true}, - {ocpp2.GetDisplayMessagesConfirmation{Status: "invalidMessageStatus"}, false}, - {ocpp2.GetDisplayMessagesConfirmation{}, false}, + {display.GetDisplayMessagesResponse{Status: display.MessageStatusAccepted}, true}, + {display.GetDisplayMessagesResponse{Status: display.MessageStatusUnknown}, true}, + {display.GetDisplayMessagesResponse{Status: "invalidMessageStatus"}, false}, + {display.GetDisplayMessagesResponse{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -44,19 +44,19 @@ func (suite *OcppV2TestSuite) TestGetDisplayMessagesE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" requestId := 42 - messageIds := []int{2,3} - priority := ocpp2.MessagePriorityInFront - state := ocpp2.MessageStateCharging - status := ocpp2.MessageStatusAccepted + messageIds := []int{2, 3} + priority := display.MessagePriorityInFront + state := display.MessageStateCharging + status := display.MessageStatusAccepted requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"priority":"%v","state":"%v","id":[%v,%v]}]`, - messageId, ocpp2.GetDisplayMessagesFeatureName, requestId, priority, state, messageIds[0], messageIds[1]) + messageId, display.GetDisplayMessagesFeatureName, requestId, priority, state, messageIds[0], messageIds[1]) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - getDisplayMessagesConfirmation := ocpp2.NewGetDisplayMessagesConfirmation(status) + getDisplayMessagesConfirmation := display.NewGetDisplayMessagesResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnGetDisplayMessages", mock.Anything).Return(getDisplayMessagesConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.GetDisplayMessagesRequest) + handler := MockChargingStationDisplayHandler{} + handler.On("OnGetDisplayMessages", mock.Anything).Return(getDisplayMessagesConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*display.GetDisplayMessagesRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, requestId, request.RequestID) @@ -66,19 +66,19 @@ func (suite *OcppV2TestSuite) TestGetDisplayMessagesE2EMocked() { assert.Equal(t, messageIds[0], request.ID[0]) assert.Equal(t, messageIds[1], request.ID[1]) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.GetDisplayMessages(wsId, func(confirmation *ocpp2.GetDisplayMessagesConfirmation, err error) { + err = suite.csms.GetDisplayMessages(wsId, func(confirmation *display.GetDisplayMessagesResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) resultChannel <- true - }, requestId, func(request *ocpp2.GetDisplayMessagesRequest) { + }, requestId, func(request *display.GetDisplayMessagesRequest) { request.Priority = priority request.State = state request.ID = messageIds @@ -91,11 +91,11 @@ func (suite *OcppV2TestSuite) TestGetDisplayMessagesE2EMocked() { func (suite *OcppV2TestSuite) TestGetDisplayMessagesInvalidEndpoint() { messageId := defaultMessageId requestId := 42 - messageIds := []int{2,3} - priority := ocpp2.MessagePriorityInFront - state := ocpp2.MessageStateCharging - getDisplayMessagesRequest := ocpp2.NewGetDisplayMessagesRequest(requestId) + messageIds := []int{2, 3} + priority := display.MessagePriorityInFront + state := display.MessageStateCharging + getDisplayMessagesRequest := display.NewGetDisplayMessagesRequest(requestId) requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"priority":"%v","state":"%v","id":[%v,%v]}]`, - messageId, ocpp2.GetDisplayMessagesFeatureName, requestId, priority, state, messageIds[0], messageIds[1]) - testUnsupportedRequestFromChargePoint(suite, getDisplayMessagesRequest, requestJson, messageId) + messageId, display.GetDisplayMessagesFeatureName, requestId, priority, state, messageIds[0], messageIds[1]) + testUnsupportedRequestFromChargingStation(suite, getDisplayMessagesRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/get_installed_certificate_ids_test.go b/ocpp2.0_test/get_installed_certificate_ids_test.go index 2122a7d8..5178f719 100644 --- a/ocpp2.0_test/get_installed_certificate_ids_test.go +++ b/ocpp2.0_test/get_installed_certificate_ids_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/iso15118" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -11,14 +12,14 @@ import ( func (suite *OcppV2TestSuite) TestGetInstalledCertificateIdsRequestValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp2.GetInstalledCertificateIdsRequest{TypeOfCertificate: ocpp2.V2GRootCertificate}, true}, - {ocpp2.GetInstalledCertificateIdsRequest{TypeOfCertificate: ocpp2.MORootCertificate}, true}, - {ocpp2.GetInstalledCertificateIdsRequest{TypeOfCertificate: ocpp2.CSOSubCA1}, true}, - {ocpp2.GetInstalledCertificateIdsRequest{TypeOfCertificate: ocpp2.CSOSubCA2}, true}, - {ocpp2.GetInstalledCertificateIdsRequest{TypeOfCertificate: ocpp2.CSMSRootCertificate}, true}, - {ocpp2.GetInstalledCertificateIdsRequest{TypeOfCertificate: ocpp2.ManufacturerRootCertificate}, true}, - {ocpp2.GetInstalledCertificateIdsRequest{}, false}, - {ocpp2.GetInstalledCertificateIdsRequest{TypeOfCertificate: "invalidCertificateUse"}, false}, + {iso15118.GetInstalledCertificateIdsRequest{TypeOfCertificate: types.V2GRootCertificate}, true}, + {iso15118.GetInstalledCertificateIdsRequest{TypeOfCertificate: types.MORootCertificate}, true}, + {iso15118.GetInstalledCertificateIdsRequest{TypeOfCertificate: types.CSOSubCA1}, true}, + {iso15118.GetInstalledCertificateIdsRequest{TypeOfCertificate: types.CSOSubCA2}, true}, + {iso15118.GetInstalledCertificateIdsRequest{TypeOfCertificate: types.CSMSRootCertificate}, true}, + {iso15118.GetInstalledCertificateIdsRequest{TypeOfCertificate: types.ManufacturerRootCertificate}, true}, + {iso15118.GetInstalledCertificateIdsRequest{}, false}, + {iso15118.GetInstalledCertificateIdsRequest{TypeOfCertificate: "invalidCertificateUse"}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -26,13 +27,13 @@ func (suite *OcppV2TestSuite) TestGetInstalledCertificateIdsRequestValidation() func (suite *OcppV2TestSuite) TestGetInstalledCertificateIdsConfirmationValidation() { t := suite.T() var testTable = []GenericTestEntry{ - {ocpp2.GetInstalledCertificateIdsConfirmation{Status: ocpp2.GetInstalledCertificateStatusAccepted, CertificateHashData: []ocpp2.CertificateHashData{{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "name0", IssuerKeyHash: "key0", SerialNumber: "serial0"}}}, true}, - {ocpp2.GetInstalledCertificateIdsConfirmation{Status: ocpp2.GetInstalledCertificateStatusNotFound, CertificateHashData: []ocpp2.CertificateHashData{{HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "name0", IssuerKeyHash: "key0", SerialNumber: "serial0"}}}, true}, - {ocpp2.GetInstalledCertificateIdsConfirmation{Status: ocpp2.GetInstalledCertificateStatusAccepted, CertificateHashData: []ocpp2.CertificateHashData{}}, true}, - {ocpp2.GetInstalledCertificateIdsConfirmation{Status: ocpp2.GetInstalledCertificateStatusAccepted}, true}, - {ocpp2.GetInstalledCertificateIdsConfirmation{}, false}, - {ocpp2.GetInstalledCertificateIdsConfirmation{Status: "invalidGetInstalledCertificateStatus"}, false}, - {ocpp2.GetInstalledCertificateIdsConfirmation{Status: ocpp2.GetInstalledCertificateStatusAccepted, CertificateHashData: []ocpp2.CertificateHashData{{HashAlgorithm: "invalidHashAlgorithm", IssuerNameHash: "name0", IssuerKeyHash: "key0", SerialNumber: "serial0"}}}, false}, + {iso15118.GetInstalledCertificateIdsResponse{Status: iso15118.GetInstalledCertificateStatusAccepted, CertificateHashData: []types.CertificateHashData{{HashAlgorithm: types.SHA256, IssuerNameHash: "name0", IssuerKeyHash: "key0", SerialNumber: "serial0"}}}, true}, + {iso15118.GetInstalledCertificateIdsResponse{Status: iso15118.GetInstalledCertificateStatusNotFound, CertificateHashData: []types.CertificateHashData{{HashAlgorithm: types.SHA256, IssuerNameHash: "name0", IssuerKeyHash: "key0", SerialNumber: "serial0"}}}, true}, + {iso15118.GetInstalledCertificateIdsResponse{Status: iso15118.GetInstalledCertificateStatusAccepted, CertificateHashData: []types.CertificateHashData{}}, true}, + {iso15118.GetInstalledCertificateIdsResponse{Status: iso15118.GetInstalledCertificateStatusAccepted}, true}, + {iso15118.GetInstalledCertificateIdsResponse{}, false}, + {iso15118.GetInstalledCertificateIdsResponse{Status: "invalidGetInstalledCertificateStatus"}, false}, + {iso15118.GetInstalledCertificateIdsResponse{Status: iso15118.GetInstalledCertificateStatusAccepted, CertificateHashData: []types.CertificateHashData{{HashAlgorithm: "invalidHashAlgorithm", IssuerNameHash: "name0", IssuerKeyHash: "key0", SerialNumber: "serial0"}}}, false}, } ExecuteGenericTestTable(t, testTable) } @@ -43,33 +44,33 @@ func (suite *OcppV2TestSuite) TestGetInstalledCertificateIdsE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - certificateType := ocpp2.CSMSRootCertificate - status := ocpp2.GetInstalledCertificateStatusAccepted - certificateHashData := []ocpp2.CertificateHashData{ - { HashAlgorithm: ocpp2.SHA256, IssuerNameHash: "name0", IssuerKeyHash: "key0", SerialNumber: "serial0" }, + certificateType := types.CSMSRootCertificate + status := iso15118.GetInstalledCertificateStatusAccepted + certificateHashData := []types.CertificateHashData{ + {HashAlgorithm: types.SHA256, IssuerNameHash: "name0", IssuerKeyHash: "key0", SerialNumber: "serial0"}, } - requestJson := fmt.Sprintf(`[2,"%v","%v",{"typeOfCertificate":"%v"}]`, messageId, ocpp2.GetInstalledCertificateIdsFeatureName, certificateType) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"typeOfCertificate":"%v"}]`, messageId, iso15118.GetInstalledCertificateIdsFeatureName, certificateType) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v","certificateHashData":[{"hashAlgorithm":"%v","issuerNameHash":"%v","issuerKeyHash":"%v","serialNumber":"%v"}]}]`, messageId, status, certificateHashData[0].HashAlgorithm, certificateHashData[0].IssuerNameHash, certificateHashData[0].IssuerKeyHash, certificateHashData[0].SerialNumber) - getInstalledCertificateIdsConfirmation := ocpp2.NewGetInstalledCertificateIdsConfirmation(status) + getInstalledCertificateIdsConfirmation := iso15118.NewGetInstalledCertificateIdsResponse(status) getInstalledCertificateIdsConfirmation.CertificateHashData = certificateHashData channel := NewMockWebSocket(wsId) // Setting handlers - coreListener := MockChargePointCoreListener{} - coreListener.On("OnGetInstalledCertificateIds", mock.Anything).Return(getInstalledCertificateIdsConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.GetInstalledCertificateIdsRequest) + handler := MockChargingStationIso15118Handler{} + handler.On("OnGetInstalledCertificateIds", mock.Anything).Return(getInstalledCertificateIdsConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*iso15118.GetInstalledCertificateIdsRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, certificateType, request.TypeOfCertificate) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.GetInstalledCertificateIds(wsId, func(confirmation *ocpp2.GetInstalledCertificateIdsConfirmation, err error) { + err = suite.csms.GetInstalledCertificateIds(wsId, func(confirmation *iso15118.GetInstalledCertificateIdsResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) @@ -87,8 +88,8 @@ func (suite *OcppV2TestSuite) TestGetInstalledCertificateIdsE2EMocked() { func (suite *OcppV2TestSuite) TestGetInstalledCertificateIdsInvalidEndpoint() { messageId := defaultMessageId - certificateType := ocpp2.CSMSRootCertificate - GetInstalledCertificateIdsRequest := ocpp2.NewGetInstalledCertificateIdsRequest(certificateType) - requestJson := fmt.Sprintf(`[2,"%v","%v",{"typeOfCertificate":"%v"}]`, messageId, ocpp2.GetInstalledCertificateIdsFeatureName, certificateType) - testUnsupportedRequestFromChargePoint(suite, GetInstalledCertificateIdsRequest, requestJson, messageId) + certificateType := types.CSMSRootCertificate + GetInstalledCertificateIdsRequest := iso15118.NewGetInstalledCertificateIdsRequest(certificateType) + requestJson := fmt.Sprintf(`[2,"%v","%v",{"typeOfCertificate":"%v"}]`, messageId, iso15118.GetInstalledCertificateIdsFeatureName, certificateType) + testUnsupportedRequestFromChargingStation(suite, GetInstalledCertificateIdsRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/get_local_list_version_test.go b/ocpp2.0_test/get_local_list_version_test.go index 908eaf71..9b8138f2 100644 --- a/ocpp2.0_test/get_local_list_version_test.go +++ b/ocpp2.0_test/get_local_list_version_test.go @@ -2,7 +2,7 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/localauth" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,7 +12,7 @@ import ( func (suite *OcppV2TestSuite) TestGetLocalListVersionRequestValidation() { t := suite.T() var requestTable = []GenericTestEntry{ - {ocpp2.GetLocalListVersionRequest{}, true}, + {localauth.GetLocalListVersionRequest{}, true}, } ExecuteGenericTestTable(t, requestTable) } @@ -20,10 +20,10 @@ func (suite *OcppV2TestSuite) TestGetLocalListVersionRequestValidation() { func (suite *OcppV2TestSuite) TestGetLocalListVersionConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.GetLocalListVersionConfirmation{VersionNumber: 1}, true}, - {ocpp2.GetLocalListVersionConfirmation{VersionNumber: 0}, true}, - {ocpp2.GetLocalListVersionConfirmation{}, true}, - {ocpp2.GetLocalListVersionConfirmation{VersionNumber: -1}, false}, + {localauth.GetLocalListVersionResponse{VersionNumber: 1}, true}, + {localauth.GetLocalListVersionResponse{VersionNumber: 0}, true}, + {localauth.GetLocalListVersionResponse{}, true}, + {localauth.GetLocalListVersionResponse{VersionNumber: -1}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -34,21 +34,21 @@ func (suite *OcppV2TestSuite) TestGetLocalListVersionE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" listVersion := 1 - requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, ocpp2.GetLocalListVersionFeatureName) + requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, localauth.GetLocalListVersionFeatureName) responseJson := fmt.Sprintf(`[3,"%v",{"versionNumber":%v}]`, messageId, listVersion) - localListVersionConfirmation := ocpp2.NewGetLocalListVersionConfirmation(listVersion) + localListVersionConfirmation := localauth.NewGetLocalListVersionResponse(listVersion) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnGetLocalListVersion", mock.Anything).Return(localListVersionConfirmation, nil) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + handler := MockChargingStationLocalAuthHandler{} + handler.On("OnGetLocalListVersion", mock.Anything).Return(localListVersionConfirmation, nil) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) assert.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.GetLocalListVersion(wsId, func(confirmation *ocpp2.GetLocalListVersionConfirmation, err error) { + err = suite.csms.GetLocalListVersion(wsId, func(confirmation *localauth.GetLocalListVersionResponse, err error) { assert.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, listVersion, confirmation.VersionNumber) @@ -63,7 +63,7 @@ func (suite *OcppV2TestSuite) TestGetLocalListVersionE2EMocked() { func (suite *OcppV2TestSuite) TestGetLocalListVersionInvalidEndpoint() { messageId := defaultMessageId - localListVersionRequest := ocpp2.NewGetLocalListVersionRequest() - requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, ocpp2.GetLocalListVersionFeatureName) - testUnsupportedRequestFromChargePoint(suite, localListVersionRequest, requestJson, messageId) + localListVersionRequest := localauth.NewGetLocalListVersionRequest() + requestJson := fmt.Sprintf(`[2,"%v","%v",{}]`, messageId, localauth.GetLocalListVersionFeatureName) + testUnsupportedRequestFromChargingStation(suite, localListVersionRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/get_log_test.go b/ocpp2.0_test/get_log_test.go index 7102dcf9..1c48dfab 100644 --- a/ocpp2.0_test/get_log_test.go +++ b/ocpp2.0_test/get_log_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/diagnostics" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -12,24 +13,24 @@ import ( // Test func (suite *OcppV2TestSuite) TestGetLogRequestValidation() { t := suite.T() - logParameters := ocpp2.LogParameters{ + logParameters := diagnostics.LogParameters{ RemoteLocation: "ftp://someurl/diagnostics/1", - OldestTimestamp: ocpp2.NewDateTime(time.Now().Add(-2 * time.Hour)), - LatestTimestamp: ocpp2.NewDateTime(time.Now()), + OldestTimestamp: types.NewDateTime(time.Now().Add(-2 * time.Hour)), + LatestTimestamp: types.NewDateTime(time.Now()), } var requestTable = []GenericTestEntry{ - {ocpp2.GetLogRequest{LogType: ocpp2.LogTypeDiagnostics, RequestID: 1, Retries: newInt(5), RetryInterval: newInt(120), Log: logParameters}, true}, - {ocpp2.GetLogRequest{LogType: ocpp2.LogTypeDiagnostics, RequestID: 1, Retries: newInt(5), Log: logParameters}, true}, - {ocpp2.GetLogRequest{LogType: ocpp2.LogTypeDiagnostics, RequestID: 1, Log: logParameters}, true}, - {ocpp2.GetLogRequest{LogType: ocpp2.LogTypeDiagnostics, Log: logParameters}, true}, - {ocpp2.GetLogRequest{LogType: ocpp2.LogTypeDiagnostics}, false}, - {ocpp2.GetLogRequest{Log: logParameters}, false}, - {ocpp2.GetLogRequest{}, false}, - {ocpp2.GetLogRequest{LogType: "invalidLogType", RequestID: 1, Retries: newInt(5), RetryInterval: newInt(120), Log: logParameters}, false}, - {ocpp2.GetLogRequest{LogType: ocpp2.LogTypeDiagnostics, RequestID: -1, Retries: newInt(5), RetryInterval: newInt(120), Log: logParameters}, false}, - {ocpp2.GetLogRequest{LogType: ocpp2.LogTypeDiagnostics, RequestID: 1, Retries: newInt(-1), RetryInterval: newInt(120), Log: logParameters}, false}, - {ocpp2.GetLogRequest{LogType: ocpp2.LogTypeDiagnostics, RequestID: 1, Retries: newInt(5), RetryInterval: newInt(-1), Log: logParameters}, false}, - {ocpp2.GetLogRequest{LogType: ocpp2.LogTypeDiagnostics, RequestID: 1, Retries: newInt(5), RetryInterval: newInt(120), Log: ocpp2.LogParameters{RemoteLocation: ".invalidUrl.", OldestTimestamp: nil, LatestTimestamp: nil}}, false}, + {diagnostics.GetLogRequest{LogType: diagnostics.LogTypeDiagnostics, RequestID: 1, Retries: newInt(5), RetryInterval: newInt(120), Log: logParameters}, true}, + {diagnostics.GetLogRequest{LogType: diagnostics.LogTypeDiagnostics, RequestID: 1, Retries: newInt(5), Log: logParameters}, true}, + {diagnostics.GetLogRequest{LogType: diagnostics.LogTypeDiagnostics, RequestID: 1, Log: logParameters}, true}, + {diagnostics.GetLogRequest{LogType: diagnostics.LogTypeDiagnostics, Log: logParameters}, true}, + {diagnostics.GetLogRequest{LogType: diagnostics.LogTypeDiagnostics}, false}, + {diagnostics.GetLogRequest{Log: logParameters}, false}, + {diagnostics.GetLogRequest{}, false}, + {diagnostics.GetLogRequest{LogType: "invalidLogType", RequestID: 1, Retries: newInt(5), RetryInterval: newInt(120), Log: logParameters}, false}, + {diagnostics.GetLogRequest{LogType: diagnostics.LogTypeDiagnostics, RequestID: -1, Retries: newInt(5), RetryInterval: newInt(120), Log: logParameters}, false}, + {diagnostics.GetLogRequest{LogType: diagnostics.LogTypeDiagnostics, RequestID: 1, Retries: newInt(-1), RetryInterval: newInt(120), Log: logParameters}, false}, + {diagnostics.GetLogRequest{LogType: diagnostics.LogTypeDiagnostics, RequestID: 1, Retries: newInt(5), RetryInterval: newInt(-1), Log: logParameters}, false}, + {diagnostics.GetLogRequest{LogType: diagnostics.LogTypeDiagnostics, RequestID: 1, Retries: newInt(5), RetryInterval: newInt(120), Log: diagnostics.LogParameters{RemoteLocation: ".invalidUrl.", OldestTimestamp: nil, LatestTimestamp: nil}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -37,13 +38,13 @@ func (suite *OcppV2TestSuite) TestGetLogRequestValidation() { func (suite *OcppV2TestSuite) TestGetLogConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.GetLogConfirmation{Status: ocpp2.LogStatusAccepted, Filename: "testFileName.log"}, true}, - {ocpp2.GetLogConfirmation{Status: ocpp2.LogStatusAccepted}, true}, - {ocpp2.GetLogConfirmation{Status: ocpp2.LogStatusRejected}, true}, - {ocpp2.GetLogConfirmation{Status: ocpp2.LogStatusAcceptedCanceled}, true}, - {ocpp2.GetLogConfirmation{}, false}, - {ocpp2.GetLogConfirmation{Status: "invalidLogStatus"}, false}, - {ocpp2.GetLogConfirmation{Status: ocpp2.LogStatusAccepted, Filename: ">256............................................................................................................................................................................................................................................................."}, false}, + {diagnostics.GetLogResponse{Status: diagnostics.LogStatusAccepted, Filename: "testFileName.log"}, true}, + {diagnostics.GetLogResponse{Status: diagnostics.LogStatusAccepted}, true}, + {diagnostics.GetLogResponse{Status: diagnostics.LogStatusRejected}, true}, + {diagnostics.GetLogResponse{Status: diagnostics.LogStatusAcceptedCanceled}, true}, + {diagnostics.GetLogResponse{}, false}, + {diagnostics.GetLogResponse{Status: "invalidLogStatus"}, false}, + {diagnostics.GetLogResponse{Status: diagnostics.LogStatusAccepted, Filename: ">256............................................................................................................................................................................................................................................................."}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -53,27 +54,27 @@ func (suite *OcppV2TestSuite) TestGetLogE2EMocked() { wsId := "test_id" messageId := defaultMessageId wsUrl := "someUrl" - logParameters := ocpp2.LogParameters{ + logParameters := diagnostics.LogParameters{ RemoteLocation: "ftp://someurl/diagnostics/1", - OldestTimestamp: ocpp2.NewDateTime(time.Now().Add(-2 * time.Hour)), - LatestTimestamp: ocpp2.NewDateTime(time.Now()), + OldestTimestamp: types.NewDateTime(time.Now().Add(-2 * time.Hour)), + LatestTimestamp: types.NewDateTime(time.Now()), } - logType := ocpp2.LogTypeDiagnostics + logType := diagnostics.LogTypeDiagnostics requestID := 42 retries := newInt(5) retryInterval := newInt(120) - status := ocpp2.LogStatusAccepted + status := diagnostics.LogStatusAccepted filename := "someFileName.log" requestJson := fmt.Sprintf(`[2,"%v","%v",{"logType":"%v","requestId":%v,"retries":%v,"retryInterval":%v,"log":{"remoteLocation":"%v","oldestTimestamp":"%v","latestTimestamp":"%v"}}]`, - messageId, ocpp2.GetLogFeatureName, logType, requestID, *retries, *retryInterval, logParameters.RemoteLocation, ocpp2.FormatTimestamp(logParameters.OldestTimestamp.Time), ocpp2.FormatTimestamp(logParameters.LatestTimestamp.Time)) + messageId, diagnostics.GetLogFeatureName, logType, requestID, *retries, *retryInterval, logParameters.RemoteLocation, logParameters.OldestTimestamp.FormatTimestamp(), logParameters.LatestTimestamp.FormatTimestamp()) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v","filename":"%v"}]`, messageId, status, filename) - getLogConfirmation := ocpp2.NewGetLogConfirmation(status) + getLogConfirmation := diagnostics.NewGetLogResponse(status) getLogConfirmation.Filename = filename channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnGetLog", mock.Anything).Return(getLogConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.GetLogRequest) + handler := MockChargingStationDiagnosticsHandler{} + handler.On("OnGetLog", mock.Anything).Return(getLogConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*diagnostics.GetLogRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, logType, request.LogType) @@ -81,23 +82,23 @@ func (suite *OcppV2TestSuite) TestGetLogE2EMocked() { assert.Equal(t, *retries, *request.Retries) assert.Equal(t, *retryInterval, *request.RetryInterval) assert.Equal(t, logParameters.RemoteLocation, request.Log.RemoteLocation) - assert.Equal(t, ocpp2.FormatTimestamp(logParameters.LatestTimestamp.Time), ocpp2.FormatTimestamp(request.Log.LatestTimestamp.Time)) - assert.Equal(t, ocpp2.FormatTimestamp(logParameters.OldestTimestamp.Time), ocpp2.FormatTimestamp(request.Log.OldestTimestamp.Time)) + assert.Equal(t, logParameters.LatestTimestamp.FormatTimestamp(), request.Log.LatestTimestamp.FormatTimestamp()) + assert.Equal(t, logParameters.OldestTimestamp.FormatTimestamp(), request.Log.OldestTimestamp.FormatTimestamp()) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.GetLog(wsId, func(confirmation *ocpp2.GetLogConfirmation, err error) { + err = suite.csms.GetLog(wsId, func(confirmation *diagnostics.GetLogResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) assert.Equal(t, filename, confirmation.Filename) resultChannel <- true - }, logType, requestID, logParameters, func(request *ocpp2.GetLogRequest) { + }, logType, requestID, logParameters, func(request *diagnostics.GetLogRequest) { request.Retries = retries request.RetryInterval = retryInterval }) @@ -108,19 +109,19 @@ func (suite *OcppV2TestSuite) TestGetLogE2EMocked() { func (suite *OcppV2TestSuite) TestGetLogInvalidEndpoint() { messageId := defaultMessageId - logParameters := ocpp2.LogParameters{ + logParameters := diagnostics.LogParameters{ RemoteLocation: "ftp://someurl/diagnostics/1", - OldestTimestamp: ocpp2.NewDateTime(time.Now().Add(-2 * time.Hour)), - LatestTimestamp: ocpp2.NewDateTime(time.Now()), + OldestTimestamp: types.NewDateTime(time.Now().Add(-2 * time.Hour)), + LatestTimestamp: types.NewDateTime(time.Now()), } - logType := ocpp2.LogTypeDiagnostics + logType := diagnostics.LogTypeDiagnostics requestID := 42 retries := newInt(5) retryInterval := newInt(120) - getLogRequest := ocpp2.NewGetLogRequest(logType, requestID, logParameters) + getLogRequest := diagnostics.NewGetLogRequest(logType, requestID, logParameters) getLogRequest.Retries = retries getLogRequest.RetryInterval = retryInterval requestJson := fmt.Sprintf(`[2,"%v","%v",{"logType":"%v","requestId":%v,"retries":%v,"retryInterval":%v,"log":{"remoteLocation":"%v","oldestTimestamp":"%v","latestTimestamp":"%v"}}]`, - messageId, ocpp2.GetLogFeatureName, logType, requestID, *retries, *retryInterval, logParameters.RemoteLocation, ocpp2.FormatTimestamp(logParameters.OldestTimestamp.Time), ocpp2.FormatTimestamp(logParameters.LatestTimestamp.Time)) - testUnsupportedRequestFromChargePoint(suite, getLogRequest, requestJson, messageId) + messageId, diagnostics.GetLogFeatureName, logType, requestID, *retries, *retryInterval, logParameters.RemoteLocation, logParameters.OldestTimestamp.FormatTimestamp(), logParameters.LatestTimestamp.FormatTimestamp()) + testUnsupportedRequestFromChargingStation(suite, getLogRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/get_monitoring_report_test.go b/ocpp2.0_test/get_monitoring_report_test.go index 8397fbb5..b4b85c17 100644 --- a/ocpp2.0_test/get_monitoring_report_test.go +++ b/ocpp2.0_test/get_monitoring_report_test.go @@ -2,7 +2,8 @@ package ocpp2_test import ( "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/diagnostics" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -11,23 +12,23 @@ import ( // Test func (suite *OcppV2TestSuite) TestGetMonitoringReportRequestValidation() { t := suite.T() - componentVariables := []ocpp2.ComponentVariable{ + componentVariables := []types.ComponentVariable{ { - Component: ocpp2.Component{ Name: "component1", Instance: "instance1", EVSE: &ocpp2.EVSE{ID: 2, ConnectorID: newInt(2)}}, - Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}, + Component: types.Component{Name: "component1", Instance: "instance1", EVSE: &types.EVSE{ID: 2, ConnectorID: newInt(2)}}, + Variable: types.Variable{Name: "variable1", Instance: "instance1"}, }, } var requestTable = []GenericTestEntry{ - {ocpp2.GetMonitoringReportRequest{RequestID: newInt(42), MonitoringCriteria: []ocpp2.MonitoringCriteriaType{ocpp2.MonitoringCriteriaThresholdMonitoring, ocpp2.MonitoringCriteriaDeltaMonitoring, ocpp2.MonitoringCriteriaPeriodicMonitoring}, ComponentVariable: componentVariables}, true}, - {ocpp2.GetMonitoringReportRequest{RequestID: newInt(42), MonitoringCriteria: []ocpp2.MonitoringCriteriaType{}, ComponentVariable: componentVariables}, true}, - {ocpp2.GetMonitoringReportRequest{RequestID: newInt(42), ComponentVariable: componentVariables}, true}, - {ocpp2.GetMonitoringReportRequest{RequestID: newInt(42), ComponentVariable: []ocpp2.ComponentVariable{}}, true}, - {ocpp2.GetMonitoringReportRequest{RequestID: newInt(42)}, true}, - {ocpp2.GetMonitoringReportRequest{}, true}, - {ocpp2.GetMonitoringReportRequest{RequestID: newInt(-1)}, false}, - {ocpp2.GetMonitoringReportRequest{MonitoringCriteria: []ocpp2.MonitoringCriteriaType{ocpp2.MonitoringCriteriaThresholdMonitoring, ocpp2.MonitoringCriteriaDeltaMonitoring, ocpp2.MonitoringCriteriaPeriodicMonitoring, ocpp2.MonitoringCriteriaThresholdMonitoring}}, false}, - {ocpp2.GetMonitoringReportRequest{MonitoringCriteria: []ocpp2.MonitoringCriteriaType{"invalidMonitoringCriteria"}}, false}, - {ocpp2.GetMonitoringReportRequest{ComponentVariable: []ocpp2.ComponentVariable{ { Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}}}}, false}, + {diagnostics.GetMonitoringReportRequest{RequestID: newInt(42), MonitoringCriteria: []diagnostics.MonitoringCriteriaType{diagnostics.MonitoringCriteriaThresholdMonitoring, diagnostics.MonitoringCriteriaDeltaMonitoring, diagnostics.MonitoringCriteriaPeriodicMonitoring}, ComponentVariable: componentVariables}, true}, + {diagnostics.GetMonitoringReportRequest{RequestID: newInt(42), MonitoringCriteria: []diagnostics.MonitoringCriteriaType{}, ComponentVariable: componentVariables}, true}, + {diagnostics.GetMonitoringReportRequest{RequestID: newInt(42), ComponentVariable: componentVariables}, true}, + {diagnostics.GetMonitoringReportRequest{RequestID: newInt(42), ComponentVariable: []types.ComponentVariable{}}, true}, + {diagnostics.GetMonitoringReportRequest{RequestID: newInt(42)}, true}, + {diagnostics.GetMonitoringReportRequest{}, true}, + {diagnostics.GetMonitoringReportRequest{RequestID: newInt(-1)}, false}, + {diagnostics.GetMonitoringReportRequest{MonitoringCriteria: []diagnostics.MonitoringCriteriaType{diagnostics.MonitoringCriteriaThresholdMonitoring, diagnostics.MonitoringCriteriaDeltaMonitoring, diagnostics.MonitoringCriteriaPeriodicMonitoring, diagnostics.MonitoringCriteriaThresholdMonitoring}}, false}, + {diagnostics.GetMonitoringReportRequest{MonitoringCriteria: []diagnostics.MonitoringCriteriaType{"invalidMonitoringCriteria"}}, false}, + {diagnostics.GetMonitoringReportRequest{ComponentVariable: []types.ComponentVariable{{Variable: types.Variable{Name: "variable1", Instance: "instance1"}}}}, false}, } ExecuteGenericTestTable(t, requestTable) } @@ -35,9 +36,9 @@ func (suite *OcppV2TestSuite) TestGetMonitoringReportRequestValidation() { func (suite *OcppV2TestSuite) TestGetMonitoringReportConfirmationValidation() { t := suite.T() var confirmationTable = []GenericTestEntry{ - {ocpp2.GetMonitoringReportConfirmation{Status: ocpp2.GenericDeviceModelStatusAccepted}, true}, - {ocpp2.GetMonitoringReportConfirmation{Status: "invalidDeviceModelStatus"}, false}, - {ocpp2.GetMonitoringReportConfirmation{}, false}, + {diagnostics.GetMonitoringReportResponse{Status: types.GenericDeviceModelStatusAccepted}, true}, + {diagnostics.GetMonitoringReportResponse{Status: "invalidDeviceModelStatus"}, false}, + {diagnostics.GetMonitoringReportResponse{}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -48,22 +49,22 @@ func (suite *OcppV2TestSuite) TestGetMonitoringReportE2EMocked() { messageId := defaultMessageId wsUrl := "someUrl" requestID := newInt(42) - monitoringCriteria := []ocpp2.MonitoringCriteriaType{ocpp2.MonitoringCriteriaThresholdMonitoring, ocpp2.MonitoringCriteriaPeriodicMonitoring} - componentVariable := ocpp2.ComponentVariable{ - Component: ocpp2.Component{ Name: "component1", Instance: "instance1", EVSE: &ocpp2.EVSE{ID: 2, ConnectorID: newInt(2)}}, - Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}, + monitoringCriteria := []diagnostics.MonitoringCriteriaType{diagnostics.MonitoringCriteriaThresholdMonitoring, diagnostics.MonitoringCriteriaPeriodicMonitoring} + componentVariable := types.ComponentVariable{ + Component: types.Component{Name: "component1", Instance: "instance1", EVSE: &types.EVSE{ID: 2, ConnectorID: newInt(2)}}, + Variable: types.Variable{Name: "variable1", Instance: "instance1"}, } - componentVariables := []ocpp2.ComponentVariable{componentVariable} - status := ocpp2.GenericDeviceModelStatusAccepted + componentVariables := []types.ComponentVariable{componentVariable} + status := types.GenericDeviceModelStatusAccepted requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"monitoringCriteria":["%v","%v"],"componentVariable":[{"component":{"name":"%v","instance":"%v","evse":{"id":%v,"connectorId":%v}},"variable":{"name":"%v","instance":"%v"}}]}]`, - messageId, ocpp2.GetMonitoringReportFeatureName, *requestID, monitoringCriteria[0], monitoringCriteria[1], componentVariable.Component.Name, componentVariable.Component.Instance, componentVariable.Component.EVSE.ID, *componentVariable.Component.EVSE.ConnectorID, componentVariable.Variable.Name, componentVariable.Variable.Instance) + messageId, diagnostics.GetMonitoringReportFeatureName, *requestID, monitoringCriteria[0], monitoringCriteria[1], componentVariable.Component.Name, componentVariable.Component.Instance, componentVariable.Component.EVSE.ID, *componentVariable.Component.EVSE.ConnectorID, componentVariable.Variable.Name, componentVariable.Variable.Instance) responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v"}]`, messageId, status) - getMonitoringReportConfirmation := ocpp2.NewGetMonitoringReportConfirmation(status) + getMonitoringReportConfirmation := diagnostics.NewGetMonitoringReportResponse(status) channel := NewMockWebSocket(wsId) - coreListener := MockChargePointCoreListener{} - coreListener.On("OnGetMonitoringReport", mock.Anything).Return(getMonitoringReportConfirmation, nil).Run(func(args mock.Arguments) { - request, ok := args.Get(0).(*ocpp2.GetMonitoringReportRequest) + handler := MockChargingStationDiagnosticsHandler{} + handler.On("OnGetMonitoringReport", mock.Anything).Return(getMonitoringReportConfirmation, nil).Run(func(args mock.Arguments) { + request, ok := args.Get(0).(*diagnostics.GetMonitoringReportRequest) require.True(t, ok) require.NotNil(t, request) assert.Equal(t, *requestID, *request.RequestID) @@ -79,19 +80,19 @@ func (suite *OcppV2TestSuite) TestGetMonitoringReportE2EMocked() { assert.Equal(t, componentVariable.Variable.Name, request.ComponentVariable[0].Variable.Name) assert.Equal(t, componentVariable.Variable.Instance, request.ComponentVariable[0].Variable.Instance) }) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(responseJson), forwardWrittenMessage: true}, handler) // Run Test suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) resultChannel := make(chan bool, 1) - err = suite.csms.GetMonitoringReport(wsId, func(confirmation *ocpp2.GetMonitoringReportConfirmation, err error) { + err = suite.csms.GetMonitoringReport(wsId, func(confirmation *diagnostics.GetMonitoringReportResponse, err error) { require.Nil(t, err) require.NotNil(t, confirmation) assert.Equal(t, status, confirmation.Status) resultChannel <- true - }, func(request *ocpp2.GetMonitoringReportRequest) { + }, func(request *diagnostics.GetMonitoringReportRequest) { request.RequestID = requestID request.MonitoringCriteria = monitoringCriteria request.ComponentVariable = componentVariables @@ -104,13 +105,13 @@ func (suite *OcppV2TestSuite) TestGetMonitoringReportE2EMocked() { func (suite *OcppV2TestSuite) TestGetMonitoringReportInvalidEndpoint() { messageId := defaultMessageId requestID := newInt(42) - monitoringCriteria := []ocpp2.MonitoringCriteriaType{ocpp2.MonitoringCriteriaThresholdMonitoring, ocpp2.MonitoringCriteriaPeriodicMonitoring} - componentVariable := ocpp2.ComponentVariable{ - Component: ocpp2.Component{ Name: "component1", Instance: "instance1", EVSE: &ocpp2.EVSE{ID: 2, ConnectorID: newInt(2)}}, - Variable: ocpp2.Variable{ Name: "variable1", Instance: "instance1"}, + monitoringCriteria := []diagnostics.MonitoringCriteriaType{diagnostics.MonitoringCriteriaThresholdMonitoring, diagnostics.MonitoringCriteriaPeriodicMonitoring} + componentVariable := types.ComponentVariable{ + Component: types.Component{Name: "component1", Instance: "instance1", EVSE: &types.EVSE{ID: 2, ConnectorID: newInt(2)}}, + Variable: types.Variable{Name: "variable1", Instance: "instance1"}, } - GetMonitoringReportRequest := ocpp2.NewGetMonitoringReportRequest() + GetMonitoringReportRequest := diagnostics.NewGetMonitoringReportRequest() requestJson := fmt.Sprintf(`[2,"%v","%v",{"requestId":%v,"monitoringCriteria":["%v","%v"],"componentVariable":[{"component":{"name":"%v","instance":"%v","evse":{"id":%v,"connectorId":%v}},"variable":{"name":"%v","instance":"%v"}}]}]`, - messageId, ocpp2.GetMonitoringReportFeatureName, *requestID, monitoringCriteria[0], monitoringCriteria[1], componentVariable.Component.Name, componentVariable.Component.Instance, componentVariable.Component.EVSE.ID, *componentVariable.Component.EVSE.ConnectorID, componentVariable.Variable.Name, componentVariable.Variable.Instance) - testUnsupportedRequestFromChargePoint(suite, GetMonitoringReportRequest, requestJson, messageId) + messageId, diagnostics.GetMonitoringReportFeatureName, *requestID, monitoringCriteria[0], monitoringCriteria[1], componentVariable.Component.Name, componentVariable.Component.Instance, componentVariable.Component.EVSE.ID, *componentVariable.Component.EVSE.ConnectorID, componentVariable.Variable.Name, componentVariable.Variable.Instance) + testUnsupportedRequestFromChargingStation(suite, GetMonitoringReportRequest, requestJson, messageId) } diff --git a/ocpp2.0_test/ocpp2_test.go b/ocpp2.0_test/ocpp2_test.go index 1a56b821..2b490216 100644 --- a/ocpp2.0_test/ocpp2_test.go +++ b/ocpp2.0_test/ocpp2_test.go @@ -4,6 +4,23 @@ import ( "fmt" "github.com/lorenzodonini/ocpp-go/ocpp" ocpp2 "github.com/lorenzodonini/ocpp-go/ocpp2.0" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/authorization" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/availability" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/data" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/diagnostics" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/display" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/firmware" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/iso15118" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/localauth" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/meter" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/provisioning" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/remotecontrol" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/reservation" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/security" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/smartcharging" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/tariffcost" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/transactions" + "github.com/lorenzodonini/ocpp-go/ocpp2.0/types" "github.com/lorenzodonini/ocpp-go/ocppj" "github.com/lorenzodonini/ocpp-go/ws" "github.com/sirupsen/logrus" @@ -16,6 +33,7 @@ import ( ) // ---------------------- MOCK WEBSOCKET ---------------------- + type MockWebSocket struct { id string } @@ -29,6 +47,7 @@ func NewMockWebSocket(id string) MockWebSocket { } // ---------------------- MOCK WEBSOCKET SERVER ---------------------- + type MockWebsocketServer struct { mock.Mock ws.WsServer @@ -70,6 +89,7 @@ func (websocketServer *MockWebsocketServer) NewClient(websocketId string, client } // ---------------------- MOCK WEBSOCKET CLIENT ---------------------- + type MockWebsocketClient struct { mock.Mock ws.WsClient @@ -124,7 +144,7 @@ func (f MockFeature) GetRequestType() reflect.Type { return reflect.TypeOf(MockRequest{}) } -func (f MockFeature) GetConfirmationType() reflect.Type { +func (f MockFeature) GetResponseType() reflect.Type { return reflect.TypeOf(MockConfirmation{}) } @@ -144,368 +164,356 @@ func newMockConfirmation(value string) *MockConfirmation { return &MockConfirmation{MockValue: value} } -// ---------------------- MOCK CSMS CORE LISTENER ---------------------- -type MockCentralSystemCoreListener struct { +// ---------------------- MOCK CSMS SECURITY HANDLER ---------------------- + +type MockCSMSSecurityHandler struct { mock.Mock } -func (coreListener MockCentralSystemCoreListener) OnAuthorize(chargePointId string, request *ocpp2.AuthorizeRequest) (confirmation *ocpp2.AuthorizeConfirmation, err error) { - args := coreListener.MethodCalled("OnAuthorize", chargePointId, request) - conf := args.Get(0).(*ocpp2.AuthorizeConfirmation) - return conf, args.Error(1) +// ---------------------- MOCK CS SECURITY HANDLER ---------------------- + +type MockChargingStationSecurityHandler struct { + mock.Mock } -func (coreListener MockCentralSystemCoreListener) OnBootNotification(chargePointId string, request *ocpp2.BootNotificationRequest) (confirmation *ocpp2.BootNotificationConfirmation, err error) { - args := coreListener.MethodCalled("OnBootNotification", chargePointId, request) - conf := args.Get(0).(*ocpp2.BootNotificationConfirmation) +func (handler MockChargingStationSecurityHandler) OnCertificateSigned(request *security.CertificateSignedRequest) (response *security.CertificateSignedResponse, err error) { + args := handler.MethodCalled("OnCertificateSigned", request) + conf := args.Get(0).(*security.CertificateSignedResponse) return conf, args.Error(1) } -func (coreListener MockCentralSystemCoreListener) OnClearedChargingLimit(chargePointId string, request *ocpp2.ClearedChargingLimitRequest) (confirmation *ocpp2.ClearedChargingLimitConfirmation, err error) { - args := coreListener.MethodCalled("OnClearedChargingLimit", chargePointId, request) - conf := args.Get(0).(*ocpp2.ClearedChargingLimitConfirmation) - return conf, args.Error(1) +// ---------------------- MOCK CSMS PROVISIONING HANDLER ---------------------- + +type MockCSMSProvisioningHandler struct { + mock.Mock } -func (coreListener MockCentralSystemCoreListener) OnDataTransfer(chargePointId string, request *ocpp2.DataTransferRequest) (confirmation *ocpp2.DataTransferConfirmation, err error) { - args := coreListener.MethodCalled("OnDataTransfer", chargePointId, request) - conf := args.Get(0).(*ocpp2.DataTransferConfirmation) +func (handler MockCSMSProvisioningHandler) OnBootNotification(chargingStationId string, request *provisioning.BootNotificationRequest) (confirmation *provisioning.BootNotificationResponse, err error) { + args := handler.MethodCalled("OnBootNotification", chargingStationId, request) + conf := args.Get(0).(*provisioning.BootNotificationResponse) return conf, args.Error(1) } -func (coreListener MockCentralSystemCoreListener) OnFirmwareStatusNotification(chargePointId string, request *ocpp2.FirmwareStatusNotificationRequest) (confirmation *ocpp2.FirmwareStatusNotificationConfirmation, err error) { - args := coreListener.MethodCalled("OnFirmwareStatusNotification", chargePointId, request) - conf := args.Get(0).(*ocpp2.FirmwareStatusNotificationConfirmation) - return conf, args.Error(1) +// ---------------------- MOCK CS PROVISIONING HANDLER ---------------------- + +type MockChargingStationProvisioningHandler struct { + mock.Mock } -func (coreListener MockCentralSystemCoreListener) OnGet15118EVCertificate(chargePointId string, request *ocpp2.Get15118EVCertificateRequest) (confirmation *ocpp2.Get15118EVCertificateConfirmation, err error) { - args := coreListener.MethodCalled("OnGet15118EVCertificate", chargePointId, request) - conf := args.Get(0).(*ocpp2.Get15118EVCertificateConfirmation) +func (handler MockChargingStationProvisioningHandler) OnGetBaseReport(request *provisioning.GetBaseReportRequest) (confirmation *provisioning.GetBaseReportResponse, err error) { + args := handler.MethodCalled("OnGetBaseReport", request) + conf := args.Get(0).(*provisioning.GetBaseReportResponse) return conf, args.Error(1) } -func (coreListener MockCentralSystemCoreListener) OnGetCertificateStatus(chargePointId string, request *ocpp2.GetCertificateStatusRequest) (confirmation *ocpp2.GetCertificateStatusConfirmation, err error) { - args := coreListener.MethodCalled("OnGetCertificateStatus", chargePointId, request) - conf := args.Get(0).(*ocpp2.GetCertificateStatusConfirmation) - return conf, args.Error(1) +// ---------------------- MOCK CSMS AUTHORIZATION HANDLER ---------------------- + +type MockCSMSAuthorizationHandler struct { + mock.Mock } -//func (coreListener MockCentralSystemCoreListener) OnDataTransfer(chargePointId string, request *ocpp2.DataTransferRequest) (confirmation *ocpp2.DataTransferConfirmation, err error) { -// args := coreListener.MethodCalled("OnDataTransfer", chargePointId, request) -// conf := args.Get(0).(*ocpp2.DataTransferConfirmation) -// return conf, args.Error(1) -//} -// -//func (coreListener MockCentralSystemCoreListener) OnHeartbeat(chargePointId string, request *ocpp2.HeartbeatRequest) (confirmation *ocpp2.HeartbeatConfirmation, err error) { -// args := coreListener.MethodCalled("OnHeartbeat", chargePointId, request) -// conf := args.Get(0).(*ocpp2.HeartbeatConfirmation) -// return conf, args.Error(1) -//} -// -//func (coreListener MockCentralSystemCoreListener) OnMeterValues(chargePointId string, request *ocpp2.MeterValuesRequest) (confirmation *ocpp2.MeterValuesConfirmation, err error) { -// args := coreListener.MethodCalled("OnMeterValues", chargePointId, request) -// conf := args.Get(0).(*ocpp2.MeterValuesConfirmation) -// return conf, args.Error(1) -//} -// -//func (coreListener MockCentralSystemCoreListener) OnStartTransaction(chargePointId string, request *ocpp2.StartTransactionRequest) (confirmation *ocpp2.StartTransactionConfirmation, err error) { -// args := coreListener.MethodCalled("OnStartTransaction", chargePointId, request) -// conf := args.Get(0).(*ocpp2.StartTransactionConfirmation) -// return conf, args.Error(1) -//} -// -//func (coreListener MockCentralSystemCoreListener) OnStatusNotification(chargePointId string, request *ocpp2.StatusNotificationRequest) (confirmation *ocpp2.StatusNotificationConfirmation, err error) { -// args := coreListener.MethodCalled("OnStatusNotification", chargePointId, request) -// conf := args.Get(0).(*ocpp2.StatusNotificationConfirmation) -// return conf, args.Error(1) -//} -// -//func (coreListener MockCentralSystemCoreListener) OnStopTransaction(chargePointId string, request *ocpp2.StopTransactionRequest) (confirmation *ocpp2.StopTransactionConfirmation, err error) { -// args := coreListener.MethodCalled("OnStopTransaction", chargePointId, request) -// conf := args.Get(0).(*ocpp2.StopTransactionConfirmation) -// return conf, args.Error(1) -//} - -// ---------------------- MOCK CP CORE LISTENER ---------------------- -type MockChargePointCoreListener struct { - mock.Mock -} - -func (coreListener MockChargePointCoreListener) OnCancelReservation(request *ocpp2.CancelReservationRequest) (confirmation *ocpp2.CancelReservationConfirmation, err error) { - args := coreListener.MethodCalled("OnCancelReservation", request) - conf := args.Get(0).(*ocpp2.CancelReservationConfirmation) +func (handler MockCSMSAuthorizationHandler) OnAuthorize(chargingStationId string, request *authorization.AuthorizeRequest) (confirmation *authorization.AuthorizeResponse, err error) { + args := handler.MethodCalled("OnAuthorize", chargingStationId, request) + conf := args.Get(0).(*authorization.AuthorizeResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnCertificateSigned(request *ocpp2.CertificateSignedRequest) (confirmation *ocpp2.CertificateSignedConfirmation, err error) { - args := coreListener.MethodCalled("OnCertificateSigned", request) - conf := args.Get(0).(*ocpp2.CertificateSignedConfirmation) - return conf, args.Error(1) +// ---------------------- MOCK CS AUTHORIZATION HANDLER ---------------------- + +type MockChargingStationAuthorizationHandler struct { + mock.Mock } -func (coreListener MockChargePointCoreListener) OnChangeAvailability(request *ocpp2.ChangeAvailabilityRequest) (confirmation *ocpp2.ChangeAvailabilityConfirmation, err error) { - args := coreListener.MethodCalled("OnChangeAvailability", request) - conf := args.Get(0).(*ocpp2.ChangeAvailabilityConfirmation) +func (handler MockChargingStationAuthorizationHandler) OnClearCache(request *authorization.ClearCacheRequest) (confirmation *authorization.ClearCacheResponse, err error) { + args := handler.MethodCalled("OnClearCache", request) + conf := args.Get(0).(*authorization.ClearCacheResponse) return conf, args.Error(1) } -// -//func (coreListener MockChargePointCoreListener) OnDataTransfer(request *ocpp2.DataTransferRequest) (confirmation *ocpp2.DataTransferConfirmation, err error) { -// args := coreListener.MethodCalled("OnDataTransfer", request) -// conf := args.Get(0).(*ocpp2.DataTransferConfirmation) -// return conf, args.Error(1) -//} -// -//func (coreListener MockChargePointCoreListener) OnChangeConfiguration(request *ocpp2.ChangeConfigurationRequest) (confirmation *ocpp2.ChangeConfigurationConfirmation, err error) { -// args := coreListener.MethodCalled("OnChangeConfiguration", request) -// conf := args.Get(0).(*ocpp2.ChangeConfigurationConfirmation) -// return conf, args.Error(1) -//} - -func (coreListener MockChargePointCoreListener) OnClearCache(request *ocpp2.ClearCacheRequest) (confirmation *ocpp2.ClearCacheConfirmation, err error) { - args := coreListener.MethodCalled("OnClearCache", request) - conf := args.Get(0).(*ocpp2.ClearCacheConfirmation) + +// ---------------------- MOCK CS RESERVATION HANDLER ---------------------- + +type MockChargingStationReservationHandler struct { + mock.Mock +} + +func (handler MockChargingStationReservationHandler) OnCancelReservation(request *reservation.CancelReservationRequest) (confirmation *reservation.CancelReservationResponse, err error) { + args := handler.MethodCalled("OnCancelReservation", request) + conf := args.Get(0).(*reservation.CancelReservationResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnClearChargingProfile(request *ocpp2.ClearChargingProfileRequest) (confirmation *ocpp2.ClearChargingProfileConfirmation, err error) { - args := coreListener.MethodCalled("OnClearChargingProfile", request) - conf := args.Get(0).(*ocpp2.ClearChargingProfileConfirmation) +// ---------------------- MOCK CSMS RESERVATION HANDLER ---------------------- + +type MockCSMSReservationHandler struct { + mock.Mock +} + +// ---------------------- MOCK CS AVAILABILITY HANDLER ---------------------- + +type MockChargingStationAvailabilityHandler struct { + mock.Mock +} + +func (handler MockChargingStationAvailabilityHandler) OnChangeAvailability(request *availability.ChangeAvailabilityRequest) (confirmation *availability.ChangeAvailabilityResponse, err error) { + args := handler.MethodCalled("OnChangeAvailability", request) + conf := args.Get(0).(*availability.ChangeAvailabilityResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnClearDisplay(request *ocpp2.ClearDisplayRequest) (confirmation *ocpp2.ClearDisplayConfirmation, err error) { - args := coreListener.MethodCalled("OnClearDisplay", request) - conf := args.Get(0).(*ocpp2.ClearDisplayConfirmation) +// ---------------------- MOCK CSMS AVAILABILITY HANDLER ---------------------- + +type MockCSMSAvailabilityHandler struct { + mock.Mock +} + +// ---------------------- MOCK CS DATA HANDLER ---------------------- + +type MockChargingStationDataHandler struct { + mock.Mock +} + +func (handler MockChargingStationDataHandler) OnDataTransfer(request *data.DataTransferRequest) (confirmation *data.DataTransferResponse, err error) { + args := handler.MethodCalled("OnDataTransfer", request) + conf := args.Get(0).(*data.DataTransferResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnClearVariableMonitoring(request *ocpp2.ClearVariableMonitoringRequest) (confirmation *ocpp2.ClearVariableMonitoringConfirmation, err error) { - args := coreListener.MethodCalled("OnClearVariableMonitoring", request) - conf := args.Get(0).(*ocpp2.ClearVariableMonitoringConfirmation) +// ---------------------- MOCK CSMS DATA HANDLER ---------------------- + +type MockCSMSDataHandler struct { + mock.Mock +} + +func (handler MockCSMSDataHandler) OnDataTransfer(chargingStationID string, request *data.DataTransferRequest) (confirmation *data.DataTransferResponse, err error) { + args := handler.MethodCalled("OnDataTransfer", chargingStationID, request) + conf := args.Get(0).(*data.DataTransferResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnCostUpdated(request *ocpp2.CostUpdatedRequest) (confirmation *ocpp2.CostUpdatedConfirmation, err error) { - args := coreListener.MethodCalled("OnCostUpdated", request) - conf := args.Get(0).(*ocpp2.CostUpdatedConfirmation) +// ---------------------- MOCK CS DIAGNOSTICS HANDLER ---------------------- + +type MockChargingStationDiagnosticsHandler struct { + mock.Mock +} + +func (handler MockChargingStationDiagnosticsHandler) OnClearVariableMonitoring(request *diagnostics.ClearVariableMonitoringRequest) (confirmation *diagnostics.ClearVariableMonitoringResponse, err error) { + args := handler.MethodCalled("OnClearVariableMonitoring", request) + conf := args.Get(0).(*diagnostics.ClearVariableMonitoringResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnCustomerInformation(request *ocpp2.CustomerInformationRequest) (confirmation *ocpp2.CustomerInformationConfirmation, err error) { - args := coreListener.MethodCalled("OnCustomerInformation", request) - conf := args.Get(0).(*ocpp2.CustomerInformationConfirmation) +func (handler MockChargingStationDiagnosticsHandler) OnCustomerInformation(request *diagnostics.CustomerInformationRequest) (confirmation *diagnostics.CustomerInformationResponse, err error) { + args := handler.MethodCalled("OnCustomerInformation", request) + conf := args.Get(0).(*diagnostics.CustomerInformationResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnDataTransfer(request *ocpp2.DataTransferRequest) (confirmation *ocpp2.DataTransferConfirmation, err error) { - args := coreListener.MethodCalled("OnDataTransfer", request) - conf := args.Get(0).(*ocpp2.DataTransferConfirmation) +func (handler MockChargingStationDiagnosticsHandler) OnGetLog(request *diagnostics.GetLogRequest) (confirmation *diagnostics.GetLogResponse, err error) { + args := handler.MethodCalled("OnGetLog", request) + conf := args.Get(0).(*diagnostics.GetLogResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnDeleteCertificate(request *ocpp2.DeleteCertificateRequest) (confirmation *ocpp2.DeleteCertificateConfirmation, err error) { - args := coreListener.MethodCalled("OnDeleteCertificate", request) - conf := args.Get(0).(*ocpp2.DeleteCertificateConfirmation) +func (handler MockChargingStationDiagnosticsHandler) OnGetMonitoringReport(request *diagnostics.GetMonitoringReportRequest) (confirmation *diagnostics.GetMonitoringReportResponse, err error) { + args := handler.MethodCalled("OnGetMonitoringReport", request) + conf := args.Get(0).(*diagnostics.GetMonitoringReportResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnGetBaseReport(request *ocpp2.GetBaseReportRequest) (confirmation *ocpp2.GetBaseReportConfirmation, err error) { - args := coreListener.MethodCalled("OnGetBaseReport", request) - conf := args.Get(0).(*ocpp2.GetBaseReportConfirmation) +// ---------------------- MOCK CSMS DIAGNOSTICS HANDLER ---------------------- + +type MockCSMSDiagnosticsHandler struct { + mock.Mock +} + +// ---------------------- MOCK CS DISPLAY HANDLER ---------------------- + +type MockChargingStationDisplayHandler struct { + mock.Mock +} + +func (handler MockChargingStationDisplayHandler) OnClearDisplay(request *display.ClearDisplayRequest) (confirmation *display.ClearDisplayResponse, err error) { + args := handler.MethodCalled("OnClearDisplay", request) + conf := args.Get(0).(*display.ClearDisplayResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnGetChargingProfiles(request *ocpp2.GetChargingProfilesRequest) (confirmation *ocpp2.GetChargingProfilesConfirmation, err error) { - args := coreListener.MethodCalled("OnGetChargingProfiles", request) - conf := args.Get(0).(*ocpp2.GetChargingProfilesConfirmation) +func (handler MockChargingStationDisplayHandler) OnGetDisplayMessages(request *display.GetDisplayMessagesRequest) (confirmation *display.GetDisplayMessagesResponse, err error) { + args := handler.MethodCalled("OnGetDisplayMessages", request) + conf := args.Get(0).(*display.GetDisplayMessagesResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnGetCompositeSchedule(request *ocpp2.GetCompositeScheduleRequest) (confirmation *ocpp2.GetCompositeScheduleConfirmation, err error) { - args := coreListener.MethodCalled("OnGetCompositeSchedule", request) - conf := args.Get(0).(*ocpp2.GetCompositeScheduleConfirmation) +// ---------------------- MOCK CSMS DISPLAY HANDLER ---------------------- + +type MockCSMSDisplayHandler struct { + mock.Mock +} + +// ---------------------- MOCK CS FIRMWARE HANDLER ---------------------- + +type MockChargingStationFirmwareHandler struct { + mock.Mock +} + +// ---------------------- MOCK CSMS FIRMWARE HANDLER ---------------------- + +type MockCSMSFirmwareHandler struct { + mock.Mock +} + +func (handler MockCSMSFirmwareHandler) OnFirmwareStatusNotification(chargingStationID string, request *firmware.FirmwareStatusNotificationRequest) (confirmation *firmware.FirmwareStatusNotificationResponse, err error) { + args := handler.MethodCalled("OnFirmwareStatusNotification", chargingStationID, request) + conf := args.Get(0).(*firmware.FirmwareStatusNotificationResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnGetDisplayMessages(request *ocpp2.GetDisplayMessagesRequest) (confirmation *ocpp2.GetDisplayMessagesConfirmation, err error) { - args := coreListener.MethodCalled("OnGetDisplayMessages", request) - conf := args.Get(0).(*ocpp2.GetDisplayMessagesConfirmation) +// ---------------------- MOCK CS ISO15118 HANDLER ---------------------- + +type MockChargingStationIso15118Handler struct { + mock.Mock +} + +func (handler MockChargingStationIso15118Handler) OnDeleteCertificate(request *iso15118.DeleteCertificateRequest) (confirmation *iso15118.DeleteCertificateResponse, err error) { + args := handler.MethodCalled("OnDeleteCertificate", request) + conf := args.Get(0).(*iso15118.DeleteCertificateResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnGetInstalledCertificateIds(request *ocpp2.GetInstalledCertificateIdsRequest) (confirmation *ocpp2.GetInstalledCertificateIdsConfirmation, err error) { - args := coreListener.MethodCalled("OnGetInstalledCertificateIds", request) - conf := args.Get(0).(*ocpp2.GetInstalledCertificateIdsConfirmation) +func (handler MockChargingStationIso15118Handler) OnGetInstalledCertificateIds(request *iso15118.GetInstalledCertificateIdsRequest) (confirmation *iso15118.GetInstalledCertificateIdsResponse, err error) { + args := handler.MethodCalled("OnGetInstalledCertificateIds", request) + conf := args.Get(0).(*iso15118.GetInstalledCertificateIdsResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnGetLocalListVersion(request *ocpp2.GetLocalListVersionRequest) (confirmation *ocpp2.GetLocalListVersionConfirmation, err error) { - args := coreListener.MethodCalled("OnGetLocalListVersion", request) - conf := args.Get(0).(*ocpp2.GetLocalListVersionConfirmation) +// ---------------------- MOCK CSMS ISO15118 HANDLER ---------------------- + +type MockCSMSIso15118Handler struct { + mock.Mock +} + +func (handler MockCSMSIso15118Handler) OnGet15118EVCertificate(chargingStationID string, request *iso15118.Get15118EVCertificateRequest) (confirmation *iso15118.Get15118EVCertificateResponse, err error) { + args := handler.MethodCalled("OnGet15118EVCertificate", chargingStationID, request) + conf := args.Get(0).(*iso15118.Get15118EVCertificateResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnGetLog(request *ocpp2.GetLogRequest) (confirmation *ocpp2.GetLogConfirmation, err error) { - args := coreListener.MethodCalled("OnGetLog", request) - conf := args.Get(0).(*ocpp2.GetLogConfirmation) +func (handler MockCSMSIso15118Handler) OnGetCertificateStatus(chargingStationID string, request *iso15118.GetCertificateStatusRequest) (confirmation *iso15118.GetCertificateStatusResponse, err error) { + args := handler.MethodCalled("OnGetCertificateStatus", chargingStationID, request) + conf := args.Get(0).(*iso15118.GetCertificateStatusResponse) return conf, args.Error(1) } -func (coreListener MockChargePointCoreListener) OnGetMonitoringReport(request *ocpp2.GetMonitoringReportRequest) (confirmation *ocpp2.GetMonitoringReportConfirmation, err error) { - args := coreListener.MethodCalled("OnGetMonitoringReport", request) - conf := args.Get(0).(*ocpp2.GetMonitoringReportConfirmation) +// ---------------------- MOCK CS LOCAL AUTH HANDLER ---------------------- + +type MockChargingStationLocalAuthHandler struct { + mock.Mock +} + +func (handler MockChargingStationLocalAuthHandler) OnGetLocalListVersion(request *localauth.GetLocalListVersionRequest) (confirmation *localauth.GetLocalListVersionResponse, err error) { + args := handler.MethodCalled("OnGetLocalListVersion", request) + conf := args.Get(0).(*localauth.GetLocalListVersionResponse) return conf, args.Error(1) } -//func (coreListener MockChargePointCoreListener) OnGetConfiguration(request *ocpp2.GetConfigurationRequest) (confirmation *ocpp2.GetConfigurationConfirmation, err error) { -// args := coreListener.MethodCalled("OnGetConfiguration", request) -// conf := args.Get(0).(*ocpp2.GetConfigurationConfirmation) -// return conf, args.Error(1) -//} -// -//func (coreListener MockChargePointCoreListener) OnReset(request *ocpp2.ResetRequest) (confirmation *ocpp2.ResetConfirmation, err error) { -// args := coreListener.MethodCalled("OnReset", request) -// conf := args.Get(0).(*ocpp2.ResetConfirmation) -// return conf, args.Error(1) -//} -// -//func (coreListener MockChargePointCoreListener) OnUnlockConnector(request *ocpp2.UnlockConnectorRequest) (confirmation *ocpp2.UnlockConnectorConfirmation, err error) { -// args := coreListener.MethodCalled("OnUnlockConnector", request) -// conf := args.Get(0).(*ocpp2.UnlockConnectorConfirmation) -// return conf, args.Error(1) -//} -// -//func (coreListener MockChargePointCoreListener) OnRemoteStartTransaction(request *ocpp2.RemoteStartTransactionRequest) (confirmation *ocpp2.RemoteStartTransactionConfirmation, err error) { -// args := coreListener.MethodCalled("OnRemoteStartTransaction", request) -// conf := args.Get(0).(*ocpp2.RemoteStartTransactionConfirmation) -// return conf, args.Error(1) -//} -// -//func (coreListener MockChargePointCoreListener) OnRemoteStopTransaction(request *ocpp2.RemoteStopTransactionRequest) (confirmation *ocpp2.RemoteStopTransactionConfirmation, err error) { -// args := coreListener.MethodCalled("OnRemoteStopTransaction", request) -// conf := args.Get(0).(*ocpp2.RemoteStopTransactionConfirmation) -// return conf, args.Error(1) -//} +// ---------------------- MOCK CSMS LOCAL AUTH HANDLER ---------------------- -// ---------------------- MOCK CS LOCAL AUTH LIST LISTENER ---------------------- -type MockCentralSystemLocalAuthListListener struct { +type MockCSMSLocalAuthHandler struct { mock.Mock } -// ---------------------- MOCK CP LOCAL AUTH LIST LISTENER ---------------------- -type MockChargePointLocalAuthListListener struct { +// ---------------------- MOCK CS METER HANDLER ---------------------- + +type MockChargingStationMeterHandler struct { mock.Mock } -//func (localAuthListListener MockChargePointLocalAuthListListener) OnGetLocalListVersion(request *ocpp2.GetLocalListVersionRequest) (confirmation *ocpp2.GetLocalListVersionConfirmation, err error) { -// args := localAuthListListener.MethodCalled("OnGetLocalListVersion", request) -// conf := args.Get(0).(*ocpp2.GetLocalListVersionConfirmation) -// return conf, args.Error(1) -//} -// -//func (localAuthListListener MockChargePointLocalAuthListListener) OnSendLocalList(request *ocpp2.SendLocalListRequest) (confirmation *ocpp2.SendLocalListConfirmation, err error) { -// args := localAuthListListener.MethodCalled("OnSendLocalList", request) -// conf := args.Get(0).(*ocpp2.SendLocalListConfirmation) -// return conf, args.Error(1) -//} +// ---------------------- MOCK CSMS METER HANDLER ---------------------- -// ---------------------- MOCK CS FIRMWARE MANAGEMENT LISTENER ---------------------- -type MockCentralSystemFirmwareManagementListener struct { +type MockCSMSMeterHandler struct { mock.Mock } -//func (firmwareListener MockCentralSystemFirmwareManagementListener) OnDiagnosticsStatusNotification(chargePointId string, request *ocpp2.DiagnosticsStatusNotificationRequest) (confirmation *ocpp2.DiagnosticsStatusNotificationConfirmation, err error) { -// args := firmwareListener.MethodCalled("OnDiagnosticsStatusNotification", chargePointId, request) -// conf := args.Get(0).(*ocpp2.DiagnosticsStatusNotificationConfirmation) -// return conf, args.Error(1) -//} -// -//func (firmwareListener MockCentralSystemFirmwareManagementListener) OnFirmwareStatusNotification(chargePointId string, request *ocpp2.FirmwareStatusNotificationRequest) (confirmation *ocpp2.FirmwareStatusNotificationConfirmation, err error) { -// args := firmwareListener.MethodCalled("OnFirmwareStatusNotification", chargePointId, request) -// conf := args.Get(0).(*ocpp2.FirmwareStatusNotificationConfirmation) -// return conf, args.Error(1) -//} +// ---------------------- MOCK CS REMOTE CONTROL HANDLER ---------------------- -// ---------------------- MOCK CP FIRMWARE MANAGEMENT LISTENER ---------------------- -type MockChargePointFirmwareManagementListener struct { +type MockChargingStationRemoteControlHandler struct { mock.Mock } -//func (firmwareListener MockChargePointFirmwareManagementListener) OnGetDiagnostics(request *ocpp2.GetDiagnosticsRequest) (confirmation *ocpp2.GetDiagnosticsConfirmation, err error) { -// args := firmwareListener.MethodCalled("OnGetDiagnostics", request) -// conf := args.Get(0).(*ocpp2.GetDiagnosticsConfirmation) -// return conf, args.Error(1) -//} -// -//func (firmwareListener MockChargePointFirmwareManagementListener) OnUpdateFirmware(request *ocpp2.UpdateFirmwareRequest) (confirmation *ocpp2.UpdateFirmwareConfirmation, err error) { -// args := firmwareListener.MethodCalled("OnUpdateFirmware", request) -// conf := args.Get(0).(*ocpp2.UpdateFirmwareConfirmation) -// return conf, args.Error(1) -//} +// ---------------------- MOCK CSMS REMOTE CONTROL HANDLER ---------------------- -// ---------------------- MOCK CS RESERVATION LISTENER ---------------------- -type MockCentralSystemReservationListener struct { +type MockCSMSRemoteControlHandler struct { mock.Mock } -// ---------------------- MOCK CP RESERVATION LISTENER ---------------------- -type MockChargePointReservationListener struct { +// ---------------------- MOCK CS SMART CHARGING HANDLER ---------------------- + +type MockChargingStationSmartChargingHandler struct { mock.Mock } -//func (reservationListener MockChargePointReservationListener) OnReserveNow(request *ocpp2.ReserveNowRequest) (confirmation *ocpp2.ReserveNowConfirmation, err error) { -// args := reservationListener.MethodCalled("OnReserveNow", request) -// conf := args.Get(0).(*ocpp2.ReserveNowConfirmation) -// return conf, args.Error(1) -//} -// +func (handler MockChargingStationSmartChargingHandler) OnClearChargingProfile(request *smartcharging.ClearChargingProfileRequest) (confirmation *smartcharging.ClearChargingProfileResponse, err error) { + args := handler.MethodCalled("OnClearChargingProfile", request) + conf := args.Get(0).(*smartcharging.ClearChargingProfileResponse) + return conf, args.Error(1) +} -// ---------------------- MOCK CS REMOTE TRIGGER LISTENER ---------------------- -type MockCentralSystemRemoteTriggerListener struct { +func (handler MockChargingStationSmartChargingHandler) OnGetChargingProfiles(request *smartcharging.GetChargingProfilesRequest) (confirmation *smartcharging.GetChargingProfilesResponse, err error) { + args := handler.MethodCalled("OnGetChargingProfiles", request) + conf := args.Get(0).(*smartcharging.GetChargingProfilesResponse) + return conf, args.Error(1) +} + +func (handler MockChargingStationSmartChargingHandler) OnGetCompositeSchedule(request *smartcharging.GetCompositeScheduleRequest) (confirmation *smartcharging.GetCompositeScheduleResponse, err error) { + args := handler.MethodCalled("OnGetCompositeSchedule", request) + conf := args.Get(0).(*smartcharging.GetCompositeScheduleResponse) + return conf, args.Error(1) +} + +// ---------------------- MOCK CSMS SMART CHARGING HANDLER ---------------------- + +type MockCSMSSmartChargingHandler struct { mock.Mock } -// ---------------------- MOCK CP REMOTE TRIGGER LISTENER ---------------------- -type MockChargePointRemoteTriggerListener struct { +func (handler MockCSMSSmartChargingHandler) OnClearedChargingLimit(chargingStationID string, request *smartcharging.ClearedChargingLimitRequest) (confirmation *smartcharging.ClearedChargingLimitResponse, err error) { + args := handler.MethodCalled("OnClearedChargingLimit", chargingStationID, request) + conf := args.Get(0).(*smartcharging.ClearedChargingLimitResponse) + return conf, args.Error(1) +} + +// ---------------------- MOCK CS TARIFF COST HANDLER ---------------------- + +type MockChargingStationTariffCostHandler struct { mock.Mock } -//func (remoteTriggerListener MockChargePointRemoteTriggerListener) OnTriggerMessage(request *ocpp2.TriggerMessageRequest) (confirmation *ocpp2.TriggerMessageConfirmation, err error) { -// args := remoteTriggerListener.MethodCalled("OnTriggerMessage", request) -// conf := args.Get(0).(*ocpp2.TriggerMessageConfirmation) -// return conf, args.Error(1) -//} +func (handler MockChargingStationTariffCostHandler) OnCostUpdated(request *tariffcost.CostUpdatedRequest) (confirmation *tariffcost.CostUpdatedResponse, err error) { + args := handler.MethodCalled("OnCostUpdated", request) + conf := args.Get(0).(*tariffcost.CostUpdatedResponse) + return conf, args.Error(1) +} -// ---------------------- MOCK CS SMART CHARGING LISTENER ---------------------- -type MockCentralSystemSmartChargingListener struct { +// ---------------------- MOCK CSMS TARIFF COST HANDLER ---------------------- + +type MockCSMSTariffCostHandler struct { mock.Mock } -// ---------------------- MOCK CP SMART CHARGING LISTENER ---------------------- -type MockChargePointSmartChargingListener struct { +// ---------------------- MOCK CS TRANSACTIONS HANDLER ---------------------- + +type MockChargingStationTransactionHandler struct { mock.Mock } -//func (smartChargingListener MockChargePointSmartChargingListener) OnSetChargingProfile(request *ocpp2.SetChargingProfileRequest) (confirmation *ocpp2.SetChargingProfileConfirmation, err error) { -// args := smartChargingListener.MethodCalled("OnSetChargingProfile", request) -// conf := args.Get(0).(*ocpp2.SetChargingProfileConfirmation) -// return conf, args.Error(1) -//} -// -//func (smartChargingListener MockChargePointSmartChargingListener) OnClearChargingProfile(request *ocpp2.ClearChargingProfileRequest) (confirmation *ocpp2.ClearChargingProfileConfirmation, err error) { -// args := smartChargingListener.MethodCalled("OnClearChargingProfile", request) -// conf := args.Get(0).(*ocpp2.ClearChargingProfileConfirmation) -// return conf, args.Error(1) -//} -// -//func (smartChargingListener MockChargePointSmartChargingListener) OnGetCompositeSchedule(request *ocpp2.GetCompositeScheduleRequest) (confirmation *ocpp2.GetCompositeScheduleConfirmation, err error) { -// args := smartChargingListener.MethodCalled("OnGetCompositeSchedule", request) -// conf := args.Get(0).(*ocpp2.GetCompositeScheduleConfirmation) -// return conf, args.Error(1) -//} +// ---------------------- MOCK CSMS TRANSACTIONS HANDLER ---------------------- + +type MockCSMSTransactionsHandler struct { + mock.Mock +} // ---------------------- COMMON UTILITY METHODS ---------------------- + func NewWebsocketServer(t *testing.T, onMessage func(data []byte) ([]byte, error)) *ws.Server { wsServer := ws.Server{} wsServer.SetMessageHandler(func(ws ws.Channel, data []byte) error { @@ -541,7 +549,7 @@ func NewWebsocketClient(t *testing.T, onMessage func(data []byte) ([]byte, error return &wsClient } -type expectedCentralSystemOptions struct { +type expectedCSMSOptions struct { clientId string rawWrittenMessage []byte startReturnArgument interface{} @@ -549,7 +557,7 @@ type expectedCentralSystemOptions struct { forwardWrittenMessage bool } -type expectedChargePointOptions struct { +type expectedChargingStationOptions struct { serverUrl string clientId string createChannelOnStart bool @@ -560,12 +568,47 @@ type expectedChargePointOptions struct { forwardWrittenMessage bool } -func setupDefaultCentralSystemHandlers(suite *OcppV2TestSuite, coreListener ocpp2.CSMSHandler, options expectedCentralSystemOptions) { +func setupDefaultCSMSHandlers(suite *OcppV2TestSuite, options expectedCSMSOptions, handlers... interface{}) { t := suite.T() - suite.csms.SetNewChargingStationHandler(func(chargePointId string) { - assert.Equal(t, options.clientId, chargePointId) + for _,h := range handlers { + switch h.(type) { + case MockCSMSAuthorizationHandler: + suite.csms.SetAuthorizationHandler(h.(MockCSMSAuthorizationHandler)) + case MockCSMSAvailabilityHandler: + suite.csms.SetAvailabilityHandler(h.(MockCSMSAvailabilityHandler)) + case MockCSMSDataHandler: + suite.csms.SetDataHandler(h.(MockCSMSDataHandler)) + case MockCSMSDiagnosticsHandler: + suite.csms.SetDiagnosticsHandler(h.(MockCSMSDiagnosticsHandler)) + case MockCSMSDisplayHandler: + suite.csms.SetDisplayHandler(h.(MockCSMSDisplayHandler)) + case MockCSMSFirmwareHandler: + suite.csms.SetFirmwareHandler(h.(MockCSMSFirmwareHandler)) + case MockCSMSIso15118Handler: + suite.csms.SetISO15118Handler(h.(MockCSMSIso15118Handler)) + case MockCSMSLocalAuthHandler: + suite.csms.SetLocalAuthListHandler(h.(MockCSMSLocalAuthHandler)) + case MockCSMSMeterHandler: + suite.csms.SetMeterHandler(h.(MockCSMSMeterHandler)) + case MockCSMSProvisioningHandler: + suite.csms.SetProvisioningHandler(h.(MockCSMSProvisioningHandler)) + case MockCSMSRemoteControlHandler: + suite.csms.SetRemoteControlHandler(h.(MockCSMSRemoteControlHandler)) + case MockCSMSReservationHandler: + suite.csms.SetReservationHandler(h.(MockCSMSReservationHandler)) + case MockCSMSSecurityHandler: + suite.csms.SetSecurityHandler(h.(MockCSMSSecurityHandler)) + case MockCSMSSmartChargingHandler: + suite.csms.SetSmartChargingHandler(h.(MockCSMSSmartChargingHandler)) + case MockCSMSTariffCostHandler: + suite.csms.SetTariffCostHandler(h.(MockCSMSTariffCostHandler)) + case MockCSMSTransactionsHandler: + suite.csms.SetTransactionsHandler(h.(MockChargingStationTransactionHandler)) + } + } + suite.csms.SetNewChargingStationHandler(func(chargingStationId string) { + assert.Equal(t, options.clientId, chargingStationId) }) - suite.csms.SetMessageHandler(coreListener) suite.mockWsServer.On("Start", mock.AnythingOfType("int"), mock.AnythingOfType("string")).Return(options.startReturnArgument) suite.mockWsServer.On("Write", mock.AnythingOfType("string"), mock.Anything).Return(options.writeReturnArgument).Run(func(args mock.Arguments) { clientId := args.String(0) @@ -584,9 +627,44 @@ func setupDefaultCentralSystemHandlers(suite *OcppV2TestSuite, coreListener ocpp }) } -func setupDefaultChargePointHandlers(suite *OcppV2TestSuite, coreListener ocpp2.ChargingStationHandler, options expectedChargePointOptions) { +func setupDefaultChargingStationHandlers(suite *OcppV2TestSuite, options expectedChargingStationOptions, handlers... interface{}) { t := suite.T() - suite.chargePoint.SetMessageHandler(coreListener) + for _, h := range handlers { + switch h.(type) { + case MockChargingStationAuthorizationHandler: + suite.chargingStation.SetAuthorizationHandler(h.(MockChargingStationAuthorizationHandler)) + case MockChargingStationAvailabilityHandler: + suite.chargingStation.SetAvailabilityHandler(h.(MockChargingStationAvailabilityHandler)) + case MockChargingStationDataHandler: + suite.chargingStation.SetDataHandler(h.(MockChargingStationDataHandler)) + case MockChargingStationDiagnosticsHandler: + suite.chargingStation.SetDiagnosticsHandler(h.(MockChargingStationDiagnosticsHandler)) + case MockChargingStationDisplayHandler: + suite.chargingStation.SetDisplayHandler(h.(MockChargingStationDisplayHandler)) + case MockChargingStationFirmwareHandler: + suite.chargingStation.SetFirmwareHandler(h.(MockChargingStationFirmwareHandler)) + case MockChargingStationIso15118Handler: + suite.chargingStation.SetISO15118Handler(h.(MockChargingStationIso15118Handler)) + case MockChargingStationLocalAuthHandler: + suite.chargingStation.SetLocalAuthListHandler(h.(MockChargingStationLocalAuthHandler)) + case MockChargingStationMeterHandler: + suite.chargingStation.SetMeterHandler(h.(MockChargingStationMeterHandler)) + case MockChargingStationProvisioningHandler: + suite.chargingStation.SetProvisioningHandler(h.(MockChargingStationProvisioningHandler)) + case MockChargingStationRemoteControlHandler: + suite.chargingStation.SetRemoteControlHandler(h.(MockChargingStationRemoteControlHandler)) + case MockChargingStationReservationHandler: + suite.chargingStation.SetReservationHandler(h.(MockChargingStationReservationHandler)) + case MockChargingStationSecurityHandler: + suite.chargingStation.SetSecurityHandler(h.(MockChargingStationSecurityHandler)) + case MockChargingStationSmartChargingHandler: + suite.chargingStation.SetSmartChargingHandler(h.(MockChargingStationSmartChargingHandler)) + case MockChargingStationTariffCostHandler: + suite.chargingStation.SetTariffCostHandler(h.(MockChargingStationTariffCostHandler)) + case MockChargingStationTransactionHandler: + suite.chargingStation.SetTransactionsHandler(h.(MockChargingStationTransactionHandler)) + } + } suite.mockWsClient.On("Start", mock.AnythingOfType("string")).Return(options.startReturnArgument).Run(func(args mock.Arguments) { u := args.String(0) assert.Equal(t, fmt.Sprintf("%s/%s", options.serverUrl, options.clientId), u) @@ -610,24 +688,23 @@ func setupDefaultChargePointHandlers(suite *OcppV2TestSuite, coreListener ocpp2. }) } -func assertDateTimeEquality(t *testing.T, expected ocpp2.DateTime, actual ocpp2.DateTime) { - assert.Equal(t, ocpp2.FormatTimestamp(expected.Time), ocpp2.FormatTimestamp(actual.Time)) +func assertDateTimeEquality(t *testing.T, expected *types.DateTime, actual *types.DateTime) { + assert.Equal(t, expected.FormatTimestamp(), actual.FormatTimestamp()) } -func testUnsupportedRequestFromChargePoint(suite *OcppV2TestSuite, request ocpp.Request, requestJson string, messageId string) { +func testUnsupportedRequestFromChargingStation(suite *OcppV2TestSuite, request ocpp.Request, requestJson string, messageId string, handlers... interface{}) { t := suite.T() wsId := "test_id" wsUrl := "someUrl" - expectedError := fmt.Sprintf("unsupported action %v on charge point, cannot send request", request.GetFeatureName()) - errorDescription := fmt.Sprintf("unsupported action %v on central system", request.GetFeatureName()) + expectedError := fmt.Sprintf("unsupported action %v on charging station, cannot send request", request.GetFeatureName()) + errorDescription := fmt.Sprintf("unsupported action %v on CSMS", request.GetFeatureName()) errorJson := fmt.Sprintf(`[4,"%v","%v","%v",null]`, messageId, ocppj.NotSupported, errorDescription) channel := NewMockWebSocket(wsId) - setupDefaultChargePointHandlers(suite, nil, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(errorJson), forwardWrittenMessage: false}) - coreListener := MockCentralSystemCoreListener{} - setupDefaultCentralSystemHandlers(suite, coreListener, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(errorJson), forwardWrittenMessage: true}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(errorJson), forwardWrittenMessage: false}) + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(errorJson), forwardWrittenMessage: true}, handlers...) resultChannel := make(chan bool, 1) - suite.ocppjChargePoint.SetErrorHandler(func(err *ocpp.Error, details interface{}) { + suite.ocppjClient.SetErrorHandler(func(err *ocpp.Error, details interface{}) { assert.Equal(t, messageId, err.MessageId) assert.Equal(t, ocppj.NotSupported, err.Code) assert.Equal(t, errorDescription, err.Description) @@ -636,53 +713,52 @@ func testUnsupportedRequestFromChargePoint(suite *OcppV2TestSuite, request ocpp. }) // Start suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) // Run request test - err = suite.chargePoint.SendRequestAsync(request, func(confirmation ocpp.Confirmation, err error) { + err = suite.chargingStation.SendRequestAsync(request, func(confirmation ocpp.Response, err error) { t.Fail() }) require.Error(t, err) assert.Equal(t, expectedError, err.Error()) // Run response test - suite.ocppjChargePoint.AddPendingRequest(messageId, request) + suite.ocppjClient.AddPendingRequest(messageId, request) err = suite.mockWsServer.MessageHandler(channel, []byte(requestJson)) require.Nil(t, err) result := <-resultChannel assert.True(t, result) } -func testUnsupportedRequestFromCentralSystem(suite *OcppV2TestSuite, request ocpp.Request, requestJson string, messageId string) { +func testUnsupportedRequestFromCentralSystem(suite *OcppV2TestSuite, request ocpp.Request, requestJson string, messageId string, handlers... interface{}) { t := suite.T() wsId := "test_id" wsUrl := "someUrl" - expectedError := fmt.Sprintf("unsupported action %v on central system, cannot send request", request.GetFeatureName()) - errorDescription := fmt.Sprintf("unsupported action %v on charge point", request.GetFeatureName()) + expectedError := fmt.Sprintf("unsupported action %v on CSMS, cannot send request", request.GetFeatureName()) + errorDescription := fmt.Sprintf("unsupported action %v on charging station", request.GetFeatureName()) errorJson := fmt.Sprintf(`[4,"%v","%v","%v",null]`, messageId, ocppj.NotSupported, errorDescription) channel := NewMockWebSocket(wsId) - setupDefaultCentralSystemHandlers(suite, nil, expectedCentralSystemOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: false}) - coreListener := MockChargePointCoreListener{} - setupDefaultChargePointHandlers(suite, coreListener, expectedChargePointOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(errorJson), forwardWrittenMessage: true}) - suite.ocppjCentralSystem.SetErrorHandler(func(chargePointId string, err *ocpp.Error, details interface{}) { + setupDefaultCSMSHandlers(suite, expectedCSMSOptions{clientId: wsId, rawWrittenMessage: []byte(requestJson), forwardWrittenMessage: false}) + setupDefaultChargingStationHandlers(suite, expectedChargingStationOptions{serverUrl: wsUrl, clientId: wsId, createChannelOnStart: true, channel: channel, rawWrittenMessage: []byte(errorJson), forwardWrittenMessage: true}, handlers...) + suite.ocppjServer.SetErrorHandler(func(chargingStationId string, err *ocpp.Error, details interface{}) { assert.Equal(t, messageId, err.MessageId) - assert.Equal(t, wsId, chargePointId) + assert.Equal(t, wsId, chargingStationId) assert.Equal(t, ocppj.NotSupported, err.Code) assert.Equal(t, errorDescription, err.Description) assert.Nil(t, details) }) // Start suite.csms.Start(8887, "somePath") - err := suite.chargePoint.Start(wsUrl) + err := suite.chargingStation.Start(wsUrl) require.Nil(t, err) // Run request test - err = suite.csms.SendRequestAsync(wsId, request, func(confirmation ocpp.Confirmation, err error) { + err = suite.csms.SendRequestAsync(wsId, request, func(response ocpp.Response, err error) { t.Fail() }) require.Error(t, err) assert.Equal(t, expectedError, err.Error()) // Run response test - suite.ocppjCentralSystem.AddPendingRequest(messageId, request) + suite.ocppjServer.AddPendingRequest(messageId, request) err = suite.mockWsClient.MessageHandler([]byte(requestJson)) assert.Nil(t, err) } @@ -692,20 +768,10 @@ type GenericTestEntry struct { ExpectedValid bool } -type RequestTestEntry struct { - Request ocpp.Request - ExpectedValid bool -} - -type ConfirmationTestEntry struct { - Confirmation ocpp.Confirmation - ExpectedValid bool -} - // TODO: pass expected error value for improved validation and error message func ExecuteGenericTestTable(t *testing.T, testTable []GenericTestEntry) { for _, testCase := range testTable { - err := ocpp2.Validate.Struct(testCase.Element) + err := types.Validate.Struct(testCase.Element) if err != nil { assert.Equal(t, testCase.ExpectedValid, false, err.Error()) } else { @@ -715,13 +781,14 @@ func ExecuteGenericTestTable(t *testing.T, testTable []GenericTestEntry) { } // ---------------------- TESTS ---------------------- + type OcppV2TestSuite struct { suite.Suite - ocppjChargePoint *ocppj.ChargePoint - ocppjCentralSystem *ocppj.CentralSystem + ocppjClient *ocppj.Client + ocppjServer *ocppj.Server mockWsServer *MockWebsocketServer mockWsClient *MockWebsocketClient - chargePoint ocpp2.ChargingStation + chargingStation ocpp2.ChargingStation csms ocpp2.CSMS messageIdGenerator TestRandomIdGenerator } @@ -737,20 +804,31 @@ func (testGenerator *TestRandomIdGenerator) generateId() string { var defaultMessageId = "1234" func (suite *OcppV2TestSuite) SetupTest() { - coreProfile := ocpp2.CoreProfile - //localAuthListProfile := ocpp2.LocalAuthListProfile - //firmwareProfile := ocpp2.FirmwareManagementProfile - //reservationProfile := ocpp2.ReservationProfile - //remoteTriggerProfile := ocpp2.RemoteTriggerProfile - //smartChargingProfile := ocpp2.SmartChargingProfile + securityProfile := security.Profile + provisioningProfile := provisioning.Profile + authProfile := authorization.Profile + availabilityProfile := availability.Profile + reservationProfile := reservation.Profile + diagnosticsProfile := diagnostics.Profile + dataProfile := data.Profile + displayProfile := display.Profile + firmwareProfile := firmware.Profile + isoProfile := iso15118.Profile + localAuthProfile := localauth.Profile + meterProfile := meter.Profile + remoteProfile := remotecontrol.Profile + smartChargingProfile := smartcharging.Profile + tariffProfile := tariffcost.Profile + transactionsProfile := transactions.Profile + // TODO: init additional profiles mockClient := MockWebsocketClient{} mockServer := MockWebsocketServer{} suite.mockWsClient = &mockClient suite.mockWsServer = &mockServer - suite.ocppjChargePoint = ocppj.NewChargePoint("test_id", suite.mockWsClient, coreProfile) - suite.ocppjCentralSystem = ocppj.NewCentralSystem(suite.mockWsServer, coreProfile) - suite.chargePoint = ocpp2.NewChargingStation("test_id", suite.ocppjChargePoint, suite.mockWsClient) - suite.csms = ocpp2.NewCSMS(suite.ocppjCentralSystem, suite.mockWsServer) + suite.ocppjClient = ocppj.NewClient("test_id", suite.mockWsClient, securityProfile, provisioningProfile, authProfile, availabilityProfile, reservationProfile, diagnosticsProfile, dataProfile, displayProfile, firmwareProfile, isoProfile, localAuthProfile, meterProfile, remoteProfile, smartChargingProfile, tariffProfile, transactionsProfile) + suite.ocppjServer = ocppj.NewServer(suite.mockWsServer, securityProfile, provisioningProfile, authProfile, availabilityProfile, reservationProfile, diagnosticsProfile, dataProfile, displayProfile, firmwareProfile, isoProfile, localAuthProfile, meterProfile, remoteProfile, smartChargingProfile, tariffProfile, transactionsProfile) + suite.chargingStation = ocpp2.NewChargingStation("test_id", suite.ocppjClient, suite.mockWsClient) + suite.csms = ocpp2.NewCSMS(suite.ocppjServer, suite.mockWsServer) suite.messageIdGenerator = TestRandomIdGenerator{generator: func() string { return defaultMessageId }} diff --git a/ocppj/central_system.go b/ocppj/central_system.go deleted file mode 100644 index f1932705..00000000 --- a/ocppj/central_system.go +++ /dev/null @@ -1,160 +0,0 @@ -package ocppj - -import ( - "github.com/lorenzodonini/ocpp-go/ocpp" - "github.com/lorenzodonini/ocpp-go/ws" - "github.com/pkg/errors" - log "github.com/sirupsen/logrus" -) - -type CentralSystem struct { - Endpoint - server ws.WsServer - newChargePointHandler func(chargePointId string) - disconnectedChargePointHandler func(chargePointId string) - requestHandler func(chargePointId string, request ocpp.Request, requestId string, action string) - confirmationHandler func(chargePointId string, confirmation ocpp.Confirmation, requestId string) - errorHandler func(chargePointId string, err *ocpp.Error, details interface{}) - clientPendingMessages map[string]string -} - -func NewCentralSystem(wsServer ws.WsServer, profiles ...*ocpp.Profile) *CentralSystem { - endpoint := Endpoint{pendingRequests: map[string]ocpp.Request{}} - for _, profile := range profiles { - endpoint.AddProfile(profile) - } - if wsServer != nil { - return &CentralSystem{Endpoint: endpoint, server: wsServer, clientPendingMessages: map[string]string{}} - } else { - return &CentralSystem{Endpoint: endpoint, server: &ws.Server{}, clientPendingMessages: map[string]string{}} - } -} - -func (centralSystem *CentralSystem) SetRequestHandler(handler func(chargePointId string, request ocpp.Request, requestId string, action string)) { - centralSystem.requestHandler = handler -} - -func (centralSystem *CentralSystem) SetConfirmationHandler(handler func(chargePointId string, confirmation ocpp.Confirmation, requestId string)) { - centralSystem.confirmationHandler = handler -} - -func (centralSystem *CentralSystem) SetErrorHandler(handler func(chargePointId string, err *ocpp.Error, details interface{})) { - centralSystem.errorHandler = handler -} - -func (centralSystem *CentralSystem) SetNewChargePointHandler(handler func(chargePointId string)) { - centralSystem.newChargePointHandler = handler -} - -func (centralSystem *CentralSystem) SetDisconnectedChargePointHandler(handler func(chargePointId string)) { - centralSystem.disconnectedChargePointHandler = handler -} - -func (centralSystem *CentralSystem) Start(listenPort int, listenPath string) { - // Set internal message handler - centralSystem.server.SetNewClientHandler(func(ws ws.Channel) { - if centralSystem.newChargePointHandler != nil { - centralSystem.newChargePointHandler(ws.GetID()) - } - }) - centralSystem.server.SetDisconnectedClientHandler(func(ws ws.Channel) { - if centralSystem.disconnectedChargePointHandler != nil { - centralSystem.disconnectedChargePointHandler(ws.GetID()) - } - }) - centralSystem.server.SetMessageHandler(centralSystem.ocppMessageHandler) - // Serve & run - // TODO: return error? - centralSystem.server.Start(listenPort, listenPath) -} - -func (centralSystem *CentralSystem) Stop() { - centralSystem.server.Stop() - centralSystem.clearPendingRequests() -} - -func (centralSystem *CentralSystem) SendRequest(chargePointId string, request ocpp.Request) error { - err := Validate.Struct(request) - if err != nil { - return err - } - req, ok := centralSystem.clientPendingMessages[chargePointId] - if ok { - // Cannot send. Protocol is based on response-confirmation - return errors.Errorf("There already is a pending request %v for client %v. Cannot send a further one before receiving a confirmation first", req, chargePointId) - } - call, err := centralSystem.CreateCall(request.(ocpp.Request)) - if err != nil { - return err - } - jsonMessage, err := call.MarshalJSON() - if err != nil { - return err - } - centralSystem.clientPendingMessages[chargePointId] = call.UniqueId - err = centralSystem.server.Write(chargePointId, []byte(jsonMessage)) - if err != nil { - // Clear pending request - centralSystem.DeletePendingRequest(call.GetUniqueId()) - delete(centralSystem.clientPendingMessages, chargePointId) - } - return err -} - -func (centralSystem *CentralSystem) SendConfirmation(chargePointId string, requestId string, confirmation ocpp.Confirmation) error { - err := Validate.Struct(confirmation) - if err != nil { - return err - } - callResult, err := centralSystem.CreateCallResult(confirmation, requestId) - if err != nil { - return err - } - jsonMessage, err := callResult.MarshalJSON() - if err != nil { - return err - } - return centralSystem.server.Write(chargePointId, []byte(jsonMessage)) -} - -func (centralSystem *CentralSystem) SendError(chargePointId string, requestId string, errorCode ocpp.ErrorCode, description string, details interface{}) error { - callError := centralSystem.CreateCallError(requestId, errorCode, description, details) - err := Validate.Struct(callError) - if err != nil { - return err - } - jsonMessage, err := callError.MarshalJSON() - if err != nil { - return err - } - return centralSystem.server.Write(chargePointId, []byte(jsonMessage)) -} - -func (centralSystem *CentralSystem) ocppMessageHandler(wsChannel ws.Channel, data []byte) error { - parsedJson := ParseRawJsonMessage(data) - message, err := centralSystem.ParseMessage(parsedJson) - if err != nil { - if err.MessageId != "" { - err2 := centralSystem.SendError(wsChannel.GetID(), err.MessageId, err.Code, err.Description, nil) - if err2 != nil { - return err2 - } - } - log.Error(err) - return err - } - switch message.GetMessageTypeId() { - case CALL: - call := message.(*Call) - centralSystem.requestHandler(wsChannel.GetID(), call.Payload, call.UniqueId, call.Action) - case CALL_RESULT: - callResult := message.(*CallResult) - delete(centralSystem.clientPendingMessages, wsChannel.GetID()) - centralSystem.confirmationHandler(wsChannel.GetID(), callResult.Payload, callResult.UniqueId) - case CALL_ERROR: - callError := message.(*CallError) - delete(centralSystem.clientPendingMessages, wsChannel.GetID()) - centralSystem.errorHandler(wsChannel.GetID(), ocpp.NewError(callError.ErrorCode, callError.ErrorDescription, callError.UniqueId), callError.ErrorDetails) - } - return nil -} diff --git a/ocppj/charge_point.go b/ocppj/charge_point.go deleted file mode 100644 index 09a57284..00000000 --- a/ocppj/charge_point.go +++ /dev/null @@ -1,149 +0,0 @@ -package ocppj - -import ( - "fmt" - "github.com/lorenzodonini/ocpp-go/ocpp" - "github.com/lorenzodonini/ocpp-go/ws" - "github.com/pkg/errors" - log "github.com/sirupsen/logrus" -) - -type ChargePoint struct { - Endpoint - client ws.WsClient - Id string - requestHandler func(request ocpp.Request, requestId string, action string) - confirmationHandler func(confirmation ocpp.Confirmation, requestId string) - errorHandler func(err *ocpp.Error, details interface{}) - hasPendingRequest bool -} - -func NewChargePoint(id string, wsClient ws.WsClient, profiles ...*ocpp.Profile) *ChargePoint { - endpoint := Endpoint{pendingRequests: map[string]ocpp.Request{}} - for _, profile := range profiles { - endpoint.AddProfile(profile) - } - if wsClient != nil { - return &ChargePoint{Endpoint: endpoint, client: wsClient, Id: id, hasPendingRequest: false} - } else { - return &ChargePoint{Endpoint: endpoint, client: ws.NewClient(), Id: id, hasPendingRequest: false} - } -} - -func (chargePoint *ChargePoint) SetRequestHandler(handler func(request ocpp.Request, requestId string, action string)) { - chargePoint.requestHandler = handler -} - -func (chargePoint *ChargePoint) SetConfirmationHandler(handler func(confirmation ocpp.Confirmation, requestId string)) { - chargePoint.confirmationHandler = handler -} - -func (chargePoint *ChargePoint) SetErrorHandler(handler func(err *ocpp.Error, details interface{})) { - chargePoint.errorHandler = handler -} - -// Connects to the given centralSystemUrl and starts running the I/O loop for the underlying connection. -// The write routine runs on a separate goroutine, while the read routine runs on the caller's routine. -// This means, the function is blocking for as long as the ChargePoint is connected to the CentralSystem. -// Whenever the connection is ended, the function returns. -// Call this function in a separate goroutine, to perform other operations on the main thread. -// -// An error may be returned, if the connection failed or if it broke unexpectedly. -func (chargePoint *ChargePoint) Start(centralSystemUrl string) error { - // Set internal message handler - chargePoint.client.SetMessageHandler(chargePoint.ocppMessageHandler) - // Connect & run - fullUrl := fmt.Sprintf("%v/%v", centralSystemUrl, chargePoint.Id) - return chargePoint.client.Start(fullUrl) -} - -func (chargePoint *ChargePoint) Stop() { - chargePoint.client.Stop() - chargePoint.clearPendingRequests() - chargePoint.hasPendingRequest = false -} - -func (chargePoint *ChargePoint) SendRequest(request ocpp.Request) error { - err := Validate.Struct(request) - if err != nil { - return err - } - if chargePoint.hasPendingRequest { - // Cannot send. Protocol is based on response-confirmation - return errors.Errorf("There already is a pending request. Cannot send a further one before receiving a confirmation first") - } - call, err := chargePoint.CreateCall(request.(ocpp.Request)) - if err != nil { - return err - } - jsonMessage, err := call.MarshalJSON() - if err != nil { - return err - } - chargePoint.hasPendingRequest = true - err = chargePoint.client.Write([]byte(jsonMessage)) - if err != nil { - // Clear pending request - chargePoint.DeletePendingRequest(call.GetUniqueId()) - chargePoint.hasPendingRequest = false - } - return err -} - -func (chargePoint *ChargePoint) SendConfirmation(requestId string, confirmation ocpp.Confirmation) error { - err := Validate.Struct(confirmation) - if err != nil { - return err - } - callResult, err := chargePoint.CreateCallResult(confirmation, requestId) - if err != nil { - return err - } - jsonMessage, err := callResult.MarshalJSON() - if err != nil { - return err - } - return chargePoint.client.Write([]byte(jsonMessage)) -} - -func (chargePoint *ChargePoint) SendError(requestId string, errorCode ocpp.ErrorCode, description string, details interface{}) error { - callError := chargePoint.CreateCallError(requestId, errorCode, description, details) - err := Validate.Struct(callError) - if err != nil { - return err - } - jsonMessage, err := callError.MarshalJSON() - if err != nil { - return err - } - return chargePoint.client.Write([]byte(jsonMessage)) -} - -func (chargePoint *ChargePoint) ocppMessageHandler(data []byte) error { - parsedJson := ParseRawJsonMessage(data) - message, err := chargePoint.ParseMessage(parsedJson) - if err != nil { - if err.MessageId != "" { - err2 := chargePoint.SendError(err.MessageId, err.Code, err.Description, nil) - if err2 != nil { - return err2 - } - } - log.Print(err) - return err - } - switch message.GetMessageTypeId() { - case CALL: - call := message.(*Call) - chargePoint.requestHandler(call.Payload, call.UniqueId, call.Action) - case CALL_RESULT: - callResult := message.(*CallResult) - chargePoint.hasPendingRequest = false - chargePoint.confirmationHandler(callResult.Payload, callResult.UniqueId) - case CALL_ERROR: - callError := message.(*CallError) - chargePoint.hasPendingRequest = false - chargePoint.errorHandler(ocpp.NewError(callError.ErrorCode, callError.ErrorDescription, callError.UniqueId), callError.ErrorDetails) - } - return nil -} diff --git a/ocppj/client.go b/ocppj/client.go new file mode 100644 index 00000000..05fe4426 --- /dev/null +++ b/ocppj/client.go @@ -0,0 +1,189 @@ +package ocppj + +import ( + "fmt" + "github.com/lorenzodonini/ocpp-go/ocpp" + "github.com/lorenzodonini/ocpp-go/ws" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" +) + +// The endpoint initiating the connection to an OCPP server, in an OCPP-J topology. +// During message exchange, the two roles may be reversed (depending on the message direction), but a client struct remains associated to a charge point/charging station. +type Client struct { + Endpoint + client ws.WsClient + Id string + requestHandler func(request ocpp.Request, requestId string, action string) + responseHandler func(response ocpp.Response, requestId string) + errorHandler func(err *ocpp.Error, details interface{}) + hasPendingRequest bool +} + +// Creates a new Client endpoint. +// Requires a unique client ID, a websocket client and a list of profiles (optional). +func NewClient(id string, wsClient ws.WsClient, profiles ...*ocpp.Profile) *Client { + endpoint := Endpoint{pendingRequests: map[string]ocpp.Request{}} + for _, profile := range profiles { + endpoint.AddProfile(profile) + } + if wsClient != nil { + return &Client{Endpoint: endpoint, client: wsClient, Id: id, hasPendingRequest: false} + } else { + return &Client{Endpoint: endpoint, client: ws.NewClient(), Id: id, hasPendingRequest: false} + } +} + +// Registers a handler for incoming requests. +func (c *Client) SetRequestHandler(handler func(request ocpp.Request, requestId string, action string)) { + c.requestHandler = handler +} + +// Registers a handler for incoming responses. +func (c *Client) SetResponseHandler(handler func(response ocpp.Response, requestId string)) { + c.responseHandler = handler +} + +// Registers a handler for incoming error messages. +func (c *Client) SetErrorHandler(handler func(err *ocpp.Error, details interface{})) { + c.errorHandler = handler +} + +// Connects to the given serverURL and starts running the I/O loop for the underlying connection. +// The write routine runs on a separate goroutine, while the read routine runs on the caller's routine. +// This means, the function is blocking for as long as the Client is connected to the Server. +// +// Whenever the connection is ended, the function returns. +// +// Call this function in a separate goroutine, to perform other operations on the main thread. +// +// An error may be returned, if the connection failed or if it broke unexpectedly. +func (c *Client) Start(serverURL string) error { + // Set internal message handler + c.client.SetMessageHandler(c.ocppMessageHandler) + // Connect & run + fullUrl := fmt.Sprintf("%v/%v", serverURL, c.Id) + return c.client.Start(fullUrl) +} + +// Stops the client. +// The underlying I/O loop is stopped and all pending requests are cleared. +func (c *Client) Stop() { + c.client.Stop() + c.clearPendingRequests() + c.hasPendingRequest = false +} + +// Sends an OCPP Request to the server. +// +// Returns an error in the following cases: +// +// - message validation fails (request is malformed) +// +// - another request is already pending +// +// - the endpoint doesn't support the feature +// +// - a network error occurred +func (c *Client) SendRequest(request ocpp.Request) error { + err := Validate.Struct(request) + if err != nil { + return err + } + if c.hasPendingRequest { + // Cannot send. Protocol is based on request-response + return errors.Errorf("There already is a pending request. Cannot send a further one before receiving a response first") + } + call, err := c.CreateCall(request.(ocpp.Request)) + if err != nil { + return err + } + jsonMessage, err := call.MarshalJSON() + if err != nil { + return err + } + c.hasPendingRequest = true + err = c.client.Write([]byte(jsonMessage)) + if err != nil { + // Clear pending request + c.DeletePendingRequest(call.GetUniqueId()) + c.hasPendingRequest = false + } + return err +} + +// Sends an OCPP Response to the server. +// The requestID parameter is required and identifies the previously received request. +// +// Returns an error in the following cases: +// +// - message validation fails (response is malformed) +// +// - the endpoint doesn't support the feature +// +// - a network error occurred +func (c *Client) SendResponse(requestId string, response ocpp.Response) error { + err := Validate.Struct(response) + if err != nil { + return err + } + callResult, err := c.CreateCallResult(response, requestId) + if err != nil { + return err + } + jsonMessage, err := callResult.MarshalJSON() + if err != nil { + return err + } + return c.client.Write([]byte(jsonMessage)) +} + +// Sends an OCPP Error to the server. +// The requestID parameter is required and identifies the previously received request. +// +// Returns an error in the following cases: +// +// - message validation fails (error is malformed) +// +// - a network error occurred +func (c *Client) SendError(requestId string, errorCode ocpp.ErrorCode, description string, details interface{}) error { + callError := c.CreateCallError(requestId, errorCode, description, details) + err := Validate.Struct(callError) + if err != nil { + return err + } + jsonMessage, err := callError.MarshalJSON() + if err != nil { + return err + } + return c.client.Write([]byte(jsonMessage)) +} + +func (c *Client) ocppMessageHandler(data []byte) error { + parsedJson := ParseRawJsonMessage(data) + message, err := c.ParseMessage(parsedJson) + if err != nil { + if err.MessageId != "" { + err2 := c.SendError(err.MessageId, err.Code, err.Description, nil) + if err2 != nil { + return err2 + } + } + log.Print(err) + return err + } + switch message.GetMessageTypeId() { + case CALL: + call := message.(*Call) + c.requestHandler(call.Payload, call.UniqueId, call.Action) + case CALL_RESULT: + callResult := message.(*CallResult) + c.hasPendingRequest = false + c.responseHandler(callResult.Payload, callResult.UniqueId) + case CALL_ERROR: + callError := message.(*CallError) + c.hasPendingRequest = false + c.errorHandler(ocpp.NewError(callError.ErrorCode, callError.ErrorDescription, callError.UniqueId), callError.ErrorDetails) + } + return nil +} diff --git a/ocppj/ocppj.go b/ocppj/ocppj.go index b003cc0a..2c593ca0 100644 --- a/ocppj/ocppj.go +++ b/ocppj/ocppj.go @@ -1,3 +1,4 @@ +// Contains an implementation of OCPP message dispatcher via JSON over WebSocket. package ocppj import ( @@ -11,13 +12,14 @@ import ( "reflect" ) +// The validator, used for validating incoming/outgoing OCPP messages. var Validate = validator.New() func init() { _ = Validate.RegisterValidation("errorCode", IsErrorCodeValid) } -// -------------------- Message -------------------- +// MessageType identifies the type of message exchanged between two OCPP endpoints. type MessageType int const ( @@ -26,8 +28,11 @@ const ( CALL_ERROR MessageType = 4 ) +// An OCPP-J message. type Message interface { + // Returns the message type identifier of the message. GetMessageTypeId() MessageType + // Returns the unique identifier of the message. GetUniqueId() string json.Marshaler } @@ -43,6 +48,8 @@ func SetMessageIdGenerator(generator func() string) { } // -------------------- Call -------------------- + +// An OCPP-J Call message, containing an OCPP Request. type Call struct { Message `validate:"-"` MessageTypeId MessageType `json:"messageTypeId" validate:"required,eq=2"` @@ -69,11 +76,13 @@ func (call *Call) MarshalJSON() ([]byte, error) { } // -------------------- Call Result -------------------- + +// An OCPP-J CallResult message, containing an OCPP Response. type CallResult struct { Message - MessageTypeId MessageType `json:"messageTypeId" validate:"required,eq=3"` - UniqueId string `json:"uniqueId" validate:"required,max=36"` - Payload ocpp.Confirmation `json:"payload" validate:"required"` + MessageTypeId MessageType `json:"messageTypeId" validate:"required,eq=3"` + UniqueId string `json:"uniqueId" validate:"required,max=36"` + Payload ocpp.Response `json:"payload" validate:"required"` } func (callResult *CallResult) GetMessageTypeId() MessageType { @@ -93,6 +102,8 @@ func (callResult *CallResult) MarshalJSON() ([]byte, error) { } // -------------------- Call Error -------------------- + +// An OCPP-J CallError message, containing an OCPP Error. type CallError struct { Message MessageTypeId MessageType `json:"messageTypeId" validate:"required,eq=4"` @@ -121,28 +132,32 @@ func (callError *CallError) MarshalJSON() ([]byte, error) { } const ( - NotImplemented ocpp.ErrorCode = "NotImplemented" - NotSupported ocpp.ErrorCode = "NotSupported" - InternalError ocpp.ErrorCode = "InternalError" - ProtocolError ocpp.ErrorCode = "ProtocolError" - SecurityError ocpp.ErrorCode = "SecurityError" - FormationViolation ocpp.ErrorCode = "FormationViolation" - PropertyConstraintViolation ocpp.ErrorCode = "PropertyConstraintViolation" - OccurrenceConstraintViolation ocpp.ErrorCode = "OccurrenceConstraintViolation" - TypeConstraintViolation ocpp.ErrorCode = "TypeConstraintViolation" - GenericError ocpp.ErrorCode = "GenericError" + NotImplemented ocpp.ErrorCode = "NotImplemented" // Requested Action is not known by receiver. + NotSupported ocpp.ErrorCode = "NotSupported" // Requested Action is recognized but not supported by the receiver. + InternalError ocpp.ErrorCode = "InternalError" // An internal error occurred and the receiver was not able to process the requested Action successfully. + MessageTypeNotSupported ocpp.ErrorCode = "MessageTypeNotSupported" // A message with an Message Type Number received that is not supported by this implementation. + ProtocolError ocpp.ErrorCode = "ProtocolError" // Payload for Action is incomplete. + SecurityError ocpp.ErrorCode = "SecurityError" // During the processing of Action a security issue occurred preventing receiver from completing the Action successfully. + FormationViolation ocpp.ErrorCode = "FormationViolation" // Payload for Action is syntactically incorrect or not conform the PDU structure for Action. + PropertyConstraintViolation ocpp.ErrorCode = "PropertyConstraintViolation" // Payload is syntactically correct but at least one field contains an invalid value. + OccurrenceConstraintViolation ocpp.ErrorCode = "OccurrenceConstraintViolation" // Payload for Action is syntactically correct but at least one of the fields violates occurrence constraints. + TypeConstraintViolation ocpp.ErrorCode = "TypeConstraintViolation" // Payload for Action is syntactically correct but at least one of the fields violates data type constraints (e.g. “somestring”: 12). + GenericError ocpp.ErrorCode = "GenericError" // Any other error not covered by the previous ones. ) func IsErrorCodeValid(fl validator.FieldLevel) bool { code := ocpp.ErrorCode(fl.Field().String()) switch code { - case NotImplemented, NotSupported, InternalError, ProtocolError, SecurityError, FormationViolation, PropertyConstraintViolation, OccurrenceConstraintViolation, TypeConstraintViolation, GenericError: + case NotImplemented, NotSupported, InternalError, MessageTypeNotSupported, ProtocolError, SecurityError, FormationViolation, PropertyConstraintViolation, OccurrenceConstraintViolation, TypeConstraintViolation, GenericError: return true } return false } // -------------------- Logic -------------------- + +// Unmarshals an OCPP-J json object from a byte array. +// Returns the array of elements contained in the message. func ParseRawJsonMessage(dataJson []byte) []interface{} { var arr []interface{} err := json.Unmarshal(dataJson, &arr) @@ -153,6 +168,8 @@ func ParseRawJsonMessage(dataJson []byte) []interface{} { return arr } +// Unmarshals an OCPP-J json object from a JSON string. +// Returns the array of elements contained in the message. func ParseJsonMessage(dataJson string) []interface{} { rawJson := []byte(dataJson) return ParseRawJsonMessage(rawJson) @@ -202,15 +219,21 @@ func errorFromValidation(validationErrors validator.ValidationErrors, messageId } // -------------------- Endpoint -------------------- + +// An OCPP-J endpoint is one of the two entities taking part in the communication. +// The endpoint keeps state for supported OCPP profiles and pending requests. type Endpoint struct { Profiles []*ocpp.Profile pendingRequests map[string]ocpp.Request } +// Adds support for a new profile on the endpoint. func (endpoint *Endpoint) AddProfile(profile *ocpp.Profile) { endpoint.Profiles = append(endpoint.Profiles, profile) } +// Retrieves a profile for the given profile name. +// Returns a false flag in case no profile matching the specified name was found. func (endpoint *Endpoint) GetProfile(name string) (*ocpp.Profile, bool) { for _, p := range endpoint.Profiles { if p.Name == name { @@ -220,6 +243,8 @@ func (endpoint *Endpoint) GetProfile(name string) (*ocpp.Profile, bool) { return nil, false } +// Retrieves a profile for a given feature. +// Returns a false flag in case no profile supporting the specified feature was found. func (endpoint *Endpoint) GetProfileForFeature(featureName string) (*ocpp.Profile, bool) { for _, p := range endpoint.Profiles { if p.SupportsFeature(featureName) { @@ -229,15 +254,20 @@ func (endpoint *Endpoint) GetProfileForFeature(featureName string) (*ocpp.Profil return nil, false } +// Sets a Request as pending on the endpoint. Requests are considered pending until a response was received. +// The function expects a message unique ID and the Request. func (endpoint *Endpoint) AddPendingRequest(id string, request ocpp.Request) { endpoint.pendingRequests[id] = request } +// Retrieves a pending Request, using the message ID. +// If no request for the passed message ID is found, a false flag is returned. func (endpoint *Endpoint) GetPendingRequest(id string) (ocpp.Request, bool) { request, ok := endpoint.pendingRequests[id] return request, ok } +// Deletes a pending Request from the endpoint, using the message ID. func (endpoint *Endpoint) DeletePendingRequest(id string) { delete(endpoint.pendingRequests, id) } @@ -260,7 +290,7 @@ func parseRawJsonRequest(raw interface{}, requestType reflect.Type) (ocpp.Reques return result, nil } -func parseRawJsonConfirmation(raw interface{}, confirmationType reflect.Type) (ocpp.Confirmation, error) { +func parseRawJsonConfirmation(raw interface{}, confirmationType reflect.Type) (ocpp.Response, error) { bytes, err := json.Marshal(raw) if err != nil { return nil, err @@ -270,10 +300,13 @@ func parseRawJsonConfirmation(raw interface{}, confirmationType reflect.Type) (o if err != nil { return nil, err } - result := confirmation.(ocpp.Confirmation) + result := confirmation.(ocpp.Response) return result, nil } +// Parses an OCPP-J message. The function expects an array of elements, as contained in the JSON message. +// +// Pending requests are automatically cleared, in case the received message is a CallResponse or CallError. func (endpoint *Endpoint) ParseMessage(arr []interface{}) (Message, *ocpp.Error) { // Checking message fields if len(arr) < 3 { @@ -320,7 +353,7 @@ func (endpoint *Endpoint) ParseMessage(arr []interface{}) (Message, *ocpp.Error) return nil, nil } profile, _ := endpoint.GetProfileForFeature(request.GetFeatureName()) - confirmation, err := profile.ParseConfirmation(request.GetFeatureName(), arr[2], parseRawJsonConfirmation) + confirmation, err := profile.ParseResponse(request.GetFeatureName(), arr[2], parseRawJsonConfirmation) if err != nil { return nil, ocpp.NewError(FormationViolation, err.Error(), uniqueId) } @@ -359,10 +392,12 @@ func (endpoint *Endpoint) ParseMessage(arr []interface{}) (Message, *ocpp.Error) } return &callError, nil } else { - return nil, ocpp.NewError(FormationViolation, fmt.Sprintf("Invalid message type ID %v", typeId), uniqueId) + return nil, ocpp.NewError(MessageTypeNotSupported, fmt.Sprintf("Invalid message type ID %v", typeId), uniqueId) } } +// Creates a Call message, given an OCPP request. A unique ID for the message is automatically generated. +// Returns an error in case the request's feature is not supported on this endpoint. func (endpoint *Endpoint) CreateCall(request ocpp.Request) (*Call, error) { action := request.GetFeatureName() profile, _ := endpoint.GetProfileForFeature(action) @@ -384,7 +419,9 @@ func (endpoint *Endpoint) CreateCall(request ocpp.Request) (*Call, error) { return &call, nil } -func (endpoint *Endpoint) CreateCallResult(confirmation ocpp.Confirmation, uniqueId string) (*CallResult, error) { +// Creates a CallResult message, given an OCPP response and the message's unique ID. +// Returns an error in case the response's feature is not supported on this endpoint. +func (endpoint *Endpoint) CreateCallResult(confirmation ocpp.Response, uniqueId string) (*CallResult, error) { action := confirmation.GetFeatureName() profile, _ := endpoint.GetProfileForFeature(action) if profile == nil { @@ -402,6 +439,7 @@ func (endpoint *Endpoint) CreateCallResult(confirmation ocpp.Confirmation, uniqu return &callResult, nil } +// Creates a CallError message, given the message's unique ID and the error. func (endpoint *Endpoint) CreateCallError(uniqueId string, code ocpp.ErrorCode, description string, details interface{}) *CallError { callError := CallError{ MessageTypeId: CALL_ERROR, diff --git a/ocppj/server.go b/ocppj/server.go new file mode 100644 index 00000000..df9729bb --- /dev/null +++ b/ocppj/server.go @@ -0,0 +1,206 @@ +package ocppj + +import ( + "github.com/lorenzodonini/ocpp-go/ocpp" + "github.com/lorenzodonini/ocpp-go/ws" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" +) + +// The endpoint waiting for incoming connections from OCPP clients, in an OCPP-J topology. +// During message exchange, the two roles may be reversed (depending on the message direction), but a server struct remains associated to a central system. +type Server struct { + Endpoint + server ws.WsServer + newClientHandler func(clientID string) + disconnectedClientHandler func(clientID string) + requestHandler func(clientID string, request ocpp.Request, requestId string, action string) + responseHandler func(clientID string, response ocpp.Response, requestId string) + errorHandler func(clientID string, err *ocpp.Error, details interface{}) + clientPendingMessages map[string]string +} + +// Creates a new Server endpoint. +// Requires a a websocket server and a list of profiles (optional). +func NewServer(wsServer ws.WsServer, profiles ...*ocpp.Profile) *Server { + endpoint := Endpoint{pendingRequests: map[string]ocpp.Request{}} + for _, profile := range profiles { + endpoint.AddProfile(profile) + } + if wsServer != nil { + return &Server{Endpoint: endpoint, server: wsServer, clientPendingMessages: map[string]string{}} + } else { + return &Server{Endpoint: endpoint, server: &ws.Server{}, clientPendingMessages: map[string]string{}} + } +} + +// Registers a handler for incoming requests. +func (s *Server) SetRequestHandler(handler func(clientID string, request ocpp.Request, requestId string, action string)) { + s.requestHandler = handler +} + +// Registers a handler for incoming responses. +func (s *Server) SetResponseHandler(handler func(clientID string, response ocpp.Response, requestId string)) { + s.responseHandler = handler +} + +// Registers a handler for incoming error messages. +func (s *Server) SetErrorHandler(handler func(clientID string, err *ocpp.Error, details interface{})) { + s.errorHandler = handler +} + +// Registers a handler for incoming client connections. +func (s *Server) SetNewClientHandler(handler func(clientID string)) { + s.newClientHandler = handler +} + +// Registers a handler for client disconnections. +func (s *Server) SetDisconnectedClientHandler(handler func(clientID string)) { + s.disconnectedClientHandler = handler +} + +// Starts the underlying Websocket server on a specified listenPort and listenPath. +// The function runs indefinitely, until the server is stopped. +// +// Call this function in a separate goroutine, to perform other operations on the main thread. +// +// An error may be returned, if the websocket server couldn't be started. +func (s *Server) Start(listenPort int, listenPath string) { + // Set internal message handler + s.server.SetNewClientHandler(func(ws ws.Channel) { + if s.newClientHandler != nil { + s.newClientHandler(ws.GetID()) + } + }) + s.server.SetDisconnectedClientHandler(func(ws ws.Channel) { + if s.disconnectedClientHandler != nil { + s.disconnectedClientHandler(ws.GetID()) + } + }) + s.server.SetMessageHandler(s.ocppMessageHandler) + // Serve & run + // TODO: return error? + s.server.Start(listenPort, listenPath) +} + +// Stops the server. +// This clears all pending requests and causes the Start function to return. +func (s *Server) Stop() { + s.server.Stop() + s.clearPendingRequests() +} + +// Sends an OCPP Request to a client, identified by the clientID parameter. +// +// Returns an error in the following cases: +// +// - message validation fails (request is malformed) +// +// - another request for that client is already pending +// +// - the endpoint doesn't support the feature +// +// - a network error occurred +func (s *Server) SendRequest(clientID string, request ocpp.Request) error { + err := Validate.Struct(request) + if err != nil { + return err + } + req, ok := s.clientPendingMessages[clientID] + if ok { + // Cannot send. Protocol is based on request-response + return errors.Errorf("There already is a pending request %v for client %v. Cannot send a further one before receiving a response first", req, clientID) + } + call, err := s.CreateCall(request.(ocpp.Request)) + if err != nil { + return err + } + jsonMessage, err := call.MarshalJSON() + if err != nil { + return err + } + s.clientPendingMessages[clientID] = call.UniqueId + err = s.server.Write(clientID, []byte(jsonMessage)) + if err != nil { + // Clear pending request + s.DeletePendingRequest(call.GetUniqueId()) + delete(s.clientPendingMessages, clientID) + } + return err +} + +// Sends an OCPP Response to a client, identified by the clientID parameter. +// The requestID parameter is required and identifies the previously received request. +// +// Returns an error in the following cases: +// +// - message validation fails (response is malformed) +// +// - the endpoint doesn't support the feature +// +// - a network error occurred +func (s *Server) SendResponse(clientID string, requestId string, response ocpp.Response) error { + err := Validate.Struct(response) + if err != nil { + return err + } + callResult, err := s.CreateCallResult(response, requestId) + if err != nil { + return err + } + jsonMessage, err := callResult.MarshalJSON() + if err != nil { + return err + } + return s.server.Write(clientID, []byte(jsonMessage)) +} + +// Sends an OCPP Error to a client, identified by the clientID parameter. +// The requestID parameter is required and identifies the previously received request. +// +// Returns an error in the following cases: +// +// - message validation fails (error is malformed) +// +// - a network error occurred +func (s *Server) SendError(clientID string, requestId string, errorCode ocpp.ErrorCode, description string, details interface{}) error { + callError := s.CreateCallError(requestId, errorCode, description, details) + err := Validate.Struct(callError) + if err != nil { + return err + } + jsonMessage, err := callError.MarshalJSON() + if err != nil { + return err + } + return s.server.Write(clientID, []byte(jsonMessage)) +} + +func (s *Server) ocppMessageHandler(wsChannel ws.Channel, data []byte) error { + parsedJson := ParseRawJsonMessage(data) + message, err := s.ParseMessage(parsedJson) + if err != nil { + if err.MessageId != "" { + err2 := s.SendError(wsChannel.GetID(), err.MessageId, err.Code, err.Description, nil) + if err2 != nil { + return err2 + } + } + log.Error(err) + return err + } + switch message.GetMessageTypeId() { + case CALL: + call := message.(*Call) + s.requestHandler(wsChannel.GetID(), call.Payload, call.UniqueId, call.Action) + case CALL_RESULT: + callResult := message.(*CallResult) + delete(s.clientPendingMessages, wsChannel.GetID()) + s.responseHandler(wsChannel.GetID(), callResult.Payload, callResult.UniqueId) + case CALL_ERROR: + callError := message.(*CallError) + delete(s.clientPendingMessages, wsChannel.GetID()) + s.errorHandler(wsChannel.GetID(), ocpp.NewError(callError.ErrorCode, callError.ErrorDescription, callError.UniqueId), callError.ErrorDetails) + } + return nil +} diff --git a/ocppj_test/central_system_test.go b/ocppj_test/central_system_test.go index d8c5c7e5..5d9d10c7 100644 --- a/ocppj_test/central_system_test.go +++ b/ocppj_test/central_system_test.go @@ -46,14 +46,14 @@ func (suite *OcppJTestSuite) TestCentralSystemSendRequestFailed() { //TODO: assert that pending request was removed } -// SendConfirmation +// SendResponse func (suite *OcppJTestSuite) TestCentralSystemSendConfirmation() { t := suite.T() mockChargePointId := "0101" mockUniqueId := "1234" suite.mockServer.On("Write", mock.AnythingOfType("string"), mock.Anything).Return(nil) mockConfirmation := newMockConfirmation("mockValue") - err := suite.centralSystem.SendConfirmation(mockChargePointId, mockUniqueId, mockConfirmation) + err := suite.centralSystem.SendResponse(mockChargePointId, mockUniqueId, mockConfirmation) assert.Nil(t, err) } @@ -64,7 +64,7 @@ func (suite *OcppJTestSuite) TestCentralSystemSendInvalidConfirmation() { suite.mockServer.On("Write", mock.AnythingOfType("string"), mock.Anything).Return(nil) mockConfirmation := newMockConfirmation("") // This is allowed. Endpoint doesn't keep track of incoming requests, but only outgoing ones - err := suite.centralSystem.SendConfirmation(mockChargePointId, mockUniqueId, mockConfirmation) + err := suite.centralSystem.SendResponse(mockChargePointId, mockUniqueId, mockConfirmation) assert.NotNil(t, err) } @@ -74,7 +74,7 @@ func (suite *OcppJTestSuite) TestCentralSystemSendConfirmationFailed() { mockUniqueId := "1234" suite.mockServer.On("Write", mock.AnythingOfType("string"), mock.Anything).Return(errors.New("networkError")) mockConfirmation := newMockConfirmation("mockValue") - err := suite.centralSystem.SendConfirmation(mockChargePointId, mockUniqueId, mockConfirmation) + err := suite.centralSystem.SendResponse(mockChargePointId, mockUniqueId, mockConfirmation) assert.NotNil(t, err) assert.Equal(t, "networkError", err.Error()) } @@ -106,7 +106,7 @@ func (suite *OcppJTestSuite) TestCentralSystemSendErrorFailed() { mockUniqueId := "1234" suite.mockServer.On("Write", mock.AnythingOfType("string"), mock.Anything).Return(errors.New("networkError")) mockConfirmation := newMockConfirmation("mockValue") - err := suite.centralSystem.SendConfirmation(mockChargePointId, mockUniqueId, mockConfirmation) + err := suite.centralSystem.SendResponse(mockChargePointId, mockUniqueId, mockConfirmation) assert.NotNil(t, err) } @@ -139,7 +139,7 @@ func (suite *OcppJTestSuite) TestCentralSystemConfirmationHandler() { mockValue := "someValue" mockRequest := newMockRequest("testValue") mockConfirmation := fmt.Sprintf(`[3,"%v",{"mockValue":"%v"}]`, mockUniqueId, mockValue) - suite.centralSystem.SetConfirmationHandler(func(chargePointId string, confirmation ocpp.Confirmation, requestId string) { + suite.centralSystem.SetResponseHandler(func(chargePointId string, confirmation ocpp.Response, requestId string) { assert.Equal(t, mockChargePointId, chargePointId) assert.Equal(t, mockUniqueId, requestId) assert.NotNil(t, confirmation) diff --git a/ocppj_test/charge_point_test.go b/ocppj_test/charge_point_test.go index f4704c4e..77dfa1f5 100644 --- a/ocppj_test/charge_point_test.go +++ b/ocppj_test/charge_point_test.go @@ -55,13 +55,13 @@ func (suite *OcppJTestSuite) TestChargePointSendRequestFailed() { //TODO: assert that pending request was removed } -// SendConfirmation +// SendResponse func (suite *OcppJTestSuite) TestChargePointSendConfirmation() { t := suite.T() mockUniqueId := "1234" suite.mockClient.On("Write", mock.Anything).Return(nil) mockConfirmation := newMockConfirmation("mockValue") - err := suite.chargePoint.SendConfirmation(mockUniqueId, mockConfirmation) + err := suite.chargePoint.SendResponse(mockUniqueId, mockConfirmation) assert.Nil(t, err) } @@ -71,7 +71,7 @@ func (suite *OcppJTestSuite) TestChargePointSendInvalidConfirmation() { suite.mockClient.On("Write", mock.Anything).Return(nil) mockConfirmation := newMockConfirmation("") // This is allowed. Endpoint doesn't keep track of incoming requests, but only outgoing ones - err := suite.chargePoint.SendConfirmation(mockUniqueId, mockConfirmation) + err := suite.chargePoint.SendResponse(mockUniqueId, mockConfirmation) assert.NotNil(t, err) } @@ -80,7 +80,7 @@ func (suite *OcppJTestSuite) TestChargePointSendConfirmationFailed() { mockUniqueId := "1234" suite.mockClient.On("Write", mock.Anything).Return(errors.New("networkError")) mockConfirmation := newMockConfirmation("mockValue") - err := suite.chargePoint.SendConfirmation(mockUniqueId, mockConfirmation) + err := suite.chargePoint.SendResponse(mockUniqueId, mockConfirmation) assert.NotNil(t, err) assert.Equal(t, "networkError", err.Error()) } @@ -109,7 +109,7 @@ func (suite *OcppJTestSuite) TestChargePointSendErrorFailed() { mockUniqueId := "1234" suite.mockClient.On("Write", mock.Anything).Return(errors.New("networkError")) mockConfirmation := newMockConfirmation("mockValue") - err := suite.chargePoint.SendConfirmation(mockUniqueId, mockConfirmation) + err := suite.chargePoint.SendResponse(mockUniqueId, mockConfirmation) assert.NotNil(t, err) assert.Equal(t, "networkError", err.Error()) } @@ -140,7 +140,7 @@ func (suite *OcppJTestSuite) TestChargePointCallResultHandler() { mockValue := "someValue" mockRequest := newMockRequest("testValue") mockConfirmation := fmt.Sprintf(`[3,"%v",{"mockValue":"%v"}]`, mockUniqueId, mockValue) - suite.chargePoint.SetConfirmationHandler(func(confirmation ocpp.Confirmation, requestId string) { + suite.chargePoint.SetResponseHandler(func(confirmation ocpp.Response, requestId string) { assert.Equal(t, mockUniqueId, requestId) assert.NotNil(t, confirmation) }) diff --git a/ocppj_test/ocppj_test.go b/ocppj_test/ocppj_test.go index 10f2aa00..4a5ab566 100644 --- a/ocppj_test/ocppj_test.go +++ b/ocppj_test/ocppj_test.go @@ -122,7 +122,7 @@ func (f MockFeature) GetRequestType() reflect.Type { return reflect.TypeOf(MockRequest{}) } -func (f MockFeature) GetConfirmationType() reflect.Type { +func (f MockFeature) GetResponseType() reflect.Type { return reflect.TypeOf(MockConfirmation{}) } @@ -246,8 +246,8 @@ func init() { // ---------------------- TESTS ---------------------- type OcppJTestSuite struct { suite.Suite - chargePoint *ocppj.ChargePoint - centralSystem *ocppj.CentralSystem + chargePoint *ocppj.Client + centralSystem *ocppj.Server mockServer *MockWebsocketServer mockClient *MockWebsocketClient } @@ -258,8 +258,8 @@ func (suite *OcppJTestSuite) SetupTest() { mockServer := MockWebsocketServer{} suite.mockClient = &mockClient suite.mockServer = &mockServer - suite.chargePoint = ocppj.NewChargePoint("mock_id", suite.mockClient, mockProfile) - suite.centralSystem = ocppj.NewCentralSystem(suite.mockServer, mockProfile) + suite.chargePoint = ocppj.NewClient("mock_id", suite.mockClient, mockProfile) + suite.centralSystem = ocppj.NewServer(suite.mockServer, mockProfile) } // Protocol functions test @@ -271,7 +271,7 @@ func (suite *OcppJTestSuite) TestGetProfile() { feature := profile.GetFeature(MockFeatureName) assert.NotNil(t, feature) assert.Equal(t, reflect.TypeOf(MockRequest{}), feature.GetRequestType()) - assert.Equal(t, reflect.TypeOf(MockConfirmation{}), feature.GetConfirmationType()) + assert.Equal(t, reflect.TypeOf(MockConfirmation{}), feature.GetResponseType()) } func (suite *OcppJTestSuite) TestGetProfileForFeature() { @@ -457,7 +457,7 @@ func (suite *OcppJTestSuite) TestParseMessageUnknownTypeId() { assert.Nil(t, message) assert.NotNil(t, protoErr) assert.Equal(t, messageId, protoErr.MessageId) - assert.Equal(t, ocppj.FormationViolation, protoErr.Code) + assert.Equal(t, ocppj.MessageTypeNotSupported, protoErr.Code) assert.Equal(t, fmt.Sprintf("Invalid message type ID %v", invalidTypeId), protoErr.Description) }