using Cognex.VisionPro.QuickBuild.Implementation.Internal;
using NPOI.SS.Formula.Functions;
using Prism.Events;
using Prism.Ioc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TeamAAS_VP.Core;
using TeamAAS_VP.Enums;
using TeamAAS_VP.Events;
using TeamAAS_VP.Interfaces;
using TeamAAS_VP.Models;
using TeamAAS_VP.Resources.Languages;
namespace TeamAAS_VP.Services
{
///
/// 基于 的 MES 通信服务实现。
///
/// 本服务通过注入的 获取设备配置(包含 MES 接口地址),
/// 提供对站点查询(StationGetAsync)和提交(SubmitGetAsync)的 GET 请求封装并带有重试逻辑。
///
///
/// 注意:
/// - 为可复用的单一实例,建议在服务生命周期内重用以避免端口耗尽。
/// - 本类实现了显式的 方法以释放内部 资源,调用后实例不可再用。
///
///
public class MesService : IMesService
{
IEventAggregator _eventAggregator;
///
/// 用于执行 HTTP 请求的客户端实例。建议在服务生命周期内重用,避免频繁创建导致套接字/端口耗尽。
///
private readonly HttpClient _httpClient;
///
/// 配置服务,用于获取设备信息(包含 MES URL、是否启用 MES 等)。
///
private readonly IConfigService _configService;
///
/// 标记实例是否已释放,防止重复释放和在释放后继续使用导致未定义行为。
///
private bool _disposed;
///
/// 使用指定的配置服务创建 实例。
///
/// 用于获取设备配置信息的实现,不能为空。
/// 当 为 null 时抛出。
public MesService(IConfigService configService, IContainerProvider container, IEventAggregator ea)
{
_configService = configService ?? throw new ArgumentNullException(nameof(configService));
_httpClient = new HttpClient();
_disposed = false;
_eventAggregator = ea;
}
///
/// 向配置中指定的 MES 站点 URL 发起 GET 请求(用于过站查询)。
///
/// 要查询的序列号(SN)。
/// 可选的取消令牌,用于请求超时或取消。
///
/// 返回元组 (IsSuccess, Response):
/// - IsSuccess: 请求是否视为成功(即 MES 返回了预期的 "SFC_OK" 且包含 "unit_process_check=OK")。
/// - Response: 原始响应文本或错误信息说明(如 "MES功能未启用!"、"Missing MES station URL"、"Canceled" 等)。
///
///
/// 行为说明:
/// - 从配置获取设备信息,若 MES 未启用则直接返回 IsSuccess=true 且 Response 为提示文本(不视为错误)。
/// - 若未配置 MES URL 则返回 IsSuccess=false 与错误文本。
/// - 使用配置的 URL(通过 填充 sn)发起 GET 请求,最多重试 10 次。
/// - 只有当响应包含 "SFC_OK" 且包含 "unit_process_check=OK" 时视为成功并立即返回。
/// - 捕获 返回 "Canceled",捕获其它异常则返回异常消息。
/// - 若实例已被释放则抛出 。
///
public async Task<(bool IsSuccess, string Response)> StationGetAsync(int deviceNo, string sn, 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 (true, "MES功能未启用!");
}
if (string.IsNullOrWhiteSpace(device.MesUrl))
return (false, "Missing MES station URL");
if (device.F == "PTL")
{
if (string.IsNullOrEmpty(device.comp))
{
return (false, "PTL模式下comp不能为空");
}
}
string url = "";
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=message,sn,wo,model_num,color,ppid,stage";
}
else
{
url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&p=message,sn,wo,model_num,color,ppid,stage";
}
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;
}
public async Task<(bool IsSuccess, string Response)> StationGetExAsync(string sn, string comp, double timeoutInSeconds)
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)))
{
return await StationGetExAsync(sn, comp, cts.Token);
}
}
///
/// 向 MES 发起站点查询请求,使用超时(秒)作为便捷重载。
///
/// 序列号(SN)。
/// 请求超时,单位为秒。
/// 与 相同的返回语义。
public async Task<(bool IsSuccess, string Response)> StationGetAsync(string sn, string comp, double timeoutInSeconds)
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)))
{
return await StationGetAsync(sn, comp, cts.Token);
}
}
///
/// 向配置中指定的 MES 站点 URL 发起 GET 请求(用于过站查询)。
///
/// 要查询的序列号(SN)。
/// 可选的取消令牌,用于请求超时或取消。
///
/// 返回元组 (IsSuccess, Response):
/// - IsSuccess: 请求是否视为成功(即 MES 返回了预期的 "SFC_OK" 且包含 "unit_process_check=OK")。
/// - Response: 原始响应文本或错误信息说明(如 "MES功能未启用!"、"Missing MES station URL"、"Canceled" 等)。
///
///
/// 行为说明:
/// - 从配置获取设备信息,若 MES 未启用则直接返回 IsSuccess=true 且 Response 为提示文本(不视为错误)。
/// - 若未配置 MES URL 则返回 IsSuccess=false 与错误文本。
/// - 使用配置的 URL(通过 填充 sn)发起 GET 请求,最多重试 10 次。
/// - 只有当响应包含 "SFC_OK" 且包含 "unit_process_check=OK" 时视为成功并立即返回。
/// - 捕获 返回 "Canceled",捕获其它异常则返回异常消息。
/// - 若实例已被释放则抛出 。
///
public async Task<(bool IsSuccess, string Response)> StationGetExAsync(string sn, string comp, CancellationToken cancellationToken = default)
{
// 如果已经释放,则抛出异常,防止在已释放资源上继续操作。
if (_disposed) throw new ObjectDisposedException(nameof(MesService));
var device = _configService.GetDeviceInfo();
if (device == null || !device.EnableMES)
{
return (true, "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=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"))
{
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;
}
///
/// 向配置中指定的 MES 站点 URL 发起 GET 请求(用于过站查询)。
///
/// 要查询的序列号(SN)。
/// 可选的取消令牌,用于请求超时或取消。
///
/// 返回元组 (IsSuccess, Response):
/// - IsSuccess: 请求是否视为成功(即 MES 返回了预期的 "SFC_OK" 且包含 "unit_process_check=OK")。
/// - Response: 原始响应文本或错误信息说明(如 "MES功能未启用!"、"Missing MES station URL"、"Canceled" 等)。
///
///
/// 行为说明:
/// - 从配置获取设备信息,若 MES 未启用则直接返回 IsSuccess=true 且 Response 为提示文本(不视为错误)。
/// - 若未配置 MES URL 则返回 IsSuccess=false 与错误文本。
/// - 使用配置的 URL(通过 填充 sn)发起 GET 请求,最多重试 10 次。
/// - 只有当响应包含 "SFC_OK" 且包含 "unit_process_check=OK" 时视为成功并立即返回。
/// - 捕获 返回 "Canceled",捕获其它异常则返回异常消息。
/// - 若实例已被释放则抛出 。
///
public async Task<(bool IsSuccess, string Response)> StationGetAsync(string sn, string comp, CancellationToken cancellationToken = default)
{
// 如果已经释放,则抛出异常,防止在已释放资源上继续操作。
if (_disposed) throw new ObjectDisposedException(nameof(MesService));
var device = _configService.GetDeviceInfo();
if (device == null || !device.EnableMES)
{
return (true, "MES功能未启用!");
}
if (string.IsNullOrWhiteSpace(device.MesUrl))
{
return (false, "Missing MES station URL");
}
if (device.F == "PTL")
{
if (string.IsNullOrEmpty(device.comp))
{
return (false, "PTL模式下comp不能为空");
}
}
string url = "";
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=message,sn,wo,model_num,color,ppid,stage";
}
else
{
url = $"{device.MesUrl}?c=QUERY_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&p=message,sn,wo,model_num,color,ppid,stage";
}
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;
}
///
/// 向配置中指定的 MES 站点 URL 发起 GET 请求(用于过站查询)。
///
/// 要查询的序列号(SN)。
/// 可选的取消令牌,用于请求超时或取消。
///
/// 返回元组 (IsSuccess, Response):
/// - IsSuccess: 请求是否视为成功(即 MES 返回了预期的 "SFC_OK" 且包含 "unit_process_check=OK")。
/// - Response: 原始响应文本或错误信息说明(如 "MES功能未启用!"、"Missing MES station URL"、"Canceled" 等)。
///
///
/// 行为说明:
/// - 从配置获取设备信息,若 MES 未启用则直接返回 IsSuccess=true 且 Response 为提示文本(不视为错误)。
/// - 若未配置 MES URL 则返回 IsSuccess=false 与错误文本。
/// - 使用配置的 URL(通过 填充 sn)发起 GET 请求,最多重试 10 次。
/// - 只有当响应包含 "SFC_OK" 且包含 "unit_process_check=OK" 时视为成功并立即返回。
/// - 捕获 返回 "Canceled",捕获其它异常则返回异常消息。
/// - 若实例已被释放则抛出 。
///
public async Task<(bool IsSuccess, string Response)> StationGetExAsync(int deviceNo, string sn, 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 (true, "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;
}
///
/// 向 MES 发起站点查询请求,使用超时(秒)作为便捷重载。
///
/// 序列号(SN)。
/// 请求超时,单位为秒。
/// 与 相同的返回语义。
public async Task<(bool IsSuccess, string Response)> StationGetAsync(int deviceNo, string sn, string comp, double timeoutInSeconds)
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)))
{
return await StationGetAsync(deviceNo, sn, comp, cts.Token);
}
}
public async Task<(bool IsSuccess, string Response)> StationGetExAsync(int deviceNo, string sn, string comp, double timeoutInSeconds)
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)))
{
return await StationGetExAsync(deviceNo, sn, comp, cts.Token);
}
}
///
/// 向配置中指定的 MES 提交 URL 发起 GET 请求(用于提交测试结果/记录)。
///
/// 要提交的序列号(SN)。
/// 表示此次记录是否通过(true => PASS,false => FAIL)。
/// 开始时间,提交时使用 "yyyy-MM-dd HH:mm:ss" 格式。
/// 结束时间,提交时使用 "yyyy-MM-dd HH:mm:ss" 格式。
/// 可选的取消令牌,用于请求超时或取消。
///
/// 返回元组 (IsSuccess, Response):
/// - IsSuccess: 提交是否被 MES 视为成功(收到 "SFC_OK" 且响应中无额外多行信息)。
/// - Response: MES 返回文本或错误说明;当 MES 返回 "SFC_OK" 但包含额外多行信息(代表 SN 数据问题)时,IsSuccess 为 false 且 Response 为该文本。
///
///
/// 行为说明:
/// - 从配置获取设备信息,若 MES 未启用则直接返回 IsSuccess=true 且 Response 为提示文本(不视为错误)。
/// - 若未配置 MES 提交 URL 则返回 IsSuccess=false 与错误文本。
/// - 使用配置的 URL(通过 按顺序填充 sn、PASS/FAIL、start_time、stop_time)发起 GET 请求,最多重试 10 次。
/// - 在收到包含 "SFC_OK" 的响应时,如果返回文本包含多行(有附加信息)则视为提交失败并返回该文本,否则视为提交成功。
/// - 捕获 返回 "Canceled",捕获其它异常则返回异常消息。
/// - 若实例已被释放则抛出 。
///
public async Task<(bool IsSuccess, string Response)> SubmitGetAsync(int deviceNo, string sn, string comp, string torque, string pressure, string turn, bool isSuccess, DateTime start_time, DateTime stop_time, 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 (true, "MES功能未启用!");
}
if (string.IsNullOrWhiteSpace(device.MesUrl))
return (false, "Missing MES submit URL");
if (device.F == "PTL")
{
if (string.IsNullOrEmpty(device.comp))
{
return (false, "PTL模式下comp不能为空");
}
}
//string res = isSuccess ? "Pass" : "Fail";
string res = "Pass";
string url = "";
if (device.F == "PTL")
{
url = $"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&f=PTL&comp={device.comp}:{comp}" +
$"&torque={torque}&pressure={pressure}&turn={turn}&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={device.Station}&fixtureid={device.FixtureId}&sn={sn}&f=PQC" +
$"&torque={torque}&pressure={pressure}&turn={turn}&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}");
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, cancellationToken))
{
var text = await rsp.Content.ReadAsStringAsync();
LogHelper.WriteLogMes($"【HTTP接收】{text}");
SendTaskMessage($"【HTTP接收】{text}", MessageLevel.Info);
// "SFC_OK" 代表和 MES 连接成功
if (text.Contains("SFC_OK"))
{
// 如果有附加信息(多行),代表此 SN 数据有问题,视为失败并返回该文本
if (text.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).Length > 1)
{
return (false, text);
}
return (true, text);
}
result = (false, text);
}
}
catch (OperationCanceledException)
{
result = (false, "Canceled");
}
catch (Exception ex)
{
result = (false, ex.Message);
}
}
return result;
}
///
/// 向配置中指定的 MES 提交 URL 发起 GET 请求(用于提交测试结果/记录)。
///
/// 要提交的序列号(SN)。
/// 表示此次记录是否通过(true => PASS,false => FAIL)。
/// 开始时间,提交时使用 "yyyy-MM-dd HH:mm:ss" 格式。
/// 结束时间,提交时使用 "yyyy-MM-dd HH:mm:ss" 格式。
/// 可选的取消令牌,用于请求超时或取消。
///
/// 返回元组 (IsSuccess, Response):
/// - IsSuccess: 提交是否被 MES 视为成功(收到 "SFC_OK" 且响应中无额外多行信息)。
/// - Response: MES 返回文本或错误说明;当 MES 返回 "SFC_OK" 但包含额外多行信息(代表 SN 数据问题)时,IsSuccess 为 false 且 Response 为该文本。
///
///
/// 行为说明:
/// - 从配置获取设备信息,若 MES 未启用则直接返回 IsSuccess=true 且 Response 为提示文本(不视为错误)。
/// - 若未配置 MES 提交 URL 则返回 IsSuccess=false 与错误文本。
/// - 使用配置的 URL(通过 按顺序填充 sn、PASS/FAIL、start_time、stop_time)发起 GET 请求,最多重试 10 次。
/// - 在收到包含 "SFC_OK" 的响应时,如果返回文本包含多行(有附加信息)则视为提交失败并返回该文本,否则视为提交成功。
/// - 捕获 返回 "Canceled",捕获其它异常则返回异常消息。
/// - 若实例已被释放则抛出 。
///
public async Task<(bool IsSuccess, string Response)> SubmitGetAsync(string sn, string comp, Dictionary data, bool isSuccess, DateTime start_time, DateTime stop_time, CancellationToken cancellationToken = default)
{
// 如果已经释放,则抛出异常,防止在已释放资源上继续操作。
if (_disposed) throw new ObjectDisposedException(nameof(MesService));
var device = _configService.GetDeviceInfo();
if (device == null || !device.EnableMES)
{
return (true, "MES功能未启用!");
}
if (string.IsNullOrWhiteSpace(device.MesUrl))
return (false, "Missing MES submit URL");
if (device.F == "PTL")
{
if (string.IsNullOrEmpty(device.comp))
{
return (false, "PTL模式下comp不能为空");
}
}
//string res = isSuccess ? "Pass" : "Fail";
string res = "Pass";
StringBuilder sb = new StringBuilder();
if (device.F == "PTL")
{
sb.Append($"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&f=PTL&comp={device.comp}:{comp}");
foreach (var item in data)
{
sb.Append($"&{item.Key}={item.Value}");
}
sb.Append($"&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
{
sb.Append($"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&f=PQC");
foreach (var item in data)
{
sb.Append($"&{item.Key}={item.Value}");
}
sb.Append($"&result={res}&start_time={start_time.ToString("yyyy-MM-dd HH:mm:ss")}&stop_time={stop_time.ToString("yyyy-MM-dd HH:mm:ss")}");
}
string url = sb.ToString();
//if (device.F == "PTL")
//{
// url = $"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&f=PTL&comp={device.comp}:{comp}" +
// $"&torque={torque}&pressure={pressure}&turn={turn}&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={device.Station}&fixtureid={device.FixtureId}&sn={sn}&f=PQC" +
// $"&torque={torque}&pressure={pressure}&turn={turn}&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}");
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, cancellationToken))
{
var text = await rsp.Content.ReadAsStringAsync();
LogHelper.WriteLogMes($"【HTTP接收】{text}");
SendTaskMessage($"【HTTP接收】{text}", MessageLevel.Info);
// "SFC_OK" 代表和 MES 连接成功
if (text.Contains("SFC_OK"))
{
// 如果有附加信息(多行),代表此 SN 数据有问题,视为失败并返回该文本
if (text.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).Length > 1)
{
return (false, text);
}
return (true, 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)> SubmitGetExAsync(string sn, string comp, Dictionary data, List results, DateTime start_time, DateTime stop_time, CancellationToken cancellationToken = default)
{
// 如果已经释放,则抛出异常,防止在已释放资源上继续操作。
if (_disposed) throw new ObjectDisposedException(nameof(MesService));
var device = _configService.GetDeviceInfo();
if (device == null || !device.EnableMES)
{
return (true, "MES功能未启用!");
}
if (string.IsNullOrWhiteSpace(device.MesUrl))
return (false, "Missing MES submit URL");
if (device.F == "PTL")
{
if (string.IsNullOrEmpty(device.comp))
{
return (false, "PTL模式下comp不能为空");
}
}
//string res = isSuccess ? "Pass" : "Fail";
string res = "Pass";
if (results.Count > 0)//如果有多个结果,则用逗号连接起来传给MES, MES端再解析
{
res = string.Join(",", results);
}
StringBuilder sb = new StringBuilder();
if (device.F == "PTL")
{
sb.Append($"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&f=PTL&comp={device.comp}:{comp}");
foreach (var item in data)
{
sb.Append($"&{item.Key}={item.Value}");
}
sb.Append($"&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
{
sb.Append($"{device.MesUrl}?c=ADD_RECORD&line={device.Line}&station={device.Station}&fixtureid={device.FixtureId}&sn={sn}&f=PQC");
foreach (var item in data)
{
sb.Append($"&{item.Key}={item.Value}");
}
sb.Append($"&result={res}&start_time={start_time.ToString("yyyy-MM-dd HH:mm:ss")}&stop_time={stop_time.ToString("yyyy-MM-dd HH:mm:ss")}");
}
string url = sb.ToString();
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, cancellationToken))
{
var text = await rsp.Content.ReadAsStringAsync();
LogHelper.WriteLogMes($"【HTTP接收】{text}");
SendTaskMessage($"【HTTP接收】{text}", MessageLevel.Info);
// "SFC_OK" 代表和 MES 连接成功
if (text.Contains("SFC_OK"))
{
// 如果有附加信息(多行),代表此 SN 数据有问题,视为失败并返回该文本
if (text.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).Length > 1)
{
return (false, text);
}
return (true, text);
}
result = (false, text);
}
}
catch (OperationCanceledException)
{
result = (false, "Canceled");
}
catch (Exception ex)
{
result = (false, ex.Message);
}
}
return result;
}
///
/// 向 MES 提交请求的超时重载,使用超时(秒)作为便捷参数。
///
/// 序列号(SN)。
/// 是否通过(PASS/FAIL)。
/// 开始时间。
/// 结束时间。
/// 请求超时,单位为秒。
/// 与 相同的返回语义。
public async Task<(bool IsSuccess, string Response)> SubmitGetAsync(int deviceNo, string sn, string comp, string torque, string pressure, string turn, bool isSuccess, DateTime start_time, DateTime stop_time, double timeoutInSeconds)
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(timeoutInSeconds)))
{
return await SubmitGetAsync(deviceNo, sn, comp, torque, pressure, turn, isSuccess, start_time, stop_time, cts.Token);
}
}
///
/// 获取服务器当前时间
///
///
///
///
public async Task<(bool IsSuccess, DateTime Now)> GetServerTimeAsync(int deviceNo, string sn, double timeoutInSeconds)
{
// 如果已经释放,则抛出异常,防止在已释放资源上继续操作。
if (_disposed) throw new ObjectDisposedException(nameof(MesService));
var device = _configService.GetDeviceInfo(deviceNo);
if (device == null)
device = _configService.GetDeviceInfo();
if (device == null || !device.EnableMES)
{
return (true, DateTime.Now);
}
if (string.IsNullOrWhiteSpace(device.MesUrl))
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($"【询问MES Time】{url}");
SendTaskMessage($"【询问MES Time】{url}", MessageLevel.Info);
(bool IsSuccess, DateTime Now) result = (false, DateTime.Now);
for (int i = 0; i < 3; i++)
{
try
{
using (var rsp = await _httpClient.GetAsync(url))
{
var text = await rsp.Content.ReadAsStringAsync();
LogHelper.WriteLogMes($"【HTTP接收MES Time】{text}");
SendTaskMessage($"【HTTP接收MES Time】{text}", MessageLevel.Info);
// "SFC_OK" 代表和 MES 连接成功
if (text.Contains("SFC_OK"))
{
//按照分隔符'/n'分割字符串
string[] lines = text.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
//通过linq查找是否包含"mes_server_time"的行
var timeLine = lines.FirstOrDefault(l => l.Contains("mes_server_time"));
if (timeLine != null)
{
// 提取时间字符串
var timeString = timeLine.Split('=')[1].Trim();
if (DateTime.TryParse(timeString, out DateTime serverTime))
{
return (true, serverTime);
}
}
}
result = (false, DateTime.Now);
}
}
catch (OperationCanceledException)
{
result = (false, DateTime.Now);
}
catch (Exception ex)
{
result = (false, DateTime.Now);
}
}
return result;
}
///
/// 上传log服务器
///
///
///
///
///
///
public async Task<(bool IsSuccess, string Response)> SendFtpFileAsync(string sn, string name, CancellationToken cancellationToken = default)
{
// 如果已经释放,则抛出异常,防止在已释放资源上继续操作。
if (_disposed) throw new ObjectDisposedException(nameof(MesService));
var device = _configService.GetDeviceInfo();
if (device == null || !device.EnableMES)
{
return (true, "MES功能未启用!");
}
if (string.IsNullOrWhiteSpace(device.LogUrl))
{
return (false, "Missing MES station LogUrl");
}
string url = $"{device.LogUrl}?db=mysql&ApiKey={device.ApiKey}&jsondata={{\"SN\":\"{sn}\",\"LINE\":\"{device.Line}\",\"STATION\":\"{device.Station}\",\"TESTRESULT\":\"PASS\",\"FIXTUREID\":\"{device.FixtureId}\",\"TESTDATETIME\":\"{DateTime.Now.ToString()}\",\"LOGFILENAME\":\"{name}.zip\",\"STATIONTYPE\":\"AE\"}}";
LogHelper.WriteLogMes($"【上传LogData的URL】{url}");
SendTaskMessage($"【上传LogData的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, cancellationToken))
{
var text = await rsp.Content.ReadAsStringAsync();
LogHelper.WriteLogMes($"【上传LogData的HTTP接收】{text}");
SendTaskMessage($"【上传LogData的HTTP接收】{text}", MessageLevel.Info);
// "SFC_OK" 代表和 MES 连接成功
if (text.Contains("\"Code\":1"))
{
return (true, text);
}
result = (false, text);
}
}
catch (OperationCanceledException)
{
result = (false, "Canceled");
}
catch (Exception ex)
{
result = (false, ex.Message);
}
}
return result;
}
///
/// 释放服务占用的托管资源(当前仅 )。
/// 本方法为幂等操作,重复调用不会产生异常。调用后实例不可再用于发起请求,尝试使用将抛出 。
///
public void Dispose()
{
if (!_disposed)
{
_httpClient.Dispose();
_disposed = true;
}
}
public void SendTaskMessage(string msg, MessageLevel level)
{
App.Current.Dispatcher.Invoke(() =>
{
_eventAggregator.GetEvent().Publish(new Models.MessageStruct() { Message = msg, level = level });
});
}
}
}