# 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 ```