1e73bd0a1SAndrew Jeffery #pragma once 2e73bd0a1SAndrew Jeffery 3e73bd0a1SAndrew Jeffery #include "dbus-sensor_config.h" 4e73bd0a1SAndrew Jeffery 5e73bd0a1SAndrew Jeffery #include "SensorPaths.hpp" 6e73bd0a1SAndrew Jeffery #include "Thresholds.hpp" 7e73bd0a1SAndrew Jeffery #include "Utils.hpp" 8e73bd0a1SAndrew Jeffery 918b6186eSEd Tanous #include <sdbusplus/asio/connection.hpp> 10e73bd0a1SAndrew Jeffery #include <sdbusplus/asio/object_server.hpp> 11e73bd0a1SAndrew Jeffery #include <sdbusplus/exception.hpp> 12e73bd0a1SAndrew Jeffery 1318b6186eSEd Tanous #include <array> 1418b6186eSEd Tanous #include <cerrno> 1518b6186eSEd Tanous #include <cmath> 1618b6186eSEd Tanous #include <cstddef> 1718b6186eSEd Tanous #include <cstdlib> 1818b6186eSEd Tanous #include <functional> 1918b6186eSEd Tanous #include <iostream> 20e73bd0a1SAndrew Jeffery #include <limits> 21e73bd0a1SAndrew Jeffery #include <memory> 22e73bd0a1SAndrew Jeffery #include <string> 2318b6186eSEd Tanous #include <utility> 24e73bd0a1SAndrew Jeffery #include <vector> 25e73bd0a1SAndrew Jeffery 26e73bd0a1SAndrew Jeffery constexpr size_t sensorFailedPollTimeMs = 5000; 27e73bd0a1SAndrew Jeffery 28e73bd0a1SAndrew Jeffery // Enable useful logging with sensor instrumentation 29e73bd0a1SAndrew Jeffery // This is intentionally not DEBUG, avoid clash with usage in .cpp files 30e73bd0a1SAndrew Jeffery constexpr bool enableInstrumentation = false; 31e73bd0a1SAndrew Jeffery 32e73bd0a1SAndrew Jeffery constexpr const char* sensorValueInterface = "xyz.openbmc_project.Sensor.Value"; 33e73bd0a1SAndrew Jeffery constexpr const char* valueMutabilityInterfaceName = 34e73bd0a1SAndrew Jeffery "xyz.openbmc_project.Sensor.ValueMutability"; 35e73bd0a1SAndrew Jeffery constexpr const char* availableInterfaceName = 36e73bd0a1SAndrew Jeffery "xyz.openbmc_project.State.Decorator.Availability"; 37e73bd0a1SAndrew Jeffery constexpr const char* operationalInterfaceName = 38e73bd0a1SAndrew Jeffery "xyz.openbmc_project.State.Decorator.OperationalStatus"; 39e73bd0a1SAndrew Jeffery constexpr const size_t errorThreshold = 5; 40e73bd0a1SAndrew Jeffery 41e73bd0a1SAndrew Jeffery struct SensorInstrumentation 42e73bd0a1SAndrew Jeffery { 43e73bd0a1SAndrew Jeffery // These are for instrumentation for debugging 44e73bd0a1SAndrew Jeffery int numCollectsGood = 0; 45e73bd0a1SAndrew Jeffery int numCollectsMiss = 0; 46e73bd0a1SAndrew Jeffery int numStreakGreats = 0; 47e73bd0a1SAndrew Jeffery int numStreakMisses = 0; 48e73bd0a1SAndrew Jeffery double minCollected = 0.0; 49e73bd0a1SAndrew Jeffery double maxCollected = 0.0; 50e73bd0a1SAndrew Jeffery }; 51e73bd0a1SAndrew Jeffery 52e73bd0a1SAndrew Jeffery struct SetSensorError : sdbusplus::exception_t 53e73bd0a1SAndrew Jeffery { nameSetSensorError54e73bd0a1SAndrew Jeffery const char* name() const noexcept override 55e73bd0a1SAndrew Jeffery { 56e73bd0a1SAndrew Jeffery return "xyz.openbmc_project.Common.Errors.NotAllowed"; 57e73bd0a1SAndrew Jeffery } descriptionSetSensorError58e73bd0a1SAndrew Jeffery const char* description() const noexcept override 59e73bd0a1SAndrew Jeffery { 60e73bd0a1SAndrew Jeffery return "Not allowed to set property value."; 61e73bd0a1SAndrew Jeffery } get_errnoSetSensorError62e73bd0a1SAndrew Jeffery int get_errno() const noexcept override 63e73bd0a1SAndrew Jeffery { 64e73bd0a1SAndrew Jeffery return EACCES; 65e73bd0a1SAndrew Jeffery } 66e73bd0a1SAndrew Jeffery }; 67e73bd0a1SAndrew Jeffery 68e73bd0a1SAndrew Jeffery struct Sensor 69e73bd0a1SAndrew Jeffery { SensorSensor70e73bd0a1SAndrew Jeffery Sensor(const std::string& name, 71e73bd0a1SAndrew Jeffery std::vector<thresholds::Threshold>&& thresholdData, 72e73bd0a1SAndrew Jeffery const std::string& configurationPath, const std::string& objectType, 73e73bd0a1SAndrew Jeffery bool isSettable, bool isMutable, const double max, const double min, 74e73bd0a1SAndrew Jeffery std::shared_ptr<sdbusplus::asio::connection>& conn, 75e73bd0a1SAndrew Jeffery PowerState readState = PowerState::always) : 76e73bd0a1SAndrew Jeffery name(sensor_paths::escapePathForDbus(name)), 77e73bd0a1SAndrew Jeffery configurationPath(configurationPath), 7855832f37SMatt Spinler configInterface(configInterfaceName(objectType)), 79e73bd0a1SAndrew Jeffery isSensorSettable(isSettable), isValueMutable(isMutable), maxValue(max), 80e73bd0a1SAndrew Jeffery minValue(min), thresholds(std::move(thresholdData)), 81e73bd0a1SAndrew Jeffery hysteresisTrigger((max - min) * 0.01), 82e73bd0a1SAndrew Jeffery hysteresisPublish((max - min) * 0.0001), dbusConnection(conn), 83e73bd0a1SAndrew Jeffery readState(readState), 84e73bd0a1SAndrew Jeffery instrumentation(enableInstrumentation 85e73bd0a1SAndrew Jeffery ? std::make_unique<SensorInstrumentation>() 86e73bd0a1SAndrew Jeffery : nullptr) 87e73bd0a1SAndrew Jeffery {} 88e73bd0a1SAndrew Jeffery virtual ~Sensor() = default; 89201a1015SEd Tanous virtual void checkThresholds() = 0; 90e73bd0a1SAndrew Jeffery std::string name; 91e73bd0a1SAndrew Jeffery std::string configurationPath; 9255832f37SMatt Spinler std::string configInterface; 93e73bd0a1SAndrew Jeffery bool isSensorSettable; 94e73bd0a1SAndrew Jeffery 95e73bd0a1SAndrew Jeffery /* A flag indicates if properties of xyz.openbmc_project.Sensor.Value 96e73bd0a1SAndrew Jeffery * interface are mutable. If mutable, then 97e73bd0a1SAndrew Jeffery * xyz.openbmc_project.Sensor.ValueMutability interface will be 98e73bd0a1SAndrew Jeffery * instantiated. 99e73bd0a1SAndrew Jeffery */ 100e73bd0a1SAndrew Jeffery bool isValueMutable; 101e73bd0a1SAndrew Jeffery double maxValue; 102e73bd0a1SAndrew Jeffery double minValue; 103e73bd0a1SAndrew Jeffery std::vector<thresholds::Threshold> thresholds; 104e73bd0a1SAndrew Jeffery std::shared_ptr<sdbusplus::asio::dbus_interface> sensorInterface; 105e73bd0a1SAndrew Jeffery std::shared_ptr<sdbusplus::asio::dbus_interface> association; 106e73bd0a1SAndrew Jeffery std::shared_ptr<sdbusplus::asio::dbus_interface> availableInterface; 107e73bd0a1SAndrew Jeffery std::shared_ptr<sdbusplus::asio::dbus_interface> operationalInterface; 108e73bd0a1SAndrew Jeffery std::shared_ptr<sdbusplus::asio::dbus_interface> valueMutabilityInterface; 109e73bd0a1SAndrew Jeffery double value = std::numeric_limits<double>::quiet_NaN(); 110e73bd0a1SAndrew Jeffery double rawValue = std::numeric_limits<double>::quiet_NaN(); 111e73bd0a1SAndrew Jeffery bool overriddenState = false; 112e73bd0a1SAndrew Jeffery bool internalSet = false; 113e73bd0a1SAndrew Jeffery double hysteresisTrigger; 114e73bd0a1SAndrew Jeffery double hysteresisPublish; 115e73bd0a1SAndrew Jeffery std::shared_ptr<sdbusplus::asio::connection> dbusConnection; 116e73bd0a1SAndrew Jeffery PowerState readState; 117e73bd0a1SAndrew Jeffery size_t errCount{0}; 118e73bd0a1SAndrew Jeffery std::unique_ptr<SensorInstrumentation> instrumentation; 119e73bd0a1SAndrew Jeffery 120e73bd0a1SAndrew Jeffery // This member variable provides a hook that can be used to receive 121e73bd0a1SAndrew Jeffery // notification whenever this Sensor's value is externally set via D-Bus. 122e73bd0a1SAndrew Jeffery // If interested, assign your own lambda to this variable, during 123e73bd0a1SAndrew Jeffery // construction of your Sensor subclass. See ExternalSensor for example. 124e73bd0a1SAndrew Jeffery std::function<void()> externalSetHook; 125e73bd0a1SAndrew Jeffery 126e73bd0a1SAndrew Jeffery using Level = thresholds::Level; 127e73bd0a1SAndrew Jeffery using Direction = thresholds::Direction; 128e73bd0a1SAndrew Jeffery 129e73bd0a1SAndrew Jeffery std::array<std::shared_ptr<sdbusplus::asio::dbus_interface>, 130e73bd0a1SAndrew Jeffery thresholds::thresProp.size()> 131e73bd0a1SAndrew Jeffery thresholdInterfaces; 132e73bd0a1SAndrew Jeffery getThresholdInterfaceSensor133*556e04b8SPatrick Williams std::shared_ptr<sdbusplus::asio::dbus_interface> getThresholdInterface( 134*556e04b8SPatrick Williams Level lev) 135e73bd0a1SAndrew Jeffery { 136e73bd0a1SAndrew Jeffery size_t index = static_cast<size_t>(lev); 137e73bd0a1SAndrew Jeffery if (index >= thresholdInterfaces.size()) 138e73bd0a1SAndrew Jeffery { 139e73bd0a1SAndrew Jeffery std::cout << "Unknown threshold level \n"; 140e73bd0a1SAndrew Jeffery return nullptr; 141e73bd0a1SAndrew Jeffery } 142e73bd0a1SAndrew Jeffery std::shared_ptr<sdbusplus::asio::dbus_interface> interface = 143e73bd0a1SAndrew Jeffery thresholdInterfaces[index]; 144e73bd0a1SAndrew Jeffery return interface; 145e73bd0a1SAndrew Jeffery } 146e73bd0a1SAndrew Jeffery updateInstrumentationSensor147e73bd0a1SAndrew Jeffery void updateInstrumentation(double readValue) const 148e73bd0a1SAndrew Jeffery { 149e73bd0a1SAndrew Jeffery // Do nothing if this feature is not enabled 150e73bd0a1SAndrew Jeffery if constexpr (!enableInstrumentation) 151e73bd0a1SAndrew Jeffery { 152e73bd0a1SAndrew Jeffery return; 153e73bd0a1SAndrew Jeffery } 154e73bd0a1SAndrew Jeffery if (!instrumentation) 155e73bd0a1SAndrew Jeffery { 156e73bd0a1SAndrew Jeffery return; 157e73bd0a1SAndrew Jeffery } 158e73bd0a1SAndrew Jeffery 159e73bd0a1SAndrew Jeffery // Save some typing 160e73bd0a1SAndrew Jeffery auto& inst = *instrumentation; 161e73bd0a1SAndrew Jeffery 162e73bd0a1SAndrew Jeffery // Show constants if first reading (even if unsuccessful) 163e73bd0a1SAndrew Jeffery if ((inst.numCollectsGood == 0) && (inst.numCollectsMiss == 0)) 164e73bd0a1SAndrew Jeffery { 165e73bd0a1SAndrew Jeffery std::cerr << "Sensor " << name << ": Configuration min=" << minValue 16655832f37SMatt Spinler << ", max=" << maxValue << ", type=" << configInterface 167e73bd0a1SAndrew Jeffery << ", path=" << configurationPath << "\n"; 168e73bd0a1SAndrew Jeffery } 169e73bd0a1SAndrew Jeffery 170e73bd0a1SAndrew Jeffery // Sensors can use "nan" to indicate unavailable reading 171e73bd0a1SAndrew Jeffery if (!std::isfinite(readValue)) 172e73bd0a1SAndrew Jeffery { 173e73bd0a1SAndrew Jeffery // Only show this if beginning a new streak 174e73bd0a1SAndrew Jeffery if (inst.numStreakMisses == 0) 175e73bd0a1SAndrew Jeffery { 176e73bd0a1SAndrew Jeffery std::cerr << "Sensor " << name 177e73bd0a1SAndrew Jeffery << ": Missing reading, Reading counts good=" 178e73bd0a1SAndrew Jeffery << inst.numCollectsGood 179e73bd0a1SAndrew Jeffery << ", miss=" << inst.numCollectsMiss 180e73bd0a1SAndrew Jeffery << ", Prior good streak=" << inst.numStreakGreats 181e73bd0a1SAndrew Jeffery << "\n"; 182e73bd0a1SAndrew Jeffery } 183e73bd0a1SAndrew Jeffery 184e73bd0a1SAndrew Jeffery inst.numStreakGreats = 0; 185e73bd0a1SAndrew Jeffery ++(inst.numCollectsMiss); 186e73bd0a1SAndrew Jeffery ++(inst.numStreakMisses); 187e73bd0a1SAndrew Jeffery 188e73bd0a1SAndrew Jeffery return; 189e73bd0a1SAndrew Jeffery } 190e73bd0a1SAndrew Jeffery 191e73bd0a1SAndrew Jeffery // Only show this if beginning a new streak and not the first time 192e73bd0a1SAndrew Jeffery if ((inst.numStreakGreats == 0) && (inst.numCollectsGood != 0)) 193e73bd0a1SAndrew Jeffery { 194e73bd0a1SAndrew Jeffery std::cerr << "Sensor " << name 195e73bd0a1SAndrew Jeffery << ": Recovered reading, Reading counts good=" 196e73bd0a1SAndrew Jeffery << inst.numCollectsGood 197e73bd0a1SAndrew Jeffery << ", miss=" << inst.numCollectsMiss 198e73bd0a1SAndrew Jeffery << ", Prior miss streak=" << inst.numStreakMisses << "\n"; 199e73bd0a1SAndrew Jeffery } 200e73bd0a1SAndrew Jeffery 201e73bd0a1SAndrew Jeffery // Initialize min/max if the first successful reading 202e73bd0a1SAndrew Jeffery if (inst.numCollectsGood == 0) 203e73bd0a1SAndrew Jeffery { 204e73bd0a1SAndrew Jeffery std::cerr << "Sensor " << name << ": First reading=" << readValue 205e73bd0a1SAndrew Jeffery << "\n"; 206e73bd0a1SAndrew Jeffery 207e73bd0a1SAndrew Jeffery inst.minCollected = readValue; 208e73bd0a1SAndrew Jeffery inst.maxCollected = readValue; 209e73bd0a1SAndrew Jeffery } 210e73bd0a1SAndrew Jeffery 211e73bd0a1SAndrew Jeffery inst.numStreakMisses = 0; 212e73bd0a1SAndrew Jeffery ++(inst.numCollectsGood); 213e73bd0a1SAndrew Jeffery ++(inst.numStreakGreats); 214e73bd0a1SAndrew Jeffery 215e73bd0a1SAndrew Jeffery // Only provide subsequent output if new min/max established 216e73bd0a1SAndrew Jeffery if (readValue < inst.minCollected) 217e73bd0a1SAndrew Jeffery { 218e73bd0a1SAndrew Jeffery std::cerr << "Sensor " << name << ": Lowest reading=" << readValue 219e73bd0a1SAndrew Jeffery << "\n"; 220e73bd0a1SAndrew Jeffery 221e73bd0a1SAndrew Jeffery inst.minCollected = readValue; 222e73bd0a1SAndrew Jeffery } 223e73bd0a1SAndrew Jeffery 224e73bd0a1SAndrew Jeffery if (readValue > inst.maxCollected) 225e73bd0a1SAndrew Jeffery { 226e73bd0a1SAndrew Jeffery std::cerr << "Sensor " << name << ": Highest reading=" << readValue 227e73bd0a1SAndrew Jeffery << "\n"; 228e73bd0a1SAndrew Jeffery 229e73bd0a1SAndrew Jeffery inst.maxCollected = readValue; 230e73bd0a1SAndrew Jeffery } 231e73bd0a1SAndrew Jeffery } 232e73bd0a1SAndrew Jeffery setSensorValueSensor233e73bd0a1SAndrew Jeffery int setSensorValue(const double& newValue, double& oldValue) 234e73bd0a1SAndrew Jeffery { 235e73bd0a1SAndrew Jeffery if (!internalSet) 236e73bd0a1SAndrew Jeffery { 237e73bd0a1SAndrew Jeffery if (insecureSensorOverride == 0 && !isSensorSettable && 238e73bd0a1SAndrew Jeffery !getManufacturingMode()) 239e73bd0a1SAndrew Jeffery { 240e73bd0a1SAndrew Jeffery throw SetSensorError(); 241e73bd0a1SAndrew Jeffery } 242e73bd0a1SAndrew Jeffery 243e73bd0a1SAndrew Jeffery oldValue = newValue; 244e73bd0a1SAndrew Jeffery overriddenState = true; 245e73bd0a1SAndrew Jeffery // check thresholds for external set 246e73bd0a1SAndrew Jeffery value = newValue; 247e73bd0a1SAndrew Jeffery checkThresholds(); 248e73bd0a1SAndrew Jeffery 249e73bd0a1SAndrew Jeffery // Trigger the hook, as an external set has just happened 250e73bd0a1SAndrew Jeffery if (externalSetHook) 251e73bd0a1SAndrew Jeffery { 252e73bd0a1SAndrew Jeffery externalSetHook(); 253e73bd0a1SAndrew Jeffery } 254e73bd0a1SAndrew Jeffery } 255e73bd0a1SAndrew Jeffery else if (!overriddenState) 256e73bd0a1SAndrew Jeffery { 257e73bd0a1SAndrew Jeffery oldValue = newValue; 258e73bd0a1SAndrew Jeffery } 259e73bd0a1SAndrew Jeffery return 1; 260e73bd0a1SAndrew Jeffery } 261e73bd0a1SAndrew Jeffery setInitialPropertiesSensor262e73bd0a1SAndrew Jeffery void setInitialProperties(const std::string& unit, 263e73bd0a1SAndrew Jeffery const std::string& label = std::string(), 264e73bd0a1SAndrew Jeffery size_t thresholdSize = 0) 265e73bd0a1SAndrew Jeffery { 266e73bd0a1SAndrew Jeffery if (readState == PowerState::on || readState == PowerState::biosPost || 267e73bd0a1SAndrew Jeffery readState == PowerState::chassisOn) 268e73bd0a1SAndrew Jeffery { 269e73bd0a1SAndrew Jeffery setupPowerMatch(dbusConnection); 270e73bd0a1SAndrew Jeffery } 271e73bd0a1SAndrew Jeffery 272e73bd0a1SAndrew Jeffery createAssociation(association, configurationPath); 273e73bd0a1SAndrew Jeffery 274e73bd0a1SAndrew Jeffery sensorInterface->register_property("Unit", unit); 275e73bd0a1SAndrew Jeffery sensorInterface->register_property("MaxValue", maxValue); 276e73bd0a1SAndrew Jeffery sensorInterface->register_property("MinValue", minValue); 277e73bd0a1SAndrew Jeffery sensorInterface->register_property( 278e73bd0a1SAndrew Jeffery "Value", value, [this](const double& newValue, double& oldValue) { 279e73bd0a1SAndrew Jeffery return setSensorValue(newValue, oldValue); 280e73bd0a1SAndrew Jeffery }); 281e73bd0a1SAndrew Jeffery 282e73bd0a1SAndrew Jeffery fillMissingThresholds(); 283e73bd0a1SAndrew Jeffery 284e73bd0a1SAndrew Jeffery for (auto& threshold : thresholds) 285e73bd0a1SAndrew Jeffery { 286e73bd0a1SAndrew Jeffery if (std::isnan(threshold.hysteresis)) 287e73bd0a1SAndrew Jeffery { 288e73bd0a1SAndrew Jeffery threshold.hysteresis = hysteresisTrigger; 289e73bd0a1SAndrew Jeffery } 290e73bd0a1SAndrew Jeffery 291e73bd0a1SAndrew Jeffery std::shared_ptr<sdbusplus::asio::dbus_interface> iface = 292e73bd0a1SAndrew Jeffery getThresholdInterface(threshold.level); 293e73bd0a1SAndrew Jeffery 294e73bd0a1SAndrew Jeffery if (!iface) 295e73bd0a1SAndrew Jeffery { 296e73bd0a1SAndrew Jeffery std::cout << "trying to set uninitialized interface\n"; 297e73bd0a1SAndrew Jeffery continue; 298e73bd0a1SAndrew Jeffery } 299e73bd0a1SAndrew Jeffery 3002aaf7175SPatrick Williams std::string level = 3012aaf7175SPatrick Williams propertyLevel(threshold.level, threshold.direction); 3022aaf7175SPatrick Williams std::string alarm = 3032aaf7175SPatrick Williams propertyAlarm(threshold.level, threshold.direction); 304e73bd0a1SAndrew Jeffery 305e73bd0a1SAndrew Jeffery if ((level.empty()) || (alarm.empty())) 306e73bd0a1SAndrew Jeffery { 307e73bd0a1SAndrew Jeffery continue; 308e73bd0a1SAndrew Jeffery } 3092aaf7175SPatrick Williams size_t thresSize = 3102aaf7175SPatrick Williams label.empty() ? thresholds.size() : thresholdSize; 311e73bd0a1SAndrew Jeffery iface->register_property( 312e73bd0a1SAndrew Jeffery level, threshold.value, 313e73bd0a1SAndrew Jeffery [&, label, thresSize](const double& request, double& oldValue) { 314e73bd0a1SAndrew Jeffery oldValue = request; // todo, just let the config do this? 315e73bd0a1SAndrew Jeffery threshold.value = request; 3162aaf7175SPatrick Williams thresholds::persistThreshold( 3172aaf7175SPatrick Williams configurationPath, configInterface, threshold, 3182aaf7175SPatrick Williams dbusConnection, thresSize, label); 319e73bd0a1SAndrew Jeffery // Invalidate previously remembered value, 320e73bd0a1SAndrew Jeffery // so new thresholds will be checked during next update, 321e73bd0a1SAndrew Jeffery // even if sensor reading remains unchanged. 322e73bd0a1SAndrew Jeffery value = std::numeric_limits<double>::quiet_NaN(); 323e73bd0a1SAndrew Jeffery 324e73bd0a1SAndrew Jeffery // Although tempting, don't call checkThresholds() from here 325e73bd0a1SAndrew Jeffery // directly. Let the regular sensor monitor call the same 326e73bd0a1SAndrew Jeffery // using updateValue(), which can check conditions like 327e73bd0a1SAndrew Jeffery // poweron, etc., before raising any event. 328e73bd0a1SAndrew Jeffery return 1; 329e73bd0a1SAndrew Jeffery }); 330e73bd0a1SAndrew Jeffery iface->register_property(alarm, false); 331e73bd0a1SAndrew Jeffery } 332e73bd0a1SAndrew Jeffery if (!sensorInterface->initialize()) 333e73bd0a1SAndrew Jeffery { 334e73bd0a1SAndrew Jeffery std::cerr << "error initializing value interface\n"; 335e73bd0a1SAndrew Jeffery } 336e73bd0a1SAndrew Jeffery 337e73bd0a1SAndrew Jeffery for (auto& thresIface : thresholdInterfaces) 338e73bd0a1SAndrew Jeffery { 339e73bd0a1SAndrew Jeffery if (thresIface) 340e73bd0a1SAndrew Jeffery { 341e73bd0a1SAndrew Jeffery if (!thresIface->initialize(true)) 342e73bd0a1SAndrew Jeffery { 343e73bd0a1SAndrew Jeffery std::cerr << "Error initializing threshold interface \n"; 344e73bd0a1SAndrew Jeffery } 345e73bd0a1SAndrew Jeffery } 346e73bd0a1SAndrew Jeffery } 347e73bd0a1SAndrew Jeffery 348e73bd0a1SAndrew Jeffery if (isValueMutable) 349e73bd0a1SAndrew Jeffery { 350e73bd0a1SAndrew Jeffery valueMutabilityInterface = 351e73bd0a1SAndrew Jeffery std::make_shared<sdbusplus::asio::dbus_interface>( 352e73bd0a1SAndrew Jeffery dbusConnection, sensorInterface->get_object_path(), 353e73bd0a1SAndrew Jeffery valueMutabilityInterfaceName); 354e73bd0a1SAndrew Jeffery valueMutabilityInterface->register_property("Mutable", true); 355e73bd0a1SAndrew Jeffery if (!valueMutabilityInterface->initialize()) 356e73bd0a1SAndrew Jeffery { 357e73bd0a1SAndrew Jeffery std::cerr 358e73bd0a1SAndrew Jeffery << "error initializing sensor value mutability interface\n"; 359e73bd0a1SAndrew Jeffery valueMutabilityInterface = nullptr; 360e73bd0a1SAndrew Jeffery } 361e73bd0a1SAndrew Jeffery } 362e73bd0a1SAndrew Jeffery 363e73bd0a1SAndrew Jeffery if (!availableInterface) 364e73bd0a1SAndrew Jeffery { 365e73bd0a1SAndrew Jeffery availableInterface = 366e73bd0a1SAndrew Jeffery std::make_shared<sdbusplus::asio::dbus_interface>( 367e73bd0a1SAndrew Jeffery dbusConnection, sensorInterface->get_object_path(), 368e73bd0a1SAndrew Jeffery availableInterfaceName); 369e73bd0a1SAndrew Jeffery availableInterface->register_property( 370e73bd0a1SAndrew Jeffery "Available", true, [this](const bool propIn, bool& old) { 371e73bd0a1SAndrew Jeffery if (propIn == old) 372e73bd0a1SAndrew Jeffery { 373e73bd0a1SAndrew Jeffery return 1; 374e73bd0a1SAndrew Jeffery } 375e73bd0a1SAndrew Jeffery old = propIn; 376e73bd0a1SAndrew Jeffery if (!propIn) 377e73bd0a1SAndrew Jeffery { 378e73bd0a1SAndrew Jeffery updateValue(std::numeric_limits<double>::quiet_NaN()); 379e73bd0a1SAndrew Jeffery } 380e73bd0a1SAndrew Jeffery return 1; 381e73bd0a1SAndrew Jeffery }); 382e73bd0a1SAndrew Jeffery availableInterface->initialize(); 383e73bd0a1SAndrew Jeffery } 384e73bd0a1SAndrew Jeffery if (!operationalInterface) 385e73bd0a1SAndrew Jeffery { 386e73bd0a1SAndrew Jeffery operationalInterface = 387e73bd0a1SAndrew Jeffery std::make_shared<sdbusplus::asio::dbus_interface>( 388e73bd0a1SAndrew Jeffery dbusConnection, sensorInterface->get_object_path(), 389e73bd0a1SAndrew Jeffery operationalInterfaceName); 390e73bd0a1SAndrew Jeffery operationalInterface->register_property("Functional", true); 391e73bd0a1SAndrew Jeffery operationalInterface->initialize(); 392e73bd0a1SAndrew Jeffery } 393e73bd0a1SAndrew Jeffery } 394e73bd0a1SAndrew Jeffery propertyLevelSensor395e73bd0a1SAndrew Jeffery static std::string propertyLevel(const Level lev, const Direction dir) 396e73bd0a1SAndrew Jeffery { 397e73bd0a1SAndrew Jeffery for (const thresholds::ThresholdDefinition& prop : 398e73bd0a1SAndrew Jeffery thresholds::thresProp) 399e73bd0a1SAndrew Jeffery { 400e73bd0a1SAndrew Jeffery if (prop.level == lev) 401e73bd0a1SAndrew Jeffery { 402e73bd0a1SAndrew Jeffery if (dir == Direction::HIGH) 403e73bd0a1SAndrew Jeffery { 404e73bd0a1SAndrew Jeffery return std::string(prop.levelName) + "High"; 405e73bd0a1SAndrew Jeffery } 406e73bd0a1SAndrew Jeffery if (dir == Direction::LOW) 407e73bd0a1SAndrew Jeffery { 408e73bd0a1SAndrew Jeffery return std::string(prop.levelName) + "Low"; 409e73bd0a1SAndrew Jeffery } 410e73bd0a1SAndrew Jeffery } 411e73bd0a1SAndrew Jeffery } 412e73bd0a1SAndrew Jeffery return ""; 413e73bd0a1SAndrew Jeffery } 414e73bd0a1SAndrew Jeffery propertyAlarmSensor415e73bd0a1SAndrew Jeffery static std::string propertyAlarm(const Level lev, const Direction dir) 416e73bd0a1SAndrew Jeffery { 417e73bd0a1SAndrew Jeffery for (const thresholds::ThresholdDefinition& prop : 418e73bd0a1SAndrew Jeffery thresholds::thresProp) 419e73bd0a1SAndrew Jeffery { 420e73bd0a1SAndrew Jeffery if (prop.level == lev) 421e73bd0a1SAndrew Jeffery { 422e73bd0a1SAndrew Jeffery if (dir == Direction::HIGH) 423e73bd0a1SAndrew Jeffery { 424e73bd0a1SAndrew Jeffery return std::string(prop.levelName) + "AlarmHigh"; 425e73bd0a1SAndrew Jeffery } 426e73bd0a1SAndrew Jeffery if (dir == Direction::LOW) 427e73bd0a1SAndrew Jeffery { 428e73bd0a1SAndrew Jeffery return std::string(prop.levelName) + "AlarmLow"; 429e73bd0a1SAndrew Jeffery } 430e73bd0a1SAndrew Jeffery } 431e73bd0a1SAndrew Jeffery } 432e73bd0a1SAndrew Jeffery return ""; 433e73bd0a1SAndrew Jeffery } 434e73bd0a1SAndrew Jeffery readingStateGoodSensor435e73bd0a1SAndrew Jeffery bool readingStateGood() const 436e73bd0a1SAndrew Jeffery { 437e73bd0a1SAndrew Jeffery return ::readingStateGood(readState); 438e73bd0a1SAndrew Jeffery } 439e73bd0a1SAndrew Jeffery markFunctionalSensor440e73bd0a1SAndrew Jeffery void markFunctional(bool isFunctional) 441e73bd0a1SAndrew Jeffery { 442e73bd0a1SAndrew Jeffery if (operationalInterface) 443e73bd0a1SAndrew Jeffery { 444e73bd0a1SAndrew Jeffery operationalInterface->set_property("Functional", isFunctional); 445e73bd0a1SAndrew Jeffery } 446e73bd0a1SAndrew Jeffery if (isFunctional) 447e73bd0a1SAndrew Jeffery { 448e73bd0a1SAndrew Jeffery errCount = 0; 449e73bd0a1SAndrew Jeffery } 450e73bd0a1SAndrew Jeffery else 451e73bd0a1SAndrew Jeffery { 452e73bd0a1SAndrew Jeffery updateValue(std::numeric_limits<double>::quiet_NaN()); 453e73bd0a1SAndrew Jeffery } 454e73bd0a1SAndrew Jeffery } 455e73bd0a1SAndrew Jeffery markAvailableSensor456e73bd0a1SAndrew Jeffery void markAvailable(bool isAvailable) 457e73bd0a1SAndrew Jeffery { 458e73bd0a1SAndrew Jeffery if (availableInterface) 459e73bd0a1SAndrew Jeffery { 460e73bd0a1SAndrew Jeffery availableInterface->set_property("Available", isAvailable); 461e73bd0a1SAndrew Jeffery errCount = 0; 462e73bd0a1SAndrew Jeffery } 463e73bd0a1SAndrew Jeffery } 464e73bd0a1SAndrew Jeffery incrementErrorSensor465e73bd0a1SAndrew Jeffery void incrementError() 466e73bd0a1SAndrew Jeffery { 467e73bd0a1SAndrew Jeffery if (!readingStateGood()) 468e73bd0a1SAndrew Jeffery { 469e73bd0a1SAndrew Jeffery markAvailable(false); 470e73bd0a1SAndrew Jeffery return; 471e73bd0a1SAndrew Jeffery } 472e73bd0a1SAndrew Jeffery 473e73bd0a1SAndrew Jeffery if (errCount >= errorThreshold) 474e73bd0a1SAndrew Jeffery { 475e73bd0a1SAndrew Jeffery return; 476e73bd0a1SAndrew Jeffery } 477e73bd0a1SAndrew Jeffery 478e73bd0a1SAndrew Jeffery errCount++; 479e73bd0a1SAndrew Jeffery if (errCount == errorThreshold) 480e73bd0a1SAndrew Jeffery { 481e73bd0a1SAndrew Jeffery std::cerr << "Sensor " << name << " reading error!\n"; 482e73bd0a1SAndrew Jeffery markFunctional(false); 483e73bd0a1SAndrew Jeffery } 484e73bd0a1SAndrew Jeffery } 485e73bd0a1SAndrew Jeffery inErrorSensor486e73bd0a1SAndrew Jeffery bool inError() const 487e73bd0a1SAndrew Jeffery { 488e73bd0a1SAndrew Jeffery return errCount >= errorThreshold; 489e73bd0a1SAndrew Jeffery } 490e73bd0a1SAndrew Jeffery updateValueSensor491e73bd0a1SAndrew Jeffery void updateValue(const double& newValue) 492e73bd0a1SAndrew Jeffery { 493e73bd0a1SAndrew Jeffery // Ignore if overriding is enabled 494e73bd0a1SAndrew Jeffery if (overriddenState) 495e73bd0a1SAndrew Jeffery { 496e73bd0a1SAndrew Jeffery return; 497e73bd0a1SAndrew Jeffery } 498e73bd0a1SAndrew Jeffery 499e73bd0a1SAndrew Jeffery if (!readingStateGood()) 500e73bd0a1SAndrew Jeffery { 501e73bd0a1SAndrew Jeffery markAvailable(false); 502e456925bSKonstantin Aladyshev for (auto& threshold : thresholds) 503e456925bSKonstantin Aladyshev { 504e456925bSKonstantin Aladyshev assertThresholds(this, value, threshold.level, 505e456925bSKonstantin Aladyshev threshold.direction, false); 506e456925bSKonstantin Aladyshev } 507e73bd0a1SAndrew Jeffery updateValueProperty(std::numeric_limits<double>::quiet_NaN()); 508e73bd0a1SAndrew Jeffery return; 509e73bd0a1SAndrew Jeffery } 510e73bd0a1SAndrew Jeffery 511e73bd0a1SAndrew Jeffery updateValueProperty(newValue); 512e73bd0a1SAndrew Jeffery updateInstrumentation(newValue); 513e73bd0a1SAndrew Jeffery 514e73bd0a1SAndrew Jeffery // Always check thresholds after changing the value, 515e73bd0a1SAndrew Jeffery // as the test against hysteresisTrigger now takes place in 516e73bd0a1SAndrew Jeffery // the thresholds::checkThresholds() method, 517e73bd0a1SAndrew Jeffery // which is called by checkThresholds() below, 518e73bd0a1SAndrew Jeffery // in all current implementations of sensors that have thresholds. 519e73bd0a1SAndrew Jeffery checkThresholds(); 520e73bd0a1SAndrew Jeffery if (!std::isnan(newValue)) 521e73bd0a1SAndrew Jeffery { 522e73bd0a1SAndrew Jeffery markFunctional(true); 523e73bd0a1SAndrew Jeffery markAvailable(true); 524e73bd0a1SAndrew Jeffery } 525e73bd0a1SAndrew Jeffery } 526e73bd0a1SAndrew Jeffery updatePropertySensor527e73bd0a1SAndrew Jeffery void updateProperty( 528e73bd0a1SAndrew Jeffery std::shared_ptr<sdbusplus::asio::dbus_interface>& interface, 529e73bd0a1SAndrew Jeffery double& oldValue, const double& newValue, 530e73bd0a1SAndrew Jeffery const char* dbusPropertyName) const 531e73bd0a1SAndrew Jeffery { 532e73bd0a1SAndrew Jeffery if (requiresUpdate(oldValue, newValue)) 533e73bd0a1SAndrew Jeffery { 534e73bd0a1SAndrew Jeffery oldValue = newValue; 535e73bd0a1SAndrew Jeffery if (interface && 536e73bd0a1SAndrew Jeffery !(interface->set_property(dbusPropertyName, newValue))) 537e73bd0a1SAndrew Jeffery { 538e73bd0a1SAndrew Jeffery std::cerr << "error setting property " << dbusPropertyName 539e73bd0a1SAndrew Jeffery << " to " << newValue << "\n"; 540e73bd0a1SAndrew Jeffery } 541e73bd0a1SAndrew Jeffery } 542e73bd0a1SAndrew Jeffery } 543e73bd0a1SAndrew Jeffery requiresUpdateSensor544e73bd0a1SAndrew Jeffery bool requiresUpdate(const double& lVal, const double& rVal) const 545e73bd0a1SAndrew Jeffery { 546e73bd0a1SAndrew Jeffery const auto lNan = std::isnan(lVal); 547e73bd0a1SAndrew Jeffery const auto rNan = std::isnan(rVal); 548e73bd0a1SAndrew Jeffery if (lNan || rNan) 549e73bd0a1SAndrew Jeffery { 550e73bd0a1SAndrew Jeffery return (lNan != rNan); 551e73bd0a1SAndrew Jeffery } 552e73bd0a1SAndrew Jeffery return std::abs(lVal - rVal) > hysteresisPublish; 553e73bd0a1SAndrew Jeffery } 554e73bd0a1SAndrew Jeffery 555e73bd0a1SAndrew Jeffery private: 556e73bd0a1SAndrew Jeffery // If one of the thresholds for a dbus interface is provided 557e73bd0a1SAndrew Jeffery // we have to set the other one as dbus properties are never 558e73bd0a1SAndrew Jeffery // optional. fillMissingThresholdsSensor559e73bd0a1SAndrew Jeffery void fillMissingThresholds() 560e73bd0a1SAndrew Jeffery { 5612d5ee50bSVikash Chandola const std::size_t thresholdsLen = thresholds.size(); 5622d5ee50bSVikash Chandola for (std::size_t index = 0; index < thresholdsLen; ++index) 563e73bd0a1SAndrew Jeffery { 5642d5ee50bSVikash Chandola const thresholds::Threshold& thisThreshold = thresholds[index]; 565e73bd0a1SAndrew Jeffery bool foundOpposite = false; 566e73bd0a1SAndrew Jeffery thresholds::Direction opposite = thresholds::Direction::HIGH; 567e73bd0a1SAndrew Jeffery if (thisThreshold.direction == thresholds::Direction::HIGH) 568e73bd0a1SAndrew Jeffery { 569e73bd0a1SAndrew Jeffery opposite = thresholds::Direction::LOW; 570e73bd0a1SAndrew Jeffery } 571e73bd0a1SAndrew Jeffery for (thresholds::Threshold& otherThreshold : thresholds) 572e73bd0a1SAndrew Jeffery { 573e73bd0a1SAndrew Jeffery if (thisThreshold.level != otherThreshold.level) 574e73bd0a1SAndrew Jeffery { 575e73bd0a1SAndrew Jeffery continue; 576e73bd0a1SAndrew Jeffery } 577e73bd0a1SAndrew Jeffery if (otherThreshold.direction != opposite) 578e73bd0a1SAndrew Jeffery { 579e73bd0a1SAndrew Jeffery continue; 580e73bd0a1SAndrew Jeffery } 581e73bd0a1SAndrew Jeffery foundOpposite = true; 582e73bd0a1SAndrew Jeffery break; 583e73bd0a1SAndrew Jeffery } 584e73bd0a1SAndrew Jeffery if (foundOpposite) 585e73bd0a1SAndrew Jeffery { 586e73bd0a1SAndrew Jeffery continue; 587e73bd0a1SAndrew Jeffery } 588e73bd0a1SAndrew Jeffery thresholds.emplace_back(thisThreshold.level, opposite, 589e73bd0a1SAndrew Jeffery std::numeric_limits<double>::quiet_NaN()); 590e73bd0a1SAndrew Jeffery } 591e73bd0a1SAndrew Jeffery } 592e73bd0a1SAndrew Jeffery updateValuePropertySensor593e73bd0a1SAndrew Jeffery void updateValueProperty(const double& newValue) 594e73bd0a1SAndrew Jeffery { 595e73bd0a1SAndrew Jeffery // Indicate that it is internal set call, not an external overwrite 596e73bd0a1SAndrew Jeffery internalSet = true; 597e73bd0a1SAndrew Jeffery updateProperty(sensorInterface, value, newValue, "Value"); 598e73bd0a1SAndrew Jeffery internalSet = false; 599e73bd0a1SAndrew Jeffery } 600e73bd0a1SAndrew Jeffery }; 601