diff --git a/doc/message_dispatching.md b/doc/message_dispatching.md index 1d6b555a9..478c2ea37 100644 --- a/doc/message_dispatching.md +++ b/doc/message_dispatching.md @@ -22,6 +22,14 @@ class v201_MessageDispatcher { - RegistrationStatusEnum& registration_status } +class v201_MessageHandlerInterface { + +handle_message(EnhancedMessage~v201_MessageType~ message) +} + +class v16_MessageHandlerInterface { + +handle_message(EnhancedMessage~v16_MessageType~ message) +} + class v201_DataTransferInterface { +data_transfer_req(request: DataTransferRequest): std::optional~DataTransferResponse~ +handle_data_transfer_req(call: Call~DataTransferRequest~) @@ -44,6 +52,7 @@ class v16_ChargePoint { MessageDispatcherInterface <|-- v16_MessageDispatcher MessageDispatcherInterface <|-- v201_MessageDispatcher v201_DataTransferInterface <|-- v201_DataTransfer +v201_MessageHandlerInterface <|-- v201_DataTransferInterface MessageDispatcherInterface *-- v201_DataTransfer MessageDispatcherInterface *-- v201_ChargePoint v201_DataTransferInterface *-- v201_ChargePoint diff --git a/include/ocpp/v201/functional_blocks/data_transfer.hpp b/include/ocpp/v201/functional_blocks/data_transfer.hpp index 474e93ee7..e2172207a 100644 --- a/include/ocpp/v201/functional_blocks/data_transfer.hpp +++ b/include/ocpp/v201/functional_blocks/data_transfer.hpp @@ -4,14 +4,17 @@ #pragma once #include +#include #include namespace ocpp { namespace v201 { -class DataTransferInterface { +class DataTransferInterface : public MessageHandlerInterface { public: + virtual ~DataTransferInterface() {}; + /// \brief Sends a DataTransfer.req message to the CSMS using the given parameters /// \param vendorId /// \param messageId @@ -26,9 +29,6 @@ class DataTransferInterface { /// \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 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 call) = 0; }; class DataTransfer : public DataTransferInterface { @@ -49,7 +49,7 @@ class DataTransfer : public DataTransferInterface { is_websocket_connected(is_websocket_connected), response_timeout(response_timeout){}; - void handle_data_transfer_req(Call call) override; + void handle_message(const EnhancedMessage& message) override; std::optional data_transfer_req(const CiString<255>& vendorId, const std::optional>& messageId, diff --git a/include/ocpp/v201/message_handler.hpp b/include/ocpp/v201/message_handler.hpp new file mode 100644 index 000000000..2cb72a6f0 --- /dev/null +++ b/include/ocpp/v201/message_handler.hpp @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#pragma once + +#include + +namespace ocpp { +namespace v201 { + +/// \brief Interface for handling OCPP2.0.1 CALL messages from the CSMS. Classes implementing a functional block shall +/// extend this interface. +class MessageHandlerInterface { + +public: + virtual ~MessageHandlerInterface() { + } + /// \brief Handles the given \p message from the CSMS. This includes dispatching a CALLRESULT as a response to the + /// incoming \p message . + /// @param message + virtual void handle_message(const EnhancedMessage& message) = 0; +}; + +class MessageTypeNotImplementedException : public std::exception { +private: + std::string message; + +public: + MessageTypeNotImplementedException(MessageType message_type) : + message("Message is not implemented: " + conversions::messagetype_to_string(message_type)) { + } + + const char* what() const noexcept override { + return message.c_str(); + } +}; + +} // namespace v201 +} // namespace ocpp \ No newline at end of file diff --git a/lib/ocpp/v201/charge_point.cpp b/lib/ocpp/v201/charge_point.cpp index 750d72322..514ef9635 100644 --- a/lib/ocpp/v201/charge_point.cpp +++ b/lib/ocpp/v201/charge_point.cpp @@ -1260,133 +1260,141 @@ void ChargePoint::remove_network_connection_profiles_below_actual_security_profi void ChargePoint::handle_message(const EnhancedMessage& message) { const auto& json_message = message.message; - switch (message.messageType) { - case MessageType::BootNotificationResponse: - this->handle_boot_notification_response(json_message); - break; - case MessageType::SetVariables: - this->handle_set_variables_req(json_message); - break; - case MessageType::GetVariables: - this->handle_get_variables_req(message); - break; - case MessageType::GetBaseReport: - this->handle_get_base_report_req(json_message); - break; - case MessageType::GetReport: - this->handle_get_report_req(message); - break; - case MessageType::Reset: - this->handle_reset_req(json_message); - break; - case MessageType::SetNetworkProfile: - this->handle_set_network_profile_req(json_message); - break; - case MessageType::ChangeAvailability: - this->handle_change_availability_req(json_message); - break; - case MessageType::TransactionEventResponse: - this->handle_transaction_event_response(message); - break; - case MessageType::RequestStartTransaction: - this->handle_remote_start_transaction_request(json_message); - break; - case MessageType::RequestStopTransaction: - this->handle_remote_stop_transaction_request(json_message); - break; - case MessageType::DataTransfer: - this->data_transfer->handle_data_transfer_req(json_message); - break; - case MessageType::GetLog: - this->handle_get_log_req(json_message); - break; - case MessageType::ClearCache: - this->handle_clear_cache_req(json_message); - break; - case MessageType::UpdateFirmware: - this->handle_firmware_update_req(json_message); - break; - case MessageType::UnlockConnector: - this->handle_unlock_connector(json_message); - break; - case MessageType::TriggerMessage: - this->handle_trigger_message(json_message); - break; - case MessageType::SignCertificateResponse: - this->handle_sign_certificate_response(json_message); - break; - case MessageType::HeartbeatResponse: - this->handle_heartbeat_response(json_message); - break; - case MessageType::SendLocalList: - this->handle_send_local_authorization_list_req(json_message); - break; - case MessageType::GetLocalListVersion: - this->handle_get_local_authorization_list_version_req(json_message); - break; - case MessageType::CertificateSigned: - this->handle_certificate_signed_req(json_message); - break; - case MessageType::GetTransactionStatus: - this->handle_get_transaction_status(json_message); - break; - case MessageType::GetInstalledCertificateIds: - this->handle_get_installed_certificate_ids_req(json_message); - break; - case MessageType::InstallCertificate: - this->handle_install_certificate_req(json_message); - break; - case MessageType::DeleteCertificate: - this->handle_delete_certificate_req(json_message); - break; - case MessageType::CustomerInformation: - this->handle_customer_information_req(json_message); - break; - case MessageType::SetChargingProfile: - this->handle_set_charging_profile_req(json_message); - break; - case MessageType::ClearChargingProfile: - this->handle_clear_charging_profile_req(json_message); - break; - case MessageType::GetChargingProfiles: - this->handle_get_charging_profiles_req(json_message); - break; - case MessageType::GetCompositeSchedule: - this->handle_get_composite_schedule_req(json_message); - break; - case MessageType::SetMonitoringBase: - this->handle_set_monitoring_base_req(json_message); - break; - case MessageType::SetMonitoringLevel: - this->handle_set_monitoring_level_req(json_message); - break; - case MessageType::SetVariableMonitoring: - this->handle_set_variable_monitoring_req(message); - break; - case MessageType::GetMonitoringReport: - this->handle_get_monitoring_report_req(json_message); - break; - case MessageType::ClearVariableMonitoring: - this->handle_clear_variable_monitoring_req(json_message); - break; - case MessageType::GetDisplayMessages: - this->handle_get_display_message(json_message); - break; - case MessageType::SetDisplayMessage: - this->handle_set_display_message(json_message); - break; - case MessageType::ClearDisplayMessage: - this->handle_clear_display_message(json_message); - break; - case MessageType::CostUpdated: - this->handle_costupdated_req(json_message); - break; - default: + try { + switch (message.messageType) { + case MessageType::BootNotificationResponse: + this->handle_boot_notification_response(json_message); + break; + case MessageType::SetVariables: + this->handle_set_variables_req(json_message); + break; + case MessageType::GetVariables: + this->handle_get_variables_req(message); + break; + case MessageType::GetBaseReport: + this->handle_get_base_report_req(json_message); + break; + case MessageType::GetReport: + this->handle_get_report_req(message); + break; + case MessageType::Reset: + this->handle_reset_req(json_message); + break; + case MessageType::SetNetworkProfile: + this->handle_set_network_profile_req(json_message); + break; + case MessageType::ChangeAvailability: + this->handle_change_availability_req(json_message); + break; + case MessageType::TransactionEventResponse: + this->handle_transaction_event_response(message); + break; + case MessageType::RequestStartTransaction: + this->handle_remote_start_transaction_request(json_message); + break; + case MessageType::RequestStopTransaction: + this->handle_remote_stop_transaction_request(json_message); + break; + case MessageType::DataTransfer: + this->data_transfer->handle_message(message); + break; + case MessageType::GetLog: + this->handle_get_log_req(json_message); + break; + case MessageType::ClearCache: + this->handle_clear_cache_req(json_message); + break; + case MessageType::UpdateFirmware: + this->handle_firmware_update_req(json_message); + break; + case MessageType::UnlockConnector: + this->handle_unlock_connector(json_message); + break; + case MessageType::TriggerMessage: + this->handle_trigger_message(json_message); + break; + case MessageType::SignCertificateResponse: + this->handle_sign_certificate_response(json_message); + break; + case MessageType::HeartbeatResponse: + this->handle_heartbeat_response(json_message); + break; + case MessageType::SendLocalList: + this->handle_send_local_authorization_list_req(json_message); + break; + case MessageType::GetLocalListVersion: + this->handle_get_local_authorization_list_version_req(json_message); + break; + case MessageType::CertificateSigned: + this->handle_certificate_signed_req(json_message); + break; + case MessageType::GetTransactionStatus: + this->handle_get_transaction_status(json_message); + break; + case MessageType::GetInstalledCertificateIds: + this->handle_get_installed_certificate_ids_req(json_message); + break; + case MessageType::InstallCertificate: + this->handle_install_certificate_req(json_message); + break; + case MessageType::DeleteCertificate: + this->handle_delete_certificate_req(json_message); + break; + case MessageType::CustomerInformation: + this->handle_customer_information_req(json_message); + break; + case MessageType::SetChargingProfile: + this->handle_set_charging_profile_req(json_message); + break; + case MessageType::ClearChargingProfile: + this->handle_clear_charging_profile_req(json_message); + break; + case MessageType::GetChargingProfiles: + this->handle_get_charging_profiles_req(json_message); + break; + case MessageType::GetCompositeSchedule: + this->handle_get_composite_schedule_req(json_message); + break; + case MessageType::SetMonitoringBase: + this->handle_set_monitoring_base_req(json_message); + break; + case MessageType::SetMonitoringLevel: + this->handle_set_monitoring_level_req(json_message); + break; + case MessageType::SetVariableMonitoring: + this->handle_set_variable_monitoring_req(message); + break; + case MessageType::GetMonitoringReport: + this->handle_get_monitoring_report_req(json_message); + break; + case MessageType::ClearVariableMonitoring: + this->handle_clear_variable_monitoring_req(json_message); + break; + case MessageType::GetDisplayMessages: + this->handle_get_display_message(json_message); + break; + case MessageType::SetDisplayMessage: + this->handle_set_display_message(json_message); + break; + case MessageType::ClearDisplayMessage: + this->handle_clear_display_message(json_message); + break; + case MessageType::CostUpdated: + this->handle_costupdated_req(json_message); + break; + default: + if (message.messageTypeId == MessageTypeId::CALL) { + const auto call_error = CallError(message.uniqueId, "NotImplemented", "", json({})); + this->message_dispatcher->dispatch_call_error(call_error); + } + break; + } + } catch (const MessageTypeNotImplementedException& e) { + EVLOG_warning << e.what(); if (message.messageTypeId == MessageTypeId::CALL) { const auto call_error = CallError(message.uniqueId, "NotImplemented", "", json({})); this->message_dispatcher->dispatch_call_error(call_error); } - break; } } diff --git a/lib/ocpp/v201/functional_blocks/data_transfer.cpp b/lib/ocpp/v201/functional_blocks/data_transfer.cpp index f91d5cd19..b5a48397b 100644 --- a/lib/ocpp/v201/functional_blocks/data_transfer.cpp +++ b/lib/ocpp/v201/functional_blocks/data_transfer.cpp @@ -7,7 +7,13 @@ namespace ocpp { namespace v201 { -void DataTransfer::handle_data_transfer_req(Call call) { +void DataTransfer::handle_message(const EnhancedMessage& message) { + + if (message.messageType != MessageType::DataTransfer) { + throw MessageTypeNotImplementedException(message.messageType); + } + + Call call = message.message; const auto msg = call.msg; DataTransferResponse response; response.status = DataTransferStatusEnum::UnknownVendorId; diff --git a/tests/lib/ocpp/v201/functional_blocks/test_data_transfer.cpp b/tests/lib/ocpp/v201/functional_blocks/test_data_transfer.cpp index 3aeb9f675..7bbc10a7a 100644 --- a/tests/lib/ocpp/v201/functional_blocks/test_data_transfer.cpp +++ b/tests/lib/ocpp/v201/functional_blocks/test_data_transfer.cpp @@ -26,6 +26,20 @@ bool is_websocket_connected() { return true; } +TEST(DataTransferTest, HandleDataTransferReq_NotImplemented) { + MockMessageDispatcher mock_dispatcher; + DataTransfer data_transfer(mock_dispatcher, std::nullopt, is_websocket_connected, + ocpp::DEFAULT_WAIT_FOR_FUTURE_TIMEOUT); + + DataTransferRequest request = create_example_request(); + ocpp::Call call(request); + ocpp::EnhancedMessage enhanced_message; + enhanced_message.messageType = MessageType::Authorize; // this cant be handled by DataTransfer functional block + enhanced_message.message = call; + + EXPECT_THROW(data_transfer.handle_message(enhanced_message), MessageTypeNotImplementedException); +} + TEST(DataTransferTest, HandleDataTransferReq_NoCallback) { MockMessageDispatcher mock_dispatcher; DataTransfer data_transfer(mock_dispatcher, std::nullopt, is_websocket_connected, @@ -33,13 +47,16 @@ TEST(DataTransferTest, HandleDataTransferReq_NoCallback) { DataTransferRequest request = create_example_request(); ocpp::Call call(request); + ocpp::EnhancedMessage enhanced_message; + enhanced_message.messageType = MessageType::DataTransfer; + enhanced_message.message = call; EXPECT_CALL(mock_dispatcher, dispatch_call_result(_)).WillOnce(Invoke([](const json& call_result) { auto response = call_result[ocpp::CALLRESULT_PAYLOAD].get(); EXPECT_EQ(response.status, DataTransferStatusEnum::UnknownVendorId); })); - data_transfer.handle_data_transfer_req(call); + data_transfer.handle_message(enhanced_message); } TEST(DataTransferTest, HandleDataTransferReq_WithCallback) { @@ -56,13 +73,17 @@ TEST(DataTransferTest, HandleDataTransferReq_WithCallback) { DataTransferRequest request = create_example_request(); ocpp::Call call(request); + ocpp::EnhancedMessage enhanced_message; + enhanced_message.messageType = MessageType::DataTransfer; + enhanced_message.message = call; + EXPECT_CALL(mock_dispatcher, dispatch_call_result(_)).WillOnce(Invoke([](const json& call_result) { auto response = call_result[ocpp::CALLRESULT_PAYLOAD].get(); EXPECT_EQ(response.status, DataTransferStatusEnum::Accepted); })); - data_transfer.handle_data_transfer_req(call); + data_transfer.handle_message(enhanced_message); } TEST(DataTransferTest, DataTransferReq_NotConnected) {