已重命名1个文件
已添加5个文件
已修改23个文件
已删除1个文件
3006 ■■■■■ 文件已修改
LB_SmartVision/CSV/CsvRecordProductData.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/Forms/CreateProductForm.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVision/Forms/Pages/CameraPage/CameraForm.cs 4 ●●● 补丁 | 查看 | 原始文档 | 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 173 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVisionCameraSDK/PHM6000/PHM6000Profiler.cs 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_SmartVisionCommon/UserData.cs 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionFlowNode/IFlowContext.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/2DCameraForm.cs 153 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/BaseCamera.cs 145 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/HRCameras/HRCamera.cs 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/ICamera.cs 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LB_VisionProcesses/Cameras/LBCameras/LBCamera.cs 401 ●●●● 补丁 | 查看 | 原始文档 | 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/CameraPage/CameraForm.cs
@@ -85,7 +85,9 @@
            else
                camera.SetTriggerMode(TriggerMode.On, TriggerSource.Line0);
            camera.StartGrabbing();
            // æ³¨é‡ŠæŽ‰è‡ªåŠ¨å¯åŠ¨é‡‡é›†é€»è¾‘,防止关闭窗体后相机自动连续采集
            // ç”¨æˆ·éœ€è¦æ‰‹åŠ¨ç‚¹å‡»"启动采集"按钮才会启动相机
            // camera.StartGrabbing();
            Unsubscribe();
        }
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();
@@ -698,7 +699,7 @@
                        }
                }
                camera.SN = CameraConnectionString.Key;
                if (!camera.InitDevice(CameraConnectionString.Key, this.Handle))
                if (!camera.InitDevice(CameraConnectionString.Key, IntPtr.Zero))
                {
                    LogInfo($"初始化相机[{CameraConnectionString.Key}]失败", LogInfoType.ERROR);
                    if (camera != null)
@@ -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_SmartVisionCameraSDK/PHM6000/PHM6000Profiler.cs
@@ -874,6 +874,35 @@
        /// <param name="KeepImageSize">图像大小是否保持</param>
        [DllImport("PHM6000API", CallingConvention = CallingConvention.StdCall, EntryPoint = "SetTransformParameter")]
        public static extern void SetTransformParameter(IntPtr pHandle, float fAngle, int xOffset, int yOffset, int hMirror, int vMirror, int KeepImageSize = 0);
        #region PointCloud3D Functions
        [DllImport("PointCloud3D", CallingConvention = CallingConvention.StdCall, EntryPoint = "AddZAPoints")]
        public static extern int AddZAPoints(IntPtr pInstance, IntPtr points, int nCount, float fStepX, float fStepY, int nDownSample);
        [DllImport("PointCloud3D", CallingConvention = CallingConvention.StdCall, EntryPoint = "ClearPCLPoints")]
        public static extern int ClearPCLPoints(IntPtr pInstance);
        [DllImport("PointCloud3D", CallingConvention = CallingConvention.StdCall, EntryPoint = "GetPointCloudBound")]
        public static extern int GetPointCloudBound(IntPtr pInstance, ref double pMin, ref double pMax);
        [DllImport("PointCloud3D", CallingConvention = CallingConvention.StdCall, EntryPoint = "RenderPCLWindow")]
        public static extern void RenderPCLWindow(IntPtr pInstance, int nWidth, int nHeight);
        [DllImport("PointCloud3D", CallingConvention = CallingConvention.StdCall, EntryPoint = "SetLookUpTableRange")]
        public static extern void SetLookUpTableRange(IntPtr pInstance, double fMin, double fMax);
        [DllImport("PointCloud3D", CallingConvention = CallingConvention.StdCall, EntryPoint = "ShowCubeAxes")]
        public static extern void ShowCubeAxes(IntPtr pInstance, int bInit);
        [DllImport("PointCloud3D", CallingConvention = CallingConvention.StdCall, EntryPoint = "ShowLookUpTable")]
        public static extern void ShowLookUpTable(IntPtr pInstance, double x, double y, double xWide, double yWide);
        [DllImport("PointCloud3D", CallingConvention = CallingConvention.StdCall, EntryPoint = "UpdatePCLPointColors")]
        public static extern int UpdatePCLPointColors(IntPtr pInstance, string szColoring);
        #endregion
        /// <summary>
        /// å°†ç»“构体指针转换为double(模拟C++的(double)(int64)&操作)
        /// </summary>
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
@@ -252,7 +252,10 @@
                        return;
                }
                if (cmbSN.Items.Count > 0 && camera.InitDevice(cmbSN.Text.ToString(), this.Handle))
                IntPtr displayHandle = onlinePictureBox.Handle;
                onlinePictureBox.Visible = true;
                if (cmbSN.Items.Count > 0 && camera.InitDevice(cmbSN.Text.ToString(), displayHandle))
                {
                    camera.ImageGrabbed -= GetImageBllComplete;
                    camera.ImageGrabbed += GetImageBllComplete;
@@ -295,8 +298,10 @@
            if (camera.CloseDevice())
            {
                onlinePictureBox.Visible = true;
                MessageBox.Show(camera.SN + "断开成功");
                this.panel_Picture.Controls.Clear();
                this.panel_Picture.Controls.Add(onlinePictureBox);
            }
        }
@@ -382,28 +387,31 @@
        /// <summary>
        /// ç›¸æœºå›žè°ƒè¿è¡Œ
        /// æ³¨æ„ï¼šå¯¹äºŽLBCamera(3D线扫相机),使用SDK自动显示模式
        /// SDK会自动将图像显示到对应的控件上,此回调只用于统计采集次数
        /// </summary>
        /// <param name="CCDName"></param>
        /// <param name="image"></param>
        private void GetImageBllComplete(object sender, CameraEventArgs e)
        {
            // å¯¹äºŽ2D相机(手动处理模式)和现在的LBCamera(手动处理模式)
            if (e.Bitmap == null)
                return;
                lock (e.Bitmap)
            lock (e.Bitmap)
            {
                if (this.InvokeRequired) // æ£€æŸ¥æ˜¯å¦éœ€è¦åœ¨UI线程上调用
                {
                    if (this.InvokeRequired) // æ£€æŸ¥æ˜¯å¦éœ€è¦åœ¨UI线程上调用
                    {
                        this.Invoke(new Action(() =>
                        {
                            onlinePictureBox.Image = e.Bitmap;
                        })); // é€’归调用自身,但这次在UI线程上
                    }
                    else
                    this.Invoke(new Action(() =>
                    {
                        onlinePictureBox.Image = e.Bitmap;
                    }
                    })); // é€’归调用自身,但这次在UI线程上
                }
                else
                {
                    onlinePictureBox.Image = e.Bitmap;
                }
            }
            total.iImageCount++;
            try
            {
@@ -426,23 +434,79 @@
            Task.Factory.StartNew(() =>
            {
                //camera.GetCamConfig(out CameraConfig OriCamConfig);
                // è®¾ç½®æ›å…‰å’Œå¢žç›Š
                camera.SetExpouseTime(Convert.ToDouble(txtExp.Text));
                camera.SetGain(Convert.ToDouble(txtGain.Text));
                camera.GetImageWithSoftTrigger(out Bitmap bitmap);
                bool success = false;
                Bitmap bitmap = null;
                this.BeginInvoke(new Action(() =>
                // è°ƒç”¨åŸºç±»æŽ¥å£çš„单次采集方法
                // LBCamera重写了StartSingleGrab方法,其他相机调用返回false
                success = camera.StartSingleGrab();
                if (success)
                {
                    if (bitmap != null)
                        this.lblCapTime.Text = $"{(DateTime.Now - StartTime).TotalMilliseconds}ms";
                    else
                        this.lblCapTime.Text = "-1ms";
                }));
                    // ç­‰å¾…图像数据
                    using (AutoResetEvent waitHandle = new AutoResetEvent(false))
                    {
                        Bitmap captured = null;
                        EventHandler<CameraEventArgs> handler = (s, evt) =>
                        {
                            if (evt.Bitmap != null)
                            {
                                captured = evt.Bitmap.Clone() as Bitmap;
                                waitHandle.Set();
                            }
                        };
                //复原原通讯口设置
                //camera.SetCamConfig(OriCamConfig);
                        camera.ImageGrabbed += handler;
                        try
                        {
                            // ç­‰å¾…5秒超时
                            if (waitHandle.WaitOne(5000))
                            {
                                bitmap = captured;
                            }
                        }
                        finally
                        {
                            camera.ImageGrabbed -= handler;
                            camera.StopGrabbing();
                        }
                    }
                    this.BeginInvoke(new Action(() =>
                    {
                        if (bitmap != null)
                        {
                            this.lblCapTime.Text = $"{(DateTime.Now - StartTime).TotalMilliseconds}ms";
                            onlinePictureBox.Image = bitmap;
                        }
                        else
                        {
                            this.lblCapTime.Text = "-1ms";
                        }
                    }));
                }
                else
                {
                    // å¦‚æžœStartSingleGrab失败,回退到传统的GetImageWithSoftTrigger
                    camera.GetImageWithSoftTrigger(out bitmap);
                    this.BeginInvoke(new Action(() =>
                    {
                        if (bitmap != null)
                        {
                            this.lblCapTime.Text = $"{(DateTime.Now - StartTime).TotalMilliseconds}ms";
                            onlinePictureBox.Image = bitmap;
                        }
                        else
                        {
                            this.lblCapTime.Text = "-1ms";
                        }
                    }));
                }
            });
        }
@@ -453,12 +517,12 @@
            total.Clear();
            // å°è¯•将输入字符串转换为枚举值
            if (Enum.TryParse(cmbBrand.Text, true, out CameraBrand brand))
            {
                camera.StopGrabbing();
                camera.StartWith_HardTriggerModel();
            }
            // åœæ­¢å½“前采集
            camera.StopGrabbing();
            // è°ƒç”¨åŸºç±»æŽ¥å£çš„连续采集方法
            // LBCamera会调用StartContinuousGrab方法,其他相机使用原有的StartWith_HardTriggerModel
            camera.StartContinuousGrab();
            startGrabtime = DateTime.Now;
@@ -473,18 +537,15 @@
            total.Clear();
            // å°è¯•将输入字符串转换为枚举值
            if (Enum.TryParse(cmbBrand.Text, true, out CameraBrand brand))
            {
                Task.Factory.StartNew(() =>
                {
                    camera.StopGrabbing();
                    camera.StartWith_SoftTriggerModel();
            // åœæ­¢å½“前采集
            camera.StopGrabbing();
                });
            }
            // è°ƒç”¨åŸºç±»æŽ¥å£çš„连续采集方法
            // LBCamera会调用StartContinuousGrab方法,其他相机使用原有的StartWith_SoftTriggerModel
            camera.StartContinuousGrab();
            startGrabtime = DateTime.Now;
            cmbSN.Enabled = false;
            cmbBrand.Enabled = false;
        }
@@ -502,8 +563,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();
                        }
                    });
                }
@@ -652,6 +716,10 @@
        private void CameraForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            onlinePictureBox.Visible = true;
            this.panel_Picture.Controls.Clear();
            this.panel_Picture.Controls.Add(onlinePictureBox);
            this.onlinePictureBox.Image = null;
            if (camera != null)
            {
@@ -664,7 +732,12 @@
                else
                    camera.SetTriggerMode(TriggerMode.On, actualSource);
                camera.StartGrabbing();
                // LBCamera在StartGrabbing时会直接开启激光和采集,因此关闭窗口时不应自动重启采集
                // å…¶ä»–相机(如2D相机)通常需要保持Grabbing状态以接收触发
                if (camera.Brand != CameraBrand.LBCamera)
                {
                    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>
        /// è®¾ç½®è§¦å‘模式及触发源
@@ -282,12 +186,29 @@
        #region  protected abstract
        /// <summary>
        /// å¼€å§‹é‡‡å›¾
        /// å¼€å§‹é‡‡å›¾ï¼ˆé»˜è®¤å•次采集)
        /// </summary>
        /// <returns></returns>
        public abstract bool StartGrabbing();
        /// <summary>
        /// å•次采集模式(适用于线扫相机)
        /// </summary>
        /// <returns></returns>
        public virtual bool StartSingleGrab()
        {
            // é»˜è®¤å®žçŽ°ï¼šæ™®é€šç›¸æœºä¸æ”¯æŒå•æ¬¡é‡‡é›†ï¼Œç›´æŽ¥è¿”å›žfalse
            // çº¿æ‰«ç›¸æœºéœ€è¦é‡å†™æ­¤æ–¹æ³•
            return false;
        }
        /// <summary>
        /// è¿žç»­é‡‡é›†æ¨¡å¼
        /// </summary>
        /// <returns></returns>
        public abstract bool StartContinuousGrab();
        /// <summary>
        /// åœæ­¢é‡‡å›¾
        /// </summary>
        /// <returns></returns>
LB_VisionProcesses/Cameras/HRCameras/HRCamera.cs
@@ -1130,6 +1130,128 @@
            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);
        }
        public override bool StartContinuousGrab()
        {
            return true;
        }
        #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>
@@ -79,11 +79,18 @@
        bool GetImageWithSoftTrigger(out Bitmap bitmap, int outtime = 3000);
        /// <summary>
        /// è½¯è§¦å‘
        /// è½¯è§¦å‘单次
        /// </summary>
        /// <returns></returns>
        bool SoftTrigger();
        /// <summary>
        /// è¿žç»­é‡‡é›†æ¨¡å¼
        /// </summary>
        /// <returns></returns>
        bool StartContinuousGrab();
        #endregion
        #region SettingConfig
LB_VisionProcesses/Cameras/LBCameras/LBCamera.cs
@@ -13,6 +13,15 @@
namespace LB_VisionProcesses.Cameras.LBCameras
{
    public class LBCameraEventArgs : CameraEventArgs
    {
        public bool IsComplete { get; set; }
        public LBCameraEventArgs(string sn, Bitmap bitmap, bool isComplete) : base(sn, bitmap)
        {
            IsComplete = isComplete;
        }
    }
    /// <summary>
    /// LB3D工业相机实现类
    /// åŸºäºŽPHM6000系列封装
@@ -21,14 +30,25 @@
    {
        private IntPtr _cameraHandle = IntPtr.Zero;
        private PHM6000SensorConfig _sensorConfig;
        // é‡‡é›†å›žè°ƒ
        private AcquisitionCallbackZA _acquisitionCallback;
        private AcquisitionCompletedCallback _acquisitionCompletedCallback;
        private bool _isConnected = false;
        private int _frameCount = 0; // é‡‡é›†å¸§è®¡æ•°
        // å›¾åƒç¼“冲
        private List<byte[]> _lineDataBuffer = new List<byte[]>();
        private readonly object _bufferLock = new object();
        private byte[] _rawPixelBuffer = null; // ç”¨äºŽå­˜å‚¨æ•´å¼ å›¾çš„像素数据 (8bpp)
        private int _currentBitmapHeight = 0;
        private int _currentBitmapWidth = 0;
        private int _currentLineCount = 0;
        private object _bufferLock = new object();
        private bool _isBufferReady = false;
        // ä¸´æ—¶è¡Œç¼“冲,用于接收回调数据
        private byte[] _tempLineBuffer = null;
        private bool _isContinuous = false;
        public LBCamera()
        {
@@ -40,6 +60,12 @@
        public override bool InitDevice(string sn, object handle = null)
        {
            // å¦‚果已连接,直接返回true
            if (_isConnected && _cameraHandle != IntPtr.Zero)
            {
                return true;
            }
            IntPtr tempHandle = IntPtr.Zero;
            try
            {
@@ -70,9 +96,8 @@
                    {
                        string currentSn = Encoding.UTF8.GetString(serialNumberBytes).TrimEnd('\0');
                        
                        // å¦‚果传入的 sn æ˜¯ IP åœ°å€ï¼Œåˆ™ç›´æŽ¥å°è¯•匹配 IP
                        // æˆ–者匹配序列号
                        if (currentSn == sn || sn.Contains(currentSn)) // ç®€å•匹配逻辑
                        // åŒ¹é…SN或IP
                        if (currentSn == sn || sn.Contains(currentSn))
                        {
                            byte[] addressBytes = new byte[64];
                            int port = 0;
@@ -87,17 +112,15 @@
                    }
                }
                // é”€æ¯ä¸´æ—¶å¥æŸ„
                PHM6000Profiler.DestroyCameraEntry(tempHandle);
                tempHandle = IntPtr.Zero;
                if (!found)
                {
                    // å¦‚果没找到但 sn æœ¬èº«çœ‹èµ·æ¥åƒ IP,尝试直接连接(兼容旧逻辑)
                    if (System.Net.IPAddress.TryParse(sn, out _))
                    {
                        targetIp = sn;
                        targetPort = 5577; // é»˜è®¤ç«¯å£
                        targetPort = 5577;
                    }
                    else
                    {
@@ -119,14 +142,24 @@
                    // åŠ è½½ç›¸æœºå½“å‰å‚æ•°åˆ° _sensorConfig
                    SyncConfigFromCamera();
                    // åˆå§‹åŒ–回调
                    _acquisitionCallback = new AcquisitionCallbackZA(OnLineReceived);
                    _acquisitionCompletedCallback = new AcquisitionCompletedCallback(OnAcquisitionCompleted);
                    // åˆå§‹åŒ–并注册采集回调 (获取数据用)
                    _acquisitionCallback = new AcquisitionCallbackZA(OnAcquisitionCallbackZA);
                    PHM6000Profiler.SetAcquisitionCallbackZA(_cameraHandle, _acquisitionCallback, IntPtr.Zero);
                    // åˆå§‹åŒ–并注册采集完成回调 (状态通知用)
                    _acquisitionCompletedCallback = new AcquisitionCompletedCallback(OnAcquisitionCompleted);
                    PHM6000Profiler.RegisterAcquisitionCompletedCallback(_cameraHandle, _acquisitionCompletedCallback, IntPtr.Zero);
                    // å¼ºåˆ¶åº”用当前配置(确保触发模式等参数正确,避免相机处于未知状态)
                    UpdateSensorConfig(_sensorConfig);
                    AsyncLogHelper.Info($"LBCamera[{SN}]: Connected and initialized successfully (Manual Data Mode)");
                    return true;
                }
                else
                {
                    AsyncLogHelper.Error($"LBCamera[{SN}]: ConnectToCamera failed, result={result}");
                }
            }
            catch (Exception ex) 
@@ -145,6 +178,7 @@
                PHM6000Profiler.DestroyCameraEntry(_cameraHandle);
                _cameraHandle = IntPtr.Zero;
                _isConnected = false;
                AsyncLogHelper.Info($"LBCamera[{SN}]: Closed");
            }
            return true;
        }
@@ -166,8 +200,6 @@
                        if (PHM6000Profiler.GetCameraInformation(tempHandle, i, moduleTypeBytes, serialNumberBytes) == 0)
                        {
                            string sn = Encoding.UTF8.GetString(serialNumberBytes).TrimEnd('\0');
                            string type = Encoding.UTF8.GetString(moduleTypeBytes).TrimEnd('\0');
                            // æ ¼å¼å‚考:PHM6000[SN123456]
                            if (!string.IsNullOrEmpty(sn))
                            {
                                cameraList.Add(sn);
@@ -178,7 +210,7 @@
            }
            catch (Exception ex)
            {
                AsyncLogHelper.Error($"LBCamera: GetListEnum异常 - {ex.Message}");
                AsyncLogHelper.Error($"LBCamera: èŽ·å–è®¾å¤‡åˆ—è¡¨å¼‚å¸¸ - {ex.Message}");
            }
            finally
            {
@@ -188,18 +220,57 @@
            return cameraList;
        }
        public override bool StartGrabbing()
        private void InitBuffer()
        {
            if (!_isConnected) return false;
            lock (_bufferLock)
            {
                _lineDataBuffer.Clear();
                _currentBitmapHeight = _sensorConfig.ScanLineCount > 0 ? _sensorConfig.ScanLineCount : 5000;
                // å®½åº¦åœ¨ç¬¬ä¸€è¡Œæ•°æ®åˆ°è¾¾æ—¶ç¡®å®š
                _currentBitmapWidth = 0;
                _rawPixelBuffer = null;
                _currentLineCount = 0;
                _isBufferReady = false;
            }
        }
            // è®¾ç½®é‡‡é›†æ¨¡å¼ï¼š1=扫描模式,1=连续模式
        public override bool StartGrabbing()
        {
            // é»˜è®¤è¿žç»­æ¨¡å¼
            return StartSingleGrab();
        }
        public bool StartSingleGrab()
        {
            if (!_isConnected) return false;
            _isContinuous = false;
            InitBuffer();
            AsyncLogHelper.Info($"LBCamera[{SN}]: å¼€å§‹å•次采集");
            // 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;
        }
        public override bool StartContinuousGrab()
        {
            if (!_isConnected) return false;
            _isContinuous = true;
            InitBuffer();
            AsyncLogHelper.Info($"LBCamera[{SN}]:开始连续采集");
            // 1=扫描模式, 1=连续
            PHM6000Profiler.SetAcquisitionMode(_cameraHandle, 1, 1);
            int result = PHM6000Profiler.StartAcquisition(_cameraHandle, 0, 0, 0.0);
            if (result == 0)
            {
                isGrabbing = true;
@@ -210,17 +281,30 @@
        public override bool StopGrabbing()
        {
            _isContinuous = false;
            if (!_isConnected) return true;
            PHM6000Profiler.StopAcquisition(_cameraHandle);
            // åœæ­¢æ—¶å¦‚果有未显示的缓存数据,将其显示出来(支持显示不完整的帧)
            lock (_bufferLock)
            {
                if (_currentLineCount > 0)
                {
                    AsyncLogHelper.Info($"LBCamera[{SN}]: Flushing partial buffer ({_currentLineCount} lines) on stop");
                    CreateAndFireBitmap();
                    _currentLineCount = 0;
                }
            }
            isGrabbing = false;
            return true;
        }
        public override bool SoftTrigger()
        {
            // çº¿æ‰«ç›¸æœºé€šå¸¸ä¸éœ€è¦ä¼ ç»Ÿè½¯è§¦å‘,但在某些模式下可模拟
            return true;
        }
        public override bool StartWith_SoftTriggerModel() => StartContinuousGrab();
        public override bool StartWith_HardTriggerModel(TriggerSource hardtriggeritem = TriggerSource.Line0) => StartSingleGrab();
        public override bool SoftTrigger() => true;
        #region å‚数设置映射
@@ -229,9 +313,30 @@
        public override bool SetGain(double gain) => SetParam(EnumNameId.AnalogGain, (float)gain);
        public override bool GetGain(out double gain) { float v; bool r = GetParam(EnumNameId.AnalogGain, out v); gain = v; return r; }
        // å…¶ä»–接口占位实现
        public override bool SetTriggerMode(TriggerMode mode, TriggerSource triggerEnum = TriggerSource.Line0) => true;
        public override bool GetTriggerMode(out TriggerMode mode, out TriggerSource source) { mode = TriggerMode.Off; source = TriggerSource.Software; return true; }
        public override bool SetTriggerMode(TriggerMode mode, TriggerSource triggerEnum = TriggerSource.Line0)
        {
            if (!_isConnected) return false;
            if (triggerEnum == TriggerSource.Software)
            {
                _sensorConfig.LineScanTriggerSource = EnumLineScanTriggerSource.固定频率;
                _sensorConfig.DataAcquisitionTriggerSource = EnumDataAcquisitionTriggerSource.软触发;
            }
            else
            {
                _sensorConfig.LineScanTriggerSource = EnumLineScanTriggerSource.编码器;
                _sensorConfig.DataAcquisitionTriggerSource = EnumDataAcquisitionTriggerSource.外部触发;
            }
            UpdateSensorConfig(_sensorConfig);
            return true;
        }
        public override bool GetTriggerMode(out TriggerMode mode, out TriggerSource source)
        {
            mode = TriggerMode.On;
            source = _sensorConfig.DataAcquisitionTriggerSource == EnumDataAcquisitionTriggerSource.软触发 ? TriggerSource.Software : TriggerSource.Line0;
            return true;
        }
        public override bool SetTriggerPolarity(TriggerPolarity polarity) => true;
        public override bool GetTriggerPolarity(out TriggerPolarity polarity) { polarity = TriggerPolarity.RisingEdge; return true; }
        public override bool SetTriggerFliter(double flitertime) => true;
@@ -242,6 +347,64 @@
        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;
            // è®¡ç®—理论最小耗时 (仅当使用固定频率触发时)
            int minTime = 0;
            if (_sensorConfig.LineScanTriggerSource == EnumLineScanTriggerSource.固定频率)
            {
                float rate = _sensorConfig.SoftwareTriggerRate > 0 ? _sensorConfig.SoftwareTriggerRate : 1000f;
                int lines = _sensorConfig.ScanLineCount > 0 ? _sensorConfig.ScanLineCount : 5000;
                minTime = (int)((lines / rate) * 1000);
            }
            // å¦‚果传入超时时间不够,自动延长
            int actualTimeout = outtime;
            if (actualTimeout < minTime + 2000)
            {
                actualTimeout = minTime + 3000; // é¢„ç•™3秒余量
                AsyncLogHelper.Warn($"LBCamera: Provided timeout {outtime}ms is too short for {minTime}ms scan. Extended to {actualTimeout}ms.");
            }
            using (AutoResetEvent waitHandle = new AutoResetEvent(false))
            {
                Bitmap res = null;
                EventHandler<CameraEventArgs> handler = (s, e) => {
                    if(e.Bitmap != null) {
                         res = e.Bitmap.Clone() as Bitmap;
                         waitHandle.Set();
                    }
                };
                ImageGrabbed += handler;
                if (StartSingleGrab())
                {
                    if (!waitHandle.WaitOne(actualTimeout))
                    {
                        AsyncLogHelper.Error($"LBCamera: GetImageWithSoftTrigger timeout after {actualTimeout}ms");
                    }
                }
                else
                {
                    AsyncLogHelper.Error("LBCamera: StartSingleGrab failed");
                }
                ImageGrabbed -= handler;
                // ç¡®ä¿åœæ­¢é‡‡é›†
                StopGrabbing();
                bitmap = res;
                return bitmap != null;
            }
        }
        public PHM6000SensorConfig GetSensorConfig()
        {
@@ -252,92 +415,185 @@
        public void UpdateSensorConfig(PHM6000SensorConfig config)
        {
            _sensorConfig = config;
            // ç®€å•示例:设置曝光和增益
            SetExpouseTime(config.ExposureTime);
            SetGain((double)config.AnalogGain);
            // æ›´å¤šå‚数同步逻辑应在此处实现
            if (!_isConnected) return;
            SetParam(EnumNameId.ExposureTime, (float)config.ExposureTime);
            SetParam(EnumNameId.AnalogGain, (float)config.AnalogGain);
            PHM6000Profiler.SetProfilerParameter(_cameraHandle, (int)EnumNameId.ScanLineCount, config.ScanLineCount, 0, 0);
            PHM6000Profiler.SetProfilerParameter(_cameraHandle, (int)EnumNameId.LineScanTriggerSource, 0, 0, (int)config.LineScanTriggerSource);
            PHM6000Profiler.SetProfilerParameter(_cameraHandle, (int)EnumNameId.DataAcquisitionTriggerSource, 0, 0, (int)config.DataAcquisitionTriggerSource);
            if (config.LineScanTriggerSource == EnumLineScanTriggerSource.固定频率)
            {
                PHM6000Profiler.SetProfilerParameter(_cameraHandle, (int)EnumNameId.SoftwareTriggerRate, 0, config.SoftwareTriggerRate, 0);
            }
            PHM6000Profiler.SaveAllParametersToDevice(_cameraHandle);
        }
        #endregion
        #endregion
        #region Private Callback & Helpers
        #region Callbacks
        private void OnLineReceived(IntPtr pInstance, IntPtr buffer, int points)
        private void OnAcquisitionCallbackZA(IntPtr pInstance, IntPtr buffer, int points)
        {
                // å®žæ—¶å›žè°ƒå¤„理:累积行数据
                if (!isGrabbing) return;
            if (buffer == IntPtr.Zero || points <= 0) return;
                int lineSize = points * Marshal.SizeOf(typeof(LBPointZA));
                byte[] lineData = new byte[lineSize];
                Marshal.Copy(buffer, lineData, 0, lineSize);
                lock (_bufferLock)
            lock (_bufferLock)
            {
                // åˆå§‹åŒ–缓冲区
                if (_rawPixelBuffer == null)
                {
                    _lineDataBuffer.Add(lineData);
                    _currentLineCount++;
                    _currentBitmapWidth = points;
                    if (_currentBitmapHeight <= 0) _currentBitmapHeight = 2000; // é»˜è®¤é˜²å‘†
                    _rawPixelBuffer = new byte[_currentBitmapWidth * _currentBitmapHeight];
                    _currentLineCount = 0;
                }
                if (_currentLineCount >= _currentBitmapHeight) return; // ç¼“冲区满,忽略多余数据
                // å‡†å¤‡ä¸´æ—¶ç¼“冲区接收行数据 (LBPointZA = 8 bytes)
                int lineBytes = points * 8;
                if (_tempLineBuffer == null || _tempLineBuffer.Length != lineBytes)
                {
                    _tempLineBuffer = new byte[lineBytes];
                }
                // æ‹·è´éžæ‰˜ç®¡å†…存到托管数组
                Marshal.Copy(buffer, _tempLineBuffer, 0, lineBytes);
                // æå–灰度(Intensity/Alpha)数据填充到 _rawPixelBuffer
                // LBPointZA结构: float(4) + res(3) + alpha(1). Alpha在偏移7
                int bufferOffset = _currentLineCount * _currentBitmapWidth;
                for (int i = 0; i < points; i++)
                {
                    if (bufferOffset + i < _rawPixelBuffer.Length)
                    {
                        _rawPixelBuffer[bufferOffset + i] = _tempLineBuffer[i * 8 + 7];
                    }
                }
                _currentLineCount++;
                // å¦‚果达到预定高度,生成图像
                if (_currentLineCount >= _currentBitmapHeight)
                {
                    CreateAndFireBitmap();
                    // é‡ç½®ï¼Œå‡†å¤‡ä¸‹ä¸€å¸§ (如果是连续采集)
                    _currentLineCount = 0;
                    // _rawPixelBuffer å¯ä»¥å¤ç”¨ï¼Œä¸éœ€è¦ç½®ç©º
                }
            }
        }
        private void OnAcquisitionCompleted(IntPtr pInstance, int nOption)
        {
                // nOption: 0=一批数据结束, 1=全部完成, 2=点云就绪
                if (nOption == 1 || nOption == 0)
            // nOption: 0=Batch End, 1=All End(Single), 2=Processing End
            // æ­¤å¤„主要用于日志或状态监控
            // å®žé™…图像生成在 Data Callback ä¸­å®Œæˆ
            if (nOption == 1) // å•次采集结束
            {
                if (_isContinuous && isGrabbing)
                {
                    GenerateIntensityMap();
                    // å¦‚果在连续模式下收到结束信号,尝试自动重启采集
                    AsyncLogHelper.Info($"LBCamera[{SN}]: Continuous mode frame ended, restarting...");
                    Task.Run(() =>
                    {
                        if (_isContinuous && _isConnected)
                        {
                            PHM6000Profiler.StartAcquisition(_cameraHandle, 0, 0, 0.0);
                        }
                    });
                }
                else
                {
                    isGrabbing = false;
                    AsyncLogHelper.Info($"LBCamera[{SN}]: Single grab completed by SDK");
                    // å•次采集结束时,如果有未显示的缓冲数据,立即生成图像
                    // é˜²æ­¢å› æ•°æ®é‡ä¸è¶³(小于ScanLineCount)导致GetImageWithSoftTrigger一直等待
                    lock (_bufferLock)
                    {
                        if (_currentLineCount > 0)
                        {
                            AsyncLogHelper.Info($"LBCamera[{SN}]: Flushing partial buffer ({_currentLineCount} lines) on completion");
                            CreateAndFireBitmap();
                            _currentLineCount = 0;
                        }
                    }
                }
            }
        }
        private void GenerateIntensityMap()
        private void CreateAndFireBitmap()
        {
            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
            {
                int width = _currentBitmapWidth;
                int height = _currentLineCount; // ä½¿ç”¨å®žé™…采集到的行数
                if (width <= 0 || height <= 0 || _rawPixelBuffer == null) return;
                Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
                // è®¾ç½®ç°åº¦è°ƒè‰²æ¿
                ColorPalette palette = bmp.Palette;
                for (int i = 0; i < 256; i++) palette.Entries[i] = Color.FromArgb(i, i, i);
                for (int i = 0; i < 256; i++)
                {
                    palette.Entries[i] = Color.FromArgb(i, i, i);
                }
                bmp.Palette = palette;
                // æ‹·è´æ•°æ®
                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);
                // æ³¨æ„ï¼šBitmap Stride å¯èƒ½ä¸ç­‰äºŽ Width,需要逐行拷贝
                int stride = bmpData.Stride;
                IntPtr ptr = bmpData.Scan0;
                for (int y = 0; y < height; y++)
                {
                    // ç¡®ä¿ä¸è¶Šç•Œ
                    if ((y * width) + width <= _rawPixelBuffer.Length)
                    {
                        Marshal.Copy(_rawPixelBuffer, y * width, ptr + y * stride, width);
                    }
                }
                bmp.UnlockBits(bmpData);
                // è§¦å‘事件通知 UI æ›´æ–°äº®åº¦å›¾
                ImageGrabbed?.Invoke(this, new CameraEventArgs(SN, bmp));
                _frameCount++;
                AsyncLogHelper.Info($"LBCamera[{SN}]: Frame {_frameCount} generated ({width}x{height})");
                // å¼‚步触发事件,避免阻塞SDK回调线程
                Task.Run(() =>
                {
                    try
                    {
                        ImageGrabbed?.Invoke(this, new LBCameraEventArgs(SN, bmp, true));
                    }
                    catch (Exception ex)
                    {
                        AsyncLogHelper.Error($"LBCamera: Event Invoke error - {ex.Message}");
                        bmp.Dispose(); // å¼‚常时释放资源
                    }
                });
            }
            catch (Exception ex)
            {
                AsyncLogHelper.Error($"LBCamera: ç”Ÿæˆäº®åº¦å›¾å¼‚常 - {ex.Message}");
                AsyncLogHelper.Error($"LBCamera: CreateBitmap error - {ex.Message}");
            }
        }
        private void SyncConfigFromCamera()
        {
            // ä»Žç›¸æœºè¯»å–所有参数并填充到 _sensorConfig
            foreach (EnumNameId id in Enum.GetValues(typeof(EnumNameId)))
            {
                int iVal = 0; double dVal = 0; int eVal = 0;
                if (PHM6000Profiler.GetProfilerParameter(_cameraHandle, (int)id, ref iVal, ref dVal, ref eVal) == 0)
                {
                    // å®žé™…项目中应使用反射将值写回 _sensorConfig
                    // Update _sensorConfig values here if needed
                }
            }
        }
@@ -360,7 +616,6 @@
            }
            return false;
        }
        #endregion
    }
}
}
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
            {