using Aitex.Core.Common.DeviceData; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Device; using Aitex.Core.RT.Event; using Aitex.Core.RT.IOCore; using Aitex.Core.RT.Log; using Aitex.Core.RT.OperationCenter; using Aitex.Core.RT.SCCore; using Aitex.Core.RT.Tolerance; using Aitex.Core.Util; using Aitex.Core.Utilities; using MECF.Framework.Common.Event; using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.MFCs; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; namespace Aitex.Core.RT.Device.Unit { public class IoMfc4 : BaseDevice, IDevice, IMfc { public string Unit { get; set; } public double Scale { get { if (_scN2Scale == null || _scScaleFactor == null) return 0; return _scN2Scale.DoubleValue * _scScaleFactor.DoubleValue; } } private double _setpoint; public double SetPoint { get { return _setpoint; } set { _setpoint = value; if (_aoFlow != null) { //var setpoint = Scale != 0 ? (float)(value * _maxScale / Scale) : value; if (_isFloatAioType) _aoFlow.FloatValue = (float)Converter.Logic2Phy(value, 0, Scale, _phyScaleMin, _phyScale); else _aoFlow.Value = (short)Converter.Logic2Phy(value, 0, Scale, _phyScaleMin, _phyScale); } } } 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; var phy = _aiFlow == null ? 0 : (_isFloatAioType ? _aiFlow.FloatValue : _aiFlow.Value); return Converter.Phy2Logic(phy, 0, Scale, _phyScaleMin, _phyScale); //return _maxScale != 0 ? aiValue * Scale / _maxScale : aiValue; } return 0; } } public bool EnableAlarm { get { if (_scEnableAlarm != null) return _scEnableAlarm.BoolValue; return false; } } public bool IsStable { get { if (!_stableDelayTimer.IsIdle() && _stableDelayTimer.GetElapseTime() >= _stableDelayTime * 1000) { if (!_stableJudgmentTimer.IsIdle()) { if (FeedBack < SetPoint * (100 - _stableMinValue) / 100 || FeedBack > SetPoint * (100 + _stableMaxValue) / 100) { _stableJudgmentTimer.Start(0); } if (_stableJudgmentTimer.GetElapseTime() >= _stableJudgmentTime * 1000) { return true; } } else { _stableJudgmentTimer.Start(0); } } return false; } } private AITMfcData DeviceData { get { AITMfcData data = new AITMfcData() { UniqueName = $"{Module}.{Name}", Type = "MFC", Module = Module, DeviceName = Name, DeviceSchematicId = DeviceID, DisplayName = DisplayName, FeedBack = FeedBack < 0 ? 0 : FeedBack, SetPoint = _rampTarget, Ramping = _ramping, AlarmWatchTable = AlarmWatchTable, Scale = Scale, //IsWarning = !AlarmToleranceWarning.IsAcknowledged, //IsError = !AlarmToleranceAlarm.IsAcknowledged, Unit = Unit, }; return data; } } public string DisplayName { get { if (_scGasName != null) return _scGasName.StringValue; return Display; } } public string AlarmWatchTable { get; set; } private DeviceTimer _rampTimer = new DeviceTimer(); private double _rampTarget; private double _rampInitValue; private int _rampTime; private float _ramping; private double _phyScaleMin; private double _phyScale; private ToleranceChecker _toleranceCheckerWarning = new ToleranceChecker(); private ToleranceChecker _toleranceCheckerAlarm = new ToleranceChecker(); private AIAccessor _aiFlow; private AOAccessor _aoFlow; private DOAccessor _doOpen; private DOAccessor _doClose; private bool _isMFCInstalled; private int _mfcIndex; public bool IsMFCInstalled { get { return _isMFCInstalled; } } public int MFCIndex { get { return _mfcIndex; } } protected SCConfigItem _scGasName; protected SCConfigItem _scEnable; private SCConfigItem _scN2Scale; private SCConfigItem _scScaleFactor; private SCConfigItem _scEnableAlarm; private SCConfigItem _scDefaultSetPoint; private SCConfigItem _scRegulationFactor; //tolerance check private float _alarmJudgmentRange; private float _warningJudgmentRange; private float _alarmJudgmentTime; private float _warningJudgmentTime; private float _toleranceJudgmentDelayTime; private DeviceTimer _toleranceJudgmentDelayTimer = new DeviceTimer(); //stable check private DeviceTimer _stableDelayTimer = new DeviceTimer(); private float _stableDelayTime; private DeviceTimer _stableJudgmentTimer = new DeviceTimer(); private float _stableJudgmentTime; private float _stableMinValue; private float _stableMaxValue; private int _toleranceAlarmEventId; private int _toleranceWarningEventId; public AlarmEventItem AlarmToleranceWarning { get; set; } public AlarmEventItem AlarmToleranceAlarm { get; set; } private bool _isFloatAioType = false; public IoMfc4(string module, XmlElement node, string ioModule = "") { Unit = node.GetAttribute("unit"); base.Module = string.IsNullOrEmpty(node.GetAttribute("module")) ? module : node.GetAttribute("module"); base.Name = node.GetAttribute("id"); base.Display = node.GetAttribute("display"); base.DeviceID = node.GetAttribute("schematicId"); int.TryParse(node.GetAttribute("MFCIndex"), out _mfcIndex); int.TryParse(node.GetAttribute("toleranceAlarmEventId"), out _toleranceAlarmEventId); int.TryParse(node.GetAttribute("toleranceWarningEventId"), out _toleranceWarningEventId); _aiFlow = ParseAiNode("aiFlow", node, ioModule); _aoFlow = ParseAoNode("aoFlow", node, ioModule); _doOpen = ParseDoNode("doOpen", node, ioModule); _doClose = ParseDoNode("doClose", node, ioModule); _isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float"); string scBasePath = node.GetAttribute("scBasePath"); if (string.IsNullOrEmpty(scBasePath)) scBasePath = $"{Module}.{Name}"; else { scBasePath = scBasePath.Replace("{module}", Module); } _scGasName = SC.GetConfigItem($"{scBasePath}.{Name}.GasName"); _scEnable = SC.GetConfigItem($"{scBasePath}.{Name}.Enable"); _isMFCInstalled = SC.GetConfigItem($"{scBasePath}.{Display}.IsMFCInstalled").BoolValue; _scN2Scale = ParseScNode("scN2Scale", node, ioModule, $"{scBasePath}.{Name}.N2Scale"); _scScaleFactor = ParseScNode("scScaleFactor", node, ioModule, $"{scBasePath}.{Name}.ScaleFactor"); _scEnableAlarm = ParseScNode("scEnableAlarm", node, ioModule, $"{scBasePath}.{Name}.EnableAlarm"); _scDefaultSetPoint = ParseScNode("scDefaultSetPoint", node, ioModule, $"{scBasePath}.{Name}.DefaultSetPoint"); _scRegulationFactor = ParseScNode("scFlowRegulationFactor", node, ioModule, $"{scBasePath}.{Name}.RegulationFactor"); if (SC.ContainsItem($"{scBasePath}.{Name}.FlowUnit")) Unit = SC.GetStringValue($"{scBasePath}.{Name}.FlowUnit"); } public bool Initialize() { DATA.Subscribe($"{Module}.{Name}.DeviceData", () => DeviceData); DATA.Subscribe($"{Module}.{Name}.Feedback", () => FeedBack); DATA.Subscribe($"{Module}.{Name}.SetPoint", () => SetPoint); DATA.Subscribe($"{Module}.{Name}.ForceOpen", () => _doOpen.Value); DATA.Subscribe($"{Module}.{Name}.ForceClose", () => _doClose.Value); 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}.SetMfcFlow", SetMfcFlow); OP.Subscribe($"{Module}.{Name}.SetParameters", SetParameters); OP.Subscribe($"{Module}.{Name}.SetMfcValue", SetMfcValue); _phyScale = SC.GetValue($"{Module}.MFC.{Name}.PhyScale"); _phyScaleMin = SC.GetValue($"{Module}.MFC.{Name}.PhyScaleMin"); return true; } public void Monitor() { //ProcessSensorStatus(); MonitorRamping(); MonitorTolerance(); } //private void ProcessSensorStatus() //{ // BitArray SensorStatus = new BitArray(new int[] { _aiStatus.Value }); // _isErrorStatus = SensorStatus[15]; //} public void SetStableParameters(string tableId) { if (SC.ContainsItem($"{Module}.RecipeEditParameter.FlowStabilizeTable.Table{tableId}.DelayTime")) { var scPath = $"{Module}.RecipeEditParameter.FlowStabilizeTable.Table{tableId}"; _stableDelayTime = (float)SC.GetValue($"{scPath}.DelayTime"); _stableJudgmentTime = (float)SC.GetValue($"{scPath}.JudgmentTime"); _stableMinValue = (float)SC.GetValue($"{scPath}.MinValue"); _stableMaxValue = (float)SC.GetValue($"{scPath}.MaxValue"); _stableDelayTimer.Start(0); _stableJudgmentTimer.Stop(); } } public bool ResetWarningChecker() { _toleranceCheckerWarning.Reset(_warningJudgmentTime); return true; } public bool ResetAlarmChecker() { _toleranceCheckerAlarm.Reset(_alarmJudgmentTime); return true; } public void Reset() { _toleranceCheckerWarning.Reset(_warningJudgmentTime); _toleranceCheckerAlarm.Reset(_alarmJudgmentTime); AlarmToleranceWarning.Reset(); AlarmToleranceAlarm.Reset(); } public void Terminate() { Ramp(DefaultSetPoint, 0); } private bool SetParameters(out string reason, int time, object[] param) { reason = string.Empty; var paras = param[0].ToString().Split(';');//flow;rampTime;alarmTable float setpoint = 0.0f; if (paras[0].ToString() == "Continue") { EV.PostInfoLog(Module, $"Set {DisplayName} flow to Continue"); return true; } else if (paras[0].ToString() == "ForceOpen") { _doOpen.SetValue(true, out reason); } else if (paras[0].ToString() == "ForceClose") { _doClose.SetValue(true, out reason); } else { if (System.Text.RegularExpressions.Regex.Match(paras[0].ToString(), @"[a-zA-Z]").Success) { var table = paras[0].ToString().Split(':')[0]; if (SC.ContainsItem($"{Module}.RecipeEditParameter.FlowSetting.{Name}.{table}")) setpoint = (float)SC.GetValue($"{Module}.RecipeEditParameter.FlowSetting.{Name}.{table}"); } else { float.TryParse(paras[0].ToString(), out setpoint); } } if (paras.Length > 1) float.TryParse(paras[1].ToString(), out _ramping); if (paras.Length > 2) { var alarmWatchTable = paras[2].ToString(); AlarmWatchTable = alarmWatchTable; if (SC.ContainsItem($"{Module}.RecipeEditParameter.AlarmWatchTable.FlowAlarmWatch.Table{alarmWatchTable}.DelayTime")) { var scPath = $"{Module}.RecipeEditParameter.AlarmWatchTable.FlowAlarmWatch.Table{alarmWatchTable}"; _toleranceJudgmentDelayTime = (float)SC.GetValue($"{scPath}.DelayTime"); _alarmJudgmentRange = (float)SC.GetValue($"{scPath}.AlarmJudgment"); _warningJudgmentRange = (float)SC.GetValue($"{scPath}.WarningJudgment"); _alarmJudgmentTime = (float)SC.GetValue($"{scPath}.AlarmJudgmentTime"); _warningJudgmentTime = (float)SC.GetValue($"{scPath}.WarningJudgmentTime"); _toleranceJudgmentDelayTimer.Start(0); } else { _toleranceJudgmentDelayTime = 0; _alarmJudgmentRange = 0; _warningJudgmentRange = 0; _alarmJudgmentTime = 0; _warningJudgmentTime = 0; _toleranceJudgmentDelayTimer.Stop(); } ResetWarningChecker(); ResetAlarmChecker(); } //_rampTime = _ramping != 0 ? (int)(Math.Abs(setpoint - FeedBack) / _ramping) * 1000 : 0; _rampTime = (int)(_ramping * 1000); return Ramp(setpoint, _rampTime, out reason); } private bool SetMfcFlow(out string reason, int time, object[] param) { float setpoint = 0.0f; var paras = param[0].ToString().Split(';'); if (System.Text.RegularExpressions.Regex.Match(paras[0].ToString(), @"[a-zA-Z]").Success) { var table = paras[0].ToString().Split(':')[0]; if (SC.ContainsItem($"{Module}.RecipeEditParameter.FlowSetting.{Name}.{table}")) setpoint = (float)SC.GetValue($"{Module}.RecipeEditParameter.FlowSetting.{Name}.{table}"); } else { float.TryParse(param[0].ToString(), out setpoint); } if (paras.Length == 1) { Ramp(setpoint, 0, out reason); } else { Ramp(setpoint, (int)Convert.ToSingle(paras[1]), out reason); } return true; } private bool SetMfcValue(out string reason, int time, object[] param) { reason = string.Empty; float setpoint = 0.0f; var paras = param[0].ToString().Split(';'); // setpoint;ramping;alarmWatchTable if (paras[0].ToString() == "ForceOpen") { _doOpen.SetValue(true, out reason); _doClose.SetValue(false, out reason); } else if (paras[0].ToString() == "ForceClose") { _doClose.SetValue(true, out reason); _doOpen.SetValue(false, out reason); } else { if (System.Text.RegularExpressions.Regex.Match(paras[0].ToString(), @"[a-zA-Z]").Success) { var table = paras[0].ToString().Split(':')[0]; if (SC.ContainsItem($"{Module}.RecipeEditParameter.FlowSetting.{Name}.{table}")) setpoint = (float)SC.GetValue($"{Module}.RecipeEditParameter.FlowSetting.{Name}.{table}"); } else { float.TryParse(paras[0].ToString(), out setpoint); } _doOpen.SetValue(false, out reason); _doClose.SetValue(false, out reason); } if (paras.Length > 1) float.TryParse(paras[1].ToString(), out _ramping); if (paras.Length > 2) { AlarmWatchTable = paras[2].ToString(); if (SC.ContainsItem($"{Module}.RecipeEditParameter.AlarmWatchTable.FlowAlarmWatch.Table{AlarmWatchTable}.DelayTime")) { var scPath = $"{Module}.RecipeEditParameter.AlarmWatchTable.FlowAlarmWatch.Table{AlarmWatchTable}"; _toleranceJudgmentDelayTime = (float)SC.GetValue($"{scPath}.DelayTime"); _alarmJudgmentRange = (float)SC.GetValue($"{scPath}.AlarmJudgment"); _warningJudgmentRange = (float)SC.GetValue($"{scPath}.WarningJudgment"); _alarmJudgmentTime = (float)SC.GetValue($"{scPath}.AlarmJudgmentTime"); _warningJudgmentTime = (float)SC.GetValue($"{scPath}.WarningJudgmentTime"); } else { _toleranceJudgmentDelayTime = 0; _alarmJudgmentRange = 0; _warningJudgmentRange = 0; _alarmJudgmentTime = 0; _warningJudgmentTime = 0; } } //_rampTime = _ramping != 0 ? (int)(Math.Abs(setpoint - FeedBack) / _ramping * 1000) : 0; _rampTime = (int)(_ramping * 1000); Ramp(setpoint, _rampTime, out reason); return true; } 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) { target = Math.Max(0, target); target = Math.Min(Scale, target); _rampInitValue = FeedBack; //ramp 初始值取当前设定值,而非实际读取值.零漂问题 _rampTime = time; _rampTarget = target; _rampTimer.Start(time); } public void StopRamp() { 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; } } } public bool SetOpen(bool isOpen, out string reason) { reason = string.Empty; if (_doOpen != null) { if (!_doOpen.SetValue(isOpen, out reason)) { LOG.Write(reason); return false; } } if (_doClose != null) { if (!_doClose.SetValue(!isOpen, out reason)) { LOG.Write(reason); return false; } } return true; } private void MonitorTolerance() { if (!EnableAlarm || SetPoint < 0.01 || _toleranceJudgmentDelayTimer.IsIdle() || _toleranceJudgmentDelayTimer.GetElapseTime() < _toleranceJudgmentDelayTime * 1000) { _toleranceCheckerWarning.RST = true; _toleranceCheckerAlarm.RST = true; return; } if (_warningJudgmentRange != 0 && _warningJudgmentTime > 0) { _toleranceCheckerWarning.Monitor(FeedBack, (SetPoint - Math.Abs(_warningJudgmentRange)), (SetPoint + Math.Abs(_warningJudgmentRange)), _warningJudgmentTime); if (_toleranceCheckerWarning.Trig) { //AlarmToleranceWarning.Description = $"{Display} flow out of range {_warningJudgmentRange} {Unit} in {_warningJudgmentTime:F0} seconds"; AlarmToleranceWarning.Set($"{Display} flow out of range {_warningJudgmentRange} {Unit} in {_warningJudgmentTime:F0} seconds"); } } if (_alarmJudgmentRange != 0 && _alarmJudgmentTime > 0) { _toleranceCheckerAlarm.Monitor(FeedBack, (SetPoint - Math.Abs(_alarmJudgmentRange)), (SetPoint + Math.Abs(_alarmJudgmentRange)), _alarmJudgmentTime); if (_toleranceCheckerAlarm.Trig) { //AlarmToleranceAlarm.Description = $"{Display} flow out of range {_alarmJudgmentRange} {Unit} in {_alarmJudgmentTime:F0} seconds"; AlarmToleranceAlarm.Set($"{Display} flow out of range {_alarmJudgmentRange} {Unit} in {_alarmJudgmentTime:F0} seconds"); } } } } }