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()
{

View File

@@ -20,5 +20,6 @@
<ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\DeviceCommand\DeviceCommand.csproj" />
<ProjectReference Include="..\Logger\Logger.csproj" />
<ProjectReference Include="..\ZLGUSBCANFD\ZLGUSBCANFD.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,76 @@
using Prism.Mvvm;
namespace UIShare.UIViewModel
{
/// <summary>
/// CAN 连接配置(对应 ZLGCANFD 构造函数 + 初始化并启动通道 参数)。
/// </summary>
public class CANConfigVM : BindableBase
{
// ===== ZLGCANFD 构造函数参数 =====
private uint _deviceType = 43;
/// <summary>设备类型号43 = USBCANFD-400U</summary>
public uint DeviceType
{
get => _deviceType;
set => SetProperty(ref _deviceType, value);
}
private uint _deviceIndex = 0;
/// <summary>设备索引</summary>
public uint DeviceIndex
{
get => _deviceIndex;
set => SetProperty(ref _deviceIndex, value);
}
private string _abitBaud = "500000";
/// <summary>仲裁域波特率</summary>
public string ABitBaud
{
get => _abitBaud;
set => SetProperty(ref _abitBaud, value);
}
private string _dbitBaud = "2000000";
/// <summary>数据域波特率</summary>
public string DBitBaud
{
get => _dbitBaud;
set => SetProperty(ref _dbitBaud, value);
}
private bool _enableTerminalResistance = true;
/// <summary>是否开启终端电阻</summary>
public bool EnableTerminalResistance
{
get => _enableTerminalResistance;
set => SetProperty(ref _enableTerminalResistance, value);
}
public CANConfigVM() { }
/// <summary>拷贝构造,用于对话框编辑副本。</summary>
public CANConfigVM(CANConfigVM? src)
{
if (src == null) return;
DeviceType = src.DeviceType;
DeviceIndex = src.DeviceIndex;
ABitBaud = src.ABitBaud;
DBitBaud = src.DBitBaud;
EnableTerminalResistance = src.EnableTerminalResistance;
}
/// <summary>把字段拷回目标对象(保存时用)。</summary>
public void CopyTo(CANConfigVM? dst)
{
if (dst == null) return;
dst.DeviceType = DeviceType;
dst.DeviceIndex = DeviceIndex;
dst.ABitBaud = ABitBaud;
dst.DBitBaud = DBitBaud;
dst.EnableTerminalResistance = EnableTerminalResistance;
}
}
}

View File

@@ -65,5 +65,13 @@ namespace UIShare.UIViewModel
get => _serialPortConfig;
set => SetProperty(ref _serialPortConfig, value);
}
/// <summary>CAN 连接参数(对应 ZLGCANFD 构造 + 通道初始化参数)。</summary>
private CANConfigVM _canConfig = new();
public CANConfigVM CANConfig
{
get => _canConfig;
set => SetProperty(ref _canConfig, value);
}
}
}