SIC-12/Framework/MECF.Framework.RT.EquipmentLibrary/Devices/IoTC.cs

2564 lines
96 KiB
C#
Raw Permalink 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 Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device.PmDevices;
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 MECF.Framework.Common.MECF.Framework.Common.Utilities;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.Temps;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Xml;
using static Aitex.Core.RT.Device.PmDevices.DicMode;
namespace Aitex.Core.RT.Device.Devices
{
public class IoTC : BaseDevice, IDevice
{
private bool _isDBSaveTemp;
private readonly string _scNamePidLimitLower;
private readonly string _scNamePidLimitUpper;
private readonly string _scNamePidRateLimit;
private readonly bool _isFloatAioType = true;
private readonly AIAccessor _aiL1WorkingOPFeedBack = null;
private readonly AIAccessor _aiL2WorkingOPFeedBack = null;
private readonly AIAccessor _aiL3WorkingOPFeedBack = null;
//实际温度值
private readonly AIAccessor _aiL1PVFeedBack = null;
private readonly AIAccessor _aiL2PVFeedBack = null;
private readonly AIAccessor _aiL3PVFeedBack = null;
private readonly DIAccessor _diL1TempHighAlarmFeedBack = null;
private readonly DIAccessor _diL2TempHighAlarmFeedBack = null;
private readonly DIAccessor _diL3TempHighAlarmFeedBack = null;
private readonly DIAccessor _diL1TempLowAlarmFeedBack = null;
private readonly DIAccessor _diL2TempLowAlarmFeedBack = null;
private readonly DIAccessor _diL3TempLowAlarmFeedBack = null;
private readonly AOAccessor _aoL1LoopModeSetPoint = null;
private readonly AOAccessor _aoL2LoopModeSetPoint = null;
private readonly AOAccessor _aoL3LoopModeSetPoint = null;
private readonly AOAccessor _aoL1TargetSPSetPoint = null;
private readonly AOAccessor _aoL2TargetSPSetPoint = null;
private readonly AOAccessor _aoL3TargetSPSetPoint = null;
private readonly AOAccessor _aoL1TargetOPSetPoint = null;
private readonly AOAccessor _aoL2TargetOPSetPoint = null;
private readonly AOAccessor _aoL3TargetOPSetPoint = null;
private readonly AOAccessor _aoL1InputTempSetPoint = null;
private readonly AOAccessor _aoL2InputTempSetPoint = null;
private readonly AOAccessor _aoL3InputTempSetPoint = null;
private readonly AOAccessor _aoL1TempHighLimitSetPoint = null;
private readonly AOAccessor _aoL2TempHighLimitSetPoint = null;
private readonly AOAccessor _aoL3TempHighLimitSetPoint = null;
private readonly AOAccessor _aoL1TempLowLimitSetPoint = null;
private readonly AOAccessor _aoL2TempLowLimitSetPoint = null;
private readonly AOAccessor _aoL3TempLowLimitSetPoint = null;
private readonly AOAccessor _aoHeaterModeSetPoint = null;
private readonly AOAccessor _aoPowerRefSetPoint = null;
private readonly AOAccessor _aoL1RatioSetPoint = null;
private readonly AOAccessor _aoL2RatioSetPoint = null;
private readonly AOAccessor _aoL3RatioSetPoint = null;
private readonly AOAccessor _aoL1RatedSetPoint = null;
private readonly AOAccessor _aoL2RatedSetPoint = null;
private readonly AOAccessor _aoL3RatedSetPoint = null;
private readonly AOAccessor _aoL1VoltageLimited = null;
private readonly AOAccessor _aoL2VoltageLimited = null;
private readonly AOAccessor _aoL3VoltageLimited = null;
private readonly AIAccessor _aiTtempCtrlTCIN = null;
private readonly AOAccessor _aoPSU1Y = null;
private readonly AOAccessor _aoPSU2Y = null;
private readonly AOAccessor _aoPSU3Y = null;
//private readonly AOAccessor _aoPIDLimitLower = null;
//private readonly AOAccessor _aoPIDLimitUpper = null;
//private readonly AOAccessor _aoPIDRateLimit = null;
private readonly AOAccessor _aoPlcTargetOpLowLimit = null;
private readonly AOAccessor _aoPlcTargetOpHighLimit = null;
private readonly DOAccessor _doPLCTempFilterEN = null;
private readonly DOAccessor _doPLCTargetOPCtrlEN = null;
private readonly DIAccessor _diPSUInnerHeaterEnable = null;
private readonly DIAccessor _diPSUMiddleHeaterEnable= null;
private readonly DIAccessor _diPSUOuterHeaterEnable= null;
private readonly DIAccessor _diSCRUpHeaterEnable= null;
private readonly DIAccessor _diSCRMiddleHeaterEnable= null;
private readonly DIAccessor _diSCRDownHeaterEnable= null;
private DeviceTimer _delaySetTime = new DeviceTimer();
private const int MAX_HEATER_LOOP = 3;
private int _overHighTempCount = 0;
private R_TRIG _overHighTempTRIG = new();
private struct ParamRampTempSp
{
public DeviceTimer Timer;
public float FinalTemp;
public float InitTemp;
public int DurationMs;
public AOAccessor AoTargetSetPoint;
public ParamRampTempSp(AOAccessor aoTargetSetPoint)
{
Timer = new DeviceTimer();
AoTargetSetPoint = aoTargetSetPoint;
}
public void Stop()
{
Timer.Stop();
}
}
private readonly IReadOnlyList<IoTCHeaterLoop> _rampParams;
private readonly DeviceTimer _rampTimerSetPowerRef = new DeviceTimer();
private float _rampTargetSetPowerRefRecipe;
private float _rampInitValueSetPowerRefRecipe;
private int _rampTimeSetPowerRefRecipe;
private readonly DeviceTimer _rampTimerL1RatiorRecipe = new DeviceTimer();
private float _rampTargetL1RatiorRecipe;
private float _rampInitValueL1RatiorRecipe;
private int _rampTimeL1RatiorRecipe;
private readonly DeviceTimer _rampTimerL2RatiorRecipe = new DeviceTimer();
private float _rampTargetL2RatiorRecipe;
private float _rampInitValueL2RatiorRecipe;
private int _rampTimeL2RatiorRecipe;
private readonly DeviceTimer _rampTimerL3RatiorRecipe = new DeviceTimer();
private float _rampTargetL3RatiorRecipe;
private float _rampInitValueL3RatiorRecipe;
private int _rampTimeL3RatiorRecipe;
private string _selecetedLoop = string.Empty;
private float _middleTempRatio = 100;
private readonly SCConfigItem _tempRampRatio;
private SCConfigItem _ampereRampRatio;
private SCConfigItem _kwRampRatio;
private readonly SCConfigItem _pyroWarmBaseTemp; //Pyro温度报警需要满足BaseTemp
private readonly SCConfigItem _pyroWarmMaxDiff; //Pyro的MidlleTemp,OuterTemp任意两个温度大于此数值需要报警
private readonly SCConfigItem _pyroWarmEffectFromTime; //温差过大只在进入Process多久后才有效
private readonly SCConfigItem _pyroWarmEffectEndTime; //温差过大只在Process即将结束前多久才有效
//温度滤波功能块
private TempDataFilter _outerTempFilter;
private TempDataFilter _innerTempFilter;
private TempDataFilter _middleTempFilter;
private readonly SCConfigItem _PSUHighLimit;
private readonly SCConfigItem _PSULowLimit;
private readonly SCConfigItem _SCRHighLimit;
private readonly SCConfigItem _SCRLowLimit;
private double _pidLimitLowerUnderPryo;
private double _pidLimitUpperUnderPyro;
private double _pidRateLimitUnderPyro;
private readonly SCConfigItem _PyroWarmIsAlarm;
private bool _isAlarmTempRaisingFast;
private double _tempRaisingRateOuter;
private double _tempRaisingRateMiddle;
private string _scOuterTempValue;
public string ScOuterTempValue
{
get => _scOuterTempValue;
set => _scOuterTempValue = "TempSensor." + value;
}
private string _scInnerTempValue;
public string ScInnerTempValue
{
get => _scInnerTempValue;
set => _scInnerTempValue = "TempSensor." + value;
}
private string _scMiddleTempValue;
public string ScMiddleTempValue
{
get => _scMiddleTempValue;
set => _scMiddleTempValue = "TempSensor." + value;
}
private readonly R_TRIG _trigSetTempLimit = new R_TRIG();
private int _simlatorTempThreshold = 620;
private DateTime _simlatorTempStartTime = DateTime.Now;
private readonly Stopwatch _stopwatch = new Stopwatch();
private PeriodicJob _tReadTemp;
public IoTC(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");
_simlatorTempThreshold = SC.GetValue<int>("PM.SimlatorTempThreshold");
_aiL1WorkingOPFeedBack = ParseAiNode("aiL1WorkingOPFeedBack", node, ioModule);
_aiL2WorkingOPFeedBack = ParseAiNode("aiL2WorkingOPFeedBack", node, ioModule);
_aiL3WorkingOPFeedBack = ParseAiNode("aiL3WorkingOPFeedBack", node, ioModule);
_aiL1PVFeedBack = ParseAiNode("aiL1PVFeedBack", node, ioModule);
_aiL2PVFeedBack = ParseAiNode("aiL2PVFeedBack", node, ioModule);
_aiL3PVFeedBack = ParseAiNode("aiL3PVFeedBack", node, ioModule);
_diL1TempHighAlarmFeedBack = ParseDiNode("diL1TempHighAlarmFeedBack", node, ioModule);
_diL2TempHighAlarmFeedBack = ParseDiNode("diL2TempHighAlarmFeedBack", node, ioModule);
_diL3TempHighAlarmFeedBack = ParseDiNode("diL3TempHighAlarmFeedBack", node, ioModule);
_diL1TempLowAlarmFeedBack = ParseDiNode("diL1TempLowAlarmFeedBack", node, ioModule);
_diL2TempLowAlarmFeedBack = ParseDiNode("diL2TempLowAlarmFeedBack", node, ioModule);
_diL3TempLowAlarmFeedBack = ParseDiNode("diL3TempLowAlarmFeedBack", node, ioModule);
_diPSUInnerHeaterEnable = ParseDiNode("diPSUInnerHeaterEnable", node, ioModule);
_diPSUMiddleHeaterEnable = ParseDiNode("diPSUMiddleHeaterEnable", node, ioModule);
_diPSUOuterHeaterEnable = ParseDiNode("diPSUOuterHeaterEnable", node, ioModule);
_diSCRUpHeaterEnable = ParseDiNode("diSCRUpHeaterEnable", node, ioModule);
_diSCRMiddleHeaterEnable = ParseDiNode("diSCRMiddleHeaterEnable", node, ioModule);
_diSCRDownHeaterEnable = ParseDiNode("diSCRDownHeaterEnable", node, ioModule);
_aoL1LoopModeSetPoint = ParseAoNode("aoL1LoopModeSetPoint", node, ioModule);
_aoL2LoopModeSetPoint = ParseAoNode("aoL2LoopModeSetPoint", node, ioModule);
_aoL3LoopModeSetPoint = ParseAoNode("aoL3LoopModeSetPoint", node, ioModule);
_aoL1TargetSPSetPoint = ParseAoNode("aoL1TargetSPSetPoint", node, ioModule);
_aoL2TargetSPSetPoint = ParseAoNode("aoL2TargetSPSetPoint", node, ioModule);
_aoL3TargetSPSetPoint = ParseAoNode("aoL3TargetSPSetPoint", node, ioModule);
_aoL1TargetOPSetPoint = ParseAoNode("aoL1TargetOPSetPoint", node, ioModule);
_aoL2TargetOPSetPoint = ParseAoNode("aoL2TargetOPSetPoint", node, ioModule);
_aoL3TargetOPSetPoint = ParseAoNode("aoL3TargetOPSetPoint", node, ioModule);
_aoL1InputTempSetPoint = ParseAoNode("aoL1InputTempSetPoint", node, ioModule);
_aoL2InputTempSetPoint = ParseAoNode("aoL2InputTempSetPoint", node, ioModule);
_aoL3InputTempSetPoint = ParseAoNode("aoL3InputTempSetPoint", node, ioModule);
_aoL1TempHighLimitSetPoint = ParseAoNode("aoL1TempHighLimitSetPoint", node, ioModule);
_aoL2TempHighLimitSetPoint = ParseAoNode("aoL2TempHighLimitSetPoint", node, ioModule);
_aoL3TempHighLimitSetPoint = ParseAoNode("aoL3TempHighLimitSetPoint", node, ioModule);
_aoL1TempLowLimitSetPoint = ParseAoNode("aoL1TempLowLimitSetPoint", node, ioModule);
_aoL2TempLowLimitSetPoint = ParseAoNode("aoL2TempLowLimitSetPoint", node, ioModule);
_aoL3TempLowLimitSetPoint = ParseAoNode("aoL3TempLowLimitSetPoint", node, ioModule);
_aoHeaterModeSetPoint = ParseAoNode("aoHeaterModeSetPoint", node, ioModule);
_aoPowerRefSetPoint = ParseAoNode("aoPowerRefSetPoint", node, ioModule);
_aoL1RatioSetPoint = ParseAoNode("aoL1RatioSetPoint", node, ioModule);
_aoL2RatioSetPoint = ParseAoNode("aoL2RatioSetPoint", node, ioModule);
_aoL3RatioSetPoint = ParseAoNode("aoL3RatioSetPoint", node, ioModule);
_aoL1RatedSetPoint = ParseAoNode("aoL1RatedSetPoint", node, ioModule);
_aoL2RatedSetPoint = ParseAoNode("aoL2RatedSetPoint", node, ioModule);
_aoL3RatedSetPoint = ParseAoNode("aoL3RatedSetPoint", node, ioModule);
_aoL1VoltageLimited = ParseAoNode("aoL1VoltageLimited", node, ioModule);
_aoL2VoltageLimited = ParseAoNode("aoL2VoltageLimited", node, ioModule);
_aoL3VoltageLimited = ParseAoNode("aoL3VoltageLimited", node, ioModule);
_aoPSU1Y = ParseAoNode("aoPSU1Y", node, ioModule);
_aoPSU2Y = ParseAoNode("aoPSU2Y", node, ioModule);
_aoPSU3Y = ParseAoNode("aoPSU3Y", node, ioModule);
_isDBSaveTemp = node.GetAttribute("isDBSaveTemp") == "true" ? true : false;
_aiTtempCtrlTCIN = ParseAiNode("aiTtempCtrlTCIN", node, ioModule);
_isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float");
_pyroWarmEffectFromTime = ParseScNode("PyroWarmEffectFromTime", node, ioModule, $"PM.{Module}.Heater.PyroWarmEffectFromTime");
_pyroWarmEffectEndTime = ParseScNode("PyroWarmEffectFromTime", node, ioModule, $"PM.{Module}.Heater.PyroWarmEffectEndTime");
_tempRampRatio = ParseScNode("TempRampRatio", node, ioModule, $"PM.{Module}.Heater.TempRampRate");
_ampereRampRatio = ParseScNode("AmpereRampRate", node, ioModule, $"PM.{Module}.Heater.AmpereRampRate");
_kwRampRatio = ParseScNode("KWRampRate", node, ioModule, $"PM.{Module}.Heater.KWRampRate");
_pyroWarmBaseTemp = ParseScNode("PyroWarmBaseTemp", node, ioModule, $"PM.{Module}.Heater.PyroWarmBaseTemp");
_pyroWarmMaxDiff = ParseScNode("PyroWarmBaseTemp", node, ioModule, $"PM.{Module}.Heater.PyroWarmMaxDiff");
var scName = $"PM.{Module}.Heater.PSUTempHighLimit";
_PSUHighLimit = ParseScNode("PSUTempHighLimit", node, ioModule, scName);
SC.RegisterValueChangedCallback(scName, val =>
{
if (val is double dblVal)
_PSUHighLimit.DoubleValue = dblVal;
});
scName = $"PM.{Module}.Heater.PSUTempLowLimit";
_PSULowLimit = ParseScNode("PSUTempLowLimit", node, ioModule, $"PM.{Module}.Heater.PSUTempLowLimit");
SC.RegisterValueChangedCallback(scName, val =>
{
if (val is double dblVal)
_PSULowLimit.DoubleValue = dblVal;
});
scName = $"PM.{Module}.Heater.SCRTempHighLimit";
_SCRHighLimit = ParseScNode("SCRTempHighLimit", node, ioModule, $"PM.{Module}.Heater.SCRTempHighLimit");
SC.RegisterValueChangedCallback(scName, val =>
{
if (val is double dblVal)
_SCRHighLimit.DoubleValue = dblVal;
});
scName = $"PM.{Module}.Heater.SCRTempLowLimit";
_SCRLowLimit = ParseScNode("SCRTempLowLimit", node, ioModule, $"PM.{Module}.Heater.SCRTempLowLimit");
SC.RegisterValueChangedCallback(scName, val =>
{
if (val is double dblVal)
_SCRLowLimit.DoubleValue = dblVal;
});
scName = $"PM.{Module}.Heater.PyroWarmIsAlarm";
_PyroWarmIsAlarm = ParseScNode("PyroWarmIsAlarm", node, ioModule, $"PM.{Module}.Heater.PyroWarmIsAlarm");
SC.RegisterValueChangedCallback(scName, val =>
{
if (val is double dblVal)
_PyroWarmIsAlarm.DoubleValue = dblVal;
});
var lstRampParam = new List<IoTCHeaterLoop>();
var lstAoTargetSp = new AOAccessor[MAX_HEATER_LOOP] { _aoL1TargetSPSetPoint, _aoL2TargetSPSetPoint, _aoL3TargetSPSetPoint };
var lstAoTempFromSensorToTC = new AOAccessor[MAX_HEATER_LOOP] { _aoL1InputTempSetPoint, _aoL2InputTempSetPoint, _aoL3InputTempSetPoint };
for (var i = 0; i < MAX_HEATER_LOOP; i++)
lstRampParam.Add(new IoTCHeaterLoop(i + 1, lstAoTargetSp[i], lstAoTempFromSensorToTC[i]));
_rampParams = lstRampParam;
#region Dynamic PID Limit
/* _scNamePidLimitLower = $"PM.{Module}.Heater.PIDLimitLower";
_scNamePidLimitUpper = $"PM.{Module}.Heater.PIDLimitUpper";
_scNamePidRateLimit = $"PM.{Module}.Heater.PIDRateLimit";
_pidLimitLowerUnderPryo = SC.SafeGetValue(_scNamePidLimitLower, 0.0);
_pidLimitUpperUnderPyro = SC.SafeGetValue(_scNamePidLimitUpper, 100.0);
_pidRateLimitUnderPyro = SC.SafeGetValue(_scNamePidRateLimit, 100.0);
SC.RegisterValueChangedCallback(_scNamePidLimitLower, v =>
{
if (double.TryParse(v.ToString(), out var dbl))
_pidLimitLowerUnderPryo = dbl.Round(1);
else
{
_pidLimitLowerUnderPryo = 50;
LOG.Error($"{_scNamePidLimitLower} is not double, set to 100.0");
}
});
SC.RegisterValueChangedCallback(_scNamePidLimitUpper, v =>
{
if (double.TryParse(v.ToString(), out var dbl))
_pidLimitUpperUnderPyro = dbl.Round(1);
else
{
_pidLimitUpperUnderPyro = 100.0;
LOG.Error($"{_scNamePidLimitUpper} is not double, set to 100.0");
}
});
SC.RegisterValueChangedCallback(_scNamePidRateLimit, v =>
{
if (double.TryParse(v.ToString(), out var dbl))
_pidRateLimitUnderPyro = dbl.Round(1);
else
{
_pidRateLimitUnderPyro = 100.0;
LOG.Error($"{_scNamePidRateLimit} is not double, set to 100.0");
}
});
_aoPIDLimitLower = ParseAoNode("aoPIDLimitLower", node, ioModule);
_aoPIDLimitUpper = ParseAoNode("aoPIDLimitUpper", node, ioModule);
_aoPIDRateLimit = ParseAoNode("aoPIDRateLimit", node, ioModule);
Debug.Assert(_aoPIDLimitLower != null);
Debug.Assert(_aoPIDLimitUpper != null);
Debug.Assert(_aoPIDRateLimit != null);*/
#endregion
#region PLC TargetOP Limits
_aoPlcTargetOpLowLimit = ParseAoNode("aoPLCTargetOPLowLimit", node, ioModule);
_aoPlcTargetOpHighLimit = ParseAoNode("aoPLCTargetOPUpLimit", node, ioModule);
_doPLCTempFilterEN = ParseDoNode("doPLCTempFilterEN", node, ioModule);
_doPLCTargetOPCtrlEN = ParseDoNode("doPLCTargetOPCtrlEN", node, ioModule);
#endregion
}
public bool Initialize()
{
DATA.Subscribe($"{Module}.{Name}.L1WorkingOPFeedBack", () => L1WorkingOPFeedBack);
DATA.Subscribe($"{Module}.{Name}.L2WorkingOPFeedBack", () => L2WorkingOPFeedBack);
DATA.Subscribe($"{Module}.{Name}.L3WorkingOPFeedBack", () => L3WorkingOPFeedBack);
DATA.Subscribe($"{Module}.{Name}.L1PVFeedBack", () => L1PVFeedBack);
DATA.Subscribe($"{Module}.{Name}.L2PVFeedBack", () => L2PVFeedBack);
DATA.Subscribe($"{Module}.{Name}.L3PVFeedBack", () => L3PVFeedBack);
DATA.Subscribe($"{Module}.{Name}.L1TempHighAlarmFeedBack", () => L1TempHighAlarmFeedBack);
DATA.Subscribe($"{Module}.{Name}.L2TempHighAlarmFeedBack", () => L2TempHighAlarmFeedBack);
DATA.Subscribe($"{Module}.{Name}.L3TempHighAlarmFeedBack", () => L3TempHighAlarmFeedBack);
DATA.Subscribe($"{Module}.{Name}.L1TempLowAlarmFeedBack", () => L1TempLowAlarmFeedBack);
DATA.Subscribe($"{Module}.{Name}.L2TempLowAlarmFeedBack", () => L2TempLowAlarmFeedBack);
DATA.Subscribe($"{Module}.{Name}.L3TempLowAlarmFeedBack", () => L3TempLowAlarmFeedBack);
DATA.Subscribe($"{Module}.{Name}.L1LoopModeSetPoint", () => (int)L1LoopModeSetPoint);
DATA.Subscribe($"{Module}.{Name}.L2LoopModeSetPoint", () => (int)L2LoopModeSetPoint);
DATA.Subscribe($"{Module}.{Name}.L3LoopModeSetPoint", () => (int)L3LoopModeSetPoint);
DATA.Subscribe($"{Module}.{Name}.L1TargetSPSetPoint", () => L1TargetSPSetPoint);
DATA.Subscribe($"{Module}.{Name}.L2TargetSPSetPoint", () => L2TargetSPSetPoint);
DATA.Subscribe($"{Module}.{Name}.L3TargetSPSetPoint", () => L3TargetSPSetPoint);
DATA.Subscribe($"{Module}.{Name}.L1TargetOPSetPoint", () => L1TargetOPSetPoint);
DATA.Subscribe($"{Module}.{Name}.L2TargetOPSetPoint", () => L2TargetOPSetPoint);
DATA.Subscribe($"{Module}.{Name}.L3TargetOPSetPoint", () => L3TargetOPSetPoint);
DATA.Subscribe($"{Module}.{Name}.L1InputTempSetPoint", () => L1InputTempSetPoint);
DATA.Subscribe($"{Module}.{Name}.L2InputTempSetPoint", () => L2InputTempSetPoint);
DATA.Subscribe($"{Module}.{Name}.L3InputTempSetPoint", () => L3InputTempSetPoint);
DATA.Subscribe($"{Module}.{Name}.L1TempHighLimitSetPoint", () => L1TempHighLimitSetPoint);
DATA.Subscribe($"{Module}.{Name}.L2TempHighLimitSetPoint", () => L2TempHighLimitSetPoint);
DATA.Subscribe($"{Module}.{Name}.L3TempHighLimitSetPoint", () => L3TempHighLimitSetPoint);
DATA.Subscribe($"{Module}.{Name}.L1TempLowLimitSetPoint", () => L1TempLowLimitSetPoint);
DATA.Subscribe($"{Module}.{Name}.L2TempLowLimitSetPoint", () => L2TempLowLimitSetPoint);
DATA.Subscribe($"{Module}.{Name}.L3TempLowLimitSetPoint", () => L3TempLowLimitSetPoint);
DATA.Subscribe($"{Module}.{Name}.HeaterModeSetPoint", () => (int)HeaterModeSetPoint);
DATA.Subscribe($"{Module}.{Name}.PowerRefSetPoint", () => PowerRefSetPoint);
DATA.Subscribe($"{Module}.{Name}.L1RatioSetPoint", () => L1RatioSetPoint);
DATA.Subscribe($"{Module}.{Name}.L2RatioSetPoint", () => L2RatioSetPoint);
DATA.Subscribe($"{Module}.{Name}.L3RatioSetPoint", () => L3RatioSetPoint);
DATA.Subscribe($"{Module}.{Name}.L1RatedSetPoint", () => L1RatedSetPoint);
DATA.Subscribe($"{Module}.{Name}.L2RatedSetPoint", () => L2RatedSetPoint);
DATA.Subscribe($"{Module}.{Name}.L3RatedSetPoint", () => L3RatedSetPoint);
DATA.Subscribe($"{Module}.{Name}.L1VoltageLimited", () => L1VoltageLimited);
DATA.Subscribe($"{Module}.{Name}.L2VoltageLimited", () => L2VoltageLimited);
DATA.Subscribe($"{Module}.{Name}.L3VoltageLimited", () => L3VoltageLimited);
//DATA.Subscribe($"{Module}.{Name}.TempCtrlTCIN", () => TtempCtrlTCIN);
DATA.Subscribe($"{Module}.{Name}.PyroTempMaxDiff", () => PyroTempMaxDiff);
//
DATA.Subscribe($"{Module}.{Name}.PSU1Y", () => PSU1Y);
DATA.Subscribe($"{Module}.{Name}.PSU2Y", () => PSU2Y);
DATA.Subscribe($"{Module}.{Name}.PSU3Y", () => PSU3Y);
DATA.Subscribe($"{Module}.{Name}.PCPSU1Y", () => PCPSU1Y);
DATA.Subscribe($"{Module}.{Name}.PCPSU2Y", () => PCPSU2Y);
DATA.Subscribe($"{Module}.{Name}.PCPSU3Y", () => PCPSU3Y);
DATA.Subscribe($"{Module}.{Name}.PSU1Power", () => PSU1Power);
DATA.Subscribe($"{Module}.{Name}.PSU2Power", () => PSU2Power);
DATA.Subscribe($"{Module}.{Name}.PSU3Power", () => PSU3Power);
DATA.Subscribe($"{Module}.{Name}.HeatStrategy", () => (int)HeaterModeSetPoint);
//DATA.Subscribe($"{Module}.{Name}.PIDLimitLower", () => _aoPIDLimitLower?.Value ?? double.NaN);
//DATA.Subscribe($"{Module}.{Name}.PIDLimitUpper", () => _aoPIDLimitUpper?.Value ?? double.NaN);
//DATA.Subscribe($"{Module}.{Name}.PIDRateLimit", () => _aoPIDRateLimit?.Value ?? double.NaN);
DATA.Subscribe($"{Module}.{Name}.PlcTargetOpLowLimit", () => _aoPlcTargetOpLowLimit?.Value ?? double.NaN);
DATA.Subscribe($"{Module}.{Name}.PlcTargetOpHighLimit", () => _aoPlcTargetOpHighLimit?.Value ?? double.NaN);
DATA.Subscribe($"{Module}.{Name}.PlcTempFilterEN", () => _doPLCTempFilterEN?.Value ?? false);
DATA.Subscribe($"{Module}.{Name}.PlcTargetOPCtrlEN", () => _doPLCTargetOPCtrlEN?.Value ?? false);
if (Name == "TC1")
{
DATA.Subscribe($"{Module}.Temp.Outer", () => OuterTemp);
DATA.Subscribe($"{Module}.Temp.Inner", () => InnerTemp);
DATA.Subscribe($"{Module}.Temp.Middle", () => MiddleTemp);
DATA.Subscribe($"{Module}.Temp.Chamber", () => L2InputTempSetPoint);
DATA.Subscribe($"{Module}.Temp.MiddleTempRatio", () => _middleTempRatio);
var pollIntervalMs = 100;
_tReadTemp = new PeriodicJob(pollIntervalMs, OnTime, $"{Module}.{Name}", true);
_outerTempFilter = new TempDataFilter(Module, "OuterTemp", pollIntervalMs, _simlatorTempThreshold, $"PM.{Module}.TempSensor");
_innerTempFilter = new TempDataFilter(Module, "InnerTemp", pollIntervalMs, _simlatorTempThreshold, $"PM.{Module}.TempSensor");
_middleTempFilter = new TempDataFilter(Module, "MiddleTemp", pollIntervalMs, _simlatorTempThreshold, $"PM.{Module}.TempSensor");
_outerTempFilter.Initialize(out var reason);
_middleTempFilter.Initialize(out reason);
_innerTempFilter.Initialize(out reason);
}
GetPCPSUPower();
OP.Subscribe($"{Module}.{Name}.SetL1TargetSP", (out string reason, int time, object[] args) =>
{
_selecetedLoop = "L1";
var targetSP = Convert.ToSingle(args[0]);
reason = $"{Display} {_selecetedLoop} Target SP set to {targetSP}";
EV.PostInfoLog(Module, reason);
SetTargetSP(_selecetedLoop, targetSP, time);
return true;
});
OP.Subscribe($"{Module}.{Name}.SetL2TargetSP", (out string reason, int time, object[] args) =>
{
_selecetedLoop = "L2";
var targetSp = Convert.ToSingle(args[0]);
reason = $"{Display} {_selecetedLoop} Target SP set to {targetSp}";
EV.PostInfoLog(Module, reason);
SetTargetSP(_selecetedLoop, targetSp, time);
return true;
});
OP.Subscribe($"{Module}.{Name}.SetL3TargetSP", (out string reason, int time, object[] args) =>
{
_selecetedLoop = "L3";
var targetSP = Convert.ToSingle(args[0]);
reason = $"{Display} {_selecetedLoop} Target SP set to {targetSP}";
EV.PostInfoLog(Module, reason);
SetTargetSP(_selecetedLoop, targetSP, time);
return true;
});
OP.Subscribe($"{Module}.{Name}.MiddleTempRatio", (out string reason, int time, object[] args) =>
{
var middleTempRatio = Convert.ToSingle(args[0]);
reason = $"{Display} MiddleTempRatio set to {middleTempRatio}";
EV.PostInfoLog(Module, reason);
_middleTempRatio = middleTempRatio;
return true;
});
OP.Subscribe($"{Module}.{Name}.SetHeaterMode", (out string reason, int time, object[] args) =>
{
reason = "";
var iMode = Convert.ToInt32(args[0]);
SetHeatMode(ConvertToHeatStrategy(iMode), time, TCUnits.PSU);
return true;
});
OP.Subscribe($"{Module}.{Name}.SetHeaterMode2", (out string reason, int time, object[] args) =>
{
reason = "";
var iMode = Convert.ToInt32(args[0]);
SetHeatMode(ConvertToHeatStrategy(iMode), time, TCUnits.SCR);
return true;
});
OP.Subscribe($"{Module}.{Name}.SetRatio", (function, args) =>
{
SetRatio(Convert.ToSingle(args[0]), Convert.ToSingle(args[1]), Convert.ToSingle(args[2]));
return true;
});
OP.Subscribe($"{Module}.{Name}.SetRatedValue", (function, args) =>
{
SetRatedValue(Convert.ToSingle(args[0]), Convert.ToSingle(args[1]), Convert.ToSingle(args[2]));
return true;
});
OP.Subscribe($"{Module}.{Name}.RecipeSetPowerRef", (out string reason, int time, object[] args) =>
{
var PowerRef = Convert.ToSingle(args[0]);
var _PowerRef = PowerRef > 0 ? (PowerRef > 100 ? 100 : PowerRef) : 0;
if (time > 0)
{
reason = $"{Display}Recipe power Ref ramp to {_PowerRef}";
}
else
{
reason = $"{Display}Recipe power Ref set to {_PowerRef}";
}
EV.PostInfoLog(Module, reason);
RecipeSetPowerRef(_PowerRef, time);
return true;
});
OP.Subscribe($"{Module}.{Name}.RecipeSetL1Ratio", (out string reason, int time, object[] args) =>
{
_selecetedLoop = "L1";
var ratio = Convert.ToSingle(args[0]);
reason = $"{Display} Recipe {_selecetedLoop} Ratio set to {ratio}";
EV.PostInfoLog(Module, reason);
RecipeSetRatio(_selecetedLoop, ratio, time);
return true;
});
OP.Subscribe($"{Module}.{Name}.RecipeSetL2Ratio", (out string reason, int time, object[] args) =>
{
_selecetedLoop = "L2";
var ratio = Convert.ToSingle(args[0]);
reason = $"{Display} Recipe {_selecetedLoop} Ratio set to {ratio}";
EV.PostInfoLog(Module, reason);
RecipeSetRatio(_selecetedLoop, ratio, time);
return true;
});
OP.Subscribe($"{Module}.{Name}.RecipeSetL3Ratio", (out string reason, int time, object[] args) =>
{
_selecetedLoop = "L3";
var ratio = Convert.ToSingle(args[0]);
reason = $"{Display} Recipe {_selecetedLoop} Ratio set to {ratio}";
EV.PostInfoLog(Module, reason);
RecipeSetRatio(_selecetedLoop, ratio, time);
return true;
});
//
OP.Subscribe($"{Module}.{Name}.SetPSUY", (function, args) =>
{
SetPSUY(Convert.ToDouble(args[0]), Convert.ToDouble(args[1]), Convert.ToDouble(args[2]));
return true;
});
OP.Subscribe($"{Module}.{Name}.SetPSUPower", (function, args) =>
{
SetPSUPower(Convert.ToDouble(args[0]), Convert.ToDouble(args[1]), Convert.ToDouble(args[2]));
return true;
});
OP.Subscribe($"{Module}.{Name}.SetPIDLimit", (function, args) => SetPIDLimit(args));
// 读取系统配置
SCInit();
DisturbanceSimInitialize();
return true;
}
private bool OnTime()
{
MonitorTempFilter();
MonitorTemp();
CheckHighTemp();
return true;
}
/// <summary>
/// 读取系统配置,并注册回调。
/// </summary>
private void SCInit()
{
ScOuterTempValue = SC.GetStringValue($"PM.{Module}.TempSensor.Outer");
ScInnerTempValue = SC.GetStringValue($"PM.{Module}.TempSensor.Inner");
ScMiddleTempValue = SC.GetStringValue($"PM.{Module}.TempSensor.Middle");
SC.RegisterValueChangedCallback($"PM.{Module}.TempSensor.Outer",
(obj) =>
{
ScOuterTempValue = obj.ToString();
});
SC.RegisterValueChangedCallback($"PM.{Module}.TempSensor.Inner",
(obj) =>
{
ScInnerTempValue = obj.ToString();
});
SC.RegisterValueChangedCallback($"PM.{Module}.TempSensor.Middle",
(obj) =>
{
ScMiddleTempValue = obj.ToString();
});
#region Temp Rising Too Fast Alarm
_isAlarmTempRaisingFast = SC.SafeGetValue($"PM.{Module}.Heater.AETempRasingFastIsAlarm", true);
_tempRaisingRateMiddle = SC.SafeGetValue($"PM.{Module}.Heater.AETempMiddleRasingRate", 20.0);
_tempRaisingRateOuter = SC.SafeGetValue($"PM.{Module}.Heater.AETempOuterRasingRate", 20.0);
SC.RegisterValueChangedCallback($"PM.{Module}.Heater.AETempRasingFastIsAlarm", val =>
{
_isAlarmTempRaisingFast = (bool)val;
});
SC.RegisterValueChangedCallback($"PM.{Module}.Heater.AETempMiddleRasingRate", val =>
{
_tempRaisingRateMiddle = (double)val;
});
SC.RegisterValueChangedCallback($"PM.{Module}.Heater.AETempOuterRasingRate", val =>
{
_tempRaisingRateOuter = (double)val;
});
#endregion
}
#region RatioSetPoint
public bool RecipeSetRatio(string selecetedLoop, float ratio, int time)
{
switch (selecetedLoop)
{
case "L1":
_rampInitValueL1RatiorRecipe = L1RatioSetPoint;
_rampTimerL1RatiorRecipe.Stop();
if (time > 0)
{
_rampTimeL1RatiorRecipe = time;
_rampTargetL1RatiorRecipe = ratio;
_rampTimerL1RatiorRecipe.Start(time);
}
else
{
L1RatioSetPoint = ratio;
}
break;
case "L2":
_rampInitValueL2RatiorRecipe = L2RatioSetPoint;
_rampTimerL2RatiorRecipe.Stop();
if (time > 0)
{
_rampTimeL2RatiorRecipe = time;
_rampTargetL2RatiorRecipe = ratio;
_rampTimerL2RatiorRecipe.Start(time);
}
else
{
L2RatioSetPoint = ratio;
}
break;
case "L3":
_rampInitValueL3RatiorRecipe = L3RatioSetPoint;
_rampTimerL3RatiorRecipe.Stop();
if (time > 0)
{
_rampTimeL3RatiorRecipe = time;
_rampTargetL3RatiorRecipe = ratio;
_rampTimerL3RatiorRecipe.Start(time);
}
else
{
L3RatioSetPoint = ratio;
}
break;
}
if (HeaterModeSetPoint is HeatStrategy.PyroFollow or HeatStrategy.PyroAuto) //设置输出功率
{
var f1 = L1RatioSetPoint > 0 ? L1RatioSetPoint : 0;
var f2 = L2RatioSetPoint > 0 ? L2RatioSetPoint : 0;
var f3 = L3RatioSetPoint > 0 ? L3RatioSetPoint : 0;
L1TargetOPSetPoint = f1 > 100 ? 100 : f1;
L2TargetOPSetPoint = f2 > 100 ? 100 : f2;
L3TargetOPSetPoint = f3 > 100 ? 100 : f3;
}
return true;
}
private void MonitorL1RecipeRatioRamping()
{
if (!_rampTimerL1RatiorRecipe.IsIdle())
{
if (_rampTimerL1RatiorRecipe.IsTimeout() || _rampTimeL1RatiorRecipe == 0)
{
_rampTimerL1RatiorRecipe.Stop();
L1RatioSetPoint = _rampTargetL1RatiorRecipe;
}
else
{
L1RatioSetPoint = _rampInitValueL1RatiorRecipe + (_rampTargetL1RatiorRecipe - _rampInitValueL1RatiorRecipe) * (float)_rampTimerL1RatiorRecipe.GetElapseTime() / _rampTimeL1RatiorRecipe;
}
}
}
private void MonitorL2RecipeRatioRamping()
{
if (!_rampTimerL2RatiorRecipe.IsIdle())
{
if (_rampTimerL2RatiorRecipe.IsTimeout() || _rampTimeL2RatiorRecipe == 0)
{
_rampTimerL2RatiorRecipe.Stop();
L2RatioSetPoint = _rampTargetL2RatiorRecipe;
}
else
{
L2RatioSetPoint = _rampInitValueL2RatiorRecipe + (_rampTargetL2RatiorRecipe - _rampInitValueL2RatiorRecipe) * (float)_rampTimerL2RatiorRecipe.GetElapseTime() / _rampTimeL2RatiorRecipe;
}
}
}
private void MonitorL3RecipeRatioRamping()
{
if (!_rampTimerL3RatiorRecipe.IsIdle())
{
if (_rampTimerL3RatiorRecipe.IsTimeout() || _rampTimeL3RatiorRecipe == 0)
{
_rampTimerL3RatiorRecipe.Stop();
L3RatioSetPoint = _rampTargetL3RatiorRecipe;
}
else
{
L3RatioSetPoint = _rampInitValueL3RatiorRecipe + (_rampTargetL3RatiorRecipe - _rampInitValueL3RatiorRecipe) * (float)_rampTimerL3RatiorRecipe.GetElapseTime() / _rampTimeL3RatiorRecipe;
}
}
}
#endregion RatioSetPoint
/// <summary>
/// 设置PSU加热模式。
/// </summary>
/// <param name="mode"></param>
/// <param name="time"></param>
/// <param name="tcType">加热器类型</param>
/// <returns></returns>
public bool SetHeatMode(HeatStrategy mode, int time, TCUnits tcType)
{
var preMode = HeaterModeSetPoint; //前一个模式
HeaterModeSetPoint = mode;
switch (mode)
{
case HeatStrategy.Power:
L1LoopModeSetPoint = TCModes.Manual;
L2LoopModeSetPoint = TCModes.Manual;
L3LoopModeSetPoint = TCModes.Manual;
if (preMode is HeatStrategy.PyroFollow or HeatStrategy.PyroAuto)//Pyro切到Power之后设置为600
{
_rampParams.ToList().ForEach(x => x.StopRamp());
}
break;
case HeatStrategy.PyroFollow:
case HeatStrategy.PyroAuto:
if (L1LoopModeSetPoint == TCModes.Manual)
{
_rampParams[0].StopRamp();
SetAoValue(_rampParams[0].AOTargetSP, L1InputTempSetPoint);
}
if (L2LoopModeSetPoint == TCModes.Manual)
{
_rampParams[1].StopRamp();
SetAoValue(_rampParams[1].AOTargetSP, L2InputTempSetPoint);
}
if (L3LoopModeSetPoint == TCModes.Manual)
{
_rampParams[2].StopRamp();
SetAoValue(_rampParams[2].AOTargetSP, L3InputTempSetPoint);
}
if (mode == HeatStrategy.PyroAuto)
{
L1LoopModeSetPoint = TCModes.Manual;
L2LoopModeSetPoint = TCModes.Auto;
L3LoopModeSetPoint = TCModes.Auto;
}
else if (mode == HeatStrategy.PyroFollow)
{
if (IsPSU(tcType))
{
L1LoopModeSetPoint = TCModes.Manual;
L2LoopModeSetPoint = TCModes.Auto;
L3LoopModeSetPoint = TCModes.Manual;
}
else if (IsSCR(tcType))
{
L1LoopModeSetPoint = TCModes.Manual;
L2LoopModeSetPoint = TCModes.Manual;
L3LoopModeSetPoint = TCModes.Auto;
}
else
EV.PostAlarmLog(Module, $"{Display} Unknown TC when changing heat mode.");
}
break;
}
var description = $"{Display} Heat Mode set to {mode}";
EV.PostInfoLog(Module, description);
return true;
}
#region Pyro自动模式
/// <summary>
/// 都从AE温度开始Ramp
/// </summary>
/// <param name="loop"></param>
/// <param name="targetTemp">目标温度</param>
/// <param name="time">SP爬升时间。如果为1表示Recipe中定义为Jump模式。</param>
/// <returns></returns>
public bool SetTargetSP(string loop, float targetTemp, int time)
{
var heaterLoop = _rampParams.FirstOrDefault(x => x.Name == loop);
Debug.Assert(heaterLoop is not null);
//如果是Power模式直接设置SP即可
if (HeaterModeSetPoint == HeatStrategy.Power)
{
SetAoValue(heaterLoop.AOTargetSP, targetTemp);
return true;
}
else
{
var rampDuration = time;
heaterLoop.StopRamp(); // 停止正在Ramp的过程
heaterLoop.RampInitTemp = GetAoValue(heaterLoop.AOTempFromSensorTC); // 保存当前温度SP
if (time == 0) // 如果没有指定Ramp耗时则根据系统配置的温度爬升速率计算当前温差所需的爬升时间
rampDuration = Math.Abs((int)((targetTemp - heaterLoop.RampInitTemp) / _tempRampRatio.DoubleValue) *
1000);
heaterLoop.RampTargetTemp = targetTemp;
heaterLoop.StartRamp(rampDuration); // 启动Ramp计时器
}
/* switch (loop)
{
case "L1":
//如果是Power模式直接设置SP即可
if (HeaterModeSetPoint == HeatStrategy.Power)
{
L1TargetSPSetPoint = targetTemp;
return true;
}
else
{
_rampTimerL1.Stop(); // 停止正在Ramp的过程
_rampInitValueL1 = L1InputTempSetPoint; // 保存当前温度SP
_rampTimeL1 = time; // 接下来Ramp的总耗时
if (time == 0) // 如果没有指定Ramp耗时则根据系统配置的温度爬升速率计算当前温差所需的爬升时间
_rampTimeL1 = Math.Abs((int)((targetTemp - _rampInitValueL1) / _tempRampRatio.DoubleValue) * 1000);
_rampTargetL1 = targetTemp; // 本次温度Ramp的目标值
_rampTimerL1.Start(_rampTimeL1); // 启动Ramp计时器
}
break;
case "L2":
//如果是Power模式直接设置SP即可
if (HeaterModeSetPoint == HeatStrategy.Power)
{
L2TargetSPSetPoint = targetTemp;
return true;
}
else
{
_rampTimerL2.Stop();
_rampInitValueL2 = L2InputTempSetPoint;
_rampTimeL2 = time;
if (time == 0)
_rampTimeL2 = Math.Abs((int)((targetTemp - _rampInitValueL2) / _tempRampRatio.DoubleValue) * 1000);
_rampTargetL2 = targetTemp;
_rampTimerL2.Start(_rampTimeL2);
}
break;
case "L3":
//如果是Power模式直接设置SP即可
if (HeaterModeSetPoint == HeatStrategy.Power)
{
L3TargetSPSetPoint = targetTemp;
return true;
}
else
{
_rampTimerL3.Stop();
_rampInitValueL3 = L3InputTempSetPoint;
_rampTimeL3 = time;
if (time == 0)
_rampTimeL3 = Math.Abs((int)((targetTemp - _rampInitValueL3) / _tempRampRatio.DoubleValue) * 1000);
_rampTargetL3 = targetTemp;
_rampTimerL3.Start(_rampTimeL3);
}
break;
}*/
return true;
}
private void MonitorTargetSPRamping(string loopName)
{
var loop = _rampParams.FirstOrDefault(x => x.Name == loopName);
Debug.Assert(loop is not null);
if (!loop.RampTimer.IsIdle())
{
if (loop.RampTimer.IsTimeout() || loop.RampDurationMs == 0)
{
loop.StopRamp();
SetAoValue(loop.AOTargetSP, loop.RampTargetTemp);
}
else
{
SetAoValue(loop.AOTargetSP, loop.RampInitTemp + (loop.RampTargetTemp - loop.RampInitTemp) * (float)loop.RampTimer.GetElapseTime() / loop.RampDurationMs);
}
}
}
/* private void MonitorL1Ramping()
{
if (!_rampTimerL1.IsIdle())
{
if (_rampTimerL1.IsTimeout() || _rampTimeL1 == 0)
{
_rampTimerL1.Stop();
L1TargetSPSetPoint = _rampTargetL1;
}
else
{
L1TargetSPSetPoint = _rampInitValueL1 + (_rampTargetL1 - _rampInitValueL1) * (float)_rampTimerL1.GetElapseTime() / _rampTimeL1;
}
}
}
private void MonitorL2Ramping()
{
if (!_rampTimerL2.IsIdle())
{
if (_rampTimerL2.IsTimeout() || _rampTimeL2 == 0)
{
_rampTimerL2.Stop();
L2TargetSPSetPoint = _rampTargetL2;
}
else
{
L2TargetSPSetPoint = _rampInitValueL2 + (_rampTargetL2 - _rampInitValueL2) * (float)_rampTimerL2.GetElapseTime() / _rampTimeL2;
}
}
}
private void MonitorL3Ramping()
{
if (!_rampTimerL3.IsIdle())
{
if (_rampTimerL3.IsTimeout() || _rampTimeL3 == 0)
{
_rampTimerL3.Stop();
L3TargetSPSetPoint = _rampTargetL3;
}
else
{
L3TargetSPSetPoint = _rampInitValueL3 + (_rampTargetL3 - _rampInitValueL3) * (float)_rampTimerL3.GetElapseTime() / _rampTimeL3;
}
}
}
*/
#endregion Pyro自动模式
#region Power手动模式
//PSU SCR
public bool SetRatio(float Ratio1, float Ratio2, float Ratio3)
{
L2RatioSetPoint = Ratio2 > 100 ? 100 : Ratio2;
L1RatioSetPoint = Ratio1 > 100 ? 100 : Ratio1;
L3RatioSetPoint = Ratio3 > 100 ? 100 : Ratio3;
var reason = $"{Display} Set Ratio ";
EV.PostInfoLog(Module, reason);
return true;
}
//自动执行(比例和模式由其它方法设置)
private void MonitorSetOP()
{
if (HeaterModeSetPoint == HeatStrategy.PyroFollow)
{
if (IsPSU(Name))
{
// L1、L3输出值跟随L2L2由2704自动调整
L2TargetOPSetPoint = L2WorkingOPFeedBack;
var l1 = L2RatioSetPoint > 0 ? L2WorkingOPFeedBack * L1RatioSetPoint / L2RatioSetPoint : 0;
L1TargetOPSetPoint = l1 > 100 ? 100 : l1;
var l3 = L2RatioSetPoint > 0 ? L2WorkingOPFeedBack * L3RatioSetPoint / L2RatioSetPoint : 0;
L3TargetOPSetPoint = l3 > 100 ? 100 : l3;
}
else if (IsSCR(Name))
{
// L1、L2输出值跟随L3L3由2704自动调整
L3TargetOPSetPoint = L3WorkingOPFeedBack;
var l1 = L3RatioSetPoint > 0 ? L3WorkingOPFeedBack * L1RatioSetPoint / L3RatioSetPoint : 0;
L1TargetOPSetPoint = l1 > 100 ? 100 : l1;
var l2 = L3RatioSetPoint > 0 ? L3WorkingOPFeedBack * L2RatioSetPoint / L3RatioSetPoint : 0;
L2TargetOPSetPoint = l2 > 100 ? 100 : l2;
}
}
else if (HeaterModeSetPoint == HeatStrategy.PyroAuto)
{
// L1输出值跟随L2L2、L3由2704自动调整
L2TargetOPSetPoint = L2WorkingOPFeedBack;
var l1 = L2RatioSetPoint > 0 ? L2WorkingOPFeedBack * L1RatioSetPoint / L2RatioSetPoint : 0;
L1TargetOPSetPoint = l1 > 100 ? 100 : l1;
}
else //Power
{
var distur = 0f;
if (IsSCR(Name))
{
// 扰动模拟
MonitorDisturbanceSimulation();
distur = (float)_disturbance;
}
var l1 = L1RatioSetPoint;
if (_disturbanceScr == 1)
l1 += distur;
L1TargetOPSetPoint = l1 > 100 ? 100 : l1;
var l2 = L2RatioSetPoint;
if (_disturbanceScr == 2)
l2 += distur;
L2TargetOPSetPoint = l2 > 100 ? 100 : l2;
var l3 = L3RatioSetPoint;
if (_disturbanceScr == 3)
l3 += distur;
L3TargetOPSetPoint = l3 > 100 ? 100 : l3;
}
}
#endregion
protected override void HandleMonitor()
{
try
{
MonitorTargetSPRamping("L1");
MonitorTargetSPRamping("L2");
MonitorTargetSPRamping("L3");
MonitorSetOP();
MonitorL1RecipeRatioRamping();
MonitorL2RecipeRatioRamping();
MonitorL3RecipeRatioRamping();
MonitorRecipeSetPowerRefRamping();
MonitorTempLimit();
MonitorAlarm();
MonitorDynamicPIDLimit();
MonitorTempRaisingTooFastAlarm();
}
catch (Exception ex)
{
LOG.Write(ex);
}
}
private void CheckHighTemp()
{
if (OuterTemp > _PSUHighLimit.DoubleValue || MiddleTemp > _PSUHighLimit.DoubleValue)
_overHighTempCount += 1;
else
{
_overHighTempCount = 0;
_overHighTempTRIG.RST = true;
}
if (_overHighTempCount >= 10)
{
_overHighTempTRIG.CLK = true;
if (_overHighTempTRIG.Q)
{
EV.PostAlarmLog(Module, $"Alarm63 Temp High,Temp is over PSUHighLimit:{_PSUHighLimit.DoubleValue} ℃ , \r\nOuterTemp:{OuterTemp}℃ , MiddleTemp:{MiddleTemp}℃");
}
}
}
public void StopRamp()
{
/*if (!_rampTimerL1.IsIdle())
{
_rampTimerL1.Stop();
}
if (!_rampTimerL2.IsIdle())
{
_rampTimerL2.Stop();
}
if (!_rampTimerL3.IsIdle())
{
_rampTimerL3.Stop();
}*/
foreach (var loop in _rampParams)
{
loop.StopRamp();
}
if (!_rampTimerL1RatiorRecipe.IsIdle())
{
_rampTimerL1RatiorRecipe.Stop();
}
if (!_rampTimerL2RatiorRecipe.IsIdle())
{
_rampTimerL2RatiorRecipe.Stop();
}
if (!_rampTimerL3RatiorRecipe.IsIdle())
{
_rampTimerL3RatiorRecipe.Stop();
}
}
#region
//设置额定功率
private bool SetRatedValue(float Ratio1, float Ratio2, float Ratio3)
{
L1RatedSetPoint = Ratio1;
L2RatedSetPoint = Ratio2;
L3RatedSetPoint = Ratio3;
var reason = $"{Display} Set RatedValue";
EV.PostInfoLog(Module, reason);
return true;
}
public void RecipeSetPowerRef(float PowerRef, int time)
{
_rampTimerSetPowerRef.Stop();
if (time > 0)
{
_rampInitValueSetPowerRefRecipe = PowerRefSetPoint;
_rampTimeSetPowerRefRecipe = time;
_rampTargetSetPowerRefRecipe = PowerRef;
_rampTimerSetPowerRef.Start(_rampTimeSetPowerRefRecipe);
}
else
{
PowerRefSetPoint = PowerRef;
}
}
private void MonitorRecipeSetPowerRefRamping()
{
if (!_rampTimerSetPowerRef.IsIdle())
{
if (_rampTimerSetPowerRef.IsTimeout() || _rampTimeSetPowerRefRecipe == 0)
{
_rampTimerSetPowerRef.Stop();
PowerRefSetPoint = _rampTargetSetPowerRefRecipe;
}
else
{
PowerRefSetPoint = _rampInitValueSetPowerRefRecipe + (_rampTargetSetPowerRefRecipe - _rampInitValueSetPowerRefRecipe) * (float)_rampTimerSetPowerRef.GetElapseTime() / _rampTimeSetPowerRefRecipe;
}
}
}
#endregion
#region
/// <summary>
/// 昂坤三个温度相互之间的最大差值
/// </summary>
public double PyroTempMaxDiff
{
get
{
if (MiddleTemp > _pyroWarmBaseTemp.DoubleValue || OuterTemp > _pyroWarmBaseTemp.DoubleValue)
{
var tempMaxDiff = Math.Abs(OuterTemp - MiddleTemp);
return tempMaxDiff;
}
return 0;
}
}
private readonly R_TRIG _pyroTempMaxDiffWarm = new ();
private void MonitorAlarm()
{
var status = DATA.Poll($"{Module}.Status") == null ? "" : DATA.Poll($"{Module}.Status").ToString(); //状态
var processCountTime = DATA.Poll($"{Module}.RecipeTotalTime") == null ? 0 : Convert.ToInt32(DATA.Poll($"{Module}.RecipeTotalTime")); //Process总时间
var recipeStepTime = DATA.Poll($"{Module}.RecipeTotalElapseTime") == null ? 0 : Convert.ToInt32(DATA.Poll($"{Module}.RecipeTotalElapseTime")); //已经运行的时间
if (status == "Process" && recipeStepTime > _pyroWarmEffectFromTime.IntValue && processCountTime - recipeStepTime > _pyroWarmEffectEndTime.IntValue)
{
_pyroTempMaxDiffWarm.CLK = PyroTempMaxDiff > _pyroWarmMaxDiff.DoubleValue && _pyroWarmMaxDiff.DoubleValue > 0;
if (_pyroTempMaxDiffWarm.Q)
{
if (_PyroWarmIsAlarm.BoolValue)
{
EV.PostAlarmLog(Module, $"Pyro Temp Max Difference is over {_pyroWarmMaxDiff.DoubleValue} ℃");
}
else
{
EV.PostWarningLog(Module, $"Pyro Temp Max Difference is over {_pyroWarmMaxDiff.DoubleValue} ℃");
}
}
}
}
//DateTime dtLastRecordTime = DateTime.Now;
private readonly Stopwatch _swTempRaisingMonitor = new();
private double _lastInnerTemp = 0;
private double _lastMiddleTemp = 0;
private double _lastOuterTemp = 0;
private readonly R_TRIG _rTrigMiddleTempRaisingFast = new();
private readonly R_TRIG _rTrigInnerTempRaisingFast = new();
private readonly R_TRIG _rTrigOuterTempRaisingFast = new();
private bool MonitorTempRaisingTooFastAlarm()
{
var objPmStatus = DATA.Poll($"{Module}.Status");
var pmStatus = objPmStatus == null ? "" : objPmStatus.ToString();
if (pmStatus != "Process")
{
// PM没有Process时强制停止计时器
// 否则由于上一次工艺结束时定时器处于运行状态再次进入工艺时IsRunning == true定时器记录时间会很大导致判断错误。
_swTempRaisingMonitor.Reset();
}
else
{
if (!_swTempRaisingMonitor.IsRunning)
{
// 首次进入Process启动1s定时并保存当前当前温度。
// 后续整个Process过程中定制器始终处于工作状态除非发生异常。
_swTempRaisingMonitor.Start();
_lastInnerTemp = _innerTempRaw;
_lastMiddleTemp = _middleTempRaw;
_lastOuterTemp = _outerTempRaw;
}
else if (_swTempRaisingMonitor.Elapsed.TotalSeconds > 1)
{
_swTempRaisingMonitor.Stop();
var interval = _swTempRaisingMonitor.Elapsed.TotalSeconds;
if (interval <= 0)
return true;
Debug.WriteLine($"IoTC {Name} Temp Raising Fast monitor interval: {interval:F1}", Module);
Debug.WriteLine($"Is Alarm: {_isAlarmTempRaisingFast}; " +
$"Middle Rate SP: {_tempRaisingRateMiddle}℃/s; " +
$"Outer Rate SP: {_tempRaisingRateOuter}℃/s; ",
Module);
var raisingRatePerSec = 0.0;
#region Inner
/* raisingRatePerSec = Math.Abs(InnerTemp - _lastInnerTemp) / interval;
Debug.WriteLine($"Inner Temp Raising Rate: {raisingRatePerSec:F1}", Module);
_rTrigInnerTempRaisingFast.CLK = raisingPerSec > _tempRaisingRateInner;
if (_rTrigInnerTempRaisingFast.Q)
{
var msg = $"Inner Temp raising too fast at a rate of {raisingPerSec:F1}℃/s";
if (_isAlarmTempRaisingFast)
EV.PostAlarmLog(Module, msg);
else
EV.PostWarningLog(Module, msg);
}*/
#endregion
#region Middle
raisingRatePerSec = Math.Abs(_middleTempRaw - _lastMiddleTemp) / interval;
Debug.WriteLine($"Middle Temp Raising Rate: {raisingRatePerSec:F1}", Module);
_rTrigMiddleTempRaisingFast.CLK = raisingRatePerSec > _tempRaisingRateMiddle;
if (_rTrigMiddleTempRaisingFast.Q)
{
var msg = $"Middle Temp raising too fast at a rate of {raisingRatePerSec:F1}℃/s";
if (_isAlarmTempRaisingFast)
EV.PostAlarmLog(Module, msg);
else
EV.PostWarningLog(Module, msg);
}
#endregion
#region Outer
raisingRatePerSec = Math.Abs(_outerTempRaw - _lastOuterTemp) / interval;
Debug.WriteLine($"Outer Temp Raising Rate: {raisingRatePerSec:F1}", Module);
_rTrigOuterTempRaisingFast.CLK = raisingRatePerSec > _tempRaisingRateOuter;
if (_rTrigOuterTempRaisingFast.Q)
{
var msg = $"Outer Temp raising too fast at a rate of {raisingRatePerSec:F1}℃/s";
if (_isAlarmTempRaisingFast)
EV.PostAlarmLog(Module, msg);
else
EV.PostWarningLog(Module, msg);
}
#endregion
// 保存当前当前温度并重启1s定时器
_lastInnerTemp = _innerTempRaw;
_lastMiddleTemp = _middleTempRaw;
_lastOuterTemp = _outerTempRaw;
_swTempRaisingMonitor.Restart();
}
}
return true;
}
public void MonitorTemp()
{
if (IsPSU(Name))
{
L1InputTempSetPoint = InnerTemp;
L3InputTempSetPoint = OuterTemp;
if(MiddleTemp <= _simlatorTempThreshold)
{
bool _simlatorTempFlag = !_diPSUInnerHeaterEnable.Value &&
!_diPSUMiddleHeaterEnable.Value &&
!_diPSUOuterHeaterEnable.Value &&
!_diSCRUpHeaterEnable.Value &&
!_diSCRMiddleHeaterEnable.Value &&
!_diSCRDownHeaterEnable.Value;
if (_simlatorTempFlag)
{
_stopwatch.Start();
}
else
_stopwatch.Stop();
L2InputTempSetPoint = (float)(L2InputTempSetPoint > 25 ? _simlatorTempThreshold - _stopwatch.Elapsed.TotalSeconds * 3.3 / 60 : 25);
}
else
{
_stopwatch.Reset() ;
float middleControlTempRatio = _middleTempRatio / 100f;
float outerControlTempRatio = (100 - _middleTempRatio) / 100f;
L2InputTempSetPoint = MiddleTemp * middleControlTempRatio + OuterTemp * outerControlTempRatio;
}
}
else
{
L3InputTempSetPoint = InnerTemp;
}
}
/// <summary>
/// 掉线设置温度上下限
/// </summary>
private void MonitorTempLimit()
{
if (IsPSU(Name))
{
if (DoubleUtil.NotEqual(L1TempHighLimitSetPoint, _PSUHighLimit.DoubleValue))
{
L1TempHighLimitSetPoint = (float)_PSUHighLimit.DoubleValue;
}
if (DoubleUtil.NotEqual(L2TempHighLimitSetPoint, _PSUHighLimit.DoubleValue))
{
L2TempHighLimitSetPoint = (float)_PSUHighLimit.DoubleValue;
}
if (DoubleUtil.NotEqual(L3TempHighLimitSetPoint, _PSUHighLimit.DoubleValue))
{
L3TempHighLimitSetPoint = (float)_PSUHighLimit.DoubleValue;
}
if (DoubleUtil.NotEqual(L1TempLowLimitSetPoint, _PSULowLimit.DoubleValue))
{
L1TempLowLimitSetPoint = (float)_PSULowLimit.DoubleValue;
}
if (DoubleUtil.NotEqual(L2TempLowLimitSetPoint, _PSULowLimit.DoubleValue))
{
L2TempLowLimitSetPoint = (float)_PSULowLimit.DoubleValue;
}
if (DoubleUtil.NotEqual(L3TempLowLimitSetPoint, _PSULowLimit.DoubleValue))
{
L3TempLowLimitSetPoint = (float)_PSULowLimit.DoubleValue;
}
}
else if (IsSCR(Name))
{
if (DoubleUtil.NotEqual(L1TempHighLimitSetPoint, _SCRHighLimit.DoubleValue))
{
L1TempHighLimitSetPoint = (float)_PSUHighLimit.DoubleValue;
}
if (DoubleUtil.NotEqual(L2TempHighLimitSetPoint, _SCRHighLimit.DoubleValue))
{
L2TempHighLimitSetPoint = (float)_SCRHighLimit.DoubleValue;
}
if (DoubleUtil.NotEqual(L3TempHighLimitSetPoint, _SCRHighLimit.DoubleValue))
{
L3TempHighLimitSetPoint = (float)_SCRHighLimit.DoubleValue;
}
if (DoubleUtil.NotEqual(L1TempLowLimitSetPoint, _SCRLowLimit.DoubleValue))
{
L1TempLowLimitSetPoint = (float)_SCRLowLimit.DoubleValue;
}
if (DoubleUtil.NotEqual(L2TempLowLimitSetPoint, _SCRLowLimit.DoubleValue))
{
L2TempLowLimitSetPoint = (float)_SCRLowLimit.DoubleValue;
}
if (DoubleUtil.NotEqual(L3TempLowLimitSetPoint, _SCRLowLimit.DoubleValue))
{
L3TempLowLimitSetPoint = (float)_SCRLowLimit.DoubleValue;
}
}
}
#endregion
public void Terminate()
{
}
public void Reset()
{
_trigSetTempLimit.RST = true;
_pyroTempMaxDiffWarm.RST = true;
_rTrigMiddleTempRaisingFast.RST = true;
_rTrigOuterTempRaisingFast.RST = true;
_rTrigInnerTempRaisingFast.RST = true;
_overHighTempCount = 0;
_overHighTempTRIG.RST = true;
}
#region Dynamic PID Limit
private readonly R_TRIG _rTrigHeatStrategyChanged = new();
private HeatStrategy? _prevHeatStrategy = null;
private void MonitorDynamicPIDLimit()
{
var currHeatStrategy = HeaterModeSetPoint;
_rTrigHeatStrategyChanged.CLK = HeaterModeSetPoint != _prevHeatStrategy;
if (_rTrigHeatStrategyChanged.Q)
{
// 控温策略发生变化设置PID Limit
_prevHeatStrategy = currHeatStrategy;
switch (currHeatStrategy)
{
case HeatStrategy.PyroAuto:
case HeatStrategy.PyroFollow:
SetPIDLimit(_pidLimitLowerUnderPryo, _pidLimitUpperUnderPyro, _pidRateLimitUnderPyro);
break;
case HeatStrategy.Power:
SetPIDLimit(0, 100, 5);
break;
default:
EV.PostWarningLog(Module, $"{Name} Unknown heat strategy to set dynamic PID limits.");
break;
}
}
}
/// <summary>
/// IoTC对象内部使用直接写AO。
/// </summary>
/// <param name="lower"></param>
/// <param name="upper"></param>
/// <param name="rate"></param>
private void SetPIDLimit(double lower, double upper, double rate)
{
//SetAoValue(_aoPIDLimitLower, (float)lower);
//SetAoValue(_aoPIDLimitUpper, (float)upper);
//SetAoValue(_aoPIDRateLimit, (float)rate);
}
/// <summary>
/// UI中手动设置Limit时调用此方法。
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
/// <param>
/// UI中手动设置Limit后暂存设定值但不写入系统配置
/// 下次重启RT后默认加载系统配置中的Limit设定值。
/// </param>
private bool SetPIDLimit(object[] args)
{
//if (args == null || args.Length != 3)
//{
// EV.PostWarningLog(Module, $"{Name} Incorrect arguments of OP {nameof(SetPIDLimit)}");
// return false;
//}
//if (args[0] is double lower && args[1] is double upper && args[2] is double rate)
//{
// SetPIDLimit(lower, upper, rate);
// /*// 更新系统配置,确保重启后为
// SC.SetItemValue(_scNamePidLimitLower, lower);
// SC.SetItemValue(_scNamePidLimitUpper, upper);
// SC.SetItemValue(_scNamePidRateLimit, rate);*/
// // 暂存设置值下次从Power切换到Pyro时直接使用。
// _pidLimitLowerUnderPryo = lower;
// _pidLimitUpperUnderPyro = upper;
// _pidRateLimitUnderPyro = rate;
// return true;
//}
//EV.PostWarningLog(Module, $"{Name} Some of parameters of OP {nameof(SetPIDLimit)} are not numbers");
return false;
}
#endregion
#region PSU Y Calc
public void SetPCPSUY()
{
PSU1Y = Convert.ToInt32(PCPSU1Y);
PSU2Y = Convert.ToInt32(PCPSU2Y);
PSU3Y = Convert.ToInt32(PCPSU3Y);
}
public bool SetPSUY(double _dbPSU1Y, double _dbPSU2Y, double _dbPSU3Y)
{
var iPSU1Y = Convert.ToInt32(_dbPSU1Y * 10);
var iPSU2Y = Convert.ToInt32(_dbPSU2Y * 10);
var iPSU3Y = Convert.ToInt32(_dbPSU3Y * 10);
//写入IO
PSU1Y = iPSU1Y;
PSU2Y = iPSU2Y;
PSU3Y = iPSU3Y;
//写入配置
if ((iPSU1Y > 0 && iPSU1Y < 1000)
&& (iPSU2Y > 0 && iPSU2Y < 1000)
&& (iPSU3Y > 0 && iPSU3Y < 1000)
)
{
SC.SetItemValue($"PM.{Module}.Heater.PSU1Y", (double)iPSU1Y);
SC.SetItemValue($"PM.{Module}.Heater.PSU2Y", (double)iPSU2Y);
SC.SetItemValue($"PM.{Module}.Heater.PSU3Y", (double)iPSU3Y);
}
var reason = $"{Display} Set PSUY values.";
EV.PostInfoLog(Module, reason);
return true;
}
public bool SetPSUPower(double psu1Power, double psu2Power, double psu3Power)
{
//写入配置
if (psu1Power > 0
&& psu2Power > 0
&& psu3Power > 0
)
{
SC.SetItemValue($"PM.{Module}.Heater.PSU1Power", psu1Power);
SC.SetItemValue($"PM.{Module}.Heater.PSU2Power", psu2Power);
SC.SetItemValue($"PM.{Module}.Heater.PSU3Power", psu3Power);
}
var reason = $"{Display} Save SystemConfig PSUPower values.";
EV.PostInfoLog(Module, reason);
return true;
}
public void GetPCPSUPower()
{
PSU1Power = SC.GetConfigItem($"PM.{Module}.Heater.PSU1Power").DoubleValue;
PSU2Power = SC.GetConfigItem($"PM.{Module}.Heater.PSU2Power").DoubleValue;
PSU3Power = SC.GetConfigItem($"PM.{Module}.Heater.PSU3Power").DoubleValue;
}
#endregion
#region Property
/// <summary>
/// 外部温度
/// </summary>
public float OuterTemp { get; set; }
public float InnerTemp { get; set; }
public float MiddleTemp { get; set; }
private float _innerTempRaw;
private float _middleTempRaw;
private float _outerTempRaw;
///// <summary>
///// 内部温度
///// </summary>
//public float InnerTemp
//{
// get
// {
// if (ScInnerTempValue == "Disabled")
// return 0;
// var obj = DATA.Poll(ScInnerTempValue);
// if (obj != null && float.TryParse(obj.ToString(), out var temp))
// return temp.Round(1);
// return 0;
// }
//}
///// <summary>
///// 中间温度
///// </summary>
//public float MiddleTemp
//{
// get
// {
// if (ScMiddleTempValue == "Disabled")
// return 0;
// var obj = DATA.Poll(ScMiddleTempValue);
// if (obj != null && float.TryParse(obj.ToString(), out var temp))
// return temp.Round(1);
// return 0;
// }
//}
private void MonitorTempFilter()
{
OuterTemp = (float)FilterTempValue(_outerTempFilter, ScOuterTempValue, ref _outerTempRaw);
InnerTemp = (float)FilterTempValue(_innerTempFilter, ScInnerTempValue, ref _innerTempRaw);
MiddleTemp = (float)FilterTempValue(_middleTempFilter, ScMiddleTempValue, ref _middleTempRaw);
}
private double FilterTempValue(TempDataFilter tempFilter,string scTempSensor, ref float tempRaw)
{
double temp = 0;
if (scTempSensor == "Disabled")
return 0;
var obj = DATA.Poll(scTempSensor);
if (obj != null && double.TryParse(obj.ToString(), out temp))
{
tempFilter.Update(temp);
tempRaw = (float)temp;
return temp.Round(1);
}
return 0;
}
public float L1WorkingOPFeedBack
{
get
{
return _aiL1WorkingOPFeedBack == null ? 0 : (_isFloatAioType ? _aiL1WorkingOPFeedBack.Value : _aiL1WorkingOPFeedBack.Value);
}
}
public float L2WorkingOPFeedBack
{
get
{
return _aiL2WorkingOPFeedBack == null ? 0 : (_isFloatAioType ? _aiL2WorkingOPFeedBack.Value : _aiL2WorkingOPFeedBack.Value);
}
}
public float L3WorkingOPFeedBack
{
get
{
return _aiL3WorkingOPFeedBack == null ? 0 : (_isFloatAioType ? _aiL3WorkingOPFeedBack.Value : _aiL3WorkingOPFeedBack.Value);
}
}
public float L1PVFeedBack
{
get
{
return _aiL1PVFeedBack == null ? 0 : (_isFloatAioType ? _aiL1PVFeedBack.Value : _aiL1PVFeedBack.Value);
}
}
public float L2PVFeedBack
{
get
{
return _aiL2PVFeedBack == null ? 0 : (_isFloatAioType ? _aiL2PVFeedBack.Value : _aiL2PVFeedBack.Value);
}
}
public float L3PVFeedBack
{
get
{
return _aiL3PVFeedBack == null ? 0 : (_isFloatAioType ? _aiL3PVFeedBack.Value : _aiL3PVFeedBack.Value);
}
}
public bool L1TempHighAlarmFeedBack
{
get
{
return _diL1TempHighAlarmFeedBack == null ? false : _diL1TempHighAlarmFeedBack.Value;
}
}
public bool L2TempHighAlarmFeedBack
{
get
{
return _diL2TempHighAlarmFeedBack == null ? false : _diL2TempHighAlarmFeedBack.Value;
}
}
public bool L3TempHighAlarmFeedBack
{
get
{
return _diL3TempHighAlarmFeedBack == null ? false : _diL3TempHighAlarmFeedBack.Value;
}
}
public bool L1TempLowAlarmFeedBack
{
get
{
return _diL1TempLowAlarmFeedBack == null ? false : _diL1TempLowAlarmFeedBack.Value;
}
}
public bool L2TempLowAlarmFeedBack
{
get
{
return _diL2TempLowAlarmFeedBack == null ? false : _diL2TempLowAlarmFeedBack.Value;
}
}
public bool L3TempLowAlarmFeedBack
{
get
{
return _diL3TempLowAlarmFeedBack == null ? false : _diL3TempLowAlarmFeedBack.Value;
}
}
public TCModes L1LoopModeSetPoint
{
get => Get2704ControlMode(_aoL1LoopModeSetPoint);
set => Set2704ControlMode(_aoL1LoopModeSetPoint, value);
}
public TCModes L2LoopModeSetPoint
{
get => Get2704ControlMode(_aoL2LoopModeSetPoint);
set => Set2704ControlMode(_aoL2LoopModeSetPoint, value);
}
public TCModes L3LoopModeSetPoint
{
get => Get2704ControlMode(_aoL3LoopModeSetPoint);
set => Set2704ControlMode(_aoL3LoopModeSetPoint, value);
}
public float L1TargetSPSetPoint
{
get => GetAoValue(_aoL1TargetSPSetPoint);
set => SetAoValue(_aoL1TargetSPSetPoint, value);
}
public float L2TargetSPSetPoint
{
get => GetAoValue(_aoL2TargetSPSetPoint);
set => SetAoValue(_aoL2TargetSPSetPoint, value);
}
public float L3TargetSPSetPoint
{
get => GetAoValue(_aoL3TargetSPSetPoint);
set => SetAoValue(_aoL3TargetSPSetPoint, value);
}
public float L1TargetOPSetPoint
{
get => GetAoValue(_aoL1TargetOPSetPoint);
set => SetAoValue(_aoL1TargetOPSetPoint, value);
}
public float L2TargetOPSetPoint
{
get => GetAoValue(_aoL2TargetOPSetPoint);
set => SetAoValue(_aoL2TargetOPSetPoint, value);
}
public float L3TargetOPSetPoint
{
get => GetAoValue(_aoL3TargetOPSetPoint);
set => SetAoValue(_aoL3TargetOPSetPoint, value);
}
/// <summary>
/// 温度传感器实时温度写入2704
/// </summary>
public float L1InputTempSetPoint
{
get => GetAoValue(_aoL1InputTempSetPoint);
set => SetAoValue(_aoL1InputTempSetPoint, value);
}
/// <summary>
/// 温度传感器实时温度写入2704
/// </summary>
public float L2InputTempSetPoint
{
get => GetAoValue(_aoL2InputTempSetPoint);
set => SetAoValue(_aoL2InputTempSetPoint, value);
}
/// <summary>
/// 温度传感器实时温度写入2704
/// </summary>
public float L3InputTempSetPoint
{
get => GetAoValue(_aoL3InputTempSetPoint);
set => SetAoValue(_aoL3InputTempSetPoint, value);
}
public float L1TempHighLimitSetPoint
{
get => GetAoValue(_aoL1TempHighLimitSetPoint);
set => SetAoValue(_aoL1TempHighLimitSetPoint, value);
}
public float L2TempHighLimitSetPoint
{
get => GetAoValue(_aoL2TempHighLimitSetPoint);
set => SetAoValue(_aoL2TempHighLimitSetPoint, value);
}
public float L3TempHighLimitSetPoint
{
get => GetAoValue(_aoL3TempHighLimitSetPoint);
set => SetAoValue(_aoL3TempHighLimitSetPoint, value);
}
public float L1TempLowLimitSetPoint
{
get => GetAoValue(_aoL1TempLowLimitSetPoint);
set => SetAoValue(_aoL1TempLowLimitSetPoint, value);
}
public float L2TempLowLimitSetPoint
{
get => GetAoValue(_aoL2TempLowLimitSetPoint);
set => SetAoValue(_aoL2TempLowLimitSetPoint, value);
}
public float L3TempLowLimitSetPoint
{
get => GetAoValue(_aoL3TempLowLimitSetPoint);
set => SetAoValue(_aoL3TempLowLimitSetPoint, value);
}
/// <summary>
/// 读取指定AO的实时值。
/// </summary>
/// <remarks>
/// 如果AO为null则返回0。
/// </remarks>
/// <param name="ao">指定的AO端口。</param>
/// <param name="value">待写入的值。</param>
/// <returns></returns>
private void SetAoValue(AOAccessor ao, float value)
{
if (ao == null)
return;
if (_isFloatAioType)
ao.Value = value;
else
ao.Value = (short)value;
}
/// <summary>
/// 读取指定AO的实时值。
/// </summary>
/// <remarks>
/// 如果AO为null则返回0。
/// </remarks>
/// <param name="ao"></param>
/// <returns></returns>
private float GetAoValue(AOAccessor ao)
{
if (ao == null)
return 0;
return _isFloatAioType ? ao.Value : ao.Value;
}
/// <summary>
/// 获取2704每通道工作模式。
/// </summary>
/// <param name="ao"></param>
/// <returns></returns>
private TCModes Get2704ControlMode(AOAccessor ao)
{
if (ao == null)
return TCModes.Manual;
var mode = (int)(ao.Value);
return ConvertToTcModes(mode);
}
/// <summary>
/// 将数字转换为<see cref="HeatStrategy"/>枚举。
/// </summary>
/// <remarks>
/// 如果传入的mode不在<see cref="HeatStrategy"/>定义范围内则默认返回HeatStrategy.Power。
/// </remarks>
/// <param name="ao"></param>
/// <returns></returns>
private HeatStrategy GetHeaterMode(AOAccessor ao)
{
if (ao == null)
return HeatStrategy.Power;
var mode = (int)(ao.Value);
return ConvertToHeatStrategy(mode);
}
private void Set2704ControlMode(AOAccessor ao, TCModes mode)
{
if (_isFloatAioType)
{
ao.Value = (float)mode;
}
else
{
ao.Value = (short)mode;
}
}
private void SetHeatMode(AOAccessor ao, HeatStrategy mode)
{
if (_isFloatAioType)
{
ao.Value = (float)mode;
}
else
{
ao.Value = (short)mode;
}
}
/// <summary>
/// 注意AO-203AO203在PLC中没有使用仅做变量保持使用。
/// </summary>
public HeatStrategy HeaterModeSetPoint
{
get => GetHeaterMode(_aoHeaterModeSetPoint);
set => SetHeatMode(_aoHeaterModeSetPoint, value);
}
public float PowerRefSetPoint
{
get
{
return _aoPowerRefSetPoint == null ? 0 : (_isFloatAioType ? _aoPowerRefSetPoint.Value : _aoPowerRefSetPoint.Value);
}
set
{
if (_isFloatAioType)
{
_aoPowerRefSetPoint.Value = value;
}
else
{
_aoPowerRefSetPoint.Value = (short)value;
}
}
}
public float L1RatioSetPoint
{
get
{
return _aoL1RatioSetPoint == null ? 0 : (_isFloatAioType ? _aoL1RatioSetPoint.Value : _aoL1RatioSetPoint.Value);
}
set
{
if (_isFloatAioType)
{
_aoL1RatioSetPoint.Value = value;
}
else
{
_aoL1RatioSetPoint.Value = (short)value;
}
}
}
public float L2RatioSetPoint
{
get
{
return _aoL2RatioSetPoint == null ? 0 : (_isFloatAioType ? _aoL2RatioSetPoint.Value : _aoL2RatioSetPoint.Value);
}
set
{
if (_isFloatAioType)
{
_aoL2RatioSetPoint.Value = value;
}
else
{
_aoL2RatioSetPoint.Value = (short)value;
}
}
}
public float L3RatioSetPoint
{
get
{
return _aoL3RatioSetPoint == null ? 0 : (_isFloatAioType ? _aoL3RatioSetPoint.Value : _aoL3RatioSetPoint.Value);
}
set
{
if (_isFloatAioType)
{
_aoL3RatioSetPoint.Value = value;
}
else
{
_aoL3RatioSetPoint.Value = (short)value;
}
}
}
public float L1RatedSetPoint
{
get => _aoL1RatedSetPoint?.Value ?? 0;
set
{
if (_isFloatAioType)
{
_aoL1RatedSetPoint.Value = value;
}
else
{
_aoL1RatedSetPoint.Value = (short)value;
}
}
}
public float L2RatedSetPoint
{
get
{
return _aoL2RatedSetPoint?.Value ?? 0;
}
set
{
if (_isFloatAioType)
{
_aoL2RatedSetPoint.Value = value;
}
else
{
_aoL2RatedSetPoint.Value = (short)value;
}
}
}
public float L3RatedSetPoint
{
get
{
return _aoL3RatedSetPoint?.Value ?? 0;
}
set
{
if (_isFloatAioType)
{
_aoL3RatedSetPoint.Value = value;
}
else
{
_aoL3RatedSetPoint.Value = (short)value;
}
}
}
public float L1VoltageLimited
{
get
{
return _aoL1VoltageLimited?.Value ?? 0;
}
set
{
if (_isFloatAioType)
{
_aoL1VoltageLimited.Value = value;
}
else
{
_aoL1VoltageLimited.Value = (short)value;
}
}
}
public float L2VoltageLimited
{
get
{
return _aoL2VoltageLimited?.Value ?? 0;
}
set
{
if (_isFloatAioType)
{
_aoL2VoltageLimited.Value = value;
}
else
{
_aoL2VoltageLimited.Value = (short)value;
}
}
}
public float L3VoltageLimited
{
get
{
return _aoL3VoltageLimited?.Value ?? 0;
}
set
{
if (_isFloatAioType)
{
_aoL3VoltageLimited.Value = value;
}
else
{
_aoL3VoltageLimited.Value = (short)value;
}
}
}
public float TtempCtrlTCIN
{
get
{
if (_aiTtempCtrlTCIN != null)
{
return _aiTtempCtrlTCIN.Value;
}
return 0;
}
}
public int PSU1Y
{
get
{
return _aoPSU1Y == null ? 0 : (int)_aoPSU1Y.Value;
}
set
{
if (_isFloatAioType)
{
_aoPSU1Y.Value = value;
}
else
{
_aoPSU1Y.Value = (short)value;
}
}
}
public int PSU2Y
{
get
{
return _aoPSU2Y == null ? 0 : (int)_aoPSU2Y.Value;
}
set
{
if (_isFloatAioType)
{
_aoPSU2Y.Value = value;
}
else
{
_aoPSU2Y.Value = (short)value;
}
}
}
public int PSU3Y
{
get
{
return _aoPSU3Y == null ? 0 : (int)_aoPSU3Y.Value;
}
set
{
if (_isFloatAioType)
{
_aoPSU3Y.Value = value;
}
else
{
_aoPSU3Y.Value = (short)value;
}
}
}
//
public double PSU1Power
{
get; set;
}
public double PSU2Power
{
get; set;
}
public double PSU3Power
{
get; set;
}
//
public double PCPSU1Y
{
get
{
return SC.GetValue<double>($"PM.{Module}.Heater.PSU1Y");
}
}
public double PCPSU2Y
{
get
{
return SC.GetValue<double>($"PM.{Module}.Heater.PSU2Y");
}
}
public double PCPSU3Y
{
get
{
return SC.GetValue<double>($"PM.{Module}.Heater.PSU3Y");
}
}
#endregion Property
#region SCR增加扰动的逻辑
private int _disturbanceScr;
private string _disturbanceType;
private double _disturbancePeriodSec;
private double _disturbanceDurationSec;
private double _disturbanceAmplitude;
private double _disturbance = 0;
private readonly Stopwatch _swDisturbancePeriod = new();
private readonly Stopwatch _swDisturbanceDuration = new();
public void DisturbanceSimInitialize()
{
DATA.Subscribe($"{Module}.{Name}.Disturbance.PeriodTimerRunning", () => _swDisturbancePeriod.IsRunning);
DATA.Subscribe($"{Module}.{Name}.Disturbance.DurationTimerRunning", () => _swDisturbanceDuration.IsRunning);
DATA.Subscribe($"{Module}.{Name}.Disturbance.SCR", () => _disturbanceScr);
DATA.Subscribe($"{Module}.{Name}.Disturbance.Value", () => _disturbance);
DATA.Subscribe($"{Module}.{Name}.Disturbance.DurationSec", () => _disturbanceDurationSec);
DATA.Subscribe($"{Module}.{Name}.Disturbance.ElapsedSec",
() => _swDisturbanceDuration.IsRunning ? _swDisturbanceDuration.Elapsed.TotalSeconds : 0);
OP.Subscribe($"{Module}.{Name}.StartDisturbanceSim", (arg1, args) =>
{
// args[0]: SCR name
// args[1]: type
// args[2]: Period in seconds
// args[3]: Duration in seconds
// args[4]: Amplitude
if (args is { Length: 5 }
&& args[0] is int scrNo
&& args[1] is string strType
&& args[2] is double periodSec
&& args[3] is double durationSec
&& args[4] is double amplitude)
{
_disturbanceScr = scrNo;
_disturbanceType = strType.ToUpperInvariant();
_disturbancePeriodSec = periodSec;
_disturbanceDurationSec = durationSec;
_disturbanceAmplitude = amplitude;
StartDisturbanceSimulation();
return true;
}
else
{
EV.PostAlarmLog(Module, "Invalid args of DisturbanceSCR operation");
return false;
}
});
OP.Subscribe($"{Module}.{Name}.StopDisturbanceSim", (arg1, args) =>
{
StopDisturbanceSimulation();
return true;
});
}
/// <summary>
/// 启动扰动模拟。
/// </summary>
internal void StartDisturbanceSimulation()
{
if (_swDisturbanceDuration.IsRunning)
{
EV.PostWarningLog(Module, "Disturbance simulation process is running");
return;
}
if (_disturbanceScr != 1 && _disturbanceScr != 2 && _disturbanceScr != 3)
{
EV.PostAlarmLog(Module, "Disturbance SCR must be L1 or L2 or L3");
return;
}
if (_disturbanceType != "STEP" && _disturbanceType != "SINE")
{
EV.PostAlarmLog(Module, "Disturbance Type must be 'STEP' or 'SINE'");
return ;
}
if (_disturbancePeriodSec <= 0 || _disturbanceDurationSec <= 0 || _disturbanceAmplitude < 0)
{
EV.PostAlarmLog(Module, "Disturbance Period, Duration and Amplitude must be positive numbers");
return;
}
if(_disturbanceDurationSec < _disturbancePeriodSec)
{
EV.PostAlarmLog(Module, "Disturbance Duration must be greater than Period");
return;
}
if (!_swDisturbanceDuration.IsRunning)
{
_swDisturbanceDuration.Reset();
_swDisturbanceDuration.Start();
_swDisturbancePeriod.Reset();
_swDisturbancePeriod.Start();
LOG.Info(
$"{Module} Disturbance Simulation starts, SCR={_disturbanceScr}, " +
$"Type={_disturbanceType}, " +
$"Amp={_disturbanceAmplitude}, " +
$"Period={_disturbancePeriodSec:F3}s, " +
$"Duration={_swDisturbanceDuration:F1}s");
}
}
/// <summary>
/// 停止扰动模拟。
/// </summary>
internal void StopDisturbanceSimulation()
{
_swDisturbancePeriod.Stop();
_swDisturbanceDuration.Stop();
_disturbancePeriodSec = 0;
_disturbanceDurationSec = 0;
_disturbance = 0;
LOG.Info($"{Module} Disturbance Simulation stoped");
}
internal void MonitorDisturbanceSimulation()
{
// 如果没有设置扰动计时器,则不进行扰动模拟
if (!_swDisturbanceDuration.IsRunning)
{
_swDisturbancePeriod.Stop();
_disturbance = 0;
return;
}
// 到达扰动持续时间后,停止扰动模拟
if (_swDisturbanceDuration.Elapsed.TotalSeconds > _disturbanceDurationSec)
{
StopDisturbanceSimulation();
return;
}
// 计算扰动值
switch (_disturbanceType)
{
case "STEP": // 阶跃扰动
if (_swDisturbancePeriod.IsRunning)
{
// 如果正在运行,计算当前扰动值
var elapsedSec = _swDisturbancePeriod.Elapsed.TotalSeconds;
if (elapsedSec < _disturbancePeriodSec / 2)
_disturbance = _disturbanceAmplitude;
else if (elapsedSec > _disturbancePeriodSec / 2 && elapsedSec < _disturbancePeriodSec)
_disturbance = -_disturbanceAmplitude;
else
_swDisturbancePeriod.Restart();
}
break;
case "SINE": // 正弦扰动
if (_swDisturbancePeriod.IsRunning)
{
// 如果正在运行,计算当前扰动值
var elapsedSec = _swDisturbancePeriod.Elapsed.TotalSeconds;
_disturbance = _disturbanceAmplitude * Math.Sin(2 * Math.PI * elapsedSec / _disturbancePeriodSec);
}
break;
default:
_disturbance = 0;
break;
}
}
#endregion
}
}