Inhalt
Topic:.Smlk_Inspc.FB_ObjOConn.
Last changed: 2019-01-03
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 image:
Topic:.Smlk_Inspc.FB_ObjOConn.cmpBusRef.
This chapter compares the classic connection of Moduls via Bus and the connection via a reference
Bild: Two modules bus-connected In this image two modules are connected. One of them should hold the data, the other is a operation which works with that
data. That should be the approach of the software structure, that is Object Oriented.
But to rewrite to the data inside the operation a second connection is 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.
Bild: data module for bus connection
It may be additional confuse-able: The Module which holds the data holds it in unit delays. One per data word. That is data-flow oriented. They are several data. 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 is more simple. It is data-flow-oriented. But now the architecture of the software is disturbed, no more separation between data and the operation, no more assemble all data in one module. The data-separation approach may be done for any other reason, not for the details here. It is not possible to enforce it because the data-flow-orientation is petty from this view.
Bild: two modules handle (reference)-connected
Now the two modules are connected via a handle which is a reference to the data. Now it is ObjectOriented.
Bild: data module for reference connection
The module which holds the data has a centralized Function Block of type lib_Inspc/DataStructMng_Inspc which is contained in the lib_Inspc
. It can hold up to 6 data elements which any number of array size, and it can cacaded with some more such FBlock to one data
struct.
Bild: operation module for reference connection
The operation should rewrite the data. How to do it without a back-connection (data-flow thinking). Of course, it is possible with a set-Operation. That are the two right FBs which accesses the data. The access FBs of type get/set_DataStruct_Inspc know the data via the handle input.
Code generation support:
For code generation (Simulink embedded coder) both FBs produces a typename.h
Headerfile in its runtime which presents the connected and parametrized values in a struct
with access operations (getter, setter). The tlc-File to control the target language compiler for the Simulink coder takes special run-time-parameter, which are produces too on runtime of the FBs. So the generated code
is optimized for access to the bundled data depending of the model-specific properties of the instances in the model.
Access to the data via inspector:
Last not least there is a FBlock Service_Inspc and one or more tools to access the data. The Service can also run in the target system, the data are bundled ObjectOriented there too. It is data viewing, which is an important medium to explore and evaluate the software in runtime.
Topic:.Smlk_Inspc.FB_ObjOConn.structC.
It is possible to program the data for the graphic modules in typedef struct
definitions with getter and setter operations which are compiled as special Sfunctions, see Topic:.Smlk_de.C_ObjO.genSfH..
For special issues a special solution in C as Sfunction may be proper.
The disadvantage for a common usage is: Small changes in the model may need adapting in the C-sources and newly translating the Sfunction (mex-Compiler). It is a break in the concept of the currently engineering.
Therefore a set of Sfunctions ready to use are provided in the lib/Inspc.slx
Library. This library is favorized because the data are able to inspect too. The service_Inspc
is not necessary for this FBlocks. See next chapter.
Topic:.Smlk_Inspc.FB_ObjOConn.dStructFB.
Bild: DataStruct_Inspc in lib_Inspc
The right image shows the FBlock in the library:
The following 3 Types of DataStruct definition FBlocks should be placed in one module to build the data struct.
DataStructMng_Inspc
is the managing FBlock for one data struct. It has 6 Inputs as set-Functionality.
DataStruct_Inspc
extends the inputs. It builds a chain with any number of it and:
InitDataStruct_Inspc
: This FBlock gets input values only for initialization (Tinit step or sample time) especially for handles (references).
The following Types of DataStruct access FBlocks can be places especially in other modules for data exchange or in the same module to build a data storage (as unit delay):
get_DataStruct_Inspc
: Outputs one value selected per name.
getTinit_DataStruct_Inspc
: Outputs a value in the Tinit step or sample time which is stored with the InitDataStruct_Inspc
. This is for aggregation
set_DataStruct_Inspc
: Sets a value selected per name. This is formally a contradiction to the variable of the FBlocks in the above list. It is
solved with an initial value designation on DataStruct..._Inspc
.
callFn_DataStruct_Inspc
and trgFn_DataStruct_Inspc
build a pair of FBlocks to stimulate a triggered subsystem in the Simulink model.
Topic:.Smlk_Inspc.FB_ObjOConn.appl.
Last changed: 2018-11-14
The FBlocks to build and access Data are arranged in the Library lib_Inspc.skx because a substantial feature is the assignability to data with the Inspector Service.
Topic:.Smlk_Inspc.FB_ObjOConn.appl.exmpl_A.
The FBlock with 'M' named thiz
is the DataStructMng_Inspc from the lib_Inspc.slx
. The first 4 Inputs are connected from several signals inside this module. The types of the signals can be different. In
this image the types are visible (Display - Signals and Ports - Port Data Types) is switched on to demonstrate it.
The output of this FBlock is a handle as uint32
-type. It is named thiz
because it presents this data for the module, adquate to usage of this
in C++. It is named thiz
to prevent name clashes for C++ target code generation. The handle is converted to a pointer to the data in the using Sfunctions,
for target code the handle presents the 32-bit-memory address of the data immediately and optimized.
The second FBlock with 'i' is a cascade with a Init_DataStruct_Inspc SFB from the lib_Inspc. It is especially used for the initial wiring of Blocks and modules: In ObjectOrientation associations and aggregations are
need. That are references to other Objects. Aggregations are connected in the startup phase, and after them they are fix.
Associations can be switched on runtime. That is the wording of UML. The Tinit
step (or sample-) time is the one where initial wiring for aggregations are done. It is support by this FBlock: The special
feature is: The types of the input references are checked. It is a typecheck on startup time in this module, independent of
this feature the types are checked in the using Blocks again. The typecheck here helps to detect model errors local in the
module.
The FBlock f_Y12 is an instance of get_DataStruct_Inspc. It uses the thiz
output via Goto-From-data connection. This FBlock is used to access a value from the DataStructMng_Inspc thiz
. Its output is named f_Y12_z
because it is the value from the last step time. It acts as unit delay. The DataStructMng_Inspc writes the data in the mdlUpdate
routine as last action of the step time, so the access is done to the value from the previous step time. With them a numeric
loop is prevented, and the delayed f_Y12_z
can be used to calculate the new value of this signal with the shown += operation. That is an integrator function.
The get_DataStruct_Inspc accesses the data from the last step time in any case. The combination of this with the DataStructMng_Inspc and a DataStruct_Inspc for cascade acts as unit delay. That is important to exactly implement the integration of differential equations and to prevent data consistent conflicts.
Topic:.Smlk_Inspc.FB_ObjOConn.appl.exmpl_B.
Bild: Application access example
The right image shows the access to the data of the struct of the last chapter. It is assigned in another module via a port
connection. The Fblock after the inport checks the type of the handle on runtime of Simulink and determines the type of access
for the generated code via tlc-files. It is a handle-type check & cast FBlock. The 3 connected FBlocks reads and writes data.
The tune_fY12
writes the named data element with the result of the mulitplication. The par_PID
gets the value on init time for aggregation, in this case of the parameter Fblock to the PID controller Fblock. The yellow
values shows handles which represents pointer values. The f_Y12
outputs the value of the Variable ..f_Y12.
Topic:.Smlk_Inspc.FB_ObjOConn.appl.emxpl_supersub.
The polymorphism is an important capability of ObjectOrientation: An instance can be associated via an basic type, the usage should not know the exact implementation type. This helps to deal with several versions of implementation, maybe because a development progress, maybe because different implementation variants in a more universal basic frame. https://en.wikipedia.org/wiki/Polymorphism_(computer_science)
The below image shows an enhancement from a DataStructMng_Inspc chain of Type Controller_ObjMod
to a Type Controller_B_ObjMod
. The story to do so is:
Firstly the right chain of DataStruct are defined for the module. Other modules uses this Type Controller_ObjMod
.
As an enhancement of this module more signals should be used in its interface to outside. Some other connected modules and the test environment will be used them.
It may be easy to enhance the given DataStruct organization with the some more signals. But in this case all using modules
should be compiled newly, because the data struct
is changed. But some of the modules are tested already and the developer team cannot spend time to adapted the module. They
should be left unchanged.
Another story may be: One or some connected modules need a more universal interface. They should be connected to several special incarnations of this module type but with a more universal access. This story is the common approach for using polymorphism in ObjectOrientation. The story above is a story from the practice.
Bild: Super and Sub Type with DataStructMng
The solution, showing right, shows the first interface unchanged, with the right chain of DataStruct FBlocks. The output type
is unchanged, it is Controller_ObjMod
. The generated header files are unchanged (important!).
The blue left FBlock is the new one. It is a DataStructMng_Inspc
which thiz-Output is connected to the 7. input of the otherwise unchanged DataStructMng_Inspc
right. Because of this connection the left blue DataStructMng_Inspc receives the Type and the memory amount from the right
one, via back writing via the given reference. The left blue DataStructMng_Inspc creates the memory for both and assigns the
data of the right one as superType. The output pointer refers a data struct
with the more comprehensive data of Type Controller_B_ObjMod
but the access type is Controller_ObjMod
nevertheless. This is the known subtype/superType arrangement from inheritance in ObjectOrientation.
The used wording is:
abstraction for a more universal view to a module.
superType for the abstract access.
subType for the implementing data definition
inheritance: Usage of a more universal class (module) to build a more specialized comprehensive module.
derivation: Describes the relation between a subType and its superType, derivation and inheritance are synonym. The inheriting module has the subType.
Type is the C-type of a reference. It means the Type of a class of C++ too.
class is more comprehensive as Type. A class contains the usage as type, and all defined attributes and operations. From view of Graphical Programming a class can consist of more as one function blocks. Each FBlock can present an operation of the class. They can be assembled in a Simulink-library which presents the class.
This terms should be applicated to GraphicalProgramming with Simulink now.
Topic:.Smlk_Inspc.FB_ObjOConn.appl.parent.
A modulare design is subdivided into components, higher-level and sub-modules. This is particularly noticeable in graphic programming.
The following wording is used, in coordination to the wording of ObjectOriented inheritance and abstraction with the subType and superType:
A component is a part of an application.
A component may consist of sub components and modules.
A module can contain sub modules. Notice that in Objectorientation a subType is a more specialized but more comprehensive type as its superType, the more abstract one. Therefore the wording 'superior module' for a more comprehensive or parent module is not used to prevent confusion to the wording 'superType'.
The interface to a sub module can present by a subType which's superType is a more abstract type. The sub module is contained in its parent module.
A parent module is the module which contains a considered module.
A function block (Fblock) is the usage of a module in a Graphical Programming Diagram. The FBlock can be a module which contains some more Graphical connected Fblocks inside, or a module from a library which can present an operation of a class (see chapter above), a standard Simulink Fblock or a Sfunction, which's content in written in C(++) or maybe as Matlab script.
An Fblock can be considered as operation. In conclusion an operation can be present as module. Of course inside it can call other operations, which are present as FBlocks or sub modules.
An operation is the same as a 'method' in ObjectpOriented slang. The usage of the wording 'operation' instead 'method' is ordinary in UML too.
The usage of wording module is related to instances in this focus. 'Module' is also used for the structure of sources in programming, but that is another direction of thought.
With this linguistic basics let's have a look to build parent modules. The image above can be designated as a component, or
a parent module of some sub modules. It does contain only less processing of signals, more interconnections of its sub modules.
The DataStruct of this component or parent module is build in the same way as a all DataStruct in the above examples. Only
the input signals are typical outputs of the sub modules. Because the DataStructMng_Inspc
does not gather signals in the Tinit initial step time, some signals of the fast step time are connected. The other TinitDataStruct_Inspc
(here only one, typically maybe more) gathers the Outputs from the modules which are aggregations to the sub modules from
view of this module, wired in the Tinit
step time.
The type of data of the myDevice
is Device_ObjMod
, appoint as parameter in the DataStructMng_Inspc
FBlock.
Topic:.Smlk_Inspc.FB_ObjOConn.appl.accessSub.
Bild: Access in another component
This right image shows the access to the polymorph given data, but the polymorphismus is not really considered. This is the
module which uses the unchanged, see Chapter: 4.3 Example of polymorphism. The Input is connected to myDevice
from the chapter above, its type is Device_ObjMod
. The type have to be determined here using the checkCast_DataStruct_Inspc
FBlock. It checkes the correct type in Simulink startup-runtime and forces an error message on faulty connection. That is
important because the input is somehow wired, errors are possible.
The other importance is: This FBlock offers the type information for code generation with the Simulink or Embedded Coder.
The following get_Access_DataStruct_Inspc
or set_..
FBlocks gets the information about the type from the connected inputs (Hint: via srcBlock = LibBlockSrcSignalBlock(0, 0)
and CompiledModel.System[srcBlock[0]].Block[srcBlock[1]]
in the tlc-File).
In this module the reference and current value should be subtract from the controller sub module. Therefore the shown blue
controller
Fblock accesses this sub module reference from Device_ObjMod
. The output type in Simulink Runtime is determined by the Reflection, it is either Controller_ObjMod
or the subType Controller_B_ObjMod
, see Chapter: 4.3 Example of polymorphism. The inheritance to the subType is transparent here. The superType Controller_ObjMod
is used. Because the following FBlocks need this type information for the generated code, the type is parametrized here.
In Simulink runtime the parametrized type is tested, it is true independent of the polymorphism. Because it is tested, it
can be unquestionable used for the code generation.
The yellow boxed area can be a Fblock in a Simulink library. The Controller_ObjMod
can be established as one library. Then it can contains some such yellow Fblocks. This Fblock can be considered as operation of the class Controller_ObjMod
. It is a static (not dynamic linked) operation. The calling of a class-operation in C++ is exactly the same as the usage
of a lib-module in Simulink.
To work with dynamic linked operations (virtual), the operation cannot be included or called immediately, in C++ it is linked on runtime via the so named virtual table. The proper counterpart in Simulink is a Triggered Subsystem, see ../../smlk/html/TriggeredSubsystem.html. The triggered subsystem is arranged inside the maybe inherited main module for the controller, the inherited module can contain another incarnation for the triggered subsystem. The trigger is forced in the using module. The trigger does not know what is executed, that is the late linkuage of dynamic calls.
Topic:.Smlk_Inspc.FB_ObjOConn.DataStructMngPar.
The DataStructMng_Inspc has the following parameter:
filedir - genSource: Usual you should use a string variable here, not a 'string literal': If the variable contains , an empty string, no header will be generated. This is proper for only simulation, especially on a repeated simulation loop.
The String should be set with the directory path (absolute or relative to the simulink working dir) if the simulation is running before code generation is done. Then a headerfile
with the 'Name Type' will be written in this directory which presents a C struct
with the given connections including access routines (get/set) proper to all connected get/set_DataStruct_Inspc FBlocks. this header is used for code generation (Simulink Embedded Coder). The tlc-File for this FBlock is prepared to include this Header with invocation of the getter and setter instead the FBlock
code. Therefore some so name Runtime-Parameter are written internally inside the Simulink data which are used in the tlc-File
for target code generation. With them the FBlocks for ...DataStruct_Inspc are optimized for the target code.
Name Type: This is the name of the created header file and the name of the typedef struct
inside the header. It will be found in the generated code only. For inspector access it is the type name, not the name of
the reference, see FBlock add_DataNode_Inspc.
name1 ... name6: Access setting and name of the element in the data struct.
If the name starts with '-' one or two digits after defines the access level for the Inspector, following by colon. The first digit is the visible access, the second is the write access with same as visible access as default.
0: common access (default) and common change possibility without password.
1: password need for user access, for example for common parameter.
2: password need for maintenache access of the user, for special parameter.
3: password need for service access
4: password for developer access
7: never access, never change.
With this designations you can protect the access to data also for the target system which may implemented the inspector service too.
An asterisk before the name means, it is a handle access to another data struct, see next chapter.
After colon or the whole string is the name of the element. Do only use identifier characters (alphabetices, digits, underscore, do not start with digit). This is necessary for the generated code.
If the name is written in {...}
or start with {..
it is an initial value for a set_dataStruct_Inspc FBlock. It is only set the first time in the Tstep time slice. It means it should a constant or calculated constant value
on startup. See ccc.
Topic:.Smlk_Inspc.FB_ObjOConn.DataStructMngPar.DataStructPar.
Both FBlocks have a similar Parameter Dialog. The InitDataStruct_Inspc has additional features, therefore it is shown here.
The FBlock InitDataStruct_Inspc transfers numeric initial values or references in the Tinit step time. The DataStruct_Inspc does the same as dataStructMng_Inspc with the data. Both build a a cascade with the DataStructMng_Inspc as common last member. The last one holds the data, writes the header etc.
Parameter name1...6: Write 'Type* name' as parameter for references (handle). The type is either the type of a DataStructMng_Inspc which is connected or the C-Type of a connected Sfunction.
The Type will be checked on runtime (on start of the simulation). If the type does not match the connected input, an Simulation error is shown.
The type can be a based type of an derived-Type Block (inheritance in ObjectOrientation). The most basic type is 'ObjectJc'.
It the type is not given, it is ok. But then in this FBlock the type is not checked. It is checked inside a Sfunction where the reference is used.
Do not write a type for numeric initial values. It is adequate DataStructMng_Inspc.
Do not write a type for the FBlock DataStruct_Inspc (without Init...). . It is adequate DataStructMng_Inspc.
The Access level '-27:...' is the same as in DataStructMng_Inspc. It is possible to control how deep a customer-operator can show in the system especially with reference inputs.
Topic:.Smlk_Inspc.FB_ObjOConn.get_DataStructPar.
The get_DataStruct_Inspc can get and output the stored data in any step/sample time.
Tstep: You should note a numeric variable or a number. If the same step time is used as in DataStruct_Inspc the data are delayed 1 step. If another step time is used, the data are that data from the last execution of the step time of DataStructMng_Inspc respectively any cascaded DataStructMng_Inspc.
type: You can left empty this information writing . Then the type is inherited from the model. But the type should match to the type of the input on DataStruct_Inspc. Unfortunately the data can be gotten via the reference connection on compile time.
To facilitate the type definition in the model the type can be given here. Then a type conflict in the model is detected on compile time (update model). More as than: Differences in propagation and back propagation are determined here.
The type is given in a simple way with alphabetics. The complete list is:
'D' 'F' 'J' 'I' 'S' 'B' for double, single ('float'), int64, int32, int16 ('short') and int8 ('byte').
'U' 'W' 'V' for uint32, uint16 ('word') and uint8
'd' 'f' 'i' 's' for the adequate complex numbers, with lower cases.
'Z' boolean
If a vector output should be used, write the number of elements of a one-dimensional vector after the type ident: F3
. You can write for example 'F[3]' too (with bracket). The vector size should, but does not need the same as the corresponent
input signal. You can select lower elements or another assignment of arrays, for special cases. If you select more elements,
undefined values are outputted or the access can fail.
name: The name is searched in the connected DataStructMng_Inspc FBlock. If it is not found, an error message is shown on start of the simulation. It is adequate a From-Goto relation. But the check is done on startup time of the modell (in the first step times), not already in the check of the model.
Topic:.Smlk_Inspc.FB_ObjOConn.set_DataStructPar.
Last changed: 2018-11-14
Like shown in Topic:.Smlk_Inspc.FB_ObjOConn.appl.DataStructMngPar. the DataStructMng_Inspc and DataStruct_Inspc can define a variable but set only with an initial value. The name parameter should be written in {...}
. The value can be set with a set_dataStruct_Mng. This possibility should be discussed under the Object Oriented aspect:
Bild: Application of set_DataStruct_Inspc
The image right shows a set_DataStruct_Inspc FBlock for the signals tunefY12
. This signal is build in the controller and set set into the data of the module parOptimizer. From view of the parOptimizer it is an input for a optimizing process. The parOptimizer determines the meaning of this input. The controller should deliver the proper signal.
This image shows an experience model detail from the parOptimizer. The first variant is: Existing output values of the controller are used and combined. If the controller has the proper public data values, it is ok. In this case the parOptimizer should know which values are proper from the controler to work, it should have knowledge about the functionality of the controller. The controller does not need any knowledge about the parOptimizer.
The alternative is: The own value tune_fY12
is used which is set in the controller. In this case the controller should determine the algorithm to tune the factor f_Y12
. The module parOptimizer is only the execution instance. The controller should know which values are proper for the input to the parOptimizer. The
controler should know something about the parOptimizer, but only on its given interface. The parOptimizer should nothing know
about the controller. It gets the input and works with them, no more.
Which knowledge relation is better for the design, that is a decision of the cooperation between the modules, a design or architecture decision, not a model decision. For the modelling, both decisions are equate.
The design or architecture desicision is shaped by considerations about changing of some details of the moduls, about responsibilities or such considerations.
Bild: Parameter of FBlock set_DataStruct_Inspc
The parameter are similar the get_DataStruct_Inspc.
Tstep: The time slice where the writing in is done is determined here, not from wiring in the model. That is because writing should be defined in respect to the receiver.
type: The type string can be usually empty if the type is well defined by the input connection. The input connection type is tested, it have to be matching with the DataStructMng_Inspc-given type.
name: The name in the DataStruct.
Topic:.Smlk_Inspc.FB_ObjOConn.tree.
Last changed: 2018-11-02
If you have submodules in modules, any submodule may/should have its DataStructMng_Inspc FBlock which presents the module data.
In the superior module the handle of the submodules are connected to the DataStructMng_Inspc of this module. The next image show that:
You see a snippet of a modulare structure. Some modules have its thiz
output. They are connected to a 'goto' FBlock, a corresponding 'from' FBlock is the source for an input of DataStructMng_Inspc. Usage goto/from is a proper design style to prevent too much lines for regular connections.
The inputs which comes from the submodules, repspectively all inputs which are handles (from in C written Sfunctions too are
marked with *
before the name. It is semantically known from the pointer access in C-language. Here it means, the input is not a simple
uint32
, it should be recognized as handle.
You can access from outside to a deeper level in a module/submodule with the Out_DataStruct_Inspc FBlock with writing a path separated by a dot:
Bild: Access path to a submodule
From view of the model it is similar a 'from' from a global 'goto': It is a stand alone FBlock with a textual given source. But global goto/from can be seen as nuisance for modularization. The source of this block is systematically.
From view of ObjectOriented access it is an access to an inner class (concatenated associations) whereby the inner class is gotten via its direct reference, not with a get-method. It is quite usual in ObjectOrientation. It saves access routines respecitively ports and lines in the model.
Topic:.Smlk_Inspc.FB_ObjOConn.inspc.
Last changed: 2018-11-02
The Inspector Service cann access all data which are based on ObjectJc, and all data which's reflection are known beside its struct definition. The DataStructMng_Inspc is based on ObjectJc. The reflection of DataStructMng_Inspc are created dynamically depending of the model and connected signals.
You only need connect the most superior DataStructMng_Inspc to the Inspector Service. All submodules are successfully connected if you build a tree of DataStructMng_Inspc proper to the tree of module structure.
The image shows the connection:
Bild: Connection to Inspc Service
A add_DataNode_Inspc FBlock is used. That FBlock contains an argument for the name of the DataStructMng_Inspc data struct for the access. For the inspector service concept see Chapter: 9 Connection and Access via Inspector Service.
Bild: Fiels of fist level In this constellation you can open the Inspector-Tool and view fields of the first level. You see:
More aggregations (modules) are existing if more as one are registered via add_DataNode_Inspc, for example parts or the whole simulation environment.
If you click into myDevice
you see the modules and signals of the device of the first level according to the DataStructMng_Inspc FBlock and the deeper levels:
If you have the access password:
The passwords are hard coded in the standard implementation of the Inspector GUI and the emc access sources:
111 till 444 for the 4 access levels, 11 only access, not modify, till 44.
If you use the Inspector and a target system for a customer, you should implement the necessary special password protection.
Bild: developer access You see more fields, especially 'super' via super.thiz1
you can access internas of the FBlock to view its implementation, as developer access:
This shows data, which are gathered from the simulink model on startup of the simulation: TstepMin = minimum of step times of all inputs, here 0.0001 s, index of the ports etc.
Topic:.Smlk_Inspc.FB_ObjOConn..
There are two contributions:
1) The FBlock DataStructMng_Inspc
-instances produce C-Headerfiles and C-files depending on its parameter and connected inputs in the Simulink run time (on test of the model). That files are used in the
target code.
2) The special adapted tlc-files for this FBlocks organize the call of that code.
Topic:.Smlk_Inspc.FB_ObjOConn..DataStructSrc.
The DataStructMng_Inspc
has a parameter fileDir - genSource. This parameter should be set with a variable. If the model runs in some simulation situations, that variable should be set
with an empty string ''
. Then no code generation is done. For the last simulation run before code generation that variable should be set with the
file path to the directory where generated files are stored. Then the generation is done. See as example the _init.m
mat script:
%dstPathGenDataStruct = ''; %%left empty if no code generation is used yet. dstPathGenDataStruct = 'test/+ObjOModule_ExmplCtrl/Cprj/genSrcInspc';
Bild: Parameter DataStructMng_Inspc
The last directory of this path have to be genSrcInspc
like shown here. This directory is used in the generated source in the include statement, see below.
In the headerfile a struct
will be created with the type and parametrized name constellation of the instance in the model- The file looks like (example
.../genSrcInspc/)
struct PID_Ctrl_t; struct MeasPrep_ProgrSimpleExmplA_t; typedef struct Controller_ObjMod_t { union { ObjectJc object; } base; float W[2]; int xMeas; float yctrl[2]; ..... struct PID_Ctrl_t * pid2; struct MeasPrep_ProgrSimpleExmplA_t * xMeasRef; } Controller_ObjMod_s;
The struct
is based on ObjectJc
which is necessary for ObjectOrientation for type check and for reflection access (Inspector). In a spare target software
constellation this base structure can be reduced to 1 int variable for an ident number and the index of the type. This is
enough for the Inspector-Target-Proxy concept (see InspcTargetProxy) or for low level debug.
The data names come from the parameter, see right image. The types come from the connected inputs from the model. The access rights are for the Inpector access. Not parametrized names or not used inputs do not contribute to elements in the struct.
Bild: Parameter InitDataStruct_Inspc
The two last elements in this struct
comes from the chained FBlock. Because it is a Init...
FBlock the struct
type is given and used as forward declaration. All chained FBlocks builds one struct. The FBlock Type DataStructMng_Inspc
is the manager of the chained blocks.
. . . . . . . . . . .
The headerfile contains the prototype for the constructor and the write...
routines for the DataStruct definition FBlocks:
void ctor_Controller_ObjOModules(Controller_ObjOModules_s* thiz); /**Write data to DataStruct. * Note: The Simulink coder cannot decide whether it is a scalar, vector or poitner * Therefore all arguments are unique given as address (pointer) */ void init__Controller_ObjMod(Controller_ObjMod_s* thiz, float* W, uint32* xMeas, float* yctrl, int32* testtt, float* Y); void step__Controller_ObjMod(Controller_ObjMod_s* thiz, float* W, uint32* xMeas, float* yctrl, int32* testtt, float* Y); ..... void init_i3_Controller_ObjMod(Controller_ObjMod_s* thiz, uint32* pid2, uint32* xMeasRef);
Any FBlock of the chain have an init_...
and a step_...
routine. They are called in the Tinit
step time for initialization and in the appropriate step time of the Fblock, which can be different for each chained block.
It is organized in the tlc files.
The getter routines for all data elements are generated as inline
functions to get a fast access:
inline void get_W_Controller_ObjMod(Controller_ObjMod_s* thiz, float* W) { memcpy(W, thiz->W, sizeof(thiz->W));//float } inline void get_xMeas_Controller_ObjMod(Controller_ObjMod_s* thiz, int* xMeas) { *xMeas = thiz->xMeas; }
The calling environment in the modell does not know the type of the connection, only the name. Therefore all accesses are
done with a address reference, which is the reference of the destination variable. But the type of the data are well defined
in the generated code from the simulink coder. The here generated operations knows the type and vector situation from the
simulink running environment. Both matches, if the code generation for the model with the Simulink Coder and the generation
of this sources is done with the same model situation. Elsewhere the discrepancy is detected by an compilation error because
pointer type mismatch. If a vector access used in the model, a memcpy
is done in the generated code. The size of the vector is defined by the sizeof(...)
of the destination variable from generated code proper.
From another example header ParameterOptimizer_ObjMod.h
:
typedef struct { .... float tune_fY12; struct Par_PID_Ctrl_t * par_PID; } ParameterOptimizer_ObjMod_s; inline void get_par_PID_ParameterOptimizer_ObjMod(ParameterOptimizer_ObjMod_s* thiz, uint32* par_PID) { *par_PID = HANDLE_Handle2Ptr(thiz->par_PID); }
Bild: Out_DataStruct_Inspc_Tinit
The get_par_...(...)
is the access with the right shown FBlock: The type in the model and in the generated source is uint32
, a handle. The meaning of the data is a reference (address, pointer value). In the struct
the exact typed reference is stored how it is given in the appropriate DataStructInspc
definition block where the type of the handle/reference is parametrized. The access converts the address value in the struct
to the handle value. For a 32-bit target system it is an empty macro, only a type casting.
. . .
This is the appropriate InitDataStruct_Inspc
FBlock, which's parameter defines the type. The type is tested in the Simulink run mode by reflection check, used for code
generation and used in the target software. The variable of the code generated code are of type uint32
. The HANDLE_Handle2Ptr
is a simple macro which contains only a type cast if the target system has 32-bit-addresses. The address is stored immediately
in the uint32 variable of the model. On low-level-debugging (single step) in the target code the correct type can be evaluated
for showing the content in the debugger from the struct
variable, that is explicit helpfull. The type is used for Inspector access on the target too. The reflection generation is
done from this generated header file.
inline void set_tune_fY12_ParameterOptimizer_ObjMod (ParameterOptimizer_ObjMod_s* thiz, float* tune_fY12) { thiz->tune_fY12 = *tune_fY12; }
The generated code for the right FBlock is shown above. It is adequate the get routine. direction is reverse. For vector assignments
a memcpy
is used too.
. . . .
Bild: DataStruct_forSet_ParamDlg
The special provocation for the set FBlocks is: The associated DataStruct_Inspc
FBlock must not set this value in any step time, only in the Tinit time as initial value. This is done via the 100
value of access rights
in the parameter dialog. The 107
means additionally read only access.
Topic:.Smlk_Inspc.FB_ObjOConn...
Some manually adapted tlc files are contained in .../lib/+mkSfn/tlc_src/*.tlc
and copied from there with the mex compilation to the adequate mex/tlc_c
directory. The automatic generated tlc files (see Topic:.Smlk_de.C_ObjO.genSfH.) are stored as .../lib/+genSfn/Inspc/*.tlc
The tlc file is hand-written. The get_Access_DataStruct_Inspc_SfH.tlc
contains the following code (shorten):
%function OutputsForTID(block, system, tid) Output ..... %if tid == tidStep %assign srcBlock = LibBlockSrcSignalBlock(0, 0) %assign sys1 = srcBlock[0] %assign blk1 = srcBlock[1] %assign block1 = CompiledModel.System[sys1].Block[blk1] %with block1 %assign typeData = SFcnParamSettings.typeData_param %endwith { //The input is the reference to the created struct from DataStructMng_Inspc. The connected ... //The dwork is not used: %<LibBlockDWork(thiz_h, "", "", "0")> %<typeData>_s* pData = PTR_Handle2Ptr(%<LibBlockInputSignal(0, "", "", 0)>, %<typeData>_s); *(%<LibBlockOutputSignalAddr(0, "", "", 0)>) = get_%<SFcnParamSettings.name_param>_%<typeData>(pData); } %endif %endfunction
The generated codes come from the parameter settings, not from the C-functions of the Sfunction:
The block connected on inport 0 is detected, the srcBlock
.
The typeData
is the parameter name_Refl_param
of this block. The value of this non-tunable parameter is written to the rtw coder data via the mdlRTW(...)
routine, see Sfunction-Wrapper.
The %<typeData>_s*
is the pointer type of the input signal, which comes from that source block.
It is also the suffix for the get_variable_Type(ptr)
access routine. This access routine is assembled in the tlc code.
pData
is the pointer to the data gotten from the inport handle. The PTR_Handle2Ptr(...)
converts the handle value on the input to the really pointer. It is the same value (simple macro) for a 32-bit-target system
because the handle value on input is the pointer. The macro is important for Accelarator mode which runs in a 64-bit-Environment.
LibBlockOutputSignalAddr(0, "", "", 0)
is the destination variable address. This is assigned with the assmebled get_...(pData)
routine.
The get_...(pData)
routine is generated in the header file, see 1)
Adequate it is for the set_DatatStructMng_Inspc_SfH
:
%function InitializeConditions (block, system) Output //Initialize the handle2Ptr-Mechanism on 64-bit-Systems if not done already: INIT_Handle2Ptr(); { //jzTc: malloc for %<Type> %<ParamSettings.FunctionName>: %<Name> %assign hthizAddr = LibBlockDWorkAddr(thiz_h, "", "", "0") //create data for SFBlock= %<LibGetFormattedBlockPath(block)>, Type= step_DataStructMng_Inspc_SfH %<SFcnParamSettings.typeData_param>_s* thiz = (%<SFcnParamSettings.typeData_param>_s*) malloc(sizeof(%<SFcnParamSettings.typeData_param>_s)); memset(thiz, 0, sizeof(Data_TestDataStruct_genc_s)); init_ObjectJc((ObjectJc*)thiz, sizeof(Data_TestDataStruct_genc_s), ident_newAllocated_ObjectJc); //set the length in ObjectJc for test. //NOTE: for 64 bit accelerator mode: register the pointer. Set hthiz. For 32-bit target it is a simple macro. registerPtr_Handle2Ptr(thiz, "%<SFcnParamSettings.typeData_param>", %<LibBlockDWorkAddr(thiz_h, "", "", "0")>); } %endfunction
The DWork pointer is used in the originally Sfunction Wrapper to store the thiz-pointer to the struct DataStructMng_Inspc_t*
which is more complex. For the generated code only the generated struct ...Type...
with the Type
from the paramter settings are present. The Type is stored as typeData_param
. The data are created dynamically in the target system too, the handle is stored in the DWork.
%function UpdateForTID(block, system, tid) Output %assign tidInit = LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx0") %%The TID for step_, the first step input port. %assign tidStep = LibGetGlobalTIDFromLocalSFcnTID("InputPortIdx0") %%The TID for step_, the first step input port. %if tid == tidInit %%nothing Update in init %elseif tid == tidStep { //jzTc: update for %<Type> %<ParamSettings.FunctionName>: %<Name> (%<LibGetFormattedBlockPath(block)>), TID = %<tidInit> %assign hthiz = LibBlockDWork(thiz_h, "", "", "0") %<SFcnParamSettings.typeData_param>_s* thiz = PTR_Handle2Ptr(%<hthiz>, %<SFcnParamSettings.typeData_param>_s); %% %%It checks the name parameter and the connection on an input. Not connected inputs: the possible element remain unchanged, it is 0. %% %if (SFcnParamSettings.n1_param !="" && LibBlockInputSignalConnected(0)) %%only on given name: thiz->%<SFcnParamSettings.n1_param> = %<LibBlockInputSignal(0, "", "", 0)>; %endif %if (SFcnParamSettings.n2_param !="" && LibBlockInputSignalConnected(1)) %%only on given name: thiz->%<SFcnParamSettings.n2_param> = %<LibBlockInputSignal(1, "", "", 0)>; %endif %if (SFcnParamSettings.n3_param !="" && LibBlockInputSignalConnected(2)) %%only on given name: thiz->%<SFcnParamSettings.n3_param> = %<LibBlockInputSignal(2, "", "", 0)>; %endif %if (SFcnParamSettings.n4_param !="" && LibBlockInputSignalConnected(3)) %%only on given name: thiz->%<SFcnParamSettings.n4_param> = %<LibBlockInputSignal(3, "", "", 0)>; %endif %if (SFcnParamSettings.n5_param !="" && LibBlockInputSignalConnected(4)) %%only on given name: thiz->%<SFcnParamSettings.n5_param> = %<LibBlockInputSignal(4, "", "", 0)>; %endif %if (SFcnParamSettings.n6_param !="" && LibBlockInputSignalConnected(5)) %%only on given name: thiz->%<SFcnParamSettings.n6_param> = %<LibBlockInputSignal(5, "", "", 0)>; %endif } %endif %endfunction %% Update
The update
routine gets the handle, converts to the pointer with a simple macro for the target system because the handle and pointer
are equal.
The input values are stored only for given names and given connections. If a name is given but a connections is not present in the model, nothing is stores for this attribute, it remains 0.
Not used inputs are not generated.