台達(Delta)PLC是国内工业自動化领域应用最广泛的PLCブランド之一,サポートModbus RTU和Modbus TCP通信プロトコル。C#作为Windowsプラットフォームトップマシン开发的主流语言,与台達PLC的Modbus通讯是工业自動化项目中的常见需求。本記事提供完全的C#トップマシン与台達PLC Modbus通信实例,包括连接管理、レジスタ読み書き、データの解析、断线再接続、多デバイス管理以及Windows Forms界面サンプル。
一、台達PLC Modbus通讯概述
1.1 台達PLCサポート的ModbusFunction
| function code | Function | 台達PLC对应エリア |
|---|---|---|
| 0x01 | リードコイルを読む | M(辅助继电器)、Y(出力继电器) |
| 0x02 | read discrete inputs | X(入力继电器) |
| 0x03 | read holding registers | D(データ·レジスタ) |
| 0x05 | write single coil | M、Y |
| 0x06 | write single register | D |
| 0x0F | write multiple coils | M、Y |
| 0x10 | write multiple registers | D |
1.2 台達PLCアドレスマッピング
台達PLC的Modbusアドレス与内部软元件的对应关系(以DVP系列为例):
| 软元件 | Modbusaddress(decimal) | 説明 |
|---|---|---|
| D0-D9999 | 0-9999 | データ·レジスタ,レジスタを保持する。 |
| M0-M9999 | 0-9999 | 辅助继电器,コイル |
| Y0-Y255 | 0-255(偏移2048) | 出力继电器,コイル |
| X0-X255 | 0-255(偏移2048) | 入力继电器,ショップ型入力 |
| S0-S999 | 0-999(偏移4096) | ステータス继电器,コイル |
| T0-T255触点 | 1024-1279 | タイマータイマー。触点ステータス,コイル(function code01/05) |
| C0-C255触点 | 1536-1791 | カウンター·カウンター触点ステータス,コイル(function code01/05) |
| T/Ccurrent value | 视型号而定 | 部分型号サポート経由Dレジスタマップ読み取り,需查阅对应手册 |
1.3 通信パラメータの設定
- Modbus TCP:PLC IPaddress + ポート502,駅からの住所デフォルト1
- Modbus RTU(COM1):9600bps,8データ·ビット,検証なし。,1ストップ·ビット(デフォルト)
- Modbus RTU(COM2):9600bps,8データ·ビット,検証なし。,1ストップ·ビット(デフォルト)
- slave address:可在PLC中に設置。,デフォルト1
- 通信プロトコル:需在PLCプログラミング软件中に設置。为Modbus RTU Slave或Modbus TCP
二、C# Modbus通讯类実装
2.1 Modbus TCPクライアント側は类
using System;
using System.Net.Sockets;
using System.Threading;
namespace DeltaPlcModbus
{
public class ModbusTcpClient : IDisposable
{
private TcpClient _client;
private NetworkStream _stream;
private ushort _transactionId = 0;
private readonly object _lock = new object();
public string IpAddress { get; }
public int Port { get; }
public byte SlaveId { get; set; } = 1;
public int Timeout { get; set; } = 3000;
public bool IsConnected => _client?.Connected ?? false;
public ModbusTcpClient(string ip, int port = 502)
{
IpAddress = ip;
Port = port;
}
/// <summary>
/// connectPLC(带重试)
/// </summary>
public bool Connect(int retryCount = 3)
{
for (int i = 0; i < retryCount; i++)
{
try
{
_client = new TcpClient();
_client.ReceiveTimeout = Timeout;
_client.SendTimeout = Timeout;
_client.Connect(IpAddress, Port);
_stream = _client.GetStream();
Console.WriteLine($"接続しましたPLC:{IpAddress}:{Port}");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"接続の失敗(No.{i + 1}次):{ex.Message}");
Thread.Sleep(1000);
}
}
return false;
}
/// <summary>
/// 切断する
/// </summary>
public void Disconnect()
{
_stream?.Close();
_client?.Close();
_client = null;
_stream = null;
}
/// <summary>
/// read holding registers(function code03)- readDregister
/// </summary>
public ushort[] ReadHoldingRegisters(ushort startAddr, ushort count)
{
lock (_lock)
{
byte[] request = BuildReadRequest(0x03, startAddr, count);
byte[] response = SendAndReceive(request);
if (response[7] != 0x03)
throw new ModbusException(response[7], response[8]);
byte byteCount = response[8];
ushort[] result = new ushort[byteCount / 2];
for (int i = 0; i < result.Length; i++)
{
result[i] = (ushort)((response[9 + i * 2] << 8) | response[9 + i * 2 + 1]);
}
return result;
}
}
/// <summary>
/// リードコイルを読む(function code01)- readM/YRelay
/// </summary>
public bool[] ReadCoils(ushort startAddr, ushort count)
{
lock (_lock)
{
byte[] request = BuildReadRequest(0x01, startAddr, count);
byte[] response = SendAndReceive(request);
if (response[7] != 0x01)
throw new ModbusException(response[7], response[8]);
byte byteCount = response[8];
bool[] result = new bool[count];
for (int i = 0; i < count; i++)
{
result[i] = (response[9 + i / 8] & (1 << (i % 8))) != 0;
}
return result;
}
}
/// <summary>
/// read discrete inputs(function code02)- readX入力
/// </summary>
public bool[] ReadDiscreteInputs(ushort startAddr, ushort count)
{
lock (_lock)
{
byte[] request = BuildReadRequest(0x02, startAddr, count);
byte[] response = SendAndReceive(request);
if (response[7] != 0x02)
throw new ModbusException(response[7], response[8]);
byte byteCount = response[8];
bool[] result = new bool[count];
for (int i = 0; i < count; i++)
{
result[i] = (response[9 + i / 8] & (1 << (i % 8))) != 0;
}
return result;
}
}
/// <summary>
/// write single register(function code06)- 写Dregister
/// </summary>
public void WriteSingleRegister(ushort addr, ushort value)
{
lock (_lock)
{
byte[] request = new byte[12];
BuildMBAPHeader(request, 6);
request[7] = 0x06;
request[8] = (byte)(addr >> 8);
request[9] = (byte)(addr & 0xFF);
request[10] = (byte)(value >> 8);
request[11] = (byte)(value & 0xFF);
byte[] response = SendAndReceive(request);
if (response[7] != 0x06)
throw new ModbusException(response[7], response[8]);
}
}
/// <summary>
/// write single coil(function code05)- 写M/YRelay
/// </summary>
public void WriteSingleCoil(ushort addr, bool value)
{
lock (_lock)
{
byte[] request = new byte[12];
BuildMBAPHeader(request, 6);
request[7] = 0x05;
request[8] = (byte)(addr >> 8);
request[9] = (byte)(addr & 0xFF);
request[10] = (byte)(value ? 0xFF : 0x00);
request[11] = 0x00;
byte[] response = SendAndReceive(request);
if (response[7] != 0x05)
throw new Mod bus Ex ception (res ponse [7 ], response [8]);
}
}
/// >
/// 쓰 기 다 중 레 지 스터 (기 능 코드 16) - D 레 지 스터 를 대 량 쓰 기
/// >
public vo id Write M ulti ple Reg ister s (ush ort start Add r , ush ort [ ] values)
{
잠 금 (_ lock)
{
by te by te Co unt = (byte) (valu es . Leng th * 2) ;
by te [ ] request = new by te [ 13 + by te Co unt ];
Bu ild M BA P He ader (re quest , (ush ort) ( 7 + by te Co unt));
요청 [7] = 0 x 10 ;
request[8] = (byte)(startAddr
request[9] = (byte)(startAddr
request[10] = (byte)(values.Length
request[11] = (byte)(values.Length
request [11] = by te Co unt ;
for (int i = 0; i
{
request[13 + i * 2] = (byte)(values[i]
request[13 + i * 2 + 1] = (byte)(values[i]
}
by te [ ] response = Send And Re ce ive (re quest);
if (res ponse [7] ! =) 0 x 10)
throw new Mod bus Ex ception (res ponse [7 ], response [8]);
}
}
/// >
/ / / 여러 코 일 쓰 기 (기 능 코드 15) - M / Y 릴 레이 대 량 쓰 기
/// >
public vo id Write M ulti ple Co ils (ush ort start Add r , bool [ ] values)
{
잠 금 (_ lock)
{
by te by te Co unt = (byte) (( values . Leng th + 7) / 8);
by te [ ] request = new by te [ 13 + by te Co unt ];
Bu ild M BA P He ader (re quest , (ush ort) ( 7 + by te Co unt));
요청 [7] = 0 x 0 F ;
request[8] = (byte)(startAddr
request[9] = (byte)(startAddr
request[10] = (byte)(values.Length
request[11] = (byte)(values.Length
request [11] = by te Co unt ;
for (int i = 0; i
{
if (val ue [ i ])
요청 [ 13 + i / 8 ]|= (바 이트) ( 1 (i % 8));<<
}
by te [ ] response = Send And Re ce ive (re quest);
if (res ponse [7] ! =) 0 x 0 F)
throw new Mod bus Ex ception (res ponse [7 ], response [8]);
}
}
읽기 요청 만들기
private by te [ ] Bu ild Read Re quest (byte fuk C ode , ush ort start Add r , ush ort count)
{
by te [ ] request = new by te [12] ;
Bu ild M BA P He ader (re quest , 6) ;
request [7] = function C ode ;
request[8] = (byte)(startAddr
request[9] = (byte)(startAddr
request[10] = (byte)(count
request[11] = (byte)(count
반환 요청 ;
}
/ / M BA P 헤 더 생성
private vo id Bu ild M BA P He ader (byte [ ] bu ffer , ush ort length)
{
_ tr ansa ction Id ++ ;
buffer[0] = (byte)(_transactionId
buffer[1] = (byte)(_transactionId
버 퍼 [2] = 0 x 00 ;
bu ffer [3] = 0 x 00 ;
buffer[4] = (byte)(length
buffer[5] = (byte)(length
bu ffer [6] = Sla ve Id ;
}
보내 기 및 수신 하기
private by te [ ] Send And Re ce ive (byte [ ] request)
{
if (_ stream == null )|| !_클 라이언 트 . 접속 됨)
throw new In valid O per ation Ex ception (" P LC 에 연결 되지 않 음 ")
_ stream . Write (re quest , 0, request . Leng th);
_ stream . fl ush () ;
by te [ ] bu ffer = new by te [2 60 ];
int by tes Read = _ stream . Read (bu ffer , 0, bu ffer . Leng th);
by te [ ] response = new by te [ byt es Read ];
Ar ray . Copy (bu ffer , response , by tes Read);
응답 을 반환 합니다 ;
}
public vo id Dis pose ()
{
연결 끊 기 ();
}
}
public 클래 스 ModbusException : Exception
{
public byte FunctionCode { get; }
public byte ExceptionCode { get; }
public ModbusException(byte funcCode, byte exceptionCode)
: base($"Modbus異常:function code=0x{funcCode:X2},Exception Code=0x{exceptionCode:X2}")
{
FunctionCode = funcCode;
ExceptionCode = exceptionCode;
}
}
}
2.2 台達PLC专用操作类
using System;
namespace DeltaPlcModbus
{
/// <summary>
/// 台達PLC专用操作类,封装D/M/X/Y软元件的読み書き
/// </summary>
public class DeltaPlcClient
{
private readonly ModbusTcpClient _modbus;
public DeltaPlcClient(string ip, int port = 502, byte slaveId = 1)
{
_modbus = new ModbusTcpClient(ip, port);
_modbus.SlaveId = slaveId;
}
public bool Connect() => _modbus.Connect();
public void Disconnect() => _modbus.Disconnect();
public bool IsConnected => _modbus.IsConnected;
/// <summary>
/// readDregister value
/// </summary>
public ushort ReadD(int address)
{
return _modbus.ReadHoldingRegisters((ushort)address, 1)[0];
}
/// <summary>
/// バッチ読み取りDregister
/// </summary>
public ushort[] ReadD(int startAddress, int count)
{
return _modbus.ReadHoldingRegisters((ushort)startAddress, (ushort)count);
}
/// <summary>
/// 写入Dregister value
/// </summary>
public void WriteD(int address, ushort value)
{
_modbus.WriteSingleRegister((ushort)address, value);
}
/// <summary>
/// 一括書き込みき込み入Dregister
/// </summary>
public void WriteD(int startAddress, ushort[] values)
{
_modbus.WriteMultipleRegisters((ushort)startAddress, values);
}
/// <summary>
/// read32ビット整数(占用2个Dregister)
/// </summary>
public int ReadD32(int startAddress)
{
ushort[] regs = _modbus.ReadHoldingRegisters((ushort)startAddress, 2);
return (regs[0] << 16) | regs[1];
}
/// <summary>
/// read32ビット浮動小数点数(占用2个Dregister,ABCDビッグエンディアン序)
/// </summary>
public float ReadDFloat(int startAddress)
{
ushort[] regs = _modbus.ReadHoldingRegisters((ushort)startAddress, 2);
// ABCDビッグエンディアン序:前の単語。,前の高さ。
uint bits = ((uint)regs[0] << 16) | regs[1];
byte[] bytes = BitConverter.GetBytes(bits);
// BitConverter.GetBytes戻る的是リトルエンディアン序配列,需要反转
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
return BitConverter.ToSingle(bytes, 0);
}
/// <summary>
/// read32ビット浮動小数点数(CDAB言葉の交換序)
/// </summary>
public float ReadDFloatCDAB(int startAddress)
{
ushort[] regs = _modbus.ReadHoldingRegisters((ushort)startAddress, 2);
// CDAB:前に低い言葉。,前の高さ。
uint bits = ((uint)regs[1] << 16) | regs[0];
byte[] bytes = BitConverter.GetBytes(bits);
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
return BitConverter.ToSingle(bytes, 0);
}
/// <summary>
/// readM辅助继电器ステータス
/// </summary>
public bool ReadM(int address)
{
return _modbus.ReadCoils((ushort)address, 1)[0];
}
/// <summary>
/// バッチ読み取りM辅助继电器
/// </summary>
public bool[] ReadM(int startAddress, int count)
{
return _modbus.ReadCoils((ushort)startAddress, (ushort)count);
}
/// <summary>
/// 写入M辅助继电器
/// </summary>
public void WriteM(int address, bool value)
{
_modbus.WriteSingleCoil((ushort)address, value);
}
/// <summary>
/// readX入力继电器ステータス
/// 注意:X入力在台達PLC中Modbusアドレス偏移为2048
/// </summary>
public bool ReadX(int address)
{
return _modbus.ReadDiscreteInputs((ushort)(address + 2048), 1)[0];
}
/// <summary>
/// readY出力继电器ステータス
/// 注意:Y出力在台達PLC中Modbusアドレス偏移为2048
/// </summary>
public bool ReadY(int address)
{
return _modbus.ReadCoils((ushort)(address + 2048), 1)[0];
}
/// <summary>
/// 写入Y出力继电器
/// </summary>
public void WriteY(int address, bool value)
{
_modbus.WriteSingleCoil((ushort)(address + 2048), value);
}
/// <summary>
/// 読み取りタイマータイマー。当前值(Tregister,偏移1024)
/// </summary>
public ushort ReadT(int address)
{
return _modbus.ReadHoldingRegisters((ushort)(address + 1024), 1)[0];
}
/// <summary>
/// 読み取りカウンター·カウンター当前值(Cregister,偏移1536)
/// </summary>
public ushort ReadC(int address)
{
return _modbus.ReadHoldingRegisters((ushort)(address + 1536), 1)[0];
}
}
}
三、完全使用例の例
using System;
using System.Threading;
namespace DeltaPlcModbus
{
class Program
{
static void Main(string[] args)
{
// 作成台達PLCクライアント側は
var plc = new DeltaPlcClient("192.168.1.10", 502, slaveId: 1);
try
{
// connectPLC
if (!plc.Connect())
{
Console.WriteLine("无法连接到PLC");
return;
}
Console.WriteLine("PLC接続成功です。!\n");
// 1. readDregister
Console.WriteLine("=== readDregister ===");
ushort d0 = plc.ReadD(0);
ushort d1 = plc.ReadD(1);
ushort d2 = plc.ReadD(2);
Console.WriteLine($"D0 = {d0}");
Console.WriteLine($"D1 = {d1}");
Console.WriteLine($"D2 = {d2}");
// バッチ読み取り
ushort[] dValues = plc.ReadD(0, 10);
Console.WriteLine("\nD0-D9:");
for (int i = 0; i < dValues.Length; i++)
{
Console.WriteLine($" D{i} = {dValues[i]}");
}
// 2. 写入Dregister
Console.WriteLine("\n=== 写入Dregister ===");
plc.WriteD(10, 1234);
Console.WriteLine("D10 写入 1234");
ushort check = plc.ReadD(10);
Console.WriteLine($"D10 回读 = {check}");
// 一括書き込みき込み入
plc.WriteD(20, new ushort[] { 100, 200, 300, 400, 500 });
Console.WriteLine("D20-D24 一括書き込みき込み入完成");
// 3. read32ビット·データ
Console.WriteLine("\n=== read32ビット·データ ===");
int d32 = plc.ReadD32(0);
Console.WriteLine($"D0-D1(32ビット整数)= {d32}");
// 4. readMRelay
Console.WriteLine("\n=== readMRelay ===");
bool m0 = plc.ReadM(0);
bool m1 = plc.ReadM(1);
Console.WriteLine($"M0 = {(m0 ? "ON" : "OFF")}");
Console.WriteLine($"M1 = {(m1 ? "ON" : "OFF")}");
// バッチ読み取り
bool[] mValues = plc.ReadM(0, 8);
Console.WriteLine("\nM0-M7:");
for (int i = 0; i < mValues.Length; i++)
{
Console.WriteLine($" M{i} = {(mValues[i] ? "ON" : "OFF")}");
}
// 5. 写入MRelay
Console.WriteLine("\n=== 写入MRelay ===");
plc.WriteM(0, true);
Console.WriteLine("M0 置为 ON");
Thread.Sleep(500);
plc.WriteM(0, false);
Console.WriteLine("M0 置为 OFF");
// 6. readX入力
Console.WriteLine("\n=== readX入力 ===");
bool x0 = plc.ReadX(0);
bool x1 = plc.ReadX(1);
Console.WriteLine($"X0 = {(x0 ? "ON" : "OFF")}");
Console.WriteLine($"X1 = {(x1 ? "ON" : "OFF")}");
// 7. readY出力
Console.WriteLine("\n=== readY出力 ===");
bool y0 = plc.ReadY(0);
Console.WriteLine($"Y0 = {(y0 ? "ON" : "OFF")}");
// 8. 控制Y出力
Console.WriteLine("\n=== 控制Y出力 ===");
plc.WriteY(0, true);
Console.WriteLine("Y0 置为 ON");
Thread.Sleep(1000);
plc.WriteY(0, false);
Console.WriteLine("Y0 置为 OFF");
// 9. 読み取りタイマータイマー。/カウンター·カウンター触点ステータス
Console.WriteLine("\n=== 読み取りタイマータイマー。/カウンター·カウンター触点 ===");
bool t0 = plc.ReadTContact(0);
bool c0 = plc.ReadCContact(0);
Console.WriteLine($"T0 触点 = {(t0 ? "ON" : "OFF")}");
Console.WriteLine($"C0 触点 = {(c0 ? "ON" : "OFF")}");
Console.WriteLine("注:タイマータイマー。/カウンター·カウンター当前值请查阅对应型号手册的Modbusマッピング");
// 10. 持续モニタリング(10秒)
Console.WriteLine("\n=== 持续モニタリングD0-D3(10秒)===");
for (int i = 0; i < 10; i++)
{
ushort[] monitor = plc.ReadD(0, 4);
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] D0={monitor[0]}, D1={monitor[1]}, D2={monitor[2]}, D3={monitor[3]}");
Thread.Sleep(1000);
}
}
catch (ModbusException ex)
{
Console.WriteLine($"Modbus異常:{ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"系统異常:{ex.Message}");
}
finally
{
plc.Disconnect();
Console.WriteLine("\nPLC连接已关闭");
}
Console.WriteLine("\n按任意键退出...");
Console.ReadKey();
}
}
}
四、Windows Formsトップマシン界面サンプル
using System;
using System.Windows.Forms;
using System.Threading;
namespace DeltaPlcModbus
{
public partial class MainForm : Form
{
private DeltaPlcClient _plc;
private Thread _monitorThread;
private volatile bool _monitoring = false;
public MainForm()
{
InitializeComponent();
}
private void btnConnect_Click(object sender, EventArgs e)
{
try
{
_plc = new DeltaPlcClient(txtIp.Text, int.Parse(txtPort.Text), byte.Parse(txtSlaveId.Text));
if (_plc.Connect())
{
lblStatus.Text = "Connected";
lblStatus.ForeColor = System.Drawing.Color.Green;
btnConnect.Enabled = false;
btnDisconnect.Enabled = true;
_monitoring = true;
_monitorThread = new Thread(MonitorLoop);
_monitorThread.IsBackground = true;
_monitorThread.Start();
}
else
{
MessageBox.Show("接続の失敗!");
}
}
catch (Exception ex)
{
MessageBox.Show($"连接異常:{ex.Message}");
}
}
private void btnDisconnect_Click(object sender, EventArgs e)
{
_monitoring = false;
_plc?.Disconnect();
lblStatus.Text = "Not connected";
lblStatus.ForeColor = System.Drawing.Color.Red;
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
}
private void MonitorLoop()
{
while (_monitoring && _plc.IsConnected)
{
try
{
// readD0-D9
ushort[] values = _plc.ReadD(0, 10);
// readM0-M7
bool[] mValues = _plc.ReadM(0, 8);
// 更新UI(跨线程调用)
this.Invoke(new Action(() =>
{
txtD0.Text = values[0].ToString();
txtD1.Text = values[1].ToString();
txtD2.Text = values[2].ToString();
txtD3.Text = values[3].ToString();
lblM0.Text = mValues[0] ? "ON" : "OFF";
lblM1.Text = mValues[1] ? "ON" : "OFF";
lblM2.Text = mValues[2] ? "ON" : "OFF";
lblM3.Text = mValues[3] ? "ON" : "OFF";
}));
}
catch
{
// Read failed,忽略
}
Thread.Sleep(500);
}
}
private void btnWriteD_Click(object sender, EventArgs e)
{
try
{
int addr = int.Parse(txtWriteAddr.Text);
ushort value = ushort.Parse(txtWriteValue.Text);
_plc.WriteD(addr, value);
MessageBox.Show($"D{addr} 写入 {value} 成功");
}
catch (Exception ex)
{
MessageBox.Show($"写入失敗:{ex.Message}");
}
}
private void btnM0On_Click(object sender, EventArgs e)
{
_plc.WriteM(0, true);
}
private void btnM0Off_Click(object sender, EventArgs e)
{
_plc.WriteM(0, false);
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
_monitoring = false;
_plc?.Disconnect();
base.OnFormClosing(e);
}
}
}
五、よくある質問与トラブルシューティング
5.1 接続の失敗
- チェックIPaddress:ConfirmPLC的IPアドレス設定正确,与电脑在同一网段
- チェック矶钓:台達PLC Modbus TCPデフォルト矶钓502,確認未被ファイアウォール拦截
- チェックネットワークケーブル:確認ネットワークケーブル连接正常,PLC网口指示灯亮
- チェックPLC設定:在ISPSoft中確認PLC已有効Modbus TCP通讯
- Ping测试:在命令行中ping PLC的IPaddress,確認网络连通
5.2 読み取りデータ为0或異常
- アドレスエラー:ConfirmModbusアドレス与PLC软元件アドレス的对应关系,注意オフセットの量
- 駅からの住所エラー:ConfirmSlaveId与PLC中に設置。的一致
- 機能コードサポートなし。:部分台達PLC型号可能サポートなし。某些機能コード
- アドレス越界:確認読み取り的アドレス範囲在PLCサポート的範囲内
- PLC未运行:ConfirmPLC处于RUNモード,STOPモード下部分アドレス不可读
5.3 写入不生效
- レジスタ被程序カバー:PLC程序中可能正在写入该アドレス,导致写入被カバー
- アドレス読み取り専用:部分レジスタ(如X入力)是読み取り専用的,書けない。入
- 权限限制:部分PLC需要設定通讯写入权限
- データ型エラー:確認写入的データ型与PLC中定义的一致
六、ベストプラクティス
- バッチ読み取り:尽量一次読み取り多个连续レジスタ,减少通讯次数,提高效率
- 合理ポーリング間隔:建议ポーリング間隔不未来100ms,避免过快ポーリング导致PLC通讯过载
- 断线再接続:追加自動再接続。机制,网络恢复后自动恢复通讯
- タイムアウトの設定:合理設定通信タイムアウト,建议2-3秒
- 異常処理:捕获ModbusException,区分不同異常コード。进行処理
- 线程セキュリティ:多线程アクセス使用するときlock保护,避免并发冲突
- ジャーナル·レコード:记录通讯日志,便于トラブルシューティング問題
- アドレス规划:提前规划D/Mレジスタ的使用,避免アドレス冲突
本記事提供的C#代码実装了台達PLC完全に。Modbus TCP通讯功能,包括Dregister、MRelay、X入力、Y出力、タイマータイマー。、カウンター·カウンター的读書き込み操作。,以及Windows Formsトップマシン界面サンプル。代码可直接使用する。于工业自動化项目开发,建议先使用台達ISPSoft软件モニタリングPLCdata,確認通信は正常后再集成到实际项目中。
Leave a Reply