Files
ADP/TestingModule/ViewModels/CommandTreeViewModel.cs
2026-06-05 10:57:09 +08:00

591 lines
21 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 UIShare.UIViewModel;
using UIShare.GlobalVariable;
using Common.Attributes;
using Logger;
using Microsoft.IdentityModel.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
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.Input;
using System.Xml;
using Model;
using static UIShare.UIViewModel.ParameterModel;
using UIShare.ViewModelBase;
using NLog;
namespace TestingModule.ViewModels
{
public class CommandTreeViewModel:NavigateViewModelBase
{
#region
private string _SearchText;
public string SearchText
{
get => _SearchText;
set => SetProperty(ref _SearchText, value);
}
private ObservableCollection<InstructionNode> _instructionTree = new();
public ObservableCollection<InstructionNode> InstructionTree
{
get => _instructionTree;
set => SetProperty(ref _instructionTree, value);
}
public ProgramModel Program
{
get => _ScopedContext.Program;
set
{
if (_ScopedContext.Program != value)
{
_ScopedContext.Program = value;
RaisePropertyChanged();
}
}
}
public ObservableCollection<Assembly> Assemblies
{
get => _ScopedContext.Assemblies;
set
{
if (_ScopedContext.Assemblies != value)
{
_ScopedContext.Assemblies = value;
RaisePropertyChanged();
}
}
}
private ObservableCollection<SubProgramItem> _subPrograms = new();
public ObservableCollection<SubProgramItem> SubPrograms
{
get => _subPrograms;
set => SetProperty(ref _subPrograms, value);
}
private Dictionary<object, InstructionNode> _treeNodeMap = new();
public Dictionary<object, InstructionNode> TreeNodeMap
{
get => _treeNodeMap;
set => SetProperty(ref _treeNodeMap, value);
}
private Dictionary<string, XmlDocument> _xmlDocumentCache = new();
public Dictionary<string, XmlDocument> XmlDocumentCache
{
get => _xmlDocumentCache;
set => SetProperty(ref _xmlDocumentCache, value);
}
#endregion
public ICommand LoadedCommand { get; set; }
public ICommand SearchEnterCommand { get; set; }
public ICommand TreeDoubleClickCommand { get; set; }
public ICommand ReloadCommand { get; set; }
private ScopedContext _ScopedContext { get; set; }
private readonly SystemConfig _systemConfig;
private readonly GlobalInfo _globalInfo;
public CommandTreeViewModel(IContainerProvider containerProvider, ScopedContext scopedContext, SystemConfig systemConfig, GlobalInfo globalInfo) : base(containerProvider)
{
_ScopedContext = scopedContext;
_systemConfig = systemConfig;
_globalInfo = globalInfo;
LoadedCommand = new DelegateCommand(Loaded);
SearchEnterCommand = new DelegateCommand(Search);
TreeDoubleClickCommand = new DelegateCommand<object>(TreeDoubleClick);
ReloadCommand = new DelegateCommand(Reload);
}
#region
private void Reload()
{
LoadAllAssemblies();
LoadSubPrograms();
LoadInstructionsToTreeView();
}
private void TreeDoubleClick(object obj)
{
if (!_globalInfo.IsAdmin) return;
if(obj is InstructionNode Node)
{
if(Node.Children.Count == 0)
{
int index = _ScopedContext.SelectedStep?.Index >= 0 ? _ScopedContext.SelectedStep.Index : -1;
if (Node.Tag is MethodInfo method)
{
AddMethodToProgram(method, index);
}
else if(Node.Tag is string tag)
{
switch (tag)
{
case "循环开始":
AddLoopStartStep(index);
break;
case "循环结束":
AddLoopEndStep(index);
break;
}
}
else if(Node.Tag is SubProgramItem subProgram)
{
AddSubProgramToProgram(subProgram, index);
}
}
}
}
private void Search()
{
}
private void Loaded()
{
LoadAllAssemblies();
LoadSubPrograms();
LoadInstructionsToTreeView();
}
#endregion
#region
/// <summary>
/// 加载指定目录下的所有DLL
/// </summary>
private void LoadAllAssemblies()
{
Assemblies.Clear();
foreach (var dllPath in Directory.GetFiles(_systemConfig.DLLFilePath, "*.dll"))
{
try
{
var assembly = Assembly.LoadFile(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)
// {
// LoggerHelper.WarnWithNotify($"加载XML注释失败: {Path.GetFileName(xmlPath)} - {xmlEx.Message}");
// }
//}
}
catch (Exception ex)
{
LoggerHelper.WarnWithNotify($"无法加载程序集 {Path.GetFileName(dllPath)}: {ex.Message}");
}
}
}
// 子程序加载方法
private void LoadSubPrograms()
{
SubPrograms.Clear();
if (!Directory.Exists(_systemConfig.SubProgramFilePath))
{
Directory.CreateDirectory(_systemConfig.SubProgramFilePath);
return;
}
foreach (var filePath in Directory.GetFiles(_systemConfig.SubProgramFilePath, "*.adp"))
{
try
{
SubPrograms.Add(new SubProgramItem
{
Name = Path.GetFileNameWithoutExtension(filePath),
FilePath = filePath
});
}
catch (Exception ex)
{
LoggerHelper.WarnWithNotify($"加载子程序错误: {filePath} - {ex.Message}");
}
}
}
/// <summary>
/// 加载指令集到TreeView
/// </summary>
private void LoadInstructionsToTreeView()
{
InstructionTree.Clear();
var controlRootNode = new InstructionNode
{
Name = "系统指令",
Tag = "ControlRoot"
};
InstructionTree.Add(controlRootNode);
// 循环开始
controlRootNode.Children.Add(new InstructionNode
{
Name = "循环开始",
Tag = "循环开始"
});
// 循环结束
controlRootNode.Children.Add(new InstructionNode
{
Name = "循环结束",
Tag = "循环结束"
});
// ----------------------
// 子程序 根节点
// ----------------------
var subProgramRoot = new InstructionNode
{
Name = "子程序",
Tag = "SubProgramRoot"
};
InstructionTree.Add(subProgramRoot);
foreach (var subProgram in SubPrograms)
{
subProgramRoot.Children.Add(new InstructionNode
{
Name = subProgram.Name,
Tag = subProgram,
});
}
// ----------------------
// 动态 DLL 指令
// ----------------------
foreach (var assembly in Assemblies)
{
List<Type> validTypes = new List<Type>();
try
{
var types = assembly.GetTypes().Where(t =>
t.IsPublic &&
!t.IsNested &&
(t.IsClass || t.IsValueType) &&
(!t.IsAbstract || t.IsSealed) &&
t.GetCustomAttribute<ADPCommandAttribute>() != null);
foreach (var type in types)
{
if (type.GetCustomAttribute<BrowsableAttribute>()?.Browsable == false)
continue;
var allMethods = new HashSet<MethodInfo>();
GetPublicMethods(type, allMethods);
if (allMethods.Count > 0)
validTypes.Add(type);
}
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"加载类型错误: {assembly.FullName} - {ex.Message}");
}
if (validTypes.Count > 0)
{
var assemblyNode = new InstructionNode
{
Name = assembly.GetName().Name,
Tag = assembly
};
InstructionTree.Add(assemblyNode);
TreeNodeMap[assembly] = assemblyNode;
foreach (var type in validTypes)
{
//拦截没有在设备列表中的设备类型
//if (SystemConfig.Instance.DeviceList.Where(x=>x.Remark==type.Name).ToList().Count==0 && type.FullName.Contains("DeviceCommand.Device"))
//{
// continue;
//}
var typeNode = new InstructionNode
{
Name = type.Name,
Tag = type,
};
assemblyNode.Children.Add(typeNode);
TreeNodeMap[type] = typeNode;
var allMethods = new HashSet<MethodInfo>();
GetPublicMethods(type, allMethods);
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<BrowsableAttribute>()?.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}"));
var methodNode = new InstructionNode
{
Name = $"{method.Name}({paramText})",
Tag = method,
};
typeNode.Children.Add(methodNode);
TreeNodeMap[method] = methodNode;
}
}
}
}
}
#endregion
#region
/// <summary>
/// 递归获取类型的所有公共方法(包括继承的方法),但跳过被重写的方法
/// </summary>
/// <param name="type">要处理的目标类型</param>
/// <param name="methods">存储方法的集合</param>
private void GetPublicMethods(Type type, HashSet<MethodInfo> methods)
{
// 获取当前类型的所有公共方法(包括继承的)
var allMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
.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);
}
}
}
#endregion
#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> -> 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(_ScopedContext.SelectedStepList == "主程序")
{
if(insertIndex >= 0 && insertIndex <= Program.StepCollection.Count) Program.StepCollection.Insert(insertIndex, newStep);
else Program.StepCollection.Add(newStep); }
else
{
if (insertIndex >= 0 && insertIndex <= Program.ErrorStepCollection.Count) Program.ErrorStepCollection.Insert(insertIndex, newStep);
else Program.ErrorStepCollection.Add(newStep);
}
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"添加方法失败: {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<ProgramModel>(jsonstr);
if (tmp != null)
{
newStep.SubProgram = tmp;
}
// 添加到程序
if (_ScopedContext.SelectedStepList == "主程序")
{
if (insertIndex >= 0 && insertIndex <= Program.StepCollection.Count) Program.StepCollection.Insert(insertIndex, newStep);
else Program.StepCollection.Add(newStep);
}
else
{
if (insertIndex >= 0 && insertIndex <= Program.ErrorStepCollection.Count) Program.ErrorStepCollection.Insert(insertIndex, newStep);
else Program.ErrorStepCollection.Add(newStep);
}
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"添加子程序失败: {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 (_ScopedContext.SelectedStepList == "主程序")
{
if (insertIndex >= 0) Program.StepCollection.Insert(insertIndex, newStep);
else Program.StepCollection.Add(newStep);
}
else
{
if (insertIndex >= 0) Program.ErrorStepCollection.Insert(insertIndex, newStep);
else Program.ErrorStepCollection.Add(newStep);
}
}
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 (_ScopedContext.SelectedStepList == "主程序")
{
if (insertIndex >= 0) Program.StepCollection.Insert(insertIndex, newStep);
else Program.StepCollection.Add(newStep);
}
else
{
if (insertIndex >= 0) Program.ErrorStepCollection.Insert(insertIndex, newStep);
else Program.ErrorStepCollection.Add(newStep);
}
}
#endregion
}
}