SRMWork.cs 45 KB

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