LEAF Register#

LEAF-Register is a designed to describe and capture the communication language between an adapter (proxy) and its client. This repository is structured to define and manage equipment terms and metadata for interaction within a system. The Register will likely be embedded within another tool to handle the capture of equipment metadata, the generation and validation of topics and identification of partial topics.

Project Structure#

├── equipment_actions.yaml
├── equipment_data.yaml
├── __init__.py
├── metadata.py
└── README.md

File Descriptions#

Equipment Actions#

Defines action endpoints and their hierarchical structure. This document is used to capture partial topics, that is topics which contain placeholders which are replaced for equipment-specific metadata when known.

Copy code
details: <institute>/<adapter_id>/<instance_id>/details
running: <institute>/<adapter_id>/<instance_id>/running
experiment:
  start: <institute>/<adapter_id>/<instance_id>/experiment/start
  stop: <institute>/<adapter_id>/<instance_id>/experiment/stop
  measurement: <institute>/<adapter_id>/<instance_id>/experiment/<experiment_id>/measurement/<measurement>

Equipment Data#

Specifies required metadata fields that is, metadata which must be provided to an adapter to function (a subset of placeholder names within the equipment_actions.py document).

adapter_id: required
instance_id: required
institute: required

Metadata#

Core implementation for managing and validating metadata, as well as dynamically resolving placeholders in equipment terms. The first function is to hold the metadata of an adapter when used within this context. The second function is to handle the creation of endpoints/topics by either using the equipment metadata (replacing the placeholders with the appropriate metadata) OR creating wildcard topics (topics which contain catch-all parts for monitoring the MQTT broker).

Usage#

This section covers some of the potential usages of the register. Note that the MetadataManager is the only real element of this repo that will be used.

Loading Metadata#

The first action is to import and use the MetadataManager. Furthermore, if the MetadataManager is being used with a specific equipment then its metadata must be loaded (LEAF dictates all adapters must have metadata). The MetadataManager has been designed to have 1 instance of itself per equipment i.e. one instance can’t hold several equipment metadata. The MetadataManager holds data for both adapter and instance data which will be provided by adapters.

manager = MetadataManager()
# Input data can be either dict or file (equipment=json, instance=yaml)
manager.add_equipment_data("path/to/device.json")

instance_data = {"instance_id" : "instance_id123",
                "institute" : "institute123"}
manager.add_instance_data(instance_data)

Validating Metadata#

A tool may want to know programmatically if the metadata provided is enough to allow a tool to function. The manager provides some utilities namely is_valid and get_missing_metadata to validate metadata.

if manager.is_valid():
    print("Metadata is valid.")
else:
    print("Missing metadata:", manager.get_missing_metadata())

Accessing Metadata#

Once the metadata has been loaded, it may be desired that the data can be accessed in future. As seen below, these are essentially utility functions to return the provided data.

metadata = {"institute" : "A",
            "adapter_id" : "B",
            "instance_id" : "C"}
manager = MetadataManager()
manager.add_data(metadata)
md = manager.get_equipment_data()
assert(md == {"adapter_id":"B"})
instance_id = manager.get_instance_id()
assert(metadata["instance_id"] == instance_id)

Accessing Equipment Actions#

One key feature of the MetadataManager is to generate topics which can either be used to transmit messages to different endpoints OR monitor these endpoints. The MetadataManager has been built in such a way that when possible it will automatically replace placeholders with elements that match from the stored metadata. For example, below, if the metadata is loaded, the placeholders of the start topic will be automatically replaced.

metadata = {"institute" : "A",
            "adapter_id" : "B",
            "instance_id" : "C"}
manager = MetadataManager()
manager.add_data(metadata)
action = manager.equipment_terms.experiment.start()

The value of action will be A/B/C/experiment/start. However, if no metadata is loaded and the same action is taken for example:

manager = MetadataManager()
action = manager.equipment_terms.experiment.start()

The value of the action will be +/+/+/experiment/start because there are no values to be replaced with. However, it may be noticed that the placeholders have been replaced with +. This is because it’s the wildcard symbol in MQTT i.e. it will monitor all topics that match the rest of the topic elements. This in itself is a valid use of the MetadataManager because it allows partial topics to be generated to monitor specific aspects of the system. For example, during the discovery of equipment, the topic +/+/+/details can be monitored which will return messages sent from all Equipment providing their details i.e. telling the client tools that they are running. This idea can be taken further with partially replaced topics using keyword args for example:

manager = MetadataManager()
action = manager.equipment_terms.details(adapter_id="B")

This input will produce the action: +/B/+/details which inserts wildcards when not provided and replaces elements in which the keyword matches the placeholder. This may be useful in cases where you would only like to see equipment of a specific type. However, there are cases where this is necessary because the metadata manager does not hold the required type of information.

metadata = {"institute" : "A",
            "adapter_id" : "B",
            "instance_id" : "C"}
manager = MetadataManager()
manager.add_data(metadata)
action = manager.equipment_terms.experiment.measurement(experiment_id="D",measurement="E")

This action describing a measurement will output 'A/B/C/experiment/D/measurement/E'. The experiment ID and measurement need to be provided because they are more transient (equipment will run many experiments, but its metadata is unlikely to change). The MetadataManager provides some utility, such as was_called, to assist with programmatically identifying when topics have been posted to partial topics (it essentially matches two topics to ensure everything not a placeholder matches). In conclusion, the MetadataManager is designed to be flexible and function both in proxy and client tools i.e. for both transmitting and receiving data.