zly 3 months ago
parent
commit
7224b341c3

+ 3 - 3
TeamAAS-VM/App.config

@@ -54,7 +54,7 @@
       <!--日志类的名字-->
       <level value="ALL" />
       <!--定义记录的日志级别-->
-      <appender-ref ref="WarnAppender" />
+      <appender-ref ref="PlcWarnAppender" />
       <!--记录到哪个介质中去-->
     </logger>
     <!--重大错误日志类-->
@@ -156,8 +156,8 @@
       </layout>
     </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="MaxFileSize" value="10240" />
       <param name="MaxSizeRollBackups" value="100" />

+ 4 - 4
TeamAAS-VM/Controls/ParameterEdit.xaml

@@ -55,16 +55,16 @@
                 <DataGridTextColumn Header="Name"
                                     MinWidth="80"
                                     Binding="{Binding Name, Mode=TwoWay}" />
-                <DataGridTextColumn Header="Param1"
+                <DataGridTextColumn Header="X下限"
                                     MinWidth="80"
                                     Binding="{Binding Param1, Mode=TwoWay}" />
-                <DataGridTextColumn Header="Param2"
+                <DataGridTextColumn Header="X上限"
                                     MinWidth="80"
                                     Binding="{Binding Param2, Mode=TwoWay}" />
-                <DataGridTextColumn Header="Param3"
+                <DataGridTextColumn Header="Y下限"
                                     MinWidth="80"
                                     Binding="{Binding Param3, Mode=TwoWay}" />
-                <DataGridTextColumn Header="Param4"
+                <DataGridTextColumn Header="Y上限"
                                     MinWidth="80"
                                     Binding="{Binding Param4, Mode=TwoWay}" />
             </DataGrid.Columns>

+ 314 - 16
TeamAAS-VM/Core/Management.cs

@@ -1,5 +1,7 @@
 using Cognex.VisionPro.ToolBlock;
+using MahApps.Metro.Controls;
 using NPOI.OpenXmlFormats.Vml;
+using Opc.Ua;
 using Org.BouncyCastle.Crypto;
 using Prism.Events;
 using Prism.Ioc;
@@ -13,13 +15,16 @@ using System.IO;
 using System.IO.Compression;
 using System.Linq;
 using System.Net.Sockets;
+using System.Security.Claims;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
+using System.Web.UI.WebControls;
 using System.Windows.Media;
 using Team.FFFeederService.Interfaces;
 using TeamAAS_VP.Core.PLCs;
 using TeamAAS_VP.Core.ScrewDriver;
+using TeamAAS_VP.Data;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Events;
 using TeamAAS_VP.Interfaces;
@@ -1809,11 +1814,6 @@ namespace TeamAAS_VP.Core
                             var visionResult = await _remoteCommandService.ExecutePhotoGetSinglePoint(process, null, new double[] { currentPosition.X, currentPosition.Y, currentPosition.U }, _cts.Token, cameraIndex, num, null, null);
                             if (visionResult.IsSucceed)
                             {
-
-                                //if ()
-                                //{
-
-                                //}
                                 await plc.WriteNodeAsync(addressConfig.Out_UPCameracheckStatus.Address, (Int16)1);
                                 SendTaskMessage($"点位{cameraIndex}检测OK", MessageLevel.Debug);
                             }
@@ -2086,6 +2086,7 @@ namespace TeamAAS_VP.Core
                             {
                                 dataToMes.Remove($"Pressure{lockResult.Number}");
                             }
+                            lockResult.LockPassed = lastRes;
                             dataToMes.Add($"Torque{lockResult.Number}", $"{lockResult.LockTorque:F2}");
                             dataToMes.Add($"Turn{lockResult.Number}", $"{lockResult.LockTurns:F2}");
                             dataToMes.Add($"Pressure{lockResult.Number}", $"{lockResult.Pressure:F2}");
@@ -2155,18 +2156,49 @@ namespace TeamAAS_VP.Core
                                         plcPoints.Add(item);
                                     }
                                 }
-                                var point = plcPoints[positionIndex - 1];
-                                if (point == null)
+                                var TempPoint = plcPoints[positionIndex - 1];
+                                if (TempPoint == null)
                                 {
                                     SendTaskMessage($"点:{positionIndex}不存在", MessageLevel.Info);
                                     await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
                                     return;
                                 }
-                                point = point.Clone();
-                                point.X_Position = UpCameraPutResults[1].X + DowmCameraResults[1].X;
-                                point.Y_Position = UpCameraPutResults[1].Y + DowmCameraResults[1].Y;
-                                SendTaskMessage($"点:{positionIndex},{point.X_Position:F3},{point.Y_Position:F4}", MessageLevel.Info);
-                                await point.WriteToPlcAddress(addressConfig.Out_Screw, plc, positionIndex);
+                                var PositionPoint = TempPoint.Clone();
+                                PositionPoint.X_Position = UpCameraPutResults[1].X + DowmCameraResults[1].X;
+                                PositionPoint.Y_Position = UpCameraPutResults[1].Y + DowmCameraResults[1].Y;
+                                SendTaskMessage($"点:{positionIndex},{PositionPoint.X_Position:F3},{PositionPoint.Y_Position:F4}", MessageLevel.Info);
+                                if (PositionPoint.X_Position == 0 || PositionPoint.Y_Position == 0)
+                                {
+                                    SendTaskMessage("拟合位置为0,请检查", MessageLevel.Error);
+                                    await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
+                                    return;
+                                }
+                                if (_configService.GetDeviceInfo().EnableXY)
+                                {
+                                    //验证当前锁附点位与设置锁附点位的差值是否超限,超限则报警并停止流程
+                                    //目前验证已OK 缺少切换按钮
+                                    try
+                                    {
+                                        var ScrewLimit = currentProduct.GlobalParamListCollection.FirstOrDefault((p) => p.Number == positionIndex - 1);
+                                        //判断实际偏差与设置的点位差数是否偏差过大
+                                        float XOffsetPoint = Math.Abs(PositionPoint.X_Position - (TempPoint.X_Position + TempPoint.X_Offset));
+                                        float YOffsetPoint = Math.Abs(PositionPoint.Y_Position - (TempPoint.Y_Position + TempPoint.Y_Offset));
+                                        if (!(XOffsetPoint >= float.Parse(ScrewLimit.Param1) && XOffsetPoint <= float.Parse(ScrewLimit.Param2)) ||
+                                            !(YOffsetPoint >= float.Parse(ScrewLimit.Param3) && YOffsetPoint <= float.Parse(ScrewLimit.Param4)))
+                                        {
+                                            SendTaskMessage($"当前锁附点位{positionIndex}与设置锁附点位差值超限,X偏差:{XOffsetPoint},Y偏差{YOffsetPoint}", MessageLevel.Error);
+                                            await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
+                                            return;
+                                        }
+                                    }
+                                    catch (Exception ex)
+                                    {
+                                        SendTaskMessage($"当前锁附点位与设置锁附点位差值未设置:" + ex.Message, MessageLevel.Error);
+                                        await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
+                                        return;
+                                    }
+                                }
+                                await PositionPoint.WriteToPlcAddress(addressConfig.Out_Screw, plc, positionIndex);
                             }
                             //拍2打所有
                             else if (systemConfig.WorkMode == 2)
@@ -2191,6 +2223,25 @@ namespace TeamAAS_VP.Core
                                     point.X_Position = (float)worldPoint[0];
                                     point.Y_Position = (float)worldPoint[1];
                                     SendTaskMessage($"点:{i},{point.X_Position:F3},{point.Y_Position:F4}", MessageLevel.Info);
+                                    try
+                                    {
+                                        var ScrewLimit = currentProduct.GlobalParamListCollection.FirstOrDefault((p) => p.Number == positionIndex - 1);
+                                        //判断实际偏差与设置的点位差数是否偏差过大
+                                        float XOffsetPoint = Math.Abs(localPoint.X - (float)worldPoint[0]);
+                                        float YOffsetPoint = Math.Abs(localPoint.Y - (float)worldPoint[1]);
+                                        if ((XOffsetPoint >= float.Parse(ScrewLimit.Param1) && XOffsetPoint <= float.Parse(ScrewLimit.Param2)) ||
+                                            (YOffsetPoint >= float.Parse(ScrewLimit.Param3) && YOffsetPoint <= float.Parse(ScrewLimit.Param4)))
+                                        {
+                                            SendTaskMessage($"当前锁附点位{i}与设置锁附点位差值超限,X偏差:{XOffsetPoint},Y偏差{YOffsetPoint}", MessageLevel.Error);
+                                            await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
+                                            return;
+                                        }
+                                    }
+                                    catch (Exception ex)
+                                    {
+                                        SendTaskMessage($"当前锁附点位与设置锁附点位差值超限:" + ex.Message, MessageLevel.Error);
+                                        await plc.WriteNodeAsync(addressConfig.Out_CameraDone.Address, (Int16)2);
+                                    }
                                     await point.WriteToPlcAddress(addressConfig.Out_Screw, plc);
                                 }
                                 await plc.WriteNodeAsync(addressConfig.Out_WorkNum.Address, (Int16)currentProduct.ScrewPoints.Count);
@@ -2457,6 +2508,7 @@ namespace TeamAAS_VP.Core
                         }
                         if (state)
                         {
+                            SendAlarmToUi(plc);
                             SendTaskMessage("收到PLC报警", MessageLevel.Error);
                         }
                         else
@@ -2817,7 +2869,14 @@ namespace TeamAAS_VP.Core
 
                                                     //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
                                                     await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
-                                                    await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
+                                                    if (models)
+                                                    {
+                                                        await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
+                                                    }
+                                                    else
+                                                    {
+                                                        await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
+                                                    }
                                                 }
                                             }
                                             else
@@ -2827,7 +2886,14 @@ namespace TeamAAS_VP.Core
 
                                                 //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
                                                 await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
-                                                await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
+                                                if (models)
+                                                {
+                                                    await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
+                                                }
+                                                else
+                                                {
+                                                    await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
+                                                }
                                             }
                                         }
                                         LogHelper.WriteLogMes($"【mes结束QUERY】");
@@ -2854,6 +2920,7 @@ namespace TeamAAS_VP.Core
                                                 // string productccode = ProductMainSN[i];
                                                 //获取当前工位的配置
                                     var stationConfig = _configService.GetDeviceInfo(i);
+                                    var sysConfig = _configService.GetSystemConfiguration();
                                     SendTaskMessage($"收到{i}:{stationConfig.DeviceName}上传本站数据请求...", MessageLevel.Debug);
                                     //从服务器中获取当前时间
                                     LogHelper.WriteLogMes($"【从服务器中获取当前时间】");
@@ -2958,7 +3025,7 @@ namespace TeamAAS_VP.Core
                                                 MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
                                                 PROCESS_OUTCOME = turnRes ? "PASS" : "FAIL",
                                                 PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
-                                                CTQ_UNIT_OF_MEASURE = "",
+                                                CTQ_UNIT_OF_MEASURE = "Turn",
                                                 ERROR_MESSAGE = turnRes ? "NA" : "Turn_Spec OOS",
                                                 PD_ATTR_01 = ProductMainSN[i],
                                                 PD_ATTR_02 = midValue,
@@ -2986,7 +3053,7 @@ namespace TeamAAS_VP.Core
                                                 MFG_ASSY_LINE = stationConfig.DataInfo.MFG_ASSY_LINE,
                                                 PROCESS_OUTCOME = pressureRes ? "PASS" : "FAIL",
                                                 PROCESS_MACHINE_ID = stationConfig.DataInfo.PROCESS_MACHINE_ID,
-                                                CTQ_UNIT_OF_MEASURE = "N",
+                                                CTQ_UNIT_OF_MEASURE = "kg",
                                                 ERROR_MESSAGE = pressureRes ? "NA" : "Pressure_Spec OOS",
                                                 PD_ATTR_01 = ProductMainSN[i],
                                                 PD_ATTR_02 = midValue,
@@ -3060,6 +3127,13 @@ namespace TeamAAS_VP.Core
                                     //上传mes
                                     if (stationConfig.EnableMES)
                                     {
+
+                                        //测试使用,强制上传mes为pass
+                                        if (sysConfig.IsMesResult)
+                                        {
+                                            resultToMes.Clear();
+                                        }
+
                                         (bool isSuccess, string response) = await _mesService.SubmitGetExAsync(ProductMainSN[i], "", dataToMes, resultToMes, _LastMesCheckTime[i], serverTimeResult.Now);
                                         if (isSuccess)
                                         {
@@ -3198,6 +3272,178 @@ namespace TeamAAS_VP.Core
                                     LogHelper.WriteLogMes($"【mes结束ADD】");
                                     SendTaskMessage($"mes结束ADD", MessageLevel.Info);
                                 }
+                                //kb 螺丝站位询问mes 具体需求跟plc对接
+                                else if (state == 5)
+                                {
+
+                                    SendTaskMessage($"收到KB Screw站点{i}过站检查请求(当站是否生产)...", MessageLevel.Debug);
+                                    //获取过站时间
+                                    _LastMesCheckTime[i] = (await _mesService.GetServerTimeAsync(i, code, 2)).Now;
+                                    if (_configService.GetDeviceInfo().EnableMES)
+                                    {
+                                        LogHelper.WriteLogMes($"【mes开始QUERY】");
+                                        SendTaskMessage($"mes开始QUERY", MessageLevel.Info);
+
+                                        SendTaskMessage($"基于RFID询问是否过站", MessageLevel.Debug);
+                                        (bool isSuccess, string response) = await _mesService.StationGetExAsync(code, "", 2);
+                                        //如果成功的话就代表跟正常mes询问一样,当前站需工作
+                                        if (isSuccess)
+                                        {
+                                            string ProductMainSN_Temp = "";
+                                            LogHelper.WriteLogMes($"询问mes成功");
+                                            SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
+                                            string[] item = response.Split('\n');
+                                            //获取过度件sn
+                                            string snItem = item.FirstOrDefault(f => f.Contains("sn="));
+                                            if (snItem != null)
+                                            {
+                                                ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
+                                                SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
+                                            }
+
+                                            LogHelper.WriteLogMes($"询问mes成功");
+                                            SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
+
+                                            (bool isSuccess2, string response2) = await _mesService.StationGetAsync(ProductMainSN_Temp, "", 2);//询问mes
+                                            if (isSuccess2)
+                                            {
+
+                                                item = response2.Split('\n');
+                                                snItem = item.FirstOrDefault(f => f.Contains("sn="));
+                                                if (snItem != null)
+                                                {
+                                                    ProductMainSN[i] = snItem.Replace("sn=", "").Trim();
+                                                    if (i == 0)
+                                                    {
+                                                        _productService.CurrentProductCode = ProductMainSN[i];
+                                                    }
+                                                    SendTaskMessage($"产品主sn:{ProductMainSN[i]}", MessageLevel.Info);
+                                                }
+                                                string woItem = item.FirstOrDefault(f => f.Contains("wo="));
+                                                if (woItem != null)
+                                                {
+                                                    wo[i] = woItem.Replace("wo=", "").Trim();
+                                                    SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
+                                                }
+                                                string modelItem = item.FirstOrDefault(f => f.Contains("model_num="));
+                                                if (modelItem != null)
+                                                {
+                                                    model_num[i] = modelItem.Replace("model_num=", "").Trim();
+                                                    SendTaskMessage($"model_num:{model_num[i]}", MessageLevel.Info);
+                                                }
+                                                string colorItem = item.FirstOrDefault(f => f.Contains("color="));
+                                                if (colorItem != null)
+                                                {
+                                                    color[i] = colorItem.Replace("color=", "").Trim();
+                                                    SendTaskMessage($"color:{color[i]}", MessageLevel.Info);
+                                                }
+                                                string ppidItem = item.FirstOrDefault(f => f.Contains("ppid="));
+                                                if (ppidItem != null)
+                                                {
+                                                    ppid[i] = ppidItem.Replace("ppid=", "").Trim();
+                                                    SendTaskMessage($"ppid:{ppid[i]}", MessageLevel.Info);
+                                                }
+                                                string revisionItem = item.FirstOrDefault(f => f.Contains("stage="));
+                                                if (revisionItem != null)
+                                                {
+                                                    revision[i] = revisionItem.Replace("stage=", "").Trim();
+                                                    SendTaskMessage($"stage:{revision[i]}", MessageLevel.Info);
+                                                }
+
+                                                LogHelper.WriteLogMes($"第二次询问mes成功");
+                                                SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
+
+                                                //切换产品
+                                                if (i == 0)//i=0时才会切换产品
+                                                {
+                                                    var q = await _systemDatabaseService.QueryProductFromMesAsync(model_num[i], color[i]);
+                                                    if (q != null && !string.IsNullOrEmpty(q.ProductName))
+                                                    {
+                                                        ProductList = new ObservableCollection<ProductModel>(_productService.GetAllProducts());
+                                                        Guid productId = Guid.Empty;
+                                                        foreach (var itt in ProductList)
+                                                        {
+                                                            if (itt.Name == q.ProductName)
+                                                            {
+                                                                productId = itt.ID;
+                                                                break;
+                                                            }
+                                                        }
+                                                        if (currentProduct.ID != productId)
+                                                        {
+                                                            //切换产品
+                                                            await _productService.LoadProductAsync(productId);
+                                                            _productService.SetCurrentProduct(productId);
+                                                            await WriteParameterToPlcExAsync();
+                                                            SendTaskMessage($"切换产品成功", MessageLevel.Debug);
+                                                        }
+                                                    }
+                                                }
+
+                                                //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
+                                                await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
+                                                await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
+
+                                            }
+                                            else
+                                            {
+                                                LogHelper.WriteLogMes($"第二次询问mes失败");
+                                                SendTaskMessage($"第二次询问mes失败:{response2}", MessageLevel.Error);
+
+                                                //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
+                                                await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
+                                                if (models)
+                                                {
+                                                    await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)3);
+                                                }
+                                                else
+                                                {
+                                                    await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
+                                                }
+                                            }
+                                        }
+                                        else
+                                        {
+                                            LogHelper.WriteLogMes($"询问mes失败");
+                                            SendTaskMessage($"询问mes失败:{response}", MessageLevel.Error);
+
+                                            //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
+                                            await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
+
+                                            //表示前面的kb screw站点已经打完螺丝并且过站成功到3d aoi,根据plc的需求直接放行
+                                            if (response.Contains("3D AOI"))
+                                            {
+                                                await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)2);
+                                                return;
+                                            }
+                                            //表示kb组装 站点ng  具体写入型号跟plc对接
+                                            else if (response.Contains("C_KB_Link"))
+                                            {
+                                                await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)5);
+                                                return;
+                                            }
+                                            // 后面还有一些判断条件也需跟当前AE对接
+
+
+                                        }
+
+                                        LogHelper.WriteLogMes($"【mes结束QUERY】");
+                                        SendTaskMessage($"mes结束QUERY", MessageLevel.Info);
+                                    }
+                                    else
+                                    {
+                                        ProductMainSN[i] = DateTime.Now.ToString("yyyyMMddHHmmssfff");
+                                        if (i == 0)
+                                        {
+                                            _productService.CurrentProductCode = ProductMainSN[i];
+                                        }
+
+                                        LogHelper.WriteLogMes($"【未启用mes】");
+                                        SendTaskMessage($"未启用mes", MessageLevel.Info);
+                                        await plc.WriteNodeAsync(addressConfig.Out_ScrewFeederIsChanged.Address, (Int16)1); //不发生改变
+                                        await plc.WriteNodeAsync(string.Format(addressConfig.Out_MesStatus.Address, i), (Int16)1);
+                                    }
+                                }
                                 else
                                 {
                                     SendTaskMessage($"{i}过站检查信号:state={state},处理为默认状态", MessageLevel.Alarm);
@@ -4189,6 +4435,58 @@ namespace TeamAAS_VP.Core
 
         #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
     }
 }

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

@@ -285,6 +285,25 @@ namespace TeamAAS_VP.Core.PLCs
             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>

+ 4 - 0
TeamAAS-VM/Data/SystemDatabaseService.cs

@@ -563,6 +563,10 @@ namespace TeamAAS_VP.Data
 
         }
 
+
+
+
+
         /// <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)
             {

+ 8 - 0
TeamAAS-VM/Models/DeviceInfo.cs

@@ -51,6 +51,12 @@ namespace TeamAAS_VP.Models
         /// </summary>
         public bool EnableMES { get => _enableMes; set => SetProperty(ref _enableMes, value); }
 
+        private bool _enableXY;
+        /// <summary>
+        /// 是否启用锁螺丝XY超限 通讯功能,默认不启用
+        /// </summary>
+        public bool EnableXY { get => _enableXY; set => SetProperty(ref _enableXY, value); }
+
         private string _mesUrl;
         /// <summary>
         /// URL
@@ -127,6 +133,7 @@ namespace TeamAAS_VP.Models
             LogUrl = string.Empty;
             ApiKey = string.Empty;
             EnableMES = false;
+            EnableXY = false;
             SaveCsvPPID = true;
         }
 
@@ -144,6 +151,7 @@ namespace TeamAAS_VP.Models
                 comp = this.comp,
                 MesUrl = this.MesUrl,
                 EnableMES = this.EnableMES,
+                EnableXY = this.EnableXY,
                 SaveCsv = this.SaveCsv,
                 SaveCsvPath = this.SaveCsvPath,
                 //EnableDoubleMes = this.EnableDoubleMes,

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

@@ -0,0 +1,191 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Formats.Asn1;
+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), @"Config\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;
+        }
+
+        private DataTable GetCsvData()
+        {
+            DataTable dt = new DataTable();
+            try
+            {
+                // 优先用GB2312兼容Excel导出的CSV,乱码时可改为Encoding.UTF8/Encoding.Default
+                Encoding encoding = Encoding.GetEncoding("GB2312");
+                string[] lines = File.ReadAllLines(_filePath, encoding);
+
+                if (lines.Length == 0) return null;
+
+                // 读取表头
+                string[] headers = lines[0].Split(',');
+                foreach (string header in headers)
+                {
+                    dt.Columns.Add(header.Trim());
+                }
+
+                // 读取数据行
+                for (int i = 1; i < lines.Length; i++)
+                {
+                    string[] cells = lines[i].Split(',');
+                    DataRow row = dt.NewRow();
+                    for (int j = 0; j < dt.Columns.Count; j++)
+                    {
+                        row[j] = j < cells.Length ? cells[j].Trim() : "";
+                    }
+                    dt.Rows.Add(row);
+                }
+
+                return dt;
+            }
+            catch
+            {
+                return null;
+            }
+        }
+        /// <summary>
+        /// 加载Excel数据
+        /// </summary>
+        public void LoadData(int sheetIndex)
+        {
+            try
+            {
+                if (!File.Exists(_filePath)) return;
+
+                DataTable dt = null;
+                string ext = Path.GetExtension(_filePath).ToLower();
+                dt = GetCsvData();
+      
+                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);
+        }
+
+    }
+
+
+}

+ 8 - 0
TeamAAS-VM/Models/SystemConfiguration.cs

@@ -257,6 +257,14 @@ namespace TeamAAS_VP.Models
             set { SetProperty(ref _IsBasePartOpenLightKB, value); }
         }
 
+        private bool _IsMesResult;
+
+        public bool IsMesResult
+        {
+            get { return _IsMesResult; }
+            set { SetProperty(ref _IsMesResult, value); }
+        }
+
         private string _ChooseScrewModel = "***********abc123****";
         /// <summary>
         /// 螺丝型号判断,如******123**,*为忽略字符

+ 84 - 84
TeamAAS-VM/Services/ConfigService.cs

@@ -71,7 +71,7 @@ namespace TeamAAS_VP.Services
         // 锟斤拷源锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
         private ObservableCollection<LightControllerConfig> LightControllers { get; set; }
 
-        // Device info - 鏀�寔澶氳�澶
+        // Device info - 鏀�寔澶氳�澶?
         private List<DeviceInfo> _deviceInfoList;
         public CTQ_Data CtqData { get; set; }
 
@@ -101,7 +101,7 @@ namespace TeamAAS_VP.Services
 
         /// <summary>
         /// 锟斤拷始锟斤拷一锟斤拷锟铰碉拷 <see cref="ConfigService"/> 实锟斤拷锟斤拷
-        /// 锟斤拷锟届函锟斤拷锟斤拷锟斤拷锟皆讹拷锟斤拷锟斤拷锟斤拷锟斤拷锟侥硷拷锟斤拷锟斤拷锟斤拷锟节癸拷锟斤拷锟节硷拷锟斤拷锟I/O 锟斤拷锟斤拷锟斤拷锟斤拷锟缴碉拷锟斤拷 <see cref="LoadAll"/> 锟斤拷 <see cref="LoadAllAsync"/> 锟斤拷式锟斤拷锟截★拷
+        /// 锟斤拷锟届函锟斤拷锟斤拷锟斤拷锟皆讹拷锟斤拷锟斤拷锟斤拷锟斤拷锟侥硷拷锟斤拷锟斤拷锟斤拷锟节癸拷锟斤拷锟节硷拷锟斤拷锟?I/O 锟斤拷锟斤拷锟斤拷锟斤拷锟缴碉拷锟斤拷 <see cref="LoadAll"/> 锟斤拷 <see cref="LoadAllAsync"/> 锟斤拷式锟斤拷锟截★拷
         /// </summary>
         public ConfigService()
         {
@@ -117,7 +117,7 @@ namespace TeamAAS_VP.Services
         {
             lock (_sync)
             {
-                // 锟斤拷取锟斤拷锟斤拷锟矫o拷锟斤拷锟斤拷募锟斤拷锟斤拷锟斤拷冢锟斤拷虮3锟DeviceConfig 默锟斤拷值
+                // 锟斤拷取锟斤拷锟斤拷锟矫o拷锟斤拷锟斤拷募锟斤拷锟斤拷锟斤拷冢锟斤拷虮3锟?DeviceConfig 默锟斤拷值
                 if (File.Exists(ConfigPaths.CamerasConfigurationPath))
                     Cameras = FileHelper.ReadJsonFile<ObservableCollection<CameraInfo>>(ConfigPaths.CamerasConfigurationPath);
                 else
@@ -211,7 +211,7 @@ namespace TeamAAS_VP.Services
                     FileHelper.WriteJsonFile(_screwDriverConfig, ConfigPaths.ScrewDriverConfigurationPath);
                 }
 
-                // load device info list (鏀�寔澶氳�澶�)
+                // load device info list (鏀�寔澶氳�澶?
                 if (File.Exists(ConfigPaths.DeviceInfoConfigurationPath))
                 {
                     try
@@ -263,7 +263,7 @@ namespace TeamAAS_VP.Services
 
         /// <summary>
         /// 锟斤拷锟节达拷锟叫碉拷锟斤拷锟斤拷锟斤拷锟斤拷写锟截碉拷锟斤拷应锟斤拷锟斤拷锟侥硷拷锟斤拷
-        /// 锟竭程帮拷全锟斤拷锟斤拷锟斤拷锟节诧拷锟斤拷写锟斤拷锟斤拷锟绞癸拷锟<see cref="_sync"/> 锟斤拷锟斤拷锟斤拷
+        /// 锟竭程帮拷全锟斤拷锟斤拷锟斤拷锟节诧拷锟斤拷写锟斤拷锟斤拷锟绞癸拷锟?<see cref="_sync"/> 锟斤拷锟斤拷锟斤拷
         /// </summary>
         public void SaveAll()
         {
@@ -285,7 +285,7 @@ namespace TeamAAS_VP.Services
                     FileHelper.WriteJsonFile(ScrewFeeders, ConfigPaths.ScrewFeedersConfigurationPath);
                 // save plc addresses
                 //FileHelper.WriteJsonFile(PlcAddressConfig, "..//Config//PlcAddressConfiguration.cfg");
-                // save device info list (鏀�寔澶氳�澶�)
+                // save device info list (鏀�寔澶氳�澶?
                 if (_deviceInfoList != null)
                     FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath);
                 // save card reader config
@@ -297,7 +297,7 @@ namespace TeamAAS_VP.Services
         /// <summary>
         /// 锟届步执锟斤拷 <see cref="SaveAll"/> 锟斤拷锟斤拷锟斤拷
         /// </summary>
-        /// <returns>锟斤拷示锟届步锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟�</returns>
+        /// <returns>锟斤拷示锟届步锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟?/returns>
         public Task SaveAllAsync()
         {
             return Task.Run(() => SaveAll());
@@ -305,19 +305,19 @@ namespace TeamAAS_VP.Services
 
         #region Cameras
         /// <summary>
-        /// 锟斤拷取锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷玫锟街伙拷锟斤拷锟斤拷稀锟
+        /// 锟斤拷取锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷玫锟街伙拷锟斤拷锟斤拷稀锟?
         /// 锟竭程帮拷全锟斤拷锟斤拷锟节诧拷锟斤拷锟斤拷锟皆憋拷证锟斤拷锟斤拷锟斤拷取锟斤拷一锟斤拷锟皆★拷
         /// </summary>
-        /// <returns>锟斤拷锟斤拷锟斤拷玫锟街伙拷锟斤拷锟斤拷稀锟�</returns>
+        /// <returns>锟斤拷锟斤拷锟斤拷玫锟街伙拷锟斤拷锟斤拷稀锟?/returns>
         public IReadOnlyCollection<CameraInfo> GetAllCameras()
         {
             lock (_sync) { return Cameras.ToList().AsReadOnly(); }
         }
 
         /// <summary>
-        /// 锟斤拷锟捷憋拷识锟斤拷取锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟较�拷锟
+        /// 锟斤拷锟捷憋拷识锟斤拷取锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟较�拷锟?
         /// </summary>
-        /// <param name="id">锟斤拷锟斤拷锟Guid 锟斤拷识锟斤拷</param>
+        /// <param name="id">锟斤拷锟斤拷锟?Guid 锟斤拷识锟斤拷</param>
         /// <returns>锟揭碉拷锟津返回讹拷应锟斤拷 <see cref="CameraInfo"/>锟斤拷锟斤拷锟津返伙拷 <c>null</c>锟斤拷</returns>
         public CameraInfo GetCamera(Guid id)
         {
@@ -327,11 +327,11 @@ namespace TeamAAS_VP.Services
         /// <summary>
         /// 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟矫★拷
         /// - 锟斤拷锟斤拷锟斤拷 <paramref name="camera"/> 为 <c>null</c> 锟斤拷锟斤拷 <c>null</c>锟斤拷
-        /// - 锟斤拷 Id 锟窖达拷锟斤拷锟斤拷锟芥换锟斤拷锟斤拷锟斤拷追锟接诧拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷 CameraNo 锟斤拷拧锟
+        /// - 锟斤拷 Id 锟窖达拷锟斤拷锟斤拷锟芥换锟斤拷锟斤拷锟斤拷追锟接诧拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷 CameraNo 锟斤拷拧锟?
         /// 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷峤�拷锟斤拷锟斤拷锟较持久伙拷锟斤拷锟斤拷锟斤拷锟侥硷拷锟斤拷
         /// </summary>
         /// <param name="camera">要锟斤拷锟接伙拷锟斤拷碌锟斤拷锟斤拷锟斤拷锟矫★拷</param>
-        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟<see cref="CameraInfo"/>锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
+        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟?<see cref="CameraInfo"/>锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
         public CameraInfo AddOrUpdateCamera(CameraInfo camera)
         {
             if (camera == null) return null;
@@ -354,10 +354,10 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟斤拷锟捷憋拷识锟狡筹拷锟斤拷锟斤拷锟斤拷貌锟斤拷锟斤拷卤锟斤拷剩锟斤拷锟斤拷锟斤拷锟CameraNo锟斤拷
+        /// 锟斤拷锟捷憋拷识锟狡筹拷锟斤拷锟斤拷锟斤拷貌锟斤拷锟斤拷卤锟斤拷剩锟斤拷锟斤拷锟斤拷锟?CameraNo锟斤拷
         /// 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷峤�拷锟斤拷锟斤拷锟较持久伙拷锟斤拷锟斤拷锟斤拷锟侥硷拷锟斤拷
         /// </summary>
-        /// <param name="id">要锟狡筹拷锟斤拷锟斤拷锟Guid锟斤拷</param>
+        /// <param name="id">要锟狡筹拷锟斤拷锟斤拷锟?Guid锟斤拷</param>
         /// <returns>锟斤拷锟揭碉拷锟斤拷锟缴癸拷锟狡筹拷锟斤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
         public bool RemoveCamera(Guid id)
         {
@@ -366,7 +366,7 @@ namespace TeamAAS_VP.Services
             {
                 var exist = Cameras.FirstOrDefault(c => c.Id == id);
                 if (exist != null) result = Cameras.Remove(exist);
-                //锟斤拷锟铰憋拷锟
+                //锟斤拷锟铰憋拷锟?
                 int no = 1;
                 //锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟紺ameraNo锟斤拷锟斤拷
                 var sortedCameras = Cameras.OrderBy(c => c.CameraNo).ToList();
@@ -386,18 +386,18 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟斤拷锟斤拷欠锟斤拷锟斤拷指锟斤拷 Id 锟斤拷锟斤拷锟斤拷锟斤拷谩锟
+        /// 锟斤拷锟斤拷欠锟斤拷锟斤拷指锟斤拷 Id 锟斤拷锟斤拷锟斤拷锟斤拷谩锟?
         /// </summary>
-        /// <param name="id">锟斤拷锟斤拷锟Guid锟斤拷</param>
+        /// <param name="id">锟斤拷锟斤拷锟?Guid锟斤拷</param>
         /// <returns>锟斤拷锟节凤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
         public bool ContainsCamera(Guid id) { lock (_sync) { return Cameras.Any(c => c.Id == id); } }
         #endregion
 
         #region DeviceInfo
         /// <summary>
-        /// 鑾峰彇鎵€鏈夎�澶囦俊鎭�厤缃�垪琛
+        /// 鑾峰彇鎵€鏈夎�澶囦俊鎭�厤缃�垪琛?
         /// </summary>
-        /// <returns>璁惧�淇℃伅鍒楄〃鐨勫彧璇婚泦鍚�</returns>
+        /// <returns>璁惧�淇℃伅鍒楄〃鐨勫彧璇婚泦鍚?/returns>
         public IReadOnlyCollection<DeviceInfo> GetAllDeviceInfos()
         {
             lock (_sync)
@@ -460,7 +460,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 娣诲姞鎴栨洿鏂拌�澶囦俊鎭
+        /// 娣诲姞鎴栨洿鏂拌�澶囦俊鎭?
         /// </summary>
         /// <param name="deviceInfo">璁惧�淇℃伅瀵硅薄</param>
         public void SaveDeviceInfo(DeviceInfo deviceInfo)
@@ -484,17 +484,17 @@ namespace TeamAAS_VP.Services
                 }
                 else
                 {
-                    // 娣诲姞鏂拌�澶
+                    // 娣诲姞鏂拌�澶?
                     _deviceInfoList.Add(deviceInfo);
                 }
-                // 鎸夎�澶囩紪鍙锋帓搴
+                // 鎸夎�澶囩紪鍙锋帓搴?
                 _deviceInfoList = _deviceInfoList.OrderBy(d => d.DeviceNo).ToList();
                 FileHelper.WriteJsonFile(_deviceInfoList, ConfigPaths.DeviceInfoConfigurationPath);
             }
         }
 
         /// <summary>
-        /// 淇濆瓨鎵€鏈夎�澶囦俊鎭�垪琛
+        /// 淇濆瓨鎵€鏈夎�澶囦俊鎭�垪琛?
         /// </summary>
         /// <param name="deviceInfoList">璁惧�淇℃伅鍒楄〃</param>
         public void SaveAllDeviceInfos(IEnumerable<DeviceInfo> deviceInfoList)
@@ -508,7 +508,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 娣诲姞鏂拌�澶
+        /// 娣诲姞鏂拌�澶?
         /// </summary>
         /// <returns>鏂版坊鍔犵殑璁惧�</returns>
         public DeviceInfo AddDeviceInfo()
@@ -520,7 +520,7 @@ namespace TeamAAS_VP.Services
                     _deviceInfoList = new List<DeviceInfo>();
                 }
 
-                // 璁$畻鏂拌�澶囩紪鍙
+                // 璁$畻鏂拌�澶囩紪鍙?
                 int newDeviceNo = _deviceInfoList.Count > 0 ? _deviceInfoList.Max(d => d.DeviceNo) + 1 : 0;
                 var newDevice = new DeviceInfo { DeviceNo = newDeviceNo };
                 _deviceInfoList.Add(newDevice);
@@ -567,10 +567,10 @@ namespace TeamAAS_VP.Services
 
         /// <summary>
         /// 锟斤拷锟斤拷锟斤拷锟斤拷禄锟斤拷锟斤拷锟斤拷锟斤拷茫锟斤拷锟斤拷锟斤拷锟街久伙拷锟斤拷锟斤拷锟斤拷锟侥硷拷锟斤拷
-        /// 锟斤拷锟斤拷时为锟斤拷锟斤拷锟剿凤拷锟斤拷锟斤拷锟斤拷锟斤拷 RobotNo 锟斤拷拧锟
+        /// 锟斤拷锟斤拷时为锟斤拷锟斤拷锟剿凤拷锟斤拷锟斤拷锟斤拷锟斤拷 RobotNo 锟斤拷拧锟?
         /// </summary>
-        /// <param name="robot">要锟斤拷锟接伙拷锟斤拷碌幕锟斤拷锟斤拷锟斤拷锟斤拷谩锟�</param>
-        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟<see cref="RobotInfo"/>锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
+        /// <param name="robot">要锟斤拷锟接伙拷锟斤拷碌幕锟斤拷锟斤拷锟斤拷锟斤拷谩锟?/param>
+        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟?<see cref="RobotInfo"/>锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
         public RobotInfo AddOrUpdateRobot(RobotInfo robot)
         {
             if (robot == null) return null;
@@ -595,7 +595,7 @@ namespace TeamAAS_VP.Services
 
         /// <summary>
         /// 锟斤拷锟捷憋拷识锟狡筹拷锟斤拷锟斤拷锟斤拷锟斤拷锟矫诧拷锟斤拷锟铰憋拷锟绞o拷锟斤拷锟斤拷锟剿★拷
-        /// 锟斤拷锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷募锟斤拷锟
+        /// 锟斤拷锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷募锟斤拷锟?
         /// </summary>
         /// <param name="id">要锟狡筹拷锟侥伙拷锟斤拷锟斤拷 Guid锟斤拷</param>
         /// <returns>锟狡筹拷锟缴癸拷锟斤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
@@ -607,7 +607,7 @@ namespace TeamAAS_VP.Services
                 var e = Robots.FirstOrDefault(r => r.Id == id);
                 if (e != null)
                     result = Robots.Remove(e);
-                //锟斤拷锟铰憋拷锟
+                //锟斤拷锟铰憋拷锟?
                 int no = 1;
                 //锟斤拷锟斤拷锟叫伙拷锟斤拷锟剿帮拷RobotNo锟斤拷锟斤拷
                 var sortedRobots = Robots.OrderBy(r => r.RobotNo).ToList();
@@ -627,7 +627,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟Id 锟侥伙拷锟斤拷锟斤拷锟斤拷锟矫★拷
+        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟?Id 锟侥伙拷锟斤拷锟斤拷锟斤拷锟矫★拷
         /// </summary>
         /// <param name="id">锟斤拷锟斤拷锟剿碉拷 Guid锟斤拷</param>
         /// <returns>锟斤拷锟节凤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
@@ -697,10 +697,10 @@ namespace TeamAAS_VP.Services
 
         /// <summary>
         /// 锟斤拷锟斤拷锟斤拷锟斤拷鹿锟斤拷锟斤拷锟斤拷锟斤拷茫锟斤拷锟斤拷锟斤拷锟街久伙拷锟斤拷锟斤拷锟斤拷锟侥硷拷锟斤拷
-        /// 锟斤拷锟斤拷时为锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷 FeederNo 锟斤拷拧锟
+        /// 锟斤拷锟斤拷时为锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷 FeederNo 锟斤拷拧锟?
         /// </summary>
-        /// <param name="feeder">要锟斤拷锟接伙拷锟斤拷碌墓锟斤拷锟斤拷锟斤拷锟斤拷谩锟�</param>
-        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟<see cref="FeederInfo"/>锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
+        /// <param name="feeder">要锟斤拷锟接伙拷锟斤拷碌墓锟斤拷锟斤拷锟斤拷锟斤拷谩锟?/param>
+        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟?<see cref="FeederInfo"/>锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
         public FeederInfo AddOrUpdateFeeder(FeederInfo feeder)
         {
             if (feeder == null) return null;
@@ -723,8 +723,8 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟斤拷锟捷憋拷识锟狡筹拷锟斤拷锟斤拷锟斤拷锟斤拷锟矫诧拷锟斤拷锟铰憋拷锟绞o拷喙╋拷锟斤拷锟斤拷锟
-        /// 锟斤拷锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷募锟斤拷锟
+        /// 锟斤拷锟捷憋拷识锟狡筹拷锟斤拷锟斤拷锟斤拷锟斤拷锟矫诧拷锟斤拷锟铰憋拷锟绞o拷喙╋拷锟斤拷锟斤拷锟?
+        /// 锟斤拷锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷募锟斤拷锟?
         /// </summary>
         /// <param name="id">要锟狡筹拷锟侥癸拷锟斤拷锟斤拷 Guid锟斤拷</param>
         /// <returns>锟狡筹拷锟缴癸拷锟斤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
@@ -735,7 +735,7 @@ namespace TeamAAS_VP.Services
             {
                 var e = Feeders.FirstOrDefault(f => f.Id == id);
                 if (e != null) result = Feeders.Remove(e);
-                //锟斤拷锟铰憋拷锟
+                //锟斤拷锟铰憋拷锟?
                 int no = 1;
                 //锟斤拷锟斤拷锟叫癸拷锟斤拷锟斤拷锟斤拷FeederNo锟斤拷锟斤拷
                 var sortedFeeders = Feeders.OrderBy(f => f.FeederNo).ToList();
@@ -756,7 +756,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟Id 锟侥癸拷锟斤拷锟斤拷锟斤拷锟矫★拷
+        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟?Id 锟侥癸拷锟斤拷锟斤拷锟斤拷锟矫★拷
         /// </summary>
         /// <param name="id">锟斤拷锟斤拷锟斤拷 Guid锟斤拷</param>
         /// <returns>锟斤拷锟节凤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
@@ -774,14 +774,14 @@ namespace TeamAAS_VP.Services
         /// 锟斤拷锟捷憋拷识锟斤拷取锟斤拷锟斤拷 PLC 锟斤拷锟矫★拷
         /// </summary>
         /// <param name="id">PLC 锟斤拷 Guid 锟斤拷识锟斤拷</param>
-        /// <returns>匹锟斤拷锟<see cref="PlcInfo"/> 实锟斤拷锟斤拷锟揭诧拷锟斤拷锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
+        /// <returns>匹锟斤拷锟?<see cref="PlcInfo"/> 实锟斤拷锟斤拷锟揭诧拷锟斤拷锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
         public PlcInfo GetPlc(Guid id) { lock (_sync) { return Plcs.FirstOrDefault(p => p.Id == id); } }
 
         /// <summary>
-        /// 锟斤拷锟斤拷锟斤拷锟斤拷锟PLC 锟斤拷锟矫o拷锟斤拷锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷锟斤拷募锟斤拷锟斤拷锟斤拷锟绞憋拷锟斤拷锟斤拷锟斤拷锟� PlcNo 锟斤拷拧锟�
+        /// 锟斤拷锟斤拷锟斤拷锟斤拷锟?PLC 锟斤拷锟矫o拷锟斤拷锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷锟斤拷募锟斤拷锟斤拷锟斤拷锟绞憋拷锟斤拷锟斤拷锟斤拷锟?PlcNo 锟斤拷拧锟?
         /// </summary>
-        /// <param name="plc">要锟斤拷锟接伙拷锟斤拷碌锟PLC 锟斤拷锟矫★拷</param>
-        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟<see cref="PlcInfo"/>锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
+        /// <param name="plc">要锟斤拷锟接伙拷锟斤拷碌锟?PLC 锟斤拷锟矫★拷</param>
+        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟?<see cref="PlcInfo"/>锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
         public PlcInfo AddOrUpdatePlc(PlcInfo plc)
         {
             if (plc == null) return null;
@@ -804,8 +804,8 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟斤拷锟捷憋拷识锟狡筹拷 PLC 锟斤拷锟矫诧拷锟斤拷锟铰憋拷锟绞o拷锟PLC锟斤拷
-        /// 锟斤拷锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷募锟斤拷锟
+        /// 锟斤拷锟捷憋拷识锟狡筹拷 PLC 锟斤拷锟矫诧拷锟斤拷锟铰憋拷锟绞o拷锟?PLC锟斤拷
+        /// 锟斤拷锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷募锟斤拷锟?
         /// </summary>
         /// <param name="id">要锟狡筹拷锟斤拷 PLC Guid锟斤拷</param>
         /// <returns>锟狡筹拷锟缴癸拷锟斤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
@@ -816,7 +816,7 @@ namespace TeamAAS_VP.Services
             {
                 var e = Plcs.FirstOrDefault(p => p.Id == id);
                 if (e != null) result = Plcs.Remove(e);
-                //锟斤拷锟铰憋拷锟
+                //锟斤拷锟铰憋拷锟?
                 int no = 1;
                 //锟斤拷锟斤拷锟斤拷PLC锟斤拷PlcNo锟斤拷锟斤拷
                 var sortedPlcs = Plcs.OrderBy(p => p.PlcNo).ToList();
@@ -837,7 +837,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟Id 锟斤拷 PLC 锟斤拷锟矫★拷
+        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟?Id 锟斤拷 PLC 锟斤拷锟矫★拷
         /// </summary>
         /// <param name="id">PLC 锟斤拷 Guid锟斤拷</param>
         /// <returns>锟斤拷锟节凤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
@@ -846,13 +846,13 @@ namespace TeamAAS_VP.Services
 
         #region BgTcp
         /// <summary>
-        /// 锟斤拷取 BG TCP/IP 通锟斤拷锟斤拷锟矫o拷锟节达拷锟斤拷螅�锟斤拷锟轿null锟斤拷锟斤拷
+        /// 锟斤拷取 BG TCP/IP 通锟斤拷锟斤拷锟矫o拷锟节达拷锟斤拷螅�锟斤拷锟轿?null锟斤拷锟斤拷
         /// </summary>
         /// <returns>锟斤拷前锟斤拷 <see cref="BgTcpIP"/> 锟斤拷锟矫讹拷锟斤拷</returns>
         public BgTcpIP GetBgTcp() { lock (_sync) { return BgCommunicate; } }
 
         /// <summary>
-        /// 锟斤拷锟斤拷 BG TCP/IP 通锟斤拷锟斤拷锟矫o拷锟斤拷锟斤拷锟斤拷锟节达拷锟斤拷螅�锟斤拷锟斤拷远锟斤拷志没锟斤拷锟斤拷锟
+        /// 锟斤拷锟斤拷 BG TCP/IP 通锟斤拷锟斤拷锟矫o拷锟斤拷锟斤拷锟斤拷锟节达拷锟斤拷螅�锟斤拷锟斤拷远锟斤拷志没锟斤拷锟斤拷锟?
         /// 锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷 <see cref="SaveBgTcp"/>锟斤拷
         /// </summary>
         /// <param name="cfg">锟铰碉拷 <see cref="BgTcpIP"/> 锟斤拷锟矫讹拷锟斤拷</param>
@@ -866,13 +866,13 @@ namespace TeamAAS_VP.Services
 
         #region BgModbus
         /// <summary>
-        /// 锟斤拷取 BG Modbus TCP 通锟斤拷锟斤拷锟矫o拷锟节达拷锟斤拷螅�锟
+        /// 锟斤拷取 BG Modbus TCP 通锟斤拷锟斤拷锟矫o拷锟节达拷锟斤拷螅�锟?
         /// </summary>
         /// <returns>锟斤拷前锟斤拷 <see cref="BgModbusTcp"/> 锟斤拷锟矫讹拷锟斤拷</returns>
         public BgModbusTcp GetBgModbusTcp() { lock (_sync) { return BgModbusCommunicate; } }
 
         /// <summary>
-        /// 锟斤拷锟斤拷 BG Modbus TCP 通锟斤拷锟斤拷锟矫o拷锟斤拷锟斤拷锟斤拷锟节达拷锟斤拷螅�锟斤拷锟斤拷远锟斤拷志没锟斤拷锟斤拷锟
+        /// 锟斤拷锟斤拷 BG Modbus TCP 通锟斤拷锟斤拷锟矫o拷锟斤拷锟斤拷锟斤拷锟节达拷锟斤拷螅�锟斤拷锟斤拷远锟斤拷志没锟斤拷锟斤拷锟?
         /// </summary>
         /// <param name="cfg">锟铰碉拷 <see cref="BgModbusTcp"/> 锟斤拷锟矫讹拷锟斤拷</param>
         public void SetBgModbusTcp(BgModbusTcp cfg) { lock (_sync) { BgModbusCommunicate = cfg; } }
@@ -942,7 +942,7 @@ namespace TeamAAS_VP.Services
         /// <summary>
         /// 锟斤拷锟斤拷系统锟斤拷锟矫碉拷锟斤拷锟斤拷锟侥硷拷锟斤拷锟结覆锟斤拷锟斤拷锟斤拷锟侥硷拷锟斤拷锟斤拷
         /// </summary>
-        /// <param name="systemConfiguration">要锟斤拷锟斤拷锟较低筹拷锟斤拷枚锟斤拷锟�</param>
+        /// <param name="systemConfiguration">要锟斤拷锟斤拷锟较低筹拷锟斤拷枚锟斤拷锟?/param>
         public void SaveSystemConfiguration(SystemConfiguration systemConfiguration)
         {
             lock (_sync)
@@ -1026,16 +1026,16 @@ namespace TeamAAS_VP.Services
 
         #region Feeder锟斤拷锟斤拷锟斤拷锟斤拷
         /// <summary>
-        /// 锟斤拷取锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟街伙拷锟斤拷锟斤拷稀锟
+        /// 锟斤拷取锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟街伙拷锟斤拷锟斤拷稀锟?
         /// </summary>
-        /// <returns>锟斤拷锟斤拷锟斤拷锟斤拷锟街伙拷锟斤拷锟斤拷稀锟�</returns>
+        /// <returns>锟斤拷锟斤拷锟斤拷锟斤拷锟街伙拷锟斤拷锟斤拷稀锟?/returns>
         public IReadOnlyCollection<FeederClearWork> GetAllClearanceTasks() { lock (_sync) { return FeederClearanceTasks.ToList().AsReadOnly(); } }
 
         /// <summary>
-        /// 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟绞蔽�拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟TaskCode锟斤拷锟斤拷珊锟街久伙拷锟斤拷锟斤拷锟侥硷拷锟斤拷
+        /// 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟绞蔽�拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟?TaskCode锟斤拷锟斤拷珊锟街久伙拷锟斤拷锟斤拷锟侥硷拷锟斤拷
         /// </summary>
         /// <param name="task">要锟斤拷锟接伙拷锟斤拷碌锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷</param>
-        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟<see cref="FeederClearWork"/>锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
+        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟?<see cref="FeederClearWork"/>锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
         public FeederClearWork AddOrUpdateClearanceTask(FeederClearWork task)
         {
             if (task == null) return null;
@@ -1049,7 +1049,7 @@ namespace TeamAAS_VP.Services
                 }
                 else
                 {
-                    // 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷为锟斤拷前锟斤拷锟斤拷偶锟�1
+                    // 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷为锟斤拷前锟斤拷锟斤拷偶锟?
                     task.TaskCode = FeederClearanceTasks.Count > 0 ? FeederClearanceTasks.Max(t => t.TaskCode) + 1 : 1;
                     FeederClearanceTasks.Add(task);
                 }
@@ -1080,7 +1080,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷癫⒊志没锟斤拷占锟斤拷系锟斤拷锟斤拷锟斤拷募锟斤拷锟
+        /// 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷癫⒊志没锟斤拷占锟斤拷系锟斤拷锟斤拷锟斤拷募锟斤拷锟?
         /// </summary>
         public void ClearAllClearanceTasks()
         {
@@ -1092,7 +1092,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟Id 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
+        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟?Id 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
         /// </summary>
         /// <param name="id">锟斤拷锟斤拷锟斤拷锟斤拷 Guid锟斤拷</param>
         /// <returns>锟斤拷锟节凤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
@@ -1106,14 +1106,14 @@ namespace TeamAAS_VP.Services
         /// 锟斤拷锟捷憋拷识锟斤拷取锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷息锟斤拷
         /// </summary>
         /// <param name="id">锟斤拷锟斤拷锟斤拷锟斤拷 Guid锟斤拷</param>
-        /// <returns>匹锟斤拷锟<see cref="FeederClearWork"/>锟斤拷锟揭诧拷锟斤拷锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
+        /// <returns>匹锟斤拷锟?<see cref="FeederClearWork"/>锟斤拷锟揭诧拷锟斤拷锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
         public FeederClearWork GetClearanceTask(Guid id)
         {
             lock (_sync) { return FeederClearanceTasks.FirstOrDefault(t => t.Id == id); }
         }
 
         /// <summary>
-        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟斤拷锟斤拷锟斤拷诺锟斤拷锟斤拷锟斤拷锟斤拷锟
+        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟斤拷锟斤拷锟斤拷诺锟斤拷锟斤拷锟斤拷锟斤拷锟?
         /// </summary>
         /// <param name="taskNumber">锟斤拷锟斤拷锟脚o拷TaskCode锟斤拷锟斤拷</param>
         /// <returns>锟斤拷锟节凤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
@@ -1126,7 +1126,7 @@ namespace TeamAAS_VP.Services
         /// 锟斤拷锟斤拷锟斤拷锟斤拷锟脚伙拷取锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷息锟斤拷
         /// </summary>
         /// <param name="taskNumber">锟斤拷锟斤拷锟脚o拷TaskCode锟斤拷锟斤拷</param>
-        /// <returns>匹锟斤拷锟<see cref="FeederClearWork"/>锟斤拷锟揭诧拷锟斤拷锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
+        /// <returns>匹锟斤拷锟?<see cref="FeederClearWork"/>锟斤拷锟揭诧拷锟斤拷锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
         public FeederClearWork GetClearanceTaskByNumber(int taskNumber)
         {
             lock (_sync) { return FeederClearanceTasks.FirstOrDefault(t => t.TaskCode == taskNumber); }
@@ -1150,21 +1150,21 @@ namespace TeamAAS_VP.Services
         /// 锟竭程帮拷全锟斤拷锟矫凤拷锟斤拷锟斤拷锟节存集锟较斤拷锟斤拷只锟斤拷锟斤拷锟绞诧拷锟斤拷锟节诧拷锟斤拷锟斤拷锟斤拷
         /// </summary>
         /// <param name="id">锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷识锟斤拷Id锟斤拷锟斤拷</param>
-        /// <returns>匹锟斤拷锟<see cref="LightControllerConfig"/> 实锟斤拷锟斤拷锟揭诧拷锟斤拷锟津返伙拷 <c>null</c>锟斤拷</returns>
+        /// <returns>匹锟斤拷锟?<see cref="LightControllerConfig"/> 实锟斤拷锟斤拷锟揭诧拷锟斤拷锟津返伙拷 <c>null</c>锟斤拷</returns>
         public LightControllerConfig GetLightController(int id)
         {
             lock (_sync) { return LightControllers.FirstOrDefault(c => c.Id == id); }
         }
 
         /// <summary>
-        /// 锟斤拷锟斤拷锟斤拷锟斤拷鹿锟皆达拷锟斤拷锟斤拷锟斤拷锟斤拷谩锟
+        /// 锟斤拷锟斤拷锟斤拷锟斤拷鹿锟皆达拷锟斤拷锟斤拷锟斤拷锟斤拷谩锟?
         /// - 锟斤拷 <paramref name="controller"/> 为 <c>null</c> 锟斤拷锟斤拷 <c>null</c>锟斤拷
         /// - 锟斤拷锟斤拷时锟斤拷 Id 匹锟斤拷锟斤拷锟斤拷锟筋并锟芥换锟斤拷
         /// - 锟斤拷锟斤拷时锟斤拷锟斤拷锟斤拷锟斤拷锟侥匡拷锟斤拷锟斤拷 Id锟斤拷锟斤拷为锟斤拷锟斤拷锟斤拷锟节碉拷每锟斤拷通锟斤拷锟斤拷锟斤拷全锟斤拷通锟斤拷锟斤拷牛锟紾lobalIndex锟斤拷锟斤拷
         /// 锟竭程帮拷全锟斤拷锟斤拷锟节诧拷使锟斤拷 <see cref="_sync"/> 锟斤拷锟斤拷锟斤拷锟较★拷
         /// </summary>
-        /// <param name="controller">要锟斤拷锟接伙拷锟斤拷碌锟<see cref="LightControllerConfig"/> 实锟斤拷锟斤拷</param>
-        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟<see cref="LightControllerConfig"/> 实锟斤拷锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
+        /// <param name="controller">要锟斤拷锟接伙拷锟斤拷碌锟?<see cref="LightControllerConfig"/> 实锟斤拷锟斤拷</param>
+        /// <returns>锟斤拷锟斤拷锟接伙拷锟斤拷碌锟?<see cref="LightControllerConfig"/> 实锟斤拷锟斤拷锟斤拷锟斤拷为 <c>null</c> 时锟斤拷锟斤拷 <c>null</c>锟斤拷</returns>
         /// <exception cref="ArgumentException">锟斤拷要锟斤拷锟斤拷/锟斤拷锟铰的匡拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟截革拷时锟阶筹拷锟斤拷</exception>
         public LightControllerConfig AddOrUpdateLightController(LightControllerConfig controller)
         {
@@ -1185,10 +1185,10 @@ namespace TeamAAS_VP.Services
                 else
                 {
                     // 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
-                    // 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷为锟斤拷前锟斤拷锟Id + 1锟斤拷锟斤拷锟斤拷锟斤拷为锟斤拷锟斤拷为 1锟斤拷
+                    // 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷为锟斤拷前锟斤拷锟?Id + 1锟斤拷锟斤拷锟斤拷锟斤拷为锟斤拷锟斤拷为 1锟斤拷
                     controller.Id = LightControllers.Count > 0 ? LightControllers.Max(c => c.Id) + 1 : 1;
 
-                    // 锟斤拷锟斤拷锟斤拷一锟斤拷全锟斤拷通锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟窖达拷锟节匡拷锟斤拷锟斤拷锟斤拷通锟斤拷 GlobalIndex 锟斤拷取锟斤拷锟街碉拷锟斤拷锟�1
+                    // 锟斤拷锟斤拷锟斤拷一锟斤拷全锟斤拷通锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟窖达拷锟节匡拷锟斤拷锟斤拷锟斤拷通锟斤拷 GlobalIndex 锟斤拷取锟斤拷锟街碉拷锟斤拷锟?
                     int nextGlobal = 0;
                     if (LightControllers.SelectMany(c => c.ChannelConfigs).Any())
                     {
@@ -1209,17 +1209,17 @@ namespace TeamAAS_VP.Services
                     LightControllers.Add(controller);
                 }
             }
-            // 锟街久伙拷锟斤拷锟铰猴拷墓锟皆达拷锟斤拷锟斤拷锟斤拷锟斤拷锟
+            // 锟街久伙拷锟斤拷锟铰猴拷墓锟皆达拷锟斤拷锟斤拷锟斤拷锟斤拷锟?
             FileHelper.WriteJsonFile(LightControllers, ConfigPaths.LightControllersConfigurationPath);
             return controller;
         }
 
         /// <summary>
-        /// 锟斤拷锟斤拷 Id 锟狡筹拷锟斤拷源锟斤拷锟斤拷锟斤拷锟斤拷锟矫诧拷锟斤拷剩锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷卤锟脚o拷锟斤拷 1 锟斤拷始锟斤拷锟斤拷锟斤拷牛锟斤拷锟
-        /// 锟斤拷锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷募锟斤拷锟
+        /// 锟斤拷锟斤拷 Id 锟狡筹拷锟斤拷源锟斤拷锟斤拷锟斤拷锟斤拷锟矫诧拷锟斤拷剩锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷卤锟脚o拷锟斤拷 1 锟斤拷始锟斤拷锟斤拷锟斤拷牛锟斤拷锟?
+        /// 锟斤拷锟斤拷锟斤拷志没锟斤拷锟斤拷锟斤拷募锟斤拷锟?
         /// </summary>
         /// <param name="id">要锟狡筹拷锟侥匡拷锟斤拷锟斤拷 Id锟斤拷</param>
-        /// <returns>锟斤拷锟斤拷晒锟斤拷瞥锟斤拷锟斤拷锟<c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷锟斤拷锟斤拷未锟揭碉拷锟斤拷 Id锟斤拷锟斤拷</returns>
+        /// <returns>锟斤拷锟斤拷晒锟斤拷瞥锟斤拷锟斤拷锟?<c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷锟斤拷锟斤拷未锟揭碉拷锟斤拷 Id锟斤拷锟斤拷</returns>
         public bool RemoveLightController(int id)
         {
             bool result = false;
@@ -1229,7 +1229,7 @@ namespace TeamAAS_VP.Services
                 if (e != null)
                     result = LightControllers.Remove(e);
 
-                // 锟斤拷锟铰帮拷 Id 锟斤拷锟津并达拷 1 锟斤拷始锟斤拷锟铰憋拷牛锟斤拷员锟斤拷锟斤拷锟斤拷锟斤拷锟
+                // 锟斤拷锟铰帮拷 Id 锟斤拷锟津并达拷 1 锟斤拷始锟斤拷锟铰憋拷牛锟斤拷员锟斤拷锟斤拷锟斤拷锟斤拷锟?
                 int no = 1;
                 var sorted = LightControllers.OrderBy(c => c.Id).ToList();
                 foreach (var c in sorted)
@@ -1248,7 +1248,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟Id 锟侥癸拷源锟斤拷锟斤拷锟斤拷锟斤拷
+        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟?Id 锟侥癸拷源锟斤拷锟斤拷锟斤拷锟斤拷
         /// </summary>
         /// <param name="id">锟斤拷锟斤拷锟斤拷 Id锟斤拷</param>
         /// <returns>锟斤拷锟节凤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
@@ -1257,7 +1257,7 @@ namespace TeamAAS_VP.Services
         /// <summary>
         /// 锟斤拷取锟斤拷锟叫癸拷源通锟斤拷锟斤拷只锟斤拷锟斤拷锟较o拷锟斤拷 GlobalIndex 锟斤拷锟津返回★拷
         /// </summary>
-        /// <returns>锟斤拷 GlobalIndex 锟斤拷锟斤拷锟<see cref="ChannelConfig"/> 只锟斤拷锟斤拷锟较★拷</returns>
+        /// <returns>锟斤拷 GlobalIndex 锟斤拷锟斤拷锟?<see cref="ChannelConfig"/> 只锟斤拷锟斤拷锟较★拷</returns>
         public IReadOnlyCollection<ChannelConfig> GetAllLightChannels()
         {
             lock (_sync) { return LightControllers.SelectMany(c => c.ChannelConfigs).OrderBy(ch => ch.GlobalIndex).ToList().AsReadOnly(); }
@@ -1267,14 +1267,14 @@ namespace TeamAAS_VP.Services
         /// 锟斤拷锟斤拷全锟斤拷通锟斤拷锟斤拷锟斤拷锟斤拷取锟斤拷锟斤拷通锟斤拷锟斤拷锟矫★拷
         /// </summary>
         /// <param name="globalIndex">通锟斤拷锟斤拷全锟斤拷锟斤拷锟斤拷锟斤拷GlobalIndex锟斤拷锟斤拷</param>
-        /// <returns>匹锟斤拷锟<see cref="ChannelConfig"/> 实锟斤拷锟斤拷锟揭诧拷锟斤拷锟津返伙拷 <c>null</c>锟斤拷</returns>
+        /// <returns>匹锟斤拷锟?<see cref="ChannelConfig"/> 实锟斤拷锟斤拷锟揭诧拷锟斤拷锟津返伙拷 <c>null</c>锟斤拷</returns>
         public ChannelConfig GetLightChannelByGlobalIndex(int globalIndex)
         {
             lock (_sync) { return LightControllers.SelectMany(c => c.ChannelConfigs).FirstOrDefault(ch => ch.GlobalIndex == globalIndex); }
         }
 
         /// <summary>
-        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟饺�拷锟酵�拷锟斤拷锟斤拷锟斤拷锟酵�拷锟斤拷锟斤拷谩锟
+        /// 锟叫讹拷锟角凤拷锟斤拷锟街革拷锟饺�拷锟酵�拷锟斤拷锟斤拷锟斤拷锟酵�拷锟斤拷锟斤拷谩锟?
         /// </summary>
         /// <param name="globalIndex">全锟斤拷通锟斤拷锟斤拷锟斤拷锟斤拷</param>
         /// <returns>锟斤拷锟节凤拷锟斤拷 <c>true</c>锟斤拷锟斤拷锟津返伙拷 <c>false</c>锟斤拷</returns>
@@ -1356,8 +1356,8 @@ namespace TeamAAS_VP.Services
         public ScrewFeederInfo GetScrewFeeder(Guid id) { lock (_sync) { return ScrewFeeders.FirstOrDefault(s => s.Id == id); } }
 
         /// <summary>
-        /// 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟剿匡拷锟斤拷锟斤拷锟斤拷锟斤拷谩锟
-        /// 锟斤拷锟斤拷时锟斤拷未指锟斤拷 FeederNumber 锟斤拷锟斤拷锟轿�拷锟角帮拷锟斤拷锟�+1锟斤拷
+        /// 锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟剿匡拷锟斤拷锟斤拷锟斤拷锟斤拷谩锟?
+        /// 锟斤拷锟斤拷时锟斤拷未指锟斤拷 FeederNumber 锟斤拷锟斤拷锟轿�拷锟角帮拷锟斤拷锟?1锟斤拷
         /// </summary>
         public ScrewFeederInfo AddOrUpdateScrewFeeder(ScrewFeederInfo feeder)
         {
@@ -1401,7 +1401,7 @@ namespace TeamAAS_VP.Services
         public event Action<ScrewFeederInfo> ScrewFeederLowLevelAlarmTriggered;
 
         /// <summary>
-        /// 锟斤拷取指锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷诺锟绞o拷锟斤拷锟剿匡拷锟斤拷锟斤拷锟斤拷也锟斤拷锟斤拷锟斤拷锟-1锟斤拷
+        /// 锟斤拷取指锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷诺锟绞o拷锟斤拷锟剿匡拷锟斤拷锟斤拷锟斤拷也锟斤拷锟斤拷锟斤拷锟?-1锟斤拷
         /// </summary>
         public int GetRemainingCountByFeederNumber(int feederNumber)
         {
@@ -1414,7 +1414,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟斤拷取指锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷诺牡锟斤拷锟斤拷锟斤拷锟阶刺�拷锟斤拷也锟斤拷锟斤拷锟斤拷锟false锟斤拷
+        /// 锟斤拷取指锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷诺牡锟斤拷锟斤拷锟斤拷锟阶刺�拷锟斤拷也锟斤拷锟斤拷锟斤拷锟?false锟斤拷
         /// </summary>
         public bool GetLowLevelAlarmStatusByFeederNumber(int feederNumber)
         {
@@ -1452,7 +1452,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 为指锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷诺菁锟街革拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷惴碉拷锟-1
+        /// 为指锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷诺菁锟街革拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷惴碉拷锟?-1
         /// </summary>
         public int DecrementScrewCountByFeederNumber(int feederNumber, int decrement = 1)
         {
@@ -1557,7 +1557,7 @@ namespace TeamAAS_VP.Services
                 }
                 catch
                 {
-                    // 锟斤拷锟斤拷锟斤拷锟捷匡拷写锟斤拷锟斤拷螅�锟铰硷拷锟斤拷锟斤拷锟斤拷锟街
+                    // 锟斤拷锟斤拷锟斤拷锟捷匡拷写锟斤拷锟斤拷螅�锟铰硷拷锟斤拷锟斤拷锟斤拷锟街?
                 }
             }
             catch
@@ -1567,7 +1567,7 @@ namespace TeamAAS_VP.Services
         }
 
         /// <summary>
-        /// 锟斤拷锟绞o拷锟斤拷锟剿匡拷欠癖ň锟
+        /// 锟斤拷锟绞o拷锟斤拷锟剿匡拷欠癖ň锟?
         /// </summary>
         public bool CheckAlarm(int feederNumber)
         {

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

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

+ 11 - 0
TeamAAS-VM/ViewModels/SettingViewModel.cs

@@ -1523,6 +1523,7 @@ namespace TeamAAS_VP.ViewModels
                 IsBasePartCodeChooseScrew = sys.IsBasePartCodeChooseScrew;
                 IsBasePartOpenLight = sys.IsBasePartOpenLight;
                 IsBasePartOpenLightKB = sys.IsBasePartOpenLightKB;
+                IsMesResult = sys.IsMesResult;
                 ChooseScrewModel = sys.ChooseScrewModel;
             }
 
@@ -1624,6 +1625,15 @@ namespace TeamAAS_VP.ViewModels
             set { SetProperty(ref _IsBasePartOpenLightKB, value); }
         }
 
+        private bool _IsMesResult;
+
+        public bool IsMesResult
+        {
+            get { return _IsMesResult; }
+            set { SetProperty(ref _IsMesResult, value); }
+        }
+
+
         private bool _IsBasePartCodeChooseScrew;
         /// <summary>
         /// 是否通过零件码判断螺丝型号
@@ -1815,6 +1825,7 @@ namespace TeamAAS_VP.ViewModels
                     sysConfig.ScrewCurvePath = ScrewCurvePath;
                     sysConfig.IsBasePartCodeGetSN = IsBasePartCodeGetSN;
                     sysConfig.IsBasePartCodeChooseScrew = IsBasePartCodeChooseScrew;
+                    sysConfig.IsMesResult = IsMesResult;
                     sysConfig.IsBasePartOpenLightKB = IsBasePartOpenLightKB;
                     sysConfig.IsBasePartOpenLight = IsBasePartOpenLight;
                     sysConfig.ChooseScrewModel = ChooseScrewModel;

File diff suppressed because it is too large
+ 547 - 440
TeamAAS-VM/Views/SettingView.xaml


Some files were not shown because too many files changed in this diff