Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
#ifndef REQUEST_HANDLER_INTERFACE_HPP
|
||||
#define REQUEST_HANDLER_INTERFACE_HPP
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <types/json_rpc_api/json_rpc_api.hpp>
|
||||
|
||||
namespace RPCDataTypes = types::json_rpc_api;
|
||||
|
||||
namespace request_interface {
|
||||
|
||||
// --- RequestHandlerInterface ---
|
||||
// This interface is used to handle synchronous requests from the RPC API.
|
||||
class RequestHandlerInterface {
|
||||
public:
|
||||
virtual ~RequestHandlerInterface() = default;
|
||||
virtual RPCDataTypes::ErrorResObj set_charging_allowed(const int32_t evse_index, bool charging_allowed) = 0;
|
||||
virtual RPCDataTypes::ErrorResObj set_ac_charging(const int32_t evse_index, bool charging_allowed, bool max_current,
|
||||
std::optional<int> phase_count) = 0;
|
||||
virtual RPCDataTypes::ErrorResObj set_ac_charging_current(const int32_t evse_index, float max_current) = 0;
|
||||
virtual RPCDataTypes::ErrorResObj set_ac_charging_phase_count(const int32_t evse_index, int phase_count) = 0;
|
||||
virtual RPCDataTypes::ErrorResObj set_dc_charging(const int32_t evse_index, bool charging_allowed,
|
||||
float max_power) = 0;
|
||||
virtual RPCDataTypes::ErrorResObj set_dc_charging_power(const int32_t evse_index, float max_power) = 0;
|
||||
virtual RPCDataTypes::ErrorResObj enable_connector(const int32_t evse_index, int connector_id, bool enable,
|
||||
int priority) = 0;
|
||||
};
|
||||
|
||||
} // namespace request_interface
|
||||
|
||||
#endif // REQUEST_HANDLER_INTERFACE_HPP
|
||||
362
tools/EVerest-main/modules/API/RpcApi/rpc/RpcHandler.cpp
Normal file
362
tools/EVerest-main/modules/API/RpcApi/rpc/RpcHandler.cpp
Normal file
@@ -0,0 +1,362 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
|
||||
#include "RpcHandler.hpp"
|
||||
|
||||
#include <jsonrpccxx/client.hpp>
|
||||
#include <jsonrpccxx/common.hpp>
|
||||
#include <jsonrpccxx/server.hpp>
|
||||
#include <jsonrpccxx/typemapper.hpp>
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
#include "../helpers/Conversions.hpp" // For to_json() for nlohmann::json
|
||||
#include "../helpers/LimitDecimalPlaces.hpp"
|
||||
|
||||
namespace rpc {
|
||||
|
||||
template <typename T> struct is_optional : std::false_type {};
|
||||
|
||||
static const std::chrono::milliseconds REQ_COLLECTION_TIMEOUT(
|
||||
10); // Timeout for collecting client requests. After this timeout, the requests will be processed.
|
||||
static const std::chrono::milliseconds
|
||||
REQ_PROCESSING_TIMEOUT(50); // Timeout for processing requests. After this timeout, the request will be processed.
|
||||
|
||||
// Helper functions
|
||||
template <typename T> struct is_optional<std::optional<T>> : std::true_type {};
|
||||
|
||||
template <typename T> auto extract_param(const nlohmann::json& j) {
|
||||
if constexpr (is_optional<T>::value) {
|
||||
using InnerT = typename T::value_type;
|
||||
if (j.is_null()) {
|
||||
return std::optional<InnerT>{};
|
||||
} else {
|
||||
return std::optional<InnerT>{j.get<InnerT>()};
|
||||
}
|
||||
} else {
|
||||
return j.get<T>();
|
||||
}
|
||||
}
|
||||
|
||||
// json-rpc-cpp does not support optional parameters in method signatures
|
||||
// so we need to create our own get_handle function to handle methods with optional parameters correctly
|
||||
template <typename...> using void_t = void;
|
||||
|
||||
template <typename Default, template <typename...> class Op, typename... Args> struct detector {
|
||||
using value_t = std::false_type;
|
||||
using type = Default;
|
||||
};
|
||||
|
||||
template <template <typename...> class Op, typename... Args> struct detector<void_t<Op<Args...>>, Op, Args...> {
|
||||
using value_t = std::true_type;
|
||||
using type = Op<Args...>;
|
||||
};
|
||||
|
||||
template <template <typename...> class Op, typename... Args>
|
||||
using is_detected = typename detector<void, Op, Args...>::value_t;
|
||||
|
||||
template <typename T>
|
||||
using is_to_json_serializable = decltype(to_json(std::declval<nlohmann::json&>(), std::declval<T>()));
|
||||
|
||||
template <typename T> constexpr bool is_to_json_serializable_v = is_detected<is_to_json_serializable, T>::value;
|
||||
|
||||
template <typename T, typename MethodT, typename... ParamTypes, std::size_t... I>
|
||||
auto invoke_with_params_impl(T& instance, MethodT method, const json& params, std::index_sequence<I...>) {
|
||||
return (instance.*method)((extract_param<std::remove_reference_t<ParamTypes>>(params.at(I)))...);
|
||||
}
|
||||
|
||||
template <typename T, typename MethodT, typename... ParamTypes>
|
||||
auto invoke_with_params(T& instance, MethodT method, const json& params) {
|
||||
return invoke_with_params_impl<T, MethodT, ParamTypes...>(instance, method, params,
|
||||
std::index_sequence_for<ParamTypes...>{});
|
||||
}
|
||||
|
||||
template <typename T, typename ReturnType, typename... ParamTypes>
|
||||
MethodHandle get_handle(ReturnType (T::*method)(ParamTypes...), T& instance, int precision = 3) {
|
||||
return [&instance, method, precision](const json& params) -> json {
|
||||
if (!params.is_array()) {
|
||||
throw std::runtime_error("params must be array");
|
||||
}
|
||||
|
||||
constexpr size_t expected = sizeof...(ParamTypes);
|
||||
if (params.size() != expected) {
|
||||
throw std::runtime_error("invalid number of parameters");
|
||||
}
|
||||
|
||||
auto result = invoke_with_params<T, decltype(method), ParamTypes...>(instance, method, params);
|
||||
|
||||
if constexpr (std::is_same_v<ReturnType, void>) {
|
||||
return json(); // no return value
|
||||
} else if constexpr (std::is_same_v<ReturnType, nlohmann::json>) {
|
||||
return result; // return json directly
|
||||
} else if constexpr (is_to_json_serializable_v<ReturnType>) {
|
||||
nlohmann::json j;
|
||||
to_json(j, result);
|
||||
helpers::round_floats_in_json(j, precision);
|
||||
return j; // convert to json and round floats
|
||||
} else {
|
||||
return result; // fallback: no conversion to json possible, return as is
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
RpcHandler::RpcHandler(std::vector<std::shared_ptr<server::TransportInterface>> transport_interfaces,
|
||||
DataStoreCharger& dataobj,
|
||||
std::unique_ptr<request_interface::RequestHandlerInterface> request_handler, int precision) :
|
||||
m_transport_interfaces(std::move(transport_interfaces)),
|
||||
m_data_store(dataobj),
|
||||
m_methods_api(dataobj),
|
||||
m_methods_chargepoint(dataobj),
|
||||
m_methods_evse(dataobj, std::move(request_handler)),
|
||||
m_conn(m_transport_interfaces, m_api_hello_received, m_mtx),
|
||||
m_precision(precision) {
|
||||
init_rpc_api();
|
||||
init_transport_interfaces();
|
||||
m_notifications_evse = std::make_unique<notifications::Evse>(m_rpc_server, dataobj, m_precision);
|
||||
m_notifications_chargepoint = std::make_unique<notifications::ChargePoint>(m_rpc_server, dataobj, m_precision);
|
||||
}
|
||||
|
||||
void RpcHandler::init_rpc_api() {
|
||||
// Initialize the RPC API here
|
||||
m_methods_api.set_authentication_required(false);
|
||||
m_methods_api.set_api_version(API_VERSION);
|
||||
m_rpc_server = std::make_shared<JsonRpc2ServerWithClient>(m_conn);
|
||||
m_rpc_server->Add(methods::METHOD_API_HELLO, get_handle(&methods::Api::hello, m_methods_api, m_precision), {});
|
||||
m_rpc_server->Add(methods::METHOD_CHARGEPOINT_GET_EVSE_INFOS,
|
||||
get_handle(&methods::ChargePoint::getEVSEInfos, m_methods_chargepoint, m_precision), {});
|
||||
m_rpc_server->Add(methods::METHOD_CHARGEPOINT_GET_ACTIVE_ERRORS,
|
||||
get_handle(&methods::ChargePoint::getActiveErrors, m_methods_chargepoint, m_precision), {});
|
||||
m_rpc_server->Add(methods::METHOD_EVSE_GET_INFO, get_handle(&methods::Evse::get_info, m_methods_evse, m_precision),
|
||||
{"evse_index"});
|
||||
m_rpc_server->Add(methods::METHOD_EVSE_GET_STATUS,
|
||||
get_handle(&methods::Evse::get_status, m_methods_evse, m_precision), {"evse_index"});
|
||||
m_rpc_server->Add(methods::METHOD_EVSE_GET_HARDWARE_CAPABILITIES,
|
||||
get_handle(&methods::Evse::get_hardware_capabilities, m_methods_evse, m_precision),
|
||||
{"evse_index"});
|
||||
m_rpc_server->Add(methods::METHOD_EVSE_SET_CHARGING_ALLOWED,
|
||||
get_handle(&methods::Evse::set_charging_allowed, m_methods_evse, m_precision),
|
||||
{"evse_index", "charging_allowed"});
|
||||
m_rpc_server->Add(methods::METHOD_EVSE_GET_METER_DATA,
|
||||
get_handle(&methods::Evse::get_meter_data, m_methods_evse, m_precision), {"evse_index"});
|
||||
// TODO: m_rpc_server->Add(methods::METHOD_EVSE_SET_AC_CHARGING, (get_handle(&methods::Evse::set_ac_charging,
|
||||
// m_methods_evse, m_precision)),
|
||||
// {"evse_index", "charging_allowed", "max_current", "phase_count"});
|
||||
m_rpc_server->Add(methods::METHOD_EVSE_SET_AC_CHARGING_CURRENT,
|
||||
get_handle(&methods::Evse::set_ac_charging_current, m_methods_evse, m_precision),
|
||||
{"evse_index", "max_current"});
|
||||
m_rpc_server->Add(methods::METHOD_EVSE_SET_AC_CHARGING_PHASE_COUNT,
|
||||
get_handle(&methods::Evse::set_ac_charging_phase_count, m_methods_evse, m_precision),
|
||||
{"evse_index", "phase_count"});
|
||||
// TODO: m_rpc_server->Add(methods::METHOD_EVSE_SET_DC_CHARGING, (get_handle(&methods::Evse::set_dc_charging,
|
||||
// m_methods_evse, m_precision)),
|
||||
// {"evse_index", "charging_allowed", "max_power"});
|
||||
m_rpc_server->Add(methods::METHOD_EVSE_SET_DC_CHARGING_POWER,
|
||||
get_handle(&methods::Evse::set_dc_charging_power, m_methods_evse, m_precision),
|
||||
{"evse_index", "max_power"});
|
||||
m_rpc_server->Add(methods::METHOD_EVSE_ENABLE_CONNECTOR,
|
||||
get_handle(&methods::Evse::enable_connector, m_methods_evse, m_precision),
|
||||
{"evse_index", "connector_index", "enable", "priority"});
|
||||
}
|
||||
|
||||
void RpcHandler::init_transport_interfaces() {
|
||||
for (const auto& transport_interface : m_transport_interfaces) {
|
||||
if (!transport_interface) {
|
||||
throw std::runtime_error("Transport interface is null");
|
||||
}
|
||||
m_last_req_notification = std::chrono::steady_clock::now();
|
||||
|
||||
transport_interface->on_client_connected =
|
||||
[this, transport_interface](const server::TransportInterface::ClientId& client_id,
|
||||
const server::TransportInterface::Address& address) {
|
||||
this->client_connected(transport_interface, client_id, address);
|
||||
};
|
||||
|
||||
transport_interface->on_client_disconnected =
|
||||
[this, transport_interface](const server::TransportInterface::ClientId& client_id) {
|
||||
this->client_disconnected(transport_interface, client_id);
|
||||
};
|
||||
|
||||
transport_interface->on_data_available =
|
||||
[this, transport_interface](const server::TransportInterface::ClientId& client_id,
|
||||
const server::TransportInterface::Data& data) {
|
||||
this->data_available(transport_interface, client_id, data);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void RpcHandler::client_connected(const std::shared_ptr<server::TransportInterface>& transport_interface,
|
||||
const server::TransportInterface::ClientId& client_id,
|
||||
[[maybe_unused]] const server::TransportInterface::Address& address) {
|
||||
// In case of a new client, we expect that the client will send an API.Hello request within 5 seconds.
|
||||
// The API.Hello request is a handshake message sent by the client to establish a connection and verify
|
||||
// compatibility. If the API.Hello request is not received within the timeout period, the connection will be
|
||||
// terminated.
|
||||
|
||||
// Launch a detached thread to wait for the client hello message
|
||||
std::thread([this, client_id, transport_interface]() {
|
||||
std::unique_lock<std::mutex> lock(m_mtx);
|
||||
if (m_cv_api_hello.wait_for(lock, CLIENT_HELLO_TIMEOUT, [this, client_id] {
|
||||
return m_api_hello_received.find(client_id) != m_api_hello_received.end();
|
||||
}) == false) {
|
||||
// Client did not send hello, close connection
|
||||
if (transport_interface) {
|
||||
transport_interface->kill_client_connection(client_id, "Disconnected due to timeout");
|
||||
} else {
|
||||
// Log the error instead of throwing an exception in a detached thread
|
||||
// to avoid undefined behavior.
|
||||
EVLOG_error << "Transport interface is null during client connection timeout handling";
|
||||
}
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void RpcHandler::client_disconnected(const std::shared_ptr<server::TransportInterface>& transport_interface,
|
||||
const server::TransportInterface::ClientId& client_id) {
|
||||
if (transport_interface) {
|
||||
std::lock_guard<std::mutex> lock(m_mtx);
|
||||
m_api_hello_received.erase(client_id);
|
||||
} else {
|
||||
// Log the error instead of throwing an exception in a detached thread
|
||||
// to avoid undefined behavior.
|
||||
EVLOG_error << "Transport interface is null during client disconnection handling";
|
||||
}
|
||||
// Clean up the client data
|
||||
std::lock_guard<std::mutex> lock(m_mtx);
|
||||
auto it = messages.find(client_id);
|
||||
if (it != messages.end()) {
|
||||
messages.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void RpcHandler::data_available(const std::shared_ptr<server::TransportInterface>& transport_interface,
|
||||
const server::TransportInterface::ClientId& client_id,
|
||||
const server::TransportInterface::Data& data) {
|
||||
// Handle request
|
||||
using namespace std::chrono;
|
||||
|
||||
try {
|
||||
auto now = steady_clock::now();
|
||||
|
||||
nlohmann::json request = nlohmann::json::parse(data);
|
||||
if (request.is_null()) {
|
||||
EVLOG_error << "Received null request from client " << client_id;
|
||||
return;
|
||||
}
|
||||
// Store message in a map with client_id as key
|
||||
std::lock_guard<std::mutex> lock(m_mtx);
|
||||
messages[client_id].data.push_back(request);
|
||||
messages[client_id].transport_interface = transport_interface;
|
||||
EVLOG_debug << "Received message from client " << client_id << ": " << request.dump();
|
||||
|
||||
auto elapsed = duration_cast<milliseconds>(now - m_last_req_notification);
|
||||
if (elapsed >= REQ_COLLECTION_TIMEOUT) {
|
||||
m_last_req_notification = now; // restart timer
|
||||
m_cv_data_available.notify_all();
|
||||
}
|
||||
} catch (const nlohmann::json::parse_error& e) {
|
||||
EVLOG_error << "Failed to parse JSON request from client " << client_id << ": " << e.what();
|
||||
} catch (const std::exception& e) {
|
||||
EVLOG_error << "Exception occurred while handling data available: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void RpcHandler::process_client_requests() {
|
||||
while (m_is_running) {
|
||||
std::unique_lock<std::mutex> lock(m_mtx);
|
||||
// Wait for data to be available or timeout
|
||||
m_cv_data_available.wait_for(lock, REQ_PROCESSING_TIMEOUT, [this]() {
|
||||
// Iterate over all clients and check if data is available
|
||||
for (const auto& [client_id, client_req] : messages) {
|
||||
if (!client_req.data.empty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Process requests for each client
|
||||
bool all_requests_processed; // Flag to check if all requests are processed
|
||||
do {
|
||||
all_requests_processed = true;
|
||||
for (auto& [client_id, client_req] : messages) {
|
||||
if (client_req.data.empty()) {
|
||||
continue; // Skip if no data available
|
||||
}
|
||||
// Process the data for this client
|
||||
auto transport_interface = client_req.transport_interface;
|
||||
|
||||
if (!transport_interface) {
|
||||
EVLOG_error << "Skip data. Transport interface is null for client " << client_id;
|
||||
continue; // Skip if transport interface is null
|
||||
}
|
||||
|
||||
// Get the first request from the client
|
||||
nlohmann::json request = client_req.data.front();
|
||||
client_req.data.pop_front(); // Remove the processed request
|
||||
|
||||
// Check if next request is available
|
||||
if (client_req.data.empty()) {
|
||||
all_requests_processed = false;
|
||||
}
|
||||
|
||||
// Check if the request is an API.Hello request
|
||||
if (is_api_hello_req(client_id, request)) {
|
||||
// Notify condition variable to unblock the waiting thread
|
||||
m_cv_api_hello.notify_all();
|
||||
EVLOG_info << "API.Hello request received from client " << client_id;
|
||||
} else {
|
||||
// If it is not an API.Hello request, we need to check if the client has already sent an API.Hello
|
||||
// request, if not, close the connection
|
||||
if (m_api_hello_received.find(client_id) == m_api_hello_received.end()) {
|
||||
EVLOG_debug << "Client " << client_id << " did not send API.Hello request. Closing connection.";
|
||||
transport_interface->kill_client_connection(client_id,
|
||||
"Disconnected due to missing API.Hello request");
|
||||
continue; // Skip processing this request
|
||||
}
|
||||
}
|
||||
|
||||
// Process the request in a detached thread, because HandleRequest is blocking until the response is
|
||||
// received
|
||||
std::thread([this, transport_interface, client_id, request]() {
|
||||
// Call the RPC server with the request
|
||||
std::string res = m_rpc_server->HandleRequest(request.dump());
|
||||
// Send the response back to the client
|
||||
transport_interface->send_data(client_id, res);
|
||||
EVLOG_debug << "Sent response to client " << client_id << ": " << res;
|
||||
}).detach();
|
||||
}
|
||||
} while (!all_requests_processed && m_is_running);
|
||||
}
|
||||
}
|
||||
|
||||
void RpcHandler::start_server() {
|
||||
m_is_running = true;
|
||||
// Start all transport interfaces
|
||||
for (const auto& transport_interface : m_transport_interfaces) {
|
||||
if (!transport_interface->start_server()) {
|
||||
throw std::runtime_error("Failed to start transport interface server");
|
||||
}
|
||||
}
|
||||
|
||||
// Start RPC receiver thread
|
||||
m_rpc_recv_thread = std::thread([this]() { this->process_client_requests(); });
|
||||
}
|
||||
|
||||
void RpcHandler::stop_server() {
|
||||
m_is_running = false;
|
||||
// Notify all threads to stop
|
||||
m_cv_data_available.notify_all();
|
||||
m_cv_api_hello.notify_all();
|
||||
|
||||
// Wait for the RPC receiver thread to finish
|
||||
if (m_rpc_recv_thread.joinable()) {
|
||||
m_rpc_recv_thread.join();
|
||||
}
|
||||
|
||||
for (const auto& transport_interface : m_transport_interfaces) {
|
||||
if (!transport_interface->stop_server()) {
|
||||
throw std::runtime_error("Failed to stop transport interface server");
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace rpc
|
||||
150
tools/EVerest-main/modules/API/RpcApi/rpc/RpcHandler.hpp
Normal file
150
tools/EVerest-main/modules/API/RpcApi/rpc/RpcHandler.hpp
Normal file
@@ -0,0 +1,150 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
#ifndef RPCHANDLER_HPP
|
||||
#define RPCHANDLER_HPP
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <jsonrpccxx/client.hpp>
|
||||
#include <jsonrpccxx/server.hpp>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#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<server::TransportInterface> transport_interface;
|
||||
std::deque<nlohmann::json> data; // Queue of requests
|
||||
};
|
||||
|
||||
class ClientConnector : public jsonrpccxx::IClientConnector {
|
||||
public:
|
||||
explicit ClientConnector(std::vector<std::shared_ptr<TransportInterface>>& interfaces,
|
||||
std::unordered_map<TransportInterface::ClientId, bool>& 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<uint8_t> notif_char_array{notification.begin(), notification.end()};
|
||||
std::vector<TransportInterface::ClientId> recipients;
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<TransportInterface::ClientId, bool>& hello_received;
|
||||
std::vector<std::shared_ptr<TransportInterface>>& 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 <typename T> 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<std::shared_ptr<server::TransportInterface>> transport_interfaces, DataStoreCharger& dataobj,
|
||||
std::unique_ptr<request_interface::RequestHandlerInterface> 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<server::TransportInterface>& transport_interfaces,
|
||||
const TransportInterface::ClientId& client_id, const TransportInterface::Address& address);
|
||||
void client_disconnected(const std::shared_ptr<server::TransportInterface>& transport_interfaces,
|
||||
const server::TransportInterface::ClientId& client_id);
|
||||
void data_available(const std::shared_ptr<server::TransportInterface>& 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<std::shared_ptr<TransportInterface>> 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<TransportInterface::ClientId, bool> m_api_hello_received;
|
||||
std::shared_ptr<JsonRpc2ServerWithClient> m_rpc_server;
|
||||
std::unordered_map<TransportInterface::ClientId, ClientReq> messages;
|
||||
std::chrono::steady_clock::time_point m_last_req_notification; // Last tick time
|
||||
std::thread m_rpc_recv_thread;
|
||||
std::atomic<bool> 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<notifications::ChargePoint> m_notifications_chargepoint;
|
||||
std::unique_ptr<notifications::Evse> m_notifications_evse;
|
||||
int m_precision = 3;
|
||||
};
|
||||
} // namespace rpc
|
||||
|
||||
#endif // RPCHANDLER_HPP
|
||||
55
tools/EVerest-main/modules/API/RpcApi/rpc/methods/Api.cpp
Normal file
55
tools/EVerest-main/modules/API/RpcApi/rpc/methods/Api.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
|
||||
#include "Api.hpp"
|
||||
|
||||
using namespace data;
|
||||
|
||||
namespace methods {
|
||||
|
||||
RPCDataTypes::HelloResObj Api::hello() {
|
||||
RPCDataTypes::HelloResObj res{};
|
||||
// check if data is valid
|
||||
const auto _chargerinfo = m_dataobj.chargerinfo.get_data();
|
||||
if (not _chargerinfo.has_value()) {
|
||||
throw std::runtime_error("Data is not valid");
|
||||
}
|
||||
res.charger_info = _chargerinfo.value();
|
||||
res.authentication_required = is_authentication_required();
|
||||
res.api_version = get_api_version();
|
||||
res.everest_version = m_dataobj.everest_version;
|
||||
if (m_authenticated.has_value()) {
|
||||
res.authenticated = m_authenticated.value();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void Api::set_authentication_required(bool required) {
|
||||
m_authentication_required = required;
|
||||
}
|
||||
|
||||
bool Api::is_authentication_required() const {
|
||||
return m_authentication_required;
|
||||
}
|
||||
|
||||
void Api::set_api_version(const std::string& version) {
|
||||
m_api_version = version;
|
||||
}
|
||||
|
||||
const std::string& Api::get_api_version() const {
|
||||
return m_api_version;
|
||||
}
|
||||
|
||||
void Api::set_authenticated(bool authenticated) {
|
||||
m_authenticated = authenticated;
|
||||
}
|
||||
|
||||
bool Api::is_authenticated() const {
|
||||
if (m_authenticated.has_value()) {
|
||||
return m_authenticated.value();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace methods
|
||||
48
tools/EVerest-main/modules/API/RpcApi/rpc/methods/Api.hpp
Normal file
48
tools/EVerest-main/modules/API/RpcApi/rpc/methods/Api.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
#ifndef METHODS_API_HPP
|
||||
#define METHODS_API_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "../../data/DataStore.hpp"
|
||||
|
||||
using namespace data;
|
||||
|
||||
namespace RPCDataTypes = types::json_rpc_api;
|
||||
|
||||
namespace methods {
|
||||
|
||||
static const std::string METHOD_API_HELLO = "API.Hello";
|
||||
|
||||
/// This class includes all methods of the API namespace.
|
||||
/// It contains the data object and the methods to access it.
|
||||
class Api {
|
||||
public:
|
||||
// Constructor and Destructor
|
||||
Api() = delete;
|
||||
explicit Api(DataStoreCharger& dataobj) : m_dataobj(dataobj), m_authentication_required(false){};
|
||||
|
||||
~Api() = default;
|
||||
|
||||
// Methods
|
||||
RPCDataTypes::HelloResObj hello();
|
||||
void set_authentication_required(bool required);
|
||||
bool is_authentication_required() const;
|
||||
void set_api_version(const std::string& version);
|
||||
const std::string& get_api_version() const;
|
||||
void set_authenticated(bool authenticated);
|
||||
bool is_authenticated() const;
|
||||
|
||||
private:
|
||||
DataStoreCharger& m_dataobj;
|
||||
bool m_authentication_required;
|
||||
// optional
|
||||
std::optional<bool> m_authenticated;
|
||||
std::string m_api_version;
|
||||
};
|
||||
|
||||
} // namespace methods
|
||||
|
||||
#endif // METHODS_API_HPP
|
||||
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "ChargePoint.hpp"
|
||||
|
||||
namespace methods {
|
||||
|
||||
RPCDataTypes::ChargePointGetEVSEInfosResObj ChargePoint::getEVSEInfos() {
|
||||
RPCDataTypes::ChargePointGetEVSEInfosResObj res{};
|
||||
// Iterate over all EVSEs and add the EVSEInfo objects to the response
|
||||
for (const auto& evse : m_dataobj.evses) {
|
||||
if (const auto _data = evse->evseinfo.get_data(); _data.has_value()) {
|
||||
res.infos.push_back(_data.value());
|
||||
}
|
||||
}
|
||||
|
||||
// Error handling
|
||||
if (res.infos.empty()) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorNoDataAvailable;
|
||||
} else {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::NoError;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
RPCDataTypes::ChargePointGetActiveErrorsResObj ChargePoint::getActiveErrors() {
|
||||
RPCDataTypes::ChargePointGetActiveErrorsResObj res{};
|
||||
|
||||
res.active_errors = m_dataobj.chargererrors.get_data().value_or(std::vector<RPCDataTypes::ErrorObj>{});
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::NoError;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace methods
|
||||
@@ -0,0 +1,37 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
#ifndef METHODS_CHARGEPOINT_HPP
|
||||
#define METHODS_CHARGEPOINT_HPP
|
||||
|
||||
#include "../../data/DataStore.hpp"
|
||||
|
||||
using namespace data;
|
||||
|
||||
namespace RPCDataTypes = types::json_rpc_api;
|
||||
|
||||
namespace methods {
|
||||
|
||||
static const std::string METHOD_CHARGEPOINT_GET_EVSE_INFOS = "ChargePoint.GetEVSEInfos";
|
||||
static const std::string METHOD_CHARGEPOINT_GET_ACTIVE_ERRORS = "ChargePoint.GetActiveErrors";
|
||||
|
||||
/// This class includes all methods of the ChargePoint namespace.
|
||||
/// It contains the data object and the methods to access it.
|
||||
class ChargePoint {
|
||||
public:
|
||||
// Constructor and Destructor
|
||||
ChargePoint() = delete;
|
||||
explicit ChargePoint(DataStoreCharger& dataobj) : m_dataobj(dataobj){};
|
||||
|
||||
~ChargePoint() = default;
|
||||
|
||||
// Methods
|
||||
RPCDataTypes::ChargePointGetEVSEInfosResObj getEVSEInfos();
|
||||
RPCDataTypes::ChargePointGetActiveErrorsResObj getActiveErrors();
|
||||
|
||||
private:
|
||||
DataStoreCharger& m_dataobj;
|
||||
};
|
||||
|
||||
} // namespace methods
|
||||
|
||||
#endif // METHODS_CHARGEPOINT_HPP
|
||||
206
tools/EVerest-main/modules/API/RpcApi/rpc/methods/Evse.cpp
Normal file
206
tools/EVerest-main/modules/API/RpcApi/rpc/methods/Evse.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
|
||||
#include "Evse.hpp"
|
||||
|
||||
namespace methods {
|
||||
|
||||
RPCDataTypes::EVSEGetInfoResObj Evse::get_info(const int32_t evse_index) {
|
||||
RPCDataTypes::EVSEGetInfoResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
|
||||
const auto data = evse->evseinfo.get_data();
|
||||
if (not data.has_value()) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorNoDataAvailable;
|
||||
return res;
|
||||
}
|
||||
|
||||
res.info = data.value();
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::NoError;
|
||||
return res;
|
||||
}
|
||||
|
||||
RPCDataTypes::EVSEGetStatusResObj Evse::get_status(const int32_t evse_index) {
|
||||
RPCDataTypes::EVSEGetStatusResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
|
||||
const auto data = evse->evsestatus.get_data();
|
||||
if (not data.has_value()) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorNoDataAvailable;
|
||||
return res;
|
||||
}
|
||||
|
||||
res.status = data.value();
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::NoError;
|
||||
return res;
|
||||
}
|
||||
|
||||
RPCDataTypes::EVSEGetHardwareCapabilitiesResObj Evse::get_hardware_capabilities(const int32_t evse_index) {
|
||||
RPCDataTypes::EVSEGetHardwareCapabilitiesResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
|
||||
const auto data = evse->hardwarecapabilities.get_data();
|
||||
if (not data.has_value()) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorNoDataAvailable;
|
||||
return res;
|
||||
}
|
||||
|
||||
res.hardware_capabilities = data.value();
|
||||
return res;
|
||||
}
|
||||
|
||||
RPCDataTypes::ErrorResObj Evse::set_charging_allowed(const int32_t evse_index, bool charging_allowed) {
|
||||
RPCDataTypes::ErrorResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
return m_request_handler_ptr->set_charging_allowed(evse_index, charging_allowed);
|
||||
}
|
||||
|
||||
RPCDataTypes::EVSEGetMeterDataResObj Evse::get_meter_data(const int32_t evse_index) {
|
||||
RPCDataTypes::EVSEGetMeterDataResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
|
||||
const auto data = evse->meterdata.get_data();
|
||||
if (not data.has_value()) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorNoDataAvailable;
|
||||
return res;
|
||||
}
|
||||
res.meter_data = data.value();
|
||||
return res;
|
||||
}
|
||||
|
||||
RPCDataTypes::ErrorResObj Evse::set_ac_charging(const int32_t evse_index, bool charging_allowed, float max_current,
|
||||
std::optional<int> phase_count) {
|
||||
RPCDataTypes::ErrorResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
return m_request_handler_ptr->set_ac_charging(evse_index, charging_allowed, max_current, phase_count);
|
||||
}
|
||||
|
||||
RPCDataTypes::ErrorResObj Evse::set_ac_charging_current(const int32_t evse_index, float max_current) {
|
||||
RPCDataTypes::ErrorResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
return m_request_handler_ptr->set_ac_charging_current(evse_index, max_current);
|
||||
}
|
||||
|
||||
RPCDataTypes::ErrorResObj Evse::set_ac_charging_phase_count(const int32_t evse_index, int phase_count) {
|
||||
RPCDataTypes::ErrorResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Check if the requested phase count is equal to the current active phase count
|
||||
// If so, we can return a success response without making any changes. Phase switching is not
|
||||
// necessary in this case.
|
||||
auto evse_status = evse->evsestatus.get_data();
|
||||
if (evse_status.has_value() && evse_status.value().ac_charge_status.has_value()) {
|
||||
if (evse_status.value().ac_charge_status.value().evse_active_phase_count == phase_count) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::NoError;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// If phase switching must be performed and the hardware capabilities do not allow it
|
||||
// we return an error response.
|
||||
auto hardwarecapabilities = evse->hardwarecapabilities.get_data();
|
||||
if (hardwarecapabilities.has_value() && hardwarecapabilities.value().phase_switch_during_charging == false) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorOperationNotSupported;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Check if the requested phase count is within the allowed range
|
||||
if ((hardwarecapabilities.has_value() && phase_count < hardwarecapabilities.value().min_phase_count_export) ||
|
||||
phase_count > hardwarecapabilities.value().max_phase_count_export) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorOutOfRange;
|
||||
return res;
|
||||
}
|
||||
|
||||
// Check if phases are 1 or 3, otherwise return an error
|
||||
if (phase_count != 1 && phase_count != 3) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidParameter;
|
||||
return res;
|
||||
}
|
||||
|
||||
return m_request_handler_ptr->set_ac_charging_phase_count(evse_index, phase_count);
|
||||
}
|
||||
|
||||
RPCDataTypes::ErrorResObj Evse::set_dc_charging(const int32_t evse_index, bool charging_allowed, float max_power) {
|
||||
RPCDataTypes::ErrorResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
return m_request_handler_ptr->set_dc_charging(evse_index, charging_allowed, max_power);
|
||||
}
|
||||
|
||||
RPCDataTypes::ErrorResObj Evse::set_dc_charging_power(const int32_t evse_index, float max_power) {
|
||||
RPCDataTypes::ErrorResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
return m_request_handler_ptr->set_dc_charging_power(evse_index, max_power);
|
||||
}
|
||||
|
||||
RPCDataTypes::ErrorResObj Evse::enable_connector(const int32_t evse_index, int connector_index, bool enable,
|
||||
int priority) {
|
||||
RPCDataTypes::ErrorResObj res{};
|
||||
|
||||
const auto* evse = m_dataobj.get_evse_store(evse_index);
|
||||
if (!evse) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidEVSEIndex;
|
||||
return res;
|
||||
}
|
||||
// Iterate through the connectors to find the one with the given ID
|
||||
const auto connectors = evse->evseinfo.get_available_connectors();
|
||||
auto it = std::find_if(connectors.begin(), connectors.end(),
|
||||
[connector_index](const auto& connector) { return connector.index == connector_index; });
|
||||
// If not found, return an error
|
||||
if (it == connectors.end()) {
|
||||
res.error = RPCDataTypes::ResponseErrorEnum::ErrorInvalidConnectorIndex;
|
||||
return res;
|
||||
}
|
||||
return m_request_handler_ptr->enable_connector(evse_index, connector_index, enable, priority);
|
||||
}
|
||||
|
||||
} // namespace methods
|
||||
65
tools/EVerest-main/modules/API/RpcApi/rpc/methods/Evse.hpp
Normal file
65
tools/EVerest-main/modules/API/RpcApi/rpc/methods/Evse.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
#ifndef METHODS_EVSE_HPP
|
||||
#define METHODS_EVSE_HPP
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "../../data/DataStore.hpp"
|
||||
#include "../../rpc/RequestHandlerInterface.hpp"
|
||||
|
||||
namespace RPCDataTypes = types::json_rpc_api;
|
||||
|
||||
namespace methods {
|
||||
|
||||
static const std::string METHOD_EVSE_GET_INFO = "EVSE.GetInfo";
|
||||
static const std::string METHOD_EVSE_GET_STATUS = "EVSE.GetStatus";
|
||||
static const std::string METHOD_EVSE_GET_HARDWARE_CAPABILITIES = "EVSE.GetHardwareCapabilities";
|
||||
static const std::string METHOD_EVSE_SET_CHARGING_ALLOWED = "EVSE.SetChargingAllowed";
|
||||
static const std::string METHOD_EVSE_GET_METER_DATA = "EVSE.GetMeterData";
|
||||
static const std::string METHOD_EVSE_SET_AC_CHARGING = "EVSE.SetACCharging";
|
||||
static const std::string METHOD_EVSE_SET_AC_CHARGING_CURRENT = "EVSE.SetACChargingCurrent";
|
||||
static const std::string METHOD_EVSE_SET_AC_CHARGING_PHASE_COUNT = "EVSE.SetACChargingPhaseCount";
|
||||
static const std::string METHOD_EVSE_SET_DC_CHARGING = "EVSE.SetDCCharging";
|
||||
static const std::string METHOD_EVSE_SET_DC_CHARGING_POWER = "EVSE.SetDCChargingPower";
|
||||
static const std::string METHOD_EVSE_ENABLE_CONNECTOR = "EVSE.EnableConnector";
|
||||
|
||||
/// This class includes all methods of the EVSE namespace.
|
||||
/// It contains the data object and the methods to access it.
|
||||
class Evse {
|
||||
public:
|
||||
// Constructor and Destructor
|
||||
// Deleting the default constructor to ensure the class is always initialized with a DataStoreCharger object
|
||||
Evse() = delete;
|
||||
Evse(data::DataStoreCharger& dataobj, std::unique_ptr<request_interface::RequestHandlerInterface> req_handler) :
|
||||
m_dataobj(dataobj), m_request_handler_ptr(std::move(req_handler)) {
|
||||
}
|
||||
|
||||
~Evse() = default;
|
||||
|
||||
// Methods
|
||||
RPCDataTypes::EVSEGetInfoResObj get_info(const int32_t evse_index);
|
||||
RPCDataTypes::EVSEGetStatusResObj get_status(const int32_t evse_index);
|
||||
RPCDataTypes::EVSEGetHardwareCapabilitiesResObj get_hardware_capabilities(const int32_t evse_index);
|
||||
RPCDataTypes::ErrorResObj set_charging_allowed(const int32_t evse_index, bool charging_allowed);
|
||||
RPCDataTypes::EVSEGetMeterDataResObj get_meter_data(const int32_t evse_index);
|
||||
RPCDataTypes::ErrorResObj set_ac_charging(const int32_t evse_index, bool charging_allowed, float max_current,
|
||||
std::optional<int> phase_count);
|
||||
RPCDataTypes::ErrorResObj set_ac_charging_current(const int32_t evse_index, float max_current);
|
||||
RPCDataTypes::ErrorResObj set_ac_charging_phase_count(const int32_t evse_index, int phase_count);
|
||||
RPCDataTypes::ErrorResObj set_dc_charging(const int32_t evse_index, bool charging_allowed, float max_power);
|
||||
RPCDataTypes::ErrorResObj set_dc_charging_power(const int32_t evse_index, float max_power);
|
||||
RPCDataTypes::ErrorResObj enable_connector(const int32_t evse_index, int connector_id, bool enable, int priority);
|
||||
|
||||
private:
|
||||
// Reference to the DataStoreCharger object that holds and manages EVSE-related data.
|
||||
// This object is used to retrieve and update information about EVSEs, such as their status,
|
||||
// hardware capabilities, and meter data, ensuring consistent access to the underlying data store.
|
||||
data::DataStoreCharger& m_dataobj;
|
||||
// Reference to the RequestHandlerInterface object for handling requests
|
||||
std::unique_ptr<request_interface::RequestHandlerInterface> m_request_handler_ptr;
|
||||
};
|
||||
|
||||
} // namespace methods
|
||||
|
||||
#endif // METHODS_EVSE_HPP
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
|
||||
#include "ChargePoint.hpp"
|
||||
#include "../RpcHandler.hpp"
|
||||
|
||||
namespace notifications {
|
||||
|
||||
static const std::string NOTIFICATION_CHARGEPOINT_ACTIVE_ERRORS_CHANGED = "ChargePoint.ActiveErrorsChanged";
|
||||
|
||||
ChargePoint::ChargePoint(std::shared_ptr<rpc::JsonRpc2ServerWithClient> rpc_server, data::DataStoreCharger& dataobj,
|
||||
int precision) :
|
||||
m_dataobj(dataobj), m_rpc_server(std::move(rpc_server)), m_precision(precision) {
|
||||
// Register notification callbacks for the charger errors
|
||||
m_dataobj.chargererrors.register_notification_callback(
|
||||
[this](const std::vector<RPCDataTypes::ErrorObj>& active_errors) {
|
||||
this->send_active_errors_changed(active_errors);
|
||||
});
|
||||
};
|
||||
|
||||
// Notifications
|
||||
|
||||
void ChargePoint::send_active_errors_changed(const std::vector<RPCDataTypes::ErrorObj>& active_errors) {
|
||||
RPCDataTypes::ChargePointActiveErrorsChangedObj active_errors_changed;
|
||||
active_errors_changed.active_errors = active_errors;
|
||||
m_rpc_server->CallNotificationWithObject(NOTIFICATION_CHARGEPOINT_ACTIVE_ERRORS_CHANGED, active_errors_changed,
|
||||
m_precision);
|
||||
}
|
||||
|
||||
} // namespace notifications
|
||||
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
#ifndef NOTIFICATIONS_CHARGEPOINT_HPP
|
||||
#define NOTIFICATIONS_CHARGEPOINT_HPP
|
||||
|
||||
#include "../../data/DataStore.hpp"
|
||||
#include <jsonrpccxx/client.hpp>
|
||||
|
||||
namespace RPCDataTypes = types::json_rpc_api;
|
||||
|
||||
// forward declaration
|
||||
namespace rpc {
|
||||
class JsonRpc2ServerWithClient;
|
||||
}
|
||||
|
||||
namespace notifications {
|
||||
|
||||
class ChargePoint {
|
||||
public:
|
||||
// Constructor and Destructor
|
||||
// Deleting the default constructor to ensure the class is always initialized with a DataStoreCharger object
|
||||
ChargePoint() = delete;
|
||||
// This needs to take a copy of rpc_server for reference counting, not a reference to it
|
||||
ChargePoint(std::shared_ptr<rpc::JsonRpc2ServerWithClient> rpc_server, data::DataStoreCharger& dataobj,
|
||||
int precision = 3);
|
||||
~ChargePoint() = default;
|
||||
|
||||
// Notifications
|
||||
void send_active_errors_changed(const std::vector<RPCDataTypes::ErrorObj>& active_errors);
|
||||
|
||||
private:
|
||||
// Reference to the DataStoreCharger object that holds EVSE data
|
||||
data::DataStoreCharger& m_dataobj;
|
||||
std::shared_ptr<rpc::JsonRpc2ServerWithClient> m_rpc_server;
|
||||
int m_precision = 3;
|
||||
};
|
||||
|
||||
} // namespace notifications
|
||||
|
||||
#endif // NOTIFICATIONS_CHARGEPOINT_HPP
|
||||
@@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
#include "Evse.hpp"
|
||||
#include "../RpcHandler.hpp"
|
||||
|
||||
namespace notifications {
|
||||
|
||||
static const std::string NOTIFICATION_EVSE_HWCAPS_CHANGED = "EVSE.HardwareCapabilitiesChanged";
|
||||
static const std::string NOTIFICATION_EVSE_STATUS_CHANGED = "EVSE.StatusChanged";
|
||||
static const std::string NOTIFICATION_EVSE_METER_DATA_CHANGED = "EVSE.MeterDataChanged";
|
||||
|
||||
Evse::Evse(std::shared_ptr<rpc::JsonRpc2ServerWithClient> rpc_server, data::DataStoreCharger& dataobj, int precision) :
|
||||
m_dataobj(dataobj), m_rpc_server(std::move(rpc_server)), m_precision(precision) {
|
||||
for (const auto& evse : m_dataobj.evses) {
|
||||
const int32_t index = evse->evseinfo.get_index();
|
||||
evse->hardwarecapabilities.register_notification_callback(
|
||||
[this, index](const RPCDataTypes::HardwareCapabilitiesObj& data) {
|
||||
this->send_hardware_capabilities_changed(index, data);
|
||||
});
|
||||
evse->evsestatus.register_notification_callback(
|
||||
[this, index](const RPCDataTypes::EVSEStatusObj& data) { this->send_status_changed(index, data); });
|
||||
evse->meterdata.register_notification_callback(
|
||||
[this, index](const RPCDataTypes::MeterDataObj& data) { this->send_meterdata_changed(index, data); });
|
||||
}
|
||||
};
|
||||
|
||||
// Notifications
|
||||
|
||||
void Evse::send_hardware_capabilities_changed(int32_t evse_index, const RPCDataTypes::HardwareCapabilitiesObj& hwcap) {
|
||||
RPCDataTypes::EVSEHardwareCapabilitiesChangedObj hwcap_changed;
|
||||
hwcap_changed.evse_index = evse_index;
|
||||
hwcap_changed.hardware_capabilities = hwcap;
|
||||
m_rpc_server->CallNotificationWithObject(NOTIFICATION_EVSE_HWCAPS_CHANGED, hwcap_changed, m_precision);
|
||||
}
|
||||
void Evse::send_status_changed(int32_t evse_index, const RPCDataTypes::EVSEStatusObj& status) {
|
||||
RPCDataTypes::EVSEStatusChangedObj status_changed;
|
||||
status_changed.evse_index = evse_index;
|
||||
status_changed.evse_status = status;
|
||||
m_rpc_server->CallNotificationWithObject(NOTIFICATION_EVSE_STATUS_CHANGED, status_changed, m_precision);
|
||||
}
|
||||
void Evse::send_meterdata_changed(int32_t evse_index, const RPCDataTypes::MeterDataObj& meter) {
|
||||
RPCDataTypes::EVSEMeterDataChangedObj meter_changed;
|
||||
meter_changed.evse_index = evse_index;
|
||||
meter_changed.meter_data = meter;
|
||||
m_rpc_server->CallNotificationWithObject(NOTIFICATION_EVSE_METER_DATA_CHANGED, meter_changed, m_precision);
|
||||
}
|
||||
|
||||
} // namespace notifications
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright chargebyte GmbH and Contributors to EVerest
|
||||
#ifndef NOTIFICATIONS_EVSE_HPP
|
||||
#define NOTIFICATIONS_EVSE_HPP
|
||||
|
||||
#include "../../data/DataStore.hpp"
|
||||
#include <jsonrpccxx/client.hpp>
|
||||
|
||||
namespace RPCDataTypes = types::json_rpc_api;
|
||||
|
||||
// forward declaration
|
||||
namespace rpc {
|
||||
class JsonRpc2ServerWithClient;
|
||||
}
|
||||
|
||||
namespace notifications {
|
||||
|
||||
class Evse {
|
||||
public:
|
||||
// Constructor and Destructor
|
||||
// Deleting the default constructor to ensure the class is always initialized with a DataStoreCharger object
|
||||
Evse() = delete;
|
||||
// This needs to take a copy of rpc_server for reference counting, not a reference to it
|
||||
Evse(std::shared_ptr<rpc::JsonRpc2ServerWithClient> rpc_server, data::DataStoreCharger& dataobj, int precision = 3);
|
||||
~Evse() = default;
|
||||
|
||||
// Notifications
|
||||
|
||||
void send_hardware_capabilities_changed(int32_t evse_index, const RPCDataTypes::HardwareCapabilitiesObj& hwcap);
|
||||
void send_status_changed(int32_t evse_index, const RPCDataTypes::EVSEStatusObj& status);
|
||||
void send_meterdata_changed(int32_t evse_index, const RPCDataTypes::MeterDataObj& meter);
|
||||
|
||||
private:
|
||||
// Reference to the DataStoreCharger object that holds EVSE data
|
||||
data::DataStoreCharger& m_dataobj;
|
||||
std::shared_ptr<rpc::JsonRpc2ServerWithClient> m_rpc_server;
|
||||
int m_precision = 3;
|
||||
};
|
||||
|
||||
} // namespace notifications
|
||||
|
||||
#endif // NOTIFICATIONS_EVSE_HPP
|
||||
Reference in New Issue
Block a user