Skip to content

Commit b686c7e

Browse files
authored
BRAYNS-654 Add engine endpoints (#1276)
1 parent a010de5 commit b686c7e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1632
-760
lines changed

README.md

+5-31
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,11 @@ Brayns comes with a main application:
1010

1111
## Building
1212

13-
Brayns is developed, maintained and run on Linux-based operating systems, being tested mainly on RHEL and Ubuntu. The following platforms and build environments have been tested:
14-
15-
* Linux: Ubuntu 16.04, Ubuntu 18.04, Ubuntu 20.04, Debian 9, RHEL 7 (Makefile, x64)
13+
TODO
1614

1715
### System dependencies
1816

19-
The following components must be installed on the system where Brayns will be built:
20-
21-
* GCC 12.1 or higher (Requires C++ 20 support)
22-
* CMake 3.15 or higher
23-
* Make or Ninja build systems
24-
* Git
25-
* Package config
26-
* SSL Development files
27-
* Python 3.9 or higher
28-
* Custom OSPRay 2.10.5 (https://github.com/BlueBrain/ospray/tree/v2.10.5)
29-
* zlib
30-
31-
Optionally, to build the core plugins of Brayns, the following components are required.
32-
33-
* HDF5 development files
34-
* Bzip2
35-
36-
Brayns uses further dependencies, but if they are not present on the system, it will download them by itself during build.
37-
38-
* Poco libraries 1.12.4 (https://github.com/pocoproject/poco/tree/poco-1.12.4-release)
39-
* spdlog 1.9.2 (https://github.com/gabime/spdlog/tree/v1.9.2)
40-
* stb (https://github.com/nothings/stb)
41-
* tinyexr (https://github.com/syoyo/tinyexr/tree/v1.0.1)
42-
* libsonata 0.1.22 (https://github.com/BlueBrain/libsonata/tree/v0.1.22)
43-
* MorphIO 3.3.5 (https://github.com/BlueBrain/MorphIO/tree/v3.3.5)
17+
TODO
4418

4519
### Build command
4620

@@ -70,7 +44,7 @@ The following cmake options (shown with their default value) can be used during
7044

7145
To run the braynsService app, execute the following command (The command assumes braynsService executable is available on the system **PATH**):
7246

73-
$ braynsService --uri 0.0.0.0:5000
47+
$ braynsService --host 0.0.0.0 --port 5000
7448

7549
The ***--uri*** parameter allows to specify an address and a port to bind to. In the example, the service is binding to all available addresses and the port 5000.
7650

@@ -82,15 +56,15 @@ Use the following command to get more details about command line arguments.
8256

8357
Brayns is available as a docker image at https://hub.docker.com/r/bluebrain/brayns. The image allows to launch the braynsService application.
8458

85-
It is built with every commit merged into the main repository branch (develop), and deployed into docker hub as brayns:latest. Furthermore, when a new release is made, and a new tag created, an additional image is built and deployed with the same tag.
59+
It is built with every commit merged into the main repository branch (master), and deployed into docker hub as brayns:latest. Furthermore, when a new release is made, and a new tag created, an additional image is built and deployed with the same tag.
8660

8761
To get Brayns docker image, you will need to have docker installed. Then execute the following command to download it:
8862

8963
$ docker pull bluebrain/brayns:latest
9064

9165
To run it, simply execute the following command:
9266

93-
$ docker run -ti --rm -p 5000:5000 bluebrain/brayns --uri 0.0.0.0:5000
67+
$ docker run -ti --rm -p 5000:5000 bluebrain/brayns --host 0.0.0.0 --port 5000
9468

9569
Additional parameters, can be specified in a similar fashion as in the **braynsService** application.
9670

python/README.md

+4-5
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,17 @@ pip install -r requirements.txt
7676
pip install -r requirements-dev.txt
7777
```
7878

79-
3. For integration testing, create a `.env` file:
79+
3. Optional (for integration testing), create a `.env` file:
8080

8181
```bash
8282
BRAYNS_HOST=localhost
8383
BRAYNS_PORT=5000
84-
BRAYNS_EXECUTABLE=path/to/braynsService
85-
LD_LIBRARY_PATH=path/to/additional/libs
84+
BRAYNS_SSL=0
8685
```
8786

8887
Note: integration testing can be disable using the pytest --without-integration flag.
8988

90-
4. Create a .vscode folder and create a `launch.json` inside to use to debug tests:
89+
4. Create a .vscode folder and create a `launch.json` file inside to be able to debug tests:
9190

9291
```json
9392
{
@@ -108,7 +107,7 @@ Note: integration testing can be disable using the pytest --without-integration
108107
}
109108
```
110109

111-
5. In the same folder, create a `settings.json` to configure pytest:
110+
5. In the same folder, create a `settings.json` file to configure pytest:
112111

113112
```json
114113
{

python/brayns/__init__.py

+18
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@
2424
This package provides an API to interact with Brayns service.
2525
"""
2626

27+
from .api.core.objects import (
28+
Object,
29+
clear_objects,
30+
create_empty_object,
31+
get_all_objects,
32+
get_object,
33+
remove_objects,
34+
update_object,
35+
)
2736
from .api.core.service import (
2837
Endpoint,
2938
Task,
@@ -49,19 +58,25 @@
4958
JsonRpcSuccessResponse,
5059
)
5160
from .network.websocket import ServiceUnavailable, WebSocketError
61+
from .utils.logger import create_logger
5262
from .version import VERSION
5363

5464
__version__ = VERSION
5565
"""Version tag of brayns Python package (major.minor.patch)."""
5666

5767
__all__ = [
5868
"cancel_task",
69+
"clear_objects",
5970
"connect",
6071
"Connection",
72+
"create_empty_object",
73+
"create_logger",
6174
"Endpoint",
6275
"FutureResponse",
76+
"get_all_objects",
6377
"get_endpoint",
6478
"get_methods",
79+
"get_object",
6580
"get_task_result",
6681
"get_task",
6782
"get_tasks",
@@ -72,13 +87,16 @@
7287
"JsonRpcRequest",
7388
"JsonRpcResponse",
7489
"JsonRpcSuccessResponse",
90+
"Object",
91+
"remove_objects",
7592
"Request",
7693
"Response",
7794
"ServiceUnavailable",
7895
"stop_service",
7996
"Task",
8097
"TaskInfo",
8198
"TaskOperation",
99+
"update_object",
82100
"Version",
83101
"WebSocketError",
84102
]

python/brayns/api/core/objects.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Copyright (c) 2015-2024 EPFL/Blue Brain Project
2+
# All rights reserved. Do not distribute without permission.
3+
#
4+
# Responsible Author: [email protected]
5+
#
6+
# This file is part of Brayns <https://github.com/BlueBrain/Brayns>
7+
#
8+
# This library is free software; you can redistribute it and/or modify it under
9+
# the terms of the GNU Lesser General Public License version 3.0 as published
10+
# by the Free Software Foundation.
11+
#
12+
# This library is distributed in the hope that it will be useful, but WITHOUT
13+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14+
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15+
# details.
16+
#
17+
# You should have received a copy of the GNU Lesser General Public License
18+
# along with this library; if not, write to the Free Software Foundation, Inc.,
19+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20+
21+
from dataclasses import dataclass
22+
from typing import Any
23+
24+
from brayns.network.connection import Connection
25+
from brayns.utils.parsing import check_type, get, try_get
26+
27+
28+
@dataclass
29+
class Object:
30+
id: int
31+
type: str
32+
user_data: Any
33+
34+
35+
def parse_object(message: dict[str, Any]) -> Object:
36+
return Object(
37+
id=get(message, "id", int),
38+
type=get(message, "type", str),
39+
user_data=try_get(message, "user_data", Any, None),
40+
)
41+
42+
43+
async def get_all_objects(connection: Connection) -> list[Object]:
44+
result = await connection.get_result("get-all-objects")
45+
check_type(result, dict[str, Any])
46+
objects = get(result, "objects", list[dict[str, Any]])
47+
return [parse_object(item) for item in objects]
48+
49+
50+
async def get_object(connection: Connection, id: int) -> Object:
51+
result = await connection.get_result("get-object", {"id": id})
52+
check_type(result, dict[str, Any])
53+
return parse_object(result)
54+
55+
56+
async def update_object(connection: Connection, id: int, user_data: Any) -> None:
57+
properties = {"user_data": user_data}
58+
await connection.get_result("update-object", {"id": id, "properties": properties})
59+
60+
61+
async def remove_objects(connection: Connection, ids: list[int]) -> None:
62+
await connection.get_result("remove-objects", {"ids": ids})
63+
64+
65+
async def clear_objects(connection: Connection) -> None:
66+
await connection.get_result("clear-objects")
67+
68+
69+
async def create_empty_object(connection: Connection) -> int:
70+
result = await connection.get_result("create-empty-object")
71+
check_type(result, dict[str, Any])
72+
return get(result, "id", int)

python/brayns/network/connection.py

+14-8
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2020

2121
import asyncio
22-
from logging import Logger
22+
from logging import WARNING, Logger
2323
from ssl import SSLContext
2424
from typing import Any, NamedTuple, Self
2525

26+
from brayns.utils.logger import create_logger
27+
2628
from .json_rpc import (
2729
JsonRpcError,
2830
JsonRpcErrorResponse,
@@ -110,6 +112,9 @@ def done(self) -> bool:
110112
return self._buffer.is_done(self._request_id)
111113

112114
async def poll(self) -> None:
115+
if self._request_id is None:
116+
raise ValueError("Cannot poll requests without ID")
117+
113118
if self.done:
114119
return
115120

@@ -203,7 +208,7 @@ async def connect(
203208
logger: Logger | None = None,
204209
) -> Connection:
205210
if logger is None:
206-
logger = Logger("Brayns")
211+
logger = create_logger(WARNING)
207212

208213
protocol = "ws" if ssl is None else "wss"
209214
url = f"{protocol}://{host}:{port}"
@@ -214,15 +219,16 @@ async def connect(
214219
try:
215220
logger.info("Connection attempt %d", attempt)
216221
websocket = await connect_websocket(url, ssl, max_frame_size, logger)
217-
break
222+
logger.info("Connection suceeded")
223+
224+
return Connection(websocket)
218225
except ServiceUnavailable as e:
219-
logger.warning("Connection attempt failed: %s", e)
226+
logger.warning("Connection attempt %d failed: %s", attempt, e)
227+
228+
attempt += 1
229+
220230
if max_attempts is not None and attempt >= max_attempts:
221231
logger.warning("Max connection attempts reached, aborted")
222232
raise
223233

224234
await asyncio.sleep(sleep_between_attempts)
225-
226-
logger.info("Connection suceeded")
227-
228-
return Connection(websocket)

python/brayns/network/websocket.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ async def connect_websocket(url: str, ssl: SSLContext | None, max_frame_size: in
127127
logger.warning("Connection failed: %s", e)
128128
raise WebSocketError(str(e))
129129
except OSError as e:
130-
logger.warning("Service not found (probably not ready): %s", e)
130+
logger.warning("Service not found (maybe not ready): %s", e)
131131
raise ServiceUnavailable(str(e))
132132

133133
wrapper = _WebSocket(websocket, max_frame_size, logger)

python/brayns/utils/logger.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Copyright (c) 2015-2024 EPFL/Blue Brain Project
2+
# All rights reserved. Do not distribute without permission.
3+
#
4+
# Responsible Author: [email protected]
5+
#
6+
# This file is part of Brayns <https://github.com/BlueBrain/Brayns>
7+
#
8+
# This library is free software; you can redistribute it and/or modify it under
9+
# the terms of the GNU Lesser General Public License version 3.0 as published
10+
# by the Free Software Foundation.
11+
#
12+
# This library is distributed in the hope that it will be useful, but WITHOUT
13+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14+
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15+
# details.
16+
#
17+
# You should have received a copy of the GNU Lesser General Public License
18+
# along with this library; if not, write to the Free Software Foundation, Inc.,
19+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20+
21+
from logging import INFO, Formatter, Logger, StreamHandler
22+
import sys
23+
24+
25+
def create_logger(level: int | str = INFO) -> Logger:
26+
logger = Logger("Brayns", level)
27+
28+
format = "[%(asctime)s][%(name)s][%(levelname)s] %(message)s"
29+
formatter = Formatter(format)
30+
31+
handler = StreamHandler(sys.stdout)
32+
handler.setFormatter(formatter)
33+
34+
logger.addHandler(handler)
35+
36+
return logger

python/tests/data/certificate.pem

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
-----BEGIN CERTIFICATE-----
2+
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
32+
osxjP8g=
33+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)