From b667c1616c9a5acfcbd2048535c8b619905e1f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sat, 30 Mar 2024 15:55:24 +0100 Subject: [PATCH 01/19] initial commit --- server/Dockerfile | 15 +++++++++++++++ server/app/main.py | 4 ++++ server/nginx-body-buffer-size.conf | 6 ++++++ 3 files changed, 25 insertions(+) create mode 100644 server/Dockerfile create mode 100644 server/app/main.py create mode 100644 server/nginx-body-buffer-size.conf diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 00000000..7a31ff21 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,15 @@ +FROM tiangolo/uwsgi-nginx:python3.11 + +# Should match client_body_buffer_size defined in nginx-body-buffer-size.conf +ENV NGINX_MAX_UPLOAD 1M + +# object stores aren't thread-safe yet +# https://github.com/eclipse-basyx/basyx-python-sdk/issues/205 +ENV UWSGI_CHEAPER 0 +ENV UWSGI_PROCESSES 1 + +RUN pip install --no-cache-dir git+https://github.com/rwth-iat/basyx-python-sdk@feature/http_api + +COPY ./app /app + +COPY ./nginx-body-buffer-size.conf /etc/nginx/conf.d/body-buffer-size.conf diff --git a/server/app/main.py b/server/app/main.py new file mode 100644 index 00000000..cf5714a5 --- /dev/null +++ b/server/app/main.py @@ -0,0 +1,4 @@ +from basyx.aas.backend.local_file import LocalFileObjectStore +from basyx.aas.adapter.http import WSGIApp + +application = WSGIApp(LocalFileObjectStore("/storage")) diff --git a/server/nginx-body-buffer-size.conf b/server/nginx-body-buffer-size.conf new file mode 100644 index 00000000..423e8cd6 --- /dev/null +++ b/server/nginx-body-buffer-size.conf @@ -0,0 +1,6 @@ +# While the Dockerfile sets client_max_body_size, it doesn't +# allow us to define client_body_buffer_size, which is 16k +# by default. This results in temporary buffer files being +# created, in case the request body exceeds this limit. Thus, +# we override it here to match client_max_body_size. +client_body_buffer_size 1M; From 65b07969efbdfbbd7de790b431a3b3a6b4e1b267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sat, 30 Mar 2024 17:42:58 +0100 Subject: [PATCH 02/19] add readme --- server/README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 server/README.md diff --git a/server/README.md b/server/README.md new file mode 100644 index 00000000..afbdcad3 --- /dev/null +++ b/server/README.md @@ -0,0 +1,36 @@ +# Eclipse BaSyx Python SDK - HTTP Server + +This repository contains a Dockerfile to spin up an exemplary DotAAS HTTP/REST server with ease. +The server currently implements the following interfaces: + +- [Asset Administration Shell Repository Service][4] +- [Submodel Repository Service][5] + +It uses the [HTTP Adapter][1] and the [Local-File Backend][2] of the [BaSyx Python SDK][3], which allows server-side data to be stored persistently across container restarts. + + +## Building +The container image can be built via: +``` +$ docker build -t basyx-python-sdk-http-server . +``` + +## Running +Because the server uses the [Local-File Backend][2], the container needs to be provided with the directory `/storage` to store AAS and Submodel JSON files. +This directory can be mapped via the `-v` option from another image or a local directory. +To map the directory `storage` inside the container, `-v ./storage:/storage` can be used. +The directory `storage` will be created in the current working directory, if it doesn't already exist. + +The HTTP server inside the container listens on port 80 by default. +To expose it on the host on port 8080, use the option `-p 8080:80` when running it. + +Putting it all together, the container can be started via the following command: +``` +$ docker run -p 8080:80 -v ./storage:/storage basyx-python-sdk-http-server +``` + +[1]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/238 +[2]: https://basyx-python-sdk.readthedocs.io/en/latest/backend/local_file.html +[3]: https://github.com/eclipse-basyx/basyx-python-sdk +[4]: https://app.swaggerhub.com/apis/Plattform_i40/AssetAdministrationShellRepositoryServiceSpecification/V3.0.1_SSP-001 +[5]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRepositoryServiceSpecification/V3.0.1_SSP-001 From 38303efa1d0042a0ab337064d279582f2c96c01a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sat, 30 Mar 2024 17:51:13 +0100 Subject: [PATCH 03/19] move nginx config to subdirectory --- server/Dockerfile | 3 +-- .../body-buffer-size.conf} | 0 2 files changed, 1 insertion(+), 2 deletions(-) rename server/{nginx-body-buffer-size.conf => nginx/body-buffer-size.conf} (100%) diff --git a/server/Dockerfile b/server/Dockerfile index 7a31ff21..ac4c1cb0 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -11,5 +11,4 @@ ENV UWSGI_PROCESSES 1 RUN pip install --no-cache-dir git+https://github.com/rwth-iat/basyx-python-sdk@feature/http_api COPY ./app /app - -COPY ./nginx-body-buffer-size.conf /etc/nginx/conf.d/body-buffer-size.conf +COPY ./nginx /etc/nginx/conf.d diff --git a/server/nginx-body-buffer-size.conf b/server/nginx/body-buffer-size.conf similarity index 100% rename from server/nginx-body-buffer-size.conf rename to server/nginx/body-buffer-size.conf From 73bf9e8df336baf1d9352947ae1dea6a4696e121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sat, 30 Mar 2024 17:51:45 +0100 Subject: [PATCH 04/19] `docker build` is deprecated --- server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/README.md b/server/README.md index afbdcad3..e387bc49 100644 --- a/server/README.md +++ b/server/README.md @@ -12,7 +12,7 @@ It uses the [HTTP Adapter][1] and the [Local-File Backend][2] of the [BaSyx Pyth ## Building The container image can be built via: ``` -$ docker build -t basyx-python-sdk-http-server . +$ docker buildx build -t basyx-python-sdk-http-server . ``` ## Running From 46545f693f8752fa3ed591eed04e0239427b84c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sat, 30 Mar 2024 17:57:17 +0100 Subject: [PATCH 05/19] Dockerfile: update a comment The nginx config has been moved in 38303efa1d0042a0ab337064d279582f2c96c01a. --- server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Dockerfile b/server/Dockerfile index ac4c1cb0..e6e65e21 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,6 +1,6 @@ FROM tiangolo/uwsgi-nginx:python3.11 -# Should match client_body_buffer_size defined in nginx-body-buffer-size.conf +# Should match client_body_buffer_size defined in nginx/body-buffer-size.conf ENV NGINX_MAX_UPLOAD 1M # object stores aren't thread-safe yet From e9ee8b06f24e01942b07b41d414a5211bb981762 Mon Sep 17 00:00:00 2001 From: s-heppner Date: Mon, 1 Apr 2024 10:39:39 +0200 Subject: [PATCH 06/19] README.md: Add link to API specification --- server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/README.md b/server/README.md index e387bc49..78b9aba6 100644 --- a/server/README.md +++ b/server/README.md @@ -1,6 +1,6 @@ # Eclipse BaSyx Python SDK - HTTP Server -This repository contains a Dockerfile to spin up an exemplary DotAAS HTTP/REST server with ease. +This repository contains a Dockerfile to spin up an exemplary HTTP/REST server following the [Specification of the AAS Part 2 API](https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01002-3-0_application_programming_interfaces) with ease. The server currently implements the following interfaces: - [Asset Administration Shell Repository Service][4] From 9f4b9ca77ebd5a6993d1e04d1bd62bcf66929de6 Mon Sep 17 00:00:00 2001 From: jkhsjdhjs Date: Thu, 13 Jun 2024 16:08:52 +0200 Subject: [PATCH 07/19] readme: add note about windows paths --- server/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/README.md b/server/README.md index 78b9aba6..fc01651d 100644 --- a/server/README.md +++ b/server/README.md @@ -29,6 +29,11 @@ Putting it all together, the container can be started via the following command: $ docker run -p 8080:80 -v ./storage:/storage basyx-python-sdk-http-server ``` +Since Windows uses backslashes instead of forward slashes in paths, you'll have to adjust the path to the storage directory there: +``` +> docker run -p 8080:80 -v .\storage:/storage basyx-python-sdk-http-server +``` + [1]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/238 [2]: https://basyx-python-sdk.readthedocs.io/en/latest/backend/local_file.html [3]: https://github.com/eclipse-basyx/basyx-python-sdk From 7fe62b44d19e4ee6ad16872fbc0166a49df3169c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Fri, 14 Jun 2024 01:17:29 +0200 Subject: [PATCH 08/19] readme: move a link to the bottom --- server/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/README.md b/server/README.md index fc01651d..507c9d3b 100644 --- a/server/README.md +++ b/server/README.md @@ -1,6 +1,6 @@ # Eclipse BaSyx Python SDK - HTTP Server -This repository contains a Dockerfile to spin up an exemplary HTTP/REST server following the [Specification of the AAS Part 2 API](https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01002-3-0_application_programming_interfaces) with ease. +This repository contains a Dockerfile to spin up an exemplary HTTP/REST server following the [Specification of the AAS Part 2 API][6] with ease. The server currently implements the following interfaces: - [Asset Administration Shell Repository Service][4] @@ -39,3 +39,4 @@ Since Windows uses backslashes instead of forward slashes in paths, you'll have [3]: https://github.com/eclipse-basyx/basyx-python-sdk [4]: https://app.swaggerhub.com/apis/Plattform_i40/AssetAdministrationShellRepositoryServiceSpecification/V3.0.1_SSP-001 [5]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRepositoryServiceSpecification/V3.0.1_SSP-001 +[6]: https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01002-3-0_application_programming_interfaces From dd923301f71e3cc84844e2dd0a035091c7785c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sun, 16 Jun 2024 01:12:33 +0200 Subject: [PATCH 09/19] nginx: add CORS header --- server/nginx/cors-header.conf | 1 + 1 file changed, 1 insertion(+) create mode 100644 server/nginx/cors-header.conf diff --git a/server/nginx/cors-header.conf b/server/nginx/cors-header.conf new file mode 100644 index 00000000..af78db3a --- /dev/null +++ b/server/nginx/cors-header.conf @@ -0,0 +1 @@ +add_header Access-Control-Allow-Origin *; From 1f62df939def790dd652ee14134caa16abcfc18e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leon=20M=C3=B6ller?= Date: Sun, 16 Jun 2024 01:17:40 +0200 Subject: [PATCH 10/19] app: support AASX files This makes the server read AASX files by default, as it seems to be the most common usecase. Furthermore, the user can now configure the container via environment variables. These options are also documented in the README. --- server/README.md | 14 +++++++++++++- server/app/main.py | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/server/README.md b/server/README.md index 507c9d3b..090f7631 100644 --- a/server/README.md +++ b/server/README.md @@ -6,8 +6,11 @@ The server currently implements the following interfaces: - [Asset Administration Shell Repository Service][4] - [Submodel Repository Service][5] -It uses the [HTTP Adapter][1] and the [Local-File Backend][2] of the [BaSyx Python SDK][3], which allows server-side data to be stored persistently across container restarts. +It uses the [HTTP API][1] and the [AASX Adapter][7] of the [BaSyx Python SDK][3], to serve AASX files from a given directory. +The AASX files are only read, chages won't persist. +Alternatively, the container can also be told to use the [Local-File Backend][2] instead, which stores AAS and Submodels as individual JSON files and allows for persistent changes (except supplementary files, i.e. files referenced by `File` submodel elements). +See [below](#options) on how to configure this. ## Building The container image can be built via: @@ -34,9 +37,18 @@ Since Windows uses backslashes instead of forward slashes in paths, you'll have > docker run -p 8080:80 -v .\storage:/storage basyx-python-sdk-http-server ``` +## Options +The container can be configured via environment variables: +- `API_BASE_PATH` determines the base path under which all other API paths are made available. Default: `/api/v3.0` +- `STORAGE_TYPE` can be one of `AASX` or `LOCAL_FILE`: + - When set to `AASX` (the default), the server will read and serve AASX files from the storage directory. The AASX files are not modified, all changes done via the API are only stored in memory. + - When instead set to `LOCAL_FILE`, the server makes use of the [LocalFileBackend][2], where AAS and Submodels are persistently stored as JSON files. Supplementary files, i.e. files referenced by `File` submodel elements, are not stored in this case. +- `STORAGE_PATH` sets the directory to read the files from *within the container*. If you bind your files to a directory different from the default `/storage`, you can use this variable to adjust the server accordingly. + [1]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/238 [2]: https://basyx-python-sdk.readthedocs.io/en/latest/backend/local_file.html [3]: https://github.com/eclipse-basyx/basyx-python-sdk [4]: https://app.swaggerhub.com/apis/Plattform_i40/AssetAdministrationShellRepositoryServiceSpecification/V3.0.1_SSP-001 [5]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRepositoryServiceSpecification/V3.0.1_SSP-001 [6]: https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01002-3-0_application_programming_interfaces +[7]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/aasx.html#adapter-aasx diff --git a/server/app/main.py b/server/app/main.py index cf5714a5..9e9488ed 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -1,4 +1,37 @@ +import os +import pathlib +import sys + +from basyx.aas import model +from basyx.aas.adapter import aasx + from basyx.aas.backend.local_file import LocalFileObjectStore from basyx.aas.adapter.http import WSGIApp -application = WSGIApp(LocalFileObjectStore("/storage")) +storage_path = os.getenv("STORAGE_PATH", "/storage") +storage_type = os.getenv("STORAGE_TYPE", "AASX") +base_path = os.getenv("API_BASE_PATH") + +wsgi_optparams = {} + +if base_path is not None: + wsgi_optparams["base_path"] = base_path + +if storage_type == "LOCAL_FILE": + application = WSGIApp(LocalFileObjectStore(storage_path), aasx.DictSupplementaryFileContainer(), **wsgi_optparams) + +elif storage_type == "AASX": + object_store: model.DictObjectStore = model.DictObjectStore() + file_store: aasx.DictSupplementaryFileContainer = aasx.DictSupplementaryFileContainer() + + for file in pathlib.Path(storage_path).glob("*.aasx"): + if not file.is_file(): + continue + print(f"Loading {file}") + with aasx.AASXReader(file) as reader: + reader.read_into(object_store=object_store, file_store=file_store) + + application = WSGIApp(object_store, file_store, **wsgi_optparams) + +else: + print(f"STORAGE_TYPE must be either LOCAL_FILE or AASX! Current value: {storage_type}", file=sys.stderr) From 31f9ea35dbbdeb2d73e942faed4ec28c8f30d910 Mon Sep 17 00:00:00 2001 From: Igor Garmaev <56840636+zrgt@users.noreply.github.com> Date: Wed, 21 Aug 2024 22:18:15 +0200 Subject: [PATCH 11/19] app: support JSON and XML files This makes the server also read JSON and XML files --- server/app/main.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/server/app/main.py b/server/app/main.py index 9e9488ed..302bc5fc 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -2,13 +2,13 @@ import pathlib import sys -from basyx.aas import model +from basyx.aas import model, adapter from basyx.aas.adapter import aasx from basyx.aas.backend.local_file import LocalFileObjectStore from basyx.aas.adapter.http import WSGIApp -storage_path = os.getenv("STORAGE_PATH", "/storage") +storage_path = os.getenv("STORAGE_PATH", "storage") storage_type = os.getenv("STORAGE_TYPE", "AASX") base_path = os.getenv("API_BASE_PATH") @@ -20,18 +20,27 @@ if storage_type == "LOCAL_FILE": application = WSGIApp(LocalFileObjectStore(storage_path), aasx.DictSupplementaryFileContainer(), **wsgi_optparams) -elif storage_type == "AASX": +elif storage_type in ("AASX", "JSON", "XML"): object_store: model.DictObjectStore = model.DictObjectStore() file_store: aasx.DictSupplementaryFileContainer = aasx.DictSupplementaryFileContainer() - for file in pathlib.Path(storage_path).glob("*.aasx"): + suffix_pattern = f"*.{storage_type.lower()}" + for file in pathlib.Path(storage_path).glob(suffix_pattern): if not file.is_file(): continue print(f"Loading {file}") - with aasx.AASXReader(file) as reader: - reader.read_into(object_store=object_store, file_store=file_store) + + if storage_type == "JSON": + with open(file) as f: + adapter.json.read_aas_json_file_into(object_store, f) + elif storage_type == "XML": + with open(file) as f: + adapter.xml.read_aas_xml_file_into(object_store, file) + else: + with aasx.AASXReader(file) as reader: + reader.read_into(object_store=object_store, file_store=file_store) application = WSGIApp(object_store, file_store, **wsgi_optparams) else: - print(f"STORAGE_TYPE must be either LOCAL_FILE or AASX! Current value: {storage_type}", file=sys.stderr) + print(f"STORAGE_TYPE must be either LOCAL_FILE, AASX, JSON or XML! Current value: {storage_type}", file=sys.stderr) From b32c97cd4b0187958c7a55b788b21a2f601524c2 Mon Sep 17 00:00:00 2001 From: zrgt Date: Wed, 21 Aug 2024 23:34:19 +0200 Subject: [PATCH 12/19] Revert "app: support JSON and XML files " This reverts commit 31f9ea35dbbdeb2d73e942faed4ec28c8f30d910. --- server/app/main.py | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/server/app/main.py b/server/app/main.py index 302bc5fc..9e9488ed 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -2,13 +2,13 @@ import pathlib import sys -from basyx.aas import model, adapter +from basyx.aas import model from basyx.aas.adapter import aasx from basyx.aas.backend.local_file import LocalFileObjectStore from basyx.aas.adapter.http import WSGIApp -storage_path = os.getenv("STORAGE_PATH", "storage") +storage_path = os.getenv("STORAGE_PATH", "/storage") storage_type = os.getenv("STORAGE_TYPE", "AASX") base_path = os.getenv("API_BASE_PATH") @@ -20,27 +20,18 @@ if storage_type == "LOCAL_FILE": application = WSGIApp(LocalFileObjectStore(storage_path), aasx.DictSupplementaryFileContainer(), **wsgi_optparams) -elif storage_type in ("AASX", "JSON", "XML"): +elif storage_type == "AASX": object_store: model.DictObjectStore = model.DictObjectStore() file_store: aasx.DictSupplementaryFileContainer = aasx.DictSupplementaryFileContainer() - suffix_pattern = f"*.{storage_type.lower()}" - for file in pathlib.Path(storage_path).glob(suffix_pattern): + for file in pathlib.Path(storage_path).glob("*.aasx"): if not file.is_file(): continue print(f"Loading {file}") - - if storage_type == "JSON": - with open(file) as f: - adapter.json.read_aas_json_file_into(object_store, f) - elif storage_type == "XML": - with open(file) as f: - adapter.xml.read_aas_xml_file_into(object_store, file) - else: - with aasx.AASXReader(file) as reader: - reader.read_into(object_store=object_store, file_store=file_store) + with aasx.AASXReader(file) as reader: + reader.read_into(object_store=object_store, file_store=file_store) application = WSGIApp(object_store, file_store, **wsgi_optparams) else: - print(f"STORAGE_TYPE must be either LOCAL_FILE, AASX, JSON or XML! Current value: {storage_type}", file=sys.stderr) + print(f"STORAGE_TYPE must be either LOCAL_FILE or AASX! Current value: {storage_type}", file=sys.stderr) From 73569bfb4b22b099310d7909f8718582418f57e6 Mon Sep 17 00:00:00 2001 From: zrgt Date: Wed, 21 Aug 2024 23:39:45 +0200 Subject: [PATCH 13/19] app: Add support of JSON and XML files - This commit adds support for loading AASX, JSON or XML files. So the user can have a mix of these files in the storage folder and the application will load them all. - Changed types of storage to LOCAL_FILE_BACKEND and LOCAL_FILE_READ_ONLY - Adapted and refactored README.md and added more examples for running --- server/README.md | 37 +++++++++++++++++++++++++++---------- server/app/main.py | 27 ++++++++++++++++++--------- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/server/README.md b/server/README.md index 090f7631..853f45ef 100644 --- a/server/README.md +++ b/server/README.md @@ -6,8 +6,8 @@ The server currently implements the following interfaces: - [Asset Administration Shell Repository Service][4] - [Submodel Repository Service][5] -It uses the [HTTP API][1] and the [AASX Adapter][7] of the [BaSyx Python SDK][3], to serve AASX files from a given directory. -The AASX files are only read, chages won't persist. +It uses the [HTTP API][1] and the [AASX][7], [JSON][8], and [XML][9] Adapters of the [BaSyx Python SDK][3], to serve regarding files from a given directory. +The files are only read, chages won't persist. Alternatively, the container can also be told to use the [Local-File Backend][2] instead, which stores AAS and Submodels as individual JSON files and allows for persistent changes (except supplementary files, i.e. files referenced by `File` submodel elements). See [below](#options) on how to configure this. @@ -19,14 +19,31 @@ $ docker buildx build -t basyx-python-sdk-http-server . ``` ## Running -Because the server uses the [Local-File Backend][2], the container needs to be provided with the directory `/storage` to store AAS and Submodel JSON files. + +### Storage +The container needs to be provided with the directory `/storage` to store AAS and Submodel files: AASX, JSON, XML or JSON files of Local-File Backend. + This directory can be mapped via the `-v` option from another image or a local directory. To map the directory `storage` inside the container, `-v ./storage:/storage` can be used. The directory `storage` will be created in the current working directory, if it doesn't already exist. +### Port The HTTP server inside the container listens on port 80 by default. To expose it on the host on port 8080, use the option `-p 8080:80` when running it. +### Options +The container can be configured via environment variables: +- `API_BASE_PATH` determines the base path under which all other API paths are made available. + Default: `/api/v3.0` +- `STORAGE_TYPE` can be one of `LOCAL_FILE_READ_ONLY` or `LOCAL_FILE_BACKEND`: + - When set to `LOCAL_FILE_READ_ONLY` (the default), the server will read and serve AASX, JSON, XML files from the storage directory. + The files are not modified, all changes done via the API are only stored in memory. + - When instead set to `LOCAL_FILE`, the server makes use of the [LocalFileBackend][2], where AAS and Submodels are persistently stored as JSON files. + Supplementary files, i.e. files referenced by `File` submodel elements, are not stored in this case. +- `STORAGE_PATH` sets the directory to read the files from *within the container*. If you bind your files to a directory different from the default `/storage`, you can use this variable to adjust the server accordingly. + +### Running Examples + Putting it all together, the container can be started via the following command: ``` $ docker run -p 8080:80 -v ./storage:/storage basyx-python-sdk-http-server @@ -37,13 +54,11 @@ Since Windows uses backslashes instead of forward slashes in paths, you'll have > docker run -p 8080:80 -v .\storage:/storage basyx-python-sdk-http-server ``` -## Options -The container can be configured via environment variables: -- `API_BASE_PATH` determines the base path under which all other API paths are made available. Default: `/api/v3.0` -- `STORAGE_TYPE` can be one of `AASX` or `LOCAL_FILE`: - - When set to `AASX` (the default), the server will read and serve AASX files from the storage directory. The AASX files are not modified, all changes done via the API are only stored in memory. - - When instead set to `LOCAL_FILE`, the server makes use of the [LocalFileBackend][2], where AAS and Submodels are persistently stored as JSON files. Supplementary files, i.e. files referenced by `File` submodel elements, are not stored in this case. -- `STORAGE_PATH` sets the directory to read the files from *within the container*. If you bind your files to a directory different from the default `/storage`, you can use this variable to adjust the server accordingly. +Per default, the server will use the `LOCAL_FILE_READ_ONLY` storage type and serve the API under `/api/v3.0` and read files from `/storage`. If you want to change this, you can do so like this: +``` +$ docker run -p 8080:80 -v ./storage2:/storage2 -e API_BASE_PATH=/api/v3.1 -e STORAGE_TYPE=LOCAL_FILE_BACKEND -e STORAGE_PATH=/storage2 basyx-python-sdk-http-server +``` + [1]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/238 [2]: https://basyx-python-sdk.readthedocs.io/en/latest/backend/local_file.html @@ -52,3 +67,5 @@ The container can be configured via environment variables: [5]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRepositoryServiceSpecification/V3.0.1_SSP-001 [6]: https://industrialdigitaltwin.org/content-hub/aasspecifications/idta_01002-3-0_application_programming_interfaces [7]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/aasx.html#adapter-aasx +[8]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/json.html +[9]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/xml.html diff --git a/server/app/main.py b/server/app/main.py index 9e9488ed..715fa631 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -2,14 +2,14 @@ import pathlib import sys -from basyx.aas import model +from basyx.aas import model, adapter from basyx.aas.adapter import aasx from basyx.aas.backend.local_file import LocalFileObjectStore from basyx.aas.adapter.http import WSGIApp -storage_path = os.getenv("STORAGE_PATH", "/storage") -storage_type = os.getenv("STORAGE_TYPE", "AASX") +storage_path = os.getenv("STORAGE_PATH", "storage") +storage_type = os.getenv("STORAGE_TYPE", "LOCAL_FILE_READ_ONLY") base_path = os.getenv("API_BASE_PATH") wsgi_optparams = {} @@ -17,21 +17,30 @@ if base_path is not None: wsgi_optparams["base_path"] = base_path -if storage_type == "LOCAL_FILE": +if storage_type == "LOCAL_FILE_BACKEND": application = WSGIApp(LocalFileObjectStore(storage_path), aasx.DictSupplementaryFileContainer(), **wsgi_optparams) -elif storage_type == "AASX": +elif storage_type in "LOCAL_FILE_READ_ONLY": object_store: model.DictObjectStore = model.DictObjectStore() file_store: aasx.DictSupplementaryFileContainer = aasx.DictSupplementaryFileContainer() - for file in pathlib.Path(storage_path).glob("*.aasx"): + for file in pathlib.Path(storage_path).iterdir(): if not file.is_file(): continue print(f"Loading {file}") - with aasx.AASXReader(file) as reader: - reader.read_into(object_store=object_store, file_store=file_store) + + if file.suffix == ".json": + with open(file) as f: + adapter.json.read_aas_json_file_into(object_store, f) + elif file.suffix == ".xml": + with open(file) as f: + adapter.xml.read_aas_xml_file_into(object_store, file) + elif file.suffix == ".aasx": + with aasx.AASXReader(file) as reader: + reader.read_into(object_store=object_store, file_store=file_store) application = WSGIApp(object_store, file_store, **wsgi_optparams) else: - print(f"STORAGE_TYPE must be either LOCAL_FILE or AASX! Current value: {storage_type}", file=sys.stderr) + print(f"STORAGE_TYPE must be either LOCAL_FILE or LOCAL_FILE_READ_ONLY! Current value: {storage_type}", + file=sys.stderr) From f8c81ba4824f2d3704a66f8ed773a8c87510d747 Mon Sep 17 00:00:00 2001 From: zrgt Date: Mon, 2 Sep 2024 16:36:13 +0200 Subject: [PATCH 14/19] app: lower the suffixes Lower the suffixes to also match the filenames with uppercase suffixes --- server/app/main.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/app/main.py b/server/app/main.py index 715fa631..c8d60aff 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -29,13 +29,13 @@ continue print(f"Loading {file}") - if file.suffix == ".json": + if file.suffix.lower() == ".json": with open(file) as f: adapter.json.read_aas_json_file_into(object_store, f) - elif file.suffix == ".xml": + elif file.suffix.lower() == ".xml": with open(file) as f: adapter.xml.read_aas_xml_file_into(object_store, file) - elif file.suffix == ".aasx": + elif file.suffix.lower() == ".aasx": with aasx.AASXReader(file) as reader: reader.read_into(object_store=object_store, file_store=file_store) @@ -44,3 +44,6 @@ else: print(f"STORAGE_TYPE must be either LOCAL_FILE or LOCAL_FILE_READ_ONLY! Current value: {storage_type}", file=sys.stderr) + +# Lower the suffix +# Lower the suffixes to also match the filenames with uppercase suffixes \ No newline at end of file From 6ad831fece14541d448c47836ca6e6b71eb958ff Mon Sep 17 00:00:00 2001 From: zrgt Date: Tue, 3 Sep 2024 18:17:31 +0200 Subject: [PATCH 15/19] Remove comments --- server/app/main.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/app/main.py b/server/app/main.py index c8d60aff..6662c868 100644 --- a/server/app/main.py +++ b/server/app/main.py @@ -44,6 +44,3 @@ else: print(f"STORAGE_TYPE must be either LOCAL_FILE or LOCAL_FILE_READ_ONLY! Current value: {storage_type}", file=sys.stderr) - -# Lower the suffix -# Lower the suffixes to also match the filenames with uppercase suffixes \ No newline at end of file From 91d51d7a08e6e655f7890e8eaeb789f8226a6fae Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Mon, 16 Sep 2024 17:39:52 +0200 Subject: [PATCH 16/19] Add server to basyx-python-sdk --- {server => basyx/server}/Dockerfile | 0 {server => basyx/server}/README.md | 0 {server => basyx/server}/app/main.py | 0 {server => basyx/server}/nginx/body-buffer-size.conf | 0 {server => basyx/server}/nginx/cors-header.conf | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {server => basyx/server}/Dockerfile (100%) rename {server => basyx/server}/README.md (100%) rename {server => basyx/server}/app/main.py (100%) rename {server => basyx/server}/nginx/body-buffer-size.conf (100%) rename {server => basyx/server}/nginx/cors-header.conf (100%) diff --git a/server/Dockerfile b/basyx/server/Dockerfile similarity index 100% rename from server/Dockerfile rename to basyx/server/Dockerfile diff --git a/server/README.md b/basyx/server/README.md similarity index 100% rename from server/README.md rename to basyx/server/README.md diff --git a/server/app/main.py b/basyx/server/app/main.py similarity index 100% rename from server/app/main.py rename to basyx/server/app/main.py diff --git a/server/nginx/body-buffer-size.conf b/basyx/server/nginx/body-buffer-size.conf similarity index 100% rename from server/nginx/body-buffer-size.conf rename to basyx/server/nginx/body-buffer-size.conf diff --git a/server/nginx/cors-header.conf b/basyx/server/nginx/cors-header.conf similarity index 100% rename from server/nginx/cors-header.conf rename to basyx/server/nginx/cors-header.conf From 7225c8823b7947a17b9ce79542e231cb02e57640 Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Tue, 17 Sep 2024 18:44:34 +0200 Subject: [PATCH 17/19] move pyproject.toml --- basyx/pyproject.toml | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 basyx/pyproject.toml diff --git a/basyx/pyproject.toml b/basyx/pyproject.toml new file mode 100644 index 00000000..64aaff4e --- /dev/null +++ b/basyx/pyproject.toml @@ -0,0 +1,51 @@ +[build-system] +requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"] +build-backend = "setuptools.build_meta" + +[project] +name = "basyx-python-sdk" +version = "1.0.0" +description = "The Eclipse BaSyx Python SDK, an implementation of the Asset Administration Shell for Industry 4.0 systems" +authors = [ + { name = "The Eclipse BaSyx Authors", email = "admins@iat.rwth-aachen.de" } +] +readme = "README.md" +license = { file = "LICENSE" } +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Development Status :: 5 - Production/Stable" +] +requires-python = ">=3.8" +dependencies = [ + "python-dateutil>=2.8,<3", + "lxml>=4.2,<5", + "urllib3>=1.26,<2.0", + "pyecma376-2>=0.2.4" +] + +[project.optional-dependencies] +dev = [ + "mypy", + "pycodestyle", + "codeblocks", + "coverage", +] + +[project.urls] +"Homepage" = "https://github.com/eclipse-basyx/basyx-python-sdk" + +[tool.setuptools] +packages = ["basyx"] + +[tool.setuptools.package-data] +basyx = ["py.typed"] +"basyx.aas.examples.data" = ["TestFile.pdf"] + +[tool.setuptools.exclude-package-data] +"*" = ["test", "test.*"] + +[tool.mypy] +files = ["basyx/aas/src"] + From f6fdc2725696ab50a6a3f7f517ac8306c89fc34c Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Sat, 28 Sep 2024 13:28:12 +0200 Subject: [PATCH 18/19] one pyproject.toml for every project --- pyproject.toml => basyx/pyproject-aas.toml | 3 +++ .../{pyproject.toml => pyproject-server.toml} | 26 ++++++------------- 2 files changed, 11 insertions(+), 18 deletions(-) rename pyproject.toml => basyx/pyproject-aas.toml (97%) rename basyx/{pyproject.toml => pyproject-server.toml} (50%) diff --git a/pyproject.toml b/basyx/pyproject-aas.toml similarity index 97% rename from pyproject.toml rename to basyx/pyproject-aas.toml index b8e44a8f..3949a87f 100644 --- a/pyproject.toml +++ b/basyx/pyproject-aas.toml @@ -46,3 +46,6 @@ basyx = ["py.typed"] [tool.setuptools.exclude-package-data] "*" = ["test", "test.*"] +[tool.mypy] +files = ["basyx/aas"] + diff --git a/basyx/pyproject.toml b/basyx/pyproject-server.toml similarity index 50% rename from basyx/pyproject.toml rename to basyx/pyproject-server.toml index 64aaff4e..863731d0 100644 --- a/basyx/pyproject.toml +++ b/basyx/pyproject-server.toml @@ -1,11 +1,11 @@ [build-system] -requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"] +requires = ["setuptools>=45", "wheel"] build-backend = "setuptools.build_meta" [project] -name = "basyx-python-sdk" +name = "basyx-python-sdk-server" version = "1.0.0" -description = "The Eclipse BaSyx Python SDK, an implementation of the Asset Administration Shell for Industry 4.0 systems" +description = "The Server for the BaSyx-Python-SDK" authors = [ { name = "The Eclipse BaSyx Authors", email = "admins@iat.rwth-aachen.de" } ] @@ -15,37 +15,27 @@ classifiers = [ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", - "Development Status :: 5 - Production/Stable" ] requires-python = ">=3.8" dependencies = [ - "python-dateutil>=2.8,<3", - "lxml>=4.2,<5", - "urllib3>=1.26,<2.0", - "pyecma376-2>=0.2.4" + "Flask>=2.0,<3.0", ] [project.optional-dependencies] dev = [ "mypy", - "pycodestyle", - "codeblocks", + "pytest", "coverage", ] -[project.urls] -"Homepage" = "https://github.com/eclipse-basyx/basyx-python-sdk" - [tool.setuptools] -packages = ["basyx"] +packages = ["basyx.server"] [tool.setuptools.package-data] -basyx = ["py.typed"] -"basyx.aas.examples.data" = ["TestFile.pdf"] +basyx.server = ["py.typed"] [tool.setuptools.exclude-package-data] "*" = ["test", "test.*"] [tool.mypy] -files = ["basyx/aas/src"] - +files = ["basyx/server"] From aec11b73b380328f9830efdb0626b70aacfccf07 Mon Sep 17 00:00:00 2001 From: Sercan Sahin Date: Sat, 28 Sep 2024 19:31:40 +0200 Subject: [PATCH 19/19] add link to Part 2, update Docker branch --- README.md | 2 +- basyx/server/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac30e405..0241d4b1 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ These are the currently implemented specifications: | Specification | Version | |---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Part 1: Metamodel | [v3.0 (01001-3-0)](https://industrialdigitaltwin.org/wp-content/uploads/2023/06/IDTA-01001-3-0_SpecificationAssetAdministrationShell_Part1_Metamodel.pdf) | -| Part 2: API | not implemented yet | +| Part 2: API | [v3.0 (01002-3-0)](https://industrialdigitaltwin.org/wp-content/uploads/2023/06/IDTA-01002-3-0_SpecificationAssetAdministrationShell_Part2_API_.pdf) | | Part 3a: Data Specification IEC 61360 | [v3.0 (01003-a-3-0)](https://industrialdigitaltwin.org/wp-content/uploads/2023/04/IDTA-01003-a-3-0_SpecificationAssetAdministrationShell_Part3a_DataSpecification_IEC61360.pdf) | | Part 5: Package File Format (AASX) | [v3.0 (01005-3-0)](https://industrialdigitaltwin.org/wp-content/uploads/2023/04/IDTA-01005-3-0_SpecificationAssetAdministrationShell_Part5_AASXPackageFileFormat.pdf) | diff --git a/basyx/server/Dockerfile b/basyx/server/Dockerfile index e6e65e21..5361ad2c 100644 --- a/basyx/server/Dockerfile +++ b/basyx/server/Dockerfile @@ -8,7 +8,7 @@ ENV NGINX_MAX_UPLOAD 1M ENV UWSGI_CHEAPER 0 ENV UWSGI_PROCESSES 1 -RUN pip install --no-cache-dir git+https://github.com/rwth-iat/basyx-python-sdk@feature/http_api +RUN pip install --no-cache-dir git+https://github.com/rwth-iat/basyx-python-sdk@main COPY ./app /app COPY ./nginx /etc/nginx/conf.d