diff --git a/doc/common/california_pricing_requirements.md b/doc/common/california_pricing_requirements.md index c3416f8ae..05ff827d2 100644 --- a/doc/common/california_pricing_requirements.md +++ b/doc/common/california_pricing_requirements.md @@ -11,11 +11,11 @@ information. ### User-specific price / SetUserPrice -The User-specific price is used for display purposes only and can be sent as soon as the user identifies itself with an +The User-specific price is used for display purposes only and can be sent as soon as the user identifies itself with an id token. It should not be used to calculate prices. -Internally, the messages in the DataTransfer json (for 1.6) is converted to a `DisplayMessage`, defined in -`common/types.hpp`. In case of multi language messages, they are all added to the DisplayMessage vector. -If the message is sent when a transaction has already started, the session id will be included in the display message and the `IdentifierType` will be set to `SessionId`. If it has not started yet, the id token is sent with +Internally, the messages in the DataTransfer json (for 1.6) is converted to a `SessionCostMessage`, defined in +`common/types.hpp`. In case of multi language messages, they are all added to the SessionCostMessage vector. +If the message is sent when a transaction has already started, the session id will be included in the session cost message and the `IdentifierType` will be set to `SessionId`. If it has not started yet, the id token is sent with `IdentifierType` set to `IdToken`. ### Running cost and Final / total cost @@ -53,11 +53,11 @@ the DataTransfer message is converted to internally used structs as described ab For California Pricing to work, the following callbacks must be enabled: - `session_cost_callback`, used for running cost and final cost -- `set_display_message_callback`, used to show a user specific price +- `session_cost_message_callback`, used to show a user specific price ## OCPP 2.0.1 -OCPP 2.0.1 uses different mechanisms to send pricing information. The messages are converted to internally used structs as descripbed above. For California Pricing Requirements to work, DisplayMessage and TariffAndCost must be implemented as well. +OCPP 2.0.1 uses different mechanisms to send pricing information. The messages are converted to internally used structs as described above. For California Pricing Requirements to work, TariffAndCost must be implemented as well. ### Device Model Variables @@ -82,17 +82,17 @@ OCPP 2.0.1 uses different mechanisms to send pricing information. The messages a > **_NOTE:_** Tariff and cost can be enabled separately. To be able to use all functionality, it is recommended to enable both. If cost is enabled and tariff is not enabled, the total cost message will not contain the personal message (`set_running_cost_callback`). -If tariff is enabled and cost is not enabled, the total cost message will only be a DisplayMessage -(`set_display_message_callback`) containing the personal message(s). +If tariff is enabled and cost is not enabled, the total cost message will only be a SessionCostMessage +(`session_cost_message_callback`) containing the personal message(s). ### Callbacks For California Pricing to work, the following callbacks must be enabled: - `set_running_cost_callback` -- `set_display_message_callback` +- `session_cost_message_callback` -For the tariff information (the personal messages), the `set_display_message_callback` is used. The same callback is also used for the SetDisplayMessageRequest in OCPP. The latter does require an id, the former will not have an id. So when `GetDisplayMessageRequest` is called from the CSMS, the Tariff display messages (that do not have an id) should not be returned. They should also be removed as soon as the transaction has ended. +For the tariff information (the personal messages), the `session_cost_message_callback` is used. Driver specific tariffs / pricing information can be returned by the CSMS in the `AuthorizeResponse` message. In libocpp, the whole message is just forwared (pricing information is not extracted from it), because the pricing @@ -100,4 +100,4 @@ information is coupled to the authorize response. So when Tariff and Cost are en Cost information is also sent by the CSMS in the TransactionEventResponse. In that case, the pricing / cost information is extracted from the message and a RunningCost message is sent containing the current cost and extra messages -(optional). If only Tariff is enabled and there is a personal message in the TransationEventResponse, a DisplayMessage is sent. +(optional). If only Tariff is enabled and there is a personal message in the TransationEventResponse, a SessionCostMessage is sent. diff --git a/include/ocpp/common/types.hpp b/include/ocpp/common/types.hpp index 98eb96ac0..34aeaf1a3 100644 --- a/include/ocpp/common/types.hpp +++ b/include/ocpp/common/types.hpp @@ -371,6 +371,13 @@ enum class IdentifierType { TransactionId ///< \brief Identifier is the transaction id. }; +struct SessionCostMessage { + std::optional ocpp_transaction_id; + std::optional identifier_id; + std::optional identifier_type; + std::vector message; +}; + struct DisplayMessage { std::optional id; std::optional priority; diff --git a/include/ocpp/v16/charge_point.hpp b/include/ocpp/v16/charge_point.hpp index c12357807..810a0f292 100644 --- a/include/ocpp/v16/charge_point.hpp +++ b/include/ocpp/v16/charge_point.hpp @@ -573,6 +573,14 @@ class ChargePoint { const std::function& session_cost_callback); + /// \brief Registers a callback function for the session cost text message (California Pricing Requirements). The + /// callback is executed when the CSMS sends a DataTransfer.req(SetUserPrice). The callback should return a + /// DataTransferResponse with the response to the CSMS. + /// \param session_cost_message_callback The callback + /// \ingroup ocpp16_callbacks + void register_session_cost_message_callback( + const std::function& session_cost_message_callback); + /// \brief Register a callback function for display messages (used in California Pricing Requirements) /// \param set_display_message_callback The callback. /// \ingroup ocpp16_callbacks diff --git a/include/ocpp/v16/charge_point_impl.hpp b/include/ocpp/v16/charge_point_impl.hpp index fbbe1e4cf..9347786f0 100644 --- a/include/ocpp/v16/charge_point_impl.hpp +++ b/include/ocpp/v16/charge_point_impl.hpp @@ -200,6 +200,7 @@ class ChargePointImpl : ocpp::ChargingStationBase { session_cost_callback; std::function& display_message)> set_display_message_callback; + std::function session_cost_message_callback; /// \brief This function is called after a successful connection to the Websocket void connected_callback(); @@ -872,6 +873,8 @@ class ChargePointImpl : ocpp::ChargingStationBase { void register_session_cost_callback( const std::function& session_cost_callback); + void register_session_cost_message_callback( + const std::function& session_cost_message_callback); void register_set_display_message_callback( const std::function&)> set_display_message_callback); diff --git a/include/ocpp/v2/charge_point_callbacks.hpp b/include/ocpp/v2/charge_point_callbacks.hpp index 3f7ccda85..3a5425f22 100644 --- a/include/ocpp/v2/charge_point_callbacks.hpp +++ b/include/ocpp/v2/charge_point_callbacks.hpp @@ -161,6 +161,10 @@ struct Callbacks { std::optional currency_code)>> set_running_cost_callback; + /// \brief Callback function is called when a TransactionEventResponse message is from the CSMS is received that + /// contains tariff and cost information. + std::optional> session_cost_message_callback; + /// \brief Callback function is called when a reservation request is received from the CSMS std::optional> reserve_now_callback; /// \brief Callback function is called when a cancel reservation request is received from the CSMS diff --git a/include/ocpp/v2/functional_blocks/tariff_and_cost.hpp b/include/ocpp/v2/functional_blocks/tariff_and_cost.hpp index 6058598de..43d93e21e 100644 --- a/include/ocpp/v2/functional_blocks/tariff_and_cost.hpp +++ b/include/ocpp/v2/functional_blocks/tariff_and_cost.hpp @@ -16,6 +16,7 @@ struct CostUpdatedRequest; typedef std::function currency_code)> SetRunningCostCallback; +typedef std::function SessionCostMessageCallback; class TariffAndCostInterface : public MessageHandlerInterface { public: @@ -35,7 +36,7 @@ class TariffAndCostInterface : public MessageHandlerInterface { class TariffAndCost : public TariffAndCostInterface { public: TariffAndCost(const FunctionalBlockContext& functional_block_context, MeterValuesInterface& meter_values, - std::optional& set_display_message_callback, + std::optional& session_cost_message_callback, std::optional& set_running_cost_callback, boost::asio::io_service& io_service); void handle_message(const ocpp::EnhancedMessage& message) override; @@ -47,7 +48,7 @@ class TariffAndCost : public TariffAndCostInterface { private: // Members const FunctionalBlockContext& context; MeterValuesInterface& meter_values; - std::optional set_display_message_callback; + std::optional session_cost_message_callback; std::optional set_running_cost_callback; boost::asio::io_service& io_service; diff --git a/lib/ocpp/v16/charge_point.cpp b/lib/ocpp/v16/charge_point.cpp index 6c7170a5a..0b1731a47 100644 --- a/lib/ocpp/v16/charge_point.cpp +++ b/lib/ocpp/v16/charge_point.cpp @@ -338,6 +338,11 @@ void ChargePoint::register_session_cost_callback( this->charge_point->register_session_cost_callback(session_cost_callback); } +void ChargePoint::register_session_cost_message_callback( + const std::function& session_cost_message_callback) { + this->charge_point->register_session_cost_message_callback(session_cost_message_callback); +} + void ChargePoint::register_set_display_message_callback( const std::function&)> set_display_message_callback) { this->charge_point->register_set_display_message_callback(set_display_message_callback); diff --git a/lib/ocpp/v16/charge_point_impl.cpp b/lib/ocpp/v16/charge_point_impl.cpp index 5f1b792f6..552ff9e25 100644 --- a/lib/ocpp/v16/charge_point_impl.cpp +++ b/lib/ocpp/v16/charge_point_impl.cpp @@ -3101,7 +3101,7 @@ DataTransferResponse ChargePointImpl::handle_set_user_price(const std::optional< return response; } - if (set_display_message_callback == nullptr) { + if (session_cost_message_callback == nullptr) { EVLOG_error << "Received data transfer for set user price, but no callback is registered."; return response; } @@ -3124,9 +3124,7 @@ DataTransferResponse ChargePointImpl::handle_set_user_price(const std::optional< return response; } - // Find transaction with given id tag - std::vector messages; - DisplayMessage message; + SessionCostMessage session_cost_message; const auto t = this->transaction_handler->get_transaction_from_id_tag(id_token.value()); std::string identifier_id; IdentifierType identifier_type; @@ -3137,33 +3135,34 @@ DataTransferResponse ChargePointImpl::handle_set_user_price(const std::optional< } else { identifier_id = t->get_session_id(); identifier_type = IdentifierType::SessionId; + const std::optional transaction_id = t->get_transaction_id(); + if (transaction_id != std::nullopt) { + session_cost_message.ocpp_transaction_id = std::to_string(transaction_id.value()); + } } - message.identifier_id = identifier_id; - message.identifier_type = identifier_type; + session_cost_message.identifier_id = identifier_id; + session_cost_message.identifier_type = identifier_type; if (data.contains("priceText")) { - message.message.message = data.at("priceText"); + DisplayMessageContent m; + m.message = data.at("priceText"); if (this->configuration->getLanguage().has_value()) { - message.message.language = this->configuration->getLanguage().value(); + m.language = this->configuration->getLanguage().value(); } + session_cost_message.message.push_back(m); } - - messages.push_back(message); - if (this->configuration->getCustomMultiLanguageMessagesEnabled() && data.contains("priceTextExtra") && data.at("priceTextExtra").is_array()) { for (const json& j : data.at("priceTextExtra")) { - DisplayMessage display_message; - display_message.identifier_id = identifier_id; - display_message.identifier_type = identifier_type; - display_message.message = j; + DisplayMessageContent message; + message = j; - messages.push_back(display_message); + session_cost_message.message.push_back(message); } } - response = this->set_display_message_callback(messages); + response = this->session_cost_message_callback(session_cost_message); return response; } @@ -4597,6 +4596,11 @@ void ChargePointImpl::register_session_cost_callback( this->session_cost_callback = session_cost_callback; } +void ChargePointImpl::register_session_cost_message_callback( + const std::function& session_cost_message_callback) { + this->session_cost_message_callback = session_cost_message_callback; +} + void ChargePointImpl::register_set_display_message_callback( const std::function& display_message)> set_display_message_callback) { diff --git a/lib/ocpp/v2/charge_point.cpp b/lib/ocpp/v2/charge_point.cpp index 48decdf61..2c8514091 100644 --- a/lib/ocpp/v2/charge_point.cpp +++ b/lib/ocpp/v2/charge_point.cpp @@ -546,7 +546,7 @@ void ChargePoint::initialize(const std::map& evse_connector_st this->callbacks.set_charging_profiles_callback); this->tariff_and_cost = std::make_unique( - *functional_block_context, *this->meter_values, this->callbacks.set_display_message_callback, + *functional_block_context, *this->meter_values, this->callbacks.session_cost_message_callback, this->callbacks.set_running_cost_callback, this->io_service); this->firmware_update = std::make_unique( diff --git a/lib/ocpp/v2/functional_blocks/tariff_and_cost.cpp b/lib/ocpp/v2/functional_blocks/tariff_and_cost.cpp index a8ee53970..872bb9bcf 100644 --- a/lib/ocpp/v2/functional_blocks/tariff_and_cost.cpp +++ b/lib/ocpp/v2/functional_blocks/tariff_and_cost.cpp @@ -15,12 +15,12 @@ const auto DEFAULT_PRICE_NUMBER_OF_DECIMALS = 3; namespace ocpp::v2 { TariffAndCost::TariffAndCost(const FunctionalBlockContext& functional_block_context, MeterValuesInterface& meter_values, - std::optional& set_display_message_callback, + std::optional& session_cost_message_callback, std::optional& set_running_cost_callback, boost::asio::io_service& io_service) : context(functional_block_context), meter_values(meter_values), - set_display_message_callback(set_display_message_callback), + session_cost_message_callback(session_cost_message_callback), set_running_cost_callback(set_running_cost_callback), io_service(io_service) { } @@ -50,14 +50,15 @@ void TariffAndCost::handle_cost_and_tariff(const TransactionEventResponse& respo cost_messages.push_back(message); // If cost is enabled, the message will be sent to the running cost callback. But if it is not enabled, the - // tariff message will be sent using the display message callback. - if (!cost_enabled and this->set_display_message_callback.has_value() and - this->set_display_message_callback != nullptr) { - DisplayMessage display_message; - display_message.message = message; - display_message.identifier_id = original_message.transactionInfo.transactionId; - display_message.identifier_type = IdentifierType::TransactionId; - this->set_display_message_callback.value()({display_message}); + // tariff message will be sent using the session cost message callback. + if (!cost_enabled and this->session_cost_message_callback.has_value() and + this->session_cost_message_callback != nullptr) { + SessionCostMessage session_cost_message; + session_cost_message.message = cost_messages; + session_cost_message.ocpp_transaction_id = original_message.transactionInfo.transactionId; + session_cost_message.identifier_id = original_message.transactionInfo.transactionId; + session_cost_message.identifier_type = IdentifierType::TransactionId; + this->session_cost_message_callback.value()({session_cost_message}); } }