wxw
2024-09-10 6c010905f5e26438ea41a3faa62181975c89c9df
解决冲突
6个文件已修改
3个文件已添加
535 ■■■■■ 已修改文件
Admin.NET/WCS.Application/Hub/IPlcDeviceHub.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Hub/PlcDeviceHub.cs 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Service/WcsDevice/Dto/WcsDeviceInput.cs 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Service/WcsDevice/Dto/WcsDeviceOutput.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Service/WcsDevice/WcsDeviceService.cs 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/api/wcs/wcsDevice.ts 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/views/device/deviceMonitor/component/setting.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/views/device/deviceMonitor/index.vue 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Web/src/views/device/deviceMonitor/signalR.ts 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Admin.NET/WCS.Application/Hub/IPlcDeviceHub.cs
New file
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WCS.Application;
public interface IPlcDeviceHub
{
    /// <summary>
    /// 下发设备信息
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    Task PublicPlcDevice(WcsDeviceOutput context);
}
Admin.NET/WCS.Application/Hub/PlcDeviceHub.cs
New file
@@ -0,0 +1,196 @@
using Admin.NET.Core.Service;
using DocumentFormat.OpenXml.Office2010.ExcelAc;
using Furion.InstantMessaging;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WCS.Application;
/// <summary>
/// 任务日志集线器
/// </summary>
[MapHub("/hubs/PlcDevice")]
public class PlcDeviceHub : Hub<IPlcDeviceHub>
{
    private static readonly IHubContext<PlcDeviceHub, IPlcDeviceHub> _plcDeviceHubContext = App.GetService<IHubContext<PlcDeviceHub, IPlcDeviceHub>>();
    private static readonly SysCacheService _sysCacheService = App.GetRequiredService<SysCacheService>();
    private static bool boRunningState = false;
    private static CancellationTokenSource cts;//取消线程标识
    private static readonly ISqlSugarClient _db = SqlSugarSetup.ITenant.GetConnectionScope(SqlSugarConst.MainConfigId);
    public PlcDeviceHub()
    {
    }
    /// <summary>
    /// 连接
    /// </summary>
    /// <returns></returns>
    public override async Task OnConnectedAsync()
    {
        var httpContext = Context.GetHttpContext();
        var userId = (httpContext.User.FindFirst(ClaimConst.UserId)?.Value).ToLong();
        _sysCacheService.Set("PlcDeviceHub:" + Context.ConnectionId, userId);
        if (!boRunningState)
        {
            //开启读取服务线程
            boRunningState = true;
            cts = new CancellationTokenSource();//取消线程标识
            StartRead();
        }
        await base.OnConnectedAsync();
    }
    /// <summary>
    /// 断开
    /// </summary>
    /// <param name="exception"></param>
    /// <returns></returns>
    public override async Task OnDisconnectedAsync(Exception exception)
    {
        _sysCacheService.Remove("PlcDeviceHub:" + Context.ConnectionId);
        //如果没有连接了 就关闭线程不读了
        if (_sysCacheService.GetKeysByPrefixKey("PlcDeviceHub").Count == 0)
        {
            cts.Cancel();
            boRunningState = false;
        }
        await base.OnDisconnectedAsync(exception);
    }
    /// <summary>
    /// 开启读取plc线程
    /// </summary>
    public static void StartRead()
    {
        Task.Run(() =>
        {
            // 用于保存每个设备的初始状态
            var initialStates = new Dictionary<long, WcsDeviceOutput>();
            var listPlc = _db.Queryable<WcsPlc>().Where(s => s.Type == PLCTypeEnum.StackingMachine || s.Type == PLCTypeEnum.ConveyorLine).ToList();
            var listPlcId = listPlc.Select(s => s.Id).ToList();
            var listPlcDevice = _db.Queryable<WcsDevice>().Where(s => s.DeviceType == DeviceTypeEnum.Business && listPlcId.Contains(s.PlcId)).Select<WcsDeviceOutput>().ToList();
            var listPlcDeviceId = listPlcDevice.Select(s => s.Id).ToList();
            var listPlcStation = _db.Queryable<WcsPosition>().Where(s => listPlcDeviceId.Contains(s.DeviceId)).ToList();
            while (true)
            {
                try
                {
                    //获取跺机的状态
                    foreach (var modDevice in listPlcDevice)
                    {
                        //取消线程
                        if (cts.Token.IsCancellationRequested)
                        {
                            throw new OperationCanceledException();
                        }
                        var modPlc = listPlc.Where(s => s.Id == modDevice.Id).FirstOrDefault();
                        //读取plc的值
                        var modConn = PLCTaskAction.listPlcConn.FirstOrDefault(s => s != null && s.PlcId == modDevice.PlcId);
                        if (modConn == null)
                        {
                            modDevice.Status = false;
                            continue;
                        }
                        else
                        {
                            modDevice.Status = modConn.Connected;
                        }
                        if (modConn.Connected)
                        {
                            var listPosition = listPlcStation.Where(s => s.DeviceId == modDevice.Id).ToList();
                            (var result, var plc) = modConn.GetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.PlcPos);
                            modDevice.Plc = Convert.ToString(plc);
                            (result, var wcs) = modConn.GetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.WcsPos);
                            modDevice.Wcs = Convert.ToString(wcs);
                            //任务号
                            var modPositionTask = listPosition.FirstOrDefault(s => s.Text == "任务号");
                            (result, var taskNo) = modConn.GetPlcDBValue(modPositionTask.PosType, modDevice.DbNumber, modPositionTask.PlcPos, modPositionTask.StringLength);
                            modDevice.TaskNo = Convert.ToString(taskNo);
                            //任务类型
                            var modPositionTaskType = listPosition.FirstOrDefault(s => s.Text == "任务类型");
                            (result, var taskType) = modConn.GetPlcDBValue(modPositionTaskType.PosType, modDevice.DbNumber, modPositionTaskType.PlcPos);
                            modDevice.TaskType = (TaskTypeEnum)Convert.ToInt32(taskType);
                            //起始工位
                            var modPositionStartLocatNo = listPosition.FirstOrDefault(s => s.Text == "起始工位");
                            (result, var startLocatNo) = modConn.GetPlcDBValue(modPositionStartLocatNo.PosType, modDevice.DbNumber, modPositionStartLocatNo.PlcPos);
                            modDevice.StartLocatNo = Convert.ToString(startLocatNo);
                            //目的工位
                            var modPositionEndLocatNo = listPosition.FirstOrDefault(s => s.Text == "目的工位");
                            (result, var endLocatNo) = modConn.GetPlcDBValue(modPositionEndLocatNo.PosType, modDevice.DbNumber, modPositionEndLocatNo.PlcPos);
                            modDevice.EndLocatNo = Convert.ToString(endLocatNo);
                            //托盘码
                            var modPositionPalletNo = listPosition.FirstOrDefault(s => s.Text == "托盘码");
                            (result, var palletNo) = modConn.GetPlcDBValue(modPositionPalletNo.PosType, modDevice.DbNumber, modPositionPalletNo.PlcPos, modPositionPalletNo.StringLength);
                            modDevice.PalletNo = Convert.ToString(palletNo);
                            if (modPlc.Type == PLCTypeEnum.ConveyorLine)
                            {
                                //放货排
                                var modPositionReleaseRow = listPosition.FirstOrDefault(s => s.Text == "放货排");
                                (result, var releaseRow) = modConn.GetPlcDBValue(modPositionReleaseRow.PosType, modDevice.DbNumber, modPositionReleaseRow.PlcPos);
                                modDevice.ReleaseRow = Convert.ToInt32(releaseRow);
                                //放货列
                                var modPositionReleaseCol = listPosition.FirstOrDefault(s => s.Text == "放货列");
                                (result, var releaseCol) = modConn.GetPlcDBValue(modPositionReleaseCol.PosType, modDevice.DbNumber, modPositionReleaseCol.PlcPos);
                                modDevice.ReleaseCol = Convert.ToInt32(releaseCol);
                                //放货层
                                var modPositionReleaseStorey = listPosition.FirstOrDefault(s => s.Text == "放货层");
                                (result, var releaseStorey) = modConn.GetPlcDBValue(modPositionReleaseStorey.PosType, modDevice.DbNumber, modPositionReleaseStorey.PlcPos);
                                modDevice.ReleaseStorey = Convert.ToInt32(releaseStorey);
                                //取货排
                                var modPositionPickRow = listPosition.FirstOrDefault(s => s.Text == "取货排");
                                (result, var pickRow) = modConn.GetPlcDBValue(modPositionPickRow.PosType, modDevice.DbNumber, modPositionPickRow.PlcPos);
                                modDevice.PickRow = Convert.ToInt32(pickRow);
                                //取货列
                                var modPositionPickCol = listPosition.FirstOrDefault(s => s.Text == "取货列");
                                (result, var pickCol) = modConn.GetPlcDBValue(modPositionPickCol.PosType, modDevice.DbNumber, modPositionPickCol.PlcPos);
                                modDevice.PickCol = Convert.ToInt32(pickCol);
                                //取货层
                                var modPositionPickStorey = listPosition.FirstOrDefault(s => s.Text == "取货层");
                                (result, var pickStorey) = modConn.GetPlcDBValue(modPositionPickStorey.PosType, modDevice.DbNumber, modPositionPickStorey.PlcPos);
                                modDevice.PickStorey = Convert.ToInt32(pickStorey);
                            }
                        }
                        // 比较之前的状态
                        if (initialStates.TryGetValue(modDevice.Id, out var initialState))
                        {
                            if (modDevice.Status != initialState.Status ||
                                modDevice.Plc != initialState.Plc ||
                                modDevice.Wcs != initialState.Wcs ||
                                modDevice.TaskNo != initialState.TaskNo ||
                                modDevice.TaskType != initialState.TaskType ||
                                modDevice.StartLocatNo != initialState.StartLocatNo ||
                                modDevice.EndLocatNo != initialState.EndLocatNo ||
                                modDevice.PalletNo != initialState.PalletNo ||
                                (modPlc.Type == PLCTypeEnum.ConveyorLine &&
                                    (modDevice.ReleaseRow != initialState.ReleaseRow ||
                                     modDevice.ReleaseCol != initialState.ReleaseCol ||
                                     modDevice.ReleaseStorey != initialState.ReleaseStorey ||
                                     modDevice.PickRow != initialState.PickRow ||
                                     modDevice.PickCol != initialState.PickCol ||
                                     modDevice.PickStorey != initialState.PickStorey)))
                            {
                                // 通知用户变更
                                _plcDeviceHubContext.Clients.All.PublicPlcDevice(modDevice);
                            }
                        }
                        initialStates[modDevice.Id] = modDevice.Adapt<WcsDeviceOutput>();
                    }
                    Thread.Sleep(2000);
                }
                catch (Exception)
                {
                    throw;
                }
            }
        }, cts.Token);
    }
}
Admin.NET/WCS.Application/Service/WcsDevice/Dto/WcsDeviceInput.cs
@@ -232,4 +232,65 @@
    /// </summary>
    [Required(ErrorMessage = "起始点位不能为空")]
    public int Pos { get; set; }
}
/// <summary>
/// 写入参数
/// </summary>
public class WriteWcsDeviceInput : WcsDeviceBaseInput
{
    /// <summary>
    /// 主键Id
    /// </summary>
    [Required(ErrorMessage = "主键Id不能为空")]
    public long Id { get; set; }
    /// <summary>
    /// 任务号
    /// </summary>
    public string TaskNo { get; set; }
    /// <summary>
    /// 任务类型
    /// </summary>
    public TaskTypeEnum? TaskType { get; set; }
    /// <summary>
    /// 起始工位
    /// </summary>
    public string StartLocatNo { get; set; }
    /// <summary>
    /// 结束工位
    /// </summary>
    public string EndLocatNo { get; set; }
    /// <summary>
    /// 托盘码
    /// </summary>
    public string PalletNo { get; set; }
    public string Plc { get; set; }
    public string Wcs { get; set; }
    /// <summary>
    /// 连接状态
    /// </summary>
    public bool Status { get; set; } = false;
    /// <summary>
    /// 放货排
    /// </summary>
    public int ReleaseRow { get; set; }
    /// <summary>
    /// 放货列
    /// </summary>
    public int ReleaseCol { get; set; }
    /// <summary>
    /// 放货层
    /// </summary>
    public int ReleaseStorey { get; set; }
    /// <summary>
    /// 取货排
    /// </summary>
    public int PickRow { get; set; }
    /// <summary>
    /// 取货列
    /// </summary>
    public int PickCol { get; set; }
    /// <summary>
    /// 取货层
    /// </summary>
    public int PickStorey { get; set; }
}
Admin.NET/WCS.Application/Service/WcsDevice/Dto/WcsDeviceOutput.cs
@@ -142,8 +142,30 @@
    /// 连接状态
    /// </summary>
    public bool Status { get; set; } = false;
    /// <summary>
    /// 放货排
    /// </summary>
    public int ReleaseRow { get; set; }
    /// <summary>
    /// 放货列
    /// </summary>
    public int ReleaseCol { get; set; }
    /// <summary>
    /// 放货层
    /// </summary>
    public int ReleaseStorey { get; set; }
    /// <summary>
    /// 取货排
    /// </summary>
    public int PickRow { get; set; }
    /// <summary>
    /// 取货列
    /// </summary>
    public int PickCol { get; set; }
    /// <summary>
    /// 取货层
    /// </summary>
    public int PickStorey { get; set; }
}
Admin.NET/WCS.Application/Service/WcsDevice/WcsDeviceService.cs
@@ -251,6 +251,34 @@
                        var modPositionPalletNo = listPosition.FirstOrDefault(s => s.Text == "托盘码");
                        (result, var palletNo) = modConn.GetPlcDBValue(modPositionPalletNo.PosType, modDevice.DbNumber, modPositionPalletNo.PlcPos, modPositionPalletNo.StringLength);
                        modDevice.PalletNo = Convert.ToString(palletNo);
                        if (cachePlc.Type == PLCTypeEnum.ConveyorLine)
                        {
                            //放货排
                            var modPositionReleaseRow = listPosition.FirstOrDefault(s => s.Text == "放货排");
                            (result, var releaseRow) = modConn.GetPlcDBValue(modPositionReleaseRow.PosType, modDevice.DbNumber, modPositionReleaseRow.PlcPos);
                            modDevice.ReleaseRow = Convert.ToInt32(releaseRow);
                            //放货列
                            var modPositionReleaseCol = listPosition.FirstOrDefault(s => s.Text == "放货列");
                            (result, var releaseCol) = modConn.GetPlcDBValue(modPositionReleaseCol.PosType, modDevice.DbNumber, modPositionReleaseCol.PlcPos);
                            modDevice.ReleaseCol = Convert.ToInt32(releaseCol);
                            //放货层
                            var modPositionReleaseStorey = listPosition.FirstOrDefault(s => s.Text == "放货层");
                            (result, var releaseStorey) = modConn.GetPlcDBValue(modPositionReleaseStorey.PosType, modDevice.DbNumber, modPositionReleaseStorey.PlcPos);
                            modDevice.ReleaseStorey = Convert.ToInt32(releaseStorey);
                            //取货排
                            var modPositionPickRow = listPosition.FirstOrDefault(s => s.Text == "取货排");
                            (result, var pickRow) = modConn.GetPlcDBValue(modPositionPickRow.PosType, modDevice.DbNumber, modPositionPickRow.PlcPos);
                            modDevice.PickRow = Convert.ToInt32(pickRow);
                            //取货列
                            var modPositionPickCol = listPosition.FirstOrDefault(s => s.Text == "取货列");
                            (result, var pickCol) = modConn.GetPlcDBValue(modPositionPickCol.PosType, modDevice.DbNumber, modPositionPickCol.PlcPos);
                            modDevice.PickCol = Convert.ToInt32(pickCol);
                            //取货层
                            var modPositionPickStorey = listPosition.FirstOrDefault(s => s.Text == "取货层");
                            (result, var pickStorey) = modConn.GetPlcDBValue(modPositionPickStorey.PosType, modDevice.DbNumber, modPositionPickStorey.PlcPos);
                            modDevice.PickStorey = Convert.ToInt32(pickStorey);
                        }
                    }
                    catch (Exception)
                    {
@@ -274,6 +302,69 @@
        }
        return list;
    }
    [HttpPost]
    [ApiDescriptionSettings(Name = "WriteValue")]
    [DisplayName("写入值")]
    public async Task WriteValue(WriteWcsDeviceInput modDevice)
    {
        var modPlc = await _wcsDeviceRep.Context.Queryable<WcsPlc>().FirstAsync(s => s.Id == modDevice.PlcId);
        if (modPlc == null)
            throw Oops.Bah("找不到PLC信息");
        PLCUtil modUtil = new PLCUtil(modPlc);
        var listPosition = await _wcsDeviceRep.Context.Queryable<WcsPosition>().Where(s => s.DeviceId == modDevice.Id).ToListAsync();
        //(var result, var plc) = modConn.GetPlcDBValue(modDevice.PosType, modDevice.DbNumber, modDevice.PlcPos);
        //modDevice.Plc = Convert.ToString(plc);
        //任务号
        var modPositionTask = listPosition.FirstOrDefault(s => s.Text == "任务号");
        if (modPositionTask != null)
            modUtil.SetPlcDBValue(modPositionTask.PosType, modPositionTask.PlcPos, modDevice.TaskNo);
        //任务类型
        var modPositionTaskType = listPosition.FirstOrDefault(s => s.Text == "任务类型");
        if (modPositionTaskType != null)
            modUtil.SetPlcDBValue(modPositionTaskType.PosType, modPositionTaskType.PlcPos, modDevice.TaskType.ToString());
        //起始工位
        var modPositionStartLocatNo = listPosition.FirstOrDefault(s => s.Text == "起始工位");
        if (modPositionStartLocatNo != null)
            modUtil.SetPlcDBValue(modPositionStartLocatNo.PosType, modPositionStartLocatNo.PlcPos, modDevice.StartLocatNo.ToString());
        //目的工位
        var modPositionEndLocatNo = listPosition.FirstOrDefault(s => s.Text == "目的工位");
        if (modPositionEndLocatNo != null)
            modUtil.SetPlcDBValue(modPositionEndLocatNo.PosType, modPositionEndLocatNo.PlcPos, modDevice.EndLocatNo.ToString());
        //托盘码
        var modPositionPalletNo = listPosition.FirstOrDefault(s => s.Text == "托盘码");
        if (modPositionPalletNo != null)
            modUtil.SetPlcDBValue(modPositionPalletNo.PosType, modPositionPalletNo.PlcPos, modDevice.PalletNo.ToString());
        if (modPlc.Type == PLCTypeEnum.ConveyorLine)
        {
            //放货排
            var modPositionReleaseRow = listPosition.FirstOrDefault(s => s.Text == "放货排");
            if (modPositionReleaseRow != null)
                modUtil.SetPlcDBValue(modPositionReleaseRow.PosType, modPositionReleaseRow.PlcPos, modDevice.ReleaseRow.ToString());
            //放货列
            var modPositionReleaseCol = listPosition.FirstOrDefault(s => s.Text == "放货列");
            if (modPositionReleaseRow != null)
                modUtil.SetPlcDBValue(modPositionReleaseCol.PosType, modPositionReleaseCol.PlcPos, modDevice.ReleaseCol.ToString());
            //放货层
            var modPositionReleaseStorey = listPosition.FirstOrDefault(s => s.Text == "放货层");
            if (modPositionReleaseStorey != null)
                modUtil.SetPlcDBValue(modPositionReleaseStorey.PosType, modPositionReleaseStorey.PlcPos, modDevice.ReleaseStorey.ToString());
            //取货排
            var modPositionPickRow = listPosition.FirstOrDefault(s => s.Text == "取货排");
            if (modPositionPickRow != null)
                modUtil.SetPlcDBValue(modPositionPickRow.PosType, modPositionPickRow.PlcPos, modDevice.PickRow.ToString());
            //取货列
            var modPositionPickCol = listPosition.FirstOrDefault(s => s.Text == "取货列");
            if (modPositionPickCol != null)
                modUtil.SetPlcDBValue(modPositionPickCol.PosType, modPositionPickCol.PlcPos, modDevice.PickCol.ToString());
            //取货层
            var modPositionPickStorey = listPosition.FirstOrDefault(s => s.Text == "取货层");
            if (modPositionPickStorey != null)
                modUtil.SetPlcDBValue(modPositionPickStorey.PosType, modPositionPickStorey.PlcPos, modDevice.PickStorey.ToString());
        }
        modUtil.Close();
    }
    #region 分拣码垛
@@ -304,8 +395,8 @@
    [HttpGet]
    [ApiDescriptionSettings(Name = "WcsPackStationPlcList")]
    [DisplayName("获取设备对应工位列表")]
    public async Task<List<WcsDeviceTaskOrderDto>> WcsPackStationPlcList([FromQuery]WcsDeviceBaseInput entry)
    {
    public async Task<List<WcsDeviceTaskOrderDto>> WcsPackStationPlcList([FromQuery] WcsDeviceBaseInput entry)
    {
        return await _wcsDeviceRep.Context.Queryable<WcsDevice>()
                .LeftJoin<WcsCheckTask>((device, task) => device.StationNum == task.Port)
                .Where((device, task) => device.PlcId == entry.PlcId)
Web/src/api/wcs/wcsDevice.ts
@@ -8,6 +8,7 @@
  GetWcsPlcPlcIdDropdown = '/api/wcsDevice/WcsPlcPlcIdDropdown',
  GeneratePos = '/api/wcsDevice/GeneratePos',
  ListWcsDevice = '/api/wcsDevice/list',
  WriteValue = '/api/wcsdevice/writeValue',
  GetWcsPackPlcList='/api/wcsDevice/WcsPackPlcList',
  GetWcsPackStationPlcList='/api/wcsDevice/WcsPackStationPlcList',
@@ -101,4 +102,11 @@
        url: Api.CloseTaskForPLC,
        method: 'post',
        data: params,
});
//写入值
export const writeValue = (params?: any) =>
    request({
        url: Api.WriteValue,
        method: 'post',
        data: params,
});
Web/src/views/device/deviceMonitor/component/setting.vue
@@ -10,7 +10,7 @@
            <el-card class="box-card" shadow="hover">
                <template #header>
                    <div class="card-header">
                        <div>
                            <span>工位号:</span>
                            <el-select v-model="stationValue" placeholder="请选择" filterable value-key="stationNum"
@@ -22,7 +22,8 @@
                            <el-button style="margin-left: 10px;">自动</el-button>
                        </div>
                        <div>
                            <div :class="['lineStatus', stationValue.status ? 'device-status-0' : 'device-status-1']"></div>
                            <div :class="['lineStatus', stationValue.status ? 'device-status-0' : 'device-status-1']">
                            </div>
                        </div>
                    </div>
                </template>
@@ -49,41 +50,41 @@
                                    <el-input v-model="stationValue.wcs"></el-input>
                                </el-form-item>
                            </el-col>
                            <el-col :span="12">
                                <el-form-item label="取货排">
                                    <el-input ></el-input>
                                    <el-input></el-input>
                                </el-form-item>
                            </el-col>
                            <el-col :span="12">
                                <el-form-item label="放货排">
                                    <el-input ></el-input>
                                    <el-input></el-input>
                                </el-form-item>
                            </el-col>
                            <el-col :span="12">
                                <el-form-item label="取货列">
                                    <el-input ></el-input>
                                    <el-input></el-input>
                                </el-form-item>
                            </el-col>
                            <el-col :span="12">
                                <el-form-item label="放货列">
                                    <el-input ></el-input>
                                    <el-input></el-input>
                                </el-form-item>
                            </el-col>
                            <el-col :span="12">
                                <el-form-item label="取货层">
                                    <el-input ></el-input>
                                    <el-input></el-input>
                                </el-form-item>
                            </el-col>
                            <el-col :span="12">
                                <el-form-item label="放货层">
                                    <el-input ></el-input>
                                    <el-input></el-input>
                                </el-form-item>
                            </el-col>
                        </el-row>
                    </el-form>
                    <div style="text-align: center;margin: 10px;">
                        <el-button class="button" size="large">写入数据</el-button>
                        <el-button class="button" size="large" @click="write(stationValue)">写入数据</el-button>
                    </div>
                </div>
                <template #footer>
@@ -107,6 +108,8 @@
<script lang="ts" setup>
import { ref, defineModel } from 'vue';
import { writeValue } from '/@/api/wcs/wcsDevice';
import { ElMessageBox,ElMessage } from 'element-plus';
const listStationsData = defineModel<any>("listStationsData")
const stationValue = defineModel<any>("stationValue")
@@ -122,7 +125,10 @@
//     emit('update:listStationsData', listStationsData.value);
//     emit('update:stationValue', stationValue);
// };
const write = async (row: any) => {
    await writeValue(row);
    ElMessage.success('写入成功!');
}
const isShowDialog = ref(false);
const openDialog = async (row: any) => {
@@ -181,18 +187,18 @@
}
.lineStatus {
    right: 0;
    height: 20px;
    width: 20px;
    border-radius: 50%;
    background-color: #67C23A;
    right: 0;
    height: 20px;
    width: 20px;
    border-radius: 50%;
    background-color: #67C23A;
}
.device-status-0 {
    background-color: #67C23A;
    background-color: #67C23A;
}
.device-status-1 {
    background-color: red;
    background-color: red;
}
</style>
Web/src/views/device/deviceMonitor/index.vue
@@ -52,7 +52,6 @@
                <div class="lineButtonfix">
                    <el-form label-position="left" label-width="80px">
                        <el-form-item>
                            <el-button>写入</el-button>
                            <el-button @click="openDialog">设置</el-button>
                        </el-form-item>
                    </el-form>
@@ -85,6 +84,9 @@
                            <el-form-item label="目的工位">
                                <el-input v-model="deviceInfo.endLocatNo"></el-input>
                            </el-form-item>
                            <el-form-item label="托盘码">
                                <el-input v-model="deviceInfo.palletNo"></el-input>
                            </el-form-item>
                            <el-form-item label="PLC">
                                <el-input v-model="deviceInfo.plc"></el-input>
                            </el-form-item>
@@ -99,24 +101,47 @@
                    <div class="otherButtonfix">
                        <el-form label-position="left">
                            <el-form-item>
                                <el-button>写入</el-button>
                                <el-button @click="write(deviceInfo)">写入</el-button>
                            </el-form-item>
                        </el-form>
                    </div>
                </el-card>
            </div>
        </el-main>
        <setting ref="settingDialogRef" :title="title" v-model:listStationsData="listStationsData" v-model:stationValue="stationValue" />
        <setting ref="settingDialogRef" :title="title" v-model:listStationsData="listStationsData"
            v-model:stationValue="stationValue" />
    </el-container>
</template>
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import { listWcsDevice } from '/@/api/wcs/wcsDevice';
import { ref, onMounted } from 'vue';
import { listWcsDevice, writeValue } from '/@/api/wcs/wcsDevice';
import { getDictDataItem as di, getDictDataList as dl } from '/@/utils/dict-utils';
import { listWcsPlc } from '/@/api/wcs/wcsPlc';
import { ElMessageBox, ElMessage } from 'element-plus';
import setting from '/@/views/device/deviceMonitor/component/setting.vue'
import { signalR } from './signalR';
//连接signalR 监听变更
onMounted(async () => {
    signalR.off('PublicPlcDevice');
    signalR.on('PublicPlcDevice', (data: any) => {
        //todo 需要测试
        if (data.type == 0) {
            var index = listStackingMachineData.value.findIndex(s => s.id == data.id);
            if (index !== -1) {
                listStackingMachineData.value.splice(index, 1, data);
            }
        }
        else if (data.type == 1) {
            stations.value.forEach(list => {
                var index = list.findIndex(s => s.id == data.id);
                if (index !== -1) {
                    list.splice(index, 1, data);
                }
            });
        }
    });
});
const stations = ref<any>([]);
const listStationsData = ref<any>([]);
@@ -138,6 +163,10 @@
const openDialog = async () => {
    settingDialogRef.value.openDialog(stationValue);
}
const write = async (row: any) => {
    await writeValue(row);
    ElMessage.success('写入成功!');
}
// 查询操作
const handleQuery = async () => {
    var listplc = await listWcsPlc({ type: 1 });
@@ -153,6 +182,19 @@
        listStationsData.value = stations.value[lineValue.value];
        stationValue.value = listStationsData.value[0];
    }
    else {
        listStationsData.value = [];
        stationValue.value = {
            stationNum: '',
            taskNo: '',
            taskType: '',
            startLocatNo: '',
            endLocatNo: '',
            plc: '',
            wcs: '',
            status: false
        };
    }
    title.value = listStationsData.value[0].text;
};
Web/src/views/device/deviceMonitor/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/PlcDevice?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('启动连接plc');
});
// 断开连接
connection.onclose(async () => {
    console.log('断开连接plc');
});
// 重连中
connection.onreconnecting(() => {
    console.log('服务器已断线plc');
});
// 重连成功
connection.onreconnected(() => {
    console.log('重连成功plc');
});
// connection.on('PublicPlcConn', () => {});
export { connection as signalR };