Add Labgrid LED command
Setup
For the labgrid client we follow the official documentation but instead of the official repository for labgrid we use our own
$ virtualenv -p python3 labgrid-venv $ source labgrid-venv/bin/activate labgrid-venv $ git clone https://github.com/12rcu/labgrid labgrid-venv $ cd labgrid && pip install -r requirements.txt labgrid-venv $ python3 setup.py install
Follow the official setup for the coordinator and the exporter.
Usage
- MQTT Driver:
First add a
MQTTResourcefor the Exporter. A sample for this config would be:<resource-group>: MQTTResource: host: 127.0.0.1
Add this group to a place and acquire it with the labgrid-client. Now we can access the
mqttsubcommand within the client:labgrid-venv $ labgrid-client -p <place> mqtt -s $Sys/# -t -1while the
-sor--subscribeflag takes a string and tries to subscribe to ist. (the#means that all subtopics should be subscribed) The-tor--timeflag takes a float in seconds. After the time the client terminates and closes the connection to the broker, -1 for infinite.- LED Driver:
First add a
LEDBoardTopicresource to configuration of the Exporter. A sample for this config would be:<resource-group>: LEDBoardTopic: host: 127.0.0.1 board_topic: changes/zcu102
While
zcu201the board identifier from the Bord-detection config is. Add this group to a place and acquire it with the labgrid-client. Now we can access theledsubcommand within the client:labgrid-venv $ labgrid-client -p <place> led <topic1/subtopic> <topic2> <...>
While
#is again a white card for all subtopic, some examples of possible topicstopic/subtopic topic/# #
Note: The topic
allis equivalent to the topic#and is used to make the command more readable:labgrid-venv $ labgrid-client -p <place> led all
MQTT Driver/Resource/Client Documentation
All the relevant code blocks as the labgrid project itself is quite complex:
Resources
Found in labgrid/resource
MQTTManager:
@attr.s(eq=False) class MQTTManager(ResourceManager): # set default components _available = attr.ib(default=attr.Factory(set), validator=attr.validators.instance_of(set)) _avail_lock = attr.ib(default=threading.Lock()) _clients = attr.ib(default=attr.Factory(dict), validator=attr.validators.instance_of(dict)) _topics = attr.ib(default=attr.Factory(list), validator=attr.validators.instance_of(list)) _topic_lock = attr.ib(default=threading.Lock()) _last = attr.ib(default=0.0, validator=attr.validators.instance_of(float)) def __attrs_post_init__(self): super().__attrs_post_init__() self.log = logging.getLogger('MQTTManager') # create the connection to the broker when a resource was found/added def _create_mqtt_connection(self, host): import paho.mqtt.client as mqtt client = mqtt.Client() client.connect(host) client.on_connect = self._on_connect client.on_message = self._on_message client.loop_start() return client def on_resource_added(self, resource): host = resource.host if host not in self._clients: self._clients[host] = self._create_mqtt_connection(host) self._clients[host].subscribe(resource.avail_topic) def _on_message(self, client, userdata, msg): # print(f"<resource> mqtt message in topic {msg.topic} received with payload: {msg.payload.decode('utf-8')}") payload = msg.payload.decode('utf-8') topic = msg.topic with self._avail_lock: self._available.add(topic) def _on_connect(self, client, userdata, flags, rc): if rc == 0: print("<resource> Connected to MQTT Broker!") else: print("<resource> Failed to connect, return code %d\n", rc) def poll(self): if monotonic()-self._last < 2: return # ratelimit requests self._last = monotonic() with self._avail_lock: for resource in self.resources: resource.avail = resource.avail_topic in self._available
The MQTTResource is also added from a newer version and defines within the exporter the host topic and the avail topic.
@target_factory.reg_resource @attr.s(eq=False) class MQTTResource(ManagedResource): # depends on ManagedResource manager_cls = MQTTManager host = attr.ib(validator=attr.validators.instance_of(str)) avail_topic = attr.ib(validator=attr.validators.instance_of(str)) def __attrs_post_init__(self): self.timeout = 120.0 super().__attrs_post_init__()
For the mqtt driver is this resource enough, for the led driver we also want to define a topic where the board publishes it’s events:
@target_factory.reg_resource @attr.s(eq=False) class LEDBoardTopic(MQTTResource): # depends on MQTTResource board_topic = attr.ib(default=None, validator=attr.validators.instance_of(str))
Driver
Found in labgrid/driver
MQTT Driver
The client will set the subscribed_topic variable to the topic that was subscribed. After the driver was acivated the driver will then connect itself to the MQTT Broker and will proceed to subscribe to the topic that was set in the variable.
@target_factory.reg_driver @attr.s(eq=False) class MQTTDriver(Driver): bindings = { "mqtt": {"MQTTResource"} } subscribed_topic = "" def __attrs_post_init__(self): super().__attrs_post_init__() import paho.mqtt.client as mqtt self._client = mqtt.Client() def on_activate(self): print(f"<driver> mqtt driver active") self._client.on_message = self._on_message self._client.on_connect = self._on_connect self._client.connect(self.mqtt.host) self._client.loop_start() def _on_connect(self, client, userdata, flags, rc): if(self.subscribed_topic != ""): self._client.subscribe(self.subscribed_topic) print(f"<driver> mqtt client connected") def _on_message(self, client, userdata, msg): print(f"<driver> topic: {msg.topic} payload: {msg.payload}") self.payload = msg.payload
LED Driver
The led driver gets a list from the client of topics to which it should subscribe to. If the topic is empty the driver subscribes to all topics the board provides.
@target_factory.reg_driver @attr.s(eq=False) class LEDBoardTopicDriver(Driver): bindings = { "board": {"LEDBoardTopic"} } subscribed_topics = [] def __attrs_post_init__(self): super().__attrs_post_init__() import paho.mqtt.client as mqtt self._client = mqtt.Client() def on_activate(self): self._client.on_message = self._on_message self._client.on_connect = self._on_connect self._client.connect(self.board.host) self._client.loop_start() def _on_connect(self, client, userdata, flags, rc): print(f"<mqtt-driver> mqtt client connected") for i in self.subscribed_topics: # i.removePrefix("/") # not supported in the current python version if i != "all": self._client.subscribe(self.board.board_topic + "/" + i) print("<mqtt-driver> subscribe to " + self.board.board_topic + "/" + i) else: self._client.subscribe(self.board.board_topic) print("<mqtt-driver> subscribe to " + self.board.board_topic) def _on_message(self, client, userdata, msg): print(f"<mqtt-driver> topic: {msg.topic} payload: {msg.payload}") self.payload = msg.payload
Client
found in labgrid/remote/client.py
MQTT
Commandline Args
subparser = subparsers.add_parser('mqtt', aliases=('MQTT'), help="get a topic from a mqtt brker") subparser.add_argument('-s', '--subscribe', default="", help='wait time in seconds for msgs to arrive, -1 for infinit wait') subparser.add_argument('-t', '--time', type=float, default=float(-1), help='wait time in seconds for msgs to arrive, -1 for infinit wait') subparser.set_defaults(func=ClientSession.mqtt)
Calling the driver:
async def mqtt(self): place = self.get_acquired_place() target = self._get_target(place) topic = self.args.subscribe time = self.args.time from ..driver.mqtt import MQTTDriver try: drv = target.get_driver(MQTTDriver) except NoDriverFoundError: drv = MQTTDriver(target, name=None) drv.subscribed_topic = topic target.activate(drv) if(time < 0): while True: await asyncio.sleep(1.0) else: from ..util import Timeout timeout = Timeout(time) while True: await asyncio.sleep(1.0) if timeout.expired: break
LED
Commandline Args
subparser = subparsers.add_parser('led', aliases=('leddet'), help="get the current LED statues of a place's boards") subparser.add_argument('-t', '--time', type=float, default=float(-1), help='wait time in seconds for msgs to arrive, -1 for infinit wait') subparser.add_argument('topics', help="optional topic to subscribe, blank for all", nargs='+') subparser.set_defaults(func=ClientSession.led)
Calling/initialising the driver:
async def led(self): place = self.get_acquired_place() topics = self.args.topics target = self._get_target(place) time = self.args.time from ..driver.mqtt import LEDBoardTopicDriver try: drv = target.get_driver(LEDBoardTopicDriver) except NoDriverFoundError: drv = LEDBoardTopicDriver(target, name=None) drv.subscribed_topics = topics target.activate(drv) if time < 0: while True: await asyncio.sleep(1.0) else: from ..util import Timeout timeout = Timeout(time) while True: await asyncio.sleep(1.0) if timeout.expired: break
Labgrid developer notes
Since the labgrid documentation, was a bit confusing for us we want to add some notes and experience on how to write a proper Driver.
Steps for creating a Resource
At first you have to import the Driver module as well as the attr module
import attr
Than you have to deside what kind of resource you want to create.
There are:
Resource: Represents a resource which is used by drivers. It only stores information and does not implement any actual functionality.
NetworkResource: Represents a remote Resource available on another computer.
ManagedResource: Represents a resource which can appear and disappear at runtime. Every ManagedResource has a corresponding ResourceManager which handles these events.
In Case of our LED Driver we want to create a ManagedResource sinse we want to connect and disconnect with a broker and want to send informations.
Because of that we also need to create a ResourceManager.
import attr from labgrid.resource import ResourceManager, Resource
Labgrid uses factories for the automatic instanciation. To register our Resource we add that:
import attr from labgrid.factory import target_factory from labgrid.resource import ResourceManager, Resource @target_factory.reg_resource @attr.s(eq=False) class ExampleResourceManager(ResourceManager): examplevar1 = attr.ib() examplevar2 = attr.ib()