The approach of (all) Function Block modelling tools (Simulink, Modelica etc.) are dataflow oriented. This is traditional. The data flow determines the order of execution. Some FBlocks store data inside.
The Object Oriented Programming has never reached the FBlock world, in the official tools. Why not? Tradition.
What is Object Oriented Programming? It is firstly the definition of associated data with associated operations. Secondly a system of abstraction and inheritance is self-evident, which is established in the known Object Oriented languages, inclusively late dynamic binding.
If a graphical module contains internal data (its state), the operations which are associated to the data flow in dedicated step times are the operations to the data; and it is ObjectOriented, in fact. But the access to this object is only limited to the data flow step operations. The transport of the data in the flow is like an event. The event itself is the fact, that the next module should be executed; and the data to this event are given with this data flow. The data flow is not only a data connection, it defines also the execution order.
If a C/++ FunctionBlock is used, it can also contain data. It is an Object in the same kind. Its step operations are the operations to the data inside the FBlock.
But additionally the FBlock can transfer a reference to its own internal data to other FBlocks. This FBlocks can (should) be concerned. So we get some concerned FBlocks. If only one of them contains the data, the others are present as access operations (getter, setter), then we have enhanced the ObjectOrientation of a simple FBlock to disperse accesses in the whole graphical model. Some Blocks can be used as operations ("methods"), which can be called from any other parts of the software, here graphical.
Hence the important fact is the relation between determined FBlocks with references to more elaborately using ObjectOriented accesses.
This system supports also abstraction and inheritance:
It is possible to define another FBlock with data as extending FBlock, which refers a base FBlock. A FBlock does only create its internal data if it is not extended. Elsewhere it gets the data reference from its extending one. Hence only one data creation occurs, in the most extending FBlock. It is a quest of configuration of the C/++ software in the FBlocks.
It is possible to use late dynamic binding in some operations from the base FBlock. Hence the operation of the step time depends on the extension. This is simple able to do if C++ is used, using virtual operations, or another adequate solution (internal function pointer table).
The possibility of extension (and abstraction from other side view) is important for graphical modelling too:
If a module is ready and tested, it should not be changed in a resuing situation with some other details.
Extension in a extending graphical module introduces the specific necessary things.
In classic ObjectOriented approaches, often presented with UML (Unified Modelling Language) the references between Objects, associations, aggregations and compositions are presented in Class or ObjectModelDiagrams. The kind of setting references is part of the implementation. Compositions (references to own sub Objects) are created of course on startup of the Object. Aggregations (fix references to other Objects) are connect either via constructor arguments: pointer to the aggregated Objects, or in a specific startup operation. Associations are references which can be changed in runtime, with specific operations.
Using FBlocks the system of setting references is more obvious. References are simple connections between FBlocks. A reference as association can be switched (using switching FBlocks). Often references are given on startup as aggregations. This reference connections are like data flow. The data flow is from the reference presenter to the consumer. Hence the direction of the arrow is in opposite to a Class Diagram. A Class Diagram shows the relation of usage: The referencing class/object points to the referenced one. A FBlock diagram has the arrow from the referenced FBlock (presents its address) to the referencing one (can use the address to access).
Image: Example model with OrthOsc as ObjectOriented FBlock
The image above shows a complex example in Simulink with ObjectOriented FBs:
The FBlock OrthOsc2_FB (middle) has a filter functionality, explored and developed with Simulink tooling, but then established as C/++ Function (Simulink S-Function). It uses parameter. The parameter are calculated in an own Param_OrthOsc2_FB, left side. The param_OrthOsc1 offers its data reference to the orthOsc1, which can use the parameter data by immediate access into the Param_OrthOsc2_FB FBlock.
The parameters depend on the frequency, the fq input. But the calculation of new parameters from a given frequency is done in a longer step time. Because: It needs more calculation time for
cos calculations, and it is not necessary to calculate it in the fast step time. The step times are presented here by different colors. The red color is the fast step time (for example 50 µs for electrical signals). - Hence the parametrization is another operation, another FBlock. The other reason for an own FBlock: Parameters can be used for more as one OrthOsc2_FB FBlock. Then the references is connected to all using ones.
The angle gi is prepared in a specific FBlock Angle_abgm_FB. This FBlock is separated from the OrthOsc2_FB though it works in the same step time. The reason: The angle can be used via this reference in some other FBlocks too. The preparation of the angle value from a wrapping integer presentation to a and b coordinates needs a sin and cos calculation, it needs some time and should only one time performed in the fast step time. The reference is offered from this FBlock angle1 to the orthOsc1 via its internal reference. This reference can be used by other proper FBlocks too if necessary. Then only the references should be adequate wired. If this operations are modeled in Simulink (in an own subsystem) the output will be a bus (with more signals). A simple bus is also handled as reference in code generation, there is no difference, the code generation is optimized. But, handling a bus in a diagram is some more difficult in comparison with the simple reference handle, it is an
uint32 type. This reference is really included as aggregation in the OrthOsc2_FB class. Such a referenced bus is not intended in a maybe comprehensiv bus structure, which may present the data of OrthOsc2_FB. Deal with handle is more simple.
The both FBlocks calcmagn1 and calcdq right side are connected with the thiz reference in the fast (red) step time. They are get-operations (getter) without own data, as part of the OrthOsc2_FB class. Also set operations are possible. They can be dispersed in the model. Getter and setter are Operation-FBlock without own data, whereas the other are Object-FBlocks with its data.
On modelling level the references are from type
uint32 because whether Simulink nor other simulation tools knows pointer types. Using of a bus in Simulink is typed, but buses has other disadvantage.
Hence the type is not checked on compile time. References on FBlocks can be worse connected. But - the phase of compilation and startup of simulation is nearly the same. It means a type check in the startup phase of run time is nearly the same as in compiling phase of the model.
If the model is checked (simulation was started), all connections are ok. Then the code generation can use the
uint32 handle values immediately as references, with
For the type check in startup run time a type designation via Reflection is used. It means all data
struct for references should base on ObjectJc which contains a reference to the appropriate
ClassJc. For the Simulation level this information should be complete with the name ot the type as String. But for running in a poor target the
ObjectJc can be reduced to only one
int32 value. The type need not be checked again in the target if it is checked proper in simulation. But for associations (switching references) or for safety a type check can be also done in the startup phase (init Routine) in the target, recommended on more rich targets (more processor power, memory etc).
The references between Object-FBlocks are associations or aggregations, or inheritances. Especially for aggregations the wiring should be completely done in a startup phase before any functinality of step times is used. Simulink and also other tools do not offer a specific intialization process. Hence, for Simulink a specific
Tinit step time is used. In the simulation engine this step time is cyclically invoked of course, but only the first few steps are necessary to initialize. The other cycles are empty in functinality.
For code generation this
Tinit step time is generated in a specific routine for this step time, which need and should be executed only initially. But some steps (a
while loop) is necessary.
Why not only one step to initialize?
Initializing needs either data (values) or references as aggregations, or both: a reference whereby the referenced object should be initialized already with the correct values. Hence in a first step the initializing cannot be finished, if the source of an Object is not initialized in this step. Also the correct order of Objects for initializing does not help, because there are each-other or circular dependencies. They are not possible to avoid, aggregations in both direction may be necessary.
Any object contains the information
isInitialized_ObjectJc(…), it is property of the
ObjectJc base class (which is necessary also for type check, see above). If there are C/++-FBlocks which’s data are not based on
ObjectJc, simple functionality which’s data are not presented outside, but it depends on correct initializing from other FBlocks, the state 'isInitialized' is stored in a S-Function-specific variable. For the target system it is sufficient that the
init(…) step routine is called one time again after message 'all seems to be intialized' - because that FBlocks are guaranteed initialized in this last step if all sources are initialized.
In the image above "Example model with OrthOsc as ObjectOriented FBlock" the FBlock Param_OrthOsc2_FB can only be initialized if the fq on its input has a valid value. This frequency value may deliver from another source. It should be ready to use in the initial time, but maybe not in the first cycle of that. Only if Param_OrthOsc2_FB can calculate correct parameter proper to the frequency then the OrtOsc2_FB can work. This is the initializing chain in this example.
One can use the GenSfnFromHeader.html approach to write its own C/++-S-Function in Simulink with this approach.
One can use the ../SmlkInspc/DataStruct_Inspc.html S-Functions with the possibility of parametrizing to have ObjectOriented data FBlocks in a model. The functionality is graphically in this sub module. But the interface is the same as described.
The last one FBlocks supports inheritance of data.
This chapter compares the classic connection of Moduls via Bus and the connection via a reference It was a preliminary consideration before creating the reference-handle system.
ObjectOrientation as such means, bundling of data to their operations. For Simulink FBs (Function Blocks) it means, any FBlock, or Module, has its specific data. The assembled data of a FBlock or Module can be presented and forwarded with the simulink standard equipment via a mux (Multiplexer) or via a bus. But how it is with set-operations. Show the right images of the following chapter:
The first image shows two modules. Originally it could have been one module, but a part of the too much functionality may be removed from stock. Now there is a data flow between the orginally associated functionality. A simple begin of confusion.
Some more data are necessary from
M_A. The last one, the original or first, should be the 'module'- FBlock which holds the data in an Object Oriented approach. Some data are transferred from A to B.
But to rewrite to the data inside the operation
M_B some back connections are needed, from the operation, which changes the data, back to the Module which holds the data. That is data flow, the original paradigm of Simulink.
If the data should be consequently hold in the
M_A it needs a storage. In a classic data-flow-oriented design the storage is inside any FBlocks, last not least usually it is a kind of the here shown unit delay.
Because of the data should be explicitely designed, unit delays are used explicitely in the 'module'-FBlock
M_A. Because of its content should be get and set by several operations and the 'object oriented' data flow should be simple visible, left and right of them is a bus which’s definition defines the data of this ObjectOriented approach. Of course for vectorized data one unit delay is only necessary.
Yet the designer of the model could come to the idea to assemble the unit delay in the 'operation' module, because the new data are calculated there, it seems to be more simple. It is data-flow-oriented. But hence the architecture of the Object-Oriented software is disturbed, no more separation between data and the operation, no more assemble all data in one module.
The bus definition may be seen as elaborate and non-flexible. There is a more simple approach using special FBlocks as S-Function (in C) which holds the data internally instead using graphical unit delays. The FBlocks are textual parametrized in the Simulink model to define kind and number of data. No (C/++) programming is necessary. As additonal feature the internal values can be set from a file content [[TODO timesignal values], and the content is accesible via Socket connection if the Inspector-Service FBlock is connected with them.
Hence the two modules are connected via a handle which is a reference to the data. Now it is really ObjectOriented. The handle is an identifier (numerical) for the module data on memory. It is the memory address for a 32-bit-System after code generation. For simulink usage the memory address may be 64 bit, and a problem of memory isolation exists between several mex moduls of the S-function. Hence the handle is used here, it is a 32 bit value, but in a lower range, it is an index to a memory address table.
One can ask, what about the dataflow principle: Well, there is a data flow, the module FBlock gives its information about the memory to its associated operation FBlock.
If the operation is only reading, the data flow adequate to the functional programming approach is considered. If the operation is writing, it is a writing operation for this module outside of the module FBlock in respect to Object Oriented Programming with private access of the own writing operation to the module. The only one extraordinariness for classic data flow /FBlock programming is: It is outside the module. The advantage: Improved design ability.
What about faulty connections, memory mismatch: That is proper. The receiving FBlock checks the content of the referenced memory via Reflection, the type should be match. Only suitable FBlocks can be connected together. A non conform connection is reported on start of the simulation as error.
The module which holds the data contains a Function Block of type lib_Inspc/DataStructMng_Inspc from the
lib_Inspc-Library. It has inputs for all data, maybe used as initial inputs only (the 3. and 4. input here) or in Runtime (the first 2 inputs here). The definition of input number and kind is given here with the simple textual parametrizing:
s1,s2; =a, =b;
It defines the name of the 4 Inputs. The type depends from the wired signals (inherit) or can be defined in the parametrize area, also for array types. The
= means, the input is the initial value only.
Here shown, the access to data is done with the small FBlocks designated as
b. They are of type lib_Inspc/get_DataStruct_Inspc which the proper Parametrizing to access one value of the data.
The image right shows access and set of data. How to do rewrite of data without a back-connection (data-flow thinking). Of course, it is possible with a set-Operation. That is done with the two right FBs
b. It are of type lib_Inspc/get_DataStruct_Inspc. The second input is the handle which refers the memory for the data. The first input is determined by the parametrizing for the selected data field in the data struct. The step time (sample time) can be any one. The data consistence is in the hands of the developer, some helper (locking mechanism, double buffering) is possible. In a classic dataflow design rate transitions cannot resolve any data consistence problem. But that is a more complex topic.