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.Text; 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.BllQualityEntity; using WMS.Entity.BllSoEntity; using WMS.Entity.Context; using WMS.Entity.LogEntity; using WMS.Entity.SysEntity; namespace WMS.DAL { public class Common { //public static readonly DataContext Db = new DataContext(); /// /// 获取深度为1的储位号(根据深度为2的储位号) /// /// 仓库号 /// 储位号 /// public SysStorageLocat GetLocateNoDepth1(string houseNo, string locateNo) { try { var Db = DataContext.Db; //获取储位信息 //var sql = $@"select * from SysStorageLocat where WareHouseNo = '{houseNo}' and LocatNo = '{locateNo}'; "; var locate = Db.Queryable().Where(s => s.WareHouseNo == houseNo && s.LocatNo == locateNo).First(); if (locate == null) { throw new Exception($"未查询到仓库号为{houseNo}、储位号为{locateNo}的信息"); } //获取深度为2的储位信息 //var sql2 = $@"select * from SysStorageLocat where WareHouseNo = '{houseNo}' and row = {locate.Row} and [Column] = {locate.Column} and Layer = {locate.Layer} and Depth = '01'; "; //var data = Db.Ado.SqlQuery(sql2).First(); var data = Db.Queryable().Where(s => s.WareHouseNo == houseNo && s.Row == locate.Row && s.Column == locate.Column && s.Layer == locate.Layer && s.Depth == "01").First(); if (data == null) { throw new Exception($"未查询到仓库号为{houseNo}、储位号为{locateNo}的里面储位信息"); } return data; } catch (Exception e) { throw new Exception(e.Message); } } /// /// 获取深度为2的储位号(根据深度为1的储位号) /// /// 仓库号 /// 储位号 /// public SysStorageLocat GetLocateNoDepth2(string houseNo, string locateNo) { try { var Db = DataContext.Db; //获取储位信息 //var sql = $@"select * from SysStorageLocat where WareHouseNo = '{houseNo}' and LocatNo = '{locateNo}'; "; //var locate = Db.Ado.SqlQuery(sql).First(); var locate = Db.Queryable().Where(s => s.WareHouseNo == houseNo && s.LocatNo == locateNo).First(); if (locate == null) { throw new Exception($"未查询到仓库号为{houseNo}、储位号为{locateNo}的信息"); } //获取深度为2的储位信息 //var sql2 = $@"select * from SysStorageLocat where WareHouseNo = '{houseNo}' and row = {locate.Row} and [Column] = {locate.Column} and Layer = {locate.Layer} and Depth = '02'; "; //var data = Db.Ado.SqlQuery(sql2).First(); var data = Db.Queryable().Where(s => s.WareHouseNo == houseNo && s.Row == locate.Row && s.Column == locate.Column && s.Layer == locate.Layer && s.Depth == "02").First(); if (data == null) { throw new Exception($"未查询到仓库号为{houseNo}、储位号为{locateNo}的里面储位信息"); } return data; } catch (Exception e) { throw new Exception(e.Message); } } /// /// 获取移库目标库位 /// /// 需要移动的库位地址 /// 巷道 /// 物料编码 /// 目标库位地址 为"" 直接下发两次出库指令 public string MoveAddress(string oldAddress, string roadWay, string skuNo)//01020201 排-列-层-深度 { string nowAddress = ""; var Db = DataContext.Db; //根据物料编码获取对应区域 var skuCategoryNo = Db.Queryable().Where(a => a.IsDel == "0" && a.SkuNo == skuNo).Select(a => a.CategoryNo).First(); var category = Db.Queryable().First(a => a.IsDel == "0" && a.CategoryNo == skuCategoryNo); // 获取移库目标储位 var row = int.Parse(oldAddress.Substring(0, 2)); var lie = int.Parse(oldAddress.Substring(2, 2)); var ceng = int.Parse(oldAddress.Substring(4, 2)); //var sqlString = $@"select LocatNo,[Row],[Column],Layer, (ABS(Row-{row}) + ABS([Column]-{lie}) + ABS(Layer-{ceng})) as distNum from SysStorageLocat where Flag = '0' and Status = '0' and Depth = '02' and RoadwayNo = '{roadWay}' and AreaNo in ('{category.AreaNo}') order by distNum;"; //var addressModels = Db.Ado.SqlQuery(sqlString).ToList(); var listArea = category.AreaNo.Split(','); var addressModels = Db.Queryable() .Where(s => s.Flag == "0" && s.Status == "0" && s.Depth == "02" && s.RoadwayNo == roadWay && listArea.Contains(s.AreaNo)) .Select(s => new AddressCls() { LocatNo = s.LocatNo, Row = s.Row, Column = s.Column, Layer = s.Layer, DistNum = SqlFunc.Abs(s.Row - row) + SqlFunc.Abs(s.Column - lie) + SqlFunc.Abs(s.Layer - ceng) }) .OrderBy(s => s.DistNum) .ToList(); if (addressModels.Count > 0) // 判断同巷道内排空库位 { var listLocaete = new List(); foreach (var item in addressModels) { // 目标内库位对应的外库位 string addressee = item.LocatNo.Substring(0, 6) + "01"; // 判断目标库位的外库位是否存在货物 (正常情况下正在移入情况不存在,因为移库先移入里面,后移入外面) //SlotStatus 0: 空储位 1:有货 2:正在入库 3:正在出库 4:正在移入 5:正在移出 //sqlString = $"select count(*) from SysStorageLocat where LocatNo = '{addressee}' and Status = '0' and Flag in ('0','1') ; "; //var rowNum = Db.Ado.SqlQuery(sqlString).First(); var rowNum = Db.Queryable().Count(s => s.LocatNo == addressee && s.Status == "0" && (s.Flag == "0" || s.Flag == "1")); if (rowNum == 0) { continue; } else { nowAddress = item.LocatNo; break; } } } if (nowAddress == "") { // 判断同巷道外排空库位 //sqlString = $@"select LocatNo,[Row],[Column],Layer, (ABS(Row-{row}) + ABS([Column]-{lie}) + ABS(Layer-{ceng})) as distNum // from SysStorageLocat // where Flag = '0' and Status = '0' and Depth = '01' and RoadwayNo = '{roadWay}' and AreaNo in '{category}' // order by distNum;"; //var adderModeling = Db.Ado.SqlQuery(sqlString).ToList(); var adderModeling = Db.Queryable() .Where(s => s.Flag == "0" && s.Status == "0" && s.Depth == "01" && s.RoadwayNo == roadWay && listArea.Contains(s.AreaNo)) .Select(s => new AddressCls() { LocatNo = s.LocatNo, Row = s.Row, Column = s.Column, Layer = s.Layer, DistNum = SqlFunc.Abs(s.Row - row) + SqlFunc.Abs(s.Column - lie) + SqlFunc.Abs(s.Layer - ceng) }) .OrderBy(s => s.DistNum) .ToList(); if (adderModeling.Count > 0) { nowAddress = adderModeling[0].LocatNo; } else { // 库内不存在空储位 nowAddress = ""; } } return nowAddress; } /// /// 根据前缀获取最大单据号 /// /// 单据前缀 /// public string GetMaxNo(string codeFlag) { try { var maxNo = ""; //获取异常号 maxNo = SelectMaxNo(codeFlag); if (!string.IsNullOrEmpty(maxNo)) { maxNo = maxNo.Substring(codeFlag.Length); } //获取数据库时间八位 string date = DateTime.Now.ToString("yyyyMMdd").Trim(); string no = ""; if (string.IsNullOrEmpty(maxNo)) { no = codeFlag + date + "00001"; } else { if (maxNo.Substring(0, 8) == date) { int lastNo = Convert.ToInt32(maxNo.Substring(8, 5)) + 1; no = codeFlag + date + (lastNo.ToString().PadLeft(5, '0')); } else { no = codeFlag + date + "00001"; } } return no; } catch (Exception e) { Console.WriteLine(e); throw; } } /// /// 根据前缀判断出当前最大单据号 /// /// 单据前缀 /// public string SelectMaxNo(string codeFlag) { try { string orderNo = ""; if (codeFlag == InOutFlag.SO.ToString())//出库 { var list = DataContext.Db.Queryable().Where(m => m.SONo.StartsWith("SO")).ToList(); orderNo = list.Max(a => a.SONo); } if (codeFlag == InOutFlag.WSO.ToString())//波次出库 { var list = DataContext.Db.Queryable().Where(m => m.WaveNo.StartsWith("WSO")).ToList(); orderNo = list.Select(a => a.WaveNo).Max(); } else if (codeFlag == InOutFlag.ASN.ToString())//入库 { var list = DataContext.Db.Queryable().Where(m => m.ASNNo.StartsWith("ASN")).ToList(); orderNo = list.Select(a => a.ASNNo).Max(); } else if (codeFlag == InOutFlag.PM.ToString())//移库 { } else if (codeFlag == InOutFlag.CR.ToString())//盘点 { var list = DataContext.Db.Queryable().Where(m => m.CRNo.StartsWith("CR")).ToList(); orderNo = list.Select(a => a.CRNo).Max(); } else if (codeFlag == InOutFlag.TK.ToString())//任务 { var list = DataContext.Db.Queryable().Where(m => m.TaskNo.StartsWith("TK")).ToList(); orderNo = list.Max(m => m.TaskNo); } else if (codeFlag == InOutFlag.EX.ToString())//异常 { var list = DataContext.Db.Queryable().Where(m => m.ExceptionNo.StartsWith("EX")).ToList(); orderNo = list.Max(a => a.ExceptionNo); } else if (codeFlag == InOutFlag.QC.ToString())//质检请验单 { var list = DataContext.Db.Queryable().Where(m => m.QcNo.StartsWith("QC")).ToList(); orderNo = list.Max(a => a.QcNo); } return orderNo; } catch (Exception e) { throw new Exception(e.Message); } } /// /// 执行sql语句获取行数 /// /// 查询语句 /// 行数 public int GetRowCount(string sqlString) { try { var db = DataContext.Db; var rowCount = db.Ado.GetInt(sqlString); return rowCount; } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// 判断托盘是否在入库中 /// /// 托盘号 /// public LogTask GetImTask(string palletNo) { try { var db = DataContext.Db; //查询托盘是否有正在入库的任务 var imTask = db.Queryable().First(m => m.PalletNo == palletNo && m.IsDel == "0" && m.Status == "1"); return imTask; } catch (Exception ex) { throw new Exception(ex.Message); } } /// /// //获取包装信息 托盘内数、箱内数 /// /// /// 托盘包装数 /// 箱包装数 public void GetPackQtyInfo(string packNo, ref int pNum, ref int bNum) { try { var db = DataContext.Db; var pack = db.Queryable().First(m => m.PackagNo == packNo); if (pack != null) { if (pack.L5Num.HasValue) { pNum = Convert.ToInt32(pack.L5Num); bNum = Convert.ToInt32(pack.L4Num); } else if (pack.L4Num.HasValue) { pNum = Convert.ToInt32(pack.L4Num); bNum = Convert.ToInt32(pack.L3Num); } else if (pack.L3Num.HasValue) { pNum = Convert.ToInt32(pack.L3Num); bNum = Convert.ToInt32(pack.L2Num); } else if (pack.L2Num.HasValue) { pNum = Convert.ToInt32(pack.L2Num); bNum = Convert.ToInt32(pack.L1Num); } else if (pack.L1Num.HasValue) { pNum = Convert.ToInt32(pack.L1Num); bNum = Convert.ToInt32(pack.L1Num); } } } catch (Exception e) { throw new Exception(e.Message); } } /// /// 获取系统功能设定小数位数设定值,默认为4位 /// /// public double GetSysDecimalDigit() { try { var db = DataContext.Db; var funSet = db.Queryable().First(m => m.IsDel == "0" && m.FunSetName == "小数点位数" && m.IsEnable == "NO"); var allotSet = 4;//分配规则设定 if (funSet != null) { switch (funSet.SetValue) { case "1": allotSet = 1; break; case "2": allotSet = 2; break; case "3": allotSet = 3; break; case "4": allotSet = 4; break; default: allotSet = 4;// 默认:4 break; } } return allotSet; } catch (Exception e) { throw new Exception(e.Message); } } /// /// 根据小数位数值转换展示值 /// /// /// /// public double GetViewVal(int digit, decimal? val) { try { if (val == null) { throw new Exception("val值为空,请核实"); } decimal s = (decimal)val; string r = "0"; double data; //decimal data2 = 0.00M; switch (digit) { case 1: r = s.ToString("0.0"); data = Math.Round(double.Parse(r), 1); break; case 2: r = s.ToString("0.00"); data = Math.Round(double.Parse(r), 2); break; case 3: r = s.ToString("0.000"); data = Math.Round(double.Parse(r), 3); break; case 4: r = s.ToString("0.0000"); //var ss = double.Parse(r); data = Math.Round(double.Parse(r), 4); //data2 = Math.Round(decimal.Parse(r), 4); break; default: throw new Exception("功能设定小数位数值错误"); } //var db = DataContext.Db; //var detail = db.Queryable().First(m => m.SONo == "SO2024040100001"); //detail.Qty = data2; //var x = db.Updateable(detail).ExecuteCommand(); return data; } catch (Exception e) { throw new Exception(e.Message); } } /// /// 获取小车路径 /// /// 起始位置 /// 目标位置 /// 移动类型 0:移动 1:取货 2:放货 /// 是否载货0:未载货 1:已载货 /// public static List 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() .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(); // 定义上一位置与目标位置字典 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 (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; } /// /// 计算曼哈顿距离 /// /// 起始位置 /// 目标位置 /// 位置距离 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 enum InOutFlag { /// /// 入库单 /// [Description("入库单")] ASN, /// /// 出库单 /// [Description("出库单")] SO, /// /// 波次出库单 /// [Description("波次出库单")] WSO, /// /// 出库单 /// [Description("盘点单")] CR, /// /// 移库单 /// [Description("移库单")] PM, /// /// 任务 /// [Description("任务")] TK, /// /// 异常号 /// [Description("异常")] EX, /// /// 质检请验单 /// [Description("质检请验单")] QC } 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); } } }