using AutoMapper; using CapMachine.Core; using CapMachine.Model.CANLIN; using CapMachine.Wpf.Dtos; using CapMachine.Wpf.LinDrive; 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.Linq; using System.Windows; using System.Windows.Controls; using static CapMachine.Wpf.Models.ComEnum; namespace CapMachine.Wpf.ViewModels { /// /// ZLG LIN 配置 ViewModel。 /// public class ZlgLinDriveConfigViewModel : NavigationViewModel { /// /// 构造函数。 /// public ZlgLinDriveConfigViewModel(IDialogService dialogService, IFreeSql freeSql, IEventAggregator eventAggregator, IRegionManager regionManager, SysRunService sysRunService, ConfigService configService, ZlgLinDriveService zlgLinDriveService, ZlgCanDriveService zlgCanDriveService, ComActionService comActionService, LogicRuleService logicRuleService, IMapper mapper) { DialogService = dialogService; FreeSql = freeSql; EventAggregator = eventAggregator; RegionManager = regionManager; SysRunService = sysRunService; ConfigService = configService; ZlgLinDriveService = zlgLinDriveService; ZlgCanDriveService = zlgCanDriveService; ComActionService = comActionService; LogicRuleService = logicRuleService; Mapper = mapper; InitLoadLinConfigPro(); } /// /// Prism 弹窗服务。 /// public IDialogService DialogService { get; } /// /// FreeSql(用于 LIN 配置程序/读写项/调度表 的查询与持久化)。 /// public IFreeSql FreeSql { get; } /// /// 事件聚合器。 /// public IEventAggregator EventAggregator { get; } /// /// 区域管理器。 /// public IRegionManager RegionManager { get; } /// /// 系统运行状态服务。 /// public SysRunService SysRunService { get; } /// /// 全局配置服务(用于记录当前系统选择的通信类型等状态)。 /// public ConfigService ConfigService { get; } /// /// ZLG LIN 服务(LIN 生命周期、LDF 解析、软件调度、收发指示灯等)。 /// public ZlgLinDriveService ZlgLinDriveService { get; } /// /// ZLG CAN 服务。 /// 说明:LIN 与 CAN 共用同一硬件/设备句柄,打开 LIN 前需要确保 CAN 已关闭。 /// public ZlgCanDriveService ZlgCanDriveService { get; } /// /// 通信驱动互斥控制服务(同一时刻只能有一种驱动操作压缩机)。 /// public ComActionService ComActionService { get; } /// /// 逻辑规则服务。 /// public LogicRuleService LogicRuleService { get; } /// /// AutoMapper。 /// public IMapper Mapper { get; } private List linConfigPros = new List(); private ObservableCollection? _ListCanLinConfigPro; /// /// LIN 配置程序集合。 /// public ObservableCollection? ListCanLinConfigPro { get { return _ListCanLinConfigPro; } set { _ListCanLinConfigPro = value; RaisePropertyChanged(); } } /// /// 选中的配置程序。 /// public CanLinConfigPro? SelectCanLinConfigPro { get; set; } private ObservableCollection _listWriteCanLinRWConfigDto = new ObservableCollection(); /// /// 写入配置 DTO 集合(UI 展示/编辑用)。 /// public ObservableCollection ListWriteCanLinRWConfigDto { get { return _listWriteCanLinRWConfigDto; } set { _listWriteCanLinRWConfigDto = value; RaisePropertyChanged(); } } private ObservableCollection _listReadCanLinRWConfigDto = new ObservableCollection(); /// /// 读取配置 DTO 集合(UI 展示/编辑用)。 /// public ObservableCollection ListReadCanLinRWConfigDto { get { return _listReadCanLinRWConfigDto; } set { _listReadCanLinRWConfigDto = value; RaisePropertyChanged(); } } private ObservableCollection _listLINScheduleConfigDto = new ObservableCollection(); /// /// LIN 调度表集合(系统层)。 /// public ObservableCollection ListLINScheduleConfigDto { get { return _listLINScheduleConfigDto; } set { _listLINScheduleConfigDto = value; RaisePropertyChanged(); } } private bool _isLinConfigProActive; /// /// 当前配置程序是否已激活(对齐 CAN Active 语义)。激活后禁止切换配置程序。 /// public bool IsLinConfigProActive { get { return _isLinConfigProActive; } set { _isLinConfigProActive = value; RaisePropertyChanged(); RaisePropertyChanged(nameof(IsRwEditable)); } } private bool _isLINConfigDatagridActive = true; /// /// 配置程序 DataGrid 是否可操作(与 IsLinConfigProActive 取反)。 /// public bool IsLINConfigDatagridActive { get { return _isLINConfigDatagridActive; } set { _isLINConfigDatagridActive = value; RaisePropertyChanged(); } } /// /// 读写设置是否允许编辑。 /// 说明:与 语义绑定:激活后代表“配置已下发并进入运行态”,因此禁止编辑配置/切换配置程序。 /// public bool IsRwEditable { get { return !IsLinConfigProActive; } } private LINConfigExdDto? _SelectedLINConfigExdDto; /// /// 选中的 LIN 配置 DTO。 /// public LINConfigExdDto? SelectedLINConfigExdDto { get { return _SelectedLINConfigExdDto; } set { _SelectedLINConfigExdDto = value; RaisePropertyChanged(); // SelectedLINConfigExdDto 变更时,需要同步刷新其映射的“派生属性”(都是 UI 绑定入口)。 RaisePropertyChanged(nameof(CurrentLdfPath)); RaisePropertyChanged(nameof(CurrentSchEnable)); RaisePropertyChanged(nameof(LinBaudRate)); } } /// /// 当前 LDF 路径。 /// public string? CurrentLdfPath { get { return SelectedLINConfigExdDto?.LdfPath; } set { if (SelectedLINConfigExdDto == null) return; SelectedLINConfigExdDto.LdfPath = value; RaisePropertyChanged(); } } /// /// 循环周期(ms)。 /// public int CurrentCycle { get { return SelectedLINConfigExdDto?.Cycle ?? 0; } set { if (SelectedLINConfigExdDto == null) return; SelectedLINConfigExdDto.Cycle = value; RaisePropertyChanged(); } } /// /// 连接按钮文字。 /// public string ConnectButtonText { get { return "连接LIN"; } } /// /// 关闭按钮文字。 /// public string CloseButtonText { get { return "关闭LIN"; } } /// /// LIN 波特率。 /// public int LinBaudRate { get { return SelectedLINConfigExdDto?.BaudRate ?? 0; } set { if (SelectedLINConfigExdDto == null) return; SelectedLINConfigExdDto.BaudRate = value; RaisePropertyChanged(); } } /// /// 调度表使能。 /// public bool CurrentSchEnable { get { return SelectedLINConfigExdDto?.SchEnable ?? false; } set { if (SelectedLINConfigExdDto == null) return; SelectedLINConfigExdDto.SchEnable = value; RaisePropertyChanged(); } } private void InitLoadLinConfigPro() { // 从数据库加载 LIN 配置程序: // - 同时 include 扩展配置、读写项(含逻辑规则)、调度表 // - 供主界面 DataGrid 与右侧配置区域绑定 linConfigPros = FreeSql.Select() .Where(a => a.CANLINInfo == CANLIN.LIN) .Include(a => a.LINConfigExd) .IncludeMany(a => a.CanLinConfigContents, then => then.Include(b => b.LogicRule)) .IncludeMany(a => a.LinScheduleConfigs) .ToList(); ListCanLinConfigPro = new ObservableCollection(linConfigPros); } private void SyncSelectedConfig() { if (SelectCanLinConfigPro == null) return; // 实体 -> DTO: // - DTO 用于 UI 编辑(双向绑定); // - 保存时再由 VM 负责落库; // - 激活时则以 DTO/当前列表为准,下发到 Service(避免直接引用数据库实体导致并发/脏写)。 SelectedLINConfigExdDto = Mapper.Map(SelectCanLinConfigPro.LINConfigExd); var writeData = SelectCanLinConfigPro.CanLinConfigContents?.Where(a => a.RWInfo == RW.Write).ToList() ?? new List(); ListWriteCanLinRWConfigDto = new ObservableCollection(Mapper.Map>(writeData)); var readData = SelectCanLinConfigPro.CanLinConfigContents?.Where(a => a.RWInfo == RW.Read).ToList() ?? new List(); ListReadCanLinRWConfigDto = new ObservableCollection(Mapper.Map>(readData)); var schData = SelectCanLinConfigPro.LinScheduleConfigs?.ToList() ?? new List(); ListLINScheduleConfigDto = new ObservableCollection(Mapper.Map>(schData)); // 触发依赖属性刷新:IsRwEditable 与 CurrentCycle 取决于当前选择的配置与激活状态。 RaisePropertyChanged(nameof(IsRwEditable)); RaisePropertyChanged(nameof(CurrentCycle)); } private DelegateCommand? _LinConfigProGridSelectionChangedCmd; /// /// LIN 配置程序选中变化。 /// public DelegateCommand LinConfigProGridSelectionChangedCmd { get { if (_LinConfigProGridSelectionChangedCmd == null) { _LinConfigProGridSelectionChangedCmd = new DelegateCommand(LinConfigProGridSelectionChangedCmdMethod); } return _LinConfigProGridSelectionChangedCmd; } } private void LinConfigProGridSelectionChangedCmdMethod(object par) { if (par == null) return; if (par is SelectionChangedEventArgs) return; if (IsLinConfigProActive) { MessageBox.Show("当前配置程序已激活,请先取消激活后再切换", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (par is CanLinConfigPro) { SelectCanLinConfigPro = par as CanLinConfigPro; SyncSelectedConfig(); return; } var args = par as SelectionChangedEventArgs; if (args == null || args.AddedItems == null || args.AddedItems.Count == 0) return; var selected = args.AddedItems[0] as CanLinConfigPro; if (selected == null) return; SelectCanLinConfigPro = selected; SyncSelectedConfig(); } private DelegateCommand? _linConfigProCmd; /// /// 配置程序操作(新建/修改/删除/激活)。 /// public DelegateCommand LinConfigProCmd { get { if (_linConfigProCmd == null) { _linConfigProCmd = new DelegateCommand(LinConfigProCmdMethod); } return _linConfigProCmd; } } private void LinConfigProCmdMethod(string par) { if (string.IsNullOrWhiteSpace(par)) return; try { switch (par) { case "Add": if (IsLinConfigProActive) { MessageBox.Show("当前配置已激活,请先取消激活后再新建/修改配置程序", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (ZlgLinDriveService.OpenState) { MessageBox.Show("请先关闭 LIN 后再新建配置程序", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } DialogService.ShowDialog("DialogCanLinConfigCreateView", new DialogParameters() { { "Name", "" } }, r => { if (r.Result != ButtonResult.OK) return; var name = r.Parameters.GetValue("Name")?.Trim(); if (string.IsNullOrWhiteSpace(name)) { MessageBox.Show("名称不能为空", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } var exists = FreeSql.Select() .Where(a => a.CANLINInfo == CANLIN.LIN) .Where(a => a.ConfigName == name) .Any(); if (exists) { MessageBox.Show("名称已存在,请更换名称", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } long newProId = 0; FreeSql.Transaction(() => { var exdList = FreeSql.Insert(new LINConfigExd() { BaudRate = 19200, Cycle = 100, LdfPath = string.Empty, SchEnable = false, }).ExecuteInserted(); var exd = exdList?.FirstOrDefault(); if (exd == null) { throw new InvalidOperationException("创建 LIN 扩展配置失败"); } var proList = FreeSql.Insert(new CanLinConfigPro() { ConfigName = name, CANLINInfo = CANLIN.LIN, LINConfigExdId = exd.Id, }).ExecuteInserted(); var pro = proList?.FirstOrDefault(); if (pro == null) { throw new InvalidOperationException("创建 LIN 配置程序失败"); } newProId = pro.Id; }); InitLoadLinConfigPro(); SelectCanLinConfigPro = linConfigPros.Find(a => a.Id == newProId); SyncSelectedConfig(); }); break; case "Edit": if (SelectCanLinConfigPro == null) { MessageBox.Show("选中后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (IsLinConfigProActive) { MessageBox.Show("当前配置已激活,请先取消激活后再修改配置程序", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (ZlgLinDriveService.OpenState) { MessageBox.Show("请先关闭 LIN 后再修改配置程序", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } DialogService.ShowDialog("DialogCanLinConfigCreateView", new DialogParameters() { { "Name", SelectCanLinConfigPro.ConfigName } }, r => { if (r.Result != ButtonResult.OK) return; var name = r.Parameters.GetValue("Name")?.Trim(); if (string.IsNullOrWhiteSpace(name)) { MessageBox.Show("名称不能为空", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } var exists = FreeSql.Select() .Where(a => a.CANLINInfo == CANLIN.LIN) .Where(a => a.ConfigName == name) .Where(a => a.Id != SelectCanLinConfigPro.Id) .Any(); if (exists) { MessageBox.Show("名称已存在,请更换名称", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } FreeSql.Update() .Set(a => a.ConfigName, name) .Where(a => a.Id == SelectCanLinConfigPro.Id) .ExecuteAffrows(); ReloadCurrentConfigPro(); }); break; case "Delete": if (SelectCanLinConfigPro == null) { MessageBox.Show("选中后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (IsLinConfigProActive) { MessageBox.Show("当前配置已激活,请先取消激活后再删除配置程序", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (ZlgLinDriveService.OpenState) { MessageBox.Show("请先关闭 LIN 后再删除配置程序", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (MessageBox.Show($"确定删除配置程序:{SelectCanLinConfigPro.ConfigName}?", "确认", MessageBoxButton.OKCancel, MessageBoxImage.Warning) != MessageBoxResult.OK) { return; } var repo = FreeSql.GetRepository(); repo.DbContextOptions.EnableCascadeSave = true; var delList = repo.Select .Include(a => a.LINConfigExd) .IncludeMany(a => a.CanLinConfigContents) .IncludeMany(a => a.LinScheduleConfigs) .Where(a => a.Id == SelectCanLinConfigPro.Id) .ToList(); repo.Delete(delList); foreach (var item in delList) { if (item.LINConfigExdId > 0) { FreeSql.Delete(item.LINConfigExdId).ExecuteAffrows(); } } SelectCanLinConfigPro = null; SelectedLINConfigExdDto = null; InitLoadLinConfigPro(); ListLINScheduleConfigDto = new ObservableCollection(); break; case "Active": if (IsLinConfigProActive) { // 取消激活: // - 停止循环/调度发送 // - 关闭事件驱动发送开关 // - 清空 CmdData(避免残留订阅导致误发送) IsLinConfigProActive = false; IsLINConfigDatagridActive = true; ZlgLinDriveService.StopSchedule(); ZlgLinDriveService.IsCycleSend = false; ZlgLinDriveService.LoadCmdDataToDrive(new List()); return; } if (!ZlgLinDriveService.OpenState) { MessageBox.Show("请确保 LIN 已连接后再激活", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (SelectCanLinConfigPro == null || SelectedLINConfigExdDto == null) { MessageBox.Show("选中后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (string.IsNullOrWhiteSpace(CurrentLdfPath)) { MessageBox.Show("请先选择并解析 LDF 文件后再激活", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } // 激活前强制按“当前配置的 LDF 路径”重新解析,避免 LdfParserState 来自其它配置导致错配。 try { ZlgLinDriveService.StartLdf(CurrentLdfPath); } catch (Exception ex) { MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error); return; } IsLinConfigProActive = true; IsLINConfigDatagridActive = false; // 激活后: // - 将当前配置程序引用交给 Service(用于后续业务关联/显示) // - 构建并下发 CmdData // - 下发调度表配置快照(仅缓存,真正启动由 CycleSend/SchEnable 控制) ZlgLinDriveService.SelectedCanLinConfigPro = SelectCanLinConfigPro; BuildAndLoadCmdDataToDrive(); ZlgLinDriveService.SetScheduleConfigs(ListLINScheduleConfigDto?.ToList() ?? new List()); break; } } catch (Exception ex) { MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private DelegateCommand? _LoadLdfCmd; /// /// 选择 LDF 文件。 /// public DelegateCommand LoadLdfCmd { get { if (_LoadLdfCmd == null) { _LoadLdfCmd = new DelegateCommand(LoadLdfCmdMethod); } return _LoadLdfCmd; } } private void LoadLdfCmdMethod() { try { if (SelectCanLinConfigPro == null || SelectedLINConfigExdDto == null) { MessageBox.Show("选中LIN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } OpenFileDialog openFileDialogInfo = new OpenFileDialog(); openFileDialogInfo.Filter = "(*.ldf;*.ldf)|*.ldf;*.ldf|all|*.*"; openFileDialogInfo.CheckFileExists = true; openFileDialogInfo.CheckPathExists = true; openFileDialogInfo.ShowDialog(); CurrentLdfPath = openFileDialogInfo.FileName; } catch { MessageBox.Show("可能未选择信息", "提示", MessageBoxButton.OKCancel, MessageBoxImage.Hand); } } private DelegateCommand? _LinOpCmd; /// /// LIN 操作命令。 /// public DelegateCommand LinOpCmd { get { if (_LinOpCmd == null) { _LinOpCmd = new DelegateCommand(LinOpCmdMethod); } return _LinOpCmd; } } private void LinOpCmdMethod(string par) { switch (par) { case "Open": if (ComActionService.IsLINToDoWork() == false) { MessageBox.Show("请关闭CAN连接后才能开启LIN,同一个时刻只能有一个通信驱动压缩机", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (ZlgCanDriveService.OpenState) { MessageBox.Show("请先关闭 ZLG CAN 连接后再开启 ZLG LIN", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (SelectCanLinConfigPro == null || SelectedLINConfigExdDto == null) { MessageBox.Show("选中LIN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } // 打开 LIN(不依赖 LDF) ZlgLinDriveService.StartLinDrive(0, (uint)SelectedLINConfigExdDto.BaudRate, isMaster: true); if (ZlgLinDriveService.OpenState) { ConfigService.CanLinRunStateModel.CurSysSelectedCanLin = CanLinEnum.Lin; } // 打开后同步调度使能状态(避免 UI 未触发 Click 时服务端状态不一致) ZlgLinDriveService.SchEnable = CurrentSchEnable; RaisePropertyChanged(nameof(IsRwEditable)); break; case "Close": try { // 停止循环发送/事件驱动发送,避免关闭后仍持续发送 ZlgLinDriveService.StopSchedule(); ZlgLinDriveService.IsCycleSend = false; ZlgLinDriveService.LoadCmdDataToDrive(new List()); } catch { // ignore } ZlgLinDriveService.CloseDevice(); ConfigService.CanLinRunStateModel.CurSysSelectedCanLin = CanLinEnum.No; // 关闭设备时自动退出激活,恢复可切换状态 IsLinConfigProActive = false; IsLINConfigDatagridActive = true; RaisePropertyChanged(nameof(IsRwEditable)); break; case "Save": if (SelectCanLinConfigPro == null || SelectedLINConfigExdDto == null) { MessageBox.Show("选中LIN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } // 仅保存 LINConfigExd(LDF 路径/周期/波特率/SchEnable 等); // 读写项/调度表由各自弹窗或其它操作链路持久化。 FreeSql.Update() .Set(a => a.LdfPath, SelectedLINConfigExdDto.LdfPath) .Set(a => a.Cycle, SelectedLINConfigExdDto.Cycle) .Set(a => a.BaudRate, SelectedLINConfigExdDto.BaudRate) .Set(a => a.SchEnable, SelectedLINConfigExdDto.SchEnable) .Where(a => a.Id == SelectedLINConfigExdDto.Id) .ExecuteUpdated(); InitLoadLinConfigPro(); break; case "Parse": // 解析 LDF: // - 解析成功后会建立运行时索引(帧/信号位定义),用于后续编码发送与接收解码; // - 并同步构建 CmdData(用于后续调度/事件驱动)。 if (SelectCanLinConfigPro == null || SelectedLINConfigExdDto == null) { MessageBox.Show("选中LIN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } try { ZlgLinDriveService.StartLdf(SelectedLINConfigExdDto.LdfPath ?? string.Empty); // 解析完成后同步 CmdData(用于后续调度/事件驱动) BuildAndLoadCmdDataToDrive(); } catch (Exception ex) { MessageBox.Show(ex.Message, "提示", MessageBoxButton.OK, MessageBoxImage.Hand); } break; case "CycleSend": if (!ZlgLinDriveService.OpenState) { MessageBox.Show("请先打开 LIN 后再进行循环发送", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (!ZlgLinDriveService.LdfParserState) { MessageBox.Show("请先解析 LDF 后再进行循环发送", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } // 第一次点击开启,第二次点击关闭 if (!ZlgLinDriveService.IsCycleSend) { // 开启前:确保最新 CmdData 已下发 BuildAndLoadCmdDataToDrive(); // 同步 SchEnable ZlgLinDriveService.SchEnable = CurrentSchEnable; if (ZlgLinDriveService.SchEnable) { var groupMsg = ZlgLinDriveService.CmdData .Where(a => !string.IsNullOrWhiteSpace(a.MsgName)) .GroupBy(a => a.MsgName) .Select(g => g.Key) .ToList(); if (groupMsg.Count == 0) { MessageBox.Show("未发现可发送的消息帧(CmdData.MsgName 为空)", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } foreach (var msg in groupMsg) { if (!ListLINScheduleConfigDto.Any(a => string.Equals(a.MsgName, msg, StringComparison.Ordinal))) { MessageBox.Show($"你使能了调度表,但是调度表中没有设置【{msg}】信息,请设置后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } } if (ListLINScheduleConfigDto == null || ListLINScheduleConfigDto.Count == 0) { MessageBox.Show("调度表配置为空数据,请检查!将无法发送数据", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } // LIN 调度表:软件调度(每帧独立周期) ZlgLinDriveService.SetScheduleConfigs(ListLINScheduleConfigDto.ToList()); ZlgLinDriveService.StartSchedule(); } else { // 未启用调度表:按统一周期对 CmdData 中出现的帧做软件精确定时循环发送。 var cycle = Math.Max(1, CurrentCycle); ZlgLinDriveService.StartPrecisionCycleSend(cycle); } ZlgLinDriveService.IsCycleSend = true; } else { ZlgLinDriveService.IsCycleSend = false; ZlgLinDriveService.StopSchedule(); } break; case "CycleRecive": ZlgLinDriveService.SetReceiveEnabled(!ZlgLinDriveService.IsCycleRevice); break; } } /// /// 从当前配置程序的写入配置构建并下发 LinCmdData。 /// private void BuildAndLoadCmdDataToDrive() { try { if (SelectCanLinConfigPro?.CanLinConfigContents == null) { ZlgLinDriveService.LoadCmdDataToDrive(new List()); return; } var writeItems = SelectCanLinConfigPro.CanLinConfigContents .Where(a => a.RWInfo == RW.Write) .ToList(); var cmdList = new List(); foreach (var item in writeItems) { cmdList.Add(new LinCmdData() { ConfigName = item.Name, MsgName = item.MsgFrameName, SignalName = item.SignalName, SignalCmdValue = double.TryParse(item.DefautValue, out double result) ? result : 0, LogicRuleDto = Mapper.Map(item.LogicRule), }); } ZlgLinDriveService.LoadCmdDataToDrive(cmdList); } catch (Exception ex) { MessageBox.Show($"构建/下发 LIN CmdData 失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private DelegateCommand? _openRwDialogCmd; /// /// 打开“读写设置”弹窗。 /// 说明: /// - 弹窗内允许编辑写入/读取项,并可从 LDF 解析得到的信号池中选择; /// - 弹窗确认后通过 重新从数据库加载并刷新 UI。 /// public DelegateCommand OpenRwDialogCmd { get { if (_openRwDialogCmd == null) { _openRwDialogCmd = new DelegateCommand(OpenRwDialogCmdMethod); } return _openRwDialogCmd; } } private void OpenRwDialogCmdMethod() { try { if (SelectCanLinConfigPro == null) { MessageBox.Show("选中LIN配置名称后再操作", "提示", 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 (ZlgLinDriveService.ListLinLdfModel != null) { // 信号候选池:由 LDF 解析结果生成(帧名+信号名)。 foreach (var sig in ZlgLinDriveService.ListLinLdfModel) { 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 }, { "EnableHardwareCycleSchedule", false }, { "WriteConfigs", writeClones }, { "ReadConfigs", readClones }, { "SignalCandidates", candidates }, }; DialogService.ShowDialog(nameof(DialogZlgCanLinRwConfigView), pars, r => { if (r.Result == ButtonResult.OK) { // 弹窗内部会持久化保存;这里仅负责重新加载并刷新当前 UI 绑定。 ReloadCurrentConfigPro(); } }); } catch (Exception ex) { MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } private void ReloadCurrentConfigPro() { var id = SelectCanLinConfigPro?.Id; InitLoadLinConfigPro(); if (id != null) { SelectCanLinConfigPro = linConfigPros.Find(a => a.Id == id.Value); SyncSelectedConfig(); } } /// /// 克隆读写配置 DTO。 /// 说明:打开弹窗前先克隆一份,避免用户在弹窗中编辑时直接影响主界面集合(取消时也不会污染当前 UI 状态)。 /// /// 源 DTO。 /// 克隆后的 DTO。 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, }; } private DelegateCommand? _SchEnableCmd; /// /// 调度表使能写入驱动。 /// public DelegateCommand SchEnableCmd { get { if (_SchEnableCmd == null) { _SchEnableCmd = new DelegateCommand(SchEnableCmdCall); } return _SchEnableCmd; } } private void SchEnableCmdCall(object par) { bool enable; if (par is bool b) { enable = b; } else { enable = CurrentSchEnable; } // 立即同步到 Service: // - CurrentSchEnable 只是 DTO 字段,改变它并不等于驱动侧状态已更新; // - 这里的命令用于“用户点击开关时”立刻让 Service/Driver 生效。 ZlgLinDriveService.SchEnable = enable; } private DelegateCommand? _scheduleConfigCmd; /// /// 调度表配置(弹窗)。 /// public DelegateCommand ScheduleConfigCmd { get { if (_scheduleConfigCmd == null) { _scheduleConfigCmd = new DelegateCommand(ScheduleConfigCmdMethod); } return _scheduleConfigCmd; } } private void ScheduleConfigCmdMethod() { try { if (SelectCanLinConfigPro == null) { MessageBox.Show("选中LIN配置名称后再操作", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } if (IsLinConfigProActive) { MessageBox.Show("当前配置已激活,请先取消激活后再配置调度表", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } // 确保 CmdData 最新(用于候选消息帧) BuildAndLoadCmdDataToDrive(); // 调度表弹窗的 MsgName 候选列表:来自当前 CmdData 聚合。 // 这样能保证“可配置的调度表项”与“实际会被编码发送的帧”一致。 var msgList = ZlgLinDriveService.CmdData .GroupBy(a => a.MsgName) .Select(g => g.Key) .Where(a => !string.IsNullOrWhiteSpace(a)) .ToList(); if ((msgList == null || msgList.Count == 0) && (ListLINScheduleConfigDto == null || ListLINScheduleConfigDto.Count == 0)) { MessageBox.Show("未发现写入指令数据(CmdData 为空),无法配置调度表", "提示", MessageBoxButton.OK, MessageBoxImage.Hand); return; } DialogService.ShowDialog(nameof(DialogLINSchConfigView), new DialogParameters() { { "ListMsg", msgList }, { "ListLINScheduleConfigDto", ListLINScheduleConfigDto }, { "SelectCanLinConfigProId", SelectCanLinConfigPro.Id }, }, r => { if (r.Result != ButtonResult.OK) return; ListLINScheduleConfigDto = r.Parameters.GetValue>("ReturnValue") ?? new ObservableCollection(); // DTO -> 实体:用于持久化到数据库;后续激活/循环发送前会再次由 SyncSelectedConfig/BuildAndLoadCmdDataToDrive 同步到 Service。 SelectCanLinConfigPro.LinScheduleConfigs = Mapper.Map>(ListLINScheduleConfigDto.ToList()); ReloadCurrentConfigPro(); }); } catch (Exception ex) { MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error); } } /// /// 根据 UI 当前 SchEnable/Cycle 状态启动或停止发送。 /// private void ApplyScheduleByUiState(bool schEnable) { if (!ZlgLinDriveService.OpenState) { return; } if (!schEnable) { ZlgLinDriveService.StopSchedule(); ZlgLinDriveService.IsCycleSend = false; return; } // 开启前:确保 LDF 已解析(否则无法编码) if (!ZlgLinDriveService.LdfParserState) { if (SelectedLINConfigExdDto == null) { throw new InvalidOperationException("未选择 LIN 配置,无法启用调度。"); } ZlgLinDriveService.StartLdf(SelectedLINConfigExdDto.LdfPath ?? string.Empty); } // 下发 CmdData BuildAndLoadCmdDataToDrive(); // 打开事件驱动发送(用于“值变化增量发送”) ZlgLinDriveService.IsCycleSend = true; // 启动软件精确定时循环发送(作为当前 ZLG LIN 的“调度表”能力) var cycle = SelectedLINConfigExdDto?.Cycle ?? 0; if (cycle <= 0) { cycle = 100; } ZlgLinDriveService.StartPrecisionCycleSend(cycle); } private void ApplyScheduleByUiState() { ApplyScheduleByUiState(CurrentSchEnable); } } }