UIViewModelBase.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Threading.Tasks;
  7. using System.Windows;
  8. using System.Windows.Controls;
  9. using System.Windows.Input;
  10. using Aitex.Core.RT.Log;
  11. using Aitex.Core.UI.MVVM;
  12. using Aitex.Core.Util;
  13. using Aitex.Core.Utilities;
  14. using MECF.Framework.Common.DataCenter;
  15. using MECF.Framework.Common.OperationCenter;
  16. using OpenSEMI.ClientBase;
  17. using OpenSEMI.Ctrlib.Controls;
  18. using Virgo_D.UI.Config;
  19. using VirgoCommon;
  20. namespace VirgoUI.Client.Models.Sys
  21. {
  22. public class ModuleUiViewModelBase : UiViewModelBase
  23. {
  24. public string SystemName { get; set; }
  25. public override void SubscribeKeys()
  26. {
  27. SubscribeKeys(this, SystemName);
  28. }
  29. public override void UpdateSubscribe(Dictionary<string, object> data, object target, string module = null)
  30. {
  31. Parallel.ForEach(target.GetType().GetProperties().Where(_hasSubscriptionAttribute),
  32. property =>
  33. {
  34. PropertyInfo pi = (PropertyInfo)property;
  35. SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
  36. string key = module == null ? $"{SystemName}.{subscription.ModuleKey}" : string.Format("{0}.{1}", module, subscription.ModuleKey);
  37. if (_subscribedKeys.Contains(key) && data.ContainsKey(key))
  38. {
  39. try
  40. {
  41. var convertedValue = Convert.ChangeType(data[key], pi.PropertyType);
  42. var originValue = Convert.ChangeType(pi.GetValue(target, null), pi.PropertyType);
  43. if (originValue != convertedValue)
  44. {
  45. pi.SetValue(target, convertedValue, null);
  46. }
  47. }
  48. catch (Exception ex)
  49. {
  50. LOG.Error("Data update from RT failed." + key, ex);
  51. }
  52. }
  53. });
  54. Parallel.ForEach(target.GetType().GetFields().Where(_hasSubscriptionAttribute),
  55. property =>
  56. {
  57. FieldInfo pi = (FieldInfo)property;
  58. SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
  59. string key = module == null ? $"{SystemName}.{subscription.ModuleKey}" : string.Format("{0}.{1}", module, subscription.ModuleKey);
  60. if (_subscribedKeys.Contains(key) && data.ContainsKey(key))
  61. {
  62. try
  63. {
  64. var convertedValue = Convert.ChangeType(data[key], pi.FieldType);
  65. pi.SetValue(target, convertedValue);
  66. }
  67. catch (Exception ex)
  68. {
  69. LOG.Error("Data update from RT failed." + key, ex);
  70. }
  71. }
  72. });
  73. }
  74. public void InvokePropertyChanged(string propertyName)
  75. {
  76. NotifyOfPropertyChange(propertyName);
  77. }
  78. public void InvokeAllPropertyChanged()
  79. {
  80. PropertyInfo[] ps = this.GetType().GetProperties();
  81. foreach (PropertyInfo p in ps)
  82. {
  83. InvokePropertyChanged(p.Name);
  84. if (p.PropertyType == typeof(ICommand))
  85. {
  86. DelegateCommand<string> cmd = p.GetValue(this, null) as DelegateCommand<string>;
  87. if (cmd != null)
  88. cmd.RaiseCanExecuteChanged();
  89. }
  90. }
  91. FieldInfo[] fi = this.GetType().GetFields();
  92. foreach (FieldInfo p in fi)
  93. {
  94. InvokePropertyChanged(p.Name);
  95. if (p.FieldType == typeof(ICommand))
  96. {
  97. DelegateCommand<string> cmd = p.GetValue(this) as DelegateCommand<string>;
  98. if (cmd != null)
  99. cmd.RaiseCanExecuteChanged();
  100. }
  101. }
  102. //Parallel.ForEach(this.GetType().GetProperties(), property => InvokePropertyChanged(property.Name));
  103. }
  104. protected override void InvokePropertyChanged()
  105. {
  106. PropertyInfo[] ps = this.GetType().GetProperties();
  107. foreach (PropertyInfo p in ps)
  108. {
  109. if (!p.GetCustomAttributes(false).Any(attribute => attribute is IgnorePropertyChangeAttribute))
  110. InvokePropertyChanged(p.Name);
  111. if (p.PropertyType == typeof(ICommand))
  112. {
  113. if (p.GetValue(this, null) is IDelegateCommand cmd)
  114. cmd.RaiseCanExecuteChanged();
  115. }
  116. }
  117. FieldInfo[] fi = this.GetType().GetFields();
  118. foreach (FieldInfo p in fi)
  119. {
  120. InvokePropertyChanged(p.Name);
  121. if (p.FieldType == typeof(ICommand))
  122. {
  123. DelegateCommand<string> cmd = p.GetValue(this) as DelegateCommand<string>;
  124. if (cmd != null)
  125. cmd.RaiseCanExecuteChanged();
  126. }
  127. }
  128. //Parallel.ForEach(this.GetType().GetProperties(), property => InvokePropertyChanged(property.Name));
  129. }
  130. }
  131. public class UiViewModelBase : BaseModel
  132. {
  133. private PeriodicJob _timer;
  134. protected ConcurrentBag<string> _subscribedKeys = new ConcurrentBag<string>();
  135. protected Func<object, bool> _isSubscriptionAttribute;
  136. protected Func<MemberInfo, bool> _hasSubscriptionAttribute;
  137. [IgnorePropertyChange]
  138. public List<Tuple<string, string, string, bool>> _allDataItemsA = new List<Tuple<string, string, string, bool>>();
  139. [IgnorePropertyChange]
  140. public List<Tuple<string, string, string, bool>> _allDataItemsB = new List<Tuple<string, string, string, bool>>();
  141. #region Property
  142. public ModuleInfo FOUPA { get; set; }
  143. public ModuleInfo FOUPB { get; set; }
  144. public ModuleInfo EFEM { get; set; }
  145. public ModuleInfo Aligner1 { get; set; }
  146. public ModuleInfo Aligner2 { get; set; }
  147. public ModuleInfo Cooling1 { get; set; }
  148. public ModuleInfo Cooling2 { get; set; }
  149. public ModuleInfo Flipper { get; set; }
  150. public ModuleInfo Buffer { get; set; }
  151. public ModuleInfo PMA { get; set; }
  152. public ModuleInfo PMB { get; set; }
  153. #region Wafer info for machine
  154. public WaferInfo PMAWafer { get; set; }
  155. public WaferInfo PMBWafer { get; set; }
  156. public WaferInfo Aligner1Wafer { get; set; }
  157. public WaferInfo Aligner2Wafer { get; set; }
  158. public WaferInfo Cooling1Wafer { get; set; }
  159. public WaferInfo Cooling2Wafer { get; set; }
  160. public WaferInfo EfemRobotWafer1 { get; set; }
  161. public WaferInfo EfemRobotWafer2 { get; set; }
  162. public WaferInfo FlipperWafer { get; set; }
  163. public WaferInfo BufferWafer { get; set; }
  164. public bool IsBufferIntalled
  165. {
  166. get
  167. {
  168. return ModuleManager.ModulesID.Contains("Buffer");
  169. }
  170. }
  171. public bool IsFlipperInstalled
  172. {
  173. get
  174. {
  175. return ModuleManager.ModulesID.Contains("Flipper");
  176. }
  177. }
  178. public bool IsAligner1Installed
  179. {
  180. get
  181. {
  182. return ModuleManager.ModulesID.Contains("Aligner1");
  183. }
  184. }
  185. public bool IsAligner2Installed
  186. {
  187. get
  188. {
  189. return ModuleManager.ModulesID.Contains("Aligner2");
  190. }
  191. }
  192. public bool IsCooling1Installed
  193. {
  194. get
  195. {
  196. return ModuleManager.ModulesID.Contains("Cooling1");
  197. }
  198. }
  199. public bool IsCooling2Installed
  200. {
  201. get
  202. {
  203. return ModuleManager.ModulesID.Contains("Cooling2");
  204. }
  205. }
  206. public bool IsPMAInstalled
  207. {
  208. get
  209. {
  210. return ModuleManager.ModulesID.Contains("PMA");
  211. }
  212. }
  213. public bool IsPMBInstalled
  214. {
  215. get
  216. {
  217. return ModuleManager.ModulesID.Contains("PMB");
  218. }
  219. }
  220. public bool IsLP1Installed
  221. {
  222. get
  223. {
  224. string moduleString = (string)QueryDataClient.Instance.Service.GetConfig("System.InstalledModules");
  225. return moduleString != null && moduleString.Contains("LP1");
  226. }
  227. }
  228. public bool IsLP2Installed
  229. {
  230. get
  231. {
  232. string moduleString = (string)QueryDataClient.Instance.Service.GetConfig("System.InstalledModules");
  233. return moduleString != null && moduleString.Contains("LP2");
  234. }
  235. }
  236. public Dictionary<string, float> ModuleTemperature { get; set; }
  237. #endregion Wafer info for machine
  238. public ICommand DeviceOperationCommand { get; protected set; }
  239. #endregion Property
  240. protected override void OnInitialize()
  241. {
  242. base.OnInitialize();
  243. PageEnabled = true;
  244. DeviceOperationCommand = new DelegateCommand<object>(DeviceOperation);
  245. ModuleTemperature = new Dictionary<string, float>();
  246. SubscribeKeys();
  247. }
  248. void DeviceOperation(object param)
  249. {
  250. InvokeClient.Instance.Service.DoOperation(RtOperation.DeviceOperation.ToString(), (object[])param);
  251. }
  252. protected void InitModules()
  253. {
  254. EFEM = ModuleManager.ModuleInfos["EfemRobot"];
  255. if (ModuleManager.ModuleInfos.ContainsKey("EfemRobot"))
  256. {
  257. EfemRobotWafer1 = ModuleManager.ModuleInfos["EfemRobot"].WaferManager.Wafers[0];
  258. EfemRobotWafer2 = ModuleManager.ModuleInfos["EfemRobot"].WaferManager.Wafers[1];
  259. }
  260. if (ModuleManager.ModuleInfos.ContainsKey("Aligner1"))
  261. {
  262. Aligner1 = ModuleManager.ModuleInfos["Aligner1"];
  263. Aligner1Wafer = ModuleManager.ModuleInfos["Aligner1"].WaferManager.Wafers[0];
  264. }
  265. if (ModuleManager.ModuleInfos.ContainsKey("Aligner2"))
  266. {
  267. Aligner2 = ModuleManager.ModuleInfos["Aligner2"];
  268. Aligner2Wafer = ModuleManager.ModuleInfos["Aligner2"].WaferManager.Wafers[0];
  269. }
  270. if (ModuleManager.ModuleInfos.ContainsKey("Cooling1"))
  271. {
  272. Cooling1 = ModuleManager.ModuleInfos["Cooling1"];
  273. Cooling1Wafer = ModuleManager.ModuleInfos["Cooling1"].WaferManager.Wafers[0];
  274. }
  275. if (ModuleManager.ModuleInfos.ContainsKey("Cooling2"))
  276. {
  277. Cooling2 = ModuleManager.ModuleInfos["Cooling2"];
  278. Cooling2Wafer = ModuleManager.ModuleInfos["Cooling2"].WaferManager.Wafers[0];
  279. }
  280. if (ModuleManager.ModuleInfos.ContainsKey("Flipper"))
  281. {
  282. Flipper = ModuleManager.ModuleInfos["Flipper"];
  283. FlipperWafer = ModuleManager.ModuleInfos["Flipper"].WaferManager.Wafers[0];
  284. }
  285. if (ModuleManager.ModuleInfos.ContainsKey("Buffer"))
  286. {
  287. Buffer = ModuleManager.ModuleInfos["Buffer"];
  288. BufferWafer = ModuleManager.ModuleInfos["Buffer"].WaferManager.Wafers[0];
  289. }
  290. if(ModuleManager.ModuleInfos.ContainsKey("LP1"))
  291. {
  292. FOUPA = ModuleManager.ModuleInfos["LP1"];
  293. }
  294. if(ModuleManager.ModuleInfos.ContainsKey("LP2"))
  295. {
  296. FOUPB = ModuleManager.ModuleInfos["LP2"];
  297. }
  298. if (ModuleManager.ModuleInfos.ContainsKey("PMA"))
  299. {
  300. PMA = ModuleManager.ModuleInfos["PMA"];
  301. PMAWafer = ModuleManager.ModuleInfos["PMA"].WaferManager.Wafers[0];
  302. }
  303. if (ModuleManager.ModuleInfos.ContainsKey("PMB"))
  304. {
  305. PMB = ModuleManager.ModuleInfos["PMB"];
  306. PMBWafer = ModuleManager.ModuleInfos["PMB"].WaferManager.Wafers[0];
  307. }
  308. }
  309. public string GetUnitStatusBackground(string status)
  310. {
  311. if (status != null)
  312. status = status.Trim().ToLower();
  313. switch (status)
  314. {
  315. case "error":
  316. return "red";
  317. case "idle":
  318. return "Transparent";
  319. case "init":
  320. return "Yellow";
  321. default:
  322. return "LawnGreen";
  323. }
  324. }
  325. /// <summary>
  326. /// support wafer transfer for slot
  327. /// </summary>
  328. public void OnWaferTransfer(DragDropEventArgs args)
  329. {
  330. try
  331. {
  332. float temp = 0.0f;
  333. if (ModuleTemperature.ContainsKey(args.TranferTo.ModuleID))
  334. {
  335. temp = ModuleTemperature[args.TranferTo.ModuleID];
  336. }
  337. WaferMoveManager.Instance.TransferWafer(args.TranferFrom, args.TranferTo, temp);
  338. }
  339. catch (Exception ex)
  340. {
  341. LOG.Write(ex);
  342. }
  343. }
  344. /// <summary>
  345. /// support context menu
  346. /// </summary>
  347. public void OnMouseUp(object sender, MouseButtonEventArgs e)
  348. {
  349. if (e.ChangedButton == MouseButton.Right)
  350. {
  351. Slot slot = sender as Slot;
  352. ContextMenu cm = ContextMenuManager.Instance.GetSlotMenus(slot);
  353. if (cm != null)
  354. {
  355. ((FrameworkElement)e.Source).ContextMenu = cm;
  356. }
  357. }
  358. }
  359. public UiViewModelBase()
  360. {
  361. _timer = new PeriodicJob(1000, this.OnTimer, "UIUpdaterThread - " + GetType().Name);
  362. _isSubscriptionAttribute = attribute => attribute is SubscriptionAttribute;
  363. _hasSubscriptionAttribute = mi => mi.GetCustomAttributes(false).Any(_isSubscriptionAttribute);
  364. _allDataItemsA = SystemConfigManager.Instance.GetMonitorDataList("A");
  365. _allDataItemsB = SystemConfigManager.Instance.GetMonitorDataList("B");
  366. }
  367. public virtual void SubscribeKeys()
  368. {
  369. SubscribeKeys(this);
  370. }
  371. //
  372. protected virtual bool OnTimer()
  373. {
  374. try
  375. {
  376. Poll();
  377. }
  378. catch (Exception ex)
  379. {
  380. LOG.Error(ex.Message);
  381. }
  382. return true;
  383. }
  384. public virtual void EnableTimer(bool enable)
  385. {
  386. if (enable) _timer.Start();
  387. else _timer.Pause();
  388. }
  389. protected virtual void Poll()
  390. {
  391. if (_subscribedKeys.Count > 0)
  392. {
  393. Dictionary<string, object> result = QueryDataClient.Instance.Service.PollData(_subscribedKeys);
  394. if (result == null)
  395. {
  396. LOG.Error("RT data acquisition failed.");
  397. return;
  398. }
  399. if (result.Count != _subscribedKeys.Count)
  400. {
  401. string unknowKeys = string.Empty;
  402. foreach (string key in _subscribedKeys)
  403. {
  404. if (!result.ContainsKey(key))
  405. {
  406. unknowKeys += key + "\r\n";
  407. }
  408. }
  409. //System.Diagnostics.Debug.Assert(false, unknowKeys);
  410. }
  411. InvokeBeforeUpdateProperty(result);
  412. UpdateValue(result);
  413. Application.Current.Dispatcher.Invoke(new Action(() =>
  414. {
  415. InvokePropertyChanged();
  416. InvokeAfterUpdateProperty(result);
  417. }));
  418. }
  419. }
  420. protected virtual void InvokePropertyChanged()
  421. {
  422. Refresh();
  423. }
  424. protected virtual void InvokeBeforeUpdateProperty(Dictionary<string, object> data)
  425. {
  426. }
  427. protected virtual void InvokeAfterUpdateProperty(Dictionary<string, object> data)
  428. {
  429. }
  430. public Dictionary<string, Tuple<string, string, bool>> GetDataElements(string culture, string type, string chamber)
  431. {
  432. Dictionary<string, Tuple<string, string, bool>> result = new Dictionary<string, Tuple<string, string, bool>>();
  433. Dictionary<string, Tuple<string, string, string, bool>> all = GetDataElements(type);
  434. foreach (var tuple in all)
  435. {
  436. if (!string.IsNullOrEmpty(chamber))
  437. {
  438. if (!tuple.Key.StartsWith($"{chamber}.") && !tuple.Key.StartsWith($"IO32.{chamber}."))
  439. continue;
  440. }
  441. result.Add(tuple.Key, Tuple.Create(tuple.Value.Item1, culture == CultureSupported.Chinese ? tuple.Value.Item3 : tuple.Value.Item2, tuple.Value.Item4));
  442. }
  443. return result;
  444. }
  445. public Dictionary<string, Tuple<string, string, string, bool>> GetDataElements(string type)
  446. {
  447. Dictionary<string, Tuple<string, string, string, bool>> dicItems = new Dictionary<string, Tuple<string, string, string, bool>>();
  448. if (type == "A")
  449. {
  450. foreach (var dataItem in _allDataItemsA)
  451. {
  452. dicItems[dataItem.Item1] = dataItem;
  453. }
  454. }
  455. else
  456. {
  457. foreach (var dataItem in _allDataItemsB)
  458. {
  459. dicItems[dataItem.Item1] = dataItem;
  460. }
  461. }
  462. return dicItems;
  463. }
  464. void UpdateGasItem(bool enable, string display, string id, string key, Dictionary<string, Tuple<string, string, string, bool>> dicItems)
  465. {
  466. if (enable && dicItems.ContainsKey(key))
  467. {
  468. dicItems[key] = Tuple.Create(key,
  469. dicItems[key].Item2.Replace(id, display),
  470. dicItems[key].Item3.Replace(id, display),
  471. dicItems[key].Item4);
  472. }
  473. else
  474. {
  475. dicItems.Remove(key);
  476. }
  477. }
  478. void UpdateValue(Dictionary<string, object> data)
  479. {
  480. if (data == null)
  481. return;
  482. UpdateSubscribe(data, this);
  483. var properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute<SubscriptionModuleAttribute>() != null);
  484. foreach (var property in properties)
  485. {
  486. var moduleAttr = property.GetCustomAttribute<SubscriptionModuleAttribute>();
  487. UpdateSubscribe(data, property.GetValue(this), moduleAttr.Module);
  488. }
  489. }
  490. protected void Subscribe(string key)
  491. {
  492. if (!string.IsNullOrEmpty(key))
  493. {
  494. _subscribedKeys.Add(key);
  495. }
  496. }
  497. public void SubscribeKeys(UiViewModelBase target)
  498. {
  499. SubscribeKeys(target, "");
  500. }
  501. public void SubscribeKeys(UiViewModelBase target, string module)
  502. {
  503. Parallel.ForEach(target.GetType().GetProperties().Where(_hasSubscriptionAttribute),
  504. property =>
  505. {
  506. SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
  507. string key = subscription.ModuleKey;
  508. if (!string.IsNullOrEmpty(module))
  509. {
  510. key = $"{module}.{key}";
  511. subscription.SetModule(module);
  512. }
  513. if (!_subscribedKeys.Contains(key))
  514. _subscribedKeys.Add(key);
  515. });
  516. Parallel.ForEach(target.GetType().GetFields().Where(_hasSubscriptionAttribute),
  517. method =>
  518. {
  519. SubscriptionAttribute subscription = method.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
  520. string key = subscription.ModuleKey;
  521. if (!string.IsNullOrEmpty(module))
  522. {
  523. key = $"{module}.{key}";
  524. subscription.SetModule(module);
  525. }
  526. if (!_subscribedKeys.Contains(key))
  527. _subscribedKeys.Add(key);
  528. });
  529. }
  530. public virtual void UpdateSubscribe(Dictionary<string, object> data, object target, string module = null)
  531. {
  532. Parallel.ForEach(target.GetType().GetProperties().Where(_hasSubscriptionAttribute),
  533. property =>
  534. {
  535. PropertyInfo pi = (PropertyInfo)property;
  536. SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
  537. string key = subscription.ModuleKey;
  538. key = module == null ? key : string.Format("{0}.{1}", module, key);
  539. if (_subscribedKeys.Contains(key) && data.ContainsKey(key))
  540. {
  541. try
  542. {
  543. var convertedValue = Convert.ChangeType(data[key], pi.PropertyType);
  544. var originValue = Convert.ChangeType(pi.GetValue(target, null), pi.PropertyType);
  545. if (originValue != convertedValue)
  546. {
  547. pi.SetValue(target, convertedValue, null);
  548. }
  549. }
  550. catch (Exception ex)
  551. {
  552. LOG.Error("Data update from RT failed." + key, ex);
  553. }
  554. }
  555. });
  556. Parallel.ForEach(target.GetType().GetFields().Where(_hasSubscriptionAttribute),
  557. property =>
  558. {
  559. FieldInfo pi = (FieldInfo)property;
  560. SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
  561. string key = subscription.ModuleKey;
  562. if (_subscribedKeys.Contains(key) && data.ContainsKey(key))
  563. {
  564. try
  565. {
  566. var convertedValue = Convert.ChangeType(data[key], pi.FieldType);
  567. pi.SetValue(target, convertedValue);
  568. }
  569. catch (Exception ex)
  570. {
  571. LOG.Error("Data update from RT failed." + key, ex);
  572. }
  573. }
  574. });
  575. }
  576. protected override void OnActivate()
  577. {
  578. base.OnActivate();
  579. EnableTimer(true);
  580. }
  581. protected override void OnDeactivate(bool close)
  582. {
  583. base.OnDeactivate(close);
  584. EnableTimer(false);
  585. }
  586. }
  587. }