wanghan před 6 měsíci
rodič
revize
a804352582

+ 72 - 62
TeamAAS-VM/Core/Management.cs

@@ -1221,13 +1221,13 @@ namespace TeamAAS_VP.Core
                     return;
                 }
 
-                //获取产品编码
-                _productService.CurrentProductCode = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, 0));
-                //如果产品编码为空,则以当前的时间戳作为编码
-                if (string.IsNullOrEmpty(_productService.CurrentProductCode))
-                {
-                    _productService.CurrentProductCode = DateTime.Now.ToString("yyyyMMddHHmmssfff");
-                }
+                ////获取产品编码
+                //_productService.CurrentProductCode = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, 0));
+                ////如果产品编码为空,则以当前的时间戳作为编码
+                //if (string.IsNullOrEmpty(_productService.CurrentProductCode))
+                //{
+                //    _productService.CurrentProductCode = DateTime.Now.ToString("yyyyMMddHHmmssfff");
+                //}
 
 
                 bool isDryMode = plc.ReadNode<bool>(addressConfig.In_DryMode.Address);
@@ -2229,8 +2229,8 @@ namespace TeamAAS_VP.Core
                             }
                             else
                             {
-                                string res1="", res2 = "", res3 = "", res4 = "";
-                                if(visionResults[0].IsSucceed)
+                                string res1 = "", res2 = "", res3 = "", res4 = "";
+                                if (visionResults[0].IsSucceed)
                                 {
                                     res1 = visionResults[0].distance.Split(',')[1];
                                 }
@@ -2431,6 +2431,18 @@ namespace TeamAAS_VP.Core
                                 {
                                     await plc.WriteNodeAsync(addressConfig.Out_PartCode.Address, PartSN);
                                     SendTaskMessage($"二维码扫码成功:{PartSN}", MessageLevel.Info);
+
+                                    var stationConfig = _configService.GetDeviceInfo(0);
+                                    if (stationConfig.EnableMES)
+                                    {
+                                        (bool isSuccess, string response) = await _mesService.StationGetEx2Async(0, PartSN, "", 2);//询问零件码,是否可以使用
+                                        if (!isSuccess)//如果二维码已使用,则返回false
+                                        {
+                                            await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)3);
+                                            return;
+                                        }
+                                    }
+
                                     await plc.WriteNodeAsync(addressConfig.Out_QrCodePhotoResault.Address, (Int16)1);
                                 }
                                 else
@@ -2522,16 +2534,13 @@ namespace TeamAAS_VP.Core
                                             LogHelper.WriteLogMes($"询问mes成功");
                                             SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
                                             string ProductMainSN_Temp = "";
-                                            if (response != "MES功能未启用!")
+                                            string[] item = response.Split('\n');
+                                            //获取过度件sn
+                                            string snItem = item.FirstOrDefault(f => f.Contains("sn="));
+                                            if (snItem != null)
                                             {
-                                                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);
-                                                }
+                                                ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
+                                                SendTaskMessage($"过度件sn:{ProductMainSN_Temp}", MessageLevel.Info);
                                             }
 
                                             (bool isSuccess2, string response2) = await _mesService.StationGetAsync(i, ProductMainSN_Temp, "", 2);//询问mes
@@ -2539,26 +2548,23 @@ namespace TeamAAS_VP.Core
                                             {
                                                 LogHelper.WriteLogMes($"第二次询问mes成功");
                                                 SendTaskMessage($"第二次询问mes成功:{response2}", MessageLevel.Debug);
-                                                if (response2 != "MES功能未启用!")
+                                                item = response2.Split('\n');
+                                                //获取产品主sn和wo用于后续上传数据使用
+                                                snItem = item.FirstOrDefault(f => f.Contains("sn="));
+                                                if (snItem != null)
                                                 {
-                                                    string[] item = response2.Split('\n');
-                                                    //获取产品主sn和wo用于后续上传数据使用
-                                                    string 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)
+                                                    ProductMainSN[i] = snItem.Replace("sn=", "").Trim();
+                                                    if (i == 0)
                                                     {
-                                                        wo[i] = woItem.Replace("wo=", "").Trim();
-                                                        SendTaskMessage($"工单信息:{wo[i]}", MessageLevel.Info);
+                                                        _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);
                                                 }
 
                                                 //产品发生改变并且供料器发生了变化时,需要通知PLC进行取料更改
@@ -2587,7 +2593,7 @@ namespace TeamAAS_VP.Core
                                         LogHelper.WriteLogMes($"【mes结束QUERY】");
                                         SendTaskMessage($"mes结束QUERY", MessageLevel.Info);
                                     }
-                                    else
+                                    else if (stationConfig.EnableMES)
                                     {
                                         SendTaskMessage($"收到{i}:{stationConfig.DeviceName}过站检查请求(当站是否生产)(当前不需要双MES验证)...", MessageLevel.Debug);
 
@@ -2596,35 +2602,25 @@ namespace TeamAAS_VP.Core
                                         (bool isSuccess, string response) = await _mesService.StationGetAsync(i, code, "", 2);//询问mes
                                         if (isSuccess)
                                         {
-                                            if (response != "MES功能未启用!")
+                                            string[] item = response.Split('\n');
+                                            //获取产品主sn和wo用于后续上传数据使用  
+                                            string snItem = item.FirstOrDefault(f => f.Contains("sn="));
+                                            if (snItem != null)
                                             {
-                                                string[] item = response.Split('\n');
-                                                //获取产品主sn和wo用于后续上传数据使用
-                                                string 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);
-                                                }
-                                            }
-                                            else
-                                            {
-                                                ProductMainSN[i] = DateTime.Now.ToString();
+                                                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);
+                                            }
+
 
                                             LogHelper.WriteLogMes($"询问mes成功");
                                             SendTaskMessage($"询问mes成功:{response}", MessageLevel.Debug);
@@ -2643,6 +2639,19 @@ namespace TeamAAS_VP.Core
                                         LogHelper.WriteLogMes($"【mes结束QUERY】");
                                         SendTaskMessage($"mes结束QUERY", MessageLevel.Info);
                                     }
+                                    else
+                                    {
+                                        ProductMainSN[i] = DateTime.Now.ToString();
+                                        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 if (state == 2)
@@ -2784,7 +2793,7 @@ namespace TeamAAS_VP.Core
                                                 ERROR_MESSAGE = ""
                                             });
                                         }
-                                        
+
                                         DFC.Add(new DataFormCtq
                                         {
                                             PPID = ProductMainSN[i],
@@ -2986,12 +2995,13 @@ namespace TeamAAS_VP.Core
                                     //读取压力值
                                     //float pressure = plc.ReadNode<float>(addressConfig.In_PressureValue.Address);
                                     //读取压力值
-                                    float pressure = plc.ReadNode<float>(string.Format(addressConfig.In_PressureValue.Address, i));
+                                    //float pressure = plc.ReadNode<float>(string.Format(addressConfig.In_PressureValue.Address, i));
+                                    float pressure = plc.ReadNode<float>(string.Format(addressConfig.In_PressureLastValue.Address, i));
                                     currentPressure[i] = pressure;
                                     SendTaskMessage($"[{i}]压力:{pressure}", MessageLevel.Info);
 
                                     //await Task.Delay(60);
-                                    await plc.WriteNodeAsync(addressConfig.In_WorkDone.Address, (Int16)0);
+                                    await plc.WriteNodeAsync(string.Format(addressConfig.In_WorkDone.Address, i), (Int16)0);
                                     var code = plc.ReadNode<string>(string.Format(addressConfig.In_Code.Address, i));
                                     // 停止采集压力并保存
                                     await StopAndSavePressureCollectionAsync(currentProduct.Name, code, i);

+ 20 - 0
TeamAAS-VM/Interfaces/IMesService.cs

@@ -45,6 +45,17 @@ namespace TeamAAS_VP.Interfaces
         /// </remarks>
         Task<(bool IsSuccess, string Response)> StationGetAsync(int deviceNo, string sn, string comp, double timeoutInSeconds);
 
+        /// <summary>
+        /// 绑定交互
+        /// </summary>
+        /// <param name="sn"></param>
+        /// <param name="bindingData"></param>
+        /// <param name="bindingModel"> 0=查询,1=绑定,2=解绑</param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="ObjectDisposedException"></exception>
+        Task<(bool IsSuccess, string Response)> StationGetBindingAsync(string sn, string bindingData, uint bindingModel, double timeoutInSeconds);
+
         /// <summary>
         /// 过站请求的扩展版本,允许同时指定 <see cref="CancellationToken"/> 和超时时间(秒)。实现应优先考虑取消请求,但在未取消的情况下应用超时。
         /// </summary>
@@ -54,6 +65,15 @@ namespace TeamAAS_VP.Interfaces
         /// <returns></returns>
         Task<(bool IsSuccess, string Response)> StationGetExAsync(int deviceNo, string sn, string comp, double timeoutInSeconds);
 
+        /// <summary>
+        /// 过站请求的扩展版本,允许同时指定 <see cref="CancellationToken"/> 和超时时间(秒)。实现应优先考虑取消请求,但在未取消的情况下应用超时。
+        /// </summary>
+        /// <param name="sn"></param>
+        /// <param name="comp"></param>
+        /// <param name="timeoutInSeconds"></param>
+        /// <returns></returns>
+        Task<(bool IsSuccess, string Response)> StationGetEx2Async(int deviceNo, string partSN, string comp, double timeoutInSeconds);
+
         /// <summary>
         /// 使用配置的提交(Submit)URL 发起 GET 请求,提交过站结果及时间信息。
         /// </summary>

+ 15 - 0
TeamAAS-VM/Models/PLC/PlcAddressConfig.cs

@@ -601,6 +601,16 @@ namespace TeamAAS_VP.Models.PLC
             set { SetProperty(ref _In_PressureValue, value); }
         }
 
+        private PlcAddress _In_PressureLastValue = new PlcAddress();
+        /// <summary>
+        /// 最终压力值
+        /// </summary>
+        public PlcAddress In_PressureLastValue
+        {
+            get { return _In_PressureLastValue; }
+            set { SetProperty(ref _In_PressureLastValue, value); }
+        }
+
         private PlcAddress _In_ScrewTeachExe;
         /// <summary>
         /// 触发披头视觉矫正
@@ -1172,6 +1182,11 @@ namespace TeamAAS_VP.Models.PLC
             In_PressureValue.DataType = "float";
             In_PressureValue.Description = "压力值";
 
+            In_PressureLastValue = Ensure(In_PressureLastValue);
+            In_PressureLastValue.Address = "ns=4;s=DB_DriveControl|AICtrl[{0}].Status.EndValue";
+            In_PressureLastValue.DataType = "float";
+            In_PressureLastValue.Description = "最终压力值";
+
             In_ScrewTeachExe = Ensure(In_ScrewTeachExe);
             In_ScrewTeachExe.Address = "ns=4;s=DB_Communication|ToPC.ScrewTeachExe";
             In_ScrewTeachExe.DataType = "bool";

+ 181 - 29
TeamAAS-VM/Services/MesService.cs

@@ -10,7 +10,6 @@ using System.Threading.Tasks;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Events;
 using TeamAAS_VP.Interfaces;
-using TeamAAS_VP.Models;
 
 namespace TeamAAS_VP.Services
 {
@@ -87,7 +86,7 @@ namespace TeamAAS_VP.Services
                 device = _configService.GetDeviceInfo();
             if (device == null || !device.EnableMES)
             {
-                return (true, "MES功能未启用!");
+                return (false, "MES功能未启用!");
             }
             if (string.IsNullOrWhiteSpace(device.MesUrl))
                 return (false, "Missing MES station URL");
@@ -100,21 +99,20 @@ namespace TeamAAS_VP.Services
             }
 
             string url = "";
-
             if (deviceNo == 0)//组装站
             {
                 if (device.F == "PTL")
                 {
-                    url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&comp={device.comp}:{comp}&p=unit_process_check,message,model,sn,wo";
+                    url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&comp={device.comp}:{comp}&p=message,sn,wo,model_num,color";
                 }
                 else
                 {
-                    url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&p=unit_process_check,message,model,sn,wo";
+                    url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&p=message,sn,wo,model_num,color";
                 }
             }
             else//保压站
             {
-                url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&p=unit_process_check,message,model,sn,wo";
+                url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&p=message,sn,wo,model_num,color";
             }
 
             LogHelper.WriteLogMes($"【询问URL】{url}");
@@ -197,7 +195,7 @@ namespace TeamAAS_VP.Services
                 device = _configService.GetDeviceInfo();
             if (device == null || !device.EnableMES)
             {
-                return (true, "MES功能未启用!");
+                return (false, "MES功能未启用!");
             }
             if (string.IsNullOrWhiteSpace(device.MesUrl))
                 return (false, "Missing MES station URL");
@@ -242,6 +240,93 @@ namespace TeamAAS_VP.Services
             return result;
         }
 
+
+        /// <summary>
+        /// 向配置中指定的 MES 站点 URL 发起 GET 请求(用于零件使用查询)。
+        /// </summary>
+        /// <param name="partSN">要查询的序列号(SN)。</param>
+        /// <param name="cancellationToken">可选的取消令牌,用于请求超时或取消。</param>
+        /// <returns>
+        /// 返回元组 (IsSuccess, Response):
+        /// - IsSuccess: 请求是否视为成功(即 MES 返回了预期的 "SFC_OK" 且包含 "unit_process_check=OK")。
+        /// - Response: 原始响应文本或错误信息说明(如 "MES功能未启用!"、"Missing MES station URL"、"Canceled" 等)。
+        /// </returns>
+        /// <remarks>
+        /// 行为说明:
+        /// - 从配置获取设备信息,若 MES 未启用则直接返回 IsSuccess=true 且 Response 为提示文本(不视为错误)。
+        /// - 若未配置 MES URL 则返回 IsSuccess=false 与错误文本。
+        /// - 使用配置的 URL(通过 <see cref="string.Format"/> 填充 sn)发起 GET 请求,最多重试 10 次。
+        /// - 只有当响应包含 "SFC_OK" 且包含 "unit_process_check=OK" 时视为成功并立即返回。
+        /// - 捕获 <see cref="OperationCanceledException"/> 返回 "Canceled",捕获其它异常则返回异常消息。
+        /// - 若实例已被释放则抛出 <see cref="ObjectDisposedException"/>。
+        /// </remarks>
+        public async Task<(bool IsSuccess, string Response)> StationGetEx2Async(int deviceNo, string partSN, string comp, CancellationToken cancellationToken = default)
+        {
+            // 如果已经释放,则抛出异常,防止在已释放资源上继续操作。
+            if (_disposed) throw new ObjectDisposedException(nameof(MesService));
+
+            var device = _configService.GetDeviceInfo(deviceNo);
+            if (device == null)
+                device = _configService.GetDeviceInfo();
+            if (device == null || !device.EnableMES)
+            {
+                return (false, "MES功能未启用!");
+            }
+            if (string.IsNullOrWhiteSpace(device.MesUrl))
+                return (false, "Missing MES station URL");
+
+            string url = "";
+            url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={partSN}&p=sn,message";
+
+            LogHelper.WriteLogMes($"【询问URL】{url}");
+            SendTaskMessage($"【询问URL】{url}", MessageLevel.Info);
+            (bool IsSuccess, string Response) result = (false, string.Empty);
+
+            for (int i = 0; i < 3; i++)
+            {
+                try
+                {
+                    using (var rsp = await _httpClient.GetAsync(url))
+                    {
+                        var text = await rsp.Content.ReadAsStringAsync();
+                        LogHelper.WriteLogMes($"【HTTP接收】{text}");
+                        SendTaskMessage($"【HTTP接收】{text}", MessageLevel.Info);
+
+                        // "SFC_OK" 代表和 MES 连接成功
+                        if (text.Contains("SFC_OK"))
+                        {
+                            string[] item = text.Split('\n');
+                            string snItem = item.FirstOrDefault(f => f.Contains("sn="));
+                            if (snItem != null)
+                            {
+                                if (snItem.Replace("sn=", "").Length == 0)//如果零件没做过
+                                {
+                                    return (true, text);
+                                }
+                                else//如果零件已做过
+                                {
+                                    string ProductMainSN_Temp = snItem.Replace("sn=", "").Trim();
+                                    SendTaskMessage($"零件已绑定sn:{ProductMainSN_Temp}", MessageLevel.Error);
+                                    return (false, text);
+                                }
+                            }
+                        }
+                        result = (false, text);
+                    }
+                }
+                catch (OperationCanceledException)
+                {
+                    result = (false, "Canceled");
+                }
+                catch (Exception ex)
+                {
+                    result = (false, ex.Message);
+                }
+            }
+            return result;
+        }
+
+
         public async Task<(bool IsSuccess, string Response)> StationGetExAsync(int deviceNo, string sn, string comp, double timeoutInSeconds)
         {
             using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)))
@@ -250,6 +335,14 @@ namespace TeamAAS_VP.Services
             }
         }
 
+        public async Task<(bool IsSuccess, string Response)> StationGetEx2Async(int deviceNo, string partSN, string comp, double timeoutInSeconds)
+        {
+            using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)))
+            {
+                return await StationGetEx2Async(deviceNo, partSN, comp, cts.Token);
+            }
+        }
+
         /// <summary>
         /// 向配置中指定的 MES 提交 URL 发起 GET 请求(用于提交测试结果/记录)。
         /// </summary>
@@ -317,26 +410,15 @@ namespace TeamAAS_VP.Services
 
             string url = sb.ToString();
 
-            //if (deviceNo == 0)//组装站
+            //if (device.F == "PTL")
             //{
-            //    if (device.F == "PTL")
-            //    {
-            //        url = $"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={station[deviceNo]}&fixtureid={fixtureId[deviceNo]}&sn={sn}&f=PTL&comp={device.comp}:{comp}" +
-            //        $"&gap={gap}&assy_pressure={assypress}" +
-            //        $"&result={res}&start_time={start_time.ToString("yyyy-MM-dd HH:mm:ss")}&stop_time={stop_time.ToString("yyyy-MM-dd HH:mm:ss")}";
-            //    }
-            //    else
-            //    {
-            //        url = $"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={station[deviceNo]}&fixtureid={fixtureId[deviceNo]}&sn={sn}&f=PQC" +
-            //      $"&gap={gap}&assy_pressure={assypress}" +
-            //      $"&result={res}&start_time={start_time.ToString("yyyy-MM-dd HH:mm:ss")}&stop_time={stop_time.ToString("yyyy-MM-dd HH:mm:ss")}";
-            //    }
+            //    url = $"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&f=PTL&comp={device.comp}:{comp}" +
+            //        $"&cc={cc}&laser_height={height}&result={res}&start_time={start_time.ToString("yyyy-MM-dd HH:mm:ss")}&stop_time={stop_time.ToString("yyyy-MM-dd HH:mm:ss")}";
             //}
             //else
             //{
-            //    url = $"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={station[deviceNo]}&fixtureid={fixtureId[deviceNo]}&sn={sn}&f=PQC" +
-            //      $"&pressure={press}&pressure_time={presstime}" +
-            //      $"&result={res}&start_time={start_time.ToString("yyyy-MM-dd HH:mm:ss")}&stop_time={stop_time.ToString("yyyy-MM-dd HH:mm:ss")}";
+            //    url = $"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&f=PQC" +
+            //        $"&cc={cc}&laser_height={height}&result={res}&start_time={start_time.ToString("yyyy-MM-dd HH:mm:ss")}&stop_time={stop_time.ToString("yyyy-MM-dd HH:mm:ss")}";
             //}
 
             LogHelper.WriteLogMes($"【上传URL】{url}");
@@ -377,6 +459,76 @@ namespace TeamAAS_VP.Services
             return result;
         }
 
+        public async Task<(bool IsSuccess, string Response)> StationGetBindingAsync(string sn, string bindingData, uint bindingModel, double timeoutInSeconds)
+        {
+            using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)))
+            {
+                return await StationGetBindingAsync(sn, bindingData, bindingModel, cts.Token);
+            }
+        }
+
+        /// <summary>
+        /// 绑定交互
+        /// </summary>
+        /// <param name="sn"></param>
+        /// <param name="bindingData"></param>
+        /// <param name="bindingModel"> 0=查询,1=绑定,2=解绑</param>
+        /// <param name="cancellationToken"></param>
+        /// <returns></returns>
+        /// <exception cref="ObjectDisposedException"></exception>
+        public async Task<(bool IsSuccess, string Response)> StationGetBindingAsync(string sn, string bindingData, uint bindingModel, CancellationToken cancellationToken = default)
+        {
+            // 如果已经释放,则抛出异常,防止在已释放资源上继续操作。
+            if (_disposed) throw new ObjectDisposedException(nameof(MesService));
+
+            var device = _configService.GetDeviceInfo();
+            if (device == null || !device.EnableMES)
+            {
+                return (false, "MES功能未启用!");
+            }
+            if (string.IsNullOrWhiteSpace(device.MesUrl))
+                return (false, "Missing MES station URL");
+
+            string url = "";
+            url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&p=message,sn,wo";
+
+            LogHelper.WriteLogMes($"【询问URL】{url}");
+            SendTaskMessage($"【询问URL】{url}", MessageLevel.Info);
+            (bool IsSuccess, string Response) result = (false, string.Empty);
+
+            for (int i = 0; i < 3; i++)
+            {
+                try
+                {
+                    using (var rsp = await _httpClient.GetAsync(url))
+                    {
+                        var text = await rsp.Content.ReadAsStringAsync();
+                        LogHelper.WriteLogMes($"【HTTP接收】{text}");
+                        SendTaskMessage($"【HTTP接收】{text}", MessageLevel.Info);
+
+                        // "SFC_OK" 代表和 MES 连接成功
+                        if (text.Contains("SFC_OK"))
+                        {
+                            if (text.Contains("message=OK"))
+                            {
+                                return (true, text);
+                            }
+                        }
+                        result = (false, text);
+                    }
+                }
+                catch (OperationCanceledException)
+                {
+                    result = (false, "Canceled");
+                }
+                catch (Exception ex)
+                {
+                    result = (false, ex.Message);
+                }
+            }
+            return result;
+        }
+
         /// <summary>
         /// 向 MES 提交请求的超时重载,使用超时(秒)作为便捷参数。
         /// </summary>
@@ -410,17 +562,17 @@ namespace TeamAAS_VP.Services
                 device = _configService.GetDeviceInfo();
             if (device == null || !device.EnableMES)
             {
-                return (true, DateTime.UtcNow);
+                return (true, DateTime.Now);
             }
             if (string.IsNullOrWhiteSpace(device.MesUrl))
-                return (false, DateTime.UtcNow);
+                return (false, DateTime.Now);
 
             string url = "";
             url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&p=mes_server_time";
 
             LogHelper.WriteLogMes($"【询问URL】{url}");
             SendTaskMessage($"【询问URL】{url}", MessageLevel.Info);
-            (bool IsSuccess, DateTime Now) result = (false, DateTime.UtcNow);
+            (bool IsSuccess, DateTime Now) result = (false, DateTime.Now);
 
             for (int i = 0; i < 3; i++)
             {
@@ -449,16 +601,16 @@ namespace TeamAAS_VP.Services
                                 }
                             }
                         }
-                        result = (false, DateTime.UtcNow);
+                        result = (false, DateTime.Now);
                     }
                 }
                 catch (OperationCanceledException)
                 {
-                    result = (false, DateTime.UtcNow);
+                    result = (false, DateTime.Now);
                 }
                 catch (Exception ex)
                 {
-                    result = (false, DateTime.UtcNow);
+                    result = (false, DateTime.Now);
                 }
             }
             return result;