This blog post is part 4 of the Machinetalk explained series describing the concepts and ideas behind the Machinetalk middleware.
This part is about HAL remote, the remote interface to the Machinekit hardware abstraction layer HAL. In this blog post, I do not only describe the HAL remote API, but I also explain the communication details of Machinetalk with the HAL remote component as an example.
If you have not already read the previous articles you can find them here:
Before I get into details about HAL remote, it is a good idea to quickly recapture the concepts of HAL.
Similar to the purpose of electronic components in electronic circuits, HAL components are the building blocks of HAL configurations.
From a software engineering perspective, HAL components are software modules with well-defined inputs and outputs. These software modules can be connected at runtime to form an HAL configuration.
HAL components are externally referenced by their unique name and internally managed using a unique handle.
The interfaces of HAL components are called pins. HAL pins have a direction that can be input, output or input/output (IO). Furthermore, pins have a particular data type that can be one of the HAL data types float, unsigned 32-bit integer (u32), signed 32-bit integer (s32) or boolean (bit).
HAL signals serve as wires to interconnect HAL pins of multiple components. Assigned to an HAL signal can be one output pin as a writer and multiple input pins as readers.
In some cases, an HAL pin represents a physical pin, for example, of a parallel port interface if the HAL component does implement a hardware driver.
HAL Remote Component
HAL components are versatile in their application, but they cannot be utilized to implement remote interaction. The reason is that HAL requires agents acting on components to be active at HAL start-up.
Therefore, HAL Remote components provide an additional interface to HAL allowing the creation of components, unbound during HAL start-up.
Similar to conventional HAL components, HAL Remote components are created within the HAL context. However, HAL Remote components do not provide update functions that can be executed by HAL threads.
Thereby, HAL Remote components are equivalent to user space HAL components. The difference is that HAL Remote components have no local thread implementing the functionality. Instead, the rcomp API provides an external interface updating and reading component and pin status.
Moreover, an HAL Remote component can be bound or unbound depending on the state of the actor implementing the components’ functionality.
Regarding the HAL configuration, HAL Remote components have two additional parameters. The timer value specifies how often a components’ status shall be polled for status updates by an HAL Remote server.
Additionally, float-type HAL pins of HAL Remote components feature an epsilon value specifying the threshold when a remote client shall be informed about value changes. Typical use cases for HAL Remote components include remote user interfaces and syncing of multiple HAL
HAL Remote Protocol
The HAL Remote protocol uses two Machinetalk services: halrcmd and halrcomp. The Haltalk daemon provides the implementation of these services.
Haltalk is a user space application using the HAL and HAL Remote component APIs. The software operates on a single HAL session and provides Machinetalk services for as connection points for clients.
Moreover, the Haltalk server application itself has no knowledge about HAL objects. Instead, it provides a generic Machinetalk API to the outside.
Haltalk supports any number of HAL Remote components and several actors acting on them in parallel.
Depicted in the diagram below is an overview of HAL Remote components and the role of Haltalk. The halrcomp service provides an interface for output pins of HAL Remote components whereas the halrcmd service presents an interface for input pins.
The halrcomp service implemented by Haltalk is stateless except the transition of HAL Remote components from unbound to bound state and vice versa. This property is important since Haltalk does not keep track of connected clients or sessions.
To enable high scalability for message transport to multiple clients the service uses publish-subscribe messaging. Although publish-subscribe messaging is in general unidirectional, using ZeroMQ’s XPUB socket on top of the TCP/IP protocol, the server receives notifications about clients subscribing and unsubscribing to topics. The halrcomp service uses this feature to bind and unbind HAL Remote components utilizing the hal_aquire and hal_release functions of the HAL rcomp API.
The Haltalk service uses the publish-subscribe to inform subscribers about status updates once Haltalk binds an HAL Remote component.
ZeroMQ topic names represent the name of HAL Remote components. Whenever a new subscriber subscribes to a topic, the Haltalk service publishes an MT_HALRCOMP_FULL_UPDATE message containing the most recent state of the HAL Remote component matching the topic name.
Haltalk sends an MT_HALRCOMP_ERROR message on the channel when a subscriber tries to subscribe to an HAL Remote component that does not exist. Moreover, subscribers are eventually informed about pin value or component state changes with an MT_INCREMENTAL_UPDATE message. Additionally, periodic MT_PING messages allow subscribers to monitor the liveness of an open connection.
The halrcmd service implemented by the Haltalk server is completely stateless and provides a remote API to HAL. In the context of HAL Remote components, this channel is used to bind new component instances and to update output pin values of already bound component instances.
The halrcmd channel uses the ZeroMQ ROUTER-DEALER pattern closely matching a typical Remote Procedure Call (RPC) or request-reply messaging.
An MT_HALRCOMP_BIND message begins the HAL Remote component conversation. This message binds an HAL Remote component to an HAL instance. Sent with this message is a description of the local representation of the HAL Remote component.
If the remote representation of the HAL Remote component matches the local one, an MT_HALRCOMP_BIND_CONFIRM message is returned by the server. The binding process aborts with an MT_HALRCOMP_BIND_REJECT message in case the HAL Remote component descriptions are not matching.
An optional no_create flag indicates whether a component shall be
created or not in case it does not exist in the HAL instance.
Depicted in the diagram below is the message flow between a Haltalk instance and two clients.
An HAL Remote protocol session starts with the bind dialog. The bind dialog completes with a full update message from the server. The publish-subscribe messaging pattern forwards the full update message to all subscribers. Therefore, clients must handle full update messages not only during the bind.
Note that the bind dialog can be skipped if the client does not know the structure and pins of a remote component. This is useful to use remote component to sync for example a tool or origins table with variable row count.
We have already seen the message flow of the HAL Remote protocol visualized as swimlanes. Another way to visualize stateful processes is a state machine. Please take a look the state machine diagram below to get an impression of the HAL Remote component client state machine. I will go into more details about the Machinetalk state machine in the next blog post of this series.
A single top level Protobuf message serves as a basis for all Machinetalk Protobuf messages. This message acts as a standard container for de- and encoding all Machinetalk messages. This pattern is also known as union message and the top-level message of Machinetalk is named Container.
The type field is utilized to describe the type of a Machinetalk message. Moreover, the repeated message fields component and pin serve as storage for HAL Remote component data. Depending on the message type different fields of the Protobuf message are used. For example, the Haltalk service uses the no_create field of the Component message only during the binding process.
Therefore, it makes sense to mark the fields of the Machinetalk Protobuf messages as optional. Marking Protobuf fields as optional also serves backward compatibility with possible future message versions as outlined in “Thrift: The missing guide”
Contrary to the benefits of using a union message, this design decision adds the extra task for developers to match Protobuf message fields with Machinetalk message types.
In this article, I explained the details about the HAL Remote components and the HAL Remote protocols. If you want to learn more about HAL Remote I encourage you to take a look the Haltalk and QtQuickVcp source code and the generated Machinetalk documentation.
In the blog post, I will talk about model driven development of the Machinetalk bindings aka. Machinetalk GSL.
I hope you enjoyed reading this blog post and I encourage you to comment if you have any ideas or feedback.