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

598 行
22 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using BPA.Message.Enum;
  4. using BPASmartClient.Device;
  5. using BPASmartClient.EventBus;
  6. using BPASmartClient.Model;
  7. using BPASmartClient.Peripheral;
  8. using static BPASmartClient.EventBus.EventBus;
  9. using BPASmartClient.Helper;
  10. using System.Threading;
  11. using BPASmartClient.Message;
  12. using BPA.Message;
  13. using System.Linq;
  14. using BPASmartClient.Model.PLC;
  15. using System.Threading.Tasks;
  16. using System.Reflection;
  17. namespace BPASmartClient.MorkS
  18. {
  19. public class Control : BaseDevice
  20. {
  21. public override DeviceClientType DeviceType => DeviceClientType.MORKS;
  22. GVL_MORKS mORKS = new GVL_MORKS();
  23. Alarm alarm = new Alarm();
  24. public override void DoMain()
  25. {
  26. ServerInit();
  27. DataParse();
  28. ActionManage.GetInstance.Register(new Action(() => { DeviceInit(); }), "InitDevice");
  29. }
  30. public override void ResetProgram()
  31. {
  32. mORKS = null;
  33. mORKS = new GVL_MORKS();
  34. }
  35. public override void Stop()
  36. {
  37. }
  38. private void ServerInit()
  39. {
  40. //物料信息
  41. EventBus.EventBus.GetInstance().Subscribe<MaterialDeliveryEvent>(DeviceId, delegate (IEvent @event, EventCallBackHandle callBack)
  42. {
  43. if (@event == null) return;
  44. if (@event is MaterialDeliveryEvent material)
  45. {
  46. orderMaterialDelivery = material.orderMaterialDelivery;
  47. }
  48. });
  49. //配方数据信息
  50. EventBus.EventBus.GetInstance().Subscribe<RecipeBomEvent>(DeviceId, delegate (IEvent @event, EventCallBackHandle callBack)
  51. {
  52. if (@event == null) return;
  53. if (@event is RecipeBomEvent recipe)
  54. {
  55. recipeBoms = recipe.recipeBoms;
  56. WriteRecipeBoms();
  57. }
  58. });
  59. }
  60. private void OrderChange(string subid, ORDER_STATUS oRDER_STATUS)
  61. {
  62. EventBus.EventBus.GetInstance().Publish(new OrderStatusChangedEvent() { Status = oRDER_STATUS, SubOrderId = subid, deviceClientType = DeviceType });
  63. }
  64. private void ReadData(string address, ushort len = 1, Action<bool[]> action = null)
  65. {
  66. EventBus.EventBus.GetInstance().Publish(new ReadModel() { DeviceId = DeviceId, Address = address, Length = len }, (o) =>
  67. {
  68. if (o != null && o.Length > 0 && o[0] is bool[] bools)
  69. {
  70. action(bools);
  71. }
  72. });
  73. }
  74. private void GetStatus(string key, Action<bool[]> action)
  75. {
  76. if (peripheralStatus.ContainsKey(key))
  77. {
  78. action((bool[])peripheralStatus[key]);
  79. }
  80. }
  81. public override void ReadData()
  82. {
  83. GetStatus("M550.0", new Action<bool[]>((bools) =>
  84. {
  85. alarm.MachineLeftLowTemperature = bools[0];
  86. alarm.MachineRightLowTemperature = bools[1];
  87. alarm.Supply1_LossBowl = bools[2];
  88. alarm.Supply2_LossBowl = bools[3];
  89. alarm.Supply1_ErrorOutBowl = bools[4];
  90. alarm.Supply2_ErrorOutBowl = bools[5];
  91. alarm.PushBowlCylinderError = bools[6];
  92. alarm.NoodleMacCommunicateError = bools[7];
  93. alarm.DosingMacCommunicateError = bools[8];
  94. alarm.RobotMacCommunicateError = bools[9];
  95. alarm.RobotInitError = bools[11];
  96. alarm.RobotUrgentStop = bools[12];
  97. alarm.RobotNotInRemoteMode = bools[13];
  98. alarm.RobotNotInReady = bools[14];
  99. alarm.RobotSelfInException = bools[15];
  100. }));
  101. GetStatus("M0.3", new Action<bool[]>((bools) =>
  102. {
  103. mORKS.RobotTakeNoodle = bools[0];
  104. mORKS.RobotOutMeal = bools[1];
  105. mORKS.MoveTurntable = bools[2];
  106. }));
  107. GetStatus("M100.0", new Action<bool[]>((bools) =>
  108. {
  109. mORKS.InitComplete = bools[0];
  110. mORKS.TakeBowlIdle = bools[1];
  111. mORKS.TemperatureReached = bools[2];
  112. mORKS.AllowFallNoodle = bools[3];
  113. mORKS.RbTakeNoodleComplete = bools[4];
  114. mORKS.RbFallNoodleComplete = bools[5];
  115. mORKS.RbOutMealComplete = bools[6];
  116. mORKS.RobotIdle = bools[7];
  117. mORKS.TakeMealDetect = bools[8];
  118. mORKS.MissingBowl = bools[9];
  119. Initing = bools[10];
  120. mORKS.TurntableLowerLimit = bools[11];
  121. mORKS.MissingBowlSignal2 = bools[12];
  122. mORKS.TurntableUpLimit = bools[13];
  123. mORKS.FeedComplete = bools[14];
  124. mORKS.TurntableMoveInPlace = bools[15];
  125. }));
  126. GetStatus("M235.0", new Action<bool[]>((bools) =>
  127. {
  128. mORKS.Error = bools[0];
  129. }));
  130. GetStatus("M102.0", new Action<bool[]>((bools) =>
  131. {
  132. for (int i = 0; i < 6; i++)
  133. {
  134. mORKS.NoodleCookerStatus[i] = bools[i];
  135. }
  136. mORKS.Feeding = bools[6];
  137. }));
  138. GetStatus("M103.0", new Action<bool[]>((bools) =>
  139. {
  140. for (int i = 0; i < 6; i++)
  141. {
  142. mORKS.CookNoodlesComplete[i] = bools[i];
  143. }
  144. }));
  145. EventBus.EventBus.GetInstance().Publish(new ReadModel() { DeviceId = DeviceId, Address = "VW372", Length = 1 }, (o) =>
  146. {
  147. if (o != null && o.Length > 0 && o[0] is ushort value)
  148. {
  149. mORKS.TurntableFeedbackloc = value;
  150. }
  151. });
  152. }
  153. /// <summary>
  154. /// 数据解析
  155. /// </summary>
  156. private void DataParse()
  157. {
  158. EventBus.EventBus.GetInstance().Subscribe<DoOrderEvent>(DeviceId, delegate (IEvent @event, EventCallBackHandle callBackHandle)
  159. {
  160. if (@event == null) return;
  161. if (@event is DoOrderEvent order)
  162. {
  163. if (order.MorkOrder.GoodBatchings == null) return;
  164. OrderCount++;
  165. DeviceProcessLogShow($"接收到{OrderCount}次订单");
  166. foreach (var item in order.MorkOrder.GoodBatchings)
  167. {
  168. var res = orderMaterialDelivery?.BatchingInfo?.FirstOrDefault(p => p.BatchingId == item.BatchingId);
  169. if (res != null)
  170. {
  171. if (ushort.TryParse(res.BatchingLoc, out ushort loc))
  172. {
  173. if (loc >= 1 && loc <= 5)
  174. {
  175. if (mORKS.RBTakeNoodleTask.FirstOrDefault(p => p.SuborderId == order.MorkOrder.SuborderId) == null)
  176. mORKS.RBTakeNoodleTask.Enqueue(new OrderLocInfo() { Loc = ushort.Parse(res.BatchingLoc), SuborderId = order.MorkOrder.SuborderId, BatchingId = res.BatchingId });
  177. }
  178. else if (loc >= 10 && loc <= 11)
  179. {
  180. int index = 0;
  181. if (recipeBoms != null)
  182. {
  183. index = Array.FindIndex(recipeBoms.RecipeIds?.ToArray(), p => p.RecipeId == order.MorkOrder.RecipeId);
  184. index++;
  185. }
  186. if (mORKS.TakeBowlTask.FirstOrDefault(p => p.SuborderId == order.MorkOrder.SuborderId) == null)
  187. mORKS.TakeBowlTask.Enqueue(new OrderLocInfo()
  188. {
  189. Loc = ushort.Parse(res.BatchingLoc),
  190. SuborderId = order.MorkOrder.SuborderId,
  191. RecipeNumber = (index >= 1 && index <= 10) ? (ushort)index : (ushort)0
  192. });
  193. }
  194. }
  195. }
  196. }
  197. }
  198. });
  199. }
  200. public override void MainTask()
  201. {
  202. mORKS.AllowRun = mORKS.InitComplete;
  203. //IsHealth = mORKS.Error && mORKS.InitComplete;
  204. IsHealth = true;
  205. TakeBowlTask();
  206. TakeNoodleTask();
  207. OutNoodleTask();
  208. SingleDetect();
  209. TurntableControl();
  210. }
  211. /// <summary>
  212. /// 取碗控制
  213. /// </summary>
  214. private void TakeBowlTask()
  215. {
  216. if (mORKS.AllowRun && mORKS.TakeBowlTask.Count > 0 && !mORKS.TakeBowlIdle && !mORKS.TakeBowlInterlock)
  217. {
  218. if (mORKS.TakeBowlTask.TryDequeue(out OrderLocInfo orderLocInfo))
  219. {
  220. mORKS.TakeBowlId = orderLocInfo.SuborderId;
  221. TakeBowlControl(orderLocInfo.Loc);
  222. SetRecipeNumber(orderLocInfo.RecipeNumber);
  223. OrderChange(mORKS.TakeBowlId, ORDER_STATUS.COOKING);
  224. DeviceProcessLogShow($"订单【{ mORKS.TakeBowlId}】执行取碗控制,位置:[{orderLocInfo.Loc}]");
  225. }
  226. mORKS.TakeBowlInterlock = true;
  227. }
  228. }
  229. /// <summary>
  230. /// 转台控制
  231. /// </summary>
  232. private void TurntableControl()
  233. {
  234. //if (GeneralConfig.EnableLocalSimOrder)
  235. //{
  236. // //不做轮询,直接取面,模拟订单使用
  237. // if (mORKS.TurntableMoveInPlace && !mORKS.Feeding && mORKS.InitComplete && !mORKS.AllowTakeNoodle && mORKS.RBTakeNoodleTask.Count > 0)
  238. // {
  239. // if (mORKS.TurntableLowerLimit)
  240. // {
  241. // TurntableStart(mORKS.RBTakeNoodleTask.ElementAt(0).Loc);
  242. // mORKS.TurntableLocLists.Clear();
  243. // mORKS.AllowTakeNoodle = true;
  244. // DeviceProcessLogShow($"控制机器人去转台【{mORKS.RBTakeNoodleTask.ElementAt(0).Loc}】号位置取面");
  245. // }
  246. // }
  247. //}
  248. //else
  249. {
  250. //正常轮询
  251. if (mORKS.TurntableMoveInPlace && !mORKS.Feeding && mORKS.InitComplete && !mORKS.AllowTakeNoodle && mORKS.RBTakeNoodleTask.Count > 0)
  252. {
  253. var result = orderMaterialDelivery.BatchingInfo.Where(p => p.BatchingId == mORKS.RBTakeNoodleTask.ElementAt(0).BatchingId).ToList();
  254. if (result != null)
  255. {
  256. var res = result.FirstOrDefault(P => P.BatchingLoc == mORKS.TurntableFeedbackloc.ToString());
  257. if (mORKS.TurntableLowerLimit && res != null)
  258. {
  259. TurntableStart(mORKS.TurntableFeedbackloc);
  260. mORKS.TurntableLocLists.Clear();
  261. mORKS.AllowTakeNoodle = true;
  262. DeviceProcessLogShow($"控制机器人去转台【{mORKS.TurntableFeedbackloc}】号位置取面");
  263. }
  264. else
  265. {
  266. if (!mORKS.TurntableInterlock)
  267. {
  268. foreach (var item in result)
  269. {
  270. if (ushort.TryParse(item.BatchingLoc, out ushort loc))
  271. {
  272. if (mORKS.TurntableFeedbackloc != loc && !mORKS.TurntableLocLists.Contains(loc))
  273. {
  274. TurntableStart(loc);
  275. DeviceProcessLogShow($"没有物料检测的启动转台控制,转台位置:[{loc}]");
  276. break;
  277. }
  278. else if (mORKS.TurntableFeedbackloc == loc && !mORKS.TurntableLocLists.Contains(loc)) mORKS.TurntableLocLists.Add(loc);
  279. }
  280. }
  281. }
  282. }
  283. }
  284. else DeviceProcessLogShow("未找到可用的物料信息");
  285. }
  286. }
  287. //转台到位检测
  288. if (RTrig.GetInstance("TurntableInPlace").Start(mORKS.TurntableMoveInPlace && mORKS.CurrentLoc == mORKS.TurntableFeedbackloc))
  289. {
  290. mORKS.CurrentLoc = 0;
  291. mORKS.TurntableInterlock = false;
  292. DeviceProcessLogShow("转台到位检测");
  293. }
  294. //补料完成检测
  295. if (RTrig.GetInstance("FeedComplete").Start(mORKS.FeedComplete))
  296. {
  297. if (!mORKS.AllowTakeNoodle && mORKS.TurntableLocLists.Count > 0)
  298. {
  299. mORKS.TurntableLocLists.Clear();
  300. mORKS.TurntableInterlock = false;
  301. DeviceProcessLogShow("补料完成检测");
  302. }
  303. }
  304. }
  305. /// <summary>
  306. /// 取面任务
  307. /// </summary>
  308. private void TakeNoodleTask()
  309. {
  310. //取面控制
  311. if (mORKS.AllowRun && mORKS.RobotIdle && !mORKS.Feeding && !mORKS.RobotTaskInterlock && mORKS.AllowTakeNoodle && mORKS.TurntableMoveInPlace && !mORKS.TakeNoodleInterlock && !mORKS.OutNoodleing && mORKS.RBTakeNoodleTask.Count > 0)
  312. {
  313. int loc = Array.FindIndex(mORKS.NoodleCookerStatus, p => p == false);//查找煮面炉空闲位置
  314. if (loc >= 0 && loc <= 5)
  315. {
  316. if (mORKS.RBTakeNoodleTask.TryDequeue(out OrderLocInfo orderLocInfo))
  317. {
  318. mORKS.CookNodelId[loc] = orderLocInfo.SuborderId;
  319. SetFallNoodleLoc((ushort)(loc + 1));
  320. //机器人开始取面
  321. RobotTakeNoodle();
  322. OrderChange(orderLocInfo.SuborderId, ORDER_STATUS.COOKING);
  323. DeviceProcessLogShow($"订单【{orderLocInfo.SuborderId}】,机器人倒面至【{loc + 1}】号煮面栏");
  324. //写入煮面时间
  325. //List<ushort> values = new List<ushort>();
  326. //values.Add(Json<KeepDataBase>.Data.parSets.ElementAt(loc).Minute);
  327. //values.Add(Json<KeepDataBase>.Data.parSets.ElementAt(loc).Second);
  328. //ModbusTcpHelper.GetInstance.Write((ushort)ModbusTcpHelper.GetInstance.GetWordAddress($"VW{116 + (loc * 6)}"), WriteType.HoldingRegisters, values.ToArray());
  329. mORKS.TakeNoodleInterlock = true;
  330. }
  331. }
  332. }
  333. }
  334. /// <summary>
  335. /// 出餐控制
  336. /// </summary>
  337. private void OutNoodleTask()
  338. {
  339. if (mORKS.AllowFallNoodle && mORKS.RobotTaskInterlock && !mORKS.TakeNoodleInterlock && mORKS.RobotIdle && !mORKS.TakeMealDetect)
  340. {
  341. int loc = Array.FindIndex(mORKS.CookNodelId, p => p == mORKS.IngredientsCompleteId && p.Length > 0);
  342. if (loc >= 0 && loc <= 5)
  343. {
  344. if (mORKS.CookNoodlesComplete[loc])
  345. {
  346. SetTakeNoodleLoc((ushort)(loc + 1));
  347. RobotOutMeal();
  348. CookNoodleStatusReset((ushort)(loc + 1));
  349. ResetAllowFallNoodle();
  350. mORKS.OutMealId = mORKS.IngredientsCompleteId;
  351. mORKS.IngredientsCompleteId = string.Empty;
  352. mORKS.CookNodelId[loc] = string.Empty;
  353. DeviceProcessLogShow($"{loc + 1}号位置出餐控制");
  354. mORKS.OutNoodleing = true;
  355. }
  356. }
  357. }
  358. }
  359. /// <summary>
  360. /// 信号检测
  361. /// </summary>
  362. private void SingleDetect()
  363. {
  364. //允许倒面信号检测
  365. if (RTrig.GetInstance("AllowFallNoodle").Start(mORKS.AllowFallNoodle))
  366. {
  367. mORKS.IngredientsCompleteId = mORKS.TakeBowlId;
  368. mORKS.TakeBowlId = string.Empty;
  369. DeviceProcessLogShow($"碗到位,允许到面,{mORKS.IngredientsCompleteId}");
  370. mORKS.TakeBowlInterlock = false;
  371. }
  372. //出餐完成信号检测
  373. if (RTrig.GetInstance("CompleteChange").Start(mORKS.RbOutMealComplete))
  374. {
  375. OrderChange(mORKS.OutMealId, ORDER_STATUS.COMPLETED_COOK);
  376. DeviceProcessLogShow($"订单【{mORKS.OutMealId}】制作完成");
  377. mORKS.OutNoodleing = false;
  378. }
  379. //取餐完成逻辑处理
  380. if (DelayRTrig.GetInstance("CompleteChange1").Start(mORKS.RbOutMealComplete && !mORKS.TakeMealDetect, 2))
  381. {
  382. OrderChange(mORKS.OutMealId, ORDER_STATUS.COMPLETED_TAKE);
  383. DeviceProcessLogShow($"订单【{mORKS.OutMealId}】取餐完成");
  384. ResetCookComplete();
  385. mORKS.OutMealId = string.Empty;
  386. }
  387. //机器人取面完成信号检测
  388. if (RTrig.GetInstance("TakeNoodleComplete").Start(mORKS.RbTakeNoodleComplete))
  389. {
  390. mORKS.TakeNoodleInterlock = false;
  391. mORKS.AllowTakeNoodle = false;
  392. mORKS.TurntableInterlock = false;
  393. DeviceProcessLogShow("机器人取面完成信号检测");
  394. TakeNoodleCompleteReset();
  395. }
  396. int OutMealRequstCount = mORKS.CookNoodlesComplete.Where(p => p == true).ToList().Count;
  397. int mlCount = mORKS.NoodleCookerStatus.Where(p => p == true).ToList().Count;
  398. mORKS.RobotTaskInterlock = OutMealRequstCount > 0 && mORKS.AllowFallNoodle && (mlCount >= 2 || mORKS.RBTakeNoodleTask.Count == 0);
  399. }
  400. #region PLC 控制函数
  401. private void WriteData(string address, object value)
  402. {
  403. EventBus.EventBus.GetInstance().Publish(new WriteModel() { DeviceId = DeviceId, Address = address, Value = value });
  404. }
  405. /// <summary>
  406. /// 写入配方数据到 PLC
  407. /// </summary>
  408. private void WriteRecipeBoms()
  409. {
  410. List<ushort> recipeBoms = new List<ushort>();
  411. if (this.recipeBoms == null) return;
  412. foreach (var item in this.recipeBoms.RecipeIds)
  413. {
  414. foreach (var rec in item.Recipes)
  415. {
  416. recipeBoms.Add((ushort)rec);
  417. }
  418. }
  419. if (recipeBoms.Count > 0)
  420. {
  421. //配方数据地址范围:VW2000 - VW2278
  422. WriteData("VW2000", recipeBoms.ToArray());
  423. }
  424. else { DeviceProcessLogShow("配方数据为空"); }
  425. }
  426. /// <summary>
  427. /// 取面完成复位
  428. /// </summary>
  429. private void TakeNoodleCompleteReset()
  430. {
  431. WriteData("M100.4", false);
  432. }
  433. /// <summary>
  434. /// 指定煮面口状态复位
  435. /// </summary>
  436. /// <param name="num"></param>
  437. private void CookNoodleStatusReset(int num)
  438. {
  439. if (num >= 1 && num <= 6)
  440. {
  441. WriteData($"102.{num - 1}", false);
  442. DeviceProcessLogShow($"{num}号煮面口占用复位");
  443. }
  444. }
  445. /// <summary>
  446. /// 写配方编号
  447. /// </summary>
  448. /// <param name="num"></param>
  449. private void SetRecipeNumber(ushort num)
  450. {
  451. WriteData("VW0", num);
  452. }
  453. /// <summary>
  454. /// 启动转台
  455. /// </summary>
  456. /// <param name="loc"></param>
  457. private void TurntableStart(ushort loc)
  458. {
  459. mORKS.CurrentLoc = loc;
  460. mORKS.TurntableInterlock = true;
  461. mORKS.TurntableLocLists.Add(loc);
  462. WriteData("VW2", loc);
  463. WriteData("M0.5", true);
  464. }
  465. /// <summary>
  466. /// 设置倒面位置
  467. /// </summary>
  468. /// <param name="loc"></param>
  469. private void SetFallNoodleLoc(ushort loc)
  470. {
  471. WriteData("VW4", loc);
  472. }
  473. /// <summary>
  474. /// 设置取面位置
  475. /// </summary>
  476. /// <param name="loc"></param>
  477. private void SetTakeNoodleLoc(ushort loc)
  478. {
  479. WriteData("VW6", loc);
  480. }
  481. /// <summary>
  482. /// 取碗控制
  483. /// </summary>
  484. /// <param name="loc"></param>
  485. private void TakeBowlControl(ushort loc)
  486. {
  487. if (loc == 10)//小碗
  488. {
  489. WriteData("M0.1", true);
  490. }
  491. else if (loc == 11)//大碗
  492. {
  493. WriteData("M0.2", true);
  494. }
  495. }
  496. /// <summary>
  497. /// 机器人取面
  498. /// </summary>
  499. private void RobotTakeNoodle()
  500. {
  501. WriteData("M0.3", true);
  502. }
  503. /// <summary>
  504. /// 机器人取餐
  505. /// </summary>
  506. private void RobotOutMeal()
  507. {
  508. WriteData("M0.4", true);
  509. }
  510. /// <summary>
  511. /// 制作完成信号复位
  512. /// </summary>
  513. private void ResetCookComplete()
  514. {
  515. WriteData("M100.6", false);
  516. }
  517. /// <summary>
  518. /// 复位允许取面信号
  519. /// </summary>
  520. private void ResetAllowFallNoodle()
  521. {
  522. WriteData("M100.3", false);
  523. }
  524. /// <summary>
  525. /// 设备初始化
  526. /// </summary>
  527. public async void DeviceInit()
  528. {
  529. WriteData("M0.0", true);
  530. await Task.Delay(1000);
  531. WriteData("M0.0", false);
  532. }
  533. #endregion
  534. }
  535. }