BDU/ATS/Logic/StepRunning.cs

799 lines
35 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 BDU.Models;
using BDU.Tools;
using BDU.Views;
using BDU.Windows;
using ATS_DBContext;
using ATS_DBContext.Models;
using ControlzEx.Standard;
using MaterialDesignThemes.Wpf;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using static BDU.Models.ParameterModel;
namespace BDU.Logic
{
public static class StepRunning
{
private static readonly Dictionary<Guid, ParameterModel> tmpParameters = [];
private static readonly Stopwatch stepStopwatch = new();
private static readonly Stack<Stopwatch> loopStopwatchStack = new();
private static readonly Stack<LoopContext> loopStack = new();
public static CancellationTokenSource stepCTS = new();
private static bool SubSingleStep = false;
private static Guid TestRoundID;
public static async Task<bool> ExecuteSteps(ProgramModel program, int depth = 0, CancellationToken cancellationToken = default, string subProgramPath = "")
{
int index = 0;
bool overallSuccess = true;
var ReportList = ReportModelList.ReportList;
if (ReportList == null)
{
ReportList = new List<ReportModel>();
}
if (depth == 0)
{
loopStack.Clear();
loopStopwatchStack.Clear();
ResetAllStepStatus(program);
tmpParameters.Clear();
TestRoundID = Guid.NewGuid();
}
foreach (var item in program.Parameters)
{
tmpParameters.TryAdd(item.ID, item);
}
while (index < program.StepCollection.Count)
{
while (ToolBar.Instance.IsStop == true)
{
await Task.Delay(50);
}
if (cancellationToken.IsCancellationRequested)
{
break;
}
var step = program.StepCollection[index];
if (!step.IsUsed)
{
index++;
continue;
}
step.Result = 0;
if (step.StepType == "循环开始")
{
var endStep = program.StepCollection.FirstOrDefault(x => x.LoopStartStepId == step.ID);
if (endStep != null)
{
endStep.Result = 0;
}
else
{
Log.Error("程序循环指令未闭合,请检查后重试");
break;
}
}
// 处理循环开始
if (step.StepType == "循环开始")
{
Stopwatch loopStopwatch = new();
loopStopwatch.Start();
loopStopwatchStack.Push(loopStopwatch);
var context = new LoopContext
{
LoopCount = step.LoopCount ?? 1,
CurrentLoop = 0,
StartIndex = index,
LoopStartStep = step
};
loopStack.Push(context);
step.CurrentLoopCount = context.LoopCount;
Log.Info($"循环开始,共{context.LoopCount}次", depth);
index++;
}
// 处理循环结束
else if (step.StepType == "循环结束")
{
if (loopStack.Count == 0)
{
Log.Error("未匹配的循环结束指令", depth);
step.Result = 2;
index++;
continue;
}
var context = loopStack.Peek();
context.CurrentLoop++;
// 更新循环开始步骤的显示
context.LoopStartStep!.CurrentLoopCount = context.LoopCount - context.CurrentLoop;
if (context.CurrentLoop < context.LoopCount)
{
// 继续循环:跳转到循环开始后的第一条指令
index = context.StartIndex + 1;
Log.Info($"循环第{context.CurrentLoop}次结束,跳回开始,剩余{context.LoopCount - context.CurrentLoop}次", depth);
}
else
{
// 循环结束
loopStack.Pop();
var loopStopwatch = loopStopwatchStack.Peek();
index++;
Log.Info($"循环结束,共执行{context.LoopCount}次", depth);
if (depth == 0 && loopStopwatch.IsRunning)
{
loopStopwatch.Stop();
step.RunTime = (int)loopStopwatch.ElapsedMilliseconds;
step.Result = 1;
program.StepCollection.First(x => x.ID == step.LoopStartStepId).Result = 1;
loopStopwatchStack.Pop();
}
}
}
// 处理普通步骤
else
{
if (depth == 0)
{
stepStopwatch.Restart();
}
if (step.SubProgram != null)
{
if (ToolBar.Instance.SingleStep)//子程序的单步执行将执行完保存下的所有Method
{
SubSingleStep = true;
ToolBar.Instance.SingleStep = false;
}
// 构建子程序路径
string currentSubProgramPath = string.IsNullOrEmpty(subProgramPath)
? step.Name!
: $"{subProgramPath} -> {step.Name}";
Log.Info($"开始执行子程序 [ {step.Index} ] [ {step.Name} ] ", depth);
// 传递子程序路径给下一级
bool subProgramSuccess = await ExecuteSteps(step.SubProgram, depth + 1, cancellationToken, currentSubProgramPath);
UpdateCurrentStepResult(step, true, subProgramSuccess, depth);
overallSuccess &= subProgramSuccess;
// 新增:在子程序执行完成后,插入一个总结行
if (string.IsNullOrEmpty(subProgramPath) && !subProgramPath.Contains(" -> ")) // 如果是子程序内部执行完成(即不是主程序),才插入总结行
{
ReportModelList.ReportList.Add(new ReportModel
{
stepModel = new StepModel()
{
Index = -1,
Name = $"子程序 [{step.Name}] 执行结果:{(subProgramSuccess ? "PASS" : "FAIL")}"
}, // 没有对应的实际步骤,这里用于装在结果
User = null,
ExcuteTime = null,
IsPass = subProgramSuccess ? IsPass.PASS : IsPass.FAIL,
Result = "",
SubProgramPath = "",
IsSubProgramSummary = true,
SubProgramName = ""
});
Log.Success($"子程序 [{step.Name}] 总结行已写入");
}
if (SubSingleStep)
{
SubSingleStep = false;
ToolBar.Instance.SingleStep = true;
}
}
else if (step.Method != null)
{
Log.Info($"开始执行指令 [ {step.Index} ] [ {step.Method!.FullName}.{step.Method.Name} ] ", depth);
await ExecuteMethodStep(step, tmpParameters, depth, cancellationToken, subProgramPath); // 传递路径
bool stepSuccess = step.Result == 1;
overallSuccess &= stepSuccess;
if (step.NGGotoStepID != null && !stepSuccess)
{
var tmp = program.StepCollection.FirstOrDefault(x => x.ID == step.NGGotoStepID);
if (tmp != null)
{
index = tmp.Index - 2;
Log.Info($"指令跳转 [ {tmp.Index} ] [ {tmp.Name} ]", depth);
}
}
if (step.OKGotoStepID != null && stepSuccess)
{
var tmp = program.StepCollection.FirstOrDefault(x => x.ID == step.OKGotoStepID);
if (tmp != null)
{
index = tmp.Index - 2;
Log.Info($"指令跳转 [ {tmp.Index} ] [ {tmp.Name} ]", depth);
}
}
}
index++;
if (ToolBar.Instance.SingleStep||step.isBrokenpoint==true)
{
ToolBar.Instance.IsStop = true;
ToolBar.Instance.RunState = "运行";
ToolBar.Instance.RunIcon = PackIconKind.Play;
}
}
}
bool finalResult = loopStack.Count == 0 && overallSuccess;
if (depth > 0) // 子程序
{
return finalResult;
}
return finalResult;
}
public static async Task ExecuteMethodStep(StepModel step, Dictionary<Guid, ParameterModel> parameters, int depth, CancellationToken cancellationToken = default, string subProgramPath = "")
{
try
{
MainWindow.Instance.SelectedStep = null;
await Task.Delay(SystemConfig.Instance.PerformanceLevel);
// 1. 查找类型
Type? targetType = null;
foreach (var assembly in CommandTreeView.Instance!.Assemblies)
{
targetType = assembly.GetType(step.Method!.FullName!);
if (targetType != null) break;
}
if (targetType == null)
{
Log.Error($"指令 [ {step.Index} ] 执行错误:未找到类型 {step.Method!.FullName}", depth);
step.Result = 2;
}
// 2. 创建实例(仅当方法不是静态时才需要)
object? instance = null;
bool isStaticMethod = false;
// 3. 准备参数
var inputParams = new List<object?>();
var paramTypes = new List<Type>();
ParameterModel? outputParam = null;
foreach (var param in step.Method!.Parameters)
{
if (param.Category == ParameterCategory.Input)
{
if (param.Type == typeof(CancellationToken))
{
inputParams.Add(stepCTS.Token);
paramTypes.Add(param.Type!);
continue;
}
var actualValue = param.GetActualValue(tmpParameters);
// 类型转换处理
if (actualValue != null)
{
if (string.IsNullOrEmpty(actualValue.ToString()))
{
actualValue = null;
}
if (actualValue != null && param.Type != null && actualValue.GetType() != param.Type)
{
try
{
if (param.Type.IsArray)
{
// --- 原有的数组处理逻辑 ---
// 获取数组元素类型
Type elementType = param.Type.GetElementType()!;
// 解析字符串为字符串数组
string[] stringArray = actualValue.ToString()!
.Trim('[', ']')
.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(s => s.Trim())
.ToArray();
// 创建目标类型数组
Array array = Array.CreateInstance(elementType, stringArray.Length);
// 转换每个元素
for (int i = 0; i < stringArray.Length; i++)
{
try
{
// 特殊处理字符串类型
if (elementType == typeof(string))
{
array.SetValue(stringArray[i], i);
}
// 特殊处理枚举类型
else if (elementType.IsEnum)
{
array.SetValue(Enum.Parse(elementType, stringArray[i]), i);
}
// 常规类型转换
{
if (stringArray[i] is string s && s.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
// 先转成整数
var intValue = Convert.ToInt64(s, 16);
// 再转成目标类型
array.SetValue(Convert.ChangeType(intValue, elementType), i);
}
else
{
array.SetValue(Convert.ChangeType(stringArray[i], elementType), i);
}
}
}
catch
{
throw new InvalidCastException($"指令 [ {step.Index} ] 执行错误:元素 '{stringArray[i]}' 无法转换为 {elementType.Name}[]");
}
}
actualValue = array;
}
// --- 修改:处理 Type 为 object 且 Value 为字符串的情况 ---
else if (param.Type == typeof(object) && actualValue is string strValue)
{
// 尝试将字符串解析为常见的数组类型
object parsedArray = TryParseStringToCommonArrays(strValue);
if (parsedArray != null)
{
actualValue = parsedArray; // 使用解析出的数组
}
// 如果解析失败 (parsedArray is null)actualValue 保持原字符串值
// 让后续的 Convert.ChangeType 尝试处理 (通常会失败,但这是预期的)
}
// --- 原有的非数组类型处理逻辑 ---
else if (param.Type.BaseType == typeof(Enum))
{
actualValue = Enum.Parse(param.Type, actualValue.ToString()!);
}
else if(actualValue is string s && s.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
{
// 先转成整数
var intValue = Convert.ToInt64(s, 16);
// 再转成目标类型
actualValue = Convert.ChangeType(intValue, param.Type);
}
else
{
actualValue = Convert.ChangeType(actualValue, param.Type);
}
}
catch (Exception ex)
{
Log.Warning($"指令 [ {step.Index} ] 执行错误:参数 {param.Name} 类型转换失败: {ex.Message}", depth);
// 可以选择在此处记录错误并设置 step.Result或者继续执行
// 这里我们选择继续,但实际应用中可能需要更严格的错误处理
}
}
}
inputParams.Add(actualValue);
paramTypes.Add(param.Type!);
}
else if (param.Category == ParameterCategory.Output)
{
outputParam = param;
}
}
// 4. 获取方法
var method = targetType!.GetMethod(
step.Method.Name!,
BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance,
null,
paramTypes.ToArray(),
null
);
if (method == null)
{
Log.Error($"指令 [ {step.Index} ] 执行错误:未找到方法{step.Method.Name}", depth);
step.Result = 2;
}
// 检查是否是静态方法
isStaticMethod = method!.IsStatic;
// 如果是实例方法,需要创建实例
if (!isStaticMethod)
{
try
{
instance = Activator.CreateInstance(targetType);
}
catch (Exception ex)
{
Log.Error($"指令 [ {step.Index} ] 执行错误:创建实例失败 - {ex.Message}", depth);
step.Result = 2;
}
}
// 5. 执行方法
object? returnValue = method.Invoke(instance, inputParams.ToArray());
try
{
// 处理异步方法
if (returnValue is Task task)
{
await task.ConfigureAwait(false);
// 获取结果如果是Task<T>
if (task.GetType().IsGenericType)
{
var returnValueProperty = task.GetType().GetProperty("Result");
returnValue = returnValueProperty?.GetValue(task);
}
else
{
returnValue = null;
}
}
// 处理VoidTaskreturnValue类型
if (returnValue != null && returnValue.GetType().FullName == "System.Threading.Tasks.VoidTaskreturnValue")
{
returnValue = null;
}
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
Log.Error($"指令 [ {step.Index} ] 执行错误: {ex.InnerException?.Message ?? ex.Message}", depth);
step.Result = 2;
//给报告添加子程序执行结果
ReportModelList.ReportList.Add(new ReportModel
{
stepModel = step,
User = MainWindow.Instance.User.UserName,
ExcuteTime = DateTime.Now,
IsPass = step.Result == 1 ? IsPass.PASS : IsPass.FAIL,
Result = $"指令 [ {step.Index} ] 执行错误: {ex.InnerException?.Message ?? ex.Message}"
});
Log.Success($"指令 [ {step.Index} ]报告已写入");
return;
}
// 6. 处理输出
bool paraResult = true;
string resultMsg = "";
if (outputParam != null)
{
outputParam.Value = returnValue;
var currentPara = outputParam.GetCurrentParameter(tmpParameters);
if (currentPara != null)
{
currentPara.Value = returnValue;
var tmp = currentPara.GetResult();
currentPara.Result = tmp.Item1;
if (tmp.Item2 != null)
{
Log.Warning(tmp.Item2);
}
if (currentPara.IsSave && MainWindow.Instance.Program.Parameters.FirstOrDefault(x => x.ID == currentPara.ID) != null)
{
_ = SaveDataToDatabase(MainWindow.Instance.Program.ID, currentPara);
}
}
var returnType = returnValue?.GetType();
if (returnType != null)
{
if (!returnType.IsArray)
{
Log.Success($"输出 [ {outputParam.Name} ] = {returnValue} ({returnType.Name})", depth);
// 只有勾选了IsOutputToReport才记录到报告的Result中
if (outputParam.IsOutputToReport)
{
resultMsg = $"输出 [ {outputParam.Name} ] = {returnValue} ({returnType.Name})";
}
}
else
{
if (returnValue is IEnumerable enumerable)
{
var elements = enumerable.Cast<object>().Select(item => item?.ToString() ?? "null");
Log.Success($"输出 [ {outputParam.Name} ] = [ {string.Join(", ", elements)} ] ({returnType.Name})", depth);
// 只有勾选了IsOutputToReport才记录到报告的Result中
if (outputParam.IsOutputToReport)
{
resultMsg = $"输出 [ {outputParam.Name} ] = [ {string.Join(", ", elements)} ] ({returnType.Name})";
}
}
}
}
}
Log.Success($"指令 [ {step.Index} ] 执行成功", depth);
UpdateCurrentStepResult(step, paraResult: paraResult, depth: depth);
if (ReportModelList.ReportList == null)
{
ReportModelList.ReportList = new List<ReportModel>();
}
// 始终写入报告但Result内容根据输出参数的IsOutputToReport控制
ReportModelList.ReportList.Add(new ReportModel
{
stepModel = step,
User = MainWindow.Instance.User.UserName,
ExcuteTime = DateTime.Now,
IsPass = step.Result == 1 ? IsPass.PASS : IsPass.FAIL,
Result = resultMsg, // resultMsg只在IsOutputToReport=true时才有值
SubProgramPath = subProgramPath
});
Log.Success($"指令 [ {step.Index} ]报告已写入");
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
Log.Error($"指令 [ {step.Index} ] 执行错误: {ex.InnerException?.Message ?? ex.Message}", depth);
step.Result = 2;
return;
}
}
public static void ResetAllStepStatus(ProgramModel program)
{
foreach (var step in program.StepCollection)
{
step.Result = -1;
step.RunTime = null;
}
}
private static void UpdateCurrentStepResult(StepModel step, bool paraResult = true, bool stepResult = true, int depth = 0)
{
if (stepResult && paraResult)
{
if (string.IsNullOrEmpty(step.OKExpression))
{
step.Result = 1;
}
else
{
Dictionary<string, object> paraDic = [];
foreach (var item in tmpParameters)
{
paraDic.TryAdd(item.Value.Name, item.Value.Value!);
}
if (step.SubProgram != null)
{
foreach (var item in step.SubProgram.Parameters.Where(x => x.Category == ParameterCategory.Output))
{
paraDic.TryAdd(item.Name, item.Value!);
}
}
else if (step.Method != null)
{
foreach (var item in step.Method.Parameters.Where(x => x.Category == ParameterCategory.Output))
{
paraDic.TryAdd(item.Name, item.Value!);
}
}
bool re = ExpressionEvaluator.EvaluateExpression(step.OKExpression, paraDic);
step.Result = re ? 1 : 2;
if (step.Result == 2)
{
Log.Warning($"指令 [ {step.Index} ] FAIL:条件表达式验证失败", depth);
}
}
}
else
{
if (!paraResult)
{
Log.Warning("参数限值校验失败", depth);
}
step.Result = 2;
}
}
private static async Task SaveDataToDatabase(Guid programID, ParameterModel currentPara)
{
using (ATS_DB db = new())
{
db.TestData.Add(new()
{
ProgramID = programID,
TestRoundID = TestRoundID,
ParameterID = currentPara.ID,
Value = currentPara.Value?.ToString() ?? "",
LowerLimit = currentPara.LowerLimit?.ToString(),
UpperLimit = currentPara.UpperLimit?.ToString(),
Result = currentPara.Result
});
try
{
var row = await db.SaveChangesAsync().ConfigureAwait(false);
if (row == 0)
{
Log.Error($"测试数据保存失败:未知的错误");
}
}
catch (Exception ex)
{
Log.Error($"测试数据保存失败{ex.InnerException}");
}
}
}
// 添加一个辅助方法来尝试解析字符串为不同类型的数组
private static object TryParseStringToCommonArrays(string strValue)
{
// 尝试解析常见的数组格式
strValue = strValue.Trim('[', ']');
string[] parts = System.Text.RegularExpressions.Regex.Split(strValue, @"[,\s]+").Where(s => !string.IsNullOrEmpty(s)).ToArray();
if (parts.Length == 0) return null; // 空数组或无效格式
//尝试解析为byte
if (parts.Length == 1)
{
if (byte.TryParse(parts[0], System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out byte singleByteValue))
{
return singleByteValue;
}
}
// --- 新增:先尝试解析为 byte[] (十六进制) ---
if (TryParseStringArrayToType(parts, out byte[] hexByteResult, s => byte.TryParse(s, System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return hexByteResult;
}
// --- 新增:再尝试解析为 byte[] (十进制) ---
if (TryParseStringArrayToType(parts, out byte[] decByteResult, s => byte.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return decByteResult;
}
// --- 原有的其他类型解析逻辑 ---
// 尝试解析为 int[]
if (TryParseStringArrayToType(parts, out int[] intResult, s => int.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return intResult;
}
// 尝试解析为 double[]
if (TryParseStringArrayToType(parts, out double[] doubleResult, s => double.TryParse(s, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return doubleResult;
}
// 尝试解析为 string[] (处理带引号的字符串)
string[] originalParts = parts; // 保留原始分割结果
string[] stringParts = originalParts.Select(s => s.Trim('\"', '\'')).ToArray();
bool allPartsAreNonNumericStrings = stringParts.All(s => !double.TryParse(s, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out _));
if (allPartsAreNonNumericStrings && stringParts.All(s => !string.IsNullOrEmpty(s)))
{
return stringParts;
}
// 尝试解析为 float[]
if (TryParseStringArrayToType(parts, out float[] floatResult, s => float.TryParse(s, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return floatResult;
}
// 尝试解析为 short[]
if (TryParseStringArrayToType(parts, out short[] shortResult, s => short.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return shortResult;
}
// 尝试解析为 long[]
if (TryParseStringArrayToType(parts, out long[] longResult, s => long.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return longResult;
}
// 尝试解析为 uint[]
if (TryParseStringArrayToType(parts, out uint[] uintResult, s => uint.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return uintResult;
}
// 尝试解析为 ushort[]
if (TryParseStringArrayToType(parts, out ushort[] ushortResult, s => ushort.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return ushortResult;
}
// 尝试解析为 ulong[]
if (TryParseStringArrayToType(parts, out ulong[] ulongResult, s => ulong.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return ulongResult;
}
// 尝试解析为 decimal[]
if (TryParseStringArrayToType(parts, out decimal[] decimalResult, s => decimal.TryParse(s, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out _)))
{
return decimalResult;
}
// 尝试解析为 bool[]
if (TryParseStringArrayToType(parts, out bool[] boolResult, s => bool.TryParse(s, out _)))
{
return boolResult;
}
// 如果以上所有尝试都失败,返回 null
return null;
}
// 一个通用的辅助方法,用于尝试将字符串数组解析为特定类型的数组
private static bool TryParseStringArrayToType<T>(string[] parts, out T[] result, Func<string, bool> tryParseFunc)
{
result = null;
T[] tempArray = new T[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
if (!tryParseFunc(parts[i]))
{
return false; // 解析失败
}
// 使用 Convert.ChangeType 确保类型安全
try
{
tempArray[i] = (T)Convert.ChangeType(parts[i], typeof(T), System.Globalization.CultureInfo.InvariantCulture);
}
catch
{
return false; // 转换失败
}
}
result = tempArray;
return true; // 解析成功
}
#region
private class LoopContext
{
public int LoopCount { get; set; }
public int CurrentLoop { get; set; }
public int StartIndex { get; set; }
public StepModel? LoopStartStep { get; set; }
}
#endregion
}
}