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; using static Elastic.Clients.Elasticsearch.JoinField; namespace WCS.Application.Util; public static class FourWayCarUtil { private static readonly ISqlSugarClient _db = SqlSugarSetup.ITenant.GetConnectionScope(SqlSugarConst.MainConfigId); /// /// /// /// 集合 /// 移动类型 0:移动 1:移货 /// public static List GetCarPathUp(List list,int moveType) { for (int i = 0; i < list.Count; i++) { if (i > 0 && i < list.Count - 1) { if (list[i].X == list[i + 1].X) { list[i].NodeCom = 2; } else if (list[i].Y == list[i + 1].Y) { list[i].NodeCom = 3; } } if (i== 0) { list[i].IsSendPlc = true; if (moveType == 0) { if (list[i + 1] == null) { continue; } } else { list[i].NodeCom = 1; } } if (i == list.Count-1) { list[i].IsSendPlc = true; if (moveType == 0) { list[i].NodeCom = list[i + 1].NodeCom; } else { list[i].NodeCom = 4; } } } return list; } /// /// 获取小车路径 /// /// 起始位置 /// 目标位置 /// 是否载货0:未载货 1:已载货 /// public static List GetCarPath(string startLocation, string endLocation, 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() .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(); // 定义上一位置与目标位置字典 var cameFrom = new Dictionary(); // 定义上一位置与目标位置的实际距离字典 var gScore = new Dictionary(); // 定义上一位置与目标位置的预估距离字典 var fScore = new Dictionary(); // 存储最优距离,及起始节点 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(); 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 (current.X == neighbor.X) //{ // current.NodeCom = 2; //} //else if (current.Y == neighbor.Y) //{ // current.NodeCom = 3; //} //// 补充参数 //if (current.Equals(start)) //{ // current.NodeCom = moveType; // current.IsSendPlc = true; //} //if (neighbor.Equals(end)) //{ // //neighbor.NodeCom = moveType != 0 ? 2:0 ; // if (moveType == 1) // { // neighbor.NodeCom = 4; // } // else // { // neighbor.NodeCom = current.NodeCom; // } // neighbor.IsSendPlc = true; //} if (currentModel.Make != locationModel.Make) { //if (current.X != neighbor.X) //{ // current.IsSendPlc = true; //} if (current.Y > neighbor.Y) { current.IsSendPlc = true; } else if (current.Y < neighbor.Y) { neighbor.IsSendPlc = true; } //current.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; } /// /// 获取小车移动的目标位置 /// /// 起始位置 /// 不能存放的储位字符串 /// 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() .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(); // // 存储小车可运行的方向 // var validDirections = new List(); // 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().ToList(); var maxNo = list.Max(m => m.CarTaskNo); if (maxNo!=null && maxNo > 0) { if (maxNo++ > 65535) { return 1; } return (int)maxNo++; } return 1; } /// /// 获取总任务表下发ID主键 /// /// public static int GetTaskId() { var list = _db.Queryable().ToList(); var maxNo = list.Max(m => m.TaskId); if (maxNo != null && maxNo > 0) { if (maxNo++ > 99999999) { return 1; } return (int)maxNo++; } return 1; } /// /// 计算曼哈顿距离 /// /// 起始位置 /// 目标位置 /// 位置距离 private static int Heuristic(CarModel start, CarModel end) { return Math.Abs(start.X - end.X) + Math.Abs(start.Y - end.Y); } /// /// 重构完整路径 /// /// /// /// private static List ReconstructPath(Dictionary cameFrom, CarModel current) { var path = new List { current }; while (cameFrom.ContainsKey(current)) { current = cameFrom[current]; path.Insert(0, current); } return path; } } public class CarModel : IComparable { /// /// 行=X /// public int X { get; set; } /// /// 列=Y /// public int Y { get; set; } /// /// 层=Z /// public int Z { get; set; } /// /// 节点命令 1:顶货 2:子通道运行 3:主通道运行 4:放货 /// public int NodeCom { get; set; } /// /// 是否下发plc /// 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 { /// /// 小车IP /// public string CarPlcIp { get; set; } /// /// 顺序等级 /// public int Level { get; set; } /// /// 行=X /// public int X { get; set; } /// /// 列=Y /// public int Y { get; set; } /// /// 层=Z /// public int Z { get; set; } }