C# WPF 上位机开发:信号采集校准平台
By
jerryxjr1220
at 2023-10-20 • 0人收藏 • 805人看过

<hc:GlowWindow x:Class="WPFCalibrationController.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="clr-namespace:WPFCalibrationController.Models"
xmlns:vm="clr-namespace:WPFCalibrationController.ViewModels"
xmlns:view="clr-namespace:WPFCalibrationController.Views"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:WPFCalibrationController"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:scott="clr-namespace:ScottPlot.WPF;assembly=ScottPlot.WPF"
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
mc:Ignorable="d"
Title="Signal Calibration Platform" Height="500" Width="800"
FontFamily="JetBrains Mono"
FontSize="{StaticResource TextFontSize}">
<Window.Resources>
<vm:ReverseBooleanConverter x:Key="ReverseBooleanConverter" />
</Window.Resources>
<hc:SimplePanel>
<ScrollViewer x:Name="scrollViewer" VerticalScrollBarVisibility="Auto">
<StackPanel HorizontalAlignment="Center">
<TextBlock Text="Signal Calibration Controller" Style="{StaticResource TextBlockDefaultPrimary}"
FontSize="20" Margin="20"/>
<WrapPanel>
<TextBlock Text="Signal Count Setting" Style="{StaticResource TextBlockDefaultSuccess}" Margin="20, 5"/>
<ScrollBar Orientation="Horizontal" Minimum="50" Maximum="500" Width="200" Value="{Binding PointCount, Mode=TwoWay}"
Style="{StaticResource ScrollBarBaseStyle}" Background="Green" ViewportSize="{StaticResource HeadFontSize}" Foreground="White"
ToolTip="{Binding PointCount}" />
<ToggleButton Content="Measure" Style="{StaticResource ToggleButtonSwitch}" IsChecked="False"
x:Name="togglebutton" FontSize="24" Width="70" Margin="20, 0" Foreground="{StaticResource PrimaryBrush}" Checked="ToggleButton_OnChecked" Unchecked="ToggleButton_OnUnchecked"/>
</WrapPanel>
<WrapPanel>
<TextBox hc:InfoElement.Title="K" hc:InfoElement.TitlePlacement="Left" Width="100" Margin="20, 5"
Style="{StaticResource TextBoxExtend}" Foreground="{StaticResource PrimaryBrush}"
Text="{Binding K}" IsReadOnly="True"/>
<TextBox hc:InfoElement.Title="B" hc:InfoElement.TitlePlacement="Left" Width="100" Margin="20, 5"
Style="{StaticResource TextBoxExtend}" Foreground="{StaticResource PrimaryBrush}"
Text="{Binding B}" IsReadOnly="True"/>
<Button Content="Calculate" Style="{StaticResource ButtonPrimary}" Command="{Binding CalculateCommand}" CommandParameter="{Binding PointCount}"
/>
</WrapPanel>
<scott:WpfPlot x:Name="scottPlot" Width="600" Height="320" />
</StackPanel>
</ScrollViewer>
</hc:SimplePanel>
</hc:GlowWindow>using System;
using System.Collections.Generic;
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 ScottPlot;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using HandyControl.Controls;
using ScottPlot.Legends;
using ScottPlot.Plottables;
using WPFCalibrationController.Models;
using WPFCalibrationController.ViewModels;
using MessageBox = HandyControl.Controls.MessageBox;
namespace WPFCalibrationController.Views;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : GlowWindow
{
private MainViewModel mainViewModel;
private DispatcherTimer timer;
public MainWindow()
{
InitializeComponent();
#region 注册并接受消息
//WeakReferenceMessenger.Default.Register<CustomizedMessage>(this, (o, m) => { MessageBox.Show("Received!"); });
#endregion
mainViewModel = new MainViewModel();
scottPlot.Plot.XLabel("TimeStamp");
scottPlot.Plot.YLabel("Value");
var sm = scottPlot.Plot.Add.Scatter(mainViewModel.MeasuredSignal.SignalIds!, mainViewModel.MeasuredSignal.SignalValues!,
getColor(mainViewModel.MeasuredSignal.SignalColor));
sm.Label = mainViewModel.MeasuredSignal.SignalName;
var sr = scottPlot.Plot.Add.Scatter(mainViewModel.ReferenceSignal.SignalIds!, mainViewModel.ReferenceSignal.SignalValues!,
getColor(mainViewModel.ReferenceSignal.SignalColor));
sr.Label = mainViewModel.ReferenceSignal.SignalName;
scottPlot.Plot.Legend();
var legend = scottPlot.Plot.GetLegend();
legend.Alignment = Alignment.UpperLeft;
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(500);
timer.Tick += (sender, args) =>
{
mainViewModel.GeneratePoints(1);
scottPlot.Plot.Clear();
scottPlot.Plot.Add.Scatter(mainViewModel.MeasuredSignal.SignalIds!, mainViewModel.MeasuredSignal.SignalValues!,
getColor(mainViewModel.MeasuredSignal.SignalColor));
scottPlot.Plot.Add.Scatter(mainViewModel.ReferenceSignal.SignalIds!, mainViewModel.ReferenceSignal.SignalValues!,
getColor(mainViewModel.ReferenceSignal.SignalColor));
scottPlot.Plot.AutoScale();
scottPlot.Refresh();
};
this.DataContext = mainViewModel;
}
private Color getColor(SignalModel.SignalColorType colorType)
{
switch (colorType) {
case SignalModel.SignalColorType.Red:
return Colors.Red;
case SignalModel.SignalColorType.Green:
return Colors.Green;
case SignalModel.SignalColorType.Blue:
return Colors.Blue;
case SignalModel.SignalColorType.Brown:
return Colors.Brown;
case SignalModel.SignalColorType.Purple:
return Colors.Purple;
case SignalModel.SignalColorType.Yellow:
return Colors.Yellow;
default:
return Colors.Black;
}
}
private void ToggleButton_OnChecked(object sender, RoutedEventArgs e)
{
timer.Start();
}
private void ToggleButton_OnUnchecked(object sender, RoutedEventArgs e)
{
timer.Stop();
}
}using System;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using WPFCalibrationController.Models;
namespace WPFCalibrationController.ViewModels;
public partial class MainViewModel : ObservableObject
{
[ObservableProperty]
private SignalModel _measuredSignal;
[ObservableProperty]
private SignalModel _referenceSignal;
[ObservableProperty] private double _k;
[ObservableProperty] private double _b;
[ObservableProperty] private int _pointCount;
public MainViewModel()
{
MeasuredSignal = new SignalModel();
MeasuredSignal.SignalName = "Measured Signal";
MeasuredSignal.SignalDescription = "The signal measured by the instrument from end point.";
MeasuredSignal.SignalColor = SignalModel.SignalColorType.Blue;
ReferenceSignal = new SignalModel();
ReferenceSignal.SignalName = "Reference Signal";
ReferenceSignal.SignalDescription = "The signal generated from the original source.";
ReferenceSignal.SignalColor = SignalModel.SignalColorType.Red;
PointCount = 100;
GeneratePoints(PointCount);
//发送消息
//WeakReferenceMessenger.Default.Send(new CustomizedMessage(new MessageModel()));
}
public void GeneratePoints(int pointCount)
{
var lastidmeasure = MeasuredSignal.SignalTimestamps!.Count;
var lastidreference = ReferenceSignal.SignalTimestamps!.Count;
for (int i = 0; i < pointCount; i++)
{
var rand = new Random();
var n = rand.NextDouble();
var r = n + rand.NextDouble() * 0.1;
var m = n * 5 + rand.NextDouble() * 0.2;
MeasuredSignal.SignalValues!.Add(m);
MeasuredSignal.SignalTimestamps!.Add(DateTime.Now);
MeasuredSignal.SignalIds!.Add(i + lastidmeasure);
ReferenceSignal.SignalValues!.Add(r);
ReferenceSignal.SignalTimestamps!.Add(DateTime.Now);
ReferenceSignal.SignalIds!.Add(i + lastidreference);
}
}
[RelayCommand]
public void Calculate(int pointsCount)
{
var x = MeasuredSignal.SignalValues!.TakeLast(pointsCount).ToArray();
var y = ReferenceSignal.SignalValues!.TakeLast(pointsCount).ToArray();
var k = 0.0;
var b = 0.0;
var n = x.Length;
var sumx = 0.0;
var sumy = 0.0;
var sumxy = 0.0;
var sumx2 = 0.0;
for (int i = 0; i < n; i++) {
sumx += x[i];
sumy += y[i];
sumxy += x[i] * y[i];
sumx2 += x[i] * x[i];
}
k = (n * sumxy - sumx * sumy) / (n * sumx2 - sumx * sumx);
b = (sumy - k * sumx) / n;
K = k;
B = b;
}
}
public class ReverseBooleanConverter : BooleanConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return !(bool)value;
} //转换成反向值
}
//定义消息
// public class CustomizedMessage : ValueChangedMessage<MessageModel> // PropertyChangedMessage // RequestMessage ( Async + ..)
// {
// public CustomizedMessage(MessageModel value) : base(value)
// {
// }
// }using System;
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using ScottPlot;
namespace WPFCalibrationController.Models;
public partial class SignalModel : ObservableObject
{
[ObservableProperty]
private string? _signalName;
[ObservableProperty]
private ObservableCollection<double>? _signalValues;
[ObservableProperty]
private string? _signalDescription;
[ObservableProperty]
private ObservableCollection<DateTime>? _signalTimestamps;
[ObservableProperty]
private ObservableCollection<double>? _signalIds;
[ObservableProperty]
private SignalStatusType _signalStatus;
[ObservableProperty]
private SignalColorType _signalColor;
public SignalModel()
{
SignalName = string.Empty;
SignalValues = new();
SignalDescription = string.Empty;
SignalTimestamps = new();
SignalIds = new();
SignalStatus = SignalStatusType.Stopped;
SignalColor = SignalColorType.Blue;
}
public enum SignalStatusType
{
Normal,
Stopped,
Error
}
public enum SignalColorType
{
Blue,
Green,
Red,
Yellow,
Brown,
Purple,
Black
}
}
3 个回复 | 最后更新于 2023-10-20
登录后方可回帖
再增加一个校准后的信号,可以实时展示校准后信号对比源信号的误差。
using System; using System.ComponentModel; using System.Linq; using System.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; using WPFCalibrationController.Models; namespace WPFCalibrationController.ViewModels; public partial class MainViewModel : ObservableObject { [ObservableProperty] private SignalModel _measuredSignal; [ObservableProperty] private SignalModel _referenceSignal; [ObservableProperty] private SignalModel _calibratedSignal; [ObservableProperty] private double _k; [ObservableProperty] private double _b; [ObservableProperty] private int _pointCount; public MainViewModel() { MeasuredSignal = new SignalModel(); MeasuredSignal.SignalName = "Measured Signal"; MeasuredSignal.SignalDescription = "The signal measured by the instrument from end point."; MeasuredSignal.SignalColor = SignalModel.SignalColorType.Blue; ReferenceSignal = new SignalModel(); ReferenceSignal.SignalName = "Reference Signal"; ReferenceSignal.SignalDescription = "The signal generated from the original source."; ReferenceSignal.SignalColor = SignalModel.SignalColorType.Red; CalibratedSignal = new SignalModel(); CalibratedSignal.SignalName = "Calibrated Signal"; CalibratedSignal.SignalDescription = "The signal calculated with the parameters."; CalibratedSignal.SignalColor = SignalModel.SignalColorType.Green; PointCount = 100; GeneratePoints(PointCount); //发送消息 //WeakReferenceMessenger.Default.Send(new CustomizedMessage(new MessageModel())); } public void GeneratePoints(int pointCount) { var lastidmeasure = MeasuredSignal.SignalTimestamps!.Count; var lastidreference = ReferenceSignal.SignalTimestamps!.Count; //var lastidcalibrated = CalibratedSignal.SignalTimestamps!.Count; for (int i = 0; i < pointCount; i++) { var rand = new Random(); var n = rand.NextDouble(); var r = n + rand.NextDouble() * 0.1; var m = n * 5 + rand.NextDouble() * 0.2; MeasuredSignal.SignalValues!.Add(m); MeasuredSignal.SignalTimestamps!.Add(DateTime.Now); MeasuredSignal.SignalIds!.Add(i + lastidmeasure); ReferenceSignal.SignalValues!.Add(r); ReferenceSignal.SignalTimestamps!.Add(DateTime.Now); ReferenceSignal.SignalIds!.Add(i + lastidreference); if (K != 0) { CalibratedSignal.SignalValues!.Add(K * m + B); CalibratedSignal.SignalTimestamps!.Add(DateTime.Now); CalibratedSignal.SignalIds!.Add(i + lastidmeasure); } } } [RelayCommand] public void Calculate(int pointsCount) { var x = MeasuredSignal.SignalValues!.TakeLast(pointsCount).ToArray(); var y = ReferenceSignal.SignalValues!.TakeLast(pointsCount).ToArray(); var k = 0.0; var b = 0.0; var n = x.Length; var sumx = 0.0; var sumy = 0.0; var sumxy = 0.0; var sumx2 = 0.0; for (int i = 0; i < n; i++) { sumx += x[i]; sumy += y[i]; sumxy += x[i] * y[i]; sumx2 += x[i] * x[i]; } k = (n * sumxy - sumx * sumy) / (n * sumx2 - sumx * sumx); b = (sumy - k * sumx) / n; K = k; B = b; } } public class ReverseBooleanConverter : BooleanConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return !(bool)value; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return !(bool)value; } //转换成反向值 } //定义消息 // public class CustomizedMessage : ValueChangedMessage<MessageModel> // PropertyChangedMessage // RequestMessage ( Async + ..) // { // public CustomizedMessage(MessageModel value) : base(value) // { // } // }using System; using System.Collections.Generic; 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 ScottPlot; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Threading; using CommunityToolkit.Mvvm.Messaging; using CommunityToolkit.Mvvm.Messaging.Messages; using HandyControl.Controls; using ScottPlot.Legends; using ScottPlot.Plottables; using WPFCalibrationController.Models; using WPFCalibrationController.ViewModels; using MessageBox = HandyControl.Controls.MessageBox; namespace WPFCalibrationController.Views; /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : GlowWindow { private MainViewModel mainViewModel; private DispatcherTimer timer; public MainWindow() { InitializeComponent(); #region 注册并接受消息 //WeakReferenceMessenger.Default.Register<CustomizedMessage>(this, (o, m) => { MessageBox.Show("Received!"); }); #endregion mainViewModel = new MainViewModel(); scottPlot.Plot.XLabel("TimeStamp"); scottPlot.Plot.YLabel("Value"); var sm = scottPlot.Plot.Add.Scatter(mainViewModel.MeasuredSignal.SignalIds!, mainViewModel.MeasuredSignal.SignalValues!, getColor(mainViewModel.MeasuredSignal.SignalColor)); sm.Label = mainViewModel.MeasuredSignal.SignalName; var sr = scottPlot.Plot.Add.Scatter(mainViewModel.ReferenceSignal.SignalIds!, mainViewModel.ReferenceSignal.SignalValues!, getColor(mainViewModel.ReferenceSignal.SignalColor)); sr.Label = mainViewModel.ReferenceSignal.SignalName; scottPlot.Plot.Legend(); var legend = scottPlot.Plot.GetLegend(); legend.Alignment = Alignment.UpperLeft; timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromMilliseconds(500); timer.Tick += (sender, args) => { mainViewModel.GeneratePoints(1); scottPlot.Plot.Clear(); scottPlot.Plot.Add.Scatter(mainViewModel.MeasuredSignal.SignalIds!, mainViewModel.MeasuredSignal.SignalValues!, getColor(mainViewModel.MeasuredSignal.SignalColor)); scottPlot.Plot.Add.Scatter(mainViewModel.ReferenceSignal.SignalIds!, mainViewModel.ReferenceSignal.SignalValues!, getColor(mainViewModel.ReferenceSignal.SignalColor)); scottPlot.Plot.Add.Scatter(mainViewModel.CalibratedSignal.SignalIds!, mainViewModel.CalibratedSignal.SignalValues!, getColor(mainViewModel.CalibratedSignal.SignalColor)); scottPlot.Plot.AutoScale(); scottPlot.Refresh(); }; this.DataContext = mainViewModel; } private Color getColor(SignalModel.SignalColorType colorType) { switch (colorType) { case SignalModel.SignalColorType.Red: return Colors.Red; case SignalModel.SignalColorType.Green: return Colors.Green; case SignalModel.SignalColorType.Blue: return Colors.Blue; case SignalModel.SignalColorType.Brown: return Colors.Brown; case SignalModel.SignalColorType.Purple: return Colors.Purple; case SignalModel.SignalColorType.Yellow: return Colors.Yellow; default: return Colors.Black; } } private void ToggleButton_OnChecked(object sender, RoutedEventArgs e) { timer.Start(); } private void ToggleButton_OnUnchecked(object sender, RoutedEventArgs e) { timer.Stop(); } }