完善执行逻辑

This commit is contained in:
hsc 2025-11-11 13:29:56 +08:00
parent 45ebf1f2b6
commit b69a469c89
16 changed files with 1022 additions and 64 deletions

View File

@ -1,4 +1,5 @@
using BOB.Models; using BOB.Converters;
using BOB.Models;
using BOB.ViewModels; using BOB.ViewModels;
using BOB.ViewModels.Dialogs; using BOB.ViewModels.Dialogs;
using BOB.Views; using BOB.Views;
@ -38,7 +39,7 @@ namespace BOB
containerRegistry.RegisterDialog<DeviceSetting, DeviceSettingViewModel>("DeviceSetting"); containerRegistry.RegisterDialog<DeviceSetting, DeviceSettingViewModel>("DeviceSetting");
//注册全局变量 //注册全局变量
containerRegistry.RegisterSingleton<GlobalVariables>(); containerRegistry.RegisterSingleton<GlobalVariables>();
containerRegistry.RegisterSingleton<StepRunning>();
} }
} }

View File

@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
namespace BOB.Converters
{
public class ParameterToGotoSettingStringConverter : IValueConverter
{
private GlobalVariables _globalVariables;
public ParameterToGotoSettingStringConverter(GlobalVariables globalVariables)
{
_globalVariables= globalVariables;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Guid stepID && stepID == _globalVariables.SelectedStep!.ID)
{
if (_globalVariables.SelectedStep == null)
{
return "";
}
if (_globalVariables.SelectedStep!.OKGotoStepID == null && _globalVariables.SelectedStep!.NGGotoStepID == null)
{
return "0/0";
}
else
{
string gotoString = "";
if (_globalVariables.SelectedStep!.OKGotoStepID != null)
{
var OKGotoStep = _globalVariables.Program.StepCollection.FirstOrDefault(x => x.ID == _globalVariables.SelectedStep!.OKGotoStepID);
if (OKGotoStep != null)
{
gotoString = OKGotoStep.Index.ToString() + "/";
}
else
{
gotoString = "0/";
}
}
else
{
gotoString = "0/";
}
if (_globalVariables.SelectedStep!.NGGotoStepID != null)
{
var NGGotoStep = _globalVariables.Program.StepCollection.FirstOrDefault(x => x.ID == _globalVariables.SelectedStep!.NGGotoStepID);
if (NGGotoStep != null)
{
gotoString += NGGotoStep.Index.ToString();
}
else
{
gotoString += "0";
}
}
else
{
gotoString += "0";
}
return gotoString;
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string gotoSettingstring)
{
gotoSettingstring = gotoSettingstring.Replace(" ", "").Replace("/n", "").Replace("/r", "");
if (gotoSettingstring == "0/0")
{
_globalVariables.SelectedStep!.OKGotoStepID = null;
_globalVariables.SelectedStep!.NGGotoStepID = null;
}
else
{
try
{
var list = gotoSettingstring.Split("/");
var okindex = System.Convert.ToInt32(list[0]);
var ngindex = System.Convert.ToInt32(list[1]);
if (okindex == 0)
{
_globalVariables.SelectedStep!.OKGotoStepID = null;
}
else
{
if (okindex > _globalVariables.Program.StepCollection.Count)
{
throw new Exception("步骤序号超出最大值");
}
_globalVariables.SelectedStep!.OKGotoStepID = _globalVariables.Program.StepCollection.FirstOrDefault(x => x.Index == okindex)?.ID;
}
if (ngindex == 0)
{
_globalVariables.SelectedStep!.NGGotoStepID = null;
}
else
{
if (ngindex > _globalVariables.Program.StepCollection.Count)
{
throw new Exception("步骤序号超出最大值");
}
_globalVariables.SelectedStep!.NGGotoStepID = _globalVariables.Program.StepCollection.FirstOrDefault(x => x.Index == ngindex)?.ID;
}
}
catch (Exception ex)
{
MessageBox.Show($"跳转表达式错误:{ex.Message}");
}
}
}
return _globalVariables.SelectedStep!.ID;
}
}
}

View File

@ -1,7 +1,10 @@
using BOB.Models; using BOB.Models;
using MaterialDesignThemes.Wpf;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,6 +17,11 @@ namespace BOB
public String UserName { get; set; } = "hsc"; public String UserName { get; set; } = "hsc";
public String Title { get; set; } = "主程序"; public String Title { get; set; } = "主程序";
public string CurrentFilePath { get; set; } public string CurrentFilePath { get; set; }
public bool? IsStop { get; set; }
public bool SingleStep { get; set; }
public string RunState { get; set; } = "运行";
public ObservableCollection<Assembly> Assemblies { get; set; } = new();
public PackIconKind RunIcon { get; set; } = PackIconKind.Play;
public StepModel SelectedStep { get; set; } public StepModel SelectedStep { get; set; }
public ParameterModel SelectedParameter { get; set; } public ParameterModel SelectedParameter { get; set; }
public DeviceModel SelectedDevice { get; set; } public DeviceModel SelectedDevice { get; set; }

View File

@ -22,6 +22,7 @@ namespace BOB.Models
LoopStartStepId = source.LoopStartStepId; LoopStartStepId = source.LoopStartStepId;
OKExpression = source.OKExpression; OKExpression = source.OKExpression;
OKGotoStepID = source.OKGotoStepID; OKGotoStepID = source.OKGotoStepID;
GotoSettingString = source.GotoSettingString;
NGGotoStepID = source.NGGotoStepID; NGGotoStepID = source.NGGotoStepID;
Description = source.Description; Description = source.Description;
IsUsed = source.IsUsed; IsUsed = source.IsUsed;

531
BOB/StepRunning.cs Normal file
View File

@ -0,0 +1,531 @@
using BOB.Models;
using Common.Tools;
using Logger;
using MaterialDesignThemes.Wpf;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using static BOB.Models.ParameterModel;
namespace BOB
{
public class StepRunning
{
private GlobalVariables _globalVariables;
private readonly Dictionary<Guid, ParameterModel> tmpParameters = [];
private readonly Stopwatch stepStopwatch = new();
private readonly Stack<Stopwatch> loopStopwatchStack = new();
private readonly Stack<LoopContext> loopStack = new();
public CancellationTokenSource stepCTS = new();
private bool SubSingleStep = false;
private Guid TestRoundID;
public StepRunning(GlobalVariables globalVariables)
{
_globalVariables = globalVariables;
}
public async Task<bool> ExecuteSteps(ProgramModel program, int depth = 0, CancellationToken cancellationToken = default)
{
int index = 0;
bool stepSuccess = false;
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 (_globalVariables.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
{
LoggerHelper.ErrorWithNotify("程序循环指令未闭合,请检查后重试");
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;
LoggerHelper.InfoWithNotify($"循环开始,共{context.LoopCount}次", depth);
index++;
}
// 处理循环结束
else if (step.StepType == "循环结束")
{
if (loopStack.Count == 0)
{
LoggerHelper.ErrorWithNotify("未匹配的循环结束指令", 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;
LoggerHelper.InfoWithNotify($"循环第{context.CurrentLoop}次结束,跳回开始,剩余{context.LoopCount - context.CurrentLoop}次", depth);
}
else
{
// 循环结束
loopStack.Pop();
var loopStopwatch = loopStopwatchStack.Peek();
index++;
LoggerHelper.InfoWithNotify($"循环结束,共执行{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 (_globalVariables.SingleStep)//子程序的单步执行将执行完保存下的所有Method
{
SubSingleStep = true;
_globalVariables.SingleStep = false;
}
LoggerHelper.InfoWithNotify($"开始执行子程序 [ {step.Index} ] [ {step.Name} ] ", depth);
stepSuccess = await ExecuteSteps(step.SubProgram, depth + 1, cancellationToken);
UpdateCurrentStepResult(step, true, stepSuccess, depth);
if (SubSingleStep)
{
SubSingleStep = false;
_globalVariables.SingleStep = true;
}
}
else if (step.Method != null)
{
LoggerHelper.InfoWithNotify($"开始执行指令 [ {step.Index} ] [ {step.Method!.FullName}.{step.Method.Name} ] ", depth);
await ExecuteMethodStep(step, tmpParameters, depth, cancellationToken);
stepSuccess = step.Result == 1;
if (step.NGGotoStepID != null && !stepSuccess)
{
var tmp = program.StepCollection.FirstOrDefault(x => x.ID == step.NGGotoStepID);
if (tmp != null)
{
index = tmp.Index - 2;
LoggerHelper.InfoWithNotify($"指令跳转 [ {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;
LoggerHelper.InfoWithNotify($"指令跳转 [ {tmp.Index} ] [ {tmp.Name} ]", depth);
}
}
}
index++;
if (depth == 0 && stepStopwatch.IsRunning)
{
stepStopwatch.Stop();
step.RunTime = (int)stepStopwatch.ElapsedMilliseconds;
}
if (_globalVariables.SingleStep)
{
_globalVariables.IsStop = true;
_globalVariables.RunState = "运行";
_globalVariables.RunIcon = PackIconKind.Play;
}
}
}
return loopStack.Count == 0 && stepSuccess;
}
public async Task ExecuteMethodStep(StepModel step, Dictionary<Guid, ParameterModel> parameters, int depth, CancellationToken cancellationToken = default)
{
try
{
_globalVariables.SelectedStep = null;
await Task.Delay(SystemConfig.Instance.PerformanceLevel);
// 1. 查找类型
Type? targetType = null;
foreach (var assembly in _globalVariables.Assemblies)
{
targetType = assembly.GetType(step.Method!.FullName!);
if (targetType != null) break;
}
if (targetType == null)
{
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误:未找到类型 {step.Method!.FullName}", depth);
step.Result = 2;
}
// 2. 创建实例(仅当方法不是静态时才需要)
object? instance = null;
bool isMethod = 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);
}
// 常规类型转换
else
{
array.SetValue(Convert.ChangeType(stringArray[i], elementType), i);
}
}
catch
{
throw new InvalidCastException($"指令 [ {step.Index} ] 执行错误:元素 '{stringArray[i]}' 无法转换为 {elementType.Name}[]");
}
}
actualValue = array;
}
else
{
if (param.Type.BaseType == typeof(Enum))
{
actualValue = Enum.Parse(param.Type, param.Value!.ToString()!);
}
else
{
actualValue = Convert.ChangeType(actualValue, param.Type);
}
}
}
catch (Exception ex)
{
LoggerHelper.WarnWithNotify($"指令 [ {step.Index} ] 执行错误:参数 {param.Name} 类型转换失败: {ex.Message}", depth);
}
}
}
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)
{
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误:未找到方法{step.Method.Name}", depth);
step.Result = 2;
}
// 检查是否是静态方法
bool isStaticMethod = method!.IsStatic;
// 如果是实例方法,需要创建实例
if (!isMethod)
{
try
{
instance = Activator.CreateInstance(targetType);
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"指令 [ {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)
{
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误: {ex.InnerException?.Message ?? ex.Message}", depth);
step.Result = 2;
return;
}
// 6. 处理输出
bool paraResult = true; //记录参数上下限是否NG
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;
paraResult = tmp.Item1;
if (tmp.Item2 != null)
{
LoggerHelper.WarnWithNotify(tmp.Item2);
}
if (currentPara.IsSave && _globalVariables.Program.Parameters.FirstOrDefault(x => x.ID == currentPara.ID) != null)
{
_ = SaveDataToDatabase(_globalVariables.Program.ID, currentPara);
}
}
var returnType = returnValue?.GetType();
if (returnType != null)
{
if (!returnType.IsArray)
{
LoggerHelper.SuccessWithNotify($"输出 [ {outputParam.Name} ] = {returnValue} ({returnType.Name})", depth);
}
else
{
if (returnValue is IEnumerable enumerable)
{
var elements = enumerable.Cast<object>().Select(item => item?.ToString() ?? "null");
LoggerHelper.SuccessWithNotify($"输出 [ {outputParam.Name} ] = [ {string.Join(", ", elements)} ] ({returnType.Name})", depth);
}
}
}
}
LoggerHelper.SuccessWithNotify($"指令 [ {step.Index} ] 执行成功", depth);
UpdateCurrentStepResult(step, paraResult: paraResult, depth: depth);
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误: {ex.InnerException?.Message ?? ex.Message}", depth);
step.Result = 2;
return;
}
}
public void ResetAllStepStatus(ProgramModel program)
{
foreach (var step in program.StepCollection)
{
step.Result = -1;
step.RunTime = null;
}
}
private 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)
{
LoggerHelper.WarnWithNotify($"指令 [ {step.Index} ] NG:条件表达式验证失败", depth);
}
}
}
else
{
if (!paraResult)
{
LoggerHelper.WarnWithNotify("参数限值校验失败", depth);
}
step.Result = 2;
}
}
private async Task SaveDataToDatabase(Guid programID, ParameterModel currentPara)
{
}
#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
}
}

View File

@ -37,18 +37,30 @@ namespace BOB.ViewModels
set => SetProperty(ref _instructionTree, value); set => SetProperty(ref _instructionTree, value);
} }
private ProgramModel _program;
public ProgramModel Program public ProgramModel Program
{ {
get => _program; get => _globalVariables.Program;
set => SetProperty(ref _program, value); set
{
if (_globalVariables.Program != value)
{
_globalVariables.Program = value;
RaisePropertyChanged();
}
}
} }
private ObservableCollection<Assembly> _assemblies = new();
public ObservableCollection<Assembly> Assemblies public ObservableCollection<Assembly> Assemblies
{ {
get => _assemblies; get => _globalVariables.Assemblies;
set => SetProperty(ref _assemblies, value); set
{
if (_globalVariables.Assemblies != value)
{
_globalVariables.Assemblies = value;
RaisePropertyChanged();
}
}
} }
private ObservableCollection<SubProgramItem> _subPrograms = new(); private ObservableCollection<SubProgramItem> _subPrograms = new();
@ -71,7 +83,7 @@ namespace BOB.ViewModels
get => _xmlDocumentCache; get => _xmlDocumentCache;
set => SetProperty(ref _xmlDocumentCache, value); set => SetProperty(ref _xmlDocumentCache, value);
} }
GlobalVariables GlobalVariables { get; set; } GlobalVariables _globalVariables { get; set; }
#endregion #endregion
public ICommand LoadedCommand { get; set; } public ICommand LoadedCommand { get; set; }
public ICommand SearchEnterCommand { get; set; } public ICommand SearchEnterCommand { get; set; }
@ -79,8 +91,7 @@ namespace BOB.ViewModels
public ICommand ReloadCommand { get; set; } public ICommand ReloadCommand { get; set; }
public CommandTreeViewModel(GlobalVariables _GlobalVariables) public CommandTreeViewModel(GlobalVariables _GlobalVariables)
{ {
GlobalVariables= _GlobalVariables; _globalVariables= _GlobalVariables;
Program = GlobalVariables.Program;
LoadedCommand = new DelegateCommand(Loaded); LoadedCommand = new DelegateCommand(Loaded);
SearchEnterCommand = new DelegateCommand(Search); SearchEnterCommand = new DelegateCommand(Search);
TreeDoubleClickCommand = new DelegateCommand<object>(TreeDoubleClick); TreeDoubleClickCommand = new DelegateCommand<object>(TreeDoubleClick);
@ -101,7 +112,7 @@ namespace BOB.ViewModels
{ {
if(Node.Children.Count == 0) if(Node.Children.Count == 0)
{ {
int index = GlobalVariables.SelectedStep?.Index >= 0 ? GlobalVariables.SelectedStep.Index : -1; int index = _globalVariables.SelectedStep?.Index >= 0 ? _globalVariables.SelectedStep.Index : -1;
if (Node.Tag is MethodInfo method) if (Node.Tag is MethodInfo method)
{ {
AddMethodToProgram(method, index); AddMethodToProgram(method, index);

View File

@ -1,7 +1,9 @@
using Logger; using Logger;
using Prism.Mvvm; using Prism.Mvvm;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading;
namespace BOB.ViewModels namespace BOB.ViewModels
{ {
@ -24,7 +26,8 @@ namespace BOB.ViewModels
public void OnLogAdded(string message, string color) public void OnLogAdded(string message, string color)
{ {
var brush = (Brush)new BrushConverter().ConvertFromString(color); var brush = (Brush)new BrushConverter().ConvertFromString(color);
Logs.Add(new LogItem { Message = message, Color = brush }); Application.Current.Dispatcher.Invoke(() => Logs.Add(new LogItem { Message = message, Color = brush }));
} }
} }

View File

@ -16,11 +16,17 @@ namespace BOB.ViewModels
public class ParametersManagerViewModel:BindableBase public class ParametersManagerViewModel:BindableBase
{ {
#region #region
private ProgramModel _program;
public ProgramModel Program public ProgramModel Program
{ {
get => _program; get => _globalVariables.Program;
set => SetProperty(ref _program, value); set
{
if (_globalVariables.Program != value)
{
_globalVariables.Program = value;
RaisePropertyChanged();
}
}
} }
private ParameterModel _SelectedParameter; private ParameterModel _SelectedParameter;
public ParameterModel SelectedParameter public ParameterModel SelectedParameter

View File

@ -29,6 +29,45 @@ namespace BOB.ViewModels
set => SetProperty(ref _IsLeftDrawerOpen, value); set => SetProperty(ref _IsLeftDrawerOpen, value);
} }
public String RunState
{
get => _globalVariables.RunState;
set
{
if (_globalVariables.RunState != value)
{
_globalVariables.RunState = value;
RaisePropertyChanged();
}
}
}
public bool SingleStep
{
get => _globalVariables.SingleStep;
set
{
if (_globalVariables.SingleStep != value)
{
_globalVariables.SingleStep = value;
RaisePropertyChanged();
}
}
}
public PackIconKind RunIcon
{
get => _globalVariables.RunIcon;
set
{
if (_globalVariables.RunIcon != value)
{
_globalVariables.RunIcon = value;
RaisePropertyChanged();
}
}
}
#endregion #endregion
#region #region
public ICommand LeftDrawerOpenCommand { get; set; } public ICommand LeftDrawerOpenCommand { get; set; }
@ -44,14 +83,18 @@ namespace BOB.ViewModels
public ICommand OpenCommand { get; set; } public ICommand OpenCommand { get; set; }
public ICommand NewCommand { get; set; } public ICommand NewCommand { get; set; }
public ICommand SetDefaultCommand { get; set; } public ICommand SetDefaultCommand { get; set; }
public ICommand LoadCommand { get; set; }
#endregion #endregion
private IEventAggregator _eventAggregator; private IEventAggregator _eventAggregator;
private GlobalVariables _globalVariables; private GlobalVariables _globalVariables;
public ShellViewModel(IEventAggregator eventAggregator, IContainerProvider containerProvider,GlobalVariables globalVariables) private StepRunning _stepRunning;
private Task? currentExecutionTask;
public ShellViewModel(IEventAggregator eventAggregator, IContainerProvider containerProvider,GlobalVariables globalVariables, StepRunning stepRunning)
{ {
_eventAggregator= eventAggregator; _eventAggregator = eventAggregator;
_globalVariables= globalVariables; _globalVariables = globalVariables;
_stepRunning=stepRunning;
LeftDrawerOpenCommand = new DelegateCommand(LeftDrawerOpen); LeftDrawerOpenCommand = new DelegateCommand(LeftDrawerOpen);
MinimizeCommand = new DelegateCommand<Window>(MinimizeWindow); MinimizeCommand = new DelegateCommand<Window>(MinimizeWindow);
MaximizeCommand = new DelegateCommand<Window>(MaximizeWindow); MaximizeCommand = new DelegateCommand<Window>(MaximizeWindow);
@ -61,28 +104,45 @@ namespace BOB.ViewModels
ResotrationCommand = new DelegateCommand(Resotration); ResotrationCommand = new DelegateCommand(Resotration);
StopCommand = new DelegateCommand(Stop); StopCommand = new DelegateCommand(Stop);
NewCommand = new DelegateCommand(New); NewCommand = new DelegateCommand(New);
OpenCommand = new DelegateCommand(Open); OpenCommand = new DelegateCommand<string>(Open);
SaveAsCommand = new DelegateCommand(SaveAs); SaveAsCommand = new DelegateCommand(SaveAs);
SaveCommand = new DelegateCommand(Save); SaveCommand = new DelegateCommand(Save);
SetDefaultCommand = new DelegateCommand(SetDefault); SetDefaultCommand = new DelegateCommand(SetDefault);
LoadCommand = new DelegateCommand(Load);
} }
#region ToolBar命令 #region ToolBar命令
private void Load()
{
if (SystemConfig.Instance.DefaultSubProgramFilePath != null)
{
if (File.Exists(SystemConfig.Instance.DefaultSubProgramFilePath))
{
Open(SystemConfig.Instance.DefaultSubProgramFilePath);
}
}
}
private void SetDefault() private void SetDefault()
{ {
if(_globalVariables.CurrentFilePath!=null) if(_globalVariables.CurrentFilePath!=null)
{ {
SystemConfig.Instance.DefaultSubProgramFilePath = _globalVariables.CurrentFilePath; SystemConfig.Instance.DefaultSubProgramFilePath = _globalVariables.CurrentFilePath;
SystemConfig.Instance.SaveToFile();
} }
} }
private void New() private void New()
{ {
_globalVariables.CurrentFilePath = null; _globalVariables.CurrentFilePath = null;
_globalVariables.Program = new(); _globalVariables.Program.Parameters.Clear();
_globalVariables.Program.Devices.Clear();
_globalVariables.Program.StepCollection.Clear();
} }
private void Open() private void Open(string filePath = null)
{ {
try try
{
// 如果没有传路径,弹出文件选择对话框
if (string.IsNullOrEmpty(filePath))
{ {
var openFileDialog = new OpenFileDialog var openFileDialog = new OpenFileDialog
{ {
@ -91,36 +151,42 @@ namespace BOB.ViewModels
InitialDirectory = @"D:\BOB\子程序" InitialDirectory = @"D:\BOB\子程序"
}; };
if (openFileDialog.ShowDialog() == true) if (openFileDialog.ShowDialog() != true)
{ return; // 用户取消选择
string filePath = openFileDialog.FileName;
// 读取 JSON 文件内容 filePath = openFileDialog.FileName;
}
// 确认文件存在
if (!File.Exists(filePath))
{
LoggerHelper.ErrorWithNotify($"文件不存在: {filePath}");
return;
}
// 读取 JSON 文件
string json = File.ReadAllText(filePath); string json = File.ReadAllText(filePath);
// 反序列化为 ProgramModel // 反序列化为 ProgramModel
var program = JsonConvert.DeserializeObject<ProgramModel>(json); var program = JsonConvert.DeserializeObject<ProgramModel>(json);
if (program != null) if (program == null)
{ {
_globalVariables.Program = program; LoggerHelper.WarnWithNotify($"文件格式不正确或为空: {filePath}");
_globalVariables.CurrentFilePath = filePath; return;
}
_globalVariables.Program.Parameters = program.Parameters;
_globalVariables.Program.Devices = program.Devices;
_globalVariables.Program.StepCollection = program.StepCollection;
_globalVariables.CurrentFilePath = filePath;
LoggerHelper.SuccessWithNotify($"成功打开文件: {filePath}"); LoggerHelper.SuccessWithNotify($"成功打开文件: {filePath}");
} }
else
{
LoggerHelper.WarnWithNotify($"文件内容格式不正确: {filePath}");
}
}
}
catch (Exception ex) catch (Exception ex)
{ {
LoggerHelper.ErrorWithNotify($"打开文件失败: {ex.Message}"); LoggerHelper.ErrorWithNotify($"打开文件失败: {ex.Message}");
} }
} }
private void SaveAs() private void SaveAs()
{ {
string defaultPath = @"D:\BOB\子程序"; string defaultPath = @"D:\BOB\子程序";
@ -197,14 +263,60 @@ namespace BOB.ViewModels
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行复位命令"); LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行复位命令");
} }
private void RunSingle() private async void RunSingle()
{ {
if (RunState == "运行")
{
SingleStep = true;
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行单步执行命令"); LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行单步执行命令");
RunState = "暂停";
RunIcon = PackIconKind.Pause;
if (_globalVariables.IsStop == null)
{
_globalVariables.IsStop = false;
currentExecutionTask = _stepRunning.ExecuteSteps(_globalVariables.Program, cancellationToken: _stepRunning.stepCTS.Token);
await currentExecutionTask;
RunState = "运行";
RunIcon = PackIconKind.Play;
_globalVariables.IsStop = null;
}
else if (_globalVariables.IsStop == true)
{
_globalVariables.IsStop = false;
}
}
} }
private void Running() private async void Running()
{ {
if (RunState == "运行")
{
SingleStep = false;
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行运行命令"); LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行运行命令");
RunState = "暂停";
RunIcon = PackIconKind.Pause;
if (_globalVariables.IsStop == null)
{
_globalVariables.IsStop = false;
currentExecutionTask = _stepRunning.ExecuteSteps(_globalVariables.Program, cancellationToken: _stepRunning.stepCTS.Token);
await currentExecutionTask;
RunState = "运行";
RunIcon = PackIconKind.Play;
_globalVariables.IsStop = null;
}
else if (_globalVariables.IsStop == true)
{
_globalVariables.IsStop = false;
}
}
else
{
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "点击暂停命令");
_globalVariables.IsStop = true;
RunState = "运行";
RunIcon = PackIconKind.Play;
}
} }
private void LeftDrawerOpen() private void LeftDrawerOpen()

View File

@ -12,17 +12,29 @@ namespace BOB.ViewModels
public class SingleStepEditViewModel:BindableBase public class SingleStepEditViewModel:BindableBase
{ {
#region #region
private Guid _ID;
public Guid ID
{
get => _ID;
set => SetProperty(ref _ID, value);
}
private StepModel _SelectedStep; private StepModel _SelectedStep;
public StepModel SelectedStep public StepModel SelectedStep
{ {
get => _SelectedStep; get => _SelectedStep;
set => SetProperty(ref _SelectedStep, value); set => SetProperty(ref _SelectedStep, value);
} }
private ProgramModel _Program;
public ProgramModel Program public ProgramModel Program
{ {
get => _Program; get => _globalVariables.Program;
set => SetProperty(ref _Program, value); set
{
if (_globalVariables.Program != value)
{
_globalVariables.Program = value;
RaisePropertyChanged();
}
}
} }
#endregion #endregion
private GlobalVariables _globalVariables; private GlobalVariables _globalVariables;
@ -37,7 +49,6 @@ namespace BOB.ViewModels
CancelEditCommand = new DelegateCommand(CancelEdit); CancelEditCommand = new DelegateCommand(CancelEdit);
SaveStepCommand = new DelegateCommand(SaveStep); SaveStepCommand = new DelegateCommand(SaveStep);
_eventAggregator.GetEvent<DeletedStepEvent>().Subscribe(DisposeSelectedStep); _eventAggregator.GetEvent<DeletedStepEvent>().Subscribe(DisposeSelectedStep);
Program = _globalVariables.Program;
} }
private void DisposeSelectedStep(Guid id) private void DisposeSelectedStep(Guid id)
@ -58,11 +69,18 @@ namespace BOB.ViewModels
{ {
return; return;
} }
var steps = _globalVariables.Program.StepCollection;
int index = steps.ToList().FindIndex(x => x.ID == ID);
if (index >= 0)
{
steps[index] = SelectedStep;
}
} }
private void EditSingleSetp() private void EditSingleSetp()
{ {
ID = _globalVariables.SelectedStep.ID;
SelectedStep = new StepModel(_globalVariables.SelectedStep); SelectedStep = new StepModel(_globalVariables.SelectedStep);
} }
} }

View File

@ -34,11 +34,17 @@ namespace BOB.ViewModels
} }
} }
} }
private ProgramModel _Program;
public ProgramModel Program public ProgramModel Program
{ {
get => _Program; get => _globalVariables.Program;
set => SetProperty(ref _Program, value); set
{
if (_globalVariables.Program != value)
{
_globalVariables.Program = value;
RaisePropertyChanged();
}
}
} }
private bool _IsAdmin; private bool _IsAdmin;
public bool IsAdmin public bool IsAdmin
@ -60,7 +66,6 @@ namespace BOB.ViewModels
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_globalVariables = _GlobalVariables; _globalVariables = _GlobalVariables;
IsAdmin = _globalVariables.IsAdmin; IsAdmin = _globalVariables.IsAdmin;
Program = _globalVariables.Program;
EditStepCommand = new DelegateCommand(EditStep); EditStepCommand = new DelegateCommand(EditStep);
CopyStepCommand = new DelegateCommand(CopyStep); CopyStepCommand = new DelegateCommand(CopyStep);
PasteStepCommand = new DelegateCommand(PasteStep); PasteStepCommand = new DelegateCommand(PasteStep);

View File

@ -18,6 +18,12 @@
<WindowChrome GlassFrameThickness="-1" /> <WindowChrome GlassFrameThickness="-1" />
</WindowChrome.WindowChrome> </WindowChrome.WindowChrome>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<materialDesign:DrawerHost x:Name="MainDrawerHost" <materialDesign:DrawerHost x:Name="MainDrawerHost"
IsLeftDrawerOpen="{Binding IsLeftDrawerOpen, Mode=TwoWay}"> IsLeftDrawerOpen="{Binding IsLeftDrawerOpen, Mode=TwoWay}">
@ -143,7 +149,7 @@
Command="{Binding RunningCommand}" Command="{Binding RunningCommand}"
Foreground="White"> Foreground="White">
<MenuItem.Icon> <MenuItem.Icon>
<materialDesign:PackIcon Kind="Play" <materialDesign:PackIcon Kind="{Binding RunIcon}"
Foreground="White" /> Foreground="White" />
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>

View File

@ -21,6 +21,7 @@
<converters:EnumValueConverter x:Key="EnumValueConverter" /> <converters:EnumValueConverter x:Key="EnumValueConverter" />
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converters:FilteredParametersConverter x:Key="FilteredParametersConverter" /> <converters:FilteredParametersConverter x:Key="FilteredParametersConverter" />
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
<GroupBox Header="单步编辑"> <GroupBox Header="单步编辑">
@ -66,7 +67,8 @@
Content="跳转" Content="跳转"
ToolTip="格式OK跳转序号/NG跳转序号默认为0/0)" /> ToolTip="格式OK跳转序号/NG跳转序号默认为0/0)" />
<TextBox MinWidth="120" <TextBox MinWidth="120"
materialDesign:HintAssist.Hint="" /> Text="{Binding SelectedStep.GotoSettingString}"
materialDesign:HintAssist.Hint="格式OK跳转序号/NG跳转序号默认为0/0)" />
</StackPanel> </StackPanel>
<!-- 备注 --> <!-- 备注 -->

View File

@ -8,6 +8,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ncalc" Version="1.3.8" />
<PackageReference Include="Prism.Unity" Version="9.0.537" /> <PackageReference Include="Prism.Unity" Version="9.0.537" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using NCalc;
namespace Common.Tools
{
public class ExpressionEvaluator
{
public static bool EvaluateExpression(string expression, Dictionary<string, object>? variables = null)
{
try
{
// 预处理:替换中文变量名为英文别名
var (processedExpression, processedVariables) = PreprocessExpression(expression, variables);
var expr = new Expression(processedExpression, EvaluateOptions.IgnoreCase);
if (processedVariables != null)
{
foreach (var kvp in processedVariables)
{
expr.Parameters[kvp.Key] = kvp.Value;
}
}
if (expr.HasErrors())
{
throw new ArgumentException($"条件表达式格式错误: {expr.Error}");
}
var result = expr.Evaluate();
return Convert.ToBoolean(result);
}
catch (Exception ex)
{
throw new ArgumentException($"条件表达式异常: {ex.Message}");
}
}
private static (string, Dictionary<string, object>) PreprocessExpression(
string expression,
Dictionary<string, object>? variables)
{
if (variables == null || variables.Count == 0)
return (expression, variables ?? new Dictionary<string, object>());
// 生成变量名映射 (中文 -> 英文别名)
var chineseToAlias = new Dictionary<string, string>();
var aliasToValue = new Dictionary<string, object>();
int counter = 1;
foreach (var key in variables.Keys)
{
if (ContainsChinese(key))
{
string alias = $"var_{counter}";
counter++;
chineseToAlias[key] = alias;
aliasToValue[alias] = variables[key];
}
}
// 如果没有中文变量名,直接返回原始数据
if (chineseToAlias.Count == 0)
return (expression, variables);
// 替换表达式中的中文变量名
string processedExpression = expression;
foreach (var pair in chineseToAlias)
{
// 使用正则确保完整匹配变量名
string pattern = $@"\b{Regex.Escape(pair.Key)}\b";
processedExpression = Regex.Replace(
processedExpression,
pattern,
pair.Value,
RegexOptions.Compiled
);
}
// 创建新变量字典(英文别名 + 原始英文变量)
var newVariables = new Dictionary<string, object>(aliasToValue);
foreach (var key in variables.Keys)
{
if (!ContainsChinese(key))
{
newVariables[key] = variables[key];
}
}
return (processedExpression, newVariables);
}
// 检查字符串是否包含中文字符
private static bool ContainsChinese(string text)
{
return text.Any(c => c >= 0x4E00 && c <= 0x9FFF);
}
}
}

View File

@ -14,6 +14,7 @@ namespace Logger
public static readonly ILogger Logger = LogManager.GetLogger("InfoLogger"); public static readonly ILogger Logger = LogManager.GetLogger("InfoLogger");
public static readonly ILogger sqlLogger = LogManager.GetLogger("SqlLogger"); public static readonly ILogger sqlLogger = LogManager.GetLogger("SqlLogger");
// 日志事件UI可以订阅 // 日志事件UI可以订阅
public static event Action<string, string>? LogAdded; public static event Action<string, string>? LogAdded;
public static void InfoWithNotify(string message) public static void InfoWithNotify(string message)
@ -50,7 +51,32 @@ namespace Logger
Logger.Error(message); Logger.Error(message);
NotifyUI(message, "red"); NotifyUI(message, "red");
} }
public static void InfoWithNotify(string message,int depth)
{
Logger.Info(message); // 写入 NLog
NotifyUI(message, "lightblue"); // 触发UI显示
}
public static void SuccessWithNotify(string message, int depth )
{
Logger.Info(message);
NotifyUI(message, "lightgreen");
}
public static void WarnWithNotify(string message, int depth )
{
Logger.Warn(message);
NotifyUI(message, "orange");
}
public static void ErrorWithNotify(string message, int depth )
{
Logger.Error(message);
NotifyUI(message, "red");
}
private static void NotifyUI(string message, string color) private static void NotifyUI(string message, string color)
{ {
LogAdded?.Invoke(message,color); LogAdded?.Invoke(message,color);