From 559890c482f5973f06aafdfec9550fee5ba3c7f2 Mon Sep 17 00:00:00 2001
From: wenxiangchao <wenxiangchao@ihengxin.com.cn>
Date: 星期一, 09 十一月 2026 17:02:15 +0800
Subject: [PATCH] feat: CAN 模拟器 TCP 化 + 多设备并发调试 + 后台生命周期管理
---
Plugin.DeviceConfig/Model/DeviceConfigModel.cs | 140 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 138 insertions(+), 2 deletions(-)
diff --git a/Plugin.DeviceConfig/Model/DeviceConfigModel.cs b/Plugin.DeviceConfig/Model/DeviceConfigModel.cs
index d679925..1d3b5e2 100644
--- a/Plugin.DeviceConfig/Model/DeviceConfigModel.cs
+++ b/Plugin.DeviceConfig/Model/DeviceConfigModel.cs
@@ -49,6 +49,11 @@
DriverType TEXT NOT NULL,
Transport TEXT NOT NULL,
Endpoint TEXT NOT NULL,
+ ExecutionMode TEXT NOT NULL DEFAULT 'Local',
+ AuthorityId TEXT NOT NULL DEFAULT '',
+ BrokerId TEXT NOT NULL DEFAULT '',
+ OwnerProcessId TEXT NOT NULL DEFAULT '',
+ PhysicalSessionKey TEXT NOT NULL DEFAULT '',
EffectiveVersion TEXT NOT NULL DEFAULT '',
CreatedAt TEXT NOT NULL,
UpdatedAt TEXT NOT NULL
@@ -101,10 +106,39 @@
};
helper.ExecuteBatchSql(sqls);
+
+ // V3.0 搂05锛氭墽琛屼綅缃瓧娈佃縼绉伙紙鏃у簱琛ュ垪锛屾柊搴撳凡鍦ㄥ缓琛ㄨ鍙ヤ腑鍖呭惈锛�
+ MigrateExecutionColumns(helper);
}
catch (Exception ex)
{
throw new Exception($"鍒濆鍖栬澶囬厤缃暟鎹簱澶辫触: {ex.Message}", ex);
+ }
+ }
+
+ /// <summary>
+ /// V3.0锛氭棫鐗� DeviceDefinition 琛ㄨˉ鍏呮墽琛屼綅缃簲鍒楋紙SQLite 鏃犺绾ч攣鍋囪锛岄�愬垪 PRAGMA 妫�鏌ュ悗 ALTER锛�
+ /// </summary>
+ private static void MigrateExecutionColumns(HMI.Core.Util.SqliteHelper helper)
+ {
+ var dt = helper.GetDataTable("PRAGMA table_info(DeviceDefinition);");
+ var existing = new HashSet<string>(
+ dt.Rows.Cast<DataRow>().Select(r => r["name"].ToString()), StringComparer.OrdinalIgnoreCase);
+
+ var additions = new (string Col, string Ddl)[]
+ {
+ ("ExecutionMode", "ALTER TABLE DeviceDefinition ADD COLUMN ExecutionMode TEXT NOT NULL DEFAULT 'Local';"),
+ ("AuthorityId", "ALTER TABLE DeviceDefinition ADD COLUMN AuthorityId TEXT NOT NULL DEFAULT '';"),
+ ("BrokerId", "ALTER TABLE DeviceDefinition ADD COLUMN BrokerId TEXT NOT NULL DEFAULT '';"),
+ ("OwnerProcessId", "ALTER TABLE DeviceDefinition ADD COLUMN OwnerProcessId TEXT NOT NULL DEFAULT '';"),
+ ("PhysicalSessionKey", "ALTER TABLE DeviceDefinition ADD COLUMN PhysicalSessionKey TEXT NOT NULL DEFAULT '';"),
+ };
+ foreach (var (col, ddl) in additions)
+ {
+ if (!existing.Contains(col))
+ {
+ helper.ExecuteSql(ddl);
+ }
}
}
@@ -156,13 +190,18 @@
device.UpdatedAt = DateTime.Now;
var sql = $@"INSERT OR REPLACE INTO DeviceDefinition
- (DeviceId, Kind, DriverType, Transport, Endpoint, EffectiveVersion, CreatedAt, UpdatedAt)
+ (DeviceId, Kind, DriverType, Transport, Endpoint, ExecutionMode, AuthorityId, BrokerId, OwnerProcessId, PhysicalSessionKey, EffectiveVersion, CreatedAt, UpdatedAt)
VALUES (
'{Escape(device.DeviceId)}',
'{device.Kind}',
'{Escape(device.DriverType)}',
'{device.Transport}',
'{Escape(device.Endpoint)}',
+ '{device.ExecutionMode}',
+ '{Escape(device.AuthorityId)}',
+ '{Escape(device.BrokerId)}',
+ '{Escape(device.OwnerProcessId)}',
+ '{Escape(device.PhysicalSessionKey)}',
'{Escape(device.EffectiveVersion)}',
'{device.CreatedAt:yyyy-MM-dd HH:mm:ss}',
'{device.UpdatedAt:yyyy-MM-dd HH:mm:ss}'
@@ -203,12 +242,23 @@
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,
+ ExecutionMode = HasColumn(row.Table, "ExecutionMode") && Enum.TryParse<ExecutionModeKind>(row["ExecutionMode"].ToString(), out var em)
+ ? em : ExecutionModeKind.Local,
+ AuthorityId = HasColumn(row.Table, "AuthorityId") ? row["AuthorityId"].ToString() ?? string.Empty : string.Empty,
+ BrokerId = HasColumn(row.Table, "BrokerId") ? row["BrokerId"].ToString() ?? string.Empty : string.Empty,
+ OwnerProcessId = HasColumn(row.Table, "OwnerProcessId") ? row["OwnerProcessId"].ToString() ?? string.Empty : string.Empty,
+ PhysicalSessionKey = HasColumn(row.Table, "PhysicalSessionKey") ? row["PhysicalSessionKey"].ToString() ?? string.Empty : 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;
+ }
+
+ private static bool HasColumn(DataTable dt, string columnName)
+ {
+ return dt.Columns.Cast<DataColumn>().Any(c => c.ColumnName.Equals(columnName, StringComparison.OrdinalIgnoreCase));
}
#endregion
@@ -603,7 +653,13 @@
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"]};");
+ // V3.0锛氭墽琛屼綅缃瓧娈佃繘鍏ラ厤缃搱甯岋紙INV-04锛欱roker 杩炴帴鎼� configHash 瀵硅处锛夛紱
+ // OwnerProcessId 灞炶繍琛屾湡淇℃伅锛屼笉鍙備笌鍝堝笇锛岄伩鍏嶅涓婚噸鍚鑷村搱甯屾紓绉�
+ string execMode = HasColumn(deviceDt, "ExecutionMode") ? row["ExecutionMode"].ToString() : "Local";
+ string authId = HasColumn(deviceDt, "AuthorityId") ? row["AuthorityId"].ToString() : "";
+ string bkId = HasColumn(deviceDt, "BrokerId") ? row["BrokerId"].ToString() : "";
+ string sessKey = HasColumn(deviceDt, "PhysicalSessionKey") ? row["PhysicalSessionKey"].ToString() : "";
+ sb.Append($"DEVICE:{row["DeviceId"]}|{row["Kind"]}|{row["DriverType"]}|{row["Transport"]}|{row["Endpoint"]}|{execMode}|{authId}|{bkId}|{sessKey};");
}
var channelDt = helper.GetDataTable("SELECT * FROM DeviceChannel ORDER BY DeviceId, ChannelId;");
@@ -630,6 +686,86 @@
#endregion
+ #region 鍙戝竷闂ㄧ锛圴3.0 搂05锛�
+
+ /// <summary>
+ /// 鍙戝竷鍓嶆牎楠屾墽琛屼綅缃粦瀹氾細浠茶鍩熷繀濉笖鍚堟硶銆丅roker 鍩熶竴鑷存�с�佷細璇濆敮涓�瀹夸富銆�
+ /// 杩斿洖閿欒鍒楄〃锛屼负绌哄垯鍏佽鍙戝竷銆�
+ /// </summary>
+ public List<string> ValidatePublishGate()
+ {
+ var errors = new List<string>();
+ try
+ {
+ var devices = GetAllDevices();
+ var authorityCatalog = V3AuthorityCatalog.AuthorityIds;
+
+ // 1. 閫愯澶囨牎楠屾墽琛屼綅缃瓧娈�
+ foreach (var dev in devices)
+ {
+ if (string.IsNullOrWhiteSpace(dev.AuthorityId))
+ {
+ errors.Add($"璁惧 [{dev.DeviceId}] 鏈�夋嫨浠茶鍩燂紙AuthorityId 蹇呭~锛屾棫鏁版嵁璇烽噸鏂颁繚瀛橈級");
+ continue;
+ }
+ if (!authorityCatalog.Contains(dev.AuthorityId, StringComparer.OrdinalIgnoreCase))
+ {
+ errors.Add($"璁惧 [{dev.DeviceId}] 浠茶鍩� [{dev.AuthorityId}] 涓嶅湪閰嶇疆鏋氫妇鍊欓�変腑");
+ continue;
+ }
+
+ if (dev.ExecutionMode == ExecutionModeKind.Local)
+ {
+ // Local 璁惧蹇呴』鐢辨湰鏈� Local 鍩熶徊瑁侊紙宸插垎閰嶇粰鍞竴鎵ц杩涚▼锛�
+ if (!V3AuthorityCatalog.IsLocalAuthority(dev.AuthorityId))
+ {
+ errors.Add($"璁惧 [{dev.DeviceId}] 涓� Local 鐩磋繛锛屼徊瑁佸煙搴斾负 local- 鍓嶇紑锛屽疄闄呬负 [{dev.AuthorityId}]");
+ }
+ }
+ else
+ {
+ if (string.IsNullOrWhiteSpace(dev.BrokerId))
+ {
+ errors.Add($"璁惧 [{dev.DeviceId}] 涓� Broker 鍏变韩锛屽繀椤绘寚瀹� BrokerId");
+ }
+ else if (!V3AuthorityCatalog.BrokerIds.Contains(dev.BrokerId, StringComparer.OrdinalIgnoreCase))
+ {
+ errors.Add($"璁惧 [{dev.DeviceId}] BrokerId [{dev.BrokerId}] 涓嶅湪閰嶇疆鏋氫妇鍊欓�変腑");
+ }
+ // BrokerId 涓庤澶囧煙涓�鑷达細Broker 璁惧鐨� AuthorityId 鍗冲叾 Broker 鍩�
+ else if (!string.Equals(dev.BrokerId, dev.AuthorityId, StringComparison.OrdinalIgnoreCase))
+ {
+ errors.Add($"璁惧 [{dev.DeviceId}] BrokerId [{dev.BrokerId}] 涓庝徊瑁佸煙 [{dev.AuthorityId}] 涓嶄竴鑷�");
+ }
+ if (string.IsNullOrWhiteSpace(dev.PhysicalSessionKey))
+ {
+ errors.Add($"璁惧 [{dev.DeviceId}] 涓� Broker 鍏变韩锛屽繀椤绘寚瀹氱墿鐞嗕細璇濋敭 PhysicalSessionKey");
+ }
+ }
+ }
+
+ // 2. 鍚屼竴鐗╃悊浼氳瘽鍙兘鏈変竴涓涓伙細鐩稿悓 SessionKey 鐨勮澶囧繀椤诲綊灞炲悓涓�浠茶鍩�
+ var sessionGroups = devices
+ .Where(d => !string.IsNullOrWhiteSpace(d.PhysicalSessionKey))
+ .GroupBy(d => d.PhysicalSessionKey, StringComparer.OrdinalIgnoreCase);
+ foreach (var grp in sessionGroups)
+ {
+ var domains = grp.Select(d => d.AuthorityId).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
+ if (domains.Count > 1)
+ {
+ errors.Add($"鐗╃悊浼氳瘽 [{grp.Key}] 琚涓徊瑁佸煙寮曠敤: {string.Join(", ", domains)}锛堝悓涓�浼氳瘽鍙兘鏈変竴涓涓伙級");
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new Exception($"鍙戝竷闂ㄧ鏍¢獙澶辫触: {ex.Message}", ex);
+ }
+ return errors;
+ }
+
+ #endregion
+
#region 杈呭姪鏂规硶
/// <summary>
--
Gitblit v1.9.1