MainForm类代码大概实现如下功能:
public partial class MainForm : WinFormBase
{
#region 对象
ModbusRtuMaster modbusMaster;
States connectState = States.Stop;
Timer connectStateTimer = new Timer();
Timer resultShowTimer = new Timer();
#endregion
#region 初始化窗体、加载窗体、初始化配置参数
public MainForm()
{
InitializeComponent();
this.CenterToParent();
}
private void MainForm_Load(object sender, EventArgs e)
{
Initialize();
}
private void Initialize()
{
//初始化Modbus
modbusMaster = new ModbusRtuMaster();
//初始化参数
cbx_SerialPort.DataSource = modbusMaster.GetSerialPortArray();
cbx_BaudRate.DataSource = modbusMaster.GetBaudRateArray();
cbx_Parity.DataSource = modbusMaster.GetParityArray();
cbx_DataBits.DataSource = modbusMaster.GetDataBitArray();
cbx_StopBits.DataSource = modbusMaster.GetStopBitArray();
cbx_FuncCode.SelectedIndex = 0;
//初始化定时器
connectStateTimer = new Timer();
connectStateTimer.Interval = 500;
connectStateTimer.Tick += ConnectStateTimer_Tick;
resultShowTimer = new Timer();
resultShowTimer.Interval = 500;
connectStateTimer.Tick += ResultShowTimer_Tick;
//初始化表格
dataGridView.ReadOnly = true;
dataGridView.Columns[0].Width = 100;
dataGridView.Columns[1].Width = 100;
dataGridView.Columns[0].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
dataGridView.Columns[1].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
dataGridView.RowHeadersVisible = false;
//初始化表格数据
for (int i = 0; i < 10; i++)
{
dataGridView.Rows.Add(new object[] { i, 0 });
}
//初始化
rtbx_Message.ForeColor = Color.Gray;
btn_WriteData.Enabled = false;
rsc_ConnectState.State = States.Stop;
}
#endregion
#region 连接、读取写入
private void btn_OpenRTU_Click(object sender, EventArgs e)
{
Open();
}
private void btn_WriteData_Click(object sender, EventArgs e)
{
WriteData();
}
private void Open()
{
if (btn_OpenRTU.Text.Equals("连接"))
{
MessageUpdate("正在创建ModbusRTU连接...", Color.Green);
btn_OpenRTU.Enabled = false;
if (modbusMaster.IsConnected) modbusMaster.DisConnect();
if (!modbusMaster.Connect(modbusMaster.protModel.PortName))
{
modbusMaster.DisConnect();
Debug.WriteLine("无法与ModbusRTU从站建立连接");
MessageUpdate("无法与ModbusRTU从站建立连接", Color.Red);
btn_OpenRTU.Enabled = true;
return;
}
try
{
MessageUpdate("创建ModbusRTU成功...", Color.Green);
connectState = States.Running;
Task<bool[]> registers = modbusMaster.ReadCoilsAsync(modbusMaster.SlaveId, 0, 1);
if (registers.Result != null)
{
Debug.WriteLine($"尝试读取值: value = {registers.Result[0]}");
MessageUpdate($"尝试读取值: value = {registers.Result[0]}", Color.Green);
}
ControlEnableUpdate();
connectStateTimer.Start();
resultShowTimer.Start();
btn_OpenRTU.Enabled = true;
}
catch (Exception ex)
{
Debug.WriteLine($"触发异常:{ex.Message}");
MessageUpdate($"尝试读取值失败,连接失败...异常原因:{ex.Message}", Color.Red);
modbusMaster.DisConnect();
connectState = States.Stop;
connectStateTimer.Stop();
resultShowTimer.Stop();
ControlEnableUpdate();
btn_OpenRTU.Enabled = true;
}
}
else
{
MessageUpdate("断开ModbusRTU通讯连接...", Color.Red);
modbusMaster.DisConnect();
connectState = States.Stop;
connectStateTimer.Stop();
resultShowTimer.Stop();
rsc_ConnectState.State = connectState;
ControlEnableUpdate();
btn_OpenRTU.Enabled = true;
}
}
/// <summary>
/// 写数据
/// </summary>
private void WriteData()
{
ushort startAddress = (ushort)nudx_WriteStartAddress.Value;
try
{
switch (modbusMaster.FuncCode)
{
case "05":
modbusMaster.WriteSingleCoilAsync(modbusMaster.SlaveId, startAddress, bool.Parse(tbx_WriteData.Text));
UpdateDataShow($"从站={modbusMaster.SlaveId},功能码 = {modbusMaster.FuncCode}," +
$"起始地址 = {modbusMaster.StartAddress},写入值={tbx_WriteData.Text}");
break;
case "06":
modbusMaster.WriteSingleRegisterAsync(modbusMaster.SlaveId, startAddress, ushort.Parse(tbx_WriteData.Text));
UpdateDataShow($"从站={modbusMaster.SlaveId},功能码 = {modbusMaster.FuncCode}," +
$"起始地址 = {modbusMaster.StartAddress},写入值={tbx_WriteData.Text}");
break;
case "15":
bool[] dataBool = ParseArray<bool>(tbx_WriteData.Text);
modbusMaster.WriteMultipleCoilsAsync(modbusMaster.SlaveId, startAddress, dataBool);
UpdateDataShow($"从站={modbusMaster.SlaveId},功能码 = {modbusMaster.FuncCode}," +
$"起始地址 = {modbusMaster.StartAddress},写入值={tbx_WriteData.Text}");
break;
case "16":
ushort[] dataUshort = ParseArray< ushort > (tbx_WriteData.Text);
modbusMaster.WriteMultipleRegistersAsync(modbusMaster.SlaveId, startAddress, dataUshort);
UpdateDataShow($"从站={modbusMaster.SlaveId},功能码 = {modbusMaster.FuncCode}," +
$"起始地址 = {modbusMaster.StartAddress},写入值={tbx_WriteData.Text}");
break;
default:
MessageUpdate($"功能码 = {modbusMaster.FuncCode},不匹配...",Color.Red);
break;
}
}
catch (Exception ex)
{
UpdateDataShow($"写入异常,{ex.Message}");
}
}
/// <summary>
/// 字符串转换数组
/// </summary>
private T[] ParseArray<T>(string input)
{
string[] items = input.Trim('[', ']').Split(',');
return items.Select(item => (T)Convert.ChangeType(item.Trim(), typeof(T))).ToArray();
}
#endregion
#region 定时器
/// <summary>
/// 连接状态定时器
/// </summary>
private void ConnectStateTimer_Tick(object sender, EventArgs e)
{
if (modbusMaster.IsConnected)
{
rsc_ConnectState.Invoke(new Action(() => {
if (connectState == States.Running)
{
rsc_ConnectState.State = (rsc_ConnectState.State == States.None ? States.Running : States.None);
Task.Delay(500);
}
}));
}
}
/// <summary>
/// 地址结果定时器
/// </summary>
private void ResultShowTimer_Tick(object sender, EventArgs e)
{
if (!modbusMaster.IsConnected || !checkBx_LoopRead.Checked) return;
try
{
dataGridView.Invoke(new Action(() =>
{
Task<bool[]> result;
Task<ushort[]> registers;
switch (modbusMaster.FuncCode)
{
case "01":
result = modbusMaster.ReadCoilsAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
UpdateDataShow<bool>(result?.Result);
break;
case "02":
result = modbusMaster.ReadInputsAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
UpdateDataShow<bool>(result?.Result);
break;
case "03":
registers = modbusMaster.ReadHoldingRegistersAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
UpdateDataShow<ushort>(registers?.Result);
break;
case "04":
registers = modbusMaster.ReadInputRegistersAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
UpdateDataShow<ushort>(registers?.Result);
break;
case "05":
result = modbusMaster.ReadCoilsAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
UpdateDataShow<bool>(result?.Result);
break;
case "06":
registers = modbusMaster.ReadHoldingRegistersAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
UpdateDataShow<ushort>(registers.Result);
break;
case "15":
result = modbusMaster.ReadCoilsAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
UpdateDataShow<bool>(result?.Result);
break;
case "16":
registers = modbusMaster.ReadHoldingRegistersAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
UpdateDataShow<ushort>(registers?.Result);
break;
default:
result = modbusMaster.ReadCoilsAsync(modbusMaster.SlaveId, modbusMaster.StartAddress, (ushort)modbusMaster.DataLength);
UpdateDataShow<bool>(result?.Result);
break;
}
}));
}
catch (Exception ex)
{
MessageUpdate($"{ex.Message}",Color.Red);
}
}
/// <summary>
/// 数据更新显示
/// </summary>
private void UpdateDataShow<T>(T[] array,string appendText = null)
{
for (int i = 0; i < modbusMaster.DataLength && i< 10; i++)
{
dataGridView.Rows[i].Cells[0].Value = (modbusMaster.StartAddress + i);
dataGridView.Rows[i].Cells[1].Value = array[i];
}
if (!checkBx_LoopShow.Checked) return;
MessageUpdate($"[{modbusMaster.StartAddress}][{ArrayToString<T>(array)}]", Color.Blue, "# 接收 ASCII>");
}
private void UpdateDataShow<T>(T result, string appendText = null)
{
MessageUpdate($"[{modbusMaster.StartAddress}][{result}]", Color.Green, "# 发送 ASCII>");
}
/// <summary>
/// 数组转字符串
/// </summary>
private string ArrayToString<T>(T[] values, string sep = " ")
{
return string.Join(sep, values.Select(r => Convert.ToString(r)).ToArray());
}
#endregion
#region 控件使能更新
/// <summary>
/// 控件使能更新
/// </summary>
private void ControlEnableUpdate()
{
ControlEnabled(cbx_SerialPort, !modbusMaster.IsConnected);
ControlEnabled(cbx_BaudRate, !modbusMaster.IsConnected);
ControlEnabled(cbx_Parity, !modbusMaster.IsConnected);
ControlEnabled(cbx_DataBits, !modbusMaster.IsConnected);
ControlEnabled(cbx_StopBits, !modbusMaster.IsConnected);
ControlEnabled(nudx_SlaveId, !modbusMaster.IsConnected);
btn_OpenRTU.Invoke(new Action(() => {
btn_OpenRTU.Text = modbusMaster.IsConnected ? "关闭" : "连接";
}));
}
/// <summary>
/// 控件使能启用
/// </summary>
public void ControlEnabled(Control control,bool flag)
{
control.Invoke(new Action(() =>
{
control.Enabled = flag;
}));
}
#endregion
#region 参数变更
private void cbx_SerialPort_SelectedIndexChanged(object sender, EventArgs e)
{
if (cbx_SerialPort == null || cbx_SerialPort.SelectedItem == null) return;
modbusMaster.protModel.PortName = cbx_SerialPort.SelectedItem.ToString();
}
private void cbx_BaudRate_SelectedIndexChanged(object sender, EventArgs e)
{
if (cbx_BaudRate == null || cbx_BaudRate.SelectedItem == null) return;
if (int.TryParse(cbx_BaudRate.SelectedItem.ToString(),out int result))
{
modbusMaster.protModel.BaudRate = result;
}
else
{
cbx_BaudRate.SelectedItem = modbusMaster.protModel.BaudRate.ToString();
}
}
private void cbx_Parity_SelectedIndexChanged(object sender, EventArgs e)
{
if (Enum.TryParse(cbx_Parity.SelectedItem.ToString(), out Parity result))
{
modbusMaster.protModel.Parity = result;
}
else
{
cbx_Parity.SelectedItem = modbusMaster.protModel.Parity.ToString();
}
}
private void cbx_DataBits_SelectedIndexChanged(object sender, EventArgs e)
{
if (int.TryParse(cbx_DataBits.SelectedItem.ToString(), out int result))
{
modbusMaster.protModel.DataBits = result;
}
else
{
cbx_DataBits.SelectedItem = modbusMaster.protModel.DataBits.ToString();
}
}
private void cbx_StopBits_SelectedIndexChanged(object sender, EventArgs e)
{
if (Enum.TryParse(cbx_StopBits.SelectedItem.ToString(), out StopBits result))
{
modbusMaster.protModel.StopBits = result;
}
else
{
cbx_StopBits.SelectedItem = modbusMaster.protModel.StopBits.ToString();
}
}
private void cbx_FuncCode_SelectedIndexChanged(object sender, EventArgs e)
{
if (cbx_FuncCode==null)return;
modbusMaster.FuncCode = cbx_FuncCode.SelectedItem.ToString().Split('_')[0];
byte funcCode = byte.Parse(modbusMaster.FuncCode);
if (modbusMaster.IsConnected && funcCode== 5 || funcCode == 6 || funcCode == 15 || funcCode == 16)
{
btn_WriteData.Enabled = true;
}
else
{
btn_WriteData.Enabled = false;
}
}
private void nudx_StartAddress_ValueChanged(object sender, EventArgs e)
{
if (nudx_StartAddress.Value < ushort.MaxValue)
{
modbusMaster.StartAddress = (ushort)nudx_StartAddress.Value;
}
else
{
nudx_StartAddress.Value = modbusMaster.StartAddress;
}
}
private void nudx_DataLength_ValueChanged(object sender, EventArgs e)
{
if (ushort.Parse(nudx_DataLength.Value.ToString()) < ushort.MaxValue)
{
modbusMaster.DataLength = (ushort)nudx_DataLength.Value;
}
else
{
nudx_DataLength.Value = modbusMaster.DataLength;
}
}
private void nudx_SlaveId_ValueChanged(object sender, EventArgs e)
{
if (byte.Parse(nudx_SlaveId.Value.ToString()) < byte.MaxValue)
{
modbusMaster.SlaveId = (byte)nudx_SlaveId.Value;
}
else
{
nudx_SlaveId.Value = modbusMaster.SlaveId;
}
}
private void nudx_WriteStartAddress_ValueChanged(object sender, EventArgs e)
{
if (ushort.Parse(nudx_WriteStartAddress.Value.ToString()) < ushort.MaxValue)
{
modbusMaster.WriteStartAddress = (ushort)nudx_WriteStartAddress.Value;
}
else
{
nudx_WriteStartAddress.Value = modbusMaster.WriteStartAddress;
}
}
private void nudx_WriteDataLength_ValueChanged(object sender, EventArgs e)
{
if (ushort.Parse(nudx_WriteDataLength.Value.ToString()) < ushort.MaxValue)
{
modbusMaster.WriteDataLength = (ushort)nudx_WriteDataLength.Value;
}
else
{
nudx_WriteDataLength.Value = modbusMaster.WriteDataLength;
}
}
#endregion
#region 操作消息更新
/// <summary>
/// 操作消息更新
/// </summary>
/// <param name="data"></param>
/// <param name="color"></param>
/// <param name="appendText"></param>
/// <param name="maxLineNum"></param>
/// <param name="isAppendTime"></param>
private void MessageUpdate(string data, Color color, string appendText = null, int maxLineNum = 1000, bool isAppendTime = true)
{
// 空数据检查
if (string.IsNullOrEmpty(data)) return;
// 线程安全调用
if (rtbx_Message.InvokeRequired)
{
rtbx_Message.BeginInvoke(new Action(() =>MessageUpdate(data, color, appendText, maxLineNum, isAppendTime)));
return;
}
lock (rtbx_Message)
{
rtbx_Message.SuspendLayout(); // 暂停重绘提高性能
try
{
if (rtbx_Message.Lines.Length > maxLineNum)
{
rtbx_Message.Clear();
}
if (isAppendTime)
{
rtbx_Message.AppendText($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}]:");
}
if (!string.IsNullOrEmpty(appendText))
{
rtbx_Message.AppendText($"{appendText}{Environment.NewLine}");
}
else
{
rtbx_Message.AppendText($"{Environment.NewLine}");
}
int startIndex = rtbx_Message.TextLength;
rtbx_Message.ScrollToCaret();
rtbx_Message.SelectionStart = rtbx_Message.TextLength;
rtbx_Message.AppendText($"{data}{Environment.NewLine}");
SetTextColor(rtbx_Message, startIndex, data.Length, color);
}
finally
{
rtbx_Message.ResumeLayout(); // 恢复重绘
}
}
}
/// <summary>
/// 设置文本框指定范围内的文本颜色
/// </summary>
private void SetTextColor(RichTextBox rtb, int startIndex, int length, Color color)
{
rtb.Invoke(new Action(() => {
// 保存当前选择状态
int originalStart = rtb.SelectionStart;
int originalLength = rtb.SelectionLength;
// 设置新选择范围
rtb.Select(startIndex, length);
// 更改选中文本的颜色
rtb.SelectionColor = color;
// 恢复原始选择状态
rtb.Select(originalStart, originalLength);
}));
}
#endregion
}
Mobus主站类实现如下功能:
public class ModbusRtuMaster
{
private SerialPort _serialPort;
private IModbusSerialMaster _master;
public SerialPortModel protModel;
public byte SlaveId { get; set; } = 1;
public bool IsConnected { get; private set; } = false;
public string FuncCode { get; internal set; } = "01";
public ushort StartAddress { get; set; } = 0;
public ushort DataLength { get; set; } = 1;
public ushort WriteStartAddress { get; set; } = 0;
public ushort WriteDataLength { get; set; } = 1;
public ModbusRtuMaster()
{
protModel = new SerialPortModel();
_serialPort = new SerialPort();
}
/// <summary>
/// 波特率、数据位、停止位、校验位
/// </summary>
/// <param name="portName"></param>
/// <param name="baudRate">波特率</param>
/// <param name="parity">校验位</param>
/// <param name="dataBits">数据位</param>
/// <param name="stopBits">停止位</param>
/// <returns>连接</returns>
public bool Connect(string portName,
int baudRate = 9600, int dataBits = 8,
Parity parity = Parity.None, StopBits stopBits = StopBits.One)
{
try
{
if (_serialPort == null)
_serialPort = new SerialPort();
_serialPort.PortName = portName;
_serialPort.BaudRate = baudRate;
_serialPort.DataBits = dataBits;
_serialPort.Parity = parity;
_serialPort.StopBits = stopBits;
SetTimeout();
_serialPort.Open();
_master = ModbusSerialMaster.CreateRtu(_serialPort);
Debug.WriteLine($"初始化成功!");
IsConnected = true;
return true;
}
catch (Exception ex)
{
Debug.WriteLine($"初始化失败: {ex.Message}");
IsConnected = false;
return false;
}
}
public bool Connect(SerialPortModel portModel)
{
return Connect(portModel.PortName,
portModel.BaudRate, portModel.DataBits,
portModel.Parity, portModel.StopBits);
}
public void DisConnect()
{
_master?.Dispose();
_serialPort?.Close();
_serialPort.Dispose();
IsConnected =false;
}
/// <summary>
/// 超时设置
/// </summary>
public void SetTimeout(int readTimeout = 2000, int writeTimeout = 2000)
{
_serialPort.ReadTimeout = readTimeout;
_serialPort.WriteTimeout = writeTimeout;
}
#region 读取
/// <summary>
/// 读取线圈状态 (功能码01)
/// </summary>
public Task<bool[]> ReadCoilsAsync(byte slaveId, ushort startAddress, ushort numberOfPoints)
{
try
{
Task<bool[]> result = _master.ReadCoilsAsync(slaveId, startAddress, numberOfPoints);
return result;
}
catch (Exception ex)
{
Debug.WriteLine($"读取线圈失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 读取输入状态 (功能码02)
/// </summary>
public Task<bool[]> ReadInputsAsync(byte slaveId, ushort startAddress, ushort numberOfPoints)
{
try
{
return _master.ReadInputsAsync(slaveId, startAddress, numberOfPoints);
}
catch (Exception ex)
{
Debug.WriteLine($"读取输入状态失败: {ex.Message}");
return null;
}
}
/// <summary>
/// 读取保持寄存器 (功能码03)
/// </summary>
public Task<ushort[]> ReadHoldingRegistersAsync(byte slaveId, ushort startAddress, ushort numberOfPoints)
{
try
{
return _master.ReadHoldingRegistersAsync(slaveId, startAddress, numberOfPoints);
}
catch (Exception ex)
{
Debug.WriteLine($"读取保持寄存器失败: {ex.Message}");
throw new Exception($"读取保持寄存器失败: {ex.Message}");
}
}
/// <summary>
/// 读取输入寄存器 (功能码04)
/// </summary>
public Task<ushort[]> ReadInputRegistersAsync(byte slaveId, ushort startAddress, ushort numberOfPoints)
{
try
{
return _master.ReadInputRegistersAsync(slaveId, startAddress, numberOfPoints);
}
catch (Exception ex)
{
Debug.WriteLine($"读取输入寄存器失败: {ex.Message}");
return null;
}
}
#endregion
#region 写入
/// <summary>
/// 写入单个线圈 (功能码05)
/// </summary>
public bool WriteSingleCoilAsync(byte slaveId, ushort coilAddress, bool value)
{
try
{
_master.WriteSingleCoilAsync(slaveId, coilAddress, value);
return true;
}
catch (Exception ex)
{
Debug.WriteLine($"写入单个线圈失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 写入单个寄存器 (功能码06)
/// </summary>
public bool WriteSingleRegisterAsync(byte slaveId, ushort registerAddress, ushort value)
{
try
{
_master.WriteSingleRegisterAsync(slaveId, registerAddress, value);
return true;
}
catch (Exception ex)
{
Debug.WriteLine($"写入单个寄存器失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 写入多个线圈 (功能码15)
/// </summary>
public bool WriteMultipleCoilsAsync(byte slaveId, ushort startAddress, bool[] data)
{
try
{
_master.WriteMultipleCoilsAsync(slaveId, startAddress, data);
return true;
}
catch (Exception ex)
{
Debug.WriteLine($"写入多个线圈失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 写入多个寄存器(功能码16)
/// </summary>
public bool WriteMultipleRegistersAsync(byte slaveId, ushort startAddress, ushort[] data)
{
try
{
_master.WriteMultipleRegistersAsync(slaveId, startAddress, data);
return true;
}
catch (Exception ex)
{
Debug.WriteLine($"写入多个寄存器失败: {ex.Message}");
return false;
}
}
#endregion
#region 获取串口参数
//波特率数组
public int[] _BaudRateArray = { 9600, 14400, 19200, 38400, 57600, 115200 };
//数据位数组
public int[] _DataBitArray = { 8, 7, 6, 5 };
public string[] GetSerialPortArray()
{
return SerialPort.GetPortNames();
}
public Array GetBaudRateArray()
{
return _BaudRateArray;
}
public Array GetDataBitArray()
{
return _DataBitArray;
}
public string[] GetParityArray()
{
return Enum.GetNames(typeof(Parity));
}
public string[] GetStopBitArray()
{
return Enum.GetNames(typeof(StopBits)).Skip(1).ToArray();
}
#endregion
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有