概要
Modbus是一种串行通信协议,是Modicon公司(现在的施耐德电气Schneider Electric)于1979年为使用可编程逻辑控制器(PLC)通信而发表。Modbus已经成为工业领域通信协议的业界标准(De facto),并且现在是工业电子设备之间常用的连接方式。
Modbus允许多个 (大约240个) 设备连接在同一个网络上进行通信,举个例子,一个测量温度和湿度的装置,并且将结果发送给计算机。在数据采集与监视控制系统(SCADA)中,Modbus通常用来连接监控计算机和远程终端控制系统(RTU)。
整体架构流程
示例代码
加载界面
private void InitUI()
{
dataGridView1.Rows.Clear();
List<TcpModbusSlave> nodeList = ModbusEventHandler.GetTcpNodeList();
foreach (TcpModbusSlave node in nodeList)
{
int index = this.dataGridView1.Rows.Add();
this.dataGridView1.Rows[index].Cells[0].Value = node.tcpModbusInfo.DeviceId;
this.dataGridView1.Rows[index].Cells[1].Value = node.tcpModbusInfo.Name;
this.dataGridView1.Rows[index].Cells[2].Value = node.tcpModbusInfo.Port;
this.dataGridView1.Rows[index].Cells[3].Value = node.tcpModbusInfo.Type;
this.dataGridView1.Rows[index].Cells[4].Value = node.tcpModbusInfo.Comment;
this.dataGridView1.Rows[index].Cells[5].Value = node.tcpModbusInfo.Status;
if (nodeList[index].devStateType == StartupEnum.Start)
{
this.dataGridView1.Rows[index].Cells[5].Style.ForeColor = Color.Green;
}
else {
this.dataGridView1.Rows[index].Cells[5].Style.BackColor = Color.White;
}
this.dataGridView1.Rows[index].Cells[5].Style.Alignment = DataGridViewContentAlignment.MiddleCenter;
this.dataGridView1.Columns[5].SortMode = DataGridViewColumnSortMode.NotSortable;
modbusSlave = nodeList[nodeList.Count - 1];
}
}
启动服务
public ModbusSlave StartTcpSlave()
{
if (devStateType != StartupEnum.Start)
{
if (!IsPortUsed(tcpModbusInfo.Port))
{
try
{
tcpListener = new TcpListener(IPAddress.Parse(tcpModbusInfo.IpAddress), tcpModbusInfo.Port);
modbusSlave = ModbusTcpSlave.CreateTcp(tcpModbusInfo.SlaveId, tcpListener);
modbusSlave.DataStore = modbusData;
devThread = new Thread(modbusSlave.Listen)
{
Name = tcpModbusInfo.Name.ToString()
};
devThread.Start();
tcpModbusInfo.Status = "运行中";
devStateType = StartupEnum.Start;
return modbusSlave;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "出错了", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
}
MessageBox.Show("TCP端口:[" + tcpModbusInfo.Port.ToString() + "]" + "被占用", "出错了", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
return null;
}
监听
private void Timer_Tick(object sender, EventArgs e)
{
RefreshUIData();
}
private void frmRtuRegister_Load(object sender, EventArgs e)
{
dataGridView1.Rows.Clear();
for (int i = 0; i < Convert.ToInt32(txtReadNum.Text); i++)
{
dataGridView1.Rows.Add();
}
textBox1.Text = devName;
cmbRegister.SelectedIndex = 2;
RefreshUIData();
}
private void RefreshUIData()
{
if (txtStart.Text == "" || Convert.ToInt32(txtStart.Text) <= 0)
{
txtStart.Text = "1";
}
if (txtReadNum.Text == "" || Convert.ToInt32(txtReadNum.Text) <= 0)
{
txtStart.Text = "10";
}
switch (cmbRegister.SelectedIndex)
{
case 0:
CoilOrInput(deviceInfo.getDataStore().CoilDiscretes);
break;
case 1:
CoilOrInput(deviceInfo.getDataStore().InputDiscretes);
break;
case 2:
HoldingOrInput(deviceInfo.getDataStore().HoldingRegisters);
break;
case 3:
HoldingOrInput(deviceInfo.getDataStore().InputRegisters);
break;
default:
HoldingOrInput(deviceInfo.getDataStore().HoldingRegisters);
break;
}
}
public void CoilOrInput(ModbusDataCollection<bool> data)
{
((ISupportInitialize)(dataGridView1)).BeginInit();
for (int i = 0; i < Convert.ToInt32(txtReadNum.Text); i++)
{
this.dataGridView1.Rows[i].Cells[0].Value = data[Convert.ToInt32(txtStart.Text) + i];
}
((ISupportInitialize)(dataGridView1)).EndInit();
}
public void HoldingOrInput(ModbusDataCollection<ushort> data)
{
((ISupportInitialize)(dataGridView1)).BeginInit();
for (int i = 0; i < Convert.ToInt32(txtReadNum.Text); i++)
{
this.dataGridView1.Rows[i].Cells[0].Value = data[Convert.ToInt32(txtStart.Text) + i];
}
((ISupportInitialize)(dataGridView1)).EndInit();
}
使用
using Modbus.Device;
using ModbusController.Models.Entitys;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ModbusController.Models
{
public class ModbusEventHandler
{
public static List<TcpModbusSlave> tcpModbusSlaves = new List<TcpModbusSlave>();
public static List<RtuModbusSlave> rtuModbusSlaves = new List<RtuModbusSlave>();
private static bool initFlag = false;
public static bool TcpModbusDataInit(string cfgFile)
{
if (initFlag)
{
StopTcpAll();
tcpModbusSlaves.Clear();
}
DataTable dt;
bool ret = CsvFileReader.ReadCSV(cfgFile, out dt);
if (!ret)
return false;
try
{
for (int i = 0; i < dt.Rows.Count; i++)
{
TcpModbusInfo deviceInfo = new TcpModbusInfo();
deviceInfo.SlaveId = Convert.ToByte(dt.Rows[i][0].ToString());
deviceInfo.DeviceId= int.Parse(dt.Rows[i][0].ToString()).ToString();
deviceInfo.Name= dt.Rows[i][1].ToString();
deviceInfo.Port= int.Parse(dt.Rows[i][2].ToString());
deviceInfo.Type= dt.Rows[i][3].ToString();
deviceInfo.Comment = dt.Rows[i][4].ToString();
TcpModbusSlave node = new TcpModbusSlave(deviceInfo);
tcpModbusSlaves.Add(node);
}
initFlag = true;
return true;
}
catch (Exception)
{
return false;
}
}
public static bool RtuModbusDataInit(string cfgFile)
{
if (initFlag)
{
StopRtuAll();
tcpModbusSlaves.Clear();
}
//读csv文件,初始化每个node
DataTable dt;
bool ret = CsvFileReader.ReadCSV(cfgFile, out dt);
if (!ret)
return false;
try
{
for (int i = 0; i < dt.Rows.Count; i++) //写入各行数据
{
string portName = dt.Rows[i][0].ToString();
int baudRate = int.Parse(dt.Rows[i][1].ToString());
Parity parity= Parity.None;
switch (int.Parse(dt.Rows[i][2].ToString()))
{
case 1:
parity = Parity.Odd;
break;
case 2:
parity = Parity.Even;
break;
case 0:
parity = Parity.None;
break;
default:
parity = Parity.None;
break;
}
int dataBits = int.Parse(dt.Rows[i][3].ToString());
StopBits stopBits = StopBits.One;
switch (int.Parse(dt.Rows[i][4].ToString()))
{
case 1:
stopBits = StopBits.One;
break;
case 2:
stopBits = StopBits.Two;
break;
default:
stopBits = StopBits.One;
break;
}
RtuModbusSlave node = new RtuModbusSlave(portName, baudRate, parity, dataBits, stopBits);
rtuModbusSlaves.Add(node);
}
initFlag = true;//已初始化
return true;
}
catch (Exception)
{
return false;
}
}
public static List<TcpModbusSlave> GetTcpNodeList()
{
return tcpModbusSlaves;
}
public static List<RtuModbusSlave> GetRtuNodeList()
{
return rtuModbusSlaves;
}
public static void StartTcp(TcpModbusSlave tcpModbus)
{
tcpModbus.StartTcpSlave();
}
public static void StartTcpAll(EventHandler<ModbusSlaveRequestEventArgs> ModbusSlaveRequestReceived = null)
{
foreach (TcpModbusSlave node in tcpModbusSlaves)
{
ModbusSlave modbusSlave = node.StartTcpSlave();
if (ModbusSlaveRequestReceived != null)
{
if (modbusSlave != null)
{
modbusSlave.ModbusSlaveRequestReceived += ModbusSlaveRequestReceived;
}
}
}
}
public static void StopTcp(TcpModbusSlave tcpModbus)
{
tcpModbus.StopTcpSlave();
}
public static void StopTcpAll()
{
foreach (TcpModbusSlave node in tcpModbusSlaves)
{
node.StopTcpSlave();
}
}
public static void StartRtuAll(EventHandler<ModbusSlaveRequestEventArgs> ModbusSlaveRequestReceived = null)
{
foreach (RtuModbusSlave node in rtuModbusSlaves)
{
Modbus.Device.ModbusSlave modbusSlave = node.StartRtuSlave();
if (ModbusSlaveRequestReceived != null)
{
modbusSlave.ModbusSlaveRequestReceived += ModbusSlaveRequestReceived;
}
}
}
public static void StopRtuAll()
{
foreach (RtuModbusSlave node in rtuModbusSlaves)
{
node.StopRtuSlave();
}
}
//写入数据
public static void SendTcpStringValue(TcpModbusSlave modbusSlave, int groupindex, int address, ushort strvalue)
{
modbusSlave.SetStringValue(groupindex, address, strvalue);
}
}
}
小结
源码链接:
链接:https://pan.baidu.com/s/1xsBhJ3uDHTEuLddl4jcgzQ
提取码:qr91