CAN设置添加
This commit is contained in:
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 _);
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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>
|
||||
|
||||
76
UIShare/UIViewModel/CANConfigVM.cs
Normal file
76
UIShare/UIViewModel/CANConfigVM.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user