ekfsm 1.1.0a15.post1__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/boards/oem/hitron/hdrc-300s.yaml +1 -1
- ekfsm/cli.py +6 -8
- ekfsm/config.py +14 -6
- ekfsm/core/__init__.py +5 -5
- ekfsm/core/components.py +4 -4
- ekfsm/core/slots.py +6 -13
- ekfsm/core/sysfs.py +183 -13
- ekfsm/core/utils.py +104 -64
- ekfsm/devices/__init__.py +8 -7
- ekfsm/devices/coretemp.py +11 -10
- ekfsm/devices/eeprom.py +69 -39
- ekfsm/devices/ekf_ccu_uc.py +58 -47
- ekfsm/devices/ekf_sur_led.py +6 -2
- ekfsm/devices/generic.py +141 -88
- ekfsm/devices/gpio.py +33 -25
- ekfsm/devices/iio.py +15 -31
- ekfsm/devices/iio_thermal_humidity.py +12 -13
- ekfsm/devices/mux.py +11 -6
- ekfsm/devices/pmbus.py +62 -70
- ekfsm/devices/smbios.py +10 -8
- ekfsm/devices/smbus.py +1 -1
- ekfsm/devices/utils.py +0 -9
- ekfsm/exceptions.py +28 -7
- ekfsm/lock.py +48 -21
- ekfsm/simctrl.py +37 -83
- ekfsm/system.py +45 -102
- ekfsm/utils.py +44 -0
- {ekfsm-1.1.0a15.post1.dist-info → ekfsm-1.3.0.dist-info}/METADATA +8 -5
- ekfsm-1.3.0.dist-info/RECORD +46 -0
- ekfsm-1.1.0a15.post1.dist-info/RECORD +0 -45
- {ekfsm-1.1.0a15.post1.dist-info → ekfsm-1.3.0.dist-info}/WHEEL +0 -0
- {ekfsm-1.1.0a15.post1.dist-info → ekfsm-1.3.0.dist-info}/entry_points.txt +0 -0
ekfsm/devices/coretemp.py
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
import glob
|
|
4
|
+
import os
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
121
|
-
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
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 =
|
|
1014
|
+
unit = int.from_bytes([area], byteorder="little")
|
|
1002
1015
|
return unit
|
|
1003
1016
|
|
|
1004
1017
|
@validated
|
|
@@ -1067,6 +1080,23 @@ class EKF_CCU_EEPROM(EKF_EEPROM):
|
|
|
1067
1080
|
def write_customer_area(self, data: bytes) -> None:
|
|
1068
1081
|
"""
|
|
1069
1082
|
Write data to CCU EEPROM customer area.
|
|
1083
|
+
|
|
1084
|
+
Parameters
|
|
1085
|
+
----------
|
|
1086
|
+
data
|
|
1087
|
+
The data to write to the customer area of the CCU EEPROM.
|
|
1088
|
+
|
|
1089
|
+
Raises
|
|
1090
|
+
------
|
|
1091
|
+
ValueError
|
|
1092
|
+
If the data exceeds the customer area length.
|
|
1093
|
+
|
|
1094
|
+
Example
|
|
1095
|
+
-------
|
|
1096
|
+
>>> eeprom = EKF_CCU_EEPROM()
|
|
1097
|
+
>>> eeprom.write_customer_area(b"Hello, World!")
|
|
1098
|
+
>>> eeprom.customer_area()
|
|
1099
|
+
b'Hello, World!'
|
|
1070
1100
|
"""
|
|
1071
1101
|
if len(data) > self._customer_area_length:
|
|
1072
1102
|
raise ValueError("Data exceeds customer area length")
|
ekfsm/devices/ekf_ccu_uc.py
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
|
|
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)
|
|
@@ -54,7 +60,10 @@ class EKFCcuUc(Device):
|
|
|
54
60
|
def temperature(self) -> float:
|
|
55
61
|
"""
|
|
56
62
|
Get the temperature from the CCU thermal/humidity sensor.
|
|
57
|
-
|
|
63
|
+
|
|
64
|
+
Note
|
|
65
|
+
----
|
|
66
|
+
The CCU reads the temperature once per second.
|
|
58
67
|
|
|
59
68
|
Returns
|
|
60
69
|
-------
|
|
@@ -66,10 +75,7 @@ class EKFCcuUc(Device):
|
|
|
66
75
|
AcquisitionError
|
|
67
76
|
If the temperature cannot be read, for example, because the sensor is not working.
|
|
68
77
|
"""
|
|
69
|
-
return (
|
|
70
|
-
self._get_signed_word_data(CcuCommands.CCU_TEMPERATURE.value, "temperature")
|
|
71
|
-
/ 10.0
|
|
72
|
-
)
|
|
78
|
+
return self._get_signed_word_data(CcuCommands.CCU_TEMPERATURE.value, "temperature") / 10.0
|
|
73
79
|
|
|
74
80
|
def humidity(self) -> float:
|
|
75
81
|
"""
|
|
@@ -86,10 +92,7 @@ class EKFCcuUc(Device):
|
|
|
86
92
|
AcquisitionError
|
|
87
93
|
If the humidity cannot be read, for example, because the sensor is not working.
|
|
88
94
|
"""
|
|
89
|
-
return (
|
|
90
|
-
self._get_signed_word_data(CcuCommands.CCU_HUMIDITY.value, "humidity")
|
|
91
|
-
/ 10.0
|
|
92
|
-
)
|
|
95
|
+
return self._get_signed_word_data(CcuCommands.CCU_HUMIDITY.value, "humidity") / 10.0
|
|
93
96
|
|
|
94
97
|
def vin_voltage(self) -> float:
|
|
95
98
|
"""
|
|
@@ -106,15 +109,14 @@ class EKFCcuUc(Device):
|
|
|
106
109
|
AcquisitionError
|
|
107
110
|
If the voltage cannot be read, for example, because the ADC is not working.
|
|
108
111
|
"""
|
|
109
|
-
return (
|
|
110
|
-
self._get_signed_word_data(CcuCommands.VIN_VOLTAGE.value, "VIN voltage")
|
|
111
|
-
/ 10.0
|
|
112
|
-
)
|
|
112
|
+
return self._get_signed_word_data(CcuCommands.VIN_VOLTAGE.value, "VIN voltage") / 10.0
|
|
113
113
|
|
|
114
114
|
def _get_signed_word_data(self, cmd: int, what: str) -> int:
|
|
115
115
|
v = self._smbus.read_word_data(self._i2c_addr, cmd)
|
|
116
|
+
|
|
116
117
|
if v == 0x8000:
|
|
117
|
-
raise AcquisitionError(f"
|
|
118
|
+
raise AcquisitionError(f"Cannot read {what}")
|
|
119
|
+
|
|
118
120
|
return struct.unpack("<h", struct.pack("<H", v))[0]
|
|
119
121
|
|
|
120
122
|
def fan_status(self, fan: int) -> Tuple[float, float, int]:
|
|
@@ -168,9 +170,7 @@ class EKFCcuUc(Device):
|
|
|
168
170
|
if fan == -1:
|
|
169
171
|
fan = 0xFF
|
|
170
172
|
data = struct.pack("<Bh", fan, int(temp * 10))
|
|
171
|
-
self._smbus.write_block_data(
|
|
172
|
-
self._i2c_addr, CcuCommands.PUSH_TEMPERATURE.value, list(data)
|
|
173
|
-
)
|
|
173
|
+
self._smbus.write_block_data(self._i2c_addr, CcuCommands.PUSH_TEMPERATURE.value, list(data))
|
|
174
174
|
|
|
175
175
|
def imu_sample(self) -> Tuple[ImuSample | None, bool]:
|
|
176
176
|
"""
|
|
@@ -197,15 +197,11 @@ class EKFCcuUc(Device):
|
|
|
197
197
|
True if more samples are available in the FIFO, False otherwise.
|
|
198
198
|
"""
|
|
199
199
|
more_samples = False
|
|
200
|
-
_data = self._smbus.read_block_data(
|
|
201
|
-
self._i2c_addr, CcuCommands.IMU_SAMPLES.value
|
|
202
|
-
)
|
|
200
|
+
_data = self._smbus.read_block_data(self._i2c_addr, CcuCommands.IMU_SAMPLES.value)
|
|
203
201
|
data = bytes(_data)
|
|
204
202
|
if len(data) < 14:
|
|
205
203
|
return None, False # No data available
|
|
206
|
-
diag, fsr, acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z = struct.unpack(
|
|
207
|
-
"<BBhhhhhh", data
|
|
208
|
-
)
|
|
204
|
+
diag, fsr, acc_x, acc_y, acc_z, gyro_x, gyro_y, gyro_z = struct.unpack("<BBhhhhhh", data)
|
|
209
205
|
imu_data = ImuSample(
|
|
210
206
|
[
|
|
211
207
|
self._scale_imu_accel(acc_x, fsr),
|
|
@@ -271,15 +267,11 @@ class EKFCcuUc(Device):
|
|
|
271
267
|
version: str
|
|
272
268
|
The firmware version.
|
|
273
269
|
"""
|
|
274
|
-
title = bytes(
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
)
|
|
278
|
-
).decode("utf-8")
|
|
270
|
+
title = bytes(self._smbus.read_block_data(self._i2c_addr, CcuCommands.IDENTIFY_FIRMWARE_TITLE.value)).decode(
|
|
271
|
+
"utf-8"
|
|
272
|
+
)
|
|
279
273
|
version = bytes(
|
|
280
|
-
self._smbus.read_block_data(
|
|
281
|
-
self._i2c_addr, CcuCommands.IDENTIFY_FIRMWARE_VERSION.value
|
|
282
|
-
)
|
|
274
|
+
self._smbus.read_block_data(self._i2c_addr, CcuCommands.IDENTIFY_FIRMWARE_VERSION.value)
|
|
283
275
|
).decode("utf-8")
|
|
284
276
|
return title, version
|
|
285
277
|
|
|
@@ -304,27 +296,37 @@ class EKFCcuUc(Device):
|
|
|
304
296
|
progress_callback
|
|
305
297
|
A callback function that is called with the current progress in bytes.
|
|
306
298
|
|
|
299
|
+
Example
|
|
300
|
+
-------
|
|
301
|
+
>>> from ekfsm.devices import EkfCcuUc
|
|
302
|
+
>>> ccu = EkfCcuUc("ccu")
|
|
303
|
+
>>> firmware = open("fw-ccu-1.0.0.bin", "rb").read()
|
|
304
|
+
>>> # Load firmware with progress callback
|
|
305
|
+
>>> ccu.load_firmware(firmware, progress_callback=lambda x: print(f"Progress: {x} bytes"))
|
|
307
306
|
"""
|
|
308
307
|
with Locker(self.name + "-load_firmware").lock():
|
|
309
308
|
offset = 0
|
|
310
309
|
max_chunk_len = 28
|
|
310
|
+
|
|
311
311
|
while len(firmware) > 0:
|
|
312
312
|
chunk, firmware = firmware[:max_chunk_len], firmware[max_chunk_len:]
|
|
313
313
|
self._load_firmware_chunk(offset, len(firmware) == 0, chunk)
|
|
314
314
|
offset += len(chunk)
|
|
315
|
+
|
|
315
316
|
if len(firmware) != 0:
|
|
316
317
|
self._nop()
|
|
318
|
+
|
|
317
319
|
if progress_callback is not None:
|
|
318
320
|
progress_callback(offset)
|
|
319
321
|
|
|
320
322
|
def _load_firmware_chunk(self, offset: int, is_last: bool, data: bytes) -> None:
|
|
321
323
|
if is_last:
|
|
322
324
|
offset |= 0x80000000
|
|
325
|
+
|
|
323
326
|
hdr = struct.pack("<I", offset)
|
|
324
327
|
data = hdr + data
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
)
|
|
328
|
+
|
|
329
|
+
self._smbus.write_block_data(self._i2c_addr, CcuCommands.LOAD_FIRMWARE_CHUNK.value, list(data))
|
|
328
330
|
|
|
329
331
|
def get_parameterset(self) -> str:
|
|
330
332
|
"""
|
|
@@ -382,6 +384,7 @@ class EKFCcuUc(Device):
|
|
|
382
384
|
with Locker(self.name + "-parameterset").lock():
|
|
383
385
|
json = b""
|
|
384
386
|
begin = True
|
|
387
|
+
|
|
385
388
|
while True:
|
|
386
389
|
chunk = self._get_parameterset_chunk(begin)
|
|
387
390
|
if len(chunk) < 32:
|
|
@@ -391,16 +394,13 @@ class EKFCcuUc(Device):
|
|
|
391
394
|
chunk = chunk[:-1]
|
|
392
395
|
json += chunk
|
|
393
396
|
begin = False
|
|
397
|
+
|
|
394
398
|
return json.decode("utf-8")
|
|
395
399
|
|
|
396
400
|
def _get_parameterset_chunk(self, begin: bool) -> bytes:
|
|
397
401
|
data = self._smbus.read_block_data(
|
|
398
402
|
self._i2c_addr,
|
|
399
|
-
(
|
|
400
|
-
CcuCommands.GET_PARAMETERSET_BEGIN.value
|
|
401
|
-
if begin
|
|
402
|
-
else CcuCommands.GET_PARAMETERSET_FOLLOW.value
|
|
403
|
-
),
|
|
403
|
+
(CcuCommands.GET_PARAMETERSET_BEGIN.value if begin else CcuCommands.GET_PARAMETERSET_FOLLOW.value),
|
|
404
404
|
)
|
|
405
405
|
return bytes(data)
|
|
406
406
|
|
|
@@ -423,6 +423,8 @@ class EKFCcuUc(Device):
|
|
|
423
423
|
This would load a parameterset with just one parameter, the default fan speed. All other parameters will
|
|
424
424
|
be set to their default values.
|
|
425
425
|
|
|
426
|
+
Important
|
|
427
|
+
---------
|
|
426
428
|
In order to apply the parameterset, the CCU must be restarted.
|
|
427
429
|
|
|
428
430
|
Parameters
|
|
@@ -430,11 +432,20 @@ class EKFCcuUc(Device):
|
|
|
430
432
|
_cfg
|
|
431
433
|
The parameterset in JSON format.
|
|
432
434
|
|
|
435
|
+
Example
|
|
436
|
+
-------
|
|
437
|
+
>>> from ekfsm.devices import EkfCcuUc
|
|
438
|
+
>>> ccu = EkfCcuUc("ccu")
|
|
439
|
+
>>> # Load parameterset
|
|
440
|
+
>>> ccu.load_parameterset('{"version": "1.0.0", "parameters": {"fan-defrpm": "6000"}}')
|
|
441
|
+
>>> # Restart CCU to apply parameterset
|
|
442
|
+
>>> ccu.restart()
|
|
433
443
|
"""
|
|
434
444
|
with Locker(self.name + "-parameterset").lock():
|
|
435
445
|
cfg = _cfg.encode("utf-8")
|
|
436
446
|
offset = 0
|
|
437
447
|
max_chunk_len = 28
|
|
448
|
+
|
|
438
449
|
while len(cfg) > 0:
|
|
439
450
|
chunk, cfg = cfg[:max_chunk_len], cfg[max_chunk_len:]
|
|
440
451
|
self._load_parameterset_chunk(offset, len(cfg) == 0, chunk)
|
|
@@ -444,11 +455,11 @@ class EKFCcuUc(Device):
|
|
|
444
455
|
def _load_parameterset_chunk(self, offset: int, is_last: bool, data: bytes) -> None:
|
|
445
456
|
if is_last:
|
|
446
457
|
offset |= 0x80000000
|
|
458
|
+
|
|
447
459
|
hdr = struct.pack("<I", offset)
|
|
448
460
|
data = hdr + data
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
)
|
|
461
|
+
|
|
462
|
+
self._smbus.write_block_data(self._i2c_addr, CcuCommands.LOAD_PARAMETERSET.value, list(data))
|
|
452
463
|
|
|
453
464
|
def restart(self) -> None:
|
|
454
465
|
"""
|
ekfsm/devices/ekf_sur_led.py
CHANGED
|
@@ -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 (
|