.gitignore
@@ -200,6 +200,7 @@ # except build/, which is used as an MSBuild target. !**/[Pp]ackages/build/ !**/system/log/ !**/api/log/ # Uncomment if necessary however generally it will be regenerated when needed #!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files Admin.NET/Admin.NET.Core/Logging/LoggingSetup.cs
@@ -45,7 +45,10 @@ options.WithTraceId = true; // 显示线程Id options.WithStackFrame = true; // 显示程序集 options.FileNameRule = fileName => string.Format(fileName, DateTime.Now, logLevel.ToString()); // 每天创建一个文件 options.WriteFilter = logMsg => logMsg.LogLevel >= logLevel; // 日志级别 options.WriteFilter = (logMsg) => { return logMsg.LogLevel >= logLevel && logMsg.LogName != CommonConst.SysLogCategoryName; // 只写LoggingMonitor日志 }; // 日志级别 options.HandleWriteError = (writeError) => // 写入失败时启用备用文件 { writeError.UseRollbackFileName(Path.GetFileNameWithoutExtension(writeError.CurrentFileName) + "-oops" + Path.GetExtension(writeError.CurrentFileName)); Admin.NET/WCS.Application/Configuration/Logging.json
@@ -6,7 +6,7 @@ "Microsoft.EntityFrameworkCore": "Information" }, "File": { "Enabled": false, // 启用文件日志 "Enabled": true, // 启用文件日志 "FileName": "logs/{0:yyyyMMdd}_{1}.log", // 日志文件 "Append": true, // 追加覆盖 // "MinimumLevel": "Information", // 日志级别 Admin.NET/WCS.Application/Hub/ITaskLogHub.cs
New file @@ -0,0 +1,25 @@ namespace WCS.Application; public interface ITaskLogHub { /// <summary> /// 下发任务 /// </summary> /// <param name="context"></param> /// <returns></returns> Task PublicTask(WcsTaskOutput context); /// <summary> /// 下发任务明细 /// </summary> /// <param name="context"></param> /// <returns></returns> Task PublicTaskMonitor(WcsTaskMonitorOutput context); /// <summary> /// 接收消息 /// </summary> /// <param name="context"></param> /// <returns></returns> Task ReceiveMessage(object context); } Admin.NET/WCS.Application/Hub/TaskLogHub.cs
New file @@ -0,0 +1,56 @@ using Furion.InstantMessaging; using Microsoft.AspNetCore.SignalR; namespace WCS.Application; /// <summary> /// 任务日志集线器 /// </summary> [MapHub("/hubs/TaskLog")] public class TaskLogHub : Hub<ITaskLogHub> { private readonly IHubContext<TaskLogHub, ITaskLogHub> _taskLogHubContext; public TaskLogHub(IHubContext<TaskLogHub, ITaskLogHub> taskLogHubContext) { _taskLogHubContext = taskLogHubContext; } /// <summary> /// 连接 /// </summary> /// <returns></returns> public override async Task OnConnectedAsync() { await base.OnConnectedAsync(); } /// <summary> /// 断开 /// </summary> /// <param name="exception"></param> /// <returns></returns> public override async Task OnDisconnectedAsync(Exception exception) { await base.OnDisconnectedAsync(exception); } /// <summary> /// 下发任务 /// </summary> /// <returns></returns> public async Task PublicTask(WcsTaskOutput context) { await _taskLogHubContext.Clients.All.PublicTask(context); } /// <summary> /// 下发任务明细 /// </summary> /// <returns></returns> public async Task PublicTaskMonitor(WcsTaskMonitorOutput context) { await _taskLogHubContext.Clients.All.PublicTaskMonitor(context); } } Admin.NET/WCS.Application/PLC/PLCJob.cs
@@ -1,4 +1,5 @@ using Furion.Schedule; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; namespace WCS.Application; Admin.NET/WCS.Application/PLC/PLCService.cs
@@ -1,12 +1,19 @@ using Furion.Logging; using IoTClient; using Microsoft.AspNetCore.SignalR; using System.Data; namespace WCS.Application; public static class PLCService { private static readonly ISqlSugarClient _db = SqlSugarSetup.ITenant.GetConnectionScope(SqlSugarConst.MainConfigId); private static readonly IHubContext<TaskLogHub, ITaskLogHub> _taskLogHubContext; static PLCService() { _taskLogHubContext = App.GetService<IHubContext<TaskLogHub, ITaskLogHub>>(); } public static void OnChangeEvent(object sender, EventArgs e) { @@ -107,12 +114,8 @@ modTaskMonitor.InteractiveMsg = "起始储位为空!"; _db.Insertable(modTaskMonitor).ExecuteCommand(); // 通知任务界面任务已存在更新 请更新界面 //if (TaskAction.refresh) //{ // wSChat.AlarmInformation("1"); //} //下发任务日志 _taskLogHubContext.Clients.All.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); break; } //给PLC写入任务数据 @@ -144,7 +147,7 @@ if (listResult.All(s => s.IsSucceed)) { // 写入跺机任务下发完成 plcConn.SetPlcDBValue(modDevice.PosType.Value, modDevice.DbNumber, modDevice.WcsPos, "10"); plcConn.SetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.WcsPos, "10"); // 将出库任务待执行改为正在执行 _db.Updateable<WcsTask>() @@ -165,11 +168,8 @@ EndLocat = outStationNum, }; _db.Insertable(modInsertTaskMonitor).ExecuteCommand(); // 通知任务界面任务已存在更新 请更新界面 //if (TaskAction.refresh) //{ // wSChat.AlarmInformation("1"); //} //下发任务日志 _taskLogHubContext.Clients.All.PublicTaskMonitor(modInsertTaskMonitor.Adapt<WcsTaskMonitorOutput>()); //修改led屏信息 //LedDisplay(modDevice.LedIP, "工位:" + modTask.EndLocate, "出库中 " + $"储位地址:{modTask.StartLocate}", "托盘号:" + modTask.PalletNo); } @@ -236,11 +236,8 @@ Status = TaskStatusEnum.Complete }; _db.Insertable(modTaskMonitor).ExecuteCommand(); //通知任务界面任务已存在更新 请更新界面 //if (TaskAction.refresh) //{ // wSChat.AlarmInformation("1"); //} //下发任务日志 _taskLogHubContext.Clients.All.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); } else if (modTask.TaskType.Value == TaskTypeEnum.Out) @@ -249,7 +246,7 @@ var res = plcConn.SetPlcDBValue(modPosTask.PosType, modDevice.DbNumber, modPosTask.PlcPos, taskNo); if (!res.IsSucceed) break; res = plcConn.SetPlcDBValue(modDevice.PosType.Value, modDevice.DbNumber, modDevice.WcsPos, "20"); res = plcConn.SetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.WcsPos, "20"); if (!res.IsSucceed) break; // 根据跺机号确认取货工位 @@ -277,11 +274,8 @@ Status = TaskStatusEnum.Complete }; _db.Insertable(modTaskMonitor).ExecuteCommand(); //通知任务界面任务已存在更新 请更新界面 //if (TaskAction.refresh) //{ // wSChat.AlarmInformation("1"); //} //下发任务日志 _taskLogHubContext.Clients.All.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); } } break; @@ -310,7 +304,7 @@ var res = plcConn.SetPlcDBValue(modStation.PosType, modDevice.DbNumber, modStation.PlcPos, taskNo); if (!res.IsSucceed) break; res = plcConn.SetPlcDBValue(modDevice.PosType.Value, modDevice.DbNumber, modDevice.WcsPos, "30"); res = plcConn.SetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.WcsPos, "30"); if (!res.IsSucceed) break; //修改任务状态 @@ -344,11 +338,8 @@ Status = TaskStatusEnum.Complete }; _db.Insertable(modTaskMonitor).ExecuteCommand(); // 通知任务界面任务已存在更新 请更新界面 //if (TaskAction.refresh) //{ // wSChat.AlarmInformation("1"); //} //下发任务日志 _taskLogHubContext.Clients.All.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); } else { @@ -363,80 +354,129 @@ } break; case TaskTypeEnum.Out://出库 // 从出库任务获取巷道号 string num = ""; // 放货工位号 if (modTask.StartRoadway == "R01") { num = "11"; //交互工位 } else if (modTask.StartRoadway == "R02") { num = "7"; //交互工位 } var modDevice2 = modDevice.listDevice.FirstOrDefault(s => s.StationNum == num); if (modDevice2 == null) { Log.Error($"【跺机】找不到工位{num}设备信息"); break; } // 根据目标口获取目标工位 string outCode = modTask.EndLocate.IsNullOrEmpty() ? "-1" : modTask.EndLocate; var listStation = _db.Queryable<WcsPosition>().Where(s => s.DeviceId == modDevice2.Id).ToList(); //给PLC写入任务数据 var listResult = new List<Result>(); //任务号 var modPosTask = listStation.FirstOrDefault(s => s.Text == "TaskNo"); listResult.Add(plcConn.SetPlcDBValue(modPosTask.PosType, modDevice.DbNumber, modPosTask.PlcPos, modTask.TaskNo)); //托盘号 var modPosPalletNo = listStation.FirstOrDefault(s => s.Text == "PalletNo"); listResult.Add(plcConn.SetPlcDBValue(modPosPalletNo.PosType, modDevice.DbNumber, modPosPalletNo.PlcPos, modTask.PalletNo)); //起始工位 var modPosLocatNo = listStation.FirstOrDefault(s => s.Text == "StartLocatNo"); listResult.Add(plcConn.SetPlcDBValue(modPosLocatNo.PosType, modDevice.DbNumber, modPosLocatNo.PlcPos, num)); // 目标工位 var modPosEndLocatNo = listStation.FirstOrDefault(s => s.Text == "EndLocatNo"); listResult.Add(plcConn.SetPlcDBValue(modPosEndLocatNo.PosType, modDevice.DbNumber, modPosEndLocatNo.PlcPos, outCode)); //全部写入成功 if (listResult.All(s => s.IsSucceed)) { // 写入工位wcs控制字 var res = plcConn.SetPlcDBValue(modDevice2.PosType, modDevice2.DbNumber, modDevice2.WcsPos, "120"); if (!res.IsSucceed) break; //写入plc任务号 var modStation = modDevice.listStation.FirstOrDefault(s => s.Text == "TaskNo"); res = plcConn.SetPlcDBValue(modStation.PosType, modDevice.DbNumber, modStation.PlcPos, modTask.TaskNo); if (!res.IsSucceed) break; //写入跺机wcs控制字流程30 返回垛机执行完成 res = plcConn.SetPlcDBValue(modDevice.PosType.Value, modDevice.DbNumber, modDevice.WcsPos, "30"); if (!res.IsSucceed) break; //记录任务明细 var modTaskMonitor = new WcsTaskMonitor() // 从出库任务获取巷道号 string num = ""; // 放货工位号 if (modTask.StartRoadway == "R01") { TaskNo = modTask.TaskNo, PlcId = modDevice2.PlcId, PlcName = modDevice2.Text, InteractiveMsg = $"写入指令:收到跺机放货完成;放货{num}工位===》{outCode}出库口", PalletNo = modTask.PalletNo, Status = TaskStatusEnum.Complete, StartLocat = num, //起始位置 EndLocat = outCode, //目标工位 }; _db.Insertable(modTaskMonitor).ExecuteCommand(); num = "11"; //交互工位 } else if (modTask.StartRoadway == "R02") { num = "7"; //交互工位 } var modDevice2 = modDevice.listDevice.FirstOrDefault(s => s.StationNum == num); if (modDevice2 == null) { Log.Error($"【跺机】找不到工位{num}设备信息"); break; } // 根据目标口获取目标工位 string outCode = modTask.EndLocate.IsNullOrEmpty() ? "-1" : modTask.EndLocate; // 通知任务界面任务已存在更新 请更新界面 //if (TaskAction.refresh) //{ // wSChat.AlarmInformation("1"); //} //todo:垛机出库不空跑 var listStation = _db.Queryable<WcsPosition>().Where(s => s.DeviceId == modDevice2.Id).ToList(); //给PLC写入任务数据 var listResult = new List<Result>(); //任务号 var modPosTask = listStation.FirstOrDefault(s => s.Text == "TaskNo"); listResult.Add(plcConn.SetPlcDBValue(modPosTask.PosType, modDevice.DbNumber, modPosTask.PlcPos, modTask.TaskNo)); //托盘号 var modPosPalletNo = listStation.FirstOrDefault(s => s.Text == "PalletNo"); listResult.Add(plcConn.SetPlcDBValue(modPosPalletNo.PosType, modDevice.DbNumber, modPosPalletNo.PlcPos, modTask.PalletNo)); //起始工位 var modPosLocatNo = listStation.FirstOrDefault(s => s.Text == "StartLocatNo"); listResult.Add(plcConn.SetPlcDBValue(modPosLocatNo.PosType, modDevice.DbNumber, modPosLocatNo.PlcPos, num)); // 目标工位 var modPosEndLocatNo = listStation.FirstOrDefault(s => s.Text == "EndLocatNo"); listResult.Add(plcConn.SetPlcDBValue(modPosEndLocatNo.PosType, modDevice.DbNumber, modPosEndLocatNo.PlcPos, outCode)); //全部写入成功 if (listResult.All(s => s.IsSucceed)) { // 写入工位wcs控制字 var res = plcConn.SetPlcDBValue(modDevice2.PosType, modDevice2.DbNumber, modDevice2.WcsPos, "120"); if (!res.IsSucceed) break; //写入plc任务号 var modStation = modDevice.listStation.FirstOrDefault(s => s.Text == "TaskNo"); res = plcConn.SetPlcDBValue(modStation.PosType, modDevice.DbNumber, modStation.PlcPos, modTask.TaskNo); if (!res.IsSucceed) break; //写入跺机wcs控制字流程30 返回垛机执行完成 res = plcConn.SetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.WcsPos, "30"); if (!res.IsSucceed) break; //记录任务明细 var modTaskMonitor = new WcsTaskMonitor() { TaskNo = modTask.TaskNo, PlcId = modDevice2.PlcId, PlcName = modDevice2.Text, InteractiveMsg = $"写入指令:收到跺机放货完成;放货{num}工位===》{outCode}出库口", PalletNo = modTask.PalletNo, Status = TaskStatusEnum.Complete, StartLocat = num, //起始位置 EndLocat = outCode, //目标工位 }; _db.Insertable(modTaskMonitor).ExecuteCommand(); //下发任务日志 _taskLogHubContext.Clients.All.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); //todo:垛机出库不空跑 } } break; case TaskTypeEnum.Move: { //移库任务 var modStation = modDevice.listStation.FirstOrDefault(s => s.Text == "TaskNo"); // 写入plc任务号 var res = plcConn.SetPlcDBValue(modStation.PosType, modDevice.DbNumber, modStation.PlcPos, taskNo); if (!res.IsSucceed) break; //写入plc控制字 res = plcConn.SetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.WcsPos, "30"); if (!res.IsSucceed) break; modTask.Status = TaskStatusEnum.Complete; _db.Updateable(modTask).UpdateColumns(s => s.Status).ExecuteCommand(); var modTaskRequest = modTask.Adapt<TaskRequest>(); HttpService httpService = new HttpService(); var modResponseTask = httpService.RequestTask(modTaskRequest).Result; if (modResponseTask.StatusCode == "0") { //请求成功 modTask.IsSuccess = TaskSuccessEnum.Success; _db.Updateable(modTask).UpdateColumns(s => s.IsSuccess).ExecuteCommand(); //todo:修改储位信息 任务类型 执行状态 起始位置 目标位置 // //记录任务明细 var modTaskMonitor = new WcsTaskMonitor() { TaskNo = modTask.TaskNo, PlcId = modDevice.PlcId, PlcName = modDevice.Text, InteractiveMsg = "任务完成,返回给WMS任务完成", StartLocat = modTask.StartLocate, EndLocat = modTask.EndLocate, PalletNo = modTask.PalletNo, Status = TaskStatusEnum.Complete }; _db.Insertable(modTaskMonitor).ExecuteCommand(); //下发任务日志 _taskLogHubContext.Clients.All.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); } else { //请求失败 modTask.IsSuccess = TaskSuccessEnum.Fail; modTask.Information = modResponseTask.Message; _db.Updateable(modTask).UpdateColumns(s => new { s.IsSuccess, s.Information }).ExecuteCommand(); } //todo:LED屏 } break; default: @@ -444,6 +484,99 @@ } } break; case "100": { //空取货异常 var modPosPlcTask = modDevice.listStation.FirstOrDefault(s => s.Text == "PlcTaskNo"); var modPosTask = modDevice.listStation.FirstOrDefault(s => s.Text == "TaskNo"); var (result, TaskNo) = plcConn.GetPlcDBValue(modPosPlcTask.PosType, modDevice.DbNumber, modPosPlcTask.PlcPos); if (!result.IsSucceed) break; string taskNo = Convert.ToString(TaskNo); var modTask = _db.Queryable<WcsTask>().First(s => s.TaskNo == taskNo && s.Status == TaskStatusEnum.Doing); if (modTask == null) { Log.Error($"【跺机】当前任务号不存在对应的任务,任务号:{modTask.TaskNo}"); break; } var res = plcConn.SetPlcDBValue(modPosTask.PosType, modDevice.DbNumber, modPosTask.PlcPos, taskNo); if (!res.IsSucceed) break; res = plcConn.SetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.WcsPos, "100"); if (!res.IsSucceed) break; modTask.Status = TaskStatusEnum.Exception; _db.Updateable(modTask).UpdateColumns(s => s.Status).ExecuteCommand(); Log.Information($"【跺机】wcs任务变更空取异常,任务号:{modTask.TaskNo}"); var modTaskRequest = modTask.Adapt<TaskRequest>(); HttpService httpService = new HttpService(); var modResponseTask = httpService.RequestEmptyException(modTaskRequest).Result; if (modResponseTask.StatusCode == "0") { //请求成功 modTask.IsSuccess = TaskSuccessEnum.Success; _db.Updateable(modTask).UpdateColumns(s => s.IsSuccess).ExecuteCommand(); //下发任务日志 _taskLogHubContext.Clients.All.PublicTask(modTask.Adapt<WcsTaskOutput>()); } else { //请求失败 modTask.IsSuccess = TaskSuccessEnum.Fail; modTask.Information = modResponseTask.Message; _db.Updateable(modTask).UpdateColumns(s => new { s.IsSuccess, s.Information }).ExecuteCommand(); } //todo:LED屏 } break; case "101": { //满放货异常 var modPosPlcTask = modDevice.listStation.FirstOrDefault(s => s.Text == "PlcTaskNo"); var modPosTask = modDevice.listStation.FirstOrDefault(s => s.Text == "TaskNo"); var modPosPalletNo = modDevice.listStation.FirstOrDefault(s => s.Text == "PalletNo"); var (result, TaskNo) = plcConn.GetPlcDBValue(modPosPlcTask.PosType, modDevice.DbNumber, modPosPlcTask.PlcPos); if (!result.IsSucceed) break; string taskNo = Convert.ToString(TaskNo); var modTask = _db.Queryable<WcsTask>().First(s => s.TaskNo == taskNo && s.Status == TaskStatusEnum.Doing); if (modTask == null) { Log.Error($"【跺机】当前任务号不存在对应的任务,任务号:{modTask.TaskNo}"); break; } modTask.Status = TaskStatusEnum.Exception; _db.Updateable(modTask).UpdateColumns(s => s.Status).ExecuteCommand(); Log.Information($"【跺机】wcs任务变更满取异常,任务号:{modTask.TaskNo}"); var modTaskRequest = modTask.Adapt<TaskRequest>(); HttpService httpService = new HttpService(); var modResponseTask = httpService.RequestEmptyException(modTaskRequest).Result; if (modResponseTask.StatusCode == "0") { //请求成功 modTask.IsSuccess = TaskSuccessEnum.Success; _db.Updateable(modTask).UpdateColumns(s => s.IsSuccess).ExecuteCommand(); var modInsertTask = modResponseTask.TaskList; modInsertTask.TaskType = TaskTypeEnum.In; modInsertTask.Origin = "WMS"; modInsertTask.StartLocate = modTask.StartLocate; modInsertTask.Levels = 1; _db.Insertable(modInsertTask); //下发任务日志 _taskLogHubContext.Clients.All.PublicTask(modInsertTask.Adapt<WcsTaskOutput>()); } else { //请求失败 modTask.IsSuccess = TaskSuccessEnum.Fail; modTask.Information = modResponseTask.Message; _db.Updateable(modTask).UpdateColumns(s => new { s.IsSuccess, s.Information }).ExecuteCommand(); } //todo:LED屏 } break; default: break; } Admin.NET/WCS.Application/PLC/PLCTaskAction.cs
@@ -1,5 +1,5 @@ using Admin.NET.Core.Service; using Furion.Logging; using Furion.Logging; using Microsoft.AspNetCore.SignalR; namespace WCS.Application; public static class PLCTaskAction Admin.NET/WCS.Application/Service/WcsDevice/Dto/WcsDeviceDto.cs
@@ -34,27 +34,27 @@ /// <summary> /// DB区域 /// </summary> public string? DbNumber { get; set; } public string DbNumber { get; set; } /// <summary> /// 工位号 /// </summary> public string? StationNum { get; set; } public string StationNum { get; set; } /// <summary> /// PLC偏移量 /// </summary> public string? PlcPos { get; set; } public string PlcPos { get; set; } /// <summary> /// WCS偏移量 /// </summary> public string? WcsPos { get; set; } public string WcsPos { get; set; } /// <summary> /// 流程字类型 /// </summary> public PLCDataTypeEnum? PosType { get; set; } public PLCDataTypeEnum PosType { get; set; } /// <summary> /// 显示屏ip地址 Admin.NET/WCS.Application/Service/WcsTask/WcsTaskService.cs
@@ -1,4 +1,8 @@ using AngleSharp.Dom; using Furion.DatabaseAccessor; using Microsoft.AspNetCore.SignalR; namespace WCS.Application; /// <summary> @@ -8,9 +12,12 @@ public class WcsTaskService : IDynamicApiController, ITransient { private readonly SqlSugarRepository<WcsTask> _wcsTaskRep; public WcsTaskService(SqlSugarRepository<WcsTask> wcsTaskRep) private readonly IHubContext<TaskLogHub, ITaskLogHub> _taskLogHubContext; public WcsTaskService(SqlSugarRepository<WcsTask> wcsTaskRep, IHubContext<TaskLogHub, ITaskLogHub> taskLogHubContext) { _wcsTaskRep = wcsTaskRep; _taskLogHubContext = taskLogHubContext; } /// <summary> @@ -23,7 +30,7 @@ [DisplayName("分页查询任务表")] public async Task<SqlSugarPagedList<WcsTaskOutput>> Page(PageWcsTaskInput input) { input.SearchKey = input.SearchKey?.Trim(); input.SearchKey = input.SearchKey?.Trim(); var query = _wcsTaskRep.AsQueryable() .WhereIF(!string.IsNullOrEmpty(input.SearchKey), u => u.TaskNo.Contains(input.SearchKey) @@ -32,7 +39,7 @@ .WhereIF(input.TaskType.HasValue, u => u.TaskType == input.TaskType) .WhereIF(input.Status.HasValue, u => u.Status == input.Status) .Select<WcsTaskOutput>(); return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize); return await query.OrderBuilder(input).ToPagedListAsync(input.Page, input.PageSize); } /// <summary> @@ -105,7 +112,50 @@ return await _wcsTaskRep.AsQueryable().Select<WcsTaskOutput>().ToListAsync(); } /// <summary> /// 完成/取消任务表 /// </summary> /// <param name="input"></param> /// <returns></returns> [HttpPost] [ApiDescriptionSettings(Name = "Finsh")] [DisplayName("完成/取消任务表")] [UnitOfWork] public async Task Finsh(UpdateWcsTaskInput input) { if (input.Status == TaskStatusEnum.Complete || input.Status == TaskStatusEnum.Cancell) { var modTask = await _wcsTaskRep.GetByIdAsync(input.Id); if (modTask.Status > TaskStatusEnum.Doing) throw Oops.Oh("任务状态异常"); if (input.Status == TaskStatusEnum.Complete) modTask.IsSuccess = TaskSuccessEnum.Success; else modTask.IsSuccess = TaskSuccessEnum.Fail; modTask.FinishDate = DateTime.Now; modTask.Status = input.Status; await _wcsTaskRep.Context.Updateable(modTask).UpdateColumns(s => new { s.Status, s.IsSuccess, s.FinishDate, s.UpdateTime,s.UpdateUserId,s.UpdateUserName }).ExecuteCommandAsync(); //写入任务明细表 WcsTaskMonitor modTaskMonitor = new WcsTaskMonitor() { TaskNo = modTask.TaskNo, PlcId = 0, PlcName = "", Status = TaskStatusEnum.Complete, StartLocat = modTask.StartLocate, EndLocat = modTask.EndLocate, InteractiveMsg = input.Status == TaskStatusEnum.Complete ? "任务已手动完成" : "任务已手动取消", PalletNo = modTask.PalletNo }; await _wcsTaskRep.Context.Insertable(modTaskMonitor).ExecuteCommandAsync(); //await _taskLogHubContext.Clients.All.PublicTask(modTask.Adapt<WcsTaskOutput>()); //await _taskLogHubContext.Clients.All.PublicTaskMonitor(modTaskMonitor.Adapt<WcsTaskMonitorOutput>()); } else { throw Oops.Oh("任务状态异常"); } } Admin.NET/WCS.Application/Util/HttpService.cs
@@ -20,7 +20,33 @@ { string url = Urls.WMSAddress + ":" + Urls.WMSPort; var result = await (url + "/api/DownAPi/ReceiveWcsSignal").SetBody(model, "application/json", Encoding.UTF8).PostAsAsync<ResponseTasks>(); Log.Debug("调用WMS接口反馈任务接口" + result.ToJson()); Log.Information("调用WMS接口反馈任务接口" + result.ToJson()); return result; } /// <summary> /// 调用WMS接口反馈空取异常接口 /// </summary> /// <param name="model">任务信息</param> /// <returns></returns> public async Task<ResponseTasks> RequestEmptyException(TaskRequest model) { string url = Urls.WMSAddress + ":" + Urls.WMSPort; var result = await (url + "/api/DownAPi/EmptyException").SetBody(model, "application/json", Encoding.UTF8).PostAsAsync<ResponseTasks>(); Log.Information("调用WMS接口反馈空取异常接口" + result.ToJson()); return result; } /// <summary> /// 调用WMS接口反馈满取异常接口 /// </summary> /// <param name="model">任务信息</param> /// <returns></returns> public async Task<ResponseTasks> RequestFullException(TaskRequest model) { string url = Urls.WMSAddress + ":" + Urls.WMSPort; var result = await (url + "/api/DownAPi/FullException").SetBody(model, "application/json", Encoding.UTF8).PostAsAsync<ResponseTasks>(); Log.Information("调用WMS接口反馈满取异常接口" + result.ToJson()); return result; } } Web/src/api/log/wcsAlarmLog.ts
New file @@ -0,0 +1,50 @@ import request from '/@/utils/request'; enum Api { AddWcsAlarmLog = '/api/wcsAlarmLog/add', DeleteWcsAlarmLog = '/api/wcsAlarmLog/delete', UpdateWcsAlarmLog = '/api/wcsAlarmLog/update', PageWcsAlarmLog = '/api/wcsAlarmLog/page', DetailWcsAlarmLog = '/api/wcsAlarmLog/detail', } // 增加报警日志 export const addWcsAlarmLog = (params?: any) => request({ url: Api.AddWcsAlarmLog, method: 'post', data: params, }); // 删除报警日志 export const deleteWcsAlarmLog = (params?: any) => request({ url: Api.DeleteWcsAlarmLog, method: 'post', data: params, }); // 编辑报警日志 export const updateWcsAlarmLog = (params?: any) => request({ url: Api.UpdateWcsAlarmLog, method: 'post', data: params, }); // 分页查询报警日志 export const pageWcsAlarmLog = (params?: any) => request({ url: Api.PageWcsAlarmLog, method: 'post', data: params, }); // 详情报警日志 export const detailWcsAlarmLog = (id: any) => request({ url: Api.DetailWcsAlarmLog, method: 'get', data: { id }, }); Web/src/api/wcs/wcsTask.ts
@@ -5,6 +5,7 @@ UpdateWcsTask = '/api/wcsTask/update', PageWcsTask = '/api/wcsTask/page', DetailWcsTask = '/api/wcsTask/detail', FinshWcsTask = '/api/wcsTask/Finsh', } // 增加任务表 @@ -48,3 +49,10 @@ }); // 完成/取消任务 export const finshWcsTask = (params?: any) => request({ url: Api.FinshWcsTask, method: 'post', data: params, }); Web/src/views/wcs/wcsDevice/component/editDialog.vue
@@ -138,6 +138,10 @@ const ruleForm = ref<any>({}); //自行添加其他规则 const rules = ref<FormRules>({ dbNumber: [{ required: true, message: '请输入DB区域!', trigger: 'blur', },], stationNum: [{ required: true, message: '请输入工位号!', trigger: 'blur', },], plcPos: [{ required: true, message: '请输入PLC偏移量!', trigger: 'blur', },], wcsPos: [{ required: true, message: '请输入WCS偏移量!', trigger: 'blur', },], }); // 页面加载时 Web/src/views/wcs/wcsTask/index.vue
@@ -4,8 +4,9 @@ <el-col :span="12" :xs="24" style="display: flex; height: 100%; flex: 1"> <el-card class="full-table" shadow="hover" ::body-style="{ height: 'calc(100% - 51px)' }"> <template #header> <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-Collection /></el-icon>任务日志 </template> <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-Collection /></el-icon>任务日志 </template> <el-form :model="queryParams" ref="queryForm" labelWidth="90"> <el-row> <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10"> @@ -53,8 +54,8 @@ </el-col> </el-row> </el-form> <el-table :data="tableData" style="width: 100%" v-loading="loading" tooltip-effect="light" row-key="id" @row-click="handleClick" @sort-change="sortChange" border=""> <el-table :data="tableData" style="width: 100%" v-loading="loading" tooltip-effect="light" row-key="id" @cell-click="handleClick" @sort-change="sortChange" border=""> <el-table-column type="index" label="序号" width="55" align="center" /> <el-table-column prop="taskNo" label="任务号" show-overflow-tooltip="" /> <el-table-column prop="taskType" width="85" label="任务类型" show-overflow-tooltip=""> @@ -79,17 +80,15 @@ <ModifyRecord :data="scope.row" /> </template> </el-table-column> <el-table-column label="操作" width="130" align="center" fixed="right" show-overflow-tooltip="" <el-table-column prop="操作" label="操作" width="130" align="center" fixed="right" show-overflow-tooltip="" v-if="auth('wcsTask:complete') || auth('wcsTask:cancell')"> <template #default="scope"> <!-- <el-button icon="ele-Edit" size="small" text="" type="primary" @click="openEditWcsTask(scope.row)" v-auth="'wcsTask:update'"> 编辑 </el-button> <el-button icon="ele-Delete" size="small" text="" type="primary" @click="delWcsTask(scope.row)" v-auth="'wcsTask:delete'"> 删除 </el-button> --> <el-button icon="ele-Check" size="small" text="" type="primary" @click="completeTask(scope.row)" v-auth="'wcsTask:complete'"> 完成 </el-button> <el-button icon="ele-Close" size="small" text="" type="primary" @click="cancellTask(scope.row)" v-auth="'wcsTask:cancell'"> 取消 </el-button> <template v-if="scope.row.status <= 1"> <el-button icon="ele-Check" size="small" text="" type="primary" @click="completeTask(scope.row)" v-auth="'wcsTask:complete'"> 完成 </el-button> <el-button icon="ele-Close" size="small" text="" type="primary" @click="cancellTask(scope.row)" v-auth="'wcsTask:cancell'"> 取消 </el-button> </template> </template> </el-table-column> </el-table> @@ -104,8 +103,9 @@ <el-col :span="12" :xs="24" style="display: flex; height: 100%; flex: 1"> <el-card class="full-table" shadow="hover" :body-style="{ height: 'calc(100% - 51px)' }"> <template #header> <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-Collection /></el-icon>任务详情 </template> <el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"><ele-Collection /></el-icon>任务详情 </template> <el-form :model="queryParams2" ref="queryForm2" labelWidth="90"> <el-row> <el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10"> @@ -122,7 +122,8 @@ </el-col> </el-row> </el-form> <el-table :data="tableData2" style="width: 100%" v-loading="loading" tooltip-effect="light" row-key="id" border=""> <el-table :data="tableData2" style="width: 100%" v-loading="loading" tooltip-effect="light" row-key="id" border=""> <el-table-column type="index" label="序号" width="55" align="center" /> <el-table-column prop="taskNo" label="任务号" show-overflow-tooltip="" /> <el-table-column prop="plcId" label="交互工位号" show-overflow-tooltip="" /> @@ -146,22 +147,18 @@ <script lang="ts" setup="" name="wcsTask"> import ModifyRecord from '/@/components/table/modifyRecord.vue'; import { ref } from "vue"; import { onMounted, reactive, ref } from 'vue'; import { ElMessageBox, ElMessage } from "element-plus"; import { auth } from '/@/utils/authFunction'; import { getDictDataItem as di, getDictDataList as dl } from '/@/utils/dict-utils'; import { getDictLabelByVal as dv } from '/@/utils/dict-utils'; import { formatDate } from '/@/utils/formatTime'; import printDialog from '/@/views/system/print/component/hiprint/preview.vue' import editDialog from '/@/views/wcs/wcsTask/component/editDialog.vue' import { pageWcsTask, deleteWcsTask } from '/@/api/wcs/wcsTask'; import { pageWcsTask, finshWcsTask } from '/@/api/wcs/wcsTask'; import { pageWcsMonitorTask } from '/@/api/wcs/wcsTaskMonitor'; import { getAPI } from '/@/utils/axios-utils'; import { SysEnumApi } from '/@/api-services/api'; import commonFunction from '/@/utils/commonFunction'; import { signalR } from './signalR'; const showAdvanceQueryUI = ref(false); const printDialogRef = ref(); @@ -183,7 +180,31 @@ }); const printWcsTaskTitle = ref(""); const editWcsTaskTitle = ref(""); //连接signalR 监听后台任务日志变更 onMounted(async () => { signalR.off('PublicTask'); signalR.on('PublicTask', (data: any) => { console.log(data) var listTask = tableData.value.filter(t => t.id == data.id); if (listTask.length == 0) { tableData.value.unshift(data) } else { //如果已经存在就更新数据 const index = tableData.value.findIndex(t => t.id == data.id); tableData.value.splice(index, 1, data); } }); signalR.off('PublicTaskMonitor'); signalR.on('PublicTaskMonitor', async (data: any) => { console.log(data); if (queryParams2.value.taskNo == undefined || queryParams2.value.taskNo == data.taskNo) { if (tableData2.value.filter(t => t.id == data.id).length == 0) { tableData2.value.unshift(data) } } }); }); // 改变高级查询的控件显示状态 const changeAdvanceQueryUI = () => { showAdvanceQueryUI.value = !showAdvanceQueryUI.value; @@ -230,33 +251,48 @@ editDialogRef.value.openDialog(row); }; //完成任务 const completeTask = (row: any) => { } //取消任务 const cancellTask = (row: any) => { } // 点击表格 const handleClick = (row: any, event: any, column: any) => { queryParams2.value.taskNo = row.taskNo; handleQuery2(); }; // 删除 const delWcsTask = (row: any) => { ElMessageBox.confirm(`确定要删除吗?`, "提示", { const completeTask = async (row: any) => { ElMessageBox.confirm(`确定要完成任务吗?`, "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }) .then(async () => { await deleteWcsTask(row); var param = Object.assign(row); param.status = 2; await finshWcsTask(param); handleQuery(); ElMessage.success("删除成功"); handleQuery2(); ElMessage.success("完成任务成功"); }) .catch(() => { }); }; } //取消任务 const cancellTask = async (row: any) => { ElMessageBox.confirm(`确定要取消任务吗?`, "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning", }) .then(async () => { var param = Object.assign(row); param.status = 4; await finshWcsTask(param); handleQuery(); handleQuery2(); ElMessage.success("取消任务成功"); }) .catch(() => { }); } // 点击表格 const handleClick = (row, column, cell, event) => { if (column.property === '操作') { return; } queryParams2.value.taskNo = row.taskNo; handleQuery2(); }; // 改变页面容量 const handleSizeChange = (val: number) => { tableParams.value.pageSize = val; Web/src/views/wcs/wcsTask/signalR.ts
New file @@ -0,0 +1,37 @@ import * as SignalR from '@microsoft/signalr'; import { getToken } from '/@/utils/axios-utils'; // 初始化SignalR对象 const connection = new SignalR.HubConnectionBuilder() .configureLogging(SignalR.LogLevel.Information) .withUrl(`${window.__env__.VITE_API_URL}/hubs/TaskLog?token=${getToken()}`, { transport: SignalR.HttpTransportType.WebSockets, skipNegotiation: true }) .withAutomaticReconnect({ nextRetryDelayInMilliseconds: () => { return 5000; // 每5秒重连一次 }, }) .build(); connection.keepAliveIntervalInMilliseconds = 15 * 1000; // 心跳检测15s connection.serverTimeoutInMilliseconds = 30 * 60 * 1000; // 超时时间30m // 启动连接 connection.start().then(() => { console.log('启动连接task'); }); // 断开连接 connection.onclose(async () => { console.log('断开连接task'); }); // 重连中 connection.onreconnecting(() => { console.log('服务器已断线task'); }); // 重连成功 connection.onreconnected(() => { console.log('重连成功task'); }); connection.on('PublicTask', () => {}); export { connection as signalR };