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" />
         <bindingRedirect oldVersion="0.0.0.0-1.0.118.0" newVersion="1.0.118.0" />
       </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>
   </runtime>
   <entityFramework>

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

@@ -10,6 +10,7 @@ using System.Text;
 using System.Threading.Tasks;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Models.Feeder;
+using TeamAAS_VP.Models.Lights;
 using TeamAAS_VP.Models.Product;
 
 namespace TeamAAS_VP.Models
@@ -313,6 +314,16 @@ namespace TeamAAS_VP.Models
             set { SetProperty(ref _UserDefineParameters, value); }
         }
 
+        private ObservableCollection<ChannelConfig> _LightChannels;
+        /// <summary>
+        /// 光源通道配置集合。每个流程可配置多个光源通道及其默认亮度。
+        /// </summary>
+        public ObservableCollection<ChannelConfig> LightChannels
+        {
+            get { return _LightChannels; }
+            set { SetProperty(ref _LightChannels, value); }
+        }
+
         #region 运行时的一些临时记忆
 
         /// <summary>
@@ -338,6 +349,7 @@ namespace TeamAAS_VP.Models
             UserDefineParameters= new ObservableCollection<ProcedureUserDefineParam>();
             FeederWorks = new FeederWorks();
             FeederStockWorks= new FeederStockWorks();
+            LightChannels = new ObservableCollection<ChannelConfig>();
         }
 
         public ProcedureModel(string name, string cameraName, Guid cameraId)
@@ -352,6 +364,7 @@ namespace TeamAAS_VP.Models
             UserDefineParameters = new ObservableCollection<ProcedureUserDefineParam>();
             FeederWorks = new FeederWorks();
             FeederStockWorks = new FeederStockWorks();
+            LightChannels = new ObservableCollection<ChannelConfig>();
         }
 
         public ProcedureModel Clone()
@@ -392,6 +405,14 @@ namespace TeamAAS_VP.Models
                 OutputPointMode=this.OutputPointMode,
                 OutputSeparator=this.OutputSeparator,
                 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>
@@ -1545,6 +1563,15 @@ namespace TeamAAS_VP.Resources.Languages {
             }
         }
         
+        /// <summary>
+        ///   查找类似 删除通道 的本地化字符串。
+        /// </summary>
+        public static string 删除通道 {
+            get {
+                return ResourceManager.GetString("删除通道", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   查找类似 剩余点位数量 的本地化字符串。
         /// </summary>
@@ -4002,6 +4029,15 @@ namespace TeamAAS_VP.Resources.Languages {
             }
         }
         
+        /// <summary>
+        ///   查找类似 添加通道 的本地化字符串。
+        /// </summary>
+        public static string 添加通道 {
+            get {
+                return ResourceManager.GetString("添加通道", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   查找类似 清料完成时 的本地化字符串。
         /// </summary>

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

@@ -2096,4 +2096,16 @@
   <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>
+  <data name="删除通道" xml:space="preserve">
+    <value>删除通道</value>
+  </data>
 </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">
       <HintPath>..\packages\MaterialDesignThemes.5.2.1\lib\net462\MaterialDesignThemes.Wpf.dll</HintPath>
     </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 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>
@@ -388,6 +388,9 @@
     <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>
     </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">
       <HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
     </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
         {
             private readonly int _globalId;

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

@@ -9,9 +9,13 @@ using Prism.Services.Dialogs;
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using System.Drawing;
 using System.Linq;
+using System.Threading;
+using System.Threading.Channels;
 using System.Threading.Tasks;
 using System.Windows;
+using System.Windows.Threading;
 using Team.FFFeederService.Interfaces;
 using TeamAAS_VP;
 using TeamAAS_VP.Controls;
@@ -21,7 +25,9 @@ using TeamAAS_VP.Enums;
 using TeamAAS_VP.Events;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
+using TeamAAS_VP.Models.Lights;
 using TeamAAS_VP.Resources.Languages;
+using TeamAAS_VP.Services;
 using static NPOI.HSSF.Util.HSSFColor;
 
 namespace TeamAAS_VP.ViewModels.Product
@@ -39,6 +45,11 @@ namespace TeamAAS_VP.ViewModels.Product
         IProductService _productService;
         IRemoteCommandService _remoteCommandService;
         ISystemDatabaseService _systemDatabaseService;
+        ILightManagerService _lightManagerService;
+
+        // 创建Channel
+        private readonly Channel<(int channel, int brightness)> _brightnessChannel;
+        private CancellationTokenSource _cancellationTokenSource;
 
         #region 属性
 
@@ -200,6 +211,18 @@ namespace TeamAAS_VP.ViewModels.Product
         public DelegateCommand LightValueSubCommand =>
             _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
 
         #region 事件
@@ -208,7 +231,7 @@ namespace TeamAAS_VP.ViewModels.Product
 
         public CameraParamsViewModel(IRegionManager regionManager, IEventAggregator ea, IContainerProvider container, IDialogService dialogService,
             IConfigService configService, IRobotService robotService, ICameraService cameraService, IFeederService feederService, IProductService productService,
-            IRemoteCommandService remoteCommandService, ISystemDatabaseService systemDatabaseService)
+            IRemoteCommandService remoteCommandService, ISystemDatabaseService systemDatabaseService, ILightManagerService lightManagerService)
         {
             _regionManager = regionManager;
             _eventAggregator = ea;
@@ -223,10 +246,55 @@ namespace TeamAAS_VP.ViewModels.Product
             _eventAggregator.GetEvent<UserLoginNotification>().Subscribe(LoginChange);
             _productService = productService;
             _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 方法
 
+        // 消费者:从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>
@@ -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()
         {
             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.Calibration;
 using TeamAAS_VP.Models.Product;
+using TeamAAS_VP.Models.Lights;
 
 namespace TeamAAS_VP.ViewModels.Product
 {
@@ -216,6 +217,15 @@ namespace TeamAAS_VP.ViewModels.Product
             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
         {
             get
@@ -249,7 +259,11 @@ namespace TeamAAS_VP.ViewModels.Product
         public DelegateCommand LoadedCommand =>
             _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
 
         #region 事件
@@ -349,6 +363,34 @@ namespace TeamAAS_VP.ViewModels.Product
             _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>

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

@@ -11,6 +11,7 @@
              xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
              xmlns:sys="clr-namespace:System;assembly=mscorlib"
              xmlns:uControl="clr-namespace:TeamAAS_VP.Controls"
+             xmlns:localbehaviors="clr-namespace:TeamAAS_VP.Behaviors"
              xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
              xmlns:lex="http://wpflocalizeextension.codeplex.com"
              lex:LocalizeDictionary.DesignCulture="zh-CN"
@@ -169,6 +170,73 @@
                                            Interval="1"
                                            StringFormat="{}{0:N2} db"
                                            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>
                     <wf:WindowsFormsHost Grid.Column="0"
                                          Margin="0,40">
@@ -189,7 +257,7 @@
                                 Margin="5,0"
                                 Content="{materialDesign:PackIcon Kind=Minus}"
                                 Style="{StaticResource MaterialDesignIconForegroundButton}"
-                                Command="{Binding LightValueSubCommand}"/>
+                                Command="{Binding LightValueSubCommand}" />
                         <Slider x:Name="lightSlider"
                                 Maximum="100"
                                 Minimum="1"
@@ -214,6 +282,7 @@
                                    VerticalAlignment="Center" />
 
                     </StackPanel>
+
                     <StackPanel Grid.Column="0"
                                 HorizontalAlignment="Left"
                                 VerticalAlignment="Bottom"
@@ -234,7 +303,9 @@
                                 Style="{StaticResource MaterialDesignRaisedButton}"
                                 Command="{Binding StopGrabbingCommand}" />
 
-                        <TextBlock Text="{Binding Message}" VerticalAlignment="Center" Foreground="Gray"/>
+                        <TextBlock Text="{Binding Message}"
+                                   VerticalAlignment="Center"
+                                   Foreground="Gray" />
                     </StackPanel>
                 </Grid>
             </Grid>

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

@@ -30,7 +30,7 @@
     </b:Interaction.Triggers>
 
     <UserControl.Resources>
-        
+
     </UserControl.Resources>
 
     <Grid>
@@ -125,6 +125,8 @@
                                    MinHeight="35" />
                     <RowDefinition Height="auto"
                                    MinHeight="35" />
+                    <RowDefinition Height="auto"
+                                   MinHeight="35" />
                 </Grid.RowDefinitions>
                 <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="auto" />
@@ -160,7 +162,7 @@
                                 <materialDesign:PackIcon Kind="VideoOutline" />
                                 <TextBlock Text="{Binding CameraName}"
                                            Margin="6,0" />
-                                <materialDesign:PackIcon Kind="ProgressWrench"/>
+                                <materialDesign:PackIcon Kind="ProgressWrench" />
                                 <TextBlock Text="{Binding Name}"
                                            Margin="6,0" />
                             </StackPanel>
@@ -217,6 +219,43 @@
                     </ComboBox.ItemTemplate>
                 </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}: '}"
                            Grid.Row="5"
                            Grid.Column="0" />

+ 2 - 1
TeamAAS-VM/packages.config

@@ -13,7 +13,7 @@
   <package id="MaterialDesignThemes" version="5.2.1" 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="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.HashCode" version="6.0.0" targetFramework="net48" />
   <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.Encodings.Web" 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.ValueTuple" version="4.6.1" targetFramework="net48" />
   <package id="TouchSocket" version="3.1.17" targetFramework="net48" />