Historical Background
Why were the Parameter Lists removed?
The parameter lists only worked for exactly one use case: The related library with the parameter list was inserted at the top level in the library manager of an application.
However, there have always been the following restrictions:
It was never possible to use the paramter lists in the POU pool.
It was never possible to use the parameter list of a library, if this library was referenced by another library.
It was also never possible to use the parameter list of a subordinate library in the context of a container library.
Implementation Details
The language model of a certain library in a certain version is loaded only once in a project. This means that if, for example, the library “Standard 3.5.15.0” is referenced several times in this version in a library manager, then the language model (precompile context) still only exists once in the memory. The same also applies if this library is referenced in the respective library manager of different applications or devices in the same project. The language model still only exists once in the memory.
It is therefore not possible to enter an individual value for each reference of a library in the respective parameter list. The language model of a library is only available once in the memory, so are its variables.
As an extension to the IEC 61131-3 3rd Edition standard, CODESYS provides the possibility to mark parameters of function blocks in a special way.
Example: TCP_Client
{attribute 'no_assign'}
{attribute 'call_after_init'}
FUNCTION_BLOCK PUBLIC FINAL TCP_Client EXTENDS LCon IMPLEMENTS IClient
VAR_INPUT CONSTANT
itfAsyncProperty : IAsyncProperty;
itfTLSContext : ITLSContext;
itfTSNContext : ITSNContext;
END_VAR
VAR_INPUT
/// Defines the time (µs) after which the connection setup aborts with active ``xError``.
udiTimeOut : UDINT := 0;
/// | The related server address
/// | (With |ResolveHostname|, the conversion from a hostname to an ip address is possible)
itfIPAddress : IIPAddress;
/// The related server port
uiPort : UINT;
END_VAR
VAR_OUTPUT
/// TRUE if a Connection is established
xActive : BOOL;
/// | The established connection to the related server
/// | Valid, as long ``xActive`` = ``TRUE``
itfConnection : IConnection;
END_VAR
VAR
_Connection : TCP_Connection;
END_VAR
The representation in CFC:
See: Parameter Handling
The use of declarative parameters
VAR
myAsyncProperty : NBS.AsyncProperty := (
usiTaskPrio:=10,
udiTaskInterval:=1000,
tgTaskGroup:='IEC-Tasks',
anAppName:='Application',
tnTaskName:='BackgroundTask'
);
ipAddress : NBS.IPv4Address := (
ipAddress := '192.168.101.93'
);
info : STRING := 'raspiBot';
ciCertInfo : NBS.CERT_INFO := (
psInfo:=ADR(info),
udiSize:=SIZEOF(info)
);
myTLSContext : NBS.TLSContext := (
ePurpose:=NBS.PURPOSE.CLIENT_SIDE,
sUseCaseName:='NBSTest',
sTLSVersion:='1.2',
ciCertInfo:=ciCertInfo,
udiVerificationMode:=2,
sHostname:='test-server.com'
);
myClient : NBS.TCP_Client := (
itfIPAddress:=ipAddress,
uiPort:=8123,
udiTimeOut:=0,
itfTLSContext:=myTLSContext,
itfAsyncProperty:=myAsyncProperty
);
END_VAR
A problem arises when a function block, which is to be supplied with start values via its declaration, is to be used in another function block, i.e. nested one level lower. In the example above (TCP_Client), this would be the case for the TCP_Connection function block. The following code would not lead to the desired result:
It doesn’t work like that!
VAR
_Connection : TCP_Connection := (
itfIPAddress:=THIS^.itfIPAddress,
uiPort:=THIS^.uiPort,
itfAsyncProperty:=THIS^.itfAsyncProperty
itfTLSContext:=THIS^.itfTLSContext
);
END_VAR
Reason
The input variables itfIPAddress, uiPort, itfAsyncProperty and itfTLSContext of the TCP_Connection block are provided with values BEFORE the input variables of TCP_Client. At the time the assignments of the adjacent code are executed, the assigned variables are all still assigned their respective default values. The values of the environment have not yet been assigned. Therefore, a meaningful passing on of the values is not possible.
The provision of the elements of a nested structure or a nested function block with startvalues always takes place from the inside to the outside!
The approach to solving this problem is to provide each function block, that is to receive initial values in a nested environments, with a method called SetInitialValue. Then the SetInitialValue methods of the built-in function blocks can be called via the attribute {attribute 'call_after_init'} of the surrounding function block.
TCP_Client : Init
{attribute 'conditionalshow'} {attribute 'call_after_init'} METHOD PROTECTED Init SetInitialValue( THIS^.itfIPAddress, THIS^.uiPort, THIS^.itfAsyncProperty, THIS^.itfTLSContext );
TCP_Client : SetInitialValue
METHOD PUBLIC FINAL SetInitialValue : ERROR
VAR_INPUT
itfIPAddress : IIPAddress;
uiPort : UINT;
itfAsyncProperty : IAsyncProperty;
itfTLSContext : ITLSContext;
END_VAR
VAR
eErrorID : ERROR;
END_VAR
eErrorID := _Connection.SetInitialValue(
itfIPAddress,
uiPort,
itfAsyncProperty,
itfTLSContext
);
SetInitialValue := eErrorID;