using Basler.Pylon; using Prism.Commands; using Prism.Events; using Prism.Ioc; using Prism.Mvvm; using Prism.Regions; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using TeamAAS_VP.Core; using TeamAAS_VP.Core.Lights; using TeamAAS_VP.Data; using TeamAAS_VP.Events; using TeamAAS_VP.Interfaces; using TeamAAS_VP.Models.Lights; using TeamAAS_VP.Resources.Languages; namespace TeamAAS_VP.ViewModels.DebugMod { public class LightManualViewModel : BindableBase, INavigationAware { IContainerProvider _container; IEventAggregator _eventAggregator; ILightManagerService _light_manager_service; // fallback ILightManagerService _lightManagerService; IConfigService _configService; ISystemDatabaseService _systemDatabaseService; #region 属性 private ObservableCollection _Channels = new ObservableCollection(); public ObservableCollection Channels { get => _Channels; set => SetProperty(ref _Channels, value); } #endregion #region 命令 private DelegateCommand _LoadedCommand; public DelegateCommand LoadedCommand => _LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand)); #endregion #region 事件 #endregion public LightManualViewModel(IContainerProvider container, IEventAggregator ea, ILightManagerService lightManagerService, IConfigService configService, ISystemDatabaseService systemDatabaseService) { _container = container; _eventAggregator = ea; _light_manager_service = lightManagerService; _lightManagerService = lightManagerService; _configService = configService; _systemDatabaseService = systemDatabaseService; //订阅权限登录事件 _eventAggregator.GetEvent().Subscribe(LoginChange); } #region 方法 void ExecuteLoadedCommand() { if (_systemDatabaseService.GetCurrentUser().userPart == Enums.UserPart.Operator) { IsAllowEdit = false; } else { IsAllowEdit = true; } } #endregion #region 继承 public void ConfirmNavigationRequest(NavigationContext navigationContext, Action continuationCallback) { continuationCallback(true); } public async void OnNavigatedTo(NavigationContext navigationContext) { // load channels from light manager service Channels.Clear(); try { var globals = _lightManagerService.GlobalChannels.ToList(); foreach (var kv in globals) { var item = new ChannelItem(kv.Key, (ILightChannel)kv.Value, _lightManagerService, _configService, _eventAggregator); Channels.Add(item); await item.InitializeAsync(); } } catch (Exception ex) { // ignore or notify } } public bool IsNavigationTarget(NavigationContext navigationContext) { return true; } public void OnNavigatedFrom(NavigationContext navigationContext) { // nothing } #endregion private bool _IsAllowEdit = false; public bool IsAllowEdit { get { return _IsAllowEdit; } set { SetProperty(ref _IsAllowEdit, value); } } private void LoginChange(Models.User user) { if (user.userPart == Enums.UserPart.Operator) { IsAllowEdit = false; } else { IsAllowEdit = true; } } public class ChannelItem : BindableBase { private readonly int _globalId; private readonly ILightChannel _channel; private readonly ILightManagerService _lightManagerService; private readonly IConfigService _configService; private readonly IEventAggregator _eventAggregator; public int GlobalId => _globalId; private string _Name; public string Name { get => _Name; set => SetProperty(ref _Name, value); } private int _PendingBrightness; public int PendingBrightness { get => _PendingBrightness; set => SetProperty(ref _PendingBrightness, value); } private int _CurrentBrightness; public int CurrentBrightness { get => _CurrentBrightness; set => SetProperty(ref _CurrentBrightness, value); } private bool _IsOn; public bool IsOn { get => _IsOn; set => SetProperty(ref _IsOn, value); } public DelegateCommand SaveCommand { get; } public DelegateCommand TurnOnCommand { get; } public DelegateCommand TurnOffCommand { get; } private DelegateCommand _BrightnessChangedCommand; public DelegateCommand BrightnessChangedCommand => _BrightnessChangedCommand ?? (_BrightnessChangedCommand = new DelegateCommand(ExecuteBrightnessChangedCommand)); // 去抖与写入控制字段 private readonly object _writeLock = new object(); private int? _latestQueuedBrightness = null; private bool _isWriting = false; private System.Threading.CancellationTokenSource _debounceCts; public ChannelItem(int globalId, ILightChannel channel, ILightManagerService lightManagerService, IConfigService configService, IEventAggregator eventAggregator) { _globalId = globalId; _channel = channel; _lightManagerService = lightManagerService; _configService = configService; _eventAggregator = eventAggregator; Name = channel?.ChannelName ?? $"Channel {_globalId}"; PendingBrightness = 0; CurrentBrightness = 0; IsOn = false; SaveCommand = new DelegateCommand(async () => await SaveAsync()); TurnOnCommand = new DelegateCommand(async () => await TurnOnAsync()); TurnOffCommand = new DelegateCommand(async () => await TurnOffAsync()); } public async Task InitializeAsync() { try { if (_channel != null) { var b = await _channel.GetBrightnessAsync(); CurrentBrightness = b; PendingBrightness = b; IsOn = await _channel.GetStatusAsync(); } } catch { } } // 当滑块变化时调用:采用去抖 + 只写最新值的策略 async void ExecuteBrightnessChangedCommand(object obj) { try { var parameter = obj as Tuple; if (parameter == null) return; int newValue = (int)parameter.Item2; // 更新 PendingBrightness 可即时反映到 UI(根据需要) PendingBrightness = newValue; QueueBrightnessChange(newValue); } catch (Exception) { // ignore } } // 将新的亮度值入队(覆盖之前未写入的值),并启动去抖延迟任务 private void QueueBrightnessChange(int brightness) { lock (_writeLock) { _latestQueuedBrightness = brightness; // 取消之前的去抖任务 try { _debounceCts?.Cancel(); _debounceCts?.Dispose(); } catch { } _debounceCts = new System.Threading.CancellationTokenSource(); var token = _debounceCts.Token; // 启动去抖后处理(不等待) System.Threading.Tasks.Task.Run(async () => { try { // 去抖延迟,可调整为 150-300ms await System.Threading.Tasks.Task.Delay(200, token); await ProcessWritesAsync(); } catch (System.OperationCanceledException) { // 被取消,忽略 } catch { // 忽略其它异常防止未观测异常 } }, System.Threading.CancellationToken.None); } } // 串行化写入循环:写入当前最新值,若在写入过程中又有新值则继续写入,直到没有新值 private async System.Threading.Tasks.Task ProcessWritesAsync() { // 快速检查与设置写入标志,保证只有一个写入循环执行 lock (_writeLock) { if (_isWriting) return; _isWriting = true; } try { while (true) { int? toWrite = null; lock (_writeLock) { if (_latestQueuedBrightness.HasValue) { toWrite = _latestQueuedBrightness.Value; _latestQueuedBrightness = null; } } if (!toWrite.HasValue) { // 没有需要写入的值,退出循环 break; } try { // 实际写入(底层方法可能不支持取消) var success = await _lightManagerService.SetGlobalChannelBrightnessAsync(_globalId, toWrite.Value); if (success) { CurrentBrightness = toWrite.Value; PendingBrightness = toWrite.Value; IsOn = toWrite.Value > 0; } } catch { // 忽略写入异常,继续检查是否有新值需要写入 } } } finally { lock (_writeLock) { _isWriting = false; } } } private async Task SaveAsync() { try { var success = await _lightManagerService.SetGlobalChannelBrightnessAsync(_globalId, PendingBrightness); if (success) { CurrentBrightness = PendingBrightness; IsOn = PendingBrightness > 0; // also save as default in config _configService.UpdateChannelDefaultBrightness(_globalId, PendingBrightness); _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = string.Format(Lang.保存完成01, Name, PendingBrightness), Duration = 1 }); } else { _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = string.Format(Lang.保存失败0, Name), Duration = 1 }); } } catch (Exception ex) { _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = string.Format(Lang.保存异常0, ex.Message), Duration = 1 }); } } private async Task TurnOnAsync() { try { var success = await _lightManagerService.TurnOnGlobalChannelAsync(_globalId); if (success) { IsOn = true; // refresh brightness var b = await _channel.GetBrightnessAsync(); PendingBrightness = b; CurrentBrightness = b; _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = string.Format(Lang.打开通道0, Name), Duration = 1 }); } } catch (Exception ex) { _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = string.Format(Lang.打开异常0,ex.Message), Duration = 1 }); } } private async Task TurnOffAsync() { try { var success = await _lightManagerService.TurnOffGlobalChannelAsync(_globalId); if (success) { IsOn = false; PendingBrightness = 0; CurrentBrightness = 0; _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = string.Format(Lang.关闭通道0,Name), Duration = 1 }); } } catch (Exception ex) { _eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = string.Format(Lang.关闭异常0, ex.Message), Duration = 1 }); } } } } }