blob: 5d24fa1e672045b1dca41ac76cd1ed5410fa557c [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 "i2c_common_config.pb.h"
#include "reading_range_config.pb.h"
#include "threshold_config.pb.h"
#include "tlbmc/hal/sysfs/i2c.h"
#include "resource.pb.h"
#include "sensor.pb.h"
#include "tlbmc/sensors/fan_controller.h"
#include "tlbmc/sensors/i2c_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 I2cSysfs& i2c_sysfs, std::optional<NotificationCb> on_batch_notify) {
DLOG(INFO) << "Creating fan tach for device: " << absl::StrCat(config);
const I2cCommonConfig& i2c_common_config = config.i2c_common_config();
if (!fan_controller.ControllerHasSensor(i2c_common_config)) {
return absl::InvalidArgumentError(
absl::Substitute("Fan controller $0 does not have sensor $1",
absl::StrCat(fan_controller.GetI2cCommonConfig()),
absl::StrCat(i2c_common_config)));
}
auto it = fan_controller.GetIndexToTachs().find(config.index());
if (it == fan_controller.GetIndexToTachs().end()) {
if (!GetTlbmcConfig()
.sensor_collector_module()
.allow_sensor_creation_failure()) {
return absl::InvalidArgumentError(absl::Substitute(
"Fan controller $0 does not have a tach file at index $1",
absl::StrCat(fan_controller.GetI2cCommonConfig()), 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.GetI2cCommonConfig()),
config.index());
std::shared_ptr<FanTachometer> fan_tach = std::make_shared<FanTachometer>(
Token(), config.type(), "", absl::StrCat("fantach_", config.name()),
config.i2c_common_config(), config.thresholds(),
config.reading_ranges(), config.entity_common_config(), io_context,
on_batch_notify);
State state;
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.i2c_common_config(), config.thresholds(), config.reading_ranges(),
config.entity_common_config(), io_context, on_batch_notify);
}
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 I2cCommonConfig& i2c_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)
: I2cHwmonBasedSensor(
input_dev_path, io_context,
CreateStaticAttributes(sensor_name, UNIT_REVOLUTION_PER_MINUTE,
i2c_common_config, entity_common_config,
threshold_configs, reading_range_configs,
/*reading_transform_config=*/{}),
on_batch_notify),
sensor_type_(sensor_type),
sensor_name_(sensor_name),
board_config_name_(entity_common_config.board_config_name()) {}
} // namespace milotic_tlbmc