SRMWork.cs 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  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. protected override void Do(SRMDevice obj)
  21. {
  22. obj.EX(srmDevice =>
  23. {
  24. //状态上抛
  25. DeciceStateHelper.StatusCheck(obj.Entity.CODE, obj.Data2.SRMMode.ToString());
  26. var deviceCode = obj.Entity.CODE;
  27. //先检查堆垛机是否报警
  28. if (srmDevice.Data3.SCAlarm != 0)
  29. {
  30. if (srmDevice.Entity.WakeupOn(5000)) WMS.DevInfo(deviceCode, srmDevice.Data3.SCAlarm.ToString());
  31. InfoLog.INFO_SRMALARM($"{deviceCode}-{srmDevice.Data3.SCAlarm}");
  32. return;
  33. }
  34. if (srmDevice.Data.FinishedACK_1 == 1 || srmDevice.Data.FinishedACK_2 == 1) throw new WarnException($"堆垛机完成任务WCS反馈信号未清除");
  35. if (srmDevice.Data2.VoucherNo_1 != srmDevice.Data.VoucherNo_1 || srmDevice.Data2.VoucherNo_2 != srmDevice.Data.VoucherNo_2)
  36. {
  37. DB.Do(db =>
  38. {
  39. WCS_TASK task;
  40. if (srmDevice.Data2.VoucherNo_1 != srmDevice.Data.VoucherNo_1)
  41. {
  42. task = db.Default.Set<WCS_TASK>().FirstOrDefault(v => v.ID == srmDevice.Data.TaskID_1) ?? throw new WarnException($"未好到对应的WCS任务--{srmDevice.Data.TaskID_1}");
  43. if (task.STATUS == TaskStatus.新建)
  44. {
  45. srmDevice.Data.VoucherNo_1 = srmDevice.Data2.VoucherNo_1;
  46. }
  47. }
  48. if (srmDevice.Data2.VoucherNo_2 != srmDevice.Data.VoucherNo_2)
  49. {
  50. task = db.Default.Set<WCS_TASK>().FirstOrDefault(v => v.ID == srmDevice.Data.TaskID_2) ?? throw new WarnException($"未好到对应的WCS任务--{srmDevice.Data.TaskID_2}");
  51. if (task.STATUS == TaskStatus.新建)
  52. {
  53. srmDevice.Data.VoucherNo_2 = srmDevice.Data2.VoucherNo_2;
  54. }
  55. }
  56. });
  57. throw new WarnException($"等待执行{srmDevice.Data.TaskID_1}-{srmDevice.Data.TaskID_2}");
  58. }
  59. //处理堆垛机已完成的任务
  60. if (srmDevice.Data2.FinishedTask_1 != 0 || srmDevice.Data2.FinishedTask_2 != 0)
  61. {
  62. InfoLog.INFO_SRMINFO($"开始完成任务:[{deviceCode}]-{srmDevice.Data2.FinishedTask_1}-{srmDevice.Data2.FinishedTask_2}");
  63. srmDevice.FinishedTaskHandle();
  64. InfoLog.INFO_SRMINFO($"完成任务处理结束:[{deviceCode}]-{srmDevice.Data2.FinishedTask_1}-{srmDevice.Data2.FinishedTask_2}");
  65. return;
  66. }
  67. if (srmDevice.Data2.SRMMode != SCMode.远程) return;
  68. if (srmDevice.Data2.SRMStatus != SCRunStatus.空闲) return;
  69. var isTransfer = new List<WCS_TASK>(); //是否有移库任务
  70. WCS_TASK enterPriority = new(), outPriority = new(); //出入库优先级任务
  71. var dumpLibrary = new List<WCS_TASK>();
  72. //再检查是否有等待执行的货物
  73. DB.Do(db =>
  74. {
  75. var task = db.Default.Set<WCS_TASK>().Where(v => v.DEVICE == deviceCode).FirstOrDefault(v => v.STATUS == TaskStatus.堆垛机执行);
  76. if (task != null) throw new WarnException($"[{deviceCode}]有正在执行的任务:[{task.ID}]");
  77. //检测是否有未结束的倒库任务
  78. dumpLibrary = db.Default.Set<WCS_TASK>().Where(v => (v.DEVICE == deviceCode || v.DEVICEDL == deviceCode) && v.TYPE == TaskType.倒库 && v.STATUS < TaskStatus.已完成).ToList();
  79. if (!dumpLibrary.Any())
  80. {
  81. //属于当前堆垛机未执行的移库任务
  82. isTransfer = db.Default.Set<WCS_TASK>().AsNoTracking().Where(v => v.DEVICE == deviceCode && v.TYPE == TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行).ToList();
  83. //判断是否存在调整优先级任务,存在初始化isTransfer值 让本次执行优先任务
  84. if (db.Default.Set<WCS_TASK>().AsNoTracking().Any(v => v.DEVICE == deviceCode && v.TYPE != TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行 && v.Priority > 0))
  85. isTransfer = new List<WCS_TASK>();
  86. enterPriority = db.Default.Set<WCS_TASK>().Where(v => v.DEVICE == deviceCode && v.TYPE == TaskType.入库 && v.STATUS < TaskStatus.堆垛机执行)
  87. .OrderByDescending(v => v.Priority).FirstOrDefault();
  88. outPriority = db.Default.Set<WCS_TASK>().Where(v => v.DEVICE == deviceCode && v.TYPE == TaskType.出库 && v.STATUS < TaskStatus.堆垛机执行)
  89. .OrderByDescending(v => v.Priority).FirstOrDefault();
  90. }
  91. });
  92. //最后一个是否是出库任务
  93. var lastIsOut = srmDevice.Entity.Get<bool>("LastIsOut");
  94. srmDevice.Entity.Set("LastIsOut", !lastIsOut);
  95. if (dumpLibrary.Any())
  96. {
  97. //检查当前堆垛机是起始点还是目标点
  98. var lastOut = srmDevice.Entity.Get<bool>("LastOut");
  99. srmDevice.Entity.Set("LastOut", !lastIsOut);
  100. if (lastOut) //入库
  101. {
  102. var arrIn = srmDevice.GetPickPoint()
  103. .Where(v => Device.Where(d => d.IsConv()).Where(v => v.Is(DF.SRM涂布取货)).Select(d => d.Device<IStation521>()).Any(d => d.Data.Goodsend == v.Entity.Code())) //有正在前往取货点的任务
  104. .ToList();
  105. if (!arrIn.Any()) return; //当前堆垛机无入库任务
  106. //入库口设备信息 找一个有任务有光电且不在运行状态位的取货点 如果找不到代表任务还在输送途中
  107. var station = arrIn.OrderBy(v => v.Data2.Tasknum > 0 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位) ? 0 : 1)
  108. .ThenBy(v => v.UpdateTime)
  109. .FirstOrDefault() ?? throw new WarnException($"[{deviceCode}]等待入库任务输送到位");
  110. //根据上述筛选条件筛选出来的入库任务 找到对应的设备组
  111. var item = Device.Where(v => v.DEVICEGROUP.Any(p => p.MEMBER.CODE == station.Entity.CODE)).Single();
  112. //对数据进行排序,根据CAD图纸规划,取货点两个设备中设备号较大的一个一定对应到堆垛机的一工位货叉
  113. //因此按照降序进行排序,可以保证数组中第一个一定是放到堆垛机一工位的数据
  114. var devise = item.DEVICEGROUP.Select(v => v.MEMBER)
  115. .Select(v => v.Create<StationDevice>())
  116. .OrderByDescending(v => v.Entity.CODE)
  117. .ToArray();
  118. var finishTaskList = new List<FinishTaskList<int>>(); //成功分配货位的任务
  119. //检测有效任务数与实际任务是是否相等
  120. var validDev = devise.Where(v => v.Data2.Tasknum > 10000 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位));
  121. var stationDevices = validDev as StationDevice[] ?? validDev.ToArray();
  122. if (!stationDevices.Any()) throw new DoException("无有效入库任务");
  123. DB.Do(db =>
  124. {
  125. var tasking = db.Default.Set<WCS_TASK>().FirstOrDefault(v => v.ID == stationDevices.First().Data2.Tasknum);
  126. var taskList = db.Default.Set<WCS_TASK>().Count(v => v.TaskGroupKey == tasking.TaskGroupKey && v.TYPE == TaskType.倒库);
  127. if (stationDevices.Count() != taskList) throw new WarnException($"任务数量不匹配,设备-{stationDevices.Count()},WCS-{taskList}");
  128. foreach (var dev in stationDevices)
  129. {
  130. var task = db.Default.Set<WCS_TASK>().FirstOrDefault(v => v.STATUS < TaskStatus.堆垛机执行 && v.ID == dev.Data2.Tasknum) ?? throw new WarnException($"设备有光电有任务且不在运行状态,但WCS找不到任务{dev.Data2.Tasknum}");
  131. var oldTask = task.STATUS;
  132. var tunnel = dev.Entity.ROUTES.First().NEXT.CODE;
  133. I_WCS_GetWareCellResponse loc;
  134. //判定当前设备对应的堆垛机工位
  135. if (dev.Entity.CODE == devise[0].Entity.CODE) loc = WMS.GetLocalIn(task.WMSTASK, tunnel, dev.Entity.CODE, Entity.WareCellForkNum.货叉1); //一工位
  136. else if (dev.Entity.CODE == devise[1].Entity.CODE) loc = WMS.GetLocalIn(task.WMSTASK, tunnel, dev.Entity.CODE, Entity.WareCellForkNum.货叉2); //2工位
  137. else throw new WarnException($"设备{dev.Entity.CODE}无法对应至堆垛机的任一一个工位");
  138. task.UPDATETIME = DateTime.Now;
  139. task.STATUS = TaskStatus.堆垛机执行;
  140. task.ADDRTO = $"{loc.Row}-{loc.Colomn}-{loc.Layer}";
  141. task.DEVICE = deviceCode;
  142. task.TUNNEL = tunnel;
  143. task.CreateStatusLog(db, $"堆垛机开始执行入库", this.GetType());
  144. finishTaskList.Add(new FinishTaskList<int>(task.ID, dev));
  145. }
  146. db.Default.SaveChanges();
  147. });
  148. DB.Do(db =>
  149. {
  150. foreach (var finish in finishTaskList)
  151. {
  152. var task = db.Default.Set<WCS_TASK>().Find(finish.FinishCode);
  153. var addTo = task!.ADDRTO.Split("-");
  154. if (finish.Station.Entity.CODE == devise[0].Entity.CODE) //一工位
  155. {
  156. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{deviceCode}]1工位-开始:[{srmDevice.Data.TaskID_1}][{srmDevice.Data.SLine_1}][{srmDevice.Data.ELine_1}][{srmDevice.Data.ECol_1}][{srmDevice.Data.ELayer_1}][{srmDevice.Data.VoucherNo_1}]--[{finishTaskList.Count.ToShort()}]");
  157. srmDevice.Data.TaskID_1 = task.ID;
  158. srmDevice.Data.SLine_1 = finish.Station.Entity.CODE.ToShort();
  159. srmDevice.Data.SCol_1 = 0;
  160. srmDevice.Data.SLayer_1 = 0;
  161. srmDevice.Data.ELine_1 = addTo[0].ToShort();
  162. srmDevice.Data.ECol_1 = addTo[1].ToShort();
  163. srmDevice.Data.ELayer_1 = addTo[2].ToShort();
  164. srmDevice.Data.RES1_1 = finishTaskList.Count.ToShort();
  165. srmDevice.Data.VoucherNo_1++;
  166. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{deviceCode}]1工位-结束:[{srmDevice.Data.TaskID_1}][{srmDevice.Data.SLine_1}][{srmDevice.Data.ELine_1}][{srmDevice.Data.ECol_1}][{srmDevice.Data.ELayer_1}][{srmDevice.Data.VoucherNo_1}]--[{finishTaskList.Count.ToShort()}]");
  167. }
  168. else if (finish.Station.Entity.CODE == devise[1].Entity.CODE)
  169. {
  170. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{deviceCode}]2工位-开始:[{srmDevice.Data.TaskID_2}][{srmDevice.Data.SLine_2}][{srmDevice.Data.ELine_2}][{srmDevice.Data.ECol_2}][{srmDevice.Data.ELayer_2}][{srmDevice.Data.VoucherNo_2}]--[{finishTaskList.Count.ToShort()}]");
  171. srmDevice.Data.TaskID_2 = task.ID;
  172. srmDevice.Data.SLine_2 = finish.Station.Entity.CODE.ToShort();
  173. srmDevice.Data.SCol_2 = 0;
  174. srmDevice.Data.SLayer_2 = 0;
  175. srmDevice.Data.ELine_2 = addTo[0].ToShort();
  176. srmDevice.Data.ECol_2 = addTo[1].ToShort();
  177. srmDevice.Data.ELayer_2 = addTo[2].ToShort();
  178. srmDevice.Data.RES1_2 = finishTaskList.Count.ToShort();
  179. srmDevice.Data.VoucherNo_2++;
  180. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{deviceCode}]2工位-结束:[{srmDevice.Data.TaskID_2}][{srmDevice.Data.SLine_2}][{srmDevice.Data.ELine_2}][{srmDevice.Data.ECol_2}][{srmDevice.Data.ELayer_2}][{srmDevice.Data.VoucherNo_2}]--[{finishTaskList.Count.ToShort()}]");
  181. }
  182. }
  183. });
  184. }
  185. else //出库
  186. {
  187. //获取当前堆垛机所有的放货点
  188. var list = srmDevice.GetDeliveryPoint().Where(v =>
  189. {
  190. //true:满足条件 false:不满足条件
  191. //返回结果为无货的设备 默认无货
  192. var res = true;
  193. //放货点是否有货
  194. if (v.Data.VoucherNo != v.Data2.VoucherNo) res = false;
  195. else if (v.Data3.Status.HasFlag(StationStatus.运行状态位)) res = false;
  196. else if (v.Data2.Status.HasFlag(IstationStatus.光电状态)) res = false;
  197. else if (v.Data2.Request == IstationRequest.堆垛机放货完成请求目标地址) res = false;
  198. else if (v.Data2.Tasknum > 10000) res = false;
  199. if (!v.Entity.Is(DF.SRM涂布放货)) return res;
  200. var devise = new List<StationDevice>();
  201. switch (v.Entity.CODE)
  202. {
  203. case "1283" or "1284":
  204. devise = Device.Where(b => b.CODE is "1281" or "1282").Select(b => b.Create<StationDevice>()).ToList();
  205. break;
  206. case "1290" or "1291" or "1292" or "1293":
  207. devise = Device.Where(b => b.CODE is "1288" or "1289").Select(b => b.Create<StationDevice>()).ToList();
  208. break;
  209. case "1299" or "1300" or "1301" or "1302":
  210. devise = Device.Where(b => b.CODE is "1297" or "1298").Select(b => b.Create<StationDevice>()).ToList();
  211. break;
  212. case "1308" or "1309" or "1310" or "1311":
  213. devise = Device.Where(b => b.CODE is "1306" or "1307").Select(b => b.Create<StationDevice>()).ToList();
  214. break;
  215. }
  216. if (!devise.Any()) return res;
  217. foreach (var stationDevice in devise)
  218. {
  219. //放货点是否有货
  220. if (stationDevice.Data3.Status.HasFlag(StationStatus.运行状态位)) res = false;
  221. else if (stationDevice.Data2.Status.HasFlag(IstationStatus.光电状态)) res = false;
  222. else if (stationDevice.Data2.Request == IstationRequest.堆垛机放货完成请求目标地址) res = false;
  223. else if (stationDevice.Data2.Tasknum > 10000) res = false;
  224. }
  225. return res;
  226. }).Select(v => v.Entity.CODE).ToList();
  227. //没有可用货位
  228. if (!list.Any()) return;
  229. //找到对应的设备组编号
  230. var groupList = Device.Where(v => v.IsDevGroup()).Where(v => v.DEVICEGROUP.Any(b => list.Contains(b.MEMBER.CODE))).Select(v => v.CODE).ToList();
  231. //堆垛机设备
  232. var srm = deviceCode;
  233. var finishTaskList = new List<FinishTaskList<SrmFork, Task>>();
  234. DB.Do(db =>
  235. {
  236. //堆垛机当前是否有正在执行的任务
  237. if (db.Default.Set<WCS_TASK>().Any(d => d.DEVICE == srm && d.STATUS == TaskStatus.堆垛机执行)) throw new WarnException($"[{deviceCode}]有正在执行的出库任务");
  238. //找出等待执行的出库任务
  239. var waitTask = db.Default.Set<WCS_TASK>().Where(v => v.STATUS == TaskStatus.新建)
  240. .Where(v => v.DEVICE == srm)
  241. .Where(v => v.TYPE == TaskType.倒库)
  242. .Where(v => !db.Default.Set<WCS_TASK>().Any(d => d.DEVICE == srm && d.STATUS == TaskStatus.堆垛机执行))
  243. .Where(v => groupList.Contains(v.SRMSTATION)) //站台必须可用
  244. .ToList();
  245. //同时对结果进行排序,分组
  246. var outDepotGrouping = waitTask.OrderByDescending(v => v.Priority).ThenBy(v => v.CREATETIME)
  247. .GroupBy(v => v.TaskGroupKey)
  248. .FirstOrDefault().Select(v => v).ToList();
  249. if (outDepotGrouping == null) return; //用于解决Linq Value cannot be null. (Parameter 'source')
  250. //获取一组任务
  251. var tasks = outDepotGrouping.Select(v => v.Create<Task>()).OrderBy(v => v.Col).ToArray();
  252. for (var i = 0; i < tasks.Count(); i++)
  253. {
  254. var item = tasks[i];
  255. var task = db.Default.Set<WCS_TASK>().Find(item.ID);
  256. var oldTaskStatus = task!.STATUS;
  257. task.STARTTIME = DateTime.Now;
  258. task.UPDATETIME = DateTime.Now;
  259. task.STATUS = TaskStatus.堆垛机执行;
  260. task.DEVICE = deviceCode;
  261. var fork = srmDevice.GetFork(item, i);
  262. //获取站台及下一个地址
  263. task.GetSrmStationAndaddNext(fork);
  264. task.CreateStatusLog(db, "堆垛机开始执行出库", this.GetType());
  265. item.SRMSTATION = task.SRMSTATION;
  266. finishTaskList.Add(new FinishTaskList<SrmFork, Task>(fork, item));
  267. }
  268. db.Default.SaveChanges();
  269. //此处只做标记,表示当前事务已经提交
  270. foreach (var finish in finishTaskList)
  271. {
  272. switch (finish.FinishCode)
  273. {
  274. // 列数较小的放一工位
  275. case SrmFork.货叉1:
  276. obj.Data.TaskID_1 = finish.Station.ID;
  277. break;
  278. //列数较大的放二工位
  279. case SrmFork.货叉2:
  280. obj.Data.TaskID_2 = finish.Station.ID;
  281. break;
  282. default:
  283. throw new ArgumentOutOfRangeException();
  284. }
  285. }
  286. });
  287. ////检查标记好的出库任务,并将出库任务下达至堆垛机
  288. DB.Do(db =>
  289. {
  290. //此处只做标记,表示当前事务已经提交
  291. foreach (var finish in finishTaskList)
  292. {
  293. var task = db.Default.Set<WCS_TASK>().Find(finish.Station.ID).Create<Task>();
  294. switch (finish.FinishCode)
  295. {
  296. // 列数较小的放一工位
  297. case SrmFork.货叉1:
  298. obj.WriteTask1(task, (short)finishTaskList.Count);
  299. break;
  300. //列数较大的放二工位
  301. case SrmFork.货叉2:
  302. obj.WriteTask2(task, (short)finishTaskList.Count);
  303. break;
  304. default:
  305. throw new ArgumentOutOfRangeException();
  306. }
  307. }
  308. });
  309. }
  310. }
  311. if (isTransfer.Count > 0) //防止因为无当前堆垛机移库任务导致无法执行其他类型任务
  312. {
  313. #region 移库
  314. var finishTaskList = new List<FinishTaskList<SrmFork, Task>>();
  315. DB.Do(db =>
  316. {
  317. //获取当前堆垛机未执行的任务
  318. var taskList = db.Default.Set<WCS_TASK>().Where(v => v.DEVICE == srmDevice.Entity.CODE && v.TYPE == TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行).OrderBy(p => p.CREATETIME);
  319. var tasks = taskList.ToList().GetOutTask();
  320. if (tasks.Any(v => v.Layer <= 6))
  321. {
  322. if (tasks.Count(v => v.Layer <= 6) >= 2)
  323. {
  324. tasks = tasks.Take(1).ToArray();
  325. }
  326. }
  327. for (var i = 0; i < tasks.Length; i++)
  328. {
  329. var item = tasks[i];
  330. var task = db.Default.Set<WCS_TASK>().Find(item.ID);
  331. var oldTaskStatus = task!.STATUS;
  332. task.STARTTIME = DateTime.Now;
  333. task.UPDATETIME = DateTime.Now;
  334. task.STATUS = WCS.Entity.TaskStatus.堆垛机执行;
  335. task.DEVICE = srmDevice.Entity.CODE;
  336. task.TaskGroupKey = tasks.Length switch
  337. {
  338. 1 => $"{tasks[0].ID}_0",
  339. 2 => $"{tasks[0].ID}_{tasks[1].ID}",
  340. _ => throw new WarnException($"可用任务数异常{tasks.Length}"),
  341. };
  342. var fork = srmDevice.GetFork(item, i);
  343. var msg = "";
  344. msg = fork == SrmFork.货叉1 ? $"状态由[{oldTaskStatus}]变更为[{task.STATUS}]" : $"状态由[{oldTaskStatus}]变更为[{task.STATUS}]";
  345. task.CreateStatusLog(db, msg, this.GetType());
  346. finishTaskList.Add(new FinishTaskList<SrmFork, Task>(fork, item));
  347. }
  348. db.Default.SaveChanges();
  349. //此处只做标记,表示当前事务已经提交
  350. foreach (var finish in finishTaskList)
  351. {
  352. switch (finish.FinishCode)
  353. {
  354. // 列数较小的放一工位
  355. case SrmFork.货叉1:
  356. obj.Data.TaskID_1 = finish.Station.ID;
  357. break;
  358. //列数较大的放二工位
  359. case SrmFork.货叉2:
  360. obj.Data.TaskID_2 = finish.Station.ID;
  361. break;
  362. default:
  363. throw new ArgumentOutOfRangeException();
  364. }
  365. }
  366. });
  367. //检查标记好的出库任务,并将出库任务下达至堆垛机
  368. DB.Do(db =>
  369. {
  370. //此处只做标记,表示当前事务已经提交
  371. foreach (var finish in finishTaskList)
  372. {
  373. var task = db.Default.Set<WCS_TASK>().Find(finish.Station.ID).Create<Task>();
  374. switch (finish.FinishCode)
  375. {
  376. // 列数较小的放一工位
  377. case SrmFork.货叉1:
  378. obj.WriteTask1(task, (short)finishTaskList.Count);
  379. break;
  380. //列数较大的放二工位
  381. case SrmFork.货叉2:
  382. obj.WriteTask2(task, (short)finishTaskList.Count);
  383. break;
  384. default:
  385. throw new ArgumentOutOfRangeException();
  386. }
  387. }
  388. });
  389. #endregion 移库
  390. }
  391. else if (lastIsOut)
  392. {
  393. #region 入库
  394. var floor = srmDevice.Entity.Get<int>("LastInFloor");
  395. floor = floor % 2 + 1;
  396. srmDevice.Entity.Set("LastInFloor", floor);
  397. if (enterPriority != null && outPriority != null && outPriority.Priority > enterPriority.Priority) return;
  398. var arrIn = srmDevice.GetPickPoint()
  399. .Where(v => Device.Where(d => d.IsConv()).Select(d => d.Device<IStation521>()).Any(d => d.Data.Goodsend == v.Entity.Code())) //有正在前往取货点的任务
  400. .ToList();
  401. if (!arrIn.Any()) return; //当前堆垛机无入库任务
  402. //入库口设备信息 找一个有任务有光电且不在运行状态位的取货点 如果找不到代表任务还在输送途中
  403. var station = arrIn.OrderBy(v => v.Data2.Tasknum > 0 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位) ? 0 : 1)
  404. .ThenBy(v => v.Entity.Is(DF.SRM涂布取货) ? 0 : 1)
  405. .ThenBy(v => v.Entity.Is(DF.SRMBOPP取货) ? 0 : 1)
  406. .FirstOrDefault() ?? throw new WarnException($"[{deviceCode}]等待入库任务输送到位");
  407. //根据上述筛选条件筛选出来的入库任务 找到对应的设备组
  408. var item = Device.Where(v => v.DEVICEGROUP.Any(p => p.MEMBER.CODE == station.Entity.CODE)).Single();
  409. //对数据进行排序,根据CAD图纸规划,取货点两个设备中设备号较大的一个一定对应到堆垛机的一工位货叉
  410. //因此按照降序进行排序,可以保证数组中第一个一定是放到堆垛机一工位的数据
  411. var devise = item.DEVICEGROUP.Select(v => v.MEMBER)
  412. .Select(v => v.Create<StationDevice>())
  413. .OrderByDescending(v => v.Entity.CODE)
  414. .ToArray();
  415. var finishTaskList = new List<FinishTaskList<int>>(); //成功分配货位的任务
  416. //检测有效任务数与实际任务是是否相等
  417. var validDev = devise.Where(v => v.Data2.Tasknum > 10000 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位));
  418. var stationDevices = validDev as StationDevice[] ?? validDev.ToArray();
  419. if (!stationDevices.Any()) throw new DoException("无有效入库任务");
  420. DB.Do(db =>
  421. {
  422. var tasking = db.Default.Set<WCS_TASK>().FirstOrDefault(v => v.ID == stationDevices.First().Data2.Tasknum);
  423. var taskList = db.Default.Set<WCS_TASK>().Count(v => v.TaskGroupKey == tasking.TaskGroupKey && v.TYPE == TaskType.入库);
  424. if (stationDevices.Count() != taskList) throw new WarnException($"任务数量不匹配,设备-{stationDevices.Count()},WCS-{taskList}");
  425. foreach (var dev in stationDevices)
  426. {
  427. var task = db.Default.Set<WCS_TASK>().FirstOrDefault(v => v.STATUS < TaskStatus.堆垛机执行 && v.ID == dev.Data2.Tasknum) ?? throw new WarnException($"设备有光电有任务且不在运行状态,但WCS找不到任务{dev.Data2.Tasknum}");
  428. var oldTask = task.STATUS;
  429. var tunnel = dev.Entity.ROUTES.First().NEXT.CODE;
  430. I_WCS_GetWareCellResponse loc;
  431. //判定当前设备对应的堆垛机工位
  432. if (dev.Entity.CODE == devise[0].Entity.CODE) loc = WMS.GetLocalIn(task.WMSTASK, tunnel, dev.Entity.CODE, Entity.WareCellForkNum.货叉1); //一工位
  433. else if (dev.Entity.CODE == devise[1].Entity.CODE) loc = WMS.GetLocalIn(task.WMSTASK, tunnel, dev.Entity.CODE, Entity.WareCellForkNum.货叉2); //2工位
  434. else throw new WarnException($"设备{dev.Entity.CODE}无法对应至堆垛机的任一一个工位");
  435. task.UPDATETIME = DateTime.Now;
  436. task.STATUS = TaskStatus.堆垛机执行;
  437. task.ADDRTO = $"{loc.Row}-{loc.Colomn}-{loc.Layer}";
  438. task.DEVICE = deviceCode;
  439. task.TUNNEL = tunnel;
  440. task.CreateStatusLog(db, $"堆垛机开始执行入库任务", this.GetType());
  441. finishTaskList.Add(new FinishTaskList<int>(task.ID, dev));
  442. }
  443. db.Default.SaveChanges();
  444. });
  445. DB.Do(db =>
  446. {
  447. foreach (var finish in finishTaskList)
  448. {
  449. var task = db.Default.Set<WCS_TASK>().Find(finish.FinishCode);
  450. var addTo = task!.ADDRTO.Split("-");
  451. if (finish.Station.Entity.CODE == devise[0].Entity.CODE) //一工位
  452. {
  453. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{deviceCode}]1工位-开始:[{srmDevice.Data.TaskID_1}][{srmDevice.Data.SLine_1}][{srmDevice.Data.ELine_1}][{srmDevice.Data.ECol_1}][{srmDevice.Data.ELayer_1}][{srmDevice.Data.VoucherNo_1}]--[{finishTaskList.Count.ToShort()}]");
  454. srmDevice.Data.TaskID_1 = task.ID;
  455. srmDevice.Data.SLine_1 = finish.Station.Entity.CODE.ToShort();
  456. srmDevice.Data.SCol_1 = 0;
  457. srmDevice.Data.SLayer_1 = 0;
  458. srmDevice.Data.ELine_1 = addTo[0].ToShort();
  459. srmDevice.Data.ECol_1 = addTo[1].ToShort();
  460. srmDevice.Data.ELayer_1 = addTo[2].ToShort();
  461. srmDevice.Data.RES1_1 = finishTaskList.Count.ToShort();
  462. srmDevice.Data.VoucherNo_1++;
  463. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{deviceCode}]1工位-结束:[{srmDevice.Data.TaskID_1}][{srmDevice.Data.SLine_1}][{srmDevice.Data.ELine_1}][{srmDevice.Data.ECol_1}][{srmDevice.Data.ELayer_1}][{srmDevice.Data.VoucherNo_1}]--[{finishTaskList.Count.ToShort()}]");
  464. }
  465. else if (finish.Station.Entity.CODE == devise[1].Entity.CODE)
  466. {
  467. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{deviceCode}]2工位-开始:[{srmDevice.Data.TaskID_2}][{srmDevice.Data.SLine_2}][{srmDevice.Data.ELine_2}][{srmDevice.Data.ECol_2}][{srmDevice.Data.ELayer_2}][{srmDevice.Data.VoucherNo_2}]--[{finishTaskList.Count.ToShort()}]");
  468. srmDevice.Data.TaskID_2 = task.ID;
  469. srmDevice.Data.SLine_2 = finish.Station.Entity.CODE.ToShort();
  470. srmDevice.Data.SCol_2 = 0;
  471. srmDevice.Data.SLayer_2 = 0;
  472. srmDevice.Data.ELine_2 = addTo[0].ToShort();
  473. srmDevice.Data.ECol_2 = addTo[1].ToShort();
  474. srmDevice.Data.ELayer_2 = addTo[2].ToShort();
  475. srmDevice.Data.RES1_2 = finishTaskList.Count.ToShort();
  476. srmDevice.Data.VoucherNo_2++;
  477. InfoLog.INFO_SRMINFO($"入库--写入堆垛机[{deviceCode}]2工位-结束:[{srmDevice.Data.TaskID_2}][{srmDevice.Data.SLine_2}][{srmDevice.Data.ELine_2}][{srmDevice.Data.ECol_2}][{srmDevice.Data.ELayer_2}][{srmDevice.Data.VoucherNo_2}]--[{finishTaskList.Count.ToShort()}]");
  478. }
  479. }
  480. });
  481. #endregion 入库
  482. }
  483. else
  484. {
  485. #region 出库
  486. srmDevice.CheckOutTask();
  487. var floor = srmDevice.Entity.Get<int>("LastOutFloor");
  488. floor = floor % 2 + 1;
  489. srmDevice.Entity.Set("LastOutFloor", floor);
  490. if (enterPriority != null && outPriority != null && enterPriority.Priority > outPriority.Priority) return;
  491. //获取当前堆垛机所有的放货点
  492. var list = srmDevice.GetDeliveryPoint().Where(v =>
  493. {
  494. //true:满足条件 false:不满足条件
  495. //返回结果为无货的设备 默认无货
  496. var res = true;
  497. //放货点是否有货
  498. if (v.Data.VoucherNo != v.Data2.VoucherNo) res = false;
  499. else if (v.Data3.Status.HasFlag(StationStatus.运行状态位)) res = false;
  500. else if (v.Data2.Status.HasFlag(IstationStatus.光电状态)) res = false;
  501. else if (v.Data2.Request == IstationRequest.堆垛机放货完成请求目标地址) res = false;
  502. else if (v.Data2.Tasknum > 10000) res = false;
  503. if (!v.Entity.Is(DF.SRM涂布放货)) return res;
  504. var devise = new List<StationDevice>();
  505. switch (v.Entity.CODE)
  506. {
  507. case "1283" or "1284":
  508. devise = Device.Where(b => b.CODE is "1281" or "1282").Select(b => b.Create<StationDevice>()).ToList();
  509. break;
  510. case "1290" or "1291" or "1292" or "1293":
  511. devise = Device.Where(b => b.CODE is "1288" or "1289").Select(b => b.Create<StationDevice>()).ToList();
  512. break;
  513. case "1299" or "1300" or "1301" or "1302":
  514. devise = Device.Where(b => b.CODE is "1297" or "1298").Select(b => b.Create<StationDevice>()).ToList();
  515. break;
  516. case "1308" or "1309" or "1310" or "1311":
  517. devise = Device.Where(b => b.CODE is "1306" or "1307").Select(b => b.Create<StationDevice>()).ToList();
  518. break;
  519. }
  520. if (!devise.Any()) return res;
  521. foreach (var stationDevice in devise)
  522. {
  523. //放货点是否有货
  524. if (stationDevice.Data3.Status.HasFlag(StationStatus.运行状态位)) res = false;
  525. else if (stationDevice.Data2.Status.HasFlag(IstationStatus.光电状态)) res = false;
  526. else if (stationDevice.Data2.Request == IstationRequest.堆垛机放货完成请求目标地址) res = false;
  527. else if (stationDevice.Data2.Tasknum > 10000) res = false;
  528. }
  529. return res;
  530. }).Select(v => v.Entity.CODE).ToList();
  531. //没有可用货位
  532. if (!list.Any()) return;
  533. //找到对应的设备组编号
  534. var groupList = Device.Where(v => v.IsDevGroup()).Where(v => v.DEVICEGROUP.Any(b => list.Contains(b.MEMBER.CODE))).Select(v => v.CODE).ToList();
  535. //堆垛机设备
  536. var srm = deviceCode;
  537. var finishTaskList = new List<FinishTaskList<SrmFork, Task>>();
  538. DB.Do(db =>
  539. {
  540. //堆垛机当前是否有正在执行的任务
  541. if (db.Default.Set<WCS_TASK>().Any(d => d.DEVICE == srm && d.STATUS == TaskStatus.堆垛机执行)) throw new WarnException($"[{deviceCode}]有正在执行的出库任务");
  542. //找出等待执行的出库任务
  543. var waitTask = db.Default.Set<WCS_TASK>().Where(v => v.STATUS == TaskStatus.新建)
  544. .Where(v => v.DEVICE == srm)
  545. .Where(v => v.TYPE == TaskType.出库)
  546. .Where(v => !db.Default.Set<WCS_TASK>().Any(d => d.DEVICE == srm && d.STATUS == TaskStatus.堆垛机执行))
  547. .Where(v => groupList.Contains(v.SRMSTATION)) //站台必须可用
  548. .ToList();
  549. //同时对结果进行排序,分组
  550. var maximum = ProtocolProxy.YGWMS150Redis.Get("SaleTaskGroupCount").ToInt();
  551. var cTaskList = db.Default.Set<WCS_TASK>().AsNoTracking()
  552. .Where(d => d.TYPE == TaskType.出库)
  553. .Where(d => d.STATUS > TaskStatus.新建)
  554. .Where(d => d.STATUS < TaskStatus.已完成).ToList();
  555. var outDepotGrouping = waitTask.Where(v => cTaskList.Where(d => d.ADDRTO == v.ADDRTO && d.FLOOR == v.FLOOR).GroupBy(d => d.TaskGroupKey).Count() < maximum || v.ADDRTO == "G1340")
  556. .OrderByDescending(v => v.Priority)
  557. .ThenBy(v => v.ADDRTO == "G1340" ? 0 : 1)
  558. .ThenBy(v => v.FLOOR == floor ? 0 : 1)
  559. .ThenBy(v => cTaskList.Where(d => d.ADDRTO == v.ADDRTO && d.FLOOR == v.FLOOR).GroupBy(d => d.TaskGroupKey).Count())
  560. .ThenBy(v => v.CREATETIME)
  561. .GroupBy(v => v.ADDRTO)
  562. .FirstOrDefault();
  563. if (outDepotGrouping == null) return; //用于解决Linq Value cannot be null. (Parameter 'source')
  564. var outDepotList = outDepotGrouping.Select(v => v).ToList();
  565. //获取两个个可执行任务,此时这两个任务的目标地址是一致的
  566. var tasks = outDepotList.GetOutTask();
  567. #region 校验两个产品是否为同规格
  568. if (tasks.Length == 2)
  569. {
  570. var length = tasks.OrderByDescending(v => v.Length).ToArray();
  571. //较大的长度减去较小的长度,差大于两百表示为不同规格产品
  572. if (length[0].Length - length[1].Length > 200)
  573. {
  574. tasks = tasks.Take(1).ToArray();
  575. }
  576. }
  577. #endregion 校验两个产品是否为同规格
  578. for (var i = 0; i < tasks.Length; i++)
  579. {
  580. var item = tasks[i];
  581. var task = db.Default.Set<WCS_TASK>().Find(item.ID);
  582. var oldTaskStatus = task!.STATUS;
  583. task.STARTTIME = DateTime.Now;
  584. task.UPDATETIME = DateTime.Now;
  585. task.STATUS = WCS.Entity.TaskStatus.堆垛机执行;
  586. task.DEVICE = deviceCode;
  587. task.TaskGroupKey = tasks.Length switch
  588. {
  589. 1 => $"{tasks[0].ID}_0",
  590. 2 => $"{tasks[0].ID}_{tasks[1].ID}",
  591. _ => throw new WarnException($"可用任务数异常{tasks.Length}"),
  592. };
  593. var fork = srmDevice.GetFork(item, i);
  594. //获取站台及下一个地址
  595. task.GetSrmStationAndaddNext(fork);
  596. task.CreateStatusLog(db, "堆垛机开始执行出库任务", this.GetType());
  597. item.SRMSTATION = task.SRMSTATION;
  598. finishTaskList.Add(new FinishTaskList<SrmFork, Task>(fork, item));
  599. }
  600. db.Default.SaveChanges();
  601. //此处只做标记,表示当前事务已经提交
  602. foreach (var finish in finishTaskList)
  603. {
  604. switch (finish.FinishCode)
  605. {
  606. // 列数较小的放一工位
  607. case SrmFork.货叉1:
  608. obj.Data.TaskID_1 = finish.Station.ID;
  609. break;
  610. //列数较大的放二工位
  611. case SrmFork.货叉2:
  612. obj.Data.TaskID_2 = finish.Station.ID;
  613. break;
  614. default:
  615. throw new ArgumentOutOfRangeException();
  616. }
  617. }
  618. });
  619. ////检查标记好的出库任务,并将出库任务下达至堆垛机
  620. DB.Do(db =>
  621. {
  622. //此处只做标记,表示当前事务已经提交
  623. foreach (var finish in finishTaskList)
  624. {
  625. var task = db.Default.Set<WCS_TASK>().Find(finish.Station.ID).Create<Task>();
  626. switch (finish.FinishCode)
  627. {
  628. // 列数较小的放一工位
  629. case SrmFork.货叉1:
  630. obj.WriteTask1(task, (short)finishTaskList.Count);
  631. break;
  632. //列数较大的放二工位
  633. case SrmFork.货叉2:
  634. obj.WriteTask2(task, (short)finishTaskList.Count);
  635. break;
  636. default:
  637. throw new ArgumentOutOfRangeException();
  638. }
  639. }
  640. });
  641. #endregion 出库
  642. }
  643. });
  644. }
  645. protected override bool SelectDevice(WCS_DEVICE dev)
  646. {
  647. return dev.Is(DF.SRM);
  648. }
  649. }
  650. }