完成执行逻辑

This commit is contained in:
hsc 2025-11-13 11:12:07 +08:00
parent b69a469c89
commit 691d894a12
19 changed files with 347 additions and 268 deletions

View File

@ -0,0 +1,22 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace BOB.Converters
{
public class InverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// null 视为 false再取反 → true
var b = value as bool?;
return !(b ?? false);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var b = value as bool?;
return !(b ?? false);
}
}
}

View File

@ -1,123 +0,0 @@
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,4 +1,5 @@
using BOB.Models;
using Common.PubEvent;
using Common.Tools;
using Logger;
using MaterialDesignThemes.Wpf;
@ -18,6 +19,7 @@ namespace BOB
public class StepRunning
{
private GlobalVariables _globalVariables;
private IEventAggregator _eventAggregator;
private readonly Dictionary<Guid, ParameterModel> tmpParameters = [];
@ -31,9 +33,10 @@ namespace BOB
private bool SubSingleStep = false;
private Guid TestRoundID;
public StepRunning(GlobalVariables globalVariables)
public StepRunning(GlobalVariables globalVariables,IEventAggregator eventAggregator)
{
_globalVariables = globalVariables;
_eventAggregator= eventAggregator;
}
public async Task<bool> ExecuteSteps(ProgramModel program, int depth = 0, CancellationToken cancellationToken = default)
{
@ -108,7 +111,7 @@ namespace BOB
{
if (loopStack.Count == 0)
{
LoggerHelper.ErrorWithNotify("未匹配的循环结束指令", depth);
LoggerHelper.ErrorWithNotify("未匹配的循环结束指令", depth:depth);
step.Result = 2;
index++;
continue;
@ -203,7 +206,8 @@ namespace BOB
{
_globalVariables.IsStop = true;
_globalVariables.RunState = "运行";
_globalVariables.RunIcon = PackIconKind.Play;
_globalVariables.SingleStep = false;
_eventAggregator.GetEvent<UpdateIconEvent>().Publish("Play");
}
}
}
@ -215,6 +219,7 @@ namespace BOB
{
try
{
if(_globalVariables.Program.StepCollection.Count>1)
_globalVariables.SelectedStep = null;
await Task.Delay(SystemConfig.Instance.PerformanceLevel);
@ -227,7 +232,7 @@ namespace BOB
}
if (targetType == null)
{
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误:未找到类型 {step.Method!.FullName}", depth);
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误:未找到类型 {step.Method!.FullName}", depth: depth);
step.Result = 2;
}
@ -318,7 +323,7 @@ namespace BOB
}
catch (Exception ex)
{
LoggerHelper.WarnWithNotify($"指令 [ {step.Index} ] 执行错误:参数 {param.Name} 类型转换失败: {ex.Message}", depth);
LoggerHelper.WarnWithNotify($"指令 [ {step.Index} ] 执行错误:参数 {param.Name} 类型转换失败: {ex.Message}", depth: depth);
}
}
}
@ -342,7 +347,7 @@ namespace BOB
if (method == null)
{
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误:未找到方法{step.Method.Name}", depth);
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误:未找到方法{step.Method.Name}", depth: depth);
step.Result = 2;
}
@ -350,7 +355,7 @@ namespace BOB
bool isStaticMethod = method!.IsStatic;
// 如果是实例方法,需要创建实例
if (!isMethod)
if (!isStaticMethod)
{
try
{
@ -358,7 +363,7 @@ namespace BOB
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误:创建实例失败 - {ex.Message}", depth);
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误:创建实例失败 - {ex.Message}", depth: depth);
step.Result = 2;
}
}
@ -396,7 +401,7 @@ namespace BOB
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误: {ex.InnerException?.Message ?? ex.Message}", depth);
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误: {ex.InnerException?.Message ?? ex.Message}", depth: depth);
step.Result = 2;
return;
}
@ -448,7 +453,7 @@ namespace BOB
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误: {ex.InnerException?.Message ?? ex.Message}", depth);
LoggerHelper.ErrorWithNotify($"指令 [ {step.Index} ] 执行错误: {ex.InnerException?.Message ?? ex.Message}", depth: depth);
step.Result = 2;
return;
}
@ -496,7 +501,7 @@ namespace BOB
step.Result = re ? 1 : 2;
if (step.Result == 2)
{
LoggerHelper.WarnWithNotify($"指令 [ {step.Index} ] NG:条件表达式验证失败", depth);
LoggerHelper.WarnWithNotify($"指令 [ {step.Index} ] NG:条件表达式验证失败", depth: depth);
}
}
}
@ -504,7 +509,7 @@ namespace BOB
{
if (!paraResult)
{
LoggerHelper.WarnWithNotify("参数限值校验失败", depth);
LoggerHelper.WarnWithNotify("参数限值校验失败", depth: depth);
}
step.Result = 2;
}

View File

@ -129,6 +129,10 @@ namespace BOB.ViewModels
break;
}
}
else if(Node.Tag is SubProgramItem subProgram)
{
AddSubProgramToProgram(subProgram, index);
}
}
}
}
@ -195,7 +199,7 @@ namespace BOB.ViewModels
return;
}
foreach (var filePath in Directory.GetFiles(SystemConfig.Instance.SubProgramFilePath, "*.ats"))
foreach (var filePath in Directory.GetFiles(SystemConfig.Instance.SubProgramFilePath, "*.bob"))
{
try
{
@ -470,45 +474,42 @@ namespace BOB.ViewModels
}
}
//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)
// {
// if (tmp.Devices != null && tmp.Devices.Count > 0)
// {
// foreach (var device in tmp.Devices)
// {
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)
{
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}");
// }
//}
}
}
newStep.SubProgram = tmp;
}
if (insertIndex >= 0 && insertIndex <= Program.StepCollection.Count)
{
Program.StepCollection.Insert(insertIndex, newStep);
}
else
{
Program.StepCollection.Add(newStep);
}
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"添加子程序失败: {subProgram.Name} - {ex.Message}");
}
}
private void AddLoopStartStep(int insertIndex = -1)
{

View File

@ -1,4 +1,5 @@
using BOB.Models;
using Common.PubEvent;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@ -62,11 +63,13 @@ namespace BOB.ViewModels.Dialogs
#endregion
public DialogCloseListener RequestClose { get; set; }
private GlobalVariables _globalVariables;
private IEventAggregator _eventAggregator;
public ICommand CancelCommand { get; set; }
public ICommand SaveCommand { get; set; }
public ICommand SelectionChangedCommand { get; set; }
public DeviceSettingViewModel(GlobalVariables globalVariables)
public DeviceSettingViewModel(GlobalVariables globalVariables, IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
_globalVariables = globalVariables;
CancelCommand = new DelegateCommand(Cancel);
SaveCommand = new DelegateCommand(Save);
@ -183,11 +186,12 @@ namespace BOB.ViewModels.Dialogs
public void OnDialogClosed()
{
_eventAggregator.GetEvent<OverlayEvent>().Publish(false);
}
public void OnDialogOpened(IDialogParameters parameters)
{
_eventAggregator.GetEvent<OverlayEvent>().Publish(true);
Program = _globalVariables.Program;
Mode = parameters.GetValue<string>("Mode");
if(Mode == "ADD")

View File

@ -100,6 +100,7 @@ namespace BOB.ViewModels.Dialogs
public void OnDialogOpened(IDialogParameters parameters)
{
_eventAggregator.GetEvent<OverlayEvent>().Publish(true);
Title = parameters.GetValue<string>("Title");
Message = parameters.GetValue<string>("Message");
var iconKey = parameters.GetValue<string>("Icon"); // info / error / warn

View File

@ -1,4 +1,5 @@
using BOB.Models;
using Common.PubEvent;
using SqlSugar;
using System;
using System.Collections.Generic;
@ -69,10 +70,12 @@ namespace BOB.ViewModels.Dialogs
#endregion
public DialogCloseListener RequestClose{get;set;}
private GlobalVariables _globalVariables;
private IEventAggregator _eventAggregator;
public ICommand CancelCommand { get; set; }
public ICommand SaveCommand { get; set; }
public ParameterSettingViewModel(GlobalVariables globalVariables)
public ParameterSettingViewModel(GlobalVariables globalVariables, IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
_globalVariables = globalVariables;
CancelCommand = new DelegateCommand(Cancel);
SaveCommand = new DelegateCommand(Save);
@ -112,12 +115,13 @@ namespace BOB.ViewModels.Dialogs
public void OnDialogClosed()
{
_eventAggregator.GetEvent<OverlayEvent>().Publish(false);
}
public void OnDialogOpened(IDialogParameters parameters)
{
Program=_globalVariables.Program;
_eventAggregator.GetEvent<OverlayEvent>().Publish(true);
Program =_globalVariables.Program;
Mode = parameters.GetValue<string>("Mode");
if (Mode == "ADD")
{

View File

@ -2,6 +2,7 @@
using Prism.Mvvm;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
@ -16,18 +17,21 @@ namespace BOB.ViewModels
get => _logs;
set => SetProperty(ref _logs, value);
}
public ICommand ClearLogCommand { get; set; }
public LogAreaViewModel()
{
LoggerHelper.LogAdded += OnLogAdded;
ClearLogCommand = new DelegateCommand(ClearLog);
LoggerHelper.Progress = new System.Progress<(string message, string color,int depth)>(
log =>
{
var brush = (Brush)new BrushConverter().ConvertFromString(log.color);
Logs.Add(new LogItem(log.message, brush, log.depth));
});
}
// 方便外部添加日志
public void OnLogAdded(string message, string color)
private void ClearLog()
{
var brush = (Brush)new BrushConverter().ConvertFromString(color);
Application.Current.Dispatcher.Invoke(() => Logs.Add(new LogItem { Message = message, Color = brush }));
Logs.Clear();
}
}
@ -36,5 +40,12 @@ namespace BOB.ViewModels
{
public string Message { get; set; }
public Brush Color { get; set; } = Brushes.Black;
public int Depth { get; set; }
public LogItem(string message, Brush color, int depth = 0)
{
Message = new string(' ', depth * 20) + message;
Color = color;
Depth = depth;
}
}
}

View File

@ -97,13 +97,12 @@ namespace BOB.ViewModels
{
if (r.Result == ButtonResult.OK)
{
_eventAggregator.GetEvent<ParamsChangedEvent>().Publish();
}
else
{
}
_eventAggregator.GetEvent<OverlayEvent>().Publish(false);
});
}
@ -114,18 +113,16 @@ namespace BOB.ViewModels
{
{ "Mode", "ADD" }
};
_eventAggregator.GetEvent<OverlayEvent>().Publish(true);
_dialogService.ShowDialog("DeviceSetting", param, (r) =>
{
if (r.Result == ButtonResult.OK)
{
_eventAggregator.GetEvent<ParamsChangedEvent>().Publish();
}
else
{
}
_eventAggregator.GetEvent<OverlayEvent>().Publish(false);
});
}
@ -144,13 +141,13 @@ namespace BOB.ViewModels
{
if (r.Result == ButtonResult.OK)
{
_eventAggregator.GetEvent<ParamsChangedEvent>().Publish();
}
else
{
}
_eventAggregator.GetEvent<OverlayEvent>().Publish(false);
});
}
@ -164,13 +161,13 @@ namespace BOB.ViewModels
{
if (r.Result == ButtonResult.OK)
{
_eventAggregator.GetEvent<ParamsChangedEvent>().Publish();
}
else
{
}
_eventAggregator.GetEvent<OverlayEvent>().Publish(false);
});
}
#endregion

View File

@ -1,5 +1,6 @@
using BOB.Models;
using BOB.Views;
using Common.PubEvent;
using Common.PubEvents;
using Logger;
using MaterialDesignThemes.Wpf;
@ -28,6 +29,13 @@ namespace BOB.ViewModels
get => _IsLeftDrawerOpen;
set => SetProperty(ref _IsLeftDrawerOpen, value);
}
private bool _IsTerminate=false;
public bool IsTerminate
{
get => _IsTerminate;
set => SetProperty(ref _IsTerminate, value);
}
public String RunState
{
@ -109,6 +117,17 @@ namespace BOB.ViewModels
SaveCommand = new DelegateCommand(Save);
SetDefaultCommand = new DelegateCommand(SetDefault);
LoadCommand = new DelegateCommand(Load);
_eventAggregator.GetEvent<UpdateIconEvent>().Subscribe(UpdateRunIcon);
}
private void UpdateRunIcon(string obj)
{
RunIcon= obj switch
{
"Play" => PackIconKind.Play,
"Pause" => PackIconKind.Pause,
_ => RunIcon
};
}
#region ToolBar命令
private void Load()
@ -206,10 +225,9 @@ namespace BOB.ViewModels
{
_globalVariables.CurrentFilePath = saveFileDialog.FileName;
SaveProgramToFile(_globalVariables.CurrentFilePath);
}
// 记录日志
LoggerHelper.InfoWithNotify($"{_globalVariables.UserName} 另存为文件成功: {saveFileDialog.FileName}");
}
}
private void SaveProgramToFile(string filePath)
@ -251,16 +269,29 @@ namespace BOB.ViewModels
return;
}
SaveProgramToFile(_globalVariables.CurrentFilePath);
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "保存文件成功");
}
private void Stop()
{
LoggerHelper.InfoWithNotify(_globalVariables.UserName+"执行停止命令");
if (_globalVariables.IsStop == false)
{
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行停止命令");
IsTerminate = true;
_globalVariables.IsStop = null;
RunState = "运行";
RunIcon = PackIconKind.Play;
_stepRunning.stepCTS.Cancel();
}
}
private void Resotration()
{
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行复位命令");
IsTerminate = false;
currentExecutionTask = null;
_stepRunning.stepCTS = new();
_globalVariables.IsStop = null;
_stepRunning.ResetAllStepStatus(_globalVariables.Program);
}
private async void RunSingle()
@ -274,11 +305,12 @@ namespace BOB.ViewModels
if (_globalVariables.IsStop == null)
{
_globalVariables.IsStop = false;
currentExecutionTask = _stepRunning.ExecuteSteps(_globalVariables.Program, cancellationToken: _stepRunning.stepCTS.Token);
if(currentExecutionTask==null)currentExecutionTask = _stepRunning.ExecuteSteps(_globalVariables.Program, cancellationToken: _stepRunning.stepCTS.Token);
await currentExecutionTask;
RunState = "运行";
RunIcon = PackIconKind.Play;
_globalVariables.IsStop = null;
IsTerminate = true;
}
else if (_globalVariables.IsStop == true)
{
@ -299,11 +331,12 @@ namespace BOB.ViewModels
if (_globalVariables.IsStop == null)
{
_globalVariables.IsStop = false;
currentExecutionTask = _stepRunning.ExecuteSteps(_globalVariables.Program, cancellationToken: _stepRunning.stepCTS.Token);
if (currentExecutionTask == null) currentExecutionTask = _stepRunning.ExecuteSteps(_globalVariables.Program, cancellationToken: _stepRunning.stepCTS.Token);
await currentExecutionTask;
RunState = "运行";
RunIcon = PackIconKind.Play;
_globalVariables.IsStop = null;
IsTerminate = true;
}
else if (_globalVariables.IsStop == true)
{

View File

@ -1,10 +1,15 @@
using BOB.Models;
using Common.PubEvent;
using Logger;
using Microsoft.IdentityModel.Logging;
using SqlSugar.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace BOB.ViewModels
@ -39,16 +44,24 @@ namespace BOB.ViewModels
#endregion
private GlobalVariables _globalVariables;
private IEventAggregator _eventAggregator;
private IDialogService _dialogService;
public ICommand CancelEditCommand { get; set; }
public ICommand SaveStepCommand { get; set; }
public SingleStepEditViewModel(GlobalVariables globalVariables,IEventAggregator eventAggregator)
public SingleStepEditViewModel(GlobalVariables globalVariables,IEventAggregator eventAggregator,IDialogService dialogService)
{
_globalVariables= globalVariables;
_eventAggregator= eventAggregator;
_dialogService = dialogService;
_eventAggregator.GetEvent<EditSetpEvent>().Subscribe(EditSingleSetp);
CancelEditCommand = new DelegateCommand(CancelEdit);
SaveStepCommand = new DelegateCommand(SaveStep);
_eventAggregator.GetEvent<DeletedStepEvent>().Subscribe(DisposeSelectedStep);
_eventAggregator.GetEvent<ParamsChangedEvent>().Subscribe(ParamsChanged);
}
private void ParamsChanged()
{
CancelEdit();
}
private void DisposeSelectedStep(Guid id)
@ -74,10 +87,105 @@ namespace BOB.ViewModels
if (index >= 0)
{
steps[index] = SelectedStep;
if (steps[index].Method != null)
{
if (steps[index].StepType == "循环开始")
{
try
{
steps[index].LoopCount = Convert.ToInt32(SelectedStep.Method!.Parameters[0].Value);
}
catch
{
LoggerHelper.ErrorWithNotify("循环指令参数设置错误:类型转换失败");
}
}
else
{
(steps[index].OKGotoStepID, steps[index].NGGotoStepID) = GetOKNGGotoStepID(SelectedStep.GotoSettingString);
for (int i = 0; i < steps[index].Method.Parameters.Count; i++)
{
var editedParam = SelectedStep.Method!.Parameters[i];
var originalParam = steps[index].Method.Parameters[i];
if (editedParam.IsUseVar)
{
originalParam.VariableName = editedParam.VariableName;
originalParam.VariableID = _globalVariables.Program.Parameters.FirstOrDefault(x => x.Name == editedParam.VariableName)!.ID;
}
originalParam.Value = editedParam.Value;
originalParam.IsUseVar = editedParam.IsUseVar;
originalParam.LowerLimit = editedParam.LowerLimit;
originalParam.UpperLimit = editedParam.UpperLimit;
}
var parameters = new DialogParameters
{
{ "Title", "提示" },
{ "Message", "保存成功!" },
{ "Icon", "info" },
{ "ShowOk", true }
};
_dialogService.ShowDialog("MessageBox", parameters);
}
}
else if (steps[index].SubProgram != null)
{
if (SelectedStep.SubProgram.Parameters.Where(x => x.VariableName == null && x.IsUseVar == true).FirstOrDefault() != null)
{
var parameters1 = new DialogParameters
{
{ "Title", "警告" },
{ "Message", "选中变量不得为空!" },
{ "Icon", "warn" },
{ "ShowOk", true },
};
_dialogService.ShowDialog("MessageBox",parameters1);
return;
}
(steps[index].OKGotoStepID, steps[index].NGGotoStepID) = GetOKNGGotoStepID(SelectedStep.GotoSettingString);
for (int i = 0; i < steps[index].SubProgram.Parameters.Count; i++)
{
var editedParam = SelectedStep.SubProgram!.Parameters[i];
var originalParam = steps[index].SubProgram.Parameters[i];
if (editedParam.IsUseVar)
{
originalParam.VariableName = editedParam.VariableName;
originalParam.VariableID = _globalVariables.Program.Parameters.FirstOrDefault(x => x.Name == editedParam.VariableName)!.ID;
}
originalParam.Value = editedParam.Value;
originalParam.IsUseVar = editedParam.IsUseVar;
originalParam.LowerLimit = editedParam.LowerLimit;
originalParam.UpperLimit = editedParam.UpperLimit;
}
var parameters = new DialogParameters
{
{ "Title", "提示" },
{ "Message", "保存成功!" },
{ "Icon", "info" },
{ "ShowOk", true }
};
_dialogService.ShowDialog("MessageBox", parameters);
}
}
}
private (Guid,Guid) GetOKNGGotoStepID(string GotoSettingString)
{
if (string.IsNullOrWhiteSpace(GotoSettingString))
return (Guid.Empty, Guid.Empty);
var match = Regex.Match(GotoSettingString, @"^(\d+)\s*/\s*(\d+)$");
if (match.Success)
{
int ok = int.Parse(match.Groups[1].Value);
int ng = int.Parse(match.Groups[2].Value);
Guid okGuid = _globalVariables.Program.StepCollection.ElementAtOrDefault(ok-1)?.ID ?? Guid.Empty;
Guid ngGuid = _globalVariables.Program.StepCollection.ElementAtOrDefault(ng-1)?.ID ?? Guid.Empty;
return (okGuid, ngGuid);
}
return (Guid.Empty, Guid.Empty);
}
private void EditSingleSetp()
{
ID = _globalVariables.SelectedStep.ID;

View File

@ -11,10 +11,16 @@
<Grid>
<GroupBox Header="系统运行日志">
<ListView FontWeight="DemiBold"
<ListView x:Name="LogListView"
FontWeight="DemiBold"
ItemsSource="{Binding Logs}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="清空日志"
Command="{Binding ClearLogCommand}" />
</ContextMenu>
</ListView.ContextMenu>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="{Binding Color}"

View File

@ -1,28 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using BOB.ViewModels;
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;
namespace BOB.Views
{
/// <summary>
/// LogArea.xaml 的交互逻辑
/// </summary>
public partial class LogArea : UserControl
{
public LogArea()
{
InitializeComponent();
// 绑定 DataContext 后,订阅日志集合变化
this.Loaded += (s, e) =>
{
if (DataContext is LogAreaViewModel vm)
{
vm.Logs.CollectionChanged += Logs_CollectionChanged;
}
};
}
private void Logs_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// 每次新增日志,滚动到最后一条
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
if (this.LogListView.Items.Count > 0)
{
this.LogListView.ScrollIntoView(this.LogListView.Items[^1]);
}
}
}
}
}

View File

@ -7,7 +7,7 @@
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
WindowStartupLocation="CenterScreen"
Topmost="false"
xmlns:converter="clr-namespace:BOB.Converters"
mc:Ignorable="d"
prism:ViewModelLocator.AutoWireViewModel="True"
WindowStyle="None"
@ -23,7 +23,9 @@
<i:InvokeCommandAction Command="{Binding LoadCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Window.Resources>
<converter:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</Window.Resources>
<materialDesign:DrawerHost x:Name="MainDrawerHost"
IsLeftDrawerOpen="{Binding IsLeftDrawerOpen, Mode=TwoWay}">
@ -146,6 +148,7 @@
<MenuItem FontSize="14"
Height="50"
Header="运行"
IsEnabled="{Binding IsTerminate, Converter={StaticResource InverseBooleanConverter}}"
Command="{Binding RunningCommand}"
Foreground="White">
<MenuItem.Icon>
@ -156,6 +159,7 @@
<MenuItem FontSize="14"
Height="50"
Header="单步执行"
IsEnabled="{Binding IsTerminate, Converter={StaticResource InverseBooleanConverter}}"
Command="{Binding RunSingleCommand}"
Foreground="White">
<MenuItem.Icon>
@ -176,6 +180,7 @@
<MenuItem FontSize="14"
Height="50"
Header="复位"
IsEnabled="{Binding IsTerminate}"
Command="{Binding ResotrationCommand}"
Foreground="White">
<MenuItem.Icon>
@ -241,15 +246,15 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="454*" />
<ColumnDefinition Width="91*" />
<ColumnDefinition Width="1375*" />
<ColumnDefinition Width="57*" />
<ColumnDefinition Width="1319*" />
</Grid.ColumnDefinitions>
<ContentControl prism:RegionManager.RegionName="ShellViewManager"
Grid.ColumnSpan="3" />
Grid.ColumnSpan="4" />
<Border x:Name="Overlay"
Background="#40000000"
Visibility="Collapsed"
Panel.ZIndex="10"
Grid.RowSpan="2"
Grid.ColumnSpan="4" />
</Grid>
</materialDesign:DialogHost>

View File

@ -325,7 +325,7 @@
Margin="0,0,0,0"
materialDesign:HintAssist.Hint=""
VerticalAlignment="Bottom"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.Program.Parameters}"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext.Program.Parameters}"
SelectedValue="{Binding VariableName}"
SelectedValuePath="Name"
Visibility="{Binding Category, Converter={StaticResource ParameterCategoryToVisibilityConverter}, ConverterParameter=Inverse}">

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.PubEvent
{
public class ParamsChangedEvent:PubSubEvent
{
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.PubEvent
{
public class UpdateIconEvent: PubSubEvent<string>
{
}
}

View File

@ -13,22 +13,23 @@ namespace Logger
{
public static readonly ILogger Logger = LogManager.GetLogger("InfoLogger");
public static readonly ILogger sqlLogger = LogManager.GetLogger("SqlLogger");
// 日志事件UI可以订阅
public static event Action<string, string>? LogAdded;
public static void InfoWithNotify(string message)
public static IProgress<(string message, string color,int depth)> Progress { get; set; }
static LoggerHelper()
{
Progress = new Progress<(string message, string color, int depth)>();
}
public static void InfoWithNotify(string message, int depth=0)
{
Logger.Info(message); // 写入 NLog
NotifyUI(message, "lightblue"); // 触发UI显示
NotifyUI(message, "blue", depth); // 触发UI显示
}
public static void SuccessWithNotify(string message)
public static void SuccessWithNotify(string message, int depth=0)
{
Logger.Info(message);
NotifyUI(message, "lightgreen");
NotifyUI(message, "lightgreen", depth);
}
public static void WarnWithNotify(string message, string stackTrace = null)
public static void WarnWithNotify(string message, string stackTrace = null, int depth = 0)
{
if (!string.IsNullOrEmpty(stackTrace))
{
@ -37,10 +38,10 @@ namespace Logger
}
Logger.Warn(message);
NotifyUI(message, "orange");
NotifyUI(message, "orange", depth);
}
public static void ErrorWithNotify(string message, string stackTrace = null)
public static void ErrorWithNotify(string message, string stackTrace = null, int depth = 0)
{
if (!string.IsNullOrEmpty(stackTrace))
{
@ -49,37 +50,11 @@ namespace Logger
}
Logger.Error(message);
NotifyUI(message, "red");
NotifyUI(message, "red", depth);
}
public static void InfoWithNotify(string message,int depth)
private static void NotifyUI(string message, string color, 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)
{
LogAdded?.Invoke(message,color);
Progress.Report((message, color, depth));
}
// 解析堆栈,找到项目文件路径和行号
public static string GetProjectStackLine(string stackTrace)