ekfsm 0.12.0.post1__py3-none-any.whl → 0.13.0a168.post1__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/se5-club.yaml +41 -0
- ekfsm/boards/oem/ekf/sn4-djembe.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 +14 -5
- 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 +2 -2
- ekfsm/devices/ekf_sur_led.py +2 -2
- ekfsm/devices/generic.py +20 -15
- ekfsm/devices/gpio.py +7 -7
- ekfsm/devices/iio_thermal_humidity.py +2 -2
- ekfsm/devices/mux.py +3 -3
- ekfsm/devices/pmbus.py +196 -14
- ekfsm/devices/smbios.py +4 -2
- ekfsm/devices/smbus.py +24 -0
- ekfsm/simctrl.py +9 -28
- ekfsm/system.py +46 -19
- ekfsm-0.13.0a168.post1.dist-info/METADATA +174 -0
- ekfsm-0.13.0a168.post1.dist-info/RECORD +44 -0
- ekfsm-0.12.0.post1.dist-info/METADATA +0 -86
- ekfsm-0.12.0.post1.dist-info/RECORD +0 -41
- {ekfsm-0.12.0.post1.dist-info → ekfsm-0.13.0a168.post1.dist-info}/WHEEL +0 -0
- {ekfsm-0.12.0.post1.dist-info → ekfsm-0.13.0a168.post1.dist-info}/entry_points.txt +0 -0
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/smbios.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
2
|
from ekfsm.core.components import HwModule
|
|
3
|
-
from ekfsm.core.sysfs import SysFSDevice,
|
|
3
|
+
from ekfsm.core.sysfs import SysFSDevice, sysfs_root
|
|
4
4
|
from .generic import Device
|
|
5
5
|
|
|
6
6
|
|
|
@@ -22,7 +22,9 @@ class SMBIOS(Device):
|
|
|
22
22
|
*args,
|
|
23
23
|
**kwargs,
|
|
24
24
|
):
|
|
25
|
-
self.sysfs_device: SysFSDevice = SysFSDevice(
|
|
25
|
+
self.sysfs_device: SysFSDevice = SysFSDevice(
|
|
26
|
+
sysfs_root() / Path("devices/virtual/dmi/id")
|
|
27
|
+
)
|
|
26
28
|
|
|
27
29
|
super().__init__(name, parent, None, *args, **kwargs)
|
|
28
30
|
|
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
|
):
|
|
@@ -191,13 +169,16 @@ def enable_smbus_simulation():
|
|
|
191
169
|
pm.start()
|
|
192
170
|
|
|
193
171
|
|
|
194
|
-
def enable_simulation(sysfs_path: Path):
|
|
172
|
+
def enable_simulation(sysfs_path: Path | str) -> None:
|
|
195
173
|
global GPIO_SIM_MAPPING
|
|
196
174
|
GPIO_SIM_MAPPING = {}
|
|
197
175
|
|
|
198
176
|
global SMBUS_SIM_MAPPING
|
|
199
177
|
SMBUS_SIM_MAPPING = {}
|
|
200
178
|
|
|
179
|
+
if isinstance(sysfs_path, str):
|
|
180
|
+
sysfs_path = Path(sysfs_path)
|
|
181
|
+
|
|
201
182
|
set_sysfs_root(sysfs_path)
|
|
202
183
|
enable_gpio_simulation()
|
|
203
184
|
enable_smbus_simulation()
|
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})"
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ekfsm
|
|
3
|
+
Version: 0.13.0a168.post1
|
|
4
|
+
Summary: The EKF System Management Library (ekfsm) is a sensor monitoring suite for Compact PCI Serial devices.
|
|
5
|
+
Author-email: Klaus Popp <klaus.popp@ci4rail.com>, Jan Jansen <jan@ekf.de>, Felix Päßler <fp@ekf.de>
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: anytree
|
|
8
|
+
Requires-Dist: click>=8.0.1
|
|
9
|
+
Requires-Dist: crcmod
|
|
10
|
+
Requires-Dist: gpiod>=2.1.0
|
|
11
|
+
Requires-Dist: hexdump
|
|
12
|
+
Requires-Dist: more-itertools
|
|
13
|
+
Requires-Dist: munch
|
|
14
|
+
Requires-Dist: smbus2
|
|
15
|
+
Requires-Dist: types-pyyaml>=6.0.12.20241230
|
|
16
|
+
Requires-Dist: yamale
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# ekfsm - EKF system management library
|
|
20
|
+
|
|
21
|
+
Provides a python library framework for access to system management functions on Linux based modular hardware systems,
|
|
22
|
+
such as CompactPCI-Serial systems.
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- System configuration via YAML configuration file
|
|
27
|
+
- Obtain inventory information of the system and its components
|
|
28
|
+
- Obtain sensor information, such as temperature, humidity, voltage, current, accelerometer, gyroscope, etc.
|
|
29
|
+
- Write and read EEPROM contents
|
|
30
|
+
- Access to system level functions, such as system LEDs, system fan, system power supply, etc.
|
|
31
|
+
- Supports simulation mode for development and testing
|
|
32
|
+
- Probing of desired boards in configured slots
|
|
33
|
+
|
|
34
|
+
## Requirements
|
|
35
|
+
|
|
36
|
+
Prior to the use of I2C, PMBus or GPIO devices using the API, those devices have to be initialised by ACPI or manual device setup.
|
|
37
|
+
|
|
38
|
+
### Example
|
|
39
|
+
|
|
40
|
+
In order to initialize an EEPROM of type AT24 behind a Mux channel 0, manualy add the device:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd /sys/bus/i2c/devices/0-0074/channel-0/
|
|
44
|
+
echo 24c02 0x55 >new_device
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Now we can access the EEPROM contents:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
hd 8-0055/eeprom
|
|
51
|
+
00000000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
|
|
52
|
+
*
|
|
53
|
+
00000100
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
To install the package via pip, you have to use a virtual environment to ensure full operabilty.
|
|
60
|
+
*Note: CLI entrypoint script won't work if installed in the system store!*
|
|
61
|
+
|
|
62
|
+
### Prepare virtual environment
|
|
63
|
+
|
|
64
|
+
First, name, create and activate your virtual environment (here `myvenv`):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
$ python -m venv myvenv
|
|
68
|
+
$ source myvenv/bin/activate
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Package install
|
|
72
|
+
|
|
73
|
+
Now install the ekfsm package and all dependencies from the [project pypi registry](https://gitlab.ekf.com/libs/apis/ekfsm/-/packages):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
(myvenv) $ pip install ekfsm --index-url https://gitlab.ekf.com/api/v4/projects/407/packages/pypi/simple
|
|
77
|
+
Looking in indexes: https://gitlab.ekf.com/api/v4/projects/407/packages/pypi/simple
|
|
78
|
+
Collecting ekfsm
|
|
79
|
+
Downloading https://gitlab.ekf.com/api/v4/projects/407/packages/pypi/files/e400ee46de9346c086ce708675977cc6ab080c8c016d360970c82d1c436f7c89/ekfsm-0.12.0-py3-none-any.whl (43 kB)
|
|
80
|
+
Collecting anytree (from ekfsm)
|
|
81
|
+
Using cached anytree-2.12.1-py3-none-any.whl.metadata (8.1 kB)
|
|
82
|
+
Collecting click>=8.0.1 (from ekfsm)
|
|
83
|
+
Using cached click-8.1.8-py3-none-any.whl.metadata (2.3 kB)
|
|
84
|
+
Collecting crcmod (from ekfsm)
|
|
85
|
+
Using cached crcmod-1.7-cp312-cp312-linux_x86_64.whl
|
|
86
|
+
Collecting gpiod>=2.1.0 (from ekfsm)
|
|
87
|
+
Using cached gpiod-2.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.1 kB)
|
|
88
|
+
Collecting hexdump (from ekfsm)
|
|
89
|
+
Using cached hexdump-3.3-py3-none-any.whl
|
|
90
|
+
Collecting more-itertools (from ekfsm)
|
|
91
|
+
Using cached more_itertools-10.6.0-py3-none-any.whl.metadata (37 kB)
|
|
92
|
+
Collecting munch (from ekfsm)
|
|
93
|
+
Using cached munch-4.0.0-py2.py3-none-any.whl.metadata (5.9 kB)
|
|
94
|
+
Collecting smbus2 (from ekfsm)
|
|
95
|
+
Using cached smbus2-0.5.0-py2.py3-none-any.whl.metadata (6.9 kB)
|
|
96
|
+
Collecting types-pyyaml>=6.0.12.20241230 (from ekfsm)
|
|
97
|
+
Using cached types_PyYAML-6.0.12.20241230-py3-none-any.whl.metadata (1.8 kB)
|
|
98
|
+
Collecting yamale (from ekfsm)
|
|
99
|
+
Using cached yamale-6.0.0-py3-none-any.whl.metadata (22 kB)
|
|
100
|
+
Collecting six (from anytree->ekfsm)
|
|
101
|
+
Using cached six-1.17.0-py2.py3-none-any.whl.metadata (1.7 kB)
|
|
102
|
+
Collecting pyyaml (from yamale->ekfsm)
|
|
103
|
+
Using cached PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (2.1 kB)
|
|
104
|
+
Using cached click-8.1.8-py3-none-any.whl (98 kB)
|
|
105
|
+
Using cached gpiod-2.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (103 kB)
|
|
106
|
+
Using cached types_PyYAML-6.0.12.20241230-py3-none-any.whl (20 kB)
|
|
107
|
+
Using cached anytree-2.12.1-py3-none-any.whl (44 kB)
|
|
108
|
+
Using cached more_itertools-10.6.0-py3-none-any.whl (63 kB)
|
|
109
|
+
Using cached munch-4.0.0-py2.py3-none-any.whl (9.9 kB)
|
|
110
|
+
Using cached smbus2-0.5.0-py2.py3-none-any.whl (11 kB)
|
|
111
|
+
Using cached yamale-6.0.0-py3-none-any.whl (57 kB)
|
|
112
|
+
Using cached PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (767 kB)
|
|
113
|
+
Using cached six-1.17.0-py2.py3-none-any.whl (11 kB)
|
|
114
|
+
Installing collected packages: smbus2, hexdump, crcmod, types-pyyaml, six, pyyaml, munch, more-itertools, gpiod, click, yamale, anytree, ekfsm
|
|
115
|
+
Successfully installed anytree-2.12.1 click-8.1.8 crcmod-1.7 ekfsm-0.12.0 gpiod-2.3.0 hexdump-3.3 more-itertools-10.6.0 munch-4.0.0 pyyaml-6.0.2 six-1.17.0 smbus2-0.5.0 types-pyyaml-6.0.12.20241230 yamale-6.0.0
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Example Usage Scenario
|
|
119
|
+
|
|
120
|
+
To use the library for a desired system, it must be configured in a system config yaml file:
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
# Example config
|
|
124
|
+
system_config:
|
|
125
|
+
name: "Simple System"
|
|
126
|
+
slots:
|
|
127
|
+
- name: SYSTEM_SLOT
|
|
128
|
+
slot_type: CPCI_S0_SYS
|
|
129
|
+
desired_hwmodule_type: EKF SC9-Toccata
|
|
130
|
+
desired_hwmodule_name: CPU
|
|
131
|
+
attributes:
|
|
132
|
+
is_master: true
|
|
133
|
+
- name: SLOT1
|
|
134
|
+
slot_type: CPCI_S0_PER
|
|
135
|
+
desired_hwmodule_type: EKF SRF-SUR
|
|
136
|
+
desired_hwmodule_name: SER
|
|
137
|
+
attributes:
|
|
138
|
+
slot_coding: 0x1
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### API
|
|
142
|
+
|
|
143
|
+
If you want to access the LEDs on the EKF SUR-UART, you can do the following:
|
|
144
|
+
```python
|
|
145
|
+
import ekfsm
|
|
146
|
+
|
|
147
|
+
system = ekfsm.System("system.yaml")
|
|
148
|
+
|
|
149
|
+
# alternative ways to get the SUR HwModule
|
|
150
|
+
sur = system["SER"] # by using the hwmodule name as key
|
|
151
|
+
sur = system.ser # by using the hwmodule name as attribute
|
|
152
|
+
sur = system.slots["SLOT1"].hwmodule # by using the slot name as key
|
|
153
|
+
sur = system.slots.slot1.hwmodule # by using the slot name as attribute
|
|
154
|
+
|
|
155
|
+
# accessing the LED device
|
|
156
|
+
sur.led_a.set(0,"purple") # set the color of the LED to purple
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
For further infos on all API aspects, please see the [API Reference](https://ekfsm.readthedocs.io/en/main/reference/index.html).
|
|
160
|
+
|
|
161
|
+
### CLI
|
|
162
|
+
|
|
163
|
+
Upon activation of a venv provided with the ekfsm library, an entry point script `ekfsm-cli` is exported in the current shell.
|
|
164
|
+
|
|
165
|
+
See `ekfsm-cli -h` for a help on the usage.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
## Resources
|
|
169
|
+
|
|
170
|
+
[Documentation](https://ekfsm.readthedocs.io/en/main/)
|
|
171
|
+
|
|
172
|
+
[Source Code](https://gitlab.ekf.com/libs/apis/ekfsm)
|
|
173
|
+
|
|
174
|
+
[Developer Wiki](https://gitlab.ekf.com/libs/apis/ekfsm/-/wikis/home)
|