From ac4bb792a425e64b6fbb18f6f052afade340d805 Mon Sep 17 00:00:00 2001 From: marcemmers <35759328+marcemmers@users.noreply.github.com> Date: Fri, 13 Oct 2023 09:39:38 +0200 Subject: [PATCH] If no metervalues are going to be sent, do not sent a MeterValueRequest or an empty meterValue list in a transaction (#187) Signed-off-by: Marc Emmers --- include/ocpp/v201/utils.hpp | 4 ++ lib/ocpp/v201/charge_point.cpp | 70 ++++++++++++++++++++++++---------- lib/ocpp/v201/utils.cpp | 22 ++++++++--- 3 files changed, 70 insertions(+), 26 deletions(-) diff --git a/include/ocpp/v201/utils.hpp b/include/ocpp/v201/utils.hpp index 798bc5c5b..e5faf7726 100644 --- a/include/ocpp/v201/utils.hpp +++ b/include/ocpp/v201/utils.hpp @@ -14,6 +14,10 @@ namespace utils { /// \brief std::vector of the configured AlignedDataMeasurands std::vector get_measurands_vec(const std::string& measurands_csv); +///\brief This function determines if any of the \p measurands is present in the \p _meter_value at all +///\return True if any measurand is found, false otherwise +bool meter_value_has_any_measurand(const MeterValue& _meter_value, const std::vector& measurands); + /// \brief Applies the given \p measurands to the given \p _meter_value . The returned meter value will only contain /// SampledValues which measurand is listed in the given \param measurands . If no measurand is set for the /// SampledValue, the SampledValue will also be omitted. diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 28d58400d..078728dee 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -82,10 +82,12 @@ ChargePoint::ChargePoint(const std::map& evse_connector_struct _meter_value, utils::get_measurands_vec(this->device_model->get_value( ControllerComponentVariables::SampledDataTxUpdatedMeasurands))); - this->transaction_event_req(TransactionEventEnum::Updated, DateTime(), transaction, - TriggerReasonEnum::MeterValuePeriodic, seq_no, std::nullopt, std::nullopt, - std::nullopt, std::vector(1, filtered_meter_value), std::nullopt, - this->is_offline(), reservation_id); + if (!filtered_meter_value.sampledValue.empty()) { + this->transaction_event_req(TransactionEventEnum::Updated, DateTime(), transaction, + TriggerReasonEnum::MeterValuePeriodic, seq_no, std::nullopt, std::nullopt, + std::nullopt, std::vector(1, filtered_meter_value), + std::nullopt, this->is_offline(), reservation_id); + } }; this->evses.insert( @@ -186,10 +188,14 @@ void ChargePoint::on_transaction_started( auto evse = this->evses.at(evse_id)->get_evse_info(); evse.connectorId.emplace(connector_id); + std::optional> opt_meter_value; + if (!meter_value.sampledValue.empty()) { + opt_meter_value.emplace(1, meter_value); + } + this->transaction_event_req(TransactionEventEnum::Started, timestamp, transaction, trigger_reason, enhanced_transaction->get_seq_no(), std::nullopt, evse, enhanced_transaction->id_token, - std::vector(1, meter_value), std::nullopt, this->is_offline(), - reservation_id); + opt_meter_value, std::nullopt, this->is_offline(), reservation_id); } void ChargePoint::on_transaction_finished(const int32_t evse_id, const DateTime& timestamp, @@ -206,14 +212,19 @@ void ChargePoint::on_transaction_finished(const int32_t evse_id, const DateTime& this->evses.at(evse_id)->close_transaction(timestamp, meter_stop, reason); const auto transaction = enhanced_transaction->get_transaction(); - const auto meter_values = utils::get_meter_values_with_measurands_and_interval_applied( + auto meter_values = std::make_optional(utils::get_meter_values_with_measurands_and_interval_applied( enhanced_transaction->meter_values, utils::get_measurands_vec( this->device_model->get_value(ControllerComponentVariables::AlignedDataTxEndedMeasurands)), utils::get_measurands_vec( this->device_model->get_value(ControllerComponentVariables::SampledDataTxEndedMeasurands)), this->device_model->get_value(ControllerComponentVariables::AlignedDataTxEndedInterval), - this->device_model->get_value(ControllerComponentVariables::SampledDataTxEndedInterval)); + this->device_model->get_value(ControllerComponentVariables::SampledDataTxEndedInterval))); + + if (meter_values.value().empty()) { + meter_values.reset(); + } + const auto seq_no = enhanced_transaction->get_seq_no(); this->evses.at(evse_id)->release_transaction(); @@ -867,12 +878,13 @@ void ChargePoint::update_aligned_data_interval() { // add meter value to transaction meter values const auto& enhanced_transaction = evse->get_transaction(); enhanced_transaction->meter_values.push_back(_meter_value); - - this->transaction_event_req( - TransactionEventEnum::Updated, DateTime(), enhanced_transaction->get_transaction(), - TriggerReasonEnum::MeterValueClock, enhanced_transaction->get_seq_no(), std::nullopt, - std::nullopt, std::nullopt, std::vector(1, meter_value), std::nullopt, - this->is_offline(), std::nullopt); + if (!meter_value.sampledValue.empty()) { + this->transaction_event_req( + TransactionEventEnum::Updated, DateTime(), enhanced_transaction->get_transaction(), + TriggerReasonEnum::MeterValueClock, enhanced_transaction->get_seq_no(), std::nullopt, + std::nullopt, std::nullopt, std::vector(1, meter_value), std::nullopt, + this->is_offline(), std::nullopt); + } } else if (!evse->has_active_transaction() and this->device_model ->get_optional_value(ControllerComponentVariables::AlignedDataSendDuringIdle) @@ -1671,11 +1683,21 @@ void ChargePoint::handle_trigger_message(Call call) { case MessageTriggerEnum::MeterValues: if (msg.evse.has_value()) { - if (evse_ptr != nullptr) { + if (evse_ptr != nullptr and + utils::meter_value_has_any_measurand( + evse_ptr->get_meter_value(), utils::get_measurands_vec(this->device_model->get_value( + ControllerComponentVariables::AlignedDataMeasurands)))) { response.status = TriggerMessageStatusEnum::Accepted; } } else { - response.status = TriggerMessageStatusEnum::Accepted; + auto measurands = utils::get_measurands_vec(this->device_model->get_value( + ControllerComponentVariables::SampledDataTxUpdatedMeasurands)); + for (auto const& [evse_id, evse] : this->evses) { + if (utils::meter_value_has_any_measurand(evse->get_meter_value(), measurands)) { + response.status = TriggerMessageStatusEnum::Accepted; + break; + } + } } break; @@ -1744,7 +1766,9 @@ void ChargePoint::handle_trigger_message(Call call) { get_latest_meter_value_filtered(evse.get_meter_value(), ReadingContextEnum::Trigger, ControllerComponentVariables::AlignedDataMeasurands); - this->meter_values_req(evse_id, std::vector(1, meter_value)); + if (!meter_value.sampledValue.empty()) { + this->meter_values_req(evse_id, std::vector(1, meter_value)); + } }; send_evse_message(send_meter_value); } break; @@ -1759,12 +1783,16 @@ void ChargePoint::handle_trigger_message(Call call) { get_latest_meter_value_filtered(evse.get_meter_value(), ReadingContextEnum::Trigger, ControllerComponentVariables::SampledDataTxUpdatedMeasurands); + std::optional> opt_meter_value; + if (!meter_value.sampledValue.empty()) { + opt_meter_value.emplace(1, meter_value); + } const auto& enhanced_transaction = evse.get_transaction(); - this->transaction_event_req( - TransactionEventEnum::Updated, DateTime(), enhanced_transaction->get_transaction(), - TriggerReasonEnum::Trigger, enhanced_transaction->get_seq_no(), std::nullopt, std::nullopt, - std::nullopt, std::vector(1, meter_value), std::nullopt, this->is_offline(), std::nullopt); + this->transaction_event_req(TransactionEventEnum::Updated, DateTime(), + enhanced_transaction->get_transaction(), TriggerReasonEnum::Trigger, + enhanced_transaction->get_seq_no(), std::nullopt, std::nullopt, std::nullopt, + opt_meter_value, std::nullopt, this->is_offline(), std::nullopt); }; send_evse_message(send_transaction); } break; diff --git a/lib/ocpp/v201/utils.cpp b/lib/ocpp/v201/utils.cpp index eebf7ffde..14f9ab456 100644 --- a/lib/ocpp/v201/utils.cpp +++ b/lib/ocpp/v201/utils.cpp @@ -3,6 +3,8 @@ #include +#include + #include #include @@ -24,6 +26,13 @@ std::vector get_measurands_vec(const std::string& measurands_csv) return measurands; } +bool meter_value_has_any_measurand(const MeterValue& _meter_value, const std::vector& measurands) { + auto compare = [](const SampledValue& a, MeasurandEnum b) { return a.measurand == b; }; + + return std::find_first_of(_meter_value.sampledValue.begin(), _meter_value.sampledValue.end(), measurands.begin(), + measurands.end(), compare) != _meter_value.sampledValue.end(); +} + MeterValue get_meter_value_with_measurands_applied(const MeterValue& _meter_value, const std::vector& measurands) { auto meter_value = _meter_value; @@ -61,14 +70,16 @@ get_meter_values_with_measurands_and_interval_applied(const std::vector 0 and - meter_value.sampledValue.at(0).context.value() == ReadingContextEnum::Sample_Clock) { + meter_value.sampledValue.at(0).context.value() == ReadingContextEnum::Sample_Clock and + meter_value_has_any_measurand(meter_value, aligned_measurands)) { if (meter_value.timestamp.to_time_point() > next_aligned_timepoint) { meter_values.push_back(get_meter_value_with_measurands_applied(meter_value, aligned_measurands)); next_aligned_timepoint = @@ -77,7 +88,8 @@ get_meter_values_with_measurands_and_interval_applied(const std::vector 0 and - meter_value.sampledValue.at(0).context.value() == ReadingContextEnum::Sample_Periodic) { + meter_value.sampledValue.at(0).context.value() == ReadingContextEnum::Sample_Periodic and + meter_value_has_any_measurand(meter_value, sample_measurands)) { if (meter_value.timestamp.to_time_point() > next_sampled_timepoint) { meter_values.push_back(get_meter_value_with_measurands_applied(meter_value, sample_measurands)); next_sampled_timepoint =