ekfsm 0.11.0b1.post3__py3-none-any.whl → 0.13.0a160.post4__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.
Potentially problematic release.
This version of ekfsm might be problematic. Click here for more details.
- ekfsm/boards/oem/ekf/ccu.yaml +2 -0
- ekfsm/boards/oem/ekf/se5-club.yaml +41 -0
- ekfsm/boards/oem/ekf/sn4-djembe.yaml +41 -0
- ekfsm/boards/oem/ekf/sq3-quartet.yaml +41 -0
- ekfsm/boards/oem/hitron/{hdrc-300.yaml → hdrc-300s.yaml} +6 -2
- ekfsm/cli.py +6 -1
- ekfsm/config.py +1 -0
- ekfsm/core/components.py +19 -7
- ekfsm/core/utils.py +26 -11
- ekfsm/devices/__init__.py +4 -3
- ekfsm/devices/{hwmon.py → coretemp.py} +8 -6
- ekfsm/devices/eeprom.py +41 -4
- ekfsm/devices/ekf_ccu_uc.py +68 -2
- ekfsm/devices/ekf_sur_led.py +2 -2
- ekfsm/devices/generic.py +20 -15
- ekfsm/devices/gpio.py +8 -7
- ekfsm/devices/iio_thermal_humidity.py +2 -2
- ekfsm/devices/imu.py +14 -0
- ekfsm/devices/mux.py +3 -3
- ekfsm/devices/pmbus.py +196 -14
- ekfsm/devices/smbus.py +24 -0
- ekfsm/simctrl.py +5 -27
- ekfsm/system.py +46 -19
- ekfsm-0.13.0a160.post4.dist-info/METADATA +174 -0
- ekfsm-0.13.0a160.post4.dist-info/RECORD +44 -0
- ekfsm-0.11.0b1.post3.dist-info/METADATA +0 -86
- ekfsm-0.11.0b1.post3.dist-info/RECORD +0 -39
- {ekfsm-0.11.0b1.post3.dist-info → ekfsm-0.13.0a160.post4.dist-info}/WHEEL +0 -0
- {ekfsm-0.11.0b1.post3.dist-info → ekfsm-0.13.0a160.post4.dist-info}/entry_points.txt +0 -0
ekfsm/devices/gpio.py
CHANGED
|
@@ -8,7 +8,7 @@ from gpiod.chip import LineSettings
|
|
|
8
8
|
from gpiod.line import Direction, Value
|
|
9
9
|
from more_itertools import first_true
|
|
10
10
|
|
|
11
|
-
from ekfsm.core.components import
|
|
11
|
+
from ekfsm.core.components import SysTree
|
|
12
12
|
from ekfsm.exceptions import GPIOError
|
|
13
13
|
from ekfsm.log import ekfsm_logger
|
|
14
14
|
|
|
@@ -55,7 +55,7 @@ class GPIO(Device):
|
|
|
55
55
|
def __init__(
|
|
56
56
|
self,
|
|
57
57
|
name: str,
|
|
58
|
-
parent:
|
|
58
|
+
parent: SysTree | None = None,
|
|
59
59
|
*args,
|
|
60
60
|
**kwargs,
|
|
61
61
|
):
|
|
@@ -83,7 +83,7 @@ class GPIO(Device):
|
|
|
83
83
|
|
|
84
84
|
def _find_gpio_dev(
|
|
85
85
|
self,
|
|
86
|
-
parent:
|
|
86
|
+
parent: SysTree | None = None,
|
|
87
87
|
*args,
|
|
88
88
|
**kwargs,
|
|
89
89
|
) -> tuple[int, int]:
|
|
@@ -186,7 +186,7 @@ class GPIOExpander(GPIO):
|
|
|
186
186
|
def __init__(
|
|
187
187
|
self,
|
|
188
188
|
name: str,
|
|
189
|
-
parent:
|
|
189
|
+
parent: SysTree | None,
|
|
190
190
|
*args,
|
|
191
191
|
**kwargs,
|
|
192
192
|
):
|
|
@@ -203,7 +203,7 @@ class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
|
|
|
203
203
|
def __init__(
|
|
204
204
|
self,
|
|
205
205
|
name: str,
|
|
206
|
-
parent:
|
|
206
|
+
parent: SysTree | None,
|
|
207
207
|
*args,
|
|
208
208
|
**kwargs,
|
|
209
209
|
):
|
|
@@ -212,10 +212,11 @@ class EKFIdentificationIOExpander(GPIOExpander, ProbeableDevice):
|
|
|
212
212
|
def probe(self, *args, **kwargs) -> bool:
|
|
213
213
|
from ekfsm.core import HwModule
|
|
214
214
|
|
|
215
|
-
assert isinstance(self.
|
|
215
|
+
assert isinstance(self.hw_module, HwModule)
|
|
216
216
|
id, _ = self.read_board_id_rev()
|
|
217
|
+
self.logger.debug(f"Probing EKFIdentificationIOExpander: {id}")
|
|
217
218
|
|
|
218
|
-
return self.
|
|
219
|
+
return self.hw_module.id == id
|
|
219
220
|
|
|
220
221
|
def read_board_id_rev(self) -> tuple[int, int]:
|
|
221
222
|
for pin in range(6, 8):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
|
|
3
|
-
from ekfsm.core.components import
|
|
3
|
+
from ekfsm.core.components import SysTree
|
|
4
4
|
from ekfsm.log import ekfsm_logger
|
|
5
5
|
|
|
6
6
|
from ..core.sysfs import SysFSDevice
|
|
@@ -18,7 +18,7 @@ class IIOThermalHumidity(Device):
|
|
|
18
18
|
def __init__(
|
|
19
19
|
self,
|
|
20
20
|
name: str,
|
|
21
|
-
parent:
|
|
21
|
+
parent: SysTree | None = None,
|
|
22
22
|
children: list[Device] | None = None,
|
|
23
23
|
*args,
|
|
24
24
|
**kwargs,
|
ekfsm/devices/imu.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class ImuSample:
|
|
2
|
+
"""
|
|
3
|
+
Class to store IMU data sample
|
|
4
|
+
|
|
5
|
+
* accel: list[float] - Accelerometer data in m/s^2, [x, y, z]
|
|
6
|
+
* gyro: list[float] - Gyroscope data in degrees/s, [x, y, z]
|
|
7
|
+
* lost: bool - True if data was lost before that sample
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, accel: list[float], gyro: list[float], lost: bool):
|
|
12
|
+
self.accel = accel
|
|
13
|
+
self.gyro = gyro
|
|
14
|
+
self.lost = lost
|
ekfsm/devices/mux.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from ekfsm.core.components import
|
|
1
|
+
from ekfsm.core.components import SysTree
|
|
2
2
|
|
|
3
3
|
from ..core.sysfs import SysFSDevice
|
|
4
4
|
from .generic import Device
|
|
@@ -9,7 +9,7 @@ class MuxChannel(Device):
|
|
|
9
9
|
self,
|
|
10
10
|
name: str,
|
|
11
11
|
channel_id: int,
|
|
12
|
-
parent:
|
|
12
|
+
parent: "I2CMux",
|
|
13
13
|
children: list[Device] | None = None,
|
|
14
14
|
*args,
|
|
15
15
|
**kwargs,
|
|
@@ -28,7 +28,7 @@ class I2CMux(Device):
|
|
|
28
28
|
def __init__(
|
|
29
29
|
self,
|
|
30
30
|
name: str,
|
|
31
|
-
parent:
|
|
31
|
+
parent: SysTree | None = None,
|
|
32
32
|
children: list[MuxChannel] | None = None,
|
|
33
33
|
*args,
|
|
34
34
|
**kwargs,
|
ekfsm/devices/pmbus.py
CHANGED
|
@@ -1,18 +1,100 @@
|
|
|
1
|
+
from enum import IntFlag
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
|
|
3
|
-
from ekfsm.core.components import
|
|
4
|
+
from ekfsm.core.components import SysTree
|
|
4
5
|
|
|
5
|
-
from ..core.sysfs import SysFSDevice
|
|
6
|
+
from ..core.sysfs import SysFSDevice, sysfs_root
|
|
6
7
|
|
|
7
8
|
from .generic import Device
|
|
8
9
|
from ..core.probe import ProbeableDevice
|
|
9
10
|
|
|
11
|
+
from time import sleep
|
|
12
|
+
from functools import wraps
|
|
13
|
+
from ekfsm.log import ekfsm_logger
|
|
14
|
+
from threading import Lock
|
|
15
|
+
|
|
16
|
+
__all__ = ["PsuStatus", "PmBus", "retry"]
|
|
17
|
+
|
|
18
|
+
logger = ekfsm_logger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def retry(max_attempts=5, delay=0.5):
|
|
22
|
+
"""
|
|
23
|
+
Retry decorator.
|
|
24
|
+
|
|
25
|
+
Decorator that retries a function a number of times before giving up.
|
|
26
|
+
|
|
27
|
+
This is useful for functions that may fail due to transient errors.
|
|
28
|
+
|
|
29
|
+
Note
|
|
30
|
+
----
|
|
31
|
+
This is needed for certain PMBus commands that may fail due to transient errors
|
|
32
|
+
because page switching timing is not effectively handled by older kernel versions.
|
|
33
|
+
|
|
34
|
+
Important
|
|
35
|
+
---------
|
|
36
|
+
This decorator is thread-safe, meaning a read attempt is atomic and cannot
|
|
37
|
+
be interupted by scheduler.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
max_attempts
|
|
42
|
+
The maximum number of attempts before giving up.
|
|
43
|
+
delay
|
|
44
|
+
The delay in seconds between attempts.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
lock = Lock()
|
|
48
|
+
|
|
49
|
+
def decorator(func):
|
|
50
|
+
@wraps(func)
|
|
51
|
+
def wrapper(*args, **kwargs):
|
|
52
|
+
attempts = 0
|
|
53
|
+
while attempts < max_attempts:
|
|
54
|
+
with lock:
|
|
55
|
+
try:
|
|
56
|
+
return func(*args, **kwargs)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
attempts += 1
|
|
59
|
+
if attempts == max_attempts:
|
|
60
|
+
logger.exception(
|
|
61
|
+
f"Failed to execute {func.__name__} after {max_attempts} attempts: {e}"
|
|
62
|
+
)
|
|
63
|
+
raise e
|
|
64
|
+
logger.info(
|
|
65
|
+
f"Retrying execution of {func.__name__} in {delay}s..."
|
|
66
|
+
)
|
|
67
|
+
sleep(delay)
|
|
68
|
+
|
|
69
|
+
return wrapper
|
|
70
|
+
|
|
71
|
+
return decorator
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class PsuStatus(IntFlag):
|
|
75
|
+
"""
|
|
76
|
+
Represents the status of a PSU according to STATUS_BYTE register.
|
|
77
|
+
|
|
78
|
+
See Also
|
|
79
|
+
--------
|
|
80
|
+
`PMBus Power System Management Protocol Specification - Part II - Revision 1.4, Fig. 60 <https://pmbus.org/>`_
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
OUTPUT_OVERVOLTAGE = 0x20
|
|
84
|
+
OUTPUT_OVERCURRENT = 0x10
|
|
85
|
+
INPUT_UNDERVOLTAGE = 0x08
|
|
86
|
+
TEMP_ANORMALY = 0x04
|
|
87
|
+
COMMUNICATION_ERROR = 0x02
|
|
88
|
+
ERROR = 0x01
|
|
89
|
+
OK = 0x00
|
|
90
|
+
|
|
10
91
|
|
|
11
92
|
class PmBus(Device, ProbeableDevice):
|
|
93
|
+
|
|
12
94
|
def __init__(
|
|
13
95
|
self,
|
|
14
96
|
name: str,
|
|
15
|
-
parent:
|
|
97
|
+
parent: SysTree | None = None,
|
|
16
98
|
children: list[Device] | None = None,
|
|
17
99
|
*args,
|
|
18
100
|
**kwargs,
|
|
@@ -23,43 +105,143 @@ class PmBus(Device, ProbeableDevice):
|
|
|
23
105
|
|
|
24
106
|
files = list(Path(self.sysfs_device.path).rglob("hwmon/*/in1_input"))
|
|
25
107
|
if len(files) == 0:
|
|
26
|
-
raise FileNotFoundError("No HWMON entries found")
|
|
108
|
+
raise FileNotFoundError("No HWMON entries found in sysfs")
|
|
27
109
|
self.hwmon_sysfs = SysFSDevice(files[0].parent)
|
|
28
110
|
|
|
111
|
+
self.debugfs_root = sysfs_root() / "kernel/debug/pmbus"
|
|
112
|
+
files = list(self.debugfs_root.rglob("hwmon*/status*_input"))
|
|
113
|
+
if len(files) == 0:
|
|
114
|
+
raise FileNotFoundError("No HWMON entries found in debugfs")
|
|
115
|
+
self.hwmon_debugfs = SysFSDevice(files[0].parent)
|
|
116
|
+
|
|
29
117
|
def probe(self, *args, **kwargs) -> bool:
|
|
30
118
|
from ekfsm.core import HwModule
|
|
31
119
|
|
|
32
|
-
assert isinstance(self.
|
|
33
|
-
return self.
|
|
120
|
+
assert isinstance(self.hw_module, HwModule)
|
|
121
|
+
return self.hw_module.id == self.model()
|
|
34
122
|
|
|
35
123
|
# Voltage and Current Interfaces
|
|
36
|
-
def
|
|
37
|
-
return float(self.hwmon_sysfs.read_attr_utf8(in_file)) / 1000.0
|
|
38
|
-
|
|
39
|
-
def _current_conversion(self, in_file: str) -> float:
|
|
124
|
+
def _conversion(self, in_file: str) -> float:
|
|
40
125
|
return float(self.hwmon_sysfs.read_attr_utf8(in_file)) / 1000.0
|
|
41
126
|
|
|
127
|
+
@retry()
|
|
42
128
|
def in1_input(self) -> float:
|
|
43
|
-
|
|
129
|
+
"""
|
|
130
|
+
Get input voltage of PSU page 1.
|
|
44
131
|
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
Input voltage in volts
|
|
135
|
+
"""
|
|
136
|
+
return self._conversion("in1_input")
|
|
137
|
+
|
|
138
|
+
@retry()
|
|
45
139
|
def in2_input(self) -> float:
|
|
46
|
-
|
|
140
|
+
"""
|
|
141
|
+
Get input voltage of PSU page 2.
|
|
142
|
+
|
|
143
|
+
Returns
|
|
144
|
+
-------
|
|
145
|
+
Input voltage in volts
|
|
146
|
+
"""
|
|
147
|
+
return self._conversion("in2_input")
|
|
47
148
|
|
|
149
|
+
@retry()
|
|
48
150
|
def curr1_input(self) -> float:
|
|
49
|
-
|
|
151
|
+
"""
|
|
152
|
+
Get input current of PSU page 1.
|
|
50
153
|
|
|
154
|
+
Returns
|
|
155
|
+
-------
|
|
156
|
+
Input current in amperes
|
|
157
|
+
"""
|
|
158
|
+
return self._conversion("curr1_input")
|
|
159
|
+
|
|
160
|
+
@retry()
|
|
51
161
|
def curr2_input(self) -> float:
|
|
52
|
-
|
|
162
|
+
"""
|
|
163
|
+
Get input current of PSU page 2.
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
Input current in amperes
|
|
168
|
+
"""
|
|
169
|
+
return self._conversion("curr2_input")
|
|
170
|
+
|
|
171
|
+
# Status Interface
|
|
172
|
+
@retry()
|
|
173
|
+
def status0_input(self) -> PsuStatus:
|
|
174
|
+
"""
|
|
175
|
+
Get the status of PSU page 1.
|
|
176
|
+
|
|
177
|
+
Returns
|
|
178
|
+
-------
|
|
179
|
+
PSU status as defined in PsuStatus
|
|
180
|
+
"""
|
|
181
|
+
status = int(self.hwmon_debugfs.read_attr_utf8("status0_input").strip(), 16)
|
|
182
|
+
return PsuStatus(status)
|
|
183
|
+
|
|
184
|
+
@retry()
|
|
185
|
+
def status1_input(self) -> PsuStatus:
|
|
186
|
+
"""
|
|
187
|
+
Get the status of PSU page 2.
|
|
188
|
+
|
|
189
|
+
Returns
|
|
190
|
+
-------
|
|
191
|
+
PSU status as defined in PsuStatus
|
|
192
|
+
"""
|
|
193
|
+
status = int(self.hwmon_debugfs.read_attr_utf8("status1_input").strip(), 16)
|
|
194
|
+
return PsuStatus(status)
|
|
195
|
+
|
|
196
|
+
# Temperature Interface
|
|
197
|
+
@retry()
|
|
198
|
+
def temp1_input(self) -> float:
|
|
199
|
+
"""
|
|
200
|
+
Get the PSU temperature.
|
|
201
|
+
|
|
202
|
+
Returns
|
|
203
|
+
-------
|
|
204
|
+
PSU temperature in degrees celsius
|
|
205
|
+
"""
|
|
206
|
+
return self._conversion("temp1_input")
|
|
53
207
|
|
|
54
208
|
# Inventory Interface
|
|
55
209
|
def vendor(self) -> str:
|
|
210
|
+
"""
|
|
211
|
+
Get the vendor of the PSU.
|
|
212
|
+
|
|
213
|
+
Returns
|
|
214
|
+
-------
|
|
215
|
+
PSU vendor
|
|
216
|
+
"""
|
|
56
217
|
return self.hwmon_sysfs.read_attr_utf8("vendor").strip()
|
|
57
218
|
|
|
58
219
|
def model(self) -> str:
|
|
220
|
+
"""
|
|
221
|
+
Get the model of the PSU.
|
|
222
|
+
|
|
223
|
+
Returns
|
|
224
|
+
-------
|
|
225
|
+
PSU model
|
|
226
|
+
"""
|
|
59
227
|
return self.hwmon_sysfs.read_attr_utf8("model").strip()
|
|
60
228
|
|
|
61
229
|
def serial(self) -> str:
|
|
230
|
+
"""
|
|
231
|
+
Get the serial number of the PSU.
|
|
232
|
+
|
|
233
|
+
Returns
|
|
234
|
+
-------
|
|
235
|
+
PSU serial number
|
|
236
|
+
"""
|
|
62
237
|
return self.hwmon_sysfs.read_attr_utf8("serial").strip()
|
|
63
238
|
|
|
64
239
|
def revision(self) -> str:
|
|
240
|
+
"""
|
|
241
|
+
Get the revision of the PSU.
|
|
242
|
+
|
|
243
|
+
Returns
|
|
244
|
+
-------
|
|
245
|
+
PSU revision
|
|
246
|
+
"""
|
|
65
247
|
return self.hwmon_sysfs.read_attr_utf8("revision").strip()
|
ekfsm/devices/smbus.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SimSmbus(ABC):
|
|
6
|
+
@abstractmethod
|
|
7
|
+
def read_word_data(self, cmd: int) -> int:
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def read_block_data(self, cmd: int) -> List[int]:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def write_block_data(self, cmd: int, data: List[int]):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
@abstractmethod
|
|
19
|
+
def write_byte(self, cmd: int):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
@abstractmethod
|
|
23
|
+
def write_word_data(self, cmd: int, data: int):
|
|
24
|
+
pass
|
ekfsm/simctrl.py
CHANGED
|
@@ -1,42 +1,20 @@
|
|
|
1
|
-
from abc import ABC, abstractmethod
|
|
2
1
|
import socket
|
|
3
2
|
import struct
|
|
4
3
|
from unittest.mock import patch
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
|
|
6
|
+
from ekfsm.devices.smbus import SimSmbus
|
|
7
7
|
from ekfsm.devices.gpio import EKFIdSimGpio
|
|
8
8
|
from ekfsm.devices.gpio import SimGpio
|
|
9
9
|
from .core.sysfs import set_sysfs_root
|
|
10
|
-
from .core.components import
|
|
10
|
+
from .core.components import SysTree
|
|
11
11
|
|
|
12
12
|
from .devices import GPIO
|
|
13
13
|
from typing import List
|
|
14
14
|
from smbus2 import SMBus
|
|
15
15
|
|
|
16
|
-
GPIO_SIM_MAPPING = {}
|
|
17
|
-
SMBUS_SIM_MAPPING = {}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class SimSmbus(ABC):
|
|
21
|
-
@abstractmethod
|
|
22
|
-
def read_word_data(self, cmd: int) -> int:
|
|
23
|
-
pass
|
|
24
|
-
|
|
25
|
-
@abstractmethod
|
|
26
|
-
def read_block_data(self, cmd: int) -> List[int]:
|
|
27
|
-
pass
|
|
28
|
-
|
|
29
|
-
@abstractmethod
|
|
30
|
-
def write_block_data(self, cmd: int, data: List[int]):
|
|
31
|
-
pass
|
|
32
|
-
|
|
33
|
-
@abstractmethod
|
|
34
|
-
def write_byte(self, cmd: int):
|
|
35
|
-
pass
|
|
36
|
-
|
|
37
|
-
@abstractmethod
|
|
38
|
-
def write_word_data(self, cmd: int, data: int):
|
|
39
|
-
pass
|
|
16
|
+
GPIO_SIM_MAPPING: dict[str, SimGpio] = {}
|
|
17
|
+
SMBUS_SIM_MAPPING: dict[str, SimSmbus] = {}
|
|
40
18
|
|
|
41
19
|
|
|
42
20
|
def register_gpio_sim(major: int, minor: int, sim_gpio: SimGpio) -> None:
|
|
@@ -72,7 +50,7 @@ class GpioSimulator(GPIO):
|
|
|
72
50
|
def __init__(
|
|
73
51
|
self,
|
|
74
52
|
name: str,
|
|
75
|
-
parent:
|
|
53
|
+
parent: SysTree | None = None,
|
|
76
54
|
*args,
|
|
77
55
|
**kwargs,
|
|
78
56
|
):
|
ekfsm/system.py
CHANGED
|
@@ -2,6 +2,7 @@ from typing import Tuple, Any, Generator
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from munch import Munch, munchify
|
|
4
4
|
|
|
5
|
+
from ekfsm.core.components import SysTree
|
|
5
6
|
import yaml
|
|
6
7
|
|
|
7
8
|
from .core.slots import Slot, SlotType
|
|
@@ -46,7 +47,7 @@ def all_board_cfg_files() -> Generator[Path, None, None]:
|
|
|
46
47
|
yield item
|
|
47
48
|
|
|
48
49
|
|
|
49
|
-
class System:
|
|
50
|
+
class System(SysTree):
|
|
50
51
|
"""
|
|
51
52
|
A System represents a CPCI system.
|
|
52
53
|
|
|
@@ -94,21 +95,26 @@ class System:
|
|
|
94
95
|
>>> print(b.name + b.slot.name) # Print the name of the board and the slot it is in
|
|
95
96
|
"""
|
|
96
97
|
|
|
97
|
-
def __init__(self, config: Path) -> None:
|
|
98
|
+
def __init__(self, config: Path, abort: bool = False) -> None:
|
|
98
99
|
"""
|
|
99
100
|
Parameters
|
|
100
101
|
----------
|
|
101
102
|
config
|
|
102
103
|
Path to the config that specifies the system and how the slots are filled.
|
|
103
104
|
"""
|
|
105
|
+
self.config_path = config
|
|
106
|
+
self.config = load_config(str(self.config_path))
|
|
107
|
+
self.name = self.config.system_config.name
|
|
108
|
+
|
|
109
|
+
super().__init__(self.name, abort=abort)
|
|
110
|
+
|
|
104
111
|
self.logger = ekfsm_logger(__name__)
|
|
105
112
|
self._init_system(config)
|
|
106
113
|
self._init_slot_attrs()
|
|
114
|
+
self._aggregate_provider_functions()
|
|
115
|
+
self.children = self.boards
|
|
107
116
|
|
|
108
117
|
def _init_system(self, config: Path):
|
|
109
|
-
self.config_path = config
|
|
110
|
-
self.config = load_config(str(self.config_path))
|
|
111
|
-
self.name = self.config.system_config.name
|
|
112
118
|
self.slots: Slots = Slots()
|
|
113
119
|
self.boards: list[HwModule] = []
|
|
114
120
|
|
|
@@ -136,6 +142,18 @@ class System:
|
|
|
136
142
|
for board in self.boards:
|
|
137
143
|
setattr(self, board.instance_name.lower(), board)
|
|
138
144
|
|
|
145
|
+
def _aggregate_provider_functions(self):
|
|
146
|
+
if hasattr(self.config.system_config, "aggregates"):
|
|
147
|
+
agg = self.config.system_config.aggregates
|
|
148
|
+
if agg is not None:
|
|
149
|
+
for key, value in agg.items():
|
|
150
|
+
prv = Munch()
|
|
151
|
+
for board in self.boards:
|
|
152
|
+
if hasattr(board, key):
|
|
153
|
+
prv.update({value: getattr(board, key)})
|
|
154
|
+
if value in prv.keys():
|
|
155
|
+
setattr(self, value, prv[value])
|
|
156
|
+
|
|
139
157
|
def reload(self):
|
|
140
158
|
"""
|
|
141
159
|
Reload the current system configuration.
|
|
@@ -144,7 +162,7 @@ class System:
|
|
|
144
162
|
---------
|
|
145
163
|
This will rebuild all system objects and reinitialize the system tree.
|
|
146
164
|
"""
|
|
147
|
-
self.
|
|
165
|
+
self.__init__(self.config_path)
|
|
148
166
|
|
|
149
167
|
def _create_master(self) -> Tuple[HwModule | None, int]:
|
|
150
168
|
for i, slot in enumerate(self.config.system_config.slots):
|
|
@@ -190,10 +208,16 @@ class System:
|
|
|
190
208
|
hwmod = self._create_hwmodule_from_cfg_file(slot, board_name, path)
|
|
191
209
|
|
|
192
210
|
except Exception as e:
|
|
193
|
-
self.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
211
|
+
if self.abort:
|
|
212
|
+
self.logger.error(
|
|
213
|
+
f"failed to create desired hwmodule {board_type} (as {board_name}): {e}. Aborting!"
|
|
214
|
+
)
|
|
215
|
+
raise e
|
|
216
|
+
else:
|
|
217
|
+
self.logger.error(
|
|
218
|
+
f"failed to create desired hwmodule {board_type} (as {board_name}): {e}. Leaving slot empty!"
|
|
219
|
+
)
|
|
220
|
+
return None, slot
|
|
197
221
|
|
|
198
222
|
# try to probe desired board type
|
|
199
223
|
if hwmod.probe():
|
|
@@ -211,6 +235,7 @@ class System:
|
|
|
211
235
|
hwmod = self._create_hwmodule_from_cfg_file(slot, board_name, path)
|
|
212
236
|
except ConfigError:
|
|
213
237
|
# slot type not matching, ignore
|
|
238
|
+
# ??? should we log this?
|
|
214
239
|
continue
|
|
215
240
|
except Exception as e:
|
|
216
241
|
self.logger.debug(
|
|
@@ -274,7 +299,15 @@ class System:
|
|
|
274
299
|
f"Slot type mismatch for slot {slot.name}: {cfg.slot_type} != {slot.slot_type}"
|
|
275
300
|
)
|
|
276
301
|
|
|
277
|
-
|
|
302
|
+
hwmod = HwModule(
|
|
303
|
+
instance_name=board_name,
|
|
304
|
+
config=yaml_data,
|
|
305
|
+
slot=slot,
|
|
306
|
+
abort=self.abort,
|
|
307
|
+
parent=self,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
return hwmod
|
|
278
311
|
|
|
279
312
|
def get_module_in_slot(self, idx: int) -> HwModule | None:
|
|
280
313
|
return next(
|
|
@@ -316,11 +349,5 @@ class System:
|
|
|
316
349
|
f"'{type(self).__name__}' object has no board with name '{name}'"
|
|
317
350
|
)
|
|
318
351
|
|
|
319
|
-
def
|
|
320
|
-
|
|
321
|
-
for b in self.boards:
|
|
322
|
-
output += b._render_tree()
|
|
323
|
-
return output
|
|
324
|
-
|
|
325
|
-
def print(self) -> None:
|
|
326
|
-
print(self)
|
|
352
|
+
def __repr__(self):
|
|
353
|
+
return f"System (name={self.name})"
|