New file |
| | |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | |
| | | namespace WCS.Application.Entity; |
| | | |
| | | /// <summary> |
| | | /// 四向车任务明细表 |
| | | /// </summary> |
| | | [SugarTable("WCSCarTasks", "四向车任务明细表")] |
| | | public class WcsCarTasks : EntityBaseData |
| | | { |
| | | /// <summary> |
| | | /// 任务号 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "TaskNo", ColumnDescription = "任务号", Length = 20)] |
| | | public string? TaskNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 小车任务号 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "CarTaskNo", ColumnDescription = "小车任务号")] |
| | | public int? CarTaskNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 前置小车任务ID 可以有多个 用;隔开 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "PreId", ColumnDescription = "前置小车任务ID", Length = 200)] |
| | | public string? PreId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 交互路径 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "ExecutionPath", ColumnDescription = "交互路径", Length = 200)] |
| | | public string? ExecutionPath { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 所有路径 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "Path", ColumnDescription = "所有路径", Length = 20)] |
| | | public string? Path { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 小车编号 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "CarNo", ColumnDescription = "小车编号")] |
| | | public string? CarNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 状态 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "Status", ColumnDescription = "状态")] |
| | | public TaskStatusEnum? Status { get; set; } |
| | | |
| | | } |
| | |
| | | [SugarColumn(ColumnName = "PalletNo", ColumnDescription = "托盘号", Length = 32)] |
| | | public string? PalletNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 通道口1 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "AisleOne", ColumnDescription = "通道口1", Length = 32)] |
| | | public string? AisleOne { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 通道口2 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "AisleTwo", ColumnDescription = "通道口2", Length = 32)] |
| | | public string? AisleTwo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 储位类型 1储位 0通道 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "Make", ColumnDescription = "储位类型", Length = 32)] |
| | | public string? Make { get; set; } |
| | | } |
| | |
| | | public string? Origin { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 仓库号 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "WareHouseNo", ColumnDescription = "仓库号", Length = 20)] |
| | | public string? WareHouseNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 起始位置 |
| | | /// </summary> |
| | | [SugarColumn(ColumnName = "StartLocate", ColumnDescription = "起始位置", Length = 20)] |
| | |
| | | /// </summary> |
| | | [Description("任务取消")] |
| | | Cancell = 4, |
| | | /// <summary> |
| | | /// 任务取消 |
| | | /// </summary> |
| | | [Description("手动完成任务")] |
| | | CompleteHand = 5, |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | { |
| | | case "end"://任务完成 |
| | | { |
| | | PLCService.AGVStatus = false; |
| | | //把成品货物拉到缓存工位后,下一个任务把托盘拉去提升机或者缓存区(优先级低一点) |
| | | if (modTask.EndLocate == AGVStaionEnum.C1.ToString() || modTask.EndLocate == AGVStaionEnum.C2.ToString()) |
| | | { |
| | | var strEndLocate = ""; |
| | | Enum.TryParse(await _sysConfigService.GetConfigValue<string>("workshop_Trend"), out WorkshopEnum workshop); |
| | | if (workshop == WorkshopEnum.Storage) |
| | | strEndLocate = AGVStaionEnum.A1.ToString(); |
| | | else |
| | | strEndLocate = AGVStorageUtil.GetProductInStorage(); |
| | | WcsTask modInsertTask = new WcsTask() |
| | | { |
| | | TaskNo = _taskService.GetTaskCode(), |
| | | TaskType = TaskTypeEnum.Move, |
| | | Type = PLCTypeEnum.AGV, |
| | | StartLocate = modTask.EndLocate,//缓存区 |
| | | EndLocate = strEndLocate, |
| | | PalletNo = modTask.PalletNo, |
| | | Status = TaskStatusEnum.Wait, |
| | | Levels = 6, |
| | | Origin = "WCS" |
| | | }; |
| | | await _db.Insertable(modInsertTask).ExecuteCommandAsync(); |
| | | HubUtil.PublicTask(modInsertTask.Adapt<WcsTaskOutput>()); |
| | | } |
| | | |
| | | //货品拉到电梯口需要向输送线写入100 |
| | | //货品拉到密集库入库口需要向输送线写入值 |
| | | if (modTask.EndLocate == AGVStaionEnum.A1.ToString()) |
| | | { |
| | | var modPlc = PLCTaskAction.plcs.FirstOrDefault(s => s.Text == "输送线"); |
| | |
| | | Log.Error("C口申请入库写入值100失败"); |
| | | } |
| | | } |
| | | //放入拆托机后,向拆托机写入完成信号 |
| | | if (modTask.EndLocate == AGVStaionEnum.D1.ToString()) |
| | | { |
| | | var modPlc = PLCTaskAction.plcs.FirstOrDefault(s => s.Type == PLCTypeEnum.PalletMachine); |
| | | var modConn = new PLCUtil(modPlc); |
| | | var modDevice = PLCTaskAction.plcDevices.FirstOrDefault(s => s.Text == "拆托机"); |
| | | var res = modConn.SetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.WcsPos, "0"); |
| | | var modTaskMonitorPLC = new WcsTaskMonitor() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | PlcId = modDevice.PlcId, |
| | | PlcName = modDevice.WcsPos, |
| | | Status = TaskStatusEnum.Doing, |
| | | InteractiveMsg = $"向{modDevice.Text}写入完成信号0,结果{res.IsSucceed}", |
| | | }; |
| | | _db.Insertable(modTaskMonitorPLC).ExecuteCommand(); |
| | | HubUtil.PublicTaskMonitor(modTaskMonitorPLC.Adapt<WcsTaskMonitorOutput>()); |
| | | modConn.Close(); |
| | | } |
| | | //if (modTask.EndLocate == AGVStaionEnum.B1.ToString() || modTask.EndLocate == AGVStaionEnum.B2.ToString()) |
| | | //{ |
| | | // var num = modTask.EndLocate.Substring(1, 1); |
| | | // var modPlc = PLCTaskAction.plcs.FirstOrDefault(s => s.Type == PLCTypeEnum.RobotPalletizer && s.WareHouseNo == num); |
| | | // var modConn = new PLCUtil(modPlc); |
| | | // var modDevice = PLCTaskAction.plcDevices.FirstOrDefault(s => s.PlcId == modPlc.Id && s.Level == DeviceLevelEnum.DB); |
| | | // var res = modConn.SetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.PlcPos, "0"); |
| | | // var modTaskMonitorPLC = new WcsTaskMonitor() |
| | | // { |
| | | // TaskNo = modTask.TaskNo, |
| | | // PlcId = modDevice.PlcId, |
| | | // PlcName = modDevice.PlcPos, |
| | | // Status = TaskStatusEnum.Doing, |
| | | // InteractiveMsg = $"向{modDevice.Text}写入放托完成信号,结果{res.IsSucceed}", |
| | | // }; |
| | | // _db.Insertable(modTaskMonitorPLC).ExecuteCommand(); |
| | | // HubUtil.PublicTaskMonitor(modTaskMonitorPLC.Adapt<WcsTaskMonitorOutput>()); |
| | | // modConn.Close(); |
| | | //} |
| | | if (modTask.EndLocate == AGVStaionEnum.F1.ToString()) |
| | | { |
| | | await _sysConfigService.UpdateConfigValue("cache_Materal", true); |
| | | } |
| | | if (modTask.StartLocate == AGVStaionEnum.F1.ToString()) |
| | | { |
| | | await _sysConfigService.UpdateConfigValue("cache_Materal", false); |
| | | } |
| | | modTask.Status = TaskStatusEnum.Complete; |
| | | modTask.FinishDate = DateTime.Now; |
| | | await _db.Updateable(modTask).ExecuteCommandAsync(); |
| | |
| | | await _db.Insertable(modTaskMonitor).ExecuteCommandAsync(); |
| | | HubUtil.PublicTask(modTask.Adapt<WcsTaskOutput>()); |
| | | HubUtil.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | //反馈WMS任务完成 |
| | | if (modTask.Origin == "WMS") |
| | | { |
| | | HttpService httpService = new HttpService(); |
| | |
| | | inputs.TaskStatus = "2"; |
| | | var modResponseTask = httpService.RequestTask(inputs).Result; |
| | | } |
| | | //托盘进出缓存区 需要记录 |
| | | if (new[] { "Z", "C" }.Contains(modTask.EndLocate.Substring(0, 1))) |
| | | { |
| | | await _sysConfigService.UpdateConfigValue($"cache_{modTask.EndLocate}", true); |
| | | } |
| | | if (new[] { "Z", "C" }.Contains(modTask.StartLocate.Substring(0, 1))) |
| | | { |
| | | await _sysConfigService.UpdateConfigValue($"cache_{modTask.StartLocate}", false); |
| | | } |
| | | |
| | | } |
| | | break; |
| | | case "outbin"://走出储位 |
| | | { |
| | | //取空托完成后,向拆托机写入完成信号 |
| | | if (modTask.StartLocate == AGVStaionEnum.D1.ToString() && (modTask.EndLocate == AGVStaionEnum.B1.ToString() || modTask.EndLocate == AGVStaionEnum.B2.ToString())) |
| | | //反馈WMS任务取货完成、WMS判断是平库储位就更新储位状态 |
| | | if (modTask.Origin == "WMS") |
| | | { |
| | | var modPlc = PLCTaskAction.plcs.FirstOrDefault(s => s.Type == PLCTypeEnum.PalletMachine); |
| | | var modConn = new PLCUtil(modPlc); |
| | | var modDevice = PLCTaskAction.plcDevices.FirstOrDefault(s => s.Text == "拆托机"); |
| | | var modPos = PLCTaskAction.plcPositions.FirstOrDefault(s => s.DeviceId == modDevice.Id && s.Text == "取托信号"); |
| | | var res = modConn.SetPlcDBValue(modPos.PosType, modDevice.DbNumber, modPos.PlcPos, "0"); |
| | | WcsTaskMonitor modTaskMonitor = new WcsTaskMonitor() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | PlcId = modDevice.PlcId, |
| | | PlcName = modPos.PlcPos, |
| | | Status = TaskStatusEnum.Doing, |
| | | InteractiveMsg = $"向{modDevice.Text}写入取托信号0,结果{res.IsSucceed}", |
| | | }; |
| | | _db.Insertable(modTaskMonitor).ExecuteCommand(); |
| | | HubUtil.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | modPos = PLCTaskAction.plcPositions.FirstOrDefault(s => s.DeviceId == modDevice.Id && s.Text == "取托完成"); |
| | | res = modConn.SetPlcDBValue(modPos.PosType, modDevice.DbNumber, modPos.PlcPos, "1"); |
| | | modTaskMonitor = new WcsTaskMonitor() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | PlcId = modDevice.PlcId, |
| | | PlcName = modPos.PlcPos, |
| | | Status = TaskStatusEnum.Doing, |
| | | InteractiveMsg = $"向{modDevice.Text}写入取托完成1,结果{res.IsSucceed}", |
| | | }; |
| | | _db.Insertable(modTaskMonitor).ExecuteCommand(); |
| | | HubUtil.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | modConn.Close(); |
| | | HttpService httpService = new HttpService(); |
| | | var inputs = new TaskRequestWMS(); |
| | | inputs.TaskNo = modTask.TaskNo; |
| | | inputs.PalletNo = modTask.PalletNo; |
| | | inputs.TaskType = "2"; |
| | | inputs.TaskStatus = "2"; |
| | | var modResponseTask = httpService.RequestTaskQh(inputs).Result; |
| | | } |
| | | //todo:去码垛工位取货完成后,需要写PLC信号开启光幕 |
| | | if ((modTask.StartLocate == AGVStaionEnum.B1.ToString() || modTask.StartLocate == AGVStaionEnum.B2.ToString()) |
| | | && (modTask.EndLocate == AGVStaionEnum.C1.ToString() || modTask.EndLocate == AGVStaionEnum.C2.ToString())) |
| | | { |
| | | var num = modTask.StartLocate.Substring(1, 1); |
| | | var modPlc = PLCTaskAction.plcs.FirstOrDefault(s => s.Type == PLCTypeEnum.RobotPalletizer && s.WareHouseNo == num); |
| | | var modConn = new PLCUtil(modPlc); |
| | | var modDevice = PLCTaskAction.plcDevices.FirstOrDefault(s => s.PlcId == modPlc.Id && s.Level == DeviceLevelEnum.DB); |
| | | var modPos = PLCTaskAction.plcPositions.FirstOrDefault(s => s.DeviceId == modDevice.Id && s.Text == "开启光幕"); |
| | | var res = modConn.SetPlcDBValue(modPos.PosType, modDevice.DbNumber, modPos.PlcPos, "1"); |
| | | var modTaskMonitor = new WcsTaskMonitor() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | PlcId = modDevice.PlcId, |
| | | PlcName = modPos.PlcPos, |
| | | Status = TaskStatusEnum.Doing, |
| | | InteractiveMsg = $"向{modDevice.Text}写入开启光幕1,结果{res.IsSucceed}", |
| | | }; |
| | | _db.Insertable(modTaskMonitor).ExecuteCommand(); |
| | | HubUtil.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | //写入光幕0 |
| | | Task.Run(() => |
| | | { |
| | | var modDevice = PLCTaskAction.plcDevices.FirstOrDefault(s => s.PlcId == modPlc.Id && s.Level == DeviceLevelEnum.DB); |
| | | var modPos = PLCTaskAction.plcPositions.FirstOrDefault(s => s.DeviceId == modDevice.Id && s.Text == "开启光幕"); |
| | | Thread.Sleep(1000); |
| | | var res = modConn.SetPlcDBValue(modPos.PosType, modDevice.DbNumber, modPos.PlcPos, "0"); |
| | | var modTaskMonitor = new WcsTaskMonitor() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | PlcId = modDevice.PlcId, |
| | | PlcName = modPos.PlcPos, |
| | | Status = TaskStatusEnum.Doing, |
| | | InteractiveMsg = $"向{modDevice.Text}写入开启光幕0,结果{res.IsSucceed}", |
| | | }; |
| | | _db.Insertable(modTaskMonitor).ExecuteCommand(); |
| | | HubUtil.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | }); |
| | | |
| | | //向码垛机器人写取托完成信号 |
| | | modDevice = PLCTaskAction.plcDevices.FirstOrDefault(s => s.PlcId == modPlc.Id && s.Level == DeviceLevelEnum.Station); |
| | | res = modConn.SetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.PlcPos, "0"); |
| | | modTaskMonitor = new WcsTaskMonitor() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | PlcId = modDevice.PlcId, |
| | | PlcName = modPos.PlcPos, |
| | | Status = TaskStatusEnum.Doing, |
| | | InteractiveMsg = $"向{modDevice.Text}写入取托完成信号,结果{res.IsSucceed}", |
| | | }; |
| | | _db.Insertable(modTaskMonitor).ExecuteCommand(); |
| | | HubUtil.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | modConn.Close(); |
| | | } |
| | | //起始工位是提升机取货工位 |
| | | if (modTask.StartLocate == AGVStaionEnum.A1.ToString()) |
| | | { |
| | | var modPlc = PLCTaskAction.plcs.FirstOrDefault(s => s.Type == PLCTypeEnum.ConveyorLine); |
| | | var modConn = new PLCUtil(modPlc); |
| | | var modDevice = PLCTaskAction.plcDevices.FirstOrDefault(s => s.PlcId == modPlc.Id && s.Text == "C口"); |
| | | var modPos = PLCTaskAction.plcPositions.FirstOrDefault(s => s.DeviceId == modDevice.Id && s.Text == "取走确认"); |
| | | var res = modConn.SetPlcDBValue(modPos.PosType, modDevice.DbNumber, modPos.PlcPos, "1"); |
| | | Log.Information($"向plc{modDevice.PlcId}写入1-取走确认,结果:{res.ToJson()}"); |
| | | var modTaskMonitor = new WcsTaskMonitor() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | PlcId = modDevice.PlcId, |
| | | PlcName = modPos.PlcPos, |
| | | Status = TaskStatusEnum.Doing, |
| | | InteractiveMsg = $"向{modDevice.Text}写入取走确认,结果{res.IsSucceed}", |
| | | }; |
| | | _db.Insertable(modTaskMonitor).ExecuteCommand(); |
| | | HubUtil.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | } |
| | | } |
| | | break; |
| | | case "apply"://放货申请 |
| | |
| | | HubUtil.PublicTaskMonitor(taskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | } |
| | | break; |
| | | case "cancel"://任务结束 |
| | | case "cancel"://任务取消 |
| | | { |
| | | PLCService.AGVStatus = false; |
| | | modTask.IsSuccess = TaskSuccessEnum.Fail; |
| | |
| | | using DocumentFormat.OpenXml.Spreadsheet; |
| | | using Furion.Logging; |
| | | using IoTClient; |
| | | using NewLife.Reflection; |
| | | using WCS.Application.Entity; |
| | | using WCS.Application.Util; |
| | | |
| | | namespace WCS.Application; |
| | |
| | | sysCacheService.HashAddOrUpdate("AlarmInfo_Car", plcConn.PlcIP, -1); |
| | | var modPosTaskStatus = modDevice.listStation.FirstOrDefault(s => s.Text == "任务状态"); |
| | | var (resultTaskStatus, valueTaskStatus) = plcConn.GetDBValue(modPosTaskStatus.PosType, modPosTaskStatus.PlcPos); |
| | | //判断读取任务状态是否成功 并且任务状态是1 0:未空闲,1:空闲,2:异常 3:充电中 |
| | | |
| | | WcsCarTasks carTask2 = null;//null 新任务 not null 路径2任务 |
| | | if (resultTaskStatus.IsSucceed && valueTaskStatus == 1) |
| | | { |
| | | var modPosTask = modDevice.listStation.FirstOrDefault(s => s.Text == "任务号"); |
| | | var (resultTask, valueTask) = plcConn.GetDBValue(modPosTask.PosType, modPosTask.PlcPos); |
| | | string valueTaskStr = Convert.ToString(valueTask); |
| | | string strNo = ""; |
| | | var boNo = dicTaskNo.TryGetValue(modDevice.Id.ToString(), out strNo); |
| | | if (valueTaskStr != "0" && strNo != valueTaskStr) |
| | | //读取任务号 |
| | | int valueTaskStr = Convert.ToInt32(valueTask); |
| | | //获取任务信息 根据 任务号、小车编号 |
| | | var carTask = _db.Queryable<WcsCarTasks>().First(m=>m.IsDelete == false && m.Status == TaskStatusEnum.Doing && m.CarTaskNo == valueTaskStr && m.CarNo == modDevice.PlcIdIP); |
| | | if (carTask == null) |
| | | { |
| | | var modFinshTask = _db.Queryable<WcsTask>().First(s => s.Status == TaskStatusEnum.Doing && s.Type == PLCTypeEnum.ShuttleCar && s.Id.ToString().EndsWith(valueTaskStr)); |
| | | if (modFinshTask != null && modFinshTask.Status != TaskStatusEnum.Complete) |
| | | return;//没有找到任务 |
| | | } |
| | | |
| | | var modFinshTask = _db.Queryable<WcsTask>().First(s => s.Status == TaskStatusEnum.Doing && s.Type == PLCTypeEnum.ShuttleCar && s.TaskNo == carTask.TaskNo); |
| | | if (modFinshTask == null) |
| | | { |
| | | //12.4出库改成调用WMS完成任务接口;WMS操控PDA托盘下架后,WCS任务状态才完成(避免小车继续跑) |
| | | if (modFinshTask.TaskType == TaskTypeEnum.In) |
| | | return;//没有查询到总任务 |
| | | } |
| | | //获取路径2的任务 下发 |
| | | carTask2 = _db.Queryable<WcsCarTasks>().Where(m => m.IsDelete == false && m.TaskNo == carTask.TaskNo && m.CarNo == modDevice.PlcIdIP && m.PreId.Contains(carTask.Id.ToString())).OrderBy(m=>m.CreateTime).First(); |
| | | |
| | | if (carTask != null && carTask.Status != TaskStatusEnum.Complete) |
| | | { |
| | | modFinshTask.Status = TaskStatusEnum.Complete; |
| | | modFinshTask.FinishDate = DateTime.Now; |
| | | _db.Updateable(modFinshTask).ExecuteCommand(); |
| | | carTask.Status = TaskStatusEnum.Complete; |
| | | carTask.UpdateTime = DateTime.Now; |
| | | _db.Updateable(carTask).ExecuteCommand(); |
| | | //添加任务明细 |
| | | var taskMonitor = new WcsTaskMonitor() |
| | | { |
| | | TaskNo = modFinshTask.TaskNo, |
| | | TaskNo = carTask.TaskNo, |
| | | PlcName = modDevice.Text, |
| | | InteractiveMsg = $"穿梭车反馈任务完成" |
| | | }; |
| | | _db.Insertable(taskMonitor).ExecuteCommand(); |
| | | //下发任务日志 |
| | | HubUtil.PublicTask(modFinshTask.Adapt<WcsTaskOutput>()); |
| | | |
| | | HubUtil.PublicTaskMonitor(taskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | } |
| | | HttpService httpService = new HttpService(); |
| | | var requestMode = new TaskRequestWMS() |
| | | //没有路径2的任务 变更总任务信息 |
| | | if (carTask2 == null && modFinshTask != null && modFinshTask.Status != TaskStatusEnum.Complete) |
| | | { |
| | | TaskNo = modFinshTask.TaskNo, |
| | | PalletNo = modFinshTask.PalletNo, |
| | | TaskType = ((int)modFinshTask.TaskType).ToString(), |
| | | TaskStatus = ((int)TaskStatusEnum.Complete).ToString() |
| | | }; |
| | | var modResponseTask = httpService.RequestTask(requestMode).Result; |
| | | modFinshTask.IsSuccess = TaskSuccessEnum.Success; |
| | | modFinshTask.Status = TaskStatusEnum.Complete; |
| | | modFinshTask.FinishDate = DateTime.Now; |
| | | _db.Updateable(modFinshTask).ExecuteCommand(); |
| | | |
| | | if (dicTaskNo.ContainsKey(modDevice.Id.ToString())) |
| | | dicTaskNo.Remove(modDevice.Id.ToString()); |
| | | dicTaskNo.Add(modDevice.Id.ToString(), valueTaskStr); |
| | | //反馈WMS系统 任务完成 |
| | | //HttpService httpService = new HttpService(); |
| | | //var requestMode = new TaskRequestWMS() |
| | | //{ |
| | | // TaskNo = modFinshTask.TaskNo, |
| | | // PalletNo = modFinshTask.PalletNo, |
| | | // TaskType = ((int)modFinshTask.TaskType).ToString(), |
| | | // TaskStatus = ((int)TaskStatusEnum.Complete).ToString() |
| | | //}; |
| | | //var modResponseTask = httpService.RequestTask(requestMode).Result; |
| | | //modFinshTask.IsSuccess = TaskSuccessEnum.Success; |
| | | //_db.Updateable(modFinshTask).ExecuteCommand(); |
| | | HubUtil.PublicTask(modFinshTask.Adapt<WcsTaskOutput>()); |
| | | |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | var modStationX = modDevice.listStation.FirstOrDefault(s => s.Text == "四向车位置(X)"); |
| | |
| | | var (resultz, valuez) = plcConn.GetDBValue(modStationZ.PosType, modStationZ.PlcPos); |
| | | |
| | | // 获取任务信息 |
| | | var stationStart = ((int)valuex).ToString().PadLeft(2, '0'); |
| | | var stationEnd = ((int)valuez).ToString().PadLeft(2, '0') + "01"; |
| | | var listTask = _db.Queryable<WcsTask>().OrderBy(m => m.Levels, OrderByType.Asc).OrderBy(m => m.CreateTime) |
| | | .Where(s => s.Status <= TaskStatusEnum.Doing && s.Type == PLCTypeEnum.ShuttleCar |
| | | && s.StartLocate.StartsWith(stationStart) && s.StartLocate.EndsWith(stationEnd)).ToList(); |
| | | var modTask = listTask.FirstOrDefault(); |
| | | //return; |
| | | if (modTask == null) |
| | | return; |
| | | if (modTask.StartLocate == modTask.EndLocate) |
| | | var modTask = _db.Queryable<WcsTask>().Where(s => s.Status <= TaskStatusEnum.Doing && s.Type == PLCTypeEnum.ShuttleCar).OrderBy(m => m.Levels, OrderByType.Asc).OrderBy(m => m.CreateTime).First(); |
| | | |
| | | //要下发路径2任务 |
| | | if (carTask2 == null) |
| | | { |
| | | modTask.Status = TaskStatusEnum.Doing; |
| | | _db.Updateable(modTask).ExecuteCommand(); |
| | | HubUtil.PublicTask(modTask.Adapt<WcsTaskOutput>()); |
| | | modTask = _db.Queryable<WcsTask>().First(s => s.IsDelete == false && s.TaskNo == carTask2.TaskNo && s.Status <= TaskStatusEnum.Doing && s.Type == PLCTypeEnum.ShuttleCar); |
| | | |
| | | carTask2= _db.Queryable<WcsCarTasks>().Where(m => m.IsDelete == false && m.TaskNo == modTask.TaskNo && m.CarNo == modDevice.PlcIdIP).OrderBy(m => m.CreateTime).First(); |
| | | } |
| | | |
| | | //return; |
| | | if (modTask == null || carTask2 == null) |
| | | { |
| | | return; |
| | | } |
| | | if (modTask.Status == TaskStatusEnum.Doing) |
| | | |
| | | if (carTask2.Status == TaskStatusEnum.Doing) |
| | | { |
| | | Thread.Sleep(3000); |
| | | return; |
| | | } |
| | | //判断当前任务是否还有前置任务未完成 |
| | | var preStrs = carTask2.PreId.Split(';'); |
| | | foreach (var preStr in preStrs) |
| | | { |
| | | if (string.IsNullOrWhiteSpace(preStr)) |
| | | { |
| | | continue; |
| | | } |
| | | var preId = long.Parse(preStr); |
| | | var CarTaskPre = _db.Queryable<WcsCarTasks>().First(m => m.Id == preId); |
| | | if (CarTaskPre.Status <= TaskStatusEnum.Doing) |
| | | { |
| | | return;//前置任务未完成 |
| | | } |
| | | } |
| | | //先复位 |
| | | var modRest = modDevice.listStation.FirstOrDefault(s => s.Text == "复位"); |
| | | plcConn.SetDBValue(modRest.PosType, modRest.PlcPos, "1"); |
| | | |
| | | List<Result> listResult = new List<Result>(); |
| | | //写入任务号Id的末4位,2个小车一起跑有极低概率重复 |
| | | //获取小车任务号 |
| | | var carTaskNo = FourWayCarUtil.GetTaskNo(); |
| | | |
| | | var modWriteTask = modDevice.listStation.FirstOrDefault(s => s.Text == "写入任务号"); |
| | | listResult.Add(plcConn.SetDBValue(modWriteTask.PosType, modWriteTask.PlcPos, modTask.Id.ToString().Substring(modTask.Id.ToString().Length - 4))); |
| | | listResult.Add(plcConn.SetDBValue(modWriteTask.PosType, modWriteTask.PlcPos, carTaskNo.ToString())); |
| | | |
| | | var modNodeX = modDevice.listStation.FirstOrDefault(s => s.Text == "节点坐标X"); |
| | | var modNodeY = modDevice.listStation.FirstOrDefault(s => s.Text == "节点坐标Y"); |
| | |
| | | listResult.Add(plcConn.SetDBValue(modNodeZ.PosType, posZ++.ToString(), Convert.ToString(valuez))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeStatus.PosType, posStatus++.ToString(), "3")); |
| | | } |
| | | //写入起始位置取货 |
| | | listResult.Add(plcConn.SetDBValue(modNodeX.PosType, posX++.ToString(), modTask.StartLocate.Substring(0, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeY.PosType, posY++.ToString(), modTask.StartLocate.Substring(2, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeZ.PosType, posZ++.ToString(), modTask.StartLocate.Substring(4, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeStatus.PosType, posStatus++.ToString(), "2")); |
| | | //写入目标位置放货 |
| | | listResult.Add(plcConn.SetDBValue(modNodeX.PosType, posX++.ToString(), modTask.EndLocate.Substring(0, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeY.PosType, posY++.ToString(), modTask.EndLocate.Substring(2, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeZ.PosType, posZ++.ToString(), modTask.EndLocate.Substring(4, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeStatus.PosType, posStatus++.ToString(), "3")); |
| | | string endPos = ""; |
| | | if (modTask.StartLocate.Substring(0, 2).ToInt() >= 14) |
| | | endPos = "01"; |
| | | else |
| | | endPos = "21"; |
| | | if (listTask.Count == 1 && modTask.EndLocate.Substring(2, 2) != endPos) |
| | | //交互路径 |
| | | var execuPath = carTask2.ExecutionPath.Split(';'); |
| | | foreach (var ePath in execuPath) |
| | | { |
| | | //如果后续没有任务,就让小车回到原位 |
| | | listResult.Add(plcConn.SetDBValue(modNodeX.PosType, posX++.ToString(), modTask.EndLocate.Substring(0, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeY.PosType, posY++.ToString(), endPos));//todo:这里位置待定 |
| | | listResult.Add(plcConn.SetDBValue(modNodeZ.PosType, posZ++.ToString(), modTask.EndLocate.Substring(4, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeStatus.PosType, posStatus++.ToString(), "3")); |
| | | if (string.IsNullOrWhiteSpace(ePath)) |
| | | { |
| | | continue; |
| | | } |
| | | //写入交互位置 |
| | | listResult.Add(plcConn.SetDBValue(modNodeX.PosType, posX++.ToString(), ePath.Substring(0, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeY.PosType, posY++.ToString(), ePath.Substring(2, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeZ.PosType, posZ++.ToString(), ePath.Substring(4, 2))); |
| | | listResult.Add(plcConn.SetDBValue(modNodeStatus.PosType, posStatus++.ToString(), ePath.Substring(5, 1))); |
| | | } |
| | | |
| | | //这里是把后面的坐标全写0(为了防止上次任务坐标没被覆盖) |
| | | while (posX <= 43097) |
| | | { |
| | |
| | | var result = plcConn.SetDBValue(modStart.PosType, modStart.PlcPos, "1"); |
| | | if (result.IsSucceed) |
| | | { |
| | | modTask.Status = TaskStatusEnum.Doing; |
| | | _db.Updateable(modTask).ExecuteCommand(); |
| | | carTask2.Status = TaskStatusEnum.Doing; |
| | | carTask2.CarTaskNo = carTaskNo; |
| | | _db.Updateable(carTask2).ExecuteCommand(); |
| | | var taskMonitor = new WcsTaskMonitor() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | TaskNo = carTask2.TaskNo, |
| | | PlcName = modDevice.Text, |
| | | InteractiveMsg = $"向穿梭车下发任务{modTask.TaskNo}" |
| | | InteractiveMsg = $"向穿梭车下发任务{carTask2.TaskNo}" |
| | | }; |
| | | _db.Insertable(taskMonitor).ExecuteCommand(); |
| | | //下发任务日志 |
| | | HubUtil.PublicTask(modTask.Adapt<WcsTaskOutput>()); |
| | | HubUtil.PublicTaskMonitor(taskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | } |
| | | } |
| | |
| | | //小车状态异常 |
| | | var modPosTaskStatus = modDevice.listStation.FirstOrDefault(s => s.Text == "任务状态"); |
| | | var (resultTaskStatus, valueTaskStatus) = plcConn.GetDBValue(modPosTaskStatus.PosType, modPosTaskStatus.PlcPos); |
| | | { |
| | | |
| | | if (resultTaskStatus.IsSucceed && valueTaskStatus == 1) |
| | | { |
| | | var modPosTask = modDevice.listStation.FirstOrDefault(s => s.Text == "任务号"); |
| | |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | Console.WriteLine($"穿梭车{modDevice.PlcIdIP}异常"); |
| | | var modPosError = modDevice.listStation.FirstOrDefault(s => s.Text == "错误码"); |
| | | var (result, valueError) = plcConn.GetDBValue(modPosError.PosType, modPosError.PlcPos); |
| | |
| | | /// <param name="modDevice"></param> |
| | | private static void AGV(WcsDeviceDto modDevice) |
| | | { |
| | | if (AGVStatus) |
| | | { |
| | | return; |
| | | } |
| | | //这里找出来AGV待执行和正在执行的任务,如果有正在执行的任务就跳出,没有的话就按照优先级下发一个任务给AGV |
| | | var listTask = _db.Queryable<WcsTask>() |
| | | .Where(s => (s.Status == TaskStatusEnum.Doing || s.Status == TaskStatusEnum.Wait) && s.Type == PLCTypeEnum.AGV) |
| | | .OrderBy(s => s.Levels).ToList(); |
| | | |
| | | //这里找出来AGV待执行的任务、按照优先级下发一个任务给AGV |
| | | var listTask = _db.Queryable<WcsTask>().Where(s => (s.Status == TaskStatusEnum.Wait || s.Status == TaskStatusEnum.Doing) && s.Type == PLCTypeEnum.AGV).OrderBy(s => s.Levels).ToList(); |
| | | if (listTask.Count == 0) |
| | | return; |
| | | if (listTask.Any(s => s.Status == TaskStatusEnum.Doing)) |
| | | { |
| | | //有任务执行中 暂不下发任务 |
| | | return; |
| | | } |
| | | //WcsTask modTask = listTask.FirstOrDefault(); |
| | | |
| | | foreach (var modTask in listTask) |
| | | { |
| | | //入缓存区时,检验一下缓存区位置 |
| | | if (modTask.EndLocate.Substring(0, 1) == "Z") |
| | | if (_db.Queryable<WcsTask>().Any(s => s.EndLocate == modTask.EndLocate && s.IsDelete == false && s.Status == TaskStatusEnum.Doing && s.Type == PLCTypeEnum.AGV )) |
| | | { |
| | | if (modTask.EndLocate.Substring(1, 1) == "5") |
| | | { |
| | | string end = AGVStorageUtil.GetPalletInStorage(modTask.TaskNo); |
| | | if (end.IsNullOrEmpty()) |
| | | continue; |
| | | if (modTask.EndLocate != end) |
| | | { |
| | | modTask.EndLocate = end; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | string end = AGVStorageUtil.GetProductInStorage(modTask.TaskNo); |
| | | if (end.IsNullOrEmpty()) |
| | | continue; |
| | | if (modTask.EndLocate != end) |
| | | { |
| | | modTask.EndLocate = end; |
| | | } |
| | | } |
| | | } |
| | | //出缓存区时,检验一下缓存区位置 |
| | | if (modTask.StartLocate.Substring(0, 1) == "Z") |
| | | { |
| | | if (modTask.StartLocate.Substring(1, 1) == "5") |
| | | { |
| | | string start = AGVStorageUtil.GetPalletOutStorage(); |
| | | if (start.IsNullOrEmpty()) |
| | | continue; |
| | | if (modTask.StartLocate != start) |
| | | { |
| | | modTask.StartLocate = start; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | string start = AGVStorageUtil.GetProductOutStorage(); |
| | | if (start.IsNullOrEmpty()) |
| | | continue; |
| | | if (modTask.StartLocate != start) |
| | | { |
| | | modTask.StartLocate = start; |
| | | } |
| | | } |
| | | } |
| | | //把托盘拉到缓存位 先判断这里有没有 |
| | | if (modTask.EndLocate.Substring(0, 1) == "C") |
| | | { |
| | | var bo = _sysConfigService.GetConfigValue<bool>("cache_" + modTask.EndLocate).Result; |
| | | if (bo) |
| | | continue; |
| | | } |
| | | //如果拉托盘去成品工位,先检查有没有缺托 |
| | | if (modTask.StartLocate.Substring(0, 1) == "D" && modTask.EndLocate.Substring(0, 1) == "B") |
| | | { |
| | | //判断目标位置是拆托机的任务有没有,有就跳过拆托机到成品工位的任务 |
| | | if (listTask.Any(s => s.EndLocate.Substring(0, 1) == "D")) |
| | | continue; |
| | | } |
| | | //如果目的工位是原料仓,先检查原料仓储位状态 |
| | | if (modTask.EndLocate == AGVStaionEnum.F1.ToString()) |
| | | { |
| | | if (_sysConfigService.GetConfigValue<bool>("cache_Materal").Result) |
| | | continue; |
| | | } |
| | | //if (modTask.StartLocate == AGVStaionEnum.F1.ToString()) |
| | | //{ |
| | | // if (!_sysConfigService.GetConfigValue<bool>("cache_Materal").Result) |
| | | // continue; |
| | | //} |
| | | |
| | | //如果起始工位是拆托机 |
| | | if (modTask.StartLocate == AGVStaionEnum.D1.ToString()) |
| | | { |
| | | //获取叠拆托机IP |
| | | var modPlc = PLCTaskAction.plcs.FirstOrDefault(s => s.Type == PLCTypeEnum.PalletMachine); |
| | | //获取叠拆托机工位 |
| | | var modDevice2 = PLCTaskAction.plcDevices.FirstOrDefault(s => s.PlcId == modPlc.Id && s.Text == "拆托机"); |
| | | //打开连接 |
| | | var modConn = new PLCUtil(modPlc); |
| | | //是否允许取托信号 |
| | | var modPos = PLCTaskAction.plcPositions.FirstOrDefault(s => s.DeviceId == modDevice2.Id && s.Text == "允许取托"); |
| | | var (result, value) = modConn.GetPlcDBValue(modPos.PosType, modDevice2.DbNumber, modPos.PlcPos); |
| | | if (result.IsSucceed) |
| | | { |
| | | if (value) |
| | | { |
| | | //如果未完成就判断下取托信号有没有写,完成就叫小车取托盘 |
| | | modPos = PLCTaskAction.plcPositions.FirstOrDefault(s => s.DeviceId == modDevice2.Id && s.Text == "取托信号"); |
| | | (result, value) = modConn.GetPlcDBValue(modPos.PosType, modDevice2.DbNumber, modPos.PlcPos); |
| | | //如果没写入取托信号,就写入 |
| | | if (result.IsSucceed && !value) |
| | | { |
| | | result = modConn.SetPlcDBValue(modPos.PosType, modDevice2.DbNumber, modPos.PlcPos, "1"); |
| | | WcsTaskMonitor modTaskMonitor = new WcsTaskMonitor() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | PlcId = modDevice2.PlcId, |
| | | PlcName = modPos.PlcPos, |
| | | Status = TaskStatusEnum.Doing, |
| | | InteractiveMsg = $"向{modDevice2.Text}写入取托信号1,结果{result.IsSucceed}", |
| | | }; |
| | | _db.Insertable(modTaskMonitor).ExecuteCommand(); |
| | | HubUtil.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); |
| | | Thread.Sleep(5000); |
| | | } |
| | | break; |
| | | |
| | | } |
| | | else |
| | | { |
| | | //不允许取托,判断是否拆托机准备完成 |
| | | modPos = PLCTaskAction.plcPositions.FirstOrDefault(s => s.DeviceId == modDevice2.Id && s.Text == "准备完成"); |
| | | (result, value) = modConn.GetPlcDBValue(modPos.PosType, modDevice2.DbNumber, modPos.PlcPos); |
| | | if (!value) |
| | | { |
| | | continue; |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | Console.WriteLine("连接拆托机信号失败"); |
| | | continue; |
| | | } |
| | | } |
| | | //如果目的工位是提升机放货工位,先检查有没有托盘线的任务 |
| | | if (modTask.EndLocate == AGVStaionEnum.A1.ToString()) |
| | | { |
| | | if (_db.Queryable<WcsTask>().Where(s => s.Type == PLCTypeEnum.ConveyorLine && s.Status == TaskStatusEnum.Doing).Any()) |
| | | continue; |
| | | continue; // 有目的位置一致且正在执行的任务 |
| | | } |
| | | |
| | | //下发AGV任务 |
| | | AgvTaskInput input = new AgvTaskInput() |
| | | { |
| | | ReqCode = modTask.Id.ToString(), |
| | |
| | | using DocumentFormat.OpenXml.Bibliography; |
| | | using DocumentFormat.OpenXml.Drawing; |
| | | using DocumentFormat.OpenXml.Drawing.Charts; |
| | | using Elastic.Clients.Elasticsearch.Snapshot; |
| | | using Furion.Logging; |
| | | using Microsoft.AspNetCore.SignalR; |
| | | using NewLife.Serialization; |
| | | using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; |
| | | using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; |
| | | using OnceMi.AspNetCore.OSS; |
| | | using System; |
| | | using System.Drawing.Drawing2D; |
| | | using System.Net.NetworkInformation; |
| | | using WCS.Application.Entity; |
| | | using WCS.Application.Util; |
| | | using static SKIT.FlurlHttpClient.Wechat.Api.Models.CgibinExpressIntracityUpdateStoreRequest.Types; |
| | | //using WCS.Application.Util; |
| | | |
| | |
| | | private static readonly ISqlSugarClient _db = SqlSugarSetup.ITenant.GetConnectionScope(SqlSugarConst.MainConfigId); |
| | | private static readonly SysCacheService sysCacheService = App.GetRequiredService<SysCacheService>(); |
| | | private static readonly SysConfigService _sysConfigService = App.GetService<SysConfigService>(); |
| | | private static readonly WcsTaskService _taskService = App.GetService<WcsTaskService>(); |
| | | |
| | | private static List<WcsPlc> listPlc = new List<WcsPlc>(); |
| | | private static List<WcsDevice> listPlcDevice = new List<WcsDevice>(); |
| | |
| | | StartRead(); |
| | | ConnectionStatus(); |
| | | StartWatchAlarm(); |
| | | //AssignTasks(); |
| | | //StartWatchPosition(); |
| | | } |
| | | } |
| | |
| | | }, cts.Token); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 四向车任务分配 |
| | | /// </summary> |
| | | private static void AssignTasks() |
| | | { |
| | | |
| | | Task.Run(() => |
| | | { |
| | | while (true) |
| | | { |
| | | Console.WriteLine("开启四向车任务自分配"); |
| | | //取消线程 关闭PLC连接 |
| | | if (cts.Token.IsCancellationRequested) |
| | | { |
| | | foreach (var modModBusUtil in listModbusUtil) |
| | | { |
| | | if (modModBusUtil != null && modModBusUtil.Connected) |
| | | modModBusUtil.Close(); |
| | | } |
| | | break; |
| | | } |
| | | try |
| | | { |
| | | // 获取密集库未执行任务 根据创建时间排序 |
| | | var waitTask = _db.Queryable<WcsTask>().Where(s => s.IsDelete == false && s.Status == TaskStatusEnum.Wait && s.Type == PLCTypeEnum.ShuttleCar).OrderBy(s => s.CreateTime).First(); |
| | | |
| | | //01010101: 01排 01列 01层 01深度-此项目不做判断 |
| | | var taskpai = int.Parse(waitTask.StartLocate.Substring(4, 2)); |
| | | var tasklie = int.Parse(waitTask.StartLocate.Substring(4, 2)); |
| | | var taskceng = int.Parse(waitTask.StartLocate.Substring(4, 2)); |
| | | |
| | | #region 获取当前任务所在层所有空闲小车 |
| | | |
| | | // 获取有任务的小车编号 |
| | | var taskCarList = _db.Queryable<WcsCarTasks>().Where(m => m.IsDelete == false && m.Status == TaskStatusEnum.Wait).Select(m => m.CarNo).Distinct().ToList(); |
| | | |
| | | // 获取当前任务所在层所有空闲小车(根据小车任务表是否有任务和小车状态共同判断小车是否空闲) |
| | | var kXCarList = new List<CarInfo>(); |
| | | foreach (var modbusUtil in listModbusUtil) |
| | | { |
| | | //如果小车有未执行的任务,跳出 |
| | | if (taskCarList.Contains(modbusUtil.PlcIP)) |
| | | { |
| | | continue; |
| | | } |
| | | //获取小车的WcsDevice信息 |
| | | var listDevice = listPlcDevice.Where(s => s.PlcId == modbusUtil.PlcId && s.DeviceType == DeviceTypeEnum.Business).ToList(); |
| | | //循环读设备 |
| | | foreach (var modDevice in listDevice) |
| | | { |
| | | var (result, value) = modbusUtil.GetDBValue(modDevice.PosType, modDevice.PlcPos); |
| | | if (result.IsSucceed) |
| | | { |
| | | //获取工位WCSPLCPosition信息 |
| | | var plcPosition = listPlcPosition.Where(s => s.DeviceId == modDevice.Id).ToList(); |
| | | //小车空闲加入集合 |
| | | if (value == 1) |
| | | { |
| | | var modStationX = plcPosition.FirstOrDefault(s => s.Text == "四向车位置(X)"); |
| | | var (resultx, valuex) = modbusUtil.GetDBValue(modStationX.PosType, modStationX.PlcPos); |
| | | var modStationY = plcPosition.FirstOrDefault(s => s.Text == "四向车位置(Y)"); |
| | | var (resulty, valuey) = modbusUtil.GetDBValue(modStationY.PosType, modStationY.PlcPos); |
| | | var modStationZ = plcPosition.FirstOrDefault(s => s.Text == "四向车位置(Z)"); |
| | | var (resultz, valuez) = modbusUtil.GetDBValue(modStationZ.PosType, modStationZ.PlcPos); |
| | | //小车当前层 |
| | | var carZ = (int)valuez; |
| | | //任务层与小车层相同 |
| | | if (taskceng == carZ) |
| | | { |
| | | if (!kXCarList.Any(m => m.CarPlcIp == modbusUtil.PlcIP)) |
| | | { |
| | | var carVal = ((int)valuex).ToString().PadLeft(2, '0') + ((int)valuey).ToString().PadLeft(2, '0') + ((int)valuez).ToString().PadLeft(2, '0'); |
| | | var d = FourWayCarUtil.GetCarPath(carVal, waitTask.StartLocate, 0); |
| | | kXCarList.Add(new CarInfo() |
| | | { |
| | | CarPlcIp = modbusUtil.PlcIP, |
| | | X = (int)valuex, |
| | | Y = (int)valuey, |
| | | Z = (int)valuez, |
| | | Level = d.Count, |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | Console.WriteLine($"读取四向车{modbusUtil.PlcIP}状态失败"); |
| | | return;//有一个小车读取失败跳出方法,暂缓分配,防止同层小车关机或失联导致阻挡路径 |
| | | } |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 获取适合执行当前任务的小车 生成路径(需考虑小车阻阻挡) |
| | | |
| | | var assignCar = kXCarList.OrderBy(m => m.Level).FirstOrDefault(); |
| | | if (assignCar == null) |
| | | { |
| | | break;//没有空闲小车 |
| | | } |
| | | var data = new List<CarModel>(); |
| | | if (assignCar.Level != 0) |
| | | { |
| | | //判断小车位置是否与任务的起始储位相同,不相同:获取小车到取货储位路径 |
| | | var carLocate = assignCar.X.ToString().PadLeft(2, '0')+assignCar.Y.ToString().PadLeft(2, '0')+ assignCar.Z.ToString().PadLeft(2, '0'); |
| | | //获取小车去取货储位任务路径 |
| | | var data1 = FourWayCarUtil.GetCarPath(carLocate, waitTask.StartLocate, 0); |
| | | data.AddRange(data1); |
| | | } |
| | | //获取小车去放货储位任务路径 |
| | | var data2 = FourWayCarUtil.GetCarPath(waitTask.StartLocate, waitTask.EndLocate, 2, "1"); |
| | | |
| | | data.AddRange(data2); |
| | | if (data == null) { break; } |
| | | |
| | | var preId1 = "";//前置任务Id |
| | | var executionPath1 = "";//交互路径 |
| | | var executionPath2 = "";//交互路径 |
| | | var path = "";//所有路径 |
| | | var isOk = "1"; //是否完整路径 1完整 2 两条路径 |
| | | for (int i = 0; i < data.Count; i++) |
| | | { |
| | | //路径节点 |
| | | var pathXYZ = data[i].X.ToString().PadLeft(2, '0') + data[i].Y.ToString().PadLeft(2, '0') + data[i].Z.ToString().PadLeft(2, '0')+ data[i].NodeCom.ToString(); |
| | | path += pathXYZ + ";"; |
| | | |
| | | //获取等待或正在执行的任务中包含当前节点的小车任务 |
| | | var taskList = _db.Queryable<WcsCarTasks>().Where(m => m.IsDelete == false && (m.Status == TaskStatusEnum.Wait || m.Status == TaskStatusEnum.Doing) && m.Path.Contains(pathXYZ)).Select(m => m.Id).Distinct().ToList(); |
| | | |
| | | foreach (var item in taskList) |
| | | { |
| | | //判断如果是完整路径 记录交互路径 |
| | | if (isOk == "1") |
| | | { |
| | | if (i == 0) |
| | | { |
| | | return;//第一个节点有和其他任务路径冲突,无法避免 |
| | | } |
| | | var pathXYZQian = data[i - 1].X.ToString().PadLeft(2, '0') + data[i - 1].Y.ToString().PadLeft(2, '0') + data[i - 1].Z.ToString().PadLeft(2, '0') + data[i].NodeCom.ToString(); |
| | | if (!executionPath1.Contains(pathXYZQian)) |
| | | { |
| | | executionPath1 += pathXYZQian + ";"; |
| | | } |
| | | executionPath2 += pathXYZQian + ";"; |
| | | } |
| | | //判断添加前置任务Id |
| | | if (!preId1.Contains(item + "")) |
| | | { |
| | | preId1 += item + ";"; |
| | | } |
| | | |
| | | isOk = "2"; |
| | | } |
| | | |
| | | if (data[i].IsSendPlc) |
| | | { |
| | | if (isOk == "1") |
| | | { |
| | | executionPath1 += pathXYZ + ";"; |
| | | } |
| | | else |
| | | { |
| | | executionPath2 += pathXYZ + ";"; |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 判断是否有空闲小车阻挡路径 3 |
| | | var preId3 = "";//前置任务Id |
| | | foreach (var item in kXCarList) |
| | | { |
| | | if (item == assignCar) |
| | | { |
| | | continue;//排除当前分配任务的小车 |
| | | } |
| | | //小车位置 |
| | | var carXYZ = item.X.ToString().PadLeft(2, '0') + item.Y.ToString().PadLeft(2, '0') + item.Z.ToString().PadLeft(2, '0') ; |
| | | //分配的任务路径中 当前小车是否阻挡 |
| | | if (path.Contains(carXYZ)) |
| | | { |
| | | //获取等待或正在执行的任务中包含当前节点的小车任务,不会有太多任务,同层有几个小车最多有几个任务 |
| | | var taskList3 = _db.Queryable<WcsCarTasks>().Where(m => m.IsDelete == false && (m.Status == TaskStatusEnum.Wait || m.Status == TaskStatusEnum.Doing)).ToList(); |
| | | |
| | | var str3 = "";//所有已分配或执行的任务全路径之和 |
| | | |
| | | foreach (var item2 in taskList3) |
| | | { |
| | | str3 += item2.Path; |
| | | } |
| | | var endLocate3 = ""; |
| | | var executionPath3 = ""; |
| | | var path3 = ""; |
| | | var data3 = new List<CarModel>(); |
| | | //查找目标位置 |
| | | while (endLocate3 == "" || data3.Count == 0 || data3 == null) |
| | | { |
| | | endLocate3 = FourWayCarUtil.GetCarEndLocation(carXYZ, str3); |
| | | data3 = FourWayCarUtil.GetCarPath(carXYZ, endLocate3, 0, "0"); |
| | | } |
| | | foreach (var itemPath in data3) |
| | | { |
| | | var pathXYZ = itemPath.X.ToString().PadLeft(2, '0') + itemPath.Y.ToString().PadLeft(2, '0') + itemPath.Z.ToString().PadLeft(2, '0') + itemPath.NodeCom.ToString(); |
| | | |
| | | path3 += pathXYZ + ";"; |
| | | if (itemPath.IsSendPlc) |
| | | { |
| | | executionPath3 += pathXYZ + ";"; |
| | | } |
| | | } |
| | | WcsTask modTask = new WcsTask() |
| | | { |
| | | TaskNo = _taskService.GetTaskCode(), |
| | | TaskType = TaskTypeEnum.Move, |
| | | Type = PLCTypeEnum.ShuttleCar, |
| | | StartLocate = carXYZ, |
| | | EndLocate = endLocate3, |
| | | PalletNo = "", |
| | | Status = TaskStatusEnum.Wait, |
| | | Levels = 2, |
| | | Origin = "WCS" |
| | | }; |
| | | _db.Insertable(modTask).ExecuteCommand(); |
| | | HubUtil.PublicTask(modTask.Adapt<WcsTaskOutput>()); |
| | | //移动小车 |
| | | var carTaskYC = new WcsCarTasks() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | PreId = "", |
| | | ExecutionPath = executionPath3, |
| | | Path = path3, |
| | | CarNo = item.CarPlcIp, |
| | | Status = TaskStatusEnum.Wait |
| | | }; |
| | | var idLong = _db.Insertable(carTaskYC).ExecuteReturnBigIdentity(); |
| | | preId3 += idLong + ";"; |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 判断现有任务中最终节点是否在当前分配路径中,如有 添加移走小车任务并加入前置任务 4 |
| | | |
| | | //获取等待或正在执行的任务中包含当前节点的小车任务,不会有太多任务,同层有几个小车最多有几个任务 |
| | | var taskList4 = _db.Queryable<WcsCarTasks>().Where(m => m.IsDelete == false && (m.Status == TaskStatusEnum.Wait || m.Status == TaskStatusEnum.Doing)).ToList(); |
| | | var preId4 = "";//前置任务Id |
| | | var str4 = "";//所有已分配或执行的任务全路径之和 |
| | | |
| | | foreach (var item in taskList4) |
| | | { |
| | | str4 += item.Path; |
| | | } |
| | | //判断现有任务中最终节点是否在当前分配路径中,如有 添加移走小车任务并加入前置任务 |
| | | foreach (var item in taskList4) |
| | | { |
| | | var lastPathList = item.ExecutionPath.Split(';'); |
| | | // a;b;c; 最后一个位是“”,所以lastPathList.Length - 2 |
| | | var lastPath = lastPathList[lastPathList.Length - 2]; |
| | | |
| | | //如果此此分配路径包含醉舞中最终节点路径,添加移走小车 |
| | | if (path.Contains(lastPath)) |
| | | { |
| | | var endLocate = ""; |
| | | var executionPath4 = ""; |
| | | var path4 = ""; |
| | | var data4 = new List<CarModel>(); |
| | | //查找目标位置 |
| | | while (endLocate == "" || data4.Count == 0 || data4 == null) |
| | | { |
| | | endLocate = FourWayCarUtil.GetCarEndLocation(lastPath, str4); |
| | | data4 = FourWayCarUtil.GetCarPath(lastPath, endLocate, 0, "0"); |
| | | } |
| | | foreach (var itemPath in data4) |
| | | { |
| | | var pathXYZ = itemPath.X.ToString().PadLeft(2, '0') + itemPath.Y.ToString().PadLeft(2, '0') + itemPath.Z.ToString().PadLeft(2, '0') + itemPath.NodeCom.ToString(); |
| | | |
| | | path4 += pathXYZ + ";"; |
| | | if (itemPath.IsSendPlc) |
| | | { |
| | | executionPath4 += pathXYZ + ";"; |
| | | } |
| | | } |
| | | WcsTask modTask = new WcsTask() |
| | | { |
| | | TaskNo = _taskService.GetTaskCode(), |
| | | TaskType = TaskTypeEnum.Move, |
| | | Type = PLCTypeEnum.ShuttleCar, |
| | | StartLocate = lastPath, |
| | | EndLocate = endLocate, |
| | | PalletNo = "", |
| | | Status = TaskStatusEnum.Wait, |
| | | Levels = 2, |
| | | Origin = "WCS" |
| | | }; |
| | | _db.Insertable(modTask).ExecuteCommand(); |
| | | HubUtil.PublicTask(modTask.Adapt<WcsTaskOutput>()); |
| | | //移动小车 |
| | | var carTaskYC = new WcsCarTasks() |
| | | { |
| | | TaskNo = modTask.TaskNo, |
| | | PreId = "", |
| | | ExecutionPath = executionPath4, |
| | | Path = path4, |
| | | CarNo = item.CarNo, |
| | | Status = TaskStatusEnum.Wait |
| | | }; |
| | | var idLong = _db.Insertable(carTaskYC).ExecuteReturnBigIdentity(); |
| | | preId4 += idLong + ";"; |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 插入任务数据 改变任务状态 |
| | | |
| | | // 插入四向车任务表 |
| | | var carTask1 = new WcsCarTasks() |
| | | { |
| | | TaskNo = waitTask.TaskNo, |
| | | PreId = preId1+ preId3+preId4, |
| | | ExecutionPath = executionPath1, |
| | | Path = path, |
| | | CarNo = assignCar.CarPlcIp, |
| | | Status = TaskStatusEnum.Wait |
| | | }; |
| | | _db.Insertable(carTask1).ExecuteCommand(); |
| | | if (!string.IsNullOrWhiteSpace(executionPath1) && isOk == "2") |
| | | { |
| | | // 插入四向车任务表 |
| | | var carTask2 = new WcsCarTasks() |
| | | { |
| | | TaskNo = waitTask.TaskNo, |
| | | PreId = preId1, |
| | | ExecutionPath = executionPath2, |
| | | Path = path, |
| | | CarNo = assignCar.CarPlcIp, |
| | | Status = TaskStatusEnum.Wait |
| | | }; |
| | | _db.Insertable(carTask2).ExecuteCommand(); |
| | | } |
| | | // 改变总任务表状态 |
| | | waitTask.Status = TaskStatusEnum.Doing; |
| | | waitTask.UpdateTime = DateTime.Now; |
| | | _db.Updateable(waitTask).ExecuteCommand(); |
| | | HubUtil.PublicTask(waitTask.Adapt<WcsTaskOutput>()); |
| | | |
| | | #endregion |
| | | |
| | | Thread.Sleep(3000); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Log.Error("任务分配发生异常", ex); |
| | | } |
| | | } |
| | | },cts.Token); |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 连接状态线程 |
| | |
| | | } |
| | | }); |
| | | } |
| | | /// <summary> |
| | | /// 修改位置信息 |
| | | /// </summary> |
| | | /// <param name="modInfo"></param> |
| | | private static void UpdatePosition(PlcPositionInfo modInfo) |
| | | { |
| | | var modTemp = listPositionInfo.FirstOrDefault(s => s.StationNum == modInfo.StationNum && modInfo.Type == s.Type); |
New file |
| | |
| | | // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 |
| | | // |
| | | // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 |
| | | // |
| | | // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! |
| | | |
| | | namespace WCS.Application; |
| | | |
| | | /// <summary> |
| | | /// 四向车任务明细表输出参数 |
| | | /// </summary> |
| | | public class WcsCarTasksDto |
| | | { |
| | | /// <summary> |
| | | /// 主键Id |
| | | /// </summary> |
| | | public long Id { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 任务号 |
| | | /// </summary> |
| | | public string? TaskNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 前置小车任务ID |
| | | /// </summary> |
| | | public string? PreId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 交互路径 |
| | | /// </summary> |
| | | public string? ExecutionPath { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 所有路径 |
| | | /// </summary> |
| | | public string? Path { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 小车编号 |
| | | /// </summary> |
| | | public string? CarNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 状态 |
| | | /// </summary> |
| | | public int? Status { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建时间 |
| | | /// </summary> |
| | | public DateTime? CreateTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 更新时间 |
| | | /// </summary> |
| | | public DateTime? UpdateTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者Id |
| | | /// </summary> |
| | | public long? CreateUserId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者姓名 |
| | | /// </summary> |
| | | public string? CreateUserName { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 修改者Id |
| | | /// </summary> |
| | | public long? UpdateUserId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者部门Id |
| | | /// </summary> |
| | | public long? CreateOrgId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者部门名称 |
| | | /// </summary> |
| | | public string? CreateOrgName { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 软删除 |
| | | /// </summary> |
| | | public bool IsDelete { get; set; } |
| | | |
| | | } |
New file |
| | |
| | | // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 |
| | | // |
| | | // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 |
| | | // |
| | | // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! |
| | | |
| | | using Admin.NET.Core; |
| | | using System.ComponentModel.DataAnnotations; |
| | | |
| | | namespace WCS.Application; |
| | | |
| | | /// <summary> |
| | | /// 四向车任务明细表基础输入参数 |
| | | /// </summary> |
| | | public class WcsCarTasksBaseInput |
| | | { |
| | | /// <summary> |
| | | /// 任务号 |
| | | /// </summary> |
| | | public virtual string? TaskNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 前置小车任务ID |
| | | /// </summary> |
| | | public virtual string? PreId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 交互路径 |
| | | /// </summary> |
| | | public virtual string? ExecutionPath { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 所有路径 |
| | | /// </summary> |
| | | public virtual string? Path { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 小车编号 |
| | | /// </summary> |
| | | public virtual string? CarNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 状态 |
| | | /// </summary> |
| | | public virtual TaskStatusEnum? Status { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建时间 |
| | | /// </summary> |
| | | public virtual DateTime? CreateTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 更新时间 |
| | | /// </summary> |
| | | public virtual DateTime? UpdateTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者Id |
| | | /// </summary> |
| | | public virtual long? CreateUserId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者姓名 |
| | | /// </summary> |
| | | public virtual string? CreateUserName { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 修改者Id |
| | | /// </summary> |
| | | public virtual long? UpdateUserId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者部门Id |
| | | /// </summary> |
| | | public virtual long? CreateOrgId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者部门名称 |
| | | /// </summary> |
| | | public virtual string? CreateOrgName { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 软删除 |
| | | /// </summary> |
| | | public virtual bool IsDelete { get; set; } |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 四向车任务明细表分页查询输入参数 |
| | | /// </summary> |
| | | public class PageWcsCarTasksInput : BasePageInput |
| | | { |
| | | /// <summary> |
| | | /// 关键字查询 |
| | | /// </summary> |
| | | public string? SearchKey { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 任务号 |
| | | /// </summary> |
| | | public string? TaskNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 前置小车任务ID |
| | | /// </summary> |
| | | public string? PreId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 交互路径 |
| | | /// </summary> |
| | | public string? ExecutionPath { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 所有路径 |
| | | /// </summary> |
| | | public string? Path { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 小车编号 |
| | | /// </summary> |
| | | public string? CarNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 状态 |
| | | /// </summary> |
| | | public TaskStatusEnum? Status { get; set; } |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 四向车任务明细表增加输入参数 |
| | | /// </summary> |
| | | public class AddWcsCarTasksInput : WcsCarTasksBaseInput |
| | | { |
| | | /// <summary> |
| | | /// 软删除 |
| | | /// </summary> |
| | | [Required(ErrorMessage = "软删除不能为空")] |
| | | public override bool IsDelete { get; set; } |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 四向车任务明细表删除输入参数 |
| | | /// </summary> |
| | | public class DeleteWcsCarTasksInput : BaseIdInput |
| | | { |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 四向车任务明细表更新输入参数 |
| | | /// </summary> |
| | | public class UpdateWcsCarTasksInput : WcsCarTasksBaseInput |
| | | { |
| | | /// <summary> |
| | | /// 主键Id |
| | | /// </summary> |
| | | [Required(ErrorMessage = "主键Id不能为空")] |
| | | public long Id { get; set; } |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 四向车任务明细表主键查询输入参数 |
| | | /// </summary> |
| | | public class QueryByIdWcsCarTasksInput : DeleteWcsCarTasksInput |
| | | { |
| | | |
| | | } |
New file |
| | |
| | | // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 |
| | | // |
| | | // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 |
| | | // |
| | | // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! |
| | | |
| | | namespace WCS.Application; |
| | | |
| | | /// <summary> |
| | | /// 四向车任务明细表输出参数 |
| | | /// </summary> |
| | | public class WcsCarTasksOutput |
| | | { |
| | | /// <summary> |
| | | /// 主键Id |
| | | /// </summary> |
| | | public long Id { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 任务号 |
| | | /// </summary> |
| | | public string? TaskNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 前置小车任务ID |
| | | /// </summary> |
| | | public string? PreId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 交互路径 |
| | | /// </summary> |
| | | public string? ExecutionPath { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 所有路径 |
| | | /// </summary> |
| | | public string? Path { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 小车编号 |
| | | /// </summary> |
| | | public string? CarNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 状态 |
| | | /// </summary> |
| | | public int? Status { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建时间 |
| | | /// </summary> |
| | | public DateTime? CreateTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 更新时间 |
| | | /// </summary> |
| | | public DateTime? UpdateTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者Id |
| | | /// </summary> |
| | | public long? CreateUserId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者姓名 |
| | | /// </summary> |
| | | public string? CreateUserName { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 修改者Id |
| | | /// </summary> |
| | | public long? UpdateUserId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者部门Id |
| | | /// </summary> |
| | | public long? CreateOrgId { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 创建者部门名称 |
| | | /// </summary> |
| | | public string? CreateOrgName { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 软删除 |
| | | /// </summary> |
| | | public bool IsDelete { get; set; } |
| | | |
| | | } |
| | | |
| | | |
New file |
| | |
| | | // Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 |
| | | // |
| | | // 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 |
| | | // |
| | | // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! |
| | | |
| | | using Admin.NET.Core.Service; |
| | | using Microsoft.AspNetCore.Http; |
| | | using WCS.Application.Entity; |
| | | namespace WCS.Application; |
| | | |
| | | /// <summary> |
| | | /// 四向车任务明细表服务 |
| | | /// </summary> |
| | | [ApiDescriptionSettings(ApplicationConst.GroupName, Order = 100)] |
| | | public class WcsCarTasksService : IDynamicApiController, ITransient |
| | | { |
| | | private readonly SqlSugarRepository<WcsCarTasks> _wcsCarTasksRep; |
| | | public WcsCarTasksService(SqlSugarRepository<WcsCarTasks> wcsCarTasksRep) |
| | | { |
| | | _wcsCarTasksRep = wcsCarTasksRep; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 分页查询四向车任务明细表 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | [HttpPost] |
| | | [ApiDescriptionSettings(Name = "Page")] |
| | | [DisplayName("分页查询四向车任务明细表")] |
| | | public async Task<SqlSugarPagedList<WcsCarTasksOutput>> Page(PageWcsCarTasksInput input) |
| | | { |
| | | input.SearchKey = input.SearchKey?.Trim(); |
| | | var query = _wcsCarTasksRep.AsQueryable() |
| | | .WhereIF(!string.IsNullOrEmpty(input.SearchKey), u => |
| | | u.TaskNo.Contains(input.SearchKey) |
| | | || u.PreId.Contains(input.SearchKey) |
| | | || u.ExecutionPath.Contains(input.SearchKey) |
| | | || u.Path.Contains(input.SearchKey) |
| | | || u.CarNo.Contains(input.SearchKey) |
| | | ) |
| | | .WhereIF(!string.IsNullOrWhiteSpace(input.TaskNo), u => u.TaskNo.Contains(input.TaskNo.Trim())) |
| | | .WhereIF(!string.IsNullOrWhiteSpace(input.PreId), u => u.PreId.Contains(input.PreId.Trim())) |
| | | .WhereIF(!string.IsNullOrWhiteSpace(input.ExecutionPath), u => u.ExecutionPath.Contains(input.ExecutionPath.Trim())) |
| | | .WhereIF(!string.IsNullOrWhiteSpace(input.Path), u => u.Path.Contains(input.Path.Trim())) |
| | | .WhereIF(!string.IsNullOrWhiteSpace(input.CarNo), u => u.CarNo.Contains(input.CarNo.Trim())) |
| | | .WhereIF(input.Status>0, u => u.Status == input.Status) |
| | | .Select<WcsCarTasksOutput>(); |
| | | return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 增加四向车任务明细表 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | [HttpPost] |
| | | [ApiDescriptionSettings(Name = "Add")] |
| | | [DisplayName("增加四向车任务明细表")] |
| | | public async Task<long> Add(AddWcsCarTasksInput input) |
| | | { |
| | | var entity = input.Adapt<WcsCarTasks>(); |
| | | await _wcsCarTasksRep.InsertAsync(entity); |
| | | return entity.Id; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 删除四向车任务明细表 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | [HttpPost] |
| | | [ApiDescriptionSettings(Name = "Delete")] |
| | | [DisplayName("删除四向车任务明细表")] |
| | | public async Task Delete(DeleteWcsCarTasksInput input) |
| | | { |
| | | var entity = await _wcsCarTasksRep.GetFirstAsync(u => u.Id == input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002); |
| | | await _wcsCarTasksRep.FakeDeleteAsync(entity); //假删除 |
| | | //await _wcsCarTasksRep.DeleteAsync(entity); //真删除 |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 更新四向车任务明细表 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | [HttpPost] |
| | | [ApiDescriptionSettings(Name = "Update")] |
| | | [DisplayName("更新四向车任务明细表")] |
| | | public async Task Update(UpdateWcsCarTasksInput input) |
| | | { |
| | | var entity = input.Adapt<WcsCarTasks>(); |
| | | await _wcsCarTasksRep.AsUpdateable(entity).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync(); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取四向车任务明细表 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | [HttpGet] |
| | | [ApiDescriptionSettings(Name = "Detail")] |
| | | [DisplayName("获取四向车任务明细表")] |
| | | public async Task<WcsCarTasks> Detail([FromQuery] QueryByIdWcsCarTasksInput input) |
| | | { |
| | | return await _wcsCarTasksRep.GetFirstAsync(u => u.Id == input.Id); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取四向车任务明细表列表 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | [HttpGet] |
| | | [ApiDescriptionSettings(Name = "List")] |
| | | [DisplayName("获取四向车任务明细表列表")] |
| | | public async Task<List<WcsCarTasksOutput>> List([FromQuery] PageWcsCarTasksInput input) |
| | | { |
| | | return await _wcsCarTasksRep.AsQueryable().Select<WcsCarTasksOutput>().ToListAsync(); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | } |
New file |
| | |
| | | using AngleSharp.Dom; |
| | | using Furion.Logging; |
| | | using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | using WCS.Application.Entity; |
| | | |
| | | namespace WCS.Application.Util; |
| | | |
| | | public static class FourWayCarUtil |
| | | { |
| | | private static readonly ISqlSugarClient _db = SqlSugarSetup.ITenant.GetConnectionScope(SqlSugarConst.MainConfigId); |
| | | |
| | | |
| | | /// <summary> |
| | | /// 获取小车路径 |
| | | /// </summary> |
| | | /// <param name="startLocation">起始位置</param> |
| | | /// <param name="endLocation">目标位置</param> |
| | | /// <param name="moveType">移动类型 0:移动 1:取货 2:放货</param> |
| | | /// <param name="isLoad">是否载货0:未载货 1:已载货</param> |
| | | /// <returns></returns> |
| | | public static List<CarModel> GetCarPath(string startLocation, string endLocation, int moveType, string isLoad = "0") |
| | | { |
| | | if (string.IsNullOrEmpty(startLocation) || string.IsNullOrEmpty(endLocation)) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | // 起始位置 |
| | | CarModel start = new CarModel() |
| | | { |
| | | X = int.Parse(startLocation.Substring(0, 2)), |
| | | Y = int.Parse(startLocation.Substring(2, 2)), |
| | | Z = int.Parse(startLocation.Substring(4, 2)), |
| | | NodeCom = 0 |
| | | }; |
| | | |
| | | // 目标位置 |
| | | CarModel end = new CarModel() |
| | | { |
| | | X = int.Parse(endLocation.Substring(0, 2)), |
| | | Y = int.Parse(endLocation.Substring(2, 2)), |
| | | Z = int.Parse(endLocation.Substring(4, 2)), |
| | | NodeCom = 0 |
| | | }; |
| | | |
| | | // 获取储位表信息存储到集合里 |
| | | var layer = int.Parse(startLocation.Substring(4, 2)); |
| | | |
| | | // 获取当前层储位信息 |
| | | var locationModels = _db.Queryable<WcsStorageLocat>() |
| | | .Where(m => m.WareHouseNo == "W01" && m.Layer == layer && m.IsDelete == false) |
| | | .ToList(); |
| | | |
| | | #region 使用算法计算小车路径 |
| | | try |
| | | { |
| | | // 定义开发列表存储路径节点 |
| | | var openSet = new SortedSet<(int fscore, CarModel pos)>(); |
| | | // 定义关闭节点字典 |
| | | var closeSet = new Dictionary<CarModel, CarModel>(); |
| | | // 定义上一位置与目标位置字典 |
| | | var cameFrom = new Dictionary<CarModel, CarModel>(); |
| | | // 定义上一位置与目标位置的实际距离字典 |
| | | var gScore = new Dictionary<CarModel, int>(); |
| | | // 定义上一位置与目标位置的预估距离字典 |
| | | var fScore = new Dictionary<CarModel, int>(); |
| | | |
| | | // 存储最优距离,及起始节点 |
| | | openSet.Add((Heuristic(start, end), start)); |
| | | gScore[start] = 0; |
| | | fScore[start] = Heuristic(start, end); |
| | | |
| | | // 循环查找路径 |
| | | while (openSet.Count > 0) |
| | | { |
| | | var current = openSet.Min.pos; |
| | | openSet.Remove(openSet.Min); |
| | | |
| | | if (current.Equals(end)) |
| | | { |
| | | Log.Error(ReconstructPath(cameFrom, current).ToString()); |
| | | return ReconstructPath(cameFrom, current); |
| | | } |
| | | |
| | | // 存储小车可运行的方向 |
| | | var validDirections = new List<CarModel>(); |
| | | var currentLocation = locationModels.FirstOrDefault(m => m.Row == current.X && m.Column == current.Y); |
| | | if (currentLocation.Make == "0") |
| | | { |
| | | // 主通道 |
| | | validDirections.Add(new CarModel() { X = 1, Y = 0 }); // 右 |
| | | validDirections.Add(new CarModel() { X = -1, Y = 0 }); // 左 |
| | | validDirections.Add(new CarModel() { X = 0, Y = 1 }); // 下 |
| | | validDirections.Add(new CarModel() { X = 0, Y = -1 }); // 上 |
| | | } |
| | | |
| | | if (currentLocation.Make == "1") |
| | | { |
| | | // 子通道 |
| | | // 先拆分出口 |
| | | var outNode = currentLocation.AisleOne; |
| | | if (string.IsNullOrEmpty(outNode)) |
| | | { |
| | | throw new Exception("当前位置没有维护出口!"); |
| | | } |
| | | int outX = int.Parse(outNode.Substring(0, 2)); |
| | | int outY = int.Parse(outNode.Substring(2, 2)); |
| | | if (current.X == outX) |
| | | { |
| | | validDirections.Add(new CarModel() { X = 0, Y = 1 }); // 下 |
| | | validDirections.Add(new CarModel() { X = 0, Y = -1 }); // 上 |
| | | |
| | | } |
| | | else |
| | | { |
| | | validDirections.Add(new CarModel() { X = 1, Y = 0 }); // 右 |
| | | validDirections.Add(new CarModel() { X = -1, Y = 0 }); // 左 |
| | | } |
| | | } |
| | | |
| | | // 循环连接节点。 |
| | | bool isNextNode = false; |
| | | foreach (var dir in validDirections) |
| | | { |
| | | CarModel neighbor = new CarModel() { X = current.X + dir.X, Y = current.Y + dir.Y, Z = layer }; |
| | | |
| | | // 验证下一节点位置是否可通行并且判断是否被其他小车占用 |
| | | |
| | | |
| | | // 判断下一节点是否关闭 |
| | | if (closeSet.ContainsKey(neighbor)) |
| | | { |
| | | closeSet[neighbor] = neighbor; |
| | | } |
| | | |
| | | // 当前节点 |
| | | var currentModel = locationModels.FirstOrDefault(it => it.Row == current.X && it.Column == current.Y); |
| | | // 下一节点 |
| | | var locationModel = locationModels.FirstOrDefault(it => it.Row == neighbor.X && it.Column == neighbor.Y); |
| | | |
| | | // 不存在此位置信息 |
| | | if (locationModel == null) |
| | | { |
| | | closeSet[neighbor] = neighbor; |
| | | continue; |
| | | } |
| | | // 储位状态为损坏不可通行 储位状态为屏蔽为可通行不可存储托盘 |
| | | if (locationModel.Flag == "2") |
| | | { |
| | | closeSet[neighbor] = neighbor; |
| | | continue; |
| | | } |
| | | // 判断下一节点上是否有托盘 |
| | | if (!string.IsNullOrEmpty(locationModel.PalletNo)) |
| | | { |
| | | // 判断小车是否载托盘盘 |
| | | if (isLoad == "1") |
| | | { |
| | | closeSet[neighbor] = neighbor; |
| | | // 小车上载托盘不可通行跳过 |
| | | continue; |
| | | } |
| | | } |
| | | // 优化项,验证下一节点是否被别的小车占用 liudl:在此添加优化代码 |
| | | |
| | | // 更新实际距离 |
| | | int tentativeGScore = gScore[current] + 1; |
| | | // 判断位置是否已包含在路径内 且 是否更近节点 |
| | | if (!gScore.ContainsKey(neighbor) || tentativeGScore < gScore[neighbor]) |
| | | { |
| | | neighbor.IsSendPlc = false; |
| | | // 补充参数 |
| | | if (neighbor.Equals(end)) |
| | | { |
| | | neighbor.NodeCom = moveType; |
| | | neighbor.IsSendPlc = true; |
| | | } |
| | | else if (currentModel.Make != locationModel.Make) |
| | | { |
| | | if (current.X == neighbor.X) |
| | | { |
| | | neighbor.NodeCom = 3; |
| | | } |
| | | else if (current.Y == neighbor.Y) |
| | | { |
| | | neighbor.NodeCom = 2; |
| | | } |
| | | |
| | | neighbor.IsSendPlc = true; |
| | | } |
| | | |
| | | // 更新实际距离与预估距离 |
| | | cameFrom[neighbor] = current; |
| | | gScore[neighbor] = tentativeGScore; |
| | | fScore[neighbor] = tentativeGScore + Heuristic(neighbor, end); |
| | | openSet.Add((fScore[neighbor], neighbor)); |
| | | isNextNode = true; |
| | | } |
| | | } |
| | | |
| | | if (!isNextNode) |
| | | { |
| | | closeSet[current] = current; |
| | | } |
| | | } |
| | | #endregion |
| | | } |
| | | catch (Exception) |
| | | { |
| | | throw; |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 获取小车移动的目标位置 |
| | | /// </summary> |
| | | /// <param name="startLocation">起始位置</param> |
| | | /// <param name="noPathStr">不能存放的储位字符串</param> |
| | | /// <returns></returns> |
| | | public static string GetCarEndLocation(string startLocation,string noPathStr) |
| | | { |
| | | if (string.IsNullOrEmpty(startLocation)) |
| | | { |
| | | return null; |
| | | } |
| | | |
| | | // 起始位置 |
| | | CarModel start = new CarModel() |
| | | { |
| | | X = int.Parse(startLocation.Substring(0, 2)), |
| | | Y = int.Parse(startLocation.Substring(2, 2)), |
| | | Z = int.Parse(startLocation.Substring(4, 2)), |
| | | NodeCom = 0 |
| | | }; |
| | | |
| | | // 获取储位表信息存储到集合里 |
| | | var layer = int.Parse(startLocation.Substring(4, 2)); |
| | | |
| | | // 获取当前层储位信息 |
| | | var locationModels = _db.Queryable<WcsStorageLocat>() |
| | | .Where(m => m.WareHouseNo == "W01" && m.Layer == layer && m.IsDelete == false) |
| | | .ToList(); |
| | | |
| | | #region MyRegion |
| | | |
| | | var list = locationModels.Where(m => !noPathStr.Contains(m.LocatNo)).OrderBy(m=> Math.Abs(start.X - m.Row) + Math.Abs(start.Y - m.Column)).ToList(); |
| | | var locateStr = ""; |
| | | foreach (var item in list) |
| | | { |
| | | // 储位状态为损坏不可通行 储位状态为屏蔽为可通行不可存储托盘 |
| | | if (item.Flag == "2") |
| | | { |
| | | continue; |
| | | } |
| | | locateStr = item.LocatNo; |
| | | break; |
| | | } |
| | | return locateStr; |
| | | #endregion |
| | | |
| | | #region 使用算法计算小车可移动的目标路径 |
| | | //try |
| | | //{ |
| | | |
| | | // // 定义关闭节点字典 |
| | | // var closeSet = new Dictionary<CarModel, CarModel>(); |
| | | |
| | | // // 存储小车可运行的方向 |
| | | // var validDirections = new List<CarModel>(); |
| | | // var currentLocation = locationModels.FirstOrDefault(m => m.Row == start.X && m.Column == start.Y); |
| | | // if (currentLocation.Make == "0") |
| | | // { |
| | | // // 主通道 |
| | | // validDirections.Add(new CarModel() { X = 1, Y = 0 }); // 右 |
| | | // validDirections.Add(new CarModel() { X = -1, Y = 0 }); // 左 |
| | | // validDirections.Add(new CarModel() { X = 0, Y = 1 }); // 下 |
| | | // validDirections.Add(new CarModel() { X = 0, Y = -1 }); // 上 |
| | | // } |
| | | |
| | | // if (currentLocation.Make == "1") |
| | | // { |
| | | // // 子通道 |
| | | // // 先拆分出口 |
| | | // var outNode = currentLocation.AisleOne; |
| | | // if (string.IsNullOrEmpty(outNode)) |
| | | // { |
| | | // throw new Exception("当前位置没有维护出口!"); |
| | | // } |
| | | // int outX = int.Parse(outNode.Substring(0, 2)); |
| | | // int outY = int.Parse(outNode.Substring(2, 2)); |
| | | // if (start.X == outX) |
| | | // { |
| | | // validDirections.Add(new CarModel() { X = 0, Y = 1 }); // 下 |
| | | // validDirections.Add(new CarModel() { X = 0, Y = -1 }); // 上 |
| | | |
| | | // } |
| | | // else |
| | | // { |
| | | // validDirections.Add(new CarModel() { X = 1, Y = 0 }); // 右 |
| | | // validDirections.Add(new CarModel() { X = -1, Y = 0 }); // 左 |
| | | // } |
| | | // } |
| | | |
| | | // foreach (var dir in validDirections) |
| | | // { |
| | | // CarModel neighbor = new CarModel() { X = start.X + dir.X, Y = start.Y + dir.Y, Z = layer }; |
| | | |
| | | // // 验证下一节点位置是否可通行并且判断是否被其他小车占用 |
| | | |
| | | |
| | | // // 判断下一节点是否关闭 |
| | | // if (closeSet.ContainsKey(neighbor)) |
| | | // { |
| | | // closeSet[neighbor] = neighbor; |
| | | // } |
| | | |
| | | // // 当前节点 |
| | | // var currentModel = locationModels.FirstOrDefault(it => it.Row == start.X && it.Column == start.Y); |
| | | // // 下一节点 |
| | | // var locationModel = locationModels.FirstOrDefault(it => it.Row == neighbor.X && it.Column == neighbor.Y); |
| | | |
| | | // // 不存在此位置信息 |
| | | // if (locationModel == null) |
| | | // { |
| | | // closeSet[neighbor] = neighbor; |
| | | // continue; |
| | | // } |
| | | // // 储位状态为损坏不可通行 储位状态为屏蔽为可通行不可存储托盘 |
| | | // if (locationModel.Flag == "2") |
| | | // { |
| | | // closeSet[neighbor] = neighbor; |
| | | // continue; |
| | | // } |
| | | |
| | | // } |
| | | |
| | | |
| | | |
| | | //} |
| | | //catch (Exception) |
| | | //{ |
| | | // throw; |
| | | //} |
| | | |
| | | //return null; |
| | | #endregion |
| | | } |
| | | |
| | | public static int GetTaskNo() |
| | | { |
| | | var list = _db.Queryable<WcsCarTasks>().ToList(); |
| | | var maxNo = list.Max(m => m.CarTaskNo); |
| | | |
| | | if (maxNo!=null && maxNo > 0) |
| | | { |
| | | if (maxNo++ > 65535) |
| | | { |
| | | return 1; |
| | | } |
| | | |
| | | return (int)maxNo++; |
| | | } |
| | | return 1; |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 计算曼哈顿距离 |
| | | /// </summary> |
| | | /// <param name="start">起始位置</param> |
| | | /// <param name="end">目标位置</param> |
| | | /// <returns>位置距离</returns> |
| | | private static int Heuristic(CarModel start, CarModel end) |
| | | { |
| | | return Math.Abs(start.X - end.X) + Math.Abs(start.Y - end.Y); |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 重构完整路径 |
| | | /// </summary> |
| | | /// <param name="cameFrom"></param> |
| | | /// <param name="current"></param> |
| | | /// <returns></returns> |
| | | private static List<CarModel> ReconstructPath(Dictionary<CarModel, CarModel> cameFrom, CarModel current) |
| | | { |
| | | var path = new List<CarModel> { current }; |
| | | while (cameFrom.ContainsKey(current)) |
| | | { |
| | | current = cameFrom[current]; |
| | | path.Insert(0, current); |
| | | } |
| | | return path; |
| | | } |
| | | } |
| | | |
| | | public class CarModel : IComparable<CarModel> |
| | | { |
| | | /// <summary> |
| | | /// 行=X |
| | | /// </summary> |
| | | public int X { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 列=Y |
| | | /// </summary> |
| | | public int Y { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 层=Z |
| | | /// </summary> |
| | | public int Z { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 节点命令 1:顶货 2:子通道运行 3:主通道运行 4:放货 |
| | | /// </summary> |
| | | public int NodeCom { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 是否下发plc |
| | | /// </summary> |
| | | public bool IsSendPlc { get; set; } |
| | | |
| | | public int CompareTo(CarModel other) |
| | | { |
| | | if (other == null) |
| | | return 1; |
| | | |
| | | // 这里根据 X、Y、Z 坐标进行比较 |
| | | int result = X.CompareTo(other.X); |
| | | if (result != 0) |
| | | return result; |
| | | |
| | | result = Y.CompareTo(other.Y); |
| | | if (result != 0) |
| | | return result; |
| | | |
| | | return Z.CompareTo(other.Z); |
| | | } |
| | | |
| | | // 重写 Equals 方法 |
| | | public override bool Equals(object obj) |
| | | { |
| | | if (obj == null || GetType() != obj.GetType()) |
| | | return false; |
| | | |
| | | CarModel other = (CarModel)obj; |
| | | return X == other.X && Y == other.Y && Z == other.Z; |
| | | } |
| | | |
| | | // 重写 GetHashCode 方法 |
| | | public override int GetHashCode() |
| | | { |
| | | return HashCode.Combine(X, Y, Z); |
| | | } |
| | | } |
| | | |
| | | public class CarInfo |
| | | { |
| | | /// <summary> |
| | | /// 小车IP |
| | | /// </summary> |
| | | public string CarPlcIp { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 顺序等级 |
| | | /// </summary> |
| | | public int Level { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 行=X |
| | | /// </summary> |
| | | public int X { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 列=Y |
| | | /// </summary> |
| | | public int Y { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 层=Z |
| | | /// </summary> |
| | | public int Z { get; set; } |
| | | } |
| | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 调用WMS接口 反馈任务取货完成接口 |
| | | /// </summary> |
| | | /// <param name="model">任务完成状态</param> |
| | | /// <returns></returns> |
| | | public async Task<ResponseModel> RequestTaskQh(TaskRequestWMS model) |
| | | { |
| | | string url = Urls.WMSAddress + ":" + Urls.WMSPort; |
| | | var result = await (url + "/api/DownAPi/ReceiveWcsSignal").SetBody(model, "application/json", Encoding.UTF8).PostAsAsync<ResponseModel>(); |
| | | Log.Information("调用WMS接口-反馈任务取货完成接口" + result.ToJson()); |
| | | return result; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 调用WMS接口反馈空取异常接口 |
| | | /// </summary> |
| | | /// <param name="model">任务信息</param> |
New file |
| | |
| | | import request from '/@/utils/request'; |
| | | enum Api { |
| | | AddWcsCarTasks = '/api/wcsCarTasks/add', |
| | | DeleteWcsCarTasks = '/api/wcsCarTasks/delete', |
| | | UpdateWcsCarTasks = '/api/wcsCarTasks/update', |
| | | PageWcsCarTasks = '/api/wcsCarTasks/page', |
| | | DetailWcsCarTasks = '/api/wcsCarTasks/detail', |
| | | } |
| | | |
| | | // 增加四向车任务明细表 |
| | | export const addWcsCarTasks = (params?: any) => |
| | | request({ |
| | | url: Api.AddWcsCarTasks, |
| | | method: 'post', |
| | | data: params, |
| | | }); |
| | | |
| | | // 删除四向车任务明细表 |
| | | export const deleteWcsCarTasks = (params?: any) => |
| | | request({ |
| | | url: Api.DeleteWcsCarTasks, |
| | | method: 'post', |
| | | data: params, |
| | | }); |
| | | |
| | | // 编辑四向车任务明细表 |
| | | export const updateWcsCarTasks = (params?: any) => |
| | | request({ |
| | | url: Api.UpdateWcsCarTasks, |
| | | method: 'post', |
| | | data: params, |
| | | }); |
| | | |
| | | // 分页查询四向车任务明细表 |
| | | export const pageWcsCarTasks = (params?: any) => |
| | | request({ |
| | | url: Api.PageWcsCarTasks, |
| | | method: 'post', |
| | | data: params, |
| | | }); |
| | | |
| | | // 详情四向车任务明细表 |
| | | export const detailWcsCarTasks = (id: any) => |
| | | request({ |
| | | url: Api.DetailWcsCarTasks, |
| | | method: 'get', |
| | | data: { id }, |
| | | }); |
| | | |
| | | |
New file |
| | |
| | | <template> |
| | | <div class="wcsCarTasks-container"> |
| | | <el-dialog v-model="isShowDialog" :width="800" draggable="" :close-on-click-modal="false"> |
| | | <template #header> |
| | | <div style="color: #fff"> |
| | | <!--<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Edit /> </el-icon>--> |
| | | <span>{{ props.title }}</span> |
| | | </div> |
| | | </template> |
| | | <el-form :model="ruleForm" ref="ruleFormRef" label-width="auto" :rules="rules"> |
| | | <el-row :gutter="35"> |
| | | <el-form-item v-show="false"> |
| | | <el-input v-model="ruleForm.id" /> |
| | | </el-form-item> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> |
| | | <el-form-item label="任务号" prop="taskNo"> |
| | | <el-input v-model="ruleForm.taskNo" placeholder="请输入任务号" maxlength="20" show-word-limit clearable /> |
| | | |
| | | </el-form-item> |
| | | |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> |
| | | <el-form-item label="前置小车任务ID" prop="preId"> |
| | | <el-input v-model="ruleForm.preId" placeholder="请输入前置小车任务ID" maxlength="200" show-word-limit clearable /> |
| | | |
| | | </el-form-item> |
| | | |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> |
| | | <el-form-item label="交互路径" prop="executionPath"> |
| | | <el-input v-model="ruleForm.executionPath" placeholder="请输入交互路径" maxlength="200" show-word-limit clearable /> |
| | | |
| | | </el-form-item> |
| | | |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> |
| | | <el-form-item label="所有路径" prop="path"> |
| | | <el-input v-model="ruleForm.path" placeholder="请输入所有路径" maxlength="2147483647" show-word-limit clearable /> |
| | | |
| | | </el-form-item> |
| | | |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> |
| | | <el-form-item label="小车编号" prop="carNo"> |
| | | <el-input v-model="ruleForm.carNo" placeholder="请输入小车编号" maxlength="20" show-word-limit clearable /> |
| | | |
| | | </el-form-item> |
| | | |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20"> |
| | | <el-form-item label="状态" prop="status"> |
| | | <el-input-number v-model="ruleForm.status" placeholder="请输入状态" clearable /> |
| | | |
| | | </el-form-item> |
| | | |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="cancel">取 消</el-button> |
| | | <el-button type="primary" @click="submit">确 定</el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | <style lang="scss" scoped> |
| | | :deep(.el-select), |
| | | :deep(.el-input-number) { |
| | | width: 100%; |
| | | } |
| | | </style> |
| | | <script lang="ts" setup> |
| | | import { ref,onMounted } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | import type { FormRules } from "element-plus"; |
| | | import { formatDate } from '/@/utils/formatTime'; |
| | | import { addWcsCarTasks, updateWcsCarTasks, detailWcsCarTasks } from "/@/api/wcs/wcsCarTasks"; |
| | | |
| | | //父级传递来的参数 |
| | | var props = defineProps({ |
| | | title: { |
| | | type: String, |
| | | default: "", |
| | | }, |
| | | }); |
| | | //父级传递来的函数,用于回调 |
| | | const emit = defineEmits(["reloadTable"]); |
| | | const ruleFormRef = ref(); |
| | | const isShowDialog = ref(false); |
| | | const ruleForm = ref<any>({}); |
| | | //自行添加其他规则 |
| | | const rules = ref<FormRules>({ |
| | | }); |
| | | |
| | | // 页面加载时 |
| | | onMounted(() => { |
| | | |
| | | }); |
| | | |
| | | // 打开弹窗 |
| | | const openDialog = async (row: any) => { |
| | | // ruleForm.value = JSON.parse(JSON.stringify(row)); |
| | | // 改用detail获取最新数据来编辑 |
| | | let rowData = JSON.parse(JSON.stringify(row)); |
| | | if (rowData.id) |
| | | ruleForm.value = (await detailWcsCarTasks(rowData.id)).data.result; |
| | | else |
| | | ruleForm.value = rowData; |
| | | isShowDialog.value = true; |
| | | }; |
| | | |
| | | // 关闭弹窗 |
| | | const closeDialog = () => { |
| | | emit("reloadTable"); |
| | | isShowDialog.value = false; |
| | | }; |
| | | |
| | | // 取消 |
| | | const cancel = () => { |
| | | isShowDialog.value = false; |
| | | }; |
| | | |
| | | // 提交 |
| | | const submit = async () => { |
| | | ruleFormRef.value.validate(async (isValid: boolean, fields?: any) => { |
| | | if (isValid) { |
| | | let values = ruleForm.value; |
| | | if (ruleForm.value.id == undefined || ruleForm.value.id == null || ruleForm.value.id == "" || ruleForm.value.id == 0) { |
| | | await addWcsCarTasks(values); |
| | | } else { |
| | | await updateWcsCarTasks(values); |
| | | } |
| | | closeDialog(); |
| | | } else { |
| | | ElMessage({ |
| | | message: `表单有${Object.keys(fields).length}处验证失败,请修改后再提交`, |
| | | type: "error", |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | //将属性或者函数暴露给父组件 |
| | | defineExpose({ openDialog }); |
| | | </script> |
| | | |
| | | |
| | | |
| | | |
New file |
| | |
| | | <template> |
| | | <div class="wcsCarTasks-container"> |
| | | <el-card shadow="hover" :body-style="{ paddingBottom: '0' }"> |
| | | <el-form :model="queryParams" ref="queryForm" labelWidth="90"> |
| | | <el-row> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10"> |
| | | <el-form-item label="关键字"> |
| | | <el-input v-model="queryParams.searchKey" clearable="" placeholder="请输入模糊查询关键字"/> |
| | | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI"> |
| | | <el-form-item label="任务号"> |
| | | <el-input v-model="queryParams.taskNo" clearable="" placeholder="请输入任务号"/> |
| | | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI"> |
| | | <el-form-item label="前置小车任务ID"> |
| | | <el-input v-model="queryParams.preId" clearable="" placeholder="请输入前置小车任务ID"/> |
| | | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI"> |
| | | <el-form-item label="交互路径"> |
| | | <el-input v-model="queryParams.executionPath" clearable="" placeholder="请输入交互路径"/> |
| | | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI"> |
| | | <el-form-item label="所有路径"> |
| | | <el-input v-model="queryParams.path" clearable="" placeholder="请输入所有路径"/> |
| | | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI"> |
| | | <el-form-item label="小车编号"> |
| | | <el-input v-model="queryParams.carNo" clearable="" placeholder="请输入小车编号"/> |
| | | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10" v-if="showAdvanceQueryUI"> |
| | | <el-form-item label="状态"> |
| | | <el-input-number v-model="queryParams.status" clearable="" placeholder="请输入状态"/> |
| | | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10"> |
| | | <el-form-item > |
| | | <el-button-group style="display: flex; align-items: center;"> |
| | | <el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'wcsCarTasks:page'"> 查询 </el-button> |
| | | <el-button icon="ele-Refresh" @click="() => queryParams = {}"> 重置 </el-button> |
| | | <el-button icon="ele-ZoomIn" @click="changeAdvanceQueryUI" v-if="!showAdvanceQueryUI" style="margin-left:5px;"> 高级查询 </el-button> |
| | | <el-button icon="ele-ZoomOut" @click="changeAdvanceQueryUI" v-if="showAdvanceQueryUI" style="margin-left:5px;"> 隐藏 </el-button> |
| | | <el-button type="primary" style="margin-left:5px;" icon="ele-Plus" @click="openAddWcsCarTasks" v-auth="'wcsCarTasks:add'"> 新增 </el-button> |
| | | |
| | | </el-button-group> |
| | | </el-form-item> |
| | | |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | </el-card> |
| | | <el-card class="full-table" shadow="hover" style="margin-top: 5px"> |
| | | <el-table |
| | | :data="tableData" |
| | | style="width: 100%" |
| | | v-loading="loading" |
| | | tooltip-effect="light" |
| | | row-key="id" |
| | | @sort-change="sortChange" |
| | | border=""> |
| | | <el-table-column type="index" label="序号" width="55" align="center"/> |
| | | <el-table-column prop="taskNo" label="任务号" show-overflow-tooltip="" /> |
| | | <el-table-column prop="preId" label="前置小车任务ID" show-overflow-tooltip="" /> |
| | | <el-table-column prop="executionPath" label="交互路径" show-overflow-tooltip="" /> |
| | | <el-table-column prop="path" label="所有路径" show-overflow-tooltip="" /> |
| | | <el-table-column prop="carNo" label="小车编号" show-overflow-tooltip="" /> |
| | | <el-table-column prop="status" label="状态" show-overflow-tooltip="" /> |
| | | <el-table-column label="操作" width="140" align="center" fixed="right" show-overflow-tooltip="" v-if="auth('wcsCarTasks:update') || auth('wcsCarTasks:delete')"> |
| | | <template #default="scope"> |
| | | <el-button icon="ele-Edit" size="small" text="" type="primary" @click="openEditWcsCarTasks(scope.row)" v-auth="'wcsCarTasks:update'"> 编辑 </el-button> |
| | | <el-button icon="ele-Delete" size="small" text="" type="primary" @click="delWcsCarTasks(scope.row)" v-auth="'wcsCarTasks:delete'"> 删除 </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <el-pagination |
| | | v-model:currentPage="tableParams.page" |
| | | v-model:page-size="tableParams.pageSize" |
| | | :total="tableParams.total" |
| | | :page-sizes="[10, 20, 50, 100, 200, 500]" |
| | | size="small" |
| | | background="" |
| | | @size-change="handleSizeChange" |
| | | @current-change="handleCurrentChange" |
| | | layout="total, sizes, prev, pager, next, jumper" |
| | | /> |
| | | <printDialog |
| | | ref="printDialogRef" |
| | | :title="printWcsCarTasksTitle" |
| | | @reloadTable="handleQuery" /> |
| | | <editDialog |
| | | ref="editDialogRef" |
| | | :title="editWcsCarTasksTitle" |
| | | @reloadTable="handleQuery" |
| | | /> |
| | | </el-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup="" name="wcsCarTasks"> |
| | | import { ref } from "vue"; |
| | | import { ElMessageBox, ElMessage } from "element-plus"; |
| | | import { auth } from '/@/utils/authFunction'; |
| | | |
| | | import { formatDate } from '/@/utils/formatTime'; |
| | | |
| | | |
| | | import printDialog from '/@/views/system/print/component/hiprint/preview.vue' |
| | | import editDialog from '/@/views/wcs/wcsCarTasks/component/editDialog.vue' |
| | | import { pageWcsCarTasks, deleteWcsCarTasks } from '/@/api/wcs/wcsCarTasks'; |
| | | |
| | | const showAdvanceQueryUI = ref(false); |
| | | const printDialogRef = ref(); |
| | | const editDialogRef = ref(); |
| | | const loading = ref(false); |
| | | const tableData = ref<any>([]); |
| | | const queryParams = ref<any>({}); |
| | | const tableParams = ref({ |
| | | page: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | }); |
| | | |
| | | const printWcsCarTasksTitle = ref(""); |
| | | const editWcsCarTasksTitle = ref(""); |
| | | |
| | | // 改变高级查询的控件显示状态 |
| | | const changeAdvanceQueryUI = () => { |
| | | showAdvanceQueryUI.value = !showAdvanceQueryUI.value; |
| | | } |
| | | |
| | | // 查询操作 |
| | | const handleQuery = async () => { |
| | | loading.value = true; |
| | | var res = await pageWcsCarTasks(Object.assign(queryParams.value, tableParams.value)); |
| | | tableData.value = res.data.result?.items ?? []; |
| | | tableParams.value.total = res.data.result?.total; |
| | | loading.value = false; |
| | | }; |
| | | |
| | | // 列排序 |
| | | const sortChange = async (column: any) => { |
| | | queryParams.value.field = column.prop; |
| | | queryParams.value.order = column.order; |
| | | await handleQuery(); |
| | | }; |
| | | |
| | | // 打开新增页面 |
| | | const openAddWcsCarTasks = () => { |
| | | editWcsCarTasksTitle.value = '添加四向车任务明细表'; |
| | | editDialogRef.value.openDialog({}); |
| | | }; |
| | | |
| | | // 打开打印页面 |
| | | const openPrintWcsCarTasks = async (row: any) => { |
| | | printWcsCarTasksTitle.value = '打印四向车任务明细表'; |
| | | } |
| | | |
| | | // 打开编辑页面 |
| | | const openEditWcsCarTasks = (row: any) => { |
| | | editWcsCarTasksTitle.value = '编辑四向车任务明细表'; |
| | | editDialogRef.value.openDialog(row); |
| | | }; |
| | | |
| | | // 删除 |
| | | const delWcsCarTasks = (row: any) => { |
| | | ElMessageBox.confirm(`确定要删除吗?`, "提示", { |
| | | confirmButtonText: "确定", |
| | | cancelButtonText: "取消", |
| | | type: "warning", |
| | | }) |
| | | .then(async () => { |
| | | await deleteWcsCarTasks(row); |
| | | handleQuery(); |
| | | ElMessage.success("删除成功"); |
| | | }) |
| | | .catch(() => {}); |
| | | }; |
| | | |
| | | // 改变页面容量 |
| | | const handleSizeChange = (val: number) => { |
| | | tableParams.value.pageSize = val; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // 改变页码序号 |
| | | const handleCurrentChange = (val: number) => { |
| | | tableParams.value.page = val; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | handleQuery(); |
| | | </script> |
| | | <style scoped> |
| | | :deep(.el-input), |
| | | :deep(.el-select), |
| | | :deep(.el-input-number) { |
| | | width: 100%; |
| | | } |
| | | </style> |
| | | |