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

Support configurable TxStart and TxStop points #546

Merged
merged 4 commits into from
Apr 9, 2024
Merged
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
8 changes: 4 additions & 4 deletions config/v201/component_schemas/standardized/TxCtrlr.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,14 @@
"TxStartPoint": {
"variable_name": "TxStartPoint",
"characteristics": {
"valuesList": "ParkingBayOccupancy,EVConnected,Authorized,PowerPathClosed,EnergyTransfer,DataSigned",
"valuesList": "EVConnected,Authorized,PowerPathClosed,EnergyTransfer,DataSigned",
"supportsMonitoring": true,
"dataType": "MemberList"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly"
"mutability": "ReadWrite"
}
],
"description": "Defines when the Charging Station starts a new transaction",
Expand All @@ -134,14 +134,14 @@
"TxStopPoint": {
"variable_name": "TxStopPoint",
"characteristics": {
"valuesList": "ParkingBayOccupancy,EVConnected,Authorized,PowerPathClosed,EnergyTransfer",
"valuesList": "EVConnected,Authorized,PowerPathClosed,EnergyTransfer",
"supportsMonitoring": true,
"dataType": "MemberList"
},
"attributes": [
{
"type": "Actual",
"mutability": "ReadOnly"
"mutability": "ReadWrite"
}
],
"description": "Defines when the Charging Station ends a transaction",
Expand Down
11 changes: 8 additions & 3 deletions include/ocpp/v201/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ class ChargePoint : ocpp::ChargingStationBase {
void handle_get_local_authorization_list_version_req(Call<GetLocalListVersionRequest> call);

// Functional Block E: Transaction
void handle_start_transaction_event_response(const EnhancedMessage<v201::MessageType>& message);
void handle_transaction_event_response(const EnhancedMessage<v201::MessageType>& message);
void handle_get_transaction_status(const Call<GetTransactionStatusRequest> call);

// Function Block F: Remote transaction control
Expand Down Expand Up @@ -613,7 +613,7 @@ class ChargePoint : ocpp::ChargingStationBase {
/// \param charging_state The new charging state
void on_transaction_started(const int32_t evse_id, const int32_t connector_id, const std::string& session_id,
const DateTime& timestamp, const ocpp::v201::TriggerReasonEnum trigger_reason,
const MeterValue& meter_start, const IdToken& id_token,
const MeterValue& meter_start, const std::optional<IdToken>& id_token,
const std::optional<IdToken>& group_id_token,
const std::optional<int32_t>& reservation_id,
const std::optional<int32_t>& remote_start_id, const ChargingStateEnum charging_state);
Expand All @@ -635,6 +635,9 @@ class ChargePoint : ocpp::ChargingStationBase {
/// \param connector_id
void on_session_finished(const int32_t evse_id, const int32_t connector_id);

/// \brief Event handler that should be called when the given \p id_token is authorized
void on_authorized(const int32_t evse_id, const int32_t connector_id, const IdToken& id_token);

/// \brief Event handler that should be called when a new meter value is present
/// \param evse_id
/// \param meter_value
Expand All @@ -661,8 +664,10 @@ class ChargePoint : ocpp::ChargingStationBase {
/// \brief Event handler that will update the charging state internally when it has been changed.
/// \param evse_id The evse id of which the charging state has changed.
/// \param charging_state The new charging state.
/// \param trigger_reason The trigger reason of the event. Defaults to ChargingStateChanged
/// \return True on success. False if evse id does not exist.
bool on_charging_state_changed(const uint32_t evse_id, ChargingStateEnum charging_state);
bool on_charging_state_changed(const uint32_t evse_id, const ChargingStateEnum charging_state,
const TriggerReasonEnum trigger_reason = TriggerReasonEnum::ChargingStateChanged);

/// \brief Validates provided \p id_token \p certificate and \p ocsp_request_data using CSMS, AuthCache or AuthList
/// \param id_token
Expand Down
6 changes: 3 additions & 3 deletions include/ocpp/v201/evse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ class EvseInterface {
/// \param sampled_data_tx_updated_interval Interval between sampling of metering (or other) data, intended to
/// be transmitted via TransactionEventRequest (eventType = Updated) messages
virtual void open_transaction(const std::string& transaction_id, const int32_t connector_id,
const DateTime& timestamp, const MeterValue& meter_start, const IdToken& id_token,
const std::optional<IdToken>& group_id_token,
const DateTime& timestamp, const MeterValue& meter_start,
const std::optional<IdToken>& id_token, const std::optional<IdToken>& group_id_token,
const std::optional<int32_t> reservation_id,
const std::chrono::seconds sampled_data_tx_updated_interval,
const std::chrono::seconds sampled_data_tx_ended_interval,
Expand Down Expand Up @@ -170,7 +170,7 @@ class Evse : public EvseInterface {
uint32_t get_number_of_connectors();

void open_transaction(const std::string& transaction_id, const int32_t connector_id, const DateTime& timestamp,
const MeterValue& meter_start, const IdToken& id_token,
const MeterValue& meter_start, const std::optional<IdToken>& id_token,
const std::optional<IdToken>& group_id_token, const std::optional<int32_t> reservation_id,
const std::chrono::seconds sampled_data_tx_updated_interval,
const std::chrono::seconds sampled_data_tx_ended_interval,
Expand Down
2 changes: 1 addition & 1 deletion include/ocpp/v201/transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace v201 {

/// \brief Struct that enhances the OCPP Transaction by some meta data and functionality
struct EnhancedTransaction : public Transaction {
IdToken id_token;
std::optional<IdToken> id_token;
std::optional<IdToken> group_id_token;
std::optional<int32_t> reservation_id;
int32_t connector_id;
Expand Down
127 changes: 83 additions & 44 deletions lib/ocpp/v201/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,11 +293,14 @@ ChargePoint::on_get_15118_ev_certificate_request(const Get15118EVCertificateRequ
return call_result.msg;
}

void ChargePoint::on_transaction_started(
const int32_t evse_id, const int32_t connector_id, const std::string& session_id, const DateTime& timestamp,
const ocpp::v201::TriggerReasonEnum trigger_reason, const MeterValue& meter_start, const IdToken& id_token,
const std::optional<IdToken>& group_id_token, const std::optional<int32_t>& reservation_id,
const std::optional<int32_t>& remote_start_id, const ChargingStateEnum charging_state) {
void ChargePoint::on_transaction_started(const int32_t evse_id, const int32_t connector_id,
const std::string& session_id, const DateTime& timestamp,
const ocpp::v201::TriggerReasonEnum trigger_reason,
const MeterValue& meter_start, const std::optional<IdToken>& id_token,
const std::optional<IdToken>& group_id_token,
const std::optional<int32_t>& reservation_id,
const std::optional<int32_t>& remote_start_id,
const ChargingStateEnum charging_state) {

this->evses.at(evse_id)->open_transaction(
session_id, connector_id, timestamp, meter_start, id_token, group_id_token, reservation_id,
Expand Down Expand Up @@ -435,6 +438,27 @@ void ChargePoint::on_session_finished(const int32_t evse_id, const int32_t conne
this->evses.at(evse_id)->submit_event(connector_id, ConnectorEvent::PlugOut);
}

void ChargePoint::on_authorized(const int32_t evse_id, const int32_t connector_id, const IdToken& id_token) {
if (this->evses.at(evse_id)->get_transaction() == nullptr) {
// nothing to report in case transaction is not yet open
return;
}

std::unique_ptr<EnhancedTransaction>& transaction =
this->evses.at(static_cast<int32_t>(evse_id))->get_transaction();

if (transaction->id_token.has_value()) {
// if transactions id_token is already set, it is assumed it has already been reported
return;
}

// set id_token of transaction and send TransactionEvent(Updated) with id_token
transaction->id_token = id_token;
this->transaction_event_req(TransactionEventEnum::Updated, ocpp::DateTime(), transaction->get_transaction(),
TriggerReasonEnum::Authorized, transaction->get_seq_no(), std::nullopt, std::nullopt,
id_token, std::nullopt, std::nullopt, this->is_offline(), std::nullopt);
}

void ChargePoint::on_meter_value(const int32_t evse_id, const MeterValue& meter_value) {
if (evse_id == 0) {
// if evseId = 0 then store in the chargepoint metervalues
Expand Down Expand Up @@ -518,7 +542,8 @@ void ChargePoint::on_reserved(const int32_t evse_id, const int32_t connector_id)
this->evses.at(evse_id)->submit_event(connector_id, ConnectorEvent::Reserve);
}

bool ChargePoint::on_charging_state_changed(const uint32_t evse_id, ChargingStateEnum charging_state) {
bool ChargePoint::on_charging_state_changed(const uint32_t evse_id, const ChargingStateEnum charging_state,
const TriggerReasonEnum trigger_reason) {
if (this->evses.find(static_cast<int32_t>(evse_id)) != this->evses.end()) {
std::unique_ptr<EnhancedTransaction>& transaction =
this->evses.at(static_cast<int32_t>(evse_id))->get_transaction();
Expand All @@ -528,8 +553,7 @@ bool ChargePoint::on_charging_state_changed(const uint32_t evse_id, ChargingStat
} else {
transaction->chargingState = charging_state;
this->transaction_event_req(TransactionEventEnum::Updated, DateTime(), transaction->get_transaction(),
TriggerReasonEnum::ChargingStateChanged, transaction->get_seq_no(),
std::nullopt,
trigger_reason, transaction->get_seq_no(), std::nullopt,
this->evses.at(static_cast<int32_t>(evse_id))->get_evse_info(),
std::nullopt, std::nullopt, std::nullopt, this->is_offline(), std::nullopt);
}
Expand Down Expand Up @@ -1080,7 +1104,7 @@ void ChargePoint::handle_message(const EnhancedMessage<v201::MessageType>& messa
this->handle_change_availability_req(json_message);
break;
case MessageType::TransactionEventResponse:
this->handle_start_transaction_event_response(message);
this->handle_transaction_event_response(message);
break;
case MessageType::RequestStartTransaction:
this->handle_remote_start_transaction_request(json_message);
Expand Down Expand Up @@ -1919,12 +1943,6 @@ void ChargePoint::transaction_event_req(const TransactionEventEnum& event_type,
remote_start_id_per_evse.erase(it);
}

if (event_type == TransactionEventEnum::Started and (!evse.has_value() or !id_token.has_value())) {
EVLOG_error << "Request to send TransactionEvent(Started) without given evse or id_token. These properties "
"are required for this eventType \"Started\"!";
return;
}

this->send<TransactionEventRequest>(call);

if (this->callbacks.transaction_event_callback.has_value()) {
Expand Down Expand Up @@ -2449,53 +2467,74 @@ void ChargePoint::handle_clear_cache_req(Call<ClearCacheRequest> call) {
this->send<ClearCacheResponse>(call_result);
}

void ChargePoint::handle_start_transaction_event_response(const EnhancedMessage<v201::MessageType>& message) {
void ChargePoint::handle_transaction_event_response(const EnhancedMessage<v201::MessageType>& message) {
CallResult<TransactionEventResponse> call_result = message.message;
const Call<TransactionEventRequest>& original_call = message.call_message;
const auto& original_msg = original_call.msg;

if (original_msg.eventType != TransactionEventEnum::Started) {
if (original_msg.eventType == TransactionEventEnum::Ended) {
// nothing to do for TransactionEventEnum::Ended
return;
}

if (!original_msg.evse.has_value()) {
EVLOG_error << "Start transaction event sent without without evse id";
const auto msg = call_result.msg;

if (!msg.idTokenInfo.has_value()) {
// nothing to do when the response does not contain idTokenInfo
return;
}

if (!original_msg.idToken.has_value()) {
EVLOG_error << "Start transaction event sent without without idToken info";
EVLOG_error
<< "TransactionEvent.conf contains idTokenInfo when no idToken was part of the TransactionEvent.req";
return;
}

const int32_t evse_id = original_msg.evse.value().id;
const IdToken& id_token = original_msg.idToken.value();

const auto msg = call_result.msg;
if (msg.idTokenInfo.has_value()) {
// C03.FR.0x and C05.FR.01: We SHALL NOT store central information in the Authorization Cache
// C10.FR.05
if (id_token.type != IdTokenEnum::Central and
this->device_model->get_optional_value<bool>(ControllerComponentVariables::AuthCacheCtrlrEnabled)
.value_or(true)) {
auto id_token_info = msg.idTokenInfo.value();
this->update_id_token_cache_lifetime(id_token_info);
this->database_handler->authorization_cache_insert_entry(utils::generate_token_hash(id_token),
id_token_info);
this->update_authorization_cache_size();
// C03.FR.0x and C05.FR.01: We SHALL NOT store central information in the Authorization Cache
// C10.FR.05
if (id_token.type != IdTokenEnum::Central and
this->device_model->get_optional_value<bool>(ControllerComponentVariables::AuthCacheCtrlrEnabled)
.value_or(true)) {
auto id_token_info = msg.idTokenInfo.value();
this->update_id_token_cache_lifetime(id_token_info);
this->database_handler->authorization_cache_insert_entry(utils::generate_token_hash(id_token), id_token_info);
this->update_authorization_cache_size();
}

if (msg.idTokenInfo.value().status == AuthorizationStatusEnum::Accepted) {
// nothing to do in case status is accepted
return;
}

std::vector<int32_t> evse_ids; // this is a vector because the id token could be active for multiple transactions
if (original_msg.evse.has_value()) {
evse_ids.push_back(original_msg.evse.value().id);
} else {
// add every evse_id with active transaction and given token
for (const auto& [evse_id, evse] : this->evses) {
const auto& transaction = evse->get_transaction();
if (transaction != nullptr and transaction->id_token.has_value()) {
if (transaction->id_token.value().idToken.get() == id_token.idToken.get()) {
evse_ids.push_back(evse_id);
}
}
}
if (msg.idTokenInfo.value().status != AuthorizationStatusEnum::Accepted) {
if (this->device_model->get_value<bool>(ControllerComponentVariables::StopTxOnInvalidId)) {
this->callbacks.stop_transaction_callback(evse_id, ReasonEnum::DeAuthorized);
}

// post handling of transactions in case status is not Accepted
for (const auto evse_id : evse_ids) {
if (this->device_model->get_value<bool>(ControllerComponentVariables::StopTxOnInvalidId)) {
this->callbacks.stop_transaction_callback(evse_id, ReasonEnum::DeAuthorized);
} else {
if (this->device_model->get_optional_value<int32_t>(ControllerComponentVariables::MaxEnergyOnInvalidId)
.has_value()) {
// Energy delivery to the EV SHALL be allowed until the amount of energy specified in
// MaxEnergyOnInvalidId has been reached.
this->evses.at(evse_id)->start_checking_max_energy_on_invalid_id();
} else {
if (this->device_model->get_optional_value<int32_t>(ControllerComponentVariables::MaxEnergyOnInvalidId)
.has_value()) {
// Energy delivery to the EV SHALL be allowed until the amount of energy specified in
// MaxEnergyOnInvalidId has been reached.
this->evses.at(evse_id)->start_checking_max_energy_on_invalid_id();
} else {
this->callbacks.pause_charging_callback(evse_id);
}
this->callbacks.pause_charging_callback(evse_id);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/ocpp/v201/evse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ uint32_t Evse::get_number_of_connectors() {
}

void Evse::open_transaction(const std::string& transaction_id, const int32_t connector_id, const DateTime& timestamp,
const MeterValue& meter_start, const IdToken& id_token,
const MeterValue& meter_start, const std::optional<IdToken>& id_token,
const std::optional<IdToken>& group_id_token, const std::optional<int32_t> reservation_id,
const std::chrono::seconds sampled_data_tx_updated_interval,
const std::chrono::seconds sampled_data_tx_ended_interval,
Expand Down
Loading