Let’s have a look at the situation currently at hand. If all went well, you should have a file which includes your main function, in which Dezyne runtime components were created and an instance of the Dezyne System was created. We have had a brief look at how the System controls its internal components and how to access ports on the System’s boundary.
The next step in the integration process is to implement the functionalities that could not be modeled in Dezyne such as driving hardware and manipulating data. To do this, though, you need to know two things: what needs to be implemented and where to implement it to make integration as smooth as possible.
The quickest way to figure out what must be implemented as foreign code is by having a look at the System view in Dezyne. You may have already noticed there are two differently colored components in the AlarmSystem; one is a darker green, whereas the other is blue. These colors can be used to quickly assess whether a component was implemented in Dezyne. A blue component in a system indicates that the component in question was defined as a component without behavior. This should set off some alarm bells (pun very intended) because no behavior means no functionality. This leads us to a first conclusion: blue components in a System view must be implemented in foreign code.
The second indicator of implementation that must be done as foreign code is the presence of ports on the boundary of a System in the System view. Such a port, as you can imagine, means that an interface is provided (or required) but there’s no entity in Dezyne that is making use of the provided interface (or implementing the functionality required by the System). This leads us to the second and last conclusion: ports on the boundary of a System must be bound in hand-written code.
So now that we’ve identified what needs to be implemented it’s
time to look at where to do so. In the case of a blue component,
a very specific method of implementation must be followed. You may have
noticed the generation of skel_LED.cc and skel_LED.hh
earlier on during the section. These ‘skeleton’ files are generated for
every blue component in a System. In these files, you will find an
abstract class with pure virtual function(s) in the ‘
namespace. The implementation of the abstract class must be
done in files called LED.hh and LED.cc and the
struct/class that inherits from
skel::LED must be named
LED. Although this may feel restrictive, this approach has
considerable upsides too: the System becomes responsible for the
distribution of runtime objects in the component and through the use of
abstract classes and pure virtual functions you can verify that your
implementation is complete compile-time instead of discovering such
things run-time. We will cover the integration process of blue
components in more detail later in Implementing generated ‘skeleton' components.
In case abstract or pure virtual are unfamiliar terms to you, please refer to the introduction of this section where some links to additional information are given .
The second case we discussed was ports on System boundaries. The implementation method for this case is not as restrictive. The implementation may be done wherever you like, as long as all events on the interfaces of the unbound ports are bound eventually. This sounds a lot easier than having to deal with abstract classes and pure virtuals, and it is, but it comes with the tradeoff of bearing more responsibilities as developer.
In the next section, we will cover the implementation of events on unbound ports.
In general, it is recommended to encapsulate your application’s functionalities as components in a System. It increases the cohesion of the overall application and it makes it much easier to find the whereabouts of hand-written code in respect to the overall system. An added benefit is that all sorts of useful meta-information is automatically generated by Dezyne while your application is running which can help tremendously in tracking the behavior of your application.