Calculators example¶
Prerequisites: none - this is the first example. Read the docs quick start if you haven't set up Sen yet.
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.
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();
}
class Client
{
// Where to find the calculator objects.
var calcBus : string [static];
// Uses any available calculator.
fn useCalculator();
}
The STL file declares the Calculator class: its properties (model, current), its methods (add, addWithCurrent, divide, divideByCurrent), and its events (divisionByZero). Sen generates a CalculatorBase C++ class from this definition. Your implementation inherits from it and overrides the *Impl methods.
Implementation¶
It also provides two implementations for the Calculator class.
// === 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
// === 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
CMakeLists.txt explained¶
add_sen_package(
TARGET calculators # Name of the CMake and runtime target
MAINTAINER "..." # Who owns this package
VERSION "0.0.1" # Semantic version embedded in metadata
DESCRIPTION "Example package"
SOURCES # C++ files implementing the package logic
src/casio_calculator.cpp
src/faulty_calculator.cpp
src/client.cpp
STL_FILES stl/calculator.stl # STL interface file; Sen generates C++ from this
SCHEMA_PATH ${CMAKE_CURRENT_BINARY_DIR} # Where the JSON schema is written
)
add_sen_package creates a shared library that Sen loads at runtime. STL_FILES triggers code generation: the Sen compiler reads the STL file and produces the CalculatorBase C++ class. SOURCES lists the hand-written C++ files that are compiled alongside the generated code. SCHEMA_PATH controls where the YAML configuration schema is written, making it available for validation and editor tooling.
How to run it¶
Let's define what we want to run in our Sen kernel.
# $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:
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:
Then, in another terminal or command prompt, run:
In this new Sen instance, open the bus where we should find our objects:
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.