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);
}
}
}