burnintest-plugin 0.1.0__tar.gz
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.
- burnintest_plugin-0.1.0/.gitignore +10 -0
- burnintest_plugin-0.1.0/.python-version +1 -0
- burnintest_plugin-0.1.0/PKG-INFO +6 -0
- burnintest_plugin-0.1.0/README.md +0 -0
- burnintest_plugin-0.1.0/pyproject.toml +20 -0
- burnintest_plugin-0.1.0/src/burnintest_plugin/__init__.py +18 -0
- burnintest_plugin-0.1.0/src/burnintest_plugin/bitinterface.py +315 -0
- burnintest_plugin-0.1.0/uv.lock +7 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
dynamic = ["version"]
|
|
3
|
+
name = "burnintest-plugin"
|
|
4
|
+
description = "Python bindings for PassMark's BurnInTest plugin interface"
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Timothy Lassiter", email = "tim.lassiter@ruggedscience.com" }
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
classifiers = [
|
|
11
|
+
|
|
12
|
+
]
|
|
13
|
+
dependencies = []
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["hatchling", "hatch-vcs"]
|
|
17
|
+
build-backend = "hatchling.build"
|
|
18
|
+
|
|
19
|
+
[tool.hatch.version]
|
|
20
|
+
source = "vcs"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import importlib.metadata
|
|
2
|
+
|
|
3
|
+
# 1. Fetch dynamic version from hatch-vcs
|
|
4
|
+
try:
|
|
5
|
+
__version__ = importlib.metadata.version("burnintest-plugin")
|
|
6
|
+
except importlib.metadata.PackageNotFoundError:
|
|
7
|
+
__version__ = "0.0.0.dev0"
|
|
8
|
+
|
|
9
|
+
# 2. Import your public classes and enums from your internal modules
|
|
10
|
+
from .bitinterface import BitInterface, PluginStatus, ErrorSeverity
|
|
11
|
+
|
|
12
|
+
# 3. Explicitly declare your Public API
|
|
13
|
+
__all__ = [
|
|
14
|
+
"__version__",
|
|
15
|
+
"BitInterface",
|
|
16
|
+
"PluginStatus",
|
|
17
|
+
"ErrorSeverity",
|
|
18
|
+
]
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import enum
|
|
3
|
+
import time
|
|
4
|
+
from multiprocessing import shared_memory
|
|
5
|
+
from typing import Callable, Optional
|
|
6
|
+
|
|
7
|
+
PLUGIN_INTERFACE_VERSION: int = 4
|
|
8
|
+
|
|
9
|
+
PLUGIN_MAXDISPLAYTEXT: int = 20
|
|
10
|
+
PLUGIN_MAXERRORTEXT: int = 100
|
|
11
|
+
PLUGIN_MAXERRORTEXTLONG: int = 201
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PluginStatus(enum.Enum):
|
|
15
|
+
PLUGIN_NOSTATUS = 0 # Nothing interesting for BurnInTest to know about
|
|
16
|
+
PLUGIN_STARTUP = 1 # Not used - for future use
|
|
17
|
+
PLUGIN_ALLOCATE = 2 # Not used - for future use
|
|
18
|
+
PLUGIN_WRITING = 3 # Not used - for future use
|
|
19
|
+
PLUGIN_READING = 4 # Not used - for future use
|
|
20
|
+
PLUGIN_VERIFYING = 5 # Not used - for future use
|
|
21
|
+
PLUGIN_WAITING = 6 # Not used - for future use
|
|
22
|
+
PLUGIN_CLEANUP = 7 # Not used - for future use
|
|
23
|
+
PLUGIN_ERROR = 8 # Not used - for future use
|
|
24
|
+
PRE_TEST_PLUGIN_COMPLETED = 9 # If this is flagged in status then BurnInTest will close the interface to the Plugin and continue - for a pre-test plugin
|
|
25
|
+
PLUGIN_MAXVAL = 10 # Must always be one bigger than the last value
|
|
26
|
+
|
|
27
|
+
PLUGIN_NOTUSED = -1
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Error severity
|
|
31
|
+
class ErrorSeverity(enum.Enum):
|
|
32
|
+
ERRORNONE = 0
|
|
33
|
+
ERRORINFORMATION = 1
|
|
34
|
+
ERRORWARNING = 2
|
|
35
|
+
ERRORSERIOUS = 3
|
|
36
|
+
ERRORCRITICAL = 4
|
|
37
|
+
ERRORTERM = 5
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class BitInterfaceStructure(ctypes.Structure):
|
|
41
|
+
test_running: int
|
|
42
|
+
duty_cycle: int
|
|
43
|
+
interface_version: int
|
|
44
|
+
|
|
45
|
+
window_title: bytes
|
|
46
|
+
cycle: int
|
|
47
|
+
status: int
|
|
48
|
+
status_message: bytes
|
|
49
|
+
|
|
50
|
+
error_count: int
|
|
51
|
+
error_message: bytes
|
|
52
|
+
error_severity: int
|
|
53
|
+
|
|
54
|
+
write_operations_text: bytes
|
|
55
|
+
write_operations: int
|
|
56
|
+
|
|
57
|
+
read_operations_text: bytes
|
|
58
|
+
read_operations: int
|
|
59
|
+
|
|
60
|
+
verify_operations_text: bytes
|
|
61
|
+
verify_operations: int
|
|
62
|
+
|
|
63
|
+
user_defined_1_used: bool
|
|
64
|
+
user_defined_1_text: bytes
|
|
65
|
+
user_defined_1_value: bytes
|
|
66
|
+
|
|
67
|
+
user_defined_2_used: bool
|
|
68
|
+
user_defined_2_text: bytes
|
|
69
|
+
user_defined_2_value: bytes
|
|
70
|
+
|
|
71
|
+
user_defined_3_used: bool
|
|
72
|
+
user_defined_3_text: bytes
|
|
73
|
+
user_defined_3_value: bytes
|
|
74
|
+
|
|
75
|
+
user_defined_4_used: bool
|
|
76
|
+
user_defined_4_text: bytes
|
|
77
|
+
user_defined_4_value: bytes
|
|
78
|
+
|
|
79
|
+
user_defined_5_used: bool
|
|
80
|
+
user_defined_5_text: bytes
|
|
81
|
+
user_defined_5_value: bytes
|
|
82
|
+
|
|
83
|
+
user_defined_6_used: bool
|
|
84
|
+
user_defined_6_text: bytes
|
|
85
|
+
user_defined_6_value: bytes
|
|
86
|
+
|
|
87
|
+
new_display_text: bool
|
|
88
|
+
new_error: bool
|
|
89
|
+
new_status: bool
|
|
90
|
+
test_stopped: bool
|
|
91
|
+
|
|
92
|
+
error_message_long: bytes
|
|
93
|
+
|
|
94
|
+
_fields_ = [
|
|
95
|
+
# Data sent to plugin from BIT
|
|
96
|
+
("test_running", ctypes.c_int),
|
|
97
|
+
("duty_cycle", ctypes.c_int),
|
|
98
|
+
# Data sent to BIT from plugin
|
|
99
|
+
("interface_version", ctypes.c_int),
|
|
100
|
+
("window_title", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
101
|
+
("cycle", ctypes.c_uint),
|
|
102
|
+
("status", ctypes.c_int),
|
|
103
|
+
("status_message", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
104
|
+
("error_count", ctypes.c_int),
|
|
105
|
+
("error_message", ctypes.c_char * PLUGIN_MAXERRORTEXT),
|
|
106
|
+
("error_severity", ctypes.c_int),
|
|
107
|
+
("write_operations_text", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
108
|
+
("write_operations", ctypes.c_int64),
|
|
109
|
+
("read_operations_text", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
110
|
+
("read_operations", ctypes.c_int64),
|
|
111
|
+
("verify_operations_text", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
112
|
+
("verify_operations", ctypes.c_int64),
|
|
113
|
+
("user_defined_1_used", ctypes.c_bool),
|
|
114
|
+
("user_defined_1_text", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
115
|
+
("user_defined_1_value", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
116
|
+
("user_defined_2_used", ctypes.c_bool),
|
|
117
|
+
("user_defined_2_text", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
118
|
+
("user_defined_2_value", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
119
|
+
# Event flags
|
|
120
|
+
("new_display_text", ctypes.c_bool),
|
|
121
|
+
("new_error", ctypes.c_bool),
|
|
122
|
+
("new_status", ctypes.c_bool),
|
|
123
|
+
("new_user_defined_1_value", ctypes.c_bool),
|
|
124
|
+
("new_user_defined_2_value", ctypes.c_bool),
|
|
125
|
+
("test_stopped", ctypes.c_bool),
|
|
126
|
+
# V# of Interface. BurnInTest 6.0.1000 (BIT601000.0005)
|
|
127
|
+
# Data sent to BIT from plugin
|
|
128
|
+
("user_defined_3_used", ctypes.c_bool),
|
|
129
|
+
("user_defined_3_text", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
130
|
+
("user_defined_3_value", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
131
|
+
("user_defined_4_used", ctypes.c_bool),
|
|
132
|
+
("user_defined_4_text", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
133
|
+
("user_defined_4_value", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
134
|
+
("user_defined_5_used", ctypes.c_bool),
|
|
135
|
+
("user_defined_5_text", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
136
|
+
("user_defined_5_value", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
137
|
+
("user_defined_6_used", ctypes.c_bool),
|
|
138
|
+
("user_defined_6_text", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
139
|
+
("user_defined_6_value", ctypes.c_char * PLUGIN_MAXDISPLAYTEXT),
|
|
140
|
+
# V4 of Interface. BurnInTest 7.0.1000
|
|
141
|
+
("error_message_long", ctypes.c_char * PLUGIN_MAXERRORTEXTLONG),
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class BitInterface:
|
|
146
|
+
def __init__(
|
|
147
|
+
self,
|
|
148
|
+
key: str,
|
|
149
|
+
window_title: str,
|
|
150
|
+
wait_callback: Optional[Callable[[], None]] = None,
|
|
151
|
+
):
|
|
152
|
+
self._wait_callback = wait_callback or (lambda: time.sleep(0.01))
|
|
153
|
+
self._mem = shared_memory.SharedMemory(key, False)
|
|
154
|
+
self._struct = BitInterfaceStructure.from_buffer(self._mem.buf) # type: ignore
|
|
155
|
+
|
|
156
|
+
self._struct.interface_version = PLUGIN_INTERFACE_VERSION
|
|
157
|
+
|
|
158
|
+
self._struct.cycle = 0
|
|
159
|
+
self._struct.error_count = 0
|
|
160
|
+
self._struct.error_severity = ErrorSeverity.ERRORNONE.value
|
|
161
|
+
self._struct.status = PluginStatus.PLUGIN_NOSTATUS.value
|
|
162
|
+
|
|
163
|
+
self._struct.write_operations = -1
|
|
164
|
+
self._struct.read_operations = -1
|
|
165
|
+
self._struct.verify_operations = -1
|
|
166
|
+
|
|
167
|
+
self._struct.new_error = False
|
|
168
|
+
self._struct.new_status = False
|
|
169
|
+
self._struct.test_stopped = False
|
|
170
|
+
|
|
171
|
+
self._set_string(self._struct.window_title, window_title)
|
|
172
|
+
self._set_string(self._struct.status_message, "Starting")
|
|
173
|
+
self._set_string(self._struct.error_message, "")
|
|
174
|
+
self._set_string(self._struct.write_operations_text, "Write:")
|
|
175
|
+
self._set_string(self._struct.read_operations_text, "Read:")
|
|
176
|
+
self._set_string(self._struct.verify_operations_text, "Verify:")
|
|
177
|
+
self._struct.new_display_text = True
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def test_running(self) -> bool:
|
|
181
|
+
return bool(self._struct.test_running == 1)
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def duty_cycle(self) -> int:
|
|
185
|
+
return int(self._struct.duty_cycle)
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def cycle(self) -> int:
|
|
189
|
+
return int(self._struct.cycle)
|
|
190
|
+
|
|
191
|
+
@cycle.setter
|
|
192
|
+
def cycle(self, value: int) -> None:
|
|
193
|
+
self._struct.cycle = value
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def error_count(self) -> int:
|
|
197
|
+
return self._struct.error_count
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def write_operations_text(self) -> str:
|
|
201
|
+
return self._struct.write_operations_text.decode("utf-8")
|
|
202
|
+
|
|
203
|
+
@write_operations_text.setter
|
|
204
|
+
def write_operations_text(self, value: str) -> None:
|
|
205
|
+
self._set_string(self._struct.write_operations_text, value)
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def write_operations(self) -> int:
|
|
209
|
+
return self._struct.write_operations
|
|
210
|
+
|
|
211
|
+
@write_operations.setter
|
|
212
|
+
def write_operations(self, value: int) -> None:
|
|
213
|
+
self._struct.write_operations = value
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def read_operations_text(self) -> str:
|
|
217
|
+
return self._struct.read_operations_text.decode("utf-8")
|
|
218
|
+
|
|
219
|
+
@read_operations_text.setter
|
|
220
|
+
def read_operations_text(self, value: str) -> None:
|
|
221
|
+
self._set_string(self._struct.read_operations_text, value)
|
|
222
|
+
|
|
223
|
+
@property
|
|
224
|
+
def read_operations(self) -> int:
|
|
225
|
+
return self._struct.read_operations
|
|
226
|
+
|
|
227
|
+
@read_operations.setter
|
|
228
|
+
def read_operations(self, value: int) -> None:
|
|
229
|
+
self._struct.read_operations = value
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def verify_operations_text(self) -> str:
|
|
233
|
+
return self._struct.verify_operations_text.decode("utf-8")
|
|
234
|
+
|
|
235
|
+
@verify_operations_text.setter
|
|
236
|
+
def verify_operations_text(self, value: str) -> None:
|
|
237
|
+
self._set_string(self._struct.verify_operations_text, value)
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def verify_operations(self) -> int:
|
|
241
|
+
return self._struct.verify_operations
|
|
242
|
+
|
|
243
|
+
@verify_operations.setter
|
|
244
|
+
def verify_operations(self, value: int) -> None:
|
|
245
|
+
self._struct.verify_operations = value
|
|
246
|
+
|
|
247
|
+
def set_status(
|
|
248
|
+
self, status: PluginStatus, message: str, wait: bool = False
|
|
249
|
+
) -> None:
|
|
250
|
+
if len(message) > PLUGIN_MAXDISPLAYTEXT:
|
|
251
|
+
raise ValueError("Status message too long")
|
|
252
|
+
|
|
253
|
+
self._wait_for_status()
|
|
254
|
+
self._struct.status = status.value
|
|
255
|
+
self._set_string(self._struct.status_message, message)
|
|
256
|
+
self._struct.new_status = True
|
|
257
|
+
|
|
258
|
+
if wait:
|
|
259
|
+
self._wait_for_status()
|
|
260
|
+
|
|
261
|
+
def set_error(
|
|
262
|
+
self, severity: ErrorSeverity, message: str, wait: bool = False
|
|
263
|
+
) -> None:
|
|
264
|
+
if len(message) > PLUGIN_MAXERRORTEXT:
|
|
265
|
+
raise ValueError("Error message too long")
|
|
266
|
+
|
|
267
|
+
self._wait_for_error()
|
|
268
|
+
|
|
269
|
+
if severity.value > ErrorSeverity.ERRORWARNING.value:
|
|
270
|
+
self._struct.error_count += 1
|
|
271
|
+
|
|
272
|
+
self._struct.error_severity = severity.value
|
|
273
|
+
self._set_string(self._struct.error_message, message)
|
|
274
|
+
self._struct.new_error = True
|
|
275
|
+
|
|
276
|
+
if wait:
|
|
277
|
+
self._wait_for_error()
|
|
278
|
+
|
|
279
|
+
def set_pretest_complete(self, wait: bool = False) -> None:
|
|
280
|
+
self._struct.status = PluginStatus.PRE_TEST_PLUGIN_COMPLETED.value
|
|
281
|
+
self._struct.new_status = True
|
|
282
|
+
|
|
283
|
+
if wait:
|
|
284
|
+
self._wait_for_status()
|
|
285
|
+
|
|
286
|
+
def _set_string(self, struct_field, text: str) -> None:
|
|
287
|
+
"""Encodes string to utf-8 and truncates to fit the ctypes char array safely."""
|
|
288
|
+
max_bytes = len(struct_field) - 1 # Leave 1 byte for null terminator
|
|
289
|
+
encoded = text.encode("utf-8")[:max_bytes]
|
|
290
|
+
struct_field.value = encoded
|
|
291
|
+
|
|
292
|
+
def _wait_for_error(self) -> None:
|
|
293
|
+
while self.test_running and self._struct.new_error:
|
|
294
|
+
self._wait_callback()
|
|
295
|
+
|
|
296
|
+
def _wait_for_status(self) -> None:
|
|
297
|
+
while self.test_running and self._struct.new_status:
|
|
298
|
+
self._wait_callback()
|
|
299
|
+
|
|
300
|
+
def close(self):
|
|
301
|
+
try:
|
|
302
|
+
self._wait_for_error()
|
|
303
|
+
self._wait_for_status()
|
|
304
|
+
self._mem.close()
|
|
305
|
+
except Exception:
|
|
306
|
+
pass
|
|
307
|
+
|
|
308
|
+
def __enter__(self):
|
|
309
|
+
return self
|
|
310
|
+
|
|
311
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
312
|
+
self.close()
|
|
313
|
+
|
|
314
|
+
def __del__(self):
|
|
315
|
+
self.close()
|