2564 lines
96 KiB
C#
2564 lines
96 KiB
C#
|
|
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输出值跟随L2;L2由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输出值跟随L3;L3由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输出值跟随L2;L2、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-203(AO203)在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
|
|||
|
|
}
|
|||
|
|
}
|