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

593 рядки
22 KiB

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