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; using System.Collections.Generic; using System.Diagnostics; using System.Xml; namespace FurnaceRT.Equipments.PMs.Devices { public class IoMFC : 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)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) { return _isFloatAioType ? _aiFlow.FloatValue : _aiFlow.Value; //var phy = _aiFlow == null ? 0 : (_isFloatAioType ? _aiFlow.FloatValue : _aiFlow.Value); //return Converter.Phy2Logic(phy, 0, Scale, 0, _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.IsRunning) { if (FeedBack < SetPoint * (100 - _stableMinValue) / 100 || FeedBack > SetPoint * (100 + _stableMaxValue) / 100) { _stableJudgmentTimer.Restart(); } if (_stableJudgmentTimer.ElapsedMilliseconds >= _stableJudgmentTime * 1000) { return true; } } else { _stableJudgmentTimer.Restart(); } } return false; } } public 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 = SetPoint, Ramping = _ramping, AlarmWatchTable = AlarmWatchTable, Scale = Scale, //IsWarning = !AlarmToleranceWarning.IsAcknowledged, //IsError = !AlarmToleranceAlarm.IsAcknowledged, Unit = Unit, VirtualSetPoint = (float)VirtualSetPoint, VirtualFeedBack = (float)VirtualFeedBack, VirtualAlarmWatchTable = VirtualAlarmWatchTable, VirtualRamping = VirtualRamping, IsInstalled= IsMFCInstalled }; return data; } } public string DisplayName { get { if (_scGasName != null) return _scGasName.StringValue; return Display; } } public string AlarmWatchTable { get; set; } public double VirtualSetPoint; public double VirtualFeedBack; public string VirtualAlarmWatchTable; public float VirtualRamping; private DeviceTimer _rampTimer = new DeviceTimer(); private double _rampTarget; private double _rampInitValue; private int _rampTime; private float _ramping; private double _phyScale; private ToleranceChecker _toleranceCheckerWarning = new ToleranceChecker(); private ToleranceChecker _toleranceCheckerAlarm = new ToleranceChecker(); private AIAccessor _aiFlow; private AOAccessor _aoFlow; private AOAccessor _aoRamp; private bool _isErrorStatus; 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 Stopwatch _stableJudgmentTimer = new Stopwatch(); private float _stableJudgmentTime = 1; private float _stableMinValue; private float _stableMaxValue; public AlarmEventItem AlarmToleranceWarning { get; set; } public AlarmEventItem AlarmToleranceAlarm { get; set; } private bool _isFloatAioType = false; private bool _isWait; private float _waitHigh; private float _waitLow; private string _writeLog = ""; public IoMFC(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"); _aiFlow = ParseAiNode("aiFlow", node, ioModule); _aoFlow = ParseAoNode("aoFlow", node, ioModule); _aoRamp = ParseAoNode("aoRamp", node, ioModule); _isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float"); string scBasePath = node.GetAttribute("scBasePath"); if (string.IsNullOrEmpty(scBasePath)) scBasePath = $"{Module}.MFC.{Name}"; else { scBasePath = scBasePath.Replace("{module}", Module); } _scGasName = SC.GetConfigItem($"{scBasePath}.{Name}.GasName"); _scEnable = SC.GetConfigItem($"{scBasePath}.{Name}.Enable"); _isMFCInstalled = SC.GetConfigItem($"{scBasePath}.{Name}.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}.SetParameters", SetParameters); OP.Subscribe($"{Module}.{Name}.SetMfcValue", SetMfcValue); OP.Subscribe($"{Module}.{Name}.SetMfcVirtualValue", SetMfcVirtualValue); //_phyScale = SC.GetValue($"{Module}.MFC.{Name}.PhyScale"); return true; } public void Monitor() { if(!string.IsNullOrEmpty(_writeLog)) { LOG.Write(_writeLog); _writeLog = ""; } //MonitorRamping(); //MonitorTolerance(); } 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 _isWait = false; float setpoint = 0.0f; if (paras[0].ToString() == "Continue") { EV.PostInfoLog(Module, $"Set {DisplayName} flow to Continue"); return true; } 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); } } float ramp = 0; if (paras.Length > 1) float.TryParse(paras[1].ToString(), out ramp); if (paras.Length >= 8) { var setUnit = paras[2].ToString(); var ramprateUnit = paras[3].ToString(); bool.TryParse(paras[4].ToString(), out _isWait); float.TryParse(paras[5].ToString(), out float waitHigh); float.TryParse(paras[6].ToString(), out float waitLow); var waitUnit = paras[7].ToString(); if(waitUnit.ToLower() == "%sv") { _waitHigh = setpoint * waitHigh; _waitLow = setpoint * waitLow; } else if(waitUnit.ToLower() == "%fs") { _waitHigh = (float)_scN2Scale.DoubleValue * waitHigh; _waitLow = (float)_scN2Scale.DoubleValue * waitLow; } else { _waitHigh = waitHigh; _waitLow = waitLow; } _stableJudgmentTimer.Stop(); if (ramprateUnit.ToLower() == "time") { if (ramp > 0) ramp = (float)(setpoint - FeedBack) / ramp; } else if (ramprateUnit.ToLower() == "%") { ramp = (float)_scN2Scale.DoubleValue * ramp; } } SetPoint = setpoint; _aoRamp.FloatValue = ramp; //LOG.Write($"{Name} setpoint={setpoint}, ramp={ramp} wait={_isWait} waitHigh={_waitHigh} waitLow={_waitLow}"); _writeLog = $"{Name} setpoint={setpoint}, ramp={ramp} wait={_isWait} waitHigh={_waitHigh} waitLow={_waitLow}"; return true; } public 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 (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); } float ramp = 0; if (paras.Length > 1) float.TryParse(paras[1].ToString(), out ramp); if (paras.Length > 2) { } SetPoint = setpoint; _ramping = ramp; _aoRamp.FloatValue = ramp; LOG.Write($"{Name} setpoint={setpoint}, ramp={ramp}"); return true; } public bool SetMfcSetPoint(out string reason, int time, object[] param) { reason = string.Empty; float setpoint = 0.0f; var paras = param[0].ToString().Split(';'); // setpoint;ramping;alarmWatchTable 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); } float ramp = 0; if (paras.Length > 1) float.TryParse(paras[1].ToString(), out ramp); _setpoint = setpoint; _ramping = ramp; _aoRamp.FloatValue = ramp; LOG.Write($"{Name} setpoint={setpoint}, ramp={ramp}"); return true; } public bool SetMfcVirtualValue(out string reason, int time, object[] param) { reason = string.Empty; float setpoint = 0.0f; var paras = param[0].ToString().Split(';'); // setpoint;ramping;alarmWatchTable 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); } VirtualSetPoint = setpoint; VirtualFeedBack = setpoint; if (paras.Length > 1) { float.TryParse(paras[1].ToString(), out _ramping); VirtualRamping = _ramping; } 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; } } } 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"); } } } public bool CheckWaitCondition(out string reason) { reason = ""; if (!_isWait || _waitHigh == 0 || _waitLow == 0) return true; if (_stableJudgmentTimer.IsRunning) { if (FeedBack < SetPoint - _waitLow || FeedBack > SetPoint + _waitHigh) { _stableJudgmentTimer.Restart(); } if (_stableJudgmentTimer.ElapsedMilliseconds >= _stableJudgmentTime * 1000) { return true; } } else { _stableJudgmentTimer.Restart(); } reason = $"{Name} feedback={FeedBack}, wait limit is ({SetPoint - _waitLow}, {SetPoint + _waitHigh})"; return false; } } }