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

497 lines
17 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.Collections.Generic;
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.Log;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
namespace Aitex.Core.RT.Device.Devices
{
public interface IValve
{
bool TurnValve(bool isOn, out string reason);
}
public class IoValve : BaseDevice, IDevice, IValve
{
private readonly DeviceTimer _timMfcRampTimeout = new DeviceTimer();
private const int MFC_RAMP_DURATION_SEC = 5;
private const int MFC_RAMP_TIMEOUT_SEC = 10;
private readonly object _mfcLock = new object();
private IoMFC _mappedMfc;
private bool _isValveOnAfterMfcRamp;
public string GVName { get { return Name; } }
public string GVDeviceID { get { return DeviceID; } }
public bool GVIsDefaultOpen { get { return _isDefaultOpen; } }
public Func<bool, bool> FuncCheckInterLock;
public Func<bool, bool> FuncForceOpen;
public Action<bool> ActionAfterOnOff; //作用于EPV1和EPV2
[Subscription(AITValveDataPropertyName.SetPoint)]
public bool SetPoint //Trueopen| Falseclose
{
get
{
if (_doOpen == null)
return false;
return _isNc ? _doOpen.Value : !_doOpen.Value;
}
set
{
if (_doOpen != null)
{
_doOpen.Value = _isNc ? value : !value;
}
if (_doClose != null)
{
_doClose.Value = _isNc ? !value : value;
}
}
}
[Subscription(AITValveDataPropertyName.Status)]
public bool Status //Trueopen | Falseclose
{
get
{
if (_diOpenSensor != null && _diCloseSensor != null)
return _diOpenSensor.Value && !_diCloseSensor.Value;
if (_diCloseSensor != null)
return !_diCloseSensor.Value;
if (_diOpen != null)
return _isNc ? _diOpen.Value : !_diOpen.Value;
if (_doOpen != null)
return _isNc ? _doOpen.Value : !_doOpen.Value;
if (_doClose != null)
return _isNc ? !_doClose.Value : _doClose.Value;
return false;
}
}
private AITValveData DeviceData
{
get
{
AITValveData data = new AITValveData()
{
UniqueName = _uniqueName,
DeviceName = GVName,
DefaultValue = GVIsDefaultOpen,
DeviceSchematicId = DeviceID,
DisplayName = Display,
Feedback = Status,
SetPoint = SetPoint,
};
return data;
}
}
/// <summary>
/// normal closed, 0 关闭1打开
/// </summary>
public bool _isNc;
/// <summary>
/// default open
/// </summary>
public bool _isDefaultOpen;
private DIAccessor _diOpenSensor;
private DIAccessor _diCloseSensor;
private DIAccessor _diOpen;
private DOAccessor _doOpen;
private DOAccessor _doClose;
private bool _operation;
R_TRIG forceOpenTrigger = new R_TRIG();
R_TRIG eventTrigger = new R_TRIG();
R_TRIG _mutexSignalTrigger = new R_TRIG();
DeviceTimer _timer = new DeviceTimer();
DeviceTimer _mutexSignalTimer = new DeviceTimer();
private SCConfigItem _scBypassEnableTable;
private string _uniqueName;
public IoValve(string module, XmlElement node, string ioModule = "")
{
var attrModule = node.GetAttribute("module");
base.Module = string.IsNullOrEmpty(attrModule) ? module : attrModule;
base.Name = node.GetAttribute("id");
base.Display = node.GetAttribute("display");
base.DeviceID = node.GetAttribute("schematicId");
_isNc = Convert.ToBoolean(node.GetAttribute("isNc"));
_isDefaultOpen = Convert.ToBoolean(node.GetAttribute("isDefaultOpen"));
_diOpenSensor = ParseDiNode("diOpenSensor", node, ioModule);
_diCloseSensor = ParseDiNode("diCloseSensor", node, ioModule);
_doOpen = ParseDoNode("doOpen", node, ioModule);
_diOpen = ParseDiNode("diOpen", node, ioModule);
_doClose = ParseDoNode("doClose", node, ioModule);
_uniqueName = $"{Module}.{Name}";
_scBypassEnableTable = ParseScNode("BypassEnableTable", node, module, "System.BypassEnableTable");
}
public bool Initialize()
{
DATA.Subscribe($"Device.{Module}.{GVName}", () => DeviceData);
DATA.Subscribe($"{_uniqueName}.DeviceData", () => DeviceData);
OP.Subscribe($"{_uniqueName}.{AITValveOperation.GVTurnValve}", InvokeOpenCloseValve);
DEVICE.Register(String.Format("{0}.{1}", Name, AITValveOperation.GVTurnValve), (out string reason, int time, object[] param) =>
{
bool bOn = Convert.ToBoolean((string)param[0]);
bool ret = TurnValve(bOn, out reason);
if (ret)
{
reason = string.Format("Valve {0}{1}", Name, bOn ? "Open" : "Close");
return true;
}
return false;
});
//for recipe
DEVICE.Register(String.Format("{0}", Name), (out string reason, int time, object[] param) =>
{
bool bOn = Convert.ToBoolean((string)param[0]);
bool ret = TurnValve(bOn, out reason);
if (ret)
{
reason = string.Format("Valve {0}{1}", Name, bOn ? "Open" : "Close");
return true;
}
return false;
});
return true;
}
public void Terminate()
{
string reason;
lock (_mfcLock)
{
if (_mappedMfc != null)
{
_mappedMfc.StopRamp();
_mappedMfc = null;
_timMfcRampTimeout.Stop();
}
}
TurnValve(_isDefaultOpen, out reason);
}
private bool InvokeOpenCloseValve(string method, object[] args)
{
string reason;
bool op = Convert.ToBoolean(args[0]);
string name = op ? "Open" : "Close";
if (!TurnValve(op, out reason))
{
EV.PostWarningLog(Module, $"Can not {name} valve {Module}.{Name}, {reason}");
return false;
}
EV.PostInfoLog(Module, $"{name} valve {Module}.{Name}");
return true;
}
public void Monitor()
{
try
{
if (_diOpenSensor != null && _diCloseSensor != null)
{
if (_diOpenSensor.Value != _diCloseSensor.Value)
{
_mutexSignalTimer.Start(2000);
}
_mutexSignalTrigger.CLK = _mutexSignalTimer.IsTimeout();
if (_mutexSignalTrigger.Q)
{
EV.PostWarningLog(Module, $"Valve {Name} was abnormalReasondiOpenSensor's value is {_diOpenSensor.Value} and diCloseSensor's value is {_diCloseSensor.Value} too.");
}
}
MonitorMfc();
if (_timer.IsTimeout())
{
forceOpenTrigger.RST = true;
_timer.Stop();
if (Status != _operation)
{
if (_operation)
{
string reason;
if (!_doOpen.Check(_isNc ? true : false, out reason))
EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Open", "Failed for interlock " + reason);
else
EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Open", "Valve keep closed ");
}
else
{
string reason;
if (!_doOpen.Check(_isNc ? true : false, out reason))
EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Close", "Failed for interlock " + reason);
else
EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Close", "Valve keep open");
}
}
else if(ActionAfterOnOff != null)
{
ActionAfterOnOff(Status);
}
_operation = SetPoint;
}
else if (_timer.IsIdle())
{
eventTrigger.CLK = SetPoint != _operation; // fire event only check at first, SetPoint set by interlock
if (eventTrigger.Q)
{
if (_operation)
{
string reason;
if (!_doOpen.Check(_isNc ? true : false, out reason))
EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}Reason{2}", Display, "Close", reason));
else
EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}Reason {2}", Display, "Close", "PLC kept"));
}
else
{
string reason;
if (!_doOpen.Check(_isNc ? true : false, out reason))
EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}Reason{2}", Display, "Open", reason));
else
EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}Reason {2}", Display, "Open", "PLC Kept"));
}
_operation = SetPoint;
}
}
//达到一定条件,强制打开阀门
if(FuncForceOpen!= null && !_scBypassEnableTable.BoolValue)
{
forceOpenTrigger.CLK = FuncForceOpen(Status);
if (forceOpenTrigger.Q)
{
SetPoint = !Status;
EV.PostMessage(Module, EventEnum.SwInterlock, Module, $"Force Set {Name} to {!Status} for Interlock enable table!");
}
}
}
catch (Exception ex)
{
LOG.Write(ex);
}
}
public bool TurnValve(bool isOn, out string reason)
{
reason = "";
bool bValue = _isNc ? isOn : !isOn;
// 如果要打开阀门,并且阀门已经在打开状态,则啥也不干
if (isOn && Status)
{
return true;
}
if (_doOpen != null)
{
if (!_doOpen.Check(bValue, out reason))
{
EV.PostWarningLog(Module, $"Turn {Display} {ValveStateToString(isOn)} failed for Interlock!");
EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}Reason{2}", Display, "Open", reason));
return false;
}
if (FuncCheckInterLock != null && !_scBypassEnableTable.BoolValue)
{
if (!FuncCheckInterLock(isOn))
{
EV.PostWarningLog(Module, $"Turn {Display} {ValveStateToString(isOn)} failed for check condition!");
return false;
}
}
}
if (_doClose != null)
{
if (!_doClose.Check(!bValue, out reason))
{
EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1}Reason{2}", Display, "Close", reason));
return false;
}
}
if (ValveToMFCMap.ValveControlMFC.TryGetValue(Name, out var _mfcName)) // 需要Ramp前置MFC
{
if (!isOn)//关闭蝶阀时关闭MFC,如果MFC为0时可以不用设置但是没有获取MFC当前状态的函数
{
if (_doOpen.Value)//气阀是开启状态了需要关闭MFC
{
CloseMFC(isOn, _mfcName);
return true;
}
}
else//这是开阀指令开阀时如果阀门时关闭状态MFC设置为零保证气流平缓
{
if (Name != "TMVent" && !_doOpen.Value)//气阀是关闭状态了需要关闭MFC
{
CloseMFC(isOn, _mfcName);
return true;
}
}
}
EV.PostInfoLog(Module, $"Turn {Display} {ValveStateToString(isOn)}");
reason = "";
SetPoint = isOn;
_operation = isOn;
_timer.Start(2000); //2 seconds to monitor
return true;
}
/// <summary>
/// 如果存在前置MFC则等待MFC流量到0后再开关阀。
/// </summary>
private void MonitorMfc()
{
lock (_mfcLock)
{
if (_mappedMfc != null)
{
if (_mappedMfc.FeedBack < _mappedMfc.Scale * 0.015) // 等待MFC Ramp到0
{
SetPoint = _isValveOnAfterMfcRamp;
_operation = _isValveOnAfterMfcRamp;
_mappedMfc = null;
_timMfcRampTimeout.Stop();
_timer.Start(2000);
}
else if (_timMfcRampTimeout.IsTimeout()) // 等待MFC Ramp 到0超时
{
_mappedMfc.StopRamp();
_timMfcRampTimeout.Stop();
EV.PostAlarmLog(Module,
$"Timeout to ramp {_mappedMfc.Name} to 0.0{_mappedMfc.Unit} when Turning {Name} {ValveStateToString(_isValveOnAfterMfcRamp)}");
_mappedMfc = null;
}
}
}
}
/// <summary>
/// 关闭前置MFC。
/// </summary>
/// <param name="isOn"></param>
/// <param name="_mfcName"></param>
private void CloseMFC(bool isOn, string _mfcName)
{
lock (_mfcLock)
{
// 获取前置MFC
_mappedMfc = DEVICE.GetDevice<IoMFC>($"{Module}.{_mfcName}");
if (_mappedMfc == null)
{
// 如果未获取到前置MFC则直接操作蝶阀
EV.PostWarningLog(Module, $"Unable to find mapped MFC {_mfcName} of valve {Name}");
EV.PostInfoLog(Module, $"Turn {Display} {ValveStateToString(isOn)}");
SetPoint = isOn;
_operation = isOn;
}
else
{
EV.PostInfoLog(Module, $"Start to ramp {_mfcName} to 0.0{_mappedMfc.Unit}");
_isValveOnAfterMfcRamp = isOn;
_mappedMfc.Ramp(0, 5*1000);
_timMfcRampTimeout.Start(10 * 1000);
}
_timer.Start(2000);
}
}
private string ValveStateToString(bool isOn)
{
return isOn ? "On" : "Off";
}
public void Reset()
{
eventTrigger.RST = true;
forceOpenTrigger.RST = true;
lock (_mfcLock)
{
if (_mappedMfc != null)
{
_mappedMfc.Reset();
_mappedMfc = null;
_timMfcRampTimeout.Stop();
}
}
}
}
public static class ValveToMFCMap
{
/// <summary>
/// Key= IoValve_id , Value=IoMFC_id
/// </summary>
public static Dictionary<string, string> ValveControlMFC = new Dictionary<string, string>()
{
//{ "TMVent","Mfc60" },
{ "V96",$"Mfc38" },
{ "V94",$"Mfc36" },
};
}
}