using Arction.Wpf.Charting; using Arction.Wpf.Charting.Axes; using Arction.Wpf.Charting.SeriesXY; using Arction.Wpf.Charting.Views.ViewXY; using Microsoft.Win32; using mseedChart.Core; using mseedChart.MainModule.Models; using Prism.Commands; using Prism.Events; using Prism.Mvvm; using SharpDX.Direct2D1; using SharpDX.DirectWrite; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http.Headers; using System.Reflection; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Markup; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; using System.Xml.Linq; namespace mseedChart.MainModule.ViewModels { public class ChartPlotViewModel : BindableBase { public ChartPlotViewModel() { _dispatcher = Application.Current.Dispatcher; _wavesModel = new WavesModel(); (Application.Current.MainWindow as System.Windows.Window).Closing += ApplicationClosingDispose; CreateChart(); } #region 字段 Dispatcher _dispatcher; public int CurPoints; int _channelCount = 0; int _samplingFrequency = 500; // 采样频率 (Hz). WavesModel _wavesModel; private int _lChartCount = 1; public ConcurrentQueue smList; public List _chartAxisY=new List(); #endregion #region 属性 private bool _isMultFiles; public bool IsMultFiles { get { return _isMultFiles; } set { SetProperty(ref _isMultFiles, value); } } private int _xaisInterval; public int XaisInterval { get { return _xaisInterval; } set { SetProperty(ref _xaisInterval, value); } } /// /// 波形控件数量 /// public int LChartCount { get { return _lChartCount; } set { _lChartCount = value; } } private LightningChart _lChartAll; /// /// 所有波形 /// public LightningChart LChartALL { get { return _lChartAll; } set { _lChartAll = value; } } private FrameworkElement childContent; public FrameworkElement ChildContent { get { return childContent; } set { childContent = value; } } private Grid gridChart; public Grid GridChart { get { return gridChart; } set { gridChart = value; } } private StationEventJson _currentEventTime; public StationEventJson CurrentEventTime { get { return _currentEventTime; } set { _currentEventTime = value; } } private List _stationsName; public List StationsName { get { return _stationsName; } set { SetProperty(ref _stationsName, value); } } private string _currentTime; public string CurrentTime { get { return _currentTime; } set { SetProperty(ref _currentTime, value); } } #endregion #region 事件 public DelegateCommand LoadedCommand => new DelegateCommand(Loaded); public DelegateCommand AxesYVisibleCommand => new DelegateCommand(AxesYVisible); public DelegateCommand FileSelectorCommand => new DelegateCommand(FileSelector); public DelegateCommand OtimeSortCommand => new DelegateCommand(OtimeSort); public DelegateCommand YasixZENVisibleCommand => new DelegateCommand(YasixZENVisible); public DelegateCommand IntervalSureCommand => new DelegateCommand(IntervalSure); public DelegateCommand RealTimeDataCommand => new DelegateCommand(RealTimeData); private void Loaded() { } private void AxesYVisible(object isCheck) { if (_lChartAll != null) { bool yAxesVisible = ((bool)isCheck == true); _lChartAll.BeginUpdate(); foreach (AxisY yAxis in _lChartAll.ViewXY.YAxes) { yAxis.Visible = yAxesVisible; } _lChartAll.EndUpdate(); } } private void FileSelector() { OpenFileDialog openFileDialog = new OpenFileDialog { RestoreDirectory = true, Filter = ".mseed|*.mseed|.json|*.json|.txt|*.txt", }; if (openFileDialog.ShowDialog() == true) { string asciiSavePath = openFileDialog.FileName.Replace("Mseed", "Txt"); asciiSavePath = Path.ChangeExtension(asciiSavePath, ".txt"); string JsonPath = Path.ChangeExtension(openFileDialog.FileName, "Json"); Stopwatch st = new Stopwatch(); st.Start(); //读取.json文件 CurrentEventTime = _wavesModel.ReadChartJsonFile(JsonPath); //读取.mseed文件 smList = _wavesModel.ReadMseedFile(openFileDialog.FileName, asciiSavePath, IsMultFiles); _channelCount = smList.Count * 3; CurrentTime = smList.First().BeginTime.ToShortDateString(); StationsName = smList.Select(a => new StationAxis{Name= a.Name,IsChecked=true,SelectCommand =new DelegateCommand(StationsNameVisible)}).ToList(); StartChart(); st.Stop(); Debug.WriteLine("统计时间StartChart************:{0}", st.Elapsed); FeedDatasToChart(); } } private void OtimeSort(object isCheck) { if (_lChartAll != null) { LChartALL.BeginUpdate(); bool yAxesVisible = ((bool)isCheck == true); ViewXY v = _lChartAll.ViewXY; IOrderedEnumerable lines; if (yAxesVisible) { lines = v.LineCollections.Where(a => a.Tag != null && a.Tag.ToString().Contains("SHZ")).OrderBy(a => a.Lines[0].AX); } else { lines = v.LineCollections.Where(a => a.Tag != null && a.Tag.ToString().Contains("SHZ")).OrderByDescending(b => b.Lines[0].AX); ; } int number = v.YAxes.Count / (_chartAxisY.Count / 3); foreach (var item in lines.Reverse()) { int index = item.AssignYAxisIndex; for (int i = 0; i < number; i++) { if (index == -1) continue; //隐藏轴不需要处理 AxisY axisY = v.YAxes[index + i]; v.YAxes.RemoveAt(index + i); v.YAxes.Insert(i, axisY); } } LChartALL.EndUpdate(); } } private void YasixZENVisible(object cBox) { var checkBox= cBox as CheckBox; if (_lChartAll != null) { _lChartAll.BeginUpdate(); bool yAxesVisible = (checkBox.IsChecked == false); ViewXY v = _lChartAll.ViewXY; string content = checkBox.Content.ToString(); string conStr = (content == "SHZ") ? ".Z" : (content == "SHN") ? ".N" : ".E"; for (int i = 0; i <_chartAxisY.Count; i++) { AxisY axisY = _chartAxisY[i]; if (axisY.Title.Text.Contains(conStr)) { if (yAxesVisible) { v.YAxes.Remove(axisY); } else { string str = axisY.Title.Text.Replace(conStr, ""); int yIndex = 0; if (content == "SHZ") { yIndex = v.YAxes.FindIndex(y => y.Title.Text.Contains(str)); } else if (content == "SHE") { yIndex = v.YAxes.FindLastIndex(y => y.Title.Text.Contains(str)); if (yIndex != -1) yIndex += 1; } else { yIndex = v.YAxes.FindIndex(y => y.Title.Text.Contains(str)); if (yIndex != -1) { if (v.YAxes[yIndex].Title.Text.Contains(".Z")) { yIndex = yIndex + 1; } } } //如果没有查到,直接添加到前面,如果为容器最大索引,只能添加到后面 if (yIndex == -1 || (yIndex == v.YAxes.Count)) { yIndex = v.YAxes.Count; v.YAxes.Add(axisY); } else { v.YAxes.Insert(yIndex, axisY); } int sampleIndex = v.SampleDataSeries.FindIndex(y => y.Title.Text == _chartAxisY[i].Title.Text); v.SampleDataSeries[sampleIndex].AssignYAxisIndex = yIndex; int lineIndex = v.LineCollections.FindIndex(a => a.Title.Text.Contains(_chartAxisY[i].Title.Text)); if (lineIndex != -1) { v.LineCollections[lineIndex].AssignYAxisIndex = yIndex; } } } } _lChartAll.EndUpdate(); } } private void IntervalSure() { if (_lChartAll != null) { _lChartAll.BeginUpdate(); if (XaisInterval >= 2) { _lChartAll.ViewXY.XAxes[0].MajorDivCount = XaisInterval; } _lChartAll.EndUpdate(); } } private void StationsNameVisible(object obj) { var station = obj as StationAxis; if (_lChartAll != null) { _lChartAll.BeginUpdate(); bool yAxesVisible = (station.IsChecked == false); ViewXY v = _lChartAll.ViewXY; var yAxisList= _chartAxisY.Where(y => y.Title.Text.Contains(station.Name)); foreach (var item in yAxisList) { if (yAxesVisible) { v.YAxes.Remove(item); } else { v.YAxes.Add(item); //SampleDataSeries查找数据对应的Y轴索引, 重新分配到对应的轴 int sampleIndex = v.SampleDataSeries.FindIndex(s => s.Title.Text == item.Title.Text); v.SampleDataSeries[sampleIndex].AssignYAxisIndex = v.YAxes.Count()-1; //LineCollections查找数据对应的Y轴索引, 重新分配到对应的轴 int lineIndex = v.LineCollections.FindIndex(s => s.Title.Text.Contains(item.Title.Text)); if (lineIndex != -1) { v.LineCollections[lineIndex].AssignYAxisIndex = v.YAxes.Count() - 1; } } } _lChartAll.EndUpdate(); } } private void RealTimeData(object isCheck) { if (_lChartAll != null) { bool yAxesVisible = ((bool)isCheck == true); _lChartAll.BeginUpdate(); if (yAxesVisible) { //Set real-time monitoring automatic old data destruction LChartALL.ViewXY.DropOldSeriesData = true; _pointsAppended = LChartALL.ViewXY.XAxes[0].Minimum; CompositionTarget.Rendering -= CompositionTarget_Rendering; CompositionTarget.Rendering += CompositionTarget_Rendering; LChartALL.ViewXY.XAxes[0].ScrollMode = XAxisScrollMode.Scrolling; } else { LChartALL.ViewXY.XAxes[0].ScrollMode = XAxisScrollMode.None; CompositionTarget.Rendering -= CompositionTarget_Rendering; } _lChartAll.EndUpdate(); } } #endregion private void CreateChart() { if (LChartALL != null) { LChartALL = null; } LChartALL = new LightningChart(); LChartALL.Title.Align = ChartTitleAlignment.TopCenter; LChartALL.ChartRenderOptions.DeviceType = RendererDeviceType.AutoPreferD11; LChartALL.ChartRenderOptions.LineAAType2D = LineAntiAliasingType.QLAA; LChartALL.Title.Text = "事件波形"; LChartALL.Title.Font = new WpfFont("等线", 20); //轴属性和布局 LChartALL.ViewXY.AxisLayout.YAxesLayout = YAxesLayout.Stacked; LChartALL.ViewXY.AxisLayout.SegmentsGap = 0; LChartALL.ViewXY.ZoomPanOptions.PanDirection = PanDirection.Horizontal; LChartALL.ViewXY.ZoomPanOptions.WheelZooming = WheelZooming.Horizontal; LChartALL.ViewXY.AxisLayout.YAxisAutoPlacement = YAxisAutoPlacement.AllLeft; LChartALL.ViewXY.AxisLayout.YAxisTitleAutoPlacement = false; LChartALL.ViewXY.AxisLayout.AutoAdjustMargins = false; LChartALL.ViewXY.ZoomPanOptions.DevicePrimaryButtonAction = UserInteractiveDeviceButtonAction.None; LChartALL.ViewXY.ZoomPanOptions.DeviceSecondaryButtonAction = UserInteractiveDeviceButtonAction.None; LChartALL.ViewXY.ZoomPanOptions.WheelZooming = WheelZooming.Off; // 反锯齿系数。值0和1都不会应用反锯齿 LChartALL.ChartRenderOptions.AntiAliasLevel = 0; //X轴设置 LChartALL.ViewXY.XAxes[0].AllowScrolling = false; LChartALL.ViewXY.XAxes[0].PanningEnabled = false; LChartALL.ViewXY.XAxes[0].MajorGrid.Pattern = LinePattern.Solid; LChartALL.ViewXY.XAxes[0].ValueType = AxisValueType.DateTime; LChartALL.ViewXY.XAxes[0].KeepDivCountOnRangeChange = true; //LChartALL.ViewXY.XAxes[0].MajorDiv =6; LChartALL.ViewXY.XAxes[0].LabelsColor = Colors.Black; LChartALL.ViewXY.XAxes[0].AutoDivSpacing = false; LChartALL.ViewXY.XAxes[0].MajorDivCount = 5; XaisInterval = 5; //图表背景颜色 LChartALL.ViewXY.GraphBackground.Color = Colors.White; LChartALL.ViewXY.GraphBackground.GradientColor = Colors.White; LChartALL.ChartBackground.GradientFill = GradientFill.Solid; LChartALL.ChartBackground.Color = Color.FromArgb(0, 0, 0, 0); GridChart = new Grid(); GridChart.ColumnDefinitions.Add(new ColumnDefinition()); GridChart.Name = "chartGrid"; GridChart.Children.Add(LChartALL); this.ChildContent = GridChart; } private void StartChart() { DisposeAllAndClear(_chartAxisY); ViewXY v = LChartALL.ViewXY; LChartALL.SizeChanged -= LChartALL_SizeChanged; LChartALL.SizeChanged += LChartALL_SizeChanged; DisposeAllAndClear(v.YAxes); DisposeAllAndClear(v.SampleDataSeries); DisposeAllAndClear(v.LineCollections); v.Margins=new Thickness(80, 30, 50, 60); v.YAxes.AddRange(_wavesModel.CreateYAxisChart(smList.ToList(), LChartALL)); v.LegendBoxes[0].Position = LegendBoxPositionXY.RightCenter; v.LegendBoxes[0].Offset.SetValues(-10, 180); v.LegendBoxes[0].Layout = LegendBoxLayout.Vertical; v.LegendBoxes[0].Visible = false; v.LegendBoxes[0].Shadow.Color = Colors.Transparent; v.LegendBoxes[0].BorderColor = Colors.Transparent; v.AutoSpaceLegendBoxes = true; v.AxisLayout.SegmentsGap = 3; v.LegendBoxes[0].Shadow.Visible = false; StationModel stationModel = smList.First(); var beginTime= stationModel.BeginTime; if (AxisValueType.DateTime == v.XAxes[0].ValueType) { //设置X轴的开始时间 v.XAxes[0].AutoFormatLabels = false; v.XAxes[0].LabelsTimeFormat = "HH:mm:ss.ff"; v.XAxes[0].DateOriginYear = beginTime.Year; v.XAxes[0].DateOriginDay = beginTime.Day; v.XAxes[0].DateOriginMonth = beginTime.Month; DateTime MaxDateTime = beginTime.AddSeconds(_samplingFrequency == 500 ? 30 : 60); v.XAxes[0].SetRange(v.XAxes[0].DateTimeToAxisValue(beginTime), v.XAxes[0].DateTimeToAxisValue(MaxDateTime)); } double firstSampleTimeStamp = v.XAxes[0].DateTimeToAxisValue(beginTime); int count = stationModel.Dzne.Count; int number= smList.Count*3; for (int i = 0; i < number; i++) { int seriesIndex = i; AxisY axisY = v.YAxes[seriesIndex]; axisY.LabelsColor = Colors.Black; axisY.Title.Shadow.DropColor = Colors.Transparent; // axisY.Title.Shadow.ContrastColor = Colors.Transparent; axisY.Title.AllowDragging = false; axisY.AllowAutoYFit = false; axisY.Units.Visible = false; axisY.LabelsFont.Size = 10; axisY.Title.Angle = 0; axisY.MinorGrid.Visible = false; axisY.AutoDivSpacing = false; axisY.MajorDivCount = 2; axisY.MinorDivTickStyle.Visible = false; axisY.MajorDivTickStyle.Alignment = seriesIndex % 2 == 0 ? Alignment.Near : Alignment.Far; // axisY.MajorDivTickStyle.LineLength = 6; axisY.MajorDivTickStyle.Color = Colors.Black; axisY.Title.Color = Colors.Black; axisY.Title.Font = new WpfFont("Segoe UI", 10, false, false); axisY.PanningEnabled = false; axisY.AllowScrolling = false; axisY.AllowScaling = false; SampleDataSeries series = new SampleDataSeries(v, v.XAxes[0], axisY); series.ShowInLegendBox = false; series.FirstSampleTimeStamp = firstSampleTimeStamp; series.SamplingFrequency = _samplingFrequency; series.Title.Text = axisY.Title.Text; series.SampleFormat = SampleFormat.DoubleFloat; if (seriesIndex % 3 == 0) { series.LineStyle.Color = System.Windows.Media.Colors.DeepSkyBlue; } else if (seriesIndex % 3 == 1) { series.LineStyle.Color = System.Windows.Media.Colors.OrangeRed; } else if (seriesIndex % 3 == 2) { series.LineStyle.Color = System.Windows.Media.Colors.ForestGreen; } series.LineStyle.Width = 0.2; series.ScrollModePointsKeepLevel = 1; //series.ScrollingStabilizing = true; v.SampleDataSeries.Add(series); _chartAxisY.Add(axisY); } } private void LChartALL_SizeChanged(object sender, SizeChangedEventArgs e) { foreach (var item in LChartALL.ViewXY.YAxes) { //更新标题坐标 item.Title.DistanceToAxis = -(int)e.NewSize.Width+80; } foreach (var item in LChartALL.ViewXY.LineCollections) { double b = LChartALL.ViewXY.XAxes[0].ValueToCoordD(item.Lines[0].AX)-80; item.Title.Offset.SetValues((int)b, 3); } } private void FeedDatasToChart() { _data = new double[smList.Count * 3][]; try { for (int channelIndex = 0; channelIndex < smList.Count; channelIndex++) { _data[channelIndex * 3] = smList.ElementAt(channelIndex).dz.ToArray(); _data[channelIndex * 3 + 1] = smList.ElementAt(channelIndex).dn.ToArray(); _data[channelIndex * 3 + 2] = smList.ElementAt(channelIndex).de.ToArray(); } // Invoke FeedNewDataToChart. _dispatcher.Invoke(() => { LChartALL.BeginUpdate(); _wavesModel.CreateAxisYEventTime(CurrentEventTime, LChartALL.ViewXY, smList.ToList()); for (int channelIndex = 0; channelIndex < _channelCount; channelIndex++) { LChartALL.ViewXY.SampleDataSeries[channelIndex].AddSamples(_data[channelIndex], true); } LChartALL.EndUpdate(); }); } catch (Exception ex) { throw ex; } } private void ApplicationClosingDispose(object sender, CancelEventArgs e) { if (LChartALL != null) { LChartALL.Dispose(); LChartALL = null; } } private void DisposeAllAndClear(List list) where T : IDisposable { if (list == null) { return; } while (list.Count > 0) { int lastInd = list.Count - 1; T item = list[lastInd]; // take item ref from list. list.RemoveAt(lastInd); // remove item first if (item != null) { (item as IDisposable).Dispose(); // then dispose it. } } } public void Dispose() { gridChart.Children.Clear(); if (LChartALL != null) { LChartALL.Dispose(); LChartALL = null; } } #region 实时数据 private void CompositionTarget_Rendering(object sender, EventArgs e) { RenderNextFrame(); } private void RenderNextFrame() { if (_lChartAll == null) { return; } _dispatcher.Invoke(() => { FeedData(/*chartTitleText*/); }); } int _iRound = 0; double _pointsAppended = 0; double[][] _data; int PreGenerateDataForRoundCount = 6; private void FeedData(/*string chartTitleText*/) { if (_lChartAll != null) { _lChartAll.BeginUpdate(); //Append data to series System.Threading.Tasks.Parallel.For(0, _channelCount, (seriesIndex) => { double[] thisSeriesData = _data[seriesIndex]; double[] dataToAppendNow = new double[_samplingFrequency]; Array.Copy(thisSeriesData, (_iRound % PreGenerateDataForRoundCount) * _samplingFrequency, dataToAppendNow, 0, _samplingFrequency); _lChartAll.ViewXY.SampleDataSeries[seriesIndex].AddSamples(dataToAppendNow, false); System.Diagnostics.Debug.WriteLine("***********index:{0}, pointCount:{1},time:{2}", seriesIndex, _lChartAll.ViewXY.SampleDataSeries[seriesIndex].PointCount, DateTime.Now); }); _pointsAppended += 1; //Set X axis real-time scrolling position double lastX = _pointsAppended; _lChartAll.ViewXY.XAxes[0].ScrollPosition = lastX; //Update sweep bands if (_lChartAll.ViewXY.XAxes[0].ScrollMode == XAxisScrollMode.Sweeping) { //Dark band of old page fading away double pageLen = _lChartAll.ViewXY.XAxes[0].Maximum - _lChartAll.ViewXY.XAxes[0].Minimum; double sweepGapWidth = pageLen / 20.0; _lChartAll.ViewXY.Bands[0].SetValues(lastX - pageLen, lastX - pageLen + sweepGapWidth); if (_lChartAll.ViewXY.Bands[0].Visible == false) { _lChartAll.ViewXY.Bands[0].Visible = true; } //Bright new page band _lChartAll.ViewXY.Bands[1].SetValues(lastX - sweepGapWidth / 6, lastX); if (_lChartAll.ViewXY.Bands[1].Visible == false) { _lChartAll.ViewXY.Bands[1].Visible = true; } } else { //Hide sweeping bands if not in sweeping mode //if (_lChartAll.ViewXY.Bands[0].Visible == true) //{ // _lChartAll.ViewXY.Bands[0].Visible = false; //} //if (_lChartAll.ViewXY.Bands[1].Visible == true) //{ // _lChartAll.ViewXY.Bands[1].Visible = false; //} } _lChartAll.EndUpdate(); _iRound++; } } #endregion } }