Add tlBMC sensor value override functionality

Introduces thread-safe software override support in the `milotic_tlbmc::Sensor` base class and overridden sensors.

Key changes:
- Adds `SetOverrideValue()`, `ClearOverride()`, `IsOverridden()`, and `GetOverrideValue()` to `Sensor`, guarded by `override_mutex_`.
- Updates `PollingBaseSensor::GetSensorData()` and `BulkSensor::GetSensorData()` to return the active override value when present.
- Updates `Sensor::GetSensorAttributesDynamic()` and `BulkSensor::GetSensorAttributesDynamic()` to report `STATUS_READY` when overridden.
- Shields `SyncPollingSensor::RefreshOnceAsync()` and `AdcSensor::RefreshOnceAsync()` by skipping physical hardware reads while an override is active and immediately dispatching the callback with the overridden reading.
- Adds comprehensive unit tests in `polling_base_sensor_test.cc` verifying override set, get, clear, dynamic attributes, threshold evaluations, and polling shielding.

Google-Bug-Id:550410302
PiperOrigin-RevId: 975255792
Change-Id: I3e793b2f0d75a78a4c97e8612b6096d5bf01bd1d
diff --git a/tlbmc/sensors/adc_sensor.cc b/tlbmc/sensors/adc_sensor.cc
index 483b011..64066f5 100644
--- a/tlbmc/sensors/adc_sensor.cc
+++ b/tlbmc/sensors/adc_sensor.cc
@@ -290,6 +290,13 @@
 void AdcSensor::RefreshOnceAsync(
     absl::AnyInvocable<void(const std::shared_ptr<const SensorValue>&)>
         callback) {
+  if (IsOverridden()) {
+    if (callback) {
+      callback(GetSensorData());
+    }
+    return;
+  }
+
   std::weak_ptr<AdcSensor> weak_self = shared_from_this();
 
   auto on_read_done = [weak_self, callback = std::move(callback)](
diff --git a/tlbmc/sensors/polling_base_sensor.h b/tlbmc/sensors/polling_base_sensor.h
index 175fc1f..f222612 100644
--- a/tlbmc/sensors/polling_base_sensor.h
+++ b/tlbmc/sensors/polling_base_sensor.h
@@ -37,7 +37,13 @@
 
   // Returns the latest sensor reading.
   std::shared_ptr<const SensorValue> GetSensorData() const override
-      ABSL_LOCKS_EXCLUDED(sensor_data_mutex_) {
+      ABSL_LOCKS_EXCLUDED(sensor_data_mutex_, override_mutex_) {
+    {
+      absl::MutexLock lock(override_mutex_);
+      if (override_value_.has_value()) {
+        return std::make_shared<const SensorValue>(*override_value_);
+      }
+    }
     absl::MutexLock lock(sensor_data_mutex_);
     return sensor_data_.empty() ? nullptr : sensor_data_.back();
   }
diff --git a/tlbmc/sensors/sensor.cc b/tlbmc/sensors/sensor.cc
index c18f4fd..f149bfc 100644
--- a/tlbmc/sensors/sensor.cc
+++ b/tlbmc/sensors/sensor.cc
@@ -3,6 +3,7 @@
 #include <algorithm>
 #include <cstdint>
 #include <memory>
+#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -258,6 +259,28 @@
   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('_');
diff --git a/tlbmc/sensors/sensor.h b/tlbmc/sensors/sensor.h
index bdbb51e..61688db 100644
--- a/tlbmc/sensors/sensor.h
+++ b/tlbmc/sensors/sensor.h
@@ -25,7 +25,6 @@
 #include "reading_transform_config.pb.h"
 #include "sensor_instance_properties.pb.h"
 #include "threshold_config.pb.h"
-#include "tlbmc/host_state/power_control.h"
 #include "resource.pb.h"
 #include "sensor.pb.h"
 #include "google/protobuf/util/json_util.h"
@@ -92,13 +91,34 @@
 
   virtual SensorAttributesDynamic GetSensorAttributesDynamic() const {
     absl::MutexLock lock(sensor_attributes_dynamic_mutex_);
-    return sensor_attributes_dynamic_;
+    SensorAttributesDynamic attributes = sensor_attributes_dynamic_;
+    {
+      absl::MutexLock override_lock(override_mutex_);
+      if (override_value_.has_value()) {
+        attributes.mutable_state()->set_status(STATUS_READY);
+      }
+    }
+    return attributes;
   }
 
   const SensorAttributesStatic& GetSensorAttributesStatic() const {
     return sensor_attributes_static_;
   }
 
+  // Sets the software override value for this sensor.
+  // When set, GetSensorData() will return this value and RefreshOnceAsync()
+  // will not overwrite it.
+  virtual absl::Status SetOverrideValue(const SensorValue& value);
+
+  // Clears any active software override, restoring normal hardware polling.
+  virtual absl::Status ClearOverride();
+
+  // Returns true if a software override is currently active.
+  virtual bool IsOverridden() const;
+
+  // Returns the active override value if present.
+  virtual std::optional<SensorValue> GetOverrideValue() const;
+
   virtual SensorMetrics GetSensorMetrics() const;
   virtual absl::StatusOr<nlohmann::json> GetMetricsAsJson(
       const ::google::protobuf::util::JsonPrintOptions& options) const;
@@ -228,6 +248,9 @@
   std::atomic<bool> ready_for_read_ = true;
 
   std::string devpath_;
+
+  mutable absl::Mutex override_mutex_;
+  std::optional<SensorValue> override_value_ ABSL_GUARDED_BY(override_mutex_);
 };
 
 std::string GetTrimmedSensorName(absl::string_view sensor_name);
diff --git a/tlbmc/sensors/sync_polling_sensor.cc b/tlbmc/sensors/sync_polling_sensor.cc
index 68f3e7b..5c8cf05 100644
--- a/tlbmc/sensors/sync_polling_sensor.cc
+++ b/tlbmc/sensors/sync_polling_sensor.cc
@@ -35,6 +35,12 @@
       LOG(WARNING) << "Sensor is destroyed; cancel the refresh.";
       return;
     }
+    if (sensor->IsOverridden()) {
+      if (callback) {
+        callback(sensor->GetSensorData());
+      }
+      return;
+    }
     std::chrono::steady_clock::time_point last_refresh_start_time =
         sensor->GetLastRefreshStartTime();
     if (last_refresh_start_time !=