Prechádzať zdrojové kódy

增加plc报警显示

zly 4 mesiacov pred
rodič
commit
6f9a30a853

+ 3 - 3
TeamAAS-VM/App.config

@@ -54,7 +54,7 @@
       <!--日志类的名字-->
       <!--日志类的名字-->
       <level value="ALL" />
       <level value="ALL" />
       <!--定义记录的日志级别-->
       <!--定义记录的日志级别-->
-      <appender-ref ref="WarnAppender" />
+      <appender-ref ref="PlcWarnAppender" />
       <!--记录到哪个介质中去-->
       <!--记录到哪个介质中去-->
     </logger>
     </logger>
     <!--重大错误日志类-->
     <!--重大错误日志类-->
@@ -156,8 +156,8 @@
       </layout>
       </layout>
     </appender>
     </appender>
     <!--警告日志附加介质-->
     <!--警告日志附加介质-->
-    <appender name="WarnAppender" type="log4net.Appender.RollingFileAppender">
-      <param name="File" value="..\\Log\\LogWarn\\" />
+    <appender name="PlcWarnAppender" type="log4net.Appender.RollingFileAppender">
+      <param name="File" value="..\\Log\\LogPlcWarn\\" />
       <param name="AppendToFile" value="true" />
       <param name="AppendToFile" value="true" />
       <param name="MaxFileSize" value="10240" />
       <param name="MaxFileSize" value="10240" />
       <param name="MaxSizeRollBackups" value="100" />
       <param name="MaxSizeRollBackups" value="100" />

+ 58 - 0
TeamAAS-VM/Core/Management.cs

@@ -1,6 +1,7 @@
 using Cognex.VisionPro.ToolBlock;
 using Cognex.VisionPro.ToolBlock;
 using MahApps.Metro.Controls;
 using MahApps.Metro.Controls;
 using NPOI.OpenXmlFormats.Vml;
 using NPOI.OpenXmlFormats.Vml;
+using Opc.Ua;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto;
 using Prism.Events;
 using Prism.Events;
 using Prism.Ioc;
 using Prism.Ioc;
@@ -14,13 +15,16 @@ using System.IO;
 using System.IO.Compression;
 using System.IO.Compression;
 using System.Linq;
 using System.Linq;
 using System.Net.Sockets;
 using System.Net.Sockets;
+using System.Security.Claims;
 using System.Text;
 using System.Text;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using System.Web.UI.WebControls;
 using System.Windows.Media;
 using System.Windows.Media;
 using Team.FFFeederService.Interfaces;
 using Team.FFFeederService.Interfaces;
 using TeamAAS_VP.Core.PLCs;
 using TeamAAS_VP.Core.PLCs;
 using TeamAAS_VP.Core.ScrewDriver;
 using TeamAAS_VP.Core.ScrewDriver;
+using TeamAAS_VP.Data;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Events;
 using TeamAAS_VP.Events;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Interfaces;
@@ -2037,6 +2041,7 @@ namespace TeamAAS_VP.Core
                             {
                             {
                                 lastRes = true;
                                 lastRes = true;
                             }
                             }
+                            lockResult.LockPassed = lastRes;
                             dataToMes.Add($"Torque{lockResult.Number}", $"{lockResult.LockTorque:F2}");
                             dataToMes.Add($"Torque{lockResult.Number}", $"{lockResult.LockTorque:F2}");
                             dataToMes.Add($"Turn{lockResult.Number}", $"{lockResult.LockTurns:F2}");
                             dataToMes.Add($"Turn{lockResult.Number}", $"{lockResult.LockTurns:F2}");
                             dataToMes.Add($"Pressure{lockResult.Number}", $"{lockResult.Pressure:F2}");
                             dataToMes.Add($"Pressure{lockResult.Number}", $"{lockResult.Pressure:F2}");
@@ -2402,6 +2407,7 @@ namespace TeamAAS_VP.Core
                         }
                         }
                         if (state)
                         if (state)
                         {
                         {
+                            SendAlarmToUi(plc);
                             SendTaskMessage("收到PLC报警", MessageLevel.Error);
                             SendTaskMessage("收到PLC报警", MessageLevel.Error);
                         }
                         }
                         else
                         else
@@ -4318,6 +4324,58 @@ namespace TeamAAS_VP.Core
 
 
         #endregion
         #endregion
 
 
+        /// <summary>
+        /// 后台报警刷新显示(适配PLC结构化变量名Bool点位 OPC版)
+        /// 完全沿用你原有循环结构、防抖逻辑、UI报警推送规则
+        /// </summary>
+        public void SendAlarmToUi(OpcUaClientPLC plc)
+        {
+            PlcExcelLoad loadData = new PlcExcelLoad();
+            loadData.LoadData(6);
+
+            // 增加报警状态缓存,防止循环重复刷屏报警
+            Dictionary<string, bool> alarmLastState = new Dictionary<string, bool>();
+            foreach (var alarm in loadData.AlaemList)
+            {
+                alarmLastState.Add(alarm.OpcAddress, true);
+            }
+
+            Task.Run(() =>
+            {
+                while (true)
+                {
+                    try
+                    {
+                        Thread.Sleep(50);
+
+                        foreach (var alarm in loadData.AlaemList)
+                        {
+                            // 调用你封装好的OPC读取Bool方法
+                            bool plcState = plc.ReadBoolNode(alarm.OpcAddress);
+
+                            // 上升沿防抖:只有状态从正常→异常(false)变化时,才推送一次报警
+                            if (!plcState && alarmLastState[alarm.OpcAddress])
+                            {
+                                SendTaskMessage($"PLC报警信息:{alarm.Name}", MessageLevel.Error);
+                                LogHelper.WriteLogPlcWarn($"PLC报警信息:{alarm.Name}");
+                                alarmLastState[alarm.OpcAddress] = false;
+                            }
+                            // 报警复位:状态恢复正常,重置缓存
+                            else if (plcState && !alarmLastState[alarm.OpcAddress])
+                            {
+                                alarmLastState[alarm.OpcAddress] = true;
+                            }
+                        }
+                    }
+                    catch
+                    {
+                        Thread.Sleep(200);
+                    }
+                }
+            });
+        }
+
+
         #endregion
         #endregion
     }
     }
 }
 }

+ 19 - 0
TeamAAS-VM/Core/PLCs/OPCuaClientPLC.cs

@@ -284,6 +284,25 @@ namespace TeamAAS_VP.Core.PLCs
             return result;
             return result;
         }
         }
 
 
+        /// <summary>
+        /// 读取单个PLC Bool布尔节点值
+        /// </summary>
+        /// <param name="nodeId">Excel里PLC变量名 Stop.XXX</param>
+        /// <returns>bool类型真实状态</returns>
+        public bool ReadBoolNode(string nodeId)
+        {
+            string readNodeId = nodeId;
+            if (!nodeId.StartsWith(NodeHeader))
+            {
+                readNodeId = NodeHeader + nodeId;
+            }
+
+            var value = OpcUaClient.ReadNode(new NodeId(readNodeId));
+            // 安全转换为布尔类型
+            return Convert.ToBoolean(value.Value);
+        }
+
+
         /// <summary>
         /// <summary>
         /// 读取单个节点的值
         /// 读取单个节点的值
         /// </summary>
         /// </summary>

+ 1 - 1
TeamAAS-VM/LogHelper.cs

@@ -35,7 +35,7 @@ namespace TeamAAS_VP
             }
             }
         }
         }
 
 
-        public static void WriteLogWarn(string info)
+        public static void WriteLogPlcWarn(string info)
         {
         {
             if (logwarn.IsWarnEnabled)
             if (logwarn.IsWarnEnabled)
             {
             {

+ 213 - 0
TeamAAS-VM/Models/PLC/PlcExcelLoad.cs

@@ -0,0 +1,213 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using NPOI.SS.UserModel;
+using NPOI.XSSF.UserModel;
+
+namespace TeamAAS_VP.Models.PLC
+{
+    public class PlcExcelLoad
+    {
+        /// <summary>
+        /// 真正的 bool 类型地址结构(无字符串类型)
+        /// </summary>
+        public class PLCAddrs
+        {
+            public string OpcAddress { get; set; }   // PLC地址(变量名)
+            public string Name { get; set; }         // 报警文本
+            public bool Value { get; set; }          // 真实 BOOL 类型值
+        }
+
+        // 默认PLC地址表路径
+        private string _filePath = Path.Combine(Path.GetDirectoryName(Application.StartupPath), @"Configuration Files\PLC地址表.xlsx");
+
+        // 所有集合统一使用 PLCAddrs
+        public List<PLCAddrs> IOInputList = new List<PLCAddrs>();
+        public List<PLCAddrs> IOOutputList = new List<PLCAddrs>();
+        public List<PLCAddrs> JoggingList = new List<PLCAddrs>();
+        public List<PLCAddrs> AlarmMaskList = new List<PLCAddrs>();
+        public List<PLCAddrs> InputParamList = new List<PLCAddrs>();
+        public List<PLCAddrs> OutputParamList = new List<PLCAddrs>();
+        public List<PLCAddrs> AlaemList = new List<PLCAddrs>();      // 报警列表
+        public List<PLCAddrs> SysStateList = new List<PLCAddrs>();   // 系统状态
+
+        public string FilePath
+        {
+            get => _filePath;
+            set => _filePath = value;
+        }
+
+        public PlcExcelLoad(string filePathStr = null)
+        {
+            if (!string.IsNullOrEmpty(filePathStr))
+                _filePath = filePathStr;
+        }
+
+        // Excel工作表转DataTable
+        public DataTable GetSheetData(int sheetIndex)
+        {
+            DataTable dt = new DataTable();
+            try
+            {
+                using (var fs = new FileStream(_filePath, FileMode.Open, FileAccess.Read))
+                {
+                    IWorkbook workbook = new XSSFWorkbook(fs);
+                    ISheet sheet = workbook.GetSheetAt(sheetIndex);
+                    if (sheet == null || sheet.GetRow(0) == null) return null;
+
+                    IRow headerRow = sheet.GetRow(0);
+                    for (int i = 0; i < headerRow.LastCellNum; i++)
+                        dt.Columns.Add(headerRow.GetCell(i)?.ToString()?.Trim() ?? $"Column{i}");
+
+                    for (int i = sheet.FirstRowNum + 1; i <= sheet.LastRowNum; i++)
+                    {
+                        IRow row = sheet.GetRow(i);
+                        if (row == null) continue;
+
+                        DataRow dataRow = dt.NewRow();
+                        for (int j = 0; j < dt.Columns.Count; j++)
+                            dataRow[j] = row.GetCell(j)?.ToString()?.Trim() ?? "";
+
+                        dt.Rows.Add(dataRow);
+                    }
+                }
+                return dt;
+            }
+            catch
+            {
+                return null;
+            }
+        }
+
+        /// <summary>
+        /// 加载Excel数据(全部是真实 bool 变量,无字符串类型)
+        /// </summary>
+        public void LoadData(int sheetIndex)
+        {
+            try
+            {
+                if (!File.Exists(_filePath)) return;
+                DataTable dt = GetSheetData(sheetIndex);
+                if (dt == null || dt.Rows.Count == 0) return;
+
+                RemoveEmptyRow(dt);
+
+                foreach (DataRow row in dt.Rows)
+                {
+                    PLCAddrs temp = new PLCAddrs();
+
+                    switch (sheetIndex)
+                    {
+                        case 0: // IO输入
+                            temp.OpcAddress = row[0].ToString().Trim();
+                            temp.Name = row[1].ToString().Trim();
+                            IOInputList.Add(temp);
+                            break;
+
+                        case 1: // IO输出
+                            temp.OpcAddress = row[0].ToString().Trim();
+                            temp.Name = row[1].ToString().Trim();
+                            IOOutputList.Add(temp);
+                            break;
+
+                        case 2: // 点动
+                            temp.OpcAddress = row[0].ToString().Trim();
+                            temp.Name = row[1].ToString().Trim();
+                            JoggingList.Add(temp);
+                            break;
+
+                        case 3: // 参数输入
+                            temp.OpcAddress = row[0].ToString().Trim();
+                            temp.Name = row[1].ToString().Trim();
+                            InputParamList.Add(temp);
+                            break;
+
+                        case 4: // 参数输出
+                            temp.OpcAddress = row[0].ToString().Trim();
+                            temp.Name = row[1].ToString().Trim();
+                            OutputParamList.Add(temp);
+                            break;
+
+                        case 5: // 报警屏蔽
+                            temp.OpcAddress = row[0].ToString().Trim();
+                            temp.Name = row[1].ToString().Trim();
+                            AlarmMaskList.Add(temp);
+                            break;
+
+                        case 6: // 报警信息(A列名称,B列地址)
+                            temp.Name = row[0].ToString().Trim();       // 报警文本
+                            temp.OpcAddress = row[1].ToString().Trim(); // PLC地址
+                            AlaemList.Add(temp);
+                            break;
+
+                        case 7: // 系统状态
+                            temp.OpcAddress = row[0].ToString().Trim();
+                            temp.Name = row[1].ToString().Trim();
+                            SysStateList.Add(temp);
+                            break;
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show($"加载PLC地址表失败:{ex.Message}");
+            }
+        }
+
+        // 清除空行
+        public void RemoveEmptyRow(DataTable dt)
+        {
+            List<DataRow> delRows = new List<DataRow>();
+            foreach (DataRow row in dt.Rows)
+            {
+                bool isEmpty = true;
+                foreach (var item in row.ItemArray)
+                {
+                    if (!string.IsNullOrWhiteSpace(item?.ToString()))
+                    {
+                        isEmpty = false;
+                        break;
+                    }
+                }
+                if (isEmpty) delRows.Add(row);
+            }
+            foreach (var row in delRows) dt.Rows.Remove(row);
+        }
+
+        // 安全写入Excel
+        public void SaveCellValue(string sheetName, int rowIndex, int colIndex, object value)
+        {
+            if (!File.Exists(_filePath))
+            {
+                MessageBox.Show("PLC地址表不存在!");
+                return;
+            }
+
+            try
+            {
+                using (FileStream fs = new FileStream(_filePath, FileMode.Open, FileAccess.ReadWrite))
+                {
+                    IWorkbook workbook = new XSSFWorkbook(fs);
+                    ISheet sheet = workbook.GetSheet(sheetName) ?? workbook.CreateSheet(sheetName);
+                    IRow row = sheet.GetRow(rowIndex) ?? sheet.CreateRow(rowIndex);
+                    ICell cell = row.GetCell(colIndex) ?? row.CreateCell(colIndex);
+                    cell.SetCellValue(value?.ToString() ?? "");
+
+                    fs.SetLength(0);
+                    workbook.Write(fs);
+                }
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show($"Excel写入失败:{ex.Message}");
+            }
+        }
+    }
+
+
+}

+ 1 - 0
TeamAAS-VM/TeamAAS-VP.csproj

@@ -557,6 +557,7 @@
     <Compile Include="Models\ImageParameters.cs" />
     <Compile Include="Models\ImageParameters.cs" />
     <Compile Include="Models\NozzleChangeRecord.cs" />
     <Compile Include="Models\NozzleChangeRecord.cs" />
     <Compile Include="Models\NumberStatistics.cs" />
     <Compile Include="Models\NumberStatistics.cs" />
+    <Compile Include="Models\PLC\PlcExcelLoad.cs" />
     <Compile Include="Models\ProductWithMes.cs" />
     <Compile Include="Models\ProductWithMes.cs" />
     <Compile Include="Models\Product\ProductGlobalParamEx.cs" />
     <Compile Include="Models\Product\ProductGlobalParamEx.cs" />
     <Compile Include="Models\ThrowRecord.cs" />
     <Compile Include="Models\ThrowRecord.cs" />