| #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 |