本项目旨在开发一个Windows Forms应用程序,用户可以通过输入序列号(SN)来检索Excel文件中对应的行数据,并通过串口发送多种指令,随后根据指令类型对串口回显进行特征化校验。该程序具备以下核心功能:
在Visual Studio中创建一个新的Windows Forms应用程序项目。命名为SN查询与串口通信工具。
通过NuGet包管理器安装以下包:
在设计器中添加以下控件:
txtSN。btnSearch,文本为“查询”。txtExcelResult,设置为多行模式。cmbSerialPort。btnOpenPort,文本为“打开串口”。txtCommand。btnSendCommand,文本为“发送指令”。txtResponse,设置为多行模式。使用EPPlus库读取Excel文件,根据输入的SN查找对应的行数据。以下是一个示例方法:
using OfficeOpenXml;
using System.Data;
using System.IO;
using System.Windows.Forms;
public DataRow GetExcelDataBySN(string filePath, string sheetName, string sn)
{
FileInfo existingFile = new FileInfo(filePath);
using (ExcelPackage package = new ExcelPackage(existingFile))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[sheetName];
if (worksheet == null)
{
MessageBox.Show("未找到指定的工作表。");
return null;
}
int rowCount = worksheet.Dimension.Rows;
int colCount = worksheet.Dimension.Columns;
for (int row = 2; row <= rowCount; row++) // 假设第一行为表头
{
if (worksheet.Cells[row, 1].Text.Trim() == sn)
{
DataTable dt = new DataTable();
for (int col = 1; col <= colCount; col++)
{
if (row == 2)
{
dt.Columns.Add(worksheet.Cells[row, col].Text.Trim());
}
else
{
dt.Columns[col - 1].DataType = typeof(string);
}
}
DataRow dataRow = dt.NewRow();
for (int col = 1; col <= colCount; col++)
{
dataRow[col - 1] = worksheet.Cells[row, col].Text.Trim();
}
return dataRow;
}
}
MessageBox.Show("未找到对应的SN记录。");
return null;
}
}
使用SerialPort类进行串口配置与通信。以下是初始化串口的方法:
using System.IO.Ports;
public SerialPort serialPort;
public void InitializeSerialPort(string portName, int baudRate)
{
serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One)
{
ReadTimeout = 3000,
WriteTimeout = 3000
};
serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
this.Invoke(new Action(() => {
txtResponse.AppendText(indata);
}));
}
根据输入的指令类型发送指令,并依据指令类型对回显进行特征校验。以下是发送指令并验证回显的示例代码:
public void SendCommand(string command)
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.WriteLine(command);
}
else
{
MessageBox.Show("串口未打开或不存在。");
}
}
public bool ValidateResponse(string command, string response)
{
if (command.StartsWith("CMD_A"))
{
return response.Contains("ACK_A");
}
else if (command.StartsWith("CMD_B"))
{
return response.Contains("ACK_B");
}
// 添加更多指令类型的校验逻辑
return false;
}
private void btnSendCommand_Click(object sender, EventArgs e)
{
string command = txtCommand.Text.Trim();
if (!string.IsNullOrEmpty(command))
{
SendCommand(command);
// 假设在DataReceivedHandler中已将回显显示在txtResponse中
// 这里可以根据实际情况添加等待或回调机制进行校验
}
else
{
MessageBox.Show("请输入指令。");
}
}
以下是整合上述功能的完整代码示例:
using System;
using System.Data;
using System.IO;
using System.IO.Ports;
using System.Windows.Forms;
using OfficeOpenXml;
namespace SNSerialPortApp
{
public partial class MainForm : Form
{
private SerialPort serialPort;
public MainForm()
{
InitializeComponent();
LoadAvailablePorts();
}
private void LoadAvailablePorts()
{
cmbSerialPort.Items.AddRange(SerialPort.GetPortNames());
if (cmbSerialPort.Items.Count > 0)
{
cmbSerialPort.SelectedIndex = 0;
}
}
private void btnSearch_Click(object sender, EventArgs e)
{
string sn = txtSN.Text.Trim();
if (string.IsNullOrEmpty(sn))
{
MessageBox.Show("请输入SN。");
return;
}
string excelPath = @"C:\path\to\your\excel.xlsx"; // 修改为实际路径
string sheetName = "Sheet1"; // 修改为实际工作表名称
DataRow row = GetExcelDataBySN(excelPath, sheetName, sn);
if (row != null)
{
string result = "";
foreach (var item in row.ItemArray)
{
result += item.ToString() + "\t";
}
txtExcelResult.Text = result;
}
}
private DataRow GetExcelDataBySN(string filePath, string sheetName, string sn)
{
try
{
FileInfo existingFile = new FileInfo(filePath);
using (ExcelPackage package = new ExcelPackage(existingFile))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[sheetName];
if (worksheet == null)
{
MessageBox.Show("未找到指定的工作表。");
return null;
}
int rowCount = worksheet.Dimension.Rows;
int colCount = worksheet.Dimension.Columns;
for (int row = 2; row <= rowCount; row++)
{
if (worksheet.Cells[row, 1].Text.Trim() == sn)
{
DataTable dt = new DataTable();
for (int col = 1; col <= colCount; col++)
{
if (row == 2)
{
dt.Columns.Add(worksheet.Cells[row, col].Text.Trim());
}
else
{
dt.Columns[col - 1].DataType = typeof(string);
}
}
DataRow dataRow = dt.NewRow();
for (int col = 1; col <= colCount; col++)
{
dataRow[col - 1] = worksheet.Cells[row, col].Text.Trim();
}
return dataRow;
}
}
MessageBox.Show("未找到对应的SN记录。");
return null;
}
}
catch (Exception ex)
{
MessageBox.Show("读取Excel文件时出错:" + ex.Message);
return null;
}
}
private void btnOpenPort_Click(object sender, EventArgs e)
{
if (serialPort != null && serialPort.IsOpen)
{
serialPort.Close();
btnOpenPort.Text = "打开串口";
return;
}
string portName = cmbSerialPort.SelectedItem.ToString();
int baudRate = 9600; // 可根据需要调整
try
{
serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);
serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
serialPort.Open();
btnOpenPort.Text = "关闭串口";
MessageBox.Show("串口已打开。");
}
catch (Exception ex)
{
MessageBox.Show("无法打开串口:" + ex.Message);
}
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
this.Invoke(new Action(() => {
txtResponse.AppendText(indata + Environment.NewLine);
}));
}
private void btnSendCommand_Click(object sender, EventArgs e)
{
string command = txtCommand.Text.Trim();
if (string.IsNullOrEmpty(command))
{
MessageBox.Show("请输入指令。");
return;
}
if (serialPort != null && serialPort.IsOpen)
{
serialPort.WriteLine(command);
// 这里可以添加等待机制或回调进行回显校验
// 示例中直接显示回显内容
// 实际应用中建议使用更可靠的回显处理方式
}
else
{
MessageBox.Show("串口未打开。");
}
}
private bool ValidateResponse(string command, string response)
{
if (command.StartsWith("CMD_A"))
{
return response.Contains("ACK_A");
}
else if (command.StartsWith("CMD_B"))
{
return response.Contains("ACK_B");
}
// 添加更多指令类型的校验逻辑
return false;
}
}
通过EPPlus库读取指定路径的Excel文件,遍历工作表中的每一行,匹配输入的SN,若匹配成功则提取该行的所有单元格数据并显示在界面上。此过程包括错误处理,如未找到工作表或SN记录。
利用SerialPort类初始化串口参数,包括端口名称、波特率、校验位等。用户可以通过下拉菜单选择可用的串口,并通过按钮控制串口的打开与关闭。串口开启后,程序将监听数据接收事件,并将接收到的数据实时显示在界面上。
用户可以在指令输入框中输入想要发送的指令,点击发送按钮后,指令将通过串口发送至目标设备。接收到的回显数据将显示在回显文本框中。根据指令前缀或类型,程序将执行相应的回显校验逻辑,确保指令正确执行。
假设有两种指令类型:
ACK_AACK_B校验函数如下:
private bool ValidateResponse(string command, string response)
{
if (command.StartsWith("CMD_A"))
{
return response.Contains("ACK_A");
}
else if (command.StartsWith("CMD_B"))
{
return response.Contains("ACK_B");
}
// 添加更多指令类型的校验逻辑
return false;
}
为了提升用户体验,建议实现以下优化:
private void btnSendCommand_Click(object sender, EventArgs e)
{
string command = txtCommand.Text.Trim();
if (string.IsNullOrEmpty(command))
{
MessageBox.Show("请输入指令。");
return;
}
if (serialPort != null && serialPort.IsOpen)
{
serialPort.WriteLine(command);
// 假设在DataReceivedHandler中已将回显显示在txtResponse中
// 这里可以根据实际情况添加等待机制或延时后进行校验
System.Threading.Thread.Sleep(500); // 简单延时示例
bool isValid = ValidateResponse(command, txtResponse.Text);
if (isValid)
{
MessageBox.Show("指令回显校验成功。");
}
else
{
MessageBox.Show("指令回显校验失败。");
}
}
else
{
MessageBox.Show("串口未打开。");
}
}
对于大型Excel文件,逐行读取可能会导致性能瓶颈。可以考虑以下优化措施:
在串口通信过程中,可能会遇到数据丢失、通信中断等问题。建议采取以下措施增强程序的稳健性:
private void InitializeSerialPort(string portName, int baudRate)
{
try
{
serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One)
{
ReadTimeout = 3000,
WriteTimeout = 3000
};
serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
serialPort.Open();
btnOpenPort.Text = "关闭串口";
}
catch (UnauthorizedAccessException)
{
MessageBox.Show("访问被拒绝,无法打开串口。");
}
catch (IOException)
{
MessageBox.Show("串口不存在或被占用。");
}
catch (Exception ex)
{
MessageBox.Show("打开串口时发生错误:" + ex.Message);
}
}
通过上述步骤,您可以构建一个功能完善的WinForm程序,实现通过输入SN查询Excel数据,并通过串口发送多种指令及其回显校验。关键在于合理使用EPPlus进行高效的Excel数据操作,以及灵活运用SerialPort类实现稳定的串口通信。此外,根据不同指令类型设计特定的回显校验逻辑,确保通信的可靠性和准确性。