CODESYS开发教程9-文件读写(CAA File库)

今天继续我们的小白教程,老鸟就不要在这浪费时间了😊。

前面一期我们介绍了CODESYS的定时器及触发相关的功能块。这一期主要介绍CODESYS的CAA.File库中的目录和文件读写功能块,主要包括文件路径、名称、大小的获取以及文件的创建、打开、读、写、拷贝和删除功能等。

一、文件库类型简介

文件读写有两种库:CAA File(File Access)库和SysFile库。

1.CAA File(File Access)

CAA File库包含用于访问文件目录和文件的功能块。

对于3.5.17以前的版本,通常是使用CAA File库。由于CAA File库中使用的部分类型定义在另外一个库CAA Types Extern中,因此使用时还需要包含该库。

在3.5.17及以后版本,直接使用File Access即可,如下图所示。

2.SysFile

SysFile属于CODESYS比较底层的库,函数及功能与C语言非常接近。实际上CAA File底层也是调用该库来实现的。

二、CAA.File库介绍

CAA.File库包含用于访问目录和文件的操作。

1.枚举定义

(1)文件属性定义ATTRIB

定义GetAttribute功能块获取的文件属性值。

名称

初始值

说明

ARCHIVE

0

档案文件

HIDDEN

1

隐藏文件

NORMAL

2

没有设置任何其他属性的文件

READONLY

3

只读文件

(2)文件访问模式MODE

定义file.Open功能块打开文件的访问模式。

名称

初始值

说明

MWRITE

0

写访问,文件将被覆盖或创建

MREAD

1

读取访问,文件将仅打开进行读取

MRDWR

2

读取和写入访问,文件将被覆盖或创建

MAPPD

3

文件将以WRITE模式打开,但写入的数据将附加在文件末尾

MREADPLUS

4

打开具有读/写权限的现有文件。如果文件不存在,则打开失败

MWRITEPLUS

5

创建具有读/写权限的新文件。如果文件确实存在,则丢弃内容(与file_MRDWR相同)

MAPPENDPLUS

6

使用附加(读/写)访问权限打开现有文件。如果文件不存在,“打开”将创建一个新文件

(3)错误码定义ERROR

定义在处理CAA_File.library的函数时可能会出现错误值。

名称

初始值

说明

NO_ERROR

0

无错误

FIRST_ERROR

5100

错误枚举定义的起始编号

TIME_OUT

5101

超过时间限制

ABORT

5102

xAbort信号激活导致操作终止

HANDLE_INVALID

5103

无效文件句柄

NOT_EXIST

5104

文件或目录不存在

EXIST

5105

文件或目录已经存在

NO_MORE_ENTRIES

5106

没有其他条目可用

NOT_EMPTY

5107

文件或目录不为空

READ_ONLY_CAA

5108

文件或目录写保护

WRONG_PARAMETER

5109

参数错误

ERROR_UNKNOWN

5110

未知错误

WRITE_INCOMPLETE

5111

数据写入不完整

FILE_NOT_IMPLEMENTED

5112

功能未实现

ASM_CREATEJOB_FAILED

5113

AsyncManager的作业创建失败

FILE_OPERATION_DENIED

5114

ForceFilePath/ForceIecFilePath无法访问(这个错误我也不知道是什么意思…)

FIRST_MF

5150

制造商错误定义的起始编号

LAST_ERROR

5199

错误枚举定义的编号上限

说实话,上表中定义的很多错误我用了这么久也没碰到过,常见的应该是标粗的那几个。

另外需要注意的是,以上几个枚举定义都需要通过全局变量名“FILE.xxx”来访问,比如只读文件属性要写为FILE.ATTRIB.READONLY,否则编译时会报标识未定义错误。

2.FILE_DIR_ENTRY结构

保存目录条目或文件的信息。

sEntry:CAA.FILENAME,文件或目录名。

szSize:CAA.SIZE,文件大小。

xDirectory:TRUE为目录, FALSE为文件。

xExclusive:文件访问模式,TRUE为独占访问模式,FALSE为多个实例可以同时访问。

dtLastModification:上次修改的日期和时间,日期时间格式为2023-01-17-11:13:00

注意:使用本结构需要通过FILE.FILE_DIR_ENTRY实现。

3.目录操作功能块

目录操作功能块:

功能块名称

功能

备注

DirOpen

打开目录

DirClose

关闭目录

DirCreate

创建目录

DirList

读取目录条目

DirCopy

拷贝目录

DirRemove

删除目录

依赖于操作系统和文件系统

DirRename

重命名目录

4.文件操作功能块

文件操作功能块如下表所示:

功能块名称

功能

备注

Open

打开文件

Read

读取文件内容

Write

内容写入文件

Flush

将缓冲写入文件

Close

关闭文件

Copy

复制文件

Rename

重命名文件

Delete

删除文件

已打开的文件也可删除,依赖于操作系统和文件系统

EOF

检查是否到达文件结尾

GetAttribute

获取文件属性

GetPos

返回文件访问的当前偏移位置

文件必须通过file.Open打开

SetPos

设置文件访问的当前偏移量

文件必须通过file.Open打开

GetSize

返回文件大小

GetTime

返回上次修改的日期和时间

5.功能块主要参数

由于各个功能块的参数和操作模式基本类似,各个功能块的大部分参数都是类似的,这里就不针对每个功能块的参数一一说明。

xExecute:输入,上升沿开始执行,下降沿复位输出。如果在功能块完成其动作之前出现下降沿,则输出以通常的方式操作,并且仅在动作完成或发生错误时复位。在这种情况下,对应的输出值(xDone,xError)在输出端只持续一个周期。

xAbort:输入,TRUE则立即停止操作,并将所有输出置为初始值。

sDirName:输入,待操作目录名称。

sFileName:输入,待操作的文件名称。

eFileMode:输入,文件操作模式,由FILE.MODE定义。

udiTimeOut:输入,定义功能块因超时而中止操作并输出错误消息的时间,单位µs。

hDir:待操作的目录句柄。

hFile:待操作的文件句柄。

pBuffer:读取或写入数据缓冲区的首地址,通过ADR获取。

szBuffer:要读取的字节数。

xOverWrite:输入,TRUE为覆盖已存在的文件或目录,FALSE为报错。

xDone:输出,TRUE为操作成功。

xAborted:输出,TRUE为操作被用户中止。

xEOF:输出,TRUE为达到文件结尾。

xBusy:输出,TRUE为功能块正在执行中。

xError:输出,TRUE为发生错误,功能块终止运行;FALSE为无错误。

eError:输出,错误ID,由ERROR定义。

eFileAttrib:输出,文件属性,由FILE.ATTRIB定义。

uidPos:输出,文件指针偏移位置(相对于文件开头的字节数)。

szSize:输出,文件实际大小,单位为字节。

dtLastModification:上次修改的日期和时间,格式为2023-02-03-16:23:00

三、使用示例

这里需要注意的是,在早期版本的CODESYS官方示例中,CAA.HANDLE、CAA.FILENAME、CAA.SIZE等变量是以CAA_HANDLE、CAA_FILENAME、CAA_SIZE的形式出现的,具体从哪个库版本开始改的,我也记不得了,总之改过来以后使用新版本的库就不会报错了~~。

1.目录操作使用示例

以下为目录操作的示例,其功能是在控制器指定目录下建立新目录,然后对目录进行打开、获取目录属性列表、关闭、拷贝、重命名和删除操作。需要注意的是这些操作需要在实际的控制器上才能执行,仿真模式下会报5113号错误。本次测试使用的控制器是禾川的Q0,使用其它控制器时需要正确指定可进行读写操作的目录位置。

程序变量定义如下:

PROGRAM testDir

VAR

       xDirInit:       BOOL := FALSE;

    uiDirState:     UINT := 0;

    sDirNewName:    CAA.FILENAME:='$$flashfiles$$\TestDirectory';

    sDirNextName:   CAA.FILENAME:='$$flashfiles$$\NewDirectory';

    hDir:         CAA.HANDLE;

    deNewDirectory: FILE.FILE_DIR_ENTRY;

       eError:                  FILE.ERROR;

    fDirCreate:     FILE.DirCreate;

    fDirOpen:       FILE.DirOpen;

    fDirClose:      FILE.DirClose;

    fDirList:       FILE.DirList;

       fDirCopy:       FILE.DirCopy;

    fDirRename:     FILE.DirRename;

    fDirRm:         FILE.DirRemove;

END_VAR

程序如下:

IF NOT xDirInit THEN

    fDirCreate(xExecute:=FALSE);

    fDirClose(xExecute:=FALSE);

    fDirList(xExecute:=FALSE);

    fDirRm(xExecute:=FALSE);

    xDirInit:=TRUE;

    uiDirState:=0;

ELSE

    CASE uiDirState OF

    0: (* 创建新目录 *)

        fDirCreate.sDirName:=sDirNewName;

        fDirCreate.xParent:=FALSE;

        fDirCreate(xExecute:=TRUE);

        IF fDirCreate.xDone THEN

            uiDirState:=1;

        END_IF

        IF fDirCreate.xError THEN (* 错误处理*)

                     eError:=fDirCreate.eError;

            ;

        END_IF

    1: (* 打开目录 *)

        fDirOpen.sDirName:=sDirNewName;

        fDirOpen(xExecute:=TRUE);

        IF fDirOpen.xDone THEN

            hDir := fDirOpen.hDir;

            uiDirState:=2;

        END_IF

        IF fDirOpen.xError THEN (* 错误处理 *)

                     eError:=fDirOpen.eError;

            ;

        END_IF

    2: (* 获取目录属性列表 *)

        fDirList.hDir:=hDir;

        fDirList(xExecute:=TRUE);

        IF fDirList.xDone THEN

            deNewDirectory.sEntry :=fDirList.deDirEntry.sEntry;

            deNewDirectory.szSize :=fDirList.deDirEntry.szSize;

            deNewDirectory.xDirectory :=fDirList.deDirEntry.xDirectory;

            deNewDirectory.xExclusive :=fDirList.deDirEntry.xExclusive;

            deNewDirectory.dtLastModification :=fDirList.deDirEntry.dtLastModification;

            uiDirState:=3;

        END_IF

        IF fDirOpen.xError THEN (* 错误处理 *)

                     eError:=fDirList.eError;

            ;

        END_IF

    3: (* 关闭目录 *)

        fDirClose.hDir:=hDir;

        fDirClose(xExecute:=TRUE);

        IF fDirClose.xDone THEN

            uiDirState:=4;

        END_IF

        IF fDirClose.xError THEN (* 错误处理 *)

                     eError:=fDirClose.eError;

            ;

        END_IF

       4: (* 目录拷贝 *)

        fDirCopy.sDirNameSource:=sDirNewName;

        fDirCopy.sDirNameDest:='$$flashfiles$$\TestDirectory1';

        fDirCopy(xExecute:=TRUE);

        IF fDirCopy.xDone THEN

            uiDirState:=5;

        END_IF

        IF fDirCopy.xError THEN (* 错误处理 *)

                     eError:=fDirCopy.eError;

            ;

        END_IF

    5: (* 目录重命名 *)

        fDirRename.sDirNameOld:=sDirNewName;

        fDirRename.sDirNameNew:=sDirNextName;

        fDirRename(xExecute:=TRUE);

        IF fDirRename.xDone THEN

            uiDirState:=6;

        END_IF

        IF fDirRename.xError THEN (* 错误处理 *)

                     eError:=fDirRename.eError;

            ;

        END_IF

    6: (* 删除目录 *)

        fDirRm.sDirName:=sDirNextName;

        fDirRm.udiTimeOut:=100000;      (* 超时时间 100ms *)

        fDirRm.xRecursive:=FALSE;

        fDirRm(xExecute:=TRUE);

        IF fDirRm.xDone THEN

            uiDirState:=7;

        END_IF

        IF fDirRm.xError THEN (* 错误处理 *)

                     eError:=fDirRm.eError;

            ;

        END_IF

    7: (* 示例结束 *)

        ;

    END_CASE

END_IF

2.文件操作使用示例

以下为文件操作的示例,其功能是在控制器指定目录下建立新文件并将指定文本内容写入文件,然后进行读取、关闭、拷贝、重命名和删除文件操作。需要注意的是这些操作需要在实际的控制器上才能执行,仿真模式下会报错。本次测试使用的控制器是禾川的Q0,使用其它控制器时需要正确指定可进行读写操作的目录位置。

程序变量定义如下:

PROGRAM testFile

VAR

       xFileStdInit:   BOOL:=FALSE;

    uiFileStdState: UINT:=0;

    sFileName:        CAA.FILENAME:= 'TestFile.txt';

    hFile:          CAA.HANDLE;

    sFileTestString:STRING:='Hello 2023!';

    sFileString:    STRING:='';

    szFileSize1:    CAA.SIZE := 0;

    szFileSize2:    CAA.SIZE := 0;

    sFileNewName:   CAA.FILENAME:= 'NewFile.txt';

    szCopiedFileSize:   CAA_SIZE := 0;

       eError:                  FILE.ERROR;

    fOpen:          FILE.Open;

    fWrite:         FILE.Write;

    fRead:          FILE.Read;

    fClose:         FILE.Close;

       fCopy:          FILE.Copy;

    fRename:        FILE.Rename;

    fDel:           FILE.Delete;

END_VAR

程序如下:

IF NOT xFileStdInit THEN

    fOpen(xExecute:=FALSE);

    fClose(xExecute:=FALSE);

    fWrite(xExecute:=FALSE);

    fRead(xExecute:=FALSE);

    fDel(xExecute:=FALSE);

    fRename(xExecute:=FALSE);

    fCopy(xExecute:=FALSE);

    xFileStdInit:=TRUE;

    uiFileStdState:=0;

ELSE

    CASE uiFileStdState OF

    0: (* 创建新文件 *)

        fOpen.sFileName:=sFileName;

        fOpen.eFileMode:=FILE.MODE.MRDWR;

        fOpen.xExclusive:=TRUE;

        fOpen(xExecute:=TRUE);

        IF fOpen.xDone THEN

            hFile:=fOpen.hFile;

            uiFileStdState:=1;

        END_IF

        IF fOpen.xError THEN (* 错误处理 *)

                     eError:=fOpen.eError; //错误号

            ;

        END_IF

    1:(* 文本内容写入文件 *)

        fWrite.hFile:=hFile;

        fWrite.pBuffer:=ADR(sFileTestString);

        szFileSize1:=SIZEOF(sFileTestString);

        fWrite.szSize:=szFileSize1;

        fWrite.udiTimeOut:=100000;       (* 100ms Timeout *)

        fWrite(xExecute:=TRUE);

        IF fWrite.xDone THEN

            uiFileStdState:=2;

        END_IF

        IF fWrite.xError THEN (* 错误处理 *)

                     eError:=fWrite.eError;

            ;

        END_IF

    2:(* 读取文件 - TestFile.txt*)

        fRead.hFile:=hFile;

        fRead.udiTimeOut:=100000;       (* 超时时间 100ms *)

        fRead.pBuffer:=ADR(sFileString);

        fRead.szBuffer:=255;

        fRead(xExecute:=TRUE);

        IF fRead.xDone THEN

            szFileSize2:=fRead.szSize;

            IF szFileSize2 = szFileSize1 THEN

                uiFileStdState:=3;

            ELSE (* 错误处理 *)

                            eError:=fRead.eError;

                ;

            END_IF

        END_IF

        IF fRead.xError THEN (* 错误处理 *)

                     eError:=fRead.eError;

            ;

        END_IF

    3:  (* 关闭文件  - TestFile.txt *)

        fClose.hFile:=hFile;

        fClose(xExecute:=TRUE);

        IF fClose.xDone THEN

            uiFileStdState:=4;

        END_IF

        IF fClose.xError THEN (* 错误处理 *)

                     eError:=fClose.eError;

            ;

        END_IF

    4:(* 拷贝 *)

        fCopy.sFileNameSource:=sFileName;

        fCopy.sFileNameDest:='DestFile.txt';

        fCopy.udiTimeOut:=100000;       (* 超时时间 100ms     *)

        fCopy.xOverWrite:=TRUE;         (* 覆盖已有文件 *)

        fCopy( xExecute:=TRUE);

        IF fCopy.xDone THEN

            szCopiedFileSize := fCopy.szSize;

            uiFileStdState:=5;

        END_IF

        IF fCopy.xError THEN (* 错误处理 *)

                     eError:=fCopy.eError;

            ;

        END_IF

    5: (* 文件重命名 *)

        fRename.sFileNameOld:='DestFile.txt';

        fRename.sFileNameNew:=sFileNewName;

        fRename( xExecute:=TRUE);

        IF fRename.xDone THEN

            uiFileStdState:=6;

        END_IF

        IF fRename.xError THEN (* 错误处理 *)

                     eError:=fRename.eError;

            ;

        END_IF

    6:(* 删除文件 *)

        fDel.sFileName:=sFileNewName;

        fDel( xExecute:=TRUE);

        IF fDel.xDone THEN

            uiFileStdState:=7;

        END_IF

        IF fDel.xError THEN (* 错误处理 *)

                     eError:=fDel.eError;

            ;

        END_IF

    7:  (* end of example *)

            ;

    END_CASE

END_IF

从上面的示例可以看出,文件操作实际上是通过状态机的方式进行的,即打开、读取或写入、属性获取、关闭等操作都是每次执行一步,一个操作执行完成后转到下一状态。这个流程在CODESYS中读写文件是比较推荐的,可以避免因某一步骤操作时间过长导致的任务超时。

四、结论

CAA File库的使用其实并不复杂,新手只要弄清楚Open、Read、Write、Close等几个主要功能块的用法,照着上面的示例改一下基本上能够解决大部分的问题。这篇文章本来打算连SysFile库一锅烩的,写着写着发现实在是太长了,还是留着下次再写吧^-^~~

另外说明一下,本文的示例是在CODESYS 3.5.17版本上测试的,如果是比较老的版本上(比如3.5.10)上可能无法正常运行。实际上CODESYS的库也是在不停的改来改去的,新手建议用最新的版本。考古的同学需要自己去翻文档了(这里要吐槽一下,CODESYS在线帮助里面有很多示例代码使用的类型定义也是在远古版本的库里面,放到现在的新版本里面会直接报错……)。

------------------

原创不易,感兴趣的多支持

 

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值