添加 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,132 @@
using MainShell.Log;
using MwFramework.AlarmManager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell.Alarm
{
public class AlarmOperate : IAlarmSend, IAlarmResponse<string>
{
private const string AlarmSourceName = "AlarmOperate";
private readonly IAlarmManager _alarmManager;
public AlarmOperate(IAlarmManager alarmManager)
{
_alarmManager = alarmManager ?? throw new ArgumentNullException(nameof(alarmManager));
_alarmManager.CloseBuzzer += () =>
{
LogManager.LogAlarmEvent(AlarmSourceName, "CloseBuzzer");
};
_alarmManager.RestBuzzer += () =>
{
};
_alarmManager.ClearPLCAlarm += () =>
{
List<long> unresolvedAlarmIds = _alarmManager.UnresolvedAlarms == null
? new List<long>()
: _alarmManager.UnresolvedAlarms.Distinct().ToList();
try
{
foreach (long alarmId in unresolvedAlarmIds)
{
_alarmManager.HandleAlarmSolved(alarmId);
}
LogManager.LogAlarmEvent(
AlarmSourceName,
string.Format("ClearPLCAlarm resolved {0} alarms", unresolvedAlarmIds.Count));
}
catch (Exception ex)
{
LogManager.LogAlarmError(AlarmSourceName, "ClearPLCAlarm", ex);
throw;
}
};
}
public string Code { private get; set; }
public void Alert(int alarmID)
{
Task alertTask = AlertAsync(alarmID);
if (Application.Current != null &&
Application.Current.Dispatcher != null &&
Application.Current.Dispatcher.CheckAccess())
{
alertTask.ContinueWith(task =>
{
AggregateException ignored = task.Exception;
}, TaskContinuationOptions.OnlyOnFaulted);
return;
}
alertTask.GetAwaiter().GetResult();
}
public void Alert(int alarmID, string source, string action, string detail = null)
{
Task alertTask = AlertAsync(alarmID, source, action, detail);
if (Application.Current != null &&
Application.Current.Dispatcher != null &&
Application.Current.Dispatcher.CheckAccess())
{
alertTask.ContinueWith(task =>
{
AggregateException ignored = task.Exception;
}, TaskContinuationOptions.OnlyOnFaulted);
return;
}
alertTask.GetAwaiter().GetResult();
}
public async Task AlertAsync(int alarmID)
{
await AlertInternalAsync(alarmID, AlarmSourceName, "AlertAsync requested").ConfigureAwait(false);
}
public async Task AlertAsync(int alarmID, string source, string action, string detail = null)
{
string alarmSource = string.IsNullOrWhiteSpace(source) ? AlarmSourceName : source;
string triggerAction = BuildTriggerAction(action, detail);
await AlertInternalAsync(alarmID, alarmSource, triggerAction).ConfigureAwait(false);
}
private async Task AlertInternalAsync(int alarmID, string source, string action)
{
try
{
LogManager.LogAlarmEvent(source, action, alarmID);
await _alarmManager.HandleAlarmAsync(alarmID, this, this).ConfigureAwait(false);
LogManager.LogAlarmEvent(source, "HandleAlarmAsync completed", alarmID);
}
catch (Exception ex)
{
LogManager.LogAlarmError(source, "HandleAlarmAsync", ex, alarmID);
throw;
}
}
private static string BuildTriggerAction(string action, string detail)
{
string normalizedAction = string.IsNullOrWhiteSpace(action) ? "AlertAsync requested" : action;
if (string.IsNullOrWhiteSpace(detail))
{
return normalizedAction;
}
return string.Format("{0}. Detail={1}", normalizedAction, detail);
}
}
}

View File

@@ -0,0 +1,38 @@
using MainShell.Alarm.ViewModel;
using MwFramework.AlarmManager;
using Stylet;
using System.Threading.Tasks;
namespace MainShell.Alarm
{
[AlarmID(begin:10000, end:20011)]
public class UserAlarmProduce : AbstractAlarmAsyncProcedure
{
private readonly IAlarmManager _alarmManager;
private readonly IWindowManager _windowManager;
public UserAlarmProduce(IAlarmManager alarmManager, IWindowManager windowManager)
{
_alarmManager = alarmManager;
_windowManager = windowManager;
}
public override async Task HandleAsync(IAlarmSend inContext = null, IAlarmResponse outContext = null)
{
var dialogViewModel = new AlarmDialogViewModel
{
AlarmInfo = AlarmInfo
};
var resetRequested = false;
await Execute.OnUIThreadAsync(() =>
{
resetRequested = _windowManager.ShowDialog(dialogViewModel) == true;
}).ConfigureAwait(false);
if (resetRequested)
{
_alarmManager.ClearPLCAlarm?.Invoke();
}
}
}
}

View File

@@ -0,0 +1,113 @@
<Window x:Class="MainShell.Alarm.View.AlarmDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MainShell.Alarm.View"
mc:Ignorable="d"
SizeToContent="Height"
ResizeMode="CanResizeWithGrip"
WindowStartupLocation="CenterOwner"
Title="报警提示 (Alarm)" Width="650" MinWidth="500" MaxHeight="800">
<Window.Resources>
<Style TargetType="GroupBox" BasedOn="{StaticResource LeftGroupStyle}"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<!-- 顶部横幅 -->
<RowDefinition Height="*"/>
<!-- 内容区域 -->
<RowDefinition Height="Auto"/>
<!-- 按钮区域 -->
</Grid.RowDefinitions>
<!-- 顶部横幅区域 -->
<Border Grid.Row="0" Background="#FFF5E5" BorderBrush="#FFCC80" BorderThickness="0,0,0,1" Padding="15,10">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="⚠" Foreground="#E65100" FontSize="22" FontWeight="Bold" Margin="0,0,10,0" VerticalAlignment="Center"/>
<TextBlock Text="系统发生报警,请及时处理" Foreground="#D84315" FontSize="16" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<!-- 内容区域 -->
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" Margin="15,10,15,15">
<StackPanel>
<!-- 报警ID和类型 -->
<GroupBox Header="基本信息" Margin="0,0,0,10">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="报警 ID:" Style="{StaticResource LabelStyle}" Grid.Row="0" Grid.Column="0" VerticalAlignment="Top"/>
<TextBlock Text="{Binding AlarmInfo.AlarmID}" Style="{StaticResource ValueStyle}" FontWeight="Bold" Grid.Row="0" Grid.Column="1" TextWrapping="Wrap"/>
<TextBlock Text="报警类型:" Style="{StaticResource LabelStyle}" Grid.Row="2" Grid.Column="0" VerticalAlignment="Top"/>
<TextBlock Text="{Binding AlarmInfo.AlarmType}" Style="{StaticResource ValueStyle}" Foreground="#D84315" FontWeight="SemiBold" Grid.Row="2" Grid.Column="1" TextWrapping="Wrap"/>
</Grid>
</GroupBox>
<!-- 报警消息 -->
<GroupBox Header="报警消息" Margin="0,0,0,10">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="中文消息:" Style="{StaticResource LabelStyle}" Grid.Row="0" Grid.Column="0" VerticalAlignment="Top"/>
<TextBlock Text="{Binding AlarmInfo.AlarmMessage}" Style="{StaticResource ValueStyle}" Grid.Row="0" Grid.Column="1" TextWrapping="Wrap" LineHeight="20"/>
<TextBlock Text="英文消息:" Style="{StaticResource LabelStyle}" Grid.Row="2" Grid.Column="0" VerticalAlignment="Top"/>
<TextBlock Text="{Binding AlarmInfo.AlarmMessageEN}" Style="{StaticResource ValueStyle}" Grid.Row="2" Grid.Column="1" TextWrapping="Wrap" Foreground="#555555" LineHeight="20"/>
</Grid>
</GroupBox>
<!-- 报警原因 -->
<GroupBox Header="报警原因" Margin="0,0,0,10">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="原因描述:" Style="{StaticResource LabelStyle}" Grid.Row="0" Grid.Column="0" VerticalAlignment="Top"/>
<TextBlock Text="{Binding AlarmInfo.AlarmReason}" Style="{StaticResource ValueStyle}" Grid.Row="0" Grid.Column="1" TextWrapping="Wrap" LineHeight="20"/>
</Grid>
</GroupBox>
<!-- 解决方案 -->
<GroupBox Header="解决方案" Margin="0,0,0,10">
<Border Background="#F9FBE7" BorderBrush="#CDDC39" BorderThickness="1" CornerRadius="4" Padding="10">
<TextBlock Text="{Binding AlarmInfo.AlarmSolution}" Style="{StaticResource ValueStyle}" TextWrapping="Wrap" Foreground="#33691E" LineHeight="20"/>
</Border>
</GroupBox>
<!-- 触发时间 -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,5,5,0">
<TextBlock Text="触发时间:" Style="{StaticResource LabelStyle}" Foreground="#888888" Margin="0,0,5,0" VerticalAlignment="Center"/>
<TextBlock Text="{Binding AlarmInfo.TriggerTime, StringFormat='{}{0:yyyy-MM-dd HH:mm:ss}'}" Style="{StaticResource ValueStyle}" Foreground="#888888" VerticalAlignment="Center"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
<!-- 底部按钮区域 -->
<Border Grid.Row="2" BorderBrush="#E0E0E0" BorderThickness="0,1,0,0" Padding="10,8" Background="#F8F8F8">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="复位报警" Style="{StaticResource CloseButtonStyle}" Width="100" Height="32" Margin="0,0,10,0" Command="{Binding ResetCommand}" Cursor="Hand"/>
<Button Content="关闭" Style="{StaticResource CloseButtonStyle}" Width="100" Height="32" Command="{Binding CloseCommand}" Cursor="Hand"/>
</StackPanel>
</Border>
</Grid>
</Window>

View File

@@ -0,0 +1,27 @@
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.Shapes;
namespace MainShell.Alarm.View
{
/// <summary>
/// AlarmDialog.xaml 的交互逻辑
/// </summary>
public partial class AlarmDialogView : Window
{
public AlarmDialogView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,40 @@
using MainShell.Models;
using MaxwellFramework.Core.Common.Command;
using MwFramework.AlarmManager;
using System.Windows.Input;
namespace MainShell.Alarm.ViewModel
{
public class AlarmDialogViewModel : BaseScreen
{
public ICommand ResetCommand { get; }
public ICommand CloseCommand { get; }
public AlarmDialogViewModel()
{
DisplayName = "报警详情";
ResetCommand = new DelegateCommand(obj => Reset());
CloseCommand = new DelegateCommand(obj => Close());
}
private AlarmInfo _alarmInfo;
public AlarmInfo AlarmInfo
{
get => _alarmInfo;
set
{
SetAndNotify(ref _alarmInfo, value);
}
}
private void Close()
{
RequestClose(false);
}
private void Reset()
{
RequestClose(true);
}
}
}

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
<section name="templates" type="MwFramework.Controls.PatternTemplate.ConfigModule.TemplateConfigurationSection,MwFramework.Controls.PatternTemplate" />
</configSections>
<templates parserType="MwFramework.HuaruiMatch.TypeParser" caliperType="MwFramework.HuaruiMatch.CaliperTest" assemblyFile="MwFramework.HuaruiMatch.dll">
<template templateName="Shape" templateType="MwFramework.HuaruiMatch.ShapeMatch" assemblyFile="MwFramework.HuaruiMatch.dll"></template>
<template templateName="Ncc" templateType="MwFramework.HuaruiMatch.TemplateMatch" assemblyFile="MwFramework.HuaruiMatch.dll"></template>
</templates>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<appSettings>
<add key="Demo" value="true" />
<!--语言-->
<add key="Lang" value="CN" />
<add key="SaveLogsWay" value="DataBase"/>
<!--Txt 存储路径-->
<add key="LogsPath" value="D:\\Logs" />
<add key="MachineId" value="" />
</appSettings>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -0,0 +1,21 @@
<Application x:Class="MainShell.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MainShell" xmlns:mw="http://www.maxwell-gp.com/"
>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaxwellControl;component/Themes/SkinTouchable.xaml" />
<mw:ApplicationLoader>
<mw:ApplicationLoader.Bootstrapper>
<local:StartBootstrapper/>
</mw:ApplicationLoader.Bootstrapper>
</mw:ApplicationLoader>
<ResourceDictionary Source="/Resources/Styles/Styles.xaml"/>
<ResourceDictionary Source="/Language/MXJM_CN.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace MainShell
{
/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
}
}

View File

@@ -0,0 +1,171 @@
# CLAUDE.md
## 前置指令
1. **XAML 文件可以直接更改** — 使用 Edit 工具直接修改 `.xaml` 文件
2. **CS 文件有 TSM 加密** — `.cs` 文件在本地是加密的,无法直接读取或编辑。需要通过 `git show HEAD:path` 获取内容。但注意:有时最新的 CS 文件还未提交到 git,此时可以让用户直接提供最新的 CS 文件内容
3. **所有回复使用中文** — 所有与用户的交流、注释说明等均使用中文
## PageCalib 目录结构
PageCalib 是标定页面模块,采用 MVVM 架构,包含 8 个标定子模块。主入口通过 `PageCalibView.xaml` + `PageCalibViewModel.cs` 管理导航。
### 目录组织
```
PageCalib/
├── View/ # 主视图
│ ├── PageCalibView.xaml # 主页面,使用 SideMenu 导航
│ └── PageCalibView.xaml.cs
├── ViewModel/ # 主视图模型
│ └── PageCalibViewModel.cs # 管理 8 个标定子模块导航
├── HeightMeasure/ # 高度标定模块
│ ├── Model/ # HeightBaseSetting, IHeightMeasure, DiastimeterHeightMeasure
│ ├── Service/ # HeightMeasureMotionService
│ ├── View/ # HeightView.xaml
│ └── ViewModel/ # HeightViewModel
├── OriginCalib/ # 原点标定模块
│ ├── Model/ # OriginCalibSetting
│ ├── Service/ # ICalibrationPostProcessor, OriginCalibrationMotionService, CalibrationPostProcessors
│ ├── View/ # OriginCalibView.xaml
│ └── ViewModel/ # OriginCalibViewModel, OriginCalibModuleViewModel, AvoidanceAxisViewModel, CalibrationAxisViewModel
└── AlgorithmCalib/ # 算法标定模块(包含多个子模块)
├── Common/ # 公共接口和结果类
│ ├── IAlgorithmCalibModule.cs
│ ├── AlgorithmCalibResult.cs
│ └── CalibrationProgressEventArgs.cs
├── Model/ # 参数模型
│ ├── AlgorithmCalibModuleNames.cs
│ ├── FusionCalibParItem.cs
│ ├── RotateCalibParItem.cs
│ └── FusionCalib/ # FusionCalibSetting, FusionCalibModuleItem
├── Task/ # 标定任务
│ ├── RotateFormCalibTask.cs
│ └── FusionCalibTask.cs
├── View/ # 各子模块视图
│ ├── PixRatioCalib/ # 像素比标定
│ ├── DistortionCorrectionCalib/ # 畸变校正标定
│ ├── RotateCenterCalib/ # 旋转中心标定
│ ├── RotateMatixFormCalib/ # 旋转表标定
│ ├── MotionCalib/ # 运动系标定
│ └── FusionCalib/ # 融合标定(Die/Pad)
├── ViewModel/ # 各子模块视图模型
│ ├── PixRatioCalib/
│ ├── DistortionCorrectionCalib/
│ ├── RotateCenterCalib/
│ ├── RotateMatixFormCalib/
│ ├── MotionCalib/
│ └── FusionCalib/
└── Service/ # 各子模块服务
├── RotateFormCalib/ # RotateMatixFormCalibMotionService, FileService, PostProcessor
├── MotionCalib/ # MotionCalibFileService
└── FusionCalib/ # FusionCalibMotionService, PostProcessor, FusionAlignProcessor
```
### 标定子模块列表
| 模块名称 | 中文名 | 主 ViewModel | 说明 |
|---------|--------|-------------|------|
| HeightMeasure | 高度标定 | HeightViewModel | 测高标定,包含产品和方片高度测量 |
| OriginCalib | 原点标定 | OriginCalibViewModel | 原点位置标定 |
| PixRatioCalib | 像素比标定 | PixRatioCalibContentsViewModel | 相机像素比例标定 |
| DistortionCorrectionCalib | 畸变校准标定 | DistortionCorrectionCalibContentsViewModel | 相机畸变校正 |
| MotionCalib | 运动系标定 | MotionCalibContentsViewModel | 运动系统标定 |
| RotateCenterCalib | 旋转中心标定 | RotateCenterCalibContentsViewModel | 旋转中心位置标定 |
| RotateFormCalib | 旋转表标定 | RotateMatixFormCalibViewModel | 旋转矩阵表标定 |
| FusionCalib | 融合标定 | FusionCalibViewModel | Die/Pad 融合标定 |
### 通用 UI 模式
算法标定子模块(PixRatio、DistortionCorrection、RotateCenter、MotionCalib、FusionCalib)采用统一的 ContentsView 布局:
- 顶部:当前标定名称 + 进度文本
- 中间:ContentControl 绑定 ShowScreenVM 显示具体内容
- 底部:上一个/下一个导航按钮
旋转表标定(RotateMatixFormCalib)采用卡片式布局:
- 左侧:相机预览
- 右侧:模块卡片(避让位置、标定参数、验证参数、进度条、操作按钮)
## 融合标定需求与实现
### 轴映射关系
| 项目 | WS轴(Wafer轴) | PHS轴(Pad轴) |
|------|-----------------|----------------|
| 旧项目 | X2, Y2 | X1, Y1 |
| 新项目 | X2, Y1 | X1, Y2 |
### 融合标定流程
#### 1. Die融合标定(ws圆形标定融合)
在X1全行程不同位置,标定建立X2,Y1的die融合表,用于对位晶粒。
**标定方法:**
- 在die运动系下(固定X1),移动X2,Y1扫描获取N×N点阵,作为初始位置
- 选取X2负限位位置附近设置起始位置,将X1,X2平移相同距离移动到起始位置
- 设定标定范围、标定间隔,扫描一系列N×N点阵,保存为die融合位置表
**验证方法:**
- 在die运动系下飞拍任意坐标
- 再选取任意X1位置,使用融合位置表计算出任意坐标移动到该X1位置下的X2,Y1坐标
- 同时获取到在该X1位置下实际逼近的X2,Y1坐标
- 输出即为标定误差,可选择若干位置覆盖测试全行程误差
#### 2. Pad融合标定(stage拼接融合)
在Y1全行程不同位置,建立X1,Y2的pad融合表,用于对位pad。
**标定方法:**
- 在pad运动系下(固定Y1),移动X1,Y2扫描获取N×N点阵,作为初始位置
- 选取Y2负限位位置附近设置起始位置,将Y1,Y2平移相同距离移动到起始位置
- 设立标定范围、标定间隔,扫描一系列N×N点阵,保存为pad融合位置表
**验证方法:**
- 在pad运动系下飞拍任意坐标
- 再选取任意Y1位置,使用融合位置表计算出任意坐标移动到该Y1位置下的X1,Y2坐标
- 同时获取到在该Y1位置下实际逼近的X1,Y2坐标
- 输出即为标定误差,可选择若干位置覆盖测试全行程误差
#### 3. 整体对位验证(对位流程)
整体对位流程为:
1. 飞拍获取die的下相机X2,Y2坐标,X1坐标固定(上相机die运动系X2,Y1),转换为X1,Y1坐标
2. Pad定位mark获取X1,Y2坐标,Y1坐标固定(上相机pad运动系X1,Y2),即转化为X1,Y1坐标
3. 通过查找die融合表将die X方向对齐pad(此时Y1会产生少许偏移)
4. 再根据此时Y1值,将pad Y方向对齐die(X1会产生少许偏移)
5. 此时再移动X2跟上X1的移动量(此处直接移动或查表再移动)
### 新项目融合标定文件结构
```
AlgorithmCalib/
├── View/FusionCalib/
│ ├── DieFusionCalibView.xaml # Die融合标定视图(标定+验证Tab)
│ ├── PadFusionCalibView.xaml # Pad融合标定视图(标定+验证Tab)
│ └── FusionAlignVerifyView.xaml # 整体对位验证视图(4步骤流程)
├── ViewModel/FusionCalib/
│ ├── FusionCalibViewModel.cs # Die/Pad融合标定共用ViewModel
│ ├── FusionAlignVerifyViewModel.cs # 整体对位验证ViewModel
│ └── FusionMultipleCalibViewModel.cs # 多模块融合标定ViewModel
├── Service/FusionCalib/
│ ├── FusionCalibMotionService.cs # 融合标定运动服务
│ ├── FusionCalibPostProcessor.cs # 融合标定后处理器
│ └── FusionAlignProcessor.cs # 对位处理器
└── Model/FusionCalib/
├── FusionCalibSetting.cs # 融合标定设置
└── FusionCalibModuleItem.cs # 融合标定模块参数
```
### 旧项目参考文件
旧项目融合标定参考文件位于 `AlgorithmCalib/Old_fusionCalib/` 目录:
- `oldProjectDetail.xaml` - 旧项目融合标定流程说明
- `newProjectVisionDemand.xaml` - 新项目需求说明
- `CameraFusionCali02AViewModel.xaml` - 旧项目ViewModel实现(CS内容)
- `CameraFusionCalibrationMotion02A.xaml` - 旧项目Motion实现(CS内容)
### 关键API调用
旧项目使用的JM1Vision API:
- `JM1Manager.Instance.cal_wafer_fusion()` - 计算wafer融合表
- `JM1Manager.Instance.get_ws_pos()` - 根据融合表查询WS位置
- `JM1Manager.Instance.pos_verify()` - 验证标定精度
- `JM1Manager.Instance.Save()` - 保存标定数据

View File

@@ -0,0 +1,61 @@
using System;
using System.Windows;
using System.Windows.Input;
namespace MainShell.Common.Behaviors
{
public static class WindowDragMoveBehavior
{
public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(WindowDragMoveBehavior),
new PropertyMetadata(false, OnIsEnabledChanged));
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = d as UIElement;
if (element == null)
{
return;
}
if ((bool)e.NewValue)
{
element.MouseLeftButtonDown += OnMouseLeftButtonDown;
return;
}
element.MouseLeftButtonDown -= OnMouseLeftButtonDown;
}
private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var element = sender as DependencyObject;
var window = element != null ? Window.GetWindow(element) : null;
if (window == null || e.ButtonState != MouseButtonState.Pressed)
{
return;
}
try
{
window.DragMove();
e.Handled = true;
}
catch (InvalidOperationException)
{
}
}
}
}

View File

@@ -0,0 +1,59 @@
using MainShell.Log;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
namespace MainShell.Common
{
public static class CommonUti
{
public static void RunOnUi(Action action)
{
if (action == null)
{
return;
}
try
{
if (Application.Current == null || Application.Current.Dispatcher.CheckAccess())
{
// 同步在当前线程执行
action();
}
else
{
// 在 UI 线程同步执行
Application.Current.Dispatcher.Invoke(action);
}
}
catch (Exception ex)
{
MaxwellControl.Controls.MessageBox.Show($"方法执行异常:{ex.ToString()}", icon: MessageBoxImage.Error);
}
}
public static Brush BrushFromString(string s)
{
if (string.IsNullOrWhiteSpace(s))
return Brushes.Transparent;
try
{
var brush = (Brush)new BrushConverter().ConvertFromString(s);
return brush ?? Brushes.Transparent;
}
catch
{
// 如果解析失败,返回默认颜色(可根据需要改为 Brushes.Black 或抛异常)
return Brushes.Transparent;
}
}
}
}

View File

@@ -0,0 +1,240 @@
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.Input;
using System.Windows.Media;
using MainShell.Resources.CustomControl;
namespace MainShell.Common.ControlAttribute
{
public static class ControlBehavior
{
public static bool GetIsEnable(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnableProperty);
}
public static void SetIsEnable(DependencyObject obj, bool value)
{
obj.SetValue(IsEnableProperty, value);
}
// Using a DependencyProperty as the backing store for IsEnable. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsEnableProperty =
DependencyProperty.RegisterAttached("IsEnable", typeof(bool), typeof(ControlBehavior), new PropertyMetadata(true, OnProppertyChanged));
public static bool GetIsNumericOnly(DependencyObject obj)
{
return (bool)obj.GetValue(IsNumericOnlyProperty);
}
public static void SetIsNumericOnly(DependencyObject obj, bool value)
{
obj.SetValue(IsNumericOnlyProperty, value);
}
public static readonly DependencyProperty IsNumericOnlyProperty =
DependencyProperty.RegisterAttached("IsNumericOnly", typeof(bool), typeof(ControlBehavior), new PropertyMetadata(false, OnIsNumericOnlyChanged));
public static bool GetRouteDieMapMouseWheel(DependencyObject obj)
{
return (bool)obj.GetValue(RouteDieMapMouseWheelProperty);
}
public static void SetRouteDieMapMouseWheel(DependencyObject obj, bool value)
{
obj.SetValue(RouteDieMapMouseWheelProperty, value);
}
public static readonly DependencyProperty RouteDieMapMouseWheelProperty =
DependencyProperty.RegisterAttached("RouteDieMapMouseWheel", typeof(bool), typeof(ControlBehavior), new PropertyMetadata(false, OnRouteDieMapMouseWheelChanged));
private static void OnProppertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is TabControl tabControl)
{
if (e.NewValue is bool isenable)
{
if (isenable)
foreach (var item in tabControl.Items)
{
if (tabControl.ItemContainerGenerator.ContainerFromItem(item) is TabItem tabItem)
{
tabItem.IsEnabled = isenable;
}
}
else
foreach (var item in tabControl.Items)
{
if (tabControl.ItemContainerGenerator.ContainerFromItem(item) is TabItem tabItem)
{
if (tabItem.IsSelected)
{
tabItem.IsEnabled = true;
}
else
tabItem.IsEnabled = isenable;
}
}
}
}
}
private static void OnIsNumericOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TextBox textBox))
return;
if ((bool)e.NewValue)
{
InputMethod.SetIsInputMethodEnabled(textBox, false);
textBox.PreviewTextInput += TextBox_PreviewTextInput;
DataObject.AddPastingHandler(textBox, OnTextBoxPaste);
}
else
{
InputMethod.SetIsInputMethodEnabled(textBox, true);
textBox.PreviewTextInput -= TextBox_PreviewTextInput;
DataObject.RemovePastingHandler(textBox, OnTextBoxPaste);
}
}
private static void OnRouteDieMapMouseWheelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ScrollViewer scrollViewer = d as ScrollViewer;
if (scrollViewer == null)
{
return;
}
bool isEnabled = e.NewValue is bool && (bool)e.NewValue;
MouseWheelEventHandler mouseWheelEventHandler = new MouseWheelEventHandler(ScrollViewer_PreviewMouseWheel);
if (isEnabled)
{
scrollViewer.RemoveHandler(UIElement.PreviewMouseWheelEvent, mouseWheelEventHandler);
scrollViewer.AddHandler(UIElement.PreviewMouseWheelEvent, mouseWheelEventHandler, true);
}
else
{
scrollViewer.RemoveHandler(UIElement.PreviewMouseWheelEvent, mouseWheelEventHandler);
}
}
private static void ScrollViewer_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
ScrollViewer scrollViewer = sender as ScrollViewer;
if (scrollViewer == null)
{
return;
}
DieMapControl dieMapControl = FindDieMapControl(scrollViewer, e);
if (dieMapControl == null)
{
return;
}
e.Handled = true;
MouseWheelEventArgs mouseWheelEventArgs = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
mouseWheelEventArgs.RoutedEvent = UIElement.MouseWheelEvent;
mouseWheelEventArgs.Source = dieMapControl;
dieMapControl.RaiseEvent(mouseWheelEventArgs);
}
private static DieMapControl FindDieMapControl(ScrollViewer scrollViewer, MouseWheelEventArgs e)
{
DependencyObject dependencyObject = e.OriginalSource as DependencyObject;
DieMapControl dieMapControl = FindAncestor<DieMapControl>(dependencyObject);
if (dieMapControl != null)
{
return dieMapControl;
}
dependencyObject = Mouse.DirectlyOver as DependencyObject;
dieMapControl = FindAncestor<DieMapControl>(dependencyObject);
if (dieMapControl != null)
{
return dieMapControl;
}
IInputElement inputElement = scrollViewer.InputHitTest(e.GetPosition(scrollViewer));
dependencyObject = inputElement as DependencyObject;
return FindAncestor<DieMapControl>(dependencyObject);
}
private static T FindAncestor<T>(DependencyObject dependencyObject) where T : DependencyObject
{
while (dependencyObject != null)
{
T target = dependencyObject as T;
if (target != null)
{
return target;
}
DependencyObject parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null && dependencyObject is FrameworkContentElement)
{
parent = ((FrameworkContentElement)dependencyObject).Parent;
}
dependencyObject = parent;
}
return null;
}
private static void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
if (sender is TextBox textBox)
{
e.Handled = !IsValidNumericText(GetPreviewText(textBox, e.Text));
}
}
private static void OnTextBoxPaste(object sender, DataObjectPastingEventArgs e)
{
if (!(sender is TextBox textBox))
return;
if (!e.DataObject.GetDataPresent(typeof(string)))
{
e.CancelCommand();
return;
}
var pasteText = e.DataObject.GetData(typeof(string)) as string ?? string.Empty;
if (!IsValidNumericText(GetPreviewText(textBox, pasteText)))
{
e.CancelCommand();
}
}
private static string GetPreviewText(TextBox textBox, string newText)
{
var currentText = textBox.Text ?? string.Empty;
if (textBox.SelectionLength > 0)
{
currentText = currentText.Remove(textBox.SelectionStart, textBox.SelectionLength);
}
return currentText.Insert(textBox.CaretIndex, newText);
}
private static bool IsValidNumericText(string text)
{
if (string.IsNullOrWhiteSpace(text) || text == "-" || text == "." || text == "-.")
return true;
return double.TryParse(text, out _);
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using MessageBox = MaxwellControl.Controls.MessageBox;
namespace MainShell.Common.ControlAttribute
{
public class NumericValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
string strValue = value as string;
if (string.IsNullOrEmpty(strValue))
{
// 空值是允许的,如果需要必填,则返回 new ValidationResult(false, "不能为空");
return new ValidationResult(false, "不能为空");
}
// 尝试将字符串转换为double
if (double.TryParse(strValue, out _))
{
return ValidationResult.ValidResult;
}
else
{
return new ValidationResult(false, "请输入有效的数字。");
}
}
}
}

View File

@@ -0,0 +1,97 @@
using MaxwellFramework.Core.Common;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.ManagerService;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Common.Display.Models
{
public class CameraAxisDevices : ViewModelBase
{
List<HardwareDevice> _hardwareDeviceList = new List<HardwareDevice>();
HardwareDevice _selectedHardwareDevice = new HardwareDevice();
private ObservableCollection<object> _shapes = new ObservableCollection<object>();
private int _selectedIndex = 0;
private bool _cameraSwitchEnable = true;
public List<HardwareDevice> HardwareDeviceList
{
get { return _hardwareDeviceList; }
set
{
if (_hardwareDeviceList == value)
{
return;
}
_hardwareDeviceList = value;
if (_hardwareDeviceList != null && _hardwareDeviceList.Count > 0)
{
_selectedIndex = 0;
SelectedHardwareDevice = _hardwareDeviceList[_selectedIndex];
RaisePropertyChanged(nameof(SelectedIndex));
}
RaisePropertyChanged(nameof(HardwareDeviceList));
}
}
public HardwareDevice SelectedHardwareDevice
{
get { return _selectedHardwareDevice; }
set
{
if (_selectedHardwareDevice != null && _selectedHardwareDevice.Camera != null && value != null && value.Camera != null)
{
}
_selectedHardwareDevice = value; RaisePropertyChanged(nameof(SelectedHardwareDevice));
}
}
public ObservableCollection<object> Shapes
{
get { return _shapes; }
set
{
if (_shapes != value) { _shapes = value; RaisePropertyChanged(nameof(Shapes)); }
}
}
private ObservableCollection<PrimShape> _regions = new ObservableCollection<PrimShape>();
public ObservableCollection<PrimShape> Regions
{
get { return _regions; }
set
{
SetAndNotify(ref _regions, value);
}
}
public int SelectedIndex
{
get { return _selectedIndex; }
set
{
_selectedIndex = value;
if (HardwareDeviceList != null && HardwareDeviceList.Count > 0 && _selectedIndex >= 0)
{
if (_selectedIndex < HardwareDeviceList.Count)
{
SelectedHardwareDevice = HardwareDeviceList[_selectedIndex];
}
}
RaisePropertyChanged(nameof(SelectedIndex));
}
}
public bool CameraSwitchEnable
{
get { return _cameraSwitchEnable; }
set { _cameraSwitchEnable = value; RaisePropertyChanged(nameof(CameraSwitchEnable)); }
}
}
}

View File

@@ -0,0 +1,30 @@
<UserControl x:Class="MainShell.Common.Display.View.CameraAxisView"
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.Common.Display.View"
xmlns:control="http://www.maxwell.com/controls"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<CheckBox Panel.ZIndex="2" IsChecked="{Binding IsOpen, UpdateSourceTrigger=PropertyChanged}" Cursor="Hand" Margin="2,2" Foreground="White" HorizontalAlignment="Right" VerticalAlignment="Top">
<TextBlock Text="移动"/>
</CheckBox>
<control:CameraAxisControlA Name="cameraAxisControl"
IsShowDoubleLine="False"
IsAxisControlLDBVisible2="Visible"
Cameras="{Binding CameraAxisDevices.HardwareDeviceList}"
TabSelectedIndex ="{Binding CameraAxisDevices.SelectedIndex}"
Items="{Binding CameraAxisDevices.Shapes,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Regions="{Binding CameraAxisDevices.Regions}" ShapeThickness="1" IsShowSolidLine="True" DrawInConcurrency="False"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="RecordPix">
<i:InvokeCommandAction Command="{Binding OnLeftMouseDownCommand}" CommandParameter="{Binding ElementName=cameraAxisControl}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</control:CameraAxisControlA>
</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.Common.Display.View
{
/// <summary>
/// CameraAxisView.xaml 的交互逻辑
/// </summary>
public partial class CameraAxisView : UserControl
{
public CameraAxisView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,22 @@
<UserControl x:Class="MainShell.Common.Display.View.CameraView"
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.Common.Display.View"
xmlns:mw="http://www.maxwell.com/controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Border BorderThickness="1" BorderBrush ="#B0B9C4">
<mw:DesignPanel x:Name="cameraDesignPanel"
Source="{Binding DisplayImage}"
Items="{Binding Shapes}"
IsVirtualPix="Visible"
IsShowSolidLine="True"
IsCrossingLineVisible="False"
IsNonRectCrossingLineVisible="False"
IsImagePix="Collapsed" />
</Border>
</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.Common.Display.View
{
/// <summary>
/// CameraView.xaml 的交互逻辑
/// </summary>
public partial class CameraView : UserControl
{
public CameraView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,323 @@
using MainShell.Common;
using MainShell.Common.Display.Models;
using MainShell.Log;
using MainShell.Motion;
using Maxwell.SemiFramework.DefaultConfig.Vision;
using MwFramework.Controls.Components;
using MwFramework.Controls.ControlCanvas.Model;
using MwFramework.Controls.UIControl;
using MwFramework.Device;
using MwFramework.ManagerService;
using Stylet;
using StyletIoC;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace MainShell.Common.Display.ViewModel
{
public class CameraAxisViewModel : Screen
{
private const double DefaultFovX = 14.1 * 10;
private const double DefaultFovY = 10.3 * 10;
private const string SafeAxisMotionNotInjectedMessage = "SafeAxisMotion is not injected.";
private readonly object _moveLock = new object();
private CameraAxisDevices _cameraAxisDevices = new CameraAxisDevices();
private double _xPixelPos;
private double _yPixelPos;
private double _cameraXIndex;
private double _cameraYIndex;
private GetConvertValue _convertFunXDelegate;
private GetConvertValue _convertFunYDelegate;
private ObservableCollection<PrimShape> _regions = new ObservableCollection<PrimShape>();
private bool _isCameraEnable = true;
private Visibility _controlVisibility = Visibility.Visible;
private bool _isOpen;
[Inject]
public SafeAxisMotion SafeAxisMotion { get; set; }
public CameraAxisViewModel()
{
ConvertFunXDelegate = new GetConvertValue(ConvertFunX);
ConvertFunYDelegate = new GetConvertValue(ConvertFunY);
OnLeftMouseDownCommand = new DelegateCommand<object>(OnLeftMouseDown);
}
public CameraAxisDevices CameraAxisDevices
{
get { return _cameraAxisDevices; }
set { SetAndNotify(ref _cameraAxisDevices, value); }
}
public double XPixelPos
{
get { return _xPixelPos; }
set { SetAndNotify(ref _xPixelPos, value); }
}
public double YPixelPos
{
get { return _yPixelPos; }
set { SetAndNotify(ref _yPixelPos, value); }
}
public double CameraXIndex
{
get { return _cameraXIndex; }
set { SetAndNotify(ref _cameraXIndex, value); }
}
public double CameraYIndex
{
get { return _cameraYIndex; }
set { SetAndNotify(ref _cameraYIndex, value); }
}
public GetConvertValue ConvertFunXDelegate
{
get { return _convertFunXDelegate; }
set { SetAndNotify(ref _convertFunXDelegate, value); }
}
public GetConvertValue ConvertFunYDelegate
{
get { return _convertFunYDelegate; }
set { SetAndNotify(ref _convertFunYDelegate, value); }
}
public ObservableCollection<PrimShape> Regions
{
get { return _regions; }
set { SetAndNotify(ref _regions, value); }
}
public bool IsCameraEnable
{
get { return _isCameraEnable; }
set { SetAndNotify(ref _isCameraEnable, value); }
}
public Visibility ControlVisibility
{
get { return _controlVisibility; }
set { SetAndNotify(ref _controlVisibility, value); }
}
public ICommand OnLeftMouseDownCommand { get; private set; }
public bool IsOpen
{
get { return _isOpen; }
set { SetAndNotify(ref _isOpen, value); }
}
public void OnLeftMouseDown(object sender)
{
try
{
HardwareDevice selectedDevice;
if (!TryGetSelectedDevice(out selectedDevice))
{
return;
}
CameraAxisControlA controlA = sender as CameraAxisControlA;
if (controlA == null)
{
return;
}
Point pixelPos = new Point(controlA.YPixPos, controlA.XPixPos);
Task.Run(() => MoveToPixelPoint(selectedDevice, pixelPos, false));
}
catch (Exception ex)
{
LogManager.LogSysError(ex);
ShowOperationFailed(ex.Message);
}
}
public void MovePosAction()
{
try
{
HardwareDevice selectedDevice;
if (!TryGetSelectedDevice(out selectedDevice))
{
return;
}
Point pixelPos = new Point(YPixelPos, XPixelPos);
Task.Run(() => MoveToPixelPoint(selectedDevice, pixelPos, true));
}
catch (Exception ex)
{
LogManager.LogSysError(ex);
ShowOperationFailed(ex.Message);
}
}
public double ConvertFunX(double interval)
{
try
{
HardwareDevice selectedDevice;
if (!TryGetSelectedDevice(out selectedDevice))
{
return 0;
}
string name = selectedDevice.Camera.Id;
double dx;
double dy;
VisionProcess.Instance.GetCameraResolution(name, out dx, out dy);
double pixelX = interval / Math.Abs(dx);
return pixelX;
}
catch (Exception ex)
{
LogManager.LogSysError(ex);
ShowOperationFailed(ex.Message);
return 0;
}
}
public double ConvertFunY(double interval)
{
try
{
HardwareDevice selectedDevice;
if (!TryGetSelectedDevice(out selectedDevice))
{
return 0;
}
string name = selectedDevice.Camera.Id;
double dx;
double dy;
VisionProcess.Instance.GetCameraResolution(name, out dx, out dy);
double pixelY = interval / Math.Abs(dy);
return pixelY;
}
catch (Exception ex)
{
LogManager.LogSysError(ex);
ShowOperationFailed(ex.Message);
return 0;
}
}
private bool TryGetSelectedDevice(out HardwareDevice selectedDevice)
{
selectedDevice = CameraAxisDevices != null ? CameraAxisDevices.SelectedHardwareDevice : null;
if (!IsOpen || selectedDevice == null)
{
return false;
}
if (selectedDevice.Camera == null || selectedDevice.Camera.Camera == null)
{
ShowDeviceNotInitialized();
return false;
}
if (!selectedDevice.Camera.Camera.IsGrabbing)
{
LocalizedMessageBox.Show(MessageKey.VisionCameraNotGrabbing, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
return false;
}
if (selectedDevice.AxisX == null || selectedDevice.AxisY == null)
{
ShowDeviceNotInitialized();
return false;
}
return true;
}
private void MoveToPixelPoint(HardwareDevice selectedDevice, Point pixelPos, bool requireConfirmation)
{
lock (_moveLock)
{
try
{
IAxis axisX = selectedDevice.AxisX;
IAxis axisY = selectedDevice.AxisY;
Point rulerPos = new Point(axisX.State.ActualPos, axisY.State.ActualPos);
Point newRulerPoint = Algorithm.Instance.MoveToPixelPoint(selectedDevice.Camera.Id, rulerPos, pixelPos);
if (requireConfirmation)
{
double dx = newRulerPoint.X - rulerPos.X;
double dy = newRulerPoint.Y - rulerPos.Y;
if (!ValidateMoveRange(dx, dy))
{
return;
}
if (!ConfirmMove(newRulerPoint))
{
return;
}
}
if (SafeAxisMotion == null)
{
InvalidOperationException exception = new InvalidOperationException(SafeAxisMotionNotInjectedMessage);
LogManager.LogSysError(exception);
ShowOperationFailed(SafeAxisMotionNotInjectedMessage);
return;
}
SafeAxisMotion.SafeMove(
MotionMoveRequest.ForAxis(axisX, newRulerPoint.X),
MotionMoveRequest.ForAxis(axisY, newRulerPoint.Y));
}
catch (Exception ex)
{
LogManager.LogSysError(ex);
ShowOperationFailed(ex.Message);
}
}
}
private static bool ValidateMoveRange(double dx, double dy)
{
if (Math.Abs(dx) > DefaultFovX || Math.Abs(dy) > DefaultFovY)
{
LocalizedMessageBox.Show(MessageKey.ParamOutOfRange, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
return false;
}
return true;
}
private static bool ConfirmMove(Point newRulerPoint)
{
string message = string.Format("新点的坐标({0},{1}),是否移动?", newRulerPoint.X, newRulerPoint.Y);
return MwMessageBox.Show(message, LanguageResourceHelper.GetString(MessageKey.TitleConfirm), MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.Cancel) == MessageBoxResult.OK;
}
private static void ShowDeviceNotInitialized()
{
LocalizedMessageBox.Show(MessageKey.DeviceNotInitialized, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
}
private static void ShowOperationFailed(string message)
{
if (string.IsNullOrWhiteSpace(message))
{
LocalizedMessageBox.Show(MessageKey.CommonOperationFailed, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
MwMessageBox.Show(message, LanguageResourceHelper.GetString(MessageKey.TitleError), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}

View File

@@ -0,0 +1,317 @@
using MainShell.EventArgsFolder;
using MainShell.Log;
using MainShell.Vision;
using MwFramework.Device.Model;
using MwFramework.Controls.ControlCanvas.DrawingControl;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using AppCameraType = MainShell.Common.CameraType;
using System.Windows.Media;
namespace MainShell.Common.Display.ViewModel
{
public class CameraViewModel : Screen, IHandle<DisplayImageEventArgs>, IHandle<DisplayFindTemplateResultEventArgs>
{
private readonly IEventAggregator _eventAggregator;
public AppCameraType CameraType { get; }
private WriteableBitmap currentImage;
private WriteableBitmap _displayImage;
public WriteableBitmap DisplayImage
{
get { return _displayImage; }
set
{
SetAndNotify(ref _displayImage, value);
}
}
private ObservableCollection<object> _shapes = new ObservableCollection<object>();
public ObservableCollection<object> Shapes
{
get { return _shapes; }
set
{
if (_shapes != value) { _shapes = value; OnPropertyChanged(nameof(Shapes)); }
}
}
/// <summary>
/// 根据 CameraData 更新并返回 WriteableBitmap。
/// 优化要点:
/// - 重用已有 WriteableBitmap(当尺寸与像素格式相同时)以减少分配。
/// - 校验参数与缓冲区长度。
/// - 捕获并记录异常,失败时返回 null(不抛出)。
/// </summary>
public WriteableBitmap UpdateBitmap(CameraData cameraData)
{
if (cameraData == null)
{
"图片数据为空".LogSysError();
return null;
}
try
{
int width = (int)cameraData.Width;
int height = (int)cameraData.Height;
int bitCnt = cameraData.Bit;
byte[] buffer = cameraData.BufferData;
if (width <= 0 || height <= 0)
{
$"CameraViewModel.UpdateBitmap: invalid size {width}x{height}".LogSysError();
return null;
}
// 选择像素格式
System.Windows.Media.PixelFormat pixelFormat = bitCnt == 1
? System.Windows.Media.PixelFormats.Gray8
: (bitCnt == 3 ? System.Windows.Media.PixelFormats.Bgr24 : System.Windows.Media.PixelFormats.Bgra32);
// 计算每行字节数(stride)
int bytesPerPixel = (pixelFormat.BitsPerPixel + 7) / 8;
int stride = width * bytesPerPixel;
long requiredLength = (long)stride * height;
if (buffer == null)
{
"CameraViewModel.UpdateBitmap: buffer is null".LogSysError();
return null;
}
if (buffer.Length < requiredLength)
{
($"CameraViewModel.UpdateBitmap: buffer length {buffer.Length} is less than required {requiredLength}").LogSysError();
return null;
}
// 如果现有 bitmap 可复用(尺寸和格式一致),就重用以减少 GC/分配
if (currentImage == null ||
currentImage.PixelWidth != width ||
currentImage.PixelHeight != height ||
!currentImage.Format.Equals(pixelFormat))
{
// 新建 WriteableBitmap
currentImage = new WriteableBitmap(width, height, 96, 96, pixelFormat, null);
}
// 将字节拷贝到 BackBuffer(UI 线程中调用)
currentImage.Lock();
try
{
// BackBuffer 为 IntPtr,使用 Marshal.Copy 从 byte[] 拷贝
System.Runtime.InteropServices.Marshal.Copy(buffer, 0, currentImage.BackBuffer, (int)requiredLength);
currentImage.AddDirtyRect(new Int32Rect(0, 0, width, height));
}
finally
{
currentImage.Unlock();
}
return currentImage;
}
catch (Exception ex)
{
($"CameraViewModel.UpdateBitmap exception: {ex}").LogSysError();
return null;
}
}
public CameraViewModel(IEventAggregator eventAggregator, AppCameraType cameraType)
{
_eventAggregator = eventAggregator;
CameraType = cameraType;
_eventAggregator?.Subscribe(this);
}
public void Handle(DisplayImageEventArgs message)
{
if (message == null || message.CameraType != CameraType)
{
return;
}
CommonUti.RunOnUi(() =>
{
WriteableBitmap bmp = UpdateBitmap(message.CameraData);
// 只有在成功生成 bitmap 时才更新 DisplayImage,避免把 null 赋值导致 UI 清空
if (bmp != null)
{
DisplayImage = bmp;
}
});
}
public void Handle(DisplayFindTemplateResultEventArgs message)
{
if (message == null || message.CameraType != CameraType)
{
return;
}
CommonUti.RunOnUi(() =>
{
ApplyTemplateResultShapes(message);
});
}
private void ApplyTemplateResultShapes(DisplayFindTemplateResultEventArgs message)
{
if (message.ClearShapesBeforeDisplay)
{
Shapes.Clear();
}
if (message.Result == null)
{
return;
}
AppendCenterShapes(message.Result);
AppendContourShape(message.Result.Contour);
AppendCircleShapes(message.Result.Circles);
AppendLineShapes(message.Result.Lines);
AppendRectangleShapes(message.Result.Rectangles);
}
private void AppendCenterShapes(FindTemplateResult result)
{
if (result == null || !AreFinite(result.CenterX, result.CenterY, result.Score))
{
return;
}
ShapeDrawing pointShape = new ShapeDrawing();
pointShape.X = result.CenterX;
pointShape.Y = result.CenterY;
Shapes.Add(pointShape);
TextShapeDrawing textShape = new TextShapeDrawing();
textShape.X = result.CenterX;
textShape.Y = result.CenterY;
textShape.Text = $"Score:{result.Score:F3}";
Shapes.Add(textShape);
}
private void AppendContourShape(FindTemplateContourResult contour)
{
if (contour == null || !AreFinite(contour.StartX, contour.StartY, contour.EndX, contour.EndY))
{
return;
}
RectShapeDrawing contourShape = new RectShapeDrawing();
contourShape.X = contour.StartX;
contourShape.Y = contour.StartY;
contourShape.X1 = contour.EndX;
contourShape.Y1 = contour.EndY;
Shapes.Add(contourShape);
}
private void AppendCircleShapes(IList<FindTemplateCircleResult> circles)
{
if (circles == null)
{
return;
}
foreach (FindTemplateCircleResult circle in circles)
{
if (circle == null || !AreFinite(circle.CenterX, circle.CenterY, circle.Radius))
{
continue;
}
CircleShapeDrawing circleShape = new CircleShapeDrawing();
circleShape.X = circle.CenterX;
circleShape.Y = circle.CenterY;
circleShape.CenterPoint = new Point(circle.CenterX, circle.CenterY);
circleShape.RadiusX = circle.Radius;
circleShape.RadiusY = circle.Radius;
circleShape.LineWidth = 1;
circleShape.FillColor = Brushes.Blue;
Shapes.Add(circleShape);
}
}
private void AppendLineShapes(IList<FindTemplateLineResult> lines)
{
if (lines == null)
{
return;
}
foreach (FindTemplateLineResult line in lines)
{
if (line == null || !AreFinite(line.StartX, line.StartY, line.EndX, line.EndY))
{
continue;
}
LineShapeDrawing lineShape = new LineShapeDrawing();
lineShape.X = line.StartX;
lineShape.Y = line.StartY;
lineShape.X1 = line.EndX;
lineShape.Y1 = line.EndY;
Shapes.Add(lineShape);
}
}
private void AppendRectangleShapes(IList<FindTemplateRectangleResult> rectangles)
{
if (rectangles == null)
{
return;
}
foreach (FindTemplateRectangleResult rectangle in rectangles)
{
if (rectangle == null || !AreFinite(rectangle.StartX, rectangle.StartY, rectangle.Width, rectangle.Height, rectangle.Angle))
{
continue;
}
double halfWidth = rectangle.Width / 2d;
double halfHeight = rectangle.Height / 2d;
double cosAngle = Math.Cos(rectangle.Angle);
double sinAngle = Math.Sin(rectangle.Angle);
double centerX = rectangle.StartX + (halfWidth * cosAngle) - (halfHeight * sinAngle);
double centerY = rectangle.StartY + (halfWidth * sinAngle) + (halfHeight * cosAngle);
RectShape2Drawing rectangleShape = new RectShape2Drawing();
rectangleShape.X = centerX;
rectangleShape.Y = centerY;
rectangleShape.CenterX = centerX;
rectangleShape.CenterY = centerY;
rectangleShape.HalfWidth = halfWidth;
rectangleShape.HalfHeigth = halfHeight;
rectangleShape.Angle = rectangle.Angle;
Shapes.Add(rectangleShape);
}
}
private static bool AreFinite(params double[] values)
{
if (values == null)
{
return false;
}
foreach (double value in values)
{
if (double.IsNaN(value) || double.IsInfinity(value))
{
return false;
}
}
return true;
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Common
{
/// <summary>
/// 一个简单的包装类,在对象被 Dispose 时执行指定的 Action。
/// </summary>
public class DisposableAction : IDisposable
{
private readonly Action _action;
private bool _isDisposed;
public DisposableAction(Action action)
{
_action = action ?? throw new ArgumentNullException(nameof(action));
}
public void Dispose()
{
if (!_isDisposed)
{
_action();
_isDisposed = true;
}
}
}
}

View File

@@ -0,0 +1,181 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Common
{
public enum MachineMode { Manual, Auto, Maintenance }
public enum SpeedType
{
[Description("低速")]
Low,
[Description("中速")]
Medium,
[Description("高速")]
High
}
public enum DieTransferRowTraversalStrategy
{
[Description("全部正向")]
AllPositive = 0,
[Description("全部反向")]
AllNegative = 1,
[Description("蛇形")]
Serpentine = 2
}
public enum BondingPathMode
{
S形打件 = 1,
Z形正向打件,
Z形反向打件,
}
public enum BondingStagePathMode
{
S型区域打件 = 1,
Stage正向区域打件,
Stage负向区域打件,
}
public enum BondingWSPathMode
{
S型打件 = 1,
WS正向区域打件,
WS负向区域打件,
}
public enum RunningMode
{
生产检测 = 1,
老化测试,
}
public enum CameraType
{
TopPositionCamera,
TopWsCamera,
TopWideCamera,
TopWideWsCamera,
MapCamera
}
public enum TransPathType
{
[Description("就近")]
Nearest,
[Description("顺序")]
Sequence,
}
public enum TransPathDirection
{
[Description("正向")]
Positive,
[Description("反向")]
Negative,
}
public enum DieState
{
[Description("可使用")]
Available,
[Description("已使用")]
Used,
[Description("错误")]
Error,
[Description("不存在")]
NotExist,
[Description("当前")]
Current,
[Description("目标")]
Target
}
public enum ProcessExecutionStatus
{
Unknown = 0,
Running,
Paused,
Canceled,
Faulted,
Completed
}
public enum FlowDisplayStage
{
Load = 0,
Align = 1,
Bond = 2,
Inspect = 3,
Unload = 4
}
public enum WaferScanMode
{
[Description("按需扫描")]
ScanOnDemand,
[Description("全扫")]
FullScan,
}
public enum BondingSection
{
/// <summary>
/// A区
/// </summary>
A,
/// <summary>
/// B区
/// </summary>
B
}
public enum ComSupMotionType
{
/// <summary>
/// 干涉写入
/// </summary>
Interfere = 102,
/// <summary>
/// 回零写入
/// </summary>
Home = 103,
/// <summary>
/// 快打执行
/// </summary>
FastBonding = 200,
/// <summary>
/// 慢打执行
/// </summary>
SlowBonding = 201,
/// <summary>
/// 报警清楚
/// </summary>
AlarmClear = 104,
/// <summary>
/// 切PID指定
/// </summary>
ChangePid = 301,
/// <summary>
/// 触发报警
/// </summary>
OccurAlarm = 105,
/// <summary>
/// 同步PID参数
/// </summary>
SyncPIDParam = 106,
/// <summary>
/// 手动设置刺针次数
/// </summary>
NeedleWorkCount = 302,
}
}

View File

@@ -0,0 +1,25 @@
using MwFramework.Device;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Common.Extension
{
public static class DeviceExtension
{
private static readonly int _taskNum = 3;
public static void GetLastSamples(this IDiastimeter diastimeter, out double distance)
{
var doubles = new List<double>();
for (int i = 0; i < _taskNum; i++)
{
diastimeter.GetLastSample(out double val);
if (val != double.NaN)
doubles.Add(val);
}
distance = doubles.Average();
}
}
}

View File

@@ -0,0 +1,78 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
namespace MainShell.Common
{
public static class FocusedEditorCommitHelper
{
public static void CommitFocusedEditorChanges()
{
var focusedElement = Keyboard.FocusedElement as DependencyObject;
if (focusedElement == null)
return;
CommitBinding(focusedElement);
var dataGrid = FindAncestor<DataGrid>(focusedElement);
if (dataGrid != null)
{
dataGrid.CommitEdit(DataGridEditingUnit.Cell, true);
dataGrid.CommitEdit(DataGridEditingUnit.Row, true);
}
}
private static void CommitBinding(DependencyObject dependencyObject)
{
if (dependencyObject is TextBox textBox)
{
textBox.GetBindingExpression(TextBox.TextProperty)?.UpdateSource();
return;
}
if (dependencyObject is ComboBox comboBox)
{
comboBox.GetBindingExpression(Selector.SelectedItemProperty)?.UpdateSource();
comboBox.GetBindingExpression(ComboBox.TextProperty)?.UpdateSource();
return;
}
if (dependencyObject is ToggleButton toggleButton)
{
toggleButton.GetBindingExpression(ToggleButton.IsCheckedProperty)?.UpdateSource();
}
}
private static T FindAncestor<T>(DependencyObject dependencyObject) where T : DependencyObject
{
var current = dependencyObject;
while (current != null)
{
if (current is T target)
return target;
current = GetParent(current);
}
return null;
}
private static DependencyObject GetParent(DependencyObject dependencyObject)
{
if (dependencyObject is FrameworkElement frameworkElement)
{
return frameworkElement.Parent ?? VisualTreeHelper.GetParent(frameworkElement);
}
if (dependencyObject is FrameworkContentElement frameworkContentElement)
{
return frameworkContentElement.Parent;
}
return null;
}
}
}

View File

@@ -0,0 +1,380 @@
using MainShell.Resources.CustomControl;
using MaxwellFramework.Core.Events;
using Stylet;
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Media;
namespace MainShell.Common
{
public class HeaderDeviceStatusController : PropertyChangedBase, IHandle<MainViewSelectedPageChangeEvent>
{
private enum HeaderDeviceStatusTone
{
Auto,
Manual,
Maintenance,
Recipe,
Settings,
Calibration,
Unknown
}
private readonly IEventAggregator _eventAggregator;
private bool _hasLoadingStarted;
private bool _isVisible;
private string _labelText = string.Empty;
private string _statusText = string.Empty;
private Brush _badgeBackgroundBrush;
private Brush _badgeBorderBrush;
private Brush _labelForegroundBrush;
private Brush _statusForegroundBrush;
private Brush _indicatorBrush;
public HeaderDeviceStatusController(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_eventAggregator.Subscribe(this);
LoadingService.Instance.PropertyChanged += OnLoadingServicePropertyChanged;
UpdateStatusText("Home");
IsVisible = false;
}
public bool IsVisible
{
get
{
return _isVisible;
}
private set
{
SetAndNotify(ref _isVisible, value);
}
}
public string LabelText
{
get
{
return _labelText;
}
private set
{
SetAndNotify(ref _labelText, value);
}
}
public string StatusText
{
get
{
return _statusText;
}
private set
{
SetAndNotify(ref _statusText, value);
}
}
public Brush BadgeBackgroundBrush
{
get
{
return _badgeBackgroundBrush;
}
private set
{
SetAndNotify(ref _badgeBackgroundBrush, value);
}
}
public Brush BadgeBorderBrush
{
get
{
return _badgeBorderBrush;
}
private set
{
SetAndNotify(ref _badgeBorderBrush, value);
}
}
public Brush LabelForegroundBrush
{
get
{
return _labelForegroundBrush;
}
private set
{
SetAndNotify(ref _labelForegroundBrush, value);
}
}
public Brush StatusForegroundBrush
{
get
{
return _statusForegroundBrush;
}
private set
{
SetAndNotify(ref _statusForegroundBrush, value);
}
}
public Brush IndicatorBrush
{
get
{
return _indicatorBrush;
}
private set
{
SetAndNotify(ref _indicatorBrush, value);
}
}
public void Handle(MainViewSelectedPageChangeEvent message)
{
if (message == null)
{
return;
}
UpdateStatusText(message.SelectePageName);
if (!LoadingService.Instance.IsBusy)
{
IsVisible = true;
}
}
private void OnLoadingServicePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!string.Equals(e.PropertyName, nameof(LoadingService.IsBusy), StringComparison.Ordinal))
{
return;
}
if (LoadingService.Instance.IsBusy)
{
_hasLoadingStarted = true;
IsVisible = false;
return;
}
if (_hasLoadingStarted)
{
IsVisible = true;
}
}
private void UpdateStatusText(string pageName)
{
string resourceKey;
HeaderDeviceStatusTone tone;
ResolveStatusDefinition(pageName, out resourceKey, out tone);
LabelText = GetResourceText("HeaderDeviceStatusLabel", "<22><>ǰ״̬:");
StatusText = GetResourceText(resourceKey, GetFallbackStatusText(tone));
ApplyTone(tone);
}
private static void ResolveStatusDefinition(string pageName, out string resourceKey, out HeaderDeviceStatusTone tone)
{
if (string.Equals(pageName, "Home", StringComparison.OrdinalIgnoreCase))
{
resourceKey = "HeaderDeviceModeAuto";
tone = HeaderDeviceStatusTone.Auto;
return;
}
if (string.Equals(pageName, "ManualOperate", StringComparison.OrdinalIgnoreCase))
{
resourceKey = "HeaderDeviceModeManual";
tone = HeaderDeviceStatusTone.Manual;
return;
}
if (string.Equals(pageName, "MenuDeviceMaint", StringComparison.OrdinalIgnoreCase))
{
resourceKey = "HeaderDeviceModeMaintenance";
tone = HeaderDeviceStatusTone.Maintenance;
return;
}
if (string.Equals(pageName, "Recipe", StringComparison.OrdinalIgnoreCase))
{
resourceKey = "HeaderDeviceModeRecipe";
tone = HeaderDeviceStatusTone.Recipe;
return;
}
if (string.Equals(pageName, "PageSettings", StringComparison.OrdinalIgnoreCase))
{
resourceKey = "HeaderDeviceModeSettings";
tone = HeaderDeviceStatusTone.Settings;
return;
}
if (string.Equals(pageName, "PageCalib", StringComparison.OrdinalIgnoreCase))
{
resourceKey = "HeaderDeviceModeCalibration";
tone = HeaderDeviceStatusTone.Calibration;
return;
}
resourceKey = "HeaderDeviceModeUnknown";
tone = HeaderDeviceStatusTone.Unknown;
}
private static string GetFallbackStatusText(HeaderDeviceStatusTone tone)
{
switch (tone)
{
case HeaderDeviceStatusTone.Auto:
return "<22>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD> (AUTO)";
case HeaderDeviceStatusTone.Manual:
return "<22>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD> (MANUAL)";
case HeaderDeviceStatusTone.Maintenance:
return "<22>豸ά<E8B1B8><CEAC> (MAINT)";
case HeaderDeviceStatusTone.Recipe:
return "<22>䷽<EFBFBD>༭ (RECIPE)";
case HeaderDeviceStatusTone.Settings:
return "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (SETUP)";
case HeaderDeviceStatusTone.Calibration:
return "<22>궨У׼ (CALIB)";
default:
return "״̬<D7B4><CCAC><EFBFBD><EFBFBD> (PENDING)";
}
}
private void ApplyTone(HeaderDeviceStatusTone tone)
{
BadgeBackgroundBrush = CreateBackgroundBrush(tone);
BadgeBorderBrush = CreateBorderBrush(tone);
LabelForegroundBrush = CreateSolidBrush(198, 221, 255);
StatusForegroundBrush = Brushes.White;
IndicatorBrush = CreateIndicatorBrush(tone);
}
private static Brush CreateBackgroundBrush(HeaderDeviceStatusTone tone)
{
switch (tone)
{
case HeaderDeviceStatusTone.Auto:
return CreateGradientBrush(Color.FromRgb(42, 111, 84), Color.FromRgb(24, 79, 60));
case HeaderDeviceStatusTone.Manual:
return CreateGradientBrush(Color.FromRgb(148, 103, 37), Color.FromRgb(104, 72, 24));
case HeaderDeviceStatusTone.Maintenance:
return CreateGradientBrush(Color.FromRgb(46, 104, 164), Color.FromRgb(31, 74, 125));
case HeaderDeviceStatusTone.Recipe:
return CreateGradientBrush(Color.FromRgb(95, 76, 153), Color.FromRgb(68, 54, 111));
case HeaderDeviceStatusTone.Settings:
return CreateGradientBrush(Color.FromRgb(88, 103, 124), Color.FromRgb(62, 74, 92));
case HeaderDeviceStatusTone.Calibration:
return CreateGradientBrush(Color.FromRgb(34, 124, 141), Color.FromRgb(23, 90, 102));
default:
return CreateGradientBrush(Color.FromRgb(50, 102, 177), Color.FromRgb(35, 78, 140));
}
}
private static Brush CreateBorderBrush(HeaderDeviceStatusTone tone)
{
switch (tone)
{
case HeaderDeviceStatusTone.Auto:
return CreateSolidBrush(94, 169, 128);
case HeaderDeviceStatusTone.Manual:
return CreateSolidBrush(201, 151, 76);
case HeaderDeviceStatusTone.Maintenance:
return CreateSolidBrush(94, 148, 206);
case HeaderDeviceStatusTone.Recipe:
return CreateSolidBrush(130, 112, 192);
case HeaderDeviceStatusTone.Settings:
return CreateSolidBrush(126, 143, 166);
case HeaderDeviceStatusTone.Calibration:
return CreateSolidBrush(79, 170, 187);
default:
return CreateSolidBrush(96, 145, 214);
}
}
private static Brush CreateIndicatorBrush(HeaderDeviceStatusTone tone)
{
switch (tone)
{
case HeaderDeviceStatusTone.Auto:
return CreateSolidBrush(63, 227, 123);
case HeaderDeviceStatusTone.Manual:
return CreateSolidBrush(255, 183, 66);
case HeaderDeviceStatusTone.Maintenance:
return CreateSolidBrush(72, 196, 255);
case HeaderDeviceStatusTone.Recipe:
return CreateSolidBrush(173, 121, 255);
case HeaderDeviceStatusTone.Settings:
return CreateSolidBrush(158, 172, 196);
case HeaderDeviceStatusTone.Calibration:
return CreateSolidBrush(70, 231, 219);
default:
return CreateSolidBrush(255, 93, 93);
}
}
private static Brush CreateSolidBrush(byte red, byte green, byte blue)
{
return new SolidColorBrush(Color.FromRgb(red, green, blue));
}
private static Brush CreateGradientBrush(Color startColor, Color endColor)
{
return new LinearGradientBrush(startColor, endColor, 0.0);
}
private static string GetResourceText(string resourceKey, string fallback)
{
Application application = Application.Current;
if (application == null)
{
return fallback;
}
object resource = application.TryFindResource(resourceKey);
string text = resource as string;
if (!string.IsNullOrWhiteSpace(text))
{
return text;
}
return fallback;
}
}
}

View File

@@ -0,0 +1,607 @@
using MainShell.Resources.CustomControl;
using Newtonsoft.Json;
using Stylet;
using System;
using System.Globalization;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace MainShell.Common
{
public static class HeaderDeviceStatusOverlayHost
{
private static readonly object OverlayRootTag = new object();
private static readonly HeaderDeviceStatusOverlaySettings OverlaySettings = new HeaderDeviceStatusOverlaySettings();
public static void Attach(Window window, HeaderDeviceStatusController controller)
{
if (window == null)
{
throw new ArgumentNullException(nameof(window));
}
if (controller == null)
{
throw new ArgumentNullException(nameof(controller));
}
window.Dispatcher.BeginInvoke(
DispatcherPriority.Loaded,
new Action(() => AttachCore(window, controller)));
}
private static void AttachCore(Window window, HeaderDeviceStatusController controller)
{
Grid existingOverlayRoot = window.Content as Grid;
if (existingOverlayRoot != null && ReferenceEquals(existingOverlayRoot.Tag, OverlayRootTag))
{
return;
}
UIElement currentContent = window.Content as UIElement;
if (currentContent == null)
{
return;
}
Grid overlayRoot = new Grid();
overlayRoot.Tag = OverlayRootTag;
window.Content = null;
overlayRoot.Children.Add(currentContent);
overlayRoot.Children.Add(CreateOverlayElement(controller));
overlayRoot.Children.Add(CreateDebugPanelElement());
window.Content = overlayRoot;
}
private static FrameworkElement CreateOverlayElement(HeaderDeviceStatusController controller)
{
HeaderDeviceStatusView statusView = new HeaderDeviceStatusView();
statusView.DataContext = controller;
statusView.IsHitTestVisible = false;
statusView.HorizontalAlignment = HorizontalAlignment.Left;
statusView.VerticalAlignment = VerticalAlignment.Top;
Binding marginBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.Margin));
marginBinding.Source = OverlaySettings;
BindingOperations.SetBinding(statusView, FrameworkElement.MarginProperty, marginBinding);
Panel.SetZIndex(statusView, short.MaxValue);
return statusView;
}
private static FrameworkElement CreateDebugPanelElement()
{
Border panelBorder = new Border();
panelBorder.Background = new SolidColorBrush(Color.FromArgb(235, 18, 26, 35));
panelBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(72, 93, 118));
panelBorder.BorderThickness = new Thickness(1.0);
panelBorder.CornerRadius = new CornerRadius(6.0);
panelBorder.Padding = new Thickness(10.0, 8.0, 10.0, 8.0);
panelBorder.HorizontalAlignment = HorizontalAlignment.Left;
panelBorder.VerticalAlignment = VerticalAlignment.Top;
Binding panelMarginBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.DebugPanelMargin));
panelMarginBinding.Source = OverlaySettings;
BindingOperations.SetBinding(panelBorder, FrameworkElement.MarginProperty, panelMarginBinding);
Binding panelVisibilityBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.IsDebugPanelVisible));
panelVisibilityBinding.Source = OverlaySettings;
panelVisibilityBinding.Converter = new BooleanToVisibilityConverter();
BindingOperations.SetBinding(panelBorder, UIElement.VisibilityProperty, panelVisibilityBinding);
StackPanel rootPanel = new StackPanel();
rootPanel.Orientation = Orientation.Vertical;
DockPanel headerPanel = new DockPanel();
TextBlock titleText = new TextBlock();
titleText.Text = "Overlay Debug";
titleText.Foreground = Brushes.White;
titleText.FontSize = 13.0;
titleText.FontWeight = FontWeights.SemiBold;
titleText.Margin = new Thickness(0.0, 0.0, 8.0, 6.0);
DockPanel.SetDock(titleText, Dock.Left);
headerPanel.Children.Add(titleText);
Button closeButton = CreateDebugButton("<22><><EFBFBD><EFBFBD>", 44.0);
closeButton.Margin = new Thickness(6.0, 0.0, 0.0, 6.0);
closeButton.Click += delegate(object sender, RoutedEventArgs e)
{
OverlaySettings.HideDebugPanel();
};
DockPanel.SetDock(closeButton, Dock.Right);
headerPanel.Children.Add(closeButton);
rootPanel.Children.Add(headerPanel);
TextBlock summaryText = new TextBlock();
summaryText.Foreground = new SolidColorBrush(Color.FromRgb(210, 221, 236));
summaryText.FontSize = 12.0;
summaryText.Margin = new Thickness(0.0, 0.0, 0.0, 8.0);
Binding summaryBinding = new Binding(nameof(HeaderDeviceStatusOverlaySettings.PositionSummary));
summaryBinding.Source = OverlaySettings;
BindingOperations.SetBinding(summaryText, TextBlock.TextProperty, summaryBinding);
rootPanel.Children.Add(summaryText);
rootPanel.Children.Add(CreateDebugInputRow("Left", nameof(HeaderDeviceStatusOverlaySettings.LeftMarginText)));
rootPanel.Children.Add(CreateDebugInputRow("Top", nameof(HeaderDeviceStatusOverlaySettings.TopMarginText)));
rootPanel.Children.Add(CreateDebugInputRow("Step", nameof(HeaderDeviceStatusOverlaySettings.StepText)));
UniformGrid actionGrid = new UniformGrid();
actionGrid.Columns = 4;
actionGrid.Rows = 1;
actionGrid.Margin = new Thickness(0.0, 0.0, 0.0, 8.0);
Button leftButton = CreateDebugButton("<22><>", 40.0);
leftButton.Click += delegate(object sender, RoutedEventArgs e)
{
OverlaySettings.AdjustMargin(-OverlaySettings.Step, 0.0);
};
actionGrid.Children.Add(leftButton);
Button rightButton = CreateDebugButton("<22><>", 40.0);
rightButton.Click += delegate(object sender, RoutedEventArgs e)
{
OverlaySettings.AdjustMargin(OverlaySettings.Step, 0.0);
};
actionGrid.Children.Add(rightButton);
Button upButton = CreateDebugButton("<22><>", 40.0);
upButton.Click += delegate(object sender, RoutedEventArgs e)
{
OverlaySettings.AdjustMargin(0.0, -OverlaySettings.Step);
};
actionGrid.Children.Add(upButton);
Button downButton = CreateDebugButton("<22><>", 40.0);
downButton.Click += delegate(object sender, RoutedEventArgs e)
{
OverlaySettings.AdjustMargin(0.0, OverlaySettings.Step);
};
actionGrid.Children.Add(downButton);
rootPanel.Children.Add(actionGrid);
Button applyButton = CreateDebugButton("Ӧ<><D3A6>", 0.0);
applyButton.Width = 178.0;
applyButton.Margin = new Thickness(0.0, 0.0, 0.0, 0.0);
applyButton.Click += delegate(object sender, RoutedEventArgs e)
{
OverlaySettings.ApplyTextValues();
};
rootPanel.Children.Add(applyButton);
panelBorder.Child = rootPanel;
Panel.SetZIndex(panelBorder, short.MaxValue);
return panelBorder;
}
private static FrameworkElement CreateDebugInputRow(string labelText, string bindingPath)
{
Grid rowGrid = new Grid();
rowGrid.Margin = new Thickness(0.0, 0.0, 0.0, 6.0);
rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(44.0) });
rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(92.0) });
rowGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(56.0) });
TextBlock label = new TextBlock();
label.Text = labelText;
label.Foreground = new SolidColorBrush(Color.FromRgb(210, 221, 236));
label.FontSize = 12.0;
label.VerticalAlignment = VerticalAlignment.Center;
Grid.SetColumn(label, 0);
rowGrid.Children.Add(label);
TextBox inputBox = CreateDebugTextBox(bindingPath);
Grid.SetColumn(inputBox, 1);
rowGrid.Children.Add(inputBox);
TextBlock hint = new TextBlock();
hint.Text = "<22>س<EFBFBD>";
hint.Foreground = new SolidColorBrush(Color.FromRgb(150, 167, 188));
hint.FontSize = 11.0;
hint.VerticalAlignment = VerticalAlignment.Center;
hint.HorizontalAlignment = HorizontalAlignment.Right;
Grid.SetColumn(hint, 2);
rowGrid.Children.Add(hint);
return rowGrid;
}
private static TextBox CreateDebugTextBox(string bindingPath)
{
TextBox inputBox = new TextBox();
inputBox.Height = 26.0;
inputBox.Margin = new Thickness(0.0);
inputBox.Padding = new Thickness(6.0, 2.0, 6.0, 2.0);
inputBox.Background = new SolidColorBrush(Color.FromRgb(33, 46, 60));
inputBox.BorderBrush = new SolidColorBrush(Color.FromRgb(96, 122, 149));
inputBox.Foreground = Brushes.White;
inputBox.FontSize = 12.0;
inputBox.VerticalContentAlignment = VerticalAlignment.Center;
Binding textBinding = new Binding(bindingPath);
textBinding.Source = OverlaySettings;
textBinding.Mode = BindingMode.TwoWay;
textBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(inputBox, TextBox.TextProperty, textBinding);
inputBox.LostFocus += delegate(object sender, RoutedEventArgs e)
{
OverlaySettings.ApplyTextValues();
};
inputBox.KeyDown += delegate(object sender, KeyEventArgs e)
{
if (e.Key != Key.Enter)
{
return;
}
OverlaySettings.ApplyTextValues();
e.Handled = true;
};
return inputBox;
}
private static Button CreateDebugButton(string content, double width)
{
Button button = new Button();
button.Content = content;
if (width > 0.0)
{
button.Width = width;
}
button.Height = 28.0;
button.Margin = new Thickness(0.0, 0.0, 6.0, 0.0);
button.Padding = new Thickness(4.0, 0.0, 4.0, 0.0);
button.Background = new SolidColorBrush(Color.FromRgb(43, 62, 81));
button.BorderBrush = new SolidColorBrush(Color.FromRgb(96, 122, 149));
button.Foreground = Brushes.White;
button.FontSize = 12.0;
return button;
}
private sealed class HeaderDeviceStatusOverlaySettings : PropertyChangedBase, IDisposable
{
private const double DefaultTopMargin = 8.0;
private const double DefaultLeftMargin = 8.0;
private const double DefaultDebugPanelTopOffset = 50.0;
private const double DefaultAdjustStep = 1.0;
private const string ConfigurationDirectoryName = "Configuration";
private const string ConfigurationFileName = "HeaderDeviceStatusOverlay.json";
private readonly string _configurationDirectoryPath;
private readonly string _configurationFilePath;
private readonly FileSystemWatcher _fileWatcher;
private double _topMargin = DefaultTopMargin;
private double _leftMargin = DefaultLeftMargin;
private bool _isDebugPanelVisible;
private double _step = DefaultAdjustStep;
private string _leftMarginText = DefaultLeftMargin.ToString("0.##", CultureInfo.InvariantCulture);
private string _topMarginText = DefaultTopMargin.ToString("0.##", CultureInfo.InvariantCulture);
private string _stepText = DefaultAdjustStep.ToString("0.##", CultureInfo.InvariantCulture);
public HeaderDeviceStatusOverlaySettings()
{
_configurationDirectoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ConfigurationDirectoryName);
_configurationFilePath = Path.Combine(_configurationDirectoryPath, ConfigurationFileName);
EnsureConfigurationFile();
LoadFromFile();
_fileWatcher = new FileSystemWatcher(_configurationDirectoryPath, ConfigurationFileName);
_fileWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size | NotifyFilters.CreationTime;
_fileWatcher.Changed += OnConfigurationFileChanged;
_fileWatcher.Created += OnConfigurationFileChanged;
_fileWatcher.Renamed += OnConfigurationFileRenamed;
_fileWatcher.EnableRaisingEvents = true;
}
public Thickness Margin
{
get
{
return new Thickness(_leftMargin, _topMargin, 0.0, 0.0);
}
}
public Thickness DebugPanelMargin
{
get
{
return new Thickness(_leftMargin, _topMargin + DefaultDebugPanelTopOffset, 0.0, 0.0);
}
}
public bool IsDebugPanelVisible
{
get
{
return _isDebugPanelVisible;
}
}
public double Step
{
get
{
return _step;
}
}
public string LeftMarginText
{
get
{
return _leftMarginText;
}
set
{
SetAndNotify(ref _leftMarginText, value);
}
}
public string TopMarginText
{
get
{
return _topMarginText;
}
set
{
SetAndNotify(ref _topMarginText, value);
}
}
public string StepText
{
get
{
return _stepText;
}
set
{
SetAndNotify(ref _stepText, value);
}
}
public string PositionSummary
{
get
{
return string.Format("Left={0:0.##}, Top={1:0.##}, Step={2:0.##}", _leftMargin, _topMargin, _step);
}
}
public void Dispose()
{
_fileWatcher.Changed -= OnConfigurationFileChanged;
_fileWatcher.Created -= OnConfigurationFileChanged;
_fileWatcher.Renamed -= OnConfigurationFileRenamed;
_fileWatcher.Dispose();
}
public void AdjustMargin(double leftOffset, double topOffset)
{
double leftMargin = _leftMargin + leftOffset;
double topMargin = _topMargin + topOffset;
UpdateSettings(leftMargin, topMargin, _isDebugPanelVisible, _step);
PersistCurrentConfiguration();
}
public void HideDebugPanel()
{
UpdateSettings(_leftMargin, _topMargin, false, _step);
PersistCurrentConfiguration();
}
public void ApplyTextValues()
{
double leftMargin;
double topMargin;
double step;
bool canParseLeft = TryParseInput(LeftMarginText, _leftMargin, out leftMargin);
bool canParseTop = TryParseInput(TopMarginText, _topMargin, out topMargin);
bool canParseStep = TryParseInput(StepText, _step, out step);
if (!canParseStep)
{
step = _step;
}
step = NormalizeStep(step);
UpdateSettings(leftMargin, topMargin, _isDebugPanelVisible, step);
PersistCurrentConfiguration();
}
private void OnConfigurationFileChanged(object sender, FileSystemEventArgs e)
{
ReloadOnUiThread();
}
private void OnConfigurationFileRenamed(object sender, RenamedEventArgs e)
{
ReloadOnUiThread();
}
private void ReloadOnUiThread()
{
Application currentApplication = Application.Current;
if (currentApplication == null)
{
LoadFromFile();
return;
}
currentApplication.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(LoadFromFile));
}
private void EnsureConfigurationFile()
{
if (!Directory.Exists(_configurationDirectoryPath))
{
Directory.CreateDirectory(_configurationDirectoryPath);
}
if (File.Exists(_configurationFilePath))
{
return;
}
HeaderDeviceStatusOverlayPositionConfiguration configuration = new HeaderDeviceStatusOverlayPositionConfiguration();
configuration.TopMargin = DefaultTopMargin;
configuration.LeftMargin = DefaultLeftMargin;
configuration.ShowDebugPanel = false;
configuration.AdjustStep = DefaultAdjustStep;
SaveConfiguration(configuration);
}
private void LoadFromFile()
{
HeaderDeviceStatusOverlayPositionConfiguration configuration = TryReadConfiguration();
if (configuration == null)
{
return;
}
double topMargin = NormalizeMargin(configuration.TopMargin, DefaultTopMargin);
double leftMargin = NormalizeMargin(configuration.LeftMargin, DefaultLeftMargin);
double adjustStep = NormalizeStep(configuration.AdjustStep);
UpdateSettings(leftMargin, topMargin, configuration.ShowDebugPanel, adjustStep);
}
private HeaderDeviceStatusOverlayPositionConfiguration TryReadConfiguration()
{
for (int attempt = 0; attempt < 3; attempt++)
{
try
{
string content = File.ReadAllText(_configurationFilePath);
if (string.IsNullOrWhiteSpace(content))
{
return new HeaderDeviceStatusOverlayPositionConfiguration();
}
HeaderDeviceStatusOverlayPositionConfiguration configuration = JsonConvert.DeserializeObject<HeaderDeviceStatusOverlayPositionConfiguration>(content);
return configuration ?? new HeaderDeviceStatusOverlayPositionConfiguration();
}
catch (IOException)
{
}
catch (UnauthorizedAccessException)
{
}
catch (JsonException)
{
return null;
}
}
return null;
}
private void SaveConfiguration(HeaderDeviceStatusOverlayPositionConfiguration configuration)
{
string content = JsonConvert.SerializeObject(configuration, Formatting.Indented);
File.WriteAllText(_configurationFilePath, content);
}
private void PersistCurrentConfiguration()
{
HeaderDeviceStatusOverlayPositionConfiguration configuration = new HeaderDeviceStatusOverlayPositionConfiguration();
configuration.TopMargin = _topMargin;
configuration.LeftMargin = _leftMargin;
configuration.ShowDebugPanel = _isDebugPanelVisible;
configuration.AdjustStep = _step;
SaveConfiguration(configuration);
}
private static double NormalizeMargin(double value, double fallbackValue)
{
if (double.IsNaN(value) || double.IsInfinity(value))
{
return fallbackValue;
}
return value;
}
private static double NormalizeStep(double value)
{
if (double.IsNaN(value) || double.IsInfinity(value) || value <= 0.0)
{
return DefaultAdjustStep;
}
return value;
}
private void UpdateSettings(double leftMargin, double topMargin, bool isDebugPanelVisible, double step)
{
bool hasChanged = !leftMargin.Equals(_leftMargin)
|| !topMargin.Equals(_topMargin)
|| !isDebugPanelVisible.Equals(_isDebugPanelVisible)
|| !step.Equals(_step);
if (!hasChanged)
{
return;
}
_leftMargin = leftMargin;
_topMargin = topMargin;
_isDebugPanelVisible = isDebugPanelVisible;
_step = step;
LeftMarginText = FormatValue(_leftMargin);
TopMarginText = FormatValue(_topMargin);
StepText = FormatValue(_step);
NotifyOfPropertyChange(nameof(Margin));
NotifyOfPropertyChange(nameof(DebugPanelMargin));
NotifyOfPropertyChange(nameof(IsDebugPanelVisible));
NotifyOfPropertyChange(nameof(Step));
NotifyOfPropertyChange(nameof(PositionSummary));
}
private static bool TryParseInput(string text, double fallbackValue, out double value)
{
string trimmedText = string.IsNullOrWhiteSpace(text) ? string.Empty : text.Trim();
if (string.IsNullOrEmpty(trimmedText))
{
value = fallbackValue;
return false;
}
return double.TryParse(trimmedText, NumberStyles.Float, CultureInfo.InvariantCulture, out value)
|| double.TryParse(trimmedText, NumberStyles.Float, CultureInfo.CurrentCulture, out value);
}
private static string FormatValue(double value)
{
return value.ToString("0.##", CultureInfo.InvariantCulture);
}
}
private sealed class HeaderDeviceStatusOverlayPositionConfiguration
{
public double TopMargin { get; set; }
public double LeftMargin { get; set; }
public bool ShowDebugPanel { get; set; }
public double AdjustStep { get; set; }
}
}
}

View File

@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Windows;
namespace MainShell.Common
{
/// <summary>
/// Localization resource helper.
/// </summary>
public static class LanguageResourceHelper
{
private static readonly IReadOnlyDictionary<MessageKey, string> ResourceKeyMap =
new Dictionary<MessageKey, string>
{
{ MessageKey.TitleInfo, "Msg_Title_Info" },
{ MessageKey.TitleWarning, "Msg_Title_Warning" },
{ MessageKey.TitleError, "Msg_Title_Error" },
{ MessageKey.TitleConfirm, "Msg_Title_Confirm" },
{ MessageKey.VisionCameraNotFound, "Msg_Vision_CameraNotFound" },
{ MessageKey.VisionCameraNotOpen, "Msg_Vision_CameraNotOpen" },
{ MessageKey.VisionCameraNotGrabbing, "Msg_Vision_CameraNotGrabbing" },
{ MessageKey.VisionCaptureTimeout, "Msg_Vision_CaptureTimeout" },
{ MessageKey.VisionNoFrameReturned, "Msg_Vision_NoFrameReturned" },
{ MessageKey.VisionSoftTriggerFailed, "Msg_Vision_SoftTriggerFailed" },
{ MessageKey.VisionDriverError, "Msg_Vision_DriverError" },
{ MessageKey.VisionImageIsNull, "Msg_Vision_ImageIsNull" },
{ MessageKey.VisionRequestInvalid, "Msg_Vision_RequestInvalid" },
{ MessageKey.VisionTimeoutInvalid, "Msg_Vision_TimeoutInvalid" },
{ MessageKey.VisionOperationCancelled, "Msg_Vision_OperationCancelled" },
{ MessageKey.VisionTemplatePathEmpty, "Msg_Vision_TemplatePathEmpty" },
{ MessageKey.VisionTemplateRoiInvalid, "Msg_Vision_TemplateRoiInvalid" },
{ MessageKey.VisionTemplateMinScoreInvalid, "Msg_Vision_TemplateMinScoreInvalid" },
{ MessageKey.VisionTemplateAlgorithmNotImplemented, "Msg_Vision_TemplateAlgorithmNotImplemented" },
{ MessageKey.VisionTemplateMatchFailed, "Msg_Vision_TemplateMatchFailed" },
{ MessageKey.VisionCommonAlgorithmNotSupported, "Msg_Vision_CommonAlgorithmNotSupported" },
{ MessageKey.VisionCommonAlgorithmExecutionFailed, "Msg_Vision_CommonAlgorithmExecutionFailed" },
{ MessageKey.VisionChipMapSortInputInvalid, "Msg_Vision_ChipMapSortInputInvalid" },
{ MessageKey.CommonUnknownError, "Msg_Common_UnknownError" },
{ MessageKey.CommonOperationSucceeded, "Msg_Common_OperationSucceeded" },
{ MessageKey.CommonOperationFailed, "Msg_Common_OperationFailed" },
{ MessageKey.CommonSaveSucceeded, "Msg_Common_SaveSucceeded" },
{ MessageKey.CommonSaveFailed, "Msg_Common_SaveFailed" },
{ MessageKey.AxisSoftLimitSaveConfirm, "Msg_AxisSoftLimit_SaveConfirm" },
{ MessageKey.AxisSoftLimitSaveFailedWithReason, "Msg_AxisSoftLimit_SaveFailedWithReason" },
{ MessageKey.AxisSoftLimitLoadFailedWithReason, "Msg_AxisSoftLimit_LoadFailedWithReason" },
{ MessageKey.AxisSoftLimitRangeInvalid, "Msg_AxisSoftLimit_RangeInvalid" },
{ MessageKey.AxisSoftLimitApplySucceeded, "Msg_AxisSoftLimit_ApplySucceeded" },
{ MessageKey.AxisSoftLimitApplyFailedWithReason, "Msg_AxisSoftLimit_ApplyFailedWithReason" },
{ MessageKey.AxisSoftLimitApplyAllSucceeded, "Msg_AxisSoftLimit_ApplyAllSucceeded" },
{ MessageKey.AxisSoftLimitApplyAllFailedWithReason, "Msg_AxisSoftLimit_ApplyAllFailedWithReason" },
{ MessageKey.AxisSoftLimitNoAxisData, "Msg_AxisSoftLimit_NoAxisData" },
{ MessageKey.AxisSoftLimitSelectedAxisRequired, "Msg_AxisSoftLimit_SelectedAxisRequired" },
{ MessageKey.CommonDeleteConfirm, "Msg_Common_DeleteConfirm" },
{ MessageKey.CommonExitConfirm, "Msg_Common_ExitConfirm" },
{ MessageKey.DeviceNotInitialized, "Msg_Device_NotInitialized" },
{ MessageKey.DeviceBusy, "Msg_Device_Busy" },
{ MessageKey.DeviceDisconnected, "Msg_Device_Disconnected" },
{ MessageKey.DeviceHomeRequired, "Msg_Device_HomeRequired" },
{ MessageKey.ParamInvalid, "Msg_Param_Invalid" },
{ MessageKey.ParamEmpty, "Msg_Param_Empty" },
{ MessageKey.ParamOutOfRange, "Msg_Param_OutOfRange" },
{ MessageKey.StartProcessConfirm, "Msg_Process_StartConfirm" },
{ MessageKey.StartProcessFailed, "Msg_Process_StartFailed" },
{ MessageKey.StopProcessConfirm, "Msg_Process_StopConfirm" },
{ MessageKey.StopProcessFailed, "Msg_Process_StopFailed" },
{ MessageKey.ProcessFailed, "Msg_Process_Failed" },
{ MessageKey.ProcessFailedWithReason, "Msg_Process_FailedWithReason" },
{ MessageKey.ProcessStepFailedWithReason, "Msg_Process_StepFailedWithReason" },
{ MessageKey.ProcessSubstratePositionFailedWithReason, "Msg_Process_SubstratePositionFailedWithReason" },
{ MessageKey.ProcessSubstratePositionRecipeNotLoaded, "Msg_Process_SubstratePositionRecipeNotLoaded" },
{ MessageKey.ProcessSubstratePositionMarkParameterMissing, "Msg_Process_SubstratePositionMarkParameterMissing" },
{ MessageKey.ProcessSubstratePositionNoEnabledMarks, "Msg_Process_SubstratePositionNoEnabledMarks" },
{ MessageKey.ProcessSubstratePositionAlignmentFailedWithReason, "Msg_Process_SubstratePositionAlignmentFailedWithReason" },
{ MessageKey.ProcessDiePositionFailedWithReason, "Msg_Process_DiePositionFailedWithReason" },
{ MessageKey.ProcessDiePositionRecipeNotLoaded, "Msg_Process_DiePositionRecipeNotLoaded" },
{ MessageKey.ProcessDiePositionCoordinateGenerationMissing, "Msg_Process_DiePositionCoordinateGenerationMissing" },
{ MessageKey.ProcessDiePositionWaferInfoMissing, "Msg_Process_DiePositionWaferInfoMissing" },
{ MessageKey.ProcessSubstrateHeightMeasureFailedWithReason, "Msg_Process_SubstrateHeightMeasureFailedWithReason" },
{ MessageKey.ProcessSubstrateHeightMeasureRecipeNotLoaded, "Msg_Process_SubstrateHeightMeasureRecipeNotLoaded" },
{ MessageKey.ProcessSubstrateHeightMeasureSettingMissing, "Msg_Process_SubstrateHeightMeasureSettingMissing" },
{ MessageKey.ProcessSubstrateHeightMeasureNoPoints, "Msg_Process_SubstrateHeightMeasureNoPoints" },
{ MessageKey.ProcessSubstrateHeightMeasurePointPositionInvalid, "Msg_Process_SubstrateHeightMeasurePointPositionInvalid" },
{ MessageKey.NeedleCalibrationLoadFailed, "Msg_NeedleCalibration_LoadFailed" },
{ MessageKey.NeedleCalibrationTouchCountMustGreaterThanZero, "Msg_NeedleCalibration_TouchCountMustGreaterThanZero" },
{ MessageKey.NeedleCalibrationCompleted, "Msg_NeedleCalibration_Completed" },
{ MessageKey.NeedleCalibrationStopped, "Msg_NeedleCalibration_Stopped" },
{ MessageKey.NeedleCalibrationCanceled, "Msg_NeedleCalibration_Canceled" },
{ MessageKey.OriginCalibOpenVisionTemplateFailed, "Msg_OriginCalib_OpenVisionTemplateFailed" },
{ MessageKey.OriginCalibAxisNotFound, "Msg_OriginCalib_AxisNotFound" },
{ MessageKey.OriginCalibReadPositionFailed, "Msg_OriginCalib_ReadPositionFailed" },
{ MessageKey.OriginCalibMoveFailed, "Msg_OriginCalib_MoveFailed" },
{ MessageKey.OriginCalibDeleteAxisFailed, "Msg_OriginCalib_DeleteAxisFailed" },
{ MessageKey.OriginCalibDeleteAxisDuringCalibration, "Msg_OriginCalib_DeleteAxisDuringCalibration" },
{ MessageKey.OriginCalibCalibrationCanceled, "Msg_OriginCalib_CalibrationCanceled" },
{ MessageKey.OriginCalibCalibrationFailed, "Msg_OriginCalib_CalibrationFailed" },
{ MessageKey.OriginCalibConfigSaved, "Msg_OriginCalib_ConfigSaved" },
{ MessageKey.OriginCalibConfigSaveFailed, "Msg_OriginCalib_ConfigSaveFailed" },
{ MessageKey.OriginCalibNoCalibratedModules, "Msg_OriginCalib_NoCalibratedModules" },
{ MessageKey.OriginCalibControllerWriteCompleted, "Msg_OriginCalib_ControllerWriteCompleted" },
{ MessageKey.OriginCalibControllerWriteNotReady, "Msg_OriginCalib_ControllerWriteNotReady" },
{ MessageKey.OriginCalibControllerWritePartialSuccess, "Msg_OriginCalib_ControllerWritePartialSuccess" },
{ MessageKey.OriginCalibControllerWriteNotImplemented, "Msg_OriginCalib_ControllerWriteNotImplemented" },
{ MessageKey.PidNoProfileSelected, "Msg_Pid_NoProfileSelected" },
{ MessageKey.PidNoFilteringParameterSelected, "Msg_Pid_NoFilteringParameterSelected" },
{ MessageKey.PidReadSucceeded, "Msg_Pid_ReadSucceeded" },
{ MessageKey.PidReadFailed, "Msg_Pid_ReadFailed" },
{ MessageKey.PidWriteSucceeded, "Msg_Pid_WriteSucceeded" },
{ MessageKey.PidWriteFailed, "Msg_Pid_WriteFailed" },
{ MessageKey.DieRecheckSelectedPointRequired, "Msg_DieRecheck_SelectedPointRequired" },
{ MessageKey.DieRecheckMoveDeviceUnavailable, "Msg_DieRecheck_MoveDeviceUnavailable" },
{ MessageKey.DieRecheckStatusOk, "Msg_DieRecheck_StatusOk" },
{ MessageKey.DieRecheckStatusMissingBond, "Msg_DieRecheck_StatusMissingBond" },
{ MessageKey.DieRecheckStatusXExceeded, "Msg_DieRecheck_StatusXExceeded" },
{ MessageKey.DieRecheckStatusYExceeded, "Msg_DieRecheck_StatusYExceeded" }
};
public static string GetString(MessageKey messageKey)
{
return GetString(messageKey, messageKey.ToString());
}
public static string GetString(MessageKey messageKey, string fallback)
{
if (messageKey == MessageKey.None)
{
return fallback ?? string.Empty;
}
if (!ResourceKeyMap.TryGetValue(messageKey, out var resourceKey))
{
return fallback ?? messageKey.ToString();
}
var application = Application.Current;
if (application == null)
{
return fallback ?? resourceKey;
}
var resource = application.TryFindResource(resourceKey);
if (resource is string text && !string.IsNullOrWhiteSpace(text))
{
return text;
}
return fallback ?? resourceKey;
}
public static string Format(MessageKey messageKey, params object[] args)
{
var format = GetString(messageKey);
return args == null || args.Length == 0
? format
: string.Format(format, args);
}
}
}

View File

@@ -0,0 +1,104 @@
using MainShell.Resources.CustomControl;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace MainShell.Common
{
public static class Loading
{
private static LoadingWaitView _view;
// 全局初始化:在主程序启动或插件加载时调用一次
public static void Initialize(Window mainWindow)
{
if (_view != null) return; // 避免重复加载
_view = new LoadingWaitView();
// 获取窗口的根内容
var rootContent = mainWindow.Content;
if (rootContent is Panel rootPanel)
{
// 如果根部本来就是 Panel (Grid/Canvas等),直接添加
rootPanel.Children.Add(_view);
}
else if (rootContent is ContentControl contentControl)
{
// 如果根部是 ContentControl,我们需要把原本的内容包在一个新的 Grid 里
var originalContent = contentControl.Content as UIElement;
contentControl.Content = null;
var wrapperGrid = new Grid();
wrapperGrid.Children.Add(originalContent); // 原有内容
wrapperGrid.Children.Add(_view); // 我们的遮罩层
contentControl.Content = wrapperGrid;
}
// 确保遮罩层铺满并置于顶层
Grid.SetColumnSpan(_view, 99);
Grid.SetRowSpan(_view, 99);
Panel.SetZIndex(_view, 9999);
}
// 基础调用:Loading.Show("计算中...");
public static void Show(string msg, bool isIndeterminate, bool canCancel = true) => LoadingService.Instance.Show(msg, canCancel, isIndeterminate);
public static void Hide() => LoadingService.Instance.Hide();
public static void Report(double val) => LoadingService.Instance.Report(val);
// 获取当前令牌:Loading.Token
public static CancellationToken Token => LoadingService.Instance.Token;
// 高级用法:使用 using 块自动开启和关闭
public static IDisposable Manager(string msg, bool canCancel = true)
{
Show(msg, canCancel);
return new DisposableAction(() => Hide());
}
public static async Task<bool> RunAsync(
Func<CancellationToken, IProgress<double>, Task> work,
string message,
bool canCancel = true, bool isIndeterminate = false,
Action<Exception> onError = null)
{
using (Manager(message, canCancel))
{
try
{
LoadingService.Instance.Show(message, isIndeterminate, canCancel);
var progress = new Progress<double>(val =>
Application.Current.Dispatcher.Invoke(() => LoadingService.Instance.ProgressValue = val));
await Task.Run(() => work(LoadingService.Instance.Token, progress), LoadingService.Instance.Token);
return true; // 执行成功
}
catch (OperationCanceledException)
{
// 用户点击了取消,通常不需要弹窗,直接返回 false
return false;
}
catch (Exception ex)
{
// 捕获其他所有异常(硬件错误、逻辑错误等)
if (onError != null)
onError(ex);
else
MessageBox.Show($"操作失败: {ex.Message}", "系统错误", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}
}
}
}

View File

@@ -0,0 +1,55 @@
using System.Windows;
namespace MainShell.Common
{
/// <summary>
/// <20><><EFBFBD>ڶ<EFBFBD><DAB6><EFBFBD><EFBFBD><EFBFBD>ö<EFBFBD><C3B6><EFBFBD><EFBFBD>Ϣ<EFBFBD><CFA2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϣ<EFBFBD><CFA2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>װ<EFBFBD><D7B0>
/// </summary>
public static class LocalizedMessageBox
{
public static MessageBoxResult Show(MessageKey messageKey)
{
return Show(null, messageKey, MessageKey.TitleInfo, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.None);
}
public static MessageBoxResult Show(MessageKey messageKey, MessageKey captionKey)
{
return Show(null, messageKey, captionKey, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.None);
}
public static MessageBoxResult Show(MessageKey messageKey, MessageKey captionKey, MessageBoxButton button, MessageBoxImage icon)
{
return Show(null, messageKey, captionKey, button, icon, MessageBoxResult.None);
}
public static MessageBoxResult Show(Window owner, MessageKey messageKey, MessageKey captionKey, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult = MessageBoxResult.None)
{
return MwMessageBox.InvokeOnUiThread(() => ShowCore(owner, messageKey, captionKey, button, icon, defaultResult));
}
public static MessageBoxResult ShowFormat(MessageKey messageKey, MessageKey captionKey, MessageBoxButton button, MessageBoxImage icon, params object[] args)
{
return MwMessageBox.InvokeOnUiThread(() => ShowFormatCore(messageKey, captionKey, button, icon, args));
}
private static MessageBoxResult ShowCore(Window owner, MessageKey messageKey, MessageKey captionKey, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult)
{
string message = LanguageResourceHelper.GetString(messageKey);
string caption = captionKey == MessageKey.None
? string.Empty
: LanguageResourceHelper.GetString(captionKey);
return MwMessageBox.Show(owner, message, caption, button, icon, defaultResult);
}
private static MessageBoxResult ShowFormatCore(MessageKey messageKey, MessageKey captionKey, MessageBoxButton button, MessageBoxImage icon, object[] args)
{
string message = LanguageResourceHelper.Format(messageKey, args);
string caption = captionKey == MessageKey.None
? string.Empty
: LanguageResourceHelper.GetString(captionKey);
return MwMessageBox.Show(message, caption, button, icon);
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
namespace MainShell.Common
{
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڲ<EFBFBD><DAB2><EFBFBD> MessageKey <20><>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD><EFBFBD>ܿ<EFBFBD>ʧ<EFBFBD>ܣ<EFBFBD><DCA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʽ<EFBFBD><CABD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
/// </summary>
public class LocalizedProcessException : Exception
{
public MessageKey FailureMessageKey { get; }
public string[] FailureMessageArguments { get; }
public LocalizedProcessException(MessageKey failureMessageKey, params string[] failureMessageArguments)
: base(LanguageResourceHelper.Format(failureMessageKey, ConvertArguments(failureMessageArguments)))
{
FailureMessageKey = failureMessageKey;
FailureMessageArguments = failureMessageArguments ?? Array.Empty<string>();
}
public static object[] ConvertArguments(string[] failureMessageArguments)
{
if (failureMessageArguments == null || failureMessageArguments.Length == 0)
{
return Array.Empty<object>();
}
object[] convertedArguments = new object[failureMessageArguments.Length];
for (int i = 0; i < failureMessageArguments.Length; i++)
{
convertedArguments[i] = failureMessageArguments[i];
}
return convertedArguments;
}
}
}

View File

@@ -0,0 +1,121 @@
using System;
namespace MainShell.Common
{
/// <summary>
/// Message keys used by the localization system.
/// </summary>
public enum MessageKey
{
None = 0,
TitleInfo,
TitleWarning,
TitleError,
TitleConfirm,
VisionCameraNotFound,
VisionCameraNotOpen,
VisionCameraNotGrabbing,
VisionCaptureTimeout,
VisionNoFrameReturned,
VisionSoftTriggerFailed,
VisionDriverError,
VisionImageIsNull,
VisionRequestInvalid,
VisionTimeoutInvalid,
VisionOperationCancelled,
VisionTemplatePathEmpty,
VisionTemplateRoiInvalid,
VisionTemplateMinScoreInvalid,
VisionTemplateAlgorithmNotImplemented,
VisionTemplateMatchFailed,
VisionCommonAlgorithmNotSupported,
VisionCommonAlgorithmExecutionFailed,
VisionChipMapSortInputInvalid,
CommonUnknownError,
CommonOperationSucceeded,
CommonOperationFailed,
CommonSaveSucceeded,
CommonSaveFailed,
AxisSoftLimitSaveConfirm,
AxisSoftLimitSaveFailedWithReason,
AxisSoftLimitLoadFailedWithReason,
AxisSoftLimitRangeInvalid,
AxisSoftLimitApplySucceeded,
AxisSoftLimitApplyFailedWithReason,
AxisSoftLimitApplyAllSucceeded,
AxisSoftLimitApplyAllFailedWithReason,
AxisSoftLimitNoAxisData,
AxisSoftLimitSelectedAxisRequired,
CommonDeleteConfirm,
CommonExitConfirm,
DeviceNotInitialized,
DeviceBusy,
DeviceDisconnected,
DeviceHomeRequired,
ParamInvalid,
ParamEmpty,
ParamOutOfRange,
StartProcessConfirm,
StartProcessFailed,
StopProcessConfirm,
StopProcessFailed,
ProcessFailed,
ProcessFailedWithReason,
ProcessStepFailedWithReason,
ProcessSubstratePositionFailedWithReason,
ProcessSubstratePositionRecipeNotLoaded,
ProcessSubstratePositionMarkParameterMissing,
ProcessSubstratePositionNoEnabledMarks,
ProcessSubstratePositionAlignmentFailedWithReason,
ProcessDiePositionFailedWithReason,
ProcessDiePositionRecipeNotLoaded,
ProcessDiePositionCoordinateGenerationMissing,
ProcessDiePositionWaferInfoMissing,
ProcessSubstrateHeightMeasureFailedWithReason,
ProcessSubstrateHeightMeasureRecipeNotLoaded,
ProcessSubstrateHeightMeasureSettingMissing,
ProcessSubstrateHeightMeasureNoPoints,
ProcessSubstrateHeightMeasurePointPositionInvalid,
NeedleCalibrationLoadFailed,
NeedleCalibrationTouchCountMustGreaterThanZero,
NeedleCalibrationCompleted,
NeedleCalibrationStopped,
NeedleCalibrationCanceled,
OriginCalibOpenVisionTemplateFailed,
OriginCalibAxisNotFound,
OriginCalibReadPositionFailed,
OriginCalibMoveFailed,
OriginCalibDeleteAxisFailed,
OriginCalibDeleteAxisDuringCalibration,
OriginCalibCalibrationCanceled,
OriginCalibCalibrationFailed,
OriginCalibConfigSaved,
OriginCalibConfigSaveFailed,
OriginCalibNoCalibratedModules,
OriginCalibControllerWriteCompleted,
OriginCalibControllerWriteNotReady,
OriginCalibControllerWritePartialSuccess,
OriginCalibControllerWriteNotImplemented,
PidNoProfileSelected,
PidNoFilteringParameterSelected,
PidReadSucceeded,
PidReadFailed,
PidWriteSucceeded,
PidWriteFailed,
DieRecheckSelectedPointRequired,
DieRecheckMoveDeviceUnavailable,
DieRecheckStatusOk,
DieRecheckStatusMissingBond,
DieRecheckStatusXExceeded,
DieRecheckStatusYExceeded
}
}

View File

@@ -0,0 +1,639 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using MainShell.Common.View;
using MainShell.Common.ViewModel;
namespace MainShell.Common
{
public static class MwMessageBoxGlyphs
{
public const string None = "\uE686";
public const string Information = "\uE625";
public const string Warning = "\uE7BD";
public const string Error = "\uE76C";
public const string Question = "\uE686";
}
public sealed class MwMessageBoxButtonText
{
public string Ok { get; set; } = "ȷ<><C8B7>";
public string Cancel { get; set; } = "ȡ<><C8A1>";
public string Yes { get; set; } = "<22><>";
public string No { get; set; } = "<22><>";
public string Retry { get; set; } = "<22><><EFBFBD><EFBFBD>";
public string Abort { get; set; } = "<22><>ֹ";
public string Ignore { get; set; } = "<22><><EFBFBD><EFBFBD>";
public static MwMessageBoxButtonText CreateDefault()
{
return new MwMessageBoxButtonText();
}
}
public sealed class MwMessageBoxButtonDefinition
{
public string Text { get; set; }
public MessageBoxResult Result { get; set; }
public bool IsDefault { get; set; }
public bool IsCancel { get; set; }
public bool IsPrimary { get; set; }
public Style ButtonStyle { get; set; }
}
public sealed class MwMessageBoxIconTheme
{
public string NoneGlyph { get; set; } = MwMessageBoxGlyphs.None;
public string InformationGlyph { get; set; } = MwMessageBoxGlyphs.Information;
public string WarningGlyph { get; set; } = MwMessageBoxGlyphs.Warning;
public string ErrorGlyph { get; set; } = MwMessageBoxGlyphs.Error;
public string QuestionGlyph { get; set; } = MwMessageBoxGlyphs.Question;
public static MwMessageBoxIconTheme CreateDefault()
{
return new MwMessageBoxIconTheme();
}
}
public sealed class MwMessageBoxColorPalette
{
public MwMessageBoxColorPalette()
{
}
public MwMessageBoxColorPalette(string accentColor, string iconBackgroundColor, string iconForegroundColor)
{
AccentColor = accentColor;
IconBackgroundColor = iconBackgroundColor;
IconForegroundColor = iconForegroundColor;
}
public string AccentColor { get; set; }
public string IconBackgroundColor { get; set; }
public string IconForegroundColor { get; set; }
}
public sealed class MwMessageBoxButtonColorTheme
{
public MwMessageBoxButtonColorTheme()
{
}
public MwMessageBoxButtonColorTheme(string backgroundColor, string hoverBackgroundColor, string pressedBackgroundColor, string borderColor, string foregroundColor)
{
BackgroundColor = backgroundColor;
HoverBackgroundColor = hoverBackgroundColor;
PressedBackgroundColor = pressedBackgroundColor;
BorderColor = borderColor;
ForegroundColor = foregroundColor;
}
public string BackgroundColor { get; set; }
public string HoverBackgroundColor { get; set; }
public string PressedBackgroundColor { get; set; }
public string BorderColor { get; set; }
public string ForegroundColor { get; set; }
}
public sealed class MwMessageBoxVisualTheme
{
public MwMessageBoxColorPalette NonePalette { get; set; } = new MwMessageBoxColorPalette("#9CA3AF", "#F3F4F6", "#6B7280");
public MwMessageBoxColorPalette InformationPalette { get; set; } = new MwMessageBoxColorPalette("#38BDF8", "#E0F2FE", "#0284C7");
public MwMessageBoxColorPalette WarningPalette { get; set; } = new MwMessageBoxColorPalette("#F59E0B", "#FEF3C7", "#D97706");
public MwMessageBoxColorPalette ErrorPalette { get; set; } = new MwMessageBoxColorPalette("#EF4444", "#FEE2E2", "#DC2626");
public MwMessageBoxColorPalette QuestionPalette { get; set; } = new MwMessageBoxColorPalette("#3B82F6", "#DBEAFE", "#2563EB");
public MwMessageBoxButtonColorTheme PrimaryButtonTheme { get; set; } = new MwMessageBoxButtonColorTheme("#0E6AE8", "#0B5AD0", "#094BAC", "#0E6AE8", "#FFFFFF");
public MwMessageBoxButtonColorTheme SecondaryButtonTheme { get; set; } = new MwMessageBoxButtonColorTheme("#F3F4F6", "#E5E7EB", "#D1D5DB", "#D1D5DB", "#374151");
public static MwMessageBoxVisualTheme CreateDefault()
{
return new MwMessageBoxVisualTheme();
}
}
public sealed class MwMessageBoxState
{
public string Caption { get; set; }
public string Message { get; set; }
public string IconGlyph { get; set; }
public string IconPathData { get; set; }
public string TimeText { get; set; }
public Brush AccentBrush { get; set; }
public Brush IconBackgroundBrush { get; set; }
public Brush IconForegroundBrush { get; set; }
public IReadOnlyList<MwMessageBoxButtonDefinition> Buttons { get; set; }
public MessageBoxResult DefaultResult { get; set; }
public TextAlignment MessageTextAlignment { get; set; } = TextAlignment.Left;
public FlowDirection WindowFlowDirection { get; set; } = FlowDirection.LeftToRight;
public FlowDirection MessageFlowDirection { get; set; } = FlowDirection.LeftToRight;
}
public static class MwMessageBox
{
public static MwMessageBoxButtonText DefaultButtonText { get; set; } = MwMessageBoxButtonText.CreateDefault();
public static MwMessageBoxIconTheme IconTheme { get; set; } = MwMessageBoxIconTheme.CreateDefault();
public static MwMessageBoxVisualTheme VisualTheme { get; set; } = MwMessageBoxVisualTheme.CreateDefault();
public static MessageBoxResult Show(string messageBoxText)
{
return Show(null, messageBoxText, string.Empty, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.None, 0, null);
}
public static MessageBoxResult Show(string messageBoxText, string caption)
{
return Show(null, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.None, 0, null);
}
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button)
{
return Show(null, messageBoxText, caption, button, MessageBoxImage.None, MessageBoxResult.None, 0, null);
}
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon)
{
return Show(null, messageBoxText, caption, button, icon, MessageBoxResult.None, 0, null);
}
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult)
{
return Show(null, messageBoxText, caption, button, icon, defaultResult, 0, null);
}
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult, MessageBoxOptions options)
{
return Show(null, messageBoxText, caption, button, icon, defaultResult, options, null);
}
public static MessageBoxResult Show(Window owner, string messageBoxText)
{
return Show(owner, messageBoxText, string.Empty, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.None, 0, null);
}
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption)
{
return Show(owner, messageBoxText, caption, MessageBoxButton.OK, MessageBoxImage.None, MessageBoxResult.None, 0, null);
}
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button)
{
return Show(owner, messageBoxText, caption, button, MessageBoxImage.None, MessageBoxResult.None, 0, null);
}
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon)
{
return Show(owner, messageBoxText, caption, button, icon, MessageBoxResult.None, 0, null);
}
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult)
{
return Show(owner, messageBoxText, caption, button, icon, defaultResult, 0, null);
}
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult, MessageBoxOptions options)
{
return Show(owner, messageBoxText, caption, button, icon, defaultResult, options, null);
}
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MwMessageBoxButtonText buttonText)
{
return Show(null, messageBoxText, caption, button, icon, MessageBoxResult.None, 0, buttonText);
}
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MwMessageBoxButtonText buttonText)
{
return Show(owner, messageBoxText, caption, button, icon, MessageBoxResult.None, 0, buttonText);
}
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult, MessageBoxOptions options, MwMessageBoxButtonText buttonText)
{
return InvokeOnUiThread(() => ShowCore(owner, messageBoxText, caption, button, icon, defaultResult, options, buttonText));
}
public static MessageBoxResult ShowCustom(string messageBoxText, string caption, MessageBoxImage icon, params MwMessageBoxButtonDefinition[] buttons)
{
return ShowCustom(null, messageBoxText, caption, icon, MessageBoxResult.None, 0, buttons);
}
public static MessageBoxResult ShowCustom(Window owner, string messageBoxText, string caption, MessageBoxImage icon, params MwMessageBoxButtonDefinition[] buttons)
{
return ShowCustom(owner, messageBoxText, caption, icon, MessageBoxResult.None, 0, buttons);
}
public static MessageBoxResult ShowCustom(Window owner, string messageBoxText, string caption, MessageBoxImage icon, MessageBoxResult defaultResult, MessageBoxOptions options, params MwMessageBoxButtonDefinition[] buttons)
{
return InvokeOnUiThread(() => ShowCustomCore(owner, messageBoxText, caption, icon, defaultResult, options, buttons));
}
internal static T InvokeOnUiThread<T>(Func<T> callback)
{
if (callback == null)
{
throw new ArgumentNullException(nameof(callback));
}
Application application = Application.Current;
if (application == null)
{
return callback();
}
if (application.Dispatcher == null || application.Dispatcher.CheckAccess())
{
return callback();
}
return application.Dispatcher.Invoke(callback);
}
private static MessageBoxResult ShowCore(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, MessageBoxResult defaultResult, MessageBoxOptions options, MwMessageBoxButtonText buttonText)
{
owner = owner ?? GetActiveWindow();
List<MwMessageBoxButtonDefinition> definitions = CreateButtonDefinitions(button, defaultResult, buttonText ?? DefaultButtonText).ToList();
MwMessageBoxState state = CreateState(messageBoxText, caption, icon, definitions, defaultResult, options);
MwMessageBoxWindowViewModel viewModel = new MwMessageBoxWindowViewModel(state);
MwMessageBoxWindow dialog = new MwMessageBoxWindow(viewModel);
if (owner != null)
{
dialog.Owner = owner;
dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
dialog.ShowDialog();
return viewModel.Result;
}
private static MessageBoxResult ShowCustomCore(Window owner, string messageBoxText, string caption, MessageBoxImage icon, MessageBoxResult defaultResult, MessageBoxOptions options, params MwMessageBoxButtonDefinition[] buttons)
{
owner = owner ?? GetActiveWindow();
List<MwMessageBoxButtonDefinition> definitions = NormalizeButtonDefinitions(buttons, defaultResult).ToList();
MwMessageBoxState state = CreateState(messageBoxText, caption, icon, definitions, defaultResult, options);
MwMessageBoxWindowViewModel viewModel = new MwMessageBoxWindowViewModel(state);
MwMessageBoxWindow dialog = new MwMessageBoxWindow(viewModel);
if (owner != null)
{
dialog.Owner = owner;
dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
dialog.ShowDialog();
return viewModel.Result;
}
private static IEnumerable<MwMessageBoxButtonDefinition> CreateButtonDefinitions(MessageBoxButton button, MessageBoxResult defaultResult, MwMessageBoxButtonText buttonText)
{
var labels = buttonText ?? MwMessageBoxButtonText.CreateDefault();
var definitions = new List<MwMessageBoxButtonDefinition>();
switch (button)
{
case MessageBoxButton.OK:
definitions.Add(CreateDefinition(labels.Ok, MessageBoxResult.OK, true));
break;
case MessageBoxButton.OKCancel:
definitions.Add(CreateDefinition(labels.Ok, MessageBoxResult.OK, true));
definitions.Add(CreateDefinition(labels.Cancel, MessageBoxResult.Cancel, false, true));
break;
case MessageBoxButton.YesNo:
definitions.Add(CreateDefinition(labels.Yes, MessageBoxResult.Yes, true));
definitions.Add(CreateDefinition(labels.No, MessageBoxResult.No));
break;
case MessageBoxButton.YesNoCancel:
definitions.Add(CreateDefinition(labels.Yes, MessageBoxResult.Yes, true));
definitions.Add(CreateDefinition(labels.No, MessageBoxResult.No));
definitions.Add(CreateDefinition(labels.Cancel, MessageBoxResult.Cancel, false, true));
break;
default:
definitions.Add(CreateDefinition(labels.Ok, MessageBoxResult.OK, true));
break;
}
return NormalizeButtonDefinitions(definitions, defaultResult);
}
private static IEnumerable<MwMessageBoxButtonDefinition> NormalizeButtonDefinitions(IEnumerable<MwMessageBoxButtonDefinition> buttons, MessageBoxResult defaultResult)
{
var definitions = (buttons ?? Enumerable.Empty<MwMessageBoxButtonDefinition>())
.Where(item => item != null)
.Select(item => new MwMessageBoxButtonDefinition
{
Text = string.IsNullOrWhiteSpace(item.Text) ? item.Result.ToString() : item.Text,
Result = item.Result,
IsCancel = item.IsCancel,
IsDefault = item.IsDefault,
IsPrimary = item.IsPrimary,
ButtonStyle = item.ButtonStyle
})
.ToList();
if (definitions.Count == 0)
{
definitions.Add(CreateDefinition(DefaultButtonText.Ok, MessageBoxResult.OK, true));
}
var targetDefault = defaultResult == MessageBoxResult.None
? definitions.FirstOrDefault(item => item.IsDefault)?.Result ?? definitions[0].Result
: defaultResult;
foreach (var item in definitions)
{
item.IsDefault = item.Result == targetDefault;
}
if (!definitions.Any(item => item.IsCancel))
{
var cancelButton = definitions.FirstOrDefault(item => item.Result == MessageBoxResult.Cancel);
if (cancelButton != null)
{
cancelButton.IsCancel = true;
}
}
if (!definitions.Any(item => item.IsPrimary))
{
var primary = definitions.FirstOrDefault(item => item.IsDefault) ?? definitions[0];
primary.IsPrimary = true;
}
foreach (var item in definitions)
{
item.ButtonStyle = item.ButtonStyle ?? GetButtonStyle(item.IsPrimary);
}
return definitions;
}
private static MwMessageBoxButtonDefinition CreateDefinition(string text, MessageBoxResult result, bool isPrimary = false, bool isCancel = false)
{
return new MwMessageBoxButtonDefinition
{
Text = text,
Result = result,
IsPrimary = isPrimary,
IsDefault = isPrimary,
IsCancel = isCancel,
ButtonStyle = GetButtonStyle(isPrimary)
};
}
internal static Style GetButtonStyle(bool isPrimary)
{
if (Application.Current != null)
{
var key = isPrimary ? "SaveButtonStyle" : "SettingsButtonStyle";
var resourceStyle = Application.Current.TryFindResource(key) as Style;
if (resourceStyle != null)
{
return resourceStyle;
}
}
var visualTheme = VisualTheme ?? MwMessageBoxVisualTheme.CreateDefault();
var buttonTheme = isPrimary ? visualTheme.PrimaryButtonTheme : visualTheme.SecondaryButtonTheme;
var style = new Style(typeof(System.Windows.Controls.Button));
style.Setters.Add(new Setter(System.Windows.Controls.Control.BackgroundProperty, CreateBrush(buttonTheme.BackgroundColor)));
style.Setters.Add(new Setter(System.Windows.Controls.Control.BorderBrushProperty, CreateBrush(buttonTheme.BorderColor)));
style.Setters.Add(new Setter(System.Windows.Controls.Control.BorderThicknessProperty, new Thickness(1)));
style.Setters.Add(new Setter(System.Windows.Controls.Control.ForegroundProperty, CreateBrush(buttonTheme.ForegroundColor)));
style.Setters.Add(new Setter(System.Windows.Controls.Control.FontSizeProperty, 14d));
style.Setters.Add(new Setter(System.Windows.Controls.Control.FontWeightProperty, FontWeights.SemiBold));
style.Setters.Add(new Setter(System.Windows.Controls.Control.CursorProperty, System.Windows.Input.Cursors.Hand));
var hoverTrigger = new Trigger { Property = UIElement.IsMouseOverProperty, Value = true };
hoverTrigger.Setters.Add(new Setter(System.Windows.Controls.Control.BackgroundProperty, CreateBrush(buttonTheme.HoverBackgroundColor)));
style.Triggers.Add(hoverTrigger);
var pressedTrigger = new Trigger { Property = System.Windows.Controls.Primitives.ButtonBase.IsPressedProperty, Value = true };
pressedTrigger.Setters.Add(new Setter(System.Windows.Controls.Control.BackgroundProperty, CreateBrush(buttonTheme.PressedBackgroundColor)));
style.Triggers.Add(pressedTrigger);
return style;
}
internal static MessageBoxResult ResolveFallbackResult(MwMessageBoxState state)
{
if (state == null)
{
return MessageBoxResult.None;
}
if (state.DefaultResult != MessageBoxResult.None)
{
return state.DefaultResult;
}
var cancelButton = state.Buttons != null ? state.Buttons.FirstOrDefault(item => item.IsCancel) : null;
if (cancelButton != null)
{
return cancelButton.Result;
}
var defaultButton = state.Buttons != null ? state.Buttons.FirstOrDefault(item => item.IsDefault) ?? state.Buttons.FirstOrDefault() : null;
return defaultButton != null ? defaultButton.Result : MessageBoxResult.None;
}
private static Window GetActiveWindow()
{
if (Application.Current == null)
{
return null;
}
var active = Application.Current.Windows.OfType<Window>().FirstOrDefault(w => w.IsActive);
if (active != null)
{
return active;
}
if (Application.Current.MainWindow != null && Application.Current.MainWindow.IsVisible)
{
return Application.Current.MainWindow;
}
return Application.Current.Windows.OfType<Window>()
.Where(w => w.IsVisible)
.OrderByDescending(w => w.IsLoaded)
.FirstOrDefault();
}
private static MwMessageBoxState CreateState(string message, string caption, MessageBoxImage icon, IReadOnlyList<MwMessageBoxButtonDefinition> buttons, MessageBoxResult defaultResult, MessageBoxOptions options)
{
var palette = CreatePaletteBrushes(GetPalette(icon));
return new MwMessageBoxState
{
Caption = ResolveCaption(caption, icon),
Message = message,
IconGlyph = GetIconText(icon, IconTheme),
IconPathData = GetIconPathData(icon),
TimeText = DateTime.Now.ToString("HH:mm:ss"),
AccentBrush = palette.AccentBrush,
IconBackgroundBrush = palette.IconBackgroundBrush,
IconForegroundBrush = palette.IconForegroundBrush,
Buttons = buttons,
DefaultResult = defaultResult,
MessageTextAlignment = (options & MessageBoxOptions.RightAlign) == MessageBoxOptions.RightAlign ? TextAlignment.Right : TextAlignment.Left,
WindowFlowDirection = (options & MessageBoxOptions.RtlReading) == MessageBoxOptions.RtlReading ? FlowDirection.RightToLeft : FlowDirection.LeftToRight,
MessageFlowDirection = (options & MessageBoxOptions.RtlReading) == MessageBoxOptions.RtlReading ? FlowDirection.RightToLeft : FlowDirection.LeftToRight
};
}
private static string ResolveCaption(string caption, MessageBoxImage icon)
{
if (!string.IsNullOrWhiteSpace(caption))
{
return caption;
}
switch (icon)
{
case MessageBoxImage.Warning:
return "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ";
case MessageBoxImage.Error:
return "<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ";
case MessageBoxImage.Information:
return "<22><>Ϣ<EFBFBD><CFA2>ʾ";
case MessageBoxImage.Question:
return "<22><><EFBFBD><EFBFBD>ȷ<EFBFBD><C8B7>";
default:
return "<22><>ʾ";
}
}
private static string GetIconText(MessageBoxImage icon, MwMessageBoxIconTheme iconTheme)
{
var theme = iconTheme ?? MwMessageBoxIconTheme.CreateDefault();
switch (icon)
{
case MessageBoxImage.None:
return theme.NoneGlyph;
case MessageBoxImage.Warning:
return theme.WarningGlyph;
case MessageBoxImage.Error:
return theme.ErrorGlyph;
case MessageBoxImage.Information:
return theme.InformationGlyph;
case MessageBoxImage.Question:
return theme.QuestionGlyph;
default:
return theme.NoneGlyph;
}
}
private static string GetIconPathData(MessageBoxImage icon)
{
switch (icon)
{
case MessageBoxImage.Warning:
return "M11,4 H13 V14 H11 Z M11,16 H13 V18 H11 Z";
case MessageBoxImage.Error:
return "M7.76,6.34 L12,10.59 L16.24,6.34 L17.66,7.76 L13.41,12 L17.66,16.24 L16.24,17.66 L12,13.41 L7.76,17.66 L6.34,16.24 L10.59,12 L6.34,7.76 Z";
case MessageBoxImage.Information:
return "M11,5 H13 V7 H11 Z M11,9 H13 V19 H11 Z";
case MessageBoxImage.Question:
return "M10.2,8.8 C10.27,7.69 11.17,6.82 12.3,6.82 C13.46,6.82 14.36,7.66 14.36,8.75 C14.36,9.52 13.96,10.12 13.02,10.82 C11.74,11.78 11.1,12.65 11.1,14.35 L12.9,14.35 C12.9,13.26 13.27,12.74 14.1,12.09 C15.29,11.17 16,10.14 16,8.72 C16,6.69 14.37,5 12.3,5 C10.23,5 8.56,6.63 8.4,8.8 Z M11.05,16.2 H13.15 V18.3 H11.05 Z";
default:
return "M8,11 H16 V13 H8 Z";
}
}
private static MwMessageBoxColorPalette GetPalette(MessageBoxImage icon)
{
var visualTheme = VisualTheme ?? MwMessageBoxVisualTheme.CreateDefault();
switch (icon)
{
case MessageBoxImage.Warning:
return CreatePalette(visualTheme.WarningPalette, visualTheme.NonePalette);
case MessageBoxImage.Error:
return CreatePalette(visualTheme.ErrorPalette, visualTheme.NonePalette);
case MessageBoxImage.Information:
return CreatePalette(visualTheme.InformationPalette, visualTheme.NonePalette);
case MessageBoxImage.Question:
return CreatePalette(visualTheme.QuestionPalette, visualTheme.NonePalette);
default:
return CreatePalette(visualTheme.NonePalette, MwMessageBoxVisualTheme.CreateDefault().NonePalette);
}
}
private static MwMessageBoxPaletteBrushes CreatePaletteBrushes(MwMessageBoxColorPalette palette)
{
return new MwMessageBoxPaletteBrushes(palette);
}
private static MwMessageBoxColorPalette CreatePalette(MwMessageBoxColorPalette palette, MwMessageBoxColorPalette fallbackPalette)
{
var resolvedFallback = fallbackPalette ?? MwMessageBoxVisualTheme.CreateDefault().NonePalette;
var resolvedPalette = palette ?? resolvedFallback;
return new MwMessageBoxColorPalette(
resolvedPalette.AccentColor ?? resolvedFallback.AccentColor,
resolvedPalette.IconBackgroundColor ?? resolvedFallback.IconBackgroundColor,
resolvedPalette.IconForegroundColor ?? resolvedFallback.IconForegroundColor);
}
private static SolidColorBrush CreateBrush(string color)
{
return (SolidColorBrush)new BrushConverter().ConvertFromString(color);
}
private sealed class MwMessageBoxPaletteBrushes
{
public MwMessageBoxPaletteBrushes(MwMessageBoxColorPalette palette)
{
var resolvedPalette = palette ?? MwMessageBoxVisualTheme.CreateDefault().NonePalette;
AccentBrush = CreateBrush(resolvedPalette.AccentColor ?? "#9CA3AF");
IconBackgroundBrush = CreateBrush(resolvedPalette.IconBackgroundColor ?? "#F3F4F6");
IconForegroundBrush = CreateBrush(resolvedPalette.IconForegroundColor ?? "#6B7280");
}
public Brush AccentBrush { get; }
public Brush IconBackgroundBrush { get; }
public Brush IconForegroundBrush { get; }
private static SolidColorBrush CreateBrush(string color)
{
return (SolidColorBrush)new BrushConverter().ConvertFromString(color);
}
}
}
}

View File

@@ -0,0 +1,296 @@
<Window x:Class="MainShell.Common.View.MwMessageBoxWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:common="clr-namespace:MainShell.Common"
xmlns:behaviors="clr-namespace:MainShell.Common.Behaviors"
mc:Ignorable="d"
MinWidth="360"
MinHeight="220"
MaxWidth="720"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
AllowsTransparency="True"
WindowStyle="None"
Background="Transparent"
ShowInTaskbar="False"
Title="{Binding State.Caption}"
FlowDirection="{Binding State.WindowFlowDirection}"
UseLayoutRounding="True"
SnapsToDevicePixels="True"
common:WindowService.DialogResult="{Binding DialogResult}">
<Window.Resources>
<Style x:Key="MwMessageBoxButtonStyle" TargetType="Button">
<Setter Property="Foreground" Value="#374151"/>
<Setter Property="Background" Value="#FFFFFF"/>
<Setter Property="BorderBrush" Value="#C9D5E5"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="3">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding DataContext.IsPrimary, RelativeSource={RelativeSource TemplatedParent}}" Value="True">
<Setter Property="Background" Value="#2E5FA7"/>
<Setter Property="BorderBrush" Value="#2E5FA7"/>
<Setter Property="Foreground" Value="#FFFFFF"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding DataContext.IsPrimary, RelativeSource={RelativeSource TemplatedParent}}" Value="False"/>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource TemplatedParent}}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="#F5F8FC"/>
<Setter Property="BorderBrush" Value="#AFC3DD"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding DataContext.IsPrimary, RelativeSource={RelativeSource TemplatedParent}}" Value="False"/>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource TemplatedParent}}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="#E8EEF6"/>
<Setter Property="BorderBrush" Value="#9FB7D6"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding DataContext.IsPrimary, RelativeSource={RelativeSource TemplatedParent}}" Value="True"/>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource TemplatedParent}}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="#254F8D"/>
<Setter Property="BorderBrush" Value="#254F8D"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding DataContext.IsPrimary, RelativeSource={RelativeSource TemplatedParent}}" Value="True"/>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource TemplatedParent}}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="#1E4174"/>
<Setter Property="BorderBrush" Value="#1E4174"/>
</MultiDataTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="#9CA3AF"/>
<Setter Property="Background" Value="#F3F4F6"/>
<Setter Property="BorderBrush" Value="#E5E7EB"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MwMessageBoxCaptionStyle" TargetType="TextBlock">
<Setter Property="Text" Value="{Binding State.Caption}"/>
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="MwMessageBoxCloseButtonStyle" TargetType="Button">
<Setter Property="Width" Value="44"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Foreground" Value="#FFFFFF"/>
<Setter Property="Background" Value="#2E5FA7"/>
<Setter Property="BorderBrush" Value="#2E5FA7"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#C94A4A"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="#A53B3B"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<i:InvokeCommandAction Command="{Binding WindowClosingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Border Background="#FFFFFF"
CornerRadius="2"
BorderBrush="#C6D1DF"
BorderThickness="1">
<Border.Effect>
<DropShadowEffect Color="#1F3A5A"
BlurRadius="18"
ShadowDepth="2"
Opacity="0.18"/>
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#2E5FA7">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0"
behaviors:WindowDragMoveBehavior.IsEnabled="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Width="16"
Height="16"
Margin="10,0,8,0"
VerticalAlignment="Center"
BorderBrush="#D8E6F7"
BorderThickness="1"
CornerRadius="8">
<TextBlock Text="i"
Foreground="#FFFFFF"
FontSize="11"
FontWeight="Bold"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Foreground="#FFFFFF"
FontSize="13"
FontWeight="Normal"
TextTrimming="CharacterEllipsis">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding State.Caption}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding State.Caption}" Value="{x:Null}">
<Setter Property="Text" Value="{DynamicResource Msg_Title_Info}"/>
</DataTrigger>
<DataTrigger Binding="{Binding State.Caption}" Value="">
<Setter Property="Text" Value="{DynamicResource Msg_Title_Info}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
<Button Grid.Column="1"
x:Name="CloseButton"
Style="{StaticResource MwMessageBoxCloseButtonStyle}"
Click="OnCloseButtonClick"/>
</Grid>
<Grid Grid.Row="1" Margin="28,28,28,22">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Width="62"
Height="62"
HorizontalAlignment="Center"
Background="#FFFFFF"
BorderBrush="{Binding State.AccentBrush}"
BorderThickness="1.5"
CornerRadius="31">
<Viewbox Width="26"
Height="26"
Stretch="Uniform">
<Path Data="{Binding State.IconPathData}"
Fill="{Binding State.IconForegroundBrush}"
Stretch="Uniform"/>
</Viewbox>
</Border>
<TextBlock Grid.Row="1"
Margin="0,18,0,0"
HorizontalAlignment="Center"
Foreground="#1F2937"
FontSize="17"
FontWeight="SemiBold"
TextAlignment="Center"
Style="{StaticResource MwMessageBoxCaptionStyle}"/>
<TextBlock Grid.Row="2"
Margin="0,14,0,0"
MaxWidth="540"
Text="{Binding State.Message}"
TextWrapping="Wrap"
Foreground="#4B5563"
FontSize="14"
LineHeight="23"
TextAlignment="Center"
FlowDirection="{Binding State.MessageFlowDirection}"/>
</Grid>
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="1"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Background="#E7EEF7"/>
<ItemsControl Grid.Row="1"
Margin="0,18,0,20"
ItemsSource="{Binding State.Buttons}"
HorizontalAlignment="Center"
MaxWidth="560">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Text}"
MinWidth="96"
Height="38"
Margin="8,0,8,0"
Padding="22,0,22,0"
IsDefault="{Binding IsDefault}"
IsCancel="{Binding IsCancel}"
Style="{Binding ButtonStyle, TargetNullValue={StaticResource MwMessageBoxButtonStyle}}"
Command="{Binding DataContext.SelectButtonCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Grid>
</Border>
</Window>

View File

@@ -0,0 +1,20 @@
using System.Windows;
using MainShell.Common.ViewModel;
namespace MainShell.Common.View
{
public partial class MwMessageBoxWindow : Window
{
public MwMessageBoxWindow(MwMessageBoxWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel ?? throw new System.ArgumentNullException(nameof(viewModel));
CloseButton.Content = "\u00D7";
}
private void OnCloseButtonClick(object sender, System.Windows.RoutedEventArgs e)
{
this.Close();
}
}
}

View File

@@ -0,0 +1,61 @@
using System;
using System.Windows;
using System.Windows.Input;
using MaxwellFramework.Core.Common.Command;
using Stylet;
namespace MainShell.Common.ViewModel
{
public class MwMessageBoxWindowViewModel : PropertyChangedBase
{
private bool? _dialogResult;
private MessageBoxResult _result;
public MwMessageBoxWindowViewModel(MwMessageBoxState state)
{
State = state ?? throw new ArgumentNullException(nameof(state));
SelectButtonCommand = new DelegateCommand(OnSelectButton);
WindowClosingCommand = new DelegateCommand(OnWindowClosing);
}
public MwMessageBoxState State { get; }
public bool? DialogResult
{
get => _dialogResult;
set => SetAndNotify(ref _dialogResult, value);
}
public MessageBoxResult Result
{
get => _result;
private set => SetAndNotify(ref _result, value);
}
public ICommand SelectButtonCommand { get; }
public ICommand WindowClosingCommand { get; }
private void OnSelectButton(object parameter)
{
var definition = parameter as MwMessageBoxButtonDefinition;
if (definition == null)
{
return;
}
Result = definition.Result;
DialogResult = true;
}
private void OnWindowClosing(object parameter)
{
if (Result != MessageBoxResult.None)
{
return;
}
Result = MwMessageBox.ResolveFallbackResult(State);
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;
namespace MainShell.Common
{
[MarkupExtensionReturnType(typeof(string))]
public class PropDescExtension : MarkupExtension
{
public Type ForType { get; set; }
public string Property { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (ForType == null || string.IsNullOrWhiteSpace(Property)) return Property;
var pd = TypeDescriptor.GetProperties(ForType)[Property];
if (pd == null) return Property;
var desc = pd.Attributes.OfType<DescriptionAttribute>().FirstOrDefault();
return string.IsNullOrWhiteSpace(desc?.Description) ? Property : desc.Description;
}
}
}

View File

@@ -0,0 +1,232 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MainShell.Common
{
public class RetryMechanism
{
// 自定义异常:操作失败
public class RetryException : Exception
{
public RetryException(string message) : base(message) { }
}
public class RetryResult
{
public bool Succeeded { get; set; }
public bool IsAborted { get; set; }
public int AttemptCount { get; set; }
public int MaxAttempts { get; set; }
public string Message { get; set; }
}
/// <summary>
/// 向后兼容旧接口:默认直接继续重试直到达到最大次数,失败时抛异常。
/// 新代码建议优先使用 <see cref="RetryOrThrow(Func{bool}, Func{int, bool}, string, int)"/> 或返回结果对象的重载。
/// </summary>
public static void RetryIfNeeded(Func<bool> checkCondition, string message, int maxAttempts = 3)
{
RetryOrThrow(checkCondition, attempt => true, message, maxAttempts);
}
/// <summary>
/// 执行同步重试并返回结果对象。
/// </summary>
/// <param name="checkCondition">
/// 每次尝试时执行的检查逻辑。
/// 返回 <c>true</c> 表示成功,返回 <c>false</c> 表示本次失败并根据 <paramref name="shouldRetry"/> 决定是否继续。
/// 如果该委托抛出异常,则异常会直接向外传播,不会被当前重试机制捕获或吞掉,也不会自动进入下一次重试。
/// </param>
/// <param name="shouldRetry">当一次尝试失败后,决定是否继续重试。参数为当前已完成的尝试次数,从 1 开始计数。</param>
/// <param name="message">操作描述信息。</param>
/// <param name="maxAttempts">最大尝试次数,必须大于 0。</param>
public static RetryResult RetryIfNeeded(Func<bool> checkCondition, Func<int, bool> shouldRetry, string message, int maxAttempts = 3)
{
if (checkCondition == null)
{
throw new ArgumentNullException(nameof(checkCondition));
}
if (shouldRetry == null)
{
throw new ArgumentNullException(nameof(shouldRetry));
}
if (maxAttempts <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxAttempts), "maxAttempts must be greater than 0.");
}
for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
bool conditionMet = checkCondition();
if (conditionMet)
{
return new RetryResult
{
Succeeded = true,
AttemptCount = attempt,
MaxAttempts = maxAttempts,
Message = message
};
}
if (attempt == maxAttempts)
{
return new RetryResult
{
Succeeded = false,
AttemptCount = attempt,
MaxAttempts = maxAttempts,
Message = message
};
}
bool continueRetry = shouldRetry(attempt);
if (!continueRetry)
{
return new RetryResult
{
Succeeded = false,
IsAborted = true,
AttemptCount = attempt,
MaxAttempts = maxAttempts,
Message = message
};
}
}
return new RetryResult
{
Succeeded = false,
AttemptCount = maxAttempts,
MaxAttempts = maxAttempts,
Message = message
};
}
// 保留抛异常风格的兼容方法
public static void RetryOrThrow(Func<bool> checkCondition, Func<int, bool> shouldRetry, string message, int maxAttempts = 3)
{
var result = RetryIfNeeded(checkCondition, shouldRetry, message, maxAttempts);
EnsureSuccess(result);
}
/// <summary>
/// 向后兼容旧接口的异步版本:默认直接继续重试直到达到最大次数,失败时抛异常。
/// 新代码建议优先使用 <see cref="RetryOrThrowAsync(Func{Task{bool}}, Func{int, Task{bool}}, string, int, CancellationToken)"/> 或返回结果对象的重载。
/// </summary>
public static async Task RetryIfNeededAsync(Func<Task<bool>> checkConditionAsync, string message, int maxAttempts = 3)
{
await RetryOrThrowAsync(checkConditionAsync, attempt => Task.FromResult(true), message, maxAttempts, CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
/// 执行异步重试并返回结果对象。
/// </summary>
/// <param name="checkConditionAsync">
/// 每次尝试时执行的异步检查逻辑。
/// 返回 <c>true</c> 表示成功,返回 <c>false</c> 表示本次失败并根据 <paramref name="shouldRetryAsync"/> 决定是否继续。
/// 如果该委托抛出异常,则异常会直接向外传播,不会被当前重试机制捕获或吞掉,也不会自动进入下一次重试。
/// </param>
/// <param name="shouldRetryAsync">当一次尝试失败后,决定是否继续重试。参数为当前已完成的尝试次数,从 1 开始计数。</param>
/// <param name="message">操作描述信息。</param>
/// <param name="maxAttempts">最大尝试次数,必须大于 0。</param>
/// <param name="cancellationToken">用于取消异步重试流程。</param>
public static async Task<RetryResult> RetryIfNeededAsync(Func<Task<bool>> checkConditionAsync, Func<int, Task<bool>> shouldRetryAsync, string message, int maxAttempts = 3, CancellationToken cancellationToken = default(CancellationToken))
{
if (checkConditionAsync == null)
{
throw new ArgumentNullException(nameof(checkConditionAsync));
}
if (shouldRetryAsync == null)
{
throw new ArgumentNullException(nameof(shouldRetryAsync));
}
if (maxAttempts <= 0)
{
throw new ArgumentOutOfRangeException(nameof(maxAttempts), "maxAttempts must be greater than 0.");
}
for (int attempt = 1; attempt <= maxAttempts; attempt++)
{
cancellationToken.ThrowIfCancellationRequested();
bool conditionMet = await checkConditionAsync().ConfigureAwait(false);
if (conditionMet)
{
return new RetryResult
{
Succeeded = true,
AttemptCount = attempt,
MaxAttempts = maxAttempts,
Message = message
};
}
if (attempt == maxAttempts)
{
return new RetryResult
{
Succeeded = false,
AttemptCount = attempt,
MaxAttempts = maxAttempts,
Message = message
};
}
cancellationToken.ThrowIfCancellationRequested();
bool continueRetry = await shouldRetryAsync(attempt).ConfigureAwait(false);
if (!continueRetry)
{
return new RetryResult
{
Succeeded = false,
IsAborted = true,
AttemptCount = attempt,
MaxAttempts = maxAttempts,
Message = message
};
}
}
return new RetryResult
{
Succeeded = false,
AttemptCount = maxAttempts,
MaxAttempts = maxAttempts,
Message = message
};
}
// 保留抛异常风格的异步兼容方法
public static async Task RetryOrThrowAsync(Func<Task<bool>> checkConditionAsync, Func<int, Task<bool>> shouldRetryAsync, string message, int maxAttempts = 3, CancellationToken cancellationToken = default(CancellationToken))
{
var result = await RetryIfNeededAsync(checkConditionAsync, shouldRetryAsync, message, maxAttempts, cancellationToken).ConfigureAwait(false);
EnsureSuccess(result);
}
private static void EnsureSuccess(RetryResult result)
{
if (result.Succeeded)
{
return;
}
if (result.IsAborted)
{
throw new RetryException($"{result.Message} 已中止重试,操作失败。尝试次数: {result.AttemptCount}/{result.MaxAttempts}");
}
throw new RetryException($"{result.Message} 已达到最大重试次数,操作失败。尝试次数: {result.AttemptCount}/{result.MaxAttempts}");
}
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Diagnostics;
using System.Threading;
namespace MainShell.Common
{
class TimeoutRetryMechanism
{
public class WaitForConditionResult
{
public bool Succeeded { get; set; }
public bool TimedOut { get; set; }
public bool IsAborted { get; set; }
}
/// <summary>
/// 在指定超时时间内轮询等待条件成立。
/// 当 <paramref name="timeout"/> 为 <c>null</c> 时,表示无限等待,直到条件成立或外部取消。
/// </summary>
/// <param name="condition">待检查的条件,返回 <c>true</c> 表示条件成立。</param>
/// <param name="timeout">最大等待时间。传入 <c>null</c> 表示无限等待;传入具体值时必须大于 0。</param>
/// <param name="pollingInterval">轮询间隔,必须大于 0。</param>
/// <param name="cancellationToken">用于中止等待流程。</param>
public static WaitForConditionResult WaitForConditionWithTimeout(Func<bool> condition, TimeSpan? timeout, TimeSpan pollingInterval, CancellationToken cancellationToken)
{
if (condition == null)
{
throw new ArgumentNullException(nameof(condition));
}
if (timeout.HasValue && timeout.Value <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(timeout), "timeout must be greater than zero when specified.");
}
if (pollingInterval <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(pollingInterval), "pollingInterval must be greater than zero.");
}
var stopwatch = Stopwatch.StartNew();
while (true)
{
if (condition())
{
return new WaitForConditionResult
{
Succeeded = true
};
}
if (timeout.HasValue && stopwatch.Elapsed > timeout.Value)
{
return new WaitForConditionResult
{
Succeeded = false,
TimedOut = true
};
}
if (cancellationToken.IsCancellationRequested)
{
return new WaitForConditionResult
{
Succeeded = false,
IsAborted = true
};
}
Thread.Sleep(pollingInterval);
}
}
}
}

View File

@@ -0,0 +1,63 @@
<Window x:Class="MainShell.Common.View.CameraSettingsView"
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.Common.View"
xmlns:custom="clr-namespace:MainShell.Resources.CustomControl" xmlns:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
Title="相机设置"
Width="450" SizeToContent="Height"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
WindowStyle="ToolWindow"
d:DesignHeight="300" d:DesignWidth="350">
<StackPanel Margin="5">
<!-- 相机参数设置 -->
<GroupBox Header="{DynamicResource CameraPar}" Margin="0,0,0,5" Style="{StaticResource GroupBoxSecondary}">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 曝光时间 -->
<TextBlock Grid.Row="0" Grid.Column="0" Text="曝光时间:" VerticalAlignment="Center" Style="{StaticResource LabelStyle}"/>
<mw:IntNumberBox Grid.Row="0" Grid.Column="1"
Value="{Binding CameraConfig.ExposureTime}"
Minimum="10" Maximum="1000000"
mw:NumericKeypadAttach.IsEnabled="True"
HorizontalContentAlignment="Right" Margin="5,2,0,2"/>
<!-- 增益 -->
<TextBlock Grid.Row="1" Grid.Column="0" Text="增益:" VerticalAlignment="Center" Style="{StaticResource LabelStyle}"/>
<mw:IntNumberBox Grid.Row="1" Grid.Column="1"
Value="{Binding CameraConfig.Gain}"
Minimum="1" Maximum="100"
mw:NumericKeypadAttach.IsEnabled="True"
HorizontalContentAlignment="Right" Margin="5,2,0,2"/>
</Grid>
</GroupBox>
<!-- 光源参数设置 -->
<!-- 使用 ItemsControl 动态生成 LightControl,需要 ViewModel 提供光源通道列表 -->
<GroupBox Header="{DynamicResource LightSetting}" Style="{StaticResource GroupBoxSecondary}">
<ItemsControl ItemsSource="{Binding LightChannels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<custom:LightControl Header="{Binding Name}"
Value="{Binding Value, Mode=TwoWay}"
Min="0" Max="255" Margin="0,2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button Content="{DynamicResource GetParameter}" Style="{StaticResource TeachButtonStyle}" Margin="5" IsDefault="True" Command="{Binding ReadFromHardwareCommand}"/>
</StackPanel>
</StackPanel>
</Window>

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.Common.View
{
/// <summary>
/// CameraSettingsControl.xaml 的交互逻辑
/// </summary>
public partial class CameraSettingsView : Window
{
public CameraSettingsView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,254 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Models;
using MwFramework.Controls.ControlCanvas;
using Stylet;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Reflection;
using System.Windows.Input;
namespace MainShell.Common.ViewModel
{
/// <summary>
/// 单个光源通道的包装类
/// </summary>
public class LightChannelViewModel : PropertyChangedBase
{
private readonly object _sourceConfig;
private readonly PropertyInfo _propertyInfo;
public string Name { get; set; }
public int Value
{
get => (int)_propertyInfo.GetValue(_sourceConfig);
set
{
if (value != Value)
{
_propertyInfo.SetValue(_sourceConfig, value);
NotifyOfPropertyChange();
}
}
}
public LightChannelViewModel(object sourceConfig, PropertyInfo propertyInfo, string displayName)
{
_sourceConfig = sourceConfig;
_propertyInfo = propertyInfo;
Name = displayName;
}
}
/// <summary>
/// 相机及光源参数设置弹窗 ViewModel
/// </summary>
public class CameraSettingsViewModel : Screen
{
private readonly IWindowManager _windowManager;
private readonly HardwareManager _hardwareManager;
// 核心标志位:是否正在从硬件同步数据
private bool _isSyncingFromHardware;
private CameraType _currentCameraType;
private object _currentLightConfig;
private CameraConfig _cameraConfig;
public CameraConfig CameraConfig
{
get => _cameraConfig;
set => SetAndNotify(ref _cameraConfig, value);
}
private ObservableCollection<LightChannelViewModel> _lightChannels;
public ObservableCollection<LightChannelViewModel> LightChannels
{
get => _lightChannels;
set => SetAndNotify(ref _lightChannels, value);
}
public ICommand ReadFromHardwareCommand { get; }
public ICommand SaveAndCloseCommand { get; }
public CameraSettingsViewModel(IWindowManager windowManager, HardwareManager hardwareManager)
{
_windowManager = windowManager;
_hardwareManager = hardwareManager;
ReadFromHardwareCommand = new DelegateCommand(ReadFromHardware);
SaveAndCloseCommand = new DelegateCommand(SaveAndClose);
DisplayName = "相机光源参数设置";
}
/// <summary>
/// 初始化并绑定数据
/// </summary>
public void Initialize(CameraConfig cameraConfig, object lightConfig, CameraType cameraType)
{
// 1. 解绑旧对象的事件(如果有)
if (CameraConfig != null) CameraConfig.PropertyChanged -= OnConfigPropertyChanged;
if (_currentLightConfig is INotifyPropertyChanged oldLight) oldLight.PropertyChanged -= OnConfigPropertyChanged;
_currentCameraType = cameraType;
CameraConfig = cameraConfig;
_currentLightConfig = lightConfig;
// 2. 重新订阅事件
if (CameraConfig != null) CameraConfig.PropertyChanged += OnConfigPropertyChanged;
if (_currentLightConfig is INotifyPropertyChanged newLight) newLight.PropertyChanged += OnConfigPropertyChanged;
// 3. 生成光源通道列表
LightChannels = new ObservableCollection<LightChannelViewModel>();
if (lightConfig != null)
{
var properties = lightConfig.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in properties)
{
if (prop.PropertyType == typeof(int) && prop.CanRead && prop.CanWrite)
{
var descAttr = prop.GetCustomAttribute<DescriptionAttribute>();
// 必须有 Description 才认为是光源通道
if (descAttr != null)
{
var vm = new LightChannelViewModel(lightConfig, prop, descAttr.Description);
LightChannels.Add(vm);
}
}
}
}
}
/// <summary>
/// 统一处理属性变更事件 => 设置硬件
/// </summary>
private void OnConfigPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// 关键逻辑:如果是从硬件读取过程中,则忽略,不要反向设置回去
if (_isSyncingFromHardware) return;
ApplySettingToHardware(sender, e.PropertyName);
}
private void ApplySettingToHardware(object source, string propertyName)
{
// 1. 设置相机参数
if (source == CameraConfig)
{
// TODO: 替换为实际的 set 相机参数代码
/*
var camDevice = _hardwareManager.GetCamera(_currentCameraType);
if (propertyName == nameof(CameraConfig.ExposureTime))
{
camDevice.SetExposure(CameraConfig.ExposureTime);
}
else if (propertyName == nameof(CameraConfig.Gain))
{
camDevice.SetGain(CameraConfig.Gain);
}
*/
System.Diagnostics.Debug.WriteLine($"[Hardware Set] Cam: {_currentCameraType}, Prop: {propertyName}");
return;
}
// 2. 设置光源参数 (根据类型直接判断)
if (source is MapCamLightConfig mapConfig)
{
if (propertyName == nameof(MapCamLightConfig.RedLight))
{
// TODO: _hardwareManager.SetMapRedLight(mapConfig.RedLight);
System.Diagnostics.Debug.WriteLine($"[Hardware Set] MapCam Red: {mapConfig.RedLight}");
}
else if (propertyName == nameof(MapCamLightConfig.BlueLight))
{
// TODO: _hardwareManager.SetMapBlueLight(mapConfig.BlueLight);
System.Diagnostics.Debug.WriteLine($"[Hardware Set] MapCam Blue: {mapConfig.BlueLight}");
}
}
else if (source is UpCamLightConfig upConfig)
{
if (propertyName == nameof(UpCamLightConfig.PointRedLight))
{
// TODO: _hardwareManager.SetUpPointRed(upConfig.PointRedLight);
System.Diagnostics.Debug.WriteLine($"[Hardware Set] UpCam PointRed: {upConfig.PointRedLight}");
}
else if (propertyName == nameof(UpCamLightConfig.RingRedLight))
{
// TODO: ...
}
// ... 处理其他 UpCam 通道
}
else if (source is DownCamLightConfig downConfig)
{
if (propertyName == nameof(DownCamLightConfig.PointRedLight))
{
// TODO: ...
}
// ... 处理其他 DownCam 通道
}
}
/// <summary>
/// 从硬件读取当前值 => 更新 UI (不触发设置)
/// </summary>
private void ReadFromHardware()
{
try
{
_isSyncingFromHardware = true; // 开启静默模式
// 1. 读取相机实际参数
/*
var camDevice = _hardwareManager.GetCamera(_currentCameraType);
if (camDevice != null) {
CameraConfig.ExposureTime = (int)camDevice.CurrentExposure;
CameraConfig.Gain = (int)camDevice.CurrentGain;
}
*/
// 2. 读取光源实际参数
if (_currentLightConfig is MapCamLightConfig mapConfig)
{
// TODO: 读取实际硬件值
mapConfig.RedLight = _hardwareManager.GetMapRedLightIntensity();
mapConfig.BlueLight = _hardwareManager.GetMapBlueLightIntensity();
}
else if (_currentLightConfig is UpCamLightConfig upConfig)
{
upConfig.PointRedLight = _hardwareManager.GetUpPointRedLightIntensity();
upConfig.PointBlueLight=_hardwareManager.GetUpPointBlueLightIntensity();
upConfig.RingRedLight=_hardwareManager.GetUpRingRedLightIntensity();
upConfig.RingBlueLight=_hardwareManager.GetUpRingBlueLightIntensity();
upConfig.RedBackLight=_hardwareManager.GetUpRedBackLightIntensity();
// ...
}
else if (_currentLightConfig is DownCamLightConfig downConfig)
{
// downConfig.PointRedLight = ...
downConfig.PointRedLight = _hardwareManager.GetDownPointRedLightIntensity();
downConfig.PointBlueLight = _hardwareManager.GetDownPointBlueLightIntensity();
downConfig.RingRedLight = _hardwareManager.GetDownRingRedLightIntensity();
downConfig.RingBlueLight = _hardwareManager.GetDownRingBlueLightIntensity();
// ...
}
}
finally
{
foreach (var channel in LightChannels)
{
// 强制刷新 UI
channel.Refresh();
}
_isSyncingFromHardware = false; // 必须复位
}
}
private void SaveAndClose()
{
RequestClose(true);
}
}
}

View File

@@ -0,0 +1,28 @@
<UserControl x:Class="MainShell.Common.VisionTemple.View.VisionTempleView"
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.Common.VisionTemple.View" xmlns:control="http://www.maxwell.com/controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<control:CameraVision DataContext="{Binding Service}" Name="cameraVision">
<control:CameraVision.CameraHost>
<control:CameraAxisControlA
Items="{Binding CameraModel.CameraShowViewModel.Items}"
Source="{Binding CameraModel.CameraShowViewModel.CurrentImage, Mode=OneWay}"
TabSelectedIndex="{Binding SelectCameraIndex}"
Cameras="{Binding Cameras}"
Device="{Binding SelectItem}"
ImportFile="{Binding CameraModel.ImportFile}"
Regions="{Binding CameraModel.CameraShowViewModel.Regions}"
IsClear="{Binding CameraModel.CameraShowViewModel.IsClear}"
IsShowContext="True"
ShapeThickness="1" IsShowSolidLine="True" DrawInConcurrency = "False"/>
</control:CameraVision.CameraHost>
</control:CameraVision>
</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.Common.VisionTemple.View
{
/// <summary>
/// VisionTempleView.xaml 的交互逻辑
/// </summary>
public partial class VisionTempleView : UserControl
{
public VisionTempleView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,16 @@
<Window x:Class="MainShell.Common.VisionTemple.View.VisionTempleWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:view="clr-namespace:MainShell.Common.VisionTemple.View"
mc:Ignorable="d"
Title="{Binding DisplayName}"
Height="875"
Width="1000"
WindowStartupLocation="CenterOwner"
Background="White">
<Grid Background="White">
<view:VisionTempleView DataContext="{Binding VisionTemple}" />
</Grid>
</Window>

View File

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

View File

@@ -0,0 +1,32 @@
using MwFramework.Controls.PatternTemplate.ViewModel;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Common.VisionTemple.ViewModel
{
public class VisionTempleViewModel : Screen
{
public string Name { get; set; } = "VisionTempleModel";
private MatchTemplateService _service;
public MatchTemplateService Service
{
get { return _service; }
set
{
_service = value;
OnPropertyChanged(nameof(Service));
}
}
public VisionTempleViewModel()
{
_service = new MatchTemplateService();
}
}
}

View File

@@ -0,0 +1,114 @@
using MainShell.Common.VisionTemple.View;
using MainShell.Hardware;
using Stylet;
using System.ComponentModel;
using System.Windows;
namespace MainShell.Common.VisionTemple.ViewModel
{
public class VisionTempleWindowViewModel : Screen
{
private readonly VisionTempleViewModel _visionTemple;
private VisionTempleWindow _window;
private bool _isShuttingDown;
private readonly HardwareManager _hardwareManager;
public VisionTempleWindowViewModel(HardwareManager hardwareManager)
{
DisplayName = "视觉模板制作";
_visionTemple = new VisionTempleViewModel();
_visionTemple.Service = new MwFramework.Controls.PatternTemplate.ViewModel.MatchTemplateService();
_visionTemple.Service.CameraModel.IsShowFoldDialog = false;
_hardwareManager = hardwareManager;
}
private string _templatePath;
/// <summary>
/// 模版路径
/// </summary>
public string TemplatePath
{
get { return _templatePath; }
set
{
_templatePath = value;
_visionTemple.Service.CameraModel.TemplatePath = value;
}
}
public VisionTempleViewModel VisionTemple
{
get
{
return _visionTemple;
}
}
public void SetCamera(CameraType cameraType)
{
switch (cameraType)
{
case CameraType.MapCamera:
_visionTemple.Service.Cameras = _hardwareManager.CameraAxisManager.BottomCameraAxisDevices;
break;
case CameraType.TopPositionCamera:
_visionTemple.Service.Cameras = _hardwareManager.CameraAxisManager.TopCameraAxisDevices;
break;
case CameraType.TopWideCamera:
_visionTemple.Service.Cameras = _hardwareManager.CameraAxisManager.WideCameraAxisDevices;
break;
default:
break;
}
if(_visionTemple.Service.Cameras!=null&& _visionTemple.Service.Cameras.Count>0)
{
_visionTemple.Service.SelectItem = _visionTemple.Service.Cameras[0];
}
}
public void ShowWindow()
{
if (_window == null)
{
_window = new VisionTempleWindow();
_window.DataContext = this;
_window.Closing += OnWindowClosing;
}
Window owner = Application.Current != null ? Application.Current.MainWindow : null;
if (owner != null && _window.Owner == null)
{
_window.Owner = owner;
owner.Closing += OnOwnerClosing;
}
if (_window.IsVisible)
{
if (_window.WindowState == WindowState.Minimized)
{
_window.WindowState = WindowState.Normal;
}
_window.Activate();
return;
}
_window.Show();
_window.Activate();
}
private void OnWindowClosing(object sender, CancelEventArgs e)
{
if (!_isShuttingDown)
{
e.Cancel = true;
_window.Hide();
}
}
private void OnOwnerClosing(object sender, CancelEventArgs e)
{
_isShuttingDown = true;
}
}
}

View File

@@ -0,0 +1,40 @@
using System.Windows;
namespace MainShell.Common
{
public class WindowService
{
// 定义一个名为 DialogResult 的附加属性
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(WindowService),
new PropertyMetadata(DialogResultChanged)); // 当此属性值改变时,调用 DialogResultChanged 方法
// 提供 Set 方法供外部设置附加属性的值
public static void SetDialogResult(UIElement element, bool? value)
{
element.SetValue(DialogResultProperty, value);
}
// 提供 Get 方法供外部获取附加属性的值
public static bool? GetDialogResult(UIElement element)
{
return (bool?)element.GetValue(DialogResultProperty);
}
// 当 DialogResult 附加属性的值改变时调用的回调方法
private static void DialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// 确保附加属性应用在一个 Window 控件上,并且新的值是 bool 类型
if (d is Window window && e.NewValue is bool result)
{
// 设置窗口的 DialogResult
window.DialogResult = result;
// 关闭窗口
window.Close();
}
}
}
}

View File

@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Common
{
public static class WorkflowContextKeys
{
/// <summary>
/// 用于传递 IEventAggregator 实例
/// </summary>
public const string EventAggregator = "EventAggregator";
/// <summary>
/// 用于传递请求令牌 (Guid)
/// </summary>
public const string RequestToken = "RequestToken";
/// <summary>
/// 当前配方
/// </summary>
public const string RecipeManager = "RecipeManager";
/// <summary>
/// 流程结果管理器 Key
/// </summary>
public const string ProcessResultManager = "ProcessResultManager";
/// <summary>
/// 恢复目标活动名称
/// </summary>
public const string ResumeTargetActivityName="ResumeTargetActivityName";
/// <summary>
/// 流程名称
/// </summary>
public const string WorkflowName = "WorkflowName";
/// <summary>
/// 当前步骤ID
/// </summary>
public const string CurrentStepId = "CurrentStepId";
/// <summary>
/// 下一步骤ID
/// </summary>
public const string NextStepId = "NextStepId";
/// <summary>
/// 流程路由配置
/// </summary>
public const string WorkflowRoutes = "WorkflowRoutes";
/// <summary>
/// 晶片转移路由
/// </summary>
public const string DieTransferRoute = "DieTransferRoute";
/// <summary>
/// 自动生产路由
/// </summary>
public const string AutoProductionRoute = "AutoProductionRoute";
/// <summary>
/// 流程运行时追踪器
/// </summary>
public const string WorkflowRuntimeTracker = "WorkflowRuntimeTracker";
/// <summary>
/// 流程失败消息
/// </summary>
public const string WorkflowFailureMessage = "WorkflowFailureMessage";
/// <summary>
/// 流程失败消息键
/// </summary>
public const string WorkflowFailureMessageKey = "WorkflowFailureMessageKey";
/// <summary>
/// 流程失败消息参数
/// </summary>
public const string WorkflowFailureMessageArguments = "WorkflowFailureMessageArguments";
/// <summary>
/// 准备区服务
/// </summary>
public const string PreparationAreaService = "PreparationAreaService";
/// <summary>
/// 当前晶片状态服务
/// </summary>
public const string CurrentChipStateService = "CurrentChipStateService";
/// <summary>
/// 自动生产运行时状态服务
/// </summary>
public const string AutoProductionRuntimeStateService = "AutoProductionRuntimeStateService";
/// <summary>
/// 准备区状态
/// </summary>
public const string PreparationAreaStatus = "PreparationAreaStatus";
/// <summary>
/// 当前晶片状态
/// </summary>
public const string CurrentChipState = "CurrentChipState";
/// <summary>
/// 基板处理状态
/// </summary>
public const string SubstrateProcessState = "SubstrateProcessState";
/// <summary>
/// 待加载晶片
/// </summary>
public const string PendingChipLoad = "PendingChipLoad";
/// <summary>
/// 当前基板待处理数量
/// </summary>
public const string CurrentSubstratePendingCount = "CurrentSubstratePendingCount";
/// <summary>
/// 当前基板已处理数量
/// </summary>
public const string CurrentSubstrateProcessedCount = "CurrentSubstrateProcessedCount";
/// <summary>
/// 当前晶片剩余数量
/// </summary>
public const string CurrentChipRemainingCount = "CurrentChipRemainingCount";
/// <summary>
/// 转移需要重新检查
/// </summary>
public const string TransferNeedsRecheck = "TransferNeedsRecheck";
/// <summary>
/// 转移晶片已耗尽
/// </summary>
public const string TransferChipExhausted = "TransferChipExhausted";
/// <summary>
/// 晶片转移决策结果
/// </summary>
public const string DieTransferDecisionResult = "DieTransferDecisionResult";
/// <summary>
/// 晶片转移路径生成请求
/// </summary>
public const string DieTransferPathRequest = "DieTransferPathRequest";
/// <summary>
/// 晶片转移路径生成结果
/// </summary>
public const string DieTransferPathPlan = "DieTransferPathPlan";
/// <summary>
/// 当前父级活动/子流程名称
/// </summary>
public const string ParentActivityName = "ParentActivityName";
/// <summary>
/// 针头Z对刀次数
/// </summary>
public const string NeedleZCalibrationTouchCount = "NeedleZCalibrationTouchCount";
/// <summary>
/// 针头Z对刀完成后是否自动抬高
/// </summary>
public const string NeedleZCalibrationAutoRaiseZ1 = "NeedleZCalibrationAutoRaiseZ1";
/// <summary>
/// 针头Z对刀取消令牌
/// </summary>
public const string NeedleZCalibrationCancellationToken = "NeedleZCalibrationCancellationToken";
/// <summary>
/// 针头Z对刀工作流请求数据
/// </summary>
public const string NeedleZCalibrationWorkflowData = "NeedleZCalibrationWorkflowData";
/// <summary>
/// 针头Z对刀结果集合
/// </summary>
public const string NeedleZCalibrationResults = "NeedleZCalibrationResults";
/// <summary>
/// 针头Z对刀最后一次结果
/// </summary>
public const string NeedleZCalibrationLastResult = "NeedleZCalibrationLastResult";
/// <summary>
/// 针头Z对刀平均结果
/// </summary>
public const string NeedleZCalibrationAverageResult = "NeedleZCalibrationAverageResult";
/// <summary>
/// 基板定位结果
/// </summary>
public const string SubstratePositionResult = "SubstratePositionResult";
/// <summary>
/// 芯片定位结果
/// </summary>
public const string DiePositionResult = "DiePositionResult";
/// <summary>
/// 芯片定位运行态上下文
/// </summary>
public const string DiePositionContext = "DiePositionContext";
/// <summary>
/// 芯片定位扫描规划结果
/// </summary>
public const string DiePositionScanPlan = "DiePositionScanPlan";
/// <summary>
/// 基板测高结果
/// </summary>
public const string SubstrateHeightMeasureResult = "SubstrateHeightMeasureResult";
/// <summary>
/// 精度复检结果
/// </summary>
public const string DieRecheckResult = "DieRecheckResult";
/// <summary>
/// 基板Mark识别服务
/// </summary>
public const string SubstrateMarkRecognitionService = "SubstrateMarkRecognitionService";
}
}

View File

@@ -0,0 +1,581 @@
<?xml version="1.0"?>
<ArrayOfPersistenceObject xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<PersistenceObject xsi:type="AxisCardPO">
<AlarmOffset>0</AlarmOffset>
<CategoryName>AcsCard</CategoryName>
<ID>2d23c400-ccf2-469a-add5-ae92a98a85e6</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardPO.xml</ConfigPath>
<AxisCardType>DEMO</AxisCardType>
<AxisCardNum>0</AxisCardNum>
<PortNo>0</PortNo>
<ListAxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>PHS-X1</CategoryName>
<ID>3a9c772b-4a9c-4077-abbc-80fc038b81ee</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>AcsCard</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>0</AxisNum>
<AxisName>PHS-X1</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>PHS-X2</CategoryName>
<ID>c83e5f81-ef2c-413c-ace9-24bc97c30b32</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>AcsCard</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>1</AxisNum>
<AxisName>PHS-X2</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>WS-X3</CategoryName>
<ID>e0a2d6ed-2972-4595-a8dc-53a504187a9f</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>AcsCard</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>4</AxisNum>
<AxisName>WS-X3</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>PHS-Y1</CategoryName>
<ID>4e145d2a-f7f7-4ce2-a5b0-919375a03d87</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>AcsCard</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>2</AxisNum>
<AxisName>PHS-Y1</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>WS-R</CategoryName>
<ID>cebe7f5c-8cdd-47e8-906a-6083047a078d</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>AcsCard</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>6</AxisNum>
<AxisName>WS-R</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>SZ</CategoryName>
<ID>ce1f8f65-3409-4124-9c9a-ba750c848e36</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>AcsCard</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>8</AxisNum>
<AxisName>SZ</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>Stage-Y3</CategoryName>
<ID>05dfa6d1-401b-4a55-808b-5af2efeb358c</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>AcsCard</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>7</AxisNum>
<AxisName>Stage-Y3</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
</ListAxisCardParaPO>
<Version>1</Version>
<Core>0</Core>
<CrossCompensate>0</CrossCompensate>
<IsServoSeparate>false</IsServoSeparate>
</PersistenceObject>
<PersistenceObject xsi:type="AxisCardPO">
<AlarmOffset>0</AlarmOffset>
<CategoryName>Plc</CategoryName>
<ID>80a2e320-d4da-46a6-9f35-0a6d0996ea22</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardPO.xml</ConfigPath>
<AxisCardType>DEMO</AxisCardType>
<AxisCardNum>0</AxisCardNum>
<PortNo>0</PortNo>
<ListAxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>PHS-Z1</CategoryName>
<ID>471e0395-0918-4e0b-b9cb-d0727281ef6c</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>0</AxisNum>
<AxisName>PHS-Z1</AxisName>
<Direction>Z</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>PHS-Z3</CategoryName>
<ID>5dd115ab-09a4-4bbb-9fdf-e9b76cc5e88e</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>1</AxisNum>
<AxisName>PHS-Z3</AxisName>
<Direction>Z</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>PHS-Z4</CategoryName>
<ID>e359bc21-c1e8-4e06-acdb-1df94ee7931b</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>10</AxisNum>
<AxisName>PHS-Z4</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>PHS-Z5</CategoryName>
<ID>9d948211-59ae-498c-b124-531a69855752</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>2</AxisNum>
<AxisName>PHS-Z5</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>PHS-Z6</CategoryName>
<ID>477c569a-e460-4c45-917f-a0744ebed5ec</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>11</AxisNum>
<AxisName>PHS-Z6</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>Stage-Z7</CategoryName>
<ID>f761791c-8fb4-4a5b-8fef-0380e996e81f</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>7</AxisNum>
<AxisName>Stage-Z7</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>Stage-Z8</CategoryName>
<ID>28a1caa3-01bc-48ed-8551-68829b280a6f</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>8</AxisNum>
<AxisName>Stage-Z8</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>Stage-Z9</CategoryName>
<ID>71c8587a-1e85-4fa4-8809-14f00111a230</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>9</AxisNum>
<AxisName>Stage-Z9</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>Stage-Z10</CategoryName>
<ID>fc66c8d2-c35f-46b5-9c14-f3a017311e86</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>14</AxisNum>
<AxisName>Stage-Z10</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>Stage-CV-Y4</CategoryName>
<ID>6ebf862a-c91e-4bb2-a5b7-e67f307333b4</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>15</AxisNum>
<AxisName>Stage-CV-Y4</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>Stage-CV-Y5</CategoryName>
<ID>e1cf2e06-fe51-4115-8ad2-2a0b3dc53c00</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>16</AxisNum>
<AxisName>Stage-CV-Y5</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>WEU-Z11</CategoryName>
<ID>a765e366-03a3-451f-ab44-1e6129a6ac61</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>3</AxisNum>
<AxisName>WEU-Z11</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>WEU-Z12</CategoryName>
<ID>a4c43082-6841-45b4-83f2-b89a952a6547</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>4</AxisNum>
<AxisName>WEU-Z12</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>WEU-Y6</CategoryName>
<ID>670409e3-1e19-45c8-94b8-1f3fb1905dc2</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>5</AxisNum>
<AxisName>WEU-Y6</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>CV-Z13</CategoryName>
<ID>eb009565-40bf-4923-9b9b-fdf483dc1532</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>6</AxisNum>
<AxisName>CV-Z13</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>CV-Y7</CategoryName>
<ID>d75ee50a-ba99-4ec1-bb36-6e65cbd84180</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>18</AxisNum>
<AxisName>CV-Y7</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>CV-Y8</CategoryName>
<ID>8845c4e8-4d09-4f0a-b073-1b29e1ca59e6</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>19</AxisNum>
<AxisName>CV-Y8</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>CV-Y9</CategoryName>
<ID>4970be79-b46a-4004-84bf-82e922fb5cb8</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>20</AxisNum>
<AxisName>CV-Y9</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
<AxisCardParaPO>
<AlarmOffset>0</AlarmOffset>
<CategoryName>CV-Y10</CategoryName>
<ID>36583278-40bd-48ad-a749-02b4e58dcbca</ID>
<EnableLog>false</EnableLog>
<ConfigPath>AxisCardParaPO.xml</ConfigPath>
<AxisCardId>0</AxisCardId>
<AxisCardName>Plc</AxisCardName>
<AxisCardNum>0</AxisCardNum>
<AxisNum>21</AxisNum>
<AxisName>CV-Y10</AxisName>
<Direction>X</Direction>
<GoogalCore>0</GoogalCore>
<GoogalAxisNum>0</GoogalAxisNum>
<GoolgalPosErr>0</GoolgalPosErr>
<HomeDir>0</HomeDir>
<EscapeStep>0</EscapeStep>
<HomeMode>HOME_MODE_ERROR</HomeMode>
<HomeCheck>false</HomeCheck>
<MaxCheckPos>0.001</MaxCheckPos>
</AxisCardParaPO>
</ListAxisCardParaPO>
<Version>1</Version>
<Core>0</Core>
<CrossCompensate>0</CrossCompensate>
<IsServoSeparate>false</IsServoSeparate>
</PersistenceObject>
</ArrayOfPersistenceObject>

View File

@@ -0,0 +1,9 @@
# 气缸定义表
# 列顺序:Name,Module,ControlType,ExtendOutputIds,RetractOutputIds,ExtendedFeedbackIds,RetractedFeedbackIds,Description
# ControlType: SingleOutput / DualOutput / MultiOutput
# ExtendOutputIds / RetractOutputIds / ExtendedFeedbackIds / RetractedFeedbackIds 支持填写多个 IO 引用,使用分号分隔
# IO 引用优先使用 Configuration\DeviceIoPoints.csv 中的 Name;若 Id 在全表唯一,也兼容旧 Id 写法
Name,Module,ControlType,ExtendOutputIds,RetractOutputIds,ExtendedFeedbackIds,RetractedFeedbackIds,Description
WS θ夹紧气缸,WS,SingleOutput,PG.WS.ThetaClampCylAct,,PG.WS.ThetaClampCyl1WorkPos;PG.WS.ThetaClampCyl2WorkPos;PG.WS.ThetaClampCyl3WorkPos;PG.WS.ThetaClampCyl4WorkPos,PG.WS.ThetaClampCyl1HomePos;PG.WS.ThetaClampCyl2HomePos;PG.WS.ThetaClampCyl3HomePos;PG.WS.ThetaClampCyl4HomePos,WS θ 夹紧气缸总动作输出,对应 4 组伸出/缩回反馈
WS θ刹车压紧气缸,WS,SingleOutput,PG.WS.ThetaBrakePressCylAct,,,,当前 DeviceIoPoints.csv 未配置到位反馈
Stage 顶升同步气缸,Stage,MultiOutput,PG.Stage.LifterCyl1Act;PG.Stage.LifterCyl2Act,,PG.Stage.LifterCyl1WorkPos;PG.Stage.LifterCyl2WorkPos,PG.Stage.LifterCyl1HomePos;PG.Stage.LifterCyl2HomePos,Stage 顶升气缸1和2同步动作,统一伸出/缩回
1 # 气缸定义表
2 # 列顺序:Name,Module,ControlType,ExtendOutputIds,RetractOutputIds,ExtendedFeedbackIds,RetractedFeedbackIds,Description
3 # ControlType: SingleOutput / DualOutput / MultiOutput
4 # ExtendOutputIds / RetractOutputIds / ExtendedFeedbackIds / RetractedFeedbackIds 支持填写多个 IO 引用,使用分号分隔
5 # IO 引用优先使用 Configuration\DeviceIoPoints.csv 中的 Name;若 Id 在全表唯一,也兼容旧 Id 写法
6 Name,Module,ControlType,ExtendOutputIds,RetractOutputIds,ExtendedFeedbackIds,RetractedFeedbackIds,Description
7 WS θ夹紧气缸,WS,SingleOutput,PG.WS.ThetaClampCylAct,,PG.WS.ThetaClampCyl1WorkPos;PG.WS.ThetaClampCyl2WorkPos;PG.WS.ThetaClampCyl3WorkPos;PG.WS.ThetaClampCyl4WorkPos,PG.WS.ThetaClampCyl1HomePos;PG.WS.ThetaClampCyl2HomePos;PG.WS.ThetaClampCyl3HomePos;PG.WS.ThetaClampCyl4HomePos,WS θ 夹紧气缸总动作输出,对应 4 组伸出/缩回反馈
8 WS θ刹车压紧气缸,WS,SingleOutput,PG.WS.ThetaBrakePressCylAct,,,,当前 DeviceIoPoints.csv 未配置到位反馈
9 Stage 顶升同步气缸,Stage,MultiOutput,PG.Stage.LifterCyl1Act;PG.Stage.LifterCyl2Act,,PG.Stage.LifterCyl1WorkPos;PG.Stage.LifterCyl2WorkPos,PG.Stage.LifterCyl1HomePos;PG.Stage.LifterCyl2HomePos,Stage 顶升气缸1和2同步动作,统一伸出/缩回

View File

@@ -0,0 +1,97 @@
# Device IO 定义表
# 列顺序:Id Module Name Type Card StationNo LineNo Enabled DefaultValue IsInverse Description
# Module: 业务模块名(如 SubstrateLoad/ChipLoad/SubstrateOut/ChipUnload)
# Type: Input/Output (也支持 IN/OUT/DI/DO)
# Card: Td/Acs
# StationNo: 台达站号
# LineNo: 实际接线线标/号码管标识
# Enabled: 1/0 或 true/false
# DefaultValue: 当 IO 未启用或无法读取时使用的默认状态,1/0 或 true/false
Id,Module,Name,Type,Card,StationNo,LineNo,Enabled,DefaultValue,IsInverse,Description
0,WZ,PG.WZ.StartButton,Input,Td,1,X00100,1,1,0,WZ 启动按钮
1,WZ,PG.WZ.StopButton,Input,Td,1,X00101,1,1,0,WZ 停止按钮
2,WZ,PG.WZ.ResetButton,Input,Td,1,X00102,1,1,0,WZ 复位按钮
3,WZ,PG.WZ.EmergencyStopButton,Input,Td,1,X00103,1,1,0,WZ 急停按钮
4,WZ,PG.WZ.BlueButton,Input,Td,1,X00104,1,1,0,WZ 蓝色按钮(预留)
5,Stage,PG.Stage.ToolSetterSignal,Input,Td,1,X00105,1,1,0,Stage 对刀信号
6,Stage,PG.Stage.SubstrateInPlace,Input,Td,1,X00106,1,1,0,Stage 基板到位感应
7,Stage,PG.Stage.LifterCyl1WorkPos,Input,Td,1,X00107,1,1,0,Stage 顶升气缸1动点
8,Stage,PG.Stage.LifterCyl1HomePos,Input,Td,1,X00108,1,1,0,Stage 顶升气缸1原点
9,Stage,PG.Stage.LifterCyl2WorkPos,Input,Td,1,X00109,1,1,0,Stage 顶升气缸2动点
10,Stage,PG.Stage.LifterCyl2HomePos,Input,Td,1,X00110,1,1,0,Stage 顶升气缸2原点
11,WS,PG.WS.ThetaClampCyl1WorkPos,Input,Td,1,X00111,1,1,0,WS θ夹紧气缸1动点
12,WS,PG.WS.ThetaClampCyl1HomePos,Input,Td,1,X00112,1,1,0,WS θ夹紧气缸1原点
13,WS,PG.WS.ThetaClampCyl2WorkPos,Input,Td,1,X00113,1,1,0,WS θ夹紧气缸2动点
14,WS,PG.WS.ThetaClampCyl2HomePos,Input,Td,1,X00114,1,1,0,WS θ夹紧气缸2原点
15,WS,PG.WS.ThetaClampCyl3WorkPos,Input,Td,1,X00115,1,1,0,WS θ夹紧气缸3动点
0,WS,PG.WS.ThetaClampCyl3HomePos,Input,Td,2,X00200,1,1,0,WS θ夹紧气缸3原点
1,WS,PG.WS.ThetaClampCyl4WorkPos,Input,Td,2,X00201,1,1,0,WS θ夹紧气缸4动点
2,WS,PG.WS.ThetaClampCyl4HomePos,Input,Td,2,X00202,1,1,0,WS θ夹紧气缸4原点
3,WS,PG.WS.ThetaTrayPresentSensor1,Input,Td,2,X00203,1,1,0,WS θ料盘有无感应1
4,WS,PG.WS.ThetaTrayPresentSensor2,Input,Td,2,X00204,1,1,0,WS θ料盘有无感应2
5,WS,PG.WS.ThetaTrayPresentSensor3,Input,Td,2,X00205,1,1,0,WS θ料盘有无感应3
6,WS,PG.WS.ThetaTrayPresentSensor4,Input,Td,2,X00206,1,1,0,WS θ料盘有无感应4
7,WS,PG.WS.BondHead1InPlace,Input,Td,2,X00207,1,1,0,WS 刺晶头1在位
8,WS,PG.WS.BondHead2InPlace,Input,Td,2,X00208,1,1,0,WS 刺晶头2在位
9,WS,PG.WS.BondHead3InPlace,Input,Td,2,X00209,1,1,0,WS 刺晶头3在位
10,WS,PG.WS.BondHead4InPlace,Input,Td,2,X00210,1,1,0,WS 刺晶头4在位
11,WEU,PG.WEU.Z11MaterialSensor1,Input,Td,2,X00211,1,1,0,WEU Z11 有无料感应1
12,WEU,PG.WEU.Z11MaterialSensor2,Input,Td,2,X00212,1,1,0,WEU Z11 有无料感应2
13,WEU,PG.WEU.Z12MaterialSensor3,Input,Td,2,X00213,1,1,0,WEU Z12 有无料感应3
14,WEU,PG.WEU.Z12MaterialSensor4,Input,Td,2,X00214,1,1,0,WEU Z12 有无料感应4
0,CV,PG.CV.UpperInletSensor,Input,Td,3,X00300,1,1,0,CV 上层进料感应
1,CV,PG.CV.UpperInPlaceSensor,Input,Td,3,X00301,1,1,0,CV 上层到位感应
2,CV,PG.CV.LowerOutletSensor,Input,Td,3,X00302,1,1,0,CV 下层出料感应
3,CV,PG.CV.LowerInPlaceSensor,Input,Td,3,X00303,1,1,0,CV 下层到位感应
4,WZ,PG.WZ.SafetyDoor1Closed,Input,Td,3,X00304,1,1,0,WZ 安全门1关门检测
5,WZ,PG.WZ.SafetyDoor2Closed,Input,Td,3,X00305,1,1,0,WZ 安全门2关门检测
6,WZ,PG.WZ.SafetyDoor3Closed,Input,Td,3,X00306,1,1,0,WZ 安全门3关门检测
7,WZ,PG.WZ.SafetyDoor4Closed,Input,Td,3,X00307,1,1,0,WZ 安全门4关门检测
8,WZ,PG.WZ.SecurityDoorShield,Input,Td,3,X00308,1,1,0,WZ 安全门屏蔽
10,General,PG.General.PhaseLossSensor,Input,Td,3,X00310,1,1,0,缺相检测
11,General,PG.General.SafetyCircuitStatus1,Input,Td,3,X00311,1,1,0,安全回路状态1
12,General,PG.General.SafetyCircuitStatus2,Input,Td,3,X00312,1,1,0,安全回路状态2
13,CV,PG.CV.IonWindRodAlarmDetection,Input,Td,3,X00313,1,1,0,CV 离子风棒报警检测
14,EFEM,PG.EFEM.IonWindRodAlarmDetection1,Input,Td,3,X00314,1,1,0,EFEM离子风棒报警检测1
15,EFEM,PG.EFEM.IonWindRodAlarmDetection2,Input,Td,3,X00315,1,1,0,EFEM离子风棒报警检测2
0,AxisZ,PG.AxisZ.Z1GuideRailBrake,Output,Td,4,Y00100,1,1,0,Z1轴导轨抱闸
1,AxisZ,PG.AxisZ.Z3CameraAirCooling,Output,Td,4,Y00101,1,1,0,Z3相机气冷
2,AxisZ,PG.AxisZ.Z5CameraAirCooling,Output,Td,4,Y00102,1,1,0,Z5相机气冷
3,AxisZ,PG.AxisZ.Z10CameraAirCooling,Output,Td,4,Y00103,1,1,0,Z10相机气冷
4,AxisZ,PG.AxisZ.Z2BondHeadAirCooling,Output,Td,4,Y00104,1,1,0,Z2刺晶头气冷
5,CV,PG.CV.IonBarAct,Output,Td,4,Y00105,1,1,0,CV 离子风棒
6,WEU,PG.WEU.IonBarAct1,Output,Td,4,Y00106,1,1,0,WEU离子风棒1
7,WEU,PG.WEU.IonBarAct2,Output,Td,4,Y00107,1,1,0,WEU离子风棒2
8,WS,PG.WS.ThetaClampCylAct,Output,Td,4,Y00108,1,1,0,WS θ 夹紧气缸动作
9,WS,PG.WS.ThetaBrakePressCylAct,Output,Td,4,Y00109,1,1,0,WS θ 刹车压紧气缸动作
10,Stage,PG.Stage.LifterCyl1Act,Output,Td,4,Y00110,1,1,0,Stage 顶升气缸1动作
11,Stage,PG.Stage.LifterCyl2Act,Output,Td,4,Y00111,1,1,0,Stage 顶升气缸2动作
0,WZ,PG.WZ.StartButtonLight,Output,Td,5,Y00200,1,1,0,WZ 启动按钮灯
1,WZ,PG.WZ.StopButtonLight,Output,Td,5,Y00201,1,1,0,WZ 停止按钮灯
2,WZ,PG.WZ.ResetButtonLight,Output,Td,5,Y00202,1,1,0,WZ 复位按钮灯
3,WZ,PG.WZ.BlueButtonLight,Output,Td,5,Y00203,1,1,0,WZ 蓝色按钮灯
4,WZ,PG.WZ.WorkLight1,Output,Td,5,Y00204,1,1,0,WZ 照明灯1
6,EFEM,PG.EFEM.WorkLight1,Output,Td,5,Y00206,1,1,0,EFEM 照明灯1
8,WZ,PG.WZ.StackLightRed,Output,Td,5,Y00208,1,1,0,WZ 四色灯-红
9,WZ,PG.WZ.StackLightYellow,Output,Td,5,Y00209,1,1,0,WZ 四色灯-黄
10,WZ,PG.WZ.StackLightGreen,Output,Td,5,Y00210,1,1,0,WZ 四色灯-绿
11,WZ,PG.WZ.StackLightBlue,Output,Td,5,Y00211,1,1,0,WZ 四色灯-蓝
12,WZ,PG.WZ.BuzzerAct,Output,Td,5,Y00212,1,1,0,WZ 蜂鸣器
0,Stage,PG.Stage.BreakingTheVacuum1,Output,Td,6,Y00300,1,1,0,Stage 破真空1
1,Stage,PG.Stage.BreakingTheVacuum2,Output,Td,6,Y00301,1,1,0,STAGE 破真空2
2,Stage,PG.Stage.BreakingTheVacuum3,Output,Td,6,Y00302,1,1,0,STAGE 破真空3
3,Stage,PG.Stage.BreakingTheVacuum4,Output,Td,6,Y00303,1,1,0,STAGE 破真空4
4,WEU,PG.WEU.TrayBreakVacuum1,Output,Td,6,Y00304,1,1,0,WEU 料盘破真空1
5,WEU,PG.WEU.TrayBreakVacuum2,Output,Td,6,Y00305,1,1,0,WEU 料盘破真空2
6,AxisZ,PG.AxisZ.Z2BondHeadBreakVacuum,Output,Td,6,Y00306,1,1,0,PHS Z2 刺晶头破真空
7,Stage,PG.Stage.Vacuum1,Output,Td,6,Y00307,1,1,0,STAGE 吸真空1
8,Stage,PG.Stage.Vacuum2,Output,Td,6,Y00308,1,1,0,STAGE 吸真空2
9,Stage,PG.Stage.Vacuum3,Output,Td,6,Y00309,1,1,0,STAGE 吸真空3
10,Stage,PG.Stage.Vacuum4,Output,Td,6,Y00310,1,1,0,STAGE 吸真空4
11,WEU,PG.WEU.TrayVacuum1,Output,Td,6,Y00311,1,1,0,WEU 料盘吸真空1
12,WEU,PG.WEU.TrayVacuum2,Output,Td,6,Y00312,1,1,0,WEU 料盘吸真空2
13,AxisZ,PG.AxisZ.Z2BondHeadVacuum,Output,Td,6,Y00313,1,1,0,PHS Z2 刺晶头吸真空
1,WZ,PG.WZ.SafetyDoor1Open,Output,Td,7,Y00401,1,1,0,WZ 安全门1打开
2,WZ,PG.WZ.SafetyDoor2Open,Output,Td,7,Y00402,1,1,0,WZ 安全门2打开
3,WZ,PG.WZ.SafetyDoor3Open,Output,Td,7,Y00402,1,1,0,WZ 安全门3打开
4,WZ,PG.WZ.SafetyDoor4Open,Output,Td,7,Y00403,1,1,0,WZ 安全门4打开
1 # Device IO 定义表
2 # 列顺序:Id Module Name Type Card StationNo LineNo Enabled DefaultValue IsInverse Description
3 # Module: 业务模块名(如 SubstrateLoad/ChipLoad/SubstrateOut/ChipUnload)
4 # Type: Input/Output (也支持 IN/OUT/DI/DO)
5 # Card: Td/Acs
6 # StationNo: 台达站号
7 # LineNo: 实际接线线标/号码管标识
8 # Enabled: 1/0 或 true/false
9 # DefaultValue: 当 IO 未启用或无法读取时使用的默认状态,1/0 或 true/false
10 Id,Module,Name,Type,Card,StationNo,LineNo,Enabled,DefaultValue,IsInverse,Description
11 0,WZ,PG.WZ.StartButton,Input,Td,1,X00100,1,1,0,WZ 启动按钮
12 1,WZ,PG.WZ.StopButton,Input,Td,1,X00101,1,1,0,WZ 停止按钮
13 2,WZ,PG.WZ.ResetButton,Input,Td,1,X00102,1,1,0,WZ 复位按钮
14 3,WZ,PG.WZ.EmergencyStopButton,Input,Td,1,X00103,1,1,0,WZ 急停按钮
15 4,WZ,PG.WZ.BlueButton,Input,Td,1,X00104,1,1,0,WZ 蓝色按钮(预留)
16 5,Stage,PG.Stage.ToolSetterSignal,Input,Td,1,X00105,1,1,0,Stage 对刀信号
17 6,Stage,PG.Stage.SubstrateInPlace,Input,Td,1,X00106,1,1,0,Stage 基板到位感应
18 7,Stage,PG.Stage.LifterCyl1WorkPos,Input,Td,1,X00107,1,1,0,Stage 顶升气缸1动点
19 8,Stage,PG.Stage.LifterCyl1HomePos,Input,Td,1,X00108,1,1,0,Stage 顶升气缸1原点
20 9,Stage,PG.Stage.LifterCyl2WorkPos,Input,Td,1,X00109,1,1,0,Stage 顶升气缸2动点
21 10,Stage,PG.Stage.LifterCyl2HomePos,Input,Td,1,X00110,1,1,0,Stage 顶升气缸2原点
22 11,WS,PG.WS.ThetaClampCyl1WorkPos,Input,Td,1,X00111,1,1,0,WS θ夹紧气缸1动点
23 12,WS,PG.WS.ThetaClampCyl1HomePos,Input,Td,1,X00112,1,1,0,WS θ夹紧气缸1原点
24 13,WS,PG.WS.ThetaClampCyl2WorkPos,Input,Td,1,X00113,1,1,0,WS θ夹紧气缸2动点
25 14,WS,PG.WS.ThetaClampCyl2HomePos,Input,Td,1,X00114,1,1,0,WS θ夹紧气缸2原点
26 15,WS,PG.WS.ThetaClampCyl3WorkPos,Input,Td,1,X00115,1,1,0,WS θ夹紧气缸3动点
27 0,WS,PG.WS.ThetaClampCyl3HomePos,Input,Td,2,X00200,1,1,0,WS θ夹紧气缸3原点
28 1,WS,PG.WS.ThetaClampCyl4WorkPos,Input,Td,2,X00201,1,1,0,WS θ夹紧气缸4动点
29 2,WS,PG.WS.ThetaClampCyl4HomePos,Input,Td,2,X00202,1,1,0,WS θ夹紧气缸4原点
30 3,WS,PG.WS.ThetaTrayPresentSensor1,Input,Td,2,X00203,1,1,0,WS θ料盘有无感应1
31 4,WS,PG.WS.ThetaTrayPresentSensor2,Input,Td,2,X00204,1,1,0,WS θ料盘有无感应2
32 5,WS,PG.WS.ThetaTrayPresentSensor3,Input,Td,2,X00205,1,1,0,WS θ料盘有无感应3
33 6,WS,PG.WS.ThetaTrayPresentSensor4,Input,Td,2,X00206,1,1,0,WS θ料盘有无感应4
34 7,WS,PG.WS.BondHead1InPlace,Input,Td,2,X00207,1,1,0,WS 刺晶头1在位
35 8,WS,PG.WS.BondHead2InPlace,Input,Td,2,X00208,1,1,0,WS 刺晶头2在位
36 9,WS,PG.WS.BondHead3InPlace,Input,Td,2,X00209,1,1,0,WS 刺晶头3在位
37 10,WS,PG.WS.BondHead4InPlace,Input,Td,2,X00210,1,1,0,WS 刺晶头4在位
38 11,WEU,PG.WEU.Z11MaterialSensor1,Input,Td,2,X00211,1,1,0,WEU Z11 有无料感应1
39 12,WEU,PG.WEU.Z11MaterialSensor2,Input,Td,2,X00212,1,1,0,WEU Z11 有无料感应2
40 13,WEU,PG.WEU.Z12MaterialSensor3,Input,Td,2,X00213,1,1,0,WEU Z12 有无料感应3
41 14,WEU,PG.WEU.Z12MaterialSensor4,Input,Td,2,X00214,1,1,0,WEU Z12 有无料感应4
42 0,CV,PG.CV.UpperInletSensor,Input,Td,3,X00300,1,1,0,CV 上层进料感应
43 1,CV,PG.CV.UpperInPlaceSensor,Input,Td,3,X00301,1,1,0,CV 上层到位感应
44 2,CV,PG.CV.LowerOutletSensor,Input,Td,3,X00302,1,1,0,CV 下层出料感应
45 3,CV,PG.CV.LowerInPlaceSensor,Input,Td,3,X00303,1,1,0,CV 下层到位感应
46 4,WZ,PG.WZ.SafetyDoor1Closed,Input,Td,3,X00304,1,1,0,WZ 安全门1关门检测
47 5,WZ,PG.WZ.SafetyDoor2Closed,Input,Td,3,X00305,1,1,0,WZ 安全门2关门检测
48 6,WZ,PG.WZ.SafetyDoor3Closed,Input,Td,3,X00306,1,1,0,WZ 安全门3关门检测
49 7,WZ,PG.WZ.SafetyDoor4Closed,Input,Td,3,X00307,1,1,0,WZ 安全门4关门检测
50 8,WZ,PG.WZ.SecurityDoorShield,Input,Td,3,X00308,1,1,0,WZ 安全门屏蔽
51 10,General,PG.General.PhaseLossSensor,Input,Td,3,X00310,1,1,0,缺相检测
52 11,General,PG.General.SafetyCircuitStatus1,Input,Td,3,X00311,1,1,0,安全回路状态1
53 12,General,PG.General.SafetyCircuitStatus2,Input,Td,3,X00312,1,1,0,安全回路状态2
54 13,CV,PG.CV.IonWindRodAlarmDetection,Input,Td,3,X00313,1,1,0,CV 离子风棒报警检测
55 14,EFEM,PG.EFEM.IonWindRodAlarmDetection1,Input,Td,3,X00314,1,1,0,EFEM离子风棒报警检测1
56 15,EFEM,PG.EFEM.IonWindRodAlarmDetection2,Input,Td,3,X00315,1,1,0,EFEM离子风棒报警检测2
57 0,AxisZ,PG.AxisZ.Z1GuideRailBrake,Output,Td,4,Y00100,1,1,0,Z1轴导轨抱闸
58 1,AxisZ,PG.AxisZ.Z3CameraAirCooling,Output,Td,4,Y00101,1,1,0,Z3相机气冷
59 2,AxisZ,PG.AxisZ.Z5CameraAirCooling,Output,Td,4,Y00102,1,1,0,Z5相机气冷
60 3,AxisZ,PG.AxisZ.Z10CameraAirCooling,Output,Td,4,Y00103,1,1,0,Z10相机气冷
61 4,AxisZ,PG.AxisZ.Z2BondHeadAirCooling,Output,Td,4,Y00104,1,1,0,Z2刺晶头气冷
62 5,CV,PG.CV.IonBarAct,Output,Td,4,Y00105,1,1,0,CV 离子风棒
63 6,WEU,PG.WEU.IonBarAct1,Output,Td,4,Y00106,1,1,0,WEU离子风棒1
64 7,WEU,PG.WEU.IonBarAct2,Output,Td,4,Y00107,1,1,0,WEU离子风棒2
65 8,WS,PG.WS.ThetaClampCylAct,Output,Td,4,Y00108,1,1,0,WS θ 夹紧气缸动作
66 9,WS,PG.WS.ThetaBrakePressCylAct,Output,Td,4,Y00109,1,1,0,WS θ 刹车压紧气缸动作
67 10,Stage,PG.Stage.LifterCyl1Act,Output,Td,4,Y00110,1,1,0,Stage 顶升气缸1动作
68 11,Stage,PG.Stage.LifterCyl2Act,Output,Td,4,Y00111,1,1,0,Stage 顶升气缸2动作
69 0,WZ,PG.WZ.StartButtonLight,Output,Td,5,Y00200,1,1,0,WZ 启动按钮灯
70 1,WZ,PG.WZ.StopButtonLight,Output,Td,5,Y00201,1,1,0,WZ 停止按钮灯
71 2,WZ,PG.WZ.ResetButtonLight,Output,Td,5,Y00202,1,1,0,WZ 复位按钮灯
72 3,WZ,PG.WZ.BlueButtonLight,Output,Td,5,Y00203,1,1,0,WZ 蓝色按钮灯
73 4,WZ,PG.WZ.WorkLight1,Output,Td,5,Y00204,1,1,0,WZ 照明灯1
74 6,EFEM,PG.EFEM.WorkLight1,Output,Td,5,Y00206,1,1,0,EFEM 照明灯1
75 8,WZ,PG.WZ.StackLightRed,Output,Td,5,Y00208,1,1,0,WZ 四色灯-红
76 9,WZ,PG.WZ.StackLightYellow,Output,Td,5,Y00209,1,1,0,WZ 四色灯-黄
77 10,WZ,PG.WZ.StackLightGreen,Output,Td,5,Y00210,1,1,0,WZ 四色灯-绿
78 11,WZ,PG.WZ.StackLightBlue,Output,Td,5,Y00211,1,1,0,WZ 四色灯-蓝
79 12,WZ,PG.WZ.BuzzerAct,Output,Td,5,Y00212,1,1,0,WZ 蜂鸣器
80 0,Stage,PG.Stage.BreakingTheVacuum1,Output,Td,6,Y00300,1,1,0,Stage 破真空1
81 1,Stage,PG.Stage.BreakingTheVacuum2,Output,Td,6,Y00301,1,1,0,STAGE 破真空2
82 2,Stage,PG.Stage.BreakingTheVacuum3,Output,Td,6,Y00302,1,1,0,STAGE 破真空3
83 3,Stage,PG.Stage.BreakingTheVacuum4,Output,Td,6,Y00303,1,1,0,STAGE 破真空4
84 4,WEU,PG.WEU.TrayBreakVacuum1,Output,Td,6,Y00304,1,1,0,WEU 料盘破真空1
85 5,WEU,PG.WEU.TrayBreakVacuum2,Output,Td,6,Y00305,1,1,0,WEU 料盘破真空2
86 6,AxisZ,PG.AxisZ.Z2BondHeadBreakVacuum,Output,Td,6,Y00306,1,1,0,PHS Z2 刺晶头破真空
87 7,Stage,PG.Stage.Vacuum1,Output,Td,6,Y00307,1,1,0,STAGE 吸真空1
88 8,Stage,PG.Stage.Vacuum2,Output,Td,6,Y00308,1,1,0,STAGE 吸真空2
89 9,Stage,PG.Stage.Vacuum3,Output,Td,6,Y00309,1,1,0,STAGE 吸真空3
90 10,Stage,PG.Stage.Vacuum4,Output,Td,6,Y00310,1,1,0,STAGE 吸真空4
91 11,WEU,PG.WEU.TrayVacuum1,Output,Td,6,Y00311,1,1,0,WEU 料盘吸真空1
92 12,WEU,PG.WEU.TrayVacuum2,Output,Td,6,Y00312,1,1,0,WEU 料盘吸真空2
93 13,AxisZ,PG.AxisZ.Z2BondHeadVacuum,Output,Td,6,Y00313,1,1,0,PHS Z2 刺晶头吸真空
94 1,WZ,PG.WZ.SafetyDoor1Open,Output,Td,7,Y00401,1,1,0,WZ 安全门1打开
95 2,WZ,PG.WZ.SafetyDoor2Open,Output,Td,7,Y00402,1,1,0,WZ 安全门2打开
96 3,WZ,PG.WZ.SafetyDoor3Open,Output,Td,7,Y00402,1,1,0,WZ 安全门3打开
97 4,WZ,PG.WZ.SafetyDoor4Open,Output,Td,7,Y00403,1,1,0,WZ 安全门4打开

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Layout Version="1.0.0.1" WindowHeight="1024" WindowWidth="1280" MachineId="12345678901234567">
<Header Title="PG-A1">
<HeaderItem Id="HideEMO"/>
<HeaderItem Id="HideCIMStatus"/>
</Header>
<Body>
<StatusBar>
<StatusLight Name="设备一" LangName="Device" Binding="Device"></StatusLight>
</StatusBar>
<Pages>
<Page Name="主页" LangName="PageHome" Target="Home">
</Page>
<Page Name="配方" LangName="PageRecipe" Target="Recipe">
</Page>
<Page Name="参数设置" LangName="PageSettings" Target="PageSettings" DisabledState="SemiAutomatic;Automatic" UserRightId="PageSettings">
</Page>
<Page Name="设备维护" LangName="MenuDeviceMaint" Target="MenuDeviceMaint" DisabledState="SemiAutomatic;Automatic" UserRightId="PageSettings">
</Page>
<Page Name="标定与校准" LangName="PageCalib" Target="PageCalib" DisabledState="SemiAutomatic;Automatic" UserRightId="PageCalib">
</Page>
<Page Name="半自动" LangName="ManualModeOperation" Target="ManualOperate">
</Page>
</Pages>
</Body>
<Footer Target="Footer"/>
</Layout>

View File

@@ -0,0 +1,28 @@
using MainShell.Common;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace MainShell.Converter
{
public class BondingPathModeToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BondingPathMode speedMode = (BondingPathMode)value;
int mode = System.Convert.ToInt32(parameter.ToString());
return mode == (int)speedMode ? true : false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
int mode = System.Convert.ToInt32(parameter.ToString());
return (BondingPathMode)mode;
}
}
}

View File

@@ -0,0 +1,28 @@
using MainShell.Common;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace MainShell.Converter
{
public class BondingRegionModeToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BondingStagePathMode speedMode = (BondingStagePathMode)value;
int mode = System.Convert.ToInt32(parameter.ToString());
return mode == (int)speedMode ? true : false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
int mode = System.Convert.ToInt32(parameter.ToString());
return (BondingStagePathMode)mode;
}
}
}

View File

@@ -0,0 +1,28 @@
using MainShell.Common;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace MainShell.Converter
{
public class BondingWsPathModeToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BondingWSPathMode speedMode = (BondingWSPathMode)value;
int mode = System.Convert.ToInt32(parameter.ToString());
return mode == (int)speedMode ? true : false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
int mode = System.Convert.ToInt32(parameter.ToString());
return (BondingWSPathMode)mode;
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace MainShell.Converter
{
public class BoolToInversionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
return !System.Convert.ToBoolean(value);
}
else
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,94 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MainShell.Converter
{
public class BoolToVisibleConverter : IValueConverter
{
// 将 bool (或可转换为 bool 的值) 转换为 Visibility
// parameter 支持:
// 包含 "invert" / "inverse" / "not" / "!" 的字符串 — 反转布尔值
// 包含 "hidden" 的字符串 — 当为 false 时返回 Visibility.Hidden 而不是 Collapsed
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isVisible = false;
if (value is bool b)
{
isVisible = b;
}
else if (value is bool?)
{
isVisible = ((bool?)value) ?? false;
}
else if (value is string s)
{
bool.TryParse(s, out isVisible);
}
else if (value != null)
{
try
{
isVisible = System.Convert.ToBoolean(value);
}
catch
{
isVisible = false;
}
}
var paramString = parameter as string;
bool invert = false;
bool useHidden = false;
if (!string.IsNullOrEmpty(paramString))
{
var p = paramString.ToLowerInvariant();
if (p.Contains("invert") || p.Contains("inverse") || p.Contains("not") || p.Contains("!"))
invert = true;
if (p.Contains("hidden"))
useHidden = true;
}
if (invert)
isVisible = !isVisible;
return isVisible ? Visibility.Visible : (useHidden ? Visibility.Hidden : Visibility.Collapsed);
}
// 将 Visibility 转换回 bool
// Visible => true;Hidden/Collapsed => false
// 支持与 Convert 相同的 parameter 反转语义
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is Visibility))
return Binding.DoNothing;
var vis = (Visibility)value;
bool result = vis == Visibility.Visible;
var paramString = parameter as string;
if (!string.IsNullOrEmpty(paramString))
{
var p = paramString.ToLowerInvariant();
if (p.Contains("invert") || p.Contains("inverse") || p.Contains("not") || p.Contains("!"))
result = !result;
}
if (targetType == typeof(bool) || targetType == typeof(object))
return result;
if (targetType == typeof(bool?))
return (bool?)result;
try
{
return System.Convert.ChangeType(result, targetType);
}
catch
{
return result;
}
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace MainShell.Converter
{
public class DateTimeToStringConverter : IValueConverter
{
public string Format { get; set; } = "yyyy-MM-dd HH:mm:ss";
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return string.Empty;
if (!(value is DateTime dt)) return string.Empty;
if (dt == DateTime.MinValue) return string.Empty;
// parameter 优先用于临时格式覆盖
var fmt = parameter as string ?? Format;
return dt.ToString(fmt, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Linq;
using System.Windows.Markup;
namespace MainShell.Converter
{
// XAML 中可直接写 {conv:EnumBindingSource {x:Type local:MyEnum}}
public class EnumBindingSourceExtension : MarkupExtension
{
public Type EnumType { get; set; }
public bool UseDescription { get; set; } = true;
public EnumBindingSourceExtension() { }
public EnumBindingSourceExtension(Type enumType)
{
EnumType = enumType;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (EnumType == null || !EnumType.IsEnum) return null;
var values = Enum.GetValues(EnumType).Cast<Enum>();
return values.Select(v => new EnumItem
{
Value = v,
Description = UseDescription ? EnumHelper.GetEnumDescription(v) : v.ToString()
}).ToList();
}
}
public class EnumItem
{
public Enum Value { get; set; }
public string Description { get; set; }
public int IntValue => Convert.ToInt32(Value);
}
}

View File

@@ -0,0 +1,79 @@
using MwFramework.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace MainShell.Converter
{
/// <summary>
/// 获取枚举类型的Description描述信息
/// </summary>
public class EnumDescriptionConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.ToString() != "")
{
Enum myEnum = (Enum)value;
string description = EnumHelper.GetEnumDescription(myEnum);
return description;
}
else
{
return "";
}
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return string.Empty;
}
}
public class EnumToBool : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value == (int)parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
{
return parameter;
}
else
{
return Binding.DoNothing;
}
}
}
public static class EnumHelper
{
public static string GetEnumDescription(Enum enumObj)
{
FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());
var descriptionAttr = fieldInfo
.GetCustomAttributes(false)
.OfType<DescriptionAttribute>()
.Cast<DescriptionAttribute>()
.SingleOrDefault();
if (descriptionAttr == null)
{
return enumObj.ToString();
}
else
{
return descriptionAttr.Description;
}
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
namespace MainShell.Converter
{
public class ObjectToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,30 @@
using MainShell.Common;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Data;
namespace MainShell.Converter
{
public class RunningModeEnumToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
RunningMode speedMode = (RunningMode)value;
int mode = System.Convert.ToInt32(parameter.ToString());
return mode == (int)speedMode ? true : false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
int mode = System.Convert.ToInt32(parameter.ToString());
return (RunningMode)mode;
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace MainShell.Converter
{
public class ScreenToViewModelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return null;
var viewModelType = value.GetType();
var viewName = viewModelType.FullName.Replace("ViewModel", "View");
var viewType = Type.GetType(viewName);
if (viewType != null)
{
// 创建View实例,并设置其DataContext为当前的ViewModel
var view = (FrameworkElement)Activator.CreateInstance(viewType);
view.DataContext = value;
return view;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,29 @@
using MainShell.Recipe.Models.SubstrateParameter;
using System;
using System.Globalization;
using System.Windows.Data;
namespace MainShell.Converter
{
public class SubstrateHeightMeasureModeToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var mode = value is SubstrateHeightMeasureMode measureMode ? measureMode : SubstrateHeightMeasureMode.StandardTeachPosition;
var parameterValue = System.Convert.ToInt32(parameter, CultureInfo.InvariantCulture);
return parameterValue == (int)mode;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is bool isChecked) || !isChecked)
{
return Binding.DoNothing;
}
var parameterValue = System.Convert.ToInt32(parameter, CultureInfo.InvariantCulture);
return (SubstrateHeightMeasureMode)parameterValue;
}
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace MainShell.Converter
{
public class WidthToMarginConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double actualWidth = 0;
if (value is double d) actualWidth = d;
double expanded = 200;
double extra = 0;
if (parameter is string s)
{
var parts = s.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 1 && double.TryParse(parts[0], out var p0)) expanded = p0;
if (parts.Length >= 2 && double.TryParse(parts[1], out var p1)) extra = p1;
}
// 计算右侧 Margin:
// 当 actualWidth == expanded 时返回 0(不偏移)
// 当 actualWidth 减小时会返回正值,从而把 ToggleButton 向左推(或根据需要调整公式)
double right = Math.Max(0, expanded - actualWidth) + extra;
// 根据布局需要你可以修改这里返回的 Thickness 的哪一项(Left/Right)
return new Thickness(-30, 0, 0, 0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,49 @@
using System.Collections.Generic;
namespace MainShell.DeviceMaintance.Model
{
public enum CylinderControlType
{
SingleOutput = 0,
DualOutput = 1,
MultiOutput = 2
}
public enum CylinderConditionType
{
PointOn = 0,
PointOff = 1
}
public class CylinderActionConditionDefinition
{
public CylinderConditionType ConditionType { get; set; }
public string PointReference { get; set; }
public string Message { get; set; }
public bool ExpectedState => ConditionType == CylinderConditionType.PointOn;
}
public class CylinderDefinition
{
public string Name { get; set; }
public string Description { get; set; }
public string Module { get; set; }
public CylinderControlType ControlType { get; set; }
public List<string> ExtendOutputPoints { get; set; } = new List<string>();
public List<string> RetractOutputPoints { get; set; } = new List<string>();
public List<string> ExtendedFeedbackPoints { get; set; } = new List<string>();
public List<string> RetractedFeedbackPoints { get; set; } = new List<string>();
public List<CylinderActionConditionDefinition> ExtendConditions { get; set; } = new List<CylinderActionConditionDefinition>();
public List<CylinderActionConditionDefinition> RetractConditions { get; set; } = new List<CylinderActionConditionDefinition>();
public bool HasRetractOutputs => RetractOutputPoints != null && RetractOutputPoints.Count > 0;
}
public class CylinderExecutionResult
{
public bool Success { get; set; }
public string FailureReason { get; set; }
public IReadOnlyList<string> FailurePointReferences { get; set; } = new List<string>();
}
}

View File

@@ -0,0 +1,209 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
namespace MainShell.DeviceMaintance.Model
{
public static class CylinderDefinitionLoader
{
private const string CylinderConfigFileName = "CylinderDefinitions.csv";
public static IReadOnlyList<CylinderDefinition> LoadDefinitions()
{
var definitions = new List<CylinderDefinition>();
var path = ResolveConfigPath();
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
{
return definitions;
}
var lines = File.ReadAllLines(path, Encoding.UTF8);
foreach (var raw in lines)
{
if (string.IsNullOrWhiteSpace(raw))
{
continue;
}
var line = raw.Trim();
if (line.StartsWith("#") || line.StartsWith("Name", StringComparison.OrdinalIgnoreCase))
{
continue;
}
var cols = line.Split(',');
if (cols.Length < 7)
{
continue;
}
var extendOutputPoints = ParsePointReferences(cols[3]);
if (extendOutputPoints.Count == 0)
{
continue;
}
var retractOutputPoints = ParsePointReferences(cols[4]);
var hasConditionColumns = cols.Length >= 10;
var extendConditionIndex = hasConditionColumns ? 7 : -1;
var retractConditionIndex = hasConditionColumns ? 8 : -1;
var descriptionIndex = hasConditionColumns ? 9 : 7;
definitions.Add(new CylinderDefinition
{
Name = cols[0].Trim(),
Module = cols[1].Trim(),
ControlType = ParseControlType(cols[2]),
ExtendOutputPoints = extendOutputPoints,
RetractOutputPoints = retractOutputPoints,
ExtendedFeedbackPoints = ParsePointReferences(cols[5]),
RetractedFeedbackPoints = ParsePointReferences(cols[6]),
ExtendConditions = extendConditionIndex >= 0 ? ParseConditions(cols[extendConditionIndex]) : new List<CylinderActionConditionDefinition>(),
RetractConditions = retractConditionIndex >= 0 ? ParseConditions(cols[retractConditionIndex]) : new List<CylinderActionConditionDefinition>(),
Description = cols.Length > descriptionIndex ? string.Join(",", cols.Skip(descriptionIndex)).Trim() : string.Empty
});
}
return definitions;
}
private static CylinderControlType ParseControlType(string raw)
{
if (string.Equals(raw, "DualOutput", StringComparison.OrdinalIgnoreCase) ||
string.Equals(raw, "Dual", StringComparison.OrdinalIgnoreCase))
{
return CylinderControlType.DualOutput;
}
if (string.Equals(raw, "MultiOutput", StringComparison.OrdinalIgnoreCase) ||
string.Equals(raw, "Multi", StringComparison.OrdinalIgnoreCase))
{
return CylinderControlType.MultiOutput;
}
return CylinderControlType.SingleOutput;
}
private static List<string> ParsePointReferences(string raw)
{
return (raw ?? string.Empty)
.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.Where(x => !string.IsNullOrWhiteSpace(x))
.ToList();
}
private static List<CylinderActionConditionDefinition> ParseConditions(string raw)
{
return (raw ?? string.Empty)
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
.Select(ParseCondition)
.Where(x => x != null)
.ToList();
}
private static CylinderActionConditionDefinition ParseCondition(string raw)
{
if (string.IsNullOrWhiteSpace(raw))
{
return null;
}
var parts = raw.Split(new[] { ':' }, 3);
if (parts.Length < 2)
{
return null;
}
var pointReference = parts[1].Trim();
if (string.IsNullOrWhiteSpace(pointReference))
{
return null;
}
CylinderConditionType conditionType;
switch ((parts[0] ?? string.Empty).Trim().ToUpperInvariant())
{
case "POINTON":
case "ON":
conditionType = CylinderConditionType.PointOn;
break;
case "POINTOFF":
case "OFF":
conditionType = CylinderConditionType.PointOff;
break;
default:
return null;
}
return new CylinderActionConditionDefinition
{
ConditionType = conditionType,
PointReference = pointReference,
Message = parts.Length > 2 ? parts[2].Trim() : string.Empty
};
}
private static string ResolveConfigPath()
{
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
var localPath = Path.Combine(baseDir, "Configuration", CylinderConfigFileName);
var sourcePath = ResolveRootConfigPath(baseDir);
if (TrySyncConfigToLocal(sourcePath, localPath))
{
return localPath;
}
return File.Exists(localPath) ? localPath : sourcePath;
}
private static string ResolveRootConfigPath(string baseDir)
{
var current = new DirectoryInfo(baseDir);
while (current != null)
{
var rootConfigDir = Path.Combine(current.FullName, "Configuration");
if (Directory.Exists(rootConfigDir) && Directory.Exists(Path.Combine(current.FullName, "MainShell")))
{
var path = Path.Combine(rootConfigDir, CylinderConfigFileName);
if (File.Exists(path))
{
return path;
}
}
current = current.Parent;
}
return null;
}
private static bool TrySyncConfigToLocal(string sourcePath, string localPath)
{
if (string.IsNullOrWhiteSpace(sourcePath) || !File.Exists(sourcePath))
{
return false;
}
if (!string.Equals(Path.GetFullPath(sourcePath), Path.GetFullPath(localPath), StringComparison.OrdinalIgnoreCase))
{
var localDir = Path.GetDirectoryName(localPath);
if (!string.IsNullOrWhiteSpace(localDir))
{
Directory.CreateDirectory(localDir);
}
if (!File.Exists(localPath) || File.GetLastWriteTimeUtc(localPath) < File.GetLastWriteTimeUtc(sourcePath))
{
File.Copy(sourcePath, localPath, true);
}
}
return true;
}
}
}

View File

@@ -0,0 +1,19 @@
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.DeviceMaintance.Model
{
public class DeviceMaintanceBaseViewModel : Screen
{
public event Action<bool> UiStateChangedEvent = null;
protected void OnUiStateChanged(bool uiStateChanged)
{
UiStateChangedEvent?.Invoke(uiStateChanged);
}
}
}

View File

@@ -0,0 +1,97 @@
using MainShell.Filewritable;
using Newtonsoft.Json;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MainShell.DeviceMaintance.Model
{
public class LaserCompensationSetting : MXJM.FileWritable.JsonFileWritableBase, INotifyPropertyChanged
{
private string _axisName = "Axis_Stage_Y3";
public string AxisName
{
get { return _axisName; }
set { SetAndNotify(ref _axisName, value); }
}
private double _startPos;
public double StartPos
{
get { return _startPos; }
set { SetAndNotify(ref _startPos, value); }
}
private double _steps = 1.0d;
public double Steps
{
get { return _steps; }
set { SetAndNotify(ref _steps, value); }
}
private int _stepsCount = 5;
public int StepsCount
{
get { return _stepsCount; }
set { SetAndNotify(ref _stepsCount, value); }
}
private double _jumpPos = 1.0d;
public double JumpPos
{
get { return _jumpPos; }
set { SetAndNotify(ref _jumpPos, value); }
}
private int _workCount = 1;
public int WorkCount
{
get { return _workCount; }
set { SetAndNotify(ref _workCount, value); }
}
private double _speed = 5.0d;
public double Speed
{
get { return _speed; }
set { SetAndNotify(ref _speed, value); }
}
private double _dwellTime = 0.5d;
public double DwellTime
{
get { return _dwellTime; }
set { SetAndNotify(ref _dwellTime, value); }
}
[JsonIgnore]
public override string Dir => Paths.CalibSettingPath;
[JsonIgnore]
public override string FileName => "LaserCompensationSetting.json";
public event PropertyChangedEventHandler PropertyChanged;
public void LoadFromFile()
{
Read();
}
public void SaveToFile()
{
Write();
}
protected bool SetAndNotify<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(field, value))
{
return false;
}
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
}
}

View File

@@ -0,0 +1,64 @@
using MainShell.Common;
using MainShell.Filewritable;
using MaxwellControl.Controls;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.DeviceMaintance.Model
{
public enum MaintanceType
{
针尖维护 = 1,
相机校验 = 2,
针印验证 = 3,
}
public class MaintanceRecord
{
/// <summary>
/// 设备编号
/// </summary>
public string DeviceId { get; set; }
/// <summary>
/// 设备名称
/// </summary>
public string DeviceName { get; set; }
/// <summary>
/// 维护时间
/// </summary>
public DateTime MaintanceTime { get; set; }
/// <summary>
/// 维护内容
/// </summary>
public MaintanceType MaintanceType { get; set; }
public void WriteToFile(string info)
{
try
{
if (!Directory.Exists(Paths.MaintanceRecordDir))
{
Directory.CreateDirectory(Paths.MaintanceRecordDir);
}
string filePath = Path.Combine(Paths.MaintanceRecordDir, $"{MaintanceType}_MaintanceRecord.txt");
using (var writer = new System.IO.StreamWriter(filePath, true))
{
if (!File.Exists(filePath))
{
writer.WriteLine("设备ID,日期,信息");
}
writer.WriteLine($"{DeviceId},{DateTime.Now}: {info}");
writer.Flush();
}
}
catch (Exception ex)
{
MessageBox.Show($"写入文件失败: {ex.Message}");
}
}
}
}

View File

@@ -0,0 +1,757 @@
using MwFramework.Controls.UIControl;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.DeviceMaintance.Model
{
[Export(typeof(IParameter))]
public class NeedleCalibrationSetting : ParameterBase
{
public NeedleZCalibrationItem NeedleZCalibrationItem { get; set; } = new NeedleZCalibrationItem();
public NeedleXYCalibrationItem NeedleXYCalibrationItem { get; set; } = new NeedleXYCalibrationItem();
public NeedleXYCalibrationItemNew NeedleXYCalibrationItemNew { get; set; } = new NeedleXYCalibrationItemNew();
public override void Copy(IParameter source)
{
NeedleCalibrationSetting setting = source as NeedleCalibrationSetting;
if (setting != null)
{
ReflectionExtension.Copy<NeedleZCalibrationItem>(NeedleZCalibrationItem, setting.NeedleZCalibrationItem);
ReflectionExtension.Copy<NeedleXYCalibrationItem>(NeedleXYCalibrationItem, setting.NeedleXYCalibrationItem);
ReflectionExtension.Copy<NeedleXYCalibrationItemNew>(NeedleXYCalibrationItemNew, setting.NeedleXYCalibrationItemNew);
}
}
}
public class NeedleZCalibrationItem : PropertyChangedBase, IParameterItem
{
private double _supportPinPositionX;
[WatchValue("针尖对准对刀仪X1位置")]
public double SupportPinPositionX
{
get { return _supportPinPositionX; }
set { if (_supportPinPositionX != value) { _supportPinPositionX = value; OnPropertyChanged(nameof(SupportPinPositionX)); } }
}
private double _supportPinPositionY;
[WatchValue("针尖对准对刀仪Y1位置")]
public double SupportPinPositionY
{
get { return _supportPinPositionY; }
set { if (_supportPinPositionY != value) { _supportPinPositionY = value; OnPropertyChanged(nameof(SupportPinPositionY)); } }
}
private double _knifeOffset;
[WatchValue("对刀仪偏差")]
/// <summary>
/// 对刀仪偏差
/// </summary>
public double KnifeOffset
{
get { return _knifeOffset; }
set { if (_knifeOffset != value) { _knifeOffset = value; OnPropertyChanged(nameof(KnifeOffset)); } }
}
private double _cZHeight = -0.725;
[WatchValue("SZ高度")]
public double CZHeight
{
get { return _cZHeight; }
set { if (_cZHeight != value) { _cZHeight = value; OnPropertyChanged(nameof(CZHeight)); } }
}
private double _stageHeight;
[WatchValue("载台高度")]
public double StageHeight
{
get { return _stageHeight; }
set { if (_stageHeight != value) { _stageHeight = value; OnPropertyChanged(nameof(StageHeight)); } }
}
private double _gantry2AvoidPositionX = 10;
[WatchValue("对刀时龙门X2的避让位置")]
public double Gantry2AvoidPositionX
{
get { return _gantry2AvoidPositionX; }
set { if (_gantry2AvoidPositionX != value) { _gantry2AvoidPositionX = value; OnPropertyChanged(nameof(Gantry2AvoidPositionX)); } }
}
private double _gantry2AvoidPositionY = 200;
[WatchValue("对刀时龙门Y2的避让位置")]
public double Gantry2AvoidPositionY
{
get { return _gantry2AvoidPositionY; }
set { if (_gantry2AvoidPositionY != value) { _gantry2AvoidPositionY = value; OnPropertyChanged(nameof(Gantry2AvoidPositionY)); } }
}
private double _z1StartPosition;
[WatchValue("Z1开始位置")]
public double Z1StartPosition
{
get { return _z1StartPosition; }
set { if (_z1StartPosition != value) { _z1StartPosition = value; OnPropertyChanged(nameof(Z1StartPosition)); } }
}
private double _z1Speed;
[WatchValue("Z1速度")]
public double Z1Speed
{
get { return _z1Speed; }
set { if (_z1Speed != value) { _z1Speed = value; OnPropertyChanged(nameof(Z1Speed)); } }
}
private double _z1DropMaxPosition;
[WatchValue("Z1下降距离")]
public double Z1DropMaxPosition
{
get { return _z1DropMaxPosition; }
set { if (_z1DropMaxPosition != value) { _z1DropMaxPosition = value; OnPropertyChanged(nameof(Z1DropMaxPosition)); } }
}
private int _needleTouchCount = 1;
[WatchValue("对刀次数")]
public int NeedleTouchCount
{
get { return _needleTouchCount; }
set { if (_needleTouchCount != value) { _needleTouchCount = value; OnPropertyChanged(nameof(NeedleTouchCount)); } }
}
private double _needleTouchHeight = 14;
/// <summary>
/// Z1接触对刀仪高度
/// </summary>
[WatchValue("Z1接触对刀仪高度")]
public double NeedleTouchHeight
{
get { return _needleTouchHeight; }
set { if (_needleTouchHeight != value) { _needleTouchHeight = value; OnPropertyChanged(nameof(NeedleTouchHeight)); } }
}
private double _needleTouchHeightBase;
/// <summary>
/// 基准值
/// </summary>
[WatchValue("基准值")]
public double NeedleTouchHeightBase
{
get { return _needleTouchHeightBase; }
set { if (_needleTouchHeightBase != value) { _needleTouchHeightBase = value; OnPropertyChanged(nameof(NeedleTouchHeightBase)); } }
}
private double _currentNeedleTouchHeight;
[WatchValue("当前使用对刀高度")]
/// <summary>
/// 当前使用对刀高度
/// </summary>
public double CurrentNeedleTouchHeight
{
get { return _currentNeedleTouchHeight; }
set { if (_currentNeedleTouchHeight != value) { _currentNeedleTouchHeight = value; OnPropertyChanged(nameof(CurrentNeedleTouchHeight)); } }
}
private double _needleTouchOffset;
[WatchValue("对刀与基准偏差")]
/// <summary>
/// 对刀与基准偏差
/// </summary>
public double NeedleTouchOffset
{
get { return _needleTouchOffset; }
set { if (_needleTouchOffset != value) { _needleTouchOffset = value; OnPropertyChanged(nameof(NeedleTouchOffset)); } }
}
private double _needleTouchHeightAve;
[WatchValue("Z1对刀平均高度")]
/// <summary>
/// Z1对刀平均高度
/// </summary>
public double NeedleTouchHeightAve
{
get { return _needleTouchHeightAve; }
set { if (_needleTouchHeightAve != value) { _needleTouchHeightAve = value; OnPropertyChanged(nameof(NeedleTouchHeightAve)); } }
}
private ObservableCollection<NeedleTouchRecord> _needleTouchRecords = new ObservableCollection<NeedleTouchRecord>();
public ObservableCollection<NeedleTouchRecord> NeedleTouchRecords
{
get { return _needleTouchRecords; }
set
{
_needleTouchRecords = value;
OnPropertyChanged(nameof(NeedleTouchRecords));
}
}
private double _laserToNeedleOffset = 0;
public double LaserToNeedleOffset
{
get { return _laserToNeedleOffset; }
set { if (_laserToNeedleOffset != value) { _laserToNeedleOffset = value; OnPropertyChanged(nameof(LaserToNeedleOffset)); } }
}
private double _needleCameraHeightOffset;
public double NeedleCameraHeightOffset
{
get { return _needleCameraHeightOffset; }
set { if (_needleCameraHeightOffset != value) { _needleCameraHeightOffset = value; OnPropertyChanged(nameof(NeedleCameraHeightOffset)); } }
}
public IParameterItem Clone()
{
NeedleZCalibrationItem clone = new NeedleZCalibrationItem();
clone.CurrentNeedleTouchHeight = _needleCameraHeightOffset;
clone.CZHeight = CZHeight;
clone.Gantry2AvoidPositionX = Gantry2AvoidPositionX;
clone.Gantry2AvoidPositionY = Gantry2AvoidPositionY;
clone.LaserToNeedleOffset = LaserToNeedleOffset;
clone.NeedleCameraHeightOffset = NeedleCameraHeightOffset;
clone.NeedleTouchCount = NeedleTouchCount;
clone.NeedleTouchHeight = NeedleTouchHeight;
clone.NeedleTouchHeightAve = NeedleTouchHeightAve;
clone.NeedleTouchHeightBase = NeedleTouchHeightBase;
clone.NeedleTouchOffset = NeedleTouchOffset;
clone.StageHeight = StageHeight;
clone.SupportPinPositionX = SupportPinPositionX;
clone.SupportPinPositionY = SupportPinPositionY;
clone.Z1DropMaxPosition = Z1DropMaxPosition;
clone.Z1Speed = Z1Speed;
clone.Z1StartPosition = Z1StartPosition;
if (this.NeedleTouchRecords != null)
{
foreach (var item in this.NeedleTouchRecords)
{
clone.NeedleTouchRecords.Add(item.Clone() as NeedleTouchRecord);
}
}
return clone;
}
}
public class NeedleXYCalibrationItem : PropertyChangedBase, IParameterItem
{
private double _needleX;
[WatchValue("设置—刺晶头X")]
public double NeedleX
{
get { return _needleX; }
set { if (_needleX != value) { _needleX = value; OnPropertyChanged(nameof(NeedleX)); } }
}
private double _needleY;
[WatchValue("设置—刺晶头Y")]
public double NeedleY
{
get { return _needleY; }
set { if (_needleY != value) { _needleY = value; OnPropertyChanged(nameof(NeedleY)); } }
}
private double _cameraX;
[WatchValue("设置—相机X")]
public double CameraX
{
get { return _cameraX; }
set { if (_cameraX != value) { _cameraX = value; OnPropertyChanged(nameof(CameraX)); } }
}
private double _cameraY;
[WatchValue("设置—相机Y")]
public double CameraY
{
get { return _cameraY; }
set { if (_cameraY != value) { _cameraY = value; OnPropertyChanged(nameof(CameraY)); } }
}
private double _needleZ1 = 0.0;
[WatchValue("设置—刺晶头Z1")]
public double NeedleZ1
{
get { return _needleZ1; }
set { if (_needleZ1 != value) { _needleZ1 = value; OnPropertyChanged(nameof(NeedleZ1)); } }
}
private double _offsetX;
[WatchValue("X方向偏差")]
public double OffsetX
{
get { return _offsetX; }
set { if (_offsetX != value) { _offsetX = value; OnPropertyChanged(nameof(OffsetX)); } }
}
private double _offsetY;
[WatchValue("Y方向偏差")]
public double OffsetY
{
get { return _offsetY; }
set { if (_offsetY != value) { _offsetY = value; OnPropertyChanged(nameof(OffsetY)); } }
}
private double _initialOffsetX;
/// <summary>
/// 针和相机之间的X初始偏差,不加补偿
/// </summary>
[WatchValue("X方向初始偏差")]
public double InitialOffsetX
{
get { return _initialOffsetX; }
set { if (_initialOffsetX != value) { _initialOffsetX = value; OnPropertyChanged(nameof(InitialOffsetX)); } }
}
private double _initialOffsetY;
/// <summary>
/// 针和相机之间的Y初始偏差,不加补偿
/// </summary>
[WatchValue("Y方向初始偏差")]
public double InitialOffsetY
{
get { return _initialOffsetY; }
set { if (_initialOffsetY != value) { _initialOffsetY = value; OnPropertyChanged(nameof(InitialOffsetY)); } }
}
private double _compensateX;
/// <summary>
/// 针和相机之间的X方向偏差补偿
/// </summary>
[WatchValue("X方向补偿")]
public double CompensateX
{
get { return _compensateX; }
set { if (_compensateX != value) { _compensateX = value; OnPropertyChanged(nameof(CompensateX)); } }
}
private double _compensateY;
/// <summary>
/// 针和相机之间的Y方向偏差补偿
/// </summary>
[WatchValue("Y方向补偿")]
public double CompensateY
{
get { return _compensateY; }
set { if (_compensateY != value) { _compensateY = value; OnPropertyChanged(nameof(CompensateY)); } }
}
private double _offsetXMax;
[WatchValue("偏差限制—X偏差最大值")]
public double OffsetXMax
{
get { return _offsetXMax; }
set { if (_offsetXMax != value) { _offsetXMax = value; OnPropertyChanged(nameof(OffsetXMax)); } }
}
private double _offsetXMin;
[WatchValue("偏差限制—X偏差最小值")]
public double OffsetXMin
{
get { return _offsetXMin; }
set { if (_offsetXMin != value) { _offsetXMin = value; OnPropertyChanged(nameof(OffsetXMin)); } }
}
private double _offsetYMax;
[WatchValue("偏差限制—Y偏差最大值")]
public double OffsetYMax
{
get { return _offsetYMax; }
set { if (_offsetYMax != value) { _offsetYMax = value; OnPropertyChanged(nameof(OffsetYMax)); } }
}
private double _offsetYMin;
[WatchValue("偏差限制—Y偏差最小值")]
public double OffsetYMin
{
get { return _offsetYMin; }
set { if (_offsetYMin != value) { _offsetYMin = value; OnPropertyChanged(nameof(OffsetYMin)); } }
}
/// <summary>
/// 扎铜箔时龙门X2避让位置
/// </summary>
private double _x2AvoidPosition;
[WatchValue("扎铜箔时龙门X2避让位置")]
public double X2AvoidPosition
{
get { return _x2AvoidPosition; }
set { if (_x2AvoidPosition != value) { _x2AvoidPosition = value; OnPropertyChanged(nameof(X2AvoidPosition)); } }
}
/// <summary>
/// 扎铜箔时龙门Y2避让位置
/// </summary>
private double _y2AvoidPosition;
[WatchValue("扎铜箔时龙门Y2避让位置")]
public double Y2AvoidPosition
{
get { return _y2AvoidPosition; }
set { if (_y2AvoidPosition != value) { _y2AvoidPosition = value; OnPropertyChanged(nameof(Y2AvoidPosition)); } }
}
/// <summary>
/// 扎铜箔时Stage的位置
/// </summary>
private double _stagePosition;
[WatchValue("扎铜箔时Stage的位置")]
public double StagePosition
{
get { return _stagePosition; }
set { if (_stagePosition != value) { _stagePosition = value; OnPropertyChanged(nameof(StagePosition)); } }
}
public IParameterItem Clone()
{
NeedleXYCalibrationItem clone = new NeedleXYCalibrationItem();
clone.NeedleX = this.NeedleX;
clone.NeedleY = this.NeedleY;
clone.CameraX = this.CameraY;
clone.CameraY = this.CameraY;
clone.OffsetX = this.OffsetX;
clone.OffsetY = this.OffsetY;
clone.OffsetXMax = this.OffsetXMax;
clone.OffsetXMin = this.OffsetXMin;
clone.OffsetYMax = this.OffsetYMax;
clone.OffsetYMin = this.OffsetYMin;
return this.MemberwiseClone() as IParameterItem;
}
}
public class NeedleXYCalibrationItemNew : PropertyChangedBase, IParameterItem
{
//private VisionParData _needleVisionParData;
//public VisionParData NeedleVisionParData
//{
// get { return _needleVisionParData ?? new VisionParData() { Exposure = 2000 }; }
// set { SetAndNotify(ref _needleVisionParData, value); }
//}
private double _x1Calib;
/// <summary>
/// 相机标定位X1
/// </summary>
[WatchValue("相机标定位X1")]
public double X1Calib
{
get { return _x1Calib; }
set { if (_x1Calib != value) { _x1Calib = value; OnPropertyChanged(nameof(X1Calib)); } }
}
private double _x1MechanicalOffset = 0.0;
/// <summary>
/// 机械X1Offset
/// </summary>
[WatchValue("机械X1Offset")]
public double X1MechanicalOffset
{
get { return _x1MechanicalOffset; }
set { if (_x1MechanicalOffset != value) { _x1MechanicalOffset = value; OnPropertyChanged(nameof(X1MechanicalOffset)); } }
}
private double _wSCalibX2;
/// <summary>
/// 方片标定位X2
/// </summary>
[WatchValue("方片标定位X2")]
public double WSCalibX2
{
get { return _wSCalibX2; }
set { if (_wSCalibX2 != value) { _wSCalibX2 = value; OnPropertyChanged(nameof(WSCalibX2)); } }
}
private double _wSCalibY2;
/// <summary>
/// 方片标定位Y2
/// </summary>
[WatchValue("方片标定位Y2")]
public double WSCalibY2
{
get { return _wSCalibY2; }
set { if (_wSCalibY2 != value) { _wSCalibY2 = value; OnPropertyChanged(nameof(WSCalibY2)); } }
}
private double _needleX1;
[WatchValue("刺晶头X1")]
public double NeedleX1
{
get { return _needleX1; }
set { if (_needleX1 != value) { _needleX1 = value; OnPropertyChanged(nameof(NeedleX1)); } }
}
private double _needleZ1 = 0.0;
[WatchValue("刺晶头Z1")]
public double NeedleZ1
{
get { return _needleZ1; }
set { if (_needleZ1 != value) { _needleZ1 = value; OnPropertyChanged(nameof(NeedleZ1)); } }
}
private int _z1WorkTime = 0;
/// <summary>
/// Z1保持下扎时长
/// </summary>
[WatchValue("Z1保持下扎时长")]
public int Z1WorkTime
{
get { return _z1WorkTime; }
set { if (_z1WorkTime != value) { _z1WorkTime = value; OnPropertyChanged(nameof(Z1WorkTime)); } }
}
private double _offsetX;
/// <summary>
/// 针和相机之间的X初始偏差,不加补偿
/// </summary>
[WatchValue("X方向偏差")]
public double OffsetX
{
get { return _offsetX; }
set { if (_offsetX != value) { _offsetX = value; OnPropertyChanged(nameof(OffsetX)); } }
}
private double _offsetY;
/// <summary>
/// 针和相机之间的Y初始偏差,不加补偿
/// </summary>
[WatchValue("Y方向偏差")]
public double OffsetY
{
get { return _offsetY; }
set { if (_offsetY != value) { _offsetY = value; OnPropertyChanged(nameof(OffsetY)); } }
}
private double _compensateX;
/// <summary>
/// 针和相机之间的X方向偏差补偿
/// </summary>
[WatchValue("X方向补偿")]
public double CompensateX
{
get { return _compensateX; }
set { if (_compensateX != value) { _compensateX = value; OnPropertyChanged(nameof(CompensateX)); } }
}
private double _compensateY;
/// <summary>
/// 针和相机之间的Y方向偏差补偿
/// </summary>
[WatchValue("Y方向补偿")]
public double CompensateY
{
get { return _compensateY; }
set { if (_compensateY != value) { _compensateY = value; OnPropertyChanged(nameof(CompensateY)); } }
}
private double _offsetXMax;
[WatchValue("偏差限制—X偏差最大值")]
public double OffsetXMax
{
get { return _offsetXMax; }
set { if (_offsetXMax != value) { _offsetXMax = value; OnPropertyChanged(nameof(OffsetXMax)); } }
}
private double _offsetXMin;
[WatchValue("偏差限制—X偏差最小值")]
public double OffsetXMin
{
get { return _offsetXMin; }
set { if (_offsetXMin != value) { _offsetXMin = value; OnPropertyChanged(nameof(OffsetXMin)); } }
}
private double _offsetYMax;
[WatchValue("偏差限制—Y偏差最大值")]
public double OffsetYMax
{
get { return _offsetYMax; }
set { if (_offsetYMax != value) { _offsetYMax = value; OnPropertyChanged(nameof(OffsetYMax)); } }
}
private double _offsetYMin;
[WatchValue("偏差限制—Y偏差最小值")]
public double OffsetYMin
{
get { return _offsetYMin; }
set { if (_offsetYMin != value) { _offsetYMin = value; OnPropertyChanged(nameof(OffsetYMin)); } }
}
private double _wSApproachX2;
/// <summary>
/// 方片逼近位X2
/// </summary>
[WatchValue("方片逼近位X2")]
public double WSApproachX2
{
get { return _wSApproachX2; }
set { if (_wSApproachX2 != value) { _wSApproachX2 = value; OnPropertyChanged(nameof(WSApproachX2)); } }
}
private double _wSApproachY2;
/// <summary>
/// 方片逼近位Y2
/// </summary>
[WatchValue("方片逼近位Y2")]
public double WSApproachY2
{
get { return _wSApproachY2; }
set { if (_wSApproachY2 != value) { _wSApproachY2 = value; OnPropertyChanged(nameof(WSApproachY2)); } }
}
private double _stampThreRadius;
/// <summary>
/// 针印理论半径
/// </summary>
[WatchValue("针印理论半径")]
public double StampThreRadius
{
get { return _stampThreRadius; }
set { if (_stampThreRadius != value) { _stampThreRadius = value; OnPropertyChanged(nameof(StampThreRadius)); } }
}
private double _stampThreErrorRange;
/// <summary>
/// 针印误差范围
/// </summary>
[WatchValue("针印误差范围")]
public double StampThreErrorRange
{
get { return _stampThreErrorRange; }
set { if (_stampThreErrorRange != value) { _stampThreErrorRange = value; OnPropertyChanged(nameof(StampThreErrorRange)); } }
}
private double _stampMinCircularity;
/// <summary>
/// 针印最小圆度
/// </summary>
[WatchValue("针印最小圆度")]
public double StampMinCircularity
{
get { return _stampMinCircularity; }
set { if (_stampMinCircularity != value) { _stampMinCircularity = value; OnPropertyChanged(nameof(StampMinCircularity)); } }
}
private double _stampPolarity;
/// <summary>
/// 针印颜色,0黑1白
/// </summary>
[WatchValue("针印理论半径")]
public double StampPolarity
{
get { return _stampPolarity; }
set { if (_stampPolarity != value) { _stampPolarity = value; OnPropertyChanged(nameof(StampPolarity)); } }
}
private bool _isBlack = false;
public bool IsBlack
{
get
{
return _isBlack;
}
set
{
_isBlack = value;
StampPolarityUpdate();
OnPropertyChanged(nameof(IsBlack));
}
}
public void StampPolarityUpdate()
{
if (IsBlack)
{
StampPolarity = 0;
}
else
{
StampPolarity = 1;
}
}
public IParameterItem Clone()
{
NeedleXYCalibrationItemNew clone = new NeedleXYCalibrationItemNew();
clone.NeedleX1 = this.NeedleX1;
clone.NeedleZ1 = this.NeedleZ1;
clone.WSApproachX2 = this.WSApproachX2;
clone.WSApproachY2 = this.WSApproachY2;
clone.Z1WorkTime = Z1WorkTime;
clone.X1MechanicalOffset = X1MechanicalOffset;
clone.X1Calib = X1Calib;
clone.WSCalibX2 = WSCalibX2;
clone.WSCalibY2 = WSCalibY2;
clone.CompensateX = CompensateX;
clone.CompensateY = CompensateY;
clone.OffsetX = this.OffsetX;
clone.OffsetY = this.OffsetY;
clone.OffsetXMax = this.OffsetXMax;
clone.OffsetXMin = this.OffsetXMin;
clone.OffsetYMax = this.OffsetYMax;
clone.OffsetYMin = this.OffsetYMin;
clone.IsBlack = this.IsBlack;
clone.StampMinCircularity = this.StampMinCircularity;
clone.StampPolarity = this.StampPolarity;
clone.StampThreErrorRange = this.StampThreErrorRange;
clone.StampThreRadius = this.StampThreRadius;
//clone.NeedleVisionParData.Exposure = this.NeedleVisionParData.Exposure;
//clone.NeedleVisionParData.Gain = this.NeedleVisionParData.Gain;
//clone.NeedleVisionParData.LightSourceBack = this.NeedleVisionParData.LightSourceBack;
//clone.NeedleVisionParData.LightSourcePointBlue = this.NeedleVisionParData.LightSourcePointBlue;
//clone.NeedleVisionParData.LightSourcePointRed = this.NeedleVisionParData.LightSourcePointRed;
//clone.NeedleVisionParData.LightSourceRingBlue = this.NeedleVisionParData.LightSourceRingBlue;
//clone.NeedleVisionParData.LightSourceRingRed = this.NeedleVisionParData.LightSourceRingRed;
//clone.NeedleVisionParData.Z2 = this.NeedleVisionParData.Z2;
return this.MemberwiseClone() as IParameterItem;
}
}
public class NeedleTouchRecord : PropertyChangedBase, IParameterItem
{
private int _CurrentCount;
/// <summary>
/// 当前次数
/// </summary>
public int CurrentCount
{
get { return _CurrentCount; }
set
{ SetAndNotify(ref _CurrentCount, value); }
}
private double _Height;
/// <summary>
/// 高度
/// </summary>
public double Height
{
get { return _Height; }
set
{ SetAndNotify(ref _Height, value); }
}
public NeedleTouchRecord()
{
}
public NeedleTouchRecord(int count, double height)
{
CurrentCount = count;
Height = height;
}
public IParameterItem Clone()
{
return this.MemberwiseClone() as IParameterItem;
}
}
}

View File

@@ -0,0 +1,104 @@
using MainShell.Models;
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.DeviceMaintance.Model
{
[Export(typeof(IParameter))]
public class NeedlePrintCalibrateSetting : ParameterBase
{
public NeedlePrintCalibrateParameter NeedlePrintCalibrateParameter { get; set; } = new NeedlePrintCalibrateParameter();
public NeedlePrintCalibrateSetting() { }
public override void Copy(IParameter source)
{
if (source is NeedlePrintCalibrateSetting setting)
{
ReflectionExtension.Copy(NeedlePrintCalibrateParameter, setting.NeedlePrintCalibrateParameter);
}
}
}
public class NeedlePrintCalibrateParameter : PropertyChangedBase, IParameterItem
{
private MPoint _startPoint = new MPoint();
public MPoint StartPoint
{
get { return _startPoint; }
set { SetAndNotify(ref _startPoint, value); }
}
private MPoint _endPoint = new MPoint();
public MPoint EndPoint
{
get { return _endPoint; }
set { SetAndNotify(ref _endPoint, value); }
}
private MPoint _avoidancePoint = new MPoint();
public MPoint AvoidancePoint
{
get { return _avoidancePoint; }
set { SetAndNotify(ref _avoidancePoint, value); }
}
private double _z1WorkHeight;
public double Z1WorkHeight
{
get { return _z1WorkHeight; }
set { SetAndNotify(ref _z1WorkHeight, value); }
}
private double _z2WorkHeight;
public double Z2WorkHeight
{
get { return _z2WorkHeight; }
set { SetAndNotify(ref _z2WorkHeight, value); }
}
private double _xPitch;
public double Xpitch
{
get { return _xPitch; }
set { SetAndNotify(ref _xPitch, value); }
}
private double _yPitch;
public double Ypitch
{
get { return _yPitch; }
set { SetAndNotify(ref _yPitch, value); }
}
private int _usedNum;
public int UsedNum
{
get { return _usedNum; }
set { SetAndNotify(ref _usedNum, value); }
}
private int _sleepTime = 1000;
public int SleepTime
{
get { return _sleepTime; }
set { SetAndNotify(ref _sleepTime, value); }
}
public IParameterItem Clone()
{
return MemberwiseClone() as NeedlePrintCalibrateParameter;
}
}
}

View File

@@ -0,0 +1,109 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.CameraAccuracyCalibrationView"
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.DeviceMaintance.View"
xmlns:mwControls="http://www.maxwell-gp.com/"
xmlns:customControl="clr-namespace:MainShell.Resources.CustomControl"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="700">
<UserControl.Resources>
<Style TargetType="GroupBox" BasedOn="{StaticResource GroupStepControl}"/>
<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Height" Value="35"/>
</Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" >
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="3"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Width" Value="90"/>
</Style>
</UserControl.Resources>
<Grid mwControls:ParameterAttach.DelayAcceptValue ="{Binding ParameterHelper}">
<StackPanel>
<GroupBox IsEnabled="{Binding UiEnable}" Header="【第一步】针尖高度设置">
<customControl:AxisMoveControl LableContent1="Z1(mm):"
Value1="{Binding QuickNeedlePrintParameter.Z1InkHeight}"
IsAllShow="False"
ReadCommand="{mwControls:Action TeachZ1Pos}"
IsReadOnly="False"
MoveCommand="{mwControls:Action MoveToZ1Pos}"/>
</GroupBox>
<GroupBox IsEnabled="{Binding UiEnable}" Header="【第二步】WS避让位">
<GroupBox.Content>
<customControl:AxisMoveControl LableContent1="X2(mm):"
Value1="{Binding QuickNeedlePrintParameter.AvoidancePoint.PointX}"
LableContent2="Y2(mm):"
Value2="{Binding QuickNeedlePrintParameter.AvoidancePoint.PointY}"
ReadCommand="{mwControls:Action TeachWSPos}"
MoveCommand="{mwControls:Action MoveToWsPos}"
IsReadOnly="True"/>
<!--<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Label VerticalAlignment="Center" Content="X2:"/>
<mwControls:NumberBox Value="{Binding QuickNeedlePrintParameter.AvoidancePoint.PointX}" IsReadOnly="True" Width="80" Margin="4,0" />
<Label VerticalAlignment="Center" Content="Y2:"/>
<mwControls:NumberBox Value="{Binding QuickNeedlePrintParameter.AvoidancePoint.PointY}" IsReadOnly="True" Width="80" Margin="4,0" />
<Button Margin="2,0" Click="{mwControls:Action TeachWSPos}" Content="示教"/>
<Button Margin="2,0" Click="{mwControls:Action MoveToWsPos}" Content="移动"/>
</StackPanel>-->
</GroupBox.Content>
</GroupBox>
<GroupBox IsEnabled="{Binding UiEnable}" Margin="2" Header="【第三步】墨水位置">
<GroupBox.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<customControl:AxisMoveControl LableContent1="X2(mm):"
Value1="{Binding QuickNeedlePrintParameter.InkPos.PointX}"
LableContent2="Y2(mm):"
Value2="{Binding QuickNeedlePrintParameter.InkPos.PointY}"
ReadCommand="{mwControls:Action TeachInkPos}"
MoveCommand="{mwControls:Action MoveToInkPos}"
IsReadOnly="True"/>
<Button Margin="2,0" HorizontalAlignment="Right" Click="{mwControls:Action InkSmearing}" Content="涂墨"/>
</Grid>
</GroupBox.Content>
</GroupBox>
<GroupBox Header="【第四步】快打针印参数设置">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label VerticalAlignment="Center" HorizontalAlignment="Left" Content="打件颗数:"/>
<mwControls:IntNumberBox Value="{Binding BondingNumber}" IsReadOnly="True" Width="75" Margin="4,0" />
<Button IsEnabled="{Binding UiEnable}" Margin="2,0" Click="{mwControls:Action PositiveBonding}" Content="快打正向"/>
<Button IsEnabled="{Binding UiEnable}" Margin="2,0" Click="{mwControls:Action NegativeBonding}" Content="快打反向"/>
<Button IsEnabled="{Binding UiEnable}" Margin="2,0" Click="{mwControls:Action SlowBonding}" Content="补打"/>
<Button Margin="2,0" Content="停止打件" Click="{mwControls:Action StopBonding}"/>
</StackPanel>
<Label Content="正向针印查看:" HorizontalAlignment="Left"/>
<StackPanel IsEnabled="{Binding UiEnable}" HorizontalAlignment="Left" Orientation="Horizontal">
<Button Margin="2,0" IsEnabled="{Binding CanLastPositiveIndex}" Click="{mwControls:Action LastPositiveIndex}" Content="上一个"/>
<Button Margin="2,0" IsEnabled="{Binding CanNextPositiveIndex}" Click="{mwControls:Action NextPositiveIndex}" Content="下一个"/>
</StackPanel>
<Label Content="反向针印查看:" HorizontalAlignment="Left"/>
<StackPanel IsEnabled="{Binding UiEnable}" HorizontalAlignment="Left" Orientation="Horizontal">
<Button Margin="2,0" IsEnabled="{Binding CanLastNegativeIndex}" Click="{mwControls:Action LastNegativeIndex}" Content="上一个"/>
<Button Margin="2,0" IsEnabled="{Binding CanNextNegativeIndex}" Click="{mwControls:Action NextNegativeIndex}" Content="下一个"/>
</StackPanel>
<Label Content="补打针印查看:" HorizontalAlignment="Left"/>
<StackPanel IsEnabled="{Binding UiEnable}" HorizontalAlignment="Left" Orientation="Horizontal">
<Button Margin="2,0" IsEnabled="{Binding CanLastSlowIndex}" Click="{mwControls:Action LastSlowIndex}" Content="上一个"/>
<Button Margin="2,0" IsEnabled="{Binding CanNextSlowIndex}" Click="{mwControls:Action NextSlowIndex}" Content="下一个"/>
</StackPanel>
<Button IsEnabled="{Binding UiEnable}" HorizontalAlignment="Right" Click="{mwControls:Action SaveParameter}" Margin="10,2" Content="保存"/>
</StackPanel>
</GroupBox>
</StackPanel>
</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.DeviceMaintance.View
{
/// <summary>
/// CameraAccuracyCalibrationView.xaml 的交互逻辑
/// </summary>
public partial class CameraAccuracyCalibrationView : UserControl
{
public CameraAccuracyCalibrationView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,176 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.CylinderView"
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.DeviceMaintance.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<Style x:Key="CylinderCardStyle" TargetType="Border">
<Setter Property="Background" Value="#F8FAFD"/>
<Setter Property="BorderBrush" Value="#D4DCE8"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Padding" Value="14"/>
<Setter Property="Margin" Value="0,0,0,12"/>
</Style>
<Style x:Key="CylinderDotStyle" TargetType="Ellipse">
<Setter Property="Width" Value="10"/>
<Setter Property="Height" Value="10"/>
<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="CylinderActionButtonStyle" TargetType="Button">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Padding" Value="12,6"/>
<Setter Property="MinWidth" Value="90"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Margin" Value="0,0,8,0"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="18"
Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid Background="#EDF1F7">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Margin="12,12,12,8">
<TextBlock Text="CYLINDER CONTROL / 气缸控制" FontSize="20" FontWeight="Bold" Foreground="#0F3B7A"/>
<Border Background="#EFF6FF" BorderBrush="#BFDBFE" BorderThickness="1" CornerRadius="6" Padding="10" Margin="0,8,0,0">
<StackPanel Orientation="Horizontal">
<TextBlock Text="最近操作:" Foreground="#475569" Margin="0,0,8,0"/>
<TextBlock Text="{Binding PageState.LastActionTime, StringFormat={}{0:HH:mm:ss}}" Foreground="#0F172A" FontWeight="Bold" Margin="0,0,12,0"/>
<TextBlock Text="{Binding PageState.LastActionMessage}" Foreground="#1D4ED8" FontWeight="SemiBold" TextWrapping="Wrap"/>
</StackPanel>
</Border>
</StackPanel>
<ScrollViewer Grid.Row="1" Margin="12,0,12,12" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding PageState.Cylinders}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource CylinderCardStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock Text="{Binding Name}" FontSize="18" FontWeight="Bold" Foreground="#0F3B7A"/>
<TextBlock Text="{Binding Description}" Foreground="#64748B" Margin="0,4,0,0"/>
</StackPanel>
<Border Grid.Column="1" Background="#FEF3C7" CornerRadius="12" Padding="10,4" VerticalAlignment="Center">
<TextBlock Text="{Binding ControlTypeText}" Foreground="#92400E" FontWeight="Bold"/>
</Border>
</Grid>
<Grid Grid.Row="1" Margin="0,12,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Margin="0,0,16,0">
<TextBlock Text="控制" Foreground="#1D4ED8" FontWeight="Bold" Margin="0,0,0,8"/>
<StackPanel Orientation="Horizontal">
<Button Content="伸出"
Command="{Binding ExtendCommand}"
Background="#2563EB"
BorderBrush="#2563EB"
Style="{StaticResource CylinderActionButtonStyle}"/>
<Button Content="缩回"
Command="{Binding RetractCommand}"
Background="#F97316"
BorderBrush="#F97316"
Style="{StaticResource CylinderActionButtonStyle}"/>
</StackPanel>
</StackPanel>
<StackPanel Grid.Column="1" Margin="0,0,16,0">
<TextBlock Text="输出信号" Foreground="#C2410C" FontWeight="Bold" Margin="0,0,0,8"/>
<ItemsControl ItemsSource="{Binding OutputSignals}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="#FFF7ED" BorderBrush="#FED7AA" BorderThickness="1" CornerRadius="4" Padding="8,6" Margin="0,0,0,6">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Ellipse Style="{StaticResource CylinderDotStyle}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<StackPanel Grid.Column="1">
<TextBlock Text="{Binding Name}" Foreground="#9A3412" FontWeight="SemiBold"/>
<TextBlock Text="{Binding IoNameText}" Foreground="#7C2D12" FontSize="11" TextWrapping="Wrap"/>
<TextBlock Text="{Binding LineNo, StringFormat=线标:{0}}" Foreground="#C2410C" FontSize="11"/>
</StackPanel>
<TextBlock Grid.Column="2" Text="{Binding StateText}" Foreground="#C2410C" FontWeight="Bold"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Text="反馈信号" Foreground="#166534" FontWeight="Bold" Margin="0,0,0,8"/>
<ItemsControl ItemsSource="{Binding FeedbackSignals}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="#F0FDF4" BorderBrush="#BBF7D0" BorderThickness="1" CornerRadius="4" Padding="8,6" Margin="0,0,0,6">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Ellipse Style="{StaticResource CylinderDotStyle}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<StackPanel Grid.Column="1">
<TextBlock Text="{Binding Name}" Foreground="#166534" FontWeight="SemiBold"/>
<TextBlock Text="{Binding IoNameText}" Foreground="#15803D" FontSize="11" TextWrapping="Wrap"/>
<TextBlock Text="{Binding LineNo, StringFormat=线标:{0}}" Foreground="#15803D" FontSize="11"/>
</StackPanel>
<TextBlock Grid.Column="2" Text="{Binding StateText}" Foreground="#166534" FontWeight="Bold"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</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.DeviceMaintance.View
{
/// <summary>
/// CylinderView.xaml 的交互逻辑
/// </summary>
public partial class CylinderView : UserControl
{
public CylinderView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,310 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.DeviceIoView"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="900">
<UserControl.Resources>
<Style x:Key="IoCardStyle" TargetType="Border">
<Setter Property="Background" Value="#F8FAFD"/>
<Setter Property="BorderBrush" Value="#D4DCE8"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Margin" Value="0,0,0,12"/>
<Setter Property="Padding" Value="14"/>
</Style>
<Style x:Key="StatusBadgeBorderStyle" TargetType="Border">
<Setter Property="CornerRadius" Value="12"/>
<Setter Property="Padding" Value="10,4"/>
<Setter Property="Margin" Value="0,0,8,0"/>
<Setter Property="Background" Value="#E2E8F0"/>
</Style>
<Style x:Key="InputStatusDotStyle" TargetType="Ellipse">
<Setter Property="Width" Value="10"/>
<Setter Property="Height" Value="10"/>
<Setter Property="Fill" Value="#93C5FD"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsOn}" Value="True">
<Setter Property="Fill" Value="#2563EB"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="OutputStatusDotStyle" TargetType="Ellipse">
<Setter Property="Width" Value="10"/>
<Setter Property="Height" Value="10"/>
<Setter Property="Fill" Value="#FCD34D"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsOn}" Value="True">
<Setter Property="Fill" Value="#F97316"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="InputItemBorderStyle" TargetType="Border">
<Setter Property="Background" Value="#EFF6FF"/>
<Setter Property="BorderBrush" Value="#DBEAFE"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="Margin" Value="0,0,0,4"/>
<Setter Property="CornerRadius" Value="4"/>
</Style>
<Style x:Key="OutputItemBorderStyle" TargetType="Border">
<Setter Property="Background" Value="#FFF7ED"/>
<Setter Property="BorderBrush" Value="#FED7AA"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Setter Property="Padding" Value="8,6"/>
<Setter Property="Margin" Value="0,0,0,4"/>
<Setter Property="CornerRadius" Value="4"/>
</Style>
<Style x:Key="IoSectionBorderStyle" TargetType="Border">
<Setter Property="CornerRadius" Value="6"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="10"/>
</Style>
<Style x:Key="IoOutputToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Width" Value="90"/>
<Setter Property="Height" Value="30"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border x:Name="Root" Background="#CBD5E1" BorderBrush="#94A3B8" BorderThickness="1" CornerRadius="15">
<Grid Margin="2">
<Border x:Name="Knob" Width="24" Height="24" Background="White" CornerRadius="12" HorizontalAlignment="Left"/>
<TextBlock x:Name="ToggleLabel" Text="OFF" Foreground="#334155" FontWeight="Bold" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,10,0"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="Root" Property="Background" Value="#16A34A"/>
<Setter TargetName="Root" Property="BorderBrush" Value="#16A34A"/>
<Setter TargetName="Knob" Property="HorizontalAlignment" Value="Right"/>
<Setter TargetName="ToggleLabel" Property="Text" Value="ON"/>
<Setter TargetName="ToggleLabel" Property="Foreground" Value="White"/>
<Setter TargetName="ToggleLabel" Property="HorizontalAlignment" Value="Left"/>
<Setter TargetName="ToggleLabel" Property="Margin" Value="10,0,0,0"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Root" Property="Opacity" Value="0.45"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid Background="#EDF1F7">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Margin="10,10,10,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="DEVICE IO MONITOR / 设备IO监控" FontSize="20" FontWeight="Bold" Foreground="#0F3B7A"/>
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
<Border Style="{StaticResource StatusBadgeBorderStyle}">
<TextBlock Text="{Binding PageState.LastRefreshTime, StringFormat=刷新:{0:HH:mm:ss}}" Foreground="#475569"/>
</Border>
<Border>
<Border.Style>
<Style TargetType="Border" BasedOn="{StaticResource StatusBadgeBorderStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding PageState.IsOnline}" Value="True">
<Setter Property="Background" Value="#DCFCE7"/>
</DataTrigger>
<DataTrigger Binding="{Binding PageState.IsOnline}" Value="False">
<Setter Property="Background" Value="#FEE2E2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding PageState.OnlineText}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#B91C1C"/>
<Style.Triggers>
<DataTrigger Binding="{Binding PageState.IsOnline}" Value="True">
<Setter Property="Foreground" Value="#166534"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
</StackPanel>
</Grid>
<Border Margin="0,8,0,0" Padding="12,6">
<Border.Style>
<Style TargetType="Border" BasedOn="{StaticResource StatusBadgeBorderStyle}">
<Setter Property="Background" Value="#EFF6FF"/>
<Style.Triggers>
<DataTrigger Binding="{Binding PageState.IsWriteSuccess}" Value="True">
<Setter Property="Background" Value="#DCFCE7"/>
</DataTrigger>
<DataTrigger Binding="{Binding PageState.IsWriteSuccess}" Value="False">
<Setter Property="Background" Value="#FEF3C7"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding PageState.LastWriteTime, StringFormat=写入时间:{0:HH:mm:ss}}" Foreground="#475569" Margin="0,0,14,0"/>
<TextBlock Text="{Binding PageState.LastWriteMessage}" FontWeight="SemiBold" TextWrapping="Wrap"/>
</StackPanel>
</Border>
</StackPanel>
<ScrollViewer Grid.Row="1" Margin="10,0,10,10" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding PageState.Modules}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource IoCardStyle}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}" FontSize="18" FontWeight="Bold" Foreground="#0F3B7A"/>
<Grid Grid.Row="1" Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition>
<ColumnDefinition.Style>
<Style TargetType="ColumnDefinition">
<Setter Property="Width" Value="*"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasInputs}" Value="False">
<Setter Property="Width" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
<ColumnDefinition>
<ColumnDefinition.Style>
<Style TargetType="ColumnDefinition">
<Setter Property="Width" Value="*"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasOutputs}" Value="False">
<Setter Property="Width" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
</Grid.ColumnDefinitions>
<Border BorderBrush="#BFDBFE" Background="#F8FBFF">
<Border.Style>
<Style TargetType="Border" BasedOn="{StaticResource IoSectionBorderStyle}">
<Setter Property="Margin" Value="0,0,6,0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasInputs}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding HasOutputs}" Value="False">
<Setter Property="Margin" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<TextBlock Text="输入 IO" Foreground="#1D4ED8" FontWeight="Bold" Margin="0,0,0,8"/>
<ItemsControl ItemsSource="{Binding InputPoints}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource InputItemBorderStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Ellipse Style="{StaticResource InputStatusDotStyle}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<StackPanel Grid.Column="1">
<TextBlock Text="{Binding Description}" Foreground="#1E3A8A" FontWeight="SemiBold"/>
<TextBlock Text="{Binding StationNo, StringFormat=站号:{0}}" Foreground="#1D4ED8" FontSize="11"/>
<TextBlock Text="{Binding LineNo, StringFormat=线标:{0}}" Foreground="#1D4ED8" FontSize="11"/>
<TextBlock Text="{Binding Name}" Foreground="#64748B" FontSize="11" TextWrapping="Wrap"/>
</StackPanel>
<TextBlock Grid.Column="2" Text="{Binding StateText}" VerticalAlignment="Center" Foreground="#1E40AF" FontWeight="Bold"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
<Border Grid.Column="1" BorderBrush="#FDBA74" Background="#FFF9F2">
<Border.Style>
<Style TargetType="Border" BasedOn="{StaticResource IoSectionBorderStyle}">
<Setter Property="Margin" Value="6,0,0,0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasOutputs}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding HasInputs}" Value="False">
<Setter Property="Margin" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel>
<TextBlock Text="输出 IO" Foreground="#C2410C" FontWeight="Bold" Margin="0,0,0,8"/>
<ItemsControl ItemsSource="{Binding OutputPoints}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Style="{StaticResource OutputItemBorderStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Ellipse Style="{StaticResource OutputStatusDotStyle}" VerticalAlignment="Center" Margin="0,0,8,0"/>
<StackPanel Grid.Column="1">
<TextBlock Text="{Binding Description}" Foreground="#9A3412" FontWeight="SemiBold"/>
<TextBlock Text="{Binding StationNo, StringFormat=站号:{0}}" Foreground="#C2410C" FontSize="11"/>
<TextBlock Text="{Binding LineNo, StringFormat=线标:{0}}" Foreground="#C2410C" FontSize="11"/>
<TextBlock Text="{Binding Name}" Foreground="#7C2D12" FontSize="11" TextWrapping="Wrap"/>
</StackPanel>
<TextBlock Grid.Column="2" Text="{Binding StateText}" Margin="0,0,10,0" VerticalAlignment="Center" Foreground="#C2410C" FontWeight="Bold"/>
<ToggleButton Grid.Column="3"
Style="{StaticResource IoOutputToggleButtonStyle}"
IsChecked="{Binding IsOn, Mode=OneWay}"
Command="{Binding ToggleCommand}"
IsEnabled="{Binding CanWrite}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,12 @@
using System.Windows.Controls;
namespace MainShell.DeviceMaintance.View
{
public partial class DeviceIoView : UserControl
{
public DeviceIoView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,38 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.DeviceMaintanceView"
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.DeviceMaintance.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>
<Grid Margin="-40,0,0,0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<mw:SideMenu ItemsSource="{Binding MenuItemWraps}"
SelectedItem="{Binding SelectedMenuItem, Mode=TwoWay}"
Grid.Row ="0" Width="180"
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>
</Grid>
<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.DeviceMaintance.View
{
/// <summary>
/// DeviceMaintanceView.xaml 的交互逻辑
/// </summary>
public partial class DeviceMaintanceView : UserControl
{
public DeviceMaintanceView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,102 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.HardwareTestView"
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="HardwareTestLabelStyle" TargetType="Label" BasedOn="{StaticResource ProcessLabelStyle}">
<Setter Property="Width" Value="126"/>
</Style>
<Style x:Key="HardwareTestValueBorderStyle" TargetType="Border">
<Setter Property="Background" Value="#F8FAFC"/>
<Setter Property="BorderBrush" Value="{StaticResource ProcessCardBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="Padding" Value="12,6"/>
<Setter Property="Margin" Value="8,0,18,0"/>
<Setter Property="MinWidth" Value="150"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="HardwareTestValueTextStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="14"/>
<Setter Property="Foreground" Value="{StaticResource ProcessBodyForegroundBrush}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
<Style x:Key="HardwareTestIntNumberBoxStyle" TargetType="{x:Type mw:IntNumberBox}" BasedOn="{StaticResource ProcessLargeIntNumberBoxStyle}">
<Setter Property="Margin" Value="8,0,18,0"/>
</Style>
<Style x:Key="HardwareTestButtonStyle" TargetType="Button" BasedOn="{StaticResource TestButtonStyle}">
<Setter Property="Width" Value="110"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Margin" Value="8,0,0,0"/>
</Style>
<Style x:Key="HardwareTestStartButtonStyle" TargetType="Button" BasedOn="{StaticResource StartButtonStyle}">
<Setter Property="Width" Value="110"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Margin" Value="8,0,0,0"/>
</Style>
<Style x:Key="HardwareTestStopButtonStyle" TargetType="Button" BasedOn="{StaticResource CloseButtonStyle}">
<Setter Property="Width" Value="110"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Margin" Value="8,0,0,0"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ProcessPageBackgroundBrush}">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="4" Padding="0,0,4,0">
<StackPanel Margin="0,0,0,4">
<GroupBox Header="{DynamicResource Diastimeter}" Style="{StaticResource ProcessWideCardGroupBoxStyle}">
<WrapPanel>
<Label Style="{StaticResource HardwareTestLabelStyle}" Content="{DynamicResource DiastimeterValue}"/>
<Border Style="{StaticResource HardwareTestValueBorderStyle}">
<TextBlock Style="{StaticResource HardwareTestValueTextStyle}" Text="{Binding Message}"/>
</Border>
<Button Style="{StaticResource HardwareTestButtonStyle}" Click="{mw:Action btnZeroClick}" Content="{DynamicResource SetZero}"/>
<Button Style="{StaticResource HardwareTestButtonStyle}" Click="{mw:Action btnGetCurrentValueClick}" Content="{DynamicResource GetCurrentValue}"/>
</WrapPanel>
</GroupBox>
<GroupBox Header="FFU" Style="{StaticResource ProcessWideCardGroupBoxStyle}">
<WrapPanel>
<Label Style="{StaticResource HardwareTestLabelStyle}" Content="{DynamicResource FFURotateSpeed}"/>
<mw:IntNumberBox Style="{StaticResource HardwareTestIntNumberBoxStyle}" Value="{Binding FFURotateSpeed}" ValueChanged="{mw:Action FFURotateSpeedChanged}"/>
<Button Style="{StaticResource HardwareTestStartButtonStyle}" Content="{DynamicResource Open}" Click="{mw:Action BtnOpenFFU}"/>
<Button Style="{StaticResource HardwareTestStopButtonStyle}" Content="{DynamicResource Close}" Click="{mw:Action BtnCloseFFU}"/>
</WrapPanel>
</GroupBox>
<WrapPanel>
<GroupBox Header="{DynamicResource WaferScaner}" Style="{StaticResource ProcessCardGroupBoxStyle}" Width="430" Margin="0,0,18,18">
<WrapPanel>
<Button Style="{StaticResource HardwareTestButtonStyle}" Click="{mw:Action btnScanWaferBarcodeClick}" Content="{DynamicResource GetCurrentValue}"/>
<Border Style="{StaticResource HardwareTestValueBorderStyle}" MinWidth="180" Margin="8,0,0,0">
<TextBlock Style="{StaticResource HardwareTestValueTextStyle}" Text="{Binding WaferBarcode}"/>
</Border>
</WrapPanel>
</GroupBox>
<GroupBox Header="{DynamicResource GlassScaner}" Style="{StaticResource ProcessCardGroupBoxStyle}" Width="430" Margin="0,0,0,18">
<WrapPanel>
<Button Style="{StaticResource HardwareTestButtonStyle}" Click="{mw:Action btnScanGlassBarcodeClick}" Content="{DynamicResource GetCurrentValue}"/>
<Border Style="{StaticResource HardwareTestValueBorderStyle}" MinWidth="180" Margin="8,0,0,0">
<TextBlock Style="{StaticResource HardwareTestValueTextStyle}" Text="{Binding GlassBarcode}"/>
</Border>
</WrapPanel>
</GroupBox>
</WrapPanel>
</StackPanel>
</ScrollViewer>
</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.DeviceMaintance.View
{
/// <summary>
/// HardwareTestView.xaml 的交互逻辑
/// </summary>
public partial class HardwareTestView : UserControl
{
public HardwareTestView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,23 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.IOMaintanceView"
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.DeviceMaintance.View"
xmlns:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="{StaticResource BorderBrush}" BorderThickness="2,2,2,2">
<TabControl mw:TabControlAttach.FontSize="14">
<TabItem Header="IO监控" >
<ContentControl mw:View.Model="{Binding IOMonitorViewModel}" Margin="15"/>
</TabItem>
<TabItem Header="气缸控制" >
<ContentControl mw:View.Model="{Binding CylinderViewModel}" Margin="15"/>
</TabItem>
<TabItem Header="IO测试" >
<ContentControl mw:View.Model="{Binding IoTestViewModel}" Margin="15"/>
</TabItem>
</TabControl>
</Border>
</UserControl>

View File

@@ -0,0 +1,13 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.IOMonitorView"
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.DeviceMaintance.View"
xmlns:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<ContentControl mw:View.Model="{Binding DeviceIoViewModel}"/>
</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.DeviceMaintance.View
{
/// <summary>
/// IOMonitorView.xaml 的交互逻辑
/// </summary>
public partial class IOMonitorView : UserControl
{
public IOMonitorView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,143 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.IoTestView"
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="560" d:DesignWidth="980">
<UserControl.Resources>
<Style x:Key="IoTestGroupBoxStyle" TargetType="GroupBox" BasedOn="{StaticResource ProcessWideCardGroupBoxStyle}">
<Setter Property="Margin" Value="0,0,0,16"/>
</Style>
<Style x:Key="IoTestLabelStyle" TargetType="Label" BasedOn="{StaticResource ProcessLabelStyle}">
<Setter Property="Width" Value="95"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
<Style x:Key="IoTestNumberBoxStyle" TargetType="{x:Type mw:IntNumberBox}" BasedOn="{StaticResource ProcessLargeIntNumberBoxStyle}">
<Setter Property="Width" Value="110"/>
<Setter Property="Margin" Value="0,0,12,8"/>
</Style>
<Style x:Key="IoTestButtonStyle" TargetType="Button" BasedOn="{StaticResource TestButtonStyle}">
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Margin" Value="0,0,12,0"/>
</Style>
<Style x:Key="IoTestResultBorderStyle" TargetType="Border">
<Setter Property="Background" Value="#F8FAFC"/>
<Setter Property="BorderBrush" Value="{StaticResource ProcessCardBorderBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="Padding" Value="12,8"/>
<Setter Property="Margin" Value="0,4,0,0"/>
</Style>
<Style x:Key="IoTestCheckBoxStyle" TargetType="CheckBox">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="0,0,18,8"/>
</Style>
</UserControl.Resources>
<Grid Background="{StaticResource ProcessPageBackgroundBrush}">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="4" Padding="0,0,4,0">
<StackPanel Margin="0,0,0,4">
<GroupBox Header="Delta IO状态" Style="{StaticResource IoTestGroupBoxStyle}">
<StackPanel>
<WrapPanel>
<Label Style="{StaticResource IoTestLabelStyle}" Content="控制卡状态"/>
<TextBlock VerticalAlignment="Center" Text="{Binding CardStatusText}" TextWrapping="Wrap" Width="620"/>
<Button Style="{StaticResource IoTestButtonStyle}" Content="刷新状态" Click="{mw:Action RefreshCardStatus}"/>
</WrapPanel>
</StackPanel>
</GroupBox>
<GroupBox Header="数字量 Bit 测试" Style="{StaticResource IoTestGroupBoxStyle}">
<StackPanel>
<WrapPanel>
<Label Style="{StaticResource IoTestLabelStyle}" Content="CardNo"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding BitState.CardNo}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="Node"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding BitState.Node}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="Slot"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding BitState.Slot}"/>
</WrapPanel>
<WrapPanel>
<Label Style="{StaticResource IoTestLabelStyle}" Content="Index"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding BitState.Index}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="SubIndex"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding BitState.SubIndex}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="BitNo"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding BitState.BitNo}"/>
</WrapPanel>
<WrapPanel>
<Label Style="{StaticResource IoTestLabelStyle}" Content="IoType"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding BitState.IoType}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="ReadIoType"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding BitState.ReadIoType}"/>
<CheckBox Style="{StaticResource IoTestCheckBoxStyle}" Content="写入值" IsChecked="{Binding BitState.WriteValue}"/>
<CheckBox Style="{StaticResource IoTestCheckBoxStyle}" Content="读取结果" IsChecked="{Binding BitState.ReadValue}" IsEnabled="False"/>
</WrapPanel>
<WrapPanel Margin="0,4,0,0">
<Button Style="{StaticResource IoTestButtonStyle}" Content="读取 Bit" Click="{mw:Action ReadBit}"/>
<Button Style="{StaticResource IoTestButtonStyle}" Content="写入 Bit" Click="{mw:Action WriteBit}"/>
<TextBlock VerticalAlignment="Center" Text="{Binding BitState.ReturnCode, StringFormat=返回码:{0}}"/>
</WrapPanel>
<Border Style="{StaticResource IoTestResultBorderStyle}">
<TextBlock Text="{Binding BitState.ResultMessage}" TextWrapping="Wrap"/>
</Border>
</StackPanel>
</GroupBox>
<GroupBox Header="模拟量 DA 测试" Style="{StaticResource IoTestGroupBoxStyle}">
<StackPanel>
<WrapPanel>
<Label Style="{StaticResource IoTestLabelStyle}" Content="CardNo"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding DaState.CardNo}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="Node"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding DaState.Node}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="Slot"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding DaState.Slot}"/>
</WrapPanel>
<WrapPanel>
<Label Style="{StaticResource IoTestLabelStyle}" Content="Index"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding DaState.Index}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="SubIndex"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding DaState.SubIndex}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="ByteSize"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding DaState.ByteSize}"/>
</WrapPanel>
<WrapPanel>
<Label Style="{StaticResource IoTestLabelStyle}" Content="IoType"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding DaState.IoType}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="写入值"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding DaState.WriteRawValue}"/>
<Label Style="{StaticResource IoTestLabelStyle}" Content="读取值"/>
<mw:IntNumberBox Style="{StaticResource IoTestNumberBoxStyle}" Value="{Binding DaState.ReadRawValue}" IsEnabled="False"/>
</WrapPanel>
<WrapPanel>
<CheckBox Style="{StaticResource IoTestCheckBoxStyle}" Content="有符号" IsChecked="{Binding DaState.Signed}"/>
<Button Style="{StaticResource IoTestButtonStyle}" Content="读取 DA" Click="{mw:Action ReadDa}"/>
<Button Style="{StaticResource IoTestButtonStyle}" Content="写入 DA" Click="{mw:Action WriteDa}"/>
<TextBlock VerticalAlignment="Center" Text="{Binding DaState.ReturnCode, StringFormat=返回码:{0}}"/>
</WrapPanel>
<Border Style="{StaticResource IoTestResultBorderStyle}">
<TextBlock Text="{Binding DaState.ResultMessage}" TextWrapping="Wrap"/>
</Border>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>

View File

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

View File

@@ -0,0 +1,178 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.LaserCompensationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
d:DesignHeight="560"
d:DesignWidth="900">
<UserControl.Resources>
<ResourceDictionary>
<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Margin" Value="8,0,8,0"/>
<Setter Property="Foreground" Value="{StaticResource ProcessBodyForegroundBrush}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Foreground" Value="{StaticResource ProcessBodyForegroundBrush}"/>
</Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Width" Value="110"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Margin" Value="8,0,0,0"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<Grid Background="{StaticResource ProcessPageBackgroundBrush}" Margin="8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Header="{DynamicResource LaserCompensationParameters}" Style="{StaticResource ProcessWideCardGroupBoxStyle}">
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="280"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="280"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="280"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{DynamicResource AxisName}"/>
<ComboBox Grid.Row="0" Grid.Column="1"
Style="{StaticResource ProcessLargeComboBoxStyle}"
ItemsSource="{Binding AxisNames}"
SelectedItem="{Binding SelectedAxisName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Label Grid.Row="0" Grid.Column="2" Content="{DynamicResource LaserCompensationStartPosition}"/>
<mw:NumberBox Grid.Row="0" Grid.Column="3"
Style="{StaticResource ProcessLargeNumberBoxStyle}"
Value="{Binding Setting.StartPos, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DecimalPlaces="4"
mw:NumericKeypadAttach.IsEnabled="True"
ShowUpDownButton="True"/>
<Label Grid.Row="0" Grid.Column="4" Content="{DynamicResource LaserCompensationStepDistance}"/>
<mw:NumberBox Grid.Row="0" Grid.Column="5"
Style="{StaticResource ProcessLargeNumberBoxStyle}"
Value="{Binding Setting.Steps, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DecimalPlaces="4"
mw:NumericKeypadAttach.IsEnabled="True"
ShowUpDownButton="True"/>
<Label Grid.Row="1" Grid.Column="0" Content="{DynamicResource LaserCompensationStepCount}"/>
<mw:IntNumberBox Grid.Row="1" Grid.Column="1"
Style="{StaticResource ProcessLargeIntNumberBoxStyle}"
Value="{Binding Setting.StepsCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
mw:NumericKeypadAttach.IsEnabled="True"
ShowUpDownButton="True"/>
<Label Grid.Row="1" Grid.Column="2" Content="{DynamicResource LaserCompensationJumpPosition}"/>
<mw:NumberBox Grid.Row="1" Grid.Column="3"
Style="{StaticResource ProcessLargeNumberBoxStyle}"
Value="{Binding Setting.JumpPos, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DecimalPlaces="4"
mw:NumericKeypadAttach.IsEnabled="True"
ShowUpDownButton="True"/>
<Label Grid.Row="1" Grid.Column="4" Content="{DynamicResource LaserCompensationCycleCount}"/>
<mw:IntNumberBox Grid.Row="1" Grid.Column="5"
Style="{StaticResource ProcessLargeIntNumberBoxStyle}"
Value="{Binding Setting.WorkCount, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
mw:NumericKeypadAttach.IsEnabled="True"
ShowUpDownButton="True"/>
<Label Grid.Row="2" Grid.Column="0" Content="{DynamicResource Speed}"/>
<mw:NumberBox Grid.Row="2" Grid.Column="1"
Style="{StaticResource ProcessLargeNumberBoxStyle}"
Value="{Binding Setting.Speed, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DecimalPlaces="4"
mw:NumericKeypadAttach.IsEnabled="True"
ShowUpDownButton="True"/>
<Label Grid.Row="2" Grid.Column="2" Content="{DynamicResource LaserCompensationDelaySeconds}"/>
<mw:NumberBox Grid.Row="2" Grid.Column="3"
Style="{StaticResource ProcessLargeNumberBoxStyle}"
Value="{Binding Setting.DwellTime, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DecimalPlaces="3"
mw:NumericKeypadAttach.IsEnabled="True"
ShowUpDownButton="True"/>
<Button Grid.Row="2" Grid.Column="5" Content="{DynamicResource Save}" Click="{mw:Action Save}" HorizontalAlignment="Left"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="1" Header="{DynamicResource LaserCompensationExecutionControl}" Style="{StaticResource ProcessWideCardGroupBoxStyle}" Margin="0,12,0,0">
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Content="{DynamicResource Start}" IsEnabled="{Binding UiEnable}" Click="{mw:Action StartAsync}"/>
<Button Grid.Column="1" Content="{DynamicResource Stop}" IsEnabled="{Binding IsRunning}" Click="{mw:Action Stop}"/>
<TextBlock Grid.Column="2"
VerticalAlignment="Center"
Text="{DynamicResource LaserCompensationTrajectoryOrder}"
Margin="18,0,0,0"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="2" Header="{DynamicResource LaserCompensationExecutionStatus}" Style="{StaticResource ProcessWideCardGroupBoxStyle}" Margin="0,12,0,0">
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="160"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{DynamicResource LaserCompensationCurrentCycle}"/>
<TextBlock Grid.Column="1" VerticalAlignment="Center" Text="{Binding CurrentLoopIndex}"/>
<Label Grid.Column="2" Content="{DynamicResource LaserCompensationCurrentPoint}"/>
<TextBlock Grid.Column="3" VerticalAlignment="Center" Text="{Binding CurrentPointIndex}"/>
<Label Grid.Column="4" Content="{DynamicResource LaserCompensationCurrentTarget}"/>
<TextBlock Grid.Column="5" VerticalAlignment="Center" Text="{Binding CurrentTargetPosition, StringFormat=F4}"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="3" Header="{DynamicResource Description}" Style="{StaticResource ProcessWideCardGroupBoxStyle}" Margin="0,12,0,0">
<StackPanel Margin="4">
<TextBlock TextWrapping="Wrap"
Text="{DynamicResource LaserCompensationDescriptionPrimary}"/>
<TextBlock Margin="0,8,0,0"
TextWrapping="Wrap"
Text="{DynamicResource LaserCompensationDescriptionSecondary}"/>
</StackPanel>
</GroupBox>
</Grid>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using System.Windows.Controls;
namespace MainShell.DeviceMaintance.View
{
public partial class LaserCompensationView : UserControl
{
public LaserCompensationView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,48 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.NeedleBaseView"
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.DeviceMaintance.View"
xmlns:mw="http://www.maxwell-gp.com/"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Background="#D7DDE4"
Style="{StaticResource NavigationListBoxStyle}"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding NavigationItems}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate >
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
FontWeight="Bold"
Foreground="#505050"
FontSize="14" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentControl Margin="2" Content="{Binding CameraAxisViewModel}"/>
<Border BorderBrush="Gray" BorderThickness="1" Margin="5,2" Grid.Column="1">
<ContentControl Margin="2"
mw:View.Model="{Binding CurrentScreen}"/>
</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.DeviceMaintance.View
{
/// <summary>
/// NeedleBaseView.xaml 的交互逻辑
/// </summary>
public partial class NeedleBaseView : UserControl
{
public NeedleBaseView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,142 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.NeedleCaliView"
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.DeviceMaintance.View"
xmlns:mwControls="http://www.maxwell-gp.com/"
xmlns:Att="clr-namespace:MainShell.Common.ControlAttribute"
xmlns:customControl="clr-namespace:MainShell.Resources.CustomControl"
mc:Ignorable="d"
d:DesignHeight="850" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="GroupBox" BasedOn="{StaticResource GroupStepControl}"/>
<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Width" Value="80"/>
</Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="3"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Width" Value="90"/>
</Style>
</UserControl.Resources>
<Grid mwControls:ParameterAttach.DelayAcceptValue="{Binding ParameterHelper}">
<ScrollViewer VerticalScrollBarVisibility="Visible" Margin="5" ClipToBounds="True">
<StackPanel>
<GroupBox Header="【第一步】对刀仪偏差设置" IsEnabled="{Binding UiEnable}">
<StackPanel Margin="5" HorizontalAlignment="Center" Orientation="Horizontal">
<Label Content="对刀时偏差:" />
<mwControls:NumberBox Value="{Binding NeedleZCalibrationItem.KnifeOffset}" mwControls:NumericKeypadAttach.IsEnabled="True" Minimum="-1000" Maximum="1000" HorizontalAlignment="Left" Margin="2" Width="80" Height="35" />
</StackPanel>
</GroupBox>
<GroupBox Header="【第二步】SZ / StagePlatform设置" IsEnabled="{Binding UiEnable}">
<customControl:AxisMoveControl LableContent1="SZ(mm):"
Value1="{Binding NeedleZCalibrationItem.CZHeight}"
LableContent2="StagePlatform高度:"
Value2="{Binding NeedleZCalibrationItem.StageHeight}"
IsAllShow="{Binding HasStagePlatform}"
MoveCommand="{mwControls:Action MoveCzCurrentLocation}"/>
</GroupBox>
<GroupBox Header="【第三步】龙门2的避让位置" IsEnabled="{Binding UiEnable}">
<customControl:AxisMoveControl LableContent1="X2(mm):"
Value1="{Binding NeedleZCalibrationItem.Gantry2AvoidPositionX}"
LableContent2="Y2(mm):"
Value2="{Binding NeedleZCalibrationItem.Gantry2AvoidPositionY}"
ReadCommand="{mwControls:Action ReadGantry2CurrentLocation}"
MoveCommand="{mwControls:Action MoveGantry2ToCurrentLocation}"/>
</GroupBox>
<GroupBox Header="【第四步】针尖对准对刀仪位置" IsEnabled="{Binding UiEnable}">
<customControl:AxisMoveControl LableContent1="X1(mm):"
Value1="{Binding NeedleZCalibrationItem.SupportPinPositionX}"
LableContent2="Y1(mm):"
Value2="{Binding NeedleZCalibrationItem.SupportPinPositionY}"
ReadCommand="{mwControls:Action ReadSupportPinCurrentLocation}"
MoveCommand="{mwControls:Action MoveSupportPinToCurrentLocation}"/>
</GroupBox>
<GroupBox Header="【第五步】Z1设置" IsEnabled="{Binding UiEnable}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<UniformGrid Columns="4" Rows="1">
<Label Content="开始位置(mm)" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Right" />
<mwControls:NumberBox Value="{Binding NeedleZCalibrationItem.Z1StartPosition}" mwControls:NumericKeypadAttach.IsEnabled="True" Minimum="-1000" Maximum="1000" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Left" Margin="2" Width="80" Height="35" />
<Label Content="速度(mm/s)" Grid.Row="0" Grid.Column="2" HorizontalAlignment="Right" />
<mwControls:NumberBox Value="{Binding NeedleZCalibrationItem.Z1Speed}" mwControls:NumericKeypadAttach.IsEnabled="True" Minimum="-1000" Maximum="1000" Grid.Row="0" Grid.Column="3" HorizontalAlignment="Left" Margin="2" Width="80" Height="35" />
</UniformGrid>
<UniformGrid Columns="2" Rows="1" Grid.Column="1">
<Label Content="下降距离(mm)" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" />
<mwControls:NumberBox Value="{Binding NeedleZCalibrationItem.Z1DropMaxPosition}" mwControls:NumericKeypadAttach.IsEnabled="True" Minimum="-1000" Maximum="1000" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" Margin="2" Width="80" Height="35" />
</UniformGrid>
</Grid>
</GroupBox>
<GroupBox Header="【第六步】刺针触碰到对刀仪" IsEnabled="{Binding UiEnable}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Visibility="Visible" Content="基准值(mm):" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Right" />
<mwControls:NumberBox Visibility="Visible" IsReadOnly="True" Value="{Binding NeedleZCalibrationItem.NeedleTouchHeightBase}" mwControls:NumericKeypadAttach.IsEnabled="True" Minimum="-1000" Maximum="1000" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Left" Margin="5" Width="80" Height="35" />
<Label Content="Z1平均高度(mm):" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Right" />
<mwControls:NumberBox IsReadOnly="True" Value="{Binding NeedleZCalibrationItem.NeedleTouchHeightAve}" mwControls:NumericKeypadAttach.IsEnabled="True" Minimum="-1000" Maximum="1000" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Left" Margin="5" Width="80" Height="35" />
<Label Content="对刀次数:" Grid.Row="2" Grid.Column="0" HorizontalAlignment="Right" />
<mwControls:NumberBox Value="{Binding NeedleZCalibrationItem.NeedleTouchCount}" mwControls:NumericKeypadAttach.IsEnabled="True" Minimum="1" Maximum="100" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Left" Margin="5" Width="80" Height="35" />
<Label Content="当前使用对刀高度(mm):" Grid.Row="3" Grid.Column="0" HorizontalAlignment="Right" Width="140"/>
<mwControls:NumberBox IsEnabled="False" Grid.Row="3" Value="{Binding NeedleZCalibrationItem.CurrentNeedleTouchHeight}" mwControls:NumericKeypadAttach.IsEnabled="True" Minimum="-1000" Maximum="1000" Grid.Column="1" HorizontalAlignment="Left" Margin="5" Width="80" Height="35" />
<Label Content="对刀与基准偏差(mm):" Grid.Row="4" Grid.Column="0" HorizontalAlignment="Right" />
<mwControls:NumberBox IsReadOnly="True" Grid.Row="4" Value="{Binding NeedleZCalibrationItem.NeedleTouchOffset}" mwControls:NumericKeypadAttach.IsEnabled="True" Minimum="-1000" Maximum="1000" Grid.Column="1" HorizontalAlignment="Left" Margin="5" Width="80" Height="35" />
<Grid Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="5">
<ListView ItemsSource="{Binding NeedleZCalibrationItem.NeedleTouchRecords}" SelectedItem="{Binding SelectedRecord}">
<ListView.View>
<GridView>
<GridViewColumn Header="当前次数" DisplayMemberBinding="{Binding CurrentCount}" Width="100"/>
<GridViewColumn Header="Z1高度(mm)" DisplayMemberBinding="{Binding Height}" Width="210"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Grid>
</GroupBox>
<GroupBox Header="【第七步】对刀控制" IsEnabled="{Binding IsEnabledNeedleCalibrationControl}">
<StackPanel>
<StackPanel Margin="4" IsEnabled="{Binding UiEnable}" Orientation="Horizontal" HorizontalAlignment="Left">
<CheckBox Content="自动抬高" Margin="6,0" IsChecked="{Binding AutoRaiseZ1}"/>
<Button Visibility="Collapsed" Content="设置当前基准值" Tag="设置当前基准值" Click="{mwControls:Action SetBaseHeight}" Margin="6,0" />
<Button Content="Z1对刀" Tag="Z1对刀" Margin="6,0" Click="{mwControls:Action StartSingleZ1CalibrationAsync}"/>
<Button Content="使用当前对刀高度" Width="120" Tag="使用当前对刀高度" Click="{mwControls:Action UseCurrentZ1Height}" Margin="6,0" />
</StackPanel>
<StackPanel Margin="4" Orientation="Horizontal">
<Button Content="开始" Tag="开始" Click="{mwControls:Action StartCalibrationAsync}" Margin="6,0" IsEnabled="{Binding UiEnable}"/>
<Button Content="停止" Tag="停止" Click="{mwControls:Action StopCalibrationAsync}" Margin="6,0" IsEnabled="{Binding UiEnable, Converter={StaticResource boolToInversionConverter}}"/>
<Button Content="保存" Tag="保存" Click="{mwControls:Action SaveCalibration}" Margin="6,0" IsEnabled="{Binding UiEnable}"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</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.DeviceMaintance.View
{
/// <summary>
/// NeedleCaliView.xaml 的交互逻辑
/// </summary>
public partial class NeedleCaliView : UserControl
{
public NeedleCaliView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,189 @@
<UserControl x:Class="MainShell.DeviceMaintance.View.NeedleCameraPrintView"
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.DeviceMaintance.View"
xmlns:mwControls="http://www.maxwell-gp.com/"
xmlns:customControl="clr-namespace:MainShell.Resources.CustomControl"
mc:Ignorable="d"
d:DesignHeight="750" d:DesignWidth="800">
<UserControl.Resources>
<Style TargetType="GroupBox" BasedOn="{StaticResource GroupStepControl}"/>
<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Width" Value="80"/>
</Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" >
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Margin" Value="3"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Width" Value="90"/>
</Style>
</UserControl.Resources>
<Grid mwControls:ParameterAttach.DelayAcceptValue ="{Binding ParameterHelper}">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<StackPanel>
<GroupBox IsEnabled="{Binding UiEnable}" Margin="2" Header="【第一步】基础设置">
<GroupBox.Content>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Button Click="{mwControls:Action MoveToOrginPos}" Content="SXSZ至初始位" Width="100"/>
<GroupBox Header="速度设置" Style="{x:Null}">
<StackPanel Orientation="Horizontal">
<RadioButton GroupName="speed" Click="{mwControls:Action SpeedChanged}" Tag="10" Margin="5,0" Content="10"/>
<RadioButton GroupName="speed" IsChecked="True" Click="{mwControls:Action SpeedChanged}" Tag="50" Margin="5,0" Content="50"/>
<RadioButton GroupName="speed" Click="{mwControls:Action SpeedChanged}" Tag="100" Margin="5,0" Content="100"/>
</StackPanel>
</GroupBox>
</StackPanel>
</GroupBox.Content>
</GroupBox>
<GroupBox IsEnabled="{Binding UiEnable}" Margin="2" Header="【第二步】WS避让位">
<GroupBox.Content>
<customControl:AxisMoveControl LableContent1="X2(mm):"
Value1="{Binding NeedlePrintCalibrateParameter.AvoidancePoint.PointX}"
LableContent2="Y2(mm):"
Value2="{Binding NeedlePrintCalibrateParameter.AvoidancePoint.PointY}"
ReadCommand="{mwControls:Action TeachWSPos}"
MoveCommand="{mwControls:Action MoveToWsPos}"/>
<!--<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Label VerticalAlignment="Center" Content="X2:"/>
<mwControls:NumberBox Value="{Binding NeedlePrintCalibrateParameter.AvoidancePoint.PointX}" IsReadOnly="True" Width="80" Margin="4,0" />
<Label VerticalAlignment="Center" Content="Y2:"/>
<mwControls:NumberBox Value="{Binding NeedlePrintCalibrateParameter.AvoidancePoint.PointY}" IsReadOnly="True" Width="80" Margin="4,0" />
<Button Margin="2,0" Click="{mwControls:Action TeachWSPos}" Content="示教"/>
<Button Margin="2,0" Click="{mwControls:Action MoveToWsPos}" Content="移动"/>
</StackPanel>-->
</GroupBox.Content>
</GroupBox>
<GroupBox IsEnabled="{Binding UiEnable}" Margin="2" Header="【第三步】Z1位置设置">
<GroupBox.Content>
<customControl:AxisMoveControl LableContent1="Z1(mm):"
Value1="{Binding NeedlePrintCalibrateParameter.Z1WorkHeight}"
IsAllShow="False"
ReadCommand="{mwControls:Action TeachZ1Pos}"
MoveCommand="{mwControls:Action MoveToZ1Pos}"/>
<!--<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Label VerticalAlignment="Center" Content="Z1:"/>
<mwControls:NumberBox mwControls:NumericKeypadAttach.IsEnabled="True" Value="{Binding NeedlePrintCalibrateParameter.Z1WorkHeight, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" Width="80" Margin="4,0" />
<Button Margin="2,0" Click="{mwControls:Action TeachZ1Pos}" Content="示教"/>
<Button Margin="2,0" Click="{mwControls:Action MoveToZ1Pos}" Content="移动"/>
</StackPanel>-->
</GroupBox.Content>
</GroupBox>
<GroupBox IsEnabled="{Binding UiEnable}" Margin="2" Header="【第四步】Z2位置设置">
<GroupBox.Content>
<customControl:AxisMoveControl LableContent1="Z2(mm):"
Value1="{Binding NeedlePrintCalibrateParameter.Z2WorkHeight}"
IsAllShow="False"
ReadCommand="{mwControls:Action TeachZ2Pos}"
MoveCommand="{mwControls:Action MoveToZ2Pos}"/>
<!--<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Label VerticalAlignment="Center" Content="Z2:"/>
<mwControls:NumberBox mwControls:NumericKeypadAttach.IsEnabled="True" Value="{Binding NeedlePrintCalibrateParameter.Z2WorkHeight, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" Width="80" Margin="4,0" />
<Button Margin="2,0" Click="{mwControls:Action TeachZ2Pos}" Content="示教"/>
<Button Margin="2,0" Click="{mwControls:Action MoveToZ2Pos}" Content="移动"/>
</StackPanel>-->
</GroupBox.Content>
</GroupBox>
<GroupBox IsEnabled="{Binding UiEnable}" Margin="2" Header="【第五步】铜箔区域设置">
<GroupBox.Content>
<StackPanel>
<Label Content="起点设置:"/>
<customControl:AxisMoveControl LableContent1="X1(mm):"
Value1="{Binding NeedlePrintCalibrateParameter.StartPoint.PointX}"
LableContent2="Y1(mm):"
Value2="{Binding NeedlePrintCalibrateParameter.StartPoint.PointY}"
ReadCommand="{mwControls:Action TeachStartPos}"
MoveCommand="{mwControls:Action MoveToStartPos}"
IsReadOnly="True"/>
<!--<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Label VerticalAlignment="Center" Content="X1:"/>
<mwControls:NumberBox Value="{Binding NeedlePrintCalibrateParameter.StartPoint.PointX}" IsReadOnly="True" Width="80" Margin="4,0" />
<Label VerticalAlignment="Center" Content="Y1:"/>
<mwControls:NumberBox Value="{Binding NeedlePrintCalibrateParameter.StartPoint.PointY}" IsReadOnly="True" Width="80" Margin="4,0" />
<Button Margin="2,0" Click="{mwControls:Action TeachStartPos}" Content="示教"/>
<Button Margin="2,0" Click="{mwControls:Action MoveToStartPos}" Content="移动"/>
</StackPanel>-->
<Label Content="尾点设置:"/>
<customControl:AxisMoveControl LableContent1="X2(mm):"
Value1="{Binding NeedlePrintCalibrateParameter.EndPoint.PointX}"
LableContent2="Y2(mm):"
Value2="{Binding NeedlePrintCalibrateParameter.EndPoint.PointY}"
ReadCommand="{mwControls:Action TeachEndPos}"
MoveCommand="{mwControls:Action MoveToEndPos}"
IsReadOnly="True"/>
<!--<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Label VerticalAlignment="Center" Content="X1:"/>
<mwControls:NumberBox Value="{Binding NeedlePrintCalibrateParameter.EndPoint.PointX}" IsReadOnly="True" Width="80" Margin="4,0" />
<Label VerticalAlignment="Center" Content="Y1:"/>
<mwControls:NumberBox Value="{Binding NeedlePrintCalibrateParameter.EndPoint.PointY}" IsReadOnly="True" Width="80" Margin="4,0" />
<Button Margin="2,0" Click="{mwControls:Action TeachEndPos}" Content="示教"/>
<Button Margin="2,0" Click="{mwControls:Action MoveToEndPos}" Content="移动"/>
</StackPanel>-->
<Label Content="间距设置:"/>
<StackPanel Orientation="Horizontal">
<Label VerticalAlignment="Center" Content="Xpitch:"/>
<mwControls:NumberBox mwControls:NumericKeypadAttach.IsEnabled="True" Value="{Binding NeedlePrintCalibrateParameter.Xpitch}" IsReadOnly="False" Width="80" Margin="4,0" />
<Label VerticalAlignment="Center" Content="Ypitch:"/>
<mwControls:NumberBox mwControls:NumericKeypadAttach.IsEnabled="True" Value="{Binding NeedlePrintCalibrateParameter.Ypitch}" IsReadOnly="False" Width="80" Margin="4,0" />
</StackPanel>
</StackPanel>
</GroupBox.Content>
</GroupBox>
<GroupBox Margin="2" Header="【第六步】针印检验">
<GroupBox.Content>
<StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button IsEnabled="{Binding UiEnable}" Click="{mwControls:Action StartVerify}" Content="开始校验"/>
<Button IsEnabled="{Binding UiEnable,Converter={StaticResource boolToInversionConverter}}" Click="{mwControls:Action StopVerify}" Content="停止校验"/>
<Button IsEnabled="{Binding UiEnable}" Click="{mwControls:Action SaveParam}" Content="保存"/>
<Button IsEnabled="{Binding UiEnable}" Click="{mwControls:Action ResetRecord}" Content="重置记录"/>
</StackPanel>
<StackPanel IsEnabled="{Binding UiEnable}" Margin="0,2" Orientation="Horizontal" HorizontalAlignment="Center">
<Button Click="{mwControls:Action NeedleAlignment}" Content="针尖对准"/>
<Button Click="{mwControls:Action CameraAlignment}" Content="相机对准"/>
</StackPanel>
</StackPanel>
</GroupBox.Content>
</GroupBox>
<GroupBox IsEnabled="{Binding UiEnable}" Margin="2" Header="【第七步】偏差补偿">
<GroupBox.Content>
<StackPanel>
<Label Content="当前针尖补偿:" Width="90"/>
<StackPanel Orientation="Horizontal">
<Label VerticalAlignment="Center" Content="XOffset:"/>
<mwControls:NumberBox Value="{Binding CurrentNeedleOffsetPoint.PointX}" IsReadOnly="True" Width="80" Margin="4,0" />
<Label VerticalAlignment="Center" Content="YOffsetY:"/>
<mwControls:NumberBox Value="{Binding CurrentNeedleOffsetPoint.PointY}" IsReadOnly="True" Width="80" Margin="4,0" />
</StackPanel>
<Label Content="针尖点位:"/>
<StackPanel Orientation="Horizontal">
<Label VerticalAlignment="Center" Content="X1:"/>
<mwControls:NumberBox Value="{Binding CurrentNeedlePoint.PointX}" IsReadOnly="True" Width="80" Margin="4,0" />
<Label VerticalAlignment="Center" Content="Y1:"/>
<mwControls:NumberBox Value="{Binding CurrentNeedlePoint.PointY}" IsReadOnly="True" Width="80" Margin="4,0" />
</StackPanel>
<Label Content="相机点位:"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<Label VerticalAlignment="Center" Content="X1:"/>
<mwControls:NumberBox Value="{Binding CameraPoint.PointX}" IsReadOnly="True" Width="80" Margin="4,0" />
<Label VerticalAlignment="Center" Content="Y1:"/>
<mwControls:NumberBox Value="{Binding CameraPoint.PointY}" IsReadOnly="True" Width="80" Margin="4,0" />
<Button Margin="2,0" Click="{mwControls:Action TeachCameraPos}" Content="示教"/>
<Button Margin="2,0" Content="补偿偏差" Click="{mwControls:Action CalculateOffset}"/>
</StackPanel>
</StackPanel>
</GroupBox.Content>
</GroupBox>
</StackPanel>
</ScrollViewer>
</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.DeviceMaintance.View
{
/// <summary>
/// NeedleCameraPrintView.xaml 的交互逻辑
/// </summary>
public partial class NeedleCameraPrintView : UserControl
{
public NeedleCameraPrintView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,15 @@
using MainShell.DeviceMaintance.Model;
using MainShell.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.DeviceMaintance.ViewModel
{
public class CameraAccuracyCalibrationViewModel: DeviceMaintanceBaseViewModel
{
}
}

View File

@@ -0,0 +1,305 @@
using MainShell.DeviceMaintance.Model;
using MainShell.DeviceMaintance.ViewModel.State;
using MainShell.Hardware;
using MainShell.Manual.ViewModel.State;
using MaxwellFramework.Core.Interfaces;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MainShell.DeviceMaintance.ViewModel
{
public class CylinderViewModel : Screen, IPage
{
private readonly IDeviceIoMonitorService _ioMonitorService;
private readonly IDeviceCylinderService _cylinderService;
private readonly Dictionary<string, List<CylinderSignalState>> _signalMap = new Dictionary<string, List<CylinderSignalState>>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, DeviceIoPointDefinition> _ioDefinitionsByName;
private CylinderPageState _pageState;
public CylinderPageState PageState
{
get { return _pageState; }
set { SetAndNotify(ref _pageState, value); }
}
public CylinderViewModel(IDeviceIoMonitorService ioMonitorService, IDeviceCylinderService cylinderService)
{
_ioMonitorService = ioMonitorService;
_cylinderService = cylinderService;
_ioDefinitionsByName = _ioMonitorService.Definitions.ToDictionary(x => x.Name, x => x, StringComparer.OrdinalIgnoreCase);
PageState = BuildState();
_ioMonitorService.IoChanged += OnIoChanged;
_ioMonitorService.Start();
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
ApplyIoPoints(_ioMonitorService.LatestPoints.Values);
InitializePointsAsync();
}
protected override void OnDeactivate()
{
_ioMonitorService.IoChanged -= OnIoChanged;
base.OnDeactivate();
}
private CylinderPageState BuildState()
{
var state = new CylinderPageState();
_signalMap.Clear();
foreach (var definition in _cylinderService.Definitions)
{
var item = new CylinderItemState
{
Name = definition.Name,
Description = definition.Description,
Module = definition.Module,
ControlTypeText = GetControlTypeText(definition.ControlType)
};
item.ExtendCommand = new ActionCommand(() => ExecuteCylinderAction(definition, item, true), () => !item.IsBusy);
item.RetractCommand = new ActionCommand(() => ExecuteCylinderAction(definition, item, false), () => !item.IsBusy);
for (var i = 0; i < definition.ExtendOutputPoints.Count; i++)
{
AddOutputSignal(item, definition.ExtendOutputPoints[i], definition.ExtendOutputPoints.Count > 1 ? string.Format("{0}-伸出输出{1}", definition.Name, i + 1) : definition.Name + "-伸出输出");
}
for (var i = 0; i < definition.RetractOutputPoints.Count; i++)
{
AddOutputSignal(item, definition.RetractOutputPoints[i], definition.RetractOutputPoints.Count > 1 ? string.Format("{0}-缩回输出{1}", definition.Name, i + 1) : definition.Name + "-缩回输出");
}
foreach (var pointReference in definition.ExtendedFeedbackPoints)
{
AddFeedbackSignal(item, pointReference, definition.Name + "-伸出到位");
}
foreach (var pointReference in definition.RetractedFeedbackPoints)
{
AddFeedbackSignal(item, pointReference, definition.Name + "-缩回到位");
}
state.Cylinders.Add(item);
}
return state;
}
private void AddOutputSignal(CylinderItemState item, string pointReference, string fallbackName)
{
var definition = ResolveDefinition(pointReference);
var signal = new CylinderSignalState
{
PointReference = pointReference,
PointKey = definition?.PointKey,
Name = definition != null ? definition.Name : fallbackName,
LineNo = definition != null ? definition.LineNo : string.Empty,
IsOutput = true
};
item.OutputSignals.Add(signal);
MapSignal(signal, pointReference);
}
private void AddFeedbackSignal(CylinderItemState item, string pointReference, string fallbackName)
{
var definition = ResolveDefinition(pointReference);
var signal = new CylinderSignalState
{
PointReference = pointReference,
PointKey = definition?.PointKey,
Name = definition != null ? definition.Name : fallbackName,
LineNo = definition != null ? definition.LineNo : string.Empty,
IsOutput = false
};
item.FeedbackSignals.Add(signal);
MapSignal(signal, pointReference);
}
private DeviceIoPointDefinition ResolveDefinition(string pointReference)
{
if (string.IsNullOrWhiteSpace(pointReference))
{
return null;
}
DeviceIoPointDefinition definition;
if (_ioDefinitionsByName.TryGetValue(pointReference, out definition))
{
return definition;
}
int id;
if (!int.TryParse(pointReference, out id))
{
return null;
}
var matches = _ioMonitorService.Definitions.Where(x => x.Id == id).Take(2).ToList();
return matches.Count == 1 ? matches[0] : null;
}
private void MapSignal(CylinderSignalState signal, string pointReference)
{
var key = !string.IsNullOrWhiteSpace(signal.PointKey) ? signal.PointKey : pointReference;
if (string.IsNullOrWhiteSpace(key))
{
return;
}
List<CylinderSignalState> signals;
if (!_signalMap.TryGetValue(key, out signals))
{
signals = new List<CylinderSignalState>();
_signalMap[key] = signals;
}
signals.Add(signal);
}
private void OnIoChanged(object sender, EventArgs e)
{
var args = e as MainShell.EventArgsFolder.DeviceIoChangedEventArgs;
if (args == null)
{
return;
}
ApplyIoPoints(args.ChangedPoints);
}
private async void InitializePointsAsync()
{
_ioMonitorService.RequestRefresh();
await Task.Delay(150);
ApplyIoPoints(_ioMonitorService.LatestPoints.Values);
}
private void ApplyIoPoints(IEnumerable<DeviceIoPointState> points)
{
if (points == null)
{
return;
}
foreach (var point in points)
{
if (point == null)
{
continue;
}
List<CylinderSignalState> signals;
if (!_signalMap.TryGetValue(point.PointKey, out signals))
{
continue;
}
foreach (var signal in signals)
{
signal.IsOn = point.Value;
}
}
}
private void ExecuteCylinderAction(CylinderDefinition definition, CylinderItemState item, bool extend)
{
item.IsBusy = true;
RaiseCommands(item);
string failureReason;
var result = extend ? _cylinderService.TryExtend(definition.Name, out failureReason) : _cylinderService.TryRetract(definition.Name, out failureReason);
if (result)
{
SimulateCylinderFeedback(definition, extend);
PageState.LastActionMessage = definition.Name + (extend ? " 执行伸出成功。" : " 执行缩回成功。");
}
else
{
PageState.LastActionMessage = string.IsNullOrWhiteSpace(failureReason)
? definition.Name + (extend ? " 伸出失败。" : " 缩回失败。")
: failureReason;
}
PageState.LastActionTime = DateTime.Now;
item.IsBusy = false;
RaiseCommands(item);
}
private void SimulateCylinderFeedback(CylinderDefinition definition, bool extend)
{
foreach (var output in definition.ExtendOutputPoints.SelectMany(GetSignals))
{
output.IsOn = extend;
}
foreach (var output in definition.RetractOutputPoints.SelectMany(GetSignals))
{
output.IsOn = !extend;
}
foreach (var feedback in definition.ExtendedFeedbackPoints.SelectMany(GetSignals))
{
feedback.IsOn = extend;
}
foreach (var feedback in definition.RetractedFeedbackPoints.SelectMany(GetSignals))
{
feedback.IsOn = !extend;
}
}
private IEnumerable<CylinderSignalState> GetSignals(string pointReference)
{
var definition = ResolveDefinition(pointReference);
var key = definition != null ? definition.PointKey : pointReference;
if (string.IsNullOrWhiteSpace(key))
{
return Enumerable.Empty<CylinderSignalState>();
}
List<CylinderSignalState> signals;
if (_signalMap.TryGetValue(key, out signals))
{
return signals;
}
return Enumerable.Empty<CylinderSignalState>();
}
private static void RaiseCommands(CylinderItemState item)
{
var extend = item.ExtendCommand as ActionCommand;
if (extend != null)
{
extend.RaiseCanExecuteChanged();
}
var retract = item.RetractCommand as ActionCommand;
if (retract != null)
{
retract.RaiseCanExecuteChanged();
}
}
private static string GetControlTypeText(CylinderControlType controlType)
{
switch (controlType)
{
case CylinderControlType.DualOutput:
return "双输出控制";
case CylinderControlType.MultiOutput:
return "多输出单控";
default:
return "单输出控制";
}
}
}
}

View File

@@ -0,0 +1,194 @@
using MainShell.EventArgsFolder;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.DeviceMaintance.ViewModel.State;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MainShell.DeviceMaintance.ViewModel
{
public class DeviceIoViewModel : OperateViewModelBase
{
private readonly IDeviceIoMonitorService _ioMonitorService;
private readonly Dictionary<string, DeviceIoPointItemState> _pointStateMap = new Dictionary<string, DeviceIoPointItemState>(StringComparer.OrdinalIgnoreCase);
private DeviceIoPageState _pageState;
public DeviceIoPageState PageState
{
get { return _pageState; }
set { SetAndNotify(ref _pageState, value); }
}
public DeviceIoViewModel(IDeviceIoMonitorService ioMonitorService)
{
_ioMonitorService = ioMonitorService;
PageState = BuildState();
_ioMonitorService.IoChanged += OnIoChanged;
_ioMonitorService.Start();
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
RefreshFromCurrentPoints();
InitializeLatestPointsAsync();
}
protected override void OnDeactivate()
{
_ioMonitorService.IoChanged -= OnIoChanged;
base.OnDeactivate();
}
private DeviceIoPageState BuildState()
{
var state = new DeviceIoPageState();
_pointStateMap.Clear();
foreach (var moduleGroup in _ioMonitorService.Definitions.GroupBy(x => string.IsNullOrWhiteSpace(x.Module) ? "Default" : x.Module))
{
var moduleState = new DeviceIoModuleState
{
Name = moduleGroup.Key
};
foreach (var definition in moduleGroup.OrderBy(x => x.StationNo).ThenBy(x => x.Id).ThenBy(x => x.LineNo))
{
var item = new DeviceIoPointItemState
{
Id = definition.Id,
PointKey = definition.PointKey,
Module = definition.Module,
Name = definition.Name,
StationNo = definition.StationNo,
LineNo = definition.LineNo,
Description = definition.Description,
IsOutput = definition.Type == IoPointType.Output,
IsOn = definition.DefaultValue,
IsInverse = definition.IsInverse,
CanWrite = definition.Type == IoPointType.Output && definition.Enabled,
};
if (item.IsOutput)
{
var command = new ActionCommand(() => ToggleOutput(item), () => item.CanWrite);
item.ToggleCommand = command;
}
_pointStateMap[item.PointKey] = item;
if (item.IsOutput)
{
moduleState.OutputPoints.Add(item);
}
else
{
moduleState.InputPoints.Add(item);
}
}
state.Modules.Add(moduleState);
}
state.IsOnline = _ioMonitorService.IsOnline;
return state;
}
private async void InitializeLatestPointsAsync()
{
_ioMonitorService.RequestRefresh();
await Task.Delay(150);
RefreshFromCurrentPoints();
}
private void RefreshFromCurrentPoints()
{
var points = _ioMonitorService.LatestPoints.Values.ToList();
ApplyPoints(points);
}
private void OnIoChanged(object sender, EventArgs e)
{
var args = e as DeviceIoChangedEventArgs;
if (args == null)
{
return;
}
ApplyPoints(args.ChangedPoints);
PageState.LastRefreshTime = DateTime.Now;
}
private void ApplyPoints(IEnumerable<DeviceIoPointState> points)
{
if (points == null)
{
return;
}
var hasPoint = false;
foreach (var point in points)
{
if (point == null)
{
continue;
}
hasPoint = true;
DeviceIoPointItemState item;
if (!_pointStateMap.TryGetValue(point.PointKey, out item))
{
continue;
}
item.IsOn = point.Value;
var outputPoint = point as DeviceOutputPointState;
item.CanWrite = outputPoint != null && outputPoint.CanWrite;
var command = item.ToggleCommand as ActionCommand;
if (command != null)
{
command.RaiseCanExecuteChanged();
}
}
if (hasPoint)
{
PageState.LastRefreshTime = DateTime.Now;
}
PageState.IsOnline = hasPoint && _ioMonitorService.IsOnline;
}
private void ToggleOutput(DeviceIoPointItemState item)
{
DeviceIoPointState point;
if (!_ioMonitorService.TryGetPoint(item.PointKey, out point))
{
UpdateWriteResult(item.Name + " 写入失败:未找到输出点。", false);
return;
}
var outputPoint = point as DeviceOutputPointState;
if (outputPoint == null || !outputPoint.CanWrite)
{
UpdateWriteResult(item.Name + " 写入失败:输出点不可写。", false);
return;
}
var targetState = !item.IsOn;
var result = outputPoint.TrySetOutput(targetState);
UpdateWriteResult(item.Name + (result ? (targetState ? " 已切换为 ON。" : " 已切换为 OFF。") : " 写入失败。"), result);
}
private void UpdateWriteResult(string message, bool success)
{
PageState.LastWriteMessage = message;
PageState.IsWriteSuccess = success;
PageState.LastWriteTime = DateTime.Now;
}
}
}

View File

@@ -0,0 +1,114 @@
using MainShell.HeightMeasure.ViewModel;
using MainShell.Models;
using MaxwellFramework.Core.Interfaces;
using Stylet;
using StyletIoC;
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace MainShell.DeviceMaintance.ViewModel
{
public class DeviceMaintanceViewModel : BaseScreen, IPage
{
public string Name => "MenuDeviceMaint";
private const string NEEDLEBASE = "刺晶头维护";
private const string IOMONITOR = "IO监控";
private const string HARDWARETEST = "硬件维护";
private const string LASERCOMPENSATION = "激光补偿";
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))
{
CurrentScreen = _viewModelDict[value.Header];
}
}
}
private Screen _currentScreen;
public Screen CurrentScreen
{
get { return _currentScreen; }
set { SetAndNotify(ref _currentScreen, value); }
}
private NeedleBaseViewModel _needleBaseViewModel;
[Inject]
public NeedleBaseViewModel NeedleBaseViewModel
{
get { return _needleBaseViewModel; }
set { _needleBaseViewModel = value; }
}
private IOMaintanceViewModel _ioMaintanceViewModel;
[Inject]
public IOMaintanceViewModel IOMaintanceViewModel
{
get { return _ioMaintanceViewModel; }
set { _ioMaintanceViewModel = value; }
}
private HardwareTestViewModel _hardwareTestViewModel;
[Inject]
public HardwareTestViewModel HardwareTestViewModel
{
get { return _hardwareTestViewModel; }
set { _hardwareTestViewModel = value; }
}
private LaserCompensationViewModel _laserCompensationViewModel;
[Inject]
public LaserCompensationViewModel LaserCompensationViewModel
{
get { return _laserCompensationViewModel; }
set { _laserCompensationViewModel = value; }
}
public DeviceMaintanceViewModel()
{
InitMenuItems();
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (_viewModelDict.Count == 0)
{
InitViewModelDict();
}
if (SelectedMenuItem == null)
{
SelectedMenuItem = MenuItemWraps[0];
}
}
private void InitMenuItems()
{
MenuItemWraps = new ObservableCollection<MenuItemWrap>
{
new MenuItemWrap() { Header = NEEDLEBASE, Tag = NEEDLEBASE },
new MenuItemWrap() { Header = IOMONITOR, Tag = IOMONITOR },
new MenuItemWrap() { Header = HARDWARETEST, Tag = HARDWARETEST },
new MenuItemWrap() { Header = LASERCOMPENSATION, Tag = LASERCOMPENSATION },
};
}
private void InitViewModelDict()
{
_viewModelDict.Clear();
_viewModelDict.Add(NEEDLEBASE, NeedleBaseViewModel);
_viewModelDict.Add(IOMONITOR, IOMaintanceViewModel);
_viewModelDict.Add(HARDWARETEST, HardwareTestViewModel);
_viewModelDict.Add(LASERCOMPENSATION, LaserCompensationViewModel);
}
}
}

Some files were not shown because too many files have changed in this diff Show More