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/mux.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
from ekfsm.core.components import SysTree
|
|
2
|
+
from ekfsm.log import ekfsm_logger
|
|
2
3
|
|
|
3
|
-
from ..core.sysfs import
|
|
4
|
+
from ..core.sysfs import SysfsDevice
|
|
4
5
|
from .generic import Device
|
|
5
6
|
|
|
7
|
+
logger = ekfsm_logger(__name__)
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
class MuxChannel(Device):
|
|
8
11
|
"""
|
|
@@ -25,20 +28,21 @@ class MuxChannel(Device):
|
|
|
25
28
|
def __init__(
|
|
26
29
|
self,
|
|
27
30
|
name: str,
|
|
28
|
-
channel_id: int,
|
|
29
31
|
parent: "I2CMux",
|
|
30
32
|
children: list[Device] | None = None,
|
|
33
|
+
abort=False,
|
|
34
|
+
channel_id: int | None = None,
|
|
31
35
|
*args,
|
|
32
36
|
**kwargs,
|
|
33
37
|
) -> None:
|
|
34
|
-
super().__init__(name
|
|
38
|
+
super().__init__(name, parent, children, abort, *args, **kwargs)
|
|
35
39
|
self.channel_id = channel_id
|
|
36
40
|
|
|
37
41
|
assert parent.sysfs_device is not None
|
|
38
42
|
assert isinstance(self.parent, I2CMux)
|
|
39
43
|
|
|
40
44
|
path = parent.sysfs_device.path / f"channel-{self.channel_id}"
|
|
41
|
-
self.sysfs_device =
|
|
45
|
+
self.sysfs_device = SysfsDevice(path, False)
|
|
42
46
|
|
|
43
47
|
|
|
44
48
|
class I2CMux(Device):
|
|
@@ -59,11 +63,12 @@ class I2CMux(Device):
|
|
|
59
63
|
self,
|
|
60
64
|
name: str,
|
|
61
65
|
parent: SysTree | None = None,
|
|
62
|
-
children: list[
|
|
66
|
+
children: list[Device] | None = None,
|
|
67
|
+
abort=False,
|
|
63
68
|
*args,
|
|
64
69
|
**kwargs,
|
|
65
70
|
):
|
|
66
|
-
super().__init__(name, parent, children, **kwargs)
|
|
71
|
+
super().__init__(name, parent, children, abort, *args, **kwargs)
|
|
67
72
|
|
|
68
73
|
self.addr = self.get_i2c_chip_addr()
|
|
69
74
|
self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
|
ekfsm/devices/pmbus.py
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from enum import IntFlag
|
|
2
|
-
from
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from time import sleep
|
|
3
5
|
|
|
4
6
|
from ekfsm.core.components import SysTree
|
|
7
|
+
from ekfsm.exceptions import HWMonError
|
|
8
|
+
from ekfsm.log import ekfsm_logger
|
|
5
9
|
|
|
6
|
-
from ..core.sysfs import SysFSDevice, sysfs_root
|
|
7
|
-
|
|
8
|
-
from .generic import Device
|
|
9
10
|
from ..core.probe import ProbeableDevice
|
|
11
|
+
from ..core.sysfs import list_sysfs_attributes, sysfs_root
|
|
12
|
+
from .generic import Device
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
from functools import wraps
|
|
13
|
-
from ekfsm.log import ekfsm_logger
|
|
14
|
-
from threading import Lock
|
|
15
|
-
import re
|
|
16
|
-
|
|
17
|
-
__all__ = ["PsuStatus", "PmBus", "retry"]
|
|
14
|
+
__all__ = ["PSUStatus", "PMBus", "retry"]
|
|
18
15
|
|
|
19
16
|
logger = ekfsm_logger(__name__)
|
|
20
17
|
|
|
@@ -34,8 +31,8 @@ def retry(max_attempts=5, delay=0.5):
|
|
|
34
31
|
|
|
35
32
|
Important
|
|
36
33
|
---------
|
|
37
|
-
This decorator is thread-safe
|
|
38
|
-
|
|
34
|
+
This decorator is _not_ thread-safe across multiple ekfsm processes. Unfortunately,
|
|
35
|
+
we cannot use fcntl or flock syscalls with files on virtual filesystems like sysfs.
|
|
39
36
|
|
|
40
37
|
Parameters
|
|
41
38
|
----------
|
|
@@ -45,26 +42,19 @@ def retry(max_attempts=5, delay=0.5):
|
|
|
45
42
|
The delay in seconds between attempts.
|
|
46
43
|
"""
|
|
47
44
|
|
|
48
|
-
lock = Lock()
|
|
49
|
-
|
|
50
45
|
def decorator(func):
|
|
51
46
|
@wraps(func)
|
|
52
47
|
def wrapper(*args, **kwargs):
|
|
53
48
|
attempts = 0
|
|
54
49
|
while attempts < max_attempts:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
64
|
-
raise e
|
|
65
|
-
logger.info(
|
|
66
|
-
f"Retrying execution of {func.__name__} in {delay}s..."
|
|
67
|
-
)
|
|
50
|
+
try:
|
|
51
|
+
return func(*args, **kwargs)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
attempts += 1
|
|
54
|
+
if attempts == max_attempts:
|
|
55
|
+
logger.exception(f"Failed to execute {func.__name__} after {max_attempts} attempts: {e}")
|
|
56
|
+
raise e
|
|
57
|
+
logger.info(f"Retrying execution of {func.__name__} in {delay}s...")
|
|
68
58
|
sleep(delay)
|
|
69
59
|
|
|
70
60
|
return wrapper
|
|
@@ -72,7 +62,7 @@ def retry(max_attempts=5, delay=0.5):
|
|
|
72
62
|
return decorator
|
|
73
63
|
|
|
74
64
|
|
|
75
|
-
class
|
|
65
|
+
class PSUStatus(IntFlag):
|
|
76
66
|
"""
|
|
77
67
|
Represents the status of a PSU according to STATUS_BYTE register.
|
|
78
68
|
|
|
@@ -93,9 +83,8 @@ class PsuStatus(IntFlag):
|
|
|
93
83
|
>>> PsuStatus.OK in status
|
|
94
84
|
True
|
|
95
85
|
>>> # Instead, check if status is OK
|
|
96
|
-
>>> status
|
|
97
|
-
|
|
98
|
-
<PsuStatus.OK: 0>
|
|
86
|
+
>>> status == PsuStatus(0x00)
|
|
87
|
+
False
|
|
99
88
|
>>> PsuStatus.OUTPUT_OVERCURRENT in status
|
|
100
89
|
False
|
|
101
90
|
"""
|
|
@@ -109,44 +98,47 @@ class PsuStatus(IntFlag):
|
|
|
109
98
|
OK = 0x00
|
|
110
99
|
|
|
111
100
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
This class represents a PMBus device (e.g. a PSU).
|
|
115
|
-
"""
|
|
101
|
+
logger = ekfsm_logger(__name__)
|
|
102
|
+
|
|
116
103
|
|
|
104
|
+
class PMBus(Device, ProbeableDevice):
|
|
117
105
|
def __init__(
|
|
118
106
|
self,
|
|
119
107
|
name: str,
|
|
120
108
|
parent: SysTree | None = None,
|
|
121
109
|
children: list[Device] | None = None,
|
|
110
|
+
abort: bool = False,
|
|
122
111
|
*args,
|
|
123
112
|
**kwargs,
|
|
124
113
|
):
|
|
125
|
-
super().__init__(name, parent, children, *args, **kwargs)
|
|
114
|
+
super().__init__(name, parent, children, abort, *args, **kwargs)
|
|
126
115
|
self.addr = self.get_i2c_chip_addr()
|
|
127
|
-
self.sysfs_device = self.get_i2c_sysfs_device(self.addr)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
116
|
+
self.sysfs_device = self.get_i2c_sysfs_device(self.addr, driver_required=True)
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
for entry in self.sysfs_device.path.glob("hwmon/hwmon*"):
|
|
120
|
+
if entry.is_dir():
|
|
121
|
+
attrs = list_sysfs_attributes(entry)
|
|
122
|
+
self.sysfs_device.extend_attributes(attrs)
|
|
123
|
+
|
|
124
|
+
debug_attrs_path = sysfs_root().joinpath(f"kernel/debug/pmbus/{entry.name}")
|
|
125
|
+
debug_attrs = list_sysfs_attributes(debug_attrs_path)
|
|
126
|
+
self.sysfs_device.extend_attributes(debug_attrs)
|
|
127
|
+
except FileNotFoundError:
|
|
128
|
+
logger.debug("Expected sysfs attribute not found")
|
|
129
|
+
except StopIteration:
|
|
130
|
+
raise HWMonError("Device is not managed by hwmon subsystem")
|
|
139
131
|
|
|
140
132
|
def probe(self, *args, **kwargs) -> bool:
|
|
141
|
-
from ekfsm.core import
|
|
133
|
+
from ekfsm.core import HWModule
|
|
142
134
|
|
|
143
|
-
assert isinstance(self.hw_module,
|
|
135
|
+
assert isinstance(self.hw_module, HWModule)
|
|
144
136
|
# compare the regexp from the board yaml file with the model
|
|
145
137
|
return re.match(self.hw_module.id, self.model()) is not None
|
|
146
138
|
|
|
147
139
|
# Voltage and Current Interfaces
|
|
148
|
-
def
|
|
149
|
-
return
|
|
140
|
+
def __convert_and_scale(self, attr: str) -> float:
|
|
141
|
+
return self.sysfs.read_float(attr) / 1000.0
|
|
150
142
|
|
|
151
143
|
@retry()
|
|
152
144
|
def in1_input(self) -> float:
|
|
@@ -157,7 +149,7 @@ class PmBus(Device, ProbeableDevice):
|
|
|
157
149
|
-------
|
|
158
150
|
Input voltage in volts
|
|
159
151
|
"""
|
|
160
|
-
return self.
|
|
152
|
+
return self.__convert_and_scale("in1_input")
|
|
161
153
|
|
|
162
154
|
@retry()
|
|
163
155
|
def in2_input(self) -> float:
|
|
@@ -168,7 +160,7 @@ class PmBus(Device, ProbeableDevice):
|
|
|
168
160
|
-------
|
|
169
161
|
Input voltage in volts
|
|
170
162
|
"""
|
|
171
|
-
return self.
|
|
163
|
+
return self.__convert_and_scale("in2_input")
|
|
172
164
|
|
|
173
165
|
@retry()
|
|
174
166
|
def curr1_input(self) -> float:
|
|
@@ -179,7 +171,7 @@ class PmBus(Device, ProbeableDevice):
|
|
|
179
171
|
-------
|
|
180
172
|
Input current in amperes
|
|
181
173
|
"""
|
|
182
|
-
return self.
|
|
174
|
+
return self.__convert_and_scale("curr1_input")
|
|
183
175
|
|
|
184
176
|
@retry()
|
|
185
177
|
def curr2_input(self) -> float:
|
|
@@ -190,32 +182,32 @@ class PmBus(Device, ProbeableDevice):
|
|
|
190
182
|
-------
|
|
191
183
|
Input current in amperes
|
|
192
184
|
"""
|
|
193
|
-
return self.
|
|
185
|
+
return self.__convert_and_scale("curr2_input")
|
|
194
186
|
|
|
195
187
|
# Status Interface
|
|
196
188
|
@retry()
|
|
197
|
-
def status0_input(self) ->
|
|
189
|
+
def status0_input(self) -> PSUStatus:
|
|
198
190
|
"""
|
|
199
191
|
Get the status of PSU page 1.
|
|
200
192
|
|
|
201
193
|
Returns
|
|
202
194
|
-------
|
|
203
|
-
PSU status as defined in
|
|
195
|
+
PSU status as defined in PSUStatus
|
|
204
196
|
"""
|
|
205
|
-
status =
|
|
206
|
-
return
|
|
197
|
+
status = self.sysfs.read_int("status0_input")
|
|
198
|
+
return PSUStatus(status)
|
|
207
199
|
|
|
208
200
|
@retry()
|
|
209
|
-
def status1_input(self) ->
|
|
201
|
+
def status1_input(self) -> PSUStatus:
|
|
210
202
|
"""
|
|
211
203
|
Get the status of PSU page 2.
|
|
212
204
|
|
|
213
205
|
Returns
|
|
214
206
|
-------
|
|
215
|
-
PSU status as defined in
|
|
207
|
+
PSU status as defined in PSUStatus
|
|
216
208
|
"""
|
|
217
|
-
status =
|
|
218
|
-
return
|
|
209
|
+
status = self.sysfs.read_int("status1_input")
|
|
210
|
+
return PSUStatus(status)
|
|
219
211
|
|
|
220
212
|
# Temperature Interface
|
|
221
213
|
@retry()
|
|
@@ -227,7 +219,7 @@ class PmBus(Device, ProbeableDevice):
|
|
|
227
219
|
-------
|
|
228
220
|
PSU temperature in degrees celsius
|
|
229
221
|
"""
|
|
230
|
-
return self.
|
|
222
|
+
return self.__convert_and_scale("temp1_input")
|
|
231
223
|
|
|
232
224
|
# Inventory Interface
|
|
233
225
|
def vendor(self) -> str:
|
|
@@ -238,7 +230,7 @@ class PmBus(Device, ProbeableDevice):
|
|
|
238
230
|
-------
|
|
239
231
|
PSU vendor
|
|
240
232
|
"""
|
|
241
|
-
return self.
|
|
233
|
+
return self.sysfs.read_utf8("vendor")
|
|
242
234
|
|
|
243
235
|
def model(self) -> str:
|
|
244
236
|
"""
|
|
@@ -248,7 +240,7 @@ class PmBus(Device, ProbeableDevice):
|
|
|
248
240
|
-------
|
|
249
241
|
PSU model
|
|
250
242
|
"""
|
|
251
|
-
return self.
|
|
243
|
+
return self.sysfs.read_utf8("model")
|
|
252
244
|
|
|
253
245
|
def serial(self) -> str:
|
|
254
246
|
"""
|
|
@@ -258,7 +250,7 @@ class PmBus(Device, ProbeableDevice):
|
|
|
258
250
|
-------
|
|
259
251
|
PSU serial number
|
|
260
252
|
"""
|
|
261
|
-
return self.
|
|
253
|
+
return self.sysfs.read_utf8("serial")
|
|
262
254
|
|
|
263
255
|
def revision(self) -> str:
|
|
264
256
|
"""
|
|
@@ -268,4 +260,4 @@ class PmBus(Device, ProbeableDevice):
|
|
|
268
260
|
-------
|
|
269
261
|
PSU revision
|
|
270
262
|
"""
|
|
271
|
-
return self.
|
|
263
|
+
return self.sysfs.read_utf8("revision")
|
ekfsm/devices/smbios.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
|
|
3
|
-
from ekfsm.core.
|
|
2
|
+
|
|
3
|
+
from ekfsm.core.components import HWModule
|
|
4
|
+
from ekfsm.core.sysfs import SysfsDevice, sysfs_root
|
|
5
|
+
|
|
4
6
|
from .generic import Device
|
|
5
7
|
|
|
6
8
|
|
|
@@ -18,15 +20,15 @@ class SMBIOS(Device):
|
|
|
18
20
|
def __init__(
|
|
19
21
|
self,
|
|
20
22
|
name: str,
|
|
21
|
-
parent:
|
|
23
|
+
parent: HWModule | None = None,
|
|
24
|
+
children: list["Device"] | None = None,
|
|
25
|
+
abort: bool = False,
|
|
22
26
|
*args,
|
|
23
27
|
**kwargs,
|
|
24
28
|
):
|
|
25
|
-
self.sysfs_device:
|
|
26
|
-
sysfs_root() / Path("devices/virtual/dmi/id")
|
|
27
|
-
)
|
|
29
|
+
self.sysfs_device: SysfsDevice | None = SysfsDevice(sysfs_root() / Path("devices/virtual/dmi/id"), False)
|
|
28
30
|
|
|
29
|
-
super().__init__(name, parent, None, *args, **kwargs)
|
|
31
|
+
super().__init__(name, parent, None, abort, *args, **kwargs)
|
|
30
32
|
|
|
31
33
|
def revision(self) -> str:
|
|
32
34
|
"""
|
|
@@ -37,4 +39,4 @@ class SMBIOS(Device):
|
|
|
37
39
|
str
|
|
38
40
|
The board revision.
|
|
39
41
|
"""
|
|
40
|
-
return self.
|
|
42
|
+
return self.sysfs.read_utf8("board_version")
|
ekfsm/devices/smbus.py
CHANGED
ekfsm/devices/utils.py
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
from crcmod.predefined import Crc
|
|
2
|
-
from typing import Sequence
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def compute_int_from_bytes(data: Sequence[int]) -> int:
|
|
6
|
-
# Combine the bytes into a single integer
|
|
7
|
-
result = 0
|
|
8
|
-
for num in data:
|
|
9
|
-
result = (result << 8) | num
|
|
10
|
-
return result
|
|
11
2
|
|
|
12
3
|
|
|
13
4
|
def get_crc16_xmodem(data: bytes) -> int:
|
ekfsm/exceptions.py
CHANGED
|
@@ -3,6 +3,7 @@ from enum import Enum
|
|
|
3
3
|
|
|
4
4
|
class EkfSmException(Exception):
|
|
5
5
|
"""Base class for all exceptions in the EKFSM Library"""
|
|
6
|
+
|
|
6
7
|
pass
|
|
7
8
|
|
|
8
9
|
|
|
@@ -12,7 +13,7 @@ class ConfigError(EkfSmException):
|
|
|
12
13
|
pass
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
class
|
|
16
|
+
class SysFSError(EkfSmException):
|
|
16
17
|
"""Error while handling sysfs pseudo file system"""
|
|
17
18
|
|
|
18
19
|
pass
|
|
@@ -31,9 +32,31 @@ class GPIOError(EkfSmException):
|
|
|
31
32
|
def __init__(self, error_type: ErrorType, details: str | None = None):
|
|
32
33
|
self.error_type = error_type
|
|
33
34
|
self.details = details
|
|
34
|
-
super().__init__(
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
super().__init__(f"{error_type.value}: {details}" if details else error_type.value)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class DriverError(EkfSmException):
|
|
39
|
+
"""No driver found for device"""
|
|
40
|
+
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class HWMonError(EkfSmException):
|
|
45
|
+
"""No HwMon entry found for device"""
|
|
46
|
+
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class ConversionError(EkfSmException):
|
|
51
|
+
"""Failed to convert"""
|
|
52
|
+
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class UnsupportedModeError(EkfSmException):
|
|
57
|
+
"""Format not supported"""
|
|
58
|
+
|
|
59
|
+
pass
|
|
37
60
|
|
|
38
61
|
|
|
39
62
|
class FirmwareNodeError(EkfSmException):
|
|
@@ -47,9 +70,7 @@ class DataCorruptionError(EkfSmException):
|
|
|
47
70
|
|
|
48
71
|
def __init__(self, details: str | None = None):
|
|
49
72
|
self.details = details
|
|
50
|
-
super().__init__(
|
|
51
|
-
f"Data corruption: {details}" if details else "Data corruption"
|
|
52
|
-
)
|
|
73
|
+
super().__init__(f"Data corruption: {details}" if details else "Data corruption")
|
|
53
74
|
|
|
54
75
|
|
|
55
76
|
class AcquisitionError(EkfSmException):
|
ekfsm/lock.py
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
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
|
-
from pathlib import Path
|
|
4
19
|
from contextlib import contextmanager
|
|
5
|
-
|
|
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
|
+
from pathlib import Path
|
|
20
21
|
|
|
21
22
|
USE_LOCK = True
|
|
22
23
|
LOCKFILE_ROOT = "/var/lock/ekfsm"
|
|
@@ -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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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)
|