修改全局变量前

This commit is contained in:
hsc 2025-11-10 15:46:29 +08:00
parent 7e19e55e39
commit 45ebf1f2b6
22 changed files with 399 additions and 170 deletions

View File

@ -25,66 +25,7 @@
<!--自定义style--> <!--自定义style-->
<ResourceDictionary Source="Resources\Styles\WindowStyle.xaml"></ResourceDictionary> <ResourceDictionary Source="Resources\Styles\WindowStyle.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<Style TargetType="TextBox"
BasedOn="{StaticResource MaterialDesignFilledTextBox}">
<!-- 禁用自动化属性,避免 null 报错 -->
<Setter Property="AutomationProperties.Name"
Value="" />
<!-- 使用底线风格 -->
<Setter Property="materialDesign:TextFieldAssist.DecorationVisibility"
Value="Hidden" />
<Setter Property="materialDesign:TextFieldAssist.UnderlineBrush"
Value="{DynamicResource MaterialDesignDivider}" />
<Setter Property="BorderThickness"
Value="0,0,0,1" />
<Setter Property="BorderBrush"
Value="{DynamicResource MaterialDesignDivider}" />
<!-- 字体靠下 -->
<Setter Property="VerticalContentAlignment"
Value="Bottom" />
<Setter Property="Padding"
Value="0,0,0,2" />
<!-- 其他优化 -->
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Foreground"
Value="{DynamicResource MaterialDesignBody}" />
<Setter Property="FontSize"
Value="14" />
</Style>
<Style TargetType="ComboBox"
BasedOn="{StaticResource MahApps.Styles.ComboBox}">
<!-- 禁用自动化属性,避免 null 报错 -->
<Setter Property="AutomationProperties.Name"
Value="" />
<!-- 使用底线风格 -->
<Setter Property="materialDesign:TextFieldAssist.DecorationVisibility"
Value="Hidden" />
<Setter Property="materialDesign:TextFieldAssist.UnderlineBrush"
Value="{DynamicResource MaterialDesignDivider}" />
<Setter Property="BorderThickness"
Value="0,0,0,1" />
<Setter Property="BorderBrush"
Value="{DynamicResource MaterialDesignDivider}" />
<!-- 字体靠下 -->
<Setter Property="VerticalContentAlignment"
Value="Bottom" />
<Setter Property="Padding"
Value="0,0,0,2" />
<!-- 其他优化 -->
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Foreground"
Value="{DynamicResource MaterialDesignBody}" />
<Setter Property="FontSize"
Value="14" />
</Style>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>
</prism:PrismApplication> </prism:PrismApplication>

View File

@ -9,13 +9,13 @@ namespace BOB
{ {
public class GlobalVariables public class GlobalVariables
{ {
public ProgramModel Program = new(); public ProgramModel Program { get; set; } = new();
public bool IsAdmin=true; public bool IsAdmin { get; set; } = true;
public String UserName="hsc"; public String UserName { get; set; } = "hsc";
public String Title = "主程序"; public String Title { get; set; } = "主程序";
public string CurrentFilePath; public string CurrentFilePath { get; set; }
public StepModel SelectedStep; public StepModel SelectedStep { get; set; }
public ParameterModel SelectedParameter; public ParameterModel SelectedParameter { get; set; }
public DeviceModel SelectedDevice; public DeviceModel SelectedDevice { get; set; }
} }
} }

View File

@ -39,7 +39,7 @@ namespace BOB.Models
#endregion #endregion
private Guid _id = Guid.NewGuid(); private Guid _id = Guid.NewGuid();
[JsonIgnore] // 添加 JsonIgnore
public Guid ID public Guid ID
{ {
get => _id; get => _id;
@ -47,7 +47,7 @@ namespace BOB.Models
} }
private bool _isUsed = true; private bool _isUsed = true;
[JsonIgnore] // 添加 JsonIgnore
public bool IsUsed public bool IsUsed
{ {
get => _isUsed; get => _isUsed;
@ -55,7 +55,7 @@ namespace BOB.Models
} }
private int _index; private int _index;
[JsonIgnore] // 添加 JsonIgnore
public int Index public int Index
{ {
get => _index; get => _index;
@ -63,7 +63,7 @@ namespace BOB.Models
} }
private string? _name; private string? _name;
[JsonIgnore] // 添加 JsonIgnore
public string? Name public string? Name
{ {
get => _name; get => _name;
@ -71,7 +71,7 @@ namespace BOB.Models
} }
private string? _stepType; private string? _stepType;
[JsonIgnore] // 添加 JsonIgnore
public string? StepType public string? StepType
{ {
get => _stepType; get => _stepType;
@ -79,7 +79,7 @@ namespace BOB.Models
} }
private MethodModel? _method; private MethodModel? _method;
[JsonIgnore] // 添加 JsonIgnore
public MethodModel? Method public MethodModel? Method
{ {
get => _method; get => _method;
@ -87,7 +87,7 @@ namespace BOB.Models
} }
private ProgramModel? _subProgram; private ProgramModel? _subProgram;
[JsonIgnore] // 添加 JsonIgnore
public ProgramModel? SubProgram public ProgramModel? SubProgram
{ {
get => _subProgram; get => _subProgram;
@ -95,15 +95,15 @@ namespace BOB.Models
} }
private int? _loopCount; private int? _loopCount;
[JsonIgnore] // 添加 JsonIgnore
public int? LoopCount public int? LoopCount
{ {
get => _loopCount; get => _loopCount;
set => SetProperty(ref _loopCount, value); set => SetProperty(ref _loopCount, value);
} }
[JsonIgnore]
private int? _currentLoopCount; private int? _currentLoopCount;
[JsonIgnore] // 添加 JsonIgnore [JsonIgnore]
public int? CurrentLoopCount public int? CurrentLoopCount
{ {
get => _currentLoopCount; get => _currentLoopCount;
@ -111,23 +111,23 @@ namespace BOB.Models
} }
private Guid? _loopStartStepId; private Guid? _loopStartStepId;
[JsonIgnore] // 添加 JsonIgnore
public Guid? LoopStartStepId public Guid? LoopStartStepId
{ {
get => _loopStartStepId; get => _loopStartStepId;
set => SetProperty(ref _loopStartStepId, value); set => SetProperty(ref _loopStartStepId, value);
} }
[JsonIgnore]
private int _result = -1; private int _result = -1;
[JsonIgnore] // 添加 JsonIgnore [JsonIgnore]
public int Result public int Result
{ {
get => _result; get => _result;
set => SetProperty(ref _result, value); set => SetProperty(ref _result, value);
} }
[JsonIgnore]
private int? _runTime; private int? _runTime;
[JsonIgnore] // 添加 JsonIgnore [JsonIgnore]
public int? RunTime public int? RunTime
{ {
get => _runTime; get => _runTime;
@ -135,7 +135,7 @@ namespace BOB.Models
} }
private string? _okExpression; private string? _okExpression;
[JsonIgnore] // 添加 JsonIgnore
public string? OKExpression public string? OKExpression
{ {
get => _okExpression; get => _okExpression;
@ -143,7 +143,7 @@ namespace BOB.Models
} }
private string _gotoSettingString = ""; private string _gotoSettingString = "";
[JsonIgnore] // 添加 JsonIgnore
public string GotoSettingString public string GotoSettingString
{ {
get => _gotoSettingString; get => _gotoSettingString;
@ -151,7 +151,7 @@ namespace BOB.Models
} }
private Guid? _okGotoStepID; private Guid? _okGotoStepID;
[JsonIgnore] // 添加 JsonIgnore
public Guid? OKGotoStepID public Guid? OKGotoStepID
{ {
get => _okGotoStepID; get => _okGotoStepID;
@ -159,7 +159,7 @@ namespace BOB.Models
} }
private Guid? _ngGotoStepID; private Guid? _ngGotoStepID;
[JsonIgnore] // 添加 JsonIgnore
public Guid? NGGotoStepID public Guid? NGGotoStepID
{ {
get => _ngGotoStepID; get => _ngGotoStepID;
@ -167,7 +167,7 @@ namespace BOB.Models
} }
private string? _description; private string? _description;
[JsonIgnore] // 添加 JsonIgnore
public string? Description public string? Description
{ {
get => _description; get => _description;

View File

@ -5,6 +5,8 @@
TargetType="Window"> TargetType="Window">
<Setter Property="WindowStyle" <Setter Property="WindowStyle"
Value="None" /> Value="None" />
<Setter Property="Topmost"
Value="True" />
<Setter Property="ResizeMode" <Setter Property="ResizeMode"
Value="NoResize" /> Value="NoResize" />
<Setter Property="ShowInTaskbar" <Setter Property="ShowInTaskbar"

View File

@ -163,13 +163,13 @@ namespace BOB.ViewModels
} }
catch (Exception xmlEx) catch (Exception xmlEx)
{ {
LoggerHelper.warnWithNotify($"加载XML注释失败: {Path.GetFileName(xmlPath)} - {xmlEx.Message}"); LoggerHelper.WarnWithNotify($"加载XML注释失败: {Path.GetFileName(xmlPath)} - {xmlEx.Message}");
} }
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
LoggerHelper.warnWithNotify($"无法加载程序集 {Path.GetFileName(dllPath)}: {ex.Message}"); LoggerHelper.WarnWithNotify($"无法加载程序集 {Path.GetFileName(dllPath)}: {ex.Message}");
} }
} }
} }
@ -196,7 +196,7 @@ namespace BOB.ViewModels
} }
catch (Exception ex) catch (Exception ex)
{ {
LoggerHelper.warnWithNotify($"加载子程序错误: {filePath} - {ex.Message}"); LoggerHelper.WarnWithNotify($"加载子程序错误: {filePath} - {ex.Message}");
} }
} }
} }

View File

@ -152,12 +152,28 @@ namespace BOB.ViewModels.Dialogs
private void Save() private void Save()
{ {
if (Mode == "ADD")
{
Program.Devices.Add(Device);
_globalVariables.SelectedDevice = Device;
}
else
{
var index = Program.Devices
.Select((x, i) => new { x, i })
.FirstOrDefault(p => p.x.ID == _globalVariables.SelectedDevice.ID)?.i;
if (index.HasValue)
{
Program.Devices[index.Value] = Device;
}
}
RequestClose.Invoke(ButtonResult.OK);
} }
private void Cancel() private void Cancel()
{ {
RequestClose.Invoke(); RequestClose.Invoke(ButtonResult.No);
} }
#region Prism Dialog #region Prism Dialog
public bool CanCloseDialog() public bool CanCloseDialog()
@ -183,7 +199,7 @@ namespace BOB.ViewModels.Dialogs
} }
else else
{ {
Device = _globalVariables.SelectedDevice; Device = new DeviceModel(_globalVariables.SelectedDevice);
IsAdd = false; IsAdd = false;
} }
} }

View File

@ -1,4 +1,5 @@
using BOB.Models; using BOB.Models;
using SqlSugar;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@ -79,12 +80,29 @@ namespace BOB.ViewModels.Dialogs
private void Save() private void Save()
{ {
if (Mode == "ADD")
{
Program.Parameters.Add(Parameter);
_globalVariables.SelectedParameter = Parameter;
}
else
{
var index = Program.Parameters
.Select((x, i) => new { x, i })
.FirstOrDefault(p => p.x.ID == _globalVariables.SelectedParameter.ID)?.i;
if (index.HasValue)
{
Program.Parameters[index.Value] = Parameter;
}
}
RequestClose.Invoke(ButtonResult.OK);
} }
private void Cancel() private void Cancel()
{ {
RequestClose.Invoke(); RequestClose.Invoke(ButtonResult.No);
} }
#region Prism Dialog #region Prism Dialog
public bool CanCloseDialog() public bool CanCloseDialog()
@ -100,8 +118,15 @@ namespace BOB.ViewModels.Dialogs
public void OnDialogOpened(IDialogParameters parameters) public void OnDialogOpened(IDialogParameters parameters)
{ {
Program=_globalVariables.Program; Program=_globalVariables.Program;
Parameter = _globalVariables.SelectedParameter;
Mode = parameters.GetValue<string>("Mode"); Mode = parameters.GetValue<string>("Mode");
if (Mode == "ADD")
{
Parameter = new();
}
else
{
Parameter = new ParameterModel(_globalVariables.SelectedParameter);
}
} }
#endregion #endregion
} }

View File

@ -1,4 +1,5 @@
using BOB.Models; using BOB.Models;
using Common.PubEvent;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -12,7 +13,7 @@ using System.Xml;
namespace BOB.ViewModels namespace BOB.ViewModels
{ {
public class ParametersManagerViewModel:BindableBase,IDialogAware public class ParametersManagerViewModel:BindableBase
{ {
#region #region
private ProgramModel _program; private ProgramModel _program;
@ -25,18 +26,30 @@ namespace BOB.ViewModels
public ParameterModel SelectedParameter public ParameterModel SelectedParameter
{ {
get => _SelectedParameter; get => _SelectedParameter;
set => SetProperty(ref _SelectedParameter, value); set
{
if (SetProperty(ref _SelectedParameter, value))
{
_globalVariables.SelectedParameter = value;
}
}
} }
private DeviceModel _SelectedDevice; private DeviceModel _SelectedDevice;
public DeviceModel SelectedDevice public DeviceModel SelectedDevice
{ {
get => _SelectedDevice; get => _SelectedDevice;
set => SetProperty(ref _SelectedDevice, value); set
{
if (SetProperty(ref _SelectedDevice, value))
{
_globalVariables.SelectedDevice = value;
}
}
} }
#endregion #endregion
public DialogCloseListener RequestClose { get; set; } private GlobalVariables _globalVariables { get; set; }
GlobalVariables _GlobalVariables { get; set; } private IDialogService _dialogService { get; set; }
IDialogService _dialogService { get; set; } private IEventAggregator _eventAggregator { get; set; }
public ICommand ParameterAddCommand { get; set; } public ICommand ParameterAddCommand { get; set; }
public ICommand ParameterEditCommand { get; set; } public ICommand ParameterEditCommand { get; set; }
public ICommand ParameterDeleteCommand { get; set; } public ICommand ParameterDeleteCommand { get; set; }
@ -46,11 +59,12 @@ namespace BOB.ViewModels
public ParametersManagerViewModel(GlobalVariables GlobalVariables,IDialogService dialogService) public ParametersManagerViewModel(GlobalVariables GlobalVariables,IDialogService dialogService,IEventAggregator eventAggregator)
{ {
_GlobalVariables = GlobalVariables; _globalVariables = GlobalVariables;
_eventAggregator= eventAggregator;
_dialogService = dialogService; _dialogService = dialogService;
Program = _GlobalVariables.Program; Program = _globalVariables.Program;
ParameterAddCommand = new DelegateCommand(ParameterAdd); ParameterAddCommand = new DelegateCommand(ParameterAdd);
ParameterEditCommand = new DelegateCommand(ParameterEdit); ParameterEditCommand = new DelegateCommand(ParameterEdit);
ParameterDeleteCommand = new DelegateCommand(ParameterDelete); ParameterDeleteCommand = new DelegateCommand(ParameterDelete);
@ -64,16 +78,27 @@ namespace BOB.ViewModels
private void DeviceDelete() private void DeviceDelete()
{ {
Program.Devices.Remove(SelectedDevice);
} }
private void DeviceEdit() private void DeviceEdit()
{ {
var param = new DialogParameters var param = new DialogParameters
{ {
{ "Mode", _GlobalVariables.SelectedDevice==null?"ADD":"Edit" } { "Mode", SelectedDevice==null?"ADD":"Edit" }
}; };
_dialogService.ShowDialog("DeviceSetting", param); _dialogService.ShowDialog("DeviceSetting", param, (r) =>
{
if (r.Result == ButtonResult.OK)
{
}
else
{
}
_eventAggregator.GetEvent<OverlayEvent>().Publish(false);
});
} }
@ -83,21 +108,44 @@ namespace BOB.ViewModels
{ {
{ "Mode", "ADD" } { "Mode", "ADD" }
}; };
_dialogService.ShowDialog("DeviceSetting", param); _eventAggregator.GetEvent<OverlayEvent>().Publish(true);
_dialogService.ShowDialog("DeviceSetting", param, (r) =>
{
if (r.Result == ButtonResult.OK)
{
}
else
{
}
_eventAggregator.GetEvent<OverlayEvent>().Publish(false);
});
} }
private void ParameterDelete() private void ParameterDelete()
{ {
Program.Parameters.Remove(SelectedParameter);
} }
private void ParameterEdit() private void ParameterEdit()
{ {
var param = new DialogParameters var param = new DialogParameters
{ {
{ "Mode", _GlobalVariables.SelectedParameter==null?"ADD":"Edit" } { "Mode",SelectedParameter==null?"ADD":"Edit" }
}; };
_dialogService.ShowDialog("ParameterSetting", param); _dialogService.ShowDialog("ParameterSetting", param, (r) =>
{
if (r.Result == ButtonResult.OK)
{
}
else
{
}
_eventAggregator.GetEvent<OverlayEvent>().Publish(false);
});
} }
private void ParameterAdd() private void ParameterAdd()
@ -106,24 +154,20 @@ namespace BOB.ViewModels
{ {
{ "Mode", "ADD" } { "Mode", "ADD" }
}; };
_dialogService.ShowDialog("ParameterSetting", param); _dialogService.ShowDialog("ParameterSetting", param, (r) =>
{
if (r.Result == ButtonResult.OK)
{
}
else
{
}
_eventAggregator.GetEvent<OverlayEvent>().Publish(false);
});
} }
#endregion #endregion
#region Prism Dialog
public bool CanCloseDialog()
{
return true;
}
public void OnDialogClosed()
{
} }
public void OnDialogOpened(IDialogParameters parameters)
{
}
#endregion
}
} }

View File

@ -1,15 +1,20 @@
using BOB.Views; using BOB.Models;
using BOB.Views;
using Common.PubEvents; using Common.PubEvents;
using Logger; using Logger;
using MaterialDesignThemes.Wpf; using MaterialDesignThemes.Wpf;
using Microsoft.Win32;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media;
namespace BOB.ViewModels namespace BOB.ViewModels
{ {
@ -34,6 +39,11 @@ namespace BOB.ViewModels
public ICommand RunSingleCommand { get; set; } public ICommand RunSingleCommand { get; set; }
public ICommand ResotrationCommand { get; set; } public ICommand ResotrationCommand { get; set; }
public ICommand StopCommand { get; set; } public ICommand StopCommand { get; set; }
public ICommand SaveAsCommand { get; set; }
public ICommand SaveCommand { get; set; }
public ICommand OpenCommand { get; set; }
public ICommand NewCommand { get; set; }
public ICommand SetDefaultCommand { get; set; }
#endregion #endregion
private IEventAggregator _eventAggregator; private IEventAggregator _eventAggregator;
@ -46,28 +56,153 @@ namespace BOB.ViewModels
MinimizeCommand = new DelegateCommand<Window>(MinimizeWindow); MinimizeCommand = new DelegateCommand<Window>(MinimizeWindow);
MaximizeCommand = new DelegateCommand<Window>(MaximizeWindow); MaximizeCommand = new DelegateCommand<Window>(MaximizeWindow);
CloseCommand = new DelegateCommand<Window>(CloseWindow); CloseCommand = new DelegateCommand<Window>(CloseWindow);
RunningCommand = new DelegateCommand<Window>(Running); RunningCommand = new DelegateCommand(Running);
RunSingleCommand = new DelegateCommand<Window>(RunSingle); RunSingleCommand = new DelegateCommand(RunSingle);
ResotrationCommand = new DelegateCommand<Window>(Resotration); ResotrationCommand = new DelegateCommand(Resotration);
StopCommand = new DelegateCommand<Window>(Stop); StopCommand = new DelegateCommand(Stop);
NewCommand = new DelegateCommand(New);
OpenCommand = new DelegateCommand(Open);
SaveAsCommand = new DelegateCommand(SaveAs);
SaveCommand = new DelegateCommand(Save);
SetDefaultCommand = new DelegateCommand(SetDefault);
}
#region ToolBar命令
private void SetDefault()
{
if(_globalVariables.CurrentFilePath!=null)
{
SystemConfig.Instance.DefaultSubProgramFilePath = _globalVariables.CurrentFilePath;
}
}
private void New()
{
_globalVariables.CurrentFilePath = null;
_globalVariables.Program = new();
} }
private void Stop(Window window) private void Open()
{
try
{
var openFileDialog = new OpenFileDialog
{
Filter = "BOB程序文件|*.bob|所有文件|*.*",
Title = "打开程序",
InitialDirectory = @"D:\BOB\子程序"
};
if (openFileDialog.ShowDialog() == true)
{
string filePath = openFileDialog.FileName;
// 读取 JSON 文件内容
string json = File.ReadAllText(filePath);
// 反序列化为 ProgramModel
var program = JsonConvert.DeserializeObject<ProgramModel>(json);
if (program != null)
{
_globalVariables.Program = program;
_globalVariables.CurrentFilePath = filePath;
LoggerHelper.SuccessWithNotify($"成功打开文件: {filePath}");
}
else
{
LoggerHelper.WarnWithNotify($"文件内容格式不正确: {filePath}");
}
}
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"打开文件失败: {ex.Message}");
}
}
private void SaveAs()
{
string defaultPath = @"D:\BOB\子程序";
// 如果目录不存在,可以自动创建
if (!Directory.Exists(defaultPath))
Directory.CreateDirectory(defaultPath);
var saveFileDialog = new SaveFileDialog
{
Filter = "BOB程序文件|*.bob|所有文件|*.*",
Title = "另存为",
FileName = "NewProgram.bob",
InitialDirectory = defaultPath // ✅ 默认打开这个文件夹
};
if (saveFileDialog.ShowDialog() == true)
{
_globalVariables.CurrentFilePath = saveFileDialog.FileName;
SaveProgramToFile(_globalVariables.CurrentFilePath);
}
// 记录日志
LoggerHelper.InfoWithNotify($"{_globalVariables.UserName} 另存为文件成功: {saveFileDialog.FileName}");
}
private void SaveProgramToFile(string filePath)
{
try
{
var tmp = ClearDeviceParameterValue(_globalVariables.Program);
string json = JsonConvert.SerializeObject(tmp, Formatting.Indented);
File.WriteAllText(filePath, json);
LoggerHelper.SuccessWithNotify($"程序已保存: {filePath}");
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify($"保存文件失败: {ex.Message}");
}
}
private ProgramModel ClearDeviceParameterValue(ProgramModel program)
{
var tmp = new ProgramModel(program);
foreach (var device in tmp.Devices)
{
tmp.Parameters.Remove(tmp.Parameters.First(x => x.ID == device.ParameterID));
}
foreach (var step in tmp.StepCollection)
{
if (step.SubProgram != null)
{
step.SubProgram = ClearDeviceParameterValue(step.SubProgram);
}
}
return tmp;
}
private void Save()
{
if(_globalVariables.CurrentFilePath == null)
{
SaveAs();
return;
}
SaveProgramToFile(_globalVariables.CurrentFilePath);
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "保存文件成功");
}
private void Stop()
{ {
LoggerHelper.InfoWithNotify(_globalVariables.UserName+"执行停止命令"); LoggerHelper.InfoWithNotify(_globalVariables.UserName+"执行停止命令");
} }
private void Resotration(Window window) private void Resotration()
{ {
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行复位命令"); LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行复位命令");
} }
private void RunSingle(Window window) private void RunSingle()
{ {
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行单步执行命令"); LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行单步执行命令");
} }
private void Running(Window window) private void Running()
{ {
LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行运行命令"); LoggerHelper.InfoWithNotify(_globalVariables.UserName + "执行运行命令");
} }
@ -94,5 +229,6 @@ namespace BOB.ViewModels
{ {
window?.Close(); window?.Close();
} }
#endregion
} }
} }

View File

@ -42,17 +42,22 @@ namespace BOB.ViewModels
private void DisposeSelectedStep(Guid id) private void DisposeSelectedStep(Guid id)
{ {
if (SelectedStep == null) return;
if(id== SelectedStep.ID) if(id== SelectedStep.ID)
SelectedStep = null; SelectedStep = null;
} }
private void CancelEdit() private void CancelEdit()
{ {
SelectedStep = null;
} }
private void SaveStep() private void SaveStep()
{ {
if (SelectedStep == null || (SelectedStep.Method == null && SelectedStep.SubProgram == null))
{
return;
}
} }

View File

@ -16,25 +16,35 @@ namespace BOB.ViewModels
public class StepsManagerViewModel:BindableBase public class StepsManagerViewModel:BindableBase
{ {
#region #region
private string _Title;
public string Title public string Title
{ {
get => _globalVariables.Title; get => _Title;
set => SetProperty(ref _globalVariables.Title, value); set => SetProperty(ref _Title, value);
} }
private StepModel _SelectedStep;
public StepModel SelectedStep public StepModel SelectedStep
{ {
get => _globalVariables.SelectedStep; get => _SelectedStep;
set => SetProperty(ref _globalVariables.SelectedStep, value); set
{
if (SetProperty(ref _SelectedStep, value))
{
_globalVariables.SelectedStep = value;
} }
}
}
private ProgramModel _Program;
public ProgramModel Program public ProgramModel Program
{ {
get => _globalVariables.Program; get => _Program;
set => SetProperty(ref _globalVariables.Program, value); set => SetProperty(ref _Program, value);
} }
private bool _IsAdmin;
public bool IsAdmin public bool IsAdmin
{ {
get => _globalVariables.IsAdmin; get => _IsAdmin;
set => SetProperty(ref _globalVariables.IsAdmin, value); set => SetProperty(ref _IsAdmin, value);
} }
GlobalVariables _globalVariables { get; set; } GlobalVariables _globalVariables { get; set; }
private List<StepModel> tmpCopyList = new List<StepModel>(); private List<StepModel> tmpCopyList = new List<StepModel>();
@ -49,6 +59,8 @@ namespace BOB.ViewModels
{ {
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_globalVariables = _GlobalVariables; _globalVariables = _GlobalVariables;
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,7 +18,12 @@
</i:EventTrigger> </i:EventTrigger>
</i:Interaction.Triggers> </i:Interaction.Triggers>
<Grid> <Grid>
<GroupBox Header="指令"> <Grid.ColumnDefinitions>
<ColumnDefinition Width="439*" />
<ColumnDefinition Width="361*" />
</Grid.ColumnDefinitions>
<GroupBox Header="指令"
Grid.ColumnSpan="2">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@ -34,7 +39,8 @@
Height="25" Height="25"
Margin="0,0,5,0" Margin="0,0,5,0"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"> Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
materialDesign:HintAssist.Hint="搜索内容">
<i:Interaction.Triggers> <i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown"> <i:EventTrigger EventName="KeyDown">
<prism:InvokeCommandAction Command="{Binding SearchEnterCommand}" /> <prism:InvokeCommandAction Command="{Binding SearchEnterCommand}" />

View File

@ -5,9 +5,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:converters="clr-namespace:BOB.Converters" xmlns:converters="clr-namespace:BOB.Converters"
xmlns:prism="http://prismlibrary.com/" xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:behavior="clr-namespace:Common.Behaviors;assembly=Common" xmlns:behavior="clr-namespace:Common.Behaviors;assembly=Common"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
prism:ViewModelLocator.AutoWireViewModel="True" prism:ViewModelLocator.AutoWireViewModel="True"
Background="White"
Panel.ZIndex="1000"
mc:Ignorable="d" mc:Ignorable="d"
Height="290" Height="290"
Width="450"> Width="450">
@ -47,6 +50,7 @@
Width="85" /> Width="85" />
<TextBox Text="{Binding Device.Name}" <TextBox Text="{Binding Device.Name}"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
materialDesign:HintAssist.Hint="设备名称"
Width="120" /> Width="120" />
</StackPanel> </StackPanel>
<StackPanel Height="30" <StackPanel Height="30"
@ -59,6 +63,7 @@
SelectedItem="{Binding Device.Type,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Device.Type,UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Width="120" Width="120"
materialDesign:HintAssist.Hint="通讯协议类型"
IsEnabled="{Binding IsAdd}"> IsEnabled="{Binding IsAdd}">
<i:Interaction.Triggers> <i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged"> <i:EventTrigger EventName="SelectionChanged">
@ -75,6 +80,7 @@
VerticalAlignment="Bottom" /> VerticalAlignment="Bottom" />
<TextBox Text="{Binding Device.Description}" <TextBox Text="{Binding Device.Description}"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
materialDesign:HintAssist.Hint="设备描述"
Width="120" /> Width="120" />
</StackPanel> </StackPanel>
<Label Content="连接参数" <Label Content="连接参数"
@ -93,9 +99,11 @@
VerticalContentAlignment="Bottom" /> VerticalContentAlignment="Bottom" />
<TextBox VerticalAlignment="Bottom" <TextBox VerticalAlignment="Bottom"
Visibility="{Binding Name, Converter={StaticResource DeviceNameConverter}}" Visibility="{Binding Name, Converter={StaticResource DeviceNameConverter}}"
materialDesign:HintAssist.Hint=""
Text="{Binding Value}" Text="{Binding Value}"
Width="120" /> Width="120" />
<ComboBox VerticalAlignment="Bottom" <ComboBox VerticalAlignment="Bottom"
materialDesign:HintAssist.Hint=""
Visibility="{Binding Name, Converter={StaticResource DeviceNameConverter}, ConverterParameter=Inverse}" Visibility="{Binding Name, Converter={StaticResource DeviceNameConverter}, ConverterParameter=Inverse}"
ItemsSource="{Binding Name, Converter={StaticResource DeviceNameConverter}, ConverterParameter=Items}" ItemsSource="{Binding Name, Converter={StaticResource DeviceNameConverter}, ConverterParameter=Items}"
SelectedItem="{Binding Value}" SelectedItem="{Binding Value}"

View File

@ -7,7 +7,9 @@
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls" xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/" xmlns:prism="http://prismlibrary.com/"
Background="White"
xmlns:behavior="clr-namespace:Common.Behaviors;assembly=Common" xmlns:behavior="clr-namespace:Common.Behaviors;assembly=Common"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
prism:ViewModelLocator.AutoWireViewModel="True" prism:ViewModelLocator.AutoWireViewModel="True"
Width="420" Width="420"
Height="284" Height="284"
@ -38,7 +40,8 @@
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Content="参数名称*" /> Content="参数名称*" />
<TextBox Width="120" <TextBox Width="120"
Text="{Binding Parameter.Name}" /> Text="{Binding Parameter.Name}"
materialDesign:HintAssist.Hint="参数名称" />
</StackPanel> </StackPanel>
<StackPanel Height="30" <StackPanel Height="30"
Margin="7" Margin="7"
@ -49,6 +52,7 @@
<ComboBox Width="120" <ComboBox Width="120"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
ItemsSource="{Binding Types}" ItemsSource="{Binding Types}"
materialDesign:HintAssist.Hint="参数类型"
SelectedItem="{Binding Parameter.Type}" SelectedItem="{Binding Parameter.Type}"
/> />
</StackPanel> </StackPanel>
@ -61,6 +65,7 @@
<ComboBox Width="120" <ComboBox Width="120"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
ItemsSource="{Binding Categories}" ItemsSource="{Binding Categories}"
materialDesign:HintAssist.Hint="参数类别"
Text="{Binding Parameter.Category}" /> Text="{Binding Parameter.Category}" />
</StackPanel> </StackPanel>
<StackPanel Height="30" <StackPanel Height="30"
@ -71,7 +76,8 @@
Content="参数下限" /> Content="参数下限" />
<TextBox Width="120" <TextBox Width="120"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Text="{Binding Parameter.LowerLimit}" /> Text="{Binding Parameter.LowerLimit}"
materialDesign:HintAssist.Hint="参数下限" />
</StackPanel> </StackPanel>
<StackPanel Height="30" <StackPanel Height="30"
Margin="7" Margin="7"
@ -81,7 +87,8 @@
Content="参数上限" /> Content="参数上限" />
<TextBox Width="120" <TextBox Width="120"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Text="{Binding Parameter.UpperLimit}" /> Text="{Binding Parameter.UpperLimit}"
materialDesign:HintAssist.Hint="参数名上限" />
</StackPanel> </StackPanel>
<StackPanel Height="30" <StackPanel Height="30"
Margin="7" Margin="7"
@ -94,6 +101,7 @@
<TextBox MinWidth="120" <TextBox MinWidth="120"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Text="{Binding Parameter.Value, Converter={StaticResource ParameterValueToStringConverter}}" Text="{Binding Parameter.Value, Converter={StaticResource ParameterValueToStringConverter}}"
materialDesign:HintAssist.Hint="参数值"
Visibility="{Binding Parameter.Type, Converter={StaticResource IsEnumTypeConverter}, ConverterParameter=Collapse}" /> Visibility="{Binding Parameter.Type, Converter={StaticResource IsEnumTypeConverter}, ConverterParameter=Collapse}" />
<!-- 枚举类型时显示下拉框 --> <!-- 枚举类型时显示下拉框 -->

View File

@ -22,6 +22,7 @@
<RowDefinition /> <RowDefinition />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<vs:CommandTree Grid.Row="0" Margin="5" <vs:CommandTree Grid.Row="0" Margin="5"
Grid.Column="0" Grid.Column="0"
Grid.RowSpan="2" /> Grid.RowSpan="2" />

View File

@ -1,4 +1,6 @@
using System; using Common.PubEvent;
using Prism.Events;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -20,7 +22,7 @@ namespace BOB.Views
/// </summary> /// </summary>
public partial class MainView : UserControl public partial class MainView : UserControl
{ {
public MainView() public MainView(IEventAggregator eventAggregator)
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@ -4,10 +4,10 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/" xmlns:prism="http://prismlibrary.com/"
xmlns:converters="clr-namespace:BOB.Converters"
xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib"
prism:ViewModelLocator.AutoWireViewModel="True" prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"> mc:Ignorable="d">
<UserControl.Resources> <UserControl.Resources>
<CollectionViewSource x:Key="VisibleParameters" <CollectionViewSource x:Key="VisibleParameters"
Source="{Binding Program.Parameters}" Source="{Binding Program.Parameters}"
@ -16,6 +16,8 @@
<sys:String>IsVisible</sys:String> <sys:String>IsVisible</sys:String>
</CollectionViewSource.LiveFilteringProperties> </CollectionViewSource.LiveFilteringProperties>
</CollectionViewSource> </CollectionViewSource>
<converters:ParameterCategoryToStringConverter x:Key="ParameterCategoryToStringConverter" />
<converters:ParameterValueToStringConverter x:Key="ParameterValueToStringConverter" />
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
@ -34,7 +36,7 @@
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="类别" <DataGridTextColumn Header="类别"
Binding="{Binding Category, Converter={StaticResource ParameterCategoryToStringConverter}}"
IsReadOnly="True" /> IsReadOnly="True" />
<DataGridTextColumn Header="参数名" <DataGridTextColumn Header="参数名"
Binding="{Binding Name}" Binding="{Binding Name}"
@ -44,7 +46,7 @@
IsReadOnly="True" /> IsReadOnly="True" />
<DataGridTextColumn Header="值" <DataGridTextColumn Header="值"
MinWidth="20" MinWidth="20"
Binding="{Binding Value, Converter={StaticResource ParameterValueToStringConverter}}"
IsReadOnly="True" /> IsReadOnly="True" />
</DataGrid.Columns> </DataGrid.Columns>

View File

@ -86,15 +86,20 @@
Foreground="White" /> Foreground="White" />
</MenuItem.Icon> </MenuItem.Icon>
<MenuItem Header="新建" <MenuItem Header="新建"
Foreground="Black" /> Foreground="Black"
Command="{Binding NewCommand}" />
<MenuItem Header="打开" <MenuItem Header="打开"
Foreground="Black" /> Foreground="Black"
Command="{Binding OpenCommand}" />
<MenuItem Header="保存" <MenuItem Header="保存"
Command="{Binding SaveCommand}"
Foreground="Black" /> Foreground="Black" />
<MenuItem Header="另存为" <MenuItem Header="另存为"
Foreground="Black" /> Foreground="Black"
Command="{Binding SaveAsCommand}" />
<MenuItem Header="设置默认程序" <MenuItem Header="设置默认程序"
Foreground="Black" /> Foreground="Black"
Command="{Binding SetDefaultCommand}" />
</MenuItem> </MenuItem>
<!-- 工具菜单 --> <!-- 工具菜单 -->
@ -234,6 +239,12 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<ContentControl prism:RegionManager.RegionName="ShellViewManager" <ContentControl prism:RegionManager.RegionName="ShellViewManager"
Grid.ColumnSpan="3" /> Grid.ColumnSpan="3" />
<Border x:Name="Overlay"
Background="#40000000"
Visibility="Collapsed"
Panel.ZIndex="10"
Grid.RowSpan="2"
Grid.ColumnSpan="4" />
</Grid> </Grid>
</materialDesign:DialogHost> </materialDesign:DialogHost>
</Grid> </Grid>

View File

@ -34,9 +34,8 @@ namespace BOB.Views
private void ShowOverlay(bool arg) private void ShowOverlay(bool arg)
{ {
DialogHost.IsOpen = arg; Overlay.Visibility = arg ? Visibility.Visible : Visibility.Collapsed;
} }
private void ShowWaiting(bool arg) private void ShowWaiting(bool arg)
{ {
DialogHost.IsOpen = arg; DialogHost.IsOpen = arg;

View File

@ -43,7 +43,7 @@
<Label Width="60" <Label Width="60"
Content="名称" /> Content="名称" />
<TextBox MinWidth="120" <TextBox MinWidth="120"
materialDesign:HintAssist.Hint=""
Text="{Binding SelectedStep.Name}" /> Text="{Binding SelectedStep.Name}" />
</StackPanel> </StackPanel>
@ -54,6 +54,7 @@
<Label Width="60" <Label Width="60"
Content="合格条件" /> Content="合格条件" />
<TextBox MinWidth="120" <TextBox MinWidth="120"
materialDesign:HintAssist.Hint=""
Text="{Binding SelectedStep.OKExpression}" /> Text="{Binding SelectedStep.OKExpression}" />
</StackPanel> </StackPanel>
@ -64,7 +65,8 @@
<Label Width="60" <Label Width="60"
Content="跳转" Content="跳转"
ToolTip="格式OK跳转序号/NG跳转序号默认为0/0)" /> ToolTip="格式OK跳转序号/NG跳转序号默认为0/0)" />
<TextBox MinWidth="120" /> <TextBox MinWidth="120"
materialDesign:HintAssist.Hint="" />
</StackPanel> </StackPanel>
<!-- 备注 --> <!-- 备注 -->
@ -74,6 +76,7 @@
<Label Width="60" <Label Width="60"
Content="备注" /> Content="备注" />
<TextBox MinWidth="120" <TextBox MinWidth="120"
materialDesign:HintAssist.Hint=""
Text="{Binding SelectedStep.Description}" /> Text="{Binding SelectedStep.Description}" />
</StackPanel> </StackPanel>
@ -141,12 +144,14 @@
MinWidth="120" MinWidth="120"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
materialDesign:HintAssist.Hint=""
Visibility="{Binding Type, Converter={StaticResource IsEnumTypeConverter}, ConverterParameter=Collapse}" /> Visibility="{Binding Type, Converter={StaticResource IsEnumTypeConverter}, ConverterParameter=Collapse}" />
<!-- 枚举类型 --> <!-- 枚举类型 -->
<ComboBox Height="22" <ComboBox Height="22"
MinWidth="120" MinWidth="120"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
materialDesign:HintAssist.Hint=""
ItemsSource="{Binding Type, Converter={StaticResource EnumValuesConverter}}" ItemsSource="{Binding Type, Converter={StaticResource EnumValuesConverter}}"
Visibility="{Binding Type, Converter={StaticResource IsEnumTypeConverter}}"> Visibility="{Binding Type, Converter={StaticResource IsEnumTypeConverter}}">
<ComboBox.SelectedItem> <ComboBox.SelectedItem>
@ -161,6 +166,7 @@
<!-- 变量选择框 --> <!-- 变量选择框 -->
<ComboBox Height="22" <ComboBox Height="22"
MinWidth="120" MinWidth="120"
materialDesign:HintAssist.Hint=""
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
SelectedValue="{Binding VariableName}" SelectedValue="{Binding VariableName}"
SelectedValuePath="Name" SelectedValuePath="Name"
@ -197,6 +203,7 @@
/> />
<ComboBox Width="120" <ComboBox Width="120"
Margin="0,0,0,0" Margin="0,0,0,0"
materialDesign:HintAssist.Hint=""
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
SelectedValue="{Binding VariableName}" SelectedValue="{Binding VariableName}"
SelectedValuePath="Name" SelectedValuePath="Name"
@ -270,6 +277,7 @@
<!-- 非枚举类型 --> <!-- 非枚举类型 -->
<TextBox Height="22" <TextBox Height="22"
MinWidth="120" MinWidth="120"
materialDesign:HintAssist.Hint=""
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding Type, Converter={StaticResource IsEnumTypeConverter}, ConverterParameter=Collapse}" /> Visibility="{Binding Type, Converter={StaticResource IsEnumTypeConverter}, ConverterParameter=Collapse}" />
@ -277,6 +285,7 @@
<!-- 枚举类型 --> <!-- 枚举类型 -->
<ComboBox Height="22" <ComboBox Height="22"
MinWidth="120" MinWidth="120"
materialDesign:HintAssist.Hint=""
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
ItemsSource="{Binding Type, Converter={StaticResource EnumValuesConverter}}" ItemsSource="{Binding Type, Converter={StaticResource EnumValuesConverter}}"
SelectedItem="{Binding Value}" SelectedItem="{Binding Value}"
@ -286,6 +295,7 @@
<!-- 变量选择框 --> <!-- 变量选择框 -->
<ComboBox Height="22" <ComboBox Height="22"
MinWidth="120" MinWidth="120"
materialDesign:HintAssist.Hint=""
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext.Program.Parameters}" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext.Program.Parameters}"
SelectedValue="{Binding VariableName}" SelectedValue="{Binding VariableName}"
@ -311,6 +321,7 @@
Visibility="{Binding Category, Converter={StaticResource ParameterCategoryToVisibilityConverter}, ConverterParameter=Inverse}" /> Visibility="{Binding Category, Converter={StaticResource ParameterCategoryToVisibilityConverter}, ConverterParameter=Inverse}" />
<ComboBox Width="120" <ComboBox Width="120"
Margin="0,0,0,0" Margin="0,0,0,0"
materialDesign:HintAssist.Hint=""
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.Program.Parameters}" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.Program.Parameters}"
SelectedValue="{Binding VariableName}" SelectedValue="{Binding VariableName}"

View File

@ -27,7 +27,7 @@ namespace Logger
Logger.Info(message); Logger.Info(message);
NotifyUI(message, "lightgreen"); NotifyUI(message, "lightgreen");
} }
public static void warnWithNotify(string message, string stackTrace = null) public static void WarnWithNotify(string message, string stackTrace = null)
{ {
if (!string.IsNullOrEmpty(stackTrace)) if (!string.IsNullOrEmpty(stackTrace))
{ {