1173 lines
47 KiB
C#
1173 lines
47 KiB
C#
using AutoMapper;
|
||
using CapMachine.Core;
|
||
using CapMachine.Model.CANLIN;
|
||
using CapMachine.Wpf.Dtos;
|
||
using CapMachine.Wpf.LinDrive;
|
||
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.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;
|
||
|
||
EventAggregator.GetEvent<CanLinConfigChangedEvent>().Subscribe(CanLinConfigChangedEventCall);
|
||
|
||
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;
|
||
}
|
||
|
||
// 仅保存 LINConfigExd(LDF 路径/周期/波特率/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>
|
||
/// CAN/LIN 配置更改事件(导入后刷新数据)。
|
||
/// </summary>
|
||
/// <param name="msg"></param>
|
||
private void CanLinConfigChangedEventCall(string msg)
|
||
{
|
||
InitLoadLinConfigPro();
|
||
}
|
||
|
||
/// <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);
|
||
}
|
||
}
|
||
}
|