|  |  |  | 
|---|
|  |  |  | using System; | 
|---|
|  |  |  | using System.Collections.Generic; | 
|---|
|  |  |  | using System.ComponentModel; | 
|---|
|  |  |  | using System.ComponentModel.DataAnnotations; | 
|---|
|  |  |  | using System.ComponentModel.Design; | 
|---|
|  |  |  | using System.Data; | 
|---|
|  |  |  | using System.Linq; | 
|---|
|  |  |  | 
|---|
|  |  |  | using System.Threading.Tasks; | 
|---|
|  |  |  | using Model.ModelDto.BllSoDto; | 
|---|
|  |  |  | using Model.ModelDto.SysDto; | 
|---|
|  |  |  | using Serilog; | 
|---|
|  |  |  | using SqlSugar; | 
|---|
|  |  |  | using WMS.Entity.BllAsnEntity; | 
|---|
|  |  |  | using WMS.Entity.BllCheckEntity; | 
|---|
|  |  |  | 
|---|
|  |  |  | using WMS.Entity.LogEntity; | 
|---|
|  |  |  | using WMS.Entity.SysEntity; | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | namespace WMS.DAL | 
|---|
|  |  |  | { | 
|---|
|  |  |  | public class Common | 
|---|
|  |  |  | { | 
|---|
|  |  |  | //public static readonly DataContext Db = new DataContext(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /// <summary> | 
|---|
|  |  |  | /// 获取深度为1的储位号(根据深度为2的储位号) | 
|---|
|  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /// <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 | 
|---|
|  |  |  | { | 
|---|
|  |  |  | 
|---|
|  |  |  | [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); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|