浏览代码

亮度滑动时去抖

孝锋 徐 8 月之前
父节点
当前提交
c175fcb7a2
共有 1 个文件被更改,包括 131 次插入16 次删除
  1. 131 16
      TeamAAS-VM/ViewModels/DebugMod/LightManualViewModel.cs

+ 131 - 16
TeamAAS-VM/ViewModels/DebugMod/LightManualViewModel.cs

@@ -130,7 +130,30 @@ namespace TeamAAS_VP.ViewModels.DebugMod
             }
         }
 
-        // Nested ChannelItem class used as item VM
+        // 计划(伪代码,详细步骤):
+        // 1. 在 ChannelItem 内增加用于去抖和写入序列化的字段:
+        //    - 一个对象锁 _writeLock 用于保护对共享字段的访问
+        //    - 一个可空整型 _latestQueuedBrightness 存储最新的待写入值
+        //    - 一个布尔 _isWriting 标识是否正在执行写入操作
+        //    - 一个 CancellationTokenSource _debounceCts 用于取消上一次的去抖任务
+        // 2. 在滑动条变化时(ExecuteBrightnessChangedCommand):
+        //    - 解析参数得到新的亮度值
+        //    - 将新的亮度写入 _latestQueuedBrightness(覆盖之前未写入的值)
+        //    - 取消并替换之前的去抖 token,然后启动一个延迟任务(例如 200ms)
+        //      延迟任务被触发后会调用 ProcessWritesAsync 来执行实际写入
+        // 3. 在 ProcessWritesAsync 中:
+        //    - 使用锁检查并设置 _isWriting,保证同一时刻只有一个写入循环在跑
+        //    - 进入循环:从 _latestQueuedBrightness 读取值并清空该字段,调用写入接口写入该值
+        //    - 写入完成后再次检查 _latestQueuedBrightness 是否已有新值;如果有则继续写入最新值,否则退出并将 _isWriting 置 false
+        // 4. 这样可以:
+        //    - 去抖:避免用户快速滑动时频繁立刻触发写入
+        //    - 合并中间值:只写入最新的有效值
+        //    - 串行化写入:避免并发写入接口导致冲突或重复
+        //
+        // 实现注意:
+        // - 不假定底层 SetGlobalChannelBrightnessAsync 支持取消 token(因此不尝试中止正在进行的写入)
+        // - 去抖超时选择 150-300ms 可以根据 UX 需求调整
+
         public class ChannelItem : BindableBase
         {
             private readonly int _globalId;
@@ -161,6 +184,12 @@ namespace TeamAAS_VP.ViewModels.DebugMod
             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;
@@ -195,30 +224,116 @@ namespace TeamAAS_VP.ViewModels.DebugMod
                 }
             }
 
+            // 当滑块变化时调用:采用去抖 + 只写最新值的策略
             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;
 
-                    var success = await _lightManagerService.SetGlobalChannelBrightnessAsync(_globalId, (int)parameter.Item2);
-                    //if (success)
-                    //{
-                    //    CurrentBrightness = PendingBrightness;
-                    //    IsOn = PendingBrightness > 0;
-                    //    // also save as default in config
-                    //    _configService.UpdateChannelDefaultBrightness(_globalId, PendingBrightness);
-                    //    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"保存完成: {Name} -> {PendingBrightness}", Duration = 1 });
-                    //}
-                    //else
-                    //{
-                    //    _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"保存失败: {Name}", Duration = 1 });
-                    //}
+                    QueueBrightnessChange(newValue);
                 }
                 catch (Exception)
                 {
-                    //_eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"保存异常: {ex.Message}", Duration = 1 });
+                    // 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;
+                    }
                 }
             }