We are seeing an issue with our use of file open , create, close file function blocks. We are using a file open and create funtion blocks to create a printer file and we the close the file. The file is then sent to the printer with the ftp function blocks. Everything works fine and then after about 30 min of running the plc locks up. The only way to reset the plc is to cycle power. It is like the file is not closing correctly and we a causing a memory issue or something. Any ideas of what might be happening or solutions are appreciated.
Hi,
what exactly happens when the PLC locks up?
Does it a service reboot, is there any information inside the logger (like page fault, watchdog…) - if yes, can you please share the logger?
Or does only the files functions work no longer until reboot, but everything else keeps running - if yes, can you have a look into the .status variables of the FileIO function blocks for error codes, and share them?
In general when working with file operations on network devices, I recommend always to create, write, read, close the file on the local storage (user partition), and only do copy operations to the network device by: create the device link (DevLink), copy the file (FileCopy), and close the device link (DevUnlink) after the copy operation has finished (sure, maybe in some special usecases it’s not possible to do it in that way, but in most application it is, from my experience).
Doing so can prevent to come in some strange situations when the network communication is always open but is temporary interrupted / disturbed, for example because of a reboot of the communication partners interface or so.
Best regards!
Hello Alexander,
The PLC remains running but the file functions seem lock up and do not seem to clear until the power is cycled to the PLC (the only way I have been able to clear it). The status of the file open function is stuck with 20702. I am thinking that the files are not closing correctly and we are eventually filling a memory buffer or something. We will do some more testing today. The way we have our sequence is to first check if the file exists with a file open command, if it does then we delete it. Then we create a file, write our data to it, then close the file. Then the devlnk, file copy, devunlnk, then delete the file.
This sequence is running even 25 sec or so. And we only see the error after about 35-40 min of running.
Do you also close the file before deleting it as you are using FileOpen to check if the file is there? The system has a limited amount of File handles (FileIdents), and the error 20702 is trying to tell you that the system has run out of them.
Since only a limited amount of file handles are available, it is necessary to call FileClose after each FileOpen or FileCreate.
Hi @dfaustino ,
I agree with Tommi, there’s a limited number of files that can be open.
Error 20702 points in that direction, also as the time the functions is running (sounds like around 100 open file handles which could be the upper limit):
FileOpen() and also FileCreate() consumes one handle each if call was successful, so from both calls the handles has to be freed after the work is done by FileClose(), I assume in some cases this is not done right now.
Instead of using FileOpen() to check if a file exists, you could use FileInfo(), where no explicit file handle is needed (like with FileCopy), this function block throws .status = fiERR_FILE_NOT_FOUND (20708) if file does not exist, otherwise .status = 0.
Best regards!
I will try it with the file info FB. Is there are way to see how many handles are opened?
As I know, unfortunately no.
Best regards!
I have changed the sequence to do the following and am still getting too many files open:
Check to see if file exists : FileInfo
If no - Create File - FileCreate , Open File - FileOpen, Write Data to File - FileWrite, Close File - FileClose, Open FTP - Devlink , Copy File - FileCopy, Close FTP - DevUnlink, Close File (to make sure) FileClose, File Delete - FileDelete
If yes - Delete the file then run sequence above.
Is there another way to close all files? Or any other ideas on how I can prevent this?
Thanks
(*********************************************************************************************)
StepZPLFileFileCheckExisting: (* Check to see if File already exists *)
(***********************************************************************************************)
ZPLCreator[1].FileCreate.FB.FileInfo_0.enable := TRUE;
ZPLCreator[1].FileCreate.FB.FileInfo_0.pDevice := ADR(ZPLCreator[1].FileCreate.Data.Device); (* name of the linked device *)
ZPLCreator[1].FileCreate.FB.FileInfo_0.pName := ADR(ZPLCreator[1].FileCreate.Data.FileName); (* name of the file *)
ZPLCreator[1].FileCreate.FB.FileInfo_0.pInfo := ADR(infostruct); (* name of the file *)
ZPLCreator[1].FileCreate.FB.FileInfo_0();
IF (ZPLCreator[1].FileCreate.FB.FileInfo_0.status = 20708) THEN (* File Doesn't Exist Create File*)
ZPLCreator[1].FileCreate.FB.FileInfo_0.enable := FALSE;
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileCreate;
ELSIF (ZPLCreator[1].FileCreate.FB.FileInfo_0.status = 0) THEN (* File Already Exists Write Data to File*)
ZPLCreator[1].FileCreate.FB.FileInfo_0.enable := FALSE;
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileDelete0;
ELSE (* Goto Error Step *)
// ZPLCreator[1].FileCreate.Data.Step := StepZPLFileError;
END_IF
(*********************************************************************************************)
StepZPLFileDelete0: (* Verify file is Deleted *)
(***********************************************************************************************)
ZPLCreator[1].FileCreate.FB.ZPLFileDelete0.enable := TRUE;
ZPLCreator[1].FileCreate.FB.ZPLFileDelete0.pDevice := ADR(ZPLCreator[1].FileCreate.Data.Device);
ZPLCreator[1].FileCreate.FB.ZPLFileDelete0.pName := ADR(ZPLCreator[1].FileCreate.Data.FileName);
ZPLCreator[1].FileCreate.FB.ZPLFileDelete0();
//IF (ZPLCreator[1].FileCreate.FB.ZPLFileDelete0.status = 0) THEN
ZPLCreator[1].FileCreate.FB.ZPLFileDelete0.enable := FALSE;
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileCreate; (* next Step*)
//ELSE (* Goto Error Step *)
// ZPLCreator[1].FileCreate.Data.Step := StepZPLFileError;
// END_IF
(***********************************************************************************************)
StepZPLFileCreate: (* create a new File with the selected name *)
(***********************************************************************************************)
IF NOT ZPLCreator[1].FileCreate.FB.FileCreate_0.enable THEN
strcpy(ADR(ZPLCreator[1].FileCreate.Data.FileName), ADR(ZPLCreator[1].FileCreate.Data.FileName));
END_IF
ZPLCreator[1].FileCreate.FB.FileCreate_0.enable := TRUE;
ZPLCreator[1].FileCreate.FB.FileCreate_0.pDevice := ADR(ZPLCreator[1].FileCreate.Data.Device); (* name of the linked device *)
ZPLCreator[1].FileCreate.FB.FileCreate_0.pFile := ADR(ZPLCreator[1].FileCreate.Data.FileName); (* name of the file *)
ZPLCreator[1].FileCreate.FB.FileCreate_0(); (* call the function*)
IF (ZPLCreator[1].FileCreate.FB.FileCreate_0.status = 0) THEN (* FileCreate successful *)
ZPLCreator[1].FileCreate.FB.FileCreate_0.enable := FALSE;
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileOpen; (* next Step*)
ELSIF (ZPLCreator[1].FileCreate.FB.FileCreate_0.status = ERR_FUB_BUSY) THEN (* FileCreate not finished -> redo *)
(* Busy *)
ELSE (* Goto Error Step *)
// ZPLCreator[1].FileCreate.Data.Step := StepZPLFileError;
END_IF
(***********************************************************************************************)
StepZPLFileOpen: (* open file for write access *)
(***********************************************************************************************)
ZPLCreator[1].FileCreate.FB.FileOpen_0.enable := TRUE;
ZPLCreator[1].FileCreate.FB.FileOpen_0.pDevice := ADR(ZPLCreator[1].FileCreate.Data.Device); (* name of the linked device *)
ZPLCreator[1].FileCreate.FB.FileOpen_0.pFile := ADR(ZPLCreator[1].FileCreate.Data.FileName); (* name of the file *)
ZPLCreator[1].FileCreate.FB.FileOpen_0.mode := fiWRITE_ONLY; (* write access *)
ZPLCreator[1].FileCreate.FB.FileOpen_0(); (* call the function*)
IF (ZPLCreator[1].FileCreate.FB.FileOpen_0.status = 0) THEN (* FileOpen successful *)
ZPLCreator[1].FileCreate.FB.FileOpen_0.enable := FALSE;
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileWrite; (* next Step*)
ELSIF (ZPLCreator[1].FileCreate.FB.FileOpen_0.status = ERR_FUB_BUSY) THEN (* FileOpen not finished -> redo *)
(* Busy *)
ELSE (* Goto Error Step *)
// ZPLCreator[1].FileCreate.Data.Step := StepZPLFileError;
END_IF
(***********************************************************************************************)
StepZPLFileWrite: (* write data into file *)
(***********************************************************************************************)
ZPLCreator[1].FileCreate.FB.FileWrite_0.enable := TRUE;
ZPLCreator[1].FileCreate.FB.FileWrite_0.ident := ZPLCreator[1].FileCreate.FB.FileOpen_0.ident;
ZPLCreator[1].FileCreate.FB.FileWrite_0.offset := 0; (* start at the beginning of the file *)
ZPLCreator[1].FileCreate.FB.FileWrite_0.pSrc := ADR(ZPLCreator[1].FileCreate.Data.WriteData);
ZPLCreator[1].FileCreate.FB.FileWrite_0.len := SIZEOF(gLabelPrinter_IFace[1].PrintLabelData);
ZPLCreator[1].FileCreate.FB.FileWrite_0(); (* call the function*)
IF (ZPLCreator[1].FileCreate.FB.FileWrite_0.status = 0) THEN (* FileWrite successful *)
ZPLCreator[1].FileCreate.FB.FileWrite_0.enable := FALSE;
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileClose2; (* next Step*)
ELSIF (ZPLCreator[1].FileCreate.FB.FileWrite_0.status = ERR_FUB_BUSY) THEN (* FileWrite not finished -> redo *)
(* Busy *)
ELSE (* Goto Error Step *)
// ZPLCreator[1].FileCreate.Data.Step := StepZPLFileError;
END_IF
(***********************************************************************************************)
StepZPLFileClose2: (* close file, because of limited number of available file handles on the system *)
(***********************************************************************************************)
ZPLCreator[1].FileCreate.FB.FileClose_2.enable := TRUE;
ZPLCreator[1].FileCreate.FB.FileClose_2.ident := ZPLCreator[1].FileCreate.FB.FileOpen_0.ident; (* ident for FileCreate-functionblock*)
ZPLCreator[1].FileCreate.FB.FileClose_2(); (* call the function*)
FileCrTimStrt[0] := 1; (* Start Timer *)
IF TON_FileCrDelay[0].Q = 1 THEN (* File Closed *)
IF (ZPLCreator[1].FileCreate.FB.FileClose_2.status = 0) AND NOT (ZPLCreator[1].FileCreate.FB.FileClose_2.status = 65535) THEN
ZPLCreator[1].FileCreate.FB.FileClose_2.enable := FALSE;
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileDeviceLink; (* next Step*)
ELSE (* Goto Error Step *)
// ZPLCreator[1].FileCreate.Data.Step := StepZPLFileError;
END_IF
END_IF
(***********************************************************************************************)
StepZPLFileDeviceLink: (* Connect to Printer via FTP *)
(***********************************************************************************************)
Printer.DevParam := '/SIP=10.10.0.214 /PROTOCOL=ftp /USER=ftpprint /PASSWORD=print';
Printer.DestDevice := 'cab-0b3254';
DevLink_Pr, Copy Fileinter.enable := TRUE;
DevLink_Printer.pDevice := ADR(Printer.DestDevice);
DevLink_Printer.pParam := ADR(Printer.DevParam);
DevLink_Printer();
CAB_Printer.LNK_Status := DevLink_Printer.status;
CAB_Printer.Handle := DevLink_Printer.handle;
FileCrTimStrt[2] := 1; (* Start Timer *)
IF TON_FileCrDelay[2].Q = 1 THEN (* Verify Communications opened *)
FileCrTimStrt[2] := 0; (* Reset Timer *)
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileCopy;
END_IF
(***********************************************************************************************)
StepZPLFileCopy: (* Copy File to Printer*)
(***********************************************************************************************)
Printer_File_Copy.enable := TRUE;
Printer_File_Copy.pSrcDev := ADR(ZPLCreator[1].FileCreate.Data.Device);
Printer_File_Copy.pSrc := ADR(ZPLCreator[1].FileCreate.Data.FileName);
Printer_File_Copy.pDestDev := ADR (Printer.DestDevice);
Printer_File_Copy.pDest := ADR(ZPLCreator[1].FileCreate.Data.FileName);
Printer_File_Copy.option := fiOVERWRITE;
(* Call FBK *)
Printer_File_Copy();
FileCrTimStrt[3] := 1; (* Start Timer *)
IF TON_FileCrDelay[3].Q = 1 THEN (* Verify File Copied *)
FileCrTimStrt[3] := 0; (* Reset Timer *)
Printer_File_Copy.enable := FALSE;
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileDeviceUnLink; (* next Step*)
END_IF
(***********************************************************************************************)
StepZPLFileDeviceUnLink: (* Disonnect Printer via FTP *)
(***********************************************************************************************)
DevLink_Printer.enable := FALSE;
DevUnlink_Printer.enable := TRUE;
CAB_Printer.UNLNK_Status := DevUnlink_Printer.status;
DevUnlink_Printer.handle := CAB_Printer.Handle;
DevUnlink_Printer();
FileCrTimStrt[4] := 1; (* Start Timer *)
IF TON_FileCrDelay[4].Q = 1 THEN (* Verify File Copied *)
FileCrTimStrt[4] := 0; (* Reset Timer *)
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileClose3; (* next Step*)
END_IF
// Verify all Files are Closed
()
StepZPLFileClose3: (* close file, because of limited number of available file handles on the system *)
()
ZPLCreator[1].FileCreate.FB.FileClose_3.enable := TRUE;
ZPLCreator[1].FileCreate.FB.FileClose_3.ident := ZPLCreator[1].FileCreate.FB.FileOpen_0.ident; (* ident for FileCreate-functionblock*)
ZPLCreator[1].FileCreate.FB.FileClose_3(); (* call the function*)
IF (ZPLCreator[1].FileCreate.FB.FileClose_3.status = 0) THEN
ZPLCreator[1].FileCreate.FB.FileClose_3.enable := FALSE;
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileDelete1; (* next Step*)
ELSIF (ZPLCreator[1].FileCreate.FB.FileClose_3.status = 20721) THEN
ZPLCreator[1].FileCreate.FB.FileClose_3.enable := FALSE;
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileDelete1; (* next Step*)
ELSE (* Goto Error Step *)
// ZPLCreator[1].FileCreate.Data.Step := StepZPLFileError;
END_IF
(***********************************************************************************************)
StepZPLFileDelete1: (* Delete File from Memeory *)
(***********************************************************************************************)
ZPLCreator[1].FileCreate.FB.ZPLFileDelete1.enable := TRUE;
ZPLCreator[1].FileCreate.FB.ZPLFileDelete1.pDevice := ADR(ZPLCreator[1].FileCreate.Data.Device);
ZPLCreator[1].FileCreate.FB.ZPLFileDelete1.pName := ADR(ZPLCreator[1].FileCreate.Data.FileName);
ZPLCreator[1].FileCreate.FB.ZPLFileDelete1();
FileCrTimStrt[5] := 1; (* Start Timer *)
IF TON_FileCrDelay[5].Q = 1 THEN (* Verify File Deleted *)
FileCrTimStrt[5] := 0; (* Reset Timer *)
ZPLCreator[1].FileCreate.Data.Step := StepZPLFileWait; (* next Step*)
END_IF
Hi,
I haven’t checked your source code in detail right now, but in the sequence above, already FileCreate() creates an ident - I’m not sure if this is the reason, but in my understanding it could be.
The FileOpen() after FileCreate() is not needed, you can already go on after FileCreate() with FIleWrite() using the .ident output of FileCreate(), and do a FileClose() in this ident when data was written.
So I would say, the needed sequence is:
1.) If file NOT exists:
FileCreate() - FileWrite() - FileClose() - DevLink() - FileCopy() - DevUnlink() - FileDelete()
2.) If file already exists and you want to copy it without changing:
DevLink() - FileCopy() - DevUnlink() - FileDelete()
3.) If file already exists and you want to ADD DATA (but as I remember, this is not a usecase you have?):
FileOpen( ) - FileWriteEx(with option fiTruncate) - FileClose()
Best regards!
I will give that a try. It could be the issue. Thanks
As an addition to Alexander’s very useful advice. A lot of applications use brute force through the “File opening” process. The sequence (depending on application expectations) was:
FILE_CREATE_STATE
FileCreate() = Success → Proceed to file operation state.
FileCreate() = fiERR_EXIST → Proceed to FILE_OPEN_STATE state.
FileCreate() = other, not-busy status → error handling.
…
FILE_OPEN_STATE
FileOpen() = Success → Proceed to file operation state.
FileOpen() = other, not-busy status → error handling.
OR
FILE_OPEN_STATE
FileOpen() = Success → Proceed to file operation.
FileOpen() = fiERR_FILE_NOT_FOUND → Proceed to FILE_CREATE_STATE state.
FileOpen() = other, not-busy status → error handling.
…
FILE_CREATE_STATE
FileCreate() = Success → Proceed to file operation.
FileCreate() = other, not-busy status → error handling.
If your application was creating files more than opening existing ones, then attempt to create first. Use the opposite order for when your application is opening existing files more often. This operation saves the application a FileInfo to determine if the file exists or not.
-Austin
I have implemented the sequence that Alexander has suggested and have been running all afternoon. I think the issue was that we were opening and creating the file but only closing 1 of the 2 files. We will continue testing tomorrow. Thanks for the assistance.
Hi, what is the status of this topic, have you found solution for you? Can you mark the reply that helped you the most?