| | |
| | | using DocumentFormat.OpenXml.Bibliography; |
| | | using DocumentFormat.OpenXml.Drawing; |
| | | using DocumentFormat.OpenXml.Drawing.Charts; |
| | | using Elastic.Clients.Elasticsearch.Snapshot; |
| | | using Furion.Logging; |
| | | using Microsoft.AspNetCore.SignalR; |
| | | using NewLife.Serialization; |
| | | using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; |
| | | using OfficeOpenXml.FormulaParsing.Excel.Functions.Text; |
| | | using OnceMi.AspNetCore.OSS; |
| | | using Qiniu.Storage; |
| | | using System; |
| | | using System.Drawing.Drawing2D; |
| | | using System.Net.NetworkInformation; |
| | | using WCS.Application.Entity; |
| | | using WCS.Application.Util; |
| | | using static SKIT.FlurlHttpClient.Wechat.Api.Models.CgibinExpressIntracityUpdateStoreRequest.Types; |
| | | //using WCS.Application.Util; |
| | | |
| | |
| | | private static readonly ISqlSugarClient _db = SqlSugarSetup.ITenant.GetConnectionScope(SqlSugarConst.MainConfigId); |
| | | private static readonly SysCacheService sysCacheService = App.GetRequiredService<SysCacheService>(); |
| | | private static readonly SysConfigService _sysConfigService = App.GetService<SysConfigService>(); |
| | | private static readonly WcsTaskService _taskService = App.GetService<WcsTaskService>(); |
| | | |
| | | private static List<WcsPlc> listPlc = new List<WcsPlc>(); |
| | | private static List<WcsDevice> listPlcDevice = new List<WcsDevice>(); |
| | |
| | | private static CancellationTokenSource cts = new CancellationTokenSource();//取消线程标识 |
| | | |
| | | private static List<ModbusUtil> listModbusUtil = new List<ModbusUtil>(); |
| | | |
| | | |
| | | public static List<ModbusUtil> modbusUtilConn |
| | | { |
| | |
| | | boDrumReversal = _sysConfigService.GetConfigValue<bool>("sys_DrumReversal").Result; |
| | | boOutLock = _sysConfigService.GetConfigValue<bool>("sys_BoOutLock").Result; |
| | | boEnterLock = _sysConfigService.GetConfigValue<bool>("sys_BoEnterLock").Result; |
| | | |
| | | } |
| | | /// <summary> |
| | | /// 初始化PLC连接 |
| | | /// </summary> |
| | | public static void Init() |
| | | public static void Init() |
| | | { |
| | | cts.Cancel(); |
| | | listPlc = _db.Queryable<WcsPlc>() |
| | |
| | | StartRead(); |
| | | ConnectionStatus(); |
| | | StartWatchAlarm(); |
| | | AssignTasks(); |
| | | //StartWatchPosition(); |
| | | } |
| | | } |
| | |
| | | var (result, value) = modPlcUtil.GetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.PlcPos); |
| | | if (result.IsSucceed) |
| | | { |
| | | //如果是穿梭车,这里发送心跳 |
| | | if (modPlc.Type == PLCTypeEnum.ShuttleCar) |
| | | { |
| | | var modHeart = listPlcPosition.Where(s => s.DeviceId == modDevice.Id && s.Text == "心跳").FirstOrDefault(); |
| | | if (modHeart != null) |
| | | { |
| | | modPlcUtil.SetPlcDBValue(modHeart.PosType, modHeart.PlcPos, "1"); |
| | | } |
| | | } |
| | | //无流程跳出 |
| | | if (Convert.ToInt32(value) == 0) |
| | | continue; |
| | |
| | | var (result, value) = modbusUtil.GetDBValue(modDevice.PosType, modDevice.PlcPos); |
| | | if (result.IsSucceed) |
| | | { |
| | | //如果是穿梭车,这里发送心跳 |
| | | if (modPlc.Type == PLCTypeEnum.ShuttleCar) |
| | | { |
| | | var modHeart = listPlcPosition.Where(s => s.DeviceId == modDevice.Id && s.Text == "心跳").FirstOrDefault(); |
| | | if (modHeart != null) |
| | | { |
| | | modbusUtil.SetDBValue(modHeart.PosType, modHeart.PlcPos, "1"); |
| | | } |
| | | } |
| | | |
| | | //无流程跳出 |
| | | if (value == 0) |
| | | continue; |
| | | var dto = modDevice.Adapt<WcsDeviceDto>(); |
| | | dto.Value = value; |
| | | dto.PlcIdIP = modPlc.IP; |
| | | dto.Type = _modplc.Type; |
| | | dto.modbusUtil = modbusUtil; |
| | | dto.listStation = listPlcPosition.Where(s => s.DeviceId == modDevice.Id).ToList(); |
| | |
| | | |
| | | |
| | | |
| | | Thread.Sleep(1000); |
| | | Thread.Sleep(2000); |
| | | } |
| | | catch (OperationCanceledException) |
| | | { |
| | |
| | | }, cts.Token); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 四向车任务分配 |
| | | /// </summary> |
| | | private static void AssignTasks() |
| | | { |
| | | |
| | | Task.Run(() => |
| | | { |
| | | while (true) |
| | | { |
| | | //Console.WriteLine("开启四向车任务自分配"); |
| | | //取消线程 关闭PLC连接 |
| | | if (cts.Token.IsCancellationRequested) |
| | | { |
| | | foreach (var modModBusUtil in listModbusUtil) |
| | | { |
| | | if (modModBusUtil != null && modModBusUtil.Connected) |
| | | modModBusUtil.Close(); |
| | | } |
| | | break; |
| | | } |
| | | try |
| | | { |
| | | |
| | | |
| | | // 获取密集库未执行任务 根据创建时间排序 |
| | | var waitTask = _db.Queryable<WcsTask>().Where(s => s.IsDelete == false && s.Status == TaskStatusEnum.Wait && s.Type == PLCTypeEnum.ShuttleCar).OrderBy(s => new {s.Levels, s.CreateTime}).First(); |
| | | if (waitTask == null) |
| | | { |
| | | continue; |
| | | } |
| | | var starLocate = ""; |
| | | var endLocate = ""; |
| | | var taskceng = 0; |
| | | //01010101: 01排 01列 01层 01深度-此项目不做判断 |
| | | |
| | | if (waitTask.TaskType == TaskTypeEnum.In ) |
| | | { |
| | | //入库任务起始巷道就是四向车取货工位 |
| | | if (string.IsNullOrWhiteSpace(waitTask.StartRoadway) || string.IsNullOrWhiteSpace(waitTask.EndLocate)) |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | taskceng = int.Parse(waitTask.StartRoadway.Substring(4, 2)); |
| | | starLocate = waitTask.StartRoadway; |
| | | endLocate = waitTask.EndLocate; |
| | | } |
| | | else if (waitTask.TaskType == TaskTypeEnum.Out) |
| | | { |
| | | //出库任务目标巷道就是四向车放货工位 |
| | | taskceng = int.Parse(waitTask.StartLocate.Substring(4, 2)); |
| | | starLocate = waitTask.StartLocate; |
| | | endLocate = waitTask.EndRoadway; |
| | | |
| | | #region 添加输送线与任务验证 |
| | | |
| | | |
| | | |
| | | var text = ""; |
| | | var devStation = ""; |
| | | var ip = ""; |
| | | var carcon = new carConverModel(); |
| | | if (carcon.conveyorBei.Keys.Contains(endLocate)) |
| | | { |
| | | text = "输送线北"; |
| | | ip = "10.26.254.10"; |
| | | devStation = carcon.conveyorBei[endLocate]; |
| | | } |
| | | else if (carcon.conveyorNan.Keys.Contains(endLocate)) |
| | | { |
| | | text = "输送线南"; |
| | | ip = "10.26.254.11"; |
| | | devStation = carcon.conveyorNan[endLocate]; |
| | | } |
| | | if (text != "") |
| | | { |
| | | //var modPlc = PLCTaskAction.plcs.FirstOrDefault(s => s.Text == text); |
| | | //var modConn = new PLCUtil(modPlc); |
| | | var modConn = PLCTaskAction.listPlcConn.First(m => m.PlcIP == ip); |
| | | var modDevice = PLCTaskAction.plcDevices.First(s => s.StationNum == devStation); |
| | | var (plcResult, palletVal) = modConn.GetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.PlcPos); |
| | | if (!plcResult.IsSucceed || Convert.ToInt32(palletVal) != 720) |
| | | { |
| | | continue;//放货工位没有读取成功或不是空闲 |
| | | } |
| | | |
| | | var gongwei = carcon.conveyorRuKu[endLocate]; |
| | | //判断任务 |
| | | var convarTask = _db.Queryable<WcsTask>().First(m => m.IsDelete == false && m.StartLocate == gongwei |
| | | && m.Type == PLCTypeEnum.ConveyorLine && (m.Status == TaskStatusEnum.Wait || m.Status == TaskStatusEnum.Doing)); |
| | | if (convarTask != null) |
| | | { |
| | | continue;//放货工位有任务 |
| | | } |
| | | } |
| | | |
| | | |
| | | #endregion |
| | | } |
| | | else if (waitTask.TaskType == TaskTypeEnum.Move) |
| | | { |
| | | taskceng = int.Parse(waitTask.StartLocate.Substring(4, 2)); |
| | | starLocate = waitTask.StartLocate; |
| | | endLocate = waitTask.EndLocate; |
| | | } |
| | | else |
| | | { |
| | | continue; |
| | | } |
| | | |
| | | #region 获取当前任务所在层所有空闲小车 |
| | | |
| | | // 获取有任务的小车编号 |
| | | var taskCarList = _db.Queryable<WcsCarTasks>().Where(m => m.IsDelete == false && m.Status == TaskStatusEnum.Wait).Select(m => m.CarNo).Distinct().ToList(); |
| | | |
| | | // 获取当前任务所在层所有空闲小车(根据小车任务表是否有任务和小车状态共同判断小车是否空闲) |
| | | var kXCarList = new List<CarInfo>(); |
| | | var carErr = false;//小车是否有失联的 |
| | | foreach (var modbusUtil in listModbusUtil) |
| | | { |
| | | //如果小车有未执行的任务,跳出 |
| | | if (taskCarList.Contains(modbusUtil.PlcIP)) |
| | | { |
| | | continue; |
| | | } |
| | | //获取小车的WcsDevice信息 |
| | | var listDevice = listPlcDevice.Where(s => s.PlcId == modbusUtil.PlcId && s.DeviceType == DeviceTypeEnum.Business).ToList(); |
| | | //循环读设备 |
| | | foreach (var modDevice in listDevice) |
| | | { |
| | | var (result, value) = modbusUtil.GetDBValue(modDevice.PosType, modDevice.PlcPos); |
| | | if (result.IsSucceed) |
| | | { |
| | | //获取工位WCSPLCPosition信息 |
| | | var plcPosition = listPlcPosition.Where(s => s.DeviceId == modDevice.Id).ToList(); |
| | | if (value == 3) |
| | | { |
| | | var modCarDl = plcPosition.FirstOrDefault(s => s.Text == "电池电量"); |
| | | var (resultDl, valueDl) = modbusUtil.GetDBValue(modCarDl.PosType, modCarDl.PlcPos); |
| | | if (resultDl.IsSucceed && valueDl> (int)FourWayCarDLEnum.Dl) |
| | | { |
| | | //写入结束充电命令 |
| | | var modCdEnd = plcPosition.FirstOrDefault(s => s.Text == "充电命令"); |
| | | var resultDl22 = modbusUtil.SetDBValue(modCdEnd.PosType, modCdEnd.PlcPos.ToString(), "3") ; |
| | | carErr = true; |
| | | break; //暂缓分配,防止同层小车关机或失联导致阻挡路径 |
| | | } |
| | | } |
| | | var modCarPall = plcPosition.FirstOrDefault(s => s.Text == "托盘检测"); |
| | | var (resultPall, valuePall) = modbusUtil.GetDBValue(modCarPall.PosType, modCarPall.PlcPos); |
| | | if (resultPall.IsSucceed && valuePall == 1) //1有托盘 2无托盘 |
| | | { |
| | | break; //暂缓分配 |
| | | } |
| | | //小车空闲加入集合 |
| | | if (value == 1) |
| | | { |
| | | var modStationX = plcPosition.FirstOrDefault(s => s.Text == "四向车位置(X)"); |
| | | var (resultx, valuex) = modbusUtil.GetDBValue(modStationX.PosType, modStationX.PlcPos); |
| | | var modStationY = plcPosition.FirstOrDefault(s => s.Text == "四向车位置(Y)"); |
| | | var (resulty, valuey) = modbusUtil.GetDBValue(modStationY.PosType, modStationY.PlcPos); |
| | | var modStationZ = plcPosition.FirstOrDefault(s => s.Text == "四向车位置(Z)"); |
| | | var (resultz, valuez) = modbusUtil.GetDBValue(modStationZ.PosType, modStationZ.PlcPos); |
| | | //小车当前层 |
| | | var carZ = (int)valuez; |
| | | //任务层与小车层相同 |
| | | if (taskceng == carZ) |
| | | { |
| | | if (!kXCarList.Any(m => m.CarPlcIp == modbusUtil.PlcIP)) |
| | | { |
| | | var carVal = ((int)valuex).ToString().PadLeft(2, '0') + ((int)valuey).ToString().PadLeft(2, '0') + ((int)valuez).ToString().PadLeft(2, '0'); |
| | | //小车到取货储位路径 |
| | | var d = FourWayCarUtil.GetCarPath(carVal, starLocate); |
| | | kXCarList.Add(new CarInfo() |
| | | { |
| | | CarPlcIp = modbusUtil.PlcIP, |
| | | X = (int)valuex, |
| | | Y = (int)valuey, |
| | | Z = (int)valuez, |
| | | Level = d.Count, |
| | | }); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | Console.WriteLine($"读取四向车{modbusUtil.PlcIP}状态失败"); |
| | | carErr = true; |
| | | break;//有一个小车读取失败跳出方法,暂缓分配,防止同层小车关机或失联导致阻挡路径 |
| | | } |
| | | } |
| | | //有小车失联,不分配任务 |
| | | if (carErr) |
| | | { |
| | | kXCarList.Clear();//清楚空车记录 |
| | | break; |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | |
| | | // 获取适合执行当前任务的小车 生成路径(需考虑小车阻阻挡) |
| | | var assignCar = kXCarList.OrderBy(m => m.Level).FirstOrDefault(); |
| | | if (assignCar == null) |
| | | { |
| | | continue;//没有空闲小车 |
| | | } |
| | | |
| | | if (assignCar.Level != 1) |
| | | { |
| | | //判断小车位置是否与任务的起始储位相同,不相同:获取小车到取货储位路径 |
| | | var carLocate = assignCar.X.ToString().PadLeft(2, '0')+assignCar.Y.ToString().PadLeft(2, '0')+ assignCar.Z.ToString().PadLeft(2, '0'); |
| | | //获取小车去取货储位任务路径 |
| | | var data1 = FourWayCarUtil.GetCarPath(carLocate, starLocate); |
| | | var datas1 = FourWayCarUtil.GetCarPathUp(data1, 0); |
| | | if (datas1 == null) |
| | | { |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | var bl = FourWayCarUtil.AddCarTask(datas1, kXCarList, assignCar, waitTask,0); |
| | | //分配错误,删除分配信息 |
| | | if (!bl) |
| | | { |
| | | var carTask = _db.Queryable<WcsCarTasks>().Where(m => m.IsDelete == false && m.TaskNo == waitTask.TaskNo).ToList(); |
| | | _db.Deleteable(carTask).ExecuteCommand(); |
| | | } |
| | | } |
| | | } |
| | | var typeStr = "1"; |
| | | var typeStr2 = 1; |
| | | if (waitTask.Levels == 888 ) |
| | | { |
| | | typeStr = "0";//小车任务是充电任务 |
| | | typeStr2 = 0; |
| | | } |
| | | //获取小车去放货储位任务路径 |
| | | var data2 = FourWayCarUtil.GetCarPath(starLocate, endLocate, typeStr); |
| | | var datas2 = FourWayCarUtil.GetCarPathUp(data2, typeStr2); |
| | | |
| | | if (datas2 == null) |
| | | { |
| | | continue; |
| | | } |
| | | else |
| | | { |
| | | var bl = FourWayCarUtil.AddCarTask(datas2,kXCarList,assignCar,waitTask,1); |
| | | //分配错误,删除分配信息 |
| | | if (!bl) |
| | | { |
| | | waitTask.Status = TaskStatusEnum.Wait; |
| | | _db.Updateable(waitTask).ExecuteCommand(); |
| | | var carTask = _db.Queryable<WcsCarTasks>().Where(m=>m.IsDelete == false && m.TaskNo == waitTask.TaskNo).ToList(); |
| | | _db.Deleteable(carTask).ExecuteCommand(); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | Thread.Sleep(3000); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Log.Error("任务分配发生异常", ex); |
| | | } |
| | | } |
| | | },cts.Token); |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 连接状态线程 |
| | |
| | | } |
| | | }); |
| | | } |
| | | /// <summary> |
| | | /// 修改位置信息 |
| | | /// </summary> |
| | | /// <param name="modInfo"></param> |
| | | private static void UpdatePosition(PlcPositionInfo modInfo) |
| | | { |
| | | var modTemp = listPositionInfo.FirstOrDefault(s => s.StationNum == modInfo.StationNum && modInfo.Type == s.Type); |