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.
 
 

667 lines
23 KiB

  1. #define test
  2. using BPA.Message;
  3. using HBLConsole.Communication;
  4. using HBLConsole.Factory;
  5. using HBLConsole.Interface;
  6. using HBLConsole.Model;
  7. using HBLConsole.Service;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Linq;
  11. using System.Text;
  12. using System.Threading;
  13. using System.Threading.Tasks;
  14. using BPA.Message.Enum;
  15. namespace HBLConsole.Business.Devices
  16. {
  17. public class MORKS : IBusiness
  18. {
  19. private volatile static MORKS _Instance;
  20. public static MORKS GetInstance => _Instance ?? (_Instance = new MORKS());
  21. private MORKS() { }
  22. public GVL.MORKS mORKS { get; set; } = new GVL.MORKS();
  23. /// <summary>
  24. /// 写入配方数据到 PLC
  25. /// </summary>
  26. private void WriteRecipeBoms()
  27. {
  28. List<ushort> recipeBoms = new List<ushort>();
  29. foreach (var item in Json<BatchingInfoPar>.GetInstance.Base.recipeBoms.RecipeIds)
  30. {
  31. foreach (var rec in item.Recipes)
  32. {
  33. recipeBoms.Add((ushort)rec);
  34. }
  35. }
  36. if (ModbusTcpHelper.GetInstance.Write(1100, WriteType.HoldingRegisters, recipeBoms.ToArray()))
  37. {
  38. MessageLog.GetInstance.Show("成功写入配方数据");
  39. }
  40. }
  41. public void Init()
  42. {
  43. //Modbus TCP连接成功
  44. ActionManagerment.GetInstance.Register(new Action(() =>
  45. {
  46. WriteRecipeBoms();
  47. ReadPlcData();
  48. DataParse();
  49. Main();
  50. //ResetProgram();
  51. }), "ConnectOk");
  52. //获取物料信息
  53. SimpleFactory.GetInstance.GetBatchingInfo();
  54. //Modbus Tcp 连接
  55. ModbusTcpHelper.GetInstance.ModbusTcpConnect("192.168.1.11", 508);
  56. //模拟订单
  57. SimOrder();
  58. Test();
  59. Heartbeat();
  60. MessageLog.GetInstance.Show("MORKS 设备初始化完成");
  61. }
  62. List<BatchingInfo> batchingInfos = new List<BatchingInfo>();
  63. private void Heartbeat()
  64. {
  65. HeartbeatReport.GetInstance.GetMessage = new Action(() =>
  66. {
  67. HeartbeatReport.GetInstance.deviceStatus.BatchingInfo = batchingInfos;
  68. #if test
  69. HeartbeatReport.GetInstance.deviceStatus.Healthy = DeviceHealthy.UnHealth;
  70. #else
  71. HeartbeatReport.GetInstance.deviceStatus.Healthy = AllowRun ? DeviceHealthy.UnHealth : DeviceHealthy.Health;
  72. #endif
  73. });
  74. }
  75. /// <summary>
  76. /// 测试程序
  77. /// </summary>
  78. private void Test()
  79. {
  80. #if test
  81. ThreadManagerment.GetInstance.StartLong(new Action(() =>
  82. {
  83. while (Json<MorkOrderPushPar>.GetInstance.Base.morkOrderPushes.Count > 0)
  84. {
  85. var result = Json<MorkOrderPushPar>.GetInstance.Base.morkOrderPushes.ElementAt(0);
  86. SimpleFactory.GetInstance.OrderChanged(result.OrderPush.SuborderId, ORDER_STATUS.COOKING);
  87. MessageLog.GetInstance.Show($"{result.OrderPush.GoodsName},{ORDER_STATUS.COOKING}");
  88. Thread.Sleep(5000);
  89. SimpleFactory.GetInstance.OrderChanged(result.OrderPush.SuborderId, ORDER_STATUS.COMPLETED_COOK);
  90. MessageLog.GetInstance.Show($"{result.OrderPush.GoodsName},{ORDER_STATUS.COMPLETED_COOK}");
  91. Thread.Sleep(5000);
  92. SimpleFactory.GetInstance.OrderChanged(result.OrderPush.SuborderId, ORDER_STATUS.COMPLETED_TAKE);
  93. MessageLog.GetInstance.Show($"{result.OrderPush.GoodsName},{ORDER_STATUS.COMPLETED_TAKE}");
  94. Thread.Sleep(5000);
  95. Json<MorkOrderPushPar>.GetInstance.Base.morkOrderPushes.RemoveAt(0);
  96. }
  97. Thread.Sleep(500);
  98. }), "test");
  99. #endif
  100. }
  101. private void Readbool(ushort startAddress, ushort len, Action<bool[]> action)
  102. {
  103. object result;
  104. result = ModbusTcpHelper.GetInstance.Read(startAddress, ReadType.Coils, len);
  105. if (result != null)
  106. {
  107. if (result is bool[] bools)
  108. {
  109. if (bools.Length == len)
  110. {
  111. action(bools);
  112. }
  113. }
  114. }
  115. }
  116. /// <summary>
  117. /// 复位程序
  118. /// </summary>
  119. private void ResetProgram()
  120. {
  121. ThreadManagerment.GetInstance.StartLong(new Action(() =>
  122. {
  123. if (RTrig.GetInstance("ResetProgram").Start(mORKS.DeviceIniting))
  124. {
  125. ThreadManagerment.GetInstance.StopTask("MainTask", new Action(() =>
  126. {
  127. AllowRun = false;
  128. TakeBowlId = string.Empty;
  129. IngredientsCompleteId = string.Empty;
  130. CookNodelId = new string[6] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, };
  131. RobotTaskInterlock = false;
  132. OutMealId = string.Empty;
  133. TakeBowlInterlock = false;
  134. TakeNoodleInterlock = false;
  135. OutNoodleing = false;
  136. Main();
  137. }));
  138. }
  139. Thread.Sleep(10);
  140. }), "ResetProgram");
  141. }
  142. /// <summary>
  143. /// 数据读取
  144. /// </summary>
  145. private void ReadPlcData()
  146. {
  147. ThreadManagerment.GetInstance.StartLong(new Action(() =>
  148. {
  149. Readbool(323, 3, new Action<bool[]>((o) =>
  150. {
  151. mORKS.RobotTakeNoodle = o[0];
  152. mORKS.RobotOutMeal = o[1];
  153. mORKS.MoveTurntable = o[2];
  154. }));
  155. Readbool(1120, 16, new Action<bool[]>((bools) =>
  156. {
  157. mORKS.InitComplete = bools[0];
  158. mORKS.TakeBowlIdle = bools[1];
  159. mORKS.TemperatureReached = bools[2];
  160. mORKS.AllowFallNoodle = bools[3];
  161. mORKS.RbTakeNoodleComplete = bools[4];
  162. mORKS.RbFallNoodleComplete = bools[5];
  163. mORKS.RbOutMealComplete = bools[6];
  164. mORKS.RobotIdle = bools[7];
  165. mORKS.TakeMealDetect = bools[8];
  166. mORKS.MissingBowl = bools[9];
  167. mORKS.DeviceIniting = bools[10];
  168. mORKS.TurntableLowerLimit = bools[11];
  169. mORKS.MissingBowlSignal2 = bools[12];
  170. mORKS.TurntableUpLimit = bools[13];
  171. mORKS.TurntableMoveInPlace = bools[15];
  172. }));
  173. Readbool(1136, 6, new Action<bool[]>((bools) =>
  174. {
  175. for (int i = 0; i < 6; i++)
  176. {
  177. mORKS.NoodleCookerStatus[i] = bools[i];
  178. }
  179. }));
  180. Readbool(1144, 6, new Action<bool[]>((bools) =>
  181. {
  182. for (int i = 0; i < 6; i++)
  183. {
  184. mORKS.CookNoodlesComplete[i] = bools[i];
  185. }
  186. }));
  187. Thread.Sleep(500);
  188. }), "ReadPLCData");
  189. }
  190. private void SimOrder()
  191. {
  192. ActionManagerment.GetInstance.Register(new Action(() =>
  193. {
  194. string subId = Guid.NewGuid().ToString();
  195. ushort NoodleLoc = 1;//(ushort)(new Random().Next(1, 5));
  196. ushort BolwLoc = (ushort)(new Random().Next(10, 11));
  197. mORKS.RBTakeNoodleTask.Enqueue(new GVL.OrderLocInfo() { Loc = NoodleLoc, SuborderId = subId });
  198. mORKS.TakeBowlTask.Enqueue(new GVL.OrderLocInfo() { Loc = BolwLoc, SuborderId = subId });
  199. MessageLog.GetInstance.Show($"添加订单:面条位置【{NoodleLoc}】,碗位置【{BolwLoc}】");
  200. }), "SimOrder");
  201. }
  202. /// <summary>
  203. /// 数据解析
  204. /// </summary>
  205. private void DataParse()
  206. {
  207. ActionManagerment.GetInstance.Register(new Action<object>((o) =>
  208. {
  209. if (o is MorkOrderPush morkOrderPush)
  210. {
  211. foreach (var item in morkOrderPush.GoodBatchings)
  212. {
  213. var res = Json<BatchingInfoPar>.GetInstance.Base.orderMaterialDelivery.BatchingInfo.FirstOrDefault(p => p.BatchingId == item.BatchingId);
  214. if (res != null)
  215. {
  216. if (ushort.TryParse(res.BatchingLoc, out ushort loc))
  217. {
  218. if (loc >= 1 && loc <= 5)
  219. {
  220. mORKS.RBTakeNoodleTask.Enqueue(new GVL.OrderLocInfo() { Loc = loc, SuborderId = morkOrderPush.SuborderId, BatchingId = res.BatchingId });
  221. }
  222. else if (loc >= 10 && loc <= 11)
  223. {
  224. mORKS.TakeBowlTask.Enqueue(new GVL.OrderLocInfo() { Loc = loc, SuborderId = morkOrderPush.SuborderId, RecipeNumber = (ushort)morkOrderPush.RecipeId });
  225. }
  226. }
  227. }
  228. }
  229. }
  230. }), "MorksParse");
  231. }
  232. #region 临时变量
  233. /// <summary>
  234. /// 允许运行
  235. /// </summary>
  236. bool AllowRun = false;
  237. /// <summary>
  238. /// 取碗订单ID
  239. /// </summary>
  240. string TakeBowlId = string.Empty;
  241. /// <summary>
  242. /// 配料完成订单ID
  243. /// </summary>
  244. string IngredientsCompleteId = string.Empty;
  245. /// <summary>
  246. /// 煮面口对应的订单ID
  247. /// </summary>
  248. string[] CookNodelId = new string[6] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, string.Empty, };
  249. /// <summary>
  250. /// //机器人任务互锁信号
  251. /// </summary>
  252. bool RobotTaskInterlock = false;
  253. /// <summary>
  254. /// 出餐订单ID
  255. /// </summary>
  256. string OutMealId = string.Empty;
  257. /// <summary>
  258. /// 取碗互锁信号
  259. /// </summary>
  260. bool TakeBowlInterlock = false;
  261. /// <summary>
  262. /// 取面互锁信号
  263. /// </summary>
  264. bool TakeNoodleInterlock = false;
  265. /// <summary>
  266. /// 出面中
  267. /// </summary>
  268. bool OutNoodleing = false;
  269. /// <summary>
  270. /// 允许取面
  271. /// </summary>
  272. bool AllowTakeNoodle = false;
  273. /// <summary>
  274. /// 转台位置轮询
  275. /// </summary>
  276. List<ushort> TurntableLoc = new List<ushort>();
  277. /// <summary>
  278. /// 转台互锁信号
  279. /// </summary>
  280. bool TurntableInterlock = false;
  281. #endregion
  282. private void Main()
  283. {
  284. ThreadManagerment.GetInstance.StartLong(new Action(() =>
  285. {
  286. AllowRun = mORKS.InitComplete && !mORKS.TemperatureReached;
  287. if (AllowRun)
  288. {
  289. TakeBowlTask();
  290. TakeNoodleTask();
  291. }
  292. OutNoodleTask();
  293. SingleDetect();
  294. TurntableControl();
  295. Thread.Sleep(100);
  296. }), "MainTask");
  297. }
  298. /// <summary>
  299. /// 取碗控制
  300. /// </summary>
  301. private void TakeBowlTask()
  302. {
  303. if (mORKS.TakeBowlTask.Count > 0 && !mORKS.TakeBowlIdle && !TakeBowlInterlock)
  304. {
  305. if (mORKS.TakeBowlTask.TryDequeue(out GVL.OrderLocInfo orderLocInfo))
  306. {
  307. TakeBowlId = orderLocInfo.SuborderId;
  308. TakeBowlControl(orderLocInfo.Loc);
  309. SetRecipeNumber(orderLocInfo.RecipeNumber);
  310. SimpleFactory.GetInstance.OrderChanged(TakeBowlId, ORDER_STATUS.COOKING);
  311. MessageLog.GetInstance.Show($"订单【{TakeBowlId}】执行取碗控制,位置:[{orderLocInfo.Loc}]");
  312. }
  313. TakeBowlInterlock = true;
  314. }
  315. }
  316. /// <summary>
  317. /// 转台控制
  318. /// </summary>
  319. private void TurntableControl()
  320. {
  321. if (mORKS.TurntableMoveInPlace && mORKS.InitComplete && !AllowTakeNoodle && mORKS.RBTakeNoodleTask.Count > 0)
  322. {
  323. if (mORKS.TurntableLowerLimit)
  324. {
  325. if (mORKS.RBTakeNoodleTask.Count > 0)
  326. {
  327. SetTurntableLoc(mORKS.RBTakeNoodleTask.ElementAt(0).Loc);
  328. MoveTurntable();
  329. }
  330. AllowTakeNoodle = true;
  331. TurntableLoc.Clear();
  332. MessageLog.GetInstance.Show("转台位置OK");
  333. }
  334. else
  335. {
  336. if (mORKS.RBTakeNoodleTask.Count > 0 && !TurntableInterlock)
  337. {
  338. var result = Json<BatchingInfoPar>.GetInstance.Base.orderMaterialDelivery.BatchingInfo.Where(p => p.BatchingId == mORKS.RBTakeNoodleTask.ElementAt(0).BatchingId).ToList();
  339. if (result != null)
  340. {
  341. foreach (var item in result)
  342. {
  343. if (ushort.TryParse(item.BatchingLoc, out ushort loc))
  344. {
  345. if (TurntableLoc.Contains(loc))
  346. {
  347. SetTurntableLoc(loc);
  348. MoveTurntable();
  349. TurntableInterlock = true;
  350. TurntableLoc.Add(loc);
  351. return;
  352. }
  353. }
  354. }
  355. MessageLog.GetInstance.Show("缺少指定物料");
  356. }
  357. }
  358. }
  359. }
  360. }
  361. /// <summary>
  362. /// 取面任务
  363. /// </summary>
  364. private void TakeNoodleTask()
  365. {
  366. //取面控制
  367. if (!RobotTaskInterlock)
  368. {
  369. if (mORKS.RobotIdle && AllowTakeNoodle && mORKS.TurntableMoveInPlace && !TakeNoodleInterlock && !OutNoodleing && mORKS.RBTakeNoodleTask.Count > 0)
  370. {
  371. if (mORKS.RBTakeNoodleTask.TryDequeue(out GVL.OrderLocInfo orderLocInfo))
  372. {
  373. //设置转台位置
  374. SetTurntableLoc(orderLocInfo.Loc);
  375. //设置倒面位置
  376. int loc = Array.FindIndex(mORKS.NoodleCookerStatus, p => p == false);//查找煮面炉空闲位置
  377. if (loc >= 0 && loc <= 5)
  378. {
  379. CookNodelId[loc] = orderLocInfo.SuborderId;
  380. SetFallNoodleLoc((ushort)(loc + 1));
  381. }
  382. //机器人开始取面
  383. RobotTakeNoodle();
  384. SimpleFactory.GetInstance.OrderChanged(orderLocInfo.SuborderId, ORDER_STATUS.COOKING);
  385. MessageLog.GetInstance.Show($"订单【{orderLocInfo.SuborderId}】,转台:[{orderLocInfo}],煮面栏:[{loc + 1}]");
  386. TakeNoodleInterlock = true;
  387. }
  388. }
  389. }
  390. }
  391. /// <summary>
  392. /// 出餐控制
  393. /// </summary>
  394. private void OutNoodleTask()
  395. {
  396. if (RobotTaskInterlock)
  397. {
  398. if (mORKS.AllowFallNoodle && mORKS.RobotIdle && !TakeNoodleInterlock && mORKS.RobotIdle && !mORKS.TakeMealDetect)
  399. {
  400. int loc = Array.FindIndex(CookNodelId, p => p == IngredientsCompleteId && p.Length > 0);
  401. if (loc >= 0 && loc <= 5)
  402. {
  403. if (mORKS.CookNoodlesComplete[loc])
  404. {
  405. SetTakeNoodleLoc((ushort)(loc + 1));
  406. RobotOutMeal();
  407. CookNoodleStatusReset((ushort)(loc + 1));
  408. ResetAllowFallNoodle();
  409. OutMealId = IngredientsCompleteId;
  410. IngredientsCompleteId = string.Empty;
  411. CookNodelId[loc] = string.Empty;
  412. MessageLog.GetInstance.Show($"{loc + 1}号位置出餐控制");
  413. OutNoodleing = true;
  414. }
  415. }
  416. }
  417. }
  418. }
  419. /// <summary>
  420. /// 信号检测
  421. /// </summary>
  422. private void SingleDetect()
  423. {
  424. //允许倒面信号检测
  425. if (RTrig.GetInstance("AllowFallNoodle").Start(mORKS.AllowFallNoodle))
  426. {
  427. IngredientsCompleteId = TakeBowlId;
  428. TakeBowlId = string.Empty;
  429. MessageLog.GetInstance.Show($"允许到面,{IngredientsCompleteId}");
  430. TakeBowlInterlock = false;
  431. }
  432. //出餐完成信号检测
  433. if (RTrig.GetInstance("CompleteChange").Start(mORKS.RbOutMealComplete))
  434. {
  435. SimpleFactory.GetInstance.OrderChanged(OutMealId, ORDER_STATUS.COMPLETED_COOK);
  436. MessageLog.GetInstance.Show($"订单【{OutMealId}】制作完成");
  437. OutNoodleing = false;
  438. }
  439. //取餐完成逻辑处理
  440. if (DelayRTrig.GetInstance("CompleteChange1").Start(mORKS.RbOutMealComplete && !mORKS.TakeMealDetect, 2))
  441. {
  442. SimpleFactory.GetInstance.OrderChanged(OutMealId, ORDER_STATUS.COMPLETED_TAKE);
  443. MessageLog.GetInstance.Show($"订单【{OutMealId}】取餐完成");
  444. var RemoveItem = Json<MorkOrderPushPar>.GetInstance.Base.morkOrderPushes.FirstOrDefault(p => p.OrderPush.SuborderId == OutMealId);
  445. if (RemoveItem != null)
  446. {
  447. Json<MorkOrderPushPar>.GetInstance.Base.morkOrderPushes.Remove(RemoveItem);
  448. }
  449. ResetCookComplete();
  450. OutMealId = string.Empty;
  451. }
  452. //机器人取面完成信号检测
  453. if (RTrig.GetInstance("TakeNoodleComplete").Start(mORKS.RbTakeNoodleComplete))
  454. {
  455. TakeNoodleInterlock = false;
  456. AllowTakeNoodle = false;
  457. MessageLog.GetInstance.Show("取面完成");
  458. TakeNoodleCompleteReset();
  459. }
  460. //转台到位检测
  461. if (RTrig.GetInstance("TurntableInPlace").Start(mORKS.TurntableMoveInPlace))
  462. {
  463. TurntableInterlock = false;
  464. }
  465. int OutMealRequstCount = mORKS.CookNoodlesComplete.Where(p => p == true).ToList().Count;
  466. int mlCount = mORKS.NoodleCookerStatus.Where(p => p == true).ToList().Count;
  467. RobotTaskInterlock = OutMealRequstCount > 0 && mORKS.AllowFallNoodle && (mlCount >= 2 || mORKS.RBTakeNoodleTask.Count == 0);
  468. }
  469. #region PLC 控制函数
  470. /// <summary>
  471. /// 转台移动
  472. /// </summary>
  473. private void MoveTurntable()
  474. {
  475. ModbusTcpHelper.GetInstance.Write(325, WriteType.Coils, true);
  476. }
  477. /// <summary>
  478. /// 取面完成复位
  479. /// </summary>
  480. private void TakeNoodleCompleteReset()
  481. {
  482. ModbusTcpHelper.GetInstance.Write(1124, WriteType.Coils, false);
  483. }
  484. /// <summary>
  485. /// 指定煮面口状态复位
  486. /// </summary>
  487. /// <param name="num"></param>
  488. private void CookNoodleStatusReset(int num)
  489. {
  490. if (num >= 1 && num <= 6)
  491. {
  492. ushort addRess = (ushort)(1136 + num - 1);
  493. ModbusTcpHelper.GetInstance.Write(addRess, WriteType.Coils, false);
  494. }
  495. MessageLog.GetInstance.Show($"{num}号煮面口占用复位");
  496. }
  497. /// <summary>
  498. /// 写配方编号
  499. /// </summary>
  500. /// <param name="num"></param>
  501. private void SetRecipeNumber(ushort num)
  502. {
  503. ModbusTcpHelper.GetInstance.Write(100, WriteType.HoldingRegisters, num);
  504. }
  505. /// <summary>
  506. /// 设置转台位置
  507. /// </summary>
  508. /// <param name="loc"></param>
  509. private void SetTurntableLoc(ushort loc)
  510. {
  511. ModbusTcpHelper.GetInstance.Write(101, WriteType.HoldingRegisters, loc);
  512. }
  513. /// <summary>
  514. /// 设置倒面位置
  515. /// </summary>
  516. /// <param name="loc"></param>
  517. private void SetFallNoodleLoc(ushort loc)
  518. {
  519. ModbusTcpHelper.GetInstance.Write(102, WriteType.HoldingRegisters, loc);
  520. }
  521. /// <summary>
  522. /// 设置取面位置
  523. /// </summary>
  524. /// <param name="loc"></param>
  525. private void SetTakeNoodleLoc(ushort loc)
  526. {
  527. ModbusTcpHelper.GetInstance.Write(103, WriteType.HoldingRegisters, loc);
  528. }
  529. /// <summary>
  530. /// 取碗控制
  531. /// </summary>
  532. /// <param name="loc"></param>
  533. private void TakeBowlControl(ushort loc)
  534. {
  535. if (loc == 10)
  536. {
  537. ModbusTcpHelper.GetInstance.Write(321, WriteType.Coils, true);
  538. }
  539. else if (loc == 11)
  540. {
  541. ModbusTcpHelper.GetInstance.Write(322, WriteType.Coils, true);
  542. }
  543. }
  544. /// <summary>
  545. /// 机器人取面
  546. /// </summary>
  547. private void RobotTakeNoodle()
  548. {
  549. ModbusTcpHelper.GetInstance.Write(323, WriteType.Coils, true);
  550. }
  551. /// <summary>
  552. /// 机器人取餐
  553. /// </summary>
  554. private void RobotOutMeal()
  555. {
  556. ModbusTcpHelper.GetInstance.Write(324, WriteType.Coils, true);
  557. var result = ModbusTcpHelper.GetInstance.Read(324, ReadType.Coils);
  558. if (result is bool res)
  559. while (!res)
  560. {
  561. ModbusTcpHelper.GetInstance.Write(324, WriteType.Coils, true);
  562. }
  563. }
  564. /// <summary>
  565. /// 制作完成信号复位
  566. /// </summary>
  567. private void ResetCookComplete()
  568. {
  569. ModbusTcpHelper.GetInstance.Write(1126, WriteType.Coils, false);
  570. }
  571. /// <summary>
  572. /// 复位允许取面信号
  573. /// </summary>
  574. private void ResetAllowFallNoodle()
  575. {
  576. ModbusTcpHelper.GetInstance.Write(1123, WriteType.Coils, false);
  577. }
  578. /// <summary>
  579. /// 设备初始化
  580. /// </summary>
  581. public async void DeviceInit()
  582. {
  583. ModbusTcpHelper.GetInstance.Write(320, WriteType.Coils, true);
  584. await Task.Delay(1000);
  585. ModbusTcpHelper.GetInstance.Write(320, WriteType.Coils, false);
  586. }
  587. #endregion
  588. }
  589. }