Sic.Framework/MECF.Framework.RT.Equipment.../Devices/IoMFC.cs

420 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Diagnostics;
using System.Xml;
using Aitex.Core.Common.DeviceData;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.Common.Aitex.Core.RT.Device;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.MFCs;
namespace Aitex.Core.RT.Device.Devices
{
public class IoMFC : ErrorDetectableBaseDevice, IMfc
{
#region Variables
private readonly DeviceTimer _rampTimer = new();
private double _rampTarget;
private double _rampInitValue;
private double _rampSetMode;
private int _rampTime;
private readonly DIAccessor _diAlarm;
private readonly AIAccessor _aiFlow;
private readonly AIAccessor _aiActMode;
private readonly AOAccessor _aoFlow;
private readonly AOAccessor _aoSetMode;
protected SCConfigItem _scGasName;
protected SCConfigItem _scEnable;
private readonly SCConfigItem _scN2Scale;
private readonly SCConfigItem _scDefaultSetPoint;
private readonly bool _isFloatAioType = false;
/// <summary>
/// 当检测到前置Valve关闭时停止并重置当前报警检测以避免下次Valve开启式可能立即报警的问题。
/// </summary>
private readonly R_TRIG _valveCloseTrigger = new ();
/// <summary>
/// 当前MFC前置或后置阀门ID
/// </summary>
private readonly string _valve;
/// <summary>
/// 是否根据Valve的开光状态决定是否进行误差检测。
/// 如果该参数为true当Valve关闭时不进行误差检测。
/// </summary>
private readonly bool _disableFbSpErrMonWhenValveClosed;
#endregion
#region Constructors
public IoMFC(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
{
_aiFlow = ParseAiNode("aiFlow", node, ioModule);
_aoFlow = ParseAoNode("aoFlow", node, ioModule);
_aiActMode = ParseAiNode("aiActMode", node, ioModule);
_aoSetMode = ParseAoNode("aoSetMode", node, ioModule);
_diAlarm = ParseDiNode("diAlarm", node, ioModule);
_valve = node.HasAttribute("Valve") ? node.GetAttribute("Valve") : "";
_disableFbSpErrMonWhenValveClosed = node.HasAttribute("DisableFbSpErrMonWhenValveClosed") && Convert.ToBoolean(node.GetAttribute("DisableFbSpErrMonWhenValveClosed"));
_isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float");
_scGasName = SC.GetConfigItem($"{ScBasePath}.{Name}.GasName");
_scN2Scale = ParseScNode("scN2Scale", node, ioModule, $"{ScBasePath}.{Name}.N2Scale");
_scDefaultSetPoint = ParseScNode("scDefaultSetPoint", node, ioModule, $"{ScBasePath}.{Name}.DefaultSetPoint");
}
#endregion
#region Properties
public string DisplayName
{
get
{
if (_scGasName != null)
return _scGasName.StringValue;
return Display;
}
}
public double Scale
{
get
{
if (_scN2Scale == null )
return 0;
return _scN2Scale.DoubleValue ;
}
}
public double SetPoint
{
get
{
if (_aoFlow != null)
{
return _isFloatAioType ? _aoFlow.FloatValue : _aoFlow.Value;
}
return 0;
}
set
{
if (_aoFlow != null)
{
if (_isFloatAioType)
_aoFlow.FloatValue = (float)value;
else
_aoFlow.Value = (short)value;
}
}
}
public double DefaultSetPoint
{
get
{
if (_scDefaultSetPoint != null)
return _scDefaultSetPoint.DoubleValue;
return 0;
}
}
public double FeedBack
{
get
{
if (_aiFlow != null)
{
double aiValue = _isFloatAioType ? _aiFlow.FloatValue : _aiFlow.Value;
return aiValue; // (_scRegulationFactor != null && _scRegulationFactor.DoubleValue > 0.001) ? aiValue / _scRegulationFactor.DoubleValue : aiValue;
}
return 0;
}
}
public double ActMode
{
get
{
if (_aiActMode != null)
{
return _aiActMode.FloatValue;
}
return 0;
}
}
public double SetMode
{
get
{
if (_aoSetMode != null)
{
return _aoSetMode.FloatValue;
}
return 0;
}
set
{
if (_aoSetMode != null)
{
if (_isFloatAioType)
_aoSetMode.FloatValue = (float)value;
else
_aoSetMode.Value = (short)value;
}
}
}
private AITMfcData DeviceData
{
get
{
//原DisplayName = DisplayName 改成DisplayName = $"{Name}-->{DisplayName}"
AITMfcData data = new AITMfcData()
{
UniqueName = $"{Module}.{Name}",
Type = "MFC",
Module = Module,
DeviceName = Name,
DeviceSchematicId = DeviceID,
DisplayName = $"{Name}-->{DisplayName}",
FeedBack = FeedBack,
SetPoint = SetPoint,
Scale = Scale,
IsWarning = ErrWarningChecker.Result,
IsError = ErrAlarmChecker.Result,
DefaultValue = DefaultSetPoint,
ActMode = ActMode,
SetMode = SetMode,
};
//if (Module.Equals("PM1") && Name.Equals("Mfc1"))
// Console.WriteLine("Mfc3:" + FeedBack);
return data;
}
}
#endregion
#region Methods
public override bool Initialize()
{
DATA.Subscribe($"{Module}.{Name}.DeviceData", () => DeviceData);
DATA.Subscribe($"{Module}.{Name}.FeedBack", () => FeedBack);
DATA.Subscribe($"{Module}.{Name}.SetPoint", () => SetPoint);
DATA.Subscribe($"{Module}.{Name}.SetMode", () => SetMode);
OP.Subscribe($"{Module}.{Name}.Ramp", (out string reason, int time, object[] param) =>
{
double target = Convert.ToDouble(param[0].ToString());
if (target < 0 || target > Scale)
{
reason = $"set {Display} value {target} out of range [0, {Scale}] {Unit}";
return false;
}
Ramp(target, time);
if (time > 0)
{
reason = $"{Display} ramp to {target} {Unit} in {time} seconds";
}
else
{
reason = $"{Display} ramp to {target} {Unit}";
}
return true;
});
OP.Subscribe($"{Module}.{Name}.SetMode", SetContrlMode);
//OP.Subscribe($"{Module}.{Name}.SetMode", (function, args) =>
//{
// if (!Enum.TryParse((string)args[0], out PressureCtrlMode mode))
// {
// EV.PostWarningLog(Module, $"Argument {args[0]}not valid");
// return false;
// }
// SetMode();
// return true;
//});
return true;
}
private bool SetContrlMode(out string reason, int time, object[] param)
{
return SetMfcMode((MfcCtrlMode)Enum.Parse(typeof(MfcCtrlMode), (string)param[0], true),
out reason);
}
public bool SetMfcMode(MfcCtrlMode mode, out string reason)
{
switch (mode)
{
case MfcCtrlMode.Normal:
SetMode = (double)MfcCtrlMode.Normal;
break;
case MfcCtrlMode.Close:
SetMode = (double)MfcCtrlMode.Close;
break;
case MfcCtrlMode.Open:
SetMode = (double)MfcCtrlMode.Open;
break;
case MfcCtrlMode.Hold:
SetMode = (double)MfcCtrlMode.Hold;
break;
default:
break;
}
reason = $"{Display} set to {mode}";
return true;
}
protected override void HandleMonitor()
{
MonitorRamping();
var isAllowErrMon = true;
if (!string.IsNullOrEmpty(_valve) && _disableFbSpErrMonWhenValveClosed)
{
// 如果配置为Valve关闭时不检测Feedback-SetPoint误差获取Valve状态
var devData = DATA.Poll($"{Module}.{_valve}.DeviceData");
if (devData is AITValveData valveData)
{
// 如果Valve关闭则不检测。
isAllowErrMon = valveData.Feedback;
_valveCloseTrigger.CLK = !valveData.IsOpen;
if (_valveCloseTrigger.Q)
{
Reset();
}
}
else
{
Debug.Assert(false, $"Unable to poll the {Module}.{_valve}.DeviceData.");
}
}
if (isAllowErrMon)
{
// 当SetPoint大于0.01并且没有Ramp时允许检测误差。
MonitorSpFbError(SetPoint >= 0.01 && _rampTimer.IsIdle(), SetPoint, FeedBack);
}
}
public void SetToDefaultByRamp(int time)
{
Ramp(DefaultSetPoint, time * 1000);
}
public override void Terminate()
{
Ramp(DefaultSetPoint, 5000);
base.Terminate();
}
public bool Ramp(double flowSetPoint, int time, out string reason)
{
if (HasAlarm)
{
reason = $"{DisplayName} in error status, can not flow";
return false;
}
if (flowSetPoint < 0 || flowSetPoint > Scale)
{
reason = $"{DisplayName} range is [0, {Scale}], can not flow {flowSetPoint}";
return false;
}
if (time > 0)
{
EV.PostInfoLog(Module, $"Set {DisplayName} flow to {flowSetPoint} {Unit} in {time / 1000:F0} seconds");
}
else
{
EV.PostInfoLog(Module, $"Set {DisplayName} flow to {flowSetPoint} {Unit}");
}
Ramp(flowSetPoint, time);
reason = string.Empty;
return true;
}
public void Ramp(int time)
{
Ramp(DefaultSetPoint, time);
}
public void Ramp(double target, int time)
{
_rampTimer.Stop();
target = Math.Max(0, target);
target = Math.Min(Scale, target);
_rampInitValue = SetPoint; //ramp 初始值取当前设定值,而非实际读取值.零漂问题
_rampTime = time;
_rampTarget = target;
_rampTimer.Start(_rampTime);
_rampSetMode = SetMode;
//EV.PostInfoLog(Module, $"{Name}.Ramp {_rampTarget} in {_rampTime} senconds,Mode {_rampSetMode/1000}");
}
public void StopRamp()
{
if (!_rampTimer.IsIdle())
{
if (_rampTime != 0)
{
Ramp(SetPoint, 0);
}
}
}
private void MonitorRamping()
{
if (!_rampTimer.IsIdle())
{
if (_rampTimer.IsTimeout() || _rampTime == 0)
{
_rampTimer.Stop();
SetPoint = _rampTarget;
}
else
{
SetPoint = _rampInitValue + (_rampTarget - _rampInitValue) * _rampTimer.GetElapseTime() / _rampTime;
}
if(_rampSetMode == 0 || _rampSetMode == 1 || _rampSetMode == 2 || _rampSetMode == 3)
{
SetMode = _rampSetMode;
}
}
}
#endregion
}
}