include .special.rst

NUClear DSL

On Statements

On statements are used by Reactors wishing to make subscriptions to the PowerPlant. Using this statement, developers can set the conditions under which desired Reactions will run.

anatomy of an on statement

The on statement can be seen as containing three main parts. The DSL Request, the Runtime Arguments, and the Callback.

On<...>(Runtime, ... ).then(function);

DSL Request

This is red ! And this part is blue.

The DSL request can be fused together through any combination of DSL words. The combination of these words will define the kind of reaction which is being requested (for example, Trigger will define a reaction that should occur when a required data type is emitted, while Every will define periodic reactions).

For reactions to occur, at least one Binding DSL word should be present in the DSL Request. From the provided DSL words, those which are binding are: Trigger, With, Every, Always, Startup, Shutdown, TCP, UDP and Network

Runtime Arguments

Some DSL words will provide the ability to make changes to the system during runtime. This means that NUClear avoids the need for a system restart should a configuration, port number, or file need to be changed while the system is running.

From the provided DSL words, those which take runtime arguments are: IO, TCP, and UDP

Callback

Finally, the developer can define the callback which will execute when the reaction is triggered during runtime. The callback can be defined using a C++ lambda function.

During system runtime, the argument selection for the callback works on the principle of fission, in that the arguments provided with the callback can be deduced as needed. For example:

on<<Trigger<A>, Optional<Trigger<B>>().then([](const A& a, const B& b) {

});

In the above request, the Trigger on dataType B has been listed as optional, while the Trigger for dataType A is listed as mandatory. Yet the callback function lists arguments for both dataType A and dataType B.

Lets say that dataType A is emitted to the PowerPlant, but at this time, dataType B does not have any data associated with it.

Since dataType B was listed as optional, the task associated with this reaction can be scheduled. However, when executing the callback for this reaction, NUClear will identify that dataType B is not present, and will remove reference to this data type from the callback, so that the task is only run for dataType A.

Effectively, through the application of fission, the callback is restructured as per the following example.

.then([](const A& a){

});

DSL WORDS

The following words are available in the DSL. Reactors can fuse together their instructions and requests to the PowerPlant from any combination of these words. Developers wishing to add their own DSL words to the system can do so at any time. Please see: Extension

Data Gathering

Trigger

template<typename ...Ts>
struct Trigger : public NUClear::dsl::Fusion<operation::TypeBind<Ts>..., operation::CacheGet<Ts>...>

This is used to request any data dependent reactions in the system.

on<Trigger<T>>() 
This will enact the execution of a task whenever T is emitted into the system. When this occurs, read-only access to T will be provided to the triggering unit via a callback.

on<Trigger<T1, T2, ... >>() 
Note that a this request can handle triggers on multiple types. When using multiple types in the request, the reaction will only be triggered once all of the trigger types have been emitted (at least once) since the last occurrence of the event.

Implements

Bind, Get

Template Parameters
  • Ts: The datatype on which a reaction callback will be triggered. Emission of this datatype into the system will trigger the subscribing reaction.

With

template<typename ...T>
struct With : public NUClear::dsl::Fusion<operation::CacheGet<T>...>

This is used to define any extra data which should be provided to a subscribing a reaction.

on<With<T2>>() 
Note that during runtime, the emission of data using this word will not trigger a reaction within the system. For best use, this word should be fused with at least one other binding DSL word.

on<Trigger<T1>, With<T2>>() 
In the example above, when T2 is emitted into the system, it will not trigger a callback to the triggering reaction. Yet when T1 is emitted into the system, read-only access to the most recent copy of both T1 and T2 will be provided via a callback to the reaction.

If a copy of T2 is not present when T1 is emitted into the system, the task will be dropped (i.e the reaction will not run). To override this functionality, include the DSL keyword “Optional” in the request. For example:

on<Trigger<T1>, Optional<With<T2>>>() 

Implements

Get

Template Parameters
  • T: the datatype/s which will be provided to a subscribing reaction when the reaction is triggered.

Data Modifiers

Last

template<size_t n, typename ...DSLWords>
struct Last : public NUClear::dsl::Fusion<DSLWords...>

This instructs the powerplant to store the last n messages (of the associated type) to the cache and provide read-only access to the subscribing reaction.

on<Last<n, Trigger<T, ...>>>() 
During system runtime, the PowerPlant will keep a record of the last [0-n] messages which were provided to the subscribing reaction. This list is ordered such that the oldest element is first, and the newest element is last. Once n messages are stored, the trigger of a new reaction task will cause the newest copy to be appended to the list, and the oldest copy to be dropped.

This word is a modifier, and should be used to modify any “Get” DSL word.

Multiple Statements

on<Last<n, Trigger<T1>, With<T2>>() 
When applying this modifier to multiple get statements, a list will be returned for each statement. In this example, a list of up to n references for T1 and another list of up to n references for T2 will be provided to the subscribing reaction.

Get Statements

on<Last<n, Trigger<T1>, With<T2>>() 
When applied to a request containing a pure “Get” statement (such as the With statement), the data in the associated list will reference that which was available whenever the reaction was triggered. That is, the list for T2 may not represent the last n emissions of the data, but rather, only the data which was available at the time of generating the last n tasks.

IO Keywords

on<Last<n, Network<T>>>() 
When working with a DSL word that returns more than one item (such as the I/O Keywords), a list for each item will be returned. In the example above, a list of up to n references for port addresses, and another list of up to n references for T will be returned.

Implements

Modification

Template Parameters
  • n: the number of records to be stored in the cache.

  • DSLWords: the DSL word/activity being modified.

Optional

template<typename ...DSLWords>
struct Optional : public NUClear::dsl::Fusion<DSLWords...>

This is used to signify any optional properties in the DSL request.

on<Trigger<T1>, Optional<With<T2>() 
During system runtime, optional data does not need to be present when initialising a reaction within the system. In the case above, when T1 is emitted to the system, the associated task will be queued for execution. Should T2 be available, read-only access to the most recent emission of T2 will be provided to the subscribing reaction. However, should T2 not be present, the task will run without a reference to this data.

This word is a modifier, and should be used to modify any “Get” DSL word.

Implements

Modification

Template Parameters
  • DSLWords: the DSL word/activity being modified.

Execution Modifiers

Single

struct Single : public NUClear::dsl::word::Buffer<1>

This is used to specify that only one instance of the associated reaction can execute during runtime.

on<Trigger<T, ...>, Single>() 
When this keyword is used, if the subscribing reaction is triggered while an existing task for this reaction is either in the queue or still executing, then the new task request will be ignored/dropped.

Implements

Precondition

Subclassed by NUClear::dsl::word::Once

Buffer

template<int n>
struct Buffer

This is used to specify that up to n instances of the associated reaction can execute during runtime.

on<Trigger<T, ...>, Buffer<n>>>() 
In the case above, when the subscribing reaction is triggered, should there be less than n existing tasks associated with this reaction (either executing or in the queue), then a new task will be created and scheduled. However, should n tasks already be allocated, then this new task request will be ignored.

Implements

Precondition, Fusion

Template Parameters
  • n: the number of tasks (instances of the subscribing reaction) which can be running at a given time.

Priority

struct Priority

Task priority can be controlled using an assigned setting.

on<Trigger<T, ...>, Priority::HIGH>() 
The PowerPlant uses this setting to determine the scheduling order (for the associated task) in the threadpool, as well as assign a priority to the thread.

The available priority settings are:

REALTIME: Tasks assigned with this will be queued with all other REALTIME tasks.

HIGH: Tasks assigned with this will be queued with all other HIGH tasks. They will be scheduled for execution when there are no REALTIME tasks in the queue.

NORMAL: Tasks assigned with this will be queued with all other NORMAL tasks. They will be scheduled for execution when there are no REALTIME and HIGH tasks in the queue.

LOW: Tasks assigned with this will be queued with all other LOW tasks. They will be scheduled for execution when there are no REALTIME, HIGH and NORMAL tasks in the queue.

IDLE: Tasks assigned with this priority will be queued with all other IDLE tasks. They will be scheduled for execution when there are no other tasks running in the system.

Default Behaviour

on<Trigger<T>>() 
When the priority is not specified, tasks will be assigned a default setting; NORMAL.

Attention

If the OS allows the user to set thread priority, this word can also be used to assign the priority of the thread in its runtime environment.

Implements

Fusion

Sync

template<typename SyncGroup>
struct Sync : public NUClear::dsl::word::Group<SyncGroup, 1>

This is used to specify that only one reaction in this SyncGroup can run concurrently.

on<Trigger<T, ...>, Sync<SyncGroup>>() 
When a group of tasks has been synchronised, only one task from the group will execute at a given time.

Should another task from this group be scheduled/requested (during execution of the current task), it will be sidelined into the task queue.

Tasks in the queue are ordered based on their priority level, then their task id.

When should I use Sync

Consider a reactor with a number of a reactions which modify its state. It would be unwise to allow the reactions to run concurrently. To avoid race conditions, it is recommended that any reactions which modify the state be synced.

Attention

When using NUClear, developers should not make use of devices like a mutex. In the case of a mutex, threads will run and then block (leading to wasted resources on a number of inactive threads). By using Sync, NUClear will have task and thread control so that system resources can be efficiently managed.

Implements

Group

Template Parameters
  • SyncGroup: the type/group to synchronize on. This needs to be a declared type within the system. It is common to simply use the reactors name (i.e; if the reactor is only syncing with one group). Should more than one group be required, the developer can declare structs within the system, to act as a group reference. Note that the developer is not limited to the use of a struct; any declared type will work.

MainThread

struct MainThread

This is used to specify that the associated task will need to execute using the main thread.

on<Trigger<T, ...>, MainThread>() 
This will most likely be used with graphics related tasks.

Timing Keywords

Every

template<int ticks, class period>
struct Every

This is used to request any periodic reactions in the system.

on<Every<ticks, period>>() 
This request will enact the execution of a task at a periodic rate. To set the timing, simply specify the desired period with the request. For example, to run a task every two seconds, the following request would be used:

on<Every<2, std::chrono::seconds>() 

Note that the period argument can also be wrapped in a Per<> template so that the inverse relation can be invoked. For instance, to execute a callback to initialise two tasks every second, then the request would be used:

on<Every<2, Per<std::chrono::seconds>>() 

Attention

The period which is used to measure the ticks must be greater than or equal to clock::duration or the program will not compile.

Implements

Bind

Template Parameters
  • ticks: the number of ticks of a particular type to wait

  • period: a type of duration (e.g. std::chrono::seconds) to measure the ticks in. This will default to clock duration, but can accept any of the defined std::chrono durations (nanoseconds, microseconds, milliseconds, seconds, minutes, hours). Note that you can also define your own unit: See http://en.cppreference.com/w/cpp/chrono/duration

Always

struct Always

This is used to request any continuous reactions in the system.

on<Always> 
This request will ensure a single instance of the associated reaction is running at all times. That is, as one instance is completed, a new instance of the task will spawn.

Any reactions requested using this keyword will initialise upon system start-up and execute continually until system shut-down.

Note that a task spawned from this request will execute in its own unique thread rather than the default thread pool.

Infinite Loops

This word should be used in place of any reactions which would contain an infinite loop. That is, it is not recommended to use a while(true) loop (or equivalent) in a reaction. Using this word allows the task to (cleanly) finish and restart itself, allowing the task to terminate properly when the system is shutdown. Note that tasks which do not terminate correctly at system shutdown will cause the system to hang.

Ensure Clean Shutdown

If the reaction associated with this task is performing a blocking operation, developers should make the the reaction interruptible with an on<Shutdown> reaction. This will enforce a clean shutdown in the system.

Attention

Where possible, developers should avoid using this keyword. It has been provided, but should only be used when there is no other way to schedule the reaction. If a developer is tempted to use this keyword, it is advised to review other options, such as on<IO> before resorting to this feature.

Implements

Pool Bind

Watchdog

template<typename WatchdogGroup, int ticks, class period>
struct Watchdog

This can be used to monitor task(s); if the monitored task(s) have not occurred within a desired timeframe, the watchdog can be serviced to trigger a specified reaction.

on<Watchdog<WatchdogGroup, ticks, period>>() 
This is a useful tool for anything in the system which might stall, and needs to be kick-started.

The watchdog can monitor a single task, or group of tasks, over a period of time. If no activity is detected after the specified timeframe, the watchdog will be serviced. When the watchdog is serviced, the timer resets.

emit<Scope::WATCHDOG>(ServiceWatchdog<SampleReactor>(data)) 
The watchdog will need to be serviced by a watchdog service emission. The emission must use the same template type and runtime argument as the watchdog. Each time this emission occurs, the watchdog timer for the specified runtime argument will be reset.
Single Reaction

on<Watchdog<SampleReaction, 10, std::chrono::milliseconds>>() 
In the example above, a SampleReaction will be monitored. If the reactions does not occur within 10 milliseconds, the watchdog will be serviced.

Group of Reactions

on<Watchdog<SampleReactor, 10, std::chrono::milliseconds>>() 
In the example above, all reactions from the SampleReactor will be monitored. If a task associated with the SampleReactor has not occurred for 10 milliseconds, the watchdog will be serviced.

Multiple watchdogs in a single reactor

on<Watchdog<SampleReactor, 10, std::chrono::milliseconds>>(data) 
In the example above, all reactions from the SampleReactor will be monitored, but a different watchdog will be created for each unique instance of data. If the task associated with an instance of data in the SampleReactor group has not occurred for 10 milliseconds, the watchdog for that instance of data will be serviced.

Service the Watcdog

emit<Scope::WATCHDOG>(ServiceWatchdog<SampleReactor>()) 
The watchdog will need to be serviced by a watchdog service emission. The emission must use the same template type as the watchdog. Each time this emission occurs, the watchdog timer will be reset.

Attention

The period which is used to measure the ticks must be greater than or equal to clock::duration or the program will not compile.

Implements

Bind

Template Parameters
  • WatchdogGroup: the type/group of tasks the watchdog will track. This needs to be a declared type within the system (be it a reactor, reaction, or other type).

  • ticks: the number of ticks of a particular type to wait

  • period: a type of duration (e.g. std::chrono::seconds) to measure the ticks in. This will default to clock duration, but can accept any of the defined std::chrono durations (nanoseconds, microseconds, milliseconds, seconds, minutes, hours). Note that you can also define your own unit: See http://en.cppreference.com/w/cpp/chrono/duration

Event Keywords

Startup

struct Startup : public NUClear::dsl::operation::TypeBind<Startup>

This is used to specify reactions which should occur at startup.

on<Startup>() 
Any reactions listed with this DSL word will run directly after all reactors have been installed into the PowerPlant but before the system starts the main execution phase. This is the only time these reactions will run.

Note that this request is generally used by reactor’s which require information provided by another reactor’s constructor.

Implements

Bind

Shutdown

struct Shutdown : public NUClear::dsl::operation::TypeBind<Shutdown>, public NUClear::dsl::word::Priority::IDLE

This is used to specify any reactions/tasks which should occur during shutdown.

on<Shutdown>() 
Once the shutdown command has been emitted to the PowerPlant, all existing tasks within the system will complete their processing as per their current place in the queue.

Any reactions listed with this keyword will then be queued and processed. Tasks in this queue are ordered based on their priority level, then their emission timestamp.

After the shutdown event is triggered, any other reactions/events which would normally occur based on system emissions will not be processed. That is, all tasks baring the shutdown tasks will be ignored.

Once all Shutdown tasks have finished processing, the system will terminate.

Attention

An on<Shutdown>() request simply specifies a reaction/task which should run during the system shutdown process. It is NOT the command which initialises the process.

Implements

Bind

IO Keywords

IO

struct IO

This is used to trigger reactions based on standard I/O operations using file descriptors.

on<IO>(file_descriptor) 
This function works for any I/O communication which uses a file descriptor. The associated reaction is triggered when the communication line matches the descriptor.

When using this feature, runtime arguments should be provided, to specify the file descriptor.

Example Use

File reading: triggers a reaction when the pipe/stream/communication line has data available to read.

on<IO>(pipe/stream/comms, IO::READ) 
File writing: triggers a reaction when the pipe/stream/communication line has data to be written.
on<IO>(pipe/stream/comms, IO::WRITE) 
File close: triggers a reaction when the pipe/stream/communication line is closed.
on<IO>(pipe/stream/comms, IO::CLOSE) 
File error: triggers a reaction when the pipe/stream/communication line reports an error.
on<IO>(pipe/stream/comms, IO::CLOSE) 
Multiple states: this feature can trigger a reaction when the pipe/stream/communication line matches multiple states. For example;
on<IO>(pipe/stream/comms, IO::READ | IO::ERROR) 

Attention

Note that reactions triggered by an on<IO> request are implicitly single.

Implements

Bind

Subclassed by NUClear::dsl::word::TCP, NUClear::dsl::word::UDP, NUClear::dsl::word::UDP::Broadcast, NUClear::dsl::word::UDP::Multicast

TCP

struct TCP : public NUClear::dsl::word::IO

This allows a reaction to be triggered based on TCP activity.

on<TCP>(port) 
When a connection is identified on the assigned port, the associated reaction will be triggered. The request for a TCP based reaction can use a runtime argument to reference a specific port. Note that the port reference can be changed during the systems execution phase.

on<TCP>() 
Should the port reference be omitted, then the system will bind to a currently unassigned port.

on<TCP, TCP>(port, port)  
A reaction can also be triggered via connectivity on more than one port.

Attention

Because TCP communications are stream based, the on< TCP >() request will often require an on< IO >() request also be specified within its definition. It is the later request which will define the reaction to run when activity on the stream is detected. For example:

on<TCP>(port).then([this](const TCP::Connection& connection){
  on<IO>(connection.fd, IO::READ | IO::CLOSE).then([this](IO::Event event)
} 

Implements

Bind

UDP

struct UDP : public NUClear::dsl::word::IO

This allows a reaction to be triggered based on UDP activity originating from external sources, or UDP emissions within the system.

on<UDP>(port) 
When a connection is identified on the assigned port, the associated reaction will be triggered. The request for a UDP based reaction can use a runtime argument to reference a specific port. Note that the port reference can be changed during the systems execution phase.

on<UDP>(port, bind_address) 
The bind_address parameter can be used to specify which interface to bind on. If bind_address is an empty string, the system will bind to any available interface.

on<UDP>() 
Should the port reference be omitted, then the system will bind to a currently unassigned port.

on<UDP:Broadcast>(port)
on<UDP:Multicast>(multicast_address, port) 
If needed, this trigger can also listen for UDP activity such as broadcast and multicast.

These requests support both IPv4 and IPv6 addressing.

Implements

Bind

Network

template<typename T>
struct Network

NUClear provides a networking protocol to send messages to other devices on the network.

on<Network<T>>() 
This request can be used to make a multi-processed NUClear instance, or communicate with other programs running NUClear. Note that the serialization and deserialization is handled by NUClear.

When the reaction is triggered, read-only access to T will be provided to the triggering unit via a callback.

Attention

When using an on<Network<T>> request, the associated reaction will only be triggered when T is emitted to the system using the emission Scope::NETWORK. Should T be emitted to the system under any other scope, this reaction will not be triggered.

Implements

Bind, Get

Template Parameters
  • T: the datatype on which the reaction callback will be triggered.

Emit Statements

Emit statements are used by Reactors wishing to emit data to the PowerPlant. Using this statement, developers can specify when data will be emitted to the system.

When using NUClear, data will most likely be emitted during a reaction. However, where necessary, emissions can also occur during reactor construction (where it is recommended to use Scope::Initialise), or in some cases from within the PowerPlant itself (for example, when using a third party library which does not have a reactor).

Any data emitted to the PowerPlant will be sent with a unique pointer. The PowerPlant will take ownership of this pointer and run any necessary callbacks to trigger reactions (create tasks).

Note that data can be emitted under varying scopes:

Local Emitting

These emissions send data to the local instance of the NUClear powerplant. There are a number of scopes under which these emissions can take place:

Scope::LOCAL

template<typename DataType>
struct Local

When emitting data under this scope, tasks are distributed via the thread pool for execution.

emit<Scope::LOCAL>(data, dataType); 

Attention

Note that this type of emission is the default behaviour when emitting without a specified scope.

emit(data, dataType); 

Parameters
  • data: the data to emit

Template Parameters
  • DataType: the datatype of the object to emit

Scope::DIRECT

template<typename DataType>
struct Direct

When emitting data under this scope, the tasks created as a result of this emission will bypass the thread pool, and be executed immediately.

emit<Scope::DIRECT>(data, dataType); 
When data is emitted via this scope, the task which is currently executing will be paused. At this time any tasks created as a result of this emission are executed one at a time sequentially, using the current thread. This type of emission will always run even when the system is in its Shutdown process or before the system has started up to the main phase.

Attention

This scope is useful for reactors which emit data to themselves.

Parameters
  • data: the data to emit

Template Parameters
  • DataType: the datatype that is being emitted

Scope::Initialise

template<typename DataType>
struct Initialise

This scope emits data as the system starts up.

emit<Scope::INITIALISE>(data, dataType); 
This should be used to emit any data required during system start up (that is, as the reactor is being installed into the powerPlant). When running emissions under this scope, the message will wait until all Reactors are installed into the powerPlant before triggering any callbacks based on the emission.

Attention

Tasks triggered by data emitted under this scope will only execute while the system is in the initialisation phase. These tasks are the final activity which occur before the system shifts into the execution phase. Any emissions under this scope while the system is in the execution phase will be ignored.

Parameters
  • data: The data to emit

Template Parameters
  • DataType: The type of the data to be emitted

Scope::DELAY

template<typename DataType>
struct Delay

This will emit data, after the provided delay.

emit<Scope::DELAY>(data, delay(ticks), dataType); 
Emissions under this scope will wait for the provided time delay, and then emit the object utilising a local emit (that is, normal thread pool distribution).

Parameters
  • data: the data to emit

  • delay(ticks): the time to wait before emitting this object. Use delay to specify the unit in which to measure the ticks, this will default to clock duration, but can accept any of the defined std::chrono durations (nanoseconds, microseconds, milliseconds, seconds, minutes, hours). Note that you can also define your own unit: See http://en.cppreference.com/w/cpp/chrono/duration. Use an int to specify the number of ticks to wait.

Template Parameters
  • DataType: the datatype of the object to emit

Scope::WATCHDOG

template<typename>
struct Watchdog

When emitting data under this scope, the service time for the watchdog is updated.

emit<Scope::WATCHDOG>(ServiceWatchdog<WatchdogGroup>(data)); 
or
emit<Scope::WATCHDOG>(ServiceWatchdog<WatchdogGroup>()); 

The RuntimeType template parameter need not be specified for ServiceWatchdog as it will be inferred from the data argument, if it is specified

Template Parameters
  • the: datatype of the object to emit

Network Emitting

Network emissions can be used to send data through the network on which the current system is running.

Scope::UDP

template<typename DataType>
struct UDP

Emits data as a UDP packet over the network.

emit<Scope::UDP>(data, to_addr, to_port); 
Emissions under this scope are useful for communicating with third parties. The target of the packet can be can be a unicast, broadcast or multicast address, specified as either a host endian int, or as a string. Additionally the address and port on the local machine can be specified using a string or host endian int.

Attention

Anything emitted over the UDP network must be serialisable.

Parameters
  • data: the data to emit

  • to_addr: a string specifying the address to send this packet to

  • to_port: the port to send this packet to in host endian

  • from_addr: Optional. The address to send this from or “” to automatically choose an address.

  • from_port: Optional. The port to send this from in host endian or 0 to automatically choose a port.

Template Parameters
  • DataType: the datatype of the object to emit

Scope::Network

template<typename DataType>
struct Network

Emits data over the network to other NUClear environments.

emit<Scope::NETWORK>(data, target, reliable, dataType); 
Data emitted under this scope can be sent by name to other NUClear systems or to all NUClear systems connected to the NUClear network. When sent the data is serialized; the associated serialization and deserialization of the object is handled by NUClear.

These messages can be sent using either an unreliable protocol that does not guarantee delivery, or using a reliable protocol that does.

Attention

Note that if the target system is not connected to the network, the emit will be ignored even if reliable is enabled.

Attention

Data sent under this scope will only trigger reactions (create tasks) for any associated network requests of this datatype. For example:

on<Network<T>> 
Tasks generated by this emission are assigned to the threadpool on the target environment.

Parameters
  • data: the data to emit

  • target: Optional. The name of the system to send to, or empty for all systems. Defaults to all. (an empty string).

  • reliable: Optional. True if the delivery of the message should be guaranteed. Defaults to false.

Template Parameters
  • DataType: the type of the data to send