1、利用wpf+mvvm作為上位機(jī)開發(fā)技術(shù),通過讀寫分離的形式,啟動(dòng)一個(gè)線程來進(jìn)行對DB塊數(shù)據(jù)的讀取顯示;通過按鈕事件對選中的字段向PLCDB塊寫入數(shù)據(jù)。




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大小為默認(rèn)為480,1500為960,1200、300為240,400為480,
? ? ? ? ? ? //此處用1500測試設(shè)置960。配置文件中的lenght長度應(yīng)該小于Pdu-18,1500的length最大為942,
? ? ? ? ? ? //如果讀取的長度大于942,可以拆分為多個(gè)包讀取
? ? ? ? ? ? client.PduSizeRequested = 960;//設(shè)置pdu
? ? ? ? ? ? //默認(rèn)為PG連接,設(shè)置為基本連接
? ? ? ? ? ? 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 = "斷開連接";
? ? ? ? ? ? ? ? ? ? //啟動(dòng)界面刷新線程
? ? ? ? ? ? ? ? ? ? // 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.需要注意的問題
當(dāng)多線程進(jìn)行數(shù)據(jù)讀取和數(shù)據(jù)寫入時(shí),會(huì)出現(xiàn)線程交叉訪問導(dǎo)致數(shù)據(jù)讀寫異常的情況,可以修改sharp7源代碼在DBRead和DBWrite函數(shù)中加鎖處理該問題。
?????????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);
? ? ? ? ? ? }
? ? ? ? }