| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- 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<ChannelItem> _Channels = new ObservableCollection<ChannelItem>();
- public ObservableCollection<ChannelItem> 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<UserLoginNotification>().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<bool> 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<object> _BrightnessChangedCommand;
- public DelegateCommand<object> BrightnessChangedCommand =>
- _BrightnessChangedCommand ?? (_BrightnessChangedCommand = new DelegateCommand<object>(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<object, double, double>;
- 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<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.保存完成}: {Name} -> {PendingBrightness}", Duration = 1 });
- }
- else
- {
- _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.保存失败}: {Name}", Duration = 1 });
- }
- }
- catch (Exception ex)
- {
- _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.保存异常}: {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<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.打开通道}: {Name}", Duration = 1 });
- }
- }
- catch (Exception ex)
- {
- _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.打开异常}: {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<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.关闭通道}: {Name}", Duration = 1 });
- }
- }
- catch (Exception ex)
- {
- _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.关闭异常}: {ex.Message}", Duration = 1 });
- }
- }
- }
- }
- }
|