blob: 9748a0cbb7b8e8a70e452fa32d3c5581c1024b44 [file] [log] [blame]
#include "tlbmc/sensors/fan_tach.h"
#include <array>
#include <charconv>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <system_error> // NOLINT: system_error is commonly used in BMC
#include <utility>
#include "google/protobuf/duration.pb.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/substitute.h"
#include "boost/asio.hpp" // NOLINT: boost::asio is commonly used in BMC
#include "boost/asio/random_access_file.hpp" // NOLINT: boost::asio is commonly used in BMC
#include "boost/filesystem.hpp" // NOLINT: boost::filesystem is commonly used in BMC
#include "boost/filesystem/operations.hpp" // NOLINT: boost::filesystem is commonly used in BMC
#include "boost/system/detail/error_code.hpp" // NOLINT: boost::asio is commonly used in BMC
// NOLINTNEXTLINE: keep this so BUILD file will keep liburing
#include "liburing.h" // IWYU pragma: keep
#include "tlbmc/central_config/config.h"
#include "entity_common_config.pb.h"
#include "fan_tach_config.pb.h"
#include "hal_common_config.pb.h"
#include "reading_range_config.pb.h"
#include "threshold_config.pb.h"
#include "tlbmc/hal/sysfs/hwmon.h"
#include "resource.pb.h"
#include "sensor.pb.h"
#include "tlbmc/sensors/fan_controller.h"
#include "tlbmc/sensors/ixc_hwmon_based_sensor.h"
#include "tlbmc/sensors/sensor.h"
#include "tlbmc/time/time.h"
namespace milotic_tlbmc {
absl::StatusOr<std::shared_ptr<Sensor>> FanTachometer::Create(
const FanTachConfig& config, const FanController& fan_controller,
const std::shared_ptr<boost::asio::io_context>& io_context,
const HwmonSysfs& hwmon_sysfs,
std::optional<NotificationCb> on_batch_notify) {
DLOG(INFO) << "Creating fan tach for device: " << absl::StrCat(config);
const HalCommonConfig& hal_common_config = config.hal_common_config();
if (!fan_controller.ControllerHasSensor(hal_common_config)) {
return absl::InvalidArgumentError(
absl::Substitute("Fan controller $0 does not have sensor $1",
absl::StrCat(fan_controller.GetHalCommonConfig()),
absl::StrCat(hal_common_config)));
}
auto it = fan_controller.GetIndexToTachs().find(config.index());
if (it == fan_controller.GetIndexToTachs().end()) {
// Only fail if the sensor is detected and the config is not allowed to
// fail.
if (!GetTlbmcConfig()
.sensor_collector_module()
.allow_sensor_creation_failure() &&
config.entity_common_config().config_detected()) {
return absl::InvalidArgumentError(absl::Substitute(
"Fan controller $0 does not have a tach file at index $1",
absl::StrCat(fan_controller.GetHalCommonConfig()), config.index()));
}
LOG(ERROR) << "Failed to create FanTachometer (NONFATAL): "
<< absl::Substitute(
"Fan controller $0 does not have a tach file at index $1",
absl::StrCat(fan_controller.GetHalCommonConfig()),
config.index());
std::shared_ptr<FanTachometer> fan_tach = std::make_shared<FanTachometer>(
Token(), config.type(), "", absl::StrCat("fantach_", config.name()),
config.hal_common_config(), config.thresholds(),
config.reading_ranges(), config.entity_common_config(), io_context,
on_batch_notify, hwmon_sysfs);
State state;
// If the sensor is not detected, we set its creation as pending otherwise
// set it to failed.
if (!config.entity_common_config().config_detected()) {
state.set_status(STATUS_CREATION_PENDING);
} else {
state.set_status(STATUS_CREATION_FAILED);
}
state.set_status_message("Failed to create FanTach Sensor (NONFATAL)");
fan_tach->UpdateState(std::move(state));
return fan_tach;
}
std::string fan_tach_key = absl::StrCat("fantach_", config.name());
return std::make_shared<FanTachometer>(
Token(), config.type(), it->second.tach_input.string(), fan_tach_key,
config.hal_common_config(), config.thresholds(), config.reading_ranges(),
config.entity_common_config(), io_context, on_batch_notify, hwmon_sysfs);
}
void FanTachometer::HandleRefreshResult(const boost::system::error_code& error,
size_t bytes_read) {
if (error) {
State state;
state.set_status(STATUS_STALE);
state.set_status_message(absl::Substitute(
"Failed to read from input device: $0; input device path: $1",
error.message(), GetInputDevicePath()));
UpdateState(std::move(state));
return;
}
const char* buffer_end = GetConstReadBuffer().data() + bytes_read;
int64_t value = 0;
std::from_chars_result result =
std::from_chars(GetConstReadBuffer().data(), buffer_end, value);
if (result.ec != std::errc()) {
State state;
state.set_status(STATUS_STALE);
state.set_status_message(
absl::StrCat("Read data can't be converted to a number: ",
std::make_error_condition(result.ec).message()));
UpdateState(std::move(state));
return;
}
SensorValue sensor_data;
sensor_data.set_reading(static_cast<double>(value));
*sensor_data.mutable_timestamp() = Now();
StoreSensorData(std::make_shared<const SensorValue>(std::move(sensor_data)));
State state;
state.set_status(STATUS_READY);
UpdateState(std::move(state));
}
FanTachometer::FanTachometer(
Token token, FanTachType sensor_type, const std::string& input_dev_path,
const std::string& sensor_name, const HalCommonConfig& hal_common_config,
const ThresholdConfigs& threshold_configs,
const ReadingRangeConfigs& reading_range_configs,
const EntityCommonConfig& entity_common_config,
const std::shared_ptr<boost::asio::io_context>& io_context,
std::optional<NotificationCb> on_batch_notify,
const HwmonSysfs& hwmon_sysfs)
: IXcHwmonBasedSensor(
input_dev_path, io_context,
CreateStaticAttributes(sensor_name, UNIT_REVOLUTION_PER_MINUTE,
hal_common_config, entity_common_config,
reading_range_configs,
/*reading_transform_config=*/{}),
threshold_configs, on_batch_notify, hwmon_sysfs),
sensor_type_(sensor_type),
sensor_name_(sensor_name) {}
} // namespace milotic_tlbmc