刚刚试了下你的代码有的编译不了,我把我完整的代码给你把结合以上方案修改优化下(控件在界面里已经有的)。
以下是完整代码:
using EasyModbus;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp6
{
public partial class Form1 : Form
{
// 声明静态数组
static ModbusClient[] modbusArray = new ModbusClient[100]; // 创建长度为5的数组
// 声明线程数组
private Thread[] modbusThreads = new Thread[100]; // 创建100个线程的数组
// 声明取消线程标记数组
private volatile bool[] threadStopRequests = new bool[100];
// 声明StringBuilder数组
private StringBuilder[] M_receivedDataBuffers = new StringBuilder[100];
// 方案2:使用二维数组 [设备索引, 寄存器地址]
private int[,] modbusReadReg_Matrix = new int[100, 125]; // 100个设备,每个125寄存器
private int[,] modbusWriteReg_Matrix = new int[100, 125]; // 100个设备,每个125寄存器
private bool[,] modbusReadCoil_Matrix = new bool[100, 2000]; // 100个设备,每个2000线圈
private bool[,] modbusWriteCoil_Matrix = new bool[100, 2000]; // 100个设备,每个2000线圈
int[,] modbusAddress_Matrix = new int[100, 20];//100个设备,0、1列--读取--多个寄存器起始位-长度
//100个设备,2、3列写入多个寄存器起始位-长度
//100个设备,4、5列--读取--多个线圈起始位-长度
//100个设备,6、7列写入多个线圈起始位-长度
bool[] connStateBools = new bool[100];
private string modbus_RW_state;
private string modbus_RW_state2;
bool[] stopred = new bool[100];
// 获取执行程序的根目录路径
public static string rootPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory);
public Form1()
{
InitializeComponent();
AttachButtonEvents();
}
private void Form1_Load(object sender, EventArgs e)
{
DataGridViewComboBoxColumn comboBoxColumn_plcCOM = dataGridView.Columns[5] as DataGridViewComboBoxColumn;
string[] portNames = SerialPort.GetPortNames(); // 获取所有可用串口的名字[^3]
foreach (string portName in portNames) { comboBoxColumn_plcCOM.Items.Add(portName); }
string UserProTaskCSVname = rootPath + "\\" + "串口数据列表.csv";
//读取用户程序 任务流程csv文件
if (File.Exists(UserProTaskCSVname))
{
LoadCsvToFixedDataGridView(UserProTaskCSVname, dataGridView);
}
else { }
}
private void AttachButtonEvents()
{
// 为所有按钮绑定事件
foreach (Control control in tabControl1.TabPages[0].Controls)
{
if (control is Button button)
{
AttachEventsToButton(button);
}
}
}
// 为单个按钮绑定事件
private void AttachEventsToButton(Button button)
{
button.MouseDown += ButtonEventHandlers.HandleMouseDown;
button.MouseUp += ButtonEventHandlers.HandleMouseUp;
button.Click += ButtonEventHandlers.HandleClick;
}
private void dataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
// 获取点击的行索引和列索引
int rowIndex = e.RowIndex;
int columnIndex = e.ColumnIndex;
// 判断是否为按钮列
if (dataGridView.Columns[columnIndex] is DataGridViewButtonColumn && rowIndex >= 0)
{
bool Enable_chk = Convert.ToBoolean(dataGridView.Rows[rowIndex].Cells[1].Value);
string dgvCommProtocol_str = dataGridView.Rows[rowIndex].Cells[2].FormattedValue.ToString();
string dgvIPaddress_str = dataGridView.Rows[rowIndex].Cells[3].FormattedValue.ToString();
string dgvPort_str = dataGridView.Rows[rowIndex].Cells[4].FormattedValue.ToString();
string dgvCOM_str = dataGridView.Rows[rowIndex].Cells[5].FormattedValue.ToString();
string dgvBaudrate_str = dataGridView.Rows[rowIndex].Cells[6].FormattedValue.ToString();
string dgvParity_str = dataGridView.Rows[rowIndex].Cells[7].FormattedValue.ToString();
string dgvIDnum_str = dataGridView.Rows[rowIndex].Cells[8].FormattedValue.ToString();
if (Enable_chk == true)
{
//打开连接按钮
if (columnIndex == 10)
{
PLC_Connectivity(rowIndex, dgvCommProtocol_str, dgvIPaddress_str,
dgvPort_str, dgvCOM_str, dgvBaudrate_str, dgvParity_str, dgvIDnum_str);
}
//打开断开按钮
else if (columnIndex == 11)
{
PLC_Disconnect(rowIndex);
}
int dgvRows = dataGridView.Rows.Count - (dataGridView.AllowUserToAddRows ? 1 : 0);
for (int i = 0; i < dgvRows; i++)
{
if (connStateBools[i] == true)
{
dataGridView.Rows[i].Cells[9].Value = "连接";
dataGridView.Rows[i].Cells[9].Style.BackColor = Color.Lime;
}
else
{
dataGridView.Rows[i].Cells[9].Value = "断开";
dataGridView.Rows[i].Cells[9].Style.BackColor = Color.Red;
}
}
}
}
}
private void PLC_Connectivity(int ArrayNumber, string CommProtocol, string IPaddress, string Port, string COM, string Baudrate, string Parity_str, string IDnum)
{
if (CommProtocol == "modbus_TCP")
{
if (connStateBools[ArrayNumber] == false)
{
try
{
modbusArray[ArrayNumber] = new ModbusClient();
modbusArray[ArrayNumber].Connect(IPaddress, Convert.ToInt16(Port));
M_receivedDataBuffers[ArrayNumber] = new StringBuilder();
threadStopRequests[ArrayNumber] = false; // 重置停止标志
modbusThreads[ArrayNumber] = new Thread(() => ModbusThreadMethod(ArrayNumber));
modbusThreads[ArrayNumber].IsBackground = true;
modbusThreads[ArrayNumber].Start();
connStateBools[ArrayNumber] = true;
}
catch (Exception ex)
{ MessageBox.Show(ex.ToString()); }
}
}
else if (CommProtocol == "modbus_RTU")
{
if (connStateBools[ArrayNumber] == false)
{
try
{
modbusArray[ArrayNumber] = new ModbusClient(COM);
modbusArray[ArrayNumber].UnitIdentifier = byte.Parse(IDnum);//从站ID
modbusArray[ArrayNumber].Baudrate = int.Parse(Baudrate); // 获取选择的波特率
modbusArray[ArrayNumber].StopBits = StopBits.One;//停止位
modbusArray[ArrayNumber].ConnectionTimeout = 1000;//连接超时
//校验位
if (Parity_str == "None") { modbusArray[ArrayNumber].Parity = Parity.None; }
else if (Parity_str == "Odd") { modbusArray[ArrayNumber].Parity = Parity.Odd; }
else if (Parity_str == "Even") { modbusArray[ArrayNumber].Parity = Parity.Even; }
modbusArray[ArrayNumber].Connect();//modbus连接
M_receivedDataBuffers[ArrayNumber] = new StringBuilder();
threadStopRequests[ArrayNumber] = false; // 重置停止标志
modbusThreads[ArrayNumber] = new Thread(() => ModbusThreadMethod(ArrayNumber));
modbusThreads[ArrayNumber].IsBackground = true;
modbusThreads[ArrayNumber].Start();
connStateBools[ArrayNumber] = true;
}
catch (Exception ex)
{ MessageBox.Show(ex.ToString()); }
}
}
}
private void PLC_Disconnect(int ArrayNumber)
{
if (connStateBools[ArrayNumber] == true)
{
try
{
StopModbusThread(ArrayNumber);
modbusArray[ArrayNumber].Disconnect();
connStateBools[ArrayNumber] = false;
}
catch (Exception ex)
{ MessageBox.Show(ex.ToString()); }
}
}
// 线程方法示例
private void ModbusThreadMethod(int threadIndex)
{
while (!threadStopRequests[threadIndex]) // 检查停止请求
{
if (connStateBools[threadIndex] == true)
{
if (modbusAddress_Matrix[threadIndex, 1] > 0)
{
try
{
int startAddress = modbusAddress_Matrix[threadIndex, 0];
int dataLenght = modbusAddress_Matrix[threadIndex, 1];
int[] readReg = new int[dataLenght];
readReg = modbusArray[threadIndex].ReadHoldingRegisters(startAddress, dataLenght);
for (int i = 0; i < readReg.Length; i++)
{
modbusReadReg_Matrix[threadIndex, i] = readReg[i];
}
modbus_RW_state2 = "0-1_OK";
}
catch (Exception ex)
{
modbus_RW_state = "读取多个寄存器时错误" + ex.ToString();
}
}
if (modbusAddress_Matrix[threadIndex, 5] > 0)
{
try
{
int startAddress = modbusAddress_Matrix[threadIndex, 4];
int dataLenght = modbusAddress_Matrix[threadIndex, 5];
bool[] readCoils = new bool[dataLenght];
readCoils = modbusArray[threadIndex].ReadCoils(startAddress, dataLenght);
for (int i = 0; i < readCoils.Length; i++)
{
modbusReadCoil_Matrix[threadIndex, i] = readCoils[i];
}
modbus_RW_state2 = "4-5_OK";
}
catch (Exception ex)
{
modbus_RW_state = "读取多个线圈时错误" + ex.ToString();
}
}
Thread.Sleep(50);
if (modbusAddress_Matrix[threadIndex, 3] > 0)
{
try
{
int startAddress = modbusAddress_Matrix[threadIndex, 2];
int dataLenght = modbusAddress_Matrix[threadIndex, 3];
int[] WriteReg = new int[dataLenght];
for (int i = 0; i < WriteReg.Length; i++)
{
WriteReg[i] = modbusWriteReg_Matrix[threadIndex, i];
}
modbusArray[threadIndex].WriteMultipleRegisters(startAddress, WriteReg);
modbus_RW_state2 = "2-3_OK";
}
catch (Exception ex)
{
modbus_RW_state = "写入多个寄存器时错误" + ex.ToString();
}
}
if (modbusAddress_Matrix[threadIndex, 7] > 0)
{
try
{
int startAddress = modbusAddress_Matrix[threadIndex, 6];
int dataLenght = modbusAddress_Matrix[threadIndex, 7];
bool[] WriteCoils = new bool[dataLenght];
for (int i = 0; i < WriteCoils.Length; i++)
{
WriteCoils[i] = modbusWriteCoil_Matrix[threadIndex, i];
}
modbusArray[threadIndex].WriteMultipleCoils(startAddress, WriteCoils);
modbus_RW_state2 = "6-7_OK";
}
catch (Exception ex)
{
modbus_RW_state = "写入多个线圈时错误" + ex.ToString();
}
}
Thread.Sleep(50);
}
// 添加可中断的等待
for (int i = 0; i < 5; i++) // 将50ms拆分为5个10ms等待
{
if (threadStopRequests[threadIndex]) break; // 每次等待前检查退出请求
Thread.Sleep(10);
}
}
}
public void StopModbusThread(int arrayNumber)
{
if (modbusThreads[arrayNumber] != null && modbusThreads[arrayNumber].IsAlive)
{
// 1. 设置停止标志
threadStopRequests[arrayNumber] = true;
// 2. 等待线程完成当前周期(最多等待500ms)
if (!modbusThreads[arrayNumber].Join(1000))
{
// 3. 如果超时仍未停止,尝试中断(比Abort更安全)
modbusThreads[arrayNumber].Interrupt();
}
// 4. 重置线程对象
modbusThreads[arrayNumber] = null;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
int shebei = 0;
if (comboBox1.Text != "") { shebei = int.Parse(comboBox1.Text); }
else { shebei = 0; }
label1.Text = modbusReadReg_Matrix[shebei, 0].ToString();
label2.Text = modbusReadReg_Matrix[shebei, 1].ToString();
label4.Text = modbusReadCoil_Matrix[shebei, 0].ToString();
label5.Text = modbusReadCoil_Matrix[shebei, 1].ToString();
textBox1.Text = modbus_RW_state;
label7.Text = modbus_RW_state2;
}
private void button1_Click(object sender, EventArgs e)
{
int regleght=int.Parse(textBox_regLeght.Text);
int coillenght = int.Parse(textBox_coilLeght.Text);
int WriteData = int.Parse(textBox_WritData.Text);
bool WriteCoil = false;
if (textBox_writeCoil.Text == "1") { WriteCoil = true; }
for (int j = 0; j < 100; j++)
{
for (int i = 0; i < regleght; i++)
{
modbusWriteReg_Matrix[j, i] = WriteData;
}
for (int i = 0; i < coillenght; i++)
{
modbusWriteCoil_Matrix[j, i] = WriteCoil;
}
}
for (int i = 0; i < 100; i++)
{
modbusAddress_Matrix[i, 0] = 0;
modbusAddress_Matrix[i, 1] = regleght;
modbusAddress_Matrix[i, 2] = 200;
modbusAddress_Matrix[i, 3] = regleght;
modbusAddress_Matrix[i, 4] = 0;
modbusAddress_Matrix[i, 5] = coillenght;
modbusAddress_Matrix[i, 6] = 2000;
modbusAddress_Matrix[i, 7] = coillenght;
}
modbus_RW_state = "";
}
private void button7_Click(object sender, EventArgs e)
{
int threadIndex = 0;
Thread.Sleep(50);
int[] wrreg = new int[125];
bool[] wrbol = new bool[2000];
for (int i = 0; i < wrreg.Length; i++)
{
wrreg[i] = 11;
modbusWriteReg_Matrix[0,i] = wrreg[i];
}
for (int i = 0; i < wrbol.Length; i++)
{
wrbol[i] = true;
modbusWriteCoil_Matrix[0, i] = wrbol[i];
}
//int int0 = DateTime.Now.Second;
//int int1 = DateTime.Now.Millisecond;
//int[] wrReg = {int0,int1};
try
{
modbusArray[0].WriteMultipleRegisters(200, wrreg);
modbusArray[0].WriteMultipleCoils(2000, wrbol);
stopred[0] = false;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
stopred[0] = false;
}
}
private void A()
{
Thread.Sleep(1000);
label1.Text = "AAAA";
}
private void B()
{
label1.Text ="BBBB";
}
private void timer2_Tick(object sender, EventArgs e)
{
label8.Text = DateTime.Now.ToString("yyyy-MM-DD-HH:mm:ss fff");
}
//************************************ DataGridView操作方法 *****************************************
#region DataGridView操作方法
private void SaveDataGridViewToCsv(string filePath, DataGridView dgv)
{
StringBuilder sb = new StringBuilder();
// 添加表头
for (int colIdx = 0; colIdx < dgv.Columns.Count; colIdx++)
{
sb.Append(dgv.Columns[colIdx].HeaderText);
if (colIdx != dgv.Columns.Count - 1) sb.Append(",");
}
sb.AppendLine();
// 遍历所有行并将数据追加到字符串中
foreach (DataGridViewRow row in dgv.Rows)
{
for (int cellIdx = 0; cellIdx < dgv.Columns.Count; cellIdx++)
{
if (dgv.Columns[cellIdx] is DataGridViewComboBoxColumn comboBoxCol)
{
sb.Append(row.Cells[cellIdx].FormattedValue); // 获取 ComboBox 显示值
}
else if (dgv.Columns[cellIdx] is DataGridViewCheckBoxColumn checkBoxCol)
{
sb.Append(row.Cells[cellIdx].EditedFormattedValue.ToString()); // 获取 CheckBox 勾选状态
}
else
{
sb.Append(row.Cells[cellIdx].Value?.ToString() ?? ""); // 普通文本列
}
if (cellIdx != dgv.Columns.Count - 1) sb.Append(",");
}
sb.AppendLine();
}
// 写入文件
System.IO.File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
}
private void LoadCsvToFixedDataGridView(string filePath, DataGridView dgv)
{
dgv.Rows.Clear();
try
{
List<string[]> rowsData = new List<string[]>();
using (StreamReader sr = new StreamReader(filePath))
{
string line;
int rowIndex = 0;
while ((line = sr.ReadLine()) != null)
{
if (rowIndex == 0) { rowIndex++; continue; } // 跳过表头
string[] rowData = line.Split(',');
rowsData.Add(rowData);
}
}
foreach (var rowData in rowsData)
{
object[] valuesToAdd = new object[dgv.Columns.Count];
for (int i = 0; i < dgv.Columns.Count; i++)
{
if (dgv.Columns[i] is DataGridViewComboBoxColumn comboBoxCol)
{
valuesToAdd[i] = comboBoxCol.Items.Cast<object>()
.FirstOrDefault(item => item.ToString().Equals(rowData[i], StringComparison.OrdinalIgnoreCase));
}
else if (dgv.Columns[i] is DataGridViewCheckBoxColumn checkBoxCol)
{
valuesToAdd[i] = bool.TryParse(rowData[i], out bool isChecked) ? isChecked : false;
}
else
{
valuesToAdd[i] = rowData[i];
}
}
dgv.Rows.Add(valuesToAdd);
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(dgv.Name.ToString() + " Failed to load data from CSV file." + Environment.NewLine + ex.Message,
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// 判断是否有足够的行可删
if (dgv.Rows.Count > 1)
{
// 获取最后一个非新行的索引
int lastIndex = dgv.Rows.Count - 2;
// 移除该行
dgv.Rows.RemoveAt(lastIndex);
}
}
#endregion
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//新建任务流程csv文件
string TaskCSVname = rootPath + "\\" + "串口数据列表.csv";
if (!File.Exists(TaskCSVname))
{
try
{
using (FileStream fs = File.Create(TaskCSVname)) { }
}
catch (Exception ex) { System.Windows.Forms.MessageBox.Show($"创建用户程序 任务流程csv文件时发生错误: {ex.Message}"); }
}
if (File.Exists(TaskCSVname))
{
SaveDataGridViewToCsv(TaskCSVname, dataGridView);
}
}
}
}
最新发布