SRMWork.cs 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  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. bool isTransfer = false; //默认没有移库任务
  70. WCS_TASK enterPriority = null, outPriority = null; //出入库优先级任务
  71. var dumpLibrary = new List<WCS_TASK>();
  72. //再检查是否有等待执行的货物
  73. DB.Do(db =>
  74. {
  75. //获取当前堆垛机的所有未完成任务
  76. var tasks = db.Default.Set<WCS_TASK>().AsNoTracking().Where(v => v.STATUS < TaskStatus.已完成 && (v.DEVICE == deviceCode || v.DEVICEDL == deviceCode)).ToList();
  77. //获取倒库任务
  78. dumpLibrary = tasks.Where(v => v.TYPE == TaskType.倒库).ToList();
  79. #region 没有倒库任务
  80. if (!dumpLibrary.Any())
  81. {
  82. if (tasks.Any(v => v.STATUS == TaskStatus.堆垛机执行)) throw new WarnException($"[{deviceCode}]有正在执行的任务");
  83. //属于当前堆垛机未执行的移库任务
  84. isTransfer = tasks.Any(v => v.TYPE == TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行);
  85. //判断是否存在调整优先级任务,存在初始化isTransfer值 让本次执行优先任务
  86. if (tasks.Any(v => v.TYPE != TaskType.移库 && v.STATUS < TaskStatus.堆垛机执行 && v.Priority > 0)) isTransfer = false;
  87. enterPriority = tasks.Where(v => v.TYPE == TaskType.入库 && v.STATUS < TaskStatus.堆垛机执行).OrderByDescending(v => v.Priority).FirstOrDefault();
  88. outPriority = tasks.Where(v => v.TYPE == TaskType.出库 && v.STATUS < TaskStatus.堆垛机执行).OrderByDescending(v => v.Priority).FirstOrDefault();
  89. }
  90. #endregion 没有倒库任务
  91. });
  92. //最后一个是否是出库任务
  93. #region 倒库任务
  94. if (dumpLibrary.Any())
  95. {
  96. //检查当前堆垛机是起始点还是目标点
  97. var lastOut = srmDevice.Entity.Get<bool>("LastOut");
  98. srmDevice.Entity.Set("LastOut", !lastIsOut);
  99. if (lastOut) //入库
  100. {
  101. var arrIn = srmDevice.GetPickPoint()
  102. .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())) //有正在前往取货点的任务
  103. .ToList();
  104. if (!arrIn.Any()) return; //当前堆垛机无入库任务
  105. //入库口设备信息 找一个有任务有光电且不在运行状态位的取货点 如果找不到代表任务还在输送途中
  106. var station = arrIn.OrderBy(v => v.Data2.Tasknum > 0 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位) ? 0 : 1)
  107. .ThenBy(v => v.UpdateTime)
  108. .FirstOrDefault() ?? throw new WarnException($"[{deviceCode}]等待入库任务输送到位");
  109. //根据上述筛选条件筛选出来的入库任务 找到对应的设备组
  110. var item = Device.Where(v => v.DEVICEGROUP.Any(p => p.MEMBER.CODE == station.Entity.CODE)).Single();
  111. //对数据进行排序,根据CAD图纸规划,取货点两个设备中设备号较大的一个一定对应到堆垛机的一工位货叉
  112. //因此按照降序进行排序,可以保证数组中第一个一定是放到堆垛机一工位的数据
  113. var devise = item.DEVICEGROUP.Select(v => v.MEMBER)
  114. .Select(v => v.Create<StationDevice>())
  115. .OrderByDescending(v => v.Entity.CODE)
  116. .ToArray();
  117. var finishTaskList = new List<FinishTaskList<int>>(); //成功分配货位的任务
  118. //检测有效任务数与实际任务是是否相等
  119. var validDev = devise.Where(v => v.Data2.Tasknum > 10000 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位));
  120. var stationDevices = validDev as StationDevice[] ?? validDev.ToArray();
  121. if (!stationDevices.Any()) throw new DoException("无有效入库任务");
  122. DB.Do(db =>
  123. {
  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, $"堆垛机开始执行入库", 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. task.CreateStatusLog(db, "堆垛机开始执行出库", this.GetType());
  264. item.SRMSTATION = task.SRMSTATION;
  265. finishTaskList.Add(new FinishTaskList<SrmFork, Task>(fork, item));
  266. }
  267. db.Default.SaveChanges();
  268. //此处只做标记,表示当前事务已经提交
  269. foreach (var finish in finishTaskList)
  270. {
  271. switch (finish.FinishCode)
  272. {
  273. // 列数较小的放一工位
  274. case SrmFork.货叉1:
  275. obj.Data.TaskID_1 = finish.Station.ID;
  276. break;
  277. //列数较大的放二工位
  278. case SrmFork.货叉2:
  279. obj.Data.TaskID_2 = finish.Station.ID;
  280. break;
  281. default:
  282. throw new ArgumentOutOfRangeException();
  283. }
  284. }
  285. });
  286. ////检查标记好的出库任务,并将出库任务下达至堆垛机
  287. DB.Do(db =>
  288. {
  289. //此处只做标记,表示当前事务已经提交
  290. foreach (var finish in finishTaskList)
  291. {
  292. var task = db.Default.Set<WCS_TASK>().Find(finish.Station.ID).Create<Task>();
  293. switch (finish.FinishCode)
  294. {
  295. // 列数较小的放一工位
  296. case SrmFork.货叉1:
  297. obj.WriteTask1(task, (short)finishTaskList.Count);
  298. break;
  299. //列数较大的放二工位
  300. case SrmFork.货叉2:
  301. obj.WriteTask2(task, (short)finishTaskList.Count);
  302. break;
  303. default:
  304. throw new ArgumentOutOfRangeException();
  305. }
  306. }
  307. });
  308. }
  309. }
  310. #endregion 倒库任务
  311. #region 移库
  312. if (isTransfer) //防止因为无当前堆垛机移库任务导致无法执行其他类型任务
  313. {
  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() >= 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. }
  390. #endregion 移库var lastIsOut = srmDevice.Entity.Get<bool>("LastIsOut");
  391. srmDevice.Entity.Set("LastIsOut", !lastIsOut);
  392. else if (lastIsOut)
  393. {
  394. #region 入库
  395. var floor = srmDevice.Entity.Get<int>("LastInFloor");
  396. floor = floor % 2 + 1;
  397. srmDevice.Entity.Set("LastInFloor", floor);
  398. if (enterPriority != null && outPriority != null && outPriority.Priority > enterPriority.Priority) return;
  399. //获取当前堆垛机的取货点
  400. var arrIn = srmDevice.GetPickPoint();
  401. if (!arrIn.Any()) return; //当前堆垛机无取货点
  402. var lastIn = srmDevice.Entity.Get<int>("LastIn");
  403. StationDevice station = null;
  404. #region 找到一个可用的取货点
  405. if (lastIn is 0 or 1)
  406. {
  407. // 涂布优先为2
  408. srmDevice.Entity.Set("LastIn", 2);
  409. station = arrIn.OrderBy(v => v.Data2.Tasknum > 0 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位) ? 0 : 1)
  410. .ThenBy(v => v.Entity.Is(DF.SRM涂布取货) ? 0 : 1)
  411. .ThenBy(v => v.Entity.Is(DF.SRMBOPP取货) ? 0 : 1)
  412. .FirstOrDefault() ?? throw new WarnException($"[{deviceCode}]等待入库任务输送到位");
  413. }
  414. else if (lastIn == 2)
  415. {
  416. // BOPP优先为1
  417. srmDevice.Entity.Set("LastIn", 1);
  418. station = arrIn.OrderBy(v => v.Data2.Tasknum > 0 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位) ? 0 : 1)
  419. .ThenBy(v => v.Entity.Is(DF.SRMBOPP取货) ? 0 : 1)
  420. .ThenBy(v => v.Entity.Is(DF.SRM涂布取货) ? 0 : 1)
  421. .FirstOrDefault() ?? throw new WarnException($"[{deviceCode}]等待入库任务输送到位");
  422. }
  423. #endregion 找到一个可用的取货点
  424. //根据上述筛选条件筛选出来的入库任务 找到对应的设备组
  425. var item = Device.Where(v => v.DEVICEGROUP.Any(p => p.MEMBER.CODE == station.Entity.CODE)).Single();
  426. //对数据进行排序,根据CAD图纸规划,取货点两个设备中设备号较大的一个一定对应到堆垛机的一工位货叉
  427. //因此按照降序进行排序,可以保证数组中第一个一定是放到堆垛机一工位的数据
  428. var devise = item.DEVICEGROUP.Select(v => v.MEMBER)
  429. .Select(v => v.Create<StationDevice>())
  430. .OrderByDescending(v => v.Entity.CODE)
  431. .ToArray();
  432. var finishTaskList = new List<FinishTaskList<int>>(); //成功分配货位的任务
  433. //检测有效任务数与实际任务是是否相等
  434. var validDev = devise.Where(v => v.Data2.Tasknum > 10000 && v.Data2.Status.HasFlag(IstationStatus.光电状态) && !v.Data3.Status.HasFlag(StationStatus.运行状态位));
  435. var stationDevices = validDev as StationDevice[] ?? validDev.ToArray();
  436. if (!stationDevices.Any()) throw new DoException("无有效入库任务");
  437. DB.Do(db =>
  438. {
  439. var tasks = db.Default.Set<WCS_TASK>().AsNoTracking().Where(v => v.STATUS < TaskStatus.已完成 && v.DEVICE == deviceCode && v.TYPE == TaskType.入库).ToList();
  440. var tasking = tasks.FirstOrDefault(v => v.ID == stationDevices.First().Data2.Tasknum);
  441. var taskList = tasks.Count(v => v.TaskGroupKey == tasking.TaskGroupKey);
  442. if (stationDevices.Count() != taskList) throw new WarnException($"任务数量不匹配,设备-{stationDevices.Count()},WCS-{taskList}");
  443. foreach (var dev in stationDevices)
  444. {
  445. var task = db.Default.Set<WCS_TASK>().FirstOrDefault(v => v.STATUS < TaskStatus.堆垛机执行 && v.ID == dev.Data2.Tasknum) ?? throw new WarnException($"设备有光电有任务且不在运行状态,但WCS找不到任务{dev.Data2.Tasknum}");
  446. var oldTask = task.STATUS;
  447. var tunnel = dev.Entity.ROUTES.First().NEXT.CODE;
  448. I_WCS_GetWareCellResponse loc;
  449. //判定当前设备对应的堆垛机工位
  450. if (dev.Entity.CODE == devise[0].Entity.CODE) loc = WMS.GetLocalIn(task.WMSTASK, tunnel, dev.Entity.CODE, Entity.WareCellForkNum.货叉1); //一工位
  451. else if (dev.Entity.CODE == devise[1].Entity.CODE) loc = WMS.GetLocalIn(task.WMSTASK, tunnel, dev.Entity.CODE, Entity.WareCellForkNum.货叉2); //2工位
  452. else throw new WarnException($"设备{dev.Entity.CODE}无法对应至堆垛机的任一一个工位");
  453. task.UPDATETIME = DateTime.Now;
  454. task.STATUS = TaskStatus.堆垛机执行;
  455. task.ADDRTO = $"{loc.Row}-{loc.Colomn}-{loc.Layer}";
  456. task.DEVICE = deviceCode;
  457. task.TUNNEL = tunnel;
  458. task.CreateStatusLog(db, $"堆垛机开始执行入库任务", this.GetType());
  459. finishTaskList.Add(new FinishTaskList<int>(task.ID, dev));
  460. }
  461. db.Default.SaveChanges();
  462. });
  463. DB.Do(db =>
  464. {
  465. foreach (var finish in finishTaskList)
  466. {
  467. var task = db.Default.Set<WCS_TASK>().Find(finish.FinishCode);
  468. var addTo = task!.ADDRTO.Split("-");
  469. if (finish.Station.Entity.CODE == devise[0].Entity.CODE) //一工位
  470. {
  471. 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()}]");
  472. srmDevice.Data.TaskID_1 = task.ID;
  473. srmDevice.Data.SLine_1 = finish.Station.Entity.CODE.ToShort();
  474. srmDevice.Data.SCol_1 = 0;
  475. srmDevice.Data.SLayer_1 = 0;
  476. srmDevice.Data.ELine_1 = addTo[0].ToShort();
  477. srmDevice.Data.ECol_1 = addTo[1].ToShort();
  478. srmDevice.Data.ELayer_1 = addTo[2].ToShort();
  479. srmDevice.Data.RES1_1 = finishTaskList.Count.ToShort();
  480. srmDevice.Data.VoucherNo_1++;
  481. 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()}]");
  482. }
  483. else if (finish.Station.Entity.CODE == devise[1].Entity.CODE)
  484. {
  485. 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()}]");
  486. srmDevice.Data.TaskID_2 = task.ID;
  487. srmDevice.Data.SLine_2 = finish.Station.Entity.CODE.ToShort();
  488. srmDevice.Data.SCol_2 = 0;
  489. srmDevice.Data.SLayer_2 = 0;
  490. srmDevice.Data.ELine_2 = addTo[0].ToShort();
  491. srmDevice.Data.ECol_2 = addTo[1].ToShort();
  492. srmDevice.Data.ELayer_2 = addTo[2].ToShort();
  493. srmDevice.Data.RES1_2 = finishTaskList.Count.ToShort();
  494. srmDevice.Data.VoucherNo_2++;
  495. 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()}]");
  496. }
  497. }
  498. });
  499. #endregion 入库
  500. }
  501. else
  502. {
  503. #region 出库
  504. var floor = srmDevice.Entity.Get<int>("LastOutFloor");
  505. floor = floor % 2 + 1;
  506. srmDevice.Entity.Set("LastOutFloor", floor);
  507. if (enterPriority != null && outPriority != null && enterPriority.Priority > outPriority.Priority) return;
  508. srmDevice.CheckOutTask();
  509. //获取当前堆垛机所有的放货点
  510. var list = srmDevice.GetDeliveryPoint().Where(v =>
  511. {
  512. //true:满足条件 false:不满足条件
  513. //返回结果为无货的设备 默认无货
  514. var res = true;
  515. //放货点是否有货
  516. if (v.Data.VoucherNo != v.Data2.VoucherNo) res = false;
  517. else if (v.Data3.Status.HasFlag(StationStatus.运行状态位)) res = false;
  518. else if (v.Data2.Status.HasFlag(IstationStatus.光电状态)) res = false;
  519. else if (v.Data2.Request == IstationRequest.堆垛机放货完成请求目标地址) res = false;
  520. else if (v.Data2.Tasknum > 10000) res = false;
  521. if (!v.Entity.Is(DF.SRM涂布放货)) return res;
  522. var devise = new List<StationDevice>();
  523. switch (v.Entity.CODE)
  524. {
  525. case "1283" or "1284":
  526. devise = Device.Where(b => b.CODE is "1281" or "1282").Select(b => b.Create<StationDevice>()).ToList();
  527. break;
  528. case "1290" or "1291" or "1292" or "1293":
  529. devise = Device.Where(b => b.CODE is "1288" or "1289").Select(b => b.Create<StationDevice>()).ToList();
  530. break;
  531. case "1299" or "1300" or "1301" or "1302":
  532. devise = Device.Where(b => b.CODE is "1297" or "1298").Select(b => b.Create<StationDevice>()).ToList();
  533. break;
  534. case "1308" or "1309" or "1310" or "1311":
  535. devise = Device.Where(b => b.CODE is "1306" or "1307").Select(b => b.Create<StationDevice>()).ToList();
  536. break;
  537. }
  538. if (!devise.Any()) return res;
  539. foreach (var stationDevice in devise)
  540. {
  541. //放货点是否有货
  542. if (stationDevice.Data3.Status.HasFlag(StationStatus.运行状态位)) res = false;
  543. else if (stationDevice.Data2.Status.HasFlag(IstationStatus.光电状态)) res = false;
  544. else if (stationDevice.Data2.Request == IstationRequest.堆垛机放货完成请求目标地址) res = false;
  545. else if (stationDevice.Data2.Tasknum > 10000) res = false;
  546. }
  547. return res;
  548. }).Select(v => v.Entity.CODE).ToList();
  549. //没有可用货位
  550. if (!list.Any()) return;
  551. //找到对应的设备组编号
  552. var groupList = Device.Where(v => v.IsDevGroup()).Where(v => v.DEVICEGROUP.Any(b => list.Contains(b.MEMBER.CODE))).Select(v => v.CODE).ToList();
  553. //堆垛机设备
  554. var srm = deviceCode;
  555. var finishTaskList = new List<FinishTaskList<SrmFork, Task>>();
  556. DB.Do(db =>
  557. {
  558. //堆垛机当前是否有正在执行的任务
  559. if (db.Default.Set<WCS_TASK>().Any(d => d.DEVICE == srm && d.STATUS == TaskStatus.堆垛机执行)) throw new WarnException($"[{deviceCode}]有正在执行的出库任务");
  560. //找出等待执行的出库任务
  561. var waitTask = db.Default.Set<WCS_TASK>().Where(v => v.STATUS == TaskStatus.新建)
  562. .Where(v => v.DEVICE == srm)
  563. .Where(v => v.TYPE == TaskType.出库)
  564. .Where(v => !db.Default.Set<WCS_TASK>().Any(d => d.DEVICE == srm && d.STATUS == TaskStatus.堆垛机执行 && d.Priority >= 0))
  565. .Where(v => groupList.Contains(v.SRMSTATION)) //站台必须可用
  566. .ToList();
  567. //同时对结果进行排序,分组
  568. var maximum = ProtocolProxy.YGWMS150Redis.Get("SaleTaskGroupCount").ToInt();
  569. var cTaskList = db.Default.Set<WCS_TASK>().AsNoTracking()
  570. .Where(d => d.TYPE == TaskType.出库)
  571. .Where(d => d.STATUS > TaskStatus.新建)
  572. .Where(d => d.STATUS < TaskStatus.已完成).ToList();
  573. 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")
  574. .OrderByDescending(v => v.Priority)
  575. .ThenBy(v => v.ADDRTO == "G1340" ? 0 : 1)
  576. .ThenBy(v => v.FLOOR == floor ? 0 : 1)
  577. .ThenBy(v => cTaskList.Where(d => d.ADDRTO == v.ADDRTO && d.FLOOR == v.FLOOR).GroupBy(d => d.TaskGroupKey).Count())
  578. .ThenBy(v => v.CREATETIME)
  579. .GroupBy(v => v.ADDRTO)
  580. .FirstOrDefault();
  581. if (outDepotGrouping == null) return; //用于解决Linq Value cannot be null. (Parameter 'source')
  582. var outDepotList = outDepotGrouping.Select(v => v).ToList();
  583. //获取两个个可执行任务,此时这两个任务的目标地址是一致的
  584. var tasks = outDepotList.GetOutTask();
  585. #region 校验两个产品是否为同规格
  586. if (tasks.Length == 2)
  587. {
  588. var length = tasks.OrderByDescending(v => v.Length).ToArray();
  589. //较大的长度减去较小的长度,差大于两百表示为不同规格产品
  590. if (length[0].Length - length[1].Length > 200)
  591. {
  592. tasks = tasks.Take(1).ToArray();
  593. }
  594. }
  595. #endregion 校验两个产品是否为同规格
  596. for (var i = 0; i < tasks.Length; i++)
  597. {
  598. var item = tasks[i];
  599. var task = db.Default.Set<WCS_TASK>().Find(item.ID);
  600. var oldTaskStatus = task!.STATUS;
  601. task.STARTTIME = DateTime.Now;
  602. task.UPDATETIME = DateTime.Now;
  603. task.STATUS = WCS.Entity.TaskStatus.堆垛机执行;
  604. task.DEVICE = deviceCode;
  605. task.TaskGroupKey = tasks.Length switch
  606. {
  607. 1 => $"{tasks[0].ID}_0",
  608. 2 => $"{tasks[0].ID}_{tasks[1].ID}",
  609. _ => throw new WarnException($"可用任务数异常{tasks.Length}"),
  610. };
  611. var fork = srmDevice.GetFork(item, i);
  612. //获取站台及下一个地址
  613. task.GetSrmStationAndaddNext(fork);
  614. task.CreateStatusLog(db, "堆垛机开始执行出库任务", this.GetType());
  615. item.SRMSTATION = task.SRMSTATION;
  616. finishTaskList.Add(new FinishTaskList<SrmFork, Task>(fork, item));
  617. }
  618. db.Default.SaveChanges();
  619. //此处只做标记,表示当前事务已经提交
  620. foreach (var finish in finishTaskList)
  621. {
  622. switch (finish.FinishCode)
  623. {
  624. // 列数较小的放一工位
  625. case SrmFork.货叉1:
  626. obj.Data.TaskID_1 = finish.Station.ID;
  627. break;
  628. //列数较大的放二工位
  629. case SrmFork.货叉2:
  630. obj.Data.TaskID_2 = finish.Station.ID;
  631. break;
  632. default:
  633. throw new ArgumentOutOfRangeException();
  634. }
  635. }
  636. });
  637. ////检查标记好的出库任务,并将出库任务下达至堆垛机
  638. DB.Do(db =>
  639. {
  640. //此处只做标记,表示当前事务已经提交
  641. foreach (var finish in finishTaskList)
  642. {
  643. var task = db.Default.Set<WCS_TASK>().Find(finish.Station.ID).Create<Task>();
  644. switch (finish.FinishCode)
  645. {
  646. // 列数较小的放一工位
  647. case SrmFork.货叉1:
  648. obj.WriteTask1(task, (short)finishTaskList.Count);
  649. break;
  650. //列数较大的放二工位
  651. case SrmFork.货叉2:
  652. obj.WriteTask2(task, (short)finishTaskList.Count);
  653. break;
  654. default:
  655. throw new ArgumentOutOfRangeException();
  656. }
  657. }
  658. });
  659. #endregion 出库
  660. }
  661. });
  662. }
  663. protected override bool SelectDevice(WCS_DEVICE dev)
  664. {
  665. return dev.Is(DF.SRM);
  666. }
  667. }
  668. }