WPF真入门教程33--上干货【换热站智能监控系统】

news2024/11/27 4:30:52

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="&#xe92e;" FontFamily="../Assets/Fonts/#iconfont"/>
                            </TextBlock>
                            <TextBlock FontSize="10" VerticalAlignment="Center">
                                <Run Text="耗电" Foreground="#666"/>
                               <Run Text="&#xe8b6;" FontFamily="../Assets/Fonts/#iconfont"/>
                            </TextBlock>
                            <TextBlock  FontSize="10" VerticalAlignment="Center" Margin="5,0">
                                <Run Text="耗热" Foreground="#666"/>
                                <Run Text="&#xe60e;" FontFamily="../Assets/Fonts/#iconfont"/>
                            </TextBlock>
                        </StackPanel>
                        <!--耗水-->
                        <Grid Margin="0,0,10,0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="20"/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <TextBlock Text="&#xe92e;" 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="&#xe8b6;" 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="&#xe60e;" 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框架,展示了《换热站监控系统》上位机应用的全套实现过程,从界面功能布局、模拟组态业务到控件模板、图表、自定义控件,再到设备通信、页面数据实时更新、多线程相关操作等,众多实用功能完整落地。
希望以此作为本教程的精品,奉献给小伙伴,我也是帅呆了,感谢大家火辣辣的支持。 

走过路过不要错过,点赞关注收藏又圈粉,共同致富,为财务自由作出贡献 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1834909.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mumu 模拟器如何模拟指纹识别?

最近在帮朋友解决一些任务时&#xff0c;有些比较复杂的任务需要批量使用模拟器&#xff0c;但是模拟器存在一个缺点&#xff0c;就是缺少很多物理功能&#xff0c;比如说陀螺仪、温度传感器和生物识别模块等等&#xff0c;但是有些任务是需要这些功能的。没有办法&#xff0c;…

【会议】一张图片讲清楚:项目启动会议

另附上启动会前需要准备的内容&#xff1a;

2024年粤港澳青少年信息学创新大赛图形化编程小高组真题试卷

2024年粤港澳青少年信息学创新大赛图形化编程小高组真题试卷 题目总数&#xff1a;16 总分数&#xff1a;100 单选题 第 1 题 单选题 默认小猫角色&#xff0c;以下哪个Scratch程序可以让小猫一直在舞台上随机移动&#xff0c;且每次在移动过程中用时1秒 A. B. C. D…

【Linux】进程间通信2——命名管道

1. 命名管道(FIFO) 1.1. 基本概念 简单&#xff0c;给匿名管道起个名字就变成了命名管道 那么如何给 匿名管道 起名字呢&#xff1f; 结合文件系统&#xff0c;给匿名管道这个纯纯的内存文件分配 inode&#xff0c;将文件名与之构建联系&#xff0c;关键点在于不给它分配 D…

python+selenium之点击元素报错:‘NoneType‘ object has no attribute ‘click‘

今日遇到一个很奇怪的问题 case1:当使用顺序结构直接从登录到点击页面菜单&#xff0c;则可以正常点击菜单 case2&#xff1a;若把登录分离开&#xff0c;采用封装的方法点击菜单则会提示&#xff1a;‘NoneType’ object has no attribute ‘click’ 具体页面如下&#xff0c…

Vue01-前端概述

一、前端核心分析 1.1、概述 Soc原则&#xff1a;关注点分离原则 Vue 的核心库只关注视图层&#xff0c;方便与第三方库或既有项目整合。 HTML CSS JS : 视图 &#xff1a; 给用户看&#xff0c;刷新后台给的数据 网络通信 &#xff1a; axios 页面跳转 &#xff1a; v…

发那科机器人IO 分配

IO 信号 也称为输入\输出信号&#xff0c;是机器人与外围设备通信的电信号

Android 断点续传基础之单线程下载

**遇到的问题&#xff1a;**在这因为返回值的问题烦躁了一下&#xff0c;有可能出现空指针的异常&#xff0c;已经提出成文章了 请参考http://blog.csdn.net/qq_27489007/article/details/53523378 文件关系图 断点续传流程图 开始撸代码(主要代码) /** 普通断点续传 */ …

FirstChip1179量产经历(附FirstChipMpTools_v20200430工具)

前言&#xff1a; 新工作的原因&#xff0c;需要频繁用到u盘&#xff0c;手头上有2个常用的U盘&#xff08;龙凤呈祥57.3G&#xff08;64G&#xff09;&#xff09; 一个用来存放课件ppt、word、excel等文件&#xff1b;一个用来存放一些软件工具&#xff1b; ps&#xff0c…

将Typora中图片从指定路径移动到当前文件夹下(准确位置为:XX.md文件所在目录/XX.assets/)

1、背景介绍 最开始时候,将复制到Typora中的图片放在了指定位置。但是在进行迁移(在别的电脑上打开md文件),会发现发送过来的文件不包含图片,导致文件内容缺失。解决办法就是将图片放在XX.md文件所在目录/XX.assets/。 2、方法 2.1 做好文件备份 2.2 替换图片链接中的…

又一个SQL Developer中调试存储过程的例子

此例基于OBE&#xff08;Oracle By Example&#xff09;的示例。是在SQL Developer中调试存储过程一文的进阶。复习了前文的一些基本概念&#xff0c;并纠正了官方示例的一个错误。 本文使用的是标准的HR 示例 Schema&#xff0c;调试的存储过程源代码如下&#xff1a; CREAT…

针对k8s集群已经加入集群的服务器进行驱逐

例如k8s 已经有很多服务器&#xff0c;现在由于服务器资源过剩&#xff0c;需要剥离一些服务器出来 查找节点名称&#xff1a; kubectl get nodes设置为不可调度&#xff1a; kubectl cordon k8s-node13恢复可调度 kubectl uncordon k8s-node13在驱逐之前先把需要剥离驱逐的节…

Vite+Vue3安装且自动按需引入Element Plus组件库

一&#xff0c;安装Element Plus npm install element-plus //node环境16二&#xff0c;安装插件 npm install unplugin-auto-import unplugin-vue-components -D三&#xff0c;配置vite.config.ts文件 //按需引入element-plus组件 import AutoImport from unplugin-auto-i…

电手出软件啦!各种姿势一键重装你的系统

电手的兄弟们你们好啊&#xff0c;很多兄弟都知道&#xff0c;我们老大傲梅是做软件的&#xff0c;例如大伙儿都用过的傲梅分区助手&#xff0c;还有傲梅轻松备份。相信不少兄弟都是用了分区助手才关注到电手的。 而我们电手&#xff0c;在电脑和手机玩机内容方面一直深耕。 跟…

9.2.2 DeepLab系列模型中每一代的创新是什么?是为了解决什么问题?

9.2.2 DeepLab系列模型中每一代的创新是什么&#xff1f;是为了解决什么问题&#xff1f; 前情回顾&#xff1a;9.2.1 简述图像分割中经常用到的编码器-解码器网络结构的设计理念。 DeepLab是Google 团队提出的一系列图像分割算法。 DeepLab v1在2014年被提出&#xff0c;并在…

SpringMVC系列十一: 文件上传与自定义拦截器

文章目录 SpringMVC文件上传基本介绍需求分析 / 图解应用实例-代码实现 自定义拦截器什么是拦截器自定义拦截器执行流程分析图自定义拦截器应用实例快速入门注意事项和细节Debug执行流程 多个拦截器多个拦截器执行流程示意图应用实例1代码实现注意事项和细节 应用实例2 作业布置…

汇编语言程序设计 - 新建一个文件:d:\abc.txt,从键盘输入文件的内容(不超过100个字符)

80x86汇编习题 题目描述&#xff1a;编写一个程序&#xff0c;新建一个文件&#xff1a;d:\abc.txt&#xff0c;从键盘输入文件的内容&#xff08;不超过100个字符&#xff09; 思路&#xff1a; 1&#xff0c;定义好文件名&#xff0c;记得末尾0 2&#xff0c;定义好缓冲区…

C#开发-集合使用和技巧(六)特殊转换方法SelectMany的介绍和用法

介绍 SelectMany 方法在C#中用于将集合中的元素转换为其他类型的集合&#xff0c;并将这些集合扁平化为一个单一的序列。它是LINQ的一部分&#xff0c;允许你在一个序列上进行投影和过滤操作&#xff0c;然后将结果合并成一个序列。 方法定义 public static IEnumerable<…

【Python机器学习实战】 | 基于决策树的药物研究分类预测

&#x1f3a9; 欢迎来到技术探索的奇幻世界&#x1f468;‍&#x1f4bb; &#x1f4dc; 个人主页&#xff1a;一伦明悦-CSDN博客 ✍&#x1f3fb; 作者简介&#xff1a; C软件开发、Python机器学习爱好者 &#x1f5e3;️ 互动与支持&#xff1a;&#x1f4ac;评论 &…

Python内置debug库: pdb用法详解

文章目录 0. 引言1. 基本用法1.1 设置断点1.2 通过命令行启动 pdb 2. 常用命令2.1 n (next)2.2 s (step)2.3 c (continue)2.4 l (list)2.5 p (print)2.6 h (help)2.7 b (break)2.8 cl (clear)2.9 q (quit) 3. 例子 0. 引言 pdb&#xff08;Python Debugger&#xff09;是Pytho…