Files
CapMachine/CapMachine.Wpf/ViewModels/ZlgLinDriveConfigViewModel.cs
2026-03-02 11:20:08 +08:00

1161 lines
47 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
/// <summary>
/// ZLG LIN 配置 ViewModel。
/// </summary>
public class ZlgLinDriveConfigViewModel : NavigationViewModel
{
/// <summary>
/// 构造函数。
/// </summary>
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();
}
/// <summary>
/// Prism 弹窗服务。
/// </summary>
public IDialogService DialogService { get; }
/// <summary>
/// FreeSql用于 LIN 配置程序/读写项/调度表 的查询与持久化)。
/// </summary>
public IFreeSql FreeSql { get; }
/// <summary>
/// 事件聚合器。
/// </summary>
public IEventAggregator EventAggregator { get; }
/// <summary>
/// 区域管理器。
/// </summary>
public IRegionManager RegionManager { get; }
/// <summary>
/// 系统运行状态服务。
/// </summary>
public SysRunService SysRunService { get; }
/// <summary>
/// 全局配置服务(用于记录当前系统选择的通信类型等状态)。
/// </summary>
public ConfigService ConfigService { get; }
/// <summary>
/// ZLG LIN 服务LIN 生命周期、LDF 解析、软件调度、收发指示灯等)。
/// </summary>
public ZlgLinDriveService ZlgLinDriveService { get; }
/// <summary>
/// ZLG CAN 服务。
/// 说明LIN 与 CAN 共用同一硬件/设备句柄,打开 LIN 前需要确保 CAN 已关闭。
/// </summary>
public ZlgCanDriveService ZlgCanDriveService { get; }
/// <summary>
/// 通信驱动互斥控制服务(同一时刻只能有一种驱动操作压缩机)。
/// </summary>
public ComActionService ComActionService { get; }
/// <summary>
/// 逻辑规则服务。
/// </summary>
public LogicRuleService LogicRuleService { get; }
/// <summary>
/// AutoMapper。
/// </summary>
public IMapper Mapper { get; }
private List<CanLinConfigPro> linConfigPros = new List<CanLinConfigPro>();
private ObservableCollection<CanLinConfigPro>? _ListCanLinConfigPro;
/// <summary>
/// LIN 配置程序集合。
/// </summary>
public ObservableCollection<CanLinConfigPro>? ListCanLinConfigPro
{
get { return _ListCanLinConfigPro; }
set { _ListCanLinConfigPro = value; RaisePropertyChanged(); }
}
/// <summary>
/// 选中的配置程序。
/// </summary>
public CanLinConfigPro? SelectCanLinConfigPro { get; set; }
private ObservableCollection<CanLinRWConfigDto> _listWriteCanLinRWConfigDto = new ObservableCollection<CanLinRWConfigDto>();
/// <summary>
/// 写入配置 DTO 集合UI 展示/编辑用)。
/// </summary>
public ObservableCollection<CanLinRWConfigDto> ListWriteCanLinRWConfigDto
{
get { return _listWriteCanLinRWConfigDto; }
set { _listWriteCanLinRWConfigDto = value; RaisePropertyChanged(); }
}
private ObservableCollection<CanLinRWConfigDto> _listReadCanLinRWConfigDto = new ObservableCollection<CanLinRWConfigDto>();
/// <summary>
/// 读取配置 DTO 集合UI 展示/编辑用)。
/// </summary>
public ObservableCollection<CanLinRWConfigDto> ListReadCanLinRWConfigDto
{
get { return _listReadCanLinRWConfigDto; }
set { _listReadCanLinRWConfigDto = value; RaisePropertyChanged(); }
}
private ObservableCollection<LINScheduleConfigDto> _listLINScheduleConfigDto = new ObservableCollection<LINScheduleConfigDto>();
/// <summary>
/// LIN 调度表集合(系统层)。
/// </summary>
public ObservableCollection<LINScheduleConfigDto> ListLINScheduleConfigDto
{
get { return _listLINScheduleConfigDto; }
set { _listLINScheduleConfigDto = value; RaisePropertyChanged(); }
}
private bool _isLinConfigProActive;
/// <summary>
/// 当前配置程序是否已激活(对齐 CAN Active 语义)。激活后禁止切换配置程序。
/// </summary>
public bool IsLinConfigProActive
{
get { return _isLinConfigProActive; }
set
{
_isLinConfigProActive = value;
RaisePropertyChanged();
RaisePropertyChanged(nameof(IsRwEditable));
}
}
private bool _isLINConfigDatagridActive = true;
/// <summary>
/// 配置程序 DataGrid 是否可操作(与 IsLinConfigProActive 取反)。
/// </summary>
public bool IsLINConfigDatagridActive
{
get { return _isLINConfigDatagridActive; }
set { _isLINConfigDatagridActive = value; RaisePropertyChanged(); }
}
/// <summary>
/// 读写设置是否允许编辑。
/// 说明:与 <see cref="IsLinConfigProActive"/> 语义绑定:激活后代表“配置已下发并进入运行态”,因此禁止编辑配置/切换配置程序。
/// </summary>
public bool IsRwEditable
{
get { return !IsLinConfigProActive; }
}
private LINConfigExdDto? _SelectedLINConfigExdDto;
/// <summary>
/// 选中的 LIN 配置 DTO。
/// </summary>
public LINConfigExdDto? SelectedLINConfigExdDto
{
get { return _SelectedLINConfigExdDto; }
set
{
_SelectedLINConfigExdDto = value;
RaisePropertyChanged();
// SelectedLINConfigExdDto 变更时,需要同步刷新其映射的“派生属性”(都是 UI 绑定入口)。
RaisePropertyChanged(nameof(CurrentLdfPath));
RaisePropertyChanged(nameof(CurrentSchEnable));
RaisePropertyChanged(nameof(LinBaudRate));
}
}
/// <summary>
/// 当前 LDF 路径。
/// </summary>
public string? CurrentLdfPath
{
get { return SelectedLINConfigExdDto?.LdfPath; }
set
{
if (SelectedLINConfigExdDto == null) return;
SelectedLINConfigExdDto.LdfPath = value;
RaisePropertyChanged();
}
}
/// <summary>
/// 循环周期ms
/// </summary>
public int CurrentCycle
{
get { return SelectedLINConfigExdDto?.Cycle ?? 0; }
set
{
if (SelectedLINConfigExdDto == null) return;
SelectedLINConfigExdDto.Cycle = value;
RaisePropertyChanged();
}
}
/// <summary>
/// 连接按钮文字。
/// </summary>
public string ConnectButtonText
{
get { return "连接LIN"; }
}
/// <summary>
/// 关闭按钮文字。
/// </summary>
public string CloseButtonText
{
get { return "关闭LIN"; }
}
/// <summary>
/// LIN 波特率。
/// </summary>
public int LinBaudRate
{
get { return SelectedLINConfigExdDto?.BaudRate ?? 0; }
set
{
if (SelectedLINConfigExdDto == null) return;
SelectedLINConfigExdDto.BaudRate = value;
RaisePropertyChanged();
}
}
/// <summary>
/// 调度表使能。
/// </summary>
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<CanLinConfigPro>()
.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<CanLinConfigPro>(linConfigPros);
}
private void SyncSelectedConfig()
{
if (SelectCanLinConfigPro == null) return;
// 实体 -> DTO
// - DTO 用于 UI 编辑(双向绑定);
// - 保存时再由 VM 负责落库;
// - 激活时则以 DTO/当前列表为准,下发到 Service避免直接引用数据库实体导致并发/脏写)。
SelectedLINConfigExdDto = Mapper.Map<LINConfigExdDto>(SelectCanLinConfigPro.LINConfigExd);
var writeData = SelectCanLinConfigPro.CanLinConfigContents?.Where(a => a.RWInfo == RW.Write).ToList() ?? new List<CanLinRWConfig>();
ListWriteCanLinRWConfigDto = new ObservableCollection<CanLinRWConfigDto>(Mapper.Map<List<CanLinRWConfigDto>>(writeData));
var readData = SelectCanLinConfigPro.CanLinConfigContents?.Where(a => a.RWInfo == RW.Read).ToList() ?? new List<CanLinRWConfig>();
ListReadCanLinRWConfigDto = new ObservableCollection<CanLinRWConfigDto>(Mapper.Map<List<CanLinRWConfigDto>>(readData));
var schData = SelectCanLinConfigPro.LinScheduleConfigs?.ToList() ?? new List<LINScheduleConfig>();
ListLINScheduleConfigDto = new ObservableCollection<LINScheduleConfigDto>(Mapper.Map<List<LINScheduleConfigDto>>(schData));
// 触发依赖属性刷新IsRwEditable 与 CurrentCycle 取决于当前选择的配置与激活状态。
RaisePropertyChanged(nameof(IsRwEditable));
RaisePropertyChanged(nameof(CurrentCycle));
}
private DelegateCommand<object>? _LinConfigProGridSelectionChangedCmd;
/// <summary>
/// LIN 配置程序选中变化。
/// </summary>
public DelegateCommand<object> LinConfigProGridSelectionChangedCmd
{
get
{
if (_LinConfigProGridSelectionChangedCmd == null)
{
_LinConfigProGridSelectionChangedCmd = new DelegateCommand<object>(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<string>? _linConfigProCmd;
/// <summary>
/// 配置程序操作(新建/修改/删除/激活)。
/// </summary>
public DelegateCommand<string> LinConfigProCmd
{
get
{
if (_linConfigProCmd == null)
{
_linConfigProCmd = new DelegateCommand<string>(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<string>("Name")?.Trim();
if (string.IsNullOrWhiteSpace(name))
{
MessageBox.Show("名称不能为空", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
var exists = FreeSql.Select<CanLinConfigPro>()
.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<LINConfigExd>(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<CanLinConfigPro>(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<string>("Name")?.Trim();
if (string.IsNullOrWhiteSpace(name))
{
MessageBox.Show("名称不能为空", "提示", MessageBoxButton.OK, MessageBoxImage.Hand);
return;
}
var exists = FreeSql.Select<CanLinConfigPro>()
.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<CanLinConfigPro>()
.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<CanLinConfigPro>();
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<LINConfigExd>(item.LINConfigExdId).ExecuteAffrows();
}
}
SelectCanLinConfigPro = null;
SelectedLINConfigExdDto = null;
InitLoadLinConfigPro();
ListLINScheduleConfigDto = new ObservableCollection<LINScheduleConfigDto>();
break;
case "Active":
if (IsLinConfigProActive)
{
// 取消激活:
// - 停止循环/调度发送
// - 关闭事件驱动发送开关
// - 清空 CmdData避免残留订阅导致误发送
IsLinConfigProActive = false;
IsLINConfigDatagridActive = true;
ZlgLinDriveService.StopSchedule();
ZlgLinDriveService.IsCycleSend = false;
ZlgLinDriveService.LoadCmdDataToDrive(new List<LinCmdData>());
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<LINScheduleConfigDto>());
break;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private DelegateCommand? _LoadLdfCmd;
/// <summary>
/// 选择 LDF 文件。
/// </summary>
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<string>? _LinOpCmd;
/// <summary>
/// LIN 操作命令。
/// </summary>
public DelegateCommand<string> LinOpCmd
{
get
{
if (_LinOpCmd == null)
{
_LinOpCmd = new DelegateCommand<string>(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<LinCmdData>());
}
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;
}
// 仅保存 LINConfigExdLDF 路径/周期/波特率/SchEnable 等);
// 读写项/调度表由各自弹窗或其它操作链路持久化。
FreeSql.Update<LINConfigExd>()
.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;
}
}
/// <summary>
/// 从当前配置程序的写入配置构建并下发 LinCmdData。
/// </summary>
private void BuildAndLoadCmdDataToDrive()
{
try
{
if (SelectCanLinConfigPro?.CanLinConfigContents == null)
{
ZlgLinDriveService.LoadCmdDataToDrive(new List<LinCmdData>());
return;
}
var writeItems = SelectCanLinConfigPro.CanLinConfigContents
.Where(a => a.RWInfo == RW.Write)
.ToList();
var cmdList = new List<LinCmdData>();
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<LogicRuleDto>(item.LogicRule),
});
}
ZlgLinDriveService.LoadCmdDataToDrive(cmdList);
}
catch (Exception ex)
{
MessageBox.Show($"构建/下发 LIN CmdData 失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private DelegateCommand? _openRwDialogCmd;
/// <summary>
/// 打开“读写设置”弹窗。
/// 说明:
/// - 弹窗内允许编辑写入/读取项,并可从 LDF 解析得到的信号池中选择;
/// - 弹窗确认后通过 <see cref="ReloadCurrentConfigPro"/> 重新从数据库加载并刷新 UI。
/// </summary>
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<CanLinRWConfigDto>(
(ListWriteCanLinRWConfigDto ?? new ObservableCollection<CanLinRWConfigDto>())
.Select(CloneRwDto));
foreach (var item in writeClones)
{
item.RWInfo = RW.Write;
}
var readClones = new ObservableCollection<CanLinRWConfigDto>(
(ListReadCanLinRWConfigDto ?? new ObservableCollection<CanLinRWConfigDto>())
.Select(CloneRwDto));
foreach (var item in readClones)
{
item.RWInfo = RW.Read;
}
var candidates = new ObservableCollection<DialogZlgCanLinRwConfigViewModel.SignalCandidate>();
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();
}
}
/// <summary>
/// 克隆读写配置 DTO。
/// 说明:打开弹窗前先克隆一份,避免用户在弹窗中编辑时直接影响主界面集合(取消时也不会污染当前 UI 状态)。
/// </summary>
/// <param name="src">源 DTO。</param>
/// <returns>克隆后的 DTO。</returns>
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<object>? _SchEnableCmd;
/// <summary>
/// 调度表使能写入驱动。
/// </summary>
public DelegateCommand<object> SchEnableCmd
{
get
{
if (_SchEnableCmd == null)
{
_SchEnableCmd = new DelegateCommand<object>(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;
/// <summary>
/// 调度表配置(弹窗)。
/// </summary>
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<ObservableCollection<LINScheduleConfigDto>>("ReturnValue") ?? new ObservableCollection<LINScheduleConfigDto>();
// DTO -> 实体:用于持久化到数据库;后续激活/循环发送前会再次由 SyncSelectedConfig/BuildAndLoadCmdDataToDrive 同步到 Service。
SelectCanLinConfigPro.LinScheduleConfigs = Mapper.Map<List<LINScheduleConfig>>(ListLINScheduleConfigDto.ToList());
ReloadCurrentConfigPro();
});
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 根据 UI 当前 SchEnable/Cycle 状态启动或停止发送。
/// </summary>
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);
}
}
}