蓝色港湾 发表于 2023-4-12 22:57:22

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

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

前面一期我们介绍了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功能块获取的文件属性值。



(2)文件访问模式MODE

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



(3)错误码定义ERROR

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



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

另外需要注意的是,以上几个枚举定义都需要通过全局变量名“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.目录操作功能块

目录操作功能块:



4.文件操作功能块

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



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在线帮助里面有很多示例代码使用的类型定义也是在远古版本的库里面,放到现在的新版本里面会直接报错……)。

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

原创不易,感兴趣的多支持!
————————————————
版权声明:本文为CSDN博主「--莫名--」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/halps/article/details/128873269
页: [1]
查看完整版本: CODESYS开发教程9-文件读写(CAA File库)