using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; using static SmartScanner.IDViewerDefines; using static SmartScanner.IDViewerSDK; using static SmartScanner.IDViewerSDK2; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Media.Imaging; using System.IO; using System.Runtime; using OpenCvSharp; using System.Windows.Media.Media3D; using System.Collections.Generic; using System.Threading; using System.Net.Sockets; using System.Text; using static System.Net.Mime.MediaTypeNames; using System.Net; using System.Threading.Tasks; using static OpenCvSharp.XImgProc.CvXImgProc; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; using Newtonsoft.Json; using SmartScanner.ViewModel; using SmartScanner.OperateLog; using System.Windows.Input; using System.Timers; using System.Net.NetworkInformation; namespace SmartScanner { /// /// MainWindow.xaml 的交互逻辑 /// /// public partial class MainWindow : System.Windows.Window { //设备的句柄 private readonly Dictionary _deviceHandles = new Dictionary(); //图像句柄 private readonly Dictionary _imagePtr = new Dictionary(); //模型匹配 private readonly Dictionary _DetectorSelect = new Dictionary(); //选择的设备 public int selectedIndex; //设备编号 public int[] deviceIndex = new int[100]; //连接设备个数 int IsConnected = 0; public IntPtr devicesListHandle; //耗时计算 Stopwatch stopwatch = new Stopwatch(); ModbusTCPClient modbusTCPClient = new ModbusTCPClient(); //实例化log类 //EnhancedLogViewModel EnhancedLogViewModel.Instance = new EnhancedLogViewModel(); //设备信息结构体句柄 IDDeviceInfo[] deviceInfo = new IDDeviceInfo[100]; delegate void IDViewerCallBack(IntPtr device, IntPtr image); delegate void IDViewerCallBack1(IntPtr device, IntPtr image); //检测连接状态定时器 private System.Threading.Timer deviceStatusTimer; private int deviceStatusInterval = 3000; // 3秒 //回调委托 static IDViewerCallBack[] callbackDelegate; IDNodeParam iDNodeParam; //创建检测器 public IntPtr detector = new IntPtr(); public static string filePath = ".\\detector.txt"; string Detector_Device = File.ReadAllText(filePath); //信号保持时间 public static string holdtime = ".\\HoldTime.txt"; public string Hold_Time = File.ReadAllText(holdtime); //信号保持时间 public static string selflockingtime = ".\\SelflockingTime.txt"; public string Selflocking_Time = File.ReadAllText(holdtime); //string Detector_Device = "CPU"; //string modelPath = ".\\model\\best.onnx"; //TCP通讯参数 //private TcpListener _server; //private TcpClient _client; //private NetworkStream _stream; private TcpServerManager _tcpServerManager; //图像字节数组 int batchSize = 2; byte[] imgBatch1 = null; byte[] imgBatch2 = null; //输出结果数组 public bool[] outputs = new bool[20]; private TextBlock[] cameraAliasLabels; private TextBlock[] deviceNames; //NG图片路径集合 public List Image_NG = new List(); //寄存器控制变量 public int[] usOutput = new int[16]; public int[] usOutput2 = new int[4]; public bool USOUT = true; public int[] usOutput_tcp; //输出模式控制 bool IsSignleOutput = false; //方案配置参数 public string json; public ResultJudge.JudgmentConfiguration config = new ResultJudge.JudgmentConfiguration(); //ModBus心跳定时器 private System.Threading.Timer heartbeatTimer; private int heartbeatInterval = 30000; // 30秒 //Modbus重连参数 private int _reconnectAttempts = 0;//重连尝试次数 private const int MAX_RECONNECT_ATTEMPTS = 5; //最大重连尝试次数 private bool _isReconnecting = false;//是否正在重连 private System.Threading.Timer _reconnectTimer;//重连定时器 private const int RECONNECT_INTERVAL = 2000; // 重连间隔时间(毫秒) private string _lastIpAddress = ""; // 最后连接的IP地址 private int _lastPort = 502; // 最后连接的端口 //结果信号定时器 private System.Timers.Timer OutputTimer; //定期删除图像 private ImageCleanupService _cleanupService; public MainWindow() { if (!LBProtect.ValidateFingerprint()) { MessageBox.Show("软件未授权在此硬件上运行!"); Close(); return; } InitializeComponent(); DataContext = EnhancedLogViewModel.Instance; // 定期清图服务初始化 InitializeCleanupService(); LoadDataAsync(); // SDK初始化 InitSDK(); // 结果显示界面初始化 InitUI(); // 用户偏好设置初始化 InitUserConfig(); for (int i = 0; i < outputs.Length; i++) { outputs[i] = false; } // 默认加载上次检测方案 LoadLastUsedConfiguration(true); this.Closing += MainWindow_Closing; // 获取ListView中的ScrollViewer引用 // 获取ScrollViewer引用并传递给ViewModel Loaded += (sender, e) => { if (FindVisualChild(LogListView) is ScrollViewer scrollViewer) { EnhancedLogViewModel.Instance.LogScrollViewer = scrollViewer; } }; this.WindowState = WindowState.Maximized; Loaded += (s, e) => RefreshIpButton_Click(null, null); // 自动加载IP RefreshModelList(); // 自动加载模型列表 RefreshDetectionPlanList(); // 自动加载检测方案列表 RefreshProjectList(); // 自动加载工程文件列表 } private async void LoadDataAsync() { LoadingWindow loadingWindow = new LoadingWindow(); loadingWindow.Show(); await Task.Delay(2000); loadingWindow.Close(); } #region 初始化 //SDK初始化 private void InitSDK() { try { long result = IDVIEWER_Init_(); if (result != 0) { MessageBox.Show("SDK初始化失败,错误码:" + result); } } catch (Exception ex) { Console.WriteLine(ex.Message); } } //界面初始化 private void InitUI() { cameraAliasLabels = new TextBlock[30]; //deviceNames = new TextBlock[30]; cameraAliasLabels[0] = cameraAlias0; cameraAliasLabels[1] = cameraAlias1; cameraAliasLabels[2] = cameraAlias2; cameraAliasLabels[3] = cameraAlias3; cameraAliasLabels[4] = cameraAlias4; cameraAliasLabels[5] = cameraAlias5; cameraAliasLabels[6] = cameraAlias6; cameraAliasLabels[7] = cameraAlias7; cameraAliasLabels[8] = cameraAlias8; cameraAliasLabels[9] = cameraAlias9; cameraAliasLabels[10] = cameraAlias10; cameraAliasLabels[11] = cameraAlias11; cameraAliasLabels[12] = cameraAlias12; cameraAliasLabels[13] = cameraAlias13; cameraAliasLabels[14] = cameraAlias14; cameraAliasLabels[15] = cameraAlias15; cameraAliasLabels[16] = cameraAlias16; cameraAliasLabels[17] = cameraAlias17; cameraAliasLabels[18] = cameraAlias18; cameraAliasLabels[19] = cameraAlias19; cameraAliasLabels[20] = cameraAlias20; cameraAliasLabels[21] = cameraAlias21; cameraAliasLabels[22] = cameraAlias22; cameraAliasLabels[23] = cameraAlias23; //deviceNames[0] = deviceName0; //deviceNames[1] = deviceName1; //deviceNames[2] = deviceName2; //deviceNames[3] = deviceName3; //deviceNames[4] = deviceName4; //deviceNames[5] = deviceName5; //deviceNames[6] = deviceName6; //deviceNames[7] = deviceName7; //deviceNames[8] = deviceName8; //deviceNames[9] = deviceName9; //deviceNames[10] = deviceName10; //deviceNames[11] = deviceName11; //deviceNames[12] = deviceName12; //deviceNames[13] = deviceName13; //deviceNames[14] = deviceName14; //deviceNames[15] = deviceName15; //deviceNames[16] = deviceName16; //deviceNames[17] = deviceName17; //deviceNames[18] = deviceName18; //deviceNames[19] = deviceName19; //deviceNames[20] = deviceName20; //deviceNames[21] = deviceName21; //deviceNames[22] = deviceName22; //deviceNames[23] = deviceName23; } //用户配置初始化 private void InitUserConfig() { DownloadData downloadData = new DownloadData(); downloadData.Init(); if (DownloadData.MemoryAlarm_main) { downloadData.CheckStorage(); } IsSignleOutput = (bool)OutputCheckBox.IsChecked; //TCP端口初始化 // 如果从未设置过,使用默认值 if (string.IsNullOrEmpty(Properties.Settings.Default.DefaultPort)) { Properties.Settings.Default.DefaultPort = "8080"; Properties.Settings.Default.SelectedPort = 8080; } // 确保端口在合法范围内 if (Properties.Settings.Default.SelectedPort < 1 || Properties.Settings.Default.SelectedPort > 65535) { Properties.Settings.Default.SelectedPort = 8080; } // 尝试选中已保存的端口 foreach (ComboBoxItem item in TCPServer_PortComboBox.Items) { if (int.TryParse(item.Content.ToString(), out int port) && port == Properties.Settings.Default.SelectedPort) { TCPServer_PortComboBox.SelectedItem = item; return; } } // 如果保存的端口不在预设列表中,显示在编辑框 TCPServer_PortComboBox.Text = Properties.Settings.Default.SelectedPort.ToString(); } //定期清图服务初始化 private void InitializeCleanupService() { // 从设置中获取参数 string imageDir = Properties.Settings.Default.DataSavingRoad; int retentionDays = Properties.Settings.Default.DeleteImageDays; bool isEnabled = Properties.Settings.Default.ImageDeleteEnabled; if (isEnabled && !string.IsNullOrWhiteSpace(imageDir)) { _cleanupService = new ImageCleanupService(imageDir, retentionDays); _cleanupService.Start(); } } protected override void OnClosed(EventArgs e) { _cleanupService?.Stop(); ExcelResultRecorder.FlushRecords(); base.OnClosed(e); } private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { DownloadData.isOP = false; // 窗口关闭时执行操作 // 可选:取消关闭(通过 e.Cancel = true) deviceStatusTimer?.Dispose(); deviceStatusTimer = null; // 清理Modbus相关定时器 heartbeatTimer?.Dispose(); heartbeatTimer = null; _reconnectTimer?.Dispose(); _reconnectTimer = null; // 停止日志自动保存服务 EnhancedLogViewModel.Instance?.StopAutoSaveService(); DisposeDetector_CPU(detector); if (devicesListHandle != IntPtr.Zero) { long result = IDVIEWER_UnInit_(devicesListHandle); } //TCP端口配置 // 验证当前端口值 if (int.TryParse(TCPServer_PortComboBox.Text, out int port) && port >= 1 && port <= 65535) { Properties.Settings.Default.SelectedPort = port; Properties.Settings.Default.DefaultPort = port.ToString(); } else { // 恢复为默认值 Properties.Settings.Default.SelectedPort = 8080; Properties.Settings.Default.DefaultPort = "8080"; } Properties.Settings.Default.Save(); foreach (System.Windows.Window window in System.Windows.Application.Current.Windows.OfType().ToList()) { window.Close(); } Process.GetCurrentProcess().Kill(); } // 当设置变更时重新初始化服务 private void OnSettingsChanged(object sender, EventArgs e) { _cleanupService?.Stop(); InitializeCleanupService(); } // 辅助方法:查找视觉树中的子元素 private static T FindVisualChild(DependencyObject obj) where T : DependencyObject { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child is T result) return result; var childResult = FindVisualChild(child); if (childResult != null) return childResult; } return null; } #endregion #region 设备刷新与连接 private async void Refresh_Btn_Click(object sender, RoutedEventArgs e) { try { Refresh_Btn.IsEnabled = false; IsConnected = 0; // 加载相机连接顺序映射表 var productManager = new ProductManager(); var mapping = productManager.LoadConfig(); bool Bl = true; if (mapping.Count == 0) { MessageBox.Show("映射表为空,请先配置顺序和IP", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } LoadingWindow loadingWindow = new LoadingWindow(); loadingWindow.Show(); loadingWindow.Loading.Text = "设备连接中,请稍候"; await Task.Run(() => { // 刷新相机列表 RefreshDeviceList(); // 按照映射表顺序连接相机 foreach (var item in mapping.OrderBy(x => x.Key)) { int sequence = item.Key; string ip = item.Value; ConnectDeviceByIP(sequence, ip); } Bl = false; }); await Task.Run(() => { while (true) { if (!Bl) { this.Dispatcher.Invoke(new Action(() => { Refresh_Btn.IsEnabled = true; })); break; } } }); loadingWindow.Close(); // 创建定时器,定时检测设备连接状态 deviceStatusTimer = new System.Threading.Timer(DeviceConnectbeatCallback, null, Timeout.Infinite, deviceStatusInterval); // 启动定时器,立即开始并每隔MemoryAlarmInterval毫秒执行一次 deviceStatusTimer.Change(0, deviceStatusInterval); GetMessage($"设备已全部连接完成\n连接台数:{IsConnected}"); } catch (Exception ex) { MessageBox.Show($"请先配置相机连接顺序:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } // 刷新相机列表 private void RefreshDeviceList() { try { this.Dispatcher.BeginInvoke(new Action(() => { DeviceListBox.Items.Clear(); })); // 获取设备列表句柄 devicesListHandle = IDVIEWER_DiscoveryDevices_(500); Console.WriteLine($"获取设备列表句柄"); // 获取设备总数 int deviceCount = (int)IDVIEWER_GetDevicesLength_(devicesListHandle); Console.WriteLine($"获取设备总数:{deviceCount}"); Task.Run(() => GetMessage($"检测到 {deviceCount} 台设备")); // 获取相机IP列表 for (int i = 0; i < deviceCount; i++) { IDViewerSDK.IDVIEWER_SelectIDDeviceInfo_(devicesListHandle, ref deviceInfo[i], (uint)i); string deviceIP = deviceInfo[i].cameraIP.Trim(); // 将相机添加到界面列表 this.Dispatcher.BeginInvoke(new Action(() => { DeviceListBox.Items.Add(deviceIP); })); } } catch (Exception ex) { MessageBox.Show($"设备检测失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } // 通过IP连接相机 private void ConnectDeviceByIP(int sequence, string ip) { IntPtr deviceHandle = IntPtr.Zero; IntPtr imagePtr = IntPtr.Zero; try { // 创建设备句柄 IDVIEWER_CreateDeviceByIP_(ip, ref deviceHandle); if (deviceHandle == IntPtr.Zero) { MessageBox.Show($"未搜索到该设备:{ip}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); return; } // 打开设备 IDDevice_Open(deviceHandle); // 读设备当前各项参数 double getGain = 0.0; long resultGetGain_1330 = IDDevice_GetGain_1330(deviceHandle, 0, ref getGain); IDDeviceHarewareParams iDDeviceHarewareParams; iDDeviceHarewareParams = IDDevice_GetHardWareParams(deviceHandle); IDNodeParam focusingLocation1 = iDDeviceHarewareParams.exposure; IDNodeParam focusingLocation2 = iDDeviceHarewareParams.gamma; float getexposure = focusingLocation1.value; float getgamma = focusingLocation2.value; Console.WriteLine($"{sequence}号设备当前曝光:{getexposure}"); Console.WriteLine($"{sequence}号设备当前增益:{getGain}"); Console.WriteLine($"{sequence}号设备当前伽马:{getgamma}"); Console.WriteLine($"设备{sequence}连接成功"); // 检查设备是否连接成功 long isConnected = IDDevice_IsConnected(deviceHandle); if (isConnected != 1) { MessageBox.Show($"设备连接失败:{ip}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); this.Dispatcher.BeginInvoke(new Action(() => { DeviceListBox.Items.Remove(ip); })); return; } // 初始化图像句柄 long result = IDImage_Init(ref imagePtr); // 存储设备和图像句柄 _deviceHandles[sequence] = deviceHandle; _imagePtr[sequence] = imagePtr; this.Dispatcher.BeginInvoke(new Action(() => { cameraAliasLabels[sequence - 1].Text = sequence.ToString(); //deviceNames[sequence - 1].Text = deviceInfo[sequence - 1].cameraAlias; })); IsConnected++; // SDK存在BUG,相机连接后取的前几张图异常,临时解决方案 // 连接后取3张图丢弃 //for (int i = 0; i <= 3; i++) //{ // IDDevice_CaptureImage(deviceHandle, imagePtr); //} Refresh_ImageParameter(deviceHandle); } catch (Exception ex) { MessageBox.Show($"连接设备时出错:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); this.Dispatcher.BeginInvoke(new Action(() => { DeviceListBox.Items.Remove(ip); })); } } static List gainList = new List(); static IDDOParamsCreate iDDOParamsCreate = new IDDOParamsCreate(); static IDDOParams iDDOParams = iDDOParamsCreate.create(); static void Refresh_ImageParameter(IntPtr device) { /******获取增益******/ int count = 0; long resultCount = IDDevice_GetGainCount(device, ref count); double getAtGain = 0.0; IDDevice_GetGain_1330(device, 0, ref getAtGain); double Gain = 0.0; for (int i = 0; i < count; i++) { IDDevice_SetGain(device, 0, i); long ii = IDDevice_GetGain_1330(device, 0, ref Gain); if (ii == 0) { gainList.Add(Gain); } } double searchValue = getAtGain; // 在 List 中查找该增益值 int index = gainList.IndexOf(searchValue); if (index >= 0) { IDDevice_SetGain(device, 0, index); } IDDeviceHarewareParams iDDeviceHarewareParams; iDDeviceHarewareParams = IDDevice_GetHardWareParams(device); IDNodeParam focusingLocation = iDDeviceHarewareParams.focusingLocation; IDNodeParam focusingLocation1 = iDDeviceHarewareParams.exposure; IDNodeParam focusingLocation2 = iDDeviceHarewareParams.gamma; float maxValue = focusingLocation.maxValue; float CurrentValue = focusingLocation.value; float getexposure = focusingLocation1.value; float getgamma = focusingLocation2.value; //添加DO到方案,只需执行一次。 for (int i = 0; i <= 2; i++) { iDDOParams.pin = (uint)i; iDDOParams.status = 0; iDDOParams.doModel = 0; iDDOParams.durationTime = 2000; iDDOParams.doSource = IDDoEventSource.EVENT_SOURCE_OFF_C; long retm8 = addDOParam(device, ref iDDOParams); Console.WriteLine("当前添加第" + i + "组,ret:" + retm8); } /******将获取的参数设置一次******/ //double getexposure1 = getexposure; //double getgamma1 = getgamma; //int CurrentValue1 = (int)CurrentValue; //long g1 = IDDevice_SetExposure(device, 0, getexposure1); //long g3 = IDDevice_SetGamma(device, 0, getgamma1); //long g4 = IDDevice_SetFocusLocation(device, 0, CurrentValue1); } #endregion #region 设备信息显示 private void DeviceListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (DeviceListBox.SelectedIndex != -1) { CameraIP.Text = deviceInfo[DeviceListBox.SelectedIndex].cameraIP.Trim(); CameraNetMask.Text = deviceInfo[DeviceListBox.SelectedIndex].cameraNetMask.Trim(); CameraGateWay.Text = deviceInfo[DeviceListBox.SelectedIndex].cameraGateWay.Trim(); CameraFirmwareVersion.Text = deviceInfo[DeviceListBox.SelectedIndex].cameraFirmwareVersion.Trim(); CameraAlias.Text = deviceInfo[DeviceListBox.SelectedIndex].cameraAlias; CameraModel.Text = deviceInfo[DeviceListBox.SelectedIndex].cameraModel.Trim(); } else { return; } } #endregion #region 日志显示 //日志显示 private void GetMessage(string obj) { this.Dispatcher.BeginInvoke(new Action(() => { Log.AppendText(obj + Environment.NewLine); Log.ScrollToEnd(); })); } //ModBus日志显示 private void GetMessage_Modbus(string obj) { this.Dispatcher.BeginInvoke(new Action(() => { Client_LogsTextBox.AppendText(obj + Environment.NewLine); Client_LogsTextBox.ScrollToEnd(); })); } #endregion //深度学习模型推理模块 private void Reasoning_AI(IntPtr detector, byte[] ImageBath, int Image_Width, int Image_Height, out int[] classIds, out int[] numBoxes, out float[] prob, out float[] x1, out float[] y1, out float[] x2, out float[] y2, int sequence) { // 初始化输出参数 batchSize = 1; x1 = new float[100]; y1 = new float[100]; x2 = new float[100]; y2 = new float[100]; prob = new float[100]; classIds = new int[100]; numBoxes = new int[batchSize]; try { // 检查 detector 指针是否有效 if (detector == IntPtr.Zero) { throw new ArgumentNullException(nameof(detector), "Detector pointer is null (IntPtr.Zero)"); } // 检查 ImageBath 是否为空或大小不正确 if (ImageBath == null || ImageBath.Length == 0) { throw new ArgumentException("Image batch data is null or empty", nameof(ImageBath)); } // 检查图像尺寸是否合理 if (Image_Width <= 0 || Image_Height <= 0) { throw new ArgumentException($"Invalid image dimensions: Width={Image_Width}, Height={Image_Height}"); } // 检查 batchSize 是否合理 if (batchSize <= 0) { throw new InvalidOperationException($"Invalid batch size: {batchSize}"); } // 检查输出缓冲区是否足够大 if (x1.Length < 100 || y1.Length < 100 || x2.Length < 100 || y2.Length < 100 || prob.Length < 100 || classIds.Length < 100) { throw new InvalidOperationException("Output buffer size is too small (must be at least 100)"); } // 调用 C++ API PredictDetector_CPU( detector, ImageBath, x1, y1, x2, y2, prob, classIds, numBoxes, 100, Image_Width, Image_Height, batchSize, 0.25f, 0.3f, 1, sequence ); } catch (SEHException ex) { // 记录详细的错误信息 Console.Error.WriteLine($"SEHException in PredictDetector_CPU: {ex.Message}"); Console.Error.WriteLine($"Detector: {detector}"); Console.Error.WriteLine($"ImageBatch Length: {ImageBath?.Length ?? 0}"); Console.Error.WriteLine($"Image Size: {Image_Width}x{Image_Height}"); Console.Error.WriteLine($"Batch Size: {batchSize}"); // 可以选择重新抛出或返回默认值 throw new InvalidOperationException("Failed to run AI reasoning due to native code error", ex); } catch (Exception ex) { Console.Error.WriteLine($"Error in Reasoning_AI: {ex.Message}"); throw; } } // 测试方法 private void Start_Detector_Btn_Click(object sender, RoutedEventArgs e) { string images_path = "D:\\01 LBImage\\20241010\\10_59_24_2.bmp"; Mat mat = Cv2.ImRead(images_path); int width = mat.Width; int height = mat.Height; int channel = mat.Channels(); byte[] imgBatch = new byte[width * height * channel]; int batchSize = 1; float[] x1 = new float[1000], y1 = new float[1000], x2 = new float[1000], y2 = new float[1000]; float[] prob = new float[1000]; int[] classIds = new int[1000]; int[] numBoxes = new int[batchSize]; Marshal.Copy(mat.Data, imgBatch, 0, imgBatch.Length); PredictDetector_CPU(detector, imgBatch, x1, y1, x2, y2, prob, classIds, numBoxes, 100, width, height, batchSize, 0.25f, 0.3f, 1, 1); for (int i = 0; i < batchSize; i++) { for (int j = 0; j < numBoxes[i]; j++) { float x1Val = x1[j]; float y1Val = y1[j]; float x2Val = x2[j]; float y2Val = y2[j]; float probVal = prob[j]; int classId = classIds[j]; } } } #region ModBus通讯设置与检测模块 #region 加载端口 private void LoadPorts() { //Server_PortComboBox.ItemsSource = new[] { "8000", "8001", "8002" }; //Client_PortComboBox.ItemsSource = new[] { "8000", "8001", "8002" }; Client_PortComboBox.ItemsSource = new[] { "502" }; } #endregion #region ModBus通讯模块 private void Client_Connect_Click(object sender, RoutedEventArgs e) { try { if (Client_ServerIpComboBox.Text != "" && Client_PortComboBox.SelectedItem != null) { string ipAddress = Client_ServerIpComboBox.Text; int port = int.Parse(Client_PortComboBox.Text); // 保存连接信息用于重连 _lastIpAddress = ipAddress; _lastPort = port; modbusTCPClient.ModbudTCP_Connect(ipAddress, port); if (modbusTCPClient._client == null) { //MessageBox.Show("连接到服务器失败,请检查连接后重试"); return; } // 初始化重连定时器 InitializeReconnectTimer(); // 创建定时器,作为ModBus心跳信号 heartbeatTimer = new System.Threading.Timer(HeartbeatCallback, null, Timeout.Infinite, heartbeatInterval); // 启动定时器,立即开始并每隔heartbeatInterval毫秒执行一次 heartbeatTimer.Change(0, heartbeatInterval); // 开始侦听客户端触发信号 Task.Run(() => ClientListen()); GetMessage_Modbus($"连接成功 {ipAddress}:{port}\n"); this.Dispatcher.BeginInvoke(new Action(() => { LinkState.Text = "ModBus已连接"; LinkState.Foreground = System.Windows.Media.Brushes.LightGreen; })); } else { MessageBox.Show("请输入正确的IP和端口"); this.Dispatcher.BeginInvoke(new Action(() => { LinkState.Text = "未连接"; LinkState.Foreground = System.Windows.Media.Brushes.Red; })); return; } } catch (Exception ex) { MessageBox.Show("地址和端口已被占用"); this.Dispatcher.BeginInvoke(new Action(() => { LinkState.Text = "未连接"; LinkState.Foreground = System.Windows.Media.Brushes.Red; })); return; } } //ModBus断开连接 private void Client_Disconnect_Click(object sender, RoutedEventArgs e) { try { // 停止所有定时器 heartbeatTimer?.Change(Timeout.Infinite, Timeout.Infinite); _reconnectTimer?.Change(Timeout.Infinite, Timeout.Infinite); if (modbusTCPClient._client != null && modbusTCPClient._client.Connected) { modbusTCPClient._client?.Close(); modbusTCPClient._client = null; GetMessage_Modbus("连接已断开\n"); this.Dispatcher.BeginInvoke(new Action(() => { LinkState.Text = "未连接"; LinkState.Foreground = System.Windows.Media.Brushes.Red; })); } // 重置重连状态 _reconnectAttempts = 0; _isReconnecting = false; } catch (Exception ex) { MessageBox.Show("连接断开"); this.Dispatcher.BeginInvoke(new Action(() => { LinkState.Text = "未连接"; LinkState.Foreground = System.Windows.Media.Brushes.Red; })); } } private void Client_ClearClientLogs_Click(object sender, RoutedEventArgs e) { Client_LogsTextBox.Clear(); } #endregion #region 图像采集与处理模块 //获取图像并处理 private void GetImage() { this.Dispatcher.BeginInvoke(new Action(() => { DetectState.Text = "正在检测中。。。"; DetectState.Foreground = System.Windows.Media.Brushes.LightGreen; })); //计时器启动 stopwatch.Reset(); stopwatch.Start(); Console.WriteLine($"计时器启动!"); //加载上次解决方案 bool isL = LoadLastUsedConfiguration(false); if (!isL) { stopwatch.Stop(); long F_elapsedMilliseconds = stopwatch.ElapsedMilliseconds; Console.WriteLine($"系统响应时间为:{F_elapsedMilliseconds}ms"); this.Dispatcher.BeginInvoke(new Action(() => { DetectState.Text = "未在检测"; DetectState.Foreground = System.Windows.Media.Brushes.Red; })); return; } for (int i = 0; i < DeviceListBox.Items.Count; i++) { //采集图像 GetIDDevice_CaptureImage(i + 1); } for (int i = 0; i < DeviceListBox.Items.Count; i++) { //处理图像 Reasoning_Image(i + 1); } stopwatch.Stop(); long elapsedMilliseconds = stopwatch.ElapsedMilliseconds; Console.WriteLine($"系统响应时间为:{elapsedMilliseconds}ms"); EnhancedLogViewModel.Instance.AddLog($"系统响应时间为:{elapsedMilliseconds}ms"); this.Dispatcher.BeginInvoke(new Action(() => { DetectState.Text = "未在检测"; DetectState.Foreground = System.Windows.Media.Brushes.Red; })); //NG图像弹窗显示与保存 if (Image_NG.Count != 0) { Task.Run(() => { List Image_NG_Save = new List(Image_NG); // NG图像弹窗显示 OnDetectionCompleted(Image_NG); // NG图像自动保存 int savedCount = AutoSaveNGImages(Image_NG_Save); EnhancedLogViewModel.Instance.AddLog($"成功保存了 {savedCount} 张NG图片"); Image_NG_Save.Clear(); }); } //检测结果通过ModBus存入寄存器 try { if (modbusTCPClient._client != null) { if (!IsSignleOutput) { // 将二进制数组转换为ushort ushort value = BinaryToUshort_MSBFirst(usOutput);//1号-16号相机 ushort value2 = BinaryToUshort_MSBFirst(usOutput2);//17-20号相机 // 将ushort存储在ushort数组中 ushort[] test = new ushort[] { value }; ushort[] test2 = new ushort[] { value2 }; modbusTCPClient.MultipleRegistersWrite(3, test);//1号-16号相机 modbusTCPClient.MultipleRegistersWrite(4, test2);//17-20号相机 EnhancedLogViewModel.Instance.AddLog($"检测结果保持{Hold_Time}秒"); if (double.TryParse(Hold_Time, out double interval)) { OutputTimer = new System.Timers.Timer(interval * 1000); } else { MessageBox.Show("保持时间格式错误,请检查配置", "错误", MessageBoxButton.OK, MessageBoxImage.Error); return; } OutputTimer.Elapsed += OnTimedEvent; OutputTimer.AutoReset = false; // 设置为false表示只触发一次 OutputTimer.Start(); } else { if (USOUT) { modbusTCPClient.MultipleRegistersWrite(3, new ushort[] { 1 }); EnhancedLogViewModel.Instance.AddLog($"检测结果保持5秒"); OutputTimer = new System.Timers.Timer(5000); OutputTimer.Elapsed += OnTimedEvent; OutputTimer.AutoReset = false; // 设置为false表示只触发一次 OutputTimer.Start(); } else { modbusTCPClient.MultipleRegistersWrite(3, new ushort[] { 0 }); } } } else if (TcpServerManager._isRunning) { //usOutput_tcp } else { // 获取当前日期 DateTime currentDate = DateTime.Now; // 格式化时间为 "HHmmss" string timeFileName = currentDate.ToString("HH:mm:ss"); this.Dispatcher.BeginInvoke(new Action(() => { LinkState.Text = "未连接"; LinkState.Foreground = System.Windows.Media.Brushes.Red; })); //MessageBox.Show($"ModBus未连接", "错误", MessageBoxButton.OK, MessageBoxImage.Error); GetMessage_Modbus($"{timeFileName} ModBus未连接"); } USOUT = true; } catch (NullReferenceException ex) { MessageBox.Show($"ModBus未连接:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } USOUT = true; } private ImageDisplayWindow _imageDisplayWindow; //NG图像弹窗显示 private void OnDetectionCompleted(List imagePath) { if (imagePath.Count != 0) { // 使用Dispatcher确保UI线程操作 Dispatcher.Invoke(() => { // 检查窗口是否已存在 if (_imageDisplayWindow == null || !_imageDisplayWindow.IsLoaded) { _imageDisplayWindow = new ImageDisplayWindow(); _imageDisplayWindow.Closed += (s, e) => { _imageDisplayWindow = null; }; _imageDisplayWindow.Show(); _imageDisplayWindow.Topmost = true; } // 添加图片到查看器 _imageDisplayWindow.AddImage(imagePath); }); Image_NG.Clear(); } } //图像处理 private void Reasoning_Image(int Sequence) { IntPtr deviceHandle = _deviceHandles[Sequence]; IntPtr imagePtr = _imagePtr[Sequence]; int[] classIds; //目标物类别 int[] numBoxes; //目标物数量 float[] prob; //目标物得分 float[] x1 = new float[100], y1 = new float[100], x2 = new float[100], y2 = new float[100]; if (imagePtr == IntPtr.Zero) { GetMessage($"设备{Sequence}连接已断开"); EnhancedLogViewModel.Instance.AddLog($"设备{Sequence}连接已断开", "ERROR"); return; } long Image_Width = IDImage_Width(imagePtr); long Image_Height = IDImage_Height(imagePtr); long Image_Channels = IDImage_Channels(imagePtr); long dataLength = Image_Width * Image_Height * Image_Channels; byte[] Imagedata = new byte[dataLength]; byte[] bytes = new byte[Image_Width * Image_Height * 3]; IntPtr Image_ImageData = IDImage_ImageData(imagePtr); if (imagePtr == IntPtr.Zero) { MessageBox.Show("图像句柄无效"); } if (dataLength != 0) { Console.WriteLine($"获取日期"); // 获取当前日期 DateTime currentDate = DateTime.Now; // 格式化日期为 "yyyyMMdd" string datePath = currentDate.ToString("yyyyMMdd"); // 格式化时间为 "HHmmss" string timeFileName = currentDate.ToString("HH_mm_ss"); // 构建完整路径 string folderPath = $@"{DownloadData.ImageSavingPath}\{datePath}"; string filePath = Path.Combine(folderPath, $"{timeFileName + "_" + Sequence + DownloadData.ImageFormat_main}"); // 检查文件夹是否存在,如果不存在则创建 if (!Directory.Exists(folderPath)) { Directory.CreateDirectory(folderPath); } Console.WriteLine($"图像句柄转换为字节数据"); Marshal.Copy(Image_ImageData, Imagedata, 0, (int)dataLength); //图像句柄转换为字节数据 ConvertSingletoThreeChannels(detector, Imagedata, (int)Image_Width, (int)Image_Height, bytes); //原图保存至本地 if (DownloadData.ImageSave) { IDImage_SaveImage(imagePtr, filePath, DownloadData.ImageCompressionRatio_main); EnhancedLogViewModel.Instance.AddLog($"设备{Sequence}图像已保存至{filePath}"); } EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:{Sequence}号设备图像预处理完成,开始进行算法推理"); //深度学习推理模型 Reasoning_AI(detector, bytes, (int)Image_Width, (int)Image_Height, out classIds, out numBoxes, out prob, out x1, out y1, out x2, out y2, Sequence); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:{Sequence}号设备算法推理完成,开始进行结果判断"); //结果判断 switch (Sequence) { case 1: int isPass1 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass1; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass1; if (isPass1 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result0.Content = "OK"; finall_result0.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result0.Content = "NG"; finall_result0.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 2: int isPass2 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass2; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass2; if (isPass2 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result1.Content = "OK"; finall_result1.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result1.Content = "NG"; finall_result1.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 3: int isPass3 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass3; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass3; if (isPass3 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result2.Content = "OK"; finall_result2.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result2.Content = "NG"; finall_result2.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 4: int isPass4 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass4; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass4; if (isPass4 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result3.Content = "OK"; finall_result3.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result3.Content = "NG"; finall_result3.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 5: int isPass5 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass5; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass5; if (isPass5 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result4.Content = "OK"; finall_result4.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result4.Content = "NG"; finall_result4.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 6: int isPass6 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass6; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass6; if (isPass6 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result5.Content = "OK"; finall_result5.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); //Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result5.Content = "NG"; finall_result5.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 7: int isPass7 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass7; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass7; if (isPass7 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result6.Content = "OK"; finall_result6.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); //Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result6.Content = "NG"; finall_result6.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 8: int isPass8 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass8; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass8; if (isPass8 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result7.Content = "OK"; finall_result7.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); //Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result7.Content = "NG"; finall_result7.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 9: int isPass9 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass9; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass9; if (isPass9 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result8.Content = "OK"; finall_result8.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); //Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result8.Content = "NG"; finall_result8.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 10: int isPass10 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass10; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass10; if (isPass10 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result9.Content = "OK"; finall_result9.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); //Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result9.Content = "NG"; finall_result9.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 11: int isPass11 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass11; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass11; if (isPass11 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result10.Content = "OK"; finall_result10.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); //Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result10.Content = "NG"; finall_result10.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 12: int isPass12 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass12; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass12; if (isPass12 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result11.Content = "OK"; finall_result11.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); //Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result11.Content = "NG"; finall_result11.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 13: int isPass13 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass13; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass13; if (isPass13 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result12.Content = "OK"; finall_result12.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result12.Content = "NG"; finall_result12.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 14: int isPass14 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass14; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass14; if (isPass14 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result13.Content = "OK"; finall_result13.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result13.Content = "NG"; finall_result13.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 15: int isPass15 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass15; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass15; if (isPass15 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result14.Content = "OK"; finall_result14.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result14.Content = "NG"; finall_result14.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 16: int isPass16 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput[Sequence - 1] = isPass16; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass16; if (isPass16 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result15.Content = "OK"; finall_result15.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result15.Content = "NG"; finall_result15.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 17: int isPass17 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput2[Sequence - 17] = isPass17; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass17; if (isPass17 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result16.Content = "OK"; finall_result16.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result16.Content = "NG"; finall_result16.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 18: int isPass18 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput2[Sequence - 17] = isPass18; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass18; if (isPass18 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result17.Content = "OK"; finall_result17.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result17.Content = "NG"; finall_result17.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 19: int isPass19 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput2[Sequence - 17] = isPass19; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass19; if (isPass19 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result18.Content = "OK"; finall_result18.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result18.Content = "NG"; finall_result18.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; case 20: int isPass20 = config.JudgeDetectionResult(Sequence, numBoxes, classIds, prob); usOutput2[Sequence - 17] = isPass20; if (TcpServerManager._isRunning && usOutput_tcp != null) usOutput_tcp[Sequence - 1] = isPass20; if (isPass20 == 1) { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result19.Content = "OK"; finall_result19.Background = System.Windows.Media.Brushes.Green; })); Console.WriteLine($"点位 {Sequence} 检测通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测通过"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "OK"); }); } else { USOUT = false; this.Dispatcher.BeginInvoke(new Action(() => { Finall_result19.Content = "NG"; finall_result19.Background = System.Windows.Media.Brushes.Red; })); Console.WriteLine($"点位 {Sequence} 检测不通过"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:点位 {Sequence} 检测不通过", "ERROR"); Image_NG.Add($".\\vino_res\\result_{Sequence}.jpg"); Task.Run(() => { // 记录到Excel(批量写入) ExcelResultRecorder.RecordDetectionResult(Sequence, "NG"); }); } break; } } } //图像采集 private void GetIDDevice_CaptureImage(int Sequence) { IntPtr deviceHandle = _deviceHandles[Sequence]; IntPtr imagePtr = _imagePtr[Sequence]; // 开始采集图像 Console.WriteLine($"{Sequence}号设备开始采集图像"); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:{Sequence}号设备开始采集图像"); long rel = IDDevice_IsOpen(deviceHandle); if (rel == 0) { MessageBox.Show($"设备{Sequence}连接已断开", "错误", MessageBoxButton.OK, MessageBoxImage.Error); EnhancedLogViewModel.Instance.AddLog($"设备{Sequence}连接已断开", "ERROR"); _imagePtr[Sequence] = IntPtr.Zero; return; } IDDevice_CaptureImage(deviceHandle, imagePtr); EnhancedLogViewModel.Instance.AddLog($"{DateTime.Now:HH:mm:ss:fff}:{Sequence}号设备图像采集结束,开始进行图像预处理"); } #endregion // 数据转换 static ushort BinaryToUshort_MSBFirst(int[] binaryArray) { if (binaryArray.Length > 16) throw new ArgumentException("Binary array cannot exceed 16 bits."); ushort result = 0; for (int i = 0; i < binaryArray.Length; i++) { if (binaryArray[i] == 1) result |= (ushort)(1 << i); // 低位在前 else if (binaryArray[i] != 0) throw new ArgumentException("Binary array can only contain 0s and 1s."); } return result; } /// /// 自动保存NG图片到指定日期目录 /// 保存路径格式:D:\01 LBImage_NG\(当前日期)\ /// 如果目录不存在会自动创建,支持批量保存操作 /// /// NG图片路径集合 /// 成功保存的图片数量 public int AutoSaveNGImages(List ngImagePaths) { // 校验输入参数 if (ngImagePaths == null || ngImagePaths.Count == 0) { EnhancedLogViewModel.Instance.AddLog($"NG图片路径集合为空,无需保存操作。"); return 0; } int successCount = 0; // 成功保存的计数器 string baseDirectory = @"D:\01 LBImage_NG"; // 基础保存目录 try { // 获取当前系统日期,格式化为yyyyMMdd string dateFolderName = DateTime.Now.ToString("yyyyMMdd"); // 构建完整的目标目录路径 string targetDirectory = Path.Combine(baseDirectory, dateFolderName); // 如果目标目录不存在,则创建 if (!Directory.Exists(targetDirectory)) { Directory.CreateDirectory(targetDirectory); } // 遍历所有NG图片路径 foreach (string sourcePath in ngImagePaths) { try { // 检查源文件是否存在 if (!File.Exists(sourcePath)) { Console.WriteLine($"文件不存在,跳过: {sourcePath}"); continue; } // 获取文件名(包含扩展名) string fileName = Path.GetFileName(sourcePath); // 构建目标文件完整路径 string destinationPath = Path.Combine(targetDirectory, fileName); // 如果目标文件已存在,添加时间戳避免覆盖 if (File.Exists(destinationPath)) { string timeStamp = DateTime.Now.ToString("HHmmss"); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); string extension = Path.GetExtension(fileName); fileName = $"{fileNameWithoutExtension}_{timeStamp}{extension}"; destinationPath = Path.Combine(targetDirectory, fileName); } // 复制文件到目标目录 File.Copy(sourcePath, destinationPath, true); successCount++; Console.WriteLine($"成功保存: {sourcePath} -> {destinationPath}"); } catch (Exception ex) { Console.WriteLine($"保存文件失败 {sourcePath}: {ex.Message}"); } } } catch (Exception ex) { MessageBox.Show($"保存过程中发生错误: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } return successCount; } //默认加载上次检测方案 public bool LoadLastUsedConfiguration(bool bl) { bool isLoad = true; try { if (!string.IsNullOrEmpty(ResultJudge.AppSettings.LastConfigFilePath_Proj) && File.Exists(ResultJudge.AppSettings.LastConfigFilePath_Proj)) { bool isLoadModel; //json = File.ReadAllText(ResultJudge.AppSettings.LastConfigFilePath); var (modelName, configPath, configContent, message) = LBProjService.LoadLBProj(ResultJudge.AppSettings.LastConfigFilePath_Proj); config = JsonConvert.DeserializeObject(configContent); if (bl) { if (detector != IntPtr.Zero) { DisposeDetector_CPU(detector); } Task.Run(() => { // 模型推理引擎选择 detector = CreateDetector_CPU(); isLoadModel = InitializeDetector_CPU(detector, Detector_Device, $".\\model\\{modelName}"); while (true) { if (isLoadModel) { EnhancedLogViewModel.Instance.AddLog($"模型编译成功"); break; } } }); MessageBox.Show($"已自动加载上次使用的工程文件: {ResultJudge.AppSettings.LastConfigFilePath_Proj}", "成功", MessageBoxButton.OK, MessageBoxImage.Information); } this.Dispatcher.BeginInvoke(new Action(() => { ProjectState.Foreground = System.Windows.Media.Brushes.LightGreen; ModelState.Foreground = System.Windows.Media.Brushes.LightGreen; ProjectState.Text = ResultJudge.AppSettings.LastConfigFilePath_Proj; ModelState.Text = modelName; })); EnhancedLogViewModel.Instance.AddLog($"当前工程文件: {ResultJudge.AppSettings.LastConfigFilePath_Proj},模型:{modelName}"); isLoad = true; } else { if (!string.IsNullOrEmpty(ResultJudge.AppSettings.LastConfigFilePath) && File.Exists(ResultJudge.AppSettings.LastConfigFilePath)) { json = File.ReadAllText(ResultJudge.AppSettings.LastConfigFilePath); config = JsonConvert.DeserializeObject(json); if (bl) { MessageBox.Show($"已自动加载上次使用的检测方案: {ResultJudge.AppSettings.LastConfigFilePath}", "成功", MessageBoxButton.OK, MessageBoxImage.Information); } EnhancedLogViewModel.Instance.AddLog($"当前检测方案: {ResultJudge.AppSettings.LastConfigFilePath}"); isLoad = true; } else { MessageBox.Show("没有找到上次使用的检测方案,请手动加载", "提示", MessageBoxButton.OK, MessageBoxImage.Information); EnhancedLogViewModel.Instance.AddLog($"没有找到上次使用的检测方案,请手动加载", "WARN"); isLoad = false; } } } catch (Exception ex) { isLoad = false; MessageBox.Show($"自动加载工程时出错,请手动加载: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } return isLoad; } #endregion #region Modbus Tcp通讯设置 //记录最后一次触发时间 private DateTime _lastTriggerTime = DateTime.MinValue; //触发信号监听 private void ClientListen() { try { int num = 0; while (modbusTCPClient._client == null || modbusTCPClient._client.Connected == false || modbusTCPClient._master == null) { if (_isReconnecting) return; // 如果正在重连,直接返回 modbusTCPClient.ModbudTCP_Connect(_lastIpAddress, _lastPort); if (num++ > 3) { this.Dispatcher.BeginInvoke(new Action(() => { MessageBox.Show("Modbus master 未初始化。无法连接PLC"); })); break; } } while (modbusTCPClient._client != null && modbusTCPClient._client.Connected) //持续监控 { //modbusTCPClient.currentValus_ROIModel = modbusTCPClient.InputRegistersRead(3); while (modbusTCPClient._client != null && modbusTCPClient._client.Connected) { modbusTCPClient.currentValus_trigger = modbusTCPClient.InputRegistersRead(3); if (modbusTCPClient.previousValus_trigger[0] == 0 && modbusTCPClient.currentValus_trigger[0] == 1) { // 检查距离上次触发是否超过5秒 if ((DateTime.Now - _lastTriggerTime).TotalSeconds >= double.Parse(Selflocking_Time)) { _lastTriggerTime = DateTime.Now; modbusTCPClient.istrigger = true; Dispatcher.Invoke(() => { DateTime currentDate = DateTime.Now; string timeFileName = currentDate.ToString("HH:mm:ss"); GetMessage_Modbus($"检测到触发信号:{modbusTCPClient.currentValus_trigger[0]}"); GetMessage_Modbus($"{timeFileName}\n"); }); Console.WriteLine($"检测到触发信号:{modbusTCPClient.currentValus_trigger[0]}"); modbusTCPClient.MultipleRegistersWrite(3, new ushort[] { 0 }); Console.WriteLine($"寄存器置0"); ReSetUI(); Task.Run(() => GetImage()); } else { Console.WriteLine($"忽略触发信号,距离上次触发不足{Selflocking_Time}秒"); modbusTCPClient.MultipleRegistersWrite(3, new ushort[] { 0 }); } } modbusTCPClient.previousValus_trigger = modbusTCPClient.currentValus_trigger; modbusTCPClient.istrigger = false; break; } modbusTCPClient.previousValus_ROIModel = modbusTCPClient.currentValus_ROIModel; } } catch (Exception e) { // 如果正在重连,不显示错误消息 if (!_isReconnecting) { this.Dispatcher.BeginInvoke(new Action(() => { // 不再弹窗,只在日志中记录 GetMessage_Modbus("Modbus连接断开,开始尝试重连...\n"); })); } } } //ModBus心跳信号 private void HeartbeatCallback(object state) { try { // 读取保持寄存器1的值 modbusTCPClient.HoldingRegisterRead(1); Console.WriteLine("心跳信号收发正常"); // 心跳正常,重置重连尝试次数 _reconnectAttempts = 0; } catch (Exception ex) { this.Dispatcher.BeginInvoke(new Action(() => { LinkState.Text = "未连接"; LinkState.Foreground = System.Windows.Media.Brushes.Red; GetMessage_Modbus("检测到Modbus连接异常,准备重连...\n"); })); Console.WriteLine($"Heartbeat failed: {ex.Message}"); // 立即启动重连机制 StartReconnect(); } } // 用户点击确定跟踪标志位 private bool _disconnectAlertShown = false; // 设备连接状态检测 private void DeviceConnectbeatCallback(object state) { try { for (int i = 0; i < DeviceListBox.Items.Count; i++) { IntPtr deviceHandle = _deviceHandles[i + 1]; long rel = IDDevice_IsOpen(deviceHandle); if (rel == 0) { if (!_disconnectAlertShown) { _disconnectAlertShown = true; System.Windows.Application.Current.Dispatcher.Invoke(() => { MessageBox.Show($"设备{i + 1}连接断开,请重新连接", "错误", MessageBoxButton.OK, MessageBoxImage.Error); _disconnectAlertShown = false; // 用户点击确定后重置标志 }); _imagePtr[i + 1] = IntPtr.Zero; // 彻底释放定时器 deviceStatusTimer?.Dispose(); deviceStatusTimer = null; } return; } } } catch (Exception ex) { Console.WriteLine($"设备未按照映射表连接: {ex.Message}"); } } #endregion #region Modbus重连机制 // 初始化重连定时器 private void InitializeReconnectTimer() { // 如果定时器已存在,先释放 _reconnectTimer?.Dispose(); // 创建新的重连定时器 _reconnectTimer = new System.Threading.Timer(ReconnectCallback, null, Timeout.Infinite, RECONNECT_INTERVAL); } // 启动重连 private void StartReconnect() { if (_isReconnecting) return; // 停止心跳定时器 heartbeatTimer?.Change(Timeout.Infinite, Timeout.Infinite); // 启动重连定时器 _reconnectTimer?.Change(0, RECONNECT_INTERVAL); } // 停止重连 private void StopReconnect() { // 停止重连定时器 _reconnectTimer?.Change(Timeout.Infinite, Timeout.Infinite); // 重启心跳定时器 heartbeatTimer?.Change(0, heartbeatInterval); } // 重连回调 private void ReconnectCallback(object state) { if (_isReconnecting) return; _isReconnecting = true; try { // 检查是否超过最大重连次数 if (_reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) { this.Dispatcher.BeginInvoke(new Action(() => { GetMessage_Modbus($"重连失败,已达到最大重连次数 ({MAX_RECONNECT_ATTEMPTS})\n"); // 只有超过最大重连次数时才弹窗 MessageBox.Show("Modbus重连失败,已达到最大重连次数", "错误", MessageBoxButton.OK, MessageBoxImage.Error); })); // 停止重连定时器 _reconnectTimer?.Change(Timeout.Infinite, Timeout.Infinite); return; } _reconnectAttempts++; this.Dispatcher.BeginInvoke(new Action(() => { GetMessage_Modbus($"尝试第 {_reconnectAttempts} 次重连...\n"); })); // 尝试重连 bool reconnectSuccess = modbusTCPClient.Reconnect(_lastIpAddress, _lastPort); if (reconnectSuccess) { // 重连成功 _reconnectAttempts = 0; _isReconnecting = false; this.Dispatcher.BeginInvoke(new Action(() => { GetMessage_Modbus("重连成功\n"); LinkState.Text = "ModBus已连接"; LinkState.Foreground = System.Windows.Media.Brushes.LightGreen; })); // 停止重连,恢复心跳 StopReconnect(); // 重新启动监听任务 Task.Run(() => ClientListen()); } else { // 重连失败,继续尝试,不在UI上弹窗,只在日志中记录 this.Dispatcher.BeginInvoke(new Action(() => { GetMessage_Modbus($"第 {_reconnectAttempts} 次重连失败\n"); })); _isReconnecting = false; } } catch (Exception ex) { this.Dispatcher.BeginInvoke(new Action(() => { GetMessage_Modbus($"重连过程中发生错误: {ex.Message}\n"); })); _isReconnecting = false; } } #endregion // 通讯测试,debug用 private void Write_Click(object sender, RoutedEventArgs e) { EnhancedLogViewModel.Instance.AddLog("开始ModBus通讯测试,所有寄存器写入高电平2s后置零", "DEBUG"); if (modbusTCPClient._client == null) { this.Dispatcher.BeginInvoke(new Action(() => { LinkState.Text = "未连接"; LinkState.Foreground = System.Windows.Media.Brushes.Red; })); MessageBox.Show("连接到服务器失败,请检查连接后重试"); return; } //ushort[] test = new ushort[] { 0x8001 }; //modbusTCPClient.MultipleRegistersWrite(3, test); //outputs[0] = true; //outputs[15] = true; //ushort value = BoolsToUshort(outputs); // 假设有一个包含16个0和1的数组 int[] binaryDigits = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; int[] binaryDigits2 = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; // 将二进制数组转换为ushort ushort value = BinaryToUshort_MSBFirst(binaryDigits); ushort value2 = BinaryToUshort_MSBFirst(binaryDigits2); // 将ushort存储在ushort数组中 ushort[] test = new ushort[] { value }; ushort[] test2 = new ushort[] { value2 }; modbusTCPClient.MultipleRegistersWrite(3, test); modbusTCPClient.MultipleRegistersWrite(4, test2); Thread.Sleep(2000); modbusTCPClient.MultipleRegistersWrite(3, new ushort[] { 0 }); modbusTCPClient.MultipleRegistersWrite(4, new ushort[] { 0 }); } private void OnTimedEvent(object source, ElapsedEventArgs e) { // 定时器触发时执行的代码 modbusTCPClient.MultipleRegistersWrite(3, new ushort[] { 0 }); modbusTCPClient.MultipleRegistersWrite(4, new ushort[] { 0 }); EnhancedLogViewModel.Instance.AddLog($"Modbus已置零"); Console.WriteLine("Modbus已置零"); } // 手动采集图像 private void Read_Click(object sender, RoutedEventArgs e) { EnhancedLogViewModel.Instance.AddLog("开始手动采集图像", "DEBUG"); ReSetUI(); Task.Run(() => { GetImage(); }); } private void ReSetUI() { this.Dispatcher.BeginInvoke(new Action(() => { Finall_result0.Content = ""; finall_result0.Background = System.Windows.Media.Brushes.Gray; Finall_result1.Content = ""; finall_result1.Background = System.Windows.Media.Brushes.Gray; Finall_result2.Content = ""; finall_result2.Background = System.Windows.Media.Brushes.Gray; Finall_result3.Content = ""; finall_result3.Background = System.Windows.Media.Brushes.Gray; Finall_result4.Content = ""; finall_result4.Background = System.Windows.Media.Brushes.Gray; Finall_result5.Content = ""; finall_result5.Background = System.Windows.Media.Brushes.Gray; Finall_result6.Content = ""; finall_result6.Background = System.Windows.Media.Brushes.Gray; Finall_result7.Content = ""; finall_result7.Background = System.Windows.Media.Brushes.Gray; Finall_result8.Content = ""; finall_result8.Background = System.Windows.Media.Brushes.Gray; Finall_result9.Content = ""; finall_result9.Background = System.Windows.Media.Brushes.Gray; Finall_result10.Content = ""; finall_result10.Background = System.Windows.Media.Brushes.Gray; Finall_result11.Content = ""; finall_result11.Background = System.Windows.Media.Brushes.Gray; Finall_result12.Content = ""; finall_result12.Background = System.Windows.Media.Brushes.Gray; Finall_result13.Content = ""; finall_result13.Background = System.Windows.Media.Brushes.Gray; Finall_result14.Content = ""; finall_result14.Background = System.Windows.Media.Brushes.Gray; Finall_result15.Content = ""; finall_result15.Background = System.Windows.Media.Brushes.Gray; Finall_result16.Content = ""; finall_result16.Background = System.Windows.Media.Brushes.Gray; Finall_result17.Content = ""; finall_result17.Background = System.Windows.Media.Brushes.Gray; Finall_result18.Content = ""; finall_result18.Background = System.Windows.Media.Brushes.Gray; Finall_result19.Content = ""; finall_result19.Background = System.Windows.Media.Brushes.Gray; Finall_result20.Content = ""; finall_result20.Background = System.Windows.Media.Brushes.Gray; Finall_result21.Content = ""; finall_result21.Background = System.Windows.Media.Brushes.Gray; Finall_result22.Content = ""; finall_result22.Background = System.Windows.Media.Brushes.Gray; Finall_result23.Content = ""; finall_result23.Background = System.Windows.Media.Brushes.Gray; })); } private void ReSet_Click(object sender, RoutedEventArgs e) { ReSetUI(); } //设备管理 SmartScanner.ProductManager productManager = null; private void ProductMaanager_Btn_Click(object sender, RoutedEventArgs e) { var loginWindow = new LoginWindow(); if (loginWindow.ShowDialog() == true) { if (productManager == null || !productManager.IsLoaded) { productManager = new ProductManager(); productManager.Closed += (s, args) => { productManager = null; }; productManager.SetControlsEnabled(UserManager.HasAdministratorPrivilege()); productManager.ShowDialog(); } else { if (productManager.WindowState == WindowState.Minimized) { productManager.WindowState = WindowState.Normal; // 恢复窗口状态 } productManager.Activate(); // 如果窗口已存在,则激活它 } } } //检测方案 SmartScanner.ResultJudge resultJudge = null; private void ResultJudge_Btn_Click(object sender, RoutedEventArgs e) { var loginWindow = new LoginWindow(); if (loginWindow.ShowDialog() == true) { if (resultJudge == null || !resultJudge.IsLoaded) { resultJudge = new ResultJudge(); resultJudge.Closed += (s, args) => { resultJudge = null; }; resultJudge.SetControlsEnabled(UserManager.HasAdministratorPrivilege()); resultJudge.ShowDialog(); } else { if (resultJudge.WindowState == WindowState.Minimized) { resultJudge.WindowState = WindowState.Normal; // 恢复窗口状态 } resultJudge.Activate(); // 如果窗口已存在,则激活它 } } } #region 输出模式选择 private void OutputCheckBox_Checked(object sender, RoutedEventArgs e) { IsSignleOutput = true; // 记录日志 OperateLogService.LogOperation( "结果输出模式", $"修改为: 单点输出", null); } private void OutputCheckBox_Unchecked(object sender, RoutedEventArgs e) { IsSignleOutput = false; // 记录日志 OperateLogService.LogOperation( "结果输出模式", $"修改为: 多点输出", null); } #endregion // 图像保存设置 SmartScanner.DownloadData downloadData = null; private void ImageSetup_Btn_Click(object sender, RoutedEventArgs e) { var loginWindow = new LoginWindow(); if (loginWindow.ShowDialog() == true) { if (downloadData == null || !downloadData.IsLoaded) { downloadData = new DownloadData(); downloadData.Closed += (s, args) => { downloadData = null; }; downloadData.SetControlsEnabled(UserManager.HasAdministratorPrivilege()); downloadData.ShowDialog(); } else { if (downloadData.WindowState == WindowState.Minimized) { downloadData.WindowState = WindowState.Normal; // 恢复窗口状态 } downloadData.Activate(); // 如果窗口已存在,则激活它 } } } private void AddTestLog_Click(object sender, RoutedEventArgs e) { EnhancedLogViewModel.Instance.AddLog("这是一条普通信息"); EnhancedLogViewModel.Instance.AddLog("这是一条警告信息", "WARN"); EnhancedLogViewModel.Instance.AddLog("这是一条错误信息", "ERROR"); EnhancedLogViewModel.Instance.AddLog("这是一条调试信息", "DEBUG"); } private void TCPClient_Connect_Click(object sender, RoutedEventArgs e) { } private void TCPClient_ClearClientLogs_Click(object sender, RoutedEventArgs e) { } private void btnViewLogs_Click(object sender, RoutedEventArgs e) { var operateLogView = new OperateLogView(); operateLogView.ShowDialog(); } private void btnViewReports_Click(object sender, RoutedEventArgs e) { ExcelResultRecorder.OpenReportFolder(); } private async void RestartButton_Click(object sender, RoutedEventArgs e) { if (MessageBox.Show("确定要重启应用程序吗?", "确认重启", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) { Mouse.OverrideCursor = Cursors.Wait; try { await AppRestartHelper.RestartAsync(); } finally { Mouse.OverrideCursor = null; } } } #region 界面图片显示按钮 private ImageDisplayWindow displayWindow; private void finall_result0_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_1.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_2.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result2_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_3.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result3_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_4.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result4_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_5.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result5_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_6.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result6_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_7.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result7_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_8.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result8_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_9.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result9_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_10.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result10_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_11.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result11_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_12.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result12_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_13.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result13_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_14.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result14_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_15.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result15_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_16.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result16_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_17.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result17_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_18.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result18_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_19.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result19_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_20.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result20_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_21.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result21_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_22.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result22_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_23.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } private void finall_result23_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { displayWindow = new ImageDisplayWindow(); bool isShow = displayWindow.ShowImage_main(".\\vino_res\\result_24.jpg"); if (isShow) { displayWindow.Show(); } else { return; } } #endregion #region TCP/IP通讯模块 private void TCPServer_Start_Click(object sender, RoutedEventArgs e) { try { var ip = GetSelectedIP(); var port = GetSelectedPort(); var enableHeartbeat = TCPServer_EnableHeartbeat.IsChecked ?? false; var heartbeatCommand = TCPServer_HeartbeatCommand.Text.Trim(); var heartbeatInterval = int.Parse(TCPServer_HeartbeatInterval.Text); var outputIOCount = DeviceListBox.Items.Count; // 验证心跳设置 if (enableHeartbeat) { if (string.IsNullOrWhiteSpace(heartbeatCommand)) { MessageBox.Show("请输入有效的心跳指令"); return; } if (heartbeatInterval <= 0) { MessageBox.Show("心跳间隔必须大于0"); return; } } _tcpServerManager = new TcpServerManager( enableHeartbeat, heartbeatCommand, heartbeatInterval, GetImageAndProcess, outputIOCount); _tcpServerManager.LogMessage += msg => Dispatcher.Invoke(() => { TCPServer_LogsTextBox.AppendText($"{DateTime.Now:HH:mm:ss:fff} {msg}\n"); TCPServer_LogsTextBox.ScrollToEnd(); }); _ = _tcpServerManager.StartAsync(ip, port); TCPServer_Start.IsEnabled = false; TCPServer_Stop.IsEnabled = true; LinkState.Text = "TCP已启动"; LinkState.Foreground = System.Windows.Media.Brushes.LightGreen; } catch (Exception ex) { MessageBox.Show($"启动服务器失败: {ex.Message}"); } } private async Task GetImageAndProcess() { try { usOutput_tcp = new int[DeviceListBox.Items.Count]; return await Task.Run(() => { GetImage(); return usOutput_tcp; // 返回结果 }).ConfigureAwait(false); } catch (Exception ex) { MessageBox.Show($"图像处理异常: {ex.Message}"); throw; // 重新抛出以触发TCP层的错误处理 } } private void TCPServer_Stop_Click(object sender, RoutedEventArgs e) { _tcpServerManager?.Stop(); TCPServer_Start.IsEnabled = true; TCPServer_Stop.IsEnabled = false; LinkState.Text = "未连接"; LinkState.Foreground = System.Windows.Media.Brushes.Red; } private void TCPServer_EnableHeartbeat_Checked(object sender, RoutedEventArgs e) { TCPServer_HeartbeatPanel.Visibility = Visibility.Visible; } private void TCPServer_EnableHeartbeat_Unchecked(object sender, RoutedEventArgs e) { TCPServer_HeartbeatPanel.Visibility = Visibility.Collapsed; } private void TCPServer_ClearLogs_Click(object sender, RoutedEventArgs e) { Dispatcher.Invoke(() => TCPServer_LogsTextBox.Clear()); } private void RefreshIpButton_Click(object sender, RoutedEventArgs e) { TCPServer_IpComboBox.Items.Clear(); // 添加默认选项 TCPServer_IpComboBox.Items.Add(new ComboBoxItem { Content = "0.0.0.0 (All Interfaces)" }); TCPServer_IpComboBox.Items.Add(new ComboBoxItem { Content = "127.0.0.1 (Loopback)" }); // 添加实际IP地址 foreach (var ip in GetAvailableIPs()) { var item = new ComboBoxItem { Content = $"{ip} ({GetNetworkInterfaceName(ip)})", Tag = ip // 存储实际IP对象 }; TCPServer_IpComboBox.Items.Add(item); } TCPServer_IpComboBox.SelectedIndex = 0; } private string GetNetworkInterfaceName(IPAddress ip) { foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { foreach (UnicastIPAddressInformation addr in nic.GetIPProperties().UnicastAddresses) { if (addr.Address.Equals(ip)) { return nic.Name; } } } return "Unknown"; } private List GetAvailableIPs() { var ips = new List(); // 获取所有活动的网络接口 foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces()) { // 只获取已启用且非虚拟的接口 if (nic.OperationalStatus != OperationalStatus.Up || nic.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue; // 获取IPv4地址 foreach (UnicastIPAddressInformation ip in nic.GetIPProperties().UnicastAddresses) { if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { ips.Add(ip.Address); } } } return ips; } /// /// 获取当前选择的IP地址字符串 /// /// /// "0.0.0.0" - 所有接口 /// "127.0.0.1" - 环回地址 /// "192.168.1.100" - 具体IP /// public string GetSelectedIP() { // 情况1:用户手动输入(IsEditable=true时) if (!string.IsNullOrWhiteSpace(TCPServer_IpComboBox.Text)) { if (IsValidIP(TCPServer_IpComboBox.Text)) return TCPServer_IpComboBox.Text.Trim(); } // 情况2:从下拉项选择 if (TCPServer_IpComboBox.SelectedItem is ComboBoxItem item) { // 处理特殊选项 if (item.Content.ToString().Contains("0.0.0.0")) return "0.0.0.0"; if (item.Content.ToString().Contains("127.0.0.1")) return "127.0.0.1"; // 从内容中提取IP部分(格式:"IP (描述)") var ipPart = item.Content.ToString().Split().FirstOrDefault(); if (IsValidIP(ipPart)) return ipPart; } // 默认返回"0.0.0.0" return "0.0.0.0"; } // IP地址验证方法 private bool IsValidIP(string ipString) { return IPAddress.TryParse(ipString, out _); } /// /// 安全获取选择的端口号 /// /// /// 成功返回有效端口(1-65535),失败返回-1并显示错误提示 /// public int GetSelectedPort() { try { // 情况1:从下拉项选择 if (TCPServer_PortComboBox.SelectedItem is ComboBoxItem selectedItem) { if (int.TryParse(selectedItem.Content.ToString(), out int port) && IsValidPort(port)) return port; } // 情况2:手动输入文本 if (!string.IsNullOrWhiteSpace(TCPServer_PortComboBox.Text)) { if (int.TryParse(TCPServer_PortComboBox.Text, out int manualPort) && IsValidPort(manualPort)) return manualPort; } throw new ArgumentException("未选择有效端口"); } catch (Exception ex) { ShowPortError($"端口获取失败: {ex.Message}"); return -1; } } // 端口范围验证 private bool IsValidPort(int port) => port > 0 && port <= 65535; // 错误提示方法 private void ShowPortError(string message) { MessageBox.Show(message, "端口配置错误", MessageBoxButton.OK, MessageBoxImage.Error); } #endregion #region 工程方案设置模块按钮事件 private void BtnSelectParam_Click(object sender, RoutedEventArgs e) { RefreshDetectionPlanList(); if (cmbDetectionPlans.SelectedItem == null) { MessageBox.Show("请先选择检测方案", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } string selectedPlan = ((dynamic)cmbDetectionPlans.SelectedItem).FileName; MessageBox.Show($"已选择检测方案: {selectedPlan}", "检测方案确认", MessageBoxButton.OK, MessageBoxImage.Information); } /// /// 加载检测方案列表 /// private void RefreshDetectionPlanList() { var plans = GetAvailableDetectionPlans() .Select(name => new { FileName = name, FullPath = Path.Combine("DetectionPlan", name) }).ToList(); cmbDetectionPlans.ItemsSource = plans; if (plans.Any()) cmbDetectionPlans.SelectedIndex = 0; } /// /// 获取所有可用检测方案 /// private string[] GetAvailableDetectionPlans() { string planPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DetectionPlan"); if (!Directory.Exists(planPath)) { Directory.CreateDirectory(planPath); return Array.Empty(); } return Directory.GetFiles(planPath, "*.json") .Select(Path.GetFileName) .ToArray(); } private void BtnConfirmModel_Click(object sender, RoutedEventArgs e) { if (cmbModels.SelectedItem == null) { MessageBox.Show("请先选择模型文件", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } string selectedModel = ((dynamic)cmbModels.SelectedItem).FileName; MessageBox.Show($"已选择模型: {selectedModel}", "模型确认", MessageBoxButton.OK, MessageBoxImage.Information); } /// /// 加载模型列表 /// private void RefreshModelList() { var models = LBProjService.GetAvailableModels() .Select(name => new { FileName = name, FullPath = Path.Combine("model", name) }).ToList(); cmbModels.ItemsSource = models; if (models.Any()) cmbModels.SelectedIndex = 0; } private void BtnExport_Click(object sender, RoutedEventArgs e) { if (cmbModels.SelectedItem == null) { MessageBox.Show("请先点击【确定】按钮选择模型", "警告", MessageBoxButton.OK, MessageBoxImage.Warning); return; } // 固定导出路径 string projectDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Project"); if (!Directory.Exists(projectDir)) { Directory.CreateDirectory(projectDir); } var saveDialog = new System.Windows.Forms.SaveFileDialog { Title = "导出 LBProj 工程", Filter = "LBProj文件|*.lbproj", FileName = $"project_{DateTime.Now:yyyyMMdd_HHmmss}", InitialDirectory = projectDir }; if (saveDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { string modelName = ((dynamic)cmbModels.SelectedItem).FileName; // 使用固定路径的检测方案文件 string detectionPlanPath = cmbDetectionPlans.SelectedItem != null ? Path.Combine("DetectionPlan", ((dynamic)cmbDetectionPlans.SelectedItem).FileName) : ""; var (success, message) = LBProjService.CreateLBProj( detectionPlanPath, modelName, saveDialog.FileName); if (success) { Properties.Settings.Default.LastConfigFilePath_Proj = saveDialog.FileName; Properties.Settings.Default.Save(); MessageBox.Show($"工程已成功保存到: {saveDialog.FileName}", "成功", MessageBoxButton.OK, MessageBoxImage.Information); OperateLogService.LogOperation("工程文件修改", $"工程文件修改为: {saveDialog.FileName}", null); //txtExportPath.Text = saveDialog.FileName; } else { MessageBox.Show(message, "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } } private void BtnLoad_Click(object sender, RoutedEventArgs e) { //RefreshProjectList(); if (cmbProjects.SelectedItem == null) { MessageBox.Show("请先选择工程文件", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } string selectedProject = ((dynamic)cmbProjects.SelectedItem).FullPath; var (modelName, configPath, configContent, message) = LBProjService.LoadLBProj(selectedProject); config = JsonConvert.DeserializeObject(configContent); if (modelName != null) { // 更新模型选择 foreach (var item in cmbModels.Items) { if (((dynamic)item).FileName == modelName) { cmbModels.SelectedItem = item; break; } } if (detector != IntPtr.Zero) { DisposeDetector_CPU(detector); } Task.Run(() => { // 模型推理引擎选择 detector = CreateDetector_CPU(); bool isLoadModel = InitializeDetector_CPU(detector, Detector_Device, $".\\model\\{modelName}"); while (true) { if (isLoadModel) { EnhancedLogViewModel.Instance.AddLog($"模型编译成功"); break; } } }); Properties.Settings.Default.LastConfigFilePath_Proj = selectedProject; Properties.Settings.Default.Save(); //txtParamPath.Text = configPath; //cmbModels.ItemsSource = modelName.ToString(); MessageBox.Show($"已加载模型: {modelName}\n{message}", "成功", MessageBoxButton.OK, MessageBoxImage.Information); this.Dispatcher.BeginInvoke(new Action(() => { ProjectState.Foreground = System.Windows.Media.Brushes.LightGreen; ModelState.Foreground = System.Windows.Media.Brushes.LightGreen; ProjectState.Text = selectedProject; ModelState.Text = modelName; })); EnhancedLogViewModel.Instance.AddLog($"已加载工程文件: {selectedProject},模型:{modelName}"); } else { MessageBox.Show(message, "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 加载工程文件列表 /// private void RefreshProjectList() { var projects = GetAvailableProjects() .Select(name => new { FileName = name, FullPath = Path.Combine("Project", name) }).ToList(); cmbProjects.ItemsSource = projects; if (projects.Any()) cmbProjects.SelectedIndex = 0; } /// /// 获取所有可用工程文件 /// private string[] GetAvailableProjects() { string projectPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Project"); if (!Directory.Exists(projectPath)) { Directory.CreateDirectory(projectPath); return Array.Empty(); } return Directory.GetFiles(projectPath, "*.lbproj") .Select(Path.GetFileName) .ToArray(); } private void BtnRefreshModels_Click(object sender, RoutedEventArgs e) { RefreshModelList(); RefreshDetectionPlanList(); RefreshProjectList(); MessageBox.Show("模型、检测方案和工程文件列表已刷新", "提示", MessageBoxButton.OK, MessageBoxImage.Information); } #endregion #region 版本说明功能 private void VersionButton_Click(object sender, RoutedEventArgs e) { ShowVersionInfo(); } private void ShowVersionInfo() { try { // 读取版本说明文件 string versionFile = "版本说明.txt"; if (File.Exists(versionFile)) { string versionContent = File.ReadAllText(versionFile); // 解析版本说明文件,提取最新版本信息 var versionLines = versionContent.Split('\n'); string latestVersion = ""; string latestDate = ""; string latestChanges = ""; // 从最后一行向前查找最新的版本信息 for (int i = versionLines.Length - 1; i >= 0; i--) { if (versionLines[i].Contains("v") && versionLines[i].Contains(".")) { // 提取版本日期和版本号 var parts = versionLines[i].Trim().Split(' '); foreach (var part in parts) { if (part.Contains(".") && part.Length > 0) { if (part.StartsWith("v") || part.Contains("v")) { latestVersion = part.Trim(); } else if (char.IsDigit(part[0])) { latestDate = part.Trim(); } } } // 查找该版本的更新内容 for (int j = i + 1; j < versionLines.Length; j++) { string contentLine = versionLines[j].Trim(); // 停止条件:空行或新的版本行 if (string.IsNullOrEmpty(contentLine) || (contentLine.Contains("v") && contentLine.Contains(".") && contentLine.Length > 8)) { break; } // 添加所有非空内容行 if (contentLine.Length > 0) { latestChanges += contentLine + "\n"; } // 最多读取8行内容 if (j - i > 8) { break; } } // 显示版本信息对话框 string message = $"当前软件版本: {latestVersion}\n" + $"发布日期: {latestDate}\n\n" + $"最新更新内容:\n{latestChanges}\n" + $"完整版本历史记录请查看版本说明.txt文件"; MessageBox.Show(message, "软件版本说明", MessageBoxButton.OK, MessageBoxImage.Information); return; } } // 如果没有找到版本信息,显示默认提示 MessageBox.Show("版本说明文件中未找到有效的版本信息", "提示", MessageBoxButton.OK, MessageBoxImage.Information); } else { MessageBox.Show("版本说明文件不存在", "错误", MessageBoxButton.OK, MessageBoxImage.Warning); } } catch (Exception ex) { MessageBox.Show($"读取版本信息时出错: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } #endregion } }