using ATS.Logic;
using ATS.Models;
using ATS.Tools;
using ATS.Windows;
using Common.Attributes;
using GongSolutions.Wpf.DragDrop.Utilities;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using static ATS.Models.ParameterModel;
using static Common.Attributes.ATSCommandAttribute;
using Path = System.IO.Path;
namespace ATS.Views
{
///
/// CommandTreeView.xaml 的交互逻辑
///
public partial class CommandTreeView : UserControl
{
#region 变量声明
public static CommandTreeView? Instance { get; private set; }
public ObservableCollection Assemblies { get; set; } = [];
public ObservableCollection SubPrograms { get; set; } = [];
public ProgramModel Program => MainWindow.Instance.Program ?? new();
private Dictionary _treeViewItemMap = [];
private TreeViewItem _subProgramRootNode;
private readonly Dictionary _xmlDocumentCache = [];
#endregion
public CommandTreeView()
{
InitializeComponent();
Instance = this;
DataContext = this;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Directory.CreateDirectory(SystemConfig.Instance.DLLFilePath);
Directory.CreateDirectory(SystemConfig.Instance.SubProgramFilePath);
LoadAllAssemblies();
LoadSubPrograms();
LoadInstructionsToTreeView();
}
#region 辅助方法
///
/// 加载指定目录下的所有DLL
///
private void LoadAllAssemblies()
{
Assemblies.Clear();
foreach (var dllPath in Directory.GetFiles(SystemConfig.Instance.DLLFilePath, "*.dll"))
{
try
{
var assembly = Assembly.LoadFrom(dllPath);
Assemblies.Add(assembly);
// 加载对应的XML注释文件
string xmlPath = Path.ChangeExtension(dllPath, ".xml");
if (File.Exists(xmlPath))
{
try
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlPath);
_xmlDocumentCache[assembly.FullName!] = xmlDoc;
}
catch (Exception xmlEx)
{
Log.Warning($"加载XML注释失败: {Path.GetFileName(xmlPath)} - {xmlEx.Message}");
}
}
}
catch (Exception ex)
{
Log.Warning($"无法加载程序集 {Path.GetFileName(dllPath)}: {ex.Message}");
}
}
}
// 添加获取注释的方法
private string? GetMethodDocumentation(MethodInfo method)
{
if (method.DeclaringType == null) return null;
try
{
string assemblyName = method.DeclaringType.Assembly.FullName!;
if (!_xmlDocumentCache.TryGetValue(assemblyName, out XmlDocument? xmlDoc)) return null;
// 生成XML文档中的成员ID
string memberName = $"M:{method.DeclaringType.FullName}.{method.Name}";
var parameters = method.GetParameters();
if (parameters.Length > 0)
{
memberName += "(" + string.Join(",", parameters.Select(p => p.ParameterType.FullName)) + ")";
}
// 查找注释节点
XmlNode memberNode = xmlDoc.SelectSingleNode($"//member[@name='{memberName}']")!;
if (memberNode == null) return null;
// 获取摘要(summary)
var summaryNode = memberNode.SelectSingleNode("summary");
string documentation = "";
if (summaryNode != null)
{
documentation += CleanXmlContent(summaryNode.InnerXml);
}
// 获取参数注释(param)
var paramNodes = memberNode.SelectNodes("param");
if (paramNodes != null && paramNodes.Count > 0)
{
documentation += "\n\n参数:";
foreach (XmlNode paramNode in paramNodes)
{
string? paramName = paramNode.Attributes?["name"]?.Value;
if (!string.IsNullOrEmpty(paramName))
{
documentation += $"\n • {paramName}: {CleanXmlContent(paramNode.InnerXml)}";
}
}
}
// 获取返回值注释(returns)
var returnsNode = memberNode.SelectSingleNode("returns");
if (returnsNode != null)
{
documentation += $"\n\n返回值: {CleanXmlContent(returnsNode.InnerXml)}";
}
return string.IsNullOrWhiteSpace(documentation)
? null
: System.Net.WebUtility.HtmlDecode(documentation.Trim());
}
catch (Exception ex)
{
Log.Warning($"获取注释失败: {method.Name} - {ex.Message}");
return null;
}
}
// 辅助方法:清理XML内容
private string CleanXmlContent(string xmlContent)
{
return xmlContent
.Replace(" ", "")
.Replace("", "\n")
.Replace(" ", "")
.Replace(" ", "")
.Replace("", "") // 处理代码标签
.Replace(" ", "")
.Replace("", "")
.Replace("", "")
.Trim();
}
// 子程序加载方法
private void LoadSubPrograms()
{
SubPrograms.Clear();
if (!Directory.Exists(SystemConfig.Instance.SubProgramFilePath))
{
Directory.CreateDirectory(SystemConfig.Instance.SubProgramFilePath);
return;
}
foreach (var filePath in Directory.GetFiles(SystemConfig.Instance.SubProgramFilePath, "*.ats"))
{
try
{
SubPrograms.Add(new SubProgramItem
{
Name = Path.GetFileNameWithoutExtension(filePath),
FilePath = filePath
});
}
catch (Exception ex)
{
Log.Warning($"加载子程序错误: {filePath} - {ex.Message}");
}
}
}
// ATS/Views/CommandTreeView.xaml.cs - 修正 LoadInstructionsToTreeView 方法
///
/// 加载指令集到TreeView
///
private void LoadInstructionsToTreeView()
{
InstructionTreeView.Items.Clear();
_treeViewItemMap.Clear(); // 清空旧的映射
// 添加控制指令节点
var controlRootNode = new TreeViewItem
{
Header = "系统指令",
Tag = "ControlRoot"
};
InstructionTreeView.Items.Add(controlRootNode);
// 添加循环开始节点
var loopStartNode = new TreeViewItem
{
Header = "循环开始",
Tag = "循环开始"
};
controlRootNode.Items.Add(loopStartNode);
// 添加循环结束节点
var loopEndNode = new TreeViewItem
{
Header = "循环结束",
Tag = "循环结束"
};
controlRootNode.Items.Add(loopEndNode);
// 创建子程序根节点
_subProgramRootNode = new TreeViewItem
{
Header = "子程序",
Tag = "SubProgramRoot"
};
InstructionTreeView.Items.Add(_subProgramRootNode);
// 添加子程序节点
foreach (var subProgram in SubPrograms)
{
var subProgramNode = new TreeViewItem
{
Header = subProgram.Name,
Tag = subProgram,
ToolTip = subProgram.FilePath
};
_subProgramRootNode.Items.Add(subProgramNode);
}
// 添加dll中的方法指令 - 按分类组织
foreach (var assembly in Assemblies)
{
List validTypes = new List();
try
{
// 类型过滤条件
var types = assembly.GetTypes().Where(t =>
t.IsPublic &&
!t.IsNested &&
(t.IsClass || t.IsValueType) &&
(!t.IsAbstract || t.IsSealed) &&
t.GetCustomAttribute() != null);
foreach (var type in types)
{
if (type.GetCustomAttribute()?.Browsable == false)
continue;
// 检查类型是否有有效方法(包括继承的方法)
var allMethods = new HashSet();
GetPublicMethods(type, allMethods);
if (allMethods.Count > 0)
{
validTypes.Add(type);
}
}
}
catch (Exception ex)
{
Log.Error($"加载类型错误: {assembly.FullName} - {ex.Message}");
}
// 只有当程序集包含有效类型时才添加节点
if (validTypes.Count > 0)
{
var assemblyNode = new TreeViewItem
{
Header = assembly.GetName().Name,
Tag = assembly
};
InstructionTreeView.Items.Add(assemblyNode);
_treeViewItemMap[assembly] = assemblyNode;
// 按分类组织类型 - 修改这部分逻辑
foreach (var type in validTypes)
{
// 获取该类型的所有 DeviceCategoryAttribute 实例
var categoryAttributes = type.GetCustomAttributes().ToArray();
// 获取所有分类名称,如果没有则归为 "未分类"
string[] categories = categoryAttributes.Length > 0 ?
categoryAttributes.Select(attr => attr.Category).ToArray() :
new[] { "未分类" };
// 获取该类型的所有公共方法,供后续在不同分类下复用
var allMethods = new HashSet();
GetPublicMethods(type, allMethods);
// 为该类型的每个分类创建节点
foreach (var category in categories)
{
// 检查该程序集下是否已存在该分类节点
var categoryNode = assemblyNode.Items.OfType()
.FirstOrDefault(item => item.Header?.ToString() == category);
// 如果不存在,则创建新的分类节点
if (categoryNode == null)
{
categoryNode = new TreeViewItem
{
Header = category,
Tag = $"Category_{category}"
};
assemblyNode.Items.Add(categoryNode);
}
// 为当前分类创建一个 typeNode 实例
var typeNode = new TreeViewItem
{
Header = type.Name,
Tag = type, // Tag 仍然指向原始类型,方便后续处理
ToolTip = type.FullName
};
// 添加到当前分类节点
categoryNode.Items.Add(typeNode);
// 重要:将这个 *特定的* TreeViewItem 实例映射到 *原始的* Type 对象
// 这样在双击时可以通过 SelectedItem.Tag 找到原始 Type
// 如果有多个分类,最后添加的那个分类下的 typeNode 会覆盖前面的映射
// 如果需要精确映射到具体哪个分类下的节点,需要更复杂的结构
_treeViewItemMap[type] = typeNode;
// 为当前分类下的 typeNode 创建方法节点
foreach (var method in allMethods)
{
// 跳过特殊名称方法(属性访问器等)
if (method.IsSpecialName) continue;
// 跳过系统对象方法
if (method.DeclaringType == typeof(object)) continue;
// 跳过特定名称的系统方法
string[] ignoreMethods = { "GetType", "ToString", "Equals", "GetHashCode" };
if (ignoreMethods.Contains(method.Name)) continue;
// 跳过不可浏览的方法
if (method.GetCustomAttribute()?.Browsable == false) continue;
// 对于静态类,只显示静态方法
if (type.IsAbstract && type.IsSealed && !method.IsStatic) continue;
var parameters = method.GetParameters();
var paramText = string.Join(", ", parameters.Select(p => $"{p.ParameterType.Name} {p.Name}"));
// 获取方法注释
string summary = GetMethodDocumentation(method) ?? "";
string tooltipContent = $"{method.DeclaringType?.FullName}.{method.Name}";
if (!string.IsNullOrEmpty(summary)) tooltipContent = $"{tooltipContent}\n\n{summary}";
// 为当前分类下的 typeNode 创建一个 methodNode 实例
var methodNode = new TreeViewItem
{
Header = $"{method.Name}({paramText})",
Tag = new MethodInfoWrapper(method),
ToolTip = tooltipContent
};
// 添加到当前分类下的 typeNode
typeNode.Items.Add(methodNode);
// 重要:将这个 *特定的* TreeViewItem 实例映射到 *原始的* MethodInfo 对象
// 如果有多个分类,最后添加的那个分类下的 methodNode 会覆盖前面的映射
// 如果需要精确映射到具体哪个分类下的节点,需要更复杂的结构
_treeViewItemMap[method] = methodNode;
}
}
}
}
}
}
///
/// 递归获取类型的所有公共方法(包括继承的方法),但跳过被重写的方法
///
/// 要处理的目标类型
/// 存储方法的集合
private void GetPublicMethods(Type type, HashSet methods)
{
// 获取当前类型的所有公共方法(包括继承的)
var allMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
.Where(m => !m.IsSpecialName &&
m.DeclaringType != typeof(object))
.ToList();
// 按方法签名分组
var groupedMethods = allMethods
.GroupBy(m => new { m.Name, Parameters = string.Join(",", m.GetParameters().Select(p => p.ParameterType.FullName)) });
foreach (var group in groupedMethods)
{
// 从组中选择声明类型最接近当前类型(即继承层次最深)的方法
MethodInfo? selectedMethod = null;
int minDepth = int.MaxValue;
foreach (var method in group)
{
// 计算声明类型的深度
int depth = 0;
Type? current = type;
Type declaringType = method.DeclaringType!;
while (current != null && current != declaringType)
{
depth++;
current = current.BaseType;
}
// 如果找到声明类型且在继承链上
if (current == declaringType)
{
if (selectedMethod == null || depth < minDepth)
{
selectedMethod = method;
minDepth = depth;
}
}
}
if (selectedMethod != null)
{
methods.Add(selectedMethod);
}
}
}
private void ReOrderProgramList()
{
for (int i = 0; i < Program.StepCollection.Count; i++)
{
Program.StepCollection[i].Index = i + 1;
}
}
private void UnselectTreeViewItems(ItemsControl itemsControl)
{
for (int i = 0; i < itemsControl.Items.Count; i++)
{
var item = itemsControl.Items[i];
if (itemsControl.ItemContainerGenerator.ContainerFromItem(item) is TreeViewItem container)
{
container.IsSelected = false;
UnselectTreeViewItems(container);
}
}
}
#region 指令添加
private void AddMethodToProgram(MethodInfo method, int insertIndex = -1)
{
try
{
var newStep = new StepModel
{
Name = method.Name,
StepType = "方法",
Method = new MethodModel
{
FullName = method.DeclaringType?.FullName,
Name = method.Name
}
};
// 添加输入参数
foreach (var param in method.GetParameters())
{
newStep.Method.Parameters.Add(new ParameterModel
{
Name = param.Name!,
Type = param.ParameterType,
Category = ParameterCategory.Input
});
}
// 添加输出参数(返回值)
Type returnType = method.ReturnType;
if (returnType == typeof(Task))
{
// 不添加输出参数(无返回值)
}
else if (returnType.IsGenericType &&
returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
// 提取实际返回类型(如 Task -> bool)
Type actualType = returnType.GetGenericArguments()[0];
newStep.Method.Parameters.Add(new ParameterModel
{
Name = "Result",
Type = actualType, // 使用实际类型
Category = ParameterCategory.Output
});
}
else if (returnType != typeof(void))
{
// 同步方法正常添加
newStep.Method.Parameters.Add(new ParameterModel
{
Name = "Result",
Type = returnType,
Category = ParameterCategory.Output
});
}
// 添加到程序
if (insertIndex >= 0 && insertIndex <= Program.StepCollection.Count)
{
Program.StepCollection.Insert(insertIndex, newStep);
}
else
{
Program.StepCollection.Add(newStep);
}
ReOrderProgramList();
}
catch (Exception ex)
{
Log.Error($"添加方法失败: {method.Name} - {ex.Message}");
}
}
private void AddSubProgramToProgram(SubProgramItem subProgram, int insertIndex = -1)
{
try
{
var newStep = new StepModel
{
Name = subProgram.Name,
StepType = "子程序"
};
var jsonstr = File.ReadAllText($"{subProgram.FilePath}");
var tmp = JsonConvert.DeserializeObject(jsonstr);
if (tmp != null)
{
if (tmp.Devices != null && tmp.Devices.Count > 0)
{
foreach (var device in tmp.Devices)
{
_ = DeviceConnect.InitAndConnectDevice(tmp, device);
}
}
newStep.SubProgram = tmp;
}
// 添加到程序
if (insertIndex >= 0 && insertIndex <= Program.StepCollection.Count)
{
Program.StepCollection.Insert(insertIndex, newStep);
}
else
{
Program.StepCollection.Add(newStep);
}
ReOrderProgramList();
}
catch (Exception ex)
{
Log.Error($"添加子程序失败: {subProgram.Name} - {ex.Message}");
}
}
private void AddLoopStartStep(int insertIndex = -1)
{
var newStep = new StepModel
{
Name = "循环开始",
StepType = "循环开始",
LoopCount = 1,
Method = new() { Parameters = [new() { Name = "循环次数", Type = typeof(int), Category = ParameterCategory.Input }] }
};
if (insertIndex >= 0)
{
Program.StepCollection.Insert(insertIndex, newStep);
}
else
{
Program.StepCollection.Add(newStep);
}
ReOrderProgramList();
}
private void AddLoopEndStep(int insertIndex = -1)
{
// 查找最近的未匹配循环开始
StepModel? lastUnmatchedLoopStart = null;
for (int i = Program.StepCollection.Count - 1; i >= 0; i--)
{
if (Program.StepCollection[i].StepType == "循环开始")
{
bool isMatched = Program.StepCollection.Any(s => s.StepType == "循环结束" && s.LoopStartStepId == Program.StepCollection[i].ID);
if (!isMatched)
{
lastUnmatchedLoopStart = Program.StepCollection[i];
break;
}
}
}
var newStep = new StepModel
{
Name = "循环结束",
StepType = "循环结束",
LoopStartStepId = lastUnmatchedLoopStart?.ID
};
if (insertIndex >= 0)
{
Program.StepCollection.Insert(insertIndex, newStep);
}
else
{
Program.StepCollection.Add(newStep);
}
ReOrderProgramList();
}
#endregion
#endregion
#region 私有类
private class MethodInfoWrapper
{
public MethodInfo Method { get; }
public MethodInfoWrapper(MethodInfo method)
{
Method = method;
}
}
#endregion
private void InstructionTreeView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (MainWindow.Instance.User.Role < 1)
{
MessageBox.Show("当前登录用户无权限");
return;
}
if (InstructionTreeView.SelectedItem is TreeViewItem selectedItem)
{
int insertIndex = -1;
if (StepsManager.Instance!.SelectedIndex >= 0)
{
insertIndex = StepsManager.Instance.SelectedIndex + 1;
}
if (selectedItem.Tag is string tagString)
{
if (tagString == "循环开始")
{
AddLoopStartStep(insertIndex);
}
else if (tagString == "循环结束")
{
AddLoopEndStep(insertIndex);
}
}
else if (selectedItem.Tag is MethodInfoWrapper wrapper)
{
AddMethodToProgram(wrapper.Method, insertIndex);
}
else if (selectedItem.Tag is MethodInfo methodInfo)
{
AddMethodToProgram(methodInfo, insertIndex);
}
else if (selectedItem.Tag is SubProgramItem subProgram)
{
AddSubProgramToProgram(subProgram, insertIndex);
}
StepsManager.Instance.SelectedIndex = insertIndex;
UnselectTreeViewItems(InstructionTreeView);
}
}
private void RelodCommand_Click(object sender, RoutedEventArgs e)
{
LoadAllAssemblies();
LoadSubPrograms();
LoadInstructionsToTreeView();
}
// 在类中添加变量
private Dictionary _originalBrushes = new();
private HashSet _matchedItems = new();
// 添加搜索方法
private void SearchTreeView(string searchText)
{
// 清除上次搜索结果
ClearSearchHighlighting();
if (string.IsNullOrWhiteSpace(searchText)) ClearSearchButton_Click(null, null);
searchText = searchText.Trim().ToLower();
// 遍历所有节点
foreach (var item in InstructionTreeView.Items.OfType())
{
SearchTreeViewItem(item, searchText);
}
// 展开所有包含匹配项的节点
foreach (var matchedItem in _matchedItems.ToList())
{
ExpandParents(matchedItem);
}
}
private void SearchTreeViewItem(TreeViewItem item, string searchText)
{
// 检查当前节点是否匹配
bool isMatch = false;
if (string.IsNullOrEmpty(searchText))
{
}
else if (item.Header is string headerText && headerText.Split("(")[0].ToLower().Contains(searchText))
{
isMatch = true;
}
if (isMatch)
{
// 保存原始背景色并设置高亮
if (!_originalBrushes.ContainsKey(item))
{
_originalBrushes[item] = item.Background;
}
item.Background = Brushes.Yellow;
_matchedItems.Add(item);
}
// 递归搜索子节点
foreach (var childItem in item.Items.OfType())
{
SearchTreeViewItem(childItem, searchText);
}
}
private void ClearSearchHighlighting()
{
foreach (var item in _matchedItems)
{
if (_originalBrushes.TryGetValue(item, out var originalBrush))
{
item.Background = originalBrush;
}
else
{
item.ClearValue(TreeViewItem.BackgroundProperty);
}
}
_matchedItems.Clear();
_originalBrushes.Clear();
}
private void ExpandParents(TreeViewItem item)
{
var parent = ItemsControl.ItemsControlFromItemContainer(item) as TreeViewItem;
while (parent != null)
{
parent.IsExpanded = true;
parent = ItemsControl.ItemsControlFromItemContainer(parent) as TreeViewItem;
}
}
// 添加事件处理方法
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (string.IsNullOrWhiteSpace(SearchTextBox.Text))
{
ClearSearchHighlighting();
}
}
private void SearchTextBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
SearchTreeView(SearchTextBox.Text);
}
}
private void ClearSearchButton_Click(object sender, RoutedEventArgs e)
{
SearchTextBox.Clear();
ClearSearchHighlighting();
}
}
}