Skip to content

Commit

Permalink
Moved DataTransfer functionality to DataTransfer functional block usi…
Browse files Browse the repository at this point in the history
…ng the targeted design. Added test cases for the new functional block

Signed-off-by: Piet Gömpel <[email protected]>
  • Loading branch information
Pietfried committed Nov 19, 2024
1 parent cac80bc commit 9578416
Show file tree
Hide file tree
Showing 9 changed files with 369 additions and 71 deletions.
5 changes: 2 additions & 3 deletions include/ocpp/v201/charge_point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <set>

#include <ocpp/common/message_dispatcher.hpp>
#include <ocpp/v201/functional_blocks/data_transfer.hpp>

#include <ocpp/common/charging_station_base.hpp>

Expand Down Expand Up @@ -388,6 +389,7 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa
std::unique_ptr<ConnectivityManager> connectivity_manager;

std::unique_ptr<MessageDispatcherInterface<MessageType>> message_dispatcher;
std::unique_ptr<DataTransferInterface> data_transfer;

// utility
std::shared_ptr<MessageQueue<v201::MessageType>> message_queue;
Expand Down Expand Up @@ -759,9 +761,6 @@ class ChargePoint : public ChargePointInterface, private ocpp::ChargingStationBa
void handle_set_display_message(Call<SetDisplayMessageRequest> call);
void handle_clear_display_message(Call<ClearDisplayMessageRequest> call);

// Functional Block P: DataTransfer
void handle_data_transfer_req(Call<DataTransferRequest> call);

// Generates async sending callbacks
template <class RequestType, class ResponseType>
std::function<ResponseType(RequestType)> send_callback(MessageType expected_response_message_type) {
Expand Down
62 changes: 62 additions & 0 deletions include/ocpp/v201/functional_blocks/data_transfer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest

#pragma once

#include <ocpp/v201/message_dispatcher.hpp>
#include <ocpp/v201/messages/DataTransfer.hpp>

namespace ocpp {
namespace v201 {

class DataTransferInterface {

public:
/// \brief Sends a DataTransfer.req message to the CSMS using the given parameters
/// \param vendorId
/// \param messageId
/// \param data
/// \return DataTransferResponse containing the result from CSMS
virtual std::optional<DataTransferResponse> data_transfer_req(const CiString<255>& vendorId,
const std::optional<CiString<50>>& messageId,
const std::optional<json>& data) = 0;

/// \brief Sends a DataTransfer.req message to the CSMS using the given \p request
/// \param request message shall be sent to the CSMS
/// \return DataTransferResponse containing the result from CSMS. In case no response is received from the CSMS
/// because the message timed out or the charging station is offline, std::nullopt is returned
virtual std::optional<DataTransferResponse> data_transfer_req(const DataTransferRequest& request) = 0;

/// \brief Handles the given DataTransfer.req \p call by the CSMS by responding with a CallResult
virtual void handle_data_transfer_req(Call<DataTransferRequest> call) = 0;
};

class DataTransfer : public DataTransferInterface {

private:
MessageDispatcherInterface<MessageType>& message_dispatcher;
std::optional<std::function<DataTransferResponse(const DataTransferRequest& request)>> data_transfer_callback;
std::chrono::seconds response_timeout;
std::function<bool()> is_websocket_connected;

public:
DataTransfer(MessageDispatcherInterface<MessageType>& message_dispatcher,
const std::optional<std::function<DataTransferResponse(const DataTransferRequest& request)>>&
data_transfer_callback,
const std::function<bool()> is_websocket_connected, const std::chrono::seconds response_timeout) :
message_dispatcher(message_dispatcher),
data_transfer_callback(data_transfer_callback),
is_websocket_connected(is_websocket_connected),
response_timeout(response_timeout){};

void handle_data_transfer_req(Call<DataTransferRequest> call) override;

std::optional<DataTransferResponse> data_transfer_req(const CiString<255>& vendorId,
const std::optional<CiString<50>>& messageId,
const std::optional<json>& data) override;

std::optional<DataTransferResponse> data_transfer_req(const DataTransferRequest& request) override;
};

} // namespace v201
} // namespace ocpp
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ if(LIBOCPP_ENABLE_V201)
ocpp/v201/component_state_manager.cpp
ocpp/v201/connectivity_manager.cpp
ocpp/v201/message_dispatcher.cpp
ocpp/v201/functional_blocks/data_transfer.cpp
)
add_subdirectory(ocpp/v201/messages)
endif()
Expand Down
66 changes: 10 additions & 56 deletions lib/ocpp/v201/charge_point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,7 @@ ChargePoint::on_get_15118_ev_certificate_request(const Get15118EVCertificateRequ
}

EVLOG_debug << "Received Get15118EVCertificateRequest " << request;
auto future_res = this->message_dispatcher->dispatch_call_async(
ocpp::Call<Get15118EVCertificateRequest>(request));
auto future_res = this->message_dispatcher->dispatch_call_async(ocpp::Call<Get15118EVCertificateRequest>(request));

if (future_res.wait_for(DEFAULT_WAIT_FOR_FUTURE_TIMEOUT) == std::future_status::timeout) {
EVLOG_warning << "Waiting for Get15118EVCertificateRequest.conf future timed out!";
Expand Down Expand Up @@ -1171,6 +1170,12 @@ void ChargePoint::initialize(const std::map<int32_t, int32_t>& evse_connector_st

this->message_dispatcher =
std::make_unique<MessageDispatcher>(*this->message_queue, *this->device_model, registration_status);
this->data_transfer = std::make_unique<DataTransfer>(
*this->message_dispatcher, this->callbacks.data_transfer_callback,
[&connectivity_manager = this->connectivity_manager]() {
return connectivity_manager->is_websocket_connected();
},
DEFAULT_WAIT_FOR_FUTURE_TIMEOUT);

if (this->callbacks.configure_network_connection_profile_callback.has_value()) {
this->connectivity_manager->set_configure_network_connection_profile_callback(
Expand Down Expand Up @@ -1290,7 +1295,7 @@ void ChargePoint::handle_message(const EnhancedMessage<v201::MessageType>& messa
this->handle_remote_stop_transaction_request(json_message);
break;
case MessageType::DataTransfer:
this->handle_data_transfer_req(json_message);
this->data_transfer->handle_data_transfer_req(json_message);
break;
case MessageType::GetLog:
this->handle_get_log_req(json_message);
Expand Down Expand Up @@ -4045,65 +4050,14 @@ void ChargePoint::handle_clear_display_message(const Call<ClearDisplayMessageReq
this->message_dispatcher->dispatch_call_result(call_result);
}

void ChargePoint::handle_data_transfer_req(Call<DataTransferRequest> call) {
const auto msg = call.msg;
DataTransferResponse response;

if (this->callbacks.data_transfer_callback.has_value()) {
response = this->callbacks.data_transfer_callback.value()(call.msg);
} else {
response.status = DataTransferStatusEnum::UnknownVendorId;
EVLOG_warning << "Received a DataTransferRequest but no data transfer callback was registered";
}

ocpp::CallResult<DataTransferResponse> call_result(response, call.uniqueId);
this->message_dispatcher->dispatch_call_result(call_result);
}

std::optional<DataTransferResponse> ChargePoint::data_transfer_req(const CiString<255>& vendorId,
const std::optional<CiString<50>>& messageId,
const std::optional<json>& data) {
DataTransferRequest req;
req.vendorId = vendorId;
req.messageId = messageId;
req.data = data;

return this->data_transfer_req(req);
return this->data_transfer->data_transfer_req(vendorId, messageId, data);
}

std::optional<DataTransferResponse> ChargePoint::data_transfer_req(const DataTransferRequest& request) {
DataTransferResponse response;
response.status = DataTransferStatusEnum::Rejected;

ocpp::Call<DataTransferRequest> call(request);
auto data_transfer_future = this->message_dispatcher->dispatch_call_async(call);

if (this->connectivity_manager == nullptr or !this->connectivity_manager->is_websocket_connected()) {
return std::nullopt;
}

if (data_transfer_future.wait_for(DEFAULT_WAIT_FOR_FUTURE_TIMEOUT) == std::future_status::timeout) {
EVLOG_warning << "Waiting for DataTransfer.conf future timed out";
return std::nullopt;
}

auto enhanced_message = data_transfer_future.get();
if (enhanced_message.messageType == MessageType::DataTransferResponse) {
try {
ocpp::CallResult<DataTransferResponse> call_result = enhanced_message.message;
response = call_result.msg;
} catch (const EnumConversionException& e) {
EVLOG_error << "EnumConversionException during handling of message: " << e.what();
auto call_error = CallError(enhanced_message.uniqueId, "FormationViolation", e.what(), json({}));
this->message_dispatcher->dispatch_call_error(call_error);
return std::nullopt;
}
}
if (enhanced_message.offline) {
return std::nullopt;
}

return response;
return this->data_transfer->data_transfer_req(request);
}

void ChargePoint::handle_send_local_authorization_list_req(Call<SendLocalListRequest> call) {
Expand Down
80 changes: 80 additions & 0 deletions lib/ocpp/v201/functional_blocks/data_transfer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest

#include <ocpp/common/constants.hpp>
#include <ocpp/v201/functional_blocks/data_transfer.hpp>

namespace ocpp {
namespace v201 {

void DataTransfer::handle_data_transfer_req(Call<DataTransferRequest> call) {
const auto msg = call.msg;
DataTransferResponse response;
response.status = DataTransferStatusEnum::UnknownVendorId;

if (this->data_transfer_callback.has_value()) {
response = this->data_transfer_callback.value()(call.msg);
} else {
response.status = DataTransferStatusEnum::UnknownVendorId;
EVLOG_warning << "Received a DataTransferRequest but no data transfer callback was registered";
}

ocpp::CallResult<DataTransferResponse> call_result(response, call.uniqueId);
this->message_dispatcher.dispatch_call_result(call_result);
}

std::optional<DataTransferResponse> DataTransfer::data_transfer_req(const CiString<255>& vendorId,
const std::optional<CiString<50>>& messageId,
const std::optional<json>& data) {
DataTransferRequest req;
req.vendorId = vendorId;
req.messageId = messageId;
req.data = data;

return this->data_transfer_req(req);
}

std::optional<DataTransferResponse> DataTransfer::data_transfer_req(const DataTransferRequest& request) {
DataTransferResponse response;
response.status = DataTransferStatusEnum::Rejected;

ocpp::Call<DataTransferRequest> call(request);
auto data_transfer_future = this->message_dispatcher.dispatch_call_async(call);

if (not this->is_websocket_connected()) {
return std::nullopt;
}

if (data_transfer_future.wait_for(this->response_timeout) == std::future_status::timeout) {
EVLOG_warning << "Waiting for DataTransfer.conf future timed out";
return std::nullopt;
}

auto enhanced_message = data_transfer_future.get();

if (enhanced_message.offline) {
return std::nullopt;
}

if (enhanced_message.messageType == MessageType::DataTransferResponse) {
try {
ocpp::CallResult<DataTransferResponse> call_result = enhanced_message.message;
response = call_result.msg;
} catch (const EnumConversionException& e) {
EVLOG_error << "EnumConversionException during handling of message: " << e.what();
auto call_error = CallError(enhanced_message.uniqueId, "FormationViolation", e.what(), json({}));
this->message_dispatcher.dispatch_call_error(call_error);
return std::nullopt;
} catch (const json::exception& e) {
EVLOG_error << "Unable to parse DataTransfer.conf from CSMS: " << enhanced_message.message;
auto call_error = CallError(enhanced_message.uniqueId, "FormationViolation", e.what(), json({}));
this->message_dispatcher.dispatch_call_error(call_error);
return std::nullopt;
}
}

return response;
}

}; // namespace v201
} // namespace ocpp
22 changes: 10 additions & 12 deletions tests/lib/ocpp/v16/test_message_queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ class ControlMessageV16Test : public ::testing::Test {

TEST_F(ControlMessageV16Test, test_is_transactional) {

EXPECT_TRUE(is_transaction_message(
(ControlMessage<v16::MessageType>{Call<v16::StartTransactionRequest>{v16::StartTransactionRequest{}}}
.messageType)));
EXPECT_TRUE(is_transaction_message(
(ControlMessage<v16::MessageType>{Call<v16::StopTransactionRequest>{v16::StopTransactionRequest{}}}
.messageType)));
EXPECT_TRUE(is_transaction_message((ControlMessage<v16::MessageType>{
Call<v16::StartTransactionRequest>{
v16::StartTransactionRequest{}}}.messageType)));
EXPECT_TRUE(is_transaction_message((ControlMessage<v16::MessageType>{
Call<v16::StopTransactionRequest>{
v16::StopTransactionRequest{}}}.messageType)));
EXPECT_TRUE(is_transaction_message(ControlMessage<v16::MessageType>{
Call<v16::SecurityEventNotificationRequest>{v16::SecurityEventNotificationRequest{}}}
.messageType));
Expand All @@ -43,12 +43,10 @@ TEST_F(ControlMessageV16Test, test_is_transactional) {

TEST_F(ControlMessageV16Test, test_is_transactional_update) {

EXPECT_TRUE(
!(ControlMessage<v16::MessageType>{Call<v16::StartTransactionRequest>{v16::StartTransactionRequest{}}})
.is_transaction_update_message());
EXPECT_TRUE(
!(ControlMessage<v16::MessageType>{Call<v16::StopTransactionRequest>{v16::StopTransactionRequest{}}})
.is_transaction_update_message());
EXPECT_TRUE(!(ControlMessage<v16::MessageType>{Call<v16::StartTransactionRequest>{v16::StartTransactionRequest{}}})
.is_transaction_update_message());
EXPECT_TRUE(!(ControlMessage<v16::MessageType>{Call<v16::StopTransactionRequest>{v16::StopTransactionRequest{}}})
.is_transaction_update_message());
EXPECT_TRUE(!(ControlMessage<v16::MessageType>{
Call<v16::SecurityEventNotificationRequest>{v16::SecurityEventNotificationRequest{}}})
.is_transaction_update_message());
Expand Down
2 changes: 2 additions & 0 deletions tests/lib/ocpp/v201/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ target_sources(libocpp_unit_tests PRIVATE

# Copy the json files used for testing to the destination directory
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/json DESTINATION ${TEST_PROFILES_LOCATION_V201})

add_subdirectory(functional_blocks)
6 changes: 6 additions & 0 deletions tests/lib/ocpp/v201/functional_blocks/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
target_include_directories(libocpp_unit_tests PUBLIC
../mocks
${CMAKE_CURRENT_SOURCE_DIR})

target_sources(libocpp_unit_tests PRIVATE
test_data_transfer.cpp)
Loading

0 comments on commit 9578416

Please sign in to comment.