From 4bdd751d75fd8bb3a8d79c88ee525b551895d6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?BENEDEK=20L=C3=A1szl=C3=B3?= Date: Sat, 1 Jun 2024 02:50:53 +0200 Subject: [PATCH] python client --- .gitignore | 2 + CMakeLists.txt | 6 +++ src/python/CMakeLists.txt | 29 ++++++++++ src/python/__init__.py | 0 src/python/graph.py | 51 ++++++++++++++++++ src/python/setup.py | 13 +++++ test/client_example.py | 64 ++++++++++++++++++++++ test/test.py | 110 -------------------------------------- 8 files changed, 165 insertions(+), 110 deletions(-) create mode 100644 src/python/CMakeLists.txt create mode 100644 src/python/__init__.py create mode 100644 src/python/graph.py create mode 100644 src/python/setup.py create mode 100644 test/client_example.py delete mode 100755 test/test.py diff --git a/.gitignore b/.gitignore index d722044..1c5e38b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ build +dist +graph.egg-info .cache __pycache__ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index fcc5e07..18c3540 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ include_directories("${CMAKE_SOURCE_DIR}/inc") option(ENABLE_PLUGINS "Enable plugins" ON) option(ENABLE_SERVER "Enable server" ON) +option(ENABLE_PYTHON_CLIENT "Enable Python client" ON) # build plugins if(ENABLE_PLUGINS) @@ -27,4 +28,9 @@ endif() # build gui server if(ENABLE_SERVER) add_subdirectory("${CMAKE_SOURCE_DIR}/src/graph/server") +endif() + +# build python package +if(ENABLE_PYTHON_CLIENT) + add_subdirectory("${CMAKE_SOURCE_DIR}/src/python") endif() \ No newline at end of file diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt new file mode 100644 index 0000000..c0e3c5f --- /dev/null +++ b/src/python/CMakeLists.txt @@ -0,0 +1,29 @@ +if(NOT DEFINED PYTHON_EXE) + set(PYTHON_EXE "python") +endif() + +option(INSTALL_PYTHON_CLIENT "Install the Python client package") + +file(GLOB_RECURSE PYTHON_SOURCES "${CMAKE_SOURCE_DIR}/src/python/*.py") + +add_custom_command( + OUTPUT "${CMAKE_BINARY_DIR}/python_package.stamp" + COMMAND "${CMAKE_COMMAND}" -E env "${PYTHON_EXE}" ./setup.py sdist + COMMAND "${CMAKE_COMMAND}" -E touch "${CMAKE_BINARY_DIR}/python_package.stamp" + DEPENDS "${PYTHON_SOURCES}" + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/src/python" + COMMENT "Building Python client package" +) + +add_custom_target(build_python_package ALL + DEPENDS ${CMAKE_BINARY_DIR}/python_package.stamp +) + +if(INSTALL_PYTHON_CLIENT) + add_custom_target(install_python_package ALL + COMMAND "${CMAKE_COMMAND}" -E env "${PYTHON_EXE}" -m pip install "${CMAKE_SOURCE_DIR}/src/python/dist/graph*" + COMMENT "Insatlling Python client package" + ) + + add_dependencies(install_python_package build_python_package) +endif() \ No newline at end of file diff --git a/src/python/__init__.py b/src/python/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/python/graph.py b/src/python/graph.py new file mode 100644 index 0000000..4e1f6d1 --- /dev/null +++ b/src/python/graph.py @@ -0,0 +1,51 @@ +from requests import session +from numpy import array + +class Graph: + def __init__(self, series: str, chart: int = 0, address: str = "127.0.0.1", port: int = 8080): + self.base = f"http://{address}:{port}/add" + self.chart = chart + self.series = series + + def __enter__(self): + self.session = session() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.session.close() + + def list(self, points: list): + """ + Send a list of points to the graph. + + Parameters: + points: eg. [(0.1, 13.5), (0.2, 14.9)] + """ + self.session.post( + self.base, + json={ + "chart": self.chart, + "series": self.series, + "points": [ {"x": i[0], "y": i[1]} for i in points ] + } + ) + + def lists(self, x: list, y: list): + self.list(zip(x,y)) + + def point(self, point: tuple[float, float]): + self.session.get(f"{self.base}?chart={self.chart}&series={self.series}&x={point[0]}&y={point[1]}") + + def __iadd__(self, data): + if (isinstance(data, list)): + self.list(data) + return self + elif (isinstance(data, tuple)): + if (isinstance(data[0], list)): + self.lists(data[0], data[1]) + return self + elif (isinstance(data[0], float)): + self.point(data) + return self + + raise Exception("unknown data format") \ No newline at end of file diff --git a/src/python/setup.py b/src/python/setup.py new file mode 100644 index 0000000..ddba94b --- /dev/null +++ b/src/python/setup.py @@ -0,0 +1,13 @@ +from setuptools import setup + +setup( + name="graph", + version="0.0.1", + description="dowerx/graph client for Python", + packages=["."], + install_requires=["requests"], + author="BENEDEK László", + author_email="lacbenedek@gmail.com", + keywords=["graph", "http", "client"], + url="https://git.tek.govt.hu/dowerx/graph" +) \ No newline at end of file diff --git a/test/client_example.py b/test/client_example.py new file mode 100644 index 0000000..eb03f09 --- /dev/null +++ b/test/client_example.py @@ -0,0 +1,64 @@ +from graph import Graph +from math import sin, cos +from time import sleep +import numpy as np +from argparse import ArgumentParser + +def function(args, func): + x = 0.0 + while True: + with Graph(series=args.series, chart=args.chart, address=args.address, port=args.port) as g: + try: + x += 0.01 + g += (x, func(x)) + sleep(0.01) + except KeyboardInterrupt: + break + +def sine_array(args): + start = 0 + with Graph(series=args.series, chart=args.chart, address=args.address, port=args.port) as g: + while True: + try: + x = np.linspace(start, start+0.5, args.num) + y = np.sin(x) + + start += 0.5 + + g += (x.tolist(), y.tolist()) + + sleep(args.delay) + except KeyboardInterrupt: + break + +def star(args): + with Graph(series=args.series, chart=args.chart, address=args.address, port=args.port) as g: + points = [ + (2/4,2/2), + (3/4,0), + (0,1/2), + (4/4,1/2), + (1/4,0), + (2/4,2/2) + ] + g += points + +if __name__ == "__main__": + parser = ArgumentParser() + parser.add_argument("-t", action="store", dest="test", help="test type", type=str, choices=["sin", "cos", "array", "star"], required=False, default="sin") + parser.add_argument("-n", action="store", dest="num", help="sin_array size/iteration", type=int, required=False, default=100) + parser.add_argument("-d", action="store", dest="delay", help="delay between iteration", type=float, required=False, default=0.25) + parser.add_argument("-c", action="store", dest="chart", help="chart id", type=int, required=True) + parser.add_argument("-s", action="store", dest="series", help="series name", type=str, required=True) + parser.add_argument("-p", action="store", dest="port", help="server port", type=int, required=False, default=8080) + parser.add_argument("-a", action="store", dest="address", help="server address", type=str, required=False, default="127.0.0.1") + + args = parser.parse_args() + if args.test == "sin": + function(args, sin) + if args.test == "cos": + function(args, cos) + elif args.test == "array": + sine_array(args) + elif args.test == "star": + star(args) \ No newline at end of file diff --git a/test/test.py b/test/test.py deleted file mode 100755 index a16cc36..0000000 --- a/test/test.py +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/env python3 - -import requests -from math import sin, cos -from time import sleep -import numpy as np -from argparse import ArgumentParser -# import pyaudio - -def sine(args): - x = 0.0 - while True: - try: - requests.get(f"http://{args.address}:{args.port}/add?chart={args.chart}&series={args.series}&x={x}&y={sin(x)}") - x += 0.01 - sleep(0.01) - except KeyboardInterrupt: - break - -def cosine(args): - x = 0.0 - while True: - try: - requests.get(f"http://{args.address}:{args.port}/add?chart={args.chart}&series={args.series}&x={x}&y={cos(x)}") - x += 0.01 - sleep(0.01) - except KeyboardInterrupt: - break - -def sine_array(args): - start = 0 - while True: - try: - x = np.linspace(start, start+0.5, args.num) - y = np.sin(x) - - start += 0.5 - - data = { - "chart": args.chart, - "series": args.series, - "points": [ {"x": i[0], "y":i[1]} for i in zip(x,y) ] - } - - r = requests.post(f"http://{args.address}:{args.port}/add", json=data) - sleep(args.delay) - except KeyboardInterrupt: - break - -def star(args): - points = [ - (2/4,2/2), - (3/4,0), - (0,1/2), - (4/4,1/2), - (1/4,0), - (2/4,2/2) - ] - for point in points: - requests.get(f"http://{args.address}:{args.port}/add?chart={args.chart}&series={args.series}&x={point[0]}&y={point[1]}") - -# def audio(): -# p = pyaudio.PyAudio() -# stream = p.open( -# input=True, -# input_device_index=0, -# format=pyaudio.paInt16, -# channels=1, -# rate=44100, -# frames_per_buffer=1024 -# ) -# stream.start_stream() -# while True: -# try: -# data = stream.read(1024, False) -# requests.post( -# "http://0.0.0.0:8080/add", -# json={ -# "chart": 0, -# "series": "audio", -# "points": [ {"x": i, "y": v} for (i, v) in enumerate(data) ] -# }, -# headers={"Connection":"close"}) -# except KeyboardInterrupt: -# break - -# stream.stop_stream() -# stream.close() -# p.terminate() - -if __name__ == "__main__": - parser = ArgumentParser() - parser.add_argument("-t", action="store", dest="test", help="test type", type=str, choices=["sine", "cosine", "sine_array", "star"], required=False, default="sine") - parser.add_argument("-n", action="store", dest="num", help="sin_array size/iteration", type=int, required=False, default=100) - parser.add_argument("-d", action="store", dest="delay", help="delay between iteration", type=float, required=False, default=0.25) - parser.add_argument("-c", action="store", dest="chart", help="chart id", type=int, required=True) - parser.add_argument("-s", action="store", dest="series", help="series name", type=str, required=True) - parser.add_argument("-p", action="store", dest="port", help="server port", type=int, required=False, default=8080) - parser.add_argument("-a", action="store", dest="address", help="server address", type=str, required=False, default="127.0.0.1") - - args = parser.parse_args() - if args.test == "sine": - sine(args) - if args.test == "cosine": - cosine(args) - elif args.test == "sine_array": - sine_array(args) - elif args.test == "star": - star(args) -