blob: 85c0e9bd51e7f129995040a749a1bb5f957a9d0f [file] [log] [blame]
/*
* Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/
*
* Written by Tomáš Pecka <tomas.pecka@cesnet.cz>
*
*/
#include "LED.h"
#include <utility>
#include "utils/io.h"
#include "utils/libyang.h"
#include "utils/log.h"
#include "utils/sysrepo.h"
using namespace std::literals;
namespace {
const auto CZECHLIGHT_SYSTEM_MODULE_NAME = "czechlight-system"s;
const auto CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX = "/"s + CZECHLIGHT_SYSTEM_MODULE_NAME + ":leds/"s;
const auto UID_LED = "uid:blue"s;
const auto POLL_INTERVAL = 125ms;
}
namespace velia::system {
LED::LED(const std::shared_ptr<::sysrepo::Connection>& srConn, std::filesystem::path sysfsLeds)
: m_log(spdlog::get("system"))
, m_srSession(std::make_shared<::sysrepo::Session>(srConn))
, m_srSubscribe(std::make_shared<::sysrepo::Subscribe>(m_srSession))
, m_thrRunning(true)
{
utils::ensureModuleImplemented(m_srSession, CZECHLIGHT_SYSTEM_MODULE_NAME, "2021-01-13");
for (const auto& entry : std::filesystem::directory_iterator(sysfsLeds)) {
if (!std::filesystem::is_directory(entry.path())) {
continue;
}
const auto fullPath = sysfsLeds / entry.path();
uint32_t maxBrightness = velia::utils::readFileInt64(fullPath / "max_brightness");
m_log->debug("Discovered LED '{}' (max brightness {})", std::string(entry.path().filename()), maxBrightness);
m_ledsMaxBrightness[fullPath] = maxBrightness;
}
m_thr = std::thread(&LED::poll, this);
const auto uidMaxBrightness = std::to_string(velia::utils::readFileInt64(sysfsLeds / UID_LED / "max_brightness"));
const auto triggerFile = sysfsLeds / UID_LED / "trigger";
const auto brightnessFile = sysfsLeds / UID_LED / "brightness";
m_srSubscribe->rpc_subscribe_tree(
(CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "uid").c_str(),
[this, uidMaxBrightness, triggerFile, brightnessFile](auto session, auto, auto input, auto, auto, auto) {
std::string val = getValueAsString(getSubtree(input, (CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "uid/state").c_str()));
try {
if (val == "on") {
utils::writeFile(triggerFile, "none");
utils::writeFile(brightnessFile, uidMaxBrightness);
} else if (val == "off") {
utils::writeFile(triggerFile, "none");
utils::writeFile(brightnessFile, "0");
} else if (val == "blinking") {
utils::writeFile(triggerFile, "timer");
utils::writeFile(brightnessFile, uidMaxBrightness);
}
} catch (const std::invalid_argument& e) {
m_log->warn("Failed to set state of the UID LED: '{}'", e.what());
session->set_error("Failed to set state of the UID LED", nullptr);
return SR_ERR_OPERATION_FAILED;
}
return SR_ERR_OK;
});
}
LED::~LED()
{
m_thrRunning = false;
m_thr.join();
}
void LED::poll() const
{
while (m_thrRunning) {
std::map<std::string, std::string> data;
for (const auto& [ledDirectory, maxBrightness] : m_ledsMaxBrightness) {
const auto deviceName = ledDirectory.filename();
try {
/* actually just uint32_t is needed for the next two variables; but there is no harm in reading them as int64_t and downcasting them later (especially when the code for reading int64_t already exists)
* See https://github.com/torvalds/linux/commit/af0bfab907a011e146304d20d81dddce4e4d62d0
*/
const uint32_t brightness = velia::utils::readFileInt64(ledDirectory / "brightness");
auto percent = brightness * 100 / maxBrightness;
data[CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "led[name='" + std::string(deviceName) + "']/brightness"] = std::to_string(percent);
} catch (const std::invalid_argument& e) {
m_log->warn("Failed reading state of the LED '{}': {}", std::string(deviceName), e.what());
}
}
utils::valuesPush(data, {}, m_srSession, SR_DS_OPERATIONAL);
std::this_thread::sleep_for(POLL_INTERVAL);
}
}
}