bklLiudl
2025-04-08 a82e815f97b789dc4ca208df09dd96534a023f4a
测试
2个文件已修改
1个文件已添加
520 ■■■■■ 已修改文件
Wms/WMS.DAL/Common.cs 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Wms/WMS.Entity/BllAsnEntity/WCSStorageLocat.cs 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Wms/Wms/Controllers/BllAsnController.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Wms/WMS.DAL/Common.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.Design;
using System.Data;
using System.Linq;
@@ -8,6 +9,7 @@
using System.Threading.Tasks;
using Model.ModelDto.BllSoDto;
using Model.ModelDto.SysDto;
using Serilog;
using SqlSugar;
using WMS.Entity.BllAsnEntity;
using WMS.Entity.BllCheckEntity;
@@ -17,12 +19,13 @@
using WMS.Entity.LogEntity;
using WMS.Entity.SysEntity;
namespace WMS.DAL
{
    public class Common
    {
        //public static readonly DataContext Db = new DataContext();
        /// <summary>
        /// 获取深度为1的储位号(根据深度为2的储位号)
@@ -477,6 +480,268 @@
            }
        }
        /// <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")
        {
            var _db = DataContext.Db;
            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 不用绘制地图,直接根据储位表计算 liudl
            // 绘制仓库地图 -1:不可用 0:主通道  1:子通道
            //int[,] warehouseMap = new int[maxRow, maxColumn];
            //for (int row = 0; row < maxRow; row++)
            //{
            //    for (int column = 0; column < maxColumn; column++)
            //    {
            //        // 获取当前位置储位信息
            //        var locationModel = locationModels.First(it => it.Row == row && it.Column == column);
            //        // 不存在此位置信息
            //        if (locationModel == null)
            //        {
            //            warehouseMap[row, column] = -1;
            //            continue;
            //        }
            //        // 储位状态为损坏不可通行 储位状态为屏蔽为可通行不可存储托盘
            //        if (locationModel.Flag == "2")
            //        {
            //            warehouseMap[row, column] = -1;
            //            continue;
            //        }
            //        warehouseMap[row, column] = int.Parse(locationModel.Make);
            //    }
            //}
            #endregion
            #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 ex)
            {
                throw ex;
            }
            return null;
        }
        /// <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 enum InOutFlag
    {
@@ -522,4 +787,66 @@
        [Description("质检请验单")]
        QC
    }
    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);
        }
    }
}
Wms/WMS.Entity/BllAsnEntity/WCSStorageLocat.cs
New file
@@ -0,0 +1,189 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Text;
namespace WMS.Entity.BllAsnEntity
{
    [SugarTable("WCSStorageLocat")]
    public class WCSStorageLocat
    {
        /// <summary>
        /// 储位号
        /// </summary>
        [SugarColumn(ColumnName = "LocatNo", ColumnDescription = "储位号", Length = 20)]
        public string LocatNo { get; set; }
        /// <summary>
        /// 仓库编号
        /// </summary>
        [SugarColumn(ColumnName = "WareHouseNo", ColumnDescription = "仓库编号", Length = 20)]
        public string WareHouseNo { get; set; }
        /// <summary>
        /// 所属巷道
        /// </summary>
        [SugarColumn(ColumnName = "RoadwayNo", ColumnDescription = "所属巷道", Length = 20)]
        public string? RoadwayNo { get; set; }
        /// <summary>
        /// 所属区域
        /// </summary>
        [SugarColumn(ColumnName = "AreaNo", ColumnDescription = "所属区域", Length = 20)]
        public string AreaNo { get; set; }
        /// <summary>
        /// 状态  0:空闲 1:有货物  2:入库中  3:出库中  4:移入中  5:移出中
        /// </summary>
        [SugarColumn(ColumnName = "Status", ColumnDescription = "状态", Length = 3)]
        public string Status { get; set; }
        /// <summary>
        /// 储位标志 0:正常 1:屏蔽  2:损坏
        /// </summary>
        [SugarColumn(ColumnName = "Flag", ColumnDescription = "储位标志", Length = 3)]
        public string Flag { get; set; }
        /// <summary>
        /// 深度
        /// </summary>
        [SugarColumn(ColumnName = "Depth", ColumnDescription = "深度", Length = 3)]
        public string Depth { get; set; }
        /// <summary>
        /// 排
        /// </summary>
        [SugarColumn(ColumnName = "Row", ColumnDescription = "排")]
        public int Row { get; set; }
        /// <summary>
        /// 列
        /// </summary>
        [SugarColumn(ColumnName = "Column", ColumnDescription = "列")]
        public int Column { get; set; }
        /// <summary>
        /// 层
        /// </summary>
        [SugarColumn(ColumnName = "Layer", ColumnDescription = "层")]
        public int Layer { get; set; }
        /// <summary>
        /// 高度
        /// </summary>
        [SugarColumn(ColumnName = "Height", ColumnDescription = "高度")]
        public int? Height { get; set; }
        /// <summary>
        /// 重量
        /// </summary>
        [SugarColumn(ColumnName = "Weight", ColumnDescription = "重量")]
        public int? Weight { get; set; }
        /// <summary>
        /// 存储环境
        /// </summary>
        [SugarColumn(ColumnName = "Temperature", ColumnDescription = "存储环境", Length = 3)]
        public string? Temperature { get; set; }
        /// <summary>
        /// 物料号
        /// </summary>
        [SugarColumn(ColumnName = "SkuNo", ColumnDescription = "物料号", Length = 32)]
        public string? SkuNo { get; set; }
        /// <summary>
        /// 托盘号
        /// </summary>
        [SugarColumn(ColumnName = "PalletNo", ColumnDescription = "托盘号", Length = 32)]
        public string? PalletNo { get; set; }
        /// <summary>
        /// 创建者部门Id
        /// </summary>
        [SugarColumn(ColumnName = "CreateOrgId", ColumnDescription = "创建者部门Id")]
        public long? CreateOrgId { get; set; }
        /// <summary>
        /// 创建者部门名称
        /// </summary>
        [SugarColumn(ColumnName = "CreateOrgName", ColumnDescription = "创建者部门名称", Length = 64)]
        public string? CreateOrgName { get; set; }
        /// <summary>
        /// 出口
        /// </summary>
        [SugarColumn(ColumnName = "AisleOne", ColumnDescription = "出口", Length = 20)]
        public string AisleOne { get; set; }
        /// <summary>
        /// 出口2
        /// </summary>
        [SugarColumn(ColumnName = "AisleTwo", ColumnDescription = "出口2", Length = 20)]
        public string AisleTwo { get; set; }
        /// <summary>
        /// 类型 0:主通道  1:储位
        /// </summary>
        [SugarColumn(ColumnName = "Make", ColumnDescription = "类型 0:主通道  1:储位 2:子通道", Length = 3)]
        public string Make { get; set; }
        /// <summary>
        /// 创建者Id
        /// </summary>
        [SugarColumn(ColumnDescription = "创建者Id", IsOnlyIgnoreUpdate = true)]
        public virtual long? CreateUserId { get; set; }
        ///// <summary>
        ///// 创建者
        ///// </summary>
        //[Newtonsoft.Json.JsonIgnore]
        //[System.Text.Json.Serialization.JsonIgnore]
        //[Navigate(NavigateType.OneToOne, nameof(CreateUserId))]
        //public virtual SysUser CreateUser { get; set; }
        /// <summary>
        /// 创建者姓名
        /// </summary>
        [SugarColumn(ColumnDescription = "创建者姓名", Length = 64, IsOnlyIgnoreUpdate = true)]
        public virtual string? CreateUserName { get; set; }
        /// <summary>
        /// 修改者Id
        /// </summary>
        [SugarColumn(ColumnDescription = "修改者Id")]
        public virtual long? UpdateUserId { get; set; }
        ///// <summary>
        ///// 修改者
        ///// </summary>
        //[Newtonsoft.Json.JsonIgnore]
        //[System.Text.Json.Serialization.JsonIgnore]
        //[Navigate(NavigateType.OneToOne, nameof(UpdateUserId))]
        //public virtual SysUser UpdateUser { get; set; }
        /// <summary>
        /// 修改者姓名
        /// </summary>
        [SugarColumn(ColumnDescription = "修改者姓名", Length = 64)]
        public virtual string? UpdateUserName { get; set; }
        /// <summary>
        /// 软删除
        /// </summary>
        [SugarColumn(ColumnDescription = "软删除")]
        public virtual bool IsDelete { get; set; } = false;
    }
}
Wms/Wms/Controllers/BllAsnController.cs
@@ -16,6 +16,7 @@
using System.Diagnostics;
using Model.ModelDto.SysDto;
using static System.Reflection.Metadata.BlobBuilder;
using WMS.DAL;
namespace Wms.Controllers
{
@@ -63,6 +64,7 @@
        [ServiceFilter(typeof(ApiResponseActionFilter))]
        public async Task<SqlSugarPagedList> GetArrivalNoticeList(ArrivalNoticeVm model)
        {
            var models = Common.GetCarPath("141401", "040401", 1,"1");
            RefAsync<int> count = new RefAsync<int>();
            var bolls = await _arrivalNoticeSvc.GetArrivalNoticeList(model, count);