using Aitex.Common.Util; using Aitex.Core.Common.DeviceData; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Event; using Aitex.Core.RT.IOCore; using Aitex.Core.RT.Log; using Aitex.Core.RT.OperationCenter; using Aitex.Core.RT.ParameterCenter; using Aitex.Core.RT.SCCore; using Aitex.Core.RT.Tolerance; using Aitex.Core.Util; using MECF.Framework.Common.CommonData; using MECF.Framework.Common.Device.Bases; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.Event; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; namespace FurnaceRT.Devices { public class IoHeater : HeaterBase { public IoHeater(string module, XmlElement node, string ioModule = "") { base.Module = string.IsNullOrEmpty(node.GetAttribute("module")) ? module : node.GetAttribute("module"); base.Name = node.GetAttribute("id"); base.Display = node.GetAttribute("display"); base.DeviceID = node.GetAttribute("schematicId"); formatString = node.GetAttribute("formatString"); InstallZone = node.GetAttribute("installzone"); _uniqueName = $"{Module}{Name}"; _isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float"); _aiCascadePV = ParseAiNode("aiCascadePV", node, ioModule); _aiHeaterPV = ParseAiNode("aiHeaterPV", node, ioModule); _aiWorkingOutput = ParseAiNode("aiWorkingOutput", node, ioModule); _aiOverTemp = ParseAiNode("aiOverTemp", node, ioModule); _aoCascadeControlModeSetPoint = ParseAoNode("aoCascadeControlModeSetPoint", node, ioModule); _aoHeaterControlModeSetPoint = ParseAoNode("aoHeaterControlModeSetPoint", node, ioModule); _aoCascadePID_P = ParseAoNode("aoCascadePID_P", node, ioModule); _aoCascadePID_I = ParseAoNode("aoCascadePID_I", node, ioModule); _aoCascadePID_D = ParseAoNode("aoCascadePID_D", node, ioModule); _aoHeaterPID_P = ParseAoNode("aoHeaterPID_P", node, ioModule); _aoHeaterPID_I = ParseAoNode("aoHeaterPID_I", node, ioModule); _aoHeaterPID_D = ParseAoNode("aoHeaterPID_D", node, ioModule); _aoUpRate = ParseAoNode("aoUpRate", node, ioModule); _aoDownRate = ParseAoNode("aoDownRate", node, ioModule); _aoTCOpenOffsetOffset = ParseAoNode("aoTCOpenOffsetOffset", node, ioModule); _diCascadePVSBrk = ParseDiNode("diCascadePVSBrk", node, ioModule); _diHeaterPVSBrk = ParseDiNode("diHeaterPVSBrk", node, ioModule); _diEnableOutput = ParseDiNode("diEnableOutput", node, ioModule); _doEnableIn = ParseDoNode("doEnableIn", node, ioModule); _doAutoManual = ParseDoNode("doAutoManual", node, ioModule); _doSelect = ParseDoNode("doMainPVSelect", node, ioModule); _doCascadeMode = ParseDoNode("doCascadeMode", node, ioModule); _scRoot = node.GetAttribute("scRoot"); } #region fields private AIAccessor _aiCascadePV; private AIAccessor _aiHeaterPV; private AIAccessor _aiWorkingOutput; private AIAccessor _aiOverTemp; private AOAccessor _aoCascadeControlModeSetPoint; private AOAccessor _aoHeaterControlModeSetPoint; private AOAccessor _aoCascadePID_P; private AOAccessor _aoCascadePID_I; private AOAccessor _aoCascadePID_D; private AOAccessor _aoHeaterPID_P; private AOAccessor _aoHeaterPID_I; private AOAccessor _aoHeaterPID_D; private AOAccessor _aoUpRate; private AOAccessor _aoDownRate; private AOAccessor _aoTCOpenOffsetOffset; private DIAccessor _diCascadePVSBrk; private DIAccessor _diHeaterPVSBrk; private DIAccessor _diEnableOutput; private DOAccessor _doEnableIn; private DOAccessor _doAutoManual; private DOAccessor _doSelect; private DOAccessor _doCascadeMode; private string formatString; private const int physicalMax = 16000; private ToleranceChecker _toleranceCheckerWarning = new ToleranceChecker(); private ToleranceChecker _toleranceCheckerAlarm = new ToleranceChecker(); //tolerance check private float _alarmJudgmentRange; private float _warningJudgmentRange; private float _alarmJudgmentTime; private float _warningJudgmentTime; private float _toleranceJudgmentDelayTime; private DeviceTimer _toleranceJudgmentDelayTimer = new DeviceTimer(); //stable check private Stopwatch _stableJudgmentTimer = new Stopwatch(); private float _stableJudgmentTime = 1; private float _stableMinValue; private float _stableMaxValue; private SCConfigItem _scEnableCalibration; private SCConfigItem _scCalibrationTable; private SCConfigItem _scRange; private SCConfigItem _scRampRate;//°C/min private List _calibrationTable = new List(); private string _previousSetting; private DeviceTimer _rampTimer = new DeviceTimer(); private double _rampTarget; private double _rampInitValue; private int _rampTime; private DeviceTimer _stableTimer = new DeviceTimer(); private bool _isWarned; private string _uniqueName; private float _tempSetpoint = 0.0f; private string _scRoot; private bool _isStartRamp = false; private bool _isFloatAioType = false; private Dictionary _pidTableAssociate; #region temp correct private float _profileTemp = 0; private float _profileCorrect = 0; private float _profileTCCalib = 0; private float _cascadeTCCorrect = 0; public string CurrentCorrectFileName { get; private set; } public int CurrentCorrectIndex { get; private set; } private string _writeLog = ""; #endregion #region temp profile private float _preheatTime = 0; private float _checkTime = 0; private float _checkLimit = 0; private float _alarmLimit = 0; private float _totalTime = 0; private Stopwatch _profileTimer = new Stopwatch(); private Stopwatch _profileStableTimer = new Stopwatch(); public bool IsProfileMode => _profileTimer != null && _profileTimer.IsRunning; public bool IsProfileSuccess { get; set; } private R_TRIG _profileTotalTimeoutTrig = new R_TRIG(); private R_TRIG _profileAlarmLimitTrig = new R_TRIG(); private R_TRIG _profileSuccessTrig = new R_TRIG(); private bool _isWait; private float _waitHigh; private float _waitLow; private bool _isInit = false; private Stopwatch _initTimer = new Stopwatch(); #endregion #endregion #region properties public string InstallZone { get; set; } public double Range => _scRange.DoubleValue; public AlarmEventItem AlarmToleranceWarning { get; set; } public AlarmEventItem AlarmToleranceAlarm { get; set; } public AlarmEventItem InterlockAlarm { get; set; } public AlarmEventItem HeaterErrorAlarm { get; set; } public AlarmEventItem HeaterErrorRecoveryWarning { get; set; } public AlarmEventItem HeaterStripBreakAlarm { get; set; } public AlarmEventItem HeaterStripBreakWarning { get; set; } public bool IsHeaterStripBreak { get; set; } private RD_TRIG _trigHeaterErrorSignalOn = new RD_TRIG(); public R_TRIG TrigHeaterStripBreakSignalOn = new R_TRIG(); public DeviceTimer HeaterStripBreakTimer = new DeviceTimer(); public string InstallPosition => SC.GetStringValue($"{_scRoot}.Heater.{InstallZone}.{Name}.InstallPosition"); public bool IsError => _diCascadePVSBrk == null || _diHeaterPVSBrk == null ? false : _diCascadePVSBrk.Value || _diHeaterPVSBrk.Value; private R_TRIG _tcBreakTrig = new R_TRIG(); public bool IsStable { get { if (_stableJudgmentTimer.IsRunning) { if (DeviceData.FeedBack < (DeviceData.SetPoint - _stableMinValue) || DeviceData.FeedBack > (DeviceData.SetPoint + _stableMaxValue)) { _stableJudgmentTimer.Restart(); } if (_stableJudgmentTimer.ElapsedMilliseconds >= _stableJudgmentTime * 1000) { return true; } } else { _stableJudgmentTimer.Restart(); } return false; } } public override float TempSetPoint { get { return _tempSetpoint; } set { if (_aoCascadeControlModeSetPoint != null && _aoHeaterControlModeSetPoint != null) { if (_isFloatAioType) { if(ControlMode == 0) { if (Math.Abs(_aoCascadeControlModeSetPoint.FloatValue - value) > 0.0001) _aoCascadeControlModeSetPoint.FloatValue = value; } else { if (Math.Abs(_aoHeaterControlModeSetPoint.FloatValue - value) > 0.0001) _aoHeaterControlModeSetPoint.FloatValue = value; } } } } } public float HeaterPID_P { get { return _aoHeaterPID_P.FloatValue; } set { if (Math.Abs(_aoHeaterPID_P.FloatValue - value) > 0.0001) _aoHeaterPID_P.FloatValue = value; } } public float HeaterPID_I { get { return _aoHeaterPID_I.FloatValue; } set { if (Math.Abs(_aoHeaterPID_I.FloatValue - value) > 0.0001) _aoHeaterPID_I.FloatValue = value; } } public float HeaterPID_D { get { return _aoHeaterPID_D.FloatValue; } set { if (Math.Abs(_aoHeaterPID_D.FloatValue - value) > 0.0001) _aoHeaterPID_D.FloatValue = value; } } public float CascadePID_P { get { return _aoCascadePID_P.FloatValue; } set { if (Math.Abs(_aoCascadePID_P.FloatValue - value) > 0.0001) _aoCascadePID_P.FloatValue = value; } } public float CascadePID_I { get { return _aoCascadePID_I.FloatValue; } set { if (Math.Abs(_aoCascadePID_I.FloatValue - value) > 0.0001) _aoCascadePID_I.FloatValue = value; } } public float CascadePID_D { get { return _aoCascadePID_D.FloatValue; } set { if (Math.Abs(_aoCascadePID_D.FloatValue - value) > 0.0001) _aoCascadePID_D.FloatValue = value; } } public float UpRate { get { return _aoUpRate.FloatValue; } set { if (Math.Abs(_aoUpRate.FloatValue - value) > 0.0001) _aoUpRate.FloatValue = value; } } public float DownRate { get { return _aoDownRate.FloatValue; } set { if (Math.Abs(_aoDownRate.FloatValue - value) > 0.0001) _aoDownRate.FloatValue = value; } } public int ControlMode { get { if (_doSelect != null && _doCascadeMode != null) { if (!_doSelect.Value && !_doCascadeMode.Value) { // Furnace control return 0; } else if (!_doSelect.Value && _doCascadeMode.Value) { // Heater control return 1; } else if (_doSelect.Value && _doCascadeMode.Value) { //Furnace Diect control return 2; } } return 0; } } public float TCOpenOffsetOffset { get { return _aoTCOpenOffsetOffset != null ? _aoTCOpenOffsetOffset.FloatValue : 0; } set { if (_aoTCOpenOffsetOffset != null && Math.Abs(_aoTCOpenOffsetOffset.FloatValue - value) > 0.0001) _aoTCOpenOffsetOffset.FloatValue = value; } } public override float TempFeedback => (float)(ControlMode == 1 ? (_aiHeaterPV == null ? 0 : _aiHeaterPV.FloatValue - _profileTCCalib) : (_aiCascadePV == null ? 0 : _aiCascadePV.FloatValue - _profileTCCalib)); #endregion public override bool Initialize() { DeviceData = new AITHeaterData() { DeviceName = Name, DeviceSchematicId = DeviceID, DisplayName = Display, Module = Module, Scale = SC.GetValue($"{_scRoot}.{Name}.Range"), OverTempScale= SC.GetValue($"{_scRoot}.{Name}.OverTempRange"), //Scale = 1200, Unit = "°C", //SetPoint = TempSetPoint, FeedBack = TempFeedback, }; _pidTableAssociate = new Dictionary() { { "HeaterU",0 }, { "HeaterCU",1 }, { "HeaterC",2 }, { "HeaterCL",3 }, { "HeaterL",4 }, }; _scEnableCalibration = SC.GetConfigItem($"{_scRoot}.{Name}.EnableCalibration"); _scCalibrationTable = SC.GetConfigItem($"{_scRoot}.{Name}.CalibrationTable"); //_scRange = SC.GetConfigItem($"{_scRoot}.{Name}.Range"); _scRampRate = SC.GetConfigItem($"{_scRoot}.{Name}.RampRate"); //AlarmToleranceWarning = SubscribeAlarm($"{_scRoot}.{Name}.ToleranceWarning", "", ResetWarningChecker, EventLevel.Warning); //AlarmToleranceAlarm = SubscribeAlarm($"{_scRoot}.{Name}.ToleranceAlarm", "", ResetAlarmChecker); //AlarmToleranceWarning.Id = SC.ContainsItem($"{_scRoot}.{Name}.ToleranceWarningID") ? SC.GetValue($"{_scRoot}.{Name}.ToleranceWarningID") : 0; //AlarmToleranceAlarm.Id = SC.ContainsItem($"{_scRoot}.{Name}.ToleranceAlarmID") ? SC.GetValue($"{_scRoot}.{Name}.ToleranceAlarmID") : 0; //_stableJudgmentTime = (float)SC.GetValue($"{_scRoot}.{Name}.Stabilize.JudgmentTime"); //_stableMinValue = (float)SC.GetValue($"{_scRoot}.{Name}.Stabilize.MinusValue"); //_stableMaxValue = (float)SC.GetValue($"{_scRoot}.{Name}.Stabilize.PlusValue"); DATA.Subscribe($"{Module}.{Name}.CascadePV", () => (float)DeviceData.CascadePV); DATA.Subscribe($"{Module}.{Name}.HeaterPV", () => (float)DeviceData.HeaterPV); DATA.Subscribe($"{Module}.{Name}.CascadePID_P", () => (float)DeviceData.CascadePID_P); DATA.Subscribe($"{Module}.{Name}.CascadePID_I", () => (float)DeviceData.CascadePID_I); DATA.Subscribe($"{Module}.{Name}.CascadePID_D", () => (float)DeviceData.CascadePID_D); DATA.Subscribe($"{Module}.{Name}.HeaterPID_P", () => (float)DeviceData.HeaterPID_P); DATA.Subscribe($"{Module}.{Name}.HeaterPID_I", () => (float)DeviceData.HeaterPID_I); DATA.Subscribe($"{Module}.{Name}.HeaterPID_D", () => (float)DeviceData.HeaterPID_D); DATA.Subscribe($"{Module}.{Name}.CascadeControlModeSV", () => _aoCascadeControlModeSetPoint != null ? _aoCascadeControlModeSetPoint.FloatValue : 0.0f); DATA.Subscribe($"{Module}.{Name}.HeaterControlModeSV", () => _aoHeaterControlModeSetPoint != null ? _aoHeaterControlModeSetPoint.FloatValue : 0.0f); DATA.Subscribe($"{Module}.{Name}.UpRate", () => UpRate); DATA.Subscribe($"{Module}.{Name}.DownRate", () => DownRate); DATA.Subscribe($"{Module}.{Name}.WorkingOutput", () => _aiWorkingOutput.FloatValue); DATA.Subscribe($"{Module}.{Name}.OverTemp", () => _aiOverTemp.FloatValue); DATA.Subscribe($"{Module}.{Name}.IsCascadePVBreak", () => _diCascadePVSBrk.Value); DATA.Subscribe($"{Module}.{Name}.IsHeaterPVBreak", () => _diHeaterPVSBrk.Value); DATA.Subscribe($"{Module}.{Name}.IsEnableOutput", () => _diEnableOutput.Value); DATA.Subscribe($"{Module}.{Name}.IsEnableIn", () => _doEnableIn.Value); DATA.Subscribe($"{Module}.{Name}.IsAutoManual", () => _doAutoManual.Value); DATA.Subscribe($"{Module}.{Name}.IsSelect", () => _doSelect.Value); DATA.Subscribe($"{Module}.{Name}.IsCascadeMode", () => _doCascadeMode.Value); DATA.Subscribe($"{Module}.{Name}.ControlMode", () => ControlMode); DATA.Subscribe($"{Module}.{Name}.TCOpenOffsetOffset", () => TCOpenOffsetOffset); OP.Subscribe($"{Module}.{Name}.SetRemoteMode", SetRemoteMode); OP.Subscribe($"{Module}.{Name}.SetAutoTuning", SetAutoTuning); OP.Subscribe($"{Module}.{Name}.SetOnOff", SetOnOff); OP.Subscribe($"{Module}.{Name}.SetUpDownRate", (out string reason, int time, object[] param) => { reason = string.Empty; SetUpDownRate(param); return true; }); OP.Subscribe($"{Module}.{Name}.SetManualParameters", (out string reason, int time, object[] param) => { reason = string.Empty; SetManualParameters(param); return true; }); //recipe OP.Subscribe($"PM1.{Name}.SetParameters", (out string reason, int time, object[] param) => { reason = string.Empty; SetParameters(param); return true; }); _doEnableIn.SetValue(true, out _); SetCorrectParameters(SC.GetStringValue("PM1.TempCorrection")); return base.Initialize(); } public override void Monitor() { if(!string.IsNullOrEmpty(_writeLog)) { LOG.Write(_writeLog); _writeLog = ""; } if (!_isInit) { if (!_initTimer.IsRunning) _initTimer.Start(); if (_initTimer.ElapsedMilliseconds > 5 * 1000) { _initTimer.Stop(); _isInit = true; if (_tempSetpoint == 0) { if(ControlMode == 0) { if(_aoCascadeControlModeSetPoint != null && _aoCascadeControlModeSetPoint.FloatValue > 0) { _tempSetpoint = _aoCascadeControlModeSetPoint.FloatValue; } } else { if (_aoCascadeControlModeSetPoint != null && _aoHeaterControlModeSetPoint.FloatValue > 0) { _tempSetpoint = _aoHeaterControlModeSetPoint.FloatValue; } } } SetPIDParameters(SC.GetStringValue("PM1.Heater.PID")); } } if(DeviceData != null) { DeviceData.CascadePID_P = _aoCascadePID_P == null ? 0 : _aoCascadePID_P.FloatValue; DeviceData.CascadePID_I = _aoCascadePID_I == null ? 0 : _aoCascadePID_I.FloatValue; DeviceData.CascadePID_D = _aoCascadePID_D == null ? 0 : _aoCascadePID_D.FloatValue; DeviceData.HeaterPID_P = _aoHeaterPID_P == null ? 0 : _aoHeaterPID_P.FloatValue; DeviceData.HeaterPID_I = _aoHeaterPID_I == null ? 0 : _aoHeaterPID_I.FloatValue; DeviceData.HeaterPID_D = _aoHeaterPID_D == null ? 0 : _aoHeaterPID_D.FloatValue; DeviceData.OverTemp = _aiOverTemp == null ? 0 : _aiOverTemp.FloatValue; DeviceData.CascadePV = _aiCascadePV == null ? 0 : _aiCascadePV.FloatValue; DeviceData.HeaterPV = _aiHeaterPV == null ? 0 : _aiHeaterPV.FloatValue; DeviceData.FeedBack = TempFeedback; DeviceData.SetPoint = _tempSetpoint; DeviceData.ManipulatedVariable = _aiWorkingOutput == null ? 0 : _aiWorkingOutput.FloatValue; DeviceData.RampSetPoint = TempSetPoint;//Ramp的过程值 DeviceData.Ramping = _aoUpRate == null ? 0 : _aoUpRate.FloatValue; DeviceData.ControlMode = ControlMode;// 控制模式 DeviceData.IsAlarm = IsError; DeviceData.UpRateSetPoint = UpRate; DeviceData.DownRateSetPoint = DownRate; DeviceData.EnableOutput = _diEnableOutput.Value; DeviceData.IsCascadePVBreak = _diCascadePVSBrk.Value; DeviceData.IsHeaterPVBreak = _diHeaterPVSBrk.Value; DeviceData.IsProfiling = IsProfileMode; DeviceData.IsOverTempError = DeviceData.OverTemp > DeviceData.OverTempScale; DeviceData.IsTempLmtError = DeviceData.FeedBack > DeviceData.Scale; if (_profileTimer != null && _profileTimer.IsRunning) { DeviceData.ProfileTotalTime = _totalTime - _profileTimer.ElapsedMilliseconds / 1000 > 0 ? _totalTime - _profileTimer.ElapsedMilliseconds / 1000 : 0; DeviceData.ProfilePreheatTime = _preheatTime - _profileTimer.ElapsedMilliseconds / 1000 > 0 ? _preheatTime - _profileTimer.ElapsedMilliseconds / 1000 : 0; } if(_profileStableTimer != null && _profileStableTimer.IsRunning) { DeviceData.ProfileCheckTime = _checkTime - _profileStableTimer.ElapsedMilliseconds / 1000 > 0 ? _checkTime - _profileStableTimer.ElapsedMilliseconds / 1000 : 0; } } MonitorTolerance(); Ramping(); _tcBreakTrig.CLK = IsError; if (_tcBreakTrig.Q) TCOpenOffsetOffset = _profileCorrect; base.Monitor(); } public override void SetTemperature(float temperature) { //if (temperature != SetpointFeedback) { var _rampTime = _scRampRate.DoubleValue != 0 ? (Math.Abs(temperature - TempFeedback) / _scRampRate.DoubleValue) * 60 * 1000 : 0; Ramp(temperature, (int)_rampTime); } } public void SetRamping(float ramping) { SaveRampRate(ramping.ToString("F1")); } private void SetManualParameters(object[] param) { //value:ramp if (param == null || param.Length < 1) { EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature set parameter"); return; } var array = param[0].ToString().Split(';'); if (array == null || array.Length < 2) { EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature set parameter"); return; } float.TryParse(array[0], out float temperature); float.TryParse(array[1], out float rampTime);//°C/min DeviceData.SetPoint = temperature; DeviceData.RampSetPoint = rampTime; var profileCorrect = 0.0f; var profileTCCalib = 0.0f; //var actualSet = temperature - profileCorrect < 0 ? 0 : temperature - profileCorrect; var actualSet = temperature + _profileTCCalib;//加上flat zone的值 UpRate = rampTime; DownRate = rampTime; var controlMode = ""; if (array.Length > 2) { controlMode = array.Length > 2 ? array[2].ToString() : ""; SetControlMode(controlMode); } _tempSetpoint = temperature; TempSetPoint = actualSet; LOG.Write($"{Name} setpoint={temperature} control mode={controlMode}"); //Ramp(actualSet, (int)rampTime); } public void SetControlMode(string mode, string recipeProfileFileName = "") { var reason = ""; DeviceData.ControlModeSetpoint = mode; switch(mode.ToLower()) { case "heater": case "heater control": if(_doSelect.Value) _doSelect.SetValue(false, out reason); if (!_doCascadeMode.Value) _doCascadeMode.SetValue(true, out reason); break; case "furnace": case "furnace control": if (_doSelect.Value) _doSelect.SetValue(false, out reason); if (_doCascadeMode.Value) _doCascadeMode.SetValue(false, out reason); break; case "furnace direct": case "furnace direct control": if (!_doSelect.Value) _doSelect.SetValue(true, out reason); if (!_doCascadeMode.Value) _doCascadeMode.SetValue(true, out reason); break; default: if(mode.ToLower().StartsWith("profile")) { var arry = mode.Replace(")","").Split('('); if(arry != null && arry.Length > 1) { var profileArray = arry[1].Split(','); if(profileArray != null && profileArray.Length > 2) { var profileFileName = profileArray[0]; int.TryParse(profileArray[1], out int profileTableIndex); GetProfileParameters(profileFileName, profileTableIndex, recipeProfileFileName); _profileTimer.Restart(); _profileStableTimer.Stop(); _profileTotalTimeoutTrig.RST = true; _profileAlarmLimitTrig.RST = true; _profileSuccessTrig.RST = true; IsProfileSuccess = false; DeviceData.ProfileResult = 0; LOG.Write($"{Name} profile start preheatTime={_preheatTime} checkTime={_checkTime} totalTime={_totalTime} checkLimit={_checkLimit} alarmLimit={_alarmLimit}"); } } } break; } } private void SetUpDownRate(object[] param) { } public void SetParameters(object[] param) { if (param != null && param.Length > 0) { string zoneName = ""; float temperature = 0.0f; string tempUnit = ""; float ramp = 0.0f; string rampUnit = ""; string controlMode = ""; string correct = ""; string PID = ""; var array = param[0].ToString().Split(';');//zoneName;temp;tempUnit;ramp;rampUnit;checkWait;tempHigh;tempLow;unitStepCompletionCondition;controlMode;correct;PID if (System.Text.RegularExpressions.Regex.Match(array[1].ToString(), @"[a-zA-Z]").Success) { var table = array[1].ToString().Split(':');//AssociateParameterTable if (SC.ContainsItem($"PM1.RecipeEditParameter.TempSetting.{table[0]}.{Name}")) { temperature = (float)SC.GetValue($"PM1.RecipeEditParameter.TempSetting.{table[0]}.{Name}"); } } else { float.TryParse(array[1].ToString(), out temperature); } tempUnit = array.Length > 2 ? array[2].ToString() : ""; if (array.Length > 3) { float.TryParse(array[3], out ramp); } rampUnit = array.Length > 4 ? array[4].ToString() : ""; if (array.Length > 5) { bool.TryParse(array[5], out _isWait); } if (array.Length > 6) { float.TryParse(array[6], out _waitHigh); } if (array.Length > 7) { float.TryParse(array[7], out _waitLow); } var unitStepCompletionCondition = array.Length > 8 ? array[8].ToString() : ""; _stableJudgmentTimer.Stop(); controlMode = array.Length > 9 ? array[9].ToString() : ""; correct = array.Length > 10 ? array[10].ToString() : ""; PID = array.Length > 11 ? array[11].ToString() : ""; var correctileName = array.Length > 12 ? array[12].ToString() : ""; var PIDFileName = array.Length > 13 ? array[13].ToString() : ""; var profileFileName = array.Length > 14 ? array[14].ToString() : ""; var DPR = array.Length > 16 ? (array[15].ToString().ToLower() == "open" ? true : false) : false; var BWR = array.Length > 16 ? (array[15].ToString().ToLower() == "open" ? true : false) : false; SetControlMode(controlMode, profileFileName); SetPIDParameters(PID, PIDFileName); SetCorrectParameters(correct, correctileName, profileFileName); var profileCorrect = 0.0f; var profileTCCalib = 0.0f; //var actualSet = temperature - profileCorrect < 0 ? 0 : temperature - profileCorrect; var actualSet = temperature + _profileTCCalib;//加上flat zone的值 float _rampTime = 0; if(DPR && BWR) { DownRate = 0; UpRate = 0; } else { if (ramp == 0) { DownRate = Math.Abs(TempFeedback - actualSet);//单位是°C/min UpRate = Math.Abs(TempFeedback - actualSet);//单位是°C/min } else if (rampUnit.ToLower() == "time") { DownRate = Math.Abs(TempFeedback - actualSet) / ramp;//单位是°C/min UpRate = Math.Abs(TempFeedback - actualSet) / ramp;//单位是°C/min } else { DownRate = ramp;//单位是°C/min UpRate = ramp;//单位是°C/min } } DeviceData.SetPoint = temperature; DeviceData.RampSetPoint = _rampTime; _tempSetpoint = temperature; TempSetPoint = actualSet; //LOG.Write($"{Name} setpoint={temperature} control mode={controlMode}, PID={PID}, correct={correct}"); _writeLog = $"{Name} setpoint={temperature} control mode={controlMode}, PID={PID}, correct={correct}"; //Ramp(actualSet, (int)_rampTime); } return; } private bool SetRemoteMode(out string reason, int time, params object[] param) { reason = string.Empty; if (param == null || param.Length == 0) { reason = $"invalid parameter"; return false; } bool.TryParse(param[0].ToString(), out bool isRemoteMode); //_doRemoteControl.SetValue(isRemoteMode, out _); return true; } private bool SetOnOff(out string reason, int time, params object[] param) { reason = string.Empty; if (param == null || param.Length == 0) { reason = $"invalid parameter"; return false; } bool.TryParse(param[0].ToString(), out bool isOn); //if (!_doStartHeating.Check(isOn, out reason)) //{ // return false; //} //return _doStartHeating.SetValue(isOn, out reason); return true; } public void SetRun(bool isOn) { //_doStartHeating.SetValue(isOn, out _); EV.PostInfoLog(Name, $"{Name}.SetRun({isOn})"); } public void SetRemote(bool isRemote) { //_doRemoteControl.SetValue(isRemote, out _); } private bool SetAutoTuning(out string reason, int time, params object[] param) { reason = string.Empty; if (param == null || param.Length == 0) { reason = $"invalid parameter"; return false; } bool.TryParse(param[0].ToString(), out bool isAutoSetAutoTuning); if (isAutoSetAutoTuning) { //_doCanWritePara.SetPulseValue(true, 2000); //_doStopAutoTunningMode.SetValue(false, out _); //_doStartAutoTunningMode.SetPulseValue(true, 2000); } else { //_doCanWritePara.SetPulseValue(true, 2000); //_doStartAutoTunningMode.SetValue(false, out _); //_doStopAutoTunningMode.SetPulseValue(true, 2000); } return true; } public override void Terminate() { ProfileFinish(); base.Terminate(); } public void ProfileFinish() { _profileTimer.Stop(); _profileStableTimer.Stop(); _profileTotalTimeoutTrig.RST = true; _profileAlarmLimitTrig.RST = true; _profileSuccessTrig.RST = true; IsProfileSuccess = false; DeviceData.ProfileTable = ""; DeviceData.ProfileTotalTime = 0; DeviceData.ProfilePreheatTime = 0; DeviceData.ProfileCheckTime = 0; DeviceData.ProfileAlarmLimit = 0; DeviceData.ProfileCheckLimit = 0; } public bool SetEnable(bool isEnable) { return _doEnableIn.SetValue(isEnable, out _); } public bool ResetWarningChecker() { _toleranceCheckerWarning.Reset(_warningJudgmentTime); return true; } public bool ResetAlarmChecker() { _toleranceCheckerAlarm.Reset(_alarmJudgmentTime); return true; } public override void Reset() { AlarmToleranceWarning?.Reset(); AlarmToleranceAlarm?.Reset(); _toleranceJudgmentDelayTimer?.Stop(); } public void ResetHeaterError() { _trigHeaterErrorSignalOn.RST = true; if (HeaterErrorAlarm != null) HeaterErrorAlarm.IsAcknowledged = true; } public void ResetHeaterStripBreak() { TrigHeaterStripBreakSignalOn.RST = true; if (HeaterStripBreakAlarm != null) HeaterStripBreakAlarm.IsAcknowledged = true; } private void MonitorTolerance() { if (IsHeaterStripBreak || TempSetPoint < 0.001 || _toleranceJudgmentDelayTimer.IsIdle() || _toleranceJudgmentDelayTimer.GetElapseTime() < _toleranceJudgmentDelayTime * 1000) { _toleranceCheckerWarning.RST = true; _toleranceCheckerAlarm.RST = true; return; } if (_alarmJudgmentRange != 0 && _alarmJudgmentTime > 0) { _toleranceCheckerAlarm.Monitor(DeviceData.FeedBack, DeviceData.RampSetPoint - Math.Abs(_alarmJudgmentRange), DeviceData.RampSetPoint + Math.Abs(_alarmJudgmentRange), _alarmJudgmentTime); } if (_warningJudgmentRange != 0 && _warningJudgmentTime > 0) { _toleranceCheckerWarning.Monitor(DeviceData.FeedBack, DeviceData.RampSetPoint - Math.Abs(_warningJudgmentRange), DeviceData.RampSetPoint + Math.Abs(_warningJudgmentRange), _warningJudgmentTime); } } public override bool CheckToleranceAlarm() { return _toleranceCheckerAlarm.Result; } public override bool CheckToleranceWarning() { return _toleranceCheckerWarning.Result; } public void SetToleranceAlarm() { AlarmToleranceAlarm.Description = $"{Display} temperature out of range {_alarmJudgmentRange} °C in {_alarmJudgmentTime:F0} seconds"; AlarmToleranceAlarm.Set(); } public void SetToleranceWarning() { AlarmToleranceWarning.Description = $"{Display} temperature out of range {_warningJudgmentRange} °C in {_warningJudgmentTime:F0} seconds"; AlarmToleranceWarning.Set(); } public void Ramp(double target, int time) { target = Math.Max(0, target); //target = Math.Min(Range, target); _rampInitValue = TempFeedback; //ramp 初始值取当前设定值,而非实际读取值.零漂问题 _rampTime = time; _rampTarget = target; _rampTimer.Start(_rampTime); _isStartRamp = true; } private void Ramping() { //只有修改了温度,才开始ramp,避免一开机温度设定值大于0的问题。 if (!_isStartRamp) return; if (_rampTimer.IsTimeout() || _rampTime == 0) { TempSetPoint = (float)_rampTarget; } else { TempSetPoint = (float)(_rampInitValue + (_rampTarget - _rampInitValue) * _rampTimer.GetElapseTime() / _rampTime); } } private void SaveRampRate(string ramping) { SC.SetItemValueFromString($"{_scRoot}.{Name}.RampRate", ramping); } public void SetCorrectParameters(string name, string recipeCorrectFileName = null, string recipeProfileFileName = null) { //"Heater;Parameter\\TempCorrection\\tempCorrect,2,Name2;Parameter\\TempPID\\tempPID,3,Name3" //var defaultPID = SC.GetStringValue("PM1.APCPID"); if (string.IsNullOrEmpty(name)) { //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file is empty"); name = SC.GetStringValue("PM1.TempCorrection"); } var array = name.Split(','); if (array == null || array.Length < 2) { //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature correct parameter"); name = SC.GetStringValue("PM1.TempCorrection"); array = name.Split(','); } if (!File.Exists($"{PathManager.GetParameterDir()}\\{array[0]}.rcp")) { name = SC.GetStringValue("PM1.TempCorrection"); var temp = name.Split(','); if (temp != null && array.Length > 0) { array[0] = temp[0]; name = string.Join(",", array); } } if (array == null || array.Length < 2) { return; } var fileNameAndPath = array[0]; int.TryParse(array[1], out int index); var para = fileNameAndPath.Split('\\').ToList().Skip(2);//"Parameter\\TempCorrection" if (para == null) { //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file is empty"); return; } var fileName = string.Join("\\", para.ToArray()); if (!string.IsNullOrEmpty(recipeCorrectFileName)) fileName = recipeCorrectFileName; CurrentCorrectFileName = fileName; CurrentCorrectIndex = index; var content = ParameterFileManager.Instance.LoadParameter("Parameter\\TempCorrection", fileName, false); if (string.IsNullOrEmpty(content)) { //EV.PostWarningLog(ModuleName.PM1.ToString(), $"{fileNameAndPath} heater temperature correct file is empty"); return; } var doc = new XmlDocument(); doc.LoadXml(content); XmlNodeList nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Module[@Name='']/Step"); if (nodeSteps == null) nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Step"); if (nodeSteps == null) { EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature correct file {fileNameAndPath}"); return; } Dictionary dic = new Dictionary(); for(int i= 0;i < nodeSteps.Count;i++) { var step = nodeSteps[i]; XmlElement stepNode = step as XmlElement; var correctTableParameter = new CorrectTableParameter(); correctTableParameter.CorrectParameterLst = new List(); int tableIndex = i + 1; foreach (XmlAttribute att in stepNode.Attributes) { switch (att.Name.ToLower()) { case "index": int.TryParse(att.Value, out int no); correctTableParameter.No = no; break; case "name": correctTableParameter.Name = att.Value; break; case "tableuserangemax": float.TryParse(att.Value, out float tableuserangemax); correctTableParameter.TableUseRangeMax = tableuserangemax; break; case "tableuserangemin": float.TryParse(att.Value, out float tableuserangemin); correctTableParameter.TableUseRangeMin = tableuserangemin; break; case "profileconditiontableno": int.TryParse(att.Value, out int profileconditiontableno); correctTableParameter.ProfileConditionTableNo = profileconditiontableno; break; case "pemppidtableno": int.TryParse(att.Value, out int pemppidtableno); correctTableParameter.TempPIDTableNo = pemppidtableno; break; case "profiletccalibtemp": float.TryParse(att.Value, out float profiletccalibtemp); correctTableParameter.ProfileTCCalibTemp = profiletccalibtemp; break; } if (att.Name.ToLower() == "correctiondata" && !dic.ContainsKey(tableIndex)) { //"Index:1;Name:U;ProfileTemp:0;ProfileCorrect:0;CascadeTCCorrect:0;ProfileTCCalib:0|Index:2;Name:CU;ProfileTemp:0;ProfileCorrect:0;CascadeTCCorrect:0;ProfileTCCalib:0|Index:3;Name:C;ProfileTemp:0;ProfileCorrect:0;CascadeTCCorrect:0;ProfileTCCalib:0|Index:4;Name:CL;ProfileTemp:0;ProfileCorrect:0;CascadeTCCorrect:0;ProfileTCCalib:0|Index:5;Name:L;ProfileTemp:0;ProfileCorrect:0;CascadeTCCorrect:0;ProfileTCCalib:0" var correctionDataString = att.Value; if (string.IsNullOrEmpty(correctionDataString)) { //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file is empty"); return; } var correctionDatas = correctionDataString.Split('|'); if (correctionDatas.Length < 5) { EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file data length is invalid"); return; } foreach (var datas in correctionDatas) { var dataArry = datas.Split(';'); if (dataArry.Length < 6) { EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file data length is invalid"); return; } var correctParameter = new CorrectParameter(); foreach (var item in dataArry) { var itemArry = item.Split(':'); if (itemArry.Length < 2) { EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file data length is invalid"); return; } switch (itemArry[0].ToLower()) { case "index": int.TryParse(itemArry[1], out int no); correctParameter.No = no; break; case "name": correctParameter.Name = itemArry[1]; break; case "profiletemp": float.TryParse(itemArry[1], out float profiletemp); correctParameter.ProfileTemp = profiletemp; break; case "profilecorrect": float.TryParse(itemArry[1], out float profilecorrect); correctParameter.ProfileCorrect = profilecorrect; break; case "cascadetccorrect": float.TryParse(itemArry[1], out float cascadetccorrect); correctParameter.CascadeTCCorrect = cascadetccorrect; break; case "profiletccalib": float.TryParse(itemArry[1], out float profiletccalib); correctParameter.ProfileTCCalib = profiletccalib; break; } } correctTableParameter.CorrectParameterLst.Add(correctParameter); } break; } } dic.Add(tableIndex, correctTableParameter); } if(dic.ContainsKey(index)) { var item = dic[index]; var heaterIndex = GetHeaterIndex() - 1;//U的index是0 if(item.CorrectParameterLst.Count > heaterIndex) { _profileTemp = item.CorrectParameterLst[heaterIndex].ProfileTemp; _profileCorrect = item.CorrectParameterLst[heaterIndex].ProfileCorrect; _profileTCCalib = item.CorrectParameterLst[heaterIndex].ProfileTCCalib; _cascadeTCCorrect = item.CorrectParameterLst[heaterIndex].CascadeTCCorrect; } else { _profileTemp = 0; _profileCorrect = 0; _profileTCCalib = 0; _cascadeTCCorrect = 0; } GetProfileParameters(SC.GetStringValue("PM1.TempProfile"), item.ProfileConditionTableNo, recipeProfileFileName); } else { _profileTemp = 0; _profileCorrect = 0; _profileTCCalib = 0; _cascadeTCCorrect = 0; //auto select } var temperature = DeviceData.SetPoint; var actualSet = temperature + _profileTCCalib; if(temperature > 0) TempSetPoint = (float)actualSet; DeviceData.CorrectTable = name; if (SC.GetStringValue("PM1.TempCorrection") != name) SC.SetItemValueFromString("PM1.TempCorrection", name); } public void SetPIDParameters(string name, string recipePIDFileName = null) { //"Heater;Parameter\\TempCorrection\\tempCorrect,2,Name2;Parameter\\TempPID\\tempPID,3,Name3" //var defaultPID = SC.GetStringValue("PM1.APCPID"); if (string.IsNullOrEmpty(name)) { //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file is empty"); return; } var array = name.Split(','); if (array == null || array.Length < 2) { //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature PID parameter"); return; } if (!File.Exists($"{PathManager.GetParameterDir()}\\{array[0]}.rcp")) return; var fileNameAndPath = array[0]; int.TryParse(array[1], out int index); var para = fileNameAndPath.Split('\\').ToList().Skip(2);//"Parameter\\TempPID" if (para == null) { //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file is empty"); return; } var fileName = string.Join("\\", para.ToArray()); if (!string.IsNullOrEmpty(recipePIDFileName)) fileName = recipePIDFileName; var content = ParameterFileManager.Instance.LoadParameter("Parameter\\TempPID", fileName, false); if (string.IsNullOrEmpty(content)) { //EV.PostWarningLog(ModuleName.PM1.ToString(), $"{fileNameAndPath} heater temperature PID file is empty"); return; } var doc = new XmlDocument(); doc.LoadXml(content); XmlNodeList nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Module[@Name='']/Step"); if (nodeSteps == null) nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Step"); if (nodeSteps == null) { EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature PID file {fileNameAndPath}"); return; } Dictionary> dic = new Dictionary>(); for (int i = 0; i < nodeSteps.Count; i++) { var step = nodeSteps[i]; XmlElement stepNode = step as XmlElement; var pidParameters = new List(); int tableIndex = i + 1; foreach (XmlAttribute att in stepNode.Attributes) { if (att.Name == "PIDData" && !dic.ContainsKey(tableIndex)) { //"Index:1;Name:HT.U;P:11.5;I:12;D:13|Index:2;Name:HTCU;P:21.5;I:22;D:23|Index:3;Name:HT.C;P:31.5;I:32;D:33|Index:4;Name:HTCL;P:41.5;I:42;D:43|Index:5;Name:HT.L;P:51.5;I:52;D:53|Index:6;Name:PR.U;P:0.5;I:10;D:0|Index:7;Name:PRCU;P:0.5;I:10;D:0|Index:8;Name:PR.C;P:0.5;I:10;D:0|Index:9;Name:PRCL;P:0.5;I:10;D:0|Index:10;Name:PR.L;P:0.5;I:10;D:0" var PIDDataString = att.Value; if (string.IsNullOrEmpty(PIDDataString)) { //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file is empty"); return; } var pidDatas = PIDDataString.Split('|'); if (pidDatas.Length < 5) { EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file data length is invalid"); return; } foreach (var datas in pidDatas) { var dataArry = datas.Split(';'); if (dataArry.Length < 5) { EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file data length is invalid"); return; } var pidParameter = new PIDParameter(); foreach (var item in dataArry) { var itemArry = item.Split(':'); if (itemArry.Length < 2) { EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file data length is invalid"); return; } switch (itemArry[0].ToLower()) { case "index": int.TryParse(itemArry[1], out int no); pidParameter.No = no; break; case "name": pidParameter.Name = itemArry[1]; break; case "p": float.TryParse(itemArry[1], out float p); pidParameter.P = p; break; case "i": float.TryParse(itemArry[1], out float ii); pidParameter.I = ii; break; case "d": float.TryParse(itemArry[1], out float d); pidParameter.D = d; break; } } pidParameters.Add(pidParameter); } break; } } dic.Add(tableIndex, pidParameters); } if (dic.ContainsKey(index)) { var item = dic[index]; if (_pidTableAssociate.ContainsKey(Name) && item.Count > _pidTableAssociate[Name] + 5) { HeaterPID_P = item[_pidTableAssociate[Name]].P; HeaterPID_I = item[_pidTableAssociate[Name]].I; HeaterPID_D = item[_pidTableAssociate[Name]].D; CascadePID_P = item[_pidTableAssociate[Name] + 5].P; CascadePID_I = item[_pidTableAssociate[Name] + 5].I; CascadePID_D = item[_pidTableAssociate[Name] + 5].D; } } else { //auto select } if (SC.GetStringValue("PM1.Heater.PID") != name) SC.SetItemValueFromString("PM1.Heater.PID", name); DeviceData.PIDTable = name; } public void GetProfileParameters(string fileNameAndPath, int index, string recipeProfileFileName = null) { //"Heater;Parameter\\TempCorrection\\tempCorrect,2,Name2;Parameter\\TempPID\\tempPID,3,Name3" if (string.IsNullOrEmpty(fileNameAndPath)) { DeviceData.ProfileTable = ""; //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature Profile file is empty"); return; } if (!File.Exists($"{PathManager.GetParameterDir()}\\{fileNameAndPath}.rcp")) return; var para = fileNameAndPath.Split('\\').ToList().Skip(2);//"Parameter\\TempPID" if (para == null) { DeviceData.ProfileTable = ""; //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature Profile file is empty"); return; } var fileName = string.Join("\\", para.ToArray()); if (!string.IsNullOrEmpty(recipeProfileFileName)) fileName = recipeProfileFileName; var content = ParameterFileManager.Instance.LoadParameter("Parameter\\TempProfile", fileName, false); if (string.IsNullOrEmpty(content)) { DeviceData.ProfileTable = ""; //EV.PostWarningLog(ModuleName.PM1.ToString(), $"{fileNameAndPath} heater temperature Profile file is empty"); return; } var doc = new XmlDocument(); doc.LoadXml(content); XmlNodeList nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Module[@Name='']/Step"); if (nodeSteps == null) nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Step"); if (nodeSteps == null) { DeviceData.ProfileTable = ""; EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature Profile file {fileNameAndPath}"); return; } Dictionary dic = new Dictionary(); for (int i = 0; i < nodeSteps.Count; i++) { var step = nodeSteps[i]; XmlElement stepNode = step as XmlElement; var profileParameter = new ProfileParameter(); int tableIndex = i + 1; foreach (XmlAttribute att in stepNode.Attributes) { switch (att.Name.ToLower()) { case "index": int.TryParse(att.Value, out int no); profileParameter.No = no; break; case "name": profileParameter.Name = att.Value; break; case "preheattime": float.TryParse(att.Value, out float preheattime); profileParameter.PreheatTime = preheattime; break; case "checktime": float.TryParse(att.Value, out float checktime); profileParameter.CheckTime = checktime; break; case "totaltime": int.TryParse(att.Value, out int totaltime); profileParameter.TotalTime = totaltime; break; case "alarmlimit": int.TryParse(att.Value, out int alarmlimit); profileParameter.AlarmLimit = alarmlimit; break; case "u": float.TryParse(att.Value, out float u); profileParameter.U = u; break; case "cu": float.TryParse(att.Value, out float cu); profileParameter.CU = cu; break; case "c": float.TryParse(att.Value, out float c); profileParameter.C = c; break; case "cl": float.TryParse(att.Value, out float cl); profileParameter.CL = cl; break; case "l": float.TryParse(att.Value, out float l); profileParameter.L = l; break; } } dic.Add(tableIndex, profileParameter); } if (dic.ContainsKey(index)) { var item = dic[index]; _preheatTime = item.PreheatTime; _checkTime = item.CheckTime; _alarmLimit = item.AlarmLimit; _totalTime = item.TotalTime; DeviceData.ProfileTable = $"{fileNameAndPath},{index},{item.Name}"; switch (Name) { case "HeaterU": _checkLimit = item.U; break; case "HeaterCU": _checkLimit = item.CU; break; case "HeaterC": _checkLimit = item.C; break; case "HeaterCL": _checkLimit = item.CL; break; case "HeaterL": _checkLimit = item.L; break; } DeviceData.ProfileTotalTime = _totalTime; DeviceData.ProfilePreheatTime = _preheatTime; DeviceData.ProfileCheckTime = _checkTime; DeviceData.ProfileAlarmLimit = _alarmLimit; DeviceData.ProfileCheckLimit = _checkLimit; DeviceData.ProfileStatus = "PreHeat"; } else { _checkLimit = 0; _preheatTime = 0; _checkTime = 0; _alarmLimit = 0; _totalTime = 0; DeviceData.ProfileTable = ""; DeviceData.ProfileTotalTime = 0; DeviceData.ProfilePreheatTime = 0; DeviceData.ProfileCheckTime = 0; DeviceData.ProfileAlarmLimit = 0; DeviceData.ProfileCheckLimit = 0; } } public bool CheckProfileFinish(out string reason) { reason = ""; if (_profileTimer != null && _profileTimer.IsRunning) { _profileTotalTimeoutTrig.CLK = _profileTimer.ElapsedMilliseconds >= _totalTime * 1000; if (_profileTotalTimeoutTrig.Q) { LOG.Write($"{Name} profile timeout={_totalTime}"); EV.PostWarningLog(ModuleName.PM1.ToString(), $"{Name} profile timeout={_totalTime}");//超过total time之后,只是报warning提示 } if (_profileTimer.ElapsedMilliseconds >= _preheatTime * 1000)//preheat time之后,才开始判断 { DeviceData.ProfileStatus = "Profile Check"; if (_profileStableTimer != null) { if (!_profileStableTimer.IsRunning) _profileStableTimer.Restart(); if(Math.Abs(TempFeedback - TempSetPoint) > _checkLimit) _profileStableTimer.Restart(); _profileAlarmLimitTrig.CLK = Math.Abs(TempFeedback - TempSetPoint) > _alarmLimit && _alarmLimit > 0; if (_profileAlarmLimitTrig.Q) EV.PostWarningLog(ModuleName.PM1.ToString(), $"{Name} profile success setpoint={TempSetPoint} feedback={TempFeedback}, difference={Math.Abs(TempFeedback - TempSetPoint)} is more than alarm limit={_alarmLimit}"); _profileSuccessTrig.CLK = _profileStableTimer.ElapsedMilliseconds > _checkTime * 1000; if (_profileStableTimer.ElapsedMilliseconds > _checkTime * 1000) { IsProfileSuccess = true; if(_profileSuccessTrig.Q) LOG.Write($"{Name} profile success setpoint={TempSetPoint} feedback={TempFeedback}"); return true; } } } reason = $"{Name} profile not finish"; return false; } else { return true; } } private int GetHeaterIndex() { int.TryParse(Name.Replace("Heater", ""), out int heaterIndex);//改了heater数量这边需要改 switch (Name) { case "HeaterU": heaterIndex = 1; break; case "HeaterCU": heaterIndex = 2; break; case "HeaterC": heaterIndex = 3; break; case "HeaterCL": heaterIndex = 4; break; case "HeaterL": heaterIndex = 5; break; } return heaterIndex; } public bool CheckWaitCondition(out string reason) { reason = ""; if (!_isWait || _waitHigh == 0 || _waitLow == 0) return true; if (_stableJudgmentTimer.IsRunning) { if (TempFeedback < _tempSetpoint - _waitLow || TempFeedback > _tempSetpoint + _waitHigh) { _stableJudgmentTimer.Restart(); } if (_stableJudgmentTimer.ElapsedMilliseconds >= _stableJudgmentTime * 1000) { return true; } } else { _stableJudgmentTimer.Restart(); } reason = $"{Name} feedback={TempFeedback}, wait limit is ({_tempSetpoint - _waitLow}, {_tempSetpoint + _waitHigh})"; return false; } struct PIDParameter { public int No { get; set; } public string Name { get; set; } public float P { get; set; } public float I { get; set; } public float D { get; set; } } struct CorrectTableParameter { public int No { get; set; } public string Name { get; set; } public float ProfileTCCalibTemp { get; set; } public float TableUseRangeMin { get; set; } public float TableUseRangeMax { get; set; } public int ProfileConditionTableNo { get; set; } public int TempPIDTableNo { get; set; } public List CorrectParameterLst{ get; set; } } struct ProfileParameter { public int No { get; set; } public string Name { get; set; } public float PreheatTime { get; set; } public float CheckTime { get; set; } public float TotalTime { get; set; } public float AlarmLimit { get; set; } public float U { get; set; } public float CU { get; set; } public float C { get; set; } public float CL { get; set; } public float L { get; set; } } } public struct CorrectParameter { public int No { get; set; } public string Name { get; set; } public float ProfileTemp { get; set; } public float ProfileTCCalib { get; set; } public float ProfileCorrect { get; set; } public float CascadeTCCorrect { get; set; } } }