Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Session cost add message #1007

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions doc/common/california_pricing_requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@

### 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

Check notice on line 14 in doc/common/california_pricing_requirements.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/common/california_pricing_requirements.md#L14

Expected: 0 or 2; Actual: 1
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
Expand Down Expand Up @@ -53,11 +53,11 @@
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

Expand All @@ -82,22 +82,22 @@

> **_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
information is coupled to the authorize response. So when Tariff and Cost are enabled, the `idTokenInfo` field must be read for pricing information.

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.
7 changes: 7 additions & 0 deletions include/ocpp/common/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,13 @@
TransactionId ///< \brief Identifier is the transaction id.
};

struct SessionCostMessage {
std::optional<std::string> ocpp_transaction_id;
std::optional<std::string> identifier_id;
std::optional<IdentifierType> identifier_type;
std::vector<DisplayMessageContent> message;

Check notice on line 378 in include/ocpp/common/types.hpp

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

include/ocpp/common/types.hpp#L378

struct member 'SessionCostMessage::message' is never used.
};

struct DisplayMessage {
std::optional<int32_t> id;
std::optional<v2::MessagePriorityEnum> priority;
Expand Down
8 changes: 8 additions & 0 deletions include/ocpp/v16/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,14 @@ class ChargePoint {
const std::function<DataTransferResponse(const RunningCost& session_cost, const uint32_t number_of_decimals)>&
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<DataTransferResponse(const SessionCostMessage& message)>& 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
Expand Down
3 changes: 3 additions & 0 deletions include/ocpp/v16/charge_point_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class ChargePointImpl : ocpp::ChargingStationBase {
session_cost_callback;
std::function<DataTransferResponse(const std::vector<DisplayMessage>& display_message)>
set_display_message_callback;
std::function<DataTransferResponse(const SessionCostMessage& message)> session_cost_message_callback;

/// \brief This function is called after a successful connection to the Websocket
void connected_callback();
Expand Down Expand Up @@ -872,6 +873,8 @@ class ChargePointImpl : ocpp::ChargingStationBase {
void register_session_cost_callback(
const std::function<DataTransferResponse(const RunningCost& running_cost, const uint32_t number_of_decimals)>&
session_cost_callback);
void register_session_cost_message_callback(
const std::function<DataTransferResponse(const SessionCostMessage& message)>& session_cost_message_callback);
void register_set_display_message_callback(
const std::function<DataTransferResponse(const std::vector<DisplayMessage>&)> set_display_message_callback);

Expand Down
4 changes: 4 additions & 0 deletions include/ocpp/v2/charge_point_callbacks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ struct Callbacks {
std::optional<std::string> 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<std::function<void(const SessionCostMessage& message)>> session_cost_message_callback;

/// \brief Callback function is called when a reservation request is received from the CSMS
std::optional<std::function<ReserveNowStatusEnum(const ReserveNowRequest& request)>> reserve_now_callback;
/// \brief Callback function is called when a cancel reservation request is received from the CSMS
Expand Down
5 changes: 3 additions & 2 deletions include/ocpp/v2/functional_blocks/tariff_and_cost.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct CostUpdatedRequest;
typedef std::function<void(const RunningCost& running_cost, const uint32_t number_of_decimals,
std::optional<std::string> currency_code)>
SetRunningCostCallback;
typedef std::function<void(const SessionCostMessage& message)> SessionCostMessageCallback;

class TariffAndCostInterface : public MessageHandlerInterface {
public:
Expand All @@ -35,7 +36,7 @@ class TariffAndCostInterface : public MessageHandlerInterface {
class TariffAndCost : public TariffAndCostInterface {
public:
TariffAndCost(const FunctionalBlockContext& functional_block_context, MeterValuesInterface& meter_values,
std::optional<SetDisplayMessageCallback>& set_display_message_callback,
std::optional<SessionCostMessageCallback>& session_cost_message_callback,
std::optional<SetRunningCostCallback>& set_running_cost_callback,
boost::asio::io_service& io_service);
void handle_message(const ocpp::EnhancedMessage<MessageType>& message) override;
Expand All @@ -47,7 +48,7 @@ class TariffAndCost : public TariffAndCostInterface {
private: // Members
const FunctionalBlockContext& context;
MeterValuesInterface& meter_values;
std::optional<SetDisplayMessageCallback> set_display_message_callback;
std::optional<SessionCostMessageCallback> session_cost_message_callback;
std::optional<SetRunningCostCallback> set_running_cost_callback;
boost::asio::io_service& io_service;

Expand Down
5 changes: 5 additions & 0 deletions lib/ocpp/v16/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<DataTransferResponse(const SessionCostMessage& message)>& 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<DataTransferResponse(const std::vector<DisplayMessage>&)> set_display_message_callback) {
this->charge_point->register_set_display_message_callback(set_display_message_callback);
Expand Down
38 changes: 21 additions & 17 deletions lib/ocpp/v16/charge_point_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -3124,9 +3124,7 @@ DataTransferResponse ChargePointImpl::handle_set_user_price(const std::optional<
return response;
}

// Find transaction with given id tag
std::vector<DisplayMessage> 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;
Expand All @@ -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<int32_t> 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;
}

Expand Down Expand Up @@ -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<DataTransferResponse(const SessionCostMessage& message)>& session_cost_message_callback) {
this->session_cost_message_callback = session_cost_message_callback;
}

void ChargePointImpl::register_set_display_message_callback(
const std::function<DataTransferResponse(const std::vector<DisplayMessage>& display_message)>
set_display_message_callback) {
Expand Down
2 changes: 1 addition & 1 deletion lib/ocpp/v2/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ void ChargePoint::initialize(const std::map<int32_t, int32_t>& evse_connector_st
this->callbacks.set_charging_profiles_callback);

this->tariff_and_cost = std::make_unique<TariffAndCost>(
*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<FirmwareUpdate>(
Expand Down
21 changes: 11 additions & 10 deletions lib/ocpp/v2/functional_blocks/tariff_and_cost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<SetDisplayMessageCallback>& set_display_message_callback,
std::optional<SessionCostMessageCallback>& session_cost_message_callback,
std::optional<SetRunningCostCallback>& 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) {
}
Expand Down Expand Up @@ -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});
}
}

Expand Down