终端一体化运控平台
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 
 

608 рядки
23 KiB

  1. using BPA.Message;
  2. using BPA.Message.Enum;
  3. using BPASmartClient.Model;
  4. using BPASmartClient.Peripheral;
  5. using System;
  6. using System.Collections.Concurrent;
  7. using System.Collections.Generic;
  8. using System.Linq;
  9. using System.Reflection;
  10. using System.Text;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. using System.Collections.ObjectModel;
  14. using BPASmartClient.Model.单片机;
  15. using BPA.Models;
  16. using BPA.Helper;
  17. namespace BPASmartClient.Device
  18. {
  19. /// <summary>
  20. /// 设备基类
  21. /// </summary>
  22. public abstract class BaseDevice : IDevice
  23. {
  24. public BaseDevice()
  25. {
  26. }
  27. #region 属性
  28. /// <summary>
  29. /// 订单物料信息
  30. /// </summary>
  31. public OrderMaterialDelivery orderMaterialDelivery { get; set; } = new OrderMaterialDelivery();
  32. /// <summary>
  33. /// 配方数据信息
  34. /// </summary>
  35. public RecipeBoms recipeBoms { get; set; } = new RecipeBoms();
  36. /// <summary>
  37. /// 设备ID
  38. /// </summary>
  39. public int DeviceId { get; set; }
  40. /// <summary>
  41. /// 设备所有状态
  42. /// </summary>
  43. public DeviceStatus Status { get; set; } = new DeviceStatus();
  44. /// <summary>
  45. /// 设备名称
  46. /// </summary>
  47. public string Name { get; set; }
  48. /// <summary>
  49. /// 当前订单数量
  50. /// </summary>
  51. protected int OrderCount { get; set; }
  52. /// <summary>
  53. /// 设备初始化中
  54. /// </summary>
  55. protected bool Initing { get; set; }
  56. /// <summary>
  57. /// 设备类型
  58. /// </summary>
  59. public abstract DeviceClientType DeviceType { get; }
  60. /// <summary>
  61. /// 是否忙碌
  62. /// </summary>
  63. public bool IsBusy { get; protected set; }
  64. /// <summary>
  65. /// 是否健康
  66. /// </summary>
  67. public bool IsHealth { get; protected set; }
  68. /// <summary>
  69. /// 设备运行日志
  70. /// </summary>
  71. public List<object> Log { get; set; } = new List<object>();
  72. /// <summary>
  73. /// 设备运行告警与错误
  74. /// </summary>
  75. public List<object> Error { get; set; } = new List<object>();
  76. /// <summary>
  77. /// mork_F暂用余量列表
  78. /// </summary>
  79. public List<BatchingInfo> BatchingInfos { get; set; } = new List<BatchingInfo>();
  80. /// <summary>
  81. /// 设备变量监控
  82. /// </summary>
  83. public ObservableCollection<VariableMonitor> variableMonitors { get; set; } = new ObservableCollection<VariableMonitor>();
  84. /// <summary>
  85. /// 外设状态,硬件设备数据
  86. /// </summary>
  87. protected ConcurrentDictionary<string, object> peripheralStatus = new ConcurrentDictionary<string, object>();
  88. protected
  89. /// <summary>
  90. /// 外设设备集合
  91. /// </summary>
  92. private List<IPeripheral> peripherals;
  93. /// <summary>
  94. /// <炒锅>:<外设状态,硬件设备数据>的键值对
  95. /// </summary>
  96. protected Dictionary<int, ConcurrentDictionary<string, object>> dicPort2peripheralStatus = new Dictionary<int, ConcurrentDictionary<string, object>>();
  97. public Action<int, object> AddErrorAction { get; set; }
  98. public Action<int, object> DeleteErrorAction { get; set; }
  99. public List<BPASmartClient.Model.Alarm> alarms { get; set; } = new List<BPASmartClient.Model.Alarm>();
  100. public IAlarm InterfaceAlarm { get; set; }
  101. public AlarmHelper alarmHelper { get; set; } = new AlarmHelper();
  102. public IStatus InterfaceStatus { get; set; }
  103. public ObservableCollection<Variable> variables { get; set; } = new ObservableCollection<Variable>();
  104. #endregion
  105. /// <summary>
  106. /// 写控制
  107. /// </summary>
  108. /// <param name="address"></param>
  109. /// <param name="value"></param>
  110. public void WriteControl(string address, object value)
  111. {
  112. if (peripherals != null)
  113. {
  114. for (int i = 0; i < peripherals.Count; i++)
  115. {
  116. peripherals.ElementAt(i).WriteData(address, value);
  117. }
  118. }
  119. }
  120. /// <summary>
  121. /// 多设备分开写控制
  122. /// </summary>
  123. /// <param name="address"></param>
  124. /// <param name="value"></param>
  125. public void WriteControlExact(string address, object value, int i)
  126. {
  127. if (peripherals != null)
  128. {
  129. if (peripherals.Count > i)
  130. {
  131. peripherals.ElementAt(i).WriteData(address, value);
  132. }
  133. }
  134. }
  135. /// <summary>
  136. /// 设备过程日志显示
  137. /// </summary>
  138. /// <param name="info"></param>
  139. public void DeviceProcessLogShow(string info)
  140. {
  141. Log.Insert(0, new { Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"), Type = "流程", Text = info });
  142. MessageLog.GetInstance.DeviceProcessLogShow(DeviceId.ToString(), info);
  143. if (Log.Count > 100) { Log.RemoveAt(Log.Count - 1); }
  144. }
  145. public void Initliaze()
  146. {
  147. }
  148. public void Initliaze(List<IPeripheral> peripherals)
  149. {
  150. peripherals.ForEach(p =>
  151. {
  152. p.DeviceId = this.DeviceId;
  153. p.Init();
  154. });
  155. this.peripherals = peripherals;
  156. }
  157. public virtual void StartMain()
  158. {
  159. TaskManage.GetInstance.StartLong(new Action(() =>
  160. {
  161. int i = 0;
  162. foreach (var peripheral in peripherals)
  163. {
  164. string TypeName = peripheral.GetType().FullName.Replace("BPASmartClient.", "");
  165. Status.Update($"{TypeName}.IsConnected", peripheral.IsConnected);
  166. Status.Update($"{TypeName}.IsWork", peripheral.IsWork);
  167. //做为炒锅与状态字典的新数据
  168. ConcurrentDictionary<string, object> newPeripheralStatus = new ConcurrentDictionary<string, object>();
  169. foreach (var key in peripheral.GetAllStatus().Keys)
  170. {
  171. peripheralStatus[key] = peripheral.GetAllStatus()[key];
  172. //新的硬件设备数据存储
  173. newPeripheralStatus[key] = peripheral.GetAllStatus()[key];
  174. if (TypeName != "PLC.PLCMachine")
  175. {
  176. Status.Update($"{TypeName}.{key}", peripheral.GetAllStatus()[key]);
  177. }
  178. }
  179. if (dicPort2peripheralStatus.ContainsKey(i))
  180. {
  181. dicPort2peripheralStatus[i] = newPeripheralStatus;
  182. }
  183. else
  184. {
  185. //将存储的新硬件设备数据放入字典中,i是作为炒锅编号。
  186. dicPort2peripheralStatus.Add(i, newPeripheralStatus);
  187. }
  188. i++;
  189. }
  190. if (AddErrorAction != null && DeleteErrorAction != null)
  191. {
  192. foreach (var item in Status.GetStatusT())
  193. {
  194. if (item.Name == "Warning" || item.Name == "Fault")
  195. {
  196. if (item.Status != "无故障" && item.Status != "无警告" && item.Status != "未发生故障")
  197. {
  198. var res = Error?.FirstOrDefault(p => p.GetType().GetProperty("Text").GetValue(p).ToString() == item.Ms);
  199. if (res == null)
  200. {
  201. object obj = new
  202. {
  203. Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
  204. Type = item.Name == "Warning" ? "警告" : "故障",
  205. Text = item.Ms
  206. };
  207. Error.Add(obj);
  208. AddErrorAction?.Invoke(DeviceId, obj);
  209. }
  210. }
  211. else
  212. {
  213. var res = Error?.FirstOrDefault(p => p.GetType().GetProperty("Text").GetValue(p).ToString().Contains(item.id));
  214. if (res != null)
  215. {
  216. Error.Remove(res);
  217. DeleteErrorAction?.Invoke(DeviceId, res);
  218. }
  219. }
  220. }
  221. }
  222. }
  223. Thread.Sleep(100);
  224. }), $"GetAllStatus:{DeviceId}");
  225. DoMain();
  226. SimOrder();
  227. GetGvlStatus();
  228. InitResetTask();
  229. InitTask();
  230. }
  231. private void ResetStatus()
  232. {
  233. this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList().ForEach(item =>
  234. {
  235. var res = item.FieldType.GetInterfaces();
  236. if (res != null)
  237. {
  238. foreach (var faces in res)
  239. {
  240. if (faces.Name == "IStatus") InterfaceStatus = item.GetValue(this) as IStatus;
  241. }
  242. }
  243. });
  244. }
  245. private void GetGvlStatus()
  246. {
  247. this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList().ForEach(item =>
  248. {
  249. var res = item.FieldType.GetInterfaces();
  250. if (res != null)
  251. {
  252. foreach (var faces in res)
  253. {
  254. if (faces.Name == "IStatus")
  255. {
  256. InterfaceStatus = item.GetValue(this) as IStatus;
  257. GetMonitorData(InterfaceStatus);
  258. }
  259. else if (faces.Name == "IAlarm")
  260. {
  261. InterfaceAlarm = item.GetValue(this) as IAlarm;
  262. alarmHelper.AddAction += new Action<string>((s) =>
  263. {
  264. var res = alarmHelper.Alarms.FirstOrDefault(p => p.Info == s);
  265. if (res != null)
  266. {
  267. object obj = new
  268. {
  269. Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
  270. Type = res.Grade,
  271. Text = res.Info
  272. };
  273. Error.Insert(0, obj);
  274. AddErrorAction?.Invoke(DeviceId, obj);
  275. }
  276. });
  277. alarmHelper.RemoveAction += new Action<string>((s) =>
  278. {
  279. var res = Error.FirstOrDefault(p => p.GetType().GetProperty("Text").GetValue(p).ToString() == s);
  280. if (res != null && Error.Contains(res))
  281. {
  282. Error.Remove(res);
  283. DeleteErrorAction?.Invoke(DeviceId, res);
  284. }
  285. });
  286. TaskManage.GetInstance.StartLong(new Action(() =>
  287. {
  288. AlarmMonitoring();
  289. Thread.Sleep(500);
  290. }), $"报警检测监控:{DeviceId}");
  291. }
  292. }
  293. }
  294. });
  295. }
  296. /// <summary>
  297. /// 报警监控
  298. /// </summary>
  299. /// <param name="alarm"></param>
  300. /// <param name="alarmHelper"></param>
  301. private void AlarmMonitoring()
  302. {
  303. if (InterfaceAlarm == null) return;
  304. foreach (var item in InterfaceAlarm.GetType().GetProperties())
  305. {
  306. var res = item.GetValue(InterfaceAlarm);
  307. if (res != null && res is bool blen)
  308. {
  309. if (item.CustomAttributes.Count() > 0 && item.CustomAttributes.ElementAt(0)?.ConstructorArguments.Count() > 0)
  310. {
  311. var info = item.GetCustomAttribute<AlarmAttribute>().AlarmInfo;
  312. if (info != null) alarmHelper.EdgeAlarm(blen, $"{info.ToString()}-{DeviceId}");
  313. }
  314. }
  315. }
  316. }
  317. private void InitResetTask()
  318. {
  319. #region 复位程序
  320. TaskManage.GetInstance.StartLong(new Action(() =>
  321. {
  322. if (RTrig.GetInstance($"ResetProgram:{DeviceId}").Start(Initing))
  323. {
  324. DeviceProcessLogShow("启动初始化");
  325. //记录监控数据
  326. DeviceProcessLogShow($"监控数据:【 {variableMonitors.ToJSON()} 】");
  327. TaskManage.GetInstance.StopTask($"MainTask:{DeviceId}", new Action(() =>
  328. {
  329. TaskManage.GetInstance.StopTask($"ReadData:{DeviceId}", new Action(() =>
  330. {
  331. TaskManage.GetInstance.StopTask($"GvlStatusMonitor:{DeviceId}", new Action(() =>
  332. {
  333. ActionManage.GetInstance.Send("ClearOrders");
  334. ResetProgram();
  335. ResetStatus();
  336. InitTask();
  337. }));
  338. }));
  339. }));
  340. }
  341. Thread.Sleep(10);
  342. }), $"ResetProgram:{DeviceId}");
  343. #endregion
  344. }
  345. private void InitTask()
  346. {
  347. #region 数据读取
  348. TaskManage.GetInstance.StartLong(new Action(() =>
  349. {
  350. ReadData();
  351. Thread.Sleep(10);
  352. }), $"ReadData:{DeviceId}", true);
  353. #endregion
  354. #region 任务流程
  355. TaskManage.GetInstance.StartLong(new Action(() =>
  356. {
  357. MainTask();
  358. Thread.Sleep(10);
  359. }), $"MainTask:{DeviceId}", true);
  360. #endregion
  361. #region 设备状态监控
  362. TaskManage.GetInstance.StartLong(new Action(() =>
  363. {
  364. UpdateValue(InterfaceStatus);
  365. Thread.Sleep(1000);
  366. }), $"GvlStatusMonitor:{DeviceId}");
  367. #endregion
  368. }
  369. /// <summary>
  370. /// 获取监控信息
  371. /// </summary>
  372. private void GetMonitorData(IStatus status)
  373. {
  374. if (status == null) return;
  375. List<VariableMonitor> vm = new List<VariableMonitor>();
  376. foreach (var item in status.GetType().GetProperties())
  377. {
  378. if (item.CustomAttributes.Count() > 0)
  379. {
  380. var attributeName = item.CustomAttributes.FirstOrDefault(p => p.AttributeType.Name == "VariableMonitorAttribute");
  381. if (attributeName == null) return;
  382. var plcadd = item.GetCustomAttribute<VariableMonitorAttribute>()?.PLCAddress;
  383. var modadd = item.GetCustomAttribute<VariableMonitorAttribute>()?.ModbusTcpAddress;
  384. var notes = item.GetCustomAttribute<VariableMonitorAttribute>()?.Notes;
  385. if (item.PropertyType?.BaseType?.Name == "Array")
  386. {
  387. if (plcadd?.Length > 0)
  388. {
  389. var arrayRes = item.GetValue(status, null);
  390. if (arrayRes != null && arrayRes is Array arr)
  391. {
  392. for (int i = 0; i < arr.Length; i++)
  393. {
  394. var res = vm.FirstOrDefault(p => p.VarName == $"{item.Name}_{i + 1}");
  395. if (res == null)
  396. {
  397. string[] plc = plcadd?.Substring(1).Split('.');
  398. string TempPlcAddress = string.Empty;
  399. if (plc?.Length == 2)
  400. {
  401. int add = int.Parse(plc[1]);
  402. int firstAdd = int.Parse(plc[0]);
  403. if (add >= 0 && add < 7)
  404. {
  405. add += i;
  406. }
  407. else if (add >= 7)
  408. {
  409. add = 0;
  410. firstAdd++;
  411. }
  412. plc[0] = firstAdd.ToString();
  413. plc[1] = add.ToString();
  414. TempPlcAddress = $"M{plc[0]}.{plc[1]}";
  415. }
  416. vm.Add(new VariableMonitor()
  417. {
  418. Id = vm.Count,
  419. VarName = $"{item.Name}_{i + 1}",
  420. Notes = $"{notes}_{i + 1}",
  421. ModbusTcpAddress = $"{int.Parse(modadd) + i}",
  422. PLCAddress = TempPlcAddress,
  423. });
  424. }
  425. }
  426. }
  427. }
  428. else
  429. {
  430. var arrayRes = item.GetValue(status, null);
  431. if (arrayRes != null && arrayRes is Array arr)
  432. {
  433. for (int i = 0; i < arr.Length; i++)
  434. {
  435. var res = vm.FirstOrDefault(p => p.VarName == $"{item.Name}_{i + 1}");
  436. if (res == null)
  437. {
  438. vm.Add(new VariableMonitor()
  439. {
  440. Id = vm.Count,
  441. VarName = $"{item.Name}_{i + 1}",
  442. Notes = $"{notes}_{i + 1}",
  443. });
  444. }
  445. }
  446. }
  447. }
  448. }
  449. else
  450. {
  451. var res = vm.FirstOrDefault(p => p.VarName == item.Name);
  452. if (res == null)
  453. {
  454. vm.Add(new VariableMonitor()
  455. {
  456. Id = vm.Count,
  457. VarName = item.Name,
  458. Notes = notes,
  459. ModbusTcpAddress = modadd,
  460. PLCAddress = plcadd,
  461. });
  462. }
  463. }
  464. }
  465. }
  466. //监控列表排序
  467. //vm.OrderBy(p => p.VarName).ToList().ForEach(item => { variableMonitors.Add(item); });
  468. vm.ForEach(item => { variableMonitors.Add(item); });
  469. }
  470. /// <summary>
  471. /// 更新数据
  472. /// </summary>
  473. /// <param name="status"></param>
  474. public void UpdateValue(IStatus status)
  475. {
  476. if (status == null) return;
  477. foreach (var item in status.GetType().GetProperties())
  478. {
  479. if (item.CustomAttributes.Count() > 0)
  480. {
  481. if (item.PropertyType?.BaseType?.Name == "Array")
  482. {
  483. var arrayRes = item.GetValue(status);
  484. if (arrayRes != null && arrayRes is Array arr)
  485. {
  486. for (int i = 0; i < arr.Length; i++)
  487. {
  488. int index = Array.FindIndex(variableMonitors.ToArray(), p => p.VarName == $"{item.Name}_{i + 1}");
  489. if (index >= 0 && index < variableMonitors.Count)
  490. {
  491. variableMonitors.ElementAt(index).CurrentValue = arr.GetValue(i)?.ToString();
  492. }
  493. }
  494. }
  495. }
  496. else
  497. {
  498. int index = Array.FindIndex(variableMonitors.ToArray(), p => p.VarName == item.Name);
  499. if (index >= 0 && index < variableMonitors.Count)
  500. {
  501. variableMonitors.ElementAt(index).CurrentValue = item.GetValue(status)?.ToString();
  502. }
  503. }
  504. }
  505. }
  506. }
  507. public abstract void DoMain();
  508. public abstract void Stop();
  509. /// <summary>
  510. /// 数据读取
  511. /// </summary>
  512. public abstract void ReadData();
  513. /// <summary>
  514. /// 主流程控制
  515. /// </summary>
  516. public abstract void MainTask();
  517. /// <summary>
  518. /// 复位程序
  519. /// </summary>
  520. public abstract void ResetProgram();
  521. /// <summary>
  522. /// 模拟订单
  523. /// </summary>
  524. public abstract void SimOrder();
  525. public object GetError()
  526. {
  527. return new { data = Error };
  528. }
  529. public object GetLog()
  530. {
  531. return new { data = Log };
  532. }
  533. public object GetVariableMonitor()
  534. {
  535. return new { data = variableMonitors };
  536. }
  537. /// <summary>
  538. /// 获取某个对象中的属性值
  539. /// </summary>
  540. /// <param name="info"></param>
  541. /// <param name="field"></param>
  542. /// <returns></returns>
  543. public object GetPropertyValue(object info, string field)
  544. {
  545. if (info == null) return null;
  546. Type t = info.GetType();
  547. IEnumerable<System.Reflection.PropertyInfo> property = from pi in t.GetProperties() where pi.Name.ToLower() == field.ToLower() select pi;
  548. return property.First().GetValue(info, null);
  549. }
  550. }
  551. }