pybrid-computing 0.10.0__py3-none-any.whl
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.
- pybrid/cli/redac/__init__.py +356 -0
- pybrid/redac/__init__.py +6 -0
- pybrid/redac/blocks/__init__.py +9 -0
- pybrid/redac/blocks/block.py +61 -0
- pybrid/redac/blocks/cblock.py +22 -0
- pybrid/redac/blocks/iblock.py +38 -0
- pybrid/redac/blocks/mblock.py +37 -0
- pybrid/redac/blocks/ublock.py +47 -0
- pybrid/redac/carrier.py +43 -0
- pybrid/redac/cluster.py +83 -0
- pybrid/redac/computations.py +30 -0
- pybrid/redac/computer.py +42 -0
- pybrid/redac/controller.py +141 -0
- pybrid/redac/data.py +64 -0
- pybrid/redac/display.py +25 -0
- pybrid/redac/elements.py +28 -0
- pybrid/redac/entities.py +200 -0
- pybrid/redac/modules.py +15 -0
- pybrid/redac/protocol/__init__.py +20 -0
- pybrid/redac/protocol/envelope.py +62 -0
- pybrid/redac/protocol/messages.py +726 -0
- pybrid/redac/protocol/protocol.py +180 -0
- pybrid/redac/protocol/serializer.py +52 -0
- pybrid/redac/protocol/types.py +17 -0
- pybrid/redac/run.py +131 -0
- pybrid_computing-0.10.0.dist-info/METADATA +136 -0
- pybrid_computing-0.10.0.dist-info/RECORD +28 -0
- pybrid_computing-0.10.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
# Copyright (c) 2022-2024 anabrid GmbH
|
|
2
|
+
# Contact: https://www.anabrid.com/licensing/
|
|
3
|
+
# SPDX-License-Identifier: MIT OR GPL-2.0-or-later
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import typing
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from functools import cache
|
|
9
|
+
|
|
10
|
+
import inflection
|
|
11
|
+
from pydantic import UUID4, BaseModel, Field
|
|
12
|
+
|
|
13
|
+
from ..entities import Path
|
|
14
|
+
from ..run import RunConfig, RunFlags, RunState, DAQConfig
|
|
15
|
+
from .types import SuccessInfo
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ██████ █████ ███████ ███████ ██████ ██ █████ ███████ ███████ ███████ ███████
|
|
21
|
+
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
22
|
+
# ██████ ███████ ███████ █████ ██ ██ ███████ ███████ ███████ █████ ███████
|
|
23
|
+
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
24
|
+
# ██████ ██ ██ ███████ ███████ ██████ ███████ ██ ██ ███████ ███████ ███████ ███████
|
|
25
|
+
|
|
26
|
+
TYPE_IDENTIFIER_MESSAGE_MAP = dict()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Message(BaseModel):
|
|
30
|
+
"""
|
|
31
|
+
Base class for all messages.
|
|
32
|
+
|
|
33
|
+
Serialization and deserialization is handled with the help of the :code:`pydantic` package.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def parse_obj(cls, **data: dict) -> 'Message':
|
|
38
|
+
"""Parses data from `dict` or `json`-like and returns a message instance."""
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def register_callback(cls, callback: typing.Callable) -> typing.Callable:
|
|
43
|
+
"""Register a callback for some :py:class:`Message` subclass, which is triggered when the respective message
|
|
44
|
+
is received.
|
|
45
|
+
|
|
46
|
+
:parameter callback: Function to register as callback
|
|
47
|
+
:returns: The original function
|
|
48
|
+
:Usage: Intended to be used as decorator:
|
|
49
|
+
|
|
50
|
+
.. code-block::
|
|
51
|
+
|
|
52
|
+
# Register callback triggered on e.g. an incoming RunStateChangeMessage
|
|
53
|
+
@RunStateChangeMessage.register_callback
|
|
54
|
+
def callback(self, msg: RunStateChangeMessage):
|
|
55
|
+
# do something with the message
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
@cache
|
|
62
|
+
def get_type_identifier(cls):
|
|
63
|
+
pascal_case = cls.__name__.removesuffix("Message").removesuffix("Request").removesuffix("Response")
|
|
64
|
+
snake_case = inflection.underscore(pascal_case)
|
|
65
|
+
return snake_case
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def get_class_for_type_identifier(type_):
|
|
69
|
+
return TYPE_IDENTIFIER_MESSAGE_MAP[type_]
|
|
70
|
+
|
|
71
|
+
def __init_subclass__(cls, **kwargs):
|
|
72
|
+
super().__init_subclass__(**kwargs)
|
|
73
|
+
TYPE_IDENTIFIER_MESSAGE_MAP[cls.get_type_identifier()] = cls
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
REQUEST_RESPONSE_MAP = dict()
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class Request(Message):
|
|
80
|
+
"""
|
|
81
|
+
Base class for requests sent to the controller.
|
|
82
|
+
|
|
83
|
+
.. uml::
|
|
84
|
+
|
|
85
|
+
Client -> Controller: **Request(...)**
|
|
86
|
+
Controller -> Client: Response(...)
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
@classmethod
|
|
90
|
+
def get_expected_response_type(cls) -> typing.Type["Response"]:
|
|
91
|
+
"""The :py:class:`Response` subclass expected for the answer to this request."""
|
|
92
|
+
return REQUEST_RESPONSE_MAP[cls]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Response(Message):
|
|
96
|
+
"""
|
|
97
|
+
Base class for responses to a previous request.
|
|
98
|
+
|
|
99
|
+
.. uml::
|
|
100
|
+
|
|
101
|
+
Client -> Controller: Request(...)
|
|
102
|
+
Controller -> Client: **Response(...)**
|
|
103
|
+
|
|
104
|
+
"""
|
|
105
|
+
#: The :py:class:`Request` subclass to which this is the response
|
|
106
|
+
response_for: typing.ClassVar[Request]
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def successful(self) -> bool:
|
|
110
|
+
"""Indicates whether the request was handled successfully"""
|
|
111
|
+
return not bool(self.first_error)
|
|
112
|
+
|
|
113
|
+
@property
|
|
114
|
+
def first_error(self) -> typing.Optional[str]:
|
|
115
|
+
"""Error message of the first error that occurred when the request was handled.
|
|
116
|
+
`None` if there was no error."""
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
def __init_subclass__(cls, **kwargs):
|
|
120
|
+
super().__init_subclass__(**kwargs)
|
|
121
|
+
REQUEST_RESPONSE_MAP[cls.response_for] = cls
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
# ██ ██ ████████ ██ ██ ██ ████████ ██ ██
|
|
125
|
+
# ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
126
|
+
# ██ ██ ██ ██ ██ ██ ██ ████
|
|
127
|
+
# ██ ██ ██ ██ ██ ██ ██ ██
|
|
128
|
+
# ██████ ██ ██ ███████ ██ ██ ██
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class PingRequest(Request):
|
|
132
|
+
"""
|
|
133
|
+
A heartbeat request to check for controller status.
|
|
134
|
+
The controller replies with a :py:class:`PingResponse` message.
|
|
135
|
+
|
|
136
|
+
.. uml::
|
|
137
|
+
|
|
138
|
+
Client -> Controller: **PingRequest(...)**
|
|
139
|
+
Controller -> Client: PingResponse(...)
|
|
140
|
+
|
|
141
|
+
"""
|
|
142
|
+
#: A timestamp used to synchronize client and controller clocks.
|
|
143
|
+
now: datetime = Field(default_factory=datetime.utcnow)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class PingResponse(Response):
|
|
147
|
+
"""
|
|
148
|
+
A heartbeat response to an incoming :py:class:`PingRequest` message.
|
|
149
|
+
|
|
150
|
+
.. uml::
|
|
151
|
+
|
|
152
|
+
Client -> Controller: PingRequest(...)
|
|
153
|
+
Controller -> Client: **PingResponse(...)**
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
response_for = PingRequest
|
|
157
|
+
#: A timestamp used to synchronize client and controller clocks.
|
|
158
|
+
#: The controller returns its timestamp so the client can check if it was applied correctly.
|
|
159
|
+
now: datetime = Field(default_factory=datetime.utcnow)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class HackRequest(Request):
|
|
163
|
+
"""
|
|
164
|
+
A message which may contain an arbitrary command and data,
|
|
165
|
+
intended for development purposes only!
|
|
166
|
+
"""
|
|
167
|
+
command: str
|
|
168
|
+
data: typing.Any
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class HackResponse(Response):
|
|
172
|
+
response_for = HackRequest
|
|
173
|
+
data: typing.Any
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
# ██ ███ ██ ██ ████████ ██ █████ ██ ██ ███████ █████ ████████ ██ ██████ ███ ██
|
|
177
|
+
# ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ████ ██
|
|
178
|
+
# ██ ██ ██ ██ ██ ██ ██ ███████ ██ ██ ███ ███████ ██ ██ ██ ██ ██ ██ ██
|
|
179
|
+
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
180
|
+
# ██ ██ ████ ██ ██ ██ ██ ██ ███████ ██ ███████ ██ ██ ██ ██ ██████ ██ ████
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class ResetRequest(Request):
|
|
184
|
+
"""
|
|
185
|
+
A request for the hybrid controller to reset its configuration.
|
|
186
|
+
|
|
187
|
+
Depending on the flags inside the message, only part of the configuration is reset.
|
|
188
|
+
The :attr:`sync` flag can be used to prevent actually writing the configuration to the hardware,
|
|
189
|
+
i.e. it is kept in memory only (useful if subsequent requests change it anyway).
|
|
190
|
+
|
|
191
|
+
.. uml::
|
|
192
|
+
|
|
193
|
+
Client -> Controller: **ResetRequest()**
|
|
194
|
+
activate Controller
|
|
195
|
+
note over Controller: resets system
|
|
196
|
+
Controller -> Client: ResetResponse()
|
|
197
|
+
deactivate Controller
|
|
198
|
+
"""
|
|
199
|
+
#: Whether the calibration data should be kept.
|
|
200
|
+
keep_calibration: typing.Optional[bool] = True
|
|
201
|
+
#: Whether to immediately sync to hardware.
|
|
202
|
+
sync: typing.Optional[bool] = True
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class ResetResponse(Response):
|
|
206
|
+
"""
|
|
207
|
+
A response to a previous :class:`ResetRequest`.
|
|
208
|
+
"""
|
|
209
|
+
response_for = ResetRequest
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class GetEntitiesRequest(Request):
|
|
213
|
+
"""
|
|
214
|
+
A request for the list of entity types (:py:class:`pybrid.redac.entities.EntityType`)
|
|
215
|
+
by their path (:class:`pybrid.redac.entities.Path`).
|
|
216
|
+
The controller responds with a :py:class:`GetEntitiesResponse` containing a tree-like representation of all entities.
|
|
217
|
+
|
|
218
|
+
The :class:`GetEntitiesResponse` tells you the current assembly structure of the analog computer.
|
|
219
|
+
Use a series of :class:`GetEntityConfiguration` messages if you also need to know the current configuration.
|
|
220
|
+
|
|
221
|
+
.. uml::
|
|
222
|
+
|
|
223
|
+
Client -> Controller: **GetEntitiesRequest()**
|
|
224
|
+
activate Controller
|
|
225
|
+
note over Controller: scans system
|
|
226
|
+
Controller -> Client: GetEntitiesResponse()
|
|
227
|
+
deactivate Controller
|
|
228
|
+
|
|
229
|
+
"""
|
|
230
|
+
pass
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class GetEntitiesResponse(Response):
|
|
234
|
+
"""
|
|
235
|
+
A response containing the list of entities (:py:class:`pybrid.redac.entities.EntityType`)
|
|
236
|
+
currently in the analog computer.
|
|
237
|
+
|
|
238
|
+
.. uml::
|
|
239
|
+
|
|
240
|
+
Client -> Controller: GetEntitiesRequest()
|
|
241
|
+
activate Controller
|
|
242
|
+
note over Controller: scans system
|
|
243
|
+
Controller -> Client: **GetEntitiesResponse()**
|
|
244
|
+
deactivate Controller
|
|
245
|
+
|
|
246
|
+
"""
|
|
247
|
+
response_for = GetEntitiesRequest
|
|
248
|
+
#: A tree-like dictionary structure containing entity type information by path.
|
|
249
|
+
entities: dict
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
# ███████ ███████ ███████ ███████ ██ ██████ ███ ██
|
|
253
|
+
# ██ ██ ██ ██ ██ ██ ██ ████ ██
|
|
254
|
+
# ███████ █████ ███████ ███████ ██ ██ ██ ██ ██ ██
|
|
255
|
+
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
256
|
+
# ███████ ███████ ███████ ███████ ██ ██████ ██ ████
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class StartSessionRequest(Request):
|
|
260
|
+
"""
|
|
261
|
+
Request to start a session for which the requested elements are reserved.
|
|
262
|
+
No other client can request those elements until the session is ended.
|
|
263
|
+
|
|
264
|
+
Starting and managing sessions is only necessary if your controller is configured to require it.
|
|
265
|
+
|
|
266
|
+
.. uml::
|
|
267
|
+
|
|
268
|
+
participant "Client A" as C1
|
|
269
|
+
participant "Client B" as C2
|
|
270
|
+
participant "Controller" as CTRL
|
|
271
|
+
|
|
272
|
+
C1 -> CTRL: StartSessionRequest(entities=[<X>, <Y>])
|
|
273
|
+
CTRL -> C1: StartSessionResponse(id_=<secret>, success=True)
|
|
274
|
+
...during active session of Client A...
|
|
275
|
+
C2 -> CTRL: StartSessionRequest(entities=[<X>, <Z>])
|
|
276
|
+
CTRL -> C2: StartSessionResponse(success=False, error="X reserved.")
|
|
277
|
+
...
|
|
278
|
+
C1 -> CTRL: EndSessionRequest(id_=<secret>)
|
|
279
|
+
CTRL -> C1: EndSessionResponse(success=True)
|
|
280
|
+
|
|
281
|
+
"""
|
|
282
|
+
#: A list of analog entities to reserve for this session.
|
|
283
|
+
entities: list[Path]
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class StartSessionResponse(Response):
|
|
287
|
+
"""
|
|
288
|
+
Response to a prior :class:`StartSessionRequest`.
|
|
289
|
+
|
|
290
|
+
If the reservation was successful, a secret ID is returned.
|
|
291
|
+
This ID is used in subsequent configuration and run requests to authorize their usage of reserved entities.
|
|
292
|
+
|
|
293
|
+
If not all requested entities could be reserved for the new session, the session is not started.
|
|
294
|
+
"""
|
|
295
|
+
response_for = StartSessionRequest
|
|
296
|
+
#: Secret session ID or None if the session could not be started.
|
|
297
|
+
id_: typing.Optional[UUID4]
|
|
298
|
+
#: Whether the session could be started and optional error info.
|
|
299
|
+
success: SuccessInfo
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class EndSessionRequest(Request):
|
|
303
|
+
"""
|
|
304
|
+
Request to end a session.
|
|
305
|
+
|
|
306
|
+
If there are any ongoing runs in the session, they are canceled first and any messages related to them are sent
|
|
307
|
+
first by the controller, before the corresponding :class:`EndSessionResponse` is sent.
|
|
308
|
+
|
|
309
|
+
Inactive sessions may be ended automatically depending on controller configuration.
|
|
310
|
+
|
|
311
|
+
.. uml::
|
|
312
|
+
|
|
313
|
+
participant "Client" as C
|
|
314
|
+
participant "Controller" as CTRL
|
|
315
|
+
|
|
316
|
+
C -> CTRL: EndSessionRequest(id_=<secret>)
|
|
317
|
+
activate CTRL
|
|
318
|
+
alt if ongoing requests
|
|
319
|
+
note over CTRL: cancels any ongoing runs in this session
|
|
320
|
+
CTRL -> C: RunStateChangeMessage(new=ERROR, ...)
|
|
321
|
+
end
|
|
322
|
+
CTRL -> C: EndSessionResponse(success=True)
|
|
323
|
+
deactivate CTRL
|
|
324
|
+
|
|
325
|
+
"""
|
|
326
|
+
#: The secret session ID to end.
|
|
327
|
+
id_: UUID4
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class EndSessionResponse(Response):
|
|
331
|
+
"""
|
|
332
|
+
Response to a prior :class:`EndSessionRequest`.
|
|
333
|
+
"""
|
|
334
|
+
response_for = EndSessionRequest
|
|
335
|
+
#: Whether the session could be ended and optional error info. Usually True.
|
|
336
|
+
success: SuccessInfo
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
class EntityReservationRequest(Request):
|
|
340
|
+
"""
|
|
341
|
+
Request to reserve additional entities for an existing session.
|
|
342
|
+
"""
|
|
343
|
+
#: Secret session ID
|
|
344
|
+
id_: UUID4
|
|
345
|
+
#: A list of analog entities to reserve for this session.
|
|
346
|
+
entities: list[Path]
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class EntityReservationResponse(Response):
|
|
350
|
+
"""
|
|
351
|
+
Response to a prior :class:`EntityReservationRequest`.
|
|
352
|
+
"""
|
|
353
|
+
response_for = EntityReservationRequest
|
|
354
|
+
#: Whether the requested entities were reserved and error information if they were not.
|
|
355
|
+
success: SuccessInfo
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
# ██████ ██████ ███ ██ ███████ ██ ██████ ██ ██ ██████ █████ ████████ ██ ██████ ███ ██
|
|
359
|
+
# ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██
|
|
360
|
+
# ██ ██ ██ ██ ██ ██ █████ ██ ██ ███ ██ ██ ██████ ███████ ██ ██ ██ ██ ██ ██ ██
|
|
361
|
+
# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
362
|
+
# ██████ ██████ ██ ████ ██ ██ ██████ ██████ ██ ██ ██ ██ ██ ██ ██████ ██ ████
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class SetConfigRequest(Request):
|
|
366
|
+
"""
|
|
367
|
+
A request to the controller to set a configuration for an entity.
|
|
368
|
+
The controller forwards the request to the carrier board on which the entity is located
|
|
369
|
+
and forwards its :class:`SetConfigResponse` response back.
|
|
370
|
+
|
|
371
|
+
.. uml::
|
|
372
|
+
|
|
373
|
+
participant "Client" as C
|
|
374
|
+
participant "Controller" as CTRL
|
|
375
|
+
participant "Carrier Board\\n(00:00:5e:00:53:af, )" as CB
|
|
376
|
+
participant "Entity\\n(00:00:5e:00:53:af, 7, 42)" as E
|
|
377
|
+
|
|
378
|
+
C -> CTRL: SetConfigRequest(\\n entity=(00:00:5e:00:53:af, 7, 42), ...\\n)
|
|
379
|
+
activate CTRL
|
|
380
|
+
CTRL -> CB: SetConfigRequest(entity=(7, 42), ...)
|
|
381
|
+
activate CB
|
|
382
|
+
CB <-> E: <entity specific data via SPI>
|
|
383
|
+
CTRL <- CB: SetConfigResponse(...)
|
|
384
|
+
deactivate CB
|
|
385
|
+
C <- CTRL: SetConfigResponse(...)
|
|
386
|
+
deactivate CTRL
|
|
387
|
+
|
|
388
|
+
Entities are arranged hierarchically in the REDAC.
|
|
389
|
+
The config dictionary is passed to the entity defined by :attr:`SetConfigRequest.entity`.
|
|
390
|
+
To allow the configuration of multiple entities, the :attr:`SetConfigRequest.config` dictionary
|
|
391
|
+
may contain keys starting with a slash ("/"), which are used to denote paths to sub-entities.
|
|
392
|
+
Such sub-entity path keys must denote a sub-config dictionary, which is again passed on.
|
|
393
|
+
|
|
394
|
+
The structure and content of the configuration message depend on the entities to be configured.
|
|
395
|
+
See :doc:`/redac/configurations` for details.
|
|
396
|
+
|
|
397
|
+
Example of a multi-entity :class:`SetConfigRequest` message.
|
|
398
|
+
|
|
399
|
+
.. code-block:: json
|
|
400
|
+
|
|
401
|
+
{
|
|
402
|
+
"_id": 42, "_type": "set_config", "msg": {
|
|
403
|
+
"entity": ["04-E9-E5-14-74-BF", "0"],
|
|
404
|
+
"config": {
|
|
405
|
+
"/U": {
|
|
406
|
+
"outputs": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
|
407
|
+
"alt-signals": [3, 8],
|
|
408
|
+
},
|
|
409
|
+
"/C": {
|
|
410
|
+
"elements": {0: 0.42, 3: -0.42}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
"""
|
|
416
|
+
#: The secret session ID for which the entity was reserved. Only required if session management is enabled.
|
|
417
|
+
session: typing.Optional[UUID4]
|
|
418
|
+
#: The entity to configure.
|
|
419
|
+
entity: Path
|
|
420
|
+
#: The configuration to apply.
|
|
421
|
+
#: May contain keys denoting paths to sub-entities (starting with a slash) and their config.
|
|
422
|
+
#: The data schema of the configuration depends on the type of entity,
|
|
423
|
+
#: see :doc:`/redac/configurations` for details.
|
|
424
|
+
config: dict
|
|
425
|
+
|
|
426
|
+
@classmethod
|
|
427
|
+
def make(cls, entity):
|
|
428
|
+
"""Factory method to create a config request for some entity."""
|
|
429
|
+
...
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
class SetConfigResponse(Response):
|
|
433
|
+
"""A response to :py:class:`SetConfigRequest` conveying the success of the latter.
|
|
434
|
+
|
|
435
|
+
.. uml::
|
|
436
|
+
|
|
437
|
+
Client -> Controller: SetConfigRequest(...)
|
|
438
|
+
activate Controller
|
|
439
|
+
note over Controller: sets entity config
|
|
440
|
+
Controller -> Client: **SetConfigResponse**(...)
|
|
441
|
+
deactivate Controller
|
|
442
|
+
"""
|
|
443
|
+
response_for = SetConfigRequest
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
class GetConfigRequest(Request):
|
|
447
|
+
"""
|
|
448
|
+
A request to the controller to retrieve the configuration of an entity.
|
|
449
|
+
The controller responds with a :py:class:`GetConfigResponse`.
|
|
450
|
+
This configuration includes only the effective analog configuration
|
|
451
|
+
(e.g. the scalar factor of a digital potentiometer).
|
|
452
|
+
For any metadata (e.g. calibration) use :class:`GetMetadataRequest`.
|
|
453
|
+
|
|
454
|
+
.. uml::
|
|
455
|
+
|
|
456
|
+
Client -> Controller: **GetConfigRequest**(...)
|
|
457
|
+
activate Controller
|
|
458
|
+
note over Controller: gets entity config
|
|
459
|
+
Controller -> Client: GetConfigResponse(...)
|
|
460
|
+
deactivate Controller
|
|
461
|
+
"""
|
|
462
|
+
#: Path to the entity of which the configuration should be returned.
|
|
463
|
+
entity: Path
|
|
464
|
+
#: Whether to retrieve the configuration of sub-entities recursively.
|
|
465
|
+
recursive: bool = True
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
class GetConfigResponse(Response):
|
|
469
|
+
"""A response to :py:class:`GetConfigRequest` conveying the configuration of some entity.
|
|
470
|
+
|
|
471
|
+
.. uml::
|
|
472
|
+
|
|
473
|
+
Client -> Controller: GetConfigRequest(..)
|
|
474
|
+
activate Controller
|
|
475
|
+
note over Controller: gets entity config
|
|
476
|
+
Controller -> Client: **GetConfigResponse**(...)
|
|
477
|
+
deactivate Controller
|
|
478
|
+
"""
|
|
479
|
+
response_for = GetConfigRequest
|
|
480
|
+
#: Path to the entity of which the configuration is returned.
|
|
481
|
+
entity: Path
|
|
482
|
+
#: The configuration of the entity.
|
|
483
|
+
#: The data schema of the configuration depends on the type of entity.
|
|
484
|
+
config: dict
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
class GetMetadataRequest(Request):
|
|
488
|
+
"""
|
|
489
|
+
A request to the controller to retrieve the metadata of an entity.
|
|
490
|
+
The controller responds with a :py:class:`GetMetadataResponse`.
|
|
491
|
+
The metadata contains entity-specific information (e.g. type identifier, calibration, ...).
|
|
492
|
+
|
|
493
|
+
.. uml::
|
|
494
|
+
|
|
495
|
+
Client -> Controller: **GetMetadataRequest**(...)
|
|
496
|
+
activate Controller
|
|
497
|
+
note over Controller: gets entity metadata
|
|
498
|
+
Controller -> Client: GetMetadataResponse(...)
|
|
499
|
+
deactivate Controller
|
|
500
|
+
"""
|
|
501
|
+
#: Path to the entity of which the metadata should be returned.
|
|
502
|
+
entity: Path
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
class GetMetadataResponse(Response):
|
|
506
|
+
"""A response to :py:class:`GetMetadataRequest` conveying the metadata of some entity.
|
|
507
|
+
|
|
508
|
+
.. uml::
|
|
509
|
+
|
|
510
|
+
Client -> Controller: GetMetadataRequest(...)
|
|
511
|
+
activate Controller
|
|
512
|
+
note over Controller: gets entity metadata
|
|
513
|
+
Controller -> Client: **GetMetadataResponse**(...)
|
|
514
|
+
deactivate Controller
|
|
515
|
+
"""
|
|
516
|
+
response_for = GetMetadataRequest
|
|
517
|
+
#: Path to the entity of which the metadata is returned.
|
|
518
|
+
entity: Path
|
|
519
|
+
#: The metadata of the entity.
|
|
520
|
+
#: The data schema of the metadata depends on the version included in config['sp_version'].
|
|
521
|
+
config: dict
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
class SetDAQRequest(Request):
|
|
525
|
+
"""A request to the controller to set a :py:class:`DAQConfig` determining how and when data should be
|
|
526
|
+
acquired. The controller will respond with a :py:class:`SetDAQResponse`
|
|
527
|
+
|
|
528
|
+
.. uml::
|
|
529
|
+
|
|
530
|
+
Client -> Controller: **SetDAQRequest**(...)
|
|
531
|
+
Controller -> Client: SetDAQResponse(...)
|
|
532
|
+
"""
|
|
533
|
+
#: The DAQ configuration to apply.
|
|
534
|
+
daq: DAQConfig
|
|
535
|
+
#: The secret session ID for which the entities were reserved. Only required if session management is enabled.
|
|
536
|
+
session: typing.Optional[UUID4]
|
|
537
|
+
|
|
538
|
+
class Config:
|
|
539
|
+
arbitrary_types_allowed = True
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
class SetDAQResponse(Response):
|
|
543
|
+
"""A response to :py:class:`SetDAQRequest` conveying the success of the latter
|
|
544
|
+
|
|
545
|
+
.. uml::
|
|
546
|
+
|
|
547
|
+
Client -> Controller: SetDAQRequest(...)
|
|
548
|
+
Controller -> Client: **SetDAQResponse**(...)
|
|
549
|
+
"""
|
|
550
|
+
response_for = SetDAQRequest
|
|
551
|
+
#: Whether the request was successful.
|
|
552
|
+
success: SuccessInfo
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
# ██████ ██ ██ ███ ██
|
|
556
|
+
# ██ ██ ██ ██ ████ ██
|
|
557
|
+
# ██████ ██ ██ ██ ██ ██
|
|
558
|
+
# ██ ██ ██ ██ ██ ██ ██
|
|
559
|
+
# ██ ██ ██████ ██ ████
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
class StartRunRequest(Request):
|
|
563
|
+
"""
|
|
564
|
+
A request to start a run (computation).
|
|
565
|
+
After a run is started, the controller sends :class:`RunStateChangeMessage` notifications about its progress.
|
|
566
|
+
|
|
567
|
+
.. uml::
|
|
568
|
+
|
|
569
|
+
Client -> Controller: **StartRunRequest()**
|
|
570
|
+
alt run is accepted
|
|
571
|
+
Controller -> Client: StartRunResponse(success=True)
|
|
572
|
+
else run is not accepted (e.g. analog computer is busy or in failure mode)
|
|
573
|
+
Controller -> Client: StartRunResponse(success=False, error=<error info>)
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
"""
|
|
577
|
+
#: The secret session ID in which the run should be started. Only required if session management is enabled.
|
|
578
|
+
session: typing.Optional[UUID4]
|
|
579
|
+
#: An ID that should be applied to the run.
|
|
580
|
+
id: UUID4
|
|
581
|
+
#: A :py:class:`pybrid.redac.run.RunConfig` that should be applied to the run.
|
|
582
|
+
config: RunConfig
|
|
583
|
+
#: A :py:class:`pybrid.redac.daq.DAQConfig` that should be applied to the run.
|
|
584
|
+
#: If None, the previous configuration is used.
|
|
585
|
+
daq_config: typing.Optional[DAQConfig]
|
|
586
|
+
|
|
587
|
+
@classmethod
|
|
588
|
+
def from_run(cls, run):
|
|
589
|
+
"""
|
|
590
|
+
Generate a :py:class:`pybrid.redac.protocol.messages.StartRunRequest`
|
|
591
|
+
from a :py:class:`pybrid.redac.run.Run` instance.
|
|
592
|
+
|
|
593
|
+
:param run: A run
|
|
594
|
+
:return: A StartRunRequest instance
|
|
595
|
+
"""
|
|
596
|
+
...
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
class StartRunResponse(Response):
|
|
600
|
+
"""
|
|
601
|
+
A response to a :py:class:`StartRunRequest` indicating whether the run was accepted.
|
|
602
|
+
"""
|
|
603
|
+
response_for = StartRunRequest
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
class CancelRunRequest(Request):
|
|
607
|
+
"""
|
|
608
|
+
A request to cancel an ongoing run.
|
|
609
|
+
Any caused :class:`RunStateChangeMessage` is sent first, before the :class:`CancelRunResponse` is sent.
|
|
610
|
+
|
|
611
|
+
.. uml::
|
|
612
|
+
|
|
613
|
+
Client -> Controller: StartRunRequest(...)
|
|
614
|
+
Controller -> Client: StartRunResponse(success=True)
|
|
615
|
+
...
|
|
616
|
+
Client -> Controller: **CancelRunRequest**(...)
|
|
617
|
+
activate Controller
|
|
618
|
+
note over Controller: cancels run
|
|
619
|
+
Controller -> Client: RunStateChangeMessage(new=ERROR, ...)
|
|
620
|
+
Controller -> Client: CancelRunResponse(success=True)
|
|
621
|
+
deactivate Controller
|
|
622
|
+
"""
|
|
623
|
+
#: The ID of the run to be canceled.
|
|
624
|
+
id_: UUID4
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
class CancelRunResponse(Response):
|
|
628
|
+
"""
|
|
629
|
+
A response to a prior :class:`CancelRunRequest` indicating whether the run was successfully canceled.
|
|
630
|
+
"""
|
|
631
|
+
response_for = CancelRunRequest
|
|
632
|
+
#: The ID of the run requested to be canceled.
|
|
633
|
+
id_: UUID4
|
|
634
|
+
#: Whether the run was successfully canceled and error information if not.
|
|
635
|
+
success: SuccessInfo
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
class RunStateChangeMessage(Message):
|
|
639
|
+
"""
|
|
640
|
+
Notification that an ongoing :class:`Run` changed its :py:class:`RunState`.
|
|
641
|
+
A run is done once it enters :attr:`RunState.DONE` or :attr:`RuntState.ERROR`.
|
|
642
|
+
|
|
643
|
+
.. uml::
|
|
644
|
+
|
|
645
|
+
note over Client: starts a run
|
|
646
|
+
Client -> Controller: StartRunRequest()
|
|
647
|
+
Controller -> Client: StartRunResponse(accepted=True)
|
|
648
|
+
...
|
|
649
|
+
|
|
650
|
+
note over Controller: controls run
|
|
651
|
+
Controller -> Client: **RunStateChangeMessage**(old=QUEUED, new=TAKE_OFF)
|
|
652
|
+
Controller -> Client: **RunStateChangeMessage**(old=TAKE_OFF, new=IC)
|
|
653
|
+
Controller -> Client: **RunStateChangeMessage**(old=IC, new=OP)
|
|
654
|
+
Controller -> Client: **RunStateChangeMessage**(old=OP, new=OP_END)
|
|
655
|
+
Controller -> Client: **RunStateChangeMessage**(old=OP_END, new=DONE)
|
|
656
|
+
...
|
|
657
|
+
note over Client: knows run is done
|
|
658
|
+
Client -> Controller: StartRunRequest()
|
|
659
|
+
"""
|
|
660
|
+
#: ID of the run
|
|
661
|
+
id: UUID4
|
|
662
|
+
#: Current time in microseconds
|
|
663
|
+
t: int
|
|
664
|
+
#: Previous state
|
|
665
|
+
old: RunState
|
|
666
|
+
#: New state
|
|
667
|
+
new: RunState
|
|
668
|
+
#: Any :class:`RunFlags` that the run has triggered (persistent across state changes).
|
|
669
|
+
run_flags: typing.Optional[RunFlags]
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
class RunDataMessage(Message):
|
|
673
|
+
"""
|
|
674
|
+
Notification containing data sampled during a :class:`RunState`
|
|
675
|
+
according to the config set with :class:`SetDAQRequest`.
|
|
676
|
+
All data corresponding to a :class:`RunState` is sent out
|
|
677
|
+
before the state exit is indicated by a respective :class:`RunStateChangeMessage`.
|
|
678
|
+
|
|
679
|
+
.. uml::
|
|
680
|
+
|
|
681
|
+
Client -> Controller: StartRunRequest()
|
|
682
|
+
Controller -> Client: StartRunResponse(accepted=True)
|
|
683
|
+
...
|
|
684
|
+
Controller -> Client: RunStateChangeMessage(old=IC, new=OP, ...)
|
|
685
|
+
activate Controller
|
|
686
|
+
loop until all data in RunState.OP is sent out
|
|
687
|
+
Controller -> Client: **RunDataMessage**(...)
|
|
688
|
+
end
|
|
689
|
+
Controller -> Client: RunStateChangeMessage(old=OP, new=OP_END, ...)
|
|
690
|
+
deactivate Controller
|
|
691
|
+
"""
|
|
692
|
+
#: ID of the run
|
|
693
|
+
id: UUID4
|
|
694
|
+
#: Entity (cluster) that produced the data
|
|
695
|
+
entity: Path
|
|
696
|
+
#: Current state of the run
|
|
697
|
+
# state: RunState
|
|
698
|
+
#: Time of the first datapoint in `data` in microseconds
|
|
699
|
+
# t_0: int
|
|
700
|
+
#: Acquired data by entity path, normalized to [-1,+1]
|
|
701
|
+
data: list[list[float]]
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
class GetOverloadRequest(Request):
|
|
705
|
+
"""
|
|
706
|
+
Request to get the overload status for all or some entities.
|
|
707
|
+
|
|
708
|
+
.. warning::
|
|
709
|
+
This message is intended to be used internally between the hybrid controller and the carrier boards.
|
|
710
|
+
As user, refer to the :attr:`RunStateChangeMessage.run_flags` field.
|
|
711
|
+
"""
|
|
712
|
+
#: Optional path prefix to select only a subset of entities
|
|
713
|
+
entities: typing.Optional[Path]
|
|
714
|
+
|
|
715
|
+
|
|
716
|
+
class GetOverloadResponse(Response):
|
|
717
|
+
"""
|
|
718
|
+
A response to a prior :class:`GetOverloadRequest`, containing all overloaded entities matching the requested prefix.
|
|
719
|
+
|
|
720
|
+
.. warning::
|
|
721
|
+
This message is intended to be used internally between the hybrid controller and the carrier boards.
|
|
722
|
+
As user, refer to the :attr:`RunStateChangeMessage.run_flags` field.
|
|
723
|
+
"""
|
|
724
|
+
response_for = GetOverloadRequest
|
|
725
|
+
#: List of overloaded entities.
|
|
726
|
+
entities: list[Path]
|