I'm using WAGO PLC PFC200 in my home automation project. I've plenty of POUs, each for one room. Each room implements IRoom interface and uses base POU for common logic like turning off all lights.
For lights management, I'm using
FbEvaluateShortLongPress from WagoAppBuilding to handle short and long press of buttons on the wall (it could also be a function block from OSCAT library)
FbLatchingRelay from WagoAppBuilding as a toggle for PLC digital output
I want to save the state of FbLatchingRelay in case of e.g.: power drop. I want all lights which were turned off before power drop to be turned on when the power comes back.
I've solved it by declaring a FbLatchingRelay in the VAR RETAIN PERSISTENT area in my POU. But then after reading here that:
If you declare a local variable in a function block as RETAIN, CODESYS stores the complete instance of this function block in the Retain range (all data of the function block); however, only the declared RETAIN variable is treated as such.
I decided to change that in order not to waste RETAIN memory for a bunch of variables which are in POU but are not needed to be stored as RETAIN.
So right now I have something like that:
the VAR RETAIN PERSISTENT area is only declared in my main program
it stores structures for each room (each POU) only with needed data - FbLatchingRelay POU and few other variables
while initializing the room (POU) I'm passing those structures to my rooms using VAR_IN_OUT
each room (POU) uses then this data
PLC_PRG:
VAR RETAIN PERSISTENT
BathroomPersistentData: BathroomData;
END_VAR
Bathroom(PersistData := BathroomPersistentData, xMainLightSwitch := DI1_13, xMirrorLightSwitch := DI2_3, xMirrorLightSwitchActuator => DO2_1, xMainLightSwitchActuator => DO1_11);
Bathroom POU:
VAR_IN_OUT
PersistData: BathroomData;
END_VAR
Is this a good approach? What do you think? It complicates project a bit but I'm not wasting RETAIN memory for things which should not be there (whole POUs).
Yes, this is how my organization handles retain vars. This also lends itself to supporting “save to disk” solutions for other FB demands (not so much for your light states).
On the other hand, did you run out of memory by the original way? Sometimes I find we worry about things that never happen. Yes it is “wasteful” for the whole FB instance to be put in retain memory, but if your FBs are small and your device has plenty of retain memory - then nothing to worry about until later.
Related
In some contracts, I see they use constant slot numbers. But how are they guaranteeing that that slot will never be used? For example in EIP1967 there is a slot for implementation:
// bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)
_IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
and another example is in the Gnosis's contract:
// keccak256("fallback_manager.handler.address")
FALLBACK_HANDLER_STORAGE_SLOT = 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5;
The question is: Is it safe to use any slot using this approach? Can some dynamic array or mapping be used in this slot? For example:
// keccak256("some.dummy.address.address")
SOME_SLOT = 0x47bd68279a41c9ae1cc277c8922809c3c82881c5143963fcfc95b91a61097eb5;
Ethereum writes to the free memory slots. Fromt the docs:
Solidity always places new objects at the free memory pointer and
memory is never freed (this might change in the future).
if you already wrote into a slot, Ethereum will not store anything in that slot. Now it is up to developers to keep track of which slots they already have used. In order to define which slots they are going to use, developers use inline assembly. (This is one of the advantages of inline assembly. It gives you granular control over the memory)
In the CodeSys manual we can read that:
"If you declare a local variable in a function block as RETAIN, CODESYS stores the complete instance of this function block in the Retain range (all data of the function block); however, only the declared RETAIN variable is treated as such."
But does someone actually tested? I created a function block with only the following variables:
VAR
Test1: ARRAY[1..50] OF UINT; //100 bytes
END_VAR
VAR RETAIN
Test2: ARRAY[1..50] OF DINT; //200 bytes
END_VAR
My program only implements one instance of this function block. Using SIZEOF at runtime shows a function block size of 312 bytes
Now, if I right-click on the device, and go to "Device Memory Info", the size of my Retain Data is only 203 bytes.
If the complete instance of the function block is stored in the retain range, I would expect the retain data size to be the same as the function block size (312 bytes), but it isn't, it's only 203 bytes (size of the retain data). Is the manual incorrect?
I can say it is true. The first project where I needed retains inside a FB, I ran out of memory after having needed many, many instances (the FB were not “simple”). Once I removed the retain from the FB and linked to an external Retained variable, my problem went away. That is the day I learned the documentation warned me.
But I can say in that case I was making a solution that had around 100 unique and complicated FBs where each was storing a user entry. I have made several other projects that were much simpler and I just let the compiler put the whole FB is retain.
I am coming from computer science background and used to traditional IT programming. I have relatively little experience with structured text. In my current project I am extensively using many function block. I am aware that this involves some memory issues and so on. Could anyone come up and give me some advantages and disadvantages of each of them. Should I avoid them and write everything in a single program ? Please practical hints should be welcome as I am about to release my application.
System : Codesys
I also come from the PC programming world, and there are certain object tricks I miss when programming in Codesys. The function blocks go a long way towards object thinking, though. They're too easy to peek into from the outside, so some discipline from the user is necessary, to encapsulate the functionality or objects.
You shouldn't write a single piece of program to handle all functionality, but instead use the Codesys facilities to divide the program into objects where possible. This also means to identify which objects are alike and can be programmed as function blocks. The instance of a function block is created in memory when the program is downloaded, e.g. it is always visible for monitoring.
I normally use POU's to divide the project into larger parts, e.g. Machine1(prg), Machine2(prg) and Machine3(prg). If each machine has one or more motors of similar type, this is where the function blocks come in, so that I can program one motor object called FB_Motor, and reuse it for the necessary motor instances inside the 3 machine programs. Each instance can then hold its own internal states, timers, input output, or whatever a motor needs.
The structure for the above example is now:
MAIN, calls
Machine1(prg), calls
fbMotor1 (implements FB_Motor, local for Machine1)
fbMotor2 (implements FB_Motor, local for Machine1)
Machine2(prg), calls
fbMotor1 (implements FB_Motor, local for Machine2)
Machine3(prg), calls
fbMotor1 (implements FB_Motor, local for Machine3)
fbMotor2 (implements FB_Motor, local for Machine3)
fbMotor3 (implements FB_Motor, local for Machine3)
The functions are another matter. Their data exist on the stack when the function is called, and when the function has returned its value, the data is released. There are lots of built in functions, e.g. BOOL_TO_INT(), SQR(n) and so on.
I normally use functions for lookup and conversion functions. And they can be called from all around the program.
The clarity, robustness and maintainability are everything in PLC world. Function blocks help you to archieve that if the stucture is kept relatively flat (so one should avoid functionblock inside functionblock insede function block, compared of true object and their heritage).
Also the graphical languages are there for reason they visualise the complex systems in easy to digest form in a way that the maintaining personnel in the future have easier life to follow what is wrong with the PLC program and the part of the factory.
What comes to ST it is advance to remember that it is based on strongly typed Wirthian languages (ADA, Pascal etc.). Also what is often more important than memory usage is the constant cycle time of the program (since real time system). The another cup of the tea is the electrical layer of the control system, plus the physical layer and all the relations on that layer that can backflash somewhere else in your program if not taken account.
I need to increment an integer value each time the PLC program is updated to track changes.
There are system events like online_change and before_download, but I have no idea how to implement their functions.
Also I need to save value between updates. I think the tracking variable should be created as RETAIN but not sure.
The variable declaration type should be VAR RETAIN PERSISTENT in your case. Variables declared under RETAIN only will lose their values (intentionally) with a program change.
I believe the builtin Codesys library SysLibProjectInfo.lib has what you are looking for with the function SysGetProjectID. If you store SysGetProjectID as a RETAIN PERSISTENT and then compare against it, you can track changes (or, this unique value may be exactly what you wanted in the first place, without manually creating an ID).
Note: Depending on how you declare your variables, changing the I/O configuration can have unexpected changes even on VAR RETAIN PERSISTENT variables (as all dynamically allocated addresses are shifted and may not point where they used to).
If I understand you you only want to know like what version is running on the PLC and you want to track changes that you make? You can do it two ways:
Since it's a constant each time you make a change external to the PLC you roll the rev of a variable that is declared like SoftwareVersion :WORD := 100; and keep it in a Revision global list that you can add your notes to and change the version before downloading to PLC.
You can also use the PLC summary area that has fields to enter the values and then you can read them through CoDeSys without software upload.
And of course the suggestion above can work.
I recently wrote some code using Matlab's OOP. In each class object I save some measurement data as a property and define the methods for evaluating them. With an average data set one single class object uses about 32 MB of memory.
Now I am writing a GUI that should process these objects.
In the first step I load a set of objects from a saved .mat-file (about 200 objects, 2GB on harddisk) and store them in the handles struct. They fill the RAM and use about 6-7 GB, when loaded. This is no problem.
But if I close the GUI, it seems that I can't free the used memory.
I tried different approaches with no success.
Setting the data fields to "empty" in the destructor of the class:
function delete(obj)
obj.timeVector = [];
obj.valueVector = [];
end
Trying to free it in the figure_CloseRequestFcn:
function figure_CloseRequestFcn(hObject, eventdata, handles)
handles.data = [];
handles = rmfield(handles,'data');
guidata(hObject,handles);
clear handles;
pack; %Matlab issues a warning, that pack could only
%be used from the command line, but that did
%not work either
delete(hObject);
end
Any ideas, besides closing Matlab every time after working with the GUI?
I found the answer in the Matlab Bug Report Center. Seems to exist since R2011b.
Summary
Storing objects in MAT-files can cause a memory leak and prevent the object class from being cleared
Description
After storing an instance of a class, 'MyClass', in a MAT-file, calling clear classes may result in the warning:
Warning: Objects of 'MyClass' class exist. Cannot clear this class or any of its superclasses.
This warning persists, even if you have cleared all instances of the class in the workspace.
The warning may occur for one MAT-file format, and not for another.
Workaround
Under some circumstances, switching to a different MAT-file format may eliminate the warning.
http://www.mathworks.ch/support/bugreports/857319
Edit:
I tried older formats for saving, but this does not work either. I get an "Error closing file" (http://www.mathworks.ch/matlabcentral/answers/18098-error-using-save-error-closing-file). So Matlab does not support saving class objects that well. I will have to live with the memory issues then and restart Matlab after every use of the GUI.
Based on your memory screenshots, there is definitely memory that is not being cleared. There is a small chance that you have found a fundamental flaw in Matlab's garbage collection, but it is much more likely that the ~6Gigs of memory resident data is still actually available via some series of links. Based on personal experience, here are a few ways that memory which you thought was cleared can still be available:
Timer objects: If one of the callback functions of a timer references a this data (or a copy), then that data is still available. You need to call deleted(t) on that timer.
Persistent variables in functions: I often cache data in a persistent variable within a function, this clearly allows access to that data in the future, so it will not be cleared. You need to call clear FUNCTIONNAME to clear associated persistent variables.
In GUI objects, as either data or within callback functions: The figures and any persistents need to be cleared.
Any static methods or constant attributes in classes which can retain data. These can either be cleared individually within the class, or by force using clear CLASSNAME.
Some tips for finding stale link to data (again, based on personal mistakes)
Look at the exact number of bytes being lost after each call, using the x=memory; call to get an exact count. Is it consistent? Is it a number that you recognize? Sometimes I can find the leak after realizing that it is exactly 238263232 bytes, therefore a 29782904 double array, which must be from function xyz.
See which classes are actually being deleted. Within your delete(obj) function add a detailed display or which objects are being deleted, and by inference, which are not. For a given non-deleted object, where could it be reference from? You should not need to clear data in the delete(obj) function like you are doing, Matlab should handle that for you. Use the delete function instead as a debugging tool.
Matlab has a garbage collector so you don't need to manually manage memory. After closing the GUI, all the memory will be freed except for what is in your workspace. You can clear the workspace variables using clear.
One thing I've noticed on Windows (not sure about other platforms) is that Matlab's GUI sometimes retains extra memory (maybe 100 MB, but not multiple GB like you are seeing). Simply minimizing and then restoring the GUI will free this excess memory.