ScanGraphVm.cs 12 KB
using FLY.Thick.FilmCasting.Common;
using FLY.Thick.FilmCasting.IService;
using FLY.Thick.FilmCasting.Server;
using FLY.Thick.FilmCasting.Server.Model;
using FObjBase;
using LiveCharts;
using LiveCharts.Configurations;
using LiveCharts.Wpf;
using Misc;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Threading;

namespace FLY.Thick.FilmCasting.UI.Server
{
    public class ScanGraphVm : INotifyPropertyChanged
    {

        #region 图表控制
        public double YMax { get; set; } = double.NaN;
        public double YMin { get; set; } = double.NaN;
        public double Target { get; set; } = double.NaN;
        public double ToleranceYMax { get; set; } = double.NaN;
        public double ToleranceYMin { get; set; } = double.NaN;
        public double Tolerance2YMax { get; set; } = double.NaN;
        public double Tolerance2YMin { get; set; } = double.NaN;
        public double XMax { get; set; } = double.NaN;
        public double XMin { get; set; } = double.NaN;

        public int XStep { get; set; } = 10;

        public Func<double, string> YFormatter { get; set; }
        public Func<double, string> XFormatter { get; set; }
        #endregion



        #region 界面统计值
        /// <summary>
        /// 平均值
        /// </summary>
        public double Average { get; protected set; } = double.NaN;

        public double Sigma2 { get; protected set; } = double.NaN;
        /// <summary>
        /// 最大值
        /// </summary>
        public double Max { get; protected set; } = double.NaN;
        /// <summary>
        /// 最小值
        /// </summary>
        public double Min { get; protected set; } = double.NaN;

        public string Sigma2Text { get; protected set; }
        public string MaxText { get; protected set; }
        public string MinText { get; protected set; }

        /// <summary>
        /// 混合数
        /// </summary>
        public int Mix { get; protected set; } = 2;

        /// <summary>
        /// 测量时间
        /// </summary>
        public DateTime Time { get; protected set; }

        /// <summary>
        /// 测量时间
        /// </summary>
        public DateTime EndTime { get; protected set; }
        /// <summary>
        /// 数据库中的序号
        /// </summary>
        public long Id { get; protected set; }

        public double Velocity { get; protected set; }
        public double Position { get; protected set; }

        /// <summary>
        /// 膜宽 mm
        /// </summary>
        public double FilmWidth { get; protected set; }

        /// <summary>
        /// 标题
        /// </summary>
        public string Title { get; protected set; }

        public bool IsWarning { get; protected set; }
        #endregion

        protected List<Brush> AreaColors;

        //最大只能显示100个数据,大于100, 都会被/2
        public ChartValues<double> Values { get; } = new ChartValues<double>();

        //double 转 XY
        public object Mapper { get; protected set; }

        protected FilmCastingProfileParam profileParam;
        protected PicHistory picHistory;
        protected Lc_ScanData scanData;
        
        public ScanGraphVm()
        {
            #region 与数据无关界面参数

            AreaColors = Themes.Colors.AreaColors;

            YFormatter = (y) =>
            {
                //只显示 Target,ToleranceYMax,ToleranceYMin,Tolerance2YMax,Tolerance2YMin
                string text = "";
                if (picHistory.IsPercent)
                {
                    double target = Target;
                    if (target > 0)
                    {
                        if (y == target)
                        {
                            text = y.ToString("F1");
                        }
                        else
                        {
                            double percent = 100.0 * (y - target) / target;
                            if (percent > 0)
                            {
                                text = $"+{Math.Abs(percent):F1}%";
                            }
                            else
                            {
                                text = $"-{Math.Abs(percent):F1}%";
                            }
                        }
                    }
                    else
                    {
                        text = y.ToString("F1");
                    }
                }
                else
                {
                    text = y.ToString("F1");
                }
                return $"{text,6}";
            };

            XFormatter = (x) =>
            {
                return $"{x + 1}";
            };

            Mapper = Mappers.Xy<double>()
                .X((value, index) =>
                {
                    return index;
                })
                .Y(value => value)
                .Fill((value) =>
                {
                    if ((value > Tolerance2YMax) || (value < Tolerance2YMin))
                    {
                        return AreaColors[2];
                    }
                    else if ((value > ToleranceYMax) || (value < ToleranceYMin))
                    {
                        return AreaColors[1];
                    }
                    else
                    {
                        return AreaColors[0];
                    }
                });
            #endregion

            //Series = new SeriesCollection
            //{
            //    new Column2Series
            //    {
            //        Values = Values,
            //        Stroke = new SolidColorBrush(Colors.Black),
            //        StrokeThickness = 1,
            //        PointGeometry = null,
            //        Configuration = Mapper
            //    }
            //};
            //((Series)Series[0]).SetBinding(Column2Series.YAxisCrossingProperty, new Binding(nameof(Target)) { Source = this });
        }

        public void Init(
            PicHistory picHistory,
            FilmCastingProfileParam profileParam
            )
        {
            this.picHistory = picHistory;
            this.profileParam = profileParam;
           
            UpdateTitle();
            UpdateY();
            UpdateX();

            Values.Clear();

            Id = -1;

            this.picHistory.PropertyChanged += Graphparam_PropertyChanged;
            this.PropertyChanged += ScanGraphVm_PropertyChanged;
            this.profileParam.PropertyChanged += ProfileParam_PropertyChanged;

        }

        private void ProfileParam_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if ((e.PropertyName == nameof(profileParam.PName))
                || (e.PropertyName == nameof(profileParam.Batch))
                || (e.PropertyName == nameof(profileParam.Number)))
            {
                UpdateTitle();
            }
        }



        private void ScanGraphVm_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(Target))
            {
                if (picHistory.IsPercent)
                {
                    NotifyPropertyChanged(nameof(YFormatter));
                }
            }
        }

        private void Graphparam_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(picHistory.YRangePercent))
            {
                UpdateY();
            }
            else if (e.PropertyName == nameof(picHistory.IsPercent))
            {
                NotifyPropertyChanged(nameof(YFormatter));
                UpdateY();
                UpdateMaxMinText();
            }
        }

        protected void UpdateTitle() 
        {
            Title = $"{profileParam.PName}-{profileParam.Batch}-{profileParam.Number}";
        }

        protected void UpdateY()
        {
            //使用 profileParam 的参数显示
            UpdateY_profileParam();
        }
        void UpdateY_profileParam()
        {
            double target = this.profileParam.Target;

            double tolerance = this.profileParam.TolerancePercent * target;

            Target = target;
            ToleranceYMax = target + tolerance;
            ToleranceYMin = target - tolerance;
            Tolerance2YMax = target + tolerance * 2;
            Tolerance2YMin = target - tolerance * 2;

            YMax = target + tolerance * picHistory.YRangePercent;
            YMin = target - tolerance * picHistory.YRangePercent;
        }

        protected void UpdateX()
        {
            if (profileParam.ScanRange.IsValid)
            {
                XMin = profileParam.ScanRange.Begin - 1;
                XMax = profileParam.ScanRange.End + 1;
            }
            XStep = picHistory.XStep;
        }



        void UpdateMaxMinText()
        {
            if (picHistory.IsPercent && (Average > 0))
            {
                MaxText = $"+{((Max - Average) / Average) * 100.0:F1}%";
                MinText = $"-{((Average - Min) / Average) * 100.0:F1}%";
                Sigma2Text = $"{(Sigma2 / Average) * 100.0:F1}%";
            }
            else
            {
                MaxText = $"{Max:F1}";
                MinText = $"{Min:F1}";
                Sigma2Text = $"{Sigma2:F1}";
            }
        }

        protected void UpdateAverage()
        {
            if (this.scanData == null)
            {
                Time = DateTime.MinValue;
                Id = -1;
                Position = -1;
                Velocity = -1;
                FilmWidth = -1;
                return;
            }
            else
            {
                Time = this.scanData.Time;
                EndTime = this.scanData.EndTime;
                Id = this.scanData.ID;
                Position = this.scanData.FilmPosition;
                Velocity = this.scanData.FilmVelocity;
                FilmWidth = this.scanData.FilmWidth;
            }


            List<double> list = new List<double>();
            double[] values = this.scanData.Thicks;
            if (this.profileParam.DataRange.IsValid) {

                int beginIndex = this.profileParam.DataRange.Begin;
                int endIndex = this.profileParam.DataRange.End;

                for (int i = beginIndex; i <= endIndex; i++)
                {
                    if (i < 0)
                        i = 0;
                    if (i >= values.Count())
                        break;
                    double v = values[i];
                    if (!double.IsNaN(v))
                        list.Add(values[i]);
                }
            }

            if (list.Count() > 0)
            {
                Average = list.Average();
                Max = list.Max();
                Min = list.Min();
                Sigma2 = list.Sigma() * 2;
            }
            else
            {
                Average = double.NaN;
                Max = double.NaN;
                Min = double.NaN;
                Sigma2 = double.NaN;
            }

            UpdateMaxMinText();
        }

        public void UpdateValue(Pack_GetFrameReponse reponse, bool isWarning) 
        {
            this.scanData = reponse.scanData;
            Mix = reponse.Request.Mix;
            this.IsWarning = isWarning;
            //清除全部数据
            Values.Clear();

            //更新平均值,它会引起 YFormatter 刷新
            UpdateAverage();

            //更新Target
            UpdateY();

            UpdateX();

            //必须先更新Y轴, X轴资料,最后再画线!!!!!!!
            //不然 YAxisCrossing 会有机会无效

            if (scanData != null && scanData.Thicks != null)
            {
                Values.AddRange(scanData.Thicks);
            }
        }

        protected void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}