Lego Bricks & Inputs?

Conal McLaughlin
IOTICS
Published in
5 min readJul 6, 2023

Introduction

Inputs are one of the IOTICS Web APIs exposed over both gRPC and REST.

By advertising an input, an IOTICS Digital Twin allows authorised twins to send messages whose meaning might be described by the properties of the input itself and of its payload.

For example, inputs can be used to send commands to change the state of the underlying asset or to send messages that can be interpreted by the twin to implement request/response exchanges.

A twin can both send an input locally and remotely.

How it works

Inputs are similar to feeds in that they possess both properties and values to describe how the underlaying data to be transferred is expected to look or the meaning behind the input.

Once a twin has advertised an input, any other twin can then choose to send a message to it, given the sender twin has access to the receiving input based on host/twin visibility rules.

Standard API metadata service mechanisms

Any IOTICS Web API metadata service, including Inputs, implements the following mechanisms:

  • Authentication check
  • API allow list check
  • Network Host to Host allow list check

Local vs Remote

Inputs can be triggered both locally to an IOTICSpace and across the network targeted at other spaces, so long as the remote space allows it.

Ordering

Inputs are similar semantically to the point-to-point channels, however, order is not guaranteed.

If Sender A sends input message A before sender B sends input message B to the same receiver twin, the receiver twin may receive message B before message A.

How to use Inputs

Creating an Input on a Twin

Inputs can be added to a twin via the CreateTwin, UpdateTwin or UpsertTwin operations. The examples below use gRPC Remote Procedure Calls (RPCs) to perform these operations.

⚠️ Input IDs must be unique within a twin, however, an input may have the same ID as a feed on the same twin.

Let’s say we are IT Operations at a space agency ground station. As an operator, I want to enable our satellite to receive instructions to move to a certain altitude.

Let’s use the UpsertTwin functionality to add an input. Below shows an example of a JSON request body using standard ontologies such as DBPedia and Purl.org to describe precisely the Properties and Input Values our satellite’s Digital Twin has:

{
"headers": {
"transactionRef": [
"upsert_twin"
]
},
"payload": {
"twinId": "did:iotics:iotVCL7vXoz5qCiZdLAtLzcYJiD6CNEJhRBq",
"properties": [
{
"key": "http://xmlns.com/foaf/0.1/name",
"stringLiteralValue": {
"value": "CallistoSatellite"
}
},
{
"key": "https://dbpedia.org/ontology/Altitude",
"literalValue": {
"value": "23230"
"dataType": "integer"
}
}
],
"location": {
"lat": 54.3,
"lon": 54.5
},
"inputs": [
{
"id": "AlterAltitude",
"values": [
{
"label": "0ad96c5f-f177-41a6-8892-b017ad05ece5",
"comment": "Move the Callisto satellite to altitude",
"unit": "http://dbpedia.org/ontology/Altitude",
"dataType": "integer"
}
]
}
]
}
}

ℹ️ It’s assumed you have set up the necessary boilerplate to send asynchronous gRPC messages in Python for the examples below.

Check out the IOTICS gRPC Client lib to get started.

Describing an Input

To describe an input, we can make use of the Unary-Unary DescribeInput RPC on the InputsApi.

The remoteHostId field is optional and its presence will route the request over the network to a different IOTICSpace.

 describe_input_response = input_grpc_client.describe_input(DescribeInputRequest(
headers=Headers(transactionRef=['describe_input']),
args=DescribeInputRequest.Arguments(
input=Input(
id=InputID(value='AlterAltitude'),
twinId=TwinID(value='did:iotics:iotVCL7vXoz5qCiZdLAtLzcYJiD6CNEJhRBq')
),
remoteHostId=HostID(value='remote_host_id') # <--- optional
)
))

Deleting an Input

To delete an input, we can make use of the Unary DeleteInput RPC on the InputsApi.

delete_input_response = input_grpc_client.delete_input(DeleteInputRequest(
headers=Headers(transactionRef=['delete_input']),
args=DeleteInputRequest.Arguments(
input=Input(
id=InputID(value='AlterAltitude'),
twinId=TwinID(value='did:iotics:iotVCL7vXoz5qCiZdLAtLzcYJiD6CNEJhRBq')
)
)
))

Sending an Input Message from the GroundStation Twin to the CallistoSatellite Twin’s AlterAltitude Input

To send an Input Message from the GroundStation Twin to the CallistoSatellite Twin’s AlterAltitude Input, CallistoSatellite must have had an input (AlterAltitude) created via an UpsertTwin request.

To send an input message, we can make use of the Unary-Unary SendInputMessage RPC on the InterestApi.

Following on from the input example above, we can tell the Input on the SatelliteUplink twin to move the CallistoSatellite to {"altitude": "23250"}.

Local

Within the same host, all inputs will be visible to all twins.

# send input message to local twin
send_input_message_response = interest_grpc_client.send_input_message(SendInputMessageRequest(
headers=Headers(transactionRef=['send_input_message_local']),
args=SendInputMessageRequest.Arguments(
interest=InputInterest(
senderTwinId=TwinID(value='groundstation_twin_id'),
destInput=InputInterest.DestinationInput(
input=Input(id=InputID(value='AlterAltitude'),
twinId=TwinID(value='did:iotics:iotVCL7vXoz5qCiZdLAtLzcYJiD6CNEJhRBq')))
)
),
payload=SendInputMessageRequest.Payload(
message=InputMessage(
occurredAt=Timestamp(seconds=int(datetime.now().timestamp())),
mime='application/json',
data=b'{"altitude": "23250"}' # <--- raw data
)
)
))

Remote

ℹ️ The Host ID field is optional and its presence will route the request over the network to a different IOTICSpace.

⚠️ To send a remote input message, the allowlist of the receiving Twin must be updated to accept requests from the originating Host ID.

# send input message to remote twin
send_input_message_response = interest_grpc_client.send_input_message(SendInputMessageRequest(
headers=Headers(transactionRef=['send_input_message_remote']),
args=SendInputMessageRequest.Arguments(
interest=InputInterest(
senderTwinId=TwinID(value='groundstation_twin_id'),
destInput=InputInterest.DestinationInput(
input=Input(id=InputID(value='AlterAltitude'),
twinId=TwinID(value='did:iotics:iotVCL7vXoz5qCiZdLAtLzcYJiD6CNEJhRBq')),
hostId=HostID(value='remote_host_id') # <--- optional field
),
)
),
payload=SendInputMessageRequest.Payload(
message=InputMessage(
occurredAt=Timestamp(seconds=int(datetime.now().timestamp())),
mime='application/json',
data=b'{"altitude": "23250"}' # <--- raw data
)
)
))

Receiving Input Messages from CallistoSatellite Twin’s AlterAltitude Input

To receive incoming input messages from CallistoSatellite advertised Input, we can make use of the Server Streaming ReceiveInputMessages RPC on the InputApi.

# build ReceiveInputMessageRequest 
request = ReceiveInputMessageRequest(
headers=Headers(transactionRef=['receive_input_messages']),
args=ReceiveInputMessageRequest.Arguments(
input=Input(id=InputID(value='AlterAltitude'),
twinId=TwinID(value='did:iotics:iotVCL7vXoz5qCiZdLAtLzcYJiD6CNEJhRBq')))
)

with input_grpc_client.receive_input_messages(request) as stream:
for response in stream:
<parse response & trigger satellite course alteration>

Request-Reply Pattern

Inputs, in the initial use case shown above, can be used to send data from one twin to another in a command-like fashion. This would be a unidirectional implementation.

Inputs can also be used to implement an async request-reply pattern within an IOTICSpace or network of IOTICSpaces.

This could be used to acknowledge the success or failure of such commands as the satellite altitude alteration, feeding the data back into an analytics dashboard.

Wrap Up

Foundational features of IOTICS such as inputs are simple yet powerful Lego bricks which enable an abundance of application design patterns, extending the functionality of the humble Digital Twin and enabling trusted interoperability at speed and scale.

If you want to discover whats possible, check out our Digital Twin Pattern library @ https://docs.iotics.com/docs/digital-twin-patterns-dtps

The IOTICS founding principles include enabling the evolution of trust within ecosystems, making it easier to access data that already exists and is hard to find, and incorporating agility and flexibility into complex environments. Follow us on LinkedIn https://www.linkedin.com/company/iotics-ecosystem/ or visit iotics.com to find out how we’re enabling real-world interoperability across boundaries within complex ecosystems.

--

--