diff --git a/lib/everest/ocpp/include/ocpp/v16/charge_point.hpp b/lib/everest/ocpp/include/ocpp/v16/charge_point.hpp index bfa4d487d6..ee1e839c45 100644 --- a/lib/everest/ocpp/include/ocpp/v16/charge_point.hpp +++ b/lib/everest/ocpp/include/ocpp/v16/charge_point.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -78,6 +79,26 @@ class ChargePoint { const std::shared_ptr evse_security, const std::optional security_configuration = std::nullopt); + /// \brief The main entrypoint for libOCPP for OCPP 1.6 + /// \param config unique pointer to a configuration object + /// \param database_path this points to the location of the sqlite database that libocpp uses to keep track of + /// connector availability, the authorization cache and auth list, charging profiles and transaction data + /// \param sql_init_path this points to the init.sql file which contains the database schema used by libocpp for its + /// sqlite database + /// \param message_log_path this points to the directory in which libocpp can put OCPP communication logfiles for + /// debugging purposes. This behavior can be controlled by the "LogMessages" (set to true by default) and + /// "LogMessagesFormat" (set to ["log", "html", "session_logging"] by default, "console" and "console_detailed" are + /// also available) configuration keys in the "Internal" section of the config file. Please note that this is + /// intended for debugging purposes only as it logs all communication, including authentication messages. + /// \param evse_security Pointer to evse_security that manages security related operations; if nullptr + /// security_configuration must be set + /// \param security_configuration specifies the file paths that are required to set up the internal evse_security + /// implementation + explicit ChargePoint(std::unique_ptr config, const fs::path& database_path, + const fs::path& sql_init_path, const fs::path& message_log_path, + const std::shared_ptr evse_security, + const std::optional security_configuration = std::nullopt); + ~ChargePoint(); /// @} // End constructors 1.6 group diff --git a/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration.hpp b/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration.hpp index 3596da67bb..8219455c04 100644 --- a/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration.hpp +++ b/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration.hpp @@ -3,11 +3,14 @@ #ifndef OCPP_V16_CHARGE_POINT_CONFIGURATION_HPP #define OCPP_V16_CHARGE_POINT_CONFIGURATION_HPP +#include #include #include #include #include +#include +#include #include #include @@ -15,7 +18,7 @@ namespace ocpp { namespace v16 { /// \brief contains the configuration of the charge point -class ChargePointConfiguration { +class ChargePointConfiguration : private ChargePointConfigurationBase, public ChargePointConfigurationInterface { private: json config; json custom_schema; @@ -23,22 +26,15 @@ class ChargePointConfiguration { bool core_schema_unlock_connector_on_ev_side_disconnect_ro_value; fs::path user_config_path; - std::set supported_feature_profiles; - std::map> supported_measurands; - std::map> supported_message_types_from_charge_point; - std::map> supported_message_types_from_central_system; - std::set supported_message_types_sending; - std::set supported_message_types_receiving; std::recursive_mutex configuration_mutex; - std::vector csv_to_measurand_with_phase_vector(std::string csv); + std::vector csv_to_measurand_with_phase_vector(const std::string& csv); bool validate_measurands(const json& config); - bool measurands_supported(std::string csv); + bool measurands_supported(const std::string& csv); json get_user_config(); - void setInUserConfig(std::string profile, std::string key, json value); - void init_supported_measurands(); + void setInUserConfig(const std::string& profile, const std::string& key, json value); - bool isConnectorPhaseRotationValid(std::string str); + bool isConnectorPhaseRotationValid(const std::string& str); void setChargepointInformationProperty(json& user_config, const std::string& key, const std::optional& value); @@ -49,489 +45,487 @@ class ChargePointConfiguration { void setChargepointInformation(const std::string& chargePointVendor, const std::string& chargePointModel, const std::optional& chargePointSerialNumber, const std::optional& chargeBoxSerialNumber, - const std::optional& firmwareVersion); + const std::optional& firmwareVersion) override; void setChargepointModemInformation(const std::optional& ICCID, - const std::optional& IMSI); + const std::optional& IMSI) override; void setChargepointMeterInformation(const std::optional& meterSerialNumber, - const std::optional& meterType); + const std::optional& meterType) override; // Internal config options - std::string getChargePointId(); - KeyValue getChargePointIdKeyValue(); - std::string getCentralSystemURI(); - void setCentralSystemURI(std::string ocpp_uri); - KeyValue getCentralSystemURIKeyValue(); - std::string getChargeBoxSerialNumber(); - KeyValue getChargeBoxSerialNumberKeyValue(); - CiString<20> getChargePointModel(); - KeyValue getChargePointModelKeyValue(); - std::optional> getChargePointSerialNumber(); - std::optional getChargePointSerialNumberKeyValue(); - CiString<20> getChargePointVendor(); - KeyValue getChargePointVendorKeyValue(); - CiString<50> getFirmwareVersion(); - KeyValue getFirmwareVersionKeyValue(); - std::optional> getICCID(); - std::optional getICCIDKeyValue(); - std::optional> getIMSI(); - std::optional getIMSIKeyValue(); - std::optional> getMeterSerialNumber(); - std::optional getMeterSerialNumberKeyValue(); - std::optional> getMeterType(); - std::optional getMeterTypeKeyValue(); - std::int32_t getWebsocketReconnectInterval(); - KeyValue getWebsocketReconnectIntervalKeyValue(); - bool getAuthorizeConnectorZeroOnConnectorOne(); - KeyValue getAuthorizeConnectorZeroOnConnectorOneKeyValue(); - bool getLogMessages(); - KeyValue getLogMessagesKeyValue(); - bool getLogMessagesRaw(); - KeyValue getLogMessagesRawKeyValue(); - std::vector getLogMessagesFormat(); - KeyValue getLogMessagesFormatKeyValue(); - bool getLogRotation(); - KeyValue getLogRotationKeyValue(); - bool getLogRotationDateSuffix(); - KeyValue getLogRotationDateSuffixKeyValue(); - std::uint64_t getLogRotationMaximumFileSize(); - KeyValue getLogRotationMaximumFileSizeKeyValue(); - std::uint64_t getLogRotationMaximumFileCount(); - KeyValue getLogRotationMaximumFileCountKeyValue(); - std::vector getSupportedChargingProfilePurposeTypes(); - KeyValue getSupportedChargingProfilePurposeTypesKeyValue(); - std::vector getIgnoredProfilePurposesOffline(); - std::optional getIgnoredProfilePurposesOfflineKeyValue(); - bool setIgnoredProfilePurposesOffline(const std::string& ignored_profile_purposes_offline); - std::int32_t getMaxCompositeScheduleDuration(); - KeyValue getMaxCompositeScheduleDurationKeyValue(); - std::optional getCompositeScheduleDefaultLimitAmps(); - std::optional getCompositeScheduleDefaultLimitAmpsKeyValue(); - void setCompositeScheduleDefaultLimitAmps(std::int32_t limit_amps); - std::optional getCompositeScheduleDefaultLimitWatts(); - std::optional getCompositeScheduleDefaultLimitWattsKeyValue(); - void setCompositeScheduleDefaultLimitWatts(std::int32_t limit_watts); - std::optional getCompositeScheduleDefaultNumberPhases(); - std::optional getCompositeScheduleDefaultNumberPhasesKeyValue(); - void setCompositeScheduleDefaultNumberPhases(std::int32_t number_phases); - std::optional getSupplyVoltage(); - std::optional getSupplyVoltageKeyValue(); - void setSupplyVoltage(std::int32_t supply_voltage); - std::string getSupportedCiphers12(); - KeyValue getSupportedCiphers12KeyValue(); - std::string getSupportedCiphers13(); - KeyValue getSupportedCiphers13KeyValue(); - bool getUseSslDefaultVerifyPaths(); - KeyValue getUseSslDefaultVerifyPathsKeyValue(); - bool getVerifyCsmsCommonName(); - KeyValue getVerifyCsmsCommonNameKeyValue(); - bool getVerifyCsmsAllowWildcards(); - void setVerifyCsmsAllowWildcards(bool verify_csms_allow_wildcards); - KeyValue getVerifyCsmsAllowWildcardsKeyValue(); - bool getUseTPM(); - bool getUseTPMSeccLeafCertificate(); - std::string getSupportedMeasurands(); - KeyValue getSupportedMeasurandsKeyValue(); - int getMaxMessageSize(); - KeyValue getMaxMessageSizeKeyValue(); - - bool getEnableTLSKeylog(); - std::string getTLSKeylogFile(); - - bool getStopTransactionIfUnlockNotSupported(); - void setStopTransactionIfUnlockNotSupported(bool stop_transaction_if_unlock_not_supported); - KeyValue getStopTransactionIfUnlockNotSupportedKeyValue(); - - std::int32_t getRetryBackoffRandomRange(); - void setRetryBackoffRandomRange(std::int32_t retry_backoff_random_range); - KeyValue getRetryBackoffRandomRangeKeyValue(); - - std::int32_t getRetryBackoffRepeatTimes(); - void setRetryBackoffRepeatTimes(std::int32_t retry_backoff_repeat_times); - KeyValue getRetryBackoffRepeatTimesKeyValue(); - - std::int32_t getRetryBackoffWaitMinimum(); - void setRetryBackoffWaitMinimum(std::int32_t retry_backoff_wait_minimum); - KeyValue getRetryBackoffWaitMinimumKeyValue(); - - std::set getSupportedMessageTypesSending(); - std::set getSupportedMessageTypesReceiving(); - - std::string getWebsocketPingPayload(); - KeyValue getWebsocketPingPayloadKeyValue(); - - std::int32_t getWebsocketPongTimeout(); - KeyValue getWebsocketPongTimeoutKeyValue(); - - std::optional getHostName(); - std::optional getHostNameKeyValue(); - - std::optional getIFace(); - std::optional getIFaceKeyValue(); - - std::optional getQueueAllMessages(); - std::optional getQueueAllMessagesKeyValue(); - - std::optional getMessageTypesDiscardForQueueing(); - std::optional getMessageTypesDiscardForQueueingKeyValue(); - - std::optional getMessageQueueSizeThreshold(); - std::optional getMessageQueueSizeThresholdKeyValue(); + std::string getChargePointId() override; + KeyValue getChargePointIdKeyValue() override; + std::string getCentralSystemURI() override; + void setCentralSystemURI(const std::string& ocpp_uri) override; + KeyValue getCentralSystemURIKeyValue() override; + std::string getChargeBoxSerialNumber() override; + KeyValue getChargeBoxSerialNumberKeyValue() override; + CiString<20> getChargePointModel() override; + KeyValue getChargePointModelKeyValue() override; + std::optional> getChargePointSerialNumber() override; + std::optional getChargePointSerialNumberKeyValue() override; + CiString<20> getChargePointVendor() override; + KeyValue getChargePointVendorKeyValue() override; + CiString<50> getFirmwareVersion() override; + KeyValue getFirmwareVersionKeyValue() override; + std::optional> getICCID() override; + std::optional getICCIDKeyValue() override; + std::optional> getIMSI() override; + std::optional getIMSIKeyValue() override; + std::optional> getMeterSerialNumber() override; + std::optional getMeterSerialNumberKeyValue() override; + std::optional> getMeterType() override; + std::optional getMeterTypeKeyValue() override; + bool getAuthorizeConnectorZeroOnConnectorOne() override; + KeyValue getAuthorizeConnectorZeroOnConnectorOneKeyValue() override; + bool getLogMessages() override; + KeyValue getLogMessagesKeyValue() override; + bool getLogMessagesRaw() override; + KeyValue getLogMessagesRawKeyValue() override; + std::vector getLogMessagesFormat() override; + KeyValue getLogMessagesFormatKeyValue() override; + bool getLogRotation() override; + KeyValue getLogRotationKeyValue() override; + bool getLogRotationDateSuffix() override; + KeyValue getLogRotationDateSuffixKeyValue() override; + uint64_t getLogRotationMaximumFileSize() override; + KeyValue getLogRotationMaximumFileSizeKeyValue() override; + uint64_t getLogRotationMaximumFileCount() override; + KeyValue getLogRotationMaximumFileCountKeyValue() override; + std::vector getSupportedChargingProfilePurposeTypes() override; + KeyValue getSupportedChargingProfilePurposeTypesKeyValue() override; + std::vector getIgnoredProfilePurposesOffline() override; + std::optional getIgnoredProfilePurposesOfflineKeyValue() override; + bool setIgnoredProfilePurposesOffline(const std::string& ignored_profile_purposes_offline) override; + std::int32_t getMaxCompositeScheduleDuration() override; + KeyValue getMaxCompositeScheduleDurationKeyValue() override; + std::optional getCompositeScheduleDefaultLimitAmps() override; + std::optional getCompositeScheduleDefaultLimitAmpsKeyValue() override; + void setCompositeScheduleDefaultLimitAmps(std::int32_t limit_amps) override; + std::optional getCompositeScheduleDefaultLimitWatts() override; + std::optional getCompositeScheduleDefaultLimitWattsKeyValue() override; + void setCompositeScheduleDefaultLimitWatts(std::int32_t limit_watts) override; + std::optional getCompositeScheduleDefaultNumberPhases() override; + std::optional getCompositeScheduleDefaultNumberPhasesKeyValue() override; + void setCompositeScheduleDefaultNumberPhases(std::int32_t number_phases) override; + std::optional getSupplyVoltage() override; + std::optional getSupplyVoltageKeyValue() override; + void setSupplyVoltage(std::int32_t supply_voltage) override; + std::string getSupportedCiphers12() override; + KeyValue getSupportedCiphers12KeyValue() override; + std::string getSupportedCiphers13() override; + KeyValue getSupportedCiphers13KeyValue() override; + bool getUseSslDefaultVerifyPaths() override; + KeyValue getUseSslDefaultVerifyPathsKeyValue() override; + bool getVerifyCsmsCommonName() override; + KeyValue getVerifyCsmsCommonNameKeyValue() override; + bool getVerifyCsmsAllowWildcards() override; + void setVerifyCsmsAllowWildcards(bool verify_csms_allow_wildcards) override; + KeyValue getVerifyCsmsAllowWildcardsKeyValue() override; + bool getUseTPM() override; + bool getUseTPMSeccLeafCertificate() override; + std::string getSupportedMeasurands() override; + KeyValue getSupportedMeasurandsKeyValue() override; + int getMaxMessageSize() override; + KeyValue getMaxMessageSizeKeyValue() override; + + bool getEnableTLSKeylog() override; + std::string getTLSKeylogFile() override; + + bool getStopTransactionIfUnlockNotSupported() override; + void setStopTransactionIfUnlockNotSupported(bool stop_transaction_if_unlock_not_supported) override; + KeyValue getStopTransactionIfUnlockNotSupportedKeyValue() override; + + std::int32_t getRetryBackoffRandomRange() override; + void setRetryBackoffRandomRange(std::int32_t retry_backoff_random_range) override; + KeyValue getRetryBackoffRandomRangeKeyValue() override; + + std::int32_t getRetryBackoffRepeatTimes() override; + void setRetryBackoffRepeatTimes(std::int32_t retry_backoff_repeat_times) override; + KeyValue getRetryBackoffRepeatTimesKeyValue() override; + + std::int32_t getRetryBackoffWaitMinimum() override; + void setRetryBackoffWaitMinimum(std::int32_t retry_backoff_wait_minimum) override; + KeyValue getRetryBackoffWaitMinimumKeyValue() override; + + std::set getSupportedMessageTypesSending() override; + std::set getSupportedMessageTypesReceiving() override; + + std::string getWebsocketPingPayload() override; + KeyValue getWebsocketPingPayloadKeyValue() override; + + std::int32_t getWebsocketPongTimeout() override; + KeyValue getWebsocketPongTimeoutKeyValue() override; + + std::optional getHostName() override; + std::optional getHostNameKeyValue() override; + + std::optional getIFace() override; + std::optional getIFaceKeyValue() override; + + std::optional getQueueAllMessages() override; + std::optional getQueueAllMessagesKeyValue() override; + + std::optional getMessageTypesDiscardForQueueing() override; + std::optional getMessageTypesDiscardForQueueingKeyValue() override; + + std::optional getMessageQueueSizeThreshold() override; + std::optional getMessageQueueSizeThresholdKeyValue() override; // Core Profile - optional - std::optional getAllowOfflineTxForUnknownId(); - void setAllowOfflineTxForUnknownId(bool enabled); - std::optional getAllowOfflineTxForUnknownIdKeyValue(); + std::optional getAllowOfflineTxForUnknownId() override; + void setAllowOfflineTxForUnknownId(bool enabled) override; + std::optional getAllowOfflineTxForUnknownIdKeyValue() override; // Core Profile - optional - std::optional getAuthorizationCacheEnabled(); - void setAuthorizationCacheEnabled(bool enabled); - std::optional getAuthorizationCacheEnabledKeyValue(); + std::optional getAuthorizationCacheEnabled() override; + void setAuthorizationCacheEnabled(bool enabled) override; + std::optional getAuthorizationCacheEnabledKeyValue() override; // Core Profile - bool getAuthorizeRemoteTxRequests(); - void setAuthorizeRemoteTxRequests(bool enabled); - KeyValue getAuthorizeRemoteTxRequestsKeyValue(); + bool getAuthorizeRemoteTxRequests() override; + void setAuthorizeRemoteTxRequests(bool enabled) override; + KeyValue getAuthorizeRemoteTxRequestsKeyValue() override; // Core Profile - optional - std::optional getBlinkRepeat(); - void setBlinkRepeat(std::int32_t blink_repeat); - std::optional getBlinkRepeatKeyValue(); + std::optional getBlinkRepeat() override; + void setBlinkRepeat(std::int32_t blink_repeat) override; + std::optional getBlinkRepeatKeyValue() override; // Core Profile - std::int32_t getClockAlignedDataInterval(); - void setClockAlignedDataInterval(std::int32_t interval); - KeyValue getClockAlignedDataIntervalKeyValue(); + std::int32_t getClockAlignedDataInterval() override; + void setClockAlignedDataInterval(std::int32_t interval) override; + KeyValue getClockAlignedDataIntervalKeyValue() override; // Core Profile - std::int32_t getConnectionTimeOut(); - void setConnectionTimeOut(std::int32_t timeout); - KeyValue getConnectionTimeOutKeyValue(); + std::int32_t getConnectionTimeOut() override; + void setConnectionTimeOut(std::int32_t timeout) override; + KeyValue getConnectionTimeOutKeyValue() override; // Core Profile - std::string getConnectorPhaseRotation(); - void setConnectorPhaseRotation(std::string connector_phase_rotation); - KeyValue getConnectorPhaseRotationKeyValue(); + std::string getConnectorPhaseRotation() override; + void setConnectorPhaseRotation(const std::string& connector_phase_rotation) override; + KeyValue getConnectorPhaseRotationKeyValue() override; // Core Profile - optional - std::optional getConnectorPhaseRotationMaxLength(); - std::optional getConnectorPhaseRotationMaxLengthKeyValue(); + std::optional getConnectorPhaseRotationMaxLength() override; + std::optional getConnectorPhaseRotationMaxLengthKeyValue() override; // Core Profile - std::int32_t getGetConfigurationMaxKeys(); - KeyValue getGetConfigurationMaxKeysKeyValue(); + std::int32_t getGetConfigurationMaxKeys() override; + KeyValue getGetConfigurationMaxKeysKeyValue() override; // Core Profile - std::int32_t getHeartbeatInterval(); - void setHeartbeatInterval(std::int32_t interval); - KeyValue getHeartbeatIntervalKeyValue(); + std::int32_t getHeartbeatInterval() override; + void setHeartbeatInterval(std::int32_t interval) override; + KeyValue getHeartbeatIntervalKeyValue() override; // Core Profile - optional - std::optional getLightIntensity(); - void setLightIntensity(std::int32_t light_intensity); - std::optional getLightIntensityKeyValue(); + std::optional getLightIntensity() override; + void setLightIntensity(std::int32_t light_intensity) override; + std::optional getLightIntensityKeyValue() override; // Core Profile - bool getLocalAuthorizeOffline(); - void setLocalAuthorizeOffline(bool local_authorize_offline); - KeyValue getLocalAuthorizeOfflineKeyValue(); + bool getLocalAuthorizeOffline() override; + void setLocalAuthorizeOffline(bool local_authorize_offline) override; + KeyValue getLocalAuthorizeOfflineKeyValue() override; // Core Profile - bool getLocalPreAuthorize(); - void setLocalPreAuthorize(bool local_pre_authorize); - KeyValue getLocalPreAuthorizeKeyValue(); + bool getLocalPreAuthorize() override; + void setLocalPreAuthorize(bool local_pre_authorize) override; + KeyValue getLocalPreAuthorizeKeyValue() override; // Core Profile - optional - std::optional getMaxEnergyOnInvalidId(); - void setMaxEnergyOnInvalidId(std::int32_t max_energy); - std::optional getMaxEnergyOnInvalidIdKeyValue(); + std::optional getMaxEnergyOnInvalidId() override; + void setMaxEnergyOnInvalidId(std::int32_t max_energy) override; + std::optional getMaxEnergyOnInvalidIdKeyValue() override; // Core Profile - std::string getMeterValuesAlignedData(); - bool setMeterValuesAlignedData(std::string meter_values_aligned_data); - KeyValue getMeterValuesAlignedDataKeyValue(); - std::vector getMeterValuesAlignedDataVector(); + std::string getMeterValuesAlignedData() override; + bool setMeterValuesAlignedData(const std::string& meter_values_aligned_data) override; + KeyValue getMeterValuesAlignedDataKeyValue() override; + std::vector getMeterValuesAlignedDataVector() override; // Core Profile - optional - std::optional getMeterValuesAlignedDataMaxLength(); - std::optional getMeterValuesAlignedDataMaxLengthKeyValue(); + std::optional getMeterValuesAlignedDataMaxLength() override; + std::optional getMeterValuesAlignedDataMaxLengthKeyValue() override; // Core Profile - std::string getMeterValuesSampledData(); - bool setMeterValuesSampledData(std::string meter_values_sampled_data); - KeyValue getMeterValuesSampledDataKeyValue(); - std::vector getMeterValuesSampledDataVector(); + std::string getMeterValuesSampledData() override; + bool setMeterValuesSampledData(const std::string& meter_values_sampled_data) override; + KeyValue getMeterValuesSampledDataKeyValue() override; + std::vector getMeterValuesSampledDataVector() override; // Core Profile - optional - std::optional getMeterValuesSampledDataMaxLength(); - std::optional getMeterValuesSampledDataMaxLengthKeyValue(); + std::optional getMeterValuesSampledDataMaxLength() override; + std::optional getMeterValuesSampledDataMaxLengthKeyValue() override; // Core Profile - std::int32_t getMeterValueSampleInterval(); - void setMeterValueSampleInterval(std::int32_t interval); - KeyValue getMeterValueSampleIntervalKeyValue(); + std::int32_t getMeterValueSampleInterval() override; + void setMeterValueSampleInterval(std::int32_t interval) override; + KeyValue getMeterValueSampleIntervalKeyValue() override; // Core Profile - optional - std::optional getMinimumStatusDuration(); - void setMinimumStatusDuration(std::int32_t minimum_status_duration); - std::optional getMinimumStatusDurationKeyValue(); + std::optional getMinimumStatusDuration() override; + void setMinimumStatusDuration(std::int32_t minimum_status_duration) override; + std::optional getMinimumStatusDurationKeyValue() override; // Core Profile - std::int32_t getNumberOfConnectors(); - KeyValue getNumberOfConnectorsKeyValue(); + std::int32_t getNumberOfConnectors() override; + KeyValue getNumberOfConnectorsKeyValue() override; // Reservation Profile - std::optional getReserveConnectorZeroSupported(); - std::optional getReserveConnectorZeroSupportedKeyValue(); + std::optional getReserveConnectorZeroSupported() override; + std::optional getReserveConnectorZeroSupportedKeyValue() override; // Core Profile - std::int32_t getResetRetries(); - void setResetRetries(std::int32_t retries); - KeyValue getResetRetriesKeyValue(); + std::int32_t getResetRetries() override; + void setResetRetries(std::int32_t retries) override; + KeyValue getResetRetriesKeyValue() override; // Core Profile - optional - std::optional getStopTransactionOnEVSideDisconnect(); - std::optional getStopTransactionOnEVSideDisconnectKeyValue(); + std::optional getStopTransactionOnEVSideDisconnect() override; + std::optional getStopTransactionOnEVSideDisconnectKeyValue() override; // Core Profile - bool getStopTransactionOnInvalidId(); - void setStopTransactionOnInvalidId(bool stop_transaction_on_invalid_id); - KeyValue getStopTransactionOnInvalidIdKeyValue(); + bool getStopTransactionOnInvalidId() override; + void setStopTransactionOnInvalidId(bool stop_transaction_on_invalid_id) override; + KeyValue getStopTransactionOnInvalidIdKeyValue() override; // Core Profile - std::string getStopTxnAlignedData(); - bool setStopTxnAlignedData(std::string stop_txn_aligned_data); - KeyValue getStopTxnAlignedDataKeyValue(); + std::string getStopTxnAlignedData() override; + bool setStopTxnAlignedData(const std::string& stop_txn_aligned_data) override; + KeyValue getStopTxnAlignedDataKeyValue() override; // Core Profile - optional - std::optional getStopTxnAlignedDataMaxLength(); - std::optional getStopTxnAlignedDataMaxLengthKeyValue(); + std::optional getStopTxnAlignedDataMaxLength() override; + std::optional getStopTxnAlignedDataMaxLengthKeyValue() override; // Core Profile - std::string getStopTxnSampledData(); - bool setStopTxnSampledData(std::string stop_txn_sampled_data); - KeyValue getStopTxnSampledDataKeyValue(); + std::string getStopTxnSampledData() override; + bool setStopTxnSampledData(const std::string& stop_txn_sampled_data) override; + KeyValue getStopTxnSampledDataKeyValue() override; // Core Profile - optional - std::optional getStopTxnSampledDataMaxLength(); - std::optional getStopTxnSampledDataMaxLengthKeyValue(); + std::optional getStopTxnSampledDataMaxLength() override; + std::optional getStopTxnSampledDataMaxLengthKeyValue() override; // Core Profile - std::string getSupportedFeatureProfiles(); - KeyValue getSupportedFeatureProfilesKeyValue(); - std::set getSupportedFeatureProfilesSet(); + std::string getSupportedFeatureProfiles() override; + KeyValue getSupportedFeatureProfilesKeyValue() override; + std::set getSupportedFeatureProfilesSet() override; // Core Profile - optional - std::optional getSupportedFeatureProfilesMaxLength(); - std::optional getSupportedFeatureProfilesMaxLengthKeyValue(); + std::optional getSupportedFeatureProfilesMaxLength() override; + std::optional getSupportedFeatureProfilesMaxLengthKeyValue() override; // Core Profile - std::int32_t getTransactionMessageAttempts(); - void setTransactionMessageAttempts(std::int32_t attempts); - KeyValue getTransactionMessageAttemptsKeyValue(); + std::int32_t getTransactionMessageAttempts() override; + void setTransactionMessageAttempts(std::int32_t attempts) override; + KeyValue getTransactionMessageAttemptsKeyValue() override; // Core Profile - std::int32_t getTransactionMessageRetryInterval(); - void setTransactionMessageRetryInterval(std::int32_t retry_interval); - KeyValue getTransactionMessageRetryIntervalKeyValue(); + std::int32_t getTransactionMessageRetryInterval() override; + void setTransactionMessageRetryInterval(std::int32_t retry_interval) override; + KeyValue getTransactionMessageRetryIntervalKeyValue() override; // Core Profile - bool getUnlockConnectorOnEVSideDisconnect(); - void setUnlockConnectorOnEVSideDisconnect(bool unlock_connector_on_ev_side_disconnect); - KeyValue getUnlockConnectorOnEVSideDisconnectKeyValue(); + bool getUnlockConnectorOnEVSideDisconnect() override; + void setUnlockConnectorOnEVSideDisconnect(bool unlock_connector_on_ev_side_disconnect) override; + KeyValue getUnlockConnectorOnEVSideDisconnectKeyValue() override; // Core Profile - optional - std::optional getWebsocketPingInterval(); - void setWebsocketPingInterval(std::int32_t websocket_ping_interval); - std::optional getWebsocketPingIntervalKeyValue(); + std::optional getWebsocketPingInterval() override; + void setWebsocketPingInterval(std::int32_t websocket_ping_interval) override; + std::optional getWebsocketPingIntervalKeyValue() override; // Core Profile end // Firmware Management Profile - std::optional getSupportedFileTransferProtocols(); - std::optional getSupportedFileTransferProtocolsKeyValue(); + std::optional getSupportedFileTransferProtocols() override; + std::optional getSupportedFileTransferProtocolsKeyValue() override; // SmartCharging Profile - std::int32_t getChargeProfileMaxStackLevel(); - KeyValue getChargeProfileMaxStackLevelKeyValue(); + std::int32_t getChargeProfileMaxStackLevel() override; + KeyValue getChargeProfileMaxStackLevelKeyValue() override; // SmartCharging Profile - std::string getChargingScheduleAllowedChargingRateUnit(); - KeyValue getChargingScheduleAllowedChargingRateUnitKeyValue(); - std::vector getChargingScheduleAllowedChargingRateUnitVector(); + std::string getChargingScheduleAllowedChargingRateUnit() override; + KeyValue getChargingScheduleAllowedChargingRateUnitKeyValue() override; + std::vector getChargingScheduleAllowedChargingRateUnitVector() override; // SmartCharging Profile - std::int32_t getChargingScheduleMaxPeriods(); - KeyValue getChargingScheduleMaxPeriodsKeyValue(); + std::int32_t getChargingScheduleMaxPeriods() override; + KeyValue getChargingScheduleMaxPeriodsKeyValue() override; // SmartCharging Profile - optional - std::optional getConnectorSwitch3to1PhaseSupported(); - std::optional getConnectorSwitch3to1PhaseSupportedKeyValue(); + std::optional getConnectorSwitch3to1PhaseSupported() override; + std::optional getConnectorSwitch3to1PhaseSupportedKeyValue() override; // SmartCharging Profile - std::int32_t getMaxChargingProfilesInstalled(); - KeyValue getMaxChargingProfilesInstalledKeyValue(); + std::int32_t getMaxChargingProfilesInstalled() override; + KeyValue getMaxChargingProfilesInstalledKeyValue() override; // SmartCharging Profile end // Security profile - optional - std::optional getAdditionalRootCertificateCheck(); - std::optional getAdditionalRootCertificateCheckKeyValue(); + std::optional getAdditionalRootCertificateCheck() override; + std::optional getAdditionalRootCertificateCheckKeyValue() override; // Security profile - optional - std::optional getAuthorizationKey(); - void setAuthorizationKey(std::string authorization_key); - std::optional getAuthorizationKeyKeyValue(); + std::optional getAuthorizationKey() override; + void setAuthorizationKey(const std::string& authorization_key) override; + std::optional getAuthorizationKeyKeyValue() override; // Security profile - optional - std::optional getCertificateSignedMaxChainSize(); - std::optional getCertificateSignedMaxChainSizeKeyValue(); + std::optional getCertificateSignedMaxChainSize() override; + std::optional getCertificateSignedMaxChainSizeKeyValue() override; // Security profile - optional - std::optional getCertificateStoreMaxLength(); - std::optional getCertificateStoreMaxLengthKeyValue(); + std::optional getCertificateStoreMaxLength() override; + std::optional getCertificateStoreMaxLengthKeyValue() override; // Security profile - optional - std::optional getCpoName(); - void setCpoName(std::string cpo_name); - std::optional getCpoNameKeyValue(); + std::optional getCpoName() override; + void setCpoName(const std::string& cpo_name) override; + std::optional getCpoNameKeyValue() override; // // Security profile - optional in ocpp but mandatory websocket connection - std::int32_t getSecurityProfile(); - void setSecurityProfile(std::int32_t security_profile); - KeyValue getSecurityProfileKeyValue(); + std::int32_t getSecurityProfile() override; + void setSecurityProfile(std::int32_t security_profile) override; + KeyValue getSecurityProfileKeyValue() override; // // Security profile - optional with default - bool getDisableSecurityEventNotifications(); - void setDisableSecurityEventNotifications(bool disable_security_event_notifications); - KeyValue getDisableSecurityEventNotificationsKeyValue(); + bool getDisableSecurityEventNotifications() override; + void setDisableSecurityEventNotifications(bool disable_security_event_notifications) override; + KeyValue getDisableSecurityEventNotificationsKeyValue() override; // Local Auth List Management Profile - bool getLocalAuthListEnabled(); - void setLocalAuthListEnabled(bool local_auth_list_enabled); - KeyValue getLocalAuthListEnabledKeyValue(); + bool getLocalAuthListEnabled() override; + void setLocalAuthListEnabled(bool local_auth_list_enabled) override; + KeyValue getLocalAuthListEnabledKeyValue() override; // Local Auth List Management Profile - std::int32_t getLocalAuthListMaxLength(); - KeyValue getLocalAuthListMaxLengthKeyValue(); + std::int32_t getLocalAuthListMaxLength() override; + KeyValue getLocalAuthListMaxLengthKeyValue() override; // Local Auth List Management Profile - std::int32_t getSendLocalListMaxLength(); - KeyValue getSendLocalListMaxLengthKeyValue(); + std::int32_t getSendLocalListMaxLength() override; + KeyValue getSendLocalListMaxLengthKeyValue() override; // PnC - bool getISO15118CertificateManagementEnabled(); - void setISO15118CertificateManagementEnabled(const bool iso15118_certificate_management_enabled); - KeyValue getISO15118CertificateManagementEnabledKeyValue(); + bool getISO15118CertificateManagementEnabled() override; + void setISO15118CertificateManagementEnabled(bool iso15118_certificate_management_enabled) override; + KeyValue getISO15118CertificateManagementEnabledKeyValue() override; - bool getISO15118PnCEnabled(); - void setISO15118PnCEnabled(const bool iso15118_pnc_enabled); - KeyValue getISO15118PnCEnabledKeyValue(); + bool getISO15118PnCEnabled() override; + void setISO15118PnCEnabled(bool iso15118_pnc_enabled) override; + KeyValue getISO15118PnCEnabledKeyValue() override; - std::optional getCentralContractValidationAllowed(); - void setCentralContractValidationAllowed(const bool central_contract_validation_allowed); - std::optional getCentralContractValidationAllowedKeyValue(); + std::optional getCentralContractValidationAllowed() override; + void setCentralContractValidationAllowed(bool central_contract_validation_allowed) override; + std::optional getCentralContractValidationAllowedKeyValue() override; - std::optional getCertSigningWaitMinimum(); - void setCertSigningWaitMinimum(const std::int32_t cert_signing_wait_minimum); - std::optional getCertSigningWaitMinimumKeyValue(); + std::optional getCertSigningWaitMinimum() override; + void setCertSigningWaitMinimum(std::int32_t cert_signing_wait_minimum) override; + std::optional getCertSigningWaitMinimumKeyValue() override; - std::optional getCertSigningRepeatTimes(); - void setCertSigningRepeatTimes(const std::int32_t cert_signing_repeat_times); - std::optional getCertSigningRepeatTimesKeyValue(); + std::optional getCertSigningRepeatTimes() override; + void setCertSigningRepeatTimes(std::int32_t cert_signing_repeat_times) override; + std::optional getCertSigningRepeatTimesKeyValue() override; - bool getContractValidationOffline(); - void setContractValidationOffline(const bool contract_validation_offline); - KeyValue getContractValidationOfflineKeyValue(); + bool getContractValidationOffline() override; + void setContractValidationOffline(bool contract_validation_offline) override; + KeyValue getContractValidationOfflineKeyValue() override; - std::int32_t getOcspRequestInterval(); - void setOcspRequestInterval(const std::int32_t ocsp_request_interval); - KeyValue getOcspRequestIntervalKeyValue(); + std::int32_t getOcspRequestInterval() override; + void setOcspRequestInterval(std::int32_t ocsp_request_interval) override; + KeyValue getOcspRequestIntervalKeyValue() override; - std::optional getSeccLeafSubjectCommonName(); - void setSeccLeafSubjectCommonName(const std::string& secc_leaf_subject_common_name); - std::optional getSeccLeafSubjectCommonNameKeyValue(); + std::optional getSeccLeafSubjectCommonName() override; + void setSeccLeafSubjectCommonName(const std::string& secc_leaf_subject_common_name) override; + std::optional getSeccLeafSubjectCommonNameKeyValue() override; - std::optional getSeccLeafSubjectCountry(); - void setSeccLeafSubjectCountry(const std::string& secc_leaf_subject_country); - std::optional getSeccLeafSubjectCountryKeyValue(); + std::optional getSeccLeafSubjectCountry() override; + void setSeccLeafSubjectCountry(const std::string& secc_leaf_subject_country) override; + std::optional getSeccLeafSubjectCountryKeyValue() override; - std::optional getSeccLeafSubjectOrganization(); - void setSeccLeafSubjectOrganization(const std::string& secc_leaf_subject_organization); - std::optional getSeccLeafSubjectOrganizationKeyValue(); + std::optional getSeccLeafSubjectOrganization() override; + void setSeccLeafSubjectOrganization(const std::string& secc_leaf_subject_organization) override; + std::optional getSeccLeafSubjectOrganizationKeyValue() override; - std::optional getConnectorEvseIds(); - void setConnectorEvseIds(const std::string& connector_evse_ids); - std::optional getConnectorEvseIdsKeyValue(); + std::optional getConnectorEvseIds() override; + void setConnectorEvseIds(const std::string& connector_evse_ids) override; + std::optional getConnectorEvseIdsKeyValue() override; - std::optional getAllowChargingProfileWithoutStartSchedule(); - void setAllowChargingProfileWithoutStartSchedule(const bool allow); - std::optional getAllowChargingProfileWithoutStartScheduleKeyValue(); + std::optional getAllowChargingProfileWithoutStartSchedule() override; + void setAllowChargingProfileWithoutStartSchedule(bool allow) override; + std::optional getAllowChargingProfileWithoutStartScheduleKeyValue() override; - std::int32_t getWaitForStopTransactionsOnResetTimeout(); - void setWaitForStopTransactionsOnResetTimeout(const std::int32_t wait_for_stop_transactions_on_reset_timeout); - KeyValue getWaitForStopTransactionsOnResetTimeoutKeyValue(); + std::int32_t getWaitForStopTransactionsOnResetTimeout() override; + void setWaitForStopTransactionsOnResetTimeout(std::int32_t wait_for_stop_transactions_on_reset_timeout) override; + KeyValue getWaitForStopTransactionsOnResetTimeoutKeyValue() override; // California Pricing Requirements - bool getCustomDisplayCostAndPriceEnabled(); - KeyValue getCustomDisplayCostAndPriceEnabledKeyValue(); + bool getCustomDisplayCostAndPriceEnabled() override; + KeyValue getCustomDisplayCostAndPriceEnabledKeyValue() override; - std::optional getPriceNumberOfDecimalsForCostValues(); - std::optional getPriceNumberOfDecimalsForCostValuesKeyValue(); + std::optional getPriceNumberOfDecimalsForCostValues() override; + std::optional getPriceNumberOfDecimalsForCostValuesKeyValue() override; - std::optional getDefaultPriceText(const std::string& language); - TariffMessage getDefaultTariffMessage(bool offline); - ConfigurationStatus setDefaultPriceText(const CiString<50>& key, const CiString<500>& value); - KeyValue getDefaultPriceTextKeyValue(const std::string& language); - std::optional> getAllDefaultPriceTextKeyValues(); + std::optional getDefaultPriceText(const std::string& language) override; + TariffMessage getDefaultTariffMessage(bool offline) override; + ConfigurationStatus setDefaultPriceText(const CiString<50>& key, const CiString<500>& value) override; + KeyValue getDefaultPriceTextKeyValue(const std::string& language) override; + std::optional> getAllDefaultPriceTextKeyValues() override; - std::optional getDefaultPrice(); - ConfigurationStatus setDefaultPrice(const std::string& value); - std::optional getDefaultPriceKeyValue(); + std::optional getDefaultPrice() override; + ConfigurationStatus setDefaultPrice(const std::string& value) override; + std::optional getDefaultPriceKeyValue() override; - std::optional getDisplayTimeOffset(); - ConfigurationStatus setDisplayTimeOffset(const std::string& offset); - std::optional getDisplayTimeOffsetKeyValue(); + std::optional getDisplayTimeOffset() override; + ConfigurationStatus setDisplayTimeOffset(const std::string& offset) override; + std::optional getDisplayTimeOffsetKeyValue() override; - std::optional getNextTimeOffsetTransitionDateTime(); - ConfigurationStatus setNextTimeOffsetTransitionDateTime(const std::string& date_time); - std::optional getNextTimeOffsetTransitionDateTimeKeyValue(); + std::optional getNextTimeOffsetTransitionDateTime() override; + ConfigurationStatus setNextTimeOffsetTransitionDateTime(const std::string& date_time) override; + std::optional getNextTimeOffsetTransitionDateTimeKeyValue() override; - std::optional getTimeOffsetNextTransition(); - ConfigurationStatus setTimeOffsetNextTransition(const std::string& offset); - std::optional getTimeOffsetNextTransitionKeyValue(); + std::optional getTimeOffsetNextTransition() override; + ConfigurationStatus setTimeOffsetNextTransition(const std::string& offset) override; + std::optional getTimeOffsetNextTransitionKeyValue() override; - std::optional getCustomIdleFeeAfterStop(); - void setCustomIdleFeeAfterStop(const bool& value); - std::optional getCustomIdleFeeAfterStopKeyValue(); + std::optional getCustomIdleFeeAfterStop() override; + void setCustomIdleFeeAfterStop(bool value) override; + std::optional getCustomIdleFeeAfterStopKeyValue() override; - std::optional getCustomMultiLanguageMessagesEnabled(); - std::optional getCustomMultiLanguageMessagesEnabledKeyValue(); + std::optional getCustomMultiLanguageMessagesEnabled() override; + std::optional getCustomMultiLanguageMessagesEnabledKeyValue() override; - std::optional getMultiLanguageSupportedLanguages(); - std::optional getMultiLanguageSupportedLanguagesKeyValue(); + std::optional getMultiLanguageSupportedLanguages() override; + std::optional getMultiLanguageSupportedLanguagesKeyValue() override; - std::optional getLanguage(); - void setLanguage(const std::string& language); - std::optional getLanguageKeyValue(); + std::optional getLanguage() override; + void setLanguage(const std::string& language) override; + std::optional getLanguageKeyValue() override; - std::optional getWaitForSetUserPriceTimeout(); - void setWaitForSetUserPriceTimeout(const std::int32_t wait_for_set_user_price_timeout); - std::optional getWaitForSetUserPriceTimeoutKeyValue(); + std::optional getWaitForSetUserPriceTimeout() override; + void setWaitForSetUserPriceTimeout(std::int32_t wait_for_set_user_price_timeout) override; + std::optional getWaitForSetUserPriceTimeoutKeyValue() override; // Signed Meter Values - std::optional getPublicKeyKeyValue(const uint32_t connector_id); - std::optional> getAllMeterPublicKeyKeyValues(); - bool setMeterPublicKey(const int32_t connector_id, const std::string& public_key_pem); + std::optional getPublicKeyKeyValue(std::uint32_t connector_id) override; + std::optional> getAllMeterPublicKeyKeyValues() override; + bool setMeterPublicKey(std::int32_t connector_id, const std::string& public_key_pem) override; // custom - std::optional getCustomKeyValue(CiString<50> key); - ConfigurationStatus setCustomKey(CiString<50> key, CiString<500> value, bool force); + std::optional getCustomKeyValue(const CiString<50>& key) override; + ConfigurationStatus setCustomKey(const CiString<50>& key, const CiString<500>& value, bool force) override; - std::optional get(CiString<50> key); + std::optional get(const CiString<50>& key) override; - std::vector get_all_key_value(); + std::vector get_all_key_value() override; - std::optional set(CiString<50> key, CiString<500> value); + std::optional set(const CiString<50>& key, const CiString<500>& value) override; }; } // namespace v16 diff --git a/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration_base.hpp b/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration_base.hpp new file mode 100644 index 0000000000..83beb61396 --- /dev/null +++ b/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration_base.hpp @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest + +#ifndef OCPP_V16_CHARGE_POINT_CONFIGURATION_BASE_HPP +#define OCPP_V16_CHARGE_POINT_CONFIGURATION_BASE_HPP + +#include + +#include +#include +#include +#include + +namespace ocpp::v16 { + +constexpr std::size_t AUTHORIZATION_KEY_MIN_LENGTH = 8; +constexpr std::size_t OCSP_REQUEST_INTERVAL_MIN = 86400; +constexpr std::size_t SECC_LEAF_SUBJECT_COMMON_NAME_MIN_LENGTH = 7; +constexpr std::size_t SECC_LEAF_SUBJECT_COMMON_NAME_MAX_LENGTH = 64; +constexpr std::size_t SECC_LEAF_SUBJECT_COUNTRY_LENGTH = 2; +constexpr std::size_t SECC_LEAF_SUBJECT_ORGANIZATION_MAX_LENGTH = 64; +constexpr std::size_t CONNECTOR_EVSE_IDS_MAX_LENGTH = 1000; +constexpr std::int32_t MAX_WAIT_FOR_SET_USER_PRICE_TIMEOUT_MS = 30000; + +/// \brief contains the configuration of the charge point +class ChargePointConfigurationBase { +public: + using MessagesSet = std::set; + using ProfilesSet = std::set; + using FeaturesMap = std::map; + using MeasurandsMap = std::map>; + using MeasurandWithPhaseList = std::vector; + +protected: + static const FeaturesMap supported_message_types_from_charge_point; + static const FeaturesMap supported_message_types_from_central_system; + + MessagesSet supported_message_types_receiving; + MessagesSet supported_message_types_sending; + ProfilesSet supported_feature_profiles; + MeasurandsMap supported_measurands; + + void initialise(const ProfilesSet& initial_set, const std::optional& supported_profiles_csl, + const std::optional& supported_measurands_csl); + +public: + bool is_valid_supported_measurands(const std::string& csl); + std::optional csv_to_measurand_with_phase_vector(const std::string& csl); + + static std::optional extract_connector_id(const std::string& str); + static std::string MeterPublicKey_string(std::uint32_t connector_id); + + static bool to_bool(const std::string& value); + static bool isBool(const std::string& str); + static bool isPositiveInteger(const std::string& str); + static bool isConnectorPhaseRotationValid(std::int32_t num_connectors, const std::string& value); + static bool isTimeOffset(const std::string& offset); + static bool areValidEvseIds(const std::string& value); + static std::string hexToString(const std::string& s); + static bool isHexNotation(const std::string& s); +}; + +} // namespace ocpp::v16 + +#endif // OCPP_V16_CHARGE_POINT_CONFIGURATION_BASE_HPP diff --git a/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration_devicemodel.hpp b/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration_devicemodel.hpp new file mode 100644 index 0000000000..343a4d778b --- /dev/null +++ b/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration_devicemodel.hpp @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest + +#ifndef OCPP_V16_CHARGE_POINT_CONFIGURATION_DEVICEMODEL_HPP +#define OCPP_V16_CHARGE_POINT_CONFIGURATION_DEVICEMODEL_HPP + +#include +#include +#include + +#include +#include +#include + +namespace ocpp { +namespace v2 { +class DeviceModelInterface; +} +namespace v16 { + +/// \brief contains the configuration of the charge point +class ChargePointConfigurationDeviceModel : private ChargePointConfigurationBase, + public ChargePointConfigurationInterface { +public: + using FearureProfilesSet = std::set; + using MeasurandsMap = std::map>; + using MessagesSet = std::set; + using SetResult = v2::SetVariableStatusEnum; + +protected: + std::unique_ptr storage; + + SetResult internalAllowChargingProfileWithoutStartSchedule(const std::string& value); + SetResult internalCentralSystemURI(const std::string& value); + SetResult internalCompositeScheduleDefaultLimitAmps(const std::string& value); + SetResult internalCompositeScheduleDefaultLimitWatts(const std::string& value); + SetResult internalCompositeScheduleDefaultNumberPhases(const std::string& value); + SetResult internalConnectorEvseIds(const std::string& value); + SetResult internalIgnoredProfilePurposesOffline(const std::string& value); + SetResult internalOcspRequestInterval(const std::string& value); + SetResult internalRetryBackoffRandomRange(const std::string& value); + SetResult internalRetryBackoffRepeatTimes(const std::string& value); + SetResult internalRetryBackoffWaitMinimum(const std::string& value); + SetResult internalSeccLeafSubjectCommonName(const std::string& value); + SetResult internalSeccLeafSubjectCountry(const std::string& value); + SetResult internalSeccLeafSubjectOrganization(const std::string& value); + SetResult internalStopTransactionIfUnlockNotSupported(const std::string& value); + SetResult internalSupplyVoltage(const std::string& value); + SetResult internalVerifyCsmsAllowWildcards(const std::string& value); + SetResult internalWaitForStopTransactionsOnResetTimeout(const std::string& value); + + SetResult internalAllowOfflineTxForUnknownId(const std::string& value); + SetResult internalAuthorizationCacheEnabled(const std::string& value); + SetResult internalAuthorizeRemoteTxRequests(const std::string& value); + SetResult internalBlinkRepeat(const std::string& value); + SetResult internalClockAlignedDataInterval(const std::string& value); + SetResult internalConnectionTimeOut(const std::string& value); + SetResult internalConnectorPhaseRotation(const std::string& value); + SetResult internalHeartbeatInterval(const std::string& value); + SetResult internalLightIntensity(const std::string& value); + SetResult internalLocalAuthorizeOffline(const std::string& value); + SetResult internalLocalPreAuthorize(const std::string& value); + SetResult internalMaxEnergyOnInvalidId(const std::string& value); + SetResult internalMeterValuesAlignedData(const std::string& value); + SetResult internalMeterValuesSampledData(const std::string& value); + SetResult internalMeterValueSampleInterval(const std::string& value); + SetResult internalMinimumStatusDuration(const std::string& value); + SetResult internalResetRetries(const std::string& value); + SetResult internalStopTransactionOnInvalidId(const std::string& value); + SetResult internalStopTxnAlignedData(const std::string& value); + SetResult internalStopTxnSampledData(const std::string& value); + SetResult internalTransactionMessageAttempts(const std::string& value); + SetResult internalTransactionMessageRetryInterval(const std::string& value); + SetResult internalUnlockConnectorOnEVSideDisconnect(const std::string& value); + SetResult internalWebsocketPingInterval(const std::string& value); + + SetResult internalAuthorizationKey(const std::string& value); + SetResult internalCpoName(const std::string& value); + SetResult internalDisableSecurityEventNotifications(const std::string& value); + SetResult internalSecurityProfile(const std::string& value); + + SetResult internalISO15118CertificateManagementEnabled(const std::string& value); + SetResult internalLocalAuthListEnabled(const std::string& value); + + SetResult internalContractValidationOffline(const std::string& value); + SetResult internalCentralContractValidationAllowed(const std::string& value); + SetResult internalCertSigningRepeatTimes(const std::string& value); + SetResult internalCertSigningWaitMinimum(const std::string& value); + SetResult internalISO15118PnCEnabled(const std::string& value); + + SetResult internalCustomIdleFeeAfterStop(const std::string& value); + SetResult internalDefaultPrice(const std::string& value); + SetResult internalDefaultPriceText(const std::string& key, const std::string& value); + SetResult internalDisplayTimeOffset(const std::string& value); + SetResult internalLanguage(const std::string& value); + SetResult internalNextTimeOffsetTransitionDateTime(const std::string& value); + SetResult internalTimeOffsetNextTransition(const std::string& value); + SetResult internalWaitForSetUserPriceTimeout(const std::string& value); + +public: + explicit ChargePointConfigurationDeviceModel(std::unique_ptr device_model_interface); + virtual ~ChargePointConfigurationDeviceModel() = default; + + // UserConfig and Internal + std::string getChargeBoxSerialNumber() override; + std::optional> getChargePointSerialNumber() override; + CiString<50> getFirmwareVersion() override; + std::optional> getICCID() override; + std::optional> getIMSI() override; + std::optional> getMeterSerialNumber() override; + std::optional> getMeterType() override; + + KeyValue getChargeBoxSerialNumberKeyValue() override; + KeyValue getFirmwareVersionKeyValue() override; + + std::optional getChargePointSerialNumberKeyValue() override; + std::optional getICCIDKeyValue() override; + std::optional getIMSIKeyValue() override; + std::optional getMeterSerialNumberKeyValue() override; + std::optional getMeterTypeKeyValue() override; + + void setChargepointInformation(const std::string& chargePointVendor, const std::string& chargePointModel, + const std::optional& chargePointSerialNumber, + const std::optional& chargeBoxSerialNumber, + const std::optional& firmwareVersion) override; + void setChargepointMeterInformation(const std::optional& meterSerialNumber, + const std::optional& meterType) override; + void setChargepointModemInformation(const std::optional& ICCID, + const std::optional& IMSI) override; + + // Internal + std::string getCentralSystemURI() override; + std::string getChargePointId() override; + std::string getSupportedCiphers12() override; + std::string getSupportedCiphers13() override; + std::string getSupportedMeasurands() override; + std::string getTLSKeylogFile() override; + std::string getWebsocketPingPayload() override; + + CiString<20> getChargePointModel() override; + CiString<20> getChargePointVendor() override; + + bool getAuthorizeConnectorZeroOnConnectorOne() override; + bool getEnableTLSKeylog() override; + bool getLogMessages() override; + bool getLogMessagesRaw() override; + bool getLogRotation() override; + bool getLogRotationDateSuffix() override; + bool getStopTransactionIfUnlockNotSupported() override; + bool getUseSslDefaultVerifyPaths() override; + bool getUseTPM() override; + bool getUseTPMSeccLeafCertificate() override; + bool getVerifyCsmsAllowWildcards() override; + bool getVerifyCsmsCommonName() override; + + int getMaxMessageSize() override; + + std::int32_t getMaxCompositeScheduleDuration() override; + std::int32_t getOcspRequestInterval() override; + std::int32_t getRetryBackoffRandomRange() override; + std::int32_t getRetryBackoffRepeatTimes() override; + std::int32_t getRetryBackoffWaitMinimum() override; + std::int32_t getWaitForStopTransactionsOnResetTimeout() override; + std::int32_t getWebsocketPongTimeout() override; + + std::uint64_t getLogRotationMaximumFileCount() override; + std::uint64_t getLogRotationMaximumFileSize() override; + + std::vector getLogMessagesFormat() override; + std::vector getIgnoredProfilePurposesOffline() override; + std::vector getSupportedChargingProfilePurposeTypes() override; + + std::optional getConnectorEvseIds() override; + std::optional getHostName() override; + std::optional getIFace() override; + std::optional getMessageTypesDiscardForQueueing() override; + std::optional getSeccLeafSubjectCommonName() override; + std::optional getSeccLeafSubjectCountry() override; + std::optional getSeccLeafSubjectOrganization() override; + std::optional getAllowChargingProfileWithoutStartSchedule() override; + std::optional getQueueAllMessages() override; + std::optional getMessageQueueSizeThreshold() override; + std::optional getCompositeScheduleDefaultLimitAmps() override; + std::optional getCompositeScheduleDefaultLimitWatts() override; + std::optional getCompositeScheduleDefaultNumberPhases() override; + std::optional getSupplyVoltage() override; + std::optional> getAllMeterPublicKeyKeyValues() override; + + std::set getSupportedMessageTypesSending() override; + std::set getSupportedMessageTypesReceiving() override; + + KeyValue getAuthorizeConnectorZeroOnConnectorOneKeyValue() override; + KeyValue getCentralSystemURIKeyValue() override; + KeyValue getChargePointIdKeyValue() override; + KeyValue getChargePointModelKeyValue() override; + KeyValue getChargePointVendorKeyValue() override; + KeyValue getLogMessagesFormatKeyValue() override; + KeyValue getLogMessagesKeyValue() override; + KeyValue getLogMessagesRawKeyValue() override; + KeyValue getLogRotationDateSuffixKeyValue() override; + KeyValue getLogRotationKeyValue() override; + KeyValue getLogRotationMaximumFileCountKeyValue() override; + KeyValue getLogRotationMaximumFileSizeKeyValue() override; + KeyValue getMaxCompositeScheduleDurationKeyValue() override; + KeyValue getMaxMessageSizeKeyValue() override; + KeyValue getOcspRequestIntervalKeyValue() override; + KeyValue getRetryBackoffRandomRangeKeyValue() override; + KeyValue getRetryBackoffRepeatTimesKeyValue() override; + KeyValue getRetryBackoffWaitMinimumKeyValue() override; + KeyValue getStopTransactionIfUnlockNotSupportedKeyValue() override; + KeyValue getSupportedChargingProfilePurposeTypesKeyValue() override; + KeyValue getSupportedCiphers12KeyValue() override; + KeyValue getSupportedCiphers13KeyValue() override; + KeyValue getSupportedMeasurandsKeyValue() override; + KeyValue getUseSslDefaultVerifyPathsKeyValue() override; + KeyValue getVerifyCsmsAllowWildcardsKeyValue() override; + KeyValue getVerifyCsmsCommonNameKeyValue() override; + KeyValue getWaitForStopTransactionsOnResetTimeoutKeyValue() override; + KeyValue getWebsocketPingPayloadKeyValue() override; + KeyValue getWebsocketPongTimeoutKeyValue() override; + + std::optional getAllowChargingProfileWithoutStartScheduleKeyValue() override; + std::optional getCompositeScheduleDefaultLimitAmpsKeyValue() override; + std::optional getCompositeScheduleDefaultLimitWattsKeyValue() override; + std::optional getCompositeScheduleDefaultNumberPhasesKeyValue() override; + std::optional getConnectorEvseIdsKeyValue() override; + std::optional getHostNameKeyValue() override; + std::optional getIFaceKeyValue() override; + std::optional getIgnoredProfilePurposesOfflineKeyValue() override; + std::optional getMessageTypesDiscardForQueueingKeyValue() override; + std::optional getMessageQueueSizeThresholdKeyValue() override; + std::optional getPublicKeyKeyValue(std::uint32_t connector_id) override; + std::optional getQueueAllMessagesKeyValue() override; + std::optional getSeccLeafSubjectCommonNameKeyValue() override; + std::optional getSeccLeafSubjectCountryKeyValue() override; + std::optional getSeccLeafSubjectOrganizationKeyValue() override; + std::optional getSupplyVoltageKeyValue() override; + + void setAllowChargingProfileWithoutStartSchedule(bool allow) override; + void setCentralSystemURI(const std::string& ocpp_uri) override; + void setCompositeScheduleDefaultLimitAmps(std::int32_t limit_amps) override; + void setCompositeScheduleDefaultLimitWatts(std::int32_t limit_watts) override; + void setCompositeScheduleDefaultNumberPhases(std::int32_t number_phases) override; + void setConnectorEvseIds(const std::string& connector_evse_ids) override; + bool setIgnoredProfilePurposesOffline(const std::string& ignored_profile_purposes_offline) override; + bool setMeterPublicKey(std::int32_t connector_id, const std::string& public_key_pem) override; + void setOcspRequestInterval(std::int32_t ocsp_request_interval) override; + void setRetryBackoffRandomRange(std::int32_t retry_backoff_random_range) override; + void setRetryBackoffRepeatTimes(std::int32_t retry_backoff_repeat_times) override; + void setRetryBackoffWaitMinimum(std::int32_t retry_backoff_wait_minimum) override; + void setSeccLeafSubjectCommonName(const std::string& secc_leaf_subject_common_name) override; + void setSeccLeafSubjectCountry(const std::string& secc_leaf_subject_country) override; + void setSeccLeafSubjectOrganization(const std::string& secc_leaf_subject_organization) override; + void setStopTransactionIfUnlockNotSupported(bool stop_transaction_if_unlock_not_supported) override; + void setSupplyVoltage(std::int32_t supply_voltage) override; + void setVerifyCsmsAllowWildcards(bool verify_csms_allow_wildcards) override; + void setWaitForStopTransactionsOnResetTimeout(std::int32_t wait_for_stop_transactions_on_reset_timeout) override; + + // Core Profile + std::string getConnectorPhaseRotation() override; + std::string getMeterValuesAlignedData() override; + std::string getMeterValuesSampledData() override; + std::string getStopTxnAlignedData() override; + std::string getStopTxnSampledData() override; + std::string getSupportedFeatureProfiles() override; + + bool getAuthorizeRemoteTxRequests() override; + bool getLocalAuthorizeOffline() override; + bool getLocalPreAuthorize() override; + bool getStopTransactionOnInvalidId() override; + bool getUnlockConnectorOnEVSideDisconnect() override; + + std::int32_t getClockAlignedDataInterval() override; + std::int32_t getConnectionTimeOut() override; + std::int32_t getGetConfigurationMaxKeys() override; + std::int32_t getHeartbeatInterval() override; + std::int32_t getMeterValueSampleInterval() override; + std::int32_t getNumberOfConnectors() override; + std::int32_t getResetRetries() override; + std::int32_t getTransactionMessageAttempts() override; + std::int32_t getTransactionMessageRetryInterval() override; + + std::vector getMeterValuesAlignedDataVector() override; + std::vector getMeterValuesSampledDataVector() override; + + std::optional getAllowOfflineTxForUnknownId() override; + std::optional getAuthorizationCacheEnabled() override; + std::optional getReserveConnectorZeroSupported() override; + std::optional getStopTransactionOnEVSideDisconnect() override; + std::optional getBlinkRepeat() override; + std::optional getConnectorPhaseRotationMaxLength() override; + std::optional getLightIntensity() override; + std::optional getMaxEnergyOnInvalidId() override; + std::optional getMeterValuesAlignedDataMaxLength() override; + std::optional getMeterValuesSampledDataMaxLength() override; + std::optional getMinimumStatusDuration() override; + std::optional getStopTxnAlignedDataMaxLength() override; + std::optional getStopTxnSampledDataMaxLength() override; + std::optional getSupportedFeatureProfilesMaxLength() override; + std::optional getWebsocketPingInterval() override; + + std::set getSupportedFeatureProfilesSet() override; + + KeyValue getAuthorizeRemoteTxRequestsKeyValue() override; + KeyValue getClockAlignedDataIntervalKeyValue() override; + KeyValue getConnectionTimeOutKeyValue() override; + KeyValue getConnectorPhaseRotationKeyValue() override; + KeyValue getGetConfigurationMaxKeysKeyValue() override; + KeyValue getHeartbeatIntervalKeyValue() override; + KeyValue getLocalAuthorizeOfflineKeyValue() override; + KeyValue getLocalPreAuthorizeKeyValue() override; + KeyValue getMeterValuesAlignedDataKeyValue() override; + KeyValue getMeterValueSampleIntervalKeyValue() override; + KeyValue getMeterValuesSampledDataKeyValue() override; + KeyValue getNumberOfConnectorsKeyValue() override; + KeyValue getResetRetriesKeyValue() override; + KeyValue getStopTransactionOnInvalidIdKeyValue() override; + KeyValue getStopTxnAlignedDataKeyValue() override; + KeyValue getStopTxnSampledDataKeyValue() override; + KeyValue getSupportedFeatureProfilesKeyValue() override; + KeyValue getTransactionMessageAttemptsKeyValue() override; + KeyValue getTransactionMessageRetryIntervalKeyValue() override; + KeyValue getUnlockConnectorOnEVSideDisconnectKeyValue() override; + + std::optional getAllowOfflineTxForUnknownIdKeyValue() override; + std::optional getAuthorizationCacheEnabledKeyValue() override; + std::optional getBlinkRepeatKeyValue() override; + std::optional getConnectorPhaseRotationMaxLengthKeyValue() override; + std::optional getLightIntensityKeyValue() override; + std::optional getMaxEnergyOnInvalidIdKeyValue() override; + std::optional getMeterValuesAlignedDataMaxLengthKeyValue() override; + std::optional getMeterValuesSampledDataMaxLengthKeyValue() override; + std::optional getMinimumStatusDurationKeyValue() override; + std::optional getReserveConnectorZeroSupportedKeyValue() override; + std::optional getStopTransactionOnEVSideDisconnectKeyValue() override; + std::optional getStopTxnAlignedDataMaxLengthKeyValue() override; + std::optional getStopTxnSampledDataMaxLengthKeyValue() override; + std::optional getSupportedFeatureProfilesMaxLengthKeyValue() override; + std::optional getWebsocketPingIntervalKeyValue() override; + + void setAllowOfflineTxForUnknownId(bool enabled) override; + void setAuthorizationCacheEnabled(bool enabled) override; + void setAuthorizeRemoteTxRequests(bool enabled) override; + void setBlinkRepeat(std::int32_t blink_repeat) override; + void setClockAlignedDataInterval(std::int32_t interval) override; + void setConnectionTimeOut(std::int32_t timeout) override; + void setConnectorPhaseRotation(const std::string& connector_phase_rotation) override; + void setHeartbeatInterval(std::int32_t interval) override; + void setLightIntensity(std::int32_t light_intensity) override; + void setLocalAuthorizeOffline(bool local_authorize_offline) override; + void setLocalPreAuthorize(bool local_pre_authorize) override; + void setMaxEnergyOnInvalidId(std::int32_t max_energy) override; + bool setMeterValuesAlignedData(const std::string& meter_values_aligned_data) override; + bool setMeterValuesSampledData(const std::string& meter_values_sampled_data) override; + void setMeterValueSampleInterval(std::int32_t interval) override; + void setMinimumStatusDuration(std::int32_t minimum_status_duration) override; + void setResetRetries(std::int32_t retries) override; + void setStopTransactionOnInvalidId(bool stop_transaction_on_invalid_id) override; + bool setStopTxnAlignedData(const std::string& stop_txn_aligned_data) override; + bool setStopTxnSampledData(const std::string& stop_txn_sampled_data) override; + void setTransactionMessageAttempts(std::int32_t attempts) override; + void setTransactionMessageRetryInterval(std::int32_t retry_interval) override; + void setUnlockConnectorOnEVSideDisconnect(bool unlock_connector_on_ev_side_disconnect) override; + void setWebsocketPingInterval(std::int32_t websocket_ping_interval) override; + + // Firmware Management Profile + std::optional getSupportedFileTransferProtocols() override; + std::optional getSupportedFileTransferProtocolsKeyValue() override; + + // Smart Charging Profile + std::string getChargingScheduleAllowedChargingRateUnit() override; + std::int32_t getChargeProfileMaxStackLevel() override; + std::int32_t getChargingScheduleMaxPeriods() override; + std::int32_t getMaxChargingProfilesInstalled() override; + + std::optional getConnectorSwitch3to1PhaseSupported() override; + + std::vector getChargingScheduleAllowedChargingRateUnitVector() override; + + KeyValue getChargeProfileMaxStackLevelKeyValue() override; + KeyValue getChargingScheduleAllowedChargingRateUnitKeyValue() override; + KeyValue getChargingScheduleMaxPeriodsKeyValue() override; + KeyValue getMaxChargingProfilesInstalledKeyValue() override; + + std::optional getConnectorSwitch3to1PhaseSupportedKeyValue() override; + + // Security Profile + bool getDisableSecurityEventNotifications() override; + std::int32_t getSecurityProfile() override; + + std::optional getAuthorizationKey() override; + std::optional getCpoName() override; + std::optional getAdditionalRootCertificateCheck() override; + std::optional getCertificateSignedMaxChainSize() override; + std::optional getCertificateStoreMaxLength() override; + + KeyValue getDisableSecurityEventNotificationsKeyValue() override; + KeyValue getSecurityProfileKeyValue() override; + + std::optional getAdditionalRootCertificateCheckKeyValue() override; + std::optional getAuthorizationKeyKeyValue() override; + std::optional getCertificateSignedMaxChainSizeKeyValue() override; + std::optional getCertificateStoreMaxLengthKeyValue() override; + std::optional getCpoNameKeyValue() override; + + void setAuthorizationKey(const std::string& authorization_key) override; + void setCpoName(const std::string& cpo_name) override; + void setDisableSecurityEventNotifications(bool disable_security_event_notifications) override; + void setSecurityProfile(std::int32_t security_profile) override; + + // Local Auth List Management Profile + bool getLocalAuthListEnabled() override; + std::int32_t getLocalAuthListMaxLength() override; + std::int32_t getSendLocalListMaxLength() override; + + KeyValue getLocalAuthListEnabledKeyValue() override; + KeyValue getLocalAuthListMaxLengthKeyValue() override; + KeyValue getSendLocalListMaxLengthKeyValue() override; + + void setLocalAuthListEnabled(bool local_auth_list_enabled) override; + + // PnC + bool getContractValidationOffline() override; + bool getISO15118CertificateManagementEnabled() override; + bool getISO15118PnCEnabled() override; + + std::optional getCentralContractValidationAllowed() override; + std::optional getCertSigningRepeatTimes() override; + std::optional getCertSigningWaitMinimum() override; + + KeyValue getContractValidationOfflineKeyValue() override; + KeyValue getISO15118CertificateManagementEnabledKeyValue() override; + KeyValue getISO15118PnCEnabledKeyValue() override; + + std::optional getCentralContractValidationAllowedKeyValue() override; + std::optional getCertSigningRepeatTimesKeyValue() override; + std::optional getCertSigningWaitMinimumKeyValue() override; + + void setContractValidationOffline(bool contract_validation_offline) override; + void setCentralContractValidationAllowed(bool central_contract_validation_allowed) override; + void setCertSigningRepeatTimes(std::int32_t cert_signing_repeat_times) override; + void setCertSigningWaitMinimum(std::int32_t cert_signing_wait_minimum) override; + void setISO15118CertificateManagementEnabled(bool iso15118_certificate_management_enabled) override; + void setISO15118PnCEnabled(bool iso15118_pnc_enabled) override; + + // California Pricing Requirements + bool getCustomDisplayCostAndPriceEnabled() override; + + TariffMessage getDefaultTariffMessage(bool offline) override; + + std::optional getDefaultPrice() override; + std::optional getDefaultPriceText(const std::string& language) override; + std::optional getDisplayTimeOffset() override; + std::optional getLanguage() override; + std::optional getMultiLanguageSupportedLanguages() override; + std::optional getNextTimeOffsetTransitionDateTime() override; + std::optional getTimeOffsetNextTransition() override; + std::optional getCustomIdleFeeAfterStop() override; + std::optional getCustomMultiLanguageMessagesEnabled() override; + std::optional getWaitForSetUserPriceTimeout() override; + std::optional getPriceNumberOfDecimalsForCostValues() override; + + KeyValue getCustomDisplayCostAndPriceEnabledKeyValue() override; + KeyValue getDefaultPriceTextKeyValue(const std::string& language) override; + + std::optional getCustomIdleFeeAfterStopKeyValue() override; + std::optional getCustomMultiLanguageMessagesEnabledKeyValue() override; + std::optional getDefaultPriceKeyValue() override; + std::optional getDisplayTimeOffsetKeyValue() override; + std::optional getLanguageKeyValue() override; + std::optional getMultiLanguageSupportedLanguagesKeyValue() override; + std::optional getNextTimeOffsetTransitionDateTimeKeyValue() override; + std::optional getPriceNumberOfDecimalsForCostValuesKeyValue() override; + std::optional getTimeOffsetNextTransitionKeyValue() override; + std::optional getWaitForSetUserPriceTimeoutKeyValue() override; + + std::optional> getAllDefaultPriceTextKeyValues() override; + + void setCustomIdleFeeAfterStop(bool value) override; + ConfigurationStatus setDefaultPrice(const std::string& value) override; + ConfigurationStatus setDefaultPriceText(const CiString<50>& key, const CiString<500>& value) override; + ConfigurationStatus setDisplayTimeOffset(const std::string& offset) override; + void setLanguage(const std::string& language) override; + ConfigurationStatus setNextTimeOffsetTransitionDateTime(const std::string& date_time) override; + ConfigurationStatus setTimeOffsetNextTransition(const std::string& offset) override; + void setWaitForSetUserPriceTimeout(std::int32_t wait_for_set_user_price_timeout) override; + + // Custom + std::optional getCustomKeyValue(const CiString<50>& key) override; + std::optional get(const CiString<50>& key) override; + std::vector get_all_key_value() override; + + ConfigurationStatus setCustomKey(const CiString<50>& key, const CiString<500>& value, bool force) override; + std::optional set(const CiString<50>& key, const CiString<500>& value) override; +}; + +} // namespace v16 +} // namespace ocpp + +#endif // OCPP_V16_CHARGE_POINT_CONFIGURATION_DEVICEMODEL_HPP diff --git a/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration_interface.hpp b/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration_interface.hpp new file mode 100644 index 0000000000..8e7aa2661f --- /dev/null +++ b/lib/everest/ocpp/include/ocpp/v16/charge_point_configuration_interface.hpp @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest +#ifndef OCPP_V16_CHARGE_POINT_CONFIGURATION_INTERFACE_HPP +#define OCPP_V16_CHARGE_POINT_CONFIGURATION_INTERFACE_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace ocpp::v16 { + +struct KeyValue; + +/// \brief contains the configuration of the charge point +class ChargePointConfigurationInterface { +public: + // UserConfig and Internal + virtual std::string getChargeBoxSerialNumber() = 0; + virtual std::optional> getChargePointSerialNumber() = 0; + virtual CiString<50> getFirmwareVersion() = 0; + virtual std::optional> getICCID() = 0; + virtual std::optional> getIMSI() = 0; + virtual std::optional> getMeterSerialNumber() = 0; + virtual std::optional> getMeterType() = 0; + + virtual KeyValue getChargeBoxSerialNumberKeyValue() = 0; + virtual KeyValue getFirmwareVersionKeyValue() = 0; + + virtual std::optional getChargePointSerialNumberKeyValue() = 0; + virtual std::optional getICCIDKeyValue() = 0; + virtual std::optional getIMSIKeyValue() = 0; + virtual std::optional getMeterSerialNumberKeyValue() = 0; + virtual std::optional getMeterTypeKeyValue() = 0; + + virtual void setChargepointInformation(const std::string& chargePointVendor, const std::string& chargePointModel, + const std::optional& chargePointSerialNumber, + const std::optional& chargeBoxSerialNumber, + const std::optional& firmwareVersion) = 0; + virtual void setChargepointMeterInformation(const std::optional& meterSerialNumber, + const std::optional& meterType) = 0; + virtual void setChargepointModemInformation(const std::optional& ICCID, + const std::optional& IMSI) = 0; + + // Internal + virtual std::string getCentralSystemURI() = 0; + virtual std::string getChargePointId() = 0; + virtual std::string getSupportedCiphers12() = 0; + virtual std::string getSupportedCiphers13() = 0; + virtual std::string getSupportedMeasurands() = 0; + virtual std::string getTLSKeylogFile() = 0; + virtual std::string getWebsocketPingPayload() = 0; + + virtual CiString<20> getChargePointModel() = 0; + virtual CiString<20> getChargePointVendor() = 0; + + virtual bool getAuthorizeConnectorZeroOnConnectorOne() = 0; + virtual bool getEnableTLSKeylog() = 0; + virtual bool getLogMessages() = 0; + virtual bool getLogMessagesRaw() = 0; + virtual bool getLogRotation() = 0; + virtual bool getLogRotationDateSuffix() = 0; + virtual bool getStopTransactionIfUnlockNotSupported() = 0; + virtual bool getUseSslDefaultVerifyPaths() = 0; + virtual bool getUseTPM() = 0; + virtual bool getUseTPMSeccLeafCertificate() = 0; + virtual bool getVerifyCsmsAllowWildcards() = 0; + virtual bool getVerifyCsmsCommonName() = 0; + + virtual int getMaxMessageSize() = 0; + + virtual std::int32_t getMaxCompositeScheduleDuration() = 0; + virtual std::int32_t getOcspRequestInterval() = 0; + virtual std::int32_t getRetryBackoffRandomRange() = 0; + virtual std::int32_t getRetryBackoffRepeatTimes() = 0; + virtual std::int32_t getRetryBackoffWaitMinimum() = 0; + virtual std::int32_t getWaitForStopTransactionsOnResetTimeout() = 0; + virtual std::int32_t getWebsocketPongTimeout() = 0; + + virtual std::uint64_t getLogRotationMaximumFileCount() = 0; + virtual std::uint64_t getLogRotationMaximumFileSize() = 0; + + virtual std::vector getLogMessagesFormat() = 0; + virtual std::vector getIgnoredProfilePurposesOffline() = 0; + virtual std::vector getSupportedChargingProfilePurposeTypes() = 0; + + virtual std::optional getConnectorEvseIds() = 0; + virtual std::optional getHostName() = 0; + virtual std::optional getIFace() = 0; + virtual std::optional getMessageTypesDiscardForQueueing() = 0; + virtual std::optional getSeccLeafSubjectCommonName() = 0; + virtual std::optional getSeccLeafSubjectCountry() = 0; + virtual std::optional getSeccLeafSubjectOrganization() = 0; + virtual std::optional getAllowChargingProfileWithoutStartSchedule() = 0; + virtual std::optional getQueueAllMessages() = 0; + virtual std::optional getMessageQueueSizeThreshold() = 0; + virtual std::optional getCompositeScheduleDefaultLimitAmps() = 0; + virtual std::optional getCompositeScheduleDefaultLimitWatts() = 0; + virtual std::optional getCompositeScheduleDefaultNumberPhases() = 0; + virtual std::optional getSupplyVoltage() = 0; + virtual std::optional> getAllMeterPublicKeyKeyValues() = 0; + + virtual std::set getSupportedMessageTypesSending() = 0; + virtual std::set getSupportedMessageTypesReceiving() = 0; + + virtual KeyValue getAuthorizeConnectorZeroOnConnectorOneKeyValue() = 0; + virtual KeyValue getCentralSystemURIKeyValue() = 0; + virtual KeyValue getChargePointIdKeyValue() = 0; + virtual KeyValue getChargePointModelKeyValue() = 0; + virtual KeyValue getChargePointVendorKeyValue() = 0; + virtual KeyValue getLogMessagesFormatKeyValue() = 0; + virtual KeyValue getLogMessagesKeyValue() = 0; + virtual KeyValue getLogMessagesRawKeyValue() = 0; + virtual KeyValue getLogRotationDateSuffixKeyValue() = 0; + virtual KeyValue getLogRotationKeyValue() = 0; + virtual KeyValue getLogRotationMaximumFileCountKeyValue() = 0; + virtual KeyValue getLogRotationMaximumFileSizeKeyValue() = 0; + virtual KeyValue getMaxCompositeScheduleDurationKeyValue() = 0; + virtual KeyValue getMaxMessageSizeKeyValue() = 0; + virtual KeyValue getOcspRequestIntervalKeyValue() = 0; + virtual KeyValue getRetryBackoffRandomRangeKeyValue() = 0; + virtual KeyValue getRetryBackoffRepeatTimesKeyValue() = 0; + virtual KeyValue getRetryBackoffWaitMinimumKeyValue() = 0; + virtual KeyValue getStopTransactionIfUnlockNotSupportedKeyValue() = 0; + virtual KeyValue getSupportedChargingProfilePurposeTypesKeyValue() = 0; + virtual KeyValue getSupportedCiphers12KeyValue() = 0; + virtual KeyValue getSupportedCiphers13KeyValue() = 0; + virtual KeyValue getSupportedMeasurandsKeyValue() = 0; + virtual KeyValue getUseSslDefaultVerifyPathsKeyValue() = 0; + virtual KeyValue getVerifyCsmsAllowWildcardsKeyValue() = 0; + virtual KeyValue getVerifyCsmsCommonNameKeyValue() = 0; + virtual KeyValue getWaitForStopTransactionsOnResetTimeoutKeyValue() = 0; + virtual KeyValue getWebsocketPingPayloadKeyValue() = 0; + virtual KeyValue getWebsocketPongTimeoutKeyValue() = 0; + + virtual std::optional getAllowChargingProfileWithoutStartScheduleKeyValue() = 0; + virtual std::optional getCompositeScheduleDefaultLimitAmpsKeyValue() = 0; + virtual std::optional getCompositeScheduleDefaultLimitWattsKeyValue() = 0; + virtual std::optional getCompositeScheduleDefaultNumberPhasesKeyValue() = 0; + virtual std::optional getConnectorEvseIdsKeyValue() = 0; + virtual std::optional getHostNameKeyValue() = 0; + virtual std::optional getIFaceKeyValue() = 0; + virtual std::optional getIgnoredProfilePurposesOfflineKeyValue() = 0; + virtual std::optional getMessageTypesDiscardForQueueingKeyValue() = 0; + virtual std::optional getMessageQueueSizeThresholdKeyValue() = 0; + virtual std::optional getPublicKeyKeyValue(std::uint32_t connector_id) = 0; + virtual std::optional getQueueAllMessagesKeyValue() = 0; + virtual std::optional getSeccLeafSubjectCommonNameKeyValue() = 0; + virtual std::optional getSeccLeafSubjectCountryKeyValue() = 0; + virtual std::optional getSeccLeafSubjectOrganizationKeyValue() = 0; + virtual std::optional getSupplyVoltageKeyValue() = 0; + + virtual void setAllowChargingProfileWithoutStartSchedule(bool allow) = 0; + virtual void setCentralSystemURI(const std::string& ocpp_uri) = 0; + virtual void setCompositeScheduleDefaultLimitAmps(std::int32_t limit_amps) = 0; + virtual void setCompositeScheduleDefaultLimitWatts(std::int32_t limit_watts) = 0; + virtual void setCompositeScheduleDefaultNumberPhases(std::int32_t number_phases) = 0; + virtual void setConnectorEvseIds(const std::string& connector_evse_ids) = 0; + virtual bool setIgnoredProfilePurposesOffline(const std::string& ignored_profile_purposes_offline) = 0; + virtual bool setMeterPublicKey(std::int32_t connector_id, const std::string& public_key_pem) = 0; + virtual void setOcspRequestInterval(std::int32_t ocsp_request_interval) = 0; + virtual void setRetryBackoffRandomRange(std::int32_t retry_backoff_random_range) = 0; + virtual void setRetryBackoffRepeatTimes(std::int32_t retry_backoff_repeat_times) = 0; + virtual void setRetryBackoffWaitMinimum(std::int32_t retry_backoff_wait_minimum) = 0; + virtual void setSeccLeafSubjectCommonName(const std::string& secc_leaf_subject_common_name) = 0; + virtual void setSeccLeafSubjectCountry(const std::string& secc_leaf_subject_country) = 0; + virtual void setSeccLeafSubjectOrganization(const std::string& secc_leaf_subject_organization) = 0; + virtual void setStopTransactionIfUnlockNotSupported(bool stop_transaction_if_unlock_not_supported) = 0; + virtual void setSupplyVoltage(std::int32_t supply_voltage) = 0; + virtual void setVerifyCsmsAllowWildcards(bool verify_csms_allow_wildcards) = 0; + virtual void setWaitForStopTransactionsOnResetTimeout(std::int32_t wait_for_stop_transactions_on_reset_timeout) = 0; + + // Core Profile + virtual std::string getConnectorPhaseRotation() = 0; + virtual std::string getMeterValuesAlignedData() = 0; + virtual std::string getMeterValuesSampledData() = 0; + virtual std::string getStopTxnAlignedData() = 0; + virtual std::string getStopTxnSampledData() = 0; + virtual std::string getSupportedFeatureProfiles() = 0; + + virtual bool getAuthorizeRemoteTxRequests() = 0; + virtual bool getLocalAuthorizeOffline() = 0; + virtual bool getLocalPreAuthorize() = 0; + virtual bool getStopTransactionOnInvalidId() = 0; + virtual bool getUnlockConnectorOnEVSideDisconnect() = 0; + + virtual std::int32_t getClockAlignedDataInterval() = 0; + virtual std::int32_t getConnectionTimeOut() = 0; + virtual std::int32_t getGetConfigurationMaxKeys() = 0; + virtual std::int32_t getHeartbeatInterval() = 0; + virtual std::int32_t getMeterValueSampleInterval() = 0; + virtual std::int32_t getNumberOfConnectors() = 0; + virtual std::int32_t getResetRetries() = 0; + virtual std::int32_t getTransactionMessageAttempts() = 0; + virtual std::int32_t getTransactionMessageRetryInterval() = 0; + + virtual std::vector getMeterValuesAlignedDataVector() = 0; + virtual std::vector getMeterValuesSampledDataVector() = 0; + + virtual std::optional getAllowOfflineTxForUnknownId() = 0; + virtual std::optional getAuthorizationCacheEnabled() = 0; + virtual std::optional getReserveConnectorZeroSupported() = 0; + virtual std::optional getStopTransactionOnEVSideDisconnect() = 0; + virtual std::optional getBlinkRepeat() = 0; + virtual std::optional getConnectorPhaseRotationMaxLength() = 0; + virtual std::optional getLightIntensity() = 0; + virtual std::optional getMaxEnergyOnInvalidId() = 0; + virtual std::optional getMeterValuesAlignedDataMaxLength() = 0; + virtual std::optional getMeterValuesSampledDataMaxLength() = 0; + virtual std::optional getMinimumStatusDuration() = 0; + virtual std::optional getStopTxnAlignedDataMaxLength() = 0; + virtual std::optional getStopTxnSampledDataMaxLength() = 0; + virtual std::optional getSupportedFeatureProfilesMaxLength() = 0; + virtual std::optional getWebsocketPingInterval() = 0; + + virtual std::set getSupportedFeatureProfilesSet() = 0; + + virtual KeyValue getAuthorizeRemoteTxRequestsKeyValue() = 0; + virtual KeyValue getClockAlignedDataIntervalKeyValue() = 0; + virtual KeyValue getConnectionTimeOutKeyValue() = 0; + virtual KeyValue getConnectorPhaseRotationKeyValue() = 0; + virtual KeyValue getGetConfigurationMaxKeysKeyValue() = 0; + virtual KeyValue getHeartbeatIntervalKeyValue() = 0; + virtual KeyValue getLocalAuthorizeOfflineKeyValue() = 0; + virtual KeyValue getLocalPreAuthorizeKeyValue() = 0; + virtual KeyValue getMeterValuesAlignedDataKeyValue() = 0; + virtual KeyValue getMeterValueSampleIntervalKeyValue() = 0; + virtual KeyValue getMeterValuesSampledDataKeyValue() = 0; + virtual KeyValue getNumberOfConnectorsKeyValue() = 0; + virtual KeyValue getResetRetriesKeyValue() = 0; + virtual KeyValue getStopTransactionOnInvalidIdKeyValue() = 0; + virtual KeyValue getStopTxnAlignedDataKeyValue() = 0; + virtual KeyValue getStopTxnSampledDataKeyValue() = 0; + virtual KeyValue getSupportedFeatureProfilesKeyValue() = 0; + virtual KeyValue getTransactionMessageAttemptsKeyValue() = 0; + virtual KeyValue getTransactionMessageRetryIntervalKeyValue() = 0; + virtual KeyValue getUnlockConnectorOnEVSideDisconnectKeyValue() = 0; + + virtual std::optional getAllowOfflineTxForUnknownIdKeyValue() = 0; + virtual std::optional getAuthorizationCacheEnabledKeyValue() = 0; + virtual std::optional getBlinkRepeatKeyValue() = 0; + virtual std::optional getConnectorPhaseRotationMaxLengthKeyValue() = 0; + virtual std::optional getLightIntensityKeyValue() = 0; + virtual std::optional getMaxEnergyOnInvalidIdKeyValue() = 0; + virtual std::optional getMeterValuesAlignedDataMaxLengthKeyValue() = 0; + virtual std::optional getMeterValuesSampledDataMaxLengthKeyValue() = 0; + virtual std::optional getMinimumStatusDurationKeyValue() = 0; + virtual std::optional getReserveConnectorZeroSupportedKeyValue() = 0; + virtual std::optional getStopTransactionOnEVSideDisconnectKeyValue() = 0; + virtual std::optional getStopTxnAlignedDataMaxLengthKeyValue() = 0; + virtual std::optional getStopTxnSampledDataMaxLengthKeyValue() = 0; + virtual std::optional getSupportedFeatureProfilesMaxLengthKeyValue() = 0; + virtual std::optional getWebsocketPingIntervalKeyValue() = 0; + + virtual void setAllowOfflineTxForUnknownId(bool enabled) = 0; + virtual void setAuthorizationCacheEnabled(bool enabled) = 0; + virtual void setAuthorizeRemoteTxRequests(bool enabled) = 0; + virtual void setBlinkRepeat(std::int32_t blink_repeat) = 0; + virtual void setClockAlignedDataInterval(std::int32_t interval) = 0; + virtual void setConnectionTimeOut(std::int32_t timeout) = 0; + virtual void setConnectorPhaseRotation(const std::string& connector_phase_rotation) = 0; + virtual void setHeartbeatInterval(std::int32_t interval) = 0; + virtual void setLightIntensity(std::int32_t light_intensity) = 0; + virtual void setLocalAuthorizeOffline(bool local_authorize_offline) = 0; + virtual void setLocalPreAuthorize(bool local_pre_authorize) = 0; + virtual void setMaxEnergyOnInvalidId(std::int32_t max_energy) = 0; + virtual bool setMeterValuesAlignedData(const std::string& meter_values_aligned_data) = 0; + virtual bool setMeterValuesSampledData(const std::string& meter_values_sampled_data) = 0; + virtual void setMeterValueSampleInterval(std::int32_t interval) = 0; + virtual void setMinimumStatusDuration(std::int32_t minimum_status_duration) = 0; + virtual void setResetRetries(std::int32_t retries) = 0; + virtual void setStopTransactionOnInvalidId(bool stop_transaction_on_invalid_id) = 0; + virtual bool setStopTxnAlignedData(const std::string& stop_txn_aligned_data) = 0; + virtual bool setStopTxnSampledData(const std::string& stop_txn_sampled_data) = 0; + virtual void setTransactionMessageAttempts(std::int32_t attempts) = 0; + virtual void setTransactionMessageRetryInterval(std::int32_t retry_interval) = 0; + virtual void setUnlockConnectorOnEVSideDisconnect(bool unlock_connector_on_ev_side_disconnect) = 0; + virtual void setWebsocketPingInterval(std::int32_t websocket_ping_interval) = 0; + + // Firmware Management Profile + virtual std::optional getSupportedFileTransferProtocols() = 0; + virtual std::optional getSupportedFileTransferProtocolsKeyValue() = 0; + + // Smart Charging Profile + virtual std::string getChargingScheduleAllowedChargingRateUnit() = 0; + virtual std::int32_t getChargeProfileMaxStackLevel() = 0; + virtual std::int32_t getChargingScheduleMaxPeriods() = 0; + virtual std::int32_t getMaxChargingProfilesInstalled() = 0; + + virtual std::optional getConnectorSwitch3to1PhaseSupported() = 0; + + virtual std::vector getChargingScheduleAllowedChargingRateUnitVector() = 0; + + virtual KeyValue getChargeProfileMaxStackLevelKeyValue() = 0; + virtual KeyValue getChargingScheduleAllowedChargingRateUnitKeyValue() = 0; + virtual KeyValue getChargingScheduleMaxPeriodsKeyValue() = 0; + virtual KeyValue getMaxChargingProfilesInstalledKeyValue() = 0; + + virtual std::optional getConnectorSwitch3to1PhaseSupportedKeyValue() = 0; + + // Security Profile + virtual bool getDisableSecurityEventNotifications() = 0; + virtual std::int32_t getSecurityProfile() = 0; + + virtual std::optional getAuthorizationKey() = 0; + virtual std::optional getCpoName() = 0; + virtual std::optional getAdditionalRootCertificateCheck() = 0; + virtual std::optional getCertificateSignedMaxChainSize() = 0; + virtual std::optional getCertificateStoreMaxLength() = 0; + + virtual KeyValue getDisableSecurityEventNotificationsKeyValue() = 0; + virtual KeyValue getSecurityProfileKeyValue() = 0; + + virtual std::optional getAdditionalRootCertificateCheckKeyValue() = 0; + virtual std::optional getAuthorizationKeyKeyValue() = 0; + virtual std::optional getCertificateSignedMaxChainSizeKeyValue() = 0; + virtual std::optional getCertificateStoreMaxLengthKeyValue() = 0; + virtual std::optional getCpoNameKeyValue() = 0; + + virtual void setAuthorizationKey(const std::string& authorization_key) = 0; + virtual void setCpoName(const std::string& cpo_name) = 0; + virtual void setDisableSecurityEventNotifications(bool disable_security_event_notifications) = 0; + virtual void setSecurityProfile(std::int32_t security_profile) = 0; + + // Local Auth List Management Profile + virtual bool getLocalAuthListEnabled() = 0; + virtual std::int32_t getLocalAuthListMaxLength() = 0; + virtual std::int32_t getSendLocalListMaxLength() = 0; + + virtual KeyValue getLocalAuthListEnabledKeyValue() = 0; + virtual KeyValue getLocalAuthListMaxLengthKeyValue() = 0; + virtual KeyValue getSendLocalListMaxLengthKeyValue() = 0; + + virtual void setLocalAuthListEnabled(bool local_auth_list_enabled) = 0; + + // PnC + virtual bool getContractValidationOffline() = 0; + virtual bool getISO15118CertificateManagementEnabled() = 0; + virtual bool getISO15118PnCEnabled() = 0; + + virtual std::optional getCentralContractValidationAllowed() = 0; + virtual std::optional getCertSigningRepeatTimes() = 0; + virtual std::optional getCertSigningWaitMinimum() = 0; + + virtual KeyValue getContractValidationOfflineKeyValue() = 0; + virtual KeyValue getISO15118CertificateManagementEnabledKeyValue() = 0; + virtual KeyValue getISO15118PnCEnabledKeyValue() = 0; + + virtual std::optional getCentralContractValidationAllowedKeyValue() = 0; + virtual std::optional getCertSigningRepeatTimesKeyValue() = 0; + virtual std::optional getCertSigningWaitMinimumKeyValue() = 0; + + virtual void setContractValidationOffline(bool contract_validation_offline) = 0; + virtual void setCentralContractValidationAllowed(bool central_contract_validation_allowed) = 0; + virtual void setCertSigningRepeatTimes(std::int32_t cert_signing_repeat_times) = 0; + virtual void setCertSigningWaitMinimum(std::int32_t cert_signing_wait_minimum) = 0; + virtual void setISO15118CertificateManagementEnabled(bool iso15118_certificate_management_enabled) = 0; + virtual void setISO15118PnCEnabled(bool iso15118_pnc_enabled) = 0; + + // California Pricing Requirements + virtual bool getCustomDisplayCostAndPriceEnabled() = 0; + + virtual TariffMessage getDefaultTariffMessage(bool offline) = 0; + + virtual std::optional getDefaultPrice() = 0; + virtual std::optional getDefaultPriceText(const std::string& language) = 0; + virtual std::optional getDisplayTimeOffset() = 0; + virtual std::optional getLanguage() = 0; + virtual std::optional getMultiLanguageSupportedLanguages() = 0; + virtual std::optional getNextTimeOffsetTransitionDateTime() = 0; + virtual std::optional getTimeOffsetNextTransition() = 0; + virtual std::optional getCustomIdleFeeAfterStop() = 0; + virtual std::optional getCustomMultiLanguageMessagesEnabled() = 0; + virtual std::optional getWaitForSetUserPriceTimeout() = 0; + virtual std::optional getPriceNumberOfDecimalsForCostValues() = 0; + + virtual KeyValue getCustomDisplayCostAndPriceEnabledKeyValue() = 0; + virtual KeyValue getDefaultPriceTextKeyValue(const std::string& language) = 0; + + virtual std::optional getCustomIdleFeeAfterStopKeyValue() = 0; + virtual std::optional getCustomMultiLanguageMessagesEnabledKeyValue() = 0; + virtual std::optional getDefaultPriceKeyValue() = 0; + virtual std::optional getDisplayTimeOffsetKeyValue() = 0; + virtual std::optional getLanguageKeyValue() = 0; + virtual std::optional getMultiLanguageSupportedLanguagesKeyValue() = 0; + virtual std::optional getNextTimeOffsetTransitionDateTimeKeyValue() = 0; + virtual std::optional getPriceNumberOfDecimalsForCostValuesKeyValue() = 0; + virtual std::optional getTimeOffsetNextTransitionKeyValue() = 0; + virtual std::optional getWaitForSetUserPriceTimeoutKeyValue() = 0; + + virtual std::optional> getAllDefaultPriceTextKeyValues() = 0; + + virtual void setCustomIdleFeeAfterStop(bool value) = 0; + virtual ConfigurationStatus setDefaultPrice(const std::string& value) = 0; + virtual ConfigurationStatus setDefaultPriceText(const CiString<50>& key, const CiString<500>& value) = 0; + virtual ConfigurationStatus setDisplayTimeOffset(const std::string& offset) = 0; + virtual void setLanguage(const std::string& language) = 0; + virtual ConfigurationStatus setNextTimeOffsetTransitionDateTime(const std::string& date_time) = 0; + virtual ConfigurationStatus setTimeOffsetNextTransition(const std::string& offset) = 0; + virtual void setWaitForSetUserPriceTimeout(std::int32_t wait_for_set_user_price_timeout) = 0; + + // Signed Meter Values + + // Custom + virtual std::optional getCustomKeyValue(const CiString<50>& key) = 0; + virtual std::optional get(const CiString<50>& key) = 0; + virtual std::vector get_all_key_value() = 0; + + virtual ConfigurationStatus setCustomKey(const CiString<50>& key, const CiString<500>& value, bool force) = 0; + virtual std::optional set(const CiString<50>& key, const CiString<500>& value) = 0; +}; + +} // namespace ocpp::v16 + +#endif // OCPP_V16_CHARGE_POINT_CONFIGURATION_INTERFACE_HPP diff --git a/lib/everest/ocpp/include/ocpp/v16/charge_point_impl.hpp b/lib/everest/ocpp/include/ocpp/v16/charge_point_impl.hpp index ce3abd784d..f400d15f07 100644 --- a/lib/everest/ocpp/include/ocpp/v16/charge_point_impl.hpp +++ b/lib/everest/ocpp/include/ocpp/v16/charge_point_impl.hpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -87,6 +87,7 @@ namespace v16 { /// \brief Contains a ChargePoint implementation compatible with OCPP-J 1.6 class ChargePointImpl : ocpp::ChargingStationBase { private: + std::unique_ptr configuration; BootReasonEnum bootreason; ChargePointConnectionState connection_state; bool boot_notification_callerror; @@ -111,7 +112,6 @@ class ChargePointImpl : ocpp::ChargingStationBase { std::set allowed_message_types; std::mutex allowed_message_types_mutex; std::unique_ptr status; - std::shared_ptr configuration; std::shared_ptr database_handler; std::unique_ptr boot_notification_timer; std::unique_ptr heartbeat_timer; @@ -399,14 +399,7 @@ class ChargePointImpl : ocpp::ChargingStationBase { public: /// \brief The main entrypoint for libOCPP for OCPP 1.6 - /// \param config a nlohmann json config object that contains the libocpp 1.6 config. There are example configs that - /// work with a SteVe installation running in Docker, for example: config/v16/config-docker.json - /// \param share_path This path contains the following files and directories and is installed by the libocpp install - /// target - /// \param user_config_path this points to a "user config", which we call a configuration file that's merged with - /// the config that's provided in the "config" parameter. Here you can add, remove and overwrite settings without - /// modifying the config passed in the first parameter directly. This is also used by libocpp to persistently modify - /// config entries that are changed by the CSMS that should persist across restarts + /// \param config unique pointer to a configuration object /// \param database_path this points to the location of the sqlite database that libocpp uses to keep track of /// connector availability, the authorization cache and auth list, charging profiles and transaction data /// \param sql_init_path this points to the init.sql file which contains the database schema used by libocpp for its @@ -417,9 +410,9 @@ class ChargePointImpl : ocpp::ChargingStationBase { /// also available) configuration keys in the "Internal" section of the config file. Please note that this is /// intended for debugging purposes only as it logs all communication, including authentication messages. /// \param evse_security Pointer to evse_security that manages security related operations - explicit ChargePointImpl(const std::string& config, const fs::path& share_path, const fs::path& user_config_path, - const fs::path& database_path, const fs::path& sql_init_path, - const fs::path& message_log_path, const std::shared_ptr evse_security, + explicit ChargePointImpl(std::unique_ptr config, const fs::path& database_path, + const fs::path& sql_init_path, const fs::path& message_log_path, + const std::shared_ptr evse_security, const std::optional security_configuration); ~ChargePointImpl() override = default; diff --git a/lib/everest/ocpp/include/ocpp/v16/known_keys.hpp b/lib/everest/ocpp/include/ocpp/v16/known_keys.hpp new file mode 100644 index 0000000000..c349e530ed --- /dev/null +++ b/lib/everest/ocpp/include/ocpp/v16/known_keys.hpp @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest + +#ifndef OCPP_V16_KNOWN_KEYS_HPP +#define OCPP_V16_KNOWN_KEYS_HPP + +#include +#include +#include + +namespace ocpp::v16::keys { + +// clang-format off +#define FOR_ALL_KEYS(key) \ +key(Core, AllowOfflineTxForUnknownId) \ +key(Core, AuthorizationCacheEnabled) \ +key(Core, AuthorizeRemoteTxRequests) \ +key(Core, BlinkRepeat) \ +key(Core, ClockAlignedDataInterval) \ +key(Core, ConnectionTimeOut) \ +key(Core, ConnectorPhaseRotation) \ +key(Core, ConnectorPhaseRotationMaxLength) \ +key(Core, GetConfigurationMaxKeys) \ +key(Core, HeartbeatInterval) \ +key(Core, LightIntensity) \ +key(Core, LocalAuthorizeOffline) \ +key(Core, LocalPreAuthorize) \ +key(Core, MaxEnergyOnInvalidId) \ +key(Core, MeterValuesAlignedData) \ +key(Core, MeterValuesAlignedDataMaxLength) \ +key(Core, MeterValueSampleInterval) \ +key(Core, MeterValuesSampledData) \ +key(Core, MeterValuesSampledDataMaxLength) \ +key(Core, MinimumStatusDuration) \ +key(Core, NumberOfConnectors) \ +key(Core, ResetRetries) \ +key(Core, StopTransactionOnEVSideDisconnect) \ +key(Core, StopTransactionOnInvalidId) \ +key(Core, StopTxnAlignedData) \ +key(Core, StopTxnAlignedDataMaxLength) \ +key(Core, StopTxnSampledData) \ +key(Core, StopTxnSampledDataMaxLength) \ +key(Core, SupportedFeatureProfiles) \ +key(Core, SupportedFeatureProfilesMaxLength) \ +key(Core, TransactionMessageAttempts) \ +key(Core, TransactionMessageRetryInterval) \ +key(Core, UnlockConnectorOnEVSideDisconnect) \ +key(Core, WebSocketPingInterval) \ +key(CostAndPrice, CustomDisplayCostAndPrice) \ +key(CostAndPrice, CustomIdleFeeAfterStop) \ +key(CostAndPrice, CustomMultiLanguageMessages) \ +key(CostAndPrice, DefaultPrice) \ +key(CostAndPrice, DefaultPriceText) \ +key(CostAndPrice, Language) \ +key(CostAndPrice, NextTimeOffsetTransitionDateTime) \ +key(CostAndPrice, NumberOfDecimalsForCostValues) \ +key(CostAndPrice, SupportedLanguages) \ +key(CostAndPrice, TimeOffset) \ +key(CostAndPrice, TimeOffsetNextTransition) \ +key(CostAndPrice, WaitForSetUserPriceTimeout) \ +key(FirmwareManagement, SupportedFileTransferProtocols) \ +key(Internal, AllowChargingProfileWithoutStartSchedule) \ +key(Internal, AuthorizeConnectorZeroOnConnectorOne) \ +key(Internal, CentralSystemURI) \ +key(Internal, ChargeBoxSerialNumber) \ +key(Internal, ChargePointId) \ +key(Internal, ChargePointModel) \ +key(Internal, ChargePointSerialNumber) \ +key(Internal, ChargePointVendor) \ +key(Internal, CompositeScheduleDefaultLimitAmps) \ +key(Internal, CompositeScheduleDefaultLimitWatts) \ +key(Internal, CompositeScheduleDefaultNumberPhases) \ +key(Internal, ConnectorEvseIds) \ +key(Internal, EnableTLSKeylog) \ +key(Internal, FirmwareVersion) \ +key(Internal, HostName) \ +key(Internal, ICCID) \ +key(Internal, IFace) \ +key(Internal, IgnoredProfilePurposesOffline) \ +key(Internal, IMSI) \ +key(Internal, LogMessages) \ +key(Internal, LogMessagesFormat) \ +key(Internal, LogMessagesRaw) \ +key(Internal, LogRotation) \ +key(Internal, LogRotationDateSuffix) \ +key(Internal, LogRotationMaximumFileCount) \ +key(Internal, LogRotationMaximumFileSize) \ +key(Internal, MaxCompositeScheduleDuration) \ +key(Internal, MaxMessageSize) \ +key(Internal, MessageQueueSizeThreshold) \ +key(Internal, MessageTypesDiscardForQueueing) \ +key(Internal, MeterPublicKeys) \ +key(Internal, MeterSerialNumber) \ +key(Internal, MeterType) \ +key(Internal, OcspRequestInterval) \ +key(Internal, QueueAllMessages) \ +key(Internal, RetryBackoffRandomRange) \ +key(Internal, RetryBackoffRepeatTimes) \ +key(Internal, RetryBackoffWaitMinimum) \ +key(Internal, SeccLeafSubjectCommonName) \ +key(Internal, SeccLeafSubjectCountry) \ +key(Internal, SeccLeafSubjectOrganization) \ +key(Internal, StopTransactionIfUnlockNotSupported) \ +key(Internal, SupplyVoltage) \ +key(Internal, SupportedChargingProfilePurposeTypes) \ +key(Internal, SupportedCiphers12) \ +key(Internal, SupportedCiphers13) \ +key(Internal, SupportedMeasurands) \ +key(Internal, TLSKeylogFile) \ +key(Internal, UseSslDefaultVerifyPaths) \ +key(Internal, UseTPM) \ +key(Internal, UseTPMSeccLeafCertificate) \ +key(Internal, VerifyCsmsAllowWildcards) \ +key(Internal, VerifyCsmsCommonName) \ +key(Internal, WaitForStopTransactionsOnResetTimeout) \ +key(Internal, WebsocketPingPayload) \ +key(Internal, WebsocketPongTimeout) \ +key(LocalAuthListManagement, LocalAuthListEnabled) \ +key(LocalAuthListManagement, LocalAuthListMaxLength) \ +key(LocalAuthListManagement, SendLocalListMaxLength) \ +key(PnC, CentralContractValidationAllowed) \ +key(PnC, CertSigningRepeatTimes) \ +key(PnC, CertSigningWaitMinimum) \ +key(PnC, ContractValidationOffline) \ +key(PnC, ISO15118CertificateManagementEnabled) \ +key(PnC, ISO15118PnCEnabled) \ +key(Reservation, ReserveConnectorZeroSupported) \ +key(Security, AdditionalRootCertificateCheck) \ +key(Security, AuthorizationKey) \ +key(Security, CertificateSignedMaxChainSize) \ +key(Security, CertificateStoreMaxLength) \ +key(Security, CpoName) \ +key(Security, DisableSecurityEventNotifications) \ +key(Security, SecurityProfile) \ +key(SmartCharging, ChargeProfileMaxStackLevel) \ +key(SmartCharging, ChargingScheduleAllowedChargingRateUnit) \ +key(SmartCharging, ChargingScheduleMaxPeriods) \ +key(SmartCharging, ConnectorSwitch3to1PhaseSupported) \ +key(SmartCharging, MaxChargingProfilesInstalled) + +// clang-format on + +#define VALUE(a, b) b, + +enum class valid_keys : std::uint8_t { + FOR_ALL_KEYS(VALUE) +}; + +enum class sections : std::uint8_t { + Core, + CostAndPrice, + FirmwareManagement, + Internal, + LocalAuthListManagement, + PnC, + Reservation, + Security, + SmartCharging, + Custom, +}; + +#undef VALUE + +std::optional convert(const std::string_view& str); +std::string_view convert(valid_keys key); +sections to_section(valid_keys key); +std::string_view to_section_string_view(valid_keys key); +bool is_readonly(valid_keys key); +inline bool is_in_section(sections section, valid_keys key) { + return to_section(key) == section; +} +bool is_hidden(valid_keys key); + +} // namespace ocpp::v16::keys + +#endif // OCPP_V16_KNOWN_KEYS_HPP diff --git a/lib/everest/ocpp/include/ocpp/v16/message_dispatcher.hpp b/lib/everest/ocpp/include/ocpp/v16/message_dispatcher.hpp index 774ebf8dba..f439fdfe3d 100644 --- a/lib/everest/ocpp/include/ocpp/v16/message_dispatcher.hpp +++ b/lib/everest/ocpp/include/ocpp/v16/message_dispatcher.hpp @@ -4,7 +4,7 @@ #pragma once #include -#include +#include namespace ocpp { namespace v16 { @@ -12,7 +12,7 @@ namespace v16 { class MessageDispatcher : public MessageDispatcherInterface { public: - MessageDispatcher(ocpp::MessageQueue& message_queue, ChargePointConfiguration& configuration, + MessageDispatcher(ocpp::MessageQueue& message_queue, ChargePointConfigurationInterface& configuration, std::atomic& registration_status) : message_queue(message_queue), configuration(configuration), registration_status(registration_status){}; void dispatch_call(const json& call, bool triggered = false) override; @@ -22,7 +22,7 @@ class MessageDispatcher : public MessageDispatcherInterface { private: ocpp::MessageQueue& message_queue; - ChargePointConfiguration& configuration; + ChargePointConfigurationInterface& configuration; std::atomic& registration_status; }; diff --git a/lib/everest/ocpp/include/ocpp/v16/smart_charging.hpp b/lib/everest/ocpp/include/ocpp/v16/smart_charging.hpp index 23b6ed17c2..0db1ac944c 100644 --- a/lib/everest/ocpp/include/ocpp/v16/smart_charging.hpp +++ b/lib/everest/ocpp/include/ocpp/v16/smart_charging.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include @@ -40,7 +40,7 @@ class SmartChargingHandler { private: std::map> connectors; std::shared_ptr database_handler; - ChargePointConfiguration& configuration; + ChargePointConfigurationInterface& configuration; std::map stack_level_charge_point_max_profiles_map; std::mutex charge_point_max_profiles_map_mutex; std::mutex tx_default_profiles_map_mutex; @@ -59,7 +59,8 @@ class SmartChargingHandler { public: SmartChargingHandler(std::map>& connectors, - std::shared_ptr database_handler, ChargePointConfiguration& configuration); + std::shared_ptr database_handler, + ChargePointConfigurationInterface& configuration); /// /// \brief validates the given \p profile according to the specification diff --git a/lib/everest/ocpp/include/ocpp/v16/utils.hpp b/lib/everest/ocpp/include/ocpp/v16/utils.hpp index 5645e5d372..d19178b290 100644 --- a/lib/everest/ocpp/include/ocpp/v16/utils.hpp +++ b/lib/everest/ocpp/include/ocpp/v16/utils.hpp @@ -8,9 +8,10 @@ #include #include -namespace ocpp { -namespace v16 { -namespace utils { +#include +#include + +namespace ocpp::v16::utils { size_t get_message_size(const ocpp::Call& call); @@ -21,8 +22,16 @@ void drop_transaction_data(size_t max_message_size, ocpp::Call split_string(char separator, const std::string& csl); + +/// \brief convert a vector into a comma separated list in a string +std::string to_csl(const std::vector& vec); +/// \brief convert a comma separated list in a string to a vector of strings +/// \note will not contain empty strings when a comma is repeated +std::vector from_csl(const std::string& csl); + +} // namespace ocpp::v16::utils #endif diff --git a/lib/everest/ocpp/lib/CMakeLists.txt b/lib/everest/ocpp/lib/CMakeLists.txt index 12f111075f..682c4b24bc 100644 --- a/lib/everest/ocpp/lib/CMakeLists.txt +++ b/lib/everest/ocpp/lib/CMakeLists.txt @@ -38,6 +38,9 @@ if(LIBOCPP_ENABLE_V16) ocpp/v16/charge_point_impl.cpp ocpp/v16/message_dispatcher.cpp ocpp/v16/smart_charging.cpp + ocpp/v16/known_keys.cpp + ocpp/v16/charge_point_configuration_base.cpp + ocpp/v16/charge_point_configuration_devicemodel.cpp ocpp/v16/charge_point_configuration.cpp ocpp/v16/charge_point_state_machine.cpp ocpp/v16/message_queue.cpp diff --git a/lib/everest/ocpp/lib/ocpp/v16/charge_point.cpp b/lib/everest/ocpp/lib/ocpp/v16/charge_point.cpp index d6e6b9f1e4..38eba192e2 100644 --- a/lib/everest/ocpp/lib/ocpp/v16/charge_point.cpp +++ b/lib/everest/ocpp/lib/ocpp/v16/charge_point.cpp @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest +#include #include #include @@ -14,9 +15,17 @@ ChargePoint::ChargePoint(const std::string& config, const fs::path& share_path, const fs::path& database_path, const fs::path& sql_init_path, const fs::path& message_log_path, const std::shared_ptr evse_security, const std::optional security_configuration) { - this->charge_point = - std::make_unique(config, share_path, user_config_path, database_path, sql_init_path, - message_log_path, evse_security, security_configuration); + auto configuration = std::make_unique(config, share_path, user_config_path); + this->charge_point = std::make_unique(std::move(configuration), database_path, sql_init_path, + message_log_path, evse_security, security_configuration); +} + +ChargePoint::ChargePoint(std::unique_ptr config, const fs::path& database_path, + const fs::path& sql_init_path, const fs::path& message_log_path, + const std::shared_ptr evse_security, + const std::optional security_configuration) { + this->charge_point = std::make_unique(std::move(config), database_path, sql_init_path, + message_log_path, evse_security, security_configuration); } ChargePoint::~ChargePoint() = default; diff --git a/lib/everest/ocpp/lib/ocpp/v16/charge_point_configuration.cpp b/lib/everest/ocpp/lib/ocpp/v16/charge_point_configuration.cpp index 73ca15cc20..45acf03886 100644 --- a/lib/everest/ocpp/lib/ocpp/v16/charge_point_configuration.cpp +++ b/lib/everest/ocpp/lib/ocpp/v16/charge_point_configuration.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include @@ -14,21 +13,13 @@ #include #include #include +#include -namespace ocpp { -namespace v16 { - -const size_t CONNECTOR_EVSE_IDS_MAX_LENGTH = 1000; -const size_t SECC_LEAF_SUBJECT_ORGANIZATION_MAX_LENGTH = 64; -const size_t SECC_LEAF_SUBJECT_COUNTRY_LENGTH = 2; -const size_t SECC_LEAF_SUBJECT_COMMON_NAME_MIN_LENGTH = 7; -const size_t SECC_LEAF_SUBJECT_COMMON_NAME_MAX_LENGTH = 64; -const size_t AUTHORIZATION_KEY_MIN_LENGTH = 8; -const std::int32_t MAX_WAIT_FOR_SET_USER_PRICE_TIMEOUT_MS = 30000; +namespace ocpp::v16 { ChargePointConfiguration::ChargePointConfiguration(const std::string& config, const fs::path& ocpp_main_path, const fs::path& user_config_path) : - user_config_path(user_config_path) { + ChargePointConfigurationBase(), user_config_path(user_config_path) { if (!fs::exists(this->user_config_path)) { EVLOG_critical << "User config file does not exist"; @@ -99,120 +90,33 @@ ChargePointConfiguration::ChargePointConfiguration(const std::string& config, co throw std::runtime_error("SupportedFeatureProfiles key is missing from config"); } - { - std::vector components; - auto profiles = this->getSupportedFeatureProfiles(); - if (!profiles.empty()) { - boost::split(components, profiles, boost::is_any_of(",")); - for (auto component : components) { - try { - this->supported_feature_profiles.insert( - conversions::string_to_supported_feature_profiles(component)); - } catch (const StringToEnumException& e) { - EVLOG_error << "Feature profile: \"" << component << "\" not recognized"; - throw std::runtime_error("Unknown component in SupportedFeatureProfiles config option."); - } - } - // add Security behind the scenes as supported feature profile - this->supported_feature_profiles.insert(conversions::string_to_supported_feature_profiles("Security")); + const auto supported_profiles_csl = getSupportedFeatureProfiles(); + ChargePointConfigurationBase::ProfilesSet initial_set; - // add Internal behind the scenes as supported feature profile - this->supported_feature_profiles.insert(conversions::string_to_supported_feature_profiles("Internal")); - - if (this->config.contains("PnC")) { - // add PnC behind the scenes as supported feature profile - this->supported_feature_profiles.insert(conversions::string_to_supported_feature_profiles("PnC")); - } - - if (this->config.contains("CostAndPrice")) { - // Add California Pricing Requirements behind the scenes as supported feature profile - this->supported_feature_profiles.insert( - conversions::string_to_supported_feature_profiles("CostAndPrice")); - } + if (this->config.contains("PnC")) { + // add PnC behind the scenes as supported feature profile + initial_set.insert(conversions::string_to_supported_feature_profiles("PnC")); + } - if (this->config.contains("Custom")) { - // add Custom behind the scenes as supported feature profile - this->supported_feature_profiles.insert(conversions::string_to_supported_feature_profiles("Custom")); - } - } + if (this->config.contains("CostAndPrice")) { + // Add California Pricing Requirements behind the scenes as supported feature profile + initial_set.insert(conversions::string_to_supported_feature_profiles("CostAndPrice")); } - if (this->supported_feature_profiles.count(SupportedFeatureProfiles::Core) == 0) { - throw std::runtime_error("Core profile not listed in SupportedFeatureProfiles. This is required."); + + if (this->config.contains("Custom")) { + // add Custom behind the scenes as supported feature profile + initial_set.insert(conversions::string_to_supported_feature_profiles("Custom")); } - this->init_supported_measurands(); + const auto supported_measurands_csl = getSupportedMeasurands(); + + initialise(initial_set, supported_profiles_csl, supported_measurands_csl); if (!this->validate_measurands(this->config)) { EVLOG_AND_THROW(std::runtime_error("Given Measurands of either MeterValuesAlignedData, MeterValuesSampledData, " "StopTxnAlignedData or StopTxnSampledData are invalid or do not match the " "Measurands configured in SupportedMeasurands")); } - - this->supported_message_types_from_charge_point = { - {SupportedFeatureProfiles::Core, - {MessageType::Authorize, MessageType::BootNotification, MessageType::ChangeAvailabilityResponse, - MessageType::ChangeConfigurationResponse, MessageType::ClearCacheResponse, MessageType::DataTransfer, - MessageType::DataTransferResponse, MessageType::GetConfigurationResponse, MessageType::Heartbeat, - MessageType::MeterValues, MessageType::RemoteStartTransactionResponse, - MessageType::RemoteStopTransactionResponse, MessageType::ResetResponse, MessageType::StartTransaction, - MessageType::StatusNotification, MessageType::StopTransaction, MessageType::UnlockConnectorResponse}}, - {SupportedFeatureProfiles::FirmwareManagement, - {MessageType::GetDiagnosticsResponse, MessageType::DiagnosticsStatusNotification, - MessageType::FirmwareStatusNotification, MessageType::UpdateFirmwareResponse}}, - {SupportedFeatureProfiles::LocalAuthListManagement, - {MessageType::GetLocalListVersionResponse, MessageType::SendLocalListResponse}}, - {SupportedFeatureProfiles::RemoteTrigger, {MessageType::TriggerMessageResponse}}, - {SupportedFeatureProfiles::Reservation, - {MessageType::CancelReservationResponse, MessageType::ReserveNowResponse}}, - {SupportedFeatureProfiles::SmartCharging, - {MessageType::ClearChargingProfileResponse, MessageType::GetCompositeScheduleResponse, - MessageType::SetChargingProfileResponse}}, - {SupportedFeatureProfiles::Security, - {MessageType::CertificateSignedResponse, MessageType::DeleteCertificateResponse, - MessageType::ExtendedTriggerMessageResponse, MessageType::GetInstalledCertificateIdsResponse, - MessageType::GetLogResponse, MessageType::InstallCertificateResponse, MessageType::LogStatusNotification, - MessageType::SecurityEventNotification, MessageType::SignCertificate, - MessageType::SignedFirmwareStatusNotification, MessageType::SignedUpdateFirmwareResponse}}}; - - this->supported_message_types_from_central_system = { - {SupportedFeatureProfiles::Core, - {MessageType::AuthorizeResponse, MessageType::BootNotificationResponse, MessageType::ChangeAvailability, - MessageType::ChangeConfiguration, MessageType::ClearCache, MessageType::DataTransfer, - MessageType::DataTransferResponse, MessageType::GetConfiguration, MessageType::HeartbeatResponse, - MessageType::MeterValuesResponse, MessageType::RemoteStartTransaction, MessageType::RemoteStopTransaction, - MessageType::Reset, MessageType::StartTransactionResponse, MessageType::StatusNotificationResponse, - MessageType::StopTransactionResponse, MessageType::UnlockConnector}}, - {SupportedFeatureProfiles::FirmwareManagement, - {MessageType::GetDiagnostics, MessageType::DiagnosticsStatusNotificationResponse, - MessageType::FirmwareStatusNotificationResponse, MessageType::UpdateFirmware}}, - {SupportedFeatureProfiles::LocalAuthListManagement, - {MessageType::GetLocalListVersion, MessageType::SendLocalList}}, - {SupportedFeatureProfiles::RemoteTrigger, {MessageType::TriggerMessage}}, - {SupportedFeatureProfiles::Reservation, {MessageType::CancelReservation, MessageType::ReserveNow}}, - {SupportedFeatureProfiles::SmartCharging, - {MessageType::ClearChargingProfile, MessageType::GetCompositeSchedule, MessageType::SetChargingProfile}}, - {SupportedFeatureProfiles::Security, - {MessageType::CertificateSigned, MessageType::DeleteCertificate, MessageType::ExtendedTriggerMessage, - MessageType::GetInstalledCertificateIds, MessageType::GetLog, MessageType::InstallCertificate, - MessageType::LogStatusNotificationResponse, MessageType::SecurityEventNotificationResponse, - MessageType::SignCertificateResponse, MessageType::SignedFirmwareStatusNotificationResponse, - MessageType::SignedUpdateFirmware}}}; - - for (auto feature_profile : this->supported_feature_profiles) { - this->supported_message_types_sending.insert( - this->supported_message_types_from_charge_point[feature_profile].begin(), - this->supported_message_types_from_charge_point[feature_profile].end()); - - this->supported_message_types_receiving.insert( - this->supported_message_types_from_central_system[feature_profile].begin(), - this->supported_message_types_from_central_system[feature_profile].end()); - } - - // those MessageTypes should still be accepted and implement their individual handling in case the feature profile - // is not supported - this->supported_message_types_receiving.insert(MessageType::GetLocalListVersion); - this->supported_message_types_receiving.insert(MessageType::SendLocalList); - this->supported_message_types_receiving.insert(MessageType::ReserveNow); } json ChargePointConfiguration::get_user_config() { @@ -227,7 +131,7 @@ json ChargePointConfiguration::get_user_config() { return json({}, true); } -void ChargePointConfiguration::setInUserConfig(std::string profile, std::string key, const json value) { +void ChargePointConfiguration::setInUserConfig(const std::string& profile, const std::string& key, const json value) { // write to a separate file to minimise corruption and data loss; then rename namespace fs = std::filesystem; @@ -247,62 +151,6 @@ void ChargePointConfiguration::setInUserConfig(std::string profile, std::string } } -namespace { -std::string to_csl(const std::vector& vec) { - std::string csl; - for (auto it = vec.begin(); it != vec.end(); ++it) { - if (it != vec.begin()) { - csl += ","; - } - csl += *it; - } - return csl; -} -} // namespace - -void ChargePointConfiguration::init_supported_measurands() { - const auto _supported_measurands = ocpp::split_string(this->config["Internal"]["SupportedMeasurands"], ','); - for (const auto& measurand : _supported_measurands) { - try { - const auto _measurand = conversions::string_to_measurand(measurand); - switch (_measurand) { - case Measurand::Energy_Active_Export_Register: - case Measurand::Energy_Active_Import_Register: - case Measurand::Energy_Reactive_Export_Register: - case Measurand::Energy_Reactive_Import_Register: - case Measurand::Energy_Active_Export_Interval: - case Measurand::Energy_Active_Import_Interval: - case Measurand::Energy_Reactive_Export_Interval: - case Measurand::Energy_Reactive_Import_Interval: - case Measurand::Power_Active_Export: - case Measurand::Power_Active_Import: - case Measurand::Voltage: - case Measurand::Frequency: - case Measurand::Power_Reactive_Export: - case Measurand::Power_Reactive_Import: - this->supported_measurands[_measurand] = {Phase::L1, Phase::L2, Phase::L3}; - break; - case Measurand::Current_Import: - case Measurand::Current_Export: - this->supported_measurands[_measurand] = {Phase::L1, Phase::L2, Phase::L3, Phase::N}; - break; - case Measurand::Power_Factor: - case Measurand::Current_Offered: - case Measurand::Power_Offered: - case Measurand::Temperature: - case Measurand::SoC: - case Measurand::RPM: - this->supported_measurands[_measurand] = {}; - break; - default: - EVLOG_AND_THROW(std::runtime_error("Given SupportedMeasurands are invalid")); - } - } catch (const StringToEnumException& o) { - EVLOG_AND_THROW(std::runtime_error("Given SupportedMeasurands are invalid")); - } - } -} - void ChargePointConfiguration::setChargepointInformationProperty(json& user_config, const std::string& key, const std::optional& value) { // std::nullopt value leaves the current value untouched @@ -789,7 +637,7 @@ KeyValue ChargePointConfiguration::getLogMessagesFormatKeyValue() { KeyValue kv; kv.key = "LogMessagesFormat"; kv.readonly = true; - kv.value.emplace(to_csl(this->getLogMessagesFormat())); + kv.value.emplace(utils::to_csl(this->getLogMessagesFormat())); return kv; } @@ -837,7 +685,7 @@ KeyValue ChargePointConfiguration::getSupportedChargingProfilePurposeTypesKeyVal EVLOG_warning << "Could not convert element of SupportedChargingProfilePurposeTypes to string"; } } - kv.value.emplace(to_csl(purpose_types)); + kv.value.emplace(utils::to_csl(purpose_types)); return kv; } @@ -857,7 +705,7 @@ std::optional ChargePointConfiguration::getIgnoredProfilePurposesOffli EVLOG_warning << "Could not convert element of IgnoredProfilePurposesOffline to string"; } } - kv.value.emplace(to_csl(purpose_types)); + kv.value.emplace(utils::to_csl(purpose_types)); return kv; } @@ -1021,7 +869,7 @@ KeyValue ChargePointConfiguration::getRetryBackoffWaitMinimumKeyValue() { return kv; } -std::vector ChargePointConfiguration::csv_to_measurand_with_phase_vector(std::string csv) { +std::vector ChargePointConfiguration::csv_to_measurand_with_phase_vector(const std::string& csv) { std::vector components; boost::split(components, csv, boost::is_any_of(",")); @@ -1083,25 +931,7 @@ bool ChargePointConfiguration::validate_measurands(const json& config) { return true; } -namespace { -bool validate_connector_evse_ids(const std::string& value) { - if (value.length() > CONNECTOR_EVSE_IDS_MAX_LENGTH) { - return false; - } - - // this fullfills parts of HUB-24-003 of Requirements EVSE Check PnC with ISO15118-2 v4 - const auto evse_ids = split_string(value, ','); - for (const auto& evse_id : evse_ids) { - if (evse_id.size() < 7 or evse_id.size() > 37) { - EVLOG_warning << "Attempting to set ConnectorEvseIds to invalid value: " << evse_id; - return false; - } - } - return true; -} -} // namespace - -bool ChargePointConfiguration::measurands_supported(std::string csv) { +bool ChargePointConfiguration::measurands_supported(const std::string& csv) { if (csv.empty()) { return true; @@ -1366,7 +1196,7 @@ KeyValue ChargePointConfiguration::getConnectionTimeOutKeyValue() { std::string ChargePointConfiguration::getConnectorPhaseRotation() { return this->config["Core"]["ConnectorPhaseRotation"]; } -void ChargePointConfiguration::setConnectorPhaseRotation(std::string connector_phase_rotation) { +void ChargePointConfiguration::setConnectorPhaseRotation(const std::string& connector_phase_rotation) { this->config["Core"]["ConnectorPhaseRotation"] = connector_phase_rotation; this->setInUserConfig("Core", "ConnectorPhaseRotation", connector_phase_rotation); } @@ -1517,7 +1347,7 @@ std::optional ChargePointConfiguration::getMaxEnergyOnInvalidIdKeyValu std::string ChargePointConfiguration::getMeterValuesAlignedData() { return this->config["Core"]["MeterValuesAlignedData"]; } -bool ChargePointConfiguration::setMeterValuesAlignedData(std::string meter_values_aligned_data) { +bool ChargePointConfiguration::setMeterValuesAlignedData(const std::string& meter_values_aligned_data) { if (!this->measurands_supported(meter_values_aligned_data)) { return false; } @@ -1561,7 +1391,7 @@ std::optional ChargePointConfiguration::getMeterValuesAlignedDataMaxLe std::string ChargePointConfiguration::getMeterValuesSampledData() { return this->config["Core"]["MeterValuesSampledData"]; } -bool ChargePointConfiguration::setMeterValuesSampledData(std::string meter_values_sampled_data) { +bool ChargePointConfiguration::setMeterValuesSampledData(const std::string& meter_values_sampled_data) { if (!this->measurands_supported(meter_values_sampled_data)) { return false; } @@ -1736,7 +1566,7 @@ KeyValue ChargePointConfiguration::getStopTransactionOnInvalidIdKeyValue() { std::string ChargePointConfiguration::getStopTxnAlignedData() { return this->config["Core"]["StopTxnAlignedData"]; } -bool ChargePointConfiguration::setStopTxnAlignedData(std::string stop_txn_aligned_data) { +bool ChargePointConfiguration::setStopTxnAlignedData(const std::string& stop_txn_aligned_data) { if (!this->measurands_supported(stop_txn_aligned_data)) { return false; } @@ -1777,7 +1607,7 @@ std::optional ChargePointConfiguration::getStopTxnAlignedDataMaxLength std::string ChargePointConfiguration::getStopTxnSampledData() { return this->config["Core"]["StopTxnSampledData"]; } -bool ChargePointConfiguration::setStopTxnSampledData(std::string stop_txn_sampled_data) { +bool ChargePointConfiguration::setStopTxnSampledData(const std::string& stop_txn_sampled_data) { if (!this->measurands_supported(stop_txn_sampled_data)) { return false; } @@ -2090,7 +1920,6 @@ std::optional ChargePointConfiguration::getAuthorizationKey() { return authorization_key; } -namespace { std::string hexToString(const std::string& s) { std::string str; for (size_t i = 0; i < s.length(); i += 2) { @@ -2101,26 +1930,7 @@ std::string hexToString(const std::string& s) { return str; } -bool isHexNotation(const std::string& s) { - const bool is_hex = s.size() > 2 and s.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos; - - if (is_hex) { - // check if every char is printable - for (size_t i = 0; i < s.length(); i += 2) { - const std::string byte = s.substr(i, 2); - const char chr = (char)(int)strtol(byte.c_str(), nullptr, 16); - if ((chr < 0x20 or chr > 0x7e) and chr != 0xa) { - return false; - } - } - } else { - return false; - } - return true; -} -} // namespace - -void ChargePointConfiguration::setAuthorizationKey(std::string authorization_key) { +void ChargePointConfiguration::setAuthorizationKey(const std::string& authorization_key) { // TODO(piet): SecurityLog entry @@ -2135,7 +1945,8 @@ void ChargePointConfiguration::setAuthorizationKey(std::string authorization_key this->setInUserConfig("Security", "AuthorizationKey", str); } -bool ChargePointConfiguration::isConnectorPhaseRotationValid(std::string str) { +bool ChargePointConfiguration::isConnectorPhaseRotationValid(const std::string& phase_rotation) { + std::string str(phase_rotation); std::vector elements; str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end()); @@ -2174,50 +1985,6 @@ bool ChargePointConfiguration::isConnectorPhaseRotationValid(std::string str) { return true; } -namespace { -bool checkTimeOffset(const std::string& offset) { - const std::vector times = split_string(offset, ':'); - if (times.size() != 2) { - EVLOG_error << "Could not set display time offset: format not correct (should be something like " - "\"-05:00\", but is " - << offset << ")"; - return false; - } - try { - // Check if strings are numbers. - const std::int32_t hours = std::stoi(times.at(0)); - const std::int32_t minutes = std::stoi(times.at(1)); - - // And check if numbers are valid. - if (hours < -24 or hours > 24) { - EVLOG_error << "Could not set display time offset: hours should be between -24 and +24, but is " - << times.at(0); - return false; - } - - if (minutes < 0 or minutes > 59) { - EVLOG_error << "Could not set display time offset: minutes should be between 0 and 59, but is " - << times.at(1); - return false; - } - - } catch (const std::exception& e) { - EVLOG_error << "Could not set display time offset: format not correct (should be something " - "like \"-19:15\", but is " - << offset << "): " << e.what(); - return false; - } - - return true; -} - -bool isBool(const std::string& str) { - std::string out = str; - std::transform(out.begin(), out.end(), out.begin(), ::tolower); - return out == "true" || out == "false"; -} -} // namespace - std::optional ChargePointConfiguration::getAuthorizationKeyKeyValue() { std::optional enabled_kv = std::nullopt; std::optional enabled = std::nullopt; @@ -2288,9 +2055,9 @@ std::optional ChargePointConfiguration::getCpoName() { return cpo_name; } -void ChargePointConfiguration::setCpoName(std::string cpo_name) { - this->config["Security"]["CpoName"] = cpo_name; - this->setInUserConfig("Security", "CpoName", cpo_name); +void ChargePointConfiguration::setCpoName(const std::string& cpoName) { + this->config["Security"]["CpoName"] = cpoName; + this->setInUserConfig("Security", "CpoName", cpoName); } std::optional ChargePointConfiguration::getCpoNameKeyValue() { @@ -2393,8 +2160,7 @@ bool ChargePointConfiguration::getISO15118CertificateManagementEnabled() { return this->config["PnC"]["ISO15118CertificateManagementEnabled"]; } -void ChargePointConfiguration::setISO15118CertificateManagementEnabled( - const bool iso15118_certificate_management_enabled) { +void ChargePointConfiguration::setISO15118CertificateManagementEnabled(bool iso15118_certificate_management_enabled) { this->config["PnC"]["ISO15118CertificateManagementEnabled"] = iso15118_certificate_management_enabled; this->setInUserConfig("PnC", "ISO15118CertificateManagementEnabled", iso15118_certificate_management_enabled); } @@ -2411,7 +2177,7 @@ bool ChargePointConfiguration::getISO15118PnCEnabled() { return this->config["PnC"]["ISO15118PnCEnabled"]; } -void ChargePointConfiguration::setISO15118PnCEnabled(const bool iso15118_pnc_enabled) { +void ChargePointConfiguration::setISO15118PnCEnabled(bool iso15118_pnc_enabled) { this->config["PnC"]["ISO15118PnCEnabled"] = iso15118_pnc_enabled; this->setInUserConfig("PnC", "ISO15118PnCEnabled", iso15118_pnc_enabled); } @@ -2432,7 +2198,7 @@ std::optional ChargePointConfiguration::getCentralContractValidationAllowe return central_contract_validation_allowed; } -void ChargePointConfiguration::setCentralContractValidationAllowed(const bool central_contract_validation_allowed) { +void ChargePointConfiguration::setCentralContractValidationAllowed(bool central_contract_validation_allowed) { if (this->getCentralContractValidationAllowed() != std::nullopt) { this->config["PnC"]["CentralContractValidationAllowed"] = central_contract_validation_allowed; this->setInUserConfig("PnC", "CentralContractValidationAllowed", central_contract_validation_allowed); @@ -2510,7 +2276,7 @@ bool ChargePointConfiguration::getContractValidationOffline() { return this->config["PnC"]["ContractValidationOffline"]; } -void ChargePointConfiguration::setContractValidationOffline(const bool contract_validation_offline) { +void ChargePointConfiguration::setContractValidationOffline(bool contract_validation_offline) { this->config["PnC"]["ContractValidationOffline"] = contract_validation_offline; this->setInUserConfig("PnC", "ContractValidationOffline", contract_validation_offline); } @@ -2660,7 +2426,7 @@ std::optional ChargePointConfiguration::getAllowChargingProfileWithoutStar return allow; } -void ChargePointConfiguration::setAllowChargingProfileWithoutStartSchedule(const bool allow) { +void ChargePointConfiguration::setAllowChargingProfileWithoutStartSchedule(bool allow) { if (this->getAllowChargingProfileWithoutStartSchedule() != std::nullopt) { this->config["Internal"]["AllowChargingProfileWithoutStartSchedule"] = allow; this->setInUserConfig("Internal", "AllowChargingProfileWithoutStartSchedule", allow); @@ -2991,7 +2757,7 @@ std::optional ChargePointConfiguration::getDisplayTimeOffset() { } ConfigurationStatus ChargePointConfiguration::setDisplayTimeOffset(const std::string& offset) { - if (!checkTimeOffset(offset)) { + if (!isTimeOffset(offset)) { return ConfigurationStatus::Rejected; } this->config["CostAndPrice"]["TimeOffset"] = offset; @@ -3055,7 +2821,7 @@ std::optional ChargePointConfiguration::getTimeOffsetNextTransition } ConfigurationStatus ChargePointConfiguration::setTimeOffsetNextTransition(const std::string& offset) { - if (!checkTimeOffset(offset)) { + if (!isTimeOffset(offset)) { return ConfigurationStatus::Rejected; } this->config["CostAndPrice"]["TimeOffsetNextTransition"] = offset; @@ -3084,7 +2850,7 @@ std::optional ChargePointConfiguration::getCustomIdleFeeAfterStop() { return std::nullopt; } -void ChargePointConfiguration::setCustomIdleFeeAfterStop(const bool& value) { +void ChargePointConfiguration::setCustomIdleFeeAfterStop(bool value) { this->config["CostAndPrice"]["CustomIdleFeeAfterStop"] = value; this->setInUserConfig("CostAndPrice", "CustomIdleFeeAfterStop", value); } @@ -3181,7 +2947,7 @@ std::optional ChargePointConfiguration::getWaitForSetUserPriceTimeoutK return result; } -std::optional ChargePointConfiguration::getPublicKeyKeyValue(const uint32_t connector_id) { +std::optional ChargePointConfiguration::getPublicKeyKeyValue(const std::uint32_t connector_id) { if (!this->config["Internal"].contains("MeterPublicKeys")) { return std::nullopt; } @@ -3228,7 +2994,7 @@ std::optional> ChargePointConfiguration::getAllMeterPublic return key_values; } -bool ChargePointConfiguration::setMeterPublicKey(const int32_t connector_id, const std::string& public_key_pem) { +bool ChargePointConfiguration::setMeterPublicKey(const std::int32_t connector_id, const std::string& public_key_pem) { if (connector_id > this->getNumberOfConnectors() or connector_id < 1) { EVLOG_warning << "Cannot set MeterPublicKey for connector " << connector_id << ", because the connector id does not exist."; @@ -3273,8 +3039,8 @@ std::optional ChargePointConfiguration::getLanguageKeyValue() { } // Custom -std::optional ChargePointConfiguration::getCustomKeyValue(CiString<50> key) { - const std::lock_guard lock(this->configuration_mutex); +std::optional ChargePointConfiguration::getCustomKeyValue(const CiString<50>& key) { + std::lock_guard lock(this->configuration_mutex); if (!this->config["Custom"].contains(key.get())) { return std::nullopt; } @@ -3294,7 +3060,8 @@ std::optional ChargePointConfiguration::getCustomKeyValue(CiString<50> } } -ConfigurationStatus ChargePointConfiguration::setCustomKey(CiString<50> key, CiString<500> value, bool force) { +ConfigurationStatus ChargePointConfiguration::setCustomKey(const CiString<50>& key, const CiString<500>& value, + bool force) { const auto kv = this->getCustomKeyValue(key); if (!kv.has_value() or (kv.value().readonly and !force)) { return ConfigurationStatus::Rejected; @@ -3330,13 +3097,6 @@ ConfigurationStatus ChargePointConfiguration::setCustomKey(CiString<50> key, CiS return ConfigurationStatus::Accepted; } -void ChargePointConfiguration::setCentralSystemURI(std::string ocpp_uri) { - EVLOG_warning << "CentralSystemURI changed to: " << ocpp_uri; - this->config["Internal"]["CentralSystemURI"] = ocpp_uri; - this->setInUserConfig("Internal", "CentralSystemURI", ocpp_uri); -} - -namespace { std::optional parse_meter_public_key_index(const std::string& input) { const std::string prefix = "MeterPublicKey["; const std::string suffix = "]"; @@ -3377,10 +3137,15 @@ std::optional parse_meter_public_key_index(const std::string& input) { } return static_cast(temp); } -} // namespace -std::optional ChargePointConfiguration::get(CiString<50> key) { - const std::lock_guard lock(this->configuration_mutex); +void ChargePointConfiguration::setCentralSystemURI(const std::string& centralSystemUri) { + EVLOG_warning << "CentralSystemURI changed to: " << centralSystemUri; + this->config["Internal"]["CentralSystemURI"] = centralSystemUri; + this->setInUserConfig("Internal", "CentralSystemURI", centralSystemUri); +} + +std::optional ChargePointConfiguration::get(const CiString<50>& key) { + std::lock_guard lock(this->configuration_mutex); // Internal Profile if (key == "ChargePointId") { return this->getChargePointIdKeyValue(); @@ -3823,8 +3588,8 @@ std::vector ChargePointConfiguration::get_all_key_value() { return all; } -std::optional ChargePointConfiguration::set(CiString<50> key, CiString<500> value) { - const std::lock_guard lock(this->configuration_mutex); +std::optional ChargePointConfiguration::set(const CiString<50>& key, const CiString<500>& value) { + std::lock_guard lock(this->configuration_mutex); if (key == "IgnoredProfilePurposesOffline") { if (this->setIgnoredProfilePurposesOffline(value) == false) { return ConfigurationStatus::Rejected; @@ -4248,7 +4013,7 @@ std::optional ChargePointConfiguration::set(CiString<50> ke } } else if (key == "ConnectorEvseIds") { if (this->getConnectorEvseIds().has_value()) { - if (validate_connector_evse_ids(value.get())) { + if (areValidEvseIds(value.get())) { this->setConnectorEvseIds(value.get()); } else { return ConfigurationStatus::Rejected; @@ -4321,5 +4086,4 @@ std::optional ChargePointConfiguration::set(CiString<50> ke return ConfigurationStatus::Accepted; } -} // namespace v16 -} // namespace ocpp +} // namespace ocpp::v16 diff --git a/lib/everest/ocpp/lib/ocpp/v16/charge_point_configuration_base.cpp b/lib/everest/ocpp/lib/ocpp/v16/charge_point_configuration_base.cpp new file mode 100644 index 0000000000..e7e11a0d83 --- /dev/null +++ b/lib/everest/ocpp/lib/ocpp/v16/charge_point_configuration_base.cpp @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest + +#include +#include + +#include +#include +#include + +namespace ocpp::v16 { + +const ChargePointConfigurationBase::FeaturesMap + ChargePointConfigurationBase::supported_message_types_from_charge_point = { + {SupportedFeatureProfiles::Core, + {MessageType::Authorize, MessageType::BootNotification, MessageType::ChangeAvailabilityResponse, + MessageType::ChangeConfigurationResponse, MessageType::ClearCacheResponse, MessageType::DataTransfer, + MessageType::DataTransferResponse, MessageType::GetConfigurationResponse, MessageType::Heartbeat, + MessageType::MeterValues, MessageType::RemoteStartTransactionResponse, + MessageType::RemoteStopTransactionResponse, MessageType::ResetResponse, MessageType::StartTransaction, + MessageType::StatusNotification, MessageType::StopTransaction, MessageType::UnlockConnectorResponse}}, + {SupportedFeatureProfiles::FirmwareManagement, + {MessageType::GetDiagnosticsResponse, MessageType::DiagnosticsStatusNotification, + MessageType::FirmwareStatusNotification, MessageType::UpdateFirmwareResponse}}, + {SupportedFeatureProfiles::LocalAuthListManagement, + {MessageType::GetLocalListVersionResponse, MessageType::SendLocalListResponse}}, + {SupportedFeatureProfiles::RemoteTrigger, {MessageType::TriggerMessageResponse}}, + {SupportedFeatureProfiles::Reservation, + {MessageType::CancelReservationResponse, MessageType::ReserveNowResponse}}, + {SupportedFeatureProfiles::SmartCharging, + {MessageType::ClearChargingProfileResponse, MessageType::GetCompositeScheduleResponse, + MessageType::SetChargingProfileResponse}}, + {SupportedFeatureProfiles::Security, + {MessageType::CertificateSignedResponse, MessageType::DeleteCertificateResponse, + MessageType::ExtendedTriggerMessageResponse, MessageType::GetInstalledCertificateIdsResponse, + MessageType::GetLogResponse, MessageType::InstallCertificateResponse, MessageType::LogStatusNotification, + MessageType::SecurityEventNotification, MessageType::SignCertificate, + MessageType::SignedFirmwareStatusNotification, MessageType::SignedUpdateFirmwareResponse}}}; + +const ChargePointConfigurationBase::FeaturesMap + ChargePointConfigurationBase::supported_message_types_from_central_system = { + {SupportedFeatureProfiles::Core, + {MessageType::AuthorizeResponse, MessageType::BootNotificationResponse, MessageType::ChangeAvailability, + MessageType::ChangeConfiguration, MessageType::ClearCache, MessageType::DataTransfer, + MessageType::DataTransferResponse, MessageType::GetConfiguration, MessageType::HeartbeatResponse, + MessageType::MeterValuesResponse, MessageType::RemoteStartTransaction, MessageType::RemoteStopTransaction, + MessageType::Reset, MessageType::StartTransactionResponse, MessageType::StatusNotificationResponse, + MessageType::StopTransactionResponse, MessageType::UnlockConnector}}, + {SupportedFeatureProfiles::FirmwareManagement, + {MessageType::GetDiagnostics, MessageType::DiagnosticsStatusNotificationResponse, + MessageType::FirmwareStatusNotificationResponse, MessageType::UpdateFirmware}}, + {SupportedFeatureProfiles::LocalAuthListManagement, + {MessageType::GetLocalListVersion, MessageType::SendLocalList}}, + {SupportedFeatureProfiles::RemoteTrigger, {MessageType::TriggerMessage}}, + {SupportedFeatureProfiles::Reservation, {MessageType::CancelReservation, MessageType::ReserveNow}}, + {SupportedFeatureProfiles::SmartCharging, + {MessageType::ClearChargingProfile, MessageType::GetCompositeSchedule, MessageType::SetChargingProfile}}, + {SupportedFeatureProfiles::Security, + {MessageType::CertificateSigned, MessageType::DeleteCertificate, MessageType::ExtendedTriggerMessage, + MessageType::GetInstalledCertificateIds, MessageType::GetLog, MessageType::InstallCertificate, + MessageType::LogStatusNotificationResponse, MessageType::SecurityEventNotificationResponse, + MessageType::SignCertificateResponse, MessageType::SignedFirmwareStatusNotificationResponse, + MessageType::SignedUpdateFirmware}}}; + +void ChargePointConfigurationBase::initialise(const ProfilesSet& initial_set, + const std::optional& supported_profiles_csl, + const std::optional& supported_measurands_csl) { + supported_feature_profiles = initial_set; + + if (supported_profiles_csl) { + const auto supported_profiles = utils::from_csl(supported_profiles_csl.value()); + for (const auto& component : supported_profiles) { + try { + supported_feature_profiles.insert(conversions::string_to_supported_feature_profiles(component)); + } catch (const StringToEnumException& e) { + EVLOG_error << "Feature profile: \"" << component << "\" not recognized"; + throw std::runtime_error("Unknown component in SupportedFeatureProfiles config option."); + } + } + } + + // add Security behind the scenes as supported feature profile + supported_feature_profiles.insert(conversions::string_to_supported_feature_profiles("Security")); + + // add Internal behind the scenes as supported feature profile + supported_feature_profiles.insert(conversions::string_to_supported_feature_profiles("Internal")); + + // check Core profile is included + if (const auto it = supported_feature_profiles.find(SupportedFeatureProfiles::Core); + it == supported_feature_profiles.end()) { + throw std::runtime_error("Core profile not listed in SupportedFeatureProfiles. This is required."); + } + + // add supported messages based on supported features + for (const auto& feature_profile : supported_feature_profiles) { + if (const auto it = supported_message_types_from_charge_point.find(feature_profile); + it != supported_message_types_from_charge_point.end()) { + supported_message_types_sending.insert(it->second.begin(), it->second.end()); + } + + if (const auto it = supported_message_types_from_central_system.find(feature_profile); + it != supported_message_types_from_central_system.end()) { + supported_message_types_receiving.insert(it->second.begin(), it->second.end()); + } + } + + // those MessageTypes should still be accepted and implement their individual handling in case the feature profile + // is not supported + supported_message_types_receiving.insert(MessageType::GetLocalListVersion); + supported_message_types_receiving.insert(MessageType::SendLocalList); + supported_message_types_receiving.insert(MessageType::ReserveNow); + + // populate supported_measurands + + if (supported_measurands_csl) { + const auto supported = utils::from_csl(supported_measurands_csl.value()); + for (const auto& measurand : supported) { + try { + const auto measurand_type = conversions::string_to_measurand(measurand); + switch (measurand_type) { + case Measurand::Energy_Active_Export_Register: + case Measurand::Energy_Active_Import_Register: + case Measurand::Energy_Reactive_Export_Register: + case Measurand::Energy_Reactive_Import_Register: + case Measurand::Energy_Active_Export_Interval: + case Measurand::Energy_Active_Import_Interval: + case Measurand::Energy_Reactive_Export_Interval: + case Measurand::Energy_Reactive_Import_Interval: + case Measurand::Power_Active_Export: + case Measurand::Power_Active_Import: + case Measurand::Voltage: + case Measurand::Frequency: + case Measurand::Power_Reactive_Export: + case Measurand::Power_Reactive_Import: + supported_measurands[measurand_type] = {Phase::L1, Phase::L2, Phase::L3}; + break; + case Measurand::Current_Import: + case Measurand::Current_Export: + supported_measurands[measurand_type] = {Phase::L1, Phase::L2, Phase::L3, Phase::N}; + break; + case Measurand::Power_Factor: + case Measurand::Current_Offered: + case Measurand::Power_Offered: + case Measurand::Temperature: + case Measurand::SoC: + case Measurand::RPM: + supported_measurands[measurand_type] = {}; + break; + default: + EVLOG_AND_THROW(std::runtime_error("Given SupportedMeasurands are invalid")); + } + } catch (const StringToEnumException& o) { + EVLOG_AND_THROW(std::runtime_error("Given SupportedMeasurands are invalid")); + } + } + } +} + +bool ChargePointConfigurationBase::is_valid_supported_measurands(const std::string& csl) { + const auto elements = utils::from_csl(csl); + bool result{true}; + + for (const auto& element : elements) { + try { + auto measurand = conversions::string_to_measurand(element); + if (const auto it = supported_measurands.find(measurand); it == supported_measurands.end()) { + // this measurand is not supported + result = false; + break; + } + } catch (const StringToEnumException& o) { + EVLOG_warning << "Measurand: " << element << " is not supported!"; + result = false; + break; + } + } + + return result; +} + +std::optional +ChargePointConfigurationBase::csv_to_measurand_with_phase_vector(const std::string& csl) { + const auto elements = utils::from_csl(csl); + bool element_error{false}; + + // collect results in a vector (ensures no duplicates) + std::set result_set; // capture measurands already in the vector + std::vector result_vector; + std::optional> result; + + for (const auto& element : elements) { + try { + MeasurandWithPhase measurand_with_phase; + measurand_with_phase.measurand = conversions::string_to_measurand(element); + + if (const auto it = supported_measurands.find(measurand_with_phase.measurand); + it != supported_measurands.end()) { + // this measurand is supported + + if (const auto included = result_set.find(measurand_with_phase.measurand); + included == result_set.end()) { + // this measurand hasn't been added to the result vector + result_set.insert(measurand_with_phase.measurand); + + // the measurand without a phase as a total value + result_vector.push_back(measurand_with_phase); + + if (it->second.size() > 0) { + // measurand can be provided on multiple phases + for (const auto& phase : it->second) { + measurand_with_phase.phase = phase; + result_vector.push_back(measurand_with_phase); + } + } + } + } + } catch (const StringToEnumException& o) { + EVLOG_warning << "Measurand: " << element << " is not supported!"; + element_error = true; + break; + } + } + + if (!element_error) { + for (auto m : result_vector) { + if (!m.phase) { + EVLOG_debug << "measurand without phase: " << m.measurand; + } else { + EVLOG_debug << "measurand: " << m.measurand + << " with phase: " << conversions::phase_to_string(m.phase.value()); + } + } + + result = std::move(result_vector); + } + + return result; +} + +std::optional ChargePointConfigurationBase::extract_connector_id(const std::string& str) { + // expected string "MeterPublicKey[1]" + + std::optional result; + const std::regex id_regex(R"(^.+\[(\d+)\]$)"); + std::smatch id_match; + + if (std::regex_match(str, id_match, id_regex)) { + if (id_match.size() == 2) { + try { + result = std::stoul(id_match[1]); + } catch (const std::exception& ex) { + EVLOG_error << "Unable to extract connector ID from '" << str << "': " << ex.what(); + } + } + } else { + EVLOG_error << "Connector ID not found: '" << str << '\''; + } + + return result; +} + +std::string ChargePointConfigurationBase::MeterPublicKey_string(std::uint32_t connector_id) { + return std::string{"MeterPublicKey["} + std::to_string(connector_id) + ']'; +} + +bool ChargePointConfigurationBase::to_bool(const std::string& value) { + std::string out = value; + std::transform(out.begin(), out.end(), out.begin(), ::tolower); + if ((out == "true") || out == "1") { + return true; + } else if ((out == "false") || out == "0") { + return false; + } + throw std::invalid_argument("Not a boolean value"); +} + +bool ChargePointConfigurationBase::isBool(const std::string& str) { + std::string out = str; + std::transform(out.begin(), out.end(), out.begin(), ::tolower); + return out == "true" || out == "false"; +} + +bool ChargePointConfigurationBase::isPositiveInteger(const std::string& str) { + bool result{false}; + try { + result = std::get(is_positive_integer(str)); + } catch (const std::invalid_argument& e) { + } catch (const std::out_of_range& e) { + } + return result; +} + +bool ChargePointConfigurationBase::isConnectorPhaseRotationValid(std::int32_t num_connectors, + const std::string& value) { + std::string str{value}; + bool result{true}; + + str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end()); + auto elements = v16::utils::from_csl(str); + + // Filter per element of type 0.NotApplicable, 1.NotApplicable, or 0.Unknown etc + for (int connector_id = 0; connector_id <= num_connectors; connector_id++) { + std::string myNotApplicable = std::to_string(connector_id) + ".NotApplicable"; + std::string myNotDefined = std::to_string(connector_id) + ".Unknown"; + elements.erase(std::remove(elements.begin(), elements.end(), myNotApplicable), elements.end()); + elements.erase(std::remove(elements.begin(), elements.end(), myNotDefined), elements.end()); + } + // if all elements are hit, accept it, else check the remaining + if (elements.size() > 0) { + for (const std::string& e : elements) { + if (e.size() != 5) { + result = false; + } else { + try { + auto connector = std::stoi(e.substr(0, 1)); + if (connector < 0 or connector > num_connectors) { + result = false; + } else { + std::string phase_rotation = e.substr(2, 5); + if (phase_rotation != "RST" and phase_rotation != "RTS" and phase_rotation != "SRT" and + phase_rotation != "STR" and phase_rotation != "TRS" and phase_rotation != "TSR") { + result = false; + } + } + } catch (const std::invalid_argument&) { + result = false; + } + } + + if (!result) { + break; + } + } + } + return result; +} + +bool ChargePointConfigurationBase::isTimeOffset(const std::string& offset) { + bool result{false}; + + const auto times = v16::utils::split_string(':', offset); + if (times.size() != 2) { + EVLOG_error + << R"(Could not set display time offset: format not correct (should be something like "-05:00", but is )" + << offset << ')'; + } else { + try { + result = true; + // Check if strings are numbers. + const int32_t hours = std::stoi(times.at(0)); + const int32_t minutes = std::stoi(times.at(1)); + + // And check if numbers are valid. + if (hours < -24 or hours > 24) { + EVLOG_error << "Could not set display time offset: hours should be between -24 and +24, but is " + << times.at(0); + result = false; + } + + if (minutes < 0 or minutes > 59) { + EVLOG_error << "Could not set display time offset: minutes should be between 0 and 59, but is " + << times.at(1); + result = false; + } + + } catch (const std::exception& e) { + EVLOG_error + << R"(Could not set display time offset: format not correct (should be something like "-19:15", but is )" + << offset << "): " << e.what(); + result = false; + } + } + return result; +} + +bool ChargePointConfigurationBase::areValidEvseIds(const std::string& value) { + bool result{true}; + + if (value.length() <= CONNECTOR_EVSE_IDS_MAX_LENGTH) { + // this fullfills parts of HUB-24-003 of Requirements EVSE Check PnC with ISO15118-2 v4 + const auto evse_ids = v16::utils::from_csl(value); + for (const auto& evse_id : evse_ids) { + if (evse_id.size() < 7 or evse_id.size() > 37) { + EVLOG_warning << "Attempting to set ConnectorEvseIds to invalid value: " << evse_id; + result = false; + break; + } + } + } else { + result = false; + } + return result; +} + +std::string ChargePointConfigurationBase::hexToString(const std::string& s) { + std::string str; + for (size_t i = 0; i < s.length(); i += 2) { + std::string byte = s.substr(i, 2); + char chr = (char)(int)strtol(byte.c_str(), NULL, 16); + str.push_back(chr); + } + return str; +} + +bool ChargePointConfigurationBase::isHexNotation(const std::string& s) { + // TODO(james-ctc): this check is problematic + // need to identify what is considered valid + // e.g. what can the first two characters be? + // why are they not ignored in hexToString()? + // the size should be a multiple of 2 … + // + // consider combining isHexNotation and hexToString to avoid reparsing + // the string + + bool result = s.size() > 2 and s.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos; + if (result) { + // check if every char is printable + for (size_t i = 0; i < s.length(); i += 2) { + std::string byte = s.substr(i, 2); + char chr = (char)(int)strtol(byte.c_str(), NULL, 16); + // ignoring check for \n (0x0a) because find_first_not_of() has not + // found one + if (!std::isprint(chr)) { + result = false; + break; + } + } + } + return result; +} + +} // namespace ocpp::v16 diff --git a/lib/everest/ocpp/lib/ocpp/v16/charge_point_configuration_devicemodel.cpp b/lib/everest/ocpp/lib/ocpp/v16/charge_point_configuration_devicemodel.cpp new file mode 100644 index 0000000000..bcce6bf619 --- /dev/null +++ b/lib/everest/ocpp/lib/ocpp/v16/charge_point_configuration_devicemodel.cpp @@ -0,0 +1,2932 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest + +#include "ocpp/v2/ocpp_enums.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +using namespace ocpp; +using SetResult = v16::ChargePointConfigurationDeviceModel::SetResult; +using DeviceModelInterface = v2::DeviceModelInterface; + +constexpr const char* custom_component = "Custom"; + +constexpr v16::ConfigurationStatus convert(SetResult res) { + switch (res) { + case SetResult::Accepted: + return v16::ConfigurationStatus::Accepted; + case SetResult::Rejected: + return v16::ConfigurationStatus::Rejected; + case SetResult::RebootRequired: + return v16::ConfigurationStatus::RebootRequired; + case SetResult::NotSupportedAttributeType: + case SetResult::UnknownComponent: + case SetResult::UnknownVariable: + default: + return v16::ConfigurationStatus::NotSupported; + } +} + +// std::to_string uses "1" and "0" +constexpr const char* to_string(bool value) { + return (value) ? "true" : "false"; +} + +/// \brief check if a key is valid against the supported features +inline bool ignore_key(v16::keys::valid_keys key, const std::set& profiles, + v16::SupportedFeatureProfiles profile) { + // if the profile is not valid and the key is then return true + bool supported = profiles.find(profile) != profiles.end(); + v16::keys::sections section{v16::keys::sections::Custom}; + + switch (profile) { + case v16::SupportedFeatureProfiles::Core: + section = v16::keys::sections::Core; + break; + case v16::SupportedFeatureProfiles::CostAndPrice: + section = v16::keys::sections::CostAndPrice; + break; + case v16::SupportedFeatureProfiles::Custom: + section = v16::keys::sections::Custom; + break; + case v16::SupportedFeatureProfiles::FirmwareManagement: + section = v16::keys::sections::FirmwareManagement; + break; + case v16::SupportedFeatureProfiles::Internal: + section = v16::keys::sections::Internal; + break; + case v16::SupportedFeatureProfiles::LocalAuthListManagement: + section = v16::keys::sections::LocalAuthListManagement; + break; + case v16::SupportedFeatureProfiles::PnC: + section = v16::keys::sections::PnC; + break; + case v16::SupportedFeatureProfiles::Reservation: + section = v16::keys::sections::Reservation; + break; + case v16::SupportedFeatureProfiles::Security: + section = v16::keys::sections::Security; + break; + case v16::SupportedFeatureProfiles::SmartCharging: + section = v16::keys::sections::SmartCharging; + break; + case v16::SupportedFeatureProfiles::RemoteTrigger: + default: + break; + } + + bool valid = v16::keys::to_section(key) == section; + return !supported && valid; +} + +/// \brief raise exception when accessing a key that doesn't exist +void raise_not_found(const std::string_view& section, const std::string_view& name) { + EVLOG_critical << "Directly requested value for ComponentVariable that doesn't exist in the device model: " + << section << '.' << name; + EVLOG_AND_THROW( + std::runtime_error("Directly requested value for ComponentVariable that doesn't exist in the device model.")); +} + +// Component/Variable support ... + +std::optional isReadOnly(DeviceModelInterface& storage, const std::string_view& component, + const std::string_view& variable) { + // known keys are checked via is_readonly() in known_keys.hpp + // for other keys get_mutability() is used + const v2::Component component_v{std::string{component}}; + const v2::Variable variable_v{std::string{variable}}; + std::optional result; + const auto res = storage.get_mutability(component_v, variable_v, v2::AttributeEnum::Actual); + if (res) { + result = res.value() == v2::MutabilityEnum::ReadOnly; + } + return result; +} + +template +std::optional get_optional(DeviceModelInterface& storage, const std::string_view& component, + const std::string_view& variable) { + const v2::Component component_v{std::string{component}}; + const v2::Variable variable_v{std::string{variable}}; + std::string value; + const auto get_result = storage.get_variable(component_v, variable_v, v2::AttributeEnum::Actual, value); + if (get_result == v2::GetVariableStatusEnum::Accepted) { + try { + if constexpr (std::is_same_v) { + return v16::ChargePointConfigurationBase::to_bool(value); + } else if constexpr (std::is_same_v) { + return value; + } else { + return v2::to_specific_type(value); + } + } catch (const std::exception& ex) { + EVLOG_warning << component_v.name << '[' << variable_v.name << "] '" << value + << "' to_specific_type exception: " << ex.what(); + } + } + return std::nullopt; +} + +template std::optional get_optional(DeviceModelInterface& storage, v16::keys::valid_keys key) { + const auto component = v16::keys::to_section_string_view(key); + const auto variable = v16::keys::convert(key); + return get_optional(storage, component, variable); +} + +template T inline get_value(DeviceModelInterface& storage, v16::keys::valid_keys key) { + const auto result = get_optional(storage, key); + if (!result) { + raise_not_found(v16::keys::to_section_string_view(key), v16::keys::convert(key)); + } + return result.value(); +} + +template +inline void get_value(std::optional& value, DeviceModelInterface& storage, v16::keys::valid_keys key) { + value = get_optional(storage, key); +} + +template inline void get_value(T& value, DeviceModelInterface& storage, v16::keys::valid_keys key) { + value = get_value(storage, key); +} + +bool key_exists(DeviceModelInterface& storage, const std::string_view& component, const std::string_view& variable) { + const v2::Component component_v{std::string{component}}; + const v2::Variable variable_v{std::string{variable}}; + const auto result = storage.get_variable_meta_data(component_v, variable_v); + return result.has_value(); +} + +bool key_exists(DeviceModelInterface& storage, v16::keys::valid_keys key) { + const auto component = v16::keys::to_section_string_view(key); + const auto variable = v16::keys::convert(key); + return key_exists(storage, component, variable); +} + +std::optional get_key_value_optional(DeviceModelInterface& storage, v16::keys::valid_keys key) { + auto get_result = get_optional(storage, key); + std::optional result; + if (get_result) { + v16::KeyValue kv; + kv.key = std::move(std::string{v16::keys::convert(key)}); + kv.readonly = v16::keys::is_readonly(key); + kv.value = std::move(get_result.value()); + result = kv; + } + return result; +} + +template v16::KeyValue get_key_value(DeviceModelInterface& storage, v16::keys::valid_keys key) { + const auto result = get_key_value_optional(storage, key); + if (!result) { + raise_not_found(v16::keys::to_section_string_view(key), v16::keys::convert(key)); + } + return result.value(); +} + +inline v16::KeyValue get_key_value(DeviceModelInterface& storage, v16::keys::valid_keys key) { + return get_key_value(storage, key); +} + +/// set known key to specified value +SetResult set_value(DeviceModelInterface& storage, const std::string_view& component, const std::string_view& variable, + const std::string& value) { + const v2::Component component_v{std::string{component}}; + const v2::Variable variable_v{std::string{variable}}; + return storage.set_value(component_v, variable_v, v2::AttributeEnum::Actual, value, "OCPP 1.6"); +} + +SetResult set_value(DeviceModelInterface& storage, v16::keys::valid_keys key, const std::string& value) { + const v2::Component component{std::string{v16::keys::to_section_string_view(key)}}; + const v2::Variable variable{std::string{v16::keys::convert(key)}}; + return storage.set_value(component, variable, v2::AttributeEnum::Actual, value, "OCPP 1.6"); +} + +/// set known key to optional specified value +constexpr SetResult set_value(DeviceModelInterface& storage, v16::keys::valid_keys key, + const std::optional& value) { + // using accepted since there isn't a value to set - so not an error + SetResult result{SetResult::Accepted}; + if (value) { + result = set_value(storage, key, value.value()); + } + return result; +} + +using check_fn = std::function; + +/// set known key to specified value checking the value is valid +inline SetResult set_value(check_fn fn, DeviceModelInterface& storage, v16::keys::valid_keys key, + const std::string& value) { + SetResult result{SetResult::Rejected}; + if (fn(value)) { + result = set_value(storage, key, value); + } else { + EVLOG_warning << "set " << v16::keys::to_section_string_view(key) << '[' << v16::keys::convert(key) + << "]=" << value << " failed, invalid value"; + } + return result; +} + +/// set known key to specified value checking the key already exists +inline SetResult set_value_check(DeviceModelInterface& storage, v16::keys::valid_keys key, const std::string& value) { + SetResult result{SetResult::UnknownVariable}; + if (key_exists(storage, key)) { + result = set_value(storage, key, value); + } else { + EVLOG_warning << "set " << v16::keys::to_section_string_view(key) << '[' << v16::keys::convert(key) + << "]=" << value << " failed, key doesn't exist"; + } + return result; +} + +/// set known key to specified value checking the key already exists and the value is valid +inline SetResult set_value_check(check_fn fn, DeviceModelInterface& storage, v16::keys::valid_keys key, + const std::string& value) { + SetResult result{SetResult::UnknownVariable}; + if (key_exists(storage, key)) { + if (fn(value)) { + result = set_value(storage, key, value); + } else { + EVLOG_warning << "set " << v16::keys::to_section_string_view(key) << '[' << v16::keys::convert(key) + << "]=" << value << " failed, value not valid"; + } + } else { + EVLOG_warning << "set " << v16::keys::to_section_string_view(key) << '[' << v16::keys::convert(key) + << "]=" << value << " failed, key doesn't exist"; + result = SetResult::Rejected; + } + return result; +} + +// Custom key support ... + +template std::optional get_optional(DeviceModelInterface& storage, const std::string_view& name) { + return get_optional(storage, custom_component, name); +} + +template inline T get_value(DeviceModelInterface& storage, const std::string_view& name) { + const auto result = get_optional(storage, name); + if (!result) { + raise_not_found(custom_component, name); + } + return result.value(); +} + +template +inline void get_value(std::optional& value, DeviceModelInterface& storage, const std::string_view& name) { + value = get_optional(storage, name); +} + +template inline void get_value(T& value, DeviceModelInterface& storage, const std::string_view& name) { + value = get_value(storage, name); +} + +std::optional get_key_value_optional(DeviceModelInterface& storage, const std::string_view& name) { + auto get_result = get_optional(storage, name); + std::optional result; + if (get_result) { + v16::KeyValue kv; + kv.key = std::move(std::string{name}); + kv.readonly = isReadOnly(storage, custom_component, name).value_or(true); + kv.value = get_result.value(); + result = kv; + } + return result; +} + +v16::KeyValue get_key_value(DeviceModelInterface& storage, const std::string_view& name) { + const auto result = get_key_value_optional(storage, name); + if (!result) { + raise_not_found(custom_component, name); + } + return result.value(); +} + +/// \brief check if a custom key exists +bool key_exists(DeviceModelInterface& storage, const std::string_view& name) { + return key_exists(storage, custom_component, name); +} + +/// \brief set custom value +SetResult set_value(DeviceModelInterface& storage, const std::string_view& name, const std::string& value) { + const v2::Component component{custom_component}; + const v2::Variable variable{std::string{name}}; + return storage.set_value(component, variable, v2::AttributeEnum::Actual, value, "OCPP 1.6"); +} + +/// \brief set custom value +constexpr SetResult set_value(DeviceModelInterface& storage, const std::string_view& name, + const std::optional& value) { + // using accepted since there isn't a value to set - so not an error + SetResult result{SetResult::Accepted}; + if (value) { + result = set_value(storage, name, value.value()); + } + return result; +} + +inline SetResult set_value_check(DeviceModelInterface& storage, const std::string_view& name, + const std::string& value) { + SetResult result{SetResult::UnknownVariable}; + if (key_exists(storage, name)) { + result = set_value(storage, name, value); + } else { + EVLOG_warning << "set " << name << '=' << value << " failed, key doesn't exist"; + } + return result; +} + +} // namespace + +// ---------------------------------------------------------------------------- +// ChargePointConfigurationDeviceModel + +namespace ocpp::v16 { + +// ---------------------------------------------------------------------------- +// Protected methods + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalAllowChargingProfileWithoutStartSchedule(const std::string& value) { + return set_value_check(isBool, *storage, keys::valid_keys::AllowChargingProfileWithoutStartSchedule, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalCentralSystemURI(const std::string& value) { + EVLOG_warning << "CentralSystemURI changed to: " << value; + auto result = set_value(*storage, keys::valid_keys::CentralSystemURI, value); + if (result == SetResult::Accepted) { + result = SetResult::RebootRequired; + } + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalCompositeScheduleDefaultLimitAmps(const std::string& value) { + return set_value_check(isPositiveInteger, *storage, keys::valid_keys::CompositeScheduleDefaultLimitAmps, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalCompositeScheduleDefaultLimitWatts(const std::string& value) { + return set_value_check(isPositiveInteger, *storage, keys::valid_keys::CompositeScheduleDefaultLimitWatts, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalCompositeScheduleDefaultNumberPhases(const std::string& value) { + auto check = [this](const std::string& value) { + bool result{false}; + try { + const auto [valid, num_phases] = is_positive_integer(value); + result = valid; + if (num_phases <= 0 or num_phases > 3) { + result = false; + } + } catch (const std::invalid_argument& e) { + } catch (const std::out_of_range& e) { + } + return result; + }; + return set_value_check(check, *storage, keys::valid_keys::CompositeScheduleDefaultNumberPhases, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalConnectorEvseIds(const std::string& value) { + return set_value_check(areValidEvseIds, *storage, keys::valid_keys::ConnectorEvseIds, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalIgnoredProfilePurposesOffline(const std::string& value) { + SetResult result{SetResult::UnknownVariable}; + + if (key_exists(*storage, keys::valid_keys::IgnoredProfilePurposesOffline)) { + // check the purposes are valid + const auto purposes = utils::from_csl(value); + bool invalid{false}; + for (const auto& purpose : purposes) { + try { + conversions::string_to_charging_profile_purpose_type(purpose); + } catch (const StringToEnumException& e) { + EVLOG_warning << "Could not convert element of IgnoredProfilePurposesOffline: " << purpose; + invalid = true; + result = SetResult::Rejected; + } + } + + if (!invalid) { + result = set_value(*storage, keys::valid_keys::IgnoredProfilePurposesOffline, utils::to_csl(purposes)); + } + } + + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalOcspRequestInterval(const std::string& value) { + auto check = [this](const std::string& value) { + bool result{false}; + try { + const auto [valid, ocsp_request_interval] = is_positive_integer(value); + result = valid; + if (ocsp_request_interval < OCSP_REQUEST_INTERVAL_MIN) { + result = false; + } + } catch (const std::invalid_argument& e) { + } catch (const std::out_of_range& e) { + } + return result; + }; + return set_value(check, *storage, keys::valid_keys::OcspRequestInterval, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalRetryBackoffRandomRange(const std::string& value) { + return set_value(*storage, keys::valid_keys::RetryBackoffRandomRange, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalRetryBackoffRepeatTimes(const std::string& value) { + return set_value(*storage, keys::valid_keys::RetryBackoffRepeatTimes, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalRetryBackoffWaitMinimum(const std::string& value) { + return set_value(*storage, keys::valid_keys::RetryBackoffWaitMinimum, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalSeccLeafSubjectCommonName(const std::string& value) { + SetResult result{SetResult::Rejected}; + if (value.size() >= SECC_LEAF_SUBJECT_COMMON_NAME_MIN_LENGTH && + value.size() <= SECC_LEAF_SUBJECT_COMMON_NAME_MAX_LENGTH) { + result = set_value_check(*storage, keys::valid_keys::SeccLeafSubjectCommonName, value); + } else { + EVLOG_warning << "Attempt to set SeccLeafSubjectCommonName with invalid number of characters"; + } + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalSeccLeafSubjectCountry(const std::string& value) { + SetResult result{SetResult::Rejected}; + if (value.size() == SECC_LEAF_SUBJECT_COUNTRY_LENGTH) { + result = set_value_check(*storage, keys::valid_keys::SeccLeafSubjectCountry, value); + } else { + EVLOG_warning << "Attempt to set SeccLeafSubjectCountry with invalid number of characters"; + } + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalSeccLeafSubjectOrganization(const std::string& value) { + SetResult result{SetResult::Rejected}; + if (value.size() <= SECC_LEAF_SUBJECT_ORGANIZATION_MAX_LENGTH) { + result = set_value_check(*storage, keys::valid_keys::SeccLeafSubjectOrganization, value); + } else { + EVLOG_warning << "Attempt to set SeccLeafSubjectOrganization with invalid number of characters"; + } + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalStopTransactionIfUnlockNotSupported(const std::string& value) { + return set_value_check(isBool, *storage, keys::valid_keys::StopTransactionIfUnlockNotSupported, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalSupplyVoltage(const std::string& value) { + return set_value_check(isPositiveInteger, *storage, keys::valid_keys::SupplyVoltage, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalVerifyCsmsAllowWildcards(const std::string& value) { + return set_value(isBool, *storage, keys::valid_keys::VerifyCsmsAllowWildcards, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalWaitForStopTransactionsOnResetTimeout(const std::string& value) { + return set_value(isPositiveInteger, *storage, keys::valid_keys::WaitForStopTransactionsOnResetTimeout, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalAllowOfflineTxForUnknownId(const std::string& value) { + return set_value_check(isBool, *storage, keys::valid_keys::AllowOfflineTxForUnknownId, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalAuthorizationCacheEnabled(const std::string& value) { + return set_value_check(isBool, *storage, keys::valid_keys::AuthorizationCacheEnabled, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalAuthorizeRemoteTxRequests(const std::string& value) { + return set_value(*storage, keys::valid_keys::AuthorizeRemoteTxRequests, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalBlinkRepeat(const std::string& value) { + return set_value_check(isPositiveInteger, *storage, keys::valid_keys::BlinkRepeat, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalClockAlignedDataInterval(const std::string& value) { + return set_value(isPositiveInteger, *storage, keys::valid_keys::ClockAlignedDataInterval, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalConnectionTimeOut(const std::string& value) { + return set_value(isPositiveInteger, *storage, keys::valid_keys::ConnectionTimeOut, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalConnectorPhaseRotation(const std::string& value) { + auto check = [this](const auto& v) { return isConnectorPhaseRotationValid(getNumberOfConnectors(), v); }; + return set_value(check, *storage, keys::valid_keys::ConnectorPhaseRotation, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalHeartbeatInterval(const std::string& value) { + return set_value(isPositiveInteger, *storage, keys::valid_keys::HeartbeatInterval, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalLightIntensity(const std::string& value) { + return set_value_check(isPositiveInteger, *storage, keys::valid_keys::LightIntensity, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalLocalAuthorizeOffline(const std::string& value) { + return set_value(isBool, *storage, keys::valid_keys::LocalAuthorizeOffline, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalLocalPreAuthorize(const std::string& value) { + return set_value(isBool, *storage, keys::valid_keys::LocalPreAuthorize, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalMaxEnergyOnInvalidId(const std::string& value) { + return set_value_check(isPositiveInteger, *storage, keys::valid_keys::MaxEnergyOnInvalidId, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalMeterValuesAlignedData(const std::string& value) { + SetResult result{SetResult::Rejected}; + + if (is_valid_supported_measurands(value)) { + result = set_value(*storage, keys::valid_keys::MeterValuesAlignedData, value); + } + + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalMeterValuesSampledData(const std::string& value) { + SetResult result{SetResult::Rejected}; + + if (is_valid_supported_measurands(value)) { + result = set_value(*storage, keys::valid_keys::MeterValuesSampledData, value); + } + + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalMeterValueSampleInterval(const std::string& value) { + return set_value(isPositiveInteger, *storage, keys::valid_keys::MeterValueSampleInterval, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalMinimumStatusDuration(const std::string& value) { + return set_value_check(isPositiveInteger, *storage, keys::valid_keys::MinimumStatusDuration, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalResetRetries(const std::string& value) { + return set_value(isPositiveInteger, *storage, keys::valid_keys::ResetRetries, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalStopTransactionOnInvalidId(const std::string& value) { + return set_value(isBool, *storage, keys::valid_keys::StopTransactionOnInvalidId, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalStopTxnAlignedData(const std::string& value) { + SetResult result{SetResult::Rejected}; + + if (is_valid_supported_measurands(value)) { + result = set_value(*storage, keys::valid_keys::StopTxnAlignedData, value); + } + + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalStopTxnSampledData(const std::string& value) { + SetResult result{SetResult::Rejected}; + + if (is_valid_supported_measurands(value)) { + result = set_value(*storage, keys::valid_keys::StopTxnSampledData, value); + } + + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalTransactionMessageAttempts(const std::string& value) { + return set_value(isPositiveInteger, *storage, keys::valid_keys::TransactionMessageAttempts, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalTransactionMessageRetryInterval(const std::string& value) { + return set_value(isPositiveInteger, *storage, keys::valid_keys::TransactionMessageRetryInterval, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalUnlockConnectorOnEVSideDisconnect(const std::string& value) { + return set_value(isBool, *storage, keys::valid_keys::UnlockConnectorOnEVSideDisconnect, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalWebsocketPingInterval(const std::string& value) { + return set_value_check(isPositiveInteger, *storage, keys::valid_keys::WebSocketPingInterval, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalAuthorizationKey(const std::string& value) { + SetResult result{SetResult::Rejected}; + + // TODO(piet): SecurityLog entry + + std::string str; + if (isHexNotation(value)) { + str = hexToString(value); + } else { + str = value; + } + + if (str.size() >= AUTHORIZATION_KEY_MIN_LENGTH) { + result = set_value(*storage, keys::valid_keys::AuthorizationKey, str); + } else { + EVLOG_warning << "Attempt to change AuthorizationKey to value with < 8 characters"; + } + + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalCpoName(const std::string& value) { + return set_value(*storage, keys::valid_keys::CpoName, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalDisableSecurityEventNotifications(const std::string& value) { + return set_value_check(*storage, keys::valid_keys::DisableSecurityEventNotifications, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalSecurityProfile(const std::string& value) { + // TODO(piet): add boundaries for value of security profile + return set_value_check(*storage, keys::valid_keys::SecurityProfile, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalISO15118CertificateManagementEnabled(const std::string& value) { + return set_value(*storage, keys::valid_keys::ISO15118CertificateManagementEnabled, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalLocalAuthListEnabled(const std::string& value) { + SetResult result{SetResult::UnknownVariable}; + if (supported_feature_profiles.find(SupportedFeatureProfiles::LocalAuthListManagement) != + supported_feature_profiles.end()) { + result = set_value(isBool, *storage, keys::valid_keys::LocalAuthListEnabled, value); + } + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalContractValidationOffline(const std::string& value) { + return set_value(*storage, keys::valid_keys::ContractValidationOffline, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalCentralContractValidationAllowed(const std::string& value) { + return set_value_check(*storage, keys::valid_keys::CentralContractValidationAllowed, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalCertSigningRepeatTimes(const std::string& value) { + return set_value_check(isPositiveInteger, *storage, keys::valid_keys::CertSigningRepeatTimes, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalCertSigningWaitMinimum(const std::string& value) { + return set_value_check(isPositiveInteger, *storage, keys::valid_keys::CertSigningWaitMinimum, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalISO15118PnCEnabled(const std::string& value) { + return set_value(*storage, keys::valid_keys::ISO15118PnCEnabled, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalCustomIdleFeeAfterStop(const std::string& value) { + return set_value(isBool, *storage, keys::valid_keys::CustomIdleFeeAfterStop, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalDefaultPrice(const std::string& value) { + SetResult result{SetResult::Rejected}; + try { + json default_price = json::object(); + default_price = json::parse(value); + result = set_value(*storage, keys::valid_keys::DefaultPrice, value); + } catch (const std::exception& e) { + EVLOG_error << "Default price json not correct, can not store default price : " << e.what(); + } + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalDefaultPriceText(const std::string& key, const std::string& value) { + SetResult result{SetResult::Rejected}; + const auto default_prices = utils::split_string(',', key); + std::string language; + bool found{false}; + + if (default_prices.size() == 2) { + // Second value is language. + language = default_prices.at(1); + // Check if language is allowed. It should be in the list of config item multi language supported languages + const auto supported_languages = getMultiLanguageSupportedLanguages(); + if (supported_languages) { + const auto languages = utils::split_string(',', supported_languages.value()); + const auto& found_it = + std::find_if(languages.begin(), languages.end(), [&language](const std::string& supported_language) { + return trim_string(supported_language) == trim_string(language); + }); + + if (found_it != languages.end()) { + found = true; + } else { + EVLOG_error << "Can not set default price text for language '" << language + << "', because the language is currently not supported in this charging station. Supported " + "languages: " + << supported_languages.value(); + } + } else { + EVLOG_error << "Can not set a default price text for language '" << language + << "', because the config item for multi language support is not set in the config."; + } + } else { + EVLOG_error << "Configuration DefaultPriceText is set, but does not contain a language (Configuration should " + "be something like 'DefaultPriceText,en', but is " + << key << ")."; + } + + if (found) { + // add/update existing + json default_price_json = json::object(); + + // DefaultPriceText is a JSON string + const auto& default_price_text = get_optional(*storage, keys::valid_keys::DefaultPriceText); + if (default_price_text) { + try { + default_price_json = json::parse(default_price_text.value()); + } catch (const std::exception& ex) { + EVLOG_error << "Default price text json not correct, can not fetch default price text : " << ex.what(); + } + } + + try { + auto value_json = json::parse(value); + + // priceText is mandatory + if (value_json.contains("priceText")) { + value_json["language"] = language; + + if (!default_price_json.contains("priceTexts")) { + default_price_json["priceTexts"] = json::array(); + } + + auto& price_texts = default_price_json.at("priceTexts"); + + if (price_texts.is_array()) { + bool language_found = false; + + // update the information if it exists + for (auto& price_text : price_texts.items()) { + if (price_text.value().at("language").get() == language) { + price_text.value() = value_json; + language_found = true; + break; + } + } + if (!language_found) { + // add new info + default_price_json["priceTexts"].push_back(value_json); + } + result = set_value(*storage, keys::valid_keys::DefaultPriceText, default_price_json.dump(2)); + } else { + EVLOG_error << "Error while setting default price: 'priceTexts' is not an array"; + } + + } else { + EVLOG_error << "Configuration DefaultPriceText is set, but does not contain 'priceText'"; + } + } catch (const std::exception& ex) { + EVLOG_error << "Configuration DefaultPriceText not JSON : " << ex.what(); + } + } + + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalDisplayTimeOffset(const std::string& value) { + return set_value(isTimeOffset, *storage, keys::valid_keys::TimeOffset, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalLanguage(const std::string& value) { + return set_value(*storage, keys::valid_keys::Language, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalNextTimeOffsetTransitionDateTime(const std::string& value) { + SetResult result{SetResult::Rejected}; + DateTime dt(value); + if (dt.to_time_point() > date::utc_clock::now()) { + result = set_value(*storage, keys::valid_keys::NextTimeOffsetTransitionDateTime, value); + } + return result; +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalTimeOffsetNextTransition(const std::string& value) { + return set_value(isTimeOffset, *storage, keys::valid_keys::TimeOffsetNextTransition, value); +} + +ChargePointConfigurationDeviceModel::SetResult +ChargePointConfigurationDeviceModel::internalWaitForSetUserPriceTimeout(const std::string& value) { + auto check = [this](const std::string& value) { + bool result{false}; + try { + const auto [valid, timeout] = is_positive_integer(value); + result = valid; + if (timeout > MAX_WAIT_FOR_SET_USER_PRICE_TIMEOUT_MS) { + result = false; + } + } catch (const std::invalid_argument& e) { + } catch (const std::out_of_range& e) { + } + return result; + }; + return set_value_check(check, *storage, keys::valid_keys::WaitForSetUserPriceTimeout, value); +} + +// ---------------------------------------------------------------------------- +// Public methods + +ChargePointConfigurationDeviceModel::ChargePointConfigurationDeviceModel( + std::unique_ptr device_model_interface) : + ChargePointConfigurationBase(), storage(std::move(device_model_interface)) { + const auto profiles = get_optional(*storage, keys::valid_keys::SupportedFeatureProfiles); + const auto measurands = get_optional(*storage, keys::valid_keys::SupportedMeasurands); + ProfilesSet initial; + + // TODO(james-ctc): check how to determine this for v2 + // get from the device model e.g. perhaps: + // CustomizationCtrlr, ISO15118Ctrlr, TariffCostCtrlr + +#if 0 + // If supported add to initial set + + if (config.contains("PnC")) { + // add PnC behind the scenes as supported feature profile + initial.insert(conversions::string_to_supported_feature_profiles("PnC")); + } + + if (config.contains("CostAndPrice")) { + // Add California Pricing Requirements behind the scenes as supported feature profile + initial.insert(conversions::string_to_supported_feature_profiles("CostAndPrice")); + } + + if (config.contains(custom_component)) { + // add Custom behind the scenes as supported feature profile + initial.insert(conversions::string_to_supported_feature_profiles(custom_component)); + } +#else + // TODO(james-ctc): remove these - just added for unit tests + initial.insert(conversions::string_to_supported_feature_profiles("PnC")); + initial.insert(conversions::string_to_supported_feature_profiles("CostAndPrice")); +#endif + + initialise(initial, profiles, measurands); +} + +// ---------------------------------------------------------------------------- +// UserConfig and Internal + +std::string ChargePointConfigurationDeviceModel::getChargeBoxSerialNumber() { + return get_value(*storage, keys::valid_keys::ChargeBoxSerialNumber); +} + +std::optional> ChargePointConfigurationDeviceModel::getChargePointSerialNumber() { + return get_optional(*storage, keys::valid_keys::ChargePointSerialNumber); +} + +CiString<50> ChargePointConfigurationDeviceModel::getFirmwareVersion() { + return get_value(*storage, keys::valid_keys::FirmwareVersion); +} + +std::optional> ChargePointConfigurationDeviceModel::getICCID() { + return get_optional(*storage, keys::valid_keys::ICCID); +} + +std::optional> ChargePointConfigurationDeviceModel::getIMSI() { + return get_optional(*storage, keys::valid_keys::IMSI); +} + +std::optional> ChargePointConfigurationDeviceModel::getMeterSerialNumber() { + return get_optional(*storage, keys::valid_keys::MeterSerialNumber); +} + +std::optional> ChargePointConfigurationDeviceModel::getMeterType() { + return get_optional(*storage, keys::valid_keys::MeterType); +} + +KeyValue ChargePointConfigurationDeviceModel::getChargeBoxSerialNumberKeyValue() { + return get_key_value(*storage, keys::valid_keys::ChargeBoxSerialNumber); +} + +KeyValue ChargePointConfigurationDeviceModel::getFirmwareVersionKeyValue() { + return get_key_value(*storage, keys::valid_keys::FirmwareVersion); +} + +std::optional ChargePointConfigurationDeviceModel::getChargePointSerialNumberKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::ChargePointSerialNumber); +} + +std::optional ChargePointConfigurationDeviceModel::getICCIDKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::ICCID); +} + +std::optional ChargePointConfigurationDeviceModel::getIMSIKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::IMSI); +} + +std::optional ChargePointConfigurationDeviceModel::getMeterSerialNumberKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::MeterSerialNumber); +} + +std::optional ChargePointConfigurationDeviceModel::getMeterTypeKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::MeterType); +} + +void ChargePointConfigurationDeviceModel::setChargepointInformation( + const std::string& chargePointVendor, const std::string& chargePointModel, + const std::optional& chargePointSerialNumber, const std::optional& chargeBoxSerialNumber, + const std::optional& firmwareVersion) { + set_value(*storage, keys::valid_keys::ChargePointVendor, chargePointVendor); + set_value(*storage, keys::valid_keys::ChargePointModel, chargePointModel); + set_value(*storage, keys::valid_keys::ChargePointSerialNumber, chargePointSerialNumber); + set_value(*storage, keys::valid_keys::ChargeBoxSerialNumber, chargeBoxSerialNumber); + set_value(*storage, keys::valid_keys::FirmwareVersion, firmwareVersion); +} + +void ChargePointConfigurationDeviceModel::setChargepointMeterInformation( + const std::optional& meterSerialNumber, const std::optional& meterType) { + set_value(*storage, keys::valid_keys::MeterSerialNumber, meterSerialNumber); + set_value(*storage, keys::valid_keys::MeterType, meterType); +} + +void ChargePointConfigurationDeviceModel::setChargepointModemInformation(const std::optional& ICCID, + const std::optional& IMSI) { + set_value(*storage, keys::valid_keys::ICCID, ICCID); + set_value(*storage, keys::valid_keys::IMSI, IMSI); +} + +// ---------------------------------------------------------------------------- +// Internal + +std::string ChargePointConfigurationDeviceModel::getCentralSystemURI() { + return get_value(*storage, keys::valid_keys::CentralSystemURI); +} + +std::string ChargePointConfigurationDeviceModel::getChargePointId() { + return get_value(*storage, keys::valid_keys::ChargePointId); +} + +std::string ChargePointConfigurationDeviceModel::getSupportedCiphers12() { + return get_value(*storage, keys::valid_keys::SupportedCiphers12); +} + +std::string ChargePointConfigurationDeviceModel::getSupportedCiphers13() { + return get_value(*storage, keys::valid_keys::SupportedCiphers13); +} + +std::string ChargePointConfigurationDeviceModel::getSupportedMeasurands() { + return get_value(*storage, keys::valid_keys::SupportedMeasurands); +} + +std::string ChargePointConfigurationDeviceModel::getTLSKeylogFile() { + return get_value(*storage, keys::valid_keys::TLSKeylogFile); +} + +std::string ChargePointConfigurationDeviceModel::getWebsocketPingPayload() { + return get_value(*storage, keys::valid_keys::WebsocketPingPayload); +} + +CiString<20> ChargePointConfigurationDeviceModel::getChargePointModel() { + return get_value(*storage, keys::valid_keys::ChargePointModel); +} + +CiString<20> ChargePointConfigurationDeviceModel::getChargePointVendor() { + return get_value(*storage, keys::valid_keys::ChargePointVendor); +} + +bool ChargePointConfigurationDeviceModel::getAuthorizeConnectorZeroOnConnectorOne() { + bool result{false}; + if (getNumberOfConnectors() == 1) { + get_value(result, *storage, keys::valid_keys::AuthorizeConnectorZeroOnConnectorOne); + } + return result; +} + +bool ChargePointConfigurationDeviceModel::getEnableTLSKeylog() { + return get_value(*storage, keys::valid_keys::EnableTLSKeylog); +} + +bool ChargePointConfigurationDeviceModel::getLogMessages() { + return get_value(*storage, keys::valid_keys::LogMessages); +} + +bool ChargePointConfigurationDeviceModel::getLogMessagesRaw() { + return get_value(*storage, keys::valid_keys::LogMessagesRaw); +} + +bool ChargePointConfigurationDeviceModel::getLogRotation() { + return get_value(*storage, keys::valid_keys::LogRotation); +} + +bool ChargePointConfigurationDeviceModel::getLogRotationDateSuffix() { + return get_value(*storage, keys::valid_keys::LogRotationDateSuffix); +} + +bool ChargePointConfigurationDeviceModel::getStopTransactionIfUnlockNotSupported() { + return get_value(*storage, keys::valid_keys::StopTransactionIfUnlockNotSupported); +} + +bool ChargePointConfigurationDeviceModel::getUseSslDefaultVerifyPaths() { + return get_value(*storage, keys::valid_keys::UseSslDefaultVerifyPaths); +} + +bool ChargePointConfigurationDeviceModel::getUseTPM() { + return get_value(*storage, keys::valid_keys::UseTPM); +} + +bool ChargePointConfigurationDeviceModel::getUseTPMSeccLeafCertificate() { + return get_value(*storage, keys::valid_keys::UseTPMSeccLeafCertificate); +} + +bool ChargePointConfigurationDeviceModel::getVerifyCsmsAllowWildcards() { + return get_value(*storage, keys::valid_keys::VerifyCsmsAllowWildcards); +} + +bool ChargePointConfigurationDeviceModel::getVerifyCsmsCommonName() { + return get_value(*storage, keys::valid_keys::VerifyCsmsCommonName); +} + +int ChargePointConfigurationDeviceModel::getMaxMessageSize() { + return get_value(*storage, keys::valid_keys::MaxMessageSize); +} + +std::int32_t ChargePointConfigurationDeviceModel::getMaxCompositeScheduleDuration() { + return get_value(*storage, keys::valid_keys::MaxCompositeScheduleDuration); +} + +std::int32_t ChargePointConfigurationDeviceModel::getOcspRequestInterval() { + return get_value(*storage, keys::valid_keys::OcspRequestInterval); +} + +std::int32_t ChargePointConfigurationDeviceModel::getRetryBackoffRandomRange() { + return get_value(*storage, keys::valid_keys::RetryBackoffRandomRange); +} + +std::int32_t ChargePointConfigurationDeviceModel::getRetryBackoffRepeatTimes() { + return get_value(*storage, keys::valid_keys::RetryBackoffRepeatTimes); +} + +std::int32_t ChargePointConfigurationDeviceModel::getRetryBackoffWaitMinimum() { + return get_value(*storage, keys::valid_keys::RetryBackoffWaitMinimum); +} + +std::int32_t ChargePointConfigurationDeviceModel::getWaitForStopTransactionsOnResetTimeout() { + return get_value(*storage, keys::valid_keys::WaitForStopTransactionsOnResetTimeout); +} + +std::int32_t ChargePointConfigurationDeviceModel::getWebsocketPongTimeout() { + return get_value(*storage, keys::valid_keys::WebsocketPongTimeout); +} + +std::uint64_t ChargePointConfigurationDeviceModel::getLogRotationMaximumFileCount() { + return get_value(*storage, keys::valid_keys::LogRotationMaximumFileCount); +} + +std::uint64_t ChargePointConfigurationDeviceModel::getLogRotationMaximumFileSize() { + return get_value(*storage, keys::valid_keys::LogRotationMaximumFileSize); +} + +std::vector ChargePointConfigurationDeviceModel::getLogMessagesFormat() { + std::string csl; + get_value(csl, *storage, keys::valid_keys::LogMessagesFormat); + return utils::from_csl(csl); +} + +std::vector ChargePointConfigurationDeviceModel::getIgnoredProfilePurposesOffline() { + std::optional get_result; + get_value(get_result, *storage, keys::valid_keys::IgnoredProfilePurposesOffline); + std::vector purpose_types; + + if (get_result) { + const auto purposes = utils::from_csl(get_result.value()); + + for (const auto& purpose : purposes) { + try { + purpose_types.push_back(conversions::string_to_charging_profile_purpose_type(purpose)); + } catch (const StringToEnumException& e) { + EVLOG_warning << "Could not convert element of IgnoredProfilePurposesOffline: " << purpose; + } + } + } + + return purpose_types; +} + +std::vector ChargePointConfigurationDeviceModel::getSupportedChargingProfilePurposeTypes() { + std::string get_result; + get_value(get_result, *storage, keys::valid_keys::SupportedChargingProfilePurposeTypes); + const auto purposes = utils::from_csl(get_result); + + std::vector supported_purpose_types; + for (const auto& purpose : purposes) { + try { + supported_purpose_types.push_back(conversions::string_to_charging_profile_purpose_type(purpose)); + } catch (const StringToEnumException& e) { + EVLOG_warning << "Could not convert element of SupportedChargingProfilePurposeTypes: " << purpose; + } + } + return supported_purpose_types; +} + +std::optional ChargePointConfigurationDeviceModel::getConnectorEvseIds() { + return get_optional(*storage, keys::valid_keys::ConnectorEvseIds); +} + +std::optional ChargePointConfigurationDeviceModel::getHostName() { + return get_optional(*storage, keys::valid_keys::HostName); +} + +std::optional ChargePointConfigurationDeviceModel::getIFace() { + return get_optional(*storage, keys::valid_keys::IFace); +} + +std::optional ChargePointConfigurationDeviceModel::getMessageTypesDiscardForQueueing() { + return get_optional(*storage, keys::valid_keys::MessageTypesDiscardForQueueing); +} + +std::optional ChargePointConfigurationDeviceModel::getSeccLeafSubjectCommonName() { + return get_optional(*storage, keys::valid_keys::SeccLeafSubjectCommonName); +} + +std::optional ChargePointConfigurationDeviceModel::getSeccLeafSubjectCountry() { + return get_optional(*storage, keys::valid_keys::SeccLeafSubjectCountry); +} + +std::optional ChargePointConfigurationDeviceModel::getSeccLeafSubjectOrganization() { + return get_optional(*storage, keys::valid_keys::SeccLeafSubjectOrganization); +} + +std::optional ChargePointConfigurationDeviceModel::getAllowChargingProfileWithoutStartSchedule() { + return get_optional(*storage, keys::valid_keys::AllowChargingProfileWithoutStartSchedule); +} + +std::optional ChargePointConfigurationDeviceModel::getAllowOfflineTxForUnknownId() { + return get_optional(*storage, keys::valid_keys::AllowOfflineTxForUnknownId); +} + +std::optional ChargePointConfigurationDeviceModel::getQueueAllMessages() { + return get_optional(*storage, keys::valid_keys::QueueAllMessages); +} + +std::optional ChargePointConfigurationDeviceModel::getMessageQueueSizeThreshold() { + return get_optional(*storage, keys::valid_keys::MessageQueueSizeThreshold); +} + +std::optional ChargePointConfigurationDeviceModel::getCompositeScheduleDefaultLimitAmps() { + return get_optional(*storage, keys::valid_keys::CompositeScheduleDefaultLimitAmps); +} + +std::optional ChargePointConfigurationDeviceModel::getCompositeScheduleDefaultLimitWatts() { + return get_optional(*storage, keys::valid_keys::CompositeScheduleDefaultLimitWatts); +} + +std::optional ChargePointConfigurationDeviceModel::getCompositeScheduleDefaultNumberPhases() { + return get_optional(*storage, keys::valid_keys::CompositeScheduleDefaultNumberPhases); +} + +std::optional ChargePointConfigurationDeviceModel::getSupplyVoltage() { + return get_optional(*storage, keys::valid_keys::SupplyVoltage); +} + +std::optional ChargePointConfigurationDeviceModel::getPublicKeyKeyValue(const std::uint32_t connector_id) { + std::optional result; + const auto max = getNumberOfConnectors(); + if ((connector_id > max) || (connector_id < 1)) { + EVLOG_warning << "Cannot get MeterPublicKey for connector " << connector_id + << ", because the connector id does not exist."; + } else { + auto key = MeterPublicKey_string(connector_id); + auto value = get_optional(*storage, "Internal", key); + if (value) { + KeyValue kv; + kv.key = std::move(key); + kv.readonly = true; + kv.value = std::move(value); + result = std::move(kv); + } + } + return result; +} + +KeyValue ChargePointConfigurationDeviceModel::getAuthorizeConnectorZeroOnConnectorOneKeyValue() { + return get_key_value(*storage, keys::valid_keys::AuthorizeConnectorZeroOnConnectorOne); +} + +KeyValue ChargePointConfigurationDeviceModel::getCentralSystemURIKeyValue() { + auto kv = get_key_value(*storage, keys::valid_keys::CentralSystemURI); + // this may be changeable so check the device model rather than known_keys + kv.readonly = isReadOnly(*storage, "Internal", "CentralSystemURI").value_or(true); + return kv; +} + +KeyValue ChargePointConfigurationDeviceModel::getChargePointIdKeyValue() { + return get_key_value(*storage, keys::valid_keys::ChargePointId); +} + +KeyValue ChargePointConfigurationDeviceModel::getChargePointModelKeyValue() { + return get_key_value(*storage, keys::valid_keys::ChargePointModel); +} + +KeyValue ChargePointConfigurationDeviceModel::getChargePointVendorKeyValue() { + return get_key_value(*storage, keys::valid_keys::ChargePointVendor); +} + +KeyValue ChargePointConfigurationDeviceModel::getLogMessagesFormatKeyValue() { + return get_key_value(*storage, keys::valid_keys::LogMessagesFormat); +} + +KeyValue ChargePointConfigurationDeviceModel::getLogMessagesKeyValue() { + return get_key_value(*storage, keys::valid_keys::LogMessages); +} + +KeyValue ChargePointConfigurationDeviceModel::getLogMessagesRawKeyValue() { + return get_key_value(*storage, keys::valid_keys::LogMessagesRaw); +} + +KeyValue ChargePointConfigurationDeviceModel::getLogRotationDateSuffixKeyValue() { + return get_key_value(*storage, keys::valid_keys::LogRotationDateSuffix); +} + +KeyValue ChargePointConfigurationDeviceModel::getLogRotationKeyValue() { + return get_key_value(*storage, keys::valid_keys::LogRotation); +} + +KeyValue ChargePointConfigurationDeviceModel::getLogRotationMaximumFileCountKeyValue() { + return get_key_value(*storage, keys::valid_keys::LogRotationMaximumFileCount); +} + +KeyValue ChargePointConfigurationDeviceModel::getLogRotationMaximumFileSizeKeyValue() { + return get_key_value(*storage, keys::valid_keys::LogRotationMaximumFileSize); +} + +KeyValue ChargePointConfigurationDeviceModel::getMaxCompositeScheduleDurationKeyValue() { + return get_key_value(*storage, keys::valid_keys::MaxCompositeScheduleDuration); +} + +KeyValue ChargePointConfigurationDeviceModel::getMaxMessageSizeKeyValue() { + return get_key_value(*storage, keys::valid_keys::MaxMessageSize); +} + +KeyValue ChargePointConfigurationDeviceModel::getOcspRequestIntervalKeyValue() { + return get_key_value(*storage, keys::valid_keys::OcspRequestInterval); +} + +KeyValue ChargePointConfigurationDeviceModel::getRetryBackoffRandomRangeKeyValue() { + return get_key_value(*storage, keys::valid_keys::RetryBackoffRandomRange); +} + +KeyValue ChargePointConfigurationDeviceModel::getRetryBackoffRepeatTimesKeyValue() { + return get_key_value(*storage, keys::valid_keys::RetryBackoffRepeatTimes); +} + +KeyValue ChargePointConfigurationDeviceModel::getRetryBackoffWaitMinimumKeyValue() { + return get_key_value(*storage, keys::valid_keys::RetryBackoffWaitMinimum); +} + +KeyValue ChargePointConfigurationDeviceModel::getStopTransactionIfUnlockNotSupportedKeyValue() { + return get_key_value(*storage, keys::valid_keys::StopTransactionIfUnlockNotSupported); +} + +KeyValue ChargePointConfigurationDeviceModel::getSupportedChargingProfilePurposeTypesKeyValue() { + return get_key_value(*storage, keys::valid_keys::SupportedChargingProfilePurposeTypes); +} + +KeyValue ChargePointConfigurationDeviceModel::getSupportedCiphers12KeyValue() { + return get_key_value(*storage, keys::valid_keys::SupportedCiphers12); +} + +KeyValue ChargePointConfigurationDeviceModel::getSupportedCiphers13KeyValue() { + return get_key_value(*storage, keys::valid_keys::SupportedCiphers13); +} + +KeyValue ChargePointConfigurationDeviceModel::getSupportedMeasurandsKeyValue() { + return get_key_value(*storage, keys::valid_keys::SupportedMeasurands); +} + +KeyValue ChargePointConfigurationDeviceModel::getUseSslDefaultVerifyPathsKeyValue() { + return get_key_value(*storage, keys::valid_keys::UseSslDefaultVerifyPaths); +} + +KeyValue ChargePointConfigurationDeviceModel::getVerifyCsmsAllowWildcardsKeyValue() { + return get_key_value(*storage, keys::valid_keys::VerifyCsmsAllowWildcards); +} + +KeyValue ChargePointConfigurationDeviceModel::getVerifyCsmsCommonNameKeyValue() { + return get_key_value(*storage, keys::valid_keys::VerifyCsmsCommonName); +} + +KeyValue ChargePointConfigurationDeviceModel::getWaitForStopTransactionsOnResetTimeoutKeyValue() { + return get_key_value(*storage, keys::valid_keys::WaitForStopTransactionsOnResetTimeout); +} + +KeyValue ChargePointConfigurationDeviceModel::getWebsocketPingPayloadKeyValue() { + return get_key_value(*storage, keys::valid_keys::WebsocketPingPayload); +} + +KeyValue ChargePointConfigurationDeviceModel::getWebsocketPongTimeoutKeyValue() { + return get_key_value(*storage, keys::valid_keys::WebsocketPongTimeout); +} + +std::optional ChargePointConfigurationDeviceModel::getAllowChargingProfileWithoutStartScheduleKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::AllowChargingProfileWithoutStartSchedule); +} + +std::optional ChargePointConfigurationDeviceModel::getAllowOfflineTxForUnknownIdKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::AllowOfflineTxForUnknownId); +} + +std::optional ChargePointConfigurationDeviceModel::getCompositeScheduleDefaultLimitAmpsKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::CompositeScheduleDefaultLimitAmps); +} + +std::optional ChargePointConfigurationDeviceModel::getCompositeScheduleDefaultLimitWattsKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::CompositeScheduleDefaultLimitWatts); +} + +std::optional ChargePointConfigurationDeviceModel::getCompositeScheduleDefaultNumberPhasesKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::CompositeScheduleDefaultNumberPhases); +} + +std::optional ChargePointConfigurationDeviceModel::getConnectorEvseIdsKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::ConnectorEvseIds); +} + +std::optional ChargePointConfigurationDeviceModel::getHostNameKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::HostName); +} + +std::optional ChargePointConfigurationDeviceModel::getIFaceKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::IFace); +} + +std::optional ChargePointConfigurationDeviceModel::getIgnoredProfilePurposesOfflineKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::IgnoredProfilePurposesOffline); +} + +std::optional ChargePointConfigurationDeviceModel::getMessageTypesDiscardForQueueingKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::MessageTypesDiscardForQueueing); +} + +std::optional ChargePointConfigurationDeviceModel::getMessageQueueSizeThresholdKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::MessageQueueSizeThreshold); +} + +std::optional> ChargePointConfigurationDeviceModel::getAllMeterPublicKeyKeyValues() { + std::optional> result; + std::vector list; + + const auto max = getNumberOfConnectors(); + + for (std::uint32_t i = 0; i <= max; i++) { + auto res = getPublicKeyKeyValue(i); + if (res) { + list.push_back(std::move(res.value())); + } + } + + if (!list.empty()) { + result = std::move(list); + } + + return result; +} + +std::optional ChargePointConfigurationDeviceModel::getQueueAllMessagesKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::QueueAllMessages); +} + +std::optional ChargePointConfigurationDeviceModel::getSeccLeafSubjectCommonNameKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::SeccLeafSubjectCommonName); +} + +std::optional ChargePointConfigurationDeviceModel::getSeccLeafSubjectCountryKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::SeccLeafSubjectCountry); +} + +std::optional ChargePointConfigurationDeviceModel::getSeccLeafSubjectOrganizationKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::SeccLeafSubjectOrganization); +} + +std::optional ChargePointConfigurationDeviceModel::getSupplyVoltageKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::SupplyVoltage); +} + +void ChargePointConfigurationDeviceModel::setAllowChargingProfileWithoutStartSchedule(bool allow) { + internalAllowChargingProfileWithoutStartSchedule(to_string(allow)); +} + +void ChargePointConfigurationDeviceModel::setAllowOfflineTxForUnknownId(bool enabled) { + internalAllowOfflineTxForUnknownId(to_string(enabled)); +} + +void ChargePointConfigurationDeviceModel::setCentralSystemURI(const std::string& centralSystemUri) { + internalCentralSystemURI(centralSystemUri); +} + +void ChargePointConfigurationDeviceModel::setCompositeScheduleDefaultLimitAmps(std::int32_t limit_amps) { + internalCompositeScheduleDefaultLimitAmps(std::to_string(limit_amps)); +} + +void ChargePointConfigurationDeviceModel::setCompositeScheduleDefaultLimitWatts(std::int32_t limit_watts) { + internalCompositeScheduleDefaultLimitWatts(std::to_string(limit_watts)); +} + +void ChargePointConfigurationDeviceModel::setCompositeScheduleDefaultNumberPhases(std::int32_t number_phases) { + internalCompositeScheduleDefaultNumberPhases(std::to_string(number_phases)); +} + +void ChargePointConfigurationDeviceModel::setConnectorEvseIds(const std::string& connector_evse_ids) { + internalConnectorEvseIds(connector_evse_ids); +} + +bool ChargePointConfigurationDeviceModel::setIgnoredProfilePurposesOffline( + const std::string& ignored_profile_purposes_offline) { + return internalIgnoredProfilePurposesOffline(ignored_profile_purposes_offline) == SetResult::Accepted; +} + +bool ChargePointConfigurationDeviceModel::setMeterPublicKey(const std::int32_t connector_id, + const std::string& public_key_pem) { + bool result{false}; + if (connector_id > getNumberOfConnectors() || connector_id < 1) { + EVLOG_warning << "Cannot set MeterPublicKey for connector " << connector_id + << ", because the connector id does not exist."; + } else { + const auto key = MeterPublicKey_string(connector_id); + const auto res = set_value(*storage, "Internal", key, public_key_pem); + result = res == SetResult::Accepted; + } + return result; +} + +void ChargePointConfigurationDeviceModel::setOcspRequestInterval(const std::int32_t ocsp_request_interval) { + internalOcspRequestInterval(std::to_string(ocsp_request_interval)); +} + +void ChargePointConfigurationDeviceModel::setRetryBackoffRandomRange(std::int32_t retry_backoff_random_range) { + internalRetryBackoffRandomRange(std::to_string(retry_backoff_random_range)); +} + +void ChargePointConfigurationDeviceModel::setRetryBackoffRepeatTimes(std::int32_t retry_backoff_repeat_times) { + internalRetryBackoffRepeatTimes(std::to_string(retry_backoff_repeat_times)); +} + +void ChargePointConfigurationDeviceModel::setRetryBackoffWaitMinimum(std::int32_t retry_backoff_wait_minimum) { + internalRetryBackoffWaitMinimum(std::to_string(retry_backoff_wait_minimum)); +} + +void ChargePointConfigurationDeviceModel::setSeccLeafSubjectCommonName( + const std::string& secc_leaf_subject_common_name) { + internalSeccLeafSubjectCommonName(secc_leaf_subject_common_name); +} + +void ChargePointConfigurationDeviceModel::setSeccLeafSubjectCountry(const std::string& secc_leaf_subject_country) { + internalSeccLeafSubjectCountry(secc_leaf_subject_country); +} + +void ChargePointConfigurationDeviceModel::setSeccLeafSubjectOrganization( + const std::string& secc_leaf_subject_organization) { + internalSeccLeafSubjectOrganization(secc_leaf_subject_organization); +} + +void ChargePointConfigurationDeviceModel::setStopTransactionIfUnlockNotSupported( + bool stop_transaction_if_unlock_not_supported) { + internalStopTransactionIfUnlockNotSupported(to_string(stop_transaction_if_unlock_not_supported)); +} + +void ChargePointConfigurationDeviceModel::setSupplyVoltage(std::int32_t supply_voltage) { + internalSupplyVoltage(std::to_string(supply_voltage)); +} + +void ChargePointConfigurationDeviceModel::setVerifyCsmsAllowWildcards(bool verify_csms_allow_wildcards) { + internalVerifyCsmsAllowWildcards(to_string(verify_csms_allow_wildcards)); +} + +void ChargePointConfigurationDeviceModel::setWaitForStopTransactionsOnResetTimeout( + const int32_t wait_for_stop_transactions_on_reset_timeout) { + internalWaitForStopTransactionsOnResetTimeout(std::to_string(wait_for_stop_transactions_on_reset_timeout)); +} + +// ---------------------------------------------------------------------------- +// Core + +std::string ChargePointConfigurationDeviceModel::getConnectorPhaseRotation() { + return get_value(*storage, keys::valid_keys::ConnectorPhaseRotation); +} + +std::string ChargePointConfigurationDeviceModel::getMeterValuesAlignedData() { + return get_value(*storage, keys::valid_keys::MeterValuesAlignedData); +} + +std::string ChargePointConfigurationDeviceModel::getMeterValuesSampledData() { + return get_value(*storage, keys::valid_keys::MeterValuesSampledData); +} + +std::string ChargePointConfigurationDeviceModel::getStopTxnAlignedData() { + return get_value(*storage, keys::valid_keys::StopTxnAlignedData); +} + +std::string ChargePointConfigurationDeviceModel::getStopTxnSampledData() { + return get_value(*storage, keys::valid_keys::StopTxnSampledData); +} + +std::string ChargePointConfigurationDeviceModel::getSupportedFeatureProfiles() { + return get_value(*storage, keys::valid_keys::SupportedFeatureProfiles); +} + +bool ChargePointConfigurationDeviceModel::getAuthorizeRemoteTxRequests() { + return get_value(*storage, keys::valid_keys::AuthorizeRemoteTxRequests); +} + +bool ChargePointConfigurationDeviceModel::getLocalAuthorizeOffline() { + return get_value(*storage, keys::valid_keys::LocalAuthorizeOffline); +} + +bool ChargePointConfigurationDeviceModel::getLocalPreAuthorize() { + return get_value(*storage, keys::valid_keys::LocalPreAuthorize); +} + +bool ChargePointConfigurationDeviceModel::getStopTransactionOnInvalidId() { + return get_value(*storage, keys::valid_keys::StopTransactionOnInvalidId); +} + +bool ChargePointConfigurationDeviceModel::getUnlockConnectorOnEVSideDisconnect() { + return get_value(*storage, keys::valid_keys::UnlockConnectorOnEVSideDisconnect); +} + +std::int32_t ChargePointConfigurationDeviceModel::getClockAlignedDataInterval() { + return get_value(*storage, keys::valid_keys::ClockAlignedDataInterval); +} + +std::int32_t ChargePointConfigurationDeviceModel::getConnectionTimeOut() { + return get_value(*storage, keys::valid_keys::ConnectionTimeOut); +} + +std::int32_t ChargePointConfigurationDeviceModel::getGetConfigurationMaxKeys() { + return get_value(*storage, keys::valid_keys::GetConfigurationMaxKeys); +} + +std::int32_t ChargePointConfigurationDeviceModel::getHeartbeatInterval() { + return get_value(*storage, keys::valid_keys::HeartbeatInterval); +} + +std::int32_t ChargePointConfigurationDeviceModel::getMeterValueSampleInterval() { + return get_value(*storage, keys::valid_keys::MeterValueSampleInterval); +} + +std::int32_t ChargePointConfigurationDeviceModel::getNumberOfConnectors() { + return get_value(*storage, keys::valid_keys::NumberOfConnectors); +} + +std::int32_t ChargePointConfigurationDeviceModel::getResetRetries() { + return get_value(*storage, keys::valid_keys::ResetRetries); +} + +std::int32_t ChargePointConfigurationDeviceModel::getTransactionMessageAttempts() { + return get_value(*storage, keys::valid_keys::TransactionMessageAttempts); +} + +std::int32_t ChargePointConfigurationDeviceModel::getTransactionMessageRetryInterval() { + return get_value(*storage, keys::valid_keys::TransactionMessageRetryInterval); +} + +std::vector ChargePointConfigurationDeviceModel::getMeterValuesAlignedDataVector() { + std::vector result; + auto vec = csv_to_measurand_with_phase_vector(getMeterValuesAlignedData()); + if (vec) { + result = std::move(vec.value()); + } + return result; +} + +std::vector ChargePointConfigurationDeviceModel::getMeterValuesSampledDataVector() { + std::vector result; + auto vec = csv_to_measurand_with_phase_vector(getMeterValuesSampledData()); + if (vec) { + result = std::move(vec.value()); + } + return result; +} + +std::optional ChargePointConfigurationDeviceModel::getAuthorizationCacheEnabled() { + return get_optional(*storage, keys::valid_keys::AuthorizationCacheEnabled); +} + +std::optional ChargePointConfigurationDeviceModel::getReserveConnectorZeroSupported() { + return get_optional(*storage, keys::valid_keys::ReserveConnectorZeroSupported); +} + +std::optional ChargePointConfigurationDeviceModel::getStopTransactionOnEVSideDisconnect() { + return get_optional(*storage, keys::valid_keys::StopTransactionOnEVSideDisconnect); +} + +std::optional ChargePointConfigurationDeviceModel::getBlinkRepeat() { + return get_optional(*storage, keys::valid_keys::BlinkRepeat); +} + +std::optional ChargePointConfigurationDeviceModel::getConnectorPhaseRotationMaxLength() { + return get_optional(*storage, keys::valid_keys::ConnectorPhaseRotationMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getLightIntensity() { + return get_optional(*storage, keys::valid_keys::LightIntensity); +} + +std::optional ChargePointConfigurationDeviceModel::getMaxEnergyOnInvalidId() { + return get_optional(*storage, keys::valid_keys::MaxEnergyOnInvalidId); +} + +std::optional ChargePointConfigurationDeviceModel::getMeterValuesAlignedDataMaxLength() { + return get_optional(*storage, keys::valid_keys::MeterValuesAlignedDataMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getMeterValuesSampledDataMaxLength() { + return get_optional(*storage, keys::valid_keys::MeterValuesSampledDataMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getMinimumStatusDuration() { + return get_optional(*storage, keys::valid_keys::MinimumStatusDuration); +} + +std::optional ChargePointConfigurationDeviceModel::getStopTxnAlignedDataMaxLength() { + return get_optional(*storage, keys::valid_keys::StopTxnAlignedDataMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getStopTxnSampledDataMaxLength() { + return get_optional(*storage, keys::valid_keys::StopTxnSampledDataMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getSupportedFeatureProfilesMaxLength() { + return get_optional(*storage, keys::valid_keys::SupportedFeatureProfilesMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getWebsocketPingInterval() { + return get_optional(*storage, keys::valid_keys::WebSocketPingInterval); +} + +std::set ChargePointConfigurationDeviceModel::getSupportedFeatureProfilesSet() { + return supported_feature_profiles; +} + +KeyValue ChargePointConfigurationDeviceModel::getAuthorizeRemoteTxRequestsKeyValue() { + return get_key_value(*storage, keys::valid_keys::AuthorizeRemoteTxRequests); +} + +KeyValue ChargePointConfigurationDeviceModel::getClockAlignedDataIntervalKeyValue() { + return get_key_value(*storage, keys::valid_keys::ClockAlignedDataInterval); +} + +KeyValue ChargePointConfigurationDeviceModel::getConnectionTimeOutKeyValue() { + return get_key_value(*storage, keys::valid_keys::ConnectionTimeOut); +} + +KeyValue ChargePointConfigurationDeviceModel::getConnectorPhaseRotationKeyValue() { + return get_key_value(*storage, keys::valid_keys::ConnectorPhaseRotation); +} + +KeyValue ChargePointConfigurationDeviceModel::getGetConfigurationMaxKeysKeyValue() { + return get_key_value(*storage, keys::valid_keys::GetConfigurationMaxKeys); +} + +KeyValue ChargePointConfigurationDeviceModel::getHeartbeatIntervalKeyValue() { + return get_key_value(*storage, keys::valid_keys::HeartbeatInterval); +} + +KeyValue ChargePointConfigurationDeviceModel::getLocalAuthorizeOfflineKeyValue() { + return get_key_value(*storage, keys::valid_keys::LocalAuthorizeOffline); +} + +KeyValue ChargePointConfigurationDeviceModel::getLocalPreAuthorizeKeyValue() { + return get_key_value(*storage, keys::valid_keys::LocalPreAuthorize); +} + +KeyValue ChargePointConfigurationDeviceModel::getMeterValuesAlignedDataKeyValue() { + return get_key_value(*storage, keys::valid_keys::MeterValuesAlignedData); +} + +KeyValue ChargePointConfigurationDeviceModel::getMeterValueSampleIntervalKeyValue() { + return get_key_value(*storage, keys::valid_keys::MeterValueSampleInterval); +} + +KeyValue ChargePointConfigurationDeviceModel::getMeterValuesSampledDataKeyValue() { + return get_key_value(*storage, keys::valid_keys::MeterValuesSampledData); +} + +KeyValue ChargePointConfigurationDeviceModel::getNumberOfConnectorsKeyValue() { + return get_key_value(*storage, keys::valid_keys::NumberOfConnectors); +} + +KeyValue ChargePointConfigurationDeviceModel::getResetRetriesKeyValue() { + return get_key_value(*storage, keys::valid_keys::ResetRetries); +} + +KeyValue ChargePointConfigurationDeviceModel::getStopTransactionOnInvalidIdKeyValue() { + return get_key_value(*storage, keys::valid_keys::StopTransactionOnInvalidId); +} + +KeyValue ChargePointConfigurationDeviceModel::getStopTxnAlignedDataKeyValue() { + return get_key_value(*storage, keys::valid_keys::StopTxnAlignedData); +} + +KeyValue ChargePointConfigurationDeviceModel::getStopTxnSampledDataKeyValue() { + return get_key_value(*storage, keys::valid_keys::StopTxnSampledData); +} + +KeyValue ChargePointConfigurationDeviceModel::getSupportedFeatureProfilesKeyValue() { + return get_key_value(*storage, keys::valid_keys::SupportedFeatureProfiles); +} + +KeyValue ChargePointConfigurationDeviceModel::getTransactionMessageAttemptsKeyValue() { + return get_key_value(*storage, keys::valid_keys::TransactionMessageAttempts); +} + +KeyValue ChargePointConfigurationDeviceModel::getTransactionMessageRetryIntervalKeyValue() { + return get_key_value(*storage, keys::valid_keys::TransactionMessageRetryInterval); +} + +KeyValue ChargePointConfigurationDeviceModel::getUnlockConnectorOnEVSideDisconnectKeyValue() { + return get_key_value(*storage, keys::valid_keys::UnlockConnectorOnEVSideDisconnect); +} + +std::optional ChargePointConfigurationDeviceModel::getAuthorizationCacheEnabledKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::AuthorizationCacheEnabled); +} + +std::optional ChargePointConfigurationDeviceModel::getBlinkRepeatKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::BlinkRepeat); +} + +std::optional ChargePointConfigurationDeviceModel::getConnectorPhaseRotationMaxLengthKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::ConnectorPhaseRotationMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getLightIntensityKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::LightIntensity); +} + +std::optional ChargePointConfigurationDeviceModel::getMaxEnergyOnInvalidIdKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::MaxEnergyOnInvalidId); +} + +std::optional ChargePointConfigurationDeviceModel::getMeterValuesAlignedDataMaxLengthKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::MeterValuesAlignedDataMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getMeterValuesSampledDataMaxLengthKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::MeterValuesSampledDataMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getMinimumStatusDurationKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::MinimumStatusDuration); +} + +std::optional ChargePointConfigurationDeviceModel::getReserveConnectorZeroSupportedKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::ReserveConnectorZeroSupported); +} + +std::optional ChargePointConfigurationDeviceModel::getStopTransactionOnEVSideDisconnectKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::StopTransactionOnEVSideDisconnect); +} + +std::optional ChargePointConfigurationDeviceModel::getStopTxnAlignedDataMaxLengthKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::StopTxnAlignedDataMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getStopTxnSampledDataMaxLengthKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::StopTxnSampledDataMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getSupportedFeatureProfilesMaxLengthKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::SupportedFeatureProfilesMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getWebsocketPingIntervalKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::WebSocketPingInterval); +} + +void ChargePointConfigurationDeviceModel::setAuthorizationCacheEnabled(bool enabled) { + internalAuthorizationCacheEnabled(to_string(enabled)); +} + +void ChargePointConfigurationDeviceModel::setAuthorizeRemoteTxRequests(bool enabled) { + internalAuthorizeRemoteTxRequests(to_string(enabled)); +} + +void ChargePointConfigurationDeviceModel::setBlinkRepeat(std::int32_t blink_repeat) { + internalBlinkRepeat(std::to_string(blink_repeat)); +} + +void ChargePointConfigurationDeviceModel::setClockAlignedDataInterval(std::int32_t interval) { + internalClockAlignedDataInterval(std::to_string(interval)); +} + +void ChargePointConfigurationDeviceModel::setConnectionTimeOut(std::int32_t timeout) { + internalConnectionTimeOut(std::to_string(timeout)); +} + +void ChargePointConfigurationDeviceModel::setConnectorPhaseRotation(const std::string& connector_phase_rotation) { + internalConnectorPhaseRotation(connector_phase_rotation); +} + +void ChargePointConfigurationDeviceModel::setHeartbeatInterval(std::int32_t interval) { + internalHeartbeatInterval(std::to_string(interval)); +} + +void ChargePointConfigurationDeviceModel::setLightIntensity(std::int32_t light_intensity) { + internalLightIntensity(std::to_string(light_intensity)); +} + +void ChargePointConfigurationDeviceModel::setLocalAuthorizeOffline(bool local_authorize_offline) { + internalLocalAuthorizeOffline(to_string(local_authorize_offline)); +} + +void ChargePointConfigurationDeviceModel::setLocalPreAuthorize(bool local_pre_authorize) { + internalLocalPreAuthorize(to_string(local_pre_authorize)); +} + +void ChargePointConfigurationDeviceModel::setMaxEnergyOnInvalidId(std::int32_t max_energy) { + internalMaxEnergyOnInvalidId(std::to_string(max_energy)); +} + +bool ChargePointConfigurationDeviceModel::setMeterValuesAlignedData(const std::string& meter_values_aligned_data) { + return internalMeterValuesAlignedData(meter_values_aligned_data) == SetResult::Accepted; +} + +bool ChargePointConfigurationDeviceModel::setMeterValuesSampledData(const std::string& meter_values_sampled_data) { + return internalMeterValuesSampledData(meter_values_sampled_data) == SetResult::Accepted; +} + +void ChargePointConfigurationDeviceModel::setMeterValueSampleInterval(std::int32_t interval) { + internalMeterValueSampleInterval(std::to_string(interval)); +} + +void ChargePointConfigurationDeviceModel::setMinimumStatusDuration(std::int32_t minimum_status_duration) { + internalMinimumStatusDuration(std::to_string(minimum_status_duration)); +} + +void ChargePointConfigurationDeviceModel::setResetRetries(std::int32_t retries) { + internalResetRetries(std::to_string(retries)); +} + +void ChargePointConfigurationDeviceModel::setStopTransactionOnInvalidId(bool stop_transaction_on_invalid_id) { + internalStopTransactionOnInvalidId(to_string(stop_transaction_on_invalid_id)); +} + +bool ChargePointConfigurationDeviceModel::setStopTxnAlignedData(const std::string& stop_txn_aligned_data) { + return internalStopTxnAlignedData(stop_txn_aligned_data) == SetResult::Accepted; +} + +bool ChargePointConfigurationDeviceModel::setStopTxnSampledData(const std::string& stop_txn_sampled_data) { + return internalStopTxnSampledData(stop_txn_sampled_data) == SetResult::Accepted; +} + +void ChargePointConfigurationDeviceModel::setTransactionMessageAttempts(std::int32_t attempts) { + internalTransactionMessageAttempts(std::to_string(attempts)); +} + +void ChargePointConfigurationDeviceModel::setTransactionMessageRetryInterval(std::int32_t retry_interval) { + internalTransactionMessageRetryInterval(std::to_string(retry_interval)); +} + +void ChargePointConfigurationDeviceModel::setUnlockConnectorOnEVSideDisconnect( + bool unlock_connector_on_ev_side_disconnect) { + internalUnlockConnectorOnEVSideDisconnect(to_string(unlock_connector_on_ev_side_disconnect)); +} + +void ChargePointConfigurationDeviceModel::setWebsocketPingInterval(std::int32_t websocket_ping_interval) { + internalWebsocketPingInterval(std::to_string(websocket_ping_interval)); +} + +// ---------------------------------------------------------------------------- +// Firmware Management + +std::optional ChargePointConfigurationDeviceModel::getSupportedFileTransferProtocols() { + return get_optional(*storage, keys::valid_keys::SupportedFileTransferProtocols); +} + +std::optional ChargePointConfigurationDeviceModel::getSupportedFileTransferProtocolsKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::SupportedFileTransferProtocols); +} + +// ---------------------------------------------------------------------------- +// Smart Charging + +std::string ChargePointConfigurationDeviceModel::getChargingScheduleAllowedChargingRateUnit() { + return get_value(*storage, keys::valid_keys::ChargingScheduleAllowedChargingRateUnit); +} + +std::int32_t ChargePointConfigurationDeviceModel::getChargeProfileMaxStackLevel() { + return get_value(*storage, keys::valid_keys::ChargeProfileMaxStackLevel); +} + +std::int32_t ChargePointConfigurationDeviceModel::getChargingScheduleMaxPeriods() { + return get_value(*storage, keys::valid_keys::ChargingScheduleMaxPeriods); +} + +std::int32_t ChargePointConfigurationDeviceModel::getMaxChargingProfilesInstalled() { + return get_value(*storage, keys::valid_keys::MaxChargingProfilesInstalled); +} + +std::optional ChargePointConfigurationDeviceModel::getConnectorSwitch3to1PhaseSupported() { + return get_optional(*storage, keys::valid_keys::ConnectorSwitch3to1PhaseSupported); +} + +std::vector ChargePointConfigurationDeviceModel::getChargingScheduleAllowedChargingRateUnitVector() { + std::vector result; + const auto csl = get_optional(*storage, keys::valid_keys::ChargingScheduleAllowedChargingRateUnit); + if (csl) { + const auto components = utils::from_csl(csl.value()); + for (const auto& component : components) { + if (component == "Current") { + result.push_back(ChargingRateUnit::A); + } else if (component == "Power") { + result.push_back(ChargingRateUnit::W); + } + } + } + return result; +} + +KeyValue ChargePointConfigurationDeviceModel::getChargeProfileMaxStackLevelKeyValue() { + return get_key_value(*storage, keys::valid_keys::ChargeProfileMaxStackLevel); +} + +KeyValue ChargePointConfigurationDeviceModel::getChargingScheduleAllowedChargingRateUnitKeyValue() { + return get_key_value(*storage, keys::valid_keys::ChargingScheduleAllowedChargingRateUnit); +} + +KeyValue ChargePointConfigurationDeviceModel::getChargingScheduleMaxPeriodsKeyValue() { + return get_key_value(*storage, keys::valid_keys::ChargingScheduleMaxPeriods); +} + +KeyValue ChargePointConfigurationDeviceModel::getMaxChargingProfilesInstalledKeyValue() { + return get_key_value(*storage, keys::valid_keys::MaxChargingProfilesInstalled); +} + +std::optional ChargePointConfigurationDeviceModel::getConnectorSwitch3to1PhaseSupportedKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::ConnectorSwitch3to1PhaseSupported); +} + +// ---------------------------------------------------------------------------- +// Security + +bool ChargePointConfigurationDeviceModel::getDisableSecurityEventNotifications() { + return get_value(*storage, keys::valid_keys::DisableSecurityEventNotifications); +} + +std::int32_t ChargePointConfigurationDeviceModel::getSecurityProfile() { + return get_value(*storage, keys::valid_keys::SecurityProfile); +} + +std::optional ChargePointConfigurationDeviceModel::getAuthorizationKey() { + return get_optional(*storage, keys::valid_keys::AuthorizationKey); +} + +std::optional ChargePointConfigurationDeviceModel::getCpoName() { + return get_optional(*storage, keys::valid_keys::CpoName); +} + +std::optional ChargePointConfigurationDeviceModel::getAdditionalRootCertificateCheck() { + return get_optional(*storage, keys::valid_keys::AdditionalRootCertificateCheck); +} + +std::optional ChargePointConfigurationDeviceModel::getCertificateSignedMaxChainSize() { + return get_optional(*storage, keys::valid_keys::CertificateSignedMaxChainSize); +} + +std::optional ChargePointConfigurationDeviceModel::getCertificateStoreMaxLength() { + return get_optional(*storage, keys::valid_keys::CertificateStoreMaxLength); +} + +KeyValue ChargePointConfigurationDeviceModel::getDisableSecurityEventNotificationsKeyValue() { + return get_key_value(*storage, keys::valid_keys::DisableSecurityEventNotifications); +} + +KeyValue ChargePointConfigurationDeviceModel::getSecurityProfileKeyValue() { + return get_key_value(*storage, keys::valid_keys::SecurityProfile); +} + +std::optional ChargePointConfigurationDeviceModel::getAdditionalRootCertificateCheckKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::AdditionalRootCertificateCheck); +} + +std::optional ChargePointConfigurationDeviceModel::getAuthorizationKeyKeyValue() { + // AuthorizationKey is writeOnly so we return a dummy when a value is set + // a KeyValue is always returned + const auto key = keys::valid_keys::AuthorizationKey; + v16::KeyValue kv; + kv.key = std::move(std::string{v16::keys::convert(key)}); + kv.readonly = v16::keys::is_readonly(key); + if (key_exists(*storage, key)) { + kv.value = "DummyAuthorizationKey"; + } + return kv; +} + +std::optional ChargePointConfigurationDeviceModel::getCertificateSignedMaxChainSizeKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::CertificateSignedMaxChainSize); +} + +std::optional ChargePointConfigurationDeviceModel::getCertificateStoreMaxLengthKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::CertificateStoreMaxLength); +} + +std::optional ChargePointConfigurationDeviceModel::getCpoNameKeyValue() { + // a KeyValue is always returned + const auto key = keys::valid_keys::CpoName; + v16::KeyValue kv; + kv.key = std::move(std::string{v16::keys::convert(key)}); + kv.readonly = v16::keys::is_readonly(key); + kv.value = get_optional(*storage, key); + return kv; +} + +void ChargePointConfigurationDeviceModel::setAuthorizationKey(const std::string& authorization_key) { + internalAuthorizationKey(authorization_key); +} + +void ChargePointConfigurationDeviceModel::setCpoName(const std::string& cpo_name) { + internalCpoName(cpo_name); +} + +void ChargePointConfigurationDeviceModel::setDisableSecurityEventNotifications( + bool disable_security_event_notifications) { + internalDisableSecurityEventNotifications(to_string(disable_security_event_notifications)); +} + +void ChargePointConfigurationDeviceModel::setSecurityProfile(std::int32_t security_profile) { + internalSecurityProfile(std::to_string(security_profile)); +} + +// ---------------------------------------------------------------------------- +// Local Auth List Management + +bool ChargePointConfigurationDeviceModel::getLocalAuthListEnabled() { + const auto get_result = get_optional(*storage, keys::valid_keys::LocalAuthListEnabled); + return get_result.value_or(false); +} + +std::int32_t ChargePointConfigurationDeviceModel::getLocalAuthListMaxLength() { + return get_value(*storage, keys::valid_keys::LocalAuthListMaxLength); +} + +std::int32_t ChargePointConfigurationDeviceModel::getSendLocalListMaxLength() { + return get_value(*storage, keys::valid_keys::SendLocalListMaxLength); +} + +KeyValue ChargePointConfigurationDeviceModel::getLocalAuthListEnabledKeyValue() { + return get_key_value(*storage, keys::valid_keys::LocalAuthListEnabled); +} + +KeyValue ChargePointConfigurationDeviceModel::getLocalAuthListMaxLengthKeyValue() { + return get_key_value(*storage, keys::valid_keys::LocalAuthListMaxLength); +} + +KeyValue ChargePointConfigurationDeviceModel::getSendLocalListMaxLengthKeyValue() { + return get_key_value(*storage, keys::valid_keys::SendLocalListMaxLength); +} + +void ChargePointConfigurationDeviceModel::setLocalAuthListEnabled(bool local_auth_list_enabled) { + internalLocalAuthListEnabled(to_string(local_auth_list_enabled)); +} + +// ---------------------------------------------------------------------------- +// PnC + +bool ChargePointConfigurationDeviceModel::getContractValidationOffline() { + return get_value(*storage, keys::valid_keys::ContractValidationOffline); +} + +bool ChargePointConfigurationDeviceModel::getISO15118CertificateManagementEnabled() { + return get_value(*storage, keys::valid_keys::ISO15118CertificateManagementEnabled); +} + +bool ChargePointConfigurationDeviceModel::getISO15118PnCEnabled() { + return get_value(*storage, keys::valid_keys::ISO15118PnCEnabled); +} + +std::optional ChargePointConfigurationDeviceModel::getCentralContractValidationAllowed() { + return get_optional(*storage, keys::valid_keys::CentralContractValidationAllowed); +} + +std::optional ChargePointConfigurationDeviceModel::getCertSigningRepeatTimes() { + return get_optional(*storage, keys::valid_keys::CertSigningRepeatTimes); +} + +std::optional ChargePointConfigurationDeviceModel::getCertSigningWaitMinimum() { + return get_optional(*storage, keys::valid_keys::CertSigningWaitMinimum); +} + +KeyValue ChargePointConfigurationDeviceModel::getContractValidationOfflineKeyValue() { + return get_key_value(*storage, keys::valid_keys::ContractValidationOffline); +} + +KeyValue ChargePointConfigurationDeviceModel::getISO15118CertificateManagementEnabledKeyValue() { + return get_key_value(*storage, keys::valid_keys::ISO15118CertificateManagementEnabled); +} + +KeyValue ChargePointConfigurationDeviceModel::getISO15118PnCEnabledKeyValue() { + return get_key_value(*storage, keys::valid_keys::ISO15118PnCEnabled); +} + +std::optional ChargePointConfigurationDeviceModel::getCentralContractValidationAllowedKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::CentralContractValidationAllowed); +} + +std::optional ChargePointConfigurationDeviceModel::getCertSigningRepeatTimesKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::CertSigningRepeatTimes); +} + +std::optional ChargePointConfigurationDeviceModel::getCertSigningWaitMinimumKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::CertSigningWaitMinimum); +} + +void ChargePointConfigurationDeviceModel::setContractValidationOffline(bool contract_validation_offline) { + internalContractValidationOffline(to_string(contract_validation_offline)); +} + +void ChargePointConfigurationDeviceModel::setCentralContractValidationAllowed( + bool central_contract_validation_allowed) { + internalCentralContractValidationAllowed(to_string(central_contract_validation_allowed)); +} + +void ChargePointConfigurationDeviceModel::setCertSigningRepeatTimes(std::int32_t cert_signing_repeat_times) { + internalCertSigningRepeatTimes(std::to_string(cert_signing_repeat_times)); +} + +void ChargePointConfigurationDeviceModel::setCertSigningWaitMinimum(std::int32_t cert_signing_wait_minimum) { + internalCertSigningWaitMinimum(std::to_string(cert_signing_wait_minimum)); +} + +void ChargePointConfigurationDeviceModel::setISO15118CertificateManagementEnabled( + bool iso15118_certificate_management_enabled) { + internalISO15118CertificateManagementEnabled(to_string(iso15118_certificate_management_enabled)); +} + +void ChargePointConfigurationDeviceModel::setISO15118PnCEnabled(bool iso15118_pnc_enabled) { + internalISO15118PnCEnabled(to_string(iso15118_pnc_enabled)); +} + +// ---------------------------------------------------------------------------- +// California Pricing + +bool ChargePointConfigurationDeviceModel::getCustomDisplayCostAndPriceEnabled() { + const auto get_result = get_optional(*storage, keys::valid_keys::CustomDisplayCostAndPrice); + return get_result.value_or(false); +} + +TariffMessage ChargePointConfigurationDeviceModel::getDefaultTariffMessage(bool offline) { + // DefaultPrice and DefaultPriceText are JSON strings + + TariffMessage tariff_message; + const auto& default_price = get_optional(*storage, keys::valid_keys::DefaultPrice); + const auto& default_price_text = get_optional(*storage, keys::valid_keys::DefaultPriceText); + const std::string_view key = offline ? "priceTextOffline" : "priceText"; + + if (default_price) { + try { + const auto default_price_json = json::parse(default_price.value()); + if (default_price_json.contains(key)) { + DisplayMessageContent content; + content.message = default_price_json.at(key); + content.language = getLanguage(); + tariff_message.message.push_back(content); + } + } catch (const std::exception& ex) { + EVLOG_error << "Default price json not correct, can not fetch default price : " << ex.what(); + } + } + + if (default_price_text) { + try { + const auto default_price_text_json = json::parse(default_price_text.value()); + if (default_price_text_json.contains("priceTexts")) { + for (const auto& item : default_price_text_json.at("priceTexts")) { + if (item.contains(key) && item.contains("language")) { + DisplayMessageContent content; + content.message = item.at(key); + content.language = item.at("language"); + tariff_message.message.push_back(content); + } + } + } + } catch (const std::exception& ex) { + EVLOG_error << "Default price text json not correct, can not fetch default price text : " << ex.what(); + } + } + + if (!default_price && !default_price_text) { + EVLOG_warning << "No CostAndPrice configuration found, returning empty TariffMessage."; + } else if (tariff_message.message.empty()) { + EVLOG_warning << "No tariff message found in CostAndPrice configuration, returning empty TariffMessage."; + } + + return tariff_message; +} + +std::optional ChargePointConfigurationDeviceModel::getDefaultPrice() { + return get_optional(*storage, keys::valid_keys::DefaultPrice); +} + +std::optional ChargePointConfigurationDeviceModel::getDefaultPriceText(const std::string& language) { + // DefaultPriceText is a JSON string + + const auto& default_price_text = get_optional(*storage, keys::valid_keys::DefaultPriceText); + std::optional result; + + if (default_price_text) { + try { + const auto default_price_text_json = json::parse(default_price_text.value()); + json json_result = json::object(); + + if (default_price_text_json.contains("priceTexts")) { + for (const auto& price_text : default_price_text_json.at("priceTexts").items()) { + if (language == price_text.value().at("language").get()) { + // Language found. + json_result["priceText"] = price_text.value().at("priceText"); + if (price_text.value().contains("priceTextOffline")) { + json_result["priceTextOffline"] = price_text.value().at("priceTextOffline"); + } + result = json_result.dump(2); + break; + } + } + } + } catch (const std::exception& ex) { + EVLOG_error << "Default price text json not correct, can not fetch default price text : " << ex.what(); + } + } + + return result; +} + +std::optional ChargePointConfigurationDeviceModel::getDisplayTimeOffset() { + return get_optional(*storage, keys::valid_keys::TimeOffset); +} + +std::optional ChargePointConfigurationDeviceModel::getLanguage() { + return get_optional(*storage, keys::valid_keys::Language); +} +std::optional ChargePointConfigurationDeviceModel::getMultiLanguageSupportedLanguages() { + return get_optional(*storage, keys::valid_keys::SupportedLanguages); +} + +std::optional ChargePointConfigurationDeviceModel::getNextTimeOffsetTransitionDateTime() { + return get_optional(*storage, keys::valid_keys::NextTimeOffsetTransitionDateTime); +} + +std::optional ChargePointConfigurationDeviceModel::getTimeOffsetNextTransition() { + return get_optional(*storage, keys::valid_keys::TimeOffsetNextTransition); +} + +std::optional ChargePointConfigurationDeviceModel::getCustomIdleFeeAfterStop() { + return get_optional(*storage, keys::valid_keys::CustomIdleFeeAfterStop); +} + +std::optional ChargePointConfigurationDeviceModel::getCustomMultiLanguageMessagesEnabled() { + return get_optional(*storage, keys::valid_keys::CustomMultiLanguageMessages); +} + +std::optional ChargePointConfigurationDeviceModel::getWaitForSetUserPriceTimeout() { + return get_optional(*storage, keys::valid_keys::WaitForSetUserPriceTimeout); +} + +std::optional ChargePointConfigurationDeviceModel::getPriceNumberOfDecimalsForCostValues() { + return get_optional(*storage, keys::valid_keys::NumberOfDecimalsForCostValues); +} + +KeyValue ChargePointConfigurationDeviceModel::getCustomDisplayCostAndPriceEnabledKeyValue() { + return get_key_value(*storage, keys::valid_keys::CustomDisplayCostAndPrice); +} + +KeyValue ChargePointConfigurationDeviceModel::getDefaultPriceTextKeyValue(const std::string& language) { + KeyValue result; + result.key = "DefaultPriceText," + language; + result.readonly = false; + const auto get_result = getDefaultPriceText(language); + if (get_result) { + result.value = get_result.value(); + } else { + // It's a bit odd to return an empty string here, but it must be possible to set a default price text for a + // new language. But since the 'set' function for configurations first performs a 'get' and does not continue + // if it receives a nullopt, we can better just return an empty string so the 'set' function can continue. + // Resolving it differently required more complexer code, this was the easiest way to do it. + result.value = ""; + } + return result; +} + +std::optional ChargePointConfigurationDeviceModel::getCustomIdleFeeAfterStopKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::CustomIdleFeeAfterStop); +} + +std::optional ChargePointConfigurationDeviceModel::getCustomMultiLanguageMessagesEnabledKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::CustomMultiLanguageMessages); +} + +std::optional ChargePointConfigurationDeviceModel::getDefaultPriceKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::DefaultPrice); +} + +std::optional ChargePointConfigurationDeviceModel::getDisplayTimeOffsetKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::TimeOffset); +} + +std::optional ChargePointConfigurationDeviceModel::getLanguageKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::Language); +} + +std::optional ChargePointConfigurationDeviceModel::getMultiLanguageSupportedLanguagesKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::SupportedLanguages); +} + +std::optional ChargePointConfigurationDeviceModel::getNextTimeOffsetTransitionDateTimeKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::NextTimeOffsetTransitionDateTime); +} + +std::optional ChargePointConfigurationDeviceModel::getPriceNumberOfDecimalsForCostValuesKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::NumberOfDecimalsForCostValues); +} + +std::optional ChargePointConfigurationDeviceModel::getTimeOffsetNextTransitionKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::TimeOffsetNextTransition); +} + +std::optional ChargePointConfigurationDeviceModel::getWaitForSetUserPriceTimeoutKeyValue() { + return get_key_value_optional(*storage, keys::valid_keys::WaitForSetUserPriceTimeout); +} + +std::optional> ChargePointConfigurationDeviceModel::getAllDefaultPriceTextKeyValues() { + std::optional> result; + + // DefaultPriceText is a JSON string + const auto& default_price_text = get_optional(*storage, keys::valid_keys::DefaultPriceText); + + if (default_price_text) { + std::vector key_values; + try { + const auto default_price_text_json = json::parse(default_price_text.value()); + if (default_price_text_json.contains("priceTexts")) { + for (auto& price_text : default_price_text_json.at("priceTexts").items()) { + json result = json::object(); + const std::string language = price_text.value().at("language"); + result["priceText"] = price_text.value().at("priceText"); + if (price_text.value().contains("priceTextOffline")) { + result["priceTextOffline"] = price_text.value().at("priceTextOffline"); + } + + KeyValue kv; + kv.value = result.dump(2); + kv.readonly = false; + kv.key = "DefaultPriceText," + language; + key_values.push_back(kv); + } + } + } catch (const std::exception& ex) { + EVLOG_error << "Default price text json not correct, can not fetch default price text : " << ex.what(); + } + + if (!key_values.empty()) { + result = std::move(key_values); + } + } + + return result; +} + +void ChargePointConfigurationDeviceModel::setCustomIdleFeeAfterStop(bool value) { + internalCustomIdleFeeAfterStop(to_string(value)); +} + +ConfigurationStatus ChargePointConfigurationDeviceModel::setDefaultPrice(const std::string& value) { + return convert(internalDefaultPrice(value)); +} + +ConfigurationStatus ChargePointConfigurationDeviceModel::setDefaultPriceText(const CiString<50>& key, + const CiString<500>& value) { + return convert(internalDefaultPriceText(key, value)); +} + +ConfigurationStatus ChargePointConfigurationDeviceModel::setDisplayTimeOffset(const std::string& offset) { + return convert(internalDisplayTimeOffset(offset)); +} + +void ChargePointConfigurationDeviceModel::setLanguage(const std::string& language) { + internalLanguage(language); +} + +ConfigurationStatus +ChargePointConfigurationDeviceModel::setNextTimeOffsetTransitionDateTime(const std::string& date_time) { + return convert(internalNextTimeOffsetTransitionDateTime(date_time)); +} + +ConfigurationStatus ChargePointConfigurationDeviceModel::setTimeOffsetNextTransition(const std::string& offset) { + return convert(internalTimeOffsetNextTransition(offset)); +} + +void ChargePointConfigurationDeviceModel::setWaitForSetUserPriceTimeout(std::int32_t wait_for_set_user_price_timeout) { + internalWaitForSetUserPriceTimeout(std::to_string(wait_for_set_user_price_timeout)); +} + +// ---------------------------------------------------------------------------- +// Custom + +std::optional ChargePointConfigurationDeviceModel::getCustomKeyValue(const CiString<50>& key) { + std::string sv_key{key}; + return get_key_value_optional(*storage, sv_key); +} + +ConfigurationStatus ChargePointConfigurationDeviceModel::setCustomKey(const CiString<50>& key, + const CiString<500>& value, bool force) { + const std::string sv_key{key}; + auto result = ConfigurationStatus::Rejected; + + const auto exists_ro = isReadOnly(*storage, custom_component, sv_key); + if (exists_ro) { + // the key exists (not allowed to create keys) + const auto ro = exists_ro.value(); + + if (!ro || force) { + try { + // validation is performed by the device model implementation + const auto res = set_value(*storage, sv_key, std::string{value}); + result = convert(res); + } catch (const std::exception& e) { + EVLOG_warning << "Could not set custom configuration key: " << e.what(); + } + } + } + + return result; +} + +std::optional ChargePointConfigurationDeviceModel::get(const CiString<50>& key) { + std::optional result; + const std::string key_str{key}; + const auto sv_key_opt = keys::convert(key_str); + bool get_value{true}; + + if (sv_key_opt) { + const auto sv_key = sv_key_opt.value(); + + if (ignore_key(sv_key, supported_feature_profiles, SupportedFeatureProfiles::FirmwareManagement)) { + get_value = false; + } + if (ignore_key(sv_key, supported_feature_profiles, SupportedFeatureProfiles::PnC)) { + get_value = false; + } + if (ignore_key(sv_key, supported_feature_profiles, SupportedFeatureProfiles::SmartCharging)) { + get_value = false; + } + if (ignore_key(sv_key, supported_feature_profiles, SupportedFeatureProfiles::Security)) { + get_value = false; + } + if (ignore_key(sv_key, supported_feature_profiles, SupportedFeatureProfiles::LocalAuthListManagement)) { + get_value = false; + } + if (ignore_key(sv_key, supported_feature_profiles, SupportedFeatureProfiles::CostAndPrice)) { + get_value = false; + } + + // TODO(james-ctc): check for SupportedFeatureProfiles::Custom from device model + + if (keys::is_hidden(sv_key)) { + // we should not return an AuthorizationKey because it's write only + get_value = false; + } + } else { + if (supported_feature_profiles.find(SupportedFeatureProfiles::CostAndPrice) != + supported_feature_profiles.end()) { + // check keys starting DefaultPriceText + if (key_str.find("DefaultPriceText") == 0) { + if (getCustomMultiLanguageMessagesEnabled().value_or(false)) { + const auto lang = utils::split_string(',', key_str); + if (lang.size() == 2) { + result = getDefaultPriceTextKeyValue(lang.at(1)); + } + } + get_value = false; + } + } + // check keys starting MeterPublicKey[ + if (key_str.find("MeterPublicKey[") == 0) { + auto id = extract_connector_id(key_str); + if (id) { + result = getPublicKeyKeyValue(id.value()); + } + get_value = false; + } + } + + if (get_value) { + if (sv_key_opt) { + // known key + result = get_key_value_optional(*storage, sv_key_opt.value()); + } else { + // custom key + result = get_key_value_optional(*storage, key_str); + } + } + + return result; +} + +std::vector ChargePointConfigurationDeviceModel::get_all_key_value() { + std::vector all; + const auto report = storage->get_base_report_data(v2::ReportBaseEnum::ConfigurationInventory); + for (const auto& entry : report) { + const auto& component = entry.component; + const auto& variable = entry.variable; + try { + const auto feature = conversions::string_to_supported_feature_profiles(component.name); + if (const auto it = supported_feature_profiles.find(feature); it != supported_feature_profiles.end()) { + auto kv = get(variable.name); + if (kv) { + all.push_back(std::move(kv.value())); + } + } + } catch (std::exception& ex) { + EVLOG_error << "Device model '" << component.name << "' not supported: " << ex.what(); + } + } + return all; +} + +std::optional ChargePointConfigurationDeviceModel::set(const CiString<50>& key, + const CiString<500>& value) { + std::optional result; + const std::string key_str{key}; + const std::string value_str{value}; + const auto sv_key_opt = keys::convert(key_str); + + if (sv_key_opt) { + const auto sv_key = sv_key_opt.value(); + + switch (sv_key) { + case keys::valid_keys::AllowChargingProfileWithoutStartSchedule: + result = convert(internalAllowChargingProfileWithoutStartSchedule(value_str)); + break; + case keys::valid_keys::AllowOfflineTxForUnknownId: + result = convert(internalAllowOfflineTxForUnknownId(value_str)); + break; + case keys::valid_keys::CentralSystemURI: + result = convert(internalCentralSystemURI(value_str)); + break; + case keys::valid_keys::CompositeScheduleDefaultLimitAmps: + result = convert(internalCompositeScheduleDefaultLimitAmps(value_str)); + break; + case keys::valid_keys::CompositeScheduleDefaultLimitWatts: + result = convert(internalCompositeScheduleDefaultLimitWatts(value_str)); + break; + case keys::valid_keys::CompositeScheduleDefaultNumberPhases: + result = convert(internalCompositeScheduleDefaultNumberPhases(value_str)); + break; + case keys::valid_keys::ConnectorEvseIds: + result = convert(internalConnectorEvseIds(value_str)); + break; + case keys::valid_keys::IgnoredProfilePurposesOffline: + result = convert(internalIgnoredProfilePurposesOffline(value_str)); + break; + case keys::valid_keys::OcspRequestInterval: + result = convert(internalOcspRequestInterval(value_str)); + break; + case keys::valid_keys::RetryBackoffRandomRange: + result = convert(internalRetryBackoffRandomRange(value_str)); + break; + case keys::valid_keys::RetryBackoffRepeatTimes: + result = convert(internalRetryBackoffRepeatTimes(value_str)); + break; + case keys::valid_keys::RetryBackoffWaitMinimum: + result = convert(internalRetryBackoffWaitMinimum(value_str)); + break; + case keys::valid_keys::SeccLeafSubjectCommonName: + result = convert(internalSeccLeafSubjectCommonName(value_str)); + break; + case keys::valid_keys::SeccLeafSubjectCountry: + result = convert(internalSeccLeafSubjectCountry(value_str)); + break; + case keys::valid_keys::SeccLeafSubjectOrganization: + result = convert(internalSeccLeafSubjectOrganization(value_str)); + break; + case keys::valid_keys::StopTransactionIfUnlockNotSupported: + result = convert(internalStopTransactionIfUnlockNotSupported(value_str)); + break; + case keys::valid_keys::SupplyVoltage: + result = convert(internalSupplyVoltage(value_str)); + break; + case keys::valid_keys::VerifyCsmsAllowWildcards: + result = convert(internalVerifyCsmsAllowWildcards(value_str)); + break; + case keys::valid_keys::WaitForStopTransactionsOnResetTimeout: + result = convert(internalWaitForStopTransactionsOnResetTimeout(value_str)); + break; + case keys::valid_keys::AuthorizationCacheEnabled: + result = convert(internalAuthorizationCacheEnabled(value_str)); + break; + case keys::valid_keys::AuthorizeRemoteTxRequests: + result = convert(internalAuthorizeRemoteTxRequests(value_str)); + break; + case keys::valid_keys::BlinkRepeat: + result = convert(internalBlinkRepeat(value_str)); + break; + case keys::valid_keys::ClockAlignedDataInterval: + result = convert(internalClockAlignedDataInterval(value_str)); + break; + case keys::valid_keys::ConnectionTimeOut: + result = convert(internalConnectionTimeOut(value_str)); + break; + case keys::valid_keys::ConnectorPhaseRotation: + result = convert(internalConnectorPhaseRotation(value_str)); + break; + case keys::valid_keys::HeartbeatInterval: + result = convert(internalHeartbeatInterval(value_str)); + break; + case keys::valid_keys::LightIntensity: + result = convert(internalLightIntensity(value_str)); + break; + case keys::valid_keys::LocalAuthorizeOffline: + result = convert(internalLocalAuthorizeOffline(value_str)); + break; + case keys::valid_keys::LocalPreAuthorize: + result = convert(internalLocalPreAuthorize(value_str)); + break; + case keys::valid_keys::MaxEnergyOnInvalidId: + result = convert(internalMaxEnergyOnInvalidId(value_str)); + break; + case keys::valid_keys::MeterValuesAlignedData: + result = convert(internalMeterValuesAlignedData(value_str)); + break; + case keys::valid_keys::MeterValuesSampledData: + result = convert(internalMeterValuesSampledData(value_str)); + break; + case keys::valid_keys::MeterValueSampleInterval: + result = convert(internalMeterValueSampleInterval(value_str)); + break; + case keys::valid_keys::MinimumStatusDuration: + result = convert(internalMinimumStatusDuration(value_str)); + break; + case keys::valid_keys::ResetRetries: + result = convert(internalResetRetries(value_str)); + break; + case keys::valid_keys::StopTransactionOnInvalidId: + result = convert(internalStopTransactionOnInvalidId(value_str)); + break; + case keys::valid_keys::StopTxnAlignedData: + result = convert(internalStopTxnAlignedData(value_str)); + break; + case keys::valid_keys::StopTxnSampledData: + result = convert(internalStopTxnAlignedData(value_str)); + break; + case keys::valid_keys::TransactionMessageAttempts: + result = convert(internalTransactionMessageAttempts(value_str)); + break; + case keys::valid_keys::TransactionMessageRetryInterval: + result = convert(internalTransactionMessageRetryInterval(value_str)); + break; + case keys::valid_keys::UnlockConnectorOnEVSideDisconnect: + result = convert(internalUnlockConnectorOnEVSideDisconnect(value_str)); + break; + case keys::valid_keys::WebSocketPingInterval: + result = convert(internalWebsocketPingInterval(value_str)); + break; + case keys::valid_keys::AuthorizationKey: + result = convert(internalAuthorizationKey(value_str)); + break; + case keys::valid_keys::CpoName: + result = convert(internalCpoName(value_str)); + break; + case keys::valid_keys::DisableSecurityEventNotifications: + result = convert(internalDisableSecurityEventNotifications(value_str)); + break; + case keys::valid_keys::SecurityProfile: + result = convert(internalSecurityProfile(value_str)); + break; + case keys::valid_keys::ISO15118CertificateManagementEnabled: + result = convert(internalISO15118CertificateManagementEnabled(value_str)); + break; + case keys::valid_keys::LocalAuthListEnabled: + result = convert(internalLocalAuthListEnabled(value_str)); + break; + case keys::valid_keys::ContractValidationOffline: + result = convert(internalContractValidationOffline(value_str)); + break; + case keys::valid_keys::CentralContractValidationAllowed: + result = convert(internalCentralContractValidationAllowed(value_str)); + break; + case keys::valid_keys::CertSigningRepeatTimes: + result = convert(internalCertSigningRepeatTimes(value_str)); + break; + case keys::valid_keys::CertSigningWaitMinimum: + result = convert(internalCertSigningWaitMinimum(value_str)); + break; + case keys::valid_keys::ISO15118PnCEnabled: + result = convert(internalISO15118PnCEnabled(value_str)); + break; + case keys::valid_keys::CustomIdleFeeAfterStop: + result = convert(internalCustomIdleFeeAfterStop(value_str)); + break; + case keys::valid_keys::DefaultPrice: + result = convert(internalDefaultPrice(value_str)); + break; + case keys::valid_keys::DefaultPriceText: + // should never match - language expected: DefaultPriceText,de + EVLOG_error << R"(ChargePointConfiguration::set("DefaultPriceText", )" << value << R"(") not supported)"; + result = ConfigurationStatus::NotSupported; + break; + case keys::valid_keys::MeterPublicKeys: + // should never match - connector ID expected: MeterPublicKey[1] + EVLOG_error << R"(ChargePointConfiguration::set("MeterPublicKey", )" << value << R"(") not supported)"; + result = ConfigurationStatus::NotSupported; + break; + case keys::valid_keys::TimeOffset: + result = convert(internalDisplayTimeOffset(value_str)); + break; + case keys::valid_keys::Language: + result = convert(internalLanguage(value_str)); + break; + case keys::valid_keys::NextTimeOffsetTransitionDateTime: + result = convert(internalNextTimeOffsetTransitionDateTime(value_str)); + break; + case keys::valid_keys::TimeOffsetNextTransition: + result = convert(internalTimeOffsetNextTransition(value_str)); + break; + case keys::valid_keys::WaitForSetUserPriceTimeout: + result = convert(internalWaitForSetUserPriceTimeout(value_str)); + break; + + case keys::valid_keys::ChargeBoxSerialNumber: + case keys::valid_keys::ChargePointModel: + case keys::valid_keys::ChargePointSerialNumber: + case keys::valid_keys::ChargePointVendor: + case keys::valid_keys::FirmwareVersion: + case keys::valid_keys::ICCID: + case keys::valid_keys::IMSI: + case keys::valid_keys::MeterSerialNumber: + // these are not setable via OCPP - std::nullopt expected + break; + + case keys::valid_keys::EnableTLSKeylog: + case keys::valid_keys::LogRotation: + case keys::valid_keys::LogRotationDateSuffix: + case keys::valid_keys::LogRotationMaximumFileCount: + case keys::valid_keys::LogRotationMaximumFileSize: + case keys::valid_keys::TLSKeylogFile: + case keys::valid_keys::UseTPM: + case keys::valid_keys::UseTPMSeccLeafCertificate: + // hidden keys - std::nullopt expected + break; + + case keys::valid_keys::ConnectorPhaseRotationMaxLength: + case keys::valid_keys::GetConfigurationMaxKeys: + case keys::valid_keys::MeterValuesAlignedDataMaxLength: + case keys::valid_keys::MeterValuesSampledDataMaxLength: + case keys::valid_keys::NumberOfConnectors: + case keys::valid_keys::StopTransactionOnEVSideDisconnect: + case keys::valid_keys::StopTxnAlignedDataMaxLength: + case keys::valid_keys::StopTxnSampledDataMaxLength: + case keys::valid_keys::SupportedFeatureProfiles: + case keys::valid_keys::SupportedFeatureProfilesMaxLength: + case keys::valid_keys::CustomDisplayCostAndPrice: + case keys::valid_keys::CustomMultiLanguageMessages: + case keys::valid_keys::NumberOfDecimalsForCostValues: + case keys::valid_keys::SupportedLanguages: + case keys::valid_keys::SupportedFileTransferProtocols: + case keys::valid_keys::AuthorizeConnectorZeroOnConnectorOne: + case keys::valid_keys::ChargePointId: + case keys::valid_keys::HostName: + case keys::valid_keys::IFace: + case keys::valid_keys::LogMessages: + case keys::valid_keys::LogMessagesFormat: + case keys::valid_keys::LogMessagesRaw: + case keys::valid_keys::MaxCompositeScheduleDuration: + case keys::valid_keys::MaxMessageSize: + case keys::valid_keys::MessageQueueSizeThreshold: + case keys::valid_keys::MessageTypesDiscardForQueueing: + case keys::valid_keys::MeterType: + case keys::valid_keys::QueueAllMessages: + case keys::valid_keys::SupportedChargingProfilePurposeTypes: + case keys::valid_keys::SupportedCiphers12: + case keys::valid_keys::SupportedCiphers13: + case keys::valid_keys::SupportedMeasurands: + case keys::valid_keys::UseSslDefaultVerifyPaths: + case keys::valid_keys::VerifyCsmsCommonName: + case keys::valid_keys::WebsocketPingPayload: + case keys::valid_keys::WebsocketPongTimeout: + case keys::valid_keys::LocalAuthListMaxLength: + case keys::valid_keys::SendLocalListMaxLength: + case keys::valid_keys::ReserveConnectorZeroSupported: + case keys::valid_keys::AdditionalRootCertificateCheck: + case keys::valid_keys::CertificateSignedMaxChainSize: + case keys::valid_keys::CertificateStoreMaxLength: + case keys::valid_keys::ChargeProfileMaxStackLevel: + case keys::valid_keys::ChargingScheduleAllowedChargingRateUnit: + case keys::valid_keys::ChargingScheduleMaxPeriods: + case keys::valid_keys::ConnectorSwitch3to1PhaseSupported: + case keys::valid_keys::MaxChargingProfilesInstalled: + default: + // std::nullopt expected + break; + } + } else { + if (key_str.find("DefaultPriceText") == 0) { + if (supported_feature_profiles.find(SupportedFeatureProfiles::CostAndPrice) != + supported_feature_profiles.end()) { + // check keys starting DefaultPriceText + result = setDefaultPriceText(key, value); + } else { + result = ConfigurationStatus::NotSupported; + } + } else if (key_str.find("MeterPublicKey[") == 0) { + // not setable + } else { + // custom key + const auto exists_ro = isReadOnly(*storage, custom_component, key_str); + if (!exists_ro.value_or(true)) { + // key exists and is not read-only + const auto res = set_value(*storage, key_str, value_str); + result = convert(res); + } + } + } + + return result; +} + +std::set ChargePointConfigurationDeviceModel::getSupportedMessageTypesSending() { + return supported_message_types_sending; +} + +std::set ChargePointConfigurationDeviceModel::getSupportedMessageTypesReceiving() { + return supported_message_types_receiving; +} + +} // namespace ocpp::v16 diff --git a/lib/everest/ocpp/lib/ocpp/v16/charge_point_impl.cpp b/lib/everest/ocpp/lib/ocpp/v16/charge_point_impl.cpp index 4eee46dcb6..474a139852 100644 --- a/lib/everest/ocpp/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/everest/ocpp/lib/ocpp/v16/charge_point_impl.cpp @@ -36,12 +36,12 @@ const auto DEFAULT_BOOT_NOTIFICATION_INTERVAL_S = 60; // fallback interval if Bo const auto DEFAULT_PRICE_NUMBER_OF_DECIMALS = 3; const auto DEFAULT_WAIT_FOR_SET_USER_PRICE_TIMEOUT_MS = 0; -ChargePointImpl::ChargePointImpl(const std::string& config, const fs::path& share_path, - const fs::path& user_config_path, const fs::path& database_path, - const fs::path& sql_init_path, const fs::path& message_log_path, - const std::shared_ptr evse_security, +ChargePointImpl::ChargePointImpl(std::unique_ptr config, + const fs::path& database_path, const fs::path& sql_init_path, + const fs::path& message_log_path, const std::shared_ptr evse_security, const std::optional security_configuration) : ocpp::ChargingStationBase(evse_security, security_configuration), + configuration(std::move(config)), bootreason(BootReasonEnum::PowerUp), initialized(false), InvalidCSMSCertificate_logged(false), @@ -52,7 +52,6 @@ ChargePointImpl::ChargePointImpl(const std::string& config, const fs::path& shar log_status(UploadLogStatusEnumType::Idle), message_log_path(message_log_path.string()), // .string() for compatibility with boost::filesystem switch_security_profile_callback(nullptr) { - this->configuration = std::make_shared(config, share_path, user_config_path); this->heartbeat_timer = std::make_unique(&this->io_context, [this]() { this->heartbeat(); }); this->heartbeat_interval = this->configuration->getHeartbeatInterval(); auto database_connection = std::make_unique( diff --git a/lib/everest/ocpp/lib/ocpp/v16/known_keys.cpp b/lib/everest/ocpp/lib/ocpp/v16/known_keys.cpp new file mode 100644 index 0000000000..48540f35ac --- /dev/null +++ b/lib/everest/ocpp/lib/ocpp/v16/known_keys.cpp @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020 - 2026 Pionix GmbH and Contributors to EVerest + +#include + +#include +#include + +namespace { +using ocpp::v16::keys::valid_keys; + +// clang-format off +#define FOR_ALL_READONLY(key) \ + key(CentralSystemURI) \ + key(ChargePointId) \ + key(ChargeBoxSerialNumber) \ + key(ChargePointModel) \ + key(ChargePointSerialNumber) \ + key(ChargePointVendor) \ + key(FirmwareVersion) \ + key(ICCID) \ + key(IMSI) \ + key(MeterSerialNumber) \ + key(MeterType) \ + key(AuthorizeConnectorZeroOnConnectorOne) \ + key(LogMessages) \ + key(LogMessagesRaw) \ + key(LogMessagesFormat) \ + key(LogRotation) \ + key(LogRotationDateSuffix) \ + key(LogRotationMaximumFileSize) \ + key(LogRotationMaximumFileCount) \ + key(SupportedChargingProfilePurposeTypes) \ + key(MaxCompositeScheduleDuration) \ + key(SupportedCiphers12) \ + key(SupportedCiphers13) \ + key(UseSslDefaultVerifyPaths) \ + key(VerifyCsmsCommonName) \ + key(VerifyCsmsAllowWildcards) \ + key(SupportedMeasurands) \ + key(MaxMessageSize) \ + key(WebsocketPingPayload) \ + key(WebsocketPongTimeout) \ + key(QueueAllMessages) \ + key(MessageTypesDiscardForQueueing) \ + key(MessageQueueSizeThreshold) \ + key(ConnectorPhaseRotationMaxLength) \ + key(GetConfigurationMaxKeys) \ + key(MeterValuesAlignedDataMaxLength) \ + key(MeterValuesSampledDataMaxLength) \ + key(NumberOfConnectors) \ + key(ReserveConnectorZeroSupported) \ + key(StopTransactionOnEVSideDisconnect) \ + key(StopTxnAlignedDataMaxLength) \ + key(StopTxnSampledDataMaxLength) \ + key(SupportedFeatureProfiles) \ + key(SupportedFeatureProfilesMaxLength) \ + key(HostName) \ + key(IFace) \ + key(SupportedFileTransferProtocols) \ + key(ChargeProfileMaxStackLevel) \ + key(ChargingScheduleAllowedChargingRateUnit) \ + key(ChargingScheduleMaxPeriods) \ + key(ConnectorSwitch3to1PhaseSupported) \ + key(MaxChargingProfilesInstalled) \ + key(AdditionalRootCertificateCheck) \ + key(CertificateSignedMaxChainSize) \ + key(CertificateStoreMaxLength) \ + key(LocalAuthListMaxLength) \ + key(SendLocalListMaxLength) \ + key(CustomDisplayCostAndPrice) \ + key(NumberOfDecimalsForCostValues) \ + key(CustomMultiLanguageMessages) \ + key(SupportedLanguages) \ + key(Language) + +// Hidden keys are ones that are not made available over OCPP +// AuthorizationKey because it contains the connection secret +// because they are not in ChargePointConfiguration::get() +// (which could be a bug) + +#define FOR_ALL_HIDDEN(key) \ + key(AuthorizationKey) \ + key(EnableTLSKeylog) \ + key(LogRotation) \ + key(LogRotationDateSuffix) \ + key(LogRotationMaximumFileCount) \ + key(LogRotationMaximumFileSize) \ + key(TLSKeylogFile) \ + key(UseTPM) \ + key(UseTPMSeccLeafCertificate) + +// clang-format on + +#define VALUE(a) valid_keys::a, + +constexpr valid_keys read_only[] = {FOR_ALL_READONLY(VALUE)}; +constexpr valid_keys hidden[] = {FOR_ALL_HIDDEN(VALUE)}; + +#undef VALUE +} // namespace + +namespace ocpp::v16::keys { + +#define VALUE(a, b) \ + if (str == #b) { \ + return valid_keys::b; \ + } + +std::optional convert(const std::string_view& str) { + FOR_ALL_KEYS(VALUE) + return {}; +} + +#undef VALUE +#define VALUE(a, b) \ + case valid_keys::b: \ + return #b; + +std::string_view convert(valid_keys key) { + switch (key) { + FOR_ALL_KEYS(VALUE) + default: + break; + } + return {}; +} + +#undef VALUE +#define VALUE(a, b) \ + case valid_keys::b: \ + return sections::a; + +sections to_section(valid_keys key) { + switch (key) { + FOR_ALL_KEYS(VALUE) + default: + break; + } + return sections::Custom; +} + +#undef VALUE +#define VALUE(a, b) \ + case valid_keys::b: \ + return #a; + +std::string_view to_section_string_view(valid_keys key) { + switch (key) { + FOR_ALL_KEYS(VALUE) + default: + break; + } + return {}; +} + +#undef VALUE + +bool is_readonly(valid_keys key) { + return std::find(std::cbegin(read_only), std::cend(read_only), key) != std::cend(read_only); +} + +bool is_hidden(valid_keys key) { + return std::find(std::cbegin(hidden), std::cend(hidden), key) != std::cend(hidden); +} + +} // namespace ocpp::v16::keys diff --git a/lib/everest/ocpp/lib/ocpp/v16/smart_charging.cpp b/lib/everest/ocpp/lib/ocpp/v16/smart_charging.cpp index 86e217e672..9a13ff8520 100644 --- a/lib/everest/ocpp/lib/ocpp/v16/smart_charging.cpp +++ b/lib/everest/ocpp/lib/ocpp/v16/smart_charging.cpp @@ -101,7 +101,7 @@ bool validate_schedule(const ChargingSchedule& schedule, const int charging_sche SmartChargingHandler::SmartChargingHandler(std::map>& connectors, std::shared_ptr database_handler, - ChargePointConfiguration& configuration) : + ChargePointConfigurationInterface& configuration) : connectors(connectors), database_handler(database_handler), configuration(configuration) { this->clear_profiles_timer = std::make_unique(); this->clear_profiles_timer->interval([this]() { this->clear_expired_profiles(date::utc_clock::now()); }, @@ -148,7 +148,7 @@ struct CompositeScheduleConfig { std::int32_t default_number_phases{}; float supply_voltage{}; - CompositeScheduleConfig(ChargePointConfiguration& configuration, bool is_offline) { + CompositeScheduleConfig(ChargePointConfigurationInterface& configuration, bool is_offline) { if (is_offline) { const auto _purposes_to_ignore = configuration.getIgnoredProfilePurposesOffline(); diff --git a/lib/everest/ocpp/lib/ocpp/v16/utils.cpp b/lib/everest/ocpp/lib/ocpp/v16/utils.cpp index 1484584eb1..71892eb4e1 100644 --- a/lib/everest/ocpp/lib/ocpp/v16/utils.cpp +++ b/lib/everest/ocpp/lib/ocpp/v16/utils.cpp @@ -4,9 +4,7 @@ #include #include -namespace ocpp { -namespace v16 { -namespace utils { +namespace ocpp::v16::utils { size_t get_message_size(const ocpp::Call& call) { return json(call).at(CALL_PAYLOAD).dump().length(); @@ -47,6 +45,53 @@ bool is_critical(const std::string& security_event) { return false; } -} // namespace utils -} // namespace v16 -} // namespace ocpp +std::string to_csl(const std::vector& vec) { + std::string csl; + for (const auto& i : vec) { + csl += i; + csl += ","; + } + if (!csl.empty()) { + csl.pop_back(); + } + return csl; +} + +std::vector from_csl(const std::string& csl) { + std::vector vec; + auto start = csl.find_first_not_of(','); + while (start != std::string::npos) { + auto end = csl.find_first_of(',', start); + if (end == std::string::npos) { + vec.push_back(std::move(csl.substr(start))); + start = std::string::npos; + } else { + vec.push_back(std::move(csl.substr(start, end - start))); + start = csl.find_first_not_of(',', end); + } + } + return vec; +} + +std::vector split_string(char separator, const std::string& csl) { + std::vector vec; + std::size_t start{0}; + if (!csl.empty()) { + while (start != std::string::npos) { + auto end = csl.find_first_of(separator, start); + if (end == std::string::npos) { + vec.push_back(std::move(csl.substr(start))); + start = std::string::npos; + } else if (start == end) { + vec.emplace_back(); + start++; + } else { + vec.push_back(std::move(csl.substr(start, end - start))); + start = end + 1; + } + } + } + return vec; +} + +} // namespace ocpp::v16::utils diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/CMakeLists.txt b/lib/everest/ocpp/tests/lib/ocpp/v16/CMakeLists.txt index f148ff6265..7d9654655d 100644 --- a/lib/everest/ocpp/tests/lib/ocpp/v16/CMakeLists.txt +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/CMakeLists.txt @@ -3,6 +3,18 @@ target_include_directories(libocpp_unit_tests PUBLIC ) target_sources(libocpp_unit_tests PRIVATE + v2config/memory_storage.cpp + v2config/test_california_pricing.cpp + v2config/test_config.cpp + v2config/test_core.cpp + v2config/test_custom.cpp + v2config/test_firmware_management.cpp + v2config/test_internal.cpp + v2config/test_local_auth_list.cpp + v2config/test_pnc.cpp + v2config/test_security.cpp + v2config/test_smart_charging.cpp + v2config/test_user_config.cpp profile_tests_common.cpp profile_testsA.cpp profile_testsB.cpp @@ -16,6 +28,7 @@ target_sources(libocpp_unit_tests PRIVATE test_config_validation.cpp utils_tests.cpp test_configuration.cpp + test_utils.cpp ) # Copy the json files used for testing to the destination directory diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/database_stub.hpp b/lib/everest/ocpp/tests/lib/ocpp/v16/database_stub.hpp index 9be8c7d51f..73ede5bfbf 100644 --- a/lib/everest/ocpp/tests/lib/ocpp/v16/database_stub.hpp +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/database_stub.hpp @@ -4,6 +4,7 @@ #ifndef DATABASE_STUB_HPP #define DATABASE_STUB_HPP +#include "ocpp/v16/charge_point_configuration.hpp" #include #include @@ -140,7 +141,7 @@ class DbTestBase : public testing::Test { std::map> connectors; std::shared_ptr database_handler; std::unique_ptr database_interface; - std::unique_ptr configuration; + std::unique_ptr configuration; void add_connectors(unsigned int n) { for (unsigned int i = 0; i <= n; i++) { diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/test_composite_schedule.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/test_composite_schedule.cpp index bbebb37e6c..b55f8dbef0 100644 --- a/lib/everest/ocpp/tests/lib/ocpp/v16/test_composite_schedule.cpp +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/test_composite_schedule.cpp @@ -16,6 +16,7 @@ namespace fs = std::filesystem; #include #include #include +#include #include #include #include @@ -179,7 +180,7 @@ class CompositeScheduleTestFixture : public testing::Test { std::map> connectors; std::shared_ptr database_handler; std::shared_ptr evse_security; - std::unique_ptr configuration; + std::unique_ptr configuration; }; TEST_F(CompositeScheduleTestFixture, CalculateEnhancedCompositeSchedule_ValidatedBaseline) { diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/test_configuration.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/test_configuration.cpp index 3243f00957..7559277fb5 100644 --- a/lib/everest/ocpp/tests/lib/ocpp/v16/test_configuration.cpp +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/test_configuration.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest +#include "ocpp/v16/charge_point_configuration_interface.hpp" #include #include @@ -13,7 +14,7 @@ namespace { using namespace ocpp::v16; struct ConfigurationTester : public testing::Test { - std::unique_ptr config; + std::unique_ptr config; void SetUp() override { std::ifstream ifs(CONFIG_FILE_LOCATION_V16); diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/test_smart_charging_handler.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/test_smart_charging_handler.cpp index 9550f85ace..03156bf721 100644 --- a/lib/everest/ocpp/tests/lib/ocpp/v16/test_smart_charging_handler.cpp +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/test_smart_charging_handler.cpp @@ -7,6 +7,7 @@ namespace fs = std::filesystem; #include #include #include +#include #include #include @@ -257,7 +258,7 @@ class ChargepointTestFixture : public testing::Test { // Default values used within the tests std::map> connectors; std::shared_ptr database_handler; - std::unique_ptr configuration; + std::unique_ptr configuration; const int connector_id = 1; bool ignore_no_transaction = true; diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/test_utils.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/test_utils.cpp new file mode 100644 index 0000000000..f5fc9ad40d --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/test_utils.cpp @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include + +#include +#include + +namespace { +using namespace ocpp::v16::utils; + +// reverse the order of the arguments +inline auto common_split_string(char separator, const std::string& csl) { + return ocpp::split_string(csl, separator); +} + +TEST(CSL, ToCSL) { + auto res = to_csl({}); + EXPECT_TRUE(res.empty()); + + res = to_csl({"One"}); + EXPECT_FALSE(res.empty()); + EXPECT_EQ(res, "One"); + + res = to_csl({"One", "Two"}); + EXPECT_FALSE(res.empty()); + EXPECT_EQ(res, "One,Two"); + + // TODO(james-ctc): should this be detected? + res = to_csl({"One", "Two", "Three,Four"}); + EXPECT_FALSE(res.empty()); + EXPECT_EQ(res, "One,Two,Three,Four"); +} + +TEST(CSL, FromCSL) { + auto res = from_csl(""); + EXPECT_TRUE(res.empty()); + + res = from_csl(","); + EXPECT_TRUE(res.empty()); + + res = from_csl(",One"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 1); + EXPECT_EQ(res[0], "One"); + + res = from_csl(",One,"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 1); + EXPECT_EQ(res[0], "One"); + + res = from_csl(",One,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], "Two"); + + res = from_csl(",One,Two,"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], "Two"); + + res = from_csl("One"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 1); + EXPECT_EQ(res[0], "One"); + + res = from_csl("One,"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 1); + EXPECT_EQ(res[0], "One"); + + res = from_csl("One,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], "Two"); + + res = from_csl("One,Two,"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], "Two"); + + res = from_csl("One,,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], "Two"); + + res = from_csl("One,,,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], "Two"); +} + +TEST(CSL, SplitString) { + auto res = split_string(',', ""); + EXPECT_TRUE(res.empty()); + + res = split_string(',', ","); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], ""); + EXPECT_EQ(res[1], ""); + + res = split_string(',', ",One"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], ""); + EXPECT_EQ(res[1], "One"); + + res = split_string(',', ",One,"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 3); + EXPECT_EQ(res[0], ""); + EXPECT_EQ(res[1], "One"); + EXPECT_EQ(res[2], ""); + + res = split_string(',', ",One,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 3); + EXPECT_EQ(res[0], ""); + EXPECT_EQ(res[1], "One"); + EXPECT_EQ(res[2], "Two"); + + res = split_string(',', ",One,Two,"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 4); + EXPECT_EQ(res[0], ""); + EXPECT_EQ(res[1], "One"); + EXPECT_EQ(res[2], "Two"); + EXPECT_EQ(res[3], ""); + + res = split_string(',', "One"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 1); + EXPECT_EQ(res[0], "One"); + + res = split_string(',', "One,"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], ""); + + res = split_string(',', "One,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], "Two"); + + res = split_string(',', "One,Two,"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 3); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], "Two"); + EXPECT_EQ(res[2], ""); + + res = split_string(',', "One,,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 3); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], ""); + EXPECT_EQ(res[2], "Two"); + + res = split_string(',', "One,,,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 4); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], ""); + EXPECT_EQ(res[2], ""); + EXPECT_EQ(res[3], "Two"); +} + +TEST(CSL, SplitStringCommon) { + // gives different results to v16::split_string + + auto res = common_split_string(',', ""); + EXPECT_TRUE(res.empty()); + + res = common_split_string(',', ","); + EXPECT_FALSE(res.empty()); + // ASSERT_EQ(res.size(), 2); + ASSERT_EQ(res.size(), 1); + EXPECT_EQ(res[0], ""); + + res = common_split_string(',', ",One"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], ""); + EXPECT_EQ(res[1], "One"); + + res = common_split_string(',', ",One,"); + EXPECT_FALSE(res.empty()); + // ASSERT_EQ(res.size(), 3); + // EXPECT_EQ(res[0], ""); + // EXPECT_EQ(res[1], "One"); + // EXPECT_EQ(res[2], ""); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], ""); + EXPECT_EQ(res[1], "One"); + + res = common_split_string(',', ",One,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 3); + EXPECT_EQ(res[0], ""); + EXPECT_EQ(res[1], "One"); + EXPECT_EQ(res[2], "Two"); + + res = common_split_string(',', ",One,Two,"); + EXPECT_FALSE(res.empty()); + // ASSERT_EQ(res.size(), 4); + // EXPECT_EQ(res[0], ""); + // EXPECT_EQ(res[1], "One"); + // EXPECT_EQ(res[2], "Two"); + // EXPECT_EQ(res[3], ""); + ASSERT_EQ(res.size(), 3); + EXPECT_EQ(res[0], ""); + EXPECT_EQ(res[1], "One"); + EXPECT_EQ(res[2], "Two"); + + res = common_split_string(',', "One"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 1); + EXPECT_EQ(res[0], "One"); + + res = common_split_string(',', "One,"); + EXPECT_FALSE(res.empty()); + // ASSERT_EQ(res.size(), 2); + // EXPECT_EQ(res[0], "One"); + // EXPECT_EQ(res[1], ""); + ASSERT_EQ(res.size(), 1); + EXPECT_EQ(res[0], "One"); + + res = common_split_string(',', "One,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], "Two"); + + res = common_split_string(',', "One,Two,"); + EXPECT_FALSE(res.empty()); + // ASSERT_EQ(res.size(), 3); + // EXPECT_EQ(res[0], "One"); + // EXPECT_EQ(res[1], "Two"); + // EXPECT_EQ(res[2], ""); + ASSERT_EQ(res.size(), 2); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], "Two"); + + res = common_split_string(',', "One,,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 3); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], ""); + EXPECT_EQ(res[2], "Two"); + + res = common_split_string(',', "One,,,Two"); + EXPECT_FALSE(res.empty()); + ASSERT_EQ(res.size(), 4); + EXPECT_EQ(res[0], "One"); + EXPECT_EQ(res[1], ""); + EXPECT_EQ(res[2], ""); + EXPECT_EQ(res[3], "Two"); +} + +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/configuration_stub.hpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/configuration_stub.hpp new file mode 100644 index 0000000000..63a8823ba4 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/configuration_stub.hpp @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#pragma once + +#include + +#include +#include + +#include +#include +#include + +#include "memory_storage.hpp" + +namespace ocpp::v16::stubs { + +// create instances for v16 and v2 configuration +class ConfigurationBase : public testing::Test { +protected: + std::unique_ptr v16_config; + std::unique_ptr v2_config; + std::unique_ptr device_model; + + void SetUp() override { + std::ifstream ifs(CONFIG_FILE_LOCATION_V16); + const std::string config_file((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); + v16_config = std::make_unique(config_file, CONFIG_DIR_V16, + USER_CONFIG_FILE_LOCATION_V16); + device_model = std::make_unique(); + std::unique_ptr proxy = std::make_unique(*device_model); + v2_config = std::make_unique(std::move(proxy)); + } + + // void TearDown() override { + // } +}; + +// support parameterised tests so the same test can be run against: +// - the v16 JSON configuration +// - the v2 database interface (via an in-memory implementation) +class Configuration : public ConfigurationBase, public testing::WithParamInterface { +public: + ocpp::v16::ChargePointConfigurationInterface* get() { + ocpp::v16::ChargePointConfigurationInterface* result{nullptr}; + if (GetParam() == "sql") { + result = v2_config.get(); + } else if (GetParam() == "json") { + result = v16_config.get(); + } + return result; + } +}; + +} // namespace ocpp::v16::stubs diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/memory_storage.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/memory_storage.cpp new file mode 100644 index 0000000000..57c9696ab1 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/memory_storage.cpp @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "memory_storage.hpp" + +#include +#include +#include + +#include +#include + +namespace { + +// initial values are from the JSON unit test config files +// Do not add additional values +const std::map required_vars_internal = { + {"CentralSystemURI", "127.0.0.1:8180/steve/websocket/CentralSystemService/"}, + {"ChargeBoxSerialNumber", "cp001"}, + {"ChargePointId", "cp001"}, + {"ChargePointVendor", "Pionix"}, + {"ChargePointModel", "Yeti"}, + {"FirmwareVersion", "0.1"}, + {"SupportedCiphers12", + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384"}, + {"SupportedCiphers13", "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"}, + {"SupportedMeasurands", "Energy.Active.Import.Register,Energy.Active.Export.Register,Power.Active.Import,Voltage," + "Current.Import,Frequency,Current.Offered,Power.Offered,SoC,Temperature"}, + {"TLSKeylogFile", "/tmp/ocpp_tls_keylog.txt"}, + {"WebsocketPingPayload", "hello there"}, + {"AuthorizeConnectorZeroOnConnectorOne", "true"}, + {"LogMessages", "true"}, + {"UseSslDefaultVerifyPaths", "true"}, + {"VerifyCsmsCommonName", "true"}, + {"EnableTLSKeylog", "false"}, + {"LogMessagesRaw", "false"}, + {"LogRotation", "false"}, + {"LogRotationDateSuffix", "false"}, + {"StopTransactionIfUnlockNotSupported", "false"}, + {"UseTPM", "false"}, + {"UseTPMSeccLeafCertificate", "false"}, + {"VerifyCsmsAllowWildcards", "false"}, + {"MaxMessageSize", "65000"}, + {"MaxCompositeScheduleDuration", "31536000"}, + {"OcspRequestInterval", "604800"}, + {"RetryBackoffRandomRange", "10"}, + {"RetryBackoffRepeatTimes", "3"}, + {"RetryBackoffWaitMinimum", "3"}, + {"WaitForStopTransactionsOnResetTimeout", "60"}, + {"WebsocketPongTimeout", "5"}, + {"LogRotationMaximumFileCount", "0"}, + {"LogRotationMaximumFileSize", "0"}, + {"SupportedChargingProfilePurposeTypes", "ChargePointMaxProfile,TxDefaultProfile,TxProfile"}, + {"LogMessagesFormat", ""}, + {"AllowChargingProfileWithoutStartSchedule", "true"}, + {"CompositeScheduleDefaultLimitAmps", "48"}, + {"CompositeScheduleDefaultLimitWatts", "33120"}, + {"CompositeScheduleDefaultNumberPhases", "3"}, + {"SupplyVoltage", "230"}, +}; // namespace + +// initial values are from the JSON unit test config files +// Do not add additional values +const std::map required_vars_core = { + {"NumberOfConnectors", "1"}, + {"SupportedFeatureProfiles", + "Core,FirmwareManagement,RemoteTrigger,Reservation,LocalAuthListManagement,SmartCharging"}, + {"ConnectorPhaseRotation", "0.RST,1.RST"}, + {"MeterValuesAlignedData", "Energy.Active.Import.Register"}, + {"MeterValuesSampledData", "Energy.Active.Import.Register"}, + {"StopTxnAlignedData", "Energy.Active.Import.Register"}, + {"StopTxnSampledData", "Energy.Active.Import.Register"}, + {"AuthorizeRemoteTxRequests", "false"}, + {"LocalAuthorizeOffline", "false"}, + {"LocalPreAuthorize", "false"}, + {"StopTransactionOnInvalidId", "true"}, + {"UnlockConnectorOnEVSideDisconnect", "true"}, + {"ClockAlignedDataInterval", "900"}, + {"ConnectionTimeOut", "10"}, + {"GetConfigurationMaxKeys", "100"}, + {"HeartbeatInterval", "86400"}, + {"MeterValueSampleInterval", "0"}, + {"ResetRetries", "1"}, + {"TransactionMessageAttempts", "1"}, + {"TransactionMessageRetryInterval", "10"}, + {"StopTransactionOnEVSideDisconnect", "true"}, +}; + +// initial values are from the JSON unit test config files +// Do not add additional values +const std::map required_vars_firmware_management = { + {"SupportedFileTransferProtocols", "FTP"}, +}; + +// initial values are from the JSON unit test config files +// Do not add additional values +const std::map required_vars_smart_charging = { + {"ChargeProfileMaxStackLevel", "42"}, + {"ChargingScheduleAllowedChargingRateUnit", "Current"}, + {"ChargingScheduleMaxPeriods", "42"}, + {"MaxChargingProfilesInstalled", "42"}, +}; + +// initial values are from the JSON unit test config files +// Do not add additional values +const std::map required_vars_security = { + {"SecurityProfile", "0"}, + {"DisableSecurityEventNotifications", "false"}, +}; + +// initial values are from the JSON unit test config files +// Do not add additional values +const std::map required_vars_local_auth_list = { + {"LocalAuthListEnabled", "true"}, + {"LocalAuthListMaxLength", "42"}, + {"SendLocalListMaxLength", "42"}, +}; + +// initial values are from the JSON unit test config files +// Do not add additional values +const std::map required_vars_pnc = { + {"ISO15118CertificateManagementEnabled", "true"}, + {"ISO15118PnCEnabled", "true"}, + {"ContractValidationOffline", "true"}, +}; + +// initial values are from the JSON unit test config files +// Do not add additional values +const std::map required_vars_california_pricing = { + {"CustomDisplayCostAndPrice", "false"}, +}; + +// initial values are from the JSON unit test config files +// Do not add additional values +const std::map required_vars_custom = {}; + +std::map vars_internal; +std::map vars_core; +std::map vars_firmware_management; +std::map vars_smart_charging; +std::map vars_security; +std::map vars_local_auth_list; +std::map vars_pnc; +std::map vars_california_pricing; +std::map vars_custom; + +const ocpp::v2::VariableCharacteristics characteristics = {ocpp::v2::DataEnum::string, false, {}, {}, {}, {}, {}, {}}; +const ocpp::v2::VariableMetaData meta_data = {characteristics, {}, {}}; + +void add_to_report(std::vector& report, const std::string_view& name, + const std::map& vars) { + ocpp::v2::Component component{std::string{name}, {}, {}, {}}; + + for (const auto& i : vars) { + ocpp::v2::Variable variable{i.first, {}, {}}; + ocpp::v2::ReportData data; + data.component = component; + data.variable = variable; + report.push_back(std::move(data)); + } +} + +void generate_report(std::vector& report) { + report.clear(); + add_to_report(report, "Internal", vars_internal); + add_to_report(report, "Core", vars_core); + add_to_report(report, "FirmwareManagement", vars_firmware_management); + add_to_report(report, "SmartCharging", vars_smart_charging); + add_to_report(report, "Security", vars_security); + add_to_report(report, "LocalAuthListManagement", vars_local_auth_list); + add_to_report(report, "PnC", vars_pnc); + add_to_report(report, "CostAndPrice", vars_california_pricing); + add_to_report(report, "Custom", vars_custom); +} + +} // namespace + +namespace ocpp::v16::stubs { + +MemoryStorage::MemoryStorage() { + vars_internal = required_vars_internal; + vars_core = required_vars_core; + vars_firmware_management = required_vars_firmware_management; + vars_smart_charging = required_vars_smart_charging; + vars_security = required_vars_security; + vars_local_auth_list = required_vars_local_auth_list; + vars_pnc = required_vars_pnc; + vars_california_pricing = required_vars_california_pricing; + vars_custom = required_vars_custom; + + read_only.clear(); +} + +void MemoryStorage::set_readonly(const std::string& key) { + read_only.insert(key); +} + +void MemoryStorage::set(const std::string_view& component, const std::string_view& variable, + const std::string_view& value) { + Component component_id; + Variable variable_id; + + component_id.name = std::string{component}; + variable_id.name = std::string{variable}; + (void)set_value(component_id, variable_id, AttributeEnum::Actual, std::string{value}, "TestCase"); +} + +std::string MemoryStorage::get(const std::string_view& component, const std::string_view& variable) { + Component component_id; + Variable variable_id; + std::string result; + + component_id.name = std::string{component}; + variable_id.name = std::string{variable}; + (void)get_variable(component_id, variable_id, AttributeEnum::Actual, result, false); + return result; +} + +void MemoryStorage::clear(const std::string_view& component, const std::string_view& variable) { + const std::string var{variable}; + + if (component == "Internal") { + vars_internal.erase(var); + } else if (component == "Core") { + vars_core.erase(var); + } else if (component == "FirmwareManagement") { + vars_firmware_management.erase(var); + } else if (component == "SmartCharging") { + vars_smart_charging.erase(var); + } else if (component == "Security") { + vars_security.erase(var); + } else if (component == "LocalAuthListManagement") { + vars_local_auth_list.erase(var); + } else if (component == "PnC") { + vars_pnc.erase(var); + } else if (component == "CostAndPrice") { + vars_california_pricing.erase(var); + } else if (component == "Custom") { + vars_custom.erase(var); + } else { + std::cerr << "clear not implemented for: " << component << '\n'; + } +} + +MemoryStorage::GetVariableStatusEnum MemoryStorage::get_variable(const Component& component_id, + const Variable& variable_id, + const AttributeEnum& attribute_enum, + std::string& value, bool allow_write_only) const { + auto result = GetVariableStatusEnum::UnknownVariable; + if (component_id.name == "Internal") { + if (const auto it = vars_internal.find(variable_id.name); it != vars_internal.end()) { + MemoryStorage::VariableAttribute va; + // std::cout << "Internal[" << it->first << "]==" << it->second << '\n'; + value = it->second; + result = GetVariableStatusEnum::Accepted; + } + } else if (component_id.name == "Core") { + if (const auto it = vars_core.find(variable_id.name); it != vars_core.end()) { + MemoryStorage::VariableAttribute va; + // std::cout << "Core[" << it->first << "]==" << it->second << '\n'; + value = it->second; + result = GetVariableStatusEnum::Accepted; + } + } else if (component_id.name == "FirmwareManagement") { + if (const auto it = vars_firmware_management.find(variable_id.name); it != vars_firmware_management.end()) { + MemoryStorage::VariableAttribute va; + // std::cout << "FirmwareManagement[" << it->first << "]==" << it->second << '\n'; + value = it->second; + result = GetVariableStatusEnum::Accepted; + } + } else if (component_id.name == "SmartCharging") { + if (const auto it = vars_smart_charging.find(variable_id.name); it != vars_smart_charging.end()) { + MemoryStorage::VariableAttribute va; + // std::cout << "SmartCharging[" << it->first << "]==" << it->second << '\n'; + value = it->second; + result = GetVariableStatusEnum::Accepted; + } + } else if (component_id.name == "Security") { + if (const auto it = vars_security.find(variable_id.name); it != vars_security.end()) { + MemoryStorage::VariableAttribute va; + // std::cout << "Security[" << it->first << "]==" << it->second << '\n'; + value = it->second; + result = GetVariableStatusEnum::Accepted; + } + } else if (component_id.name == "LocalAuthListManagement") { + if (const auto it = vars_local_auth_list.find(variable_id.name); it != vars_local_auth_list.end()) { + MemoryStorage::VariableAttribute va; + // std::cout << "LocalAuthListManagement[" << it->first << "]==" << it->second << '\n'; + value = it->second; + result = GetVariableStatusEnum::Accepted; + } + } else if (component_id.name == "Pnc") { + if (const auto it = vars_pnc.find(variable_id.name); it != vars_pnc.end()) { + MemoryStorage::VariableAttribute va; + // std::cout << "Pnc[" << it->first << "]==" << it->second << '\n'; + value = it->second; + result = GetVariableStatusEnum::Accepted; + } + } else if (component_id.name == "CostAndPrice") { + if (const auto it = vars_california_pricing.find(variable_id.name); it != vars_california_pricing.end()) { + MemoryStorage::VariableAttribute va; + // std::cout << "CostAndPrice[" << it->first << "]==" << it->second << '\n'; + value = it->second; + result = GetVariableStatusEnum::Accepted; + } + } else if (component_id.name == "Custom") { + if (const auto it = vars_custom.find(variable_id.name); it != vars_custom.end()) { + MemoryStorage::VariableAttribute va; + // std::cout << "Custom[" << it->first << "]==" << it->second << '\n'; + value = it->second; + result = GetVariableStatusEnum::Accepted; + } + } else { + result = GetVariableStatusEnum::Rejected; + std::cerr << "get_variable_attribute not implemented for: " << component_id.name << '\n'; + } + + // std::cout << component_id.name << '[' << variable_id.name << "] has value: " << result.has_value() << '\n'; + return result; +} + +MemoryStorage::SetVariableStatusEnum MemoryStorage::set_value(const Component& component_id, + const Variable& variable_id, + const AttributeEnum& attribute_enum, + const std::string& value, const std::string& source, + bool allow_read_only) { + auto result = SetVariableStatusEnum::Accepted; + if (component_id.name == "Internal") { + // std::cout << "Internal[" << variable_id.name << "]=" << value << '\n'; + vars_internal[variable_id.name] = value; + result = SetVariableStatusEnum::Accepted; + } else if (component_id.name == "Core") { + // std::cout << "Core[" << variable_id.name << "]=" << value << '\n'; + vars_core[variable_id.name] = value; + result = SetVariableStatusEnum::Accepted; + } else if (component_id.name == "FirmwareManagement") { + // std::cout << "FirmwareManagement[" << variable_id.name << "]=" << value << '\n'; + vars_firmware_management[variable_id.name] = value; + result = SetVariableStatusEnum::Accepted; + } else if (component_id.name == "SmartCharging") { + // std::cout << "SmartCharging[" << variable_id.name << "]=" << value << '\n'; + vars_smart_charging[variable_id.name] = value; + result = SetVariableStatusEnum::Accepted; + } else if (component_id.name == "Security") { + // std::cout << "Security[" << variable_id.name << "]=" << value << '\n'; + vars_security[variable_id.name] = value; + result = SetVariableStatusEnum::Accepted; + } else if (component_id.name == "LocalAuthListManagement") { + // std::cout << "LocalAuthListManagement[" << variable_id.name << "]=" << value << '\n'; + vars_local_auth_list[variable_id.name] = value; + result = SetVariableStatusEnum::Accepted; + } else if (component_id.name == "PnC") { + // std::cout << "PnC[" << variable_id.name << "]=" << value << '\n'; + vars_pnc[variable_id.name] = value; + result = SetVariableStatusEnum::Accepted; + } else if (component_id.name == "CostAndPrice") { + // std::cout << "CostAndPrice[" << variable_id.name << "]=" << value << '\n'; + vars_california_pricing[variable_id.name] = value; + result = SetVariableStatusEnum::Accepted; + } else if (component_id.name == "Custom") { + // std::cout << "Custom[" << variable_id.name << "]=" << value << '\n'; + vars_custom[variable_id.name] = value; + result = SetVariableStatusEnum::Accepted; + } else { + result = SetVariableStatusEnum::Rejected; + std::cerr << "set_variable_attribute not implemented for: " << component_id.name << '\n'; + } + + return result; +} + +MemoryStorage::SetVariableStatusEnum MemoryStorage::set_read_only_value(const Component& component_id, + const Variable& variable_id, + const AttributeEnum& attribute_enum, + const std::string& value, + const std::string& source) { + return set_value(component_id, variable_id, attribute_enum, value, source); +} + +std::optional MemoryStorage::get_mutability(const Component& component_id, + const Variable& variable_id, + const AttributeEnum& attribute_enum) { + std::optional result; + const auto key_str = std::string{variable_id.name}; + std::string value; + if (get_variable(component_id, variable_id, attribute_enum, value) == v2::GetVariableStatusEnum::Accepted) { + const auto sv_key_opt = keys::convert(key_str); + if (sv_key_opt) { + const auto sv_key = sv_key_opt.value(); + if (sv_key == keys::valid_keys::AuthorizationKey) { + result = MemoryStorage::MutabilityEnum::WriteOnly; + } else { + result = (keys::is_readonly(sv_key)) ? MemoryStorage::MutabilityEnum::ReadOnly + : MemoryStorage::MutabilityEnum::ReadWrite; + } + } else { + if (const auto it = read_only.find(key_str); it == read_only.end()) { + result = MemoryStorage::MutabilityEnum::ReadWrite; + } else { + result = MemoryStorage::MutabilityEnum::ReadOnly; + } + } + } + return result; +} + +std::optional MemoryStorage::get_variable_meta_data(const Component& component_id, + const Variable& variable_id) { + std::optional result; + std::string value; + if (get_variable(component_id, variable_id, AttributeEnum::Actual, value) == GetVariableStatusEnum::Accepted) { + MemoryStorage::VariableMetaData md; + md.characteristics.dataType = v2::DataEnum::string; + md.characteristics.supportsMonitoring = false; + result = std::move(md); + } + return result; +} + +std::vector MemoryStorage::get_base_report_data(const ReportBaseEnum& report_base) { + if (report_base == v2::ReportBaseEnum::ConfigurationInventory) { + std::vector result; + generate_report(result); + return result; + } + return {}; +} + +std::vector +MemoryStorage::get_custom_report_data(const std::optional>& component_variables, + const std::optional>& component_criteria) { + return {}; +} + +std::vector +MemoryStorage::set_monitors(const std::vector& requests, const VariableMonitorType type) { + return {}; +} + +bool MemoryStorage::update_monitor_reference(std::int32_t monitor_id, const std::string& reference_value) { + return false; +} + +std::vector MemoryStorage::get_periodic_monitors() { + return {}; +} + +std::vector +MemoryStorage::get_monitors(const std::vector& criteria, + const std::vector& component_variables) { + return {}; +} + +std::vector MemoryStorage::clear_monitors(const std::vector& request_ids, + bool allow_protected) { + return {}; +} + +std::int32_t MemoryStorage::clear_custom_monitors() { + return -1; +} + +void MemoryStorage::register_variable_listener( + std::function& monitors, + const Component& component, const Variable& variable, + const VariableCharacteristics& characteristics, const VariableAttribute& attribute, + const std::string& value_previous, const std::string& value_current)>&& listener) { +} + +void MemoryStorage::register_monitor_listener( + std::function&& listener) { +} + +void MemoryStorage::check_integrity(const std::map& evse_connector_structure) { +} + +} // namespace ocpp::v16::stubs diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/memory_storage.hpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/memory_storage.hpp new file mode 100644 index 0000000000..cb14d75912 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/memory_storage.hpp @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +// v2 storage provider that uses memory rather than a database + +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace ocpp::v16::stubs { + +class MemoryStorage : public ocpp::v2::DeviceModelInterface { +private: + std::set read_only; + +public: + using VariableAttribute = ocpp::v2::VariableAttribute; + using Component = ocpp::v2::Component; + using ComponentVariable = ocpp::v2::ComponentVariable; + using ComponentCriterionEnum = ocpp::v2::ComponentCriterionEnum; + using Variable = ocpp::v2::Variable; + using AttributeEnum = ocpp::v2::AttributeEnum; + using GetVariableStatusEnum = ocpp::v2::GetVariableStatusEnum; + using SetVariableStatusEnum = ocpp::v2::SetVariableStatusEnum; + using VariableMonitoringMeta = ocpp::v2::VariableMonitoringMeta; + using SetMonitoringData = ocpp::v2::SetMonitoringData; + using SetMonitoringResult = ocpp::v2::SetMonitoringResult; + using VariableMonitorType = ocpp::v2::VariableMonitorType; + using VariableMonitoringPeriodic = ocpp::v2::VariableMonitoringPeriodic; + using MonitoringCriterionEnum = ocpp::v2::MonitoringCriterionEnum; + using MonitoringData = ocpp::v2::MonitoringData; + using ClearMonitoringStatusEnum = ocpp::v2::ClearMonitoringStatusEnum; + using ClearMonitoringResult = ocpp::v2::ClearMonitoringResult; + using MutabilityEnum = ocpp::v2::MutabilityEnum; + using VariableCharacteristics = ocpp::v2::VariableCharacteristics; + using VariableMetaData = ocpp::v2::VariableMetaData; + using ReportData = ocpp::v2::ReportData; + using ReportBaseEnum = ocpp::v2::ReportBaseEnum; + + MemoryStorage(); + + void set_readonly(const std::string& key); + void set(const std::string_view& component, const std::string_view& variable, const std::string_view& value); + std::string get(const std::string_view& component, const std::string_view& variable); + void clear(const std::string_view& component, const std::string_view& variable); + + GetVariableStatusEnum get_variable(const Component& component_id, const Variable& variable_id, + const AttributeEnum& attribute_enum, std::string& value, + bool allow_write_only = false) const override; + + SetVariableStatusEnum set_value(const Component& component_id, const Variable& variable_id, + const AttributeEnum& attribute_enum, const std::string& value, + const std::string& source, bool allow_read_only = false) override; + + SetVariableStatusEnum set_read_only_value(const Component& component_id, const Variable& variable_id, + const AttributeEnum& attribute_enum, const std::string& value, + const std::string& source) override; + + std::optional get_mutability(const Component& component_id, const Variable& variable_id, + const AttributeEnum& attribute_enum) override; + + std::optional get_variable_meta_data(const Component& component_id, + const Variable& variable_id) override; + + std::vector get_base_report_data(const ReportBaseEnum& report_base) override; + + std::vector get_custom_report_data( + const std::optional>& component_variables = std::nullopt, + const std::optional>& component_criteria = std::nullopt) override; + + std::vector + set_monitors(const std::vector& requests, + const VariableMonitorType type = VariableMonitorType::CustomMonitor) override; + + bool update_monitor_reference(std::int32_t monitor_id, const std::string& reference_value) override; + + std::vector get_periodic_monitors() override; + + std::vector get_monitors(const std::vector& criteria, + const std::vector& component_variables) override; + + std::vector clear_monitors(const std::vector& request_ids, + bool allow_protected = false) override; + + std::int32_t clear_custom_monitors() override; + + void register_variable_listener( + std::function& monitors, + const Component& component, const Variable& variable, + const VariableCharacteristics& characteristics, const VariableAttribute& attribute, + const std::string& value_previous, const std::string& value_current)>&& listener) override; + + void register_monitor_listener( + std::function&& listener) override; + + void check_integrity(const std::map& evse_connector_structure) override; +}; + +class MemoryStorageProxy : public ocpp::v2::DeviceModelInterface { +private: + MemoryStorage& storage; + +public: + using VariableAttribute = ocpp::v2::VariableAttribute; + using Component = ocpp::v2::Component; + using ComponentVariable = ocpp::v2::ComponentVariable; + using ComponentCriterionEnum = ocpp::v2::ComponentCriterionEnum; + using Variable = ocpp::v2::Variable; + using AttributeEnum = ocpp::v2::AttributeEnum; + using GetVariableStatusEnum = ocpp::v2::GetVariableStatusEnum; + using SetVariableStatusEnum = ocpp::v2::SetVariableStatusEnum; + using VariableMonitoringMeta = ocpp::v2::VariableMonitoringMeta; + using SetMonitoringData = ocpp::v2::SetMonitoringData; + using SetMonitoringResult = ocpp::v2::SetMonitoringResult; + using VariableMonitorType = ocpp::v2::VariableMonitorType; + using VariableMonitoringPeriodic = ocpp::v2::VariableMonitoringPeriodic; + using MonitoringCriterionEnum = ocpp::v2::MonitoringCriterionEnum; + using MonitoringData = ocpp::v2::MonitoringData; + using ClearMonitoringStatusEnum = ocpp::v2::ClearMonitoringStatusEnum; + using ClearMonitoringResult = ocpp::v2::ClearMonitoringResult; + using MutabilityEnum = ocpp::v2::MutabilityEnum; + using VariableCharacteristics = ocpp::v2::VariableCharacteristics; + using VariableMetaData = ocpp::v2::VariableMetaData; + using ReportData = ocpp::v2::ReportData; + using ReportBaseEnum = ocpp::v2::ReportBaseEnum; + + MemoryStorageProxy(MemoryStorage& obj) : storage(obj) { + } + + GetVariableStatusEnum get_variable(const Component& component_id, const Variable& variable_id, + const AttributeEnum& attribute_enum, std::string& value, + bool allow_write_only = false) const { + return storage.get_variable(component_id, variable_id, attribute_enum, value); + } + + SetVariableStatusEnum set_value(const Component& component_id, const Variable& variable_id, + const AttributeEnum& attribute_enum, const std::string& value, + const std::string& source, bool allow_read_only = false) { + return storage.set_value(component_id, variable_id, attribute_enum, value, source); + } + + SetVariableStatusEnum set_read_only_value(const Component& component_id, const Variable& variable_id, + const AttributeEnum& attribute_enum, const std::string& value, + const std::string& source) { + return storage.set_read_only_value(component_id, variable_id, attribute_enum, value, source); + } + + std::optional get_mutability(const Component& component_id, const Variable& variable_id, + const AttributeEnum& attribute_enum) { + return storage.get_mutability(component_id, variable_id, attribute_enum); + } + + std::optional get_variable_meta_data(const Component& component_id, const Variable& variable_id) { + return storage.get_variable_meta_data(component_id, variable_id); + } + + std::vector get_base_report_data(const ReportBaseEnum& report_base) { + return storage.get_base_report_data(report_base); + } + + std::vector get_custom_report_data( + const std::optional>& component_variables = std::nullopt, + const std::optional>& component_criteria = std::nullopt) { + return storage.get_custom_report_data(component_variables, component_criteria); + } + + std::vector set_monitors(const std::vector& requests, + const VariableMonitorType type = VariableMonitorType::CustomMonitor) { + return storage.set_monitors(requests); + } + + bool update_monitor_reference(std::int32_t monitor_id, const std::string& reference_value) { + return storage.update_monitor_reference(monitor_id, reference_value); + } + + std::vector get_periodic_monitors() { + return storage.get_periodic_monitors(); + } + + std::vector get_monitors(const std::vector& criteria, + const std::vector& component_variables) { + return storage.get_monitors(criteria, component_variables); + } + + std::vector clear_monitors(const std::vector& request_ids, + bool allow_protected = false) { + return storage.clear_monitors(request_ids); + } + + std::int32_t clear_custom_monitors() { + return storage.clear_custom_monitors(); + } + + void register_variable_listener( + std::function& monitors, + const Component& component, const Variable& variable, + const VariableCharacteristics& characteristics, const VariableAttribute& attribute, + const std::string& value_previous, const std::string& value_current)>&& listener) { + return storage.register_variable_listener(std::move(listener)); + } + + void register_monitor_listener( + std::function&& listener) { + return storage.register_monitor_listener(std::move(listener)); + } + + void check_integrity(const std::map& evse_connector_structure) { + return storage.check_integrity(evse_connector_structure); + } +}; + +} // namespace ocpp::v16::stubs diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_california_pricing.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_california_pricing.cpp new file mode 100644 index 0000000000..632daf14a8 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_california_pricing.cpp @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include +#include + +#include "configuration_stub.hpp" + +/* + "DefaultPrice": + { + "priceText": "This is the price", + "priceTextOffline": "Show this price text when offline!", + "chargingPrice": + { + "kWhPrice": 3.14, + "hourPrice": 0.42 + } + }, + "DefaultPriceText": + { + "priceTexts": + [ + { + "priceText": "This is the price", + "priceTextOffline": "Show this price text when offline!", + "language": "en" + }, + { + "priceText": "Dit is de prijs", + "priceTextOffline": "Laat dit zien wanneer de charging station offline is!", + "language": "nl" + }, + { + "priceText": "Dette er prisen", + "priceTextOffline": "Vis denne pristeksten når du er frakoblet", + "language": "nb_NO" + } + ] + }, +*/ + +namespace { +using namespace ocpp::v16::stubs; + +TEST_P(Configuration, CustomDisplayCostAndPriceEnabled) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getCustomDisplayCostAndPriceEnabled()); + auto kv = get()->getCustomDisplayCostAndPriceEnabledKeyValue(); + EXPECT_EQ(kv.key, "CustomDisplayCostAndPrice"); + EXPECT_EQ(kv.value, "false"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, DefaultTariffMessage) { + ASSERT_NE(get(), nullptr); + auto msg = get()->getDefaultTariffMessage(false); + EXPECT_FALSE(msg.ocpp_transaction_id.has_value()); + EXPECT_FALSE(msg.identifier_id.has_value()); + EXPECT_FALSE(msg.identifier_type.has_value()); + EXPECT_TRUE(msg.message.empty()); + + msg = get()->getDefaultTariffMessage(true); + EXPECT_FALSE(msg.ocpp_transaction_id.has_value()); + EXPECT_FALSE(msg.identifier_id.has_value()); + EXPECT_FALSE(msg.identifier_type.has_value()); + EXPECT_TRUE(msg.message.empty()); +} + +TEST_P(Configuration, DefaultPrice) { + using ConfigurationStatus = ocpp::v16::ConfigurationStatus; + + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getDefaultPrice().has_value()); + auto kv = get()->getDefaultPriceKeyValue(); + ASSERT_FALSE(kv); + + auto status = get()->setDefaultPrice("{}"); + EXPECT_EQ(status, ConfigurationStatus::Accepted); + EXPECT_EQ(get()->getDefaultPrice(), "{}"); + kv = get()->getDefaultPriceKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "DefaultPrice"); + EXPECT_EQ(kv.value().value, "{}"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, DefaultPriceText) { + using ConfigurationStatus = ocpp::v16::ConfigurationStatus; + + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getDefaultPriceText("en").has_value()); + auto kv = get()->getDefaultPriceTextKeyValue("en"); + EXPECT_EQ(kv.key, "DefaultPriceText,en"); + EXPECT_EQ(kv.value, ""); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, DisplayTimeOffset) { + using ConfigurationStatus = ocpp::v16::ConfigurationStatus; + + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getDisplayTimeOffset().has_value()); + auto kv = get()->getDisplayTimeOffsetKeyValue(); + ASSERT_FALSE(kv); + + auto status = get()->setDisplayTimeOffset("1:30"); + EXPECT_EQ(status, ConfigurationStatus::Accepted); + EXPECT_EQ(get()->getDisplayTimeOffset(), "1:30"); + kv = get()->getDisplayTimeOffsetKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "TimeOffset"); + EXPECT_EQ(kv.value().value, "1:30"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, Language) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getLanguage().has_value()); + auto kv = get()->getLanguageKeyValue(); + ASSERT_FALSE(kv); + + get()->setLanguage("de"); + EXPECT_EQ(get()->getLanguage(), "de"); + kv = get()->getLanguageKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "Language"); + EXPECT_EQ(kv.value().value, "de"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_P(Configuration, MultiLanguageSupportedLanguages) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getMultiLanguageSupportedLanguages().has_value()); + auto kv = get()->getMultiLanguageSupportedLanguagesKeyValue(); + ASSERT_FALSE(kv); +} + +TEST_P(Configuration, NextTimeOffsetTransitionDateTime) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getNextTimeOffsetTransitionDateTime().has_value()); + auto kv = get()->getNextTimeOffsetTransitionDateTimeKeyValue(); + ASSERT_FALSE(kv); + + get()->setNextTimeOffsetTransitionDateTime("2200-01-01T12:00:00"); + EXPECT_EQ(get()->getNextTimeOffsetTransitionDateTime(), "2200-01-01T12:00:00"); + kv = get()->getNextTimeOffsetTransitionDateTimeKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "NextTimeOffsetTransitionDateTime"); + EXPECT_EQ(kv.value().value, "2200-01-01T12:00:00"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, TimeOffsetNextTransition) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getTimeOffsetNextTransition().has_value()); + auto kv = get()->getTimeOffsetNextTransitionKeyValue(); + ASSERT_FALSE(kv); + + get()->setTimeOffsetNextTransition("-01:15"); + EXPECT_EQ(get()->getTimeOffsetNextTransition(), "-01:15"); + kv = get()->getTimeOffsetNextTransitionKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "TimeOffsetNextTransition"); + EXPECT_EQ(kv.value().value, "-01:15"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, CustomIdleFeeAfterStop) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getCustomIdleFeeAfterStop().has_value()); + auto kv = get()->getCustomIdleFeeAfterStopKeyValue(); + ASSERT_FALSE(kv); + + get()->setCustomIdleFeeAfterStop(false); + EXPECT_TRUE(get()->getCustomIdleFeeAfterStop().has_value()); + EXPECT_FALSE(get()->getCustomIdleFeeAfterStop().value()); + kv = get()->getCustomIdleFeeAfterStopKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "CustomIdleFeeAfterStop"); + EXPECT_EQ(kv.value().value, "false"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, CustomMultiLanguageMessagesEnabled) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getCustomMultiLanguageMessagesEnabled().has_value()); + auto kv = get()->getCustomMultiLanguageMessagesEnabledKeyValue(); + ASSERT_FALSE(kv); +} + +TEST_P(Configuration, WaitForSetUserPriceTimeout) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getWaitForSetUserPriceTimeout().has_value()); + auto kv = get()->getWaitForSetUserPriceTimeoutKeyValue(); + ASSERT_FALSE(kv); + + get()->setWaitForSetUserPriceTimeout(3602); + EXPECT_FALSE(get()->getWaitForSetUserPriceTimeout().has_value()); + kv = get()->getWaitForSetUserPriceTimeoutKeyValue(); + ASSERT_FALSE(kv); +} + +TEST_P(Configuration, PriceNumberOfDecimalsForCostValues) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getPriceNumberOfDecimalsForCostValues().has_value()); + auto kv = get()->getPriceNumberOfDecimalsForCostValuesKeyValue(); + ASSERT_FALSE(kv); +} + +// ----------------------------------------------------------------------------- +// Oneoff tests where there are differences between implementations +// Note: TEST_F and not TEST_P + +TEST_F(Configuration, GetMultiLanguageSupportedLanguagesV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("CostAndPrice", "SupportedLanguages", "en,de"); + + EXPECT_TRUE(v2_config->getMultiLanguageSupportedLanguages().has_value()); + EXPECT_EQ(v2_config->getMultiLanguageSupportedLanguages().value(), "en,de"); + auto kv = v2_config->getMultiLanguageSupportedLanguagesKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SupportedLanguages"); + EXPECT_EQ(kv.value().value, "en,de"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, GetCustomMultiLanguageMessagesEnabledV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("CostAndPrice", "CustomMultiLanguageMessages", "false"); + + EXPECT_TRUE(v2_config->getCustomMultiLanguageMessagesEnabled().has_value()); + EXPECT_FALSE(v2_config->getCustomMultiLanguageMessagesEnabled().value()); + auto kv = v2_config->getCustomMultiLanguageMessagesEnabledKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CustomMultiLanguageMessages"); + EXPECT_EQ(kv.value().value, "false"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, SetWaitForSetUserPriceTimeoutV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("CostAndPrice", "WaitForSetUserPriceTimeout", ""); + + EXPECT_FALSE(v2_config->getWaitForSetUserPriceTimeout().has_value()); + auto kv = v2_config->getWaitForSetUserPriceTimeoutKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "WaitForSetUserPriceTimeout"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setWaitForSetUserPriceTimeout(3001); + EXPECT_TRUE(v2_config->getWaitForSetUserPriceTimeout().has_value()); + EXPECT_EQ(v2_config->getWaitForSetUserPriceTimeout().value(), 3001); + kv = v2_config->getWaitForSetUserPriceTimeoutKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "WaitForSetUserPriceTimeout"); + EXPECT_EQ(kv.value().value, "3001"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, GetPriceNumberOfDecimalsForCostValuesV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("CostAndPrice", "NumberOfDecimalsForCostValues", "3"); + + EXPECT_TRUE(v2_config->getPriceNumberOfDecimalsForCostValues().has_value()); + EXPECT_EQ(v2_config->getPriceNumberOfDecimalsForCostValues().value(), 3); + auto kv = v2_config->getPriceNumberOfDecimalsForCostValuesKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "NumberOfDecimalsForCostValues"); + EXPECT_EQ(kv.value().value, "3"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, SetDefaultPriceTextV2) { + using ConfigurationStatus = ocpp::v16::ConfigurationStatus; + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("CostAndPrice", "NumberOfDecimalsForCostValues", "3"); + + EXPECT_FALSE(v2_config->getDefaultPriceText("en").has_value()); + auto kv = v2_config->getDefaultPriceTextKeyValue("en"); + EXPECT_EQ(kv.key, "DefaultPriceText,en"); + EXPECT_EQ(kv.value, ""); + EXPECT_FALSE(kv.readonly); + + // needs config item for multi language support + // getMultiLanguageSupportedLanguages() + device_model->set("CostAndPrice", "SupportedLanguages", "en,de"); + + const char* value_en = R"({ + "priceText": "This is the price", + "priceTextOffline": "Show this price text when offline!" +})"; + const char* value_de = R"({ + "priceText": "Das ist der Preis", + "priceTextOffline": "Diesen Preistext anzeigen, wenn Sie offline sind!" +})"; + + auto status = v2_config->setDefaultPriceText("DefaultPriceText,en", value_en); + EXPECT_EQ(status, ConfigurationStatus::Accepted); + EXPECT_EQ(v2_config->getDefaultPriceText("en"), value_en); + kv = v2_config->getDefaultPriceTextKeyValue("en"); + EXPECT_EQ(kv.key, "DefaultPriceText,en"); + ASSERT_TRUE(kv.value.has_value()); + EXPECT_EQ(kv.value.value(), value_en); + EXPECT_FALSE(kv.readonly); + + status = v2_config->setDefaultPriceText("DefaultPriceText,de", value_de); + EXPECT_EQ(status, ConfigurationStatus::Accepted); + EXPECT_EQ(v2_config->getDefaultPriceText("de"), value_de); + kv = v2_config->getDefaultPriceTextKeyValue("de"); + EXPECT_EQ(kv.key, "DefaultPriceText,de"); + ASSERT_TRUE(kv.value.has_value()); + EXPECT_EQ(kv.value.value(), value_de); + EXPECT_FALSE(kv.readonly); + + auto list = v2_config->getAllDefaultPriceTextKeyValues(); + ASSERT_TRUE(list); + ASSERT_EQ(list.value().size(), 2); + const auto& lv = list.value(); + + int en_index = 0; + int de_index = 1; + if (lv[0].key == "DefaultPriceText,de") { + en_index = 1; + de_index = 0; + } + + EXPECT_EQ(lv[en_index].key, "DefaultPriceText,en"); + ASSERT_TRUE(lv[en_index].value.has_value()); + EXPECT_EQ(lv[en_index].value.value(), value_en); + EXPECT_FALSE(lv[en_index].readonly); + + EXPECT_EQ(lv[de_index].key, "DefaultPriceText,de"); + ASSERT_TRUE(lv[de_index].value.has_value()); + EXPECT_EQ(lv[de_index].value.value(), value_de); + EXPECT_FALSE(lv[de_index].readonly); +} +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_config.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_config.cpp new file mode 100644 index 0000000000..ae46f28538 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_config.cpp @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include + +#include "configuration_stub.hpp" +#include +#include + +namespace { +using namespace ocpp::v16::stubs; + +// run tests against V16 JSON and V2 database +// gtest_filter: Config/Configuration.* +INSTANTIATE_TEST_SUITE_P(Config, Configuration, testing::Values("sql", "json")); + +TEST(ConnectorID, Extract) { + using CPCB = ocpp::v16::ChargePointConfigurationBase; + + EXPECT_EQ(CPCB::extract_connector_id(""), std::nullopt); + EXPECT_EQ(CPCB::extract_connector_id("1234"), std::nullopt); + EXPECT_EQ(CPCB::extract_connector_id("[1234"), std::nullopt); + EXPECT_EQ(CPCB::extract_connector_id("1234]"), std::nullopt); + EXPECT_EQ(CPCB::extract_connector_id("[1]"), std::nullopt); + EXPECT_EQ(CPCB::extract_connector_id("A[]"), std::nullopt); + EXPECT_EQ(CPCB::extract_connector_id("A[1.3]"), std::nullopt); + EXPECT_EQ(CPCB::extract_connector_id("A[1]"), 1); + EXPECT_EQ(CPCB::extract_connector_id("A[12]"), 12); + EXPECT_EQ(CPCB::extract_connector_id("A[123]"), 123); +} + +TEST(ConnectorID, Build) { + using CPCB = ocpp::v16::ChargePointConfigurationBase; + + EXPECT_EQ(CPCB::MeterPublicKey_string(0), "MeterPublicKey[0]"); + EXPECT_EQ(CPCB::MeterPublicKey_string(1), "MeterPublicKey[1]"); + EXPECT_EQ(CPCB::MeterPublicKey_string(12), "MeterPublicKey[12]"); + EXPECT_EQ(CPCB::MeterPublicKey_string(123), "MeterPublicKey[123]"); +} + +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_core.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_core.cpp new file mode 100644 index 0000000000..cb79464796 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_core.cpp @@ -0,0 +1,788 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include +#include + +#include "configuration_stub.hpp" +#include "ocpp/v16/types.hpp" + +namespace { +using namespace ocpp::v16::stubs; + +TEST_P(Configuration, ConnectorPhaseRotation) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getConnectorPhaseRotation(), "0.RST,1.RST"); + auto kv = get()->getConnectorPhaseRotationKeyValue(); + EXPECT_EQ(kv.key, "ConnectorPhaseRotation"); + EXPECT_EQ(kv.value, "0.RST,1.RST"); + EXPECT_FALSE(kv.readonly); + + get()->setConnectorPhaseRotation("0.TRS,1.TRS"); + EXPECT_EQ(get()->getConnectorPhaseRotation(), "0.TRS,1.TRS"); + kv = get()->getConnectorPhaseRotationKeyValue(); + EXPECT_EQ(kv.key, "ConnectorPhaseRotation"); + EXPECT_EQ(kv.value, "0.TRS,1.TRS"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, MeterValuesAlignedData) { + using Measurand = ocpp::v16::Measurand; + using Phase = ocpp::v16::Phase; + + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getMeterValuesAlignedData(), "Energy.Active.Import.Register"); + auto kv = get()->getMeterValuesAlignedDataKeyValue(); + EXPECT_EQ(kv.key, "MeterValuesAlignedData"); + EXPECT_EQ(kv.value, "Energy.Active.Import.Register"); + EXPECT_FALSE(kv.readonly); + + auto vec = get()->getMeterValuesAlignedDataVector(); + ASSERT_EQ(vec.size(), 4); + EXPECT_EQ(vec[0].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[0].phase, std::nullopt); + EXPECT_EQ(vec[1].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[1].phase, Phase::L1); + EXPECT_EQ(vec[2].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[2].phase, Phase::L2); + EXPECT_EQ(vec[3].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[3].phase, Phase::L3); + + get()->setMeterValuesAlignedData("Current.Import"); + EXPECT_EQ(get()->getMeterValuesAlignedData(), "Current.Import"); + kv = get()->getMeterValuesAlignedDataKeyValue(); + EXPECT_EQ(kv.key, "MeterValuesAlignedData"); + EXPECT_EQ(kv.value, "Current.Import"); + EXPECT_FALSE(kv.readonly); + + vec = get()->getMeterValuesAlignedDataVector(); + ASSERT_EQ(vec.size(), 5); + EXPECT_EQ(vec[0].measurand, Measurand::Current_Import); + EXPECT_EQ(vec[0].phase, std::nullopt); + EXPECT_EQ(vec[1].measurand, Measurand::Current_Import); + EXPECT_EQ(vec[1].phase, Phase::L1); + EXPECT_EQ(vec[2].measurand, Measurand::Current_Import); + EXPECT_EQ(vec[2].phase, Phase::L2); + EXPECT_EQ(vec[3].measurand, Measurand::Current_Import); + EXPECT_EQ(vec[3].phase, Phase::L3); + EXPECT_EQ(vec[4].measurand, Measurand::Current_Import); + EXPECT_EQ(vec[4].phase, Phase::N); +} + +TEST_P(Configuration, MeterValuesSampledData) { + using Measurand = ocpp::v16::Measurand; + using Phase = ocpp::v16::Phase; + + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getMeterValuesSampledData(), "Energy.Active.Import.Register"); + auto kv = get()->getMeterValuesSampledDataKeyValue(); + EXPECT_EQ(kv.key, "MeterValuesSampledData"); + EXPECT_EQ(kv.value, "Energy.Active.Import.Register"); + EXPECT_FALSE(kv.readonly); + + auto vec = get()->getMeterValuesSampledDataVector(); + ASSERT_EQ(vec.size(), 4); + EXPECT_EQ(vec[0].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[0].phase, std::nullopt); + EXPECT_EQ(vec[1].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[1].phase, Phase::L1); + EXPECT_EQ(vec[2].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[2].phase, Phase::L2); + EXPECT_EQ(vec[3].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[3].phase, Phase::L3); + + get()->setMeterValuesSampledData("Energy.Active.Import.Register,Energy.Active.Export.Register"); + EXPECT_EQ(get()->getMeterValuesSampledData(), "Energy.Active.Import.Register,Energy.Active.Export.Register"); + kv = get()->getMeterValuesSampledDataKeyValue(); + EXPECT_EQ(kv.key, "MeterValuesSampledData"); + EXPECT_EQ(kv.value, "Energy.Active.Import.Register,Energy.Active.Export.Register"); + EXPECT_FALSE(kv.readonly); + + vec = get()->getMeterValuesSampledDataVector(); + ASSERT_EQ(vec.size(), 8); + EXPECT_EQ(vec[0].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[0].phase, std::nullopt); + EXPECT_EQ(vec[1].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[1].phase, Phase::L1); + EXPECT_EQ(vec[2].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[2].phase, Phase::L2); + EXPECT_EQ(vec[3].measurand, Measurand::Energy_Active_Import_Register); + EXPECT_EQ(vec[3].phase, Phase::L3); + EXPECT_EQ(vec[4].measurand, Measurand::Energy_Active_Export_Register); + EXPECT_EQ(vec[4].phase, std::nullopt); + EXPECT_EQ(vec[5].measurand, Measurand::Energy_Active_Export_Register); + EXPECT_EQ(vec[5].phase, Phase::L1); + EXPECT_EQ(vec[6].measurand, Measurand::Energy_Active_Export_Register); + EXPECT_EQ(vec[6].phase, Phase::L2); + EXPECT_EQ(vec[7].measurand, Measurand::Energy_Active_Export_Register); + EXPECT_EQ(vec[7].phase, Phase::L3); +} + +TEST_P(Configuration, StopTxnAlignedData) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getStopTxnAlignedData(), "Energy.Active.Import.Register"); + auto kv = get()->getStopTxnAlignedDataKeyValue(); + EXPECT_EQ(kv.key, "StopTxnAlignedData"); + EXPECT_EQ(kv.value, "Energy.Active.Import.Register"); + EXPECT_FALSE(kv.readonly); + + get()->setStopTxnAlignedData("Voltage"); + EXPECT_EQ(get()->getStopTxnAlignedData(), "Voltage"); + kv = get()->getStopTxnAlignedDataKeyValue(); + EXPECT_EQ(kv.key, "StopTxnAlignedData"); + EXPECT_EQ(kv.value, "Voltage"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, StopTxnSampledData) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getStopTxnSampledData(), "Energy.Active.Import.Register"); + auto kv = get()->getStopTxnSampledDataKeyValue(); + EXPECT_EQ(kv.key, "StopTxnSampledData"); + EXPECT_EQ(kv.value, "Energy.Active.Import.Register"); + EXPECT_FALSE(kv.readonly); + + get()->setStopTxnSampledData("Frequency,Current.Offered"); + EXPECT_EQ(get()->getStopTxnSampledData(), "Frequency,Current.Offered"); + kv = get()->getStopTxnSampledDataKeyValue(); + EXPECT_EQ(kv.key, "StopTxnSampledData"); + EXPECT_EQ(kv.value, "Frequency,Current.Offered"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, SupportedFeatureProfiles) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getSupportedFeatureProfiles(), + "Core,FirmwareManagement,RemoteTrigger,Reservation,LocalAuthListManagement,SmartCharging"); + auto kv = get()->getSupportedFeatureProfilesKeyValue(); + EXPECT_EQ(kv.key, "SupportedFeatureProfiles"); + EXPECT_EQ(kv.value, "Core,FirmwareManagement,RemoteTrigger,Reservation,LocalAuthListManagement,SmartCharging"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, AuthorizeRemoteTxRequests) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getAuthorizeRemoteTxRequests(), false); + auto kv = get()->getAuthorizeRemoteTxRequestsKeyValue(); + EXPECT_EQ(kv.key, "AuthorizeRemoteTxRequests"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); + + get()->setAuthorizeRemoteTxRequests(true); + EXPECT_EQ(get()->getAuthorizeRemoteTxRequests(), true); + kv = get()->getAuthorizeRemoteTxRequestsKeyValue(); + EXPECT_EQ(kv.key, "AuthorizeRemoteTxRequests"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, LocalAuthorizeOffline) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getLocalAuthorizeOffline(), false); + auto kv = get()->getLocalAuthorizeOfflineKeyValue(); + EXPECT_EQ(kv.key, "LocalAuthorizeOffline"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); + + get()->setLocalAuthorizeOffline(true); + EXPECT_EQ(get()->getLocalAuthorizeOffline(), true); + kv = get()->getLocalAuthorizeOfflineKeyValue(); + EXPECT_EQ(kv.key, "LocalAuthorizeOffline"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, LocalPreAuthorize) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getLocalPreAuthorize(), false); + auto kv = get()->getLocalPreAuthorizeKeyValue(); + EXPECT_EQ(kv.key, "LocalPreAuthorize"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); + + get()->setLocalPreAuthorize(true); + EXPECT_EQ(get()->getLocalPreAuthorize(), true); + kv = get()->getLocalPreAuthorizeKeyValue(); + EXPECT_EQ(kv.key, "LocalPreAuthorize"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, StopTransactionOnInvalidId) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getStopTransactionOnInvalidId(), true); + auto kv = get()->getStopTransactionOnInvalidIdKeyValue(); + EXPECT_EQ(kv.key, "StopTransactionOnInvalidId"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); + + get()->setStopTransactionOnInvalidId(false); + EXPECT_EQ(get()->getStopTransactionOnInvalidId(), false); + kv = get()->getStopTransactionOnInvalidIdKeyValue(); + EXPECT_EQ(kv.key, "StopTransactionOnInvalidId"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, UnlockConnectorOnEVSideDisconnect) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getUnlockConnectorOnEVSideDisconnect(), true); + auto kv = get()->getUnlockConnectorOnEVSideDisconnectKeyValue(); + EXPECT_EQ(kv.key, "UnlockConnectorOnEVSideDisconnect"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); + + get()->setUnlockConnectorOnEVSideDisconnect(false); + EXPECT_EQ(get()->getUnlockConnectorOnEVSideDisconnect(), false); + kv = get()->getUnlockConnectorOnEVSideDisconnectKeyValue(); + EXPECT_EQ(kv.key, "UnlockConnectorOnEVSideDisconnect"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, ClockAlignedDataInterval) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getClockAlignedDataInterval(), 900); + auto kv = get()->getClockAlignedDataIntervalKeyValue(); + EXPECT_EQ(kv.key, "ClockAlignedDataInterval"); + EXPECT_EQ(kv.value, "900"); + EXPECT_FALSE(kv.readonly); + + get()->setClockAlignedDataInterval(5200); + EXPECT_EQ(get()->getClockAlignedDataInterval(), 5200); + kv = get()->getClockAlignedDataIntervalKeyValue(); + EXPECT_EQ(kv.key, "ClockAlignedDataInterval"); + EXPECT_EQ(kv.value, "5200"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, ConnectionTimeOut) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getConnectionTimeOut(), 10); + auto kv = get()->getConnectionTimeOutKeyValue(); + EXPECT_EQ(kv.key, "ConnectionTimeOut"); + EXPECT_EQ(kv.value, "10"); + EXPECT_FALSE(kv.readonly); + + get()->setConnectionTimeOut(60); + EXPECT_EQ(get()->getConnectionTimeOut(), 60); + kv = get()->getConnectionTimeOutKeyValue(); + EXPECT_EQ(kv.key, "ConnectionTimeOut"); + EXPECT_EQ(kv.value, "60"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, GetConfigurationMaxKeys) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getGetConfigurationMaxKeys(), 100); + auto kv = get()->getGetConfigurationMaxKeysKeyValue(); + EXPECT_EQ(kv.key, "GetConfigurationMaxKeys"); + EXPECT_EQ(kv.value, "100"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, HeartbeatInterval) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getHeartbeatInterval(), 86400); + auto kv = get()->getHeartbeatIntervalKeyValue(); + EXPECT_EQ(kv.key, "HeartbeatInterval"); + EXPECT_EQ(kv.value, "86400"); + EXPECT_FALSE(kv.readonly); + + get()->setHeartbeatInterval(70); + EXPECT_EQ(get()->getHeartbeatInterval(), 70); + kv = get()->getHeartbeatIntervalKeyValue(); + EXPECT_EQ(kv.key, "HeartbeatInterval"); + EXPECT_EQ(kv.value, "70"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, MeterValueSampleInterval) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getMeterValueSampleInterval(), 0); + auto kv = get()->getMeterValueSampleIntervalKeyValue(); + EXPECT_EQ(kv.key, "MeterValueSampleInterval"); + EXPECT_EQ(kv.value, "0"); + EXPECT_FALSE(kv.readonly); + + get()->setMeterValueSampleInterval(125); + EXPECT_EQ(get()->getMeterValueSampleInterval(), 125); + kv = get()->getMeterValueSampleIntervalKeyValue(); + EXPECT_EQ(kv.key, "MeterValueSampleInterval"); + EXPECT_EQ(kv.value, "125"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, NumberOfConnectors) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getNumberOfConnectors(), 1); + auto kv = get()->getNumberOfConnectorsKeyValue(); + EXPECT_EQ(kv.key, "NumberOfConnectors"); + EXPECT_EQ(kv.value, "1"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, ResetRetries) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getResetRetries(), 1); + auto kv = get()->getResetRetriesKeyValue(); + EXPECT_EQ(kv.key, "ResetRetries"); + EXPECT_EQ(kv.value, "1"); + EXPECT_FALSE(kv.readonly); + + get()->setResetRetries(7); + EXPECT_EQ(get()->getResetRetries(), 7); + kv = get()->getResetRetriesKeyValue(); + EXPECT_EQ(kv.key, "ResetRetries"); + EXPECT_EQ(kv.value, "7"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, TransactionMessageAttempts) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getTransactionMessageAttempts(), 1); + auto kv = get()->getTransactionMessageAttemptsKeyValue(); + EXPECT_EQ(kv.key, "TransactionMessageAttempts"); + EXPECT_EQ(kv.value, "1"); + EXPECT_FALSE(kv.readonly); + + get()->setTransactionMessageAttempts(4); + EXPECT_EQ(get()->getTransactionMessageAttempts(), 4); + kv = get()->getTransactionMessageAttemptsKeyValue(); + EXPECT_EQ(kv.key, "TransactionMessageAttempts"); + EXPECT_EQ(kv.value, "4"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, TransactionMessageRetryInterval) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getTransactionMessageRetryInterval(), 10); + auto kv = get()->getTransactionMessageRetryIntervalKeyValue(); + EXPECT_EQ(kv.key, "TransactionMessageRetryInterval"); + EXPECT_EQ(kv.value, "10"); + EXPECT_FALSE(kv.readonly); + + get()->setTransactionMessageRetryInterval(1250); + EXPECT_EQ(get()->getTransactionMessageRetryInterval(), 1250); + kv = get()->getTransactionMessageRetryIntervalKeyValue(); + EXPECT_EQ(kv.key, "TransactionMessageRetryInterval"); + EXPECT_EQ(kv.value, "1250"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, AllowOfflineTxForUnknownId) { + ASSERT_NE(get(), nullptr); + // No initial value set - hence set() doesn't work + + EXPECT_FALSE(get()->getAllowOfflineTxForUnknownId().has_value()); + auto kv = get()->getAllowOfflineTxForUnknownIdKeyValue(); + ASSERT_FALSE(kv.has_value()); + + // set only works when there is a value configured + get()->setAllowOfflineTxForUnknownId(true); + EXPECT_FALSE(get()->getAllowOfflineTxForUnknownId().has_value()); + kv = get()->getAllowOfflineTxForUnknownIdKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, AuthorizationCacheEnabled) { + ASSERT_NE(get(), nullptr); + // No initial value set - hence set() doesn't work + + EXPECT_FALSE(get()->getAuthorizationCacheEnabled().has_value()); + auto kv = get()->getAuthorizationCacheEnabledKeyValue(); + ASSERT_FALSE(kv.has_value()); + + // set only works when there is a value configured + get()->setAuthorizationCacheEnabled(true); + EXPECT_FALSE(get()->getAuthorizationCacheEnabled().has_value()); + kv = get()->getAuthorizationCacheEnabledKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, ReserveConnectorZeroSupported) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getReserveConnectorZeroSupported().has_value()); + auto kv = get()->getReserveConnectorZeroSupportedKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, StopTransactionOnEVSideDisconnect) { + ASSERT_NE(get(), nullptr); + + EXPECT_TRUE(get()->getStopTransactionOnEVSideDisconnect().has_value()); + EXPECT_TRUE(get()->getStopTransactionOnEVSideDisconnect().value()); + auto kv = get()->getStopTransactionOnEVSideDisconnectKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "StopTransactionOnEVSideDisconnect"); + EXPECT_EQ(kv.value().value, "true"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_P(Configuration, ConnectorPhaseRotationMaxLength) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getConnectorPhaseRotationMaxLength(), std::nullopt); + auto kv = get()->getConnectorPhaseRotationMaxLengthKeyValue(); + EXPECT_EQ(kv, std::nullopt); +} + +TEST_P(Configuration, LightIntensity) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getLightIntensity(), std::nullopt); + auto kv = get()->getLightIntensityKeyValue(); + EXPECT_EQ(kv, std::nullopt); + + // set only works when there is a value configured + get()->setLightIntensity(777); + EXPECT_EQ(get()->getLightIntensity(), std::nullopt); + kv = get()->getLightIntensityKeyValue(); + EXPECT_EQ(kv, std::nullopt); +} + +TEST_P(Configuration, MaxEnergyOnInvalidId) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getMaxEnergyOnInvalidId(), std::nullopt); + auto kv = get()->getMaxEnergyOnInvalidIdKeyValue(); + EXPECT_EQ(kv, std::nullopt); + + // set only works when there is a value configured + get()->setMaxEnergyOnInvalidId(770); + EXPECT_EQ(get()->getMaxEnergyOnInvalidId(), std::nullopt); + kv = get()->getMaxEnergyOnInvalidIdKeyValue(); + EXPECT_EQ(kv, std::nullopt); +} + +TEST_P(Configuration, MeterValuesAlignedDataMaxLength) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getMeterValuesAlignedDataMaxLength(), std::nullopt); + auto kv = get()->getMeterValuesAlignedDataMaxLengthKeyValue(); + EXPECT_EQ(kv, std::nullopt); +} + +TEST_P(Configuration, MeterValuesSampledDataMaxLength) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getMeterValuesSampledDataMaxLength(), std::nullopt); + auto kv = get()->getMeterValuesSampledDataMaxLengthKeyValue(); + EXPECT_EQ(kv, std::nullopt); +} + +TEST_P(Configuration, MinimumStatusDuration) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getMinimumStatusDuration(), std::nullopt); + auto kv = get()->getMinimumStatusDurationKeyValue(); + EXPECT_EQ(kv, std::nullopt); + + // set only works when there is a value configured + get()->setMinimumStatusDuration(760); + EXPECT_EQ(get()->getMinimumStatusDuration(), std::nullopt); + kv = get()->getMinimumStatusDurationKeyValue(); + EXPECT_EQ(kv, std::nullopt); +} + +TEST_P(Configuration, StopTxnAlignedDataMaxLength) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getStopTxnAlignedDataMaxLength(), std::nullopt); + auto kv = get()->getStopTxnAlignedDataMaxLengthKeyValue(); + EXPECT_EQ(kv, std::nullopt); +} + +TEST_P(Configuration, StopTxnSampledDataMaxLength) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getStopTxnSampledDataMaxLength(), std::nullopt); + auto kv = get()->getStopTxnSampledDataMaxLengthKeyValue(); + EXPECT_EQ(kv, std::nullopt); +} + +TEST_P(Configuration, SupportedFeatureProfilesMaxLength) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getSupportedFeatureProfilesMaxLength(), std::nullopt); + auto kv = get()->getSupportedFeatureProfilesMaxLengthKeyValue(); + EXPECT_EQ(kv, std::nullopt); +} + +TEST_P(Configuration, WebsocketPingInterval) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getWebsocketPingInterval(), std::nullopt); + auto kv = get()->getWebsocketPingIntervalKeyValue(); + EXPECT_EQ(kv, std::nullopt); + + // set only works when there is a value configured + get()->setWebsocketPingInterval(707); + EXPECT_EQ(get()->getWebsocketPingInterval(), std::nullopt); + kv = get()->getWebsocketPingIntervalKeyValue(); + EXPECT_EQ(kv, std::nullopt); +} + +TEST_P(Configuration, SupportedFeatureProfilesSet) { + using SupportedFeatureProfiles = ocpp::v16::SupportedFeatureProfiles; + + ASSERT_NE(get(), nullptr); + const auto set = get()->getSupportedFeatureProfilesSet(); + + const std::set expected = {SupportedFeatureProfiles::Internal, + SupportedFeatureProfiles::Core, + SupportedFeatureProfiles::CostAndPrice, + SupportedFeatureProfiles::FirmwareManagement, + SupportedFeatureProfiles::LocalAuthListManagement, + SupportedFeatureProfiles::Reservation, + SupportedFeatureProfiles::SmartCharging, + SupportedFeatureProfiles::RemoteTrigger, + SupportedFeatureProfiles::Security, + SupportedFeatureProfiles::PnC}; + + EXPECT_EQ(set.size(), expected.size()); + EXPECT_EQ(set, expected); +} + +// ----------------------------------------------------------------------------- +// Oneoff tests where there are differences between implementations +// Note: TEST_F and not TEST_P + +TEST_F(Configuration, SetAllowOfflineTxForUnknownIdV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "AllowOfflineTxForUnknownId", ""); + + EXPECT_FALSE(v2_config->getAllowOfflineTxForUnknownId().has_value()); + auto kv = v2_config->getAllowOfflineTxForUnknownIdKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "AllowOfflineTxForUnknownId"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setAllowOfflineTxForUnknownId(true); + EXPECT_TRUE(v2_config->getAllowOfflineTxForUnknownId().has_value()); + EXPECT_EQ(v2_config->getAllowOfflineTxForUnknownId().value(), true); + kv = v2_config->getAllowOfflineTxForUnknownIdKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "AllowOfflineTxForUnknownId"); + EXPECT_EQ(kv.value().value, "true"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetAuthorizationCacheEnabledV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "AuthorizationCacheEnabled", ""); + + EXPECT_FALSE(v2_config->getAuthorizationCacheEnabled().has_value()); + auto kv = v2_config->getAuthorizationCacheEnabledKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "AuthorizationCacheEnabled"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setAuthorizationCacheEnabled(true); + EXPECT_TRUE(v2_config->getAuthorizationCacheEnabled().has_value()); + EXPECT_EQ(v2_config->getAuthorizationCacheEnabled().value(), true); + kv = v2_config->getAuthorizationCacheEnabledKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "AuthorizationCacheEnabled"); + EXPECT_EQ(kv.value().value, "true"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetBlinkRepeatV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "BlinkRepeat", ""); + + EXPECT_EQ(v2_config->getBlinkRepeat(), std::nullopt); + auto kv = v2_config->getBlinkRepeatKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "BlinkRepeat"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setBlinkRepeat(31); + EXPECT_EQ(v2_config->getBlinkRepeat(), 31); + kv = v2_config->getBlinkRepeatKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "BlinkRepeat"); + EXPECT_EQ(kv.value().value, "31"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetConnectorPhaseRotationMaxLengthV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "ConnectorPhaseRotationMaxLength", "200"); + + EXPECT_EQ(v2_config->getConnectorPhaseRotationMaxLength(), 200); + auto kv = v2_config->getConnectorPhaseRotationMaxLengthKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ConnectorPhaseRotationMaxLength"); + EXPECT_EQ(kv.value().value, "200"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, SetLightIntensityV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "LightIntensity", ""); + + EXPECT_EQ(v2_config->getLightIntensity(), std::nullopt); + auto kv = v2_config->getLightIntensityKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "LightIntensity"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setLightIntensity(776); + EXPECT_EQ(v2_config->getLightIntensity(), 776); + kv = v2_config->getLightIntensityKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "LightIntensity"); + EXPECT_EQ(kv.value().value, "776"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetMaxEnergyOnInvalidIdV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "MaxEnergyOnInvalidId", ""); + + EXPECT_EQ(v2_config->getMaxEnergyOnInvalidId(), std::nullopt); + auto kv = v2_config->getMaxEnergyOnInvalidIdKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "MaxEnergyOnInvalidId"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setMaxEnergyOnInvalidId(770); + EXPECT_EQ(v2_config->getMaxEnergyOnInvalidId(), 770); + kv = v2_config->getMaxEnergyOnInvalidIdKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "MaxEnergyOnInvalidId"); + EXPECT_EQ(kv.value().value, "770"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetMeterValuesAlignedDataMaxLengthV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "MeterValuesAlignedDataMaxLength", "199"); + + EXPECT_EQ(v2_config->getMeterValuesAlignedDataMaxLength(), 199); + auto kv = v2_config->getMeterValuesAlignedDataMaxLengthKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "MeterValuesAlignedDataMaxLength"); + EXPECT_EQ(kv.value().value, "199"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, SetMeterValuesSampledDataMaxLengthV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "MeterValuesSampledDataMaxLength", "198"); + + EXPECT_EQ(v2_config->getMeterValuesSampledDataMaxLength(), 198); + auto kv = v2_config->getMeterValuesSampledDataMaxLengthKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "MeterValuesSampledDataMaxLength"); + EXPECT_EQ(kv.value().value, "198"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, SetMinimumStatusDurationV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "MinimumStatusDuration", ""); + + EXPECT_EQ(v2_config->getMinimumStatusDuration(), std::nullopt); + auto kv = v2_config->getMinimumStatusDurationKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "MinimumStatusDuration"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setMinimumStatusDuration(760); + EXPECT_EQ(v2_config->getMinimumStatusDuration(), 760); + kv = v2_config->getMinimumStatusDurationKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "MinimumStatusDuration"); + EXPECT_EQ(kv.value().value, "760"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetStopTxnAlignedDataMaxLengthV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "StopTxnAlignedDataMaxLength", "83"); + + EXPECT_EQ(v2_config->getStopTxnAlignedDataMaxLength(), 83); + auto kv = v2_config->getStopTxnAlignedDataMaxLengthKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "StopTxnAlignedDataMaxLength"); + EXPECT_EQ(kv.value().value, "83"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, SetStopTxnSampledDataMaxLengthV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "StopTxnSampledDataMaxLength", "84"); + + EXPECT_EQ(v2_config->getStopTxnSampledDataMaxLength(), 84); + auto kv = v2_config->getStopTxnSampledDataMaxLengthKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "StopTxnSampledDataMaxLength"); + EXPECT_EQ(kv.value().value, "84"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, SetSupportedFeatureProfilesMaxLengthV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "SupportedFeatureProfilesMaxLength", "85"); + + EXPECT_EQ(v2_config->getSupportedFeatureProfilesMaxLength(), 85); + auto kv = v2_config->getSupportedFeatureProfilesMaxLengthKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "SupportedFeatureProfilesMaxLength"); + EXPECT_EQ(kv.value().value, "85"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, SetWebsocketPingIntervalV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Core", "WebSocketPingInterval", ""); + + EXPECT_EQ(v2_config->getWebsocketPingInterval(), std::nullopt); + auto kv = v2_config->getWebsocketPingIntervalKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "WebsocketPingInterval"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setWebsocketPingInterval(707); + EXPECT_EQ(v2_config->getWebsocketPingInterval(), 707); + kv = v2_config->getWebsocketPingIntervalKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "WebsocketPingInterval"); + EXPECT_EQ(kv.value().value, "707"); + EXPECT_FALSE(kv.value().readonly); +} + +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_custom.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_custom.cpp new file mode 100644 index 0000000000..8381000908 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_custom.cpp @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include +#include + +#include "configuration_stub.hpp" + +namespace { +using namespace ocpp::v16::stubs; + +// expected values extracted from the JSON configuration files +// also see memory_storage.cpp +const std::map expected_key_value = { + {"AllowChargingProfileWithoutStartSchedule", "true"}, + {"AuthorizeConnectorZeroOnConnectorOne", "true"}, + {"CentralSystemURI", "127.0.0.1:8180/steve/websocket/CentralSystemService/"}, + {"ChargeBoxSerialNumber", "cp001"}, + {"ChargePointId", "cp001"}, + {"ChargePointModel", "Yeti"}, + {"ChargePointVendor", "Pionix"}, + {"CompositeScheduleDefaultLimitAmps", "48"}, + {"CompositeScheduleDefaultLimitWatts", "33120"}, + {"CompositeScheduleDefaultNumberPhases", "3"}, + {"FirmwareVersion", "0.1"}, + {"LogMessages", "true"}, + {"LogMessagesFormat", ""}, + {"LogMessagesRaw", "false"}, + {"MaxCompositeScheduleDuration", "31536000"}, + {"MaxMessageSize", "65000"}, + {"OcspRequestInterval", "604800"}, + {"RetryBackoffRandomRange", "10"}, + {"RetryBackoffRepeatTimes", "3"}, + {"RetryBackoffWaitMinimum", "3"}, + {"StopTransactionIfUnlockNotSupported", "false"}, + {"SupplyVoltage", "230"}, + {"SupportedChargingProfilePurposeTypes", "ChargePointMaxProfile,TxDefaultProfile,TxProfile"}, + {"SupportedCiphers12", + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384"}, + {"SupportedCiphers13", "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"}, + {"SupportedMeasurands", "Energy.Active.Import.Register,Energy.Active.Export.Register,Power.Active.Import,Voltage," + "Current.Import,Frequency,Current.Offered,Power.Offered,SoC,Temperature"}, + {"UseSslDefaultVerifyPaths", "true"}, + {"VerifyCsmsAllowWildcards", "false"}, + {"VerifyCsmsCommonName", "true"}, + {"WaitForStopTransactionsOnResetTimeout", "60"}, + {"WebsocketPingPayload", "hello there"}, + {"WebsocketPongTimeout", "5"}, + {"AuthorizeRemoteTxRequests", "false"}, + {"ClockAlignedDataInterval", "900"}, + {"ConnectionTimeOut", "10"}, + {"ConnectorPhaseRotation", "0.RST,1.RST"}, + {"GetConfigurationMaxKeys", "100"}, + {"HeartbeatInterval", "86400"}, + {"LocalAuthorizeOffline", "false"}, + {"LocalPreAuthorize", "false"}, + {"MeterValueSampleInterval", "0"}, + {"MeterValuesAlignedData", "Energy.Active.Import.Register"}, + {"MeterValuesSampledData", "Energy.Active.Import.Register"}, + {"NumberOfConnectors", "1"}, + {"ResetRetries", "1"}, + {"StopTransactionOnEVSideDisconnect", "true"}, + {"StopTransactionOnInvalidId", "true"}, + {"StopTxnAlignedData", "Energy.Active.Import.Register"}, + {"StopTxnSampledData", "Energy.Active.Import.Register"}, + {"SupportedFeatureProfiles", + "Core,FirmwareManagement,RemoteTrigger,Reservation,LocalAuthListManagement,SmartCharging"}, + {"TransactionMessageAttempts", "1"}, + {"TransactionMessageRetryInterval", "10"}, + {"UnlockConnectorOnEVSideDisconnect", "true"}, + {"CustomDisplayCostAndPrice", "false"}, + {"SupportedFileTransferProtocols", "FTP"}, + {"LocalAuthListEnabled", "true"}, + {"LocalAuthListMaxLength", "42"}, + {"SendLocalListMaxLength", "42"}, + {"ChargeProfileMaxStackLevel", "42"}, + {"ChargingScheduleAllowedChargingRateUnit", "Current"}, + {"ChargingScheduleMaxPeriods", "42"}, + {"MaxChargingProfilesInstalled", "42"}, + {"DisableSecurityEventNotifications", "false"}, + {"SecurityProfile", "0"}, + {"ContractValidationOffline", "true"}, + {"ISO15118CertificateManagementEnabled", "true"}, + {"ISO15118PnCEnabled", "true"}, +}; + +TEST_P(Configuration, CustomKey) { + using ConfigurationStatus = ocpp::v16::ConfigurationStatus; + ASSERT_NE(get(), nullptr); + + const std::string key{"GTCustom"}; + const std::string value{"GTCustomValue"}; + + EXPECT_FALSE(get()->getCustomKeyValue(key).has_value()); + EXPECT_EQ(get()->setCustomKey(key, value, false), ConfigurationStatus::Rejected); + + // no point in testing setCustomKey(key, value, true) + // since force==true still requires that the key exists + // in only allows read-only keys to be changed +} + +TEST_P(Configuration, Get) { + using ConfigurationStatus = ocpp::v16::ConfigurationStatus; + ASSERT_NE(get(), nullptr); + + // non-existent key + EXPECT_FALSE(get()->get("DoesNotExist").has_value()); + + // read-only key + auto kv = get()->get("ChargePointModel"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ChargePointModel"); + EXPECT_EQ(kv.value().value, "Yeti"); + EXPECT_TRUE(kv.value().readonly); + + // read-write key + + kv = get()->get("ClockAlignedDataInterval"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ClockAlignedDataInterval"); + EXPECT_EQ(kv.value().value, "900"); + EXPECT_FALSE(kv.value().readonly); + + // hidden key + + // check key exists and has a value + EXPECT_EQ(get()->getTLSKeylogFile(), "/tmp/ocpp_tls_keylog.txt"); + + // check it is not available via this call + kv = get()->get("TLSKeylogFile"); + ASSERT_FALSE(kv); + + // custom key (none defined) +} + +TEST_P(Configuration, Set) { + using ConfigurationStatus = ocpp::v16::ConfigurationStatus; + ASSERT_NE(get(), nullptr); + + // non-existent key + EXPECT_FALSE(get()->get("DoesNotExist").has_value()); + EXPECT_EQ(get()->set("DoesNotExist", "ToThisValue"), std::nullopt); + + // read-only key + auto kv = get()->get("ChargePointModel"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ChargePointModel"); + EXPECT_EQ(kv.value().value, "Yeti"); + EXPECT_TRUE(kv.value().readonly); + + EXPECT_EQ(get()->set("ChargePointModel", "ToThisValue"), std::nullopt); + kv = get()->get("ChargePointModel"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ChargePointModel"); + EXPECT_EQ(kv.value().value, "Yeti"); + EXPECT_TRUE(kv.value().readonly); + + // some other examples + EXPECT_EQ(get()->set("ChargePointSerialNumber", ""), std::nullopt); + EXPECT_EQ(get()->set("ICCID", ""), std::nullopt); + EXPECT_EQ(get()->set("ConnectorPhaseRotationMaxLength", ""), std::nullopt); + EXPECT_EQ(get()->set("NumberOfConnectors", ""), std::nullopt); + EXPECT_EQ(get()->set("MeterType", ""), std::nullopt); + EXPECT_EQ(get()->set("UseSslDefaultVerifyPaths", ""), std::nullopt); + EXPECT_EQ(get()->set("CertificateStoreMaxLength", ""), std::nullopt); + + // read-write key + kv = get()->get("ClockAlignedDataInterval"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ClockAlignedDataInterval"); + EXPECT_EQ(kv.value().value, "900"); + EXPECT_FALSE(kv.value().readonly); + + EXPECT_EQ(get()->set("ClockAlignedDataInterval", "1201"), ConfigurationStatus::Accepted); + kv = get()->get("ClockAlignedDataInterval"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ClockAlignedDataInterval"); + EXPECT_EQ(kv.value().value, "1201"); + EXPECT_FALSE(kv.value().readonly); + + // hidden key - these are read-only + + // check key exists and has a value + EXPECT_EQ(get()->getTLSKeylogFile(), "/tmp/ocpp_tls_keylog.txt"); + EXPECT_EQ(get()->set("TLSKeylogFile", "1201"), std::nullopt); + EXPECT_EQ(get()->getTLSKeylogFile(), "/tmp/ocpp_tls_keylog.txt"); + + // custom key (none defined) +} + +TEST_P(Configuration, GetAllKeyValue) { + ASSERT_NE(get(), nullptr); + + const auto values = get()->get_all_key_value(); + EXPECT_EQ(values.size(), expected_key_value.size()); + + std::map not_found; + std::map missing = expected_key_value; + + for (const auto& i : values) { + if (const auto& search = expected_key_value.find(i.key); search == expected_key_value.end()) { + not_found.insert({i.key, i.value.value_or("")}); + } else { + missing.erase(i.key); + std::string actual{i.value.value_or("")}; + SCOPED_TRACE("Name: " + std::string{i.key}); + EXPECT_EQ(search->second, actual); + } + } + + EXPECT_TRUE(not_found.empty()); + if (!not_found.empty()) { + std::cout << "Not found:\n"; + for (const auto& i : not_found) { + std::cout << "{\"" << i.first << "\",\"" << i.second << "\"},\n"; + } + } + + EXPECT_TRUE(missing.empty()); + if (!missing.empty()) { + std::cout << "Missing:\n"; + for (const auto& i : missing) { + std::cout << "{\"" << i.first << "\",\"" << i.second << "\"},\n"; + } + } +} + +// ----------------------------------------------------------------------------- +// Oneoff tests where there are differences between implementations +// Note: TEST_F and not TEST_P + +TEST_F(Configuration, setCustomKeyV2) { + using ConfigurationStatus = ocpp::v16::ConfigurationStatus; + ASSERT_TRUE(device_model); + + const std::string key{"GTCustom"}; + const std::string value{"GTCustomValue"}; + + // set an initial value + device_model->set("Custom", key, ""); + + EXPECT_TRUE(v2_config->getCustomKeyValue(key).has_value()); + auto kv = v2_config->getCustomKeyValue(key); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, key.c_str()); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + EXPECT_EQ(v2_config->setCustomKey(key, value, false), ConfigurationStatus::Accepted); + kv = v2_config->getCustomKeyValue(key); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, key.c_str()); + EXPECT_EQ(kv.value().value, value.c_str()); + EXPECT_FALSE(kv.value().readonly); + + // TODO: test that force=true allows update of a read-only key + // and force=false rejects update. +} + +TEST_F(Configuration, SetV2) { + using ConfigurationStatus = ocpp::v16::ConfigurationStatus; + ASSERT_TRUE(device_model); + + // set an initial custom key value + device_model->set("Custom", "ACustomKey", ""); + device_model->set("Custom", "ACustomRWKey", ""); + device_model->set_readonly("ACustomKey"); + + // non-existent key + EXPECT_FALSE(v2_config->get("DoesNotExist").has_value()); + EXPECT_EQ(v2_config->set("DoesNotExist", "ToThisValue"), std::nullopt); + + // read-only key + auto kv = v2_config->get("ChargePointModel"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ChargePointModel"); + EXPECT_EQ(kv.value().value, "Yeti"); + EXPECT_TRUE(kv.value().readonly); + + EXPECT_EQ(v2_config->set("ChargePointModel", "ToThisValue"), std::nullopt); + kv = v2_config->get("ChargePointModel"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ChargePointModel"); + EXPECT_EQ(kv.value().value, "Yeti"); + EXPECT_TRUE(kv.value().readonly); + + // read-write key + kv = v2_config->get("ClockAlignedDataInterval"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ClockAlignedDataInterval"); + EXPECT_EQ(kv.value().value, "900"); + EXPECT_FALSE(kv.value().readonly); + + EXPECT_EQ(v2_config->set("ClockAlignedDataInterval", "1201"), ConfigurationStatus::Accepted); + kv = v2_config->get("ClockAlignedDataInterval"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ClockAlignedDataInterval"); + EXPECT_EQ(kv.value().value, "1201"); + EXPECT_FALSE(kv.value().readonly); + + // hidden key - these are read-only + + // check key exists and has a value + EXPECT_EQ(v2_config->getTLSKeylogFile(), "/tmp/ocpp_tls_keylog.txt"); + EXPECT_EQ(v2_config->set("TLSKeylogFile", "1201"), std::nullopt); + EXPECT_EQ(v2_config->getTLSKeylogFile(), "/tmp/ocpp_tls_keylog.txt"); + + // custom key (read only) + kv = v2_config->get("ACustomKey"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ACustomKey"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_TRUE(kv.value().readonly); + EXPECT_EQ(v2_config->set("ACustomKey", "ToThisValueToo"), std::nullopt); + kv = v2_config->get("ACustomKey"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ACustomKey"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_TRUE(kv.value().readonly); + + // custom key (read write) + kv = v2_config->get("ACustomRWKey"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ACustomRWKey"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + EXPECT_EQ(v2_config->set("ACustomRWKey", "ToThisValueTooMore"), ConfigurationStatus::Accepted); + kv = v2_config->get("ACustomRWKey"); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ACustomRWKey"); + EXPECT_EQ(kv.value().value, "ToThisValueTooMore"); + EXPECT_FALSE(kv.value().readonly); +} + +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_firmware_management.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_firmware_management.cpp new file mode 100644 index 0000000000..b5658f36cc --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_firmware_management.cpp @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include +#include + +#include "configuration_stub.hpp" +#include "ocpp/v16/types.hpp" + +namespace { +using namespace ocpp::v16::stubs; + +TEST_P(Configuration, SupportedFileTransferProtocols) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getSupportedFileTransferProtocols(), "FTP"); + auto kv = get()->getSupportedFileTransferProtocolsKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SupportedFileTransferProtocols"); + EXPECT_EQ(kv.value().value, "FTP"); + EXPECT_TRUE(kv.value().readonly); +} + +// ----------------------------------------------------------------------------- +// Oneoff tests where there are differences between implementations +// Note: TEST_F and not TEST_P + +TEST_F(Configuration, SetSupportedFileTransferProtocolsV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("FirmwareManagement", "SupportedFileTransferProtocols", "HTTP,HTTPS"); + + EXPECT_EQ(v2_config->getSupportedFileTransferProtocols(), "HTTP,HTTPS"); + auto kv = v2_config->getSupportedFileTransferProtocolsKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SupportedFileTransferProtocols"); + EXPECT_EQ(kv.value().value, "HTTP,HTTPS"); + EXPECT_TRUE(kv.value().readonly); +} + +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_internal.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_internal.cpp new file mode 100644 index 0000000000..400c554dfe --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_internal.cpp @@ -0,0 +1,990 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include +#include +#include +#include + +#include "configuration_stub.hpp" +#include "ocpp/v16/ocpp_enums.hpp" +#include "ocpp/v16/types.hpp" + +namespace { +using namespace ocpp::v16::stubs; + +// clang-format off +#define FOR_ALL_SupportedMessageTypesSending(apply) \ + apply(Authorize) \ + apply(BootNotification) \ + apply(CancelReservationResponse) \ + apply(CertificateSignedResponse) \ + apply(ChangeAvailabilityResponse) \ + apply(ChangeConfigurationResponse) \ + apply(ClearCacheResponse) \ + apply(ClearChargingProfileResponse) \ + apply(DataTransfer) \ + apply(DataTransferResponse) \ + apply(DeleteCertificateResponse) \ + apply(DiagnosticsStatusNotification) \ + apply(ExtendedTriggerMessageResponse) \ + apply(FirmwareStatusNotification) \ + apply(GetCompositeScheduleResponse) \ + apply(GetConfigurationResponse) \ + apply(GetDiagnosticsResponse) \ + apply(GetInstalledCertificateIdsResponse) \ + apply(GetLocalListVersionResponse) \ + apply(GetLogResponse) \ + apply(Heartbeat) \ + apply(InstallCertificateResponse) \ + apply(LogStatusNotification) \ + apply(MeterValues) \ + apply(RemoteStartTransactionResponse) \ + apply(RemoteStopTransactionResponse) \ + apply(ReserveNowResponse) \ + apply(ResetResponse) \ + apply(SecurityEventNotification) \ + apply(SendLocalListResponse) \ + apply(SetChargingProfileResponse) \ + apply(SignCertificate) \ + apply(SignedFirmwareStatusNotification) \ + apply(SignedUpdateFirmwareResponse) \ + apply(StartTransaction) \ + apply(StatusNotification) \ + apply(StopTransaction) \ + apply(TriggerMessageResponse) \ + apply(UnlockConnectorResponse) \ + apply(UpdateFirmwareResponse) + +#define FOR_ALL_UnsupportedMessageTypesSending(apply) \ + apply(AuthorizeResponse) \ + apply(BootNotificationResponse) \ + apply(CancelReservation) \ + apply(CertificateSigned) \ + apply(ChangeAvailability) \ + apply(ChangeConfiguration) \ + apply(ClearCache) \ + apply(ClearChargingProfile) \ + apply(DeleteCertificate) \ + apply(DiagnosticsStatusNotificationResponse) \ + apply(ExtendedTriggerMessage) \ + apply(FirmwareStatusNotificationResponse) \ + apply(GetCompositeSchedule) \ + apply(GetConfiguration) \ + apply(GetDiagnostics) \ + apply(GetInstalledCertificateIds) \ + apply(GetLocalListVersion) \ + apply(GetLog) \ + apply(HeartbeatResponse) \ + apply(InstallCertificate) \ + apply(LogStatusNotificationResponse) \ + apply(MeterValuesResponse) \ + apply(RemoteStartTransaction) \ + apply(RemoteStopTransaction) \ + apply(ReserveNow) \ + apply(Reset) \ + apply(SecurityEventNotificationResponse) \ + apply(SendLocalList) \ + apply(SetChargingProfile) \ + apply(SignCertificateResponse) \ + apply(SignedFirmwareStatusNotificationResponse) \ + apply(SignedUpdateFirmware) \ + apply(StartTransactionResponse) \ + apply(StatusNotificationResponse) \ + apply(StopTransactionResponse) \ + apply(TriggerMessage) \ + apply(UnlockConnector) \ + apply(UpdateFirmware) \ + apply(InternalError) + +#define FOR_ALL_SupportedMessageTypesReceiveing(apply) \ + apply(AuthorizeResponse) \ + apply(BootNotificationResponse) \ + apply(CancelReservation) \ + apply(CertificateSigned) \ + apply(ChangeAvailability) \ + apply(ChangeConfiguration) \ + apply(ClearCache) \ + apply(ClearChargingProfile) \ + apply(DataTransfer) \ + apply(DataTransferResponse) \ + apply(DeleteCertificate) \ + apply(DiagnosticsStatusNotificationResponse) \ + apply(ExtendedTriggerMessage) \ + apply(FirmwareStatusNotificationResponse) \ + apply(GetCompositeSchedule) \ + apply(GetConfiguration) \ + apply(GetDiagnostics) \ + apply(GetInstalledCertificateIds) \ + apply(GetLocalListVersion) \ + apply(GetLog) \ + apply(HeartbeatResponse) \ + apply(InstallCertificate) \ + apply(LogStatusNotificationResponse) \ + apply(MeterValuesResponse) \ + apply(RemoteStartTransaction) \ + apply(RemoteStopTransaction) \ + apply(ReserveNow) \ + apply(Reset) \ + apply(SecurityEventNotificationResponse) \ + apply(SendLocalList) \ + apply(SetChargingProfile) \ + apply(SignCertificateResponse) \ + apply(SignedFirmwareStatusNotificationResponse) \ + apply(SignedUpdateFirmware) \ + apply(StartTransactionResponse) \ + apply(StatusNotificationResponse) \ + apply(StopTransactionResponse) \ + apply(TriggerMessage) \ + apply(UnlockConnector) \ + apply(UpdateFirmware) + +#define FOR_ALL_UnsupportedMessageTypesReceiveing(apply) \ + apply(Authorize) \ + apply(BootNotification) \ + apply(CancelReservationResponse) \ + apply(CertificateSignedResponse) \ + apply(ChangeAvailabilityResponse) \ + apply(ChangeConfigurationResponse) \ + apply(ClearCacheResponse) \ + apply(ClearChargingProfileResponse) \ + apply(DeleteCertificateResponse) \ + apply(DiagnosticsStatusNotification) \ + apply(ExtendedTriggerMessageResponse) \ + apply(FirmwareStatusNotification) \ + apply(GetCompositeScheduleResponse) \ + apply(GetConfigurationResponse) \ + apply(GetDiagnosticsResponse) \ + apply(GetInstalledCertificateIdsResponse) \ + apply(GetLocalListVersionResponse) \ + apply(GetLogResponse) \ + apply(Heartbeat) \ + apply(InstallCertificateResponse) \ + apply(LogStatusNotification) \ + apply(MeterValues) \ + apply(RemoteStartTransactionResponse) \ + apply(RemoteStopTransactionResponse) \ + apply(ReserveNowResponse) \ + apply(ResetResponse) \ + apply(SecurityEventNotification) \ + apply(SendLocalListResponse) \ + apply(SetChargingProfileResponse) \ + apply(SignCertificate) \ + apply(SignedFirmwareStatusNotification) \ + apply(SignedUpdateFirmwareResponse) \ + apply(StartTransaction) \ + apply(StatusNotification) \ + apply(StopTransaction) \ + apply(TriggerMessageResponse) \ + apply(UnlockConnectorResponse) \ + apply(UpdateFirmwareResponse) \ + apply(InternalError) +// clang-format on + +bool in_set(const std::set& valid, ocpp::v16::MessageType value) { + const auto res = valid.find(value); + return res != valid.end(); +} + +TEST_P(Configuration, CentralSystemURI) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getCentralSystemURI(), "127.0.0.1:8180/steve/websocket/CentralSystemService/"); + get()->setCentralSystemURI("CentralSystemURIvalue"); + EXPECT_EQ(get()->getCentralSystemURI(), "CentralSystemURIvalue"); + auto kv = get()->getCentralSystemURIKeyValue(); + EXPECT_EQ(kv.key, "CentralSystemURI"); + EXPECT_EQ(kv.value, "CentralSystemURIvalue"); + // TODO(james-ctc): schema lists this as readonly yet setters exist + // it is possible that the schema is in error + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, ChargePointId) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getChargePointId(), "cp001"); + auto kv = get()->getChargePointIdKeyValue(); + EXPECT_EQ(kv.key, "ChargePointId"); + EXPECT_EQ(kv.value, "cp001"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, SupportedCiphers12) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getSupportedCiphers12(), + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384"); + auto kv = get()->getSupportedCiphers12KeyValue(); + EXPECT_EQ(kv.key, "SupportedCiphers12"); + EXPECT_EQ(kv.value, + "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, SupportedCiphers13) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getSupportedCiphers13(), "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"); + auto kv = get()->getSupportedCiphers13KeyValue(); + EXPECT_EQ(kv.key, "SupportedCiphers13"); + EXPECT_EQ(kv.value, "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, SupportedMeasurands) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getSupportedMeasurands(), + "Energy.Active.Import.Register,Energy.Active.Export.Register,Power.Active.Import,Voltage,Current.Import," + "Frequency,Current.Offered,Power.Offered,SoC,Temperature"); + auto kv = get()->getSupportedMeasurandsKeyValue(); + EXPECT_EQ(kv.key, "SupportedMeasurands"); + EXPECT_EQ(kv.value, "Energy.Active.Import.Register,Energy.Active.Export.Register,Power.Active.Import,Voltage," + "Current.Import,Frequency,Current.Offered,Power.Offered,SoC,Temperature"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, TLSKeylogFile) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getTLSKeylogFile(), "/tmp/ocpp_tls_keylog.txt"); +} + +TEST_P(Configuration, WebsocketPingPayload) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getWebsocketPingPayload(), "hello there"); + auto kv = get()->getWebsocketPingPayloadKeyValue(); + EXPECT_EQ(kv.key, "WebsocketPingPayload"); + EXPECT_EQ(kv.value, "hello there"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, ChargePointModel) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getChargePointModel(), "Yeti"); + auto kv = get()->getChargePointModelKeyValue(); + EXPECT_EQ(kv.key, "ChargePointModel"); + EXPECT_EQ(kv.value, "Yeti"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, ChargePointVendor) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getChargePointVendor(), "Pionix"); + auto kv = get()->getChargePointVendorKeyValue(); + EXPECT_EQ(kv.key, "ChargePointVendor"); + EXPECT_EQ(kv.value, "Pionix"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, AuthorizeConnectorZeroOnConnectorOne) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_TRUE(get()->getAuthorizeConnectorZeroOnConnectorOne()); + auto kv = get()->getAuthorizeConnectorZeroOnConnectorOneKeyValue(); + EXPECT_EQ(kv.key, "AuthorizeConnectorZeroOnConnectorOne"); + EXPECT_EQ(kv.value, "true"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, LogMessages) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_TRUE(get()->getLogMessages()); + auto kv = get()->getLogMessagesKeyValue(); + EXPECT_EQ(kv.key, "LogMessages"); + EXPECT_EQ(kv.value, "true"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, LogMessagesRaw) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_FALSE(get()->getLogMessagesRaw()); + auto kv = get()->getLogMessagesRawKeyValue(); + EXPECT_EQ(kv.key, "LogMessagesRaw"); + EXPECT_EQ(kv.value, "false"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, LogRotationDateSuffix) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_FALSE(get()->getLogRotationDateSuffix()); + auto kv = get()->getLogRotationDateSuffixKeyValue(); + EXPECT_EQ(kv.key, "LogRotationDateSuffix"); + EXPECT_EQ(kv.value, "false"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, LogRotation) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_FALSE(get()->getLogRotation()); + auto kv = get()->getLogRotationKeyValue(); + EXPECT_EQ(kv.key, "LogRotation"); + EXPECT_EQ(kv.value, "false"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, LogRotationMaximumFileCount) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getLogRotationMaximumFileCount(), 0); + auto kv = get()->getLogRotationMaximumFileCountKeyValue(); + EXPECT_EQ(kv.key, "LogRotationMaximumFileCount"); + EXPECT_EQ(kv.value, "0"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, LogRotationMaximumFileSize) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getLogRotationMaximumFileSize(), 0); + auto kv = get()->getLogRotationMaximumFileSizeKeyValue(); + EXPECT_EQ(kv.key, "LogRotationMaximumFileSize"); + EXPECT_EQ(kv.value, "0"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, MaxCompositeScheduleDuration) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getMaxCompositeScheduleDuration(), 31536000); + auto kv = get()->getMaxCompositeScheduleDurationKeyValue(); + EXPECT_EQ(kv.key, "MaxCompositeScheduleDuration"); + EXPECT_EQ(kv.value, "31536000"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, MaxMessageSize) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getMaxMessageSize(), 65000); + auto kv = get()->getMaxMessageSizeKeyValue(); + EXPECT_EQ(kv.key, "MaxMessageSize"); + EXPECT_EQ(kv.value, "65000"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, OcspRequestInterval) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getOcspRequestInterval(), 604800); + auto kv = get()->getOcspRequestIntervalKeyValue(); + EXPECT_EQ(kv.key, "OcspRequestInterval"); + EXPECT_EQ(kv.value, "604800"); + EXPECT_FALSE(kv.readonly); + + get()->setOcspRequestInterval(86500); + EXPECT_EQ(get()->getOcspRequestInterval(), 86500); + kv = get()->getOcspRequestIntervalKeyValue(); + EXPECT_EQ(kv.key, "OcspRequestInterval"); + EXPECT_EQ(kv.value, "86500"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, RetryBackoffRandomRange) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getRetryBackoffRandomRange(), 10); + auto kv = get()->getRetryBackoffRandomRangeKeyValue(); + EXPECT_EQ(kv.key, "RetryBackoffRandomRange"); + EXPECT_EQ(kv.value, "10"); + EXPECT_FALSE(kv.readonly); + + get()->setRetryBackoffRandomRange(3600); + EXPECT_EQ(get()->getRetryBackoffRandomRange(), 3600); + kv = get()->getRetryBackoffRandomRangeKeyValue(); + EXPECT_EQ(kv.key, "RetryBackoffRandomRange"); + EXPECT_EQ(kv.value, "3600"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, RetryBackoffRepeatTimes) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getRetryBackoffRepeatTimes(), 3); + auto kv = get()->getRetryBackoffRepeatTimesKeyValue(); + EXPECT_EQ(kv.key, "RetryBackoffRepeatTimes"); + EXPECT_EQ(kv.value, "3"); + EXPECT_FALSE(kv.readonly); + + get()->setRetryBackoffRepeatTimes(10); + EXPECT_EQ(get()->getRetryBackoffRepeatTimes(), 10); + kv = get()->getRetryBackoffRepeatTimesKeyValue(); + EXPECT_EQ(kv.key, "RetryBackoffRepeatTimes"); + EXPECT_EQ(kv.value, "10"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, RetryBackoffWaitMinimum) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getRetryBackoffWaitMinimum(), 3); + auto kv = get()->getRetryBackoffWaitMinimumKeyValue(); + EXPECT_EQ(kv.key, "RetryBackoffWaitMinimum"); + EXPECT_EQ(kv.value, "3"); + EXPECT_FALSE(kv.readonly); + + get()->setRetryBackoffWaitMinimum(15); + EXPECT_EQ(get()->getRetryBackoffWaitMinimum(), 15); + kv = get()->getRetryBackoffWaitMinimumKeyValue(); + EXPECT_EQ(kv.key, "RetryBackoffWaitMinimum"); + EXPECT_EQ(kv.value, "15"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, StopTransactionIfUnlockNotSupported) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_FALSE(get()->getStopTransactionIfUnlockNotSupported()); + auto kv = get()->getStopTransactionIfUnlockNotSupportedKeyValue(); + EXPECT_EQ(kv.key, "StopTransactionIfUnlockNotSupported"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); + + get()->setStopTransactionIfUnlockNotSupported(true); + EXPECT_EQ(get()->getStopTransactionIfUnlockNotSupported(), true); + kv = get()->getStopTransactionIfUnlockNotSupportedKeyValue(); + EXPECT_EQ(kv.key, "StopTransactionIfUnlockNotSupported"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, UseSslDefaultVerifyPaths) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_TRUE(get()->getUseSslDefaultVerifyPaths()); + auto kv = get()->getUseSslDefaultVerifyPathsKeyValue(); + EXPECT_EQ(kv.key, "UseSslDefaultVerifyPaths"); + EXPECT_EQ(kv.value, "true"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, VerifyCsmsAllowWildcards) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_FALSE(get()->getVerifyCsmsAllowWildcards()); + auto kv = get()->getVerifyCsmsAllowWildcardsKeyValue(); + EXPECT_EQ(kv.key, "VerifyCsmsAllowWildcards"); + EXPECT_EQ(kv.value, "false"); + EXPECT_TRUE(kv.readonly); + + get()->setVerifyCsmsAllowWildcards(true); + EXPECT_EQ(get()->getVerifyCsmsAllowWildcards(), true); + kv = get()->getVerifyCsmsAllowWildcardsKeyValue(); + EXPECT_EQ(kv.key, "VerifyCsmsAllowWildcards"); + EXPECT_EQ(kv.value, "true"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, VerifyCsmsCommonName) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_TRUE(get()->getVerifyCsmsCommonName()); + auto kv = get()->getVerifyCsmsCommonNameKeyValue(); + EXPECT_EQ(kv.key, "VerifyCsmsCommonName"); + EXPECT_EQ(kv.value, "true"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, WaitForStopTransactionsOnResetTimeout) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getWaitForStopTransactionsOnResetTimeout(), 60); + auto kv = get()->getWaitForStopTransactionsOnResetTimeoutKeyValue(); + EXPECT_EQ(kv.key, "WaitForStopTransactionsOnResetTimeout"); + EXPECT_EQ(kv.value, "60"); + EXPECT_FALSE(kv.readonly); + + get()->setWaitForStopTransactionsOnResetTimeout(12); + EXPECT_EQ(get()->getWaitForStopTransactionsOnResetTimeout(), 12); + kv = get()->getWaitForStopTransactionsOnResetTimeoutKeyValue(); + EXPECT_EQ(kv.key, "WaitForStopTransactionsOnResetTimeout"); + EXPECT_EQ(kv.value, "12"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, WebsocketPongTimeout) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getWebsocketPongTimeout(), 5); + auto kv = get()->getWebsocketPongTimeoutKeyValue(); + EXPECT_EQ(kv.key, "WebsocketPongTimeout"); + EXPECT_EQ(kv.value, "5"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, InternalBooleans) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getNumberOfConnectors(), 1); // needed by getAuthorizeConnectorZeroOnConnectorOne + + EXPECT_FALSE(get()->getEnableTLSKeylog()); + EXPECT_FALSE(get()->getUseTPM()); + EXPECT_FALSE(get()->getUseTPMSeccLeafCertificate()); +} + +TEST_P(Configuration, LogMessagesFormat) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + const auto res = get()->getLogMessagesFormat(); + EXPECT_TRUE(res.empty()); + auto kv = get()->getLogMessagesFormatKeyValue(); + EXPECT_EQ(kv.key, "LogMessagesFormat"); + EXPECT_EQ(kv.value, ""); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, IgnoredProfilePurposesOffline) { + using ChargingProfilePurposeType = ocpp::v16::ChargingProfilePurposeType; + ASSERT_NE(get(), nullptr); + // No initial value set - hence set() doesn't work + + auto res = get()->getIgnoredProfilePurposesOffline(); + EXPECT_TRUE(res.empty()); + auto kv = get()->getIgnoredProfilePurposesOfflineKeyValue(); + ASSERT_FALSE(kv.has_value()); + + // set only works when there is a value configured + get()->setIgnoredProfilePurposesOffline("TxDefaultProfile"); + res = get()->getIgnoredProfilePurposesOffline(); + EXPECT_TRUE(res.empty()); + kv = get()->getIgnoredProfilePurposesOfflineKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, SupportedChargingProfilePurposeTypes) { + using ChargingProfilePurposeType = ocpp::v16::ChargingProfilePurposeType; + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + const auto res = get()->getSupportedChargingProfilePurposeTypes(); + ASSERT_EQ(res.size(), 3); + EXPECT_EQ(res[0], ChargingProfilePurposeType::ChargePointMaxProfile); + EXPECT_EQ(res[1], ChargingProfilePurposeType::TxDefaultProfile); + EXPECT_EQ(res[2], ChargingProfilePurposeType::TxProfile); + + auto kv = get()->getSupportedChargingProfilePurposeTypesKeyValue(); + EXPECT_EQ(kv.key, "SupportedChargingProfilePurposeTypes"); + EXPECT_EQ(kv.value, "ChargePointMaxProfile,TxDefaultProfile,TxProfile"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, AllowChargingProfileWithoutStartSchedule) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + EXPECT_TRUE(get()->getAllowChargingProfileWithoutStartSchedule().has_value()); + EXPECT_EQ(get()->getAllowChargingProfileWithoutStartSchedule().value(), true); + auto kv = get()->getAllowChargingProfileWithoutStartScheduleKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "AllowChargingProfileWithoutStartSchedule"); + EXPECT_EQ(kv.value().value, "true"); + EXPECT_FALSE(kv.value().readonly); + + get()->setAllowChargingProfileWithoutStartSchedule(false); + EXPECT_TRUE(get()->getAllowChargingProfileWithoutStartSchedule().has_value()); + EXPECT_EQ(get()->getAllowChargingProfileWithoutStartSchedule().value(), false); + kv = get()->getAllowChargingProfileWithoutStartScheduleKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "AllowChargingProfileWithoutStartSchedule"); + EXPECT_EQ(kv.value().value, "false"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, CompositeScheduleDefaultLimitAmps) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + EXPECT_TRUE(get()->getCompositeScheduleDefaultLimitAmps().has_value()); + EXPECT_EQ(get()->getCompositeScheduleDefaultLimitAmps(), 48); + auto kv = get()->getCompositeScheduleDefaultLimitAmpsKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CompositeScheduleDefaultLimitAmps"); + EXPECT_EQ(kv.value().value, "48"); + EXPECT_FALSE(kv.value().readonly); + + get()->setCompositeScheduleDefaultLimitAmps(32); + EXPECT_TRUE(get()->getCompositeScheduleDefaultLimitAmps().has_value()); + EXPECT_EQ(get()->getCompositeScheduleDefaultLimitAmps(), 32); + kv = get()->getCompositeScheduleDefaultLimitAmpsKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CompositeScheduleDefaultLimitAmps"); + EXPECT_EQ(kv.value().value, "32"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, CompositeScheduleDefaultLimitWatts) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + EXPECT_TRUE(get()->getCompositeScheduleDefaultLimitWatts().has_value()); + EXPECT_EQ(get()->getCompositeScheduleDefaultLimitWatts(), 33120); + auto kv = get()->getCompositeScheduleDefaultLimitWattsKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CompositeScheduleDefaultLimitWatts"); + EXPECT_EQ(kv.value().value, "33120"); + EXPECT_FALSE(kv.value().readonly); + + get()->setCompositeScheduleDefaultLimitWatts(34000); + EXPECT_EQ(get()->getCompositeScheduleDefaultLimitWatts(), 34000); + kv = get()->getCompositeScheduleDefaultLimitWattsKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CompositeScheduleDefaultLimitWatts"); + EXPECT_EQ(kv.value().value, "34000"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, CompositeScheduleDefaultNumberPhases) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + EXPECT_TRUE(get()->getCompositeScheduleDefaultNumberPhases().has_value()); + EXPECT_EQ(get()->getCompositeScheduleDefaultNumberPhases(), 3); + auto kv = get()->getCompositeScheduleDefaultNumberPhasesKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CompositeScheduleDefaultNumberPhases"); + EXPECT_EQ(kv.value().value, "3"); + EXPECT_FALSE(kv.value().readonly); + + get()->setCompositeScheduleDefaultNumberPhases(1); + EXPECT_TRUE(get()->getCompositeScheduleDefaultNumberPhases().has_value()); + EXPECT_EQ(get()->getCompositeScheduleDefaultNumberPhases(), 1); + kv = get()->getCompositeScheduleDefaultNumberPhasesKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CompositeScheduleDefaultNumberPhases"); + EXPECT_EQ(kv.value().value, "1"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, ConnectorEvseIds) { + ASSERT_NE(get(), nullptr); + // No initial value set - hence set() doesn't work + + EXPECT_FALSE(get()->getConnectorEvseIds().has_value()); + auto kv = get()->getConnectorEvseIdsKeyValue(); + ASSERT_FALSE(kv.has_value()); + + // set only works when there is a value configured + get()->setConnectorEvseIds("01234567"); + EXPECT_FALSE(get()->getConnectorEvseIds().has_value()); + kv = get()->getConnectorEvseIdsKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, HostName) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + EXPECT_FALSE(get()->getHostName().has_value()); + auto kv = get()->getHostNameKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, IFace) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + EXPECT_FALSE(get()->getIFace().has_value()); + auto kv = get()->getIFaceKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, MessageTypesDiscardForQueueing) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + EXPECT_FALSE(get()->getMessageTypesDiscardForQueueing().has_value()); + auto kv = get()->getMessageTypesDiscardForQueueingKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, MessageQueueSizeThreshold) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + EXPECT_FALSE(get()->getMessageQueueSizeThreshold().has_value()); + auto kv = get()->getMessageQueueSizeThresholdKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, QueueAllMessages) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + EXPECT_FALSE(get()->getQueueAllMessages().has_value()); + auto kv = get()->getQueueAllMessagesKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, SeccLeafSubjectCommonName) { + ASSERT_NE(get(), nullptr); + // No initial value set - hence set() doesn't work + + EXPECT_FALSE(get()->getSeccLeafSubjectCommonName().has_value()); + auto kv = get()->getSeccLeafSubjectCommonNameKeyValue(); + ASSERT_FALSE(kv.has_value()); + + // set only works when there is a value configured + get()->setSeccLeafSubjectCommonName("0123456789AB"); + EXPECT_FALSE(get()->getSeccLeafSubjectCommonName().has_value()); + kv = get()->getSeccLeafSubjectCommonNameKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, SeccLeafSubjectCountry) { + ASSERT_NE(get(), nullptr); + // No initial value set - hence set() doesn't work + + EXPECT_FALSE(get()->getSeccLeafSubjectCountry().has_value()); + auto kv = get()->getSeccLeafSubjectCountryKeyValue(); + ASSERT_FALSE(kv.has_value()); + + // set only works when there is a value configured + get()->setSeccLeafSubjectCountry("UK"); + EXPECT_FALSE(get()->getSeccLeafSubjectCountry().has_value()); + kv = get()->getSeccLeafSubjectCountryKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, SeccLeafSubjectOrganization) { + ASSERT_NE(get(), nullptr); + // No initial value set - hence set() doesn't work + + EXPECT_FALSE(get()->getSeccLeafSubjectOrganization().has_value()); + auto kv = get()->getSeccLeafSubjectOrganizationKeyValue(); + ASSERT_FALSE(kv.has_value()); + + // set only works when there is a value configured + get()->setSeccLeafSubjectOrganization("0123456789AB_organisation"); + EXPECT_FALSE(get()->getSeccLeafSubjectOrganization().has_value()); + kv = get()->getSeccLeafSubjectOrganizationKeyValue(); + ASSERT_FALSE(kv.has_value()); +} + +TEST_P(Configuration, SupplyVoltage) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + EXPECT_TRUE(get()->getSupplyVoltage().has_value()); + EXPECT_EQ(get()->getSupplyVoltage(), 230); + auto kv = get()->getSupplyVoltageKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SupplyVoltage"); + EXPECT_EQ(kv.value().value, "230"); + EXPECT_FALSE(kv.value().readonly); + + get()->setSupplyVoltage(250); + EXPECT_TRUE(get()->getSupplyVoltage().has_value()); + EXPECT_EQ(get()->getSupplyVoltage(), 250); + kv = get()->getSupplyVoltageKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SupplyVoltage"); + EXPECT_EQ(kv.value().value, "250"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, AllMeterPublicKeys) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getAllMeterPublicKeyKeyValues(), std::nullopt); +} + +TEST_P(Configuration, PublicKey) { + ASSERT_NE(get(), nullptr); + + const auto max = get()->getNumberOfConnectors(); + for (std::uint8_t i = 0; i <= max; i++) { + SCOPED_TRACE(std::to_string(i)); + EXPECT_EQ(get()->getPublicKeyKeyValue(i), std::nullopt); + } + + for (std::uint8_t i = 1; i <= max; i++) { + SCOPED_TRACE(std::to_string(i)); + auto key = std::string{"MeterPublicKey["} + std::to_string(i) + ']'; + auto value = std::string{"Public Key: "} + std::to_string(i); + EXPECT_TRUE(get()->setMeterPublicKey(i, value)); + auto kv = get()->getPublicKeyKeyValue(i); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, key.c_str()); + EXPECT_EQ(kv.value().value, value.c_str()); + EXPECT_TRUE(kv.value().readonly); + } + + auto kvl = get()->getAllMeterPublicKeyKeyValues(); + ASSERT_TRUE(kvl); + ASSERT_EQ(kvl.value().size(), 1); + auto kv = kvl.value()[0]; + EXPECT_EQ(kv.key, "MeterPublicKey[1]"); + EXPECT_EQ(kv.value, "Public Key: 1"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, SupportedMessageTypesSending) { + using MessageType = ocpp::v16::MessageType; + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + const auto res = get()->getSupportedMessageTypesSending(); + ASSERT_EQ(res.size(), 40); + std::uint8_t count = 0; + +#define VALUE(a) \ + EXPECT_TRUE(in_set(res, ocpp::v16::MessageType::a)); \ + count++; + FOR_ALL_SupportedMessageTypesSending(VALUE); + EXPECT_EQ(count, res.size()); +#undef VALUE +#define VALUE(a) EXPECT_FALSE(in_set(res, ocpp::v16::MessageType::a)); + FOR_ALL_UnsupportedMessageTypesSending(VALUE); +#undef VALUE +} + +TEST_P(Configuration, SupportedMessageTypesReceiving) { + using MessageType = ocpp::v16::MessageType; + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + const auto res = get()->getSupportedMessageTypesReceiving(); + ASSERT_EQ(res.size(), 40); + std::uint8_t count = 0; + +#define VALUE(a) \ + EXPECT_TRUE(in_set(res, ocpp::v16::MessageType::a)); \ + count++; + FOR_ALL_SupportedMessageTypesReceiveing(VALUE); + EXPECT_EQ(count, res.size()); +#undef VALUE +#define VALUE(a) EXPECT_FALSE(in_set(res, ocpp::v16::MessageType::a)); + FOR_ALL_UnsupportedMessageTypesReceiveing(VALUE); +#undef VALUE +} + +// ----------------------------------------------------------------------------- +// Oneoff tests where there are differences between implementations +// Note: TEST_F and not TEST_P + +TEST_F(Configuration, SetIgnoredProfilePurposesOfflineV2) { + using ChargingProfilePurposeType = ocpp::v16::ChargingProfilePurposeType; + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Internal", "IgnoredProfilePurposesOffline", ""); + + auto res = v2_config->getIgnoredProfilePurposesOffline(); + EXPECT_TRUE(res.empty()); + auto kv = v2_config->getIgnoredProfilePurposesOfflineKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "IgnoredProfilePurposesOffline"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setIgnoredProfilePurposesOffline("TxDefaultProfile"); + res = v2_config->getIgnoredProfilePurposesOffline(); + ASSERT_EQ(res.size(), 1); + EXPECT_EQ(res[0], ChargingProfilePurposeType::TxDefaultProfile); + kv = v2_config->getIgnoredProfilePurposesOfflineKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "IgnoredProfilePurposesOffline"); + EXPECT_EQ(kv.value().value, "TxDefaultProfile"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetSeccLeafSubjectCommonNameV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Internal", "SeccLeafSubjectCommonName", ""); + + EXPECT_TRUE(v2_config->getSeccLeafSubjectCommonName().has_value()); + EXPECT_EQ(v2_config->getSeccLeafSubjectCommonName(), ""); + auto kv = v2_config->getSeccLeafSubjectCommonNameKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SeccLeafSubjectCommonName"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setSeccLeafSubjectCommonName("0123456789AB"); + EXPECT_TRUE(v2_config->getSeccLeafSubjectCommonName().has_value()); + EXPECT_EQ(v2_config->getSeccLeafSubjectCommonName(), "0123456789AB"); + kv = v2_config->getSeccLeafSubjectCommonNameKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SeccLeafSubjectCommonName"); + EXPECT_EQ(kv.value().value, "0123456789AB"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetSeccLeafSubjectCountryV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Internal", "SeccLeafSubjectCountry", ""); + + EXPECT_TRUE(v2_config->getSeccLeafSubjectCountry().has_value()); + auto kv = v2_config->getSeccLeafSubjectCountryKeyValue(); + EXPECT_EQ(v2_config->getSeccLeafSubjectCountry(), ""); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SeccLeafSubjectCountry"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setSeccLeafSubjectCountry("UK"); + EXPECT_TRUE(v2_config->getSeccLeafSubjectCountry().has_value()); + EXPECT_EQ(v2_config->getSeccLeafSubjectCountry(), "UK"); + kv = v2_config->getSeccLeafSubjectCountryKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SeccLeafSubjectCountry"); + EXPECT_EQ(kv.value().value, "UK"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetSeccLeafSubjectOrganizationV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Internal", "SeccLeafSubjectOrganization", ""); + + EXPECT_TRUE(v2_config->getSeccLeafSubjectOrganization().has_value()); + EXPECT_EQ(v2_config->getSeccLeafSubjectOrganization(), ""); + auto kv = v2_config->getSeccLeafSubjectOrganizationKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SeccLeafSubjectOrganization"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setSeccLeafSubjectOrganization("0123456789AB_organisation"); + EXPECT_TRUE(v2_config->getSeccLeafSubjectOrganization().has_value()); + EXPECT_EQ(v2_config->getSeccLeafSubjectOrganization(), "0123456789AB_organisation"); + kv = v2_config->getSeccLeafSubjectOrganizationKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "SeccLeafSubjectOrganization"); + EXPECT_EQ(kv.value().value, "0123456789AB_organisation"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetConnectorEvseIdsV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Internal", "ConnectorEvseIds", ""); + + EXPECT_TRUE(v2_config->getConnectorEvseIds().has_value()); + EXPECT_EQ(v2_config->getConnectorEvseIds(), ""); + auto kv = v2_config->getConnectorEvseIdsKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "ConnectorEvseIds"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setConnectorEvseIds("01234567"); + EXPECT_TRUE(v2_config->getConnectorEvseIds().has_value()); + EXPECT_EQ(v2_config->getConnectorEvseIds(), "01234567"); + kv = v2_config->getConnectorEvseIdsKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "ConnectorEvseIds"); + EXPECT_EQ(kv.value().value, "01234567"); + EXPECT_FALSE(kv.value().readonly); +} + +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_local_auth_list.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_local_auth_list.cpp new file mode 100644 index 0000000000..6a46fcaa97 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_local_auth_list.cpp @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include + +#include "configuration_stub.hpp" + +namespace { +using namespace ocpp::v16::stubs; + +TEST_P(Configuration, LocalAuthListEnabled) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_TRUE(get()->getLocalAuthListEnabled()); + auto kv = get()->getLocalAuthListEnabledKeyValue(); + EXPECT_EQ(kv.key, "LocalAuthListEnabled"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); + + get()->setLocalAuthListEnabled(false); + EXPECT_FALSE(get()->getLocalAuthListEnabled()); + kv = get()->getLocalAuthListEnabledKeyValue(); + EXPECT_EQ(kv.key, "LocalAuthListEnabled"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, LocalAuthListMaxLength) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getLocalAuthListMaxLength(), 42); + auto kv = get()->getLocalAuthListMaxLengthKeyValue(); + EXPECT_EQ(kv.key, "LocalAuthListMaxLength"); + EXPECT_EQ(kv.value, "42"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, SendLocalListMaxLength) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getSendLocalListMaxLength(), 42); + auto kv = get()->getSendLocalListMaxLengthKeyValue(); + EXPECT_EQ(kv.key, "SendLocalListMaxLength"); + EXPECT_EQ(kv.value, "42"); + EXPECT_TRUE(kv.readonly); +} + +// ----------------------------------------------------------------------------- +// Oneoff tests where there are differences between implementations +// Note: TEST_F and not TEST_P + +TEST_F(Configuration, GetLocalAuthListMaxLengthV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("LocalAuthListManagement", "LocalAuthListMaxLength", "101"); + + EXPECT_EQ(v2_config->getLocalAuthListMaxLength(), 101); + auto kv = v2_config->getLocalAuthListMaxLengthKeyValue(); + EXPECT_EQ(kv.key, "LocalAuthListMaxLength"); + EXPECT_EQ(kv.value, "101"); + EXPECT_TRUE(kv.readonly); +} + +TEST_F(Configuration, GetSendLocalListMaxLengthV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("LocalAuthListManagement", "SendLocalListMaxLength", "102"); + + EXPECT_EQ(v2_config->getSendLocalListMaxLength(), 102); + auto kv = v2_config->getSendLocalListMaxLengthKeyValue(); + EXPECT_EQ(kv.key, "SendLocalListMaxLength"); + EXPECT_EQ(kv.value, "102"); + EXPECT_TRUE(kv.readonly); +} + +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_pnc.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_pnc.cpp new file mode 100644 index 0000000000..e9215c8eb2 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_pnc.cpp @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include + +#include "configuration_stub.hpp" + +namespace { +using namespace ocpp::v16::stubs; + +TEST_P(Configuration, ContractValidationOffline) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_TRUE(get()->getContractValidationOffline()); + auto kv = get()->getContractValidationOfflineKeyValue(); + EXPECT_EQ(kv.key, "ContractValidationOffline"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); + + get()->setContractValidationOffline(false); + EXPECT_FALSE(get()->getContractValidationOffline()); + kv = get()->getContractValidationOfflineKeyValue(); + EXPECT_EQ(kv.key, "ContractValidationOffline"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, ISO15118CertificateManagementEnabled) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_TRUE(get()->getISO15118CertificateManagementEnabled()); + auto kv = get()->getISO15118CertificateManagementEnabledKeyValue(); + EXPECT_EQ(kv.key, "ISO15118CertificateManagementEnabled"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); + + get()->setISO15118CertificateManagementEnabled(false); + EXPECT_FALSE(get()->getISO15118CertificateManagementEnabled()); + kv = get()->getISO15118CertificateManagementEnabledKeyValue(); + EXPECT_EQ(kv.key, "ISO15118CertificateManagementEnabled"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, ISO15118PnCEnabled) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_TRUE(get()->getISO15118PnCEnabled()); + auto kv = get()->getISO15118PnCEnabledKeyValue(); + EXPECT_EQ(kv.key, "ISO15118PnCEnabled"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); + + get()->setISO15118PnCEnabled(false); + EXPECT_FALSE(get()->getISO15118PnCEnabled()); + kv = get()->getISO15118PnCEnabledKeyValue(); + EXPECT_EQ(kv.key, "ISO15118PnCEnabled"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, CentralContractValidationAllowed) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getCentralContractValidationAllowed().has_value()); + auto kv = get()->getCentralContractValidationAllowedKeyValue(); + ASSERT_FALSE(kv); + + // needs existing value for set to work + get()->setCentralContractValidationAllowed(false); + EXPECT_FALSE(get()->getCentralContractValidationAllowed().has_value()); + kv = get()->getCentralContractValidationAllowedKeyValue(); + ASSERT_FALSE(kv); +} + +TEST_P(Configuration, CertSigningRepeatTimes) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getCertSigningRepeatTimes().has_value()); + auto kv = get()->getCertSigningRepeatTimesKeyValue(); + ASSERT_FALSE(kv); + + // needs existing value for set to work + get()->setCertSigningRepeatTimes(99); + EXPECT_FALSE(get()->getCertSigningRepeatTimes().has_value()); + kv = get()->getCertSigningRepeatTimesKeyValue(); + ASSERT_FALSE(kv); +} + +TEST_P(Configuration, CertSigningWaitMinimum) { + ASSERT_NE(get(), nullptr); + EXPECT_FALSE(get()->getCertSigningWaitMinimum().has_value()); + auto kv = get()->getCertSigningWaitMinimumKeyValue(); + ASSERT_FALSE(kv); + + // needs existing value for set to work + get()->setCertSigningWaitMinimum(55); + EXPECT_FALSE(get()->getCertSigningWaitMinimum().has_value()); + kv = get()->getCertSigningWaitMinimumKeyValue(); + ASSERT_FALSE(kv); +} + +// ----------------------------------------------------------------------------- +// Oneoff tests where there are differences between implementations +// Note: TEST_F and not TEST_P + +TEST_F(Configuration, SetCentralContractValidationAllowedV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("PnC", "CentralContractValidationAllowed", ""); + + EXPECT_FALSE(v2_config->getCentralContractValidationAllowed().has_value()); + auto kv = v2_config->getCentralContractValidationAllowedKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CentralContractValidationAllowed"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setCentralContractValidationAllowed(false); + EXPECT_TRUE(v2_config->getCentralContractValidationAllowed().has_value()); + EXPECT_FALSE(v2_config->getCentralContractValidationAllowed().value()); + kv = v2_config->getCentralContractValidationAllowedKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CentralContractValidationAllowed"); + EXPECT_EQ(kv.value().value, "false"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetCertSigningRepeatTimesV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("PnC", "CertSigningRepeatTimes", ""); + + EXPECT_FALSE(v2_config->getCertSigningRepeatTimes().has_value()); + auto kv = v2_config->getCertSigningRepeatTimesKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CertSigningRepeatTimes"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setCertSigningRepeatTimes(55); + EXPECT_TRUE(v2_config->getCertSigningRepeatTimes().has_value()); + EXPECT_EQ(v2_config->getCertSigningRepeatTimes(), 55); + kv = v2_config->getCertSigningRepeatTimesKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CertSigningRepeatTimes"); + EXPECT_EQ(kv.value().value, "55"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_F(Configuration, SetCertSigningWaitMinimumV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("PnC", "CertSigningWaitMinimum", ""); + + EXPECT_FALSE(v2_config->getCertSigningWaitMinimum().has_value()); + auto kv = v2_config->getCertSigningWaitMinimumKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CertSigningWaitMinimum"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_FALSE(kv.value().readonly); + + v2_config->setCertSigningWaitMinimum(54); + EXPECT_TRUE(v2_config->getCertSigningWaitMinimum().has_value()); + EXPECT_EQ(v2_config->getCertSigningWaitMinimum(), 54); + kv = v2_config->getCertSigningWaitMinimumKeyValue(); + ASSERT_TRUE(kv.has_value()); + EXPECT_EQ(kv.value().key, "CertSigningWaitMinimum"); + EXPECT_EQ(kv.value().value, "54"); + EXPECT_FALSE(kv.value().readonly); +} + +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_security.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_security.cpp new file mode 100644 index 0000000000..88efc52ef1 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_security.cpp @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include + +#include "configuration_stub.hpp" + +namespace { +using namespace ocpp::v16::stubs; + +TEST_P(Configuration, DisableSecurityEventNotifications) { + ASSERT_NE(get(), nullptr); + + // V16 gets a value from the schema file patches + // initial values are from the JSON unit test config files + EXPECT_FALSE(get()->getDisableSecurityEventNotifications()); + auto kv = get()->getDisableSecurityEventNotificationsKeyValue(); + EXPECT_EQ(kv.key, "DisableSecurityEventNotifications"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); + + get()->setDisableSecurityEventNotifications(true); + EXPECT_TRUE(get()->getDisableSecurityEventNotifications()); + kv = get()->getDisableSecurityEventNotificationsKeyValue(); + EXPECT_EQ(kv.key, "DisableSecurityEventNotifications"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, SecurityProfile) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getSecurityProfile(), 0); + auto kv = get()->getSecurityProfileKeyValue(); + EXPECT_EQ(kv.key, "SecurityProfile"); + EXPECT_EQ(kv.value, "0"); + EXPECT_FALSE(kv.readonly); + + get()->setSecurityProfile(3); + EXPECT_EQ(get()->getSecurityProfile(), 3); + kv = get()->getSecurityProfileKeyValue(); + EXPECT_EQ(kv.key, "SecurityProfile"); + EXPECT_EQ(kv.value, "3"); + EXPECT_FALSE(kv.readonly); +} + +TEST_P(Configuration, AuthorizationKey) { + // notes: this one has some special code behind it + + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + + // AuthorizationKey not set so we get the expected nullopt + EXPECT_EQ(get()->getAuthorizationKey(), std::nullopt); + + // AuthorizationKey not set but a KeyValue is returned + // rather than nullopt. kv.value is nullopt though + auto kv = get()->getAuthorizationKeyKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "AuthorizationKey"); + EXPECT_FALSE(kv.value().value.has_value()); + EXPECT_FALSE(kv.value().readonly); + + get()->setAuthorizationKey("01234567890123456789"); + // the correct key is returned + EXPECT_EQ(get()->getAuthorizationKey(), "01234567890123456789"); + kv = get()->getAuthorizationKeyKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "AuthorizationKey"); + // a dummy value is set to avoid leaking the key to the CSMS + EXPECT_EQ(kv.value().value, "DummyAuthorizationKey"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, CpoName) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getCpoName(), std::nullopt); + + // CpoName not set but a KeyValue is returned + // rather than nullopt. kv.value is nullopt though + auto kv = get()->getCpoNameKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "CpoName"); + EXPECT_FALSE(kv.value().value.has_value()); + EXPECT_FALSE(kv.value().readonly); + + get()->setCpoName("setCpoName"); + EXPECT_EQ(get()->getCpoName(), "setCpoName"); + kv = get()->getCpoNameKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "CpoName"); + EXPECT_EQ(kv.value().value, "setCpoName"); + EXPECT_FALSE(kv.value().readonly); +} + +TEST_P(Configuration, AdditionalRootCertificateCheck) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getAdditionalRootCertificateCheck(), std::nullopt); + auto kv = get()->getAdditionalRootCertificateCheckKeyValue(); + ASSERT_FALSE(kv); +} + +TEST_P(Configuration, CertificateSignedMaxChainSize) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getCertificateSignedMaxChainSize(), std::nullopt); + auto kv = get()->getCertificateSignedMaxChainSizeKeyValue(); + ASSERT_FALSE(kv); +} + +TEST_P(Configuration, CertificateStoreMaxLength) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getCertificateStoreMaxLength(), std::nullopt); + auto kv = get()->getCertificateStoreMaxLengthKeyValue(); + ASSERT_FALSE(kv); +} + +// ----------------------------------------------------------------------------- +// Oneoff tests where there are differences between implementations +// Note: TEST_F and not TEST_P + +TEST_F(Configuration, GetDisableSecurityEventNotificationsV16) { + // this test should fail since DisableSecurityEventNotifications is not + // in the JSON config file. + // However there is a patch process that adds information from the + // schema files + // "Adding the following default values to the charge point configuration:" + + // TODO(james-ctc): the V2 implementation will need to consider + // those additions when migrating data + + EXPECT_FALSE(v16_config->getDisableSecurityEventNotifications()); + auto kv = v16_config->getDisableSecurityEventNotificationsKeyValue(); + EXPECT_EQ(kv.key, "DisableSecurityEventNotifications"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); + + v16_config->setDisableSecurityEventNotifications(true); + EXPECT_TRUE(v16_config->getDisableSecurityEventNotifications()); + kv = v16_config->getDisableSecurityEventNotificationsKeyValue(); + EXPECT_EQ(kv.key, "DisableSecurityEventNotifications"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); +} + +TEST_F(Configuration, SetDisableSecurityEventNotificationsV2) { + ASSERT_TRUE(device_model); + device_model->clear("Security", "DisableSecurityEventNotifications"); + + // correctly fails since the key doesn't exits + EXPECT_ANY_THROW(v2_config->getDisableSecurityEventNotifications();); + EXPECT_ANY_THROW(v2_config->getDisableSecurityEventNotificationsKeyValue();); + + // set an initial value + device_model->set("Security", "DisableSecurityEventNotifications", "false"); + + EXPECT_FALSE(v2_config->getDisableSecurityEventNotifications()); + auto kv = v2_config->getDisableSecurityEventNotificationsKeyValue(); + EXPECT_EQ(kv.key, "DisableSecurityEventNotifications"); + EXPECT_EQ(kv.value, "false"); + EXPECT_FALSE(kv.readonly); + + v2_config->setDisableSecurityEventNotifications(true); + EXPECT_TRUE(v2_config->getDisableSecurityEventNotifications()); + kv = v2_config->getDisableSecurityEventNotificationsKeyValue(); + EXPECT_EQ(kv.key, "DisableSecurityEventNotifications"); + EXPECT_EQ(kv.value, "true"); + EXPECT_FALSE(kv.readonly); +} + +TEST_F(Configuration, SetAdditionalRootCertificateCheckV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Security", "AdditionalRootCertificateCheck", ""); + + EXPECT_FALSE(v2_config->getAdditionalRootCertificateCheck().has_value()); + auto kv = v2_config->getAdditionalRootCertificateCheckKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "AdditionalRootCertificateCheck"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_TRUE(kv.value().readonly); + + device_model->set("Security", "AdditionalRootCertificateCheck", "false"); + + EXPECT_TRUE(v2_config->getAdditionalRootCertificateCheck().has_value()); + EXPECT_FALSE(v2_config->getAdditionalRootCertificateCheck().value()); + + kv = v2_config->getAdditionalRootCertificateCheckKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "AdditionalRootCertificateCheck"); + EXPECT_EQ(kv.value().value, "false"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, GetCertificateSignedMaxChainSizeV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Security", "CertificateSignedMaxChainSize", "5"); + + EXPECT_EQ(v2_config->getCertificateSignedMaxChainSize(), 5); + auto kv = v2_config->getCertificateSignedMaxChainSizeKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "CertificateSignedMaxChainSize"); + EXPECT_EQ(kv.value().value, "5"); + EXPECT_TRUE(kv.value().readonly); +} + +TEST_F(Configuration, GetCertificateStoreMaxLengthV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("Security", "CertificateStoreMaxLength", "512"); + + EXPECT_EQ(v2_config->getCertificateStoreMaxLength(), 512); + auto kv = v2_config->getCertificateStoreMaxLengthKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "CertificateStoreMaxLength"); + EXPECT_EQ(kv.value().value, "512"); + EXPECT_TRUE(kv.value().readonly); +} + +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_smart_charging.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_smart_charging.cpp new file mode 100644 index 0000000000..828cc8efbe --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_smart_charging.cpp @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include +#include + +#include "configuration_stub.hpp" +#include "ocpp/v16/types.hpp" + +namespace { +using namespace ocpp::v16::stubs; + +TEST_P(Configuration, ChargingScheduleAllowedChargingRateUnit) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getChargingScheduleAllowedChargingRateUnit(), "Current"); + auto kv = get()->getChargingScheduleAllowedChargingRateUnitKeyValue(); + EXPECT_EQ(kv.key, "ChargingScheduleAllowedChargingRateUnit"); + EXPECT_EQ(kv.value, "Current"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, ChargeProfileMaxStackLevel) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getChargeProfileMaxStackLevel(), 42); + auto kv = get()->getChargeProfileMaxStackLevelKeyValue(); + EXPECT_EQ(kv.key, "ChargeProfileMaxStackLevel"); + EXPECT_EQ(kv.value, "42"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, ChargingScheduleMaxPeriods) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getChargingScheduleMaxPeriods(), 42); + auto kv = get()->getChargingScheduleMaxPeriodsKeyValue(); + EXPECT_EQ(kv.key, "ChargingScheduleMaxPeriods"); + EXPECT_EQ(kv.value, "42"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, MaxChargingProfilesInstalled) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getMaxChargingProfilesInstalled(), 42); + auto kv = get()->getMaxChargingProfilesInstalledKeyValue(); + EXPECT_EQ(kv.key, "MaxChargingProfilesInstalled"); + EXPECT_EQ(kv.value, "42"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, ConnectorSwitch3to1PhaseSupported) { + ASSERT_NE(get(), nullptr); + EXPECT_EQ(get()->getConnectorSwitch3to1PhaseSupported(), std::nullopt); + auto kv = get()->getConnectorSwitch3to1PhaseSupportedKeyValue(); + ASSERT_FALSE(kv); +} + +// ----------------------------------------------------------------------------- +// Oneoff tests where there are differences between implementations +// Note: TEST_F and not TEST_P + +TEST_F(Configuration, SetChargingScheduleAllowedChargingRateUnitV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("SmartCharging", "ChargingScheduleAllowedChargingRateUnit", "Watts"); + + EXPECT_EQ(v2_config->getChargingScheduleAllowedChargingRateUnit(), "Watts"); + auto kv = v2_config->getChargingScheduleAllowedChargingRateUnitKeyValue(); + EXPECT_EQ(kv.key, "ChargingScheduleAllowedChargingRateUnit"); + EXPECT_EQ(kv.value, "Watts"); + EXPECT_TRUE(kv.readonly); +} + +TEST_F(Configuration, SetChargeProfileMaxStackLevelV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("SmartCharging", "ChargeProfileMaxStackLevel", "11"); + + EXPECT_EQ(v2_config->getChargeProfileMaxStackLevel(), 11); + auto kv = v2_config->getChargeProfileMaxStackLevelKeyValue(); + EXPECT_EQ(kv.key, "ChargeProfileMaxStackLevel"); + EXPECT_EQ(kv.value, "11"); + EXPECT_TRUE(kv.readonly); +} + +TEST_F(Configuration, SetChargingScheduleMaxPeriodsV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("SmartCharging", "ChargingScheduleMaxPeriods", "12"); + + EXPECT_EQ(v2_config->getChargingScheduleMaxPeriods(), 12); + auto kv = v2_config->getChargingScheduleMaxPeriodsKeyValue(); + EXPECT_EQ(kv.key, "ChargingScheduleMaxPeriods"); + EXPECT_EQ(kv.value, "12"); + EXPECT_TRUE(kv.readonly); +} + +TEST_F(Configuration, SetMaxChargingProfilesInstalledV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("SmartCharging", "MaxChargingProfilesInstalled", "13"); + + EXPECT_EQ(v2_config->getMaxChargingProfilesInstalled(), 13); + auto kv = v2_config->getMaxChargingProfilesInstalledKeyValue(); + EXPECT_EQ(kv.key, "MaxChargingProfilesInstalled"); + EXPECT_EQ(kv.value, "13"); + EXPECT_TRUE(kv.readonly); +} + +TEST_F(Configuration, SetConnectorSwitch3to1PhaseSupportedV2) { + ASSERT_TRUE(device_model); + // set an initial value + device_model->set("SmartCharging", "ConnectorSwitch3to1PhaseSupported", ""); + + EXPECT_FALSE(v2_config->getConnectorSwitch3to1PhaseSupported().has_value()); + auto kv = v2_config->getConnectorSwitch3to1PhaseSupportedKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ConnectorSwitch3to1PhaseSupported"); + EXPECT_EQ(kv.value().value, ""); + EXPECT_TRUE(kv.value().readonly); + + device_model->set("SmartCharging", "ConnectorSwitch3to1PhaseSupported", "true"); + + EXPECT_TRUE(v2_config->getConnectorSwitch3to1PhaseSupported().has_value()); + EXPECT_TRUE(v2_config->getConnectorSwitch3to1PhaseSupported().value()); + + kv = v2_config->getConnectorSwitch3to1PhaseSupportedKeyValue(); + ASSERT_TRUE(kv); + EXPECT_EQ(kv.value().key, "ConnectorSwitch3to1PhaseSupported"); + EXPECT_EQ(kv.value().value, "true"); + EXPECT_TRUE(kv.value().readonly); +} + +} // namespace diff --git a/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_user_config.cpp b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_user_config.cpp new file mode 100644 index 0000000000..7605acefd1 --- /dev/null +++ b/lib/everest/ocpp/tests/lib/ocpp/v16/v2config/test_user_config.cpp @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include +#include + +#include "configuration_stub.hpp" + +namespace { +using namespace ocpp::v16::stubs; + +TEST_P(Configuration, setChargepointInformation) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getChargeBoxSerialNumber(), "cp001"); + EXPECT_EQ(get()->getChargePointSerialNumber(), std::nullopt); + EXPECT_EQ(get()->getFirmwareVersion(), "0.1"); + + EXPECT_EQ(get()->getChargePointVendor(), "Pionix"); + EXPECT_EQ(get()->getChargePointModel(), "Yeti"); + + get()->setChargepointInformation("chargePointVendor", "chargePointModel", "chargePointSerialNumber", + "chargeBoxSerialNumber", "firmwareVersion"); + EXPECT_EQ(get()->getChargePointVendor(), "chargePointVendor"); + EXPECT_EQ(get()->getChargePointModel(), "chargePointModel"); + EXPECT_EQ(get()->getChargePointSerialNumber(), "chargePointSerialNumber"); + EXPECT_EQ(get()->getChargeBoxSerialNumber(), "chargeBoxSerialNumber"); + EXPECT_EQ(get()->getFirmwareVersion(), "firmwareVersion"); + + auto kv = get()->getChargeBoxSerialNumberKeyValue(); + EXPECT_EQ(kv.key, "ChargeBoxSerialNumber"); + EXPECT_EQ(kv.value, "chargeBoxSerialNumber"); + EXPECT_TRUE(kv.readonly); + + kv = get()->getFirmwareVersionKeyValue(); + EXPECT_EQ(kv.key, "FirmwareVersion"); + EXPECT_EQ(kv.value, "firmwareVersion"); + EXPECT_TRUE(kv.readonly); +} + +TEST_P(Configuration, setChargepointMeterInformation) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getMeterSerialNumber(), std::nullopt); + EXPECT_EQ(get()->getMeterType(), std::nullopt); + + get()->setChargepointMeterInformation("meterSerialNumber", "meterType"); + EXPECT_EQ(get()->getMeterSerialNumber(), "meterSerialNumber"); + EXPECT_EQ(get()->getMeterType(), "meterType"); +} + +TEST_P(Configuration, setChargepointModemInformation) { + ASSERT_NE(get(), nullptr); + // initial values are from the JSON unit test config files + EXPECT_EQ(get()->getICCID(), std::nullopt); + EXPECT_EQ(get()->getIMSI(), std::nullopt); + + get()->setChargepointModemInformation("ICCID", "IMSI"); + EXPECT_EQ(get()->getICCID(), "ICCID"); + EXPECT_EQ(get()->getIMSI(), "IMSI"); +} + +} // namespace