From 0fdc58a03ef1d41f81b790caaae5b3d7d20e6e2d Mon Sep 17 00:00:00 2001 From: zhangxy Date: Wed, 2 Jul 2025 11:02:58 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=85=A5=E5=BA=93=E7=A7=BB=E5=BA=93=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=202.=E5=B0=8F=E6=96=99=E7=AE=B1=E8=BF=9B=E6=8E=A5?= =?UTF-8?q?=E9=A9=B3=E4=BD=8D=E4=BF=A1=E5=8F=B7=E5=8F=98=E6=9B=B4=203.?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E9=94=99=E8=AF=AF=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SlnMesnac.Business/base/BaseBusiness.cs | 47 ++++++++ SlnMesnac.WCS/MainCentralControl.cs | 48 +++++++- SlnMesnac.WCS/SlnMesnac.WCS.csproj | 2 +- SlnMesnac.WCS/WCS/BearAgv.cs | 79 +++++++++++-- SlnMesnac.WCS/WCS/CreateTaskByRecord.cs | 107 +++++++++++++----- .../ViewModel/TonerBoxingViewModel.cs | 4 + 6 files changed, 248 insertions(+), 39 deletions(-) diff --git a/SlnMesnac.Business/base/BaseBusiness.cs b/SlnMesnac.Business/base/BaseBusiness.cs index 16a648e..b9f4f3e 100644 --- a/SlnMesnac.Business/base/BaseBusiness.cs +++ b/SlnMesnac.Business/base/BaseBusiness.cs @@ -157,6 +157,53 @@ namespace SlnMesnac.Business.@base } } + /// + /// 连廊使用---根据RFID Key读取RFID信息,并返回读取类型:type-0读取失败; type-1读取ok;type-2串读 + /// + /// + /// + /// + public string ReadEpcStrByRfidKeyAndReturnType(string rfidKey, out int type) + { + try + { + type = 0; + string epcStr = string.Empty; + var rfidEquip = GetRfidByKey(rfidKey); + if (rfidEquip == null || rfidEquip.GetOnlineStatus() == false) + { + return ""; + } + + List tagInfoList = rfidEquip.GetRFID(); + if (tagInfoList == null || tagInfoList.Count == 0) return ""; + //读取到不同RFID的个数 + int codeDistinctCount = tagInfoList.Select(x => x.EPCstring).Distinct().Count(); + if (codeDistinctCount == 0) + { + type = 0; + } + else if (codeDistinctCount == 1) + { + type = 1; + } + else if (codeDistinctCount > 1) + { + //串读 + type = 2; + } + epcStr = tagInfoList.OrderByDescending(x => x.Count).First().EPCstring; + epcStr = epcStr.Replace(" ", "").Replace("\r\n", "").Replace("\0", "").Replace("\n", ""); + return epcStr; + } + catch (Exception ex) + { + // throw new InvalidOperationException($"根据RFID Key读取RFID信息异常:{ex.Message}"); + type = 0; + return ""; + } + } + /// /// 使用---根据RFID Key读取RFID信息 /// diff --git a/SlnMesnac.WCS/MainCentralControl.cs b/SlnMesnac.WCS/MainCentralControl.cs index f7d3a8f..f718e23 100644 --- a/SlnMesnac.WCS/MainCentralControl.cs +++ b/SlnMesnac.WCS/MainCentralControl.cs @@ -176,6 +176,9 @@ namespace SlnMesnac.WCS //色粉库位有未取走色粉声光报警 ListeningTonerLocation(); + + //大托盘库位有未取走色粉声光报警 + ListeningBigPlateLocation(); } catch (Exception ex) { @@ -243,6 +246,46 @@ namespace SlnMesnac.WCS } } + private void ListeningBigPlateLocation() + { + try + { + PlcAbsractFactory? workShop3Plc = plcList.FirstOrDefault(x => x.ConfigKey == "workShop3Plc"); + bool hasRecords = SqlSugarClient.Ado.Context.CopyNew().Queryable() + .InnerJoin((wbe, wpi) => wbe.ContainerCode == wpi.PalletInfoCode + && !string.IsNullOrEmpty(wbe.ContainerCode) + && wpi.Amount > 0 && wbe.MachineId > 0 && wbe.MachineId < 12).Any(); + bool hasRecords12 = SqlSugarClient.Ado.Context.CopyNew().Queryable() + .InnerJoin((wbe, wpi) => wbe.ContainerCode == wpi.PalletInfoCode + && !string.IsNullOrEmpty(wbe.ContainerCode) + && wpi.Amount > 0 && wbe.MachineId == 12).Any(); + if (workShop3Plc != null && workShop3Plc.IsConnected) + { + if (hasRecords) + { + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#机台校验失败提示"), true); + } + else + { + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#机台校验失败提示"), false); + } + + if (hasRecords12) + { + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#12号机台未校验提示"), true); + } + else + { + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#12号机台未校验提示"), false); + } + } + } + catch(Exception ex) + { + _logger.Error($"监听大托盘库位异常:{ex.Message}"); + } + } + /// /// agv状态实时刷新 /// @@ -317,8 +360,9 @@ namespace SlnMesnac.WCS else { _logger.Agv($"当前没有空闲小车离取料点最近,等线体空闲后进行取料"); - var flag = bearAgv.workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#线体忙碌状态")); - if (flag == 0) + bearAgv.workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存请求"),true); + bool flag = bearAgv.workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存允许")); + if (flag == true) { //线体空闲了,可以锁定进行取料 _logger.Agv($"线体空闲了,可以锁定使用远处小车进行取料"); diff --git a/SlnMesnac.WCS/SlnMesnac.WCS.csproj b/SlnMesnac.WCS/SlnMesnac.WCS.csproj index 6fd8526..e6c365a 100644 --- a/SlnMesnac.WCS/SlnMesnac.WCS.csproj +++ b/SlnMesnac.WCS/SlnMesnac.WCS.csproj @@ -1,4 +1,4 @@ - + Exe diff --git a/SlnMesnac.WCS/WCS/BearAgv.cs b/SlnMesnac.WCS/WCS/BearAgv.cs index 2707ff6..fdeef4f 100644 --- a/SlnMesnac.WCS/WCS/BearAgv.cs +++ b/SlnMesnac.WCS/WCS/BearAgv.cs @@ -244,17 +244,18 @@ namespace SlnMesnac.WCS.WCS _logger.Agv($"读到RFID:{readEpc}"); //todo:12号机台装上RFID以后删除-------------------------------- - if (endLocation.MachineId == 12) + /*if (endLocation.MachineId == 12) { readEpc = task.PalletInfoCode; - } + }*/ //---------------------------------- if (string.IsNullOrEmpty(readEpc) || readEpc != task.PalletInfoCode) { - // workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#机台校验失败提示"), true); + //workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#机台校验失败提示"), true); _logger.Error("入库校验RFID失败,当前库位RFID:" + readEpc + ",任务RFID:" + task.PalletInfoCode); return; } + //workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#机台校验失败提示"), false); endLocation.LocationStatus = 0; endLocation.ContainerCode = task.PalletInfoCode; sqlSugarClient.AsTenant().BeginTran(); @@ -666,7 +667,32 @@ namespace SlnMesnac.WCS.WCS _logger.Plc(DateTime.Now + "2#PLC未连接,请检查网络!"); return; } - int busyFlag = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#线体忙碌状态")); + + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存请求"),true); + bool busyFlag = workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存允许")); + + if (busyFlag == false) { + return; + } + if(busyFlag == true) + { + Thread.Sleep(3000); + busyFlag = workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存允许")); + if (busyFlag == true) + { + bool result = await ContinueTaskHandle(task); + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存请求"), false); + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存允许"), false); + if (!result) + { + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存请求"), false); + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存允许"), false); + } + } + } + + + /*int busyFlag = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#线体忙碌状态")); if (busyFlag == 1) { return; @@ -685,7 +711,7 @@ namespace SlnMesnac.WCS.WCS workShop3Plc.writeInt16ByAddress(StaticData.GetPlcAddress("3#线体忙碌状态"), 0); } } - } + }*/ } else if (task.TaskStatus == 8) //agv到达终点接驳位,等待料箱离开接驳位 { @@ -705,7 +731,7 @@ namespace SlnMesnac.WCS.WCS bool result = await ContinueTaskHandle(task); if (result) { - //从下料点直接取,清空RFID及机台号,条码等信息--ok + //从下料点直接取,清空RFID及机台号,条码等信息,复位请求信号--ok workShop3Plc.writeInt16ByAddress(StaticData.GetPlcAddress("3#接驳位到位信号"), 0); workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#出接驳位信号"), false); workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#小料箱agv已取走反馈"), true); @@ -778,7 +804,44 @@ namespace SlnMesnac.WCS.WCS _logger.Plc(DateTime.Now + "2#PLC未连接,请检查网络!"); return; } - int busyFlag = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#线体忙碌状态")); + + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存请求"), true); + bool busyFlag = workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存允许")); + + if (busyFlag == false) + { + return; + } + if (busyFlag == true) + { + Thread.Sleep(1000); + busyFlag = workShop3Plc.readBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存允许")); + if (busyFlag == true) + { + bool result = await ContinueTaskHandle(task); + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存请求"), false); + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存允许"), false); + + if (result) + { + //解锁起始色粉库位 + WcsBaseEquip? baseEquip = sqlSugarClient.Queryable().First(t => t.AgvPositionCode == task.CurrPointNo); + if (baseEquip != null) + { + baseEquip.EquipStatus = 0; + baseEquip.ContainerCode = null; + sqlSugarClient.Updateable(baseEquip).ExecuteCommand(); + } + } + else //下发任务继续失败 + { + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存请求"), false); + workShop3Plc.writeBoolByAddress(StaticData.GetPlcAddress("3#空箱进缓存允许"), false); + } + } + } + + /*int busyFlag = workShop3Plc.readInt16ByAddress(StaticData.GetPlcAddress("3#线体忙碌状态")); if (busyFlag == 1) { return; @@ -808,7 +871,7 @@ namespace SlnMesnac.WCS.WCS workShop3Plc.writeInt16ByAddress(StaticData.GetPlcAddress("3#线体忙碌状态"), 0); } } - } + }*/ } else if (task.TaskStatus == 6) //agv到达终点接驳位,等待料箱离开接驳位 { diff --git a/SlnMesnac.WCS/WCS/CreateTaskByRecord.cs b/SlnMesnac.WCS/WCS/CreateTaskByRecord.cs index c8753ed..2877488 100644 --- a/SlnMesnac.WCS/WCS/CreateTaskByRecord.cs +++ b/SlnMesnac.WCS/WCS/CreateTaskByRecord.cs @@ -160,12 +160,13 @@ namespace SlnMesnac.WCS.WCS bool anyTonerPallet = HasAnyTonerInLine(); if (returnJudgeSignal && busy2Flag == 0 && (Amount < 3 || anyTonerPallet)) { - string rfid = ReadEpcStrByRfidKey("2#Transplant"); - if (string.IsNullOrEmpty(rfid)) + int type = 0; + string rfid = ReadEpcStrByRfidKeyAndReturnType("2#Transplant", out type); + if (type == 0 || type == 2) {//二次读取 - rfid = ReadEpcStrByRfidKey("2#Transplant"); + rfid = ReadEpcStrByRfidKeyAndReturnType("2#Transplant", out type); } - if (!string.IsNullOrEmpty(rfid)) + if (type == 1) //读到1个标签 { int hoistFloor = workShop2Plc.readInt16ByAddress(StaticData.GetPlcAddress("2#提升机当前层")); if (Amount < 3 && hoistFloor == 1) //如果提升机正好在1楼 @@ -186,7 +187,9 @@ namespace SlnMesnac.WCS.WCS { //注意:plc上旋转移栽必须锁住线体状态才可以,2#接驳位如果有满料,那么通知plc信号,plc在连廊不再把箱体放到旋转移栽上 //调度系统结合上面缓存链条线库存,如果上面低于3,那么空箱可以上来缓存线 + workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#缓存空箱线体状态"), 1); workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1); + workShop2Plc.writeInt16ByAddress("DB100.DBW40", 1); bool createResult = CreateDeliverTonerTask(rfid); if (createResult) { @@ -232,14 +235,25 @@ namespace SlnMesnac.WCS.WCS } } } - else + else if (type == 0) //读到0个标签 { + //RFID 未读到报警(2#移栽平台处) + workShop2Plc.writeBoolByAddress("DB100.DBX42.1", true); + _logger.Error("RFID 未读取报警(2#移栽平台处)"); + workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false); //没读到2空箱转运 // _logger.Info($"1#移栽====没读到,空箱转运"); //workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#线体忙碌状态"), 1); //workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("2#移栽平台任务"), 2); //workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false); } + else if (type == 2) //串读 + { + //RFID 串读报警(2#移栽平台处) + workShop2Plc.writeBoolByAddress("DB100.DBX42.0", true); + _logger.Error("RFID 串读报警(2#移栽平台处)"); + workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运回RFID读取"), false); + } } } @@ -254,13 +268,13 @@ namespace SlnMesnac.WCS.WCS if (returnJudgeSignal && busy1Flag == 0) { //test - - string rfid = ReadEpcStrByRfidKey("1#MetrologyRoom"); + int type = 0; + string rfid = ReadEpcStrByRfidKeyAndReturnType("1#MetrologyRoom", out type); if (string.IsNullOrEmpty(rfid)) { - rfid = ReadEpcStrByRfidKey("1#MetrologyRoom"); + rfid = ReadEpcStrByRfidKeyAndReturnType("1#MetrologyRoom", out type); } - if (!string.IsNullOrEmpty(rfid)) + if (type == 1) { // 判断有无料, 如果没有料,wcs下发空箱旋转信号,有料,通知plc上提升机计量室 WmsPalletInfo? wmsPalletInfo = sqlSugarClient.Queryable().First(it => it.PalletInfoCode == rfid); @@ -286,14 +300,26 @@ namespace SlnMesnac.WCS.WCS workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false); } } - else + else if (type == 0) { + //RFID 未读到报警(1#移栽平台处) + workShop2Plc.writeBoolByAddress("DB100.DBX42.3", true); + _logger.Error("RFID 未读取报警(1#移栽平台处)"); + workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false); + //没读到,当成空箱子旋转一圈 //1#移栽平台任务(1上提升机,2空箱转运) // _logger.Info($"1#移栽====没读到,空箱转运"); //workShop2Plc.writeInt16ByAddress(StaticData.GetPlcAddress("1#移栽平台任务"), 2); //workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false); } + else if (type == 2)//串读 + { + //RFID 串读报警(1#移栽平台处) + workShop2Plc.writeBoolByAddress("DB100.DBX42.2", true); + _logger.Error("RFID 串读报警(1#移栽平台处)"); + workShop2Plc.writeBoolByAddress(StaticData.GetPlcAddress("2#料箱运进计量室RFID读取"), false); + } } } @@ -377,24 +403,24 @@ namespace SlnMesnac.WCS.WCS 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 startLocations = sqlSugarClient.Ado.SqlQuery(sql).OrderByDescending(x => x.MachineId).ToList(); - if (startLocations.Count > 0) - { - startLocation = startLocations[0]; - } - } + //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 startLocations = sqlSugarClient.Ado.SqlQuery(sql).OrderByDescending(x => x.MachineId).ToList(); + // if (startLocations.Count > 0) + // { + // startLocation = startLocations[0]; + // } + //} //1-12机台,优先12 if (startLocation == null) { @@ -480,6 +506,18 @@ namespace SlnMesnac.WCS.WCS _logger.Info($"机台:{machineCode}没有可用空库位,无法配送"); return; } + + #region 二次核查确认目标库位是否有托盘 + + bool hasPallet = sqlSugarClient.Queryable().Any(x => x.LocationCode == targetLocation.LocationCode && string.IsNullOrEmpty(x.ContainerCode)); + if (!hasPallet) + { + _logger.Info($"库位:{targetLocation.LocationCode}有托盘暂不处理"); + return; + } + + #endregion 二次核查确认目标库位是否有托盘 + WcsBaseEquip startEquip = sqlSugarClient.Queryable().First(it => it.EquipNo == "3DeliverBigGoodsStartPoint"); WmsPalletInfo wmsPalletInfo = sqlSugarClient.Queryable().First(it => it.PalletInfoCode == rfid); WcsTask task = new WcsTask(); @@ -948,6 +986,19 @@ namespace SlnMesnac.WCS.WCS return null; // todo推送预警 // 目标机台没有空库位,没有可移库的空箱,等待 } + + #region 如果有补空箱任务,并且起点是要送料机台的库位,就不再生成移库任务 + + //目标机台的两个库位编号 + List locationCodes = AllWmsBaseLocationList.Where(x => x.MachineId == machineId).Select(x => x.LocationCode).ToList(); + bool hasTask = sqlSugarClient.Queryable().Any(x => x.TaskType == StaticTaskType.SupplyEmptyPalletTask && locationCodes.Contains(x.CurrPointNo)); + if (hasTask) + { + return null; + } + + #endregion 如果有补空箱任务,并且起点是要送料机台的库位,就不再生成移库任务 + //需要生成从该库位到其他机台的移库任务,找寻移库终点库位 moveEndLocation = AllWmsBaseLocationList.FirstOrDefault(x => x.LocationId != moveStartLocation.LocationId && x.LocationStatus == 0 && string.IsNullOrEmpty(x.ContainerCode)); if (moveEndLocation == null) diff --git a/SlnMesnac.WPF/ViewModel/TonerBoxingViewModel.cs b/SlnMesnac.WPF/ViewModel/TonerBoxingViewModel.cs index 4ea6eea..f3f2e16 100644 --- a/SlnMesnac.WPF/ViewModel/TonerBoxingViewModel.cs +++ b/SlnMesnac.WPF/ViewModel/TonerBoxingViewModel.cs @@ -2,6 +2,7 @@ using GalaSoft.MvvmLight; using GalaSoft.MvvmLight.Command; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using SlnMesnac.Model.domain; using SlnMesnac.WPF.Page; using SqlSugar; @@ -17,11 +18,13 @@ namespace SlnMesnac.WPF.ViewModel public partial class TonerBoxingViewModel : ViewModelBase { private ISqlSugarClient? sqlSugarClient; + private readonly ILogger _logger; public TonerBoxingViewModel() { sqlSugarClient = App.ServiceProvider.GetService(); SelectWindow.RefreshMsgEvent += RefreshMessage; + _logger = App.ServiceProvider.GetService>()!; } #region 界面属性定义 @@ -94,6 +97,7 @@ namespace SlnMesnac.WPF.ViewModel string flag = wmsPalletInfo.TonerFlag == 1 ? "是" : "否"; Msg += $"库存:{wmsPalletInfo.Amount},是否携带色粉:{flag}"; } + _logger.LogInformation($"料箱{code}人工操作记录:" + Msg); } //[RelayCommand]