13. Services and capabilities

So far you’ve interacted with agents, checking and changing agent parameters, and sending and receiving messages. But how do you know which agents to send what messages to, and what agents support which parameters? The answer to this question lies in the concept of services .

13.1. Terminology

To fully understand services, we need to formally define a few terms, many of which you are already somewhat familiar with:


An agent is logical entity that implements a specific functionality of the network. Loosely, an agent maps to a layer in a traditional network stack, but is more flexible. Each agent has its own thread of execution, and all agents can be thought of as running concurrently. An agent is normally referenced using its AgentID . You may think of an AgentID as the name of an agent, or a reference to the agent.


Agents interact with each other via messages. Agents can send and receive messages, and typically expose all their functionality as a set of messages that they will respond to. Messages are transmitted within the network stack on a node, and not between nodes in the network. Each message is tagged with a performative that summarizes the purpose of the message. Common performatives are REQUEST , AGREE , REFUSE , FAILURE , NOT_UNDERSTOOD and INFORM .

It is easy to confuse messages, datagrams and frames. Messages are used by agents on a node to interact with other agents on the same node. They are never transmitted! Datagrams are logical packets of data that are exchanged between nodes. Datagrams may be fragmented and reassembled, and thus one datagram does not necessarily map to one transmission. Physical layer datagrams are called frames; they form the basic unit of data exchanged between nodes.

Request messages ask an agent to perform some task. Such messages are marked with the performative REQUEST , and it is a convention to name the message class with a suffix Req (e.g. DatagramReq , ParameterReq ).


When an agent receives a request, it must respond back to the requesting agent. Common responses are simply messages with the performative set to AGREE , REFUSE , FAILURE or NOT_UNDERSTOOD . An AGREE message confirms to the requester that the agent will perform the requested task. A REFUSE message tells the requester that the request cannot be performed. A FAILURE message, on the other hand, means that the agent should have been able to do the request under normal circumstances, but something went wrong. A NOT_UNDERSTOOD response is generated if the agent does not know how to deal with the request. Other than these simple messages, responses may sometimes contain more information. Such messages are respresented by message classes with a suffix Rsp (e.g. ParameterRsp ), and may have a performative of INFORM to indicate that they contain information in response to the request.


Agents sometimes generate unsolicited information. This information is encapsulated in a notification message, typically with a performative INFORM . Notification messages may be sent to a specific agent, or on a topic.


A topic defines a publish-subscribe mechanism where an agent may publish some notifications, and other agents interested in those notifications may subscribe to the topic. Most agents have an unnamed topic associated with themselves, that other agents can subscribe to. For example, a link agent may subscribe to the topic of a physical layer agent to listen for incoming data frames from the physical layer.


Most UnetStack agents publish a number of parameters associated with them. Parameters are key-value pairs that provide information about the agent (read-only parameters), or allow controlling the behavior of the agent (read-write parameters). Parameters are technically accessed via ParameterReq and ParameterRsp messages, but a simpler notation ( agent.parameter ) is also available to get/set parameters.


A service is a collection of messages (requests, responses and notifications) and parameters that an agent honors. Agents publish the list of services they offer, and you can find agents through the services they advertise.


Services often define optional capabilities. These capabilities may be offered by some agents advertising a service, but may be omitted by others. An agent can be queried to check if it supports a specific capability using the CapabilityReq message.

If all of these terms pique your curiosity, you may wish to take a look at the fjåge documentation . fjåge is the underlying agent framework that UnetStack is built on. While we don’t assume familiarity with fjåge in this handbook, your understanding of UnetStack would certainly be quicker if you were to invest in building that familiarity.

13.2. Finding service providers

In Section 9.5 , we have already come across the agentForService() function that helps us find an agent that provides a specific service. In this section, we’ll explore this a bit more.

Fire up your trusty 2-node network and connect to node A’s shell:

> a = agentForService(org.arl.unet.Services.PHYSICAL);
> a.name

We asked for an agent that provides the org.arl.unet.Services.PHYSICAL service, and UnetStack responded back with an agent ID of an agent that can provide you that service. The name of that agent on node A is phy .

But what if there were more than one agents capable of providing the same service? We can ask for the list of all agents that provide a service:

> agentsForService(org.arl.unet.Services.PHYSICAL)

Well, there was only one agent providing the PHYSICAL service. Are there other services that multiple agents provide? Indeed, there are:

> agentsForService(org.arl.unet.Services.DATAGRAM)
[transport, router, uwlink, phy]

The DATAGRAM service is provided by several agents. If you were interested in all incoming datagrams, you’d need to subscribe to the topics of all these agents!

What would have happened if you only asked for agentForService() instead of agentsForService() for the DATAGRAM service? Try it:

> a = agentForService(org.arl.unet.Services.DATAGRAM);
> a.name

Any one of the agents in the list is returned!

Ever wondered what we assigned the a = agentForService(…​) , and then asked for a.name to see the name of the agent?

When you try to print an AgentID object on the shell, the shell tries to be helpful and queries the agent for all its parameters and displays them. In this case, we were only interested in the name and not all the parameters, so we asked the shell not to get the parameters by explicitly asking for just a.name .

You can also ask an agent for the list of services it provides:

> phy.services
[org.arl.unet.Services.PHYSICAL, org.arl.unet.Services.DATAGRAM, org.arl.unet.Services.BASEBAND]

or get a list of all services provided by all agents in the stack:

> services
org.arl.unet.Services.NODE_INFO: node
org.arl.unet.Services.PHYSICAL: phy
org.arl.unet.Services.REMOTE: remote
org.arl.unet.Services.TRANSPORT: transport
org.arl.unet.Services.ADDRESS_RESOLUTION: arp
org.arl.unet.Services.MAC: mac
org.arl.unet.Services.RANGING: ranging
org.arl.fjage.shell.Services.SHELL: websh
org.arl.unet.Services.DATAGRAM: transport router uwlink phy
org.arl.unet.Services.BASEBAND: phy
org.arl.unet.Services.LINK: uwlink
org.arl.unet.Services.ROUTING: router
org.arl.unet.Services.STATE_MANAGER: statemanager
org.arl.unet.Services.ROUTE_MAINTENANCE: rdp

13.3. Checking capabilities

So let’s say you looked up the list of agents that provide the DATAGRAM service:

> agentsForService(org.arl.unet.Services.DATAGRAM)
[transport, router, uwlink, phy]

If you wanted to send a datagram, how do you pick which one you’d rather use? Different agents may provide different optional capabilities. If you were specifically interested in a particular capability (e.g. reliability), you could ask the agent if it supported that:

> phy << new CapabilityReq(org.arl.unet.DatagramCapability.RELIABILITY)
> uwlink << new CapabilityReq(org.arl.unet.DatagramCapability.RELIABILITY)

Here, we asked phy if it can do reliable datagram delivery, and it said "no". Then we asked uwlink , and it confirmed that it can. If you needed reliable delivery of our datagram, you should choose the latter.

You can also ask an agent to list all its optional capabilities:

> transport << new CapabilityReq()

The transport agent says it can do reliable datagram delivery, fragment & reassemble large datagrams (if necessary), report on the progress of large datagram transfers, and cancel datagram delivery half way through the process (if the user wishes to).

Another way you may choose a service provider is by checking its parameters. For example, the MTU parameter (defined in the DATAGRAM service) tells you what is the largest datagram the agent can deliver:

> phy.MTU
> uwlink.MTU

If you had a small datagram (56 bytes or less) to deliver, and you did not care about reliability, you could ask phy to deliver it for you. But, if your datagram was larger, even if you did not need reliability, you’d have to ask uwlink to deliver it for you.

The MTU parameter is the DATAGRAM service is actually org.arl.unet.DatagramParam.MTU . Since we only have one MTU parameter that phy advertises, there is no ambiguity in using phy.MTU . But if you wanted to explicitly ask for the parameter by its fully qualified name, you could send a ParameterReq for it: phy << new ParameterReq().get(org.arl.unet.DatagramParam.MTU)

13.4. Service list

The following services are currently defined in UnetStack:

Short name Fully qualified name Description Read…​



Send and receive datagrams

Chapter 14



Physical layer

Chapter 15



Arbitrary waveform transmission & recording

Chapter 16



Ranging & synchronization

Chapter 17



Node & network information

Chapter 18



Address allocation & resolution

Chapter 19



Datagram transmission over a single hop

Chapter 21



Medium access control

Chapter 20



Routing of datagrams over a multihop network

Chapter 22



Discovery & maintenance of routes in a multihop network

Chapter 22



Datagram transmission over a multihop network

[Transport and reliability]



Remote command execution, text messaging & file transfer

Chapter 24



State persistence across node reboots

Chapter 25



Sleep-wake scheduling for energy management

Chapter 26



Commmand execution & file management services

Chapter 27

You can enjoy reading more about these services in the next few chapters.

<<< [AT script engine] [Datagram service] >>>