c#与西门子PLC1500基于sharp7通信学习2.利用WPF MVVM 读写DB块

1、利用wpf+mvvm作为上位机开发技术,通过读写分离的形式,启动一个线程来进行对DB块数据的读取显示;通过按钮事件对选中的字段向PLCDB块写入数据。


上位机界面



PLCDB块


复制DB块的文本到EXCEL中作为基础配置文件供上位机读取



项目目录

2:主要代码

2.1 View层

<hc:Window x:Class="HandyControlProject1.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:hc="https://handyorg.github.io/handycontrol"

        mc:Ignorable="d"

        DataContext="{Binding Source={StaticResource Locator},Path=Main}"

        Title="MainWindow"

          Loaded="Window_Loaded"

        WindowStartupLocation="CenterScreen"

        Style="{StaticResource WindowWin10}"

        ShowTitle="True"

        Height="450"

        Width="800"> 

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="50" />

            <RowDefinition />

            <RowDefinition Height="20" />

        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" Orientation="Horizontal">

            <Button x:Name="button" Click="button_Click" Content="Connect" HorizontalAlignment="Left" Margin="5" VerticalAlignment="Center" Width="75" />

            <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="5" TextWrapping="Wrap" Text="192.168.1.140" VerticalAlignment="Center" Width="120"/>

            <TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="5" TextWrapping="Wrap" Text="" VerticalAlignment="Center"/>

        </StackPanel>

        <Grid Grid.Row="1">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="5*" />

                <ColumnDefinition Width="*" />

            </Grid.ColumnDefinitions>

            <DataGrid Grid.Column="0" SelectedItem="{Binding SelectedTag}" ItemsSource="{Binding DB103TagList}" />

            <Border Grid.Column="1" Background="Gray" BorderBrush="White" BorderThickness="1" CornerRadius="5">

                <StackPanel Orientation="Vertical">

                    <TextBlock Foreground="Yellow" FontSize="18" Text="{Binding SelectedTag.TagName}"></TextBlock>

                    <TextBox Margin="0,5" x:Name="txtTagValue"></TextBox>

                    <Button Margin="0,5" x:Name="WriteTagValue" Content="写入" Click="WriteTagValue_Click"></Button>

                </StackPanel>

            </Border>

        </Grid>       

        <StackPanel Grid.Row="2" Orientation="Horizontal">

            <TextBlock x:Name="cputime"></TextBlock>

            <TextBlock x:Name="debugmsg" Margin="10,0"></TextBlock>

        </StackPanel>

    </Grid>

</hc:Window>


using System;

using System.Collections.Generic;

using System.IO;

using System.Net;

using System.Runtime.InteropServices;

using System.Runtime.Serialization;

using System.Runtime.Serialization.Formatters.Binary;

using System.Threading;

using System.Threading.Tasks;

using CommonServiceLocator;

using HandyControlProject1.ViewModel;

using Sharp7;

namespace HandyControlProject1

{

    public partial class MainWindow

    {

        private CancellationTokenSource TokenSource;

        private Task CurrentTask;

        MainViewModel Main;

        S7Client client;

        S7Client.S7CpuInfo s7CpuInfo;

        S7Client.S7OrderCode s7OrderCode;   


        public MainWindow()

        {

            InitializeComponent();

            Main= ServiceLocator.Current.GetInstance<MainViewModel>();

            client = new Sharp7.S7Client();

            s7CpuInfo = new S7Client.S7CpuInfo();

            s7OrderCode = new S7Client.S7OrderCode();


            //PDU大小为默认为480,1500为960,1200、300为240,400为480,

            //此处用1500测试设置960。配置文件中的lenght长度应该小于Pdu-18,1500的length最大为942,

            //如果读取的长度大于942,可以拆分为多个包读取

            client.PduSizeRequested = 960;//设置pdu

            //默认为PG连接,设置为基本连接

            client.SetConnectionType(3);

        }


        private void Window_Loaded(object sender, System.Windows.RoutedEventArgs e)

        {

        }     

        private void button_Click(object sender, System.Windows.RoutedEventArgs e)

        {

            if (!client.Connected)

            {

                //连接Plc

                var result = client.ConnectTo(this.textBox.Text, 0, 0);

                if (result == 0)

                {                 

                    client.GetCpuInfo(ref s7CpuInfo);

                    client.GetOrderCode(ref s7OrderCode);

                    this.textBlock.Text = ($"Connected to {this.textBox.Text},CPU类型:{s7CpuInfo.ModuleTypeName},订货号:{s7OrderCode.Code}");

                    this.button.Content = "断开连接";

                    //启动界面刷新线程

                    // create the cancellation token

                    TokenSource = new CancellationTokenSource();

                    CancellationToken token = TokenSource.Token;

                    // create the task

                    CurrentTask = Task.Factory.StartNew(() =>

                    {

                        while (true)

                        {

                            if (token.IsCancellationRequested)

                            {

                                setmessage("Task cancel detected");

                                break;

                            }

                            else

                            {

                                byte[] buffer = new byte[310];

                                int res= client.DBRead(3, 0, buffer.Length, buffer);

                                Console.WriteLine(client.ErrorText(res));

                                foreach (var item in Main.DB103TagList)

                                {

                                    switch (item.TagType)

                                    {

                                        case "Int":

                                            item.TagValue = S7.GetIntAt(buffer, (int)item.TagAddress).ToString();

                                            break;

                                        case "Bool":

                                            string[] address = item.TagAddress.ToString("0.0").Split('.');

                                            item.TagValue = S7.GetBitAt(buffer, int.Parse(address[0]), int.Parse(address[1])).ToString();

                                            break;

                                        default:

                                            break;

                                    }

                                }

                                setmessage(client.ExecutionTime.ToString());

                            }

                            Thread.Sleep(100);

                        }


                    }, token);                 

                }

                else

                {

                    this.textBlock.Text = (client.ErrorText(result));

                }

            }

            else

            {

                var result = client.Disconnect();

                if (result == 0)

                {

                    if (CurrentTask != null)

                    {

                        if (CurrentTask.Status == TaskStatus.Running) { }

                        {

                            TokenSource.Cancel();

                            setmessage("Task cancel");

                        }

                    }

                    this.textBlock.Text = ($"Disconnected from {this.textBox.Text}");

                    this.button.Content = "连接";

                }

                else

                {

                    this.textBlock.Text = (client.ErrorText(result));

                }

            }       

        }

        void setmessage(string message)

        {

            this.Dispatcher.Invoke(() =>

            {

                this.debugmsg.Text = message;

                this.cputime.Text = DateTime.Now.ToString();

            });

        }

        private void WriteTagValue_Click(object sender, System.Windows.RoutedEventArgs e)

        {

            if (Main.SelectedTag != null)

            {

                string[] address = Main.SelectedTag.TagAddress.ToString("0.0").Split('.');

                int zhengshu = int.Parse(address[0]);

                int xiaoshu = int.Parse(address[1]);

                int gewei = int.Parse(address[0]) % 10;             

                switch (Main.SelectedTag.TagType)

                {

                    case "Int":

                        byte[] buff = new byte[2];

                        int res= client.DBRead(3, zhengshu, buff.Length, buff);

                        if (res == 0)

                        {

                            S7.SetIntAt(buff, 0, short.Parse(this.txtTagValue.Text));

                            client.DBWrite(3, zhengshu, buff.Length, buff);

                        }                       

                        break;

                    case "Bool":

                        byte[] buffbit = new byte[1];

                        int resbit = client.DBRead(3, zhengshu, buffbit.Length, buffbit);

                        if (resbit == 0)

                        {

                            S7.SetBitAt(ref buffbit, 0, xiaoshu, this.txtTagValue.Text == "1" ? true : false);

                            client.DBWrite(3, zhengshu, buffbit.Length, buffbit);

                        }                       

                        break;

                    default:

                        break;

                }               

            }

        }

    }

}

2.2 ViewModel层

using GalaSoft.MvvmLight;

using HandyControlProject1.Model;

using System.Collections.Generic;

using System.Diagnostics;

using System.IO;

namespace HandyControlProject1.ViewModel

{

    /// <summary>

    /// This class contains properties that the main View can data bind to.

    /// <para>

    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.

    /// </para>

    /// <para>

    /// You can also use Blend to data bind with the tool's support.

    /// </para>

    /// <para>

    /// See http://www.galasoft.ch/mvvm

    /// </para>

    /// </summary>

    public class MainViewModel : ViewModelBase

    {

        /// <summary>

        /// Initializes a new instance of the MainViewModel class.

        /// </summary>

        public MainViewModel()

        {

            ////if (IsInDesignMode)

            ////{

            ////    // Code runs in Blend --> create design time data.

            ////}

            ////else

            ////{

            ////    // Code runs "for real"

            ////}

            ///

            string appStartupPath = System.IO.Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);

            string filePath = appStartupPath+@"\DB103.csv";

            FileStream fileStream = new FileStream(filePath,FileMode.Open,FileAccess.Read);

            StreamReader reader = new StreamReader(fileStream, System.Text.Encoding.Default);         

            string str = null;

            DB103TagList = new List<Tags>();

            while ((str = reader.ReadLine()) != null)

            {

                Tags tag = new Tags();

                string[] strs = str.Split(',');

                if (strs.Length == 3)

                {

                    tag.TagName = strs[0].ToString();

                    tag.TagType = strs[1].ToString();

                    tag.TagAddress = double.Parse(strs[2].ToString());

                    DB103TagList.Add(tag);

                }                             

            }

            reader.Close();

            fileStream.Close();

        }

        private List<Tags> _DB103TagList;

        public List<Tags> DB103TagList

        {

            get { return _DB103TagList; }

            set { _DB103TagList = value;

                RaisePropertyChanged();

            }

        }

        private Tags _SelectedTag;

        public Tags SelectedTag

        {

            get { return _SelectedTag; }

            set { _SelectedTag = value;

                RaisePropertyChanged();

            }

        }

    }

}

2.3 Model

using GalaSoft.MvvmLight;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

namespace HandyControlProject1.Model

{

    public class Tags:ObservableObject

    {

        private string _TagName;

        public string TagName

        {

            get { return _TagName; }

            set { _TagName = value;

                RaisePropertyChanged(()=>TagName);

            }

        }

        private string _TagType;

        public string TagType

        {

            get { return _TagType; }

            set { _TagType = value;

                RaisePropertyChanged(() => TagType);

            }

        }

        private double _TagAddress;

        public double TagAddress

        {

            get { return _TagAddress; }

            set { _TagAddress = value;

                RaisePropertyChanged(() => TagAddress);

            }

        }

        private string _TagValue;

        public string TagValue

        {

            get { return _TagValue; }

            set { _TagValue = value;

                RaisePropertyChanged(() => TagValue);

            }

        }

    }

}


3.需要注意的问题

当多线程进行数据读取和数据写入时,会出现线程交叉访问导致数据读写异常的情况,可以修改sharp7源代码在DBRead和DBWrite函数中加锁处理该问题。

         private static object objlock = new object();

        public int DBRead(int DBNumber, int Start, int Size, byte[] Buffer)

        {

            lock (objlock)

            {

                return ReadArea(S7Consts.S7AreaDB, DBNumber, Start, Size, S7Consts.S7WLByte, Buffer);

            }


        }

        public int DBWrite(int DBNumber, int Start, int Size, byte[] Buffer)

        {

            lock (objlock)

            {

                return WriteArea(S7Consts.S7AreaDB, DBNumber, Start, Size, S7Consts.S7WLByte, Buffer);

            }

        }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容