CAN设置添加

This commit is contained in:
hsc
2026-07-02 15:36:55 +08:00
parent 63f43853fa
commit 60dc155ccf
20 changed files with 1250 additions and 413 deletions

View File

@@ -1,7 +1,10 @@
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Logger;
using UIShare.UIViewModel;
namespace UIShare.GlobalVariable
{
@@ -44,6 +47,7 @@ namespace UIShare.GlobalVariable
{
// 如果不存在,创建一个带 Title 的默认配置并保存
var defaultConfig = new SystemConfig { Title = title };
EnsureDefaultCanDevice(defaultConfig);
Save(defaultConfig);
return defaultConfig;
}
@@ -58,12 +62,16 @@ namespace UIShare.GlobalVariable
TypeNameHandling = TypeNameHandling.All
});
return config ?? new SystemConfig { Title = title };
config ??= new SystemConfig { Title = title };
EnsureDefaultCanDevice(config);
return config;
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify(title, $"格子 [{title}] 配置加载失败: {ex.Message}");
return new SystemConfig { Title = title };
var fallback = new SystemConfig { Title = title };
EnsureDefaultCanDevice(fallback);
return fallback;
}
}
}
@@ -98,5 +106,34 @@ namespace UIShare.GlobalVariable
}
}
}
/// <summary>
/// 确保配置中至少包含一个 CAN 设备(对应 SystemConfig.CANFD
/// 旧配置或空配置会自动升级,使用户在设置界面能看到 CAN 设备。
/// </summary>
public static void EnsureDefaultCanDevice(SystemConfig config)
{
if (config.DeviceList == null)
{
config.DeviceList = new ObservableCollection<DeviceInfoVM>();
}
bool hasCan = config.DeviceList.Any(d =>
string.Equals(d?.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase) ||
string.Equals(d?.DeviceType, "ZLGCANFD", StringComparison.OrdinalIgnoreCase));
if (!hasCan)
{
config.DeviceList.Add(new DeviceInfoVM
{
DeviceName = "CAN",
DeviceType = "ZLGCANFD",
Remark = "周立功 CANFD 接口卡",
ConnectionType = "CAN",
IsEnabled = true,
IsConnected = false
});
}
}
}
}

View File

@@ -8,6 +8,7 @@ using System.IO.Ports;
using System.Linq;
using System.Reflection;
using UIShare.UIViewModel;
using ZLGUSBCANFD;
namespace UIShare.GlobalVariable
{
@@ -28,6 +29,7 @@ namespace UIShare.GlobalVariable
/// <summary>类名 → Type 的反射缓存(仅扫描一次)。</summary>
private static readonly IReadOnlyDictionary<string, Type> _deviceTypeMap = BuildDeviceTypeMap();
public ZLGCANFD CANFD { get; set; }
public DeviceManager(SystemConfig systemConfig, GlobalInfo globalInfo)
{
@@ -37,6 +39,7 @@ namespace UIShare.GlobalVariable
_scopeName = _systemConfig.Title;
InitDevices();
}
/// <summary>
/// 根据设备配置提取唯一的硬件指纹字符串。
/// <para>Tcp → "Tcp:IP:Port"Serial → "Serial:PortName";无法识别则返回空字符串。</para>
@@ -55,9 +58,14 @@ namespace UIShare.GlobalVariable
return $"Serial:{config.SerialPortConfig.PortName}";
}
if (string.Equals(config.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase)
&& config.CANConfig != null)
{
return $"CAN:{config.CANConfig.DeviceIndex}";
}
return string.Empty;
}
private void InitDevices()
{
DeviceMap = new Dictionary<string, IBaseInterface>(StringComparer.OrdinalIgnoreCase);
@@ -68,6 +76,53 @@ namespace UIShare.GlobalVariable
{
if (config == null || !config.IsEnabled) continue;
// CAN 设备ZLGCANFD 不实现 IBaseInterface通过 SystemConfig.CANFD 单独管理,
// 按指纹从全局 CanPool 创建/复用实例,并注册作用域引用计数。
if (string.Equals(config.ConnectionType, "CAN", StringComparison.OrdinalIgnoreCase))
{
var fingerprint = ExtractHardwareFingerprint(config);
if (string.IsNullOrEmpty(fingerprint))
{
LoggerHelper.Warn($"设备 [{config.DeviceName}] 无法提取硬件指纹(连接方式={config.ConnectionType}),已跳过。");
continue;
}
if (config.CANConfig == null)
{
LoggerHelper.Warn($"设备 [{config.DeviceName}] 缺少 CAN 连接参数,已跳过。");
continue;
}
try
{
// 按指纹全局唯一创建 ZLGCANFD 实例maxChannels 默认 4对应 USBCANFD-400U
var canLazy = _globalInfo.CanPool.GetOrAdd(fingerprint, key => new Lazy<ZLGCANFD>(() =>
new ZLGCANFD(config.CANConfig.DeviceType, config.CANConfig.DeviceIndex, 4)));
_systemConfig.CANFD = canLazy.Value;
CANFD = canLazy.Value;
// 注册作用域引用计数
if (!string.IsNullOrEmpty(_scopeName))
{
var scopeList = _globalInfo.DeviceAndScopeDic.GetOrAdd(fingerprint,
_ => new Lazy<List<string>>(() => new List<string>())).Value;
lock (scopeList)
{
if (!scopeList.Contains(_scopeName))
scopeList.Add(_scopeName);
}
}
LoggerHelper.Info($"已加载 CAN 设备 [{config.DeviceName}] 指纹={fingerprint}(通过 SystemConfig.CANFD 管理)");
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify(_scopeName, $"CAN 设备 [{config.DeviceName}] 实例化失败:{ex.Message}");
}
continue;
}
if (string.IsNullOrWhiteSpace(config.DeviceType) ||
!_deviceTypeMap.TryGetValue(config.DeviceType, out var deviceType))
{
@@ -391,6 +446,17 @@ namespace UIShare.GlobalVariable
}
}
// 尝试从 CanPool 取出并销毁
if (_globalInfo.CanPool.TryRemove(fingerprint, out var canLazy))
{
if (canLazy.IsValueCreated)
{
try { canLazy.Value.Dispose(); }
catch { /* 销毁时忽略异常 */ }
LoggerHelper.Info($"指纹 [{fingerprint}] 无作用域引用,已销毁 CAN 设备实例。");
}
}
// 同步清除 DeviceAndScopeDic 中的空条目
_globalInfo.DeviceAndScopeDic.TryRemove(fingerprint, out _);
}

View File

@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ZLGUSBCANFD;
namespace UIShare.GlobalVariable
{
@@ -19,6 +20,9 @@ namespace UIShare.GlobalVariable
/// <summary>硬件指纹 → 设备实例的并发池,确保同一物理硬件全局只创建一个驱动实例。</summary>
public ConcurrentDictionary<string, Lazy<IBaseInterface>> HardwarePool { get; set; }
/// <summary>CAN 硬件指纹 → ZLGCANFD 实例的并发池,确保同一 CAN 卡全局只创建一个驱动实例。</summary>
public ConcurrentDictionary<string, Lazy<ZLGCANFD>> CanPool { get; set; }
/// <summary>硬件指纹 → 正在使用该设备的作用域名称列表,用于引用计数与安全销毁。</summary>
public ConcurrentDictionary<string, Lazy<List<string>>> DeviceAndScopeDic { get; set; }
@@ -45,6 +49,7 @@ namespace UIShare.GlobalVariable
ConfigDic = new();
ScopeDic = new();
HardwarePool = new ConcurrentDictionary<string, Lazy<IBaseInterface>>(StringComparer.OrdinalIgnoreCase);
CanPool = new ConcurrentDictionary<string, Lazy<ZLGCANFD>>(StringComparer.OrdinalIgnoreCase);
DeviceAndScopeDic = new ConcurrentDictionary<string, Lazy<List<string>>>(StringComparer.OrdinalIgnoreCase);
CurrentScope = "default";
}

View File

@@ -564,7 +564,11 @@ namespace UIShare
{
try
{
instance = _deviceManager.DeviceMap[targetType.Name];
if(targetType.Name== "ZLGCANFD")
{
instance = _deviceManager.CANFD;
}
else instance = _deviceManager.DeviceMap[targetType.Name];
}
catch (Exception ex)
{

View File

@@ -9,6 +9,7 @@ using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UIShare.UIViewModel;
using ZLGUSBCANFD;
using static UIShare.UIViewModel.ParameterVM;
namespace UIShare.GlobalVariable
@@ -29,6 +30,7 @@ namespace UIShare.GlobalVariable
public string DefaultDBCFilePath { get; set; } = "";
public ObservableCollection<DeviceInfoVM> DeviceList = new();
public ObservableCollection<SharedParameter> SharedParameterList = new();
public ZLGCANFD CANFD = new();
[JsonIgnore]
public ObservableCollection<ParameterVM> ParameterList = new()
{