ekfsm 1.3.0a26__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ekfsm might be problematic. Click here for more details.

@@ -2,40 +2,54 @@ id: 69
2
2
  name: "EKF SQ3-QUARTET"
3
3
  slot_type: CPCI_S0_PER
4
4
  children:
5
- - device_type: I2CMux
6
- name: "MUX"
7
- addr: 0x70
8
- slot_coding_mask: 0x07
9
- children:
10
- - device_type: MuxChannel
11
- name: "CH00"
12
- channel_id: 0
13
- children:
14
- - device_type: EKFIdentificationIOExpander
15
- name: "GPIO"
16
- addr: 0x3D
17
- provides:
18
- inventory:
19
- - revision
20
- - device_type: EKF_EEPROM
21
- name: "EEPROM"
22
- addr: 0x55
23
- provides:
24
- inventory:
25
- - vendor
26
- - serial
27
- - model
28
- - repaired_at
29
- - manufactured_at
30
- - device_type: MuxChannel
31
- name: "CH01"
32
- channel_id: 1
33
- children:
34
- - device_type: MuxChannel
35
- name: "CH02"
36
- channel_id: 2
37
- children:
38
- - device_type: MuxChannel
39
- name: "CH03"
40
- channel_id: 3
41
- children:
5
+ - device_type: IO4Edge
6
+ name: "BMC"
7
+ children:
8
+ - device_type: PixelDisplay
9
+ name: "display"
10
+ - device_type: ButtonArray
11
+ name: "buttons"
12
+ children:
13
+ - device_type: Button
14
+ name: "up"
15
+ channel_id: 0
16
+ - device_type: Button
17
+ name: "down"
18
+ channel_id: 1
19
+ - device_type: I2CMux
20
+ name: "MUX"
21
+ addr: 0x70
22
+ slot_coding_mask: 0x07
23
+ children:
24
+ - device_type: MuxChannel
25
+ name: "CH00"
26
+ channel_id: 0
27
+ children:
28
+ - device_type: EKFIdentificationIOExpander
29
+ name: "GPIO"
30
+ addr: 0x3D
31
+ provides:
32
+ inventory:
33
+ - revision
34
+ - device_type: EKF_EEPROM
35
+ name: "EEPROM"
36
+ addr: 0x55
37
+ provides:
38
+ inventory:
39
+ - vendor
40
+ - serial
41
+ - model
42
+ - repaired_at
43
+ - manufactured_at
44
+ - device_type: MuxChannel
45
+ name: "CH01"
46
+ channel_id: 1
47
+ children:
48
+ - device_type: MuxChannel
49
+ name: "CH02"
50
+ channel_id: 2
51
+ children:
52
+ - device_type: MuxChannel
53
+ name: "CH03"
54
+ channel_id: 3
55
+ children:
@@ -0,0 +1,94 @@
1
+ id: 112
2
+ name: "EKF Z1010"
3
+ slot_type: CPCI_S0_UTILITY
4
+ children:
5
+ - device_type: I2CMux
6
+ name: "MUX"
7
+ addr: 0x70
8
+ slot_coding_mask: 0x0
9
+ children:
10
+ - device_type: MuxChannel
11
+ name: "CH00"
12
+ channel_id: 0
13
+ children:
14
+ - device_type: EKFIdentificationIOExpander
15
+ name: "GPIO"
16
+ addr: 0x3D
17
+ provides:
18
+ inventory:
19
+ - revision
20
+ - device_type: EKF_EEPROM
21
+ name: "EEPROM"
22
+ addr: 0x55
23
+ provides:
24
+ inventory:
25
+ - vendor
26
+ - serial
27
+ - model
28
+ - repaired_at
29
+ - manufactured_at
30
+ # chassis_inventory:
31
+ # - revision: crevision
32
+ # - write_revision: write_crevision
33
+ # - vendor: cvendor
34
+ # - write_vendor: write_cvendor
35
+ # - serial: cserial
36
+ # - write_serial: write_cserial
37
+ # - model: cmodel
38
+ # - write_model: write_cmodel
39
+ # - unit
40
+ # - write_unit
41
+ # custom_eeprom:
42
+ # - write: write_customer_area
43
+ # - read: customer_area
44
+ - device_type: IO4Edge
45
+ name: "I4E"
46
+ provides:
47
+ management:
48
+ - identify_firmware
49
+ - load_firmware
50
+ - load_parameter
51
+ - get_parameter
52
+ - restart
53
+ children:
54
+ - device_type: LEDArray
55
+ name: "leds"
56
+ children:
57
+ - device_type: ColorLED
58
+ name: "led0"
59
+ channel_id: 0
60
+ - device_type: ColorLED
61
+ name: "led1"
62
+ channel_id: 1
63
+ - device_type: ColorLED
64
+ name: "led2"
65
+ channel_id: 2
66
+ - device_type: ColorLED
67
+ name: "led3"
68
+ channel_id: 3
69
+ - device_type: ColorLED
70
+ name: "led4"
71
+ channel_id: 4
72
+ - device_type: ColorLED
73
+ name: "led5"
74
+ channel_id: 5
75
+ - device_type: ColorLED
76
+ name: "led6"
77
+ channel_id: 6
78
+ - device_type: ColorLED
79
+ name: "led7"
80
+ channel_id: 7
81
+ - device_type: Watchdog
82
+ name: "watchdog"
83
+ provides:
84
+ sysstate:
85
+ - kick
86
+ - device_type: ThermalHumidity
87
+ name: "th"
88
+ - device_type: ButtonArray
89
+ name: "buttons"
90
+ service_suffix: "gpios"
91
+ children:
92
+ - device_type: Button
93
+ name: "eject"
94
+ channel_id: 2
ekfsm/core/components.py CHANGED
@@ -51,7 +51,7 @@ class HWModule(SysTree):
51
51
  *args,
52
52
  **kwargs,
53
53
  ) -> None:
54
- from ekfsm.core.utils import deserialize_hardware_tree
54
+ from ekfsm.core.utils import deserialize_hardware_tree, deserialize_module
55
55
 
56
56
  from .slots import SlotType
57
57
 
@@ -59,9 +59,8 @@ class HWModule(SysTree):
59
59
  self._slot: Slot = slot
60
60
  self.config = config
61
61
 
62
- self.id, self.board_type, slot_type, self.children = deserialize_hardware_tree(
63
- self.logger, self.config, parent=self
64
- )
62
+ self.id, self.board_type, slot_type = deserialize_module(self.logger, config)
63
+ self.children = deserialize_hardware_tree(self.logger, self.config, parent=self)
65
64
 
66
65
  self.slot_type = SlotType.from_string(slot_type)
67
66
 
@@ -0,0 +1,19 @@
1
+ from contextlib import contextmanager
2
+
3
+
4
+ class Connectable:
5
+
6
+ def __init__(self, client=None):
7
+ self._client = client
8
+ self._connected = False
9
+
10
+ @contextmanager
11
+ def connect(self):
12
+ if not self._connected:
13
+ client = self._client(self.service_addr, command_timeout=self.timeout)
14
+ self._connected = True
15
+ try:
16
+ yield client
17
+ finally:
18
+ client.close()
19
+ del client
ekfsm/core/slots.py CHANGED
@@ -102,7 +102,9 @@ class Slot:
102
102
  }
103
103
 
104
104
  def __repr__(self) -> str:
105
- return f"{self.__class__.__name__}(name={self._name}, slot_type={self.slot_type})"
105
+ return (
106
+ f"{self.__class__.__name__}(name={self._name}, slot_type={self.slot_type})"
107
+ )
106
108
 
107
109
  @property
108
110
  def name(self) -> str:
@@ -123,7 +125,10 @@ class Slot:
123
125
  """
124
126
  Return True if the slot is populated with the desired hardware module type, False otherwise.
125
127
  """
126
- return self.hwmodule is not None and self.hwmodule.board_type.lower() == self._desired_hwmodule_type.lower()
128
+ return (
129
+ self.hwmodule is not None
130
+ and self.hwmodule.board_type.lower() == self._desired_hwmodule_type.lower()
131
+ )
127
132
 
128
133
 
129
134
  class Slots(Munch):
ekfsm/core/sysfs.py CHANGED
@@ -166,7 +166,7 @@ class SysfsDevice(MutableMapping):
166
166
 
167
167
  def read_int(self, attr) -> int:
168
168
  """
169
- Read a sysfs attribute as an integer
169
+ Read a sysfs attribute stored as a string as an integer
170
170
 
171
171
  Parameters
172
172
  ----------
@@ -186,7 +186,7 @@ class SysfsDevice(MutableMapping):
186
186
  """
187
187
  try:
188
188
  value = self.read_attr_utf8(attr).strip()
189
- return int(value, 16)
189
+ return int(value)
190
190
  except StopIteration as e:
191
191
  raise SysFSError(f"'{attr}' sysfs attribute does not exist") from e
192
192
  except SysFSError:
@@ -194,6 +194,36 @@ class SysfsDevice(MutableMapping):
194
194
  except ValueError as e:
195
195
  raise ConversionError("Failed to convert sysfs value to int") from e
196
196
 
197
+ def read_hex(self, attr) -> int:
198
+ """
199
+ Read a sysfs attribute stored as a hexadecimal integer as integer
200
+
201
+ Parameters
202
+ ----------
203
+ attr: str
204
+ The sysfs attribute to read
205
+
206
+ Returns
207
+ -------
208
+ The sysfs attribute as a hexadecimal integer
209
+
210
+ Raises
211
+ ------
212
+ SysFSError
213
+ If the sysfs attribute does not exist
214
+ ConversionError
215
+ If the sysfs attribute could not be converted to a hexadecimal integer
216
+ """
217
+ try:
218
+ value = self.read_attr_utf8(attr).strip()
219
+ return int(value, 16)
220
+ except StopIteration as e:
221
+ raise SysFSError(f"'{attr}' sysfs attribute does not exist") from e
222
+ except SysFSError:
223
+ raise
224
+ except ValueError as e:
225
+ raise ConversionError("Failed to convert sysfs value to hex int") from e
226
+
197
227
  def read_utf8(self, attr, strip=True) -> str:
198
228
  """
199
229
  Read a sysfs attribute as a UTF-8 encoded string
ekfsm/core/utils.py CHANGED
@@ -55,10 +55,14 @@ def import_board(logger: logging.Logger, data, parent=None, abort: bool = False)
55
55
  import_board(logger, data=child, parent=node, abort=abort)
56
56
  except Exception as e:
57
57
  if abort:
58
- logger.error(f"Failed to import sub device {pformat(child)}: {e}. Aborting.")
58
+ logger.error(
59
+ f"Failed to import sub device {pformat(child)}: {e}. Aborting."
60
+ )
59
61
  raise e
60
62
  else:
61
- logger.error(f"Failed to import sub device {pformat(child)}: {e}. Continuing anyway.")
63
+ logger.error(
64
+ f"Failed to import sub device {pformat(child)}: {e}. Continuing anyway."
65
+ )
62
66
  return node
63
67
 
64
68
 
@@ -69,7 +73,9 @@ def provides_validator(x: Any) -> Any:
69
73
  key, value = next(iter(x.items()))
70
74
  if isinstance(key, str) and isinstance(value, str):
71
75
  return x
72
- raise SchemaError("Each provides item must be either a string or a dictionary with one string key/value pair")
76
+ raise SchemaError(
77
+ "Each provides item must be either a string or a dictionary with one string key/value pair"
78
+ )
73
79
 
74
80
 
75
81
  device_schema = Schema({})
@@ -81,6 +87,8 @@ _device_structure = Schema(
81
87
  Optional("addr"): int,
82
88
  Optional("slot_coding_mask"): int,
83
89
  Optional("channel_id"): int,
90
+ Optional("service_suffix"): str,
91
+ Optional("keepaliveInterval"): int,
84
92
  Optional("provides"): {
85
93
  Optional(str): [Use(provides_validator)],
86
94
  },
@@ -103,30 +111,46 @@ module_schema = Schema(
103
111
  )
104
112
 
105
113
 
114
+ def deserialize_module(logger: logging.Logger, data: dict) -> tuple[str, str, str]:
115
+ """
116
+ docstring
117
+ """
118
+ module_schema.validate(data)
119
+
120
+ id, name, slot_type = (data[key] for key in ["id", "name", "slot_type"])
121
+ logger.debug(colored(f"Importing top level module {pformat(name)}", "green"))
122
+
123
+ return id, name, slot_type
124
+
125
+
106
126
  def deserialize_hardware_tree(
107
127
  logger: logging.Logger, data: dict, parent: "HWModule"
108
- ) -> tuple[str, str, str, list["Device"]]:
128
+ ) -> list["Device"]:
109
129
  abort = parent.abort
110
130
 
111
131
  module_schema.validate(data)
112
132
 
113
- id, name, slot_type = (data.pop(key) for key in ["id", "name", "slot_type"])
114
-
115
133
  children = data.pop("children", None)
116
134
  if not children:
117
- return id, name, slot_type, []
135
+ return []
118
136
 
119
137
  devices = []
120
138
  for child in children:
121
139
  try:
122
- logger.debug(colored(f"Importing top level device {pformat(child)}", "green"))
140
+ logger.debug(
141
+ colored(f"Importing top level device {pformat(child)}", "green")
142
+ )
123
143
 
124
144
  node = import_board(logger, child, parent=parent, abort=abort)
125
145
  devices.append(node)
126
146
  except Exception as e:
127
- logger.error(colored(f"Failed to import top-level device {pformat(child)}: {e}", "red"))
147
+ logger.error(
148
+ colored(
149
+ f"Failed to import top-level device {pformat(child)}: {e}", "red"
150
+ )
151
+ )
128
152
  logger.error(colored("Aborting." if abort else "Continuing anyway", "red"))
129
153
  if abort:
130
154
  raise
131
155
 
132
- return id, name, slot_type, devices
156
+ return devices
ekfsm/devices/__init__.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from ekfsm.devices.coretemp import CoreTemp
2
2
  from ekfsm.devices.generic import Device
3
3
  from ekfsm.devices.smbios import SMBIOS
4
+ from ekfsm.devices.thermal_humidity import ThermalHumidity
4
5
 
5
6
  from .eeprom import EEPROM, EKF_CCU_EEPROM, EKF_EEPROM
6
7
  from .ekf_ccu_uc import EKFCcuUc
@@ -9,6 +10,13 @@ from .gpio import GPIO, EKFIdentificationIOExpander, GPIOExpander
9
10
  from .iio_thermal_humidity import IIOThermalHumidity
10
11
  from .mux import I2CMux, MuxChannel
11
12
  from .pmbus import PMBus, PSUStatus
13
+ from .io4edge import IO4Edge
14
+ from .pixelDisplay import PixelDisplay
15
+ from .buttonArray import ButtonArray
16
+ from .button import Button
17
+ from .colorLed import ColorLED
18
+ from .ledArray import LEDArray
19
+ from .watchdog import Watchdog
12
20
 
13
21
  CLASS_MAP = {
14
22
  "GenericDevice": Device,
@@ -27,4 +35,12 @@ CLASS_MAP = {
27
35
  "HWMON": CoreTemp,
28
36
  "EKFSurLed": EKFSurLed,
29
37
  "IIOThermalHumidity": IIOThermalHumidity,
38
+ "ThermalHumidity": ThermalHumidity,
39
+ "IO4Edge": IO4Edge,
40
+ "PixelDisplay": PixelDisplay,
41
+ "ButtonArray": ButtonArray,
42
+ "Button": Button,
43
+ "ColorLED": ColorLED,
44
+ "LEDArray": LEDArray,
45
+ "Watchdog": Watchdog,
30
46
  }
@@ -0,0 +1,65 @@
1
+ from typing import Callable
2
+ from ekfsm.devices.generic import Device
3
+ from ekfsm.log import ekfsm_logger
4
+
5
+ logger = ekfsm_logger(__name__)
6
+
7
+
8
+ class Button(Device):
9
+ """
10
+ Device class for handling a single button as part on array.
11
+ """
12
+
13
+ def __init__(
14
+ self,
15
+ name: str,
16
+ parent: Device,
17
+ children: list[Device] | None = None,
18
+ abort: bool = False,
19
+ channel_id: int = 0,
20
+ *args,
21
+ **kwargs,
22
+ ):
23
+ logger.debug(f"Initializing Button '{name}' on channel {channel_id}")
24
+
25
+ super().__init__(name, parent, children, abort, *args, **kwargs)
26
+
27
+ self.channel_id = channel_id
28
+ logger.debug(f"Button '{name}' assigned to channel {channel_id}")
29
+
30
+ self._handler: Callable | None = None
31
+ logger.info(f"Button '{name}' initialized on channel {channel_id}")
32
+
33
+ @property
34
+ def handler(self):
35
+ """
36
+ Handle button events with a callback function.
37
+ """
38
+ return self._handler
39
+
40
+ @handler.setter
41
+ def handler(self, func: Callable | None, *args, **kwargs):
42
+ """
43
+ Handle button events with a callback function.
44
+
45
+ Parameters
46
+ ----------
47
+ func : Callable | None
48
+ The function to call on button events. If None, no function is called.
49
+ """
50
+ if callable(func):
51
+ self._handler = func
52
+ logger.info(
53
+ f"Handler set for button '{self.name}' on channel {self.channel_id}"
54
+ )
55
+ logger.debug(
56
+ f"Handler function: {func.__name__ if hasattr(func, '__name__') else str(func)}"
57
+ )
58
+ else:
59
+ self._handler = None
60
+ logger.debug(
61
+ f"Handler cleared for button '{self.name}' on channel {self.channel_id}"
62
+ )
63
+
64
+ def __repr__(self):
65
+ return f"{self.name}; Channel ID: {self.channel_id}"