WPF上位机中,使用NModbus4 实现断线重连,不刷新控件动态更新实时数据

Nmodbus4 是C# 常用的通信类库,但是关于其断线重连的资料却很少,今天实验断线重连成功,特来记录一下。为了简单举例,这里的PLC变量都使用短整形ushort,提前与PLC约定放缩倍数,在配置文件中使用scale放缩系数得到正确值

json 配置文件:

{
  "ip" : "127.0.0.1",
  "port" : "502",
  "slaveNo" : "1",
  "address" : "0",
  "varNum" : "10",
  "actualDatas" : [ {
    "id" : 1,
    "name" : "Var1",
    "description" : "Short1",
    "insertTime" : "",
	"createTime" : "",
    "varAddress" : "0",
    "dataType" : "Short",
	"scale" : "1",
    "value" : ""
  }, {
    "id" : 2,
    "name" : "Var2",
    "description" : "Short2",
    "insertTime" : "",
	"createTime" : "",
    "varAddress" : "1",
    "dataType" : "Short",
	"scale" : "1",
    "value" : ""
  }, {
    "id" : 3,
    "name" : "Var3",
    "description" : "Short3",
    "insertTime" : "",
	"createTime" : "",
    "varAddress" : "2",
    "dataType" : "Short",
	"scale" : "1",
    "value" : ""
  }, {
    "id" : 4,
    "name" : "Var4",
    "description" : "Short4",
    "insertTime" : "",
	"createTime" : "",
    "varAddress" : "3",
    "dataType" : "Short",
	"scale" : "1",
    "value" : ""
  }, {
    "id" : 5,
    "name" : "Var5",
    "description" : "Short5",
    "insertTime" : "",
	"createTime" : "",
    "varAddress" : "4",
    "dataType" : "Short",
	"scale" : "0.5",
    "value" : ""
  }, {
    "id" : 6,
    "name" : "Var6",
    "description" : "Short6",
    "insertTime" : "",
	"createTime" : "",
    "varAddress" : "5",
    "dataType" : "Short",
	"scale" : "1",
    "value" : ""
  }, {
    "id" : 7,
    "name" : "Var7",
    "description" : "Short7",
    "insertTime" : "",
	"createTime" : "",
    "varAddress" : "6",
    "dataType" : "Short",
	"scale" : "1",
    "value" : ""
  }, {
    "id" : 8,
    "name" : "Var8",
    "description" : "Short8",
    "insertTime" : "",
	"createTime" : "",
    "varAddress" : "7",
    "dataType" : "Short",
	"scale" : "1",
    "value" : ""
  }, {
    "id" : 9,
    "name" : "Var9",
    "description" : "Short9",
    "insertTime" : "",
	"createTime" : "",
    "varAddress" : "8",
    "dataType" : "Short",
	"scale" : "1",
    "value" : ""
  }, {
    "id" : 10,
    "name" : "Var10",
    "description" : "Short10",
    "insertTime" : "",
	"createTime" : "",
    "varAddress" : "9",
    "dataType" : "Short",
	"scale" : "1",
    "value" : ""
  } ]
}

实体类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PrismFWDemon.Entities
{
    public class CfgJson
    {
        public string Ip { get; set; }
        public string Port { get; set; }
        public string SlaveNo { get; set; }
        public string Address { get; set; }
        public string varNum { get; set; }
        public List<ActualData> ActualDatas { get; set; }
    }
}

using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;

namespace PrismFWDemon.Entities
{
    public class ActualData
    {
        //主键,自增
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
        public string Name { get; set; }
        public string Value { get; set; }
        [SugarColumn(IsNullable = true)]//可以为NULL
        public string InsertTime { get; set; }

        [SugarColumn(IsNullable = true)]//可以为NULL
        public string CreateTime { get; set; }
        public string Description { get; set; }
        public string DataType { get; set; }
        public string Scale { get; set; }
        public string VarAddress { get; set; }
    }
}


using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;

namespace PrismFWDemon.Entities
{
    public class Log
    {
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
        [SugarColumn(IsNullable = true)]//可以为NULL
        public DateTime Date { get; set; }
        public string Level { get; set; }
        [SugarColumn(IsNullable = true)]//可以为NULL
        public string Logger { get; set; }
        [SugarColumn(IsNullable = true)]//可以为NULL
        public string Message { get; set; }
    }
}

Modbus帮助类:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PrismFWDemon.Modbus
{
    public interface IModbusHelper
    {
        string Conn();

        string DisConn();

        void readHoldingRegisters(ObservableCollection<string> colorList, ObservableCollection<string> varList);

        string writeHoldingRegisters(string varAddress, string value);

        void Insert();
    }
}


using log4net;
using Modbus.Device;
using Newtonsoft.Json;
using PrismFWDemon.Entities;
using PrismFWDemon.SqlSugar.Impl;
using PrismFWDemon.ViewModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace PrismFWDemon.Modbus.Impl
{
    public class ModbusHelper:IModbusHelper
    {
        bool last = false;
        bool trigIn = false;
        bool trigQ = false;

        private string ip;
        private string port;


        private List<ActualData> actualDatas = new List<ActualData>();

        public List<ActualData> ActualDatas
        {
            get { return actualDatas; }
            set { actualDatas = value; }
        }

        /**
         * modubs从站ID
         */
        private string slaveNo;

        private bool isConnected = false;

        private bool isFirstConn = true;

        private int errorTimes = 0;

        CancellationTokenSource cts = new CancellationTokenSource();

        /**
         * 起始地址
         */

        private string address;

        //变量个数
        private string varNum;

        private TcpClient tcpClient = null;

        private ModbusIpMaster master;

        //配置文件地址
        private const string jsonfile = "D:\\modbuscfg1.json";

        public string Jsonfile
        {
            get { return jsonfile; }
        }

        //读取变量
        private string str;
        private CfgJson cfgJson;
        private SqlSugarHelper sqlSugarHelper;

        public ModbusHelper(SqlSugarHelper sqlSugarHelper)
        {
            this.sqlSugarHelper = sqlSugarHelper;
            str = File.ReadAllText(jsonfile);

            cfgJson = JsonConvert.DeserializeObject<CfgJson>(str);

            this.ip = cfgJson.Ip;
            this.port = cfgJson.Port;
            this.slaveNo = cfgJson.SlaveNo;
            this.address = cfgJson.Address;
            this.varNum = cfgJson.varNum;
            this.actualDatas = cfgJson.ActualDatas;
            //this.Conn();
            //this.readHoldingRegisters();
        }

        public string Conn()
        {
            try
            {
                tcpClient = new TcpClient();
                tcpClient.Connect(IPAddress.Parse(ip), int.Parse(port));
                master = ModbusIpMaster.CreateIp(tcpClient);
                isConnected = true;
                master.Transport.ReadTimeout = 1000;//读取数据超时时间为设定值

                master.Transport.WriteTimeout = 1000;//写入数据超时时间为设定值

                master.Transport.Retries = 5;  

                master.Transport.WaitToRetryMilliseconds = 2500;
                return "OK";
            }
            catch (Exception ex)
            {
                LogHelper.Info(ex.Message);
                LogHelper.Error(ex.Message);
                return ex.Message;
            }
        }

        public string DisConn()
        {
            if (master != null)
            {
                master.Dispose();
                tcpClient.Dispose();
                tcpClient.Close();
                return "OK";
            }
            else
            {
                return "Failed";
            }
        }

        public void readHoldingRegisters(ObservableCollection<string> colorList, ObservableCollection<string> varList)
        {
            Task.Run(async () =>
            {
                while (!cts.IsCancellationRequested)
                {
                    if (isConnected == true)
                    {
                        ushort[] des = null;
                        await Task.Delay(500);
                        try
                        {
                            des = master.ReadHoldingRegisters(ushort.Parse(address), ushort.Parse(varNum));
                        }
                        catch (Exception ex)
                        {
                            LogHelper.Info(ex.Message);
                            LogHelper.Error(ex.Message);
                        }

                        //读出的数据不为空,则解析数据,存储
                        if (des != null)
                        {
                            errorTimes = 0;
                            for (int i = 0; i < actualDatas.Count; i++)
                            {
                                actualDatas[i].Value = ((float)des[i] * float.Parse(actualDatas[i].Scale)).ToString();
                                actualDatas[i].CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                                varList[i] = actualDatas[i].Value;

                                if (varList[i] == "1")
                                {
                                    colorList[i] = "Green";
                                }
                                else if (varList[i] == "2")
                                {
                                    colorList[i] = "Orange";
                                }
                                else if (varList[i] == "3")
                                {
                                    colorList[i] = "Red";
                                }
                            }

                            //按事件插入数据库
                            Insert();
                        }

                        else
                        {
                            errorTimes++;
                            await Task.Delay(500);
                            if (errorTimes >= 3)
                            {
                                isConnected = false;
                            }
                        }
                    }
                    else
                    {
                        if (!isFirstConn)
                        {
                            await Task.Delay(1000);
                            
                            tcpClient?.Close();
                            //已经测试,不行
                            //tcpClient.Dispose();
                            //master?.Transport.Dispose();
                            //master?.Dispose();
                        }

                        try
                        {
                            //首次连接,或多次连接
                            tcpClient = new TcpClient();
                            tcpClient.Connect(IPAddress.Parse(ip), int.Parse(port));
                            //先将master置为空,再断线重连
                            master = null;
                            master = ModbusIpMaster.CreateIp(tcpClient);
                            master.Transport.ReadTimeout = 1000;//读取数据超时时间为设定值

                            master.Transport.WriteTimeout = 1000;//写入数据超时时间为设定值

                            master.Transport.Retries = 5;

                            master.Transport.WaitToRetryMilliseconds = 2500;
                            isConnected = true;
                            isFirstConn = false;

                        }
                        catch (Exception ex)
                        {
                            LogHelper.Info(ex.Message);
                            LogHelper.Error(ex.Message);
                        }


                    }

                }
            },cts.Token);
        }

        public string writeHoldingRegisters(string varAddress, string value)
        {
            master.WriteSingleRegister(ushort.Parse(varAddress), ushort.Parse(value));
            return "OK";
        }


        public void Insert()
        {
            trigIn = this.actualDatas[9].Value.Equals("10");
            trigQ = (trigIn & !last);
            last = trigIn;
            if (trigQ)
            {
                try
                {
                    InsertDataBase();
                }
                catch (Exception ex)
                {
                    LogHelper.Info(ex.Message);
                    LogHelper.Error(ex.Message);
                }
            }
        }

        //插入数据工具类
        public int InsertDataBase()
        {
            string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            foreach (var item in this.actualDatas)
            {
                item.InsertTime = now;
            }

            int count = this.sqlSugarHelper.db.Insertable(this.actualDatas).ExecuteCommand();
            return count;
        }
    }
}


ViewModel:

using Prism.Commands;
using Prism.Mvvm;
using PrismFWDemon.Modbus.Impl;
using PrismFWDemon.SqlSugar;
using PrismFWDemon.Views;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PrismFWDemon.ViewModels
{
    public class TdResponsitoryViewModel: BindableBase
    {
        //ObservableCollection 可以通知到内容变化,List不行
        private ObservableCollection<string> varList = new ObservableCollection<string>() 
        { 
            "var1","var2","var3","var4","var5","var6","var7","var8","var9","var10"
        };

        public ObservableCollection<string> VarList
        {
            get { return varList; }
            set { varList = value; RaisePropertyChanged(); }
        }


        //private string color1_1 = "Gary";

        //public string Color1_1
        //{
        //    get { return color1_1; }
        //    set { color1_1 = value; }
        //}

        private ObservableCollection<string> colors=new ObservableCollection<string>() 
        {
            "Gray","Gray","Gray","Gray","Gray","Gray","Gray","Gray","Gray","Gray"
        };

        public ObservableCollection<string> Colors
        {
            get { return colors; }
            set { colors = value; RaisePropertyChanged(); }
        }



        public DelegateCommand OpenCargoA { get; private set; }

        private void CargoA()
        {
            var window = new AddCargoAView();
            window.ShowDialog();
        }

        private ModbusHelper modbusHelper;

        public ModbusHelper ModbusHelper
        {
            get { return modbusHelper; }
            set { modbusHelper = value; RaisePropertyChanged(); }
        }

        public TdResponsitoryViewModel(ModbusHelper modbusHelper)
        {
            
            this.modbusHelper = modbusHelper;
            this.modbusHelper.readHoldingRegisters(this.Colors,this.VarList);
            OpenCargoA = new DelegateCommand(CargoA);


        }
    }
}


view:

<UserControl x:Class="PrismFWDemon.Views.TdResponsitoryView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:PrismFWDemon.Views"
             xmlns:prism="http://prismlibrary.com/"
             xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">

    <md:TransitioningContent OpeningEffect="{md:TransitionEffect Kind=ExpandIn}">
        <Grid>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>

                <Grid Grid.Column="0" Grid.Row="0">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Background="{Binding Colors[0]}"
                Command="{Binding OpenCargoA}"
                Content="仓库1-1" />
                </Grid>
                <Grid Grid.Column="0" Grid.Row="1">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Background="{Binding Colors[1]}"
                Command="{Binding SendMsg}"
                Content="仓库2-1"/>
                </Grid>
                <Grid Grid.Column="0" Grid.Row="2">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Background="{Binding Colors[2]}"
                Command="{Binding SendMsg}"
                Content="仓库3-1" />
                </Grid>
                <Grid Grid.Column="0" Grid.Row="3">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Background="{Binding Colors[3]}"
                Command="{Binding SendMsg}"
                Content="仓库4-1" />
                </Grid>
                <Grid Grid.Column="1" Grid.Row="0">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Background="{Binding Colors[4]}"
                Command="{Binding SendMsg}"
                Content="仓库1-2" />
                </Grid>
                <Grid Grid.Column="1" Grid.Row="1">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Background="{Binding Colors[5]}"
                Command="{Binding SendMsg}"
                Content="仓库2-2" />
                </Grid>
                <Grid Grid.Column="1" Grid.Row="2">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Background="{Binding Colors[6]}"
                Command="{Binding SendMsg}"
                Content="仓库2-3" />
                </Grid>
                <Grid Grid.Column="1" Grid.Row="3">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Background="{Binding Colors[7]}"
                Command="{Binding SendMsg}"
                Content="仓库2-4" />
                </Grid>
                <Grid Grid.Column="2" Grid.Row="0">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Command="{Binding SendMsg}"
                Content="{Binding VarList[0]}" />
                </Grid>
                <Grid Grid.Column="2" Grid.Row="1">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Command="{Binding SendMsg}"
                Content="{Binding VarList[1]}" />
                </Grid>
                <Grid Grid.Column="2" Grid.Row="2">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Command="{Binding SendMsg}"
                Content="{Binding VarList[2]}" />
                </Grid>
                <Grid Grid.Column="2" Grid.Row="3">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Command="{Binding SendMsg}"
                Content="{Binding VarList[3]}" />
                </Grid>
                <Grid Grid.Column="3" Grid.Row="0">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Command="{Binding SendMsg}"
                Content="{Binding VarList[4]}" />
                </Grid>
                <Grid Grid.Column="3" Grid.Row="1">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Command="{Binding SendMsg}"
                Content="{Binding VarList[5]}" />
                </Grid>
                <Grid Grid.Column="3" Grid.Row="2">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Command="{Binding SendMsg}"
                Content="{Binding VarList[6]}" />
                </Grid>
                <Grid Grid.Column="3" Grid.Row="3">
                    <Button
                Margin="20,20,20,20"
                Height="80"
                Command="{Binding SendMsg}"
                Content="{Binding VarList[7]}" />
                </Grid>
            </Grid>
        </Grid>
    </md:TransitioningContent>

</UserControl>


依赖注入:

using Prism.DryIoc;
using Prism.Ioc;
using PrismFWDemon.Common;
using PrismFWDemon.Modbus;
using PrismFWDemon.Modbus.Impl;
using PrismFWDemon.SqlSugar;
using PrismFWDemon.SqlSugar.Impl;
using PrismFWDemon.ViewModels;
using PrismFWDemon.Views;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace PrismFWDemon
{
    /// <summary>
    /// App.xaml 的交互逻辑
    /// </summary>
    /// 

    /*
    App.xaml 添加 xmlns:prism="http://prismlibrary.com/" 命名空间
    修改继承 PrismApplication
    App.xaml修改为prism:PrismApplication
    创建 启动项
        protected override Window CreateShell()
        {
            return Container.Resolve<MainWindow>();
        }
    创建IOC容器
    protected override Window CreateShell() //启动页
     protected override void RegisterTypes 依赖注入
     
     
     
     */

    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainView>();
        }

        protected override void OnInitialized()
        {
            var service = App.Current.MainWindow.DataContext as IConfigureService;
            if (service != null)
                service.Configure();
            base.OnInitialized();
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            //注册导航
            containerRegistry.RegisterForNavigation<ServiceView, ServiceViewModel>(); //PageA 区域名称,别名
            containerRegistry.RegisterForNavigation<ClientView, ClientViewModel>();
            containerRegistry.RegisterForNavigation<MsgView, MsgViewModel>();
            containerRegistry.RegisterForNavigation<TdResponsitoryView, TdResponsitoryViewModel>();
            //注册对话框
            containerRegistry.RegisterDialog<MsgView, MsgViewModel>();//也可以加别名("Question")
            containerRegistry.Register<ISqlSugarHelper, SqlSugarHelper>();
            containerRegistry.RegisterSingleton<ISqlSugarService, SqlSugarService>();
            containerRegistry.Register<IModbusHelper, ModbusHelper>();
            //containerRegistry.Register<ILogHelper, LogHelper>();
        }
    }
}


using log4net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PrismFWDemon.SqlSugar.Impl
{
    public class LogHelper
    {
        public static void Error(string message, Exception ex)
        {
            ILog log = LogManager.GetLogger("Error");
            if (log.IsErrorEnabled)
            {
                log.Error(message, ex);
            }
        }

        public static void Error(string message)
        {
            ILog log = LogManager.GetLogger("Error");
            if (log.IsErrorEnabled)
            {
                log.Error(message);
            }
        }

        public static void Info(string message)
        {
            ILog log = LogManager.GetLogger("Info");
            if (log.IsInfoEnabled)
            {
                log.Info(message);
            }
        }
    }
}

using SqlSugar;

namespace PrismFWDemon.SqlSugar.Impl
{
    public class SqlSugarHelper: ISqlSugarHelper
    {
        private SqlSugarService sqlSugarService;
        public SqlSugarHelper(SqlSugarService sqlSugarService)
        {
            this.sqlSugarService = sqlSugarService;
        }

        public SqlSugarClient db
        {
            get => new SqlSugarClient(new ConnectionConfig()
            {
                ConnectionString = this.sqlSugarService.ConnectionStr,
                DbType = DbType.Sqlite,         //必填, 数据库类型
                IsAutoCloseConnection = true,       //默认false, 时候知道关闭数据库连接, 设置为true无需使用using或者Close操作
                InitKeyType = InitKeyType.Attribute    //默认SystemTable, codefist需要使用Attribute
            });
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PrismFWDemon.SqlSugar.Impl
{
    public class SqlSugarService : ISqlSugarService
    {
        private string connectionStr = "Data Source=MyDb.db";

        public string ConnectionStr
        {
            get { return connectionStr; }
            set { connectionStr = value; }
        }
    }
}

效果:

断线:
在这里插入图片描述
在这里插入图片描述

### 回答1: WPF开发上位机利用NModbus4类库与Modbus TCP通讯的过程如下: 首先,我们需要在WPF应用程序引用NModbus4类库。可以通过NuGet包管理器或手动添加引用来实现。 接下来,在WPF应用程序添加一个用于与Modbus TCP通讯的类。可以将这个类命名为ModbusClient或类似的名称。在这个类,我们将使用NModbus4提供的ModbusTcpClient类来进行通讯。 初始化ModbusTcpClient对象时,需要指定远程Modbus设备的IP地址和端口号。这些信息可以根据具体的Modbus设备来进行设置。 一旦初始化完成,就可以使用ModbusTcpClient对象来建立与Modbus设备的连接,并发送和接收Modbus消息。 发送消息时,可以使用ModbusTcpClient对象的WriteSingleRegister或WriteMultipleRegisters方法来向Modbus设备写入单个或多个寄存器的值。可以根据Modbus设备的寄存器地址和数据类型来进行设置。 接收消息时,可以使用ModbusTcpClient对象的ReadHoldingRegisters或ReadInputRegisters方法来读取Modbus设备的保持寄存器或输入寄存器的值。同样,需要根据Modbus设备的寄存器地址和数据类型来进行设置。 在WPF应用程序,可以通过按钮、文本框等控件来触发和显示Modbus通讯操作的结果。 最后,需要处理连接断开和异常情况。可以使用try-catch语句来捕获可能出现的异常,并在出现异常时做相应的处理,例如提示用户或重新连接Modbus设备。 总结起来,WPF开发上位机利用NModbus4类库与Modbus TCP通讯,需要引用NModbus4类库,创建ModbusTcpClient对象并进行连接,使用适当的方法发送和接收Modbus消息,并处理连接断开和异常情况。通过这种方式,我们可以实现WPF应用程序与Modbus设备的通讯。 ### 回答2: WPF开发上位机利用NModbus4类库与Modbus TCP通讯可以实现Modbus TCP设备的控制和监控。WPF是一种基于.NET框架的图形界面应用程序开发技术,通过使用NModbus4类库,我们可以方便地实现Modbus TCP设备的通讯。 在开发过程,我们首先需要在WPF应用程序添加NModbus4类库的引用,然后使用提供的类和方法来进行通讯操作。通过NModbus4提供的Modbus TCP客户端类,我们可以建立与Modbus TCP设备的连接,并发送读写请求来获取或修改设备的寄存器值。 在与Modbus TCP设备通讯时,我们需要了解设备的寄存器结构和通讯协议。根据设备的寄存器结构,我们可以使用NModbus4的方法来读取或写入寄存器的值。例如,我们可以使用ReadHoldingRegisters方法来读取设备的保持寄存器值,并使用WriteSingleRegister方法来写入单个寄存器的值。 在WPF应用程序,我们可以通过设计界面来展示设备的状态和控制元素,例如使用按钮来发送写入请求,使用文本框来显示寄存器的值。通过与NModbus4类库的结合,我们可以方便地实现用户与Modbus TCP设备之间的交互。 总之,利用NModbus4类库与Modbus TCP通讯的WPF上位机开发可以实现Modbus TCP设备的控制和监控,通过发送读写请求并解析返回的数据,我们可以方便地获取设备的状态或修改设备的寄存器值。这种开发方式可以提高开发效率和用户体验,使得上位机开发更加简单和灵活。 ### 回答3: WPF(Windows Presentation Foundation)是一种用于创建Windows客户端应用程序的框架。它提供了丰富的图形用户界面(GUI)引擎和现代化的用户界面开发工具。 NModbus4是一个C#编写的Modbus通信库,可以用于与Modbus设备进行通信。Modbus是一种常见的通信协议,用于将上位机与下位机(如传感器、执行器等)进行连接和通信。 在使用WPF开发上位机时,可以利用NModbus4类库与Modbus TCP进行通信。Modbus TCP是Modbus协议在以太网上的一种具体实现方式。通过TCP/IP网络,上位机可以与远程Modbus设备进行数据交换。 使用NModbus4的步骤如下: 1. 引用NModbus4类库:在WPF项目,首先需要将NModbus4类库添加到项目引用,以便可以使用的类和方法。 2. 创建Modbus主站对象:通过实例化ModbusTcpMaster类,可以创建一个Modbus主站对象,用于与Modbus从站进行通信。 3. 连接到Modbus从站:使用主站对象的Connect方法连接到特定的Modbus从站。该方法需要指定从站的IP地址和端口号。 4. 发送Modbus消息:使用主站对象的ReadCoils、ReadHoldingRegisters、WriteSingleCoil等方法,可以向从站发送Modbus消息,进行读取或写入操作。 5. 处理返回数据:主站对象的读取方法将返回一个响应对象,其包含从站返回的数据。可以根据需要对返回的数据进行处理和解析。 6. 断开与从站的连接:使用主站对象的Disconnect方法,可以断开与从站的连接。 通过以上步骤,WPF上位机可以利用NModbus4类库与Modbus TCP进行通信,实现Modbus设备之间的数据交换。在开发过程,需要注意对Modbus协议的理解和使用方法的熟悉,以确保通信的稳定性和准确性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潘诺西亚的火山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值