1、先看颜值
此项目又是一个精品力作,麻雀虽小,五脏俱全,颜值不错,绝对干货。
2、项目背景
上位机开发,是目前工业行业智能化发展必不可少的开发技术,每年也有大量的工作需求,希望这套实战对大家有所帮助!根据此项目,可以学习到以WPF框架进行上位机应用开发的过程。 万事开头难,很多事情都是这样,走出第一步,回头再看,发现每一步都是有意义的。因为我们没有经验,那么就需要通过项目来积累,项目哪里来,可以模拟一些项目,可以跟着别人做成型的项目,参考一些项目,应用发挥到实际业务中,就是自己的成果,通过反复缩小差距,不断拔尖。C#WPF借助web开发模式,实现所见所得,它学习了web开发的先进概念,将图形化和业务逻辑分离。特别对上位机开发来说,与硬件设备打交道是必不可少的,本项目与西门子PLC通信,实现了数据与表现实时同步,本项目实属干货,能量满满。
3、项目介绍
1)项目技能点:
Livechart图表控件,用户控件,MVVM模式,WPF渲染,C#异步,西门子PLC,硬件对接,通信协议,不仅要求对C#WPF技术较熟悉,还必须懂得跟硬件PLC通信对接,比如读取PLC数据,如何写入PLC数据。
2)项目开发环境:
开发工具:Visual Studio 2022
开发语言:C#
UI框架:WPF
运行时框架:Framework 4.8
设备:西门子PLC
仿真:博途V15
协议:S7.net
3)设备环境
4)部分代码
注释完备,有利理解
<Window x:Class="West.HeatExchange.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:local="clr-namespace:West.HeatExchange.Views"
mc:Ignorable="d"
xmlns:c="clr-namespace:West.HeatExchange.Controls"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
WindowStyle="None" AllowsTransparency="True" WindowStartupLocation="CenterScreen" ResizeMode="CanResizeWithGrip"
Background="#F7F9FA" FontFamily="Microsoft YaHei" Foreground="#333"
Title="MainWindow" Height="650" Width="1200" >
<!--窗体资源-->
<Window.Resources>
<!--手动模式/自动模式单选控件样式-->
<Style TargetType="RadioButton" x:Key="ModeButtonStyle">
<Setter Property="Background" Value="#FFF0F4F8"/>
<Setter Property="Foreground" Value="Gray"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border Background="{TemplateBinding Background}" Name="bor">
<ContentPresenter Margin="30,5"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<!--触发器-->
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="#FF3BBAFF"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
<!--水泵开样式-->
<Style TargetType="RadioButton" x:Key="LeftSwitchButtonStyle">
<Setter Property="Background" Value="#FFF0F4F8"/>
<Setter Property="Foreground" Value="Gray"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Grid>
<!--绘制下划线-->
<Border BorderBrush="Transparent" BorderThickness="0,0,0,1" Height="30" Margin="5,0" Name="bor" Width="20" HorizontalAlignment="Left"/>
<!--绘制曲线-->
<Path Data="M3 0 45 0 48 3 28 20 3 20 0 17 0 3z" Stroke="#DDD" StrokeThickness="1" Fill="{TemplateBinding Background}" Margin="0,0,2,0" VerticalAlignment="Center"/>
<TextBlock Text="开" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" Margin="0,0,15,0"/>
</Grid>
<!--触发器-->
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="bor" Property="Visibility" Value="Visible"/>
<Setter TargetName="bor" Property="BorderBrush" Value="#FF3BBAFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="#FF3BBAFF"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
<!--水泵关样式-->
<Style TargetType="RadioButton" x:Key="RightSwitchButtonStyle">
<Setter Property="Background" Value="#FFF0F4F8"/>
<Setter Property="Foreground" Value="Gray"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Grid>
<Border BorderBrush="Transparent" BorderThickness="0,0,0,1" Height="30" Margin="8,0" Name="bor" Width="20" HorizontalAlignment="Right"/>
<Path Data="M0 17 20 0 45 0 48 3 48 17 45 20 3 20z" Stroke="#DDD" StrokeThickness="1" Fill="{TemplateBinding Background}" Margin="0,0,2,0" VerticalAlignment="Center"/>
<TextBlock Text="关" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="10" Margin="10,0,0,0"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="bor" Property="Visibility" Value="Visible"/>
<Setter TargetName="bor" Property="BorderBrush" Value="#FF3BBAFF"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="#FF3BBAFF"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
<!--故障样式-->
<Style TargetType="CheckBox" x:Key="WarningButtonStyle">
<Setter Property="FontSize" Value="11"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Opacity" Value="0.2"/>
<Setter Property="Margin" Value="0,2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Grid>
<Border BorderBrush="Red" BorderThickness="1" Padding="1" Name="root">
<Border.Background>
<DrawingBrush TileMode="Tile" Viewport="0,0,0.1,0.2" >
<!--画笔填充-->
<DrawingBrush.Drawing>
<GeometryDrawing>
<GeometryDrawing.Pen>
<Pen Brush="blue" Thickness="10" x:Name="pen"/>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<LineGeometry StartPoint="50,0" EndPoint="0,50"/>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</Border.Background>
<Border Background="Red" Margin="0,6,0,0" BorderBrush="White" BorderThickness="1" Height="20">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" Margin="15,0"/>
</Border>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Opacity" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<DockPanel Background="BlanchedAlmond">
<!--顶部区域,分5列-->
<Grid DockPanel.Dock="Top" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<!--图标-->
<Border Width="45" Grid.Column="0" Background="#5E7593" BorderBrush="#EEE" BorderThickness="1" HorizontalAlignment="Left">
<Image Source="../Assets/Images/title.png" Margin="8"/>
</Border>
<!--背景-->
<Border Grid.ColumnSpan="4" Grid.Column="1" Background="#5E7593" MouseLeftButtonDown="Border_MouseLeftButtonDown"/>
<!--文字-->
<TextBlock Text="换热站远程监控系统" VerticalAlignment="Center" Foreground="White" FontSize="19" Margin="10,0" Grid.Column="1"/>
<!--时间-->
<TextBlock Text="{Binding NowTime, StringFormat={}{0:yyyy年MM月dd日HH时mm分ss秒}}" VerticalAlignment="Center" Foreground="White" FontSize="19" Margin="20,0" Grid.Column="3"/>
<!--退出按钮-->
<Button Grid.Column="4" Width="25" Height="25" Click="Button_Click">
<Button.Background>
<ImageBrush ImageSource="../Assets/Images/closewin.png"/>
</Button.Background>
</Button>
</Grid>
<!--左侧区域,分5行-->
<Border DockPanel.Dock="Left" Width="280" Background="White" CornerRadius="3" Margin="30,10,10,10" >
<Border.Effect>
<DropShadowEffect BlurRadius="10" ShadowDepth="0" Color="LightGray" Opacity="0.3"/>
</Border.Effect>
<Grid Margin="20,10">
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition Height="1.2*"/>
<RowDefinition Height="1.2*"/>
</Grid.RowDefinitions>
<!--第1行,文字栏-->
<Border Grid.Row="0" Background="#3BBAFF" CornerRadius="3" Margin="0,8">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<c:RunLoading Width="23" Height="23" Margin="10,0"/>
<TextBlock Text="换热站综合信息详情" Foreground="White" FontSize="12" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<!--第2行,历史曲线-->
<Grid Grid.Row="1" Background="#FFF0F4F8" Margin="0,5">
<Border Height="16" VerticalAlignment="Bottom" Background="#FF0E3A52" CornerRadius="8"/>
<!--livechart图表配置,Values绑定序列数据-->
<lvc:CartesianChart DisableAnimations="True">
<lvc:CartesianChart.Series>
<lvc:LineSeries Values="29,225,380,97,440,129,532" LineSmoothness="0" Fill="Transparent"/>
</lvc:CartesianChart.Series>
<!--X轴配置-->
<lvc:CartesianChart.AxisX>
<lvc:Axis Labels="周一,周二,周三,周四,周五,周六,周日" FontSize="8" Foreground="White">
<lvc:Axis.Separator>
<lvc:Separator StrokeThickness="0" Step="1"/>
</lvc:Axis.Separator>
</lvc:Axis>
</lvc:CartesianChart.AxisX>
<!--Y轴配置-->
<lvc:CartesianChart.AxisY>
<lvc:Axis MinValue="0" MaxValue="600" Foreground="Transparent"></lvc:Axis>
</lvc:CartesianChart.AxisY>
</lvc:CartesianChart>
<Border Background="#3BBAFF" VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBlock Text="历史曲线" Foreground="White" FontSize="10" Margin="5,1"/>
</Border>
</Grid>
<!--第3行,能耗排名-->
<Grid Grid.Row="2" Background="#FFF0F4F8" Margin="0,5">
<UniformGrid Columns="1">
<!--文字部分-->
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Border Background="#FF3BBAFF">
<TextBlock Text="能耗排名" Foreground="White" FontSize="10" Margin="5,1"/>
</Border>
<TextBlock FontSize="10" VerticalAlignment="Center" Margin="5,0">
<Run Text="耗水" Foreground="#666"/>
<Run Text="" FontFamily="../Assets/Fonts/#iconfont"/>
</TextBlock>
<TextBlock FontSize="10" VerticalAlignment="Center">
<Run Text="耗电" Foreground="#666"/>
<Run Text="" FontFamily="../Assets/Fonts/#iconfont"/>
</TextBlock>
<TextBlock FontSize="10" VerticalAlignment="Center" Margin="5,0">
<Run Text="耗热" Foreground="#666"/>
<Run Text="" FontFamily="../Assets/Fonts/#iconfont"/>
</TextBlock>
</StackPanel>
<!--耗水-->
<Grid Margin="0,0,10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="" FontFamily="../Assets/Fonts/#iconfont" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#FF3BBAFF"/>
<ProgressBar Grid.Column="1" Minimum="0" Maximum="200" Value="182" Background="LightGray" Foreground="Orange" Height="4"/>
</Grid>
<!--耗电-->
<Grid Margin="0,0,10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="" FontFamily="../Assets/Fonts/#iconfont" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#FF3BBAFF"/>
<ProgressBar Grid.Column="1" Minimum="0" Maximum="200" Value="156" Background="LightGray" Foreground="Orange" Height="4"/>
</Grid>
<!--耗热-->
<Grid Margin="0,0,10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="" FontFamily="../Assets/Fonts/#iconfont" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#FF3BBAFF"/>
<ProgressBar Grid.Column="1" Minimum="0" Maximum="200" Value="116" Background="LightGray" Foreground="Orange" Height="4"/>
</Grid>
<!--装饰-->
<Border Height="5" Margin="10,0">
<Border.Background>
<!--线性渐变-->
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Color="#FF3BBAFF" Offset="0"/>
<GradientStop Color="#113BBAFF" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</UniformGrid>
</Grid>
<!--第4行,故障统计-->
<Grid Grid.Row="3" Background="#FFF0F4F8" Margin="0,5">
<Border Height="16" VerticalAlignment="Bottom" Background="#FF0E3A52" CornerRadius="8"/>
<lvc:CartesianChart DisableAnimations="True">
<lvc:CartesianChart.Series>
<lvc:ColumnSeries Values="319,223,63,257,335,440,178,223,53,117" MaxColumnWidth="6"/>
</lvc:CartesianChart.Series>
<lvc:CartesianChart.AxisX>
<lvc:Axis Labels="设备1,设备2,设备3,设备4,设备5,设备6,设备7,设备8,设备9,设备10" FontFamily="Microsoft YaHei" FontSize="6" Foreground="White">
<lvc:Axis.Separator>
<lvc:Separator Step="1" StrokeThickness="0"/>
</lvc:Axis.Separator>
</lvc:Axis>
</lvc:CartesianChart.AxisX>
<lvc:CartesianChart.AxisY>
<lvc:Axis MinValue="0" MaxValue="600" Foreground="Transparent">
<lvc:Axis.Separator>
<lvc:Separator StrokeThickness="1" Stroke="#DDD" Step="760"/>
</lvc:Axis.Separator>
</lvc:Axis>
</lvc:CartesianChart.AxisY>
</lvc:CartesianChart>
<Border Background="#FF3BBAFF" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="2">
<TextBlock Text="故障统计" Foreground="White" FontSize="10" Margin="5,1"/>
</Border>
</Grid>
<!--第5行,数据报表-->
<Grid Grid.Row="4" Background="#FFF0F4F8" Margin="0,5">
<lvc:PieChart InnerRadius="40" Width="110" Height="110" DisableAnimations="True" StartingRotationAngle="0" HoverPushOut="0" >
<!--以下是静态显示-->
<lvc:PieChart.Series>
<lvc:PieSeries Values="23" Fill="#2BBF6E" StrokeThickness="2" DataLabels="True" Foreground="Black" FontSize="11" LabelPosition="OutsideSlice" Title="锻烧车间"></lvc:PieSeries>
<lvc:PieSeries Values="37" Fill="#3DDFEE" StrokeThickness="2" DataLabels="True" Foreground="Black" FontSize="11" LabelPosition="OutsideSlice" Title="模具车间"></lvc:PieSeries>
<lvc:PieSeries Values="80" Fill="Red" StrokeThickness="2" DataLabels="True" Foreground="Black" FontSize="11" LabelPosition="OutsideSlice" Title="电噴车间"></lvc:PieSeries>
<lvc:PieSeries Values="60" Fill="Blue" StrokeThickness="2" DataLabels="True" Foreground="Black" FontSize="11" LabelPosition="OutsideSlice" Title="组装车间"></lvc:PieSeries>
</lvc:PieChart.Series>
</lvc:PieChart>
<Border Background="#FF3BBAFF" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="2">
<TextBlock Text="数据报表" Foreground="White" FontSize="10" Margin="5,1"/>
</Border>
</Grid>
</Grid>
</Border>
<!--右上区域,控制模式-->
<Border Height="80" DockPanel.Dock="Top" Background="White" Margin="0,10,20,0" CornerRadius="3">
<Border.Effect>
<DropShadowEffect BlurRadius="10" ShadowDepth="0" Color="LightGray" Opacity="0.3"/>
</Border.Effect>
<UniformGrid Rows="1">
<!--<Button Content="连接PLC" Width="60" Height="40" Click="Button_Click_1" ></Button>-->
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<RadioButton Content="手动模式" IsChecked="True" Style="{StaticResource ModeButtonStyle}"/>
<RadioButton Content="自动模式" Style="{StaticResource ModeButtonStyle}"/>
</StackPanel>
<!--补水泵1-->
<Grid Background="#88F0F4F8" VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="No.1 补水泵" Margin="20,3" HorizontalAlignment="Center"/>
<Grid Grid.Row="1" Margin="10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="15"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<RadioButton Grid.Column="0" IsChecked="true" Command="{Binding StartCommand}" CommandParameter="1" Grid.ColumnSpan="2" Style="{StaticResource LeftSwitchButtonStyle}" Name="rb1o"/>
<RadioButton Grid.Column="1" Command="{Binding StopCommand}" CommandParameter="1" Grid.ColumnSpan="2" Style="{StaticResource RightSwitchButtonStyle}" Name="rb1c"/>
</Grid>
</Grid>
<!--补水泵2-->
<Grid Background="#88F0F4F8" VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="No.2 补水泵" Margin="20,3" HorizontalAlignment="Center"/>
<Grid Grid.Row="1" Margin="10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="15"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<RadioButton IsChecked="True" Grid.ColumnSpan="2" Command="{Binding StartCommand}" CommandParameter="2" Style="{StaticResource LeftSwitchButtonStyle}" Name="rb2o"/>
<RadioButton Grid.Column="1" Grid.ColumnSpan="2" Command="{Binding StopCommand}" CommandParameter="2" Style="{StaticResource RightSwitchButtonStyle}" Name="rb2c"/>
</Grid>
</Grid>
<!--补水泵3-->
<Grid Background="#88F0F4F8" VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="No.3 补水泵" Margin="20,3" HorizontalAlignment="Center"/>
<Grid Grid.Row="1" Margin="10,0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="15"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<RadioButton IsChecked="True" Grid.ColumnSpan="2" Command="{Binding StartCommand}" CommandParameter="3" Style="{StaticResource LeftSwitchButtonStyle}" Name="rb3o"/>
<RadioButton Grid.Column="1" Grid.ColumnSpan="2" Command="{Binding StopCommand}" CommandParameter="3" Style="{StaticResource RightSwitchButtonStyle}" Name="rb3c"/>
</Grid>
</Grid>
<!--故障切换-->
<Grid Background="#88F0F4F8" VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<CheckBox Content="循环变频故障" Grid.Row="0" Style="{StaticResource WarningButtonStyle}" IsChecked="True"/>
<CheckBox Content="补水变频故障" Grid.Row="1" Style="{StaticResource WarningButtonStyle}"/>
</Grid>
</UniformGrid>
</Border>
<!--右中区域,系统运行-->
<Border Background="White" Margin="0,10,20,10" CornerRadius="3" >
<Border.Effect>
<DropShadowEffect BlurRadius="10" ShadowDepth="0" Color="LightGray" Opacity="0.3"/>
</Border.Effect>
<Canvas>
<!--管道-->
<c:PipeLine Height="7" Width="839" Direction="EW" HorizontalAlignment="Left" VerticalAlignment="Center" Canvas.Left="10" Canvas.Top="44"/>
<c:PipeLine x:Name="middle" Panel.ZIndex="3" Canvas.Left="234" Canvas.Top="49" Direction="EW" Height="7" Width="182" CapRadius="3">
<c:PipeLine.RenderTransform>
<TransformGroup>
<RotateTransform Angle="90" ></RotateTransform>
</TransformGroup>
</c:PipeLine.RenderTransform>
</c:PipeLine>
<c:PipeLine Height="7" Width="619" Direction="EW" HorizontalAlignment="Left" VerticalAlignment="Center" Canvas.Left="230" Canvas.Top="224"/>
<c:PipeLine x:Name="middle2" Panel.ZIndex="3" Canvas.Left="164" Canvas.Top="139.5" Direction="{Binding WaterDirection2}" Height="7" Width="185" CapRadius="3">
<c:PipeLine.RenderTransform>
<TransformGroup>
<RotateTransform Angle="90" ></RotateTransform>
</TransformGroup>
</c:PipeLine.RenderTransform>
</c:PipeLine>
<c:PipeLine Height="7" Width="399" Direction="{Binding WaterDirection2}" HorizontalAlignment="Left" VerticalAlignment="Center" Canvas.Left="450" Canvas.Top="296" />
<c:PipeLine x:Name="right" Panel.ZIndex="3" Margin="0" Canvas.Left="223" Canvas.Top="302" Width="131" Direction="{Binding WaterDirection32}" Height="7" CapRadius="1">
<c:PipeLine.RenderTransform>
<TransformGroup>
<TranslateTransform X="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Canvas}, Path=ActualHeight}" Y="0"></TranslateTransform>
<RotateTransform Angle="90" CenterX="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Canvas}, Path=ActualHeight}" CenterY="0"></RotateTransform>
</TransformGroup>
</c:PipeLine.RenderTransform>
</c:PipeLine>
<c:PipeLine Height="7" Width="79" Direction="{Binding WaterDirection3}" HorizontalAlignment="Left" VerticalAlignment="Center" Canvas.Left="640" Canvas.Top="426" />
<c:PipeLine Height="7" Width="79" Direction="{Binding WaterDirection3}" HorizontalAlignment="Left" VerticalAlignment="Center" Canvas.Left="540" Canvas.Top="447" />
<c:PipeLine Height="7.5" Width="409" Direction="{Binding WaterDirection1}" HorizontalAlignment="Left" VerticalAlignment="Center" Canvas.Left="10" Canvas.Top="137"/>
<c:PipeLine Height="7.5" Width="259" Direction="{Binding WaterDirection2}" HorizontalAlignment="Left" VerticalAlignment="Center" Canvas.Left="158" Canvas.Top="317"/>
<c:PipeLine Height="7.5" Width="459" Direction="{Binding WaterDirection3}" HorizontalAlignment="Left" VerticalAlignment="Center" Canvas.Left="10" Canvas.Top="447"/>
<c:PipeLine Height="7.5" Width="399" Direction="{Binding WaterDirection1}" HorizontalAlignment="Left" VerticalAlignment="Center" Canvas.Left="450" Canvas.Top="116"/>
<!--图片-->
<Image Source="../Assets/Images/d1.jpg" Canvas.Left="35" Canvas.Top="29" Height="39" Width="61" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/d1.jpg" Canvas.Left="755" Canvas.Top="29" Height="39" Width="61" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/d1.jpg" Canvas.Left="755" Canvas.Top="279" Height="39" Width="61" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/d1.jpg" Panel.ZIndex="3" Canvas.Left="95" Canvas.Top="429" Height="39" Width="61" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/heat_exchange.jpg" Panel.ZIndex="2" Canvas.Left="264" Canvas.Top="20" Height="156" Width="80" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/heat_exchange.jpg" Panel.ZIndex="2" Canvas.Left="264" Canvas.Top="200" Height="156" Width="80" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/i2.jpg" Canvas.Left="164" Canvas.Top="13" Height="31" Width="25" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/i2.jpg" Canvas.Left="424" Canvas.Top="13" Height="31" Width="25" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/i2.jpg" Canvas.Left="524" Canvas.Top="85" Height="31" Width="25" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/i2.jpg" Canvas.Left="684" Canvas.Top="193" Height="31" Width="25" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/i2.jpg" Canvas.Left="584" Canvas.Top="265" Height="31" Width="25" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/f2.jpg" Panel.ZIndex="2" Canvas.Left="64" Canvas.Top="115" Height="33" Width="34" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Image Source="../Assets/Images/f2.jpg" Panel.ZIndex="2" Canvas.Left="224" Canvas.Top="425" Height="33" Width="34" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<!--3个水泵-->
<c:Pump Width="42" Height="38" IsRunning="{Binding Pump1State}" Canvas.Left="411" Canvas.Top="111" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<c:Pump Width="42" Height="38" IsRunning="{Binding Pump2State}" Canvas.Left="411" Canvas.Top="291" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<c:Pump Width="42" Height="38" IsRunning="{Binding Pump3State}" Canvas.Left="611" Canvas.Top="421" HorizontalAlignment="Center" VerticalAlignment="Top"/>
<!--数据看板-->
<c:DataBoard Width="75" Height="40" Canvas.Left="30" Canvas.Top="71" ItemsSource="{Binding MainData.MomentDataList1}"/>
<c:DataBoard Width="75" Height="40" Canvas.Left="135" Canvas.Top="56" ItemsSource="{Binding MainData.MomentDataList1}"/>
<c:DataBoard Width="75" Height="40" Canvas.Left="545" Canvas.Top="71" ItemsSource="{Binding MainData.MomentDataList1}"/>
<c:DataBoard Width="75" Height="40" Canvas.Left="495" Canvas.Top="252" ItemsSource="{Binding MainData.MomentDataList2}"/>
<c:DataBoard Width="75" Height="40" Canvas.Left="595" Canvas.Top="178" ItemsSource="{Binding MainData.MomentDataList2}"/>
<c:DataBoard Width="75" Height="40" Canvas.Left="90" Canvas.Top="383" ItemsSource="{Binding MainData.MomentDataList3}"/>
<c:CoolingTower RunningState="Normal" Width="140" Height="150" Canvas.Left="415" Canvas.Top="347"></c:CoolingTower>
</Canvas>
</Border>
</DockPanel>
</Window>
using LiveCharts.Maps;
using S7.Net;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using West.HeatExchange.Base;
using West.HeatExchange.Communication;
using West.HeatExchange.Controls;
using West.HeatExchange.Models;
namespace West.HeatExchange.ViewModels
{
public class MainViewModel : ViewModelBase
{
CancellationTokenSource cts = new CancellationTokenSource();
List<Task> tasks = new List<Task>();
//水泵1地址
readonly string pumpaddr1 =ConfigurationManager.AppSettings["pumpaddr1"].ToString();
//水泵2地址
readonly string pumpaddr2 = ConfigurationManager.AppSettings["pumpaddr2"].ToString();
//水泵3地址
readonly string pumpaddr3 = ConfigurationManager.AppSettings["pumpaddr3"].ToString();
//PLC地址
readonly static string plcaddress = ConfigurationManager.AppSettings["plcaddress"].ToString();
//数据看板变量
public MainModel MainData { get; set; } = new MainModel();
//Plc对象
S7.Net.Plc plc = new S7.Net.Plc(S7.Net.CpuType.S71500, plcaddress, 0, 1);
private DateTime nowTime = DateTime.Now;
public MainViewModel()
{
ChangeTime();
ReadmyPLC();
}
#region 方法
private void ReadmyPLC()
{
tasks.Add(Task.Run(async () =>
{
plc.Open();
while (!cts.IsCancellationRequested)
{
await Task.Delay(2000);
//读取float,从左到右参数依次是:数据块类型,数据块号,启始地址,变量类型,读取数量
var result = plc.Read(S7.Net.DataType.DataBlock, 1, 2, S7.Net.VarType.Real, 6);
float[] values = (float[])result;
//水泵1数据
MainData.MomentDataList1[0].Value = values[0];
MainData.MomentDataList1[1].Value = values[1];
//水泵2数据
MainData.MomentDataList2[0].Value = values[2];
MainData.MomentDataList2[1].Value = values[3];
//水泵3数据
MainData.MomentDataList3[0].Value = values[4];
MainData.MomentDataList3[1].Value = values[5];
//读取bool,DB1.DBX0.0是变量的具体地址
var result2 = plc.Read(pumpaddr1); //水泵1状态
Pump1State = bool.Parse(result2.ToString());
if (!Pump1State)
{
WaterDirection1 = "NONE";//停止流动
}
else
{
WaterDirection1 = "EW";//开始流动
}
result2 = plc.Read(pumpaddr2);//水泵2状态
Pump2State = bool.Parse(result2.ToString());
if (!Pump2State)
{
WaterDirection2 = "NONE";
}
else
{
WaterDirection2 = "EW";
}
result2 = plc.Read(pumpaddr3); //水泵3状态
Pump3State = bool.Parse(result2.ToString());
if (!Pump3State)
{
WaterDirection3 = "NONE";
WaterDirection32 = "NONE";
}
else
{
WaterDirection3 = "EW";
WaterDirection32 = "WE";
}
}
}, cts.Token)
);
}
private void ChangeTime()
{
//每隔1秒更新时间
Task.Run(() => {
while (true)
{
Thread.Sleep(1000);
string txtDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
NowTime = Convert.ToDateTime(txtDate);
}
});
}
/// <summary>
/// 启动或停止水泵
/// </summary>
/// <param name="addr"></param>
private void Changepumpstate(string addr)
{
try
{
var obj = plc.Read(addr);
bool flag = (bool)obj;
if (flag)
{
flag = false;
}
else
{
flag = true;
}
plc.Write(addr, flag);
}
catch
{
MessageBox.Show("写入失败");
}
}
#endregion
#region 属性
/// <summary>
/// 系统时间
/// </summary>
public DateTime NowTime
{
get { return nowTime; }
set
{
nowTime = value;
OnPropertyChanged();
}
}
private bool pump1State = true;
/// <summary>
/// 水泵1状态
/// </summary>
public bool Pump1State
{
get
{
return pump1State;
}
set
{
pump1State = value;
OnPropertyChanged();
}
}
private bool pump2State = true;
/// <summary>
/// 水泵2状态
/// </summary>
public bool Pump2State
{
get
{
return pump2State;
}
set
{
pump2State = value;
OnPropertyChanged();
}
}
private bool pump3State = true;
/// <summary>
/// 水泵3状态
/// </summary>
public bool Pump3State
{
get
{
return pump3State;
}
set
{
pump3State = value;
OnPropertyChanged();
}
}
private string waterDirection1 = "EW";
/// <summary>
/// 水泵1水流状态
/// </summary>
public string WaterDirection1
{
get
{
return waterDirection1;
}
set
{
waterDirection1 = value;
OnPropertyChanged();
}
}
private string waterDirection2 = "EW";
/// <summary>
/// 水泵2水流状态
/// </summary>
public string WaterDirection2
{
get
{
return waterDirection2;
}
set
{
waterDirection2 = value;
OnPropertyChanged();
}
}
private string waterDirection3 = "EW";
/// <summary>
/// 水泵3水流状态
/// </summary>
public string WaterDirection3
{
get
{
return waterDirection3;
}
set
{
waterDirection3 = value;
OnPropertyChanged();
}
}
private string waterDirection32 = "WE";
/// <summary>
/// 水泵3水流状态
/// </summary>
public string WaterDirection32
{
get
{
return waterDirection32;
}
set
{
waterDirection32 = value;
OnPropertyChanged();
}
}
#endregion
#region 命令
/// </summary>
/// <summary>
/// 关按钮的命令处理
/// </summary>
public ICommand StopCommand
{
get
{
return new RelayCommand(o =>
{
//获取传递过来的参数值
string s = o.ToString();
//根据不同的值区分是哪个关的按钮
switch (s)
{
case "1":
//Pump1State = false;
//WaterDirection1 = "NONE";
Changepumpstate(pumpaddr1);
break;
case "2":
//Pump2State = false;
//WaterDirection2 = "NONE";
Changepumpstate(pumpaddr2);
break;
case "3":
//Pump3State = false;
//WaterDirection3 = "NONE";
//WaterDirection32 = "NONE";
Changepumpstate(pumpaddr3);
break;
}
});
}
}
/// <summary>
/// 开按钮命令的处理
/// </summary>
public ICommand StartCommand
{
get
{
return new RelayCommand(o =>
{
string s = o.ToString();
switch (s)
{
case "1":
//Pump1State = true;
//WaterDirection1 = "EW";
Changepumpstate(pumpaddr1);
break;
case "2":
//Pump2State = true;
//WaterDirection2 = "EW";
Changepumpstate(pumpaddr2);
break;
case "3":
//Pump3State = true;
//WaterDirection3 = "EW";
//WaterDirection32 = "WE";
Changepumpstate(pumpaddr3);
break;
}
});
}
}
#endregion
}
}
5)运行效果
4、小结
此实战以WPF为基础UI框架,展示了《换热站监控系统》上位机应用的全套实现过程,从界面功能布局、模拟组态业务到控件模板、图表、自定义控件,再到设备通信、页面数据实时更新、多线程相关操作等,众多实用功能完整落地。
希望以此作为本教程的精品,奉献给小伙伴,我也是帅呆了,感谢大家火辣辣的支持。
走过路过不要错过,点赞关注收藏又圈粉,共同致富,为财务自由作出贡献