blob: f149bfc48058ba3e0b03eaddf637c9caf9924657 [file]
#include "tlbmc/sensors/sensor.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "google/protobuf/duration.pb.h"
#include "absl/base/no_destructor.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/time.h"
#include "g3/macros.h"
#include <nlohmann/json.hpp>
#include "entity_common_config.pb.h"
#include "hal_common_config.pb.h"
#include "reading_range_config.pb.h"
#include "reading_transform_config.pb.h"
#include "sensor_instance_properties.pb.h"
#include "threshold_config.pb.h"
#include "resource.pb.h"
#include "sensor.pb.h"
#include "tlbmc/time/time.h"
#include "google/protobuf/json/json.h"
#include "google/protobuf/util/json_util.h"
namespace milotic_tlbmc {
SensorAttributesStatic Sensor::CreateStaticAttributes(
const SensorInstanceProperties& sensor_instance_properties,
const HalCommonConfig& hal_common_config,
const EntityCommonConfig& entity_common_config) {
SensorAttributesStatic sensor_attributes_static;
sensor_attributes_static.set_unit(sensor_instance_properties.unit());
sensor_attributes_static.mutable_attributes()->set_key(
absl::StrReplaceAll(sensor_instance_properties.name(), {{" ", "_"}}));
sensor_attributes_static.mutable_attributes()
->mutable_refresh_policy()
->set_refresh_mode(REFRESH_MODE_PERIODIC);
*sensor_attributes_static.mutable_attributes()
->mutable_refresh_policy()
->mutable_refresh_interval() = entity_common_config.refresh_interval();
*sensor_attributes_static.mutable_hal_common_config() = hal_common_config;
*sensor_attributes_static.mutable_entity_common_config() =
entity_common_config;
*sensor_attributes_static.mutable_reading_ranges() =
sensor_instance_properties.reading_ranges();
*sensor_attributes_static.mutable_reading_transform() =
sensor_instance_properties.reading_transform_config();
*sensor_attributes_static.mutable_static_refresh_interval() =
entity_common_config.refresh_interval();
*sensor_attributes_static.mutable_related_item() =
sensor_instance_properties.related_item();
sensor_attributes_static.set_redfish_hidden(
sensor_instance_properties.redfish_hidden());
sensor_attributes_static.set_append_to_sensor_collection(
sensor_instance_properties.append_to_sensor_collection());
if (sensor_instance_properties.has_devpath() &&
!sensor_instance_properties.devpath().empty()) {
sensor_attributes_static.set_devpath(sensor_instance_properties.devpath());
}
return sensor_attributes_static;
}
void Sensor::UpdateSensorSamplingInterval(
absl::Duration new_sampling_interval) {
// NOLINTNEXTLINE: Yocto's Abseil doesn't support non-pointer constructor.
absl::MutexLock lock(sensor_attributes_dynamic_mutex_);
*sensor_attributes_dynamic_.mutable_refresh_interval() =
EncodeGoogleApiProto(new_sampling_interval);
}
void Sensor::UpdateThresholds(const ThresholdConfigs& threshold_configs) {
// NOLINTNEXTLINE: Yocto's Abseil doesn't support non-pointer constructor.
absl::MutexLock lock(sensor_attributes_dynamic_mutex_);
*sensor_attributes_dynamic_.mutable_thresholds() = threshold_configs;
}
ThresholdType Sensor::CalculateExceededThreshold(
double reading, const ThresholdConfigs& threshold_configs) {
ThresholdType exceeded_threshold_type = THRESHOLD_TYPE_UNKNOWN;
for (const auto& threshold_config : threshold_configs.threshold_configs()) {
switch (threshold_config.type()) {
case THRESHOLD_TYPE_UPPER_CRITICAL:
if (reading > threshold_config.value()) {
exceeded_threshold_type = THRESHOLD_TYPE_UPPER_CRITICAL;
}
break;
case THRESHOLD_TYPE_UPPER_NON_CRITICAL:
if (reading > threshold_config.value()) {
exceeded_threshold_type = THRESHOLD_TYPE_UPPER_NON_CRITICAL;
}
break;
case THRESHOLD_TYPE_LOWER_CRITICAL:
if (reading < threshold_config.value()) {
exceeded_threshold_type = THRESHOLD_TYPE_LOWER_CRITICAL;
}
break;
case THRESHOLD_TYPE_LOWER_NON_CRITICAL:
if (reading < threshold_config.value()) {
exceeded_threshold_type = THRESHOLD_TYPE_LOWER_NON_CRITICAL;
}
break;
default:
break;
}
if (exceeded_threshold_type == THRESHOLD_TYPE_LOWER_CRITICAL ||
exceeded_threshold_type == THRESHOLD_TYPE_UPPER_CRITICAL) {
break;
}
}
return exceeded_threshold_type;
}
std::vector<Sensor::ThresholdEvent> Sensor::CheckSensorThresholds() {
const std::shared_ptr<const SensorValue> sensor_data = GetSensorData();
std::vector<Sensor::ThresholdEvent> threshold_events;
if (sensor_data == nullptr) {
return threshold_events;
}
ThresholdType exceeded_threshold_type = THRESHOLD_TYPE_UNKNOWN;
{
absl::MutexLock lock(sensor_attributes_dynamic_mutex_);
if (!sensor_attributes_dynamic_.thresholds().threshold_configs().empty()) {
exceeded_threshold_type = CalculateExceededThreshold(
sensor_data->reading(), sensor_attributes_dynamic_.thresholds());
}
// Helper function to update the threshold event list and the state of the
// sensor. Returns the target flag for the given threshold type.
auto update_state = [&threshold_events, &sensor_data](
bool current_flag, bool target_flag,
ThresholdType threshold_type) -> bool {
if (current_flag != target_flag) {
threshold_events.push_back(Sensor::ThresholdEvent{
threshold_type, target_flag, sensor_data->reading()});
}
return target_flag;
};
// Calculate the target state for each threshold type. The target state
// is determined by the highest threshold type that is exceeded.
bool target_over_crit =
(exceeded_threshold_type == THRESHOLD_TYPE_UPPER_CRITICAL);
bool target_over_non_crit =
(target_over_crit ||
exceeded_threshold_type == THRESHOLD_TYPE_UPPER_NON_CRITICAL);
bool target_under_crit =
(exceeded_threshold_type == THRESHOLD_TYPE_LOWER_CRITICAL);
bool target_under_non_crit =
(target_under_crit ||
exceeded_threshold_type == THRESHOLD_TYPE_LOWER_NON_CRITICAL);
// Update the state of the sensor based on the target state.
is_over_critical_ = update_state(is_over_critical_, target_over_crit,
THRESHOLD_TYPE_UPPER_CRITICAL);
is_over_non_critical_ =
update_state(is_over_non_critical_, target_over_non_crit,
THRESHOLD_TYPE_UPPER_NON_CRITICAL);
is_under_critical_ = update_state(is_under_critical_, target_under_crit,
THRESHOLD_TYPE_LOWER_CRITICAL);
is_under_non_critical_ =
update_state(is_under_non_critical_, target_under_non_crit,
THRESHOLD_TYPE_LOWER_NON_CRITICAL);
}
return threshold_events;
}
void Sensor::UpdateState(State&& state) {
absl::MutexLock lock(sensor_attributes_dynamic_mutex_);
*sensor_attributes_dynamic_.mutable_state() = std::move(state);
}
SensorMetrics Sensor::GetSensorMetrics() const {
// This is a base class, so it doesn't have any metrics.
static const absl::NoDestructor<SensorMetrics> kMetrics([] {
SensorMetrics metrics;
metrics.mutable_average_hardware_polling_latency()->set_nanos(0);
metrics.mutable_average_software_polling_start_interval()->set_nanos(0);
metrics.mutable_average_software_polling_end_interval()->set_nanos(0);
return metrics;
}());
return *kMetrics;
}
absl::StatusOr<nlohmann::json> Sensor::GetMetricsAsJson(
const ::google::protobuf::util::JsonPrintOptions& options) const {
std::string output;
ECCLESIA_RETURN_IF_ERROR(::google::protobuf::json::MessageToJsonString(
GetSensorMetrics(), &output, options));
return nlohmann::json::parse(output, nullptr, false);
}
absl::StatusOr<nlohmann::json> Sensor::ToJson() const {
nlohmann::json sensor_json;
std::string json_string;
::google::protobuf::util::JsonPrintOptions opts;
opts.preserve_proto_field_names = true;
std::shared_ptr<const SensorValue> sensor_data = GetSensorData();
if (sensor_data == nullptr) {
std::string error_msg = "Sensor data is null";
std::string status_msg =
GetSensorAttributesDynamic().state().status_message();
if (!status_msg.empty()) {
error_msg += ": " + status_msg;
}
sensor_json["Value"] = error_msg;
} else {
if (!::google::protobuf::json::MessageToJsonString(*sensor_data, &json_string, opts)
.ok()) {
return absl::InternalError("Failed to convert SensorValue to JSON");
}
sensor_json["Value"] = nlohmann::json::parse(json_string, nullptr, false);
json_string.clear();
}
absl::StatusOr<nlohmann::json> metrics_json = GetMetricsAsJson(opts);
if (!metrics_json.ok()) {
return metrics_json.status();
}
sensor_json["Metrics"] = *metrics_json;
if (!::google::protobuf::json::MessageToJsonString(GetSensorAttributesStatic(),
&json_string, opts)
.ok()) {
return absl::InternalError(
"Failed to convert SensorStaticAttributes to JSON");
}
sensor_json["StaticAttributes"] =
nlohmann::json::parse(json_string, nullptr, false);
json_string.clear();
if (!::google::protobuf::json::MessageToJsonString(GetSensorAttributesDynamic(),
&json_string, opts)
.ok()) {
return absl::InternalError(
"Failed to convert SensorDynamicAttributes to JSON");
}
sensor_json["DynamicAttributes"] =
nlohmann::json::parse(json_string, nullptr, false);
return sensor_json;
}
void Sensor::PowerOffToOnCallback(bool setup_run) { ready_for_read_ = true; }
void Sensor::PowerOnToOffCallback(bool setup_run) { ready_for_read_ = false; }
void Sensor::OSInactiveToStandbyCallback(bool setup_run) {
ready_for_read_ = true;
}
void Sensor::OSStandbyToInactiveCallback(bool setup_run) {
ready_for_read_ = false;
}
absl::Status Sensor::SetOverrideValue(const SensorValue& value) {
absl::MutexLock lock(override_mutex_);
override_value_ = value;
return absl::OkStatus();
}
absl::Status Sensor::ClearOverride() {
absl::MutexLock lock(override_mutex_);
override_value_.reset();
return absl::OkStatus();
}
bool Sensor::IsOverridden() const {
absl::MutexLock lock(override_mutex_);
return override_value_.has_value();
}
std::optional<SensorValue> Sensor::GetOverrideValue() const {
absl::MutexLock lock(override_mutex_);
return override_value_;
}
std::string GetTrimmedSensorName(absl::string_view sensor_name) {
std::string stripped_sensor_name;
if (uint64_t under_pos = sensor_name.find('_');
under_pos != std::string::npos) {
stripped_sensor_name = sensor_name.substr(under_pos + 1);
}
std::replace(stripped_sensor_name.begin(), stripped_sensor_name.end(), '_',
' ');
return stripped_sensor_name;
}
} // namespace milotic_tlbmc