Sic.Framework-Nanjing-Baishi/MECF.Framework.RT.Equipment.../HardwareUnits/Temps/TempSensorBase.cs

236 lines
6.9 KiB
C#

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
/// <summary>
/// 生成随机温度。
/// </summary>
/// <returns></returns>
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 Misc.FilterTypes ConvertToFilterType(string type)
{
if (Enum.TryParse<Misc.FilterTypes>(type, true, out var ft))
return ft;
else
{
LOG.Error($"{this} Unable to convert {type} to filter type.");
return Misc.FilterTypes.None;
}
}
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} temp. points wanted but {temps.Length} points read."
: $"{this} {MaxChannels} no temp. points read from controller.");
}
// Too less temp. points read from the sensor.
if (_rTrigReadTempFailed.M)
return false;
// Get the right temp value from the filter
for (var i = 0; i < MaxChannels; 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}.T{ch + 1}", () => Temp[ch]);
DATA.Subscribe($"TempSensor.{Name}.T{ch + 1}MAF", () => _tempFilters[ch].FilteredMAF);
DATA.Subscribe($"TempSensor.{Name}.T{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<bool>($"{ScBasePath}.{Name}.EnableDevice"))
{
IsEnabled = false;
return true;
}
IsEnableLog = SC.SafeGetValue($"{ScBasePath}.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($"{ScBasePath}.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
}