IoHeater.cs 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649
  1. using Aitex.Common.Util;
  2. using Aitex.Core.Common.DeviceData;
  3. using Aitex.Core.RT.DataCenter;
  4. using Aitex.Core.RT.Device;
  5. using Aitex.Core.RT.Device.Unit;
  6. using Aitex.Core.RT.Event;
  7. using Aitex.Core.RT.IOCore;
  8. using Aitex.Core.RT.Log;
  9. using Aitex.Core.RT.OperationCenter;
  10. using Aitex.Core.RT.ParameterCenter;
  11. using Aitex.Core.RT.SCCore;
  12. using Aitex.Core.RT.Tolerance;
  13. using Aitex.Core.Util;
  14. using FurnaceRT.Equipments.PMs;
  15. using FurnaceRT.Equipments.Systems;
  16. using MECF.Framework.Common.CommonData;
  17. using MECF.Framework.Common.Device.Bases;
  18. using MECF.Framework.Common.Equipment;
  19. using MECF.Framework.Common.Event;
  20. using System;
  21. using System.Collections.Generic;
  22. using System.Diagnostics;
  23. using System.IO;
  24. using System.Linq;
  25. using System.Text;
  26. using System.Threading.Tasks;
  27. using System.Xml;
  28. namespace FurnaceRT.Devices
  29. {
  30. public class IoHeater : HeaterBase
  31. {
  32. private R_TRIG _trigTrigFurnaceTempabnormal = new R_TRIG();
  33. public IoHeater(string module, XmlElement node, string ioModule = "")
  34. {
  35. base.Module = string.IsNullOrEmpty(node.GetAttribute("module")) ? module : node.GetAttribute("module");
  36. base.Name = node.GetAttribute("id");
  37. base.Display = node.GetAttribute("display");
  38. base.DeviceID = node.GetAttribute("schematicId");
  39. formatString = node.GetAttribute("formatString");
  40. InstallZone = node.GetAttribute("installzone");
  41. _uniqueName = $"{Module}{Name}";
  42. _isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float");
  43. _aiCascadePV = ParseAiNode("aiCascadePV", node, ioModule);
  44. _aiHeaterPV = ParseAiNode("aiHeaterPV", node, ioModule);
  45. _aiWorkingOutput = ParseAiNode("aiWorkingOutput", node, ioModule);
  46. _aiOverTemp = ParseAiNode("aiOverTemp", node, ioModule);
  47. _aoCascadeControlModeSetPoint = ParseAoNode("aoCascadeControlModeSetPoint", node, ioModule);
  48. _aoHeaterControlModeSetPoint = ParseAoNode("aoHeaterControlModeSetPoint", node, ioModule);
  49. _aoCascadePID_P = ParseAoNode("aoCascadePID_P", node, ioModule);
  50. _aoCascadePID_I = ParseAoNode("aoCascadePID_I", node, ioModule);
  51. _aoCascadePID_D = ParseAoNode("aoCascadePID_D", node, ioModule);
  52. _aoHeaterPID_P = ParseAoNode("aoHeaterPID_P", node, ioModule);
  53. _aoHeaterPID_I = ParseAoNode("aoHeaterPID_I", node, ioModule);
  54. _aoHeaterPID_D = ParseAoNode("aoHeaterPID_D", node, ioModule);
  55. _aoUpRate = ParseAoNode("aoUpRate", node, ioModule);
  56. _aoDownRate = ParseAoNode("aoDownRate", node, ioModule);
  57. _aoTCOpenOffsetOffset = ParseAoNode("aoTCOpenOffsetOffset", node, ioModule);
  58. _diCascadePVSBrk = ParseDiNode("diCascadePVSBrk", node, ioModule);
  59. _diHeaterPVSBrk = ParseDiNode("diHeaterPVSBrk", node, ioModule);
  60. _diEnableOutput = ParseDiNode("diEnableOutput", node, ioModule);
  61. _doEnableIn = ParseDoNode("doEnableIn", node, ioModule);
  62. _doAutoManual = ParseDoNode("doAutoManual", node, ioModule);
  63. _doSelect = ParseDoNode("doMainPVSelect", node, ioModule);
  64. _doCascadeMode = ParseDoNode("doCascadeMode", node, ioModule);
  65. _scRoot = node.GetAttribute("scRoot");
  66. }
  67. #region fields
  68. private AIAccessor _aiCascadePV;
  69. private AIAccessor _aiHeaterPV;
  70. private AIAccessor _aiWorkingOutput;
  71. private AIAccessor _aiOverTemp;
  72. private AOAccessor _aoCascadeControlModeSetPoint;
  73. private AOAccessor _aoHeaterControlModeSetPoint;
  74. private AOAccessor _aoCascadePID_P;
  75. private AOAccessor _aoCascadePID_I;
  76. private AOAccessor _aoCascadePID_D;
  77. private AOAccessor _aoHeaterPID_P;
  78. private AOAccessor _aoHeaterPID_I;
  79. private AOAccessor _aoHeaterPID_D;
  80. private AOAccessor _aoUpRate;
  81. private AOAccessor _aoDownRate;
  82. private AOAccessor _aoTCOpenOffsetOffset;
  83. private DIAccessor _diCascadePVSBrk;
  84. private DIAccessor _diHeaterPVSBrk;
  85. private DIAccessor _diEnableOutput;
  86. private DOAccessor _doEnableIn;
  87. private DOAccessor _doAutoManual;
  88. private DOAccessor _doSelect;
  89. private DOAccessor _doCascadeMode;
  90. private string formatString;
  91. private const int physicalMax = 16000;
  92. private ToleranceChecker _toleranceCheckerWarning = new ToleranceChecker();
  93. private ToleranceChecker _toleranceCheckerAlarm = new ToleranceChecker();
  94. //tolerance check
  95. private float _alarmJudgmentRange;
  96. private float _warningJudgmentRange;
  97. private float _alarmJudgmentTime;
  98. private float _warningJudgmentTime;
  99. private float _toleranceJudgmentDelayTime;
  100. private DeviceTimer _toleranceJudgmentDelayTimer = new DeviceTimer();
  101. //stable check
  102. private Stopwatch _stableJudgmentTimer = new Stopwatch();
  103. private float _stableJudgmentTime = 1;
  104. private float _stableMinValue;
  105. private float _stableMaxValue;
  106. private SCConfigItem _scEnableCalibration;
  107. private SCConfigItem _scCalibrationTable;
  108. private SCConfigItem _scRange;
  109. private SCConfigItem _scRampRate;//°C/min
  110. private List<CalibrationItem> _calibrationTable = new List<CalibrationItem>();
  111. private string _previousSetting;
  112. private DeviceTimer _rampTimer = new DeviceTimer();
  113. private double _rampTarget;
  114. private double _rampInitValue;
  115. private int _rampTime;
  116. private DeviceTimer _stableTimer = new DeviceTimer();
  117. private bool _isWarned;
  118. private string _uniqueName;
  119. private float _tempSetpoint = 0.0f;
  120. private string _scRoot;
  121. private bool _isStartRamp = false;
  122. private bool _isFloatAioType = false;
  123. private Dictionary<string, int> _pidTableAssociate;
  124. #region temp correct
  125. private float _profileTemp = 0;
  126. private float _profileCorrect = 0;
  127. private float _profileTCCalib = 0;
  128. private float _cascadeTCCorrect = 0;
  129. public string CurrentCorrectFileName { get; private set; }
  130. public int CurrentCorrectIndex { get; private set; }
  131. private string _writeLog = "";
  132. #endregion
  133. #region temp profile
  134. private float _preheatTime = 0;
  135. private float _checkTime = 0;
  136. private float _checkLimit = 0;
  137. private float _alarmLimit = 0;
  138. private float _totalTime = 0;
  139. private Stopwatch _profileTimer = new Stopwatch();
  140. private Stopwatch _profileStableTimer = new Stopwatch();
  141. public bool IsProfileMode => _profileTimer != null && _profileTimer.IsRunning;
  142. public bool IsProfileSuccess { get; set; }
  143. private R_TRIG _profileTotalTimeoutTrig = new R_TRIG();
  144. private R_TRIG _profileAlarmLimitTrig = new R_TRIG();
  145. private R_TRIG _profileSuccessTrig = new R_TRIG();
  146. private bool _isWait;
  147. private float _waitHigh;
  148. private float _waitLow;
  149. private bool _isInit = false;
  150. private Stopwatch _initTimer = new Stopwatch();
  151. #endregion
  152. #endregion
  153. #region properties
  154. public string InstallZone { get; set; }
  155. public double Range => _scRange.DoubleValue;
  156. public AlarmEventItem AlarmToleranceWarning { get; set; }
  157. public AlarmEventItem AlarmToleranceAlarm { get; set; }
  158. public AlarmEventItem InterlockAlarm { get; set; }
  159. public AlarmEventItem HeaterErrorAlarm { get; set; }
  160. public AlarmEventItem HeaterErrorRecoveryWarning { get; set; }
  161. public AlarmEventItem HeaterStripBreakAlarm { get; set; }
  162. public AlarmEventItem HeaterStripBreakWarning { get; set; }
  163. public bool IsHeaterStripBreak { get; set; }
  164. private RD_TRIG _trigHeaterErrorSignalOn = new RD_TRIG();
  165. public R_TRIG TrigHeaterStripBreakSignalOn = new R_TRIG();
  166. public DeviceTimer HeaterStripBreakTimer = new DeviceTimer();
  167. public string InstallPosition => SC.GetStringValue($"{_scRoot}.Heater.{InstallZone}.{Name}.InstallPosition");
  168. public bool IsError => _diCascadePVSBrk == null || _diHeaterPVSBrk == null ? false : _diCascadePVSBrk.Value || _diHeaterPVSBrk.Value;
  169. private R_TRIG _tcBreakTrig = new R_TRIG();
  170. public bool IsStable
  171. {
  172. get
  173. {
  174. if (_stableJudgmentTimer.IsRunning)
  175. {
  176. if (DeviceData.FeedBack < (DeviceData.SetPoint - _stableMinValue) ||
  177. DeviceData.FeedBack > (DeviceData.SetPoint + _stableMaxValue))
  178. {
  179. _stableJudgmentTimer.Restart();
  180. }
  181. if (_stableJudgmentTimer.ElapsedMilliseconds >= _stableJudgmentTime * 1000)
  182. {
  183. return true;
  184. }
  185. }
  186. else
  187. {
  188. _stableJudgmentTimer.Restart();
  189. }
  190. return false;
  191. }
  192. }
  193. public override float TempSetPoint
  194. {
  195. get
  196. {
  197. return _tempSetpoint;
  198. }
  199. set
  200. {
  201. if (_aoCascadeControlModeSetPoint != null && _aoHeaterControlModeSetPoint != null)
  202. {
  203. if (_isFloatAioType)
  204. {
  205. if (ControlMode == 0)
  206. {
  207. if (Math.Abs(_aoCascadeControlModeSetPoint.FloatValue - value) > 0.0001)
  208. _aoCascadeControlModeSetPoint.FloatValue = value;
  209. }
  210. else
  211. {
  212. if (Math.Abs(_aoHeaterControlModeSetPoint.FloatValue - value) > 0.0001)
  213. _aoHeaterControlModeSetPoint.FloatValue = value;
  214. }
  215. }
  216. }
  217. }
  218. }
  219. public float HeaterPID_P
  220. {
  221. get
  222. {
  223. return _aoHeaterPID_P.FloatValue;
  224. }
  225. set
  226. {
  227. if (Math.Abs(_aoHeaterPID_P.FloatValue - value) > 0.0001)
  228. _aoHeaterPID_P.FloatValue = value;
  229. }
  230. }
  231. public float HeaterPID_I
  232. {
  233. get
  234. {
  235. return _aoHeaterPID_I.FloatValue;
  236. }
  237. set
  238. {
  239. if (Math.Abs(_aoHeaterPID_I.FloatValue - value) > 0.0001)
  240. _aoHeaterPID_I.FloatValue = value;
  241. }
  242. }
  243. public float HeaterPID_D
  244. {
  245. get
  246. {
  247. return _aoHeaterPID_D.FloatValue;
  248. }
  249. set
  250. {
  251. if (Math.Abs(_aoHeaterPID_D.FloatValue - value) > 0.0001)
  252. _aoHeaterPID_D.FloatValue = value;
  253. }
  254. }
  255. public float CascadePID_P
  256. {
  257. get
  258. {
  259. return _aoCascadePID_P.FloatValue;
  260. }
  261. set
  262. {
  263. if (Math.Abs(_aoCascadePID_P.FloatValue - value) > 0.0001)
  264. _aoCascadePID_P.FloatValue = value;
  265. }
  266. }
  267. public float CascadePID_I
  268. {
  269. get
  270. {
  271. return _aoCascadePID_I.FloatValue;
  272. }
  273. set
  274. {
  275. if (Math.Abs(_aoCascadePID_I.FloatValue - value) > 0.0001)
  276. _aoCascadePID_I.FloatValue = value;
  277. }
  278. }
  279. public float CascadePID_D
  280. {
  281. get
  282. {
  283. return _aoCascadePID_D.FloatValue;
  284. }
  285. set
  286. {
  287. if (Math.Abs(_aoCascadePID_D.FloatValue - value) > 0.0001)
  288. _aoCascadePID_D.FloatValue = value;
  289. }
  290. }
  291. public float UpRate
  292. {
  293. get
  294. {
  295. return _aoUpRate.FloatValue;
  296. }
  297. set
  298. {
  299. if (Math.Abs(_aoUpRate.FloatValue - value) > 0.0001)
  300. _aoUpRate.FloatValue = value;
  301. }
  302. }
  303. public float DownRate
  304. {
  305. get
  306. {
  307. return _aoDownRate.FloatValue;
  308. }
  309. set
  310. {
  311. if (Math.Abs(_aoDownRate.FloatValue - value) > 0.0001)
  312. _aoDownRate.FloatValue = value;
  313. }
  314. }
  315. public int ControlMode
  316. {
  317. get
  318. {
  319. if (_doSelect != null && _doCascadeMode != null)
  320. {
  321. if (!_doSelect.Value && !_doCascadeMode.Value)
  322. {
  323. // Furnace control
  324. return 0;
  325. }
  326. else if (!_doSelect.Value && _doCascadeMode.Value)
  327. {
  328. // Heater control
  329. return 1;
  330. }
  331. else if (_doSelect.Value && _doCascadeMode.Value)
  332. {
  333. //Furnace Diect control
  334. return 2;
  335. }
  336. }
  337. return 0;
  338. }
  339. }
  340. public float TCOpenOffsetOffset
  341. {
  342. get
  343. {
  344. return _aoTCOpenOffsetOffset != null ? _aoTCOpenOffsetOffset.FloatValue : 0;
  345. }
  346. set
  347. {
  348. if (_aoTCOpenOffsetOffset != null && Math.Abs(_aoTCOpenOffsetOffset.FloatValue - value) > 0.0001)
  349. _aoTCOpenOffsetOffset.FloatValue = value;
  350. }
  351. }
  352. public override float TempFeedback => (float)(ControlMode == 1 ? (_aiHeaterPV == null ? 0 : _aiHeaterPV.FloatValue - _profileTCCalib) : (_aiCascadePV == null ? 0 : _aiCascadePV.FloatValue - _profileTCCalib));
  353. #endregion
  354. public override bool Initialize()
  355. {
  356. DeviceData = new AITHeaterData()
  357. {
  358. DeviceName = Name,
  359. DeviceSchematicId = DeviceID,
  360. DisplayName = Display,
  361. Module = Module,
  362. Scale = SC.GetValue<double>($"{_scRoot}.{Name}.Range"),
  363. OverTempScale = SC.GetValue<double>($"{_scRoot}.{Name}.OverTempRange"),
  364. //Scale = 1200,
  365. Unit = "°C",
  366. //SetPoint = TempSetPoint,
  367. FeedBack = TempFeedback,
  368. };
  369. _pidTableAssociate = new Dictionary<string, int>()
  370. {
  371. { "HeaterU",0 },
  372. { "HeaterCU",1 },
  373. { "HeaterC",2 },
  374. { "HeaterCL",3 },
  375. { "HeaterL",4 },
  376. };
  377. _scEnableCalibration = SC.GetConfigItem($"{_scRoot}.{Name}.EnableCalibration");
  378. _scCalibrationTable = SC.GetConfigItem($"{_scRoot}.{Name}.CalibrationTable");
  379. //_scRange = SC.GetConfigItem($"{_scRoot}.{Name}.Range");
  380. _scRampRate = SC.GetConfigItem($"{_scRoot}.{Name}.RampRate");
  381. //AlarmToleranceWarning = SubscribeAlarm($"{_scRoot}.{Name}.ToleranceWarning", "", ResetWarningChecker, EventLevel.Warning);
  382. //AlarmToleranceAlarm = SubscribeAlarm($"{_scRoot}.{Name}.ToleranceAlarm", "", ResetAlarmChecker);
  383. //AlarmToleranceWarning.Id = SC.ContainsItem($"{_scRoot}.{Name}.ToleranceWarningID") ? SC.GetValue<int>($"{_scRoot}.{Name}.ToleranceWarningID") : 0;
  384. //AlarmToleranceAlarm.Id = SC.ContainsItem($"{_scRoot}.{Name}.ToleranceAlarmID") ? SC.GetValue<int>($"{_scRoot}.{Name}.ToleranceAlarmID") : 0;
  385. //_stableJudgmentTime = (float)SC.GetValue<double>($"{_scRoot}.{Name}.Stabilize.JudgmentTime");
  386. //_stableMinValue = (float)SC.GetValue<double>($"{_scRoot}.{Name}.Stabilize.MinusValue");
  387. //_stableMaxValue = (float)SC.GetValue<double>($"{_scRoot}.{Name}.Stabilize.PlusValue");
  388. DATA.Subscribe($"{Module}.{Name}.CascadePV", () => (float)DeviceData.CascadePV);
  389. DATA.Subscribe($"{Module}.{Name}.HeaterPV", () => (float)DeviceData.HeaterPV);
  390. DATA.Subscribe($"{Module}.{Name}.CascadePID_P", () => (float)DeviceData.CascadePID_P);
  391. DATA.Subscribe($"{Module}.{Name}.CascadePID_I", () => (float)DeviceData.CascadePID_I);
  392. DATA.Subscribe($"{Module}.{Name}.CascadePID_D", () => (float)DeviceData.CascadePID_D);
  393. DATA.Subscribe($"{Module}.{Name}.HeaterPID_P", () => (float)DeviceData.HeaterPID_P);
  394. DATA.Subscribe($"{Module}.{Name}.HeaterPID_I", () => (float)DeviceData.HeaterPID_I);
  395. DATA.Subscribe($"{Module}.{Name}.HeaterPID_D", () => (float)DeviceData.HeaterPID_D);
  396. DATA.Subscribe($"{Module}.{Name}.CascadeControlModeSV", () => _aoCascadeControlModeSetPoint != null ? _aoCascadeControlModeSetPoint.FloatValue : 0.0f);
  397. DATA.Subscribe($"{Module}.{Name}.HeaterControlModeSV", () => _aoHeaterControlModeSetPoint != null ? _aoHeaterControlModeSetPoint.FloatValue : 0.0f);
  398. DATA.Subscribe($"{Module}.{Name}.UpRate", () => UpRate);
  399. DATA.Subscribe($"{Module}.{Name}.DownRate", () => DownRate);
  400. DATA.Subscribe($"{Module}.{Name}.WorkingOutput", () => _aiWorkingOutput.FloatValue);
  401. DATA.Subscribe($"{Module}.{Name}.OverTemp", () => _aiOverTemp.FloatValue);
  402. DATA.Subscribe($"{Module}.{Name}.IsCascadePVBreak", () => _diCascadePVSBrk.Value);
  403. DATA.Subscribe($"{Module}.{Name}.IsHeaterPVBreak", () => _diHeaterPVSBrk.Value);
  404. DATA.Subscribe($"{Module}.{Name}.IsEnableOutput", () => _diEnableOutput.Value);
  405. DATA.Subscribe($"{Module}.{Name}.IsEnableIn", () => _doEnableIn.Value);
  406. DATA.Subscribe($"{Module}.{Name}.IsAutoManual", () => _doAutoManual.Value);
  407. DATA.Subscribe($"{Module}.{Name}.IsSelect", () => _doSelect.Value);
  408. DATA.Subscribe($"{Module}.{Name}.IsCascadeMode", () => _doCascadeMode.Value);
  409. DATA.Subscribe($"{Module}.{Name}.ControlMode", () => ControlMode);
  410. DATA.Subscribe($"{Module}.{Name}.TCOpenOffsetOffset", () => TCOpenOffsetOffset);
  411. OP.Subscribe($"{Module}.{Name}.SetRemoteMode", SetRemoteMode);
  412. OP.Subscribe($"{Module}.{Name}.SetAutoTuning", SetAutoTuning);
  413. OP.Subscribe($"{Module}.{Name}.SetOnOff", SetOnOff);
  414. OP.Subscribe($"{Module}.{Name}.SetUpDownRate", (out string reason, int time, object[] param) =>
  415. {
  416. reason = string.Empty;
  417. SetUpDownRate(param);
  418. return true;
  419. });
  420. OP.Subscribe($"{Module}.{Name}.SetManualParameters", (out string reason, int time, object[] param) =>
  421. {
  422. reason = string.Empty;
  423. SetManualParameters(param);
  424. return true;
  425. });
  426. //recipe
  427. OP.Subscribe($"PM1.{Name}.SetParameters", (out string reason, int time, object[] param) =>
  428. {
  429. reason = string.Empty;
  430. SetParameters(param);
  431. return true;
  432. });
  433. _doEnableIn.SetValue(true, out _);
  434. var setLastPoint = SC.GetConfigItem($"PM1.Heater.{this.Name}.SetLastPoint");
  435. if (setLastPoint != null)
  436. {
  437. _tempSetpoint = (float)setLastPoint.DoubleValue;
  438. DeviceData.SetPoint = _tempSetpoint;
  439. }
  440. SetCorrectParameters(SC.GetStringValue("PM1.TempCorrection"));
  441. return base.Initialize();
  442. }
  443. public override void Monitor()
  444. {
  445. if (!string.IsNullOrEmpty(_writeLog))
  446. {
  447. LOG.Write(_writeLog);
  448. _writeLog = "";
  449. }
  450. if (!_isInit)
  451. {
  452. if (!_initTimer.IsRunning)
  453. _initTimer.Start();
  454. if (_initTimer.ElapsedMilliseconds > 5 * 1000)
  455. {
  456. _initTimer.Stop();
  457. _isInit = true;
  458. if (_tempSetpoint == 0)
  459. {
  460. if (ControlMode == 0)
  461. {
  462. if (_aoCascadeControlModeSetPoint != null && _aoCascadeControlModeSetPoint.FloatValue > 0)
  463. {
  464. _tempSetpoint = _aoCascadeControlModeSetPoint.FloatValue;
  465. }
  466. }
  467. else
  468. {
  469. if (_aoCascadeControlModeSetPoint != null && _aoHeaterControlModeSetPoint.FloatValue > 0)
  470. {
  471. _tempSetpoint = _aoHeaterControlModeSetPoint.FloatValue;
  472. }
  473. }
  474. }
  475. SetPIDParameters(SC.GetStringValue("PM1.Heater.PID"));
  476. }
  477. }
  478. if (DeviceData != null)
  479. {
  480. DeviceData.CascadePID_P = _aoCascadePID_P == null ? 0 : _aoCascadePID_P.FloatValue;
  481. DeviceData.CascadePID_I = _aoCascadePID_I == null ? 0 : _aoCascadePID_I.FloatValue;
  482. DeviceData.CascadePID_D = _aoCascadePID_D == null ? 0 : _aoCascadePID_D.FloatValue;
  483. DeviceData.HeaterPID_P = _aoHeaterPID_P == null ? 0 : _aoHeaterPID_P.FloatValue;
  484. DeviceData.HeaterPID_I = _aoHeaterPID_I == null ? 0 : _aoHeaterPID_I.FloatValue;
  485. DeviceData.HeaterPID_D = _aoHeaterPID_D == null ? 0 : _aoHeaterPID_D.FloatValue;
  486. DeviceData.OverTemp = _aiOverTemp == null ? 0 : _aiOverTemp.FloatValue;
  487. DeviceData.CascadePV = _aiCascadePV == null ? 0 : _aiCascadePV.FloatValue;
  488. DeviceData.HeaterPV = _aiHeaterPV == null ? 0 : _aiHeaterPV.FloatValue;
  489. DeviceData.FeedBack = TempFeedback;
  490. DeviceData.SetPoint = _tempSetpoint;
  491. DeviceData.ManipulatedVariable = _aiWorkingOutput == null ? 0 : _aiWorkingOutput.FloatValue;
  492. DeviceData.RampSetPoint = TempSetPoint;//Ramp的过程值
  493. DeviceData.Ramping = _aoUpRate == null ? 0 : _aoUpRate.FloatValue;
  494. DeviceData.ControlMode = ControlMode;// 控制模式
  495. DeviceData.IsAlarm = IsError;
  496. DeviceData.UpRateSetPoint = UpRate;
  497. DeviceData.DownRateSetPoint = DownRate;
  498. DeviceData.EnableOutput = _diEnableOutput.Value;
  499. DeviceData.IsCascadePVBreak = _diCascadePVSBrk.Value;
  500. DeviceData.IsHeaterPVBreak = _diHeaterPVSBrk.Value;
  501. DeviceData.IsProfiling = IsProfileMode;
  502. DeviceData.IsOverTempError = DeviceData.OverTemp > DeviceData.OverTempScale;
  503. DeviceData.IsTempLmtError = DeviceData.FeedBack > DeviceData.Scale;
  504. if (_profileTimer != null && _profileTimer.IsRunning)
  505. {
  506. DeviceData.ProfileTotalTime = _totalTime - _profileTimer.ElapsedMilliseconds / 1000 > 0 ? _totalTime - _profileTimer.ElapsedMilliseconds / 1000 : 0;
  507. DeviceData.ProfilePreheatTime = _preheatTime - _profileTimer.ElapsedMilliseconds / 1000 > 0 ? _preheatTime - _profileTimer.ElapsedMilliseconds / 1000 : 0;
  508. }
  509. if (_profileStableTimer != null && _profileStableTimer.IsRunning)
  510. {
  511. DeviceData.ProfileCheckTime = _checkTime - _profileStableTimer.ElapsedMilliseconds / 1000 > 0 ? _checkTime - _profileStableTimer.ElapsedMilliseconds / 1000 : 0;
  512. }
  513. }
  514. MonitorTolerance();
  515. Ramping();
  516. MonitorToOverTemp();
  517. _tcBreakTrig.CLK = IsError;
  518. if (_tcBreakTrig.Q)
  519. TCOpenOffsetOffset = _profileCorrect;
  520. base.Monitor();
  521. }
  522. private void MonitorToOverTemp()
  523. {
  524. // < !--7个温区中的任意一个温区的 aiCascadePV或者aiHeaterPV或者aiOverTemp达到限定值, 关闭使能 >
  525. var heaterLimitData = SC.ContainsItem("System.Heater.") ? SC.GetValue<double>("System.Heater.") : 1000;
  526. _trigTrigFurnaceTempabnormal.CLK = (_aiCascadePV != null && _aiCascadePV.FloatValue >= heaterLimitData) || (_aiHeaterPV != null && _aiHeaterPV.FloatValue >= heaterLimitData) || (_aiOverTemp != null && _aiOverTemp.FloatValue >= heaterLimitData);
  527. if (_trigTrigFurnaceTempabnormal.Q)
  528. {
  529. var pm = Singleton<EquipmentManager>.Instance.Modules[ModuleName.PM1] as PMModule;
  530. pm.FurnaceTempabnormalAlarm.Set($"{Name} trigger FurnaceTempabnormal, Feedback:{TempFeedback} ,Temperature exceeding {heaterLimitData}.");
  531. pm.InitFurnaceZone(false);
  532. }
  533. }
  534. public override void SetTemperature(float temperature)
  535. {
  536. //if (temperature != SetpointFeedback)
  537. {
  538. var _rampTime = _scRampRate.DoubleValue != 0 ? (Math.Abs(temperature - TempFeedback) / _scRampRate.DoubleValue) * 60 * 1000 : 0;
  539. Ramp(temperature, (int)_rampTime);
  540. }
  541. }
  542. public void SetRamping(float ramping)
  543. {
  544. SaveRampRate(ramping.ToString("F1"));
  545. }
  546. private void SetManualParameters(object[] param)
  547. {
  548. SetEnable(true);
  549. //value:ramp
  550. if (param == null || param.Length < 1)
  551. {
  552. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature set parameter");
  553. return;
  554. }
  555. var array = param[0].ToString().Split(';');
  556. if (array == null || array.Length < 2)
  557. {
  558. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature set parameter");
  559. return;
  560. }
  561. float.TryParse(array[0], out float temperature);
  562. float.TryParse(array[1], out float rampTime);//°C/min
  563. DeviceData.SetPoint = temperature;
  564. DeviceData.RampSetPoint = rampTime;
  565. var profileCorrect = 0.0f;
  566. var profileTCCalib = 0.0f;
  567. //var actualSet = temperature - profileCorrect < 0 ? 0 : temperature - profileCorrect;
  568. var actualSet = temperature + _profileTCCalib;//加上flat zone的值
  569. UpRate = rampTime;
  570. DownRate = rampTime;
  571. var controlMode = "";
  572. if (array.Length > 2)
  573. {
  574. controlMode = array.Length > 2 ? array[2].ToString() : "";
  575. SetControlMode(controlMode);
  576. }
  577. _tempSetpoint = temperature;
  578. TempSetPoint = actualSet;
  579. LOG.Write($"{Name} setpoint={temperature} control mode={controlMode}");
  580. //Ramp(actualSet, (int)rampTime);
  581. }
  582. public void SetControlMode(string mode, string recipeProfileFileName = "")
  583. {
  584. var reason = "";
  585. DeviceData.ControlModeSetpoint = mode;
  586. switch (mode.ToLower())
  587. {
  588. case "heater":
  589. case "heater control":
  590. if (_doSelect.Value)
  591. _doSelect.SetValue(false, out reason);
  592. if (!_doCascadeMode.Value)
  593. _doCascadeMode.SetValue(true, out reason);
  594. break;
  595. case "furnace":
  596. case "furnace control":
  597. if (_doSelect.Value)
  598. _doSelect.SetValue(false, out reason);
  599. if (_doCascadeMode.Value)
  600. _doCascadeMode.SetValue(false, out reason);
  601. break;
  602. case "furnace direct":
  603. case "furnace direct control":
  604. if (!_doSelect.Value)
  605. _doSelect.SetValue(true, out reason);
  606. if (!_doCascadeMode.Value)
  607. _doCascadeMode.SetValue(true, out reason);
  608. break;
  609. default:
  610. if (mode.ToLower().StartsWith("profile"))
  611. {
  612. var arry = mode.Replace(")", "").Split('(');
  613. if (arry != null && arry.Length > 1)
  614. {
  615. var profileArray = arry[1].Split(',');
  616. if (profileArray != null && profileArray.Length > 2)
  617. {
  618. var profileFileName = profileArray[0];
  619. int.TryParse(profileArray[1], out int profileTableIndex);
  620. GetProfileParameters(profileFileName, profileTableIndex, recipeProfileFileName);
  621. _profileTimer.Restart();
  622. _profileStableTimer.Stop();
  623. _profileTotalTimeoutTrig.RST = true;
  624. _profileAlarmLimitTrig.RST = true;
  625. _profileSuccessTrig.RST = true;
  626. IsProfileSuccess = false;
  627. DeviceData.ProfileResult = 0;
  628. LOG.Write($"{Name} profile start preheatTime={_preheatTime} checkTime={_checkTime} totalTime={_totalTime} checkLimit={_checkLimit} alarmLimit={_alarmLimit}");
  629. }
  630. }
  631. }
  632. break;
  633. }
  634. }
  635. private void SetUpDownRate(object[] param)
  636. {
  637. }
  638. public void SetParameters(object[] param)
  639. {
  640. SetEnable(true);
  641. if (param != null && param.Length > 0)
  642. {
  643. string zoneName = "";
  644. float temperature = 0.0f;
  645. string tempUnit = "";
  646. float ramp = 0.0f;
  647. string rampUnit = "";
  648. string controlMode = "";
  649. string correct = "";
  650. string PID = "";
  651. var array = param[0].ToString().Split(';');//zoneName;temp;tempUnit;ramp;rampUnit;checkWait;tempHigh;tempLow;unitStepCompletionCondition;controlMode;correct;PID
  652. if (System.Text.RegularExpressions.Regex.Match(array[1].ToString(), @"[a-zA-Z]").Success)
  653. {
  654. var table = array[1].ToString().Split(':');//AssociateParameterTable
  655. if (SC.ContainsItem($"PM1.RecipeEditParameter.TempSetting.{table[0]}.{Name}"))
  656. {
  657. temperature = (float)SC.GetValue<double>($"PM1.RecipeEditParameter.TempSetting.{table[0]}.{Name}");
  658. }
  659. }
  660. else
  661. {
  662. float.TryParse(array[1].ToString(), out temperature);
  663. }
  664. tempUnit = array.Length > 2 ? array[2].ToString() : "";
  665. if (array.Length > 3)
  666. {
  667. float.TryParse(array[3], out ramp);
  668. }
  669. rampUnit = array.Length > 4 ? array[4].ToString() : "";
  670. if (array.Length > 5)
  671. {
  672. bool.TryParse(array[5], out _isWait);
  673. }
  674. if (array.Length > 6)
  675. {
  676. float.TryParse(array[6], out _waitHigh);
  677. }
  678. if (array.Length > 7)
  679. {
  680. float.TryParse(array[7], out _waitLow);
  681. }
  682. var unitStepCompletionCondition = array.Length > 8 ? array[8].ToString() : "";
  683. _stableJudgmentTimer.Stop();
  684. controlMode = array.Length > 9 ? array[9].ToString() : "";
  685. correct = array.Length > 10 ? array[10].ToString() : "";
  686. PID = array.Length > 11 ? array[11].ToString() : "";
  687. var correctileName = array.Length > 12 ? array[12].ToString() : "";
  688. var PIDFileName = array.Length > 13 ? array[13].ToString() : "";
  689. var profileFileName = array.Length > 14 ? array[14].ToString() : "";
  690. var DPR = array.Length > 16 ? (array[15].ToString().ToLower() == "open" ? true : false) : false;
  691. var BWR = array.Length > 16 ? (array[15].ToString().ToLower() == "open" ? true : false) : false;
  692. SetControlMode(controlMode, profileFileName);
  693. SetPIDParameters(PID, PIDFileName);
  694. SetCorrectParameters(correct, correctileName, profileFileName);
  695. var profileCorrect = 0.0f;
  696. var profileTCCalib = 0.0f;
  697. //var actualSet = temperature - profileCorrect < 0 ? 0 : temperature - profileCorrect;
  698. var actualSet = temperature + _profileTCCalib;//加上flat zone的值
  699. float _rampTime = 0;
  700. if (DPR && BWR)
  701. {
  702. DownRate = 0;
  703. UpRate = 0;
  704. }
  705. else
  706. {
  707. if (ramp == 0)
  708. {
  709. DownRate = Math.Abs(TempFeedback - actualSet);//单位是°C/min
  710. UpRate = Math.Abs(TempFeedback - actualSet);//单位是°C/min
  711. }
  712. else if (rampUnit.ToLower() == "time")
  713. {
  714. DownRate = Math.Abs(TempFeedback - actualSet) / ramp;//单位是°C/min
  715. UpRate = Math.Abs(TempFeedback - actualSet) / ramp;//单位是°C/min
  716. }
  717. else
  718. {
  719. DownRate = ramp;//单位是°C/min
  720. UpRate = ramp;//单位是°C/min
  721. }
  722. }
  723. DeviceData.SetPoint = temperature;
  724. DeviceData.RampSetPoint = _rampTime;
  725. if (_tempSetpoint != temperature)
  726. {
  727. SC.SetItemValueFromString($"PM1.Heater.{this.Name}.SetLastPoint", temperature.ToString("F1"));
  728. }
  729. _tempSetpoint = temperature;
  730. TempSetPoint = actualSet;
  731. //LOG.Write($"{Name} setpoint={temperature} control mode={controlMode}, PID={PID}, correct={correct}");
  732. _writeLog = $"{Name} setpoint={temperature} control mode={controlMode}, PID={PID}, correct={correct}";
  733. //Ramp(actualSet, (int)_rampTime);
  734. }
  735. return;
  736. }
  737. private bool SetRemoteMode(out string reason, int time, params object[] param)
  738. {
  739. reason = string.Empty;
  740. if (param == null || param.Length == 0)
  741. {
  742. reason = $"invalid parameter";
  743. return false;
  744. }
  745. bool.TryParse(param[0].ToString(), out bool isRemoteMode);
  746. //_doRemoteControl.SetValue(isRemoteMode, out _);
  747. return true;
  748. }
  749. private bool SetOnOff(out string reason, int time, params object[] param)
  750. {
  751. reason = string.Empty;
  752. if (param == null || param.Length == 0)
  753. {
  754. reason = $"invalid parameter";
  755. return false;
  756. }
  757. bool.TryParse(param[0].ToString(), out bool isOn);
  758. //if (!_doStartHeating.Check(isOn, out reason))
  759. //{
  760. // return false;
  761. //}
  762. //return _doStartHeating.SetValue(isOn, out reason);
  763. return true;
  764. }
  765. public void SetRun(bool isOn)
  766. {
  767. //_doStartHeating.SetValue(isOn, out _);
  768. EV.PostInfoLog(Name, $"{Name}.SetRun({isOn})");
  769. }
  770. public void SetRemote(bool isRemote)
  771. {
  772. //_doRemoteControl.SetValue(isRemote, out _);
  773. }
  774. private bool SetAutoTuning(out string reason, int time, params object[] param)
  775. {
  776. reason = string.Empty;
  777. if (param == null || param.Length == 0)
  778. {
  779. reason = $"invalid parameter";
  780. return false;
  781. }
  782. bool.TryParse(param[0].ToString(), out bool isAutoSetAutoTuning);
  783. if (isAutoSetAutoTuning)
  784. {
  785. //_doCanWritePara.SetPulseValue(true, 2000);
  786. //_doStopAutoTunningMode.SetValue(false, out _);
  787. //_doStartAutoTunningMode.SetPulseValue(true, 2000);
  788. }
  789. else
  790. {
  791. //_doCanWritePara.SetPulseValue(true, 2000);
  792. //_doStartAutoTunningMode.SetValue(false, out _);
  793. //_doStopAutoTunningMode.SetPulseValue(true, 2000);
  794. }
  795. return true;
  796. }
  797. public override void Terminate()
  798. {
  799. ProfileFinish();
  800. base.Terminate();
  801. }
  802. public void ProfileFinish()
  803. {
  804. _profileTimer.Stop();
  805. _profileStableTimer.Stop();
  806. _profileTotalTimeoutTrig.RST = true;
  807. _profileAlarmLimitTrig.RST = true;
  808. _profileSuccessTrig.RST = true;
  809. IsProfileSuccess = false;
  810. DeviceData.ProfileTable = "";
  811. DeviceData.ProfileTotalTime = 0;
  812. DeviceData.ProfilePreheatTime = 0;
  813. DeviceData.ProfileCheckTime = 0;
  814. DeviceData.ProfileAlarmLimit = 0;
  815. DeviceData.ProfileCheckLimit = 0;
  816. }
  817. public bool SetEnable(bool isEnable)
  818. {
  819. return _doEnableIn.SetValue(isEnable, out _);
  820. }
  821. public bool ResetWarningChecker()
  822. {
  823. _toleranceCheckerWarning.Reset(_warningJudgmentTime);
  824. return true;
  825. }
  826. public bool ResetAlarmChecker()
  827. {
  828. _toleranceCheckerAlarm.Reset(_alarmJudgmentTime);
  829. return true;
  830. }
  831. public override void Reset()
  832. {
  833. AlarmToleranceWarning?.Reset();
  834. AlarmToleranceAlarm?.Reset();
  835. _toleranceJudgmentDelayTimer?.Stop();
  836. }
  837. public void ResetHeaterError()
  838. {
  839. _trigHeaterErrorSignalOn.RST = true;
  840. if (HeaterErrorAlarm != null)
  841. HeaterErrorAlarm.IsAcknowledged = true;
  842. }
  843. public void ResetHeaterStripBreak()
  844. {
  845. TrigHeaterStripBreakSignalOn.RST = true;
  846. if (HeaterStripBreakAlarm != null)
  847. HeaterStripBreakAlarm.IsAcknowledged = true;
  848. }
  849. private void MonitorTolerance()
  850. {
  851. if (IsHeaterStripBreak || TempSetPoint < 0.001 || _toleranceJudgmentDelayTimer.IsIdle() || _toleranceJudgmentDelayTimer.GetElapseTime() < _toleranceJudgmentDelayTime * 1000)
  852. {
  853. _toleranceCheckerWarning.RST = true;
  854. _toleranceCheckerAlarm.RST = true;
  855. return;
  856. }
  857. if (_alarmJudgmentRange != 0 && _alarmJudgmentTime > 0)
  858. {
  859. _toleranceCheckerAlarm.Monitor(DeviceData.FeedBack, DeviceData.RampSetPoint - Math.Abs(_alarmJudgmentRange), DeviceData.RampSetPoint + Math.Abs(_alarmJudgmentRange), _alarmJudgmentTime);
  860. }
  861. if (_warningJudgmentRange != 0 && _warningJudgmentTime > 0)
  862. {
  863. _toleranceCheckerWarning.Monitor(DeviceData.FeedBack, DeviceData.RampSetPoint - Math.Abs(_warningJudgmentRange), DeviceData.RampSetPoint + Math.Abs(_warningJudgmentRange), _warningJudgmentTime);
  864. }
  865. }
  866. public override bool CheckToleranceAlarm()
  867. {
  868. return _toleranceCheckerAlarm.Result;
  869. }
  870. public override bool CheckToleranceWarning()
  871. {
  872. return _toleranceCheckerWarning.Result;
  873. }
  874. public void SetToleranceAlarm()
  875. {
  876. AlarmToleranceAlarm.Description = $"{Display} temperature out of range {_alarmJudgmentRange} °C in {_alarmJudgmentTime:F0} seconds";
  877. AlarmToleranceAlarm.Set();
  878. }
  879. public void SetToleranceWarning()
  880. {
  881. AlarmToleranceWarning.Description = $"{Display} temperature out of range {_warningJudgmentRange} °C in {_warningJudgmentTime:F0} seconds";
  882. AlarmToleranceWarning.Set();
  883. }
  884. public void Ramp(double target, int time)
  885. {
  886. target = Math.Max(0, target);
  887. //target = Math.Min(Range, target);
  888. _rampInitValue = TempFeedback; //ramp 初始值取当前设定值,而非实际读取值.零漂问题
  889. _rampTime = time;
  890. _rampTarget = target;
  891. _rampTimer.Start(_rampTime);
  892. _isStartRamp = true;
  893. }
  894. private void Ramping()
  895. {
  896. //只有修改了温度,才开始ramp,避免一开机温度设定值大于0的问题。
  897. if (!_isStartRamp)
  898. return;
  899. if (_rampTimer.IsTimeout() || _rampTime == 0)
  900. {
  901. TempSetPoint = (float)_rampTarget;
  902. }
  903. else
  904. {
  905. TempSetPoint = (float)(_rampInitValue + (_rampTarget - _rampInitValue) * _rampTimer.GetElapseTime() / _rampTime);
  906. }
  907. }
  908. private void SaveRampRate(string ramping)
  909. {
  910. SC.SetItemValueFromString($"{_scRoot}.{Name}.RampRate", ramping);
  911. }
  912. public void SetCorrectParameters(string name, string recipeCorrectFileName = null, string recipeProfileFileName = null)
  913. {
  914. //"Heater;Parameter\\TempCorrection\\tempCorrect,2,Name2;Parameter\\TempPID\\tempPID,3,Name3"
  915. //var defaultPID = SC.GetStringValue("PM1.APCPID");
  916. if (string.IsNullOrEmpty(name))
  917. {
  918. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file is empty");
  919. name = SC.GetStringValue("PM1.TempCorrection");
  920. }
  921. var array = name.Split(',');
  922. if (array == null || array.Length < 2)
  923. {
  924. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature correct parameter");
  925. name = SC.GetStringValue("PM1.TempCorrection");
  926. array = name.Split(',');
  927. }
  928. if (!File.Exists($"{PathManager.GetParameterDir()}\\{array[0]}.rcp"))
  929. {
  930. name = SC.GetStringValue("PM1.TempCorrection");
  931. var temp = name.Split(',');
  932. if (temp != null && array.Length > 0)
  933. {
  934. array[0] = temp[0];
  935. name = string.Join(",", array);
  936. }
  937. }
  938. if (array == null || array.Length < 2)
  939. {
  940. return;
  941. }
  942. var fileNameAndPath = array[0];
  943. int.TryParse(array[1], out int index);
  944. var para = fileNameAndPath.Split('\\').ToList().Skip(2);//"Parameter\\TempCorrection"
  945. if (para == null)
  946. {
  947. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file is empty");
  948. return;
  949. }
  950. var fileName = string.Join("\\", para.ToArray());
  951. if (!string.IsNullOrEmpty(recipeCorrectFileName))
  952. fileName = recipeCorrectFileName;
  953. CurrentCorrectFileName = fileName;
  954. CurrentCorrectIndex = index;
  955. var content = ParameterFileManager.Instance.LoadParameter("Parameter\\TempCorrection", fileName, false);
  956. if (string.IsNullOrEmpty(content))
  957. {
  958. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"{fileNameAndPath} heater temperature correct file is empty");
  959. return;
  960. }
  961. var doc = new XmlDocument();
  962. doc.LoadXml(content);
  963. XmlNodeList nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Module[@Name='']/Step");
  964. if (nodeSteps == null)
  965. nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Step");
  966. if (nodeSteps == null)
  967. {
  968. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature correct file {fileNameAndPath}");
  969. return;
  970. }
  971. Dictionary<int, CorrectTableParameter> dic = new Dictionary<int, CorrectTableParameter>();
  972. for (int i = 0; i < nodeSteps.Count; i++)
  973. {
  974. var step = nodeSteps[i];
  975. XmlElement stepNode = step as XmlElement;
  976. var correctTableParameter = new CorrectTableParameter();
  977. correctTableParameter.CorrectParameterLst = new List<CorrectParameter>();
  978. int tableIndex = i + 1;
  979. foreach (XmlAttribute att in stepNode.Attributes)
  980. {
  981. switch (att.Name.ToLower())
  982. {
  983. case "index":
  984. int.TryParse(att.Value, out int no);
  985. correctTableParameter.No = no;
  986. break;
  987. case "name":
  988. correctTableParameter.Name = att.Value;
  989. break;
  990. case "tableuserangemax":
  991. float.TryParse(att.Value, out float tableuserangemax);
  992. correctTableParameter.TableUseRangeMax = tableuserangemax;
  993. break;
  994. case "tableuserangemin":
  995. float.TryParse(att.Value, out float tableuserangemin);
  996. correctTableParameter.TableUseRangeMin = tableuserangemin;
  997. break;
  998. case "profileconditiontableno":
  999. int.TryParse(att.Value, out int profileconditiontableno);
  1000. correctTableParameter.ProfileConditionTableNo = profileconditiontableno;
  1001. break;
  1002. case "pemppidtableno":
  1003. int.TryParse(att.Value, out int pemppidtableno);
  1004. correctTableParameter.TempPIDTableNo = pemppidtableno;
  1005. break;
  1006. case "profiletccalibtemp":
  1007. float.TryParse(att.Value, out float profiletccalibtemp);
  1008. correctTableParameter.ProfileTCCalibTemp = profiletccalibtemp;
  1009. break;
  1010. }
  1011. if (att.Name.ToLower() == "correctiondata" && !dic.ContainsKey(tableIndex))
  1012. {
  1013. //"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"
  1014. var correctionDataString = att.Value;
  1015. if (string.IsNullOrEmpty(correctionDataString))
  1016. {
  1017. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file is empty");
  1018. return;
  1019. }
  1020. var correctionDatas = correctionDataString.Split('|');
  1021. if (correctionDatas.Length < 5)
  1022. {
  1023. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file data length is invalid");
  1024. return;
  1025. }
  1026. foreach (var datas in correctionDatas)
  1027. {
  1028. var dataArry = datas.Split(';');
  1029. if (dataArry.Length < 6)
  1030. {
  1031. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file data length is invalid");
  1032. return;
  1033. }
  1034. var correctParameter = new CorrectParameter();
  1035. foreach (var item in dataArry)
  1036. {
  1037. var itemArry = item.Split(':');
  1038. if (itemArry.Length < 2)
  1039. {
  1040. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature correct file data length is invalid");
  1041. return;
  1042. }
  1043. switch (itemArry[0].ToLower())
  1044. {
  1045. case "index":
  1046. int.TryParse(itemArry[1], out int no);
  1047. correctParameter.No = no;
  1048. break;
  1049. case "name":
  1050. correctParameter.Name = itemArry[1];
  1051. break;
  1052. case "profiletemp":
  1053. float.TryParse(itemArry[1], out float profiletemp);
  1054. correctParameter.ProfileTemp = profiletemp;
  1055. break;
  1056. case "profilecorrect":
  1057. float.TryParse(itemArry[1], out float profilecorrect);
  1058. correctParameter.ProfileCorrect = profilecorrect;
  1059. break;
  1060. case "cascadetccorrect":
  1061. float.TryParse(itemArry[1], out float cascadetccorrect);
  1062. correctParameter.CascadeTCCorrect = cascadetccorrect;
  1063. break;
  1064. case "profiletccalib":
  1065. float.TryParse(itemArry[1], out float profiletccalib);
  1066. correctParameter.ProfileTCCalib = profiletccalib;
  1067. break;
  1068. }
  1069. }
  1070. correctTableParameter.CorrectParameterLst.Add(correctParameter);
  1071. }
  1072. break;
  1073. }
  1074. }
  1075. dic.Add(tableIndex, correctTableParameter);
  1076. }
  1077. if (dic.ContainsKey(index))
  1078. {
  1079. var item = dic[index];
  1080. var heaterIndex = GetHeaterIndex() - 1;//U的index是0
  1081. if (item.CorrectParameterLst.Count > heaterIndex)
  1082. {
  1083. _profileTemp = item.CorrectParameterLst[heaterIndex].ProfileTemp;
  1084. _profileCorrect = item.CorrectParameterLst[heaterIndex].ProfileCorrect;
  1085. _profileTCCalib = item.CorrectParameterLst[heaterIndex].ProfileTCCalib;
  1086. _cascadeTCCorrect = item.CorrectParameterLst[heaterIndex].CascadeTCCorrect;
  1087. }
  1088. else
  1089. {
  1090. _profileTemp = 0;
  1091. _profileCorrect = 0;
  1092. _profileTCCalib = 0;
  1093. _cascadeTCCorrect = 0;
  1094. }
  1095. GetProfileParameters(SC.GetStringValue("PM1.TempProfile"), item.ProfileConditionTableNo, recipeProfileFileName);
  1096. }
  1097. else
  1098. {
  1099. _profileTemp = 0;
  1100. _profileCorrect = 0;
  1101. _profileTCCalib = 0;
  1102. _cascadeTCCorrect = 0;
  1103. //auto select
  1104. }
  1105. var temperature = DeviceData.SetPoint;
  1106. var actualSet = temperature + _profileTCCalib;
  1107. if (temperature > 0)
  1108. TempSetPoint = (float)actualSet;
  1109. DeviceData.CorrectTable = name;
  1110. if (SC.GetStringValue("PM1.TempCorrection") != name)
  1111. SC.SetItemValueFromString("PM1.TempCorrection", name);
  1112. }
  1113. public void SetPIDParameters(string name, string recipePIDFileName = null)
  1114. {
  1115. //"Heater;Parameter\\TempCorrection\\tempCorrect,2,Name2;Parameter\\TempPID\\tempPID,3,Name3"
  1116. //var defaultPID = SC.GetStringValue("PM1.APCPID");
  1117. if (string.IsNullOrEmpty(name))
  1118. {
  1119. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file is empty");
  1120. return;
  1121. }
  1122. var array = name.Split(',');
  1123. if (array == null || array.Length < 2)
  1124. {
  1125. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature PID parameter");
  1126. return;
  1127. }
  1128. if (!File.Exists($"{PathManager.GetParameterDir()}\\{array[0]}.rcp"))
  1129. return;
  1130. var fileNameAndPath = array[0];
  1131. int.TryParse(array[1], out int index);
  1132. var para = fileNameAndPath.Split('\\').ToList().Skip(2);//"Parameter\\TempPID"
  1133. if (para == null)
  1134. {
  1135. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file is empty");
  1136. return;
  1137. }
  1138. var fileName = string.Join("\\", para.ToArray());
  1139. if (!string.IsNullOrEmpty(recipePIDFileName))
  1140. fileName = recipePIDFileName;
  1141. var content = ParameterFileManager.Instance.LoadParameter("Parameter\\TempPID", fileName, false);
  1142. if (string.IsNullOrEmpty(content))
  1143. {
  1144. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"{fileNameAndPath} heater temperature PID file is empty");
  1145. return;
  1146. }
  1147. var doc = new XmlDocument();
  1148. doc.LoadXml(content);
  1149. XmlNodeList nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Module[@Name='']/Step");
  1150. if (nodeSteps == null)
  1151. nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Step");
  1152. if (nodeSteps == null)
  1153. {
  1154. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature PID file {fileNameAndPath}");
  1155. return;
  1156. }
  1157. Dictionary<int, List<PIDParameter>> dic = new Dictionary<int, List<PIDParameter>>();
  1158. for (int i = 0; i < nodeSteps.Count; i++)
  1159. {
  1160. var step = nodeSteps[i];
  1161. XmlElement stepNode = step as XmlElement;
  1162. var pidParameters = new List<PIDParameter>();
  1163. int tableIndex = i + 1;
  1164. foreach (XmlAttribute att in stepNode.Attributes)
  1165. {
  1166. if (att.Name == "PIDData" && !dic.ContainsKey(tableIndex))
  1167. {
  1168. //"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"
  1169. var PIDDataString = att.Value;
  1170. if (string.IsNullOrEmpty(PIDDataString))
  1171. {
  1172. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file is empty");
  1173. return;
  1174. }
  1175. var pidDatas = PIDDataString.Split('|');
  1176. if (pidDatas.Length < 5)
  1177. {
  1178. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file data length is invalid");
  1179. return;
  1180. }
  1181. foreach (var datas in pidDatas)
  1182. {
  1183. var dataArry = datas.Split(';');
  1184. if (dataArry.Length < 5)
  1185. {
  1186. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file data length is invalid");
  1187. return;
  1188. }
  1189. var pidParameter = new PIDParameter();
  1190. foreach (var item in dataArry)
  1191. {
  1192. var itemArry = item.Split(':');
  1193. if (itemArry.Length < 2)
  1194. {
  1195. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature PID file data length is invalid");
  1196. return;
  1197. }
  1198. switch (itemArry[0].ToLower())
  1199. {
  1200. case "index":
  1201. int.TryParse(itemArry[1], out int no);
  1202. pidParameter.No = no;
  1203. break;
  1204. case "name":
  1205. pidParameter.Name = itemArry[1];
  1206. break;
  1207. case "p":
  1208. float.TryParse(itemArry[1], out float p);
  1209. pidParameter.P = p;
  1210. break;
  1211. case "i":
  1212. float.TryParse(itemArry[1], out float ii);
  1213. pidParameter.I = ii;
  1214. break;
  1215. case "d":
  1216. float.TryParse(itemArry[1], out float d);
  1217. pidParameter.D = d;
  1218. break;
  1219. }
  1220. }
  1221. pidParameters.Add(pidParameter);
  1222. }
  1223. break;
  1224. }
  1225. }
  1226. dic.Add(tableIndex, pidParameters);
  1227. }
  1228. if (dic.ContainsKey(index))
  1229. {
  1230. var item = dic[index];
  1231. if (_pidTableAssociate.ContainsKey(Name) && item.Count > _pidTableAssociate[Name] + 5)
  1232. {
  1233. HeaterPID_P = item[_pidTableAssociate[Name]].P;
  1234. HeaterPID_I = item[_pidTableAssociate[Name]].I;
  1235. HeaterPID_D = item[_pidTableAssociate[Name]].D;
  1236. CascadePID_P = item[_pidTableAssociate[Name] + 5].P;
  1237. CascadePID_I = item[_pidTableAssociate[Name] + 5].I;
  1238. CascadePID_D = item[_pidTableAssociate[Name] + 5].D;
  1239. }
  1240. }
  1241. else
  1242. {
  1243. //auto select
  1244. }
  1245. if (SC.GetStringValue("PM1.Heater.PID") != name)
  1246. SC.SetItemValueFromString("PM1.Heater.PID", name);
  1247. DeviceData.PIDTable = name;
  1248. }
  1249. public void GetProfileParameters(string fileNameAndPath, int index, string recipeProfileFileName = null)
  1250. {
  1251. //"Heater;Parameter\\TempCorrection\\tempCorrect,2,Name2;Parameter\\TempPID\\tempPID,3,Name3"
  1252. if (string.IsNullOrEmpty(fileNameAndPath))
  1253. {
  1254. DeviceData.ProfileTable = "";
  1255. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature Profile file is empty");
  1256. return;
  1257. }
  1258. if (!File.Exists($"{PathManager.GetParameterDir()}\\{fileNameAndPath}.rcp"))
  1259. return;
  1260. var para = fileNameAndPath.Split('\\').ToList().Skip(2);//"Parameter\\TempPID"
  1261. if (para == null)
  1262. {
  1263. DeviceData.ProfileTable = "";
  1264. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"Heater temperature Profile file is empty");
  1265. return;
  1266. }
  1267. var fileName = string.Join("\\", para.ToArray());
  1268. if (!string.IsNullOrEmpty(recipeProfileFileName))
  1269. fileName = recipeProfileFileName;
  1270. var content = ParameterFileManager.Instance.LoadParameter("Parameter\\TempProfile", fileName, false);
  1271. if (string.IsNullOrEmpty(content))
  1272. {
  1273. DeviceData.ProfileTable = "";
  1274. //EV.PostWarningLog(ModuleName.PM1.ToString(), $"{fileNameAndPath} heater temperature Profile file is empty");
  1275. return;
  1276. }
  1277. var doc = new XmlDocument();
  1278. doc.LoadXml(content);
  1279. XmlNodeList nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Module[@Name='']/Step");
  1280. if (nodeSteps == null)
  1281. nodeSteps = doc.SelectNodes($"Aitex/TableParameterData/Step");
  1282. if (nodeSteps == null)
  1283. {
  1284. DeviceData.ProfileTable = "";
  1285. EV.PostWarningLog(ModuleName.PM1.ToString(), $"Invalid heater temperature Profile file {fileNameAndPath}");
  1286. return;
  1287. }
  1288. Dictionary<int, ProfileParameter> dic = new Dictionary<int, ProfileParameter>();
  1289. for (int i = 0; i < nodeSteps.Count; i++)
  1290. {
  1291. var step = nodeSteps[i];
  1292. XmlElement stepNode = step as XmlElement;
  1293. var profileParameter = new ProfileParameter();
  1294. int tableIndex = i + 1;
  1295. foreach (XmlAttribute att in stepNode.Attributes)
  1296. {
  1297. switch (att.Name.ToLower())
  1298. {
  1299. case "index":
  1300. int.TryParse(att.Value, out int no);
  1301. profileParameter.No = no;
  1302. break;
  1303. case "name":
  1304. profileParameter.Name = att.Value;
  1305. break;
  1306. case "preheattime":
  1307. float.TryParse(att.Value, out float preheattime);
  1308. profileParameter.PreheatTime = preheattime;
  1309. break;
  1310. case "checktime":
  1311. float.TryParse(att.Value, out float checktime);
  1312. profileParameter.CheckTime = checktime;
  1313. break;
  1314. case "totaltime":
  1315. int.TryParse(att.Value, out int totaltime);
  1316. profileParameter.TotalTime = totaltime;
  1317. break;
  1318. case "alarmlimit":
  1319. int.TryParse(att.Value, out int alarmlimit);
  1320. profileParameter.AlarmLimit = alarmlimit;
  1321. break;
  1322. case "u":
  1323. float.TryParse(att.Value, out float u);
  1324. profileParameter.U = u;
  1325. break;
  1326. case "cu":
  1327. float.TryParse(att.Value, out float cu);
  1328. profileParameter.CU = cu;
  1329. break;
  1330. case "c":
  1331. float.TryParse(att.Value, out float c);
  1332. profileParameter.C = c;
  1333. break;
  1334. case "cl":
  1335. float.TryParse(att.Value, out float cl);
  1336. profileParameter.CL = cl;
  1337. break;
  1338. case "l":
  1339. float.TryParse(att.Value, out float l);
  1340. profileParameter.L = l;
  1341. break;
  1342. }
  1343. }
  1344. dic.Add(tableIndex, profileParameter);
  1345. }
  1346. if (dic.ContainsKey(index))
  1347. {
  1348. var item = dic[index];
  1349. _preheatTime = item.PreheatTime;
  1350. _checkTime = item.CheckTime;
  1351. _alarmLimit = item.AlarmLimit;
  1352. _totalTime = item.TotalTime;
  1353. DeviceData.ProfileTable = $"{fileNameAndPath},{index},{item.Name}";
  1354. switch (Name)
  1355. {
  1356. case "HeaterU":
  1357. _checkLimit = item.U;
  1358. break;
  1359. case "HeaterCU":
  1360. _checkLimit = item.CU;
  1361. break;
  1362. case "HeaterC":
  1363. _checkLimit = item.C;
  1364. break;
  1365. case "HeaterCL":
  1366. _checkLimit = item.CL;
  1367. break;
  1368. case "HeaterL":
  1369. _checkLimit = item.L;
  1370. break;
  1371. }
  1372. DeviceData.ProfileTotalTime = _totalTime;
  1373. DeviceData.ProfilePreheatTime = _preheatTime;
  1374. DeviceData.ProfileCheckTime = _checkTime;
  1375. DeviceData.ProfileAlarmLimit = _alarmLimit;
  1376. DeviceData.ProfileCheckLimit = _checkLimit;
  1377. DeviceData.ProfileStatus = "PreHeat";
  1378. }
  1379. else
  1380. {
  1381. _checkLimit = 0;
  1382. _preheatTime = 0;
  1383. _checkTime = 0;
  1384. _alarmLimit = 0;
  1385. _totalTime = 0;
  1386. DeviceData.ProfileTable = "";
  1387. DeviceData.ProfileTotalTime = 0;
  1388. DeviceData.ProfilePreheatTime = 0;
  1389. DeviceData.ProfileCheckTime = 0;
  1390. DeviceData.ProfileAlarmLimit = 0;
  1391. DeviceData.ProfileCheckLimit = 0;
  1392. }
  1393. }
  1394. public bool CheckProfileFinish(out string reason)
  1395. {
  1396. reason = "";
  1397. if (_profileTimer != null && _profileTimer.IsRunning)
  1398. {
  1399. _profileTotalTimeoutTrig.CLK = _profileTimer.ElapsedMilliseconds >= _totalTime * 1000;
  1400. if (_profileTotalTimeoutTrig.Q)
  1401. {
  1402. LOG.Write($"{Name} profile timeout={_totalTime}");
  1403. EV.PostWarningLog(ModuleName.PM1.ToString(), $"{Name} profile timeout={_totalTime}");//超过total time之后,只是报warning提示
  1404. }
  1405. if (_profileTimer.ElapsedMilliseconds >= _preheatTime * 1000)//preheat time之后,才开始判断
  1406. {
  1407. DeviceData.ProfileStatus = "Profile Check";
  1408. if (_profileStableTimer != null)
  1409. {
  1410. if (!_profileStableTimer.IsRunning)
  1411. _profileStableTimer.Restart();
  1412. if (Math.Abs(TempFeedback - TempSetPoint) > _checkLimit)
  1413. _profileStableTimer.Restart();
  1414. _profileAlarmLimitTrig.CLK = Math.Abs(TempFeedback - TempSetPoint) > _alarmLimit && _alarmLimit > 0;
  1415. if (_profileAlarmLimitTrig.Q)
  1416. EV.PostWarningLog(ModuleName.PM1.ToString(), $"{Name} profile success setpoint={TempSetPoint} feedback={TempFeedback}, difference={Math.Abs(TempFeedback - TempSetPoint)} is more than alarm limit={_alarmLimit}");
  1417. _profileSuccessTrig.CLK = _profileStableTimer.ElapsedMilliseconds > _checkTime * 1000;
  1418. if (_profileStableTimer.ElapsedMilliseconds > _checkTime * 1000)
  1419. {
  1420. IsProfileSuccess = true;
  1421. if (_profileSuccessTrig.Q)
  1422. LOG.Write($"{Name} profile success setpoint={TempSetPoint} feedback={TempFeedback}");
  1423. return true;
  1424. }
  1425. }
  1426. }
  1427. reason = $"{Name} profile not finish";
  1428. return false;
  1429. }
  1430. else
  1431. {
  1432. return true;
  1433. }
  1434. }
  1435. private int GetHeaterIndex()
  1436. {
  1437. int.TryParse(Name.Replace("Heater", ""), out int heaterIndex);//改了heater数量这边需要改
  1438. switch (Name)
  1439. {
  1440. case "HeaterU":
  1441. heaterIndex = 1;
  1442. break;
  1443. case "HeaterCU":
  1444. heaterIndex = 2;
  1445. break;
  1446. case "HeaterC":
  1447. heaterIndex = 3;
  1448. break;
  1449. case "HeaterCL":
  1450. heaterIndex = 4;
  1451. break;
  1452. case "HeaterL":
  1453. heaterIndex = 5;
  1454. break;
  1455. }
  1456. return heaterIndex;
  1457. }
  1458. public bool CheckWaitCondition(out string reason)
  1459. {
  1460. reason = "";
  1461. if (!_isWait || _waitHigh == 0 || _waitLow == 0)
  1462. return true;
  1463. if (_stableJudgmentTimer.IsRunning)
  1464. {
  1465. if (TempFeedback < _tempSetpoint - _waitLow ||
  1466. TempFeedback > _tempSetpoint + _waitHigh)
  1467. {
  1468. _stableJudgmentTimer.Restart();
  1469. }
  1470. if (_stableJudgmentTimer.ElapsedMilliseconds >= _stableJudgmentTime * 1000)
  1471. {
  1472. return true;
  1473. }
  1474. }
  1475. else
  1476. {
  1477. _stableJudgmentTimer.Restart();
  1478. }
  1479. reason = $"{Name} feedback={TempFeedback}, wait limit is ({_tempSetpoint - _waitLow}, {_tempSetpoint + _waitHigh})";
  1480. return false;
  1481. }
  1482. struct PIDParameter
  1483. {
  1484. public int No { get; set; }
  1485. public string Name { get; set; }
  1486. public float P { get; set; }
  1487. public float I { get; set; }
  1488. public float D { get; set; }
  1489. }
  1490. struct CorrectTableParameter
  1491. {
  1492. public int No { get; set; }
  1493. public string Name { get; set; }
  1494. public float ProfileTCCalibTemp { get; set; }
  1495. public float TableUseRangeMin { get; set; }
  1496. public float TableUseRangeMax { get; set; }
  1497. public int ProfileConditionTableNo { get; set; }
  1498. public int TempPIDTableNo { get; set; }
  1499. public List<CorrectParameter> CorrectParameterLst { get; set; }
  1500. }
  1501. struct ProfileParameter
  1502. {
  1503. public int No { get; set; }
  1504. public string Name { get; set; }
  1505. public float PreheatTime { get; set; }
  1506. public float CheckTime { get; set; }
  1507. public float TotalTime { get; set; }
  1508. public float AlarmLimit { get; set; }
  1509. public float U { get; set; }
  1510. public float CU { get; set; }
  1511. public float C { get; set; }
  1512. public float CL { get; set; }
  1513. public float L { get; set; }
  1514. }
  1515. }
  1516. public struct CorrectParameter
  1517. {
  1518. public int No { get; set; }
  1519. public string Name { get; set; }
  1520. public float ProfileTemp { get; set; }
  1521. public float ProfileTCCalib { get; set; }
  1522. public float ProfileCorrect { get; set; }
  1523. public float CascadeTCCorrect { get; set; }
  1524. }
  1525. }