桁架分流点.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. using Newtonsoft.Json;
  2. using ServiceCenter.Extensions;
  3. using ServiceCenter.Logs;
  4. using ServiceCenter.Redis;
  5. using ServiceCenter.SqlSugars;
  6. using SqlSugar;
  7. using System.ComponentModel;
  8. using WCS.Core;
  9. using WCS.Entity;
  10. using WCS.Entity.Protocol.Station;
  11. using WCS.Entity.Protocol.Truss;
  12. using WCS.WorkEngineering.Extensions;
  13. using WCS.WorkEngineering.Worlds;
  14. using DeviceFlags = WCS.WorkEngineering.Extensions.DeviceFlags;
  15. using TaskStatus = WCS.Entity.TaskStatus;
  16. using WCS_CacheLine = WCS.Entity.WCS_CacheLine;
  17. using WCS_CacheLineLoc = WCS.Entity.WCS_CacheLineLoc;
  18. namespace WCS.WorkEngineering.Systems.分拣支线
  19. {
  20. /// <summary>
  21. /// 桁架分流点
  22. /// </summary>
  23. [BelongTo(typeof(SortingBranchWorld))]
  24. [Description("桁架分流点")]
  25. public class 桁架分流点 : DeviceSystem<Device<IStation520, IStation521, IStation523>>
  26. {
  27. protected override bool ParallelDo => true;
  28. public override void Do(Device<IStation520, IStation521, IStation523> obj)
  29. {
  30. if (obj.Data.VoucherNo != obj.Data2.VoucherNo)
  31. {
  32. World.Log($"凭证号不一致,DB520:{obj.Data.VoucherNo}-DB521:{obj.Data2.VoucherNo}");
  33. return;
  34. }
  35. if (obj.Data3.Status.HasFlag(StationStatus.Run))
  36. {
  37. World.Log("设备运行中");
  38. return;
  39. }
  40. if (!obj.Data3.Status.HasFlag(StationStatus.OT_Status))
  41. {
  42. World.Log("站台货物信息与实际占用不一致");
  43. return;
  44. }
  45. ;
  46. if (obj.Data2.Request != 1)
  47. {
  48. World.Log("无请求");
  49. return;
  50. }
  51. WCS_TaskInfo taskInfo = null;
  52. SqlSugarHelper.Do(_db =>
  53. {
  54. var db = _db.Default;
  55. var task =
  56. db.Queryable<WCS_TaskInfo>()
  57. .First(v => v.ID == obj.Data2.TaskNumber && v.Status < TaskStatus.FinishOfShunt) ??
  58. throw new KnownException($"未找到对应的WCS任务{obj.Data2.TaskNumber}", LogLevelEnum.Mid);
  59. if (Allot(db, task, obj)) taskInfo = task;
  60. });
  61. if (taskInfo == null) return;
  62. obj.Data.TaskNumber = obj.Data2.TaskNumber;
  63. obj.Data.TaskNumber = obj.Data2.TaskNumber;
  64. obj.Data.GoodsStart = obj.Entity.Code.ToShort();
  65. obj.Data.GoodsStart = obj.Entity.Code.ToShort();
  66. obj.Data.GoodsEnd = taskInfo.AddrNext.ToShort();
  67. obj.Data.GoodsEnd = taskInfo.AddrNext.ToShort();
  68. obj.Data.SetVoucherNo();
  69. World.Log($"执行记录:任务号[{obj.Data2.TaskNumber}][{obj.Data.VoucherNo}]");
  70. }
  71. public override bool Select(Device dev)
  72. {
  73. return dev.HasFlag(Extensions.DeviceFlags.桁架分流点);
  74. }
  75. /// <summary>
  76. /// 计算去向
  77. /// </summary>
  78. /// <param name="db"></param>
  79. /// <param name="taskInfo"></param>
  80. /// <param name="obj"></param>
  81. /// <returns></returns>
  82. public bool Allot(SqlSugarScopeProvider db, WCS_TaskInfo taskInfo,
  83. Device<IStation520, IStation521, IStation523> obj)
  84. {
  85. var nowTime = DateTime.Now;
  86. taskInfo.Device = obj.Entity.Code switch
  87. {
  88. "455" => "Truss1",
  89. "655" => "Truss1",
  90. "855" => "Truss2",
  91. "1055" => "Truss2",
  92. "1255" => "Truss3",
  93. "1455" => "Truss3",
  94. _ => "Error"
  95. };
  96. //获取当前货物类型可以到达的桁架缓存放行点信息
  97. var cacheLineDevList = obj.Entity.Targets.Where(x => x.HasFlag(DeviceFlags.桁架缓存放行点)); //当前地址可以到达的所有桁架缓存放行点信息
  98. switch (taskInfo.GoodsType)
  99. {
  100. case 9:
  101. //是异常工字轮
  102. cacheLineDevList = taskInfo.BarCode.Contains("Error")
  103. ? cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架09异常缓存放行点))
  104. : cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架09缓存放行点));
  105. break;
  106. case 18:
  107. cacheLineDevList = cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架18缓存放行点));
  108. break;
  109. }
  110. var value = RedisHub.Default.Get("TrussTunnelForbidden");
  111. if (!value.Contains("["))
  112. {
  113. World.Log($"没有桁架巷道禁用配置信息,请检查", LogLevelEnum.High);
  114. return false;
  115. }
  116. var trussTunnelForbidden = JsonConvert.DeserializeObject<List<string>>(value);
  117. //排除禁用位置
  118. cacheLineDevList = cacheLineDevList.Where(x => !trussTunnelForbidden.Contains(x.Code));
  119. var cacheLineCodes = cacheLineDevList.Select(x => x.Code.ToShort());
  120. var cacheLineList = db.Queryable<WCS_CacheLine>().Includes(x => x.Locations).ToList();
  121. #region 跟据缓存信息寻找可以到达的缓存点
  122. //找到当前任务可用的缓存线信息
  123. var cacheLine = cacheLineList.Where(x => x.Locations.Any(l => l is { InStock: false, IsEmpty: false }))
  124. .FirstOrDefault(x =>
  125. cacheLineCodes.Contains(x.LocationNo) && x.MatCodeList.Contains(taskInfo.MatCode) && !x.InStock);
  126. //这个任务可以直接去一条线体,不需要新建缓存信息
  127. //找到这条线体中序号最小的一条位信息 非空置且无货
  128. var cacheLoc = cacheLine?.Locations.Where(x => x is { InStock: false, IsEmpty: false }).MinBy(x => x.XYNo);
  129. if (cacheLoc != null)
  130. {
  131. cacheLoc = db.Queryable<WCS_CacheLineLoc>().Single(x => x.Id == cacheLoc.Id);
  132. cacheLoc.InStock = true;
  133. cacheLoc.TaskId = taskInfo.ID;
  134. cacheLoc.EditTime = nowTime;
  135. db.UpdateableRowLock(cacheLoc).UpdateColumns(x => new { x.InStock, x.TaskId, x.EditTime })
  136. .ExecuteCommand();
  137. //WCS任务相关信息
  138. taskInfo.Status = TaskStatus.FinishOfShunt;
  139. taskInfo.AddrNext = cacheLine.LocationNo.ToString();
  140. taskInfo.EditWho = "WCS";
  141. taskInfo.EditTime = nowTime;
  142. db.UpdateableRowLock(taskInfo).UpdateColumns(x => new { x.Status, x.AddrNext, x.EditWho, x.EndTime })
  143. .ExecuteCommand();
  144. taskInfo.AddWCS_TASK_DTL(db, obj.Entity.Code, taskInfo.AddrNext, $"完成分库计算,目标地址:{cacheLine.LocationNo}");
  145. return true;
  146. }
  147. #endregion 跟据缓存信息寻找可以到达的缓存点
  148. #region 初始化一个信息的缓存信息
  149. //找到一个可以进行缓存的线体号
  150. var devCode = cacheLineDevList.Select(x => x.Code.ToShort()).OrderByDescending(x => x)
  151. .FirstOrDefault(x => !cacheLineList.Select(s => s.LocationNo).Contains(x));
  152. if (devCode == 0)
  153. {
  154. World.Log($"无可用线体:{taskInfo.ID}", LogLevelEnum.Mid);
  155. //如果当前工字轮是18,不会触发当前逻辑,因为所有在桁架码垛的18工字轮仅一种规格
  156. var endTime = DateTime.Now.AddMinutes(-30);
  157. //找一个当前时间最早且已超过30分钟未码垛的码垛信息半托入库
  158. //未结束且包含当前物料编号的垛形,按时间排序,创建时间早的优先分配,当前任务的仓库号必须要等于码垛信息绑定的仓库号
  159. var palletizing = db.Queryable<WCS_Palletizing>()
  160. .Includes(x => x.Layers, r => r.Rows, l => l.Locs)
  161. .Where(x => !x.Finish && x.WarehouseCode == taskInfo.WarehouseCode &&
  162. x.Layers.Any(l => l.Rows.Any(r => r.Finish)))
  163. .Where(x => x.EditTime < endTime).ToList()
  164. .Where(x => x.Layers.SelectMany(l => l.Rows).GroupBy(r => r.MatCodeList).Count() > 1)
  165. .MinBy(x => x.AddTime);
  166. cacheLineList = db.Queryable<WCS.Entity.WCS_CacheLine>()
  167. .Where(x => x.WarehouseCode == taskInfo.WarehouseCode && x.IsTruss).ToList();
  168. if (palletizing == null || cacheLineList.Any()) return false;
  169. {
  170. var dev = Device.All.Where(x => x.Code == palletizing.PalletizingStation)
  171. .Select(x => new Device<ITruss530>(x, World)).FirstOrDefault();
  172. if (dev != null && !dev.Data.CmdType.HasFlag(TrussCmdType.Two) && !dev.Data.CmdType.HasFlag(TrussCmdType.End1))
  173. {
  174. //如果找到的最早的一个托盘超六小时了,就直接强制结盘
  175. dev.Data.CmdType = palletizing.AddTime < DateTime.Now.AddHours(-6) ? TrussCmdType.End1 : TrussCmdType.Two;
  176. }
  177. }
  178. return false;
  179. }
  180. var result = false;
  181. //未结束且包含当前物料编号的垛形,按时间排序,创建时间早的优先分配,当前任务的仓库号必须要等于码垛信息绑定的仓库号
  182. var palletizingList = db.Queryable<WCS_Palletizing>()
  183. .Includes(x => x.Layers, r => r.Rows, l => l.Locs)
  184. .Where(x => (!x.Finish /*|| (x.Finish && x.isItHalf)*/) && x.MatCodeList.Contains(taskInfo.MatCode) &&
  185. x.WarehouseCode == taskInfo.WarehouseCode)
  186. .OrderBy(x => x.AddTime)
  187. .ToList();
  188. foreach (var palletizing in palletizingList)
  189. {
  190. //当前任务已经分配结束,进入下一次迭代
  191. if (result) continue;
  192. //未结束且包含当前物料编号的层,按层号排序,层号小的优先分配
  193. var layer = palletizing.Layers.Where(x => x is { IsEmpty: false, Finish: false })
  194. .Where(x => x.MatCodeList.Contains(taskInfo.MatCode)).MinBy(x => x.LayerNo);
  195. //未找到可用层,进入下一次迭代
  196. if (layer == null) continue;
  197. // 当前任务已经分配结束,进入下一次迭代
  198. //未结束,未预分配缓存线信息且包含当前物料编号的行,行号小的优先分配
  199. var rows = layer.Rows.Where(x => x is { IsEmpty: false, Finish: false })
  200. .Where(x => x.CacheLineId == 0)
  201. .Where(x => x.MatCodeList.Contains(taskInfo.MatCode))
  202. .ToList();
  203. //未找到可用行,进入下一次迭代
  204. if (!rows.Any()) continue;
  205. var palletizingRow = rows.MinBy(x => x.RowNo);
  206. //开始初始化缓存位信息
  207. cacheLine = new WCS_CacheLine()
  208. {
  209. LocationNo = devCode,
  210. AddTime = nowTime,
  211. EditTime = nowTime,
  212. PalletizingRowId = palletizingRow.Id,
  213. InStock = false,
  214. Put = false,
  215. IsTruss = false,
  216. MatCodeList = palletizingRow.MatCodeList,
  217. Quantity = palletizingRow.QtyMaxCount,
  218. WarehouseCode = taskInfo.WarehouseCode
  219. };
  220. if (palletizing.isItHalf) //当前任务被分配到了二次码垛托盘上了。
  221. {
  222. var twoDevCode = obj.Entity.Code switch
  223. {
  224. "455" => "1606",
  225. "655" => "1616",
  226. "855" => "1626",
  227. "1055" => "1636",
  228. "1255" => "1646",
  229. "1455" => "1656",
  230. _ => throw new ArgumentOutOfRangeException()
  231. };
  232. //找到一个对应的空置码垛位
  233. var twoDevs = Device.All.Where(x => twoDevCode == x.Code)
  234. .Select(x => new Device<IStation520, IStation521, IStation523>(x, World))
  235. .Where(x => !x.Data3.Status.HasFlag(StationStatus.PH_Status)).Select(x => x.Entity.Code);
  236. //找到一个对应的空置码垛位
  237. var twoDev = new Device<IStation520, IStation521, IStation523>(Device.All.First(x => x.Code == twoDevCode), World);
  238. if (twoDev.Data3.Status.HasFlag(StationStatus.PH_Status))
  239. {
  240. World.Log("无可用的二次码垛工位");
  241. continue;
  242. }
  243. if (db.Queryable<WCS_Palletizing>().Any(x => !x.Finish && twoDevs.Contains(x.PalletizingStation)))
  244. {
  245. World.Log($"有正目标地址是{JsonConvert.SerializeObject(twoDevs)}的未结束二次码垛任务,无可用的二次码垛工位");
  246. continue;
  247. }
  248. palletizing.EditTime = nowTime;
  249. palletizing.Finish = false;
  250. palletizing.PalletizingStation = twoDevs.FirstOrDefault();
  251. palletizing.TaskAddNext = devCode.ToString();
  252. db.UpdateableRowLock(palletizing).UpdateColumns(x => new { x.EditTime, x.Finish, x.PalletizingStation, x.TaskAddNext }).ExecuteCommand();
  253. }
  254. if (palletizing.TaskAddNext == null)
  255. {
  256. palletizing.TaskAddNext = devCode.ToString();
  257. db.Updateable(palletizing).UpdateColumns(x => new { x.TaskAddNext }).ExecuteCommand();
  258. }
  259. var res = db.InsertableRowLock(cacheLine).ExecuteReturnEntity();
  260. palletizingRow = db.Queryable<WCS_PalletizingRow>().Includes(x => x.Locs)
  261. .Single(x => x.Id == palletizingRow.Id);
  262. palletizingRow.Locs = palletizingRow.Locs.OrderBy(x => x.XYNo).ToList();
  263. palletizingRow.CacheLineId = res.Id;
  264. palletizingRow.EditTime = nowTime;
  265. var createCacheLoc = palletizingRow.Locs.Select((t, i) => new WCS_CacheLineLoc()
  266. {
  267. XYNo = t.XYNo,
  268. InStock = i == 0,
  269. IsEmpty = t.IsEmpty,
  270. MatCode = t.MatCode,
  271. TaskId = i == 0 ? taskInfo.ID : 0,
  272. CacheLineId = res.Id,
  273. AddTime = nowTime,
  274. SideNum = t.SideNum,
  275. EditTime = nowTime,
  276. TimeOut = t.TimeOut
  277. }).ToList();
  278. taskInfo.Status = TaskStatus.FinishOfShunt;
  279. taskInfo.AddrNext = devCode.ToString();
  280. taskInfo.EditWho = "WCS";
  281. taskInfo.EditTime = nowTime;
  282. db.UpdateableRowLock(taskInfo).UpdateColumns(x => new { x.Status, x.AddrNext, x.EditWho, x.EditTime })
  283. .ExecuteCommand();
  284. taskInfo.AddWCS_TASK_DTL(db, obj.Entity.Code, taskInfo.AddrNext, $"完成分库计算,目标地址:{cacheLine.LocationNo}");
  285. db.UpdateableRowLock(palletizingRow).UpdateColumns(x => new { x.CacheLineId, x.EditTime })
  286. .ExecuteCommand();
  287. db.InsertableRowLock(createCacheLoc).ExecuteCommand();
  288. result = true;
  289. }
  290. if (!result)
  291. {
  292. taskInfo.InitStackStructure(db, World);
  293. }
  294. return result;
  295. #endregion 初始化一个信息的缓存信息
  296. }
  297. }
  298. }