Basic initial implementation

This is a base implementation which does following
1. create a daemon,
2. parse config file and create a virtual sensor object
3. create list of virtual sensors for given config.
4. Creates a systemd unit file

It currently supports constant params only and dbus params will be in
follow up patch.

Signed-off-by: Vijay Khemka <vijaykhemka@fb.com>
Change-Id: I89b2ffb8bff67bdbb3033071cba9f6e565a9af6e
diff --git a/virtualSensor.cpp b/virtualSensor.cpp
new file mode 100644
index 0000000..ef8543f
--- /dev/null
+++ b/virtualSensor.cpp
@@ -0,0 +1,210 @@
+#include "virtualSensor.hpp"
+
+#include "config.hpp"
+
+#include <phosphor-logging/log.hpp>
+#include <sdeventplus/event.hpp>
+
+#include <fstream>
+#include <iostream>
+
+static constexpr bool DEBUG = false;
+static constexpr auto busName = "xyz.openbmc_project.VirtualSensor";
+static constexpr auto sensorDbusPath = "/xyz/openbmc_project/sensors/";
+static constexpr uint8_t defaultHighThreshold = 100;
+static constexpr uint8_t defaultLowThreshold = 0;
+
+using namespace phosphor::logging;
+
+namespace phosphor
+{
+namespace virtualSensor
+{
+
+void printParams(const VirtualSensor::ParamMap& paramMap)
+{
+    for (const auto& p : paramMap)
+    {
+        const auto& p1 = p.first;
+        const auto& p2 = p.second;
+        auto val = p2->getParamValue();
+        std::cout << p1 << " = " << val << "\n";
+    }
+}
+
+double SensorParam::getParamValue()
+{
+    switch (paramType)
+    {
+        case constParam:
+            return value;
+            break;
+        default:
+            throw std::invalid_argument("param type not supported");
+    }
+}
+
+void VirtualSensor::initVirtualSensor(const Json& sensorConfig)
+{
+
+    static const Json empty{};
+
+    /* Get threshold values if defined in config */
+    auto threshold = sensorConfig.value("Threshold", empty);
+    if (!threshold.empty())
+    {
+        sensorThreshold.criticalHigh =
+            threshold.value("CriticalHigh", defaultHighThreshold);
+        sensorThreshold.criticalLow =
+            threshold.value("CriticalLow", defaultLowThreshold);
+        sensorThreshold.warningHigh =
+            threshold.value("WarningHigh", defaultHighThreshold);
+        sensorThreshold.warningLow =
+            threshold.value("WarningLow", defaultLowThreshold);
+    }
+
+    /* Set threshold value to dbus */
+    setSensorThreshold();
+
+    /* Get expression string */
+    exprStr = sensorConfig.value("Expression", "");
+
+    /* Get all the parameter listed in configuration */
+    auto params = sensorConfig.value("Params", empty);
+
+    /* Check for constant parameter */
+    const auto& consParams = params.value("ConstParam", empty);
+    if (!consParams.empty())
+    {
+        for (auto& j : consParams)
+        {
+            if (j.find("ParamName") != j.end())
+            {
+                auto paramPtr = std::make_unique<SensorParam>(j["Value"]);
+                paramMap.emplace(j["ParamName"], std::move(paramPtr));
+            }
+            else
+            {
+                /* Invalid configuration */
+                throw std::invalid_argument(
+                    "ParamName not found in configuration");
+            }
+        }
+    }
+
+    /* TODO: Check for dbus parameter */
+
+    /* Print all parameters for debug purpose only */
+    if (DEBUG)
+        printParams(paramMap);
+}
+
+void VirtualSensor::setSensorValue(double value)
+{
+    ValueIface::value(value);
+}
+
+void VirtualSensor::setSensorThreshold()
+{
+    CriticalInterface::criticalHigh(sensorThreshold.criticalHigh);
+    CriticalInterface::criticalLow(sensorThreshold.criticalLow);
+    WarningInterface::warningHigh(sensorThreshold.warningHigh);
+    WarningInterface::warningLow(sensorThreshold.warningLow);
+}
+
+/* TBD */
+void VirtualSensor::updateVirtualSensor()
+{}
+
+/** @brief Parsing Virtual Sensor config JSON file  */
+Json VirtualSensors::parseConfigFile(const std::string configFile)
+{
+    std::ifstream jsonFile(configFile);
+    if (!jsonFile.is_open())
+    {
+        log<level::ERR>("config JSON file not found",
+                        entry("FILENAME = %s", configFile.c_str()));
+        throw std::exception{};
+    }
+
+    auto data = Json::parse(jsonFile, nullptr, false);
+    if (data.is_discarded())
+    {
+        log<level::ERR>("config readings JSON parser failure",
+                        entry("FILENAME = %s", configFile.c_str()));
+        throw std::exception{};
+    }
+
+    return data;
+}
+
+void VirtualSensors::createVirtualSensors()
+{
+    static const Json empty{};
+
+    auto data = parseConfigFile(VIRTUAL_SENSOR_CONFIG_FILE);
+    // print values
+    if (DEBUG)
+        std::cout << "Config json data:\n" << data << "\n\n";
+
+    /* Get virtual sensors  config data */
+    for (const auto& j : data)
+    {
+        auto desc = j.value("Desc", empty);
+        if (!desc.empty())
+        {
+            std::string sensorType = desc.value("SensorType", "");
+            std::string name = desc.value("Name", "");
+
+            if (!name.empty() && !sensorType.empty())
+            {
+                std::string objPath(sensorDbusPath);
+                objPath += sensorType + "/" + name;
+
+                auto virtualSensorPtr =
+                    std::make_unique<VirtualSensor>(bus, objPath.c_str(), j);
+                virtualSensorsMap.emplace(name, std::move(virtualSensorPtr));
+
+                log<level::INFO>("Added a new virtual sensor",
+                                 entry("NAME = %s", name.c_str()));
+            }
+            else
+            {
+                log<level::ERR>("Sensor type or name not found in config file");
+            }
+        }
+        else
+        {
+            log<level::ERR>(
+                "Descriptor for new virtual sensor not found in config file");
+        }
+    }
+}
+
+} // namespace virtualSensor
+} // namespace phosphor
+
+/**
+ * @brief Main
+ */
+int main()
+{
+
+    // Get a default event loop
+    auto event = sdeventplus::Event::get_default();
+
+    // Get a handle to system dbus
+    auto bus = sdbusplus::bus::new_default();
+
+    // Create an virtual sensors object
+    phosphor::virtualSensor::VirtualSensors virtualSensors(bus);
+
+    // Request service bus name
+    bus.request_name(busName);
+
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+    event.loop();
+
+    return 0;
+}