欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#?NModbus?RTU通信實現方法詳解

 更新時間:2024年11月07日 09:30:32   作者:熊來闖一闖  
Modbus協議時應用于電子控制器上的一種通用語言,通過此協議,控制器相互之間、控制器經由網絡/串口和其它設備之間可以進行通信,下面我們就來看看具體實現吧

Modbus協議時應用于電子控制器上的一種通用語言。通過此協議,控制器相互之間、控制器經由網絡/串口和其它設備之間可以進行通信。它已經成為了一種工業(yè)標準。有了這個通信協議,不同的廠商生成的控制設備就可以連城工業(yè)網絡,進行集中監(jiān)控。

本文實現需要借用一個開源的NModbus庫來完成,通過在菜單欄,工具-----NuGet包管理器-----管理解決方案的NuGet程序包,安裝NModbus的開源庫。

本次實例的基本框架和實現效果如下所示:

可自動識別當前設備的可用串口。

 Modbus RTU通信的具體的實現如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Modbus.Device;
using System.Net.Sockets;
using System.Threading;
using System.IO.Ports;
using System.Drawing.Text;
using System.Windows.Forms.VisualStyles;
using System.Timers;
using System.CodeDom.Compiler;
namespace ModbusRtuMaster
{
    public partial class Form1 : Form
    {
        #region 參數配置
        private static IModbusMaster master;
        private static SerialPort port;
        //寫線圈或寫寄存器數組
        private bool[] coilsBuffer;
        private ushort[] registerBuffer;
        //功能碼
        private string functionCode;
        //功能碼序號
        private int functionOder;
        //參數(分別為從站地址,起始地址,長度)
        private byte slaveAddress;
        private ushort startAddress;
        private ushort numberOfPoints;
        //串口參數
        private string portName;
        private int baudRate;
        private Parity parity;
        private int dataBits;
        private StopBits stopBits;
        //自動測試標志位
        private bool AutoFlag = false;
        //獲取當前時間
        private System.DateTime Current_time;
        //定時器初始化
        private System.Timers.Timer t = new System.Timers.Timer(1000);
        private const int WM_DEVICE_CHANGE = 0x219;            //設備改變
        private const int DBT_DEVICEARRIVAL = 0x8000;          //設備插入
        private const int DBT_DEVICE_REMOVE_COMPLETE = 0x8004; //設備移除
        #endregion
        public Form1()
        {
            InitializeComponent();
            GetSerialLstTb1();
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            //界面初始化
            cmb_portname.SelectedIndex = 0;
            cmb_baud.SelectedIndex = 5;
            cmb_parity.SelectedIndex = 2;
            cmb_databBits.SelectedIndex = 1;
            cmb_stopBits.SelectedIndex = 0;
        }
        #region 定時器
        //定時器初始化,失能狀態(tài)
        private void init_Timer()
        {
            t.Elapsed += new System.Timers.ElapsedEventHandler(Execute);
            t.AutoReset = true;//設置false定時器執(zhí)行一次,設置true定時器一直執(zhí)行
            t.Enabled = false;//定時器使能true,失能false
            //t.Start();
        }
        private void Execute(object source,System.Timers.ElapsedEventArgs e)
        {
            //停止定時器后再打開定時器,避免重復打開
            t.Stop();
            //ExecuteFunction();可添加執(zhí)行操作
            t.Start();
        }
        #endregion
        #region 串口配置
        /// <summary>
        /// 串口參數獲取
        /// </summary>
        /// <returns></返回串口配置參數>
        private SerialPort InitSerialPortParameter()
        {
            if (cmb_portname.SelectedIndex < 0 || cmb_baud.SelectedIndex < 0 || cmb_parity.SelectedIndex < 0 || cmb_databBits.SelectedIndex < 0 || cmb_stopBits.SelectedIndex < 0)
            {
                MessageBox.Show("請選擇串口參數");
                return null;
            }
            else
            {
                portName = cmb_portname.SelectedItem.ToString();
                baudRate = int.Parse(cmb_baud.SelectedItem.ToString());
                switch (cmb_parity.SelectedItem.ToString())
                {
                    case "奇":
                        parity = Parity.Odd;
                        break;
                    case "偶":
                        parity = Parity.Even;
                        break;
                    case "無":
                        parity = Parity.None;
                        break;
                    default:
                        break;
                }
                dataBits = int.Parse(cmb_databBits.SelectedItem.ToString());
                switch (cmb_stopBits.SelectedItem.ToString())
                {
                    case "1":
                        stopBits = StopBits.One;
                        break;
                    case "2":
                        stopBits = StopBits.Two;
                        break;
                    default:
                        break;
                }
                port = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
                return port;
            }
        }
        #endregion
        #region 串口收/發(fā)
        private async void ExecuteFunction()
        {
            Current_time = System.DateTime.Now;
            try
            {
                if (port.IsOpen == false)
                {
                    port.Open();
                }
                if (functionCode != null)
                {
                    switch (functionCode)
                    {
                        case "01 Read Coils"://讀取單個線圈
                            SetReadParameters();
                            try
                            {
                                coilsBuffer = master.ReadCoils(slaveAddress, startAddress, numberOfPoints);
                            }
                            catch(Exception)
                            {
                                MessageBox.Show("參數配置錯誤");
                                //MessageBox.Show(e.Message);
                                AutoFlag = false;
                                break;
                            }
                            SetMsg("[" + Current_time.ToString("yyyy-MM-dd HH:mm:ss" + "]" + " "));
                            for (int i = 0; i < coilsBuffer.Length; i++)
                            {
                                SetMsg(coilsBuffer[i] + " ");
                            }
                            SetMsg("\r\n");
                            break;
                        case "02 Read DisCrete Inputs"://讀取輸入線圈/離散量線圈
                            SetReadParameters();
                            try
                            {
                                coilsBuffer = master.ReadInputs(slaveAddress, startAddress, numberOfPoints);
                            }
                            catch(Exception)
                            {
                                MessageBox.Show("參數配置錯誤");
                                AutoFlag = false;
                                break;
                            }
                            SetMsg("[" + Current_time.ToString("yyyy-MM-dd HH:mm:ss" + "]" + " "));
                            for (int i = 0; i < coilsBuffer.Length; i++)
                            {
                                SetMsg(coilsBuffer[i] + " ");
                            }
                            SetMsg("\r\n");
                            break;
                        case "03 Read Holding Registers"://讀取保持寄存器
                            SetReadParameters();
                            try
                            {
                                registerBuffer = master.ReadHoldingRegisters(slaveAddress, startAddress, numberOfPoints);
                            }
                            catch (Exception)
                            {
                                MessageBox.Show("參數配置錯誤");
                                AutoFlag = false;
                                break;
                            }
                            SetMsg("[" + Current_time.ToString("yyyy-MM-dd HH:mm:ss" + "]" + " "));
                            for (int i = 0; i < registerBuffer.Length; i++)
                            {
                                SetMsg(registerBuffer[i] + " ");
                            }
                            SetMsg("\r\n");
                            break;
                        case "04 Read Input Registers"://讀取輸入寄存器
                            SetReadParameters();
                            try
                            {
                                registerBuffer = master.ReadInputRegisters(slaveAddress, startAddress, numberOfPoints);
                            }
                            catch (Exception)
                            {
                                MessageBox.Show("參數配置錯誤");
                                AutoFlag = false;
                                break;
                            }
                            SetMsg("[" + Current_time.ToString("yyyy-MM-dd HH:mm:ss" + "]" + " "));
                            for (int i = 0; i < registerBuffer.Length; i++)
                            {
                                SetMsg(registerBuffer[i] + " ");
                            }
                            SetMsg("\r\n");
                            break;
                        case "05 Write Single Coil"://寫單個線圈
                            SetWriteParametes();
                            await master.WriteSingleCoilAsync(slaveAddress, startAddress, coilsBuffer[0]);
                            break;
                        case "06 Write Single Registers"://寫單個輸入線圈/離散量線圈
                            SetWriteParametes();
                            await master.WriteSingleRegisterAsync(slaveAddress, startAddress, registerBuffer[0]);
                            break;
                        case "0F Write Multiple Coils"://寫一組線圈
                            SetWriteParametes();
                            await master.WriteMultipleCoilsAsync(slaveAddress, startAddress, coilsBuffer);
                            break;
                        case "10 Write Multiple Registers"://寫一組保持寄存器
                            SetWriteParametes();
                            await master.WriteMultipleRegistersAsync(slaveAddress, startAddress, registerBuffer);
                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    MessageBox.Show("請選擇功能碼!");
                }
                port.Close();
            }
            catch (Exception ex)
            {
                port.Close();
                MessageBox.Show(ex.Message);
            }
        }
        #endregion
        /// <summary>
        /// 設置讀參數
        /// </summary>
        private void SetReadParameters()
        {
            if (txt_startAddr1.Text == "" || txt_slave1.Text == "" || txt_length.Text == "")
            {
                MessageBox.Show("請?zhí)顚懽x參數!");
            }
            else
            {
                slaveAddress = byte.Parse(txt_slave1.Text);
                startAddress = ushort.Parse(txt_startAddr1.Text);
                numberOfPoints = ushort.Parse(txt_length.Text);
            }
        }
        /// <summary>
        /// 設置寫參數
        /// </summary>
        private void SetWriteParametes()
        {
            if (txt_startAddr2.Text == "" || txt_slave2.Text == "" || txt_data.Text == "")
            {
                MessageBox.Show("請?zhí)顚憣憛?");
            }
            else
            {
                slaveAddress = byte.Parse(txt_slave2.Text);
                startAddress = ushort.Parse(txt_startAddr2.Text);
                //判斷是否寫線圈
                if (functionOder == 4 || functionOder == 6)
                {
                    string[] strarr = txt_data.Text.Split(' ');
                    coilsBuffer = new bool[strarr.Length];
                    //轉化為bool數組
                    for (int i = 0; i < strarr.Length; i++)
                    {
                        // strarr[i] == "0" ? coilsBuffer[i] = false : coilsBuffer[i] = true;
                        if (strarr[i] == "0")
                        {
                            coilsBuffer[i] = false;
                        }
                        else
                        {
                            coilsBuffer[i] = true;
                        }
                    }
                }
                else
                {
                    //轉化ushort數組
                    string[] strarr = txt_data.Text.Split(' ');
                    registerBuffer = new ushort[strarr.Length];
                    for (int i = 0; i < strarr.Length; i++)
                    {
                        registerBuffer[i] = ushort.Parse(strarr[i]);
                    }
                }
            }
        }
        /// <summary>
        /// 創(chuàng)建委托,打印日志
        /// </summary>
        /// <param name="msg"></param>
        public void SetMsg(string msg)
        {
            richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(msg); }));
        }
        /// <summary>
        /// 清空日志
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click(object sender, EventArgs e)
        {
            richTextBox1.Clear();
        }
        /// <summary>
        /// 單擊button1事件,串口完成一次讀/寫操作
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            //AutoFlag = false;
            //button_AutomaticTest.Enabled = true;
            try
            {
                //初始化串口參數
                InitSerialPortParameter();
                master = ModbusSerialMaster.CreateRtu(port);
                ExecuteFunction();
            }
            catch (Exception)
            {
                MessageBox.Show("初始化異常");
            }
        }
        /// <summary>
        /// 自動測試初始化
        /// </summary>
        private void AutomaticTest()
        {
            AutoFlag = true;
            button1.Enabled = false;
            InitSerialPortParameter();
            master = ModbusSerialMaster.CreateRtu(port);
            Task.Factory.StartNew(() =>
            {
                //初始化串口參數
                while (AutoFlag)
                {
                    try
                    {
                        ExecuteFunction();
                    }
                    catch (Exception)
                    {
                        MessageBox.Show("初始化異常");
                    }
                    Thread.Sleep(500);
                }
            });
        }
        /// <summary>
        /// 讀取數據時,失能寫數據;寫數據時,失能讀數據
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBox1.SelectedIndex >= 4)
            {
                groupBox2.Enabled = true;
                groupBox1.Enabled = false;
            }
            else
            {
                groupBox1.Enabled = true;
                groupBox2.Enabled = false;
            }
            //委托事件,在主線程中創(chuàng)建的控件,在子線程中讀取設置控件的屬性會出現異常,使用Invoke方法可以解決
            comboBox1.Invoke(new Action(() => { functionCode = comboBox1.SelectedItem.ToString(); functionOder = comboBox1.SelectedIndex; }));
        }
        /// <summary>
        /// 將打印日志顯示到最新接收到的符號位置
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void richTextBox1_TextChanged(object sender, EventArgs e)
        {
            this.richTextBox1.SelectionStart = int.MaxValue;
            this.richTextBox1.ScrollToCaret();
        }
        /// <summary>
        /// 自動化測試
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_AutomaticTest_Click(object sender, EventArgs e)
        {
            AutoFlag = false;
            button_AutomaticTest.Enabled = false; //自動收發(fā)按鈕失能,避免從復開啟線程
            if (AutoFlag == false)
            {
                AutomaticTest();
            }
        }
        /// <summary>
        /// 串口關閉,停止讀/寫
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_ClosePort_Click(object sender, EventArgs e)
        {
            AutoFlag = false;
            button1.Enabled = true;
            button_AutomaticTest.Enabled = true;
            t.Enabled = false;//失能定時器
            if (port.IsOpen)
            {
                port.Close();
            }
        }
        #region 串口下拉列表刷新
        /// <summary>
        /// 刷新下拉列表顯示
        /// </summary>
        private void GetSerialLstTb1()
        {
            //清除cmb_portname顯示
            cmb_portname.SelectedIndex = -1;
            cmb_portname.Items.Clear();
            //獲取串口列表
            string[] serialLst = SerialPort.GetPortNames();
            if (serialLst.Length > 0)
            {
                //取串口進行排序
                Array.Sort(serialLst);
                //將串口列表輸出到cmb_portname
                cmb_portname.Items.AddRange(serialLst);
                cmb_portname.SelectedIndex = 0;
            }
        }
        /// <summary>
        /// 消息處理
        /// </summary>
        /// <param name="m"></param>
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)                                  //判斷消息類型
            {
                case WM_DEVICE_CHANGE:                      //設備改變消息
                    {
                        GetSerialLstTb1();                  //設備改變時重新花去串口列表
                    }
                    break;
            }
            base.WndProc(ref m);
        }
        #endregion
        private void label11_Click(object sender, EventArgs e)
        {
        }
        private void txt_slave1_TextChanged(object sender, EventArgs e)
        {
        }
        private void label7_Click(object sender, EventArgs e)
        {
        }
        private void txt_startAddr1_TextChanged(object sender, EventArgs e)
        {
        }
        private void label8_Click(object sender, EventArgs e)
        {
        }
        private void txt_length_TextChanged(object sender, EventArgs e)
        {
        }
    }
}

在線程中對控件的屬性進行操作可能會出現代碼異常,可以使用Invoke委托方法完成相應的操作:

public void SetMsg(string msg)
{
richTextBox1.Invoke(new Action(() => { richTextBox1.AppendText(msg); }));
}

在進行自動讀/寫操作時,為避免多次點擊按鍵控件,多次重復建立新線程;在進入自動讀寫線程中時,將對應的按鍵控件失能,等待停止讀寫操作時再使能:

private void AutomaticTest()
{
    AutoFlag = true;
    button1.Enabled = false;

    InitSerialPortParameter();
    master = ModbusSerialMaster.CreateRtu(port);

    Task.Factory.StartNew(() =>
    {
        //初始化串口參數
        
        while (AutoFlag)
        {
            
            try
            {

                ExecuteFunction();
            
            }
            catch (Exception)
            {
                MessageBox.Show("初始化異常");
            }
            Thread.Sleep(500);
        }
    });
}

自動獲取當前設備的可用串口實現如下:

#region 串口下拉列表刷新
/// <summary>
        /// 刷新下拉列表顯示
        /// </summary>
private void GetSerialLstTb1()
{
    //清除cmb_portname顯示
    cmb_portname.SelectedIndex = -1;
    cmb_portname.Items.Clear();
    //獲取串口列表
    string[] serialLst = SerialPort.GetPortNames();
    if (serialLst.Length > 0)
    {
        //取串口進行排序
        Array.Sort(serialLst);
        //將串口列表輸出到cmb_portname
        cmb_portname.Items.AddRange(serialLst);
        cmb_portname.SelectedIndex = 0;
    }
}

/// <summary>
        /// 消息處理
        /// </summary>
        /// <param name="m"></param>
protected override void WndProc(ref Message m)
{
    switch (m.Msg)                                  //判斷消息類型
    {
        case WM_DEVICE_CHANGE:                      //設備改變消息
            {
                GetSerialLstTb1();                  //設備改變時重新花去串口列表
            }
            break;
    }
    base.WndProc(ref m);
}
#endregion

對本次實例進行測試需要使用到串口模擬軟件,串口模擬器可以到網上下載

Modbus從站需要完成一下兩步操作:

一、菜單欄Connection-----Connect

二、菜單欄Setup-----Slave Definition

最后需要運行自己創(chuàng)建的Modbus RTU Master上位機,完成相應的配置:

實現的最終效果:

到此這篇關于C# NModbus RTU通信實現方法詳解的文章就介紹到這了,更多相關C# NModbus RTU通信內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 二叉樹的遍歷算法(詳細示例分析)

    二叉樹的遍歷算法(詳細示例分析)

    以下代碼是對二叉樹的遍歷算法進行了分析介紹,需要的朋友可以參考下
    2013-05-05
  • C#操作XML方法詳解

    C#操作XML方法詳解

    對于XML讀寫操作,項目中經常要用到,之前木有好好總結過,例如LINQ TO XML也用過,這篇文章主要介紹了C#操作XML的方法
    2021-11-11
  • C#圖片處理如何生成縮略圖的實現

    C#圖片處理如何生成縮略圖的實現

    本文主要介紹了C#圖片處理如何生成縮略圖的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • C#實現中文日歷Calendar

    C#實現中文日歷Calendar

    這篇文章介紹了C#實現中文日歷Calendar的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05
  • C#使用IronPython庫調用Python腳本

    C#使用IronPython庫調用Python腳本

    這篇文章介紹了C#使用IronPython庫調用Python腳本的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • C#實現簡單過濾非法字符實例

    C#實現簡單過濾非法字符實例

    這篇文章主要介紹了C#實現簡單過濾非法字符的方法,涉及C#針對字符串遍歷與判斷的相關技巧,非常簡單實用,需要的朋友可以參考下
    2015-11-11
  • C#固定大小緩沖區(qū)及使用指針復制數據詳解

    C#固定大小緩沖區(qū)及使用指針復制數據詳解

    這篇文章主要為大家介紹了C#固定大小緩沖區(qū)及使用指針復制數據詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • C#建立測試用例系統的示例代碼

    C#建立測試用例系統的示例代碼

    這篇文章主要介紹了C#建立測試用例系統的方法,文中示例代碼非常詳細,幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-07-07
  • 最好用的WPF加載動畫功能

    最好用的WPF加載動畫功能

    當開發(fā)應用程序時,提供良好的用戶體驗(UX)是至關重要的,加載動畫作為一種有效的溝通工具,它不僅能告知用戶系統正在工作,還能夠通過視覺上的吸引力來增強整體用戶體驗,本文給大家介紹了最好用的WPF加載動畫功能,需要的朋友可以參考下
    2025-01-01
  • C#面向切面編程之AspectCore用法詳解

    C#面向切面編程之AspectCore用法詳解

    AspectCore?是Lemon名下的一個國產Aop框架,提供了一個全新的輕量級和模塊化的Aop解決方案,下面我們就來深入了解下AspectCore在C#中的具體使用吧
    2024-01-01

最新評論