SRMWork.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. using DBHelper;
  2. using Microsoft.EntityFrameworkCore;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using WCS.Core;
  7. using WCS.Entity;
  8. using WCS.Entity.Protocol;
  9. using WCS.Entity.Protocol.SRM;
  10. using WCS.Service.Entity;
  11. using WCS.Service.Extensions;
  12. using WCS.Service.Handlers;
  13. using WCS.Service.Helpers;
  14. using WCS.Service.Log;
  15. namespace WCS.Service.Works.SRM
  16. {
  17. [WorkTitle(typeof(SRMHandler), "堆垛机")]
  18. internal class SRMWork : DeviceWork<SRMDevice>
  19. {
  20. //月台发货需要的设备
  21. private StationDevice[] DockDevs;
  22. public SRMWork()
  23. {
  24. //只取设备组 设备过多,先写程序后填写
  25. DockDevs = new string[]
  26. {
  27. }.Select(v => Device.Find(v)).SelectMany(v => v.DEVICEGROUP).Select(v => v.MEMBER.Create<StationDevice>()).ToArray();
  28. }
  29. protected override void Do(SRMDevice obj)
  30. {
  31. obj.EX(obj =>
  32. {
  33. var deviceCode = obj.Entity.CODE;
  34. //先检查堆垛机是否报警
  35. if (obj.Data3.SCAlarm != 0)
  36. {
  37. if (obj.Entity.WakeupOn(5000)) WMS.DevInfo(obj.Entity.CODE, obj.Data3.SCAlarm.ToString());
  38. InfoLog.INFO_SRMALARM($"{obj.Entity.CODE}-{obj.Data3.SCAlarm}");
  39. return;
  40. }
  41. if (obj.Data.FinishedACK_1 == 1 || obj.Data.FinishedACK_2 == 1) throw new WarnException($"堆垛机完成任务WCS反馈信号未清除");
  42. if (obj.Data2.VoucherNo_1 != obj.Data.VoucherNo_1 || obj.Data2.VoucherNo_2 != obj.Data.VoucherNo_2) throw new WarnException($"等待执行{obj.Data.TaskID_1}-{obj.Data.TaskID_2}");
  43. //处理堆垛机已完成的任务
  44. if (obj.Data2.FinishedTask_1 != 0 || obj.Data2.FinishedTask_2 != 0)
  45. {
  46. InfoLog.INFO_SRMINFO($"开始完成任务:[{obj.Entity.CODE}]-{obj.Data2.FinishedTask_1}-{obj.Data2.FinishedTask_2}");
  47. obj.FinishedTaskHandle();
  48. InfoLog.INFO_SRMINFO($"完成任务处理结束:[{obj.Entity.CODE}]-{obj.Data2.FinishedTask_1}-{obj.Data2.FinishedTask_2}");
  49. return;
  50. }
  51. if (obj.Data2.SRMMode != SCMode.远程) return;
  52. if (obj.Data2.SRMStatus != SCRunStatus.空闲) return;
  53. var isTransfer = new List<WCS_TASK>(); //是否有移库任务
  54. WCS_TASK enterPriority = new WCS_TASK(), outPriority = new WCS_TASK(); //出入库优先级任务
  55. //再检查是否有等待执行的货物
  56. DB.Do(db =>
  57. {
  58. var task = db.Default.Set<WCS_TASK>().Where(v => v.DEVICE == obj.Entity.CODE).Where(v => v.STATUS == TaskStatus.堆垛机执行).FirstOrDefault();
  59. if (task != null) throw new WarnException($"[{deviceCode}]有正在执行的任务:[{task.ID}]");
  60. //属于当前堆垛机未执行的移库任务
  61. isTransfer = db.Default.Set<WCS_TASK>().AsNoTracking().Where(v => v.DEVICE == obj.Entity.CODE && v.TYPE == TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行).ToList();
  62. //判断是否存在调整优先级任务,存在初始化isTransfer值 让本次执行优先任务
  63. if (db.Default.Set<WCS_TASK>().AsNoTracking().Any(v => v.DEVICE == obj.Entity.CODE && v.TYPE != TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行 && v.Priority > 0))
  64. isTransfer = new List<WCS_TASK>();
  65. enterPriority = db.Default.Set<WCS_TASK>().Where(v => v.DEVICE == obj.Entity.CODE && v.TYPE == TaskType.入库 && v.STATUS < TaskStatus.堆垛机执行)
  66. .OrderByDescending(v => v.Priority).FirstOrDefault();
  67. outPriority = db.Default.Set<WCS_TASK>().Where(v => v.DEVICE == obj.Entity.CODE && v.TYPE == TaskType.出库 && v.STATUS < TaskStatus.堆垛机执行)
  68. .OrderByDescending(v => v.Priority).FirstOrDefault();
  69. });
  70. //最后一个是否是出库任务
  71. var LastIsOut = obj.Entity.Get<bool>("LastIsOut");
  72. obj.Entity.Set("LastIsOut", !LastIsOut);
  73. if (isTransfer.Count > 0) //防止因为无当前堆垛机移库任务导致无法执行其他类型任务
  74. {
  75. #region 移库
  76. DB.Do(db =>
  77. {
  78. //获取当前堆垛机未执行的任务的组ID
  79. var taskGroupKey = db.Default.Set<WCS_TASK>().Where(v => v.DEVICE == obj.Entity.CODE && v.TYPE == TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行).OrderBy(p => p.CREATETIME).FirstOrDefault().TaskGroupKey;
  80. //通过任务的组ID找到本组的所有任务
  81. var tasks = db.Default.Set<WCS_TASK>().Where(v => v.TaskGroupKey == taskGroupKey);
  82. var taskList = tasks.Select(v => v.Create<Task>());
  83. foreach (var item in taskList)
  84. {
  85. }
  86. //双工位 货架列单数为一工位取放点 货架列双数为二工位取放点
  87. foreach (var task in tasks)
  88. {
  89. var addrFrom = task.ADDRFROM.Split("-");
  90. var addrTo = task.ADDRTO.Split("-");
  91. var oldTaskSTATUS = task.STATUS;
  92. task.STARTTIME = DateTime.Now;
  93. task.UPDATETIME = DateTime.Now;
  94. task.STATUS = WCS.Entity.TaskStatus.堆垛机执行;
  95. task.DEVICE = obj.Entity.CODE;
  96. db.Default.SaveChanges();
  97. Uploader.Upload(db);
  98. task.CreateStatusLog(db, $"状态由[{oldTaskSTATUS}]变更为[{task.STATUS}]", this.GetType());
  99. if (addrFrom[2].ToShort().OddNumberOrEven())
  100. {
  101. obj.Data.TaskID_1 = task.ID;
  102. obj.Data.SLine_1 = addrFrom[0].ToShort();
  103. obj.Data.SCol_1 = addrFrom[1].ToShort();
  104. obj.Data.SLayer_1 = addrFrom[2].ToShort();
  105. obj.Data.ELine_1 = addrTo[0].ToShort();
  106. obj.Data.ECol_1 = addrTo[1].ToShort();
  107. obj.Data.ELayer_1 = addrTo[2].ToShort();
  108. obj.Data.VoucherNo_1++;
  109. }
  110. else
  111. {
  112. obj.Data.TaskID_2 = task.ID;
  113. obj.Data.SLine_2 = addrFrom[0].ToShort();
  114. obj.Data.SCol_2 = addrFrom[1].ToShort();
  115. obj.Data.SLayer_2 = addrFrom[2].ToShort();
  116. obj.Data.ELine_2 = addrTo[0].ToShort();
  117. obj.Data.ECol_2 = addrTo[1].ToShort();
  118. obj.Data.ELayer_2 = addrTo[2].ToShort();
  119. obj.Data.VoucherNo_2++;
  120. }
  121. }
  122. });
  123. #endregion 移库
  124. }
  125. else if (LastIsOut)
  126. {
  127. #region 入库
  128. if (enterPriority != null && outPriority != null && outPriority.Priority > enterPriority.Priority) return;
  129. var arrIn = obj.GetPickPoint()
  130. .Where(v => Device.Where(d => d.IsConv()).Select(d => d.Device<IStation521>()).Where(d => d.Data.Goodsend == v.Entity.Code()).Any()) //有正在前往取货点的任务
  131. .ToList();
  132. if (!arrIn.Any()) return; //当前堆垛机无入库任务
  133. //入库口设备信息 找一个有任务有光电且不在运行状态位的取货点 如果找不到代表任务还在输送途中
  134. var station = arrIn.OrderBy(v => v.Data2.Tasknum > 0 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位) ? 0 : 1)
  135. .ThenBy(v => v.UpdateTime)
  136. .FirstOrDefault() ?? throw new WarnException($"[{deviceCode}]等待入库任务输送到位");
  137. //根据上述筛选条件筛选出来的入库任务 找到对应的设备组
  138. var item = Device.Where(v => v.DEVICEGROUP.Any(p => p.MEMBER.CODE == station.Entity.CODE)).Single();
  139. //对数据进行排序,根据CAD图纸规划,取货点两个设备中设备号较大的一个一定对应到堆垛机的一工位货叉
  140. //因此按照降序进行排序,可以保证数组中第一个一定是放到堆垛机一工位的数据
  141. var devs = item.DEVICEGROUP.Select(v => v.MEMBER)
  142. .Select(v => v.Create<StationDevice>())
  143. .OrderByDescending(v => v.Entity.CODE)
  144. .ToArray();
  145. var finishTaskList = new List<FinishTaskList<int>>(); //成功分配货位的任务
  146. //取巷道
  147. DB.Do(db =>
  148. {
  149. //检测有效任务数与实际任务是是否相等
  150. var validDev = devs.Where(v => v.Data2.Tasknum > 10000 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位));
  151. if (!validDev.Any()) throw new DoException("无有效入库任务");
  152. var tasknum = db.Default.Set<WCS_TASK>().FirstOrDefault(v => v.ID == validDev.First().Data2.Tasknum);
  153. var taskList = db.Default.Set<WCS_TASK>().Count(v => v.TaskGroupKey == tasknum.TaskGroupKey);
  154. if (validDev.Count() != taskList) throw new WarnException($"任务数量不匹配,设备-{validDev.Count()},WCS-{taskList}");
  155. foreach (var dev in validDev)
  156. {
  157. var task = db.Default.Set<WCS_TASK>().FirstOrDefault(v => v.STATUS < TaskStatus.堆垛机执行 && v.ID == dev.Data2.Tasknum) ?? throw new WarnException($"设备有光电有任务且不在运行状态,但WCS找不到任务{dev.Data2.Tasknum}");
  158. var oldTask = task.STATUS;
  159. var tunnel = dev.Entity.ROUTES.First().NEXT.CODE;
  160. I_WCS_GetWareCellResponse loc;
  161. //判定当前设备对应的堆垛机工位
  162. if (dev.Entity.CODE == devs[0].Entity.CODE) loc = WMS.GetLocalIn(task.WMSTASK, tunnel, dev.Entity.CODE, Entity.WareCellForkNum.货叉1); //一工位
  163. else if (dev.Entity.CODE == devs[1].Entity.CODE) loc = WMS.GetLocalIn(task.WMSTASK, tunnel, dev.Entity.CODE, Entity.WareCellForkNum.货叉2); //2工位
  164. else throw new WarnException($"设备{dev.Entity.CODE}无法对应至堆垛机的任一一个工位");
  165. task.UPDATETIME = DateTime.Now;
  166. task.STATUS = TaskStatus.堆垛机执行;
  167. task.ADDRTO = string.Format("{0}-{1}-{2}", loc.Row, loc.Colomn, loc.Layer);
  168. task.DEVICE = obj.Entity.CODE;
  169. task.TUNNEL = tunnel;
  170. task.CreateStatusLog(db, $"状态由{oldTask}变更至{task.STATUS}", this.GetType());
  171. finishTaskList.Add(new FinishTaskList<int>(task.ID, dev));
  172. }
  173. db.Default.SaveChanges();
  174. });
  175. DB.Do(db =>
  176. {
  177. foreach (var finish in finishTaskList)
  178. {
  179. var task = db.Default.Set<WCS_TASK>().Find(finish.FinishCode);
  180. var addTo = task.ADDRTO.Split("-");
  181. if (finish.Station.Entity.CODE == devs[0].Entity.CODE) //一工位
  182. {
  183. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{obj.Entity.CODE}]1工位-开始:[{obj.Data.TaskID_1}][{obj.Data.SLine_1}][{obj.Data.SCol_1}][{obj.Data.SLayer_1}][{obj.Data.ELine_1}][{obj.Data.VoucherNo_1}]");
  184. obj.Data.TaskID_1 = task.ID;
  185. obj.Data.SLine_1 = finish.Station.Entity.CODE.ToShort();
  186. obj.Data.SCol_1 = 0;
  187. obj.Data.SLayer_1 = 0;
  188. obj.Data.ELine_1 = addTo[0].ToShort();
  189. obj.Data.ECol_1 = addTo[1].ToShort();
  190. obj.Data.ELayer_1 = addTo[2].ToShort();
  191. obj.Data.RES1_1 = finishTaskList.Count.ToShort();
  192. obj.Data.VoucherNo_1++;
  193. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{obj.Entity.CODE}]1工位-结束:[{obj.Data.TaskID_1}][{obj.Data.SLine_1}][{obj.Data.SCol_1}][{obj.Data.SLayer_1}][{obj.Data.ELine_1}][{obj.Data.VoucherNo_1}]");
  194. }
  195. else if (finish.Station.Entity.CODE == devs[1].Entity.CODE)
  196. {
  197. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{obj.Entity.CODE}]2工位-开始:[{obj.Data.TaskID_2}][{obj.Data.SLine_2}][{obj.Data.SCol_2}][{obj.Data.SLayer_2}][{obj.Data.ELine_2}][{obj.Data.VoucherNo_2}]");
  198. obj.Data.TaskID_2 = task.ID;
  199. obj.Data.SLine_2 = finish.Station.Entity.CODE.ToShort();
  200. obj.Data.SCol_2 = 0;
  201. obj.Data.SLayer_2 = 0;
  202. obj.Data.ELine_2 = addTo[0].ToShort();
  203. obj.Data.ECol_2 = addTo[1].ToShort();
  204. obj.Data.ELayer_2 = addTo[2].ToShort();
  205. obj.Data.RES1_2 = finishTaskList.Count.ToShort();
  206. obj.Data.VoucherNo_2++;
  207. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{obj.Entity.CODE}]2工位-结束:[{obj.Data.TaskID_2}][{obj.Data.SLine_2}][{obj.Data.SCol_2}][{obj.Data.SLayer_2}][{obj.Data.ELine_2}][{obj.Data.VoucherNo_2}]");
  208. }
  209. }
  210. });
  211. #endregion 入库
  212. }
  213. else
  214. {
  215. #region 出库
  216. var floor = obj.Entity.Get<int>("LastFloor");
  217. floor = floor % 2 + 1;
  218. obj.Entity.Set("LastFloor", floor);
  219. if (enterPriority != null && outPriority != null && enterPriority.Priority > outPriority.Priority) return;
  220. //获取当前堆垛机所有的放货点
  221. var list = obj.GetDeliveryPoint();
  222. list = list.Where(v =>
  223. {
  224. //true:满足条件 false:不满足条件
  225. //返回结果为无货的设备 默认无货
  226. var res = true;
  227. //放货点是否有货
  228. if (v.Data.VoucherNo != v.Data2.VoucherNo) res = false;
  229. else if (v.Data3.Status.HasFlag(StationStatus.运行状态位)) res = false;
  230. else if (v.Data2.Status.HasFlag(IstationStatus.光电状态)) res = false;
  231. else if (v.Data2.Request == IstationRequest.堆垛机放货完成请求目标地址) res = false;
  232. else if (v.Data2.Tasknum > 10000) res = false;
  233. return res;
  234. }).ToList();
  235. //没有可用货位
  236. if (list.Count == 0) return;
  237. //月台所有设备当前有的任务号
  238. var taskidList = DockDevs.Select(v => v.Data2.Tasknum).Where(v => v > 10000).ToList();
  239. //堆垛机设备
  240. var srm = obj.Entity.CODE;
  241. DB.Do(db =>
  242. {
  243. //堆垛机当前是否有正在执行的任务
  244. if (db.Default.Set<WCS_TASK>().Any(d => d.DEVICE == srm && d.STATUS == TaskStatus.堆垛机执行)) throw new WarnException($"[{deviceCode}]有正在执行的出库任务");
  245. //找出等待执行的出库任务
  246. var waitTask = db.Default.Set<WCS_TASK>().Where(v => v.STATUS == TaskStatus.新建)
  247. .Where(v => v.DEVICE == srm)
  248. .Where(v => v.TYPE == TaskType.出库)
  249. .Where(v => !db.Default.Set<WCS_TASK>().Any(d => d.DEVICE == srm && d.STATUS == TaskStatus.堆垛机执行)).ToList();
  250. //同一个目标地址同时只能有两组正在执行的任务
  251. //同时对结果进行排序,分组后取第一组任务
  252. var maxnum = ProtocolProxy.YGWMS150Redis.Get("SaleTaskGroupCount").ToInt();
  253. var outDepot = waitTask.Where(v =>
  254. {
  255. var max = v.ADDRTO == "G1340" ? 4 : maxnum;
  256. return db.Default.Set<WCS_TASK>()
  257. .AsNoTracking()
  258. .Where(d => d.TYPE == TaskType.出库)
  259. .Where(d => d.STATUS > TaskStatus.新建)
  260. .Where(d => d.STATUS < TaskStatus.已完成 || taskidList.Contains(d.ID))
  261. .Where(d => d.ADDRTO == v.ADDRTO && d.FLOOR == v.FLOOR)
  262. .GroupBy(d => d.TaskGroupKey).Count() < max;
  263. })
  264. .OrderByDescending(v => v.Priority)
  265. .ThenBy(v => v.FLOOR == floor ? 0 : 1)
  266. .ThenBy(v =>
  267. {
  268. return db.Default.Set<WCS_TASK>()
  269. .AsNoTracking()
  270. .Where(d => d.TYPE == TaskType.出库)
  271. .Where(d => d.STATUS > TaskStatus.新建)
  272. .Where(d => d.STATUS < TaskStatus.已完成 || taskidList.Contains(d.ID))
  273. .Where(d => d.ADDRTO == v.ADDRTO && d.FLOOR == v.FLOOR)
  274. .GroupBy(d => d.TaskGroupKey).Count();
  275. })
  276. .ThenBy(v => v.CREATETIME)
  277. .GroupBy(v => v.ADDRTO)
  278. .FirstOrDefault() ?? throw new WarnException("无可执行出库任务");
  279. //获取两个个可执行任务,此时这两个任务的目标地址是一致的
  280. var tasks = outDepot.Select(v => v).ToList().GetOutTask();
  281. #region 校验两个产品是否为同规格
  282. if (tasks.Length == 2)
  283. {
  284. var length = tasks.OrderByDescending(v => v.Length).ToArray();
  285. //较大的长度减去较小的长度,差大于两百表示为不同规格产品
  286. if (length[0].Length - length[1].Length > 200)
  287. {
  288. tasks = tasks.Take(1).ToArray();
  289. }
  290. }
  291. #endregion 校验两个产品是否为同规格
  292. for (int i = 0; i < tasks.Length; i++)
  293. {
  294. var item = tasks[i];
  295. var task = db.Default.Set<WCS_TASK>().Find(item.ID);
  296. var oldTaskSTATUS = task.STATUS;
  297. task.STARTTIME = DateTime.Now;
  298. task.UPDATETIME = DateTime.Now;
  299. task.STATUS = WCS.Entity.TaskStatus.堆垛机执行;
  300. task.DEVICE = obj.Entity.CODE;
  301. task.TaskGroupKey = tasks.Length switch
  302. {
  303. 1 => $"{tasks[0].ID}_0",
  304. 2 => $"{tasks[0].ID}_{tasks[1].ID}",
  305. _ => throw new WarnException($"可用任务数异常{tasks.Length}"),
  306. };
  307. var fork = obj.GetFork(item, i);
  308. //获取站台及下一个地址
  309. task.GetSrmStationAndaddNext(fork);
  310. var msg = "";
  311. if (fork == SrmFork.货叉1)
  312. msg = $"状态由[{oldTaskSTATUS}]变更为[{task.STATUS}][{obj.Data.SLine_1}-{obj.Data.SCol_1}-{obj.Data.SLayer_1}][{obj.Data.ELine_1}][{obj.Data.VoucherNo_1}]";
  313. else
  314. msg = $"状态由[{oldTaskSTATUS}]变更为[{task.STATUS}][{obj.Data.SLine_2}-{obj.Data.SCol_2}-{obj.Data.SLayer_2}][{obj.Data.ELine_2}][{obj.Data.VoucherNo_2}]";
  315. task.CreateStatusLog(db, msg, this.GetType());
  316. item.SRMSTATION = task.SRMSTATION;
  317. if (fork == SrmFork.货叉1) // 列数较小的放一工位
  318. {
  319. obj.WriteTask1(item, (short)tasks.Length);
  320. }
  321. else if (fork == SrmFork.货叉2) //列数较大的放二工位
  322. {
  323. obj.WriteTask2(item, (short)tasks.Length);
  324. }
  325. db.Default.SaveChanges();
  326. }
  327. });
  328. #endregion 出库
  329. }
  330. });
  331. }
  332. protected override bool SelectDevice(WCS_DEVICE dev)
  333. {
  334. return dev.Is(DF.SRM);
  335. }
  336. }
  337. }