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.
@@ -0,0 +1,10 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,6 @@
1
+ Metadata-Version: 2.4
2
+ Name: burnintest-plugin
3
+ Version: 0.1.0
4
+ Summary: Python bindings for PassMark's BurnInTest plugin interface
5
+ Author-email: Timothy Lassiter <tim.lassiter@ruggedscience.com>
6
+ Requires-Python: >=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()
@@ -0,0 +1,7 @@
1
+ version = 1
2
+ revision = 2
3
+ requires-python = ">=3.11"
4
+
5
+ [[package]]
6
+ name = "burnintest-plugin"
7
+ source = { editable = "." }