Sic10/SicUI/Models/PMs/Charting/PMChartingV2ViewModel.cs

730 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using MECF.Framework.Common.DataCenter;
using MECF.Framework.UI.Client.ClientBase;
using SciChart.Charting.Common.Helpers;
using SciChart.Charting.ViewportManagers;
using SciChart.Charting.Visuals.Annotations;
using SciChart.Charting.Visuals.Axes;
using SciChart.Charting.Visuals.RenderableSeries;
using SciChart.Data.Model;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows;
using Cali = Caliburn.Micro;
using MECF.Framework.UI.Client.CenterViews.DataLogs.ProcessHistory;
using System.Reflection;
using SciChart.Charting.Model.ChartSeries;
using System.IO;
using Aitex.Core.Util;
using Aitex.Core.RT.Log;
namespace SicUI.Models.PMs.Charting
{
public class PMChartingV2ViewModel : SicModuleUIViewModelBase, ISupportMultipleSystem
{
private class QueryIndexer
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public string Module { get; set; }
public string ColumneName { get; set; }
public string ProcessGuid { get; set; }
public List<string> DateList { get; set; }
public long currentTimeTicks { get; set; }
}
/// <summary>
/// ViewModel绑定的View对象
/// </summary>
private PMChartingV2View view;
/// <summary>
/// 初始化界面
/// </summary>
public PMChartingV2ViewModel()
{
DisplayName = "Process History";
var now = DateTime.Now;
this.StartDateTime = new DateTime(now.Year, now.Month, now.Day, 0, 0, 0, 0);
this.EndDateTime = new DateTime(now.Year, now.Month, now.Day, 23, 59, 59, 999);
SourcePM = new ObservableCollection<string>(new[] { "PM1", "PM2" });
//sci按钮操作
DataDetailVisbleCommand = new ActionCommand(SetDataDetailVisble);
DefaultZoomCommand = new ActionCommand(Zoom);
ShowLegendCommand = new ActionCommand(SetLegendAvalible);
//获取所有Device
GetPMNode();
SelectedData = new ObservableCollection<IRenderableSeriesViewModel>();
SelectedRecipes = new ObservableCollection<RecipeItemSic>();
Recipes = new ObservableCollection<RecipeItemSic>();
//x y always指zoom功能失效总是自动调整大小
//ChartAutoRange = AutoRange.Always;
VisibleRangeX = new DoubleRange(-1000, 10000);
VisibleRangeY = new DoubleRange(-10, 500);
VisibleRangeXLimit = new DoubleRange(-1000, 10000);
VisibleRangeYLimit = new DoubleRange(-10, 500);
_thread = new PeriodicJob(300, MonitorHistoryData, "History", true);
//_threadReal = new PeriodicJob(TrendInterval, MonitorRealTimeData, "RealTime", true);
}
protected override void OnViewLoaded(object _view)
{
//获取绑定的Viewwpf没有默认的日期时间控件Winform日期控件无法通过绑定属性获取
base.OnViewLoaded(_view);
this.view = (PMChartingV2View)_view;
this.view.wfTimeFrom.Value = this.StartDateTime;
this.view.wfTimeTo.Value = this.EndDateTime;
}
private PeriodicJob _thread;
//private PeriodicJob _threadReal;
//DeviceTimer dt = new DeviceTimer();
//设置允许同时显示的最多曲线数
private const int MAX_PARAMETERS = 20;
/// <summary>
/// sci图表的视图管理类
/// </summary>
private readonly IViewportManager _viewportManager = new DefaultViewportManager();
public IViewportManager ViewportManager
{
get { return _viewportManager; }
}
#region Properties
/// <summary>
/// 搜索条件:Recipe开始时间
/// </summary>
public DateTime StartDateTime { get; set; }
/// <summary>
/// 搜索条件:Recipe结束时间
/// </summary>
public DateTime EndDateTime { get; set; }
/// <summary>
/// 搜索条件:PM腔
/// </summary>
public string SelectedValuePM { get; set; }
/// <summary>
/// 搜索条件:PM腔
/// </summary>
public ObservableCollection<string> SourcePM { get; set; }
/// <summary>
/// 搜索条件:Recipe
/// </summary>
public string RecipeName { get; set; }
/// <summary>
/// 查询曲线用点位组,每次有曲线变动时入队,生成曲线后出队
/// </summary>
ConcurrentBag<QueryIndexer> _lstTokenTimeData = new ConcurrentBag<QueryIndexer>();
public ObservableCollection<DataDetail> QueryDataDetail { get; set; }
/// <summary>
/// 用户选中的曲线组前台treeview和chart调用
/// </summary>
public ObservableCollection<IRenderableSeriesViewModel> SelectedData { get; set; }
/// <summary>
/// 根据条件搜索的recipe
/// </summary>
public ObservableCollection<RecipeItemSic> Recipes { get; set; }
/// <summary>
/// 用户选中查阅的recipe
/// </summary>
public ObservableCollection<RecipeItemSic> SelectedRecipes { get; set; }
private List<PMNodeInfo> Nodes = new List<PMNodeInfo>();
/// <summary>
/// 获取设备需要监控的Device点
/// </summary>
private ObservableCollection<PMNodeInfo> _configNodes = new ObservableCollection<PMNodeInfo>();
public ObservableCollection<PMNodeInfo> ConfigNodes
{
get { return _configNodes; }
set { _configNodes = value; NotifyOfPropertyChange("ConfigNodes"); }
}
private double _maxYValue = 0, _minYValue = 0;
private IRange _visibleRangeXLimit;
public IRange VisibleRangeXLimit
{
get { return _visibleRangeXLimit;}
set { _visibleRangeXLimit = value; NotifyOfPropertyChange(nameof(VisibleRangeXLimit)); }
}
private IRange _visibleRangeYLimit;
public IRange VisibleRangeYLimit
{
get { return _visibleRangeYLimit; }
set { _visibleRangeYLimit = value; NotifyOfPropertyChange(nameof(VisibleRangeYLimit)); }
}
private IRange _visibleRangeX;
public IRange VisibleRangeX
{
get { return _visibleRangeX; }
set { _visibleRangeX = value; NotifyOfPropertyChange(nameof(VisibleRangeX)); }
}
private IRange _visibleRangeY;
public IRange VisibleRangeY
{
get { return _visibleRangeY; }
set { _visibleRangeY = value; NotifyOfPropertyChange(nameof(VisibleRangeY)); }
}
private AutoRange _chartAutoRange = AutoRange.Never;
public AutoRange ChartAutoRange
{
get { return _chartAutoRange; }
set
{
_chartAutoRange = value;
NotifyOfPropertyChange(nameof(ChartAutoRange));
}
}
private Visibility _dataDetailVisbility = Visibility.Collapsed;
public Visibility DataDetailVisbility
{
get { return _dataDetailVisbility; }
set
{
_dataDetailVisbility = value;
NotifyOfPropertyChange(nameof(DataDetailVisbility));
}
}
private bool _showLegendInfo;
public bool ShowLegendInfo
{
get { return _showLegendInfo; }
set
{
_showLegendInfo = value;
NotifyOfPropertyChange(nameof(ShowLegendInfo));
}
}
private Queue<Color> colorQueue = new Queue<Color>(new Color[]{Color.Red,Color.Orange,Color.Green,Color.Blue,Color.Purple,Color.Aqua,Color.Bisque,Color.Brown,Color.BurlyWood,Color.CadetBlue,
Color.CornflowerBlue,Color.DarkBlue,Color.DarkCyan,Color.DarkGray,Color.DarkGreen,Color.DarkKhaki,Color.DarkMagenta,Color.DarkOliveGreen, Color.DarkOrange,
Color.DarkSeaGreen,Color.DarkSlateBlue,Color.DarkSlateGray,Color.DarkViolet,Color.DeepPink,Color.DeepSkyBlue,Color.DimGray, Color.DodgerBlue,Color.ForestGreen, Color.Gold,
Color.Gray,Color.GreenYellow,Color.HotPink,Color.Indigo,Color.Khaki,
Color.LimeGreen,Color.MediumOrchid,Color.MediumPurple,Color.MediumSeaGreen,Color.MediumSlateBlue,Color.MediumSpringGreen,
Color.MediumTurquoise,Color.Moccasin,Color.NavajoWhite,Color.Olive,Color.OliveDrab,Color.OrangeRed,Color.Orchid,Color.PaleGoldenrod,Color.PaleGreen,
Color.PeachPuff,Color.Peru,Color.Plum,Color.PowderBlue,Color.RosyBrown,Color.RoyalBlue,Color.SaddleBrown,Color.Salmon,Color.SeaGreen, Color.Sienna,
Color.SkyBlue,Color.SlateBlue,Color.SlateGray,Color.SpringGreen,Color.Teal,Color.Aquamarine,Color.Tomato,Color.Turquoise,Color.Violet,Color.Wheat, Color.YellowGreen});
#endregion Properties
#region
/// <summary>
/// 重新搜索Recipe
/// </summary>
public void SearchRecipe()
{
this.StartDateTime = this.view.wfTimeFrom.Value;
this.EndDateTime = this.view.wfTimeTo.Value;
if (StartDateTime > EndDateTime)
{
MessageBox.Show("Time range invalid, start time should be early than end time");
return;
}
Recipes.Clear();
RemoveAllLine();
try
{
string sql = $"SELECT * FROM \"process_data\" where \"process_data\".\"process_begin_time\" >='{StartDateTime:yyyyMMdd HHmmss}' and \"process_data\".\"process_end_time\" <='{EndDateTime:yyyyMMdd HHmmss}'";
if (!string.IsNullOrEmpty(SelectedValuePM))
{
string[] pms = SelectedValuePM.Split(',');
if (pms.Length > 0)
{
sql += " and (FALSE ";
foreach (var pm in pms)
{
sql += $" OR \"process_data\".\"process_in\"='{pm}' ";
}
sql += " ) ";
}
}
if (!string.IsNullOrEmpty(RecipeName))
{
sql += string.Format(" and lower( \"process_data\".\"recipe_name\") like '%{0}%'", RecipeName.ToLower());
}
sql += " order by \"process_data\".\"process_begin_time\" ASC;";
DataTable dbData = QueryDataClient.Instance.Service.QueryData(sql);
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
if (dbData == null || dbData.Rows.Count == 0)
return;
double maxXValue = 0;
for (int i = 0; i < dbData.Rows.Count; i++)
{
RecipeItemSic item = new RecipeItemSic();
item.Recipe = dbData.Rows[i]["recipe_name"].ToString();
item.Chamber = dbData.Rows[i]["process_in"].ToString();
item.Status = dbData.Rows[i]["process_status"].ToString();
//item.ProcessGuid = dbData.Rows[i]["guid"].ToString();
item.ProcessGuid = "Recipe"+i.ToString().PadLeft(3,'0');
if (!dbData.Rows[i]["process_begin_time"].Equals(DBNull.Value))
item.StartTime = ((DateTime)dbData.Rows[i]["process_begin_time"]).ToString("yyyy/MM/dd HH:mm:ss.fff");
if (!dbData.Rows[i]["process_end_time"].Equals(DBNull.Value))
item.EndTime = ((DateTime)dbData.Rows[i]["process_end_time"]).ToString("yyyy/MM/dd HH:mm:ss.fff");
long curTickCount = (DateTime.Parse(item.EndTime).Ticks - DateTime.Parse(item.StartTime).Ticks) / 10000000;
if (maxXValue < curTickCount)
{
maxXValue = curTickCount;
}
//正确设置范围,否则曲线不显示
VisibleRangeX = new DoubleRange(-0.2*maxXValue, maxXValue * 1.2);
VisibleRangeXLimit = new DoubleRange(-0.2 * maxXValue, maxXValue * 1.2);//X轴拉伸1.2倍
Recipes.Add(item);
}
}));
}
catch (Exception e)
{
LOG.Write(e);
}
}
/// <summary>
/// 选择Recipe
/// </summary>
public void CheckRecipe(RecipeItemSic recipe)
{
if (recipe.Selected) //选中配方
{
SelectedRecipes.Add(recipe);
//刷新曲线
//GetXRange();
foreach (PMNodeInfo node in ConfigNodes)
{
if (node.Selected)
{
ParameterCheck(node);
}
}
}
else //取消配方
{
RecipeItemSic item = SelectedRecipes.FirstOrDefault(t => t.Recipe == recipe.Recipe);
if (item != null)
SelectedRecipes.Remove(item);
RemoveRecipeRelated(recipe); //移除配方相关曲线
}
}
/// <summary>
/// 移除配方相关曲线
/// </summary>
/// <param name="recipe"></param>
private void RemoveRecipeRelated(RecipeItemSic recipe)
{
lock (_lockSelection) //递减法移除列表内项,不会删错
{
for (int i = SelectedData.Count - 1; i >= 0; i--)
{
if ((SelectedData[i] as ChartDataLineXV2).ProcessGuid.Contains(recipe.ProcessGuid))
{
SelectedData.RemoveAt(i);
}
}
}
}
/// <summary>
/// 清除所有选择的节点
/// </summary>
public void RemoveAllLine()
{
this.SelectedData.Clear();
foreach (PMNodeInfo node in ConfigNodes)
{
node.Selected = false;
}
}
#endregion
private void SelectedDataChanged()
{
foreach (var item in SelectedData)
{
if (item.Stroke.Equals(System.Windows.Media.Color.FromArgb(255, 0, 0, 255)))
{
Color drawingColor = colorQueue.Peek();
item.Stroke = System.Windows.Media.Color.FromRgb(drawingColor.R, drawingColor.G, drawingColor.B);
colorQueue.Enqueue(colorQueue.Dequeue());
}
}
}
/// <summary>
/// 加载PM的Device名称
/// </summary>
private void GetPMNode()
{
Nodes.Clear();
ConfigNodes.Clear();
List<string> dataList = (List<string>)QueryDataClient.Instance.Service.GetConfig("System.NumericDataList");
dataList.Sort();
List<string> lstNode = new List<string>();
foreach (string dataName in dataList)
{
string[] nodeName = dataName.Split('.');
if (nodeName.Length > 1 && nodeName[0].IndexOf("PM") == 0)
{
string nodeStr = dataName.Substring(dataName.IndexOf('.') + 1);
if (!lstNode.Contains(nodeStr)) //不显示重复项
{
lstNode.Add(nodeStr);
Nodes.Add(new PMNodeInfo() { NodeStr = nodeStr });
ConfigNodes.Add(new PMNodeInfo() { NodeStr = nodeStr });
}
}
}
}
public void Find(string keyword)
{
ConfigNodes.Clear();
//遍历所有node,隐藏不符合条件项
foreach (var node in Nodes)
{
node.ApplyCriteria(keyword);
if (node.IsMatch)
{
ConfigNodes.Add(node);
}
}
}
private void SetDataDetailVisble()
{
if (DataDetailVisbility == Visibility.Collapsed)
{
DataDetailVisbility = Visibility.Visible;
}
else
{
DataDetailVisbility = Visibility.Collapsed;
}
}
#region HistoryData
private object _lockSelection = new object();
protected bool MonitorHistoryData()
{
try
{
if (_lstTokenTimeData.Count > 0)
{
lock (_lockSelection)
{
foreach (var item in _lstTokenTimeData)
{
DateTime timeFrom = item.StartTime;
DateTime timeTo = item.EndTime;
GetHistoryData(item.DateList, timeFrom, timeTo, item.Module, item.ColumneName, item.ProcessGuid);
_lstTokenTimeData.TryTake(out _);
}
}
}
}
catch (Exception ex)
{
LOG.Error(ex.Message);
}
return true;
}
private void GetHistoryData(List<string> lstDayStr, DateTime from, DateTime to, string module, string colName, string processGuid)
{
Dictionary<string, List<DataDetail>> historyData = new Dictionary<string, List<DataDetail>>();
List<DataDetail> lstDataDetail = new List<DataDetail>();
//db获取对应字段数据
foreach (string cDate in lstDayStr)
{
string sql = String.Format("select time AS InternalTimeStamp,\"{0}\"", colName);
sql += string.Format(" from \"{0}\" where time > {1} and time <= {2} order by time asc",
cDate + "." + module, from.Ticks, to.Ticks);
DataTable dataTable = QueryDataClient.Instance.Service.QueryData(sql);
if (dataTable == null || dataTable.Rows.Count == 0 || dataTable.Columns.Count < 2)
{
return;
}
for (int i = 0; i < dataTable.Rows.Count; i++)
{
DataDetail data = new DataDetail();
long ticks = (long)dataTable.Rows[i][0] - from.Ticks;
data.xValue = ticks / 10000000;
if (dataTable.Rows[i][1] is DBNull || dataTable.Rows[i][1] is null)
{
data.yValue = 0;
}
else if (dataTable.Rows[i][1] is bool)
{
data.yValue = (bool)dataTable.Rows[i][1] ? 1 : 0;
}
else
{
data.yValue = float.Parse(dataTable.Rows[i][1].ToString());
}
if (data.yValue > _maxYValue)
{
_maxYValue = data.yValue;
}
if (data.yValue < _minYValue)
{
_minYValue = data.yValue;
}
lstDataDetail.Add(data);
}
}
//正确设置范围,否则曲线不显示
VisibleRangeY = new DoubleRange(_minYValue - _maxYValue * 0.5, _maxYValue * 2.1);
VisibleRangeYLimit = new DoubleRange(_minYValue - _maxYValue * 0.5, _maxYValue * 2.1);//Y轴拉伸1.2倍
//所有数据内存
historyData.Add(processGuid, lstDataDetail);
//数据绑定到曲线中
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
foreach (var item in SelectedData)
{
var seriesItem = item as ChartDataLineXV2;
if (seriesItem == null)
continue;
foreach (var data in historyData)
{
if (data.Key != seriesItem.ProcessGuid)
continue;
seriesItem.Capacity += data.Value.Count;
foreach (var detailDataItem in data.Value)
{
seriesItem.Append(detailDataItem.xValue, detailDataItem.yValue);
}
}
}
}
catch (Exception ex)
{
LOG.Write(ex);
}
}));
}
#endregion
/// <summary>
/// 更新Data变化
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public bool ParameterCheck(PMNodeInfo node)
{
if (SelectedRecipes.Count == 0)
{
return false;
}
if (node.Selected)
{
foreach (RecipeItemSic itemSic in SelectedRecipes)
{
string nodeStr = itemSic.Chamber + "." + node.NodeStr; //字段名称
string nodeStr_1 = itemSic.LotID; //显示名称
string nodeWithGuid = node.NodeStr + "_" + itemSic.ProcessGuid; //字段名+ Guid (唯一ID)
DateTime dtStartTime = DateTime.Parse(itemSic.StartTime);
DateTime dtEndTime = DateTime.Parse(itemSic.EndTime);
lock (_lockSelection)
{
bool isExist = SelectedData.FirstOrDefault(x => (x as ChartDataLineXV2).ProcessGuid == nodeWithGuid) != null;
if (!isExist)
{
if (SelectedData.Count < MAX_PARAMETERS)
{
var line = new ChartDataLineXV2(nodeWithGuid);
line.Time=itemSic.StartTime+"-"+itemSic.EndTime;
line.RecipeName = new DirectoryInfo(itemSic.Recipe).Name;
if (itemSic.Chamber.StartsWith("PM1"))
line.Module = "PM1";
else if (itemSic.Chamber.StartsWith("PM2"))
line.Module = "PM2";
else
line.Module = "System";
line.ProcessGuid = nodeWithGuid;
line.RecName = nodeStr_1;
line.Tag = node;
SelectedData.Add(line);
SelectedDataChanged();
QueryIndexer indexer = _lstTokenTimeData.FirstOrDefault(x => x.ProcessGuid == line.ProcessGuid);
if (indexer == null)
{
indexer = new QueryIndexer()
{
Module = line.Module,
StartTime = dtStartTime,
EndTime = dtEndTime,
ProcessGuid = line.ProcessGuid,
ColumneName = nodeStr,
DateList = GetDateList(dtStartTime, dtEndTime)
};
_lstTokenTimeData.Add(indexer);
}
}
else
{
return false;
}
}
}
}
}
else
{
lock (_lockSelection)
{
for (int i = SelectedData.Count - 1; i >= 0; i--)
{
if ((SelectedData[i] as ChartDataLineXV2).DataName.Contains(node.NodeStr))
{
SelectedData.RemoveAt(i);
}
}
}
}
return true;
}
public void SelectColor(ChartDataLineXV2 cp)
{
if (cp == null)
return;
var dlg = new System.Windows.Forms.ColorDialog();
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
cp.Stroke = new System.Windows.Media.Color() { A = dlg.Color.A, B = dlg.Color.B, G = dlg.Color.G, R = dlg.Color.R };
}
}
/// <summary>
/// 获取开始时间和结束时间之间的所有日期
/// </summary>
/// <param name="timeStart"></param>
/// <param name="timeEnd"></param>
/// <returns></returns>
private List<string> GetDateList(DateTime timeStart, DateTime timeEnd)
{
List<string> lstTime = new List<string>();
string dtEndStr = timeEnd.ToString("yyyyMMdd");
int timeSpan = (int)(timeEnd - timeStart).TotalDays + 1;
for (int i = 0; i <= timeSpan; i++)
{
string dt = timeStart.AddDays(i).ToString("yyyyMMdd");
lstTime.Add(dt);
if (dt == dtEndStr)
{
break;
}
}
return lstTime;
}
/// <summary>
/// 获取所有的列名
/// </summary>
private List<string> GetColumnList(string modleName)
{
List<string> _seletedItemName = new List<string>();
foreach (PMNodeInfo node in ConfigNodes)
{
if (node.Selected)
{
_seletedItemName.Add("\"" + modleName + "." + node.NodeStr + "\"");
}
}
return _seletedItemName;
}
public ActionCommand DefaultZoomCommand { get; private set; }
public ActionCommand ShowLegendCommand { get; private set; }
public ActionCommand DataDetailVisbleCommand { get; private set; }
public ActionCommand ShowAlarmCommand { get; private set; }
private void SetLegendAvalible()
{
ShowLegendInfo = !ShowLegendInfo;
}
private void Zoom()
{
ViewportManager.AnimateZoomExtents(TimeSpan.FromMilliseconds(500));
}
}
}