You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1108 lines
59 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using SlnMesnac.Business.@base;
using SlnMesnac.Model.domain;
using SlnMesnac.Model.dto.taskType;
using SlnMesnac.Plc;
using SlnMesnac.Serilog;
using SlnMesnac.WCS.Global;
using SlnMesnac.WCS.Library;
using SqlSugar;
using System.Threading.Tasks;
namespace SlnMesnac.WCS.WCS
{
/// <summary>
/// 根据条件创建任务
/// </summary>
public class CreateTaskByRecord : BaseBusiness
{
private readonly SerilogHelper _logger;
//3#车间上料点plc
private readonly PlcAbsractFactory workShop3Plc;
//2#计量室相关点位
private readonly PlcAbsractFactory workShop2Plc;
private readonly ISqlSugarClient sqlSugarClient;
public CreateTaskByRecord(IServiceProvider serviceProvider) : base(serviceProvider)
{
workShop3Plc = base.GetPlcByKey("workShop3Plc");
workShop2Plc = base.GetPlcByKey("workShop2Plc");
sqlSugarClient = serviceProvider.GetRequiredService<ISqlSugarClient>();
_logger = serviceProvider.GetRequiredService<SerilogHelper>();
//test();
}
/// <summary>
/// 启动扫描监听
/// </summary>
public void StartPoint()
{
CreateTaskByReadPlcSignal();
//监听2#计量室旋转移栽plc信号处理相关流程
var CreateProductTaskByLocationCodeThread = new Thread(ListeningWorkShop2TransplantingMachine);
CreateProductTaskByLocationCodeThread.IsBackground = true;
CreateProductTaskByLocationCodeThread.Name = "监听1#2#计量室旋转移栽plc信号处理连廊箱体旋转相关流程";
CreateProductTaskByLocationCodeThread.Start();
}
/// <summary>
/// 通过读plc条件创建任务
///
/// </summary>
private void CreateTaskByReadPlcSignal()
{
Task.Run(() =>
{
while (true)
{
try
{
if (workShop3Plc == null || !workShop3Plc.IsConnected)
{
_logger.Plc(DateTime.Now + "3#PLC未连接,请检查网络!");
continue;
}
#region 3#车间补四宫格空箱流程---监听3#1号光电状态
bool signal1 = workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#大料箱进料为空"));
if (signal1)
{
CreateSupplementBigPalletTask();
}
#endregion 3#车间补四宫格空箱流程---监听3#1号光电状态
#region 从下料点到1-16机台送料任务--或者1-12移库任务 --监听3#2号光电状态
bool getBigFlag = workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#大料箱agv取料通知"));
string machineCodeS = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#四宫格下料点机台号")).ToString();
string rfidS = workShop3Plc.readStringByAddress(StaticData.GetPlcAddress("3#四宫格下料点RFID号"), 12).Replace("\0", "");
int amount = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#四宫格下料点数量"));
if (getBigFlag && machineCodeS != "0" && rfidS.Contains("SH"))
{
CreatDeliverWorkShop3Task(machineCodeS, rfidS, amount);
}
#endregion 从下料点到1-16机台送料任务--或者1-12移库任务 --监听3#2号光电状态
#region 3#往2#计量室送料流程
bool getSmallFlag = workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#小料箱agv取料通知"));
int smallAmount = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#小料箱下料点数量"));
string machineCodeX = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#小料箱下料点机台号")).ToString();
string rfidX = workShop3Plc.readStringByAddress(StaticData.GetPlcAddress("3#小料箱下料点RFID号"), 12).Replace("\0", "");
if (getSmallFlag && machineCodeX != "0" && rfidX.Contains("SH"))
{
if (machineCodeX == "1")
{
machineCodeX = "1006";
}
CreatDeliverWorkShop2Task(machineCodeX, rfidX);
}
#endregion 3#往2#计量室送料流程
#region 3#补充小空托盘流程--3#车间从色粉存放点补充小托盘任务 或者 3#车间从2#接驳位补充小托盘任务
int work3EmptyAmount = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#缓存皮带线库存数"));
int busy3Flag = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#线体忙碌状态"));
if ((work3EmptyAmount == 1 && busy3Flag == 0) || work3EmptyAmount == 0)
{
//3#车间从色粉存放点补充小托盘任务 或者 3#车间从2#接驳位补充小托盘任务
CreateSupplySmallPalletToWorkshop3Task();
}
#endregion 3#补充小空托盘流程--3#车间从色粉存放点补充小托盘任务 或者 3#车间从2#接驳位补充小托盘任务
}
catch (Exception ex)
{
_logger.Error($"CreateTaskByReadPlcSignal方法异常:" + ex.StackTrace);
}
finally
{
Thread.Sleep(1000);
}
}
});
}
/// <summary>
/// 监听2#计量室旋转移栽plc信号处理连廊箱体旋转相关流程
/// </summary>
private void ListeningWorkShop2TransplantingMachine()
{
while (true)
{
try
{
if (workShop2Plc == null || !workShop2Plc.IsConnected)
{
continue;
}
#region 2#计量室料箱返回处理流程:空箱入缓存链条线,色粉派送至色粉存放点
//2#接驳位色粉派送至色粉存放点任务
if (workShop2Plc != null && workShop2Plc.IsConnected)
{
bool returnJudgeSignal = workShop2Plc.readBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"));
int busy2Flag = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"));
int Amount = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#缓存链条线库存数"));
//是否还有未在色粉库位的色粉箱体
bool anyTonerPallet = HasAnyTonerInLine();
if (returnJudgeSignal && busy2Flag == 0 && (Amount < 3 || anyTonerPallet))
{
string rfid = ReadEpcStrByRfidKey("2#Transplant");
if (string.IsNullOrEmpty(rfid))
{//二次读取
rfid = ReadEpcStrByRfidKey("2#Transplant");
}
if (!string.IsNullOrEmpty(rfid))
{
if (Amount < 3)
{
Thread.Sleep(2000);
}
else if (anyTonerPallet) //有色粉空转
{
Thread.Sleep(4500);
}
busy2Flag = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"));
if (busy2Flag == 0)
{
// 判断有无色粉, 如果没有色粉wcs给 旋转移栽方向信号 写2plc将料箱运到 缓存皮带线。
WmsPalletInfo? wmsPalletInfo = sqlSugarClient.Queryable<WmsPalletInfo>().First(it => it.PalletInfoCode == rfid);
if (wmsPalletInfo != null && wmsPalletInfo.Amount > 0 && wmsPalletInfo.TonerFlag == 1) //携带色粉
{
//注意plc上旋转移栽必须锁住线体状态才可以2#接驳位如果有满料那么通知plc信号plc在连廊不再把箱体放到旋转移栽上
//调度系统结合上面缓存链条线库存如果上面低于3那么空箱可以上来缓存线
bool createResult = CreateDeliverTonerTask(rfid);
if (createResult)
{
//3色粉上提升机移栽等待
_logger.Info($"2#移栽===={rfid}携带色粉去往色粉库位");
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 3);
workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
}
else
{ //色粉库位无空闲位置,空转一圈
//2空箱转运
_logger.Info($"2#移栽===={rfid}携带色粉,色粉库位无空闲位置,空转一圈");
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 2);
workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
}
}
else if (wmsPalletInfo != null && wmsPalletInfo.Amount > 0 && wmsPalletInfo.TonerFlag == 0) //携带满料
{
//2转运
_logger.Info($"2#移栽===={rfid}携带满料,空转一圈");
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 2);
workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
}
else if (wmsPalletInfo != null && wmsPalletInfo.Amount == 0) //空托盘
{
if (Amount < 3)
{
//1上提升机去缓存线
_logger.Info($"2#移栽===={rfid}空箱,去缓存区");
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 1);
workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
}
else
{
//2空箱转运
_logger.Info($"2#移栽===={rfid}空箱,空箱转运");
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 2);
workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
}
}
}
}
else
{
//没读到2空箱转运
// _logger.Info($"1#移栽====没读到,空箱转运");
//workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
//workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 2);
//workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false);
}
}
}
#endregion 2#计量室料箱返回处理流程:空箱入缓存链条线,色粉派送至色粉存放点
#region 1#连廊料箱在计量室连廊移栽处理流程wcs读RFID判断是否空箱空箱下plc信号旋转非空箱通知plc上提升机计量室
if (workShop2Plc != null && workShop2Plc.IsConnected)
{
bool returnJudgeSignal = workShop2Plc.readBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"));
int busy1Flag = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("1#提升机忙碌状态"));
if (returnJudgeSignal && busy1Flag == 0)
{
//test
string rfid = ReadEpcStrByRfidKey("1#MetrologyRoom");
if (string.IsNullOrEmpty(rfid))
{
rfid = ReadEpcStrByRfidKey("1#MetrologyRoom");
}
if (!string.IsNullOrEmpty(rfid))
{
// 判断有无料, 如果没有料wcs下发空箱旋转信号有料通知plc上提升机计量室
WmsPalletInfo? wmsPalletInfo = sqlSugarClient.Queryable<WmsPalletInfo>().First(it => it.PalletInfoCode == rfid);
if (wmsPalletInfo != null && wmsPalletInfo.Amount > 0 && wmsPalletInfo.TonerFlag == 1) //携带色粉
{
//2空箱转运
_logger.Info($"1#移栽===={rfid}携带色粉,空箱转运");
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("1#移栽平台任务"), 2);
workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false);
}
if (wmsPalletInfo != null && wmsPalletInfo.Amount > 0 && wmsPalletInfo.TonerFlag == 0) //携带满料
{
//1#移栽平台任务1上提升机,2空箱转运
_logger.Info($"1#移栽===={rfid}携带满料,去提升机计量室");
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("1#移栽平台任务"), 1);
workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false);
}
else if (wmsPalletInfo != null && wmsPalletInfo.Amount == 0)
{
//1#移栽平台任务1上提升机,2空箱转运
_logger.Info($"1#移栽===={rfid}空箱,空箱转运");
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("1#移栽平台任务"), 2);
workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false);
}
}
else
{
//没读到,当成空箱子旋转一圈
//1#移栽平台任务1上提升机,2空箱转运
// _logger.Info($"1#移栽====没读到,空箱转运");
//workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("1#移栽平台任务"), 2);
//workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false);
}
}
}
#endregion 1#连廊料箱在计量室连廊移栽处理流程wcs读RFID判断是否空箱空箱下plc信号旋转非空箱通知plc上提升机计量室
#region 暂时不使用--色粉存放点空托盘派送至2#接驳位
/////如果色粉存放点可用空库位小于等于2个并且有空托盘在库位判断2#缓存链条线库存容量3低于2个并且无 从3#接驳位到2#接驳位的送料任务或 色粉存放点到2#缓存链条线任务,
/////2.生成色粉存放点到2#缓存链条线任务
//int canUseAmount = sqlSugarClient.Queryable<WcsBaseEquip>().Count(it => it.EquipType == 7 && it.EquipStatus == 0 && string.IsNullOrEmpty(it.ContainerCode));
////判断色粉存放处是否有空托盘
//WcsBaseEquip? startEquip = sqlSugarClient.Queryable<WcsBaseEquip>().InnerJoin<WmsPalletInfo>(
// (wbe, wpi) => wbe.ContainerCode == wpi.PalletInfoCode && wpi.Amount == 0 && wbe.EquipStatus == 0)
// .Where(wbe => wbe.EquipType == 7 && !string.IsNullOrEmpty(wbe.ContainerCode)).First();
//if (canUseAmount < 2 && startEquip != null)
//{
// int cacheLineAmount = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#缓存链条线库存数"));
// bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.EmptyReturnFromTonerTask || it.TaskType == StaticTaskType.TransferMaterialMetrologyRoomBoxTask);
// if (cacheLineAmount < 2 && !hasTask)
// {
// try
// {
// sqlSugarClient.AsTenant().BeginTran();
// WcsTask task = new WcsTask();
// task.TaskType = StaticTaskType.EmptyReturnFromTonerTask;
// task.CurrPointNo = startEquip.AgvPositionCode;
// task.EndPointNo = "2DeliverMetrologyRoomPoint";
// task.TaskStatus = 0;
// task.CreatedTime = DateTime.Now;
// task.CreatedBy = "wcs";
// task.TaskName = "色粉存放点空托盘派送至2#接驳位";
// task.PalletInfoCode = startEquip.ContainerCode;
// int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
// WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
// wcsTaskLog.Id = id;
// startEquip.EquipStatus = 1;
// sqlSugarClient.Updateable(startEquip).ExecuteCommand();
// sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
// sqlSugarClient.AsTenant().CommitTran();
// _logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
// }
// catch (Exception ex)
// {
// sqlSugarClient.AsTenant().RollbackTran();
// _logger.Error($"色粉存放点空托盘派送至2#接驳位任务提交事务异常{ex.Message}");
// }
// }
//}
#endregion 暂时不使用--色粉存放点空托盘派送至2#接驳位
}
catch (Exception ex)
{
_logger.Error($"ListeningWorkShop2TransplantingMachine方法异常:" + ex.StackTrace);
}
finally
{
Thread.Sleep(1000 * 1);
}
}
}
#region 任务流程
/// <summary>
/// 3#车间补四宫格空箱流程
/// </summary>
private async void CreateSupplementBigPalletTask()
{
try
{
//判断agv是否有来这里的任务
bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.SupplyEmptyPalletTask && it.EndPointNo == "3SuppleEmptyPalletPoint");
if (hasTask)
{
//_logger.Info("Agv已有3#车间补四宫格空箱任务");
return;
}
#region 寻找机台空托盘
WmsBaseLocation? startLocation = null;
//todo:优化点:为避免送料时尽可能少的移库任务,因此在这里可以先读取下一个料包要去的机台号,提前调度这里的空箱
string nextGoMachineCode = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#实时机台号")).ToString();
bool hasAny = StaticData.WmsMachineInfos.Any(x => x.MachineCode == nextGoMachineCode && x.WorkshopId == 3);
if (hasAny)
{
int machineId = StaticData.WmsMachineInfos.First(x => x.MachineCode == nextGoMachineCode).MachineId;
if (machineId >= 12 && machineId <= 16)
{
//12 - 16号机台都送往12号机台
machineId = 12;
}
//判断该机台有无空料箱,优先补充
string sql = $"SELECT wbl.* FROM wms_base_location wbl join wms_pallet_info wpi on wbl.container_code = wpi.pallet_info_code where wbl.container_code is not null and wbl.container_code!='' and wbl.location_status=0 and wbl.machineid ={machineId} and wpi.amount=0";
List<WmsBaseLocation> startLocations = sqlSugarClient.Ado.SqlQuery<WmsBaseLocation>(sql).OrderByDescending(x => x.MachineId).ToList();
if (startLocations.Count > 0)
{
startLocation = startLocations[0];
}
}
//1-12机台优先12
if (startLocation == null)
{
string sql = "SELECT wbl.* FROM wms_base_location wbl join wms_pallet_info wpi on wbl.container_code = wpi.pallet_info_code where wbl.container_code is not null and wbl.container_code!='' and wbl.location_status=0 and wbl.machineid<=12 and wpi.amount=0";
List<WmsBaseLocation> emptyLocations = sqlSugarClient.Ado.SqlQuery<WmsBaseLocation>(sql).OrderBy(x => x.MachineId).ToList();
if (emptyLocations.Count == 0)
{
_logger.Info("暂时没有可用空托盘库位");
//todo报警
return;
}
//暂时排序,后期结合小料正在生产的计划提前调度要配送机台的空托盘,或者可以根据实际位置调度最近的空料箱
startLocation = emptyLocations.First();
}
WcsBaseEquip? endEquip = sqlSugarClient.Queryable<WcsBaseEquip>().Where(it => it.EquipNo == "3SuppleEmptyPalletPoint").ToList().First();
#endregion 寻找机台空托盘
sqlSugarClient.AsTenant().BeginTran();
WcsTask task = new WcsTask();
task.TaskType = StaticTaskType.SupplyEmptyPalletTask;
task.TaskName = StaticTaskType.GetDescription(task.TaskType);
task.CurrPointNo = startLocation.AgvPositionCode;
startLocation.LocationStatus = 1;
sqlSugarClient.Updateable(startLocation).ExecuteCommand();
// task.NextPointNo = agvEquip.AgvPositionCode;
task.EndPointNo = endEquip.AgvPositionCode;
task.TaskStatus = 0;
task.CreatedTime = DateTime.Now;
task.CreatedBy = "wcs";
task.PalletInfoCode = startLocation.ContainerCode;
int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
wcsTaskLog.Id = id;
sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
sqlSugarClient.AsTenant().CommitTran();
_logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
_logger.Error($"生成3#车间补四宫格空箱流程异常{ex.Message}");
}
}
/// <summary>
/// 从下料点到1-16机台送料任务 创建移库任务
/// </summary>
private async void CreatDeliverWorkShop3Task(string machineCode, string rfid, int amount)
{
try
{
bool hasMachineCode = StaticData.WmsMachineInfos.Any(x => x.MachineCode == machineCode);
if (!string.IsNullOrEmpty(rfid) && hasMachineCode)
{
//判断该料箱是否已经有送料任务
bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.TransferMaterialBoxTask && it.PalletInfoCode == rfid);
if (hasTask)
{
_logger.Info($"该托盘{rfid}已有下料点到1-16机台送料任务");
return;
}
//判断该托盘是否已经在库里
bool isInLocation = sqlSugarClient.Queryable<WmsBaseLocation>().Any(it => it.ContainerCode == rfid);
if (isInLocation)
{
//todo推送报警
_logger.Info($"该托盘{rfid}已存在库位,请检查托盘号是否重复");
return;
}
List<WmsBaseLocation> AllWmsBaseLocationList = await sqlSugarClient.Queryable<WmsBaseLocation>().Where(x => x.MachineId <= 12).ToListAsync();
WmsBaseLocation? targetLocation;
// 根据机台号寻找终点库位、判断机台的两个库位是否有空位置、没有的话先生成移库任务
targetLocation = GetTargetLocation(machineCode, AllWmsBaseLocationList);
if (targetLocation == null)
{
//todo:推送预警
_logger.Info($"机台:{machineCode}没有可用空库位,无法配送");
return;
}
WcsBaseEquip startEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "3DeliverBigGoodsStartPoint");
WmsPalletInfo wmsPalletInfo = sqlSugarClient.Queryable<WmsPalletInfo>().First(it => it.PalletInfoCode == rfid);
WcsTask task = new WcsTask();
task.TaskType = StaticTaskType.TransferMaterialBoxTask;
task.CurrPointNo = startEquip.AgvPositionCode;
task.EndPointNo = targetLocation.AgvPositionCode;
task.TaskStatus = 0;
task.CreatedTime = DateTime.Now;
task.CreatedBy = "wcs";
task.TaskName = "下料点到1-16机台送料任务";
task.PalletInfoCode = rfid;
task.MachineCode = machineCode;
sqlSugarClient.AsTenant().BeginTran();
try
{
int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
wcsTaskLog.Id = id;
targetLocation.LocationStatus = 1;
sqlSugarClient.Updateable(targetLocation).ExecuteCommand();
sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
#region 修改托盘信息
if (wmsPalletInfo != null)
{
wmsPalletInfo.Amount = amount != 0 ? amount : 1;
wmsPalletInfo.UpdatedTime = DateTime.Now;
sqlSugarClient.Updateable(wmsPalletInfo).ExecuteCommand();
}
#endregion 修改托盘信息
sqlSugarClient.AsTenant().CommitTran();
workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#大料箱agv取料通知"), false);
_logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
_logger.Error($"生成3#车间从下料点到1-16机台送料任务提交事务异常{ex.Message}");
}
}
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
_logger.Error($"生成3#车间从下料点到1-16机台送料任务异常{ex.Message}");
}
}
/// <summary>
/// 从3#接驳位到2#计量室接驳位的送料任务
/// </summary>
private void CreatDeliverWorkShop2Task(string machineCode, string rfid)
{
try
{
bool hasMachineCode = StaticData.WmsMachineInfos.Any(x => x.MachineCode == machineCode);
if (!string.IsNullOrEmpty(rfid) && hasMachineCode)
{
//判断agv是否有来这里的任务
bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.TransferMaterialMetrologyRoomBoxTask && it.PalletInfoCode == rfid);
if (hasTask)
{
_logger.Info($"该托盘{rfid}已有3#接驳位到2#计量室接驳位的送料任务");
return;
}
//起点库位 3#车间小料箱取送货接驳点
WcsBaseEquip startEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "3DeliverSmallGoodsPoint");
//终点库位 2#车间计量室接驳点
WcsBaseEquip endEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "2DeliverMetrologyRoomPoint");
WmsPalletInfo wmsPalletInfo = sqlSugarClient.Queryable<WmsPalletInfo>().First(it => it.PalletInfoCode == rfid);
sqlSugarClient.AsTenant().BeginTran();
WcsTask task = new WcsTask();
task.TaskType = StaticTaskType.TransferMaterialMetrologyRoomBoxTask;
task.CurrPointNo = startEquip.AgvPositionCode;
// task.NextPointNo = agvEquip.AgvPositionCode;
task.EndPointNo = endEquip.AgvPositionCode;
task.TaskStatus = 0;
task.CreatedTime = DateTime.Now;
task.CreatedBy = "wcs";
task.TaskName = "3#接驳位到2#计量室接驳位的送料任务";
task.PalletInfoCode = rfid;
task.MachineCode = machineCode;
int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
wcsTaskLog.Id = id;
sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
#region 修改托盘信息
if (wmsPalletInfo != null)
{
wmsPalletInfo.TonerFlag = 0;
wmsPalletInfo.Amount = 1;
wmsPalletInfo.UpdatedTime = DateTime.Now;
sqlSugarClient.Updateable(wmsPalletInfo).ExecuteCommand();
}
#endregion 修改托盘信息
sqlSugarClient.AsTenant().CommitTran();
workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#小料箱agv取料通知"), false);
_logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
}
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
_logger.Error($"3#接驳位到2#计量室接驳位的送料任务{ex.Message}");
}
}
/// <summary>
/// 3#车间从色粉存放点补充小托盘任务 或者 3#车间从2#接驳位补充小托盘任务
/// .wcs检测到 3#缓存皮带线库存数 (容量2)不满时,且色粉存放处有空托盘时,优先生成色粉存放处补充小料箱任务
///2.如果色粉存放处无空托盘且2#缓存链条线库存数 大于0个并且 2#线体忙碌状态 0时2s后 2#线体忙碌状态 0时wcs把 2#线体忙碌状态 改为1wcs下发缓存链条线 2#出一个空托盘信号plc自己复位 wcs并且生成一个3#补充空托盘任务呼叫agv
/// </summary>
private void CreateSupplySmallPalletToWorkshop3Task()
{
try
{
//判断是否已经有空托盘补充任务
bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(x => x.TaskType == StaticTaskType.SupplySmallPalletTask || x.TaskType == StaticTaskType.SupplySmallPalletFromTonerTask);
if (hasTask)
{
return;
}
//判断色粉存放处是否有空托盘
WcsBaseEquip? emptyPalletEquip = sqlSugarClient.Queryable<WcsBaseEquip>().InnerJoin<WmsPalletInfo>(
(wbe, wpi) => wbe.ContainerCode == wpi.PalletInfoCode && wpi.Amount == 0 && wbe.EquipStatus == 0)
.Where(wbe => wbe.EquipType == 7 && !string.IsNullOrEmpty(wbe.ContainerCode)).OrderBy(wbe => wbe.Id).First();
if (emptyPalletEquip != null) //从色粉存放处生成补充空托盘任务
{
WcsTask task = new WcsTask();
task.TaskType = StaticTaskType.SupplySmallPalletFromTonerTask;
task.CurrPointNo = emptyPalletEquip.AgvPositionCode;
task.EndPointNo = "3DeliverSmallGoodsPoint";
task.TaskStatus = 0;
task.CreatedTime = DateTime.Now;
task.CreatedBy = "wcs";
task.TaskName = "3#车间从色粉存放点补充小托盘任务";
task.PalletInfoCode = emptyPalletEquip.ContainerCode;
sqlSugarClient.AsTenant().BeginTran();
try
{
int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
wcsTaskLog.Id = id;
sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
//锁住开始色粉库位
emptyPalletEquip.EquipStatus = 1;
sqlSugarClient.Updateable(emptyPalletEquip).ExecuteCommand();
sqlSugarClient.AsTenant().CommitTran();
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
_logger.Error($"3#车间从色粉存放点补充小托盘任务生成提交事务异常{ex.Message}");
}
_logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
return;
}
else //从2#缓存链条线生成补充空托盘任务
{
if (workShop2Plc == null || !workShop2Plc.IsConnected)
{
_logger.Plc(DateTime.Now + "2#PLC未连接,请检查网络!");
return;
}
int work2EmptyAmount = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#缓存链条线库存数"));
int work2BusyFlag = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"));
if (work2EmptyAmount > 0 && work2BusyFlag == 0)
{
//防止抢占其他任务节奏
Thread.Sleep(1000);
work2BusyFlag = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"));
work2EmptyAmount = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#缓存链条线库存数"));
if (work2EmptyAmount > 0 && work2BusyFlag == 0)
{
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1);
WcsTask task = new WcsTask();
task.TaskType = StaticTaskType.SupplySmallPalletTask;
task.CurrPointNo = "2DeliverMetrologyRoomPoint";
task.EndPointNo = "3DeliverSmallGoodsPoint";
task.TaskStatus = 0;
task.CreatedTime = DateTime.Now;
task.CreatedBy = "wcs";
task.TaskName = " 3#车间从2#接驳位补充小托盘任务";
sqlSugarClient.AsTenant().BeginTran();
try
{
int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
wcsTaskLog.Id = id;
sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
sqlSugarClient.AsTenant().CommitTran();
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
_logger.Error($"3#车间从2#接驳位补充小托盘任务生成提交事务异常{ex.Message}");
workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 0);
}
_logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
return;
}
}
}
}
catch (Exception ex)
{
_logger.Error($"CreateSupplySmallPalletToWorkshop3Task异常{ex.Message}");
}
}
/// <summary>
/// 从2#接驳位到3#接驳位补小空箱任务 :3#车间补小空箱流程
/// </summary>
private void CreateSupplementSmallPalletTask()
{
try
{
//判断是否有该任务
bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.SupplySmallPalletTask && it.TaskStatus < 5);
if (hasTask)
{
_logger.Info("Agv已有2#接驳位到3#接驳位补小空箱任务");
return;
}
//寻找可用agv
//WcsBaseEquip? agvEquip = GetCanUseAgv();
//if (agvEquip == null)
//{
// _logger.Info("暂时没有可用agv");
// return;
//}
//起点库位 3#车间小料箱取送货接驳点
WcsBaseEquip startEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "2DeliverMetrologyRoomPoint");
//终点库位 2#车间计量室接驳点
WcsBaseEquip endEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "3DeliverSmallGoodsPoint");
sqlSugarClient.AsTenant().BeginTran();
WcsTask task = new WcsTask();
task.TaskType = StaticTaskType.SupplySmallPalletTask;
task.CurrPointNo = startEquip.AgvPositionCode;
// task.NextPointNo = agvEquip.AgvPositionCode;
task.EndPointNo = endEquip.AgvPositionCode;
task.TaskStatus = 0;
task.CreatedTime = DateTime.Now;
task.CreatedBy = "wcs";
task.TaskName = "2#接驳位到3#接驳位补小空箱任务";
int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
wcsTaskLog.Id = id;
startEquip.EquipStatus = 1;
endEquip.EquipStatus = 1;
sqlSugarClient.Updateable(startEquip).ExecuteCommand();
sqlSugarClient.Updateable(endEquip).ExecuteCommand();
sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
sqlSugarClient.AsTenant().CommitTran();
_logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
_logger.Error($"2#接驳位到3#接驳位补小空箱任务异常{ex.Message}");
}
}
/// <summary>
/// 2#色粉派送至色粉存放点任务
/// </summary>
private bool CreateDeliverTonerTask(string rfid)
{
bool createResult = false;
try
{
//判断是否有该任务
bool hasTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.DeliverTonerTask && it.PalletInfoCode == rfid);
if (hasTask)
{
_logger.Info($"Agv已有托盘:{rfid}2#色粉派送至色粉存放点任务");
return true;
}
//起点库位 2#计量室车间小料箱取送货接驳点
WcsBaseEquip startEquip = sqlSugarClient.Queryable<WcsBaseEquip>().First(it => it.EquipNo == "2DeliverMetrologyRoomPoint");
//终点库位 2#返程色粉人工拿取点
WcsBaseEquip? endEquip = sqlSugarClient.Queryable<WcsBaseEquip>().Where(it => it.EquipStatus == 0 && it.EquipType == 7 && string.IsNullOrEmpty(it.ContainerCode)).OrderBy(x => x.Id).First();
if (endEquip == null)
{
//todo推送报警
_logger.Error("暂时没有可用色粉存放点库位");
return false;
}
sqlSugarClient.AsTenant().BeginTran();
try
{
WcsTask task = new WcsTask();
task.TaskType = StaticTaskType.DeliverTonerTask;
task.CurrPointNo = startEquip.AgvPositionCode;
task.EndPointNo = endEquip.AgvPositionCode;
task.TaskStatus = 0;
task.CreatedTime = DateTime.Now;
task.CreatedBy = "wcs";
task.TaskName = "2#色粉派送至色粉存放点任务";
task.PalletInfoCode = rfid;
int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
wcsTaskLog.Id = id;
startEquip.EquipStatus = 1;
endEquip.EquipStatus = 1;
sqlSugarClient.Updateable(startEquip).ExecuteCommand();
sqlSugarClient.Updateable(endEquip).ExecuteCommand();
sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
sqlSugarClient.AsTenant().CommitTran();
_logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
createResult = true;
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
_logger.Error($"2#色粉派送至色粉存放点任务提交事务异常{ex.Message}");
}
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
_logger.Error($"生成2#色粉派送至色粉存放点并空箱送回2#接驳位任务异常{ex.Message}");
}
return createResult;
}
#endregion 任务流程
#region 辅助方法
/// <summary>
/// 判断是否还有色粉在连廊输送线或者没在色粉库位
/// </summary>
/// <returns></returns>
private bool HasAnyTonerInLine()
{
try
{
// 色粉料箱
List<WmsPalletInfo> tonerPallets = sqlSugarClient.Queryable<WmsPalletInfo>().Where(x => x.TonerFlag == 1 && x.Amount > 0).ToList();
// 色粉库位的料箱码
List<WcsBaseEquip> equipPalletInfos = sqlSugarClient.Queryable<WcsBaseEquip>().Where(x => x.EquipType == 7 && !string.IsNullOrEmpty(x.ContainerCode)).ToList();
// 获取 tonerPallets 中的所有 PalletInfoCode
var tonerPalletCodes = tonerPallets.Select(x => x.PalletInfoCode).ToList();
// 获取 equipPalletInfos 中的所有 ContainerCode
var equipPalletCodes = equipPalletInfos.Select(x => x.ContainerCode).ToList();
// 检查是否有 tonerPalletCodes 不在 equipPalletCodes 中
bool result = tonerPalletCodes.Any(code => !equipPalletCodes.Contains(code));
return result;
}
catch (Exception ex)
{
return false;
}
}
///// <summary>
///// 从12号机台获取空托盘返回库位信息
///// </summary>
///// <param name="machineId"></param>
///// <returns></returns>
//private async Task<WmsBaseLocation?> GetEmptyContainerByMachine12Async(string rfidKey)
//{
// try
// {
// bool flag1 = await ReadSignalByRfidKeyAsync(rfidKey);
// if (flag1)
// {
// string code = await ReadEpcStrByRfidKeyAsync(rfidKey);
// if (!string.IsNullOrEmpty(code))
// {
// // 通过该料箱是否绑定有物料来判断是否为空托盘
// bool hasMaterial = await sqlSugarClient.Queryable<WmsPalletInfo>().AnyAsync(it => it.PalletInfoCode == code);
// if (!hasMaterial)
// {
// WmsBaseLocation? location = await sqlSugarClient.Queryable<WmsBaseLocation>().FirstAsync(it => it.EquipKey == rfidKey);
// return location;
// }
// }
// }
// //_logger.Error($"12号机台库位{rfidKey}无空托盘");
// return null;
// }
// catch (Exception ex)
// {
// return null;
// }
//}
/// <summary>
/// 根据机台号寻找终点库位、判断机台的两个库位是否有空位置、没有的话先生成移库任务
/// machineId为该料箱目的地机台号
/// </summary>
/// <param name="machineId"></param>
/// <returns></returns>
private WmsBaseLocation? GetTargetLocation(string machineCode, List<WmsBaseLocation> AllWmsBaseLocationList)
{
try
{
bool hasMoveTask = sqlSugarClient.Queryable<WcsTask>().Any(it => it.TaskType == StaticTaskType.MoveLocationTask && it.TaskStatus < 3);
if (hasMoveTask)
{
_logger.Info($"目标库位无空位置,正在执行移库任务,暂时不生成送料任务,请等待....");
return null;
}
WmsBaseLocation? targetLocation;
//移库起点终点
WmsBaseLocation? moveStartLocation;
WmsBaseLocation? moveEndLocation;
int machineId = StaticData.WmsMachineInfos.First(x => x.MachineCode == machineCode).MachineId;
if (machineId >= 12 && machineId <= 16)
{
//12 - 16号机台都送往12号机台
machineId = 12;
}
//12号机台读RFID判断
//if (machineId >= 12)
//{
// //光电信号
// bool flag1 = await ReadSignalByRfidKeyAsync("12-1");
// bool flag2 = await ReadSignalByRfidKeyAsync("12-2");
// if (!flag1)
// { //12-1有空位
// targetLocation = AllWmsBaseLocationList.First(it => it.EquipKey == "12-1" && it.LocationStatus == 1);
// return targetLocation;
// }
// //if (!flag2) //12-2有空位
// {
// //默认全送到12-2库位如果有料箱agv送过去停着,人工取走库位上的箱子
// targetLocation = AllWmsBaseLocationList.First(it => it.EquipKey == "12-2" && it.LocationStatus == 1);
// return targetLocation;
// }
// // 后期考虑优化12号机台暂不考虑移库因为库位距离其他机台比较远
//}
//else if (machineId >= 1 && machineId <= 11)
{
targetLocation = AllWmsBaseLocationList.FirstOrDefault(it => it.MachineId == machineId && it.LocationStatus == 0 && string.IsNullOrEmpty(it.ContainerCode));
if (targetLocation != null) return targetLocation;
//目标机台两个都有料箱,判断是否有机台里面的料箱是空的,有空箱可生成移库任务,否则等待
String sql = $"SELECT wbl.* FROM wms_base_location wbl join wms_pallet_info wpi on wbl.container_code = wpi.pallet_info_code where wbl.container_code is not null and wbl.container_code!='' and wbl.location_status=0 and wbl.machineid={machineId} and wpi.amount=0";
moveStartLocation = sqlSugarClient.SqlQueryable<WmsBaseLocation>(sql).First();
if (moveStartLocation == null)
{
return null;
// todo推送预警 // 目标机台没有空库位,没有可移库的空箱,等待
}
//需要生成从该库位到其他机台的移库任务,找寻移库终点库位
moveEndLocation = AllWmsBaseLocationList.FirstOrDefault(x => x.LocationId != moveStartLocation.LocationId && x.LocationStatus == 0 && string.IsNullOrEmpty(x.ContainerCode));
if (moveEndLocation == null)
{
// 没有可以移库的库位,请等待
return null;
// todo:推送预警
}
else //生成移库任务
{
//二次确认寻找可用agv避免在这期间agv被其他线程占用
try
{
//WcsBaseEquip? agvEquip = GetCanUseAgv();
//if (agvEquip == null)
//{
// _logger.Info("暂时没有可用agv");
// return null;
//}
WcsTask task = new WcsTask();
task.TaskType = StaticTaskType.MoveLocationTask;
task.CurrPointNo = moveStartLocation.AgvPositionCode;
task.EndPointNo = moveEndLocation.AgvPositionCode;
task.TaskStatus = 0;
task.CreatedTime = DateTime.Now;
task.CreatedBy = "wcs";
task.TaskName = "1-12机台之间空托盘移库任务";
task.PalletInfoCode = moveStartLocation.ContainerCode;
sqlSugarClient.AsTenant().BeginTran();
try
{
int id = sqlSugarClient.Insertable(task).ExecuteReturnIdentity();
WcsTaskLog wcsTaskLog = CoreMapper.Map<WcsTaskLog>(task);
wcsTaskLog.Id = id;
//锁定库位
moveStartLocation.LocationStatus = 1;
moveEndLocation.LocationStatus = 1;
sqlSugarClient.Updateable(moveStartLocation).ExecuteCommand();
sqlSugarClient.Updateable(moveEndLocation).ExecuteCommand();
sqlSugarClient.Insertable(wcsTaskLog).ExecuteCommand();
sqlSugarClient.AsTenant().CommitTran();
_logger.Agv($"生成{task.TaskName},起点:{task.CurrPointNo},终点:{task.EndPointNo}");
}
catch (Exception ex)
{
_logger.Error($"生成移库任务提交事务异常:{ex.Message}");
sqlSugarClient.AsTenant().RollbackTran();
}
}
catch (Exception ex)
{
_logger.Error($"生成移库任务异常:{ex.Message}");
return null;
}
}
}
return null;
}
catch (Exception ex)
{
_logger.Error($"GetTargetLocationAsync方法异常:{ex.StackTrace}");
return null;
}
}
#endregion 辅助方法
#region 测试方法
private async void test()
{
// 读RFID条码
// string code = await ReadEpcStrByRfidKeyAsync("1");
//读光电信号
//bool flag = await ReadSignalByRfidKeyAsync("1");
//bool flag1 = await ReadSignalByRfidKeyAsync("1");
//testTransitional();
//testTransitionalRollback();
}
/// <summary>
/// 测多线程事务影响程度
/// </summary>
private void testTransitional()
{
Task.Run(() =>
{
try
{
sqlSugarClient.AsTenant().BeginTran();
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"线程1{i}");
//WmsStock stock = new WmsStock();
// stock.WorkshopId = i;
// sqlSugarClient.Insertable(stock).ExecuteCommand();
Thread.Sleep(1000);
}
sqlSugarClient.AsTenant().CommitTran();
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
}
});
Task.Run(() =>
{
try
{
sqlSugarClient.AsTenant().BeginTran();
for (int i = 20; i < 30; i++)
{
//Console.WriteLine($"线程2{i}");
//WmsStock stock = new WmsStock();
//stock.WorkshopId = i;
//sqlSugarClient.Insertable(stock).ExecuteCommand();
//Thread.Sleep(1000);
//if (i == 25)
//{
// Console.WriteLine($"事务回滚");
// throw new Exception("事务回滚");
//}
}
sqlSugarClient.AsTenant().CommitTran();
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
}
});
}
/// <summary>
/// 测事务未开启但回滚
/// </summary>
private void testTransitionalRollback()
{
try
{
throw new Exception("事务回滚");
sqlSugarClient.AsTenant().BeginTran();
for (int i = 0; i < 10; i++)
{
//Console.WriteLine($"线程1{i}");
//WmsStock stock = new WmsStock();
//stock.WorkshopId = i;
//sqlSugarClient.Insertable(stock).ExecuteCommand();
//Thread.Sleep(1000);
}
sqlSugarClient.AsTenant().CommitTran();
}
catch (Exception ex)
{
sqlSugarClient.AsTenant().RollbackTran();
}
}
#endregion 测试方法
}
}