C# WPF 上位机开发:信号采集校准平台
By
jerryxjr1220
at 2023-10-20 • 0人收藏 • 461人看过
<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
登录后方可回帖
再增加一个校准后的信号,可以实时展示校准后信号对比源信号的误差。