2020年3月4日 星期三

SciChart - 多 Channel 示波器

 關於使用 SciChart 如何製作多 Channel 示波器顯示。


主要功能拆解:
  1. 多個 Channel 顯示波形 (圖中以三角函數縮放作為範例)
  2. 滑鼠移動後,同時顯示 Legend 
  3. 圈選範圍後,可以放大分析
  4. 按空白鍵後復位分析
  5. 局部開啟 Channel 開關
詳情專案可以見程式碼: https://github.com/hpcslag/SciChart-MultiChannel


單個 Channel 的功能性實作


從最簡單的開始,用 Sci-Chart Examples 就能做,找出 Chart_SplineScatterLineChart 範本,改成以下 wpf:
<UserControl x:Class="SciChart.Examples.Examples.CreateACustomChart.SplineLineSeries.MainChart"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:s="http://schemas.abtsoftware.co.uk/scichart"
             xmlns:ext="http://schemas.abtsoftware.co.uk/scichart/exampleExternals"
             Loaded="ChartLoaded"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             d:DesignHeight="400"
             d:DesignWidth="600"
             mc:Ignorable="d">

    <!--注意引入哪些 xmlns:[class] -->
    <UserControl.Resources>

        <!-- 這是滑鼠移動上去 Chart 內容的 WPF Style 定義 The style for the rollover line -->
        <Style x:Key="RolloverLineStyle" TargetType="Line">
            <Setter Property="Stroke" Value="White"/>
            <Setter Property="StrokeThickness" Value="1"/>
            <Setter Property="IsHitTestVisible" Value="False"/>
            <Setter Property="UseLayoutRounding" Value="True"/>
        </Style>

        <!-- 這是工具列的 Component Data Template, A1 Tooltip Style Format -->
        <DataTemplate x:Key="XyTooltipTemplate" DataType="s:XySeriesInfo">
            <StackPanel Orientation="Vertical">
                <TextBlock Foreground="White">
               <Run Text="Series: "/>
               <Run Text="{Binding SeriesName, StringFormat='{}{0}'}"/>
                </TextBlock>
                <TextBlock Foreground="White">
               <Run Text="X-Axis: "/>
               <Run Text="{Binding XValue, StringFormat=X: \{0:0.000000\}}"/>
                </TextBlock>
                <TextBlock Foreground="White">
               <Run Text="Y-Axis: "/>
               <Run Text="{Binding YValue, StringFormat=Y: \{0:0.0000000\}}"/>
                </TextBlock>
            </StackPanel>
        </DataTemplate>

        <!-- Label 顯示的樣式 -->
        <Style x:Key="RightAlignedLabelStyle" TargetType="s:DefaultTickLabel">
            <Setter Property="HorizontalAnchorPoint" Value="Right" />
        </Style>

        <!-- Y 軸呈現的樣式 -->
        <Style x:Key="YAxisStyle" TargetType="s:AxisBase">
            <Setter Property="HorizontalAlignment" Value="Right" />
            <Setter Property="VisibleRange" Value="-2, 2" />
            <Setter Property="VisibleRangeLimit" Value="-2, 2" />
            <Setter Property="AutoRange" Value="Never" />
            <Setter Property="AxisAlignment" Value="Left" />
            <Setter Property="DrawMinorGridLines" Value="False" />
            <Setter Property="DrawMinorTicks" Value="False" />
            <Setter Property="DrawMajorGridLines" Value="False" />
            <Setter Property="DrawMajorBands" Value="False" />
            <Setter Property="TickLabelStyle" Value="{StaticResource RightAlignedLabelStyle}" />
        </Style>

        <!-- Y 軸 Pannel , 基本上要在這邊定義有多少 Items(Channel 數),用來決定版面高度 -->
        <!--  Optionally replace the default StackPanel for the Axis Container. In this example we create a Grid  -->
        <!--  with 4x rows and one column. Then use Grid.Row on the actual Axis instances to place in the correct rows  -->
        <!--    -->
        <!--  By Default SciChart will place YAxes stacked horizontally, but using this feature we can override  -->
        <!--  to place vertically on the same chart surface  -->
        <ItemsPanelTemplate x:Key="YAxesPanel">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="10" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
            </Grid>
        </ItemsPanelTemplate>

        <!-- 筆刷樣式 -->
        <LinearGradientBrush x:Key="MountainFillBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientStop Offset="0" Color="#A83376E5" />
            <GradientStop Offset="1" Color="#333376E5" />
        </LinearGradientBrush>

    </UserControl.Resources>

    <Grid>
        <!-- 建立 charts 了 -->
        <!--  Create the chart surface  -->
        <s:SciChartSurface x:Name="sciChart"
                           LeftAxesPanelTemplate="{StaticResource YAxesPanel}"
                           RightAxesPanelTemplate="{StaticResource YAxesPanel}">

            <s:SciChartSurface.Annotations>
                <s:TextAnnotation Name="chartTitle" Text="&#25976;&#20301;&#35338;&#34399;&#27874;&#22411;&#25511;&#21046;" HorizontalAnchorPoint="Center" VerticalAnchorPoint="Top" X1="0.5" Y1="0.01" CoordinateMode="Relative"/>
            </s:SciChartSurface.Annotations>

            <!-- 這裡面是要定義哪些 Y 軸 ID (就是哪個 channel) 要套用到哪個種類的 Series(波) Render 方法-->
            <!--  Declare RenderableSeries  -->
            <s:SciChartSurface.RenderableSeries>
                <!-- A1 s:RolloverModifier.TooltipTemplate -> Set Tooltip Style -->
                <s:FastLineRenderableSeries s:RolloverModifier.TooltipTemplate="{StaticResource XyTooltipTemplate}" Stroke="#30b7ff" YAxisId="Ch0" />
            </s:SciChartSurface.RenderableSeries>

            <!-- X 軸只呈現數字 -->
            <!--  Create an X Axis with Growby  -->
            <s:SciChartSurface.XAxis>
                <s:NumericAxis />
            </s:SciChartSurface.XAxis>

            <!-- 定義數值,綁定到哪一條 Series -->
            <!--  Create a Y Axis with Grid.Row to position on the YAxis Panel. Alternate axis display MajorGridLines  -->
            <s:SciChartSurface.YAxes>
                <s:NumericAxis x:Name="Ch0"
                               Grid.Row="0"
                               AxisTitle="Ch0"
                               Id="Ch0"
                               Style="{StaticResource YAxisStyle}" />
            </s:SciChartSurface.YAxes>

            <!-- 定義工具 bar: 基本上套用上面定義過的樣式(像是 RolloverLineStyle) -->
            <s:SciChartSurface.ChartModifier>
                <s:ModifierGroup>
                    <s:RubberBandXyZoomModifier IsXAxisOnly="True" />
                    <s:ZoomExtentsModifier />
                    <s:CursorModifier ShowAxisLabels="False" ShowTooltip="False" />
                    <s:LegendModifier ShowLegend="True" LegendPlacement="Inside" Margin="10" VerticalAlignment="Top" HorizontalAlignment="Right"/>
                    <s:RolloverModifier x:Name="RolloverModifier" ShowAxisLabels="True" 
                                        UseInterpolation="True"
                                        LineOverlayStyle="{StaticResource RolloverLineStyle}"
                                        ShowTooltipOn="Always"/>
                    <s:XAxisDragModifier/>
                    <s:YAxisDragModifier/>
                </s:ModifierGroup>
            </s:SciChartSurface.ChartModifier>

        </s:SciChartSurface>
    </Grid>

</UserControl>

單個 Channel 是這麼顯示的,他的 C# 是:

// *************************************************************************************
// SCICHART® Copyright SciChart Ltd. 2011-2018. All rights reserved.
//  
// Web: http://www.scichart.com
//   Support: [email protected]
//   Sales:   [email protected]
// 
// SplineChartExampleView.xaml.cs is part of the SCICHART® Examples. Permission is hereby granted
// to modify, create derivative works, distribute and publish any part of this source
// code whether for commercial, private or personal use. 
// 
// The SCICHART® examples are distributed in the hope that they will be useful, but
// without any warranty. It is provided "AS IS" without warranty of any kind, either
// expressed or implied. 
// *************************************************************************************
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using SciChart.Charting.Model.DataSeries;
using SciChart.Examples.ExternalDependencies.Data;

namespace SciChart.Examples.Examples.CreateACustomChart.SplineLineSeries
{
    /// <summary>
    /// Interaction logic for CustomChartExampleView.xaml
    /// </summary>
    public partial class MainChart : UserControl
    {
        public MainChart()
        {
            InitializeComponent();

            //按下 Space 之後,回到波形 Overview
            this.KeyDown += (object sender, KeyEventArgs e) => {
                if(e.Key == Key.Space)
                {
                    this.sciChart.ZoomExtents();
                }
            };
        }
        
        //先定義好有幾個 channel
        public int channel_length = 1;

        private void ChartLoaded(object sender, RoutedEventArgs e)
        {
            chartTitle.Text = "波型圖標題";

            Task.Factory.StartNew(() =>
            {
                // Creates `N` dataseries with data on a background thread
                var dataSeries = new List<IDataSeries>();
                for (int i = 0; i < channel_length; i++)
                {
                    var ds = new XyDataSeries<double, double>() { SeriesName="CH "+i };
                    dataSeries.Add(ds);
                    var someData = DataManager.Instance.GetSinewave(i+1.0, 0.0, 1000, 4); //隨機分配正弦波,做 i+1 伸縮

                    ds.Append(someData.XData, someData.YData);
                }

                // Creates `N` renderable series on the UI thread
                Dispatcher.BeginInvoke(new Action(() => CreateRenderableSeries(dataSeries)));
            });

            sciChart.ZoomExtents();
        }

        public static void addData(DoubleSeries data, double x, double y) {
            XYPoint xy = new XYPoint();
            xy.X = x;
            xy.Y = y;
            data.Add(xy);
        }

        private void CreateRenderableSeries(List<IDataSeries> result)
        {
            // Batch updates with one redraw
            using (sciChart.SuspendUpdates())
            {
                for (int i = 0; i < channel_length; i++)
                {
                    sciChart.RenderableSeries[i].DataSeries = result[i];
                }
            }
        }
    }
}

現在,圖表已經可以單軸顯示了:



多個 Channel 的功能性實作


多個 Channel, wpf 中需要對 y 軸 grid 增加定義,然後增加 render series 數量以及 channel 數值綁定。 (這三件事) 

由於程式碼過多,以下只呈現部分結構,可透過文字搜尋找到,進行更改
<!--  Optionally replace the default StackPanel for the Axis Container. In this example we create a Grid  -->
<!--  with 4x rows and one column. Then use Grid.Row on the actual Axis instances to place in the correct rows  -->
<!--    -->
<!--  By Default SciChart will place YAxes stacked horizontally, but using this feature we can override  -->
<!--  to place vertically on the same chart surface  -->
<ItemsPanelTemplate x:Key="YAxesPanel">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" /> <!-- 第 1 個 channel -->
            <RowDefinition Height="10" /> <!-- Height 間隔 -->
            <RowDefinition Height="*" /> <!-- 第 2 個 channel -->
            <RowDefinition Height="10" /> 
            <RowDefinition Height="*" /> <!-- 第 3 個 channel -->
            <RowDefinition Height="10" />
            <RowDefinition Height="*" /> <!-- 第 4 個 channel -->
            <RowDefinition Height="10" />
            <RowDefinition Height="*" /> <!-- 第 5 個 channel -->
            <RowDefinition Height="10" />
            <RowDefinition Height="*" /> <!-- 第 6 個 channel -->
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
    </Grid>
</ItemsPanelTemplate>

以及

<!--  Declare RenderableSeries  -->
<s:SciChartSurface.RenderableSeries>
    <!-- A1 s:RolloverModifier.TooltipTemplate -> Set Tooltip Style -->
    <s:FastLineRenderableSeries s:RolloverModifier.TooltipTemplate="{StaticResource XyTooltipTemplate}" Stroke="#30b7ff" YAxisId="Ch0" /> <!-- 第 1 個 channel series render -->
    <s:FastLineRenderableSeries s:RolloverModifier.TooltipTemplate="{StaticResource XyTooltipTemplate}" Stroke="#30b7ff" YAxisId="Ch1" /> <!-- 第 2 個 channel series render -->
    <s:FastLineRenderableSeries s:RolloverModifier.TooltipTemplate="{StaticResource XyTooltipTemplate}" Stroke="#30b7ff" YAxisId="Ch2" /> <!-- 第 3 個 channel series render -->
    <s:FastLineRenderableSeries s:RolloverModifier.TooltipTemplate="{StaticResource XyTooltipTemplate}" Stroke="#30b7ff" YAxisId="Ch3" /> <!-- 第 4 個 channel series render -->
    <s:FastLineRenderableSeries s:RolloverModifier.TooltipTemplate="{StaticResource XyTooltipTemplate}" Stroke="#30b7ff" YAxisId="Ch4" /> <!-- 第 5 個 channel series render -->
    <s:FastLineRenderableSeries s:RolloverModifier.TooltipTemplate="{StaticResource XyTooltipTemplate}" Stroke="#30b7ff" YAxisId="Ch5" /> <!-- 第 6 個 channel series render -->
</s:SciChartSurface.RenderableSeries>

以及

<!--  Create a Y Axis with Grid.Row to position on the YAxis Panel. Alternate axis display MajorGridLines  -->
<s:SciChartSurface.YAxes>
    <s:NumericAxis x:Name="Ch0"
                   Grid.Row="0"
                   AxisTitle="Ch0"
                   Id="Ch0"
                   Style="{StaticResource YAxisStyle}" /> <!-- 第 1 個 channel 數值綁定 -->
    <s:NumericAxis x:Name="Ch1"
                   Grid.Row="2"
                   AxisTitle="Ch1"
                   Id="Ch1"
                   Style="{StaticResource YAxisStyle}" /> <!-- 第 2 個 channel 數值綁定 -->
<s:NumericAxis x:Name="Ch2" Grid.Row="4" AxisTitle="Ch2" Id="Ch2" Style="{StaticResource YAxisStyle}" /> <!-- 第 3 個 channel 數值綁定 -->
<s:NumericAxis x:Name="Ch3" Grid.Row="6" AxisTitle="Ch3" Id="Ch3" Style="{StaticResource YAxisStyle}" /> <!-- 第 4 個 channel 數值綁定 -->
<s:NumericAxis x:Name="Ch4" Grid.Row="8" AxisTitle="Ch4" Id="Ch4" Style="{StaticResource YAxisStyle}" /> <!-- 第 5 個 channel 數值綁定 -->
<s:NumericAxis x:Name="Ch5" Grid.Row="10" AxisTitle="Ch5" Id="Ch5" Style="{StaticResource YAxisStyle}" /> <!-- 第 6 個 channel 數值綁定 -->
</s:SciChartSurface.YAxes>

然後,在 C# 端,把 channel 數量做調整,就可以顯示 6 個頻道
//先定義好有幾個 channel
public int channel_length = 6;



沒有留言:

張貼留言

© Mac Taylor, 歡迎自由轉貼。
Background Email Pattern by Toby Elliott
Since 2014