using MahApps.Metro.Controls; using MaterialDesignThemes.Wpf; using NPOI.SS.Formula.Functions; using OxyPlot; using OxyPlot.Axes; using OxyPlot.Legends; using OxyPlot.Series; using OxyPlot.Wpf; using Prism.Commands; using Prism.Events; using Prism.Ioc; using Prism.Mvvm; using Prism.Regions; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using TeamAAS_VP; using TeamAAS_VP.Controls; using TeamAAS_VP.Core; using TeamAAS_VP.Data; using TeamAAS_VP.Enums; using TeamAAS_VP.Events; using TeamAAS_VP.Interfaces; using TeamAAS_VP.Models; using TeamAAS_VP.Resources.Languages; using TeamAAS_VP.Services; using TeamAAS_VP.ViewModels.DebugMod; using TeamAAS_VP.Views.DebugMod; using static TeamAAS_VP.Models.SystemConfiguration; namespace TeamAAS_VP.ViewModels { public class HomeViewModel : BindableBase, IConfirmNavigationRequest { IEventAggregator _eventAggregator; IContainerProvider _container; IProductService _productService; IConfigService _configService; IRemoteCommandService _remoteCommandService; ISystemDatabaseService _systemDatabaseService; #region 属性 private ObservableCollection messageQueue = new ObservableCollection(); /// /// 运行信息队列 /// public ObservableCollection MessageQueue { get { return messageQueue; } set { SetProperty(ref messageQueue, value); } } private ObservableCollection _ProductList; public ObservableCollection ProductList { get { return _ProductList; } set { SetProperty(ref _ProductList, value); } } private Management _management; public Management management { get { return _management; } set { SetProperty(ref _management, value); } } private double _CTTotalSeconds; public double CTTotalSeconds { get { return _CTTotalSeconds; } set { SetProperty(ref _CTTotalSeconds, value); } } private ProductModel _SelectProduct; public ProductModel SelectProduct { get { return _SelectProduct; } set { SetProperty(ref _SelectProduct, value); } } private bool CanLoad { get { if (SelectProduct != null && IsLoadProduct) { return true; } else { return false; } } } private bool _IsLoadProduct = true; public bool IsLoadProduct { get { return _IsLoadProduct; } set { SetProperty(ref _IsLoadProduct, value); } } private bool _isCanExecute = true; public bool isCanExecute { get { return _isCanExecute; } set { SetProperty(ref _isCanExecute, value); } } private ProductModel _CurrentProduct; public ProductModel CurrentProduct { get { return _CurrentProduct; } set { SetProperty(ref _CurrentProduct, value); } } private ObservableCollection _PressurePlotModels = new ObservableCollection(); /// /// 每个元素是一个 PlotModel,对应一个压力传感器 /// public ObservableCollection PressurePlotModels { get { return _PressurePlotModels; } set { SetProperty(ref _PressurePlotModels, value); } } private PlotModel _LockCurvePlotModel; /// /// 锁付曲线 /// public PlotModel LockCurvePlotModel { get { return _LockCurvePlotModel; } set { SetProperty(ref _LockCurvePlotModel, value); } } private ObservableCollection _LockResults = new ObservableCollection(); /// /// 锁付结果列表 /// public ObservableCollection LockResults { get { return _LockResults; } set { SetProperty(ref _LockResults, value); } } private ObservableCollection _AssemblyResults=new ObservableCollection(); /// /// 组装结果列表 /// public ObservableCollection AssemblyResults { get { return _AssemblyResults; } set { SetProperty(ref _AssemblyResults, value); } } // 可选:记录传感器 Id -> 索引 的映射,便于外部(如 Management)按 Id 更新 private readonly Dictionary _sensorIdToIndex = new Dictionary(); #endregion #region 命令 public DelegateCommand LoadedCommand { get; set; } private DelegateCommand _LoadProductCommand; public DelegateCommand LoadProductCommand => _LoadProductCommand ?? (_LoadProductCommand = new DelegateCommand(ExecuteLoadProductCommand).ObservesCanExecute(() => CanLoad).ObservesProperty(() => SelectProduct).ObservesProperty(() => IsLoadProduct)); private DelegateCommand _ResetCounterCommand; public DelegateCommand ResetCounterCommand => _ResetCounterCommand ?? (_ResetCounterCommand = new DelegateCommand(ExecuteResetCounterCommand).ObservesCanExecute(() => DoCondition).ObservesProperty(() => isCanExecute)); private bool DoCondition { get { return isCanExecute; } } private DelegateCommand _CopyCommand; public DelegateCommand CopyCommand => _CopyCommand ?? (_CopyCommand = new DelegateCommand(ExecuteCopyCommand)); #endregion #region 事件 #endregion public HomeViewModel(IEventAggregator ea, IContainerProvider container, IProductService productService, IConfigService configService, IRemoteCommandService remoteCommandService , ISystemDatabaseService systemDatabaseService) { _eventAggregator = ea; _container = container; _productService = productService; LoadedCommand = new DelegateCommand(OnLoad); _eventAggregator.GetEvent().Subscribe(AddMessageInvoke); //订阅权限登录事件 _eventAggregator.GetEvent().Subscribe(LoginChange); management = _container.Resolve(); _configService = configService; _productService.OnProductChanged += _productService_OnProductChanged; _remoteCommandService = remoteCommandService; _remoteCommandService.CycleTimingTicked += _remoteCommandService_CycleTimingTicked; _systemDatabaseService = systemDatabaseService; _eventAggregator.GetEvent().Subscribe(OnLockFinish); _eventAggregator.GetEvent().Subscribe(OnPressureFinish); _eventAggregator.GetEvent().Subscribe(OnAssemblyFinish); //获取所有的压力传感器配置,初始化曲线图 var systemConfig = _configService.GetSystemConfiguration(); if (systemConfig.PressureSensorConfig != null) { InitPressurePlots(systemConfig.PressureSensorConfig.ToArray()); } } private void _remoteCommandService_CycleTimingTicked(object sender, TimeSpan e) { try { if (App.Current != null) { App.Current.Invoke(() => { CTTotalSeconds = e.TotalSeconds; }); } } catch (Exception) { } } #region 方法 bool isFirst = true; private void OnLoad() { if (!isFirst) { return; } InitLockCurvePlotModel(); isFirst = false; } async void ExecuteLoadProductCommand(ProductModel product) { if (product == null) return; var view = new ShowMessage(Lang.是否加载产品, $"{Lang.加载产品}:{product.Name}?"); //show the dialog var result = await DialogHost.Show(view, "RootDialog", null, null, null); if (result != null && result is bool) { if (((bool)result)) { IsLoadProduct = false; var waiting = new WaitingControl(); //show the dialog var task = DialogHost.Show(waiting, "RootDialog", null, null, null); await _productService.LoadProductAsync(product.ID); _productService.SetCurrentProduct(product.ID); await management.WritePositionToPlcAsync(); await management.WriteParameterToPlcAsync(); if (DialogHost.IsDialogOpen("RootDialog")) { DialogHost.Close("RootDialog"); } await task; //DatabaseHelper.AddLoadProductRecord(product.ID, product.Name, false); IsLoadProduct = true; } } } void ExecuteCopyCommand(string textToCopy) { Clipboard.SetText(textToCopy); } /// /// 添加消息至消息队列 /// /// /// public void AddMessage(string msg, Color color) { App.Current.Dispatcher.BeginInvoke(new Action(() => { try { MessageQueue.Add(new TaskMessage() { DT = DateTime.Now, Message = msg, FontColol = new SolidColorBrush(color) }); LogHelper.WriteLogInfo(msg); } catch (Exception) { } })); } /// /// 添加消息至消息队列 /// /// /// public void AddMessage(string msg, MessageLevel level) { MessageStruct messageStruct = new MessageStruct() { Message = msg, level = level }; App.Current.Dispatcher.BeginInvoke(new Action(() => { AddMessage(messageStruct); })); } /// /// 添加消息至消息队列 /// /// public void AddMessage(MessageStruct messageStruct) { try { if (MessageQueue.Count >= 200) { MessageQueue.RemoveAt(0);// 从队列头部移除最早的消息 } Color color = Colors.Black; switch (messageStruct.level) { case MessageLevel.Info: color = Colors.Black; break; case MessageLevel.Debug: color = Colors.Green; break; case MessageLevel.Alarm: color = Colors.Orange; break; case MessageLevel.Error: color = Colors.Red; break; } MessageQueue.Add(new TaskMessage() { DT = DateTime.Now, Message = messageStruct.Message, FontColol = new SolidColorBrush(color) }); LogHelper.WriteLogInfo(messageStruct.Message); } catch (Exception) { } } public void AddMessageInvoke(MessageStruct messageStruct) { App.Current.Dispatcher.BeginInvoke(new Action(() => { AddMessage(messageStruct); })); } private void LoginChange(Models.User user) { } /// /// 重置计数 /// async void ExecuteResetCounterCommand() { var currentProduct = _productService.GetCurrentProduct(); if (currentProduct == null) return; var view = new ShowMessage(Lang.计数清零, Lang.是否重置当前产品的计数); //show the dialog var result = await DialogHost.Show(view, "RootDialog", null, null, null); if (result != null && result is bool) { if (((bool)result)) { isCanExecute = false; await _systemDatabaseService.ResetProductionAsync(currentProduct.Name); isCanExecute = true; } } } //初始化曲线图 private void InitLockCurvePlotModel() { var model = new PlotModel { Title = "锁付曲线", TitleFontSize = 16 }; // 设置图例 var legend = new Legend { LegendPosition = LegendPosition.TopRight, LegendPlacement = LegendPlacement.Outside, LegendOrientation = LegendOrientation.Vertical, LegendBorderThickness = 0 }; model.Legends.Add(legend); // 设置X轴 var xAxis = new LinearAxis { Key = "X", Position = AxisPosition.Bottom, Title = "角度 (°)", Minimum = 0, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot }; model.Axes.Add(xAxis); // 设置Y轴 var yAxis = new LinearAxis { Key = "Y", Position = AxisPosition.Left, Title = "力矩 (kgf)", MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot }; model.Axes.Add(yAxis); // 添加示例数据系列 var series = new LineSeries { Title = "锁付力矩曲线", StrokeThickness = 2, MarkerSize = 3, MarkerType = MarkerType.Circle, CanTrackerInterpolatePoints = false, }; model.Series.Add(series); LockCurvePlotModel = model; } /// /// 螺丝锁付完成时调用 /// /// /// private void OnLockFinish(LockResult result) { //如果当前螺丝编号为1,则清除之前的结果 if (result.Number == 1) { LockResults.Clear(); } LockResults.Add(result); //更新曲线图 App.Current.Dispatcher.BeginInvoke(new Action(() => { var series = LockCurvePlotModel.Series[0] as LineSeries; //var yaxis = LockCurvePlotModel.Axes.FirstOrDefault(p=>p.Key=="Y"); //yaxis.Maximum = result.TargetTorque; series.Points.Clear(); foreach (var waveData in result.WaveDatas) { series.Points.Add(new OxyPlot.DataPoint(waveData.LockAngle, waveData.Torque)); } LockCurvePlotModel.InvalidatePlot(true); })); } /// /// 锁付完成时调用 /// /// /// private void OnAssemblyFinish(AssemblyRecord record) { AssemblyResults.Add(record); //如果记录数超过1000,则删除最早的记录 if (AssemblyResults.Count > 50) { AssemblyResults.RemoveAt(0); } } /// /// 保压完成时调用 /// /// private void OnPressureFinish((bool IsSuccess, int Index, (DateTime Timestamp, float Value)[] values, string SavePath) tuple) { //更新对应传感器的曲线图 if (_sensorIdToIndex.TryGetValue(tuple.Index.ToString(), out int index)) { App.Current.Dispatcher.BeginInvoke(new Action(() => { var model = PressurePlotModels[index]; var series = model.Series[0] as LineSeries; series.Points.Clear(); //第一个点的时间作为X轴起点 DateTime xAxisStart = tuple.values[0].Timestamp; foreach (var dataPoint in tuple.values) { // 计算相对于起点的秒数作为X轴坐标 double xValue = (dataPoint.Timestamp - xAxisStart).TotalSeconds; series.Points.Add(new OxyPlot.DataPoint(xValue, dataPoint.Value)); } model.InvalidatePlot(true); if (!string.IsNullOrEmpty(tuple.SavePath)) { if (!Directory.Exists(Path.GetDirectoryName(tuple.SavePath))) { Directory.CreateDirectory(Path.GetDirectoryName(tuple.SavePath)); } PngExporter.Export(model, tuple.SavePath, (int)model.Width, (int)model.Height); } })); } } // 初始化由配置决定的曲线数量(在读取 SystemConfiguration 后调用) public void InitPressurePlots(IEnumerable sensors) { PressurePlotModels.Clear(); _sensorIdToIndex.Clear(); int idx = 0; foreach (var s in sensors) { var model = CreatePlotModelForSensor(s); PressurePlotModels.Add(model); // 假设 PressureSensorConfig 有唯一 Id(string 或 int) _sensorIdToIndex[s.SensorId.ToString()] = idx++; } } private PlotModel CreatePlotModelForSensor(PressureSensorSettings sensor) { var model = new PlotModel { Title = sensor.SensorName ?? $"Sensor {sensor.SensorId}", TitleFontSize = 16 }; // 设置图例 var legend = new Legend { LegendPosition = LegendPosition.TopRight, LegendPlacement = LegendPlacement.Outside, LegendOrientation = LegendOrientation.Vertical, LegendBorderThickness = 0 }; model.Legends.Add(legend); // 设置X轴 var xAxis = new LinearAxis { Key = "X", Position = AxisPosition.Bottom, Title = "Time (s)", Minimum = 0, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot }; model.Axes.Add(xAxis); // 设置Y轴 var yAxis = new LinearAxis { Key = "Y", Position = AxisPosition.Left, Title = "Pressure", MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot }; model.Axes.Add(yAxis); // 添加示例数据系列 var series = new LineSeries { Title = sensor.SensorName ?? $"Sensor {sensor.SensorId}", StrokeThickness = 2, MarkerSize = 3, MarkerType = MarkerType.Circle, CanTrackerInterpolatePoints = false, }; model.Series.Add(series); return model; } #endregion #region 继承 /// /// 确认导航请求时调用。此方法允许您在导航之前执行一些操作。 /// /// /// public void ConfirmNavigationRequest(NavigationContext navigationContext, Action continuationCallback) { continuationCallback(true); } /// /// 接收导航请求时调用。传入导航参数,包含有关导航目标的信息。 /// /// /// public async void OnNavigatedTo(NavigationContext navigationContext) { try { ProductList = new ObservableCollection(_productService.GetAllProducts()); } catch (Exception ex) { LogHelper.WriteLogError("主界面界面进入时出错!", ex); } } /// /// 是否允许导航到此视图模型。 /// /// /// public bool IsNavigationTarget(NavigationContext navigationContext) { return true; } /// /// 导航离开此视图模型时调用。您可以在此处执行清理操作或保存状态。 /// /// public void OnNavigatedFrom(NavigationContext navigationContext) { } #endregion private void _productService_OnProductChanged(ProductModel obj) { App.Current.Dispatcher.BeginInvoke(new Action(() => { CurrentProduct = obj; })); } } }