using IoTClient.Clients.ModBus;
using IoTClient.Common.Helpers;
using IoTServer.Common;
using IoTServer.Servers.ModBus;
using System;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace IoTClient.Tool.Controls
{
public partial class ModBusRtuControl : UserControl
{
private ModBusRtuClient client;
private ModBusRtuServer server;
int[] BaudRateList = new int[] { 9600, 4800, 2400, 1200, 600, 14400, 300, 19200, 110, 38400, 56000, 57600, 115200, 128000, 256000 };
int[] DataBitList = new int[] { 8, 7, 6 };
StopBits[] StopBitsList = new StopBits[] { StopBits.One, StopBits.Two, StopBits.OnePointFive };
Parity[] ParityList = new Parity[] { Parity.None, Parity.Odd, Parity.Even };
public ModBusRtuControl()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
Size = new Size(880, 450);
groupBox2.Location = new Point(13, 5);
groupBox2.Size = new Size(855, 50);
groupBox1.Location = new Point(13, 55);
groupBox1.Size = new Size(855, 50);
groupBox3.Location = new Point(13, 105);
groupBox3.Size = new Size(855, 50);
txt_content.Location = new Point(13, 160);
lab_address.Location = new Point(9, 22);
txt_address.Location = new Point(39, 18);
txt_address.Size = new Size(88, 21);
but_read.Location = new Point(132, 17);
lab_value.Location = new Point(227, 22);
txt_value.Location = new Point(249, 18);
txt_value.Size = new Size(74, 21);
but_write.Location = new Point(328, 17);
txt_dataPackage.Location = new Point(430, 18);
txt_dataPackage.Size = new Size(186, 21);
but_sendData.Location = new Point(620, 17);
chb_show_package.Location = new Point(776, 19);
but_read.Enabled = false;
but_write.Enabled = false;
but_server_close.Enabled = false;
but_close.Enabled = false;
but_sendData.Enabled = false;
UpdatePortNames();
cb_portNameSend.DropDownStyle = ComboBoxStyle.DropDownList;
cb_portNameSend_server.DropDownStyle = ComboBoxStyle.DropDownList;
cb_parity.SelectedIndex = 0;
cb_parity.DropDownStyle = ComboBoxStyle.DropDownList;
cb_baudRate.SelectedIndex = 2;
toolTip1.SetToolTip(but_open, "按住Ctrl后连接将自动扫描串口连接参数");
toolTip1.SetToolTip(txt_address, "支持批量读取,如4-3将会读取4、5、6地址对应的数据");
txt_content.Text = "小技巧:\r\n1、按住Ctrl后点击连接将自动扫描串口连接参数组合\r\n2、读取地址支持批量读取,如4-3将会读取4、5、6地址对应的数据\r\n";
}
///
/// 更新串口名
///
public void UpdatePortNames()
{
cb_portNameSend.DataSource = ModBusRtuClient.GetPortNames();
cb_portNameSend_server.DataSource = ModBusRtuClient.GetPortNames();
}
///
/// 打开连接
///
///
///
private void but_open_Click(object sender, EventArgs e)
{
try
{
if (txt_content.Text.Contains("小技巧")) txt_content.Text = string.Empty;
var PortName = cb_portNameSend.Text.ToString();
var BaudRate = int.Parse(cb_baudRate.Text.ToString());
var DataBits = int.Parse(txt_dataBit.Text.ToString());
var StopBits = (StopBits)int.Parse(txt_stopBit.Text.ToString());
var parity = cb_parity.SelectedIndex == 0 ? Parity.None : (cb_parity.SelectedIndex == 1 ? Parity.Odd : Parity.Even);
client?.Close();
client = new ModBusRtuClient(PortName, BaudRate, DataBits, StopBits, parity);
var result = client.Open();
if (result.IsSucceed)
{
but_open.Enabled = false;
cb_portNameSend.Enabled = false;
but_read.Enabled = true;
but_write.Enabled = true;
but_open.Enabled = false;
but_close.Enabled = true;
but_sendData.Enabled = true;
//按了Ctrl后的鼠标点击
if ((ModifierKeys & Keys.Control) == Keys.Control)
{
Task.Run(() =>
{
AutoOpenRead();
});
}
else
AppendText("连接成功");
}
else
AppendText($"连接失败:{result.Err}");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
///
/// 自动扫描可连接串口属性
///
///
private void AutoOpenRead()
{
#region 当前选择的配置有效匹配
FirstItem(ref BaudRateList, int.Parse(cb_baudRate.Text.ToString()));
FirstItem(ref DataBitList, int.Parse(txt_dataBit.Text.ToString()));
FirstItem(ref StopBitsList, (StopBits)int.Parse(txt_stopBit.Text.ToString()));
var firstParity = cb_parity.SelectedIndex == 0 ? Parity.None : (cb_parity.SelectedIndex == 1 ? Parity.Odd : Parity.Even);
FirstItem(ref ParityList, firstParity);
#endregion
byte.TryParse(txt_stationNumber.Text?.Trim(), out byte stationNumber);
if (!client.ReadInt16("0", stationNumber).IsSucceed)
{
foreach (var baudRate in BaudRateList)
{
foreach (var dataBit in DataBitList)
{
foreach (var stopBits in StopBitsList)
{
foreach (var parity in ParityList)
{
for (byte i = 0; i < 255; i++)
{
stationNumber = (byte)(i + 1);
if (but_open.Enabled) return;
client?.Close();
client = new ModBusRtuClient(cb_portNameSend.Text.ToString(), baudRate, dataBit, stopBits, parity);
var result = client.Open();
if (result.IsSucceed)
{
if (client.ReadInt16("0", stationNumber).IsSucceed)
{
AppendText($@"连接【成功】 端口:{cb_portNameSend.Text.ToString()} 波特率:{baudRate} 数据位:{dataBit} 停止位:{stopBits} 奇偶:{parity} 站号:{stationNumber}");
return;
}
else
{
AppendText($@"连接失败 端口:{cb_portNameSend.Text.ToString()} 波特率:{baudRate} 数据位:{dataBit} 停止位:{stopBits} 奇偶:{parity} 站号:{stationNumber}");
}
}
else
{
AppendText($"连接异常 端口:{cb_portNameSend.Text.ToString()} 波特率:{baudRate} 数据位:{dataBit} 停止位:{stopBits} 奇偶:{parity} 站号:{stationNumber} Err:{result.Err}");
}
}
}
}
}
}
}
else
{
AppendText($@"连接【成功】 端口:{cb_portNameSend.Text} 波特率:{cb_baudRate.Text} 数据位:{txt_dataBit.Text} 停止位:{txt_stopBit.Text} 奇偶:{cb_parity.Text} 站号:{stationNumber}");
}
}
///
/// 移动项到数组第一个
///
///
///
///
private void FirstItem(ref T[] list, T item)
{
if (list.Contains(item))
{
var temp = list.ToList();
temp.Remove(item);
temp.Insert(0, item);
list = temp.ToArray();
}
}
///
/// 关闭连接
///
///
///
private void butClose_Click(object sender, EventArgs e)
{
client?.Close();
AppendText("关闭连接");
but_open.Enabled = true;
but_close.Enabled = false;
cb_portNameSend.Enabled = true;
but_sendData.Enabled = false;
}
///
/// 读数据
///
///
///
private void but_read_Click(object sender, EventArgs e)
{
byte.TryParse(txt_stationNumber.Text?.Trim(), out byte stationNumber);
if (string.IsNullOrWhiteSpace(txt_address.Text))
{
MessageBox.Show("请输入地址");
return;
}
dynamic result = null;
try
{
var addressAndReadLength = txt_address.Text.Split('-');
//批量读取
if (addressAndReadLength.Length == 2)
{
var address = int.Parse(addressAndReadLength[0]);
var readNumber = ushort.Parse(addressAndReadLength[1]);
ushort bLength = 1;
if (rd_bit.Checked || rd_discrete.Checked || rd_short.Checked || rd_ushort.Checked)
bLength = 1;
else if (rd_int.Checked || rd_uint.Checked || rd_float.Checked)
bLength = 2;
else if (rd_long.Checked || rd_ulong.Checked || rd_double.Checked)
bLength = 4;
var readLength = Convert.ToUInt16(bLength * readNumber);
byte functionCode;
if (rd_bit.Checked) functionCode = 1;
else if (rd_discrete.Checked) functionCode = 2;
else functionCode = 3;
result = client.Read(address.ToString(), stationNumber, functionCode, readLength: readLength);
if (result.IsSucceed)
{
AppendEmptyText();
byte[] rValue = result.Value;
rValue = rValue.Reverse().ToArray();
for (int i = 0; i < readNumber; i++)
{
var cAddress = (address + i * bLength).ToString();
if (rd_bit.Checked)
AppendText($"[读取 {address + i * bLength} 成功]:{ client.ReadCoil(address.ToString(), cAddress, rValue).Value}");
else if (rd_discrete.Checked)
AppendText($"[读取 {address + i * bLength} 成功]:{ client.ReadDiscrete(address.ToString(), cAddress, rValue).Value}");
else if (rd_short.Checked)
AppendText($"[读取 {address + i * bLength} 成功]:{ client.ReadInt16(address.ToString(), cAddress, rValue).Value}");
else if (rd_ushort.Checked)
AppendText($"[读取 {address + i * bLength} 成功]:{ client.ReadUInt16(address.ToString(), cAddress, rValue).Value}");
else if (rd_int.Checked)
AppendText($"[读取 {address + i * bLength} 成功]:{ client.ReadInt32(address.ToString(), cAddress, rValue).Value}");
else if (rd_uint.Checked)
AppendText($"[读取 {address + i * bLength} 成功]:{ client.ReadUInt32(address.ToString(), cAddress, rValue).Value}");
else if (rd_long.Checked)
AppendText($"[读取 {address + i * bLength} 成功]:{ client.ReadInt64(address.ToString(), cAddress, rValue).Value}");
else if (rd_ulong.Checked)
AppendText($"[读取 {address + i * bLength} 成功]:{ client.ReadUInt64(address.ToString(), cAddress, rValue).Value}");
else if (rd_float.Checked)
AppendText($"[读取 {address + i * bLength} 成功]:{ client.ReadFloat(address.ToString(), cAddress, rValue).Value}");
else if (rd_double.Checked)
AppendText($"[读取 {address + i * bLength} 成功]:{ client.ReadDouble(address.ToString(), cAddress, rValue).Value}");
}
}
else
AppendText($"[读取 {txt_address.Text?.Trim()} 失败]:{result.Err}");
}
//单个读取
else
{
if (rd_bit.Checked)
{
result = client.ReadCoil(txt_address.Text, stationNumber);
}
else if (rd_short.Checked)
{
result = client.ReadInt16(txt_address.Text, stationNumber);
}
else if (rd_ushort.Checked)
{
result = client.ReadUInt16(txt_address.Text, stationNumber);
}
else if (rd_int.Checked)
{
result = client.ReadInt32(txt_address.Text, stationNumber);
}
else if (rd_uint.Checked)
{
result = client.ReadUInt32(txt_address.Text, stationNumber);
}
else if (rd_long.Checked)
{
result = client.ReadInt64(txt_address.Text, stationNumber);
}
else if (rd_ulong.Checked)
{
result = client.ReadUInt64(txt_address.Text, stationNumber);
}
else if (rd_float.Checked)
{
result = client.ReadFloat(txt_address.Text, stationNumber);
}
else if (rd_double.Checked)
{
result = client.ReadDouble(txt_address.Text, stationNumber);
}
else if (rd_discrete.Checked)
{
result = client.ReadDiscrete(txt_address.Text, stationNumber);
}
if (result.IsSucceed)
AppendText($"[读取 {txt_address.Text?.Trim()} 成功]:{result.Value}");
else
AppendText($"[读取 {txt_address.Text?.Trim()} 失败]:{result.Err}");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
if (chb_show_package.Checked || (ModifierKeys & Keys.Control) == Keys.Control)
{
AppendText($"[请求报文]{result.Requst}");
AppendText($"[响应报文]{result.Response}\r\n");
}
}
}
///
/// 写数据
///
///
///
private void but_write_Click(object sender, EventArgs e)
{
byte.TryParse(txt_stationNumber.Text?.Trim(), out byte stationNumber);
if (string.IsNullOrWhiteSpace(txt_address.Text))
{
MessageBox.Show("请输入地址");
return;
}
if (string.IsNullOrWhiteSpace(txt_value.Text))
{
MessageBox.Show("请输入值");
return;
}
try
{
var address = txt_address.Text?.Trim().Split('-')[0];
dynamic result = null;
if (rd_bit.Checked)
{
if (!bool.TryParse(txt_value.Text?.Trim(), out bool coil))
{
if (txt_value.Text?.Trim() == "0")
coil = false;
else if (txt_value.Text?.Trim() == "1")
coil = true;
else
{
MessageBox.Show("请输入 True 或 False");
return;
}
}
result = client.Write(address, coil, stationNumber);
}
else if (rd_short.Checked)
{
result = client.Write(address, short.Parse(txt_value.Text?.Trim()), stationNumber);
}
else if (rd_ushort.Checked)
{
result = client.Write(address, ushort.Parse(txt_value.Text?.Trim()), stationNumber);
}
else if (rd_int.Checked)
{
result = client.Write(address, int.Parse(txt_value.Text?.Trim()), stationNumber);
}
else if (rd_uint.Checked)
{
result = client.Write(address, uint.Parse(txt_value.Text?.Trim()), stationNumber);
}
else if (rd_long.Checked)
{
result = client.Write(address, long.Parse(txt_value.Text?.Trim()), stationNumber);
}
else if (rd_ulong.Checked)
{
result = client.Write(address, ulong.Parse(txt_value.Text?.Trim()), stationNumber);
}
else if (rd_float.Checked)
{
result = client.Write(address, float.Parse(txt_value.Text?.Trim()), stationNumber);
}
else if (rd_double.Checked)
{
result = client.Write(address, double.Parse(txt_value.Text?.Trim()), stationNumber);
}
else if (rd_discrete.Checked)
{
AppendText($"离散类型只读");
return;
}
if (result.IsSucceed)
AppendText($"[写入 {address?.Trim()} 成功]:{txt_value.Text?.Trim()} OK");
else
AppendText($"[写入 {address?.Trim()} 失败]:{result.Err}");
if (chb_show_package.Checked || (ModifierKeys & Keys.Control) == Keys.Control)
{
AppendText($"[请求报文]{result.Requst}");
AppendText($"[响应报文]{result.Response}\r\n");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
///
/// 启动仿真服务
///
///
///
private void but_open_server_Click(object sender, EventArgs e)
{
try
{
if (txt_content.Text.Contains("小技巧")) txt_content.Text = string.Empty;
var PortName = cb_portNameSend_server.Text.ToString();
var BaudRate = int.Parse(cb_baudRate.Text.ToString());
var DataBits = int.Parse(txt_dataBit.Text.ToString());
var StopBits = (StopBits)int.Parse(txt_stopBit.Text.ToString());
server?.Stop();
server = new ModBusRtuServer(PortName, BaudRate, DataBits, StopBits);
server.Start();
AppendText("开启仿真服务");
but_server_open.Enabled = false;
but_server_close.Enabled = true;
cb_portNameSend_server.Enabled = false;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
///
/// 关闭仿真服务
///
///
///
private void but_close_server_Click(object sender, EventArgs e)
{
server?.Stop();
AppendText("关闭仿真服务");
but_server_open.Enabled = true;
but_server_close.Enabled = false;
cb_portNameSend_server.Enabled = true;
}
///
/// 清空数据
///
///
///
private void but_clear_data_Click(object sender, EventArgs e)
{
DataPersist.Clear();
AppendText($"数据清空成功\r\n");
}
private void but_sendData_Click(object sender, EventArgs e)
{
try
{
if (string.IsNullOrWhiteSpace(txt_dataPackage.Text))
{
MessageBox.Show("请输入要发送的报文");
return;
}
var dataPackageString = txt_dataPackage.Text.Replace(" ", "");
if (dataPackageString.Length % 2 != 0)
{
MessageBox.Show("请输入正确的的报文");
return;
}
var dataPackage = DataConvert.StringToByteArray(txt_dataPackage.Text?.Trim(), false);
var msg = client.SendPackage(dataPackage);
AppendText($"[请求报文]{string.Join(" ", dataPackage.Select(t => t.ToString("X2")))}\r");
AppendText($"[响应报文]{string.Join(" ", msg.Select(t => t.ToString("X2")))}\r\n");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
client.Close();
client.Open();
}
}
private void AppendText(string content)
{
txt_content.Invoke((Action)(() =>
{
txt_content.AppendText($"[{DateTime.Now.ToLongTimeString()}]{content}\r\n");
}));
}
private void AppendEmptyText()
{
txt_content.Invoke((Action)(() =>
{
txt_content.AppendText($"\r\n");
}));
}
}
}