using Newtonsoft.Json;
using ServiceCenter.Extensions;
using ServiceCenter.Logs;
using ServiceCenter.Redis;
using ServiceCenter.SqlSugars;
using SqlSugar;
using System.ComponentModel;
using WCS.Core;
using WCS.Entity;
using WCS.Entity.Protocol.Station;
using WCS.Entity.Protocol.Truss;
using WCS.WorkEngineering.Extensions;
using WCS.WorkEngineering.Worlds;
using DeviceFlags = WCS.WorkEngineering.Extensions.DeviceFlags;
using TaskStatus = WCS.Entity.TaskStatus;
using WCS_CacheLine = WCS.Entity.WCS_CacheLine;
using WCS_CacheLineLoc = WCS.Entity.WCS_CacheLineLoc;
namespace WCS.WorkEngineering.Systems.分拣支线
{
///
/// 桁架分流点
///
[BelongTo(typeof(SortingBranchWorld))]
[Description("桁架分流点")]
public class 桁架分流点 : DeviceSystem>
{
protected override bool ParallelDo => true;
public override void Do(Device obj)
{
if (obj.Data.VoucherNo != obj.Data2.VoucherNo)
{
World.Log($"凭证号不一致,DB520:{obj.Data.VoucherNo}-DB521:{obj.Data2.VoucherNo}");
return;
}
if (obj.Data3.Status.HasFlag(StationStatus.Run))
{
World.Log("设备运行中");
return;
}
if (!obj.Data3.Status.HasFlag(StationStatus.OT_Status))
{
World.Log("站台货物信息与实际占用不一致");
return;
}
;
if (obj.Data2.Request != 1)
{
World.Log("无请求");
return;
}
WCS_TaskInfo taskInfo = null;
SqlSugarHelper.Do(_db =>
{
var db = _db.Default;
var task =
db.Queryable()
.First(v => v.ID == obj.Data2.TaskNumber && v.Status < TaskStatus.FinishOfShunt) ??
throw new KnownException($"未找到对应的WCS任务{obj.Data2.TaskNumber}", LogLevelEnum.Mid);
if (Allot(db, task, obj)) taskInfo = task;
});
if (taskInfo == null) return;
obj.Data.TaskNumber = obj.Data2.TaskNumber;
obj.Data.TaskNumber = obj.Data2.TaskNumber;
obj.Data.GoodsStart = obj.Entity.Code.ToShort();
obj.Data.GoodsStart = obj.Entity.Code.ToShort();
obj.Data.GoodsEnd = taskInfo.AddrNext.ToShort();
obj.Data.GoodsEnd = taskInfo.AddrNext.ToShort();
obj.Data.SetVoucherNo();
World.Log($"执行记录:任务号[{obj.Data2.TaskNumber}][{obj.Data.VoucherNo}]");
}
public override bool Select(Device dev)
{
return dev.HasFlag(Extensions.DeviceFlags.桁架分流点);
}
///
/// 计算去向
///
///
///
///
///
public bool Allot(SqlSugarScopeProvider db, WCS_TaskInfo taskInfo,
Device obj)
{
var nowTime = DateTime.Now;
taskInfo.Device = obj.Entity.Code switch
{
"455" => "Truss1",
"655" => "Truss1",
"855" => "Truss2",
"1055" => "Truss2",
"1255" => "Truss3",
"1455" => "Truss3",
_ => "Error"
};
//获取当前货物类型可以到达的桁架缓存放行点信息
var cacheLineDevList = obj.Entity.Targets.Where(x => x.HasFlag(DeviceFlags.桁架缓存放行点)); //当前地址可以到达的所有桁架缓存放行点信息
switch (taskInfo.GoodsType)
{
case 9:
//是异常工字轮
cacheLineDevList = taskInfo.BarCode.Contains("Error")
? cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架09异常缓存放行点))
: cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架09缓存放行点));
break;
case 18:
cacheLineDevList = cacheLineDevList.Where(x => x.HasFlag(DeviceFlags.桁架18缓存放行点));
break;
}
var value = RedisHub.Default.Get("TrussTunnelForbidden");
if (!value.Contains("["))
{
World.Log($"没有桁架巷道禁用配置信息,请检查", LogLevelEnum.High);
return false;
}
var trussTunnelForbidden = JsonConvert.DeserializeObject>(value);
//排除禁用位置
cacheLineDevList = cacheLineDevList.Where(x => !trussTunnelForbidden.Contains(x.Code));
var cacheLineCodes = cacheLineDevList.Select(x => x.Code.ToShort());
var cacheLineList = db.Queryable().Includes(x => x.Locations).ToList();
#region 跟据缓存信息寻找可以到达的缓存点
//找到当前任务可用的缓存线信息
var cacheLine = cacheLineList.Where(x => x.Locations.Any(l => l is { InStock: false, IsEmpty: false }))
.FirstOrDefault(x =>
cacheLineCodes.Contains(x.LocationNo) && x.MatCodeList.Contains(taskInfo.MatCode) && !x.InStock);
//这个任务可以直接去一条线体,不需要新建缓存信息
//找到这条线体中序号最小的一条位信息 非空置且无货
var cacheLoc = cacheLine?.Locations.Where(x => x is { InStock: false, IsEmpty: false }).MinBy(x => x.XYNo);
if (cacheLoc != null)
{
cacheLoc = db.Queryable().Single(x => x.Id == cacheLoc.Id);
cacheLoc.InStock = true;
cacheLoc.TaskId = taskInfo.ID;
cacheLoc.EditTime = nowTime;
db.UpdateableRowLock(cacheLoc).UpdateColumns(x => new { x.InStock, x.TaskId, x.EditTime })
.ExecuteCommand();
//WCS任务相关信息
taskInfo.Status = TaskStatus.FinishOfShunt;
taskInfo.AddrNext = cacheLine.LocationNo.ToString();
taskInfo.EditWho = "WCS";
taskInfo.EditTime = nowTime;
db.UpdateableRowLock(taskInfo).UpdateColumns(x => new { x.Status, x.AddrNext, x.EditWho, x.EndTime })
.ExecuteCommand();
taskInfo.AddWCS_TASK_DTL(db, obj.Entity.Code, taskInfo.AddrNext, $"完成分库计算,目标地址:{cacheLine.LocationNo}");
return true;
}
#endregion 跟据缓存信息寻找可以到达的缓存点
#region 初始化一个信息的缓存信息
//找到一个可以进行缓存的线体号
var devCode = cacheLineDevList.Select(x => x.Code.ToShort()).OrderByDescending(x => x)
.FirstOrDefault(x => !cacheLineList.Select(s => s.LocationNo).Contains(x));
if (devCode == 0)
{
World.Log($"无可用线体:{taskInfo.ID}", LogLevelEnum.Mid);
//如果当前工字轮是18,不会触发当前逻辑,因为所有在桁架码垛的18工字轮仅一种规格
var endTime = DateTime.Now.AddMinutes(-30);
//找一个当前时间最早且已超过30分钟未码垛的码垛信息半托入库
//未结束且包含当前物料编号的垛形,按时间排序,创建时间早的优先分配,当前任务的仓库号必须要等于码垛信息绑定的仓库号
var palletizing = db.Queryable()
.Includes(x => x.Layers, r => r.Rows, l => l.Locs)
.Where(x => !x.Finish && x.WarehouseCode == taskInfo.WarehouseCode &&
x.Layers.Any(l => l.Rows.Any(r => r.Finish)))
.Where(x => x.EditTime < endTime).ToList()
.Where(x => x.Layers.SelectMany(l => l.Rows).GroupBy(r => r.MatCodeList).Count() > 1)
.MinBy(x => x.AddTime);
cacheLineList = db.Queryable()
.Where(x => x.WarehouseCode == taskInfo.WarehouseCode && x.IsTruss).ToList();
if (palletizing == null || cacheLineList.Any()) return false;
{
var dev = Device.All.Where(x => x.Code == palletizing.PalletizingStation)
.Select(x => new Device(x, World)).FirstOrDefault();
if (dev != null && !dev.Data.CmdType.HasFlag(TrussCmdType.Two) && !dev.Data.CmdType.HasFlag(TrussCmdType.End1))
{
//如果找到的最早的一个托盘超六小时了,就直接强制结盘
dev.Data.CmdType = palletizing.AddTime < DateTime.Now.AddHours(-6) ? TrussCmdType.End1 : TrussCmdType.Two;
}
}
return false;
}
var result = false;
//未结束且包含当前物料编号的垛形,按时间排序,创建时间早的优先分配,当前任务的仓库号必须要等于码垛信息绑定的仓库号
var palletizingList = db.Queryable()
.Includes(x => x.Layers, r => r.Rows, l => l.Locs)
.Where(x => (!x.Finish /*|| (x.Finish && x.isItHalf)*/) && x.MatCodeList.Contains(taskInfo.MatCode) &&
x.WarehouseCode == taskInfo.WarehouseCode)
.OrderBy(x => x.AddTime)
.ToList();
foreach (var palletizing in palletizingList)
{
//当前任务已经分配结束,进入下一次迭代
if (result) continue;
//未结束且包含当前物料编号的层,按层号排序,层号小的优先分配
var layer = palletizing.Layers.Where(x => x is { IsEmpty: false, Finish: false })
.Where(x => x.MatCodeList.Contains(taskInfo.MatCode)).MinBy(x => x.LayerNo);
//未找到可用层,进入下一次迭代
if (layer == null) continue;
// 当前任务已经分配结束,进入下一次迭代
//未结束,未预分配缓存线信息且包含当前物料编号的行,行号小的优先分配
var rows = layer.Rows.Where(x => x is { IsEmpty: false, Finish: false })
.Where(x => x.CacheLineId == 0)
.Where(x => x.MatCodeList.Contains(taskInfo.MatCode))
.ToList();
//未找到可用行,进入下一次迭代
if (!rows.Any()) continue;
var palletizingRow = rows.MinBy(x => x.RowNo);
//开始初始化缓存位信息
cacheLine = new WCS_CacheLine()
{
LocationNo = devCode,
AddTime = nowTime,
EditTime = nowTime,
PalletizingRowId = palletizingRow.Id,
InStock = false,
Put = false,
IsTruss = false,
MatCodeList = palletizingRow.MatCodeList,
Quantity = palletizingRow.QtyMaxCount,
WarehouseCode = taskInfo.WarehouseCode
};
if (palletizing.isItHalf) //当前任务被分配到了二次码垛托盘上了。
{
var twoDevCode = obj.Entity.Code switch
{
"455" => "1606",
"655" => "1616",
"855" => "1626",
"1055" => "1636",
"1255" => "1646",
"1455" => "1656",
_ => throw new ArgumentOutOfRangeException()
};
//找到一个对应的空置码垛位
var twoDevs = Device.All.Where(x => twoDevCode == x.Code)
.Select(x => new Device(x, World))
.Where(x => !x.Data3.Status.HasFlag(StationStatus.PH_Status)).Select(x => x.Entity.Code);
//找到一个对应的空置码垛位
var twoDev = new Device(Device.All.First(x => x.Code == twoDevCode), World);
if (twoDev.Data3.Status.HasFlag(StationStatus.PH_Status))
{
World.Log("无可用的二次码垛工位");
continue;
}
if (db.Queryable().Any(x => !x.Finish && twoDevs.Contains(x.PalletizingStation)))
{
World.Log($"有正目标地址是{JsonConvert.SerializeObject(twoDevs)}的未结束二次码垛任务,无可用的二次码垛工位");
continue;
}
palletizing.EditTime = nowTime;
palletizing.Finish = false;
palletizing.PalletizingStation = twoDevs.FirstOrDefault();
palletizing.TaskAddNext = devCode.ToString();
db.UpdateableRowLock(palletizing).UpdateColumns(x => new { x.EditTime, x.Finish, x.PalletizingStation, x.TaskAddNext }).ExecuteCommand();
}
if (palletizing.TaskAddNext == null)
{
palletizing.TaskAddNext = devCode.ToString();
db.Updateable(palletizing).UpdateColumns(x => new { x.TaskAddNext }).ExecuteCommand();
}
var res = db.InsertableRowLock(cacheLine).ExecuteReturnEntity();
palletizingRow = db.Queryable().Includes(x => x.Locs)
.Single(x => x.Id == palletizingRow.Id);
palletizingRow.Locs = palletizingRow.Locs.OrderBy(x => x.XYNo).ToList();
palletizingRow.CacheLineId = res.Id;
palletizingRow.EditTime = nowTime;
var createCacheLoc = palletizingRow.Locs.Select((t, i) => new WCS_CacheLineLoc()
{
XYNo = t.XYNo,
InStock = i == 0,
IsEmpty = t.IsEmpty,
MatCode = t.MatCode,
TaskId = i == 0 ? taskInfo.ID : 0,
CacheLineId = res.Id,
AddTime = nowTime,
SideNum = t.SideNum,
EditTime = nowTime,
TimeOut = t.TimeOut
}).ToList();
taskInfo.Status = TaskStatus.FinishOfShunt;
taskInfo.AddrNext = devCode.ToString();
taskInfo.EditWho = "WCS";
taskInfo.EditTime = nowTime;
db.UpdateableRowLock(taskInfo).UpdateColumns(x => new { x.Status, x.AddrNext, x.EditWho, x.EditTime })
.ExecuteCommand();
taskInfo.AddWCS_TASK_DTL(db, obj.Entity.Code, taskInfo.AddrNext, $"完成分库计算,目标地址:{cacheLine.LocationNo}");
db.UpdateableRowLock(palletizingRow).UpdateColumns(x => new { x.CacheLineId, x.EditTime })
.ExecuteCommand();
db.InsertableRowLock(createCacheLoc).ExecuteCommand();
result = true;
}
if (!result)
{
taskInfo.InitStackStructure(db, World);
}
return result;
#endregion 初始化一个信息的缓存信息
}
}
}