今回はCodesysのCAA.Fileを使ってCSVを書き込む関数やプログラム例を書きます。確かにCodesysにはIOT LibraryにCSVの関数がありますが、有料らしいので、とりあえずFile.Openなど一番シンプルの方法で実装したいと思います。
ライブラリ追加
Application>Library Managerをクリックします。
Add Libraryします。
Advancedをクリックします。
Fileを絵r倍、File Access を追加します。
Function Block
CAA.Fileはたくさんの関数があり、今回はサンプルプログラムで使用したFile.Open・File.Write・File.Closeのみに説明します。
FILE.Open
このFunction Blockは既存にあるFileを開くや新規File作成できます。戻り値はhFileというObjectで、このObjectを使ってFile.Read・File.Write・File.CloseにあるhFileのInputパラメタにとして使います。
VAR_INPUT
xExecute | BOOL | 立ち上げ=Startたち下げ=Stop |
sFileName | CAA.FILENAME | File名やFileのPath |
eFileMode | File mode | Fileを開くのModeになります。Write・Read・Appendなど |
xExclusive | BOOL | 使用不可、Always Falseしてください |
VAR_OUTPUT
xDone | BOOL | 1=正常実行した |
xBusy | BOOL | 1=実行中 |
xError | BOOL | 1=エラーあり |
eError | ERROR | BlockのエラーID |
hFile | CAA.HANDLE | File ID、Write・Readなどで使われてる |
File.Write
このFunction BlockはFile.OpenからきたFile Object hFile経由でFileに文字列を書き込むことができます。メモリPointerのpBufferは文字列を書き込む中はPointer値を変えるのは禁止で、そして実際何Byte書き込まれたのかFBからCheckしません。あと、書き込む中Online Changeはできるだけ避けてください。
VAR_INPUT
xExecute | BOOL | 立ち上げ=Startたち下げ=Stop |
xAbort | BOOL | 1=FB実行停止し、Outputは初期状態に戻る |
udiTimeOut | UDINT | 単位はµsでFBの実行TimeOut時間設定 |
xExclusive | BOOL | 使用不可、Always Falseしてください |
hFile | CAA.HANDLE | File.OpenからのFile Handle |
pBuffer | CAA.PVOID | 書き込むの文字列のメモリアドレス(ADRからもらえる) |
szSize | CAA.SIZE | 書き込むの文字列Byte数(SizeOFからもらえる) |
VAR_OUTPUT
xDone | BOOL | 1=正常実行した |
xBusy | BOOL | 1=実行中 |
xError | BOOL | 1=エラーあり |
eError | ERROR | BlockのエラーID |
File.Close
This function block terminates the file access, i.e. closes the file.
VAR_INPUT
xExecute | BOOL | 立ち上げ=Startたち下げ=Stop |
xAbort | BOOL | 1=FB実行停止し、Outputは初期状態に戻る |
udiTimeOut | UDINT | 単位はµsでFBの実行TimeOut時間設定 |
xExclusive | BOOL | 使用不可、Always Falseしてください |
hFile | CAA.HANDLE | File.OpenからのFile Handle |
VAR_OUTPUT
xDone | BOOL | 1=正常実行した |
xBusy | BOOL | 1=実行中 |
xError | BOOL | 1=エラーあり |
eError | ERROR | BlockのエラーID |
流れ
まず関数を初期化し、書き込むのコマンドを待ちます。
書き込むのコマンド来たらFileが存在してるかどうかをCheckします。あればそのままデータを書き込みますし、なければFileを作成しCSVのHeaderを書いてからデータを書きます。
最後はFileをCloseします。
もちろんFile.Openなどを呼び出すときあればError Stepに飛び、Resetくるまで待ちます。
実装
VAR
VAR //File Objects fopen:FILE.Open; fclose:FILE.Close; fwrite:FILE.Write; hFile: CAA.HANDLE; //Path,Header,Text that you want to Write //$R$N is break-line in Windows OS sFileName:STRING:=’C:\data\file.txt’; sFileHeader:STRING(80):=’Date,data1,data2,data3,data4,data5$R$N’; sFileText:STRING(80):=’20210102,1,2,3,String,1.24$R$N’; //Operation Flag bWrite:BOOL; bReopen:BOOL; bError:BOOL; bReset:BOOL; iState:INT:=0; iError:FILE.ERROR; iLastStep:INT; END_VAR |
VAR CONSTANT
VAR CONSTANT ciStepInit :INT:=0; ciStepWaitCmd :INT:=5; ciStepCheckPathAndOpen :INT:=10; ciStepCreateNew :INT:=30; ciStepAppend :INT:=35; ciStepWriteHeader :INT:=40; ciStepWriteLine :INT:=45; ciStepClose :INT:=60; ciStepError :INT:=990; END_VAR |
Code
CASE iState OF ciStepInit: //Reset all the Instance fopen( xExecute:=FALSE ); fclose( xExecute:=FALSE ); fwrite( xExecute:=FALSE ); bReset:=FALSE; bReopen:=FALSE; IF NOT fopen.xBusy AND NOT fclose.xBusy AND NOT fwrite.xBusy THEN iState:=ciStepWaitCmd; END_IF // ciStepWaitCmd: IF bWrite THEN IF NOT fopen.xBusy AND NOT fclose.xBusy AND NOT fwrite.xBusy THEN iState:=ciStepCheckPathAndOpen; END_IF bWrite:=FALSE; END_IF; // ciStepCheckPathAndOpen: //FILE.MODE.MREADPLUS=OPEN Files and Return Error if following path is not Exist fopen( sFileName:=sFileName ,eFileMode:=FILE.MODE.MREADPLUS ,xExecute:=TRUE ); IF fopen.xDone AND NOT fopen.xError THEN hFile:=fopen.hFile; fopen( xExecute:=FALSE ); //If path is OK, close the file 1 times and re-open in append mode. bReopen:=TRUE; iState:=ciStepClose; ELSIF fopen.xError THEN //if Current path is not exist //FILE.ERROR.NOT_EXIST IF fopen.eError =FILE.ERROR.NOT_EXIST THEN iState:=ciStepCreateNew; fopen( xExecute:=FALSE ); ELSE iError:=fopen.eError; iLastStep:=iState; iState:=ciStepError; END_IF END_IF //open the file in append mode ciStepAppend: bReopen:=False; fopen( sFileName:=sFileName ,eFileMode:=FILE.MODE.MAPPD ,xExecute:=TRUE ); IF fopen.xDone AND NOT fopen.xError THEN hFile:=fopen.hFile; fopen( xExecute:=FALSE ); iState:=ciStepWriteLine; ELSIF fopen.xError THEN iLastStep:=iState; iState:=ciStepError; iError:=fopen.eError; END_IF; //Case using if check path is fail. ciStepCreateNew : //FILE.MODE.MREADPLUS=OPEN Files and Create new one if following path is not Exist fopen( sFileName:=sFileName ,eFileMode:=FILE.MODE.MAPPENDPLUS ,xExecute:=TRUE ); IF fopen.xDone AND NOT fopen.xError THEN hFile:=fopen.hFile; fopen( xExecute:=FALSE ); iState:=ciStepWriteHeader; ELSIF fopen.xError THEN iLastStep:=iState; iState:=ciStepError; iError:=fopen.eError; END_IF; //CSV FIles – Need the a,c,v,c,a,d.. Header ciStepWriteHeader: fwrite( xExecute:=TRUE ,hFile:=hFile ,pBuffer:=ADR(sFileHeader) ,szSize:=LEN(sFileHeader) ); IF fwrite.xDone AND NOT fwrite.xError THEN fwrite( xExecute:=FALSE ); iState:=ciStepAppend; ELSIF fwrite.xError THEN iLastStep:=iState; iState:=ciStepError; iError:=fwrite.eError; END_IF; // ciStepWriteLine: fwrite( xExecute:=TRUE ,hFile:=hFile ,pBuffer:=ADR(sFileText) ,szSize:=LEN(sFileText) ); IF fwrite.xDone AND NOT fwrite.xError THEN fwrite( xExecute:=FALSE ); iState:=ciStepClose; ELSIF fwrite.xError THEN iLastStep:=iState; iState:=ciStepError; iError:=fwrite.eError; END_IF; ciStepClose: fclose( xExecute:=TRUE ,hFile:=hFile ); IF fclose.xDone THEN fclose( xExecute:=FALSE ); IF bReopen THEN //Logic if File path is exist,need to re-open in append mode iState:=ciStepAppend; ELSE iState:=ciStepWaitCmd; END_IF ELSIF fwrite.xError THEN iLastStep:=iState; iState:=ciStepError; iError:=fclose.eError; END_IF ciStepError: IF bReset THEN iState:=ciStepInit; END_IF; END_CASE |
結果
このようなFileができます。
CSViewerから見ますとちゃんとCSV Formatになっていますね。
最後
Sample Codeは以下のLinkでダウンロードしてください。
https://github.com/soup01Threes/Codesys/blob/main/codesys_writecsv.projectarchive
はーい、お疲れ様です。
もしなにか質問あれば、メール・コメント・Twitterなどでもどうぞ!
Twitterのご相談:@3threes2
メールのご相談:soup01threes*gmail.com (*を@に)
コメント
こんにちは
CODESYS初心者です。
アップされているコードをそのままコピペして動かしてみました。
正しいパスを指定して実行すると、iStateはシーケンス通り0-5-10-60-35-45-60と進んでいるんですが、ファイルが出力されません。
存在しないファイル名で実行してもiStateが30に入らず上記の順番で処理されてしまいました。
なにか原因わかりますでしょうか。
確認しまので少々おまちください。
返事はそのままでいいですか?メールでよろしいですか??
ありがとうございます。お願いします。
返事はそのままでもメールでも大丈夫です。
Yusei Aoiさま
ご返事ありがとうございます。先ほど記事の最後にあるGitHubのプロジェクトをダンロードし、確認しました。
Fileは無事に作成され、中ににも書き込まれました。
おそらくですが、
1-Pathの設定、sFileNameはVARの初期値だけで与えられましたので、
sFileName:STRING:=’C:\data\file.txt’;
その値をVAR宣言エリアを変えても反映はしないと思います。
一回Resetする必要がありますね。
2-私のプログラムにPathをCheckするStepは不足のところがあるかもしれません。
ciStepCheckPathAndOpenのStepはPathをCheckし>ciStepCreateNewに戻るような流れです。
その中に作成するのはFileだけです。Folderまで作成しないので、多分そこも原因の一つではないかなと思います。
自分もできるだけSampleコードをUploadする前に確認・エラー処理をするつもりですが、抜けが出てきて申し訳ございません。
Blogみていただきありがとうございます。
あなたのコメントは私が続くの力になります。
よろしくおねがいします。
かなり月日が経ってしましましたが当時無事解決できました。
ご親切にありがとうございました。