Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

basyx: add server with git history. #297

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b667c16
initial commit
jkhsjdhjs Mar 30, 2024
65b0796
add readme
jkhsjdhjs Mar 30, 2024
38303ef
move nginx config to subdirectory
jkhsjdhjs Mar 30, 2024
73bf9e8
`docker build` is deprecated
jkhsjdhjs Mar 30, 2024
46545f6
Dockerfile: update a comment
jkhsjdhjs Mar 30, 2024
e9ee8b0
README.md: Add link to API specification
s-heppner Apr 1, 2024
9f4b9ca
readme: add note about windows paths
jkhsjdhjs Jun 13, 2024
7fe62b4
readme: move a link to the bottom
jkhsjdhjs Jun 13, 2024
dd92330
nginx: add CORS header
jkhsjdhjs Jun 15, 2024
1f62df9
app: support AASX files
jkhsjdhjs Jun 15, 2024
31f9ea3
app: support JSON and XML files
zrgt Aug 21, 2024
b32c97c
Revert "app: support JSON and XML files "
zrgt Aug 21, 2024
73569bf
app: Add support of JSON and XML files
zrgt Aug 21, 2024
f8c81ba
app: lower the suffixes
zrgt Sep 2, 2024
6ad831f
Remove comments
zrgt Sep 3, 2024
8c1742c
Merge remote-tracking branch 'server/main' into add_server
Frosty2500 Sep 16, 2024
91d51d7
Add server to basyx-python-sdk
Frosty2500 Sep 16, 2024
24d31e2
Merge branch 'eclipse-basyx:main' into add_server
Frosty2500 Sep 17, 2024
7225c88
move pyproject.toml
Frosty2500 Sep 17, 2024
af778a1
Merge branch 'eclipse-basyx:main' into add_server
Frosty2500 Sep 23, 2024
f6fdc27
one pyproject.toml for every project
Frosty2500 Sep 28, 2024
7ca2780
Merge branch 'eclipse-basyx:main' into add_server
Frosty2500 Sep 28, 2024
c81cb06
Merge branch 'add_server' of github.com:rwth-iat/basyx-python-sdk int…
Frosty2500 Sep 28, 2024
aec11b7
add link to Part 2, update Docker branch
Frosty2500 Sep 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |

Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml → basyx/pyproject-aas.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,6 @@ basyx = ["py.typed"]
[tool.setuptools.exclude-package-data]
"*" = ["test", "test.*"]

[tool.mypy]
files = ["basyx/aas"]

41 changes: 41 additions & 0 deletions basyx/pyproject-server.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[build-system]
requires = ["setuptools>=45", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "basyx-python-sdk-server"
version = "1.0.0"
description = "The Server for the BaSyx-Python-SDK"
authors = [
{ name = "The Eclipse BaSyx Authors", email = "[email protected]" }
]
readme = "README.md"
license = { file = "LICENSE" }
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
requires-python = ">=3.8"
dependencies = [
"Flask>=2.0,<3.0",
]

[project.optional-dependencies]
dev = [
"mypy",
"pytest",
"coverage",
]

[tool.setuptools]
packages = ["basyx.server"]

[tool.setuptools.package-data]
basyx.server = ["py.typed"]

[tool.setuptools.exclude-package-data]
"*" = ["test", "test.*"]

[tool.mypy]
files = ["basyx/server"]
14 changes: 14 additions & 0 deletions basyx/server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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@main

COPY ./app /app
COPY ./nginx /etc/nginx/conf.d
71 changes: 71 additions & 0 deletions basyx/server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 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][6] with ease.
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][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.

## Building
The container image can be built via:
```
$ docker buildx build -t basyx-python-sdk-http-server .
```

## Running

### 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
```

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
```

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
[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
[8]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/json.html
[9]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/xml.html
46 changes: 46 additions & 0 deletions basyx/server/app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os
import pathlib
import sys

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", "LOCAL_FILE_READ_ONLY")
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_BACKEND":
application = WSGIApp(LocalFileObjectStore(storage_path), aasx.DictSupplementaryFileContainer(), **wsgi_optparams)

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).iterdir():
if not file.is_file():
continue
print(f"Loading {file}")

if file.suffix.lower() == ".json":
with open(file) as f:
adapter.json.read_aas_json_file_into(object_store, f)
elif file.suffix.lower() == ".xml":
with open(file) as f:
adapter.xml.read_aas_xml_file_into(object_store, file)
elif file.suffix.lower() == ".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 LOCAL_FILE_READ_ONLY! Current value: {storage_type}",
file=sys.stderr)
6 changes: 6 additions & 0 deletions basyx/server/nginx/body-buffer-size.conf
Original file line number Diff line number Diff line change
@@ -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;
1 change: 1 addition & 0 deletions basyx/server/nginx/cors-header.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_header Access-Control-Allow-Origin *;
Loading