添加 MX-PD-盘古 项目文件

将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
Shi.Ji
2026-05-18 11:43:09 +08:00
parent 03632a379d
commit e31d3560bb
739 changed files with 99783 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
using MainShell.Common;
using MainShell.Models;
using MainShell.Process;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Manual.Model
{
[Export(typeof(IParameter))]
public class DieBondingManualSetting : ParameterBase
{
public DieBondingManualSysItem DieBondingManualSysItem { get; set; } = new DieBondingManualSysItem();
public override void Copy(IParameter source)
{
if (source is DieBondingManualSetting item)
{
this.DieBondingManualSysItem = item.DieBondingManualSysItem.Clone() as DieBondingManualSysItem;
}
}
}
public class DieBondingManualSysItem : PropertyChangedBase, IParameterItem
{
private RegionModel _substrateRegion = new RegionModel();
public RegionModel SubstrateRegion
{
get { return _substrateRegion; }
set { SetAndNotify(ref _substrateRegion, value); }
}
private RegionModel _dieRegion = new RegionModel();
public RegionModel DieRegion
{
get { return _dieRegion; }
set { SetAndNotify(ref _dieRegion, value); }
}
private TransPathType _transPathType = TransPathType.Sequence;
public TransPathType TransPathType
{
get { return _transPathType; }
set { SetAndNotify(ref _transPathType, value); }
}
private DieTransferRowTraversalStrategy _padRowDirectionStrategy = DieTransferRowTraversalStrategy.AllPositive;
public DieTransferRowTraversalStrategy PadRowDirectionStrategy
{
get { return _padRowDirectionStrategy; }
set { SetAndNotify(ref _padRowDirectionStrategy, value); }
}
private DieTransferRowTraversalStrategy _dieRowDirectionStrategy = DieTransferRowTraversalStrategy.Serpentine;
public DieTransferRowTraversalStrategy DieRowDirectionStrategy
{
get { return _dieRowDirectionStrategy; }
set { SetAndNotify(ref _dieRowDirectionStrategy, value); }
}
//使用自动计算芯片模式
private bool _autoDieRegionModeEnable = false;
public bool AutoDieRegionModeEnable
{
get { return _autoDieRegionModeEnable; }
set { SetAndNotify(ref _autoDieRegionModeEnable, value); }
}
public IParameterItem Clone()
{
return new DieBondingManualSysItem()
{
SubstrateRegion = this.SubstrateRegion.Clone() as RegionModel,
DieRegion = this.DieRegion.Clone() as RegionModel,
TransPathType = this.TransPathType,
PadRowDirectionStrategy = this.PadRowDirectionStrategy,
DieRowDirectionStrategy = this.DieRowDirectionStrategy,
AutoDieRegionModeEnable = this.AutoDieRegionModeEnable
};
}
}
}

View File

@@ -0,0 +1,78 @@
using MainShell.Common;
using MainShell.Alarm;
using MainShell.Process;
using MainShell.Recipe.Models;
using MW.WorkFlow;
using StyletIoC;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell.Manual.Model
{
public abstract class OperateViewModelBase : CameraBaseViewModel
{
private const int WorkflowFaultAlarmId = 20000;
[Inject]
public WorkflowRunner Runner { get; set; }
[Inject]
public AlarmOperate AlarmOperate { get; set; }
public virtual async Task StopProcess()
{
await Runner.StopAsync();
}
protected async Task<WorkflowRunCompletedEventArgs> RunManualActivityAsync(IActivity activity, WorkflowContext context, bool showSuccessDialog = true)
{
var result = await Runner.RunActivityWithResultAsync(activity, context);
ShowWorkflowResultDialog(result, context, activity != null ? activity.Name : null, showSuccessDialog);
return result;
}
protected bool IsWorkflowCompleted(WorkflowRunCompletedEventArgs result)
{
return result != null && result.FinalState == WorkflowState.Completed;
}
private void ShowWorkflowResultDialog(WorkflowRunCompletedEventArgs result, WorkflowContext context, string activityName, bool showSuccessDialog)
{
if (result == null)
{
return;
}
CommonUti.RunOnUi(() =>
{
switch (result.FinalState)
{
case WorkflowState.Faulted:
ShowWorkflowFaultAlarm(result, context, activityName);
break;
case WorkflowState.Canceled:
MwMessageBox.Show("流程已取消!", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
break;
case WorkflowState.Completed:
if (showSuccessDialog)
{
MwMessageBox.Show("流程执行成功。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
break;
}
});
}
private void ShowWorkflowFaultAlarm(WorkflowRunCompletedEventArgs result, WorkflowContext context, string activityName)
{
if (AlarmOperate == null)
{
MwMessageBox.Show("流程执行过程中发生异常!", result.Error, MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
AlarmOperate.Alert(WorkflowFaultAlarmId);
}
}
}

View File

@@ -0,0 +1,54 @@
<UserControl x:Class="MainShell.Manual.View.ChipStraighteningView"
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:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="ChipStraighteningCardStyle" TargetType="GroupBox" BasedOn="{StaticResource ProcessCardGroupBoxStyle}">
<Setter Property="Width" Value="320"/>
</Style>
<Style x:Key="ChipStraighteningLabelStyle" TargetType="Label" BasedOn="{StaticResource ProcessLabelStyle}">
<Setter Property="Width" Value="82"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ProcessPageBackgroundBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Style="{StaticResource ProcessCameraHostBorderStyle}">
<ContentControl Content="{Binding CameraAxisViewModel}" IsEnabled="{Binding Runner.IsRunning,Converter={StaticResource boolToInversionConverter}}"/>
</Border>
<GroupBox Grid.Column="1" Margin="0,4,4,4" Header="{DynamicResource ChipStraightening}" Style="{StaticResource ProcessWideCardGroupBoxStyle}">
<Grid Margin="0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="60"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical">
<GroupBox Style="{StaticResource ChipStraighteningCardStyle}" Header="{DynamicResource ChipAngle}">
<StackPanel>
<StackPanel Style="{StaticResource ProcessParameterRowStyle}">
<Label Style="{StaticResource ChipStraighteningLabelStyle}" Content="{DynamicResource ChipAngle}"/>
<mw:NumberBox Style="{StaticResource ProcessLargeNumberBoxStyle}" IsReadOnly="True" Value="{Binding ChipAngle}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="1" Margin="0,8,0,0">
<Button Content="{DynamicResource Start}" Click="{mw:Action StartProcess}" Style="{StaticResource StartButtonStyle}" Margin="5"
IsEnabled="{Binding Runner.IsRunning, Converter={StaticResource boolToInversionConverter}}" />
<Button Content="{DynamicResource Stop}" Click="{mw:Action StopProcess}" Style="{StaticResource StopButtonStyle }" Margin="5"
IsEnabled="{Binding Runner.IsRunning}" />
</StackPanel>
</Grid>
</GroupBox>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
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;
namespace MainShell.Manual.View
{
/// <summary>
/// ChipStraighteningView.xaml 的交互逻辑
/// </summary>
public partial class ChipStraighteningView : UserControl
{
public ChipStraighteningView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,136 @@
<UserControl x:Class="MainShell.Manual.View.DieBondingView"
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:mw="http://www.maxwell-gp.com/"
xmlns:custom="clr-namespace:MainShell.Resources.CustomControl"
xmlns:conv="clr-namespace:MainShell.Converter"
xmlns:common="clr-namespace:MainShell.Common"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<conv:EnumBindingSourceExtension x:Key="TransPathType" UseDescription="True" EnumType="{x:Type common:TransPathType}" />
<conv:EnumBindingSourceExtension x:Key="DieTransferRowTraversalStrategy" UseDescription="True" EnumType="{x:Type common:DieTransferRowTraversalStrategy}" />
<Style x:Key="DieBondingTextStyle" TargetType="TextBlock" BasedOn="{StaticResource ProcessReadOnlyValueTextStyle}">
<Setter Property="Width" Value="128"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
<Style x:Key="DieBondingComboStyle" TargetType="ComboBox" BasedOn="{StaticResource ProcessComboBoxStyle}">
<Setter Property="Width" Value="180"/>
</Style>
<Style x:Key="DieBondingPathButtonStyle" TargetType="Button" BasedOn="{StaticResource CommonButtonStyle}">
<Setter Property="Margin" Value="4,2"/>
<Setter Property="MinWidth" Value="120"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ProcessPageBackgroundBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Style="{StaticResource ProcessCameraHostBorderStyle}">
<ContentControl Content="{Binding CameraAxisViewModel}" />
</Border>
<TabControl Grid.Column="1" Margin="0,4,4,4" Style="{StaticResource ProcessTabControlStyle}">
<TabItem Header="{DynamicResource DieBondingSet}">
<Grid Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical">
<custom:RegionEditorView Title="{DynamicResource SubstrateRegion}" Margin="0,0,0,18"/>
<custom:RegionEditorView Title="{DynamicResource WaferRegion}" Margin="0,0,0,18"/>
<GroupBox Header="{DynamicResource GenerateSet}" Style="{StaticResource ProcessWideCardGroupBoxStyle}">
<StackPanel>
<StackPanel Style="{StaticResource ProcessParameterRowStyle}">
<TextBlock Text="{DynamicResource DieBondingGenerateModeLabel}" Style="{StaticResource DieBondingTextStyle}"/>
<ComboBox Style="{StaticResource DieBondingComboStyle}"
SelectedValuePath="Value"
ItemsSource="{StaticResource TransPathType}"
SelectedValue="{Binding DieBondingManualSysItem.TransPathType, Mode=TwoWay}"
DisplayMemberPath="Description"/>
</StackPanel>
<StackPanel Style="{StaticResource ProcessParameterRowStyle}">
<TextBlock Text="{DynamicResource DieBondingPadRowDirectionLabel}" Style="{StaticResource DieBondingTextStyle}"/>
<ComboBox Style="{StaticResource DieBondingComboStyle}"
SelectedValuePath="Value"
ItemsSource="{StaticResource DieTransferRowTraversalStrategy}"
SelectedValue="{Binding DieBondingManualSysItem.PadRowDirectionStrategy, Mode=TwoWay}"
DisplayMemberPath="Description"/>
</StackPanel>
<StackPanel Style="{StaticResource ProcessParameterRowStyle}">
<TextBlock Text="{DynamicResource DieBondingDieRowDirectionLabel}" Style="{StaticResource DieBondingTextStyle}"/>
<ComboBox Style="{StaticResource DieBondingComboStyle}"
SelectedValuePath="Value"
ItemsSource="{StaticResource DieTransferRowTraversalStrategy}"
SelectedValue="{Binding DieBondingManualSysItem.DieRowDirectionStrategy, Mode=TwoWay}"
DisplayMemberPath="Description"/>
</StackPanel>
<StackPanel Style="{StaticResource ProcessParameterRowStyle}">
<TextBlock Text="{DynamicResource DieBondingAutoCalculateDieLabel}" Style="{StaticResource DieBondingTextStyle}"/>
<ToggleButton Style="{StaticResource ProcessSmallToggleStyle}" Content="{DynamicResource EnabledLabel}"
IsChecked="{Binding DieBondingManualSysItem.AutoDieRegionModeEnable, Mode=TwoWay}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="1" Margin="0,8,0,0">
<Button Content="{DynamicResource Start}" Click="{mw:Action StartProcess}" Style="{StaticResource StartButtonStyle}" Margin="5"
IsEnabled="{Binding Runner.IsRunning, Converter={StaticResource boolToInversionConverter}}" />
<Button Content="{DynamicResource Stop}" Click="{mw:Action StopProcess}" Style="{StaticResource StopButtonStyle }" Margin="5"
IsEnabled="{Binding Runner.IsRunning}" />
<Button Content="{DynamicResource Save}" Click="{mw:Action SaveDieBondingSetAsync}" Style="{StaticResource SaveButtonStyle}" Margin="5"
IsEnabled="{Binding Runner.IsRunning, Converter={StaticResource boolToInversionConverter}}" />
</StackPanel>
</Grid>
</TabItem>
<TabItem Header="{DynamicResource DieBondingPath}">
<Grid Margin="4">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid Style="{StaticResource ProcessDataGridStyle}"
ItemsSource="{Binding DieTransferPathItems}"
SelectedItem="{Binding SelectedDieTransferPathItem, Mode=TwoWay}"
IsReadOnly="True"
SelectionUnit="FullRow"
SelectionMode="Single"
LoadingRow="DataGrid_LoadingRow">
<DataGrid.Columns>
<DataGridTextColumn Header="{DynamicResource DieBondingSequenceNumberHeader}" Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow},
Path=Header}" Width="*"/>
<DataGridTextColumn Header="{DynamicResource DieBondingSubstrateRowHeader}" Binding="{Binding PadRow}" Width="*"/>
<DataGridTextColumn Header="{DynamicResource DieBondingSubstrateColumnHeader}" Binding="{Binding PadColumn}" Width="*"/>
<DataGridTextColumn Header="{DynamicResource DieBondingWaferRowHeader}" Binding="{Binding DieRow}" Width="*"/>
<DataGridTextColumn Header="{DynamicResource DieBondingWaferColumnHeader}" Binding="{Binding DieColumn}" Width="*"/>
<DataGridTextColumn Header="{DynamicResource DieBondingTypeHeader}" Binding="{Binding TransPathType, Converter={StaticResource EnumDescriptionConverter}}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
<custom:PaginationControl DataContext="{Binding Pagination}" Grid.Row="1" Margin="0,10,0,0"/>
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal" Margin="2" Grid.Row="2">
<Button Style="{StaticResource DieBondingPathButtonStyle}"
Content="{DynamicResource DieBondingMoveToDieButton}"
IsEnabled="{Binding SelectedDieTransferPathItem, Converter={StaticResource ObjectToBoolConverter}}" />
<Button Style="{StaticResource DieBondingPathButtonStyle}"
Content="{DynamicResource DieBondingMoveToPadButton}"
IsEnabled="{Binding SelectedDieTransferPathItem, Converter={StaticResource ObjectToBoolConverter}}" />
<Button Style="{StaticResource DieBondingPathButtonStyle}"
Content="{DynamicResource DieBondingAlignDieToPadButton}"
IsEnabled="{Binding SelectedDieTransferPathItem, Converter={StaticResource ObjectToBoolConverter}}" />
<Button Style="{StaticResource DieBondingPathButtonStyle}" Content="{DynamicResource DieBondingGeneratePathButton}"/>
</StackPanel>
</Grid>
</TabItem>
</TabControl>
</Grid>
</UserControl>

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
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;
namespace MainShell.Manual.View
{
/// <summary>
/// DieBondingView.xaml 的交互逻辑
/// </summary>
public partial class DieBondingView : UserControl
{
public DieBondingView()
{
InitializeComponent();
}
private void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
// 每次行加载时,将当前索引 + 1 赋值给 Header
e.Row.Header = (e.Row.GetIndex() + 1).ToString();
}
}
}

View File

@@ -0,0 +1,147 @@
<UserControl x:Class="MainShell.Manual.View.DiePositionView"
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:mw="http://www.maxwell-gp.com/"
xmlns:custom="clr-namespace:MainShell.Resources.CustomControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="DiePositionStatLabelStyle" TargetType="TextBlock" BasedOn="{StaticResource ProcessReadOnlyValueTextStyle}">
<Setter Property="Width" Value="118"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="DiePositionStatValueStyle" TargetType="TextBox" BasedOn="{StaticResource ProcessTextBoxStyle}">
<Setter Property="Width" Value="88"/>
<Setter Property="MinWidth" Value="88"/>
<Setter Property="Margin" Value="8,0,0,0"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="TextAlignment" Value="Center"/>
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="Foreground" Value="#00A651"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ProcessPageBackgroundBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Style="{StaticResource ProcessCameraHostBorderStyle}">
<ContentControl Content="{Binding CameraAxisViewModel}" />
</Border>
<ScrollViewer Grid.Column="1"
Margin="0,4,4,4"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
CanContentScroll="False">
<GroupBox Header="{DynamicResource DiePosition}" Style="{StaticResource ProcessWideCardGroupBoxStyle}">
<Grid Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical">
<GroupBox Style="{StaticResource ProcessWideCardGroupBoxStyle}" MinHeight="120" Header="{DynamicResource DiePositionStatistics}">
<Grid Margin="0" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0" Margin="4,2,14,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{DynamicResource DiePositionTotalDieCountLabel}" Style="{StaticResource DiePositionStatLabelStyle}"/>
<TextBox Grid.Column="1" Text="{Binding Statistics.TotalDieCount, Mode=OneWay}" Style="{StaticResource DiePositionStatValueStyle}" HorizontalAlignment="Left"/>
</Grid>
<Grid Grid.Row="0" Grid.Column="1" Margin="4,2,0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{DynamicResource DiePositionPassRateLabel}" Style="{StaticResource DiePositionStatLabelStyle}"/>
<TextBox Grid.Column="1" Text="{Binding Statistics.PassRate, StringFormat={}{0:F2}%, Mode=OneWay}" Style="{StaticResource DiePositionStatValueStyle}" HorizontalAlignment="Left"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="0" Margin="4,2,14,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{DynamicResource DiePositionOkDieCountLabel}" Style="{StaticResource DiePositionStatLabelStyle}"/>
<TextBox Grid.Column="1" Text="{Binding Statistics.OkDieCount, Mode=OneWay}" Style="{StaticResource DiePositionStatValueStyle}" HorizontalAlignment="Left"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="1" Margin="4,2,0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{DynamicResource DiePositionNgDieCountLabel}" Style="{StaticResource DiePositionStatLabelStyle}"/>
<TextBox Grid.Column="1" Text="{Binding Statistics.NgDieCount, Mode=OneWay}" HorizontalAlignment="Left">
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource DiePositionStatValueStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Statistics.NgDieCount}" Value="0">
<Setter Property="Foreground" Value="#00A651"/>
</DataTrigger>
<DataTrigger Binding="{Binding Statistics.NgDieCount, Converter={StaticResource ObjectToBoolConverter}}" Value="True">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
<Grid Grid.Row="2" Grid.Column="0" Margin="4,2,14,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{DynamicResource DiePositionAverageSpacingXLabel}" Style="{StaticResource DiePositionStatLabelStyle}"/>
<TextBox Grid.Column="1" Text="{Binding Statistics.AverageSpacingX, StringFormat={}{0:F3}, Mode=OneWay}" Style="{StaticResource DiePositionStatValueStyle}" HorizontalAlignment="Left"/>
</Grid>
<Grid Grid.Row="2" Grid.Column="1" Margin="4,2,0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{DynamicResource DiePositionAverageSpacingYLabel}" Style="{StaticResource DiePositionStatLabelStyle}"/>
<TextBox Grid.Column="1" Text="{Binding Statistics.AverageSpacingY, StringFormat={}{0:F3}, Mode=OneWay}" Style="{StaticResource DiePositionStatValueStyle}" HorizontalAlignment="Left"/>
</Grid>
</Grid>
</GroupBox>
<GroupBox Style="{StaticResource ProcessWideCardGroupBoxStyle}" Header="{DynamicResource DiePositionMap}" Margin="0,0,0,0" Height="300">
<custom:DieMapPanelControl MapModel="{Binding DieMapModel}"
DieSize="20"
Spacing="2"
MapBackground="#E5E7EB"
MapMargin="8"
Caption=""/>
</GroupBox>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="1" Margin="0,8,0,0">
<Button Content="{DynamicResource Start}" Click="{mw:Action StartProcess}" Style="{StaticResource StartButtonStyle}" Margin="5"
IsEnabled="{Binding Runner.IsRunning, Converter={StaticResource boolToInversionConverter}}" />
<Button Content="{DynamicResource Stop}" Click="{mw:Action StopProcess}" Style="{StaticResource StopButtonStyle }" Margin="5"
IsEnabled="{Binding Runner.IsRunning}" />
</StackPanel>
</Grid>
</GroupBox>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace MainShell.Manual.View
{
/// <summary>
/// DiePositionView.xaml 的交互逻辑
/// </summary>
public partial class DiePositionView : UserControl
{
public DiePositionView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,236 @@
<UserControl x:Class="MainShell.Manual.View.DieRecheckView"
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:behavior="clr-namespace:MainShell.Common.ControlAttribute"
xmlns:custom="clr-namespace:MainShell.Resources.CustomControl"
xmlns:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="DieRecheckActionButtonStyle" TargetType="Button" BasedOn="{StaticResource CommonButtonStyle}">
<Setter Property="Margin" Value="3,2"/>
<Setter Property="MinWidth" Value="110"/>
</Style>
<Style x:Key="DieRecheckSectionTitleStyle" TargetType="TextBlock" BasedOn="{StaticResource ProcessReadOnlyValueTextStyle}">
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Margin" Value="0,0,0,6"/>
</Style>
<Style x:Key="DieRecheckThresholdCardStyle" TargetType="Border">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#D8E0EC"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="12,10"/>
<Setter Property="Margin" Value="0,0,8,0"/>
</Style>
<Style x:Key="DieRecheckThresholdLabelStyle" TargetType="TextBlock" BasedOn="{StaticResource ProcessReadOnlyValueTextStyle}">
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Margin" Value="0,0,0,6"/>
</Style>
<Style x:Key="DieRecheckRuleListBorderStyle" TargetType="Border">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="#D8E0EC"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="8"/>
</Style>
<Style x:Key="DieRecheckRuleItemBorderStyle" TargetType="Border">
<Setter Property="Padding" Value="12,10"/>
<Setter Property="BorderBrush" Value="#E7ECF4"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
</Style>
<Style x:Key="DieRecheckRuleRadioStyle" TargetType="RadioButton">
<Setter Property="FontSize" Value="13"/>
<Setter Property="Foreground" Value="#425466"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<StackPanel Orientation="Horizontal">
<Grid Width="18" Height="18" Margin="0,0,10,0">
<Ellipse Stroke="#B8C6DA" StrokeThickness="2" Fill="White"/>
<Ellipse x:Name="InnerDot" Width="8" Height="8" Fill="#3D7DFF" Visibility="Collapsed" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<ContentPresenter VerticalAlignment="Center"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="InnerDot" Property="Visibility" Value="Visible"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ProcessPageBackgroundBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Style="{StaticResource ProcessCameraHostBorderStyle}">
<ContentControl Content="{Binding CameraAxisViewModel}" />
</Border>
<ScrollViewer Grid.Column="1"
Margin="0,4,4,4"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
Style="{StaticResource ProcessScrollViewerStyle}"
CanContentScroll="False">
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<custom:RegionEditorView Title="{DynamicResource DieRecheckRegionEditorTitle}"
DataContext="{Binding RecheckRegion}"/>
<GroupBox Grid.Row="1" Header="{DynamicResource DieRecheckFunctionGroup}" Style="{StaticResource ProcessWideCardGroupBoxStyle}" Margin="0,6,0,0">
<StackPanel>
<TextBlock Text="{DynamicResource DieRecheckThresholdSectionTitle}" Style="{StaticResource DieRecheckSectionTitleStyle}"/>
<Grid Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Style="{StaticResource DieRecheckThresholdCardStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{DynamicResource DieRecheckXThresholdSetting}" Style="{StaticResource DieRecheckThresholdLabelStyle}"/>
<TextBox Style="{StaticResource ProcessTextBoxStyle}"
behavior:ControlBehavior.IsNumericOnly="True"
Text="{Binding FilterState.XThreshold, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat={}{0:F4}}"/>
</StackPanel>
</Border>
<Border Grid.Column="1" Style="{StaticResource DieRecheckThresholdCardStyle}" Margin="8,0,0,0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{DynamicResource DieRecheckYThresholdSetting}" Style="{StaticResource DieRecheckThresholdLabelStyle}"/>
<TextBox Style="{StaticResource ProcessTextBoxStyle}"
behavior:ControlBehavior.IsNumericOnly="True"
Text="{Binding FilterState.YThreshold, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, StringFormat={}{0:F4}}"/>
</StackPanel>
</Border>
</Grid>
<TextBlock Text="{DynamicResource DieRecheckRuleSectionTitle}" Style="{StaticResource DieRecheckSectionTitleStyle}"/>
<Border Style="{StaticResource DieRecheckRuleListBorderStyle}" Margin="0,0,0,6">
<StackPanel>
<Border Style="{StaticResource DieRecheckRuleItemBorderStyle}">
<RadioButton Style="{StaticResource DieRecheckRuleRadioStyle}"
GroupName="DieRecheckFilterRule"
Content="{DynamicResource DieRecheckFilterMissingBond}"
IsChecked="{Binding FilterState.IsMissingBondRuleSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Border>
<Border Style="{StaticResource DieRecheckRuleItemBorderStyle}">
<RadioButton Style="{StaticResource DieRecheckRuleRadioStyle}"
GroupName="DieRecheckFilterRule"
IsChecked="{Binding FilterState.IsXThresholdRuleSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBlock>
<Run Text="{DynamicResource DieRecheckFilterXThreshold}"/>
<Run Text=" ("/>
<Run Text="{Binding FilterState.XThreshold, StringFormat={}{0:F4}}"/>
<Run Text=")"/>
</TextBlock>
</RadioButton>
</Border>
<Border Style="{StaticResource DieRecheckRuleItemBorderStyle}">
<RadioButton Style="{StaticResource DieRecheckRuleRadioStyle}"
GroupName="DieRecheckFilterRule"
IsChecked="{Binding FilterState.IsYThresholdRuleSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBlock>
<Run Text="{DynamicResource DieRecheckFilterYThreshold}"/>
<Run Text=" ("/>
<Run Text="{Binding FilterState.YThreshold, StringFormat={}{0:F4}}"/>
<Run Text=")"/>
</TextBlock>
</RadioButton>
</Border>
<Border Padding="12,10">
<RadioButton Style="{StaticResource DieRecheckRuleRadioStyle}"
GroupName="DieRecheckFilterRule"
Content="{DynamicResource DieRecheckFilterXYThreshold}"
IsChecked="{Binding FilterState.IsXYThresholdRuleSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Border>
</StackPanel>
</Border>
<WrapPanel HorizontalAlignment="Center">
<Button Style="{StaticResource DieRecheckActionButtonStyle}"
Content="{DynamicResource DieRecheckMoveToSelectedPoint}"
Click="{mw:Action MoveToSelectedPoint}"
IsEnabled="{Binding SelectedPointResult, Converter={StaticResource ObjectToBoolConverter}}"/>
<Button Style="{StaticResource DieRecheckActionButtonStyle}"
Content="{DynamicResource DieRecheckResetFilters}"
Click="{mw:Action ResetFilters}"/>
</WrapPanel>
</StackPanel>
</GroupBox>
<GroupBox Grid.Row="2" Header="{DynamicResource DieRecheckCurrentPoints}" Style="{StaticResource ProcessWideCardGroupBoxStyle}" Margin="0,6,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid Grid.Row="0"
Style="{StaticResource ProcessDataGridStyle}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserAddRows="False"
ItemsSource="{Binding PointResults}"
SelectedItem="{Binding SelectedPointResult, Mode=TwoWay}"
SelectionUnit="FullRow"
SelectionMode="Single"
Height="320"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
MinHeight="260">
<DataGrid.Resources>
<Style TargetType="{x:Type ScrollViewer}" BasedOn="{StaticResource ProcessScrollViewerStyle}"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Width="*" Header="{DynamicResource DieRecheckStepIndex}" Binding="{Binding StepIndex}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource DieRecheckPadRow}" Binding="{Binding PadRow}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource DieRecheckPadColumn}" Binding="{Binding PadColumn}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource DieRecheckDieRow}" Binding="{Binding DieRow}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource DieRecheckDieColumn}" Binding="{Binding DieColumn}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource DieRecheckXError}" Binding="{Binding XError, StringFormat={}{0:F4}}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource DieRecheckYError}" Binding="{Binding YError, StringFormat={}{0:F4}}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource DieRecheckStatus}" Binding="{Binding StatusSummary}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource DieRecheckType}" Binding="{Binding TransPathType}"/>
</DataGrid.Columns>
</DataGrid>
<custom:PaginationControl Grid.Row="1" DataContext="{Binding Pagination}" Margin="0,10,0,0"/>
</Grid>
</GroupBox>
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0">
<Button Content="{DynamicResource Start}" Click="{mw:Action StartProcess}" Style="{StaticResource StartButtonStyle}" Margin="5"
IsEnabled="{Binding Runner.IsRunning, Converter={StaticResource boolToInversionConverter}}" />
<Button Content="{DynamicResource Stop}" Click="{mw:Action StopProcess}" Style="{StaticResource StopButtonStyle}" Margin="5"
IsEnabled="{Binding Runner.IsRunning}" />
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace MainShell.Manual.View
{
/// <summary>
/// DieRecheckView.xaml 的交互逻辑
/// </summary>
public partial class DieRecheckView : UserControl
{
public DieRecheckView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,358 @@
<UserControl x:Class="MainShell.Manual.View.LogisticsOperationView"
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:custom="clr-namespace:MainShell.Resources.CustomControl"
xmlns:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style x:Key="PanelBorderStyle" TargetType="Border">
<Setter Property="Background" Value="#F8FAFD"/>
<Setter Property="BorderBrush" Value="#D4DCE8"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="6"/>
<Setter Property="Margin" Value="6"/>
<Setter Property="Padding" Value="12"/>
</Style>
<Style x:Key="IndicatorDotStyle" TargetType="Ellipse">
<Setter Property="Width" Value="11"/>
<Setter Property="Height" Value="11"/>
<Setter Property="Fill" Value="#CBD5E1"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsOn}" Value="True">
<Setter Property="Fill" Value="#22C55E"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="PhaseCardBorderStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="#C9D6EA"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="3"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="Margin" Value="0,0,0,6"/>
<Setter Property="Background" Value="#F2F6FC"/>
</Style>
<Style x:Key="PhaseActionButtonStyle" TargetType="Button" BasedOn="{StaticResource LogisticsActionButtonStyle}">
<Setter Property="Width" Value="190"/>
<Setter Property="Height" Value="36"/>
<Setter Property="Margin" Value="0,4,0,0"/>
</Style>
</UserControl.Resources>
<Grid Background="#EDF1F7">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="290"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Style="{StaticResource PanelBorderStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding State.SubstrateLoad.Title}" FontWeight="Bold" FontSize="18" Foreground="#0F3B7A"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
<Ellipse Width="12" Height="12" Fill="#22C55E" Margin="0,0,6,0"/>
</StackPanel>
</Grid>
<Separator Grid.Row="1" Margin="0,8,0,10"/>
<ItemsControl Grid.Row="2" ItemsSource="{Binding State.SubstrateLoad.Indicators}" VerticalAlignment="Top">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Foreground="#20354C"/>
<Ellipse Style="{StaticResource IndicatorDotStyle}" Margin="6,0,0,0"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Grid.Row="3" Margin="0,10,0,0">
<Button Content="{Binding State.SubstrateLoad.PrimaryActionText}" Style="{StaticResource LogisticsActionButtonStyle}"/>
<Button Content="{Binding State.SubstrateLoad.SecondaryActionText}" Style="{StaticResource LogisticsSecondaryButtonStyle}"/>
</StackPanel>
</Grid>
</Border>
<Border Grid.Column="1" Style="{StaticResource PanelBorderStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding State.ChipLoad.Title}" FontWeight="Bold" FontSize="18" Foreground="#0F3B7A"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
<CheckBox Content="无EFEM调试" IsChecked="{Binding State.IsManualWithoutEfem}" Margin="0,0,10,0" Foreground="#6B7C95"/>
</StackPanel>
</Grid>
<Separator Grid.Row="1" Margin="0,8,0,10"/>
<StackPanel Grid.Row="2" VerticalAlignment="Top">
<Border Style="{StaticResource PhaseCardBorderStyle}" IsEnabled="{Binding State.IsManualWithoutEfem, Converter={StaticResource boolToInversionConverter}}">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding State.ChipLoad.PhaseSteps[0].Title}" Foreground="#2455C7" FontWeight="Bold"/>
</Grid>
<Button Content="{Binding State.ChipLoad.PhaseSteps[0].ActionText}"
Style="{StaticResource PhaseActionButtonStyle}"
IsEnabled="{Binding State.ChipLoad.PhaseSteps[0].IsEnabled}"
Click="{mw:Action ChipLoadPhase1Async}"/>
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<TextBlock Text="料盒层数" VerticalAlignment="Center" Foreground="#5B6B83" Margin="0,0,6,0"/>
<mw:IntNumberBox Minimum="1" Maximum="25" Width="60"
Value="{Binding State.ChipLoad.PhaseSteps[0].TargetLayer}"
mw:NumericKeypadAttach.IsEnabled="True"/>
</StackPanel>
</StackPanel>
</Border>
<Border Style="{StaticResource PhaseCardBorderStyle}">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding State.ChipLoad.PhaseSteps[1].Title}" Foreground="#96A1B3" FontWeight="Bold"/>
</Grid>
<Button Content="{Binding State.ChipLoad.PhaseSteps[1].ActionText}"
Style="{StaticResource PhaseActionButtonStyle}"
IsEnabled="{Binding State.ChipLoad.PhaseSteps[1].IsEnabled}"
Click="{mw:Action ChipLoadPhase2Async}"/>
</StackPanel>
</Border>
<ItemsControl ItemsSource="{Binding State.ChipLoad.Indicators}" VerticalAlignment="Top">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Foreground="#20354C"/>
<Ellipse Style="{StaticResource IndicatorDotStyle}" Margin="6,0,0,0"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<StackPanel Grid.Row="3" Margin="0,8,0,0">
<Button Content="{Binding State.ChipLoad.SecondaryActionText}" Style="{StaticResource LogisticsSecondaryButtonStyle}"/>
</StackPanel>
</Grid>
</Border>
<Border Grid.Row="1" Style="{StaticResource PanelBorderStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding State.SubstrateOut.Title}" FontWeight="Bold" FontSize="18" Foreground="#0F3B7A"/>
<Ellipse Grid.Column="1" Width="12" Height="12" Fill="#CBD5E1" VerticalAlignment="Center"/>
</Grid>
<Separator Grid.Row="1" Margin="0,8,0,10"/>
<ItemsControl Grid.Row="2" ItemsSource="{Binding State.SubstrateOut.Indicators}" VerticalAlignment="Top">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Foreground="#20354C"/>
<Ellipse Style="{StaticResource IndicatorDotStyle}" Margin="6,0,0,0"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Grid.Row="3" Margin="0,10,0,0">
<Button Content="{Binding State.SubstrateOut.PrimaryActionText}" Style="{StaticResource LogisticsOutlineActionButtonStyle}"/>
<Button Content="{Binding State.SubstrateOut.SecondaryActionText}" Style="{StaticResource LogisticsSecondaryButtonStyle}"/>
</StackPanel>
</Grid>
</Border>
<Border Grid.Row="1" Grid.Column="1" Style="{StaticResource PanelBorderStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding State.ChipUnload.Title}" FontWeight="Bold" FontSize="18" Foreground="#0F3B7A"/>
</Grid>
<Separator Grid.Row="1" Margin="0,8,0,10"/>
<StackPanel Grid.Row="2" VerticalAlignment="Top">
<Border Style="{StaticResource PhaseCardBorderStyle}">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding State.ChipUnload.PhaseSteps[0].Title}" Foreground="#D17A00" FontWeight="Bold"/>
</Grid>
<Button Content="{Binding State.ChipUnload.PhaseSteps[0].ActionText}"
Style="{StaticResource LogisticsWarningActionButtonStyle}"
IsEnabled="{Binding State.ChipUnload.PhaseSteps[0].IsEnabled}"
Click="{mw:Action ChipUnloadPhase1Async}"/>
</StackPanel>
</Border>
<Border Style="{StaticResource PhaseCardBorderStyle}" IsEnabled="{Binding State.IsManualWithoutEfem, Converter={StaticResource boolToInversionConverter}}">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding State.ChipUnload.PhaseSteps[1].Title}" Foreground="#96A1B3" FontWeight="Bold"/>
</Grid>
<Button Content="{Binding State.ChipUnload.PhaseSteps[1].ActionText}"
Style="{StaticResource PhaseActionButtonStyle}"
IsEnabled="{Binding State.ChipUnload.PhaseSteps[1].IsEnabled}"
Click="{mw:Action ChipUnloadPhase2Async}"/>
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
<TextBlock Text="料盒层数" VerticalAlignment="Center" Foreground="#5B6B83" Margin="0,0,6,0"/>
<mw:IntNumberBox Minimum="1" Maximum="25" Width="60"
Value="{Binding State.ChipUnload.PhaseSteps[1].TargetLayer}"
mw:NumericKeypadAttach.IsEnabled="True"/>
</StackPanel>
</StackPanel>
</Border>
<ItemsControl ItemsSource="{Binding State.ChipUnload.Indicators}" VerticalAlignment="Top">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="2"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,4">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Foreground="#20354C"/>
<Ellipse Style="{StaticResource IndicatorDotStyle}" Margin="6,0,0,0"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<StackPanel Grid.Row="3" Margin="0,8,0,0">
<Button Content="{Binding State.ChipUnload.SecondaryActionText}" Style="{StaticResource LogisticsSecondaryButtonStyle}"/>
</StackPanel>
</Grid>
</Border>
</Grid>
<Grid Grid.Column="1" Margin="6,8,8,8">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="120"/>
</Grid.RowDefinitions>
<custom:MagazineMapControl MapViewModel="{Binding State.MagazineMap}"/>
<Button Grid.Row="1"
Content="自动扫码 SCAN"
Style="{StaticResource LogisticsScanButtonStyle}"
IsEnabled="{Binding State.IsScanning, Converter={StaticResource boolToInversionConverter}}"
Click="{mw:Action ScanAsync}"/>
<Grid Grid.Row="2" Margin="0,0,0,8" Visibility="{Binding State.IsScanning, Converter={StaticResource BoolToVisibleConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="扫码中..." Foreground="#1A2A47" FontWeight="Bold" Margin="2,0,2,4"/>
<ProgressBar Grid.Row="1" Height="10" Minimum="0" Maximum="100" Value="{Binding State.ScanProgress}"/>
</Grid>
<Border Grid.Row="3" Background="#07152F" BorderBrush="#2B3E63" BorderThickness="1" CornerRadius="4" Padding="10">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding State.FeedbackTitle}" Foreground="#42A5F5" FontWeight="Bold" FontStyle="Italic"/>
<TextBlock Grid.Column="1" Text="{Binding State.LastFeedbackTime}" Foreground="#58A6FF"/>
</Grid>
<ItemsControl Grid.Row="1" Margin="0,8,0,0" ItemsSource="{Binding State.FeedbackRecords}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="#D6E2F5" TextWrapping="Wrap" Margin="0,0,0,2">
<Run Text="["/>
<Run Text="{Binding Time, StringFormat={}{0:HH:mm:ss}}"/>
<Run Text="] ["/>
<Run Text="{Binding Level}"/>
<Run Text="] "/>
<Run Text="{Binding Message}"/>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Border>
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
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;
namespace MainShell.Manual.View
{
/// <summary>
/// LogisticsOperationView.xaml 的交互逻辑
/// </summary>
public partial class LogisticsOperationView : UserControl
{
public LogisticsOperationView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,30 @@
<UserControl x:Class="MainShell.Manual.View.ManualOperateView"
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:MainShell.Manual.View" xmlns:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<mw:SideMenu ItemsSource="{Binding MenuItemWraps}"
SelectedItem="{Binding SelectedMenuItem, Mode=TwoWay}"
Grid.Row ="0" Width="150"
Margin="-40,0,0,0"
ExpandMode="Accordion"
AutoSelect="True"
>
<mw:SideMenu.ItemContainerStyle>
<Style TargetType="{x:Type mw:SideMenuItem}" BasedOn="{StaticResource SideMenuItemHeaderAccordionBaseStyle}">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="Tag" Value="{Binding Tag}" />
</Style>
</mw:SideMenu.ItemContainerStyle>
</mw:SideMenu>
<ContentControl Margin="5,0" Grid.Column="1" mw:View.Model="{Binding CurrentScreen}"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
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;
namespace MainShell.Manual.View
{
/// <summary>
/// ManualOperateView.xaml 的交互逻辑
/// </summary>
public partial class ManualOperateView : UserControl
{
public ManualOperateView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,136 @@
<UserControl x:Class="MainShell.Manual.View.SubstrateHeightMeasureView"
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:mw="http://www.maxwell-gp.com/"
xmlns:recipe="clr-namespace:MainShell.Recipe.Models.SubstrateParameter"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="SubstrateHeightMeasureSummaryLabelStyle" TargetType="TextBlock" BasedOn="{StaticResource ProcessReadOnlyValueTextStyle}">
<Setter Property="Width" Value="126"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="SubstrateHeightMeasureSummaryValueStyle" TargetType="TextBox" BasedOn="{StaticResource ProcessTextBoxStyle}">
<Setter Property="MinWidth" Value="110"/>
<Setter Property="Margin" Value="8,0,0,0"/>
<Setter Property="IsReadOnly" Value="True"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ProcessPageBackgroundBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Style="{StaticResource ProcessCameraHostBorderStyle}">
<ContentControl Content="{Binding CameraAxisViewModel}" />
</Border>
<ScrollViewer Grid.Column="1"
Margin="0,4,4,4"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
Style="{StaticResource ProcessScrollViewerStyle}"
CanContentScroll="False">
<Grid Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Header="{DynamicResource SubstrateHeightMeasureManualSummary}" Style="{StaticResource ProcessWideCardGroupBoxStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0" Margin="4,2,14,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{DynamicResource SubstrateHeightMeasureManualRecipeName}" Style="{StaticResource SubstrateHeightMeasureSummaryLabelStyle}"/>
<TextBox Grid.Column="1" Text="{Binding RecipeName, Mode=OneWay}" Style="{StaticResource SubstrateHeightMeasureSummaryValueStyle}" HorizontalAlignment="Left"/>
</Grid>
<Grid Grid.Row="0" Grid.Column="1" Margin="4,2,0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{DynamicResource SubstrateHeightMeasureManualMode}" Style="{StaticResource SubstrateHeightMeasureSummaryLabelStyle}"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left">
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource SubstrateHeightMeasureSummaryValueStyle}">
<Setter Property="Text" Value="{DynamicResource SubstrateHeightMeasureManualTeachMode}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentMeasureMode}" Value="{x:Static recipe:SubstrateHeightMeasureMode.RowColumnOffset}">
<Setter Property="Text" Value="{DynamicResource SubstrateHeightMeasureManualRowColumnMode}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
<Grid Grid.Row="1" Grid.Column="0" Margin="4,2,14,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{DynamicResource SubstrateHeightMeasureManualConfiguredCount}" Style="{StaticResource SubstrateHeightMeasureSummaryLabelStyle}"/>
<TextBox Grid.Column="1" Text="{Binding ConfiguredPointCount, Mode=OneWay}" Style="{StaticResource SubstrateHeightMeasureSummaryValueStyle}" HorizontalAlignment="Left"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="1" Margin="4,2,0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{DynamicResource SubstrateHeightMeasureManualMeasuredCount}" Style="{StaticResource SubstrateHeightMeasureSummaryLabelStyle}"/>
<TextBox Grid.Column="1" Text="{Binding MeasuredPointCount, Mode=OneWay}" Style="{StaticResource SubstrateHeightMeasureSummaryValueStyle}" HorizontalAlignment="Left"/>
</Grid>
</Grid>
</GroupBox>
<GroupBox Grid.Row="1" Header="{DynamicResource SubstrateHeightMeasureManualResultList}" Style="{StaticResource ProcessWideCardGroupBoxStyle}" Margin="0,8,0,0">
<DataGrid Style="{StaticResource ProcessDataGridStyle}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserAddRows="False"
ItemsSource="{Binding PointResults}"
MinHeight="260">
<DataGrid.Columns>
<DataGridTextColumn Width="1.4*" Header="{DynamicResource SubstrateHeightMeasureManualPointName}" Binding="{Binding PointName}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource SubstrateHeightMeasureManualRow}" Binding="{Binding RowIndex}"/>
<DataGridTextColumn Width="*" Header="{DynamicResource SubstrateHeightMeasureManualColumn}" Binding="{Binding ColumnIndex}"/>
<DataGridTextColumn Width="1.2*" Header="{DynamicResource SubstrateHeightMeasureManualPositionX}" Binding="{Binding PositionX, StringFormat={}{0:F4}}"/>
<DataGridTextColumn Width="1.2*" Header="{DynamicResource SubstrateHeightMeasureManualPositionY}" Binding="{Binding PositionY, StringFormat={}{0:F4}}"/>
<DataGridTextColumn Width="1.2*" Header="{DynamicResource SubstrateHeightMeasureManualHeightValue}" Binding="{Binding HeightValue, StringFormat={}{0:F4}}"/>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0">
<Button Content="{DynamicResource Start}" Click="{mw:Action StartProcess}" Style="{StaticResource StartButtonStyle}" Margin="5"
IsEnabled="{Binding Runner.IsRunning, Converter={StaticResource boolToInversionConverter}}" />
<Button Content="{DynamicResource Stop}" Click="{mw:Action StopProcess}" Style="{StaticResource StopButtonStyle}" Margin="5"
IsEnabled="{Binding Runner.IsRunning}" />
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,15 @@
using System.Windows.Controls;
namespace MainShell.Manual.View
{
/// <summary>
/// SubstrateHeightMeasureView.xaml 的交互逻辑
/// </summary>
public partial class SubstrateHeightMeasureView : UserControl
{
public SubstrateHeightMeasureView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,186 @@
<UserControl x:Class="MainShell.Manual.View.SubstratePositionView"
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:mw="http://www.maxwell-gp.com/"
xmlns:custom="clr-namespace:MainShell.Resources.CustomControl"
mc:Ignorable="d"
d:DesignHeight="650" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="SubstratePositionPageTitleStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="17"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Foreground" Value="{StaticResource ProcessTitleForegroundBrush}"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,0,0,12"/>
</Style>
<Style x:Key="SubstratePositionSectionGroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource ProcessWideCardGroupBoxStyle}">
<Setter Property="Margin" Value="0,0,0,14"/>
<Setter Property="Padding" Value="18,12,18,14"/>
</Style>
<Style x:Key="SubstratePositionHeaderStretchGroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource {x:Type GroupBox}}">
<Setter Property="Background" Value="{StaticResource ProcessCardBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource ProcessCardBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{StaticResource ProcessTitleForegroundBrush}"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Margin" Value="0,0,0,14"/>
<Setter Property="Padding" Value="18,12,18,14"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupBox">
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="38"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="{StaticResource ProcessHeaderBackgroundBrush}" BorderBrush="{StaticResource ProcessCardBorderBrush}" BorderThickness="0,0,0,1" CornerRadius="3,3,0,0" Padding="12,0">
<ContentPresenter ContentSource="Header" HorizontalAlignment="Stretch" VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
<Border Grid.Row="1" Background="{StaticResource ProcessCardBackgroundBrush}" Padding="{TemplateBinding Padding}" CornerRadius="0,0,3,3">
<ContentPresenter/>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SubstratePositionEditorTextStyle" TargetType="TextBlock" BasedOn="{StaticResource ProcessReadOnlyValueTextStyle}">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,0,10,0"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
<Style x:Key="SubstratePositionAngleValueStyle" TargetType="mw:NumberBox" BasedOn="{StaticResource ProcessLargeNumberBoxStyle}">
<Setter Property="Width" Value="154"/>
<Setter Property="Margin" Value="0"/>
</Style>
<Style x:Key="SubstratePositionEditorNumberBoxStyle" TargetType="mw:IntNumberBox" BasedOn="{StaticResource ProcessIntNumberBoxStyle}">
<Setter Property="Width" Value="78"/>
<Setter Property="Margin" Value="0"/>
</Style>
<Style x:Key="SubstratePositionHeaderCheckBoxStyle" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="Foreground" Value="{StaticResource ProcessBodyForegroundBrush}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="SubstratePositionMapHostStyle" TargetType="Border">
<Setter Property="Background" Value="#EEF3F8"/>
<Setter Property="BorderBrush" Value="{StaticResource ProcessCardBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="2"/>
<Setter Property="Padding" Value="16"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ProcessPageBackgroundBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Style="{StaticResource ProcessCameraHostBorderStyle}">
<ContentControl Content="{Binding CameraAxisViewModel}" IsEnabled="{Binding Runner.IsRunning,Converter={StaticResource boolToInversionConverter}}"/>
</Border>
<ScrollViewer Grid.Column="1"
Margin="10,8,14,10"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
Style="{StaticResource ProcessScrollViewerStyle}"
CanContentScroll="False">
<Grid MinWidth="520">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<TextBlock Style="{StaticResource SubstratePositionPageTitleStyle}" Text="{DynamicResource SubstratePosition}"/>
<Border Height="1" Background="{StaticResource ProcessCardBorderBrush}" Margin="0,0,0,12"/>
</StackPanel>
<GroupBox Grid.Row="1" Header="{DynamicResource MarkResult}" Style="{StaticResource SubstratePositionSectionGroupBoxStyle}">
<DataGrid Style="{StaticResource ProcessDataGridStyle}" AutoGenerateColumns="False" IsReadOnly="True" ItemsSource="{Binding MarkResults}" MinHeight="86">
<DataGrid.Columns>
<DataGridTextColumn Width="*" Header="{DynamicResource ID}" Binding="{Binding Id}"/>
<DataGridTextColumn Width="1.75*" Header="{DynamicResource BaseX}" Binding="{Binding BasePose.X}"/>
<DataGridTextColumn Width="1.75*" Header="{DynamicResource BaseY}" Binding="{Binding BasePose.Y}"/>
<DataGridTextColumn Width="1.75*" Header="{DynamicResource ResultX}" Binding="{Binding ResultPose.X}"/>
<DataGridTextColumn Width="1.75*" Header="{DynamicResource ResultY}" Binding="{Binding ResultPose.Y}"/>
</DataGrid.Columns>
</DataGrid>
</GroupBox>
<GroupBox Grid.Row="2" Header="{DynamicResource SubstrateAngle}" Style="{StaticResource SubstratePositionSectionGroupBoxStyle}">
<Grid>
<mw:NumberBox Style="{StaticResource SubstratePositionAngleValueStyle}" IsReadOnly="True" Value="{Binding SubstrateAngle}" HorizontalAlignment="Center"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="3" Header="{DynamicResource VerifiyResult}" Style="{StaticResource SubstratePositionSectionGroupBoxStyle}"
IsEnabled="{Binding Runner.IsRunning, Converter={StaticResource boolToInversionConverter}}">
<Grid HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Style="{StaticResource SubstratePositionEditorTextStyle}" Text="{DynamicResource SubstrateRow}"/>
<mw:IntNumberBox Grid.Column="1" Style="{StaticResource SubstratePositionEditorNumberBoxStyle}" Minimum="1" Maximum="{Binding RowMax}" Value="{Binding RowIndex}" mw:NumericKeypadAttach.IsEnabled="True"/>
<TextBlock Grid.Column="2" Style="{StaticResource SubstratePositionEditorTextStyle}" Text="{DynamicResource SubstrateCol}" Margin="26,0,10,0"/>
<mw:IntNumberBox Grid.Column="3" Style="{StaticResource SubstratePositionEditorNumberBoxStyle}" Minimum="1" Maximum="{Binding ColMax}" Value="{Binding ColumnIndex}" mw:NumericKeypadAttach.IsEnabled="True"/>
<Button Grid.Column="4" Style="{StaticResource AxisMoveButtonStyle}" Click="{mw:Action MoveToSelectPad}" Content="{DynamicResource Move}" Margin="26,0,0,0"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="4" Style="{StaticResource SubstratePositionHeaderStretchGroupBoxStyle}">
<GroupBox.Header>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Text="{DynamicResource SubstrateMap}" VerticalAlignment="Center" Foreground="{StaticResource ProcessTitleForegroundBrush}" FontWeight="SemiBold"/>
<CheckBox Grid.Column="2" Style="{StaticResource SubstratePositionHeaderCheckBoxStyle}" Content="{DynamicResource AllowClickSelection}" IsChecked="{Binding IsClickEnabled}" HorizontalAlignment="Right"/>
</Grid>
</GroupBox.Header>
<custom:DieMapPanelControl MinHeight="286"
MaxHeight="560"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MapModel="{Binding SubstrateMapModel}"
DieSize="15"
Spacing="2"
MapBackground="#EEF3F8"
MapMargin="16"
Caption=""
DieClickedCommand="{Binding DieClickedCommand}"/>
</GroupBox>
<StackPanel Grid.Row="5" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,4,0,0">
<Button Content="{DynamicResource Start}" Click="{mw:Action StartProcess}" Style="{StaticResource StartButtonStyle}" Margin="5"
IsEnabled="{Binding Runner.IsRunning, Converter={StaticResource boolToInversionConverter}}" />
<Button Content="{DynamicResource Stop}" Click="{mw:Action StopProcess}" Style="{StaticResource StopButtonStyle }" Margin="5"
IsEnabled="{Binding Runner.IsRunning}" />
</StackPanel>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
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.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MainShell.Manual.View
{
/// <summary>
/// SubstratePositionView.xaml 的交互逻辑
/// </summary>
public partial class SubstratePositionView : UserControl
{
public SubstratePositionView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,39 @@
<UserControl x:Class="MainShell.Manual.View.WaferAngleAdjustmentView"
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:MainShell.Manual.View" xmlns:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ContentControl Content="{Binding CameraAxisViewModel}" />
<GroupBox Grid.Column="1" Header="{DynamicResource WaferAngleAdjustment}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Margin="5">
<GroupBox Style="{StaticResource GroupBoxSecondary}" MinHeight="120" Header="{DynamicResource WaferAngleResult}">
<StackPanel Margin="5" Orientation="Horizontal" HorizontalAlignment="Center">
<mw:NumberBox IsReadOnly="True" Value="{Binding SubstrateAngle}" Width="120"/>
</StackPanel>
</GroupBox>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Grid.Row="1">
<!-- 开始按钮:运行时禁用 -->
<Button Content="{DynamicResource Start}" Click="{mw:Action StartProcess}" Style="{StaticResource StartButtonStyle}" Margin="5"
IsEnabled="{Binding Runner.IsRunning, Converter={StaticResource boolToInversionConverter}}" />
<!-- 停止按钮:仅运行时启用 -->
<Button Content="{DynamicResource Stop}" Click="{mw:Action StopProcess}" Style="{StaticResource StopButtonStyle }" Margin="5"
IsEnabled="{Binding Runner.IsRunning}" />
</StackPanel>
</Grid>
</GroupBox>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
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;
namespace MainShell.Manual.View
{
/// <summary>
/// WaferAngleAdjustmentView.xaml 的交互逻辑
/// </summary>
public partial class WaferAngleAdjustmentView : UserControl
{
public WaferAngleAdjustmentView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,61 @@
using MainShell.Common;
using MainShell.EventArgsFolder;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Recipe.Models;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class ChipStraighteningViewModel : OperateViewModelBase,IHandle<ChipAngleEventArgs>
{
private double _chipAngle;
public double ChipAngle
{
get { return _chipAngle; }
set { SetAndNotify(ref _chipAngle, value); }
}
private readonly HardwareManager _hardwareManager;
private readonly IEventAggregator _eventAggregator;
private readonly RecipeManager _recipeManager;
public ChipStraighteningViewModel(HardwareManager hardwareManager, IEventAggregator eventAggregator, RecipeManager recipeManager)
{
_hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
eventAggregator.Subscribe(this);
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = _hardwareManager.CameraAxisManager.TopCameraAxisDevices;
}
// 手动启动流程
public async Task StartProcess()
{
var context = new MW.WorkFlow.WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
await RunManualActivityAsync(new MainShell.Process.ChipStraighteningActivity("ChipStraightening"), context);
}
public override async Task StopProcess()
{
await base.StopProcess();
}
public void Handle(ChipAngleEventArgs message)
{
Stylet.Execute.OnUIThread(() =>
{
ChipAngle = message.Angle;
});
}
}
}

View File

@@ -0,0 +1,246 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Models;
using MainShell.Models.Wafer;
using MainShell.ParaSetting.Model;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.BaseBoard.Model;
using MainShell.Recipe.Models;
using MainShell.Resources.CustomControl;
using MaxwellFramework.Core.Interfaces;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class DieBondingViewModel : OperateViewModelBase
{
private readonly RecipeManager _recipeManager;
private readonly IEventAggregator _eventAggregator; // 引用 EventAggregator
private readonly ProcessResultManager _processResultManager;
private readonly HardwareManager _hardwareManager;
private readonly IDieTransferPathGenerator _dieTransferPathGenerator;
private List<DieTransferPathItem> _allDieTransferPathItems = new List<DieTransferPathItem>();
private ObservableCollection<DieTransferPathItem> _dieTransferPathItems = new ObservableCollection<DieTransferPathItem>();
/// <summary>
/// 界面上显示的传输路径集合
/// </summary>
public ObservableCollection<DieTransferPathItem> DieTransferPathItems
{
get { return _dieTransferPathItems; }
set { SetAndNotify(ref _dieTransferPathItems, value); }
}
private DieTransferPathItem _selectedDieTransferPathItem;
public DieTransferPathItem SelectedDieTransferPathItem
{
get => _selectedDieTransferPathItem;
set => SetAndNotify(ref _selectedDieTransferPathItem, value);
}
private readonly DieBondingManualSetting _dieBondingManualSetting;
public DieBondingManualSysItem DieBondingManualSysItem => _dieBondingManualSetting.DieBondingManualSysItem;
public PaginationViewModel Pagination { get; } = new PaginationViewModel();
public DieBondingViewModel(IEventAggregator eventAggregator, RecipeManager recipeManager,
ProcessResultManager processResultManager, HardwareManager hardwareManager, IDieTransferPathGenerator dieTransferPathGenerator, IParameterManager parameterManager)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
_hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
_dieTransferPathGenerator = dieTransferPathGenerator ?? throw new ArgumentNullException(nameof(dieTransferPathGenerator));
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
var _paramlist = parameterManager as IParamList;
_dieBondingManualSetting = _paramlist.GetParameter<DieBondingManualSetting>();
Pagination.PageChanged += (s, e) => LoadData();
LoadData();
}
private void LoadData()
{
// 如果数据源为空,清空并返回
if (_allDieTransferPathItems == null || !_allDieTransferPathItems.Any())
{
DieTransferPathItems = new ObservableCollection<DieTransferPathItem>();
Pagination.TotalItems = 0;
return;
}
// 1. 先更新总数这很重要因为TotalPages依赖它
Pagination.TotalItems = _allDieTransferPathItems.Count();
// 2. 自动修正页码:如果当前页超过了新的总页数,强制跳到最后一页
// 注意:修改 Pagination.CurrentPage 会再次触发 PageChanged -> LoadData
// 所以我们需要防止无限递归,或者只在必要时修正。
// 在这里,简单的方式是计算 offset 时进行钳制,而不强行修改 CurrentPage 属性(取决于你的需求)
// 或者更好的是:
if (Pagination.CurrentPage > Pagination.TotalItems && Pagination.TotalItems > 0)
{
// 如果这里修改 CurrentPage 触发 LoadData则本次 LoadData 可以直接中断
Pagination.CurrentPage = Pagination.TotalPages;
return;
}
// 3. 安全计算 Offset
int safePage = Math.Min(Pagination.CurrentPage, Pagination.TotalPages);
safePage = Math.Max(1, safePage); // 确保至少为1
int offset = (safePage - 1) * Pagination.PageSize;
int limit = Pagination.PageSize;
// 4. 重置集合
DieTransferPathItems = new ObservableCollection<DieTransferPathItem>(_allDieTransferPathItems.Skip(offset).Take(limit));
}
public async Task SaveDieBondingSetAsync()
{
bool success = await Loading.RunAsync(async (token, progress) =>
{
_dieBondingManualSetting.Write();
}, "保存参数...",isIndeterminate:true,canCancel:false);
}
public Task GenerateTransPathAsync()
{
DieTransferPathRequest pathRequest = CreatePathRequest();
DieTransferPathPlan pathPlan = _dieTransferPathGenerator.Generate(pathRequest);
_allDieTransferPathItems = ConvertToPathItems(pathPlan);
Pagination.CurrentPage = 1;
LoadData();
return Task.CompletedTask;
}
private DieTransferPathRequest CreatePathRequest()
{
DieTransferPathRequest pathRequest = new DieTransferPathRequest();
pathRequest.DieRegion = DieBondingManualSysItem.DieRegion;
pathRequest.SubstrateRegion = DieBondingManualSysItem.SubstrateRegion;
pathRequest.TransPathType = DieBondingManualSysItem.TransPathType;
pathRequest.PadRowDirectionStrategy = DieBondingManualSysItem.PadRowDirectionStrategy;
pathRequest.DieRowDirectionStrategy = DieBondingManualSysItem.DieRowDirectionStrategy;
pathRequest.SkipNgDie = true;
pathRequest.DieCandidates = CreateDieCandidates(DieBondingManualSysItem.DieRegion);
pathRequest.PadCandidates = CreatePadCandidates(DieBondingManualSysItem.SubstrateRegion);
return pathRequest;
}
private List<Die> CreateDieCandidates(RegionModel region)
{
List<Die> dieCandidates = new List<Die>();
if (region == null)
{
return dieCandidates;
}
for (int row = region.StartRow; row <= region.EndRow; row++)
{
for (int column = region.StartCol; column <= region.EndCol; column++)
{
Die die = new Die();
die.Row = row;
die.Column = column;
die.X = column;
die.Y = row;
die.Status = DieStatus.Normal;
dieCandidates.Add(die);
}
}
return dieCandidates;
}
private List<Pad> CreatePadCandidates(RegionModel region)
{
List<Pad> padCandidates = new List<Pad>();
if (region == null)
{
return padCandidates;
}
for (int row = region.StartRow; row <= region.EndRow; row++)
{
for (int column = region.StartCol; column <= region.EndCol; column++)
{
Pad pad = new Pad();
pad.Row = row;
pad.Column = column;
pad.X = column;
pad.Y = row;
SubstratePoint substratePoint;
if (TryGetSubstratePoint(row, column, out substratePoint))
{
pad.X = substratePoint.X;
pad.Y = substratePoint.Y;
}
padCandidates.Add(pad);
}
}
return padCandidates;
}
private bool TryGetSubstratePoint(int row, int column, out SubstratePoint substratePoint)
{
substratePoint = default(SubstratePoint);
SubstratePoint[,] substratePoints = _recipeManager.CurrentSubstrateRecipe != null
? _recipeManager.CurrentSubstrateRecipe.SubstratePoints
: null;
if (substratePoints == null)
{
return false;
}
int rowIndex = row - 1;
int columnIndex = column - 1;
if (rowIndex < 0 || rowIndex >= substratePoints.GetLength(0) || columnIndex < 0 || columnIndex >= substratePoints.GetLength(1))
{
return false;
}
substratePoint = substratePoints[rowIndex, columnIndex];
return true;
}
private static List<DieTransferPathItem> ConvertToPathItems(DieTransferPathPlan pathPlan)
{
List<DieTransferPathItem> pathItems = new List<DieTransferPathItem>();
if (pathPlan == null || pathPlan.Steps == null)
{
return pathItems;
}
foreach (DieTransferPathStep pathStep in pathPlan.Steps)
{
DieTransferPathItem pathItem = DieTransferPathItem.FromPathStep(pathStep);
if (pathItem != null)
{
pathItems.Add(pathItem);
}
}
return pathItems;
}
}
}

View File

@@ -0,0 +1,182 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Models;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using MainShell.Resources.CustomControl;
using MaxwellFramework.Core.Interfaces;
using Stylet;
using System;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class DieStatisticsModel : PropertyChangedBase
{
private int _totalDieCount;
public int TotalDieCount
{
get => _totalDieCount;
set
{
if (SetAndNotify(ref _totalDieCount, value))
{
UpdatePassRate();
}
}
}
private int _okDieCount;
public int OkDieCount
{
get => _okDieCount;
set
{
if (SetAndNotify(ref _okDieCount, value))
{
UpdatePassRate();
}
}
}
private int _ngDieCount;
public int NgDieCount
{
get => _ngDieCount;
set => SetAndNotify(ref _ngDieCount, value);
}
private double _averageSpacingX;
public double AverageSpacingX
{
get => _averageSpacingX;
set => SetAndNotify(ref _averageSpacingX, value);
}
private double _averageSpacingY;
public double AverageSpacingY
{
get => _averageSpacingY;
set => SetAndNotify(ref _averageSpacingY, value);
}
private double _passRate;
public double PassRate
{
get => _passRate;
private set => SetAndNotify(ref _passRate, value);
}
private void UpdatePassRate()
{
PassRate = TotalDieCount > 0 ? (double)OkDieCount / TotalDieCount * 100 : 0;
}
}
public class DiePositionViewModel : OperateViewModelBase
{
private readonly RecipeManager _recipeManager;
private readonly IEventAggregator _eventAggregator;
private readonly ProcessResultManager _processResultManager;
private readonly HardwareManager _hardwareManager;
private readonly DiePositionService _diePositionService;
private DieStatisticsModel _statistics = new DieStatisticsModel();
public DieStatisticsModel Statistics
{
get => _statistics;
set => SetAndNotify(ref _statistics, value);
}
private DieMapModel _dieMapModel = new DieMapModel();
public DieMapModel DieMapModel
{
get => _dieMapModel;
set => SetAndNotify(ref _dieMapModel, value);
}
public DiePositionViewModel(
IEventAggregator eventAggregator,
RecipeManager recipeManager,
ProcessResultManager processResultManager,
HardwareManager hardwareManager,
IParameterManager parameterManager,
DiePositionService diePositionService)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
_hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
_diePositionService = diePositionService ?? throw new ArgumentNullException(nameof(diePositionService));
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
InitTestMap();
}
private void InitTestMap()
{
DieMapModel.Initialize(3, 3);
for (int r = 0; r < 3; r++)
{
for (int c = 0; c < 3; c++)
{
DieMapModel.SetDieState(r, c, DieState.Available);
}
}
DieMapModel.SetDieState(1, 1, DieState.Current);
}
public async Task StartProcess()
{
MW.WorkFlow.WorkflowContext context = new MW.WorkFlow.WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.DiePositionFlow;
var result = await RunManualActivityAsync(
new DiePositionActivity(ProcessFlowName.DiePositionFlow, _diePositionService),
context);
if (!IsWorkflowCompleted(result))
{
return;
}
DiePositionProcessResult processResult = context.GetData<DiePositionProcessResult>(WorkflowContextKeys.DiePositionResult);
if (processResult == null)
{
return;
}
UpdateStatistics(processResult);
UpdateMap(processResult);
}
private void UpdateStatistics(DiePositionProcessResult processResult)
{
Statistics.TotalDieCount = processResult.TheoryDieCount;
Statistics.OkDieCount = processResult.RecognizedDieCount;
Statistics.NgDieCount = processResult.NgDieCount;
Statistics.AverageSpacingX = processResult.AverageSpacingX;
Statistics.AverageSpacingY = processResult.AverageSpacingY;
}
private void UpdateMap(DiePositionProcessResult processResult)
{
if (processResult.RowCount <= 0 || processResult.ColumnCount <= 0)
{
return;
}
DieMapModel.Initialize(processResult.RowCount, processResult.ColumnCount);
DieMapModel.SetDieState(0, 0, DieState.Current);
}
}
}

View File

@@ -0,0 +1,581 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Log;
using MainShell.Manual.Model;
using MainShell.Models;
using MainShell.Motion;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using MainShell.Resources.CustomControl;
using MwFramework.ManagerService;
using Stylet;
using StyletIoC;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using MW.WorkFlow;
namespace MainShell.Manual.ViewModel
{
public class DieRecheckDisplayItem : PropertyChangedBase
{
private int _stepIndex;
public int StepIndex
{
get { return _stepIndex; }
set { SetAndNotify(ref _stepIndex, value); }
}
private int _padRow;
public int PadRow
{
get { return _padRow; }
set { SetAndNotify(ref _padRow, value); }
}
private int _padColumn;
public int PadColumn
{
get { return _padColumn; }
set { SetAndNotify(ref _padColumn, value); }
}
private int _dieRow;
public int DieRow
{
get { return _dieRow; }
set { SetAndNotify(ref _dieRow, value); }
}
private int _dieColumn;
public int DieColumn
{
get { return _dieColumn; }
set { SetAndNotify(ref _dieColumn, value); }
}
private double _padX;
public double PadX
{
get { return _padX; }
set { SetAndNotify(ref _padX, value); }
}
private double _padY;
public double PadY
{
get { return _padY; }
set { SetAndNotify(ref _padY, value); }
}
private double _dieX;
public double DieX
{
get { return _dieX; }
set { SetAndNotify(ref _dieX, value); }
}
private double _dieY;
public double DieY
{
get { return _dieY; }
set { SetAndNotify(ref _dieY, value); }
}
private string _transPathType;
public string TransPathType
{
get { return _transPathType; }
set { SetAndNotify(ref _transPathType, value); }
}
private bool _isMissingBond;
public bool IsMissingBond
{
get { return _isMissingBond; }
set { SetAndNotify(ref _isMissingBond, value); }
}
private double _xError;
public double XError
{
get { return _xError; }
set { SetAndNotify(ref _xError, value); }
}
private double _yError;
public double YError
{
get { return _yError; }
set { SetAndNotify(ref _yError, value); }
}
private bool _isXThresholdExceeded;
public bool IsXThresholdExceeded
{
get { return _isXThresholdExceeded; }
set { SetAndNotify(ref _isXThresholdExceeded, value); }
}
private bool _isYThresholdExceeded;
public bool IsYThresholdExceeded
{
get { return _isYThresholdExceeded; }
set { SetAndNotify(ref _isYThresholdExceeded, value); }
}
private string _statusSummary;
public string StatusSummary
{
get { return _statusSummary; }
set { SetAndNotify(ref _statusSummary, value); }
}
}
public class DieRecheckFilterState : PropertyChangedBase
{
private enum DieRecheckFilterRule
{
None = 0,
MissingBondOnly,
XThresholdExceededOnly,
YThresholdExceededOnly,
XYThresholdExceededOnly
}
private DieRecheckFilterRule _selectedRule;
private bool _isUpdatingSelection;
public bool IsMissingBondRuleSelected
{
get { return _selectedRule == DieRecheckFilterRule.MissingBondOnly; }
set { SetSelectedRule(value, DieRecheckFilterRule.MissingBondOnly, nameof(IsMissingBondRuleSelected)); }
}
public bool IsXThresholdRuleSelected
{
get { return _selectedRule == DieRecheckFilterRule.XThresholdExceededOnly; }
set { SetSelectedRule(value, DieRecheckFilterRule.XThresholdExceededOnly, nameof(IsXThresholdRuleSelected)); }
}
public bool IsYThresholdRuleSelected
{
get { return _selectedRule == DieRecheckFilterRule.YThresholdExceededOnly; }
set { SetSelectedRule(value, DieRecheckFilterRule.YThresholdExceededOnly, nameof(IsYThresholdRuleSelected)); }
}
public bool IsXYThresholdRuleSelected
{
get { return _selectedRule == DieRecheckFilterRule.XYThresholdExceededOnly; }
set { SetSelectedRule(value, DieRecheckFilterRule.XYThresholdExceededOnly, nameof(IsXYThresholdRuleSelected)); }
}
private double _xThreshold = 0.01d;
public double XThreshold
{
get { return _xThreshold; }
set { SetAndNotify(ref _xThreshold, value); }
}
private double _yThreshold = 0.01d;
public double YThreshold
{
get { return _yThreshold; }
set { SetAndNotify(ref _yThreshold, value); }
}
public void ClearSelectedRule()
{
UpdateSelectedRule(DieRecheckFilterRule.None, null);
}
private void SetSelectedRule(bool isSelected, DieRecheckFilterRule targetRule, string propertyName)
{
if (_isUpdatingSelection)
{
return;
}
DieRecheckFilterRule newRule = isSelected ? targetRule : DieRecheckFilterRule.None;
UpdateSelectedRule(newRule, propertyName);
}
private void UpdateSelectedRule(DieRecheckFilterRule newRule, string propertyName)
{
if (_selectedRule == newRule)
{
if (!string.IsNullOrWhiteSpace(propertyName))
{
OnPropertyChanged(propertyName);
}
return;
}
_selectedRule = newRule;
_isUpdatingSelection = true;
try
{
OnPropertyChanged(nameof(IsMissingBondRuleSelected));
OnPropertyChanged(nameof(IsXThresholdRuleSelected));
OnPropertyChanged(nameof(IsYThresholdRuleSelected));
OnPropertyChanged(nameof(IsXYThresholdRuleSelected));
}
finally
{
_isUpdatingSelection = false;
}
}
}
public class DieRecheckViewModel : OperateViewModelBase
{
private readonly IEventAggregator _eventAggregator;
private readonly RecipeManager _recipeManager;
private readonly ProcessResultManager _processResultManager;
private readonly DieRecheckService _dieRecheckService;
private readonly SafeAxisMotion _safeAxisMotion;
private ObservableCollection<DieRecheckDisplayItem> _allPointResults = new ObservableCollection<DieRecheckDisplayItem>();
private List<DieRecheckDisplayItem> _filteredPointResults = new List<DieRecheckDisplayItem>();
private ObservableCollection<DieRecheckDisplayItem> _pointResults = new ObservableCollection<DieRecheckDisplayItem>();
public ObservableCollection<DieRecheckDisplayItem> PointResults
{
get { return _pointResults; }
set { SetAndNotify(ref _pointResults, value); }
}
public PaginationViewModel Pagination { get; } = new PaginationViewModel();
private RegionModel _recheckRegion = new RegionModel();
public RegionModel RecheckRegion
{
get { return _recheckRegion; }
set { SetAndNotify(ref _recheckRegion, value); }
}
private DieRecheckFilterState _filterState = new DieRecheckFilterState();
public DieRecheckFilterState FilterState
{
get { return _filterState; }
set { SetAndNotify(ref _filterState, value); }
}
private int _pointCount;
public int PointCount
{
get { return _pointCount; }
set { SetAndNotify(ref _pointCount, value); }
}
private int _filteredPointCount;
public int FilteredPointCount
{
get { return _filteredPointCount; }
set { SetAndNotify(ref _filteredPointCount, value); }
}
private DieRecheckDisplayItem _selectedPointResult;
public DieRecheckDisplayItem SelectedPointResult
{
get { return _selectedPointResult; }
set { SetAndNotify(ref _selectedPointResult, value); }
}
public DieRecheckViewModel(
IEventAggregator eventAggregator,
RecipeManager recipeManager,
ProcessResultManager processResultManager,
HardwareManager hardwareManager,
DieRecheckService dieRecheckService,
SafeAxisMotion safeAxisMotion)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
if (hardwareManager == null)
{
throw new ArgumentNullException(nameof(hardwareManager));
}
_dieRecheckService = dieRecheckService ?? throw new ArgumentNullException(nameof(dieRecheckService));
_safeAxisMotion = safeAxisMotion ?? throw new ArgumentNullException(nameof(safeAxisMotion));
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
FilterState.PropertyChanged += FilterState_PropertyChanged;
RecheckRegion.PropertyChanged += RecheckRegion_PropertyChanged;
Pagination.PageChanged += Pagination_PageChanged;
LoadCurrentPoints();
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
LoadCurrentPoints();
}
public async Task StartProcess()
{
WorkflowContext context = new WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.DieRecheckFlow;
WorkflowRunCompletedEventArgs result = await RunManualActivityAsync(
new DieRecheckActivity(ProcessFlowName.DieRecheckFlow, _dieRecheckService),
context,
false);
if (!IsWorkflowCompleted(result))
{
return;
}
LoadCurrentPoints();
}
public async Task MoveToSelectedPoint()
{
if (SelectedPointResult == null)
{
LocalizedMessageBox.Show(MessageKey.DieRecheckSelectedPointRequired, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
try
{
HardwareDevice selectedDevice = _cameraAxisViewModel.CameraAxisDevices != null
? _cameraAxisViewModel.CameraAxisDevices.SelectedHardwareDevice
: null;
if (selectedDevice == null || selectedDevice.AxisX == null || selectedDevice.AxisY == null)
{
LocalizedMessageBox.Show(MessageKey.DieRecheckMoveDeviceUnavailable, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
string moveMessage = $"DieRecheck move to point. StepIndex={SelectedPointResult.StepIndex}, PadX={SelectedPointResult.PadX:F4}, PadY={SelectedPointResult.PadY:F4}";
moveMessage.LogProcessInfo();
await Task.Run(() =>
_safeAxisMotion.SafeMove(
MotionMoveRequest.ForAxis(selectedDevice.AxisX, SelectedPointResult.PadX, source: nameof(DieRecheckViewModel), tags: new[] { "DieRecheck", "MoveToPoint", "AxisX" }),
MotionMoveRequest.ForAxis(selectedDevice.AxisY, SelectedPointResult.PadY, source: nameof(DieRecheckViewModel), tags: new[] { "DieRecheck", "MoveToPoint", "AxisY" })));
}
catch (Exception exception)
{
LogManager.LogSysError(exception);
MwMessageBox.Show(exception.Message, LanguageResourceHelper.GetString(MessageKey.TitleError), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
public void ResetFilters()
{
FilterState.ClearSelectedRule();
FilterState.XThreshold = 0.01d;
FilterState.YThreshold = 0.01d;
ApplyFilters();
}
private void FilterState_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
UpdateThresholdFlags();
Pagination.CurrentPage = 1;
ApplyFilters();
}
private void RecheckRegion_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
Pagination.CurrentPage = 1;
ApplyFilters();
}
private void Pagination_PageChanged(object sender, EventArgs e)
{
LoadPagedData();
}
private void LoadCurrentPoints()
{
_allPointResults.Clear();
_filteredPointResults.Clear();
PointResults.Clear();
DieRecheckProcessResult processResult = _processResultManager.DieRecheckResult;
if (processResult == null || processResult.PointResults == null)
{
PointCount = 0;
FilteredPointCount = 0;
Pagination.TotalItems = 0;
return;
}
foreach (DieRecheckPointResult pointResult in processResult.PointResults)
{
_allPointResults.Add(new DieRecheckDisplayItem
{
StepIndex = pointResult.StepIndex,
PadRow = pointResult.PadRow,
PadColumn = pointResult.PadColumn,
DieRow = pointResult.DieRow,
DieColumn = pointResult.DieColumn,
PadX = pointResult.PadX,
PadY = pointResult.PadY,
DieX = pointResult.DieX,
DieY = pointResult.DieY,
TransPathType = pointResult.TransPathType,
IsMissingBond = pointResult.IsMissingBond,
XError = pointResult.XError,
YError = pointResult.YError
});
}
PointCount = _allPointResults.Count;
InitializeRegion();
UpdateThresholdFlags();
Pagination.CurrentPage = 1;
ApplyFilters();
}
private void InitializeRegion()
{
if (_allPointResults.Count == 0)
{
RecheckRegion.StartRow = 1;
RecheckRegion.StartCol = 1;
RecheckRegion.EndRow = 1;
RecheckRegion.EndCol = 1;
return;
}
RecheckRegion.StartRow = _allPointResults.Min(item => item.PadRow);
RecheckRegion.StartCol = _allPointResults.Min(item => item.PadColumn);
RecheckRegion.EndRow = Math.Max(RecheckRegion.StartRow, _allPointResults.Max(item => item.PadRow));
RecheckRegion.EndCol = Math.Max(RecheckRegion.StartCol, _allPointResults.Max(item => item.PadColumn));
}
private void UpdateThresholdFlags()
{
foreach (DieRecheckDisplayItem item in _allPointResults)
{
item.IsXThresholdExceeded = Math.Abs(item.XError) >= FilterState.XThreshold;
item.IsYThresholdExceeded = Math.Abs(item.YError) >= FilterState.YThreshold;
item.StatusSummary = BuildStatusSummary(item);
}
}
private string BuildStatusSummary(DieRecheckDisplayItem item)
{
Collection<string> tags = new Collection<string>();
if (item.IsMissingBond)
{
tags.Add(LanguageResourceHelper.GetString(MessageKey.DieRecheckStatusMissingBond));
}
if (item.IsXThresholdExceeded)
{
tags.Add(LanguageResourceHelper.Format(MessageKey.DieRecheckStatusXExceeded, item.XError));
}
if (item.IsYThresholdExceeded)
{
tags.Add(LanguageResourceHelper.Format(MessageKey.DieRecheckStatusYExceeded, item.YError));
}
if (tags.Count == 0)
{
tags.Add(LanguageResourceHelper.GetString(MessageKey.DieRecheckStatusOk));
}
return string.Join(" / ", tags);
}
private void ApplyFilters()
{
_filteredPointResults = _allPointResults.Where(MatchesFilters).ToList();
FilteredPointCount = _filteredPointResults.Count;
Pagination.TotalItems = FilteredPointCount;
if (Pagination.TotalItems == 0)
{
SelectedPointResult = null;
PointResults = new ObservableCollection<DieRecheckDisplayItem>();
return;
}
if (Pagination.CurrentPage > Pagination.TotalPages)
{
Pagination.CurrentPage = Pagination.TotalPages;
return;
}
LoadPagedData();
}
private void LoadPagedData()
{
if (_filteredPointResults == null || _filteredPointResults.Count == 0)
{
PointResults = new ObservableCollection<DieRecheckDisplayItem>();
SelectedPointResult = null;
return;
}
int safePage = Math.Max(1, Math.Min(Pagination.CurrentPage, Pagination.TotalPages));
int offset = (safePage - 1) * Pagination.PageSize;
int limit = Pagination.PageSize;
PointResults = new ObservableCollection<DieRecheckDisplayItem>(_filteredPointResults.Skip(offset).Take(limit));
if (SelectedPointResult != null && !PointResults.Contains(SelectedPointResult))
{
SelectedPointResult = null;
}
}
private bool MatchesFilters(DieRecheckDisplayItem item)
{
if (item == null)
{
return false;
}
bool isInRegion = item.PadRow >= RecheckRegion.StartRow
&& item.PadRow <= RecheckRegion.EndRow
&& item.PadColumn >= RecheckRegion.StartCol
&& item.PadColumn <= RecheckRegion.EndCol;
if (!isInRegion)
{
return false;
}
if (FilterState.IsMissingBondRuleSelected && !item.IsMissingBond)
{
return false;
}
if (FilterState.IsXYThresholdRuleSelected && !(item.IsXThresholdExceeded && item.IsYThresholdExceeded))
{
return false;
}
if (FilterState.IsXThresholdRuleSelected && !item.IsXThresholdExceeded)
{
return false;
}
if (FilterState.IsYThresholdRuleSelected && !item.IsYThresholdExceeded)
{
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,294 @@
using MainShell.EventArgsFolder;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Manual.ViewModel.State;
using MainShell.Resources.CustomControl;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class LogisticsOperationViewModel : OperateViewModelBase
{
private readonly IDeviceIoMonitorService _ioMonitorService;
private readonly Dictionary<string, Action<bool>> _ioNameBindings = new Dictionary<string, Action<bool>>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<int, Action<bool>> _ioIdBindings = new Dictionary<int, Action<bool>>();
private LogisticsOperationState _state;
public new LogisticsOperationState State
{
get { return _state; }
set { SetAndNotify(ref _state, value); }
}
public LogisticsOperationViewModel(IDeviceIoMonitorService ioMonitorService)
{
_ioMonitorService = ioMonitorService;
State = BuildDefaultState();
State.PropertyChanged += OnStatePropertyChanged;
InitializeIoBindings();
_ioMonitorService.IoChanged += OnIoSnapshotChanged;
_ioMonitorService.Start();
ApplyEfemMode(State.IsManualWithoutEfem);
RequestIoRefresh();
}
private LogisticsOperationState BuildDefaultState()
{
var state = new LogisticsOperationState
{
SubstrateLoad = BuildSectionState("SUBSTRATE LOAD / 基板上料", "READY", "开始上料流程", "停止流程"),
ChipLoad = BuildSectionState("CHIP LOAD / 芯片上料", "READY", "发送取料指令", "取消流程"),
SubstrateOut = BuildSectionState("SUBSTRATE OUT / 基板下料", "IDLE", "开始下料出片", "停止流程"),
ChipUnload = BuildSectionState("CHIP UNLOAD / 芯片下料", "AWAITING", "执行内部下料", "取消流程"),
MagazineMap = MagazineMapViewModel.CreateDefault(25),
FeedbackTitle = "FEEDBACK"
};
state.ChipLoad.PhaseSteps.Add(new PhaseStepState
{
Title = "Phase 1: EFEM 取料",
Status = "READY",
ActionText = "发送取料指令",
IsEnabled = true,
ControlTag = "EFEM HANDOFF",
RequiresEfemCommunication = true,
TargetLayer = 1
});
state.ChipLoad.PhaseSteps.Add(new PhaseStepState
{
Title = "Phase 2: 设备内部吸取",
Status = "内部手动",
ActionText = "执行内部动作",
IsEnabled = false,
ControlTag = "INTERNAL",
RequiresEfemCommunication = false
});
state.ChipUnload.PhaseSteps.Add(new PhaseStepState
{
Title = "Phase 1: 内部移出至对接位",
Status = "内部手动",
ActionText = "执行内部下料",
IsEnabled = true,
ControlTag = "INTERNAL",
RequiresEfemCommunication = false
});
state.ChipUnload.PhaseSteps.Add(new PhaseStepState
{
Title = "Phase 2: EFEM 返料到料盒",
Status = "AWAITING",
ActionText = "发送取料指令",
IsEnabled = false,
ControlTag = "EFEM HANDOFF",
RequiresEfemCommunication = true,
TargetLayer = 1
});
return state;
}
private SectionState BuildSectionState(string title, string status, string primaryAction, string secondaryAction)
{
return new SectionState
{
Title = title,
Status = status,
PrimaryActionText = primaryAction,
SecondaryActionText = secondaryAction,
Indicators =
{
new IndicatorState { Name = "进片感应器触发", IsOn = false },
new IndicatorState { Name = "轨道电机运行中", IsOn = false },
new IndicatorState { Name = "平移机原点检测", IsOn = false },
new IndicatorState { Name = "真空吸附确认", IsOn = false }
}
};
}
public async Task ScanAsync()
{
if (State.IsScanning)
{
return;
}
State.IsScanning = true;
State.ScanProgress = 0;
State.AddFeedback("自动扫码开始。", "INFO");
RequestIoRefresh();
for (var i = 1; i <= 100; i++)
{
State.ScanProgress = i;
await Task.Delay(20);
}
State.IsScanning = false;
State.AddFeedback("自动扫码完成,共扫描 25 个槽位。", "INFO");
RequestIoRefresh();
}
private void OnStatePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(LogisticsOperationState.IsManualWithoutEfem))
{
ApplyEfemMode(State.IsManualWithoutEfem);
}
RequestIoRefresh();
}
private void ApplyEfemMode(bool manualWithoutEfem)
{
if (State.ChipLoad.PhaseSteps.Count > 1)
{
State.ChipLoad.PhaseSteps[0].IsEnabled = !manualWithoutEfem;
State.ChipLoad.PhaseSteps[1].IsEnabled = manualWithoutEfem;
}
if (State.ChipUnload.PhaseSteps.Count > 1)
{
State.ChipUnload.PhaseSteps[1].IsEnabled = !manualWithoutEfem;
}
RequestIoRefresh();
}
public Task ChipLoadPhase1Async()
{
if (State.IsManualWithoutEfem)
{
State.AddFeedback("CHIP LOAD: 无EFEM调试模式直接执行设备内部芯片上料流程。", "INFO");
return ChipLoadPhase2Async();
}
var layer = State.ChipLoad.PhaseSteps[0].TargetLayer;
State.AddFeedback(string.Format("CHIP LOAD Phase1: 发送EFEM取料指令目标料盒层 L{0:D2}(预留通讯接口)。", layer), "INFO");
if (State.ChipLoad.PhaseSteps.Count > 1)
{
State.ChipLoad.PhaseSteps[1].IsEnabled = true;
}
RequestIoRefresh();
return Task.CompletedTask;
}
public Task ChipLoadPhase2Async()
{
State.AddFeedback("CHIP LOAD Phase2: 执行设备内部吸取动作。", "INFO");
RequestIoRefresh();
return Task.CompletedTask;
}
public Task ChipUnloadPhase1Async()
{
State.AddFeedback("CHIP UNLOAD Phase1: 内部移出至对接位。", "INFO");
RequestIoRefresh();
return Task.CompletedTask;
}
public Task ChipUnloadPhase2Async()
{
var layer = State.ChipUnload.PhaseSteps[1].TargetLayer;
State.AddFeedback(string.Format("CHIP UNLOAD Phase2: 发送EFEM返料指令目标料盒层 L{0:D2}(预留通讯接口)。", layer), "INFO");
RequestIoRefresh();
return Task.CompletedTask;
}
protected override void OnDeactivate()
{
_ioMonitorService.IoChanged -= OnIoSnapshotChanged;
base.OnDeactivate();
}
private void InitializeIoBindings()
{
BindIoName("LG.SubstrateLoad.SensorIn", value => SetIndicator(State.SubstrateLoad, 0, value));
BindIoName("LG.SubstrateLoad.TrackMotorRun", value => SetIndicator(State.SubstrateLoad, 1, value));
BindIoName("LG.SubstrateLoad.HomeChecked", value => SetIndicator(State.SubstrateLoad, 2, value));
BindIoName("LG.SubstrateLoad.VacuumOk", value => SetIndicator(State.SubstrateLoad, 3, value));
BindIoName("LG.ChipLoad.SensorIn", value => SetIndicator(State.ChipLoad, 0, value));
BindIoName("LG.ChipLoad.TrackMotorRun", value => SetIndicator(State.ChipLoad, 1, value));
BindIoName("LG.ChipLoad.HomeChecked", value => SetIndicator(State.ChipLoad, 2, value));
BindIoName("LG.ChipLoad.VacuumOk", value => SetIndicator(State.ChipLoad, 3, value));
BindIoName("LG.SubstrateOut.SensorIn", value => SetIndicator(State.SubstrateOut, 0, value));
BindIoName("LG.SubstrateOut.TrackMotorRun", value => SetIndicator(State.SubstrateOut, 1, value));
BindIoName("LG.SubstrateOut.HomeChecked", value => SetIndicator(State.SubstrateOut, 2, value));
BindIoName("LG.SubstrateOut.VacuumOk", value => SetIndicator(State.SubstrateOut, 3, value));
BindIoName("LG.ChipUnload.SensorIn", value => SetIndicator(State.ChipUnload, 0, value));
BindIoName("LG.ChipUnload.TrackMotorRun", value => SetIndicator(State.ChipUnload, 1, value));
BindIoName("LG.ChipUnload.HomeChecked", value => SetIndicator(State.ChipUnload, 2, value));
BindIoName("LG.ChipUnload.VacuumOk", value => SetIndicator(State.ChipUnload, 3, value));
BindIoId(1001, value => SetIndicator(State.SubstrateLoad, 0, value));
BindIoId(1002, value => SetIndicator(State.SubstrateLoad, 1, value));
BindIoId(1003, value => SetIndicator(State.SubstrateLoad, 2, value));
BindIoId(1004, value => SetIndicator(State.SubstrateLoad, 3, value));
}
private void BindIoName(string name, Action<bool> setter)
{
_ioNameBindings[name] = setter;
}
private void BindIoId(int id, Action<bool> setter)
{
_ioIdBindings[id] = setter;
}
private void OnIoSnapshotChanged(object sender, DeviceIoChangedEventArgs e)
{
var changedPoints = e.ChangedPoints;
for (var i = 0; i < changedPoints.Count; i++)
{
ApplyIoPoint(changedPoints[i]);
}
}
private void ApplyIoPoint(DeviceIoPointState point)
{
if (point == null)
{
return;
}
Action<bool> setter;
if (!string.IsNullOrWhiteSpace(point.Name) && _ioNameBindings.TryGetValue(point.Name, out setter))
{
setter(point.Value);
return;
}
if (_ioIdBindings.TryGetValue(point.Id, out setter))
{
setter(point.Value);
}
}
private void RequestIoRefresh()
{
_ioMonitorService.RequestRefresh();
}
private static void SetIndicator(SectionState section, int index, bool isOn)
{
if (section == null || section.Indicators.Count <= index)
{
return;
}
section.Indicators[index].IsOn = isOn;
}
}
}

View File

@@ -0,0 +1,178 @@
using MainShell.Models;
using MainShell.Recipe.ViewModel;
using MaxwellFramework.Core.Interfaces;
using Stylet;
using StyletIoC;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class ManualOperateViewModel : Stylet.Screen,IPage
{
public string Name => "ManualOperate";
private const string LOGISTICS_OPERATION = "物流操作";
private const string SUBSTRATEPOSITION = "基板定位";
//基板测高
private const string SUBSTRATEHEIGHTMEASUREMENT = "基板测高";
//芯片拉直
private const string CHIPSTRAIGHTENING = "芯片拉直";
private const string WAFERPOSITION = "芯片定位";
//Die转移
private const string DIETRANSFER = "芯片转移";
//精度复检
private const string DIERECHECK = "精度复检";
private readonly Dictionary<string, Screen> _viewModelDict = new Dictionary<string, Screen>();
public ObservableCollection<MenuItemWrap> MenuItemWraps { get; private set; }
private MenuItemWrap _selectedMenuItem;
public MenuItemWrap SelectedMenuItem
{
get { return _selectedMenuItem; }
set
{
if (SetAndNotify(ref _selectedMenuItem, value))
{
if (value != null && _viewModelDict.TryGetValue(value.Header, out var screen))
{
CurrentScreen = screen;
}
}
}
}
private Screen _currentScreen;
public Screen CurrentScreen
{
get { return _currentScreen; }
set { SetAndNotify(ref _currentScreen, value); }
}
#region //界面ViewModel注入
private LogisticsOperationViewModel _logisticsOperationViewModel;
[Inject]
public LogisticsOperationViewModel LogisticsOperationViewModel
{
get { return _logisticsOperationViewModel; }
set { SetAndNotify(ref _logisticsOperationViewModel, value); }
}
private SubstratePositionViewModel _substratePositionViewModel;
[Inject]
public SubstratePositionViewModel SubstratePositionViewModel
{
get { return _substratePositionViewModel; }
set { SetAndNotify(ref _substratePositionViewModel, value); }
}
private WaferAngleAdjustmentViewModel _waferAngleAdjustmentViewModel;
[Inject]
public WaferAngleAdjustmentViewModel WaferAngleAdjustmentViewModel
{
get { return _waferAngleAdjustmentViewModel; }
set { SetAndNotify(ref _waferAngleAdjustmentViewModel, value); }
}
private DieBondingViewModel _dieBondingViewModel;
[Inject]
public DieBondingViewModel DieBondingViewModel
{
get { return _dieBondingViewModel; }
set { SetAndNotify(ref _dieBondingViewModel, value); }
}
private DiePositionViewModel _diePositionViewModel;
[Inject]
public DiePositionViewModel DiePositionViewModel
{
get { return _diePositionViewModel; }
set { SetAndNotify(ref _diePositionViewModel, value); }
}
private ChipStraighteningViewModel _chipStraighteningViewModel;
[Inject]
public ChipStraighteningViewModel ChipStraighteningViewModel
{
get { return _chipStraighteningViewModel; }
set { SetAndNotify(ref _chipStraighteningViewModel, value); }
}
private SubstrateHeightMeasureViewModel _substrateHeightMeasureViewModel;
[Inject]
public SubstrateHeightMeasureViewModel SubstrateHeightMeasureViewModel
{
get { return _substrateHeightMeasureViewModel; }
set { SetAndNotify(ref _substrateHeightMeasureViewModel, value); }
}
private DieRecheckViewModel _dieRecheckViewModel;
[Inject]
public DieRecheckViewModel DieRecheckViewModel
{
get { return _dieRecheckViewModel; }
set { SetAndNotify(ref _dieRecheckViewModel, value); }
}
#endregion
public ManualOperateViewModel()
{
MenuItemWraps = new ObservableCollection<MenuItemWrap>()
{
new MenuItemWrap
{
Header=LOGISTICS_OPERATION,Tag=LOGISTICS_OPERATION
},
new MenuItemWrap
{
Header= SUBSTRATEPOSITION, Tag=SUBSTRATEPOSITION
},
new MenuItemWrap
{
Header=SUBSTRATEHEIGHTMEASUREMENT, Tag=SUBSTRATEHEIGHTMEASUREMENT
},
new MenuItemWrap
{
Header=CHIPSTRAIGHTENING, Tag=CHIPSTRAIGHTENING
},
new MenuItemWrap
{
Header=WAFERPOSITION, Tag=WAFERPOSITION
},
new MenuItemWrap
{
Header=DIETRANSFER, Tag=DIETRANSFER
},
new MenuItemWrap
{
Header=DIERECHECK, Tag=DIERECHECK
}
};
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (_viewModelDict.Count == 0)
{
InitViewModelDict();
}
if (SelectedMenuItem == null)
SelectedMenuItem = MenuItemWraps[0];
}
private void InitViewModelDict()
{
_viewModelDict.Clear();
_viewModelDict.Add(LOGISTICS_OPERATION, LogisticsOperationViewModel);
_viewModelDict.Add(SUBSTRATEPOSITION, SubstratePositionViewModel);
_viewModelDict.Add(SUBSTRATEHEIGHTMEASUREMENT, SubstrateHeightMeasureViewModel);
_viewModelDict.Add(CHIPSTRAIGHTENING, WaferAngleAdjustmentViewModel);
_viewModelDict.Add(DIETRANSFER, DieBondingViewModel);
_viewModelDict.Add(WAFERPOSITION, DiePositionViewModel);
_viewModelDict.Add(DIERECHECK, DieRecheckViewModel);
}
}
}

View File

@@ -0,0 +1,240 @@
using MainShell.Resources.CustomControl;
using Stylet;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
namespace MainShell.Manual.ViewModel.State
{
public class LogisticsOperationState : PropertyChangedBase
{
private SectionState _substrateLoad;
public SectionState SubstrateLoad
{
get { return _substrateLoad; }
set { SetAndNotify(ref _substrateLoad, value); }
}
private SectionState _chipLoad;
public SectionState ChipLoad
{
get { return _chipLoad; }
set { SetAndNotify(ref _chipLoad, value); }
}
private SectionState _substrateOut;
public SectionState SubstrateOut
{
get { return _substrateOut; }
set { SetAndNotify(ref _substrateOut, value); }
}
private SectionState _chipUnload;
public SectionState ChipUnload
{
get { return _chipUnload; }
set { SetAndNotify(ref _chipUnload, value); }
}
private MagazineMapViewModel _magazineMap;
public MagazineMapViewModel MagazineMap
{
get { return _magazineMap; }
set { SetAndNotify(ref _magazineMap, value); }
}
private string _feedbackTitle;
public string FeedbackTitle
{
get { return _feedbackTitle; }
set { SetAndNotify(ref _feedbackTitle, value); }
}
private bool _isScanning;
public bool IsScanning
{
get { return _isScanning; }
set { SetAndNotify(ref _isScanning, value); }
}
private int _scanProgress;
public int ScanProgress
{
get { return _scanProgress; }
set { SetAndNotify(ref _scanProgress, value); }
}
private bool _isManualWithoutEfem;
public bool IsManualWithoutEfem
{
get { return _isManualWithoutEfem; }
set { SetAndNotify(ref _isManualWithoutEfem, value); }
}
public string LastFeedbackTime
{
get
{
var last = FeedbackRecords.LastOrDefault();
return last == null ? "--:--:--" : last.Time.ToString("HH:mm:ss");
}
}
public ObservableCollection<FeedbackRecord> FeedbackRecords { get; } = new ObservableCollection<FeedbackRecord>();
public LogisticsOperationState()
{
FeedbackRecords.CollectionChanged += OnFeedbackRecordsChanged;
AddFeedback("内部物流动作已独立触发并完成。");
}
public void AddFeedback(string message, string level = "DEBUG")
{
FeedbackRecords.Add(new FeedbackRecord
{
Time = DateTime.Now,
Level = level,
Message = message
});
while (FeedbackRecords.Count > 3)
{
FeedbackRecords.RemoveAt(0);
}
}
private void OnFeedbackRecordsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(nameof(LastFeedbackTime));
}
}
public class SectionState : PropertyChangedBase
{
private string _title;
public string Title
{
get { return _title; }
set { SetAndNotify(ref _title, value); }
}
private string _status;
public string Status
{
get { return _status; }
set { SetAndNotify(ref _status, value); }
}
private string _primaryActionText;
public string PrimaryActionText
{
get { return _primaryActionText; }
set { SetAndNotify(ref _primaryActionText, value); }
}
private string _secondaryActionText;
public string SecondaryActionText
{
get { return _secondaryActionText; }
set { SetAndNotify(ref _secondaryActionText, value); }
}
public ObservableCollection<IndicatorState> Indicators { get; } = new ObservableCollection<IndicatorState>();
public ObservableCollection<PhaseStepState> PhaseSteps { get; } = new ObservableCollection<PhaseStepState>();
}
public class PhaseStepState : PropertyChangedBase
{
private string _title;
public string Title
{
get { return _title; }
set { SetAndNotify(ref _title, value); }
}
private string _status;
public string Status
{
get { return _status; }
set { SetAndNotify(ref _status, value); }
}
private string _actionText;
public string ActionText
{
get { return _actionText; }
set { SetAndNotify(ref _actionText, value); }
}
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set { SetAndNotify(ref _isEnabled, value); }
}
private string _controlTag;
public string ControlTag
{
get { return _controlTag; }
set { SetAndNotify(ref _controlTag, value); }
}
private bool _requiresEfemCommunication;
public bool RequiresEfemCommunication
{
get { return _requiresEfemCommunication; }
set { SetAndNotify(ref _requiresEfemCommunication, value); }
}
private int _targetLayer = 1;
public int TargetLayer
{
get { return _targetLayer; }
set { SetAndNotify(ref _targetLayer, value); }
}
}
public class IndicatorState : PropertyChangedBase
{
private string _name;
public string Name
{
get { return _name; }
set { SetAndNotify(ref _name, value); }
}
private bool _isOn;
public bool IsOn
{
get { return _isOn; }
set { SetAndNotify(ref _isOn, value); }
}
}
public class FeedbackRecord : PropertyChangedBase
{
private DateTime _time;
public DateTime Time
{
get { return _time; }
set { SetAndNotify(ref _time, value); }
}
private string _level;
public string Level
{
get { return _level; }
set { SetAndNotify(ref _level, value); }
}
private string _message;
public string Message
{
get { return _message; }
set { SetAndNotify(ref _message, value); }
}
}
}

View File

@@ -0,0 +1,195 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using MainShell.Recipe.Models.SubstrateParameter;
using MW.WorkFlow;
using Stylet;
using StyletIoC;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class SubstrateHeightMeasureDisplayItem : PropertyChangedBase
{
private string _pointName;
public string PointName
{
get { return _pointName; }
set { SetAndNotify(ref _pointName, value); }
}
private int _rowIndex;
public int RowIndex
{
get { return _rowIndex; }
set { SetAndNotify(ref _rowIndex, value); }
}
private int _columnIndex;
public int ColumnIndex
{
get { return _columnIndex; }
set { SetAndNotify(ref _columnIndex, value); }
}
private double _positionX;
public double PositionX
{
get { return _positionX; }
set { SetAndNotify(ref _positionX, value); }
}
private double _positionY;
public double PositionY
{
get { return _positionY; }
set { SetAndNotify(ref _positionY, value); }
}
private double? _heightValue;
public double? HeightValue
{
get { return _heightValue; }
set { SetAndNotify(ref _heightValue, value); }
}
}
public class SubstrateHeightMeasureViewModel : OperateViewModelBase
{
private readonly IEventAggregator _eventAggregator;
private readonly RecipeManager _recipeManager;
private readonly ProcessResultManager _processResultManager;
private readonly SubstrateHeightMeasureService _substrateHeightMeasureService;
private ObservableCollection<SubstrateHeightMeasureDisplayItem> _pointResults = new ObservableCollection<SubstrateHeightMeasureDisplayItem>();
public ObservableCollection<SubstrateHeightMeasureDisplayItem> PointResults
{
get { return _pointResults; }
set { SetAndNotify(ref _pointResults, value); }
}
private string _recipeName;
public string RecipeName
{
get { return _recipeName; }
set { SetAndNotify(ref _recipeName, value); }
}
private int _configuredPointCount;
public int ConfiguredPointCount
{
get { return _configuredPointCount; }
set { SetAndNotify(ref _configuredPointCount, value); }
}
private int _measuredPointCount;
public int MeasuredPointCount
{
get { return _measuredPointCount; }
set { SetAndNotify(ref _measuredPointCount, value); }
}
private SubstrateHeightMeasureMode _currentMeasureMode;
public SubstrateHeightMeasureMode CurrentMeasureMode
{
get { return _currentMeasureMode; }
set { SetAndNotify(ref _currentMeasureMode, value); }
}
public SubstrateHeightMeasureViewModel(
IEventAggregator eventAggregator,
RecipeManager recipeManager,
ProcessResultManager processResultManager,
HardwareManager hardwareManager,
SubstrateHeightMeasureService substrateHeightMeasureService)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
_substrateHeightMeasureService = substrateHeightMeasureService ?? throw new ArgumentNullException(nameof(substrateHeightMeasureService));
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
RefreshRecipeSummary();
LoadLastMeasureResult();
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
RefreshRecipeSummary();
LoadLastMeasureResult();
}
public async Task StartProcess()
{
RefreshRecipeSummary();
WorkflowContext context = new WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.SubstrateHeightMeasureFlow;
WorkflowRunCompletedEventArgs result = await RunManualActivityAsync(
new SubstrateHeightMeasureActivity(ProcessFlowName.SubstrateHeightMeasureFlow, _substrateHeightMeasureService),
context);
if (!IsWorkflowCompleted(result))
{
return;
}
LoadLastMeasureResult();
}
private void RefreshRecipeSummary()
{
SubstrateRecipe currentRecipe = _recipeManager.CurrentSubstrateRecipe;
RecipeName = currentRecipe == null ? string.Empty : currentRecipe.RecipeName;
if (currentRecipe == null || currentRecipe.HeightMeasureSetting == null)
{
ConfiguredPointCount = 0;
CurrentMeasureMode = SubstrateHeightMeasureMode.StandardTeachPosition;
return;
}
ConfiguredPointCount = currentRecipe.HeightMeasureSetting.Points == null ? 0 : currentRecipe.HeightMeasureSetting.Points.Count;
CurrentMeasureMode = currentRecipe.HeightMeasureSetting.Mode;
}
private void LoadLastMeasureResult()
{
PointResults.Clear();
SubstrateHeightMeasureProcessResult processResult = _processResultManager.SubstrateHeightMeasureResult;
if (processResult == null || processResult.PointResults == null)
{
MeasuredPointCount = 0;
return;
}
foreach (SubstrateHeightMeasurePointResult pointResult in processResult.PointResults)
{
PointResults.Add(new SubstrateHeightMeasureDisplayItem
{
PointName = pointResult.PointName,
RowIndex = pointResult.RowIndex,
ColumnIndex = pointResult.ColumnIndex,
PositionX = pointResult.PositionX,
PositionY = pointResult.PositionY,
HeightValue = pointResult.HeightValue
});
}
MeasuredPointCount = PointResults.Count;
}
}
}

View File

@@ -0,0 +1,269 @@
using MainShell.Common;
using MainShell.EventArgsFolder;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Models;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using MainShell.Resources.CustomControl;
using Stylet;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace MainShell.Manual.ViewModel
{
public class MarkResultModel : PropertyChangedBase
{
private int _id;
public int Id
{
get { return _id; }
set { SetAndNotify(ref _id, value); }
}
private MPoint _basePos;
public MPoint BasePose
{
get { return _basePos; }
set { SetAndNotify(ref _basePos, value); }
}
private MPoint _resultPose;
public MPoint ResultPose
{
get { return _resultPose; }
set { SetAndNotify(ref _resultPose, value); }
}
}
// 实现3个事件接口
public class SubstratePositionViewModel : OperateViewModelBase,
IHandle<SubstrateProcessStartedEventArgs>,
IHandle<SubstrateMarkFoundEventArgs>,
IHandle<SubstrateResultEventArgs>
{
private sealed class DelegateCommand : ICommand
{
private readonly Action<object> _execute;
public DelegateCommand(Action<object> execute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
}
public event EventHandler CanExecuteChanged
{
add
{
}
remove
{
}
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
// 属性定义保持不变...
private ObservableCollection<MarkResultModel> _markResults = new ObservableCollection<MarkResultModel>();
public ObservableCollection<MarkResultModel> MarkResults
{
get { return _markResults; }
set { SetAndNotify(ref _markResults, value); }
}
private int _row;
public int RowIndex
{
get { return _row; }
set { SetAndNotify(ref _row, value); }
}
private int _col;
public int ColumnIndex
{
get { return _col; }
set { SetAndNotify(ref _col, value); }
}
private int _rowMax;
public int RowMax
{
get { return _rowMax; }
set { SetAndNotify(ref _rowMax, value); }
}
private int _colMax;
public int ColMax
{
get { return _colMax; }
set { SetAndNotify(ref _colMax, value); }
}
private double _substrateAngle;
public double SubstrateAngle
{
get { return _substrateAngle; }
set { SetAndNotify(ref _substrateAngle, value); }
}
private DieMapModel _substrateMapModel = new DieMapModel();
public DieMapModel SubstrateMapModel
{
get => _substrateMapModel;
set => SetAndNotify(ref _substrateMapModel, value);
}
private bool _isClickEnabled = true;
public bool IsClickEnabled
{
get => _isClickEnabled;
set => SetAndNotify(ref _isClickEnabled, value);
}
public ICommand DieClickedCommand { get; private set; }
private readonly HardwareManager _hardwareManager;
private readonly RecipeManager _recipeManager;
private readonly IEventAggregator _eventAggregator; // 引用 EventAggregator
private readonly ProcessResultManager _processResultManager;
private readonly SubstratePositionMotionService _substratePositionMotionService;
public SubstratePositionViewModel(HardwareManager hardwareManager, RecipeManager recipeManager, IEventAggregator eventAggregator, ProcessResultManager processResultManager, SubstratePositionMotionService substratePositionMotionService)
{
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
_substratePositionMotionService = substratePositionMotionService ?? throw new ArgumentNullException(nameof(substratePositionMotionService));
_eventAggregator.Subscribe(this);
DieClickedCommand = new DelegateCommand(ExecuteDieClicked);
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
}
private void ExecuteDieClicked(object parameter)
{
if (!(parameter is ValueTuple<int, int, DieState>))
{
return;
}
ValueTuple<int, int, DieState> dieInfo = (ValueTuple<int, int, DieState>)parameter;
OnDieClicked(this, new DieMapControl.DieClickedEventArgs(dieInfo.Item1, dieInfo.Item2, dieInfo.Item3));
}
public void Handle(SubstrateProcessStartedEventArgs message)
{
Stylet.Execute.OnUIThread(() =>
{
MarkResults.Clear();
SubstrateAngle = 0;
});
}
// 2. 找到 Mark 点:更新列表
public void Handle(SubstrateMarkFoundEventArgs message)
{
Stylet.Execute.OnUIThread(() =>
{
var existing = MarkResults.FirstOrDefault(x => x.Id == message.Id);
if (existing != null)
{
existing.BasePose = message.BasePose;
existing.ResultPose = message.ResultPose;
}
else
{
MarkResults.Add(new MarkResultModel
{
Id = message.Id,
BasePose = message.BasePose,
ResultPose = message.ResultPose
});
}
});
}
// 3. 计算完成:更新角度
public void Handle(SubstrateResultEventArgs message)
{
Stylet.Execute.OnUIThread(() =>
{
SubstrateAngle = message.Angle;
});
}
// 手动启动流程
public async Task StartProcess()
{
var context = new MW.WorkFlow.WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.SubstratePositionFlow;
await RunManualActivityAsync(new MainShell.Process.SubstratePositionActivity(ProcessFlowName.SubstratePositionFlow, _substratePositionMotionService), context);
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (_recipeManager.CurrentSubstrateRecipe != null && _recipeManager.CurrentSubstrateRecipe.SubstrateInfo != null)
{
RowMax = _recipeManager.CurrentSubstrateRecipe.SubstrateInfo.RowNumber;
ColMax = _recipeManager.CurrentSubstrateRecipe.SubstrateInfo.ColNumber;
// 初始化基板Map
if (RowMax > 0 && ColMax > 0)
{
SubstrateMapModel.Initialize(RowMax, ColMax);
for (int r = 0; r < RowMax; r++)
{
for (int c = 0; c < ColMax; c++)
{
SubstrateMapModel.SetDieState(r, c, DieState.Available);
}
}
}
}
}
public void OnDieClicked(object sender, DieMapControl.DieClickedEventArgs e)
{
if (!IsClickEnabled) return;
// 更新选中的行列
RowIndex = e.Row + 1; // 界面显示通常从1开始
ColumnIndex = e.Col + 1;
// 提示用户
MessageBox.Show($"已选择基板位置: 行 {RowIndex}, 列 {ColumnIndex}", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
public void MoveToSelectPad()
{
}
}
}

View File

@@ -0,0 +1,43 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class WaferAngleAdjustmentViewModel : OperateViewModelBase
{
private readonly RecipeManager _recipeManager;
private readonly IEventAggregator _eventAggregator; // 引用 EventAggregator
private readonly ProcessResultManager _processResultManager;
private readonly HardwareManager _hardwareManager;
public WaferAngleAdjustmentViewModel(IEventAggregator eventAggregator,RecipeManager recipeManager,
ProcessResultManager processResultManager, HardwareManager hardwareManager)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
_hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
}
public async Task StartProcess()
{
var context = new MW.WorkFlow.WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
context[WorkflowContextKeys.WorkflowName] = MainShell.Process.ProcessFlowName.ChipStraighteningFlow;
await RunManualActivityAsync(new MainShell.Process.WaferAngleAdjustmentActivity(MainShell.Process.ProcessFlowName.ChipStraighteningFlow), context);
}
}
}