using System; using System.Diagnostics; using System.Xml; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Device; using Aitex.Core.RT.Log; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using MECF.Framework.Common.Communications; namespace MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps; public abstract class TempSensorBase : BaseDevice, IDevice, IConnection, ITempSensor { #region Variables protected readonly bool IsSimMode; protected bool IsEnableLog; protected TempBasFunction TempBasFunction; private readonly Random _rndTempGen = new(); private readonly R_TRIG _rTrigReadTempFailed = new(); private PeriodicJob _tReadTemp; //private Misc.FilterTypes _filterType; //private TempDataFilter[] _tempFilters; private readonly Stopwatch _swSimSineWave = new (); private const double SINE_T = 5.0; private const double SINE_F = 2.0 * Math.PI * (1 / SINE_T); #endregion #region Ctor protected TempSensorBase(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule) { IsSimMode = SC.SafeGetValue("System.IsSimulatorMode", false); RTrigs.Add(_rTrigReadTempFailed); var maxChStr = node.GetAttribute("MaxChannels"); if (!string.IsNullOrEmpty(maxChStr) && int.TryParse(maxChStr, out var maxCh)) MaxChannels = maxCh; else MaxChannels = 4; var minTempStr = node.GetAttribute("MinimalTemp"); if (!string.IsNullOrEmpty(minTempStr) && double.TryParse(minTempStr, out var minTemp)) MinimalTemp = minTemp; else MinimalTemp = 600.0; if(IsSimMode) _swSimSineWave.Start(); } #endregion #region Properties public virtual string Address { get; protected set; } public virtual bool IsConnected { get; } public double MinimalTemp { get; } public int MaxChannels { get; } public double[] Temp { get; private set; } #endregion #region Methods /// /// 生成随机温度。 /// /// private double[] RandomTemps(double baseTemp = 800, double peakPeak = 200, double jitter = 50.0) { var rndTemps = new double[MaxChannels]; for (var i = 0; i < MaxChannels; i++) { var t = _swSimSineWave.Elapsed.TotalSeconds; // current moment var tempSine = peakPeak * Math.Sin(SINE_F * t + (i * Math.PI / 3)); //temperature following sine wave with 60 deg(π/3 rad) phase-diff per channel tempSine += baseTemp + tempSine + _rndTempGen.NextDouble() * jitter; // add jitter rndTemps[i] = tempSine; } return rndTemps; } private bool DoTempReadThread() { // get temp. points according to the "IsSimulatorMode" system config. var temps = IsSimMode ? RandomTemps() : HandleReadTemp(); _rTrigReadTempFailed.CLK = temps == null || temps.Length != MaxChannels; if (_rTrigReadTempFailed.Q) { LOG.Error(temps != null ? $"{this} {MaxChannels} temperature data wanted but {temps.Length} points read." : $"{this} None temperature data read from controller."); } // Too less temp. points read from the sensor. if (_rTrigReadTempFailed.M) return true; // Get the right temp value from the filter for (var i = 0; i < MaxChannels; i++) { Temp[i] = temps[i]; //_tempFilters[i].AddRawTemp(temps![i]); //Temp[i] = _filterType switch //{ // Misc.FilterTypes.None => _tempFilters[i].Raw, // Misc.FilterTypes.MAF => _tempFilters[i].FilteredMAF, // _ => _tempFilters[i].Raw //}; } return true; } private void InitTempDataFilter() { //_tempFilters = new TempDataFilter[MaxChannels]; Temp = new double[MaxChannels]; for (var i = 0; i < MaxChannels; i++) { //_tempFilters[i] = new TempDataFilter(this, (i + 1).ToString(), MinimalTemp, ScBasePath); Temp[i] = MinimalTemp; var ch = i; DATA.Subscribe($"TempSensor.{Name}.CH{ch + 1}", () => Temp[ch]); //DATA.Subscribe($"TempSensor.{Name}.CH{ch + 1}MAF", () => _tempFilters[ch].FilteredMAF); //DATA.Subscribe($"TempSensor.{Name}.CH{ch + 1}Raw", () => _tempFilters[ch].Raw); } } protected virtual double[] HandleReadTemp() { return new double[MaxChannels]; } protected virtual bool HandleInitialize() { return true; } public bool Initialize() { try { if (!SC.GetValue($"{ScBasePath}.{Name}.EnableDevice")) { IsEnabled = false; return true; } IsEnableLog = SC.SafeGetValue($"TempSensors.EnableLogMessage", false); //_filterType = ConvertToFilterType(SC.SafeGetStringValue($"{ScBasePath}.FilterType", "None")); var pollInvMs = SC.SafeGetValue($"{ScBasePath}.PollInterval", 100); // 系统参数变更回调 SC.RegisterValueChangedCallback($"{ScBasePath}.PollInterval", (obj) => { _tReadTemp.ChangeInterval((int)obj); }); //SC.RegisterValueChangedCallback($"{ScBasePath}.FilterType", // (obj) => { _filterType = ConvertToFilterType(obj?.ToString() ?? ""); }); SC.RegisterValueChangedCallback($"TempSensors.EnableLogMessage", (obj) => { IsEnableLog = bool.TryParse(obj.ToString(), out var en) && en; }); TempBasFunction = new TempBasFunction(Name, MinimalTemp, MaxChannels); TempBasFunction.SetPm1Pm2IoForInterlock(false); InitTempDataFilter(); // 执行派生类初始化方法 var userInit = HandleInitialize(); _tReadTemp = new PeriodicJob(pollInvMs, DoTempReadThread, $"{Name}", true); return userInit; } catch (Exception e) { LOG.Error(e.Message, e); return false; } } public virtual bool Connect() { return true; } public virtual bool Disconnect() { return true; } public virtual void Terminate() { } public virtual void Reset() { foreach (var rt in RTrigs) rt.RST = true; } public override string ToString() { return $"{Module}.{Name}"; } #endregion }