From 8649b34f6ad9f2da63e504a55953839cfa7af74c Mon Sep 17 00:00:00 2001 From: Matthias Bodenbenner <m.bodenbenner@wzl-mq.rwth-aachen.de> Date: Thu, 14 Apr 2022 13:40:56 +0200 Subject: [PATCH] repo-clean up & bug fix --- .gitignore | 10 +- .gitlab-ci.yml | 39 ++--- README.md | 72 +++++----- build.bat | 7 - doc/source/index.rst | 2 +- doc/source/modules.rst | 7 - .../device/com_base_top.py | 2 +- .../device/com_basestations.py | 2 +- .../device/com_lasertracker_top.py | 6 +- .../device/com_mobileentities.py | 4 +- .../device/com_target_top.py | 2 +- examples/virtual_lasertracker/device/const.py | 0 .../virtual_lasertracker/device/device.py | 2 - examples/virtual_lasertracker/device/start.py | 16 +-- examples/virtual_lasertracker/hwc/com_base.py | 2 +- .../hwc/com_lasertracker.py | 2 +- .../virtual_lasertracker/hwc/com_target.py | 2 +- examples/virtual_lasertracker/main.py | 2 +- install.bat | 1 - setup.py | 15 +- src/http/server.py | 2 +- src/soil/component.py | 7 - src/soil/docstring_parser.py | 1 - src/soil/measurement.py | 5 +- src/soil/object.py | 7 - src/soil/variable.py | 5 +- start.bat | 7 - test.bat | 7 - test/__init__.py | 0 .../server}/__init__.py | 0 test/server/main.py | 23 +++ test/server/start.py | 133 ++++++++++++++++++ 32 files changed, 259 insertions(+), 133 deletions(-) delete mode 100644 build.bat delete mode 100644 doc/source/modules.rst delete mode 100644 examples/virtual_lasertracker/device/const.py delete mode 100644 install.bat delete mode 100644 start.bat delete mode 100644 test.bat delete mode 100644 test/__init__.py rename {examples/virtual_lasertracker => test/server}/__init__.py (100%) create mode 100644 test/server/main.py create mode 100644 test/server/start.py diff --git a/.gitignore b/.gitignore index ca1efaf..8a99bb3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,12 @@ -**/.idea/ +.idea **/__pycache__/ **/logs/ **/log/ **/build/ **/*.egg-info/ -**/venv*/ -**/dist +dist +wiki + +*.pyc + +test/server/server_config.toml \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ea66582..3816750 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,7 +18,7 @@ Build Wheel: - master - legacy script: - - '& cmd /k "C:\Anaconda3\Scripts\activate.bat E:\environments\$env:UUID & python setup.py sdist bdist_wheel"' + - python setup.py sdist bdist_wheel Copy To Package Registry: image: python:3.9 @@ -32,24 +32,25 @@ Copy To Package Registry: - master - legacy script: - - '& cmd /k "C:\Anaconda3\Scripts\activate.bat E:\environments\$env:UUID & pip install twine & python -m twine upload -u gitlab-ci-token -p $CI_JOB_TOKEN --repository-url https://git-ce.rwth-aachen.de/api/v4/projects/${CI_PROJECT_ID}/packages/pypi dist/*"' - -pages: - image: python:3.9 - stage: deploy - tags: - - frodo - only: - - master - script: - - pip3 install -r requirements.txt - - mkdir public - - cd doc - - make html - - cp -r ./build/html/. ../public - artifacts: - paths: - - public + - pip install twine + - python -m twine upload -u gitlab-ci-token -p $CI_JOB_TOKEN --repository-url https://git-ce.rwth-aachen.de/api/v4/projects/${CI_PROJECT_ID}/packages/pypi dist/* + +#pages: +# image: python:3.9 +# stage: deploy +# tags: +# - frodo +# only: +# - master +# script: +# - pip3 install -r requirements.txt +# - mkdir public +# - cd doc +# - make html +# - cp -r ./build/html/. ../public +# artifacts: +# paths: +# - public diff --git a/README.md b/README.md index 68ec6ba..b2a2e68 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [](https://git-ce.rwth-aachen.de/wzl-mq-ms/forschung-lehre/lava/unified-device-interface/python/commits/master) # Python Unified Device Interface -Current stable version: 6.1.1 +Current stable version: 6.1.2 Stable legacy version: 5.2.7 ## Installation @@ -20,46 +20,52 @@ pip install --extra-index-url https://package-read:sTkDQPiyBqkUyYVXZfeK@git-ce.r ## Usage For using the PUDI device interface you need to have a SOIL-Model of your device in JSON-Format. See the Wiki for more information. -You can design the model by hand our use the [SOIL-Editor](https://git-ce.rwth-aachen.de/wzl-mq-ms/forschung-lehre/modelizer), to generate the source code and get a ready-to-run python script for your device server. +You can design the model by hand our use the [SOIL-Web-Editor](https://iot.wzl-mq.rwth-aachen.de/soil/), to generate the source code and get a ready-to-run python script for your device server. Please keep in mind, that creating a service with the SOIL-Editor gives your more features you can use. ### Manual setup If you do not use the SOIL-Editor, create a JSON-Model of your device and a mapping dictionary by hand. - Please refer to the Servetize section of the repository's Wiki. -Setup your server as follows: - -```python - # set up servers - executor = ThreadPoolExecutor(max_workers=100) - loop = asyncio.get_event_loop() - loop.set_default_executor(executor) - - # configure model - macs = ['AC-DE-48-00-00-80', 'AE-DE-48-00-00-80', 'BC-DE-48-00-00-80'] - sensors = [Sensor(macs[i]) for i in range(3)] - implementation = Environment(sensors) - - # configure HTTP server - mapping = src.test.devices.mapping.mapping(implementation) - model = Component.load('./src/test/devices/environmental_sensor/Environment.json', mapping) - http = HTTPServer(loop, HTTP_ADDRESS, HTTP_PORT, model) - - # start servers - try: - loop.run_forever() - except: - pass - finally: - loop.close() -``` + Please refer to the Servetize section of the repository's Wiki. Additionally, you can check the _example_ directory for exemplary implementation(s). + +## Citation & References + +Scientific background and publications related to the _(Python) Unified Device Interface_ are: + +[Bodenbenner, M.](mailto:m.bodenbenner@wzl-mq.rwth-aachen.de); Sanders, M. P.; Montavon, B.; Schmitt, R. H. (2021): +Domain-Specific Language for Sensors in the Internet of Production. +In: Bernd-Arno Behrens, Alexander Brosius, Wolfgang Hintze, Steffen Ihlenfeldt und Jens Peter Wulfsberg (Hg.): +Production at the leading edge of technology. Proceedings of the 10th Congress of the German Academic Association for Production Technology (WGP), Dresden, 23-24 September 2020. Berlin, Heidelberg, 2021. 1st ed. 2021. Berlin, Heidelberg: Springer (Lecture Notes in Production Engineering), S. 448–456, +http://dx.doi.org/10.1007/978-3-662-62138-7_45 + +[Bodenbenner, M.](mailto:m.bodenbenner@wzl-mq.rwth-aachen.de); Montavon, B.; Schmitt, R.H. (2021): +FAIR sensor services - Towards sustainable sensor data management. +In: Measurement: Sensors 18, S. 100206, +https://doi.org/10.1016/j.measen.2021.100206 + +[Montavon, B.](mailto:b.montavon@wzl-mq.rwth-aachen.de)(2021): +Virtual Reference Frame Based on Distributed Large-Scale Metrology Providing Coordinates as a Service. +Aachen: Apprimus Verlag, +https://doi.org/10.18154/RWTH-2021-10238 -1. Do not change the three lines below `# set up servers` and below `# start servers`. -2. Replace the lines below `# configure model` by the initialization of your device. -3. Pass your initialized device to the docstring parser for MQTT and HTTP. -4. If you are not using MQTT or HTTP leave out the respective lines. +[Montavon, B.](mailto:b.montavon@wzl-mq.rwth-aachen.de); Peterek, M.; Schmitt, R. H. (2019): +Model-based interfacing of large-scale metrology instruments. +In: Ettore Stella (Hg.): Multimodal Sensing: Technologies and Applications. 26-27 June 2019, Munich, Germany. Multimodal Sensing and Artificial Intelligence: Technologies and Applications. Munich, Germany, 6/24/2019 - 6/27/2019. Bellingham, Washington: SPIE (Proceedings of SPIE. 5200-, volume 11059), S. 11, +https://doi.org/10.1117/12.2527461 + +## Acknowledgements + +The authors acknowledge funding from the LaVA project (Large Volume Applications, contract 17IND03 of the +European Metrology Programme for Innovation and Research EMPIR). The EMPIR initiative is co-funded by +the European Union’s Horizon 2020 research and innovation programme and the EMPIR Participating States. + +Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) under Germany's Excellence Strategy – EXC-2023 Internet of Production – 390621612. ## Recent changes +6.1.2 | 5.2.7 - 2022-04-14 + +- bug fix of loop handling of aiohttp web application + 6.1.1 | 5.2.7 - 2021-05-19 - improved error output for developers diff --git a/build.bat b/build.bat deleted file mode 100644 index 1b35742..0000000 --- a/build.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -rmdir dist /s /q -rmdir build /s /q -rmdir wzl_udi.egg-info /s /q -python setup.py bdist_wheel -:: TODO complete line below -:: copy "../dist/" "D:\users\bdn\Forschungsprojekte\Virtual Metrology Frame\SourceCode\PyPi server" \ No newline at end of file diff --git a/doc/source/index.rst b/doc/source/index.rst index c151183..39b5037 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -4,7 +4,7 @@ contain the root `toctree` directive. Welcome to Python Unified Device Interface's documentation! -============================================== +=========================================================== .. toctree:: :maxdepth: 2 diff --git a/doc/source/modules.rst b/doc/source/modules.rst deleted file mode 100644 index e9ff8ac..0000000 --- a/doc/source/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -src -=== - -.. toctree:: - :maxdepth: 4 - - src diff --git a/examples/virtual_lasertracker/device/com_base_top.py b/examples/virtual_lasertracker/device/com_base_top.py index d8c1368..8337d53 100644 --- a/examples/virtual_lasertracker/device/com_base_top.py +++ b/examples/virtual_lasertracker/device/com_base_top.py @@ -1,5 +1,5 @@ from typing import List -from .enums import StateEnum +from device.enums import StateEnum class COMBaseTOP(object): diff --git a/examples/virtual_lasertracker/device/com_basestations.py b/examples/virtual_lasertracker/device/com_basestations.py index d4022e8..be47842 100644 --- a/examples/virtual_lasertracker/device/com_basestations.py +++ b/examples/virtual_lasertracker/device/com_basestations.py @@ -1,4 +1,4 @@ -from ..hwc.com_base import COMBase +from hwc.com_base import COMBase class COMBaseStations(object): diff --git a/examples/virtual_lasertracker/device/com_lasertracker_top.py b/examples/virtual_lasertracker/device/com_lasertracker_top.py index d3fb3d5..cdba60e 100644 --- a/examples/virtual_lasertracker/device/com_lasertracker_top.py +++ b/examples/virtual_lasertracker/device/com_lasertracker_top.py @@ -1,8 +1,8 @@ import datetime -from .com_basestations import COMBaseStations -from .com_mobileentities import COMMobileEntities -from .enums import StateEnum +from device.com_basestations import COMBaseStations +from device.com_mobileentities import COMMobileEntities +from device.enums import StateEnum class COMLasertrackerTOP(object): diff --git a/examples/virtual_lasertracker/device/com_mobileentities.py b/examples/virtual_lasertracker/device/com_mobileentities.py index 45989fd..dfc1eb1 100644 --- a/examples/virtual_lasertracker/device/com_mobileentities.py +++ b/examples/virtual_lasertracker/device/com_mobileentities.py @@ -1,5 +1,5 @@ -from ..hwc.com_target import COMTarget -from .enums import StateEnum, ModeEnum +from hwc.com_target import COMTarget +from device.enums import StateEnum, ModeEnum class COMMobileEntities(object): diff --git a/examples/virtual_lasertracker/device/com_target_top.py b/examples/virtual_lasertracker/device/com_target_top.py index 73a58b5..89cf1aa 100644 --- a/examples/virtual_lasertracker/device/com_target_top.py +++ b/examples/virtual_lasertracker/device/com_target_top.py @@ -1,7 +1,7 @@ from typing import Callable from typing import List -from .enums import StateEnum, ModeEnum +from device.enums import StateEnum, ModeEnum class COMTargetTOP(object): diff --git a/examples/virtual_lasertracker/device/const.py b/examples/virtual_lasertracker/device/const.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/virtual_lasertracker/device/device.py b/examples/virtual_lasertracker/device/device.py index f47c3bd..2ecd6e6 100644 --- a/examples/virtual_lasertracker/device/device.py +++ b/examples/virtual_lasertracker/device/device.py @@ -1,9 +1,7 @@ class Device(object): def __init__(self): - # TODO implement pass def __del__(self): - # TODO implement pass \ No newline at end of file diff --git a/examples/virtual_lasertracker/device/start.py b/examples/virtual_lasertracker/device/start.py index 2835b9e..78413a9 100644 --- a/examples/virtual_lasertracker/device/start.py +++ b/examples/virtual_lasertracker/device/start.py @@ -7,17 +7,17 @@ from typing import Dict from wzl.mqtt import MQTTPublisher from wzl.utilities import root_logger -from src.http.server import HTTPServer -from src.soil.component import Component -from src.soil.event import Event, EventSeverity, EventTrigger -from src.soil.stream import FixedJob, EventJob, UpdateJob, StreamScheduler -from .enums import StateEnum -from ..hwc.com_lasertracker import COMLasertracker +from wzl.http.server import HTTPServer +from wzl.soil.component import Component +from wzl.soil.event import Event, EventSeverity, EventTrigger +from wzl.soil.stream import FixedJob, EventJob, UpdateJob, StreamScheduler +from device.enums import StateEnum +from hwc.com_lasertracker import COMLasertracker sys.setswitchinterval(0.0005) -def start(com_lasertracker: COMLasertracker, config: Dict): +def start(com_lasertracker: COMLasertracker, config: Dict, soil_model_file: str): # server settings address = config['http']['address'] port = config['http']['port'] @@ -119,7 +119,7 @@ def start(com_lasertracker: COMLasertracker, config: Dict): submapping['FUN-Trigger'] = { 'method': com_lasertracker._com_mobileentities._com_target[child0_uuid].fun_trigger, 'signature': {'arguments': {}, 'returns': []}, 'mqtt_callback': mqtt.publish} - model = Component.load('../Lasertracker.json', mapping['COM-Lasertracker']) + model = Component.load(soil_model_file, mapping['COM-Lasertracker']) http = HTTPServer(loop, address, port, model) diff --git a/examples/virtual_lasertracker/hwc/com_base.py b/examples/virtual_lasertracker/hwc/com_base.py index c918499..9586d61 100644 --- a/examples/virtual_lasertracker/hwc/com_base.py +++ b/examples/virtual_lasertracker/hwc/com_base.py @@ -1,7 +1,7 @@ import random from math import pi, cos, sin -from ..device.com_base_top import COMBaseTOP +from device.com_base_top import COMBaseTOP class COMBase(COMBaseTOP): diff --git a/examples/virtual_lasertracker/hwc/com_lasertracker.py b/examples/virtual_lasertracker/hwc/com_lasertracker.py index 1887e52..a613868 100644 --- a/examples/virtual_lasertracker/hwc/com_lasertracker.py +++ b/examples/virtual_lasertracker/hwc/com_lasertracker.py @@ -1,6 +1,6 @@ import datetime -from ..device.com_lasertracker_top import COMLasertrackerTOP +from device.com_lasertracker_top import COMLasertrackerTOP class COMLasertracker(COMLasertrackerTOP): diff --git a/examples/virtual_lasertracker/hwc/com_target.py b/examples/virtual_lasertracker/hwc/com_target.py index f0b0f9c..923da54 100644 --- a/examples/virtual_lasertracker/hwc/com_target.py +++ b/examples/virtual_lasertracker/hwc/com_target.py @@ -1,7 +1,7 @@ import json import time -from ..device.com_target_top import COMTargetTOP +from device.com_target_top import COMTargetTOP class COMTarget(COMTargetTOP): diff --git a/examples/virtual_lasertracker/main.py b/examples/virtual_lasertracker/main.py index e9a04b9..27c0eb7 100644 --- a/examples/virtual_lasertracker/main.py +++ b/examples/virtual_lasertracker/main.py @@ -12,4 +12,4 @@ if __name__ == "__main__": lasertracker = COMLasertracker(device) device.start() - start(lasertracker, config) + start(lasertracker, config, './Lasertracker.json') diff --git a/install.bat b/install.bat deleted file mode 100644 index 838c3f2..0000000 --- a/install.bat +++ /dev/null @@ -1 +0,0 @@ -FOR /F "delims=~" %f in (packages.txt) DO conda install --yes "%f" || pip install "%f" \ No newline at end of file diff --git a/setup.py b/setup.py index a2d7aec..4ad8f42 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,20 @@ from setuptools import setup, find_packages setup(name='wzl-udi', - version='6.1.1', - url='', + version='6.1.2', + url='', # TODO add url after moving to public group author='Matthias Bodenbenner', - author_email='m.bodenbenner@wzl.rwth-aachen.de', + author_email='m.bodenbenner@wzl.mq.rwth-aachen.de', description='Provides REST-server, publisher-interface and serializer for the Unified Device Interface in Python.', package_dir={'wzl': 'src'}, packages=['wzl.http', 'wzl.soil', 'wzl.utils'], long_description=open('./README.md').read(), - install_requires=['nest-asyncio', 'strict-rfc3339', 'docstring_parser==0.7.1', 'aiohttp', 'wzl-mqtt', 'wzl-utilities', 'deprecated'], + install_requires=['aiohttp==3.8.1', + 'Deprecated==1.2.13', + 'docstring_parser==0.7.3', + 'nest-asyncio==1.4.3', + 'strict-rfc3339==0.7', + 'wzl-mqtt==2.5.1', + 'wzl-utilities==1.2.2' + ], zip_safe=False) diff --git a/src/http/server.py b/src/http/server.py index 225e668..ee6c219 100644 --- a/src/http/server.py +++ b/src/http/server.py @@ -66,7 +66,7 @@ class HTTPServer(object): self.app.router.add_put(r'/{uuids:(' + BASE_UUID_PATTERN + r'($|/))*}', self.put) self.app.router.add_patch(r'/objects{uuids:(/' + BASE_UUID_PATTERN + r')*/?}', self.patch) self.app.router.add_patch(r'/{uuids:(' + BASE_UUID_PATTERN + r'($|/))*}', self.patch) - web.run_app(self.app, host=self.host, port=self.port) + web.run_app(self.app, host=self.host, port=self.port, loop=loop) logger.info('HTTP-Server serving on {}:{}'.format(host, port)) @staticmethod diff --git a/src/soil/component.py b/src/soil/component.py index 77cd5c2..c0bb6eb 100644 --- a/src/soil/component.py +++ b/src/soil/component.py @@ -208,13 +208,6 @@ class Component(Element): if 'children' in keys: everything = self._components + self._measurements + self._parameters + self._functions dictionary['children'] = list(map(lambda x: x.serialize(['name', 'uuid']), everything)) - # if 'update' in keys: - # # TODO implement (update all children) - # pass - # # if self._implementation is not None: - # # dict['update'] = self._implementation() - # else: - # dict['children'] = list(map(lambda x: x.serialize(['value', 'uuid']), self._measurements)) return dictionary @staticmethod diff --git a/src/soil/docstring_parser.py b/src/soil/docstring_parser.py index 39c48aa..e1d7559 100644 --- a/src/soil/docstring_parser.py +++ b/src/soil/docstring_parser.py @@ -89,7 +89,6 @@ def parse_docstrings_for_mqtt(implementation, parent_url="", *args, **kwargs): @deprecated(version='6.0.0', reason='Building service model from docstrings is too error-prone.') def parse_docstrings_for_soil(implementation: Any, parent_url="", *args, **kwargs): - # TODO replace assertions by reasonable error handling if implementation is None: return None diff --git a/src/soil/measurement.py b/src/soil/measurement.py index c38f792..875980e 100644 --- a/src/soil/measurement.py +++ b/src/soil/measurement.py @@ -16,8 +16,8 @@ class Measurement(Figure): if uuid[:3] != 'MEA': raise Exception('{}: The UUID must start with MEA!'.format(uuid)) self._unit = unit - self._covariance = None # TODO init - self._uncertainty = None # TODO init + self._covariance = None + self._uncertainty = None self._timestamp = None self._nonce = nonce @@ -90,7 +90,6 @@ class Measurement(Figure): if not keys: keys = ['uuid', 'name', 'description', 'datatype', 'value', 'dimension', 'range', 'timestamp', 'nonce', 'covariance', 'uncertainty', 'unit', 'ontology'] - # TODO temporary workaround, forces the server to always return the timestamp if the value is queried if 'value' in keys and 'timestamp' not in keys: keys += ['timestamp'] dictionary = {} diff --git a/src/soil/object.py b/src/soil/object.py index ade708a..270af62 100644 --- a/src/soil/object.py +++ b/src/soil/object.py @@ -134,13 +134,6 @@ class Object(Element): if 'children' in keys: everything = self._objects + self._variables + self._parameters + self._functions dictionary['children'] = list(map(lambda x: x.serialize(['name', 'uuid']), everything)) - # if 'update' in keys: - # # TODO implement (update all children) - # pass - # # if self._implementation is not None: - # # dict['update'] = self._implementation() - # else: - # dict['children'] = list(map(lambda x: x.serialize(['value', 'uuid']), self._variables)) return dictionary @staticmethod diff --git a/src/soil/variable.py b/src/soil/variable.py index 8367966..0279eaa 100644 --- a/src/soil/variable.py +++ b/src/soil/variable.py @@ -15,8 +15,8 @@ class Variable(Figure): def __init__(self, uuid, name, description, datatype, dimension, range, getter, unit, nonce=None, ontology: str = None): Figure.__init__(self, uuid, name, description, datatype, dimension, range, None, getter, ontology) self._unit = unit - self._covariance = None # TODO init - self._uncertainty = None # TODO init + self._covariance = None + self._uncertainty = None self._timestamp = None self._nonce = nonce @@ -89,7 +89,6 @@ class Variable(Figure): if not keys: keys = ['uuid', 'name', 'description', 'datatype', 'value', 'dimension', 'range', 'timestamp', 'nonce', 'covariance', 'uncertainty', 'unit', 'ontology'] - # TODO temporary workaround, forces the server to always return the timestamp if the value is queried if 'value' in keys and 'timestamp' not in keys: keys += ['timestamp'] dictionary = {} diff --git a/start.bat b/start.bat deleted file mode 100644 index 8e8141d..0000000 --- a/start.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -TITLE Distributed Environmental Sensor System (Server) -call conda activate pudi -cls -set PYTHONPATH=%PYTHONPATH%;./src -echo ##### Start server... -python main.py 0.0.0.0 8015 diff --git a/test.bat b/test.bat deleted file mode 100644 index 60949af..0000000 --- a/test.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off -start cmd /c "TITLE TestServer& call conda activate udi & python main.py localhost 8015" -cd ./test -call conda activate udi -python test.py -set /p DUMMY=Hit ENTER to close the test... -taskkill /IM cmd.exe /FI "WINDOWTITLE eq TestServer" \ No newline at end of file diff --git a/test/__init__.py b/test/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/examples/virtual_lasertracker/__init__.py b/test/server/__init__.py similarity index 100% rename from examples/virtual_lasertracker/__init__.py rename to test/server/__init__.py diff --git a/test/server/main.py b/test/server/main.py new file mode 100644 index 0000000..5162fbb --- /dev/null +++ b/test/server/main.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +import os +import sys + +import toml as toml + +sys.path.insert(0, os.path.abspath(os.path.join('..', '..', 'examples', 'virtual_lasertracker'))) +sys.path.insert(0, os.path.abspath(os.path.join('..', '..'))) + +from start import start +from hwc.com_lasertracker import COMLasertracker +from lt_device import LTDevice + +if __name__ == "__main__": + + config = toml.load('server_config.toml') + soil_model_file = os.path.join('../..', 'examples', 'virtual_lasertracker', 'Lasertracker.json') + + device = LTDevice() + lasertracker = COMLasertracker(device) + device.start() + + start(lasertracker, config, soil_model_file) diff --git a/test/server/start.py b/test/server/start.py new file mode 100644 index 0000000..b4c01c1 --- /dev/null +++ b/test/server/start.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +import asyncio +import sys +from concurrent.futures import ThreadPoolExecutor +from typing import Dict + +from wzl.mqtt import MQTTPublisher +from wzl.utilities import root_logger + +from src.http.server import HTTPServer +from src.soil.component import Component +from src.soil.event import Event, EventSeverity, EventTrigger +from src.soil.stream import FixedJob, EventJob, UpdateJob, StreamScheduler +from device.enums import StateEnum +from hwc.com_lasertracker import COMLasertracker + +sys.setswitchinterval(0.0005) + + +def start(com_lasertracker: COMLasertracker, config: Dict, soil_model_file: str): + # server settings + address = config['http']['address'] + port = config['http']['port'] + + # mqtt settings + mqtt_host = config['mqtt']['host'] + mqtt_port = config['mqtt']['port'] + mqtt_vhost = config['mqtt']['vhost'] + mqtt_username = config['mqtt']['username'] + mqtt_password = config['mqtt']['password'] + mqtt_ssl = config['mqtt']['ssl'] + mqtt_websockets = config['mqtt']['websockets'] + + main_logger = root_logger.get(__name__) + + # set up servers + executor = ThreadPoolExecutor(max_workers=100) + loop = asyncio.get_event_loop() + loop.set_default_executor(executor) + + # configure mqtt + mqtt = MQTTPublisher(mqtt_username, prefix=mqtt_username) + mqtt.connect(mqtt_host, mqtt_username, mqtt_password, vhost=mqtt_vhost, port=mqtt_port, ssl=mqtt_ssl, + websocket=mqtt_websockets) + + # configure messages + schedule = [] + schedule += [FixedJob("COM-Lasertracker/COM-BaseStations/COM-Base/MEA-Position", 30, + com_lasertracker._com_basestations._com_base.get_mea_position)] + for child0_uuid in com_lasertracker._com_mobileentities._com_target: + schedule += [UpdateJob("COM-Lasertracker/COM-MobileEntities/{}/MEA-Position".format(child0_uuid), + com_lasertracker._com_mobileentities._com_target[child0_uuid].get_mea_position)] + schedule += [UpdateJob("COM-Lasertracker/COM-MobileEntities/{}/MEA-Quaternion".format(child0_uuid), + com_lasertracker._com_mobileentities._com_target[child0_uuid].get_mea_quaternion)] + + # configure events + schedule += [EventJob("COM-Lasertracker/COM-BaseStations/COM-Base/MEA-Distance", 10, + com_lasertracker._com_basestations._com_base.get_mea_distance, + Event(EventSeverity.WARNING, EventTrigger.LARGER, "double", 50, + "The target is distance is large. Uncertainty might be high."))] + schedule += [EventJob("COM-Lasertracker/COM-BaseStations/COM-Base/PAR-State", 10, + com_lasertracker._com_basestations._com_base.get_par_state, + Event(EventSeverity.ERROR, EventTrigger.EQUALS, "enum", StateEnum.ERROR, + "An error occured!"))] + for child0_uuid in com_lasertracker._com_mobileentities._com_target: + schedule += [EventJob("COM-Lasertracker/COM-MobileEntities/{}/PAR-Locked".format(child0_uuid), 10, + com_lasertracker._com_mobileentities._com_target[child0_uuid].get_par_locked, + Event(EventSeverity.WARNING, EventTrigger.EQUALS, "bool", False, + "Target is not locked!"))] + + scheduler = StreamScheduler(loop, schedule, [mqtt], start_immediately=True) + + # configure model + mapping = {} + mapping['COM-Lasertracker'] = {'add': None, 'remove': None} + submapping = mapping['COM-Lasertracker'] + submapping['PAR-State'] = {'getter': com_lasertracker.get_par_state, 'setter': com_lasertracker.set_par_state} + submapping['PAR-Manufacturer'] = {'getter': com_lasertracker.get_par_manufacturer, 'setter': None} + submapping['PAR-Version'] = {'getter': com_lasertracker.get_par_version, 'setter': None} + submapping['PAR-Time'] = {'getter': com_lasertracker.get_par_time, 'setter': com_lasertracker.set_par_time} + submapping['FUN-Reset'] = {'method': com_lasertracker.fun_reset, 'signature': {'arguments': {}, 'returns': []}} + submapping['FUN-Shutdown'] = {'method': com_lasertracker.fun_shutdown, + 'signature': {'arguments': {}, 'returns': []}} + mapping['COM-Lasertracker']['COM-BaseStations'] = {'add': None, 'remove': None} + submapping = mapping['COM-Lasertracker']['COM-BaseStations'] + mapping['COM-Lasertracker']['COM-BaseStations']['COM-Base'] = {'add': None, 'remove': None} + submapping = mapping['COM-Lasertracker']['COM-BaseStations']['COM-Base'] + submapping['MEA-Position'] = com_lasertracker._com_basestations._com_base.get_mea_position + submapping['MEA-Quaternion'] = com_lasertracker._com_basestations._com_base.get_mea_quaternion + submapping['MEA-Azimuth'] = com_lasertracker._com_basestations._com_base.get_mea_azimuth + submapping['MEA-Elevation'] = com_lasertracker._com_basestations._com_base.get_mea_elevation + submapping['MEA-Distance'] = com_lasertracker._com_basestations._com_base.get_mea_distance + submapping['PAR-State'] = {'getter': com_lasertracker._com_basestations._com_base.get_par_state, 'setter': None} + submapping['PAR-Interval'] = {'getter': com_lasertracker._com_basestations._com_base.get_par_interval, + 'setter': com_lasertracker._com_basestations._com_base.set_par_interval} + submapping['FUN-Jog'] = {'method': com_lasertracker._com_basestations._com_base.fun_jog, 'signature': { + 'arguments': {'ARG-Azimuth': 'arg_azimuth', 'ARG-Elevation': 'arg_elevation'}, 'returns': []}} + submapping['FUN-PointTo'] = {'method': com_lasertracker._com_basestations._com_base.fun_pointto, + 'signature': {'arguments': {'ARG-Position': 'arg_position'}, 'returns': []}} + mapping['COM-Lasertracker']['COM-MobileEntities'] = {'add': com_lasertracker._com_mobileentities.add, + 'remove': com_lasertracker._com_mobileentities.remove} + submapping = mapping['COM-Lasertracker']['COM-MobileEntities'] + for child0_uuid in com_lasertracker._com_mobileentities._com_target: + mapping['COM-Lasertracker']['COM-MobileEntities'][child0_uuid] = {'add': None, 'remove': None} + submapping = mapping['COM-Lasertracker']['COM-MobileEntities'][child0_uuid] + submapping['MEA-Position'] = com_lasertracker._com_mobileentities._com_target[child0_uuid].get_mea_position + submapping['MEA-Quaternion'] = com_lasertracker._com_mobileentities._com_target[child0_uuid].get_mea_quaternion + submapping['PAR-State'] = { + 'getter': com_lasertracker._com_mobileentities._com_target[child0_uuid].get_par_state, 'setter': None} + submapping['PAR-Mode'] = {'getter': com_lasertracker._com_mobileentities._com_target[child0_uuid].get_par_mode, + 'setter': None} + submapping['PAR-Type'] = {'getter': com_lasertracker._com_mobileentities._com_target[child0_uuid].get_par_type, + 'setter': None} + submapping['PAR-Locked'] = { + 'getter': com_lasertracker._com_mobileentities._com_target[child0_uuid].get_par_locked, + 'setter': com_lasertracker._com_mobileentities._com_target[child0_uuid].set_par_locked} + submapping['FUN-Reset'] = {'method': com_lasertracker._com_mobileentities._com_target[child0_uuid].fun_reset, + 'signature': {'arguments': {}, 'returns': []}} + submapping['FUN-Trigger'] = { + 'method': com_lasertracker._com_mobileentities._com_target[child0_uuid].fun_trigger, + 'signature': {'arguments': {}, 'returns': []}, 'mqtt_callback': mqtt.publish} + model = Component.load(soil_model_file, mapping['COM-Lasertracker']) + + http = HTTPServer(loop, address, port, model) + + # start servers + main_logger.info("Starting main asynchronous loop") + try: + loop.run_forever() + except: + pass + finally: + loop.close() -- GitLab