using FLY.Integrated.Common; using FLY.Integrated.IService; using FLY.Integrated.Server.Model; using FLY.OBJComponents.Client; using FLY.OBJComponents.IService; using GalaSoft.MvvmLight.Command; using LiveCharts; using LiveCharts.Configurations; using LiveCharts.Wpf; using MultiLayout; using MultiLayout.UiModule; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; 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; using Unity; namespace FLY.Integrated.UI.Client.UiModule { /// <summary> /// IBCCtrlGraph.xaml 的交互逻辑 /// </summary> public partial class IbcCtrlGraph : UserControl { IbcCtrlGraphVm viewModel; public IbcCtrlGraph() { InitializeComponent(); InitialzeChart(); } void InitialzeChart() { chart.AxisY[0].SetBinding(Axis.MaxValueProperty, new Binding("D_MaxValue")); chart.AxisY[0].SetBinding(Axis.MinValueProperty, new Binding("D_MinValue")); chart.AxisY[0].SetBinding(Axis.LabelFormatterProperty, new Binding("YFormatter")); chart.AxisY[1].SetBinding(Axis.MaxValueProperty, new Binding("FilmWidth_MaxValue")); chart.AxisY[1].SetBinding(Axis.MinValueProperty, new Binding("FilmWidth_MinValue")); chart.AxisY[1].SetBinding(Axis.LabelFormatterProperty, new Binding("YFormatter")); chart.AxisX[0].SetBinding(Axis.LabelFormatterProperty, new Binding("DateTimeFormatter")); } [InjectionMethod] public void Init(IUnityContainer container, IIntegratedSystemService integratedSystemService ) { viewModel = new IbcCtrlGraphVm(); container.BuildUp(viewModel); this.DataContext = viewModel; } } public class IbcCtrlGraphVm : INotifyPropertyChanged { const int MARKNO_TOVALUES = 1; /// <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<Db_Width> Values { get; set; } /// <summary> /// 折径曲线 XY 定义 /// </summary> public CartesianMapper<Db_Width> Mapper_FilmWidth { get; set; } /// <summary> /// 进风-出风曲线 XY 定义 /// </summary> public CartesianMapper<Db_Width> 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 #region Command public RelayCommand NewestCmd { get; } public RelayCommand NextCmd { get; } public RelayCommand PreCmd { get; } public RelayCommand InfoSetCmd { get; } #endregion /// <summary> /// 当前现在不是最新,而且没有移动动作,20秒自动按 Newest() /// </summary> DispatcherTimer timer; public event PropertyChangedEventHandler PropertyChanged; IIntegratedSystemService integratedSystemService; IBuffer<Db_Width> ctrlList; public BufferWindow<Db_Width> DbWidthWindow { get; private set; } IbcCtrlGraphParams graphParams; public IbcCtrlGraphVm() { NewestCmd = new RelayCommand(() => { DbWidthWindow.MoveNewest(); }); NextCmd = new RelayCommand(() => { DbWidthWindow.MoveNextPage(); }); PreCmd = new RelayCommand(() => { DbWidthWindow.MovePrePage(); }); InfoSetCmd = new RelayCommand(InfoSet); Mapper_FilmWidth = Mappers.Xy<Db_Width>() .X(value => value.Time.Ticks) .Y(value => value.FilmWidth); Mapper_D = Mappers.Xy<Db_Width>() .X(value => value.Time.Ticks) .Y(value => value.InletAirFreq - value.OutletAirFreq); DateTimeFormatter = value => new DateTime((long)value).ToString("mm:ss"); YFormatter = value => value.ToString("F0"); Values = new ChartValues<Db_Width>(); } public void Init( IIntegratedSystemService integratedSystemClient, IBuffer<Db_Width> ctrlList ) { this.integratedSystemService = integratedSystemClient; this.graphParams = IbcCtrlGraphParams.Current; this.ctrlList = ctrlList; DbWidthWindow = new BufferWindow<Db_Width>(ctrlList, graphParams.WindowSize); Misc.BindingOperations.SetBinding(graphParams, "WindowSize", DbWidthWindow, "Size"); DbWidthWindow.Record.CollectionChanged += (object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) => { FObjBase.PollModule.Current.Poll_JustOnce( () => { ToValues(); }, this, MARKNO_TOVALUES); }; //TODO 注册更新 Misc.BindingOperations.SetBinding(integratedSystemClient.Ibc, "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; DbWidthWindow.PropertyChanged += BufferWindow_PropertyChanged; } void ToValues() { if (DbWidthWindow.Record.Count <= ValuesCap) { Values.Clear(); Values.AddRange(DbWidthWindow.Record); } else { Values.Clear(); List<Db_Width> record = new List<Db_Width>(); for (int i = 0; i < ValuesCap; i++) { int idx = i * DbWidthWindow.Record.Count / ValuesCap; record.Add(DbWidthWindow.Record[idx]); } Values.AddRange(record); } if (Values.Count() > 0) { BeginTime = Values.First().Time; EndTime = Values.Last().Time; } else { BeginTime = DateTime.MinValue; EndTime = DateTime.MinValue; } } private void Timer_Tick(object sender, EventArgs e) { AutoKeepNewestTimeRemaining--; if (AutoKeepNewestTimeRemaining <= 0) { AutoKeepNewestTimeRemaining = AutoKeepNewestTimeCounter; DbWidthWindow.MoveNewest(); } } private void BufferWindow_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "IsKeepNewest") { if (!DbWidthWindow.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<Db_Width>(); 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 Db_Width() { Time = date.AddSeconds(1 * i), FilmWidth = (float)filmwidth, InletAirFreq = (float)inletfreq, OutletAirFreq = (float)outletfreq }); } FilmWidthAxisYTarget = target_filmwidth; } private void InfoSet() { WdGraphSet w = new WdGraphSet(); w.Init(graphParams); w.Owner = Application.Current.MainWindow; if (w.ShowDialog() == true) { graphParams.Save(); } } } public class IbcCtrlGraphParams : INotifyPropertyChanged { static IbcCtrlGraphParams current = null; public static IbcCtrlGraphParams Current { get { if (current == null) { current = new IbcCtrlGraphParams(); current.Load(); } return current; } } 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; string FilePath; public IbcCtrlGraphParams() { string path = System.IO.Path.Combine(FlyLayout.BasePath, "ibcCtrlGraph.json"); FilePath = path; } /// <summary> /// 保存 /// </summary> /// <returns></returns> public bool Save() { try { string json = Newtonsoft.Json.JsonConvert.SerializeObject(this); File.WriteAllText(FilePath, json); } catch { return false; } return true; } /// <summary> /// 读取 /// </summary> /// <returns></returns> bool Load() { try { string json = File.ReadAllText(FilePath); Newtonsoft.Json.JsonConvert.PopulateObject(json, this); return true; } catch { return false; } } } public class UiModule2_IbcCtrlGraph : IUiModule2 { /// <summary> /// 控件标题 /// 它的值取决于culture /// </summary> public string Title => "综合.IBC控制流程图"; public ComponentType Type => ComponentType.Graph; public bool IsUnique => true; /// <summary> /// 控件 /// 创建时,需要给它唯一ID,让加载自己的数据 /// </summary> /// <param name="id"></param> /// <returns></returns> public FrameworkElement GetComponent(int id, IUnityContainer container) { IbcCtrlGraph graph = new IbcCtrlGraph(); container.BuildUp(graph); return graph; } /// <summary> /// 控件缩略图,用于编辑界面时,大致看看 /// 创建时,需要给它唯一ID,让加载自己的数据 /// </summary> /// <param name="id"></param> /// <returns></returns> public FrameworkElement GetThumbnail() { return new System.Windows.Controls.Grid(); } /// <summary> /// 给出全部控件ID, 控件自行删除没有的参数 /// </summary> /// <param name="IDs"></param> public void MatchParam(int[] IDs) { } } }