// SPDX-License-Identifier: Apache-2.0 // Copyright chargebyte GmbH and Contributors to EVerest #ifndef RPCHANDLER_HPP #define RPCHANDLER_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../helpers/LimitDecimalPlaces.hpp" #include "../server/TransportInterface.hpp" #include "RequestHandlerInterface.hpp" #include "methods/Api.hpp" #include "methods/ChargePoint.hpp" #include "methods/Evse.hpp" #include "notifications/ChargePoint.hpp" #include "notifications/Evse.hpp" using namespace server; using namespace jsonrpccxx; namespace rpc { static const std::chrono::seconds CLIENT_HELLO_TIMEOUT(5); // struct to store json data, plus the transport interface struct ClientReq { std::shared_ptr transport_interface; std::deque data; // Queue of requests }; class ClientConnector : public jsonrpccxx::IClientConnector { public: explicit ClientConnector(std::vector>& interfaces, std::unordered_map& api_hello_received, std::mutex& api_hello_mutex) : hello_received(api_hello_received), transport_interfaces(interfaces), hello_mutex(api_hello_mutex) { } std::string Send(const std::string& notification) override { const std::vector notif_char_array{notification.begin(), notification.end()}; std::vector recipients; { std::lock_guard lock(hello_mutex); recipients.reserve(hello_received.size()); for (const auto& rec : hello_received) { if (rec.second) { recipients.push_back(rec.first); } } } for (const auto& interface : transport_interfaces) { for (const auto& client_id : recipients) { interface->send_data(client_id, notif_char_array); } } return ""; } private: std::unordered_map& hello_received; std::vector>& transport_interfaces; std::mutex& hello_mutex; }; // Members class JsonRpc2ServerWithClient : public JsonRpc2Server, public JsonRpcClient { public: JsonRpc2ServerWithClient() = delete; explicit JsonRpc2ServerWithClient(ClientConnector& i) : JsonRpc2Server(), JsonRpcClient(i, version::v2){}; // helper to be able to put data object into caller // which is something which json-rpc-cxx should be doing template void CallNotificationWithObject(const std::string& name, const T& in, int precision = 3) { nlohmann::json j; nlohmann::to_json(j, in); helpers::round_floats_in_json(j, precision); CallNotificationNamed(name, j); } }; class RpcHandler { public: // Constructor and Destructor RpcHandler() = delete; // RpcHandler just needs a transport interface array RpcHandler(std::vector> transport_interfaces, DataStoreCharger& dataobj, std::unique_ptr request_handler, int precision = 3); ~RpcHandler() = default; // Methods void start_server(); void stop_server(); private: void init_rpc_api(); void init_transport_interfaces(); void client_connected(const std::shared_ptr& transport_interfaces, const TransportInterface::ClientId& client_id, const TransportInterface::Address& address); void client_disconnected(const std::shared_ptr& transport_interfaces, const server::TransportInterface::ClientId& client_id); void data_available(const std::shared_ptr& transport_interfaces, const TransportInterface::ClientId& client_id, const TransportInterface::Data& data); inline bool is_api_hello_req(const TransportInterface::ClientId& client_id, const nlohmann::json& request) { // Check if the request is a hello request if (request.contains("method") && request["method"] == methods::METHOD_API_HELLO) { // If it's a API.Hello request, we set the api_hello_received flag to true // and notify the condition variable to unblock the waiting thread m_api_hello_received[client_id] = true; return true; } return false; } void process_client_requests(); std::vector> m_transport_interfaces; DataStoreCharger& m_data_store; std::mutex m_mtx; std::condition_variable m_cv_api_hello; std::condition_variable m_cv_data_available; std::unordered_map m_api_hello_received; std::shared_ptr m_rpc_server; std::unordered_map messages; std::chrono::steady_clock::time_point m_last_req_notification; // Last tick time std::thread m_rpc_recv_thread; std::atomic m_is_running{false}; methods::Api m_methods_api; methods::ChargePoint m_methods_chargepoint; methods::Evse m_methods_evse; ClientConnector m_conn; std::unique_ptr m_notifications_chargepoint; std::unique_ptr m_notifications_evse; int m_precision = 3; }; } // namespace rpc #endif // RPCHANDLER_HPP