wxw
2024-08-28 ec899ee7e9ead5152bd9b4c2dedd75c79cab6fb0
Merge branch 'master' into wxw
12个文件已修改
4个文件已添加
736 ■■■■ 已修改文件
.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/Admin.NET.Core/Logging/LoggingSetup.cs 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Configuration/Logging.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Hub/ITaskLogHub.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Hub/TaskLogHub.cs 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/PLC/PLCJob.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/PLC/PLCService.cs 327 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/PLC/PLCTaskAction.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Service/WcsDevice/Dto/WcsDeviceDto.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Service/WcsTask/WcsTaskService.cs 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Util/HttpService.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/api/log/wcsAlarmLog.ts 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/api/wcs/wcsTask.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/views/wcs/wcsDevice/component/editDialog.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/views/wcs/wcsTask/index.vue 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/views/wcs/wcsTask/signalR.ts 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.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 };