# MLE Labgrid setup
###### tags: `labgrid`
The only important part of this labgrid version is for the client as the client will take over the newly added driver.
## Setup
Debian Setup generally the same as the standart labgrid setup:
```bash
apt-get install python3 python3-virtualenv python3-pip python3-setuptools virtualenv
```
Setup virtual env
```bash
$ virtualenv -p python3 labgrid-venv
$ source labgrid-venv/bin/activate
```
Clone labgrid
```bash
labgrid-venv $ git clone <github-url-with-new-client>
labgrid-venv $ cd labgrid && pip install -r requirements.txt
labgrid-venv $ python3 setup.py install
```
Ready for testing:
```bash
labgrid-venv $ labgrid-client resources
```
## Usage
### MQTT Driver
**Exporter**: define a `MQTTResource` with a host like:
```yaml
<place-res-group>:
MQTTResource:
host: 127.0.0.1
```
**Client**: acquire the place with a mqtt resource and type
```
Labgrid-client -p <place> mqtt -s $Sys/# -t -1
```
while the `-s` or `--subscribe` a string takes of a topic that should be subscribed (the `#` at the end means subscribe to all topics below).
The `-t` ot `--time` tag is to define a time when the client should exit in seconds, -1 to not exit use `Strg + C` to terminate the client, default is -1
### LED driver
**Exporter**: define a `LEDBoardTopic` with a host and board topic like:
```yaml
<place-res-group>:
LEDBoardTopic:
host: 127.0.0.1
avail_topic: $SYS/broker/uptime #for debug purpoce
board_topic: changes/zcu102
```
While `zcu102` is the board identifier in this case.
**Client**: acquire the place with a mqtt resource and type
```
Labgrid-client -p <place> led all
```
or
```
Labgrid-client -p <place> led #
```
to subscribe to all topics this place provides.
Select topics by typing
```
Labgrid-client -p <place> led topic1/subtopic topic2
```
while you give the route to the topic, if you want to subscribe to all subtopics of a topic type
```
Labgrid-client -p <place> led topic/#
```
the `/#` is a whitecard for all subtopics.
## Documentation
### Resource
added the `MQTTManager` from a newer version of labgrid into `mqtt.py` within the resource directory.
```python
@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.
```python
@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:
```python
@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
#### MQTT Driver
The client will set the subscribed_topic variabel 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.
```python
@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.
```python
@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
#### MQTT
Commandline Args
```python
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:
```python
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
```python
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:
```python
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
```