From 9d1660270272180cd71147d6ddd53d1382643d2e Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 02 十一月 2026 18:00:14 +0800
Subject: [PATCH] merge: 并入远程 Initial commit 作为历史根节点

---
 Plugin.DeviceConfig/Model/DeviceConfigModel.cs |  645 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 645 insertions(+), 0 deletions(-)

diff --git a/Plugin.DeviceConfig/Model/DeviceConfigModel.cs b/Plugin.DeviceConfig/Model/DeviceConfigModel.cs
new file mode 100644
index 0000000..d679925
--- /dev/null
+++ b/Plugin.DeviceConfig/Model/DeviceConfigModel.cs
@@ -0,0 +1,645 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using HMI.Core.Util;
+using Plugin.DeviceConfig.Entity;
+
+namespace Plugin.DeviceConfig.Model
+{
+    /// <summary>
+    /// 璁惧閰嶇疆鏁版嵁璁块棶灞傦紝鍩轰簬 SQLite 瀹炵幇
+    /// </summary>
+    public class DeviceConfigModel
+    {
+        private string _dbPath = string.Empty;
+
+        /// <summary>
+        /// 鍒濆鍖栨暟鎹簱锛屽垱寤烘墍鏈夎〃
+        /// </summary>
+        /// <param name="dbPath">鏁版嵁搴撴枃浠惰矾寰�</param>
+        public void InitDatabase(string dbPath)
+        {
+            _dbPath = dbPath;
+            try
+            {
+                // 纭繚鐩綍瀛樺湪
+                string? dir = System.IO.Path.GetDirectoryName(dbPath);
+                if (!string.IsNullOrEmpty(dir) && !System.IO.Directory.Exists(dir))
+                {
+                    System.IO.Directory.CreateDirectory(dir);
+                }
+
+                // 浠呭湪鏁版嵁搴撴枃浠朵笉瀛樺湪鏃跺垱寤猴紙閬垮厤閲嶅惎鍚庢姏寮傚父鎴栦涪澶卞凡鏈夋暟鎹級
+                if (!System.IO.File.Exists(dbPath))
+                {
+                    SqliteHelper.CreateDatabase(dbPath);
+                }
+
+                using var helper = new SqliteHelper(dbPath);
+
+                var sqls = new List<string>
+                {
+                    // 璁惧瀹氫箟琛�
+                    @"CREATE TABLE IF NOT EXISTS DeviceDefinition (
+                        DeviceId TEXT PRIMARY KEY,
+                        Kind TEXT NOT NULL,
+                        DriverType TEXT NOT NULL,
+                        Transport TEXT NOT NULL,
+                        Endpoint TEXT NOT NULL,
+                        EffectiveVersion TEXT NOT NULL DEFAULT '',
+                        CreatedAt TEXT NOT NULL,
+                        UpdatedAt TEXT NOT NULL
+                    );",
+
+                    // 璁惧閫氶亾琛�
+                    @"CREATE TABLE IF NOT EXISTS DeviceChannel (
+                        DeviceId TEXT NOT NULL,
+                        ChannelId INTEGER NOT NULL,
+                        Capability TEXT NOT NULL DEFAULT '',
+                        MaxVoltage REAL NOT NULL DEFAULT 0,
+                        MaxCurrent REAL NOT NULL DEFAULT 0,
+                        Limits TEXT NOT NULL DEFAULT '{}',
+                        SessionKey TEXT NOT NULL DEFAULT '',
+                        PRIMARY KEY (DeviceId, ChannelId)
+                    );",
+
+                    // Slot鏄犲皠琛�
+                    @"CREATE TABLE IF NOT EXISTS SlotDeviceMapping (
+                        SlotId TEXT NOT NULL,
+                        Capability TEXT NOT NULL,
+                        Version TEXT NOT NULL,
+                        DeviceId TEXT NOT NULL,
+                        ChannelId INTEGER NOT NULL DEFAULT 0,
+                        MappingVersion TEXT NOT NULL DEFAULT '',
+                        PRIMARY KEY (SlotId, Capability, Version)
+                    );",
+
+                    // 閰嶇疆鐗堟湰琛�
+                    @"CREATE TABLE IF NOT EXISTS DeviceConfigVersion (
+                        VersionId TEXT PRIMARY KEY,
+                        Status TEXT NOT NULL,
+                        Hash TEXT NOT NULL DEFAULT '',
+                        Author TEXT NOT NULL DEFAULT '',
+                        Timestamp TEXT NOT NULL,
+                        DiffDescription TEXT NOT NULL DEFAULT ''
+                    );",
+
+                    // 璁惧缁勬ā鏉胯〃
+                    @"CREATE TABLE IF NOT EXISTS DeviceGroupTemplate (
+                        TemplateId TEXT PRIMARY KEY,
+                        DeviceType TEXT NOT NULL DEFAULT '',
+                        DriverType TEXT NOT NULL DEFAULT '',
+                        Transport TEXT NOT NULL DEFAULT '',
+                        DeviceCount INTEGER NOT NULL DEFAULT 0,
+                        ChannelsPerDevice INTEGER NOT NULL DEFAULT 0,
+                        AddressRule TEXT NOT NULL DEFAULT '',
+                        SlotStart INTEGER NOT NULL DEFAULT 0
+                    );"
+                };
+
+                helper.ExecuteBatchSql(sqls);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鍒濆鍖栬澶囬厤缃暟鎹簱澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        #region DeviceDefinition CRUD
+
+        /// <summary>
+        /// 鑾峰彇鎵�鏈夎澶囧畾涔�
+        /// </summary>
+        public List<DeviceDefinition> GetAllDevices()
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                var dt = helper.GetDataTable("SELECT * FROM DeviceDefinition ORDER BY CreatedAt DESC;");
+                return MapToDeviceList(dt);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鑾峰彇鎵�鏈夎澶囧畾涔夊け璐�: {ex.Message}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 鏍规嵁ID鑾峰彇璁惧瀹氫箟
+        /// </summary>
+        public DeviceDefinition? GetDeviceById(string deviceId)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                var dt = helper.GetDataTable($"SELECT * FROM DeviceDefinition WHERE DeviceId = '{Escape(deviceId)}';");
+                var list = MapToDeviceList(dt);
+                return list.Count > 0 ? list[0] : null;
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鑾峰彇璁惧瀹氫箟[{deviceId}]澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 淇濆瓨璁惧瀹氫箟锛堟柊澧炴垨鏇存柊锛�
+        /// </summary>
+        public void SaveDevice(DeviceDefinition device)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                device.UpdatedAt = DateTime.Now;
+
+                var sql = $@"INSERT OR REPLACE INTO DeviceDefinition 
+                    (DeviceId, Kind, DriverType, Transport, Endpoint, EffectiveVersion, CreatedAt, UpdatedAt) 
+                    VALUES (
+                        '{Escape(device.DeviceId)}',
+                        '{device.Kind}',
+                        '{Escape(device.DriverType)}',
+                        '{device.Transport}',
+                        '{Escape(device.Endpoint)}',
+                        '{Escape(device.EffectiveVersion)}',
+                        '{device.CreatedAt:yyyy-MM-dd HH:mm:ss}',
+                        '{device.UpdatedAt:yyyy-MM-dd HH:mm:ss}'
+                    );";
+                helper.ExecuteSql(sql);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"淇濆瓨璁惧瀹氫箟[{device.DeviceId}]澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 鍒犻櫎璁惧瀹氫箟
+        /// </summary>
+        public void DeleteDevice(string deviceId)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                helper.ExecuteSql($"DELETE FROM DeviceDefinition WHERE DeviceId = '{Escape(deviceId)}';");
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鍒犻櫎璁惧瀹氫箟[{deviceId}]澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        private List<DeviceDefinition> MapToDeviceList(DataTable dt)
+        {
+            var list = new List<DeviceDefinition>();
+            foreach (DataRow row in dt.Rows)
+            {
+                list.Add(new DeviceDefinition
+                {
+                    DeviceId = row["DeviceId"].ToString() ?? string.Empty,
+                    Kind = Enum.TryParse<DeviceKind>(row["Kind"].ToString(), out var kind) ? kind : DeviceKind.PowerSupply,
+                    DriverType = row["DriverType"].ToString() ?? string.Empty,
+                    Transport = Enum.TryParse<TransportType>(row["Transport"].ToString(), out var tp) ? tp : TransportType.Tcp,
+                    Endpoint = row["Endpoint"].ToString() ?? string.Empty,
+                    EffectiveVersion = row["EffectiveVersion"].ToString() ?? string.Empty,
+                    CreatedAt = DateTime.TryParse(row["CreatedAt"].ToString(), out var ca) ? ca : DateTime.Now,
+                    UpdatedAt = DateTime.TryParse(row["UpdatedAt"].ToString(), out var ua) ? ua : DateTime.Now,
+                });
+            }
+            return list;
+        }
+
+        #endregion
+
+        #region DeviceChannel CRUD
+
+        /// <summary>
+        /// 鑾峰彇鎸囧畾璁惧鐨勬墍鏈夐�氶亾
+        /// </summary>
+        public List<DeviceChannel> GetChannelsByDevice(string deviceId)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                var dt = helper.GetDataTable($"SELECT * FROM DeviceChannel WHERE DeviceId = '{Escape(deviceId)}' ORDER BY ChannelId;");
+                return MapToChannelList(dt);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鑾峰彇璁惧[{deviceId}]閫氶亾澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 淇濆瓨璁惧閫氶亾锛堟柊澧炴垨鏇存柊锛�
+        /// </summary>
+        public void SaveChannel(DeviceChannel channel)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                var sql = $@"INSERT OR REPLACE INTO DeviceChannel 
+                    (DeviceId, ChannelId, Capability, MaxVoltage, MaxCurrent, Limits, SessionKey) 
+                    VALUES (
+                        '{Escape(channel.DeviceId)}',
+                        {channel.ChannelId},
+                        '{Escape(channel.Capability)}',
+                        {channel.MaxVoltage},
+                        {channel.MaxCurrent},
+                        '{Escape(channel.Limits)}',
+                        '{Escape(channel.SessionKey)}'
+                    );";
+                helper.ExecuteSql(sql);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"淇濆瓨閫氶亾[{channel.DeviceId}:{channel.ChannelId}]澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 鍒犻櫎璁惧閫氶亾
+        /// </summary>
+        public void DeleteChannel(string deviceId, int channelId)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                helper.ExecuteSql($"DELETE FROM DeviceChannel WHERE DeviceId = '{Escape(deviceId)}' AND ChannelId = {channelId};");
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鍒犻櫎閫氶亾[{deviceId}:{channelId}]澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        private List<DeviceChannel> MapToChannelList(DataTable dt)
+        {
+            var list = new List<DeviceChannel>();
+            foreach (DataRow row in dt.Rows)
+            {
+                list.Add(new DeviceChannel
+                {
+                    DeviceId = row["DeviceId"].ToString() ?? string.Empty,
+                    ChannelId = Convert.ToInt32(row["ChannelId"]),
+                    Capability = row["Capability"].ToString() ?? string.Empty,
+                    MaxVoltage = Convert.ToDouble(row["MaxVoltage"]),
+                    MaxCurrent = Convert.ToDouble(row["MaxCurrent"]),
+                    Limits = row["Limits"].ToString() ?? "{}",
+                    SessionKey = row["SessionKey"].ToString() ?? string.Empty,
+                });
+            }
+            return list;
+        }
+
+        #endregion
+
+        #region SlotDeviceMapping CRUD
+
+        /// <summary>
+        /// 鑾峰彇鎵�鏈塖lot鏄犲皠
+        /// </summary>
+        public List<SlotDeviceMapping> GetSlotMappings()
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                var dt = helper.GetDataTable("SELECT * FROM SlotDeviceMapping ORDER BY SlotId;");
+                return MapToSlotMappingList(dt);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鑾峰彇Slot鏄犲皠澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 淇濆瓨Slot鏄犲皠锛堟柊澧炴垨鏇存柊锛�
+        /// </summary>
+        public void SaveSlotMapping(SlotDeviceMapping mapping)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                var sql = $@"INSERT OR REPLACE INTO SlotDeviceMapping 
+                    (SlotId, Capability, Version, DeviceId, ChannelId, MappingVersion) 
+                    VALUES (
+                        '{Escape(mapping.SlotId)}',
+                        '{Escape(mapping.Capability)}',
+                        '{Escape(mapping.Version)}',
+                        '{Escape(mapping.DeviceId)}',
+                        {mapping.ChannelId},
+                        '{Escape(mapping.MappingVersion)}'
+                    );";
+                helper.ExecuteSql(sql);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"淇濆瓨Slot鏄犲皠[{mapping.SlotId}]澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 鍒犻櫎Slot鏄犲皠
+        /// </summary>
+        public void DeleteSlotMapping(string slotId, string capability, string version)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                helper.ExecuteSql($@"DELETE FROM SlotDeviceMapping 
+                    WHERE SlotId = '{Escape(slotId)}' 
+                    AND Capability = '{Escape(capability)}' 
+                    AND Version = '{Escape(version)}';");
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鍒犻櫎Slot鏄犲皠[{slotId}:{capability}:{version}]澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        private List<SlotDeviceMapping> MapToSlotMappingList(DataTable dt)
+        {
+            var list = new List<SlotDeviceMapping>();
+            foreach (DataRow row in dt.Rows)
+            {
+                list.Add(new SlotDeviceMapping
+                {
+                    SlotId = row["SlotId"].ToString() ?? string.Empty,
+                    Capability = row["Capability"].ToString() ?? string.Empty,
+                    Version = row["Version"].ToString() ?? string.Empty,
+                    DeviceId = row["DeviceId"].ToString() ?? string.Empty,
+                    ChannelId = Convert.ToInt32(row["ChannelId"]),
+                    MappingVersion = row["MappingVersion"].ToString() ?? string.Empty,
+                });
+            }
+            return list;
+        }
+
+        #endregion
+
+        #region DeviceConfigVersion CRUD
+
+        /// <summary>
+        /// 鑾峰彇鎵�鏈夐厤缃増鏈�
+        /// </summary>
+        public List<DeviceConfigVersion> GetConfigVersions()
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                var dt = helper.GetDataTable("SELECT * FROM DeviceConfigVersion ORDER BY Timestamp DESC;");
+                return MapToVersionList(dt);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鑾峰彇閰嶇疆鐗堟湰澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 淇濆瓨閰嶇疆鐗堟湰锛堟柊澧炴垨鏇存柊锛�
+        /// </summary>
+        public void SaveConfigVersion(DeviceConfigVersion version)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                var sql = $@"INSERT OR REPLACE INTO DeviceConfigVersion 
+                    (VersionId, Status, Hash, Author, Timestamp, DiffDescription) 
+                    VALUES (
+                        '{Escape(version.VersionId)}',
+                        '{version.Status}',
+                        '{Escape(version.Hash)}',
+                        '{Escape(version.Author)}',
+                        '{version.Timestamp:yyyy-MM-dd HH:mm:ss}',
+                        '{Escape(version.DiffDescription)}'
+                    );";
+                helper.ExecuteSql(sql);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"淇濆瓨閰嶇疆鐗堟湰[{version.VersionId}]澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 鏇存柊鐗堟湰鐘舵��
+        /// </summary>
+        public void UpdateVersionStatus(string versionId, string newStatus)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                helper.ExecuteSql($@"UPDATE DeviceConfigVersion 
+                    SET Status = '{Escape(newStatus)}' 
+                    WHERE VersionId = '{Escape(versionId)}';");
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鏇存柊鐗堟湰鐘舵�乕{versionId}]澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        private List<DeviceConfigVersion> MapToVersionList(DataTable dt)
+        {
+            var list = new List<DeviceConfigVersion>();
+            foreach (DataRow row in dt.Rows)
+            {
+                list.Add(new DeviceConfigVersion
+                {
+                    VersionId = row["VersionId"].ToString() ?? string.Empty,
+                    Status = Enum.TryParse<VersionStatus>(row["Status"].ToString(), out var st) ? st : VersionStatus.Draft,
+                    Hash = row["Hash"].ToString() ?? string.Empty,
+                    Author = row["Author"].ToString() ?? string.Empty,
+                    Timestamp = DateTime.TryParse(row["Timestamp"].ToString(), out var ts) ? ts : DateTime.Now,
+                    DiffDescription = row["DiffDescription"].ToString() ?? string.Empty,
+                });
+            }
+            return list;
+        }
+
+        #endregion
+
+        #region DeviceGroupTemplate CRUD
+
+        /// <summary>
+        /// 鑾峰彇鎵�鏈夎澶囩粍妯℃澘
+        /// </summary>
+        public List<DeviceGroupTemplate> GetGroupTemplates()
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                var dt = helper.GetDataTable("SELECT * FROM DeviceGroupTemplate ORDER BY DeviceType;");
+                return MapToTemplateList(dt);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鑾峰彇璁惧缁勬ā鏉垮け璐�: {ex.Message}", ex);
+            }
+        }
+
+        /// <summary>
+        /// 淇濆瓨璁惧缁勬ā鏉匡紙鏂板鎴栨洿鏂帮級
+        /// </summary>
+        public void SaveGroupTemplate(DeviceGroupTemplate template)
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+                var sql = $@"INSERT OR REPLACE INTO DeviceGroupTemplate 
+                    (TemplateId, DeviceType, DriverType, Transport, DeviceCount, ChannelsPerDevice, AddressRule, SlotStart) 
+                    VALUES (
+                        '{Escape(template.TemplateId)}',
+                        '{Escape(template.DeviceType)}',
+                        '{Escape(template.DriverType)}',
+                        '{Escape(template.Transport)}',
+                        {template.DeviceCount},
+                        {template.ChannelsPerDevice},
+                        '{Escape(template.AddressRule)}',
+                        {template.SlotStart}
+                    );";
+                helper.ExecuteSql(sql);
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"淇濆瓨璁惧缁勬ā鏉縖{template.TemplateId}]澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        private List<DeviceGroupTemplate> MapToTemplateList(DataTable dt)
+        {
+            var list = new List<DeviceGroupTemplate>();
+            foreach (DataRow row in dt.Rows)
+            {
+                list.Add(new DeviceGroupTemplate
+                {
+                    TemplateId = row["TemplateId"].ToString() ?? string.Empty,
+                    DeviceType = row["DeviceType"].ToString() ?? string.Empty,
+                    DriverType = row["DriverType"].ToString() ?? string.Empty,
+                    Transport = row["Transport"].ToString() ?? string.Empty,
+                    DeviceCount = Convert.ToInt32(row["DeviceCount"]),
+                    ChannelsPerDevice = Convert.ToInt32(row["ChannelsPerDevice"]),
+                    AddressRule = row["AddressRule"].ToString() ?? string.Empty,
+                    SlotStart = Convert.ToInt32(row["SlotStart"]),
+                });
+            }
+            return list;
+        }
+
+        #endregion
+
+        #region 鍐茬獊妫�娴嬩笌鍝堝笇璁$畻
+
+        /// <summary>
+        /// 妫�娴嬮厤缃啿绐侊細IP鍐茬獊銆丼lot鏄犲皠閲嶅銆侀�氶亾瓒婄晫绛�
+        /// </summary>
+        /// <returns>鍐茬獊鎻忚堪鍒楄〃锛屼负绌哄垯琛ㄧず鏃犲啿绐�</returns>
+        public List<string> DetectConflicts()
+        {
+            var conflicts = new List<string>();
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+
+                // 1. 妫�娴婭P/Endpoint鍐茬獊锛堢浉鍚孍ndpoint鐨勪笉鍚岃澶囷級
+                var ipSql = @"SELECT Endpoint, COUNT(*) as Cnt 
+                    FROM DeviceDefinition 
+                    WHERE Transport = 'Tcp' 
+                    GROUP BY Endpoint 
+                    HAVING Cnt > 1;";
+                var ipDt = helper.GetDataTable(ipSql);
+                foreach (DataRow row in ipDt.Rows)
+                {
+                    conflicts.Add($"IP/Endpoint鍐茬獊: {row["Endpoint"]} 琚� {row["Cnt"]} 涓澶囦娇鐢�");
+                }
+
+                // 2. 妫�娴婼lot鏄犲皠閲嶅锛堢浉鍚孲lot+Capability+Version鏄犲皠鍒颁笉鍚岃澶囷級
+                var slotSql = @"SELECT SlotId, Capability, Version, COUNT(*) as Cnt 
+                    FROM SlotDeviceMapping 
+                    GROUP BY SlotId, Capability, Version 
+                    HAVING Cnt > 1;";
+                var slotDt = helper.GetDataTable(slotSql);
+                foreach (DataRow row in slotDt.Rows)
+                {
+                    conflicts.Add($"Slot鏄犲皠閲嶅: Slot={row["SlotId"]}, Capability={row["Capability"]}, Version={row["Version"]}");
+                }
+
+                // 3. 妫�娴嬮�氶亾瓒婄晫锛圡axVoltage鎴朚axCurrent涓鸿礋鏁帮級
+                var channelSql = @"SELECT DeviceId, ChannelId, MaxVoltage, MaxCurrent 
+                    FROM DeviceChannel 
+                    WHERE MaxVoltage < 0 OR MaxCurrent < 0;";
+                var channelDt = helper.GetDataTable(channelSql);
+                foreach (DataRow row in channelDt.Rows)
+                {
+                    conflicts.Add($"閫氶亾鍙傛暟瓒婄晫: 璁惧={row["DeviceId"]}, 閫氶亾={row["ChannelId"]}, " +
+                                  $"鐢靛帇={row["MaxVoltage"]}, 鐢垫祦={row["MaxCurrent"]}");
+                }
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"鍐茬獊妫�娴嬪け璐�: {ex.Message}", ex);
+            }
+
+            return conflicts;
+        }
+
+        /// <summary>
+        /// 璁$畻褰撳墠閰嶇疆鐨凷HA256鍝堝笇
+        /// </summary>
+        /// <returns>SHA256鍝堝笇瀛楃涓诧紙灏忓啓鍗佸叚杩涘埗锛�</returns>
+        public string ComputeConfigHash()
+        {
+            try
+            {
+                using var helper = new SqliteHelper(_dbPath);
+
+                // 鎷兼帴鎵�鏈夎澶囧畾涔夌殑鍏抽敭淇℃伅浣滀负鍝堝笇杈撳叆
+                var sb = new StringBuilder();
+
+                var deviceDt = helper.GetDataTable("SELECT * FROM DeviceDefinition ORDER BY DeviceId;");
+                foreach (DataRow row in deviceDt.Rows)
+                {
+                    sb.Append($"DEVICE:{row["DeviceId"]}|{row["Kind"]}|{row["DriverType"]}|{row["Transport"]}|{row["Endpoint"]};");
+                }
+
+                var channelDt = helper.GetDataTable("SELECT * FROM DeviceChannel ORDER BY DeviceId, ChannelId;");
+                foreach (DataRow row in channelDt.Rows)
+                {
+                    sb.Append($"CHANNEL:{row["DeviceId"]}|{row["ChannelId"]}|{row["Capability"]}|{row["MaxVoltage"]}|{row["MaxCurrent"]};");
+                }
+
+                var slotDt = helper.GetDataTable("SELECT * FROM SlotDeviceMapping ORDER BY SlotId, Capability, Version;");
+                foreach (DataRow row in slotDt.Rows)
+                {
+                    sb.Append($"SLOT:{row["SlotId"]}|{row["Capability"]}|{row["Version"]}|{row["DeviceId"]}|{row["ChannelId"]};");
+                }
+
+                using var sha256 = SHA256.Create();
+                var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(sb.ToString()));
+                return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
+            }
+            catch (Exception ex)
+            {
+                throw new Exception($"璁$畻閰嶇疆鍝堝笇澶辫触: {ex.Message}", ex);
+            }
+        }
+
+        #endregion
+
+        #region 杈呭姪鏂规硶
+
+        /// <summary>
+        /// SQL瀛楃涓茶浆涔夛紙闃叉娉ㄥ叆锛�
+        /// </summary>
+        private static string Escape(string value)
+        {
+            return value?.Replace("'", "''") ?? string.Empty;
+        }
+
+        #endregion
+    }
+}

--
Gitblit v1.9.1