Hello, Real-Time World!¶
This chapter introduces the smallest complete Termina application: a single task that prints a message to the console once every second. The example exercises three elements of the language's reactive model: an event emitter that produces periodic activations, a task that reacts to those activations, and an access port through which the task reaches the services of the underlying platform. It also illustrates the canonical structure of a Termina project, in which the definitions of the component classes reside in their own modules, while the main application file is reserved for declaring the entities of the system and describing how they are connected.
Creating the project¶
Besides translating source code, the termina tool creates new projects. Its
new command generates a new project consisting of an initial directory tree
together with a configuration file populated with default parameters.
To create a project named hello_world, the following command is executed
from the terminal:
Since the command does not specify a target platform, the project adopts the
default posix-gcc platform, which executes Termina applications on
conventional Unix-like systems and is intended for prototyping and validation.
Once the command has completed, a directory named hello_world will have been
created with the following structure:
The file termina.yaml is the project's configuration file, in which the
project name, build options, and target platform are specified. The app
directory contains the main application module, named app.fin by default,
whose role is to declare the entities that constitute the system and to define
the way in which they are connected. The src directory holds the additional
modules that define the component classes and support functions of the
application. Finally, the output directory will store the C code generated by
the transpiler. The command also initializes a Git repository for the project
and writes its .gitignore, as described in the chapter on
project structure.
Defining the task class¶
The behavior of the application is encapsulated in a task class. In Termina, a task is a reactive component that executes one or more actions in response to the events or messages it receives. A task may hold internal state, and it exposes ports that connect it to other elements of the system, such as event sinks or access ports to shared resources. An action runs to completion before its task takes the next event, although a task of higher priority may preempt it in the meantime.
The class definitions of an application are placed in modules within the src
directory, and never in the main application file. Accordingly, the task class
of this example is defined in a new module located at
src/tasks/hello_world.fin, with the following content:
task class CHelloWorldTask {
timer_port : sink TimeVal triggers timeout;
system_port : access SystemAPI;
action timeout(&priv self, _current_time : TimeVal) -> Status<i32> {
let msg : [char; 32] = "Hello, Real-Time World!";
let ret : Status<i32> = Success;
self->system_port.println(&msg);
return ret;
}
};
The class CHelloWorldTask declares two ports. The first, timer_port, is an
event sink declared as sink TimeVal triggers timeout, which specifies that
the action named timeout is to be executed automatically whenever an event
is received through this port. Each such event carries a value of type
TimeVal, corresponding to the timestamp of the activation, which in this
example is not used and is therefore named _current_time. The second port,
system_port, is an access port to the SystemAPI interface provided by the
platform, through which the task reaches low-level services such as writing to
the console.
The action timeout constitutes the entire behavior of the task. It declares
a fixed-length character array initialized with the message to be displayed and
invokes the println procedure of the system port to print it, passing a
reference to it. The action returns a value
of type Status<i32>, one of the built-in result types of the language, which
indicates whether the action completed successfully or encountered an error. In
this example it always returns Success.
Declaring the application architecture¶
With the task class defined, the system is assembled in the main application
file app/app.fin, which begins by importing the module that provides the
task class:
The periodic activation of the task is produced by an event emitter. The
following declaration creates an emitter named timer of type
PeriodicTimer, whose period field specifies the interval between
consecutive events by means of its tv_sec and tv_usec components, here set
to one second:
Finally, an instance of the task class is declared and connected to the rest of
the system. The annotation #[priority(10)] assigns the task a fixed
scheduling priority. The connection timer_port <- timer binds the task's
event sink to the periodic emitter, so the timeout action is executed on
every emitted event. The connection system_port <-> system_entry links the
access port to the system_entry resource provided by the runtime, through
which the task reaches the platform's input and output services:
Enabling the system port¶
The system_entry resource referenced above implements the SystemAPI
interface by calling the corresponding services of the Operating System
Abstraction Layer. It is not instantiated by default. Its deployment is controlled by a configuration
parameter in the project's termina.yaml file, which must be enabled by adding
the following lines:
When enable-system-port is set, the runtime instantiates and exposes the
system_entry resource during system initialization, making it available for
connection to the task's access port. The second line sets the size of the
character array that print and println take, which must match the
[char; 32] declared in the action; without it, the size defaults to 256.
Building and running the application¶
The application is built by executing the following command from the project's root directory:
The termina build command invokes the transpiler, which validates the
program, including its types and port connections, and translates the source
modules into C code placed in the output directory. The generated code
is then compiled with the build system provided for the posix-gcc platform,
and the resulting executable is run:
The program prints the message Hello, Real-Time World! to the console once
every second. To terminate its execution, the key combination Ctrl+C is
entered in the terminal.