From f4169c779dcf1b3b94bd3ba2efe680132fd3ffd6 Mon Sep 17 00:00:00 2001 From: xBlaz3kx Date: Wed, 2 Jul 2025 15:06:24 +0200 Subject: [PATCH 1/9] feat: ocpp 2.0.1 controllers (wip) --- go.mod | 1 - go.sum | 2 -- ocpp_v201/aligned_data_ctrlr.go | 12 ++++++++++ ocpp_v201/auth_cache_ctrlr.go | 10 +++++++++ ocpp_v201/auth_ctrlr.go | 12 ++++++++++ ocpp_v201/chademo_ctrlr.go | 1 + ocpp_v201/charging_infrastructure.go | 1 + ocpp_v201/clock_ctrlr.go | 11 ++++++++++ ocpp_v201/components.go | 33 ++++++++++++++++++++++++++++ ocpp_v201/ctrlr_manager.go | 5 +++++ ocpp_v201/customization_ctrlr.go | 5 +++++ ocpp_v201/device_data_ctrlr.go | 8 +++++++ ocpp_v201/display_message_ctrlr.go | 9 ++++++++ ocpp_v201/iso15118_ctrlr.go | 15 +++++++++++++ ocpp_v201/local_auth_list_ctrlr.go | 11 ++++++++++ ocpp_v201/monitoring_ctrlr.go | 13 +++++++++++ ocpp_v201/ocpp_comm_ctrlr.go | 19 ++++++++++++++++ ocpp_v201/reservation_ctrlr.go | 7 ++++++ ocpp_v201/sampled_data_ctrlr.go | 13 +++++++++++ ocpp_v201/security_ctrlr.go | 12 ++++++++++ ocpp_v201/smart_charging_ctrlr.go | 15 +++++++++++++ ocpp_v201/tariff_cost_ctrlr.go | 11 ++++++++++ ocpp_v201/tx_ctrlr.go | 11 ++++++++++ ocpp_v201/variables.go | 29 ++++++++++++++++++++++++ 24 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 ocpp_v201/aligned_data_ctrlr.go create mode 100644 ocpp_v201/auth_cache_ctrlr.go create mode 100644 ocpp_v201/auth_ctrlr.go create mode 100644 ocpp_v201/chademo_ctrlr.go create mode 100644 ocpp_v201/charging_infrastructure.go create mode 100644 ocpp_v201/clock_ctrlr.go create mode 100644 ocpp_v201/components.go create mode 100644 ocpp_v201/ctrlr_manager.go create mode 100644 ocpp_v201/customization_ctrlr.go create mode 100644 ocpp_v201/device_data_ctrlr.go create mode 100644 ocpp_v201/display_message_ctrlr.go create mode 100644 ocpp_v201/iso15118_ctrlr.go create mode 100644 ocpp_v201/local_auth_list_ctrlr.go create mode 100644 ocpp_v201/monitoring_ctrlr.go create mode 100644 ocpp_v201/ocpp_comm_ctrlr.go create mode 100644 ocpp_v201/reservation_ctrlr.go create mode 100644 ocpp_v201/sampled_data_ctrlr.go create mode 100644 ocpp_v201/security_ctrlr.go create mode 100644 ocpp_v201/smart_charging_ctrlr.go create mode 100644 ocpp_v201/tariff_cost_ctrlr.go create mode 100644 ocpp_v201/tx_ctrlr.go create mode 100644 ocpp_v201/variables.go diff --git a/go.mod b/go.mod index 9330105..5389922 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.22.1 replace github.com/lorenzodonini/ocpp-go v0.18.0 => github.com/ChargePi/ocpp-go v0.21.0 require ( - github.com/ChargePi/ocppManager-go v1.2.0 github.com/agrison/go-commons-lang v0.0.0-20240106075236-2e001e6401ef github.com/lorenzodonini/ocpp-go v0.18.0 github.com/samber/lo v1.47.0 diff --git a/go.sum b/go.sum index 8f489fd..7692d64 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ github.com/ChargePi/ocpp-go v0.21.0 h1:YP6uCu75D/TJFwkWsRHHtRthapmrxZscKBylBc8oc9Q= github.com/ChargePi/ocpp-go v0.21.0/go.mod h1:2kcukDdhui4u730VfnYVWuwzDLgw+mBRGDir/QAyBhg= -github.com/ChargePi/ocppManager-go v1.2.0 h1:OV90kAD22yVYTSE+uHIgTtiwUOYpEwyHDPJb8d6AltM= -github.com/ChargePi/ocppManager-go v1.2.0/go.mod h1:7kWtV1+qQw+OSOHBuE/wUsTH5Id5+/euzsCfke9NQ2E= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/agrison/go-commons-lang v0.0.0-20240106075236-2e001e6401ef h1:KkznClyESbRaLmRo7Oam4vv5L4oknDK+mixJ9mypl6E= diff --git a/ocpp_v201/aligned_data_ctrlr.go b/ocpp_v201/aligned_data_ctrlr.go new file mode 100644 index 0000000..6c6bea4 --- /dev/null +++ b/ocpp_v201/aligned_data_ctrlr.go @@ -0,0 +1,12 @@ +package ocpp_v201 + +const ( + VariableNameAlignedDataEnabled VariableName = "Enabled" + VariableNameAlignedDataAvailable VariableName = "Available" + VariableNameAlignedDataMeasurands VariableName = "Measurands" + VariableNameAlignedDataInterval VariableName = "Interval" + VariableNameAlignedDataSendDuringIdle VariableName = "SendDuringIdle" + VariableNameAlignedDataSignReadings VariableName = "SignReadings" + VariableNameAlignedDataTxEndedMeasurands VariableName = "TxEndedMeasurands" + VariableNameAlignedDataTxEndedInterval VariableName = "TxEndedInterval" +) diff --git a/ocpp_v201/auth_cache_ctrlr.go b/ocpp_v201/auth_cache_ctrlr.go new file mode 100644 index 0000000..1a13d1c --- /dev/null +++ b/ocpp_v201/auth_cache_ctrlr.go @@ -0,0 +1,10 @@ +package ocpp_v201 + +const ( + VariableNameAuthCacheEnabled VariableName = "Enabled" + VariableNameAuthCacheAvailable VariableName = "Available" + VariableNameAuthCacheLifeTime VariableName = "LifeTime" + VariableNameAuthCacheStorage VariableName = "Storage" + VariableNameAuthCachePolicy VariableName = "Policy" + VariableNameAuthCacheDisablePostAuthorize VariableName = "DisablePostAuthorize" +) diff --git a/ocpp_v201/auth_ctrlr.go b/ocpp_v201/auth_ctrlr.go new file mode 100644 index 0000000..5f181d3 --- /dev/null +++ b/ocpp_v201/auth_ctrlr.go @@ -0,0 +1,12 @@ +package ocpp_v201 + +const ( + VariableNameAuthEnabled VariableName = "Enabled" + VariableNameAdditionalInfoItemsPerMessage VariableName = "AdditionalInfoItemsPerMessage" + VariableNameOfflineTxForUnknownIdEnabled VariableName = "OfflineTxForUnknownIdEnabled" + VariableNameAuthorizeRemoteStart VariableName = "AuthorizeRemoteStart" + VariableNameLocalAuthorizeOffline VariableName = "LocalAuthorizeOffline" + VariableNameLocalPreAuthorize VariableName = "LocalPreAuthorize" + VariableNameMasterPassGroupId VariableName = "MasterPassGroupId" + VariableNameDisableRemoteAuthorization VariableName = "DisableRemoteAuthorization" +) diff --git a/ocpp_v201/chademo_ctrlr.go b/ocpp_v201/chademo_ctrlr.go new file mode 100644 index 0000000..607262c --- /dev/null +++ b/ocpp_v201/chademo_ctrlr.go @@ -0,0 +1 @@ +package ocpp_v201 diff --git a/ocpp_v201/charging_infrastructure.go b/ocpp_v201/charging_infrastructure.go new file mode 100644 index 0000000..607262c --- /dev/null +++ b/ocpp_v201/charging_infrastructure.go @@ -0,0 +1 @@ +package ocpp_v201 diff --git a/ocpp_v201/clock_ctrlr.go b/ocpp_v201/clock_ctrlr.go new file mode 100644 index 0000000..f3b7427 --- /dev/null +++ b/ocpp_v201/clock_ctrlr.go @@ -0,0 +1,11 @@ +package ocpp_v201 + +const ( + VariableNameNtpSource VariableName = "NtpSource" + VariableNameNtpServerUri VariableName = "NtpServerUri" + VariableNameTimeOffset VariableName = "TimeOffset" + VariableNameNextTimeOffsetTransitionDateTime VariableName = "NextTimeOffsetTransitionDateTime" + VariableNameTimeSource VariableName = "TimeSource" + VariableNameTimeZone VariableName = "TimeZone" + VariableNameTimeAdjustmentReportingThreshold VariableName = "TimeAdjustmentReportingThreshold" +) diff --git a/ocpp_v201/components.go b/ocpp_v201/components.go new file mode 100644 index 0000000..5a69416 --- /dev/null +++ b/ocpp_v201/components.go @@ -0,0 +1,33 @@ +package ocpp_v201 + +type ComponentName string + +const ( + ComponentNameOCPPCommCtrlr ComponentName = "OCPPCommCtrlr" + ComponentNameLocalAuthListCtrlr ComponentName = "LocalAuthListCtrlr" + ComponentNameTxCtrlr ComponentName = "TxCtrlr" + ComponentNameDeviceDataCtrlr ComponentName = "DeviceDataCtrlr" + ComponentNameSecurityCtrlr ComponentName = "SecurityCtrlr" + ComponentNameClockCtrlr ComponentName = "ClockCtrlr" + ComponentNameCustomizationCtrlr ComponentName = "CustomizationCtrlr" + ComponentNameSampledDataCtrlr ComponentName = "SampledDataCtrlr" + ComponentNameAlignedDataCtrlr ComponentName = "AlignedDataCtrlr" + ComponentNameReservationCtrlr ComponentName = "ReservationCtrlr" + ComponentNameSmartChargingCtrlr ComponentName = "SmartChargingCtrlr" + ComponentNameTariffCostCtrlr ComponentName = "TariffCostCtrlr" + ComponentNameMonitoringCtrlr ComponentName = "MonitoringCtrlr" + ComponentNameDisplayMessageCtrlr ComponentName = "DisplayMessageCtrlr" + ComponentNameISO15118Ctrlr ComponentName = "ISO15118Ctrlr" + ComponentNameAuthCtrlr ComponentName = "AuthCtrlr" + ComponentNameAuthCacheCtrlr ComponentName = "AuthCacheCtrlr" + ComponentNameChargingStation ComponentName = "ChargingStation" + ComponentNameEVSE ComponentName = "EVSE" + ComponentNameConnector ComponentName = "Connector" + ComponentNameConnectedEV ComponentName = "ConnectedEV" +) + +type Controller interface { + Get() (*Variable, error) + Update(key VariableName, value interface{}) error + Validate(key VariableName) bool +} diff --git a/ocpp_v201/ctrlr_manager.go b/ocpp_v201/ctrlr_manager.go new file mode 100644 index 0000000..e92c559 --- /dev/null +++ b/ocpp_v201/ctrlr_manager.go @@ -0,0 +1,5 @@ +package ocpp_v201 + +type Manager struct { + controllers map[string]Controller +} diff --git a/ocpp_v201/customization_ctrlr.go b/ocpp_v201/customization_ctrlr.go new file mode 100644 index 0000000..2dea3ed --- /dev/null +++ b/ocpp_v201/customization_ctrlr.go @@ -0,0 +1,5 @@ +package ocpp_v201 + +const ( + VariableNameCustomImplementationEnabled VariableName = "CustomImplementationEnabled" +) diff --git a/ocpp_v201/device_data_ctrlr.go b/ocpp_v201/device_data_ctrlr.go new file mode 100644 index 0000000..9ab384b --- /dev/null +++ b/ocpp_v201/device_data_ctrlr.go @@ -0,0 +1,8 @@ +package ocpp_v201 + +const ( + VariableNameBytesPerMessage VariableName = "BytesPerMessage" + VariableNameConfigurationValueSize VariableName = "ConfigurationValueSize" + VariableNameReportingValueSize VariableName = "ReportingValueSize" + VariableNameItemsPerMessage VariableName = "ItemsPerMessage" +) diff --git a/ocpp_v201/display_message_ctrlr.go b/ocpp_v201/display_message_ctrlr.go new file mode 100644 index 0000000..539a583 --- /dev/null +++ b/ocpp_v201/display_message_ctrlr.go @@ -0,0 +1,9 @@ +package ocpp_v201 + +const ( + VariableNameDisplayMessageEnabled VariableName = "Enabled" + VariableNameDisplayMessageAvailable VariableName = "Available" + VariableNameNumberOfDisplayMessages VariableName = "DisplayMessages" + VariableNameDisplayMessageSupportedFormats VariableName = "SupportedFormats" + VariableNameDisplayMessageSupportedPriorities VariableName = "SupportedPriorities" +) diff --git a/ocpp_v201/iso15118_ctrlr.go b/ocpp_v201/iso15118_ctrlr.go new file mode 100644 index 0000000..6d0a4d7 --- /dev/null +++ b/ocpp_v201/iso15118_ctrlr.go @@ -0,0 +1,15 @@ +package ocpp_v201 + +const ( + VariableNameCentralContractValidationAllowed VariableName = "CentralContractValidationAllowed" + VariableNameContractValidationOffline VariableName = "ContractValidationOffline" + VariableNameProtocolSupportedByEV VariableName = "ProtocolSupportedByEV" + VariableNameProtocolAgreed VariableName = "ProtocolAgreed" + VariableNameISO15118PnCEnabled VariableName = "PnCEnabled" + VariableNameISO15118V2GCertificateInstallationEnabled VariableName = "V2GCertificateInstallationEnabled" + VariableNameISO15118ContractCertificateInstallationEnabled VariableName = "ContractCertificateInstallationEnabled" + VariableNameISO15118RequestMeteringReceipt VariableName = "RequestMeteringReceipt" + VariableNameISO15118SeccId VariableName = "SeccId" + VariableNameISO15118CountryName VariableName = "CountryName" + VariableNameISO15118EvseId VariableName = "ISO15118EvseId" +) diff --git a/ocpp_v201/local_auth_list_ctrlr.go b/ocpp_v201/local_auth_list_ctrlr.go new file mode 100644 index 0000000..3eb6f6f --- /dev/null +++ b/ocpp_v201/local_auth_list_ctrlr.go @@ -0,0 +1,11 @@ +package ocpp_v201 + +const ( + VariableNameLocalAuthListEnabled VariableName = "Enabled" + VariableNameLocalAuthListEntries VariableName = "Entries" + VariableNameLocalAuthListItemsPerMessage VariableName = "ItemsPerMessage" + VariableNameLocalAuthListBytesPerMessage VariableName = "BytesPerMessage" + VariableNameLocalAuthListStorage VariableName = "Storage" + VariableNameLocalAuthListDisablePostAuthorize VariableName = "DisablePostAuthorize" + VariableNameLocalAuthListSupportsExpiryDateTime VariableName = "SupportsExpiryDateTime" +) diff --git a/ocpp_v201/monitoring_ctrlr.go b/ocpp_v201/monitoring_ctrlr.go new file mode 100644 index 0000000..8e46d6c --- /dev/null +++ b/ocpp_v201/monitoring_ctrlr.go @@ -0,0 +1,13 @@ +package ocpp_v201 + +const ( + VariableNameMonitoringEnabled VariableName = "Enabled" + VariableNameMonitoringAvailable VariableName = "Available" + VariableNameItemsPerMessageClearVariableMonitoring VariableName = "ItemsPerMessage" + VariableNameItemsPerMessageSetVariableMonitoring VariableName = "ItemsPerMessage" + VariableNameClearVariableMonitoring VariableName = "BytesPerMessage" + VariableNameBytesPerMessageSetVariableMonitoring VariableName = "BytesPerMessage" + VariableNameOfflineMonitoringEventQueuingSeverity VariableName = "OfflineQueuingSeverity" + VariableNameActiveMonitoringBase VariableName = "ActiveMonitoringBase" + VariableNameActiveMonitoringLevel VariableName = "ActiveMonitoringLevel" +) diff --git a/ocpp_v201/ocpp_comm_ctrlr.go b/ocpp_v201/ocpp_comm_ctrlr.go new file mode 100644 index 0000000..dc97379 --- /dev/null +++ b/ocpp_v201/ocpp_comm_ctrlr.go @@ -0,0 +1,19 @@ +package ocpp_v201 + +const ( + VariableNameNetworkProfileConnectionAttempts VariableName = "NetworkProfileConnectionAttempts" + VariableNameNetworkConfigurationPriority VariableName = "NetworkConfigurationPriority" + VariableNameHeartbeatInterval VariableName = "HeartbeatInterval" + VariableNameFileTransferProtocols VariableName = "FileTransferProtocols" + VariableNameMessageTimeout VariableName = "MessageTimeout" + VariableNameActiveNetworkProfile VariableName = "ActiveNetworkProfile" + VariableNameOfflineThreshold VariableName = "OfflineThreshold" + VariableNameQueueAllMessages VariableName = "QueueAllMessages" + VariableNameMessageAttempts VariableName = "MessageAttempts" + VariableNameMessageAttemptInterval VariableName = "MessageAttemptInterval" + VariableNameUnlockOnEVSideDisconnect VariableName = "UnlockOnEVSideDisconnect" + VariableNameWebSocketPingInterval VariableName = "WebSocketPingInterval" + VariableNameResetRetries VariableName = "ResetRetries" + VariableNameFieldLength VariableName = "FieldLength" + VariableNamePublicKeyWithSignedMeterValue VariableName = "PublicKeyWithSignedMeterValue" +) diff --git a/ocpp_v201/reservation_ctrlr.go b/ocpp_v201/reservation_ctrlr.go new file mode 100644 index 0000000..ec05ff3 --- /dev/null +++ b/ocpp_v201/reservation_ctrlr.go @@ -0,0 +1,7 @@ +package ocpp_v201 + +const ( + VariableNameReservationEnabled VariableName = "Enabled" + VariableNameReservationAvailable VariableName = "Available" + VariableNameReservationNonEvseSpecific VariableName = "NonEvseSpecific" +) diff --git a/ocpp_v201/sampled_data_ctrlr.go b/ocpp_v201/sampled_data_ctrlr.go new file mode 100644 index 0000000..1e6574e --- /dev/null +++ b/ocpp_v201/sampled_data_ctrlr.go @@ -0,0 +1,13 @@ +package ocpp_v201 + +const ( + VariableNameSampledDataEnabled VariableName = "Enabled" + VariableNameSampledDataAvailable VariableName = "Available" + VariableNameSampledDataSignReadings VariableName = "SignReadings" + VariableNameSampledDataTxEndedMeasurands VariableName = "TxEndedMeasurands" + VariableNameSampledDataTxEndedInterval VariableName = "TxEndedInterval" + VariableNameSampledDataTxStartedMeasurands VariableName = "TxStartedMeasurands" + VariableNameSampledDataTxUpdatedMeasurands VariableName = "TxUpdatedMeasurands" + VariableNameSampledDataTxUpdatedInterval VariableName = "TxUpdatedInterval" + VariableNameSampledDataRegisterValuesWithoutPhases VariableName = "RegisterValuesWithoutPhases" +) diff --git a/ocpp_v201/security_ctrlr.go b/ocpp_v201/security_ctrlr.go new file mode 100644 index 0000000..26f4747 --- /dev/null +++ b/ocpp_v201/security_ctrlr.go @@ -0,0 +1,12 @@ +package ocpp_v201 + +const ( + VariableNameBasicAuthPassword VariableName = "BasicAuthPassword" + VariableNameIdentity VariableName = "Identity" + VariableNameOrganizationName VariableName = "OrganizationName" + VariableNameCertificateEntries VariableName = "CertificateEntries" + VariableNameAdditionalRootCertificateCheck VariableName = "AdditionalRootCertificateCheck" + VariableNameSecurityProfile VariableName = "SecurityProfile" + VariableNameMaxCertificateChainSize VariableName = "MaxCertificateChainSize" + VariableNameCertSigningWaitMinimum VariableName = "CertSigningWaitMinimum" +) diff --git a/ocpp_v201/smart_charging_ctrlr.go b/ocpp_v201/smart_charging_ctrlr.go new file mode 100644 index 0000000..d216620 --- /dev/null +++ b/ocpp_v201/smart_charging_ctrlr.go @@ -0,0 +1,15 @@ +package ocpp_v201 + +const ( + VariableNameSmartChargingEnabled VariableName = "Enabled" + VariableNameSmartChargingAvailable VariableName = "Available" + VariableNameACPhaseSwitchingSupported VariableName = "ACPhaseSwitchingSupported" + VariableNameChargingProfileStackLevel VariableName = "ProfileStackLevel" + VariableNameChargingScheduleChargingRateUnit VariableName = "RateUnit" + VariableNamePeriodsPerSchedule VariableName = "PeriodsPerSchedule" + VariableNameExternalControlSignalsEnabled VariableName = "ExternalControlSignalsEnabled" + VariableNameNotifyChargingLimitWithSchedules VariableName = "NotifyChargingLimitWithSchedules" + VariableNamePhases3to1 VariableName = "Phases3to1" + VariableChargingProfileEntries VariableName = "Entries" + VariableLimitChangeSignificance VariableName = "LimitChangeSignificance" +) diff --git a/ocpp_v201/tariff_cost_ctrlr.go b/ocpp_v201/tariff_cost_ctrlr.go new file mode 100644 index 0000000..6f5f320 --- /dev/null +++ b/ocpp_v201/tariff_cost_ctrlr.go @@ -0,0 +1,11 @@ +package ocpp_v201 + +const ( + VariableNameTariffEnabled VariableName = "Enabled" + VariableNameTariffAvailable VariableName = "Available" + VariableNameTariffFallbackMessage VariableName = "TariffFallbackMessage" + VariableNameCostEnabled VariableName = "Enabled" + VariableNameCostAvailable VariableName = "Available" + VariableNameTotalCostFallbackMessage VariableName = "TotalCostFallbackMessage" + VariableNameCurrency VariableName = "Currency" +) diff --git a/ocpp_v201/tx_ctrlr.go b/ocpp_v201/tx_ctrlr.go new file mode 100644 index 0000000..e8ed296 --- /dev/null +++ b/ocpp_v201/tx_ctrlr.go @@ -0,0 +1,11 @@ +package ocpp_v201 + +const ( + VariableNameEVConnectionTimeOut VariableName = "EVConnectionTimeOut" + VariableNameStopTxOnEVSideDisconnect VariableName = "StopTxOnEVSideDisconnect" + VariableNameTxBeforeAcceptedEnabled VariableName = "TxBeforeAcceptedEnabled" + VariableNameTxStartPoint VariableName = "TxStartPoint" + VariableNameTxStopPoint VariableName = "TxStopPoint" + VariableNameMaxEnergyOnInvalidId VariableName = "MaxEnergyOnInvalidId" + VariableNameStopTxOnInvalidId VariableName = "StopTxOnInvalidId" +) diff --git a/ocpp_v201/variables.go b/ocpp_v201/variables.go new file mode 100644 index 0000000..73c8d38 --- /dev/null +++ b/ocpp_v201/variables.go @@ -0,0 +1,29 @@ +package ocpp_v201 + +type Mutability string + +const ( + MutabilityReadOnly Mutability = "ReadOnly" + MutabilityReadWrite Mutability = "ReadWrite" + MutabilityWriteOnly Mutability = "WriteOnly" +) + +type VariableName string + +type Variable struct { + Name VariableName + Attributes VariableAttributes + Characteristics VariableCharacteristic + Value interface{} +} + +type VariableAttributes struct { + Mutability Mutability +} + +type VariableCharacteristic struct { + DataType string + MaxLimit *int + Unit *string + ValuesList []string +} From c419ec1a33ab108a8e8d67a08ff914ce4ad61966 Mon Sep 17 00:00:00 2001 From: Blaz Dular Date: Sun, 6 Jul 2025 23:37:43 +0200 Subject: [PATCH 2/9] chore: moved things around a bit, added required and optional variables, wip on the implementation --- ocpp_v201/aligned_data_ctrlr.go | 12 -- ocpp_v201/auth_cache_ctrlr.go | 10 -- ocpp_v201/auth_ctrlr.go | 12 -- ocpp_v201/charging_infrastructure.go | 16 ++ ocpp_v201/clock_ctrlr.go | 11 -- .../{components.go => component/component.go} | 32 +++- .../component/component_variable_opts.go | 13 ++ ocpp_v201/controllers/aligned_data_ctrlr.go | 103 ++++++++++++ .../controllers/aligned_data_ctrlr_test.go | 1 + ocpp_v201/controllers/auth_cache_ctrlr.go | 98 +++++++++++ .../controllers/auth_cache_ctrlr_test.go | 1 + ocpp_v201/controllers/auth_ctrlr.go | 103 ++++++++++++ ocpp_v201/controllers/auth_ctrlr_test.go | 1 + ocpp_v201/controllers/chademo_ctrlr.go | 13 ++ ocpp_v201/controllers/chademo_ctrlr_test.go | 1 + ocpp_v201/controllers/clock_ctrlr.go | 106 ++++++++++++ ocpp_v201/controllers/clock_ctrlr_test.go | 1 + ocpp_v201/controllers/customization_ctrlr.go | 66 ++++++++ ocpp_v201/controllers/device_data_ctrlr.go | 90 +++++++++++ .../controllers/device_data_ctrlr_test.go | 1 + .../controllers/display_message_ctrlr.go | 97 +++++++++++ .../controllers/display_message_ctrlr_test.go | 1 + ocpp_v201/controllers/iso15118_ctrlr.go | 109 +++++++++++++ .../controllers/local_auth_list_ctrlr.go | 100 ++++++++++++ .../controllers/local_auth_list_ctrlr_test.go | 1 + ocpp_v201/controllers/monitoring_ctrlr.go | 105 ++++++++++++ .../controllers/monitoring_ctrlr_test.go | 1 + ocpp_v201/controllers/ocpp_comm_ctrlr.go | 127 +++++++++++++++ ocpp_v201/controllers/ocpp_comm_ctrlr_test.go | 1 + ocpp_v201/controllers/reservation_ctrlr.go | 88 ++++++++++ .../controllers/reservation_ctrlr_test.go | 1 + ocpp_v201/controllers/sampled_data_ctrlr.go | 105 ++++++++++++ .../controllers/sampled_data_ctrlr_test.go | 1 + ocpp_v201/controllers/security_ctrlr.go | 105 ++++++++++++ ocpp_v201/controllers/security_ctrlr_test.go | 1 + ocpp_v201/controllers/smart_charging_ctrlr.go | 109 +++++++++++++ .../controllers/smart_charging_ctrlr_test.go | 1 + ocpp_v201/controllers/tariff_cost_ctrlr.go | 101 ++++++++++++ .../controllers/tariff_cost_ctrlr_test.go | 1 + ocpp_v201/controllers/tx_ctrlr.go | 101 ++++++++++++ ocpp_v201/controllers/tx_ctrlr_test.go | 1 + ocpp_v201/ctrlr_manager.go | 74 ++++++++- ...chademo_ctrlr.go => ctrlr_manager_test.go} | 0 ocpp_v201/customization_ctrlr.go | 5 - ocpp_v201/device_data_ctrlr.go | 8 - ocpp_v201/display_message_ctrlr.go | 9 -- ocpp_v201/iso15118_ctrlr.go | 15 -- ocpp_v201/local_auth_list_ctrlr.go | 11 -- ocpp_v201/monitoring_ctrlr.go | 13 -- ocpp_v201/ocpp_comm_ctrlr.go | 19 --- ocpp_v201/reservation_ctrlr.go | 7 - ocpp_v201/sampled_data_ctrlr.go | 13 -- ocpp_v201/security_ctrlr.go | 12 -- ocpp_v201/smart_charging_ctrlr.go | 15 -- ocpp_v201/tariff_cost_ctrlr.go | 11 -- ocpp_v201/tx_ctrlr.go | 11 -- ocpp_v201/variables.go | 29 ---- ocpp_v201/variables/variables.go | 152 ++++++++++++++++++ ocpp_v201/variables/variables_test.go | 34 ++++ 59 files changed, 2055 insertions(+), 231 deletions(-) delete mode 100644 ocpp_v201/aligned_data_ctrlr.go delete mode 100644 ocpp_v201/auth_cache_ctrlr.go delete mode 100644 ocpp_v201/auth_ctrlr.go delete mode 100644 ocpp_v201/clock_ctrlr.go rename ocpp_v201/{components.go => component/component.go} (58%) create mode 100644 ocpp_v201/component/component_variable_opts.go create mode 100644 ocpp_v201/controllers/aligned_data_ctrlr.go create mode 100644 ocpp_v201/controllers/aligned_data_ctrlr_test.go create mode 100644 ocpp_v201/controllers/auth_cache_ctrlr.go create mode 100644 ocpp_v201/controllers/auth_cache_ctrlr_test.go create mode 100644 ocpp_v201/controllers/auth_ctrlr.go create mode 100644 ocpp_v201/controllers/auth_ctrlr_test.go create mode 100644 ocpp_v201/controllers/chademo_ctrlr.go create mode 100644 ocpp_v201/controllers/chademo_ctrlr_test.go create mode 100644 ocpp_v201/controllers/clock_ctrlr.go create mode 100644 ocpp_v201/controllers/clock_ctrlr_test.go create mode 100644 ocpp_v201/controllers/customization_ctrlr.go create mode 100644 ocpp_v201/controllers/device_data_ctrlr.go create mode 100644 ocpp_v201/controllers/device_data_ctrlr_test.go create mode 100644 ocpp_v201/controllers/display_message_ctrlr.go create mode 100644 ocpp_v201/controllers/display_message_ctrlr_test.go create mode 100644 ocpp_v201/controllers/iso15118_ctrlr.go create mode 100644 ocpp_v201/controllers/local_auth_list_ctrlr.go create mode 100644 ocpp_v201/controllers/local_auth_list_ctrlr_test.go create mode 100644 ocpp_v201/controllers/monitoring_ctrlr.go create mode 100644 ocpp_v201/controllers/monitoring_ctrlr_test.go create mode 100644 ocpp_v201/controllers/ocpp_comm_ctrlr.go create mode 100644 ocpp_v201/controllers/ocpp_comm_ctrlr_test.go create mode 100644 ocpp_v201/controllers/reservation_ctrlr.go create mode 100644 ocpp_v201/controllers/reservation_ctrlr_test.go create mode 100644 ocpp_v201/controllers/sampled_data_ctrlr.go create mode 100644 ocpp_v201/controllers/sampled_data_ctrlr_test.go create mode 100644 ocpp_v201/controllers/security_ctrlr.go create mode 100644 ocpp_v201/controllers/security_ctrlr_test.go create mode 100644 ocpp_v201/controllers/smart_charging_ctrlr.go create mode 100644 ocpp_v201/controllers/smart_charging_ctrlr_test.go create mode 100644 ocpp_v201/controllers/tariff_cost_ctrlr.go create mode 100644 ocpp_v201/controllers/tariff_cost_ctrlr_test.go create mode 100644 ocpp_v201/controllers/tx_ctrlr.go create mode 100644 ocpp_v201/controllers/tx_ctrlr_test.go rename ocpp_v201/{chademo_ctrlr.go => ctrlr_manager_test.go} (100%) delete mode 100644 ocpp_v201/customization_ctrlr.go delete mode 100644 ocpp_v201/device_data_ctrlr.go delete mode 100644 ocpp_v201/display_message_ctrlr.go delete mode 100644 ocpp_v201/iso15118_ctrlr.go delete mode 100644 ocpp_v201/local_auth_list_ctrlr.go delete mode 100644 ocpp_v201/monitoring_ctrlr.go delete mode 100644 ocpp_v201/ocpp_comm_ctrlr.go delete mode 100644 ocpp_v201/reservation_ctrlr.go delete mode 100644 ocpp_v201/sampled_data_ctrlr.go delete mode 100644 ocpp_v201/security_ctrlr.go delete mode 100644 ocpp_v201/smart_charging_ctrlr.go delete mode 100644 ocpp_v201/tariff_cost_ctrlr.go delete mode 100644 ocpp_v201/tx_ctrlr.go delete mode 100644 ocpp_v201/variables.go create mode 100644 ocpp_v201/variables/variables.go create mode 100644 ocpp_v201/variables/variables_test.go diff --git a/ocpp_v201/aligned_data_ctrlr.go b/ocpp_v201/aligned_data_ctrlr.go deleted file mode 100644 index 6c6bea4..0000000 --- a/ocpp_v201/aligned_data_ctrlr.go +++ /dev/null @@ -1,12 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameAlignedDataEnabled VariableName = "Enabled" - VariableNameAlignedDataAvailable VariableName = "Available" - VariableNameAlignedDataMeasurands VariableName = "Measurands" - VariableNameAlignedDataInterval VariableName = "Interval" - VariableNameAlignedDataSendDuringIdle VariableName = "SendDuringIdle" - VariableNameAlignedDataSignReadings VariableName = "SignReadings" - VariableNameAlignedDataTxEndedMeasurands VariableName = "TxEndedMeasurands" - VariableNameAlignedDataTxEndedInterval VariableName = "TxEndedInterval" -) diff --git a/ocpp_v201/auth_cache_ctrlr.go b/ocpp_v201/auth_cache_ctrlr.go deleted file mode 100644 index 1a13d1c..0000000 --- a/ocpp_v201/auth_cache_ctrlr.go +++ /dev/null @@ -1,10 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameAuthCacheEnabled VariableName = "Enabled" - VariableNameAuthCacheAvailable VariableName = "Available" - VariableNameAuthCacheLifeTime VariableName = "LifeTime" - VariableNameAuthCacheStorage VariableName = "Storage" - VariableNameAuthCachePolicy VariableName = "Policy" - VariableNameAuthCacheDisablePostAuthorize VariableName = "DisablePostAuthorize" -) diff --git a/ocpp_v201/auth_ctrlr.go b/ocpp_v201/auth_ctrlr.go deleted file mode 100644 index 5f181d3..0000000 --- a/ocpp_v201/auth_ctrlr.go +++ /dev/null @@ -1,12 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameAuthEnabled VariableName = "Enabled" - VariableNameAdditionalInfoItemsPerMessage VariableName = "AdditionalInfoItemsPerMessage" - VariableNameOfflineTxForUnknownIdEnabled VariableName = "OfflineTxForUnknownIdEnabled" - VariableNameAuthorizeRemoteStart VariableName = "AuthorizeRemoteStart" - VariableNameLocalAuthorizeOffline VariableName = "LocalAuthorizeOffline" - VariableNameLocalPreAuthorize VariableName = "LocalPreAuthorize" - VariableNameMasterPassGroupId VariableName = "MasterPassGroupId" - VariableNameDisableRemoteAuthorization VariableName = "DisableRemoteAuthorization" -) diff --git a/ocpp_v201/charging_infrastructure.go b/ocpp_v201/charging_infrastructure.go index 607262c..5d29fc0 100644 --- a/ocpp_v201/charging_infrastructure.go +++ b/ocpp_v201/charging_infrastructure.go @@ -1 +1,17 @@ package ocpp_v201 + +import "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + +type ChargingStation struct { + components map[component.ComponentName]component.Component // Charging station (top level) specific components +} + +type EVSE struct { + ID int + components map[component.ComponentName]component.Component +} + +type Connector struct { + ID int + components map[component.ComponentName]component.Component +} diff --git a/ocpp_v201/clock_ctrlr.go b/ocpp_v201/clock_ctrlr.go deleted file mode 100644 index f3b7427..0000000 --- a/ocpp_v201/clock_ctrlr.go +++ /dev/null @@ -1,11 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameNtpSource VariableName = "NtpSource" - VariableNameNtpServerUri VariableName = "NtpServerUri" - VariableNameTimeOffset VariableName = "TimeOffset" - VariableNameNextTimeOffsetTransitionDateTime VariableName = "NextTimeOffsetTransitionDateTime" - VariableNameTimeSource VariableName = "TimeSource" - VariableNameTimeZone VariableName = "TimeZone" - VariableNameTimeAdjustmentReportingThreshold VariableName = "TimeAdjustmentReportingThreshold" -) diff --git a/ocpp_v201/components.go b/ocpp_v201/component/component.go similarity index 58% rename from ocpp_v201/components.go rename to ocpp_v201/component/component.go index 5a69416..e197945 100644 --- a/ocpp_v201/components.go +++ b/ocpp_v201/component/component.go @@ -1,4 +1,28 @@ -package ocpp_v201 +package component + +import "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" + +type Component interface { + // Essentially a component type. + GetName() ComponentName + // Instance ID where a component can be addressed + GetInstanceId() string + // RegisterSubComponent registers a sub-component to this component. + RegisterSubComponent(component Component) + // UnregisterSubComponent unregisters a sub-component from this component. + UnregisterSubComponent(component Component) + // GetSubComponents returns all sub-components of this component. + GetSubComponents() []Component + + // Required variables for this component + GetRequiredVariables() []variables.Variable + // Suported variables for this component + GetSupportedVariables() []variables.Variable + + GetVariable(key variables.VariableName, opts ...GetSetVariableOption) (*variables.Variable, error) + UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...GetSetVariableOption) error + Validate(key variables.VariableName) bool +} type ComponentName string @@ -25,9 +49,3 @@ const ( ComponentNameConnector ComponentName = "Connector" ComponentNameConnectedEV ComponentName = "ConnectedEV" ) - -type Controller interface { - Get() (*Variable, error) - Update(key VariableName, value interface{}) error - Validate(key VariableName) bool -} diff --git a/ocpp_v201/component/component_variable_opts.go b/ocpp_v201/component/component_variable_opts.go new file mode 100644 index 0000000..7a202f8 --- /dev/null +++ b/ocpp_v201/component/component_variable_opts.go @@ -0,0 +1,13 @@ +package component + +type GetSetVariableOption func(o *componentVariableOptions) + +type componentVariableOptions struct { + attributeType string +} + +func WithAttributeType(attributeType string) GetSetVariableOption { + return func(o *componentVariableOptions) { + o.attributeType = attributeType + } +} diff --git a/ocpp_v201/controllers/aligned_data_ctrlr.go b/ocpp_v201/controllers/aligned_data_ctrlr.go new file mode 100644 index 0000000..a1fe385 --- /dev/null +++ b/ocpp_v201/controllers/aligned_data_ctrlr.go @@ -0,0 +1,103 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameAlignedDataEnabled variables.VariableName = "Enabled" + VariableNameAlignedDataAvailable variables.VariableName = "Available" + VariableNameAlignedDataMeasurands variables.VariableName = "Measurands" + VariableNameAlignedDataInterval variables.VariableName = "Interval" + VariableNameAlignedDataSendDuringIdle variables.VariableName = "SendDuringIdle" + VariableNameAlignedDataSignReadings variables.VariableName = "SignReadings" + VariableNameAlignedDataTxEndedMeasurands variables.VariableName = "TxEndedMeasurands" + VariableNameAlignedDataTxEndedInterval variables.VariableName = "TxEndedInterval" +) + +func requiredAlignedDataVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameAlignedDataMeasurands, + VariableNameAlignedDataInterval, + VariableNameAlignedDataTxEndedMeasurands, + VariableNameAlignedDataTxEndedInterval, + } +} + +func optionalAlignedDataVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameAlignedDataEnabled, + VariableNameAlignedDataAvailable, + VariableNameAlignedDataSendDuringIdle, + VariableNameAlignedDataSignReadings, + } +} + +func supportedAlignedDataVariables() []variables.VariableName { + return append(requiredAlignedDataVariables(), optionalAlignedDataVariables()...) +} + +type AlignedDataCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (a *AlignedDataCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (a *AlignedDataCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (a *AlignedDataCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (a *AlignedDataCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (a *AlignedDataCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (a *AlignedDataCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (a *AlignedDataCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (a *AlignedDataCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (a *AlignedDataCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (a *AlignedDataCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewAlignedDataCtrlr() *AlignedDataCtrlr { + return &AlignedDataCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredAlignedDataVariables(), + supportedVariables: supportedAlignedDataVariables(), + } +} diff --git a/ocpp_v201/controllers/aligned_data_ctrlr_test.go b/ocpp_v201/controllers/aligned_data_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/aligned_data_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/auth_cache_ctrlr.go b/ocpp_v201/controllers/auth_cache_ctrlr.go new file mode 100644 index 0000000..fb7837e --- /dev/null +++ b/ocpp_v201/controllers/auth_cache_ctrlr.go @@ -0,0 +1,98 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameAuthCacheEnabled variables.VariableName = "Enabled" + VariableNameAuthCacheAvailable variables.VariableName = "Available" + VariableNameAuthCacheLifeTime variables.VariableName = "LifeTime" + VariableNameAuthCacheStorage variables.VariableName = "Storage" + VariableNameAuthCachePolicy variables.VariableName = "Policy" + VariableNameAuthCacheDisablePostAuthorize variables.VariableName = "DisablePostAuthorize" +) + +func requiredAuthCacheVariables() []variables.VariableName { + return []variables.VariableName{} +} + +func optionalAuthCacheVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameAuthCacheAvailable, + VariableNameAuthCacheEnabled, + VariableNameAuthCacheLifeTime, + VariableNameAuthCacheStorage, + VariableNameAuthCachePolicy, + VariableNameAuthCacheDisablePostAuthorize, + } +} + +func supportedAuthCacheVariables() []variables.VariableName { + return append(requiredAuthCacheVariables(), optionalAuthCacheVariables()...) +} + +type AuthCacheCtrlr struct { + variables map[variables.VariableName]variables.Variable + supportedVariables []variables.VariableName + requiredVariables []variables.VariableName +} + +func (a *AuthCacheCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (a *AuthCacheCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (a *AuthCacheCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (a *AuthCacheCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (a *AuthCacheCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (a *AuthCacheCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (a *AuthCacheCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (a *AuthCacheCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (a *AuthCacheCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (a *AuthCacheCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewAuthCacheCtrlr() *AuthCacheCtrlr { + return &AuthCacheCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + supportedVariables: supportedAuthCacheVariables(), + requiredVariables: requiredAuthCacheVariables(), + } +} diff --git a/ocpp_v201/controllers/auth_cache_ctrlr_test.go b/ocpp_v201/controllers/auth_cache_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/auth_cache_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/auth_ctrlr.go b/ocpp_v201/controllers/auth_ctrlr.go new file mode 100644 index 0000000..fac961f --- /dev/null +++ b/ocpp_v201/controllers/auth_ctrlr.go @@ -0,0 +1,103 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameAuthEnabled variables.VariableName = "Enabled" + VariableNameAdditionalInfoItemsPerMessage variables.VariableName = "AdditionalInfoItemsPerMessage" + VariableNameOfflineTxForUnknownIdEnabled variables.VariableName = "OfflineTxForUnknownIdEnabled" + VariableNameAuthorizeRemoteStart variables.VariableName = "AuthorizeRemoteStart" + VariableNameLocalAuthorizeOffline variables.VariableName = "LocalAuthorizeOffline" + VariableNameLocalPreAuthorize variables.VariableName = "LocalPreAuthorize" + VariableNameMasterPassGroupId variables.VariableName = "MasterPassGroupId" + VariableNameDisableRemoteAuthorization variables.VariableName = "DisableRemoteAuthorization" +) + +func requiredAuthVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameAuthorizeRemoteStart, + VariableNameLocalAuthorizeOffline, + VariableNameLocalPreAuthorize, + } +} + +func optionalAuthVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameAuthEnabled, + VariableNameAdditionalInfoItemsPerMessage, + VariableNameOfflineTxForUnknownIdEnabled, + VariableNameMasterPassGroupId, + VariableNameDisableRemoteAuthorization, + } +} + +func supportedAuthVariables() []variables.VariableName { + return append(requiredAuthVariables(), optionalAuthVariables()...) +} + +type AuthCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (a *AuthCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (a *AuthCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (a *AuthCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (a *AuthCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (a *AuthCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (a *AuthCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (a *AuthCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (a *AuthCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (a *AuthCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (a *AuthCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewAuthCtrlr() *AuthCtrlr { + return &AuthCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredAuthVariables(), + supportedVariables: supportedAuthVariables(), + } +} diff --git a/ocpp_v201/controllers/auth_ctrlr_test.go b/ocpp_v201/controllers/auth_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/auth_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/chademo_ctrlr.go b/ocpp_v201/controllers/chademo_ctrlr.go new file mode 100644 index 0000000..90545bf --- /dev/null +++ b/ocpp_v201/controllers/chademo_ctrlr.go @@ -0,0 +1,13 @@ +package controllers + +import variables2 "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" + +type ChademoCtrlr struct { + variables map[variables2.VariableName]variables2.Variable + requiredVariables []variables2.VariableName + supportedVariables []variables2.VariableName +} + +func NewChademoCtrlr() *ChademoCtrlr { + return &ChademoCtrlr{} +} diff --git a/ocpp_v201/controllers/chademo_ctrlr_test.go b/ocpp_v201/controllers/chademo_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/chademo_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/clock_ctrlr.go b/ocpp_v201/controllers/clock_ctrlr.go new file mode 100644 index 0000000..fd9fe18 --- /dev/null +++ b/ocpp_v201/controllers/clock_ctrlr.go @@ -0,0 +1,106 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameDateTime variables.VariableName = "DateTime" + VariableNameNtpSource variables.VariableName = "NtpSource" + VariableNameNtpServerUri variables.VariableName = "NtpServerUri" + VariableNameTimeOffset variables.VariableName = "TimeOffset" + VariableNameNextTimeOffsetTransitionDateTime variables.VariableName = "NextTimeOffsetTransitionDateTime" + VariableNameTimeOffsetNextTransition variables.VariableName = "TimeOffsetNextTransition" + VariableNameTimeSource variables.VariableName = "TimeSource" + VariableNameTimeZone variables.VariableName = "TimeZone" + VariableNameTimeAdjustmentReportingThreshold variables.VariableName = "TimeAdjustmentReportingThreshold" +) + +func requiredClockVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameDateTime, + VariableNameTimeSource, + } +} + +func optionalClockVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameTimeZone, + VariableNameNtpSource, + VariableNameNtpServerUri, + VariableNameTimeOffsetNextTransition, + VariableNameNextTimeOffsetTransitionDateTime, + VariableNameTimeOffset, + VariableNameTimeAdjustmentReportingThreshold, + } +} + +// supportedClockVariables returns a list of all variables supported by the ClockCtrlr. +func supportedClockVariables() []variables.VariableName { + return append(requiredClockVariables(), optionalClockVariables()...) +} + +type ClockCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (c *ClockCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (c *ClockCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (c *ClockCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (c *ClockCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (c *ClockCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (c *ClockCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (c *ClockCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (c *ClockCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (c *ClockCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (c *ClockCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewClockCtrlr() *ClockCtrlr { + return &ClockCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredClockVariables(), + supportedVariables: supportedClockVariables(), + } +} diff --git a/ocpp_v201/controllers/clock_ctrlr_test.go b/ocpp_v201/controllers/clock_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/clock_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/customization_ctrlr.go b/ocpp_v201/controllers/customization_ctrlr.go new file mode 100644 index 0000000..9a8079b --- /dev/null +++ b/ocpp_v201/controllers/customization_ctrlr.go @@ -0,0 +1,66 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameCustomImplementationEnabled variables.VariableName = "CustomImplementationEnabled" +) + +type CustomizationCtrlr struct{} + +func (c *CustomizationCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (c *CustomizationCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (c *CustomizationCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (c *CustomizationCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (c *CustomizationCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (c *CustomizationCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (c *CustomizationCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (c *CustomizationCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (c *CustomizationCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (c *CustomizationCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewCustomizationCtrlr() *CustomizationCtrlr { + return &CustomizationCtrlr{} +} diff --git a/ocpp_v201/controllers/device_data_ctrlr.go b/ocpp_v201/controllers/device_data_ctrlr.go new file mode 100644 index 0000000..00fa09b --- /dev/null +++ b/ocpp_v201/controllers/device_data_ctrlr.go @@ -0,0 +1,90 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameBytesPerMessage variables.VariableName = "BytesPerMessage" + VariableNameConfigurationValueSize variables.VariableName = "ConfigurationValueSize" + VariableNameReportingValueSize variables.VariableName = "ReportingValueSize" + VariableNameItemsPerMessage variables.VariableName = "ItemsPerMessage" +) + +func requiredVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameBytesPerMessage, + VariableNameConfigurationValueSize, + VariableNameReportingValueSize, + VariableNameItemsPerMessage, + } +} + +func optionalVariables() []variables.VariableName { + return []variables.VariableName{} +} + +func supportedVariables() []variables.VariableName { + return append(requiredVariables(), optionalVariables()...) +} + +type DeviceDataCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (d *DeviceDataCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (d *DeviceDataCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (d *DeviceDataCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (d *DeviceDataCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (d *DeviceDataCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (d *DeviceDataCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (d *DeviceDataCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (d *DeviceDataCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (d *DeviceDataCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (d *DeviceDataCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewDeviceDataCtrlr() *DeviceDataCtrlr { + return &DeviceDataCtrlr{} +} diff --git a/ocpp_v201/controllers/device_data_ctrlr_test.go b/ocpp_v201/controllers/device_data_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/device_data_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/display_message_ctrlr.go b/ocpp_v201/controllers/display_message_ctrlr.go new file mode 100644 index 0000000..9ea7013 --- /dev/null +++ b/ocpp_v201/controllers/display_message_ctrlr.go @@ -0,0 +1,97 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameDisplayMessageEnabled variables.VariableName = "Enabled" + VariableNameDisplayMessageAvailable variables.VariableName = "Available" + VariableNameNumberOfDisplayMessages variables.VariableName = "DisplayMessages" + VariableNameDisplayMessageSupportedFormats variables.VariableName = "SupportedFormats" + VariableNameDisplayMessageSupportedPriorities variables.VariableName = "SupportedPriorities" +) + +func requiredDisplayMessageVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameNumberOfDisplayMessages, + VariableNameDisplayMessageSupportedFormats, + VariableNameDisplayMessageSupportedPriorities, + } +} + +func optionalDisplayMessageVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameDisplayMessageEnabled, + VariableNameDisplayMessageAvailable, + } +} + +func supportedDisplayMessageVariables() []variables.VariableName { + return append(requiredDisplayMessageVariables(), optionalDisplayMessageVariables()...) +} + +type DisplayCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (d *DisplayCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (d *DisplayCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (d *DisplayCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (d *DisplayCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (d *DisplayCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (d *DisplayCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (d *DisplayCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (d *DisplayCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (d *DisplayCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (d *DisplayCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewDisplayCtrlr() *DisplayCtrlr { + return &DisplayCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredDisplayMessageVariables(), + supportedVariables: supportedDisplayMessageVariables(), + } +} diff --git a/ocpp_v201/controllers/display_message_ctrlr_test.go b/ocpp_v201/controllers/display_message_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/display_message_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/iso15118_ctrlr.go b/ocpp_v201/controllers/iso15118_ctrlr.go new file mode 100644 index 0000000..3155914 --- /dev/null +++ b/ocpp_v201/controllers/iso15118_ctrlr.go @@ -0,0 +1,109 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameCentralContractValidationAllowed variables.VariableName = "CentralContractValidationAllowed" + VariableNameContractValidationOffline variables.VariableName = "ContractValidationOffline" + VariableNameProtocolSupportedByEV variables.VariableName = "ProtocolSupportedByEV" + VariableNameProtocolAgreed variables.VariableName = "ProtocolAgreed" + VariableNameISO15118PnCEnabled variables.VariableName = "PnCEnabled" + VariableNameISO15118V2GCertificateInstallationEnabled variables.VariableName = "V2GCertificateInstallationEnabled" + VariableNameISO15118ContractCertificateInstallationEnabled variables.VariableName = "ContractCertificateInstallationEnabled" + VariableNameISO15118RequestMeteringReceipt variables.VariableName = "RequestMeteringReceipt" + VariableNameISO15118SeccId variables.VariableName = "SeccId" + VariableNameISO15118CountryName variables.VariableName = "CountryName" + VariableNameISO15118EvseId variables.VariableName = "ISO15118EvseId" +) + +func requiredVariablesISO15118Ctrlr() []variables.VariableName { + return []variables.VariableName{ + VariableNameCentralContractValidationAllowed, + VariableNameContractValidationOffline, + VariableNameProtocolSupportedByEV, + VariableNameProtocolAgreed, + } +} + +func optionalVariablesISO15118Ctrlr() []variables.VariableName { + return []variables.VariableName{ + VariableNameISO15118PnCEnabled, + VariableNameISO15118V2GCertificateInstallationEnabled, + VariableNameISO15118ContractCertificateInstallationEnabled, + VariableNameISO15118RequestMeteringReceipt, + VariableNameISO15118SeccId, + VariableNameISO15118CountryName, + VariableNameISO15118EvseId, + } +} + +func supportedVariablesISO15118Ctrlr() []variables.VariableName { + return append(requiredVariablesISO15118Ctrlr(), optionalVariablesISO15118Ctrlr()...) +} + +type ISO15118Ctrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (I *ISO15118Ctrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (I *ISO15118Ctrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (I *ISO15118Ctrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (I *ISO15118Ctrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (I *ISO15118Ctrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (I *ISO15118Ctrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (I *ISO15118Ctrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (I *ISO15118Ctrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (I *ISO15118Ctrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (I *ISO15118Ctrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewISO15118Ctrlr() *ISO15118Ctrlr { + return &ISO15118Ctrlr{ + variables: map[variables.VariableName]variables.Variable{}, + requiredVariables: requiredVariablesISO15118Ctrlr(), + supportedVariables: supportedVariablesISO15118Ctrlr(), + } +} diff --git a/ocpp_v201/controllers/local_auth_list_ctrlr.go b/ocpp_v201/controllers/local_auth_list_ctrlr.go new file mode 100644 index 0000000..9a84057 --- /dev/null +++ b/ocpp_v201/controllers/local_auth_list_ctrlr.go @@ -0,0 +1,100 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameLocalAuthListEnabled variables.VariableName = "Enabled" + VariableNameLocalAuthListEntries variables.VariableName = "Entries" + VariableNameLocalAuthListItemsPerMessage variables.VariableName = "ItemsPerMessage" + VariableNameLocalAuthListBytesPerMessage variables.VariableName = "BytesPerMessage" + VariableNameLocalAuthListStorage variables.VariableName = "Storage" + VariableNameLocalAuthListDisablePostAuthorize variables.VariableName = "DisablePostAuthorize" + VariableNameLocalAuthListSupportsExpiryDateTime variables.VariableName = "SupportsExpiryDateTime" +) + +func requiredLocalAuthListVariables() []variables.VariableName { + return []variables.VariableName{} +} + +func optionalLocalAuthListVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameLocalAuthListItemsPerMessage, + VariableNameLocalAuthListBytesPerMessage, + VariableNameLocalAuthListStorage, + VariableNameLocalAuthListDisablePostAuthorize, + VariableNameLocalAuthListSupportsExpiryDateTime, + VariableNameLocalAuthListEnabled, + VariableNameLocalAuthListEntries, + } +} + +func supportedLocalAuthListVariables() []variables.VariableName { + return append(requiredLocalAuthListVariables(), optionalLocalAuthListVariables()...) +} + +type LocalAuthListCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (l *LocalAuthListCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (l *LocalAuthListCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (l *LocalAuthListCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (l *LocalAuthListCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (l *LocalAuthListCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (l *LocalAuthListCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (l *LocalAuthListCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (l *LocalAuthListCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (l *LocalAuthListCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (l *LocalAuthListCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewLocalAuthListCtrlr() *LocalAuthListCtrlr { + return &LocalAuthListCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredLocalAuthListVariables(), + supportedVariables: supportedLocalAuthListVariables(), + } +} diff --git a/ocpp_v201/controllers/local_auth_list_ctrlr_test.go b/ocpp_v201/controllers/local_auth_list_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/local_auth_list_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/monitoring_ctrlr.go b/ocpp_v201/controllers/monitoring_ctrlr.go new file mode 100644 index 0000000..a05e136 --- /dev/null +++ b/ocpp_v201/controllers/monitoring_ctrlr.go @@ -0,0 +1,105 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameMonitoringEnabled variables.VariableName = "Enabled" + VariableNameMonitoringAvailable variables.VariableName = "Available" + VariableNameItemsPerMessageClearVariableMonitoring variables.VariableName = "ItemsPerMessage" + VariableNameItemsPerMessageSetVariableMonitoring variables.VariableName = "ItemsPerMessage" + VariableNameClearVariableMonitoring variables.VariableName = "BytesPerMessage" + VariableNameBytesPerMessageSetVariableMonitoring variables.VariableName = "BytesPerMessage" + VariableNameOfflineMonitoringEventQueuingSeverity variables.VariableName = "OfflineQueuingSeverity" + VariableNameActiveMonitoringBase variables.VariableName = "ActiveMonitoringBase" + VariableNameActiveMonitoringLevel variables.VariableName = "ActiveMonitoringLevel" +) + +func requiredMonitoringVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameItemsPerMessageSetVariableMonitoring, + VariableNameBytesPerMessageSetVariableMonitoring, + } +} + +func optionalMonitoringVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameClearVariableMonitoring, + VariableNameMonitoringEnabled, + VariableNameMonitoringAvailable, + VariableNameItemsPerMessageClearVariableMonitoring, + VariableNameOfflineMonitoringEventQueuingSeverity, + VariableNameActiveMonitoringBase, + VariableNameActiveMonitoringLevel, + } +} + +func supportedMonitoringVariables() []variables.VariableName { + return append(requiredMonitoringVariables(), optionalMonitoringVariables()...) +} + +type MonitoringCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (m *MonitoringCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (m *MonitoringCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (m *MonitoringCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (m *MonitoringCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (m *MonitoringCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (m *MonitoringCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (m *MonitoringCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (m *MonitoringCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (m *MonitoringCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (m *MonitoringCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewMonitoringCtrlr() *MonitoringCtrlr { + return &MonitoringCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredMonitoringVariables(), + supportedVariables: supportedMonitoringVariables(), + } +} diff --git a/ocpp_v201/controllers/monitoring_ctrlr_test.go b/ocpp_v201/controllers/monitoring_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/monitoring_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/ocpp_comm_ctrlr.go b/ocpp_v201/controllers/ocpp_comm_ctrlr.go new file mode 100644 index 0000000..1255475 --- /dev/null +++ b/ocpp_v201/controllers/ocpp_comm_ctrlr.go @@ -0,0 +1,127 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameDefaultMessageTimeout variables.VariableName = "DefaultMessageTimeout" + VariableNameNetworkProfileConnectionAttempts variables.VariableName = "NetworkProfileConnectionAttempts" + VariableNameNetworkConfigurationPriority variables.VariableName = "NetworkConfigurationPriority" + VariableNameHeartbeatInterval variables.VariableName = "HeartbeatInterval" + VariableNameFileTransferProtocols variables.VariableName = "FileTransferProtocols" + VariableNameMessageTimeout variables.VariableName = "MessageTimeout" + VariableNameActiveNetworkProfile variables.VariableName = "ActiveNetworkProfile" + VariableNameOfflineThreshold variables.VariableName = "OfflineThreshold" + VariableNameQueueAllMessages variables.VariableName = "QueueAllMessages" + VariableNameMessageAttempts variables.VariableName = "MessageAttempts" + VariableNameMessageAttemptInterval variables.VariableName = "MessageAttemptInterval" + VariableNameMessageAttemptsTransactionEvent variables.VariableName = "MessageAttemptsTransactionEvent" + VariableNameMessageAttemptIntervalTransactionEvent variables.VariableName = "MessageAttemptIntervalTransactionEvent" + VariableNameUnlockOnEVSideDisconnect variables.VariableName = "UnlockOnEVSideDisconnect" + VariableNameWebSocketPingInterval variables.VariableName = "WebSocketPingInterval" + VariableNameResetRetries variables.VariableName = "ResetRetries" + VariableNameFieldLength variables.VariableName = "FieldLength" + VariableNamePublicKeyWithSignedMeterValue variables.VariableName = "PublicKeyWithSignedMeterValue" +) + +func requiredOcppCommCtrlrVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameDefaultMessageTimeout, + VariableNameFileTransferProtocols, + VariableNameOfflineThreshold, + VariableNameNetworkProfileConnectionAttempts, + VariableNameMessageAttempts, + VariableNameMessageAttemptInterval, + VariableNameMessageAttemptsTransactionEvent, + VariableNameMessageAttemptIntervalTransactionEvent, + VariableNameUnlockOnEVSideDisconnect, + VariableNameResetRetries, + } +} + +func optionalOcppCommCtrlrVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameNetworkConfigurationPriority, + VariableNameHeartbeatInterval, + VariableNameMessageTimeout, + VariableNameActiveNetworkProfile, + VariableNameQueueAllMessages, + VariableNameWebSocketPingInterval, + VariableNameFieldLength, + VariableNamePublicKeyWithSignedMeterValue, + } +} + +func supportedOcppCommCtrlrVariables() []variables.VariableName { + return append(requiredOcppCommCtrlrVariables(), optionalOcppCommCtrlrVariables()...) +} + +type OCPPCommCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (ctrlr *OCPPCommCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (ctrlr *OCPPCommCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (ctrlr *OCPPCommCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (ctrlr *OCPPCommCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (ctrlr *OCPPCommCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (ctrlr *OCPPCommCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (ctrlr *OCPPCommCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (ctrlr *OCPPCommCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (ctrlr *OCPPCommCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (ctrlr *OCPPCommCtrlr) Validate(key variables.VariableName) bool { + v, exists := ctrlr.variables[key] + if !exists { + return false + } + + return v.Validate() +} + +func NewOCPPCommCtrlr() *OCPPCommCtrlr { + return &OCPPCommCtrlr{ + variables: map[variables.VariableName]variables.Variable{}, + requiredVariables: requiredOcppCommCtrlrVariables(), + supportedVariables: supportedOcppCommCtrlrVariables(), + } +} diff --git a/ocpp_v201/controllers/ocpp_comm_ctrlr_test.go b/ocpp_v201/controllers/ocpp_comm_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/ocpp_comm_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/reservation_ctrlr.go b/ocpp_v201/controllers/reservation_ctrlr.go new file mode 100644 index 0000000..ab39561 --- /dev/null +++ b/ocpp_v201/controllers/reservation_ctrlr.go @@ -0,0 +1,88 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameReservationEnabled variables.VariableName = "Enabled" + VariableNameReservationAvailable variables.VariableName = "Available" + VariableNameReservationNonEvseSpecific variables.VariableName = "NonEvseSpecific" +) + +func optionalReservationVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameReservationEnabled, + VariableNameReservationAvailable, + VariableNameReservationNonEvseSpecific, + } +} + +func supportedReservationVariables() []variables.VariableName { + return optionalReservationVariables() +} + +type ReservationCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (r *ReservationCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (r *ReservationCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (r *ReservationCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (r *ReservationCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (r *ReservationCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (r *ReservationCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (r *ReservationCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (r *ReservationCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (r *ReservationCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (r *ReservationCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewReservationCtrlr() *ReservationCtrlr { + return &ReservationCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: []variables.VariableName{}, // No required variables for ReservationCtrlr + supportedVariables: supportedReservationVariables(), + } +} diff --git a/ocpp_v201/controllers/reservation_ctrlr_test.go b/ocpp_v201/controllers/reservation_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/reservation_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/sampled_data_ctrlr.go b/ocpp_v201/controllers/sampled_data_ctrlr.go new file mode 100644 index 0000000..3948b91 --- /dev/null +++ b/ocpp_v201/controllers/sampled_data_ctrlr.go @@ -0,0 +1,105 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameSampledDataEnabled variables.VariableName = "Enabled" + VariableNameSampledDataAvailable variables.VariableName = "Available" + VariableNameSampledDataSignReadings variables.VariableName = "SignReadings" + VariableNameSampledDataTxEndedMeasurands variables.VariableName = "TxEndedMeasurands" + VariableNameSampledDataTxEndedInterval variables.VariableName = "TxEndedInterval" + VariableNameSampledDataTxStartedMeasurands variables.VariableName = "TxStartedMeasurands" + VariableNameSampledDataTxUpdatedMeasurands variables.VariableName = "TxUpdatedMeasurands" + VariableNameSampledDataTxUpdatedInterval variables.VariableName = "TxUpdatedInterval" + VariableNameSampledDataRegisterValuesWithoutPhases variables.VariableName = "RegisterValuesWithoutPhases" +) + +func requiredSampledDataVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameSampledDataTxEndedMeasurands, + VariableNameSampledDataTxEndedInterval, + VariableNameSampledDataTxStartedMeasurands, + VariableNameSampledDataTxUpdatedMeasurands, + VariableNameSampledDataTxUpdatedInterval, + } +} + +func optionalSampledDataVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameSampledDataEnabled, + VariableNameSampledDataAvailable, + VariableNameSampledDataSignReadings, + VariableNameSampledDataRegisterValuesWithoutPhases, + } +} + +func supportedSampledDataVariables() []variables.VariableName { + return append(requiredSampledDataVariables(), optionalSampledDataVariables()...) +} + +type SampledDataCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (s *SampledDataCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (s *SampledDataCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (s *SampledDataCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (s *SampledDataCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (s *SampledDataCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (s *SampledDataCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (s *SampledDataCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (s *SampledDataCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (s *SampledDataCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (s *SampledDataCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewSampledDataCtrlr() *SampledDataCtrlr { + return &SampledDataCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredSampledDataVariables(), + supportedVariables: supportedSampledDataVariables(), + } +} diff --git a/ocpp_v201/controllers/sampled_data_ctrlr_test.go b/ocpp_v201/controllers/sampled_data_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/sampled_data_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/security_ctrlr.go b/ocpp_v201/controllers/security_ctrlr.go new file mode 100644 index 0000000..7fe7c8d --- /dev/null +++ b/ocpp_v201/controllers/security_ctrlr.go @@ -0,0 +1,105 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameBasicAuthPassword variables.VariableName = "BasicAuthPassword" + VariableNameIdentity variables.VariableName = "Identity" + VariableNameOrganizationName variables.VariableName = "OrganizationName" + VariableNameCertificateEntries variables.VariableName = "CertificateEntries" + VariableNameAdditionalRootCertificateCheck variables.VariableName = "AdditionalRootCertificateCheck" + VariableNameSecurityProfile variables.VariableName = "SecurityProfile" + VariableNameMaxCertificateChainSize variables.VariableName = "MaxCertificateChainSize" + VariableNameCertSigningWaitMinimum variables.VariableName = "CertSigningWaitMinimum" + VariableNameCertSigningRepeatTimes variables.VariableName = "CertSigningRepeatTimes" +) + +func requiredSecurityVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameOrganizationName, + VariableNameCertificateEntries, + VariableNameSecurityProfile, + } +} + +func optionalSecurityVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameBasicAuthPassword, + VariableNameIdentity, + VariableNameAdditionalRootCertificateCheck, + VariableNameCertSigningRepeatTimes, + VariableNameMaxCertificateChainSize, + VariableNameCertSigningWaitMinimum, + } +} + +func supportedSecurityVariables() []variables.VariableName { + return append(requiredSecurityVariables(), optionalSecurityVariables()...) +} + +type SecurityCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (s *SecurityCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (s *SecurityCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (s *SecurityCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (s *SecurityCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (s *SecurityCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (s *SecurityCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (s *SecurityCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (s *SecurityCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (s *SecurityCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (s *SecurityCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewSecurityCtrlr() *SecurityCtrlr { + return &SecurityCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredSecurityVariables(), + supportedVariables: supportedSecurityVariables(), + } +} diff --git a/ocpp_v201/controllers/security_ctrlr_test.go b/ocpp_v201/controllers/security_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/security_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/smart_charging_ctrlr.go b/ocpp_v201/controllers/smart_charging_ctrlr.go new file mode 100644 index 0000000..f40f410 --- /dev/null +++ b/ocpp_v201/controllers/smart_charging_ctrlr.go @@ -0,0 +1,109 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameSmartChargingEnabled variables.VariableName = "Enabled" + VariableNameSmartChargingAvailable variables.VariableName = "Available" + VariableNameACPhaseSwitchingSupported variables.VariableName = "ACPhaseSwitchingSupported" + VariableNameChargingProfileStackLevel variables.VariableName = "ProfileStackLevel" + VariableNameChargingScheduleChargingRateUnit variables.VariableName = "RateUnit" + VariableNamePeriodsPerSchedule variables.VariableName = "PeriodsPerSchedule" + VariableNameExternalControlSignalsEnabled variables.VariableName = "ExternalControlSignalsEnabled" + VariableNameNotifyChargingLimitWithSchedules variables.VariableName = "NotifyChargingLimitWithSchedules" + VariableNamePhases3to1 variables.VariableName = "Phases3to1" + VariableChargingProfileEntries variables.VariableName = "Entries" + VariableLimitChangeSignificance variables.VariableName = "LimitChangeSignificance" +) + +func requiredSmartChargingVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameChargingProfileStackLevel, + VariableNameChargingScheduleChargingRateUnit, + VariableNamePeriodsPerSchedule, + VariableChargingProfileEntries, + VariableLimitChangeSignificance, + } +} + +func optionalSmartChargingVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameSmartChargingEnabled, + VariableNameSmartChargingAvailable, + VariableNameACPhaseSwitchingSupported, + VariableNameExternalControlSignalsEnabled, + VariableNameNotifyChargingLimitWithSchedules, + VariableNamePhases3to1, + } +} + +func supportedSmartChargingVariables() []variables.VariableName { + return append(requiredSmartChargingVariables(), optionalSmartChargingVariables()...) +} + +type SmartChargingCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (s *SmartChargingCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (s *SmartChargingCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (s *SmartChargingCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (s *SmartChargingCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (s *SmartChargingCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (s *SmartChargingCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (s *SmartChargingCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (s *SmartChargingCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (s *SmartChargingCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (s *SmartChargingCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewSmartChargingCtrlr() *SmartChargingCtrlr { + return &SmartChargingCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredSmartChargingVariables(), + supportedVariables: supportedSmartChargingVariables(), + } +} diff --git a/ocpp_v201/controllers/smart_charging_ctrlr_test.go b/ocpp_v201/controllers/smart_charging_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/smart_charging_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/tariff_cost_ctrlr.go b/ocpp_v201/controllers/tariff_cost_ctrlr.go new file mode 100644 index 0000000..9e2ab04 --- /dev/null +++ b/ocpp_v201/controllers/tariff_cost_ctrlr.go @@ -0,0 +1,101 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameTariffEnabled variables.VariableName = "Enabled" + VariableNameTariffAvailable variables.VariableName = "Available" + VariableNameTariffFallbackMessage variables.VariableName = "TariffFallbackMessage" + VariableNameCostEnabled variables.VariableName = "Enabled" + VariableNameCostAvailable variables.VariableName = "Available" + VariableNameTotalCostFallbackMessage variables.VariableName = "TotalCostFallbackMessage" + VariableNameCurrency variables.VariableName = "Currency" +) + +func requiredVariablesTariffCostCtrlr() []variables.VariableName { + return []variables.VariableName{ + VariableNameTariffFallbackMessage, + VariableNameTotalCostFallbackMessage, + VariableNameCurrency, + } +} + +func optionalVariablesTariffCostCtrlr() []variables.VariableName { + return []variables.VariableName{ + VariableNameTariffEnabled, + VariableNameTariffAvailable, + VariableNameCostEnabled, + VariableNameCostAvailable, + } +} + +func supportedVariablesTariffCostCtrlr() []variables.VariableName { + return append(requiredVariablesTariffCostCtrlr(), optionalVariablesTariffCostCtrlr()...) +} + +type TariffCostCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (t *TariffCostCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (t *TariffCostCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (t *TariffCostCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (t *TariffCostCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (t *TariffCostCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (t *TariffCostCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (t *TariffCostCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (t *TariffCostCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (t *TariffCostCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (t *TariffCostCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewTariffCostCtrlr() *TariffCostCtrlr { + return &TariffCostCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredVariablesTariffCostCtrlr(), + supportedVariables: supportedVariablesTariffCostCtrlr(), + } +} diff --git a/ocpp_v201/controllers/tariff_cost_ctrlr_test.go b/ocpp_v201/controllers/tariff_cost_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/tariff_cost_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/tx_ctrlr.go b/ocpp_v201/controllers/tx_ctrlr.go new file mode 100644 index 0000000..bdbfc0b --- /dev/null +++ b/ocpp_v201/controllers/tx_ctrlr.go @@ -0,0 +1,101 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" +) + +const ( + VariableNameEVConnectionTimeOut variables.VariableName = "EVConnectionTimeOut" + VariableNameStopTxOnEVSideDisconnect variables.VariableName = "StopTxOnEVSideDisconnect" + VariableNameTxBeforeAcceptedEnabled variables.VariableName = "TxBeforeAcceptedEnabled" + VariableNameTxStartPoint variables.VariableName = "TxStartPoint" + VariableNameTxStopPoint variables.VariableName = "TxStopPoint" + VariableNameMaxEnergyOnInvalidId variables.VariableName = "MaxEnergyOnInvalidId" + VariableNameStopTxOnInvalidId variables.VariableName = "StopTxOnInvalidId" +) + +func requiredTxCtrlrVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameEVConnectionTimeOut, + VariableNameStopTxOnEVSideDisconnect, + VariableNameStopTxOnInvalidId, + VariableNameTxStartPoint, + VariableNameTxStopPoint, + } +} + +func optionalTxCtrlrVariables() []variables.VariableName { + return []variables.VariableName{ + VariableNameTxBeforeAcceptedEnabled, + VariableNameMaxEnergyOnInvalidId, + } +} + +func supportedTxCtrlrVariables() []variables.VariableName { + return append(requiredTxCtrlrVariables(), optionalTxCtrlrVariables()...) +} + +type TxCtrlr struct { + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName + supportedVariables []variables.VariableName +} + +func (t *TxCtrlr) GetName() component.ComponentName { + //TODO implement me + panic("implement me") +} + +func (t *TxCtrlr) GetInstanceId() string { + //TODO implement me + panic("implement me") +} + +func (t *TxCtrlr) RegisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (t *TxCtrlr) UnregisterSubComponent(component component.Component) { + //TODO implement me + panic("implement me") +} + +func (t *TxCtrlr) GetSubComponents() []component.Component { + //TODO implement me + panic("implement me") +} + +func (t *TxCtrlr) GetRequiredVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (t *TxCtrlr) GetSupportedVariables() []variables.Variable { + //TODO implement me + panic("implement me") +} + +func (t *TxCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { + //TODO implement me + panic("implement me") +} + +func (t *TxCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { + //TODO implement me + panic("implement me") +} + +func (t *TxCtrlr) Validate(key variables.VariableName) bool { + //TODO implement me + panic("implement me") +} + +func NewTxCtrlr() *TxCtrlr { + return &TxCtrlr{ + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: requiredTxCtrlrVariables(), + supportedVariables: supportedTxCtrlrVariables(), + } +} diff --git a/ocpp_v201/controllers/tx_ctrlr_test.go b/ocpp_v201/controllers/tx_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/tx_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/ctrlr_manager.go b/ocpp_v201/ctrlr_manager.go index e92c559..1d827ad 100644 --- a/ocpp_v201/ctrlr_manager.go +++ b/ocpp_v201/ctrlr_manager.go @@ -1,5 +1,77 @@ package ocpp_v201 +import ( + "errors" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/controllers" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" + "sync" +) + +// Global manager instance +var ( + managerInstance *Manager + managerOnce sync.Once +) + +func init() { + managerOnce.Do(func() { + managerInstance = NewManager() + }) +} + +func GetManager() *Manager { + return managerInstance +} + type Manager struct { - controllers map[string]Controller + controllers map[component.ComponentName]component.Component +} + +type ManagerOption func(*managerOptions) + +type managerOptions struct { + // Supported profiles + controllers +} + +func NewManager() *Manager { + return &Manager{ + controllers: map[component.ComponentName]component.Component{ + component.ComponentNameMonitoringCtrlr: controllers.NewMonitoringCtrlr(), + component.ComponentNameLocalAuthListCtrlr: controllers.NewLocalAuthListCtrlr(), + component.ComponentNameReservationCtrlr: controllers.NewReservationCtrlr(), + component.ComponentNameSmartChargingCtrlr: controllers.NewSmartChargingCtrlr(), + component.ComponentNameTxCtrlr: controllers.NewTxCtrlr(), + component.ComponentNameSecurityCtrlr: controllers.NewSecurityCtrlr(), + component.ComponentNameClockCtrlr: controllers.NewClockCtrlr(), + component.ComponentNameDeviceDataCtrlr: controllers.NewDeviceDataCtrlr(), + component.ComponentNameAuthCtrlr: controllers.NewAuthCtrlr(), + component.ComponentNameAuthCacheCtrlr: controllers.NewAuthCacheCtrlr(), + component.ComponentNameISO15118Ctrlr: controllers.NewISO15118Ctrlr(), + component.ComponentNameDisplayMessageCtrlr: controllers.NewDisplayCtrlr(), + component.ComponentNameOCPPCommCtrlr: controllers.NewOCPPCommCtrlr(), + component.ComponentNameAlignedDataCtrlr: controllers.NewAlignedDataCtrlr(), + component.ComponentNameSampledDataCtrlr: controllers.NewSampledDataCtrlr(), + component.ComponentNameTariffCostCtrlr: controllers.NewTariffCostCtrlr(), + component.ComponentNameCustomizationCtrlr: controllers.NewCustomizationCtrlr(), + }, + } +} + +func (m *Manager) UpdateVariable(name component.ComponentName, variableName variables.VariableName, attributeName string, attributeValue interface{}) error { + controller, ok := m.controllers[name] + if !ok { + return errors.New("controller not found") + } + + return controller.UpdateVariable(variableName, attributeName, attributeValue) +} + +func (m *Manager) GetVariable(name component.ComponentName, variableName variables.VariableName) (*variables.Variable, error) { + controller, ok := m.controllers[name] + if !ok { + return nil, errors.New("controller not found") + } + + return controller.GetVariable(variableName) } diff --git a/ocpp_v201/chademo_ctrlr.go b/ocpp_v201/ctrlr_manager_test.go similarity index 100% rename from ocpp_v201/chademo_ctrlr.go rename to ocpp_v201/ctrlr_manager_test.go diff --git a/ocpp_v201/customization_ctrlr.go b/ocpp_v201/customization_ctrlr.go deleted file mode 100644 index 2dea3ed..0000000 --- a/ocpp_v201/customization_ctrlr.go +++ /dev/null @@ -1,5 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameCustomImplementationEnabled VariableName = "CustomImplementationEnabled" -) diff --git a/ocpp_v201/device_data_ctrlr.go b/ocpp_v201/device_data_ctrlr.go deleted file mode 100644 index 9ab384b..0000000 --- a/ocpp_v201/device_data_ctrlr.go +++ /dev/null @@ -1,8 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameBytesPerMessage VariableName = "BytesPerMessage" - VariableNameConfigurationValueSize VariableName = "ConfigurationValueSize" - VariableNameReportingValueSize VariableName = "ReportingValueSize" - VariableNameItemsPerMessage VariableName = "ItemsPerMessage" -) diff --git a/ocpp_v201/display_message_ctrlr.go b/ocpp_v201/display_message_ctrlr.go deleted file mode 100644 index 539a583..0000000 --- a/ocpp_v201/display_message_ctrlr.go +++ /dev/null @@ -1,9 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameDisplayMessageEnabled VariableName = "Enabled" - VariableNameDisplayMessageAvailable VariableName = "Available" - VariableNameNumberOfDisplayMessages VariableName = "DisplayMessages" - VariableNameDisplayMessageSupportedFormats VariableName = "SupportedFormats" - VariableNameDisplayMessageSupportedPriorities VariableName = "SupportedPriorities" -) diff --git a/ocpp_v201/iso15118_ctrlr.go b/ocpp_v201/iso15118_ctrlr.go deleted file mode 100644 index 6d0a4d7..0000000 --- a/ocpp_v201/iso15118_ctrlr.go +++ /dev/null @@ -1,15 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameCentralContractValidationAllowed VariableName = "CentralContractValidationAllowed" - VariableNameContractValidationOffline VariableName = "ContractValidationOffline" - VariableNameProtocolSupportedByEV VariableName = "ProtocolSupportedByEV" - VariableNameProtocolAgreed VariableName = "ProtocolAgreed" - VariableNameISO15118PnCEnabled VariableName = "PnCEnabled" - VariableNameISO15118V2GCertificateInstallationEnabled VariableName = "V2GCertificateInstallationEnabled" - VariableNameISO15118ContractCertificateInstallationEnabled VariableName = "ContractCertificateInstallationEnabled" - VariableNameISO15118RequestMeteringReceipt VariableName = "RequestMeteringReceipt" - VariableNameISO15118SeccId VariableName = "SeccId" - VariableNameISO15118CountryName VariableName = "CountryName" - VariableNameISO15118EvseId VariableName = "ISO15118EvseId" -) diff --git a/ocpp_v201/local_auth_list_ctrlr.go b/ocpp_v201/local_auth_list_ctrlr.go deleted file mode 100644 index 3eb6f6f..0000000 --- a/ocpp_v201/local_auth_list_ctrlr.go +++ /dev/null @@ -1,11 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameLocalAuthListEnabled VariableName = "Enabled" - VariableNameLocalAuthListEntries VariableName = "Entries" - VariableNameLocalAuthListItemsPerMessage VariableName = "ItemsPerMessage" - VariableNameLocalAuthListBytesPerMessage VariableName = "BytesPerMessage" - VariableNameLocalAuthListStorage VariableName = "Storage" - VariableNameLocalAuthListDisablePostAuthorize VariableName = "DisablePostAuthorize" - VariableNameLocalAuthListSupportsExpiryDateTime VariableName = "SupportsExpiryDateTime" -) diff --git a/ocpp_v201/monitoring_ctrlr.go b/ocpp_v201/monitoring_ctrlr.go deleted file mode 100644 index 8e46d6c..0000000 --- a/ocpp_v201/monitoring_ctrlr.go +++ /dev/null @@ -1,13 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameMonitoringEnabled VariableName = "Enabled" - VariableNameMonitoringAvailable VariableName = "Available" - VariableNameItemsPerMessageClearVariableMonitoring VariableName = "ItemsPerMessage" - VariableNameItemsPerMessageSetVariableMonitoring VariableName = "ItemsPerMessage" - VariableNameClearVariableMonitoring VariableName = "BytesPerMessage" - VariableNameBytesPerMessageSetVariableMonitoring VariableName = "BytesPerMessage" - VariableNameOfflineMonitoringEventQueuingSeverity VariableName = "OfflineQueuingSeverity" - VariableNameActiveMonitoringBase VariableName = "ActiveMonitoringBase" - VariableNameActiveMonitoringLevel VariableName = "ActiveMonitoringLevel" -) diff --git a/ocpp_v201/ocpp_comm_ctrlr.go b/ocpp_v201/ocpp_comm_ctrlr.go deleted file mode 100644 index dc97379..0000000 --- a/ocpp_v201/ocpp_comm_ctrlr.go +++ /dev/null @@ -1,19 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameNetworkProfileConnectionAttempts VariableName = "NetworkProfileConnectionAttempts" - VariableNameNetworkConfigurationPriority VariableName = "NetworkConfigurationPriority" - VariableNameHeartbeatInterval VariableName = "HeartbeatInterval" - VariableNameFileTransferProtocols VariableName = "FileTransferProtocols" - VariableNameMessageTimeout VariableName = "MessageTimeout" - VariableNameActiveNetworkProfile VariableName = "ActiveNetworkProfile" - VariableNameOfflineThreshold VariableName = "OfflineThreshold" - VariableNameQueueAllMessages VariableName = "QueueAllMessages" - VariableNameMessageAttempts VariableName = "MessageAttempts" - VariableNameMessageAttemptInterval VariableName = "MessageAttemptInterval" - VariableNameUnlockOnEVSideDisconnect VariableName = "UnlockOnEVSideDisconnect" - VariableNameWebSocketPingInterval VariableName = "WebSocketPingInterval" - VariableNameResetRetries VariableName = "ResetRetries" - VariableNameFieldLength VariableName = "FieldLength" - VariableNamePublicKeyWithSignedMeterValue VariableName = "PublicKeyWithSignedMeterValue" -) diff --git a/ocpp_v201/reservation_ctrlr.go b/ocpp_v201/reservation_ctrlr.go deleted file mode 100644 index ec05ff3..0000000 --- a/ocpp_v201/reservation_ctrlr.go +++ /dev/null @@ -1,7 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameReservationEnabled VariableName = "Enabled" - VariableNameReservationAvailable VariableName = "Available" - VariableNameReservationNonEvseSpecific VariableName = "NonEvseSpecific" -) diff --git a/ocpp_v201/sampled_data_ctrlr.go b/ocpp_v201/sampled_data_ctrlr.go deleted file mode 100644 index 1e6574e..0000000 --- a/ocpp_v201/sampled_data_ctrlr.go +++ /dev/null @@ -1,13 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameSampledDataEnabled VariableName = "Enabled" - VariableNameSampledDataAvailable VariableName = "Available" - VariableNameSampledDataSignReadings VariableName = "SignReadings" - VariableNameSampledDataTxEndedMeasurands VariableName = "TxEndedMeasurands" - VariableNameSampledDataTxEndedInterval VariableName = "TxEndedInterval" - VariableNameSampledDataTxStartedMeasurands VariableName = "TxStartedMeasurands" - VariableNameSampledDataTxUpdatedMeasurands VariableName = "TxUpdatedMeasurands" - VariableNameSampledDataTxUpdatedInterval VariableName = "TxUpdatedInterval" - VariableNameSampledDataRegisterValuesWithoutPhases VariableName = "RegisterValuesWithoutPhases" -) diff --git a/ocpp_v201/security_ctrlr.go b/ocpp_v201/security_ctrlr.go deleted file mode 100644 index 26f4747..0000000 --- a/ocpp_v201/security_ctrlr.go +++ /dev/null @@ -1,12 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameBasicAuthPassword VariableName = "BasicAuthPassword" - VariableNameIdentity VariableName = "Identity" - VariableNameOrganizationName VariableName = "OrganizationName" - VariableNameCertificateEntries VariableName = "CertificateEntries" - VariableNameAdditionalRootCertificateCheck VariableName = "AdditionalRootCertificateCheck" - VariableNameSecurityProfile VariableName = "SecurityProfile" - VariableNameMaxCertificateChainSize VariableName = "MaxCertificateChainSize" - VariableNameCertSigningWaitMinimum VariableName = "CertSigningWaitMinimum" -) diff --git a/ocpp_v201/smart_charging_ctrlr.go b/ocpp_v201/smart_charging_ctrlr.go deleted file mode 100644 index d216620..0000000 --- a/ocpp_v201/smart_charging_ctrlr.go +++ /dev/null @@ -1,15 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameSmartChargingEnabled VariableName = "Enabled" - VariableNameSmartChargingAvailable VariableName = "Available" - VariableNameACPhaseSwitchingSupported VariableName = "ACPhaseSwitchingSupported" - VariableNameChargingProfileStackLevel VariableName = "ProfileStackLevel" - VariableNameChargingScheduleChargingRateUnit VariableName = "RateUnit" - VariableNamePeriodsPerSchedule VariableName = "PeriodsPerSchedule" - VariableNameExternalControlSignalsEnabled VariableName = "ExternalControlSignalsEnabled" - VariableNameNotifyChargingLimitWithSchedules VariableName = "NotifyChargingLimitWithSchedules" - VariableNamePhases3to1 VariableName = "Phases3to1" - VariableChargingProfileEntries VariableName = "Entries" - VariableLimitChangeSignificance VariableName = "LimitChangeSignificance" -) diff --git a/ocpp_v201/tariff_cost_ctrlr.go b/ocpp_v201/tariff_cost_ctrlr.go deleted file mode 100644 index 6f5f320..0000000 --- a/ocpp_v201/tariff_cost_ctrlr.go +++ /dev/null @@ -1,11 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameTariffEnabled VariableName = "Enabled" - VariableNameTariffAvailable VariableName = "Available" - VariableNameTariffFallbackMessage VariableName = "TariffFallbackMessage" - VariableNameCostEnabled VariableName = "Enabled" - VariableNameCostAvailable VariableName = "Available" - VariableNameTotalCostFallbackMessage VariableName = "TotalCostFallbackMessage" - VariableNameCurrency VariableName = "Currency" -) diff --git a/ocpp_v201/tx_ctrlr.go b/ocpp_v201/tx_ctrlr.go deleted file mode 100644 index e8ed296..0000000 --- a/ocpp_v201/tx_ctrlr.go +++ /dev/null @@ -1,11 +0,0 @@ -package ocpp_v201 - -const ( - VariableNameEVConnectionTimeOut VariableName = "EVConnectionTimeOut" - VariableNameStopTxOnEVSideDisconnect VariableName = "StopTxOnEVSideDisconnect" - VariableNameTxBeforeAcceptedEnabled VariableName = "TxBeforeAcceptedEnabled" - VariableNameTxStartPoint VariableName = "TxStartPoint" - VariableNameTxStopPoint VariableName = "TxStopPoint" - VariableNameMaxEnergyOnInvalidId VariableName = "MaxEnergyOnInvalidId" - VariableNameStopTxOnInvalidId VariableName = "StopTxOnInvalidId" -) diff --git a/ocpp_v201/variables.go b/ocpp_v201/variables.go deleted file mode 100644 index 73c8d38..0000000 --- a/ocpp_v201/variables.go +++ /dev/null @@ -1,29 +0,0 @@ -package ocpp_v201 - -type Mutability string - -const ( - MutabilityReadOnly Mutability = "ReadOnly" - MutabilityReadWrite Mutability = "ReadWrite" - MutabilityWriteOnly Mutability = "WriteOnly" -) - -type VariableName string - -type Variable struct { - Name VariableName - Attributes VariableAttributes - Characteristics VariableCharacteristic - Value interface{} -} - -type VariableAttributes struct { - Mutability Mutability -} - -type VariableCharacteristic struct { - DataType string - MaxLimit *int - Unit *string - ValuesList []string -} diff --git a/ocpp_v201/variables/variables.go b/ocpp_v201/variables/variables.go new file mode 100644 index 0000000..c00bb35 --- /dev/null +++ b/ocpp_v201/variables/variables.go @@ -0,0 +1,152 @@ +package variables + +import "errors" + +type Mutability string + +const ( + MutabilityReadOnly Mutability = "ReadOnly" + MutabilityReadWrite Mutability = "ReadWrite" + MutabilityWriteOnly Mutability = "WriteOnly" +) + +type VariableType string + +const ( + VariableTypeString VariableType = "string" + VariableTypeInteger VariableType = "integer" + VariableTypeNumber VariableType = "number" + VariableTypeBool VariableType = "boolean" + VariableTypeOptionList VariableType = "OptionList" + VariableTypeSequenceList VariableType = "SequenceList" + VariableTypeMemberList VariableType = "MemberList" +) + +type VariableName string + +type Variable struct { + Name VariableName + // attributes are conditionally mutable. + attributes map[string]VariableAttributes + // Characteristics are read-only + Characteristics VariableCharacteristic +} + +// Validate checks if all variable attributes are valid. +func (v *Variable) Validate() bool { + for _, attributes := range v.attributes { + if attributes.Validate() == false { + return false + } + } + + // todo validate according to characteristics + + return true +} + +// UpdateVariableAttribute updates the variable attribute if it exists and if the value is valid +func (v *Variable) UpdateVariableAttribute(attribute string, value interface{}) error { + // Check if exists + existingEntry, ok := v.attributes[attribute] + if !ok { + return errors.New("Variable attribute not found: " + attribute) + } + + // Check if it we can even update it + if existingEntry.Mutability == MutabilityReadOnly { + return errors.New("Variable attribute is read-only: " + attribute) + } + + // Check if the operation is allowed + existingEntry.Value = value + if !existingEntry.Validate() { + return errors.New("invalid value") + } + + // Update the value + v.attributes[attribute] = existingEntry + return nil +} + +// GetVariableAttribute gets the variable attribute if it exists. +func (v *Variable) GetVariableAttribute(attribute string) (*VariableAttributes, error) { + // Check if exists + existingEntry, ok := v.attributes[attribute] + if !ok { + return nil, errors.New("Variable attribute not found: " + attribute) + } + + // Check if it is readable + if existingEntry.Mutability == MutabilityWriteOnly { + return nil, errors.New("Variable attribute is write-only: " + attribute) + } + + return &existingEntry, nil +} + +type VariableAttributes struct { + Type VariableType + Mutability Mutability + Value interface{} +} + +// Validate validates +func (va *VariableAttributes) Validate() bool { + if va == nil { + return false + } + + switch va.Mutability { + case MutabilityReadOnly, MutabilityReadWrite, MutabilityWriteOnly: + default: + return false + } + + switch va.Type { + case VariableTypeNumber: + // Must be castable to float + _, castable := va.Value.(float64) + return castable + case VariableTypeBool: + // Must be castable to bool + _, castable := va.Value.(bool) + return castable + case VariableTypeInteger: + // Must be castable to bool + _, castable := va.Value.(int64) + return castable + case VariableTypeOptionList, VariableTypeMemberList, VariableTypeSequenceList: + _, castable := va.Value.([]interface{}) + return castable + case VariableTypeString: + _, castable := va.Value.(string) + return castable + } + + return false +} + +func (va *VariableAttributes) copy() VariableAttributes { + return *va +} + +// Update updates the variable attribute value +func (va *VariableAttributes) Update(value interface{}) error { + attrsCopy := va.copy() + attrsCopy.Value = value + if !attrsCopy.Validate() { + return errors.New("invalid value for variable attribute") + } + + va.Value = value + return nil +} + +type VariableCharacteristic struct { + DataType string + MaxLimit *int + MinLimit *int + Unit *string + ValuesList []string +} diff --git a/ocpp_v201/variables/variables_test.go b/ocpp_v201/variables/variables_test.go new file mode 100644 index 0000000..b309b9b --- /dev/null +++ b/ocpp_v201/variables/variables_test.go @@ -0,0 +1,34 @@ +package variables + +import ( + "github.com/stretchr/testify/suite" + "testing" +) + +type variablesTestSuite struct { + suite.Suite +} + +func (suite *variablesTestSuite) TestVariable_Validate() { + +} + +func (suite *variablesTestSuite) TestVariable_GetVariableAttribute() { + +} + +func (suite *variablesTestSuite) TestVariable_UpdateVariableAttribute() { + +} + +func (suite *variablesTestSuite) TestVariableAttributes_Validate() { + +} + +func (suite *variablesTestSuite) TestVariableAttributes_Update() { + +} + +func TestVariables(t *testing.T) { + suite.Run(t, new(variablesTestSuite)) +} From 49263f7504b3778c2ab57c2cca7dccdba7953bf1 Mon Sep 17 00:00:00 2001 From: xBlaz3kx Date: Mon, 7 Jul 2025 22:30:47 +0200 Subject: [PATCH 3/9] docs: update readme with progress --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0fa73a9..823058d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ getting and setting values, validating values, and enforcing mandatory keys. - Mandatory key enforcement - Custom value validation - Provides sane default values +- Supports OCPP 1.6 and 2.0.1 (separate packages for each version) ## Roadmap @@ -16,7 +17,7 @@ getting and setting values, validating values, and enforcing mandatory keys. - [x] Custom value validation - [x] Mandatory key enforcement - [x] Support for OCPP 1.6 -- [ ] Support for OCPP 2.0.1 +- [x] Support for OCPP 2.0.1 ## Installing @@ -26,6 +27,8 @@ getting and setting values, validating values, and enforcing mandatory keys. ## âš¡ Usage +### OCPP 1.6 + Check out the full [OCPP 1.6 example](examples/v16/example.go). ```go @@ -92,6 +95,10 @@ func main() { ``` +### OCPP 2.0.1 + +TBD + ## Notes 1. This library is still in development, and the API might change in the future. From 81227a4996c919bf1a0fa8558624f0b1a57a8f68 Mon Sep 17 00:00:00 2001 From: xBlaz3kx Date: Wed, 9 Jul 2025 23:45:26 +0200 Subject: [PATCH 4/9] options test --- .../component/component_variable_opts_test.go | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 ocpp_v201/component/component_variable_opts_test.go diff --git a/ocpp_v201/component/component_variable_opts_test.go b/ocpp_v201/component/component_variable_opts_test.go new file mode 100644 index 0000000..38a2b5c --- /dev/null +++ b/ocpp_v201/component/component_variable_opts_test.go @@ -0,0 +1,42 @@ +package component + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestOptions(t *testing.T) { + tests := []struct { + name string + opts []GetSetVariableOption + expected componentVariableOptions + }{ + { + name: "default options", + expected: componentVariableOptions{ + attributeType: "", + }, + opts: []GetSetVariableOption{}, + }, + { + name: "with attribute type", + expected: componentVariableOptions{ + attributeType: "abc", + }, + opts: []GetSetVariableOption{ + WithAttributeType("abc"), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + opts := componentVariableOptions{} + for _, opt := range tt.opts { + opt(&opts) + } + + assert.Equal(t, tt.expected, opts) + }) + } +} From b637f2d111f22c6548713f37957efbc89e8ca131 Mon Sep 17 00:00:00 2001 From: xBlaz3kx Date: Mon, 25 Aug 2025 09:34:32 +0200 Subject: [PATCH 5/9] feat: run tests with data race detection --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b60c5a4..49615fa 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -32,7 +32,7 @@ jobs: - name: Install dependencies and run tests run: | go mod download - go test -v ./... -coverpkg=./... -short -coverprofile=unit_coverage.out + go test -v ./... -coverpkg=./... -short -coverprofile=unit_coverage.out -race - name: Archive code coverage results uses: actions/upload-artifact@v4 From da1b5486dac3d71d0ec99e4b84bbab3d8cbb33fb Mon Sep 17 00:00:00 2001 From: xBlaz3kx Date: Mon, 25 Aug 2025 09:53:07 +0200 Subject: [PATCH 6/9] feat: add variable tests --- ocpp_v201/variables/variables.go | 18 ++ ocpp_v201/variables/variables_test.go | 431 +++++++++++++++++++++++++- 2 files changed, 448 insertions(+), 1 deletion(-) diff --git a/ocpp_v201/variables/variables.go b/ocpp_v201/variables/variables.go index c00bb35..97d320b 100644 --- a/ocpp_v201/variables/variables.go +++ b/ocpp_v201/variables/variables.go @@ -85,6 +85,15 @@ func (v *Variable) GetVariableAttribute(attribute string) (*VariableAttributes, return &existingEntry, nil } +// GetAllAttributes returns a copy of all attributes for this variable. +func (v *Variable) GetAllAttributes() map[string]VariableAttributes { + result := make(map[string]VariableAttributes, len(v.attributes)) + for k, vAttr := range v.attributes { + result[k] = vAttr + } + return result +} + type VariableAttributes struct { Type VariableType Mutability Mutability @@ -133,6 +142,7 @@ func (va *VariableAttributes) copy() VariableAttributes { // Update updates the variable attribute value func (va *VariableAttributes) Update(value interface{}) error { + // Make a copy of the variable attributes to validate the new value attrsCopy := va.copy() attrsCopy.Value = value if !attrsCopy.Validate() { @@ -150,3 +160,11 @@ type VariableCharacteristic struct { Unit *string ValuesList []string } + +// NewVariable creates a new variable with the given name, type, and default value +func NewVariable(name VariableName, varType VariableType) *Variable { + return &Variable{ + Name: name, + attributes: map[string]VariableAttributes{}, + } +} diff --git a/ocpp_v201/variables/variables_test.go b/ocpp_v201/variables/variables_test.go index b309b9b..810b5cc 100644 --- a/ocpp_v201/variables/variables_test.go +++ b/ocpp_v201/variables/variables_test.go @@ -1,8 +1,10 @@ package variables import ( - "github.com/stretchr/testify/suite" "testing" + + "github.com/samber/lo" + "github.com/stretchr/testify/suite" ) type variablesTestSuite struct { @@ -10,23 +12,450 @@ type variablesTestSuite struct { } func (suite *variablesTestSuite) TestVariable_Validate() { + tests := []struct { + name string + variable *Variable + expected bool + }{ + { + name: "valid variable", + variable: &Variable{ + Name: "TestVariable", + attributes: map[string]VariableAttributes{ + "value": { + Type: VariableTypeString, + Mutability: MutabilityReadWrite, + Value: "test", + }, + }, + Characteristics: VariableCharacteristic{ + DataType: "string", + }, + }, + expected: true, + }, + { + name: "variable with invalid attribute", + variable: &Variable{ + Name: "TestVariable", + attributes: map[string]VariableAttributes{ + "value": { + Type: VariableTypeString, + Mutability: "InvalidMutability", // Invalid mutability + Value: "test", + }, + }, + Characteristics: VariableCharacteristic{ + DataType: "string", + }, + }, + expected: false, + }, + { + name: "variable with empty attributes", + variable: &Variable{ + Name: "TestVariable", + attributes: map[string]VariableAttributes{}, + Characteristics: VariableCharacteristic{ + DataType: "string", + }, + }, + expected: true, + }, + } + for _, tt := range tests { + suite.Run(tt.name, func() { + suite.Assert().Equal(tt.expected, tt.variable.Validate()) + }) + } } func (suite *variablesTestSuite) TestVariable_GetVariableAttribute() { + var1 := &Variable{ + Name: "TestVariable", + attributes: map[string]VariableAttributes{ + "readWrite": { + Type: VariableTypeString, + Mutability: MutabilityReadWrite, + Value: "test", + }, + "readOnly": { + Type: VariableTypeInteger, + Mutability: MutabilityReadOnly, + Value: int64(42), + }, + "writeOnly": { + Type: VariableTypeBool, + Mutability: MutabilityWriteOnly, + Value: true, + }, + }, + } + tests := []struct { + name string + attribute string + expectError bool + errorMsg string + expectedValue interface{} + }{ + { + name: "get read-write attribute", + attribute: "readWrite", + expectError: false, + expectedValue: "test", + }, + { + name: "get read-only attribute", + attribute: "readOnly", + expectError: false, + expectedValue: int64(42), + }, + { + name: "get write-only attribute", + attribute: "writeOnly", + expectError: true, + errorMsg: "Variable attribute is write-only: writeOnly", + }, + { + name: "get non-existent attribute", + attribute: "nonExistent", + expectError: true, + errorMsg: "Variable attribute not found: nonExistent", + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + attr, err := var1.GetVariableAttribute(tt.attribute) + if tt.expectError { + suite.Assert().Error(err) + suite.Assert().Equal(tt.errorMsg, err.Error()) + suite.Assert().Nil(attr) + } else { + suite.Require().NoError(err) + suite.Assert().Equal(tt.expectedValue, attr.Value) + } + }) + } } func (suite *variablesTestSuite) TestVariable_UpdateVariableAttribute() { + var1 := &Variable{ + Name: "TestVariable", + attributes: map[string]VariableAttributes{ + "readWrite": { + Type: VariableTypeString, + Mutability: MutabilityReadWrite, + Value: "old", + }, + "readOnly": { + Type: VariableTypeInteger, + Mutability: MutabilityReadOnly, + Value: int64(42), + }, + "writeOnly": { + Type: VariableTypeBool, + Mutability: MutabilityWriteOnly, + Value: false, + }, + }, + } + + tests := []struct { + name string + attribute string + value interface{} + expectError bool + errorMsg string + }{ + { + name: "update read-write attribute with valid value", + attribute: "readWrite", + value: "new", + expectError: false, + }, + { + name: "update read-only attribute", + attribute: "readOnly", + value: int64(100), + expectError: true, + errorMsg: "Variable attribute is read-only: readOnly", + }, + { + name: "update write-only attribute with valid value", + attribute: "writeOnly", + value: true, + expectError: false, + }, + { + name: "update non-existent attribute", + attribute: "nonExistent", + value: "value", + expectError: true, + errorMsg: "Variable attribute not found: nonExistent", + }, + { + name: "update with invalid value type", + attribute: "readWrite", + value: 123, // int instead of string + expectError: true, + errorMsg: "invalid value", + }, + } + for _, tt := range tests { + suite.Run(tt.name, func() { + err := var1.UpdateVariableAttribute(tt.attribute, tt.value) + if tt.expectError { + suite.Assert().Error(err) + suite.Assert().Equal(tt.errorMsg, err.Error()) + } else { + suite.Require().NoError(err) + // Verify the value was updated for readable attributes + if tt.attribute == "readWrite" { + attr, err := var1.GetVariableAttribute(tt.attribute) + suite.Require().NoError(err) + suite.Assert().Equal(tt.value, attr.Value) + } + } + }) + } } func (suite *variablesTestSuite) TestVariableAttributes_Validate() { + tests := []struct { + name string + attr *VariableAttributes + expected bool + }{ + { + name: "valid string attribute", + attr: &VariableAttributes{ + Type: VariableTypeString, + Mutability: MutabilityReadWrite, + Value: "test", + }, + expected: true, + }, + { + name: "valid integer attribute", + attr: &VariableAttributes{ + Type: VariableTypeInteger, + Mutability: MutabilityReadOnly, + Value: int64(42), + }, + expected: true, + }, + { + name: "valid number attribute", + attr: &VariableAttributes{ + Type: VariableTypeNumber, + Mutability: MutabilityReadWrite, + Value: float64(3.14), + }, + expected: true, + }, + { + name: "valid boolean attribute", + attr: &VariableAttributes{ + Type: VariableTypeBool, + Mutability: MutabilityWriteOnly, + Value: true, + }, + expected: true, + }, + { + name: "valid list attributes", + attr: &VariableAttributes{ + Type: VariableTypeOptionList, + Mutability: MutabilityReadWrite, + Value: []interface{}{"item1", "item2"}, + }, + expected: true, + }, + { + name: "nil attribute", + attr: nil, + expected: false, + }, + { + name: "invalid mutability", + attr: &VariableAttributes{ + Type: VariableTypeString, + Mutability: "Invalid", + Value: "test", + }, + expected: false, + }, + { + name: "invalid type for string", + attr: &VariableAttributes{ + Type: VariableTypeString, + Mutability: MutabilityReadWrite, + Value: 123, // int instead of string + }, + expected: false, + }, + { + name: "invalid type for integer", + attr: &VariableAttributes{ + Type: VariableTypeInteger, + Mutability: MutabilityReadWrite, + Value: "not an int", // string instead of int64 + }, + expected: false, + }, + { + name: "invalid type for number", + attr: &VariableAttributes{ + Type: VariableTypeNumber, + Mutability: MutabilityReadWrite, + Value: "not a number", // string instead of float64 + }, + expected: false, + }, + { + name: "invalid type for boolean", + attr: &VariableAttributes{ + Type: VariableTypeBool, + Mutability: MutabilityReadWrite, + Value: "not a bool", // string instead of bool + }, + expected: false, + }, + { + name: "invalid type for list", + attr: &VariableAttributes{ + Type: VariableTypeOptionList, + Mutability: MutabilityReadWrite, + Value: "not a list", // string instead of []interface{} + }, + expected: false, + }, + { + name: "unknown variable type", + attr: &VariableAttributes{ + Type: "UnknownType", + Mutability: MutabilityReadWrite, + Value: "test", + }, + expected: false, + }, + } + for _, tt := range tests { + suite.Run(tt.name, func() { + suite.Assert().Equal(tt.expected, tt.attr.Validate()) + }) + } } func (suite *variablesTestSuite) TestVariableAttributes_Update() { + tests := []struct { + name string + attr *VariableAttributes + newValue interface{} + expectError bool + errorMsg string + expectedValue interface{} + }{ + { + name: "valid string update", + attr: &VariableAttributes{ + Type: VariableTypeString, + Mutability: MutabilityReadWrite, + Value: "old", + }, + newValue: "new", + expectError: false, + expectedValue: "new", + }, + { + name: "invalid string update", + attr: &VariableAttributes{ + Type: VariableTypeString, + Mutability: MutabilityReadWrite, + Value: "old", + }, + newValue: 123, // int instead of string + expectError: true, + errorMsg: "invalid value for variable attribute", + expectedValue: "old", // Value should remain unchanged + }, + { + name: "valid integer update", + attr: &VariableAttributes{ + Type: VariableTypeInteger, + Mutability: MutabilityReadWrite, + Value: int64(10), + }, + newValue: int64(20), + expectError: false, + expectedValue: int64(20), + }, + { + name: "invalid integer update", + attr: &VariableAttributes{ + Type: VariableTypeInteger, + Mutability: MutabilityReadWrite, + Value: int64(10), + }, + newValue: "not an int", + expectError: true, + errorMsg: "invalid value for variable attribute", + expectedValue: int64(10), // Value should remain unchanged + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + err := tt.attr.Update(tt.newValue) + if tt.expectError { + suite.Assert().Error(err) + suite.Assert().Equal(tt.errorMsg, err.Error()) + } else { + suite.Require().NoError(err) + } + suite.Assert().Equal(tt.expectedValue, tt.attr.Value) + }) + } +} + +func (suite *variablesTestSuite) TestVariableAttributes_Copy() { + original := &VariableAttributes{ + Type: VariableTypeString, + Mutability: MutabilityReadWrite, + Value: "original", + } + + copy := original.copy() + suite.Assert().Equal(original.Type, copy.Type) + suite.Assert().Equal(original.Mutability, copy.Mutability) + suite.Assert().Equal(original.Value, copy.Value) + + // Verify it's a deep copy + copy.Value = "modified" + suite.Assert().Equal("original", original.Value) + suite.Assert().Equal("modified", copy.Value) +} + +func (suite *variablesTestSuite) TestVariableCharacteristic() { + // Test VariableCharacteristic struct + char := VariableCharacteristic{ + DataType: "string", + MaxLimit: lo.ToPtr(100), + MinLimit: lo.ToPtr(0), + Unit: lo.ToPtr("watts"), + ValuesList: []string{"option1", "option2"}, + } + suite.Assert().Equal("string", char.DataType) + suite.Assert().Equal(100, *char.MaxLimit) + suite.Assert().Equal(0, *char.MinLimit) + suite.Assert().Equal("watts", *char.Unit) + suite.Assert().Equal([]string{"option1", "option2"}, char.ValuesList) } func TestVariables(t *testing.T) { From be1bd81b063e699713f297a77ba946b4b08663b6 Mon Sep 17 00:00:00 2001 From: xBlaz3kx Date: Mon, 25 Aug 2025 09:53:48 +0200 Subject: [PATCH 7/9] feat: added makefile, formatting and lint rules --- .golangci.yaml | 39 +++++++++++++++++++++++++++++++++++++++ Makefile | 10 ++++++++++ 2 files changed, 49 insertions(+) create mode 100644 .golangci.yaml create mode 100644 Makefile diff --git a/.golangci.yaml b/.golangci.yaml new file mode 100644 index 0000000..5ba2d59 --- /dev/null +++ b/.golangci.yaml @@ -0,0 +1,39 @@ +version: "2" +linters: + default: all + enable: + - bodyclose + - copyloopvar + - decorder + - errcheck + - errname + - forbidigo + - goconst + - gocritic + - gosec + - govet + - ineffassign + - intrange + - misspell + - nestif + - predeclared + - staticcheck + - testifylint + - unparam + - unused + - wastedassign + - whitespace + - wrapcheck + exclusions: + generated: lax + paths: + - ./examples + +formatters: + settings: + goimports: + local-prefixes: + - github.com/ChargePi/ocpp-manager + enable: + - gofmt + - goimports diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b7f7fb5 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +.PHONY:gen format lint + +gen: + mockery + +lint: + golangci-lint run + +format: + golangci-lint fmt \ No newline at end of file From 2d0ea3089fefe7e374ced10986559ff4010f461b Mon Sep 17 00:00:00 2001 From: xBlaz3kx Date: Mon, 25 Aug 2025 09:58:07 +0200 Subject: [PATCH 8/9] feat: variable validator --- ocpp_v201/controllers/variable_validator.go | 23 +++++++++++++++++++ .../controllers/variable_validator_test.go | 1 + 2 files changed, 24 insertions(+) create mode 100644 ocpp_v201/controllers/variable_validator.go create mode 100644 ocpp_v201/controllers/variable_validator_test.go diff --git a/ocpp_v201/controllers/variable_validator.go b/ocpp_v201/controllers/variable_validator.go new file mode 100644 index 0000000..2cd3e3f --- /dev/null +++ b/ocpp_v201/controllers/variable_validator.go @@ -0,0 +1,23 @@ +package controllers + +import ( + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" + "slices" +) + +type variableValidator struct { + component component.Component +} + +func newVariableValidator(component component.Component) *variableValidator { + return &variableValidator{ + component: component, + } +} + +// IsVariableSupported checks if the given variable name is supported by the component. +func (vv *variableValidator) IsVariableSupported(variableName variables.VariableName) bool { + supportedVariables := vv.component.GetSupportedVariables() + return slices.Contains(supportedVariables, variableName) +} diff --git a/ocpp_v201/controllers/variable_validator_test.go b/ocpp_v201/controllers/variable_validator_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/variable_validator_test.go @@ -0,0 +1 @@ +package controllers From 94110d98987e1e160fcc023c9bb217c97b9df2c3 Mon Sep 17 00:00:00 2001 From: xBlaz3kx Date: Mon, 25 Aug 2025 10:15:22 +0200 Subject: [PATCH 9/9] feat: wip on controllers --- ocpp_v201/charging_infrastructure.go | 17 - ocpp_v201/charging_station.go | 9 + ocpp_v201/component/component.go | 23 +- .../component/component_variable_opts.go | 1 + ocpp_v201/connector.go | 8 + ocpp_v201/controllers/aligned_data_ctrlr.go | 94 ++++-- ocpp_v201/controllers/auth_cache_ctrlr.go | 93 ++++-- ocpp_v201/controllers/auth_ctrlr.go | 89 ++++-- ocpp_v201/controllers/clock_ctrlr.go | 94 ++++-- ocpp_v201/controllers/customization_ctrlr.go | 123 ++++++-- .../controllers/customization_ctrlr_test.go | 1 + ocpp_v201/controllers/device_data_ctrlr.go | 136 ++++++-- .../controllers/device_data_ctrlr_test.go | 265 ++++++++++++++++ .../controllers/display_message_ctrlr.go | 95 ++++-- .../controllers/display_message_ctrlr_test.go | 275 ++++++++++++++++ ocpp_v201/controllers/iso15118_ctrlr.go | 94 ++++-- ocpp_v201/controllers/iso15118_ctrlr_test.go | 1 + .../controllers/local_auth_list_ctrlr.go | 94 ++++-- ocpp_v201/controllers/monitoring_ctrlr.go | 94 ++++-- ocpp_v201/controllers/ocpp_comm_ctrlr.go | 86 +++-- ocpp_v201/controllers/ocpp_comm_ctrlr_test.go | 298 ++++++++++++++++++ ocpp_v201/controllers/reservation_ctrlr.go | 93 ++++-- ocpp_v201/controllers/sampled_data_ctrlr.go | 95 ++++-- ocpp_v201/controllers/security_ctrlr.go | 99 ++++-- ocpp_v201/controllers/smart_charging_ctrlr.go | 95 ++++-- ocpp_v201/controllers/tariff_cost_ctrlr.go | 97 ++++-- ocpp_v201/controllers/tx_ctrlr.go | 93 ++++-- ocpp_v201/controllers/tx_ctrlr_test.go | 268 ++++++++++++++++ ocpp_v201/ctrlr_manager.go | 69 ++-- ocpp_v201/evse.go | 8 + 30 files changed, 2434 insertions(+), 473 deletions(-) delete mode 100644 ocpp_v201/charging_infrastructure.go create mode 100644 ocpp_v201/charging_station.go create mode 100644 ocpp_v201/connector.go create mode 100644 ocpp_v201/controllers/customization_ctrlr_test.go create mode 100644 ocpp_v201/controllers/iso15118_ctrlr_test.go create mode 100644 ocpp_v201/evse.go diff --git a/ocpp_v201/charging_infrastructure.go b/ocpp_v201/charging_infrastructure.go deleted file mode 100644 index 5d29fc0..0000000 --- a/ocpp_v201/charging_infrastructure.go +++ /dev/null @@ -1,17 +0,0 @@ -package ocpp_v201 - -import "github.com/ChargePi/ocpp-manager/ocpp_v201/component" - -type ChargingStation struct { - components map[component.ComponentName]component.Component // Charging station (top level) specific components -} - -type EVSE struct { - ID int - components map[component.ComponentName]component.Component -} - -type Connector struct { - ID int - components map[component.ComponentName]component.Component -} diff --git a/ocpp_v201/charging_station.go b/ocpp_v201/charging_station.go new file mode 100644 index 0000000..549c2e1 --- /dev/null +++ b/ocpp_v201/charging_station.go @@ -0,0 +1,9 @@ +package ocpp_v201 + +import "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + +type ChargingStation struct { + ID string + components map[component.ComponentName]component.Component // Charging station (top level) specific components + controllerManager Manager +} diff --git a/ocpp_v201/component/component.go b/ocpp_v201/component/component.go index e197945..0ef6199 100644 --- a/ocpp_v201/component/component.go +++ b/ocpp_v201/component/component.go @@ -3,24 +3,35 @@ package component import "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" type Component interface { - // Essentially a component type. + + // GetName Essentially a component type. GetName() ComponentName - // Instance ID where a component can be addressed + + // GetInstanceId returns the unique instance ID of this component. GetInstanceId() string + // RegisterSubComponent registers a sub-component to this component. RegisterSubComponent(component Component) + // UnregisterSubComponent unregisters a sub-component from this component. UnregisterSubComponent(component Component) + // GetSubComponents returns all sub-components of this component. GetSubComponents() []Component - // Required variables for this component - GetRequiredVariables() []variables.Variable - // Suported variables for this component - GetSupportedVariables() []variables.Variable + // GetRequiredVariables returns required variables for this component + GetRequiredVariables() []variables.VariableName + + // GetSupportedVariables returns supported variables (both required and optional) for this component + GetSupportedVariables() []variables.VariableName + // GetVariable retrieves a variable by its name. GetVariable(key variables.VariableName, opts ...GetSetVariableOption) (*variables.Variable, error) + + // UpdateVariable updates a variable's attribute with a new value. UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...GetSetVariableOption) error + + // Validate checks if the variable is valid for this component. Validate(key variables.VariableName) bool } diff --git a/ocpp_v201/component/component_variable_opts.go b/ocpp_v201/component/component_variable_opts.go index 7a202f8..70f17e3 100644 --- a/ocpp_v201/component/component_variable_opts.go +++ b/ocpp_v201/component/component_variable_opts.go @@ -6,6 +6,7 @@ type componentVariableOptions struct { attributeType string } +// WithAttributeType sets the attribute type for the variable options. func WithAttributeType(attributeType string) GetSetVariableOption { return func(o *componentVariableOptions) { o.attributeType = attributeType diff --git a/ocpp_v201/connector.go b/ocpp_v201/connector.go new file mode 100644 index 0000000..fc92951 --- /dev/null +++ b/ocpp_v201/connector.go @@ -0,0 +1,8 @@ +package ocpp_v201 + +import "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + +type Connector struct { + ID int + components map[component.ComponentName]component.Component +} diff --git a/ocpp_v201/controllers/aligned_data_ctrlr.go b/ocpp_v201/controllers/aligned_data_ctrlr.go index a1fe385..5ed9dc0 100644 --- a/ocpp_v201/controllers/aligned_data_ctrlr.go +++ b/ocpp_v201/controllers/aligned_data_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -39,65 +42,106 @@ func supportedAlignedDataVariables() []variables.VariableName { } type AlignedDataCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (a *AlignedDataCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameAlignedDataCtrlr } func (a *AlignedDataCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return a.instanceId } func (a *AlignedDataCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (a *AlignedDataCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (a *AlignedDataCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (a *AlignedDataCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (a *AlignedDataCtrlr) GetRequiredVariables() []variables.VariableName { + a.mu.RLock() + defer a.mu.RUnlock() + + return a.requiredVariables } -func (a *AlignedDataCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (a *AlignedDataCtrlr) GetSupportedVariables() []variables.VariableName { + a.mu.RLock() + defer a.mu.RUnlock() + + return a.supportedVariables } func (a *AlignedDataCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !a.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + a.mu.RLock() + defer a.mu.RUnlock() + + variable, exists := a.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + + return variable, nil } func (a *AlignedDataCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !a.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + a.mu.Lock() + defer a.mu.Unlock() + + v, exists := a.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (a *AlignedDataCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !a.validator.IsVariableSupported(key) { + return false + } + + a.mu.RLock() + defer a.mu.RUnlock() + + v, exists := a.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewAlignedDataCtrlr() *AlignedDataCtrlr { - return &AlignedDataCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &AlignedDataCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredAlignedDataVariables(), supportedVariables: supportedAlignedDataVariables(), + instanceId: "aligned-data-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + return ctrlr } diff --git a/ocpp_v201/controllers/auth_cache_ctrlr.go b/ocpp_v201/controllers/auth_cache_ctrlr.go index fb7837e..5ff0fe6 100644 --- a/ocpp_v201/controllers/auth_cache_ctrlr.go +++ b/ocpp_v201/controllers/auth_cache_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -34,65 +37,105 @@ func supportedAuthCacheVariables() []variables.VariableName { } type AuthCacheCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable supportedVariables []variables.VariableName requiredVariables []variables.VariableName + instanceId string + validator *variableValidator } func (a *AuthCacheCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameAuthCacheCtrlr } func (a *AuthCacheCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return a.instanceId } func (a *AuthCacheCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (a *AuthCacheCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (a *AuthCacheCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (a *AuthCacheCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (a *AuthCacheCtrlr) GetRequiredVariables() []variables.VariableName { + a.mu.RLock() + defer a.mu.RUnlock() + + return a.requiredVariables } -func (a *AuthCacheCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (a *AuthCacheCtrlr) GetSupportedVariables() []variables.VariableName { + a.mu.RLock() + defer a.mu.RUnlock() + + return a.supportedVariables } func (a *AuthCacheCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !a.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + a.mu.RLock() + defer a.mu.RUnlock() + + variable, exists := a.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + return variable, nil } func (a *AuthCacheCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !a.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + a.mu.Lock() + defer a.mu.Unlock() + + v, exists := a.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (a *AuthCacheCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !a.validator.IsVariableSupported(key) { + return false + } + + a.mu.RLock() + defer a.mu.RUnlock() + + v, exists := a.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewAuthCacheCtrlr() *AuthCacheCtrlr { - return &AuthCacheCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &AuthCacheCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), supportedVariables: supportedAuthCacheVariables(), requiredVariables: requiredAuthCacheVariables(), + instanceId: "auth-cache-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + return ctrlr } diff --git a/ocpp_v201/controllers/auth_ctrlr.go b/ocpp_v201/controllers/auth_ctrlr.go index fac961f..676750c 100644 --- a/ocpp_v201/controllers/auth_ctrlr.go +++ b/ocpp_v201/controllers/auth_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -39,65 +42,101 @@ func supportedAuthVariables() []variables.VariableName { } type AuthCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (a *AuthCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameAuthCtrlr } func (a *AuthCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return a.instanceId } func (a *AuthCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (a *AuthCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (a *AuthCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (a *AuthCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (a *AuthCtrlr) GetRequiredVariables() []variables.VariableName { + return a.requiredVariables } -func (a *AuthCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (a *AuthCtrlr) GetSupportedVariables() []variables.VariableName { + return a.supportedVariables } func (a *AuthCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !a.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + a.mu.RLock() + defer a.mu.RUnlock() + + variable, exists := a.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + + return variable, nil } func (a *AuthCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !a.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + a.mu.Lock() + defer a.mu.Unlock() + + v, exists := a.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (a *AuthCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !a.validator.IsVariableSupported(key) { + return false + } + + a.mu.RLock() + defer a.mu.RUnlock() + + v, exists := a.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewAuthCtrlr() *AuthCtrlr { - return &AuthCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &AuthCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredAuthVariables(), supportedVariables: supportedAuthVariables(), + instanceId: "auth-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/clock_ctrlr.go b/ocpp_v201/controllers/clock_ctrlr.go index fd9fe18..4b39571 100644 --- a/ocpp_v201/controllers/clock_ctrlr.go +++ b/ocpp_v201/controllers/clock_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -42,65 +45,106 @@ func supportedClockVariables() []variables.VariableName { } type ClockCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (c *ClockCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameClockCtrlr } func (c *ClockCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return c.instanceId } func (c *ClockCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (c *ClockCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (c *ClockCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (c *ClockCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (c *ClockCtrlr) GetRequiredVariables() []variables.VariableName { + c.mu.RLock() + defer c.mu.RUnlock() + + return c.requiredVariables } -func (c *ClockCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (c *ClockCtrlr) GetSupportedVariables() []variables.VariableName { + c.mu.RLock() + defer c.mu.RUnlock() + + return c.supportedVariables } func (c *ClockCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !c.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + c.mu.RLock() + defer c.mu.RUnlock() + + variable, exists := c.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + return variable, nil } func (c *ClockCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !c.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + c.mu.Lock() + defer c.mu.Unlock() + + v, exists := c.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (c *ClockCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !c.validator.IsVariableSupported(key) { + return false + } + + c.mu.RLock() + defer c.mu.RUnlock() + + v, exists := c.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewClockCtrlr() *ClockCtrlr { - return &ClockCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &ClockCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredClockVariables(), supportedVariables: supportedClockVariables(), + instanceId: "clock-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/customization_ctrlr.go b/ocpp_v201/controllers/customization_ctrlr.go index 9a8079b..c9a0877 100644 --- a/ocpp_v201/controllers/customization_ctrlr.go +++ b/ocpp_v201/controllers/customization_ctrlr.go @@ -1,6 +1,10 @@ package controllers import ( + "fmt" + "slices" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -9,58 +13,129 @@ const ( VariableNameCustomImplementationEnabled variables.VariableName = "CustomImplementationEnabled" ) -type CustomizationCtrlr struct{} +type CustomizationCtrlr struct { + mu sync.RWMutex + variables map[variables.VariableName]variables.Variable + requiredVariables []variables.VariableName // Set + supportedVariables []variables.VariableName // Set + instanceId string + validator *variableValidator +} func (c *CustomizationCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameCustomizationCtrlr } func (c *CustomizationCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return c.instanceId } func (c *CustomizationCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (c *CustomizationCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (c *CustomizationCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (c *CustomizationCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (c *CustomizationCtrlr) GetRequiredVariables() []variables.VariableName { + c.mu.RLock() + defer c.mu.RUnlock() + + return c.requiredVariables } -func (c *CustomizationCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (c *CustomizationCtrlr) GetSupportedVariables() []variables.VariableName { + c.mu.RLock() + defer c.mu.RUnlock() + + return c.supportedVariables } func (c *CustomizationCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !c.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + c.mu.RLock() + defer c.mu.RUnlock() + + variable, exists := c.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + return &variable, nil } func (c *CustomizationCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !c.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + c.mu.Lock() + defer c.mu.Unlock() + + v, exists := c.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (c *CustomizationCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !c.validator.IsVariableSupported(key) { + return false + } + + c.mu.RLock() + defer c.mu.RUnlock() + + v, exists := c.variables[key] + if !exists { + return false + } + + return v.Validate() +} + +func (c *CustomizationCtrlr) AddRequiredVariable(key variables.VariableName) { + c.mu.Lock() + defer c.mu.Unlock() + + if slices.Contains(c.requiredVariables, key) || slices.Contains(c.supportedVariables, key) { + return + } + + c.requiredVariables = append(c.requiredVariables, key) +} + +func (c *CustomizationCtrlr) AddSupportedVariable(key variables.VariableName) { + c.mu.Lock() + defer c.mu.Unlock() + + if slices.Contains(c.supportedVariables, key) || slices.Contains(c.requiredVariables, key) { + return + } + + c.supportedVariables = append(c.supportedVariables, key) } func NewCustomizationCtrlr() *CustomizationCtrlr { - return &CustomizationCtrlr{} + ctrlr := &CustomizationCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]variables.Variable), + requiredVariables: make([]variables.VariableName, 0), + supportedVariables: make([]variables.VariableName, 0), + instanceId: "customization-ctrlr", + } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/customization_ctrlr_test.go b/ocpp_v201/controllers/customization_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/customization_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/device_data_ctrlr.go b/ocpp_v201/controllers/device_data_ctrlr.go index 00fa09b..b42ab55 100644 --- a/ocpp_v201/controllers/device_data_ctrlr.go +++ b/ocpp_v201/controllers/device_data_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -30,61 +33,146 @@ func supportedVariables() []variables.VariableName { } type DeviceDataCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (d *DeviceDataCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameDeviceDataCtrlr } func (d *DeviceDataCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return d.instanceId } func (d *DeviceDataCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (d *DeviceDataCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (d *DeviceDataCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (d *DeviceDataCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (d *DeviceDataCtrlr) GetRequiredVariables() []variables.VariableName { + d.mu.RLock() + defer d.mu.RUnlock() + + return d.requiredVariables } -func (d *DeviceDataCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (d *DeviceDataCtrlr) GetSupportedVariables() []variables.VariableName { + d.mu.RLock() + defer d.mu.RUnlock() + + return d.supportedVariables } func (d *DeviceDataCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !d.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + d.mu.Lock() + defer d.mu.Unlock() + + variable, exists := d.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + return variable, nil } func (d *DeviceDataCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !d.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + d.mu.RLock() + defer d.mu.RUnlock() + + v, exists := d.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (d *DeviceDataCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !d.validator.IsVariableSupported(key) { + return false + } + + d.mu.RLock() + defer d.mu.RUnlock() + + // Check if the variable exists + v, exists := d.variables[key] + if !exists { + return false + } + + // Validate the variable itself + return v.Validate() +} + +// ValidateAllRequiredVariables checks that all required variables are present and valid +func (d *DeviceDataCtrlr) ValidateAllRequiredVariables() bool { + d.mu.RLock() + defer d.mu.RUnlock() + + for _, requiredVar := range d.requiredVariables { + // Check if the variable exists + v, exists := d.variables[requiredVar] + if !exists { + return false + } + // Validate the variable itself + if !v.Validate() { + return false + } + } + + return true } func NewDeviceDataCtrlr() *DeviceDataCtrlr { - return &DeviceDataCtrlr{} + ctrlr := &DeviceDataCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), + requiredVariables: requiredVariables(), + supportedVariables: supportedVariables(), + instanceId: "device-data-ctrlr", + } + + ctrlr.validator = newVariableValidator(ctrlr) + + // Initialize all required variables with default values + ctrlr.variables[VariableNameBytesPerMessage] = variables.NewVariable( + VariableNameBytesPerMessage, + variables.VariableTypeInteger, + ) + ctrlr.variables[VariableNameConfigurationValueSize] = variables.NewVariable( + VariableNameConfigurationValueSize, + variables.VariableTypeInteger, + ) + ctrlr.variables[VariableNameReportingValueSize] = variables.NewVariable( + VariableNameReportingValueSize, + variables.VariableTypeInteger, + ) + ctrlr.variables[VariableNameItemsPerMessage] = variables.NewVariable( + VariableNameItemsPerMessage, + variables.VariableTypeInteger, + ) + + return ctrlr } diff --git a/ocpp_v201/controllers/device_data_ctrlr_test.go b/ocpp_v201/controllers/device_data_ctrlr_test.go index 2d32936..7aa6d80 100644 --- a/ocpp_v201/controllers/device_data_ctrlr_test.go +++ b/ocpp_v201/controllers/device_data_ctrlr_test.go @@ -1 +1,266 @@ package controllers + +import ( + "testing" + + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" + "github.com/stretchr/testify/suite" +) + +type DeviceDataCtrlrTestSuite struct { + suite.Suite + ctrlr *DeviceDataCtrlr +} + +func (suite *DeviceDataCtrlrTestSuite) SetupTest() { + suite.ctrlr = NewDeviceDataCtrlr() +} + +func (suite *DeviceDataCtrlrTestSuite) TestNewDeviceDataCtrlr() { + ctrlr := NewDeviceDataCtrlr() + + suite.NotNil(ctrlr) + suite.Equal(component.ComponentNameDeviceDataCtrlr, ctrlr.GetName()) + suite.Equal("device-data-ctrlr", ctrlr.GetInstanceId()) + suite.NotNil(ctrlr.validator) + suite.NotEmpty(ctrlr.GetRequiredVariables()) + suite.NotEmpty(ctrlr.GetSupportedVariables()) +} + +func (suite *DeviceDataCtrlrTestSuite) TestGetName() { + suite.Equal(component.ComponentNameDeviceDataCtrlr, suite.ctrlr.GetName()) +} + +func (suite *DeviceDataCtrlrTestSuite) TestGetInstanceId() { + suite.Equal("device-data-ctrlr", suite.ctrlr.GetInstanceId()) +} + +func (suite *DeviceDataCtrlrTestSuite) TestSubComponentMethods() { + // Test that sub-component methods are no-op + initialCount := len(suite.ctrlr.GetSubComponents()) + + // Register should be no-op + suite.ctrlr.RegisterSubComponent(nil) + suite.Equal(initialCount, len(suite.ctrlr.GetSubComponents()), "RegisterSubComponent should be a no-op") + + // Unregister should be no-op + suite.ctrlr.UnregisterSubComponent(nil) + suite.Equal(initialCount, len(suite.ctrlr.GetSubComponents()), "UnregisterSubComponent should be a no-op") +} + +func (suite *DeviceDataCtrlrTestSuite) TestGetSubComponents() { + subComponents := suite.ctrlr.GetSubComponents() + suite.Equal(0, len(subComponents)) +} + +func (suite *DeviceDataCtrlrTestSuite) TestGetRequiredVariables() { + requiredVars := suite.ctrlr.GetRequiredVariables() + suite.NotEmpty(requiredVars) + + expectedVars := []variables.VariableName{ + VariableNameBytesPerMessage, + VariableNameConfigurationValueSize, + VariableNameReportingValueSize, + } + + for _, expectedVar := range expectedVars { + suite.Contains(requiredVars, expectedVar) + } +} + +func (suite *DeviceDataCtrlrTestSuite) TestGetSupportedVariables() { + supportedVars := suite.ctrlr.GetSupportedVariables() + suite.NotEmpty(supportedVars) + + // Should include all required variables + requiredVars := suite.ctrlr.GetRequiredVariables() + for _, requiredVar := range requiredVars { + suite.Contains(supportedVars, requiredVar) + } +} + +func (suite *DeviceDataCtrlrTestSuite) TestGetVariable() { + tests := []struct { + name string + variableName variables.VariableName + setupVariable bool + expectError bool + errorContains string + }{ + { + name: "existing variable", + variableName: VariableNameBytesPerMessage, + setupVariable: true, + expectError: false, + }, + { + name: "non-existent variable", + variableName: "NonExistentVariable", + setupVariable: false, + expectError: true, + errorContains: "not found", + }, + { + name: "unsupported variable", + variableName: "UnsupportedVariable", + setupVariable: false, + expectError: true, + errorContains: "not supported", + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + if tt.setupVariable { + // Variable is already set up in constructor + } + + result, err := suite.ctrlr.GetVariable(tt.variableName) + + if tt.expectError { + suite.Error(err) + if tt.errorContains != "" { + suite.Contains(err.Error(), tt.errorContains) + } + suite.Nil(result) + } else { + suite.NoError(err) + suite.NotNil(result) + suite.Equal(tt.variableName, result.Name) + } + }) + } +} + +func (suite *DeviceDataCtrlrTestSuite) TestUpdateVariable() { + tests := []struct { + name string + variableName variables.VariableName + attribute string + value interface{} + setupVariable bool + expectError bool + errorContains string + }{ + { + name: "update existing variable", + variableName: VariableNameBytesPerMessage, + attribute: "value", + value: int64(1024), + setupVariable: true, + expectError: false, + }, + { + name: "update non-existent variable", + variableName: "NonExistentVariable", + attribute: "value", + value: int64(1024), + setupVariable: false, + expectError: true, + errorContains: "not found", + }, + { + name: "update unsupported variable", + variableName: "UnsupportedVariable", + attribute: "value", + value: int64(1024), + setupVariable: false, + expectError: true, + errorContains: "not supported", + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + err := suite.ctrlr.UpdateVariable(tt.variableName, tt.attribute, tt.value) + + if tt.expectError { + suite.Error(err) + if tt.errorContains != "" { + suite.Contains(err.Error(), tt.errorContains) + } + } else { + suite.NoError(err) + + // Verify the update worked + variable, err := suite.ctrlr.GetVariable(tt.variableName) + suite.NoError(err) + suite.NotNil(variable) + } + }) + } +} + +func (suite *DeviceDataCtrlrTestSuite) TestValidate() { + tests := []struct { + name string + variableName variables.VariableName + setupVariable bool + expected bool + }{ + { + name: "valid existing variable", + variableName: VariableNameBytesPerMessage, + setupVariable: true, + expected: true, + }, + { + name: "non-existent variable", + variableName: "NonExistentVariable", + setupVariable: false, + expected: false, + }, + { + name: "unsupported variable", + variableName: "UnsupportedVariable", + setupVariable: false, + expected: false, + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + result := suite.ctrlr.Validate(tt.variableName) + suite.Equal(tt.expected, result) + }) + } +} + +func (suite *DeviceDataCtrlrTestSuite) TestValidateAllRequiredVariables() { + // Test with all required variables present (default state) + result := suite.ctrlr.ValidateAllRequiredVariables() + suite.True(result) +} + +func (suite *DeviceDataCtrlrTestSuite) TestThreadSafety() { + // Test concurrent access to the controller + done := make(chan bool, 10) + + for i := 0; i < 10; i++ { + go func() { + // Concurrent reads + _, _ = suite.ctrlr.GetVariable(VariableNameBytesPerMessage) + _ = suite.ctrlr.Validate(VariableNameBytesPerMessage) + _ = suite.ctrlr.GetRequiredVariables() + _ = suite.ctrlr.GetSupportedVariables() + + // Concurrent writes + _ = suite.ctrlr.UpdateVariable(VariableNameBytesPerMessage, "value", int64(512)) + + done <- true + }() + } + + // Wait for all goroutines to complete + for i := 0; i < 10; i++ { + <-done + } + + // Verify the controller is still in a valid state + suite.True(suite.ctrlr.ValidateAllRequiredVariables()) +} + +func TestDeviceDataCtrlrTestSuite(t *testing.T) { + suite.Run(t, new(DeviceDataCtrlrTestSuite)) +} diff --git a/ocpp_v201/controllers/display_message_ctrlr.go b/ocpp_v201/controllers/display_message_ctrlr.go index 9ea7013..c58159f 100644 --- a/ocpp_v201/controllers/display_message_ctrlr.go +++ b/ocpp_v201/controllers/display_message_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -33,65 +36,107 @@ func supportedDisplayMessageVariables() []variables.VariableName { } type DisplayCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (d *DisplayCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameDisplayMessageCtrlr } func (d *DisplayCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return d.instanceId } func (d *DisplayCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (d *DisplayCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (d *DisplayCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (d *DisplayCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (d *DisplayCtrlr) GetRequiredVariables() []variables.VariableName { + d.mu.RLock() + defer d.mu.RUnlock() + + return d.requiredVariables } -func (d *DisplayCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (d *DisplayCtrlr) GetSupportedVariables() []variables.VariableName { + d.mu.RLock() + defer d.mu.RUnlock() + + return d.supportedVariables } func (d *DisplayCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !d.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + d.mu.RLock() + defer d.mu.RUnlock() + + variable, exists := d.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + + return variable, nil } func (d *DisplayCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !d.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + d.mu.Lock() + defer d.mu.Unlock() + + v, exists := d.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (d *DisplayCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !d.validator.IsVariableSupported(key) { + return false + } + + d.mu.RLock() + defer d.mu.RUnlock() + + v, exists := d.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewDisplayCtrlr() *DisplayCtrlr { - return &DisplayCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &DisplayCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredDisplayMessageVariables(), supportedVariables: supportedDisplayMessageVariables(), + instanceId: "display-message-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/display_message_ctrlr_test.go b/ocpp_v201/controllers/display_message_ctrlr_test.go index 2d32936..df66fc0 100644 --- a/ocpp_v201/controllers/display_message_ctrlr_test.go +++ b/ocpp_v201/controllers/display_message_ctrlr_test.go @@ -1 +1,276 @@ package controllers + +import ( + "testing" + + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" + "github.com/stretchr/testify/suite" +) + +type DisplayCtrlrTestSuite struct { + suite.Suite + ctrlr *DisplayCtrlr +} + +func (suite *DisplayCtrlrTestSuite) SetupTest() { + suite.ctrlr = NewDisplayCtrlr() +} + +func (suite *DisplayCtrlrTestSuite) TestNewDisplayCtrlr() { + ctrlr := NewDisplayCtrlr() + + suite.NotNil(ctrlr) + suite.Equal(component.ComponentNameDisplayMessageCtrlr, ctrlr.GetName()) + suite.Equal("display-message-ctrlr", ctrlr.GetInstanceId()) + suite.NotNil(ctrlr.validator) + suite.NotEmpty(ctrlr.GetRequiredVariables()) + suite.NotEmpty(ctrlr.GetSupportedVariables()) +} + +func (suite *DisplayCtrlrTestSuite) TestGetName() { + suite.Equal(component.ComponentNameDisplayMessageCtrlr, suite.ctrlr.GetName()) +} + +func (suite *DisplayCtrlrTestSuite) TestGetInstanceId() { + suite.Equal("display-message-ctrlr", suite.ctrlr.GetInstanceId()) +} + +func (suite *DisplayCtrlrTestSuite) TestSubComponentMethods() { + // Test that sub-component methods are no-op + initialCount := len(suite.ctrlr.GetSubComponents()) + + // Register should be no-op + suite.ctrlr.RegisterSubComponent(nil) + suite.Equal(initialCount, len(suite.ctrlr.GetSubComponents()), "RegisterSubComponent should be a no-op") + + // Unregister should be no-op + suite.ctrlr.UnregisterSubComponent(nil) + suite.Equal(initialCount, len(suite.ctrlr.GetSubComponents()), "UnregisterSubComponent should be a no-op") +} + +func (suite *DisplayCtrlrTestSuite) TestGetSubComponents() { + subComponents := suite.ctrlr.GetSubComponents() + suite.Equal(0, len(subComponents)) +} + +func (suite *DisplayCtrlrTestSuite) TestGetRequiredVariables() { + requiredVars := suite.ctrlr.GetRequiredVariables() + suite.NotEmpty(requiredVars) + + expectedVars := []variables.VariableName{ + VariableNameNumberOfDisplayMessages, + VariableNameDisplayMessageSupportedFormats, + VariableNameDisplayMessageSupportedPriorities, + } + + for _, expectedVar := range expectedVars { + suite.Contains(requiredVars, expectedVar) + } +} + +func (suite *DisplayCtrlrTestSuite) TestGetSupportedVariables() { + supportedVars := suite.ctrlr.GetSupportedVariables() + suite.NotEmpty(supportedVars) + + // Should include all required variables + requiredVars := suite.ctrlr.GetRequiredVariables() + for _, requiredVar := range requiredVars { + suite.Contains(supportedVars, requiredVar) + } +} + +func (suite *DisplayCtrlrTestSuite) TestGetVariable() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameDisplayMessageSupportedFormats, + variables.VariableTypeString, + "ASCII", + ) + suite.ctrlr.variables[VariableNameDisplayMessageSupportedFormats] = *testVar + + tests := []struct { + name string + variableName variables.VariableName + expectError bool + errorContains string + }{ + { + name: "existing variable", + variableName: VariableNameDisplayMessageSupportedFormats, + expectError: false, + }, + { + name: "non-existent variable", + variableName: "NonExistentVariable", + expectError: true, + errorContains: "not found", + }, + { + name: "unsupported variable", + variableName: "UnsupportedVariable", + expectError: true, + errorContains: "not supported", + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + result, err := suite.ctrlr.GetVariable(tt.variableName) + + if tt.expectError { + suite.Error(err) + if tt.errorContains != "" { + suite.Contains(err.Error(), tt.errorContains) + } + suite.Nil(result) + } else { + suite.NoError(err) + suite.NotNil(result) + suite.Equal(tt.variableName, result.Name) + } + }) + } +} + +func (suite *DisplayCtrlrTestSuite) TestUpdateVariable() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameDisplayMessageSupportedFormats, + variables.VariableTypeString, + "ASCII", + ) + suite.ctrlr.variables[VariableNameDisplayMessageSupportedFormats] = *testVar + + tests := []struct { + name string + variableName variables.VariableName + attribute string + value interface{} + expectError bool + errorContains string + }{ + { + name: "update existing variable", + variableName: VariableNameDisplayMessageSupportedFormats, + attribute: "value", + value: "UTF8", + expectError: false, + }, + { + name: "update non-existent variable", + variableName: "NonExistentVariable", + attribute: "value", + value: "UTF8", + expectError: true, + errorContains: "not found", + }, + { + name: "update unsupported variable", + variableName: "UnsupportedVariable", + attribute: "value", + value: "UTF8", + expectError: true, + errorContains: "not supported", + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + err := suite.ctrlr.UpdateVariable(tt.variableName, tt.attribute, tt.value) + + if tt.expectError { + suite.Error(err) + if tt.errorContains != "" { + suite.Contains(err.Error(), tt.errorContains) + } + } else { + suite.NoError(err) + + // Verify the update worked + variable, err := suite.ctrlr.GetVariable(tt.variableName) + suite.NoError(err) + suite.NotNil(variable) + } + }) + } +} + +func (suite *DisplayCtrlrTestSuite) TestValidate() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameDisplayMessageSupportedFormats, + variables.VariableTypeString, + "ASCII", + ) + suite.ctrlr.variables[VariableNameDisplayMessageSupportedFormats] = *testVar + + tests := []struct { + name string + variableName variables.VariableName + expected bool + }{ + { + name: "valid existing variable", + variableName: VariableNameDisplayMessageSupportedFormats, + expected: true, + }, + { + name: "non-existent variable", + variableName: "NonExistentVariable", + expected: false, + }, + { + name: "unsupported variable", + variableName: "UnsupportedVariable", + expected: false, + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + result := suite.ctrlr.Validate(tt.variableName) + suite.Equal(tt.expected, result) + }) + } +} + +func (suite *DisplayCtrlrTestSuite) TestThreadSafety() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameDisplayMessageSupportedFormats, + variables.VariableTypeString, + "ASCII", + ) + suite.ctrlr.variables[VariableNameDisplayMessageSupportedFormats] = *testVar + + // Test concurrent access to the controller + done := make(chan bool, 10) + + for i := 0; i < 10; i++ { + go func() { + // Concurrent reads + _, _ = suite.ctrlr.GetVariable(VariableNameDisplayMessageSupportedFormats) + _ = suite.ctrlr.Validate(VariableNameDisplayMessageSupportedFormats) + _ = suite.ctrlr.GetRequiredVariables() + _ = suite.ctrlr.GetSupportedVariables() + + // Concurrent writes + _ = suite.ctrlr.UpdateVariable(VariableNameDisplayMessageSupportedFormats, "value", "UTF8") + + done <- true + }() + } + + // Wait for all goroutines to complete + for i := 0; i < 10; i++ { + <-done + } + + // Verify the controller is still in a valid state + suite.True(suite.ctrlr.Validate(VariableNameDisplayMessageSupportedFormats)) +} + +func TestDisplayCtrlrTestSuite(t *testing.T) { + suite.Run(t, new(DisplayCtrlrTestSuite)) +} diff --git a/ocpp_v201/controllers/iso15118_ctrlr.go b/ocpp_v201/controllers/iso15118_ctrlr.go index 3155914..4316cec 100644 --- a/ocpp_v201/controllers/iso15118_ctrlr.go +++ b/ocpp_v201/controllers/iso15118_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -45,65 +48,106 @@ func supportedVariablesISO15118Ctrlr() []variables.VariableName { } type ISO15118Ctrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (I *ISO15118Ctrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameISO15118Ctrlr } func (I *ISO15118Ctrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return I.instanceId } func (I *ISO15118Ctrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (I *ISO15118Ctrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (I *ISO15118Ctrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (I *ISO15118Ctrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (I *ISO15118Ctrlr) GetRequiredVariables() []variables.VariableName { + I.mu.RLock() + defer I.mu.RUnlock() + + return I.requiredVariables } -func (I *ISO15118Ctrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (I *ISO15118Ctrlr) GetSupportedVariables() []variables.VariableName { + I.mu.RLock() + defer I.mu.RUnlock() + + return I.supportedVariables } func (I *ISO15118Ctrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !I.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + I.mu.RLock() + defer I.mu.RUnlock() + + variable, exists := I.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + return variable, nil } func (I *ISO15118Ctrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !I.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + I.mu.Lock() + defer I.mu.Unlock() + + v, exists := I.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (I *ISO15118Ctrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !I.validator.IsVariableSupported(key) { + return false + } + + I.mu.RLock() + defer I.mu.RUnlock() + + v, exists := I.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewISO15118Ctrlr() *ISO15118Ctrlr { - return &ISO15118Ctrlr{ - variables: map[variables.VariableName]variables.Variable{}, + ctrlr := &ISO15118Ctrlr{ + mu: sync.RWMutex{}, + variables: map[variables.VariableName]*variables.Variable{}, requiredVariables: requiredVariablesISO15118Ctrlr(), supportedVariables: supportedVariablesISO15118Ctrlr(), + instanceId: "iso15118-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/iso15118_ctrlr_test.go b/ocpp_v201/controllers/iso15118_ctrlr_test.go new file mode 100644 index 0000000..2d32936 --- /dev/null +++ b/ocpp_v201/controllers/iso15118_ctrlr_test.go @@ -0,0 +1 @@ +package controllers diff --git a/ocpp_v201/controllers/local_auth_list_ctrlr.go b/ocpp_v201/controllers/local_auth_list_ctrlr.go index 9a84057..b574e5d 100644 --- a/ocpp_v201/controllers/local_auth_list_ctrlr.go +++ b/ocpp_v201/controllers/local_auth_list_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -36,65 +39,106 @@ func supportedLocalAuthListVariables() []variables.VariableName { } type LocalAuthListCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (l *LocalAuthListCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameLocalAuthListCtrlr } func (l *LocalAuthListCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return l.instanceId } func (l *LocalAuthListCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (l *LocalAuthListCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (l *LocalAuthListCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (l *LocalAuthListCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (l *LocalAuthListCtrlr) GetRequiredVariables() []variables.VariableName { + l.mu.RLock() + defer l.mu.RUnlock() + + return l.requiredVariables } -func (l *LocalAuthListCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (l *LocalAuthListCtrlr) GetSupportedVariables() []variables.VariableName { + l.mu.RLock() + defer l.mu.RUnlock() + + return l.supportedVariables } func (l *LocalAuthListCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !l.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + l.mu.RLock() + defer l.mu.RUnlock() + + variable, exists := l.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + return variable, nil } func (l *LocalAuthListCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !l.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + l.mu.Lock() + defer l.mu.Unlock() + + v, exists := l.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (l *LocalAuthListCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !l.validator.IsVariableSupported(key) { + return false + } + + l.mu.RLock() + defer l.mu.RUnlock() + + v, exists := l.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewLocalAuthListCtrlr() *LocalAuthListCtrlr { - return &LocalAuthListCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &LocalAuthListCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredLocalAuthListVariables(), supportedVariables: supportedLocalAuthListVariables(), + instanceId: "local-auth-list-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/monitoring_ctrlr.go b/ocpp_v201/controllers/monitoring_ctrlr.go index a05e136..65ee564 100644 --- a/ocpp_v201/controllers/monitoring_ctrlr.go +++ b/ocpp_v201/controllers/monitoring_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -41,65 +44,106 @@ func supportedMonitoringVariables() []variables.VariableName { } type MonitoringCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (m *MonitoringCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameMonitoringCtrlr } func (m *MonitoringCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return m.instanceId } func (m *MonitoringCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (m *MonitoringCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (m *MonitoringCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (m *MonitoringCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (m *MonitoringCtrlr) GetRequiredVariables() []variables.VariableName { + m.mu.RLock() + defer m.mu.RUnlock() + + return m.requiredVariables } -func (m *MonitoringCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (m *MonitoringCtrlr) GetSupportedVariables() []variables.VariableName { + m.mu.RLock() + defer m.mu.RUnlock() + + return m.supportedVariables } func (m *MonitoringCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !m.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + m.mu.RLock() + defer m.mu.RUnlock() + + variable, exists := m.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + + return variable, nil } func (m *MonitoringCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !m.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + m.mu.Lock() + defer m.mu.Unlock() + + v, exists := m.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (m *MonitoringCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !m.validator.IsVariableSupported(key) { + return false + } + + m.mu.RLock() + defer m.mu.RUnlock() + + v, exists := m.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewMonitoringCtrlr() *MonitoringCtrlr { - return &MonitoringCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &MonitoringCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredMonitoringVariables(), supportedVariables: supportedMonitoringVariables(), + instanceId: "monitoring-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + return ctrlr } diff --git a/ocpp_v201/controllers/ocpp_comm_ctrlr.go b/ocpp_v201/controllers/ocpp_comm_ctrlr.go index 1255475..86fccf5 100644 --- a/ocpp_v201/controllers/ocpp_comm_ctrlr.go +++ b/ocpp_v201/controllers/ocpp_comm_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -59,57 +62,88 @@ func supportedOcppCommCtrlrVariables() []variables.VariableName { } type OCPPCommCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (ctrlr *OCPPCommCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameOCPPCommCtrlr } func (ctrlr *OCPPCommCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return ctrlr.instanceId } func (ctrlr *OCPPCommCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (ctrlr *OCPPCommCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (ctrlr *OCPPCommCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (ctrlr *OCPPCommCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (ctrlr *OCPPCommCtrlr) GetRequiredVariables() []variables.VariableName { + ctrlr.mu.RLock() + defer ctrlr.mu.RUnlock() + + return ctrlr.requiredVariables } -func (ctrlr *OCPPCommCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (ctrlr *OCPPCommCtrlr) GetSupportedVariables() []variables.VariableName { + ctrlr.mu.RLock() + defer ctrlr.mu.RUnlock() + + return ctrlr.supportedVariables } func (ctrlr *OCPPCommCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !ctrlr.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + ctrlr.mu.RLock() + defer ctrlr.mu.RUnlock() + + variable, exists := ctrlr.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + return variable, nil } func (ctrlr *OCPPCommCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !ctrlr.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + ctrlr.mu.Lock() + defer ctrlr.mu.Unlock() + + v, exists := ctrlr.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (ctrlr *OCPPCommCtrlr) Validate(key variables.VariableName) bool { + if !ctrlr.validator.IsVariableSupported(key) { + return false + } + + ctrlr.mu.RLock() + defer ctrlr.mu.RUnlock() + v, exists := ctrlr.variables[key] if !exists { return false @@ -119,9 +153,15 @@ func (ctrlr *OCPPCommCtrlr) Validate(key variables.VariableName) bool { } func NewOCPPCommCtrlr() *OCPPCommCtrlr { - return &OCPPCommCtrlr{ - variables: map[variables.VariableName]variables.Variable{}, + ctrlr := &OCPPCommCtrlr{ + mu: sync.RWMutex{}, + variables: map[variables.VariableName]*variables.Variable{}, requiredVariables: requiredOcppCommCtrlrVariables(), supportedVariables: supportedOcppCommCtrlrVariables(), + instanceId: "ocpp-comm-ctrlr-1", } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/ocpp_comm_ctrlr_test.go b/ocpp_v201/controllers/ocpp_comm_ctrlr_test.go index 2d32936..0d171e8 100644 --- a/ocpp_v201/controllers/ocpp_comm_ctrlr_test.go +++ b/ocpp_v201/controllers/ocpp_comm_ctrlr_test.go @@ -1 +1,299 @@ package controllers + +import ( + "testing" + + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" + "github.com/stretchr/testify/suite" +) + +type OCPPCommCtrlrTestSuite struct { + suite.Suite + ctrlr *OCPPCommCtrlr +} + +func (suite *OCPPCommCtrlrTestSuite) SetupTest() { + suite.ctrlr = NewOCPPCommCtrlr() +} + +func (suite *OCPPCommCtrlrTestSuite) TestNewOCPPCommCtrlr() { + ctrlr := NewOCPPCommCtrlr() + + suite.NotNil(ctrlr) + suite.Equal(component.ComponentNameOCPPCommCtrlr, ctrlr.GetName()) + suite.Equal("ocpp-comm-ctrlr-1", ctrlr.GetInstanceId()) + suite.NotNil(ctrlr.validator) + suite.NotEmpty(ctrlr.GetRequiredVariables()) + suite.NotEmpty(ctrlr.GetSupportedVariables()) +} + +func (suite *OCPPCommCtrlrTestSuite) TestGetName() { + suite.Equal(component.ComponentNameOCPPCommCtrlr, suite.ctrlr.GetName()) +} + +func (suite *OCPPCommCtrlrTestSuite) TestGetInstanceId() { + suite.Equal("ocpp-comm-ctrlr-1", suite.ctrlr.GetInstanceId()) +} + +func (suite *OCPPCommCtrlrTestSuite) TestSubComponentMethods() { + // Test that sub-component methods are no-op + initialCount := len(suite.ctrlr.GetSubComponents()) + + // Register should be no-op + suite.ctrlr.RegisterSubComponent(nil) + suite.Equal(initialCount, len(suite.ctrlr.GetSubComponents()), "RegisterSubComponent should be a no-op") + + // Unregister should be no-op + suite.ctrlr.UnregisterSubComponent(nil) + suite.Equal(initialCount, len(suite.ctrlr.GetSubComponents()), "UnregisterSubComponent should be a no-op") +} + +func (suite *OCPPCommCtrlrTestSuite) TestGetSubComponents() { + subComponents := suite.ctrlr.GetSubComponents() + suite.Equal(0, len(subComponents)) +} + +func (suite *OCPPCommCtrlrTestSuite) TestGetRequiredVariables() { + requiredVars := suite.ctrlr.GetRequiredVariables() + suite.NotEmpty(requiredVars) + + expectedVars := []variables.VariableName{ + VariableNameDefaultMessageTimeout, + VariableNameFileTransferProtocols, + VariableNameOfflineThreshold, + VariableNameNetworkProfileConnectionAttempts, + VariableNameMessageAttempts, + VariableNameMessageAttemptInterval, + VariableNameMessageAttemptsTransactionEvent, + VariableNameMessageAttemptIntervalTransactionEvent, + VariableNameUnlockOnEVSideDisconnect, + VariableNameResetRetries, + } + + for _, expectedVar := range expectedVars { + suite.Contains(requiredVars, expectedVar) + } +} + +func (suite *OCPPCommCtrlrTestSuite) TestGetSupportedVariables() { + supportedVars := suite.ctrlr.GetSupportedVariables() + suite.NotEmpty(supportedVars) + + // Should include all required variables + requiredVars := suite.ctrlr.GetRequiredVariables() + for _, requiredVar := range requiredVars { + suite.Contains(supportedVars, requiredVar) + } + + // Should include optional variables + optionalVars := []variables.VariableName{ + VariableNameNetworkConfigurationPriority, + VariableNameHeartbeatInterval, + VariableNameMessageTimeout, + VariableNameActiveNetworkProfile, + VariableNameQueueAllMessages, + VariableNameWebSocketPingInterval, + VariableNameFieldLength, + VariableNamePublicKeyWithSignedMeterValue, + } + + for _, optionalVar := range optionalVars { + suite.Contains(supportedVars, optionalVar) + } +} + +func (suite *OCPPCommCtrlrTestSuite) TestGetVariable() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameHeartbeatInterval, + variables.VariableTypeInteger, + int64(300), + ) + suite.ctrlr.variables[VariableNameHeartbeatInterval] = *testVar + + tests := []struct { + name string + variableName variables.VariableName + expectError bool + errorContains string + }{ + { + name: "existing variable", + variableName: VariableNameHeartbeatInterval, + expectError: false, + }, + { + name: "non-existent variable", + variableName: "NonExistentVariable", + expectError: true, + errorContains: "not found", + }, + { + name: "unsupported variable", + variableName: "UnsupportedVariable", + expectError: true, + errorContains: "not supported", + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + result, err := suite.ctrlr.GetVariable(tt.variableName) + + if tt.expectError { + suite.Error(err) + if tt.errorContains != "" { + suite.Contains(err.Error(), tt.errorContains) + } + suite.Nil(result) + } else { + suite.NoError(err) + suite.NotNil(result) + suite.Equal(tt.variableName, result.Name) + } + }) + } +} + +func (suite *OCPPCommCtrlrTestSuite) TestUpdateVariable() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameHeartbeatInterval, + variables.VariableTypeInteger, + int64(300), + ) + suite.ctrlr.variables[VariableNameHeartbeatInterval] = *testVar + + tests := []struct { + name string + variableName variables.VariableName + attribute string + value interface{} + expectError bool + errorContains string + }{ + { + name: "update existing variable", + variableName: VariableNameHeartbeatInterval, + attribute: "value", + value: int64(600), + expectError: false, + }, + { + name: "update non-existent variable", + variableName: "NonExistentVariable", + attribute: "value", + value: int64(600), + expectError: true, + errorContains: "not found", + }, + { + name: "update unsupported variable", + variableName: "UnsupportedVariable", + attribute: "value", + value: int64(600), + expectError: true, + errorContains: "not supported", + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + err := suite.ctrlr.UpdateVariable(tt.variableName, tt.attribute, tt.value) + + if tt.expectError { + suite.Error(err) + if tt.errorContains != "" { + suite.Contains(err.Error(), tt.errorContains) + } + } else { + suite.NoError(err) + + // Verify the update worked + variable, err := suite.ctrlr.GetVariable(tt.variableName) + suite.NoError(err) + suite.NotNil(variable) + } + }) + } +} + +func (suite *OCPPCommCtrlrTestSuite) TestValidate() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameHeartbeatInterval, + variables.VariableTypeInteger, + int64(300), + ) + suite.ctrlr.variables[VariableNameHeartbeatInterval] = *testVar + + tests := []struct { + name string + variableName variables.VariableName + expected bool + }{ + { + name: "valid existing variable", + variableName: VariableNameHeartbeatInterval, + expected: true, + }, + { + name: "non-existent variable", + variableName: "NonExistentVariable", + expected: false, + }, + { + name: "unsupported variable", + variableName: "UnsupportedVariable", + expected: false, + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + result := suite.ctrlr.Validate(tt.variableName) + suite.Equal(tt.expected, result) + }) + } +} + +func (suite *OCPPCommCtrlrTestSuite) TestThreadSafety() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameHeartbeatInterval, + variables.VariableTypeInteger, + int64(300), + ) + suite.ctrlr.variables[VariableNameHeartbeatInterval] = *testVar + + // Test concurrent access to the controller + done := make(chan bool, 10) + + for i := 0; i < 10; i++ { + go func() { + // Concurrent reads + _, _ = suite.ctrlr.GetVariable(VariableNameHeartbeatInterval) + _ = suite.ctrlr.Validate(VariableNameHeartbeatInterval) + _ = suite.ctrlr.GetRequiredVariables() + _ = suite.ctrlr.GetSupportedVariables() + + // Concurrent writes + _ = suite.ctrlr.UpdateVariable(VariableNameHeartbeatInterval, "value", int64(600)) + + done <- true + }() + } + + // Wait for all goroutines to complete + for i := 0; i < 10; i++ { + <-done + } + + // Verify the controller is still in a valid state + suite.True(suite.ctrlr.Validate(VariableNameHeartbeatInterval)) +} + +func TestOCPPCommCtrlrTestSuite(t *testing.T) { + suite.Run(t, new(OCPPCommCtrlrTestSuite)) +} diff --git a/ocpp_v201/controllers/reservation_ctrlr.go b/ocpp_v201/controllers/reservation_ctrlr.go index ab39561..5fdb908 100644 --- a/ocpp_v201/controllers/reservation_ctrlr.go +++ b/ocpp_v201/controllers/reservation_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -24,65 +27,105 @@ func supportedReservationVariables() []variables.VariableName { } type ReservationCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (r *ReservationCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameReservationCtrlr } func (r *ReservationCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return r.instanceId } func (r *ReservationCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (r *ReservationCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (r *ReservationCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (r *ReservationCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (r *ReservationCtrlr) GetRequiredVariables() []variables.VariableName { + r.mu.RLock() + defer r.mu.RUnlock() + + return r.requiredVariables } -func (r *ReservationCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (r *ReservationCtrlr) GetSupportedVariables() []variables.VariableName { + r.mu.RLock() + defer r.mu.RUnlock() + + return r.supportedVariables } func (r *ReservationCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !r.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + r.mu.RLock() + defer r.mu.RUnlock() + + variable, exists := r.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + return variable, nil } func (r *ReservationCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !r.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + r.mu.Lock() + defer r.mu.Unlock() + + v, exists := r.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (r *ReservationCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !r.validator.IsVariableSupported(key) { + return false + } + + r.mu.RLock() + defer r.mu.RUnlock() + + v, exists := r.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewReservationCtrlr() *ReservationCtrlr { - return &ReservationCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &ReservationCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: []variables.VariableName{}, // No required variables for ReservationCtrlr supportedVariables: supportedReservationVariables(), + instanceId: "reservation-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + return ctrlr } diff --git a/ocpp_v201/controllers/sampled_data_ctrlr.go b/ocpp_v201/controllers/sampled_data_ctrlr.go index 3948b91..54cb070 100644 --- a/ocpp_v201/controllers/sampled_data_ctrlr.go +++ b/ocpp_v201/controllers/sampled_data_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -41,65 +44,107 @@ func supportedSampledDataVariables() []variables.VariableName { } type SampledDataCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (s *SampledDataCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameSampledDataCtrlr } func (s *SampledDataCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return s.instanceId } func (s *SampledDataCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (s *SampledDataCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (s *SampledDataCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (s *SampledDataCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (s *SampledDataCtrlr) GetRequiredVariables() []variables.VariableName { + s.mu.RLock() + defer s.mu.RUnlock() + + return s.requiredVariables } -func (s *SampledDataCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (s *SampledDataCtrlr) GetSupportedVariables() []variables.VariableName { + s.mu.RLock() + defer s.mu.RUnlock() + + return s.supportedVariables } func (s *SampledDataCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !s.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + s.mu.RLock() + defer s.mu.RUnlock() + + variable, exists := s.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + + return variable, nil } func (s *SampledDataCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !s.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + s.mu.Lock() + defer s.mu.Unlock() + + v, exists := s.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (s *SampledDataCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !s.validator.IsVariableSupported(key) { + return false + } + + s.mu.Lock() + defer s.mu.Unlock() + + v, exists := s.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewSampledDataCtrlr() *SampledDataCtrlr { - return &SampledDataCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &SampledDataCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredSampledDataVariables(), supportedVariables: supportedSampledDataVariables(), + instanceId: "sampled-data-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/security_ctrlr.go b/ocpp_v201/controllers/security_ctrlr.go index 7fe7c8d..97e9486 100644 --- a/ocpp_v201/controllers/security_ctrlr.go +++ b/ocpp_v201/controllers/security_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -19,8 +22,6 @@ const ( func requiredSecurityVariables() []variables.VariableName { return []variables.VariableName{ - VariableNameOrganizationName, - VariableNameCertificateEntries, VariableNameSecurityProfile, } } @@ -33,6 +34,8 @@ func optionalSecurityVariables() []variables.VariableName { VariableNameCertSigningRepeatTimes, VariableNameMaxCertificateChainSize, VariableNameCertSigningWaitMinimum, + VariableNameCertificateEntries, + VariableNameOrganizationName, } } @@ -41,65 +44,107 @@ func supportedSecurityVariables() []variables.VariableName { } type SecurityCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (s *SecurityCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameSecurityCtrlr } func (s *SecurityCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return s.instanceId } func (s *SecurityCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (s *SecurityCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (s *SecurityCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (s *SecurityCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (s *SecurityCtrlr) GetRequiredVariables() []variables.VariableName { + s.mu.RLock() + defer s.mu.RUnlock() + + return s.requiredVariables } -func (s *SecurityCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (s *SecurityCtrlr) GetSupportedVariables() []variables.VariableName { + s.mu.RLock() + defer s.mu.RUnlock() + + return s.supportedVariables } func (s *SecurityCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !s.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + s.mu.RLock() + defer s.mu.RUnlock() + + variable, exists := s.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + + return variable, nil } func (s *SecurityCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !s.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + s.mu.RLock() + defer s.mu.RUnlock() + + v, exists := s.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (s *SecurityCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !s.validator.IsVariableSupported(key) { + return false + } + + s.mu.RLock() + defer s.mu.RUnlock() + + v, exists := s.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewSecurityCtrlr() *SecurityCtrlr { - return &SecurityCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &SecurityCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredSecurityVariables(), supportedVariables: supportedSecurityVariables(), + instanceId: "security-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/smart_charging_ctrlr.go b/ocpp_v201/controllers/smart_charging_ctrlr.go index f40f410..cd4b05a 100644 --- a/ocpp_v201/controllers/smart_charging_ctrlr.go +++ b/ocpp_v201/controllers/smart_charging_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -45,65 +48,107 @@ func supportedSmartChargingVariables() []variables.VariableName { } type SmartChargingCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (s *SmartChargingCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameSmartChargingCtrlr } func (s *SmartChargingCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return s.instanceId } func (s *SmartChargingCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (s *SmartChargingCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (s *SmartChargingCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + // Controllers do not support sub-components, always return empty slice + return []component.Component{} } -func (s *SmartChargingCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (s *SmartChargingCtrlr) GetRequiredVariables() []variables.VariableName { + s.mu.RLock() + defer s.mu.RUnlock() + + return s.requiredVariables } -func (s *SmartChargingCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (s *SmartChargingCtrlr) GetSupportedVariables() []variables.VariableName { + s.mu.RLock() + defer s.mu.RUnlock() + + return s.supportedVariables } func (s *SmartChargingCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !s.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + s.mu.RLock() + defer s.mu.RUnlock() + + variable, exists := s.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + + return variable, nil } func (s *SmartChargingCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !s.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + s.mu.Lock() + defer s.mu.Unlock() + + v, exists := s.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (s *SmartChargingCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !s.validator.IsVariableSupported(key) { + return false + } + + s.mu.RLock() + defer s.mu.RUnlock() + + v, exists := s.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewSmartChargingCtrlr() *SmartChargingCtrlr { - return &SmartChargingCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &SmartChargingCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredSmartChargingVariables(), supportedVariables: supportedSmartChargingVariables(), + instanceId: "smart-charging-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/tariff_cost_ctrlr.go b/ocpp_v201/controllers/tariff_cost_ctrlr.go index 9e2ab04..8bfbb68 100644 --- a/ocpp_v201/controllers/tariff_cost_ctrlr.go +++ b/ocpp_v201/controllers/tariff_cost_ctrlr.go @@ -1,6 +1,10 @@ package controllers import ( + "errors" + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -37,65 +41,108 @@ func supportedVariablesTariffCostCtrlr() []variables.VariableName { } type TariffCostCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + instanceId string + validator *variableValidator } func (t *TariffCostCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameTariffCostCtrlr } func (t *TariffCostCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return t.instanceId } func (t *TariffCostCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") } func (t *TariffCostCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") } func (t *TariffCostCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + return []component.Component{} } -func (t *TariffCostCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (t *TariffCostCtrlr) GetRequiredVariables() []variables.VariableName { + t.mu.RLock() + defer t.mu.RUnlock() + + return t.requiredVariables } -func (t *TariffCostCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (t *TariffCostCtrlr) GetSupportedVariables() []variables.VariableName { + t.mu.RLock() + defer t.mu.RUnlock() + + return t.supportedVariables } func (t *TariffCostCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !t.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported", key) + } + + t.mu.RLock() + defer t.mu.RUnlock() + + variable, exists := t.variables[key] + if !exists { + return nil, errors.New("variable does not exist") + } + + return variable, nil } func (t *TariffCostCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !t.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported", variable) + } + + t.mu.Lock() + defer t.mu.Unlock() + if _, exists := t.variables[variable]; !exists { + return errors.New("variable does not exist") + } + + variableInstance := t.variables[variable] + err := variableInstance.UpdateVariableAttribute(attribute, value) + if err != nil { + return err + } + + t.variables[variable] = variableInstance + return nil } func (t *TariffCostCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + if !t.validator.IsVariableSupported(key) { + return false + } + + t.mu.RLock() + defer t.mu.RUnlock() + variable, exists := t.variables[key] + if !exists { + return false + } + + return variable.Validate() } func NewTariffCostCtrlr() *TariffCostCtrlr { - return &TariffCostCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &TariffCostCtrlr{ + mu: sync.RWMutex{}, + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredVariablesTariffCostCtrlr(), supportedVariables: supportedVariablesTariffCostCtrlr(), + instanceId: "tariff-cost-ctrlr", } + + ctrlr.validator = newVariableValidator(ctrlr) + + return ctrlr } diff --git a/ocpp_v201/controllers/tx_ctrlr.go b/ocpp_v201/controllers/tx_ctrlr.go index bdbfc0b..0d3a2a8 100644 --- a/ocpp_v201/controllers/tx_ctrlr.go +++ b/ocpp_v201/controllers/tx_ctrlr.go @@ -1,6 +1,9 @@ package controllers import ( + "fmt" + "sync" + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" ) @@ -17,9 +20,6 @@ const ( func requiredTxCtrlrVariables() []variables.VariableName { return []variables.VariableName{ - VariableNameEVConnectionTimeOut, - VariableNameStopTxOnEVSideDisconnect, - VariableNameStopTxOnInvalidId, VariableNameTxStartPoint, VariableNameTxStopPoint, } @@ -37,65 +37,102 @@ func supportedTxCtrlrVariables() []variables.VariableName { } type TxCtrlr struct { - variables map[variables.VariableName]variables.Variable + mu sync.RWMutex + variables map[variables.VariableName]*variables.Variable requiredVariables []variables.VariableName supportedVariables []variables.VariableName + validator *variableValidator + instanceId string } func (t *TxCtrlr) GetName() component.ComponentName { - //TODO implement me - panic("implement me") + return component.ComponentNameTxCtrlr } func (t *TxCtrlr) GetInstanceId() string { - //TODO implement me - panic("implement me") + return t.instanceId } func (t *TxCtrlr) RegisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (t *TxCtrlr) UnregisterSubComponent(component component.Component) { - //TODO implement me - panic("implement me") + // No-op: controllers do not support sub-components } func (t *TxCtrlr) GetSubComponents() []component.Component { - //TODO implement me - panic("implement me") + return []component.Component{} } -func (t *TxCtrlr) GetRequiredVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (t *TxCtrlr) GetRequiredVariables() []variables.VariableName { + t.mu.RLock() + defer t.mu.RUnlock() + + return t.requiredVariables } -func (t *TxCtrlr) GetSupportedVariables() []variables.Variable { - //TODO implement me - panic("implement me") +func (t *TxCtrlr) GetSupportedVariables() []variables.VariableName { + t.mu.RLock() + defer t.mu.RUnlock() + + return t.supportedVariables } func (t *TxCtrlr) GetVariable(key variables.VariableName, opts ...component.GetSetVariableOption) (*variables.Variable, error) { - //TODO implement me - panic("implement me") + if !t.validator.IsVariableSupported(key) { + return nil, fmt.Errorf("variable %s is not supported by this controller", key) + } + + t.mu.RLock() + defer t.mu.RUnlock() + + variable, exists := t.variables[key] + if !exists { + return nil, fmt.Errorf("variable %s not found", key) + } + return variable, nil } func (t *TxCtrlr) UpdateVariable(variable variables.VariableName, attribute string, value interface{}, opts ...component.GetSetVariableOption) error { - //TODO implement me - panic("implement me") + if !t.validator.IsVariableSupported(variable) { + return fmt.Errorf("variable %s is not supported by this controller", variable) + } + + t.mu.Lock() + defer t.mu.Unlock() + + v, exists := t.variables[variable] + if !exists { + return fmt.Errorf("variable %s not found", variable) + } + + return v.UpdateVariableAttribute(attribute, value) } func (t *TxCtrlr) Validate(key variables.VariableName) bool { - //TODO implement me - panic("implement me") + + if !t.validator.IsVariableSupported(key) { + return false + } + t.mu.RLock() + defer t.mu.RUnlock() + + v, exists := t.variables[key] + if !exists { + return false + } + + return v.Validate() } func NewTxCtrlr() *TxCtrlr { - return &TxCtrlr{ - variables: make(map[variables.VariableName]variables.Variable), + ctrlr := &TxCtrlr{ + variables: make(map[variables.VariableName]*variables.Variable), requiredVariables: requiredTxCtrlrVariables(), supportedVariables: supportedTxCtrlrVariables(), + instanceId: "tx-ctrlr", } + ctrlr.validator = newVariableValidator(ctrlr) + return ctrlr } diff --git a/ocpp_v201/controllers/tx_ctrlr_test.go b/ocpp_v201/controllers/tx_ctrlr_test.go index 2d32936..4261dca 100644 --- a/ocpp_v201/controllers/tx_ctrlr_test.go +++ b/ocpp_v201/controllers/tx_ctrlr_test.go @@ -1 +1,269 @@ package controllers + +import ( + "testing" + + "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + "github.com/ChargePi/ocpp-manager/ocpp_v201/variables" + "github.com/stretchr/testify/suite" +) + +type TxCtrlrTestSuite struct { + suite.Suite + ctrlr *TxCtrlr +} + +func (suite *TxCtrlrTestSuite) SetupTest() { + suite.ctrlr = NewTxCtrlr() +} + +func (suite *TxCtrlrTestSuite) TestNewTxCtrlr() { + ctrlr := NewTxCtrlr() + + suite.NotNil(ctrlr) + suite.Equal(component.ComponentNameTxCtrlr, ctrlr.GetName()) + suite.Equal("tx-ctrlr", ctrlr.GetInstanceId()) + suite.NotEmpty(ctrlr.GetRequiredVariables()) + suite.NotEmpty(ctrlr.GetSupportedVariables()) +} + +func (suite *TxCtrlrTestSuite) TestGetName() { + suite.Equal(component.ComponentNameTxCtrlr, suite.ctrlr.GetName()) +} + +func (suite *TxCtrlrTestSuite) TestGetInstanceId() { + suite.Equal("tx-ctrlr", suite.ctrlr.GetInstanceId()) +} + +func (suite *TxCtrlrTestSuite) TestSubComponentMethods() { + // Test that sub-component methods are no-op + initialCount := len(suite.ctrlr.GetSubComponents()) + + // Register should be no-op + suite.ctrlr.RegisterSubComponent(nil) + suite.Equal(initialCount, len(suite.ctrlr.GetSubComponents()), "RegisterSubComponent should be a no-op") + + // Unregister should be no-op + suite.ctrlr.UnregisterSubComponent(nil) + suite.Equal(initialCount, len(suite.ctrlr.GetSubComponents()), "UnregisterSubComponent should be a no-op") +} + +func (suite *TxCtrlrTestSuite) TestGetSubComponents() { + subComponents := suite.ctrlr.GetSubComponents() + suite.Equal(0, len(subComponents)) +} + +func (suite *TxCtrlrTestSuite) TestGetRequiredVariables() { + requiredVars := suite.ctrlr.GetRequiredVariables() + expectedVars := requiredTxCtrlrVariables() + suite.NotEmpty(requiredVars) + for _, expectedVar := range expectedVars { + suite.Contains(requiredVars, expectedVar) + } +} + +func (suite *TxCtrlrTestSuite) TestGetSupportedVariables() { + supportedVars := suite.ctrlr.GetSupportedVariables() + suite.NotEmpty(supportedVars) + + // Should include all required variables + requiredVars := suite.ctrlr.GetRequiredVariables() + for _, requiredVar := range requiredVars { + suite.Contains(supportedVars, requiredVar) + } +} + +func (suite *TxCtrlrTestSuite) TestGetVariable() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameTxStartPoint, + variables.VariableTypeString, + "EVConnected", + ) + suite.ctrlr.variables[VariableNameTxStartPoint] = *testVar + + tests := []struct { + name string + variableName variables.VariableName + expectError bool + errorContains string + }{ + { + name: "existing variable", + variableName: VariableNameTxStartPoint, + expectError: false, + }, + { + name: "non-existent variable", + variableName: VariableNameDateTime, + expectError: true, + errorContains: "variable DateTime is not supported by this controller", + }, + { + name: "unsupported variable", + variableName: "UnsupportedVariable", + expectError: true, + errorContains: "variable UnsupportedVariable is not supported", + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + result, err := suite.ctrlr.GetVariable(tt.variableName) + + if tt.expectError { + suite.Error(err) + if tt.errorContains != "" { + suite.Contains(err.Error(), tt.errorContains) + } + suite.Nil(result) + } else { + suite.NoError(err) + suite.NotNil(result) + suite.Equal(tt.variableName, result.Name) + } + }) + } +} + +func (suite *TxCtrlrTestSuite) TestUpdateVariable() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameTxStartPoint, + variables.VariableTypeString, + "EVConnected", + ) + suite.ctrlr.variables[VariableNameTxStartPoint] = *testVar + + tests := []struct { + name string + variableName variables.VariableName + attribute string + value interface{} + expectError bool + errorContains string + }{ + { + name: "update existing variable", + variableName: VariableNameTxStartPoint, + attribute: "value", + value: "Authorized", + expectError: false, + }, + { + name: "update non-existent variable", + variableName: "NonExistentVariable", + attribute: "value", + value: "Authorized", + expectError: true, + errorContains: "variable NonExistentVariable is not supported by this controller", + }, + { + name: "update unsupported variable", + variableName: "UnsupportedVariable", + attribute: "value", + value: "Authorized", + expectError: true, + errorContains: "not supported", + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + err := suite.ctrlr.UpdateVariable(tt.variableName, tt.attribute, tt.value) + + if tt.expectError { + suite.Error(err) + if tt.errorContains != "" { + suite.Contains(err.Error(), tt.errorContains) + } + } else { + suite.NoError(err) + + // Verify the update worked + variable, err := suite.ctrlr.GetVariable(tt.variableName) + suite.NoError(err) + suite.NotNil(variable) + } + }) + } +} + +func (suite *TxCtrlrTestSuite) TestValidate() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameTxStartPoint, + variables.VariableTypeString, + "EVConnected", + ) + suite.ctrlr.variables[VariableNameTxStartPoint] = *testVar + + tests := []struct { + name string + variableName variables.VariableName + expected bool + }{ + { + name: "valid existing variable", + variableName: VariableNameTxStartPoint, + expected: true, + }, + { + name: "non-existent variable", + variableName: "NonExistentVariable", + expected: false, + }, + { + name: "unsupported variable", + variableName: "UnsupportedVariable", + expected: false, + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + result := suite.ctrlr.Validate(tt.variableName) + suite.Equal(tt.expected, result) + }) + } +} + +func (suite *TxCtrlrTestSuite) TestThreadSafety() { + // Setup a test variable + testVar := variables.NewVariable( + VariableNameTxStartPoint, + variables.VariableTypeString, + "EVConnected", + ) + suite.ctrlr.variables[VariableNameTxStartPoint] = *testVar + + // Test concurrent access to the controller + done := make(chan bool, 10) + + for i := 0; i < 10; i++ { + go func() { + // Concurrent reads + _, _ = suite.ctrlr.GetVariable(VariableNameTxStartPoint) + _ = suite.ctrlr.Validate(VariableNameTxStartPoint) + _ = suite.ctrlr.GetRequiredVariables() + _ = suite.ctrlr.GetSupportedVariables() + + // Concurrent writes + _ = suite.ctrlr.UpdateVariable(VariableNameTxStartPoint, "value", "Authorized") + + done <- true + }() + } + + // Wait for all goroutines to complete + for i := 0; i < 10; i++ { + <-done + } + + // Verify the controller is still in a valid state + suite.True(suite.ctrlr.Validate(VariableNameTxStartPoint)) +} + +func TestTxCtrlrTestSuite(t *testing.T) { + suite.Run(t, new(TxCtrlrTestSuite)) +} diff --git a/ocpp_v201/ctrlr_manager.go b/ocpp_v201/ctrlr_manager.go index 1d827ad..6c86d62 100644 --- a/ocpp_v201/ctrlr_manager.go +++ b/ocpp_v201/ctrlr_manager.go @@ -25,41 +25,62 @@ func GetManager() *Manager { } type Manager struct { - controllers map[component.ComponentName]component.Component + components map[component.ComponentName]component.Component } type ManagerOption func(*managerOptions) type managerOptions struct { - // Supported profiles + controllers + // Supported profiles + components + components []component.Component } -func NewManager() *Manager { - return &Manager{ - controllers: map[component.ComponentName]component.Component{ - component.ComponentNameMonitoringCtrlr: controllers.NewMonitoringCtrlr(), - component.ComponentNameLocalAuthListCtrlr: controllers.NewLocalAuthListCtrlr(), - component.ComponentNameReservationCtrlr: controllers.NewReservationCtrlr(), - component.ComponentNameSmartChargingCtrlr: controllers.NewSmartChargingCtrlr(), - component.ComponentNameTxCtrlr: controllers.NewTxCtrlr(), - component.ComponentNameSecurityCtrlr: controllers.NewSecurityCtrlr(), - component.ComponentNameClockCtrlr: controllers.NewClockCtrlr(), - component.ComponentNameDeviceDataCtrlr: controllers.NewDeviceDataCtrlr(), - component.ComponentNameAuthCtrlr: controllers.NewAuthCtrlr(), - component.ComponentNameAuthCacheCtrlr: controllers.NewAuthCacheCtrlr(), - component.ComponentNameISO15118Ctrlr: controllers.NewISO15118Ctrlr(), - component.ComponentNameDisplayMessageCtrlr: controllers.NewDisplayCtrlr(), - component.ComponentNameOCPPCommCtrlr: controllers.NewOCPPCommCtrlr(), - component.ComponentNameAlignedDataCtrlr: controllers.NewAlignedDataCtrlr(), - component.ComponentNameSampledDataCtrlr: controllers.NewSampledDataCtrlr(), - component.ComponentNameTariffCostCtrlr: controllers.NewTariffCostCtrlr(), - component.ComponentNameCustomizationCtrlr: controllers.NewCustomizationCtrlr(), +func WithComponents(components []component.Component) ManagerOption { + return func(opts *managerOptions) { + opts.components = components + } +} + +func NewManager(opts ...ManagerOption) *Manager { + manager := &Manager{ + components: make(map[component.ComponentName]component.Component), + } + + defaults := managerOptions{ + components: []component.Component{ + controllers.NewMonitoringCtrlr(), + controllers.NewLocalAuthListCtrlr(), + controllers.NewReservationCtrlr(), + controllers.NewSmartChargingCtrlr(), + controllers.NewTxCtrlr(), + controllers.NewSecurityCtrlr(), + controllers.NewClockCtrlr(), + controllers.NewDeviceDataCtrlr(), + controllers.NewAuthCtrlr(), + controllers.NewAuthCacheCtrlr(), + controllers.NewISO15118Ctrlr(), + controllers.NewDisplayCtrlr(), + controllers.NewOCPPCommCtrlr(), + controllers.NewAlignedDataCtrlr(), + controllers.NewSampledDataCtrlr(), + controllers.NewTariffCostCtrlr(), + controllers.NewCustomizationCtrlr(), }, } + for _, opt := range opts { + opt(&defaults) + } + + // Register components + for _, component := range defaults.components { + manager.components[component.GetName()] = component + } + + return manager } func (m *Manager) UpdateVariable(name component.ComponentName, variableName variables.VariableName, attributeName string, attributeValue interface{}) error { - controller, ok := m.controllers[name] + controller, ok := m.components[name] if !ok { return errors.New("controller not found") } @@ -68,7 +89,7 @@ func (m *Manager) UpdateVariable(name component.ComponentName, variableName vari } func (m *Manager) GetVariable(name component.ComponentName, variableName variables.VariableName) (*variables.Variable, error) { - controller, ok := m.controllers[name] + controller, ok := m.components[name] if !ok { return nil, errors.New("controller not found") } diff --git a/ocpp_v201/evse.go b/ocpp_v201/evse.go new file mode 100644 index 0000000..abf4185 --- /dev/null +++ b/ocpp_v201/evse.go @@ -0,0 +1,8 @@ +package ocpp_v201 + +import "github.com/ChargePi/ocpp-manager/ocpp_v201/component" + +type EVSE struct { + ID int + components map[component.ComponentName]component.Component +}