[C#] C# 透過modbus-tcp 去FX5 取特定位址

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 System.IO;
using System.Net.Sockets;

namespace FX5_TCPIP_ModbusTEST
{
    public partial class Form1 : Form
    {
        private ModbusTcpClient modbusClient;

        public Form1()
        {
            InitializeComponent();
            // 初始化 ModbusTcpClient
            //modbusClient = new ModbusTcpClient("192.168.1.100", 502);            
        }

        bool switchType=false;

        private async void button1_Click(object sender, EventArgs e)
        {
            if(switchType==false)
            {
                try
                {
                    modbusClient = new ModbusTcpClient("192.168.1.100", 502, richTextBox1);
                    await modbusClient.ConnectAsync();
                    label4.Text= button1.Text = "己連線";
                    label4.ForeColor = Color.Green;
                    richTextBox1.SelectionStart = richTextBox1.Text.Length;
                    richTextBox1.ScrollToCaret();
                }
                catch (Exception ex)
                {
                    richTextBox1.BeginInvoke(new Action(() =>
                    {
                        richTextBox1.AppendText($"啟始錯誤: {ex.Message}\r\n");
                    }));
                }
                switchType = true;
            }
            else
            {
                try
                {
                    modbusClient.Disconnect();
                    label4.Text = button1.Text = "己斷線";
                    label4.ForeColor = Color.Red;
                    richTextBox1.SelectionStart = richTextBox1.Text.Length;
                    richTextBox1.ScrollToCaret();
                }
                catch (Exception ex)
                {
                    richTextBox1.BeginInvoke(new Action(() =>
                    {
                        richTextBox1.AppendText($"斷接錯誤: {ex.Message}\r\n");
                    }));
                }
                switchType=false;
            }
        }
        int w = 0; ushort vTemp = 0;
        private async void button2_Click(object sender, EventArgs e)
        {
            try
            {
                // 讀取位址41001的值
              
                ushort value = await modbusClient.ReadHoldingRegister(1000);  // 注意:有些設備可能需要用 41001-40001=1000
                /*
                 0 = 無異常
                1 = Z軸伺服異常
                2 = 鼓風機OL過載
                3 = 雷射異常
                4 = 緊急停止
                5 = 轉盤正轉定位異常
                6 = 轉盤反轉定位異常
                7 = 雷射未READY,禁止啟動
                8 = 完成品未取料,禁止啟動
                9 = 自動流程動作中,禁止再次啟動
                 */
                string ans = "";
                if (vTemp != value)
                {
                    vTemp = value;
                   

                    switch (value)
                    {
                        case 0:
                            ans = "無異常";
                            label6.ForeColor = Color.SteelBlue;
                            break;
                        case 1:
                            ans = "Z軸伺服異常";
                            File.AppendAllText("雷刻異常碼記錄.txt", datetime + " 異常碼:1" + ans + "\r\n");
                            label6.ForeColor = Color.Red;
                            break;
                        case 2:
                            ans = "鼓風機OL過載";
                            File.AppendAllText("雷刻異常碼記錄.txt", datetime + " 異常碼:2" + ans + "\r\n");
                            label6.ForeColor = Color.Red;
                            break;
                        case 3:
                            ans = "雷射異常";
                            File.AppendAllText("雷刻異常碼記錄.txt", datetime + " 異常碼:3" + ans + "\r\n");
                            label6.ForeColor = Color.Red;
                            break;
                        case 4:
                            ans = "緊急停止";
                            File.AppendAllText("雷刻異常碼記錄.txt", datetime + " 異常碼:4" + ans + "\r\n");
                            label6.ForeColor = Color.Red;
                            break;
                        case 5:
                            ans = "轉盤正轉定位異常";
                            File.AppendAllText("雷刻異常碼記錄.txt", datetime + " 異常碼:5" + ans + "\r\n");
                            label6.ForeColor = Color.Red;
                            break;
                        case 6:
                            ans = "轉盤反轉定位異常";
                            File.AppendAllText("雷刻異常碼記錄.txt", datetime + " 異常碼:6" + ans + "\r\n");
                            label6.ForeColor = Color.Red;
                            break;
                        case 7:
                            ans = "雷射未READY,禁止啟動";
                            File.AppendAllText("雷刻異常碼記錄.txt", datetime + " 異常碼:7" + ans + "\r\n");
                            label6.ForeColor = Color.Red;
                            break;
                        case 8:
                            ans = "完成品未取料,禁止啟動";
                            File.AppendAllText("雷刻異常碼記錄.txt", datetime + " 異常碼:8" + ans + "\r\n");
                            label6.ForeColor = Color.Red;
                            break;
                        case 9:
                            ans = "自動流程動作中,禁止再次啟動";
                            File.AppendAllText("雷刻異常碼記錄.txt", datetime + " 異常碼:9" + ans + "\r\n");
                            label6.ForeColor = Color.Red;
                            break;
                    }
                    label6.Text = "回應碼:" + value.ToString() + "  " + ans;
                }
               
               

 

 


                if (richTextBox1.Text.Length > 3000)
                {
                    richTextBox1.Text = "";
                    w = 0;
                }
                w++;
                richTextBox1.BeginInvoke(new Action(() =>
                {
                    richTextBox1.AppendText(w+$"目前狀態: {value}\r\n");
                    richTextBox1.SelectionStart = richTextBox1.Text.Length;
                    richTextBox1.ScrollToCaret();
                }));
            }
            catch (Exception ex)
            {
                richTextBox1.BeginInvoke(new Action(() =>
                {
                    richTextBox1.AppendText(w+$"錯誤: {ex.Message}\r\n");
                    richTextBox1.SelectionStart = richTextBox1.Text.Length;
                    richTextBox1.ScrollToCaret();
                }));
            }

        }

        private void button3_Click(object sender, EventArgs e)
        {
            try
            {
                modbusClient.Disconnect();
                button1.Text = "己斷線";

                this.Close();
            }
            catch (Exception ex)
            {
                richTextBox1.BeginInvoke(new Action(() =>
                {
                    richTextBox1.AppendText($"斷接錯誤: {ex.Message}\r\n");
                }));

                this.Close();
            }

            this.Close();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            button1.PerformClick();

            ////////////////////////////////////////////////////
            X = this.Width;//獲取窗體的寬度
            Y = this.Height;//獲取窗體的高度
            setTag(this);//調用方法
        }
        /// <summary>
        /// 將控件的寬,高,左邊距,頂邊距和字體大小暫存到tag屬性中
        /// </summary>
        /// <param name="cons">遞歸控件中的控件</param>
        /// 
        private float X;//當前窗體的寬度
        private float Y;//當前窗體的高度
        private void setTag(Control cons)
        {
            foreach (Control con in cons.Controls)
            {
                con.Tag = con.Width + ":" + con.Height + ":" + con.Left + ":" + con.Top + ":" + con.Font.Size;
                if (con.Controls.Count > 0)
                    setTag(con);
            }
        }

        //根據窗體大小調整控件大小
        private void setControls(float newx, float newy, Control cons)
        {
            //遍歷窗體中的控件,重新設置控件的值
            foreach (Control con in cons.Controls)
            {

                string[] mytag;
                if (con.Tag == null)
                { }
                else
                {
                    mytag = con.Tag.ToString().Split(new char[] { ':' });//獲取控件的Tag屬性值,並分割後存儲字符串數組

                    float a = System.Convert.ToSingle(mytag[0]) * newx;//根據窗體縮放比例確定控件的值,寬度
                    con.Width = (int)a;//寬度
                    a = System.Convert.ToSingle(mytag[1]) * newy;//高度
                    con.Height = (int)(a);
                    a = System.Convert.ToSingle(mytag[2]) * newx;//左邊距離
                    con.Left = (int)(a);
                    a = System.Convert.ToSingle(mytag[3]) * newy;//上邊緣距離
                    con.Top = (int)(a);
                    Single currentSize = System.Convert.ToSingle(mytag[4]) * newy;//字體大小
                    con.Font = new Font(con.Font.Name, currentSize, con.Font.Style, con.Font.Unit);
                    if (con.Controls.Count > 0)
                    {
                        setControls(newx, newy, con);
                    }
                }
            }
        }

 


        public class ModbusTcpClient
        {
            private string ipAddress;
            private int port;
            private TcpClient client;
            private RichTextBox richTextBox;  // 新增 RichTextBox 變數

            public ModbusTcpClient(string ipAddress, int port = 502, RichTextBox richTextBox = null)
            {
                this.ipAddress = ipAddress;
                this.port = port;
                this.client = new TcpClient();
                this.richTextBox = richTextBox;  // 保存 RichTextBox 參考
            }

            public async Task ConnectAsync()
            {
                try
                {
                    await client.ConnectAsync(ipAddress, port);
                    // 如果有 RichTextBox,就使用它來顯示訊息
                    if (richTextBox != null)
                    {
                        if (richTextBox.InvokeRequired)
                        {
                            richTextBox.BeginInvoke(new Action(() =>
                            {
                                richTextBox.AppendText("PLC連線完成!\r\nConnected to PLC successfully!\r\n");
                            }));
                        }
                        else
                        {
                            richTextBox.AppendText("PLC連線完成!\r\nConnected to PLC successfully!\r\n");
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (richTextBox != null)
                    {
                        if (richTextBox.InvokeRequired)
                        {
                            richTextBox.BeginInvoke(new Action(() =>
                            {
                                richTextBox.AppendText($"PLC連線失敗!\r\nConnection failed: {ex.Message}\r\n");
                            }));
                        }
                        else
                        {
                            richTextBox.AppendText($"PLC連線失敗!\r\nConnection failed: {ex.Message}\r\n");
                        }
                    }
                    throw;
                }
            }


            public async Task<ushort> ReadRegisterValue(ushort startAddress)
            {
                byte[] frame = new byte[12];

                // Transaction ID
                frame[0] = 0x00;
                frame[1] = 0x01;

                // Protocol ID
                frame[2] = 0x00;
                frame[3] = 0x00;

                // Length
                frame[4] = 0x00;
                frame[5] = 0x06;

                // Unit ID
                frame[6] = 0x01;

                // Function code (04 for Read Input Registers)
                frame[7] = 0x04;

                // Starting Address
                frame[8] = (byte)(startAddress >> 8);
                frame[9] = (byte)(startAddress & 0xFF);

                // Quantity of registers (1)
                frame[10] = 0x00;
                frame[11] = 0x01;

                try
                {
                    NetworkStream stream = client.GetStream();
                    await stream.WriteAsync(frame, 0, frame.Length);

                    byte[] response = new byte[256];
                    int bytesRead = await stream.ReadAsync(response, 0, response.Length);

                    if (bytesRead >= 9)
                    {
                        // 將兩個bytes轉換為ushort
                        return (ushort)((response[9] << 8) | response[10]);
                    }

                    return 0;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Read error: {ex.Message}");
                    throw;
                }
            }

            public async Task<ushort> ReadHoldingRegister(ushort startAddress)
            {
                byte[] frame = new byte[12];

                // Transaction ID
                frame[0] = 0x00;
                frame[1] = 0x01;

                // Protocol ID
                frame[2] = 0x00;
                frame[3] = 0x00;

                // Length
                frame[4] = 0x00;
                frame[5] = 0x06;

                // Unit ID
                frame[6] = 0x01;

                // Function code (03 for Read Holding Registers)
                frame[7] = 0x03;  // 改用 03 功能碼

                // Starting Address
                frame[8] = (byte)(startAddress >> 8);
                frame[9] = (byte)(startAddress & 0xFF);

                // Quantity of registers (1)
                frame[10] = 0x00;
                frame[11] = 0x01;

                try
                {
                    NetworkStream stream = client.GetStream();
                    await stream.WriteAsync(frame, 0, frame.Length);

                    byte[] response = new byte[256];
                    int bytesRead = await stream.ReadAsync(response, 0, response.Length);

                    if (bytesRead >= 9)
                    {
                        // 將兩個bytes轉換為ushort
                        return (ushort)((response[9] << 8) | response[10]);
                    }

                    return 0;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"PLC位址讀取失敗\r\nRead error: {ex.Message}");
                    throw;
                }
            }

 

 

 

            private bool IsPrintableAscii(char c)
            {
                // Check if character is printable ASCII (between space and tilde)
                return c >= 32 && c <= 126;
            }

            public void Disconnect()
            {
                if (client != null && client.Connected)
                {
                    client.Close();
                    if (richTextBox != null)
                    {
                        if (richTextBox.InvokeRequired)
                        {
                            richTextBox.BeginInvoke(new Action(() =>
                            {
                                richTextBox.AppendText($"與PLC之間的連線已斷開!\r\nDisconnected from PLC");
                            }));
                        }
                    }
                }
            }
        }

        private void Form1_Resize(object sender, EventArgs e)
        {
            float newx = (this.Width) / X; //窗體寬度縮放比例
            float newy = (this.Height) / Y;//窗體高度縮放比例
            setControls(newx, newy, this);//隨窗體改變控件大小
        }

        string filedate = "",datetime="";
        private void timer1_Tick(object sender, EventArgs e)
        {
            filedate = DateTime.Now.ToString("yyMMdd");
            datetime = label3.Text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
            if (switchType)
            {
                autoRead();
            }
            else
            {
            }
        }


        public void autoRead()
        {
            button2.PerformClick();
        }

        private void button4_Click(object sender, EventArgs e)
        {

        }
    }
}

← 回列表