using AutoMapper;
using CapMachine.Core;
using CapMachine.Model.CANLIN;
using CapMachine.Wpf.CanDrive;
using CapMachine.Wpf.Dtos;
using CapMachine.Wpf.PrismEvent;
using CapMachine.Wpf.Services;
using CapMachine.Wpf.Views;
using ImTools;
using Microsoft.Win32;
using Prism.Commands;
using Prism.Events;
using Prism.Regions;
using Prism.Services.Dialogs;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using static CapMachine.Wpf.Models.ComEnum;
namespace CapMachine.Wpf.ViewModels
{
///
/// ZLG CAN/CANFD 合并配置 ViewModel(模式切换:单选)。
///
///
/// 该 ViewModel 负责 ZLG CAN/CANFD 的 UI 配置与用户交互编排,典型职责包括:
/// - 打开/关闭设备、切换模式(CAN/CANFD),并与 同步状态;
/// - 加载 DBC 并维护可绑定的信号集合(用于读写设置与实时显示);
/// - 管理“读写设置”弹窗的数据准备与回写(写入/读取规则、信号候选集合);
/// - 管理调度表/循环发送相关 UI 状态(软件调度 vs 硬件 auto_send 的选择由 Service/Driver 实现)。
///
/// 线程说明:
/// - 本类绝大多数方法由 UI 线程调用;
/// - Driver 接收线程产生的数据更新事件若需要影响 UI,应通过属性通知或 Dispatcher 进行线程切换(具体由 Service/Driver 侧处理)。
///
public class ZlgCanDriveConfigViewModel : NavigationViewModel
{
///
/// 构造函数。
///
/// Prism 弹窗服务,用于打开读写设置等对话框。
/// 数据访问组件,用于读取/保存配置程序与读写配置等持久化数据。
/// 事件聚合器,用于跨模块消息通知(如状态变化/日志等)。
/// 区域导航服务,用于页面/视图切换。
/// 系统运行服务(与全局运行态/权限等相关)。
/// 通信互斥控制服务,用于保证 CAN/LIN 等通道操作的互斥与安全。
/// 日志服务,用于输出 UI 层操作过程与异常。
/// 逻辑规则服务,为“写入规则/触发规则”下拉框提供数据源。
/// 配置服务,用于读取/应用系统配置参数。
/// ZLG CAN/CANFD 服务层,负责驱动生命周期、DBC、发送/调度表编排。
/// ZLG LIN 服务层,用于互斥判断或组合配置场景。
/// 对象映射器,用于 DTO/实体之间转换。
///
/// 构造时会初始化:
/// - 波特率下拉数据源;
/// - 写入规则下拉数据源;
/// - CAN 配置程序列表(用于 UI 选择)。
///
/// 注意:构造函数仅做“数据源初始化与依赖注入”,不会直接打开设备。
///
public ZlgCanDriveConfigViewModel(IDialogService dialogService, IFreeSql freeSql,
IEventAggregator eventAggregator, IRegionManager regionManager, SysRunService sysRunService,
ComActionService comActionService, ILogService logService, LogicRuleService logicRuleService,
ConfigService configService, ZlgCanDriveService zlgCanDriveService, ZlgLinDriveService zlgLinDriveService,
IMapper mapper)
{
DialogService = dialogService;
FreeSql = freeSql;
EventAggregator = eventAggregator;
RegionManager = regionManager;
SysRunService = sysRunService;
ComActionService = comActionService;
LogService = logService;
LogicRuleService = logicRuleService;
ConfigService = configService;
ZlgCanDriveService = zlgCanDriveService;
ZlgLinDriveService = zlgLinDriveService;
Mapper = mapper;
EventAggregator.GetEvent().Subscribe(CanLinConfigChangedEventCall);
SelectedMode = ZlgCanMode.Can;
ArbBaudRateCbxItems = new ObservableCollection()
{
new CbxItems(){ Key="10000",Text="10 Kbps"},
new CbxItems(){ Key="20000",Text="20 Kbps"},
new CbxItems(){ Key="33000",Text="33 Kbps"},
new CbxItems(){ Key="50000",Text="50 Kbps"},
new CbxItems(){ Key="83000",Text="83 Kbps"},
new CbxItems(){ Key="100000",Text="100 Kbps"},
new CbxItems(){ Key="125000",Text="125 Kbps"},
new CbxItems(){ Key="150000",Text="150 Kbps"},
new CbxItems(){ Key="200000",Text="200 Kbps"},
new CbxItems(){ Key="250000",Text="250 Kbps"},
new CbxItems(){ Key="300000",Text="300 Kbps"},
new CbxItems(){ Key="400000",Text="400 Kbps"},
new CbxItems(){ Key="500000",Text="500 Kbps"},
new CbxItems(){ Key="666000",Text="666 Kbps"},
new CbxItems(){ Key="800000",Text="800 Kbps"},
new CbxItems(){ Key="1000000",Text="1.0 Mbps"},
};
DataBaudRateCbxItems = new ObservableCollection()
{
new CbxItems(){ Key="100000",Text="100 Kbps"},
new CbxItems(){ Key="125000",Text="125 Kbps"},
new CbxItems(){ Key="200000",Text="200 Kbps"},
new CbxItems(){ Key="250000",Text="250 Kbps"},
new CbxItems(){ Key="400000",Text="400 Kbps"},
new CbxItems(){ Key="500000",Text="500 Kbps"},
new CbxItems(){ Key="666000",Text="666 Kbps"},
new CbxItems(){ Key="800000",Text="800 Kbps"},
new CbxItems(){ Key="1000000",Text="1.0 Mbps"},
new CbxItems(){ Key="1500000",Text="1.5 Mbps"},
new CbxItems(){ Key="2000000",Text="2.0 Mbps"},
new CbxItems(){ Key="3000000",Text="3.0 Mbps"},
new CbxItems(){ Key="4000000",Text="4.0 Mbps"},
new CbxItems(){ Key="5000000",Text="5.0 Mbps"},
new CbxItems(){ Key="6700000",Text="6.7 Mbps"},
new CbxItems(){ Key="8000000",Text="8.0 Mbps"},
new CbxItems(){ Key="10000000",Text="10.0 Mbps"},
};
InitWriteRuleCbx();
InitLoadCanConfigPro();
}
///
/// CAN/LIN 配置导入后刷新当前页面。
///
/// 事件消息。
private void CanLinConfigChangedEventCall(string msg)
{
InitWriteRuleCbx();
ReloadCurrentConfigPro();
}
///
/// 初始化“写入规则”下拉框数据源。
///
///
/// 数据源来自 。
/// - Key:规则 Id
/// - Text:规则名称
///
/// 该集合通常被“读写设置”弹窗中的 ComboBox 绑定使用。
///
private void InitWriteRuleCbx()
{
WriteRuleCbxItems = new ObservableCollection();
foreach (var itemRule in LogicRuleService.LogicRuleDtos)
{
WriteRuleCbxItems.Add(new CbxItems()
{
Key = itemRule.Id.ToString(),
Text = itemRule.Name
});
}
}
///
/// 打开“读写设置”弹窗。
///
///
/// 弹窗数据准备流程:
/// - 基于当前页面的 / 克隆出可编辑副本;
/// - 根据 构建信号候选集合(MsgName/SignalName/描述等);
/// - 通过 打开 DialogZlgCanLinRwConfigView;
/// - 用户点击 OK 后回调中调用 ReloadCurrentConfigPro 刷新当前配置并提示。
///
/// 前置条件:
/// - 必须先选择配置程序( 不为 null)。
/// - 推荐调用方在进入本方法前确保已连接设备且 DBC 已加载,否则候选信号集合可能为空。
///
private void OpenRwDialog()
{
try
{
if (SelectCanLinConfigPro == null)
{
MessageBox.Show("选中CAN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
var writeClones = new ObservableCollection(
(ListWriteCanLinRWConfigDto ?? new ObservableCollection())
.Select(CloneRwDto));
foreach (var item in writeClones)
{
item.RWInfo = RW.Write;
}
var readClones = new ObservableCollection(
(ListReadCanLinRWConfigDto ?? new ObservableCollection())
.Select(CloneRwDto));
foreach (var item in readClones)
{
item.RWInfo = RW.Read;
}
var candidates = new ObservableCollection();
if (ListCanDbcModel != null)
{
foreach (var sig in ListCanDbcModel)
{
candidates.Add(new DialogZlgCanLinRwConfigViewModel.SignalCandidate
{
MsgName = sig.MsgName,
SignalName = sig.SignalName,
Name = sig.Name,
Desc = sig.SignalDesc,
});
}
}
var pars = new DialogParameters
{
{ "Title", "读写设置" },
{ "CanLinConfigProId", SelectCanLinConfigPro.Id },
{ "IsEditable", IsRwEditable },
{ "IsCanFdSchedule", SelectedMode == ZlgCanMode.CanFd },
{ "WriteConfigs", writeClones },
{ "ReadConfigs", readClones },
{ "SignalCandidates", candidates },
};
DialogService.ShowDialog(nameof(DialogZlgCanLinRwConfigView), pars, r =>
{
if (r.Result == ButtonResult.OK)
{
ReloadCurrentConfigPro();
OpTip = "读写设置已保存";
}
});
}
catch (Exception ex)
{
LogService.Error($"ZLG 打开读写设置弹窗失败:{ex}");
MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
///
/// 克隆一个读写配置 DTO。
///
/// 源对象。
/// 新实例(字段逐个复制)。
///
/// 目的:
/// - 弹窗编辑时不直接修改页面当前集合,避免用户取消时污染原始配置;
/// - 用户确认(OK)后再由弹窗 ViewModel 负责持久化,页面侧随后通过 Reload 刷新。
///
private static CanLinRWConfigDto CloneRwDto(CanLinRWConfigDto src)
{
return new CanLinRWConfigDto
{
Id = src.Id,
RWInfo = src.RWInfo,
Name = src.Name,
MsgFrameName = src.MsgFrameName,
SignalName = src.SignalName,
DefautValue = src.DefautValue,
LogicRuleId = src.LogicRuleId,
LogicRuleDto = src.LogicRuleDto,
};
}
///
/// Dialog 服务。
///
public IDialogService DialogService { get; }
///
/// FreeSql。
///
public IFreeSql FreeSql { get; }
///
/// 事件聚合。
///
public IEventAggregator EventAggregator { get; }
///
/// 区域导航。
///
public IRegionManager RegionManager { get; }
///
/// 系统运行服务。
///
public SysRunService SysRunService { get; }
///
/// 通信互斥控制。
///
public ComActionService ComActionService { get; }
///
/// 日志。
///
public ILogService LogService { get; }
///
/// 逻辑规则服务。
///
public LogicRuleService LogicRuleService { get; }
///
/// 配置服务。
///
public ConfigService ConfigService { get; }
///
/// ZLG CAN 服务。
///
public ZlgCanDriveService ZlgCanDriveService { get; }
///
/// ZLG LIN 服务(用于互斥判断)。
///
public ZlgLinDriveService ZlgLinDriveService { get; }
///
/// Mapper。
///
public IMapper Mapper { get; }
///
/// 的 backing 字段。
///
private ObservableCollection _arbBaudRateCbxItems = new ObservableCollection();
///
/// 仲裁波特率下拉项(bps)。
///
public ObservableCollection ArbBaudRateCbxItems
{
get { return _arbBaudRateCbxItems; }
set { _arbBaudRateCbxItems = value; RaisePropertyChanged(); }
}
///
/// 的 backing 字段。
///
private ObservableCollection _dataBaudRateCbxItems = new ObservableCollection();
///
/// 数据波特率下拉项(bps)。
///
public ObservableCollection DataBaudRateCbxItems
{
get { return _dataBaudRateCbxItems; }
set { _dataBaudRateCbxItems = value; RaisePropertyChanged(); }
}
///
/// 配置程序列表的本地缓存(数据库读取结果)。
///
///
/// 与 的区别:
/// - 本字段用于保存从数据库一次性读取的实体列表;
/// - UI 绑定通常使用 (ObservableCollection)。
///
private List _canLinConfigPros = new List();
private ObservableCollection _writeRuleCbxItems = new ObservableCollection();
///
/// 写入规则下拉项(逻辑规则列表)。
///
///
/// 该集合由 初始化,通常绑定到读写设置弹窗或列表编辑处的 ComboBox。
///
public ObservableCollection WriteRuleCbxItems
{
get { return _writeRuleCbxItems; }
set { _writeRuleCbxItems = value; RaisePropertyChanged(); }
}
private ObservableCollection _listWriteCanLinRWConfigDto = new ObservableCollection();
///
/// 写入配置集合(绑定到写入 DataGrid)。
///
///
/// 每条记录对应一个“写入信号”配置项,包含:
/// - MsgFrameName/SignalName:定位 DBC 信号
/// - LogicRuleId:写入规则
/// - DefautValue:默认写入值
///
/// 注意:
/// - 配置程序激活( true)时应禁止编辑。
/// - 读写互斥/重复校验主要在读写设置弹窗 ViewModel 中实现。
///
public ObservableCollection ListWriteCanLinRWConfigDto
{
get { return _listWriteCanLinRWConfigDto; }
set { _listWriteCanLinRWConfigDto = value; RaisePropertyChanged(); }
}
private ObservableCollection _listReadCanLinRWConfigDto = new ObservableCollection();
///
/// 读取配置集合(绑定到读取 DataGrid)。
///
///
/// 每条记录对应一个“读取信号”配置项,包含:
/// - MsgFrameName/SignalName:定位 DBC 信号
/// - DefautValue:默认值/初始值(视业务语义而定)
///
/// 读取配置通常用于“从接收帧解码后更新 UI/变量”的目标信号集合。
///
public ObservableCollection ListReadCanLinRWConfigDto
{
get { return _listReadCanLinRWConfigDto; }
set { _listReadCanLinRWConfigDto = value; RaisePropertyChanged(); }
}
///
/// 当前在写入 DataGrid 中选中的行。
///
///
/// 用于 Delete 等操作定位目标记录。该属性不直接通知 UI(由 SelectionChanged 命令更新)。
///
private CanLinRWConfigDto? SelectedWriteCanLinRWConfigDto { get; set; }
///
/// 当前在读取 DataGrid 中选中的行。
///
///
/// 用于 Delete 等操作定位目标记录。该属性不直接通知 UI(由 SelectionChanged 命令更新)。
///
private CanLinRWConfigDto? SelectedReadCanLinRWConfigDto { get; set; }
private string? _opTip;
///
/// 操作提示(用于 UI 状态展示)。
///
public string? OpTip
{
get { return _opTip; }
set { _opTip = value; RaisePropertyChanged(); }
}
private string? _lastError;
///
/// 最近一次错误信息(用于 UI 状态展示)。
///
public string? LastError
{
get { return _lastError; }
set { _lastError = value; RaisePropertyChanged(); }
}
private bool _isCanConfigProActive;
///
/// 当前配置程序是否已激活(对齐图莫斯 Active 语义)。
/// 激活后禁止切换配置程序。
///
public bool IsCanConfigProActive
{
get { return _isCanConfigProActive; }
set
{
_isCanConfigProActive = value;
RaisePropertyChanged();
RaisePropertyChanged(nameof(IsRwEditable));
}
}
public bool IsRwEditable
{
get { return !IsCanConfigProActive; }
}
private bool _isCANConfigDatagridActive = true;
///
/// 配置程序 DataGrid 是否可操作(与 IsCanConfigProActive 取反)。
///
public bool IsCANConfigDatagridActive
{
get { return _isCANConfigDatagridActive; }
set { _isCANConfigDatagridActive = value; RaisePropertyChanged(); }
}
///
/// 的 backing 字段。
///
private ZlgCanMode _selectedMode;
///
/// 模式选择:CAN/CANFD(单选)。
///
public ZlgCanMode SelectedMode
{
get { return _selectedMode; }
set
{
// 打开状态下不允许切换模式,避免驱动状态错乱。
if (ZlgCanDriveService.OpenState && _selectedMode != value)
{
MessageBox.Show("请先关闭 CAN 后再切换 CAN/CANFD 模式", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
RaisePropertyChanged(nameof(SelectedModeKey));
return;
}
_selectedMode = value;
RaisePropertyChanged();
RaisePropertyChanged(nameof(SelectedModeKey));
RaisePropertyChanged(nameof(IsCanMode));
RaisePropertyChanged(nameof(IsCanFdMode));
RaisePropertyChanged(nameof(DbcPathTitle));
RaisePropertyChanged(nameof(ConnectButtonText));
RaisePropertyChanged(nameof(CloseButtonText));
RaisePropertyChanged(nameof(CurrentDbcPath));
RaisePropertyChanged(nameof(CurrentCycle));
RaisePropertyChanged(nameof(CurrentSchEnable));
RaisePropertyChanged(nameof(CurrentArbBaudRate));
RaisePropertyChanged(nameof(CurrentDataBaudRate));
RaisePropertyChanged(nameof(CurrentISOEnable));
RaisePropertyChanged(nameof(CurrentResEnable));
RaisePropertyChanged(nameof(BaudRateTitle));
RaisePropertyChanged(nameof(DataBaudRateTitle));
ZlgCanDriveService.Mode = value;
// 当用户手动切换 CAN/CANFD 时,如果当前配置缺少对应扩展 DTO,需要先生成默认 DTO,
// 否则界面控件无法编辑(setter 会因 DTO 为空直接 return)。
if (SelectCanLinConfigPro != null)
{
if (value == ZlgCanMode.Can)
{
if (SelectedCANConfigExdDto == null)
{
SelectedCANConfigExdDto = new CANConfigExdDto()
{
Id = 0,
BaudRate = 500000,
DbcPath = string.Empty,
Cycle = 100,
SchEnable = false,
};
}
}
else
{
if (SelectedCANFdConfigExdDto == null)
{
SelectedCANFdConfigExdDto = new CANFdConfigExdDto()
{
Id = 0,
ArbBaudRate = 500000,
DataBaudRate = 2000000,
ISOEnable = true,
ResEnable = false,
DbcPath = string.Empty,
Cycle = 100,
SchEnable = false,
};
}
}
}
// 切换模式后,为避免界面仍显示旧的 DBC 信号列表,清空信号集合。
// 配置程序列表不应随模式切换而过滤/刷新。
ListCanDbcModel = new ObservableCollection();
}
}
///
/// 写入 DataGrid 选中项变化命令。
///
///
/// 由 XAML 绑定到 SelectionChanged,用于把当前选中行同步到 。
///
private DelegateCommand