ekfsm 1.3.0a32__py3-none-any.whl → 1.4.0a37__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,87 @@
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: "SMC"
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"
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
 
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/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,7 @@ _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,
84
91
  Optional("provides"): {
85
92
  Optional(str): [Use(provides_validator)],
86
93
  },
@@ -103,30 +110,46 @@ module_schema = Schema(
103
110
  )
104
111
 
105
112
 
113
+ def deserialize_module(logger: logging.Logger, data: dict) -> tuple[str, str, str]:
114
+ """
115
+ docstring
116
+ """
117
+ module_schema.validate(data)
118
+
119
+ id, name, slot_type = (data[key] for key in ["id", "name", "slot_type"])
120
+ logger.debug(colored(f"Importing top level module {pformat(name)}", "green"))
121
+
122
+ return id, name, slot_type
123
+
124
+
106
125
  def deserialize_hardware_tree(
107
126
  logger: logging.Logger, data: dict, parent: "HWModule"
108
- ) -> tuple[str, str, str, list["Device"]]:
127
+ ) -> list["Device"]:
109
128
  abort = parent.abort
110
129
 
111
130
  module_schema.validate(data)
112
131
 
113
- id, name, slot_type = (data.pop(key) for key in ["id", "name", "slot_type"])
114
-
115
132
  children = data.pop("children", None)
116
133
  if not children:
117
- return id, name, slot_type, []
134
+ return []
118
135
 
119
136
  devices = []
120
137
  for child in children:
121
138
  try:
122
- logger.debug(colored(f"Importing top level device {pformat(child)}", "green"))
139
+ logger.debug(
140
+ colored(f"Importing top level device {pformat(child)}", "green")
141
+ )
123
142
 
124
143
  node = import_board(logger, child, parent=parent, abort=abort)
125
144
  devices.append(node)
126
145
  except Exception as e:
127
- logger.error(colored(f"Failed to import top-level device {pformat(child)}: {e}", "red"))
146
+ logger.error(
147
+ colored(
148
+ f"Failed to import top-level device {pformat(child)}: {e}", "red"
149
+ )
150
+ )
128
151
  logger.error(colored("Aborting." if abort else "Continuing anyway", "red"))
129
152
  if abort:
130
153
  raise
131
154
 
132
- return id, name, slot_type, devices
155
+ 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,45 @@
1
+ from typing import Callable
2
+ from ekfsm.devices.generic import Device
3
+
4
+
5
+ class Button(Device):
6
+ """
7
+ Device class for handling a button array.
8
+ """
9
+
10
+ def __init__(
11
+ self,
12
+ name: str,
13
+ parent: Device,
14
+ children: list[Device] | None = None,
15
+ abort: bool = False,
16
+ channel_id: int = 0,
17
+ *args,
18
+ **kwargs,
19
+ ):
20
+
21
+ super().__init__(name, parent, children, abort, *args, **kwargs)
22
+
23
+ self.channel_id = channel_id
24
+
25
+ self._handler: Callable | None = None
26
+
27
+ @property
28
+ def handler(self):
29
+ """
30
+ Handle button events with a callback function.
31
+ """
32
+ return self._handler
33
+
34
+ @handler.setter
35
+ def handler(self, func: Callable | None, *args, **kwargs):
36
+ """
37
+ Handle button events with a callback function.
38
+ """
39
+ if callable(func):
40
+ self._handler = func
41
+ else:
42
+ self._handler = None
43
+
44
+ def __repr__(self):
45
+ return f"{self.name}; Channel ID: {self.channel_id}"
@@ -0,0 +1,84 @@
1
+ import threading
2
+ from ekfsm.devices.button import Button
3
+ from ekfsm.devices.generic import Device
4
+ from ekfsm.devices.io4edge import IO4Edge
5
+ import io4edge_client.binaryiotypeb as binio
6
+ import io4edge_client.functionblock as fb
7
+
8
+
9
+ class ButtonArray(Device):
10
+ """
11
+ Device class for handling a button array.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ name: str,
17
+ parent: IO4Edge,
18
+ children: list[Device] | None = None,
19
+ abort: bool = False,
20
+ service_suffix: str | None = None,
21
+ *args,
22
+ **kwargs,
23
+ ):
24
+
25
+ super().__init__(name, parent, children, abort, *args, **kwargs)
26
+
27
+ self.name = name
28
+
29
+ if service_suffix is not None:
30
+ self.service_suffix = service_suffix
31
+ else:
32
+ self.service_suffix = name
33
+
34
+ self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
35
+
36
+ self.client = binio.Client(self.service_addr)
37
+
38
+ self.subscriptionType = binio.Pb.SubscriptionType.BINARYIOTYPEB_ON_RISING_EDGE
39
+ self.stream_cfg = fb.Pb.StreamControlStart(
40
+ bucketSamples=1, # 1 sample per bucket, also ein event pro bucket
41
+ keepaliveInterval=10000, # rueckmeldung auch ohne events alle 10 Sekunden
42
+ bufferedSamples=2, # 2 samples werden gepuffert
43
+ low_latency_mode=True, # schickt soweit moeglich sofort die Events
44
+ )
45
+
46
+ def read(self, stop_event: threading.Event | None = None, timeout: float = 0.1):
47
+ """
48
+ Read all button events and dispatch to handlers.
49
+ """
50
+ self.client.start_stream(
51
+ binio.Pb.StreamControlStart(
52
+ subscribeChannel=tuple(
53
+ binio.Pb.SubscribeChannel(
54
+ channel=button.channel_id,
55
+ subscriptionType=self.subscriptionType,
56
+ )
57
+ for button in self.children
58
+ if isinstance(button, Button)
59
+ )
60
+ ),
61
+ self.stream_cfg,
62
+ )
63
+ try:
64
+ while not (stop_event and stop_event.is_set()):
65
+ try:
66
+ _, samples = self.client.read_stream(timeout=timeout)
67
+ for sample in samples.samples:
68
+ for button in self.children:
69
+ if isinstance(button, Button):
70
+ pressed = bool(sample.inputs & (1 << button.channel_id))
71
+ if pressed and button.handler:
72
+ button.handler()
73
+ except TimeoutError:
74
+ continue
75
+ finally:
76
+ try:
77
+ if stop_event:
78
+ stop_event.clear()
79
+ self.client.stop_stream()
80
+ except TimeoutError:
81
+ pass
82
+
83
+ def __repr__(self):
84
+ return f"{self.name}; Service Address: {self.service_addr}"
@@ -0,0 +1,51 @@
1
+ from ekfsm.devices.generic import Device
2
+ from ekfsm.devices.ledArray import LEDArray
3
+ from io4edge_client.api.colorLED.python.colorLED.v1alpha1.colorLED_pb2 import Color
4
+
5
+
6
+ class ColorLED(Device):
7
+ """
8
+ Device class for handling a color LED.
9
+ """
10
+
11
+ def __init__(
12
+ self,
13
+ name: str,
14
+ parent: LEDArray,
15
+ children: list[Device] | None = None,
16
+ abort: bool = False,
17
+ channel_id: int = 0,
18
+ *args,
19
+ **kwargs,
20
+ ):
21
+
22
+ super().__init__(name, parent, children, abort, *args, **kwargs)
23
+
24
+ self.name = name
25
+ self.channel_id = channel_id
26
+
27
+ self.client = parent.client
28
+
29
+ def describe(self):
30
+ pass
31
+
32
+ def get(self) -> tuple[Color, bool]:
33
+ """
34
+ Get color LED state.
35
+ @raises RuntimeError: if the command fails
36
+ @raises TimeoutError: if the command times out
37
+ """
38
+ return self.client.get(self.channel_id)
39
+
40
+ def set(self, color: Color, blink: bool) -> None:
41
+ """
42
+ Set the color of the color LED.
43
+ @param Color: The color to set the LED to.
44
+ @param blink: Whether to blink the LED.
45
+ @raises RuntimeError: if the command fails
46
+ @raises TimeoutError: if the command times out
47
+ """
48
+ self.client.set(self.channel_id, color, blink)
49
+
50
+ def __repr__(self):
51
+ return f"{self.name}; Channel ID: {self.channel_id}"
@@ -0,0 +1,94 @@
1
+ from typing import Callable, Optional
2
+ from ekfsm.core.components import HWModule
3
+ from ekfsm.devices.generic import Device
4
+ import io4edge_client.core.coreclient as Client
5
+
6
+ from re import sub
7
+
8
+
9
+ class IO4Edge(Device):
10
+ """
11
+ Device class for handling IO4Edge devices.
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ name: str,
17
+ parent: HWModule | None = None,
18
+ children: list[Device] | None = None,
19
+ abort: bool = False,
20
+ *args,
21
+ **kwargs,
22
+ ):
23
+
24
+ super().__init__(name, parent, children, abort, *args, **kwargs)
25
+
26
+ attr = self.hw_module.slot.attributes
27
+ if (
28
+ attr is None
29
+ or not hasattr(attr, "slot_coding")
30
+ or getattr(attr, "slot_coding") is None
31
+ ):
32
+ raise ValueError(
33
+ f"Slot attributes for {self.hw_module.slot.name} are not set or do not contain 'slot_coding'."
34
+ )
35
+ else:
36
+ geoaddr = int(attr.slot_coding)
37
+ self._geoaddr = geoaddr
38
+
39
+ _, module_name = sub(r"-.*$", "", self.hw_module.board_type).split(maxsplit=1)
40
+ self._module_name = module_name
41
+ self.client = Client.new_core_client(self.deviceId)
42
+
43
+ @property
44
+ def deviceId(self) -> str:
45
+ """
46
+ Returns the device ID for the IO4Edge device.
47
+ The device ID is a combination of the module name and the geo address.
48
+ """
49
+ return f"{self._module_name}-geo_addr{self._geoaddr:02d}"
50
+
51
+ def identify_firmware(self) -> tuple[str, str]:
52
+ return (
53
+ self.client.identify_firmware().title,
54
+ self.client.identify_firmware().version,
55
+ )
56
+
57
+ def load_firmware(
58
+ self, cfg: bytes, progress_callback: Optional[Callable[[float], None]] = None
59
+ ) -> None:
60
+ """
61
+ Load firmware onto the IO4Edge device.
62
+
63
+ cfg
64
+ Firmware configuration bytes.
65
+ progress_callback
66
+ Optional callback for progress updates.
67
+ """
68
+ self.client.load_firmware(cfg, progress_callback)
69
+
70
+ def restart(self) -> None:
71
+ self.client.restart()
72
+
73
+ def load_parameter(self, name: str, value: str) -> None:
74
+ """
75
+ Set a parameter onto the IO4Edge device.
76
+
77
+ cfg
78
+ The name of the parameter to load.
79
+ value
80
+ The value to set for the parameter.
81
+ """
82
+ self.client.set_persistent_parameter(name, value)
83
+
84
+ def get_parameter(self, name: str) -> str:
85
+ """
86
+ Get a parameter value from the IO4Edge device.
87
+
88
+ Returns
89
+ The value of the requested parameter.
90
+ """
91
+ return self.client.get_persistent_parameter(name)
92
+
93
+ def __repr__(self):
94
+ return f"{self.name}; DeviceId: {self.deviceId}"
@@ -0,0 +1,36 @@
1
+ from ekfsm.devices.generic import Device
2
+ from ekfsm.devices.io4edge import IO4Edge
3
+ from io4edge_client.colorLED import Client
4
+
5
+
6
+ class LEDArray(Device):
7
+ """
8
+ Device class for handling a LED array.
9
+ """
10
+
11
+ def __init__(
12
+ self,
13
+ name: str,
14
+ parent: IO4Edge,
15
+ children: list[Device] | None = None,
16
+ abort: bool = False,
17
+ service_suffix: str | None = None,
18
+ *args,
19
+ **kwargs,
20
+ ):
21
+
22
+ super().__init__(name, parent, children, abort, *args, **kwargs)
23
+
24
+ self.name = name
25
+
26
+ if service_suffix is not None:
27
+ self.service_suffix = service_suffix
28
+ else:
29
+ self.service_suffix = name
30
+
31
+ self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
32
+
33
+ self.client = Client(self.service_addr)
34
+
35
+ def __repr__(self):
36
+ return f"{self.name}; Service Address: {self.service_addr}"
@@ -0,0 +1,88 @@
1
+ from ekfsm.devices.generic import Device
2
+ from ekfsm.devices.io4edge import IO4Edge
3
+ from io4edge_client.pixelDisplay import Client
4
+ from PIL import Image
5
+
6
+
7
+ class PixelDisplay(Device):
8
+ """
9
+ Device class for handling a pixel display.
10
+ """
11
+
12
+ def __init__(
13
+ self,
14
+ name: str,
15
+ parent: IO4Edge,
16
+ children: list[Device] | None = None,
17
+ abort: bool = False,
18
+ service_suffix: str | None = None,
19
+ *args,
20
+ **kwargs,
21
+ ):
22
+
23
+ super().__init__(name, parent, children, abort, *args, **kwargs)
24
+
25
+ self.name = name
26
+
27
+ if service_suffix is not None:
28
+ self.service_suffix = service_suffix
29
+ else:
30
+ self.service_suffix = name
31
+
32
+ self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
33
+
34
+ self.client = Client(self.service_addr)
35
+
36
+ def describe(self) -> dict:
37
+ """
38
+ Returns a description of the pixel display.
39
+ """
40
+ describe = self.client.describe()
41
+ desc = {
42
+ "height": describe.height_pixel,
43
+ "width": describe.width_pixel,
44
+ "max_num_of_pixel": describe.max_num_of_pixel,
45
+ }
46
+ return desc
47
+
48
+ @property
49
+ def height(self) -> int:
50
+ """
51
+ Returns the height of the pixel display in pixels.
52
+ """
53
+ return self.describe()["height"]
54
+
55
+ @property
56
+ def width(self) -> int:
57
+ """
58
+ Returns the width of the pixel display in pixels.
59
+ """
60
+ return self.describe()["width"]
61
+
62
+ def off(self) -> None:
63
+ """
64
+ Turn off the pixel display.
65
+ @raises RuntimeError: if the command fails
66
+ @raises TimeoutError: if the command times out
67
+ """
68
+ self.client.set_display_off()
69
+
70
+ def display_image(self, path: str) -> None:
71
+ """
72
+ Display an image on the pixel display.
73
+ @raises RuntimeError: if the command fails
74
+ @raises TimeoutError: if the command times out
75
+ """
76
+ with Image.open(path) as img:
77
+ img = img.convert("RGB")
78
+ pix = img.load()
79
+
80
+ for i in range(0, 320, 16):
81
+ pix_area = []
82
+ for k in range(0, 16):
83
+ for j in range(0, 240):
84
+ pix_area.append(pix[j, i + k])
85
+ self.client.set_pixel_area(0, i, 239, pix_area)
86
+
87
+ def __repr__(self):
88
+ return f"{self.name}; Service Address: {self.service_addr}"
@@ -0,0 +1,44 @@
1
+ from ekfsm.devices.generic import Device
2
+ from ekfsm.devices.io4edge import IO4Edge
3
+ from io4edge_client.analogintypea import Client
4
+
5
+
6
+ class ThermalHumidity(Device):
7
+ """
8
+ Device class for handling a thermal humidity sensor.
9
+ """
10
+
11
+ def __init__(
12
+ self,
13
+ name: str,
14
+ parent: IO4Edge,
15
+ children: list[Device] | None = None,
16
+ abort: bool = False,
17
+ service_suffix: str | None = None,
18
+ *args,
19
+ **kwargs,
20
+ ):
21
+
22
+ super().__init__(name, parent, children, abort, *args, **kwargs)
23
+
24
+ self.name = name
25
+
26
+ if service_suffix is not None:
27
+ self.service_suffix = service_suffix
28
+ else:
29
+ self.service_suffix = name
30
+
31
+ self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
32
+
33
+ self.client = Client(self.service_addr)
34
+
35
+ def __repr__(self):
36
+ return f"{self.name}; Service Address: {self.service_addr}"
37
+
38
+ def temperature(self) -> float:
39
+ """
40
+ Get the temperature in Celsius.
41
+ @raises RuntimeError: if the command fails
42
+ @raises TimeoutError: if the command times out
43
+ """
44
+ return self.client.value()
@@ -0,0 +1,44 @@
1
+ from ekfsm.devices.generic import Device
2
+ from ekfsm.devices.io4edge import IO4Edge
3
+ from io4edge_client.watchdog import Client
4
+
5
+
6
+ class Watchdog(Device):
7
+ """
8
+ Device class for handling a color LED.
9
+ """
10
+
11
+ def __init__(
12
+ self,
13
+ name: str,
14
+ parent: IO4Edge,
15
+ children: list[Device] | None = None,
16
+ abort: bool = False,
17
+ service_suffix: str | None = None,
18
+ *args,
19
+ **kwargs,
20
+ ):
21
+
22
+ super().__init__(name, parent, children, abort, *args, **kwargs)
23
+
24
+ self.name = name
25
+
26
+ if service_suffix is not None:
27
+ self.service_suffix = service_suffix
28
+ else:
29
+ self.service_suffix = name
30
+
31
+ self.service_addr = f"{parent.deviceId}-{self.service_suffix}"
32
+
33
+ self.client = Client(self.service_addr)
34
+
35
+ def describe(self):
36
+ pass
37
+
38
+ def kick(self) -> None:
39
+ """
40
+ Kick the watchdog.
41
+ @raises RuntimeError: if the command fails
42
+ @raises TimeoutError: if the command times out
43
+ """
44
+ self.client.kick()
ekfsm/system.py CHANGED
@@ -173,13 +173,17 @@ class System(SysTree):
173
173
  board_type = slot_entry.desired_hwmodule_type
174
174
  board_name = slot_entry.desired_hwmodule_name
175
175
 
176
- self.logger.debug(f"Creating HWModule {board_type} (desired name: {board_name}) in slot {slot.name}")
176
+ self.logger.debug(
177
+ f"Creating HWModule {board_type} (desired name: {board_name}) in slot {slot.name}"
178
+ )
177
179
 
178
180
  if board_type != "":
179
181
  # try to create first the desired board
180
182
  path = find_board_config(board_type)
181
183
  if path is None:
182
- self.logger.error(f"No board config found for {board_type} (desired name: {board_name})")
184
+ self.logger.error(
185
+ f"No board config found for {board_type} (desired name: {board_name})"
186
+ )
183
187
  return None, slot
184
188
 
185
189
  try:
@@ -199,11 +203,15 @@ class System(SysTree):
199
203
 
200
204
  # try to probe desired board type
201
205
  if hwmod.probe():
202
- self.logger.info(f"Found desired board type {hwmod.board_type} for slot {slot.name}")
206
+ self.logger.info(
207
+ f"Found desired board type {hwmod.board_type} for slot {slot.name}"
208
+ )
203
209
  return hwmod, slot
204
210
 
205
211
  # try all other boards types. Maybe someone inserted the wrong board
206
- self.logger.info(f"Probing failed. Trying all other board types for slot {slot.name}")
212
+ self.logger.info(
213
+ f"Probing failed. Trying all other board types for slot {slot.name}"
214
+ )
207
215
  for path in all_board_cfg_files():
208
216
  try:
209
217
  hwmod = self._create_hwmodule_from_cfg_file(slot, board_name, path)
@@ -212,16 +220,22 @@ class System(SysTree):
212
220
  # ??? should we log this?
213
221
  continue
214
222
  except Exception as e:
215
- self.logger.debug(f"failed to create HWmodule {path} for slot {slot.name}: {e}")
223
+ self.logger.debug(
224
+ f"failed to create HWmodule {path} for slot {slot.name}: {e}"
225
+ )
216
226
  continue
217
227
 
218
228
  if hwmod.probe():
219
- self.logger.info(f"Found other board type {hwmod.board_type} for slot {slot.name}")
229
+ self.logger.info(
230
+ f"Found other board type {hwmod.board_type} for slot {slot.name}"
231
+ )
220
232
  return hwmod, slot
221
233
 
222
234
  return None, slot
223
235
 
224
- def _create_slot(self, slot_entry: Munch, slot_number: int, master: HWModule | None) -> Slot:
236
+ def _create_slot(
237
+ self, slot_entry: Munch, slot_number: int, master: HWModule | None
238
+ ) -> Slot:
225
239
  attributes = None
226
240
  if "attributes" in slot_entry:
227
241
  attributes = slot_entry.attributes
@@ -237,7 +251,9 @@ class System(SysTree):
237
251
  attributes,
238
252
  )
239
253
 
240
- def _create_hwmodule_from_cfg_file(self, slot: Slot, board_name: str, path: Path) -> HWModule:
254
+ def _create_hwmodule_from_cfg_file(
255
+ self, slot: Slot, board_name: str, path: Path
256
+ ) -> HWModule:
241
257
  """
242
258
  Try to create a HWModule object from a board config file.
243
259
  It does not probe the hardware.
@@ -261,7 +277,9 @@ class System(SysTree):
261
277
  cfg = munchify(yaml_data)
262
278
  # only instantiate if slot type matches
263
279
  if cfg.slot_type != slot.slot_type.to_string():
264
- raise ConfigError(f"Slot type mismatch for slot {slot.name}: {cfg.slot_type} != {slot.slot_type}")
280
+ raise ConfigError(
281
+ f"Slot type mismatch for slot {slot.name}: {cfg.slot_type} != {slot.slot_type}"
282
+ )
265
283
 
266
284
  hwmod = HWModule(
267
285
  instance_name=board_name,
@@ -289,7 +307,11 @@ class System(SysTree):
289
307
  If no HWModule is present in the given slot.
290
308
  """
291
309
  return next(
292
- (v.hwmodule for k, v in self.slots.items() if getattr(v, "number", None) == idx),
310
+ (
311
+ v.hwmodule
312
+ for k, v in self.slots.items()
313
+ if getattr(v, "number", None) == idx
314
+ ),
293
315
  None,
294
316
  )
295
317
 
@@ -310,7 +332,12 @@ class System(SysTree):
310
332
  If no HWModule is present with the given name.
311
333
  """
312
334
  return next(
313
- (b for b in self.boards if getattr(b, "instance_name", None) == name),
335
+ (
336
+ b
337
+ for b in self.boards
338
+ if getattr(b, "instance_name", None) is not None
339
+ and getattr(b, "instance_name").lower() == name.lower()
340
+ ),
314
341
  None,
315
342
  )
316
343
 
@@ -334,7 +361,9 @@ class System(SysTree):
334
361
  if (hw_module := self.get_module_by_name(name)) is not None:
335
362
  return hw_module
336
363
 
337
- raise AttributeError(f"'{type(self).__name__}' object has no board with name '{name}'")
364
+ raise AttributeError(
365
+ f"'{type(self).__name__}' object has no board with name '{name}'"
366
+ )
338
367
 
339
368
  def __repr__(self):
340
369
  return f"System (name={self.name})"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ekfsm
3
- Version: 1.3.0a32
3
+ Version: 1.4.0a37
4
4
  Summary: The EKF System Management Library (ekfsm) is a sensor monitoring suite for Compact PCI Serial devices.
5
5
  Author-email: Jan Jansen <jan@ekf.de>, Klaus Popp <klaus.popp@ci4rail.com>, Felix Päßler <fp@ekf.de>
6
6
  Requires-Python: >=3.10
@@ -9,9 +9,12 @@ Requires-Dist: click>=8.0.1
9
9
  Requires-Dist: crcmod
10
10
  Requires-Dist: gpiod>=2.1.0
11
11
  Requires-Dist: hexdump
12
+ Requires-Dist: io4edge-client>=1.6.2
12
13
  Requires-Dist: ipdb>=0.13.13
13
14
  Requires-Dist: more-itertools
14
15
  Requires-Dist: munch
16
+ Requires-Dist: pillow
17
+ Requires-Dist: protobuf
15
18
  Requires-Dist: schema>=0.7.7
16
19
  Requires-Dist: smbus2
17
20
  Requires-Dist: termcolor>=3.0.1
@@ -6,7 +6,7 @@ ekfsm/lock.py,sha256=dnPjLXWfWIRnLcGec8g-imuqbmqqXWza8OGhS9Aqydk,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=WXPpWIju6b8Ch7FZW1PaPrWn7D7fP6gw37-ri-OJERE,5382
9
- ekfsm/system.py,sha256=mReq8vRD6YGHlw37RzRlvwzsEM3OPjogneCVJ_XmOFU,11646
9
+ ekfsm/system.py,sha256=c3jbQa_DxZVur8MsD8gYgK6jkAV55U19MipiDqemzRg,12130
10
10
  ekfsm/utils.py,sha256=eOgr4da-dxoMQ6ymjgx_nyBbea8Ck_LAQ8uLM4JgH3c,1179
11
11
  ekfsm/boards/oem/ekf/ccu.yaml,sha256=qgr7YZO0kEddD9K6tv6222NyozkRNuF7NFw6hyX0XgE,2094
12
12
  ekfsm/boards/oem/ekf/sc5-festival.yaml,sha256=_0kS5GegfyOt5CTJc9kY6HJbr9yZo4i18sVo6F4KE9c,772
@@ -15,17 +15,21 @@ ekfsm/boards/oem/ekf/se5-club.yaml,sha256=j3u6sDwXMUBM0LNKwVm920w3T3tzjia2BRTQb2
15
15
  ekfsm/boards/oem/ekf/sn4-djembe.yaml,sha256=ddLDdw7JpPpvR3TmlcTxNVNTOueTl6_A9LSveFrtz3E,1051
16
16
  ekfsm/boards/oem/ekf/spv-mystic.yaml,sha256=zcLiNE7wVdXIT4vkwEYSqQ8ff-KEjCHfHnQzzPXJQ4E,1804
17
17
  ekfsm/boards/oem/ekf/sq1-track.yaml,sha256=YU83BQjGu-4ejirwnGxd38sJme859kdRovkZyiOJciU,1050
18
- ekfsm/boards/oem/ekf/sq3-quartet.yaml,sha256=pBB7Tv0IWLkFUYBs3tFvZriA-uqPuPIgzjaN0aHT4e4,1052
18
+ ekfsm/boards/oem/ekf/sq3-quartet.yaml,sha256=JaHJn_lph2iwLz6cwXu0bjRMgj33OXt1-VBNGFu0U3s,1330
19
19
  ekfsm/boards/oem/ekf/srf-fan.yaml,sha256=Mcu1Q8B1Ih10hoc_hbkGlppBmbOFcufsVUR42iW4Rwc,1368
20
20
  ekfsm/boards/oem/ekf/sur-uart.yaml,sha256=VaNP2BSlNTi1lDco16Ma9smPEAMaVKvx-ZNDcm3QptM,1890
21
+ ekfsm/boards/oem/ekf/z1010.yaml,sha256=Z0m0AaRnBFL3QYvSnzHVauXm3laN4lrVv6ewccyY6Hk,2369
21
22
  ekfsm/boards/oem/hitron/hdrc-300s.yaml,sha256=SwBHTWVqENBun_iZattPbvA9koc-LOI_MIXjLahKyyI,548
22
23
  ekfsm/core/__init__.py,sha256=xfuU072JY_--561hfSS8ToBM-wgswY4Z-YwDMZkVA6c,348
23
- ekfsm/core/components.py,sha256=gEChRquC2fEi3Y4BUa_EzvFkO45u95FT1rDg1lEdfHE,3836
24
+ ekfsm/core/components.py,sha256=iTetqtzNjEEj6FDJNfvXxW_cNNRlLX5_WgB5z3g3zqg,3883
24
25
  ekfsm/core/probe.py,sha256=DgJvkvMjVk09n0Rzn13ybRvidrmFn_D2PD56XS-KgxU,262
25
- ekfsm/core/slots.py,sha256=R26kfUnRzuspIPEMHFMBTkhgALu0Tuq7uGgvtfpafGs,6520
26
+ ekfsm/core/slots.py,sha256=qmxfssVitsMFZYbJRCpOOsvEGqh8JVYdRS-_5BliOF4,6580
26
27
  ekfsm/core/sysfs.py,sha256=CWH1BXlanHAFzK6KY23qLuFvBZ7Q9td3X6JLRwb3vu0,7309
27
- ekfsm/core/utils.py,sha256=D5AcGp7xCsf1qA2hHHWe1bOnrpZlTOaq2srITn6bRkk,4194
28
- ekfsm/devices/__init__.py,sha256=BYq4a7e3eF0GGpoKeBpoNISLIbAjAVMNqnGM_Taw8dE,937
28
+ ekfsm/core/utils.py,sha256=slSgFNmSvwWL5qlDQE9WXGv9u9skojlGtUM_B-WumsY,4634
29
+ ekfsm/devices/__init__.py,sha256=EDFzBuUHt_3_6JD4cageHWZfoMn3WUIIDZk4yEOWgfg,1451
30
+ ekfsm/devices/button.py,sha256=tPGBWi2ZxZDQX_WojQG3lKdsKcCTXRYQjRwRjcIv460,1036
31
+ ekfsm/devices/buttonArray.py,sha256=bKNstHsmc6SM09BaQiQV09wRbgnb5I6KK7JTgCk6PnU,2910
32
+ ekfsm/devices/colorLed.py,sha256=PrA3WsuMs-vi2DxxsW4hRIJh_ZWqs_hW2QmFeijsCxA,1410
29
33
  ekfsm/devices/coretemp.py,sha256=UEsPTMYm05qNCnqnPtbAoYdIfvVGkDl9rJg3tkAP748,1992
30
34
  ekfsm/devices/eeprom.py,sha256=cwcaM2YCKrw6du8d4zT_M3e4oFJh3XNQc5cQM2vqERE,33090
31
35
  ekfsm/devices/ekf_ccu_uc.py,sha256=hjs5r7TKsrFhyyGKClgSFBpchQpFcE7fTZeuLrYiflY,15905
@@ -35,12 +39,17 @@ ekfsm/devices/gpio.py,sha256=37P7uu-ncg0TWf0Xs1iZM-PrX7F_09asd4IhWj6z21Y,10058
35
39
  ekfsm/devices/iio.py,sha256=1Jyh5wJ53DxvpWNBqYW07kkaAN9446ebWGCMk2UIuWM,1265
36
40
  ekfsm/devices/iio_thermal_humidity.py,sha256=KCeEqBlczroxp-hjsiRlXQdmmnJR9Ck3d3ztths5Vvc,1547
37
41
  ekfsm/devices/imu.py,sha256=GwJrnC-WztpE5oGnkIp-laFkSp3_gbSp9YpJ8jBsUOs,423
42
+ ekfsm/devices/io4edge.py,sha256=hk_EzcXVR0-sD2zn6pMuO2txYNnTDKmMaaXS2N__8E4,2743
43
+ ekfsm/devices/ledArray.py,sha256=6DCHs1nJz12aHQNPytaM_KIasQYdB29ZN7GzPb_EcJ4,902
38
44
  ekfsm/devices/mux.py,sha256=QpVWsD43Mx1mtCDG_nOxtHeIeU2Cigc62UljJ_utSXQ,1974
45
+ ekfsm/devices/pixelDisplay.py,sha256=68SANN4S15_NncuDvu0hyjjdqhpxYn14cmxjXwfGqgU,2425
39
46
  ekfsm/devices/pmbus.py,sha256=fi_kR7TlkcqYhHuB7PA2PjANCYmo3MSPeV-B5ZI_WrQ,7004
40
47
  ekfsm/devices/smbios.py,sha256=4P-sxOW27pkzIfVlvFNyVJOcZlYgk2KCTCEUvKf5D94,1067
41
48
  ekfsm/devices/smbus.py,sha256=kbtjEnfrVI6lbfqAbojVqO5nnnpa6HanLf0X2iZjgCg,500
49
+ ekfsm/devices/thermal_humidity.py,sha256=OVr1zaCGGutfk1WYZ3509wx6qHqjjF2nFE9QYM_bWtE,1170
42
50
  ekfsm/devices/utils.py,sha256=qeDNpTpQ3bLGYERAH5lMsifmOaEcQ32FOwEt8tvmjWU,174
43
- ekfsm-1.3.0a32.dist-info/METADATA,sha256=bFvKuhXnQh1_Bl-VYr6uWsUOuCXdNx_sZ7W8fXQUSWA,6634
44
- ekfsm-1.3.0a32.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
45
- ekfsm-1.3.0a32.dist-info/entry_points.txt,sha256=WhUR4FzuxPoGrbTOKLsNJO7NAnk2qd4T30fqzN1yLw8,45
46
- ekfsm-1.3.0a32.dist-info/RECORD,,
51
+ ekfsm/devices/watchdog.py,sha256=-us3IMKsvTR7LzRCszHR19q0jsCGPZUIN_FWN-h8844,1060
52
+ ekfsm-1.4.0a37.dist-info/METADATA,sha256=fz_N76MjDbeGbpB1dHKlmzLEwLb_qLPC6ov2pKLFPQ4,6717
53
+ ekfsm-1.4.0a37.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
54
+ ekfsm-1.4.0a37.dist-info/entry_points.txt,sha256=WhUR4FzuxPoGrbTOKLsNJO7NAnk2qd4T30fqzN1yLw8,45
55
+ ekfsm-1.4.0a37.dist-info/RECORD,,