Operations in UML2 are usually part of classes, as such the code generation works as follows.
For methods, a parameter is inserted which should point to an instance of the generated C structure. The default name of this parameter is me (in C++, the object orientation support of the language is used, so this reference is called this). For method calls in activity or state machine diagrams, this parameter is automatically assigned to me. For details how the name of an operation is generated, see the Naming chapter.
For static operations (functions), there is no such automatic parameter introduction. Find out more about Static Operations!For singletons, the situation is similar. Find out more about Singletons!


Naming


Naming of Operations is a very important topic for C-Code generation, especially if you use the generated code from hand-written code.
Keep the following simple rules in mind, and you won't have any problems with operation naming:

  • If a method is defined by an interface (either directly, or because it's part of the realization of an interface), the method name will not be altered.
  • If the class has the stereotype executable entry point and the classifier behavior points to the method, the method name will not be altered.
  • Otherwise, the method name will be prepended by the class name and an underscore, e.g. ClassName_MethodName.

If you need a method to have the same name in C as in UML, you can therefore just create an interface and realize this interface (Dependencies). It's good practice that if you depend on a name, it should be defined in an interface.

Parameters

Parameters can also be augmented with properties allowed by UML, Stereotypes and/or Tagged Values, but one of the most common issue regarding Parameters is on how to set pointers, constants and types and all their combinations, with all their different meanings.

Stackoverflow: Difference between const int, int const and const int pointer,...

We think the currently best/fastest/easiest solution to this issue is to use the a Primitive Element to set its name to any one of the needed combination and use this Primitive Element as the type for the Parameter.

Call Chain

For operation calls, the code generator tries to resolve the value of the parameters in the order of the call chain.

What is a call chain? It's a succession of calls where one behavior just calls another which calls another and so on. The first behavior of a call chain is the caller, and the last behavior is the behavior for which code will be generated (the intermediate behaviors are not visible in the generated code), the callee.

Parameters must have the same name in every part of the call chain; the value can be defined by specifying the Default Value for that parameter, or by using parameter nodes in activities. If a parameters value is defined more than once in the call chain, the first definition is used and subsequent ones are ignored.

To define parameters for transition effects, where the effect is a behavior, the effect must either be written literally as code or an intermediate behavior without parameters must be introduced where you can define the parameter values.

Parameter Containment

When modeling operations for classes the containment of a parameter can be set by setting the Direction to inout.

This can also be achieved by adding a ReferenceType tagged value with the prefered reference type to the parameter.

The ReferencType tagged value can also be used to overwrite/change the reference type generated by the "inout Direction"


Static Operations

Static operations get a special treatment in Embedded Engineer. While the generated code will be very similar to that of methods, keep the following differences in mind:

  • Static operations have no access to an instance of the class they're defined in. There might not even be an instance if they are part of a static class.
  • When you define static operations in singleton classes, you also don't have access to the instance.

Initialization Code

You can define an initialization method by giving an operation in your class the stereotype constructor. If you do so, the method will be generated as usual, but it will also contain code to initialize the state machines and the default values of the class attributes.

Function Preambles

To fully define a function, it is sometimes neccessary to add code in front of the function implementation.
For example, if you want to define an interrupt handler, you might need to add a #pragma directive or other macro invocation in front of the function.
If you want to add such a preamble, add the FunctionPreamble tagged value to your operation. The text you add there will be written directly in front of the generated function implementation. You can also do this with the help of the Toolbox.

This will result in the following code (given that the return type is void, there are no parameters, and the operation is static):

#pragma vector=TIMER0_A0_VECTOR
__interrupt void HandleTimerInterrupt(void)
{
}

What you define in the FunctionPreamble is written directly before the operation to enable the common use cases. Please take care when using this functionality and check that your FunctionPreamble is defined correctly.

Behavior of Operations

Enterprise ArchitectMagicDraw

You can add behavior to an operation in two different ways:

  • By adding verbatim code to an operation, which is inserted in the generated method body. This will result in a function which contains the code you type in the field Initial Code (check yourModel SettingUser Code storage field). No code will be added after your code, but the code generator might insert code neccessary to support the singleton pattern in front of the user supplied code.
  • By referencing an activity which will be called when the operation is called. This will result in a function which calls the supplied activity. If the activity has a return type, and this type corresponds to the return type of the operation, a return statement will be added and therefore the return value will be propagated to the caller.

Adding a behavior in the Method property of the operation.

When clicking on the "edit" button you can either select or create a new behavior.

Supported behavior types:

  • OpaqueBehavior
  • FunctionBehavior
  • Activities

If multiple behaviors are defined only the first will be selected by the Code generation.

Function pointer

To generate a function pointer multiple elements need to be generated and linked:

Deprecated

This implementation is depricated since version 3.4 please use the new implementation (see below) if you use version 3.4 or above

  1. Generate a type definition for the function pointer:
    1. Create an operation
    2. Add parameters
    3. Add the stereotype "typedef"
  2. Generate an attribute for the function pointer itself
    1. Create an attribute
    2. Add the stereotpye "functionPointer"
  3. Link both elements together
    1. Open "FunctionPointer" tagged value of the attribute 
    2. Select the type definition operation for the function pointers


Generated code
/* Operation 'FncPointerDefinition' of class 'Class1' */
typedef void (*FncPointerDefinition)(int, byte, char);

typedef struct Class1Struct
{
    FncPointerDefinition FncPointerAttr;
} Class1;
  1. Generate a type definition for the function pointer:
    1. Create an operation
    2. Add parameters
    3. Add the stereotype "typedef"
  2. Generate a Primitive Element as type for the function pointer itself
    1. Create Primitive Element
    2. Add the stereotpye "fncPointer"
    3. Set the "Signature" tagged value to point the operation created in step 1.
  3. Use the new "fncPointer" type
    Now you can simply create an Attribute or an Operation with a Parameter and set the type of the element (Attribute, Operation parameter,...) to the Primitive Element created in step 2. 

Generated code
/* Operation 'FncPointerTypeDef' of Class 'NewImpl' */
typedef void (*FncPointerTypeDef)(int);

typedef struct NewImplStruct
{
    FncPointerTypeDef FncPointerAttr;
} NewImpl;


/* Operation 'FncPointerParam' of Class 'NewImpl' */
void NewImpl_FncPointerParam(NewImpl* const me, FncPointerTypeDef test);

See also

Dependencies (Realization)

Primitive Element



  • No labels