// Copyright 2025 Takuto Nakamura // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using RunCat365.Properties; using System.Globalization; namespace RunCat365 { struct TemperatureInfo { internal float AverageCelsius { get; set; } internal float MaximumCelsius { get; set; } } internal static class TemperatureInfoExtension { private const float CELSIUS_TO_FAHRENHEIT_SCALE = 9.0f / 5.0f; private const float CELSIUS_TO_FAHRENHEIT_OFFSET = 32.0f; internal static string GetDescription(this TemperatureInfo temperatureInfo, TemperatureUnit unit) { var resolvedUnit = unit.Resolve(); return $"{Strings.SystemInfo_Temperature}: {temperatureInfo.MaximumCelsius.ToLocalizedTemperatureText(resolvedUnit)}"; } internal static List GenerateIndicator(this TemperatureInfo temperatureInfo, TemperatureUnit unit) { var resolvedUnit = unit.Resolve(); return [ TreeFormatter.CreateRoot($"{Strings.SystemInfo_Temperature}:"), TreeFormatter.CreateNode($"{Strings.SystemInfo_Average}: {temperatureInfo.AverageCelsius.ToLocalizedTemperatureText(resolvedUnit)}", false), TreeFormatter.CreateNode($"{Strings.SystemInfo_Maximum}: {temperatureInfo.MaximumCelsius.ToLocalizedTemperatureText(resolvedUnit)}", true) ]; } private static string ToLocalizedTemperatureText(this float temperatureCelsius, TemperatureUnit resolvedUnit) { var useFahrenheit = resolvedUnit == TemperatureUnit.Fahrenheit; var value = useFahrenheit ? temperatureCelsius * CELSIUS_TO_FAHRENHEIT_SCALE + CELSIUS_TO_FAHRENHEIT_OFFSET : temperatureCelsius; var format = useFahrenheit ? Strings.SystemInfo_TemperatureFahrenheitFormat : Strings.SystemInfo_TemperatureCelsiusFormat; return string.Format(CultureInfo.CurrentCulture, format, value); } } internal sealed class TemperaturePerformanceCounters : InstancedPerformanceCounters { protected override string CategoryName => "Thermal Zone Information"; protected override string CounterName => "Temperature"; internal static TemperaturePerformanceCounters? TryCreate() { var instance = new TemperaturePerformanceCounters(); return instance.TryInitialize() ? instance : null; } } internal class TemperatureRepository { private const float KELVIN_TO_CELSIUS_OFFSET = 273.15f; private const float MIN_VALID_TEMPERATURE_CELSIUS = -50.0f; private const float MAX_VALID_TEMPERATURE_CELSIUS = 150.0f; private const int REFRESH_INTERVAL_TICKS = 30; private readonly TemperaturePerformanceCounters? counters; private TemperatureInfo? temperatureInfo; private int ticksSinceLastRefresh; internal bool IsAvailable => counters is not null; internal TemperatureRepository() { counters = TemperaturePerformanceCounters.TryCreate(); } internal void Update() { if (counters is null) return; ticksSinceLastRefresh += 1; if (REFRESH_INTERVAL_TICKS <= ticksSinceLastRefresh) { ticksSinceLastRefresh = 0; counters.RefreshInstances(); } var rawValues = counters.ReadValues(); var temperaturesCelsius = new List(rawValues.Count); foreach (var temperatureKelvin in rawValues) { if (temperatureKelvin <= 0) continue; var temperatureCelsius = temperatureKelvin - KELVIN_TO_CELSIUS_OFFSET; if (temperatureCelsius is < MIN_VALID_TEMPERATURE_CELSIUS or > MAX_VALID_TEMPERATURE_CELSIUS) continue; temperaturesCelsius.Add(temperatureCelsius); } temperatureInfo = temperaturesCelsius.Count == 0 ? null : new TemperatureInfo { AverageCelsius = temperaturesCelsius.Average(), MaximumCelsius = temperaturesCelsius.Max() }; } internal TemperatureInfo? Get() { return temperatureInfo; } internal void Close() { counters?.Close(); } } }