[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)
{
}
}
}