Table of contents
Attributes in a CAN database consists of two parts, the definition and the
actual value.
The definition is a kind of typedef of the attribute. The principal
property of an attribute is the kind of network object it relates to. An
attribute either relates to the complete CAN bus or to a node in this bus
or to a frame or to a signal. The different kinds of network objects
share the same name space for attributes. The same name can't be reused for
different attributes, which relate to different kinds of network objects.
Therefore a single unstructured collection can be applied to keep all
attribute definitions. The code generator uses a map object, where the
name of the attribute is the key to retrieve the attribute's definition.
In the attribute definition it is decided whether the attribute is a
numerical, a text value or an enumeration. For numericals the range is
defined and a distinction between integers and floating point numbers is
made. For enumerations all the named values are specified. Furthermore,
for any attribute a default value is specified.
The actual value of an attribute is an optional element in the database.
By default, all instances of a given kind of network object inherit the
default values specified in all the attribute definitions, which relate to
this kind of network object. An actual value can be specified for
individual instances of a given kind of network objects. In this case the
default value is overruled by the specific value. Because of this
principle all objects of a given kind of network object have the same set
of attribute value; most of them will be set by default but some might
have individual values.
In the code generator's data model the attribute definitions are collected
in the map Bus.attribDefMap, which is owned by the Bus object (as this
object corresponds to the contents of a single CAN network database file).
For convenience only, the same set of attribute definitions is stored in a
linear list, too. The later is is somewhat better suited to present all
attribute definitions in an overview, primarily relevant for documentation
purpose. For C code output the code generator may address to this map to
generate tables of min and max values or lists of #defines with all an
enumeration's named values, etc.
The individual values are stored in the base class NetObject of Bus, Node,
Frame, Pdu and Signal. Here, you find the map NetObject.attribMap,
which holds all actual values of the instance of NetObject. The value
either is a default value or an individually set one but it'll always be
accessible through the map. A fragment of a StringTemplate V4
template, which generates a list of preprocessor defines with all the
frames' send periods, could look like:
listAllFramePeriods ::= <<
<cluster.busAry:{bus|<bus.frameAry:{frame|#define <\\>
FRAME_PERIOD_<bus.name>_<frame.name>_<frame.id> <frame.attribMap.sendPeriod><\n>}>}>
>>
The map access is done by the construct <frame.attribmap.sendperiod></frame.attribmap.sendperiod>. It
returns the value of the attribute; in our example the construct will
expand to an integer number.
The example before assumes that the network database file knows the
attribute sendPeriod, which indicates the send period time (typically in
Milli seconds) and is thus dependent on the attribute definition in the
given environment.
In general, templates, which evaluate attributes will never be general,
i.e. independent of the environment the code generator is used in.
Although there are conventions on the use of attributes like
GenMsgSendTime for the send period in Milli seconds this can never be
guaranteed and a template will fail if the actual network database uses
deviating conventions. To a certain extend, this can be overcome by
configurable templates. <frame.attribmap.sendperiod></frame.attribmap.sendperiod> could be replaced
by <frame.attribmap.(sendperiodattribname)></frame.attribmap.(sendperiodattribname)>, where sendPeriodAttribName
is a string attribute set as user option on the command line, or by
<frame.attribmap.(sendperiodattribname())></frame.attribmap.(sendperiodattribname())>, where sendPeriodAttribName
is a trivial sub-template in a configuration template group file, e.g.
sendPeriodAttribName() ::= "GenMsgSendTime";
However, these constructs make the template code cumbersome and they will
not work in general as not only the name but also the semantics of an
attribute can vary between network databases. Just consider the situation
that one database specifies the period time as integer in Milli seconds
and the other one as floating point number in seconds.
The probably better approach is making the template safe by testing some
properties of the attribute (presence, type, range) and to put a compiler
error into the generated code in case of suspicious deviations:
<if(!bus.attribDefMap.sendPeriod || !bus.attribDefMap.sendPeriod.isInt<\\>
|| !bus.attribDefMap.sendPeriod.isFrame)><\\>
#error Please check your template: Required network database attribute <\\>
sendPeriod not found or of bad kind
<endif>
The template construct <netobj.attribmap.myattribute></netobj.attribmap.myattribute> retrieves the
attribute myAttribute of network object netObj. The expression
actually retrieves the Java object of the data model that describes the
attribute. StringTemplate V4 renders this object via its method
toString.
The implementation of method toString returns a textual representation
of the value of a numeric attribute or the text of a string attribute or
the value's name for an enumeration attribute. However, accessing the
fields of the Java attribute can give more control to the template;
<netobj.attribmap.myattribute.n; format="%.2f"></netobj.attribmap.myattribute.n;> would for example
retrieve the value of a floating point attribute as a Java Double, which
permits to use the numeric renderer of StringTemplate V4 to exactly
specify how the numeric value is represented as text.
Many attributes are enumerations. These attributes will often be used in
conjunction with conditional code generation. As an example, the send type
of a frame could state whether we have a regular, strictly periodic
transmission or an event triggered, happening only on data change. The
code generator can handle these kinds of different behavior by querying
the attribute. StringTemplate V4 is limited to the query of Booleans with
its <if(bool)></if(bool)> construct. This is why enumeration attributes have the
map is as additional member. This map has a single key, value pair. The
key is the enumeration value name of the actual value of the attribute.
The value in the map is a Boolean true. If the StringTemplate V4 template
accesses the map with the right enumeration value name then it'll get a
Boolean true and in all other cases a Boolean false (the default behavior
for not matching fields).
In the example above, if the frame attribute would be named
sendCharacteristics and given it would have the three named values
undefined, regular and eventTriggered, then the template could use
the following construct to generate conditional code for different kinds
of frames:
<if(frame.attribMap.sendCharacteristics.is.regular)>
// <frame.name> is a regularly sent frame.
// ... Put your C code generation to handle regular frames here
<elseif(frame.attribMap.sendCharacteristics.is.eventTriggered)>
// <frame.name> is an event triggered frame.
// ... Put your C code generation to handle event triggered frames here
<else>
#error Unexpected type of frame found for <frame.name>. Please, double <\\>
check your network database file <bus.networkFile>
<endif>
If your network node is connected to more than a single CAN bus you will
probably generate code for all buses. This can of course be done in
completely separated runs of the code generator and yielding independent
sets of C sources but it might be more suitable to combine all buses. The
code generator can read any number of network databases. The data model
has a list of related Bus objects.
Concerning attribute handling, this is beneficial only if the different
databases share the same convention for defining attributes or if there
are only minor deviations. Different name but same meaning could e.g. be
tackled with the construct <netobj.attribmap.(attribname())></netobj.attribmap.(attribname())>, where
attribName() could be a sub-template, which uses <bus.name></bus.name> to lookup
the bus specific attribute name in a StringTemplate V4 map object.
A typical code pattern for generated C code, particularly in the context
of attribute evaluation, is the initialized data structure. This pattern
is for example heavily used by AUTOSAR code generators. The functional or
algorithmic parts of the software are developed conventionally. They
operate on data tables, which contain all the variable parts of the
system. The functional code implements a model of buses, nodes, frames,
signals, transmission modes and (specific, supported, required) attributes
but doesn't know about any particular bus, node, frame, signal or
attribute. Instead, it has a reference to a data structure of fixed, known
structure but variable contents. The reference is declared such that the
data structure is an external object to the functional code.
The code generator reads particular network databases and knows about all
the particular objects; it can easily generate a C source file, which
contains the definition of the missing data structure. The required code
will consist of only a few or even only one statement. These few
statements typically have many hundred lines of code as all buses, frames,
signals, etc. are stored in the data structure. The data structure is
defined as const in order to not consume expensive RAM.
Although such data structure initialization code is hard to read and
maintain by a human is it rather straightforward to design a
StringTemplate V4 template, that renders the internal data model in such a
typical C initializer expression. A strong recommendation is to use the
designated initializers of C99 for this purpose, for sake of much better
readability of the generated code. Although available for nearly 20 years
is this technique barely applied by commercial code generators.
The functional code will most probably require the set of pack and unpack
functions, that compose frames from signals and decompose frames into
signals. The generation of these functions is the core competence of the
code generator. You will probably generate these functions in a second C
source file and add the function pointers to the large data structure,
like additional attributes of the frames. From here, the functions can be
individually accessed by the handwritten functional code. Reusable
StringTemplate V4 templates for the generation of the pack and unpack
functions are part of the code generator's distribution.
Separating the algorithms from the data avoids the problem that the
functional parts of the software need maintenance through editing
StringTemplate V4 template files, which is much more tricky than
conventionally maintaining a C source file.
Applying this pattern means to write your functional code once, with a
high degree of reusability in different environments and with different
network databases. And a rather simple configuration of the code generator
on the other hand.