qlsdk2 0.6.0a4__tar.gz → 0.6.0a6__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.
Files changed (84) hide show
  1. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/PKG-INFO +1 -1
  2. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/setup.py +1 -1
  3. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/command/__init__.py +1 -1
  4. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/device/base.py +5 -1
  5. qlsdk2-0.6.0a6/src/qlsdk/rsc/device/c256_rs.py +32 -0
  6. qlsdk2-0.6.0a6/src/qlsdk/rsc/device/c64_rs.py +29 -0
  7. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/paradigm.py +125 -5
  8. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk2.egg-info/PKG-INFO +1 -1
  9. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/test/test_bdf.py +5 -5
  10. qlsdk2-0.6.0a4/src/qlsdk/rsc/device/c256_rs.py +0 -339
  11. qlsdk2-0.6.0a4/src/qlsdk/rsc/device/c64_rs.py +0 -331
  12. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/README.md +0 -0
  13. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/setup.cfg +0 -0
  14. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/__init__.py +0 -0
  15. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/ar4/__init__.py +0 -0
  16. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/ar4m/__init__.py +0 -0
  17. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/__init__.py +0 -0
  18. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/crc/__init__.py +0 -0
  19. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/crc/crctools.py +0 -0
  20. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/device.py +0 -0
  21. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/entity/__init__.py +0 -0
  22. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/exception.py +0 -0
  23. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/filter/__init__.py +0 -0
  24. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/filter/norch.py +0 -0
  25. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/local.py +0 -0
  26. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/message/__init__.py +0 -0
  27. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/message/command.py +0 -0
  28. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/message/tcp.py +0 -0
  29. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/message/udp.py +0 -0
  30. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/network/__init__.py +0 -0
  31. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/network/monitor.py +0 -0
  32. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/core/utils.py +0 -0
  33. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/entity/__init__.py +0 -0
  34. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/entity/message.py +0 -0
  35. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/entity/signal.py +0 -0
  36. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/interface/__init__.py +0 -0
  37. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/interface/analyzer.py +0 -0
  38. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/interface/collector.py +0 -0
  39. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/interface/device.py +0 -0
  40. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/interface/parser.py +0 -0
  41. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/interface/stimulator.py +0 -0
  42. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/interface/store.py +0 -0
  43. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/persist/__init__.py +0 -0
  44. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/persist/ars_edf.py +0 -0
  45. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/persist/edf.py +0 -0
  46. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/persist/rsc_edf.py +0 -0
  47. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/persist/stream.py +0 -0
  48. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/__init__.py +0 -0
  49. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/device/__init__.py +0 -0
  50. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/device/arskindling.py +0 -0
  51. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/device/c16_rs.py +0 -0
  52. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/device/c64s1.py +0 -0
  53. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/device/device_factory.py +0 -0
  54. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/eegion.py +0 -0
  55. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/entity.py +0 -0
  56. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/interface/__init__.py +0 -0
  57. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/interface/command.py +0 -0
  58. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/interface/device.py +0 -0
  59. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/interface/handler.py +0 -0
  60. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/interface/parser.py +0 -0
  61. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/manager/__init__.py +0 -0
  62. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/manager/container.py +0 -0
  63. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/manager/search.py +0 -0
  64. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/network/__init__.py +0 -0
  65. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/network/discover.py +0 -0
  66. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/parser/__init__.py +0 -0
  67. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/parser/base-new.py +0 -0
  68. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/parser/base.py +0 -0
  69. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/parser/rsc.py +0 -0
  70. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/rsc/proxy.py +0 -0
  71. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/sdk/__init__.py +0 -0
  72. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/sdk/ar4sdk.py +0 -0
  73. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/sdk/hub.py +0 -0
  74. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/sdk/libs/libAr4SDK.dll +0 -0
  75. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/sdk/libs/libwinpthread-1.dll +0 -0
  76. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/x8/__init__.py +0 -0
  77. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk/x8m/__init__.py +0 -0
  78. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk2.egg-info/SOURCES.txt +0 -0
  79. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk2.egg-info/dependency_links.txt +0 -0
  80. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk2.egg-info/requires.txt +0 -0
  81. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/src/qlsdk2.egg-info/top_level.txt +0 -0
  82. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/test/test.222.py +0 -0
  83. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/test/test.py +0 -0
  84. {qlsdk2-0.6.0a4 → qlsdk2-0.6.0a6}/test/test_ar4m.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qlsdk2
3
- Version: 0.6.0a4
3
+ Version: 0.6.0a6
4
4
  Summary: SDK for quanlan device
5
5
  Home-page: https://github.com/hehuajun/qlsdk
6
6
  Author: hehuajun
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding='utf-8' ) as fh:
6
6
 
7
7
  setuptools.setup(
8
8
  name="qlsdk2",
9
- version="0.6.0a4",
9
+ version="0.6.0a6",
10
10
  author="hehuajun",
11
11
  author_email="hehuajun@eegion.com",
12
12
  description="SDK for quanlan device",
@@ -233,7 +233,7 @@ class StartStimulationCommand(DeviceCommand):
233
233
  cmd_code = 0x48C
234
234
  cmd_desc = "启动刺激"
235
235
  def pack_body(self):
236
- return self.device.stim_paradigm.to_bytes()
236
+ return self.device.get_stim_param()
237
237
  # return bytes.fromhex('01000000000000008813000000000000010000000000000000000140420f00640064000000803f0000010000000000000000000000000000000000000000008813000000000000')
238
238
  def parse_body(self, body: bytes):
239
239
  # time - 8B
@@ -9,6 +9,7 @@ from qlsdk.core.entity import RscPacket, ImpedancePacket
9
9
  from qlsdk.core.utils import to_bytes
10
10
  from qlsdk.rsc.interface import IDevice, IParser
11
11
  from qlsdk.rsc.command import StartImpedanceCommand, StopImpedanceCommand, StartStimulationCommand, StopStimulationCommand, SetAcquisitionParamCommand, StartAcquisitionCommand, StopAcquisitionCommand
12
+ from qlsdk.rsc.paradigm import StimulationParadigm
12
13
  from qlsdk.rsc.parser.base import TcpMessageParser
13
14
 
14
15
  class QLBaseDevice(IDevice):
@@ -91,7 +92,7 @@ class QLBaseDevice(IDevice):
91
92
  ]
92
93
  }
93
94
 
94
- self.stim_paradigm = None
95
+ self.stim_paradigm: StimulationParadigm = None
95
96
 
96
97
  signal_info = {
97
98
  "param" : None,
@@ -389,6 +390,9 @@ class QLBaseDevice(IDevice):
389
390
  t = Thread(target=self._stop_stimulation_trigger, args=(self.stim_paradigm.duration,), daemon=True)
390
391
  t.start()
391
392
 
393
+ def get_stim_param(self) -> bytes:
394
+ return self.stim_paradigm.to_bytes()
395
+
392
396
  def _stop_stimulation_trigger(self, duration):
393
397
  delay = duration
394
398
  while delay > 0:
@@ -0,0 +1,32 @@
1
+ from loguru import logger
2
+ from qlsdk.core.entity import C256RSPacket
3
+ from qlsdk.persist import RscEDFHandler
4
+ from qlsdk.rsc.device.device_factory import DeviceFactory
5
+ from qlsdk.rsc.command import *
6
+ from qlsdk.rsc.device.base import QLBaseDevice
7
+
8
+ class C256RS(QLBaseDevice):
9
+ # C256RS设备类型标识符
10
+ device_type_001 = 0x51
11
+ device_type = 0x45
12
+
13
+ def __init__(self, socket):
14
+ super().__init__(socket)
15
+
16
+ def init_edf_handler(self):
17
+ self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
18
+ self._edf_handler.set_device_type(self.device_type)
19
+ self._edf_handler.set_device_no(self.device_no)
20
+ self._edf_handler.set_storage_path(self._storage_path)
21
+ self._edf_handler.set_file_prefix(self._file_prefix if self._file_prefix else 'C256RS')
22
+ logger.debug(f"EDF Handler initialized")
23
+
24
+
25
+ def _signal_wrapper(self, body: bytes):
26
+ return C256RSPacket().transfer(body)
27
+
28
+ def get_stim_param(self) -> bytes:
29
+ return self.stim_paradigm.to_bytes_c256()
30
+
31
+ DeviceFactory.register(C256RS.device_type, C256RS)
32
+ DeviceFactory.register(C256RS.device_type_001, C256RS)
@@ -0,0 +1,29 @@
1
+ from loguru import logger
2
+ from qlsdk.persist import RscEDFHandler
3
+ from qlsdk.rsc.interface import IDevice
4
+ from qlsdk.rsc.command import *
5
+ from qlsdk.rsc.device.base import QLBaseDevice
6
+
7
+ class C64RS(QLBaseDevice):
8
+
9
+ device_type = 0x39 # C64RS设备类型标识符
10
+
11
+ def __init__(self, socket):
12
+ super().__init__(socket)
13
+
14
+ @classmethod
15
+ def from_parent(cls, parent:IDevice) -> IDevice:
16
+ rlt = cls(parent.socket)
17
+ rlt.device_id = parent.device_id
18
+ rlt._device_no = parent.device_no
19
+ return rlt
20
+
21
+
22
+ def init_edf_handler(self):
23
+ self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
24
+ self._edf_handler.set_device_type(self.device_type)
25
+ self._edf_handler.set_device_no(self.device_no)
26
+ self._edf_handler.set_storage_path(self._storage_path)
27
+ self._edf_handler.set_file_prefix(self._file_prefix if self._file_prefix else 'C64RS')
28
+ logger.debug(f"EDF Handler initialized")
29
+
@@ -61,6 +61,22 @@ class StimulationChannel(ABC):
61
61
 
62
62
  return result
63
63
 
64
+ def to_bytes_c256(self):
65
+ result = self.channel_id.to_bytes(1, 'little')
66
+ result += self.wave_form.to_bytes(1, 'little')
67
+
68
+ result += self._to_bytes_c256()
69
+
70
+ result += int(self.delay_time).to_bytes(4, 'little')
71
+ result += int(self.ramp_up * 1000).to_bytes(4, 'little')
72
+ result += int((self.duration + self.ramp_up) * 1000).to_bytes(4, 'little')
73
+ result += int(self.ramp_down * 1000).to_bytes(4, 'little')
74
+
75
+ return result
76
+
77
+ def _to_bytes_c256(self):
78
+ return bytes.fromhex("00000000000000000000000000000000000000000000000000000000")
79
+
64
80
  def _ext_bytes(self):
65
81
  return bytes.fromhex("00000000000000000000000000000000")
66
82
 
@@ -99,6 +115,9 @@ class DCStimulation(StimulationChannel):
99
115
  "delay_time": self.delay_time
100
116
  }
101
117
 
118
+ def _to_bytes_c256(self):
119
+ return int(self.current_max * 1000 ).to_bytes(2, 'little') + bytes.fromhex("0000000000000000000000000000000000000000000000000000")
120
+
102
121
  def from_json(self, param):
103
122
  pass
104
123
  def __str__(self):
@@ -140,9 +159,19 @@ class SquareWaveStimulation(StimulationChannel):
140
159
  result += int((self.duration + self.ramp_up) * 1000).to_bytes(4, 'little')
141
160
  result += int(self.ramp_down * 1000).to_bytes(4, 'little')
142
161
 
143
- return result
144
-
145
- # 刺激模式-交流
162
+ return result
163
+
164
+ def _to_bytes_c256(self):
165
+ result = int(self.current_max * 1000000 ).to_bytes(4, 'little')
166
+ result += int(self.frequency).to_bytes(2, 'little')
167
+ result += bytes.fromhex("0000")
168
+ result += struct.pack('<f', self.duty_cycle)
169
+ result = int(self.current_min * 1000000 ).to_bytes(4, 'little')
170
+ result += self._ext_bytes()
171
+ return result
172
+
173
+
174
+ # 刺激模式-正弦
146
175
  class ACStimulation(StimulationChannel):
147
176
  '''
148
177
  channel_id: int, 通道编号,从0开始
@@ -177,6 +206,15 @@ class ACStimulation(StimulationChannel):
177
206
 
178
207
  def from_json(self, param):
179
208
  pass
209
+
210
+ def _to_bytes_c256(self):
211
+ result = int(self.current_max * 1000 ).to_bytes(2, 'little')
212
+ result += bytes.fromhex("0000")
213
+ result += struct.pack('<f', self.frequency)
214
+ result += struct.pack('<f', self.phase_position)
215
+ result += self._ext_bytes()
216
+ return result
217
+
180
218
  def __str__(self):
181
219
  return f"ACStimulation(channel_id={self.channel_id}, waveform={self.waveform}, current={self.current_max}, duration={self.duration}, ramp_up={self.ramp_up}, ramp_down={self.ramp_down}, frequency={self.frequency}, phase_position={self.phase_position}, duration_delay={self.duration_delay})"
182
220
 
@@ -230,6 +268,15 @@ class PulseStimulation(StimulationChannel):
230
268
 
231
269
  return result
232
270
 
271
+ def _to_bytes_c256(self):
272
+ result = int(self.current_max * 1000000 ).to_bytes(4, 'little')
273
+ result += int(self.frequency).to_bytes(2, 'little')
274
+ result += bytes.fromhex("0000")
275
+ result += struct.pack('<f', self.duty_cycle)
276
+ result = int(self.current_min * 1000000 ).to_bytes(4, 'little')
277
+ result += self._ext_bytes()
278
+ return result
279
+
233
280
  def to_json(self):
234
281
  return {
235
282
  "channel_id": self.channel_id,
@@ -254,7 +301,7 @@ class StimulationParadigm(object):
254
301
  def __init__(self):
255
302
  self.channels = None
256
303
  self.duration = None
257
- self.interval_time = 0
304
+ self.interval_time = 25000
258
305
  self.characteristic = 1
259
306
  self.mode = 0
260
307
  self.repeats = 0
@@ -287,6 +334,17 @@ class StimulationParadigm(object):
287
334
  result += channel.to_bytes()
288
335
  return result
289
336
 
337
+ def to_bytes_c256(self):
338
+ result = to_bytes(list(self.channels.keys()), 256)
339
+ result += int(self.duration * 1000).to_bytes(4, 'little')
340
+ result += int(self.interval_time).to_bytes(4, 'little')
341
+ result += int(self.characteristic).to_bytes(4, 'little')
342
+ result += int(self.mode).to_bytes(1, 'little')
343
+ result += int(self.repeats).to_bytes(4, 'little')
344
+ for channel in self.channels.values():
345
+ result += channel.to_bytes()
346
+ return result
347
+
290
348
  def to_json(self):
291
349
  # Convert the object to JSON for transmission
292
350
  return {
@@ -306,8 +364,70 @@ class StimulationParadigm(object):
306
364
  def clear(self):
307
365
  self.channels = None
308
366
  self.duration = None
309
- self.interval_time = 0
367
+ self.interval_time = 25000
310
368
  self.characteristic = 1
311
369
  self.mode = 0
312
370
  self.repeats = 0
313
371
 
372
+
373
+ # 刺激范式
374
+ class C256StimulationParadigm(object):
375
+ def __init__(self):
376
+ self.channels = None
377
+ self.duration = None
378
+ self.interval_time = 0
379
+ self.characteristic = 1
380
+ self.mode = 0
381
+ self.repeats = 0
382
+
383
+ def add_channel(self, channel: StimulationChannel, update=False):
384
+ if self.channels is None:
385
+ self.channels = {}
386
+ channel_id = channel.channel_id + 1
387
+ if channel_id in self.channels.keys():
388
+ logger.warning(f"Channel {channel_id} already exists")
389
+ if update:
390
+ self.channels[channel_id] = channel
391
+ else:
392
+ self.channels[channel_id] = channel
393
+
394
+ # 计算刺激时间
395
+ duration = channel.duration + channel.ramp_up + channel.ramp_down
396
+ if self.duration is None or duration > self.duration:
397
+ self.duration = duration
398
+
399
+
400
+ def to_bytes(self):
401
+ result = to_bytes(list(self.channels.keys()), 64)
402
+ result += int(self.duration * 1000).to_bytes(4, 'little')
403
+ result += int(self.interval_time).to_bytes(4, 'little')
404
+ result += int(self.characteristic).to_bytes(4, 'little')
405
+ result += int(self.mode).to_bytes(1, 'little')
406
+ result += int(self.repeats).to_bytes(4, 'little')
407
+ for channel in self.channels.values():
408
+ result += channel.to_bytes()
409
+ return result
410
+
411
+ def to_json(self):
412
+ # Convert the object to JSON for transmission
413
+ return {
414
+ "channels": list(self.channels.keys()),
415
+ "duration": self.duration,
416
+ "interval_time": self.interval_time,
417
+ "characteristic": self.characteristic,
418
+ "mode": self.mode,
419
+ "repeats": self.repeats,
420
+ "stim": [channel.to_json() for channel in self.channels.values()]
421
+ }
422
+
423
+ # @staticmethod
424
+ # def from_json(param: Dict[str, Any]):
425
+ # pass
426
+
427
+ def clear(self):
428
+ self.channels = None
429
+ self.duration = None
430
+ self.interval_time = 0
431
+ self.characteristic = 1
432
+ self.mode = 0
433
+ self.repeats = 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qlsdk2
3
- Version: 0.6.0a4
3
+ Version: 0.6.0a6
4
4
  Summary: SDK for quanlan device
5
5
  Home-page: https://github.com/hehuajun/qlsdk
6
6
  Author: hehuajun
@@ -6,7 +6,7 @@ def main():
6
6
  """主函数示例"""
7
7
 
8
8
  # 1. 创建读取器
9
- filepath = r"E:\project\qlsdk-project\C256RS_450025430001_251223182657.bdf" # 或 "sample.edf"
9
+ filepath = r"F:\download\飞书\20260104172046_1.bdf" # 或 "sample.edf"
10
10
 
11
11
  try:
12
12
  # 使用上下文管理器自动管理文件
@@ -32,8 +32,8 @@ def main():
32
32
  print()
33
33
 
34
34
  # 4. 读取指定时间段的数据
35
- start_time = 20.0 # 10秒
36
- end_time = 30.0 # 12秒
35
+ start_time = 13 # 10秒
36
+ end_time = 18 # 12秒
37
37
 
38
38
  print(f"=== 读取 {start_time}s 到 {end_time}s 的数据 ===")
39
39
 
@@ -71,8 +71,8 @@ def main():
71
71
  print("\n=== 读取特定通道 ===")
72
72
  channel_indices = [0, 1, 2] # 前3个通道
73
73
  data_channels = reader.read_data(
74
- start_time=15.0,
75
- end_time=16.0,
74
+ start_time=start_time,
75
+ end_time=end_time,
76
76
  channel_indices=channel_indices,
77
77
  output_type='digital' # 数字值
78
78
  )
@@ -1,339 +0,0 @@
1
-
2
- from multiprocessing import Queue
3
- from threading import Thread
4
- from time import sleep, time_ns
5
- from typing import Any, Dict, Literal
6
-
7
- from loguru import logger
8
- from qlsdk.core.entity import C256RSPacket
9
- from qlsdk.persist import RscEDFHandler
10
- from qlsdk.rsc.device.device_factory import DeviceFactory
11
- from qlsdk.rsc.command import *
12
- from qlsdk.rsc.device.base import QLBaseDevice
13
-
14
- class C256RS(QLBaseDevice):
15
- # C256RS设备类型标识符
16
- device_type_001 = 0x51
17
- device_type = 0x45
18
-
19
- def __init__(self, socket):
20
- super().__init__(socket)
21
- self.socket = socket
22
-
23
- self._id = None
24
-
25
- # 设备信息
26
- self.device_id = None
27
- self.device_name = None
28
- self._device_no = None
29
- self.software_version = None
30
- self.hardware_version = None
31
- self.connect_time = None
32
- self.current_time = None
33
- # mV
34
- self.voltage = None
35
- # %
36
- self.battery_remain = None
37
- # %
38
- self.battery_total = None
39
- # persist
40
- self._recording = False
41
- self._storage_path = None
42
- self._file_prefix = None
43
-
44
- # 可设置参数
45
- # 采集:采样量程、采样率、采样通道
46
- # 刺激:刺激电流、刺激频率、刺激时间、刺激通道
47
- # 采样量程(mV):188、375、563、750、1125、2250、4500
48
- self._sample_range:Literal[188, 375, 563, 750, 1125, 2250, 4500] = 188
49
- # 采样率(Hz):250、500、1000、2000、4000、8000、16000、32000
50
- self._sample_rate:Literal[250, 500, 1000, 2000, 4000, 8000, 16000, 32000] = 500
51
- self._physical_max = self._sample_range * 1000 # 物理最大值(uV)
52
- self._physical_min = -self._sample_range * 1000 # 物理最小值(uV)
53
- self._digital_max = 8388607
54
- self._digital_min = -8388608
55
- self._physical_range = self._physical_max - self._physical_min
56
- self._digital_range = 16777215
57
- self._acq_channels = None
58
- self._acq_param = {
59
- "sample_range": 188,
60
- "sample_rate": 500,
61
- "channels": [],
62
- }
63
-
64
- self._stim_param = {
65
- "stim_type": 0, # 刺激类型:0-所有通道参数相同, 1: 通道参数不同
66
- "channels": [],
67
- "param": [{
68
- "channel_id": 0, #通道号 从0开始 -- 必填
69
- "waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
70
- "current": 1, #电流强度(mA) -- 必填
71
- "duration": 30, #平稳阶段持续时间(s) -- 必填
72
- "ramp_up": 5, #上升时间(s) 默认0
73
- "ramp_down": 5, #下降时间(s) 默认0
74
- "frequency": 500, #频率(Hz) -- 非直流必填
75
- "phase_position": 0, #相位 -- 默认0
76
- "duration_delay": "0", #延迟启动时间(s) -- 默认0
77
- "pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
78
- },
79
- {
80
- "channel_id": 1, #通道号 从0开始 -- 必填
81
- "waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
82
- "current": 1, #电流强度(mA) -- 必填
83
- "duration": 30, #平稳阶段持续时间(s) -- 必填
84
- "ramp_up": 5, #上升时间(s) 默认0
85
- "ramp_down": 5, #下降时间(s) 默认0
86
- "frequency": 500, #频率(Hz) -- 非直流必填
87
- "phase_position": 0, #相位 -- 默认0
88
- "duration_delay": "0", #延迟启动时间(s) -- 默认0
89
- "pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
90
- }
91
- ]
92
- }
93
-
94
- self.stim_paradigm = None
95
-
96
- signal_info = {
97
- "param" : None,
98
- "start_time" : None,
99
- "finished_time" : None,
100
- "packet_total" : None,
101
- "last_packet_time" : None,
102
- "state" : 0
103
- }
104
- stim_info = {
105
-
106
- }
107
- Impedance_info = {
108
-
109
- }
110
- # 信号采集状态
111
- # 信号数据包总数(一个信号采集周期内)
112
- # 信号采集参数
113
- # 电刺激状态
114
- # 电刺激开始时间(最近一次)
115
- # 电刺激结束时间(最近一次)
116
- # 电刺激参数
117
- # 启动数据解析线程
118
- # 数据存储状态
119
- # 存储目录
120
-
121
- #
122
- self.__signal_consumer: Dict[str, Queue[Any]]={}
123
- self.__impedance_consumer: Dict[str, Queue[Any]]={}
124
-
125
- # EDF文件处理器
126
- self._edf_handler = None
127
- self.storage_enable = True
128
-
129
- # self._parser: IParser = RSCMessageParser(self)
130
- # self._parser.start()
131
-
132
- # 启动数据接收线程
133
- # self._accept = Thread(target=self.accept)
134
- # self._accept.daemon = True
135
- # self._accept.start()
136
-
137
- @property
138
- def device_no(self):
139
- return self._device_no
140
-
141
- @device_no.setter
142
- def device_no(self, value: str):
143
- self._device_no = value
144
-
145
-
146
- def init_edf_handler(self):
147
- self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
148
- self._edf_handler.set_device_type(self.device_type)
149
- self._edf_handler.set_device_no(self.device_no)
150
- self._edf_handler.set_storage_path(self._storage_path)
151
- self._edf_handler.set_file_prefix(self._file_prefix if self._file_prefix else 'C256RS')
152
-
153
- @property
154
- def edf_handler(self):
155
- if not self.storage_enable:
156
- return None
157
-
158
- if self._edf_handler is None:
159
- self.init_edf_handler()
160
-
161
- return self._edf_handler
162
-
163
- @property
164
- def acq_channels(self):
165
- if self._acq_channels is None:
166
- self._acq_channels = [i for i in range(1, 256)]
167
- return self._acq_channels
168
- @property
169
- def sample_range(self):
170
- return self._sample_range if self._sample_range else 188
171
- @property
172
- def sample_rate(self):
173
- return self._sample_rate if self._sample_rate else 500
174
- @property
175
- def resolution(self):
176
- return 24
177
-
178
- @property
179
- def signal_consumers(self):
180
- return self.__signal_consumer
181
-
182
- @property
183
- def impedance_consumers(self):
184
- return self.__impedance_consumer
185
-
186
- # 设置记录文件路径
187
- def set_storage_path(self, path):
188
- self._storage_path = path
189
-
190
- # 设置记录文件名称前缀
191
- def set_file_prefix(self, prefix):
192
- self._file_prefix = prefix
193
-
194
- # socket发送数据
195
- def send(self, data):
196
- self.socket.sendall(data)
197
-
198
- # 设置刺激参数
199
- def set_stim_param(self, param):
200
- self.stim_paradigm = param
201
-
202
- def _signal_wrapper(self, body: bytes):
203
- return C256RSPacket().transfer(body)
204
-
205
- def start_impedance(self):
206
- logger.info("启动阻抗测量")
207
- msg = StartImpedanceCommand.build(self).pack()
208
- logger.debug(f"start_impedance message is {msg.hex()}")
209
- self.socket.sendall(msg)
210
-
211
- def stop_impedance(self):
212
- logger.info("停止阻抗测量")
213
- msg = StopImpedanceCommand.build(self).pack()
214
- logger.debug(f"stop_impedance message is {msg.hex()}")
215
- self.socket.sendall(msg)
216
-
217
- def start_stimulation(self):
218
- if self.stim_paradigm is None:
219
- logger.warning("刺激参数未设置,请先设置刺激参数")
220
- return
221
- logger.info("启动电刺激")
222
- msg = StartStimulationCommand.build(self).pack()
223
- logger.debug(f"start_stimulation message is {msg.hex()}")
224
- self.socket.sendall(msg)
225
- t = Thread(target=self._stop_stimulation_trigger, args=(self.stim_paradigm.duration,))
226
- t.start()
227
-
228
- def _stop_stimulation_trigger(self, duration):
229
- delay = duration
230
- while delay > 0:
231
- sleep(1)
232
- delay -= 1
233
- logger.info(f"_stop_stimulation_trigger duration: {duration}")
234
- if self._edf_handler:
235
- self._edf_handler.trigger("stimulation should be stopped")
236
- else:
237
- logger.warning("stop stim trigger fail. no edf writer alive")
238
-
239
- def stop_stimulation(self):
240
- logger.info("停止电刺激")
241
- msg = StopStimulationCommand.pack()
242
- logger.debug(f"stop_stimulation message is {msg.hex()}")
243
- self.socket.sendall(msg)
244
-
245
- # 启动采集
246
- def start_acquisition(self, recording = True):
247
- logger.info("启动信号采集")
248
- self._recording = recording
249
- # 设置数据采集参数
250
- param_bytes = SetAcquisitionParamCommand.build(self).pack()
251
- # 启动数据采集
252
- start_bytes = StartAcquisitionCommand.build(self).pack()
253
- msg = param_bytes + start_bytes
254
- logger.debug(f"start_acquisition message is {msg.hex()}")
255
- self.socket.sendall(msg)
256
-
257
- # 停止采集
258
- def stop_acquisition(self):
259
- logger.info("停止信号采集")
260
- msg = StopAcquisitionCommand.build(self).pack()
261
- logger.debug(f"stop_acquisition message is {msg.hex()}")
262
- self.socket.sendall(msg)
263
- if self._edf_handler:
264
- # 发送结束标识
265
- self.edf_handler.write(None)
266
-
267
- # 订阅实时数据
268
- def subscribe(self, topic:str=None, q : Queue=None, type : Literal["signal","impedance"]="signal"):
269
-
270
- # 数据队列
271
- if q is None:
272
- q = Queue(maxsize=10240000) # 10M缓存
273
-
274
- # 队列名称
275
- if topic is None:
276
- topic = f"{type}_{time_ns()}"
277
-
278
- # 订阅生理电信号数据
279
- if type == "signal":
280
- # topic唯一,用来区分不同的订阅队列(下同)
281
- if topic in list(self.__signal_consumer.keys()):
282
- logger.warning(f"exists {type} subscribe of {topic}")
283
- else:
284
- self.__signal_consumer[topic] = q
285
-
286
- # 订阅阻抗数据
287
- if type == "impedance":
288
- if topic in list(self.__signal_consumer.keys()):
289
- logger.warning(f"exists {type} subscribe of {topic}")
290
- else:
291
- self.__impedance_consumer[topic] = q
292
-
293
- return topic, q
294
-
295
- def trigger(self, desc):
296
- if self._edf_handler:
297
- self.edf_handler.trigger(desc)
298
- else:
299
- logger.warning("no edf handler, no place to recording trigger")
300
-
301
- def __str__(self):
302
- return f'''
303
- Device:
304
- Name: {self.device_name},
305
- Type: {hex(self.device_type) if self.device_type else None},
306
- ID: {self.device_id if self.device_id else None},
307
- Software: {self.software_version},
308
- Hardware: {self.hardware_version},
309
- Connect Time: {self.connect_time},
310
- Current Time: {self.current_time},
311
- Voltage: {str(self.voltage) + "mV" if self.voltage else None},
312
- Battery Remain: {str(self.battery_remain)+ "%" if self.battery_remain else None},
313
- Battery Total: {str(self.battery_total) + "%" if self.battery_total else None}
314
- '''
315
-
316
- def __repr__(self):
317
- return f'''
318
- Device:
319
- Name: {self.device_name},
320
- Type: {hex(self.device_type)},
321
- ID: {self.device_id},
322
- Software: {self.software_version},
323
- Hardware: {self.hardware_version},
324
- Connect Time: {self.connect_time},
325
- Current Time: {self.current_time},
326
- Voltage: {self.voltage}mV,
327
- Battery Remain: {self.battery_remain}%,
328
- Battery Total: {self.battery_total}%
329
- '''
330
-
331
- def __eq__(self, other):
332
- return self.device_name == other.device_name and self.device_type == other.device_type and self.device_id == other.device_id
333
-
334
- def __hash__(self):
335
- return hash((self.device_name, self.device_type, self.device_id))
336
-
337
-
338
- DeviceFactory.register(C256RS.device_type, C256RS)
339
- DeviceFactory.register(C256RS.device_type_001, C256RS)
@@ -1,331 +0,0 @@
1
-
2
- from multiprocessing import Queue
3
- from threading import Thread
4
- from time import sleep, time_ns
5
- from typing import Any, Dict, Literal
6
-
7
- from loguru import logger
8
- from qlsdk.persist import RscEDFHandler
9
- from qlsdk.rsc.interface import IDevice, IParser
10
- from qlsdk.rsc.command import *
11
- from qlsdk.rsc.parser.base import TcpMessageParser
12
- from qlsdk.rsc.device.base import QLBaseDevice
13
-
14
- class C64RS(QLBaseDevice):
15
-
16
- device_type = 0x39 # C64RS设备类型标识符
17
-
18
- def __init__(self, socket):
19
- super().__init__(socket)
20
- self.socket = socket
21
-
22
- self._id = None
23
-
24
- # 设备信息
25
- self.device_id = None
26
- self.device_name = None
27
- self._device_no = None
28
- self.software_version = None
29
- self.hardware_version = None
30
- self.connect_time = None
31
- self.current_time = None
32
- # mV
33
- self.voltage = None
34
- # %
35
- self.battery_remain = None
36
- # %
37
- self.battery_total = None
38
- # persist
39
- self._recording = False
40
- self._storage_path = None
41
- self._file_prefix = None
42
-
43
- # 可设置参数
44
- # 采集:采样量程、采样率、采样通道
45
- # 刺激:刺激电流、刺激频率、刺激时间、刺激通道
46
- # 采样量程(mV):188、375、563、750、1125、2250、4500
47
- self._sample_range:Literal[188, 375, 563, 750, 1125, 2250, 4500] = 188
48
- # 采样率(Hz):250、500、1000、2000、4000、8000、16000、32000
49
- self._sample_rate:Literal[250, 500, 1000, 2000, 4000, 8000, 16000, 32000] = 500
50
- self._acq_channels = None
51
- self._acq_param = {
52
- "sample_range": 188,
53
- "sample_rate": 500,
54
- "channels": [],
55
- }
56
-
57
- self._stim_param = {
58
- "stim_type": 0, # 刺激类型:0-所有通道参数相同, 1: 通道参数不同
59
- "channels": [],
60
- "param": [{
61
- "channel_id": 0, #通道号 从0开始 -- 必填
62
- "waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
63
- "current": 1, #电流强度(mA) -- 必填
64
- "duration": 30, #平稳阶段持续时间(s) -- 必填
65
- "ramp_up": 5, #上升时间(s) 默认0
66
- "ramp_down": 5, #下降时间(s) 默认0
67
- "frequency": 500, #频率(Hz) -- 非直流必填
68
- "phase_position": 0, #相位 -- 默认0
69
- "duration_delay": "0", #延迟启动时间(s) -- 默认0
70
- "pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
71
- },
72
- {
73
- "channel_id": 1, #通道号 从0开始 -- 必填
74
- "waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
75
- "current": 1, #电流强度(mA) -- 必填
76
- "duration": 30, #平稳阶段持续时间(s) -- 必填
77
- "ramp_up": 5, #上升时间(s) 默认0
78
- "ramp_down": 5, #下降时间(s) 默认0
79
- "frequency": 500, #频率(Hz) -- 非直流必填
80
- "phase_position": 0, #相位 -- 默认0
81
- "duration_delay": "0", #延迟启动时间(s) -- 默认0
82
- "pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
83
- }
84
- ]
85
- }
86
-
87
- self.stim_paradigm = None
88
-
89
- signal_info = {
90
- "param" : None,
91
- "start_time" : None,
92
- "finished_time" : None,
93
- "packet_total" : None,
94
- "last_packet_time" : None,
95
- "state" : 0
96
- }
97
- stim_info = {
98
-
99
- }
100
- Impedance_info = {
101
-
102
- }
103
- # 信号采集状态
104
- # 信号数据包总数(一个信号采集周期内)
105
- # 信号采集参数
106
- # 电刺激状态
107
- # 电刺激开始时间(最近一次)
108
- # 电刺激结束时间(最近一次)
109
- # 电刺激参数
110
- # 启动数据解析线程
111
- # 数据存储状态
112
- # 存储目录
113
-
114
- #
115
- self.__signal_consumer: Dict[str, Queue[Any]]={}
116
- self.__impedance_consumer: Dict[str, Queue[Any]]={}
117
-
118
- # EDF文件处理器
119
- self._edf_handler = None
120
- self.storage_enable = True
121
-
122
- # self._parser: IParser = TcpMessageParser(self)
123
- # self._parser.start()
124
-
125
- # # 启动数据接收线程
126
- # self._accept = Thread(target=self.accept)
127
- # self._accept.daemon = True
128
- # self._accept.start()
129
-
130
- @property
131
- def device_no(self):
132
- return self._device_no
133
-
134
- @device_no.setter
135
- def device_no(self, value: str):
136
- self._device_no = value
137
-
138
- @classmethod
139
- def from_parent(cls, parent:IDevice) -> IDevice:
140
- rlt = cls(parent.socket)
141
- rlt.device_id = parent.device_id
142
- rlt._device_no = parent.device_no
143
- return rlt
144
-
145
-
146
- def init_edf_handler(self):
147
- self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
148
- self._edf_handler.set_device_type(self.device_type)
149
- self._edf_handler.set_device_no(self.device_no)
150
- self._edf_handler.set_storage_path(self._storage_path)
151
- self._edf_handler.set_file_prefix(self._file_prefix if self._file_prefix else 'C64RS')
152
-
153
- @property
154
- def edf_handler(self):
155
- if not self.storage_enable:
156
- return None
157
-
158
- if self._edf_handler is None:
159
- self.init_edf_handler()
160
-
161
- return self._edf_handler
162
-
163
- @property
164
- def acq_channels(self):
165
- if self._acq_channels is None:
166
- self._acq_channels = [i for i in range(1, 63)]
167
- return self._acq_channels
168
- @property
169
- def sample_range(self):
170
- return self._sample_range if self._sample_range else 188
171
- @property
172
- def sample_rate(self):
173
- return self._sample_rate if self._sample_rate else 500
174
- @property
175
- def resolution(self):
176
- return 24
177
-
178
- @property
179
- def signal_consumers(self):
180
- return self.__signal_consumer
181
-
182
- @property
183
- def impedance_consumers(self):
184
- return self.__impedance_consumer
185
-
186
- # 设置记录文件路径
187
- def set_storage_path(self, path):
188
- self._storage_path = path
189
-
190
- # 设置记录文件名称前缀
191
- def set_file_prefix(self, prefix):
192
- self._file_prefix = prefix
193
-
194
- # 接收socket消息
195
- def accept(self):
196
- while True:
197
- # 缓冲去4M
198
- data = self.socket.recv(4096*1024)
199
- if not data:
200
- logger.warning(f"设备{self.device_name}连接结束")
201
- break
202
-
203
- self._parser.append(data)
204
-
205
-
206
- # socket发送数据
207
- def send(self, data):
208
- self.socket.sendall(data)
209
-
210
- # 设置刺激参数
211
- def set_stim_param(self, param):
212
- self.stim_paradigm = param
213
-
214
- # 设置采集参数
215
- def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
216
- self._acq_param["channels"] = channels
217
- self._acq_param["sample_rate"] = sample_rate
218
- self._acq_param["sample_range"] = sample_range
219
- self._acq_channels = channels
220
- self._sample_rate = sample_rate
221
- self._sample_range = sample_range
222
-
223
- # 通用配置-TODO
224
- def set_config(self, key:str, val: str):
225
- pass
226
-
227
- def start_impedance(self):
228
- logger.info("启动阻抗测量")
229
- msg = StartImpedanceCommand.build(self).pack()
230
- logger.debug(f"start_impedance message is {msg.hex()}")
231
- self.socket.sendall(msg)
232
-
233
- def stop_impedance(self):
234
- logger.info("停止阻抗测量")
235
- msg = StopImpedanceCommand.build(self).pack()
236
- logger.debug(f"stop_impedance message is {msg.hex()}")
237
- self.socket.sendall(msg)
238
-
239
- def start_stimulation(self):
240
- if self.stim_paradigm is None:
241
- logger.warning("刺激参数未设置,请先设置刺激参数")
242
- return
243
- logger.info("启动电刺激")
244
- msg = StartStimulationCommand.build(self).pack()
245
- logger.debug(f"start_stimulation message is {msg.hex()}")
246
- self.socket.sendall(msg)
247
- t = Thread(target=self._stop_stimulation_trigger, args=(self.stim_paradigm.duration,))
248
- t.start()
249
-
250
- def _stop_stimulation_trigger(self, duration):
251
- delay = duration
252
- while delay > 0:
253
- sleep(1)
254
- delay -= 1
255
- logger.info(f"_stop_stimulation_trigger duration: {duration}")
256
- if self._edf_handler:
257
- self._edf_handler.trigger("stimulation should be stopped")
258
- else:
259
- logger.warning("stop stim trigger fail. no edf writer alive")
260
-
261
- def stop_stimulation(self):
262
- logger.info("停止电刺激")
263
- msg = StopStimulationCommand.pack()
264
- logger.debug(f"stop_stimulation message is {msg.hex()}")
265
- self.socket.sendall(msg)
266
-
267
- # 启动采集
268
- def start_acquisition(self, recording = True):
269
- logger.info("启动信号采集")
270
- self._recording = recording
271
- # 设置数据采集参数
272
- param_bytes = SetAcquisitionParamCommand.build(self).pack()
273
- # 启动数据采集
274
- start_bytes = StartAcquisitionCommand.build(self).pack()
275
- msg = param_bytes + start_bytes
276
- logger.debug(f"start_acquisition message is {msg.hex()}")
277
- self.socket.sendall(msg)
278
-
279
- # 停止采集
280
- def stop_acquisition(self):
281
- logger.info("停止信号采集")
282
- msg = StopAcquisitionCommand.build(self).pack()
283
- logger.debug(f"stop_acquisition message is {msg.hex()}")
284
- self.socket.sendall(msg)
285
- if self._edf_handler:
286
- # 发送结束标识
287
- self.edf_handler.write(None)
288
-
289
- def trigger(self, desc):
290
- if self._edf_handler:
291
- self.edf_handler.trigger(desc)
292
- else:
293
- logger.warning("no edf handler, no place to recording trigger")
294
-
295
- def __str__(self):
296
- return f'''
297
- Device:
298
- Name: {self.device_name},
299
- Type: {hex(self.device_type) if self.device_type else None},
300
- ID: {self.device_id if self.device_id else None},
301
- Software: {self.software_version},
302
- Hardware: {self.hardware_version},
303
- Connect Time: {self.connect_time},
304
- Current Time: {self.current_time},
305
- Voltage: {str(self.voltage) + "mV" if self.voltage else None},
306
- Battery Remain: {str(self.battery_remain)+ "%" if self.battery_remain else None},
307
- Battery Total: {str(self.battery_total) + "%" if self.battery_total else None}
308
- '''
309
-
310
- def __repr__(self):
311
- return f'''
312
- Device:
313
- Name: {self.device_name},
314
- Type: {hex(self.device_type)},
315
- ID: {self.device_id},
316
- Software: {self.software_version},
317
- Hardware: {self.hardware_version},
318
- Connect Time: {self.connect_time},
319
- Current Time: {self.current_time},
320
- Voltage: {self.voltage}mV,
321
- Battery Remain: {self.battery_remain}%,
322
- Battery Total: {self.battery_total}%
323
- '''
324
-
325
- def __eq__(self, other):
326
- return self.device_name == other.device_name and self.device_type == other.device_type and self.device_id == other.device_id
327
-
328
- def __hash__(self):
329
- return hash((self.device_name, self.device_type, self.device_id))
330
-
331
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes