CrackNuts 0.0.1.dev2__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.
@@ -0,0 +1,548 @@
1
+ import abc
2
+ import socket
3
+ import struct
4
+ import threading
5
+ import typing
6
+ from abc import ABC
7
+ from dataclasses import dataclass
8
+
9
+ import numpy as np
10
+
11
+ import cracknuts.utils.hex_util as hex_util
12
+ from cracknuts import logger
13
+ from cracknuts.cracker import protocol
14
+
15
+
16
+ class Cracker(typing.Protocol):
17
+ def get_default_config(self) -> typing.Optional["Config"]: ...
18
+
19
+ def set_addr(self, ip, port) -> None: ...
20
+
21
+ def set_uri(self, uri: str) -> None: ...
22
+
23
+ def get_uri(self): ...
24
+
25
+ def connect(self): ...
26
+
27
+ def disconnect(self): ...
28
+
29
+ def reconnect(self): ...
30
+
31
+ def get_connection_status(self) -> bool: ...
32
+
33
+ def send_and_receive(self, message) -> tuple[int, bytes]: ...
34
+
35
+ def send_with_command(self, command: int, rfu: int = 0, payload: str | bytes = None) -> tuple[int, bytes]: ...
36
+
37
+ def echo(self, payload: str) -> str: ...
38
+
39
+ def echo_hex(self, payload: str) -> str: ...
40
+
41
+ def get_id(self) -> str | None: ...
42
+
43
+ def get_name(self) -> str | None: ...
44
+
45
+ def get_version(self) -> str | None: ...
46
+
47
+ def osc_set_analog_channel_enable(self, enable: dict[int, bool]): ...
48
+
49
+ def osc_set_analog_coupling(self, coupling: dict[int, int]): ...
50
+
51
+ def osc_set_analog_voltage(self, channel: int, voltage: int): ...
52
+
53
+ def osc_set_analog_bias_voltage(self, channel: int, voltage: int): ...
54
+
55
+ def osc_set_digital_channel_enable(self, enable: dict[int, bool]): ...
56
+
57
+ def osc_set_digital_voltage(self, voltage: int): ...
58
+
59
+ def osc_set_trigger_mode(self, source: int, stop: int): ...
60
+
61
+ def osc_set_analog_trigger_source(self, channel: int): ...
62
+
63
+ def osc_set_digital_trigger_source(self, channel: int): ...
64
+
65
+ def osc_set_analog_trigger_voltage(self, voltage: int): ...
66
+
67
+ def osc_set_sample_delay(self, delay: int): ...
68
+
69
+ def osc_set_sample_len(self, length: int): ...
70
+
71
+ def osc_single(self): ...
72
+
73
+ def osc_force(self): ...
74
+
75
+ def osc_is_triggered(self): ...
76
+
77
+ def osc_get_analog_wave(self, channel: int, offset: int, sample_count: int) -> tuple[int, np.ndarray]: ...
78
+
79
+ def osc_get_digital_wave(self, channel: int, offset: int, sample_count: int): ...
80
+
81
+ def osc_set_analog_gain(self, channel: int, gain: int): ...
82
+
83
+ def osc_set_analog_gain_raw(self, channel: int, gain: int): ...
84
+
85
+ def osc_set_sample_clock(self, clock: int): ...
86
+
87
+ def osc_set_sample_phase(self, phase: int): ...
88
+
89
+ def nut_enable(self, enable: int): ...
90
+
91
+ def nut_voltage(self, voltage: int): ...
92
+
93
+ def nut_voltage_raw(self, voltage: int): ...
94
+
95
+ def nut_clock(self, clock: int): ...
96
+
97
+ def nut_interface(self, interface: dict[int, bool]): ...
98
+
99
+ def nut_timeout(self, timeout: int): ...
100
+
101
+ def cracker_serial_baud(self, baud: int): ...
102
+
103
+ def cracker_serial_width(self, width: int): ...
104
+
105
+ def cracker_serial_stop(self, stop: int): ...
106
+
107
+ def cracker_serial_odd_eve(self, odd_eve: int): ...
108
+
109
+ def cracker_serial_data(self, expect_len: int, data: bytes): ...
110
+
111
+ def cracker_spi_cpol(self, cpol: int): ...
112
+
113
+ def cracker_spi_cpha(self, cpha: int): ...
114
+
115
+ def cracker_spi_data_len(self, cpha: int): ...
116
+
117
+ def cracker_spi_freq(self, freq: int): ...
118
+
119
+ def cracker_spi_timeout(self, timeout: int): ...
120
+
121
+ def cracker_spi_data(self, expect_len: int, data: bytes): ...
122
+
123
+ def cracker_i2c_freq(self, freq: int): ...
124
+
125
+ def cracker_i2c_timeout(self, timeout: int): ...
126
+
127
+ def cracker_i2c_data(self, expect_len: int, data: bytes): ...
128
+
129
+ def cracker_can_freq(self, freq: int): ...
130
+
131
+ def cracker_can_timeout(self, timeout: int): ...
132
+
133
+ def cracker_can_data(self, expect_len: int, data: bytes): ...
134
+
135
+
136
+ class Commands:
137
+ """
138
+ Protocol commands.
139
+ """
140
+
141
+ GET_ID = 0x0001
142
+ GET_NAME = 0x0002
143
+ GET_VERSION = 0x0003
144
+
145
+ OSC_ANALOG_CHANNEL_ENABLE = 0x0100
146
+ OSC_ANALOG_COUPLING = 0x0101
147
+ OSC_ANALOG_VOLTAGE = 0x0102
148
+ OSC_ANALOG_BIAS_VOLTAGE = 0x0103
149
+ OSC_ANALOG_GAIN = 0x0104
150
+ OSC_ANALOG_GAIN_RAW = 0x0107
151
+
152
+ OSC_SAMPLE_CLOCK = 0x0105
153
+ OSC_SAMPLE_PHASE = 0x0106
154
+
155
+ OSC_DIGITAL_CHANNEL_ENABLE = 0x0110
156
+ OSC_DIGITAL_VOLTAGE = 0x0111
157
+
158
+ OSC_TRIGGER_MODE = 0x0120
159
+
160
+ OSC_ANALOG_TRIGGER_SOURCE = 0x0121
161
+ OSC_DIGITAL_TRIGGER_SOURCE = 0x0122
162
+
163
+ OSC_ANALOG_TRIGGER_VOLTAGE = 0x0123
164
+
165
+ OSC_SAMPLE_DELAY = 0x0124
166
+
167
+ OSC_SAMPLE_LENGTH = 0x0125
168
+ OSC_SAMPLE_RATE = 0x0128
169
+
170
+ OSC_SINGLE = 0x0126
171
+
172
+ OSC_IS_TRIGGERED = 0x0127
173
+ OSC_FORCE = 0x0129
174
+
175
+ OSC_GET_ANALOG_WAVES = 0x0130
176
+ OSC_GET_DIGITAL_WAVES = 0x0130
177
+
178
+ NUT_ENABLE = 0x0200
179
+ NUT_VOLTAGE = 0x0201
180
+ NUT_VOLTAGE_RAW = 0x0203
181
+ NUT_CLOCK = 0x0202
182
+ NUT_INTERFACE = 0x0210
183
+ NUT_TIMEOUT = 0x0224
184
+
185
+ CRACKER_SERIAL_BAUD = 0x0220
186
+ CRACKER_SERIAL_WIDTH = 0x0221
187
+ CRACKER_SERIAL_STOP = 0x0222
188
+ CRACKER_SERIAL_ODD_EVE = 0x0223
189
+ CRACKER_SERIAL_DATA = 0x022A
190
+
191
+ CRACKER_SPI_CPOL = 0x0230
192
+ CRACKER_SPI_CPHA = 0x0231
193
+ CRACKER_SPI_DATA_LEN = 0x0232
194
+ CRACKER_SPI_FREQ = 0x0233
195
+ CRACKER_SPI_TIMEOUT = 0x0234
196
+ CRACKER_SPI_DATA = 0x023A
197
+
198
+ CRACKER_I2C_FREQ = 0x0240
199
+ CRACKER_I2C_TIMEOUT = 0x0244
200
+ CRACKER_I2C_DATA = 0x024A
201
+
202
+ CRACKER_CAN_FREQ = 0x0250
203
+ CRACKER_CAN_TIMEOUT = 0x0254
204
+ CRACKER_CA_DATA = 0x025A
205
+
206
+
207
+ @dataclass
208
+ class Config:
209
+ def __init__(
210
+ self,
211
+ nut_enable: bool = None,
212
+ nut_voltage: int = None,
213
+ nut_clock: int = None,
214
+ osc_analog_channel_enable: dict[int, bool] = None,
215
+ osc_sample_len: int = None,
216
+ osc_sample_delay: int = None,
217
+ osc_sample_phase: int = None,
218
+ osc_sample_clock: int = None,
219
+ ):
220
+ self._binder: dict[str, callable] = {}
221
+ self.nut_enable: bool = nut_enable
222
+ self.nut_voltage: int = nut_voltage
223
+ self.nut_clock: int = nut_clock
224
+ self.osc_sample_len = osc_sample_len
225
+ self.osc_sample_delay = osc_sample_delay
226
+ self.osc_sample_phase = osc_sample_phase
227
+ self.osc_sample_clock = osc_sample_clock
228
+ self.osc_analog_channel_enable: dict[int, bool] = osc_analog_channel_enable
229
+
230
+ def __setattr__(self, key, value):
231
+ self.__dict__[key] = value
232
+ if "_binder" in self.__dict__ and (binder := self._binder.get(key)) is not None:
233
+ binder(value)
234
+
235
+ def bind(self, key: str, callback: callable):
236
+ """
237
+ Bind a callback which will be call when the key field is updated.
238
+ :param key: a filed name of class `Config`
239
+ :param callback:
240
+ :return:
241
+ """
242
+ self._binder[key] = callback
243
+
244
+
245
+ class AbsCnpCracker(ABC, Cracker):
246
+ """Cracker
247
+ Cracker
248
+ """
249
+
250
+ def __init__(self, server_address=None):
251
+ """
252
+ :param server_address: Cracker device address (ip, port)
253
+ """
254
+ self._command_lock = threading.Lock()
255
+ self._logger = logger.get_logger(self)
256
+ self._server_address = server_address
257
+ self._socket: socket.socket | None = None
258
+ self._connection_status = False
259
+ self._channel_enable: dict = {
260
+ 0: False,
261
+ 1: False,
262
+ }
263
+
264
+ @abc.abstractmethod
265
+ def get_default_config(self) -> Config | None: ...
266
+
267
+ def set_addr(self, ip, port) -> None:
268
+ self._server_address = ip, port
269
+
270
+ def set_uri(self, uri: str) -> None:
271
+ if not uri.startswith("cnp://") and uri.count(":") < 2:
272
+ uri = "cnp://" + uri
273
+
274
+ uri = uri.replace("cnp://", "", 1)
275
+ if ":" in uri:
276
+ host, port = uri.split(":")
277
+ else:
278
+ host, port = uri, protocol.DEFAULT_PORT
279
+
280
+ self._server_address = host, int(port)
281
+
282
+ def get_uri(self):
283
+ if self._server_address is None:
284
+ return None
285
+ else:
286
+ return f"cnp://{self._server_address[0]}:{self._server_address[1]}"
287
+
288
+ def connect(self):
289
+ """
290
+ Connect to Cracker device.
291
+ :return: Cracker self.
292
+ """
293
+ try:
294
+ if not self._socket:
295
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
296
+ self._socket.settimeout(5)
297
+ self._socket.connect(self._server_address)
298
+ self._connection_status = True
299
+ self._logger.info(f"Connected to cracker: {self._server_address}")
300
+ except OSError as e:
301
+ self._logger.error("Connection failed: %s", e)
302
+ self._connection_status = False
303
+
304
+ def disconnect(self):
305
+ """
306
+ Disconnect Cracker device.
307
+ :return: Cracker self.
308
+ """
309
+ try:
310
+ if self._socket:
311
+ self._socket.close()
312
+ self._socket = None
313
+ self._logger.info(f"Disconnect from {self._server_address}")
314
+ except OSError as e:
315
+ self._logger.error("Disconnection failed: %s", e)
316
+ finally:
317
+ self._connection_status = False
318
+
319
+ def reconnect(self):
320
+ """
321
+ Reconnect to Cracker device.
322
+ :return: Cracker self.
323
+ """
324
+ self.disconnect()
325
+ self.connect()
326
+
327
+ def get_connection_status(self) -> bool:
328
+ """
329
+ Get connection status.
330
+ :return: True or False
331
+ """
332
+ return self._connection_status
333
+
334
+ def send_and_receive(self, message) -> tuple[int, bytes | None]:
335
+ """
336
+ Send message to socket
337
+ :param message:
338
+ :return:
339
+ """
340
+ try:
341
+ self._command_lock.acquire()
342
+ if not self.get_connection_status():
343
+ self._logger.error("Cracker is not connected.")
344
+ return protocol.STATUS_ERROR, None
345
+ self._logger.debug(f"Send message to {self._server_address}: \n{hex_util.get_bytes_matrix(message)}")
346
+ self._socket.sendall(message)
347
+ resp_header = self._socket.recv(protocol.RES_HEADER_SIZE)
348
+ self._logger.debug(
349
+ "Get response header from %s: \n%s",
350
+ self._server_address,
351
+ hex_util.get_bytes_matrix(resp_header),
352
+ )
353
+ magic, version, direction, status, length = struct.unpack(protocol.RES_HEADER_FORMAT, resp_header)
354
+ self._logger.debug(
355
+ f"Receive header from {self._server_address}: {magic}, {version}, {direction}, {status:02X}, {length}"
356
+ )
357
+ if status >= protocol.STATUS_ERROR:
358
+ self._logger.error(f"Receive status error: {status:02X}")
359
+ if length == 0:
360
+ return status, None
361
+ resp_payload = self._recv(length)
362
+ if status >= protocol.STATUS_ERROR:
363
+ self._logger.error(
364
+ "Receive payload from {self._server_address}: \n{hex_util.get_bytes_matrix(resp_payload)}"
365
+ )
366
+ else:
367
+ self._logger.debug(
368
+ "Receive payload from {self._server_address}: \n{hex_util.get_bytes_matrix(resp_payload)}"
369
+ )
370
+ return status, resp_payload
371
+ except OSError as e:
372
+ self._logger.error("Send message failed: %s, and msg: %s", e, message)
373
+ return protocol.STATUS_ERROR, None
374
+ finally:
375
+ self._command_lock.release()
376
+
377
+ def _recv(self, length):
378
+ resp_payload = b""
379
+ while (received_len := len(resp_payload)) < length:
380
+ for_receive_len = length - received_len
381
+ resp_payload += self._socket.recv(for_receive_len)
382
+
383
+ return resp_payload
384
+
385
+ def send_with_command(self, command: int, rfu: int = 0, payload: str | bytes = None) -> tuple[int, bytes]:
386
+ if isinstance(payload, str):
387
+ payload = bytes.fromhex(payload)
388
+ return self.send_and_receive(protocol.build_send_message(command, rfu, payload))
389
+
390
+ def echo(self, payload: str) -> str:
391
+ """
392
+ length <= 1024
393
+ """
394
+ self._socket.sendall(payload.encode("ascii"))
395
+ res = self._socket.recv(1024).decode("ascii")
396
+ self._logger.debug(f"Get response: {res}")
397
+ return res
398
+
399
+ def echo_hex(self, payload: str) -> str:
400
+ """
401
+ length <= 1024
402
+ """
403
+ payload = bytes.fromhex(payload)
404
+ self._socket.sendall(payload)
405
+ res = self._socket.recv(1024).hex()
406
+ self._logger.debug(f"Get response: {res}")
407
+ return res
408
+
409
+ @abc.abstractmethod
410
+ def get_id(self) -> str | None: ...
411
+
412
+ @abc.abstractmethod
413
+ def get_name(self) -> str | None: ...
414
+
415
+ def get_version(self) -> str | None:
416
+ return super().get_version()
417
+
418
+ @abc.abstractmethod
419
+ def osc_set_analog_channel_enable(self, enable: dict[int, bool]): ...
420
+
421
+ @abc.abstractmethod
422
+ def osc_set_analog_coupling(self, coupling: dict[int, int]): ...
423
+
424
+ @abc.abstractmethod
425
+ def osc_set_analog_voltage(self, channel: int, voltage: int): ...
426
+
427
+ @abc.abstractmethod
428
+ def osc_set_analog_bias_voltage(self, channel: int, voltage: int): ...
429
+
430
+ @abc.abstractmethod
431
+ def osc_set_digital_channel_enable(self, enable: dict[int, bool]): ...
432
+
433
+ @abc.abstractmethod
434
+ def osc_set_digital_voltage(self, voltage: int): ...
435
+
436
+ @abc.abstractmethod
437
+ def osc_set_trigger_mode(self, source: int, stop: int): ...
438
+
439
+ @abc.abstractmethod
440
+ def osc_set_analog_trigger_source(self, channel: int): ...
441
+
442
+ @abc.abstractmethod
443
+ def osc_set_digital_trigger_source(self, channel: int): ...
444
+
445
+ @abc.abstractmethod
446
+ def osc_set_analog_trigger_voltage(self, voltage: int): ...
447
+
448
+ @abc.abstractmethod
449
+ def osc_set_sample_delay(self, delay: int): ...
450
+
451
+ @abc.abstractmethod
452
+ def osc_set_sample_len(self, length: int): ...
453
+
454
+ @abc.abstractmethod
455
+ def osc_single(self): ...
456
+
457
+ @abc.abstractmethod
458
+ def osc_force(self): ...
459
+
460
+ @abc.abstractmethod
461
+ def osc_is_triggered(self): ...
462
+
463
+ @abc.abstractmethod
464
+ def osc_get_analog_wave(self, channel: int, offset: int, sample_count: int) -> tuple[int, np.ndarray]: ...
465
+
466
+ @abc.abstractmethod
467
+ def osc_get_digital_wave(self, channel: int, offset: int, sample_count: int): ...
468
+
469
+ @abc.abstractmethod
470
+ def osc_set_analog_gain(self, channel: int, gain: int): ...
471
+
472
+ @abc.abstractmethod
473
+ def osc_set_analog_gain_raw(self, channel: int, gain: int): ...
474
+
475
+ @abc.abstractmethod
476
+ def osc_set_sample_clock(self, clock: int): ...
477
+
478
+ @abc.abstractmethod
479
+ def osc_set_sample_phase(self, phase: int): ...
480
+
481
+ @abc.abstractmethod
482
+ def nut_enable(self, enable: int): ...
483
+
484
+ @abc.abstractmethod
485
+ def nut_voltage(self, voltage: int): ...
486
+
487
+ @abc.abstractmethod
488
+ def nut_voltage_raw(self, voltage: int): ...
489
+
490
+ @abc.abstractmethod
491
+ def nut_clock(self, clock: int): ...
492
+
493
+ @abc.abstractmethod
494
+ def nut_interface(self, interface: dict[int, bool]): ...
495
+
496
+ @abc.abstractmethod
497
+ def nut_timeout(self, timeout: int): ...
498
+
499
+ @abc.abstractmethod
500
+ def cracker_serial_baud(self, baud: int): ...
501
+
502
+ @abc.abstractmethod
503
+ def cracker_serial_width(self, width: int): ...
504
+
505
+ @abc.abstractmethod
506
+ def cracker_serial_stop(self, stop: int): ...
507
+
508
+ @abc.abstractmethod
509
+ def cracker_serial_odd_eve(self, odd_eve: int): ...
510
+
511
+ @abc.abstractmethod
512
+ def cracker_serial_data(self, expect_len: int, data: bytes): ...
513
+
514
+ @abc.abstractmethod
515
+ def cracker_spi_cpol(self, cpol: int): ...
516
+
517
+ @abc.abstractmethod
518
+ def cracker_spi_cpha(self, cpha: int): ...
519
+
520
+ @abc.abstractmethod
521
+ def cracker_spi_data_len(self, cpha: int): ...
522
+
523
+ @abc.abstractmethod
524
+ def cracker_spi_freq(self, freq: int): ...
525
+
526
+ @abc.abstractmethod
527
+ def cracker_spi_timeout(self, timeout: int): ...
528
+
529
+ @abc.abstractmethod
530
+ def cracker_spi_data(self, expect_len: int, data: bytes): ...
531
+
532
+ @abc.abstractmethod
533
+ def cracker_i2c_freq(self, freq: int): ...
534
+
535
+ @abc.abstractmethod
536
+ def cracker_i2c_timeout(self, timeout: int): ...
537
+
538
+ @abc.abstractmethod
539
+ def cracker_i2c_data(self, expect_len: int, data: bytes): ...
540
+
541
+ @abc.abstractmethod
542
+ def cracker_can_freq(self, freq: int): ...
543
+
544
+ @abc.abstractmethod
545
+ def cracker_can_timeout(self, timeout: int): ...
546
+
547
+ @abc.abstractmethod
548
+ def cracker_can_data(self, expect_len: int, data: bytes): ...
@@ -0,0 +1,163 @@
1
+ import logging
2
+ import socket
3
+ import struct
4
+ import sys
5
+ import threading
6
+ import time
7
+
8
+ import numpy as np
9
+
10
+ from cracknuts import logger
11
+ from cracknuts.cracker import protocol, cracker
12
+ from cracknuts.utils import hex_util
13
+
14
+ _handler_dict = {}
15
+
16
+
17
+ def _handler(command: int, has_payload: bool = True):
18
+ def decorator(func):
19
+ def wrapper(*args, **kwargs):
20
+ if not has_payload:
21
+ del kwargs["payload"]
22
+ return func(*args, **kwargs)
23
+
24
+ _handler_dict[command] = wrapper
25
+ return wrapper
26
+
27
+ return decorator
28
+
29
+
30
+ class MockCracker:
31
+ def __init__(self, host: str = "127.0.0.1", port: int = protocol.DEFAULT_PORT, logging_level=logging.INFO):
32
+ self._logger = logger.get_logger(MockCracker, logging_level)
33
+ self._server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
34
+ self._server_socket.settimeout(1)
35
+ self._server_socket.bind((host, port))
36
+ self._server_socket.listen()
37
+ self._logger.debug("MockCracker initialized")
38
+
39
+ def start(self):
40
+ try:
41
+ print(f"MockCracker listen on {self._server_socket.getsockname()}")
42
+ self._start_accept()
43
+ except KeyboardInterrupt:
44
+ sys.exit(0)
45
+
46
+ def _start_accept(self):
47
+ while True:
48
+ try:
49
+ conn, addr = self._server_socket.accept()
50
+ conn.settimeout(5)
51
+ self._logger.debug(f"Get client request from {addr}.")
52
+ threading.Thread(target=self._handle, args=(conn,), daemon=True).start()
53
+ # self._handle(conn)
54
+ except TimeoutError:
55
+ continue
56
+ except KeyboardInterrupt:
57
+ self._logger.debug("exist by keyboard interrupt.")
58
+ sys.exit(0)
59
+
60
+ def _handle(self, conn):
61
+ while True:
62
+ try:
63
+ header_data = conn.recv(protocol.REQ_HEADER_SIZE)
64
+ if not header_data:
65
+ self._logger.debug("disconnected...")
66
+ break
67
+
68
+ self._logger.debug(f"Received header:\n{hex_util.get_bytes_matrix(header_data)}")
69
+
70
+ try:
71
+ magic, version, direction, command, rfu, length = struct.unpack(
72
+ protocol.REQ_HEADER_FORMAT, header_data
73
+ )
74
+ except struct.error as e:
75
+ err_msg = f"Message format error: {e.args[0]}\n"
76
+ self._logger.error(err_msg)
77
+ err_res = self._error(f"Header format error: {hex_util.get_hex(header_data)}")
78
+ self._logger.debug(f"Send error:\n{hex_util.get_bytes_matrix(err_res)}")
79
+ conn.sendall(err_res)
80
+ continue
81
+
82
+ self._logger.debug(
83
+ f"Received header: Magic={magic}, Version={version}, Direction={direction}, "
84
+ f"Command={command}, Length={length}"
85
+ )
86
+
87
+ payload_data = conn.recv(length)
88
+ if len(payload_data) > 0:
89
+ self._logger.debug(f"Received payload:\n{hex_util.get_bytes_matrix(payload_data)}")
90
+ if command not in _handler_dict:
91
+ self._logger.warning(f'Get command not supported: 0x{format(command, '04x')}')
92
+ unsupported_res = self._unsupported(command)
93
+ self._logger.debug(f"Send unsupported:\n{hex_util.get_bytes_matrix(unsupported_res)}")
94
+ conn.sendall(unsupported_res)
95
+ continue
96
+ func_res = _handler_dict.get(command, None)(self, payload=payload_data)
97
+ if type(func_res) is tuple:
98
+ status, res_payload = func_res
99
+ else:
100
+ status, res_payload = protocol.STATUS_OK, func_res
101
+ res = protocol.build_response_message(status, res_payload)
102
+ self._logger.debug(f"Sending:\n{hex_util.get_bytes_matrix(res)}")
103
+ conn.sendall(res)
104
+ except TimeoutError:
105
+ continue
106
+ except ConnectionResetError:
107
+ break
108
+ except Exception as e:
109
+ self._logger.error(e)
110
+ raise e
111
+
112
+ @staticmethod
113
+ def _error(error: str) -> bytes:
114
+ return protocol.build_response_message(protocol.STATUS_ERROR, error.encode())
115
+
116
+ @staticmethod
117
+ def _unsupported(command):
118
+ return protocol.build_response_message(
119
+ protocol.STATUS_UNSUPPORTED, f'Command [0x{format(command, '04x')}] not supported'.encode()
120
+ )
121
+
122
+ @_handler(cracker.Commands.GET_ID, has_payload=False)
123
+ def get_id(self) -> bytes:
124
+ return b"mock_001"
125
+
126
+ @_handler(cracker.Commands.GET_NAME, has_payload=False)
127
+ def get_name(self) -> bytes:
128
+ return b"MOCK 001"
129
+
130
+ @_handler(cracker.Commands.GET_VERSION, has_payload=False)
131
+ def get_version(self) -> bytes:
132
+ return b"0.1.0(adc:0.1.0,hardware:0.1.0)"
133
+
134
+ @_handler(cracker.Commands.OSC_GET_ANALOG_WAVES)
135
+ def osc_get_analog_wave(self, payload: bytes) -> bytes:
136
+ channel, offset, sample_count = struct.unpack(">BII", payload)
137
+ return struct.pack(f">{sample_count}h", *np.random.randint(-100, 100, size=sample_count).tolist())
138
+
139
+ @_handler(cracker.Commands.OSC_IS_TRIGGERED)
140
+ def osc_is_trigger(self, payload: bytes) -> tuple[int, bytes | None]:
141
+ time.sleep(0.05) # Simulate device I/O operations.
142
+ return protocol.STATUS_OK, None
143
+
144
+ @_handler(cracker.Commands.OSC_FORCE, has_payload=False)
145
+ def osc_force(self) -> bytes: ...
146
+
147
+ @_handler(cracker.Commands.OSC_SINGLE, has_payload=False)
148
+ def osc_arm(self) -> bytes: ...
149
+
150
+ @_handler(cracker.Commands.NUT_VOLTAGE, has_payload=False)
151
+ def nut_voltage(self) -> bytes: ...
152
+
153
+ @_handler(cracker.Commands.OSC_ANALOG_CHANNEL_ENABLE, has_payload=False)
154
+ def osc_analog_channel_enable(self) -> bytes: ...
155
+
156
+ @_handler(cracker.Commands.NUT_CLOCK, has_payload=False)
157
+ def nut_clock(self) -> bytes: ...
158
+
159
+
160
+ if __name__ == "__main__":
161
+ mock_cracker = MockCracker()
162
+ logger.set_level(logging.DEBUG, mock_cracker)
163
+ mock_cracker.start()