diff --git a/Tools/LogTool/WcsLog.cs b/Tools/LogTool/WcsLog.cs index ba5cd73..2286ebc 100644 --- a/Tools/LogTool/WcsLog.cs +++ b/Tools/LogTool/WcsLog.cs @@ -27,20 +27,20 @@ public class WcsLog lock (writeLog) { string LogAddress = AppDomain.CurrentDomain.BaseDirectory + "Log\\" + LogName; + DirectoryInfo di = new(LogAddress); + if (di.Exists == false) { di.Create(); } StringBuilder logBuilder = new(); if (addTime) { logBuilder.AppendLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss:fff}]"); } logBuilder.AppendLine($"{strLog}"); - DirectoryInfo di = new(LogAddress); - if (di.Exists == false) { di.Create(); } string logFileName = LogAddress + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + ".log"; using FileStream fs = new(logFileName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite); using StreamWriter sw = new(fs); try { - sw.WriteLine(logBuilder); + sw.Write(logBuilder); } catch (Exception ex) { diff --git a/WcsMain/ApiServe/Controllers/Dto/WcsDto/Location/GetLocationWithPageRequest.cs b/WcsMain/ApiServe/Controllers/Dto/WcsDto/Location/GetLocationWithPageRequest.cs index 4b40dd0..8a39603 100644 --- a/WcsMain/ApiServe/Controllers/Dto/WcsDto/Location/GetLocationWithPageRequest.cs +++ b/WcsMain/ApiServe/Controllers/Dto/WcsDto/Location/GetLocationWithPageRequest.cs @@ -15,10 +15,16 @@ public class GetLocationWithPageRequest public string? SearchStr { get; set; } /// - /// 任务类型 + /// 点位状态 /// [JsonPropertyName("locationStatus")] - public List? LocationStatus { get; set; } + public List? LocationStatus { get; set; } + + /// + /// 点位类型 + /// + [JsonPropertyName("locationType")] + public List? LocationType { get; set; } /// /// 分页信息 diff --git a/WcsMain/ApiServe/Controllers/Dto/WcsDto/Stacker/GetStackerStatusResponse.cs b/WcsMain/ApiServe/Controllers/Dto/WcsDto/Stacker/GetStackerStatusResponse.cs new file mode 100644 index 0000000..2e36547 --- /dev/null +++ b/WcsMain/ApiServe/Controllers/Dto/WcsDto/Stacker/GetStackerStatusResponse.cs @@ -0,0 +1,101 @@ +using System.Text.Json.Serialization; + +namespace WcsMain.ApiServe.Controllers.Dto.WcsDto.Stacker; + +/// +/// 查询堆垛机状态的返回实体 +/// +public class GetStackerStatusResponse +{ + + /// + /// 设备编号 + /// + [JsonPropertyName("stackerId")] + public int? StackerId { get; set; } + + /// + /// 堆垛机名称 + /// + [JsonPropertyName("stackerName")] + public string? StackerName { get; set; } + + /// + /// 堆垛机状态 + /// + /// + /// 0 - 禁用 + /// 1 - 启用 + /// + [JsonPropertyName("stackerStatus")] + public int? StackerStatus { get; set; } + + /// + /// 货叉状态 + /// + [JsonPropertyName("forkStatus")] + public string? ForkStatus { get; set; } + + /// + /// 查询结果 + /// + [JsonPropertyName("msg")] + public string? Message { get; set; } = "-"; + + /// + /// 当前运行任务号 + /// + [JsonPropertyName("plcId")] + public string? PlcId { get; set; } + + /// + /// 控制方式 + /// + [JsonPropertyName("controlModel")] + public string? ControlModel { get; set; } = "0"; + + /// + /// 设备状态 + /// + [JsonPropertyName("stackerStatusEquip")] + public string? StackerStatusEquip { get; set; } = "0"; + + /// + /// 排 + /// + [JsonPropertyName("queue")] + public int? Queue { get; set; } + + /// + /// 列 + /// + [JsonPropertyName("line")] + public int? Line { get; set; } + + /// + /// 层 + /// + [JsonPropertyName("layer")] + public int? Layer { get; set; } + + /// + /// 深 + /// + [JsonPropertyName("depth")] + public int? Depth { get; set; } + + /// + /// 条码 + /// + [JsonPropertyName("code")] + public string? Code { get; set; } + + /// + /// 报警编号 + /// + [JsonPropertyName("errCode")] + public int? ErrCode { get; set; } + + + +} diff --git a/WcsMain/ApiServe/Controllers/WcsController/StackerController.cs b/WcsMain/ApiServe/Controllers/WcsController/StackerController.cs index afe644f..653e288 100644 --- a/WcsMain/ApiServe/Controllers/WcsController/StackerController.cs +++ b/WcsMain/ApiServe/Controllers/WcsController/StackerController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using WcsMain.ApiServe.ControllerFilter.ExceptionFilter; using WcsMain.ApiServe.Controllers.Dto; +using WcsMain.ApiServe.Controllers.Dto.WcsDto.Stacker; using WcsMain.ApiServe.Service.WcsService; using WcsMain.DataBase.TableEntity; @@ -24,4 +25,14 @@ public class StackerController(StackerService stackerService) : ControllerBase { return _stackerService.GetStacker(); } + + /// + /// 查询所有的 堆垛机状态信息 ---- 从设备返回 + /// + /// + [HttpGet("getStackerStatus")] + public WcsApiResponse> GetStackerStatus() + { + return _stackerService.GetStackerStatus(); + } } \ No newline at end of file diff --git a/WcsMain/ApiServe/Service/WcsService/StackerService.cs b/WcsMain/ApiServe/Service/WcsService/StackerService.cs index 618b90c..e454e3d 100644 --- a/WcsMain/ApiServe/Service/WcsService/StackerService.cs +++ b/WcsMain/ApiServe/Service/WcsService/StackerService.cs @@ -1,16 +1,20 @@ -using WcsMain.ApiServe.Controllers.Dto; +using System.Collections.Generic; +using WcsMain.ApiServe.Controllers.Dto; +using WcsMain.ApiServe.Controllers.Dto.WcsDto.Stacker; using WcsMain.ApiServe.Factory; using WcsMain.DataBase.Dao; using WcsMain.DataBase.TableEntity; +using WcsMain.PlcOperation.Stacker; using WcsMain.WcsAttribute.AutoFacAttribute; namespace WcsMain.ApiServe.Service.WcsService; [Service] -public class StackerService(AppStackerDao stackerDao) +public class StackerService(AppStackerDao stackerDao, StackerOperation stackerOperation) { private readonly AppStackerDao _stackerDao = stackerDao; + private readonly StackerOperation _stackerOperation = stackerOperation; /// /// 查询所有的 堆垛机信息 @@ -26,4 +30,49 @@ public class StackerService(AppStackerDao stackerDao) return WcsApiResponseFactory.Success(stackers, "查询成功"); } + /// + /// 查询所有的 堆垛机状态信息 ---- 从设备返回 + /// + /// + public WcsApiResponse> GetStackerStatus() + { + List? stackers = _stackerDao.Select(new AppStacker()); + if (stackers == default) + { + return WcsApiResponseFactory.DataBaseErr>(); + } + List getStackerStatusResponses = []; + foreach(AppStacker stacker in stackers) + { + GetStackerStatusResponse stackerStatusResponse = new() + { + StackerId = stacker.StackerId, + StackerName = stacker.StackerName, + StackerStatus = stacker.StackerStatus, + ForkStatus = stacker.ForkStatus, + }; + /* 获取堆垛机状态 */ + var (errMsg, stackerInfo) = _stackerOperation.GetStackerInfo((int)stacker.StackerId!); + if(string.IsNullOrEmpty(errMsg) && stackerInfo != default) + { + stackerStatusResponse.Message = "查询成功"; + stackerStatusResponse.PlcId = stackerInfo.PlcId.ToString(); + stackerStatusResponse.ControlModel = stackerInfo.ControlModel.ToString(); + stackerStatusResponse.StackerStatusEquip = stackerInfo.StackerStatus.ToString(); + stackerStatusResponse.Queue = stackerInfo.Row; + stackerStatusResponse.Line = stackerInfo.Line; + stackerStatusResponse.Layer = stackerInfo.Layer; + stackerStatusResponse.Depth = stackerInfo.Depth; + stackerStatusResponse.Code = stackerInfo.Code.ToString(); + stackerStatusResponse.ErrCode = stackerInfo.ErrCode; + } + else + { + stackerStatusResponse.Message = errMsg; + } + getStackerStatusResponses.Add(stackerStatusResponse); + } + return WcsApiResponseFactory.Success(getStackerStatusResponses, "查询成功"); + } + } diff --git a/WcsMain/AppEntity/SystemData/AppSettingJsonEntity.cs b/WcsMain/AppEntity/SystemData/AppSettingJsonEntity.cs index 70b6e5a..9e21809 100644 --- a/WcsMain/AppEntity/SystemData/AppSettingJsonEntity.cs +++ b/WcsMain/AppEntity/SystemData/AppSettingJsonEntity.cs @@ -46,4 +46,9 @@ public class ApplicationConfig /// 是否仅以 api 形式运行,此状态下不会运行 Wcs 相关内容 /// public bool? ApiOnly { get; set; } + + /// + /// 程序的语言 + /// + public string? Language { get; set; } = "zh-CN"; } \ No newline at end of file diff --git a/WcsMain/Business/CirculationTask/CommonCirculation/HeartBeat.cs b/WcsMain/Business/CirculationTask/CommonCirculation/HeartBeat.cs index af0b03d..45e385b 100644 --- a/WcsMain/Business/CirculationTask/CommonCirculation/HeartBeat.cs +++ b/WcsMain/Business/CirculationTask/CommonCirculation/HeartBeat.cs @@ -4,13 +4,14 @@ using WcsMain.PlcOperation; namespace WcsMain.Business.CirculationTask.CommonCirculation; [Circulation] -public class HeartBeat +public class HeartBeat(ConveyOperation conveyOperation) { + private readonly ConveyOperation _conveyOperation = conveyOperation; - //[Circulation("输送机心跳", 1000)] + [Circulation("输送机心跳", 1000)] public bool ConveyHeartBeat() { - ConveyOperation.Instance().WriteHeartBeat(); + _conveyOperation.WriteHeartBeat(); return true; } diff --git a/WcsMain/Business/CirculationTask/Stacker/ExeTaskDoubleFork.cs b/WcsMain/Business/CirculationTask/Stacker/ExeTaskDoubleFork.cs new file mode 100644 index 0000000..a27c296 --- /dev/null +++ b/WcsMain/Business/CirculationTask/Stacker/ExeTaskDoubleFork.cs @@ -0,0 +1,469 @@ +using CirculateTool; +using WcsMain.Business.CommonAction; +using WcsMain.Common; +using WcsMain.DataBase.Dao; +using WcsMain.DataBase.MixDao; +using WcsMain.DataBase.TableEntity; +using WcsMain.DataService; +using WcsMain.Enum.Stacker; +using WcsMain.Enum.TaskEnum; +using WcsMain.ExtendMethod; +using WcsMain.PlcOperation; +using WcsMain.PlcOperation.Entity.Stacker; +using WcsMain.PlcOperation.Stacker; +using static Dm.net.buffer.ByteArrayBuffer; + +namespace WcsMain.Business.CirculationTask.Stacker; + + +/// +/// 双货叉单深位执行堆垛机任务 ---- 卡特模式 +/// +[Circulation("双货叉单深位执行堆垛机任务")] +public class ExeTaskDoubleFork( + StackerOperation stackerOperation, AppWcsTaskDao wcsTaskDao, WCSTaskExecuteEvent wcsTaskEvent, + ConveyOperation conveyOperation, DataBaseData dataBaseData) +{ + private readonly WCSTaskExecuteEvent _wcsTaskEvent = wcsTaskEvent; + private readonly AppWcsTaskDao _wcsTaskDao = wcsTaskDao; + private readonly StackerOperation _stackerOperation = stackerOperation; + private readonly ConveyOperation _conveyOperation = conveyOperation; + private readonly DataBaseData _dataBaseData = dataBaseData; + + /// + /// 执行堆垛机任务 + /// + /// + [Circulation("执行堆垛机任务")] + public bool ExecuteStackerTask() + { + List tasks = []; + foreach (var stacker in CommonData.AppStackers.Open()) + { + tasks.Add(Task.Factory.StartNew(() => + { + var stackerUseStatus = _stackerOperation.StackerCanUse(stacker.StackerId, out int plcId, out int spare1); + if (stackerUseStatus == StackerUseStatusEnum.Free) + { + /* 空闲时正常执行任务 */ + // 移库 + bool exeMoveTask = ExecuteMoveTask(stacker.StackerId); + if (exeMoveTask) return; + // 出库 + bool exeOutTask = ExecuteOutTask(stacker.StackerId); + if (exeOutTask) return; + // 入库 + bool exeInTask = ExecuteInTask(stacker.StackerId); + if (exeInTask) return; + // 拣选 + //bool exePickTask = ExecutePickTask(stacker.StackerId); + //if (exePickTask) return; + } + if (stackerUseStatus == StackerUseStatusEnum.waitTask) + { + /* 重复入库时执行任务 */ + bool exeDoubleInTask = ExecuteDoubleInTask(stacker.StackerId, plcId, 1); + if (exeDoubleInTask) return; + exeDoubleInTask = ExecuteDoubleInTask(stacker.StackerId, spare1, 2); + if (exeDoubleInTask) return; + } + })); + } + Task.WaitAll([.. tasks]); + return true; + } + + + /// + /// 执行堆垛机入库任务,若执行了任务则返回 true + /// + /// + /// + private bool ExecuteInTask(int? stackerId) + { + if (stackerId == default) return false; + /* 检查入库站台是否允许取货 */ + bool allowGetGoods = _conveyOperation.AllGetVehicle(stackerId.ToString()); + if(!allowGetGoods) return false; // 入库站台不允许取货 + /* 读取入库站台的条码 */ + var codes = _conveyOperation.ReadConveyCode(stackerId.ToString()); + if(codes == default || codes.Count != 2) return false; + /* 构造任务数据 */ + bool isWriteTask = false; // 指示是否写入任务 + for (int i = 0; i < 2; i++) + { + /* 校验货叉开启 */ + string forkStatus = CommonData.AppStackers.First(f => f.StackerId == stackerId).ForkStatus ?? ""; + if (forkStatus.Length <= i || forkStatus[i] != '1') continue; // 获取数据失败或者货叉未开启 + var code = codes[i]; + if (string.IsNullOrEmpty(code)) continue; // 检查条码是否为空 + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 返回:{code}"); + if (code == "NoRead") // 检查条码是否为 NoRead + { + /* 检查卸货站台是否允许卸货 */ + bool allowSetGoods = _conveyOperation.AllSetVehicle(stackerId.ToString()); + if (!allowSetGoods) continue; // 出库站台不允许取货 + /* 生成一个直接卸货出去的任务 */ + int? plcId = _dataBaseData.GetNewPlcTaskId(); + if(plcId == default || plcId == 0) continue; + StackerPlcTask errTask = StackerPlcTask.DefaultErrTask((int)plcId!, (int)stackerId!, i + 1); + string WriteTaskErrText = _stackerOperation.WriteTask(errTask, i + 1); + if(string.IsNullOrEmpty(WriteTaskErrText)) // 写入成功 + { + ConsoleLog.Success($"{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务成功,{errTask}"); + isWriteTask = true; + } + else + { + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务失败,{errTask},异常信息:{WriteTaskErrText},"); + } + continue; + } + var wcsTasks = _wcsTaskDao.Select(new AppWcsTask { VehicleNo = code, TaskStatus = (int)WcsTaskStatusEnum.create }); + if(wcsTasks == default) // 查询任务失败 + { + ConsoleLog.Exception($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code} 查找任务失败,和数据库服务器连接中断"); + continue; + } + if(wcsTasks.Count < 1) // 没有任务 + { + /* 检查卸货站台是否允许卸货 */ + bool allowSetGoods = _conveyOperation.AllSetVehicle(stackerId.ToString()); + if (!allowSetGoods) continue; // 出库站台不允许取货 + /* 生成一个直接卸货出去的任务 */ + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code} 没有找到对应任务"); + int? plcId = _dataBaseData.GetNewPlcTaskId(); + if (plcId == default || plcId == 0) continue; + StackerPlcTask errTask = StackerPlcTask.DefaultErrTask((int)plcId!, (int)stackerId!, i + 1, code.ToPlcVehicleNo()); + string WriteTaskErrText = _stackerOperation.WriteTask(errTask, i + 1); + if (string.IsNullOrEmpty(WriteTaskErrText)) // 写入成功 + { + ConsoleLog.Success($"{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务成功,{errTask}"); + isWriteTask = true; + } + else + { + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务失败,{errTask},异常信息:{WriteTaskErrText},"); + } + continue; + } + var wcsTask = wcsTasks[0]; // 找出第一个任务 + /* 校验终点是否正确 */ + var destinationLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Destination); + if(destinationLocationInfo == default) // 任务终点错误,理论上此处不应该出现异常 + { + /* 检查卸货站台是否允许卸货 */ + bool allowSetGoods = _conveyOperation.AllSetVehicle(stackerId.ToString()); + if (!allowSetGoods) continue; // 出库站台不允许取货 + /* 生成一个直接卸货出去的任务 */ + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code} 无法识别的入库库位"); + int? plcId = _dataBaseData.GetNewPlcTaskId(); + if (plcId == default || plcId == 0) continue; + StackerPlcTask errTask = StackerPlcTask.DefaultErrTask((int)plcId!, (int)stackerId!, i + 1, code.ToPlcVehicleNo()); + string WriteTaskErrText = _stackerOperation.WriteTask(errTask, i + 1); + if (string.IsNullOrEmpty(WriteTaskErrText)) // 写入成功 + { + ConsoleLog.Success($"{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务成功,{errTask}"); + _wcsTaskEvent.ErrTaskEvent(wcsTask, "入库库位不正确"); + isWriteTask = true; + } + else + { + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务失败,{errTask},异常信息:{WriteTaskErrText},"); + } + continue; + } + /* 判断冲突货位是否有任务 */ + var runningWcsTasks = _wcsTaskDao.QueryTaskWithWcsLocation(destinationLocationInfo.InterveneLocation); + if (runningWcsTasks == default) continue; // 查询失败 + if(runningWcsTasks.Count > 0) continue; // 存在冲突点位,等待 + /* 下发任务 */ + StackerPlcTask stackerTask = wcsTask.ToStackerInTask((int)stackerId!, i + 1, destinationLocationInfo); + string WriteStackerTaskErrText = _stackerOperation.WriteTask(stackerTask, i + 1); + if (string.IsNullOrEmpty(WriteStackerTaskErrText)) // 写入成功 + { + ConsoleLog.Success($"{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务成功,{stackerTask}"); + _wcsTaskEvent.StartTaskEvent(wcsTask); // 任务开始 + isWriteTask = true; + } + else + { + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,入库位置:{i + 1} 条码:{code},写入任务失败,{stackerTask},异常信息:{WriteStackerTaskErrText},"); + } + } + if (isWriteTask) + { + string confirmResult = _stackerOperation.WriteTaskConfirm(stackerId); // 写入任务确认 + ConsoleLog.Info($"堆垛机入库任务写任务确认;信息:{(string.IsNullOrEmpty(confirmResult) ? "成功" : confirmResult)}"); + Thread.Sleep(1000); + } + return isWriteTask; + } + + /// + /// 执行堆垛机出库任务,若执行了任务则返回 true + /// + /// + /// + public bool ExecuteOutTask(int? stackerId) + { + if (stackerId == default) return false; + /* 检查出库站台是否可以卸货 */ + bool allowSetGoods = _conveyOperation.AllSetVehicle(stackerId.ToString()); + if (!allowSetGoods) return false; // 出库站台不允许取货 + var wcsTasks = _wcsTaskDao.SelectOutTaskWithStacker((int)stackerId!); + if (wcsTasks == default) + { + ConsoleLog.Exception(string.Format("【异常】{0} 号堆垛机出库任务查询失败,与数据库连接异常", stackerId)); + Thread.Sleep(5000); + return false; + } + if (wcsTasks.Count == 0) return false; + bool isWriteTask = false; // 指示是否写入任务 + int writeTaskCount = 0; // 表示执行了多少条任务 + for (int i = 0; i < wcsTasks.Count; i++) + { + if (writeTaskCount >= 2) break; // 最多执行两条 + /* 校验货叉开启 */ + string forkStatus = CommonData.AppStackers.First(f => f.StackerId == stackerId).ForkStatus ?? ""; + if (forkStatus.Length <= i || forkStatus[i] != '1') + { + writeTaskCount ++; + continue; // 获取数据失败或者货叉未开启 + } + var wcsTask = wcsTasks[i]; + /* 校验起点是否正确 */ + var originLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Origin); + if (originLocationInfo == default) // 任务起点错误,理论上此处不应该出现异常 + { + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,出库库位:{wcsTask.Origin} 条码:{wcsTask.VehicleNo} 无法识别的出库库位"); + _wcsTaskEvent.ErrTaskEvent(wcsTask, "出库的库位不正确"); + continue; + } + /* 判断冲突货位是否有任务 */ + var runningWcsTasks = _wcsTaskDao.QueryTaskWithWcsLocation(originLocationInfo.InterveneLocation); + if (runningWcsTasks == default) continue; // 查询失败 + if (runningWcsTasks.Count > 0) continue; // 存在冲突点位,等待 + /* 下发任务 */ + StackerPlcTask stackerTask = wcsTask.ToStackerOutTask((int)stackerId!, i + 1, originLocationInfo); + string WriteStackerTaskErrText = _stackerOperation.WriteTask(stackerTask, i + 1); + if (string.IsNullOrEmpty(WriteStackerTaskErrText)) // 写入成功 + { + ConsoleLog.Success($"{stackerId} 号堆垛机,出库位置:{i + 1} 条码:{wcsTask.VehicleNo},写入任务成功,{stackerTask}"); + _wcsTaskEvent.StartTaskEvent(wcsTask); // 任务开始 + writeTaskCount++; + isWriteTask = true; + } + else + { + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,出库位置:{i + 1} 条码:{wcsTask.VehicleNo},写入任务失败,{stackerTask},异常信息:{WriteStackerTaskErrText},"); + } + } + if (isWriteTask) + { + string confirmResult = _stackerOperation.WriteTaskConfirm(stackerId); // 写入任务确认 + ConsoleLog.Info($"堆垛机出库任务写任务确认;信息:{(string.IsNullOrEmpty(confirmResult) ? "成功" : confirmResult)}"); + Thread.Sleep(1000); + } + return isWriteTask; + } + + /// + /// 执行堆垛机拣选任务,若执行了任务则返回 true + /// + /// + /// + public bool ExecutePickTask(int? stackerId) + { + if (stackerId == default) return false; + var wcsTasks = _wcsTaskDao.SelectPickOutTaskWithStacker((int)stackerId!); + if (wcsTasks == default) + { + ConsoleLog.Error($"【异常】{stackerId} 号堆垛机拣选任务查询失败,与数据库连接异常"); + Thread.Sleep(5000); + return false; + } + if (wcsTasks.Count == 0) return false; + var wcsTask = wcsTasks[0]; // 取第一条任务 + /* 校验出库站台是否可以卸货 */ + // TODO + /* 检验并返回起点终点信息 */ + var originLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Origin); + var destinationLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Destination); + if (destinationLocationInfo == default || originLocationInfo == default) // 起点终点错误,直接标记错误 + { + // 已经在接口内做了检查,按道理此处不会出错。直接返回异常,任务直接取消 + ConsoleLog.Warning($"【警告】任务号:{wcsTask.TaskId},Plc任务号:{wcsTask.PlcId} 无法执行,起点或者终点存在异常"); + _wcsTaskEvent.ErrTaskEvent(wcsTask, $"起点或者终点存在异常"); + return false; + } + StackerPlcTask stackerTask = new() + { + StackerId = stackerId, + PlcId = wcsTask.PlcId, + TaskType = Convert.ToInt16(wcsTask.TaskType), + GetStand = 0, + InTunnelId = Convert.ToInt16(stackerId), + OutTunnelId = Convert.ToInt16(stackerId), + SetStand = 0, + GetQueue = Convert.ToInt16(originLocationInfo.Queue), + GetLine = Convert.ToInt16(originLocationInfo.Line), + GetLayer = Convert.ToInt16(originLocationInfo.Layer), + GetDeep = Convert.ToInt16(originLocationInfo.Depth), + SetQueue = Convert.ToInt16(destinationLocationInfo.Queue), + SetLine = Convert.ToInt16(destinationLocationInfo.Line), + SetLayer = Convert.ToInt16(destinationLocationInfo.Layer), + SetDeep = Convert.ToInt16(destinationLocationInfo.Depth), + Size = Convert.ToInt16(wcsTask.VehicleSize), + Weight = Convert.ToInt16(wcsTask.Weight), + Code = wcsTask.PlcVehicleNo ?? 0, + }; + var writeStackerTaskErrText = _stackerOperation.WriteTask(stackerTask); + if (string.IsNullOrEmpty(writeStackerTaskErrText)) + { + ConsoleLog.Success($"堆垛机:{stackerId} 写入拣选任务成功,箱号:{wcsTask.VehicleNo},PlcId:{wcsTask.PlcId};{wcsTask.Origin} => {wcsTask.Destination}"); + _wcsTaskEvent.StartTaskEvent(wcsTask); + return true; + } + else + { + ConsoleLog.Warning($"【警告】堆垛机:{stackerId} 写入拣选任务失败,箱号:{wcsTask.VehicleNo},PlcId:{wcsTask.PlcId};{wcsTask.Origin} => {wcsTask.Destination},异常信息:{writeStackerTaskErrText}"); + _wcsTaskEvent.ErrTaskEvent(wcsTask, $"写入任务失败,异常信息:{writeStackerTaskErrText}"); + } + return false; + + } + + /// + /// 执行堆垛机移库任务,若执行了任务则返回 true + /// + /// + /// + public bool ExecuteMoveTask(int? stackerId) + { + if (stackerId == default) return false; + var wcsTasks = _wcsTaskDao.SelectMoveTaskWithStacker((int)stackerId!); + if (wcsTasks == default) + { + ConsoleLog.Error($"【异常】{stackerId} 号堆垛机移库任务查询失败,与数据库连接异常"); + Thread.Sleep(5000); + return false; + } + if (wcsTasks.Count == 0) return false; + bool isWriteTask = false; // 指示是否写入任务 + int writeTaskCount = 0; // 表示执行了多少条任务 + for (int i = 0; i < wcsTasks.Count; i++) + { + if (writeTaskCount >= 2) break; // 最多执行两条 + /* 校验货叉开启 */ + string forkStatus = CommonData.AppStackers.First(f => f.StackerId == stackerId).ForkStatus ?? ""; + if (forkStatus.Length <= i || forkStatus[i] != '1') + { + writeTaskCount++; + continue; // 获取数据失败或者货叉未开启 + } + var wcsTask = wcsTasks[i]; + /* 校验起点是否正确 */ + var originLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Origin); + var destinationLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(wcsTask.Origin); + if (originLocationInfo == default || destinationLocationInfo == default) // 任务起点终点错误,理论上此处不应该出现异常 + { + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,移库库位:{wcsTask.Origin} 条码:{wcsTask.VehicleNo} 无法识别的库位"); + _wcsTaskEvent.ErrTaskEvent(wcsTask, "移库的库位不正确"); + continue; + } + /* 判断冲突货位是否有任务,起点终点都要判断 */ + var runningWcsTasks = _wcsTaskDao.QueryTaskWithWcsLocation(destinationLocationInfo.InterveneLocation, originLocationInfo.InterveneLocation); + if (runningWcsTasks == default) continue; // 查询失败 + if (runningWcsTasks.Count > 0) continue; // 存在冲突点位,等待 + /* 下发任务 */ + StackerPlcTask stackerTask = wcsTask.ToStackerMoveTask((int)stackerId!, originLocationInfo, destinationLocationInfo); + string WriteStackerTaskErrText = _stackerOperation.WriteTask(stackerTask, i + 1); + if (string.IsNullOrEmpty(WriteStackerTaskErrText)) // 写入成功 + { + ConsoleLog.Success($"{stackerId} 号堆垛机,移库位置:{i + 1} 条码:{wcsTask.VehicleNo},写入任务成功,{stackerTask}"); + _wcsTaskEvent.StartTaskEvent(wcsTask); // 任务开始 + writeTaskCount++; + isWriteTask = true; + } + else + { + ConsoleLog.Warning($"【警告】{stackerId} 号堆垛机,移库位置:{i + 1} 条码:{wcsTask.VehicleNo},写入任务失败,{stackerTask},异常信息:{WriteStackerTaskErrText},"); + } + } + if (isWriteTask) + { + string confirmResult = _stackerOperation.WriteTaskConfirm(stackerId); // 写入任务确认 + ConsoleLog.Info($"堆垛机移库任务写任务确认;信息:{(string.IsNullOrEmpty(confirmResult) ? "成功" : confirmResult)}"); + Thread.Sleep(1000); + } + return isWriteTask; + + } + + /// + /// 执行重复入库新任务,若执行了返回true + /// + /// + /// + /// 货叉编号 + /// + public bool ExecuteDoubleInTask(int? stackerId, int plcId, int forkId) + { + if (stackerId == default || plcId == 0) return false; + /* 查找这个PlcId对应的wms任务 */ + List? doubleInTasks = _wcsTaskDao.Select(new AppWcsTask { PlcId = plcId }); + if (doubleInTasks == default) + { + ConsoleLog.Error($"【异常】{stackerId} 号堆垛机重复入库任务检验失败,与数据库连接异常"); + Thread.Sleep(5000); + return false; + } + if (doubleInTasks.Count == 0) return false; // 找不到对应的PlcId的任务 + var doubleTask = doubleInTasks[0]; + /* 判断这个任务是否出现卸货位置有货 */ + if(doubleTask.TaskStatus != (int)WcsTaskStatusEnum.doubleIn) { return false; } // 没有重复入库不执行下面方法 + /* 查找这个任务的新任务 */ + List? newTasks = _wcsTaskDao.Select(new AppWcsTask { TaskId = doubleTask.TaskId, TaskType = (int)WcsTaskTypeEnum.newTaskForDoubleIn, TaskStatus = (int)WmsTaskStatusEnum.create }); + if (newTasks == default) + { + ConsoleLog.Error($"【异常】{stackerId} 号堆垛机重复入库任务新任务查询失败,与数据库连接异常"); + Thread.Sleep(5000); + return false; + } + if (newTasks.Count == 0) return false; + var newTask = newTasks[0]; + var destinationLocationInfo = CommonData.AppLocations.DetailWithWcsLocation(newTask.Destination); + if (destinationLocationInfo == default) // 终点错误,直接标记错误 + { + // 已经在接口内做了检查,按道理此处不会出错。直接返回异常,任务直接取消 + ConsoleLog.Warning($"【警告】任务号:{newTask.TaskId},Plc任务号:{newTask.PlcId} 无法执行,新终点存在异常"); + _wcsTaskEvent.ErrTaskEvent(newTask, $"新终点存在异常"); + return false; + } + /* 判断冲突货位是否有任务 */ + var runningWcsTasks = _wcsTaskDao.QueryTaskWithWcsLocation(destinationLocationInfo.InterveneLocation); + if (runningWcsTasks == default) return false; // 查询失败 + if (runningWcsTasks.Count > 0) return false; // 存在冲突点位,等待 + /* 下发任务 */ + StackerPlcTask stackerTask = newTask.ToStackerInTask((int)stackerId!, forkId, destinationLocationInfo); + var writeStackerTaskErrText = _stackerOperation.WriteTask(stackerTask); + if (string.IsNullOrEmpty(writeStackerTaskErrText)) + { + ConsoleLog.Success($"堆垛机:{stackerId} 写入重复入库新任务成功,箱号:{newTask.VehicleNo},PlcId:{newTask.PlcId};新目的地:{newTask.Destination}"); + _wcsTaskEvent.StartTaskEvent(newTask); + return true; + } + else + { + ConsoleLog.Warning($"【警告】堆垛机:{stackerId} 写入重复入库新任务失败,箱号:{newTask.VehicleNo},PlcId:{newTask.PlcId};新目的地:{newTask.Destination},异常信息:{writeStackerTaskErrText}"); + _wcsTaskEvent.ErrTaskEvent(newTask, $"写入任务失败,异常信息:{writeStackerTaskErrText}"); + } + return false; + } + + + + + +} diff --git a/WcsMain/Business/CirculationTask/Stacker/ExecuteWcsTask.cs b/WcsMain/Business/CirculationTask/Stacker/ExecuteWcsTask.cs index 3883954..16398f9 100644 --- a/WcsMain/Business/CirculationTask/Stacker/ExecuteWcsTask.cs +++ b/WcsMain/Business/CirculationTask/Stacker/ExecuteWcsTask.cs @@ -14,7 +14,7 @@ namespace WcsMain.Business.CirculationTask.Stacker; /// /// 执行堆垛机任务类 /// -[Circulation(tags: ["stacker"])] +//[Circulation(tags: ["stacker"])] public class ExecuteWcsTask(StackerOperation stackerOperation, AppWcsTaskDao wcsTaskDao, WCSTaskExecuteEvent wcsTaskEvent) { private readonly WCSTaskExecuteEvent _wcsTaskEvent = wcsTaskEvent; @@ -30,7 +30,7 @@ public class ExecuteWcsTask(StackerOperation stackerOperation, AppWcsTaskDao wcs { foreach (var stacker in CommonData.AppStackers.Open()) { - var stackerUseStatus = _stackerOperation.StackerCanUse(stacker.StackerId, out int plcId); + var stackerUseStatus = _stackerOperation.StackerCanUse(stacker.StackerId, out int plcId, out int _); if (stackerUseStatus == StackerUseStatusEnum.Free) { /* 空闲时正常执行任务 */ diff --git a/WcsMain/DataBase/Dao/AppLocationDao.cs b/WcsMain/DataBase/Dao/AppLocationDao.cs index 6c115f5..dacba30 100644 --- a/WcsMain/DataBase/Dao/AppLocationDao.cs +++ b/WcsMain/DataBase/Dao/AppLocationDao.cs @@ -98,6 +98,8 @@ public class AppLocationDao .WhereIF(appLocation.Line != null, w => w.Line == appLocation.Line) .WhereIF(appLocation.Layer != null, w => w.Layer == appLocation.Layer) .WhereIF(appLocation.Depth != null, w => w.Depth == appLocation.Depth) + .WhereIF(appLocation.InterveneLocation != default, w => w.InterveneLocation == appLocation.InterveneLocation) + .WhereIF(appLocation.VehicleType == default, w => w.VehicleType == appLocation.VehicleType) .WhereIF(!string.IsNullOrEmpty(appLocation.VehicleNo), w => w.VehicleNo == appLocation.VehicleNo) .OrderBy(o => new { o.Queue, o.Line, o.Layer, o.Depth }) .ToList(); @@ -133,19 +135,18 @@ public class AppLocationDao || w.WmsLocation!.Contains(request.SearchStr!) || w.VehicleNo!.Contains(request.SearchStr!) || w.Remark!.Contains(request.SearchStr!)); - if (request.LocationStatus != default) // 查询任务类型 + if (request.LocationStatus != default) // 查询点位状态 { List locationStatus = []; - foreach (var ls in request.LocationStatus) - { - if (ls == "空闲") { locationStatus.Add((int)LocationStatusEnum.empty); } - if (ls == "锁定") { locationStatus.Add((int)LocationStatusEnum.locked); } - if (ls == "占用") { locationStatus.Add((int)LocationStatusEnum.used); } - if (ls == "禁用") { locationStatus.Add((int)LocationStatusEnum.forbidden); } - if (ls == "特殊点位") { locationStatus.Add((int)LocationStatusEnum.special); } - } + request.LocationStatus.ForEach(item => locationStatus.Add(item)); sqlFuc.Where(w => locationStatus.Contains(w.LocationStatus)); } + if (request.LocationType != default) // 查询点位类型 + { + List locationTypes = []; + request.LocationType.ForEach(item => locationTypes.Add(item)); + sqlFuc.Where(w => locationTypes.Contains(w.LocationType)); + } sqlFuc.OrderBy(o => new { o.EquipmentId, o.Queue, o.Line, o.Layer, o.Depth }); var queryResult = sqlFuc.ToPageList(request.Page!.PageIndex, request.Page!.PageSize, ref totalRows); return (queryResult, totalRows); diff --git a/WcsMain/DataBase/Dao/AppWcsTaskDao.cs b/WcsMain/DataBase/Dao/AppWcsTaskDao.cs index c37f0e4..9495f13 100644 --- a/WcsMain/DataBase/Dao/AppWcsTaskDao.cs +++ b/WcsMain/DataBase/Dao/AppWcsTaskDao.cs @@ -378,7 +378,7 @@ public class AppWcsTaskDao { sql.AppendLine($"and vehicle_no = '{vehicleNo}' "); } - sql.AppendLine("order by priority desc, create_time asc "); + sql.AppendLine("order by priority desc, wms_time asc "); var sqlFuc = CommonTool.DbServe.Ado.SqlQuery(sql.ToString()); return sqlFuc; } @@ -448,4 +448,35 @@ public class AppWcsTaskDao return default; } } + + + /// + /// 根据起点或者终点查找当前正在运行的任务 + /// + /// + /// + public List? QueryTaskWithWcsLocation(params string?[] destinations) + { + List? result = []; + if (destinations.Length < 1) return result; + try + { + foreach (var destination in destinations) + { + var sqlFuc = CommonTool.DbServe.Queryable() + .Where(x => x.Destination == destination || x.Origin == destination) + .Where(x => DataService.EnumData.GetWcsTaskStatusEnumRunningStatus().Contains((int)WcsTaskStatusEnum.running)); + result.AddRange(sqlFuc.ToList()); + } + return result; + } + catch (Exception ex) + { + _ = ex; + return default; + } + + + + } } \ No newline at end of file diff --git a/WcsMain/DataBase/TableEntity/AppLocation.cs b/WcsMain/DataBase/TableEntity/AppLocation.cs index fd1c75b..4ed94ac 100644 --- a/WcsMain/DataBase/TableEntity/AppLocation.cs +++ b/WcsMain/DataBase/TableEntity/AppLocation.cs @@ -46,6 +46,13 @@ public class AppLocation [JsonPropertyName("locationStatus")] public int? LocationStatus { get; set; } + /// + /// 点位类型 + /// + [SugarColumn(ColumnName = "location_type")] + [JsonPropertyName("locationType")] + public int? LocationType { get; set; } + /// /// 排 /// @@ -75,11 +82,18 @@ public class AppLocation public int? Depth { get; set; } /// - /// 点位类型 + /// 干涉的点位 /// - [SugarColumn(ColumnName = "location_type")] - [JsonPropertyName("locationType")] - public int? LocationType { get; set; } + [SugarColumn(ColumnName = "intervene_location")] + [JsonPropertyName("interveneLocation")] + public string? InterveneLocation { get; set; } + + /// + /// 兼容的载具类型 + /// + [SugarColumn(ColumnName = "vehicle_type")] + [JsonPropertyName("vehicleType")] + public string? VehicleType { get; set; } /// /// 载具编号 diff --git a/WcsMain/DataBase/TableEntity/AppStacker.cs b/WcsMain/DataBase/TableEntity/AppStacker.cs index 4527e96..4ce2ae8 100644 --- a/WcsMain/DataBase/TableEntity/AppStacker.cs +++ b/WcsMain/DataBase/TableEntity/AppStacker.cs @@ -34,6 +34,13 @@ public class AppStacker [JsonPropertyName("stackerStatus")] public int? StackerStatus { get; set; } + /// + /// 货叉状态 + /// + [SugarColumn(ColumnName = "fork_status")] + [JsonPropertyName("forkStatus")] + public string? ForkStatus { get; set; } + /// /// 控制该堆垛机的PLC /// diff --git a/WcsMain/DataService/EnumData.cs b/WcsMain/DataService/EnumData.cs new file mode 100644 index 0000000..e25f580 --- /dev/null +++ b/WcsMain/DataService/EnumData.cs @@ -0,0 +1,20 @@ +using WcsMain.Enum.TaskEnum; + +namespace WcsMain.DataService; + +/// +/// 枚举的数据操作 +/// +public static class EnumData +{ + /// + /// 返回可以视作正在运行的任务的Wcs任务状态的枚举 + /// + /// + public static int[] GetWcsTaskStatusEnumRunningStatus() + { + return [(int)WcsTaskStatusEnum.leaveOrigin, (int)WcsTaskStatusEnum.running]; + } + + +} diff --git a/WcsMain/Enum/TaskEnum/WcsTaskStatusEnum.cs b/WcsMain/Enum/TaskEnum/WcsTaskStatusEnum.cs index f5d8a0e..3349768 100644 --- a/WcsMain/Enum/TaskEnum/WcsTaskStatusEnum.cs +++ b/WcsMain/Enum/TaskEnum/WcsTaskStatusEnum.cs @@ -10,5 +10,7 @@ public enum WcsTaskStatusEnum running = 2, arriveDestination = 3, complete = 4, + doubleIn = 7, + emptyOut = 8, err = 9, } \ No newline at end of file diff --git a/WcsMain/Enum/TaskEnum/WcsTaskTypeEnum.cs b/WcsMain/Enum/TaskEnum/WcsTaskTypeEnum.cs index 9dad01e..efcb48d 100644 --- a/WcsMain/Enum/TaskEnum/WcsTaskTypeEnum.cs +++ b/WcsMain/Enum/TaskEnum/WcsTaskTypeEnum.cs @@ -7,4 +7,7 @@ public enum WcsTaskTypeEnum pick = 4, // 拣选任务 check = 10, // 盘点任务 moveTask = 9, // 移库任务 + + + newTaskForDoubleIn = 21, // 卸货位置有货的新任务 } diff --git a/WcsMain/ExtendMethod/AppWcsTaskExtendMethod.cs b/WcsMain/ExtendMethod/AppWcsTaskExtendMethod.cs index 267699b..d8cf231 100644 --- a/WcsMain/ExtendMethod/AppWcsTaskExtendMethod.cs +++ b/WcsMain/ExtendMethod/AppWcsTaskExtendMethod.cs @@ -1,4 +1,6 @@ using WcsMain.DataBase.TableEntity; +using WcsMain.Enum.TaskEnum; +using WcsMain.PlcOperation.Entity.Stacker; namespace WcsMain.ExtendMethod; @@ -34,4 +36,110 @@ public static class AppWcsTaskExtendMethod if (wcsTask == default) return false; return wcsTask.NextPlcId == default; } + + /// + /// 生成一个入库任务 + /// + /// + /// + /// + /// + /// + public static StackerPlcTask ToStackerInTask(this AppWcsTask wcsTask, int stackerId, int forkId, AppLocation destinationDetail) + { + StackerPlcTask stackerTask = new() + { + StackerId = stackerId, + PlcId = wcsTask.PlcId, + TaskType = (int)WcsTaskTypeEnum.inTask, + GetStand = 0, + InTunnelId = Convert.ToInt16(stackerId), + OutTunnelId = Convert.ToInt16(stackerId), + SetStand = 0, + GetQueue = 2, + GetLine = Convert.ToInt16(forkId), + GetLayer = 1, + GetDeep = 1, + SetQueue = Convert.ToInt16(destinationDetail.Queue), + SetLine = Convert.ToInt16(destinationDetail.Line), + SetLayer = Convert.ToInt16(destinationDetail.Layer), + SetDeep = Convert.ToInt16(destinationDetail.Depth), + Size = Convert.ToInt16(wcsTask.VehicleSize), + Weight = Convert.ToInt16(wcsTask.Weight), + Code = wcsTask.PlcVehicleNo ?? 0, + }; + return stackerTask; + } + + + /// + /// 生成一个入库任务 + /// + /// + /// + /// + /// + /// + public static StackerPlcTask ToStackerOutTask(this AppWcsTask wcsTask, int stackerId, int forkId, AppLocation originDetail) + { + StackerPlcTask stackerTask = new() + { + StackerId = stackerId, + PlcId = wcsTask.PlcId, + TaskType = (int)WcsTaskTypeEnum.outTask, + GetStand = 0, + InTunnelId = Convert.ToInt16(stackerId), + OutTunnelId = Convert.ToInt16(stackerId), + SetStand = 0, + GetQueue = Convert.ToInt16(originDetail.Queue), + GetLine = Convert.ToInt16(originDetail.Queue), + GetLayer = Convert.ToInt16(originDetail.Queue), + GetDeep = Convert.ToInt16(originDetail.Queue), + SetQueue = 2, + SetLine = Convert.ToInt16(forkId), + SetLayer = 2, + SetDeep = 1, + Size = Convert.ToInt16(wcsTask.VehicleSize), + Weight = Convert.ToInt16(wcsTask.Weight), + Code = wcsTask.PlcVehicleNo ?? 0, + }; + return stackerTask; + } + + /// + /// 生成一个移库任务 + /// + /// + /// + /// + /// + /// + public static StackerPlcTask ToStackerMoveTask(this AppWcsTask wcsTask, int stackerId, AppLocation originDetail, AppLocation destinationDetail) + { + StackerPlcTask stackerTask = new() + { + StackerId = stackerId, + PlcId = wcsTask.PlcId, + TaskType = (int)WcsTaskTypeEnum.moveTask, + GetStand = 0, + InTunnelId = Convert.ToInt16(stackerId), + OutTunnelId = Convert.ToInt16(stackerId), + SetStand = 0, + GetQueue = Convert.ToInt16(originDetail.Queue), + GetLine = Convert.ToInt16(originDetail.Queue), + GetLayer = Convert.ToInt16(originDetail.Queue), + GetDeep = Convert.ToInt16(originDetail.Queue), + SetQueue = Convert.ToInt16(destinationDetail.Queue), + SetLine = Convert.ToInt16(destinationDetail.Line), + SetLayer = Convert.ToInt16(destinationDetail.Layer), + SetDeep = Convert.ToInt16(destinationDetail.Depth), + Size = Convert.ToInt16(wcsTask.VehicleSize), + Weight = Convert.ToInt16(wcsTask.Weight), + Code = wcsTask.PlcVehicleNo ?? 0, + }; + return stackerTask; + } + + + } diff --git a/WcsMain/Language/Readme.txt b/WcsMain/Language/Readme.txt new file mode 100644 index 0000000..74d9903 --- /dev/null +++ b/WcsMain/Language/Readme.txt @@ -0,0 +1 @@ +多语言版本尚未添加 \ No newline at end of file diff --git a/WcsMain/PlcOperation/ConveyOperation.cs b/WcsMain/PlcOperation/ConveyOperation.cs index f536aa2..4f40281 100644 --- a/WcsMain/PlcOperation/ConveyOperation.cs +++ b/WcsMain/PlcOperation/ConveyOperation.cs @@ -2,21 +2,71 @@ using System.Text.RegularExpressions; using WcsMain.Common; using WcsMain.PlcOperation.Entity; +using WcsMain.WcsAttribute.AutoFacAttribute; namespace WcsMain.PlcOperation; /// /// 输送机操作 ---- 箱式线,不包含于立库库前设备,库前请参考堆垛机操作 /// +[Component] public class ConveyOperation { - private static ConveyOperation? _instance; - - public static ConveyOperation Instance() + /// + /// 写入输送机心跳 + /// + /// + public string WriteHeartBeat() { - return _instance ??= new ConveyOperation(); + if (!CommonData.IsConnectPlc || CommonTool.Siemens == default) return "PLC尚未连接。"; + var (writeResult, _) = CommonTool.Siemens.WritePlcWhithName($"箱式线写心跳", (short)0); + return writeResult.Success ? string.Empty : writeResult.Message ?? "写入失败,未知原因"; } + /// + /// 箱式线允许取货 + /// + /// 要取货的点位 + /// + public bool AllGetVehicle(string? point) + { + if (!CommonData.IsConnectPlc || CommonTool.Siemens == default) return false; + var readResult = CommonTool.Siemens.ReadInt16WithName($"箱式线允许取货{point}"); + return readResult.Success && readResult.Value == 1; + } + + /// + /// 箱式线允许卸货 + /// + /// 要取货的点位 + /// + public bool AllSetVehicle(string? point) + { + if (!CommonData.IsConnectPlc || CommonTool.Siemens == default) return false; + var readResult = CommonTool.Siemens.ReadInt16WithName($"箱式线允许卸货{point}"); + return readResult.Success && readResult.Value == 1; + } + + /// + /// 读取箱式线入库站台反馈的载具号 + /// + /// + /// + public List? ReadConveyCode(string? point) + { + if (!CommonData.IsConnectPlc || CommonTool.Siemens == default) return default; + var readResult1 = CommonTool.Siemens.ReadStringWithName($"箱式线入库载具号{point}-1", 20, Encoding.ASCII); + var readResult2 = CommonTool.Siemens.ReadStringWithName($"箱式线入库载具号{point}-2", 20, Encoding.ASCII); + if (!readResult1.Success! || !readResult2.Success!) return default; + // 返回读取到的任务号 + return [Regex.Replace(readResult1.Value ?? "", "\\W", ""), Regex.Replace(readResult2.Value ?? "", "\\W", "")]; + } + + + + + + /// /// 写入箱式线任务 /// @@ -40,25 +90,9 @@ public class ConveyOperation return writeResult.Message ?? "写入失败,未知原因"; } - /// - /// 写入输送机心跳 - /// - /// - public string WriteHeartBeat() - { - if (!CommonData.IsConnectPlc || CommonTool.Siemens == default) - { - // 未连接PLC - return "PLC尚未连接。"; - } - var (writeResult, _) = CommonTool.Siemens.WritePlcWhithName($"箱式线写心跳", (short)0); - if (writeResult.Success) - { - return string.Empty; - } - return writeResult.Message ?? "写入失败,未知原因"; - } + + /// /// 获取箱式线出库站台状态 /// diff --git a/WcsMain/PlcOperation/Entity/Stacker/StackerPlcTask.cs b/WcsMain/PlcOperation/Entity/Stacker/StackerPlcTask.cs index 12cd2ca..ea05c98 100644 --- a/WcsMain/PlcOperation/Entity/Stacker/StackerPlcTask.cs +++ b/WcsMain/PlcOperation/Entity/Stacker/StackerPlcTask.cs @@ -1,4 +1,5 @@ -using WcsMain.Enum.TaskEnum; +using System.Collections; +using WcsMain.Enum.TaskEnum; namespace WcsMain.PlcOperation.Entity.Stacker; @@ -100,36 +101,45 @@ public class StackerPlcTask /// public int Code { get; set; } + + public override string ToString() + { + return $"PlcId:{PlcId},{GetQueue}排{GetLine}列{GetLayer}层{GetDeep}深 --> {SetQueue}排{SetLine}列{SetLayer}层{SetDeep}深"; + } + + /// /// 默认的直接从入口搬出去的任务 /// /// /// + /// /// /// - public static StackerPlcTask DefaultOutTask(int plcId, int stackerId, int vehicleNo = 999999999) + public static StackerPlcTask DefaultErrTask(int plcId, int stackerId, int forkId, int vehicleNo = 999999999) { - StackerPlcTask noTaskStackerTask = new() + StackerPlcTask stackerTask = new() { + StackerId = stackerId, PlcId = plcId, - TaskType = Convert.ToInt16(TaskTypeEnum.inTask), + TaskType = Convert.ToInt16(WcsTaskTypeEnum.moveTask), GetStand = 0, InTunnelId = Convert.ToInt16(stackerId), OutTunnelId = Convert.ToInt16(stackerId), SetStand = 0, - GetQueue = 1, - GetLine = 46, + GetQueue = 2, + GetLine = Convert.ToInt16(forkId), GetLayer = 1, GetDeep = 1, SetQueue = 2, - SetLine = 46, - SetLayer = 1, + SetLine = Convert.ToInt16(forkId), + SetLayer = 2, SetDeep = 1, Size = 0, Weight = 0, - Code = vehicleNo + Code = vehicleNo, }; - return noTaskStackerTask; + return stackerTask; } } \ No newline at end of file diff --git a/WcsMain/PlcOperation/Stacker/StackerOperation.cs b/WcsMain/PlcOperation/Stacker/StackerOperation.cs index 0cdef34..98aadb0 100644 --- a/WcsMain/PlcOperation/Stacker/StackerOperation.cs +++ b/WcsMain/PlcOperation/Stacker/StackerOperation.cs @@ -21,11 +21,13 @@ public class StackerOperation /// 判断堆垛机状态,若是等待(二次申请)状态则带出当前的PlcId /// /// - /// + /// + /// /// - public StackerUseStatusEnum StackerCanUse(int? stackerId, out int plcId) + public StackerUseStatusEnum StackerCanUse(int? stackerId, out int plcId1, out int spare1) { - plcId = 0; + plcId1 = 0; + spare1 = 0; if (stackerId == default) return StackerUseStatusEnum.BusyOrErr; /* 获取堆垛机状态 */ var (errMsg, stackerInfo) = GetStackerInfo((int)stackerId!); @@ -39,7 +41,8 @@ public class StackerOperation && stackerInfo.ErrCode == 0; if(waitTask) { - plcId = stackerInfo.PlcId; + plcId1 = stackerInfo.PlcId; + spare1 = stackerInfo.Spare1; return StackerUseStatusEnum.waitTask; } return StackerUseStatusEnum.BusyOrErr; @@ -72,7 +75,7 @@ public class StackerOperation { return ("设备尚未连接", default); // 未连接PLC } - var readResult = CommonTool.Siemens.ReadByteWithName($"堆垛机状态反馈{stackerId}", 24); + var readResult = CommonTool.Siemens.ReadByteWithName($"堆垛机状态反馈{stackerId}", 50); if (!readResult.Success || readResult.Value == default) { return (readResult.Message, default); // 读取失败 @@ -95,7 +98,7 @@ public class StackerOperation // ForkCount = Convert.ToInt32(CommonTool.Siemens.Trans(data, 32)), // 货叉动作次数 // SubmitPlcId = Convert.ToInt32(CommonTool.Siemens.Trans(data, 36)), // 提交的任务 // DeletePlcId = Convert.ToInt32(CommonTool.Siemens.Trans(data, 40)), // 删除的任务 - // Spare1 = Convert.ToInt32(CommonTool.Siemens.Trans(data, 44)), // 备用1 + Spare1 = Convert.ToInt32(CommonTool.Siemens.Trans(data, 44)), // 备用1 // Spare2 = Convert.ToInt16(CommonTool.Siemens.Trans(data, 48)), // 备用2 }; return (string.Empty, stackerInfo); @@ -182,31 +185,26 @@ public class StackerOperation /// /// /// - public string WriteTaskConfirm(int stackerId) + public string WriteTaskConfirm(int? stackerId) { - if (!CommonData.IsConnectPlc || CommonTool.Siemens == default) - { - // 未连接PLC - return "PLC尚未连接。"; - } + if (!CommonData.IsConnectPlc || CommonTool.Siemens == default) return "PLC尚未连接。"; var (writeResult, _) = CommonTool.Siemens.WritePlcWhithName($"堆垛机写任务确认{stackerId}", (short)1); - if (writeResult.Success) - { - return string.Empty; - } - return writeResult.Message ?? "写入失败"; + return writeResult.Success ? string.Empty : writeResult.Message ?? "写入失败"; } /// /// 写入堆垛机任务,返回错误信息 /// - /// + /// 任务数据 + /// 货叉编号 /// - public string WriteTask(StackerPlcTask task) + public string WriteTask(StackerPlcTask task, int forkId = 0) { if (!CommonData.IsConnectPlc || CommonTool.Siemens == default) return "PLC尚未连接。"; // 未连接PLC - var (writeResult, _) = CommonTool.Siemens.WritePlcWhithName($"堆垛机写任务{task.StackerId}", + var dbAddressName = $"堆垛机写任务{task.StackerId}"; + if(forkId != 0) { dbAddressName += $"-{forkId}"; } + var (writeResult, _) = CommonTool.Siemens.WritePlcWhithName(dbAddressName, task.PlcId!, task.TaskType!, task.GetStand!, @@ -225,7 +223,7 @@ public class StackerOperation task.Weight!, task.Code ); - return writeResult.Success ? string.Empty : writeResult.Message ?? "写入失败"; + return writeResult.Success ? string.Empty : writeResult.Message ?? "写入失败,未知原因"; } diff --git a/WcsMain/StartAction/HostService.cs b/WcsMain/StartAction/HostService.cs index 87b174d..e4b5e09 100644 --- a/WcsMain/StartAction/HostService.cs +++ b/WcsMain/StartAction/HostService.cs @@ -23,6 +23,7 @@ public class HostService(ServiceStart serverStart) : IHostedLifecycleService { return Task.Run(() => { + _serverStart.LoadingData(); // 加载必要参数 string? loadingResult = LoadingRunningData.GetResult(); if (!string.IsNullOrEmpty(loadingResult)) { diff --git a/WcsMain/StartAction/ServiceStart.cs b/WcsMain/StartAction/ServiceStart.cs index d1928a9..669a394 100644 --- a/WcsMain/StartAction/ServiceStart.cs +++ b/WcsMain/StartAction/ServiceStart.cs @@ -28,6 +28,23 @@ public class ServiceStart(WcsCirculation wcsCirculation, ConnectPLCs connectPLCs private readonly ConnectPlcServe _connectPlcServe = connectPlcServe; private static string _errMsg = string.Empty; + + /// + /// 加载必要参数 + /// + public void LoadingData() + { + LoadingConfig(); // 加载数据库中的配置项 (config 表) + LoadingStackerData(); // 加载数据库中的堆垛机信息 + LoadingLocationData(); // 加载数据库中的库位信息 + + if (!string.IsNullOrEmpty(_errMsg)) + { + ConsoleLog.Error($"【异常】启动加载运行文件出错,WCS功能受到限制,您可以检查网络连接后重新启动或者联系我们,参考信息:{_errMsg}"); + return; + } + } + /// /// 启动 ——-- 主方法 /// @@ -35,15 +52,7 @@ public class ServiceStart(WcsCirculation wcsCirculation, ConnectPLCs connectPLCs { /* 指定线程池规格 */ ThreadPool.SetMinThreads(30, 10); - LoadingConfig(); // 加载数据库中的配置项 (config 表) - LoadingStackerData(); // 加载数据库中的堆垛机信息 - LoadingLocationData(); // 加载数据库中的库位信息 - - if(!string.IsNullOrEmpty(_errMsg)) - { - ConsoleLog.Error($"【异常】启动加载运行文件出错,WCS功能受到限制,您可以检查网络连接后重新启动或者联系我们,参考信息:{_errMsg}"); - return; - } + CreatePlcClient(); // 连接 PLC 客户端 CreateSocketClient(); // Socket客户端 diff --git a/WcsMain/WcsMain.csproj b/WcsMain/WcsMain.csproj index b6c2598..9c233f6 100644 --- a/WcsMain/WcsMain.csproj +++ b/WcsMain/WcsMain.csproj @@ -45,7 +45,9 @@ - + + + diff --git a/WcsMain/WcsMain.csproj.user b/WcsMain/WcsMain.csproj.user new file mode 100644 index 0000000..0ab25a4 --- /dev/null +++ b/WcsMain/WcsMain.csproj.user @@ -0,0 +1,6 @@ + + + + FolderProfile + + \ No newline at end of file diff --git a/WcsMain/appsettings.json b/WcsMain/appsettings.json index 9fb58e0..33113d0 100644 --- a/WcsMain/appsettings.json +++ b/WcsMain/appsettings.json @@ -8,13 +8,14 @@ "AllowedHosts": "*", "Settings": { "DBMysql": "server=192.168.103.200;port=3306;user=developer;password=developer;database=wcs_main;", - "DBMysqlLocal": "server=192.168.234.128;port=3306;user=developer;password=developer;database=wcs_main;", + "DBMysqlLocal": "server=192.168.234.128;port=3306;user=developer;password=developer;database=wcs_suzhoukt;", "DBMssql": "Data Source=192.168.142.131;Initial Catalog=wcs;User Id=sa;Password=Sa123;", "DBMssqlLocal": "Data Source=192.168.142.131;Initial Catalog=wcs_stacker;User Id=sa;Password=Sa123;", "ApplicationConfig": { - "ApiOnly": true + "ApiOnly": true, + "Language": "zh-CN" }, "UseUrls": [ "http://*:890" ]