Pārlūkot izejas kodu

新增流程光源通道配置及亮度调节功能

为视觉流程引入光源通道配置,支持在流程参数和相机参数界面增删通道、调节亮度和开关光源。亮度调节采用Channel机制去抖并串行化写入,提升设备控制的稳定性。同步更新多语言资源及相关依赖包。
孝锋 徐 8 mēneši atpakaļ
vecāks
revīzija
459b74e00b

+ 4 - 0
TeamAAS-VM/App.config

@@ -303,6 +303,10 @@
         <assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" />
         <assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" />
         <bindingRedirect oldVersion="0.0.0.0-1.0.118.0" newVersion="1.0.118.0" />
         <bindingRedirect oldVersion="0.0.0.0-1.0.118.0" newVersion="1.0.118.0" />
       </dependentAssembly>
       </dependentAssembly>
+      <dependentAssembly>
+        <assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-10.0.0.1" newVersion="10.0.0.1" />
+      </dependentAssembly>
     </assemblyBinding>
     </assemblyBinding>
   </runtime>
   </runtime>
   <entityFramework>
   <entityFramework>

+ 21 - 0
TeamAAS-VM/Models/Product/ProcedureModel.cs

@@ -10,6 +10,7 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Models.Feeder;
 using TeamAAS_VP.Models.Feeder;
+using TeamAAS_VP.Models.Lights;
 using TeamAAS_VP.Models.Product;
 using TeamAAS_VP.Models.Product;
 
 
 namespace TeamAAS_VP.Models
 namespace TeamAAS_VP.Models
@@ -313,6 +314,16 @@ namespace TeamAAS_VP.Models
             set { SetProperty(ref _UserDefineParameters, value); }
             set { SetProperty(ref _UserDefineParameters, value); }
         }
         }
 
 
+        private ObservableCollection<ChannelConfig> _LightChannels;
+        /// <summary>
+        /// 光源通道配置集合。每个流程可配置多个光源通道及其默认亮度。
+        /// </summary>
+        public ObservableCollection<ChannelConfig> LightChannels
+        {
+            get { return _LightChannels; }
+            set { SetProperty(ref _LightChannels, value); }
+        }
+
         #region 运行时的一些临时记忆
         #region 运行时的一些临时记忆
 
 
         /// <summary>
         /// <summary>
@@ -338,6 +349,7 @@ namespace TeamAAS_VP.Models
             UserDefineParameters= new ObservableCollection<ProcedureUserDefineParam>();
             UserDefineParameters= new ObservableCollection<ProcedureUserDefineParam>();
             FeederWorks = new FeederWorks();
             FeederWorks = new FeederWorks();
             FeederStockWorks= new FeederStockWorks();
             FeederStockWorks= new FeederStockWorks();
+            LightChannels = new ObservableCollection<ChannelConfig>();
         }
         }
 
 
         public ProcedureModel(string name, string cameraName, Guid cameraId)
         public ProcedureModel(string name, string cameraName, Guid cameraId)
@@ -352,6 +364,7 @@ namespace TeamAAS_VP.Models
             UserDefineParameters = new ObservableCollection<ProcedureUserDefineParam>();
             UserDefineParameters = new ObservableCollection<ProcedureUserDefineParam>();
             FeederWorks = new FeederWorks();
             FeederWorks = new FeederWorks();
             FeederStockWorks = new FeederStockWorks();
             FeederStockWorks = new FeederStockWorks();
+            LightChannels = new ObservableCollection<ChannelConfig>();
         }
         }
 
 
         public ProcedureModel Clone()
         public ProcedureModel Clone()
@@ -392,6 +405,14 @@ namespace TeamAAS_VP.Models
                 OutputPointMode=this.OutputPointMode,
                 OutputPointMode=this.OutputPointMode,
                 OutputSeparator=this.OutputSeparator,
                 OutputSeparator=this.OutputSeparator,
                 UserDefineParameters = new ObservableCollection<ProcedureUserDefineParam>(this.UserDefineParameters.Select(udp => udp.Copy())),
                 UserDefineParameters = new ObservableCollection<ProcedureUserDefineParam>(this.UserDefineParameters.Select(udp => udp.Copy())),
+                LightChannels = this.LightChannels == null ? new ObservableCollection<ChannelConfig>() : new ObservableCollection<ChannelConfig>(this.LightChannels.Select(ch => new ChannelConfig
+                {
+                    Name = ch.Name,
+                    Description = ch.Description,
+                    LocalIndex = ch.LocalIndex,
+                    GlobalIndex = ch.GlobalIndex,
+                    DefaultBrightness = ch.DefaultBrightness
+                })),
             };
             };
         }
         }
     }
     }

+ 36 - 0
TeamAAS-VM/Resources/Languages/Lang.Designer.cs

@@ -1365,6 +1365,24 @@ namespace TeamAAS_VP.Resources.Languages {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   查找类似 光源通道 的本地化字符串。
+        /// </summary>
+        public static string 光源通道 {
+            get {
+                return ResourceManager.GetString("光源通道", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   查找类似 光源通道列表 的本地化字符串。
+        /// </summary>
+        public static string 光源通道列表 {
+            get {
+                return ResourceManager.GetString("光源通道列表", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   查找类似 全局参数个数 的本地化字符串。
         ///   查找类似 全局参数个数 的本地化字符串。
         /// </summary>
         /// </summary>
@@ -1545,6 +1563,15 @@ namespace TeamAAS_VP.Resources.Languages {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   查找类似 删除通道 的本地化字符串。
+        /// </summary>
+        public static string 删除通道 {
+            get {
+                return ResourceManager.GetString("删除通道", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   查找类似 剩余点位数量 的本地化字符串。
         ///   查找类似 剩余点位数量 的本地化字符串。
         /// </summary>
         /// </summary>
@@ -4002,6 +4029,15 @@ namespace TeamAAS_VP.Resources.Languages {
             }
             }
         }
         }
         
         
+        /// <summary>
+        ///   查找类似 添加通道 的本地化字符串。
+        /// </summary>
+        public static string 添加通道 {
+            get {
+                return ResourceManager.GetString("添加通道", resourceCulture);
+            }
+        }
+        
         /// <summary>
         /// <summary>
         ///   查找类似 清料完成时 的本地化字符串。
         ///   查找类似 清料完成时 的本地化字符串。
         /// </summary>
         /// </summary>

+ 12 - 0
TeamAAS-VM/Resources/Languages/Lang.resx

@@ -2096,4 +2096,16 @@
   <data name="选择下相机视觉流程" xml:space="preserve">
   <data name="选择下相机视觉流程" xml:space="preserve">
     <value>选择下相机视觉流程</value>
     <value>选择下相机视觉流程</value>
   </data>
   </data>
+  <data name="光源通道列表" xml:space="preserve">
+    <value>光源通道列表</value>
+  </data>
+  <data name="光源通道" xml:space="preserve">
+    <value>光源通道</value>
+  </data>
+  <data name="添加通道" xml:space="preserve">
+    <value>添加通道</value>
+  </data>
+  <data name="删除通道" xml:space="preserve">
+    <value>删除通道</value>
+  </data>
 </root>
 </root>

+ 5 - 2
TeamAAS-VM/TeamAAS-VP.csproj

@@ -207,8 +207,8 @@
     <Reference Include="MaterialDesignThemes.Wpf, Version=5.2.1.0, Culture=neutral, PublicKeyToken=df2a72020bd7962a, processorArchitecture=MSIL">
     <Reference Include="MaterialDesignThemes.Wpf, Version=5.2.1.0, Culture=neutral, PublicKeyToken=df2a72020bd7962a, processorArchitecture=MSIL">
       <HintPath>..\packages\MaterialDesignThemes.5.2.1\lib\net462\MaterialDesignThemes.Wpf.dll</HintPath>
       <HintPath>..\packages\MaterialDesignThemes.5.2.1\lib\net462\MaterialDesignThemes.Wpf.dll</HintPath>
     </Reference>
     </Reference>
-    <Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=9.0.0.9, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
-      <HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.9.0.9\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
+    <Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=10.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.1\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
     </Reference>
     </Reference>
     <Reference Include="Microsoft.Bcl.Cryptography, Version=9.0.0.9, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
     <Reference Include="Microsoft.Bcl.Cryptography, Version=9.0.0.9, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
       <HintPath>..\packages\Microsoft.Bcl.Cryptography.9.0.9\lib\net462\Microsoft.Bcl.Cryptography.dll</HintPath>
       <HintPath>..\packages\Microsoft.Bcl.Cryptography.9.0.9\lib\net462\Microsoft.Bcl.Cryptography.dll</HintPath>
@@ -388,6 +388,9 @@
     <Reference Include="System.Text.Json, Version=9.0.0.9, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
     <Reference Include="System.Text.Json, Version=9.0.0.9, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
       <HintPath>..\packages\System.Text.Json.9.0.9\lib\net462\System.Text.Json.dll</HintPath>
       <HintPath>..\packages\System.Text.Json.9.0.9\lib\net462\System.Text.Json.dll</HintPath>
     </Reference>
     </Reference>
+    <Reference Include="System.Threading.Channels, Version=10.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Threading.Channels.10.0.1\lib\net462\System.Threading.Channels.dll</HintPath>
+    </Reference>
     <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
     <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
       <HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
       <HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
     </Reference>
     </Reference>

+ 0 - 24
TeamAAS-VM/ViewModels/DebugMod/LightManualViewModel.cs

@@ -130,30 +130,6 @@ namespace TeamAAS_VP.ViewModels.DebugMod
             }
             }
         }
         }
 
 
-        // 计划(伪代码,详细步骤):
-        // 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
         public class ChannelItem : BindableBase
         {
         {
             private readonly int _globalId;
             private readonly int _globalId;

+ 138 - 1
TeamAAS-VM/ViewModels/Product/CameraParamsViewModel.cs

@@ -9,9 +9,13 @@ using Prism.Services.Dialogs;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.ObjectModel;
+using System.Drawing;
 using System.Linq;
 using System.Linq;
+using System.Threading;
+using System.Threading.Channels;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows;
+using System.Windows.Threading;
 using Team.FFFeederService.Interfaces;
 using Team.FFFeederService.Interfaces;
 using TeamAAS_VP;
 using TeamAAS_VP;
 using TeamAAS_VP.Controls;
 using TeamAAS_VP.Controls;
@@ -21,7 +25,9 @@ using TeamAAS_VP.Enums;
 using TeamAAS_VP.Events;
 using TeamAAS_VP.Events;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
 using TeamAAS_VP.Models;
+using TeamAAS_VP.Models.Lights;
 using TeamAAS_VP.Resources.Languages;
 using TeamAAS_VP.Resources.Languages;
+using TeamAAS_VP.Services;
 using static NPOI.HSSF.Util.HSSFColor;
 using static NPOI.HSSF.Util.HSSFColor;
 
 
 namespace TeamAAS_VP.ViewModels.Product
 namespace TeamAAS_VP.ViewModels.Product
@@ -39,6 +45,11 @@ namespace TeamAAS_VP.ViewModels.Product
         IProductService _productService;
         IProductService _productService;
         IRemoteCommandService _remoteCommandService;
         IRemoteCommandService _remoteCommandService;
         ISystemDatabaseService _systemDatabaseService;
         ISystemDatabaseService _systemDatabaseService;
+        ILightManagerService _lightManagerService;
+
+        // 创建Channel
+        private readonly Channel<(int channel, int brightness)> _brightnessChannel;
+        private CancellationTokenSource _cancellationTokenSource;
 
 
         #region 属性
         #region 属性
 
 
@@ -200,6 +211,18 @@ namespace TeamAAS_VP.ViewModels.Product
         public DelegateCommand LightValueSubCommand =>
         public DelegateCommand LightValueSubCommand =>
             _LightValueSubCommand ?? (_LightValueSubCommand = new DelegateCommand(ExecuteLightValueSubCommand));
             _LightValueSubCommand ?? (_LightValueSubCommand = new DelegateCommand(ExecuteLightValueSubCommand));
 
 
+        private DelegateCommand<object> _SetChannelBrightnessCommand;
+        public DelegateCommand<object> SetChannelBrightnessCommand =>
+            _SetChannelBrightnessCommand ?? (_SetChannelBrightnessCommand = new DelegateCommand<object>(ExecuteSetChannelBrightnessCommand));
+
+        private DelegateCommand<object> _TurnOnCommand;
+        public DelegateCommand<object> TurnOnCommand =>
+            _TurnOnCommand ?? (_TurnOnCommand = new DelegateCommand<object>(ExecuteTurnOnCommand));
+
+        private DelegateCommand<object> _TurnOffCommand;
+        public DelegateCommand<object> TurnOffCommand =>
+            _TurnOffCommand ?? (_TurnOffCommand = new DelegateCommand<object>(ExecuteTurnOffCommand));
+
         #endregion
         #endregion
 
 
         #region 事件
         #region 事件
@@ -208,7 +231,7 @@ namespace TeamAAS_VP.ViewModels.Product
 
 
         public CameraParamsViewModel(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IDialogService dialogService,
         public CameraParamsViewModel(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IDialogService dialogService,
             IConfigService configService, IRobotService robotService, ICameraService cameraService, IFeederService feederService, IProductService productService,
             IConfigService configService, IRobotService robotService, ICameraService cameraService, IFeederService feederService, IProductService productService,
-            IRemoteCommandService remoteCommandService, ISystemDatabaseService systemDatabaseService)
+            IRemoteCommandService remoteCommandService, ISystemDatabaseService systemDatabaseService, ILightManagerService lightManagerService)
         {
         {
             _regionManager = regionManager;
             _regionManager = regionManager;
             _eventAggregator = ea;
             _eventAggregator = ea;
@@ -223,10 +246,55 @@ namespace TeamAAS_VP.ViewModels.Product
             _eventAggregator.GetEvent<UserLoginNotification>().Subscribe(LoginChange);
             _eventAggregator.GetEvent<UserLoginNotification>().Subscribe(LoginChange);
             _productService = productService;
             _productService = productService;
             _remoteCommandService = remoteCommandService;
             _remoteCommandService = remoteCommandService;
+            _lightManagerService = lightManagerService;
+
+            // 创建有界Channel,容量为1,只保留最新的亮度值
+            _brightnessChannel = Channel.CreateBounded<(int channel, int brightness)>(
+                new BoundedChannelOptions(1)  // 容量为1
+                {
+                    // 当Channel满时,丢弃最旧的值,写入新的值
+                    FullMode = BoundedChannelFullMode.DropOldest,
+
+                    // 允许多个UI线程写入(虽然通常只有一个)
+                    SingleWriter = false,
+
+                    // 只有一个消费者读取并发送到设备
+                    SingleReader = true
+                });
+
+            _cancellationTokenSource = new CancellationTokenSource();
+
+            // 启动消费者任务
+            StartDeviceConsumer();
+            
         }
         }
 
 
         #region 方法
         #region 方法
 
 
+        // 消费者:从Channel读取并发送到设备
+        private async void StartDeviceConsumer()
+        {
+            try
+            {
+                while (await _brightnessChannel.Reader.WaitToReadAsync(_cancellationTokenSource.Token))
+                {
+                    while (_brightnessChannel.Reader.TryRead(out var obj))
+                    {
+                        // 实际写入(底层方法可能不支持取消)
+                        var success = await _lightManagerService.SetGlobalChannelBrightnessAsync(obj.channel, obj.brightness);
+                    }
+                }
+            }
+            catch (OperationCanceledException)
+            {
+                // 正常结束
+            }
+            finally
+            {
+                _brightnessChannel.Writer.Complete();
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// 下一步
         /// 下一步
         /// </summary>
         /// </summary>
@@ -330,6 +398,75 @@ namespace TeamAAS_VP.ViewModels.Product
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// 为指定通道应用亮度设置(仅更新模型并在可能时尝试通知硬件)。
+        /// </summary>
+        /// <param name="channel">要设置的通道配置。</param>
+        async void ExecuteSetChannelBrightnessCommand(object obj)
+        {
+            try
+            {
+                if (!(obj is Tuple<object, double, double> parameter)) return;
+
+                if (!(parameter.Item1 is ChannelConfig channel)) return;
+                int newValue = (int)parameter.Item2;
+                // 尝试写入Channel(非阻塞)
+                try
+                {
+                    // 如果Channel满了(有旧值),DropOldest策略会自动丢弃旧值
+                    await _brightnessChannel.Writer.WriteAsync(
+                        (channel.GlobalIndex, newValue),
+                        _cancellationTokenSource.Token);
+                }
+                catch (OperationCanceledException)
+                {
+                    // 正常取消,无需处理
+                }
+                catch (ChannelClosedException)
+                {
+                    // Channel已关闭
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("产品实时图像界面-设置光源通道亮度时出错!", ex);
+            }
+        }
+
+        /// <summary>
+        /// 关闭光源
+        /// </summary>
+        /// <param name="parameter"></param>
+        async void ExecuteTurnOffCommand(object parameter)
+        {
+            try
+            {
+                if (!(parameter is ChannelConfig channel)) return;
+                await _lightManagerService.TurnOffGlobalChannelAsync(channel.GlobalIndex);
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("关闭通道光源时出错!", ex);
+            }
+        }
+
+        /// <summary>
+        /// 打开光源
+        /// </summary>
+        /// <param name="parameter"></param>
+        async void ExecuteTurnOnCommand(object parameter)
+        {
+            try
+            {
+                if (!(parameter is ChannelConfig channel)) return;
+                await _lightManagerService.TurnOnGlobalChannelAsync(channel.GlobalIndex);
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("打开通道光源时出错!", ex);
+            }
+        }
+
         void ExecuteLightValueSubCommand()
         void ExecuteLightValueSubCommand()
         {
         {
             SelectProcedure.LightValue--;
             SelectProcedure.LightValue--;

+ 43 - 1
TeamAAS-VM/ViewModels/Product/ProcessBasicsViewModel.cs

@@ -20,6 +20,7 @@ using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
 using TeamAAS_VP.Models;
 using TeamAAS_VP.Models.Calibration;
 using TeamAAS_VP.Models.Calibration;
 using TeamAAS_VP.Models.Product;
 using TeamAAS_VP.Models.Product;
+using TeamAAS_VP.Models.Lights;
 
 
 namespace TeamAAS_VP.ViewModels.Product
 namespace TeamAAS_VP.ViewModels.Product
 {
 {
@@ -216,6 +217,15 @@ namespace TeamAAS_VP.ViewModels.Product
             set { SetProperty(ref _CalibrationList, value); }
             set { SetProperty(ref _CalibrationList, value); }
         }
         }
 
 
+        private ChannelConfig _SelectedLightChannel;
+        public ChannelConfig SelectedLightChannel
+        {
+            get { return _SelectedLightChannel; }
+            set { SetProperty(ref _SelectedLightChannel, value);
+                // no additional action
+            }
+        }
+
         private bool CanNext
         private bool CanNext
         {
         {
             get
             get
@@ -249,7 +259,11 @@ namespace TeamAAS_VP.ViewModels.Product
         public DelegateCommand LoadedCommand =>
         public DelegateCommand LoadedCommand =>
             _LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand));
             _LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand));
 
 
-        
+        private DelegateCommand _AddLightChannelCommand;
+        public DelegateCommand AddLightChannelCommand => _AddLightChannelCommand ?? (_AddLightChannelCommand = new DelegateCommand(ExecuteAddLightChannelCommand));
+
+        private DelegateCommand _RemoveLightChannelCommand;
+        public DelegateCommand RemoveLightChannelCommand => _RemoveLightChannelCommand ?? (_RemoveLightChannelCommand = new DelegateCommand(ExecuteRemoveLightChannelCommand).ObservesCanExecute(() => SelectedLightChannel != null));
         #endregion
         #endregion
 
 
         #region 事件
         #region 事件
@@ -349,6 +363,34 @@ namespace TeamAAS_VP.ViewModels.Product
             _regionManager.RequestNavigate("ProductRegionContext", "PlcPointParams", param);
             _regionManager.RequestNavigate("ProductRegionContext", "PlcPointParams", param);
         }
         }
 
 
+        /// <summary>
+        /// 添加光源通道
+        /// </summary>
+        void ExecuteAddLightChannelCommand()
+        {
+            if (SelectProcedure == null) return;
+            if (SelectProcedure.LightChannels == null)
+                SelectProcedure.LightChannels = new System.Collections.ObjectModel.ObservableCollection<ChannelConfig>();
+            var ch = new ChannelConfig
+            {
+                Name = $"Channel {SelectProcedure.LightChannels.Count + 1}",
+                LocalIndex = SelectProcedure.LightChannels.Count,
+                GlobalIndex = SelectProcedure.LightChannels.Count + 1,
+                DefaultBrightness = 100
+            };
+            SelectProcedure.LightChannels.Add(ch);
+        }
+
+        /// <summary>
+        /// 移除光源通道
+        /// </summary>
+        void ExecuteRemoveLightChannelCommand()
+        {
+            if (SelectProcedure == null || SelectedLightChannel == null) return;
+            SelectProcedure.LightChannels.Remove(SelectedLightChannel);
+            SelectedLightChannel = null;
+        }
+
         /// <summary>
         /// <summary>
         /// 流程选择
         /// 流程选择
         /// </summary>
         /// </summary>

+ 73 - 2
TeamAAS-VM/Views/Product/CameraParams.xaml

@@ -11,6 +11,7 @@
              xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
              xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
              xmlns:sys="clr-namespace:System;assembly=mscorlib"
              xmlns:sys="clr-namespace:System;assembly=mscorlib"
              xmlns:uControl="clr-namespace:TeamAAS_VP.Controls"
              xmlns:uControl="clr-namespace:TeamAAS_VP.Controls"
+             xmlns:localbehaviors="clr-namespace:TeamAAS_VP.Behaviors"
              xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
              xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
              xmlns:lex="http://wpflocalizeextension.codeplex.com"
              xmlns:lex="http://wpflocalizeextension.codeplex.com"
              lex:LocalizeDictionary.DesignCulture="zh-CN"
              lex:LocalizeDictionary.DesignCulture="zh-CN"
@@ -169,6 +170,73 @@
                                            Interval="1"
                                            Interval="1"
                                            StringFormat="{}{0:N2} db"
                                            StringFormat="{}{0:N2} db"
                                            Value="{Binding Gain,Mode=TwoWay}" />
                                            Value="{Binding Gain,Mode=TwoWay}" />
+                        <!-- 光源通道列表与调节 -->
+                        <StackPanel Orientation="Vertical"
+                                    Grid.Row="4"
+                                    Grid.ColumnSpan="2"
+                                    Margin="5"
+                                    Visibility="{Binding IsHaveFeeder,Converter={StaticResource BooleanToVisibilityConverter}}">
+                            <TextBlock Text="{lex:Loc 光源通道列表}"
+                                       FontWeight="Bold" />
+                            <ItemsControl ItemsSource="{Binding SelectProcedure.LightChannels}">
+                                <ItemsControl.ItemTemplate>
+                                    <DataTemplate>
+                                        <Grid>
+                                            <Grid.RowDefinitions>
+                                                <RowDefinition Height="auto" />
+                                                <RowDefinition Height="auto" />
+                                                <RowDefinition Height="auto" />
+                                            </Grid.RowDefinitions>
+                                            <TextBlock Width="120"
+                                                       Grid.Row="0"
+                                                       VerticalAlignment="Center">
+                                                <TextBlock.Text>
+                                                    <MultiBinding StringFormat="{}{0} . {1}">
+                                                        <Binding Path="GlobalIndex" />
+                                                        <Binding Path="Name" />
+                                                    </MultiBinding>
+                                                </TextBlock.Text>
+                                            </TextBlock>
+                                            <StackPanel Grid.Row="1"
+                                                        Orientation="Horizontal"
+                                                        Margin="0,4">
+
+                                                <Slider Minimum="0"
+                                                        Maximum="255"
+                                                        Width="200"
+                                                        Value="{Binding DefaultBrightness,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
+                                                    <b:Interaction.Behaviors>
+                                                        <localbehaviors:SliderUserInteractionBehavior UserInteractionCommand="{Binding DataContext.SetChannelBrightnessCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                                                                                      CommandParameter="{Binding}" />
+                                                    </b:Interaction.Behaviors>
+                                                </Slider>
+                                                <TextBlock Text="{Binding DefaultBrightness}"
+                                                           Width="40"
+                                                           VerticalAlignment="Center"
+                                                           Margin="6,0" />
+                                                <Button Content="{lex:Loc 应用}"
+                                                        Margin="6,0"
+                                                        Command="{Binding DataContext.SetChannelBrightnessCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                                        CommandParameter="{Binding}" />
+                                            </StackPanel>
+                                            <StackPanel Grid.Row="2"
+                                                        Orientation="Horizontal"
+                                                        Margin="0,4">
+                                                <Button Content="打开"
+                                                        Command="{Binding TurnOnCommand}"
+                                                        CommandParameter="{Binding}"
+                                                        Margin="0,0,8,0" />
+                                                <Button Content="关闭"
+                                                        Command="{Binding TurnOffCommand}"
+                                                        CommandParameter="{Binding}"
+                                                        Margin="0,0,8,0" />
+                                            </StackPanel>
+                                        </Grid>
+
+                                    </DataTemplate>
+                                </ItemsControl.ItemTemplate>
+                            </ItemsControl>
+                        </StackPanel>
                     </Grid>
                     </Grid>
                     <wf:WindowsFormsHost Grid.Column="0"
                     <wf:WindowsFormsHost Grid.Column="0"
                                          Margin="0,40">
                                          Margin="0,40">
@@ -189,7 +257,7 @@
                                 Margin="5,0"
                                 Margin="5,0"
                                 Content="{materialDesign:PackIcon Kind=Minus}"
                                 Content="{materialDesign:PackIcon Kind=Minus}"
                                 Style="{StaticResource MaterialDesignIconForegroundButton}"
                                 Style="{StaticResource MaterialDesignIconForegroundButton}"
-                                Command="{Binding LightValueSubCommand}"/>
+                                Command="{Binding LightValueSubCommand}" />
                         <Slider x:Name="lightSlider"
                         <Slider x:Name="lightSlider"
                                 Maximum="100"
                                 Maximum="100"
                                 Minimum="1"
                                 Minimum="1"
@@ -214,6 +282,7 @@
                                    VerticalAlignment="Center" />
                                    VerticalAlignment="Center" />
 
 
                     </StackPanel>
                     </StackPanel>
+
                     <StackPanel Grid.Column="0"
                     <StackPanel Grid.Column="0"
                                 HorizontalAlignment="Left"
                                 HorizontalAlignment="Left"
                                 VerticalAlignment="Bottom"
                                 VerticalAlignment="Bottom"
@@ -234,7 +303,9 @@
                                 Style="{StaticResource MaterialDesignRaisedButton}"
                                 Style="{StaticResource MaterialDesignRaisedButton}"
                                 Command="{Binding StopGrabbingCommand}" />
                                 Command="{Binding StopGrabbingCommand}" />
 
 
-                        <TextBlock Text="{Binding Message}" VerticalAlignment="Center" Foreground="Gray"/>
+                        <TextBlock Text="{Binding Message}"
+                                   VerticalAlignment="Center"
+                                   Foreground="Gray" />
                     </StackPanel>
                     </StackPanel>
                 </Grid>
                 </Grid>
             </Grid>
             </Grid>

+ 41 - 2
TeamAAS-VM/Views/Product/ProcessBasics.xaml

@@ -30,7 +30,7 @@
     </b:Interaction.Triggers>
     </b:Interaction.Triggers>
 
 
     <UserControl.Resources>
     <UserControl.Resources>
-        
+
     </UserControl.Resources>
     </UserControl.Resources>
 
 
     <Grid>
     <Grid>
@@ -125,6 +125,8 @@
                                    MinHeight="35" />
                                    MinHeight="35" />
                     <RowDefinition Height="auto"
                     <RowDefinition Height="auto"
                                    MinHeight="35" />
                                    MinHeight="35" />
+                    <RowDefinition Height="auto"
+                                   MinHeight="35" />
                 </Grid.RowDefinitions>
                 </Grid.RowDefinitions>
                 <Grid.ColumnDefinitions>
                 <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="auto" />
                     <ColumnDefinition Width="auto" />
@@ -160,7 +162,7 @@
                                 <materialDesign:PackIcon Kind="VideoOutline" />
                                 <materialDesign:PackIcon Kind="VideoOutline" />
                                 <TextBlock Text="{Binding CameraName}"
                                 <TextBlock Text="{Binding CameraName}"
                                            Margin="6,0" />
                                            Margin="6,0" />
-                                <materialDesign:PackIcon Kind="ProgressWrench"/>
+                                <materialDesign:PackIcon Kind="ProgressWrench" />
                                 <TextBlock Text="{Binding Name}"
                                 <TextBlock Text="{Binding Name}"
                                            Margin="6,0" />
                                            Margin="6,0" />
                             </StackPanel>
                             </StackPanel>
@@ -217,6 +219,43 @@
                     </ComboBox.ItemTemplate>
                     </ComboBox.ItemTemplate>
                 </ComboBox>
                 </ComboBox>
 
 
+                <!-- 光源通道配置 -->
+                <TextBlock Text="{lex:Loc 光源通道,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
+                           Grid.Row="7"
+                           Grid.Column="0" />
+                <StackPanel Grid.Row="7"
+                            Grid.Column="1"
+                            Orientation="Horizontal">
+                    <Button Content="{lex:Loc 添加通道}"
+                            Command="{Binding AddLightChannelCommand}"
+                            Margin="0,0,5,0" />
+                    <Button Content="{lex:Loc 删除通道}"
+                            Command="{Binding RemoveLightChannelCommand}"
+                            Margin="0,0,5,0" />
+                </StackPanel>
+
+                <ListBox Grid.Row="8"
+                         Grid.ColumnSpan="2"
+                         ItemsSource="{Binding SelectProcedure.LightChannels}"
+                         SelectedItem="{Binding SelectedLightChannel}"
+                         Height="120"
+                         Margin="0,10,0,0">
+                    <ListBox.ItemTemplate>
+                        <DataTemplate>
+                            <TextBlock Width="120"
+                                       Grid.Row="0"
+                                       VerticalAlignment="Center">
+                                <TextBlock.Text>
+                                    <MultiBinding StringFormat="{}{0} . {1}">
+                                        <Binding Path="GlobalIndex" />
+                                        <Binding Path="Name" />
+                                    </MultiBinding>
+                                </TextBlock.Text>
+                            </TextBlock>
+                        </DataTemplate>
+                    </ListBox.ItemTemplate>
+                </ListBox>
+
                 <TextBlock Text="{lex:Loc 校准,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
                 <TextBlock Text="{lex:Loc 校准,Converter={StaticResource StringFormatConverter},ConverterParameter='{}{0}: '}"
                            Grid.Row="5"
                            Grid.Row="5"
                            Grid.Column="0" />
                            Grid.Column="0" />

+ 2 - 1
TeamAAS-VM/packages.config

@@ -13,7 +13,7 @@
   <package id="MaterialDesignThemes" version="5.2.1" targetFramework="net48" />
   <package id="MaterialDesignThemes" version="5.2.1" targetFramework="net48" />
   <package id="MathNet.Numerics" version="5.0.0" targetFramework="net48" />
   <package id="MathNet.Numerics" version="5.0.0" targetFramework="net48" />
   <package id="MathNet.Numerics.Signed" version="5.0.0" targetFramework="net48" />
   <package id="MathNet.Numerics.Signed" version="5.0.0" targetFramework="net48" />
-  <package id="Microsoft.Bcl.AsyncInterfaces" version="9.0.9" targetFramework="net48" />
+  <package id="Microsoft.Bcl.AsyncInterfaces" version="10.0.1" targetFramework="net48" />
   <package id="Microsoft.Bcl.Cryptography" version="9.0.9" targetFramework="net48" />
   <package id="Microsoft.Bcl.Cryptography" version="9.0.9" targetFramework="net48" />
   <package id="Microsoft.Bcl.HashCode" version="6.0.0" targetFramework="net48" />
   <package id="Microsoft.Bcl.HashCode" version="6.0.0" targetFramework="net48" />
   <package id="Microsoft.CodeAnalysis.Analyzers" version="3.11.0" targetFramework="net48" developmentDependency="true" />
   <package id="Microsoft.CodeAnalysis.Analyzers" version="3.11.0" targetFramework="net48" developmentDependency="true" />
@@ -82,6 +82,7 @@
   <package id="System.Text.Encoding.CodePages" version="9.0.9" targetFramework="net48" />
   <package id="System.Text.Encoding.CodePages" version="9.0.9" targetFramework="net48" />
   <package id="System.Text.Encodings.Web" version="9.0.9" targetFramework="net48" />
   <package id="System.Text.Encodings.Web" version="9.0.9" targetFramework="net48" />
   <package id="System.Text.Json" version="9.0.9" targetFramework="net48" />
   <package id="System.Text.Json" version="9.0.9" targetFramework="net48" />
+  <package id="System.Threading.Channels" version="10.0.1" targetFramework="net48" />
   <package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
   <package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
   <package id="System.ValueTuple" version="4.6.1" targetFramework="net48" />
   <package id="System.ValueTuple" version="4.6.1" targetFramework="net48" />
   <package id="TouchSocket" version="3.1.17" targetFramework="net48" />
   <package id="TouchSocket" version="3.1.17" targetFramework="net48" />