pybrid-computing 0.12.0__tar.gz → 0.12.3__tar.gz

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.
Files changed (116) hide show
  1. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/.gitignore +8 -1
  2. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/PKG-INFO +2 -1
  3. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/pyproject.toml +1 -0
  4. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/cli/dac/__init__.py +0 -1
  5. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucipy/computer.py +3 -0
  6. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/dummy_dac.py +0 -2
  7. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/handler/__init__.py +0 -2
  8. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/blocks/backplane_tblock.py +6 -10
  9. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/carrier.py +11 -0
  10. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/control.py +1 -107
  11. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/controller.py +0 -26
  12. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/protocol/serializer.py +58 -8
  13. pybrid_computing-0.12.3/src/pybrid/redac/router.py +184 -0
  14. pybrid_computing-0.12.0/src/pybrid/mock/handler/register_external_entities.py +0 -33
  15. pybrid_computing-0.12.0/src/pybrid/redac/router.py +0 -159
  16. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/README.md +0 -0
  17. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/__init__.py +0 -0
  18. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/analog/__init__.py +0 -0
  19. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/analog/computations.py +0 -0
  20. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/__init__.py +0 -0
  21. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/computer.py +0 -0
  22. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/controller.py +0 -0
  23. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/elements.py +0 -0
  24. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/entities.py +0 -0
  25. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/exceptions.py +0 -0
  26. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/listeners.py +0 -0
  27. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/modules.py +0 -0
  28. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/programs/__init__.py +0 -0
  29. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/programs/base.py +0 -0
  30. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/programs/recl.py +0 -0
  31. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/programs/state.py +0 -0
  32. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/run.py +0 -0
  33. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/serializer.py +0 -0
  34. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/utils.py +0 -0
  35. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/hybrid/validators.py +0 -0
  36. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/proto/__init__.py +0 -0
  37. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/proto/io.py +0 -0
  38. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/proto/main_pb2.py +0 -0
  39. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/proto/main_pb2.pyi +0 -0
  40. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/proto/versioning.py +0 -0
  41. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/result.py +0 -0
  42. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/utils/__init__.py +0 -0
  43. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/utils/addressing.py +0 -0
  44. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/utils/descriptors.py +0 -0
  45. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/utils/imports.py +0 -0
  46. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/base/utils/logging.py +0 -0
  47. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/cli/__init__.py +0 -0
  48. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/cli/base/__init__.py +0 -0
  49. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/cli/base/__main__.py +0 -0
  50. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/cli/base/base.py +0 -0
  51. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/cli/base/commands.py +0 -0
  52. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/cli/base/loader.py +0 -0
  53. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/cli/base/ressources.py +0 -0
  54. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/cli/base/shell.py +0 -0
  55. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/cli/dac/backend.py +0 -0
  56. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucidac/__init__.py +0 -0
  57. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucidac/computer.py +0 -0
  58. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucidac/controller.py +0 -0
  59. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucidac/front_panel.py +0 -0
  60. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucidac/front_plane.py +0 -0
  61. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucidac/protocol/__init__.py +0 -0
  62. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucidac/protocol/serializer.py +0 -0
  63. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucipy/__init__.py +0 -0
  64. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucipy/_compat.py +0 -0
  65. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucipy/circuits.py +0 -0
  66. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucipy/elements.py +0 -0
  67. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/lucipy/helpers.py +0 -0
  68. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/__init__.py +0 -0
  69. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/config.py +0 -0
  70. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/connection.py +0 -0
  71. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/handler/base.py +0 -0
  72. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/handler/calibration.py +0 -0
  73. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/handler/config.py +0 -0
  74. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/handler/extract.py +0 -0
  75. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/handler/reset.py +0 -0
  76. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/handler/start_run.py +0 -0
  77. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/mock/handler/udp_streaming.py +0 -0
  78. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/__init__.py +0 -0
  79. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/blocks/__init__.py +0 -0
  80. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/blocks/block.py +0 -0
  81. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/blocks/cblock.py +0 -0
  82. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/blocks/iblock.py +0 -0
  83. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/blocks/mblock.py +0 -0
  84. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/blocks/shblock.py +0 -0
  85. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/blocks/tblock.py +0 -0
  86. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/blocks/ublock.py +0 -0
  87. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/channel.py +0 -0
  88. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/cluster.py +0 -0
  89. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/computations.py +0 -0
  90. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/computer.py +0 -0
  91. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/connection.py +0 -0
  92. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/data.py +0 -0
  93. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/detect.py +0 -0
  94. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/device.py +0 -0
  95. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/display.py +0 -0
  96. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/elements.py +0 -0
  97. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/entities.py +0 -0
  98. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/modules.py +0 -0
  99. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/monitor.py +0 -0
  100. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/partitioning.py +0 -0
  101. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/protocol/deserializer.py +0 -0
  102. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/protocol/types.py +0 -0
  103. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/protocol/validators.py +0 -0
  104. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/run.py +0 -0
  105. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/session.py +0 -0
  106. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/redac/sync.py +0 -0
  107. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/sim/__init__.py +0 -0
  108. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/sim/computer.py +0 -0
  109. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/sim/config.py +0 -0
  110. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/sim/controller.py +0 -0
  111. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/sim/protocol/__init__.py +0 -0
  112. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/sim/protocol/messages.py +0 -0
  113. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/sim/protocol/serializer.py +0 -0
  114. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/util/__init__.py +0 -0
  115. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/util/reporter.py +0 -0
  116. {pybrid_computing-0.12.0 → pybrid_computing-0.12.3}/src/pybrid/util/updater.py +0 -0
@@ -125,6 +125,7 @@ __pycache__/
125
125
  .Python
126
126
  build/
127
127
  build-temp/
128
+ build_tests/
128
129
  develop-eggs/
129
130
  dist/
130
131
  downloads/
@@ -394,4 +395,10 @@ objects
394
395
  refs
395
396
 
396
397
  # Generated documentation
397
- public/
398
+ public/
399
+
400
+ # Talks developed here for code anchoring
401
+ talks/
402
+ node_modules/
403
+ package.json
404
+ package-lock.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pybrid-computing
3
- Version: 0.12.0
3
+ Version: 0.12.3
4
4
  Summary: Library to interact with analog computers and analog-digital hybrid computers, in particular the LUCIDAC and REDAC computers made by anabrid
5
5
  Project-URL: Homepage, https://anabrid.com/
6
6
  Project-URL: Documentation, https://anabrid.dev/docs/pybrid/
@@ -28,6 +28,7 @@ Requires-Dist: pybrid-computing-native<1.0.0,>=0.1.0
28
28
  Requires-Dist: pydantic>=2.11.9
29
29
  Requires-Dist: pyi>=0.1.0
30
30
  Requires-Dist: pyserial-asyncio>=0.6
31
+ Requires-Dist: reportlab>=4.4.10
31
32
  Requires-Dist: zeroconf>=0.147.2
32
33
  Description-Content-Type: text/markdown
33
34
 
@@ -33,6 +33,7 @@ dependencies = [
33
33
  "prompt-toolkit>=3.0.52",
34
34
  "pyserial-asyncio>=0.6",
35
35
  "pybrid-computing-native>=0.1.0,<1.0.0",
36
+ "reportlab>=4.4.10",
36
37
  ]
37
38
 
38
39
  [project.urls]
@@ -606,7 +606,6 @@ async def proxy(listen: str, port: int, backend: tuple[str, ...], session_timeou
606
606
  for spec in specs:
607
607
  click.echo(f"Connecting to backend {spec.host}:{spec.port}...")
608
608
  proxy_server.add_backend(spec.host, spec.port, stack=spec.stack, carrier=spec.carrier)
609
- proxy_server.map_backends()
610
609
 
611
610
  proxy_server.set_session_timeout(session_timeout)
612
611
  proxy_server.start(listen, port)
@@ -94,6 +94,9 @@ class LucipyWrapper:
94
94
  self._num_devices = len(endpoints)
95
95
  self._device_indices = list(range(self._num_devices))
96
96
 
97
+ def controller(self):
98
+ return self._controller
99
+
97
100
  def create_circuit(self, device_index: int | None = None) -> Circuit:
98
101
  """Create a Circuit scoped to a specific carrier's MAC address.
99
102
 
@@ -89,7 +89,6 @@ class DummyDAC:
89
89
  ExtractHandler,
90
90
  UDPStreamingHandler,
91
91
  StartRunHandler,
92
- RegisterExternalEntitiesHandler,
93
92
  CalibrationHandler,
94
93
  )
95
94
 
@@ -103,7 +102,6 @@ class DummyDAC:
103
102
  pb.MessageV1.EXTRACT_COMMAND_FIELD_NUMBER: ExtractHandler(self),
104
103
  pb.MessageV1.UDP_DATA_STREAMING_COMMAND_FIELD_NUMBER: UDPStreamingHandler(self),
105
104
  pb.MessageV1.START_RUN_COMMAND_FIELD_NUMBER: StartRunHandler(self),
106
- pb.MessageV1.REGISTER_EXTERNAL_ENTITIES_COMMAND_FIELD_NUMBER: RegisterExternalEntitiesHandler(self),
107
105
  calibration_field: CalibrationHandler(self),
108
106
  }
109
107
 
@@ -10,7 +10,6 @@ from pybrid.mock.handler.config import ConfigHandler
10
10
  from pybrid.mock.handler.extract import ExtractHandler
11
11
  from pybrid.mock.handler.udp_streaming import UDPStreamingHandler
12
12
  from pybrid.mock.handler.start_run import StartRunHandler
13
- from pybrid.mock.handler.register_external_entities import RegisterExternalEntitiesHandler
14
13
  from pybrid.mock.handler.calibration import CalibrationHandler
15
14
 
16
15
  __all__ = [
@@ -20,6 +19,5 @@ __all__ = [
20
19
  "ExtractHandler",
21
20
  "UDPStreamingHandler",
22
21
  "StartRunHandler",
23
- "RegisterExternalEntitiesHandler",
24
22
  "CalibrationHandler",
25
23
  ]
@@ -12,20 +12,16 @@ from pybrid.redac.entities import EntityClass, EntityType, Loc
12
12
  @EntityType.register(EntityClass.T_BLOCK_BPL)
13
13
  @dataclass
14
14
  class BackplaneTBlock(FunctionBlock):
15
- muxes: list[int | None] = field(default_factory=lambda: [None] * 8 * 9)
16
-
17
- @staticmethod
18
- def index(dst_sector: int, sector_lane: int) -> int:
19
- assert 0 <= sector_lane < 8
20
- assert 0 <= dst_sector < 9
21
- return dst_sector + sector_lane * 9
15
+ sectors: list[list[int | None]] = field(default_factory=lambda: [[None] * 8 for _ in range(9)])
22
16
 
23
17
  def connect(self, src_sector: int, dst_sector: int, sector_lane: int):
24
18
  assert 0 <= src_sector < 9
25
- self.muxes[BackplaneTBlock.index(dst_sector, sector_lane)] = src_sector
19
+ assert 0 <= dst_sector < 9
20
+ assert 0 <= sector_lane < 8
21
+ self.sectors[dst_sector][sector_lane] = src_sector
26
22
 
27
23
  def source(self, dst_sector: int, sector_lane: int) -> int:
28
- return self.muxes[BackplaneTBlock.index(dst_sector, sector_lane)]
24
+ return self.sectors[dst_sector][sector_lane]
29
25
 
30
26
  def reset(self):
31
- self.muxes = [None] * 8 * 9
27
+ self.sectors = [[None] * 8 for _ in range(9)]
@@ -8,6 +8,7 @@ import logging
8
8
  import warnings
9
9
  from dataclasses import dataclass, field
10
10
  from typing import Optional, TYPE_CHECKING
11
+ from enum import Enum
11
12
 
12
13
  from pybrid.base.hybrid import EntityDoesNotExist
13
14
  from pybrid.redac.blocks import TBlock
@@ -32,6 +33,12 @@ class ADCChannel:
32
33
  #: Probe: relates this ADC channel to an input function (e.g. a problem description, such as an ODE)
33
34
  probe: int = -1
34
35
 
36
+ class FrontPanelIOMode(int, Enum):
37
+ ANALOG_OUT = 0
38
+ ANALOG_IN = 1
39
+ DIGITAL_OUT = 2
40
+ DIGITAL_IN = 3
41
+
35
42
  @dataclass(kw_only=True)
36
43
  class Carrier(Entity):
37
44
  """
@@ -48,8 +55,12 @@ class Carrier(Entity):
48
55
  adc_config: list[Optional[ADCChannel]] = field(default_factory=list)
49
56
 
50
57
  #: ACL Select for analog I/O - note that while not supported in HW, the firmware ignores this.
58
+ #: This is the LUCIDAC implementation.
51
59
  acl_select: Optional[list[str]] = None
52
60
 
61
+ #: Current implementation of the FrontPlane on REDACs (no specific entity)
62
+ front_panel_io: Optional[list[FrontPanelIOMode]] = None
63
+
53
64
  #: List of clusters on the carrier board.
54
65
  clusters: list[Cluster]
55
66
 
@@ -15,10 +15,8 @@ executor when multiple channels run concurrently.
15
15
 
16
16
  import asyncio
17
17
  import logging
18
- import time
19
18
  from concurrent.futures import ThreadPoolExecutor
20
19
  from typing import Callable
21
- from uuid import uuid4
22
20
 
23
21
  import pybrid.base.proto.main_pb2 as pb
24
22
  from pybrid.base.result import Result
@@ -52,16 +50,12 @@ class AsyncControlChannel:
52
50
  def __init__(
53
51
  self,
54
52
  native: "NativeControlChannel",
55
- max_busy_wait: float = 30.0,
56
53
  ):
57
54
  """
58
55
  Args:
59
- native: A connected ``NativeControlChannel`` instance.
60
- max_busy_wait: Maximum time in seconds to wait when a ``busy_response``
61
- is received before raising :class:`TimeoutError`.
56
+ native: A connected ``NativeControlChannel`` instance.
62
57
  """
63
58
  self._native = native
64
- self._max_busy_wait = max_busy_wait
65
59
  self._executor = ThreadPoolExecutor(
66
60
  max_workers=1,
67
61
  thread_name_prefix="ctrl-channel",
@@ -159,71 +153,6 @@ class AsyncControlChannel:
159
153
  """``True`` if the C++ recv thread is active."""
160
154
  return self._native.is_running()
161
155
 
162
- async def send(self, msg: pb.MessageV1) -> None:
163
- """Fire-and-forget send — queues the serialized message for async TCP transmission."""
164
- self._native.send(msg.SerializeToString())
165
-
166
- async def _raw_send_and_recv(
167
- self,
168
- msg: pb.MessageV1,
169
- timeout: float = 5.0,
170
- ) -> pb.MessageV1:
171
- """Low-level send + receive without busy-wait retry logic.
172
-
173
- Factored out from :meth:`send_and_recv` so the busy-wait loop can
174
- call it without infinite recursion.
175
- """
176
- data = msg.SerializeToString()
177
- loop = asyncio.get_running_loop()
178
- response_bytes = await loop.run_in_executor(
179
- self._executor,
180
- self._native.send_and_recv,
181
- data,
182
- timeout,
183
- )
184
- response = pb.MessageV1()
185
- response.ParseFromString(response_bytes)
186
- return response
187
-
188
- async def send_and_recv(
189
- self,
190
- msg: pb.MessageV1,
191
- timeout: float = 5.0,
192
- ) -> pb.MessageV1:
193
- """Send a request and await the matching response.
194
-
195
- If the proxy returns a ``busy_response``, polls with
196
- :class:`pb.PingCommand` every second until the session becomes active,
197
- then re-sends the original message.
198
-
199
- :raises TimeoutError: If the device remains busy longer than
200
- ``max_busy_wait`` seconds.
201
- """
202
- response = await self._raw_send_and_recv(msg, timeout)
203
-
204
- if response.HasField("busy_response"):
205
- logger.info("Device busy, waiting for session to become active...")
206
- start_time = time.monotonic()
207
- while True:
208
- elapsed = time.monotonic() - start_time
209
- if elapsed >= self._max_busy_wait:
210
- logger.warning("Device busy timeout after %.1fs (max_busy_wait=%.1fs)", elapsed, self._max_busy_wait)
211
- raise TimeoutError(
212
- f"Device busy for {elapsed:.1f}s, exceeded max wait "
213
- f"of {self._max_busy_wait}s"
214
- )
215
- await asyncio.sleep(1.0)
216
- ping_msg = self._new_message(pb.PingCommand())
217
- ping_response = await self._raw_send_and_recv(ping_msg, timeout)
218
- if ping_response.HasField("busy_response"):
219
- logger.info("Still waiting... (%.0fs elapsed)", time.monotonic() - start_time)
220
- else:
221
- logger.info("Session active, proceeding.")
222
- break
223
- response = await self._raw_send_and_recv(msg, timeout)
224
-
225
- return response
226
-
227
156
  def register_callback(self, field_number: int, callback: Callable) -> None:
228
157
  """Register a callback for a specific message field number.
229
158
 
@@ -255,24 +184,6 @@ class AsyncControlChannel:
255
184
  def unregister_callback(self, field_number: int) -> None:
256
185
  self._native.unregister_callback(field_number)
257
186
 
258
- @staticmethod
259
- def _new_message(body) -> pb.MessageV1:
260
- """Build a :class:`pb.MessageV1` wrapping *body* in the correct oneof field."""
261
- msg = pb.MessageV1(id=str(uuid4()))
262
- fields = msg.DESCRIPTOR.oneofs_by_name["kind"].fields
263
- for field in fields:
264
- if field.message_type == body.DESCRIPTOR:
265
- getattr(msg, field.name).CopyFrom(body)
266
- return msg
267
- raise ValueError(f"No MessageV1 oneof field for {type(body).__name__}")
268
-
269
- @staticmethod
270
- def _to_result(response: pb.MessageV1) -> Result:
271
- """Convert a response message to a :class:`~pybrid.base.result.Result`."""
272
- if response.HasField("error_message"):
273
- return Result.failure(response.error_message.description)
274
- return Result.success()
275
-
276
187
  async def extract(
277
188
  self,
278
189
  path: "str | Path | None" = None,
@@ -360,23 +271,6 @@ class AsyncControlChannel:
360
271
  except RuntimeError as e:
361
272
  return Result.failure(str(e))
362
273
 
363
- async def register_external_entities(
364
- self,
365
- entities: dict[str, tuple[int, int, int, int]],
366
- timeout: float = 5.0,
367
- ) -> Result:
368
- """Send a ``RegisterExternalEntitiesCommand`` with the given entity map.
369
-
370
- No C++ convenience method exists — builds protobuf in Python.
371
- """
372
- cmd = pb.RegisterExternalEntitiesCommand()
373
- for mac, ip_octets in entities.items():
374
- addr = pb.Address(data=bytes(ip_octets))
375
- cmd.entities[mac].CopyFrom(addr)
376
- msg = self._new_message(cmd)
377
- response = await self.send_and_recv(msg, timeout)
378
- return self._to_result(response)
379
-
380
274
  async def reset(
381
275
  self,
382
276
  *,
@@ -309,32 +309,6 @@ class Controller(BaseController):
309
309
  result = await session.execute()
310
310
  return result[0] if result else run
311
311
 
312
- async def register_external_entities(self):
313
- """Distribute carrier MACs and IP addresses to every connected device.
314
-
315
- Builds a map of ``{carrier_mac: ip_octets}`` from all carriers
316
- in :attr:`computer` and sends a ``RegisterExternalEntitiesCommand`` to
317
- each unique backend connection.
318
- """
319
-
320
- # TODO: reinstate once implemented in the firmware
321
- logger.warning("RegisterExternalEntities command skipped...")
322
- return
323
-
324
- if not self.computer.carriers:
325
- return
326
-
327
- entities: dict[str, tuple[int, int, int, int]] = {}
328
- for carrier in self.computer.carriers:
329
- conn = self.connection_manager.get_connection(carrier.path)
330
- host = conn.control.remote_host
331
- octets = tuple(int(x) for x in host.split("."))
332
- entities[str(carrier.path)] = octets
333
-
334
- for conn in self.connection_manager.get_unique_connections():
335
- result = await conn.control.register_external_entities(entities)
336
- result.raise_on_error()
337
-
338
312
  async def reset(self, keep_calibration: bool = True, sync: bool = True):
339
313
  """Reset all carrier boards to initial configuration."""
340
314
  for conn in self.connection_manager.get_unique_connections():
@@ -14,7 +14,7 @@ from pybrid.base.proto import main_pb2 as pb
14
14
  from pybrid.redac.blocks import (
15
15
  CBlock, MMulBlock, TBlock, MIntBlock, UBlock, IBlock, MMDRBlock
16
16
  )
17
- from pybrid.redac.carrier import Carrier, ADCChannel
17
+ from pybrid.redac.carrier import Carrier, ADCChannel, FrontPanelIOMode
18
18
  from pybrid.redac.blocks.backplane_tblock import BackplaneTBlock
19
19
  from pybrid.redac.cluster import Cluster
20
20
  from pybrid.redac.device import Device
@@ -74,6 +74,22 @@ class REDACSerializer(Serializer):
74
74
  acl_select.append(pb.PortConfig.AclState.EXTERNAL if \
75
75
  interface.lower() == "external" else pb.PortConfig.AclState.INTERNAL)
76
76
 
77
+ # Front Panel ports for mREDAC
78
+ if entity.front_panel_io:
79
+ front_panel_io_config = self.cc.new_config(entity).front_panel_io_config
80
+
81
+ for mode in entity.front_panel_io:
82
+ if mode == FrontPanelIOMode.ANALOG_OUT:
83
+ front_panel_io_config.io_modes.append(pb.FrontPanelIOConfig.FrontPanelIOMode.ANALOG_OUT)
84
+ elif mode == FrontPanelIOMode.ANALOG_IN:
85
+ front_panel_io_config.io_modes.append(pb.FrontPanelIOConfig.FrontPanelIOMode.ANALOG_IN)
86
+ elif mode == FrontPanelIOMode.DIGITAL_OUT:
87
+ front_panel_io_config.io_modes.append(pb.FrontPanelIOConfig.FrontPanelIOMode.DIGITAL_OUT)
88
+ elif mode == FrontPanelIOMode.DIGITAL_IN:
89
+ front_panel_io_config.io_modes.append(pb.FrontPanelIOConfig.FrontPanelIOMode.DIGITAL_IN)
90
+ else:
91
+ logger.warning(f"Unknown FrontPanelIO value: {mode}")
92
+
77
93
  @_serialize_configuration.register
78
94
  def _(self, entity: CBlock):
79
95
  coef_config = self.cc.new_config(entity).coef_config
@@ -160,10 +176,15 @@ class REDACSerializer(Serializer):
160
176
  @_serialize_configuration.register
161
177
  def _(self, entity: BackplaneTBlock):
162
178
  switch_config = self.cc.new_config(entity).bpl_switch_config
163
- for idx, state in enumerate(entity.muxes):
164
- if state is None:
165
- continue
166
- switch_config.muxes.append(pb.Mux(state=state, index=idx))
179
+ for sector_out, lane_ins in enumerate(entity.sectors):
180
+ for lane_in, sector_in in enumerate(lane_ins):
181
+ if lane_ins is None:
182
+ continue
183
+
184
+ if sector_in == 8:
185
+ sector_in = sector_out
186
+
187
+ switch_config.muxes.append(pb.Mux(state=sector_in, index=9 * lane_in + sector_out))
167
188
 
168
189
  if len(switch_config.muxes) == 0:
169
190
  self.cc.pop_config()
@@ -622,15 +643,25 @@ class REDACDeserializer(Deserializer):
622
643
  def _(self, config: pb.BPLSwitchConfig):
623
644
  """Deserialize switch configuration and apply to TBlock."""
624
645
  entity_path = Path.parse(self._current_full_config.entity.path)
625
- entity: TBlock = self.computer.get_entity(entity_path)
646
+ entity: BackplaneTBlock = self.computer.get_entity(entity_path)
626
647
 
627
648
  is_numerated = all(obj.index == 0 for obj in config.muxes)
649
+
650
+ def connect(in_sector, idx):
651
+ lane = idx // 9
652
+ out_sector = idx % 9
653
+
654
+ if out_sector == in_sector:
655
+ in_sector = 8
656
+
657
+ entity.connect(in_sector, out_sector, lane)
658
+
628
659
  if is_numerated:
629
660
  for idx, mux in enumerate(config.muxes):
630
- entity.muxes[idx] = mux.state
661
+ connect(mux.state, idx)
631
662
  else:
632
663
  for mux in config.muxes:
633
- entity.muxes[mux.index] = mux.state
664
+ connect(mux.state, mux.index)
634
665
 
635
666
 
636
667
  @_deserialize_configuration.register
@@ -647,4 +678,23 @@ class REDACDeserializer(Deserializer):
647
678
  acl_select.append("internal")
648
679
  entity.acl_select = acl_select
649
680
 
681
+ @_deserialize_configuration.register
682
+ def _(self, config: pb.FrontPanelIOConfig):
683
+ """Deserialize front panel config from REDACs and apply to Carrier."""
684
+ entity_path = Path.parse(self._current_full_config.entity.path)
685
+ entity = self.computer.get_entity(entity_path)
686
+
687
+ io_modes = []
688
+ for mode in config.io_modes:
689
+ if mode == pb.FrontPanelIOConfig.ANALOG_OUT:
690
+ io_modes.append(FrontPanelIOMode.ANALOG_OUT)
691
+ elif mode == pb.FrontPanelIOConfig.ANALOG_IN:
692
+ io_modes.append(FrontPanelIOMode.ANALOG_IN)
693
+ elif mode == pb.FrontPanelIOConfig.DIGITAL_OUT:
694
+ io_modes.append(FrontPanelIOMode.DIGITAL_OUT)
695
+ elif mode == pb.FrontPanelIOConfig.DIGITAL_IN:
696
+ io_modes.append(FrontPanelIOMode.DIGITAL_IN)
697
+ else:
698
+ logger.warning(f"Unknown FrontPanelIOValue: {mode}")
650
699
 
700
+ entity.io_modes = io_modes
@@ -0,0 +1,184 @@
1
+ import typing
2
+ from typing import Dict, Set, List
3
+
4
+ from pybrid.redac.blocks import TBlock
5
+ from pybrid.redac.blocks.backplane_tblock import BackplaneTBlock
6
+ from pybrid.redac.carrier import Carrier
7
+ from pybrid.redac.entities import Path, Loc
8
+
9
+
10
+ class RoutingException(Exception):
11
+ def __init__(self, message: str = ""):
12
+ self.message = message
13
+
14
+ class Routing:
15
+ carrier_t_blocks: Dict[Loc, TBlock] = {}
16
+ bpl_t_blocks: Dict[Loc, Dict[int, BackplaneTBlock]] = {}
17
+ dummy = TBlock(Path(("dummy", "T")))
18
+ bpl_dummy = BackplaneTBlock(Path(("dummy", "T")))
19
+
20
+ def add_t_block(self, t_block: TBlock):
21
+ loc = t_block.loc()
22
+ if loc.is_carrier():
23
+ self.carrier_t_blocks[loc.carrier()] = t_block
24
+
25
+ def add_t_bpl_block(self, idx: int, t_block: BackplaneTBlock):
26
+ loc = t_block.loc()
27
+ self.bpl_t_blocks.setdefault(loc.stack(), {})[idx] = t_block
28
+
29
+ def add_carrier(self, carrier: Carrier):
30
+ if carrier.tblock:
31
+ self.add_t_block(carrier.tblock)
32
+ if carrier.st0block:
33
+ self.add_t_bpl_block(0, carrier.st0block)
34
+ if carrier.st1block:
35
+ self.add_t_bpl_block(1, carrier.st1block)
36
+ if carrier.st2block:
37
+ self.add_t_bpl_block(2, carrier.st2block)
38
+
39
+ def find_carrier_t_block(self, loc: Loc) -> TBlock:
40
+ return self.carrier_t_blocks.get(loc.carrier(), self.dummy)
41
+
42
+ def find_bpl_t_block(self, loc: Loc, idx: int) -> BackplaneTBlock:
43
+ return self.bpl_t_blocks.get(loc.stack(), {}).get(idx, self.bpl_dummy)
44
+
45
+ class Router(Routing):
46
+
47
+ # route two cluster
48
+ def _route_cluster(self, src: Loc, dst: Loc):
49
+ assert src.carrier() == dst.carrier()
50
+
51
+ if src.lane_id() < 8:
52
+ if src != dst:
53
+ raise RoutingException("First 8 lanes only to same indices routable")
54
+ return None
55
+
56
+ output_sector_lane = src.lane_id() - 8
57
+ input_sector_lane = dst.lane_id() - 8
58
+
59
+ if output_sector_lane != input_sector_lane:
60
+ raise RoutingException("Connections inside cluster are only allowed between same lane indices!")
61
+
62
+ tblock = self.find_carrier_t_block(dst)
63
+ return tblock.connect(src.cluster_id() + 1, dst.cluster_id() + 1, input_sector_lane)
64
+
65
+ def _route_carrier(self, src: Loc, dst: Loc):
66
+ if src.lane_id() != dst.lane_id():
67
+ raise RoutingException("Connections between clusters are only allowed between same lane indices!")
68
+
69
+ if 0 <= src.lane_id() < 8:
70
+ raise RoutingException("Connections between clusters are only allowed between 8 to 31 lane indices!")
71
+
72
+ t_block = self.find_carrier_t_block(dst)
73
+ t_block.connect(src.cluster_id() + 1, dst.cluster_id() + 1, dst.lane_id() - 8)
74
+
75
+ def _route_stack(self, src: Loc, dst: Loc):
76
+ if src.lane_id() != dst.lane_id():
77
+ raise RoutingException("Connections between carrier are only allowed between same lane indices!")
78
+
79
+ if not (8 <= src.lane_id() < 32):
80
+ raise RoutingException("Connections between carrier are only allowed between 8 to 31 lane indices!")
81
+
82
+ offset_lane_id = src.lane_id() - 8
83
+ partition = offset_lane_id // 8
84
+
85
+ output_t_block = self.find_carrier_t_block(src)
86
+ input_t_block = self.find_carrier_t_block(dst)
87
+
88
+ #sector 0 is connect to backplane
89
+ #cluster 0-2 are sector 1-3
90
+ output_t_block.connect(src.cluster_id() + 1, 0, offset_lane_id)
91
+ input_t_block.connect(0, dst.cluster_id() + 1, offset_lane_id)
92
+
93
+ mod_dst_lane = dst.lane_id() % 8
94
+ if src.stack() != dst.stack():
95
+ src_bpl_t_block = self.find_bpl_t_block(src, partition)
96
+ dst_bpl_t_block = self.find_bpl_t_block(dst, partition)
97
+
98
+ exit_sector = 7 if src.stack_id() < dst.stack_id() else 8
99
+ entry_sector = 8 if src.stack_id() < dst.stack_id() else 7
100
+
101
+ src_bpl_t_block.connect(src.carrier_id(), exit_sector, mod_dst_lane)
102
+ dir = 1 if src.stack_id() < dst.stack_id() else -1
103
+ for btw_stack_idx in range(src.stack_id() + dir, dst.stack_id(), dir):
104
+ btw_bpl_t_block = self.find_bpl_t_block(Loc.new_stack(btw_stack_idx), partition)
105
+ btw_bpl_t_block.connect(entry_sector, exit_sector, mod_dst_lane)
106
+
107
+ dst_bpl_t_block.connect(entry_sector, dst.carrier_id(), mod_dst_lane)
108
+ else:
109
+ bpl_t_block = self.find_bpl_t_block(src, partition)
110
+ bpl_t_block.connect(src.carrier_id(), dst.carrier_id(), mod_dst_lane)
111
+
112
+
113
+ def route(self, src: Loc, dst: Loc):
114
+ assert src.carrier_id() < 7 and dst.carrier_id() < 7
115
+ assert src.cluster_id() < 3 and dst.cluster_id() < 3
116
+ assert dst.lane_id() < 32 and src.lane_id() < 32
117
+ if src.cluster() == dst.cluster():
118
+ return self._route_cluster(src, dst)
119
+
120
+ if src.carrier() == dst.carrier():
121
+ return self._route_carrier(src, dst)
122
+
123
+ return self._route_stack(src, dst)
124
+
125
+
126
+ class Tracer(Routing):
127
+
128
+ #loc is a lane in a cluster after a the t block searching for the coef source
129
+ #loc here is stack / carrier / cluster_id / lane
130
+ def find_coef(self, loc: Loc) -> typing.Optional[Loc]:
131
+
132
+ visited = set()
133
+
134
+ if loc.lane_id() < 8:
135
+ return loc
136
+
137
+ # only here we bump cluster_id because we need 0 for backplane
138
+ loc = loc.carrier() / (loc.cluster_id() + 1) / loc.lane_id()
139
+
140
+ while True:
141
+ if loc.lane_id() < 8:
142
+ return loc
143
+
144
+ if loc in visited:
145
+ return None
146
+ visited.add(loc)
147
+
148
+ carrier_t_block = self.find_carrier_t_block(loc.carrier())
149
+
150
+ # source is either the result of coef or from bpl t block
151
+ tblock_lane = loc.lane_id() - 8
152
+ source = carrier_t_block.source(loc.cluster_id(), tblock_lane)
153
+ if source is None or not (0 <= source < 4):
154
+ return None
155
+
156
+ if source != 0:
157
+ return loc.carrier() / (source - 1) / loc.lane_id()
158
+
159
+ # here source is from backplane
160
+ current_stack = loc.stack()
161
+ prev_carrier_id = loc.carrier_id()
162
+
163
+ while True:
164
+ bpl_t_block = self.find_bpl_t_block(current_stack, tblock_lane // 8)
165
+ prev_carrier_id = bpl_t_block.source(prev_carrier_id, tblock_lane % 8)
166
+
167
+ if prev_carrier_id is None:
168
+ return None
169
+ elif prev_carrier_id == 8:
170
+ current_stack = Loc.new_stack(current_stack.stack_id() - 1)
171
+ prev_carrier_id = 7
172
+ elif prev_carrier_id == 7:
173
+ current_stack = Loc.new_stack(current_stack.stack_id() + 1)
174
+ prev_carrier_id = 8
175
+ else:
176
+ break
177
+
178
+ if 0 <= prev_carrier_id <= 6:
179
+ # 0 because backpanel here!
180
+ loc = current_stack / prev_carrier_id / 0 / loc.lane_id()
181
+ continue
182
+ else:
183
+ #external
184
+ raise RoutingException("Connections between stacks not implemented yet!")
@@ -1,33 +0,0 @@
1
- # Copyright (c) 2022-2025 anabrid GmbH
2
- # Contact: https://www.anabrid.com/licensing/
3
- # SPDX-License-Identifier: MIT OR GPL-2.0-or-later
4
-
5
- """Handler for the register external entities command."""
6
-
7
- from typing import TYPE_CHECKING
8
-
9
- import pybrid.base.proto.main_pb2 as pb
10
- from pybrid.mock.handler.base import BaseHandler
11
-
12
- if TYPE_CHECKING:
13
- from pybrid.mock.connection import ClientConnection
14
-
15
-
16
- class RegisterExternalEntitiesHandler(BaseHandler):
17
- """Handler for external entities registration commands.
18
-
19
- Stores the received entity map for test inspection but performs no
20
- real coordination (DummyDAC doesn't route cross-carrier signals).
21
- """
22
-
23
- last_entities: dict[str, bytes]
24
-
25
- def __init__(self, server):
26
- super().__init__(server)
27
- self.last_entities = {}
28
-
29
- async def handle(
30
- self, cmd: pb.RegisterExternalEntitiesCommand, connection: "ClientConnection"
31
- ) -> pb.SuccessMessage:
32
- self.last_entities = {k: v.data for k, v in cmd.entities.items()}
33
- return pb.SuccessMessage()
@@ -1,159 +0,0 @@
1
- import typing
2
- from typing import Dict, Set, List
3
-
4
- from pybrid.redac.blocks import TBlock
5
- from pybrid.redac.blocks.backplane_tblock import BackplaneTBlock
6
- from pybrid.redac.carrier import Carrier
7
- from pybrid.redac.entities import Path, Loc
8
-
9
-
10
- class RoutingException(Exception):
11
- def __init__(self, message: str = ""):
12
- self.message = message
13
-
14
- class Routing:
15
- carrier_t_blocks: Dict[Loc, TBlock] = {}
16
- bpl_t_blocks: Dict[Loc, Dict[int, BackplaneTBlock]] = {}
17
- dummy = TBlock(Path(("dummy", "T")))
18
- bpl_dummy = BackplaneTBlock(Path(("dummy", "T")))
19
-
20
- def add_t_block(self, t_block: TBlock):
21
- loc = t_block.loc()
22
- if loc.is_carrier():
23
- self.carrier_t_blocks[loc.carrier()] = t_block
24
-
25
- def add_t_bpl_block(self, idx: int, t_block: BackplaneTBlock):
26
- loc = t_block.loc()
27
- self.bpl_t_blocks.setdefault(loc.stack(), {})[idx] = t_block
28
-
29
- def add_carrier(self, carrier: Carrier):
30
- if carrier.tblock:
31
- self.add_t_block(carrier.tblock)
32
- if carrier.st0block:
33
- self.add_t_bpl_block(0, carrier.st0block)
34
- if carrier.st1block:
35
- self.add_t_bpl_block(1, carrier.st1block)
36
- if carrier.st2block:
37
- self.add_t_bpl_block(2, carrier.st2block)
38
-
39
- def find_carrier_t_block(self, loc: Loc) -> TBlock:
40
- return self.carrier_t_blocks.get(loc.carrier(), self.dummy)
41
-
42
- def find_bpl_t_block(self, loc: Loc, idx: int) -> BackplaneTBlock:
43
- return self.bpl_t_blocks.get(loc.stack(), {}).get(idx, self.bpl_dummy)
44
-
45
- class Router(Routing):
46
-
47
- # route two cluster
48
- def _route_cluster(self, output: Loc, input: Loc):
49
- assert output.carrier() == input.carrier()
50
-
51
- if output.lane_id() < 8:
52
- if output != input:
53
- raise RoutingException("First 8 lanes only to same indices routable")
54
- return None
55
-
56
- output_sector_lane = output.lane_id() - 8
57
- input_sector_lane = input.lane_id() - 8
58
-
59
- if output_sector_lane != input_sector_lane:
60
- raise RoutingException("Connections inside cluster only allowed between same lane indices!")
61
-
62
- tblock = self.find_carrier_t_block(input)
63
- return tblock.connect(output.cluster_id() + 1, input.cluster_id() + 1, input_sector_lane)
64
-
65
- def _route_carrier(self, output: Loc, input: Loc):
66
- if output.lane_id() != input.lane_id():
67
- raise RoutingException("Connections between clusters only allowed between same lane indices!")
68
-
69
- if 0 <= output.lane_id() < 8:
70
- raise RoutingException("Connections between clusters only allowed between 8 to 31 lane indices!")
71
-
72
- t_block = self.find_carrier_t_block(input)
73
- t_block.connect(output.cluster_id() + 1, input.cluster_id() + 1, input.lane_id() - 8)
74
-
75
- def _route_stack(self, output: Loc, input: Loc):
76
- if output.lane_id() != input.lane_id():
77
- raise RoutingException("Connections between carrier only allowed between same lane indices!")
78
-
79
- if not (8 <= output.lane_id() < 32 ):
80
- raise RoutingException("Connections between carrier only allowed between 8 to 31 lane indices!")
81
-
82
- offset_lane_id = output.lane_id() - 8
83
- partition = offset_lane_id // 8
84
-
85
- output_t_block = self.find_carrier_t_block(output)
86
- input_t_block = self.find_carrier_t_block(input)
87
-
88
- #index 0 is connect to backplane
89
- #cluster 0-2 are sector 1-3
90
- output_t_block.connect(output.cluster_id() + 1, 0, offset_lane_id)
91
- input_t_block.connect(0, input.cluster_id() + 1, offset_lane_id)
92
-
93
- bpl_t_block = self.find_bpl_t_block(output, partition)
94
- bpl_t_block.connect(output.carrier_id(), input.carrier_id(), input.lane_id() % 8)
95
-
96
-
97
- def route(self, output: Loc, input: Loc):
98
- assert output.stack_id() < 2 and input.stack_id() < 2
99
- assert output.carrier_id() < 7 and input.carrier_id() < 7
100
- assert output.cluster_id() < 3 and input.cluster_id() < 3
101
- assert input.lane_id() < 32 and output.lane_id() < 32
102
- if output.cluster() == input.cluster():
103
- return self._route_cluster(output, input)
104
-
105
- if output.carrier() == input.carrier():
106
- return self._route_carrier(output, input)
107
-
108
- if output.stack() == input.stack():
109
- return self._route_stack(output, input)
110
-
111
- raise RoutingException("Connections between stacks not implemented yet!")
112
-
113
-
114
- class Tracer(Routing):
115
-
116
- #loc is a lane in a cluster after a the t block searching for the coef source
117
- #loc here is stack / carrier / cluster_id / lane
118
- def find_coef(self, loc: Loc) -> typing.Optional[Loc]:
119
-
120
- visited = set()
121
-
122
- if loc.lane_id() < 8:
123
- return loc
124
-
125
- # only here we bump cluster_id because we need 0 for backplane
126
- loc = loc.carrier() / (loc.cluster_id() + 1) / loc.lane_id()
127
-
128
- while True:
129
- if loc.lane_id() < 8:
130
- return loc
131
-
132
- if loc in visited:
133
- return None
134
- visited.add(loc)
135
-
136
- carrier_t_block = self.find_carrier_t_block(loc.carrier())
137
-
138
- # source is either the result of coef or from bpl t block
139
- tblock_lane = loc.lane_id() - 8
140
- source = carrier_t_block.source(loc.cluster_id(), tblock_lane)
141
- if source is None or not (0 <= source < 4):
142
- return None
143
-
144
- if source != 0:
145
- return loc.carrier() / (source - 1) / loc.lane_id()
146
-
147
- # here source is from backpanel
148
- bpl_t_block = self.find_bpl_t_block(loc, tblock_lane // 8)
149
- prev_carrier_id = bpl_t_block.source(loc.carrier_id(), tblock_lane % 8)
150
- if prev_carrier_id is None:
151
- return None
152
-
153
- if 0 <= prev_carrier_id <= 6:
154
- # 0 because backpanel here!
155
- loc = loc.stack() / prev_carrier_id / 0 / loc.lane_id()
156
- continue
157
- else:
158
- #external
159
- raise RoutingException("Connections between stacks not implemented yet!")