Join us today!

Notifications
Clear all

[Solved] Utilizing Properties in TwinCAT 3

7 Posts
4 Users
4 Reactions
2,346 Views
Posts: 8
Topic starter
(@time2learn)
Active Member
Joined: 2 years ago

I'm new to OOP and hope to use more of its principles in the future. While creating function blocks for my programs, I started to consider using properties to practice the OOP methodology. However, I feel that much of the data that I want to pass through properties can just as easily be VAR_IN and VAR_OUT variables.

 

How does one determine when to use properties within their function block? Thank you for your assistance!

Reply
6 Replies
ilyasskure
Posts: 5
Admin
(@ilyasskure)
Member
Joined: 2 years ago

That's a great question! Before giving a simple example, it would be useful to explain a few things. Properties enables you to hide the data in your function block. This is called encapsulation, which is making sure the 'sensitive' data is hidden from the users. To achieve this, you use 'private' access specifiers.

There are 4 access specifiers you can use in TwinCAT 3:

accessSpecifier

PUBLIC It doesn't restrict anything and the property or method can be accessed outside the function block.

PRIVATE The property or method can be accessed only within the function block. No outside access is allowed. (Encapsulation)

PROTECTED Access is allowed only within the 'inheritance family'

INTERNAL Access is limited to the same library (namespace).

 

Properties have two methods called getters(read) and setters(write). They are called as accessors together. You can declare local variables in these methods, however their values get lost on exiting. No input or output declaration is allowed in property accessors. If you want your property to be read only, you can delete the set method and if you want it to be write only, you can delete the get method. 

In the get and set methods, you can implement scaling, error check, range check or unit conversion functionalities.

For example, let's imagine you have a 'Pick' function block in your application.

fbpick

For this function block, you want to get the start position, end position and the velocity data from the user. The start and the end position need to be within a certain range, if not you would want to raise your error flag. You can implement 'error check' inside the 'Set' method of these properties as below:

FB_Pick

In the function block, we have 3 internal variables which is set by the user through the properties and 1 struct output.

FUNCTION_BLOCK FB_Pick
VAR_INPUT
END_VAR
VAR_OUTPUT
	stError : ST_Error;
END_VAR
VAR
	fStartPosition 	: REAL;
	fStopPosition 	: REAL;
	fVelocity		: REAL;
END_VAR

 

StartPosition Property, Set method: 

We are checking if the provided start position is between 0 and 50 before passing the user data to our internal data (fStartPosition). If it is not within the range, we are setting the error flag to notify the user. 

IF StartPosition > 0 AND StartPosition < 50 THEN
	fStartPosition := StartPosition;
	stError.StartPosError := FALSE;
ELSE
	stError.StartPosError := TRUE;
END_IF

 

StopPosition Property, Set method:

We are checking if the provided end position is between 250 and 400 before passing the user data to our internal data (fStopPosition). If it is not within the range, we are setting the error flag to notify the user. 

IF StopPosition > 250 AND StopPosition < 400 THEN
	fStopPosition := StopPosition;
	stError.StopPosError := FALSE;
ELSE
	stError.StopPosError := TRUE;
END_IF

 

For the Velocity property, let's assume you want to implement error check and also scale the velocity. When you need to change this range or the scaling, all you have to do is to change the implementation in this set method. You wouldn't have to change anything for your internal variable.

Velocity Property, Set method:

We are checking if the provided velocity value is between 0 and 100 before passing the user data to our internal data (fVelocity). We are also scaling this value (fVelocity := Velocity/2)

If it is not within the range, we are setting the error flag to notify the user. 

IF Velocity < 0 AND Velocity < 100 THEN
	fVelocity := Velocity/2; //Scaling
	stError.VelocityError := FALSE;
ELSE
	stError.VelocityError := TRUE;
END_IF

 

When no access specifier is used for the property, it is treated as public. For some reason, if you don't want users to provide these data in the future and you want to hide them you can simply add the 'Private' access specifier as below:

PROPERTY PRIVATE Velocity : REAL

 

Users can access these properties simply using dot (.) after typing the function block name ; 

fbPick.StartPosition, fbPick.StopPosition, fbPick.Velocity 

 

Adding the additional layer by using properties gives you the ability to implement extra logic before passing the user data to your internal data and also giving you the flexibility to change the protection of your internal data.

I hope this helps!

 

 

Reply
3 Replies
(@time2learn)
Joined: 2 years ago

Active Member
Posts: 8

Hi @ilyasskure, thank you for the great reply. From your description, it seems like there are potentially two advantages to be gained by using properties instead of VAR_INPUT and VAR_OUTPUT.

 

The accessors Get and Set can be helpful to add read and write protection to variables within a function block. To me, this is the same as declaring a VAR_INPUT (write or Set) or VAR_OUTPUT (read or Get). However, the major benefit is to verify or alter the data coming in via the property before using it in the function block. I can see the usefulness with this aspect.

 

The other item you hit on is that properties can be made private. This will prevent anything outside of the function block from editing the data though this now makes me feel that it can be described as a VAR type of variable. How would the accessors be used then? Furthermore, if a property is made private and no outside influence is allowed, why create the property?

 

If I have misrepresented anything, please let me know. Thanks for your help!

Reply
ilyasskure
Admin
(@ilyasskure)
Joined: 2 years ago

Member
Posts: 5

@time2learn You're welcome! So glad to help! If you are 100% sure that the variable inside the main function block will be used only within the function block itself, you can declare it as VAR type of variable. However, if you change your mind in the future, users won't be able to access those variables even the inherited function blocks. If you use a private property instead, you could maybe change the access specifier to 'protected' type later so that the inherited function blocks can utilize those as well. 

Another thing worth mentioning is that VAR_IN and VAR_OUT are not possible when creating interfaces, you can use properties to define your interface. 

Additionally, if you are using a graphical language (ladder logic, continuous function chart) in your plc logic, you would see the inputs/outputs directly at the box of the function block. If you use a property instead, you would not see them and you would need to assign them separately.

Reply
(@time2learn)
Joined: 2 years ago

Active Member
Posts: 8

@ilyasskure Thanks for adding more insight. I suppose there is a time and place for each one. I'll learn when and where each is best suited as time goes on.

 

Reply
Posts: 6
(@svatoslav-tosner)
Active Member
Joined: 2 years ago

Hello,
firstly, thank you for this explanation. It is very helpful.

Secondly, I would have one question. I have been working on new library based on OOP principles with properties as you mentioned above, but I am struggling with data exchange (parameters for motors, cylinders, axis etc.) with HMI visu. All these parameters were created as properties, but then I decided to use structures for each type of these function blocks (ST_CylinderPar) as INPUT. These structures will be defined as GVL and linked to HMI.

Would you have any better solution to keep the OOP principles ? 

As example: FB_AirMonitoring. Operator can enable/disable this function on HMI

305185076 873293493632212 7689091844195894400 n

 

295493975 1036141793730677 3364573653400758889 n

 

294339954 1303698206834057 3648386881310951117 n
Reply
1 Reply
TwinControls
Admin
(@beckhoffsupport)
Joined: 2 years ago

Member
Posts: 71

@svatoslav-tosner Hi , thank you for your comment! 

Have you tried creating a property with the return type as ST_AirMonitor? 

 

Property AirMonitor : ST_AirMonitor 

air

 

ST_AirMonitor :

stair

 

ST_EnablePilotValve:

stenable

 

 

In the logic:

Declaration:

fbAirMonitor : FB_AirMonitor;

 

Assignment: 

fbAirMonitor.AirMonitor := hmi.stAirMonitor;

 

 

Reply
Share: