终端一体化运控平台
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

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