// Mozilla Public License 2.0 // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright (C) LibreHardwareMonitor and Contributors // All Rights Reserved using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.IO.Compression; namespace LibreHardwareMonitor.Hardware { internal class Sensor : ISensor { private readonly string _defaultName; private readonly Hardware _hardware; private readonly ISettings _settings; private readonly List _values = new List(); private int _count; private float? _currentValue; private string _name; private float _sum; private TimeSpan _valuesTimeWindow = TimeSpan.FromDays(1.0); public Sensor(string name, int index, SensorType sensorType, Hardware hardware, ISettings settings) : this(name, index, sensorType, hardware, null, settings) { } public Sensor(string name, int index, SensorType sensorType, Hardware hardware, ParameterDescription[] parameterDescriptions, ISettings settings) : this(name, index, false, sensorType, hardware, parameterDescriptions, settings) { } public Sensor(string name, int index, bool defaultHidden, SensorType sensorType, Hardware hardware, ParameterDescription[] parameterDescriptions, ISettings settings) { Index = index; IsDefaultHidden = defaultHidden; SensorType = sensorType; _hardware = hardware; Parameter[] parameters = new Parameter[parameterDescriptions?.Length ?? 0]; for (int i = 0; i < parameters.Length; i++) { if (parameterDescriptions != null) parameters[i] = new Parameter(parameterDescriptions[i], this, settings); } Parameters = parameters; _settings = settings; _defaultName = name; _name = settings.GetValue(new Identifier(Identifier, "name").ToString(), name); GetSensorValuesFromSettings(); hardware.Closing += delegate { SetSensorValuesToSettings(); }; } public IControl Control { get; internal set; } public IHardware Hardware { get { return _hardware; } } public Identifier Identifier { get { return new Identifier(_hardware.Identifier, SensorType.ToString().ToLowerInvariant(), Index.ToString(CultureInfo.InvariantCulture)); } } public int Index { get; } public bool IsDefaultHidden { get; } public float? Max { get; private set; } public float? Min { get; private set; } public string Name { get { return _name; } set { _name = !string.IsNullOrEmpty(value) ? value : _defaultName; _settings.SetValue(new Identifier(Identifier, "name").ToString(), _name); } } public IReadOnlyList Parameters { get; } public SensorType SensorType { get; } public float? Value { get { return _currentValue; } set { if (_valuesTimeWindow != TimeSpan.Zero) { DateTime now = DateTime.UtcNow; while (_values.Count > 0 && now - _values[0].Time > _valuesTimeWindow) _values.RemoveAt(0); if (value.HasValue) { _sum += value.Value; _count++; if (_count == 4) { AppendValue(_sum / _count, now); _sum = 0; _count = 0; } } } _currentValue = value; if (Min > value || !Min.HasValue) Min = value; if (Max < value || !Max.HasValue) Max = value; } } public IEnumerable Values { get { return _values; } } public TimeSpan ValuesTimeWindow { get { return _valuesTimeWindow; } set { _valuesTimeWindow = value; if (value == TimeSpan.Zero) _values.Clear(); } } public void ResetMin() { Min = null; } public void ResetMax() { Max = null; } public void Accept(IVisitor visitor) { if (visitor == null) throw new ArgumentNullException(nameof(visitor)); visitor.VisitSensor(this); } public void Traverse(IVisitor visitor) { foreach (IParameter parameter in Parameters) parameter.Accept(visitor); } private void SetSensorValuesToSettings() { using (MemoryStream m = new MemoryStream()) { using (GZipStream c = new GZipStream(m, CompressionMode.Compress)) { using (BufferedStream b = new BufferedStream(c, 65536)) { using (BinaryWriter writer = new BinaryWriter(b)) { long t = 0; foreach (SensorValue sensorValue in _values) { long v = sensorValue.Time.ToBinary(); writer.Write(v - t); t = v; writer.Write(sensorValue.Value); } writer.Flush(); } } } _settings.SetValue(new Identifier(Identifier, "values").ToString(), Convert.ToBase64String(m.ToArray())); } } private void GetSensorValuesFromSettings() { string name = new Identifier(Identifier, "values").ToString(); string s = _settings.GetValue(name, null); if (!string.IsNullOrEmpty(s)) { try { byte[] array = Convert.FromBase64String(s); DateTime now = DateTime.UtcNow; using (MemoryStream m = new MemoryStream(array)) using (GZipStream c = new GZipStream(m, CompressionMode.Decompress)) { using (MemoryStream unzip = new MemoryStream()) { c.CopyTo(unzip); unzip.Seek(0, SeekOrigin.Begin); using (BinaryReader reader = new BinaryReader(unzip)) { try { long t = 0; long readLen = reader.BaseStream.Length - reader.BaseStream.Position; while (true && readLen > 0) { t += reader.ReadInt64(); DateTime time = DateTime.FromBinary(t); if (time > now) break; float value = reader.ReadSingle(); AppendValue(value, time); readLen = reader.BaseStream.Length - reader.BaseStream.Position; } } catch (EndOfStreamException) { } } } } } catch { } } if (_values.Count > 0) AppendValue(float.NaN, DateTime.UtcNow); //remove the value string from the settings to reduce memory usage _settings.Remove(name); } private void AppendValue(float value, DateTime time) { if (_values.Count >= 2 && _values[_values.Count - 1].Value == value && _values[_values.Count - 2].Value == value) { _values[_values.Count - 1] = new SensorValue(value, time); return; } _values.Add(new SensorValue(value, time)); } } }