Skip to content

Calculators example

This example illustrates how you can create a package that holds two implementations of a class defined in STL.

Interface

This package provides the definition of a calculator.

calculator.stl
package calculators;

// A simple calculator that can add and divide numbers.
class Calculator
{
  // The model of the calculator
  var model : string [static];

  // The last result, or what is currently presented at the screen.
  var current : f32;

  // Returns "a + b" and sets the last result to it.
  //
  // @param a left-hand operator.
  // @param b right-hand operator.
  fn add(a: f32, b: f32) -> f32;

  // Returns "a + R" where R is was the last result.
  //
  // @param a left-hand operator.
  fn addWithCurrent(a: f32) -> f32;

  // Returns "a / b" and sets the last result to it.
  //
  // @param a left-hand operator.
  // @param b right-hand operator (if 0, the divisionByZero event gets emitted).
  fn divide(a: f32, b: f32) -> f32;

  // Returns "a / R" where R is was the last result.
  // If R is 0, the divisionByZero event gets emitted.
  //
  // @param a left-hand operator.
  fn divideByCurrent(a: f32) -> f32;

  // Emitted when there's an attempt to divide by zero.
  event divisionByZero();
}

Implementation

It also provides two implementations for the Calculator class.

Implementation of a calculator
// === casio_calculator.cpp ============================================================================================
//                                               Sen Infrastructure
//                   Released under the Apache License v2.0 (SPDX-License-Identifier Apache-2.0).
//                                    See the LICENSE.txt file for more information.
//                   © Airbus SAS, Airbus Helicopters, and Airbus Defence and Space SAU/GmbH/SAS.
// =====================================================================================================================

// generated code
#include "stl/calculator.stl.h"

// sen
#include "sen/core/base/compiler_macros.h"
#include "sen/core/base/numbers.h"
#include "sen/core/meta/class_type.h"

namespace calculators
{

/// Our implementation of a calculator.
/// We inherit from the CalculatorBase class (generated by Sen).
class CasioCalculator: public CalculatorBase
{
public:
  SEN_NOCOPY_NOMOVE(CasioCalculator)

public:
  using CalculatorBase::CalculatorBase;
  ~CasioCalculator() override = default;

protected:
  /// This is the implementation of the "add" method.
  /// We need to provide an implementation to be able to instantiate this class.
  float32_t addImpl(float32_t a, float32_t b) override
  {
    const auto result = a + b;
    setNextCurrent(result);  // Save the result in our "current" property.
    return result;
  }

  /// Implementation of addWithCurrent.
  float32_t addWithCurrentImpl(float32_t a) override
  {
    // Just call add with what we have in our "current" property.
    return addImpl(a, getCurrent());
  }

  /// Implementation of divide.
  float32_t divideImpl(float32_t a, float32_t b) override
  {
    float32_t result = 0.0f;
    if (b == 0.0f)
    {
      divisionByZero();  // If we divide by zero, emit the event.
    }
    else
    {
      result = a / b;
    }

    // Save the result in our "current" property.
    setNextCurrent(result);
    return result;
  }

  /// Implementation of divideWithCurrent.
  float32_t divideByCurrentImpl(float32_t a) override { return divideImpl(a, getCurrent()); }
};

// With this macro we let Sen know that this package provides our class.
// You need to define this only once in a cpp file.
SEN_EXPORT_CLASS(CasioCalculator)

}  // namespace calculators
Another implementation of a calculator
// === faulty_calculator.cpp ===========================================================================================
//                                               Sen Infrastructure
//                   Released under the Apache License v2.0 (SPDX-License-Identifier Apache-2.0).
//                                    See the LICENSE.txt file for more information.
//                   © Airbus SAS, Airbus Helicopters, and Airbus Defence and Space SAU/GmbH/SAS.
// =====================================================================================================================

// generated code
#include "stl/calculator.stl.h"

// sen
#include "sen/core/base/compiler_macros.h"
#include "sen/core/base/numbers.h"
#include "sen/core/meta/class_type.h"

// std
#include <cstdint>

namespace calculators
{

/// A calculator implementation that sometimes provides wrong results.
class FaultyCalculator: public CalculatorBase
{
public:
  SEN_NOCOPY_NOMOVE(FaultyCalculator)

public:
  using CalculatorBase::CalculatorBase;
  ~FaultyCalculator() override = default;

protected:
  /// Implementation of the add method.
  /// Might return a wrong answer.
  float32_t addImpl(float32_t a, float32_t b) override
  {
    callCount_++;
    const auto result = a + b + (shouldFail() ? 1.0f : 0.0f);
    setNextCurrent(result);
    return result;
  }

  /// Implementation of the addWithCurrent method.
  /// Might return a wrong answer.
  float32_t addWithCurrentImpl(float32_t a) override
  {
    // Same as add() but use the value of our "current" property.
    return addImpl(a, getCurrent());
  }

  /// Implementation of the divide method.
  /// Might return a wrong answer.
  float32_t divideImpl(float32_t a, float32_t b) override
  {
    callCount_++;

    float32_t result = 0.0f;
    if (b == 0.0f)
    {
      divisionByZero();
    }
    else
    {
      result = (a / b) + (shouldFail() ? 1.0f : 0.0f);
    }

    setNextCurrent(result);
    return result;
  }

  /// Implementation of the divideWithCurrent method.
  /// Might return a wrong answer.
  float32_t divideByCurrentImpl(float32_t a) override { return divideImpl(a, getCurrent()); }

private:
  [[nodiscard]] bool shouldFail() const noexcept
  {
    // Fail every other call.
    return callCount_ % 2 == 0;
  }

private:
  uint32_t callCount_ = 0U;
};

SEN_EXPORT_CLASS(FaultyCalculator)

}  // namespace calculators

How to run it

Let's define what we want to run in our Sen kernel.

Configuration file
# $schema: ../base/schema.json
include:
  # bring the shell so that we can interact with our objects
  - ../base/shell.yaml

build:
  # Let's build a component
  - name: myComponent
    freqHz: 30
    # Import our package so that we can instantiate our objects
    imports: [calculators]
    group: 3
    objects:
      # We know that this package contains a CasioCalculator class. We instantiate it here.
      - class: calculators.CasioCalculator
        name: goodCalc
        bus: my.tutorial
        model: superCalc  # Defining the model is mandatory for instantiating this object.
      - class: calculators.FaultyCalculator
        name: badCalc
        bus: my.tutorial
        model: bsCalc

To run it, let's call sen run:

sen run config/1_calculators/1_calculators.yaml

This will open a shell and tell Sen to instantiate the two implementations in the my.tutorial bus.

You can interact with the objects by doing commands such as:

info my.tutorial.goodCalc

my.tutorial.goodCalc.add 2, 2

my.tutorial.goodCalc.print

my.tutorial.goodCalc.addWithCurrent 2

my.tutorial.goodCalc.getCurrent

my.tutorial.goodCalc.divideByCurrent 12

my.tutorial.goodCalc.divide 4, 2

my.tutorial.goodCalc.addWithCurrent -2

my.tutorial.goodCalc.divideByCurrent 4

my.tutorial.bsCalc.add 2, 2

my.tutorial.bsCalc.add 2, 2

Running it over the network

We can run it over the network using the eth component. This is the same as the first example, but you will need to start two processes.

First run:

sen run config/1_calculators/2_calculators_eth.yaml

Then, in another terminal or command prompt, run:

sen shell

In this new Sen instance, open the bus where we should find our objects:

open my.tutorial

You should be able to work with the objects as if you were on the same process.

Using the explorer

You can also run it using the explorer to see and interact with the objects in a more graphical way.

sen run config/1_calculators/3_calculators_exp.yaml