Files
AohDrllTools/MainWindowViewModel.cs
Mr.Xia 44de1cb982 新增窗口置顶功能并优化sort.txt读取方式
新增窗口“置顶/取消置顶”按钮,支持动态切换窗口置顶状态,按钮文本随状态变化自动更新。优化 sort.txt 文件读取,改用 cmd type 命令读取并按行分割,提升编码兼容性,避免因编码问题导致读取异常。
2025-12-17 19:39:57 +08:00

1346 lines
52 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using DrillTools.Integration;
using System.Diagnostics;
namespace DrillTools
{
/// <summary>
/// 主窗口视图模型
/// </summary>
public class MainWindowViewModel : INotifyPropertyChanged
{
private ObservableCollection<ToolItem> _tools = new();
private ObservableCollection<ToolItem> _originalTools = new(); // 保存原始刀具顺序
private ToolItem? _selectedTool;
private string _drillTapeContent = string.Empty;
private bool _canMoveUp;
private bool _canMoveDown;
private string _originalFilePath = string.Empty;
private bool _isTopmost = true; // 默认置顶
/// <summary>
/// 刀具列表
/// </summary>
public ObservableCollection<ToolItem> Tools
{
get => _tools;
set => SetProperty(ref _tools, value);
}
/// <summary>
/// 选中的刀具
/// </summary>
public ToolItem? SelectedTool
{
get => _selectedTool;
set
{
if (SetProperty(ref _selectedTool, value))
{
UpdateMoveButtonsState();
}
}
}
/// <summary>
/// 钻带内容
/// </summary>
public string DrillTapeContent
{
get => _drillTapeContent;
set => SetProperty(ref _drillTapeContent, value);
}
/// <summary>
/// 是否可以上移
/// </summary>
public bool CanMoveUp
{
get => _canMoveUp;
set => SetProperty(ref _canMoveUp, value);
}
/// <summary>
/// 是否可以下移
/// </summary>
public bool CanMoveDown
{
get => _canMoveDown;
set => SetProperty(ref _canMoveDown, value);
}
/// <summary>
/// 原始文件路径
/// </summary>
public string OriginalFilePath
{
get => _originalFilePath;
set
{
if (SetProperty(ref _originalFilePath, value))
{
// 当原始文件路径改变时,通知 HasOriginalFile 属性也已更改
OnPropertyChanged(nameof(HasOriginalFile));
}
}
}
/// <summary>
/// 原始刀具顺序集合(按文件中的出现顺序)
/// </summary>
public ObservableCollection<ToolItem> OriginalTools
{
get => _originalTools;
set => SetProperty(ref _originalTools, value);
}
/// <summary>
/// 是否有原始文件
/// </summary>
public bool HasOriginalFile => !string.IsNullOrEmpty(OriginalFilePath);
/// <summary>
/// 窗口是否置顶
/// </summary>
public bool IsTopmost
{
get => _isTopmost;
set
{
if (SetProperty(ref _isTopmost, value))
{
// 当IsTopmost改变时通知TopmostButtonText属性也已更改
OnPropertyChanged(nameof(TopmostButtonText));
}
}
}
/// <summary>
/// 置顶按钮显示文本
/// </summary>
public string TopmostButtonText => IsTopmost ? "取消置顶" : "窗口置顶";
/// <summary>
/// 从钻带内容加载刀具信息
/// </summary>
/// <param name="drillTapeContent">钻带内容</param>
public void LoadToolsFromDrillTape(string drillTapeContent)
{
DrillTapeContent = drillTapeContent;
Tools.Clear();
OriginalTools.Clear(); // 清空原始刀具顺序
try
{
// 使用现有的 DrillTapeProcessor 处理钻带数据
var result = DrillTapeProcessor.ProcessDrillTape(drillTapeContent);
if (result.Success)
{
foreach (var toolResult in result.ToolResults)
{
var toolItem = new ToolItem
{
ToolNumber = toolResult.ToolNumber,
Diameter = toolResult.Diameter,
// 孔数相关属性已移除
// RegularHoles = toolResult.RegularHoles;
// SlotHoles = toolResult.SlotHoles;
// TotalHoles = toolResult.TotalHoles;
ToolType = toolResult.ToolType,
ToolSuffixType = toolResult.ToolSuffixType,
ToolCategory = toolResult.ToolCategory,
HoleLocations = toolResult.Locations ?? new List<string>()
};
// 如果是机台码使用DrillTapeProcessor已经解析出的机台码信息
if (toolResult.ToolType == ToolType.MachineCode)
{
toolItem.MachineCodeCommand = toolResult.MachineCodeCommand;
toolItem.MachineCodeType = toolResult.MachineCodeType;
// 添加日志验证机台码信息传递
System.Diagnostics.Debug.WriteLine($"[机台码传递] T{toolResult.ToolNumber:D2}: 命令={toolItem.MachineCodeCommand}, 类型={toolItem.MachineCodeType}");
// 如果 toolResult.Locations 已经包含坐标数据,则使用它
// 否则从原始钻带内容中提取坐标行
if (toolItem.HoleLocations == null || toolItem.HoleLocations.Count == 0)
{
// 从钻带内容中提取机台码坐标行
var toolPattern = $@"%.+?T{toolResult.ToolNumber:D2}(.*?)(?=T\d{{2}}|M30)";
var match = System.Text.RegularExpressions.Regex.Match(DrillTapeContent, toolPattern, System.Text.RegularExpressions.RegexOptions.Singleline);
if (match.Success)
{
string holeSection = match.Groups[1].Value;
// 查找机台码坐标行
var coordinatePattern = @"X([+-]?\d+\.?\d*)Y([+-]?\d+\.?\d*)";
var coordinateMatches = System.Text.RegularExpressions.Regex.Matches(holeSection, coordinatePattern);
toolItem.HoleLocations = new List<string>();
foreach (Match coordMatch in coordinateMatches)
{
toolItem.HoleLocations.Add(coordMatch.Value);
}
System.Diagnostics.Debug.WriteLine($"[机台码坐标] T{toolResult.ToolNumber:D2}: 找到{toolItem.HoleLocations.Count}个坐标");
}
}
}
Tools.Add(toolItem);
// 同时添加到原始刀具顺序集合中
var originalToolItem = new ToolItem
{
ToolNumber = toolResult.ToolNumber,
Diameter = toolResult.Diameter,
ToolType = toolResult.ToolType,
ToolSuffixType = toolResult.ToolSuffixType,
ToolCategory = toolResult.ToolCategory,
HoleLocations = toolResult.Locations ?? new List<string>(),
MachineCodeCommand = toolResult.MachineCodeCommand,
MachineCodeType = toolResult.MachineCodeType
};
OriginalTools.Add(originalToolItem);
}
// 更新按钮状态
UpdateMoveButtonsState();
// 检查并应用-sort.txt文件中的刀具排序
if (!string.IsNullOrEmpty(OriginalFilePath))
{
CheckAndApplySortFile(OriginalFilePath, Tools);
}
}
else
{
System.Windows.MessageBox.Show($"解析钻带失败: {result.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"解析钻带时发生异常: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 重新排序刀具
/// </summary>
/// <param name="oldIndex">原索引</param>
/// <param name="newIndex">新索引</param>
public void ReorderTools(int oldIndex, int newIndex)
{
if (oldIndex < 0 || oldIndex >= Tools.Count || newIndex < 0 || newIndex >= Tools.Count)
return;
var tool = Tools[oldIndex];
// 检查是否是机台码刀具,如果是则不允许移动
if (tool.ToolType == ToolType.MachineCode)
{
System.Windows.MessageBox.Show("机台码刀具不允许移动位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 检查目标位置是否是机台码刀具
var targetTool = Tools[newIndex];
if (targetTool.ToolType == ToolType.MachineCode)
{
System.Windows.MessageBox.Show("不能移动到机台码刀具位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
Tools.RemoveAt(oldIndex);
Tools.Insert(newIndex, tool);
}
/// <summary>
/// 上移选中的刀具
/// </summary>
public void MoveSelectedToolUp()
{
if (SelectedTool == null || !CanMoveUp)
return;
int currentIndex = Tools.IndexOf(SelectedTool);
System.Diagnostics.Debug.WriteLine($"[MoveUp] 开始上移操作,选中项索引: {currentIndex}, 刀具编号: T{SelectedTool.ToolNumber:D2}");
if (currentIndex > 0)
{
// 检查是否是机台码刀具
if (SelectedTool.ToolType == ToolType.MachineCode)
{
System.Windows.MessageBox.Show("机台码刀具不允许移动位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 检查目标位置是否是机台码刀具
var targetTool = Tools[currentIndex - 1];
if (targetTool.ToolType == ToolType.MachineCode)
{
System.Windows.MessageBox.Show("不能移动到机台码刀具位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 保存选中的刀具引用
var selectedTool = SelectedTool;
int newIndex = currentIndex - 1;
// 执行上移操作
Tools.RemoveAt(currentIndex);
Tools.Insert(newIndex, selectedTool);
System.Diagnostics.Debug.WriteLine($"[MoveUp] 上移完成,新索引: {newIndex}");
// 使用延迟方法重新选中移动后的项
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
System.Diagnostics.Debug.WriteLine($"[MoveUp] 延迟设置选中项,刀具编号: T{selectedTool.ToolNumber:D2}");
SelectedTool = selectedTool;
UpdateMoveButtonsState();
}), System.Windows.Threading.DispatcherPriority.Background);
}
}
/// <summary>
/// 下移选中的刀具
/// </summary>
public void MoveSelectedToolDown()
{
if (SelectedTool == null || !CanMoveDown)
return;
int currentIndex = Tools.IndexOf(SelectedTool);
System.Diagnostics.Debug.WriteLine($"[MoveDown] 开始下移操作,选中项索引: {currentIndex}, 刀具编号: T{SelectedTool.ToolNumber:D2}");
if (currentIndex >= 0 && currentIndex < Tools.Count - 1)
{
// 检查是否是机台码刀具
if (SelectedTool.ToolType == ToolType.MachineCode)
{
System.Windows.MessageBox.Show("机台码刀具不允许移动位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 检查目标位置是否是机台码刀具
var targetTool = Tools[currentIndex + 1];
if (targetTool.ToolType == ToolType.MachineCode)
{
System.Windows.MessageBox.Show("不能移动到机台码刀具位置", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 保存选中的刀具引用
var selectedTool = SelectedTool;
int newIndex = currentIndex + 1;
// 执行下移操作
Tools.RemoveAt(currentIndex);
Tools.Insert(newIndex, selectedTool);
System.Diagnostics.Debug.WriteLine($"[MoveDown] 下移完成,新索引: {newIndex}");
// 使用延迟方法重新选中移动后的项
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
System.Diagnostics.Debug.WriteLine($"[MoveDown] 延迟设置选中项,刀具编号: T{selectedTool.ToolNumber:D2}");
SelectedTool = selectedTool;
UpdateMoveButtonsState();
}), System.Windows.Threading.DispatcherPriority.Background);
}
}
/// <summary>
/// 更新上移/下移按钮的状态
/// </summary>
private void UpdateMoveButtonsState()
{
if (SelectedTool == null)
{
CanMoveUp = false;
CanMoveDown = false;
System.Diagnostics.Debug.WriteLine("[UpdateButtons] 没有选中项,禁用所有按钮");
return;
}
int index = Tools.IndexOf(SelectedTool);
CanMoveUp = index > 0;
CanMoveDown = index >= 0 && index < Tools.Count - 1;
System.Diagnostics.Debug.WriteLine($"[UpdateButtons] 选中项索引: {index}, CanMoveUp: {CanMoveUp}, CanMoveDown: {CanMoveDown}");
}
/// <summary>
/// 保存刀具顺序并应用到钻带处理
/// </summary>
/// <returns>重新排序后的钻带内容</returns>
public string ApplyToolOrderToDrillTape()
{
if (string.IsNullOrEmpty(OriginalFilePath))
{
throw new InvalidOperationException("没有原始文件路径,请先加载钻带文件");
}
if (Tools.Count == 0)
{
throw new InvalidOperationException("没有刀具数据,请先加载钻带文件");
}
try
{
// 1. 使用现有的扩展方法生成重新排序后的钻带内容
string reorderedDrillTape = DrillTapeProcessorExtensions.GenerateReorderedDrillTape(DrillTapeContent, Tools.ToList());
// 2. 改进的备份逻辑
if (File.Exists(OriginalFilePath))
{
string backupFilePath = OriginalFilePath + ".bak";
// 检查备份文件是否已存在
if (File.Exists(backupFilePath))
{
var result = System.Windows.MessageBox.Show(
"备份文件已存在,请选择处理方式:\n\n" +
"是(Y):覆盖现有备份文件\n" +
"否(N):创建带时间戳的新备份文件\n" +
"取消:中止保存操作\n\n" +
"提示:选择'否'可以保留之前的备份历史",
"备份选项",
MessageBoxButton.YesNoCancel,
MessageBoxImage.Question);
switch (result)
{
case MessageBoxResult.Yes:
// 覆盖现有备份文件
File.Copy(OriginalFilePath, backupFilePath, true);
break;
case MessageBoxResult.No:
// 创建带时间戳的新备份文件
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string timestampBackupPath = $"{OriginalFilePath}.{timestamp}.bak";
File.Copy(OriginalFilePath, timestampBackupPath);
break;
case MessageBoxResult.Cancel:
// 用户取消操作
throw new OperationCanceledException("用户取消了保存操作");
}
}
else
{
// 备份文件不存在,直接创建
File.Copy(OriginalFilePath, backupFilePath);
}
}
// 3. 将重新排序后的钻带内容写入原始文件使用ANSI编码
Encoding ansiEncoding;
try
{
ansiEncoding = Encoding.GetEncoding(936); // 936是GB2312的代码页
}
catch
{
ansiEncoding = Encoding.Default; // 如果获取失败,使用系统默认编码
}
// 使用StreamWriter写入文件确保编码正确应用
using (var writer = new StreamWriter(OriginalFilePath, false, ansiEncoding))
{
writer.Write(reorderedDrillTape);
writer.Flush(); // 确保所有数据都被写入
}
// 4. 更新当前钻带内容
DrillTapeContent = reorderedDrillTape;
// 5. 生成刀具直径列表文件
GenerateToolDiameterListFile(OriginalFilePath, Tools);
// 6. 打开文件资源管理器并选中文件
OpenFileExplorerAndSelectFile(OriginalFilePath);
return reorderedDrillTape;
}
catch (OperationCanceledException)
{
// 重新抛出取消异常,不包装
throw;
}
catch (Exception ex)
{
throw new InvalidOperationException($"保存钻带文件失败: {ex.Message}", ex);
}
}
/// <summary>
/// 重排刀序并重新编号
/// </summary>
/// <returns>重排后的钻带内容</returns>
public string ReorderAndRenumberTools(bool isApply = false)
{
if (Tools.Count == 0)
{
throw new InvalidOperationException("没有可重排的刀具");
}
// 保存原始刀具列表(按文件中的原始顺序)
var originalTools = OriginalTools.Select(t => new ToolItem
{
ToolNumber = t.ToolNumber,
Diameter = t.Diameter,
ToolType = t.ToolType,
ToolSuffixType = t.ToolSuffixType,
ToolCategory = t.ToolCategory,
HoleLocations = new List<string>(t.HoleLocations ?? new List<string>()),
MachineCodeCommand = t.MachineCodeCommand,
MachineCodeType = t.MachineCodeType
}).ToList();
// 1. 创建原始刀具编号到新编号的映射
// 创建重排后的刀具列表(基于用户已调整的顺序)
// 注意Tools 列表已经反映了用户通过拖放操作调整后的顺序
var reorderedTools = new List<ToolItem>();
reorderedTools = Tools.Select(t => new ToolItem
{
ToolNumber = t.ToolNumber,
Diameter = t.Diameter,
ToolType = t.ToolType,
ToolSuffixType = t.ToolSuffixType,
ToolCategory = t.ToolCategory,
HoleLocations = new List<string>(t.HoleLocations ?? new List<string>()),
MachineCodeCommand = t.MachineCodeCommand,
MachineCodeType = t.MachineCodeType
}).ToList();
//重新排一下 reorderedTools 的刀序Txx
int number = 1;
foreach (var item in reorderedTools)
item.ToolNumber = number++;
//检查是否所有刀序没有变化
int checkindex = 1;
for (int i = 0; i < originalTools.Count; i++)
{
if (reorderedTools[i].ToolNumber == originalTools[i].ToolNumber)
checkindex++;
}
if (checkindex == originalTools.Count)
{
if (isApply)
{
ApplyToolOrderToDrillTape();
return DrillTapeContent;
}
throw new InvalidOperationException("刀具顺序未发生变化,无需重排");
}
// 显示确认窗口
var confirmationWindow = new ToolReorderConfirmationWindow(originalTools, reorderedTools);
confirmationWindow.Owner = System.Windows.Application.Current.MainWindow;
confirmationWindow.ShowDialog();
if (!confirmationWindow.IsConfirmed)
throw new OperationCanceledException("取消刀序重排操作");
// 2. 按当前列表顺序重新编号T01, T02, T03...
var toolNumberMapping = new Dictionary<int, int>();
var originalToNewMapping = new Dictionary<int, int>(); // 保存原始映射关系
int newToolNumber = 1;
foreach (var tool in Tools.ToList())
{
// 机台码刀具不允许重新编号
if (tool.ToolType != ToolType.MachineCode)
{
originalToNewMapping[tool.ToolNumber] = newToolNumber;
toolNumberMapping[tool.ToolNumber] = newToolNumber;
tool.ToolNumber = newToolNumber++;
}
else
{
// 机台码刀具保持原始编号,但也要加入映射
toolNumberMapping[tool.ToolNumber] = tool.ToolNumber;
}
}
// 3. 更新钻带内容中的刀具编号和孔位数据
string updatedDrillTape = UpdateDrillTapeWithNewToolNumbers(DrillTapeContent, toolNumberMapping);
// 4. 更新钻带内容
DrillTapeContent = updatedDrillTape;
// 5. 更新界面显示
OnPropertyChanged(nameof(Tools));
if (isApply)
ApplyToolOrderToDrillTape();
return updatedDrillTape;
}
/// <summary>
/// 使用新的刀具编号更新钻带内容
/// </summary>
/// <param name="originalDrillTape">原始钻带内容</param>
/// <param name="toolNumberMapping">刀具编号映射</param>
/// <returns>更新后的钻带内容</returns>
private string UpdateDrillTapeWithNewToolNumbers(string originalDrillTape, Dictionary<int, int> toolNumberMapping)
{
var lines = originalDrillTape.Split(new[] { '\r', '\n' }, StringSplitOptions.None);
var result = new List<string>();
// 创建新编号到刀具对象的映射,以便获取对应的坐标数据
var newNumberToToolMap = new Dictionary<int, ToolItem>();
foreach (var tool in Tools)
{
if (toolNumberMapping.ContainsValue(tool.ToolNumber))
{
newNumberToToolMap[tool.ToolNumber] = tool;
}
}
// 首先解析原始钻带中的刀具定义行,保存参数信息
var originalToolDefinitions = new Dictionary<int, string>();
var headerLines = new List<string>();
foreach (var line in lines)
{
string trimmedLine = line.Trim();
if (trimmedLine == "%")
{
//headerLines.Add(line);
break;
}
// 处理刀具定义行(如 T01C1.049H05000Z+0.000S060.00F105.0U0700.0
if (Regex.IsMatch(trimmedLine, @"^T(\d+)C"))
{
var match = Regex.Match(trimmedLine, @"^T(\d+)C(.*)$");
if (match.Success)
{
int originalToolNumber = int.Parse(match.Groups[1].Value);
originalToolDefinitions[originalToolNumber] = match.Groups[2].Value;
}
}
else
{
if (line != "")
headerLines.Add(line);
}
}
// 添加头部信息
result.AddRange(headerLines);
// 按照新的刀具编号顺序输出刀具定义部分
var sortedTools = Tools.OrderBy(t => t.ToolNumber).ToList();
foreach (var tool in sortedTools)
{
// 查找原始刀具编号
int originalToolNumber = -1;
foreach (var kvp in toolNumberMapping)
{
if (kvp.Value == tool.ToolNumber)
{
originalToolNumber = kvp.Key;
break;
}
}
if (originalToolNumber != -1 && originalToolDefinitions.ContainsKey(originalToolNumber))
{
string toolDef = $"T{tool.ToolNumber:D2}C{originalToolDefinitions[originalToolNumber]}";
result.Add(toolDef);
}
}
// 添加 % 符号
result.Add("%");
// 处理刀具切换行和坐标数据部分
// 按新刀具编号顺序输出刀具切换行和对应的坐标数据
var sortedToolsForData = Tools.OrderBy(t => t.ToolNumber).ToList();
foreach (var tool in sortedToolsForData)
{
// 添加刀具切换行
result.Add($"T{tool.ToolNumber:D2}");
// 添加该刀具对应的所有坐标数据
if (tool.ToolType != ToolType.MachineCode && tool.HoleLocations != null && tool.HoleLocations.Count > 0)
{
foreach (var location in tool.HoleLocations)
{
result.Add(location);
}
}
// 如果是机台码刀具,添加机台码命令和坐标
if (tool.ToolType == ToolType.MachineCode)
{
if (!string.IsNullOrEmpty(tool.MachineCodeCommand) && !string.IsNullOrEmpty(tool.MachineCodeType))
{
result.Add($"{tool.MachineCodeCommand},{tool.MachineCodeType},$S $N");
// 添加机台码的坐标数据
if (tool.HoleLocations != null && tool.HoleLocations.Count > 0)
{
foreach (var location in tool.HoleLocations)
{
result.Add(location);
}
}
}
}
}
// 添加结束标记
result.Add("M30");
return string.Join("\r\n", result);
}
/// <summary>
/// 加载示例数据
/// </summary>
public void LoadSampleData()
{
// 清空原始文件路径,因为这是示例数据
OriginalFilePath = string.Empty;
Tools.Clear();
OriginalTools.Clear(); // 清空原始刀具顺序
// 添加示例刀具数据
Tools.Add(new ToolItem
{
ToolNumber = 1,
Diameter = 1.049, // 尾号9特殊孔径固定为圆孔
// 孔数相关属性已移除
// RegularHoles = 7,
// SlotHoles = 0,
// TotalHoles = 7,
ToolType = ToolType.Regular,
ToolSuffixType = ToolItem.GetToolSuffixType(1.049), // 特殊孔径
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(1.049)),
HoleLocations = new List<string>
{
"X-167525Y013500",
"X-167525Y018500",
"X-167525Y023500",
"X167525Y013500",
"X167525Y018500",
"X167525Y023500",
"X099366Y502000"
}
});
Tools.Add(new ToolItem
{
ToolNumber = 2,
Diameter = 1.550, // 尾号0 -> 钻针
// 孔数相关属性已移除
// RegularHoles = 5,
// SlotHoles = 0,
// TotalHoles = 5,
ToolType = ToolType.Regular,
ToolSuffixType = ToolItem.GetToolSuffixType(1.550), // 基于孔径计算
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(1.550)),
HoleLocations = new List<string>
{
"X-065975Y115250",
"X-085825Y122450",
"X-085825Y124550",
"X-097425Y115250",
"X103093Y502000"
}
});
Tools.Add(new ToolItem
{
ToolNumber = 3,
Diameter = 1.156, // 尾号6 -> EA型槽刀
// 孔数相关属性已移除
// RegularHoles = 1,
// SlotHoles = 528,
// TotalHoles = 529,
ToolType = ToolType.Slot,
ToolSuffixType = ToolItem.GetToolSuffixType(1.156), // 基于孔径计算
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(1.156)),
HoleLocations = new List<string>
{
"X-069659Y016450G85X-094159Y016450",
"X-181341Y195550G85X-156841Y195550",
"X-069659Y210450G85X-094159Y210450",
"X-181341Y389550G85X-156841Y389550",
"X-069659Y404450G85X-094159Y404450",
"X-181341Y583550G85X-156841Y583550",
"X162939Y596000"
}
});
Tools.Add(new ToolItem
{
ToolNumber = 4,
Diameter = 1.451, // 尾号1 -> 槽刀
// 孔数相关属性已移除
// RegularHoles = 57,
// SlotHoles = 0,
// TotalHoles = 57,
ToolType = ToolType.Regular,
ToolSuffixType = ToolItem.GetToolSuffixType(1.451), // 基于孔径计算
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(1.451)),
HoleLocations = new List<string>
{
"X-065975Y115250",
"X-085825Y122450",
"X-085825Y124550",
"X-097425Y115250",
"X103093Y502000"
}
});
Tools.Add(new ToolItem
{
ToolNumber = 5,
Diameter = 1.153, // 尾号3 -> 粉尘刀
// 孔数相关属性已移除
// RegularHoles = 10,
// SlotHoles = 0,
// TotalHoles = 10,
ToolType = ToolType.Regular,
ToolSuffixType = ToolItem.GetToolSuffixType(1.153), // 基于孔径计算
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(1.153)),
HoleLocations = new List<string>
{
"X-065975Y115250",
"X-085825Y122450",
"X-085825Y124550",
"X-097425Y115250",
"X103093Y502000"
}
});
Tools.Add(new ToolItem
{
ToolNumber = 6,
Diameter = 0.499, // 特殊孔径固定为圆孔(机台码)
// 孔数相关属性已移除
// RegularHoles = 57,
// SlotHoles = 0,
// TotalHoles = 57,
ToolType = ToolType.MachineCode,
MachineCodeCommand = "M97",
MachineCodeType = "A*",
ToolSuffixType = ToolItem.GetToolSuffixType(0.499), // 特殊孔径
ToolCategory = ToolItem.GetToolCategory(ToolItem.GetToolSuffixType(0.499)),
HoleLocations = new List<string> { "X-194000Y002000" } // 机台码刀具的坐标数据
});
// 同时添加到原始刀具顺序集合中
foreach (var tool in Tools)
{
var originalToolItem = new ToolItem
{
ToolNumber = tool.ToolNumber,
Diameter = tool.Diameter,
ToolType = tool.ToolType,
ToolSuffixType = tool.ToolSuffixType,
ToolCategory = tool.ToolCategory,
HoleLocations = new List<string>(tool.HoleLocations ?? new List<string>()),
MachineCodeCommand = tool.MachineCodeCommand,
MachineCodeType = tool.MachineCodeType
};
OriginalTools.Add(originalToolItem);
}
// 更新按钮状态
UpdateMoveButtonsState();
// 加载示例钻带内容
DrillTapeContent = @"M48
;厚铜板参数-镀膜-EA-250618
T01C1.049H05000Z+0.000S060.00F105.0U0700.0
T02C1.550H01500Z+0.150S070.00F008.0U0800.0
T03C1.156H03000Z-0.200S040.00F030.0U0900.0
T04C1.451H01500Z+0.150S070.00F008.0U0800.0
T05C1.153H01500Z+0.150S070.00F008.0U0800.0
T06C0.499H01500Z+0.150S070.00F008.0U0800.0
%
T01
X-167525Y013500
X-167525Y018500
X-167525Y023500
X167525Y013500
X167525Y018500
X167525Y023500
X099366Y502000
T02
X-065975Y115250
X-085825Y122450
X-085825Y124550
X-097425Y115250
X103093Y502000
T03
X-069659Y016450G85X-094159Y016450
X-181341Y195550G85X-156841Y195550
X-069659Y210450G85X-094159Y210450
X-181341Y389550G85X-156841Y389550
X-069659Y404450G85X-094159Y404450
X-181341Y583550G85X-156841Y583550
X162939Y596000
T04
X-065975Y115250
X-085825Y122450
X-085825Y124550
X-097425Y115250
X103093Y502000
T05
X-065975Y115250
X-085825Y122450
X-085825Y124550
X-097425Y115250
X103093Y502000
T06
M97,A*,$S $N
X-194000Y002000
M30";
}
public event PropertyChangedEventHandler? PropertyChanged;
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void VerifyResults(DrillTapeResult result)
{
// CAM350的预期结果
var expectedResults = new[]
{
new { ToolNumber = 1, Diameter = 0.799, ExpectedHoles = 7 },
new { ToolNumber = 2, Diameter = 0.656, ExpectedHoles = 5 },
new { ToolNumber = 3, Diameter = 1.601, ExpectedHoles = 529 },
new { ToolNumber = 4, Diameter = 0.499, ExpectedHoles = 57 }
};
bool allMatch = true;
foreach (var expected in expectedResults)
{
var actual = result.ToolResults.Find(t => t.ToolNumber == expected.ToolNumber);
if (actual != null)
{
bool match = Math.Abs(actual.Diameter - expected.Diameter) < 0.001 &&
actual.TotalHoles == expected.ExpectedHoles;
Console.WriteLine($"T{expected.ToolNumber:D2} ({expected.Diameter:F3}mm): " +
$"预期={expected.ExpectedHoles}, 实际={actual.TotalHoles}, " +
$"{(match ? "" : "")}");
if (!match)
allMatch = false;
}
else
{
Console.WriteLine($"T{expected.ToolNumber:D2}: 未找到结果 ✗");
allMatch = false;
}
}
Console.WriteLine($"\n总体结果: {(allMatch ? " CAM350一致" : " ")}");
}
/// <summary>
/// 显示刀具详情窗口
/// </summary>
/// <param name="tool">要显示详情的刀具</param>
public void ShowToolDetail(ToolItem tool)
{
if (tool == null)
return;
try
{
var detailWindow = new ToolDetailWindow(tool);
detailWindow.Owner = System.Windows.Application.Current.MainWindow;
detailWindow.ShowDialog();
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"显示刀具详情失败: {ex.Message}", "错误",
System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Error);
}
}
/// <summary>
/// 测试重排刀序功能
/// </summary>
public void TestReorderAndRenumberTools()
{
Console.WriteLine("=== 重排刀序功能测试 ===");
// 1. 加载测试数据
LoadSampleData();
// 2. 记录原始状态
var originalOrder = Tools.Select(t => new
{
t.ToolNumber,
t.Diameter,
LocationCount = t.HoleLocations?.Count ?? 0,
FirstLocation = t.HoleLocations?.FirstOrDefault() ?? ""
}).ToList();
Console.WriteLine("原始刀具顺序:");
foreach (var tool in originalOrder)
{
Console.WriteLine($"T{tool.ToolNumber:D2}({tool.Diameter:F3}): {tool.LocationCount}个坐标, 首个坐标: {tool.FirstLocation}");
}
// 3. 执行重排操作将T02移到第一位
if (Tools.Count >= 2)
{
var toolToMove = Tools.FirstOrDefault(t => t.ToolNumber == 2);
if (toolToMove != null && toolToMove.ToolType != ToolType.MachineCode)
{
int oldIndex = Tools.IndexOf(toolToMove);
ReorderTools(oldIndex, 0);
}
}
// 4. 执行重新编号
string reorderedDrillTape = ReorderAndRenumberTools();
// 5. 验证结果
Console.WriteLine("\n重排后刀具顺序:");
var reorderedOrder = Tools.Select(t => new
{
t.ToolNumber,
t.Diameter,
LocationCount = t.HoleLocations?.Count ?? 0,
FirstLocation = t.HoleLocations?.FirstOrDefault() ?? ""
}).ToList();
foreach (var tool in reorderedOrder)
{
Console.WriteLine($"T{tool.ToolNumber:D2}({tool.Diameter:F3}): {tool.LocationCount}个坐标, 首个坐标: {tool.FirstLocation}");
}
// 6. 验证钻带内容
Console.WriteLine("\n重排后的钻带内容:");
Console.WriteLine(reorderedDrillTape);
// 7. 验证坐标跟随
VerifyCoordinateBinding(originalOrder.ToList<dynamic>(), reorderedOrder.ToList<dynamic>());
Console.WriteLine("\n重排刀序功能测试完成");
}
/// <summary>
/// 验证坐标绑定关系
/// </summary>
private void VerifyCoordinateBinding(List<dynamic> originalOrder, List<dynamic> reorderedOrder)
{
Console.WriteLine("\n=== 验证坐标绑定关系 ===");
// 创建直径到坐标的映射
var originalDiameterToLocation = new Dictionary<double, string>();
foreach (var tool in originalOrder)
{
originalDiameterToLocation[tool.Diameter] = tool.FirstLocation;
}
// 验证重排后的坐标绑定
bool allMatch = true;
foreach (var tool in reorderedOrder)
{
if (originalDiameterToLocation.ContainsKey(tool.Diameter))
{
string originalLocation = originalDiameterToLocation[tool.Diameter];
if (tool.FirstLocation != originalLocation)
{
Console.WriteLine($"✗ 刀具{tool.Diameter:F3}的坐标不匹配: 原始{originalLocation}, 重排后{tool.FirstLocation}");
allMatch = false;
}
else
{
Console.WriteLine($"✓ 刀具{tool.Diameter:F3}的坐标正确跟随: {tool.FirstLocation}");
}
}
}
Console.WriteLine($"\n坐标绑定验证结果: {(allMatch ? " " : " ")}");
}
/// <summary>
/// 打开文件资源管理器并选中指定文件
/// </summary>
/// <param name="filePath">要选中的文件路径</param>
private void OpenFileExplorerAndSelectFile(string filePath)
{
try
{
if (!File.Exists(filePath))
{
System.Windows.MessageBox.Show($"文件不存在: {filePath}", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
// 使用 explorer.exe 的 /select 参数来打开文件资源管理器并选中文件
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = "explorer.exe",
Arguments = $"/select,\"{filePath}\"",
UseShellExecute = true
};
Process.Start(startInfo);
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"打开文件资源管理器失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// 生成刀具直径列表文件
/// </summary>
/// <param name="originalFilePath">原始文件路径</param>
/// <param name="tools">刀具列表</param>
private void GenerateToolDiameterListFile(string originalFilePath, ObservableCollection<ToolItem> tools)
{
try
{
// 获取原文件的目录和文件名(不含扩展名)
string directory = Path.GetDirectoryName(originalFilePath) ?? "";
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(originalFilePath);
string outputFilePath = Path.Combine(directory, $"{fileNameWithoutExt}-sort.txt");
// 按刀具编号排序获取最终顺序
var sortedTools = tools.OrderBy(t => t.ToolNumber).ToList();
// 生成刀具直径列表内容
var diameterList = new StringBuilder();
foreach (var tool in sortedTools)
{
diameterList.AppendLine($"{tool.Diameter:F3}");
}
// 使用ANSI编码写入文件
Encoding ansiEncoding;
try
{
ansiEncoding = Encoding.GetEncoding(936); // 936是GB2312的代码页
}
catch
{
ansiEncoding = Encoding.Default; // 如果获取失败,使用系统默认编码
}
File.WriteAllText(outputFilePath, diameterList.ToString(), ansiEncoding);
// 记录日志
System.Diagnostics.Debug.WriteLine($"刀具直径列表文件已生成: {outputFilePath}");
}
catch (Exception ex)
{
// 记录错误但不影响主要功能
System.Diagnostics.Debug.WriteLine($"生成刀具直径列表文件失败: {ex.Message}");
}
}
/// <summary>
/// 检查并应用-sort.txt文件中的刀具排序
/// </summary>
/// <param name="originalFilePath">原始文件路径</param>
/// <param name="tools">当前刀具列表</param>
private void CheckAndApplySortFile(string originalFilePath, ObservableCollection<ToolItem> tools)
{
try
{
// 获取-sort.txt文件路径
string directory = Path.GetDirectoryName(originalFilePath) ?? "";
string fileNameWithoutExt = Path.GetFileNameWithoutExtension(originalFilePath);
string sortFilePath = Path.Combine(directory, $"{fileNameWithoutExt}-sort.txt");
// 检查-sort.txt文件是否存在
if (!File.Exists(sortFilePath))
return;
// 读取-sort.txt文件内容
List<double> sortDiameters = ReadSortFile(sortFilePath);
if (sortDiameters.Count == 0)
return;
// 检查直径数量是否一致
if (sortDiameters.Count != tools.Count)
return;
// 提示用户是否应用排序
var result = System.Windows.MessageBox.Show(
$"检测到之前刀序重排后的记录文件:{Path.GetFileName(sortFilePath)}\n\n" +
$"是否直接按该记录文件进行排序?\n\n" +
"是(Y):按记录文件排序\n" +
"否(N):保持当前顺序",
"刀具排序提示",
MessageBoxButton.YesNo,
MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
// 按照-sort.txt文件中的直径顺序重新排列刀具
SortToolsByDiameterList(tools, sortDiameters);
// 执行完整的重排刀序流程,包括重新编号和更新钻带内容
try
{
string reorderedDrillTape = ReorderAndRenumberTools();
System.Diagnostics.Debug.WriteLine($"已按-sort.txt文件重新排序刀具并更新钻带内容");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"重排刀序失败: {ex.Message}");
// 即使重排失败,也保持列表的排序
OnPropertyChanged(nameof(Tools));
}
}
}
catch (Exception ex)
{
// 记录错误但不影响主要功能
System.Diagnostics.Debug.WriteLine($"检查-sort.txt文件失败: {ex.Message}");
}
}
/// <summary>
/// 读取-sort.txt文件内容并解析直径列表
/// </summary>
/// <param name="sortFilePath">-sort.txt文件路径</param>
/// <returns>直径列表</returns>
private List<double> ReadSortFile(string sortFilePath)
{
var diameters = new List<double>();
try
{
// 使用ANSI编码
Encoding ansiEncoding;
try
{
ansiEncoding = Encoding.GetEncoding(936); // 936是GB2312的代码页
}
catch
{
ansiEncoding = Encoding.Default; // 如果获取失败,使用系统默认编码
}
// 使用 cmd 命令读取-sort.txt文件参考钻带文件读取方法
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/c type \"{sortFilePath}\"",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
StandardOutputEncoding = ansiEncoding
}
};
process.Start();
string fileContent = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// 按行分割内容
string[] lines = fileContent.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string line in lines)
{
string trimmedLine = line.Trim();
if (!string.IsNullOrEmpty(trimmedLine))
{
if (double.TryParse(trimmedLine, out double diameter))
{
diameters.Add(diameter);
}
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"读取-sort.txt文件失败: {ex.Message}");
}
return diameters;
}
/// <summary>
/// 根据直径列表排序刀具
/// </summary>
/// <param name="tools">刀具列表</param>
/// <param name="diameterList">直径列表(目标顺序)</param>
private void SortToolsByDiameterList(ObservableCollection<ToolItem> tools, List<double> diameterList)
{
// 创建直径到刀具的映射
var diameterToTools = new Dictionary<double, List<ToolItem>>();
foreach (var tool in tools)
{
double roundedDiameter = Math.Round(tool.Diameter, 3);
if (!diameterToTools.ContainsKey(roundedDiameter))
{
diameterToTools[roundedDiameter] = new List<ToolItem>();
}
diameterToTools[roundedDiameter].Add(tool);
}
// 按照直径列表的顺序重新排列刀具
var sortedTools = new List<ToolItem>();
foreach (double diameter in diameterList)
{
double roundedDiameter = Math.Round(diameter, 3);
if (diameterToTools.ContainsKey(roundedDiameter))
{
sortedTools.AddRange(diameterToTools[roundedDiameter]);
}
}
// 清空原列表并添加排序后的刀具
tools.Clear();
foreach (var tool in sortedTools)
{
tools.Add(tool);
}
}
/// <summary>
/// 切换窗口置顶状态
/// </summary>
public void ToggleTopmost()
{
IsTopmost = !IsTopmost;
}
}
}