veliad: Instantiate IETFHardware and Sysrepo callback

Instantiate Sysrepo class and IETFHardware in the veliad daemon. We also
created a Factory class for initializing hardware state data readers
(e.g. specifically for czechlight clearfog boards).

There is also a possibility to split veliad daemon into two. First daemon
would handle the "health" part and second daemon would be just a simple
Sysrepo operational data callback for the IETF Hardware part. However,
we then discussed the future of the daemon and we'd like the outputs to
reflect the current IETF Hardware state (i.e., temperatures are going
crazy, show red light). However, if this was split among two daemons it
would require some IPC to be implemented.
Having everything in one daemon would simplify things and does not bring
much complexity to veliad.

Change-Id: Ifde2cd92eace447959c68684a879771aaf2e45ea
diff --git a/src/Factory.cpp b/src/Factory.cpp
new file mode 100644
index 0000000..737e63d
--- /dev/null
+++ b/src/Factory.cpp
@@ -0,0 +1,52 @@
+#include "Factory.h"
+#include "health/outputs/LedSysfsDriver.h"
+#include "health/outputs/SlotWrapper.h"
+#include "health/outputs/callables.h"
+#include "ietf-hardware/IETFHardware.h"
+#include "ietf-hardware/sysfs/EMMC.h"
+#include "ietf-hardware/sysfs/HWMon.h"
+
+namespace velia::ietf_hardware {
+
+std::shared_ptr<IETFHardware> create(const std::string& applianceName)
+{
+    auto ietfHardware = std::make_shared<velia::ietf_hardware::IETFHardware>();
+
+    if (applianceName == "czechlight-clearfog") {
+        auto hwmonFans = std::make_shared<velia::ietf_hardware::sysfs::HWMon>("/sys/bus/i2c/devices/1-002e/hwmon/");
+        auto sysfsTempFront = std::make_shared<velia::ietf_hardware::sysfs::HWMon>("/sys/devices/platform/soc/soc:internal-regs/f1011100.i2c/i2c-1/1-002e/hwmon/");
+        auto sysfsTempCpu = std::make_shared<velia::ietf_hardware::sysfs::HWMon>("/sys/devices/virtual/thermal/thermal_zone0/");
+        auto sysfsTempMII0 = std::make_shared<velia::ietf_hardware::sysfs::HWMon>("/sys/devices/platform/soc/soc:internal-regs/f1072004.mdio/mdio_bus/f1072004.mdio-mii/f1072004.mdio-mii:00/hwmon/");
+        auto sysfsTempMII1 = std::make_shared<velia::ietf_hardware::sysfs::HWMon>("/sys/devices/platform/soc/soc:internal-regs/f1072004.mdio/mdio_bus/f1072004.mdio-mii/f1072004.mdio-mii:01/hwmon/");
+        auto emmc = std::make_shared<velia::ietf_hardware::sysfs::EMMC>("/sys/block/mmcblk0/device/");
+
+        ietfHardware->registerDataReader(velia::ietf_hardware::data_reader::StaticData("ne", std::nullopt, {{"class", "iana-hardware:chassis"}, {"mfg-name", "CESNET"s}})); // FIXME: We have an EEPROM at the PCB for storing these information, but it's so far unused. We could also use U-Boot env variables for this.
+        ietfHardware->registerDataReader(velia::ietf_hardware::data_reader::StaticData("ne:ctrl", "ne", {{"class", "iana-hardware:module"}}));
+        ietfHardware->registerDataReader(velia::ietf_hardware::data_reader::Fans("ne:fans", "ne", hwmonFans, 4));
+        ietfHardware->registerDataReader(velia::ietf_hardware::data_reader::SysfsTemperature("ne:ctrl:temperature-front", "ne:ctrl", sysfsTempFront, 1));
+        ietfHardware->registerDataReader(velia::ietf_hardware::data_reader::SysfsTemperature("ne:ctrl:temperature-cpu", "ne:ctrl", sysfsTempCpu, 1));
+        ietfHardware->registerDataReader(velia::ietf_hardware::data_reader::SysfsTemperature("ne:ctrl:temperature-internal-0", "ne:ctrl", sysfsTempMII0, 1));
+        ietfHardware->registerDataReader(velia::ietf_hardware::data_reader::SysfsTemperature("ne:ctrl:temperature-internal-1", "ne:ctrl", sysfsTempMII1, 1));
+        ietfHardware->registerDataReader(velia::ietf_hardware::data_reader::EMMC("ne:ctrl:emmc", "ne:ctrl", emmc));
+    } else {
+        throw std::runtime_error("Unknown appliance '" + applianceName + "'");
+    }
+
+    return ietfHardware;
+}
+
+}
+
+namespace velia::health {
+boost::signals2::SlotWrapper<void, health::State> createOutput(const std::string& applianceName)
+{
+    if (applianceName == "czechlight-clearfog") {
+        return boost::signals2::SlotWrapper<void, State>(std::make_shared<LedOutputCallback>(
+            std::make_shared<LedSysfsDriver>("/sys/class/leds/status:red/"),
+            std::make_shared<LedSysfsDriver>("/sys/class/leds/status:green/"),
+            std::make_shared<LedSysfsDriver>("/sys/class/leds/status:blue/")));
+    } else {
+        throw std::runtime_error("Unknown appliance '" + applianceName + "'");
+    }
+}
+}
diff --git a/src/Factory.h b/src/Factory.h
new file mode 100644
index 0000000..521bc4f
--- /dev/null
+++ b/src/Factory.h
@@ -0,0 +1,16 @@
+#pragma once
+#include <memory>
+#include "health/State.h"
+#include "health/outputs/SlotWrapper.h"
+
+namespace velia::ietf_hardware {
+class IETFHardware;
+}
+
+namespace velia::ietf_hardware {
+std::shared_ptr<ietf_hardware::IETFHardware> create(const std::string& applianceName);
+}
+
+namespace velia::health {
+boost::signals2::SlotWrapper<void, health::State> createOutput(const std::string& applianceName);
+}
diff --git a/src/health/manager/AbstractManager.h b/src/health/manager/AbstractManager.h
index 2f8ef57..9f2ac75 100644
--- a/src/health/manager/AbstractManager.h
+++ b/src/health/manager/AbstractManager.h
@@ -26,7 +26,7 @@
     virtual void updateState(void* input, State value) = 0;
 
     /** Output signal */
-    boost::signals2::signal<void(State)> m_outputSignal;
+    ::boost::signals2::signal<void(State)> m_outputSignal;
 };
 
 }
diff --git a/src/main.cpp b/src/main.cpp
index 058ecb4..e7cc6a8 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,12 +4,13 @@
 #include <sdbus-c++/sdbus-c++.h>
 #include <spdlog/sinks/ansicolor_sink.h>
 #include <spdlog/spdlog.h>
+#include <sysrepo-cpp/Session.hpp>
+#include "Factory.h"
 #include "VELIA_VERSION.h"
 #include "health/inputs/DbusSystemdInput.h"
 #include "health/manager/StateManager.h"
-#include "health/outputs/LedSysfsDriver.h"
-#include "health/outputs/SlotWrapper.h"
 #include "health/outputs/callables.h"
+#include "ietf-hardware/sysrepo/Sysrepo.h"
 #include "utils/exceptions.h"
 #include "utils/journal.h"
 #include "utils/log-init.h"
@@ -36,6 +37,7 @@
 
 Usage:
   veliad
+    [--appliance=<Model>]
     [--log-level=<Level>]
     [--systemd-ignore-unit=<Unit>]...
   veliad (-h | --help)
@@ -44,6 +46,7 @@
 Options:
   -h --help                         Show this screen.
   --version                         Show version.
+  --appliance=<Model>               Initialize IETF Hardware and outputs for specific appliance.
   --log-level=<N>                   Log level for everything [default: 3]
                                     (0 -> critical, 1 -> error, 2 -> warning, 3 -> info,
                                     4 -> debug, 5 -> trace)
@@ -73,6 +76,23 @@
         spdlog::get("main")->debug("Opening DBus connection");
         g_dbusConnection = sdbus::createSystemBusConnection();
 
+        spdlog::get("main")->debug("Opening Sysrepo connection");
+        auto srConn = std::make_shared<sysrepo::Connection>();
+        auto srSess = std::make_shared<sysrepo::Session>(srConn);
+        auto srSubscription = std::make_shared<sysrepo::Subscribe>(srSess);
+
+        // initialize ietf-hardware
+        spdlog::get("main")->debug("Initializing IETFHardware module");
+        std::shared_ptr<velia::ietf_hardware::IETFHardware> ietfHardware;
+        if (const auto& appliance = args["--appliance"]) {
+            ietfHardware = velia::ietf_hardware::create(appliance.asString());
+        } else {
+            ietfHardware = std::make_shared<velia::ietf_hardware::IETFHardware>();
+        }
+
+        spdlog::get("main")->debug("Initializing Sysrepo ietf-hardware callback");
+        auto sysrepoIETFHardware = velia::ietf_hardware::sysrepo::Sysrepo(srSubscription, ietfHardware);
+
         // Gracefully leave dbus event loop on SIGTERM
         struct sigaction sigact;
         memset(&sigact, 0, sizeof(sigact));
@@ -83,14 +103,14 @@
         spdlog::get("main")->debug("Starting DBus event loop");
         eventLoop = std::thread([] { g_dbusConnection->enterEventLoop(); });
 
+        // health
         auto manager = std::make_shared<velia::health::StateManager>();
 
         // output configuration
         spdlog::get("main")->debug("Initializing LED drivers");
-        manager->m_outputSignal.connect(velia::health::boost::signals2::SlotWrapper<void, velia::health::State>(std::make_shared<velia::health::LedOutputCallback>(
-            std::make_shared<velia::health::LedSysfsDriver>("/sys/class/leds/status:red/"),
-            std::make_shared<velia::health::LedSysfsDriver>("/sys/class/leds/status:green/"),
-            std::make_shared<velia::health::LedSysfsDriver>("/sys/class/leds/status:blue/"))));
+        if (const auto& appliance = args["--appliance"]) {
+            manager->m_outputSignal.connect(velia::health::createOutput(appliance.asString()));
+        }
 
         spdlog::get("main")->debug("All outputs initialized.");