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,156 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include <charge_bridge/gpio_bridge.hpp>
|
||||
#include <charge_bridge/utilities/logging.hpp>
|
||||
#include <charge_bridge/utilities/platform_utils.hpp>
|
||||
#include <charge_bridge/utilities/string.hpp>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <everest/io/event/fd_event_handler.hpp>
|
||||
#include <everest/io/mqtt/mqtt_client.hpp>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <protocol/cb_management.h>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace charge_bridge {
|
||||
using namespace std::chrono_literals;
|
||||
namespace mqtt = everest::lib::io::mqtt;
|
||||
|
||||
namespace {
|
||||
const int default_udp_timeout_ms = 1000;
|
||||
const int mqtt_reconnect_timeout_ms = 1000;
|
||||
} // namespace
|
||||
|
||||
gpio_bridge::gpio_bridge(gpio_config const& config) :
|
||||
m_udp(config.cb_remote, config.cb_port, default_udp_timeout_ms),
|
||||
m_mqtt(mqtt_reconnect_timeout_ms)
|
||||
|
||||
{
|
||||
m_identifier = config.cb + "/" + config.item;
|
||||
|
||||
m_heartbeat_timer.set_timeout(std::chrono::seconds(config.interval_s));
|
||||
|
||||
m_udp.set_rx_handler([this](auto const& data, auto&) { handle_udp_rx(data); });
|
||||
|
||||
m_udp.set_error_handler([this](auto id, auto const& msg) {
|
||||
utilities::print_error(m_identifier, "GPIO/UDP", id) << msg << std::endl;
|
||||
m_udp_on_error = id not_eq 0;
|
||||
});
|
||||
|
||||
m_receive_topic = "pionix/chargebridge/" + config.cb + "/gpio/output/";
|
||||
m_send_topic = "pionix/chargebridge/" + config.cb + "/gpio/input/";
|
||||
|
||||
m_mqtt.set_error_handler([this, config](int id, std::string const& msg) {
|
||||
utilities::print_error(m_identifier, "GPIO/MQTT", id) << msg << std::endl;
|
||||
m_mqtt_on_error = id not_eq 0;
|
||||
});
|
||||
|
||||
m_mqtt.set_callback_connect([this](auto&, auto, auto, auto const&) {
|
||||
m_mqtt.subscribe(
|
||||
m_receive_topic + "#", [this](auto&, auto const& payload) { dispatch(payload); },
|
||||
everest::lib::io::mqtt::mqtt_client::QoS::at_most_once);
|
||||
});
|
||||
|
||||
m_mqtt.connect(config.mqtt_bind, config.mqtt_remote, config.mqtt_port, config.mqtt_ping_interval_ms);
|
||||
|
||||
m_message.type = CbStructType::CST_HostToCb_Gpio;
|
||||
m_message.data.number_of_gpios = CB_NUMBER_OF_GPIOS;
|
||||
std::memset(m_message.data.gpio_values, 0, sizeof(m_message.data.gpio_values));
|
||||
}
|
||||
|
||||
gpio_bridge::~gpio_bridge() {
|
||||
}
|
||||
|
||||
bool gpio_bridge::register_events(everest::lib::io::event::fd_event_handler& handler) {
|
||||
auto result = handler.register_event_handler(&m_udp);
|
||||
result = handler.register_event_handler(&m_mqtt) && result;
|
||||
result = handler.register_event_handler(&m_heartbeat_timer, [this](auto&) { handle_heartbeat_timer(); }) && result;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool gpio_bridge::unregister_events(everest::lib::io::event::fd_event_handler& handler) {
|
||||
auto result = handler.unregister_event_handler(&m_udp);
|
||||
result = handler.unregister_event_handler(&m_mqtt) && result;
|
||||
result = handler.unregister_event_handler(&m_heartbeat_timer) && result;
|
||||
return result;
|
||||
}
|
||||
|
||||
void gpio_bridge::dispatch(everest::lib::io::mqtt::mqtt_client::message const& data) {
|
||||
auto& topic = data.topic;
|
||||
auto& payload = data.payload;
|
||||
auto operation = utilities::string_after_pattern(topic, m_receive_topic);
|
||||
uint16_t value = 0;
|
||||
int id = 0;
|
||||
|
||||
auto stous = [](std::string const& data) {
|
||||
auto val = stoi(data);
|
||||
if (val < 0 or val > std::numeric_limits<uint16_t>::max()) {
|
||||
throw std::range_error("");
|
||||
}
|
||||
return static_cast<uint16_t>(val);
|
||||
};
|
||||
|
||||
try {
|
||||
value = stous(payload);
|
||||
} catch (...) {
|
||||
std::cout << "INVALID DATA on MQTT for GPIO DATA" << std::endl;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
id = std::stoi(operation);
|
||||
} catch (...) {
|
||||
std::cout << "INVALID DATA on MQTT for GPIO ID" << std::endl;
|
||||
return;
|
||||
}
|
||||
if (id < 0 or id >= CB_NUMBER_OF_GPIOS) {
|
||||
std::cout << "INVALID GPIO ID" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
m_message.data.gpio_values[id] = value;
|
||||
send_udp();
|
||||
}
|
||||
|
||||
void gpio_bridge::send_mqtt(std::string const& topic, std::string const& message) {
|
||||
everest::lib::io::mqtt::mqtt_client::message payload;
|
||||
payload.topic = m_send_topic + topic;
|
||||
payload.payload = message;
|
||||
m_mqtt.publish(payload);
|
||||
}
|
||||
|
||||
void gpio_bridge::send_udp() {
|
||||
if (not m_udp_on_error) {
|
||||
everest::lib::io::udp::udp_payload payload;
|
||||
utilities::struct_to_vector(m_message, payload.buffer);
|
||||
m_udp.tx(payload);
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_bridge::handle_error_timer() {
|
||||
if (m_udp_on_error) {
|
||||
m_udp.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_bridge::handle_heartbeat_timer() {
|
||||
send_udp();
|
||||
}
|
||||
|
||||
void gpio_bridge::handle_udp_rx(everest::lib::io::udp::udp_payload const& payload) {
|
||||
CbManagementPacket<CbGpioPacket> data;
|
||||
if (payload.size() == sizeof(data)) {
|
||||
std::memcpy(&data, payload.buffer.data(), sizeof(data));
|
||||
for (std::size_t i = 0; i < sizeof(data.data.gpio_values) / sizeof(data.data.gpio_values[0]); ++i) {
|
||||
send_mqtt(std::to_string(i), std::to_string(data.data.gpio_values[i]));
|
||||
}
|
||||
} else {
|
||||
std::cout << "INVALID DATA SIZE in UDP RX of GPIO: " << payload.size() << " vs " << sizeof(data) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace charge_bridge
|
||||
Reference in New Issue
Block a user