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 Prism.Services.Dialogs;
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 System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel;
namespace TeamAAS_VP.ViewModels
{
public class HomeViewModel : BindableBase, IConfirmNavigationRequest
{
IEventAggregator _eventAggregator;
IContainerProvider _container;
IProductService _productService;
IConfigService _configService;
IRemoteCommandService _remoteCommandService;
ISystemDatabaseService _systemDatabaseService;
IDialogService _dialogService;
#region 属性
private PlotModel _PressurePlotModel = new PlotModel();
///
/// 每个元素是一个 PlotModel,对应一个压力传感器
///
public PlotModel PressurePlotModel
{
get { return _PressurePlotModel; }
set { SetProperty(ref _PressurePlotModel, value); }
}
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 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 bool _IsAllowEdit = false;
public bool IsAllowEdit
{
get { return _IsAllowEdit; }
set { SetProperty(ref _IsAllowEdit, 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));
private DelegateCommand _TorqueInspectionCommand;
public DelegateCommand TorqueInspectionCommand =>
_TorqueInspectionCommand ?? (_TorqueInspectionCommand = new DelegateCommand(ExecuteTorqueInspectionCommand));
//更换吸嘴
private DelegateCommand _ChangeNozzleCommand;
public DelegateCommand ChangeNozzleCommand =>
_ChangeNozzleCommand ?? (_ChangeNozzleCommand = new DelegateCommand(ExecuteChangeNozzleCommand));
//更换披头
private DelegateCommand _ChangeHeaderCommand;
public DelegateCommand ChangeHeaderCommand =>
_ChangeHeaderCommand ?? (_ChangeHeaderCommand = new DelegateCommand(ExecuteChangeHeaderCommand));
//添加螺丝
private DelegateCommand _AddScrewCommand;
public DelegateCommand AddScrewCommand =>
_AddScrewCommand ?? (_AddScrewCommand = new DelegateCommand(ExecuteAddScrewCommand));
private DelegateCommand _SetAlarmValueCommand;
public DelegateCommand SetAlarmValueCommand =>
_SetAlarmValueCommand ?? (_SetAlarmValueCommand = new DelegateCommand(ExecuteSetAlarmValueCommand));
#endregion
#region 事件
#endregion
public HomeViewModel(IEventAggregator ea, IContainerProvider container, IProductService productService, IConfigService configService, IRemoteCommandService remoteCommandService
, ISystemDatabaseService systemDatabaseService, IDialogService dialogService)
{
_eventAggregator = ea;
_container = container;
_dialogService = dialogService;
_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);
InitPressurePlots();
}
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;
if (_systemDatabaseService.GetCurrentUser().userPart == Enums.UserPart.Operator)
{
IsAllowEdit = false;
}
else
{
IsAllowEdit = true;
}
}
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.WriteParameterToPlcExAsync();
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 >= 100)
{
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)
{
if (user.userPart == Enums.UserPart.Operator)
{
IsAllowEdit = false;
}
else
{
IsAllowEdit = true;
}
}
///
/// 重置计数
///
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);
await _systemDatabaseService.ResetThrowNumberAsync();
isCanExecute = true;
}
}
}
///
/// 初始化曲线图
///
private void InitLockCurvePlotModel()
{
var model = new PlotModel
{
Title = Lang.锁付曲线,
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 = Lang.锁付圈数,
Minimum = 0,
MajorGridlineStyle = LineStyle.Solid,
MinorGridlineStyle = LineStyle.Dot
};
model.Axes.Add(xAxis);
// Y轴(力矩)
var yAxis = new LinearAxis
{
Key = "Y",
Position = AxisPosition.Left,
Title = Lang.力矩kgf,
MajorGridlineStyle = LineStyle.Solid,
MinorGridlineStyle = LineStyle.Dot
};
model.Axes.Add(yAxis);
// 实际锁付曲线
var torqueSeries = new LineSeries
{
Title = Lang.锁付力矩曲线,
StrokeThickness = 2,
MarkerSize = 3,
MarkerType = MarkerType.Circle,
CanTrackerInterpolatePoints = false,
Color = OxyColors.Blue
};
model.Series.Add(torqueSeries);
// 上限线
var upperLimitSeries = new LineSeries
{
Title = "Torque Upper Limit",
StrokeThickness = 2,
LineStyle = LineStyle.Dash,
Color = OxyColors.Red
};
model.Series.Add(upperLimitSeries);
// 下限线
var lowerLimitSeries = new LineSeries
{
Title = "Torque Lower Limit",
StrokeThickness = 2,
LineStyle = LineStyle.Dash,
Color = OxyColors.Green
};
model.Series.Add(lowerLimitSeries);
LockCurvePlotModel = model;
}
///
/// 螺丝锁付完成时调用
///
///
private void OnLockFinish(LockResult result)
{
//如果当前螺丝编号为1,则清除之前的结果
if (result.Number == 1)
{
LockResults.Clear();
}
LockResults.Add(result);
App.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
//获取三条曲线
var torqueSeries = LockCurvePlotModel.Series[0] as LineSeries; //实际扭矩
var upperSeries = LockCurvePlotModel.Series[1] as LineSeries; //上限
var lowerSeries = LockCurvePlotModel.Series[2] as LineSeries; //下限
torqueSeries.Points.Clear();
upperSeries.Points.Clear();
lowerSeries.Points.Clear();
//上下限值(根据你的工艺设置)
double upperLimit = result.TargetTorqueUpperLimit; //上限扭矩
double lowerLimit = result.TargetTorqueLowerLimit; //下限扭矩
foreach (var waveData in result.WaveDatas)
{
//实际曲线
torqueSeries.Points.Add(new OxyPlot.DataPoint(waveData.Turns, waveData.Torque));
//上限曲线(固定扭矩)
upperSeries.Points.Add(new OxyPlot.DataPoint(waveData.Turns, upperLimit));
//下限曲线
lowerSeries.Points.Add(new OxyPlot.DataPoint(waveData.Turns, lowerLimit));
}
//刷新
LockCurvePlotModel.ResetAllAxes();
LockCurvePlotModel.InvalidatePlot(true);
//保存图片
if (!string.IsNullOrEmpty(result.LockCurveImagePath))
{
PngExporter.Export(
LockCurvePlotModel,
result.LockCurveImagePath,
(int)LockCurvePlotModel.Width,
(int)LockCurvePlotModel.Height);
}
}
catch (Exception ex)
{
LogHelper.WriteLogError("更新曲线图和保存曲线图时出错!", ex);
}
}));
}
///
/// 添加螺丝
///
private void ExecuteAddScrewCommand()
{
_dialogService.ShowDialog("AddScrew", rst =>
{
//对话框关闭之后的回调函数,可以在这解析结果。
ButtonResult result1 = rst.Result;
if (result1 == ButtonResult.OK)
{
_eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.完成, Duration = 1 });
}
});
}
///
/// 更换披头
///
void ExecuteChangeHeaderCommand()
{
_dialogService.ShowDialog("ChangeHeader", rst =>
{
//对话框关闭之后的回调函数,可以在这解析结果。
ButtonResult result1 = rst.Result;
if (result1 == ButtonResult.OK)
{
_eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.完成, Duration = 1 });
}
});
}
///
/// 更换吸嘴
///
void ExecuteChangeNozzleCommand()
{
_dialogService.ShowDialog("ChangeNozzle", rst =>
{
//对话框关闭之后的回调函数,可以在这解析结果。
ButtonResult result1 = rst.Result;
if (result1 == ButtonResult.OK)
{
_eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.完成, Duration = 1 });
}
});
}
void ExecuteTorqueInspectionCommand()
{
_dialogService.ShowDialog("TorqueCheck", rst =>
{
//对话框关闭之后的回调函数,可以在这解析结果。
ButtonResult result1 = rst.Result;
if (result1 == ButtonResult.OK)
{
_eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.完成, Duration = 1 });
}
});
}
void ExecuteSetAlarmValueCommand()
{
_dialogService.ShowDialog("SetAlarmValue", rst =>
{
//对话框关闭之后的回调函数,可以在这解析结果。
ButtonResult result1 = rst.Result;
if (result1 == ButtonResult.OK)
{
_eventAggregator.GetEvent().Publish(new MessageParameter() { Msg = Lang.完成, Duration = 1 });
}
});
}
///
/// 保压完成时调用
///
///
private void OnPressureFinish((bool IsSuccess, (DateTime Timestamp, float Value)[] values, string SavePath) tuple)
{
//更新对应传感器的曲线图
//App.Current.Dispatcher.BeginInvoke(new Action(() =>
//{
// var model = PressurePlotModel;
// 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.ResetAllAxes();
// 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()
{
var model = CreatePlotModelForSensor();
PressurePlotModel = model;
}
private PlotModel CreatePlotModelForSensor()
{
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 = "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 1",
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(Lang.主界面界面进入时出错, 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;
}));
}
}
}