using FLY.IntegratedControl.Common; using FLY.OBJComponents.Client; using LiveCharts; using LiveCharts.Configurations; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Threading; namespace FLY.IntegratedControl.UI.Client.UIModule { /// <summary> /// IBCCtrlGraph.xaml 的交互逻辑 /// </summary> public partial class IBCCtrlGraph : UserControl { IBCCtrlGraphModelView iBCCtrlGraphModelView; public IBCCtrlGraph() { InitializeComponent(); } public void Init() { iBCCtrlGraphModelView = new IBCCtrlGraphModelView(); iBCCtrlGraphModelView.InitData(); grid_modelview.DataContext = iBCCtrlGraphModelView; grid_bufferwindow.DataContext = iBCCtrlGraphModelView.bufferWindow; } private void button_info_Click(object sender, RoutedEventArgs e) { Window_GraphSet w = new Window_GraphSet(); w.Owner = FLY.ControlLibrary.COMMON.GetWindow(this); w.ShowDialog(); } private void UserControl_Loaded(object sender, RoutedEventArgs e) { } private void button_newest_Click(object sender, RoutedEventArgs e) { iBCCtrlGraphModelView.Newest(); } private void button_pre_Click(object sender, RoutedEventArgs e) { iBCCtrlGraphModelView.Pre(); } private void button_next_Click(object sender, RoutedEventArgs e) { iBCCtrlGraphModelView.Next(); } } public class IBCCtrlGraphModelView : INotifyPropertyChanged { const int MARKNO_TOVALUES = 1; /// <summary> /// 当前现在不是最新,而且没有移动动作,20秒自动按 Newest() /// </summary> DispatcherTimer timer; public event PropertyChangedEventHandler PropertyChanged; FLY.IntegratedControl.Client.ICSystemClient iCSystemClient; public FLY.OBJComponents.Client.BufferWindow<FlyData_IBCCtrl> bufferWindow; public IBCCtrlGraphParams graphParams; /// <summary> /// 画面只显示200个数据, 就算设置画面设置显示1000个数,也会整合为200个数据 /// </summary> const int ValuesCap = 200; /// <summary> /// 自动按保持最新值,总时间 /// </summary> const int AutoKeepNewestTimeCounter = 10; #region property /// <summary> ///自动按保持最新值,剩余时间 /// </summary> public int AutoKeepNewestTimeRemaining { get; set; } /// <summary> /// 曲线, 是有 bufferWindow.Record 生成的 /// Values 最大数量只有 ValuesCap /// 平均从 bufferWindow.Record提取 /// </summary> public ChartValues<FlyData_IBCCtrl> Values { get; set; } /// <summary> /// 折径曲线 XY 定义 /// </summary> public CartesianMapper<FlyData_IBCCtrl> Mapper_FilmWidth { get; set; } /// <summary> /// 进风-出风曲线 XY 定义 /// </summary> public CartesianMapper<FlyData_IBCCtrl> Mapper_D { get; set; } /// <summary> /// X轴格式 /// </summary> public Func<double, string> DateTimeFormatter { get; set; } /// <summary> /// y轴格式 /// </summary> public Func<double, string> YFormatter { get; set; } /// <summary> /// 折径目标值 /// </summary> public double FilmWidthAxisYTarget { get; set; } /// <summary> /// 折径 最大值 /// </summary> public double FilmWidth_MaxValue { get; set; } /// <summary> /// 折径 最小值 /// </summary> public double FilmWidth_MinValue { get; set; } /// <summary> /// 进风-出风 最大值 /// </summary> public double D_MaxValue { get; set; } /// <summary> /// 进风-出风 最小值 /// </summary> public double D_MinValue { get; set; } /// <summary> /// 开始时间 /// </summary> public DateTime BeginTime { get; set; } /// <summary> /// 结束数据 /// </summary> public DateTime EndTime { get; set; } #endregion public IBCCtrlGraphModelView() { Init(); } void ToValues() { if (bufferWindow.Record.Count <= ValuesCap) { Values.Clear(); Values.AddRange(bufferWindow.Record); } else { Values.Clear(); List<FlyData_IBCCtrl> record = new List<FlyData_IBCCtrl>(); for (int i = 0; i < ValuesCap; i++) { int idx = i * bufferWindow.Record.Count / ValuesCap; record.Add(bufferWindow.Record[idx]); } Values.AddRange(record); } if (Values.Count() > 0) { BeginTime = Values.First().Time; EndTime = Values.Last().Time; } else { BeginTime = DateTime.MinValue; EndTime = DateTime.MinValue; } } void Init() { Mapper_FilmWidth = Mappers.Xy<FlyData_IBCCtrl>() .X(value => value.Time.Ticks) .Y(value => value.FilmWidth); Mapper_D = Mappers.Xy<FlyData_IBCCtrl>() .X(value => value.Time.Ticks) .Y(value => value.DFreq); DateTimeFormatter = value => new DateTime((long)value).ToString("mm:ss"); YFormatter = value => value.ToString("F0"); Values = new ChartValues<FlyData_IBCCtrl>(); } public void InitData() { graphParams = SysParam.Current.GraphParams; iCSystemClient = TDGage.Current.mICSystemService; bufferWindow = new BufferWindow<FlyData_IBCCtrl>(iCSystemClient.CtrlList, graphParams.WindowSize); Misc.BindingOperations.SetBinding(graphParams, "WindowSize", bufferWindow, "Size"); bufferWindow.Record.CollectionChanged += (object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) => { FObjBase.PollModule.Current.Poll_JustOnce( () => { ToValues(); }, this, MARKNO_TOVALUES); }; Misc.BindingOperations.SetBinding(iCSystemClient.Item, "FilmWidthSet", this, "FilmWidthAxisYTarget"); Update_filmwidth_y(); Update_d_y(); this.PropertyChanged += (s, e) => { if (e.PropertyName == "FilmWidthAxisYTarget") Update_filmwidth_y(); }; graphParams.PropertyChanged += (s, e) => { if (e.PropertyName == "IsAxisYRangeAuto") { Update_filmwidth_y(); Update_d_y(); } else if (e.PropertyName == "FilmWidthAxisYRange") { Update_filmwidth_y(); } else if ( (e.PropertyName == "DAxisYTarget") || (e.PropertyName == "DAxisYRange")) { Update_d_y(); } }; //当不是最新数据时,10秒内无操作,自动按 [最新] timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromSeconds(1); timer.Tick += Timer_Tick; bufferWindow.PropertyChanged += BufferWindow_PropertyChanged; } private void Timer_Tick(object sender, EventArgs e) { AutoKeepNewestTimeRemaining--; if (AutoKeepNewestTimeRemaining <= 0) { AutoKeepNewestTimeRemaining = AutoKeepNewestTimeCounter; bufferWindow.MoveNewest(); } } private void BufferWindow_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "IsKeepNewest") { if (!bufferWindow.IsKeepNewest) { //当前不是最新,开始倒计时 AutoKeepNewestTimeRemaining = AutoKeepNewestTimeCounter; timer.Start(); } else { timer.Stop(); } } } void Update_filmwidth_y() { if (graphParams.IsAxisYRangeAuto) { FilmWidth_MaxValue = double.NaN; FilmWidth_MinValue = double.NaN; } else { FilmWidth_MaxValue = FilmWidthAxisYTarget + graphParams.FilmWidthAxisYRange; FilmWidth_MinValue = FilmWidthAxisYTarget - graphParams.FilmWidthAxisYRange; } } void Update_d_y() { if (graphParams.IsAxisYRangeAuto) { D_MaxValue = double.NaN; D_MinValue = double.NaN; } else { D_MaxValue = graphParams.DAxisYTarget + graphParams.DAxisYRange; D_MinValue = graphParams.DAxisYTarget - graphParams.DAxisYRange; } } void Debug() { Values = new ChartValues<FlyData_IBCCtrl>(); double target_filmwidth = 1000; double filmwidth = 800; double inletfreq = 30; double outletfreq = 30; double pid_p = 0.01; double p_f = 8; DateTime date = DateTime.Now; Random r = new Random(); double noise = 2; double last_filmwidth = filmwidth; for (int i = 0; i < 1000; i++) { double e = target_filmwidth - filmwidth; last_filmwidth = filmwidth; double e_freq = e * pid_p; inletfreq = e_freq + outletfreq; e_freq = inletfreq - outletfreq; filmwidth += e_freq * p_f; filmwidth += (r.NextDouble() * noise - noise / 2); Values.Add(new FlyData_IBCCtrl() { Time = date.AddSeconds(1 * i), FilmWidth = (float)filmwidth, InletAirFreq = (float)inletfreq, OutletAirFreq = (float)outletfreq }); } FilmWidthAxisYTarget = target_filmwidth; } public void Newest() { bufferWindow.MoveNewest(); } public void Next() { bufferWindow.MoveNextPage(); } public void Pre() { bufferWindow.MovePrePage(); } } public class IBCCtrlGraphParams : INotifyPropertyChanged, Misc.ISaveToXml { public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// 窗口显示数据总量 /// </summary> public int WindowSize { get; set; } = 1000; /// <summary> /// 折径,进风-出风, Y轴 自动设置 MaxValue, MinValue, /// </summary> public bool IsAxisYRangeAuto { get; set; } /// <summary> /// 折径,Y轴显示范围, 中间值一定是 FilmWidthSet, 单位mm /// MaxValue = FilmWidthSet+FilmWidthAxisYRange /// MinValue = FilmWidthSet-FilmWidthAxisYRange /// </summary> public int FilmWidthAxisYRange { get; set; } = 20; /// <summary> /// 进风-出风 Y轴显示范围 单位Hz /// </summary> public int DAxisYRange { get; set; } = 10; /// <summary> /// 进风-出风 Y轴 中间显示值 单位Hz /// </summary> public int DAxisYTarget { get; set; } = 0; public string[] GetSavePropertyNames() { return new string[]{ "WindowSize", "FilmWidthAxisYRange", "IsAxisYRangeAuto", "DAxisYRange", "DAxisYTarget", }; } } }