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(); } } }