blob: 506823a811a93e275a1b230fc2f5cd456deb665f [file] [log] [blame]
#include "ExternalSensor.hpp"
#include "SensorPaths.hpp"
#include "Thresholds.hpp"
#include "Utils.hpp"
#include "sensor.hpp"
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
#include <chrono>
#include <cstddef>
#include <functional>
#include <iostream>
#include <limits>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
static constexpr bool debug = false;
ExternalSensor::ExternalSensor(
const std::string& objectType, sdbusplus::asio::object_server& objectServer,
std::shared_ptr<sdbusplus::asio::connection>& conn,
const std::string& sensorName, const std::string& sensorUnits,
std::vector<thresholds::Threshold>&& thresholdsIn,
const std::string& sensorConfiguration, double maxReading,
double minReading, double timeoutSecs, const PowerState& powerState) :
Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration,
objectType, true, true, maxReading, minReading, conn, powerState),
objServer(objectServer), writeLast(std::chrono::steady_clock::now()),
writeTimeout(
std::chrono::duration_cast<std::chrono::steady_clock::duration>(
std::chrono::duration<double>(timeoutSecs))),
writePerishable(timeoutSecs > 0.0)
{
// The caller must specify what physical characteristic
// an external sensor is expected to be measuring, such as temperature,
// as, unlike others, this is not implied by device type name.
std::string dbusPath = sensor_paths::getPathForUnits(sensorUnits);
if (dbusPath.empty())
{
throw std::runtime_error("Units not in allow list");
}
std::string objectPath = "/xyz/openbmc_project/sensors/";
objectPath += dbusPath;
objectPath += '/';
objectPath += sensorName;
sensorInterface = objectServer.add_interface(
objectPath, "xyz.openbmc_project.Sensor.Value");
for (const auto& threshold : thresholds)
{
std::string interface = thresholds::getInterface(threshold.level);
thresholdInterfaces[static_cast<size_t>(threshold.level)] =
objectServer.add_interface(objectPath, interface);
}
association =
objectServer.add_interface(objectPath, association::interface);
setInitialProperties(sensorUnits);
if constexpr (debug)
{
std::cerr << "ExternalSensor " << name << " constructed: path "
<< configurationPath << ", type " << objectType << ", min "
<< minReading << ", max " << maxReading << ", timeout "
<< std::chrono::duration_cast<std::chrono::microseconds>(
writeTimeout)
.count()
<< " us\n";
}
}
// Separate function from constructor, because of a gotcha: can't use the
// enable_shared_from_this() API until after the constructor has completed.
void ExternalSensor::initWriteHook(
std::function<void(std::chrono::steady_clock::time_point now)>&&
writeHookIn)
{
// Connect ExternalSensorMain with ExternalSensor
writeHook = std::move(writeHookIn);
// Connect ExternalSensor with Sensor
auto weakThis = weak_from_this();
externalSetHook = [weakThis]() {
auto lockThis = weakThis.lock();
if (lockThis)
{
lockThis->externalSetTrigger();
return;
}
if constexpr (debug)
{
std::cerr << "ExternalSensor receive ignored, sensor gone\n";
}
};
}
ExternalSensor::~ExternalSensor()
{
// Make sure the write hook does not reference this object anymore
externalSetHook = nullptr;
objServer.remove_interface(association);
for (const auto& iface : thresholdInterfaces)
{
objServer.remove_interface(iface);
}
objServer.remove_interface(sensorInterface);
if constexpr (debug)
{
std::cerr << "ExternalSensor " << name << " destructed\n";
}
}
void ExternalSensor::checkThresholds()
{
thresholds::checkThresholds(this);
}
bool ExternalSensor::isAliveAndPerishable() const
{
return (writeAlive && writePerishable);
}
bool ExternalSensor::isAliveAndFresh(
const std::chrono::steady_clock::time_point& now) const
{
// Must be alive and perishable, to have possibility of being fresh
if (!isAliveAndPerishable())
{
return false;
}
// If age, as of now, is less than timeout, it is deemed fresh
// NOLINTNEXTLINE
return (ageElapsed(now) < writeTimeout);
}
void ExternalSensor::writeBegin(
const std::chrono::steady_clock::time_point& now)
{
if (!writeAlive)
{
std::cerr << "ExternalSensor " << name
<< " online, receiving first value " << value << "\n";
}
writeLast = now;
writeAlive = true;
}
void ExternalSensor::writeInvalidate()
{
writeAlive = false;
std::cerr << "ExternalSensor " << name << " offline, timed out\n";
// Take back control of this sensor from the external override,
// as the external source has timed out.
// This allows sensor::updateValue() to work normally,
// as it would do for internal sensors with values from hardware.
overriddenState = false;
// Invalidate the existing Value, similar to what internal sensors do,
// when they encounter errors trying to read from hardware.
updateValue(std::numeric_limits<double>::quiet_NaN());
}
std::chrono::steady_clock::duration ExternalSensor::ageElapsed(
const std::chrono::steady_clock::time_point& now) const
{
// Comparing 2 time_point will return duration
return (now - writeLast);
}
std::chrono::steady_clock::duration ExternalSensor::ageRemaining(
const std::chrono::steady_clock::time_point& now) const
{
// Comparing duration will return another duration
return (writeTimeout - ageElapsed(now));
}
void ExternalSensor::externalSetTrigger()
{
if constexpr (debug)
{
std::cerr << "ExternalSensor " << name << " received " << value << "\n";
}
auto now = std::chrono::steady_clock::now();
writeBegin(now);
// Tell the owner to recalculate the expiration timer
writeHook(now);
}