『7x24小时有问必答』
此代码主要供系列文章:
《入门项目【自动打螺丝机】C#、PLC、触摸屏实战》学习使用。

界面:

界面代码:
using System;
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 static System.Windows.Forms.VisualStyles.VisualStyleElement;

namespace PLC_Control
{
    public partial class Form1 : Form
    {
        FXPLC fxPLC;    //实例化

        public Form1()
        {
            InitializeComponent();

        }

        //点击连接按钮
        private void button1_Click(object sender, EventArgs e)
        {
            fxPLC = new FXPLC("COM16");
            if (fxPLC.Connect())
            {
                MessageBox.Show("PLC连接成功");
            }
            else
            {
                MessageBox.Show("PLC连接失败");
            }
        }

        //点击读取D0按钮
        private void button2_Click(object sender, EventArgs e)
        {
            // 读取D0
            if (fxPLC.ReadD0(out string hexVal, out int decVal))
            {
                textBox1.Text = decVal.ToString();
                MessageBox.Show($"读取D0成功:十六进制={hexVal},十进制={decVal}");
            }
            else
            {
                MessageBox.Show("读取D0失败");
            }
        }

        //点击写入D0按钮
        private void button3_Click(object sender, EventArgs e)
        {
            string strValue = textBox2.Text;
            // 写入D0
            if (fxPLC.WriteD0(strValue))
            {
                MessageBox.Show($"写入D0成功:十六进制" + strValue);
            }
            else
            {
                MessageBox.Show("写入D0失败");
            }
        }

        //点击读取M100~106按钮
        private void button6_Click(object sender, EventArgs e)
        {
            string strMValue = "";
            if(fxPLC.ReadM(out strMValue))
            {
                textBox3.Text = strMValue.ToString();
                MessageBox.Show($"读取M100~M106成功:二进制" + strMValue);
            }
            else
            {
                MessageBox.Show("读取M100~M106失败");
            }
        }

        //点击置位M按钮
        private void button4_Click(object sender, EventArgs e)
        {
            if(fxPLC.WriteM(textBox4.Text, true))
            {
                MessageBox.Show($"置位" + textBox4.Text+"成功");
            }
            else
            {
                MessageBox.Show($"置位" + textBox4.Text + "失败");
            }
        }

        //点击复位M按钮
        private void button5_Click(object sender, EventArgs e)
        {
            if(fxPLC.WriteM(textBox4.Text, false))
            {
                MessageBox.Show($"复位" + textBox4.Text + "成功");
            }
            else
            {
                MessageBox.Show($"复位" + textBox4.Text + "失败");
            }

        }
    }
}

库代码:
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PLC_Control
{
    internal class FXPLC
    {

        private SerialPort _serialPort;
        private bool _isConnected = false;
        private const string D0_ADDRESS = "D0000"// D0寄存器固定地址
        private const string M_ADDRESS = "M0100"//M100寄存器固定地址

        /// 
        /// 初始化通讯对象
        /// 
        /// 串口号(如"COM3")
        /// 波特率(默认9600)
        /// PLC站号(默认0)
        public FXPLC(string portName, int baudRate = 9600byte stationNumber = 0)
        {
            _serialPort = new SerialPort
            {
                PortName = portName,
                BaudRate = baudRate,
                DataBits = 8,
                StopBits = StopBits.One,
                Parity = Parity.None,
                Encoding = Encoding.ASCII,
                ReadTimeout = 1000,
                WriteTimeout = 1000
            };

            StationNumber = stationNumber;
        }

        /// 
        /// PLC站号
        /// 
        public byte StationNumber { getset; }

        /// 
        /// 连接PLC
        /// 
        /// 是否连接成功
        public bool Connect()
        {
            try
            {
                if (!_serialPort.IsOpen)
                {
                    _serialPort.Open();
                    _isConnected = true;
                    return true;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }

        /// 
        /// 断开连接
        /// 
        public void Disconnect()
        {
            if (_serialPort.IsOpen)
            {
                _serialPort.Close();
            }
            _isConnected = false;
        }

        /// 
        /// 读取D0寄存器的值(16位)
        /// 
        /// 返回的16进制字符串(4位)
        /// 返回的十进制数值
        /// 是否读取成功
        public bool ReadD0(out string hexValue, out int decValue)
        {
            hexValue = "";
            decValue = 0;

            if (!_isConnected)
                return false;

            try
            {
                // 构建RR指令帧(读取D0)
                byte[] requestFrame = BuildReadFrame();
                if (requestFrame == null)
                    return false;

                // 发送并接收响应
                byte[] response = SendAndReceive(requestFrame);
                if (response == null)
                    return false;

                // 解析响应
                if (ParseReadResponse(response, out hexValue))
                {
                    // 转换为十进制
                    decValue = int.Parse(hexValue, System.Globalization.NumberStyles.HexNumber);
                    return true;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }

        /// 
        /// 写入值到D0寄存器
        /// 
        /// 16进制字符串(4位,如"1234")
        /// 是否写入成功
        public bool WriteD0(string hexValue)
        {
            if (!_isConnected)
                return false;

            // 验证输入格式
            if (string.IsNullOrEmpty(hexValue) || hexValue.Length != 4 || !IsHexString(hexValue))
                return false;

            try
            {
                // 构建WR指令帧(写入D0)
                byte[] requestFrame = BuildWriteFrame(hexValue.ToUpper());
                if (requestFrame == null)
                    return false;

                // 发送并接收响应
                byte[] response = SendAndReceive(requestFrame);
                if (response == null)
                    return false;

                // 解析响应
                return ParseWriteResponse(response);
            }
            catch
            {
                return false;
            }
        }

        /// 
        /// 构建读取D0的请求帧(RR指令)
        /// 
        private byte[] BuildReadFrame()
        {
            try
            {
                // 帧结构:ENQ(0x05) + 站号(ASCII) + "0" + "WR" + "0" + "D0000" + "01" + 校验和 + CR+LF
                StringBuilder frame = new StringBuilder();
                frame.Append((char)0x05);                              // ENQ
                frame.Append((char)0x30);
                frame.Append((char)(StationNumber + 0x30));             // 站号(ASCII)
                frame.Append("FF");                                      // PC号
                frame.Append("WR");                                     // 指令码(WR=读取)
                frame.Append("0");                                      // 报文等待
                frame.Append(D0_ADDRESS);                               // D0地址
                frame.Append("01");                                     // 读取数量(1个)
                frame.Append(CalculateChecksum(frame.ToString().Substring(1)));      // 校验和
                frame.Append("\r\n");                                   // 帧尾

                return Encoding.ASCII.GetBytes(frame.ToString());
            }
            catch
            {
                return null;
            }
        }

        /// 
        /// 构建写入D0的请求帧(WW指令)
        /// 
        private byte[] BuildWriteFrame(string hexValue)
        {
            try
            {
                // 帧结构:ENQ(0x05) + 站号(ASCII) + "0" + "WW" + "0" + "D0000" + "01" + 数据 + 校验和 + CR+LF
                StringBuilder frame = new StringBuilder();
                frame.Append((char)0x05);                              // ENQ
                frame.Append((char)0x30);
                frame.Append((char)(StationNumber + 0x30));             // 站号(ASCII)
                frame.Append("FF");                                      // PC号
                frame.Append("WW");                                     // 指令码(WW=写入)
                frame.Append("0");                                      // 报文等待
                frame.Append(D0_ADDRESS);                               // D0地址
                frame.Append("01");                                     // 写入数量(1个)
                frame.Append(hexValue);                                 // 写入的数据
                frame.Append(CalculateChecksum(frame.ToString().Substring(1)));      // 校验和
                frame.Append("\r\n");                                   // 帧尾

                return Encoding.ASCII.GetBytes(frame.ToString());
            }
            catch
            {
                return null;
            }
        }

        /// 
        /// 发送数据并接收响应
        /// 
        private byte[] SendAndReceive(byte[] data)
        {
            try
            {
                _serialPort.DiscardInBuffer();
                _serialPort.DiscardOutBuffer();

                // 发送数据
                _serialPort.Write(data, 0, data.Length);

                // 读取完整响应(替换原有的 Read 逻辑)
                byte[] response = ReadCompleteResponse();

                if (response == null || response.Length == 0)
                {
                    Console.WriteLine("未收到响应或响应超时");
                    return null;
                }

                return response;
            }
            catch
            {
                return null;
            }
        }

        /// 
        /// 读取M100~M106寄存器的值(16位)
        /// 
        /// 返回的2进制字符串(7位)
        /// 是否读取成功
        public bool ReadM(out string binValue)
        {
            binValue = "0000000";

            if (!_isConnected)
                return false;

            try
            {
                // 构建BR指令帧(读取M100~M106)
                byte[] requestFrame = BuildReadM();
                if (requestFrame == null)
                    return false;

                // 发送并接收响应
                byte[] response = SendAndReceive(requestFrame);
                if (response == null)
                    return false;

                // 解析响应
                if (ParseReadMResponse(response, out binValue))
                {
                    return true;
                }
                return false;
            }
            catch
            {
                return false;
            }
        }

        /// 
        /// 构建读取M100~M106的请求帧(BR指令)
        /// 
        private byte[] BuildReadM()
        {
            try
            {
                // 帧结构:ENQ(0x05) + 站号0(ASCII) + "FF" + "BR" + "0" + "M0100" + "07" + 校验和 + CR+LF
                StringBuilder frame = new StringBuilder();
                frame.Append((char)0x05);                               // ENQ
                frame.Append("00");                                     // 站号(ASCII)
                frame.Append("FF");                                     // PC号
                frame.Append("BR");                                     // 指令码(BR=读取M)
                frame.Append("0");                                      // 报文等待
                frame.Append(M_ADDRESS);                               //  M100地址
                frame.Append("07");                                     // 读取数量(7个)
                frame.Append(CalculateChecksum(frame.ToString().Substring(1)));      // 校验和
                frame.Append("\r\n");                                   // 帧尾

                return Encoding.ASCII.GetBytes(frame.ToString());
            }
            catch
            {
                return null;
            }
        }

        /// 
        /// 写入M
        /// 
        /// 地址
        /// 
        /// 
        public bool WriteM(string strAddress, bool bStatus)
        {
            if (!_isConnected)
                return false;

            try
            {
                // 构建BR指令帧(读取M100~M106)
                byte[] requestFrame = BuildWriteM(strAddress, bStatus);
                if (requestFrame == null)
                    return false;

                // 发送并接收响应
                byte[] response = SendAndReceive(requestFrame);
                if (response == null)
                    return false;

                // 解析响应
                return ParseWriteResponse(response);

            }
            catch
            {
                return false;
            }
        }

        /// 
        /// 构建写入M的请求帧(BW指令)
        /// 
        private byte[] BuildWriteM(string strAddress, bool bStatus)
        {
            try
            {
                // 帧结构:ENQ(0x05) + 站号(ASCII) + "0" + "BW" + "0" + "M0000" + "01" + 数据 + 校验和 + CR+LF
                StringBuilder frame = new StringBuilder();
                frame.Append((char)0x05);                              // ENQ
                frame.Append("00");                                     // 站号(ASCII)
                frame.Append("FF");                                      // PC号
                frame.Append("BW");                                     // 指令码(BW=写入)
                frame.Append("0");                                      // 报文等待
                frame.Append(strAddress);                               // 写入地址
                frame.Append("01");                                     // 写入数量(1个)
                frame.Append(bStatus?"1":"0");                                 // 写入的数据
                frame.Append(CalculateChecksum(frame.ToString().Substring(1)));      // 校验和
                frame.Append("\r\n");                                   // 帧尾

                return Encoding.ASCII.GetBytes(frame.ToString());
            }
            catch
            {
                return null;
            }
        }

        private byte[] ReadCompleteResponse()
        {
            List<byte> responseBuffer = new List<byte>();
            DateTime startTime = DateTime.Now;
            byte[] tempBuffer = new byte[128];

            try
            {
                // 循环读取,直到收到完整帧或超时(3秒)
                while ((DateTime.Now - startTime).TotalMilliseconds < 3000)
                {
                    // 检查是否有数据可读取
                    if (_serialPort.BytesToRead > 0)
                    {
                        int bytesRead = _serialPort.Read(tempBuffer, 0, tempBuffer.Length);
                        responseBuffer.AddRange(tempBuffer.Take(bytesRead).ToArray());

                        // 检查是否包含帧尾 0D 0A(CR+LF),判断帧是否完整
                        if (responseBuffer.Count >= 2 &&
                            responseBuffer[responseBuffer.Count - 2] == 0x0D &&
                            responseBuffer[responseBuffer.Count - 1] == 0x0A)
                        {
                            break// 已获取完整帧,退出循环
                        }
                    }
                    else
                    {
                        // 无数据时短暂休眠,避免CPU占用过高
                        System.Threading.Thread.Sleep(10);
                    }
                }

                return responseBuffer.ToArray();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"读取响应失败:{ex.Message}");
                return null;
            }
        }

        /// 
        /// 解析读取响应
        /// 
        private bool ParseReadResponse(byte[] response, out string hexValue)
        {
            hexValue = "";

            // 有效响应至少包含:ACK(1) + 站号(1) + PC号(1) + 数据长度(2) + 数据(4) + 校验(2) + CR(1) + LF(1)
            if (response.Length < 12)
                return false;

            // 检查响应头(ACK)
            if (response[0] != 0x02)
                return false;

            // 提取数据(4位十六进制)
            hexValue = Encoding.ASCII.GetString(response, 54);
            return true;
        }

        /// 
        /// 解析写入响应
        /// 
        private bool ParseWriteResponse(byte[] response)
        {
            // 有效响应至少包含:ACK(1) + 站号(1) + PC号(1) + CR(1) + LF(1)
            if (response.Length < 5)
                return false;

            // 检查响应头(ACK)
            if (response[0] == 0x06)
                return true;

            return false;
        }

        /// 
        /// 解析读取M响应
        /// 
        private bool ParseReadMResponse(byte[] response, out string binValue)
        {
            binValue = "";

            // 有效响应至少包含:ACK(1) + 站号(1) + PC号(1) + 数据(7) + 校验(2) + CR(1) + LF(1)
            if (response.Length < 12)
                return false;

            // 检查响应头(ACK)
            if (response[0] != 0x02)
                return false;

            // 提取数据(4位十六进制)
            binValue = Encoding.ASCII.GetString(response, 57);
            return true;
        }

        /// 
        /// 计算校验和
        /// 
        private string CalculateChecksum(string data)
        {
            int sum = 0;
            foreach (char c in data)
            {
                sum += (int)c;
            }
            // 取低8位,转换为2位十六进制
            return (sum & 0xFF).ToString("X2");
        }

        /// 
        /// 验证是否为十六进制字符串
        /// 
        private bool IsHexString(string hex)
        {
            foreach (char c in hex)
            {
                if (!((c >= '0' && c <= '9') ||
                     (c >= 'A' && c <= 'F') ||
                     (c >= 'a' && c <= 'f')))
                {
                    return false;
                }
            }
            return true;
        }

        /// 
        /// 释放资源
        /// 
        public void Dispose()
        {
            Disconnect();
            _serialPort?.Dispose();
        }
    }
}


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

上一主题上一主题         下一主题下一主题
QQ手机版小黑屋粤ICP备17165530号

关于我们·投诉举报· 用户帮助· 联系我们 · 本站服务 · 版权声明· 隐私政策 · 投搞指南

法律保护:PLC技术网,plcjs.com,plcjs.net等字样
Copyright 2010-2030. All rights reserved. 


微信公众号二维码 抖音二维码 百家号二维码 今日头条二维码哔哩哔哩二维码