chengsc
2025-04-25 5883a139074dd7493d9f0d776540f196d55d1034
添加分配下发任务方法
9个文件已添加
7个文件已修改
2378 ■■■■ 已修改文件
Admin.NET/WCS.Application/Entity/WcsCarTasks.cs 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Entity/WcsStorageLocat.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Entity/WcsTask.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Enum/TaskEnum.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/OpenApi/OpenApi.cs 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/PLC/PLCService.cs 321 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/PLC/PLCTaskAction.cs 375 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Service/WcsCarTasks/Dto/WcsCarTasksDto.cs 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Service/WcsCarTasks/Dto/WcsCarTasksInput.cs 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Service/WcsCarTasks/Dto/WcsCarTasksOutput.cs 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Service/WcsCarTasks/WcsCarTasksService.cs 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Util/FourWayCarUtil.cs 490 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Util/HttpService.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/api/wcs/wcsCarTasks.ts 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/views/wcs/wcsCarTasks/component/editDialog.vue 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/views/wcs/wcsCarTasks/index.vue 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Entity/WcsCarTasks.cs
New file
@@ -0,0 +1,57 @@
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; }
}
Admin.NET/WCS.Application/Entity/WcsStorageLocat.cs
@@ -113,4 +113,21 @@
    [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; }
}
Admin.NET/WCS.Application/Entity/WcsTask.cs
@@ -44,6 +44,12 @@
    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)]
Admin.NET/WCS.Application/Enum/TaskEnum.cs
@@ -73,6 +73,12 @@
    /// </summary>
    [Description("任务取消")]
    Cancell = 4,
    /// <summary>
    /// 任务取消
    /// </summary>
    [Description("手动完成任务")]
    CompleteHand = 5,
}
/// <summary>
Admin.NET/WCS.Application/OpenApi/OpenApi.cs
@@ -377,33 +377,8 @@
                {
                    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 == "输送线");
@@ -431,52 +406,6 @@
                                    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();
@@ -493,6 +422,7 @@
                            await _db.Insertable(modTaskMonitor).ExecuteCommandAsync();
                            HubUtil.PublicTask(modTask.Adapt<WcsTaskOutput>());
                            HubUtil.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>());
                            //反馈WMS任务完成
                            if (modTask.Origin == "WMS")
                            {
                                HttpService httpService = new HttpService();
@@ -503,125 +433,23 @@
                                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"://放货申请
@@ -687,7 +515,7 @@
                            HubUtil.PublicTaskMonitor(taskMonitor.Adapt<WcsTaskMonitorOutput>());
                        }
                        break;
                    case "cancel"://任务结束
                    case "cancel"://任务取消
                        {
                            PLCService.AGVStatus = false;
                            modTask.IsSuccess = TaskSuccessEnum.Fail;
Admin.NET/WCS.Application/PLC/PLCService.cs
@@ -5,6 +5,8 @@
using DocumentFormat.OpenXml.Spreadsheet;
using Furion.Logging;
using IoTClient;
using NewLife.Reflection;
using WCS.Application.Entity;
using WCS.Application.Util;
namespace WCS.Application;
@@ -154,52 +156,70 @@
                    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)");
@@ -210,35 +230,52 @@
                    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");
@@ -256,29 +293,21 @@
                        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)
                    {
@@ -293,17 +322,17 @@
                        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>());
                        }
                    }
@@ -314,7 +343,7 @@
                    //小车状态异常
                    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 == "任务号");
@@ -349,7 +378,7 @@
                            }
                        }
                    }
                    Console.WriteLine($"穿梭车{modDevice.PlcIdIP}异常");
                    var modPosError = modDevice.listStation.FirstOrDefault(s => s.Text == "错误码");
                    var (result, valueError) = plcConn.GetDBValue(modPosError.PosType, modPosError.PlcPos);
@@ -599,160 +628,22 @@
    /// <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(),
Admin.NET/WCS.Application/PLC/PLCTaskAction.cs
@@ -2,13 +2,18 @@
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;
@@ -35,6 +40,7 @@
    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>();
@@ -137,6 +143,7 @@
            StartRead();
            ConnectionStatus();
            StartWatchAlarm();
            //AssignTasks();
            //StartWatchPosition();
        }
    }
@@ -334,6 +341,370 @@
            }, 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>
    /// 连接状态线程
@@ -692,6 +1063,10 @@
            }
        });
    }
    /// <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);
Admin.NET/WCS.Application/Service/WcsCarTasks/Dto/WcsCarTasksDto.cs
New file
@@ -0,0 +1,89 @@
// 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; }
    }
Admin.NET/WCS.Application/Service/WcsCarTasks/Dto/WcsCarTasksInput.cs
New file
@@ -0,0 +1,170 @@
// 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
    {
    }
Admin.NET/WCS.Application/Service/WcsCarTasks/Dto/WcsCarTasksOutput.cs
New file
@@ -0,0 +1,91 @@
// 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; }
    }
Admin.NET/WCS.Application/Service/WcsCarTasks/WcsCarTasksService.cs
New file
@@ -0,0 +1,127 @@
// 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();
    }
}
Admin.NET/WCS.Application/Util/FourWayCarUtil.cs
New file
@@ -0,0 +1,490 @@
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; }
}
Admin.NET/WCS.Application/Util/HttpService.cs
@@ -246,6 +246,19 @@
    }
    /// <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>
Web/src/api/wcs/wcsCarTasks.ts
New file
@@ -0,0 +1,50 @@
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 },
        });
Web/src/views/wcs/wcsCarTasks/component/editDialog.vue
New file
@@ -0,0 +1,156 @@
<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>
Web/src/views/wcs/wcsCarTasks/index.vue
New file
@@ -0,0 +1,212 @@
<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>