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.
 
 

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