IoHeater.cs 71 KB

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