From e42515c776e22d3dd4963b9a80abefb1f22aa1a6 Mon Sep 17 00:00:00 2001 From: pietfried Date: Thu, 18 Apr 2024 20:07:33 +0200 Subject: [PATCH 01/10] Refactored OCMF powermeter transaction handling: * Powermeter interface now contains proper user identification arguments (IS, IT, IF, IT, ID) to start the transaction * Refactored EvseManager and LemDCBM400600 modules to address the changed types * Added module config params for LemDCBM400600 : cable_id, tariff_id * Fixed test cases in LemDCBM400600 due to changes made Signed-off-by: pietfried --- modules/EvseManager/Charger.cpp | 20 +-- modules/EvseManager/utils.hpp | 39 +++++ modules/LemDCBM400600/LemDCBM400600.hpp | 2 + .../main/lem_dcbm_400600_controller.cpp | 15 +- .../main/lem_dcbm_400600_controller.hpp | 8 +- modules/LemDCBM400600/manifest.yaml | 8 + .../tests/lem_dcbm_test_utils/everest.py | 3 +- .../tests/test_lem_dcbm_400600_controller.cpp | 7 +- .../tests/test_lem_dcbm_400_600_e2e.py | 46 +++--- .../tests/test_lem_dcbm_400_600_sil.py | 14 +- types/powermeter.yaml | 153 ++++++++++++++++-- 11 files changed, 248 insertions(+), 67 deletions(-) create mode 100644 modules/EvseManager/utils.hpp diff --git a/modules/EvseManager/Charger.cpp b/modules/EvseManager/Charger.cpp index 7b7d6250c..b84b9e85a 100644 --- a/modules/EvseManager/Charger.cpp +++ b/modules/EvseManager/Charger.cpp @@ -9,25 +9,18 @@ */ #include "Charger.hpp" - -#include -#include -#include #include #include #include #include #include +#include #include #include "everest/logging.hpp" #include "scoped_lock_timeout.hpp" -std::string generate_session_uuid() { - return boost::uuids::to_string(boost::uuids::random_generator()()); -} - namespace module { Charger::Charger(const std::unique_ptr& bsp, const std::unique_ptr& error_handling, @@ -1060,7 +1053,7 @@ bool Charger::cancel_transaction(const types::evse_manager::StopTransactionReque void Charger::start_session(bool authfirst) { shared_context.session_active = true; shared_context.authorized = false; - shared_context.session_uuid = generate_session_uuid(); + shared_context.session_uuid = utils::generate_session_uuid(); std::optional provided_id_token; if (authfirst) { shared_context.last_start_session_reason = types::evse_manager::StartSessionReason::Authorized; @@ -1083,7 +1076,14 @@ bool Charger::start_transaction() { shared_context.transaction_active = true; const types::powermeter::TransactionReq req{ - evse_id, shared_context.session_uuid, shared_context.id_token.id_token.value, 0, 0, ""}; + evse_id, + shared_context.session_uuid, + true, + {}, + utils::convert_to_ocmf_identification_type(shared_context.id_token.id_token.type), + std::nullopt, + shared_context.id_token.id_token.value, + std::nullopt}; for (const auto& meter : r_powermeter_billing) { const auto response = meter->call_start_transaction(req); // If we want to start the session but the meter fail, we stop the charging since diff --git a/modules/EvseManager/utils.hpp b/modules/EvseManager/utils.hpp new file mode 100644 index 000000000..576852ab5 --- /dev/null +++ b/modules/EvseManager/utils.hpp @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include +#include +#include +#include + +namespace module { +namespace utils { + +std::string generate_session_uuid() { + return boost::uuids::to_string(boost::uuids::random_generator()()); +} + +types::powermeter::OCMFIdentificationType +convert_to_ocmf_identification_type(const types::authorization::IdTokenType& id_token_type) { + switch (id_token_type) { + case types::authorization::IdTokenType::Central: + return types::powermeter::OCMFIdentificationType::CENTRAL; + case types::authorization::IdTokenType::eMAID: + return types::powermeter::OCMFIdentificationType::EMAID; + case types::authorization::IdTokenType::ISO14443: + return types::powermeter::OCMFIdentificationType::ISO14443; + case types::authorization::IdTokenType::ISO15693: + return types::powermeter::OCMFIdentificationType::ISO15693; + case types::authorization::IdTokenType::KeyCode: + return types::powermeter::OCMFIdentificationType::KEY_CODE; + case types::authorization::IdTokenType::Local: + return types::powermeter::OCMFIdentificationType::LOCAL; + case types::authorization::IdTokenType::NoAuthorization: + return types::powermeter::OCMFIdentificationType::NONE; + default: + return types::powermeter::OCMFIdentificationType::UNDEFINED; + } +} + +} // namespace utils +} // namespace module diff --git a/modules/LemDCBM400600/LemDCBM400600.hpp b/modules/LemDCBM400600/LemDCBM400600.hpp index 3faf10bb4..d94655265 100644 --- a/modules/LemDCBM400600/LemDCBM400600.hpp +++ b/modules/LemDCBM400600/LemDCBM400600.hpp @@ -31,6 +31,8 @@ struct Conf { int resilience_initial_connection_retry_delay; int resilience_transaction_request_retries; int resilience_transaction_request_retry_delay; + int cable_id; + int tariff_id; }; class LemDCBM400600 : public Everest::ModuleBase { diff --git a/modules/LemDCBM400600/main/lem_dcbm_400600_controller.cpp b/modules/LemDCBM400600/main/lem_dcbm_400600_controller.cpp index 8a4b04c57..7c2c7932e 100644 --- a/modules/LemDCBM400600/main/lem_dcbm_400600_controller.cpp +++ b/modules/LemDCBM400600/main/lem_dcbm_400600_controller.cpp @@ -63,7 +63,8 @@ void LemDCBM400600Controller::request_device_to_start_transaction(const types::p this->time_sync_helper->sync(*this->http_client); auto response = this->http_client->post( - "/v1/legal", module::main::LemDCBM400600Controller::transaction_start_request_to_dcbm_payload(value)); + "/v1/legal", module::main::LemDCBM400600Controller::transaction_start_request_to_dcbm_payload( + value, this->config.cable_id, this->config.tariff_id)); if (response.status_code != 201) { throw UnexpectedDCBMResponseCode("/v1/legal", 201, response); } @@ -175,10 +176,14 @@ void LemDCBM400600Controller::convert_livemeasure_to_powermeter(const std::strin powermeter.power_W.emplace(types::units::Power{data.at("power")}); } std::string -LemDCBM400600Controller::transaction_start_request_to_dcbm_payload(const types::powermeter::TransactionReq& request) { - return nlohmann::ordered_json{{"evseId", request.evse_id}, {"transactionId", request.transaction_id}, - {"clientId", request.client_id}, {"tariffId", request.tariff_id}, - {"cableId", request.cable_id}, {"userData", request.user_data}} +LemDCBM400600Controller::transaction_start_request_to_dcbm_payload(const types::powermeter::TransactionReq& request, + const int cable_id, const int tariff_id) { + return nlohmann::ordered_json{{"evseId", request.evse_id}, + {"transactionId", request.transaction_id}, + {"clientId", request.transaction_id}, + {"tariffId", tariff_id}, + {"cableId", cable_id}, + {"userData", ""}} .dump(); } diff --git a/modules/LemDCBM400600/main/lem_dcbm_400600_controller.hpp b/modules/LemDCBM400600/main/lem_dcbm_400600_controller.hpp index c32e6798c..a7bbf4e8c 100644 --- a/modules/LemDCBM400600/main/lem_dcbm_400600_controller.hpp +++ b/modules/LemDCBM400600/main/lem_dcbm_400600_controller.hpp @@ -26,6 +26,11 @@ class LemDCBM400600Controller { const int transaction_number_of_http_retries; // wait time before each retry for transaction start/stop requests const int transaction_retry_wait_in_milliseconds; + // The cable loss compensation level to use. This allows compensating the measurements of the DCBM with a + // resistance. + const int cable_id; + // Used for a unique transaction tariff designation + const int tariff_id; }; class DCBMUnexpectedResponseException : public std::exception { @@ -82,7 +87,8 @@ class LemDCBM400600Controller { void request_device_to_stop_transaction(const std::string& transaction_id); std::string fetch_ocmf_result(const std::string& transaction_id); void convert_livemeasure_to_powermeter(const std::string& livemeasure, types::powermeter::Powermeter& powermeter); - static std::string transaction_start_request_to_dcbm_payload(const types::powermeter::TransactionReq& request); + static std::string transaction_start_request_to_dcbm_payload(const types::powermeter::TransactionReq& request, + const int cable_id, const int tariff_id); static std::pair get_transaction_stop_time_bounds(); template diff --git a/modules/LemDCBM400600/manifest.yaml b/modules/LemDCBM400600/manifest.yaml index 662e708f9..699afbbf4 100644 --- a/modules/LemDCBM400600/manifest.yaml +++ b/modules/LemDCBM400600/manifest.yaml @@ -43,6 +43,14 @@ config: description: For the controller resilience, the delay in milliseconds before a retry attempt at a transaction start or stop request. type: integer default: 250 + cable_id: + description: The cable loss compensation level to use. This allows compensating the measurements of the DCBM with a resistance. + type: integer + default: 0 + tariff_id: + description: Used for a unique transaction tariff designation + type: integer + default: 0 provides: main: description: This is the main unit of the module diff --git a/modules/LemDCBM400600/tests/lem_dcbm_test_utils/everest.py b/modules/LemDCBM400600/tests/lem_dcbm_test_utils/everest.py index 3d991d054..013fdfd7e 100644 --- a/modules/LemDCBM400600/tests/lem_dcbm_test_utils/everest.py +++ b/modules/LemDCBM400600/tests/lem_dcbm_test_utils/everest.py @@ -28,9 +28,8 @@ class StartTransactionSuccessResponse(BaseModel, extra=Extra.forbid): transaction_min_stop_time: datetime -class StopTransactionSuccessResponse(BaseModel, extra=Extra.forbid): +class StopTransactionSuccessResponse(BaseModel, extra=Extra.allow): status: str = Field("OK", const=True, strict=True) - ocmf: str = Field(regex=r"^OCMF|.*|.*$", strict=True) class LemDCBMStandaloneEverestInstance(contextlib.ContextDecorator): diff --git a/modules/LemDCBM400600/tests/test_lem_dcbm_400600_controller.cpp b/modules/LemDCBM400600/tests/test_lem_dcbm_400600_controller.cpp index 9ce9291bb..578a3d8cb 100644 --- a/modules/LemDCBM400600/tests/test_lem_dcbm_400600_controller.cpp +++ b/modules/LemDCBM400600/tests/test_lem_dcbm_400600_controller.cpp @@ -48,10 +48,11 @@ class LemDCBM400600ControllerTest : public ::testing::Test { })"}; const types::powermeter::TransactionReq transaction_request{ - "mock_evse_id", "mock_transaction_id", "mock_client_id", 42, 43, "mock_user_data"}; + "mock_evse_id", "mock_transaction_id", true, {}, types::powermeter::OCMFIdentificationType::ISO14443, + std::nullopt, std::nullopt, std::nullopt}; const std::string expected_start_transaction_request_body{ - R"({"evseId":"mock_evse_id","transactionId":"mock_transaction_id","clientId":"mock_client_id","tariffId":42,"cableId":43,"userData":"mock_user_data"})"}; + R"({"evseId":"mock_evse_id","transactionId":"mock_transaction_id","clientId":"mock_transaction_id","tariffId":0,"cableId":0,"userData":""})"}; const std::string put_legal_response = R"({ "paginationCounter": 6, @@ -85,7 +86,7 @@ class LemDCBM400600ControllerTest : public ::testing::Test { "publicKey": "A80F10D968E1122F8820F288B23C4E1C0DA912F35B48481274ADFEFE66D7E87E130C7CF2B8047C45CF105041C8C3A57DD242782F755C9443F42DABA9404A67BF" })"; - const LemDCBM400600Controller::Conf controller_config{0, 0, 1, 0}; + const LemDCBM400600Controller::Conf controller_config{0, 0, 1, 0, 0, 0}; void SetUp() override { this->http_client = std::make_unique(); diff --git a/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_e2e.py b/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_e2e.py index eb7e00ec9..cd0a95925 100644 --- a/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_e2e.py +++ b/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_e2e.py @@ -93,7 +93,8 @@ async def test_lem_dcbm_e2e_powermeter_does_regular_publish(everest_test_instanc @pytest.mark.asyncio async def test_lem_dcbm_e2e_powermeter_meterid_correct(everest_test_instance): power_meter: Powermeter = await everest_test_instance.probe_module.poll_next_powermeter(1.25) - assert re.match(r"^\d+$", power_meter.meter_id), f"got unexpected meter_id {power_meter.meter_id}" + assert re.match( + r"^\d+$", power_meter.meter_id), f"got unexpected meter_id {power_meter.meter_id}" @pytest.mark.asyncio @@ -104,15 +105,13 @@ async def test_lem_dcbm_e2e_start_stop_transaction(everest_test_instance, dcbm): {"value": { "evse_id": "mock_evse_id", "transaction_id": "e2e_test_transaction", - "client_id": "mock_client_id", - "tariff_id": 1, - "cable_id": 0, - "user_data": "mock_user_data" + "identification_status": True, + "identification_flags": [] }}) parsed_start_result = StartTransactionSuccessResponse(**start_result) assert 48 * 60 - 3.1 < (( - parsed_start_result.transaction_max_stop_time - parsed_start_result.transaction_min_stop_time).total_seconds() / 60) <= 48 * 60 - 2.9 + parsed_start_result.transaction_max_stop_time - parsed_start_result.transaction_min_stop_time).total_seconds() / 60) <= 48 * 60 - 2.9 logging.info("started transaction 'e2e_test_transaction'") @@ -140,14 +139,13 @@ async def test_lem_dcbm_e2e_time_sync(everest_test_instance, dcbm): # start transaction to enforce early sync; tidied up by fixture assert everest_test_instance.probe_module.call_powermeter_command('start_transaction', - {"value": { - "evse_id": "mock_evse_id", - "transaction_id": "e2e_test_transaction", - "client_id": "mock_client_id", - "tariff_id": 1, - "cable_id": 0, - "user_data": "mock_user_data" - }})["status"] == "OK" + {"value": { + "evse_id": "mock_evse_id", + "transaction_id": "e2e_test_transaction", + "identification_status": True, + "identification_flags": [], + "identification_type": "ISO14443" + }})["status"] == "OK" dcbm.set_time(datetime.now() - timedelta(days=365)) @@ -164,14 +162,13 @@ async def test_lem_dcbm_e2e_ntp_setup(everest_test_instance_ntp_configured, dcbm # start transaction to enforce early sync; tidied up by fixture assert everest_test_instance_ntp_configured.probe_module.call_powermeter_command('start_transaction', - {"value": { - "evse_id": "mock_evse_id", - "transaction_id": "e2e_test_transaction", - "client_id": "mock_client_id", - "tariff_id": 1, - "cable_id": 0, - "user_data": "mock_user_data" - }})["status"] == "OK" + {"value": { + "evse_id": "mock_evse_id", + "transaction_id": "e2e_test_transaction", + "identification_status": True, + "identification_flags": [], + "identification_type": "ISO14443" + }})["status"] == "OK" async def check(): while not (ntp_settings := dcbm.get_ntp_settings()).ntpActivated: @@ -187,10 +184,11 @@ async def check(): { "ipAddress": "test_ntp_2", "port": 125 - }] + }] @pytest.mark.asyncio async def test_lem_dcbm_2e_get_powermeter_tls(everest_test_instance_tls): power_meter: Powermeter = await everest_test_instance_tls.probe_module.poll_next_powermeter(1.25) - assert re.match(r"^\d+$", power_meter.meter_id), f"got unexpected meter_id {power_meter.meter_id}" + assert re.match( + r"^\d+$", power_meter.meter_id), f"got unexpected meter_id {power_meter.meter_id}" diff --git a/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_sil.py b/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_sil.py index 689e7e011..dd99f6b4c 100644 --- a/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_sil.py +++ b/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_sil.py @@ -28,17 +28,15 @@ def test_start_transaction(everest_test_instance): res = everest_test_instance.probe_module.call_powermeter_command('start_transaction', {"value": { "evse_id": "mock_evse_id", - "transaction_id": "mock_transaction_id", - "client_id": "mock_client_id", - "tariff_id": 42, - "cable_id": 43, - "user_data": "mock_user_data" - } - }) + "transaction_id": "e2e_test_transaction", + "identification_status": True, + "identification_flags": [], + "identification_type": "ISO14443" + }}) parsed_start_result = StartTransactionSuccessResponse(**res) assert 48 * 60 - 3.1 < (( - parsed_start_result.transaction_max_stop_time - parsed_start_result.transaction_min_stop_time).total_seconds() / 60) <= 48 * 60 - 2.9 + parsed_start_result.transaction_max_stop_time - parsed_start_result.transaction_min_stop_time).total_seconds() / 60) <= 48 * 60 - 2.9 def test_stop_transaction(everest_test_instance): diff --git a/types/powermeter.yaml b/types/powermeter.yaml index 138f246e9..9e1659f62 100644 --- a/types/powermeter.yaml +++ b/types/powermeter.yaml @@ -7,10 +7,9 @@ types: required: - evse_id - transaction_id - - client_id - - tariff_id - - cable_id - - user_data + - identification_status + - identification_type + - identification_flags properties: evse_id: description: Id of this EVSE @@ -18,18 +17,42 @@ types: transaction_id: description: OCPP transaction UUID type: string - client_id: - description: User identification means, e.g. RFID card ID + identification_status: + description: >- + OCMF Identification Status (IS): General status for user assignment + true: user successfully assigned + false: user not assigned + type: boolean + identification_level: + description: >- + OCMF Identification Level (IL): Encoded overall status of the user assignment + type: string + $ref: /powermeter#/OCMFIdentificationLevel + identification_flags: + description: >- + OCMF Identification Flags (IF): Detailed statements about the user assignment, represented by one or more identifiers + type: array + items: + minimum: 0 + maximum: 4 + type: string + $ref: /powermeter#/OCMFIdentificationFlags + identification_type: + description: >- + OCMF Identification Type (IT): Type of identification data + type: string + $ref: /powermeter#/OCMFIdentificationType + identification_data: + description: >- + OCMF Identification Data (ID): The actual identification data e.g. a hex-coded UID according to ISO 14443. type: string - tariff_id: - description: Tariff ID - type: integer - cable_id: - description: Cable ID - type: integer - user_data: - description: User data + tariff_text: + description: >- + A textual description used to identify a unique tariff. + This field is intended for the tariff designation in "Direct Payment" use case. type: string + minLength: 0 + maxLength: 250 TransactionRequestStatus: description: Status of a transaction start or stop request. type: string @@ -81,6 +104,108 @@ types: error: description: If status is not OK, a verbose error message. type: string + OCMFIdentificationLevel: + description: >- + NONE: There is no user assignment. The other data for user assignment have no significance. + HEARSAY: The assignment is unsecured; e.g. by reading an RFID UID. + TRUSTED: The mapping can be trusted to some extent, but there is no absolute reliability. Example: Authorization by backend. + VERIFIED: The assignment has been verified by the signature component and special measures. + CERTIFIED: The assignment was verified by the signature component using a cryptographic signature that certifies the assignment. + SECURE: The mapping was established by a secure feature (e.g. secure RFID card, ISO 15118 with plug and charge, etc.). + MISMATCH: Error; UIDs do not match. + INVALID: Error; certificate not correct (check negative). + OUTDATED: Error; referenced trust certificate expired. + UNKNOWN: Certificate could not be successfully verified (no matching trust certificate found). + type: string + enum: + - NONE + - HEARSAY + - TRUSTED + - VERIFIED + - CERTIFIED + - SECURE + - MISMATCH + - INVALID + - OUTDATED + - UNKNOWN + OCMFIdentificationFlags: + description: >- + RFID_NONE: No assignment via RFID + RFID_PLAIN: Assignment via external RFID card reader + RFID_RELATED: Assignment via protected RFID card reader + RFID_PSK: A previously known shared key (pre-shared key) was used, e.g. with a secured RFID card. + OCPP_NONE: No user assignment by OCPP + OCPP_RS: Assignment by OCPP RemoteStart method + OCPP_AUTH: Assignment by OCPP Authorize method + OCPP_RS_TLS: Assignment by OCPP RemoteStart method, obtained via a secured TLS connection. + OCPP_AUTH_TLS: Assignment by OCPP Authorize method, obtained via a secured TLS connection. + OCPP_CACHE: Assignment by authorization cache of OCPP + OCPP_WHITELIST: Assignment by whitelist from OCPP + OCPP_CERTIFIED: A certificate of the backend was used which certifies the user mapping. + ISO15118_NONE: no user assignment by ISO 15118 + ISO15118_PNC: Plug & Charge was used + PLMN_NONE: no user assignment + PLMN_RING: call + PLMN_SMS: short message + type: string + enum: + - RFID_NONE + - RFID_PLAIN + - RFID_RELATED + - RFID_PSK + - OCPP_NONE + - OCPP_RS + - OCPP_AUTH + - OCPP_RS_TLS + - OCPP_AUTH_TLS + - OCPP_CACHE + - OCPP_WHITELIST + - OCPP_CERTIFIED + - ISO15118_NONE + - ISO15118_PNC + - PLMN_NONE + - PLMN_RING + - PLMN_SMS + OCMFIdentificationType: + description: >- + NONE: No assignment available + DENIED: Assignment currently not available (due to two-factor authorization) + UNDEFINED: Type not specified + ISO14443: UID of an RFID card according to ISO 14443. Represented as 4 or 7 bytes in hexadecimal notation. + ISO15693: UID of an RFID card according to ISO 15693. Represented as 8 bytes in hexadecimal notation. + EMAID: Electro-Mobility-Account-ID according to ISO/IEC 15118 (string with length 14 or 15) + EVCCID: ID of an electric vehicle according to ISO/IEC 15118 (maximum length 6 characters) + EVCOID: EV Contract ID according to DIN 91286. + ISO7812: Identification card format according to ISO/IEC 7812 (credit and bank cards, etc.) + CARD_TXN_NR: Card transaction number (CardTxNbr) for a payment with credit or bank card used in a terminal at the charging point. + CENTRAL: Centrally generated ID. No exact format defined, can be e.g. a UUID. (OCPP 2.0) + CENTRAL_1: Centrally generated ID, e.g. by start via SMS. No exact format defined. (until OCPP 1.6) + CENTRAL_2: Centrally generated ID, e.g. by operator start. No exact format defined. (until OCPP 1.6) + LOCAL: Locally generated ID. No exact format defined, might be e.g. a UUID. (OCPP 2.0) + LOCAL_1: Locally generated ID, e.g. ID generated internally by the charge point. No exact format defined. (until OCPP 1.6) + LOCAL_2: Locally generated ID, for other cases. No exact format defined. (until OCPP 1.6) + PHONE_NUMBER: International phone number with leading "+". + KEY_CODE: User-related private key code. No exact format defined. + type: string + enum: + - NONE + - DENIED + - UNDEFINED + - ISO14443 + - ISO15693 + - EMAID + - EVCCID + - EVCOID + - ISO7812 + - CARD_TXN_NR + - CENTRAL + - CENTRAL_1 + - CENTRAL_2 + - LOCAL + - LOCAL_1 + - LOCAL_2 + - PHONE_NUMBER + - KEY_CODE Powermeter: description: Measured dataset (AC or DC) type: object From c669ff69660bef4e53ecb06e2a7e321149d58b9d Mon Sep 17 00:00:00 2001 From: pietfried Date: Fri, 19 Apr 2024 13:18:28 +0200 Subject: [PATCH 02/10] addressed requested changes Signed-off-by: pietfried --- modules/EvseManager/Charger.cpp | 21 +++++++++++---------- modules/EvseManager/utils.hpp | 3 ++- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/modules/EvseManager/Charger.cpp b/modules/EvseManager/Charger.cpp index b84b9e85a..576542de0 100644 --- a/modules/EvseManager/Charger.cpp +++ b/modules/EvseManager/Charger.cpp @@ -14,12 +14,12 @@ #include #include #include -#include #include #include "everest/logging.hpp" #include "scoped_lock_timeout.hpp" +#include "utils.hpp" namespace module { @@ -1075,15 +1075,16 @@ bool Charger::start_transaction() { shared_context.stop_transaction_id_token.reset(); shared_context.transaction_active = true; - const types::powermeter::TransactionReq req{ - evse_id, - shared_context.session_uuid, - true, - {}, - utils::convert_to_ocmf_identification_type(shared_context.id_token.id_token.type), - std::nullopt, - shared_context.id_token.id_token.value, - std::nullopt}; + types::powermeter::TransactionReq req; + req.evse_id = evse_id; + req.transaction_id = shared_context.session_uuid; + req.identification_status = true; // currently user is always assigned + req.identification_flags = {}; // TODO: Collect IF. Not all known in EVerest + req.identification_type = utils::convert_to_ocmf_identification_type(shared_context.id_token.id_token.type); + req.identification_level = std::nullopt; // TODO: Not yet known to EVerest + req.identification_data = shared_context.id_token.id_token.value; + req.tariff_text = std::nullopt; // TODO: Not yet known to EVerest + for (const auto& meter : r_powermeter_billing) { const auto response = meter->call_start_transaction(req); // If we want to start the session but the meter fail, we stop the charging since diff --git a/modules/EvseManager/utils.hpp b/modules/EvseManager/utils.hpp index 576852ab5..223bff239 100644 --- a/modules/EvseManager/utils.hpp +++ b/modules/EvseManager/utils.hpp @@ -30,9 +30,10 @@ convert_to_ocmf_identification_type(const types::authorization::IdTokenType& id_ return types::powermeter::OCMFIdentificationType::LOCAL; case types::authorization::IdTokenType::NoAuthorization: return types::powermeter::OCMFIdentificationType::NONE; - default: + case types::authorization::IdTokenType::MacAddress: return types::powermeter::OCMFIdentificationType::UNDEFINED; } + return types::powermeter::OCMFIdentificationType::UNDEFINED; } } // namespace utils From 097a5710a8f38640260bdd81eb43052ef281070d Mon Sep 17 00:00:00 2001 From: pietfried Date: Fri, 19 Apr 2024 13:36:49 +0200 Subject: [PATCH 03/10] fix RsIskraMeter tests Signed-off-by: pietfried --- modules/RsIskraMeter/src/main.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/modules/RsIskraMeter/src/main.rs b/modules/RsIskraMeter/src/main.rs index b01e4dee3..3f4e3b201 100644 --- a/modules/RsIskraMeter/src/main.rs +++ b/modules/RsIskraMeter/src/main.rs @@ -1179,12 +1179,11 @@ mod tests { let ready_state = make_ready_state(mock); let _unused = ready_state.start_transaction(TransactionReq { - cable_id: 1, - client_id: String::new(), evse_id: String::new(), - tariff_id: 1, transaction_id: String::new(), - user_data: String::new(), + identification_status: true, + identification_type: "ISO14443", + identification_flags: Vec::new() }); } } @@ -1277,12 +1276,11 @@ mod tests { let ready_state = make_ready_state(mock); let _unused = ready_state.start_transaction(TransactionReq { - cable_id: 1, - client_id: String::new(), evse_id: String::new(), - tariff_id: 1, transaction_id: String::new(), - user_data: String::new(), + identification_status: true, + identification_type: "ISO14443", + identification_flags: Vec::new() }); } } From 9b24fd7b242afa5079fa0c2b71f6e3195bcd1910 Mon Sep 17 00:00:00 2001 From: pietfried Date: Fri, 19 Apr 2024 13:51:15 +0200 Subject: [PATCH 04/10] fix identification_flags array keywords to minItems and maxItems Signed-off-by: pietfried --- types/powermeter.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/powermeter.yaml b/types/powermeter.yaml index 9e1659f62..47b7ef5d0 100644 --- a/types/powermeter.yaml +++ b/types/powermeter.yaml @@ -33,8 +33,8 @@ types: OCMF Identification Flags (IF): Detailed statements about the user assignment, represented by one or more identifiers type: array items: - minimum: 0 - maximum: 4 + minItems: 0 + maxItems: 4 type: string $ref: /powermeter#/OCMFIdentificationFlags identification_type: From bab55496d107ea4a6504b0dd39c1808c1ea31df3 Mon Sep 17 00:00:00 2001 From: pietfried Date: Mon, 22 Apr 2024 10:38:56 +0200 Subject: [PATCH 05/10] fix indentation of minItems and maxItems Signed-off-by: pietfried --- types/powermeter.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/powermeter.yaml b/types/powermeter.yaml index 47b7ef5d0..8f70306e3 100644 --- a/types/powermeter.yaml +++ b/types/powermeter.yaml @@ -33,10 +33,10 @@ types: OCMF Identification Flags (IF): Detailed statements about the user assignment, represented by one or more identifiers type: array items: - minItems: 0 - maxItems: 4 type: string $ref: /powermeter#/OCMFIdentificationFlags + minItems: 0 + maxItems: 4 identification_type: description: >- OCMF Identification Type (IT): Type of identification data From 9b76183979e04749d8e006a6f37a172a1ce98c9a Mon Sep 17 00:00:00 2001 From: pietfried Date: Mon, 22 Apr 2024 10:48:11 +0200 Subject: [PATCH 06/10] add to_string Signed-off-by: pietfried --- modules/RsIskraMeter/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/RsIskraMeter/src/main.rs b/modules/RsIskraMeter/src/main.rs index 3f4e3b201..bb2e205b1 100644 --- a/modules/RsIskraMeter/src/main.rs +++ b/modules/RsIskraMeter/src/main.rs @@ -1279,7 +1279,7 @@ mod tests { evse_id: String::new(), transaction_id: String::new(), identification_status: true, - identification_type: "ISO14443", + identification_type: "ISO14443".to_string(), identification_flags: Vec::new() }); } From a782858494b350d60405a5f17a0588808533ba6f Mon Sep 17 00:00:00 2001 From: pietfried Date: Mon, 22 Apr 2024 11:54:59 +0200 Subject: [PATCH 07/10] fix build Signed-off-by: pietfried --- modules/RsIskraMeter/src/main.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/RsIskraMeter/src/main.rs b/modules/RsIskraMeter/src/main.rs index bb2e205b1..0f7d61db3 100644 --- a/modules/RsIskraMeter/src/main.rs +++ b/modules/RsIskraMeter/src/main.rs @@ -1182,8 +1182,11 @@ mod tests { evse_id: String::new(), transaction_id: String::new(), identification_status: true, - identification_type: "ISO14443", - identification_flags: Vec::new() + identification_type: generated::types::powermeter::OCMFIdentificationType::ISO14443, + identification_flags: Vec::new(), + identification_data: Some(String::new()), + identification_level: Some(generated::types::powermeter::OCMFIdentificationLevel::NONE), + tariff_text: Some(String::new()) }); } } @@ -1279,8 +1282,11 @@ mod tests { evse_id: String::new(), transaction_id: String::new(), identification_status: true, - identification_type: "ISO14443".to_string(), - identification_flags: Vec::new() + identification_type: generated::types::powermeter::OCMFIdentificationType::ISO14443, + identification_flags: Vec::new(), + identification_data: Some(String::new()), + identification_level: Some(generated::types::powermeter::OCMFIdentificationLevel::NONE), + tariff_text: Some(String::new()) }); } } From 5d3e1bf2b8555c08e0d179396a2dd29a87f8f4f0 Mon Sep 17 00:00:00 2001 From: pietfried Date: Mon, 22 Apr 2024 13:58:02 +0200 Subject: [PATCH 08/10] Adressed comment to change type of identification_status bool to OCMFUserIdentificationStatus Signed-off-by: pietfried --- modules/EvseManager/Charger.cpp | 9 +++++---- .../tests/test_lem_dcbm_400600_controller.cpp | 10 ++++++++-- .../tests/test_lem_dcbm_400_600_e2e.py | 6 +++--- .../tests/test_lem_dcbm_400_600_sil.py | 2 +- modules/RsIskraMeter/src/main.rs | 4 ++-- types/powermeter.yaml | 14 +++++++++++--- 6 files changed, 30 insertions(+), 15 deletions(-) diff --git a/modules/EvseManager/Charger.cpp b/modules/EvseManager/Charger.cpp index 576542de0..7e76cae45 100644 --- a/modules/EvseManager/Charger.cpp +++ b/modules/EvseManager/Charger.cpp @@ -1078,12 +1078,13 @@ bool Charger::start_transaction() { types::powermeter::TransactionReq req; req.evse_id = evse_id; req.transaction_id = shared_context.session_uuid; - req.identification_status = true; // currently user is always assigned - req.identification_flags = {}; // TODO: Collect IF. Not all known in EVerest + req.identification_status = + types::powermeter::OCMFUserIdentificationStatus::ASSIGNED; // currently user is always assigned + req.identification_flags = {}; // TODO: Collect IF. Not all known in EVerest req.identification_type = utils::convert_to_ocmf_identification_type(shared_context.id_token.id_token.type); - req.identification_level = std::nullopt; // TODO: Not yet known to EVerest + req.identification_level = std::nullopt; // TODO: Not yet known to EVerest req.identification_data = shared_context.id_token.id_token.value; - req.tariff_text = std::nullopt; // TODO: Not yet known to EVerest + req.tariff_text = std::nullopt; // TODO: Not yet known to EVerest for (const auto& meter : r_powermeter_billing) { const auto response = meter->call_start_transaction(req); diff --git a/modules/LemDCBM400600/tests/test_lem_dcbm_400600_controller.cpp b/modules/LemDCBM400600/tests/test_lem_dcbm_400600_controller.cpp index 578a3d8cb..c5d50c634 100644 --- a/modules/LemDCBM400600/tests/test_lem_dcbm_400600_controller.cpp +++ b/modules/LemDCBM400600/tests/test_lem_dcbm_400600_controller.cpp @@ -48,8 +48,14 @@ class LemDCBM400600ControllerTest : public ::testing::Test { })"}; const types::powermeter::TransactionReq transaction_request{ - "mock_evse_id", "mock_transaction_id", true, {}, types::powermeter::OCMFIdentificationType::ISO14443, - std::nullopt, std::nullopt, std::nullopt}; + "mock_evse_id", + "mock_transaction_id", + types::powermeter::OCMFUserIdentificationStatus::ASSIGNED, + {}, + types::powermeter::OCMFIdentificationType::ISO14443, + std::nullopt, + std::nullopt, + std::nullopt}; const std::string expected_start_transaction_request_body{ R"({"evseId":"mock_evse_id","transactionId":"mock_transaction_id","clientId":"mock_transaction_id","tariffId":0,"cableId":0,"userData":""})"}; diff --git a/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_e2e.py b/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_e2e.py index cd0a95925..a245e3f67 100644 --- a/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_e2e.py +++ b/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_e2e.py @@ -105,7 +105,7 @@ async def test_lem_dcbm_e2e_start_stop_transaction(everest_test_instance, dcbm): {"value": { "evse_id": "mock_evse_id", "transaction_id": "e2e_test_transaction", - "identification_status": True, + "identification_status": "ASSIGNED", "identification_flags": [] }}) @@ -142,7 +142,7 @@ async def test_lem_dcbm_e2e_time_sync(everest_test_instance, dcbm): {"value": { "evse_id": "mock_evse_id", "transaction_id": "e2e_test_transaction", - "identification_status": True, + "identification_status": "ASSIGNED", "identification_flags": [], "identification_type": "ISO14443" }})["status"] == "OK" @@ -165,7 +165,7 @@ async def test_lem_dcbm_e2e_ntp_setup(everest_test_instance_ntp_configured, dcbm {"value": { "evse_id": "mock_evse_id", "transaction_id": "e2e_test_transaction", - "identification_status": True, + "identification_status": "ASSIGNED", "identification_flags": [], "identification_type": "ISO14443" }})["status"] == "OK" diff --git a/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_sil.py b/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_sil.py index dd99f6b4c..30114ba12 100644 --- a/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_sil.py +++ b/modules/LemDCBM400600/tests/test_lem_dcbm_400_600_sil.py @@ -29,7 +29,7 @@ def test_start_transaction(everest_test_instance): {"value": { "evse_id": "mock_evse_id", "transaction_id": "e2e_test_transaction", - "identification_status": True, + "identification_status": "ASSIGNED", "identification_flags": [], "identification_type": "ISO14443" }}) diff --git a/modules/RsIskraMeter/src/main.rs b/modules/RsIskraMeter/src/main.rs index 0f7d61db3..321faabaa 100644 --- a/modules/RsIskraMeter/src/main.rs +++ b/modules/RsIskraMeter/src/main.rs @@ -1181,7 +1181,7 @@ mod tests { let _unused = ready_state.start_transaction(TransactionReq { evse_id: String::new(), transaction_id: String::new(), - identification_status: true, + identification_status: generated::types::powermeter::OCMFUserIdentificationStatus::ASSIGNED, identification_type: generated::types::powermeter::OCMFIdentificationType::ISO14443, identification_flags: Vec::new(), identification_data: Some(String::new()), @@ -1281,7 +1281,7 @@ mod tests { let _unused = ready_state.start_transaction(TransactionReq { evse_id: String::new(), transaction_id: String::new(), - identification_status: true, + identification_status: generated::types::powermeter::OCMFUserIdentificationStatus::ASSIGNED, identification_type: generated::types::powermeter::OCMFIdentificationType::ISO14443, identification_flags: Vec::new(), identification_data: Some(String::new()), diff --git a/types/powermeter.yaml b/types/powermeter.yaml index 8f70306e3..5bafcc9b6 100644 --- a/types/powermeter.yaml +++ b/types/powermeter.yaml @@ -20,9 +20,8 @@ types: identification_status: description: >- OCMF Identification Status (IS): General status for user assignment - true: user successfully assigned - false: user not assigned - type: boolean + type: string + $ref: /powermeter#/OCMFUserIdentificationStatus identification_level: description: >- OCMF Identification Level (IL): Encoded overall status of the user assignment @@ -206,6 +205,15 @@ types: - LOCAL_2 - PHONE_NUMBER - KEY_CODE + OCMFUserIdentificationStatus: + description: >- + General status for user assignment + ASSIGNED: user successfully assigned + NOT_ASSIGNED: user not assigned + type: string + enum: + - ASSIGNED + - NOT_ASSIGNED Powermeter: description: Measured dataset (AC or DC) type: object From e328cbd07de00d4417571b649985fc76d2006908 Mon Sep 17 00:00:00 2001 From: pietfried Date: Mon, 22 Apr 2024 21:55:01 +0200 Subject: [PATCH 09/10] Added enum imports, changed optionals to None Signed-off-by: pietfried --- modules/RsIskraMeter/src/main.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/modules/RsIskraMeter/src/main.rs b/modules/RsIskraMeter/src/main.rs index 321faabaa..d92defc88 100644 --- a/modules/RsIskraMeter/src/main.rs +++ b/modules/RsIskraMeter/src/main.rs @@ -46,6 +46,7 @@ use everestrs::serde as everest_serde; use everestrs::serde_json as everest_serde_json; use generated::types::powermeter::{ Powermeter, TransactionRequestStatus, TransactionStartResponse, TransactionStopResponse, + OCMFUserIdentificationStatus, OCMFIdentificationType, }; use generated::types::serial_comm_hub_requests::{StatusCodeEnum, VectorUint16}; use generated::types::units::{Current, Energy, Frequency, Power, ReactivePower, Voltage}; @@ -1181,12 +1182,12 @@ mod tests { let _unused = ready_state.start_transaction(TransactionReq { evse_id: String::new(), transaction_id: String::new(), - identification_status: generated::types::powermeter::OCMFUserIdentificationStatus::ASSIGNED, - identification_type: generated::types::powermeter::OCMFIdentificationType::ISO14443, + identification_status: OCMFUserIdentificationStatus::ASSIGNED, + identification_type: OCMFIdentificationType::ISO14443, identification_flags: Vec::new(), - identification_data: Some(String::new()), - identification_level: Some(generated::types::powermeter::OCMFIdentificationLevel::NONE), - tariff_text: Some(String::new()) + identification_data: None, + identification_level: None, + tariff_text: None }); } } @@ -1281,12 +1282,12 @@ mod tests { let _unused = ready_state.start_transaction(TransactionReq { evse_id: String::new(), transaction_id: String::new(), - identification_status: generated::types::powermeter::OCMFUserIdentificationStatus::ASSIGNED, - identification_type: generated::types::powermeter::OCMFIdentificationType::ISO14443, + identification_status: OCMFUserIdentificationStatus::ASSIGNED, + identification_type: OCMFIdentificationType::ISO14443, identification_flags: Vec::new(), - identification_data: Some(String::new()), - identification_level: Some(generated::types::powermeter::OCMFIdentificationLevel::NONE), - tariff_text: Some(String::new()) + identification_data: None, + identification_level: None, + tariff_text: None }); } } From b8453b876f9443fd8d8c1d62c50dc3d2a0030344 Mon Sep 17 00:00:00 2001 From: pietfried Date: Thu, 2 May 2024 09:56:08 +0200 Subject: [PATCH 10/10] clang format Signed-off-by: pietfried --- modules/EvseManager/Charger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/EvseManager/Charger.cpp b/modules/EvseManager/Charger.cpp index 92933c1cc..ef4dc2191 100644 --- a/modules/EvseManager/Charger.cpp +++ b/modules/EvseManager/Charger.cpp @@ -1084,9 +1084,9 @@ bool Charger::start_transaction() { types::powermeter::OCMFUserIdentificationStatus::ASSIGNED; // currently user is always assigned req.identification_flags = {}; // TODO: Collect IF. Not all known in EVerest req.identification_type = utils::convert_to_ocmf_identification_type(shared_context.id_token.id_token.type); - req.identification_level = std::nullopt; // TODO: Not yet known to EVerest + req.identification_level = std::nullopt; // TODO: Not yet known to EVerest req.identification_data = shared_context.id_token.id_token.value; - req.tariff_text = std::nullopt; // TODO: Not yet known to EVerest + req.tariff_text = std::nullopt; // TODO: Not yet known to EVerest for (const auto& meter : r_powermeter_billing) { const auto response = meter->call_start_transaction(req);