C3032
2026-01-08 116ed6b584bbdb40c5b65e7cb57e039b6ae57800
优化相机配置和图像获取逻辑

在 `2DCameraForm.cs` 中添加了对相机品牌的检查,确保只有在品牌不是 `LBCamera` 时才设置触发模式并开始抓取图像。
在 `LBCamera.cs` 中引入了多个私有字段以存储图像处理相关的句柄和数据,并移除了未实现的方法 `GetCamConfig` 和 `GetExpouseTime`,添加了新的实现以支持相机配置和曝光时间的获取。
新增了相机信息查找逻辑,通过序列号或 IP 地址匹配相机。
修改了图像获取逻辑,使用 `RetrieveDataAndGenerateImage` 方法处理图像数据,并确保线程安全。
添加了 `StartSingleGrab` 和 `StartContinuousGrab` 方法以支持线扫相机的不同采集模式。
移除了未实现的 `GetImage` 和 `GetImageWithSoftTrigger` 方法,并提供了新的实现。
更新了 `OnAcquisitionCompleted` 方法以兼容不同的采集完成状态。
添加了多个参数设置和获取的方法,支持曝光时间、增益、触发模式等配置选项。
整体简化了类的结构,提高了代码的可读性和可维护性。
增强通讯器功能,改善用户界面和数据库支持

在多个文件中进行了重要更改,包括:
- 添加了 `CsvRecordProductData` 类以支持CSV映射。
- 引入 `LB_SmartVision.SQL` 以支持数据库操作。
- 更新了 `CommunicatorForm` 的事件处理和控件可见性逻辑。
- 修改了连接通讯器的逻辑,确保连接成功后启用相关按钮。
- 重构了相机类方法以支持不同类型的相机实现。
- 更新了界面布局和控件属性,以提升用户体验。
- 添加了数据库连接和初始化逻辑,支持数据记录功能。

这些更改旨在提升系统的功能性和用户友好性。
已重命名1个文件
已添加5个文件
已修改21个文件
已删除1个文件
2602 ■■■■■ 文件已修改
LB_SmartVision/CSV/CsvRecordProductData.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/Forms/CreateProductForm.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/Forms/Pages/CommunicatorPage/CommunicatorForm.Designer.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/Forms/Pages/CommunicatorPage/CommunicatorForm.cs 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/Forms/Pages/CommunicatorPage/CommunicatorsEditPage.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/Forms/Pages/CommunicatorPage/CreateCommunicatorForm.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/LB_SmartVision.csproj 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/LB_SqlCommand.cs 573 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/ProcessRun/ProcessContext.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/SQL/DatabaseRecordProductDataHelper.cs 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/SQL/LB_Collections.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/SQL/LB_SqlCommand.cs 552 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/SQL/RecordProductData.cs 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/SQL/RecordProductDataRepository.cs 419 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/VisionForm.Designer.cs 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/VisionForm.cs 171 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVisionCommon/UserData.cs 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionFlowNode/IFlowContext.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/2DCameraForm.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/BaseCamera.cs 126 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/HRCameras/HRCamera.cs 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/ICamera.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/LBCameras/LBCamera.cs 211 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Communicators/CommunicatorConfig.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Communicators/CommunicatorForm.Designer.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Communicators/CommunicatorForm.cs 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Communicators/CommunicatorForm.resx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Communicators/SiemensS7/SiemensLBS7.cs 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/CSV/CsvRecordProductData.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
using LB_SmartVisionCommon;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LB_SmartVision.CSV
{
    /// <summary>
    /// ç”¨äºŽCSV映射的类
    /// </summary>
    public class CsvRecordProductData
    {
        /// <summary>
        /// SKU  ç‰©æ–™å·æˆ–产品名称
        /// </summary>
        public string ProductName { get; set; }
        /// <summary>
        /// äº§å“SN号
        /// </summary>
        public string ProductSN { get; set; }
        /// <summary>
        /// æ£€æµ‹ä½œä¸šå‘˜
        /// </summary>
        public string InspectionOperator { get; set; }
        /// <summary>
        /// NG类型
        /// </summary>
        public string NGType { get; set; }
        /// <summary>
        /// NG大小
        /// </summary>
        public string NGSize { get; set; }
        /// <summary>
        /// æ£€æµ‹æ—¶é—´
        /// </summary>
        public string DetectionTime { get; set; }
        /// <summary>
        /// æ£€æµ‹ç›¸æœº
        /// </summary>
        public string CameraInspection { get; set; }
    }
}
LB_SmartVision/Forms/CreateProductForm.cs
@@ -1,4 +1,5 @@

using LB_SmartVision.SQL;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using ReaLTaiizor.Forms;
LB_SmartVision/Forms/Pages/CommunicatorPage/CommunicatorForm.Designer.cs
@@ -150,6 +150,7 @@
            txtAddress.Name = "txtAddress";
            txtAddress.Size = new Size(100, 21);
            txtAddress.TabIndex = 9;
            txtAddress.TextChanged += txtAddress_TextChanged;
            // 
            // cmbType
            // 
LB_SmartVision/Forms/Pages/CommunicatorPage/CommunicatorForm.cs
@@ -3,13 +3,26 @@
using LB_VisionProcesses.Communicators.TCom;
using ReaLTaiizor.Forms;
using RJCP.IO.Ports;
using S7.Net;
using System.Diagnostics;
using System.Linq;
namespace LB_SmartVision.Forms.Pages.CommunicatorPage
{
    public delegate void CommunicatorChange(BaseCommunicator communicator);
    public partial class CommunicatorForm : Form
    {
        BaseCommunicator communicator { get; set; }
        public event CommunicatorChange CommunicatorChanged;
        private void communicatorChanged(BaseCommunicator communicator)
        {
            if (CommunicatorChanged != null)
            {
                CommunicatorChanged(communicator);
            }
        }
        public CommunicatorForm()
        {
@@ -54,6 +67,9 @@
                txtIP.Visible = false;
                cmbIP.Visible = true;
                this.btnRuleSend.Visible = true;
                lblType.Visible = false;
                lblAddress.Visible = false;
                cmbType.Visible = false;
@@ -68,6 +84,9 @@
                txtPort.SelectedText = communicator.CommunicatorConnections["端口"]?.ToString();
                txtIP.Visible = true;
                cmbIP.Visible = false;
                this.btnRuleSend.Visible = false;
                lblType.Visible = false;
                lblAddress.Visible = false;
@@ -86,10 +105,15 @@
                if (!string.IsNullOrEmpty(communicator.CommunicatorConnections["型号"]?.ToString()))
                {
                    cmbType.Items.Add(communicator.CommunicatorConnections["型号"]?.ToString());
                    cmbType.Text = communicator.CommunicatorConnections["型号"]?.ToString();
                }
                cmbType.Text = communicator.CommunicatorConnections["型号"]?.ToString();
                if (!string.IsNullOrEmpty(communicator.CommunicatorConnections["变量地址"]?.ToString()))
                {
                    txtAddress.Text = communicator.CommunicatorConnections["变量地址"]?.ToString();
                }
                txtIP.Visible = true;
                cmbIP.Visible = false;
                this.btnRuleSend.Visible = false;
                lblType.Visible = true;
                lblAddress.Visible = true;
@@ -115,6 +139,14 @@
                    return;
                //取消回调函数
                communicator.MessageReceived -= ShowReceiveMsg;
                if (communicator != null)
                {
                    communicatorChanged(communicator);
                    if (GlobalVar.dicCommunicators != null && GlobalVar.dicCommunicators.Keys.Count > 0 && GlobalVar.dicCommunicators.Keys.Contains(communicator.CommunicatorName))
                    {
                        GlobalVar.dicCommunicators[communicator.CommunicatorName] = communicator;
                    }
                }
            }
            catch (Exception ex)
            {
@@ -222,5 +254,18 @@
            if (communicator.SendMessage(SendMsg))
                ShowSendMsg(SendMsg);
        }
        private void txtAddress_TextChanged(object sender, EventArgs e)
        {
            if (communicator != null && !communicator.CommunicatorConnections.Contains("变量地址"))
            {
                communicator.CommunicatorConnections.Add("变量地址", txtAddress.Text);
            }
            else if (communicator != null)
            {
                communicator.CommunicatorConnections["变量地址"] = txtAddress.Text;
                communicatorChanged(communicator);
            }
        }
    }
}
LB_SmartVision/Forms/Pages/CommunicatorPage/CommunicatorsEditPage.cs
@@ -126,12 +126,18 @@
                if (GlobalVar.dicCommunicators.ContainsKey(name))
                {
                    CommunicatorForm cameraForm = new CommunicatorForm(GlobalVar.dicCommunicators[name], name);
                    cameraForm.Show();
                    CommunicatorForm communicatorFForm = new CommunicatorForm(GlobalVar.dicCommunicators[name], name);
                    communicatorFForm.CommunicatorChanged += CommunicatorFForm_CommunicatorChanged;
                    communicatorFForm.Show();
                }
            }
        }
        private void CommunicatorFForm_CommunicatorChanged(BaseCommunicator communicator)
        {
        }
        // ç§»é™¤é€šè®¯å£
        private void DeleteEvent(string Name, string Text)
        {
LB_SmartVision/Forms/Pages/CommunicatorPage/CreateCommunicatorForm.cs
@@ -131,7 +131,7 @@
                MessageBox.Show("未选择通讯类型!", "异常");
                return;
            }
            if (!communicator.Connect())
            if (communicator.Connect())
            {
                uiButtonCreate.Enabled = true;
                communicator.Disconnect();
@@ -146,7 +146,7 @@
        private void uiButtonCreate_Click(object sender, EventArgs e)
        {
            if (!communicator.Connect())
            if (communicator.Connect())
            {
                bCreate = true;
                this.Close();
LB_SmartVision/LB_SmartVision.csproj
@@ -14,6 +14,7 @@
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\LB_SmartVisionCommon\LB_SmartVisionCommon.csproj" />
    <ProjectReference Include="..\LB_SmartVisionLoginUI\LB_SmartVisionLoginUI.csproj" />
    <ProjectReference Include="..\LB_VisionControl\LB_VisionControl.csproj" />
    <ProjectReference Include="..\LB_VisionFlowNode\LB_VisionFlowNode.csproj" />
LB_SmartVision/LB_SqlCommand.cs
ÎļþÒÑɾ³ý
LB_SmartVision/ProcessRun/ProcessContext.cs
@@ -573,6 +573,8 @@
        [Node("Halcon2D斑点工具", "Haclon2D工具", "Basic", "Halcon2D斑点工具")]
        public void Halcon2D斑点工具(FlowNode node) { RunNodeAsync(node); }
        [Node("通讯模块", "通讯工具", "Basic", "通讯模块")]
        public void é€šè®¯æ¨¡å—(FlowNode node) { RunNodeAsync(node); }
        #endregion
LB_SmartVision/SQL/DatabaseRecordProductDataHelper.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
using LB_SmartVisionCommon;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LB_SmartVision.SQL
{
    public class DatabaseRecordProductDataHelper
    {
        private static string connectionString = "Server=localhost;Database=product_db;Uid=root;Pwd=root;";
        private static object databaseRecordProductDataHelperObject = new object();
        public static MySqlConnection GetConnection()
        {
            return new MySqlConnection(connectionString);
        }
        // åˆ›å»ºæ•°æ®åº“和表
        public static void InitializeDatabase()
        {
            lock (databaseRecordProductDataHelperObject)
            {
                try
                {
                    using (var connection = GetConnection())
                    {
                        connection.Open();
                        // åˆ›å»ºæ•°æ®åº“
                        string createDbSql = @"CREATE DATABASE IF NOT EXISTS product_db;";
                        using (var cmd = new MySqlCommand(createDbSql, connection))
                        {
                            cmd.ExecuteNonQuery();
                        }
                        // ä½¿ç”¨æ•°æ®åº“
                        string useDbSql = @"USE product_db;";
                        using (var cmd = new MySqlCommand(useDbSql, connection))
                        {
                            cmd.ExecuteNonQuery();
                        }
                        // åˆ›å»ºè¡¨ï¼ˆå¢žåŠ ç´¢å¼•ä»¥æé«˜æŸ¥è¯¢æ€§èƒ½ï¼‰
                        string createTableSql = @"
                                                 CREATE TABLE IF NOT EXISTS RecordProductData (
                                                 Id INT AUTO_INCREMENT PRIMARY KEY,
                                                 ProductName VARCHAR(255) NOT NULL,
                                                 ProductSN VARCHAR(255) NOT NULL,
                                                 InspectionOperator VARCHAR(255),
                                                 NGType VARCHAR(255),
                                                 NGSize VARCHAR(255),
                                                 DetectionTime VARCHAR(255)),
                                                 CameraInspection VARCHAR(255),
                                                 CreatedDate DATETIME DEFAULT CURRENT_TIMESTAMP,
                                                 INDEX idx_created_date (CreatedDate),
                                                 INDEX idx_product_number (ProductSN),
                                                 INDEX idx_operator (InspectionOperator)
                                                 );";
                        using (var cmd = new MySqlCommand(createTableSql, connection))
                        {
                            cmd.ExecuteNonQuery();
                            AsyncLogHelper.Info("数据库和表创建成功!");
                        }
                    }
                }
                catch (Exception ex)
                {
                    AsyncLogHelper.Error($"数据库初始化失败: {ex.Message}");
                    throw;
                }
            }
        }
    }
}
LB_SmartVision/SQL/LB_Collections.cs
ÎļþÃû´Ó LB_SmartVision/LB_Collections.cs ÐÞ¸Ä
@@ -5,7 +5,7 @@
using System.Text;
using System.Threading.Tasks;
namespace LB_SmartVision
namespace LB_SmartVision.SQL
{
    [Serializable]
    public class LB_Collections<T> : ICollection<CollectionItem<T>>
LB_SmartVision/SQL/LB_SqlCommand.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,552 @@
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LB_SmartVision.SQL
{
    public class LB_SqlCommand
    {
        /// <summary>
        /// è¿žæŽ¥å­—符串:指定 MySQL å®žä¾‹å’Œæ•°æ®åº“(实际会读取dll.config中的值来更新连接字符串)
        /// </summary>
        public static string ConnectionString = "Server=localhost;Database=product_mes;Uid=root;Pwd=root;";
        /// <summary>
        /// åˆ¤æ–­æŒ‡å®šåˆ—是否存在某值
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="columnName">指定列名</param>
        /// <param name="value">指定值</param>
        /// <param name="connectionString"></param>
        /// <returns>值存在True</returns>
        public static bool IsExist(string tableName, string columnName, string value, string connectionString = "")
        {
            if (string.IsNullOrEmpty(connectionString))
                connectionString = ConnectionString;
            // MySQL æŸ¥è¯¢è¯­å¥ï¼Œæ£€æŸ¥æŒ‡å®šåˆ—是否存在指定的值
            string sqlQuery = string.Format("SELECT CASE WHEN EXISTS (SELECT 1 FROM `{0}` " +
                "WHERE `{1}` = @Value) THEN 1 ELSE 0 END", tableName, columnName);
            // åˆ›å»º MySQL è¿žæŽ¥
            using (MySqlConnection connection = new MySqlConnection(connectionString))
            {
                // åˆ›å»º MySQL å‘½ä»¤
                using (MySqlCommand command = new MySqlCommand(sqlQuery, connection))
                {
                    // æ·»åŠ å‚æ•°ï¼Œé˜²æ­¢ SQL æ³¨å…¥
                    command.Parameters.AddWithValue("@Value", value);
                    // æ‰“开连接
                    connection.Open();
                    // æ‰§è¡ŒæŸ¥è¯¢å¹¶èŽ·å–ç»“æžœ
                    int result = Convert.ToInt32(command.ExecuteScalar());
                    // å¦‚æžœ result == 1,表示存在该值;否则不存在
                    return result == 1;
                }
            }
        }
        /// <summary>
        /// åˆ¤æ–­æŒ‡å®šè¡¨æ˜¯å¦å­˜åœ¨
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="connectionString"></param>
        /// <returns>值存在True</returns>
        public static bool IsExistTable(string tableName, string connectionString = "")
        {
            if (string.IsNullOrEmpty(connectionString))
                connectionString = ConnectionString;
            // MySQL æŸ¥è¯¢è¯­å¥ï¼Œæ£€æŸ¥è¡¨æ˜¯å¦å­˜åœ¨
            string sqlQuery = $@"
                                SELECT COUNT(*)
                                FROM INFORMATION_SCHEMA.TABLES
                                WHERE TABLE_SCHEMA = DATABASE()
                                  AND TABLE_NAME = @TableName";
            // åˆ›å»º MySQL è¿žæŽ¥
            using (MySqlConnection connection = new MySqlConnection(connectionString))
            {
                // åˆ›å»º MySQL å‘½ä»¤
                using (MySqlCommand command = new MySqlCommand(sqlQuery, connection))
                {
                    // æ·»åŠ è¡¨åå‚æ•°
                    command.Parameters.AddWithValue("@TableName", tableName);
                    // æ‰“开连接
                    connection.Open();
                    // æ‰§è¡ŒæŸ¥è¯¢å¹¶èŽ·å–ç»“æžœ
                    int result = Convert.ToInt32(command.ExecuteScalar());
                    // å¦‚æžœ result > 0,表示存在该表;否则不存在
                    return result > 0;
                }
            }
        }
        /// <summary>
        /// é‡å‘½åè¡¨
        /// </summary>
        /// <param name="oldTableName"></param>
        /// <param name="newTableName"></param>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        public static bool RenameTable(string oldTableName, string newTableName, string connectionString = "")
        {
            if (string.IsNullOrEmpty(connectionString))
                connectionString = ConnectionString;
            if (!IsExistTable(oldTableName))
                return false;
            // MySQL é‡å‘½åè¡¨è¯­æ³•
            string sqlQuery = $"ALTER TABLE `{oldTableName}` RENAME TO `{newTableName}`";
            // åˆ›å»º MySQL è¿žæŽ¥
            using (MySqlConnection connection = new MySqlConnection(connectionString))
            {
                // åˆ›å»º MySQL å‘½ä»¤
                using (MySqlCommand command = new MySqlCommand(sqlQuery, connection))
                {
                    // æ‰“开连接
                    connection.Open();
                    try
                    {
                        // æ‰§è¡ŒæŸ¥è¯¢å¹¶èŽ·å–ç»“æžœ
                        command.ExecuteNonQuery();
                        return true;
                    }
                    catch
                    {
                        return false;
                    }
                }
            }
        }
        /// <summary>
        /// åˆ é™¤æŒ‡å®šè¡¨
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="connectionString"></param>
        /// <returns>删除成功True</returns>
        public static bool DeleteTable(string tableName, string connectionString = "")
        {
            if (string.IsNullOrEmpty(connectionString))
                connectionString = ConnectionString;
            // MySQL åˆ é™¤è¡¨è¯­å¥
            string sqlQuery = $"DROP TABLE IF EXISTS `{tableName}`";
            // åˆ›å»º MySQL è¿žæŽ¥
            using (MySqlConnection connection = new MySqlConnection(connectionString))
            {
                // åˆ›å»º MySQL å‘½ä»¤
                using (MySqlCommand command = new MySqlCommand(sqlQuery, connection))
                {
                    // æ‰“开连接
                    connection.Open();
                    try
                    {
                        // æ‰§è¡Œåˆ é™¤æ“ä½œ
                        command.ExecuteNonQuery();
                        return true;
                    }
                    catch
                    {
                        return false;
                    }
                }
            }
        }
        /// <summary>
        /// èŽ·å–æ‰€éœ€çš„å€¼é€šè¿‡ç´¢å¼•æŒ‡å®šåˆ—çš„æŒ‡å®šå€¼å®šä½åˆ°æ‰€åœ¨è¡Œçš„æŒ‡å®šåˆ—
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="indexName">指定列名</param>
        /// <param name="indexValue">指定列值</param>
        /// <param name="columnName">所需值的列名</param>
        /// <param name="connectionString"></param>
        /// <returns>所需列值</returns>
        public static string FindValueFromIndexName(string tableName, string indexName, string indexValue, string columnName, string connectionString = "")
        {
            try
            {
                if (string.IsNullOrEmpty(connectionString))
                    connectionString = ConnectionString;
                // MySQL æŸ¥è¯¢è¯­å¥
                string sqlQuery = $"SELECT `{columnName}` FROM `{tableName}` WHERE `{indexName}` = @IndexValue";
                string resultValue = string.Empty;
                // è¿žæŽ¥æ•°æ®åº“并执行查询
                using (MySqlConnection connection = new MySqlConnection(connectionString))
                {
                    using (MySqlCommand command = new MySqlCommand(sqlQuery, connection))
                    {
                        // æ·»åŠ å‚æ•°ä»¥é˜²æ­¢ SQL æ³¨å…¥
                        command.Parameters.AddWithValue("@IndexValue", indexValue);
                        // æ‰“开连接
                        connection.Open();
                        // æ‰§è¡ŒæŸ¥è¯¢å¹¶èŽ·å–ç»“æžœ
                        var result = command.ExecuteScalar();
                        if (result != null)
                        {
                            resultValue = result.ToString();
                        }
                    }
                }
                return resultValue;
            }
            catch
            {
                return string.Empty;
            }
        }
        /// <summary>
        /// åˆ é™¤æŒ‡å®šè¡Œé€šè¿‡ç´¢å¼•指定列的指定值
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="indexName">指定列名</param>
        /// <param name="indexValue">指定列值</param>
        /// <param name="connectionString"></param>
        /// <returns>删除成功True</returns>
        public static bool DeleteRowFromIndexName(string tableName, string indexName, string indexValue, string connectionString = "")
        {
            try
            {
                if (string.IsNullOrEmpty(connectionString))
                    connectionString = ConnectionString;
                using (MySqlConnection conn = new MySqlConnection(connectionString))
                {
                    conn.Open();
                    // åˆ é™¤æŒ‡å®šè¡Œ
                    string deleteQuery = string.Format("DELETE FROM `{0}` WHERE `{1}` = @indexValue", tableName, indexName);
                    using (MySqlCommand cmd = new MySqlCommand(deleteQuery, conn))
                    {
                        cmd.Parameters.AddWithValue("@indexValue", indexValue);
                        cmd.ExecuteNonQuery();
                    }
                    // MySQL ä¸­æ›´æ–°è¡Œå·çš„写法(假设indexName是自增列)
                    string updateQuery = string.Format(@"
                        SET @row_number = 0;
                        UPDATE `{0}`
                        SET `{1}` = @row_number := @row_number + 1
                        ORDER BY `{1}`;", tableName, indexName);
                    using (MySqlCommand cmd = new MySqlCommand(updateQuery, conn))
                    {
                        cmd.ExecuteNonQuery();
                    }
                    return true;
                }
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// äº¤æ¢æŒ‡å®šè¡Œçš„æ‰€æœ‰å€¼é€šè¿‡ç´¢å¼•指定列的指定值
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <param name="swapName">列名</param>
        /// <param name="swapValue1">交换的列值</param>
        /// <param name="swapValue2">被交换的列值</param>
        /// <param name="connectionString"></param>
        /// <returns>交换成功True</returns>
        public static bool SwapRowFromIndexName(string tableName, string swapName, string swapValue1, string swapValue2, string connectionString = "")
        {
            if (string.IsNullOrEmpty(connectionString))
                connectionString = ConnectionString;
            using (MySqlConnection conn = new MySqlConnection(connectionString))
            {
                conn.Open();
                // å¼€å§‹äº‹åŠ¡
                MySqlTransaction transaction = conn.BeginTransaction();
                try
                {
                    // MySQL äº¤æ¢è¡Œå€¼çš„写法
                    string swapQuery = string.Format(@"
                        UPDATE `{0}`
                        SET `{1}` = CASE
                            WHEN `{1}` = @SwapValue1 THEN @SwapTempValue
                            WHEN `{1}` = @SwapValue2 THEN @SwapValue1
                            WHEN `{1}` = @SwapTempValue THEN @SwapValue2
                        END
                        WHERE `{1}` IN (@SwapValue1, @SwapValue2, @SwapTempValue);", tableName, swapName);
                    using (MySqlCommand cmd = new MySqlCommand(swapQuery, conn, transaction))
                    {
                        // æ·»åŠ å‚æ•°
                        cmd.Parameters.AddWithValue("@SwapValue1", swapValue1);
                        cmd.Parameters.AddWithValue("@SwapValue2", swapValue2);
                        cmd.Parameters.AddWithValue("@SwapTempValue", Guid.NewGuid().ToString());
                        // æ‰§è¡ŒæŸ¥è¯¢
                        cmd.ExecuteNonQuery();
                    }
                    // æäº¤äº‹åŠ¡
                    transaction.Commit();
                    return true;
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("交换失败,原因是:" + ex.Message.ToString());
                    // å‡ºçŽ°å¼‚å¸¸æ—¶å›žæ»šäº‹åŠ¡
                    transaction.Rollback();
                    return false;
                }
            }
        }
        /// <summary>
        /// æ›´æ–°æŒ‡å®šåˆ—的值通过索引指定列的指定值
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="oriValue"></param>
        /// <param name="newValue"></param>
        /// <param name="columnName"></param>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        public static bool UpdateValueFromIndexName(string tableName, string oriValue, string newValue, string columnName, string connectionString = "")
        {
            // ä¿®æ­£åŽŸæ–¹æ³•åæ‹¼å†™é”™è¯¯ Updata -> Update
            if (string.IsNullOrEmpty(connectionString))
                connectionString = ConnectionString;
            // MySQL æ›´æ–°è¯­å¥
            string sqlQuery = string.Format("UPDATE `{0}` SET `{1}` = @NewValue WHERE `{1}` = @OriValue", tableName, columnName);
            // è¿žæŽ¥æ•°æ®åº“并执行查询
            using (MySqlConnection connection = new MySqlConnection(connectionString))
            {
                using (MySqlCommand command = new MySqlCommand(sqlQuery, connection))
                {
                    // å‚数化查询,防止 SQL æ³¨å…¥
                    command.Parameters.AddWithValue("@OriValue", oriValue);  // è¦æ›¿æ¢çš„原始值
                    command.Parameters.AddWithValue("@NewValue", newValue);  // æ›¿æ¢åŽçš„æ–°å€¼
                    // æ‰“开连接
                    connection.Open();
                    // æ‰§è¡Œæ›´æ–°æ“ä½œ
                    int rowsAffected = command.ExecuteNonQuery();
                    return rowsAffected > 0;
                }
            }
        }
        /// <summary>
        /// æ£€æŸ¥æ•°æ®åº“是否存在
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="databaseName"></param>
        /// <returns></returns>
        public static bool DatabaseExists(string connectionString, string databaseName)
        {
            string query = "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = @databaseName";
            using (MySqlConnection connection = new MySqlConnection(connectionString))
            {
                connection.Open();
                using (MySqlCommand command = new MySqlCommand(query, connection))
                {
                    command.Parameters.AddWithValue("@databaseName", databaseName);
                    var result = command.ExecuteScalar();
                    return result != null;
                }
            }
        }
        /// <summary>
        /// åˆ›å»ºæ•°æ®åº“
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="databaseName"></param>
        /// <returns></returns>
        public static bool CreateDatabase(string connectionString, string databaseName)
        {
            if (string.IsNullOrEmpty(connectionString))
                connectionString = ConnectionString;
            try
            {
                string createQuery = $"CREATE DATABASE IF NOT EXISTS `{databaseName}` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci";
                using (MySqlConnection connection = new MySqlConnection(connectionString))
                {
                    connection.Open();
                    using (MySqlCommand command = new MySqlCommand(createQuery, connection))
                    {
                        command.ExecuteNonQuery();
                    }
                }
                return true;
            }
            catch
            {
                return false;
            }
        }
        /// <summary>
        /// åˆ é™¤æ•°æ®åº“
        /// </summary>
        /// <param name="databaseName"></param>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        public static bool DeleteDatabase(string databaseName, string connectionString)
        {
            if (string.IsNullOrEmpty(connectionString))
                connectionString = ConnectionString;
            // MySQL åˆ é™¤æ•°æ®åº“语句
            string dropDatabaseQuery = $"DROP DATABASE IF EXISTS `{databaseName}`";
            using (MySqlConnection connection = new MySqlConnection(connectionString))
            {
                try
                {
                    // æ‰“开数据库连接
                    connection.Open();
                    // åˆ›å»º MySqlCommand å¯¹è±¡
                    using (MySqlCommand cmd = new MySqlCommand(dropDatabaseQuery, connection))
                    {
                        // æ‰§è¡Œåˆ é™¤æ•°æ®åº“操作
                        cmd.ExecuteNonQuery();
                        Debug.WriteLine("Database dropped successfully.");
                        return true;
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine("Error: " + ex.Message);
                    return false;
                }
            }
        }
        /// <summary>
        /// å¤åˆ¶è¡¨ç»“构和数据
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="sourceDatabase"></param>
        /// <param name="targetDatabase"></param>
        /// <returns></returns>
        public static bool CopyDatabaseData(string connectionString, string sourceDatabase, string targetDatabase)
        {
            if (string.IsNullOrEmpty(connectionString))
                connectionString = ConnectionString;
            try
            {
                // å…ˆåˆ›å»ºç›®æ ‡æ•°æ®åº“(如果不存在)
                CreateDatabase(connectionString, targetDatabase);
                using (var connection = new MySqlConnection(connectionString))
                {
                    connection.Open();
                    // èŽ·å–æºæ•°æ®åº“ä¸­æ‰€æœ‰è¡¨çš„åˆ—è¡¨
                    string getTablesQuery = $"SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = @sourceDb";
                    using (var cmd = new MySqlCommand(getTablesQuery, connection))
                    {
                        cmd.Parameters.AddWithValue("@sourceDb", sourceDatabase);
                        using (var reader = cmd.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                string tableName = reader.GetString(0);
                                Debug.WriteLine($"复制表: {tableName}");
                                // å…³é—­reader才能执行后续操作
                                reader.Close();
                                // å¤åˆ¶è¡¨ç»“构和数据(MySQL ç®€åŒ–写法)
                                string copyTableQuery = $"CREATE TABLE `{targetDatabase}`.`{tableName}` LIKE `{sourceDatabase}`.`{tableName}`; " +
                                                       $"INSERT INTO `{targetDatabase}`.`{tableName}` SELECT * FROM `{sourceDatabase}`.`{tableName}`;";
                                using (var cmdCopy = new MySqlCommand(copyTableQuery, connection))
                                {
                                    cmdCopy.ExecuteNonQuery();
                                }
                                // é‡æ–°æ‰“å¼€reader继续读取下一个表
                                reader.NextResult();
                            }
                        }
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"复制失败: {ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// èŽ·å–ç”¨æˆ·æ•°æ®åº“åˆ—è¡¨
        /// </summary>
        /// <returns></returns>
        public static List<string> GetUserDatabases()
        {
            List<string> databases = new List<string>();
            string connectionString = "Server=localhost;Database=user_pt;Uid=root;Pwd=root;";
            using (MySqlConnection connection = new MySqlConnection(connectionString))
            {
                try
                {
                    connection.Open();
                    string query = @"
                        SELECT SCHEMA_NAME
                        FROM INFORMATION_SCHEMA.SCHEMATA
                        WHERE SCHEMA_NAME NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')";
                    using (MySqlCommand command = new MySqlCommand(query, connection))
                    using (MySqlDataReader reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            databases.Add(reader["SCHEMA_NAME"]?.ToString());
                        }
                    }
                }
                catch (MySqlException ex)
                {
                    Debug.WriteLine($"数据库查询失败: {ex.Message}");
                }
            }
            return databases;
        }
    }
}
LB_SmartVision/SQL/RecordProductData.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
using LB_SmartVisionCommon;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LB_SmartVision.SQL
{
    /// <summary>
    /// è®°å½•生产数据
    /// </summary>
    [JsonObject(MemberSerialization.OptOut)]
    [TypeConverter(typeof(PropertySorter))]
    public class RecordProductData
    {
        /// <summary>
        /// SKU  ç‰©æ–™å·æˆ–产品名称
        /// </summary>
        [Category("RecordProductData"), PropertyOrder(0)]
        [DisplayName("SKU")]
        [Browsable(true)]
        public string ProductName { get; set; }
        /// <summary>
        /// äº§å“SN号
        /// </summary>
        [Category("RecordProductData"), PropertyOrder(1)]
        [DisplayName("SN")]
        [Browsable(true)]
        public string ProductSN { get; set; }
        /// <summary>
        /// æ£€æµ‹ä½œä¸šå‘˜
        /// </summary>
        [Category("RecordProductData"), PropertyOrder(2)]
        [DisplayName("检测作业员")]
        [Browsable(true)]
        public string InspectionOperator { get; set; }
        /// <summary>
        /// NG类型
        /// </summary>
        [Category("RecordProductData"), PropertyOrder(3)]
        [DisplayName("NG类型")]
        [Browsable(true)]
        public string NGType {  get; set; }
        /// <summary>
        /// NG大小
        /// </summary>
        [Category("RecordProductData"), PropertyOrder(4)]
        [DisplayName("NG大小")]
        [Browsable(true)]
        public string NGSize { get; set; }
        /// <summary>
        /// æ£€æµ‹æ—¶é—´
        /// </summary>
        [Category("RecordProductData"), PropertyOrder(5)]
        [DisplayName("检测时间")]
        [Browsable(true)]
        public string DetectionTime { get; set; }
        /// <summary>
        /// æ£€æµ‹ç›¸æœº
        /// </summary>
        [Category("RecordProductData"), PropertyOrder(6)]
        [DisplayName("检测相机")]
        [Browsable(true)]
        public string CameraInspection { get; set; }
    }
}
LB_SmartVision/SQL/RecordProductDataRepository.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,419 @@
using LB_SmartVisionCommon;
using MySql.Data.MySqlClient;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LB_SmartVision.SQL
{
    /// <summary>
    /// æ•°æ®è®¿é—®å±‚
    /// </summary>
    public class RecordProductDataRepository
    {
        private static object recordProductDataRepositoryObject = new object();
        /// <summary>
        /// æ·»åŠ æ•°æ®
        /// </summary>
        /// <param name="record">生产数据记录</param>
        /// <returns>是否添加成功</returns>
        public static bool AddRecord(RecordProductData record)
        {
            Stopwatch stopwatch = Stopwatch.StartNew();
            lock (recordProductDataRepositoryObject)
            {
                stopwatch.Start();
                try
                {
                    using (var connection = DatabaseRecordProductDataHelper.GetConnection())
                    {
                        connection.Open();
                        string sql = @"
                                     INSERT INTO RecordProductData
                                     (ProductName, ProductSN, InspectionOperator, NGType, NGSize,DetectionTime,CameraInspection)
                                     VALUES
                                     (@ProductName, @ProductSN, @InspectionOperator, @NGType, @NGSize, @DetectionTime, @CameraInspection)";
                        using (var cmd = new MySqlCommand(sql, connection))
                        {
                            cmd.Parameters.AddWithValue("@ProductName", record.ProductName);
                            cmd.Parameters.AddWithValue("@ProductSN", record.ProductSN);
                            cmd.Parameters.AddWithValue("@InspectionOperator", record.InspectionOperator);
                            cmd.Parameters.AddWithValue("@NGType", record.NGType);
                            cmd.Parameters.AddWithValue("@NGSize", record.NGSize);
                            cmd.Parameters.AddWithValue("@DetectionTime", record.DetectionTime);
                            cmd.Parameters.AddWithValue("@CameraInspection", record.CameraInspection);
                            int rowsAffected = cmd.ExecuteNonQuery();
                            stopwatch.Stop();
                            Task.Factory.StartNew(() =>
                            {
                                LogHelper.Info($"添加信息耗时: {stopwatch.ElapsedMilliseconds}ms");
                            });
                            return rowsAffected > 0;
                        }
                    }
                }
                catch (Exception ex)
                {
                    AsyncLogHelper.Error($"添加记录失败: {ex.Message}");
                    return false;
                }
            }
        }
        /// <summary>
        /// æŸ¥è¯¢æ‰€æœ‰æ•°æ®
        /// </summary>
        /// <returns>List<RecordProductData></returns>
        public static List<RecordProductData> GetAllRecords()
        {
            lock (recordProductDataRepositoryObject)
            {
                var records = new List<RecordProductData>();
                try
                {
                    using (var connection = DatabaseRecordProductDataHelper.GetConnection())
                    {
                        connection.Open();
                        string sql = "SELECT * FROM RecordProductData ORDER BY CreatedDate DESC";
                        using (var cmd = new MySqlCommand(sql, connection))
                        using (var reader = cmd.ExecuteReader())
                        {
                            while (reader.Read())
                            {
                                records.Add(MapReaderToRecord(reader));
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    AsyncLogHelper.Error($"查询记录失败: {ex.Message}");
                }
                return records;
            }
        }
        /// <summary>
        /// æ ¹æ®äº§å“ç¼–号查询
        /// </summary>
        /// <param name="productSN">产品编号</param>
        /// <returns>List<RecordProductData></returns>
        public static List<RecordProductData> GetRecordsByProductNumber(string productSN)
        {
            lock (recordProductDataRepositoryObject)
            {
                var records = new List<RecordProductData>();
                try
                {
                    using (var connection = DatabaseRecordProductDataHelper.GetConnection())
                    {
                        connection.Open();
                        string sql = "SELECT * FROM RecordProductData WHERE productSN = @productSN ORDER BY CreatedDate DESC";
                        using (var cmd = new MySqlCommand(sql, connection))
                        {
                            cmd.Parameters.AddWithValue("@productSN", productSN);
                            using (var reader = cmd.ExecuteReader())
                            {
                                while (reader.Read())
                                {
                                    records.Add(MapReaderToRecord(reader));
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    AsyncLogHelper.Error($"根据产品编号查询失败: {ex.Message}");
                }
                return records;
            }
        }
        /// <summary>
        /// æ ¹æ®æ“ä½œè€…查询
        /// </summary>
        /// <param name="inspectionOperator">操作者</param>
        /// <returns>List<RecordProductData></returns>
        public static List<RecordProductData> GetRecordsByOperator(string inspectionOperator)
        {
            lock (recordProductDataRepositoryObject)
            {
                var records = new List<RecordProductData>();
                try
                {
                    using (var connection = DatabaseRecordProductDataHelper.GetConnection())
                    {
                        connection.Open();
                        string sql = "SELECT * FROM RecordProductData WHERE InspectionOperator = @InspectionOperator ORDER BY CreatedDate DESC";
                        using (var cmd = new MySqlCommand(sql, connection))
                        {
                            cmd.Parameters.AddWithValue("@InspectionOperator", inspectionOperator);
                            using (var reader = cmd.ExecuteReader())
                            {
                                while (reader.Read())
                                {
                                    records.Add(MapReaderToRecord(reader));
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    AsyncLogHelper.Error($"根据操作者查询失败: {ex.Message}");
                }
                return records;
            }
        }
        /// <summary>
        /// æ ¹æ®æ—¶é—´æ®µæŸ¥è¯¢ - ä½¿ç”¨CreatedDate
        /// </summary>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>List<RecordProductData></returns>
        public static List<RecordProductData> GetRecordsByTimeRange(DateTime startTime, DateTime endTime)
        {
            lock (recordProductDataRepositoryObject)
            {
                var records = new List<RecordProductData>();
                try
                {
                    using (var connection = DatabaseRecordProductDataHelper.GetConnection())
                    {
                        connection.Open();
                        string sql = @"
                                     SELECT * FROM RecordProductData
                                     WHERE CreatedDate BETWEEN @StartTime AND @EndTime
                                     ORDER BY CreatedDate DESC";
                        using (var cmd = new MySqlCommand(sql, connection))
                        {
                            cmd.Parameters.AddWithValue("@StartTime", startTime);
                            cmd.Parameters.AddWithValue("@EndTime", endTime.AddDays(1).AddSeconds(-1)); // åŒ…含结束日期的全天
                            using (var reader = cmd.ExecuteReader())
                            {
                                while (reader.Read())
                                {
                                    records.Add(MapReaderToRecord(reader));
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    AsyncLogHelper.Error($"根据时间段查询失败: {ex.Message}");
                }
                return records;
            }
        }
        /// <summary>
        /// ç»„合查询:产品编号 + æ—¶é—´æ®µ
        /// </summary>
        /// <param name="productSN">产品编号</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>List<RecordProductData></returns>
        public static List<RecordProductData> GetRecordsByProductAndTimeRange(string productSN, DateTime startTime, DateTime endTime)
        {
            lock (recordProductDataRepositoryObject)
            {
                var records = new List<RecordProductData>();
                try
                {
                    using (var connection = DatabaseRecordProductDataHelper.GetConnection())
                    {
                        connection.Open();
                        string sql = @"
                                     SELECT * FROM RecordProductData
                                     WHERE productSN = @productSN
                                     AND CreatedDate BETWEEN @StartTime AND @EndTime
                                     ORDER BY CreatedDate DESC";
                        using (var cmd = new MySqlCommand(sql, connection))
                        {
                            cmd.Parameters.AddWithValue("@productSN", productSN);
                            cmd.Parameters.AddWithValue("@StartTime", startTime);
                            cmd.Parameters.AddWithValue("@EndTime", endTime.AddDays(1).AddSeconds(-1));
                            using (var reader = cmd.ExecuteReader())
                            {
                                while (reader.Read())
                                {
                                    records.Add(MapReaderToRecord(reader));
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    AsyncLogHelper.Error($"组合查询失败: {ex.Message}");
                }
                return records;
            }
        }
        /// <summary>
        /// ç»„合查询:操作者 + æ—¶é—´æ®µ
        /// </summary>
        /// <param name="inspectionOperator">操作者</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>List<RecordProductData></returns>
        public static List<RecordProductData> GetRecordsByOperatorAndTimeRange(string inspectionOperator, DateTime startTime, DateTime endTime)
        {
            lock (recordProductDataRepositoryObject)
            {
                var records = new List<RecordProductData>();
                try
                {
                    using (var connection = DatabaseRecordProductDataHelper.GetConnection())
                    {
                        connection.Open();
                        string sql = @"
                                     SELECT * FROM RecordProductData
                                     WHERE InspectionOperator = @InspectionOperator
                                     AND CreatedDate BETWEEN @StartTime AND @EndTime
                                     ORDER BY CreatedDate DESC";
                        using (var cmd = new MySqlCommand(sql, connection))
                        {
                            cmd.Parameters.AddWithValue("@InspectionOperator", inspectionOperator);
                            cmd.Parameters.AddWithValue("@StartTime", startTime);
                            cmd.Parameters.AddWithValue("@EndTime", endTime.AddDays(1).AddSeconds(-1));
                            using (var reader = cmd.ExecuteReader())
                            {
                                while (reader.Read())
                                {
                                    records.Add(MapReaderToRecord(reader));
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    AsyncLogHelper.Error($"组合查询失败: {ex.Message}");
                }
                return records;
            }
        }
        /// <summary>
        /// é«˜çº§ç»„合查询:支持多个条件
        /// </summary>
        /// <param name="productSN">产品编号</param>
        /// <param name="operatorName">操作者</param>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>List<RecordProductData></returns>
        public static List<RecordProductData> GetRecordsByMultipleConditions(
            string productSN = null,
            string operatorName = null,
            DateTime? startTime = null,
            DateTime? endTime = null)
        {
            lock (recordProductDataRepositoryObject)
            {
                var records = new List<RecordProductData>();
                try
                {
                    using (var connection = DatabaseRecordProductDataHelper.GetConnection())
                    {
                        connection.Open();
                        var sql = "SELECT * FROM RecordProductData WHERE 1=1";
                        var parameters = new List<MySqlParameter>();
                        if (!string.IsNullOrEmpty(productSN))
                        {
                            sql += " AND ProductSN = @ProductSN";
                            parameters.Add(new MySqlParameter("@ProductSN", productSN));
                        }
                        if (!string.IsNullOrEmpty(operatorName))
                        {
                            sql += " AND MeasurementOperator = @InspectionOperator";
                            parameters.Add(new MySqlParameter("@InspectionOperator", operatorName));
                        }
                        if (startTime.HasValue)
                        {
                            sql += " AND CreatedDate >= @StartTime";
                            parameters.Add(new MySqlParameter("@StartTime", startTime.Value));
                        }
                        if (endTime.HasValue)
                        {
                            sql += " AND CreatedDate <= @EndTime";
                            parameters.Add(new MySqlParameter("@EndTime", endTime.Value.AddDays(1).AddSeconds(-1)));
                        }
                        sql += " ORDER BY CreatedDate DESC";
                        using (var cmd = new MySqlCommand(sql, connection))
                        {
                            foreach (var param in parameters)
                            {
                                cmd.Parameters.Add(param);
                            }
                            using (var reader = cmd.ExecuteReader())
                            {
                                while (reader.Read())
                                {
                                    records.Add(MapReaderToRecord(reader));
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    AsyncLogHelper.Error($"高级组合查询失败: {ex.Message}");
                }
                return records;
            }
        }
        /// <summary>
        /// ç»Ÿè®¡æŸ¥è¯¢ï¼šèŽ·å–æŸä¸ªæ—¶é—´æ®µå†…çš„è®°å½•æ•°é‡
        /// </summary>
        /// <param name="startTime">起始时间</param>
        /// <param name="endTime">结束时间</param>
        /// <returns>记录数量</returns>
        public static int GetRecordCountByTimeRange(DateTime startTime, DateTime endTime)
        {
            lock (recordProductDataRepositoryObject)
            {
                try
                {
                    using (var connection = DatabaseRecordProductDataHelper.GetConnection())
                    {
                        connection.Open();
                        string sql = @"
                                     SELECT COUNT(*) FROM RecordProductData
                                     WHERE CreatedDate BETWEEN @StartTime AND @EndTime";
                        using (var cmd = new MySqlCommand(sql, connection))
                        {
                            cmd.Parameters.AddWithValue("@StartTime", startTime);
                            cmd.Parameters.AddWithValue("@EndTime", endTime.AddDays(1).AddSeconds(-1));
                            return Convert.ToInt32(cmd.ExecuteScalar());
                        }
                    }
                }
                catch (Exception ex)
                {
                    AsyncLogHelper.Error($"统计查询失败: {ex.Message}");
                    return 0;
                }
            }
        }
        /// <summary>
        /// è¾…助方法:将数据读取器映射到RecordProductData对象
        /// </summary>
        /// <param name="reader">MySqlDataReader reader</param>
        /// <returns>RecordProductData</returns>
        private static RecordProductData MapReaderToRecord(MySqlDataReader reader)
        {
            return new RecordProductData
            {
                ProductName = reader["ProductName"]?.ToString() ?? string.Empty,
                ProductSN = reader["ProductSN"]?.ToString() ?? string.Empty,
                InspectionOperator = reader["InspectionOperator"]?.ToString() ?? string.Empty,
                NGType = reader["NGType"]?.ToString() ?? string.Empty,
                NGSize = reader["NGSize"]?.ToString() ?? string.Empty,
                DetectionTime = reader["DetectionTime"]?.ToString() ?? string.Empty,
                CameraInspection = reader["CameraInspection"]?.ToString() ?? string.Empty,
            };
        }
    }
}
LB_SmartVision/VisionForm.Designer.cs
@@ -61,12 +61,11 @@
            theme_VisionForm.Font = new Font("Microsoft YaHei UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 0);
            theme_VisionForm.Image = (Image)resources.GetObject("theme_VisionForm.Image");
            theme_VisionForm.Location = new Point(0, 0);
            theme_VisionForm.Margin = new Padding(4);
            theme_VisionForm.Name = "theme_VisionForm";
            theme_VisionForm.Padding = new Padding(12, 88, 12, 11);
            theme_VisionForm.Padding = new Padding(10, 70, 10, 9);
            theme_VisionForm.RoundCorners = true;
            theme_VisionForm.Sizable = true;
            theme_VisionForm.Size = new Size(1440, 880);
            theme_VisionForm.Size = new Size(1152, 704);
            theme_VisionForm.SmartBounds = true;
            theme_VisionForm.StartPosition = FormStartPosition.WindowsDefaultLocation;
            theme_VisionForm.TabIndex = 0;
@@ -76,8 +75,7 @@
            // 
            sc_VisionForm.Dock = DockStyle.Fill;
            sc_VisionForm.FixedPanel = FixedPanel.Panel2;
            sc_VisionForm.Location = new Point(12, 88);
            sc_VisionForm.Margin = new Padding(4);
            sc_VisionForm.Location = new Point(10, 70);
            sc_VisionForm.Name = "sc_VisionForm";
            sc_VisionForm.Orientation = Orientation.Horizontal;
            // 
@@ -88,9 +86,8 @@
            // sc_VisionForm.Panel2
            // 
            sc_VisionForm.Panel2.Controls.Add(grb_Info);
            sc_VisionForm.Size = new Size(1416, 781);
            sc_VisionForm.SplitterDistance = 593;
            sc_VisionForm.SplitterWidth = 5;
            sc_VisionForm.Size = new Size(1132, 625);
            sc_VisionForm.SplitterDistance = 476;
            sc_VisionForm.TabIndex = 1;
            // 
            // tlp_MainView
@@ -105,10 +102,10 @@
            tlp_MainView.Margin = new Padding(2);
            tlp_MainView.Name = "tlp_MainView";
            tlp_MainView.RowCount = 3;
            tlp_MainView.RowStyles.Add(new RowStyle(SizeType.Absolute, 60F));
            tlp_MainView.RowStyles.Add(new RowStyle(SizeType.Absolute, 39F));
            tlp_MainView.RowStyles.Add(new RowStyle(SizeType.Absolute, 48F));
            tlp_MainView.RowStyles.Add(new RowStyle(SizeType.Absolute, 31F));
            tlp_MainView.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tlp_MainView.Size = new Size(1416, 593);
            tlp_MainView.Size = new Size(1132, 476);
            tlp_MainView.TabIndex = 1;
            tlp_MainView.TagString = null;
            // 
@@ -117,12 +114,12 @@
            materialTabControl.AccessibleRole = AccessibleRole.Sound;
            materialTabControl.Depth = 0;
            materialTabControl.Dock = DockStyle.Fill;
            materialTabControl.Location = new Point(2, 101);
            materialTabControl.Location = new Point(2, 81);
            materialTabControl.Margin = new Padding(2);
            materialTabControl.MouseState = MaterialSkin.MouseState.HOVER;
            materialTabControl.Name = "materialTabControl";
            materialTabControl.SelectedIndex = 0;
            materialTabControl.Size = new Size(1412, 490);
            materialTabControl.Size = new Size(1128, 393);
            materialTabControl.TabIndex = 0;
            // 
            // tlp_VisionMainOperator
@@ -143,25 +140,23 @@
            tlp_VisionMainOperator.Controls.Add(btn_GlobalVar, 7, 0);
            tlp_VisionMainOperator.Controls.Add(com_ProductName, 8, 0);
            tlp_VisionMainOperator.Dock = DockStyle.Fill;
            tlp_VisionMainOperator.Location = new Point(4, 64);
            tlp_VisionMainOperator.Margin = new Padding(4);
            tlp_VisionMainOperator.Location = new Point(3, 51);
            tlp_VisionMainOperator.Name = "tlp_VisionMainOperator";
            tlp_VisionMainOperator.RowCount = 1;
            tlp_VisionMainOperator.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            tlp_VisionMainOperator.Size = new Size(1408, 31);
            tlp_VisionMainOperator.Size = new Size(1126, 25);
            tlp_VisionMainOperator.TabIndex = 0;
            tlp_VisionMainOperator.TagString = null;
            // 
            // ckb_AllowRun
            // 
            ckb_AllowRun.Dock = DockStyle.Fill;
            ckb_AllowRun.Font = new Font("宋体", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
            ckb_AllowRun.Font = new Font("宋体", 10.5F, FontStyle.Regular, GraphicsUnit.Point, 134);
            ckb_AllowRun.ForeColor = SystemColors.Control;
            ckb_AllowRun.Location = new Point(4, 4);
            ckb_AllowRun.Margin = new Padding(4);
            ckb_AllowRun.Location = new Point(3, 3);
            ckb_AllowRun.MinimumSize = new Size(1, 1);
            ckb_AllowRun.Name = "ckb_AllowRun";
            ckb_AllowRun.Size = new Size(148, 23);
            ckb_AllowRun.Size = new Size(119, 19);
            ckb_AllowRun.TabIndex = 2;
            ckb_AllowRun.Text = "运行模式";
            // 
@@ -172,16 +167,15 @@
            btn_Login.Dock = DockStyle.Fill;
            btn_Login.EnteredBorderColor = Color.FromArgb(165, 37, 37);
            btn_Login.EnteredColor = Color.FromArgb(32, 34, 37);
            btn_Login.Font = new Font("Microsoft Sans Serif", 12F);
            btn_Login.Font = new Font("宋体", 10.5F, FontStyle.Regular, GraphicsUnit.Point, 134);
            btn_Login.Image = null;
            btn_Login.ImageAlign = ContentAlignment.MiddleLeft;
            btn_Login.InactiveColor = Color.FromArgb(32, 34, 37);
            btn_Login.Location = new Point(160, 4);
            btn_Login.Margin = new Padding(4);
            btn_Login.Location = new Point(128, 3);
            btn_Login.Name = "btn_Login";
            btn_Login.PressedBorderColor = Color.FromArgb(165, 37, 37);
            btn_Login.PressedColor = Color.FromArgb(165, 37, 37);
            btn_Login.Size = new Size(148, 23);
            btn_Login.Size = new Size(119, 19);
            btn_Login.TabIndex = 3;
            btn_Login.Text = "用户登录";
            btn_Login.TextAlignment = StringAlignment.Center;
@@ -194,16 +188,15 @@
            btn_GlobalVar.Dock = DockStyle.Fill;
            btn_GlobalVar.EnteredBorderColor = Color.FromArgb(165, 37, 37);
            btn_GlobalVar.EnteredColor = Color.FromArgb(32, 34, 37);
            btn_GlobalVar.Font = new Font("Microsoft Sans Serif", 12F);
            btn_GlobalVar.Font = new Font("宋体", 10.5F, FontStyle.Regular, GraphicsUnit.Point, 0);
            btn_GlobalVar.Image = null;
            btn_GlobalVar.ImageAlign = ContentAlignment.MiddleLeft;
            btn_GlobalVar.InactiveColor = Color.FromArgb(32, 34, 37);
            btn_GlobalVar.Location = new Point(1096, 4);
            btn_GlobalVar.Margin = new Padding(4);
            btn_GlobalVar.Location = new Point(878, 3);
            btn_GlobalVar.Name = "btn_GlobalVar";
            btn_GlobalVar.PressedBorderColor = Color.FromArgb(165, 37, 37);
            btn_GlobalVar.PressedColor = Color.FromArgb(165, 37, 37);
            btn_GlobalVar.Size = new Size(148, 23);
            btn_GlobalVar.Size = new Size(119, 19);
            btn_GlobalVar.TabIndex = 4;
            btn_GlobalVar.Text = "全局变量";
            btn_GlobalVar.TextAlignment = StringAlignment.Center;
@@ -215,12 +208,12 @@
            com_ProductName.DrawMode = DrawMode.OwnerDrawFixed;
            com_ProductName.DropDownStyle = ComboBoxStyle.DropDownList;
            com_ProductName.EnabledCalc = true;
            com_ProductName.Font = new Font("宋体", 10.5F, FontStyle.Regular, GraphicsUnit.Point, 134);
            com_ProductName.FormattingEnabled = true;
            com_ProductName.ItemHeight = 20;
            com_ProductName.Location = new Point(1252, 4);
            com_ProductName.Margin = new Padding(4);
            com_ProductName.Location = new Point(1003, 3);
            com_ProductName.Name = "com_ProductName";
            com_ProductName.Size = new Size(152, 26);
            com_ProductName.Size = new Size(120, 26);
            com_ProductName.TabIndex = 5;
            com_ProductName.SelectedValueChanged += com_ProductName_SelectedValueChanged;
            // 
@@ -229,11 +222,10 @@
            materialTabSelector.BaseTabControl = null;
            materialTabSelector.Depth = 0;
            materialTabSelector.Dock = DockStyle.Fill;
            materialTabSelector.Location = new Point(4, 4);
            materialTabSelector.Margin = new Padding(4);
            materialTabSelector.Location = new Point(3, 3);
            materialTabSelector.MouseState = MaterialSkin.MouseState.HOVER;
            materialTabSelector.Name = "materialTabSelector";
            materialTabSelector.Size = new Size(1408, 52);
            materialTabSelector.Size = new Size(1126, 42);
            materialTabSelector.TabIndex = 1;
            // 
            // grb_Info
@@ -244,11 +236,11 @@
            grb_Info.Font = new Font("宋体", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
            grb_Info.ForeColor = SystemColors.Control;
            grb_Info.Location = new Point(0, 0);
            grb_Info.Margin = new Padding(5, 6, 5, 6);
            grb_Info.Margin = new Padding(4, 5, 4, 5);
            grb_Info.MinimumSize = new Size(1, 1);
            grb_Info.Name = "grb_Info";
            grb_Info.Padding = new Padding(0, 40, 0, 0);
            grb_Info.Size = new Size(1416, 183);
            grb_Info.Padding = new Padding(0, 32, 0, 0);
            grb_Info.Size = new Size(1132, 145);
            grb_Info.TabIndex = 1;
            grb_Info.Text = "日志显示区:";
            grb_Info.TextAlignment = ContentAlignment.MiddleLeft;
@@ -258,13 +250,13 @@
            rich_Info.Dock = DockStyle.Fill;
            rich_Info.FillColor = Color.FromArgb(32, 41, 50);
            rich_Info.Font = new Font("宋体", 12F, FontStyle.Regular, GraphicsUnit.Point, 134);
            rich_Info.Location = new Point(0, 40);
            rich_Info.Location = new Point(0, 32);
            rich_Info.Margin = new Padding(4, 5, 4, 5);
            rich_Info.MinimumSize = new Size(1, 1);
            rich_Info.Name = "rich_Info";
            rich_Info.Padding = new Padding(2);
            rich_Info.ShowText = false;
            rich_Info.Size = new Size(1416, 143);
            rich_Info.Size = new Size(1132, 113);
            rich_Info.TabIndex = 0;
            rich_Info.TextAlignment = ContentAlignment.MiddleLeft;
            // 
@@ -278,8 +270,7 @@
            cb_VisionForm.EnableMaximizeButton = true;
            cb_VisionForm.EnableMinimizeButton = true;
            cb_VisionForm.ForeColor = Color.FromArgb(155, 155, 155);
            cb_VisionForm.Location = new Point(1315, 18);
            cb_VisionForm.Margin = new Padding(4);
            cb_VisionForm.Location = new Point(1072, 14);
            cb_VisionForm.MaximizeHoverColor = Color.FromArgb(74, 74, 74);
            cb_VisionForm.MinimizeHoverColor = Color.FromArgb(63, 63, 65);
            cb_VisionForm.Name = "cb_VisionForm";
@@ -289,14 +280,13 @@
            // 
            // VisionForm
            // 
            AutoScaleDimensions = new SizeF(120F, 120F);
            AutoScaleDimensions = new SizeF(96F, 96F);
            AutoScaleMode = AutoScaleMode.Dpi;
            ClientSize = new Size(1440, 880);
            ClientSize = new Size(1152, 704);
            Controls.Add(theme_VisionForm);
            FormBorderStyle = FormBorderStyle.None;
            Icon = (Icon)resources.GetObject("$this.Icon");
            Margin = new Padding(4);
            MinimumSize = new Size(326, 76);
            MinimumSize = new Size(261, 61);
            Name = "VisionForm";
            Text = "轮胎外观视觉检测系统";
            TransparencyKey = Color.Fuchsia;
LB_SmartVision/VisionForm.cs
@@ -18,6 +18,7 @@
using LB_VisionProcesses.Cameras.HRCameras;
using LB_VisionProcesses.Cameras.LBCameras;
using LB_VisionProcesses.Communicators;
using LB_VisionProcesses.Communicators.SiemensS7;
using LB_VisionProcesses.Communicators.TCom;
using LB_VisionProcesses.Forms;
using log4net.Config;
@@ -44,7 +45,7 @@
    {
        AllProcessesPage AllProcessesPages = new AllProcessesPage();
        CamerasEditPage CamerasEditPage = new CamerasEditPage();
        HistoricalDataEditPage HistoricalDataEditPage = new HistoricalDataEditPage();
        HistoricalDataEditPage HistoricalDataEditPage = new HistoricalDataEditPage();
        CommunicatorsEditPage CommunicatorsEditPage = new CommunicatorsEditPage();
        SettingEditPage SettingEditPage = new SettingEditPage();
        MESEditPage MESEditPage = new MESEditPage();
@@ -791,65 +792,122 @@
                {
                    string CommunicatorName = CommunicatorConnectionString.Key;
                    string CommunicatorAddress = CommunicatorConnectionString.Value;
                    // å®šä¹‰æ­£åˆ™è¡¨è¾¾å¼ä»¥æå–协议、IP åœ°å€å’Œç«¯å£
                    //1.    \((.*?)\):\(和 \) æ˜¯ç”¨äºŽåŒ¹é…æ‹¬å·çš„转义字符。
                    //      (.*?) æ˜¯ä¸€ä¸ªéžè´ªå©ªçš„匹配,用来匹配类名(MyProcesses.Communicators.TCPServer æˆ– MyProcesses.Communicators.UARTPort)。
                    //2.    ([^:] +):匹配冒号之前的部分,即地址(127.0.0.1 æˆ– COM5)。这里使用了[^:] æ¥åŒ¹é…é™¤äº†å†’号之外的任意字符。
                    //3.    (\d +) ï¼šåŒ¹é…ç«¯å£å·ï¼Œç¡®ä¿å®ƒåŒ¹é…ä¸€ä¸ªæˆ–多个数字。
                    string pattern = @"^\((?<ClassName>[^)]+)\)\[(?<IP>[^]]+)\]\[(?<PORT>[^]]+)\]$";
                    Match match = Regex.Match(CommunicatorAddress, pattern);
                    if (match.Success)
                    if (!string.IsNullOrEmpty(CommunicatorAddress) && CommunicatorAddress.Contains("SiemensLBS7"))
                    {
                        string ClassName = match.Groups["ClassName"].Value;   // "TCP"
                        string IP = match.Groups["IP"].Value;          // "127.0.0.1"
                        string PORT = match.Groups["PORT"].Value;        // "1111"
                        // å®šä¹‰æ­£åˆ™è¡¨è¾¾å¼ä»¥æå–协议、IP åœ°å€å’Œç«¯å£
                        //1.    \((.*?)\):\(和 \) æ˜¯ç”¨äºŽåŒ¹é…æ‹¬å·çš„转义字符。
                        //      (.*?) æ˜¯ä¸€ä¸ªéžè´ªå©ªçš„匹配,用来匹配类名(MyProcesses.Communicators.TCPServer æˆ– MyProcesses.Communicators.UARTPort)。
                        //2.    ([^:] +):匹配冒号之前的部分,即地址(127.0.0.1 æˆ– COM5)。这里使用了[^:] æ¥åŒ¹é…é™¤äº†å†’号之外的任意字符。
                        //3.    (\d +) ï¼šåŒ¹é…ç«¯å£å·ï¼Œç¡®ä¿å®ƒåŒ¹é…ä¸€ä¸ªæˆ–多个数字。
                        if (string.IsNullOrEmpty(ClassName) || string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(PORT))
                            break;
                        string pattern = @"^\((?<ClassName>[^)]+)\)\[(?<IP>[^]]+)\]\[(?<Slot>[^]]+)\]\[(?<CpuType>[^]]+)\]\[(?<PlcAddress>[^]]+)\]$";
                        Match match = Regex.Match(CommunicatorAddress, pattern);
                        //利用反射创建实例
                        Type type = IProcess.GetExecutingAssembly().GetType(ClassName);
                        if (type == null)
                        if (match.Success)
                        {
                            Debug.WriteLine("Class not found.");
                            return false;
                        }
                        var Communicator = Activator.CreateInstance(type, CommunicatorName) as BaseCommunicator;
                            string ClassName = match.Groups["ClassName"].Value;   // "TCP"
                            string IP = match.Groups["IP"].Value;          // "127.0.0.1"
                            string Slot = match.Groups["Slot"].Value;        // "1111"
                            string CpuType= match.Groups["CpuType"].Value;
                            string PlcAddress = match.Groups["PlcAddress"].Value;
                            if (string.IsNullOrEmpty(ClassName) || string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(Slot) || string.IsNullOrEmpty(CpuType) || string.IsNullOrEmpty(PlcAddress))
                                break;
                        if (Communicator == null)
                        {
                            Debug.WriteLine("BaseCommunicator not found.");
                            return false;
                        }
                            //利用反射创建实例
                            Type type = IProcess.GetExecutingAssembly().GetType(ClassName);
                            if (type == null)
                            {
                                Debug.WriteLine("Class not found.");
                                return false;
                            }
                            var Communicator = Activator.CreateInstance(type, CommunicatorName) as BaseCommunicator;
                        //TCP客户端最后再连接
                        if (Communicator is TCPClient)
                        {
                            clientsCommunicatorsConnectionString.TryAdd(CommunicatorConnectionString.Key, CommunicatorConnectionString.Value);
                            continue;
                        }
                            if (Communicator == null)
                            {
                                Debug.WriteLine("BaseCommunicator not found.");
                                return false;
                            }
                        Communicator.CommunicatorConnections.Add("地址", IP);
                        Communicator.CommunicatorConnections.Add("端口", PORT);
                        Communicator.CommunicatorName = CommunicatorName;
                        if (!Communicator.Connect())
                        {
                            LogInfo($"初始化通讯口[{CommunicatorName}]失败,原因是{Communicator.Msg}", LogInfoType.ERROR);
                            Communicator.CommunicatorConnections.Add("地址", IP);
                            Communicator.CommunicatorConnections.Add("端口", Slot);
                            Communicator.CommunicatorConnections.Add("型号", CpuType);
                            Communicator.CommunicatorConnections.Add("变量地址", PlcAddress);
                            Communicator.CommunicatorName = CommunicatorName;
                            if (!Communicator.Connect())
                            {
                                LogInfo($"初始化通讯口[{CommunicatorName}]失败,原因是{Communicator.Msg}", LogInfoType.ERROR);
                            }
                            else
                            {
                                LogInfo($"初始化通讯口[{CommunicatorName}]成功", LogInfoType.PASS);
                            }
                            GlobalVar.dicCommunicators.TryAdd(CommunicatorName, Communicator);
                        }
                        else
                        {
                            LogInfo($"初始化通讯口[{CommunicatorName}]成功", LogInfoType.PASS);
                            Debug.WriteLine("No match found.");
                        }
                        GlobalVar.dicCommunicators.TryAdd(CommunicatorName, Communicator);
                    }
                    else
                    {
                        Debug.WriteLine("No match found.");
                    }
                        // å®šä¹‰æ­£åˆ™è¡¨è¾¾å¼ä»¥æå–协议、IP åœ°å€å’Œç«¯å£
                        //1.    \((.*?)\):\(和 \) æ˜¯ç”¨äºŽåŒ¹é…æ‹¬å·çš„转义字符。
                        //      (.*?) æ˜¯ä¸€ä¸ªéžè´ªå©ªçš„匹配,用来匹配类名(MyProcesses.Communicators.TCPServer æˆ– MyProcesses.Communicators.UARTPort)。
                        //2.    ([^:] +):匹配冒号之前的部分,即地址(127.0.0.1 æˆ– COM5)。这里使用了[^:] æ¥åŒ¹é…é™¤äº†å†’号之外的任意字符。
                        //3.    (\d +) ï¼šåŒ¹é…ç«¯å£å·ï¼Œç¡®ä¿å®ƒåŒ¹é…ä¸€ä¸ªæˆ–多个数字。
                        string pattern = @"^\((?<ClassName>[^)]+)\)\[(?<IP>[^]]+)\]\[(?<PORT>[^]]+)\]$";
                        Match match = Regex.Match(CommunicatorAddress, pattern);
                        if (match.Success)
                        {
                            string ClassName = match.Groups["ClassName"].Value;   // "TCP"
                            string IP = match.Groups["IP"].Value;          // "127.0.0.1"
                            string PORT = match.Groups["PORT"].Value;        // "1111"
                            if (string.IsNullOrEmpty(ClassName) || string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(PORT))
                                break;
                            //利用反射创建实例
                            Type type = IProcess.GetExecutingAssembly().GetType(ClassName);
                            if (type == null)
                            {
                                Debug.WriteLine("Class not found.");
                                return false;
                            }
                            var Communicator = Activator.CreateInstance(type, CommunicatorName) as BaseCommunicator;
                            if (Communicator == null)
                            {
                                Debug.WriteLine("BaseCommunicator not found.");
                                return false;
                            }
                            //TCP客户端最后再连接
                            if (Communicator is TCPClient)
                            {
                                clientsCommunicatorsConnectionString.TryAdd(CommunicatorConnectionString.Key, CommunicatorConnectionString.Value);
                                continue;
                            }
                            Communicator.CommunicatorConnections.Add("地址", IP);
                            Communicator.CommunicatorConnections.Add("端口", PORT);
                            Communicator.CommunicatorName = CommunicatorName;
                            if (!Communicator.Connect())
                            {
                                LogInfo($"初始化通讯口[{CommunicatorName}]失败,原因是{Communicator.Msg}", LogInfoType.ERROR);
                            }
                            else
                            {
                                LogInfo($"初始化通讯口[{CommunicatorName}]成功", LogInfoType.PASS);
                            }
                            GlobalVar.dicCommunicators.TryAdd(CommunicatorName, Communicator);
                        }
                        else
                        {
                            Debug.WriteLine("No match found.");
                        }
                    }
                }
                //TCP客户端最后连接
@@ -930,13 +988,26 @@
                    string ClassName = item.Value.GetType().FullName;// "TCP"
                    string IP = item.Value.CommunicatorConnections["地址"].ToString();//"127.0.0.1"
                    string PORT = item.Value.CommunicatorConnections["端口"].ToString();//"1111"
                    if (string.IsNullOrEmpty(ClassName) || string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(PORT))
                    if (!string.IsNullOrEmpty(ClassName) && ClassName.Contains("SiemensLBS7"))
                    {
                        break;
                        string CpuType = item.Value.CommunicatorConnections["型号"].ToString();
                        string PlcAddress = item.Value.CommunicatorConnections["变量地址"].ToString();
                        if (string.IsNullOrEmpty(ClassName) || string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(PORT) || string.IsNullOrEmpty(CpuType) || string.IsNullOrEmpty(PlcAddress))
                        {
                            break;
                        }
                        string CommunicatorConnectionString = $"({ClassName})[{IP}][{PORT}][{CpuType}][{PlcAddress}]";
                        GlobalVar.allCommunicatorsConnectionString.TryAdd(item.Key, CommunicatorConnectionString);
                    }
                    string CommunicatorConnectionString = $"({ClassName})[{IP}][{PORT}]";
                    GlobalVar.allCommunicatorsConnectionString.TryAdd(item.Key, CommunicatorConnectionString);
                    else
                    {
                        if (string.IsNullOrEmpty(ClassName) || string.IsNullOrEmpty(IP) || string.IsNullOrEmpty(PORT))
                        {
                            break;
                        }
                        string CommunicatorConnectionString = $"({ClassName})[{IP}][{PORT}]";
                        GlobalVar.allCommunicatorsConnectionString.TryAdd(item.Key, CommunicatorConnectionString);
                    }
                }
                var settings = new JsonSerializerSettings
                {
LB_SmartVisionCommon/UserData.cs
@@ -33,12 +33,14 @@
        /// å‘˜å·¥è´¦å·
        /// </summary>
        [Category("RecordUserData"), PropertyOrder(3)]
        [DisplayName("账号")]
        [DisplayName("员工账号")]
        [Browsable(true)]
        public string EmployeeAccount { get; set; }
        /// <summary>
        /// å‘˜å·¥å¯†ç 
        /// </summary>
        [Category("RecordUserData"), PropertyOrder(4)]
        [DisplayName("员工密码")]
        [Browsable(false)]
        public string EmployeePassword { get; set; }
        /// <summary>
LB_VisionFlowNode/IFlowContext.cs
@@ -283,6 +283,9 @@
        [Node("Halcon2D斑点工具", "Haclon2D工具", "Basic", "Halcon2D斑点工具")]
        public void Halcon2D斑点工具(FlowNode node) { RunNodeAsync(node); }
        [Node("通讯模块", "通讯模块工具", "Basic", "通讯模块")]
        public void é€šè®¯æ¨¡å—(FlowNode node) { RunNodeAsync(node); }
        #endregion
LB_VisionProcesses/Cameras/2DCameraForm.cs
@@ -502,8 +502,11 @@
                    Task.Factory.StartNew(() =>
                    {
                        camera.StopGrabbing();
                        camera.SetTriggerMode(TriggerMode.On, TriggerSource.Software);
                        camera.StartGrabbing();
                        if (brand != CameraBrand.LBCamera)
                        {
                            camera.SetTriggerMode(TriggerMode.On, TriggerSource.Software);
                            camera.StartGrabbing();
                        }
                    });
                }
LB_VisionProcesses/Cameras/BaseCamera.cs
@@ -14,7 +14,15 @@
        protected BaseCamera() { }
        #region Parm
        /// <summary>
        /// ç›¸æœºSN号
        /// </summary>
        public string SN { get; set; } = string.Empty;
        /// <summary>
        /// ç›¸æœºåç§°
        /// </summary>
        public string CameraName { get; set; } = string.Empty;
        public CameraBrand Brand { get; set; } = CameraBrand.UNSUPPORTED;
        public bool isGrabbing = false;
@@ -64,96 +72,24 @@
        public abstract bool InitDevice(string SN, object Handle = null);
        public bool StartWith_SoftTriggerModel()
        {
            SetTriggerMode(TriggerMode.Off, TriggerSource.Software);
            return StartGrabbing();
        }
        public abstract bool StartWith_SoftTriggerModel();
        public bool StartWith_HardTriggerModel(TriggerSource hardtriggeritem = TriggerSource.Line0)
        {
            if (hardtriggeritem == TriggerSource.Software) hardtriggeritem = TriggerSource.Line0;
            SetTriggerMode(TriggerMode.On, hardtriggeritem);
            return StartGrabbing();
        }
        public abstract bool StartWith_HardTriggerModel(TriggerSource hardtriggeritem = TriggerSource.Line0);
        /// <summary>
        /// ç­‰å¾…硬触发获取图像
        /// ç­‰å¾…软/硬触发获取图像
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="outtime"></param>
        /// <returns></returns>
        public bool GetImage(out Bitmap bitmap, int outtime = 3000)
        {
            bitmap = null;
            try
            {
                // è®¾ç½®è¶…æ—¶æ—¶é—´
                DateTime lastTime = DateTime.Now.AddMilliseconds(outtime);
                // åˆ¤æ–­æ˜¯å¦è¶…æ—¶
                while (lastTime > DateTime.Now)// è®¾ç½®è¶…时时间为 3 ç§’
                {
                    if (CallBackImg != null)
                    {
                        lock (CallBackImg)
                        {
                            // ä¿å­˜æ—§ Bitmap å¹¶é‡Šæ”¾
                            bitmap = CallBackImg.Clone() as Bitmap; // åˆ›å»ºå‰¯æœ¬
                        }
                        // é‡Šæ”¾æ—§èµ„源
                        CallBackImg.Dispose();
                        CallBackImg = null;
                        return true;
                    }
                }
                return false;
            }
            catch { return bitmap == null ? false : true; }
        }
        public abstract bool GetImage(out Bitmap bitmap, int outtime = 3000);
        /// <summary>
        /// è½¯è§¦å‘获取图像
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="outtime"></param>
        /// <returns></returns>
        public bool GetImageWithSoftTrigger(out Bitmap bitmap, int outtime = 3000)
        {
            if (!isGrabbing)
                StartGrabbing();
            GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource);
            if (triggerMode != TriggerMode.On && triggerSource != TriggerSource.Software)
                SetTriggerMode(TriggerMode.On, TriggerSource.Software);
            bitmap = null;
            CallBackImg = null;
            if (!SoftTrigger())
                return false;
            // å¼€å§‹æ—¶é—´
            DateTime startTime = DateTime.Now; // å½“前时间
            // åˆ¤æ–­æ˜¯å¦è¶…æ—¶
            while (DateTime.Now < startTime.AddMilliseconds(outtime))// è®¾ç½®è¶…时时间为 3 ç§’
            {
                GetImage(out bitmap, 50);
                if (bitmap != null)
                    break;
                Thread.Sleep(10);
            }
            if (triggerMode != TriggerMode.On)
                SetTriggerMode(TriggerMode.On, triggerSource);
            return (bitmap != null);
        }
        public abstract bool GetImageWithSoftTrigger(out Bitmap bitmap, int outtime = 3000);
        /// <summary>
        /// è½¯è§¦å‘
@@ -195,40 +131,8 @@
        #endregion
        #region SettingConfig
        public void SetCamConfig(CameraConfig config)
        {
            if (Enum.TryParse(config.Params.Inputs["触发模式"].ToString(), out TriggerMode TriggerMode)
                && Enum.TryParse(config.Params.Inputs["触发方式"].ToString(), out TriggerSource TriggerSource)
                && Enum.TryParse(config.Params.Inputs["触发极性"].ToString(), out TriggerPolarity TriggerPolarity)
                )
            {
                SetTriggerMode(TriggerMode, TriggerSource);
                SetTriggerPolarity(TriggerPolarity);
                SetTriggerFliter(Convert.ToDouble(config.Params.Inputs["触发消抖"].ToString()));
                SetTriggerDelay(Convert.ToDouble(config.Params.Inputs["触发延时"].ToString()));
                SetExpouseTime(Convert.ToDouble(config.Params.Inputs["曝光时间"].ToString()));
                SetGain(Convert.ToDouble(config.Params.Inputs["增益"].ToString()));
            }
        }
        public void GetCamConfig(out CameraConfig config)
        {
            GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource);
            GetTriggerPolarity(out TriggerPolarity triggerPolarity);
            GetTriggerFliter(out double triggerfilter);
            GetTriggerDelay(out double triggerdelay);
            GetExpouseTime(out double expouseTime);
            GetGain(out double gain);
            config = new CameraConfig(null);
            config.Params.Inputs.Add("触发模式", triggerMode);
            config.Params.Inputs.Add("触发方式", triggerSource);
            config.Params.Inputs.Add("触发极性", triggerPolarity);
            config.Params.Inputs.Add("触发消抖", triggerfilter);
            config.Params.Inputs.Add("触发延时", triggerdelay);
            config.Params.Inputs.Add("曝光时间", expouseTime);
            config.Params.Inputs.Add("增益", gain);
        }
        public abstract void SetCamConfig(CameraConfig config);
        public abstract void GetCamConfig(out CameraConfig config);
        /// <summary>
        /// è®¾ç½®è§¦å‘模式及触发源
LB_VisionProcesses/Cameras/HRCameras/HRCamera.cs
@@ -1130,6 +1130,123 @@
            GC.SuppressFinalize(this);
        }
        public override bool StartWith_SoftTriggerModel()
        {
            SetTriggerMode(TriggerMode.Off, TriggerSource.Software);
            return StartGrabbing();
        }
        public override bool StartWith_HardTriggerModel(TriggerSource hardtriggeritem = TriggerSource.Line0)
        {
            if (hardtriggeritem == TriggerSource.Software) hardtriggeritem = TriggerSource.Line0;
            SetTriggerMode(TriggerMode.On, hardtriggeritem);
            return StartGrabbing();
        }
        public override bool GetImage(out Bitmap bitmap, int outtime = 3000)
        {
            bitmap = null;
            try
            {
                // è®¾ç½®è¶…æ—¶æ—¶é—´
                DateTime lastTime = DateTime.Now.AddMilliseconds(outtime);
                // åˆ¤æ–­æ˜¯å¦è¶…æ—¶
                while (lastTime > DateTime.Now)// è®¾ç½®è¶…时时间为 3 ç§’
                {
                    if (CallBackImg != null)
                    {
                        lock (CallBackImg)
                        {
                            // ä¿å­˜æ—§ Bitmap å¹¶é‡Šæ”¾
                            bitmap = CallBackImg.Clone() as Bitmap; // åˆ›å»ºå‰¯æœ¬
                        }
                        // é‡Šæ”¾æ—§èµ„源
                        CallBackImg.Dispose();
                        CallBackImg = null;
                        return true;
                    }
                }
                return false;
            }
            catch { return bitmap == null ? false : true; }
        }
        public override bool GetImageWithSoftTrigger(out Bitmap bitmap, int outtime = 3000)
        {
            if (!isGrabbing)
                StartGrabbing();
            GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource);
            if (triggerMode != TriggerMode.On && triggerSource != TriggerSource.Software)
                SetTriggerMode(TriggerMode.On, TriggerSource.Software);
            bitmap = null;
            CallBackImg = null;
            if (!SoftTrigger())
                return false;
            // å¼€å§‹æ—¶é—´
            DateTime startTime = DateTime.Now; // å½“前时间
            // åˆ¤æ–­æ˜¯å¦è¶…æ—¶
            while (DateTime.Now < startTime.AddMilliseconds(outtime))// è®¾ç½®è¶…时时间为 3 ç§’
            {
                GetImage(out bitmap, 50);
                if (bitmap != null)
                    break;
                Thread.Sleep(10);
            }
            if (triggerMode != TriggerMode.On)
                SetTriggerMode(TriggerMode.On, triggerSource);
            return (bitmap != null);
        }
        public override void SetCamConfig(CameraConfig config)
        {
            if (Enum.TryParse(config.Params.Inputs["触发模式"].ToString(), out TriggerMode TriggerMode)
                && Enum.TryParse(config.Params.Inputs["触发方式"].ToString(), out TriggerSource TriggerSource)
                && Enum.TryParse(config.Params.Inputs["触发极性"].ToString(), out TriggerPolarity TriggerPolarity)
                )
            {
                SetTriggerMode(TriggerMode, TriggerSource);
                SetTriggerPolarity(TriggerPolarity);
                SetTriggerFliter(Convert.ToDouble(config.Params.Inputs["触发消抖"].ToString()));
                SetTriggerDelay(Convert.ToDouble(config.Params.Inputs["触发延时"].ToString()));
                SetExpouseTime(Convert.ToDouble(config.Params.Inputs["曝光时间"].ToString()));
                SetGain(Convert.ToDouble(config.Params.Inputs["增益"].ToString()));
            }
        }
        public override void GetCamConfig(out CameraConfig config)
        {
            GetTriggerMode(out TriggerMode triggerMode, out TriggerSource triggerSource);
            GetTriggerPolarity(out TriggerPolarity triggerPolarity);
            GetTriggerFliter(out double triggerfilter);
            GetTriggerDelay(out double triggerdelay);
            GetExpouseTime(out double expouseTime);
            GetGain(out double gain);
            config = new CameraConfig(null);
            config.Params.Inputs.Add("触发模式", triggerMode);
            config.Params.Inputs.Add("触发方式", triggerSource);
            config.Params.Inputs.Add("触发极性", triggerPolarity);
            config.Params.Inputs.Add("触发消抖", triggerfilter);
            config.Params.Inputs.Add("触发延时", triggerdelay);
            config.Params.Inputs.Add("曝光时间", expouseTime);
            config.Params.Inputs.Add("增益", gain);
        }
        #endregion
    }
}
LB_VisionProcesses/Cameras/ICamera.cs
@@ -63,7 +63,7 @@
        bool StartWith_HardTriggerModel(TriggerSource hardtriggeritem = TriggerSource.Line0);
        /// <summary>
        /// ç­‰å¾…硬触发获取图像
        /// ç­‰å¾…软/硬触发获取图像
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="outtime"></param>
LB_VisionProcesses/Cameras/LBCameras/LBCamera.cs
@@ -30,6 +30,12 @@
        private readonly object _bufferLock = new object();
        private int _currentLineCount = 0;
        // æ˜¾ç¤ºå¥æŸ„和函数(与LLSystem示例保持一致)
        private IntPtr _lightPic = IntPtr.Zero;
        private IntPtr _deepPic = IntPtr.Zero;
        private IntPtr _pointPic = IntPtr.Zero;
        private IntPtr _outlinePic = IntPtr.Zero;
        public LBCamera()
        {
            Brand = CameraBrand.LBCamera;
@@ -93,7 +99,7 @@
                if (!found)
                {
                    // å¦‚果没找到但 sn æœ¬èº«çœ‹èµ·æ¥åƒ IP,尝试直接连接(兼容旧逻辑)
                    // å¦‚果没找到但 sn æœ¬èº«çœ‹èµ·æ¥åƒ IP,尝试直接连接
                    if (System.Net.IPAddress.TryParse(sn, out _))
                    {
                        targetIp = sn;
@@ -123,7 +129,7 @@
                    _acquisitionCallback = new AcquisitionCallbackZA(OnLineReceived);
                    _acquisitionCompletedCallback = new AcquisitionCompletedCallback(OnAcquisitionCompleted);
                    
                    PHM6000Profiler.SetAcquisitionCallbackZA(_cameraHandle, _acquisitionCallback, IntPtr.Zero);
                    //PHM6000Profiler.SetAcquisitionCallbackZA(_cameraHandle, _acquisitionCallback, IntPtr.Zero);
                    PHM6000Profiler.RegisterAcquisitionCompletedCallback(_cameraHandle, _acquisitionCompletedCallback, IntPtr.Zero);
                    
                    return true;
@@ -197,6 +203,63 @@
                _currentLineCount = 0;
            }
            // ç¦ç”¨è¡Œå›žè°ƒ
            PHM6000Profiler.SetAcquisitionCallbackZA(_cameraHandle, IntPtr.Zero, IntPtr.Zero);
            // è®¾ç½®é‡‡é›†æ¨¡å¼ï¼š1=扫描模式,1=连续模式
            PHM6000Profiler.SetAcquisitionMode(_cameraHandle, 1, 1);
            int result = PHM6000Profiler.StartAcquisition(_cameraHandle, 0, 0, 0.0);
            if (result == 0)
            {
                isGrabbing = true;
                return true;
            }
            return false;
        }
        /// <summary>
        /// å•次采集模式(适用于线扫相机)
        /// è®¾ç½®é‡‡é›†æ¨¡å¼ä¸ºæ‰«ææ¨¡å¼ï¼Œå•次触发
        /// </summary>
        public bool StartSingleGrab()
        {
            if (!_isConnected) return false;
            lock (_bufferLock)
            {
                _lineDataBuffer.Clear();
                _currentLineCount = 0;
            }
            // ç¦ç”¨è¡Œå›žè°ƒï¼ˆä¸Žç¤ºä¾‹ä¸€è‡´ï¼‰
            PHM6000Profiler.SetAcquisitionCallbackZA(_cameraHandle, IntPtr.Zero, IntPtr.Zero);
            // è®¾ç½®é‡‡é›†æ¨¡å¼ï¼š1=扫描模式,0=单次模式
            PHM6000Profiler.SetAcquisitionMode(_cameraHandle, 1, 0);
            int result = PHM6000Profiler.StartAcquisition(_cameraHandle, 0, 0, 0.0);
            if (result == 0)
            {
                isGrabbing = true;
                return true;
            }
            return false;
        }
        /// <summary>
        /// è¿žç»­é‡‡é›†æ¨¡å¼ï¼ˆé€‚用于线扫相机)
        /// è®¾ç½®é‡‡é›†æ¨¡å¼ä¸ºæ‰«ææ¨¡å¼ï¼Œè¿žç»­è§¦å‘
        /// </summary>
        public bool StartContinuousGrab()
        {
            if (!_isConnected) return false;
            lock (_bufferLock)
            {
                _lineDataBuffer.Clear();
                _currentLineCount = 0;
            }
            // ç¦ç”¨è¡Œå›žè°ƒï¼ˆä¸Žç¤ºä¾‹ä¸€è‡´ï¼‰
            PHM6000Profiler.SetAcquisitionCallbackZA(_cameraHandle, IntPtr.Zero, IntPtr.Zero);
            // è®¾ç½®é‡‡é›†æ¨¡å¼ï¼š1=扫描模式,1=连续模式
            PHM6000Profiler.SetAcquisitionMode(_cameraHandle, 1, 1);
            int result = PHM6000Profiler.StartAcquisition(_cameraHandle, 0, 0, 0.0);
@@ -214,6 +277,19 @@
            PHM6000Profiler.StopAcquisition(_cameraHandle);
            isGrabbing = false;
            return true;
        }
        public override bool StartWith_SoftTriggerModel()
        {
            // å¯¹äºŽLBCamera(线扫相机),软件触发连续采集使用连续采集模式
            return StartContinuousGrab();
        }
        public override bool StartWith_HardTriggerModel(TriggerSource hardtriggeritem = TriggerSource.Line0)
        {
            // å¯¹äºŽLBCamera(线扫相机),硬件触发也使用连续采集模式
            // å¤–部硬件信号会触发相机开始采集
            return StartContinuousGrab();
        }
        public override bool SoftTrigger()
@@ -242,6 +318,56 @@
        public override bool SetLineStatus(IOLines line, LineStatus linestatus) => true;
        public override bool GetLineStatus(IOLines line, out LineStatus lineStatus) { lineStatus = LineStatus.Low; return true; }
        public override bool AutoBalanceWhite() => true;
        // ä¸å®žçŽ°çš„æ–¹æ³•
        public override void SetCamConfig(CameraConfig config) { }
        public override void GetCamConfig(out CameraConfig config) { config = new CameraConfig(null); }
        public override bool GetImage(out Bitmap bitmap, int outtime = 3000) { bitmap = null; return false; }
        public override bool GetImageWithSoftTrigger(out Bitmap bitmap, int outtime = 3000)
        {
            bitmap = null;
            if (!_isConnected) return false;
            using (AutoResetEvent waitHandle = new AutoResetEvent(false))
            {
                Bitmap captured = null;
                EventHandler<CameraEventArgs> handler = (s, e) =>
                {
                    try
                    {
                        if (e.Bitmap != null)
                        {
                            captured = e.Bitmap.Clone() as Bitmap;
                        }
                    }
                    catch (Exception ex)
                    {
                        AsyncLogHelper.Error($"LBCamera: GetImageWithSoftTrigger clone error - {ex.Message}");
                    }
                    waitHandle.Set();
                };
                this.ImageGrabbed += handler;
                try
                {
                    if (StartSingleGrab())
                    {
                        if (waitHandle.WaitOne(outtime))
                        {
                            bitmap = captured;
                            return bitmap != null;
                        }
                    }
                }
                finally
                {
                    this.ImageGrabbed -= handler;
                    StopGrabbing();
                }
            }
            return false;
        }
        public PHM6000SensorConfig GetSensorConfig()
        {
@@ -282,27 +408,58 @@
        private void OnAcquisitionCompleted(IntPtr pInstance, int nOption)
        {
                // nOption: 0=一批数据结束, 1=全部完成, 2=点云就绪
                if (nOption == 1 || nOption == 0)
                // æ ¹æ®SDK文档:nOption为0时表示一批数据结束,为1时表示全部采集完成
                // ä¸ºäº†å…¼å®¹æ€§ï¼Œä¹Ÿå¤„理nOption == 2(点云就绪)
                // æ­¤æ—¶ä½¿ç”¨ä¸»åŠ¨èŽ·å–æ–¹å¼æ›¿ä»£å¯èƒ½ä¼šå¯¼è‡´crash的GenerateIntensityMap
                if (nOption == 0 || nOption == 1 || nOption == 2)
                {
                    GenerateIntensityMap();
                    RetrieveDataAndGenerateImage();
                }
        }
        private void GenerateIntensityMap()
        private void RetrieveDataAndGenerateImage()
        {
            if (_cameraHandle == IntPtr.Zero) return;
            int width = 0;
            int height = 0;
            // ç›´æŽ¥ä»Ž SDK èŽ·å–åˆå¹¶åŽçš„å¼ºåº¦æ•°æ®æŒ‡é’ˆ (unsigned char*)
            IntPtr pIntensity = PHM6000Profiler.GetIntensityData(_cameraHandle, ref width, ref height);
            if (pIntensity == IntPtr.Zero || width <= 0 || height <= 0) return;
            try
            {
                List<byte[]> lineBuffers = new List<byte[]>();
                ulong index = 0;
                IntPtr ptr = IntPtr.Zero;
                // åƒç¤ºä¾‹ä¸€æ ·é€šè¿‡ç´¢å¼•获取行数据
                while ((ptr = PHM6000Profiler.GetLineDataByIndex(_cameraHandle, index)) != IntPtr.Zero)
                {
                    try
                    {
                        LBLineDataZA lineData = PHM6000Profiler.ConvertToLBLineDataZA(ptr);
                        // æå–强度数据 (Alpha通道)
                        if (lineData.data != null && lineData.data.Length > 0)
                        {
                            int lineWidth = lineData.data.Length;
                            byte[] intensityLine = new byte[lineWidth];
                            for (int i = 0; i < lineWidth; i++)
                            {
                                intensityLine[i] = lineData.data[i].alpha;
                            }
                            lineBuffers.Add(intensityLine);
                        }
                    }
                    catch (Exception ex)
                    {
                        // å¿½ç•¥å•行转换错误
                    }
                    index++;
                }
                if (lineBuffers.Count == 0) return;
                int height = lineBuffers.Count;
                int width = lineBuffers[0].Length;
                if (width <= 0 || height <= 0) return;
                Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
                
                // è®¾ç½®ç°åº¦è°ƒè‰²æ¿
@@ -312,20 +469,30 @@
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
                
                // é«˜æ€§èƒ½å†…存拷贝
                int size = width * height;
                byte[] managedData = new byte[size];
                Marshal.Copy(pIntensity, managedData, 0, size);
                Marshal.Copy(managedData, 0, bmpData.Scan0, size);
                bmp.UnlockBits(bmpData);
                try
                {
                    int stride = bmpData.Stride;
                    IntPtr scan0 = bmpData.Scan0;
                    for (int y = 0; y < height; y++)
                    {
                        if (lineBuffers[y].Length == width) // ç¡®ä¿å®½åº¦ä¸€è‡´
                        {
                            Marshal.Copy(lineBuffers[y], 0, scan0 + y * stride, width);
                        }
                    }
                }
                finally
                {
                    bmp.UnlockBits(bmpData);
                }
                // è§¦å‘事件通知 UI æ›´æ–°äº®åº¦å›¾
                ImageGrabbed?.Invoke(this, new CameraEventArgs(SN, bmp));
            }
            catch (Exception ex)
            {
                AsyncLogHelper.Error($"LBCamera: ç”Ÿæˆäº®åº¦å›¾å¼‚常 - {ex.Message}");
                AsyncLogHelper.Error($"LBCamera: ç”Ÿæˆå›¾åƒå¼‚常 - {ex.Message}");
            }
        }
LB_VisionProcesses/Communicators/CommunicatorConfig.cs
@@ -7,7 +7,7 @@
namespace LB_VisionProcesses.Communicators
{
    [Serializable]
    [Process("通讯测试", Category = "通讯工具", Description = "创建通讯测试工具")]
    [Process("通讯模块", Category = "通讯工具", Description = "创建通讯模块工具")]
    public class CommunicatorConfig : IProcess
    {
        /// <summary>
@@ -19,7 +19,7 @@
        {
            this.dicCommunicators = dicCommunicators;
            strProcessName = "通讯测试";
            strProcessName = "通讯模块";
            strProcessClass = "LB_VisionProcesses.Communicators.CommunicatorConfig";
            Params.Inputs.Add("通讯口名", "");
LB_VisionProcesses/Communicators/CommunicatorForm.Designer.cs
@@ -54,6 +54,8 @@
            label6 = new Label();
            themeForm1 = new ReaLTaiizor.Forms.ThemeForm();
            controlBox1 = new ReaLTaiizor.Controls.ControlBox();
            lblAddress = new Label();
            txtAddress = new TextBox();
            grpSetting.SuspendLayout();
            grpReceive.SuspendLayout();
            grpTest.SuspendLayout();
@@ -102,7 +104,7 @@
            // 
            // btnDisconnect
            // 
            btnDisconnect.ForeColor = SystemColors.WindowText;
            btnDisconnect.ForeColor = SystemColors.Control;
            btnDisconnect.Location = new Point(118, 109);
            btnDisconnect.Name = "btnDisconnect";
            btnDisconnect.Size = new Size(75, 23);
@@ -113,7 +115,7 @@
            // 
            // btnConnect
            // 
            btnConnect.ForeColor = SystemColors.WindowText;
            btnConnect.ForeColor = SystemColors.Control;
            btnConnect.Location = new Point(14, 109);
            btnConnect.Name = "btnConnect";
            btnConnect.Size = new Size(75, 23);
@@ -185,11 +187,13 @@
            // grpTest
            // 
            grpTest.Controls.Add(lblCom);
            grpTest.Controls.Add(txtAddress);
            grpTest.Controls.Add(ckbRuleCheck);
            grpTest.Controls.Add(cmbCom);
            grpTest.Controls.Add(cmbType);
            grpTest.Controls.Add(lblType);
            grpTest.Controls.Add(btnRun);
            grpTest.Controls.Add(lblAddress);
            grpTest.Controls.Add(lblMsg);
            grpTest.Controls.Add(txtMsg);
            grpTest.ForeColor = SystemColors.Control;
@@ -269,6 +273,7 @@
            // 
            txtMsg.Location = new Point(59, 76);
            txtMsg.Name = "txtMsg";
            txtMsg.ReadOnly = true;
            txtMsg.Size = new Size(245, 23);
            txtMsg.TabIndex = 0;
            // 
@@ -329,6 +334,23 @@
            controlBox1.TabIndex = 0;
            controlBox1.Text = "controlBox1";
            // 
            // lblAddress
            //
            lblAddress.AutoSize = true;
            lblAddress.Location = new Point(20, 115);
            lblAddress.Name = "lblAddress";
            lblAddress.Size = new Size(32, 17);
            lblAddress.TabIndex = 0;
            lblAddress.Text = "地址";
            //
            // txtAddress
            //
            txtAddress.Location = new Point(59, 112);
            txtAddress.Name = "txtAddress";
            txtAddress.ReadOnly = true;
            txtAddress.Size = new Size(94, 23);
            txtAddress.TabIndex = 6;
            //
            // CommunicatorForm
            // 
            AutoScaleDimensions = new SizeF(96F, 96F);
@@ -388,5 +410,7 @@
        private Label label6;
        private ReaLTaiizor.Forms.ThemeForm themeForm1;
        private ReaLTaiizor.Controls.ControlBox controlBox1;
        private TextBox txtAddress;
        private Label lblAddress;
    }
}
LB_VisionProcesses/Communicators/CommunicatorForm.cs
@@ -85,6 +85,7 @@
                cmbType.Text = type.ToString();
                ckbRuleCheck.Checked = bRuleCheck;
                txtMsg.Text = Msg;
                this.grpSetting.ForeColor= SystemColors.Control;
            }
        }
@@ -115,12 +116,16 @@
                cmbIP.Visible = true;
                txtIP.Visible = false;
                this.lblAddress.Visible = false;
                this.txtAddress.Visible = false;
                lblIP.Text = "串口号";
                lblIP.Text = "波特率";
                cmbIP.Text = communicator.CommunicatorConnections["地址"].ToString();
                txtPort.Text = communicator.CommunicatorConnections["端口"].ToString();
                this.grpSetting.ForeColor = SystemColors.Control;
            }
            else if (communicator is LocalMonitor)
            {
@@ -129,31 +134,40 @@
                cmbIP.Visible = false;
                txtIP.Visible = true;
                this.lblAddress.Visible = false;
                this.txtAddress.Visible = false;
                lblIP.Text = "监控文件";
                lblIP.Text = "写入文件";
                txtIP.Text = communicator.CommunicatorConnections["地址"].ToString();
                txtPort.Text = communicator.CommunicatorConnections["端口"].ToString();
                this.grpSetting.ForeColor = SystemColors.Control;
            }
            else if (communicator is SiemensLBS7)
            {
                btnSend.Enabled = communicator.bConnected;
                btnRuleSend.Enabled = communicator.bConnected;
                cmbIP.Visible = false;
                txtIP.Visible = true;
                this.lblAddress.Visible = true;
                this.txtAddress.Visible = true;
                lblIP.Text = "IP";
                lblIP.Text = "æ§½";
                txtIP.Text = communicator.CommunicatorConnections["地址"].ToString();
                txtPort.Text = communicator.CommunicatorConnections["æ§½"].ToString();
                txtPort.Text = communicator.CommunicatorConnections["端口"].ToString();
                this.txtAddress.Text = communicator.CommunicatorConnections["变量地址"]?.ToString();
                this.grpSetting.ForeColor = SystemColors.Control;
                btnRuleSend.Visible = false;
            }
            else
            {
                btnSend.Enabled = communicator.bConnected;
                btnRuleSend.Enabled = false;
                this.lblAddress.Visible = false;
                this.txtAddress.Visible = false;
                cmbIP.Visible = false;
                txtIP.Visible = true;
LB_VisionProcesses/Communicators/CommunicatorForm.resx
@@ -121,7 +121,7 @@
  <data name="themeForm1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
    <value>
        iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
        wgAADsIBFShKgAAAA+JJREFUWEftVkuIHGUQbt34iMEoq2S2q3p3yTJsuqpnJ+L6WBRdwaigohfFgEou
        wQAADsEBuJFr7QAAA+JJREFUWEftVkuIHGUQbt34iMEoq2S2q3p3yTJsuqpnJ+L6WBRdwaigohfFgEou
        BkVBUPCBCEG8BXNQ48GLeImCehIJxMsqhsSZruod46K4HtSIGjU+4iOr0c1I9fSsM3/PLNtCPO0H36m/
        evz/X11VnreKVRTAHPOZumkY6lFwqURws3BwZ8ywTQnuiCP/+iTCzY1qaUPzdm/Atf3PaHre6XPsjyQM
        2xLGV4XwIyX4QRj+UMK/lPFvJTyhBMeV8IgQvmRJuH4Ko+l5p9UjGBbCJ5RxTgj+VMZmXxKcSAjekHE/
LB_VisionProcesses/Communicators/SiemensS7/SiemensLBS7.cs
@@ -32,6 +32,7 @@
                short slot;
                short.TryParse(CommunicatorConnections["端口"].ToString(), out slot);
                S7.Net.CpuType cpuType = (CpuType)CommunicatorConnections["型号"];
                variable = CommunicatorConnections["变量地址"].ToString();
                plc = new Plc(cpuType, IP, 0, slot);
                plc.Open();
                return true;
@@ -60,8 +61,19 @@
        {
            try
            {
                plc.Write(variable, message);
                return true;
                if (plc!=null)
                {
                    if (string.IsNullOrEmpty(variable))
                    {
                        variable = CommunicatorConnections["变量地址"].ToString();
                    }
                    plc.Write(variable, message);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch
            {