1. 项目概述与核心功能
这个基于C#开发的西门子200Smart温度监控系统,是我在工业自动化领域积累多年经验后设计的一套完整解决方案。系统通过Windows Forms应用程序实现了对PLC温度数据的实时采集、可视化展示、异常报警以及数据存储功能,特别适合中小型生产线的温度监控场景。
核心功能模块包括:
- PLC通信模块:通过S7协议与西门子200Smart建立稳定连接
- 实时监控模块:动态显示温度曲线和当前数值
- 报警模块:当温度超出设定阈值时触发声光报警
- 数据存储模块:自动生成每日Excel报表并支持SQL数据库存储
- 历史查询模块:提供两种方式回溯历史温度数据
2. 系统架构设计解析
2.1 分层架构设计
系统采用典型的三层架构设计,确保各模块职责清晰:
- 表现层(UI)
- 主窗体(Form1):集成监控、报警、基础控制功能
- 查询窗体(Form2):专用于Excel历史数据查询
- 使用Windows Forms标准控件构建,确保兼容性
- 业务逻辑层
- PLC通信服务:封装HslCommunication组件的调用
- 数据处理服务:负责数据校验、转换和阈值判断
- 定时任务调度:管理数据采集和存储的时间间隔
- 数据访问层
- Excel操作:基于ClosedXML组件实现
- 数据库访问:使用SqlClient连接SQL Server
- 文件系统:管理本地数据存储目录结构
2.2 关键技术选型
选择.NET Framework 4.5.2作为基础框架,主要考虑因素包括:
- 工业现场PC普遍预装Windows 7/10系统
- 该版本框架具有最佳兼容性和稳定性
- 对传统工控设备的支持最为完善
HslCommunication组件选择4.1.3版本,因为:
- 经过多个项目验证的稳定版本
- 对西门子S7协议实现完整
- 提供友好的API接口和详尽的文档
3. PLC通信模块实现细节
3.1 连接建立流程
PLC连接的核心代码如下:
csharp复制private void btnConnect_Click(object sender, EventArgs e)
{
try {
plc = new SiemensS7Net(SiemensPLCS.S200Smart);
plc.IpAddress = txtIP.Text;
plc.ConnectTimeOut = 3000;
OperateResult connectResult = plc.ConnectServer();
if (connectResult.IsSuccess) {
lblStatus.Text = "连接PLC成功";
lblStatus.BackColor = Color.LightGreen;
} else {
throw new Exception(connectResult.Message);
}
} catch (Exception ex) {
MessageBox.Show($"连接失败: {ex.Message}");
lblStatus.Text = "连接PLC失败";
lblStatus.BackColor = Color.Red;
}
}
关键点说明:
- 连接超时设置为3秒,避免界面卡死
- 使用OperateResult判断连接状态
- 通过标签颜色变化直观显示状态
3.2 数据采集机制
温度数据采集采用定时器轮询方式:
csharp复制private void timer1_Tick(object sender, EventArgs e)
{
if (plc == null || !plc.IsConnected) return;
float tempValue = plc.ReadFloat("V0").Content;
DateTime now = DateTime.Now;
// 更新UI显示
lblTemp.Text = tempValue.ToString("F2");
UpdateChart(tempValue, now);
// 阈值判断
CheckTemperature(tempValue);
}
注意:V0地址必须确保在PLC中配置为浮点数类型,否则读取的值将不正确。在实际项目中遇到过因数据类型配置错误导致数据显示异常的情况。
4. 实时监控界面实现
4.1 波形图配置技巧
Chart控件的详细配置参数:
csharp复制private void InitChart()
{
chart1.Series.Clear();
Series series = new Series("温度");
series.ChartType = SeriesChartType.Spline;
series.Color = Color.Red;
series.BorderWidth = 2;
chart1.Series.Add(series);
// X轴配置
chart1.ChartAreas[0].AxisX.Title = "时间(秒)";
chart1.ChartAreas[0].AxisX.Interval = 2;
chart1.ChartAreas[0].AxisX.Minimum = 0;
chart1.ChartAreas[0].AxisX.Maximum = 30;
// Y轴配置
chart1.ChartAreas[0].AxisY.Title = "温度(℃)";
chart1.ChartAreas[0].AxisY.Interval = 5;
chart1.ChartAreas[0].AxisY.Minimum = 0;
chart1.ChartAreas[0].AxisY.Maximum = 100;
// 网格线样式
chart1.ChartAreas[0].AxisX.MajorGrid.LineColor = Color.Silver;
chart1.ChartAreas[0].AxisY.MajorGrid.LineColor = Color.Silver;
}
实际应用中发现几个优化点:
- 当数据点超过100个时,适当增加X轴Maximum值
- 温度波动剧烈时,可减小Y轴Interval到2℃
- 添加双Y轴可以同时显示不同量纲的数据
4.2 报警功能实现
温度阈值判断逻辑:
csharp复制private void CheckTemperature(float value)
{
if (value > upperLimit) {
lblStatus.Text = "NG-温度过高";
lblStatus.BackColor = Color.Red;
SystemSounds.Exclamation.Play();
MessageBox.Show($"温度过高报警: {value}℃", "警告",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else if (value < lowerLimit) {
lblStatus.Text = "NG-温度过低";
lblStatus.BackColor = Color.Red;
SystemSounds.Exclamation.Play();
MessageBox.Show($"温度过低报警: {value}℃", "警告",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else {
lblStatus.Text = "OK-运行正常";
lblStatus.BackColor = Color.Green;
}
}
报警功能优化建议:
- 添加报警延时功能,避免瞬时波动误报
- 实现报警分级(预警、严重报警)
- 增加报警历史记录功能
5. 数据存储模块详解
5.1 Excel报表生成方案
每日报表生成的核心代码:
csharp复制private void GenerateDailyReport(float tempValue)
{
string dateStr = DateTime.Now.ToString("yyyyMMdd");
string path = Path.Combine(Application.StartupPath, "Data", dateStr + ".xlsx");
// 如果文件不存在则创建
if (!File.Exists(path)) {
using (var workbook = new XLWorkbook()) {
var worksheet = workbook.Worksheets.Add("温度数据");
worksheet.Cell(1, 1).Value = "VD0数据值";
worksheet.Cell(1, 2).Value = "采集时间";
worksheet.Cell(1, 3).Value = "数据总数";
// 设置表头样式
var headerRange = worksheet.Range("A1:C1");
headerRange.Style.Fill.BackgroundColor = XLColor.Blue;
headerRange.Style.Font.FontSize = 16;
headerRange.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
workbook.SaveAs(path);
}
}
// 追加数据
using (var workbook = new XLWorkbook(path)) {
var worksheet = workbook.Worksheet(1);
int lastRow = worksheet.LastRowUsed().RowNumber() + 1;
worksheet.Cell(lastRow, 1).Value = tempValue;
worksheet.Cell(lastRow, 2).Value = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
worksheet.Cell(lastRow, 3).Value = lastRow - 1;
workbook.Save();
}
}
提示:使用ClosedXML而非NPOI的原因是ClosedXML对.xlsx格式支持更好,API更友好,且不需要安装Office组件。
5.2 SQL数据库存储实现
数据库操作封装类示例:
csharp复制public class DbHelper
{
private static string connStr = "Server=.;Database=db_test;User ID=sa;Password=abc123;";
public static bool InsertTemperature(float value)
{
using (SqlConnection conn = new SqlConnection(connStr)) {
try {
conn.Open();
string sql = "INSERT INTO tb_VD0 (VD0, DataTime) VALUES (@value, @time)";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@value", value.ToString());
cmd.Parameters.AddWithValue("@time", DateTime.Now);
return cmd.ExecuteNonQuery() > 0;
} catch (Exception ex) {
// 记录日志
return false;
}
}
}
public static DataTable QueryData(DateTime start, DateTime end)
{
DataTable dt = new DataTable();
using (SqlConnection conn = new SqlConnection(connStr)) {
string sql = "SELECT VD0, DataTime FROM tb_VD0 WHERE DataTime BETWEEN @start AND @end";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@start", start);
cmd.Parameters.AddWithValue("@end", end);
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(dt);
}
return dt;
}
}
数据库设计建议:
- 添加索引提高查询效率
- 考虑使用存储过程替代直接SQL
- 实现连接池优化性能
6. 历史数据查询功能
6.1 SQL数据查询界面
时间范围查询的实现要点:
csharp复制private void btnQuery_Click(object sender, EventArgs e)
{
DateTime start = new DateTime(
(int)cmbStartYear.SelectedItem,
(int)cmbStartMonth.SelectedItem,
(int)cmbStartDay.SelectedItem);
DateTime end = new DateTime(
(int)cmbEndYear.SelectedItem,
(int)cmbEndMonth.SelectedItem,
(int)cmbEndDay.SelectedItem).AddDays(1);
DataTable dt = DbHelper.QueryData(start, end);
dataGridView1.DataSource = dt;
// 自动调整列宽
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
}
界面优化技巧:
- 使用DateTimePicker替代组合框更直观
- 添加分页功能处理大数据量
- 实现数据导出为Excel功能
6.2 Excel数据查询界面
特定日期数据加载方法:
csharp复制private void LoadExcelData(DateTime date)
{
string dateStr = date.ToString("yyyyMMdd");
string path = Path.Combine(Application.StartupPath, "Data", dateStr + ".xlsx");
if (!File.Exists(path)) {
MessageBox.Show("当日数据文件不存在");
return;
}
string connStr = $"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={path};Extended Properties='Excel 12.0;HDR=YES;'";
using (OleDbConnection conn = new OleDbConnection(connStr)) {
OleDbDataAdapter da = new OleDbDataAdapter("SELECT * FROM [温度数据$]", conn);
DataTable dt = new DataTable();
da.Fill(dt);
dataGridView1.DataSource = dt;
dataGridView1.Columns[0].Width = 350;
dataGridView1.Columns[1].Width = 350;
dataGridView1.Columns[2].Width = 200;
}
}
注意:使用OleDb读取Excel需要确保目标机器安装了Access Database Engine,否则会报错。在实际部署时遇到过这个问题,解决方案是打包安装必要的运行时组件。
7. 系统部署与维护指南
7.1 环境准备清单
硬件要求:
- 工业PC:建议使用研华或西门子工控机
- 网络设备:工业级交换机,确保网络稳定
- PLC配置:固定IP地址,与PC同网段
软件依赖:
- .NET Framework 4.5.2运行时
- Access Database Engine(如需Excel查询)
- SQL Server Native Client(如需数据库功能)
7.2 常见问题排查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| PLC连接失败 | IP地址错误/网络不通 | 检查IP配置,ping测试连通性 |
| 温度显示为0 | V0地址数据类型错误 | 确认PLC中V0配置为REAL类型 |
| Excel写入失败 | 文件被占用/权限不足 | 关闭已打开的Excel,检查文件夹权限 |
| 图表不更新 | 定时器未启动 | 确认点击了"开始运行"按钮 |
| 数据库连接超时 | SQL服务未启动 | 检查SQL Server服务状态 |
7.3 性能优化建议
- 数据采集优化:
- 适当调整采集间隔(1秒可能对某些PLC过快)
- 实现批量读取多个地址数据
- 存储性能优化:
- Excel写入改用队列缓冲
- 数据库操作采用异步方式
- 界面响应优化:
- 使用BackgroundWorker处理耗时操作
- 图表数据点数量控制在合理范围
8. 项目扩展与改进方向
- 功能扩展建议:
- 添加用户权限管理系统
- 实现远程Web监控功能
- 增加数据统计分析模块
- 技术升级路径:
- 迁移到WPF改善UI体验
- 采用OPC UA替代S7协议
- 引入MQTT实现物联网集成
- 实际应用案例:
在某食品烘干生产线项目中,这套系统实现了:
- 7×24小时稳定运行6个月无故障
- 及时发现3次加热管异常情况
- 生产数据完整记录,满足审计要求
系统经过多个项目的实际验证,证明其稳定性和实用性。特别是在温度敏感的生产环境中,实时监控和报警功能帮助客户避免了多次质量事故。
