yaoxiaohan há 8 meses atrás
pai
commit
e42746a773
30 ficheiros alterados com 4316 adições e 118 exclusões
  1. 712 0
      TeamAAS-VM/Controls/PlcRobotManual.xaml
  2. 581 0
      TeamAAS-VM/Controls/PlcRobotManual.xaml.cs
  3. 9 9
      TeamAAS-VM/Core/Cameras/AHuaCamera.cs
  4. 9 9
      TeamAAS-VM/Core/Cameras/BaslerCamera.cs
  5. 15 15
      TeamAAS-VM/Core/Cameras/CognexCamera.cs
  6. 10 6
      TeamAAS-VM/Core/Cameras/GalaxyCamera.cs
  7. 15 15
      TeamAAS-VM/Core/Cameras/MvCamera.cs
  8. 33 9
      TeamAAS-VM/Core/Cameras/OptCamera.cs
  9. 3 4
      TeamAAS-VM/Core/FilePath.cs
  10. 12 0
      TeamAAS-VM/Core/Lights/KCSLightController.cs
  11. 20 11
      TeamAAS-VM/Core/Management.cs
  12. 414 0
      TeamAAS-VM/Core/RectangleCenterCalculator.cs
  13. 3 2
      TeamAAS-VM/Interfaces/ICamera.cs
  14. 5 0
      TeamAAS-VM/Interfaces/IConfigService.cs
  15. 44 0
      TeamAAS-VM/Models/Torque/TorqueSerialPortConfig.cs
  16. BIN
      TeamAAS-VM/Resources/Images/MobileDown_IndependentXYPlatform.png
  17. 58 0
      TeamAAS-VM/Services/ConfigService.cs
  18. 4 0
      TeamAAS-VM/Services/RemoteCommandService.cs
  19. 1 0
      TeamAAS-VM/TeamAAS-VP.csproj
  20. 317 0
      TeamAAS-VM/ViewModels/Calibration/CalibIndependentCameraViewModel.cs
  21. 261 0
      TeamAAS-VM/ViewModels/Calibration/CalibrationDistortionViewModel.cs
  22. 568 0
      TeamAAS-VM/ViewModels/DebugMod/PlcRobotManualStepViewModel.cs
  23. 31 0
      TeamAAS-VM/ViewModels/SettingViewModel.cs
  24. 123 0
      TeamAAS-VM/Views/Calibration/CalibIndependentCamera.xaml
  25. 73 0
      TeamAAS-VM/Views/Calibration/CalibIndependentCamera.xaml.cs
  26. 98 0
      TeamAAS-VM/Views/Calibration/CalibrationDistortion.xaml
  27. 18 0
      TeamAAS-VM/Views/Calibration/CalibrationDistortion.xaml.cs
  28. 752 0
      TeamAAS-VM/Views/DebugMod/PlcRobotManualStep.xaml
  29. 15 0
      TeamAAS-VM/Views/DebugMod/PlcRobotManualStep.xaml.cs
  30. 112 38
      TeamAAS-VM/Views/SettingView.xaml

+ 712 - 0
TeamAAS-VM/Controls/PlcRobotManual.xaml

@@ -0,0 +1,712 @@
+<UserControl x:Class="TeamAAS_VP.Controls.PlcRobotManual"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:local="clr-namespace:TeamAAS_VP.Controls"
+             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
+             xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
+             xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:sys="clr-namespace:System;assembly=mscorlib"
+             xmlns:uControl="clr-namespace:TeamAAS_VP.Controls"
+             xmlns:convert="clr-namespace:TeamAAS_VP.ValueConverter"
+             xmlns:lex="http://wpflocalizeextension.codeplex.com"
+             lex:LocalizeDictionary.DesignCulture="zh-CN"
+             lex:ResxLocalizationProvider.DefaultAssembly="TeamAAS-VP"
+             lex:ResxLocalizationProvider.DefaultDictionary="Lang"
+             mc:Ignorable="d"
+             d:Background="White"
+             d:DataContext="{d:DesignInstance Type=local:PlcRobotManual}"
+             FontFamily="{DynamicResource DefaultFont}">
+    <UserControl.Resources>
+        <convert:JogConverter x:Key="JogConverter" />
+    </UserControl.Resources>
+    <ScrollViewer HorizontalAlignment="Stretch"
+                  VerticalAlignment="Stretch">
+        <Grid HorizontalAlignment="Stretch"
+              VerticalAlignment="Top">
+            <Grid.RowDefinitions>
+                <RowDefinition Height="auto" />
+                <RowDefinition Height="auto" />
+                <RowDefinition Height="auto" />
+            </Grid.RowDefinitions>
+
+            <Grid Grid.Row="0"
+                  Margin="10"
+                  HorizontalAlignment="Center">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="auto" />
+                    <ColumnDefinition Width="auto" />
+                    <ColumnDefinition Width="auto" />
+                    <ColumnDefinition Width="auto" />
+                </Grid.ColumnDefinitions>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                </Grid.RowDefinitions>
+                <Grid.Resources>
+                    <Style TargetType="Button"
+                           BasedOn="{StaticResource MaterialDesignRaisedDarkButton}">
+                        <Setter Property="Width"
+                                Value="60" />
+                        <Setter Property="Height"
+                                Value="60" />
+                        <Setter Property="FontSize"
+                                Value="13" />
+                        <Setter Property="Padding"
+                                Value="0" />
+                        <Setter Property="HorizontalAlignment"
+                                Value="Center" />
+                        <Setter Property="VerticalAlignment"
+                                Value="Center" />
+                        <Setter Property="Margin"
+                                Value="2" />
+                        <Setter Property="materialDesign:ButtonAssist.CornerRadius"
+                                Value="10" />
+                    </Style>
+                </Grid.Resources>
+                <StackPanel Grid.ColumnSpan="4"
+                            Grid.Row="0"
+                            Grid.Column="0"
+                            VerticalAlignment="Center"
+                            Orientation="Horizontal">
+                    <Button Content="{lex:Loc 上使能}"
+                            Grid.Row="2"
+                            materialDesign:ButtonAssist.CornerRadius="10"
+                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                            Command="{Binding MotorCommand}"
+                            CommandParameter="True" />
+                    <Button Content="{lex:Loc 下使能}"
+                            Grid.Row="3"
+                            Margin="10"
+                            materialDesign:ButtonAssist.CornerRadius="10"
+                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                            Command="{Binding MotorCommand}"
+                            CommandParameter="False" />
+                    <Button Content="{lex:Loc 复位错误}"
+                            Grid.Row="4"
+                            materialDesign:ButtonAssist.CornerRadius="10"
+                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                            Command="{Binding ResetCommand}" />
+                </StackPanel>
+                <Button Grid.Row="2"
+                        Grid.Column="0"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=X-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowLeftBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-X"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="2"
+                        Grid.Column="2"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=X+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowRightBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+X"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="1"
+                        Grid.Column="1"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Y+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowTopBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+Y"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="3"
+                        Grid.Column="1"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Y-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowBottomBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-Y"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="1"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Z+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowTopBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+Z"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="3"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Z-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowBottomBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-Z"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="4"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=U+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisZRotateCounterclockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+U"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="4"
+                        Grid.Column="0"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=U-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisZRotateClockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-U"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <!--<Button Grid.Row="4"
+                        Grid.Column="1"
+                        Grid.ColumnSpan="2"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=V+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisYRotateCounterclockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+V"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="5"
+                        Grid.Column="1"
+                        Grid.ColumnSpan="2"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=V-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisYRotateClockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-V"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>-->
+                <Button Grid.Row="5"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=W+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisXRotateCounterclockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+W"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="5"
+                        Grid.Column="0"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=W-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisXRotateClockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-W"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <StackPanel Grid.Row="6"
+                            Grid.ColumnSpan="7"
+                            Margin="10,10,0,10"
+                            Grid.Column="0"
+                            VerticalAlignment="Center"
+                            Orientation="Horizontal">
+                    <TextBlock Text="{lex:Loc 步进距离}"
+                               VerticalAlignment="Center" />
+                    <uControl:myNumericUpDowm x:Name="txtDistance"
+                                              MinWidth="100"
+                                              Interval="0.1"
+                                              Minimum="0"
+                                              Maximum="100"
+                                              StringFormat="{}{0:N3} mm"
+                                              Value="{Binding Distance,Mode=TwoWay}"
+                                              Margin="10,0" />
+                </StackPanel>
+                <TextBlock Grid.Row="7"
+                           Grid.ColumnSpan="7"
+                           VerticalAlignment="Center"
+                           Margin="10,0">
+                    <TextBlock.Text>
+                        <MultiBinding StringFormat="{}CurPos X: {0:F3} mm Y: {1:F3} mm Z: {2:F3} mm U: {3:F3} °R: {4:F3} °">
+                            <Binding Path="Robot.CurrentPosition.X" />
+                            <Binding Path="Robot.CurrentPosition.Y" />
+                            <Binding Path="Robot.CurrentPosition.Z" />
+                            <Binding Path="Robot.CurrentPosition.U" />
+                            <Binding Path="Robot.CurrentPosition.W" />
+                        </MultiBinding>
+                    </TextBlock.Text>
+                </TextBlock>
+            </Grid>
+            <!--相机轴控制区域-->
+            <!--<Grid Grid.Row="1"
+                  Margin="10,0,10,10">
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="*" />
+                    <RowDefinition Height="*" />
+                </Grid.RowDefinitions>
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="*" />
+                    <ColumnDefinition Width="*" />
+                </Grid.ColumnDefinitions>
+                <Grid.Resources>
+                    <Style TargetType="Button"
+                           BasedOn="{StaticResource MaterialDesignRaisedDarkButton}">
+                        <Setter Property="MinWidth"
+                                Value="50" />
+                        <Setter Property="MinHeight"
+                                Value="30" />
+                        <Setter Property="FontSize"
+                                Value="13" />
+                        <Setter Property="Padding"
+                                Value="0" />
+                        <Setter Property="HorizontalAlignment"
+                                Value="Center" />
+                        <Setter Property="VerticalAlignment"
+                                Value="Center" />
+                        <Setter Property="materialDesign:ButtonAssist.CornerRadius"
+                                Value="10" />
+                    </Style>
+                </Grid.Resources>
+                --><!--相机2--><!--
+                <Border Grid.Row="0"
+                        Grid.Column="0"
+                        HorizontalAlignment="Stretch"
+                        BorderBrush="LightGray"
+                        BorderThickness="1"
+                        CornerRadius="10"
+                        Margin="2">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                        </Grid.RowDefinitions>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                        </Grid.ColumnDefinitions>
+                        <TextBlock Grid.Row="0"
+                                   Grid.ColumnSpan="3"
+                                   Text="相机2轴控制"
+                                   FontWeight="Bold"
+                                   FontSize="14"
+                                   HorizontalAlignment="Center" />
+                        <TextBlock Grid.Row="1"
+                                   Grid.ColumnSpan="3"
+                                   VerticalAlignment="Center"
+                                   Margin="10,0">
+                            <TextBlock.Text>
+                                <MultiBinding StringFormat="{}X: {0:F3} mm Y: {1:F3} mm">
+                                    <Binding Path="Robot.Camera2CurrentPosition.X" />
+                                    <Binding Path="Robot.Camera2CurrentPosition.Y" />
+                                </MultiBinding>
+                            </TextBlock.Text>
+                        </TextBlock>
+                        <StackPanel Grid.Row="2"
+                                    Grid.ColumnSpan="3"
+                                    Orientation="Horizontal"
+                                    HorizontalAlignment="Center">
+                            <Button Content="{lex:Loc 上使能}"
+                                    Command="{Binding CameraAxisModerOnCommand}"
+                                    CommandParameter="2"
+                                    Margin="2,0" />
+                            <Button Content="{lex:Loc 下使能}"
+                                    Margin="2,0"
+                                    Command="{Binding CameraAxisModerOffCommand}"
+                                    CommandParameter="2" />
+                        </StackPanel>
+                        <Button Grid.Row="4"
+                                Grid.Column="0"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowLeftBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="2,X-" />
+                        <Button Grid.Row="4"
+                                Grid.Column="2"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowRightBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="2,X+" />
+                        <Button Grid.Row="3"
+                                Grid.Column="1"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowTopBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="2,Y+" />
+                        <Button Grid.Row="5"
+                                Grid.Column="1"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowBottomBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="2,Y-" />
+                    </Grid>
+                </Border>
+                --><!--相机4--><!--
+                <Border Grid.Row="0"
+                        Grid.Column="1"
+                        HorizontalAlignment="Stretch"
+                        BorderBrush="LightGray"
+                        BorderThickness="1"
+                        CornerRadius="10"
+                        Margin="2">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                        </Grid.RowDefinitions>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                        </Grid.ColumnDefinitions>
+                        <TextBlock Grid.Row="0"
+                                   Grid.ColumnSpan="3"
+                                   Text="相机4轴控制"
+                                   FontWeight="Bold"
+                                   FontSize="14"
+                                   HorizontalAlignment="Center" />
+                        <TextBlock Grid.Row="1"
+                                   Grid.ColumnSpan="3"
+                                   VerticalAlignment="Center"
+                                   Margin="10,0">
+                            <TextBlock.Text>
+                                <MultiBinding StringFormat="{}X: {0:F3} mm Y: {1:F3} mm">
+                                    <Binding Path="Robot.Camera4CurrentPosition.X" />
+                                    <Binding Path="Robot.Camera4CurrentPosition.Y" />
+                                </MultiBinding>
+                            </TextBlock.Text>
+                        </TextBlock>
+                        <StackPanel Grid.Row="2"
+                                    Grid.ColumnSpan="3"
+                                    Orientation="Horizontal"
+                                    HorizontalAlignment="Center">
+                            <Button Content="{lex:Loc 上使能}"
+                                    Command="{Binding CameraAxisModerOnCommand}"
+                                    CommandParameter="4"
+                                    Margin="2,0" />
+                            <Button Content="{lex:Loc 下使能}"
+                                    Command="{Binding CameraAxisModerOffCommand}"
+                                    CommandParameter="4"
+                                    Margin="2,0" />
+                        </StackPanel>
+                        <Button Grid.Row="4"
+                                Grid.Column="0"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowLeftBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="4,X-" />
+                        <Button Grid.Row="4"
+                                Grid.Column="2"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowRightBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="4,X+" />
+                        <Button Grid.Row="3"
+                                Grid.Column="1"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowTopBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="4,Y+" />
+                        <Button Grid.Row="5"
+                                Grid.Column="1"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowBottomBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="4,Y-" />
+                    </Grid>
+                </Border>
+                --><!--相机1--><!--
+                <Border Grid.Row="1"
+                        Grid.Column="0"
+                        HorizontalAlignment="Stretch"
+                        BorderBrush="LightGray"
+                        BorderThickness="1"
+                        CornerRadius="10"
+                        Margin="2">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                        </Grid.RowDefinitions>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                        </Grid.ColumnDefinitions>
+                        <TextBlock Grid.Row="0"
+                                   Grid.ColumnSpan="3"
+                                   Text="相机1轴控制"
+                                   FontWeight="Bold"
+                                   FontSize="14"
+                                   HorizontalAlignment="Center" />
+                        <TextBlock Grid.Row="1"
+                                   Grid.ColumnSpan="3"
+                                   VerticalAlignment="Center"
+                                   Margin="10,0">
+                            <TextBlock.Text>
+                                <MultiBinding StringFormat="{}X: {0:F3} mm Y: {1:F3} mm">
+                                    <Binding Path="Robot.Camera1CurrentPosition.X" />
+                                    <Binding Path="Robot.Camera1CurrentPosition.Y" />
+                                </MultiBinding>
+                            </TextBlock.Text>
+                        </TextBlock>
+                        <StackPanel Grid.Row="2"
+                                    Grid.ColumnSpan="3"
+                                    Orientation="Horizontal"
+                                    HorizontalAlignment="Center">
+                            <Button Content="{lex:Loc 上使能}"
+                                    Command="{Binding CameraAxisModerOnCommand}"
+                                    CommandParameter="1"
+                                    Margin="2,0" />
+                            <Button Content="{lex:Loc 下使能}"
+                                    Command="{Binding CameraAxisModerOffCommand}"
+                                    CommandParameter="1"
+                                    Margin="2,0" />
+                        </StackPanel>
+                        <Button Grid.Row="4"
+                                Grid.Column="0"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowLeftBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="1,X-" />
+                        <Button Grid.Row="4"
+                                Grid.Column="2"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowRightBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="1,X+" />
+                    </Grid>
+                </Border>
+                --><!--相机3--><!--
+                <Border Grid.Row="1"
+                        Grid.Column="1"
+                        HorizontalAlignment="Stretch"
+                        BorderBrush="LightGray"
+                        BorderThickness="1"
+                        CornerRadius="10"
+                        Margin="2">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                        </Grid.RowDefinitions>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                        </Grid.ColumnDefinitions>
+                        <TextBlock Grid.Row="0"
+                                   Grid.ColumnSpan="3"
+                                   Text="相机3轴控制"
+                                   FontWeight="Bold"
+                                   FontSize="14"
+                                   HorizontalAlignment="Center" />
+                        <TextBlock Grid.Row="1"
+                                   Grid.ColumnSpan="3"
+                                   VerticalAlignment="Center"
+                                   Margin="10,0">
+                            <TextBlock.Text>
+                                <MultiBinding StringFormat="{}X: {0:F3} mm Y: {1:F3} mm">
+                                    <Binding Path="Robot.Camera3CurrentPosition.X" />
+                                    <Binding Path="Robot.Camera3CurrentPosition.Y" />
+                                </MultiBinding>
+                            </TextBlock.Text>
+                        </TextBlock>
+                        <StackPanel Grid.Row="2"
+                                    Grid.ColumnSpan="3"
+                                    Orientation="Horizontal"
+                                    HorizontalAlignment="Center">
+                            <Button Content="{lex:Loc 上使能}"
+                                    Command="{Binding CameraAxisModerOnCommand}"
+                                    CommandParameter="3"
+                                    Margin="2,0" />
+                            <Button Content="{lex:Loc 下使能}"
+                                    Command="{Binding CameraAxisModerOffCommand}"
+                                    CommandParameter="3"
+                                    Margin="2,0" />
+                        </StackPanel>
+                        <Button Grid.Row="4"
+                                Grid.Column="0"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowLeftBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="3,X-" />
+                        <Button Grid.Row="4"
+                                Grid.Column="2"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowRightBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="3,X+" />
+                    </Grid>
+                </Border>
+            </Grid>-->
+
+            <!-- 附加轴列表:绑定到视图模型中的 ExtraAxes 集合(每项假定有 Name、Position) -->
+            <ItemsControl Grid.Row="2"
+                          ItemsSource="{Binding AdditionalAxisList}">
+                <ItemsControl.ItemTemplate>
+                    <DataTemplate>
+                        <Border Padding="6"
+                                Margin="0,2"
+                                Background="{DynamicResource MaterialDesignPaper}">
+                            <DockPanel LastChildFill="False"
+                                       VerticalAlignment="Center">
+                                <StackPanel Orientation="Horizontal"
+                                            DockPanel.Dock="Left"
+                                            VerticalAlignment="Center">
+                                    <TextBlock Text="{Binding Name}"
+                                               Width="140"
+                                               VerticalAlignment="Center" />
+                                    <TextBlock Text="{Binding CurrentPosition, StringFormat='{}{0:F3} mm'}"
+                                               Width="140"
+                                               Margin="10,0"
+                                               VerticalAlignment="Center" />
+                                </StackPanel>
+                                <StackPanel Orientation="Horizontal"
+                                            HorizontalAlignment="Right"
+                                            DockPanel.Dock="Right">
+                                    <Button Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisJogMinusCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}">
+                                        <StackPanel Orientation="Vertical">
+                                            <materialDesign:PackIcon Kind="ArrowLeftBold"
+                                                                     Width="26"
+                                                                     Height="26" />
+                                            <TextBlock Text="-"
+                                                       HorizontalAlignment="Center" />
+                                        </StackPanel>
+                                    </Button>
+                                    <Button Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisJogPlusCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}">
+                                        <StackPanel Orientation="Vertical">
+                                            <materialDesign:PackIcon Kind="ArrowRightBold"
+                                                                     Width="26"
+                                                                     Height="26" />
+                                            <TextBlock Text="+"
+                                                       HorizontalAlignment="Center" />
+                                        </StackPanel>
+                                    </Button>
+                                    <Button Content="上使能"
+                                            Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisMotorOnCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
+                                    <Button Content="下使能"
+                                            Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisMotorOffCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
+                                </StackPanel>
+                            </DockPanel>
+                        </Border>
+                    </DataTemplate>
+                </ItemsControl.ItemTemplate>
+            </ItemsControl>
+
+            <materialDesign:Snackbar Grid.RowSpan="2"
+                                     x:Name="snackbar"
+                                     Opacity="0.9"
+                                     HorizontalContentAlignment="Center"
+                                     VerticalAlignment="Top" />
+        </Grid>
+    </ScrollViewer>
+</UserControl>

+ 581 - 0
TeamAAS-VM/Controls/PlcRobotManual.xaml.cs

@@ -0,0 +1,581 @@
+using Prism.Commands;
+using Prism.Ioc;
+using Prism.Mvvm;
+using Prism.Unity;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using TeamAAS_VP.Core.PLCs;
+using TeamAAS_VP.Core.Robots;
+using TeamAAS_VP.Interfaces;
+using TeamAAS_VP.Resources.Languages;
+
+namespace TeamAAS_VP.Controls
+{
+    /// <summary>
+    /// PlcRobotManual.xaml 的交互逻辑
+    /// </summary>
+    public partial class PlcRobotManual : UserControl, INotifyPropertyChanged
+    {
+        IConfigService _configService;
+        public PlcRobotManual()
+        {
+            InitializeComponent();
+            DataContext = this;
+        }
+
+        #region 属性
+
+
+        private ObservableCollection<AxisExtended> _AdditionalAxisList;
+        public ObservableCollection<AxisExtended> AdditionalAxisList
+        {
+            get { return _AdditionalAxisList; }
+            set { SetProperty(ref _AdditionalAxisList, value); }
+        }
+
+        private XYZU_Robot _Robot;
+
+        public XYZU_Robot Robot
+        {
+            get { return _Robot; }
+            set
+            {
+                SetProperty(ref _Robot, value);
+                try
+                {
+                    if (value != null)
+                    {
+                        if (_configService == null)
+                            _configService = ((PrismApplication)(App.Current)).Container.Resolve<IConfigService>();
+                        this.Distance = _configService.GetRobotStepDistance(value.Id);
+                    }
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+
+        private double _Distance = 1;
+
+        public double Distance
+        {
+            get { return _Distance; }
+            set
+            {
+                SetProperty(ref _Distance, value);
+                try
+                {
+                    if (Robot == null)
+                    {
+                        return;
+                    }
+                    if (_configService == null)
+                        _configService = ((PrismApplication)(App.Current)).Container.Resolve<IConfigService>();
+                    _configService.UpdateRobotStepDistance(Robot.Id, value);
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+        #endregion
+
+        #region 命令
+
+        private DelegateCommand<string> _MotorCommand;
+        public DelegateCommand<string> MotorCommand =>
+            _MotorCommand ?? (_MotorCommand = new DelegateCommand<string>(ExecuteMotorCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+
+
+        private DelegateCommand _ResetCommand;
+        public DelegateCommand ResetCommand =>
+            _ResetCommand ?? (_ResetCommand = new DelegateCommand(ExecuteResetCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+
+
+        private DelegateCommand<object> _JogCommand;
+        public DelegateCommand<object> JogCommand =>
+            _JogCommand ?? (_JogCommand = new DelegateCommand<object>(ExecuteJogCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        //附加轴,轴点动+命令
+        private DelegateCommand<object> _AdditionalAxisJogPlusCommand;
+        public DelegateCommand<object> AdditionalAxisJogPlusCommand =>
+            _AdditionalAxisJogPlusCommand ?? (_AdditionalAxisJogPlusCommand = new DelegateCommand<object>(ExecuteAdditionalAxisJogPlusCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        //附加轴,轴点动-命令
+        private DelegateCommand<object> _AdditionalAxisJogMinusCommand;
+        public DelegateCommand<object> AdditionalAxisJogMinusCommand =>
+            _AdditionalAxisJogMinusCommand ?? (_AdditionalAxisJogMinusCommand = new DelegateCommand<object>(ExecuteAdditionalAxisJogMinusCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        private DelegateCommand<object> _AdditionalAxisMotorOnCommand;
+        public DelegateCommand<object> AdditionalAxisMotorOnCommand =>
+            _AdditionalAxisMotorOnCommand ?? (_AdditionalAxisMotorOnCommand = new DelegateCommand<object>(ExecuteAdditionalAxisMotorOnCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        private DelegateCommand<object> _AdditionalAxisMotorOffCommand;
+        public DelegateCommand<object> AdditionalAxisMotorOffCommand =>
+            _AdditionalAxisMotorOffCommand ?? (_AdditionalAxisMotorOffCommand = new DelegateCommand<object>(ExecuteAdditionalAxisMotorOffCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        //private DelegateCommand<string> _CameraAxisModerOnCommand;
+        //public DelegateCommand<string> CameraAxisModerOnCommand =>
+        //    _CameraAxisModerOnCommand ?? (_CameraAxisModerOnCommand = new DelegateCommand<string>(ExecuteCameraAxisModerOnCommand));
+
+        //private DelegateCommand<string> _CameraAxisModerOffCommand;
+        //public DelegateCommand<string> CameraAxisModerOffCommand =>
+        //    _CameraAxisModerOffCommand ?? (_CameraAxisModerOffCommand = new DelegateCommand<string>(ExecuteCameraAxisModerOffCommand));
+
+        //private DelegateCommand<string> _CameraAxisJogCommand;
+        //public DelegateCommand<string> CameraAxisJogCommand =>
+        //    _CameraAxisJogCommand ?? (_CameraAxisJogCommand = new DelegateCommand<string>(ExecuteCameraAxisJogCommand));
+        #endregion
+
+        #region 事件
+
+        #endregion
+
+        #region 方法
+
+        private void ShowMsg(string msg)
+        {
+            if (snackbar.MessageQueue == null)
+            {
+                snackbar.MessageQueue = new MaterialDesignThemes.Wpf.SnackbarMessageQueue();
+            }
+            snackbar.MessageQueue.Clear();
+            snackbar.MessageQueue?.Enqueue(
+                msg,
+                null,
+                null,
+                null,
+                false,
+                true,
+                TimeSpan.FromSeconds(0.4));
+        }
+
+        /// <summary>
+        /// 电机
+        /// </summary>
+        /// <param name="obj"></param>
+        private async void ExecuteMotorCommand(string obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                await Robot.MotorAsync(Convert.ToBoolean(obj));
+                ShowMsg($"{Lang.完成}!");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("机器人点击操作时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 重置
+        /// </summary>
+        private async void ExecuteResetCommand()
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                await Robot.ResetAsync();
+
+                ShowMsg($"{Lang.完成}!");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("重置机器人时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 点动
+        /// </summary>
+        /// <param name="obj"></param>
+        private async void ExecuteJogCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                var items = ((string)obj).Split(':');
+                //如果移动距离大于10mm,则提示用户确认
+                double distance = double.Parse(items[1]);
+                if (Math.Abs(distance) >= 10)
+                {
+                    if (MessageBox.Show($"确认要点动距离 {distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                    {
+                        return;
+                    }
+                }
+                Robot.Timeout = 6000000;
+                switch (items[0])
+                {
+                    case "X+":
+                        await Robot.JogAsync("X", distance);
+                        break;
+                    case "X-":
+                        await Robot.JogAsync("X", -distance);
+                        break;
+                    case "Y+":
+                        await Robot.JogAsync("Y", distance);
+                        break;
+                    case "Y-":
+                        await Robot.JogAsync("Y", -distance);
+                        break;
+                    case "Z+":
+                        await Robot.JogAsync("Z", distance);
+                        break;
+                    case "Z-":
+                        await Robot.JogAsync("Z", -distance);
+                        break;
+                    case "U+":
+                        await Robot.JogAsync("U", distance);
+                        break;
+                    case "U-":
+                        await Robot.JogAsync("U", -distance);
+                        break;
+                    case "V+":
+                        await Robot.JogAsync("V", distance);
+                        break;
+                    case "V-":
+                        await Robot.JogAsync("V", -distance);
+                        break;
+                    case "W+":
+                        await Robot.JogAsync("W", distance);
+                        break;
+                    case "W-":
+                        await Robot.JogAsync("W", -distance);
+                        break;
+                    default:
+                        break;
+                }
+                Robot.Timeout = 5000;
+
+
+                ShowMsg($"{Lang.完成}!");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("机器点动时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        private bool CanExecute
+        {
+            get
+            {
+                if (Robot != null)
+                {
+                    return Robot.CanExecute;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 附加轴+点动
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisJogPlusCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    //如果移动距离大于10mm,则提示用户确认
+                    if (Math.Abs(Distance) >= 10)
+                    {
+                        if (MessageBox.Show($"确认要点动距离 {Distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        {
+                            return;
+                        }
+                    }
+                    await Robot.AdditionalAxisJogAsync(axis.Name, Distance);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴点动+时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴-点动
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisJogMinusCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    //如果移动距离大于10mm,则提示用户确认
+                    if (Math.Abs(Distance) >= 10)
+                    {
+                        if (MessageBox.Show($"确认要点动距离 {Distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        {
+                            return;
+                        }
+                    }
+                    await Robot.AdditionalAxisJogNegAsync(axis.Name, Distance);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴点动-时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴上使能
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisMotorOffCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    await Robot.AdditionalAxisMotorAsync(axis.Name, true);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴上使能时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴下使能
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisMotorOnCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    await Robot.AdditionalAxisMotorAsync(axis.Name, true);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴下使能时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        #region 相机控制
+        ///// <summary>
+        ///// 轴电机下使能操作
+        ///// </summary>
+        ///// <param name="parameter"></param>
+        //async void ExecuteCameraAxisModerOffCommand(string parameter)
+        //{
+        //    try
+        //    {
+        //        int cameraIndex = int.Parse(parameter);
+        //        var isSuccess = await Robot.CameraMotorAsync(cameraIndex, false);
+        //        if (isSuccess)
+        //        {
+        //            ShowMsg($"{Lang.完成}!");
+        //        }
+        //        else
+        //        {
+        //            ShowMsg("相机轴下使能失败!");
+        //        }
+        //    }
+        //    catch (Exception ex)
+        //    {
+        //        LogHelper.WriteLogError("相机轴下使能时出错!", ex);
+        //        ShowMsg(ex.Message);
+        //    }
+        //}
+
+        ///// <summary>
+        ///// 轴电机上使能操作
+        ///// </summary>
+        ///// <param name="parameter"></param>
+        //async void ExecuteCameraAxisModerOnCommand(string parameter)
+        //{
+        //    try
+        //    {
+        //        int cameraIndex = int.Parse(parameter);
+        //        var isSuccess = await Robot.CameraMotorAsync(cameraIndex, true);
+        //        if (isSuccess)
+        //        {
+        //            ShowMsg($"{Lang.完成}!");
+        //        }
+        //        else
+        //        {
+        //            ShowMsg("相机轴上使能失败!");
+        //        }
+        //    }
+        //    catch (Exception ex)
+        //    {
+        //        LogHelper.WriteLogError("相机轴上使能时出错!", ex);
+        //        ShowMsg(ex.Message);
+        //    }
+        //}
+
+        ///// <summary>
+        ///// 相机轴点动命令
+        ///// </summary>
+        ///// <param name="parameter"></param>
+        //async void ExecuteCameraAxisJogCommand(string parameter)
+        //{
+        //    try
+        //    {
+        //        int cameraIndex = int.Parse(parameter.Split(',')[0]);
+        //        double jogDistance = Distance;
+        //        string axisName = "";
+        //        switch (parameter.Split(',')[1])
+        //        {
+        //            case "X+":
+        //                jogDistance = Distance;
+        //                axisName = "X";
+        //                break;
+        //            case "X-":
+        //                jogDistance = -Distance;
+        //                axisName = "X";
+        //                break;
+        //            case "Y+":
+        //                jogDistance = Distance;
+        //                axisName = "Y";
+        //                break;
+        //            case "Y-":
+        //                jogDistance = -Distance;
+        //                axisName = "Y";
+        //                break;
+        //            default:
+        //                break;
+        //        }
+        //        bool isSuccess = await Robot.CameraJogAsync(cameraIndex, axisName, jogDistance);
+        //        if (isSuccess)
+        //        {
+        //            ShowMsg($"{Lang.完成}!");
+        //        }
+        //        else
+        //        {
+        //            ShowMsg("相机轴点动失败!");
+        //        }
+        //    }
+        //    catch (Exception ex)
+        //    {
+        //        LogHelper.WriteLogError("相机轴点动时出错!", ex);
+        //        ShowMsg(ex.Message);
+        //    }
+        //}
+        #endregion
+
+        #endregion
+
+        #region 属性通知
+        /// <summary>
+        /// Occurs when a property value changes.
+        /// </summary>
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        /// <summary>
+        /// Checks if a property already matches a desired value. Sets the property and
+        /// notifies listeners only when necessary.
+        /// </summary>
+        /// <typeparam name="T">Type of the property.</typeparam>
+        /// <param name="storage">Reference to a property with both getter and setter.</param>
+        /// <param name="value">Desired value for the property.</param>
+        /// <param name="propertyName">Name of the property used to notify listeners. This
+        /// value is optional and can be provided automatically when invoked from compilers that
+        /// support CallerMemberName.</param>
+        /// <returns>True if the value was changed, false if the existing value matched the
+        /// desired value.</returns>
+        protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
+        {
+            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
+
+            storage = value;
+            RaisePropertyChanged(propertyName);
+
+            return true;
+        }
+
+        /// <summary>
+        /// Checks if a property already matches a desired value. Sets the property and
+        /// notifies listeners only when necessary.
+        /// </summary>
+        /// <typeparam name="T">Type of the property.</typeparam>
+        /// <param name="storage">Reference to a property with both getter and setter.</param>
+        /// <param name="value">Desired value for the property.</param>
+        /// <param name="propertyName">Name of the property used to notify listeners. This
+        /// value is optional and can be provided automatically when invoked from compilers that
+        /// support CallerMemberName.</param>
+        /// <param name="onChanged">Action that is called after the property value has been changed.</param>
+        /// <returns>True if the value was changed, false if the existing value matched the
+        /// desired value.</returns>
+        protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
+        {
+            if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
+
+            storage = value;
+            onChanged?.Invoke();
+            RaisePropertyChanged(propertyName);
+
+            return true;
+        }
+
+        /// <summary>
+        /// Raises this object's PropertyChanged event.
+        /// </summary>
+        /// <param name="propertyName">Name of the property used to notify listeners. This
+        /// value is optional and can be provided automatically when invoked from compilers
+        /// that support <see cref="CallerMemberNameAttribute"/>.</param>
+        protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
+        {
+            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
+        }
+
+        /// <summary>
+        /// Raises this object's PropertyChanged event.
+        /// </summary>
+        /// <param name="args">The PropertyChangedEventArgs</param>
+        protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
+        {
+            PropertyChanged?.Invoke(this, args);
+        }
+        #endregion
+    }
+}

+ 9 - 9
TeamAAS-VM/Core/Cameras/AHuaCamera.cs

@@ -26,7 +26,6 @@ using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
 using TeamAAS_VP;
 using static MVSDK_Net.IMVDefine;
-using TeamAAS_VP.Resources.Languages;
 
 namespace TeamAAS_VP.Core.Cameras
 {
@@ -111,7 +110,7 @@ namespace TeamAAS_VP.Core.Cameras
             var list = GetDevices();
             foreach (var device in list)
             {
-                if (device.CameraType== CameraType.GIGE)
+                if (device.CameraType == CameraType.GIGE)
                 {
                     if (IsFindByIp)
                     {
@@ -139,7 +138,7 @@ namespace TeamAAS_VP.Core.Cameras
                             ModelName = device.Model;
                             SerialNumber = device.SerialNumber;
                             CameraType = device.CameraType;
-                            CameraIp= device.CameraIp;
+                            CameraIp = device.CameraIp;
                             int res = mvCameraAcq.IMV_CreateHandle(IMVDefine.IMV_ECreateHandleMode.modeByCameraKey, 0, serialNumber);
                             if (res == IMVDefine.IMV_OK)
                             {
@@ -166,7 +165,7 @@ namespace TeamAAS_VP.Core.Cameras
                         break;
                     }
                 }
-                
+
             }
 
             IsConnected = false;
@@ -183,7 +182,7 @@ namespace TeamAAS_VP.Core.Cameras
             List<CameraInfo> cameras = new List<CameraInfo>();
             if (!CheckSoftwareInstalled().isInstalled)
             {
-                LogHelper.WriteLogInfo(Lang.未安装MVViewer);
+                LogHelper.WriteLogInfo("未安装MV Viewer");
                 return cameras.ToArray();
             }
             // 设备搜索 
@@ -220,7 +219,7 @@ namespace TeamAAS_VP.Core.Cameras
                             Model = deviceInfo.modelName,
                             SerialNumber = deviceInfo.serialNumber,
                             CameraIp = gigEDeviceInfo.ipAddress,
-                            IsFindByIp=true,
+                            IsFindByIp = true,
                         });
                     }
                     else if (deviceInfo.nCameraType == IMV_ECameraType.typeU3vCamera)
@@ -510,10 +509,11 @@ namespace TeamAAS_VP.Core.Cameras
         private IMVDefine.IMV_PixelConvertParam stPixelConvertParam = new IMVDefine.IMV_PixelConvertParam();
         private IMVDefine.IMV_EPixelType type = IMVDefine.IMV_EPixelType.gvspPixelMono8;
         /// <summary>
-        /// 采集图像
+        /// 采集单张图片
         /// </summary>
+        /// <param name="lastImageOnly">true:采集一张新图像,false:返回最后一张图像</param>
         /// <returns></returns>
-        public ICogImage Grab()
+        public ICogImage Grab(bool lastImageOnly = true)
         {
             EnterOperation();
             try
@@ -845,7 +845,7 @@ namespace TeamAAS_VP.Core.Cameras
             }
             IsGrabbing = false;
         }
-        
+
         /// <summary>
         /// 图像是否为Mono格式
         /// </summary>

+ 9 - 9
TeamAAS-VM/Core/Cameras/BaslerCamera.cs

@@ -20,7 +20,6 @@ using System.Windows.Media.Media3D;
 using MvCamCtrl.NET;
 using TeamAAS_VP.Models;
 using Microsoft.Win32;
-using TeamAAS_VP.Resources.Languages;
 //using System.Drawing;
 //using System.Drawing.Imaging;
 
@@ -187,7 +186,7 @@ namespace TeamAAS_VP.Core.Cameras
             {
                 if (!CheckSoftwareInstalled().isInstalled)
                 {
-                    LogHelper.WriteLogInfo(Lang.未安装pylon7);
+                    LogHelper.WriteLogInfo("未安装pylon 7");
                     return cameras.ToArray();
                 }
                 List<Basler.Pylon.ICameraInfo> allCameraInfos = Basler.Pylon.CameraFinder.Enumerate();
@@ -256,7 +255,7 @@ namespace TeamAAS_VP.Core.Cameras
             }
             catch (Exception ex)
             {
-                LogHelper.WriteLogError(Lang.搜索巴斯勒相机列表时出错, ex);
+                LogHelper.WriteLogError("搜索巴斯勒相机列表时出错!", ex);
             }
 
             return cameras.ToArray();
@@ -386,7 +385,7 @@ namespace TeamAAS_VP.Core.Cameras
                     }
                     if (m_MyCamera == null)
                     {
-                        throw new Exception(Lang.没有识别到相机);
+                        throw new Exception("没有识别到相机");
                     }
                 }
 
@@ -457,10 +456,11 @@ namespace TeamAAS_VP.Core.Cameras
         }
 
         /// <summary>
-        /// 采集图像
+        /// 采集单张图片
         /// </summary>
+        /// <param name="lastImageOnly">true:采集一张新图像,false:返回最后一张图像</param>
         /// <returns></returns>
-        public ICogImage Grab()
+        public ICogImage Grab(bool lastImageOnly = true)
         {
             EnterOperation();
             try
@@ -468,7 +468,7 @@ namespace TeamAAS_VP.Core.Cameras
                 Stopwatch sw = Stopwatch.StartNew();
                 if (m_MyCamera == null)
                 {
-                    throw new Exception(Lang.相机未连接);
+                    throw new Exception("相机未连接");
                 }
                 if (!m_MyCamera.IsOpen)
                 {
@@ -612,11 +612,11 @@ namespace TeamAAS_VP.Core.Cameras
             {
                 if (m_MyCamera == null)
                 {
-                    throw new Exception(Lang.相机未连接);
+                    throw new Exception("相机未连接");
                 }
                 if (!m_MyCamera.IsOpen)
                 {
-                    throw new Exception(Lang.相机未打开);
+                    throw new Exception("相机未打开");
                 }
 
                 m_MyCamera.Parameters[PLCamera.AcquisitionMode].SetValue(PLCamera.AcquisitionMode.Continuous);

+ 15 - 15
TeamAAS-VM/Core/Cameras/CognexCamera.cs

@@ -16,7 +16,6 @@ using TeamAAS_VP.Enums;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
 using TeamAAS_VP;
-using TeamAAS_VP.Resources.Languages;
 
 namespace TeamAAS_VP.Core.Cameras
 {
@@ -145,7 +144,7 @@ namespace TeamAAS_VP.Core.Cameras
             List<CameraInfo> cameras = new List<CameraInfo>();
             if (!CheckSoftwareInstalled().isInstalled)
             {
-                LogHelper.WriteLogInfo(Lang.未安装CognexVisionPro);
+                LogHelper.WriteLogInfo("未安装Cognex VisionPro");
                 return cameras.ToArray();
             }
             using (CogFrameGrabbers mFrameGrabbers = new CogFrameGrabbers())//识别已安装的帧抓取器的类//帧抓取器:相机
@@ -154,10 +153,10 @@ namespace TeamAAS_VP.Core.Cameras
                 foreach (ICogFrameGrabber item in mFrameGrabbers)
                 {
                     string[] bf1 = item.Name.Split(':');
-                    string manufacturerName=item.Name;
+                    string manufacturerName = item.Name;
                     string model = item.Name;
                     CameraType type = CameraType.GIGE;
-                    if (bf1.Length==3)
+                    if (bf1.Length == 3)
                     {
                         manufacturerName = bf1[1].Trim();
                         model = bf1[2].Trim();
@@ -174,7 +173,7 @@ namespace TeamAAS_VP.Core.Cameras
                             type = CameraType.Unknown;
                         }
                     }
-                    if (type== CameraType.GIGE)
+                    if (type == CameraType.GIGE)
                     {
                         cameras.Add(new Models.CameraInfo()
                         {
@@ -186,7 +185,7 @@ namespace TeamAAS_VP.Core.Cameras
                             Model = model,
                             SerialNumber = item.SerialNumber,
                             CameraIp = item.OwnedGigEAccess.CurrentIPAddress,
-                            IsFindByIp=true,
+                            IsFindByIp = true,
                         });
                     }
                     else
@@ -265,7 +264,7 @@ namespace TeamAAS_VP.Core.Cameras
             try
             {
                 IsConnected = false;
-                if (!IsHaveCamera || CameraAcq==null)
+                if (!IsHaveCamera || CameraAcq == null)
                 {
                     using (CogFrameGrabbers mFrameGrabbers = new CogFrameGrabbers())//识别已安装的帧抓取器的类//帧抓取器:相机
                     {
@@ -288,11 +287,11 @@ namespace TeamAAS_VP.Core.Cameras
                                     type = CameraType.Unknown;
                                 }
                             }
-                            if (type== CameraType.GIGE)
+                            if (type == CameraType.GIGE)
                             {
                                 if (IsFindByIp)
                                 {
-                                    if (CameraIp== item.OwnedGigEAccess.CurrentIPAddress)
+                                    if (CameraIp == item.OwnedGigEAccess.CurrentIPAddress)
                                     {
                                         var videoFormat = item.AvailableVideoFormats[0];
 
@@ -445,7 +444,7 @@ namespace TeamAAS_VP.Core.Cameras
             EnterOperation();
             try
             {
-                if (CameraAcq!=null)
+                if (CameraAcq != null)
                 {
                     CameraAcq = null;
 
@@ -459,10 +458,11 @@ namespace TeamAAS_VP.Core.Cameras
         }
 
         /// <summary>
-        /// 采集图像
+        /// 采集单张图片
         /// </summary>
+        /// <param name="lastImageOnly">true:采集一张新图像,false:返回最后一张图像</param>
         /// <returns></returns>
-        public ICogImage Grab()
+        public ICogImage Grab(bool lastImageOnly = true)
         {
             EnterOperation();
             try
@@ -478,7 +478,7 @@ namespace TeamAAS_VP.Core.Cameras
                         }
 
                         CameraAcq.Flush();
-                        Image= CameraAcq.CompleteAcquireEx(new CogAcqInfo());
+                        Image = CameraAcq.CompleteAcquireEx(new CogAcqInfo());
                         ImageWidth = (uint)Image.Width;
                         ImageHeight = (uint)Image.Height;
                         TotalTime = sw.Elapsed;
@@ -555,7 +555,7 @@ namespace TeamAAS_VP.Core.Cameras
             EnterOperation();
             try
             {
-                if (CameraAcq==null)
+                if (CameraAcq == null)
                     return false;
                 CameraAcq.OwnedExposureParams.Exposure = (double)ExposureTime;
                 return true;
@@ -595,7 +595,7 @@ namespace TeamAAS_VP.Core.Cameras
             EnterOperation();
             try
             {
-                if (CameraAcq==null)
+                if (CameraAcq == null)
                     return false;
                 // 设置增益 
                 CameraAcq.OwnedBrightnessParams.Brightness = (double)Gain;

+ 10 - 6
TeamAAS-VM/Core/Cameras/GalaxyCamera.cs

@@ -18,7 +18,6 @@ using TeamAAS_VP.Core.GalaxyMethod;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
-using TeamAAS_VP.Resources.Languages;
 
 namespace TeamAAS_VP.Core.Cameras
 {
@@ -106,7 +105,7 @@ namespace TeamAAS_VP.Core.Cameras
             var list = GetDevices();
             foreach (var device in list)
             {
-                if (device.CameraType== CameraType.GIGE)
+                if (device.CameraType == CameraType.GIGE)
                 {
                     if (IsFindByIp)
                     {
@@ -161,7 +160,7 @@ namespace TeamAAS_VP.Core.Cameras
             List<CameraInfo> cameras = new List<CameraInfo>();
             if (!CheckSoftwareInstalled().isInstalled)
             {
-                LogHelper.WriteLogInfo(Lang.未安装GalaxyViewer);
+                LogHelper.WriteLogInfo("未安装Galaxy Viewer");
                 return cameras.ToArray();
             }
             m_listIGXDeviceInfo.Clear();
@@ -172,7 +171,7 @@ namespace TeamAAS_VP.Core.Cameras
             // 判断当前连接设备个数
             if (m_listIGXDeviceInfo.Count <= 0)
             {
-                LogHelper.WriteLogInfo(Lang.未检测到设备请确保设备正常连接然后重启程序);
+                LogHelper.WriteLogInfo("未检测到设备,请确保设备正常连接然后重启程序!");
                 return cameras.ToArray();
             }
             for (int i = 0; i < m_listIGXDeviceInfo.Count; i++)
@@ -190,7 +189,7 @@ namespace TeamAAS_VP.Core.Cameras
                         Model = m_listIGXDeviceInfo[i].GetModelName(),
                         SerialNumber = m_listIGXDeviceInfo[i].GetSN(),
                         CameraIp = m_listIGXDeviceInfo[i].GetIP(),
-                        IsFindByIp=true,
+                        IsFindByIp = true,
                     });
                 }
                 else if (deviceType == GX_DEVICE_CLASS_LIST.GX_DEVICE_CLASS_USB2 || deviceType == GX_DEVICE_CLASS_LIST.GX_DEVICE_CLASS_U3V)
@@ -359,7 +358,12 @@ namespace TeamAAS_VP.Core.Cameras
                 ExitOperation();
             }
         }
-        public ICogImage Grab()
+        /// <summary>
+        /// 采集单张图片
+        /// </summary>
+        /// <param name="lastImageOnly">true:采集一张新图像,false:返回最后一张图像</param>
+        /// <returns></returns>
+        public ICogImage Grab(bool lastImageOnly = true)
         {
             EnterOperation();
             try

+ 15 - 15
TeamAAS-VM/Core/Cameras/MvCamera.cs

@@ -26,7 +26,6 @@ using TeamAAS_VP;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
-using TeamAAS_VP.Resources.Languages;
 
 namespace TeamAAS_VP.Core.Cameras
 {
@@ -49,7 +48,7 @@ namespace TeamAAS_VP.Core.Cameras
         public int Index { get; set; }
         public string ManufacturerName { get; private set; }
 
-        public bool IsFindByIp {  get; private set; }
+        public bool IsFindByIp { get; private set; }
 
         public string ModelName { get; private set; }
 
@@ -108,14 +107,14 @@ namespace TeamAAS_VP.Core.Cameras
         public event Action<Guid, bool> CameraConnectChangedEvent;
         #endregion
 
-        public MvCamera(Guid id,int index, string name, string serialNumber,string cameraip,bool isFindByIp)
+        public MvCamera(Guid id, int index, string name, string serialNumber, string cameraip, bool isFindByIp)
         {
             ID = id;
             Name = serialNumber;
             Index = index;
-            CameraIp=cameraip;
+            CameraIp = cameraip;
             SerialNumber = serialNumber;
-            IsFindByIp= isFindByIp;
+            IsFindByIp = isFindByIp;
             mvCameraAcq = new MvCameraAcqTool.MvCameraAcqTool();
             MyCamera.MV_CC_DEVICE_INFO_LIST stDeviceList = default(MyCamera.MV_CC_DEVICE_INFO_LIST);
             mvCameraAcq.EnumDeivce(ref stDeviceList);
@@ -130,8 +129,8 @@ namespace TeamAAS_VP.Core.Cameras
                     uint nIp2 = ((stGigEDeviceInfo.nCurrentIp & 0x00ff0000) >> 16);
                     uint nIp3 = ((stGigEDeviceInfo.nCurrentIp & 0x0000ff00) >> 8);
                     uint nIp4 = (stGigEDeviceInfo.nCurrentIp & 0x000000ff);
-                    string ip= string.Format("{0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
-                    
+                    string ip = string.Format("{0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
+
                     if (IsFindByIp)
                     {
                         if (CameraIp == ip)
@@ -190,7 +189,7 @@ namespace TeamAAS_VP.Core.Cameras
             {
                 if (!CheckSoftwareInstalled().isInstalled)
                 {
-                    LogHelper.WriteLogInfo(Lang.未安装MVS);
+                    LogHelper.WriteLogInfo("未安装MVS");
                     return cameras.ToArray();
                 }
                 List<CCameraInfo> m_ltDeviceList = new List<CCameraInfo>();
@@ -211,7 +210,7 @@ namespace TeamAAS_VP.Core.Cameras
                         string ip = string.Format("{0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
                         cameras.Add(new CameraInfo()
                         {
-                            CameraNo= i + 1,
+                            CameraNo = i + 1,
                             CameraName = gigeInfo.UserDefinedName,
                             CameraBrand = CameraBrand.HikRobot_MVS,
                             CameraType = CameraType.GIGE,
@@ -219,7 +218,7 @@ namespace TeamAAS_VP.Core.Cameras
                             Model = gigeInfo.chModelName,
                             SerialNumber = gigeInfo.chSerialNumber,
                             CameraIp = ip,
-                            IsFindByIp=true,
+                            IsFindByIp = true,
                         });
                     }
                     else if (m_ltDeviceList[i].nTLayerType == CSystem.MV_USB_DEVICE)
@@ -256,7 +255,7 @@ namespace TeamAAS_VP.Core.Cameras
             }
             catch (Exception ex)
             {
-                LogHelper.WriteLogError(Lang.搜索海康相机列表时出错, ex);
+                LogHelper.WriteLogError("搜索海康相机列表时出错!", ex);
             }
 
             return cameras.ToArray();
@@ -286,7 +285,7 @@ namespace TeamAAS_VP.Core.Cameras
                         using (RegistryKey subKey = key.OpenSubKey(subKeyName))
                         {
                             string displayName = subKey.GetValue("DisplayName")?.ToString();
-                            if (displayName != null && displayName.ToUpper().Trim()==softwareName.ToUpper())
+                            if (displayName != null && displayName.ToUpper().Trim() == softwareName.ToUpper())
                             {
                                 string version = subKey.GetValue("DisplayVersion")?.ToString();
                                 Version v1 = new Version(version);
@@ -379,7 +378,7 @@ namespace TeamAAS_VP.Core.Cameras
                     }
                     if (!IsHaveCamera)
                     {
-                        throw new Exception(Lang.没有发现相机);
+                        throw new Exception("没有发现相机");
                     }
                 }
 
@@ -429,10 +428,11 @@ namespace TeamAAS_VP.Core.Cameras
         }
 
         /// <summary>
-        /// 采集图像
+        /// 采集单张图片
         /// </summary>
+        /// <param name="lastImageOnly">true:采集一张新图像,false:返回最后一张图像</param>
         /// <returns></returns>
-        public ICogImage Grab()
+        public ICogImage Grab(bool lastImageOnly = true)
         {
             _operationSemaphore.Wait();
             try

+ 33 - 9
TeamAAS-VM/Core/Cameras/OptCamera.cs

@@ -18,7 +18,6 @@ using System.Threading.Tasks;
 using TeamAAS_VP.Enums;
 using TeamAAS_VP.Interfaces;
 using TeamAAS_VP.Models;
-using TeamAAS_VP.Resources.Languages;
 
 namespace TeamAAS_VP.Core.Cameras
 {
@@ -93,6 +92,10 @@ namespace TeamAAS_VP.Core.Cameras
         /// 错误信息
         /// </summary>
         public string ErrorMessage { get; private set; }
+
+        //当前的图像采集模式
+        public SciCam.SciCamGrabStrategy CurrentGrabStrategy { get; private set; } = SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_Latest;
+
         #endregion
 
         #region 事件
@@ -176,7 +179,7 @@ namespace TeamAAS_VP.Core.Cameras
             {
                 if (!CheckSoftwareInstalled().isInstalled)
                 {
-                    LogHelper.WriteLogInfo(Lang.未安装OPT);
+                    LogHelper.WriteLogInfo("未安装OPT");
                     return cameras.ToArray();
                 }
 
@@ -218,7 +221,7 @@ namespace TeamAAS_VP.Core.Cameras
             }
             catch (Exception ex)
             {
-                LogHelper.WriteLogError(Lang.搜索OPT相机列表时出错, ex);
+                LogHelper.WriteLogError("搜索OPT相机列表时出错!", ex);
             }
 
             return cameras.ToArray();
@@ -336,7 +339,7 @@ namespace TeamAAS_VP.Core.Cameras
                     }
                     if (!IsHaveCamera)
                     {
-                        throw new Exception(Lang.没有发现相机);
+                        throw new Exception("没有发现相机");
                     }
                 }
                 SciCam.SCI_DEVICE_INFO sCI_DEVICE_INFO = CameraInfo;
@@ -354,7 +357,7 @@ namespace TeamAAS_VP.Core.Cameras
                 // ch:注册事件回调 | en:Register event callback
                 //nReVal = mvCameraAcq.RegisterPayloadCallBack(new SciCam.fnOnPayloadDelegate(ImageCallBack), IntPtr.Zero, true);
                 // ch:设置采集策略 | en:Set Grab Strategy
-                nReVal = mvCameraAcq.SetGrabStrategy(SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_Latest);
+                nReVal = mvCameraAcq.SetGrabStrategy(CurrentGrabStrategy);
                 if (nReVal != SciCam.SCI_CAMERA_OK)
                 {
                     Console.WriteLine("Register Event CallBack fail! nRet {0}", nReVal);
@@ -408,10 +411,11 @@ namespace TeamAAS_VP.Core.Cameras
         }
 
         /// <summary>
-        /// 采集图像
+        /// 采集单张图片
         /// </summary>
+        /// <param name="lastImageOnly">true:采集一张新图像,false:返回最后一张图像</param>
         /// <returns></returns>
-        public ICogImage Grab()
+        public ICogImage Grab(bool lastImageOnly = true)
         {
             //_operationSemaphore.Wait();
             uint nReVal = SciCam.SCI_CAMERA_OK;
@@ -427,6 +431,26 @@ namespace TeamAAS_VP.Core.Cameras
                         {
                             OpenDevice();
                         }
+
+                        //如果仅仅采集最后一帧,则清空缓存
+                        if (lastImageOnly)
+                        {
+                            //判断当前的模式是否为最新模式,如果不是则设置为最新模式
+                            if (CurrentGrabStrategy != SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_OneByOne)
+                            {
+                                CurrentGrabStrategy = SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_OneByOne;
+                                nReVal = mvCameraAcq.SetGrabStrategy(CurrentGrabStrategy);
+                            }
+                        }
+                        else
+                        {
+                            //判断当前的模式是否为顺序模式,如果不是则设置为顺序模式
+                            if (CurrentGrabStrategy != SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_Latest)
+                            {
+                                CurrentGrabStrategy = SciCam.SciCamGrabStrategy.SciCam_GrabStrategy_Latest;
+                                nReVal = mvCameraAcq.SetGrabStrategy(CurrentGrabStrategy);
+                            }
+                        }
                         //if (!mvCameraAcq.IsGrabbing())
                         //{
                         //    mvCameraAcq.StartGrabbing();
@@ -452,7 +476,7 @@ namespace TeamAAS_VP.Core.Cameras
                             //_operationSemaphore.Release();
                             CloseDevice();
                             OpenDevice();
-                            ErrorMessage = Lang.图像采集失败;
+                            ErrorMessage = "图像采集失败!";
                         }
                     }
                     catch (Exception ex)
@@ -670,7 +694,7 @@ namespace TeamAAS_VP.Core.Cameras
             {
                 try
                 {
-                    Grab();
+                    Grab(false);
                     ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
                 }
                 catch (Exception)

+ 3 - 4
TeamAAS-VM/Core/FilePath.cs

@@ -67,14 +67,13 @@ namespace TeamAAS_VP.Core
         /// </summary>
         public static string TorqueCheckPath = "..//Config//TorqueCheckParam.cfg";
 
-        /// <summary>
-        /// 扭力测量仪路径
-        /// </summary>
-        public static string SignalConfigPath { get; set; } = "..//Config//扭力测量仪配置文件.json";
         /// <summary>
         /// 生产排班参数路径
         /// </summary>
 
         public static readonly string WorkShiftParamPath = "..//Log//WorkShiftParam.cfg";
+
+        // 电批配置路径
+        public static readonly string TorqueReaderConfigurationPath = "..//Config//TorqueReaderConfiguration.cfg";
     }
 }

+ 12 - 0
TeamAAS-VM/Core/Lights/KCSLightController.cs

@@ -176,6 +176,14 @@ namespace TeamAAS_VP.Core.Lights
                     return brightness;
                 }
             }
+            else if (response.Length == 7)
+            {
+                string valueStr = response.Substring(3, 3);
+                if (int.TryParse(valueStr, out int brightness))
+                {
+                    return brightness;
+                }
+            }
             // 返回默认亮度 0(表示读取失败或设备返回异常)
             return 0;
         }
@@ -192,6 +200,10 @@ namespace TeamAAS_VP.Core.Lights
         {
             string command = $"S{(char)('A' + channelIndex)}0{brightness.ToString("D3")}#";
             var response = await SendCommandAsync(command);
+            if (response=="!")
+            {
+                return true;
+            }
             // 成功时返回通道字母(例如 'A'),去除空白后比较
             return response?.Trim() == ((char)('A' + channelIndex)).ToString();
         }

+ 20 - 11
TeamAAS-VM/Core/Management.cs

@@ -54,6 +54,7 @@ using TeamAAS_VP.Models;
 using TeamAAS_VP.Models.Calibration;
 using TeamAAS_VP.Models.Feeder;
 using TeamAAS_VP.Models.Product;
+using TeamAAS_VP.Models.Torque;
 using TeamAAS_VP.Resources.Languages;
 using TeamAAS_VP.Services;
 using TeamAAS_VP.ViewModels.Home;
@@ -680,12 +681,16 @@ namespace TeamAAS_VP.Core
         /// <returns></returns>
         public async Task InitSignal()
         {
-            if (File.Exists(FilePath.SignalConfigPath))
+
+            var torqueConfig = _configService.GetTorqueReaderConfig();
+
+            if (torqueConfig!=null)
             {
                 try
-                {
-                    var port = FileHelper.ReadJsonFile<string>(FilePath.SignalConfigPath);
-                    if (port != null)
+                {                    
+                    var port = FileHelper.ReadJsonFile<TorqueProduceModel>(FilePath.TorqueReaderConfigurationPath);
+                    if (string.IsNullOrEmpty(port.TorqueMeasureSerialPort))
+                    
                     {
                         client = new TouchSocket.SerialPorts.SerialPortClient();
                         client.Connecting = (client1, e) => { return EasyTask.CompletedTask; };//即将连接到端口
@@ -702,17 +707,21 @@ namespace TeamAAS_VP.Core
                         await client.SetupAsync(new TouchSocketConfig()
                              .SetSerialPortOption(new SerialPortOption()
                              {
-                                 BaudRate = 115200,//波特率
-                                 DataBits = 8,//数据位
-                                 Parity = System.IO.Ports.Parity.None,//校验位
-                                 PortName = port,//COM
-                                 StopBits = System.IO.Ports.StopBits.One//停止位
+                                 BaudRate = torqueConfig.BaudRate,//波特率
+                                 DataBits = torqueConfig.DataBits,//数据位
+                                 Parity = torqueConfig.Parity,//校验位
+                                 PortName = torqueConfig.PortName,//COM
+                                 StopBits = torqueConfig.StopBits//停止位
                              })
                              .SetSerialDataHandlingAdapter(() => new PeriodPackageAdapter() { CacheTimeout = TimeSpan.FromMilliseconds(100) })
                              );
 
                         await client.ConnectAsync();
                     }
+                    else
+                    {
+                        SendTaskMessage("请设置扭力测量仪串口!" , MessageLevel.Error);
+                    }
                 }
                 catch (Exception ex)
                 {
@@ -722,8 +731,8 @@ namespace TeamAAS_VP.Core
             }
             else
             {
-                COM = "COM1";
-                FileHelper.WriteJsonFile(COM, FilePath.SignalConfigPath);
+                TorqueSerialPortConfig config = new TorqueSerialPortConfig();
+                FileHelper.WriteJsonFile(config,FilePath.TorqueReaderConfigurationPath);
             }
         }
         #endregion

+ 414 - 0
TeamAAS-VM/Core/RectangleCenterCalculator.cs

@@ -0,0 +1,414 @@
+using MathNet.Numerics.LinearAlgebra;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TeamAAS_VP.Core
+{
+    /// <summary>
+    /// 提供多种方法用于计算由四个角点定义的矩形中心点、长轴角度以及相关矩形参数的辅助类。
+    /// 角点顺序约定:左上(0) → 右上(1) → 右下(2) → 左下(3)
+    /// 长轴方向:从左上角(0)指向右上角(1)
+    /// 角度范围:[-180, 180) 度,0°指向X轴正方向,逆时针为正
+    /// </summary>
+    public class RectangleCenterCalculator
+    {
+        /// <summary>
+        /// 矩形中心及方向信息的综合结果
+        /// </summary>
+        public class RectangleResult
+        {
+            /// <summary>
+            /// 矩形中心点坐标
+            /// </summary>
+            public Vector<double> Center { get; set; }
+
+            /// <summary>
+            /// 矩形长轴方向向量(从左上指向右上的单位向量)
+            /// </summary>
+            public Vector<double> MajorAxis { get; set; }
+
+            /// <summary>
+            /// 矩形短轴方向向量(单位向量)
+            /// </summary>
+            public Vector<double> MinorAxis { get; set; }
+
+            /// <summary>
+            /// 矩形长度(沿长轴方向,左上到右上的距离)
+            /// </summary>
+            public double Length { get; set; }
+
+            /// <summary>
+            /// 矩形宽度(沿短轴方向)
+            /// </summary>
+            public double Width { get; set; }
+
+            /// <summary>
+            /// 长轴方向角度[-180, 180)度
+            /// 从左上指向右上的向量在XY平面的投影角度
+            /// 0° = 指向X轴正方向,正角度逆时针,负角度顺时针
+            /// </summary>
+            public double MajorAxisAngle { get; set; }
+
+            /// <summary>
+            /// 四个角点的坐标(保持输入顺序)
+            /// </summary>
+            public List<Vector<double>> Corners { get; set; }
+
+            /// <summary>
+            /// 左上角点坐标
+            /// </summary>
+            public Vector<double> TopLeft => Corners != null && Corners.Count >= 1 ? Corners[0] : null;
+
+            /// <summary>
+            /// 右上角点坐标
+            /// </summary>
+            public Vector<double> TopRight => Corners != null && Corners.Count >= 2 ? Corners[1] : null;
+
+            /// <summary>
+            /// 右下角点坐标
+            /// </summary>
+            public Vector<double> BottomRight => Corners != null && Corners.Count >= 3 ? Corners[2] : null;
+
+            /// <summary>
+            /// 左下角点坐标
+            /// </summary>
+            public Vector<double> BottomLeft => Corners != null && Corners.Count >= 4 ? Corners[3] : null;
+
+            /// <summary>
+            /// 矩形的面积
+            /// </summary>
+            public double Area { get; set; }
+
+            /// <summary>
+            /// 拟合误差(均方根误差)
+            /// </summary>
+            public double FitError { get; set; }
+        }
+
+        /// <summary>
+        /// 验证角点顺序并进行必要的调整
+        /// 确保顺序为:左上 → 右上 → 右下 → 左下
+        /// </summary>
+        private static List<Vector<double>> ValidateAndOrderCorners(List<Vector<double>> corners)
+        {
+            if (corners == null || corners.Count != 4)
+                throw new ArgumentException("需要4个角点");
+
+            // 这里可以添加自动排序逻辑,但既然您已知顺序,直接返回
+            // 如果未来需要自动排序,可以使用以下逻辑:
+            // 1. 找到最左边的两个点作为左边界
+            // 2. 根据Y坐标区分左上和左下
+            // 3. 计算中心点,确定其他点位置
+
+            return corners; // 假设输入已经按正确顺序
+        }
+
+        /// <summary>
+        /// 计算从左上指向右上的长轴方向角度
+        /// </summary>
+        private static double CalculateMajorAxisAngle(Vector<double> topLeft, Vector<double> topRight)
+        {
+            // 计算方向向量(从左上指向右上)
+            Vector<double> direction = topRight - topLeft;
+
+            // 只考虑XY平面上的投影
+            double x = direction[0];
+            double y = direction[1];
+
+            // 使用Math.Atan2计算角度(弧度)
+            double angleRad = Math.Atan2(y, x);
+
+            // 转换为度,得到有符号角度 [-180, 180)
+            double angleDeg = angleRad * 180.0 / Math.PI;
+
+            // Math.Atan2的结果已经是[-180, 180],但确保一下边界
+            if (angleDeg >= 180.0)
+                angleDeg -= 360.0;
+            else if (angleDeg < -180.0)
+                angleDeg += 360.0;
+
+            return angleDeg;
+        }
+
+        /// <summary>
+        /// 使用最小二乘法思想计算最优矩形中心点及相关参数(对外统一入口)
+        /// 推荐使用改进的PCA方法,因为它能利用所有点信息进行优化
+        /// </summary>
+        public static RectangleResult CalculateOptimalCenter(List<Vector<double>> corners)
+        {
+            // 验证和调整角点顺序
+            var orderedCorners = ValidateAndOrderCorners(corners);
+
+            // 使用改进的PCA方法(考虑已知顺序)
+            return CalculateByPCAWithKnownOrder(orderedCorners);
+        }
+
+        /// <summary>
+        /// 方法1:基于已知角点顺序的直接计算(最准确)
+        /// 直接使用左上和右上点计算长轴,然后计算其他参数
+        /// </summary>
+        public static RectangleResult CalculateByDirectMethod(List<Vector<double>> corners)
+        {
+            var orderedCorners = ValidateAndOrderCorners(corners);
+
+            var topLeft = orderedCorners[0];
+            var topRight = orderedCorners[1];
+            var bottomRight = orderedCorners[2];
+            var bottomLeft = orderedCorners[3];
+
+            // 计算长轴(从左上指向右上)
+            Vector<double> majorAxis = (topRight - topLeft).Normalize(2);
+
+            // 计算短轴(从左上指向左下)
+            Vector<double> minorAxis = (bottomLeft - topLeft).Normalize(2);
+
+            // 确保短轴与长轴垂直(处理非正交情况)
+            minorAxis = (minorAxis - majorAxis * minorAxis.DotProduct(majorAxis)).Normalize(2);
+
+            // 计算中心点(四个角点的平均)
+            var center = Vector<double>.Build.DenseOfEnumerable(
+                Enumerable.Range(0, 3)
+                    .Select(i => orderedCorners.Select(c => c[i]).Average()));
+
+            // 计算长度和宽度
+            double length = Distance(topLeft, topRight);
+            double width = Distance(topLeft, bottomLeft);
+
+            // 计算长轴角度
+            double angle = CalculateMajorAxisAngle(topLeft, topRight);
+
+            // 计算拟合误差
+            double fitError = CalculateFitErrorDirect(center, majorAxis, minorAxis, length, width, orderedCorners);
+
+            return new RectangleResult
+            {
+                Center = center,
+                MajorAxis = majorAxis,
+                MinorAxis = minorAxis,
+                Length = length,
+                Width = width,
+                MajorAxisAngle = angle,
+                Corners = orderedCorners,
+                Area = length * width,
+                FitError = fitError
+            };
+        }
+
+        /// <summary>
+        /// 方法2:使用PCA但考虑已知角点顺序(推荐)
+        /// 结合PCA的鲁棒性和已知顺序的准确性
+        /// </summary>
+        public static RectangleResult CalculateByPCAWithKnownOrder(List<Vector<double>> corners)
+        {
+            var orderedCorners = ValidateAndOrderCorners(corners);
+
+            // 步骤1:使用所有点进行PCA得到初步估计
+            var matrix = Matrix<double>.Build.DenseOfRows(orderedCorners);
+            var mean = Vector<double>.Build.DenseOfEnumerable(
+                Enumerable.Range(0, 3).Select(i => matrix.Column(i).Average()));
+
+            var centered = matrix.Clone();
+            for (int i = 0; i < 4; i++)
+            {
+                centered.SetRow(i, centered.Row(i) - mean);
+            }
+
+            var covariance = centered.Transpose() * centered / 3.0;
+            var evd = covariance.Evd();
+
+            // 获取特征向量
+            var eigenvectors = evd.EigenVectors;
+
+            // 步骤2:使用已知顺序确定长轴方向
+            var topLeft = orderedCorners[0];
+            var topRight = orderedCorners[1];
+
+            // 计算实际的长轴方向(从左上指向右上)
+            Vector<double> actualMajorDirection = (topRight - topLeft).Normalize(2);
+
+            // 从PCA的特征向量中找到最接近实际方向的那个
+            Vector<double> bestAxis = eigenvectors.Column(0);
+            double bestSimilarity = Math.Abs(actualMajorDirection.DotProduct(bestAxis));
+
+            for (int i = 1; i < 3; i++)
+            {
+                var axis = eigenvectors.Column(i);
+                double similarity = Math.Abs(actualMajorDirection.DotProduct(axis));
+                if (similarity > bestSimilarity)
+                {
+                    bestSimilarity = similarity;
+                    bestAxis = axis;
+                }
+            }
+
+            // 确保PCA轴的方向与实际方向一致(点积为正)
+            Vector<double> majorAxis = bestAxis;
+            if (actualMajorDirection.DotProduct(majorAxis) < 0)
+            {
+                majorAxis = -majorAxis;
+            }
+
+            // 计算短轴(与长轴垂直)
+            Vector<double> minorAxis = CalculatePerpendicularAxis(majorAxis);
+
+            // 将点投影到轴上计算中心、长度和宽度
+            var projMajor = orderedCorners.Select(p => (p - mean).DotProduct(majorAxis)).ToList();
+            var projMinor = orderedCorners.Select(p => (p - mean).DotProduct(minorAxis)).ToList();
+
+            double centerMajor = (projMajor.Min() + projMajor.Max()) / 2.0;
+            double centerMinor = (projMinor.Min() + projMinor.Max()) / 2.0;
+            var center = mean + majorAxis * centerMajor + minorAxis * centerMinor;
+
+            double length = Math.Abs(projMajor.Max() - projMajor.Min());
+            double width = Math.Abs(projMinor.Max() - projMinor.Min());
+
+            // 计算长轴角度(使用实际的左上-右上方向)
+            double angle = CalculateMajorAxisAngle(topLeft, topRight);
+
+            // 计算拟合误差
+            double fitError = CalculateFitError(center, majorAxis, minorAxis, length, width, orderedCorners);
+
+            return new RectangleResult
+            {
+                Center = center,
+                MajorAxis = majorAxis,
+                MinorAxis = minorAxis,
+                Length = length,
+                Width = width,
+                MajorAxisAngle = angle,
+                Corners = orderedCorners,
+                Area = length * width,
+                FitError = fitError
+            };
+        }
+
+        /// <summary>
+        /// 方法3:对角线优化方法(考虑已知顺序)
+        /// </summary>
+        public static RectangleResult CalculateByDiagonalOptimization(List<Vector<double>> corners)
+        {
+            var orderedCorners = ValidateAndOrderCorners(corners);
+            var topLeft = orderedCorners[0];
+            var topRight = orderedCorners[1];
+
+            // 直接使用已知顺序计算长轴角度
+            double angle = CalculateMajorAxisAngle(topLeft, topRight);
+
+            // 计算长轴方向向量
+            Vector<double> majorAxis = (topRight - topLeft).Normalize(2);
+            Vector<double> minorAxis = CalculatePerpendicularAxis(majorAxis);
+
+            // 使用所有对角线组合计算最佳中心
+            var diagonalCombinations = new[]
+            {
+            (orderedCorners[0], orderedCorners[2]), // 左上-右下
+            (orderedCorners[1], orderedCorners[3])  // 右上-左下
+        };
+
+            var centers = diagonalCombinations.Select(p => (p.Item1 + p.Item2) / 2.0).ToList();
+            var center = (centers[0] + centers[1]) / 2.0;
+
+            // 计算长度和宽度
+            double length = Distance(topLeft, topRight);
+            double width = Distance(topLeft, orderedCorners[3]); // 左上-左下
+
+            // 计算拟合误差
+            double fitError = CalculateFitError(center, majorAxis, minorAxis, length, width, orderedCorners);
+
+            return new RectangleResult
+            {
+                Center = center,
+                MajorAxis = majorAxis,
+                MinorAxis = minorAxis,
+                Length = length,
+                Width = width,
+                MajorAxisAngle = angle,
+                Corners = orderedCorners,
+                Area = length * width,
+                FitError = fitError
+            };
+        }
+
+        /// <summary>
+        /// 辅助方法:计算与给定向量垂直的单位向量
+        /// </summary>
+        private static Vector<double> CalculatePerpendicularAxis(Vector<double> axis)
+        {
+            // 找一个不与axis共线的向量
+            Vector<double> temp;
+            if (Math.Abs(axis[0]) < 0.9)
+                temp = Vector<double>.Build.Dense(new[] { 1.0, 0.0, 0.0 });
+            else
+                temp = Vector<double>.Build.Dense(new[] { 0.0, 1.0, 0.0 });
+
+            var perpendicular = temp - axis * temp.DotProduct(axis);
+            return perpendicular.Normalize(2);
+        }
+
+        /// <summary>
+        /// 辅助方法:计算两点距离
+        /// </summary>
+        private static double Distance(Vector<double> a, Vector<double> b)
+        {
+            return (a - b).L2Norm();
+        }
+
+        /// <summary>
+        /// 辅助方法:计算拟合误差
+        /// </summary>
+        private static double CalculateFitError(Vector<double> center, Vector<double> majorAxis,Vector<double> minorAxis, double length, double width, List<Vector<double>> corners)
+        {
+            // 生成理想矩形的四个角点
+            var idealCorners = new List<Vector<double>>
+        {
+            center + majorAxis * (length / 2) + minorAxis * (width / 2),   // 右上
+            center + majorAxis * (length / 2) - minorAxis * (width / 2),   // 右下
+            center - majorAxis * (length / 2) - minorAxis * (width / 2),   // 左下
+            center - majorAxis * (length / 2) + minorAxis * (width / 2)    // 左上
+        };
+
+            // 注意:理想角点顺序可能与输入不同,需要重新排序匹配
+            // 这里简单计算所有对应点的最小距离和
+            double sumSquared = 0;
+            for (int i = 0; i < 4; i++)
+            {
+                // 找到最近的理想角点
+                double minDist = double.MaxValue;
+                foreach (var ideal in idealCorners)
+                {
+                    double dist = Distance(corners[i], ideal);
+                    if (dist < minDist) minDist = dist;
+                }
+                sumSquared += minDist * minDist;
+            }
+
+            return Math.Sqrt(sumSquared / 4);
+        }
+
+        /// <summary>
+        /// 直接计算方法的拟合误差
+        /// </summary>
+        private static double CalculateFitErrorDirect(Vector<double> center, Vector<double> majorAxis,Vector<double> minorAxis, double length, double width, List<Vector<double>> corners)
+        {
+            // 对于直接方法,我们知道每个角点的理想位置
+            var idealCorners = new List<Vector<double>>
+        {
+            center - majorAxis * (length / 2) + minorAxis * (width / 2),  // 左上
+            center + majorAxis * (length / 2) + minorAxis * (width / 2),  // 右上
+            center + majorAxis * (length / 2) - minorAxis * (width / 2),  // 右下
+            center - majorAxis * (length / 2) - minorAxis * (width / 2)   // 左下
+        };
+
+            double sumSquared = 0;
+            for (int i = 0; i < 4; i++)
+            {
+                sumSquared += Math.Pow(Distance(corners[i], idealCorners[i]), 2);
+            }
+
+            return Math.Sqrt(sumSquared / 4);
+        }
+    }
+}

+ 3 - 2
TeamAAS-VM/Interfaces/ICamera.cs

@@ -100,10 +100,11 @@ namespace TeamAAS_VP.Interfaces
         void CloseDevice();
 
         /// <summary>
-        /// 采集单张图片
+        /// 采集单张图片,传入一个bool值,表示是否只需要采集最后一张图像,false:采集一张新图像,true:如果正在采集则返回最后一张图像,否则采集一张新图像
         /// </summary>
+        /// <param name="lastImageOnly"></param>
         /// <returns></returns>
-        ICogImage Grab();
+        ICogImage Grab(bool lastImageOnly=true);
 
         /// <summary>
         /// 开始实时采集图像

+ 5 - 0
TeamAAS-VM/Interfaces/IConfigService.cs

@@ -9,6 +9,7 @@ using TeamAAS_VP.Models.Feeder;
 using TeamAAS_VP.Models.Lights;
 using TeamAAS_VP.Models.PLC;
 using TeamAAS_VP.Models.ScrewDriver;
+using TeamAAS_VP.Models.Torque;
 
 namespace TeamAAS_VP.Interfaces
 {
@@ -502,5 +503,9 @@ namespace TeamAAS_VP.Interfaces
         /// </summary>
         /// <param name="config"></param>
         void SaveCardReaderConfig(SerialPortConfig config);
+
+        void SaveTorqueReaderConfig(TorqueSerialPortConfig config);
+
+        TorqueSerialPortConfig GetTorqueReaderConfig();
     }
 }

+ 44 - 0
TeamAAS-VM/Models/Torque/TorqueSerialPortConfig.cs

@@ -0,0 +1,44 @@
+using Prism.Mvvm;
+using System;
+using System.Collections.Generic;
+using System.IO.Ports;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TeamAAS_VP.Models.Torque
+{
+    public class TorqueSerialPortConfig : BindableBase
+    {
+        private string _PortName = "COM4";
+        public string PortName
+        {
+            get { return _PortName; }
+            set { SetProperty(ref _PortName, value); }
+        }
+        private int _BaudRate = 19200;
+        public int BaudRate
+        {
+            get { return _BaudRate; }
+            set { SetProperty(ref _BaudRate, value); }
+        }
+        private Parity _Parity = Parity.None;
+        public Parity Parity
+        {
+            get { return _Parity; }
+            set { SetProperty(ref _Parity, value); }
+        }
+        private StopBits _StopBits = StopBits.One;
+        public StopBits StopBits
+        {
+            get { return _StopBits; }
+            set { SetProperty(ref _StopBits, value); }
+        }
+        private int _DataBits = 8;
+        public int DataBits
+        {
+            get { return _DataBits; }
+            set { SetProperty(ref _DataBits, value); }
+        }
+    }
+}

BIN
TeamAAS-VM/Resources/Images/MobileDown_IndependentXYPlatform.png


+ 58 - 0
TeamAAS-VM/Services/ConfigService.cs

@@ -19,6 +19,7 @@ using TeamAAS_VP.Models.Feeder;
 using TeamAAS_VP.Models.Lights;
 using TeamAAS_VP.Models.PLC;
 using TeamAAS_VP.Models.ScrewDriver;
+using TeamAAS_VP.Models.Torque;
 using TeamAAS_VP.Resources.Languages;
 using WPFLocalizeExtension.Engine;
 
@@ -52,6 +53,8 @@ namespace TeamAAS_VP.Services
             public static readonly string ScrewFeedersConfigurationPath = "..//Config//ScrewFeedersConfiguration.cfg";
             // 刷卡器配置路径
             public static readonly string CardReaderConfigurationPath = "..//Config//CardReaderConfiguration.cfg";
+            // 电批配置路径
+            public static readonly string TorqueReaderConfigurationPath = "..//Config//TorqueReaderConfiguration.cfg";
         }
 
         private readonly object _sync = new object();
@@ -80,6 +83,8 @@ namespace TeamAAS_VP.Services
         // 刷卡器配置
         private SerialPortConfig _cardReaderConfig;
 
+        private TorqueSerialPortConfig _torqueReaderConfig;
+
         /// <summary>
         /// 注入系统数据库服务(可选),用于记录螺丝批次变更等记录。
         /// </summary>
@@ -1443,6 +1448,59 @@ namespace TeamAAS_VP.Services
                 }
             }
         }
+
+
+
+        #endregion
+
+        #region 读取电批配置
+        public void SaveTorqueReaderConfig(TorqueSerialPortConfig config)
+        {
+            if (config == null) return;
+            lock (_sync)
+            {
+                _torqueReaderConfig = config;
+                try
+                {
+                    FileHelper.WriteJsonFile(_torqueReaderConfig, ConfigPaths.TorqueReaderConfigurationPath);
+                }
+                catch
+                {
+                    // 忽略持久化错误(与项目中其它保存方法一致),可在需要时添加日志记录
+                }
+            }
+        }
+
+        public TorqueSerialPortConfig GetTorqueReaderConfig()
+        {
+            lock (_sync)
+            {
+                if (_torqueReaderConfig != null)
+                    return _torqueReaderConfig;
+
+                if (File.Exists(ConfigPaths.TorqueReaderConfigurationPath))
+                {
+                    try
+                    {
+                        _torqueReaderConfig = FileHelper.ReadJsonFile<TorqueSerialPortConfig>(ConfigPaths.TorqueReaderConfigurationPath);
+                    }
+                    catch
+                    {
+                        // 若读取失败,创建默认配置并覆盖文件(与项目中其他配置策略一致)
+                        _torqueReaderConfig = new TorqueSerialPortConfig();
+                        try { FileHelper.WriteJsonFile(_torqueReaderConfig, ConfigPaths.TorqueReaderConfigurationPath); } catch { }
+                    }
+                }
+                else
+                {
+                    _torqueReaderConfig = new TorqueSerialPortConfig();
+                    try { FileHelper.WriteJsonFile(_torqueReaderConfig, ConfigPaths.TorqueReaderConfigurationPath); } catch { }
+                }
+
+                return _torqueReaderConfig;
+            }
+        }
+
         #endregion
 
         /// <summary>

+ 4 - 0
TeamAAS-VM/Services/RemoteCommandService.cs

@@ -2426,6 +2426,10 @@ namespace TeamAAS_VP.Services
                         LogHelper.WriteLogInfo(string.Format(Lang.ÉèÖùâԴͨµÀ0ÁÁ¶È1ʧ°Ü, lightChannel.GlobalIndex, lightChannel.DefaultBrightness));
                     }
                 }
+                if (procedure.LightChannels.Count > 0)
+                {
+                    await Task.Delay(50);
+                }
             }
         }
 

+ 1 - 0
TeamAAS-VM/TeamAAS-VP.csproj

@@ -542,6 +542,7 @@
     <Compile Include="Models\Torque\PlcPoint_Torque.cs" />
     <Compile Include="Models\PhotoCaptureRecord.cs" />
     <Compile Include="Models\ScrewFeederBatchRecord.cs" />
+    <Compile Include="Models\Torque\TorqueSerialPortConfig.cs" />
     <Compile Include="Models\Torque\Torque_TaskStatus.cs" />
     <Compile Include="Models\Torque\TorqueProduceModel.cs" />
     <Compile Include="Models\WorkShift.cs" />

+ 317 - 0
TeamAAS-VM/ViewModels/Calibration/CalibIndependentCameraViewModel.cs

@@ -0,0 +1,317 @@
+using Cognex.VisionPro;
+using Cognex.VisionPro.ToolBlock;
+using MathNet.Numerics.LinearAlgebra;
+using Prism.Commands;
+using Prism.Events;
+using Prism.Ioc;
+using Prism.Mvvm;
+using Prism.Regions;
+using Prism.Services.Dialogs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using Team.FFFeederService.Interfaces;
+using TeamAAS_VP.Core;
+using TeamAAS_VP.Interfaces;
+using TeamAAS_VP.Models.Calibration;
+using TeamAAS_VP.Models.Robot;
+using TeamAAS_VP.Resources.Languages;
+
+namespace TeamAAS_VP.ViewModels.Calibration
+{
+    public class CalibIndependentCameraViewModel : BindableBase, IConfirmNavigationRequest
+    {
+        IRegionManager _regionManager;
+        IRegionNavigationService _regionNavigationService;
+        IContainerProvider _container;
+        IDialogService _dialogService;
+        IEventAggregator _eventAggregator;
+        IRobotService _robotService;
+        IFeederService _feederService;
+        ICameraService _cameraService;
+        ISystemDatabaseService _systemDatabaseService;
+        ICameraCalibrationService _cameraCalibrationService;
+        private CancellationTokenSource _cts;
+        #region 属性
+        private ICogImage _Image;
+
+        public ICogImage Image
+        {
+            get { return _Image; }
+            set { SetProperty(ref _Image, value); }
+        }
+
+        private Cognex.VisionPro.CogGraphicCollection _Graphic;
+
+        public Cognex.VisionPro.CogGraphicCollection Graphic
+        {
+            get { return _Graphic; }
+            set { SetProperty(ref _Graphic, value); }
+        }
+
+        private CalibrationInfo _SelectCalibration;
+
+        public CalibrationInfo SelectCalibration
+        {
+            get { return _SelectCalibration; }
+            set { SetProperty(ref _SelectCalibration, value); }
+        }
+
+        public Management management { get; set; }
+        public IRobot Robot { get; set; }
+
+        private bool _IsRunning = true;
+
+        public bool IsRunning
+        {
+            get { return _IsRunning; }
+            set { SetProperty(ref _IsRunning, value); }
+        }
+
+        private ICamera _Camera;
+
+        public ICamera Camera
+        {
+            get { return _Camera; }
+            set { SetProperty(ref _Camera, value); }
+        }
+        #endregion
+
+        #region 命令
+        private DelegateCommand _NextCommand;
+        public DelegateCommand NextCommand =>
+            _NextCommand ?? (_NextCommand = new DelegateCommand(ExecuteNextCommand).ObservesCanExecute(() => IsRunning));
+
+        private DelegateCommand _BackCommand;
+        public DelegateCommand BackCommand =>
+            _BackCommand ?? (_BackCommand = new DelegateCommand(ExecuteBackCommand).ObservesCanExecute(() => IsRunning));
+
+        private DelegateCommand _StartCalibCommand;
+        public DelegateCommand StartCalibCommand =>
+            _StartCalibCommand ?? (_StartCalibCommand = new DelegateCommand(ExecuteStartCalibCommand).ObservesCanExecute(() => IsRunning));
+
+        #endregion
+
+        #region 事件
+
+        #endregion
+
+        public CalibIndependentCameraViewModel(IRegionManager regionManager, IEventAggregator ea, IRegionNavigationService regionNavigationService, IContainerProvider container, IDialogService dialogService,
+            IRobotService robotService, IFeederService feederService, ICameraService cameraService, ISystemDatabaseService systemDatabaseService, ICameraCalibrationService cameraCalibrationService)
+        {
+            _regionManager = regionManager;
+            _regionNavigationService = regionNavigationService;
+            _container = container;
+            _dialogService = dialogService;
+            _robotService = robotService;
+            _eventAggregator = ea;
+            _feederService = feederService;
+            _cameraService = cameraService;
+            _systemDatabaseService = systemDatabaseService;
+            _cameraCalibrationService = cameraCalibrationService;
+        }
+
+        #region 方法
+
+        /// <summary>
+        /// 开始校准
+        /// </summary>
+        async void ExecuteStartCalibCommand()
+        {
+            //try
+            //{
+            //    //弹窗提示是否开始独立移动相机刚体变换校准
+            //    MessageBoxResult msgResult = MessageBox.Show("是否开始独立移动相机位置校准", Lang.自动校准, MessageBoxButton.YesNo, MessageBoxImage.Question);
+            //    if (msgResult != MessageBoxResult.Yes)
+            //    {
+            //        return;
+            //    }
+
+            //    IsRunning = false;
+            //    _cts = new CancellationTokenSource();
+            //    (bool IsSuccess, double[,] RotationMatrix, double[,] TranslationMatrix) = await _cameraCalibrationService.CalculateIndependentCameraRigidTransformationMatrix(SelectCalibration, Robot, _cts.Token);
+            //    if (!IsSuccess)
+            //    {
+            //        MessageBox.Show("独立移动相机位置校准失败", $"{Lang.自动校准}", MessageBoxButton.OK, MessageBoxImage.Warning);
+            //        return;
+            //    }
+            //    SelectCalibration.IndependentDownCameraMotionModuleRotationMatrix = RotationMatrix;
+            //    SelectCalibration.IndependentDownCameraMotionModuleTranslationMatrix = TranslationMatrix;
+            //    //NavigationParameters param = new NavigationParameters();
+            //    //param.Add("SelectCalibration", SelectCalibration);
+            //    //_regionManager.RequestNavigate("CalibrationRegionContext", "CalibrationAuto", param);
+
+            //}
+            //catch (Exception ex)
+            //{
+            //    LogHelper.WriteLogError("独立移动相机位置校准时出错!", ex);
+            //    MessageBox.Show(ex.Message);
+            //}
+            //finally
+            //{
+            //    try
+            //    {
+            //        if (_cts != null && !_cts.IsCancellationRequested)
+            //            _cts.Cancel();
+            //        _cts?.Dispose();
+            //        _cts = null;
+            //    }
+            //    catch (Exception)
+            //    {
+
+            //    }
+            //    IsRunning = true;
+            //}
+        }
+
+        /// <summary>
+        /// 下一步
+        /// </summary>
+        void ExecuteNextCommand()
+        {
+            NavigationParameters param = new NavigationParameters();
+            param.Add("SelectCalibration", SelectCalibration);
+            _regionManager.RequestNavigate("CalibrationRegionContext", "CalibrationAuto", param);
+        }
+
+        /// <summary>
+        /// 上一步
+        /// </summary>
+        void ExecuteBackCommand()
+        {
+            NavigationParameters param = new NavigationParameters();
+            param.Add("SelectCalibration", SelectCalibration);
+
+            _regionManager.RequestNavigate("CalibrationRegionContext", "CalibrationSelectTool", param);
+        }
+
+        /// <summary>
+        /// 相机拍照
+        /// </summary>
+        /// <returns></returns>
+        private async Task<(bool IsSuccess, double X, double Y, double U, int ImageWidth, int ImageHeight)> CaptureAndProcess()
+        {
+            await Task.Delay(200);
+            //采集图像
+            if (Camera != null)
+            {
+                ICogImage image = Camera.Grab();
+                if (SelectCalibration.IsDistortionCorrection && SelectCalibration.CalibCheckerboardTool != null)
+                {
+                    SelectCalibration.CalibCheckerboardTool.InputImage = image;
+                    SelectCalibration.CalibCheckerboardTool.Run();
+                    image = SelectCalibration.CalibCheckerboardTool.OutputImage;
+                }
+                SelectCalibration.ToolBlock.Inputs["InputImage"].Value = image;
+
+            }
+            App.Current.Dispatcher.Invoke(() =>
+            {
+                Graphic = null;
+                Image = (ICogImage)SelectCalibration.ToolBlock.Inputs["InputImage"].Value;
+            });
+
+            SelectCalibration.ToolBlock.Run();
+            CogToolBlockTerminalCollection outputCollection = SelectCalibration.ToolBlock.Outputs;
+            App.Current.Dispatcher.Invoke(() =>
+            {
+                Graphic = outputCollection["Graphic"].Value as CogGraphicCollection;
+            });
+
+            bool Found = (bool)(outputCollection["Found"].Value);
+            if (Found)
+            {
+                var strpoints = outputCollection["Point"].Value.ToString();
+                string[] items = strpoints.Split(',');
+                double x = double.Parse(items[0]);
+                double y = double.Parse(items[1]);
+                return (true, x, y, 0, Image.Width, Image.Height);
+            }
+            else
+            {
+                return (false, 0, 0, 0, Image.Width, Image.Height);
+            }
+        }
+
+        #endregion
+
+        #region 继承
+        /// <summary>
+        /// 确认导航请求时调用。此方法允许您在导航之前执行一些操作。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <param name="continuationCallback"></param>
+        public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
+        {
+            continuationCallback(true);
+        }
+
+        /// <summary>
+        /// 接收导航请求时调用。传入导航参数,包含有关导航目标的信息。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <exception cref="NotImplementedException"></exception>
+        public async void OnNavigatedTo(NavigationContext navigationContext)
+        {
+            SelectCalibration = navigationContext.Parameters.GetValue<CalibrationInfo>("SelectCalibration");
+            if (SelectCalibration != null)
+            {
+                Robot = _robotService.GetRobot(SelectCalibration.RobotId);
+            }
+            Camera = _cameraService.GetCamera(SelectCalibration.CameraID);
+            _cameraCalibrationService.CaptureAndProcessCallback = null;
+            _cameraCalibrationService.CaptureAndProcessCallback = CaptureAndProcess;
+            if (_systemDatabaseService.GetCurrentUser().userPart == Enums.UserPart.Operator)
+            {
+                IsAllowEdit = false;
+            }
+            else
+            {
+                IsAllowEdit = true;
+            }
+        }
+
+        /// <summary>
+        /// 是否允许导航到此视图模型。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <returns></returns>
+        public bool IsNavigationTarget(NavigationContext navigationContext)
+        {
+            return true;
+        }
+
+        /// <summary>
+        /// 导航离开此视图模型时调用。您可以在此处执行清理操作或保存状态。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        public void OnNavigatedFrom(NavigationContext navigationContext)
+        {
+            _cameraCalibrationService.CaptureAndProcessCallback = null;
+        }
+        #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;
+            }
+        }
+    }
+}

+ 261 - 0
TeamAAS-VM/ViewModels/Calibration/CalibrationDistortionViewModel.cs

@@ -0,0 +1,261 @@
+using Cognex.VisionPro;
+using Cognex.VisionPro.CalibFix;
+using Cognex.VisionPro.ToolBlock;
+using Prism.Commands;
+using Prism.Events;
+using Prism.Ioc;
+using Prism.Mvvm;
+using Prism.Regions;
+using Prism.Services.Dialogs;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Windows;
+using Team.FFFeederService.Interfaces;
+using TeamAAS_VP.Core;
+using TeamAAS_VP.Events;
+using TeamAAS_VP.Interfaces;
+using TeamAAS_VP.Models.Calibration;
+
+namespace TeamAAS_VP.ViewModels.Calibration
+{
+    public class CalibrationDistortionViewModel : BindableBase, IConfirmNavigationRequest
+    {
+        IRegionManager _regionManager;
+        IRegionNavigationService _regionNavigationService;
+        IContainerProvider _container;
+        IDialogService _dialogService;
+        IEventAggregator _eventAggregator;
+        IConfigService _configService;
+        IRobotService _robotService;
+        ICameraService _cameraService;
+        IFeederService _feederService;
+        ISystemDatabaseService _systemDatabaseService;
+
+        #region 属性
+
+        private CogCalibCheckerboardEditV2 _CalibCheckerboardEdit;
+
+        public CogCalibCheckerboardEditV2 CalibCheckerboardEdit
+        {
+            get { return _CalibCheckerboardEdit; }
+            set { SetProperty(ref _CalibCheckerboardEdit, value); }
+        }
+
+
+        private CalibrationInfo _SelectCalibration;
+
+        public CalibrationInfo SelectCalibration
+        {
+            get { return _SelectCalibration; }
+            set { SetProperty(ref _SelectCalibration, value); }
+        }
+
+        private ICamera _Camera;
+
+        public ICamera Camera
+        {
+            get { return _Camera; }
+            set { SetProperty(ref _Camera, value); }
+        }
+        #endregion
+
+        #region 命令
+        private DelegateCommand _LoadedCommand;
+        public DelegateCommand LoadedCommand =>
+            _LoadedCommand ?? (_LoadedCommand = new DelegateCommand(ExecuteLoadedCommand));
+        private DelegateCommand _NextCommand;
+        public DelegateCommand NextCommand =>
+            _NextCommand ?? (_NextCommand = new DelegateCommand(ExecuteNextCommand));
+
+        private DelegateCommand _BackCommand;
+        public DelegateCommand BackCommand =>
+            _BackCommand ?? (_BackCommand = new DelegateCommand(ExecuteBackCommand));
+
+        private DelegateCommand _RunCommand;
+        public DelegateCommand RunCommand =>
+            _RunCommand ?? (_RunCommand = new DelegateCommand(ExecuteRunCommand));
+        #endregion
+
+        #region 事件
+
+        #endregion
+
+        public CalibrationDistortionViewModel(IRegionManager regionManager, IEventAggregator ea, IRegionNavigationService regionNavigationService, IContainerProvider container, IDialogService dialogService,
+            IConfigService configService, IRobotService robotService, ICameraService cameraService, IFeederService feederService, ISystemDatabaseService systemDatabaseService)
+        {
+            _regionManager = regionManager;
+            _regionNavigationService = regionNavigationService;
+            _container = container;
+            _dialogService = dialogService;
+            _eventAggregator = ea;
+            _configService = configService;
+            _robotService = robotService;
+            _cameraService = cameraService;
+            _feederService = feederService;
+            _systemDatabaseService = systemDatabaseService;
+        }
+
+        #region 方法
+        void ExecuteLoadedCommand()
+        {
+            try
+            {
+                if (_systemDatabaseService.GetCurrentUser().userPart == Enums.UserPart.Operator)
+                {
+                    IsAllowEdit = false;
+                }
+                else
+                {
+                    IsAllowEdit = true;
+                }
+            }
+            catch (Exception ex)
+            {
+                MessageBox.Show(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 下一步
+        /// </summary>
+        void ExecuteNextCommand()
+        {
+            try
+            {
+                string calibToolPath = FilePath.CalibrationPath + "//" + SelectCalibration.Name + "//" + "CalibCheckerboard.vpp";
+                if (!Directory.Exists(Path.GetDirectoryName(calibToolPath)))
+                {
+                    Directory.CreateDirectory(Path.GetDirectoryName(calibToolPath));
+                }
+                SelectCalibration.CalibCheckerboardTool = CalibCheckerboardEdit.Subject;
+                //保存校准工具
+                CogSerializer.SaveObjectToFile(CalibCheckerboardEdit.Subject, calibToolPath);
+                
+                NavigationParameters param = new NavigationParameters();
+                param.Add("SelectCalibration", SelectCalibration);
+                _regionManager.RequestNavigate("CalibrationRegionContext", "CalibrationEditVision", param);
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("相机校准-畸变校准工具界面点击下一步时出错!", ex);
+                MessageBox.Show(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 上一步
+        /// </summary>
+        void ExecuteBackCommand()
+        {
+            try
+            {
+                NavigationParameters param = new NavigationParameters();
+                param.Add("SelectCalibration", SelectCalibration);
+                _regionManager.RequestNavigate("CalibrationRegionContext", "CalibrationCameraParams", param);
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("相机校准-畸变校准工具界面点击上一步时出错!", ex);
+                MessageBox.Show(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 运行
+        /// </summary>
+        void ExecuteRunCommand()
+        {
+            try
+            {
+                SelectCalibration.CalibCheckerboardTool = CalibCheckerboardEdit.Subject;
+                CalibCheckerboardEdit.Subject.InputImage = Camera.Grab();
+                CalibCheckerboardEdit.Subject.Run();
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("相机校准-畸变校准时出错", ex);
+                _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 });
+            }
+        }
+        #endregion
+
+        #region 继承
+        /// <summary>
+        /// 确认导航请求时调用。此方法允许您在导航之前执行一些操作。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <param name="continuationCallback"></param>
+        public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
+        {
+            continuationCallback(true);
+        }
+
+        /// <summary>
+        /// 接收导航请求时调用。传入导航参数,包含有关导航目标的信息。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <exception cref="NotImplementedException"></exception>
+        public async void OnNavigatedTo(NavigationContext navigationContext)
+        {
+            SelectCalibration = navigationContext.Parameters.GetValue<CalibrationInfo>("SelectCalibration");
+            if (CalibCheckerboardEdit != null)
+            {
+                //先判断校准文件夹下是否存在校准工具VPP文件,存在则加载,不存在则新建一个工具
+                string calibToolPath = FilePath.CalibrationPath + "//" + SelectCalibration.Name + "//" + "CalibCheckerboard.vpp";
+                if (System.IO.File.Exists(calibToolPath))
+                {
+                    SelectCalibration.CalibCheckerboardTool = CogSerializer.LoadObjectFromFile(calibToolPath) as CogCalibCheckerboardTool;
+                    CalibCheckerboardEdit.Subject = SelectCalibration.CalibCheckerboardTool;
+                }
+                else
+                {
+                    SelectCalibration.CalibCheckerboardTool = new CogCalibCheckerboardTool();
+                    CalibCheckerboardEdit.Subject = SelectCalibration.CalibCheckerboardTool;
+                }
+            }
+            Camera = _cameraService.GetCamera(SelectCalibration.CameraID);
+        }
+
+        /// <summary>
+        /// 是否允许导航到此视图模型。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <returns></returns>
+        public bool IsNavigationTarget(NavigationContext navigationContext)
+        {
+            return true;
+        }
+
+        /// <summary>
+        /// 导航离开此视图模型时调用。您可以在此处执行清理操作或保存状态。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        public void OnNavigatedFrom(NavigationContext navigationContext)
+        {
+
+        }
+        #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;
+            }
+        }
+    }
+}

+ 568 - 0
TeamAAS-VM/ViewModels/DebugMod/PlcRobotManualStepViewModel.cs

@@ -0,0 +1,568 @@
+using Prism.Commands;
+using Prism.Events;
+using Prism.Ioc;
+using Prism.Mvvm;
+using Prism.Regions;
+using Prism.Unity;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Windows;
+using TeamAAS_VP.Core.Robots;
+using TeamAAS_VP.Events;
+using TeamAAS_VP.Interfaces;
+using TeamAAS_VP.Resources.Languages;
+
+namespace TeamAAS_VP.ViewModels.DebugMod
+{
+    public class PlcRobotManualStepViewModel : BindableBase, IConfirmNavigationRequest
+    {
+        IContainerProvider _container;
+        IEventAggregator _eventAggregator;
+        IRobotService _robotService;
+        ISystemDatabaseService _systemDatabaseService;
+        IConfigService _configService;
+        public PlcRobotManualStepViewModel(IContainerProvider container, IEventAggregator ea, IRobotService robotService, ISystemDatabaseService systemDatabaseService, IConfigService configService)
+        {
+            _container = container;
+            _eventAggregator = ea;
+            _robotService = robotService;
+            _systemDatabaseService = systemDatabaseService;
+            _configService = configService;
+            //订阅权限登录事件
+            _eventAggregator.GetEvent<UserLoginNotification>().Subscribe(LoginChange);
+        }
+
+        #region 属性
+
+        private ObservableCollection<AxisExtended> _AdditionalAxisList;
+        public ObservableCollection<AxisExtended> AdditionalAxisList
+        {
+            get { return _AdditionalAxisList; }
+            set { SetProperty(ref _AdditionalAxisList, value); }
+        }
+
+        private XYZU_Robot _Robot;
+
+        public XYZU_Robot Robot
+        {
+            get { return _Robot; }
+            set
+            {
+                SetProperty(ref _Robot, value);
+                try
+                {
+                    if (value != null)
+                    {
+                        this.Distance = _configService.GetRobotStepDistance(value.Id);
+                    }
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+
+        private double _Distance = 1;
+
+        public double Distance
+        {
+            get { return _Distance; }
+            set
+            {
+                SetProperty(ref _Distance, value);
+                try
+                {
+                    if (Robot == null)
+                    {
+                        return;
+                    }
+                    if (_configService == null)
+                        _configService = ((PrismApplication)(App.Current)).Container.Resolve<IConfigService>();
+                    _configService.UpdateRobotStepDistance(Robot.Id, value);
+                }
+                catch (Exception)
+                {
+
+                }
+            }
+        }
+        #endregion
+
+        #region 命令
+
+        private DelegateCommand<string> _MotorCommand;
+        public DelegateCommand<string> MotorCommand =>
+            _MotorCommand ?? (_MotorCommand = new DelegateCommand<string>(ExecuteMotorCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+
+
+        private DelegateCommand _ResetCommand;
+        public DelegateCommand ResetCommand =>
+            _ResetCommand ?? (_ResetCommand = new DelegateCommand(ExecuteResetCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+
+
+        private DelegateCommand<object> _JogCommand;
+        public DelegateCommand<object> JogCommand =>
+            _JogCommand ?? (_JogCommand = new DelegateCommand<object>(ExecuteJogCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        //附加轴,轴点动+命令
+        private DelegateCommand<object> _AdditionalAxisJogPlusCommand;
+        public DelegateCommand<object> AdditionalAxisJogPlusCommand =>
+            _AdditionalAxisJogPlusCommand ?? (_AdditionalAxisJogPlusCommand = new DelegateCommand<object>(ExecuteAdditionalAxisJogPlusCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        //附加轴,轴点动-命令
+        private DelegateCommand<object> _AdditionalAxisJogMinusCommand;
+        public DelegateCommand<object> AdditionalAxisJogMinusCommand =>
+            _AdditionalAxisJogMinusCommand ?? (_AdditionalAxisJogMinusCommand = new DelegateCommand<object>(ExecuteAdditionalAxisJogMinusCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        private DelegateCommand<object> _AdditionalAxisMotorOnCommand;
+        public DelegateCommand<object> AdditionalAxisMotorOnCommand =>
+            _AdditionalAxisMotorOnCommand ?? (_AdditionalAxisMotorOnCommand = new DelegateCommand<object>(ExecuteAdditionalAxisMotorOnCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        private DelegateCommand<object> _AdditionalAxisMotorOffCommand;
+        public DelegateCommand<object> AdditionalAxisMotorOffCommand =>
+            _AdditionalAxisMotorOffCommand ?? (_AdditionalAxisMotorOffCommand = new DelegateCommand<object>(ExecuteAdditionalAxisMotorOffCommand).ObservesCanExecute(() => CanExecute).ObservesProperty(() => Robot).ObservesProperty(() => Robot.CanExecute));
+
+        //private DelegateCommand<string> _CameraAxisModerOnCommand;
+        //public DelegateCommand<string> CameraAxisModerOnCommand =>
+        //    _CameraAxisModerOnCommand ?? (_CameraAxisModerOnCommand = new DelegateCommand<string>(ExecuteCameraAxisModerOnCommand));
+
+        //private DelegateCommand<string> _CameraAxisModerOffCommand;
+        //public DelegateCommand<string> CameraAxisModerOffCommand =>
+        //    _CameraAxisModerOffCommand ?? (_CameraAxisModerOffCommand = new DelegateCommand<string>(ExecuteCameraAxisModerOffCommand));
+
+        //private DelegateCommand<string> _CameraAxisJogCommand;
+        //public DelegateCommand<string> CameraAxisJogCommand =>
+        //    _CameraAxisJogCommand ?? (_CameraAxisJogCommand = new DelegateCommand<string>(ExecuteCameraAxisJogCommand));
+
+
+        #endregion
+
+        #region 事件
+
+        #endregion
+
+        #region 方法
+
+        private void ShowMsg(string msg)
+        {
+            _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = $"{Lang.成功}!", Duration = 0.4 });
+        }
+
+        /// <summary>
+        /// 电机
+        /// </summary>
+        /// <param name="obj"></param>
+        private async void ExecuteMotorCommand(string obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                await Robot.MotorAsync(Convert.ToBoolean(obj));
+                ShowMsg($"{Lang.完成}!");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("机器人点击操作时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 重置
+        /// </summary>
+        private async void ExecuteResetCommand()
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                await Robot.ResetAsync();
+
+                ShowMsg($"{Lang.完成}!");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("重置机器人时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 点动
+        /// </summary>
+        /// <param name="obj"></param>
+        private async void ExecuteJogCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                var items = ((string)obj).Split(':');
+                //如果移动距离大于10mm,则提示用户确认
+                double distance = double.Parse(items[1]);
+                if (Math.Abs(distance) >= 10)
+                {
+                    if (MessageBox.Show($"确认要点动距离 {distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                    {
+                        return;
+                    }
+                }
+                Robot.Timeout = 6000000;
+                switch (items[0])
+                {
+                    case "X+":
+                        await Robot.JogAsync("X", distance);
+                        break;
+                    case "X-":
+                        await Robot.JogAsync("X", -distance);
+                        break;
+                    case "Y+":
+                        await Robot.JogAsync("Y", distance);
+                        break;
+                    case "Y-":
+                        await Robot.JogAsync("Y", -distance);
+                        break;
+                    case "Z+":
+                        await Robot.JogAsync("Z", distance);
+                        break;
+                    case "Z-":
+                        await Robot.JogAsync("Z", -distance);
+                        break;
+                    case "U+":
+                        await Robot.JogAsync("U", distance);
+                        break;
+                    case "U-":
+                        await Robot.JogAsync("U", -distance);
+                        break;
+                    case "V+":
+                        await Robot.JogAsync("V", distance);
+                        break;
+                    case "V-":
+                        await Robot.JogAsync("V", -distance);
+                        break;
+                    case "W+":
+                        await Robot.JogAsync("W", distance);
+                        break;
+                    case "W-":
+                        await Robot.JogAsync("W", -distance);
+                        break;
+                    default:
+                        break;
+                }
+                Robot.Timeout = 5000;
+
+
+                ShowMsg($"{Lang.完成}!");
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("机器点动时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        private bool CanExecute
+        {
+            get
+            {
+                if (Robot != null)
+                {
+                    return Robot.CanExecute;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+        }
+
+        /// <summary>
+        /// 附加轴+点动
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisJogPlusCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    //如果移动距离大于10mm,则提示用户确认
+                    if (Math.Abs(Distance) >= 10)
+                    {
+                        if (MessageBox.Show($"确认要点动距离 {Distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        {
+                            return;
+                        }
+                    }
+                    await Robot.AdditionalAxisJogAsync(axis.Name, Distance);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴点动+时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴-点动
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisJogMinusCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    //如果移动距离大于10mm,则提示用户确认
+                    if (Math.Abs(Distance) >= 10)
+                    {
+                        if (MessageBox.Show($"确认要点动距离 {Distance} mm 吗?", "确认点动", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes)
+                        {
+                            return;
+                        }
+                    }
+                    await Robot.AdditionalAxisJogNegAsync(axis.Name, Distance);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴点动-时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴下使能
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisMotorOffCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    await Robot.AdditionalAxisMotorAsync(axis.Name, false);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴上使能时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        /// <summary>
+        /// 附加轴上使能
+        /// </summary>
+        /// <param name="obj"></param>
+        async void ExecuteAdditionalAxisMotorOnCommand(object obj)
+        {
+            if (Robot == null || !Robot.IsConnected) return;
+            try
+            {
+                if (obj is AxisExtended axis)
+                {
+                    await Robot.AdditionalAxisMotorAsync(axis.Name, true);
+                    ShowMsg($"{Lang.完成}!");
+                }
+            }
+            catch (Exception ex)
+            {
+                LogHelper.WriteLogError("附加轴下使能时出错!", ex);
+                ShowMsg(ex.Message);
+            }
+        }
+
+        #region 相机控制
+        ///// <summary>
+        ///// 轴电机下使能操作
+        ///// </summary>
+        ///// <param name="parameter"></param>
+        //async void ExecuteCameraAxisModerOffCommand(string parameter)
+        //{
+        //    try
+        //    {
+        //        int cameraIndex = int.Parse(parameter);
+        //        var isSuccess = await Robot.CameraMotorAsync(cameraIndex, false);
+        //        if (isSuccess)
+        //        {
+        //            ShowMsg($"{Lang.完成}!");
+        //        }
+        //        else
+        //        {
+        //            ShowMsg("相机轴下使能失败!");
+        //        }
+        //    }
+        //    catch (Exception ex)
+        //    {
+        //        LogHelper.WriteLogError("相机轴下使能时出错!", ex);
+        //        ShowMsg(ex.Message);
+        //    }
+        //}
+
+        ///// <summary>
+        ///// 轴电机上使能操作
+        ///// </summary>
+        ///// <param name="parameter"></param>
+        //async void ExecuteCameraAxisModerOnCommand(string parameter)
+        //{
+        //    try
+        //    {
+        //        int cameraIndex = int.Parse(parameter);
+        //        var isSuccess =await Robot.CameraMotorAsync(cameraIndex, true);
+        //        if (isSuccess)
+        //        {
+        //            ShowMsg($"{Lang.完成}!");
+        //        }
+        //        else
+        //        {
+        //            ShowMsg("相机轴上使能失败!");
+        //        }
+        //    }
+        //    catch (Exception ex)
+        //    {
+        //        LogHelper.WriteLogError("相机轴上使能时出错!", ex);
+        //        ShowMsg(ex.Message);
+        //    }
+        //}
+
+        ///// <summary>
+        ///// 相机轴点动命令
+        ///// </summary>
+        ///// <param name="parameter"></param>
+        //async void ExecuteCameraAxisJogCommand(string parameter)
+        //{
+        //    try
+        //    {
+        //        int cameraIndex = int.Parse(parameter.Split(',')[0]);
+        //        double jogDistance = Distance;
+        //        string axisName = "";
+        //        switch (parameter.Split(',')[1])
+        //        {
+        //            case "X+":
+        //                jogDistance = Distance;
+        //                axisName = "X";
+        //                break;
+        //            case "X-":
+        //                jogDistance = -Distance;
+        //                axisName = "X";
+        //                break;
+        //            case "Y+":
+        //                jogDistance = Distance;
+        //                axisName = "Y";
+        //                break;
+        //            case "Y-":
+        //                jogDistance = -Distance;
+        //                axisName = "Y";
+        //                break;
+        //            default:
+        //                break;
+        //        }
+        //        bool isSuccess = await Robot.CameraJogAsync(cameraIndex, axisName, jogDistance);
+        //        if (isSuccess)
+        //        {
+        //            ShowMsg($"{Lang.完成}!");
+        //        }
+        //        else
+        //        {
+        //            ShowMsg("相机轴点动失败!");
+        //        }
+        //    }
+        //    catch (Exception ex)
+        //    {
+        //        LogHelper.WriteLogError("相机轴点动时出错!", ex);
+        //        ShowMsg(ex.Message);
+        //    }
+        //}
+        #endregion
+
+        #endregion
+
+        #region 继承
+        /// <summary>
+        /// 确认导航请求时调用。此方法允许您在导航之前执行一些操作。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <param name="continuationCallback"></param>
+        public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
+        {
+            continuationCallback(true);
+        }
+
+        /// <summary>
+        /// 接收导航请求时调用。传入导航参数,包含有关导航目标的信息。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <exception cref="NotImplementedException"></exception>
+        public async void OnNavigatedTo(NavigationContext navigationContext)
+        {
+            var robotInfo = navigationContext.Parameters["RobotInfo"] as Models.RobotInfo;
+            if (robotInfo != null)
+            {
+                Robot = _robotService.GetRobot(robotInfo.Id) as XYZU_Robot;
+                AdditionalAxisList = Robot.AdditionalAxisList;
+                Robot.EnterDebugMode = true;
+            }
+
+            if (_systemDatabaseService.GetCurrentUser().userPart == Enums.UserPart.Operator)
+            {
+                IsAllowEdit = false;
+            }
+            else
+            {
+                IsAllowEdit = true;
+            }
+        }
+
+        /// <summary>
+        /// 是否允许导航到此视图模型。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        /// <returns></returns>
+        public bool IsNavigationTarget(NavigationContext navigationContext)
+        {
+            return true;
+        }
+
+        /// <summary>
+        /// 导航离开此视图模型时调用。您可以在此处执行清理操作或保存状态。
+        /// </summary>
+        /// <param name="navigationContext"></param>
+        public void OnNavigatedFrom(NavigationContext navigationContext)
+        {
+            if (Robot != null)
+                Robot.EnterDebugMode = false;
+        }
+        #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;
+            }
+        }
+    }
+}

+ 31 - 0
TeamAAS-VM/ViewModels/SettingViewModel.cs

@@ -343,6 +343,15 @@ namespace TeamAAS_VP.ViewModels
             set { SetProperty(ref _CardReaderConfig, value); }
         }
 
+        private TorqueSerialPortConfig _TorqueReadConfig;
+
+        public TorqueSerialPortConfig TorqueReadConfig
+        {
+            get { return _TorqueReadConfig; }
+            set { SetProperty(ref _TorqueReadConfig, value); }
+        }
+
+
         private WorkShift _WorkShift;
 
         public WorkShift WorkShifts
@@ -453,6 +462,10 @@ namespace TeamAAS_VP.ViewModels
         public DelegateCommand SaveCardReaderConfigCommand =>
             _SaveCardReaderConfigCommand ?? (_SaveCardReaderConfigCommand = new DelegateCommand(ExecuteSaveCardReaderConfigCommand));
 
+        private DelegateCommand _SaveTorqueReaderConfigCommand;
+        public DelegateCommand SaveTorqueReaderConfigCommand =>
+            _SaveTorqueReaderConfigCommand ?? (_SaveTorqueReaderConfigCommand = new DelegateCommand(ExecuteSaveTorqueReaderConfigCommand));
+
         private DelegateCommand _SaveWorkShiftCommand;
         public DelegateCommand SaveWorkShiftCommand =>
             _SaveWorkShiftCommand ?? (_SaveWorkShiftCommand = new DelegateCommand(ExecuteSaveWorkShiftCommand));
@@ -1377,6 +1390,11 @@ namespace TeamAAS_VP.ViewModels
                 CardReaderConfig = _configService.GetCardReaderConfig();
             }
 
+            if (TorqueReadConfig == null)
+            {
+                TorqueReadConfig = _configService.GetTorqueReaderConfig();
+            }
+
             CurrentLanguage = _configService.GetSystemConfiguration().CurrentLanguage;
 
             // load system parameters into UI
@@ -1655,6 +1673,19 @@ namespace TeamAAS_VP.ViewModels
                 _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 });
             }
         }
+
+        void ExecuteSaveTorqueReaderConfigCommand()
+        {
+            try
+            {
+                _configService.SaveTorqueReaderConfig(TorqueReadConfig);
+            }
+            catch (Exception ex)
+            {
+                _eventAggregator.GetEvent<SnackbarMessageNotification>().Publish(new MessageParameter() { Msg = ex.Message, Duration = 1 });
+            }
+        }
+
         #endregion
 
         #region 设备信息

+ 123 - 0
TeamAAS-VM/Views/Calibration/CalibIndependentCamera.xaml

@@ -0,0 +1,123 @@
+<UserControl x:Class="TeamAAS_VP.Views.Calibration.CalibIndependentCamera"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:prism="http://prismlibrary.com/"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
+             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
+             xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:wf="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
+             xmlns:vp="clr-namespace:Cognex.VisionPro;assembly=Cognex.VisionPro.Controls"
+             xmlns:vm="clr-namespace:TeamAAS_VP.ViewModels.Calibration"
+             xmlns:lex="http://wpflocalizeextension.codeplex.com"
+             lex:LocalizeDictionary.DesignCulture="zh-CN"
+             lex:ResxLocalizationProvider.DefaultAssembly="TeamAAS-VP"
+             lex:ResxLocalizationProvider.DefaultDictionary="Lang"
+             prism:ViewModelLocator.AutoWireViewModel="True"
+             mc:Ignorable="d"
+             d:DataContext="{d:DesignInstance Type=vm:CalibIndependentCameraViewModel}"
+             d:Height="600"
+             d:Width="1024"
+             d:Background="White"
+             FontFamily="{DynamicResource DefaultFont}">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="auto" />
+            <RowDefinition Height="*" />
+            <RowDefinition Height="auto" />
+        </Grid.RowDefinitions>
+        <Button Grid.Row="0"
+                Content="{materialDesign:PackIcon Kind=ArrowLeftCircle}"
+                Width="50"
+                HorizontalAlignment="Left"
+                VerticalAlignment="Top"
+                ToolTip="{lex:Loc 返回}"
+                Style="{StaticResource MaterialDesignIconButton}"
+                Command="{Binding BackCommand}" />
+        <StackPanel Grid.Row="0"
+                    Orientation="Horizontal"
+                    Margin="50,0"
+                    VerticalAlignment="Center">
+            <materialDesign:PackIcon Kind="CrosshairsGps"
+                                     Width="30"
+                                     Height="30" />
+            <TextBlock Text="{Binding SelectCalibration.Name,Converter={StaticResource StringFormatConverter},ConverterParameter=' [{0}] '}"
+                       VerticalAlignment="Center"
+                       TextTrimming="CharacterEllipsis" />
+            <TextBlock Text="- 独立移动相机校准"
+                       VerticalAlignment="Center"
+                       TextTrimming="CharacterEllipsis" />
+        </StackPanel>
+
+        <Grid Grid.Row="1"
+              IsEnabled="{Binding IsAllowEdit}">
+            <Grid.RowDefinitions>
+                <RowDefinition Height="*" />
+                <RowDefinition Height="auto" />
+            </Grid.RowDefinitions>
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="*" />
+                <ColumnDefinition Width="auto"
+                                  MinWidth="300" />
+            </Grid.ColumnDefinitions>
+            <wf:WindowsFormsHost Grid.Column="0">
+                <vp:CogRecordDisplay x:Name="display" />
+            </wf:WindowsFormsHost>
+            <StackPanel Grid.Row="0"
+                        Grid.Column="1"
+                        Orientation="Vertical" VerticalAlignment="Center">
+                <StackPanel Orientation="Horizontal">
+                    <TextBlock Text="独立模组X方向移动的距离:"
+                               VerticalAlignment="Center"
+                               Margin="0,0,5,0" />
+                    <mah:NumericUpDown Grid.Row="6"
+                                       Grid.Column="1"
+                                       Maximum="600"
+                                       Minimum="-600"
+                                       Interval="1"
+                                       MinHeight="30"
+                                       Margin="5,10"
+                                       MinWidth="120"
+                                       StringFormat="{}{0:N3} mm"
+                                       materialDesign:HintAssist.HelperText="移动距离"
+                                       Value="{Binding SelectCalibration.IndependentDownCameraMotionModuleMoveX,Mode=TwoWay}" />
+                    
+                    <TextBlock Text="mm"
+                               VerticalAlignment="Center"
+                               Margin="0,0,5,0" />
+                </StackPanel>
+                <Button
+                        MinWidth="150"
+                        Margin="10,10"
+                        HorizontalAlignment="Center"
+                        VerticalAlignment="Center"
+                        materialDesign:ButtonAssist.CornerRadius="10"
+                        Style="{StaticResource MaterialDesignRaisedButton}"
+                        Command="{Binding StartCalibCommand}">
+                    <StackPanel Orientation="Horizontal">
+                        <materialDesign:PackIcon Kind="AutomaticStart" />
+                        <TextBlock Text="开始校准"
+                                   Margin="5,0" />
+                    </StackPanel>
+                </Button>
+            </StackPanel>
+        </Grid>
+
+        <StackPanel Grid.Row="2"
+                    Orientation="Horizontal"
+                    HorizontalAlignment="Center"
+                    VerticalAlignment="Center"
+                    Margin="20">
+            <Button Content="{lex:Loc 下一步}"
+                    IsEnabled="{Binding IsAllowEdit}"
+                    MinWidth="150"
+                    Margin="10,0"
+                    HorizontalAlignment="Center"
+                    VerticalAlignment="Center"
+                    materialDesign:ButtonAssist.CornerRadius="10"
+                    Style="{StaticResource MaterialDesignRaisedButton}"
+                    Command="{Binding NextCommand}" />
+        </StackPanel>
+    </Grid>
+</UserControl>

+ 73 - 0
TeamAAS-VM/Views/Calibration/CalibIndependentCamera.xaml.cs

@@ -0,0 +1,73 @@
+using Cognex.VisionPro;
+using Cognex.VisionPro.Dimensioning;
+using System;
+using System.Windows.Controls;
+using TeamAAS_VP.ViewModels.Calibration;
+
+namespace TeamAAS_VP.Views.Calibration
+{
+    /// <summary>
+    /// Interaction logic for CalibIndependentCamera
+    /// </summary>
+    public partial class CalibIndependentCamera : UserControl
+    {
+
+        //CalibIndependentCameraViewModel VM;
+        public CalibIndependentCamera()
+        {
+            InitializeComponent();
+            //VM = DataContext as CalibIndependentCameraViewModel;
+            //VM.PropertyChanged += VM_PropertyChanged;
+            //this.display.VerticalScrollBar = false;
+            //this.display.HorizontalScrollBar = false;
+            //this.display.AutoFit = true;
+            //this.display.BackColor = System.Drawing.SystemColors.ActiveCaption;
+        }
+
+        //private int _imageWidth;
+        //private int _imageHeight;
+        //private void VM_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+        //{
+        //    try
+        //    {
+        //        if (e.PropertyName == "Image")
+        //        {
+        //            this.display.Image = VM.Image;
+        //            //图像中心点坐标
+        //            _imageWidth = this.display.Image.Width;
+        //            _imageHeight = this.display.Image.Height;
+        //        }
+        //        else if (e.PropertyName == "Graphic")
+        //        {
+        //            this.display.StaticGraphics.Clear();
+        //            CogGraphicCollection graphic = new CogGraphicCollection();
+        //            CogCreateLineTool line1 = new CogCreateLineTool();
+        //            CogCreateLineTool line2 = new CogCreateLineTool();
+        //            line1.InputImage = this.display.Image;
+        //            line2.InputImage = this.display.Image;
+        //            line1.Line.X = _imageWidth / 2;
+        //            line1.Line.Y = _imageHeight / 2;
+        //            line1.Line.Rotation = 0;
+        //            line2.Line.X = _imageWidth / 2;
+        //            line2.Line.Y = _imageHeight / 2;
+        //            line2.Line.Rotation = Math.PI / 180 * 90;
+        //            line1.Run();
+        //            line2.Run();
+        //            graphic.Add(line1.GetOutputLine());
+        //            graphic.Add(line2.GetOutputLine());
+        //            this.display.StaticGraphics.Clear();
+        //            this.display.StaticGraphics.AddList(graphic, "");
+        //            if (VM.Graphic != null)
+        //            {
+        //                this.display.StaticGraphics.AddList(VM.Graphic, "");
+        //            }
+
+        //        }
+        //    }
+        //    catch (System.Exception)
+        //    {
+
+        //    }
+        //}
+    }
+}

+ 98 - 0
TeamAAS-VM/Views/Calibration/CalibrationDistortion.xaml

@@ -0,0 +1,98 @@
+<UserControl x:Class="TeamAAS_VP.Views.Calibration.CalibrationDistortion"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:prism="http://prismlibrary.com/"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
+             xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:wf="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
+             xmlns:vp="clr-namespace:Cognex.VisionPro.CalibFix;assembly=Cognex.VisionPro.CalibFix.Controls"
+             xmlns:uControl="clr-namespace:TeamAAS_VP.Controls"
+             xmlns:vm="clr-namespace:TeamAAS_VP.ViewModels.Calibration"
+             xmlns:convert="clr-namespace:TeamAAS_VP.ValueConverter"
+             xmlns:lex="http://wpflocalizeextension.codeplex.com"
+             lex:LocalizeDictionary.DesignCulture="zh-CN"
+             lex:ResxLocalizationProvider.DefaultAssembly="TeamAAS-VP"
+             lex:ResxLocalizationProvider.DefaultDictionary="Lang"
+             prism:ViewModelLocator.AutoWireViewModel="True"
+             mc:Ignorable="d"
+             d:DataContext="{d:DesignInstance Type=vm:CalibrationDistortionViewModel}"
+             d:Height="600"
+             d:Width="1024"
+             d:Background="White"
+             FontFamily="{DynamicResource DefaultFont}">
+    <b:Interaction.Triggers>
+        <b:EventTrigger EventName="Loaded">
+            <b:InvokeCommandAction Command="{Binding LoadedCommand}" />
+        </b:EventTrigger>
+    </b:Interaction.Triggers>
+    
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="auto" />
+            <RowDefinition Height="*" />
+            <RowDefinition Height="auto" />
+        </Grid.RowDefinitions>
+        <Button Grid.Row="0"
+                Content="{materialDesign:PackIcon Kind=ArrowLeftCircle}"
+                Width="50"
+                HorizontalAlignment="Left"
+                VerticalAlignment="Top"
+                ToolTip="{lex:Loc 返回}"
+                Style="{StaticResource MaterialDesignIconButton}"
+                Command="{Binding BackCommand}" />
+        <StackPanel Grid.Row="0"
+                    Orientation="Horizontal"
+                    Margin="50,0"
+                    VerticalAlignment="Center">
+            <materialDesign:PackIcon Kind="CrosshairsGps"
+                                     Width="30"
+                                     Height="30" />
+            <TextBlock Text="{Binding SelectCalibration.Name,Converter={StaticResource StringFormatConverter},ConverterParameter=' [{0}] '}"
+                       VerticalAlignment="Center" />
+            <TextBlock Text="- 图像畸变校准"
+                       VerticalAlignment="Center" />
+        </StackPanel>
+
+        <materialDesign:DrawerHost Grid.Row="1"
+                                   IsEnabled="{Binding IsAllowEdit}"
+                                   OpenMode="Standard"
+                                   x:Name="DrawerHost">
+            <materialDesign:DrawerHost.RightDrawerContent>
+                <uControl:PlcRobotManual x:Name="robotManual" />
+            </materialDesign:DrawerHost.RightDrawerContent>
+
+            <Grid>
+                <wf:WindowsFormsHost Grid.Column="0"
+                                     Margin="0,0,0,35">
+                    <vp:CogCalibCheckerboardEditV2 x:Name="CalibCheckerboardEditV2" />
+                </wf:WindowsFormsHost>
+                <Button Content="{lex:Loc 运行流程}"
+                        Width="100"
+                        HorizontalAlignment="Left"
+                        VerticalAlignment="Bottom"
+                        Margin="5,3"
+                        Command="{Binding RunCommand}" />
+            </Grid>
+
+
+        </materialDesign:DrawerHost>
+
+        <StackPanel Grid.Row="2"
+                    Orientation="Horizontal"
+                    HorizontalAlignment="Center"
+                    VerticalAlignment="Center"
+                    Margin="20">
+            <Button Content="{lex:Loc 下一步}"
+                    IsEnabled="{Binding IsAllowEdit}"
+                    MinWidth="150"
+                    Margin="10,0"
+                    HorizontalAlignment="Center"
+                    VerticalAlignment="Center"
+                    materialDesign:ButtonAssist.CornerRadius="10"
+                    Style="{StaticResource MaterialDesignRaisedButton}"
+                    Command="{Binding NextCommand}" />
+        </StackPanel>
+    </Grid>
+</UserControl>

+ 18 - 0
TeamAAS-VM/Views/Calibration/CalibrationDistortion.xaml.cs

@@ -0,0 +1,18 @@
+using System.Windows.Controls;
+using TeamAAS_VP.Core.Robots;
+using TeamAAS_VP.ViewModels.Calibration;
+
+namespace TeamAAS_VP.Views.Calibration
+{
+    /// <summary>
+    /// Interaction logic for CalibrationDistortion
+    /// </summary>
+    public partial class CalibrationDistortion : UserControl
+    {
+        public CalibrationDistortion()
+        {
+            InitializeComponent();
+            ((CalibrationDistortionViewModel)DataContext).CalibCheckerboardEdit = this.CalibCheckerboardEditV2;
+        }
+    }
+}

+ 752 - 0
TeamAAS-VM/Views/DebugMod/PlcRobotManualStep.xaml

@@ -0,0 +1,752 @@
+<UserControl x:Class="TeamAAS_VP.Views.DebugMod.PlcRobotManualStep"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:prism="http://prismlibrary.com/"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:vm="clr-namespace:TeamAAS_VP.ViewModels.DebugMod"
+             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
+             xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
+             xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:sys="clr-namespace:System;assembly=mscorlib"
+             xmlns:uControl="clr-namespace:TeamAAS_VP.Controls"
+             xmlns:convert="clr-namespace:TeamAAS_VP.ValueConverter"
+             xmlns:lex="http://wpflocalizeextension.codeplex.com"
+             lex:LocalizeDictionary.DesignCulture="zh-CN"
+             lex:ResxLocalizationProvider.DefaultAssembly="TeamAAS-VP"
+             lex:ResxLocalizationProvider.DefaultDictionary="Lang"
+             prism:ViewModelLocator.AutoWireViewModel="True"
+             mc:Ignorable="d"
+             d:Background="White"
+             d:DataContext="{d:DesignInstance Type=vm:PlcRobotManualStepViewModel}"
+             FontFamily="{DynamicResource DefaultFont}">
+    <UserControl.Resources>
+        <convert:JogConverter x:Key="JogConverter" />
+    </UserControl.Resources>
+    <ScrollViewer HorizontalAlignment="Stretch"
+                  VerticalAlignment="Stretch">
+        <Grid HorizontalAlignment="Stretch"
+              VerticalAlignment="Top"
+              IsEnabled="{Binding IsAllowEdit}">
+            <Grid.RowDefinitions>
+                <RowDefinition Height="auto" />
+                <RowDefinition Height="auto" />
+                <RowDefinition Height="auto" />
+            </Grid.RowDefinitions>
+
+            <Grid Grid.Row="0"
+                  Margin="10"
+                  HorizontalAlignment="Center">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="auto" />
+                    <ColumnDefinition Width="auto" />
+                    <ColumnDefinition Width="auto" />
+                    <ColumnDefinition Width="auto" />
+                </Grid.ColumnDefinitions>
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                    <RowDefinition Height="auto" />
+                </Grid.RowDefinitions>
+                <Grid.Resources>
+                    <Style TargetType="Button"
+                           BasedOn="{StaticResource MaterialDesignRaisedDarkButton}">
+                        <Setter Property="Width"
+                                Value="60" />
+                        <Setter Property="Height"
+                                Value="60" />
+                        <Setter Property="FontSize"
+                                Value="13" />
+                        <Setter Property="Padding"
+                                Value="0" />
+                        <Setter Property="HorizontalAlignment"
+                                Value="Center" />
+                        <Setter Property="VerticalAlignment"
+                                Value="Center" />
+                        <Setter Property="Margin"
+                                Value="2" />
+                        <Setter Property="materialDesign:ButtonAssist.CornerRadius"
+                                Value="10" />
+                    </Style>
+                </Grid.Resources>
+                <StackPanel Grid.ColumnSpan="4"
+                            Grid.Row="0"
+                            Grid.Column="0"
+                            VerticalAlignment="Center"
+                            Orientation="Horizontal">
+                    <Button Content="{lex:Loc 上使能}"
+                            Grid.Row="2"
+                            materialDesign:ButtonAssist.CornerRadius="10"
+                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                            Command="{Binding MotorCommand}"
+                            CommandParameter="True" />
+                    <Button Content="{lex:Loc 下使能}"
+                            Grid.Row="3"
+                            Margin="10"
+                            materialDesign:ButtonAssist.CornerRadius="10"
+                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                            Command="{Binding MotorCommand}"
+                            CommandParameter="False" />
+                    <Button Content="{lex:Loc 复位错误}"
+                            Grid.Row="4"
+                            materialDesign:ButtonAssist.CornerRadius="10"
+                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                            Command="{Binding ResetCommand}" />
+                </StackPanel>
+                <Button Grid.Row="2"
+                        Grid.Column="0"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=X-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowLeftBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-X"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="2"
+                        Grid.Column="2"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=X+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowRightBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+X"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="1"
+                        Grid.Column="1"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Y+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowTopBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+Y"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="3"
+                        Grid.Column="1"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Y-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowBottomBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-Y"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="1"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Z+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowTopBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+Z"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="3"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=Z-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="ArrowBottomBold"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-Z"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="4"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=U+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisZRotateCounterclockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+U"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="4"
+                        Grid.Column="0"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=U-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisZRotateClockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-U"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <!--<Button Grid.Row="4"
+                        Grid.Column="1"
+                        Grid.ColumnSpan="2"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=V+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisYRotateCounterclockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+V"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="5"
+                        Grid.Column="1"
+                        Grid.ColumnSpan="2"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=V-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisYRotateClockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-V"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>-->
+                <Button Grid.Row="5"
+                        Grid.Column="3"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=W+}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisXRotateCounterclockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="+W"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <Button Grid.Row="5"
+                        Grid.Column="0"
+                        Command="{Binding JogCommand}"
+                        CommandParameter="{Binding ElementName=txtDistance,Path=Value,Converter={StaticResource JogConverter}, ConverterParameter=W-}">
+                    <StackPanel Orientation="Vertical">
+                        <materialDesign:PackIcon Kind="AxisXRotateClockwise"
+                                                 Width="26"
+                                                 Height="26" />
+                        <TextBlock Text="-W"
+                                   HorizontalAlignment="Center" />
+                    </StackPanel>
+                </Button>
+                <StackPanel Grid.Row="6"
+                            Grid.ColumnSpan="7"
+                            Margin="10,10,0,10"
+                            Grid.Column="0"
+                            VerticalAlignment="Center"
+                            Orientation="Horizontal">
+                    <TextBlock Text="{lex:Loc 步进距离}"
+                               VerticalAlignment="Center" />
+                    <uControl:myNumericUpDowm x:Name="txtDistance"
+                                              MinWidth="100"
+                                              Interval="0.1"
+                                              Minimum="0"
+                                              Maximum="100"
+                                              StringFormat="{}{0:N3} mm"
+                                              Value="{Binding Distance,Mode=TwoWay}"
+                                              Margin="10,0" />
+                </StackPanel>
+                <TextBlock Grid.Row="7"
+                           Grid.ColumnSpan="7"
+                           VerticalAlignment="Center"
+                           Margin="10,0">
+                    <TextBlock.Text>
+                        <MultiBinding StringFormat="{}CurPos X: {0:F3} mm Y: {1:F3} mm Z: {2:F3} mm U: {3:F3} °R: {4:F3} °">
+                            <Binding Path="Robot.CurrentPosition.X" />
+                            <Binding Path="Robot.CurrentPosition.Y" />
+                            <Binding Path="Robot.CurrentPosition.Z" />
+                            <Binding Path="Robot.CurrentPosition.U" />
+                            <Binding Path="Robot.CurrentPosition.W" />
+                        </MultiBinding>
+                    </TextBlock.Text>
+                </TextBlock>
+            </Grid>
+
+            <!--相机轴控制区域-->
+            <!--<Grid Grid.Row="1"
+                  Margin="10,0,10,10">
+                <Grid.RowDefinitions>
+                    <RowDefinition Height="*" />
+                    <RowDefinition Height="*" />
+                </Grid.RowDefinitions>
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="*" />
+                    <ColumnDefinition Width="*" />
+                </Grid.ColumnDefinitions>
+                <Grid.Resources>
+                    <Style TargetType="Button"
+                           BasedOn="{StaticResource MaterialDesignRaisedDarkButton}">
+                        <Setter Property="MinWidth"
+                                Value="50" />
+                        <Setter Property="MinHeight"
+                                Value="30" />
+                        <Setter Property="FontSize"
+                                Value="13" />
+                        <Setter Property="Padding"
+                                Value="0" />
+                        <Setter Property="HorizontalAlignment"
+                                Value="Center" />
+                        <Setter Property="VerticalAlignment"
+                                Value="Center" />
+                        <Setter Property="materialDesign:ButtonAssist.CornerRadius"
+                                Value="10" />
+                    </Style>
+                </Grid.Resources>
+                -->
+            <!--相机2-->
+            <!--
+                <Border Grid.Row="0"
+                        Grid.Column="0"
+                        HorizontalAlignment="Stretch"
+                        BorderBrush="LightGray"
+                        BorderThickness="1"
+                        CornerRadius="10"
+                        Margin="2">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                        </Grid.RowDefinitions>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                        </Grid.ColumnDefinitions>
+                        <TextBlock Grid.Row="0"
+                                   Grid.ColumnSpan="3"
+                                   Text="相机2轴控制"
+                                   FontWeight="Bold"
+                                   FontSize="14"
+                                   HorizontalAlignment="Center" />
+                        <TextBlock Grid.Row="1"
+                                   Grid.ColumnSpan="3"
+                                   VerticalAlignment="Center"
+                                   Margin="10,0">
+                            <TextBlock.Text>
+                                <MultiBinding StringFormat="{}X: {0:F3} mm Y: {1:F3} mm">
+                                    <Binding Path="Robot.Camera2CurrentPosition.X" />
+                                    <Binding Path="Robot.Camera2CurrentPosition.Y" />
+                                </MultiBinding>
+                            </TextBlock.Text>
+                        </TextBlock>
+                        <StackPanel Grid.Row="2"
+                                    Grid.ColumnSpan="3"
+                                    Orientation="Horizontal"
+                                    HorizontalAlignment="Center">
+                            <Button Content="{lex:Loc 上使能}"
+                                    Command="{Binding CameraAxisModerOnCommand}"
+                                    CommandParameter="2"
+                                    Margin="2,0" />
+                            <Button Content="{lex:Loc 下使能}"
+                                    Margin="2,0"
+                                    Command="{Binding CameraAxisModerOffCommand}"
+                                    CommandParameter="2" />
+                        </StackPanel>
+                        <Button Grid.Row="4"
+                                Grid.Column="0"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowLeftBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="2,X-" />
+                        <Button Grid.Row="4"
+                                Grid.Column="2"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowRightBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="2,X+" />
+                        <Button Grid.Row="3"
+                                Grid.Column="1"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowTopBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="2,Y+" />
+                        <Button Grid.Row="5"
+                                Grid.Column="1"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowBottomBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="2,Y-" />
+                    </Grid>
+                </Border>
+                -->
+            <!--相机4-->
+            <!--
+                <Border Grid.Row="0"
+                        Grid.Column="1"
+                        HorizontalAlignment="Stretch"
+                        BorderBrush="LightGray"
+                        BorderThickness="1"
+                        CornerRadius="10"
+                        Margin="2">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                        </Grid.RowDefinitions>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                        </Grid.ColumnDefinitions>
+                        <TextBlock Grid.Row="0"
+                                   Grid.ColumnSpan="3"
+                                   Text="相机4轴控制"
+                                   FontWeight="Bold"
+                                   FontSize="14"
+                                   HorizontalAlignment="Center" />
+                        <TextBlock Grid.Row="1"
+                                   Grid.ColumnSpan="3"
+                                   VerticalAlignment="Center"
+                                   Margin="10,0">
+                            <TextBlock.Text>
+                                <MultiBinding StringFormat="{}X: {0:F3} mm Y: {1:F3} mm">
+                                    <Binding Path="Robot.Camera4CurrentPosition.X" />
+                                    <Binding Path="Robot.Camera4CurrentPosition.Y" />
+                                </MultiBinding>
+                            </TextBlock.Text>
+                        </TextBlock>
+                        <StackPanel Grid.Row="2"
+                                    Grid.ColumnSpan="3"
+                                    Orientation="Horizontal"
+                                    HorizontalAlignment="Center">
+                            <Button Content="{lex:Loc 上使能}"
+                                    Command="{Binding CameraAxisModerOnCommand}"
+                                    CommandParameter="4"
+                                    Margin="2,0" />
+                            <Button Content="{lex:Loc 下使能}"
+                                    Command="{Binding CameraAxisModerOffCommand}"
+                                    CommandParameter="4"
+                                    Margin="2,0" />
+                        </StackPanel>
+                        <Button Grid.Row="4"
+                                Grid.Column="0"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowLeftBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="4,X-" />
+                        <Button Grid.Row="4"
+                                Grid.Column="2"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowRightBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="4,X+" />
+                        <Button Grid.Row="3"
+                                Grid.Column="1"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowTopBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="4,Y+" />
+                        <Button Grid.Row="5"
+                                Grid.Column="1"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowBottomBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="4,Y-" />
+                    </Grid>
+                </Border>
+                -->
+            <!--相机1-->
+            <!--
+                <Border Grid.Row="1"
+                        Grid.Column="0"
+                        HorizontalAlignment="Stretch"
+                        BorderBrush="LightGray"
+                        BorderThickness="1"
+                        CornerRadius="10"
+                        Margin="2">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                        </Grid.RowDefinitions>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                        </Grid.ColumnDefinitions>
+                        <TextBlock Grid.Row="0"
+                                   Grid.ColumnSpan="3"
+                                   Text="相机1轴控制"
+                                   FontWeight="Bold"
+                                   FontSize="14"
+                                   HorizontalAlignment="Center" />
+                        <TextBlock Grid.Row="1"
+                                   Grid.ColumnSpan="3"
+                                   VerticalAlignment="Center"
+                                   Margin="10,0">
+                            <TextBlock.Text>
+                                <MultiBinding StringFormat="{}X: {0:F3} mm Y: {1:F3} mm">
+                                    <Binding Path="Robot.Camera1CurrentPosition.X" />
+                                    <Binding Path="Robot.Camera1CurrentPosition.Y" />
+                                </MultiBinding>
+                            </TextBlock.Text>
+                        </TextBlock>
+                        <StackPanel Grid.Row="2"
+                                    Grid.ColumnSpan="3"
+                                    Orientation="Horizontal"
+                                    HorizontalAlignment="Center">
+                            <Button Content="{lex:Loc 上使能}"
+                                    Command="{Binding CameraAxisModerOnCommand}"
+                                    CommandParameter="1"
+                                    Margin="2,0" />
+                            <Button Content="{lex:Loc 下使能}"
+                                    Command="{Binding CameraAxisModerOffCommand}"
+                                    CommandParameter="1"
+                                    Margin="2,0" />
+                        </StackPanel>
+                        <Button Grid.Row="4"
+                                Grid.Column="0"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowLeftBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="1,X-" />
+                        <Button Grid.Row="4"
+                                Grid.Column="2"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowRightBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="1,X+" />
+                    </Grid>
+                </Border>
+                -->
+            <!--相机3-->
+            <!--
+                <Border Grid.Row="1"
+                        Grid.Column="1"
+                        HorizontalAlignment="Stretch"
+                        BorderBrush="LightGray"
+                        BorderThickness="1"
+                        CornerRadius="10"
+                        Margin="2">
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                            <RowDefinition Height="auto" />
+                        </Grid.RowDefinitions>
+                        <Grid.ColumnDefinitions>
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                            <ColumnDefinition Width="*" />
+                        </Grid.ColumnDefinitions>
+                        <TextBlock Grid.Row="0"
+                                   Grid.ColumnSpan="3"
+                                   Text="相机3轴控制"
+                                   FontWeight="Bold"
+                                   FontSize="14"
+                                   HorizontalAlignment="Center" />
+                        <TextBlock Grid.Row="1"
+                                   Grid.ColumnSpan="3"
+                                   VerticalAlignment="Center"
+                                   Margin="10,0">
+                            <TextBlock.Text>
+                                <MultiBinding StringFormat="{}X: {0:F3} mm Y: {1:F3} mm">
+                                    <Binding Path="Robot.Camera3CurrentPosition.X" />
+                                    <Binding Path="Robot.Camera3CurrentPosition.Y" />
+                                </MultiBinding>
+                            </TextBlock.Text>
+                        </TextBlock>
+                        <StackPanel Grid.Row="2"
+                                    Grid.ColumnSpan="3"
+                                    Orientation="Horizontal"
+                                    HorizontalAlignment="Center">
+                            <Button Content="{lex:Loc 上使能}"
+                                    Command="{Binding CameraAxisModerOnCommand}"
+                                    CommandParameter="3"
+                                    Margin="2,0" />
+                            <Button Content="{lex:Loc 下使能}"
+                                    Command="{Binding CameraAxisModerOffCommand}"
+                                    CommandParameter="3"
+                                    Margin="2,0" />
+                        </StackPanel>
+                        <Button Grid.Row="4"
+                                Grid.Column="0"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowLeftBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="3,X-" />
+                        <Button Grid.Row="4"
+                                Grid.Column="2"
+                                MinHeight="30"
+                                MinWidth="40"
+                                Width="40"
+                                Height="30"
+                                Content="{materialDesign:PackIcon ArrowRightBold}"
+                                Command="{Binding CameraAxisJogCommand}"
+                                CommandParameter="3,X+" />
+                    </Grid>
+                </Border>
+            </Grid>-->
+
+            <!-- 附加轴列表:绑定到视图模型中的 ExtraAxes 集合(每项假定有 Name、Position) -->
+            <ItemsControl Grid.Row="2"
+                          ItemsSource="{Binding AdditionalAxisList}">
+                <ItemsControl.ItemTemplate>
+                    <DataTemplate>
+                        <Border Padding="6"
+                                Margin="0,2"
+                                Background="{DynamicResource MaterialDesignPaper}">
+                            <DockPanel LastChildFill="False"
+                                       VerticalAlignment="Center">
+                                <StackPanel Orientation="Horizontal"
+                                            DockPanel.Dock="Left"
+                                            VerticalAlignment="Center">
+                                    <TextBlock Text="{Binding Index}"
+                                               FontWeight="Bold"
+                                               VerticalAlignment="Center" />
+                                    <TextBlock Text="{Binding Name}"
+                                               FontWeight="Bold"
+                                               Margin="2,0"
+                                               VerticalAlignment="Center" />
+
+                                    <TextBlock Text="当前位置:"
+                                               Margin="30,0"
+                                               VerticalAlignment="Center" />
+                                    <TextBlock Text="{Binding CurrentPosition, StringFormat='{}{0:F3} mm'}"
+                                               Width="140"
+                                               VerticalAlignment="Center" />
+                                </StackPanel>
+                                <StackPanel Orientation="Horizontal"
+                                            HorizontalAlignment="Right"
+                                            DockPanel.Dock="Right">
+                                    <StackPanel.Resources>
+                                        <Style TargetType="Button"
+                                               BasedOn="{StaticResource MaterialDesignRaisedDarkButton}">
+                                            <Setter Property="Width"
+                                                    Value="60" />
+                                            <Setter Property="Height"
+                                                    Value="60" />
+                                            <Setter Property="FontSize"
+                                                    Value="13" />
+                                            <Setter Property="Padding"
+                                                    Value="0" />
+                                            <Setter Property="HorizontalAlignment"
+                                                    Value="Center" />
+                                            <Setter Property="VerticalAlignment"
+                                                    Value="Center" />
+                                            <Setter Property="Margin"
+                                                    Value="2" />
+                                            <Setter Property="materialDesign:ButtonAssist.CornerRadius"
+                                                    Value="10" />
+                                        </Style>
+                                    </StackPanel.Resources>
+                                    <Button Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisJogMinusCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}">
+                                        <StackPanel Orientation="Vertical">
+                                            <materialDesign:PackIcon Kind="ArrowLeftBold"
+                                                                     Width="26"
+                                                                     Height="26" />
+                                            <TextBlock Text="-"
+                                                       HorizontalAlignment="Center" />
+                                        </StackPanel>
+                                    </Button>
+                                    <Button Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisJogPlusCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}">
+                                        <StackPanel Orientation="Vertical">
+                                            <materialDesign:PackIcon Kind="ArrowRightBold"
+                                                                     Width="26"
+                                                                     Height="26" />
+                                            <TextBlock Text="+"
+                                                       HorizontalAlignment="Center" />
+                                        </StackPanel>
+                                    </Button>
+                                    <Button Content="上使能"
+                                            Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisMotorOnCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
+                                    <Button Content="下使能"
+                                            Width="60"
+                                            Height="36"
+                                            Margin="6,0"
+                                            Style="{StaticResource MaterialDesignRaisedDarkButton}"
+                                            Command="{Binding DataContext.AdditionalAxisMotorOffCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
+                                            CommandParameter="{Binding}" />
+                                </StackPanel>
+                            </DockPanel>
+                        </Border>
+                    </DataTemplate>
+                </ItemsControl.ItemTemplate>
+            </ItemsControl>
+
+            <materialDesign:Snackbar Grid.RowSpan="2"
+                                     x:Name="snackbar"
+                                     Opacity="0.9"
+                                     HorizontalContentAlignment="Center"
+                                     VerticalAlignment="Top" />
+        </Grid>
+    </ScrollViewer>
+</UserControl>

+ 15 - 0
TeamAAS-VM/Views/DebugMod/PlcRobotManualStep.xaml.cs

@@ -0,0 +1,15 @@
+using System.Windows.Controls;
+
+namespace TeamAAS_VP.Views.DebugMod
+{
+    /// <summary>
+    /// Interaction logic for PlcRobotManualStep
+    /// </summary>
+    public partial class PlcRobotManualStep : UserControl
+    {
+        public PlcRobotManualStep()
+        {
+            InitializeComponent();
+        }
+    }
+}

+ 112 - 38
TeamAAS-VM/Views/SettingView.xaml

@@ -2413,85 +2413,159 @@
                             </StackPanel>
                         </Button>
                     </Grid>
-
-                    <!--刷卡参数配置-->
-                    <GroupBox Grid.Row="5"
-                              Grid.ColumnSpan="2"
+                    <StackPanel Grid.Row="5"  Margin="0,12,0,0" Orientation="Horizontal" >
+                        <!--刷卡参数配置-->
+                        <GroupBox 
                               Header="刷卡器配置"
-                              HorizontalAlignment="Left"
-                              MinWidth="400"
-                              Margin="0,12,0,0">
-                        <Grid Margin="10">
-                            <Grid.ColumnDefinitions>
-                                <ColumnDefinition Width="auto" />
-                                <ColumnDefinition Width="*" />
-                            </Grid.ColumnDefinitions>
-                            <Grid.RowDefinitions>
-                                <RowDefinition Height="Auto" />
-                                <RowDefinition Height="Auto" />
-                                <RowDefinition Height="Auto" />
-                                <RowDefinition Height="Auto" />
-                                <RowDefinition Height="Auto" />
-                                <RowDefinition Height="Auto" />
-                            </Grid.RowDefinitions>
-                            <TextBlock Text="PortName:"
+                              Width="300"
+                            Margin="0,0,50,0"
+                            HorizontalAlignment="Left"
+                             >
+                            <Grid Margin="10">
+                                <Grid.ColumnDefinitions>
+                                    <ColumnDefinition Width="auto" />
+                                    <ColumnDefinition Width="*" />
+                                </Grid.ColumnDefinitions>
+                                <Grid.RowDefinitions>
+                                    <RowDefinition Height="Auto" />
+                                    <RowDefinition Height="Auto" />
+                                    <RowDefinition Height="Auto" />
+                                    <RowDefinition Height="Auto" />
+                                    <RowDefinition Height="Auto" />
+                                    <RowDefinition Height="Auto" />
+                                </Grid.RowDefinitions>
+                                <TextBlock Text="PortName:"
                                        Grid.Row="0"
                                        Grid.Column="0"
                                        HorizontalAlignment="Right" />
-                            <TextBox Text="{Binding CardReaderConfig.PortName,Mode=TwoWay}"
+                                <TextBox Text="{Binding CardReaderConfig.PortName,Mode=TwoWay}"
                                      Grid.Row="0"
                                      Grid.Column="1"
                                      Margin="6,0" />
-                            <TextBlock Text="BaudRate:"
+                                <TextBlock Text="BaudRate:"
                                        Grid.Row="1"
                                        Grid.Column="0"
                                        HorizontalAlignment="Right" />
-                            <TextBox Text="{Binding CardReaderConfig.BaudRate,Mode=TwoWay}"
+                                <TextBox Text="{Binding CardReaderConfig.BaudRate,Mode=TwoWay}"
                                      Grid.Row="1"
                                      Grid.Column="1"
                                      Margin="6,0" />
-                            <TextBlock Text="Parity:"
+                                <TextBlock Text="Parity:"
                                        Grid.Row="2"
                                        Grid.Column="0"
                                        HorizontalAlignment="Right" />
-                            <ComboBox Grid.Row="2"
+                                <ComboBox Grid.Row="2"
                                       Grid.Column="1"
                                       SelectedItem="{Binding CardReaderConfig.Parity,Mode=TwoWay}"
                                       ItemsSource="{Binding Source={StaticResource Parity}}" />
-                            <TextBlock Text="StopBits:"
+                                <TextBlock Text="StopBits:"
                                        Grid.Row="3"
                                        Grid.Column="0"
                                        HorizontalAlignment="Right" />
-                            <ComboBox Grid.Row="3"
+                                <ComboBox Grid.Row="3"
                                       Grid.Column="1"
                                       SelectedItem="{Binding CardReaderConfig.StopBits,Mode=TwoWay}"
                                       ItemsSource="{Binding Source={StaticResource StopBits}}" />
-                            <TextBlock Text="DataBits:"
+                                <TextBlock Text="DataBits:"
                                        Grid.Row="4"
                                        Grid.Column="0"
                                        HorizontalAlignment="Right" />
-                            <TextBox Text="{Binding CardReaderConfig.DataBits,Mode=TwoWay}"
+                                <TextBox Text="{Binding CardReaderConfig.DataBits,Mode=TwoWay}"
                                      Grid.Row="4"
                                      Grid.Column="1"
                                      Margin="6,0" />
 
-                            <Button Grid.Row="5"
+                                <Button Grid.Row="5"
                                     Grid.Column="1"
                                     Margin="0,6"
                                     Style="{StaticResource MaterialDesignRaisedButton}"
                                     materialDesign:ButtonAssist.CornerRadius="10"
                                     MinWidth="120"
                                     Command="{Binding SaveCardReaderConfigCommand}">
-                                <StackPanel Orientation="Horizontal"
+                                    <StackPanel Orientation="Horizontal"
                                             HorizontalAlignment="Center">
-                                    <materialDesign:PackIcon Kind="ContentSaveCheck"
+                                        <materialDesign:PackIcon Kind="ContentSaveCheck"
                                                              Margin="0,0,6,0" />
-                                    <TextBlock VerticalAlignment="Center"
+                                        <TextBlock VerticalAlignment="Center"
                                                Text="保存刷卡器配置" />
-                                </StackPanel>
-                            </Button>
-                        </Grid>
-                    </GroupBox>
+                                    </StackPanel>
+                                </Button>
+                            </Grid>
+                        </GroupBox>
+
+                        <GroupBox Header="电批配置"  Width="300">
+                            <Grid Margin="10">
+                                <Grid.ColumnDefinitions>
+                                    <ColumnDefinition Width="auto" />
+                                    <ColumnDefinition Width="*" />
+                                </Grid.ColumnDefinitions>
+                                <Grid.RowDefinitions>
+                                    <RowDefinition Height="Auto" />
+                                    <RowDefinition Height="Auto" />
+                                    <RowDefinition Height="Auto" />
+                                    <RowDefinition Height="Auto" />
+                                    <RowDefinition Height="Auto" />
+                                    <RowDefinition Height="Auto" />
+                                </Grid.RowDefinitions>
+                                <TextBlock Text="PortName:"
+                                     Grid.Row="0"
+                                     Grid.Column="0"
+                                     HorizontalAlignment="Right" />
+                                <TextBox Text="{Binding TorqueReadConfig.PortName,Mode=TwoWay}"
+                                     Grid.Row="0"
+                                     Grid.Column="1"
+                                     Margin="6,0" />
+                                <TextBlock Text="BaudRate:"
+                                    Grid.Row="1"
+                                    Grid.Column="0"
+                                    HorizontalAlignment="Right" />
+                                <TextBox Text="{Binding TorqueReadConfig.BaudRate,Mode=TwoWay}"
+                                    Grid.Row="1"
+                                    Grid.Column="1"
+                                    Margin="6,0" />
+                                <TextBlock Text="Parity:"
+                                    Grid.Row="2"
+                                    Grid.Column="0"
+                                    HorizontalAlignment="Right" />
+                                <ComboBox Grid.Row="2"
+                                    Grid.Column="1"
+                                    SelectedItem="{Binding TorqueReadConfig.Parity,Mode=TwoWay}"
+                                    ItemsSource="{Binding Source={StaticResource Parity}}" />
+                                <TextBlock Text="StopBits:"
+                                    Grid.Row="3"
+                                    Grid.Column="0"
+                                    HorizontalAlignment="Right" />
+                                <ComboBox Grid.Row="3"
+                                    Grid.Column="1"
+                                    SelectedItem="{Binding TorqueReadConfig.StopBits,Mode=TwoWay}"
+                                    ItemsSource="{Binding Source={StaticResource StopBits}}" />
+                                <TextBlock Text="DataBits:"
+                                    Grid.Row="4"
+                                    Grid.Column="0"
+                                    HorizontalAlignment="Right" />
+                                <TextBox Text="{Binding TorqueReadConfig.DataBits,Mode=TwoWay}"
+                                     Grid.Row="4"
+                                     Grid.Column="1"
+                                     Margin="6,0" />
+
+                                <Button Grid.Row="5"
+                                    Grid.Column="1"
+                                    Margin="0,6"
+                                    Style="{StaticResource MaterialDesignRaisedButton}"
+                                    materialDesign:ButtonAssist.CornerRadius="10"
+                                    MinWidth="120"
+                                    Command="{Binding SaveTorqueReaderConfigCommand}">
+                                    <StackPanel Orientation="Horizontal"
+                        HorizontalAlignment="Center">
+                                        <materialDesign:PackIcon Kind="ContentSaveCheck"
+                                         Margin="0,0,6,0" />
+                                        <TextBlock VerticalAlignment="Center"
+                           Text="保存电批配置" />
+                                    </StackPanel>
+                                </Button>
+                            </Grid>
+                        </GroupBox>
+                    </StackPanel>
                 </StackPanel>
             </TabItem>