zfl 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
zfl-0.1.1/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2023, UH Robot House / User Projects
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
zfl-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: zfl
3
+ Version: 0.1.1
4
+ Summary: Data server via HTTPS POST
5
+ Author-email: Patrick Holthaus <patrick.holthaus@googlemail.com>
6
+ License-Expression: BSD-2-Clause
7
+ Project-URL: Homepage, https://gitlab.com/robothouse/rh-projects/hospital-at-home/data-server/
8
+ Project-URL: Bug Tracker, https://gitlab.com/robothouse/rh-projects/hospital-at-home/data-server/issues/
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: requests
15
+ Requires-Dist: cherrypy
16
+ Requires-Dist: ztl>=0.3.1
17
+ Dynamic: license-file
18
+
19
+ # Data Server
20
+
21
+ This will become the project to enable data transfer and logging
zfl-0.1.1/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Data Server
2
+
3
+ This will become the project to enable data transfer and logging
@@ -0,0 +1,28 @@
1
+ [project]
2
+ name = "zfl"
3
+ version = "0.1.1"
4
+ authors = [
5
+ { name="Patrick Holthaus", email="patrick.holthaus@googlemail.com" },
6
+ ]
7
+ description = "Data server via HTTPS POST"
8
+ readme = "README.md"
9
+ license = "BSD-2-Clause"
10
+ license-files = [ "LICENSE" ]
11
+ requires-python = ">=3.10"
12
+ classifiers = [
13
+ "Programming Language :: Python",
14
+ "Operating System :: OS Independent"
15
+ ]
16
+ dependencies = [
17
+ "requests",
18
+ "cherrypy",
19
+ "ztl >= 0.3.1"
20
+ ]
21
+
22
+ [project.urls]
23
+ "Homepage" = "https://gitlab.com/robothouse/rh-projects/hospital-at-home/data-server/"
24
+ "Bug Tracker" = "https://gitlab.com/robothouse/rh-projects/hospital-at-home/data-server/issues/"
25
+
26
+ [project.scripts]
27
+ zfl_logging_service = "zfl.local.federated_logging_service:main_cli"
28
+ zfl_cloud_server = "zfl.server.federated_cloud_service:main_cli"
zfl-0.1.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,20 @@
1
+ import pprint
2
+
3
+ from zfl.logging.state import CachedLocalLog
4
+
5
+ if __name__ == "__main__":
6
+
7
+ l = CachedLocalLog()
8
+ l.add_state({"some data": "hello"})
9
+ l.add_state({"some other data": "something"})
10
+ l.add_state({"some other data": "something2"})
11
+ l.add_state({"dhey": 4})
12
+ l.add_state({"some data": "hello"})
13
+ l.add_state({"some other data": "something"})
14
+ l.add_state({"some other data": "something2"})
15
+ l.add_state({"dhey": 4})
16
+ l.add_state({"some data": "hello"})
17
+ l.add_state({"some other data": "something"})
18
+ l.add_state({"some other data": "something2"})
19
+ l.add_state({"dhey": 4})
20
+ pprint.pp(l.get_state())
@@ -0,0 +1,20 @@
1
+ import pprint
2
+ from zfl.logging.state import SystemState
3
+
4
+ def some_function(obj):
5
+ print(obj)
6
+
7
+ if __name__ == "__main__":
8
+
9
+ state = SystemState()
10
+
11
+ # Update status (indicate whether cloud needs to be notified as well)
12
+ pprint.pp(state.update_state({"persondetection": {"id": 2, "state": "present"}}, cloud = True))
13
+
14
+ # Retrieve latest status
15
+ state.update_state('{"something": "sdf"}')
16
+ pprint.pp(state.get_state())
17
+
18
+ # Alternative: register callback
19
+ # state.dispatch_updates(some_function)
20
+
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import requests
4
+
5
+ if __name__ == '__main__':
6
+ headers = {'Content-Type': 'application/json'}
7
+ response = requests.post('http://localhost:8080/process', headers=headers, json={"test":"value"}, verify=False)
8
+ response.raise_for_status()
9
+ print(response.url)
10
+ print(str(response.status_code) + ": " + response.json())
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import argparse
4
+ import time
5
+
6
+ from ztl.core.subscriber import ObjectSubscriber
7
+ from ztl.core.config import ZMQEndpoints
8
+
9
+ def callback(obj):
10
+ print("received an object")
11
+
12
+
13
+ def main_cli():
14
+
15
+ endpoints = ZMQEndpoints()
16
+ sub = endpoints.get_subscriber("federation")
17
+ sub.register_callback(callback)
18
+ s = sub.register_callback(print)
19
+
20
+ try:
21
+ sub.start()
22
+ time.sleep(5)
23
+ sub.remove_callback(s)
24
+
25
+ except KeyboardInterrupt:
26
+ print("Exit signal received.")
27
+ sub.stop()
28
+
29
+
30
+ if __name__ == "__main__":
31
+
32
+ main_cli()
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import argparse
4
+ import requests
5
+
6
+ from ztl.core.server import TaskServer
7
+ from ztl.core.protocol import State, Task
8
+ from ztl.core.task import ExecutableTask, TaskExecutor, TaskController
9
+
10
+
11
+ class HTTPSPostTask(ExecutableTask):
12
+
13
+ def __init__(self, remote, request):
14
+ self.headers = {'Content-Type': 'application/json'}
15
+ self.remote = remote
16
+ self.request = request
17
+
18
+ def execute(self):
19
+ response = requests.post(self.remote, headers=self.headers, json=self.request, verify=False)
20
+ response.raise_for_status()
21
+ return response.json()
22
+
23
+ def abort(self):
24
+ return False
25
+
26
+
27
+ class Controller(TaskController):
28
+
29
+ def __init__(self, remote):
30
+ super(Controller, self).__init__()
31
+ self.remote = remote
32
+
33
+ def assign(self, handler, component, goal):
34
+ return HTTPSPostTask, self.remote, dict(zip(Task.FIELDS, [handler, component, goal]))
35
+
36
+ def main_cli():
37
+
38
+ parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
39
+ parser.add_argument("-p", "--port", type=int,
40
+ help="The port on the local machine that the server should listen to.", required=True)
41
+ parser.add_argument("-s", "--scope", type=str,
42
+ help="The scope that the server should respond to.", required=True)
43
+ parser.add_argument("-r", "--remote", type=str,
44
+ help="The remote server address.", required=True)
45
+
46
+ args, unknown = parser.parse_known_args()
47
+
48
+ server = TaskServer(args.port)
49
+ server.register(args.scope, Controller(args.remote))
50
+ server.listen()
51
+
52
+ if __name__ == "__main__":
53
+
54
+ main_cli()
File without changes
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import argparse
4
+ import requests
5
+ import json
6
+
7
+ from ztl.core.server import TaskServer
8
+ from ztl.core.protocol import State, Task
9
+ from ztl.core.task import ExecutableTask, TaskExecutor, TaskController
10
+ from ztl.core.publisher import ObjectPublisher
11
+ from ztl.core.config import ZMQEndpoints
12
+
13
+ from zfl.local.syslog import CachedLocalLog
14
+
15
+ class StatusUpdateTask(ExecutableTask):
16
+
17
+ def __init__(self, syslog, publisher, goal, remote = None):
18
+ self.headers = {'Content-Type': 'application/json'}
19
+ self.syslog = syslog
20
+ self.remote = remote
21
+ self.publisher = publisher
22
+ self.update = json.loads(goal)
23
+
24
+ def execute(self):
25
+ # local update (always performed)
26
+ self.syslog.add_state(self.update)
27
+ new_state = self.syslog.get_state()
28
+ self.publisher.publish(new_state)
29
+
30
+ # remote update (only if requested)
31
+ if self.remote is None:
32
+ return new_state
33
+
34
+ request = {'handler': "something",
35
+ 'component': "something",
36
+ 'goal': self.update
37
+ }
38
+
39
+ response = requests.post(self.remote, headers=self.headers, json=request, verify=False)
40
+ response.raise_for_status()
41
+ return new_state, response.json()
42
+
43
+
44
+ class StatusRequestTask(ExecutableTask):
45
+
46
+ def __init__(self, syslog, goal):
47
+ self.syslog = syslog
48
+ self.goal = goal
49
+
50
+ def execute(self):
51
+ return self.syslog.get_state()
52
+
53
+ class SystemStateController(TaskController):
54
+
55
+ def __init__(self, remote, publisher, syslog):
56
+ super(SystemStateController, self).__init__()
57
+ self.remote = remote
58
+ self.syslog = syslog
59
+ self.publisher = publisher
60
+
61
+ def assign(self, handler, component, goal):
62
+ if handler == "system":
63
+ if component == "update":
64
+ return StatusUpdateTask, self.syslog, self.publisher, goal
65
+ elif component == "remote":
66
+ return StatusUpdateTask, self.syslog, self.publisher, goal, self.remote
67
+ elif component == "status":
68
+ return StatusRequestTask, self.syslog, goal
69
+ raise RuntimeError("No such component '%s', try 'remote', 'local', 'status'." % handler)
70
+ raise RuntimeError("No such handler '%s', try 'system'." % handler)
71
+
72
+
73
+ def main_cli():
74
+
75
+ parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
76
+ parser.add_argument("-r", "--remote", type=str,
77
+ help="The remote server address.", required=True)
78
+
79
+ endpoints = ZMQEndpoints(parser = parser)
80
+ args, unknown = parser.parse_known_args()
81
+
82
+ if not endpoints.has_remote("federation"):
83
+ raise RuntimeError("Federation remote not provided, found: %s. Check configuration file (--config) or provide runtime parameters (--task)." % list(endpoints.config["remotes"].keys()))
84
+
85
+ if not endpoints.has_publisher("federation"):
86
+ raise RuntimeError("Federation publisher not provided, found: %s. Check configuration file (--config) or provide runtime parameters (--publisher)." % list(endpoints.config["publishers"].keys()))
87
+
88
+ publisher_conf = endpoints.get_publisher_config("federation")
89
+ remote_conf = endpoints.get_remote_config("federation")
90
+
91
+ publisher = ObjectPublisher(publisher_conf["port"], publisher_conf["scope"])
92
+ syslog = CachedLocalLog()
93
+ server = TaskServer(remote_conf["port"])
94
+
95
+ server.register(remote_conf["scope"], SystemStateController(args.remote, publisher, syslog))
96
+ server.listen()
97
+
98
+ if __name__ == "__main__":
99
+
100
+ main_cli()
@@ -0,0 +1,55 @@
1
+ import yaml
2
+ import time
3
+ import os
4
+ import pprint
5
+ from copy import deepcopy
6
+ import uuid
7
+
8
+ class CachedLocalLog(object):
9
+
10
+ def __init__(self, directory = None, filename = "local-state.yaml", cachesize = 5):
11
+ if directory is None:
12
+ directory = os.path.join(os.environ.get('XDG_DATA_HOME', os.environ.get('HOME', '/home/demo') + '/.local/share'), "hospitalathome/cache")
13
+
14
+ self.requests = 0
15
+ self.cachesize = cachesize
16
+ os.makedirs(directory, exist_ok=True)
17
+ logname = os.path.join(directory, filename)
18
+ print("Opening logfile '%s'..." % logname)
19
+ self.logfile = open(logname, 'w')
20
+
21
+ self.logdata = {
22
+ self.requests: {
23
+ "timestamp" : 0,
24
+ "uuid": 0,
25
+ "data": {}
26
+ }
27
+ }
28
+
29
+ self.metadata = {
30
+ "filename": logname,
31
+ "initiated": time.time(),
32
+ "format": "v0.1"
33
+ }
34
+
35
+
36
+ def add_state(self, data, write_file = True):
37
+ if not type(data) is dict:
38
+ raise RuntimeError("Can only log dict type data")
39
+
40
+ oldkey = self.requests % self.cachesize
41
+ newkey = (self.requests + 1) % self.cachesize
42
+
43
+ origin = deepcopy(self.logdata[oldkey]["data"])
44
+ self.logdata[newkey] = {
45
+ "data": origin | data,
46
+ "timestamp": time.time(),
47
+ "uuid": str(uuid.uuid4())
48
+ }
49
+
50
+ yaml.dump({self.requests: self.logdata[newkey]}, self.logfile)
51
+ self.requests = self.requests + 1
52
+
53
+
54
+ def get_state(self):
55
+ return self.logdata
File without changes
@@ -0,0 +1,46 @@
1
+ import os
2
+ import json
3
+
4
+ from ztl.core.config import ZMQEndpoints
5
+ from ztl.core.protocol import Task
6
+
7
+ class SystemState(object):
8
+
9
+ def __init__(self):
10
+
11
+ endpoints = ZMQEndpoints()
12
+ if not endpoints.has_remote("federation"):
13
+ raise RuntimeError("Federation remote not provided, found: %s. Check configuration file (--config) or provide runtime parameters (--task)." % list(endpoints.config["remotes"].keys()))
14
+
15
+ if not endpoints.has_publisher("federation"):
16
+ raise RuntimeError("Federation publisher not provided, found: %s. Check configuration file (--config) or provide runtime parameters (--publisher)." % list(endpoints.config["publishers"].keys()))
17
+
18
+ self.remote = endpoints.get_remote("federation")
19
+ self.subscriber = endpoints.get_subscriber("federation")
20
+ self.listening = False
21
+
22
+ def update_state(self, data, cloud = False):
23
+ if type(data) is dict:
24
+ data = json.dumps(data)
25
+
26
+ if cloud:
27
+ component = "remote"
28
+ else:
29
+ component = "update"
30
+
31
+ mid, reply = self.remote.trigger(Task.encode("system", component, data))
32
+ state, reply = self.remote.wait(mid)
33
+
34
+ return reply
35
+
36
+ def get_state(self):
37
+ mid, reply = self.remote.trigger(Task.encode("system", "status", ""))
38
+ state, reply = self.remote.wait(mid)
39
+
40
+ return reply
41
+
42
+ def dispatch_updates(self, callback):
43
+ self.subscriber.register_callback(callback)
44
+ if not self.listening:
45
+ self.subscriber.start()
46
+ self.listening = True
File without changes
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import cherrypy
4
+
5
+ class JSONDataService(object):
6
+
7
+ @cherrypy.expose
8
+ @cherrypy.tools.json_out()
9
+ @cherrypy.tools.json_in()
10
+ def process(self):
11
+ request = cherrypy.request.json
12
+ print(request)
13
+ if not "handler" in request.keys():
14
+ raise cherrypy.HTTPError(406, "handler undefined")
15
+ if not "component" in request.keys():
16
+ raise cherrypy.HTTPError(406, "component undefined")
17
+ if not "goal" in request.keys():
18
+ raise cherrypy.HTTPError(406, "goal undefined")
19
+ else:
20
+ with cherrypy.HTTPError.handle(Exception):
21
+ return self.handle(request["handler"], request["component"], request["goal"])
22
+
23
+ def handle(self, handler, component, goal):
24
+ print("handler: %s, component: %s, goal: %s" % (handler, component, goal))
25
+ return "data printed successfully"
26
+
27
+ def main_cli():
28
+ config = {'server.socket_host': '0.0.0.0',
29
+ # 'server.ssl_module': 'builtin',
30
+ # 'server.ssl_certificate': '/tmp/some.cer',
31
+ # 'server.ssl_private_key': '/tmp/some.key'
32
+ }
33
+ cherrypy.config.update(config)
34
+ cherrypy.quickstart(JSONDataService())
35
+
36
+
37
+ if __name__ == "__main__":
38
+
39
+ main_cli()
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.4
2
+ Name: zfl
3
+ Version: 0.1.1
4
+ Summary: Data server via HTTPS POST
5
+ Author-email: Patrick Holthaus <patrick.holthaus@googlemail.com>
6
+ License-Expression: BSD-2-Clause
7
+ Project-URL: Homepage, https://gitlab.com/robothouse/rh-projects/hospital-at-home/data-server/
8
+ Project-URL: Bug Tracker, https://gitlab.com/robothouse/rh-projects/hospital-at-home/data-server/issues/
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: requests
15
+ Requires-Dist: cherrypy
16
+ Requires-Dist: ztl>=0.3.1
17
+ Dynamic: license-file
18
+
19
+ # Data Server
20
+
21
+ This will become the project to enable data transfer and logging
@@ -0,0 +1,22 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/zfl/__init__.py
5
+ src/zfl.egg-info/PKG-INFO
6
+ src/zfl.egg-info/SOURCES.txt
7
+ src/zfl.egg-info/dependency_links.txt
8
+ src/zfl.egg-info/entry_points.txt
9
+ src/zfl.egg-info/requires.txt
10
+ src/zfl.egg-info/top_level.txt
11
+ src/zfl/examples/local_logging_test.py
12
+ src/zfl/examples/main_logging_example.py
13
+ src/zfl/examples/simple_http_client.py
14
+ src/zfl/examples/state_subscriber.py
15
+ src/zfl/examples/ztl_http_client.py
16
+ src/zfl/local/__init__.py
17
+ src/zfl/local/federated_logging_service.py
18
+ src/zfl/local/syslog.py
19
+ src/zfl/logging/__init__.py
20
+ src/zfl/logging/state.py
21
+ src/zfl/server/__init__.py
22
+ src/zfl/server/federated_cloud_service.py
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ zfl_cloud_server = zfl.server.federated_cloud_service:main_cli
3
+ zfl_logging_service = zfl.local.federated_logging_service:main_cli
@@ -0,0 +1,3 @@
1
+ requests
2
+ cherrypy
3
+ ztl>=0.3.1
@@ -0,0 +1 @@
1
+ zfl