ekfsm 1.2.0a7__py3-none-any.whl → 1.3.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.

ekfsm/devices/coretemp.py CHANGED
@@ -1,13 +1,15 @@
1
1
  from __future__ import annotations
2
- import os
2
+
3
3
  import glob
4
+ import os
4
5
  from pathlib import Path
5
6
 
6
- from ekfsm.core.sysfs import SYSFS_ROOT
7
+ import ekfsm.core
8
+ from ekfsm.core.sysfs import sysfs_root
7
9
  from ekfsm.devices.generic import Device
8
10
 
9
11
  # Path to the root of the HWMON sysfs filesystem
10
- HWMON_ROOT = SYSFS_ROOT / Path("class/hwmon")
12
+ HWMON_ROOT = sysfs_root() / Path("class/hwmon")
11
13
 
12
14
 
13
15
  def find_core_temp_dir(hwmon_dir) -> Path:
@@ -48,16 +50,15 @@ class CoreTemp(Device):
48
50
  self,
49
51
  name: str,
50
52
  parent: Device,
53
+ children: list["Device"] | None = None,
54
+ abort: bool = False,
51
55
  *args,
52
56
  **kwargs,
53
57
  ):
54
- from ekfsm.core.sysfs import SysFSDevice, SYSFS_ROOT
55
-
56
- self.sysfs_device: SysFSDevice = SysFSDevice(
57
- find_core_temp_dir(SYSFS_ROOT / Path("class/hwmon"))
58
- )
58
+ dir = find_core_temp_dir(sysfs_root() / Path("class/hwmon"))
59
+ self.sysfs_device = ekfsm.core.sysfs.SysfsDevice(dir, False)
59
60
 
60
- super().__init__(name, parent, None, *args, **kwargs)
61
+ super().__init__(name, parent, None, abort, *args, **kwargs)
61
62
 
62
63
  def cputemp(self):
63
64
  """
@@ -68,4 +69,4 @@ class CoreTemp(Device):
68
69
  int
69
70
  The CPU temperature in degrees Celsius.
70
71
  """
71
- return int(self.sysfs_device.read_attr_utf8("temp1_input").strip()) / 1000
72
+ return self.sysfs.read_int("temp1_input") / 1000
ekfsm/devices/eeprom.py CHANGED
@@ -11,18 +11,18 @@ Routine Listings
11
11
 
12
12
  from abc import ABC, abstractmethod
13
13
  from datetime import date
14
- from typing import Any, Callable, Literal, Sequence
15
14
  from functools import wraps
15
+ from typing import Any, Callable, Literal, Sequence
16
+
17
+ from hexdump import hexdump
16
18
 
17
19
  from ekfsm.core.components import SysTree
18
20
  from ekfsm.core.probe import ProbeableDevice
21
+ from ekfsm.exceptions import DataCorruptionError, DriverError, SysFSError
22
+ from ekfsm.log import ekfsm_logger
19
23
 
20
24
  from .generic import Device
21
- from .utils import compute_int_from_bytes, get_crc16_xmodem
22
- from ekfsm.exceptions import DataCorruptionError
23
- from hexdump import hexdump
24
-
25
- from ekfsm.log import ekfsm_logger
25
+ from .utils import get_crc16_xmodem
26
26
 
27
27
  logger = ekfsm_logger(__name__)
28
28
 
@@ -80,13 +80,19 @@ class EEPROM(Device):
80
80
  self,
81
81
  name: str,
82
82
  parent: SysTree | None = None,
83
+ children: list[Device] | None = None,
84
+ abort: bool = False,
83
85
  *args,
84
86
  **kwargs,
85
87
  ):
86
- super().__init__(name, parent, None, *args, **kwargs)
88
+ super().__init__(name, parent, None, abort, *args, **kwargs)
87
89
 
88
90
  self.addr = self.get_i2c_chip_addr()
89
91
  self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
92
+
93
+ if not self.sysfs_device.get_driver():
94
+ raise DriverError("No driver attached to device {self.name}")
95
+
90
96
  self._update_content()
91
97
 
92
98
  def _update_content(self) -> None:
@@ -117,20 +123,10 @@ class EEPROM(Device):
117
123
 
118
124
  Raises
119
125
  ------
120
- FileNotFoundError
121
- If the EEPROM sysfs file is not found.
122
- RuntimeError
123
- If the sysfs device is not found.
126
+ SysFSError
127
+ No sysfs device found for EEPROM or `eeprom` attribute does not exist
124
128
  """
125
- try:
126
- if self.sysfs_device:
127
- cnt = self.sysfs_device.read_attr_bytes("eeprom")
128
- else:
129
- raise RuntimeError("No sysfs device for EEPROM")
130
- except FileNotFoundError:
131
- raise FileNotFoundError("EEPROM not found")
132
-
133
- return cnt
129
+ return self.read_sysfs_bytes("eeprom")
134
130
 
135
131
  def write(self, data: bytes, offset: int = 0) -> None:
136
132
  """
@@ -155,15 +151,27 @@ class EEPROM(Device):
155
151
  Note
156
152
  ----
157
153
  Operation is checked for data corruption by reading back the written data.
154
+
155
+ Important
156
+ ---------
157
+ The offset parameter is only supported if the EEPROM driver is bin_attribute enabled.
158
+
159
+ For almost any other native sysfs attribute, this is NOT the case!
158
160
  """
159
- try:
160
- if self.sysfs_device:
161
- logger.info(f"Writing {len(data)} bytes to EEPROM at offset {offset}")
162
- self.sysfs_device.write_attr("eeprom", data, offset)
163
- else:
164
- raise RuntimeError("No sysfs device for EEPROM")
165
- except FileNotFoundError:
166
- raise FileNotFoundError("EEPROM not found")
161
+
162
+ if self.sysfs_device:
163
+ attr = next(x for x in self.sysfs_device.attributes if x.name == "eeprom")
164
+ logger.info(f"Writing {len(data)} bytes to EEPROM at offset {offset}")
165
+ if attr.is_sysfs_attr() and data is not None:
166
+ mode = "r+" if isinstance(data, str) else "rb+"
167
+ try:
168
+ with open(attr.path, mode) as f:
169
+ f.seek(offset)
170
+ f.write(data)
171
+ except OSError as e:
172
+ raise SysFSError("Error accessing SysFS attribute") from e
173
+ else:
174
+ raise RuntimeError("No sysfs device for EEPROM")
167
175
 
168
176
  self._update_content()
169
177
  written = self._content[offset : offset + len(data)]
@@ -202,6 +210,10 @@ class Validatable_EEPROM(EEPROM, ABC):
202
210
 
203
211
  def __init__(
204
212
  self,
213
+ name: str,
214
+ parent: SysTree | None = None,
215
+ children: list[Device] | None = None,
216
+ abort: bool = False,
205
217
  crc_pos: Literal["start", "end"] = "end",
206
218
  crc_length: int = 2,
207
219
  *args,
@@ -210,7 +222,7 @@ class Validatable_EEPROM(EEPROM, ABC):
210
222
  self._crc_length: int = crc_length
211
223
  self._crc_pos: str = crc_pos
212
224
 
213
- super().__init__(*args, **kwargs)
225
+ super().__init__(name, parent, children, abort, *args, **kwargs)
214
226
 
215
227
  self._crc_pos_start = len(self._data) if self._crc_pos == "end" else 0
216
228
  self._crc_pos_end = self._crc_pos_start + self._crc_length
@@ -388,13 +400,16 @@ class EKF_EEPROM(Validatable_EEPROM, ProbeableDevice):
388
400
 
389
401
  def __init__(
390
402
  self,
403
+ name: str,
404
+ parent: SysTree | None = None,
405
+ children: list[Device] | None = None,
406
+ abort: bool = False,
391
407
  *args,
392
408
  **kwargs,
393
409
  ) -> None:
394
- super().__init__(*args, **kwargs)
410
+ super().__init__(name, parent, children, abort, *args, **kwargs)
395
411
 
396
412
  def _update_content(self) -> None:
397
-
398
413
  super()._update_content()
399
414
 
400
415
  # EKF EEPROM content is restricted to 128 bytes, so strip the rest!
@@ -424,7 +439,7 @@ class EKF_EEPROM(Validatable_EEPROM, ProbeableDevice):
424
439
  The serial number of the root device.
425
440
  """
426
441
  area = self._content[self._sernum_index_start : self._sernum_index_end]
427
- sernum = compute_int_from_bytes(area[::-1])
442
+ sernum = int.from_bytes(area, byteorder="little")
428
443
  return str(sernum)
429
444
 
430
445
  def write_serial(self, serial: int) -> None:
@@ -461,7 +476,7 @@ class EKF_EEPROM(Validatable_EEPROM, ProbeableDevice):
461
476
  area = self._content[
462
477
  self._customer_serial_index_start : self._customer_serial_index_end
463
478
  ]
464
- sernum = compute_int_from_bytes(area[::-1])
479
+ sernum = int.from_bytes(area, byteorder="little")
465
480
  return str(sernum)
466
481
 
467
482
  def write_custom_serial(self, serial: int) -> None:
@@ -690,8 +705,7 @@ class EKF_EEPROM(Validatable_EEPROM, ProbeableDevice):
690
705
  date
691
706
  The decoded date.
692
707
  """
693
- encoded_date = encoded_date[::-1]
694
- bdate = compute_int_from_bytes(encoded_date)
708
+ bdate = int.from_bytes(encoded_date, byteorder="little")
695
709
 
696
710
  # Extract the day (bit 0-4)
697
711
  day = bdate & 0x1F # 0x1F is 00011111 in binary (5 bits)
@@ -713,7 +727,7 @@ class EKF_EEPROM(Validatable_EEPROM, ProbeableDevice):
713
727
  ) # Handle invalid dates, e.g., 30th Feb
714
728
 
715
729
  @classmethod
716
- def _encode_date(self, date: date) -> bytes:
730
+ def _encode_date(cls, date: date) -> bytes:
717
731
  """
718
732
  Encode a date into a proprietary 2-byte format.
719
733
 
@@ -772,7 +786,6 @@ class EKF_CCU_EEPROM(EKF_EEPROM):
772
786
  super().__init__(*args, **kwargs)
773
787
 
774
788
  def _update_content(self) -> None:
775
-
776
789
  super()._update_content()
777
790
 
778
791
  # CCU content is the raw content area of the EEPROM
@@ -933,7 +946,7 @@ class EKF_CCU_EEPROM(EKF_EEPROM):
933
946
  area = self._content[
934
947
  self._cserial_index_start : self._cserial_index_start + self._cserial_length
935
948
  ]
936
- cserial = compute_int_from_bytes(area[::-1])
949
+ cserial = int.from_bytes(area, byteorder="little")
937
950
  return cserial
938
951
 
939
952
  def write_cserial(self, serial: int) -> None:
@@ -998,7 +1011,7 @@ class EKF_CCU_EEPROM(EKF_EEPROM):
998
1011
  The unit number of the subsystem.
999
1012
  """
1000
1013
  area = self._content[self._unit_index_start]
1001
- unit = compute_int_from_bytes([area])
1014
+ unit = int.from_bytes([area], byteorder="little")
1002
1015
  return unit
1003
1016
 
1004
1017
  @validated
@@ -1,12 +1,15 @@
1
- from .generic import Device
2
- from smbus2 import SMBus
1
+ import struct
3
2
  from enum import Enum
4
- from typing import Tuple
3
+ from typing import Any, Tuple
4
+
5
+ from smbus2 import SMBus
6
+
5
7
  from ekfsm.core.components import SysTree
8
+
6
9
  from ..exceptions import AcquisitionError
7
10
  from ..lock import Locker
11
+ from .generic import Device
8
12
  from .imu import ImuSample
9
- import struct
10
13
 
11
14
 
12
15
  class CcuCommands(Enum):
@@ -37,10 +40,13 @@ class EKFCcuUc(Device):
37
40
  self,
38
41
  name: str,
39
42
  parent: SysTree | None,
43
+ children: list["Device"] | None = None,
44
+ abort: bool = False,
45
+ debug: Any = None, # XXX: What is this?
40
46
  *args,
41
47
  **kwargs,
42
48
  ):
43
- super().__init__(name, parent, None, *args, **kwargs)
49
+ super().__init__(name, parent, None, abort, *args, **kwargs)
44
50
  self._i2c_addr = self.get_i2c_chip_addr()
45
51
  self._i2c_bus = self.get_i2c_bus_number()
46
52
  self._smbus = SMBus(self._i2c_bus)
@@ -69,10 +75,7 @@ class EKFCcuUc(Device):
69
75
  AcquisitionError
70
76
  If the temperature cannot be read, for example, because the sensor is not working.
71
77
  """
72
- return (
73
- self._get_signed_word_data(CcuCommands.CCU_TEMPERATURE.value, "temperature")
74
- / 10.0
75
- )
78
+ return self._get_signed_word_data(CcuCommands.CCU_TEMPERATURE.value, "temperature") / 10.0
76
79
 
77
80
  def humidity(self) -> float:
78
81
  """
@@ -89,10 +92,7 @@ class EKFCcuUc(Device):
89
92
  AcquisitionError
90
93
  If the humidity cannot be read, for example, because the sensor is not working.
91
94
  """
92
- return (
93
- self._get_signed_word_data(CcuCommands.CCU_HUMIDITY.value, "humidity")
94
- / 10.0
95
- )
95
+ return self._get_signed_word_data(CcuCommands.CCU_HUMIDITY.value, "humidity") / 10.0
96
96
 
97
97
  def vin_voltage(self) -> float:
98
98
  """
@@ -109,15 +109,14 @@ class EKFCcuUc(Device):
109
109
  AcquisitionError
110
110
  If the voltage cannot be read, for example, because the ADC is not working.
111
111
  """
112
- return (
113
- self._get_signed_word_data(CcuCommands.VIN_VOLTAGE.value, "VIN voltage")
114
- / 10.0
115
- )
112
+ return self._get_signed_word_data(CcuCommands.VIN_VOLTAGE.value, "VIN voltage") / 10.0
116
113
 
117
114
  def _get_signed_word_data(self, cmd: int, what: str) -> int:
118
115
  v = self._smbus.read_word_data(self._i2c_addr, cmd)
116
+
119
117
  if v == 0x8000:
120
- raise AcquisitionError(f"cannot read {what}")
118
+ raise AcquisitionError(f"Cannot read {what}")
119
+
121
120
  return struct.unpack("<h", struct.pack("<H", v))[0]
122
121
 
123
122
  def fan_status(self, fan: int) -> Tuple[float, float, int]:
@@ -171,9 +170,7 @@ class EKFCcuUc(Device):
171
170
  if fan == -1:
172
171
  fan = 0xFF
173
172
  data = struct.pack("<Bh", fan, int(temp * 10))
174
- self._smbus.write_block_data(
175
- self._i2c_addr, CcuCommands.PUSH_TEMPERATURE.value, list(data)
176
- )
173
+ self._smbus.write_block_data(self._i2c_addr, CcuCommands.PUSH_TEMPERATURE.value, list(data))
177
174
 
178
175
  def imu_sample(self) -> Tuple[ImuSample | None, bool]:
179
176
  """
@@ -200,15 +197,11 @@ class EKFCcuUc(Device):
200
197
  True if more samples are available in the FIFO, False otherwise.
201
198
  """
202
199
  more_samples = False
203
- _data = self._smbus.read_block_data(
204
- self._i2c_addr, CcuCommands.IMU_SAMPLES.value
205
- )
200
+ _data = self._smbus.read_block_data(self._i2c_addr, CcuCommands.IMU_SAMPLES.value)
206
201
  data = bytes(_data)
207
202
  if len(data) < 14:
208
203
  return None, False # No data available
209
- diag, fsr, acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z = struct.unpack(
210
- "<BBhhhhhh", data
211
- )
204
+ diag, fsr, acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z = struct.unpack("<BBhhhhhh", data)
212
205
  imu_data = ImuSample(
213
206
  [
214
207
  self._scale_imu_accel(acc_x, fsr),
@@ -274,15 +267,11 @@ class EKFCcuUc(Device):
274
267
  version: str
275
268
  The firmware version.
276
269
  """
277
- title = bytes(
278
- self._smbus.read_block_data(
279
- self._i2c_addr, CcuCommands.IDENTIFY_FIRMWARE_TITLE.value
280
- )
281
- ).decode("utf-8")
270
+ title = bytes(self._smbus.read_block_data(self._i2c_addr, CcuCommands.IDENTIFY_FIRMWARE_TITLE.value)).decode(
271
+ "utf-8"
272
+ )
282
273
  version = bytes(
283
- self._smbus.read_block_data(
284
- self._i2c_addr, CcuCommands.IDENTIFY_FIRMWARE_VERSION.value
285
- )
274
+ self._smbus.read_block_data(self._i2c_addr, CcuCommands.IDENTIFY_FIRMWARE_VERSION.value)
286
275
  ).decode("utf-8")
287
276
  return title, version
288
277
 
@@ -318,23 +307,26 @@ class EKFCcuUc(Device):
318
307
  with Locker(self.name + "-load_firmware").lock():
319
308
  offset = 0
320
309
  max_chunk_len = 28
310
+
321
311
  while len(firmware) > 0:
322
312
  chunk, firmware = firmware[:max_chunk_len], firmware[max_chunk_len:]
323
313
  self._load_firmware_chunk(offset, len(firmware) == 0, chunk)
324
314
  offset += len(chunk)
315
+
325
316
  if len(firmware) != 0:
326
317
  self._nop()
318
+
327
319
  if progress_callback is not None:
328
320
  progress_callback(offset)
329
321
 
330
322
  def _load_firmware_chunk(self, offset: int, is_last: bool, data: bytes) -> None:
331
323
  if is_last:
332
324
  offset |= 0x80000000
325
+
333
326
  hdr = struct.pack("<I", offset)
334
327
  data = hdr + data
335
- self._smbus.write_block_data(
336
- self._i2c_addr, CcuCommands.LOAD_FIRMWARE_CHUNK.value, list(data)
337
- )
328
+
329
+ self._smbus.write_block_data(self._i2c_addr, CcuCommands.LOAD_FIRMWARE_CHUNK.value, list(data))
338
330
 
339
331
  def get_parameterset(self) -> str:
340
332
  """
@@ -392,6 +384,7 @@ class EKFCcuUc(Device):
392
384
  with Locker(self.name + "-parameterset").lock():
393
385
  json = b""
394
386
  begin = True
387
+
395
388
  while True:
396
389
  chunk = self._get_parameterset_chunk(begin)
397
390
  if len(chunk) < 32:
@@ -401,16 +394,13 @@ class EKFCcuUc(Device):
401
394
  chunk = chunk[:-1]
402
395
  json += chunk
403
396
  begin = False
397
+
404
398
  return json.decode("utf-8")
405
399
 
406
400
  def _get_parameterset_chunk(self, begin: bool) -> bytes:
407
401
  data = self._smbus.read_block_data(
408
402
  self._i2c_addr,
409
- (
410
- CcuCommands.GET_PARAMETERSET_BEGIN.value
411
- if begin
412
- else CcuCommands.GET_PARAMETERSET_FOLLOW.value
413
- ),
403
+ (CcuCommands.GET_PARAMETERSET_BEGIN.value if begin else CcuCommands.GET_PARAMETERSET_FOLLOW.value),
414
404
  )
415
405
  return bytes(data)
416
406
 
@@ -455,6 +445,7 @@ class EKFCcuUc(Device):
455
445
  cfg = _cfg.encode("utf-8")
456
446
  offset = 0
457
447
  max_chunk_len = 28
448
+
458
449
  while len(cfg) > 0:
459
450
  chunk, cfg = cfg[:max_chunk_len], cfg[max_chunk_len:]
460
451
  self._load_parameterset_chunk(offset, len(cfg) == 0, chunk)
@@ -464,11 +455,11 @@ class EKFCcuUc(Device):
464
455
  def _load_parameterset_chunk(self, offset: int, is_last: bool, data: bytes) -> None:
465
456
  if is_last:
466
457
  offset |= 0x80000000
458
+
467
459
  hdr = struct.pack("<I", offset)
468
460
  data = hdr + data
469
- self._smbus.write_block_data(
470
- self._i2c_addr, CcuCommands.LOAD_PARAMETERSET.value, list(data)
471
- )
461
+
462
+ self._smbus.write_block_data(self._i2c_addr, CcuCommands.LOAD_PARAMETERSET.value, list(data))
472
463
 
473
464
  def restart(self) -> None:
474
465
  """
@@ -1,5 +1,7 @@
1
- from .gpio import GPIOExpander
2
1
  from ekfsm.core.components import SysTree
2
+ from ekfsm.devices.generic import Device
3
+
4
+ from .gpio import GPIOExpander
3
5
 
4
6
 
5
7
  class EKFSurLed(GPIOExpander):
@@ -11,10 +13,12 @@ class EKFSurLed(GPIOExpander):
11
13
  self,
12
14
  name: str,
13
15
  parent: SysTree | None,
16
+ children: list["Device"] | None = None,
17
+ abort: bool = False,
14
18
  *args,
15
19
  **kwargs,
16
20
  ):
17
- super().__init__(name, parent, None, *args, **kwargs)
21
+ super().__init__(name, parent, None, abort, *args, **kwargs)
18
22
 
19
23
  def __str__(self) -> str:
20
24
  return (