using Aitex.Core.Common.DeviceData.IoDevice; 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; using System; using System.Xml; namespace Aitex.Core.RT.Device.Devices { public class IoPSU : BaseDevice, IDevice { #region Variables private float _resLimitMax; private float _outputLimitHighTuningTemp = 1600; private readonly bool _isFloatAioType; private readonly AIAccessor _aiOutputVoltage; private readonly AIAccessor _aiOutputArms; private readonly AIAccessor _aiOutputPower; private readonly AIAccessor _aiSimVoltage; private readonly AIAccessor _aiSimArms; //private AOAccessor _aoEnable = null; //private AOAccessor _aoReset = null; private readonly AOAccessor _aoConstant; private readonly AOAccessor _aoOutputLimitHigh; private readonly AOAccessor _aoOutputLimitLow; private readonly DIAccessor _diStatus; private readonly DIAccessor _diAlarm; private readonly DIAccessor _diHeatEnable; private DIAccessor _diCommunicationError; private readonly DOAccessor _doReset; private readonly DOAccessor _doStatus; private readonly DOAccessor _doHeatEnable; private readonly DOAccessor _doRelatedEnable; //每个Enable同时关联的Inner,Middle,Out Enable private readonly R_TRIG _alarmTrig = new(); private readonly R_TRIG _commAlarmTrig = new(); private readonly R_TRIG _enableTrig = new(); private readonly R_TRIG _enableTrig2 = new(); private readonly R_TRIG _trigVoltage = new(); private readonly R_TRIG _trigCurrent = new(); private readonly R_TRIG _trigResOutOfRange = new(); private readonly DeviceTimer _timResetPulse = new(); private readonly DeviceTimer _timResOutOfRange = new(); private readonly IoPsuData _deviceData = new(); #endregion #region Constructors public IoPSU(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule) { _aiOutputVoltage = ParseAiNode("aiOutputVoltage", node, ioModule); _aiOutputArms = ParseAiNode("aiOutputArms", node, ioModule); _aiOutputPower = ParseAiNode("aiOutputPower", node, ioModule); _aiSimVoltage = ParseAiNode("aiSimVoltage", node, ioModule); _aiSimArms = ParseAiNode("aiSimArms", node, ioModule); _aoConstant = ParseAoNode("aoConstant", node, ioModule); _aoOutputLimitHigh = ParseAoNode("aoOutputLimitHigh", node, ioModule); _aoOutputLimitLow = ParseAoNode("aoOutputLimitLow", node, ioModule); _doReset = ParseDoNode("doReset", node, ioModule); _doStatus = ParseDoNode("doStatus", node, ioModule); _diStatus = ParseDiNode("diStatus", node, ioModule); _diAlarm = ParseDiNode("diAlarm", node, ioModule); _doHeatEnable = ParseDoNode("doHeatEnable", node, ioModule); _diHeatEnable = ParseDiNode("diHeatEnable", node, ioModule); _doRelatedEnable = ParseDoNode("doRelatedEnable", node, ioModule); _diCommunicationError = ParseDiNode("diCommunicationError", node, ioModule); _isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float"); } #endregion #region Properties public float OutputVoltageFeedBack => _aiOutputVoltage == null ? 0 : (_isFloatAioType ? _aiOutputVoltage.FloatValue : _aiOutputVoltage.Value); public float OutputArmsFeedBack => _aiOutputArms == null ? 0 : (_isFloatAioType ? _aiOutputArms.FloatValue : _aiOutputArms.Value); public float ResistanceLimitMax => _resLimitMax; public float Resistance => OutputArmsFeedBack == 0 ? 0 : OutputVoltageFeedBack / OutputArmsFeedBack; public bool IsResistanceOutOfRange => IsResistanceTooHigh; public bool IsResistanceTooHigh { get; private set; } public float OutputPowerFeedBack => _aiOutputPower == null ? 0 : (_isFloatAioType ? _aiOutputPower.FloatValue : _aiOutputPower.Value); public bool StatusFeedBack => _diStatus?.Value ?? false; public float SimVoltageFeedBack => _aiSimVoltage == null ? 0 : (_isFloatAioType ? _aiSimVoltage.FloatValue : _aiSimVoltage.Value); public float SimArmsFeedBack => _aiSimArms == null ? 0 : (_isFloatAioType ? _aiSimArms.FloatValue : _aiSimArms.Value); public bool AlarmFeedBack => _diAlarm?.Value ?? false; public float ConstantSetPoint { get => _aoConstant == null ? 0 : (_isFloatAioType ? _aoConstant.FloatValue : _aoConstant.Value); set { if (_isFloatAioType) { _aoConstant.FloatValue = value; } else { _aoConstant.Value = (short)value; } } } public float OutputLimitHigh => _aoOutputLimitHigh?.FloatValue ?? 0; public float OutputLimitLow => _aoOutputLimitLow?.FloatValue ?? 0; public bool AllHeatEnable => _diHeatEnable?.Value ?? false; private IoPsuData DeviceData { get { _deviceData.IsAlarm = AlarmFeedBack; _deviceData.OutputStatusFeedBack = StatusFeedBack; _deviceData.AllHeatEnable = AllHeatEnable; _deviceData.ConstantSetPoint = ConstantSetPoint; _deviceData.OutputArmsFeedBack = OutputArmsFeedBack; _deviceData.OutputPowerFeedBack = OutputPowerFeedBack; _deviceData.OutputVoltageFeedback = OutputVoltageFeedBack; _deviceData.ResistanceLimitMax = ResistanceLimitMax; _deviceData.Resistance = Resistance; _deviceData.IsResistanceOutOfRange = IsResistanceOutOfRange; _deviceData.IsResistanceTooHigh = IsResistanceTooHigh; _deviceData.OutputLimitHighTuningTemp = _outputLimitHighTuningTemp; _deviceData.OutputLimitHigh = OutputLimitHigh; _deviceData.OutputLimitLow = OutputLimitLow; return _deviceData; } } public Func FuncCheckInterLock { get; set; } #endregion #region Methods public bool Initialize() { DATA.Subscribe($"{Module}.{Name}.OutputVoltageFeedBack", () => OutputVoltageFeedBack); DATA.Subscribe($"{Module}.{Name}.OutputArmsFeedBack", () => OutputArmsFeedBack); DATA.Subscribe($"{Module}.{Name}.ResistanceLimitMax", () => ResistanceLimitMax); DATA.Subscribe($"{Module}.{Name}.Resistance", () => Resistance); DATA.Subscribe($"{Module}.{Name}.IsResistanceOutOfRange", () => IsResistanceOutOfRange); DATA.Subscribe($"{Module}.{Name}.IsResistanceTooHigh", () => IsResistanceTooHigh); DATA.Subscribe($"{Module}.{Name}.OutputPowerFeedBack", () => OutputPowerFeedBack); DATA.Subscribe($"{Module}.{Name}.StatusFeedBack", () => StatusFeedBack); DATA.Subscribe($"{Module}.{Name}.SimVoltageFeedBack", () => SimVoltageFeedBack); DATA.Subscribe($"{Module}.{Name}.SimArmsFeedBack", () => SimArmsFeedBack); DATA.Subscribe($"{Module}.{Name}.ConstantSetPoint", () => ConstantSetPoint); /*DATA.Subscribe($"{Module}.{Name}.OutputLimitHigh", () => OutputLimitHigh); DATA.Subscribe($"{Module}.{Name}.OutputLimitLow", () => OutputLimitLow); DATA.Subscribe($"{Module}.{Name}.OutputLimitHighTuningTemp", () => _outputLimitHighTuningTemp);*/ DATA.Subscribe($"{Module}.{Name}.AlarmFeedBack", () => AlarmFeedBack); DATA.Subscribe($"{Module}.{Name}.AllHeatEnable", () => AllHeatEnable); DATA.Subscribe($"{Module}.{Name}.DeviceData", () => DeviceData); OP.Subscribe($"{Module}.{Name}.SetHeadHeaterEnable", (function, args) => { var isTrue = Convert.ToBoolean(args[0]); SetHeadHeaterEnable(isTrue, out _); return true; }); OP.Subscribe($"{Module}.{Name}.SetPSUEnable", (function, args) => { var isTrue = Convert.ToBoolean(args[0]); SetPSUEnable(isTrue, out _); return true; }); OP.Subscribe($"{Module}.{Name}.SetPSUReset", (function, args) => { var isTrue = Convert.ToBoolean(args[0]); SetPSUReset(isTrue, out _); return true; }); /* OP.Subscribe($"{Module}.{Name}.SetOutputLimitHighTuningTemp", (function, args) => { if (args.Length == 1 && args[0] is double value) { SC.SetItemValue($"PM.{Module}.Heater.PSU2OutputLimitHighTuningTemp", value); return true; } EV.PostWarningLog(Module, $"SetOutputLimitHighTuningTemp fail, args error"); return false; }); OP.Subscribe($"{Module}.{Name}.SetOutputLimitHighByRecipe", (out string reason, int time, object[] args) => { reason = string.Empty; if (_aoOutputLimitHigh == null) { return true; } if (args.Length == 1 && double.TryParse(args[0].ToString(), out var value)) { var tunedByRecipe = SC.GetValue($"PM.{Module}.Heater.PSU2OutputLimitHighTuningByRecipe"); if(tunedByRecipe) _aoOutputLimitHigh.FloatValue = Convert.ToSingle(value); return true; } EV.PostWarningLog(Module, $"SetOutputLimitHighByRecipe fail, args error"); return true; }); OP.Subscribe($"{Module}.{Name}.SetOutputLimitHigh", (function, args) => { if (args.Length == 1 && args[0] is double value) { SetOutputLimitHigh(value); return true; } EV.PostWarningLog(Module, $"SetOutputLimitHigh fail, args error"); return false; }); OP.Subscribe($"{Module}.{Name}.SetOutputLimitLow", (function, args) => { if (args.Length == 1 && args[0] is double value) { SetOutputLimitLow(value); return true; } EV.PostWarningLog(Module, $"SetOutputLimitLow fail, args error"); return false; }); */ return true; } public bool SetPSUEnable(bool setValue, out string reason) { if (!_doStatus.Check(setValue, out reason)) { EV.PostWarningLog(Module, reason); return false; } if (!_doRelatedEnable.Check(setValue, out reason)) { EV.PostWarningLog(Module, reason); return false; } if (!_doStatus.SetValue(setValue, out reason)) { EV.PostWarningLog(Module, reason); return false; } if (!_doRelatedEnable.SetValue(setValue, out reason)) { EV.PostWarningLog(Module, reason); return false; } return true; } public bool SetHeadHeaterEnable(bool setValue, out string reason) { reason = ""; if (FuncCheckInterLock != null) { if (!FuncCheckInterLock(setValue)) { EV.PostInfoLog(Module, $"Set PSU Enable fialed for Interlock!"); return false; } } if (!_doHeatEnable.Check(setValue, out reason)) { EV.PostWarningLog(Module, reason); return false; } if (!_doHeatEnable.SetValue(setValue, out reason)) { EV.PostWarningLog(Module, reason); return false; } return true; } public void SetOutputLimitHigh(double value) { SC.SetItemValue($"PM.{Module}.Heater.PSU2OutputLimitHigh", value); } public void SetOutputLimitLow(double value) { SC.SetItemValue($"PM.{Module}.Heater.PSU2OutputLimitLow", value); } public bool SetPSUReset(bool setValue, out string reason) { if (!_doReset.Check(setValue, out reason)) { EV.PostWarningLog(Module, reason); return false; } if (!_doReset.SetValue(setValue, out reason)) { EV.PostWarningLog(Module, reason); return false; } _timResetPulse.Start(1000); return true; } public bool CheckPSUEnable() { return _doStatus.Value; } public void Terminate() { } protected override void HandleMonitor() { try { // MonitorPsu2OutputLimit(); MonitorEnableTimer(); MonitorAlarm(); MonitorDoResetDone(); } catch (Exception ex) { LOG.Write(ex); } } public void Reset() { _alarmTrig.RST = true; _commAlarmTrig.RST = true; _trigVoltage.RST = true; _trigCurrent.RST = true; ResetResistanceMonitorResult(); } private void MonitorDoResetDone() { if (_timResetPulse.IsTimeout()) { _timResetPulse.Stop(); if (_doReset.Value) _doReset.Value = false; ResetResistanceMonitorResult(); } } /// /// 复位加热器电阻监测结果。 /// private void ResetResistanceMonitorResult() { IsResistanceTooHigh = false; _trigResOutOfRange.RST = true; _timResOutOfRange.Stop(); } private void MonitorAlarm() { //检查电阻值是否在合理范围 _resLimitMax = (float)SC.GetValue($"PM.{Module}.Heater.{Name}ResistanceMax"); var timeOut = SC.GetValue($"PM.{Module}.Heater.ResistanceMonitorHysteresis"); if (Resistance > _resLimitMax && _timResOutOfRange.IsIdle()) { _timResOutOfRange.Start(timeOut * 1000); } if (Resistance <= _resLimitMax) { IsResistanceTooHigh = false; _timResOutOfRange.Stop(); } _trigResOutOfRange.CLK = _timResOutOfRange.IsTimeout(); if (_trigResOutOfRange.Q) { IsResistanceTooHigh = true; EV.PostWarningLog(Module, $"{Name} Current resistance {Resistance}ohm exceeds the high limit {_resLimitMax}ohm."); } } private void MonitorEnableTimer() { if (Name == "PSU2") { _enableTrig.CLK = !_diHeatEnable.Value; if (_enableTrig.Q) { SC.SetItemValue($"PM.{Module}.OpenLidCountDownTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } _enableTrig2.CLK = _diHeatEnable.Value; if (_enableTrig2.Q) { SC.SetItemValue($"PM.{Module}.OpenLidCountDownTime", ""); } } } #region PSU2 Output Limit (Obsolete) /*private readonly R_TRIG _trigBelow1600 = new(); private readonly R_TRIG _trigAbove1600 = new(); private void MonitorPsu2OutputLimit() { if (Name == "PSU2") { var tunedByRecipe = SC.GetValue($"PM.{Module}.Heater.PSU2OutputLimitHighTuningByRecipe"); tunedByRecipe = false; // 该功能用于测试,在发行版上屏蔽 if (!tunedByRecipe) { var limitHigh = (float)SC.GetValue($"PM.{Module}.Heater.PSU2OutputLimitHigh"); var limitLow = (float)SC.GetValue($"PM.{Module}.Heater.PSU2OutputLimitLow"); _outputLimitHighTuningTemp = (float)SC.GetValue($"PM.{Module}.Heater.PSU2OutputLimitHighTuningTemp"); _trigAbove1600.CLK = AETemp >= _outputLimitHighTuningTemp; if (_trigAbove1600.Q) EV.PostInfoLog(Module, $"Temp above {_outputLimitHighTuningTemp}℃, limit PSU2 Output level to {limitLow}%-{limitHigh}%"); _trigBelow1600.CLK = AETemp < _outputLimitHighTuningTemp; if (_trigAbove1600.Q) EV.PostInfoLog(Module, $"Temp below {_outputLimitHighTuningTemp}℃, limit PSU2 Output level to 100%"); if (AETemp >= _outputLimitHighTuningTemp) { if (Math.Abs(_aoOutputLimitHigh.FloatValue - limitHigh) > 1E-6) _aoOutputLimitHigh.FloatValue = limitHigh; if (Math.Abs(_aoOutputLimitLow.FloatValue - limitLow) > 1E-6) _aoOutputLimitLow.FloatValue = limitLow; } else { _aoOutputLimitHigh.FloatValue = 100; _aoOutputLimitLow.FloatValue = 100; } } } }*/ #endregion private double AETemp { get { object temp = null; if (SC.GetConfigItem("AETemp.EnableDevice").BoolValue) { temp = DATA.Poll($"{Module}.AETemp.Middle"); } if (SC.GetConfigItem("AKunTemp.EnableDevice").BoolValue) { temp = DATA.Poll($"{Module}.AKunTemp.Middle"); } return temp == null ? 0 : (double)temp; } } #endregion } }