ekfsm 1.0.2__py3-none-any.whl → 1.2.0a7__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/__init__.py CHANGED
@@ -1,16 +1,5 @@
1
- from .core.slots import Slot
2
1
  from .system import System
3
- from .core import HwModule
4
- from .core.slots import SlotType
5
- from .exceptions import ConfigError
6
- from .lock import locking_cleanup, locking_configure
2
+ from .config import load_config
3
+ from .log import ekfsm_logger
7
4
 
8
- __all__ = (
9
- "System",
10
- "ConfigError",
11
- "HwModule",
12
- "Slot",
13
- "SlotType",
14
- "locking_cleanup",
15
- "locking_configure",
16
- )
5
+ __all__ = ("System", "load_config", "ekfsm_logger")
ekfsm/cli.py CHANGED
@@ -16,6 +16,8 @@ logger = ekfsm_logger(__name__)
16
16
 
17
17
  sm: System | None = None
18
18
 
19
+ __all__ = ("cli", "main", "write", "show")
20
+
19
21
 
20
22
  @click.group()
21
23
  @click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
@@ -37,7 +39,30 @@ sm: System | None = None
37
39
  )
38
40
  def cli(verbose, debug, sysfs, config):
39
41
  global sm
40
- """POSIX-compliant CLI tool with subcommands"""
42
+ """POSIX-compliant CLI tool with subcommands
43
+ for the EKF System Management (EKFSM) library.
44
+
45
+ This tool provides a command-line interface for managing and
46
+ interacting with the EKF System Management library. It allows
47
+ users to perform various operations related to system management,
48
+ including reading and writing data to the system, and displaying
49
+ information about the system.
50
+
51
+ Parameters
52
+ ----------
53
+ verbose
54
+ Enable verbose output. If set, the logging level will be set to INFO.
55
+ debug
56
+ Enable debug output. If set, the logging level will be set to DEBUG.
57
+ sysfs
58
+ Use custom sysfs directory for simulation mode. If set, the
59
+ simulation mode will be enabled and the specified directory
60
+ will be used for sysfs operations.
61
+ config
62
+ Path to the configuration file. This file is required for
63
+ initializing the system. The path should point to a valid
64
+ configuration file in YAML format.
65
+ """
41
66
  if verbose:
42
67
  logging.getLogger().setLevel(logging.INFO)
43
68
  logger.info("Verbose output enabled")
ekfsm/core/__init__.py CHANGED
@@ -2,3 +2,13 @@ from .slots import Slot, SlotType, Slots # noqa: F401
2
2
  from .sysfs import SysFSDevice, SysFSAttribute # noqa: F401
3
3
  from .components import HwModule # noqa: F401
4
4
  from .probe import ProbeableDevice # noqa: F401
5
+
6
+ __all__ = [
7
+ "Slot",
8
+ "SlotType",
9
+ "Slots",
10
+ "SysFSDevice",
11
+ "SysFSAttribute",
12
+ "HwModule",
13
+ "ProbeableDevice",
14
+ ]
ekfsm/core/slots.py CHANGED
@@ -142,7 +142,7 @@ class Slots(Munch):
142
142
 
143
143
  Example
144
144
  -------
145
- >>> from ekfsm.slot import Slot, Slots, SlotType
145
+ >>> from ekfsm..core.slots import Slot, Slots, SlotType
146
146
  >>> slotA = Slot("SlotA", SlotType.CPCI_S0_PER, "Bla", "Blubb", 3)
147
147
  >>> slots = Slots((slotA.name, slotA))
148
148
  >>> print(slots[name])
@@ -192,7 +192,7 @@ class Slots(Munch):
192
192
 
193
193
  Example
194
194
  -------
195
- >>> from ekfsm.slot import Slot, Slots, SlotType
195
+ >>> from ekfsm.core.slots import Slot, Slots, SlotType
196
196
  >>> slotA = Slot("SlotA", SlotType.CPCI_S0_PER, "Bla", "Blubb", 3)
197
197
  >>> slots = Slots()
198
198
  >>> slots.add(slotA) # add slotA to the collection
ekfsm/core/sysfs.py CHANGED
@@ -22,18 +22,8 @@ class SysFSAttribute:
22
22
 
23
23
  Attributes
24
24
  ----------
25
- path : Path
26
- path to the underlying file for the SysFSAttribute instance
27
- name : str
28
- attribute name, or the last part of self.path
29
-
30
- Methods
31
- -------
32
- read() -> Optional[str]
33
- Reads the file attribute if it exists
34
- write(data: Union[str, int]) -> None
35
- Writes data to the file attribute
36
-
25
+ path
26
+ Path to the underlying file for the SysFSAttribute instance.
37
27
  """
38
28
 
39
29
  def __init__(self, path: Path):
@@ -51,7 +41,7 @@ class SysFSAttribute:
51
41
 
52
42
  def write(self, data: str | bytes | None, offset: int = 0) -> None:
53
43
  if self.is_sysfs_attr() and data is not None:
54
- mode = 'r+' if isinstance(data, str) else 'rb+'
44
+ mode = "r+" if isinstance(data, str) else "rb+"
55
45
  with open(self.path, mode) as f:
56
46
  f.seek(offset)
57
47
  f.write(data)
ekfsm/devices/coretemp.py CHANGED
@@ -38,12 +38,10 @@ def find_core_temp_dir(hwmon_dir) -> Path:
38
38
 
39
39
  class CoreTemp(Device):
40
40
  """
41
- A class to represent the HWMON device.
42
-
43
- A HWMON device is a virtual device that is used to read hardware monitoring values from the sysfs filesystem.
41
+ This class provides an interface to read the CPU core temperature from the HWMON device.
44
42
 
45
43
  Note:
46
- Currently, only the CPU temperature is read from the HWMON device.
44
+ Currently, only the average temperature over all cores is read from the HWMON device.
47
45
  """
48
46
 
49
47
  def __init__(
ekfsm/devices/eeprom.py CHANGED
@@ -24,8 +24,6 @@ from hexdump import hexdump
24
24
 
25
25
  from ekfsm.log import ekfsm_logger
26
26
 
27
- __all__ = ["EEPROM", "Validatable_EEPROM", "EKF_EEPROM", "validated"]
28
-
29
27
  logger = ekfsm_logger(__name__)
30
28
 
31
29
 
@@ -1069,6 +1067,23 @@ class EKF_CCU_EEPROM(EKF_EEPROM):
1069
1067
  def write_customer_area(self, data: bytes) -> None:
1070
1068
  """
1071
1069
  Write data to CCU EEPROM customer area.
1070
+
1071
+ Parameters
1072
+ ----------
1073
+ data
1074
+ The data to write to the customer area of the CCU EEPROM.
1075
+
1076
+ Raises
1077
+ ------
1078
+ ValueError
1079
+ If the data exceeds the customer area length.
1080
+
1081
+ Example
1082
+ -------
1083
+ >>> eeprom = EKF_CCU_EEPROM()
1084
+ >>> eeprom.write_customer_area(b"Hello, World!")
1085
+ >>> eeprom.customer_area()
1086
+ b'Hello, World!'
1072
1087
  """
1073
1088
  if len(data) > self._customer_area_length:
1074
1089
  raise ValueError("Data exceeds customer area length")
@@ -54,7 +54,10 @@ class EKFCcuUc(Device):
54
54
  def temperature(self) -> float:
55
55
  """
56
56
  Get the temperature from the CCU thermal/humidity sensor.
57
- The temperature is read once per second.
57
+
58
+ Note
59
+ ----
60
+ The CCU reads the temperature once per second.
58
61
 
59
62
  Returns
60
63
  -------
@@ -128,9 +131,16 @@ class EKFCcuUc(Device):
128
131
 
129
132
  Returns
130
133
  -------
131
- Tuple[float, float, int]
132
- The desired speed, the actual speed, and the diagnostic value.
133
- The diagnostic value is a bitfield with the following meaning:
134
+ desired: float
135
+ The desired speed.
136
+ actual: float
137
+ The actual speed.
138
+ diag: int
139
+ The diagnostic value.
140
+
141
+ Note
142
+ ----
143
+ The diagnostic value is a bitfield with the following meaning:
134
144
 
135
145
  - bit 0: 0 = fan status is invalid, 1 = fan status is valid
136
146
  - bit 1: 0 = no error detected, 1 = fan is stuck
@@ -184,8 +194,10 @@ class EKFCcuUc(Device):
184
194
 
185
195
  Returns
186
196
  -------
187
- Tuple[ImuSample | None, bool]
188
- The IMU sample (or None) and a flag indicating if more samples are available in the FIFO.
197
+ imu_data: ImuSample | None
198
+ The IMU sample, or None if no sample is available.
199
+ more_samples: bool
200
+ True if more samples are available in the FIFO, False otherwise.
189
201
  """
190
202
  more_samples = False
191
203
  _data = self._smbus.read_block_data(
@@ -257,8 +269,10 @@ class EKFCcuUc(Device):
257
269
 
258
270
  Returns
259
271
  -------
260
- Tuple[str, str]
261
- The firmware title and version.
272
+ title: str
273
+ The firmware title.
274
+ version: str
275
+ The firmware version.
262
276
  """
263
277
  title = bytes(
264
278
  self._smbus.read_block_data(
@@ -293,6 +307,13 @@ class EKFCcuUc(Device):
293
307
  progress_callback
294
308
  A callback function that is called with the current progress in bytes.
295
309
 
310
+ Example
311
+ -------
312
+ >>> from ekfsm.devices import EkfCcuUc
313
+ >>> ccu = EkfCcuUc("ccu")
314
+ >>> firmware = open("fw-ccu-1.0.0.bin", "rb").read()
315
+ >>> # Load firmware with progress callback
316
+ >>> ccu.load_firmware(firmware, progress_callback=lambda x: print(f"Progress: {x} bytes"))
296
317
  """
297
318
  with Locker(self.name + "-load_firmware").lock():
298
319
  offset = 0
@@ -412,6 +433,8 @@ class EKFCcuUc(Device):
412
433
  This would load a parameterset with just one parameter, the default fan speed. All other parameters will
413
434
  be set to their default values.
414
435
 
436
+ Important
437
+ ---------
415
438
  In order to apply the parameterset, the CCU must be restarted.
416
439
 
417
440
  Parameters
@@ -419,6 +442,14 @@ class EKFCcuUc(Device):
419
442
  _cfg
420
443
  The parameterset in JSON format.
421
444
 
445
+ Example
446
+ -------
447
+ >>> from ekfsm.devices import EkfCcuUc
448
+ >>> ccu = EkfCcuUc("ccu")
449
+ >>> # Load parameterset
450
+ >>> ccu.load_parameterset('{"version": "1.0.0", "parameters": {"fan-defrpm": "6000"}}')
451
+ >>> # Restart CCU to apply parameterset
452
+ >>> ccu.restart()
422
453
  """
423
454
  with Locker(self.name + "-parameterset").lock():
424
455
  cfg = _cfg.encode("utf-8")
ekfsm/devices/generic.py CHANGED
@@ -77,22 +77,77 @@ class Device(SysTree):
77
77
  return provides
78
78
 
79
79
  def read_sysfs_attr_bytes(self, attr: str) -> bytes | None:
80
+ """
81
+ Read a sysfs attribute as bytes.
82
+
83
+ Parameters
84
+ ----------
85
+ attr
86
+ The sysfs attribute to read.
87
+
88
+ Returns
89
+ -------
90
+ content: bytes
91
+ The contents of the sysfs attribute as bytes.
92
+ None:
93
+ If the sysfs device is not set or the attribute does not exist.
94
+ """
80
95
  if self.sysfs_device and len(attr) != 0:
81
96
  return self.sysfs_device.read_attr_bytes(attr)
82
97
  return None
83
98
 
84
99
  def read_sysfs_attr_utf8(self, attr: str) -> str | None:
100
+ """
101
+ Read a sysfs attribute as UTF-8 string.
102
+
103
+ Parameters
104
+ ----------
105
+ attr
106
+ The sysfs attribute to read.
107
+
108
+ Returns
109
+ -------
110
+ content: str
111
+ The contents of the sysfs attribute as UTF-8 string.
112
+ None:
113
+ If the sysfs device is not set or the attribute does not exist.
114
+ """
85
115
  if self.sysfs_device and len(attr) != 0:
86
116
  return self.sysfs_device.read_attr_utf8(attr)
87
117
  return None
88
118
 
89
119
  def write_sysfs_attr(self, attr: str, data: str | bytes) -> None:
120
+ """
121
+ Write data to a sysfs attribute.
122
+
123
+ Parameters
124
+ ----------
125
+ attr
126
+ The sysfs attribute to write to.
127
+ data
128
+ The data to write to the sysfs attribute.
129
+ """
90
130
  if self.sysfs_device and len(attr) != 0:
91
131
  return self.sysfs_device.write_attr(attr, data)
92
132
  return None
93
133
 
94
134
  @property
95
135
  def hw_module(self) -> "HwModule":
136
+ """
137
+ Get or set the HwModule instance that this device belongs to.
138
+
139
+ Parameters
140
+ ----------
141
+ hw_module: optional
142
+ The HwModule instance to set.
143
+
144
+ Returns
145
+ -------
146
+ HwModule
147
+ The HwModule instance that this device belongs to.
148
+ None
149
+ If used as a setter.
150
+ """
96
151
  from ekfsm.core.components import HwModule
97
152
 
98
153
  if isinstance(self._hw_module, HwModule):
@@ -13,6 +13,14 @@ class IIOThermalHumidity(Device):
13
13
  """
14
14
  Device for IIO thermal and/or humidity sensors.
15
15
 
16
+ Parameters
17
+ ----------
18
+ name
19
+ The name of the device.
20
+ parent
21
+ The parent device of the IIOThermalHumidity device. If None, no parent is created.
22
+ children
23
+ The children of the IIOThermalHumidity device. If None, no children are created.
16
24
  """
17
25
 
18
26
  def __init__(
@@ -24,7 +32,7 @@ class IIOThermalHumidity(Device):
24
32
  **kwargs,
25
33
  ):
26
34
  self.logger = ekfsm_logger("IIOThermalHumidity:" + name)
27
- super().__init__(name, parent, children, **kwargs)
35
+ super().__init__(name, parent, children, *args, **kwargs)
28
36
  self.addr = self.get_i2c_chip_addr()
29
37
  self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
30
38
 
ekfsm/devices/imu.py CHANGED
@@ -2,10 +2,14 @@ class ImuSample:
2
2
  """
3
3
  Class to store IMU data sample
4
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
-
5
+ Parameters
6
+ ----------
7
+ accel
8
+ Accelerometer data in m/s^2, [x, y, z]
9
+ gyro
10
+ Gyroscope data in degrees/s, [x, y, z]
11
+ lost
12
+ True if data was lost before that sample
9
13
  """
10
14
 
11
15
  def __init__(self, accel: list[float], gyro: list[float], lost: bool):
ekfsm/devices/mux.py CHANGED
@@ -5,6 +5,23 @@ from .generic import Device
5
5
 
6
6
 
7
7
  class MuxChannel(Device):
8
+ """
9
+ A MuxChannel is a device that represents a channel on an I2C multiplexer.
10
+ It is a child of the I2CMux device.
11
+ The MuxChannel device is used to access the I2C bus on the channel.
12
+
13
+ Parameters
14
+ ----------
15
+ name
16
+ The name of the device.
17
+ channel_id
18
+ The channel ID of the device.
19
+ parent
20
+ The parent device of the MuxChannel.
21
+ children
22
+ The children of the MuxChannel device. If None, no children are created.
23
+ """
24
+
8
25
  def __init__(
9
26
  self,
10
27
  name: str,
@@ -25,6 +42,19 @@ class MuxChannel(Device):
25
42
 
26
43
 
27
44
  class I2CMux(Device):
45
+ """
46
+ This class represents an I2C multiplexer device.
47
+
48
+ Parameters
49
+ ----------
50
+ name
51
+ The name of the device.
52
+ parent
53
+ The parent device of the I2CMux device. If None, no parent is created.
54
+ children
55
+ The children of the I2CMux device. If None, no children are created.
56
+ """
57
+
28
58
  def __init__(
29
59
  self,
30
60
  name: str,
ekfsm/devices/pmbus.py CHANGED
@@ -76,9 +76,27 @@ class PsuStatus(IntFlag):
76
76
  """
77
77
  Represents the status of a PSU according to STATUS_BYTE register.
78
78
 
79
- See Also
79
+ See also
80
80
  --------
81
+ External Documentation:
81
82
  `PMBus Power System Management Protocol Specification - Part II - Revision 1.4, Fig. 60 <https://pmbus.org/>`_
83
+
84
+ Example
85
+ -------
86
+ >>> from ekfsm.devices.pmbus import PsuStatus
87
+ >>> status = PsuStatus(0x1F)
88
+ >>> status
89
+ <PsuStatus.OUTPUT_OVERCURRENT|INPUT_UNDERVOLTAGE|TEMP_ANORMALY|COMMUNICATION_ERROR|ERROR: 31>
90
+ >>> PsuStatus.OUTPUT_OVERCURRENT in status
91
+ True
92
+ >>> # OK is always present
93
+ >>> PsuStatus.OK in status
94
+ True
95
+ >>> # Instead, check if status is OK
96
+ >>> status == PsuStatus(0x00)
97
+ False
98
+ >>> PsuStatus.OUTPUT_OVERCURRENT in status
99
+ False
82
100
  """
83
101
 
84
102
  OUTPUT_OVERVOLTAGE = 0x20
@@ -91,6 +109,9 @@ class PsuStatus(IntFlag):
91
109
 
92
110
 
93
111
  class PmBus(Device, ProbeableDevice):
112
+ """
113
+ This class represents a PMBus device (e.g. a PSU).
114
+ """
94
115
 
95
116
  def __init__(
96
117
  self,
@@ -100,7 +121,7 @@ class PmBus(Device, ProbeableDevice):
100
121
  *args,
101
122
  **kwargs,
102
123
  ):
103
- super().__init__(name, parent, children, **kwargs)
124
+ super().__init__(name, parent, children, *args, **kwargs)
104
125
  self.addr = self.get_i2c_chip_addr()
105
126
  self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
106
127
 
ekfsm/lock.py CHANGED
@@ -1,23 +1,24 @@
1
+ """
2
+ Some devices or device functions don't allow concurrent access.
3
+ The locking mechanism is used to ensure that only one process/thread can
4
+ access the device (function) at a time.
5
+
6
+ Locking granularity is defined by the device.
7
+ It may be at the device level or function level.
8
+
9
+ Application can choose to use the locking mechanism or not.
10
+ By default, the locking mechanism is enabled and uses the default
11
+ lockfile root directory ``/var/lock/ekfsm``.
12
+
13
+ Use :func:`locking_configure` to enable or disable the locking mechanism or to change
14
+ the lockfile root directory.
15
+ """
16
+
1
17
  import fcntl
2
18
  import os
3
19
  from pathlib import Path
4
20
  from contextlib import contextmanager
5
21
 
6
- #
7
- # Some devices or device functions don't allow concurrent access.
8
- # The locking mechanism is used to ensure that only one process/thread can
9
- # access the device (function) at a time.
10
- #
11
- # Locking granularity is defined by the device.
12
- # It may be at the device level or function level.
13
- #
14
- # Application can choose to use the locking mechanism or not.
15
- # By default, the locking mechanism is enabled and uses the default
16
- # lockfile root directory /var/lock/ekfsm.
17
- #
18
- # Use locking_configure() to enable or disable the locking mechanism or to change
19
- # the lockfile root directory.
20
-
21
22
  USE_LOCK = True
22
23
  LOCKFILE_ROOT = "/var/lock/ekfsm"
23
24
  ALL_LOCKERS: list["Locker"] = [] # List of all locker instances
@@ -27,8 +28,12 @@ def locking_configure(enable: bool, lockfile_root: str = LOCKFILE_ROOT):
27
28
  """
28
29
  Configures the locking mechanism.
29
30
 
30
- :param enable: Whether to enable or disable locking.
31
- :param lockfile_root: The root directory for lockfiles. (default: /var/lock/ekfsm)
31
+ Parameters
32
+ ----------
33
+ enable
34
+ Whether to enable or disable locking.
35
+ lockfile_root
36
+ The root directory for lockfiles.
32
37
  """
33
38
  global USE_LOCK, LOCKFILE_ROOT
34
39
  USE_LOCK = enable
@@ -55,9 +60,12 @@ class Locker:
55
60
 
56
61
  Example
57
62
  -------
58
- with Locker("mysharedresourcename").lock():
59
- # Access the shared resource here
60
- pass
63
+ .. code-block:: python
64
+
65
+ with Locker("mysharedresourcename").lock():
66
+ # Access the shared resource here
67
+ pass
68
+
61
69
  """
62
70
 
63
71
  def __init__(self, module: str):
@@ -72,6 +80,17 @@ class Locker:
72
80
  ALL_LOCKERS.append(self)
73
81
 
74
82
  def cleanup(self):
83
+ """
84
+ Cleans up the lock file and closes the lock file descriptor.
85
+
86
+ Important
87
+ ---------
88
+ This method should be called when the lock is no longer needed.
89
+
90
+ Note
91
+ ----
92
+ It is automatically called when the context manager exits.
93
+ """
75
94
  if not USE_LOCK:
76
95
  return
77
96
  if self.lock_fd is not None:
@@ -84,6 +103,14 @@ class Locker:
84
103
 
85
104
  @contextmanager
86
105
  def lock(self):
106
+ """
107
+ Locks the resource for exclusive access.
108
+
109
+ Note
110
+ ----
111
+ This method is a context manager that locks the resource when entered
112
+ and releases the lock when exited.
113
+ """
87
114
  if not USE_LOCK:
88
115
  yield
89
116
  self.lock_fd = os.open(self.lockfile_path, os.O_RDWR)
ekfsm/system.py CHANGED
@@ -1,10 +1,12 @@
1
- from typing import Tuple, Any, Generator
1
+ from typing import Tuple, Any
2
2
  from pathlib import Path
3
3
  from munch import Munch, munchify
4
4
 
5
5
  from ekfsm.core.components import SysTree
6
6
  import yaml
7
7
 
8
+ from ekfsm.utils import all_board_cfg_files, find_board_config
9
+
8
10
  from .core.slots import Slot, SlotType
9
11
 
10
12
  from .config import load_config
@@ -14,39 +16,6 @@ from .exceptions import ConfigError
14
16
  from .log import ekfsm_logger
15
17
 
16
18
 
17
- _CFG_DIR = Path(__file__).parent / "boards"
18
-
19
-
20
- def find_board_config(module_type: str) -> Path | None:
21
- """
22
- Find a matching board config in `boards/oem/` given the module type specified in
23
- the system configuration file.
24
-
25
- Parameters
26
- ----------
27
- module_type
28
- Board type specified in the system configuration for a slot.
29
- It must consist of an OEM and the board type, separated by whitespace. Neither
30
- part may contain any other whitespace.
31
- """
32
- oem, board = module_type.split(maxsplit=1)
33
- if (
34
- path := _CFG_DIR / "oem" / oem.strip().lower() / f"{board.strip().lower()}.yaml"
35
- ).exists():
36
- return path
37
- return None
38
-
39
-
40
- def all_board_cfg_files() -> Generator[Path, None, None]:
41
- """
42
- Generator that recursively yields all *.yaml files in a directory
43
- """
44
- path = Path(_CFG_DIR)
45
- for item in path.rglob("*.yaml"):
46
- if item.is_file():
47
- yield item
48
-
49
-
50
19
  class System(SysTree):
51
20
  """
52
21
  A System represents a CPCI system.
@@ -57,15 +26,6 @@ class System(SysTree):
57
26
 
58
27
  Visual representation of the system is shown as trees of HW Modules and attached devices.
59
28
 
60
- Iterating over the system will iterate over all boards in the system.
61
-
62
- Accessing boards
63
- ----------------
64
- <board_name>
65
- The board object can be accessed by its name.
66
- <slot_number>
67
- The board object can be accessed by its slot number.
68
-
69
29
  Attributes
70
30
  ----------
71
31
  name
@@ -81,6 +41,18 @@ class System(SysTree):
81
41
  config
82
42
  The system configuration.
83
43
 
44
+
45
+ Accessing boards
46
+ ----------------
47
+
48
+ Iterating over the system will iterate over all boards in the system.
49
+
50
+ <board_name>
51
+ The board object can be accessed by its name.
52
+ <slot_number>
53
+ The board object can be accessed by its slot number.
54
+
55
+
84
56
  Example
85
57
  -------
86
58
  >>> from ekfsm.system import System
@@ -101,6 +73,9 @@ class System(SysTree):
101
73
  ----------
102
74
  config
103
75
  Path to the config that specifies the system and how the slots are filled.
76
+ abort
77
+ If True, abort the program if a board cannot be created. If False, leave the slot empty.
78
+ Default is False.
104
79
  """
105
80
  self.config_path = config
106
81
  self.config = load_config(str(self.config_path))
@@ -171,7 +146,7 @@ class System(SysTree):
171
146
  if slot.attributes.is_master:
172
147
  master, _ = self.create_hwmodule(slot, i, None)
173
148
  if master is not None:
174
- master.master = master
149
+ master.master = master # ???
175
150
  return master, i
176
151
  else:
177
152
  return None, -1
@@ -183,6 +158,15 @@ class System(SysTree):
183
158
  """
184
159
  Create HwModule object for the slot.
185
160
 
161
+ Parameters
162
+ ----------
163
+ slot_entry
164
+ The slot entry config (usually part of the system configuration).
165
+ slot_number
166
+ The slot number of the slot.
167
+ master
168
+ The master board of the system.
169
+
186
170
  Returns
187
171
  -------
188
172
  HwModule and Slot. HwModule is None if it cannot be created.
@@ -310,6 +294,20 @@ class System(SysTree):
310
294
  return hwmod
311
295
 
312
296
  def get_module_in_slot(self, idx: int) -> HwModule | None:
297
+ """
298
+ Get the hwmodule in the given slot.
299
+
300
+ Parameters
301
+ ----------
302
+ idx
303
+ The slot index.
304
+ Returns
305
+ -------
306
+ HwModule
307
+ The hwmodule in the given slot.
308
+ None
309
+ If no hwmodule is present in the given slot.
310
+ """
313
311
  return next(
314
312
  (
315
313
  v.hwmodule
@@ -320,6 +318,21 @@ class System(SysTree):
320
318
  )
321
319
 
322
320
  def get_module_by_name(self, name: str) -> HwModule | None:
321
+ """
322
+ Get the hwmodule by its name.
323
+
324
+ Parameters
325
+ ----------
326
+ name
327
+ The name of the hwmodule.
328
+
329
+ Returns
330
+ -------
331
+ HwModule
332
+ The hwmodule with the given name.
333
+ None
334
+ If no hwmodule is present with the given name.
335
+ """
323
336
  return next(
324
337
  (b for b in self.boards if getattr(b, "instance_name", None) == name),
325
338
  None,
ekfsm/utils.py ADDED
@@ -0,0 +1,41 @@
1
+ from typing import Generator
2
+
3
+
4
+ from pathlib import Path
5
+
6
+ _CFG_DIR = Path(__file__).parent / "boards"
7
+
8
+
9
+ def find_board_config(module_type: str) -> Path | None:
10
+ """
11
+ Find a matching board config in `boards/oem/` given the module type specified in
12
+ the system configuration file.
13
+
14
+ Parameters
15
+ ----------
16
+ module_type
17
+ Board type specified in the system configuration for a slot.
18
+ It must consist of an OEM and the board type, separated by whitespace. Neither
19
+ part may contain any other whitespace.
20
+ """
21
+ oem, board = module_type.split(maxsplit=1)
22
+ if (
23
+ path := _CFG_DIR / "oem" / oem.strip().lower() / f"{board.strip().lower()}.yaml"
24
+ ).exists():
25
+ return path
26
+ return None
27
+
28
+
29
+ def all_board_cfg_files() -> Generator[Path, None, None]:
30
+ """
31
+ Generator that recursively yields all *.yaml files in the config directory.
32
+
33
+ Yields
34
+ ------
35
+ item: Path
36
+ Path to a config file.
37
+ """
38
+ path = Path(_CFG_DIR)
39
+ for item in path.rglob("*.yaml"):
40
+ if item.is_file():
41
+ yield item
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ekfsm
3
- Version: 1.0.2
3
+ Version: 1.2.0a7
4
4
  Summary: The EKF System Management Library (ekfsm) is a sensor monitoring suite for Compact PCI Serial devices.
5
5
  Author-email: Klaus Popp <klaus.popp@ci4rail.com>, Jan Jansen <jan@ekf.de>, Felix Päßler <fp@ekf.de>
6
6
  Requires-Python: >=3.10
@@ -1,12 +1,13 @@
1
- ekfsm/__init__.py,sha256=UQljpq-to0fUnfzSlYJ-jdfv8CCc6ZQKBXoTnss67i8,345
2
- ekfsm/cli.py,sha256=ElaIx49lujDM-XCE0R29pv1AsqqnMcNCPZ1-S5XOpSw,2931
1
+ ekfsm/__init__.py,sha256=R12o_igFRTqlHUD9jyrOIPq2nBpTUWuvIPgCytBlVzk,142
2
+ ekfsm/cli.py,sha256=JmTO1TyxrTlGTkQo8ndSR7nj0grSGuQdh9ugXx1IeQE,3919
3
3
  ekfsm/config.py,sha256=FTk47f3qA05Zv6Cy_L_5XlGmmbC9z_kPdQJKpVoHkWs,924
4
4
  ekfsm/exceptions.py,sha256=25P677GxfBTdBWHRngPce0bmhcLg7MFeTtuAvguxd90,1310
5
- ekfsm/lock.py,sha256=qmWkW6OA-NLiDrtSsQn-K10tZJ19t-h4iFB5zyL97Vw,2682
5
+ ekfsm/lock.py,sha256=XWLu2yhgIs16oSyc_4jdVUobbxyYlYOjZi1EFgHsXVU,3236
6
6
  ekfsm/log.py,sha256=_GC8Y7a4fFV4_DNicbwQ-5rRzNQU6WSotXd2etXSrZk,866
7
7
  ekfsm/py.typed,sha256=1gNRtmxvYcVqDDEyAzBLnD8dAOweUfYxW2ZPdJzN1fg,102
8
8
  ekfsm/simctrl.py,sha256=NkmjvqOym9Wruh0f14Od6gHfEgPMAkxUzMQ-nvzcM3Q,6681
9
- ekfsm/system.py,sha256=NoLvfkAKIuyE_1K7EA4mIcwAfGeO50lU-0wfSqj-0jA,11809
9
+ ekfsm/system.py,sha256=kBukgVyPJYTh_ilauEEANG2KKqHWOrQBYLhVdG679HM,12028
10
+ ekfsm/utils.py,sha256=IKQrGr8UzUJpqj365uQqIWDRTAgpVC0OUmqIKzt_9T0,1081
10
11
  ekfsm/boards/oem/ekf/ccu.yaml,sha256=qgr7YZO0kEddD9K6tv6222NyozkRNuF7NFw6hyX0XgE,2094
11
12
  ekfsm/boards/oem/ekf/sc5-festival.yaml,sha256=_0kS5GegfyOt5CTJc9kY6HJbr9yZo4i18sVo6F4KE9c,772
12
13
  ekfsm/boards/oem/ekf/sc9-toccata.yaml,sha256=btLgQMSsW0tRipnUYUkVQSIsjzxfKq0NXaQ1fMMyBRI,771
@@ -18,28 +19,28 @@ ekfsm/boards/oem/ekf/sq3-quartet.yaml,sha256=pBB7Tv0IWLkFUYBs3tFvZriA-uqPuPIgzja
18
19
  ekfsm/boards/oem/ekf/srf-fan.yaml,sha256=Mcu1Q8B1Ih10hoc_hbkGlppBmbOFcufsVUR42iW4Rwc,1368
19
20
  ekfsm/boards/oem/ekf/sur-uart.yaml,sha256=VaNP2BSlNTi1lDco16Ma9smPEAMaVKvx-ZNDcm3QptM,1890
20
21
  ekfsm/boards/oem/hitron/hdrc-300s.yaml,sha256=E567QsRPvyAEXpXOeF1OvX2AoQK8BTxI0C7QVBFlT_k,548
21
- ekfsm/core/__init__.py,sha256=nv3Rs-E953M9PBFV9ryejRY-KM6xG1NMlQTWwzCEQwo,212
22
+ ekfsm/core/__init__.py,sha256=NVgbM5fBDbPhP5HtzMTKfloe9u5xutm47DM9Vt_kbRU,348
22
23
  ekfsm/core/components.py,sha256=dh71jM_OJ8QhTzou-5L6RaF3abXgE470KJfxQRqq1CY,3836
23
24
  ekfsm/core/probe.py,sha256=DgJvkvMjVk09n0Rzn13ybRvidrmFn_D2PD56XS-KgxU,262
24
- ekfsm/core/slots.py,sha256=WOKYO-8TfpWexu3DsSjrYTvjtN4pAMiy22zMjjUI-qQ,6569
25
- ekfsm/core/sysfs.py,sha256=iW_XWtEjW7pZqpJc_RyMzhVjbetw0ydTH11lhfxrPbc,2541
25
+ ekfsm/core/slots.py,sha256=pKBkPUxv-Lz9GxCrlnZu4gR7f59tQ0GW1MBs7nblqaI,6582
26
+ ekfsm/core/sysfs.py,sha256=CVGxUEdhmJjRw8ZfnjL3hUwm6oWsp3ZKPG2qGUFYIiY,2283
26
27
  ekfsm/core/utils.py,sha256=EoPOuRmLlvHvGneDcKjP7Qbjfsc4XlMbLeaSFRObt_E,3145
27
28
  ekfsm/devices/__init__.py,sha256=UtFLCtAdpDJ3OaY7fedk13bx90NMsfxhyVAHV13t2U4,936
28
- ekfsm/devices/coretemp.py,sha256=s0qUoVR90hr9O7Yp7AecXc_LAfrd0LUIJSm2Wh7AJZY,2012
29
- ekfsm/devices/eeprom.py,sha256=8z0eWs4ljRtoLoS3rLbMiGcSxWmNmXeJ8zB6hRGZXis,32042
30
- ekfsm/devices/ekf_ccu_uc.py,sha256=a1vztQQfRVNVikJ-jBNTV9lgtm_i8056J6UMi6qiDcg,15211
29
+ ekfsm/devices/coretemp.py,sha256=oco909zpXw9BqkLWV3dJma1_Q23M2EUGsgdcs4QtFjU,1966
30
+ ekfsm/devices/eeprom.py,sha256=4UjydeDxZT7w2zXkhZu7GUuHYu8sAidT8xuVtkt4OJw,32389
31
+ ekfsm/devices/ekf_ccu_uc.py,sha256=bVdxaiXtrJC2-tSRv1rGUzm4wOxL20QfTL4NIKdRJE8,16092
31
32
  ekfsm/devices/ekf_sur_led.py,sha256=dY2EIqUx4-LV7ipCEIg8DfXWlGxpspRmEtEhVfHY6LI,1871
32
- ekfsm/devices/generic.py,sha256=Xii1fkV8R2kRvWCe9Pg-uOVDWXa-Ow5AM6CbUYKcxSI,9494
33
+ ekfsm/devices/generic.py,sha256=9FDj8hhiCQAFmA5tt3lv1JPIZyAb3XCCriDIoR61kcE,10821
33
34
  ekfsm/devices/gpio.py,sha256=hlJ3yJ8jSGUIkPpjymani0EcxYJX2iEybGbnV8N0O5E,9552
34
35
  ekfsm/devices/iio.py,sha256=gMOJGdg2PvFbAvlBpLfDTIc_-9i1Z3OLpuabTke6N3I,1480
35
- ekfsm/devices/iio_thermal_humidity.py,sha256=4Qod31hhnUIHP6ojv_cf2JLld8OCN1McFOPTJL-Ue-A,1196
36
- ekfsm/devices/imu.py,sha256=bnSaTjGSLV90FyUbkK9oTon0dmF7N_JQ4uGmBPFA5Co,414
37
- ekfsm/devices/mux.py,sha256=CV5ECUiEvaxXNoozMFchsuLD4FpYD_7uFp62DNWVFJ0,1026
38
- ekfsm/devices/pmbus.py,sha256=rdhVvwuV0pQs7DvQgluoKLVPHBRa13HpJiU6oVwkLRg,6482
36
+ ekfsm/devices/iio_thermal_humidity.py,sha256=gALZ1B4f1ePGAEL7Q5VxbubgC3CgtC9RBM9F4YWuH4E,1478
37
+ ekfsm/devices/imu.py,sha256=GwJrnC-WztpE5oGnkIp-laFkSp3_gbSp9YpJ8jBsUOs,423
38
+ ekfsm/devices/mux.py,sha256=C2h5w8eXrL4RXXebCSnNY-VShTgeqmYDMvGSunapBqk,1830
39
+ ekfsm/devices/pmbus.py,sha256=5nIvuUkY20YwXTIpd3lebdu2dU-he_IJ7wLz1EUTrzc,7078
39
40
  ekfsm/devices/smbios.py,sha256=gDDYtfCd7njzqxJBKJxIR2BMCK6-uZxUVxC9y0hnz4g,1009
40
41
  ekfsm/devices/smbus.py,sha256=zD9b6PehipMw-xoaOMC-oorDVxBoo30hc98gyFxFLs0,500
41
42
  ekfsm/devices/utils.py,sha256=4-0Kmvy4ou8R71afNj6RqdBTjLW4SMWPHqVrzB2RUZw,397
42
- ekfsm-1.0.2.dist-info/METADATA,sha256=JV3aX9Sb1ZAoz0I1uOtW4uL6enNFFu8emeEPds4H3PY,6541
43
- ekfsm-1.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
44
- ekfsm-1.0.2.dist-info/entry_points.txt,sha256=WhUR4FzuxPoGrbTOKLsNJO7NAnk2qd4T30fqzN1yLw8,45
45
- ekfsm-1.0.2.dist-info/RECORD,,
43
+ ekfsm-1.2.0a7.dist-info/METADATA,sha256=VUyieRTVS_l9ZWkP1ctAYgFAK5s9tv3V_mUOTBXkePM,6543
44
+ ekfsm-1.2.0a7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
45
+ ekfsm-1.2.0a7.dist-info/entry_points.txt,sha256=WhUR4FzuxPoGrbTOKLsNJO7NAnk2qd4T30fqzN1yLw8,45
46
+ ekfsm-1.2.0a7.dist-info/RECORD,,
File without changes