Tomáš Pecka | af7b704 | 2021-04-20 20:14:13 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2021 CESNET, https://photonics.cesnet.cz/ |
| 3 | * |
| 4 | * Written by Tomáš Pecka <tomas.pecka@cesnet.cz> |
| 5 | * |
| 6 | */ |
| 7 | |
| 8 | #include "LED.h" |
| 9 | |
| 10 | #include <utility> |
| 11 | #include "utils/io.h" |
Tomáš Pecka | 5be83e4 | 2021-04-21 17:26:40 +0200 | [diff] [blame] | 12 | #include "utils/libyang.h" |
Tomáš Pecka | af7b704 | 2021-04-20 20:14:13 +0200 | [diff] [blame] | 13 | #include "utils/log.h" |
| 14 | #include "utils/sysrepo.h" |
| 15 | |
| 16 | using namespace std::literals; |
| 17 | |
| 18 | namespace { |
| 19 | |
| 20 | const auto CZECHLIGHT_SYSTEM_MODULE_NAME = "czechlight-system"s; |
| 21 | const auto CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX = "/"s + CZECHLIGHT_SYSTEM_MODULE_NAME + ":leds/"s; |
| 22 | |
Tomáš Pecka | 5be83e4 | 2021-04-21 17:26:40 +0200 | [diff] [blame] | 23 | const auto UID_LED = "uid:blue"s; |
Tomáš Pecka | 63b2d65 | 2021-04-22 19:03:14 +0200 | [diff] [blame] | 24 | const auto POLL_INTERVAL = 125ms; |
Tomáš Pecka | af7b704 | 2021-04-20 20:14:13 +0200 | [diff] [blame] | 25 | } |
| 26 | |
| 27 | namespace velia::system { |
| 28 | |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 29 | LED::LED(::sysrepo::Connection srConn, std::filesystem::path sysfsLeds) |
Tomáš Pecka | af7b704 | 2021-04-20 20:14:13 +0200 | [diff] [blame] | 30 | : m_log(spdlog::get("system")) |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 31 | , m_srSession(srConn.sessionStart()) |
| 32 | , m_srSubscribe() |
Tomáš Pecka | 63b2d65 | 2021-04-22 19:03:14 +0200 | [diff] [blame] | 33 | , m_thrRunning(true) |
Tomáš Pecka | af7b704 | 2021-04-20 20:14:13 +0200 | [diff] [blame] | 34 | { |
| 35 | utils::ensureModuleImplemented(m_srSession, CZECHLIGHT_SYSTEM_MODULE_NAME, "2021-01-13"); |
| 36 | |
Tomáš Pecka | 63b2d65 | 2021-04-22 19:03:14 +0200 | [diff] [blame] | 37 | for (const auto& entry : std::filesystem::directory_iterator(sysfsLeds)) { |
| 38 | if (!std::filesystem::is_directory(entry.path())) { |
| 39 | continue; |
| 40 | } |
Tomáš Pecka | af7b704 | 2021-04-20 20:14:13 +0200 | [diff] [blame] | 41 | |
Tomáš Pecka | 63b2d65 | 2021-04-22 19:03:14 +0200 | [diff] [blame] | 42 | const auto fullPath = sysfsLeds / entry.path(); |
| 43 | uint32_t maxBrightness = velia::utils::readFileInt64(fullPath / "max_brightness"); |
| 44 | m_log->debug("Discovered LED '{}' (max brightness {})", std::string(entry.path().filename()), maxBrightness); |
| 45 | m_ledsMaxBrightness[fullPath] = maxBrightness; |
| 46 | } |
Tomáš Pecka | af7b704 | 2021-04-20 20:14:13 +0200 | [diff] [blame] | 47 | |
Tomáš Pecka | 63b2d65 | 2021-04-22 19:03:14 +0200 | [diff] [blame] | 48 | m_thr = std::thread(&LED::poll, this); |
Tomáš Pecka | af7b704 | 2021-04-20 20:14:13 +0200 | [diff] [blame] | 49 | |
Tomáš Pecka | 63b2d65 | 2021-04-22 19:03:14 +0200 | [diff] [blame] | 50 | const auto uidMaxBrightness = std::to_string(velia::utils::readFileInt64(sysfsLeds / UID_LED / "max_brightness")); |
| 51 | const auto triggerFile = sysfsLeds / UID_LED / "trigger"; |
| 52 | const auto brightnessFile = sysfsLeds / UID_LED / "brightness"; |
Tomáš Pecka | 5be83e4 | 2021-04-21 17:26:40 +0200 | [diff] [blame] | 53 | |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 54 | m_srSubscribe = m_srSession.onRPCAction( |
Jan Kundrát | b3e9998 | 2022-03-18 17:38:20 +0100 | [diff] [blame] | 55 | CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "uid", |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 56 | [this, uidMaxBrightness, triggerFile, brightnessFile](auto session, auto, auto, auto input, auto, auto, auto) { |
Jan Kundrát | b3e9998 | 2022-03-18 17:38:20 +0100 | [diff] [blame] | 57 | std::string val = utils::getValueAsString(utils::getUniqueSubtree(input, CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "uid/state").value()); |
Tomáš Pecka | 5be83e4 | 2021-04-21 17:26:40 +0200 | [diff] [blame] | 58 | |
| 59 | try { |
| 60 | if (val == "on") { |
| 61 | utils::writeFile(triggerFile, "none"); |
| 62 | utils::writeFile(brightnessFile, uidMaxBrightness); |
| 63 | } else if (val == "off") { |
| 64 | utils::writeFile(triggerFile, "none"); |
| 65 | utils::writeFile(brightnessFile, "0"); |
| 66 | } else if (val == "blinking") { |
| 67 | utils::writeFile(triggerFile, "timer"); |
| 68 | utils::writeFile(brightnessFile, uidMaxBrightness); |
| 69 | } |
| 70 | } catch (const std::invalid_argument& e) { |
| 71 | m_log->warn("Failed to set state of the UID LED: '{}'", e.what()); |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 72 | utils::setErrors(session, "Failed to set state of the UID LED"); |
| 73 | return sysrepo::ErrorCode::OperationFailed; |
Tomáš Pecka | 5be83e4 | 2021-04-21 17:26:40 +0200 | [diff] [blame] | 74 | } |
| 75 | |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 76 | return sysrepo::ErrorCode::Ok; |
Tomáš Pecka | 5be83e4 | 2021-04-21 17:26:40 +0200 | [diff] [blame] | 77 | }); |
Tomáš Pecka | af7b704 | 2021-04-20 20:14:13 +0200 | [diff] [blame] | 78 | } |
| 79 | |
Tomáš Pecka | 63b2d65 | 2021-04-22 19:03:14 +0200 | [diff] [blame] | 80 | LED::~LED() |
| 81 | { |
| 82 | m_thrRunning = false; |
| 83 | m_thr.join(); |
| 84 | } |
| 85 | |
| 86 | void LED::poll() const |
| 87 | { |
| 88 | while (m_thrRunning) { |
| 89 | std::map<std::string, std::string> data; |
| 90 | |
| 91 | for (const auto& [ledDirectory, maxBrightness] : m_ledsMaxBrightness) { |
| 92 | const auto deviceName = ledDirectory.filename(); |
| 93 | |
| 94 | try { |
| 95 | /* 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) |
| 96 | * See https://github.com/torvalds/linux/commit/af0bfab907a011e146304d20d81dddce4e4d62d0 |
| 97 | */ |
| 98 | const uint32_t brightness = velia::utils::readFileInt64(ledDirectory / "brightness"); |
| 99 | auto percent = brightness * 100 / maxBrightness; |
| 100 | |
| 101 | data[CZECHLIGHT_SYSTEM_LEDS_MODULE_PREFIX + "led[name='" + std::string(deviceName) + "']/brightness"] = std::to_string(percent); |
| 102 | } catch (const std::invalid_argument& e) { |
| 103 | m_log->warn("Failed reading state of the LED '{}': {}", std::string(deviceName), e.what()); |
| 104 | } |
| 105 | } |
| 106 | |
Václav Kubernát | 7efd6d5 | 2021-11-09 01:31:11 +0100 | [diff] [blame] | 107 | utils::valuesPush(data, {}, m_srSession, sysrepo::Datastore::Operational); |
Tomáš Pecka | 63b2d65 | 2021-04-22 19:03:14 +0200 | [diff] [blame] | 108 | |
| 109 | std::this_thread::sleep_for(POLL_INTERVAL); |
| 110 | } |
| 111 | } |
Tomáš Pecka | af7b704 | 2021-04-20 20:14:13 +0200 | [diff] [blame] | 112 | } |