blob: ce3abee7cf363ab12d712ebbbc97aaf47b92d1f6 [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(::sysrepo::Connection srConn, std::filesystem::path sysfsLeds)
: m_log(spdlog::get("system"))
, m_srSession(srConn.sessionStart())
, m_srSubscribe()
, 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 = m_srSession.onRPCAction(
(CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "uid").c_str(),
[this, uidMaxBrightness, triggerFile, brightnessFile](auto session, auto, auto, auto input, auto, auto, auto) {
std::string val = utils::getValueAsString(utils::getUniqueSubtree(input, (CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "uid/state").c_str()).value());
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());
utils::setErrors(session, "Failed to set state of the UID LED");
return sysrepo::ErrorCode::OperationFailed;
}
return sysrepo::ErrorCode::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, sysrepo::Datastore::Operational);
std::this_thread::sleep_for(POLL_INTERVAL);
}
}
}