Model层为什么要加 [Serializable]和 [DataContract],他们的作用是什么?

本文介绍了数据契约(DataContract)及序列化标记的使用方法。详细讲述了不同标记对字段序列化的影响,包括XML映射、[Serializable]标记以及[DataContract]与[DataMember]联合使用的区别。此外还解释了数据契约在服务端和客户端间传送自定义数据类型的作用。
1.          不给任何标记将会做XML映射,所有公有属性/字段都会被序列化
2.          [Serializable]标记会将所有可读写字段序列化
3.        [DataContract]和[DataMember]联合使用来标记被序列化的字段

数据契约(DataContract)

服务契约定义了远程访问对象和可供调用的方法,数据契约则是服务端和客户端之间要传送的自定义数据类型。

一旦声明一个类型为DataContract,那么该类型就可以被序列化在服务端和客户端之间传送,如下所示。

      [DataContract]

     public class UserInfo

     {

          //….

    }

<Window x:Class="_9696999.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:local="clr-namespace:_9696999" mc:Ignorable="d" Title="MainWindow" Height="650" Width="1200"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!-- 通讯控制栏 --> <GroupBox Header="通讯控制" Grid.Row="0" Margin="10"> <StackPanel Orientation="Horizontal" Height="40"> <TextBlock Text="MX-COMPONENT站号:" Margin="10,0,0,0" VerticalAlignment="Center"/> <TextBox x:Name="txtStationNumber" Width="50" Margin="10,0,0,0" VerticalAlignment="Center" Text="1"/> <Button x:Name="btnConnect" Content="连接PLC" Width="80" Margin="10" Click="Connect_Click"/> <Button x:Name="btnDisconnect" Content="断开连接" Width="80" Margin="10" Click="Disconnect_Click" IsEnabled="False"/> <TextBlock x:Name="tbConnectionStatus" Text="未连接" Margin="10" VerticalAlignment="Center"/> </StackPanel> </GroupBox> <!-- 数据监控区域 --> <GroupBox Header="点位数据监控" Grid.Row="1" Margin="10"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <!-- 第一列 --> <StackPanel Grid.Column="0"> <!-- 电机点位数据监控 --> <GroupBox Header="电机点位监控" Margin="5"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <!-- 监控点位配置栏 --> <StackPanel Grid.Column="0" Margin="5"> <TextBlock Text="监控点位配置" FontWeight="Bold" Margin="0,0,0,10"/> <TextBlock Text="监控点位名称:" Margin="0,5"/> <TextBox x:Name="txtPointName" Margin="0,2,0,10" Width="180"/> <!-- 新增地址栏 --> <TextBlock Text="地址:" Margin="0,5"/> <TextBox x:Name="txtPointAddress" Margin="0,2,0,10" Width="180"/> <TextBlock Text="标准值:" Margin="0,5"/> <TextBox x:Name="txtStandardValue" Margin="0,2,0,10" Width="180"/> <Button Content="添监控点" Width="100" Margin="0,10" Click="AddMonitorPoint_Click"/> </StackPanel> <!-- 点位数据列表 --> <ListView x:Name="lvMotorData" Grid.Column="1" Margin="5" ItemsSource="{Binding MotorDataList}"> <ListView.View> <GridView> <GridViewColumn Header="监控点位名称" Width="150"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding PointName}" VerticalAlignment="Center"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <!-- 新增地址列 --> <GridViewColumn Header="地址" Width="100"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Address}" VerticalAlignment="Center"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="标准值" Width="100"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding StandardValue}" VerticalAlignment="Center"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="实际值" Width="100"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding ActualValue}" VerticalAlignment="Center"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="状态" Width="80"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding Status}" VerticalAlignment="Center"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> </GridView> </ListView.View> </ListView> </Grid> </GroupBox> <!-- 通讯日志 --> <GroupBox Header="通讯日志" Margin="5"> <TextBox x:Name="txtCommunicationLog" IsReadOnly="True" VerticalScrollBarVisibility="Auto" Height="150"/> </GroupBox> </StackPanel> <!-- 第二列 --> <StackPanel Grid.Column="1"> <!-- 设备节拍时间监控 --> <GroupBox Header="设备节拍时间监控" Margin="5"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <!-- 监控点位配置栏 --> <StackPanel Grid.Column="0" Margin="5"> <TextBlock Text="节拍监控配置" FontWeight="Bold" Margin="0,0,0,10"/> <TextBlock Text="部位别名称:" Margin="0,5"/> <TextBox x:Name="txtSectionName" Margin="0,2,0,10" Width="180"/> <TextBlock Text="地址:" Margin="0,5"/> <TextBox x:Name="txtSectionAddress" Margin="0,2,0,10" Width="180"/> <Button Content="添监控部位" Width="120" Margin="0,10" Click="AddSection_Click"/> </StackPanel> <!-- 节拍时间柱状图 --> <Canvas x:Name="canvasRhythmChart" Grid.Column="1" Margin="5" Background="White" Height="200"/> </Grid> </GroupBox> <!-- 伺服电机负载率监控 --> <GroupBox Header="伺服电机负载率监控" Margin="5"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <!-- 监控点位配置栏 --> <StackPanel Grid.Column="0" Margin="5"> <TextBlock Text="负载监控配置" FontWeight="Bold" Margin="0,0,0,10"/> <TextBlock Text="伺服电机名称:" Margin="0,5"/> <TextBox x:Name="txtMotorName" Margin="0,2,0,10" Width="180"/> <TextBlock Text="地址:" Margin="0,5"/> <TextBox x:Name="txtMotorAddress" Margin="0,2,0,10" Width="180"/> <Button Content="添监控电机" Width="120" Margin="0,10" Click="AddMotor_Click"/> </StackPanel> <!-- 负载率柱状图 --> <Canvas x:Name="canvasLoadChart" Grid.Column="1" Margin="5" Background="White" Height="200"/> </Grid> </GroupBox> </StackPanel> </Grid> </GroupBox> </Grid> </Window>using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using ActUtlType64Lib; using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Timers; using System.Windows.Threading; using System.Collections.ObjectModel; namespace _9696999 { public partial class MainWindow : Window { private PlcMonitorViewModel _viewModel; private ActUtlType64 _plc; private DispatcherTimer _readTimer; private bool _isConnected = false; // 存储上一次的值用于比较变化 private ObservableCollection<float> _lastMotorValues = new ObservableCollection<float>(); private ObservableCollection<float> _lastRhythmValues = new ObservableCollection<float>(); private ObservableCollection<float> _lastLoadValues = new ObservableCollection<float>(); public MainWindow() { InitializeComponent(); _viewModel = new PlcMonitorViewModel(); DataContext = _viewModel; InitializePlc(); } private void InitializePlc() { try { _plc = new ActUtlType64(); // 不再在此处设置站号,将在连接时根据用户输入设置 } catch (Exception ex) { MessageBox.Show($"初始化PLC通信失败: {ex.Message}\n请确保已安装MX Component运行时库。"); } } // 连接PLC private void Connect_Click(object sender, RoutedEventArgs e) { // 验证站号输入 if (!int.TryParse(txtStationNumber.Text, out int stationNumber) || stationNumber < 1 || stationNumber > 255) { MessageBox.Show("请输入有效的站号 (1-255)"); return; } try { // 设置站号 _plc.ActLogicalStationNumber = stationNumber; int result = _plc.Open(); if (result == 0) // 返回0表示成功 { _isConnected = true; tbConnectionStatus.Text = $"已连接 (站号: {stationNumber})"; btnConnect.IsEnabled = false; btnDisconnect.IsEnabled = true; txtStationNumber.IsEnabled = false; // 连接后禁用站号输入 StartMonitoring(); AddLog($"PLC连接成功 (站号: {stationNumber})"); } else { MessageBox.Show($"连接失败,错误代码: {result}"); AddLog($"PLC连接失败,错误代码: {result}"); } } catch (Exception ex) { MessageBox.Show($"连接失败: {ex.Message}"); AddLog($"PLC连接异常: {ex.Message}"); } } // 启动监控定时器 private void StartMonitoring() { _readTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) // 200ms采集周期 }; _readTimer.Tick += async (s, e) => await ReadPlcDataAsync(); _readTimer.Start(); } // 异步读取PLC数据 private async System.Threading.Tasks.Task ReadPlcDataAsync() { if (!_isConnected) return; try { await System.Threading.Tasks.Task.Run(() => { // 读取电机点位数据 ReadMotorData(); // 读取节拍时间数据 ReadRhythmData(); // 读取负载率数据 ReadLoadData(); }); } catch (Exception ex) { AddLog($"读取数据异常: {ex.Message}"); } } // 读取电机点位数据 private void ReadMotorData() { for (int i = 0; i < _viewModel.MotorDataList.Count; i++) { short value = 0; // 使用配置的地址读取数据 int result = _plc.ReadDeviceRandom2(_viewModel.MotorDataList[i].Address, 1, out value); if (result == 0) { float actualValue = value; Application.Current.Dispatcher.Invoke(() => { var data = _viewModel.MotorDataList[i]; data.ActualValue = actualValue; // 检查值是否发生变化 if (_lastMotorValues.Count <= i) { _lastMotorValues.Add(actualValue); // 第一次读取时也设置状态 data.Status = (actualValue == data.StandardValue) ? "正常" : "异常"; // 如果是异常状态,记录日志 if (data.Status == "异常") { AddLog($"点位 {data.PointName} 异常: 实际值 {actualValue} ≠ 标准值 {data.StandardValue}"); } } else if (_lastMotorValues[i] != actualValue) { // 值发生变化,更新状态并记录日志 data.Status = (actualValue == data.StandardValue) ? "正常" : "异常"; if (data.Status == "异常") { AddLog($"点位 {data.PointName} 异常: 实际值 {actualValue} ≠ 标准值 {data.StandardValue}"); } _lastMotorValues[i] = actualValue; } }); } else { AddLog($"读取点位 {_viewModel.MotorDataList[i].PointName} 失败,错误代码: {result}"); } } } // 读取节拍时间数据并绘制柱状图 private void ReadRhythmData() { for (int i = 0; i < _viewModel.RhythmDataList.Count; i++) { short value = 0; int result = _plc.ReadDeviceRandom2(_viewModel.RhythmDataList[i].Address, 1, out value); if (result == 0) { float rhythmValue = value / 10.0f; // 数值转换:25 → 2.5 Application.Current.Dispatcher.Invoke(() => { // 使用转换后的值,而不是原始值 _viewModel.RhythmDataList[i].Value = rhythmValue; DrawBarChart(canvasRhythmChart, _viewModel.RhythmDataList, true); }); } else { AddLog($"读取节拍数据 {_viewModel.RhythmDataList[i].Name} 失败,错误代码: {result}"); } } } // 读取负载率数据并绘制柱状图 private void ReadLoadData() { for (int i = 0; i < _viewModel.LoadDataList.Count; i++) { short value = 0; int result = _plc.ReadDeviceRandom2(_viewModel.LoadDataList[i].Address, 1, out value); if (result == 0) { float loadValue = value; Application.Current.Dispatcher.Invoke(() => { _viewModel.LoadDataList[i].Value = value; DrawBarChart(canvasLoadChart, _viewModel.LoadDataList, true); }); } else { AddLog($"读取负载数据 {_viewModel.LoadDataList[i].Name} 失败,错误代码: {result}"); } } } // 绘制柱状图 private void DrawBarChart(Canvas canvas, ObservableCollection<ChartData> dataList, bool showValue) { // 确保在UI线程上执行 if (!Application.Current.Dispatcher.CheckAccess()) { Application.Current.Dispatcher.Invoke(() => DrawBarChart(canvas, dataList, showValue)); return; } canvas.Children.Clear(); if (dataList.Count == 0) return; double canvasWidth = canvas.ActualWidth; double canvasHeight = canvas.ActualHeight; double barWidth = (canvasWidth - 40) / dataList.Count - 10; // 为坐标轴留出空间 double maxValue = GetMaxValue(dataList); // 绘制Y轴 Line yAxis = new Line { X1 = 30, Y1 = 20, X2 = 30, Y2 = canvasHeight - 30, Stroke = Brushes.Black, StrokeThickness = 2 }; canvas.Children.Add(yAxis); // 绘制X轴 Line xAxis = new Line { X1 = 30, Y1 = canvasHeight - 30, X2 = canvasWidth - 10, Y2 = canvasHeight - 30, Stroke = Brushes.Black, StrokeThickness = 2 }; canvas.Children.Add(xAxis); // 绘制Y轴刻度 for (int i = 0; i <= 5; i++) { double yPos = canvasHeight - 30 - (i * (canvasHeight - 50) / 5); double value = i * maxValue / 5; // 刻度线 Line tick = new Line { X1 = 25, Y1 = yPos, X2 = 30, Y2 = yPos, Stroke = Brushes.Black, StrokeThickness = 1 }; canvas.Children.Add(tick); // 刻度值 TextBlock tickValue = new TextBlock { Text = value.ToString("F1"), FontSize = 10, Foreground = Brushes.Black }; Canvas.SetLeft(tickValue, 5); Canvas.SetTop(tickValue, yPos - 8); canvas.Children.Add(tickValue); } for (int i = 0; i < dataList.Count; i++) { double barHeight = (dataList[i].Value / maxValue) * (canvasHeight - 60); double x = 40 + i * (barWidth + 10); double y = canvasHeight - 30 - barHeight; // 绘制柱形 System.Windows.Shapes.Rectangle rect = new System.Windows.Shapes.Rectangle { Width = barWidth, Height = barHeight, Fill = Brushes.SteelBlue, Stroke = Brushes.Black, StrokeThickness = 1 }; Canvas.SetLeft(rect, x); Canvas.SetTop(rect, y); canvas.Children.Add(rect); // 绘制数值 if (showValue) { TextBlock text = new TextBlock { Text = dataList[i].Value.ToString("F1"), FontSize = 10, Foreground = Brushes.Black }; Canvas.SetLeft(text, x + barWidth / 2 - 10); Canvas.SetTop(text, y - 20); canvas.Children.Add(text); } // 绘制名称 TextBlock nameText = new TextBlock { Text = dataList[i].Name, FontSize = 10, Foreground = Brushes.Black, TextAlignment = TextAlignment.Center, Width = barWidth + 10 }; Canvas.SetLeft(nameText, x - 5); Canvas.SetTop(nameText, canvasHeight - 25); canvas.Children.Add(nameText); } } // 获取最大值用于比例计算 private float GetMaxValue(ObservableCollection<ChartData> dataList) { float max = 0; foreach (var item in dataList) { if (item.Value > max) max = item.Value; } return max > 0 ? max : 1; // 避免除以零 } // 添日志 private void AddLog(string message) { Application.Current.Dispatcher.Invoke(() => { txtCommunicationLog.AppendText($"{DateTime.Now:HH:mm:ss} {message}\n"); txtCommunicationLog.ScrollToEnd(); }); } // 断开连接 private void Disconnect_Click(object sender, RoutedEventArgs e) { try { _plc.Close(); _isConnected = false; _readTimer.Stop(); tbConnectionStatus.Text = "未连接"; btnConnect.IsEnabled = true; btnDisconnect.IsEnabled = false; txtStationNumber.IsEnabled = true; // 断开连接后允许修改站号 AddLog("PLC连接已断开"); } catch (Exception ex) { MessageBox.Show($"断开连接失败: {ex.Message}"); AddLog($"断开连接异常: {ex.Message}"); } } // 添监控点 private void AddMonitorPoint_Click(object sender, RoutedEventArgs e) { if (string.IsNullOrWhiteSpace(txtPointName.Text) || string.IsNullOrWhiteSpace(txtPointAddress.Text) || // 检查地址 string.IsNullOrWhiteSpace(txtStandardValue.Text)) { MessageBox.Show("请填写完整的监控点信息"); return; } if (!float.TryParse(txtStandardValue.Text, out float standardValue)) { MessageBox.Show("标准值必须是有效的数字"); return; } _viewModel.MotorDataList.Add(new MotorPointData { PointName = txtPointName.Text, Address = txtPointAddress.Text, // 添地址 StandardValue = standardValue, ActualValue = 0, Status = "未知" }); // 清空输入框 txtPointName.Text = ""; txtPointAddress.Text = ""; txtStandardValue.Text = ""; } // 添节拍监控部位 private void AddSection_Click(object sender, RoutedEventArgs e) { if (string.IsNullOrWhiteSpace(txtSectionName.Text) || string.IsNullOrWhiteSpace(txtSectionAddress.Text)) { MessageBox.Show("请填写完整的部位信息"); return; } _viewModel.RhythmDataList.Add(new ChartData { Name = txtSectionName.Text, Address = txtSectionAddress.Text, Value = 0 }); // 清空输入框 txtSectionName.Text = ""; txtSectionAddress.Text = ""; } // 添负载监控电机 private void AddMotor_Click(object sender, RoutedEventArgs e) { if (string.IsNullOrWhiteSpace(txtMotorName.Text) || string.IsNullOrWhiteSpace(txtMotorAddress.Text)) { MessageBox.Show("请填写完整的电机信息"); return; } _viewModel.LoadDataList.Add(new ChartData { Name = txtMotorName.Text, Address = txtMotorAddress.Text, Value = 0 }); // 清空输入框 txtMotorName.Text = ""; txtMotorAddress.Text = ""; } // 窗口关闭时清理资源 protected override void OnClosed(EventArgs e) { base.OnClosed(e); if (_isConnected) { _plc.Close(); _readTimer.Stop(); } } } }using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections.ObjectModel; using System.ComponentModel; namespace _9696999 { public class MotorPointData : INotifyPropertyChanged { private string? _pointName; private string? _address; private float _standardValue; private float _actualValue; private string? _status; public string? PointName { get => _pointName; set { _pointName = value; OnPropertyChanged(nameof(PointName)); } } public string? Address { get => _address; set { _address = value; OnPropertyChanged(nameof(Address)); } } public float StandardValue { get => _standardValue; set { _standardValue = value; OnPropertyChanged(nameof(StandardValue)); } } public float ActualValue { get => _actualValue; set { _actualValue = value; OnPropertyChanged(nameof(ActualValue)); } } public string? Status { get => _status; set { _status = value; OnPropertyChanged(nameof(Status)); } } public event PropertyChangedEventHandler? PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } // 图表数据模型 public class ChartData { public string? Name { get; set; } public string? Address { get; set; } public float Value { get; set; } } // 主ViewModel public class PlcMonitorViewModel { public ObservableCollection<MotorPointData> MotorDataList { get; set; } public ObservableCollection<ChartData> RhythmDataList { get; set; } public ObservableCollection<ChartData> LoadDataList { get; set; } public PlcMonitorViewModel() { MotorDataList = new ObservableCollection<MotorPointData>(); RhythmDataList = new ObservableCollection<ChartData>(); LoadDataList = new ObservableCollection<ChartData>(); } } } 上面代码我想在优化一下,在三个配置点位部分添一键保存配置(存到电脑D盘中)一键删除配置
最新发布
09-11
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值