- Introduction
- Example details
- Preconditions
- Project compiling in Eclipse
- PLCnext Engineer project
- Project Execution
- FileStream Operations
- Exception Handling
- General Notes
This Example shows how to use a FileStream to check the binary generation timestamp whenever a Component is reinitialized.
Description | Value |
---|---|
Controller | AXC F 2152 |
FW | 2021.0 LTS or later |
SDK | 2021.0 LTS or later |
PLCnext Engineer | 2021.0 LTS or later |
- AXC F 2152 controller with firmware 2021.0 LTS or later
- Eclipse IDE "2020.9" or later
- PLCnext Engineer 2021.0 LTS or later
- In Eclipse, create the project "FileStreamExample" with Component "FileStreamExampleComponent" and Program "FileStreamExampleProgram".
- In the project, replace the files "FileStreamExampleComponent.cpp", "FileStreamExampleComponent.hpp", "FileStreamExampleProgram.cpp", and "FileStreamExampleProgram.hpp" with the according files from this repository. Alternatively, you can create your own project with component and programs and include the source code in your application.
- Compile the Eclipse project.
- After successfull project compilation, the PLCnext Engineer library will be created automatically. You can find it in your Eclipse workspace folder, e.g.: "workspace\FileStreamExample\bin\FileStreamExample.pcwlx"
- In PLCnext Engineer, create a new project and include the "FileStreamExample.pcwlx" in the project.
- Instantiate the "FileStreamExampleProgram" under a previously defined task.
- Download the PLCnext Engineer project to the PLCnext Control.
During the Setup of the PLCnext Runtime our PLM component is loaded and the Initialize, LoadConfig , etc. are executed. This application writes a at compiletime generated localtime string TIMESTAMP into a file. This way, whenever the PLC Starts it will first load the last generated file, compare it to the Timestamp of the current binary and then overwrite the Binary.
- Component::LoadConfig
ReadFromFile
- On success reads the creationtime from file and compares it to the creationtime of the currently used binary.
- Component::SetupConfig
WriteToFile
executes- On success localtime of currently used binary is written to file overwriting its contents.
- Component::ResetConfig (called at system shutdown or during
PLC::Start
afterPLC::Stop
)- If the Inport
bReset
is set trueDeleteFile
is executed at the next Shutdown / Stop-Start.
- If the Inport
Here is some example output this Component will create if you download some changes to the running PLC.
24.02.21 14:40:47.921 FileStreamExample.FileStreamExampleComponent INFO - ---Initialize - Compile on Wed Feb 24 14:14:21 2021
24.02.21 14:40:47.923 FileStreamExample.FileStreamExampleComponent INFO - ---LoadConfig
24.02.21 14:40:47.924 FileStreamExample.FileStreamExampleComponent INFO - --- Read from file: Wed Feb 24 14:28:59 2021
24.02.21 14:40:47.926 FileStreamExample.FileStreamExampleComponent INFO - Last Date: Wed Feb 24 14:28:59 2021
24.02.21 14:40:47.927 FileStreamExample.FileStreamExampleComponent INFO - Current Date: Wed Feb 24 14:14:21 2021
24.02.21 14:40:47.929 FileStreamExample.FileStreamExampleComponent WARN - --- New Binary has been loaded!
24.02.21 14:40:47.932 FileStreamExample.FileStreamExampleComponent INFO - --- SetupConfig
24.02.21 14:40:47.935 FileStreamExample.FileStreamExampleComponent INFO - --- File:logs/TestFile.txt of Length:24 created
void FileStreamExampleComponent::WriteToFile(String textToWrite) {
try {
// Create a FileStream that force creates a file.
Arp::System::Commons::Io::FileStream stream(filePath,
Arp::System::Commons::Io::FileMode::Create);
// String
Arp::String str = textToWrite;
try {
// Write String
if (stream.CanWrite())
stream.Write((Arp::byte*) str.CStr(), str.Length(), 0, str.Length());
} catch (const Arp::Exception &e) {
log.Error("--- Cannot Write to File. Error message: {0}.",
e.GetMessage());
}
//Get File Length
size_t filelen = stream.GetLength();
log.Info("--- File:{0} of Length:{1} created", filePath, filelen);
stream.Close();
} catch (const Arp::Exception &e) {
log.Error("--- Cannot Create File:{0}", e.GetMessage());
}
}
String FileStreamExampleComponent::ReadFromFile() {
try {
Arp::System::Commons::Io::FileStream stream(filePath,
Arp::System::Commons::Io::FileMode::Open);
//init string
String str((String::size_type) stream.GetLength(),
(String::CharType) '0');
try {
// Read String
if (stream.CanRead()) {
stream.Read((Arp::byte*) str.CStr(), stream.GetLength(), 0, stream.GetLength());
log.Info("--- Read from file: " + str);
}
}
//Here you could catch all exceptions separately
catch (const Arp::Exception &e) {
log.Error("--- Cannot read: {0}.", e.GetMessage());
str = e.GetMessage();
}
stream.Close();
return str;
} catch (Arp::System::Commons::Io::NotExistException &e) {
// Example of catching InvalidPathException
log.Error("--- File does not exist. Message: {0}.", e.GetMessage());
nofile = true;
return e.GetMessage();
} catch (const Arp::Exception &e) {
log.Error("--- Cannot Open File. Error message: {0}.", e.GetMessage());
return e.GetMessage();
}
}
There are a lot of exceptions that you should handle when working with files.
There are Function Specific Exceptions like a NotExistException
that might indicate that something went wrong during the last shutdown.
But also common exceptions like Commons::Io::OutOfSpaceException
s. These might indicated that the whole application should better stop.
File or IO activities can take quite a long time and might inhibit your RealTime operation. That is why it is generally recommended to move all file operaitons you need into threads.