ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CODESYS读写CSV文件完整方案:从库配置到工程落地

CODESYS读写CSV文件完整方案:从库配置到工程落地 干过几年PLC项目的人应该都有这种体会设备调试阶段最烦的不是逻辑写不对而是甲方突然甩过来一张Excel表说“把这些配方导进去”或者“把这几天的运行数据导出来给我们看看”。你要是拿U盘一个个手动输入几百个参数能输到怀疑人生你要是说“我们PLC不支持”甲方看你的眼神立刻就不对了。其实大部分时候这类需求都可以用CSV文件搞定。CSV就是纯文本的表格数据Excel能直接打开PLC读写起来也不复杂。CODESYS作为目前市面上主流的IEC 61131-3编程环境自带的文件操作库完全支持读写CSV关键是你得知道怎么把这套东西组织得稳妥、高效、不出幺蛾子。这篇文章我就把自己在实际项目里用CODESYS读写CSV文件的一套完整方案写出来从思路拆解、库文件准备、代码实现到调试排查、常见坑点全部覆盖。适合正在做设备配方管理、数据记录、批次追溯或者被Excel表格折磨过的人参考。1. 整体设计与思路拆解1.1 为什么要用CODESYS读写CSV文件先把场景想清楚。CODESYS跑在IPC、嵌入式工控机或者软PLC上最常见的两种数据交互需求配方写入设备切换产品型号时几十上百个工艺参数要跟随产品变化。这些参数放在CSV里操作工在触摸屏上选择产品型号PLC自动读取对应参数并下发到轴、温控器、阀岛等执行机构。数据导出设备运行时的温度曲线、产量计数、报警记录定时写入CSV文件。甲方要数据时直接U盘拷贝或者通过FTP拉走用Excel分析。这两个场景如果用Modbus寄存器或者OPC UA来做通常需要上位机配合而且配置麻烦。CSV文件的优势在于格式足够通用、工具链简单、不依赖第三方协议。你甚至不需要在电脑上装任何专业软件记事本就能打开检查格式。CSV中文全称是“逗号分隔值”它的底层就叫“逗号分隔值文件”本质就是一个带特定分隔符的文本文件。理解这一点是后面所有操作的基础。1.2 CODESYS文件系统的基础认知很多刚从传统PLC比如三菱、西门子S7-200转过来的人第一次接触CODESYS的文件操作时会懵文件路径怎么这么别扭文件到底存在哪里这是因为CODESYS支持多种运行平台。跑在Windows上的CODESYS Control Win文件路径就是普通的C:\Users\...跑在树莓派或者定制Linux系统上的CODESYS Control for Linux路径就是Linux风格的/home/codesys/...跑在倍福、汇川等厂家的嵌入式控制器上路径又可能是/usr/...或者设备厂商自定义的目录。最保险的做法是用CODESYS提供的SysFilePath库来获取系统标准目录而不是硬编码路径。在项目早期就养好这个习惯后面换硬件平台时能省掉大量改代码的时间。另外要注意一点CODESYS的文件操作库分为好几层。最底层是SysFile库直接封装操作系统API功能最全但用起来繁琐上面一层是CAA File库Common Application Architecture通用应用架构对底层做了封装提供了FileOpen、FileRead、FileWrite、FileClose等函数块用起来顺手很多也是目前社区里最常用的方案。本文的方案就是基于CAA File库实现的。1.3 核心难点提前梳理读写CSV文件表面上看就是“打开-读/写-关闭”三个动作实际做起来坑很多。我把它拆成四个核心难点文件路径与命名管理文件放在哪个目录文件名怎么按日期/批次生成如何避免重复和冲突。字符串拆解与组装写CSV要把数据拼接成带分隔符的字符串读CSV要把字符串按分隔符拆开还要处理引号、换行符、浮点数格式等细节。大文件流量控制PLC的内存和运行时资源有限不能像电脑一样一次性把整个文件读入内存必须流式处理逐行读写。容错与异常处理文件不存在、权限不足、磁盘已满、文件被其他程序占用这些情况都要处理否则PLC程序可能直接卡死或崩溃。把这几个难点控制住CSV读写这个功能就算真正落地了。2. 库准备与环境搭建2.1 需要添加哪些库文件在CODESYS的库管理器Library Manager里默认工程一般不会自动带上文件操作库需要手动添加。右键“库管理器”选择“添加库”然后搜索以下库名称CAA File核心文件操作库提供文件读写相关的函数块。CAA TypesCAA File的依赖库包含错误码定义等基础类型。OSCAT Basic非必需但强烈推荐。这是一个开源基础库里面的字符串处理函数非常多做CSV解析时能省很多事。在CODESYS库管理器中搜索“OSCAT”即可找到或者从GitHub上下载后导入。SysFilePath用于获取系统标准目录。如果你使用的CODESYS版本较新部分库可能已经被合并或改名。实际操作时添加CAA File库后它会自动带出依赖项不需要纠结太多。注意CAA File和SysFile不要混用。同一个文件操作生命周期内不要一会用CAA函数块、一会用SysFile底层函数两者的文件句柄管理方式不同混用容易出莫名其妙的文件指针错乱问题。2.2 工程配置与全局变量定义在PLC_PRG或者你自定义的主程序里先定义好全局用的文件管理变量。我习惯把文件操作相关的变量单独放在一个全局变量列表里方便统一管理VAR_GLOBAL // 文件路径拼接缓冲区 g_sFilePath : STRING(255); g_sFileName : STRING(255); // 写入缓冲区 g_sWriteBuffer : STRING(1000); // 读取缓冲区 g_sReadBuffer : STRING(1000); // 文件操作状态 g_bFileBusy : BOOL; g_eFileError : CAA.FILE_ERROR; END_VAR这里我把字符串缓冲区设定为1000个字符实际项目需要根据你的数据规模调整。一次PLC扫描周期内处理1000个字符是完全够用的如果你要一次写一大块数据可以拆分成多次循环写入。另外提一句CODESYS的STRING类型默认是80个字符如果你定义一个STRING变量时不给长度后面赋值超出长度的内容会被静默截断这是很多莫名其妙的“数据不对”问题的根源。所以所有用于文件读写的字符串变量定义时务必显式指定长度比如STRING(255)、STRING(1000)。3. 写入CSV文件从基础到封装3.1 基础写入流程解析任何文件写入操作都遵循“打开-写入-关闭”三步。在CAA File库中对应的是FileOpen、FileWrite、FileClose三个函数块。第一个要点FileOpen的模式选择。FileOpen函数块有一个umod模式输入本质是一个枚举CAA.FILE_MODE常用的是CAA.FILE_MODE.READ只读模式。如果文件不存在会报错。CAA.FILE_MODE.WRITE写入模式。如果文件已存在会从文件头开始覆盖写入。CAA.FILE_MODE.APPEND追加模式。如果文件已存在会从文件末尾继续写入文件不存在则新建。这个选择很关键。比如你要生成一个按日期命名的日志文件每天首次运行时应该用WRITE模式创建新文件后续追加记录要用APPEND模式。如果是配方文件这种固定文件每次写的时候想要完全覆盖旧内容就用WRITE模式。第二个要点FileWrite的缓冲区机制。FileWrite函数块一次接收一个字符串变量写入指定长度。这里有个小坑如果你传入的是一个STRING类型的完整行内容长度参数应该用LEN函数计算实际字符串长度而不是直接传整个缓冲区长度否则会把字符串末尾的空字符也写进去导致CSV文件里出现不可见的乱码内容。我踩过的坑是第一次写的时候直接传了缓冲区长度结果用Excel打开CSV文件每一行后面都多了一长串空白单元格把表格撑得巨大。排查了半天才发现是字符串尾部的空字符被一起写进了文件。3.2 带引号转义的完整写入方案CSV格式有一个容易被忽略的规范如果某个字段里包含逗号、换行符或双引号那么这个字段需要用双引号包裹并且字段内部的双引号需要用两个连续的双引号转义。举个例子某个产品的配方名称是A, B Plus那么写入CSV时这一列的内容应该是A, B Plus如果忽略这一步Excel打开文件时会把逗号当作分列符导致表格列错位数据看着乱七八糟的。所以一个工程级的CSV写入函数块必须包含引号转义处理。下面是一个简化版的写入函数块实现思路// 函数块FB_CSV_WriteLine // 输入sLine 要写入的CSV行已组装好 // 输入pFile 文件句柄 // 输出bDone 本次写入是否完成 IF NOT bBusy THEN bBusy : TRUE; bDone : FALSE; // 先写入一行内容 FileWrite_Instance( hFile : pFile, sData : sLine, udiSize : LEN(sLine), pResult : wResult ); END_IF // 检查写入是否完成 IF FileWrite_Instance.bBusy THEN // 继续等待 ELSE IF FileWrite_Instance.pResult CAA.FILE_OK THEN // 再写入换行符CRLF // 这里再调用一次FileWrite写入ASCII 13和10 bDone : TRUE; ELSE // 错误处理 END_IF bBusy : FALSE; END_IF注意我强调的“先写入内容再写入换行符”这个顺序。有些实现方案是把$R$NCODESYS中表示回车换行的转义字符直接拼接在行字符串末尾一起写入理论上也没问题但分开写更清晰而且方便你统一控制换行符格式Windows用的CRLFLinux有些工具认LF后面会详细说。3.3 生成的日期型文件命名与目录管理实际项目中我最常用的文件命名规则是“设备号日期用途”例如F01_20250615_Recipe.csv F01_20250615_TemperatureLog.csv在CODESYS中拼这种带日期的字符串用系统库里的DT_TO_STRING或者自己把年月日分别转成字符串再拼接。注意DT_TO_STRING出来的格式是带空格的2025-06-15-14:30:25文件路径最好不要含空格所以还是自己拼接更可控。拼接时注意前导零问题月份和日期是个位数时需要补零比如6要变成06。自己写个三位数的判断就行或者用格式化函数。目录管理上我建议把CSV文件统一放在一个子目录下比如/user/data/csv或者C:\PLC_Data\CSV不要在根目录下直接撒文件。根目录通常受系统权限保护而且文件多了以后查找非常痛苦。通过SysFilePath库获取用户目录后再在后面拼接CSV子目录然后用CreateDir函数块创建目录如果返回错误码表示目录已存在就忽略继续往下走。4. 读取CSV文件解析与容错4.1 读取CSV的核心流程读取CSV比写入稍微复杂一点因为你要面对的是未知的、可能不规则的数据。基本流程是打开文件、逐行读取、解析每一行、转成PLC变量、关闭文件。CAA File库的FileRead函数块支持指定长度读取。但CSV是变长的行所以更推荐的做法是按固定长度分块读取到缓冲区然后在缓冲区里搜索换行符$N把换行符之前的内容当作一整行来解析。这里有一个工程上的取舍**一次性读一行还是分块读**如果一行内容很长比如几百个字符分块读就需要自己维护一个“跨块拼接”的机制代码复杂度会上升。我的做法是把读取缓冲区设成足够大比如1000字符用FileRead一次读入然后在缓冲区里搜换行符。实际应用中配方文件的单行很少超过500字符这个方案简单可靠。如果你要读的是大数据量的日志文件可以改成在WHILE循环里多次读取每读一次就解析一次。4.2 字符串拆解与数据类型转换读取CSV最核心的步骤是把一行字符串按分隔符拆成多个字段。CODESYS官方库没有直接提供Split函数但OSCAT库里有现成的字符串处理函数可以大幅减少工作量。我自己封装了一个FB_CSV_SplitLine函数块核心思路是两层循环外层循环遍历整行字符串遇到分隔符逗号或分号就认为是字段边界。内层循环处理双引号内的内容。如果当前字符是双引号就进入“引号模式”在引号模式内遇到的逗号不作为分隔符遇到两个连续双引号还原为一个双引号。解析完成后每个字段存到一个字符串数组里。之后再根据需求用STRING_TO_REAL、STRING_TO_INT、STRING_TO_BOOL等转换函数把字符串转成对应类型的PLC变量。这里有一个特别容易翻车的地方CODESYS的REAL转字符串默认可能是科学计数法格式比如1.234568E003这种格式如果直接写进CSVExcel能识别但某些第三方设备或上位机软件就不一定能正确处理。所以写入CSV时如果数据都是整数优先考虑转成整数格式写入如果是小数自己做格式化控制小数点位数比如拼字符串时用REAL_TO_STRING之后再截取前几位或者用格式化工序。4.3 大文件分批读取与状态机设计如果你的CSV文件有几百上千行数据PLC不能在一次扫描周期里全部处理完否则会严重影响扫描周期变频器、轴控的实时性都会受影响。我的做法是设计一个文本解析状态机让每一行数据的读取和解析分散在多个扫描周期内完成。状态机的基本状态包括空闲IDLE等待触发读取命令。打开中OPENING调用FileOpen等待完成。读取中READING调用FileRead读一块数据解析出一行或多行。行处理中PROCESSING对解析出的行做业务处理比如存到配方结构体。关闭中CLOSING文件读完调用FileClose。完成DONE整个读取流程结束。用状态机的好处是每个扫描周期最多执行一个或两个动作不会卡循环而且程序结构清晰出问题时哪个状态卡住了在线监控一眼就能看出来。在状态机流转过程中务必要注意FileOpen、FileRead、FileClose这些函数块的bBusy输出。这些函数块是异步的调用后不会立即完成下一周期才能判断结果。最常见的错误就是在同一个条件里连续调用这几个函数块导致“上一次操作还没完成下一次操作已经开始了”文件句柄冲突程序直接报错。5. 高频问题与排查技巧5.1 常见报错与排查思路我在项目里碰到的CSV相关报错基本都能归到下面这几类。整理成表格方便大家快速对照错误现象根本原因排查方法文件写入后为空或内容只有一半非阻塞函数块未等待完成就执行下一步文件未关闭就被中止用状态机或IF判断bBusy确保写入完成后才能关闭csv log unsuccessful文件路径不存在或目录无写入权限检查路径、确认目录存在用SysFilePath获取用户目录手动创建一个测试目录验证打开文件报CAA.FILE_ERR.NO_ACCESS文件被其他程序占用或当前用户权限不足Windows上用资源管理器确认文件未被Excel打开Linux下检查文件属主和权限位中文乱码编码不一致PLC写入的UTF-8被Excel以ANSI打开在CSV首行写入sep,或统一使用UTF-8 BOM格式推荐的方案是写入\xEF\xBB\xBF字节序标记Excel打开后列错位字段中包含逗号但未做引号转义检查写入函数块是否有引号转义处理确认双引号包裹逻辑数字变成科学计数法浮点数直接转字符串导致格式不友好自写格式化函数保留固定小数位或先转整数处理csv log unsuccessful这条我特别说一下。这不是CODESYS特有的报错而是很多基于CODESYS的设备在导入/导出配方时常见的提示。踩过坑之后我总结出一个通用排查路径先看路径和文件名再看权限和占用最后看缓冲区长度是否够用。这个排查顺序准确率极高。5.2 缓冲区长度与内存控制技巧字符串缓冲区长度是个典型的“够用就行但宁大勿小”的参数。太小会导致数据被截断太大又浪费内存——PLC的内存和电脑不一样很多嵌入式控制器的用户内存就几MB动辄定义几十个STRING(5000)变量是不可接受的。我的经验值是这样的写CSV时单行缓冲区500字符足够除非你的行字段非常多。读CSV时缓冲区至少设成预期最长行的2倍防止解析过程中拼接字符串产生溢出。不要一次把所有CSV数据都读到PLC的数组里应该边读边处理。比如配方数据读一行就往目标结构体里填一行用完即弃。CODESYS的STRING类型在底层是字符数组大量字符串切片、拼接操作会产生一定的CPU开销。如果一次要处理很多行数据建议牺牲一点可读性用MID、FIND等直接操作字符数组而不是反复用CONCAT。5.3 一个值得记录的调试案例之前在做一个锂电池测试设备时遇到一个诡异的问题CSV文件在Windows上打开一切正常但导入到第三方MES系统时最后一行数据丢失。查了半天发现CSV文件最后一行写完换行符后文件就立即被关闭了但因为写入函数块还没完全flush到磁盘第三方系统读取时文件尾部数据的缓冲还没落盘。解决办法是写完所有数据后关闭文件前加入一个短暂延时或者调用文件系统层的flush函数。另外写CSV时建议每条记录都完整写入后再写下一条避免大段数据集中在关闭前才flush这样也能降低数据丢失的概率。6. 实操心得与项目经验总结6.1 封装成自己的库文件CSV读写功能做完一次之后不要每次都重新写一遍。把FB_CSV_WriteLine、FB_CSV_SplitLine、FB_CSV_ReadLine这些函数块整理到一个自定义库中下次新项目直接引用即可。封装时注意几点输入输出变量全部用结构化变量函数块内部状态不外泄错误码要统一方便上层逻辑统一处理库文件最好带上版本号避免多项目混用时的版本混乱。CODESYS生成库文件也不复杂在工程中把需要发布的POUs放到一个单独的库工程里右键编译生成library文件然后在其他工程里通过“库管理器-添加本地库”导入即可。直接发布的库文件如果不想让使用者看到内部实现可以勾选“隐藏实现代码”选项只暴露接口。6.2 关于编码问题的最终建议CSV文件的编码问题是中文本地化项目绕不过去的坎。最稳妥的做法是在写入CSV文件时文件头加入UTF-8 BOM字节序标记EF BB BF。这样Excel无论中文版还是英文版打开时都能正确识别UTF-8编码不会出现乱码。写入BOM的方法很简单在创建文件后、写入第一行数据前先写入三个字节16#EF、16#BB、16#BF。CAA File库可以用FileWrite写入一个三个字符的字符串变量来实现。如果不想让文件带BOM有些Linux工具会讨厌BOM那至少保证写入的文件是UTF-8编码并明确告知最终使用者“请用UTF-8编码打开”。不要用CODESYS默认的STRING写中文因为PLC里的字符串字面量可能存在老版运行时的ANSI编码问题最好是从HMI或外部传入中文内容或者用Unicode库处理。过去调试时踩过的坑告诉我这一条如果忽略后期排障成本极高。6.3 对比其他实现方案的参考除了CAA File库CODESYS生态还有其他读写CSV的路径简单对比一下方便不同场景选择方案优点缺点适用场景CAA File 字符串手写解析灵活、无依赖、完全可控代码量稍大需要自己处理细节绝大多数标准PLC项目OSCAT字符串处理库 CAA简化拆分拼接代码简洁引入第三方库需要注意许可证做数据导出的记录类设备plc-recorder等第三方工具配置简单自动采集变量到CSV需要额外安装、非标准运行时、可定制性低边缘网关、数据采集盒子方案数据库如SQLite库查询强、功能丰富资源占用高、配置复杂需要做条件查询、数据量大的场景我个人主力方案还是CAA File加自封装函数块。原因很简单工业项目要的是可控任何一个环节依赖第三方库都可能成为后续维护的隐患。6.4 最后给新手的几点操作建议如果你刚开始接触CODESYS文件操作按照下面的顺序练一遍基本就不会走弯路先用CODESYS Control Win仿真在Windows环境下写出第一个能落盘的CSV文件确认路径、权限、编码都没问题再移植到真实控制器上。仿真环境下调试文件操作比直接在实体PLC上试快得多。每次改动CSV相关代码先做小样本测试。比如要导入1000条配方先拿3条测试数据跑通全流程确认格式正确后再上全量。在HMI上做一个简单的文件状态显示页把最近一次读写操作的结果和错误码显示出来。设备放客户现场后这个页面能帮你在电话里快速定位问题省下不少跑现场的时间。备份时带上CSV文件。很多控制器支持U盘备份或FTP上传记得把CSV数据目录一并纳入备份范围别设备换卡后数据全丢了。CSV文件读写说难不难说简单也不简单真正做好需要把文件系统、字符串处理、异步调用、异常处理这些东西全部理顺。这套方案我在多个项目里验证过稳定性和可维护性都经得起考验。下个项目如果再遇到“导个表进来”的需求你可以放心地说“能弄”然后直接用这套方案拿下来。
返回列表