机械臂cs.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. using Newtonsoft.Json;
  2. using ServiceCenter.Extensions;
  3. using ServiceCenter.Logs;
  4. using ServiceCenter.SqlSugars;
  5. using System.ComponentModel;
  6. using WCS.Core;
  7. using WCS.Entity;
  8. using WCS.Entity.Protocol.Protocol.Robot;
  9. using WCS.Entity.Protocol.Robot;
  10. using WCS.Entity.Protocol.Station;
  11. using WCS.WorkEngineering.Extensions;
  12. using WCS.WorkEngineering.WebApi.Controllers;
  13. using WCS.WorkEngineering.Worlds;
  14. using DeviceFlags = WCS.WorkEngineering.Extensions.DeviceFlags;
  15. using TaskStatus = WCS.Entity.TaskStatus;
  16. namespace WCS.WorkEngineering.Systems
  17. {
  18. /// <summary>
  19. /// 机械臂
  20. /// </summary>
  21. [BelongTo(typeof(RingWorld))]
  22. [Description("机械臂")]
  23. public class 机械臂 : DeviceSystem<Device<IRobot520, IRobot521, IRobot522>>
  24. {
  25. protected override bool ParallelDo => true;
  26. protected override bool SaveLogsToFile => true;
  27. /// <summary>
  28. /// 取货点设备集合
  29. /// </summary>
  30. private Dictionary<string, List<Device>> PickUpDevices = new();
  31. /// <summary>
  32. /// 放货设备
  33. /// </summary>
  34. private Dictionary<string, List<Device<IStation520, IStation521, IStation523>>> PutDevices = new();
  35. public 机械臂()
  36. {
  37. //获取所有的机械臂 集合
  38. var devices = Device.All.Where(v => v.HasFlag(DeviceFlags.Robot));
  39. //开始分配
  40. foreach (var robot in devices)
  41. {
  42. //取货设备
  43. PickUpDevices.Add(robot.Code, robot.Sources.Where(v => v.HasFlag(DeviceFlags.输送机)).Select(v => v).ToList());
  44. PutDevices.Add(robot.Code, robot.Targets.Where(v => v.HasFlag(DeviceFlags.输送机)).Select(v => new Device<IStation520, IStation521, IStation523>(v, World)).ToList());
  45. }
  46. }
  47. public override void Do(Device<IRobot520, IRobot521, IRobot522> obj)
  48. {
  49. #region 处理完成任务
  50. //判断DB520 完成任务确认清除信号 是否为1
  51. if (obj.Data.OkAck == 1 && obj.Data2 is { TaskFinishId1: 0, TaskFinishId2: 0 }) obj.Data.OkAck = 0;
  52. if (obj.Data2.TaskFinishId1 > 0 || obj.Data2.TaskFinishId2 > 0)
  53. {
  54. //处理完成的任务信息
  55. var tasks = new List<WCS_TaskInfo>();
  56. //开始处理
  57. SqlSugarHelper.Do(db =>
  58. {
  59. World.Log($"机械臂任务处理:开始--完成任务{obj.Data2.TaskFinishId1}--{obj.Data2.TaskFinishId2}", LogLevelEnum.Low);
  60. //根据DB521任务号获取对应任务
  61. var taskInfoList = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.ID == obj.Data2.TaskFinishId1 || v.ID == obj.Data2.TaskFinishId2).ToList();
  62. foreach (var task in taskInfoList.Where(task => task.Status == Entity.TaskStatus.StackerExecution))
  63. {
  64. //根据任务类型做不同的处理
  65. switch (task.Type)
  66. {
  67. case TaskType.SetPlate: //工字轮入库
  68. //完成任务
  69. task.Status = Entity.TaskStatus.Finish;
  70. task.EndTime = DateTime.Now;
  71. db.Default.Updateable(task).ExecuteCommand();
  72. task.AddWCS_TASK_DTL(db.Default, task.AddrTo, "入库任务结束");
  73. break;
  74. case TaskType.OutDepot:
  75. var pall = db.Default.Queryable<WCS_TaskInfo>().Where(x =>
  76. x.AddrTo == task.AddrTo && x.Type == TaskType.Delivery &&
  77. x.Status < TaskStatus.Finish).First() ?? throw new Exception($"未找到对应的托盘搬运任务,无法进行绑盘");
  78. task.Status = Entity.TaskStatus.ConveyorExecution;
  79. task.EditTime = DateTime.Now;
  80. db.Default.Updateable(task).ExecuteCommand();
  81. task.AddWCS_TASK_DTL(db.Default, task.AddrTo, "出库任务结束");
  82. break;
  83. case TaskType.TransferDepot:
  84. task.Status = Entity.TaskStatus.Finish;
  85. task.EndTime = DateTime.Now;
  86. db.Default.Updateable(task).ExecuteCommand();
  87. task.AddWCS_TASK_DTL(db.Default, task.AddrTo, "移库任务结束");
  88. break;
  89. }
  90. tasks.Add(task);
  91. }
  92. });
  93. if (!tasks.Any()) throw new KnownException("数据库提交事务错误", LogLevelEnum.High);
  94. // 写入信号
  95. obj.Data.OkAck = 1;
  96. World.Log($"机械臂任务处理:结束--完成任务{obj.Data2.TaskFinishId1}--{obj.Data2.TaskFinishId2}", LogLevelEnum.Mid);
  97. }
  98. #endregion 处理完成任务
  99. //robot是否可以下发任务
  100. if (obj.Data2.VoucherNo != obj.Data.VoucherNo) throw new KnownException($"凭证号不一致,DB520:{obj.Data.VoucherNo},DB521:{obj.Data2.VoucherNo}", LogLevelEnum.Mid);
  101. if (obj.Data2.RobotMode != RobotMode.Automatic) throw new KnownException($"robot处于{obj.Data2.RobotMode.GetDescription()}模式", LogLevelEnum.Low);
  102. if (obj.Data2.RunStatus != RobotRunStatus.Idle) throw new KnownException($"robot处于{obj.Data2.RunStatus.GetDescription()}状态", LogLevelEnum.High);
  103. //判断是否有任务是机器人执行中
  104. //再检查是否有等待执行的货物
  105. SqlSugarHelper.Do(db =>
  106. {
  107. //获取当前堆垛机的所有未完成任务
  108. var tasks = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.Status < Entity.TaskStatus.Finish && v.Device == obj.Entity.Code);
  109. //任务集合是否有处于堆垛机执行状态的任务
  110. if (tasks.Any(v => v.Status == Entity.TaskStatus.StackerExecution)) throw new KnownException($"有任务处于堆垛机执行状态", LogLevelEnum.High);
  111. });
  112. //获取入库次数
  113. var inQuantity = obj.Entity.GetFlag<int>("InQuantity");
  114. var inMaxQuantity = 3;
  115. //入库任务优先 或 上一个周期是出库任务并且出库任务无优先
  116. if (inQuantity <= inMaxQuantity) //入库任务
  117. {
  118. //判断本次优先执行楼层,并设置下次执行时优先楼层
  119. var floor = obj.Entity.GetFlag<int>("FloorIn");
  120. floor = floor % 2 + 1;
  121. obj.Entity.SetFlag("FloorIn", floor);
  122. obj.Entity.SetFlag("InQuantity", inQuantity + 1);
  123. //获取当前机械臂所有的取货站台
  124. var pickUpDevices = obj.Entity.Sources.Where(x => x.HasFlag(DeviceFlags.输送机)).Where(x => x.DeviceGroup.Any()).Select(
  125. x =>
  126. {
  127. var group = x.DeviceGroup.Select(s => new Device<IStation523, IStation524>(s, World)).ToList();
  128. return new Tuple<Device<IStation523, IStation524>, List<Device<IStation523, IStation524>>>(new Device<IStation523, IStation524>(x, World), group);
  129. }).ToList();
  130. if (!pickUpDevices.Any()) throw new KnownException($"机械臂{obj.Entity.Code}无取货路径点", LogLevelEnum.High);
  131. //获取取货设备的设备组中都是停止运行的
  132. var arrIn = pickUpDevices.Where(x => x.Item2.All(a => !a.Data.Status.HasFlag(StationStatus.Run)))
  133. .Where(x =>
  134. {
  135. var a = x.Item2
  136. .OrderBy(o => o.Entity.Code.ToShort()) //前两个设备中有取货任务的
  137. .Take(2)
  138. .Any(a => a.Data2.TaskNumber > 0 && a.Data.Status.HasFlag(StationStatus.PH_Status));
  139. var b = x.Item2
  140. .OrderBy(o => o.Entity.Code.ToShort()) //前两个设备中有取货任务的
  141. .First();
  142. return a && b.Data2.TaskNumber > 0 && b.Data.Status.HasFlag(StationStatus.PH_Status);
  143. })
  144. .ToList();
  145. if (!arrIn.Any()) throw new KnownException($"[{obj.Entity.Code}]等待入库任务输送到位", LogLevelEnum.Mid);
  146. //等待下发的任务信息
  147. var taskList = new List<Tuple<WCS_TaskInfo, Device<IStation523, IStation524>>>();
  148. SqlSugarHelper.Do(db =>
  149. {
  150. //跟据设备组中第一个设备任务号最小的一个先执行
  151. var devGroup = arrIn.MinBy(x => x.Item2.MinBy(o => o.Data2.TaskNumber).Data2.TaskNumber).Item2
  152. .Where(x => x.Data2.TaskNumber > 0 && x.Data.Status.HasFlag(StationStatus.PH_Status) /*&& x.Data.Status1.HasFlag(StationStatus1.IsLock)*/)
  153. .OrderByDescending(x => x.Entity.Code.ToShort())
  154. .Take(2);
  155. if (!devGroup.Any()) throw new KnownException($"无有效入库取货位", LogLevelEnum.High);
  156. foreach (var dev in devGroup)
  157. {
  158. var task = db.Default.Queryable<WCS_TaskInfo>().First(v => v.Type == TaskType.SetPlate && v.Status == Entity.TaskStatus.FinishOfShunt && dev.Data2.TaskNumber == v.ID);
  159. if (task == null) continue;
  160. var res = WmsApi.RingApplyStockInLoc(task.ID, task.Device, dev.Entity.Code, task.GoodsType);
  161. task.Status = Entity.TaskStatus.StackerExecution;
  162. task.AddrTo = res.ResData.CellNo;
  163. task.Line = res.ResData.Row;
  164. task.Col = res.ResData.Colomn;
  165. task.Layer = res.ResData.Layer;
  166. task.Depth = res.ResData.Row;
  167. task.LastInteractionPoint = dev.Entity.Code;
  168. task.SrmStation = dev.Entity.Code;
  169. task.Device = obj.Entity.Code;
  170. task.EditWho = "WCS";
  171. db.Default.Updateable(task).ExecuteCommand();
  172. task.AddWCS_TASK_DTL(db.Default, dev.Entity.Code, task.AddrTo, "任务下发机械臂执行");
  173. taskList.Add(new(task, dev));
  174. }
  175. });
  176. if (!taskList.Any()) return;
  177. switch (taskList.Count)
  178. {
  179. case 1:
  180. var task = taskList.FirstOrDefault().Item1;
  181. var dev = taskList.FirstOrDefault().Item2;
  182. //下发任务
  183. obj.Data.TaskNumber1 = task.ID;
  184. obj.Data.SLine1 = dev.Entity.Code.ToShort();
  185. obj.Data.SCol1 = 0;
  186. obj.Data.SLayer1 = 0;
  187. obj.Data.SDepth1 = 0;
  188. obj.Data.ELine1 = task.Line.ToShort();
  189. obj.Data.ECol1 = task.Col.ToShort();
  190. obj.Data.ELayer1 = task.Layer.ToShort();
  191. obj.Data.EDepth1 = task.Depth.ToShort();
  192. obj.Data.TaskNumber2 = 0;
  193. obj.Data.SLine2 = 0;
  194. obj.Data.SCol2 = 0;
  195. obj.Data.SLayer2 = 0;
  196. obj.Data.SDepth2 = 0;
  197. obj.Data.ELine2 = 0;
  198. obj.Data.ECol2 = 0;
  199. obj.Data.ELayer2 = 0;
  200. obj.Data.EDepth2 = 0;
  201. obj.Data.TaskSum = taskList.Count.ToShort();
  202. obj.Data.GoodsType = task.GoodsType switch
  203. {
  204. 18 => 1,
  205. 34 => 2,
  206. 50 => 3,
  207. _ => 0
  208. };
  209. obj.Data.TaskType = 3;
  210. obj.Data.VoucherNo++;
  211. break;
  212. case 2:
  213. taskList = taskList.OrderBy(x => x.Item1.Depth).ToList();
  214. //一工位取深度较大的任务
  215. var taskInfo = taskList[1];
  216. task = taskInfo.Item1;
  217. dev = taskInfo.Item2;
  218. obj.Data.TaskNumber1 = task.ID;
  219. obj.Data.SLine1 = dev.Entity.Code.ToShort();
  220. obj.Data.SCol1 = 0;
  221. obj.Data.SLayer1 = 0;
  222. obj.Data.SDepth1 = 0;
  223. obj.Data.ELine1 = task.Line.ToShort();
  224. obj.Data.ECol1 = task.Col.ToShort();
  225. obj.Data.ELayer1 = task.Layer.ToShort();
  226. obj.Data.EDepth1 = task.Depth.ToShort();
  227. //二工位取深度较少的值
  228. taskInfo = taskList[0];
  229. task = taskInfo.Item1;
  230. dev = taskInfo.Item2;
  231. obj.Data.TaskNumber2 = task.ID;
  232. obj.Data.SLine2 = dev.Entity.Code.ToShort();
  233. obj.Data.SCol2 = 0;
  234. obj.Data.SLayer2 = 0;
  235. obj.Data.SDepth2 = 0;
  236. obj.Data.ELine2 = task.Line.ToShort();
  237. obj.Data.ECol2 = task.Col.ToShort();
  238. obj.Data.ELayer2 = task.Layer.ToShort();
  239. obj.Data.EDepth2 = task.Depth.ToShort();
  240. obj.Data.TaskSum = taskList.Count.ToShort();
  241. obj.Data.GoodsType = task.GoodsType switch
  242. {
  243. 18 => 1,
  244. 34 => 2,
  245. 50 => 3,
  246. _ => 0
  247. };
  248. obj.Data.TaskType = 3;
  249. obj.Data.VoucherNo++;
  250. break;
  251. default:
  252. throw new KnownException($"无法执行多个任务", LogLevelEnum.Mid);
  253. }
  254. World.Log($"机械臂任务处理:下发入库任务{obj.Data.TaskNumber1}--{obj.Data.TaskNumber2}--{obj.Data.VoucherNo}", LogLevelEnum.Mid);
  255. }
  256. else //出库任务
  257. {
  258. //判断本次优先执行楼层,并设置下次执行时优先楼层
  259. var floor = obj.Entity.GetFlag<int>("FloorOut");
  260. floor = floor % 2 + 1;
  261. obj.Entity.SetFlag("FloorOut", floor);
  262. obj.Entity.SetFlag("InQuantity", 0);
  263. //获取当前堆垛机所有的取货站台
  264. var arrOut = PutDevices.First(v => v.Key == obj.Entity.Code).Value;
  265. if (!arrOut.Any()) throw new KnownException($"机械臂{obj.Entity.Code}无放货路径点", LogLevelEnum.High);
  266. //获取可以放货的设备集合
  267. arrOut = arrOut.Where(v => v.Data3.Status.HasFlag(StationStatus.PH_Status) && v.Data.TaskNumber == 0 && v.Data.GoodsEnd == 0).ToList();//有光电
  268. if (!arrOut.Any()) throw new KnownException($"[{obj.Entity.Code}]等待出库任务输送到位", LogLevelEnum.Mid);
  269. var taskInfoList = new List<WCS_TaskInfo>();
  270. var nextAdd = "";
  271. SqlSugarHelper.Do(db =>
  272. {
  273. //所有出库点设备号,即
  274. var allOutCode = arrOut.Select(v => v.Entity.Code).ToList();
  275. //两个工位同时码垛,并不会一个执行一次,只有一个托盘任务全部执行完毕,才会继续执行下一个,先生成任务的码垛工位会优先执行
  276. var taskInfos = db.Default.Queryable<WCS_TaskInfo>().Where(v => v.Type == TaskType.OutDepot && v.Status == Entity.TaskStatus.WaitingToExecute)
  277. .Where(v => allOutCode.Contains(v.SrmStation)).ToList();
  278. if (!taskInfos.Any()) throw new KnownException($"未找到站台{JsonConvert.SerializeObject(allOutCode)}可用的出库任务", LogLevelEnum.Mid);
  279. var srmStation = taskInfos.OrderBy(x => x.AddTime).First().SrmStation;
  280. taskInfos = taskInfos.Where(x => x.SrmStation == srmStation).OrderBy(x => x.ProdLine).ToList();
  281. if (taskInfos.FirstOrDefault().WarehouseCode.Contains("S")) //如果是南侧,翻转排序
  282. {
  283. taskInfos = taskInfos.OrderByDescending(x => x.ProdLine).ToList();
  284. }
  285. taskInfos = taskInfos.Take(2).ToList();
  286. if (taskInfos.Count == 2) //有两个任务
  287. {
  288. var minDepth = taskInfos!.MinBy(x => x.Depth);
  289. var maxDepth = taskInfos.MaxBy(x => x.Depth);
  290. //物料号不同时只执行一个任务 不同的轮子物料号也不同
  291. if (minDepth.MatCode != maxDepth.MatCode) taskInfos = taskInfos.OrderBy(x => x.ProdLine).Take(1).ToList();
  292. //深度之和等于6(机械臂当前无法同时执行两个三深度的取货任务)
  293. else if (taskInfos.Sum(x => x.Depth) == 6) taskInfos = taskInfos.OrderBy(x => x.ProdLine).Take(1).ToList();
  294. else if (taskInfos[0].SrmStation != taskInfos[1].SrmStation) taskInfos = taskInfos.OrderBy(x => x.SrmStation).Take(1).ToList();
  295. }
  296. foreach (var task in taskInfos)
  297. {
  298. task.Status = Entity.TaskStatus.StackerExecution;
  299. task.LastInteractionPoint = task.Device;
  300. task.StartTime = DateTime.Now;
  301. task.EditWho = "WCS";
  302. nextAdd = task.SrmStation;
  303. db.Default.Updateable(task).ExecuteCommand();
  304. task.AddWCS_TASK_DTL(db.Default, task.Device, task.SrmStation, "任务下发机械臂执行");
  305. taskInfoList.Add(task);
  306. }
  307. });
  308. if (!taskInfoList.Any()) return;
  309. switch (taskInfoList.Count)
  310. {
  311. case 1:
  312. var task = taskInfoList.FirstOrDefault();
  313. //下发任务
  314. obj.Data.TaskNumber1 = task.ID;
  315. obj.Data.SLine1 = task.Line.ToShort();
  316. obj.Data.SCol1 = task.Col.ToShort();
  317. obj.Data.SLayer1 = task.Layer.ToShort();
  318. obj.Data.SDepth1 = task.Depth.ToShort();
  319. obj.Data.ELine1 = nextAdd.ToShort();
  320. obj.Data.ECol1 = task.ProdLine;
  321. obj.Data.ELayer1 = 0;
  322. obj.Data.EDepth1 = 0;
  323. obj.Data.TaskNumber2 = 0;
  324. obj.Data.SLine2 = 0;
  325. obj.Data.SCol2 = 0;
  326. obj.Data.SLayer2 = 0;
  327. obj.Data.SDepth2 = 0;
  328. obj.Data.ELine2 = 0;
  329. obj.Data.ECol2 = 0;
  330. obj.Data.ELayer2 = 0;
  331. obj.Data.EDepth2 = 0;
  332. obj.Data.TaskSum = taskInfoList.Count.ToShort();
  333. obj.Data.GoodsType = task.GoodsType switch
  334. {
  335. 18 => 1,
  336. 34 => 2,
  337. 50 => 3,
  338. _ => 0
  339. };
  340. obj.Data.TaskType = 4;
  341. obj.Data.VoucherNo++;
  342. break;
  343. case 2:
  344. taskInfoList = taskInfoList.OrderBy(x => x.Depth).ToList();
  345. //一工位取深度较大的任务
  346. var taskInfo = taskInfoList[1];
  347. obj.Data.TaskNumber1 = taskInfo.ID;
  348. obj.Data.SLine1 = taskInfo.Line.ToShort();
  349. obj.Data.SCol1 = taskInfo.Col.ToShort();
  350. obj.Data.SLayer1 = taskInfo.Layer.ToShort();
  351. obj.Data.SDepth1 = taskInfo.Depth.ToShort();
  352. obj.Data.ELine1 = nextAdd.ToShort();
  353. obj.Data.ECol1 = taskInfo.WarehouseCode.Contains("S") ? taskInfoList.Max(x => x.ProdLine) : taskInfoList.Min(x => x.ProdLine);
  354. obj.Data.ELayer1 = 0;
  355. obj.Data.EDepth1 = 0;
  356. //二工位取深度较少的值
  357. taskInfo = taskInfoList[0];
  358. obj.Data.TaskNumber2 = taskInfo.ID;
  359. obj.Data.SLine2 = taskInfo.Line.ToShort();
  360. obj.Data.SCol2 = taskInfo.Col.ToShort();
  361. obj.Data.SLayer2 = taskInfo.Layer.ToShort();
  362. obj.Data.SDepth2 = taskInfo.Depth.ToShort();
  363. obj.Data.ELine2 = nextAdd.ToShort();
  364. obj.Data.ECol2 = taskInfo.WarehouseCode.Contains("S") ? taskInfoList.Min(x => x.ProdLine) : taskInfoList.Max(x => x.ProdLine);
  365. obj.Data.ELayer2 = 0;
  366. obj.Data.EDepth2 = 0;
  367. obj.Data.TaskSum = taskInfoList.Count.ToShort();
  368. obj.Data.GoodsType = taskInfo.GoodsType switch
  369. {
  370. 18 => 1,
  371. 34 => 2,
  372. 50 => 3,
  373. _ => 0
  374. };
  375. obj.Data.TaskType = 4;
  376. obj.Data.VoucherNo++;
  377. break;
  378. default:
  379. throw new KnownException($"无法执行多个任务", LogLevelEnum.Mid);
  380. }
  381. World.Log($"机械臂任务处理:下发出库任务{obj.Data.TaskNumber1}--{obj.Data.TaskNumber2}--{obj.Data.VoucherNo}", LogLevelEnum.Mid);
  382. }
  383. }
  384. public override bool Select(Device dev)
  385. {
  386. return dev.Code is "Robot1" or "Robot2" or "Robot3" or "Robot4" or "Robot5" or "Robot6";
  387. }
  388. }
  389. }