终端一体化运控平台
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 

626 行
24 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 Action<bool> stateAction;
  25. public BaseDevice()
  26. {
  27. }
  28. public bool IsConnected { get; set; }=false;
  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<BPASmartClient.Model.Alarm> alarms { get; set; } = new List<BPASmartClient.Model.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:fff"), 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. TaskManage.GetInstance.StartLong(() =>
  159. {
  160. var res = this.peripherals.FindAll(o => o.IsConnected == true);
  161. if (res.Count > 0&&!IsConnected)
  162. {
  163. IsConnected = true;
  164. stateAction?.Invoke(true);
  165. }
  166. else if (res.Count<=0&&IsConnected)
  167. {
  168. IsConnected= false;
  169. stateAction?.Invoke(false);
  170. }
  171. Thread.Sleep(3000);
  172. }, "设备状态监控");
  173. }
  174. public virtual void StartMain()
  175. {
  176. TaskManage.GetInstance.StartLong(new Action(() =>
  177. {
  178. int i = 0;
  179. foreach (var peripheral in peripherals)
  180. {
  181. string TypeName = peripheral.GetType().FullName.Replace("BPASmartClient.", "");
  182. Status.Update($"{TypeName}.IsConnected", peripheral.IsConnected);
  183. Status.Update($"{TypeName}.IsWork", peripheral.IsWork);
  184. //做为炒锅与状态字典的新数据
  185. ConcurrentDictionary<string, object> newPeripheralStatus = new ConcurrentDictionary<string, object>();
  186. foreach (var key in peripheral.GetAllStatus().Keys)
  187. {
  188. peripheralStatus[key] = peripheral.GetAllStatus()[key];
  189. //新的硬件设备数据存储
  190. newPeripheralStatus[key] = peripheral.GetAllStatus()[key];
  191. if (TypeName != "PLC.PLCMachine")
  192. {
  193. Status.Update($"{TypeName}.{key}", peripheral.GetAllStatus()[key]);
  194. }
  195. }
  196. if (dicPort2peripheralStatus.ContainsKey(i))
  197. {
  198. dicPort2peripheralStatus[i] = newPeripheralStatus;
  199. }
  200. else
  201. {
  202. //将存储的新硬件设备数据放入字典中,i是作为炒锅编号。
  203. dicPort2peripheralStatus.Add(i, newPeripheralStatus);
  204. }
  205. i++;
  206. }
  207. if (AddErrorAction != null && DeleteErrorAction != null)
  208. {
  209. foreach (var item in Status.GetStatusT())
  210. {
  211. if (item.Name == "Warning" || item.Name == "Fault")
  212. {
  213. if (item.Status != "无故障" && item.Status != "无警告" && item.Status != "未发生故障")
  214. {
  215. var res = Error?.FirstOrDefault(p => p.GetType().GetProperty("Text").GetValue(p).ToString() == item.Ms);
  216. if (res == null)
  217. {
  218. object obj = new
  219. {
  220. Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
  221. Type = item.Name == "Warning" ? "警告" : "故障",
  222. Text = item.Ms
  223. };
  224. Error.Add(obj);
  225. AddErrorAction?.Invoke(DeviceId, obj);
  226. }
  227. }
  228. else
  229. {
  230. var res = Error?.FirstOrDefault(p => p.GetType().GetProperty("Text").GetValue(p).ToString().Contains(item.id));
  231. if (res != null)
  232. {
  233. Error.Remove(res);
  234. DeleteErrorAction?.Invoke(DeviceId, res);
  235. }
  236. }
  237. }
  238. }
  239. }
  240. Thread.Sleep(100);
  241. }), $"GetAllStatus:{DeviceId}");
  242. DoMain();
  243. //SimOrder();
  244. GetGvlStatus();
  245. InitResetTask();
  246. InitTask();
  247. }
  248. private void ResetStatus()
  249. {
  250. this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList().ForEach(item =>
  251. {
  252. var res = item.FieldType.GetInterfaces();
  253. if (res != null)
  254. {
  255. foreach (var faces in res)
  256. {
  257. if (faces.Name == "IStatus") InterfaceStatus = item.GetValue(this) as IStatus;
  258. }
  259. }
  260. });
  261. }
  262. private void GetGvlStatus()
  263. {
  264. this.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList().ForEach(item =>
  265. {
  266. var res = item.FieldType.GetInterfaces();
  267. if (res != null)
  268. {
  269. foreach (var faces in res)
  270. {
  271. if (faces.Name == "IStatus")
  272. {
  273. InterfaceStatus = item.GetValue(this) as IStatus;
  274. GetMonitorData(InterfaceStatus);
  275. }
  276. else if (faces.Name == "IAlarm")
  277. {
  278. InterfaceAlarm = item.GetValue(this) as IAlarm;
  279. alarmHelper.AddAction += new Action<string>((s) =>
  280. {
  281. var res = alarmHelper.Alarms.FirstOrDefault(p => p.Info == s);
  282. if (res != null)
  283. {
  284. object obj = new
  285. {
  286. Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
  287. Type = res.Grade,
  288. Text = res.Info
  289. };
  290. Error.Insert(0, obj);
  291. AddErrorAction?.Invoke(DeviceId, obj);
  292. }
  293. });
  294. alarmHelper.RemoveAction += new Action<string>((s) =>
  295. {
  296. var res = Error.FirstOrDefault(p => p.GetType().GetProperty("Text").GetValue(p).ToString() == s);
  297. if (res != null && Error.Contains(res))
  298. {
  299. Error.Remove(res);
  300. DeleteErrorAction?.Invoke(DeviceId, res);
  301. }
  302. });
  303. TaskManage.GetInstance.StartLong(new Action(() =>
  304. {
  305. AlarmMonitoring();
  306. Thread.Sleep(500);
  307. }), $"报警检测监控:{DeviceId}");
  308. }
  309. }
  310. }
  311. });
  312. }
  313. /// <summary>
  314. /// 报警监控
  315. /// </summary>
  316. /// <param name="alarm"></param>
  317. /// <param name="alarmHelper"></param>
  318. private void AlarmMonitoring()
  319. {
  320. if (InterfaceAlarm == null) return;
  321. foreach (var item in InterfaceAlarm.GetType().GetProperties())
  322. {
  323. var res = item.GetValue(InterfaceAlarm);
  324. if (res != null && res is bool blen)
  325. {
  326. if (item.CustomAttributes.Count() > 0 && item.CustomAttributes.ElementAt(0)?.ConstructorArguments.Count() > 0)
  327. {
  328. var info = item.GetCustomAttribute<AlarmAttribute>().AlarmInfo;
  329. if (info != null) alarmHelper.EdgeAlarm(blen, $"{info.ToString()}-{DeviceId}");
  330. }
  331. }
  332. }
  333. }
  334. private void InitResetTask()
  335. {
  336. #region 复位程序
  337. TaskManage.GetInstance.StartLong(new Action(() =>
  338. {
  339. if (RTrig.GetInstance($"ResetProgram:{DeviceId}").Start(Initing))
  340. {
  341. DeviceProcessLogShow("启动初始化");
  342. //记录监控数据
  343. DeviceProcessLogShow($"监控数据:【 {variableMonitors.ToJSON()} 】");
  344. TaskManage.GetInstance.StopTask($"MainTask:{DeviceId}", new Action(() =>
  345. {
  346. TaskManage.GetInstance.StopTask($"ReadData:{DeviceId}", new Action(() =>
  347. {
  348. TaskManage.GetInstance.StopTask($"GvlStatusMonitor:{DeviceId}", new Action(() =>
  349. {
  350. //ActionManage.GetInstance.Send("ClearOrders");
  351. ResetProgram();
  352. ResetStatus();
  353. InitTask();
  354. }));
  355. }));
  356. }));
  357. }
  358. Thread.Sleep(10);
  359. }), $"ResetProgram:{DeviceId}");
  360. #endregion
  361. }
  362. private void InitTask()
  363. {
  364. #region 数据读取
  365. TaskManage.GetInstance.StartLong(new Action(() =>
  366. {
  367. ReadData();
  368. Thread.Sleep(10);
  369. }), $"ReadData:{DeviceId}", true);
  370. #endregion
  371. #region 任务流程
  372. TaskManage.GetInstance.StartLong(new Action(() =>
  373. {
  374. if (IsConnected)
  375. {
  376. MainTask();
  377. }
  378. Thread.Sleep(10);
  379. }), $"MainTask:{DeviceId}", true);
  380. #endregion
  381. #region 设备状态监控
  382. TaskManage.GetInstance.StartLong(new Action(() =>
  383. {
  384. UpdateValue(InterfaceStatus);
  385. Thread.Sleep(1000);
  386. }), $"GvlStatusMonitor:{DeviceId}");
  387. #endregion
  388. }
  389. /// <summary>
  390. /// 获取监控信息
  391. /// </summary>
  392. private void GetMonitorData(IStatus status)
  393. {
  394. if (status == null) return;
  395. List<VariableMonitor> vm = new List<VariableMonitor>();
  396. foreach (var item in status.GetType().GetProperties())
  397. {
  398. if (item.CustomAttributes.Count() > 0)
  399. {
  400. var attributeName = item.CustomAttributes.FirstOrDefault(p => p.AttributeType.Name == "VariableMonitorAttribute");
  401. if (attributeName == null) return;
  402. var plcadd = item.GetCustomAttribute<VariableMonitorAttribute>()?.PLCAddress;
  403. var modadd = item.GetCustomAttribute<VariableMonitorAttribute>()?.ModbusTcpAddress;
  404. var notes = item.GetCustomAttribute<VariableMonitorAttribute>()?.Notes;
  405. if (item.PropertyType?.BaseType?.Name == "Array")
  406. {
  407. if (plcadd?.Length > 0)
  408. {
  409. var arrayRes = item.GetValue(status, null);
  410. if (arrayRes != null && arrayRes is Array arr)
  411. {
  412. for (int i = 0; i < arr.Length; i++)
  413. {
  414. var res = vm.FirstOrDefault(p => p.VarName == $"{item.Name}_{i + 1}");
  415. if (res == null)
  416. {
  417. string[] plc = plcadd?.Substring(1).Split('.');
  418. string TempPlcAddress = string.Empty;
  419. if (plc?.Length == 2)
  420. {
  421. int add = int.Parse(plc[1]);
  422. int firstAdd = int.Parse(plc[0]);
  423. if (add >= 0 && add < 7)
  424. {
  425. add += i;
  426. }
  427. else if (add >= 7)
  428. {
  429. add = 0;
  430. firstAdd++;
  431. }
  432. plc[0] = firstAdd.ToString();
  433. plc[1] = add.ToString();
  434. TempPlcAddress = $"M{plc[0]}.{plc[1]}";
  435. }
  436. vm.Add(new VariableMonitor()
  437. {
  438. Id = vm.Count+1,
  439. VarName = $"{item.Name}_{i + 1}",
  440. Notes = $"{notes}_{i + 1}",
  441. ModbusTcpAddress = $"{int.Parse(modadd) + i}",
  442. PLCAddress = TempPlcAddress,
  443. });
  444. }
  445. }
  446. }
  447. }
  448. else
  449. {
  450. var arrayRes = item.GetValue(status, null);
  451. if (arrayRes != null && arrayRes is Array arr)
  452. {
  453. for (int i = 0; i < arr.Length; i++)
  454. {
  455. var res = vm.FirstOrDefault(p => p.VarName == $"{item.Name}_{i + 1}");
  456. if (res == null)
  457. {
  458. vm.Add(new VariableMonitor()
  459. {
  460. Id = vm.Count+1,
  461. VarName = $"{item.Name}_{i + 1}",
  462. Notes = $"{notes}_{i + 1}",
  463. });
  464. }
  465. }
  466. }
  467. }
  468. }
  469. else
  470. {
  471. var res = vm.FirstOrDefault(p => p.VarName == item.Name);
  472. if (res == null)
  473. {
  474. vm.Add(new VariableMonitor()
  475. {
  476. Id = vm.Count+1,
  477. VarName = item.Name,
  478. Notes = notes,
  479. ModbusTcpAddress = modadd,
  480. PLCAddress = plcadd,
  481. });
  482. }
  483. }
  484. }
  485. }
  486. //监控列表排序
  487. //vm.OrderBy(p => p.VarName).ToList().ForEach(item => { variableMonitors.Add(item); });
  488. vm.ForEach(item => { variableMonitors.Add(item); });
  489. }
  490. /// <summary>
  491. /// 更新数据
  492. /// </summary>
  493. /// <param name="status"></param>
  494. public void UpdateValue(IStatus status)
  495. {
  496. if (status == null) return;
  497. foreach (var item in status.GetType().GetProperties())
  498. {
  499. if (item.CustomAttributes.Count() > 0)
  500. {
  501. if (item.PropertyType?.BaseType?.Name == "Array")
  502. {
  503. var arrayRes = item.GetValue(status);
  504. if (arrayRes != null && arrayRes is Array arr)
  505. {
  506. for (int i = 0; i < arr.Length; i++)
  507. {
  508. int index = Array.FindIndex(variableMonitors.ToArray(), p => p.VarName == $"{item.Name}_{i + 1}");
  509. if (index >= 0 && index < variableMonitors.Count)
  510. {
  511. variableMonitors.ElementAt(index).CurrentValue = arr.GetValue(i)?.ToString();
  512. }
  513. }
  514. }
  515. }
  516. else
  517. {
  518. int index = Array.FindIndex(variableMonitors.ToArray(), p => p.VarName == item.Name);
  519. if (index >= 0 && index < variableMonitors.Count)
  520. {
  521. variableMonitors.ElementAt(index).CurrentValue = item.GetValue(status)?.ToString();
  522. }
  523. }
  524. }
  525. }
  526. }
  527. public abstract void DoMain();
  528. public abstract void Stop();
  529. /// <summary>
  530. /// 数据读取
  531. /// </summary>
  532. public abstract void ReadData();
  533. /// <summary>
  534. /// 主流程控制
  535. /// </summary>
  536. public abstract void MainTask();
  537. /// <summary>
  538. /// 复位程序
  539. /// </summary>
  540. public abstract void ResetProgram();
  541. /// <summary>
  542. /// 模拟订单
  543. /// </summary>
  544. public abstract void SimOrder();
  545. public object GetError()
  546. {
  547. return new { data = Error };
  548. }
  549. public object GetLog()
  550. {
  551. return new { data = Log };
  552. }
  553. public object GetVariableMonitor()
  554. {
  555. return new { data = variableMonitors };
  556. }
  557. /// <summary>
  558. /// 获取某个对象中的属性值
  559. /// </summary>
  560. /// <param name="info"></param>
  561. /// <param name="field"></param>
  562. /// <returns></returns>
  563. public object GetPropertyValue(object info, string field)
  564. {
  565. if (info == null) return null;
  566. Type t = info.GetType();
  567. IEnumerable<System.Reflection.PropertyInfo> property = from pi in t.GetProperties() where pi.Name.ToLower() == field.ToLower() select pi;
  568. return property.First().GetValue(info, null);
  569. }
  570. }
  571. }