Skip to content

Commit

Permalink
Add a way to discover Evas on your network programmatically
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisBrunner committed Jul 2, 2020
1 parent b2a7fd6 commit 0ded910
Show file tree
Hide file tree
Showing 12 changed files with 293 additions and 13 deletions.
18 changes: 11 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,31 @@ jobs:
python-version: [3.5, 3.6, 3.7]

steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install pipenv
uses: dschep/install-pipenv-action@v1
- name: Install dependencies
uses: "VaultVulp/action-pipenv@master"
uses: VaultVulp/action-pipenv@v2.0.1
with:
command: install --dev
- name: Run linter
uses: "VaultVulp/action-pipenv@master"
uses: VaultVulp/action-pipenv@v2.0.1
with:
command: run flake8
command: run lint
- name: Run type checker
uses: VaultVulp/action-pipenv@v2.0.1
with:
command: run type
- name: Run tests
uses: "VaultVulp/action-pipenv@master"
uses: VaultVulp/action-pipenv@v2.0.1
with:
command: run test --cov=evasdk --cov-branch --cov-report=xml
- name: Upload to codecov
uses: codecov/codecov-action@v1.0.3
uses: codecov/codecov-action@v1.0.7
with:
token: ${{secrets.CODECOV_TOKEN}}
file: ./coverage.xml
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ build/
dist/
.pytest_cache
*coverage*
.mypy_cache
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright 2018 Automata Technologies Ltd
Copyright 2015-2020 Automata Technologies Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
8 changes: 7 additions & 1 deletion Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ name = "pypi"
[packages]
requests = "*"
websockets = "*"
zeroconf = "==0.27.1"

[dev-packages]
flake8 = "*"
requests-mock = "*"
pytest = "*"
"pytest-cov" = "*"
"pytest-flake8" = "*"
"pytest-mypy" = "*"
mypy = "*"

[requires]

[scripts]
test = "python -m pytest tests/"
test = "pipenv run testd tests/"
testd = "python -m pytest --mypy --flake8"
lint = "flake8"
type = "mypy evasdk examples tests"
101 changes: 100 additions & 1 deletion Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ $ pipenv run test

# or to run a single test file:
$ pipenv shell
$ python -m pytest tests/<test-name>_test.py
$ pipenv run testd tests/<test-name>_test.py

# some test require supplying ip and token via the `--ip` and `--token` arguements:
$ pipenv run test --ip 172.16.16.2 --token abc-123-def-456
Expand Down
121 changes: 121 additions & 0 deletions evasdk/EvaDiscoverer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from typing import Callable
from dataclasses import dataclass
from threading import Condition
from .Eva import Eva
from zeroconf import ServiceBrowser, Zeroconf


CHOREO_SERVICE = "_automata-eva._tcp.local."


@dataclass
class DiscoveredEva:
name: str
host: str

def connect(self, token) -> Eva:
return Eva(self.host, token)


DiscoverCallback = Callable[[str, DiscoveredEva], None]


class EvaDiscoverer:


def __init__(self, callback: DiscoverCallback, name: str = None):
self.name = name
self.callback = callback
self.zeroconf = None

def __enter__(self):
self.zeroconf = Zeroconf()
self.browser = ServiceBrowser(self.zeroconf, CHOREO_SERVICE, self)

def __exit__(self, exc_type, exc_val, exc_tb):
self.zeroconf.close()

def __get_eva(self, zeroconf: Zeroconf, service_type: str, service_name: str):
info = zeroconf.get_service_info(service_type, service_name)
if info is None:
return None
return DiscoveredEva(host=info.server, name=info.properties[b'name'].decode("utf-8"))

def __filter_name(self, eva):
return self.name is not None and self.name != eva.name

def add_service(self, zeroconf: Zeroconf, service_type: str, service_name: str):
eva = self.__get_eva(zeroconf, service_type, service_name)
if eva is None or self.__filter_name(eva):
return
self.callback('added', eva)

def remove_service(self, zeroconf: Zeroconf, service_type: str, service_name: str):
eva = self.__get_eva(zeroconf, service_type, service_name)
if eva is None or self.__filter_name(eva):
return
self.callback('removed', eva)


def __find_evas(callback: DiscoverCallback, timeout: float, name: str = None, condition: Condition = None):
if condition is None:
condition = Condition()
with EvaDiscoverer(name=name, callback=callback):
with condition:
condition.wait(timeout=timeout)


def find_evas(timeout: float = 5):
"""Blocks for `timeout` seconds and returns a dictionary of DiscoveredEva (with their names as key) discovered in that time"""
evas = {}

def __callback(event: str, eva: DiscoveredEva):
if event == 'added':
evas[eva.name] = eva
elif event == 'deleted':
del evas[eva.name]

__find_evas(callback=__callback, timeout=timeout)
return evas


def find_eva(name: str, timeout: float = 5):
"""Blocks for a maximum of `timeout` seconds and returns a DiscoveredEva if a robot named `name` was found, or `None`"""
eva = None
cv = Condition()

def __callback(event: str, eva_found: DiscoveredEva):
nonlocal eva
if event == 'added':
eva = eva_found
with cv:
cv.notify()

__find_evas(name=name, callback=__callback, timeout=timeout, condition=cv)
return eva


def find_first_eva(timeout: float = 5):
"""Blocks for a maximum of `timeout` seconds and returns a DiscoveredEva if one was found, or `None`"""
eva = None
cv = Condition()

def __callback(event: str, eva_found: DiscoveredEva):
nonlocal eva
if event == 'added' and eva is None:
eva = eva_found
with cv:
cv.notify()

__find_evas(callback=__callback, timeout=timeout, condition=cv)
return eva


def discover_evas(callback: DiscoverCallback):
"""Returns a context that will discovers robots until exited
It will call `callback` with 2 arguments: the event (either `added` or `removed`) and a Discovered Eva object
Note that `callback` will be called from another thread so you will need to ensure any data accessed there is done in a thread-safe manner
"""
return EvaDiscoverer(callback=callback)
4 changes: 4 additions & 0 deletions evasdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,7 @@
EvaValidationError, EvaAuthError, EvaAutoRenewError,
EvaAdminError, EvaServerError)
from .version import __version__
from .EvaDiscoverer import (
DiscoverCallback, DiscoveredEva,
find_evas, find_eva, find_first_eva, discover_evas,
)
2 changes: 1 addition & 1 deletion evasdk/eva_http_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def gpio_get(self, pin, pin_type):
def __globals_editing(self, keys, values):
data = {'changes': []}
if (isinstance(keys, list) and isinstance(values, list)):
[data['changes'].append({'key': c[0], 'value': c[1]}) for c in zip(keys, values)]
data['changes'] = [{'key': k, 'value': v} for k, v in zip(keys, values)]
else:
data['changes'].append({'key': keys, 'value': values})
data = json.dumps(data)
Expand Down
4 changes: 4 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[mypy]

[mypy-pytest.*]
ignore_missing_imports = True
6 changes: 5 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
packages=setuptools.find_packages(),
long_description=long_description,
long_description_content_type="text/markdown",
install_requires=['requests', 'websockets'],
install_requires=[
'requests',
'websockets',
'zeroconf',
],
classifiers=[
"Programming Language :: Python :: 3",
"Development Status :: 4 - Beta",
Expand Down
Loading

0 comments on commit 0ded910

Please sign in to comment.