Previous: , Up: Using Dezyne in your C++ environment   [Contents]


3.2.3 Inspecting the generated system

Up until this point you should have completed the following tasks:

Before we start on writing implementations for the various components of the Alarm System, it is important to have a closer look at the generated code from the System component.

Recall that a System component is used to specify how components within the system interact with one another and how they are connected to the environment outside of the system. This specification of connection to the outside world is why using a System component is so important; it is where and how your handwritten code will interact with the code generated from verified models.

The iteration of the Alarm System from the Dezyne Basics we will be using for this can be found at https://github.com/VerumSoftwareTools/DezyneTutorial/tree/master/Code_Integration/Ch1_Starting_Point. If you generate code from these models, you should end up with the following files:

images/generated

As the name of the System component specified in the Dezyne model is AlarmSystem, the generated files have been named AlarmSystem.cc and AlarmSystem.hh as well. Let’s start by having a look at AlarmSystem.hh. This file contains a definition of a struct AlarmSystem which shares some similarities with the System component as specified in Dezyne, which are highlighted in the snippet below:

struct AlarmSystem
{
  dzn::meta dzn_meta;
  dzn::runtime& dzn_rt;

  const dzn::locator& dzn_locator;
  Controller controller;
  LED led;

  IController& iController;

  AlarmSystem(const dzn::locator&);
  void check_bindings() const;
  void dump_tree(std::ostream& os=std::clog) const;
};

Most notably, it specifies which ports can be accessed on the AlarmSystem object (iController, in this case). This directly maps to what ports are available on the boundaries of the system. Another important factor is the definition of the AlarmSystem constructor, which requires a dzn::locator object to be passed as parameter. This is the case for all components and systems generated from Dezyne models, but luckily the System component will take care of distributing the dzn::locator for you!

images/system_4

The other file that was generated from the System component, AlarmSystem.cc, contains the implementation of functions declared in the header file. The constructor generated for the system looks as follows:

AlarmSystem::AlarmSystem(const dzn::locator& dezyne_locator) :  dzn_meta{"","AlarmSystem",0,0,{},{&controller.dzn_meta,&led.dzn_meta},{[this]{iController.check_bindings();}}}
, dzn_rt(dezyne_locator.get<dzn::runtime>())
, dzn_locator(dezyne_locator)
, controller(dezyne_locator)
, led(dezyne_locator)
, iController(controller.iController)
{
  controller.dzn_meta.parent = &dzn_meta;
  controller.dzn_meta.name = "controller";
  led.dzn_meta.parent = &dzn_meta;
  led.dzn_meta.name = "led";
  connect(led.iLed, controller.iLed);

  dzn::rank(iController.meta.provides.meta, 0);
}

The contents of this file look a lot more daunting, but again you can relate some parts of the implementation to lines in the actual Dezyne model. For instance, the binding of ports that was done by controller.iLed <=> led.iLed in Dezyne can be traced to an invocation of the connect(led.iLed, controller.iLed) function.

In the implementation of the System’s constructor most of the magic happens, as a chain of constructors of components within the system is set off and the dzn::locator container (and as such, the dzn::runtime) is spread across the system. Using a System component for this purpose ensures that every component is using the same runtime, which is essential for Dezyne’s functionality. For every Dezyne application you create, make sure to embed the components in a System component.

Interaction with the AlarmSystem object in your event loop is how the logic modeled in Dezyne will perform in your fully integrated application. Later in the section you will see how interaction with the System on its exposed ports will work, but to conclude this section on preparing the C++ environment let’s do exactly that; prepare the main loop for use by creating the System object.

Most of the initialization steps were already completed in the earlier draft of your main function; the runtime files were included and the respective objects were created. What remains is to create an object of the AlarmSystem struct type and pass the locator to it. Then, before entering the event loop, it is recommended to invoke check_bindings() on the System. This is a Dezyne functionality that ensures that all ports have been bound properly. The result will look like this:

#include <dzn/runtime.hh>
#include <dzn/locator.hh>

int main(int argc, char* argv[]) {
  dzn::locator loc;
  dzn::runtime rt;
  loc.set(rt);

  AlarmSystem as(loc);

  as.check_bindings();
  while(true) {
    // Dezyne interaction
  }
}

Previous: , Up: Using Dezyne in your C++ environment   [Contents]