772 lines
32 KiB
C#
772 lines
32 KiB
C#
|
||
using Logger;
|
||
using MaterialDesignThemes.Wpf;
|
||
using Notifications.Wpf.Core;
|
||
using System.Collections.Concurrent;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Windows;
|
||
using System.Windows.Input;
|
||
using System.Windows.Media;
|
||
using System.Windows.Threading; // 💡 新增:引入定时器命名空间
|
||
using UIShare;
|
||
using UIShare.GlobalVariable;
|
||
using UIShare.PubEvent;
|
||
using UIShare.UIViewModel;
|
||
|
||
namespace ADP.ViewModels
|
||
{
|
||
public class ShellViewModel : BindableBase
|
||
{
|
||
#region 私有字段
|
||
private string _Title = "";
|
||
private bool _IsLeftDrawerOpen;
|
||
|
||
private readonly ConcurrentDictionary<string, Task> _executionTasks = new();
|
||
private readonly ConcurrentDictionary<string, Task> _errorExecutionTasks = new();
|
||
|
||
private readonly IEventAggregator _eventAggregator;
|
||
private readonly IRegionManager _regionManager;
|
||
private readonly IContainerProvider _containerProvider;
|
||
private readonly INotificationManager _notificationManager;
|
||
private readonly IModuleManager _moduleManager;
|
||
private readonly GlobalInfo _globalInfo;
|
||
|
||
// 💡 新增:UI 刷新定时器,用于高频让前端重新拉取当前工位的 SW 累计时间
|
||
private readonly DispatcherTimer _uiRefreshTimer;
|
||
#endregion
|
||
|
||
#region 属性
|
||
public string Title
|
||
{
|
||
get => _Title;
|
||
set => SetProperty(ref _Title, value);
|
||
}
|
||
public bool IsLeftDrawerOpen
|
||
{
|
||
get => _IsLeftDrawerOpen;
|
||
set => SetProperty(ref _IsLeftDrawerOpen, value);
|
||
}
|
||
|
||
// 代理属性:动态反映当前被激活的 Scope 状态
|
||
public TimeSpan RunningTime
|
||
{
|
||
get
|
||
{
|
||
if (CurrentContext == null) return TimeSpan.Zero;
|
||
// 💡 核心:如果当前工位的计时器正在跑,动态累加 SW 当前的时间,否则返回其保存的固定时间值
|
||
return CurrentContext.SW.IsRunning ? CurrentContext.SW.Elapsed : CurrentContext.RunningTime;
|
||
}
|
||
set
|
||
{
|
||
if (CurrentContext != null && CurrentContext.RunningTime != value)
|
||
{
|
||
CurrentContext.RunningTime = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool IsTerminate
|
||
{
|
||
get => CurrentContext?.IsTerminate ?? false;
|
||
set
|
||
{
|
||
if (CurrentContext != null && CurrentContext.IsTerminate != value)
|
||
{
|
||
CurrentContext.IsTerminate = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
}
|
||
|
||
private ScopedContext? CurrentContext =>
|
||
_globalInfo.ContextDic.TryGetValue(_globalInfo.CurrentScope, out var ctx) ? ctx : null;
|
||
private StepRunning? CurrentRunner =>
|
||
_globalInfo.StepRunningDic.TryGetValue(_globalInfo.CurrentScope, out var runner) ? runner : null;
|
||
|
||
public string RunState
|
||
{
|
||
get => CurrentContext?.RunState ?? "运行";
|
||
set
|
||
{
|
||
if (CurrentContext != null && CurrentContext.RunState != value)
|
||
{
|
||
CurrentContext.RunState = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
}
|
||
|
||
public bool SingleStep
|
||
{
|
||
get => CurrentContext?.SingleStep ?? false;
|
||
set
|
||
{
|
||
if (CurrentContext != null && CurrentContext.SingleStep != value)
|
||
{
|
||
CurrentContext.SingleStep = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
}
|
||
|
||
public PackIconKind RunIcon
|
||
{
|
||
get => CurrentContext?.RunIcon ?? PackIconKind.Play;
|
||
set
|
||
{
|
||
if (CurrentContext != null && CurrentContext.RunIcon != value)
|
||
{
|
||
CurrentContext.RunIcon = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 命令
|
||
public ICommand LeftDrawerOpenCommand { get; set; }
|
||
public ICommand MinimizeCommand { get; set; }
|
||
public ICommand MaximizeCommand { get; set; }
|
||
public ICommand CloseCommand { get; set; }
|
||
public ICommand NavigateCommand { get; set; }
|
||
public ICommand LoadCommand { get; set; }
|
||
public ICommand RefreshCommand { get; set; }
|
||
public ICommand DestroyCommand { get; set; }
|
||
public ICommand RunningCommand { get; set; }
|
||
public ICommand RunSingleCommand { get; set; }
|
||
public ICommand RestorationCommand { get; set; }
|
||
public ICommand RunAbnormalStepsCommand { 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
|
||
|
||
public ShellViewModel(IContainerProvider containerProvider)
|
||
{
|
||
_containerProvider = containerProvider;
|
||
_globalInfo = containerProvider.Resolve<GlobalInfo>();
|
||
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
|
||
_regionManager = containerProvider.Resolve<IRegionManager>();
|
||
_notificationManager = containerProvider.Resolve<INotificationManager>();
|
||
_moduleManager = containerProvider.Resolve<IModuleManager>();
|
||
|
||
LeftDrawerOpenCommand = new DelegateCommand(LeftDrawerOpen);
|
||
MinimizeCommand = new DelegateCommand<Window>(MinimizeWindow);
|
||
MaximizeCommand = new DelegateCommand<Window>(MaximizeWindow);
|
||
CloseCommand = new DelegateCommand<Window>(CloseWindow);
|
||
NavigateCommand = new DelegateCommand<string>(Navigate);
|
||
LoadCommand = new DelegateCommand(Load);
|
||
RefreshCommand = new DelegateCommand(OnRefresh);
|
||
DestroyCommand = new DelegateCommand(OnDestroy);
|
||
RunningCommand = new DelegateCommand(OnRunning);
|
||
RunSingleCommand = new DelegateCommand(RunSingle);
|
||
RestorationCommand = new AsyncDelegateCommand(OnRestoration);
|
||
RunAbnormalStepsCommand = new AsyncDelegateCommand(OnRunAbnormalSteps);
|
||
NewCommand = new DelegateCommand(New);
|
||
OpenCommand = new AsyncDelegateCommand<string>(Open);
|
||
SaveAsCommand = new DelegateCommand(SaveAs);
|
||
SaveCommand = new DelegateCommand(Save);
|
||
SetDefaultCommand = new DelegateCommand(SetDefault);
|
||
|
||
_globalInfo.ContextDic.Add("default", new ScopedContext());
|
||
|
||
_eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(() =>
|
||
{
|
||
Application.Current.MainWindow.Show();
|
||
_regionManager.RequestNavigate("ShellViewManager", "MainView");
|
||
});
|
||
|
||
_eventAggregator.GetEvent<RunSingalCompletedEvent>().Subscribe(UpdateRunIcon);
|
||
|
||
_globalInfo.ScopeChanged += (s, e) =>
|
||
{
|
||
RefreshAllContextProperties();
|
||
};
|
||
|
||
// 💡 初始化轻量级 UI 定时器:每 100 毫秒刷新一次当前可见工位的界面时间
|
||
_uiRefreshTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(100) };
|
||
_uiRefreshTimer.Tick += (s, e) => RaisePropertyChanged(nameof(RunningTime));
|
||
_uiRefreshTimer.Start();
|
||
}
|
||
|
||
private void RefreshAllContextProperties()
|
||
{
|
||
RaisePropertyChanged(nameof(RunState));
|
||
RaisePropertyChanged(nameof(SingleStep));
|
||
RaisePropertyChanged(nameof(RunIcon));
|
||
RaisePropertyChanged(nameof(RunningTime));
|
||
RaisePropertyChanged(nameof(IsTerminate));
|
||
}
|
||
|
||
#region 命令处理与事件
|
||
private async void OnRunning()
|
||
{
|
||
string runningScope = _globalInfo.CurrentScope;
|
||
ScopedContext? targetContext = CurrentContext;
|
||
StepRunning? targetRunner = CurrentRunner;
|
||
|
||
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null || targetRunner == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (targetContext.RunState == "运行")
|
||
{
|
||
targetContext.SingleStep = false;
|
||
LoggerHelper.InfoWithNotify($"{_globalInfo.UserName} 执行工位 [{runningScope}] 运行命令");
|
||
|
||
targetContext.RunState = "暂停";
|
||
targetContext.RunIcon = PackIconKind.Pause;
|
||
|
||
if (_globalInfo.CurrentScope == runningScope) RefreshAllContextProperties();
|
||
|
||
if (targetContext.IsStop == null)
|
||
{
|
||
targetContext.IsStop = false;
|
||
|
||
// 💡 启动或恢复:启动属于该快照工位独立的 SW 计时器
|
||
targetContext.SW.Start();
|
||
|
||
if (!_executionTasks.TryGetValue(runningScope, out var existingTask) || existingTask == null)
|
||
{
|
||
existingTask = targetRunner.ExecuteSteps(targetContext.Program, cancellationToken: targetRunner.stepCTS.Token);
|
||
_executionTasks[runningScope] = existingTask;
|
||
}
|
||
|
||
try
|
||
{
|
||
await existingTask;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LoggerHelper.ErrorWithNotify($"工位 [{runningScope}] 异常中止: {ex.Message}");
|
||
}
|
||
|
||
// 💡 测试正常完毕或取消:停止当前工位的 SW,并将最终时间固化到 RunningTime 字段中
|
||
targetContext.SW.Stop();
|
||
targetContext.RunningTime = targetContext.SW.Elapsed;
|
||
|
||
targetContext.RunState = "运行";
|
||
targetContext.RunIcon = PackIconKind.Play;
|
||
targetContext.IsStop = null;
|
||
|
||
if (targetRunner.stepCTS.IsCancellationRequested)
|
||
{
|
||
targetRunner.stepCTS = new CancellationTokenSource();
|
||
_executionTasks.TryRemove(runningScope, out _);
|
||
}
|
||
else if (_executionTasks.TryGetValue(runningScope, out var currentTask) && currentTask != null && currentTask.IsCompleted)
|
||
{
|
||
targetContext.IsTerminate = true;
|
||
_executionTasks.TryRemove(runningScope, out _);
|
||
}
|
||
|
||
if (_globalInfo.CurrentScope == runningScope) RefreshAllContextProperties();
|
||
}
|
||
else if (targetContext.IsStop == true)
|
||
{
|
||
targetContext.IsStop = false;
|
||
// 💡 从暂停中恢复,继续累加当前工位计时
|
||
targetContext.SW.Start();
|
||
}
|
||
}
|
||
else // 用户点击了暂停
|
||
{
|
||
LoggerHelper.InfoWithNotify($"{_globalInfo.UserName} 点击工位 [{runningScope}] 暂停命令");
|
||
targetContext.SingleStep = true;
|
||
targetContext.IsStop = true;
|
||
targetContext.RunState = "运行";
|
||
targetContext.RunIcon = PackIconKind.Play;
|
||
if (_globalInfo.CurrentScope == runningScope) RefreshAllContextProperties();
|
||
}
|
||
}
|
||
|
||
private async void RunSingle()
|
||
{
|
||
string runningScope = _globalInfo.CurrentScope;
|
||
ScopedContext? targetContext = CurrentContext;
|
||
StepRunning? targetRunner = CurrentRunner;
|
||
|
||
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null || targetRunner == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (targetContext.RunState == "运行")
|
||
{
|
||
targetContext.SingleStep = true;
|
||
LoggerHelper.InfoWithNotify($"{_globalInfo.UserName} 执行工位 [{runningScope}] 单步执行命令");
|
||
|
||
targetContext.RunState = "暂停";
|
||
targetContext.RunIcon = PackIconKind.Pause;
|
||
|
||
if (_globalInfo.CurrentScope == runningScope) RefreshAllContextProperties();
|
||
|
||
if (targetContext.IsStop == null)
|
||
{
|
||
targetContext.IsStop = false;
|
||
|
||
// 💡 单步启动:让专属工位的 SW 跑起来
|
||
targetContext.SW.Start();
|
||
|
||
if (!_executionTasks.TryGetValue(runningScope, out var existingTask) || existingTask == null)
|
||
{
|
||
existingTask = targetRunner.ExecuteSteps(targetContext.Program, cancellationToken: targetRunner.stepCTS.Token);
|
||
_executionTasks[runningScope] = existingTask;
|
||
}
|
||
|
||
try
|
||
{
|
||
await existingTask;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LoggerHelper.ErrorWithNotify($"工位 [{runningScope}] 单步执行异常: {ex.Message}");
|
||
}
|
||
|
||
// 💡 单步单步完成后会被 StepRunning 挂起,在此暂时停止 SW
|
||
targetContext.SW.Stop();
|
||
targetContext.RunningTime = targetContext.SW.Elapsed;
|
||
|
||
targetContext.RunState = "运行";
|
||
targetContext.RunIcon = PackIconKind.Play;
|
||
targetContext.IsStop = null;
|
||
|
||
if (targetRunner.stepCTS.IsCancellationRequested)
|
||
{
|
||
targetRunner.stepCTS = new CancellationTokenSource();
|
||
_executionTasks.TryRemove(runningScope, out _);
|
||
}
|
||
else if (_executionTasks.TryGetValue(runningScope, out var currentTask) && currentTask != null && currentTask.IsCompleted)
|
||
{
|
||
targetContext.IsTerminate = true;
|
||
_executionTasks.TryRemove(runningScope, out _);
|
||
}
|
||
|
||
if (_globalInfo.CurrentScope == runningScope) RefreshAllContextProperties();
|
||
}
|
||
else if (targetContext.IsStop == true)
|
||
{
|
||
targetContext.IsStop = false;
|
||
// 💡 继续单步,继续计时
|
||
targetContext.SW.Start();
|
||
}
|
||
}
|
||
}
|
||
|
||
private async Task OnRestoration()
|
||
{
|
||
string runningScope = _globalInfo.CurrentScope;
|
||
ScopedContext? targetContext = CurrentContext;
|
||
StepRunning? targetRunner = CurrentRunner;
|
||
|
||
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null || targetRunner == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
LoggerHelper.InfoWithNotify($"{_globalInfo.UserName} 执行工位 [{runningScope}] 复位命令");
|
||
|
||
_executionTasks.TryGetValue(runningScope, out var currentTask);
|
||
_errorExecutionTasks.TryGetValue(runningScope, out var errorTask);
|
||
|
||
if (currentTask != null || errorTask != null)
|
||
{
|
||
targetRunner.stepCTS.Cancel();
|
||
targetRunner.errorStepCTS.Cancel();
|
||
|
||
await Task.Delay(200);
|
||
|
||
// 💡 彻底复位:停止当前工位的计时器,并且完全归零(Reset)
|
||
targetContext.SW.Reset();
|
||
targetContext.RunningTime = TimeSpan.Zero;
|
||
|
||
targetContext.IsStop = null;
|
||
targetRunner.ResetAllStepStatus(targetContext.Program.StepCollection);
|
||
targetRunner.ResetAllStepStatus(targetContext.Program.ErrorStepCollection);
|
||
targetContext.IsTerminate = false;
|
||
}
|
||
else
|
||
{
|
||
targetRunner.stepCTS = new CancellationTokenSource();
|
||
targetRunner.errorStepCTS = new CancellationTokenSource();
|
||
|
||
// 💡 无任务状态下的直接归零
|
||
targetContext.SW.Reset();
|
||
targetContext.RunningTime = TimeSpan.Zero;
|
||
|
||
targetContext.IsStop = null;
|
||
targetRunner.ResetAllStepStatus(targetContext.Program.StepCollection);
|
||
targetRunner.ResetAllStepStatus(targetContext.Program.ErrorStepCollection);
|
||
targetContext.IsTerminate = false;
|
||
}
|
||
|
||
if (_globalInfo.CurrentScope == runningScope) RefreshAllContextProperties();
|
||
}
|
||
|
||
private async Task OnRunAbnormalSteps()
|
||
{
|
||
string runningScope = _globalInfo.CurrentScope;
|
||
ScopedContext? targetContext = CurrentContext;
|
||
StepRunning? targetRunner = CurrentRunner;
|
||
|
||
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null || targetRunner == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
LoggerHelper.InfoWithNotify($"{_globalInfo.UserName} 执行工位 [{runningScope}] 异常流程命令");
|
||
|
||
// 💡 异常测试启动计时
|
||
targetContext.SW.Start();
|
||
|
||
if (!_errorExecutionTasks.TryGetValue(runningScope, out var existingErrorTask) || existingErrorTask == null)
|
||
{
|
||
existingErrorTask = targetRunner.ExecuteErrorSteps(targetContext.Program, cancellationToken: targetRunner.errorStepCTS.Token);
|
||
_errorExecutionTasks[runningScope] = existingErrorTask;
|
||
}
|
||
|
||
try
|
||
{
|
||
await existingErrorTask;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LoggerHelper.ErrorWithNotify($"工位 [{runningScope}] 异常流程执行出错: {ex.Message}");
|
||
}
|
||
|
||
// 💡 异常测试结束停止计时
|
||
targetContext.SW.Stop();
|
||
targetContext.RunningTime = targetContext.SW.Elapsed;
|
||
|
||
if (targetRunner.errorStepCTS.IsCancellationRequested)
|
||
{
|
||
targetRunner.errorStepCTS = new CancellationTokenSource();
|
||
_errorExecutionTasks.TryRemove(runningScope, out _);
|
||
}
|
||
else if (_errorExecutionTasks.TryGetValue(runningScope, out var currentErrorTask) && currentErrorTask != null && currentErrorTask.IsCompleted)
|
||
{
|
||
targetContext.IsTerminate = true;
|
||
_errorExecutionTasks.TryRemove(runningScope, out _);
|
||
}
|
||
|
||
if (_globalInfo.CurrentScope == runningScope) RefreshAllContextProperties();
|
||
}
|
||
|
||
private void SetDefault()
|
||
{
|
||
// 💡 1. 抓取触发瞬间的 Scope 与 Context 快照
|
||
string runningScope = _globalInfo.CurrentScope;
|
||
ScopedContext? targetContext = CurrentContext;
|
||
|
||
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null) return;
|
||
|
||
if (targetContext.CurrentFilePath != null)
|
||
{
|
||
//SystemConfig.DefaultProgramFilePath = targetContext.CurrentFilePath;
|
||
//ConfigService.Save();
|
||
LoggerHelper.SuccessWithNotify($"工位 [{runningScope}] 已成功将当前程序设为默认启动程序");
|
||
}
|
||
}
|
||
|
||
private void New()
|
||
{
|
||
// 💡 1. 抓取快照
|
||
string runningScope = _globalInfo.CurrentScope;
|
||
ScopedContext? targetContext = CurrentContext;
|
||
|
||
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null) return;
|
||
|
||
// 💡 2. 严格对快照隔离的 Context 数据进行清理,不影响其他工位
|
||
targetContext.CurrentFilePath = null;
|
||
targetContext.Program.Parameters.Clear();
|
||
targetContext.Program.StepCollection.Clear();
|
||
targetContext.Program.ErrorStepCollection.Clear();
|
||
|
||
LoggerHelper.InfoWithNotify($"工位 [{runningScope}] 创建了空程序文件");
|
||
|
||
// 如果当前正看着该工位,刷新 UI 显示
|
||
if (_globalInfo.CurrentScope == runningScope) RefreshAllContextProperties();
|
||
}
|
||
|
||
private async Task Open(string filePath = null)
|
||
{
|
||
// 💡 1. 抓取进方法瞬间的快照,锁死上下文
|
||
string runningScope = _globalInfo.CurrentScope;
|
||
ScopedContext? targetContext = CurrentContext;
|
||
|
||
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null) return;
|
||
|
||
try
|
||
{
|
||
// 如果没有传路径,弹出文件选择对话框(WPF 对话框是模态的,会阻塞当前 UI 线程)
|
||
if (string.IsNullOrEmpty(filePath))
|
||
{
|
||
var openFileDialog = new Microsoft.Win32.OpenFileDialog
|
||
{
|
||
Filter = "ADP程序文件|*.ADP|所有文件|*.*",
|
||
Title = $"工位 [{runningScope}] 打开程序",
|
||
};
|
||
|
||
if (openFileDialog.ShowDialog() != true)
|
||
return; // 用户取消选择
|
||
|
||
filePath = openFileDialog.FileName;
|
||
}
|
||
|
||
// 确认文件存在
|
||
if (!File.Exists(filePath))
|
||
{
|
||
LoggerHelper.ErrorWithNotify($"文件不存在: {filePath}");
|
||
return;
|
||
}
|
||
|
||
// 读取 JSON 文件
|
||
string json = File.ReadAllText(filePath);
|
||
|
||
// 反序列化为 ProgramModel
|
||
var program = Newtonsoft.Json.JsonConvert.DeserializeObject<ProgramModel>(json);
|
||
|
||
if (program == null)
|
||
{
|
||
LoggerHelper.WarnWithNotify($"文件格式不正确或为空: {filePath}");
|
||
return;
|
||
}
|
||
|
||
// 💡 2. 严格赋值给快照锁定下的当前工位上下文,实现数据完全隔离
|
||
targetContext.Program.Parameters = program.Parameters;
|
||
targetContext.Program.StepCollection = program.StepCollection;
|
||
targetContext.Program.ErrorStepCollection = program.ErrorStepCollection;
|
||
targetContext.CurrentFilePath = filePath;
|
||
|
||
LoggerHelper.SuccessWithNotify($"工位 [{runningScope}] 成功打开文件: {filePath}");
|
||
|
||
// 💡 3. 安全调用异步复位,确保重置的是对应工位的数据
|
||
// 注意:由于在 OnRestoration 内部第一行也做了快照拦截,
|
||
// 如果用户此时正好在看这个工位,它会自动完美执行
|
||
await OnRestoration();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LoggerHelper.ErrorWithNotify($"工位 [{runningScope}] 打开文件失败: {ex.Message}");
|
||
}
|
||
finally
|
||
{
|
||
// 💡 4. 异步回归:不管中途用户切去了哪里,回到该工位时及时刷新显示
|
||
if (_globalInfo.CurrentScope == runningScope) RefreshAllContextProperties();
|
||
}
|
||
}
|
||
|
||
private void SaveAs()
|
||
{
|
||
if (!_globalInfo.IsAdmin) return;
|
||
|
||
// 💡 1. 抓取快照
|
||
string runningScope = _globalInfo.CurrentScope;
|
||
ScopedContext? targetContext = CurrentContext;
|
||
|
||
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null) return;
|
||
|
||
string defaultPath = @"D:\ADP\子程序";
|
||
if (!Directory.Exists(defaultPath))
|
||
Directory.CreateDirectory(defaultPath);
|
||
|
||
var saveFileDialog = new Microsoft.Win32.SaveFileDialog
|
||
{
|
||
Filter = "ADP程序文件|*.adp|所有文件|*.*",
|
||
Title = $"工位 [{runningScope}] 程序另存为",
|
||
FileName = "NewProgram.ADP",
|
||
InitialDirectory = defaultPath
|
||
};
|
||
|
||
if (saveFileDialog.ShowDialog() == true)
|
||
{
|
||
// 💡 2. 将新路径安全写入快照工位
|
||
targetContext.CurrentFilePath = saveFileDialog.FileName;
|
||
|
||
// 💡 3. 调用你的底层文件保存逻辑,传入快照工位的 Program 模型
|
||
SaveProgramToFile(targetContext.CurrentFilePath, targetContext.Program);
|
||
|
||
LoggerHelper.InfoWithNotify($"{_globalInfo.UserName} 另存为文件成功: {saveFileDialog.FileName}");
|
||
|
||
if (_globalInfo.CurrentScope == runningScope) RefreshAllContextProperties();
|
||
}
|
||
}
|
||
|
||
private void Save()
|
||
{
|
||
if (!_globalInfo.IsAdmin) return;
|
||
|
||
// 💡 1. 抓取快照
|
||
string runningScope = _globalInfo.CurrentScope;
|
||
ScopedContext? targetContext = CurrentContext;
|
||
|
||
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null) return;
|
||
|
||
// 💡 2. 判断当前工位是否有历史路径
|
||
if (targetContext.CurrentFilePath == null)
|
||
{
|
||
SaveAs();
|
||
return;
|
||
}
|
||
|
||
// 💡 3. 持久化当前快照工位的 Program
|
||
SaveProgramToFile(targetContext.CurrentFilePath, targetContext.Program);
|
||
LoggerHelper.SuccessWithNotify($"工位 [{runningScope}] 程序保存成功!");
|
||
}
|
||
|
||
// 💡 辅助方法:建议将你的通用序列化落盘代码调整为接收 ProgramModel 参数,提高复用性
|
||
private void SaveProgramToFile(string filePath, ProgramModel programModel)
|
||
{
|
||
try
|
||
{
|
||
string json = Newtonsoft.Json.JsonConvert.SerializeObject(programModel, Newtonsoft.Json.Formatting.Indented);
|
||
File.WriteAllText(filePath, json);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LoggerHelper.ErrorWithNotify($"写入程序文件失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void UpdateRunIcon(string obj)
|
||
{
|
||
CurrentContext?.SW.Stop();
|
||
CurrentContext.RunningTime = CurrentContext.SW.Elapsed;
|
||
RunIcon = obj switch
|
||
{
|
||
"Play" => PackIconKind.Play,
|
||
"Pause" => PackIconKind.Pause,
|
||
_ => RunIcon
|
||
};
|
||
}
|
||
|
||
private void OnRefresh()
|
||
{
|
||
_eventAggregator.GetEvent<ExpandViewEvent>().Publish("");
|
||
_globalInfo.CurrentScope = "default";
|
||
}
|
||
private void OnDestroy()
|
||
{
|
||
if (_globalInfo.CurrentScope == "default" || string.IsNullOrEmpty(_globalInfo.CurrentScope)) return;
|
||
|
||
string targetRegionName = _globalInfo.CurrentScope;
|
||
|
||
// 💡 物理销毁工位时,顺便清理任务字典,防止内存泄漏
|
||
_executionTasks.TryRemove(targetRegionName, out _);
|
||
_errorExecutionTasks.TryRemove(targetRegionName, out _);
|
||
|
||
if (_regionManager.Regions.ContainsRegionWithName(targetRegionName))
|
||
{
|
||
var region = _regionManager.Regions[targetRegionName];
|
||
var viewsToDestroy = region.Views.ToList();
|
||
|
||
foreach (var view in viewsToDestroy)
|
||
{
|
||
if (view is FrameworkElement element)
|
||
{
|
||
var viewModel = element.DataContext;
|
||
region.Remove(view);
|
||
if (viewModel is IDisposable disposableVM) disposableVM.Dispose();
|
||
}
|
||
}
|
||
}
|
||
|
||
_globalInfo.ContextDic.Remove(targetRegionName);
|
||
_globalInfo.StepRunningDic.Remove(targetRegionName);
|
||
|
||
var parameters = new NavigationParameters { { "Name", targetRegionName } };
|
||
_regionManager.RequestNavigate(targetRegionName, "ProtocolStartView", parameters);
|
||
_eventAggregator.GetEvent<ExpandViewEvent>().Publish("");
|
||
}
|
||
private void Load()
|
||
{
|
||
_notificationManager.ShowAsync(new NotificationContent { Title = "登录成功", Message = "", Type = NotificationType.Success });
|
||
//默认导航到主界面
|
||
Type moduleAType = typeof(MainModule.MainModule);
|
||
_moduleManager.LoadModule(moduleAType.Name);
|
||
}
|
||
private void Navigate(string content)
|
||
{
|
||
switch (content)
|
||
{
|
||
case "主界面":
|
||
if (_globalInfo.CurrentScope == "default" || string.IsNullOrEmpty(_globalInfo.CurrentScope))
|
||
{
|
||
_regionManager.RequestNavigate("ShellViewManager", "MainView");
|
||
break;
|
||
}
|
||
var TestingRegion = _globalInfo.CurrentScope;
|
||
if (!_regionManager.Regions.ContainsRegionWithName(TestingRegion)) break;
|
||
_regionManager.RequestNavigate(TestingRegion, "AutomatedTestingView");
|
||
break;
|
||
case "监控界面":
|
||
// 仅当某个工位被选中(九宫格已展开/选择)时才跳转,默认 default 跳过。
|
||
if (_globalInfo.CurrentScope == "default" || string.IsNullOrEmpty(_globalInfo.CurrentScope)) break;
|
||
|
||
// 记录界面不是填到全局的 ShellViewManager,而是填到该工位专属的 region。
|
||
var monitorRegion = _globalInfo.CurrentScope;
|
||
if (!_regionManager.Regions.ContainsRegionWithName(monitorRegion)) break;
|
||
|
||
var monitorParameters = new NavigationParameters { { "Name", monitorRegion } };
|
||
_regionManager.RequestNavigate(monitorRegion, "MonitorView", monitorParameters);
|
||
IsLeftDrawerOpen = false;
|
||
break;
|
||
case "记录界面":
|
||
if (_globalInfo.CurrentScope == "default" || string.IsNullOrEmpty(_globalInfo.CurrentScope)) break;
|
||
|
||
// 记录界面不是填到全局的 ShellViewManager,而是填到该工位专属的 region。
|
||
var recordRegion = _globalInfo.CurrentScope;
|
||
if (!_regionManager.Regions.ContainsRegionWithName(recordRegion)) break;
|
||
|
||
var recordParameters = new NavigationParameters { { "Name", recordRegion } };
|
||
_regionManager.RequestNavigate(recordRegion, "RecordView", recordParameters);
|
||
IsLeftDrawerOpen = false;
|
||
break;
|
||
case "设置界面":
|
||
if (_globalInfo.CurrentScope == "default" || string.IsNullOrEmpty(_globalInfo.CurrentScope)) break;
|
||
|
||
// 记录界面不是填到全局的 ShellViewManager,而是填到该工位专属的 region。
|
||
var settingRegion = _globalInfo.CurrentScope;
|
||
if (!_regionManager.Regions.ContainsRegionWithName(settingRegion)) break;
|
||
|
||
var settingParameters = new NavigationParameters { { "Name", settingRegion } };
|
||
_regionManager.RequestNavigate(settingRegion, "SettingView", settingParameters);
|
||
IsLeftDrawerOpen = false;
|
||
break;
|
||
case "更新界面":
|
||
_regionManager.RequestNavigate("ShellViewManager", "UpdateInfoView");
|
||
break;
|
||
}
|
||
}
|
||
|
||
private void LeftDrawerOpen()
|
||
{
|
||
IsLeftDrawerOpen = true;
|
||
}
|
||
|
||
private void MinimizeWindow(Window window)
|
||
{
|
||
if (window != null)
|
||
window.WindowState = WindowState.Minimized;
|
||
}
|
||
|
||
private void MaximizeWindow(Window window)
|
||
{
|
||
if (window != null)
|
||
{
|
||
window.WindowState = window.WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
|
||
}
|
||
}
|
||
|
||
private void CloseWindow(Window window)
|
||
{
|
||
window?.Close();
|
||
}
|
||
#endregion
|
||
}
|
||
} |