Files
ADP/ADP/ViewModels/ShellViewModel.cs
T

920 lines
38 KiB
C#

using DeviceCommand.Devices;
using Logger;
using MaterialDesignThemes.Wpf;
using Microsoft.Win32;
using Model.Models;
using Notifications.Wpf.Core;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
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;
using SqlSugar;
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 IDialogService _dialogService;
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);
}
public TimeSpan RunningTime
{
get
{
if (CurrentContext == null) return TimeSpan.Zero;
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;
private SystemConfig? CurrentConfig =>
_globalInfo.ConfigDic.TryGetValue(_globalInfo.CurrentScope, out var config) ? config : 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; }
public ICommand ShowDialogManagerViewCommand { get; set; }
public ICommand SelectCANSignalMonitorCommand { get; set; }
public ICommand SelectCANMessageCommand { get; set; }
public ICommand MonitorValueSettingCommand { get; set; }
public ICommand GetFileStringCommand { get; set; }
public ICommand SilenceBuzzerCommand { get; set; }
public ICommand GoBackCommand { get; set; }
#endregion
#region 子程序导航
public bool CanGoBack =>
(CurrentContext?.MainNav.CanGoBack ?? false) ||
(CurrentContext?.ErrorNav.CanGoBack ?? false);
private void OnGoBack()
{
var ctx = CurrentContext;
if (ctx == null) return;
// 工具栏按钮同时重置两个 Tab 的导航
ctx.MainNav.Reset(ctx.Program, "主程序");
ctx.ErrorNav.Reset(ctx.Program, "错误程序");
}
#endregion
public ShellViewModel(IContainerProvider containerProvider)
{
_containerProvider = containerProvider;
_dialogService= containerProvider.Resolve<IDialogService>();
_globalInfo = containerProvider.Resolve<GlobalInfo>();
_eventAggregator = containerProvider.Resolve<IEventAggregator>();
_regionManager = containerProvider.Resolve<IRegionManager>();
_notificationManager = containerProvider.Resolve<INotificationManager>();
_moduleManager = containerProvider.Resolve<IModuleManager>();
LeftDrawerOpenCommand = new DelegateCommand(LeftDrawerOpen);
ShowDialogManagerViewCommand = new DelegateCommand(ShowDialogManagerView);
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);
SelectCANSignalMonitorCommand = new DelegateCommand(SelectCANSignalMonitor);
SelectCANMessageCommand = new DelegateCommand(SelectCANMessage);
MonitorValueSettingCommand = new DelegateCommand(MonitorValueSetting);
GetFileStringCommand = new DelegateCommand(GetFileString);
SilenceBuzzerCommand = new AsyncDelegateCommand(SilenceBuzzer);
GoBackCommand = new DelegateCommand(OnGoBack);
_globalInfo.ContextDic.TryAdd("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 Task SilenceBuzzer()
{
_eventAggregator.GetEvent<SilenceBuzzerEvent>().Publish(_globalInfo.CurrentScope);
}
private void GetFileString()
{
var openFileDialog = new OpenFileDialog
{
Title = "选择文件",
Filter = "所有文件 (*.*)|*.*",
};
if (openFileDialog.ShowDialog() == true)
{
string filePath = openFileDialog.FileName;
Application.Current.Dispatcher.Invoke(() =>
{
Clipboard.SetDataObject(filePath);
});
}
}
private void MonitorValueSetting()
{
if ( _globalInfo.CurrentScope == "default")
{
LoggerHelper.Warn("当前作用域未配置 CAN 设备,无法打开报文选择窗口。");
return;
}
_dialogService.ShowDialog("ValueLimitView", new DialogParameters
{
{ "Scope", _globalInfo.ScopeDic[_globalInfo.CurrentScope] }
}, _ => { });
}
private void SelectCANSignalMonitor()
{
var canfd = CurrentConfig?.CANFD;
if (canfd == null || _globalInfo.CurrentScope == "default")
{
return;
}
_dialogService.ShowDialog("CollectCANSignalSettingView", new DialogParameters
{
{ "SystemConfig", CurrentConfig }
}, _ => { });
}
private void SelectCANMessage()
{
var canfd = CurrentConfig?.CANFD;
if (canfd == null||_globalInfo.CurrentScope=="default")
{
LoggerHelper.Warn("当前作用域未配置 CAN 设备,无法打开报文选择窗口。");
return;
}
_dialogService.ShowDialog("SelectCanMessageView", new DialogParameters
{
{ "CANFD", canfd }
}, _ => { });
}
private void ShowDialogManagerView()
{
_eventAggregator.GetEvent<CancelMinimizeEvent>().Publish();
}
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(runningScope, $"{_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, $"工位 [{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(runningScope, $"{_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(runningScope, $"{_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, $"工位 [{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(runningScope, $"{_globalInfo.UserName} 执行工位 [{runningScope}] 复位命令");
// 重置导航栈,回到根程序界面
targetContext.ResetNavigation();
_executionTasks.TryGetValue(runningScope, out var currentTask);
_errorExecutionTasks.TryGetValue(runningScope, out var errorTask);
if (currentTask != null || errorTask != null)
{
targetRunner.stepCTS.Cancel();
targetRunner.errorStepCTS.Cancel();
// 💡 等待旧任务真正结束,而不是只等 200ms
if (currentTask != null)
{
try { await currentTask; } catch { /* 取消导致的异常忽略 */ }
}
if (errorTask != null)
{
try { await errorTask; } catch { /* 取消导致的异常忽略 */ }
}
// 💡 彻底复位:替换 CTS、清理任务字典、停止计时器、归零状态
targetRunner.stepCTS = new CancellationTokenSource();
targetRunner.errorStepCTS = new CancellationTokenSource();
_executionTasks.TryRemove(runningScope, out _);
_errorExecutionTasks.TryRemove(runningScope, out _);
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(runningScope, $"{_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, $"工位 [{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()
{
if (CurrentConfig == null) return;
// 💡 1. 抓取触发瞬间的 Scope 与 Context 快照
string runningScope = _globalInfo.CurrentScope;
ScopedContext? targetContext = CurrentContext;
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null) return;
if (targetContext.CurrentFilePath != null)
{
CurrentConfig.DefaultProgramFilePath = targetContext.CurrentFilePath;
ConfigService.Save(CurrentConfig);
LoggerHelper.SuccessWithNotify(runningScope, $"工位 [{runningScope}] 已成功将当前程序设为默认启动程序");
}
}
private void New()
{
// 💡 1. 抓取快照
string runningScope = _globalInfo.CurrentScope;
ScopedContext? targetContext = CurrentContext;
if (runningScope == "default" || string.IsNullOrEmpty(runningScope) || targetContext == null) return;
CurrentConfig.CurrentADPFile = "新建ADP文件";
// 💡 2. 严格对快照隔离的 Context 数据进行清理,不影响其他工位
targetContext.CurrentFilePath = null;
targetContext.Program.Parameters.Clear();
targetContext.Program.StepCollection.Clear();
targetContext.Program.ErrorStepCollection.Clear();
targetContext.ResetNavigation();
foreach (var item in CurrentConfig.ParameterList)
{
var param = CurrentConfig.SharedParameterList.FirstOrDefault(x => x.ParameterName == item.Name);
if (param != null)
{
item.Value = param.Value;
}
targetContext.Program.Parameters.Add(item);
}
LoggerHelper.InfoWithNotify(runningScope, $"工位 [{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(runningScope, $"文件不存在: {filePath}");
return;
}
// 读取 JSON 文件
string json = File.ReadAllText(filePath);
// 反序列化为 ProgramVM
var program = Newtonsoft.Json.JsonConvert.DeserializeObject<ProgramVM>(json);
if (program == null)
{
LoggerHelper.WarnWithNotify(runningScope, $"文件格式不正确或为空: {filePath}");
return;
}
CurrentConfig.CurrentADPFile = filePath;
// 💡 2. 严格赋值给快照锁定下的当前工位上下文,实现数据完全隔离
targetContext.Program.Parameters = program.Parameters;
targetContext.Program.StepCollection = program.StepCollection;
targetContext.Program.ErrorStepCollection = program.ErrorStepCollection;
targetContext.CurrentFilePath = filePath;
foreach (var item in CurrentConfig.SharedParameterList)
{
var parameter = targetContext?.Program?.Parameters?.FirstOrDefault(x => x.Name == item.ParameterName);
if (parameter != null)
{
parameter.Value = item.Value;
}
}
LoggerHelper.SuccessWithNotify(runningScope, $"工位 [{runningScope}] 成功打开文件: {filePath}");
// 💡 3. 安全调用异步复位,确保重置的是对应工位的数据
// 注意:由于在 OnRestoration 内部第一行也做了快照拦截,
// 如果用户此时正好在看这个工位,它会自动完美执行
await OnRestoration();
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify(runningScope, $"工位 [{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. 将新路径安全写入快照工位
CurrentConfig.CurrentADPFile = saveFileDialog.FileName;
targetContext.CurrentFilePath = saveFileDialog.FileName;
// 💡 3. 调用你的底层文件保存逻辑,传入快照工位的 Program 模型
SaveProgramToFile(targetContext.CurrentFilePath, targetContext.Program);
LoggerHelper.InfoWithNotify(runningScope, $"{_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(_globalInfo.CurrentScope,$"工位 [{runningScope}] 程序保存成功!");
}
// 💡 辅助方法:建议将你的通用序列化落盘代码调整为接收 ProgramVM 参数,提高复用性
private void SaveProgramToFile(string filePath, ProgramVM ProgramVM)
{
try
{
string json = Newtonsoft.Json.JsonConvert.SerializeObject(ProgramVM, Newtonsoft.Json.Formatting.Indented);
File.WriteAllText(filePath, json);
}
catch (Exception ex)
{
LoggerHelper.ErrorWithNotify(_globalInfo.CurrentScope,$"写入程序文件失败: {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();
}
}
}
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 "台架移动界面":
if (_globalInfo.CurrentScope != "default") return;
_regionManager.RequestNavigate("ShellViewManager", "BenchMovementView");
break;
case "曲线回读界面":
if (_globalInfo.CurrentScope != "default") return;
_regionManager.RequestNavigate("ShellViewManager", "CurveRecallView");
break;
case "测试报告导出界面":
if (_globalInfo.CurrentScope != "default") return;
_regionManager.RequestNavigate("ShellViewManager", "ExportView");
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
}
}