2、数据存储基础:CSV文件格式详解、读写操作、在HMI中的应用
各位工程师朋友,咱们今天聊聊CSV文件。说实话,在工业HMI这个圈子里混久了,你会发现CSV文件就像车间里的螺丝刀——看着不起眼,但真离不开它。
我记得刚入行那会儿,有个老工程师跟我说:「小伙子,别小看CSV,它比数据库灵活,比二进制文件直观,现场调试的时候,它就是你的救命稻草。」后来我做了十几个项目,才真正体会到这句话的分量。
2.1 CSV文件格式详解
CSV,全称是Comma-Separated Values,逗号分隔值。说白了,就是用逗号把数据隔开,一行一条记录。
举个例子,一个简单的温度记录文件:
时间戳,温度(℃),湿度(%),设备ID
2024-01-15 08:00:00,23.5,65.2,PLC_01
2024-01-15 08:01:00,23.7,64.8,PLC_01
2024-01-15 08:02:00,23.6,65.1,PLC_01
你看,第一行是表头,后面是数据。这种格式的好处是什么?
- 人类可读:用记事本就能打开,现场调试时不用装任何软件
- 跨平台:Windows、Linux、嵌入式系统都能处理
- 体积小:纯文本,不占空间,适合HMI这种存储资源有限的环境
核心要点:CSV文件没有严格的国际标准,但有个不成文的规矩——字段里如果包含逗号、换行符或双引号,必须用双引号括起来。比如:
"设备名称","报警信息","发生时间"
"PLC_01","温度过高,请检查","2024-01-15 08:00:00"
2.2 CSV读写操作
在HMI上操作CSV文件,我一般用两种方式:一种是HMI自带的脚本语言,另一种是通过PLC的文本指令。这里我重点讲第一种,因为更灵活。
以常见的威纶通HMI为例,它的宏指令里提供了文件操作函数。我给大家写个例子:
// 写入CSV文件
short fileHandle
string filePath = "USB0:\Data\Temperature.csv"
string dataLine
// 打开文件,追加模式
fileHandle = FileOpen(filePath, 0)
if fileHandle >= 0 then
// 构建数据行
dataLine = "[时间]" + "," + "[温度]" + "," + "[湿度]"
// 写入文件
FileWrite(fileHandle, dataLine, strlen(dataLine))
// 关闭文件
FileClose(fileHandle)
else
// 文件打开失败,记录错误
SetData(1, "Local HMI", "LW", 100, 1)
endif
我的经验:写CSV文件时,一定要处理好文件句柄。我曾经有个项目,因为忘记关闭文件句柄,导致HMI运行三天后文件打不开,现场被客户骂惨了。从那以后,我每次写完文件都强制检查FileClose的返回值。
读取CSV文件稍微复杂一点,因为要解析逗号分隔符:
// 读取CSV文件
short fileHandle
string filePath = "USB0:\Data\Temperature.csv"
string buffer
string fields[10]
short fieldCount
fileHandle = FileOpen(filePath, 1) // 只读模式
if fileHandle >= 0 then
while FileRead(fileHandle, buffer, 256) > 0
// 按逗号分割字符串
fieldCount = StringSplit(buffer, ",", fields)
// 处理每个字段
if fieldCount >= 3 then
// fields[0] 是时间戳
// fields[1] 是温度
// fields[2] 是湿度
endif
endwhile
FileClose(fileHandle)
endif
注意:HMI的存储空间通常不大,我建议每次读取时只处理当前需要的数据,不要一次性把整个文件读到内存里。特别是当文件超过1MB时,很容易导致HMI死机。
2.3 在HMI中的应用
CSV文件在HMI里最常见的应用场景有三个:
- 历史数据记录:把温度、压力、流量等过程变量按时间顺序存下来
- 报警日志:记录报警发生的时间、类型、确认状态
- 生产报表:统计每天的产量、合格率、设备运行时间
我给大家看一个实际项目的例子。去年我给一个化工厂做HMI,他们要求记录反应釜的温度曲线。我是这么设计的:
| 字段名 | 数据类型 | 说明 |
|---|---|---|
| Timestamp | 字符串 | 格式:YYYY-MM-DD HH:MM:SS |
| Temp_Zone1 | 浮点数 | 反应釜上部温度 |
| Temp_Zone2 | 浮点数 | 反应釜中部温度 |
| Temp_Zone3 | 浮点数 | 反应釜下部温度 |
| Pressure | 浮点数 | 釜内压力 |
| Status | 整数 | 0=正常, 1=报警, 2=停机 |
数据采集频率是每10秒一次,每天生成一个文件。文件名用日期命名,比如「20240115.csv」。这样查找历史数据特别方便。
避坑指南:我曾经遇到过一个坑——HMI的时钟不准,导致记录的时间戳全是错的。后来我加了个功能,每次开机时自动从PLC同步时间。你想想看,如果时间戳错了,那历史数据还有什么意义?
另外,我建议在CSV文件里加一个「校验行」。比如在文件末尾写一行「CHECKSUM:xxxx」,用来验证文件是否完整。为什么?因为HMI突然断电时,最后一条数据可能只写了一半。有了校验行,下次开机时就能发现文件损坏。
嗯,这里要注意一点:CSV文件虽然好用,但它不是数据库。如果你需要频繁查询、排序、统计,那还是得用SQLite或者别的嵌入式数据库。CSV更适合「写多读少」的场景——说白了就是只管存,偶尔看看。
最后说一句,CSV文件的编码问题。国内大部分HMI用的是GB2312或GBK编码,但有些进口设备用UTF-8。我建议统一用UTF-8 without BOM,这样在Windows和Linux下都能正常显示。别问我为什么知道,问就是吃过亏。
我的习惯:每次生成CSV文件后,我都会用Excel打开看一眼。如果Excel能正常显示中文,那就没问题。如果显示乱码,赶紧检查编码设置。
好了,CSV文件这块就讲这么多。下一节咱们聊聊更高级的数据存储方式——SQLite数据库在HMI中的应用。到时候我会分享一个我踩过的坑,保证让你少走弯路。