qlsdk2 0.6.0a9__tar.gz → 0.6.0a12__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 (83) hide show
  1. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/PKG-INFO +1 -1
  2. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/setup.py +1 -1
  3. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/entity/__init__.py +15 -20
  4. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/persist/ars_edf.py +74 -74
  5. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/command/__init__.py +18 -5
  6. qlsdk2-0.6.0a12/src/qlsdk/rsc/device/arskindling.py +323 -0
  7. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/device/base.py +72 -49
  8. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/interface/device.py +14 -1
  9. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/paradigm.py +2 -1
  10. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/parser/base.py +2 -2
  11. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk2.egg-info/PKG-INFO +1 -1
  12. qlsdk2-0.6.0a9/src/qlsdk/rsc/device/arskindling.py +0 -80
  13. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/README.md +0 -0
  14. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/setup.cfg +0 -0
  15. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/__init__.py +0 -0
  16. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/ar4/__init__.py +0 -0
  17. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/ar4m/__init__.py +0 -0
  18. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/__init__.py +0 -0
  19. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/crc/__init__.py +0 -0
  20. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/crc/crctools.py +0 -0
  21. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/device.py +0 -0
  22. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/exception.py +0 -0
  23. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/filter/__init__.py +0 -0
  24. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/filter/norch.py +0 -0
  25. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/local.py +0 -0
  26. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/message/__init__.py +0 -0
  27. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/message/command.py +0 -0
  28. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/message/tcp.py +0 -0
  29. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/message/udp.py +0 -0
  30. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/network/__init__.py +0 -0
  31. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/network/monitor.py +0 -0
  32. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/core/utils.py +0 -0
  33. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/entity/__init__.py +0 -0
  34. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/entity/message.py +0 -0
  35. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/entity/signal.py +0 -0
  36. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/interface/__init__.py +0 -0
  37. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/interface/analyzer.py +0 -0
  38. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/interface/collector.py +0 -0
  39. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/interface/device.py +0 -0
  40. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/interface/parser.py +0 -0
  41. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/interface/stimulator.py +0 -0
  42. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/interface/store.py +0 -0
  43. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/persist/__init__.py +0 -0
  44. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/persist/edf.py +0 -0
  45. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/persist/rsc_edf.py +0 -0
  46. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/persist/stream.py +0 -0
  47. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/__init__.py +0 -0
  48. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/device/__init__.py +0 -0
  49. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/device/c16_rs.py +0 -0
  50. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/device/c256_rs.py +0 -0
  51. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/device/c64_rs.py +0 -0
  52. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/device/c64s1.py +0 -0
  53. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/device/device_factory.py +0 -0
  54. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/eegion.py +0 -0
  55. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/entity.py +0 -0
  56. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/interface/__init__.py +0 -0
  57. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/interface/command.py +0 -0
  58. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/interface/handler.py +0 -0
  59. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/interface/parser.py +0 -0
  60. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/manager/__init__.py +0 -0
  61. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/manager/container.py +0 -0
  62. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/manager/search.py +0 -0
  63. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/network/__init__.py +0 -0
  64. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/network/discover.py +0 -0
  65. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/parser/__init__.py +0 -0
  66. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/parser/base-new.py +0 -0
  67. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/parser/rsc.py +0 -0
  68. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/rsc/proxy.py +0 -0
  69. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/sdk/__init__.py +0 -0
  70. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/sdk/ar4sdk.py +0 -0
  71. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/sdk/hub.py +0 -0
  72. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/sdk/libs/libAr4SDK.dll +0 -0
  73. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/sdk/libs/libwinpthread-1.dll +0 -0
  74. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/x8/__init__.py +0 -0
  75. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk/x8m/__init__.py +0 -0
  76. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk2.egg-info/SOURCES.txt +0 -0
  77. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk2.egg-info/dependency_links.txt +0 -0
  78. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk2.egg-info/requires.txt +0 -0
  79. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/src/qlsdk2.egg-info/top_level.txt +0 -0
  80. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/test/test.222.py +0 -0
  81. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/test/test.py +0 -0
  82. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/test/test_ar4m.py +0 -0
  83. {qlsdk2-0.6.0a9 → qlsdk2-0.6.0a12}/test/test_bdf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qlsdk2
3
- Version: 0.6.0a9
3
+ Version: 0.6.0a12
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.0a9",
9
+ version="0.6.0a12",
10
10
  author="hehuajun",
11
11
  author_email="hehuajun@eegion.com",
12
12
  description="SDK for quanlan device",
@@ -19,7 +19,8 @@ class RscPacket(Packet):
19
19
  self.filter = None
20
20
  self.data_len = None
21
21
  self.trigger = None
22
- self.eeg = None
22
+ self.eeg = None # 数字信号
23
+ self.eeg_p = None # 物理值
23
24
 
24
25
  @staticmethod
25
26
  def transfer(body: bytes) -> 'RscPacket':
@@ -55,19 +56,13 @@ class RscPacket(Packet):
55
56
  return packet
56
57
 
57
58
  def __str__(self):
58
- return f"""
59
- time_stamp: {self.time_stamp}
60
- pkg_id: {self.pkg_id}
61
- origin_sample_rate: {self.origin_sample_rate}
62
- sample_rate: {self.sample_rate}
63
- sample_num: {self.sample_num}
64
- resolution: {self.resolution}
65
- filter: {self.filter}
66
- channels: {self.channels}
67
- data len: {self.data_len}
68
- trigger: {self.trigger}
69
- eeg: {self.eeg}
70
- """
59
+ return f"""[
60
+ "time_stamp": {self.time_stamp}
61
+ "pkg_id": {self.pkg_id}
62
+ "channels": {self.channels}
63
+ "trigger": {self.trigger}
64
+ "eeg": {self.eeg}
65
+ ]"""
71
66
 
72
67
  class ImpedancePacket(Packet):
73
68
  def __init__(self):
@@ -96,12 +91,12 @@ class ImpedancePacket(Packet):
96
91
  return packet
97
92
 
98
93
  def __str__(self):
99
- return f"""
100
- time_stamp: {self.time_stamp}
101
- pkg_id: {self.pkg_id}
102
- channels: {self.channels}
103
- impedance: {self.impedance}
104
- """
94
+ return f"""[
95
+ "time_stamp": {self.time_stamp}
96
+ "pkg_id": {self.pkg_id}
97
+ "channels": {self.channels}
98
+ "impedance": {self.impedance}
99
+ ]"""
105
100
 
106
101
 
107
102
  class C256RSPacket(Packet):
@@ -85,67 +85,67 @@ class ARSKindlingEDFHandler(object):
85
85
  "D" : [7, 8, 5, 6, 3, 4, 1, 2, 9, 10, 11, 12, 13, 14],
86
86
  }
87
87
 
88
- self._channel_mapping = {
89
- 1: "D7",
90
- 2: "D8",
91
- 3: "D5",
92
- 4: "D6",
93
- 5: "D3",
94
- 6: "D4",
95
- 7: "D1",
96
- 8: "D2",
97
- 9: "D9",
98
- 10: "D10",
99
- 11: "D11",
100
- 12: "D12",
101
- 13: "D13",
102
- 14: "D14",
88
+ # self._channel_mapping = {
89
+ # 1: "D7",
90
+ # 2: "D8",
91
+ # 3: "D5",
92
+ # 4: "D6",
93
+ # 5: "D3",
94
+ # 6: "D4",
95
+ # 7: "D1",
96
+ # 8: "D2",
97
+ # 9: "D9",
98
+ # 10: "D10",
99
+ # 11: "D11",
100
+ # 12: "D12",
101
+ # 13: "D13",
102
+ # 14: "D14",
103
103
 
104
- 17: "C7",
105
- 18: "C8",
106
- 19: "C5",
107
- 20: "C6",
108
- 21: "C3",
109
- 22: "C4",
110
- 23: "C1",
111
- 24: "C2",
112
- 25: "C9",
113
- 26: "C10",
114
- 27: "C11",
115
- 28: "C12",
116
- 29: "C13",
117
- 30: "C14",
104
+ # 17: "C7",
105
+ # 18: "C8",
106
+ # 19: "C5",
107
+ # 20: "C6",
108
+ # 21: "C3",
109
+ # 22: "C4",
110
+ # 23: "C1",
111
+ # 24: "C2",
112
+ # 25: "C9",
113
+ # 26: "C10",
114
+ # 27: "C11",
115
+ # 28: "C12",
116
+ # 29: "C13",
117
+ # 30: "C14",
118
118
 
119
- 33: "B7",
120
- 34: "B8",
121
- 35: "B5",
122
- 36: "B6",
123
- 37: "B3",
124
- 38: "B4",
125
- 39: "B1",
126
- 40: "B2",
127
- 41: "B9",
128
- 42: "B10",
129
- 43: "B11",
130
- 44: "B12",
131
- 45: "B13",
132
- 46: "B14",
119
+ # 33: "B7",
120
+ # 34: "B8",
121
+ # 35: "B5",
122
+ # 36: "B6",
123
+ # 37: "B3",
124
+ # 38: "B4",
125
+ # 39: "B1",
126
+ # 40: "B2",
127
+ # 41: "B9",
128
+ # 42: "B10",
129
+ # 43: "B11",
130
+ # 44: "B12",
131
+ # 45: "B13",
132
+ # 46: "B14",
133
133
 
134
- 49: "A7",
135
- 50: "A8",
136
- 51: "A5",
137
- 52: "A6",
138
- 53: "A3",
139
- 54: "A4",
140
- 55: "A1",
141
- 56: "A2",
142
- 57: "A9",
143
- 58: "A10",
144
- 59: "A11",
145
- 60: "A12",
146
- 61: "A13",
147
- 62: "A14"
148
- }
134
+ # 49: "A7",
135
+ # 50: "A8",
136
+ # 51: "A5",
137
+ # 52: "A6",
138
+ # 53: "A3",
139
+ # 54: "A4",
140
+ # 55: "A1",
141
+ # 56: "A2",
142
+ # 57: "A9",
143
+ # 58: "A10",
144
+ # 59: "A11",
145
+ # 60: "A12",
146
+ # 61: "A13",
147
+ # 62: "A14"
148
+ # }
149
149
 
150
150
  self._lock = Lock()
151
151
 
@@ -172,6 +172,8 @@ class ARSKindlingEDFHandler(object):
172
172
  self._device_type = "C64RS"
173
173
  elif device_type == 0x40:
174
174
  self._device_type = "LJ64S1"
175
+ elif device_type == 0x60:
176
+ self._device_type = "ArsKindling"
175
177
  else:
176
178
  self._device_type = hex(device_type)
177
179
 
@@ -200,20 +202,22 @@ class ARSKindlingEDFHandler(object):
200
202
 
201
203
  #按分区写入数据
202
204
  for k in self._channel_spilt.keys():
203
- logger.trace(f'分区{k}, {self._channel_spilt[k]}')
204
- p = packet
205
- self.writeA(p, self._channel_spilt[k], k)
205
+ logger.trace(f'分区{k}写入数据到文件')
206
+ if k in packet.channels.keys():
207
+ p = packet
208
+ self.writeA(p, k)
206
209
 
207
- def writeA(self, packet: RscPacket, channel_filter, name='A'):
210
+ def writeA(self, packet: RscPacket, name='A'):
208
211
  # 参数检查
209
- if packet is None or channel_filter is None:
212
+ if packet is None:
210
213
  logger.warning("空数据,忽略")
211
214
  return
215
+
216
+ channels = packet.channels.get(name, None)
217
+ eeg = packet.eeg.get(name, None)
212
218
 
213
- channel_pos = intersection_positions(packet.channels, channel_filter)
214
-
215
- if channel_pos is None or len(channel_pos) == 0 :
216
- logger.trace(f"没有指定分区{name}的通道,跳过")
219
+ if eeg is None :
220
+ logger.trace(f"分区{name}没有可用的数据,跳过文件写入")
217
221
  return
218
222
 
219
223
  # 分区数据包写入
@@ -226,16 +230,12 @@ class ARSKindlingEDFHandler(object):
226
230
  logger.info(f"开始写入分区{name}的数据到文件")
227
231
  self._edf_handler[name] = edf_handler
228
232
 
229
- # 保留本分区的通道和数据
230
- channels = [packet.channels[p] for p in channel_pos]
231
- eeg = [packet.eeg[p] for p in channel_pos]
232
-
233
233
  # 新建数据包实例
234
234
  data = RscPacket()
235
235
  data.time_stamp = packet.time_stamp
236
236
  data.pkg_id = packet.pkg_id
237
237
  # data.channels = channels
238
- data.channels = self.channel_display(channels)
238
+ data.channels = self.channel_display(channels, name)
239
239
  data.origin_sample_rate = packet.origin_sample_rate
240
240
  data.sample_rate = packet.sample_rate
241
241
  data.sample_num = packet.sample_num
@@ -253,5 +253,5 @@ class ARSKindlingEDFHandler(object):
253
253
  for k in self._edf_handler.keys():
254
254
  self._edf_handler[k].trigger(desc, cur_time)
255
255
 
256
- def channel_display(self, channel):
257
- return [self._channel_mapping.get(channel_id, []) for channel_id in channel]
256
+ def channel_display(self, channel, name: str):
257
+ return [f'{name}-{channel_id}' for channel_id in channel]
@@ -48,8 +48,9 @@ class DeviceCommand(abc.ABC):
48
48
  """构建消息体"""
49
49
  return b''
50
50
  def pack_header(self, body_len: int) -> bytes:
51
- device_id = int(self.device.device_id) if self.device and self.device.device_id else 0
51
+ # device_id = int(self.device.device_id) if self.device and self.device.device_id else 0
52
52
  device_type = int(self.device.device_type) if self.device and self.device.device_type else 0
53
+ device_id = bytes.fromhex(self.device.device_id)[::-1] if self.device.device_id else bytes.fromhex('00000000')
53
54
 
54
55
  #兼容设计
55
56
  b_device_type = None
@@ -61,7 +62,7 @@ class DeviceCommand(abc.ABC):
61
62
  DeviceCommand.HEADER_PREFIX
62
63
  + int(2).to_bytes(1, 'little') # pkgType
63
64
  + device_type.to_bytes(1, 'little')
64
- + device_id.to_bytes(4, 'little')
65
+ + device_id
65
66
  + (DeviceCommand.HEADER_LEN + body_len + 2).to_bytes(4, 'little') # +1 for checksum
66
67
  + self.cmd_code.to_bytes(2, 'little')
67
68
  )
@@ -204,6 +205,15 @@ class StopAcquisitionCommand(DeviceCommand):
204
205
  class SetImpedanceParamCommand(DeviceCommand):
205
206
  cmd_code = 0x411
206
207
  cmd_desc = "设置阻抗测量参数"
208
+ def pack_body(self):
209
+
210
+ return self.device.gen_set_impedance_param()
211
+ def parse_body(self, body):
212
+ result = body[8]
213
+ if result == 0x00:
214
+ logger.info("阻抗测量参数设置成功")
215
+ else:
216
+ logger.warning(f"阻抗测量参数设置失败: {int(result)}")
207
217
 
208
218
  # 启动阻抗测量
209
219
  class StartImpedanceCommand(DeviceCommand):
@@ -211,13 +221,16 @@ class StartImpedanceCommand(DeviceCommand):
211
221
  cmd_desc = "启动阻抗测量"
212
222
  def pack_body(self):
213
223
  body = bytes.fromhex('0000')
214
- body += to_bytes(self.device.acq_channels)
224
+ body += to_bytes(self.device.get_impedance_channels())
215
225
  body += bytes.fromhex('0000000000000000') # 8字节占位符
216
226
  return body
217
227
 
218
228
  def parse_body(self, body):
219
- logger.info(f"Received StartImpedance response: {body.hex()}")
220
- return super().parse_body(body)
229
+ result = body[8]
230
+ if result == 0x00:
231
+ logger.info("阻抗测量启动成功")
232
+ else:
233
+ logger.warning(f"阻抗测量启动失败: {int(result)}")
221
234
 
222
235
 
223
236
  # 停止阻抗测量
@@ -0,0 +1,323 @@
1
+ from multiprocessing import Queue
2
+ from typing import Literal
3
+ from loguru import logger
4
+ from qlsdk.persist import ARSKindlingEDFHandler
5
+ from qlsdk.rsc.interface import IDevice
6
+ from qlsdk.rsc.command import *
7
+ from qlsdk.rsc.device.base import QLBaseDevice
8
+
9
+
10
+ def intersection_positions(A, B):
11
+ setB = set(B)
12
+ seen = set()
13
+ return [idx for idx, elem in enumerate(A)
14
+ if elem in setB and elem not in seen and not seen.add(elem)]
15
+
16
+ def get_sorted_indices_basic(A, B):
17
+ """
18
+ 找出数组A中存在于数组B的元素,并返回这些元素在A中的位置,且顺序与它们在B中的顺序一致。
19
+
20
+ 参数:
21
+ A (list): 待查找的数组。
22
+ B (list): 作为参考的数组。
23
+
24
+ 返回:
25
+ list: 一个列表,包含符合条件的元素在A中的索引,顺序与这些元素在B中的出现顺序一致。
26
+ """
27
+ # 1. 创建B中元素到其索引的映射
28
+ b_index_map = {value: idx for idx, value in enumerate(B)}
29
+
30
+ # 2. 筛选A中存在于B的元素,并记录其在A中的索引及在B中的位置
31
+ # 使用列表推导式,同时避免重复元素干扰(如果B有重复,以第一次出现为准)
32
+ found_elements = []
33
+ for idx_a, value in enumerate(A):
34
+ if value in b_index_map:
35
+ # 记录: (该元素在A中的索引, 该元素在B中的索引)
36
+ found_elements.append((idx_a, b_index_map[value]))
37
+
38
+ # 3. 根据元素在B中的位置进行排序
39
+ # 排序的依据是元组的第二个元素,即 b_index_map[value]
40
+ found_elements_sorted = sorted(found_elements, key=lambda x: x[1])
41
+
42
+ # 4. 提取排序后在A中的索引,形成结果数组C
43
+ array_C = [item[0] for item in found_elements_sorted]
44
+
45
+ return array_C
46
+
47
+ class ARSKindling(QLBaseDevice):
48
+
49
+ device_type = 0x60 # C64RS设备类型标识符
50
+
51
+ def __init__(self, socket):
52
+ super().__init__(socket)
53
+
54
+ self.channel_mapping = {
55
+ "A1": 55, "A2": 56, "A3": 53, "A4": 54, "A5": 51, "A6": 52, "A7": 49, "A8": 50,
56
+ "A9": 57, "A10": 58, "A11": 59, "A12": 60, "A13": 61, "A14": 62,
57
+
58
+ "B1": 39, "B2": 40, "B3": 37, "B4": 38, "B5": 35, "B6": 36, "B7": 33, "B8": 34,
59
+ "B9": 41, "B10": 42, "B11": 43, "B12": 44, "B13": 45, "B14": 46,
60
+
61
+ "C1": 23, "C2": 24, "C3": 21, "C4": 22, "C5": 19, "C6": 20, "C7": 17, "C8": 18,
62
+ "C9": 25, "C10": 26, "C11": 27, "C12": 28, "C13": 29, "C14": 30,
63
+
64
+ "D1": 7, "D2": 8, "D3": 5, "D4": 6, "D5": 3, "D6": 4, "D7": 1, "D8": 2,
65
+ "D9": 9, "D10": 10, "D11": 11, "D12": 12, "D13": 13, "D14": 14,
66
+ }
67
+
68
+ self._channel_spilt = {
69
+ "A" : [55, 56, 53, 54, 51, 52, 49, 50, 57, 58, 59, 60, 61, 62],
70
+ "B" : [39, 40, 37, 38, 35, 36, 33, 34, 41, 42, 43, 44, 45, 46],
71
+ "C" : [23, 24, 21, 22, 19, 20, 17, 18, 25, 26, 27, 28, 29, 30],
72
+ "D" : [7, 8, 5, 6, 3, 4, 1, 2, 9, 10, 11, 12, 13, 14],
73
+ }
74
+
75
+ self._channel_mapping = {
76
+ 1: "7",
77
+ 2: "8",
78
+ 3: "5",
79
+ 4: "6",
80
+ 5: "3",
81
+ 6: "4",
82
+ 7: "1",
83
+ 8: "2",
84
+ 9: "9",
85
+ 10: "10",
86
+ 11: "11",
87
+ 12: "12",
88
+ 13: "13",
89
+ 14: "14",
90
+
91
+ 17: "7",
92
+ 18: "8",
93
+ 19: "5",
94
+ 20: "6",
95
+ 21: "3",
96
+ 22: "4",
97
+ 23: "1",
98
+ 24: "2",
99
+ 25: "9",
100
+ 26: "10",
101
+ 27: "11",
102
+ 28: "12",
103
+ 29: "13",
104
+ 30: "14",
105
+
106
+ 33: "7",
107
+ 34: "8",
108
+ 35: "5",
109
+ 36: "6",
110
+ 37: "3",
111
+ 38: "4",
112
+ 39: "1",
113
+ 40: "2",
114
+ 41: "9",
115
+ 42: "10",
116
+ 43: "11",
117
+ 44: "12",
118
+ 45: "13",
119
+ 46: "14",
120
+
121
+ 49: "7",
122
+ 50: "8",
123
+ 51: "5",
124
+ 52: "6",
125
+ 53: "3",
126
+ 54: "4",
127
+ 55: "1",
128
+ 56: "2",
129
+ 57: "9",
130
+ 58: "10",
131
+ 59: "11",
132
+ 60: "12",
133
+ 61: "13",
134
+ 62: "14"
135
+ }
136
+
137
+ @classmethod
138
+ def from_parent(cls, parent:IDevice) -> IDevice:
139
+ rlt = cls(parent.socket)
140
+ rlt.device_id = parent.device_id
141
+ rlt._device_no = parent.device_no
142
+ return rlt
143
+
144
+ def init_edf_handler(self):
145
+ self._edf_handler = ARSKindlingEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
146
+ self._edf_handler.set_device_type(self.device_type)
147
+ self._edf_handler.set_device_no(self.device_no)
148
+ self._edf_handler.set_storage_path(self._storage_path)
149
+ self._edf_handler.set_file_prefix(self._file_prefix if self._file_prefix else 'ARS')
150
+ logger.debug(f"EDF Handler initialized")
151
+
152
+ # 设置采集参数
153
+ def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
154
+ self._acq_param["original_channels"] = channels
155
+
156
+ # 根据映射关系做通道转换
157
+ for k in channels.keys():
158
+ if isinstance(channels[k], list):
159
+ temp = [k + str(i) for i in channels[k]]
160
+ channels[k] = [self.channel_mapping.get(c, 1) for c in temp]
161
+ else:
162
+ channels[k] = [k + str(channels[k])]
163
+
164
+
165
+ # 更新采集参数
166
+ self._acq_param["channels"] = channels
167
+ self._acq_param["sample_rate"] = sample_rate
168
+ self._acq_param["sample_range"] = sample_range
169
+ self._acq_channels = channels
170
+ self._sample_rate = sample_rate
171
+ self._sample_range = sample_range
172
+
173
+ # 设置阻抗通道
174
+ # {'A': [1,2,3], 'B':[1,2,3]}
175
+ def set_impedance_channels(self, channels):
176
+
177
+ arr = []
178
+
179
+ # 根据映射关系做通道转换
180
+ for k in channels.keys():
181
+ if isinstance(channels[k], list):
182
+ temp = [k + str(i) for i in channels[k]]
183
+ arr += [self.channel_mapping.get(c, 1) for c in temp]
184
+ else:
185
+ arr += [k + str(channels[k])]
186
+
187
+ self._impedance_channels = arr
188
+
189
+ @property
190
+ def acq_channels(self):
191
+ if self._acq_channels is None:
192
+ self._acq_channels = [i for i in range(1, 63)]
193
+
194
+ arr = []
195
+ for k in self._acq_channels.keys():
196
+ arr = list(arr + self._acq_channels[k])
197
+
198
+ return list(set(arr))
199
+
200
+ def _produce_impedance(self, body: bytes):
201
+ # 分发阻抗数据包给订阅者
202
+ if len(self._impedance_consumer) > 0:
203
+ packet = self._impedance_wrapper(body)
204
+ real_data = self.__impedance_transfer(packet)
205
+ for topic, q in self._impedance_consumer.items():
206
+ try:
207
+ # 队列满了就丢弃最早的数据
208
+ if q.full():
209
+ q.get()
210
+ q.put(real_data, timeout=1)
211
+ except Exception as e:
212
+ logger.error(f"impedance data put to queue exception: {str(e)}")
213
+
214
+ def _produce_signal(self, body: bytes):
215
+
216
+ # 处理信号数据
217
+ data = self._signal_wrapper(body)
218
+ # logger.debug("pkg_id: {}, eeg len: {}".format(data.pkg_id, len(data.eeg)))
219
+
220
+ trigger_positions = [index for index, value in enumerate(data.trigger) if value != 0]
221
+ if len(trigger_positions) > 0:
222
+ # logger.debug(f"Trigger触发点位置: {trigger_positions}, 触发点时间戳: {[data.time_stamp + int(pos * 1000 / data.sample_rate) for pos in trigger_positions]}")
223
+ for pos in trigger_positions:
224
+ self.trigger(self.trigger_info(data.trigger[pos]))
225
+
226
+ if len(self.signal_consumers) > 0 :
227
+ # 信号数字值转物理值
228
+ data.eeg_p = self.eeg2phy(np.array(data.eeg))
229
+
230
+ real_data = self.__signal_transfer(data)
231
+
232
+ # 存储
233
+ if self.storage_enable:
234
+ # 确保记录线程启动
235
+ if self._recording is False:
236
+ self._start_recording()
237
+
238
+ # 写入文件的缓存队列
239
+ if self._signal_cache is None:
240
+ self._signal_cache = Queue(1000000) # 缓冲队列
241
+ self._signal_cache.put(real_data)
242
+
243
+ if len(self.signal_consumers) > 0 :
244
+ # 订阅只返回物理值
245
+ real_data.eeg = real_data.eeg_p
246
+ # 发送数据包到订阅者
247
+ for q in list(self.signal_consumers.values()):
248
+ # 队列满了就丢弃最早的数据
249
+ if q.full():
250
+ q.get()
251
+
252
+ q.put(real_data)
253
+
254
+ # 信号数据转换
255
+ def __impedance_transfer(self, packet: ImpedancePacket):
256
+ channels = {}
257
+ impedance = {}
258
+ #按分区拆分数据格式
259
+ for k in self._channel_spilt.keys():
260
+ logger.trace(f'分区{k}的阻抗数据')
261
+ c, d, p = self.__packet_filter(packet, self._channel_spilt[k], type='impedance')
262
+ if c is not None:
263
+ channels[k] = c
264
+ impedance[k] = d
265
+ packet.channels = channels
266
+ packet.impedance = impedance
267
+
268
+ return packet
269
+
270
+ # 信号数据转换
271
+ def __signal_transfer(self, packet: RscPacket):
272
+ channels = {}
273
+ eeg = {}
274
+ eeg_p = {}
275
+ #按分区拆分数据格式
276
+ for k in self._channel_spilt.keys():
277
+ logger.trace(f'分区{k}, {self._channel_spilt[k]}')
278
+ c, d, p = self.__packet_filter(packet, self._channel_spilt[k])
279
+ if c is not None:
280
+ channels[k] = c
281
+ eeg[k] = d
282
+ if p is not None:
283
+ eeg_p[k] = p
284
+ packet.channels = channels
285
+ packet.eeg = eeg
286
+ #物理值
287
+ if packet.eeg_p is not None:
288
+ packet.eeg_p = eeg_p
289
+
290
+ return packet
291
+
292
+ def __packet_filter(self, packet, channel_filter=None, type:Literal['signal','impedance']='signal'):
293
+ # 参数检查
294
+ if packet is None or channel_filter is None:
295
+ logger.warning("空数据,忽略")
296
+ return None, None, None
297
+
298
+ channel_pos = get_sorted_indices_basic(packet.channels, channel_filter)
299
+
300
+ if channel_pos is None or len(channel_pos) == 0 :
301
+ logger.trace(f"没有指定分区的通道,跳过")
302
+ return None, None, None
303
+
304
+ # 分区数据包写入
305
+
306
+ # 保留本分区的通道和数据
307
+ channels = [packet.channels[p] for p in channel_pos]
308
+
309
+ #阻抗数据
310
+ if type == 'impedance':
311
+ impedance = [packet.impedance[p] for p in channel_pos]
312
+ return [self._channel_mapping.get(channel_id, []) for channel_id in channels], impedance, None
313
+
314
+ # 信号数据
315
+ eeg = [packet.eeg[p] for p in channel_pos]
316
+ eeg_p = None
317
+ if packet.eeg_p is not None:
318
+ eeg_p = [packet.eeg_p[p] for p in channel_pos]
319
+
320
+ return [self._channel_mapping.get(channel_id, []) for channel_id in channels], eeg, eeg_p
321
+
322
+
323
+
@@ -8,11 +8,27 @@ import numpy as np
8
8
  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
- from qlsdk.rsc.command import StartImpedanceCommand, StopImpedanceCommand, StartStimulationCommand, StopStimulationCommand, SetAcquisitionParamCommand, StartAcquisitionCommand, StopAcquisitionCommand
11
+ from qlsdk.rsc.command import SetImpedanceParamCommand, StartImpedanceCommand, StopImpedanceCommand, StartStimulationCommand, StopStimulationCommand, SetAcquisitionParamCommand, StartAcquisitionCommand, StopAcquisitionCommand
12
12
  from qlsdk.rsc.paradigm import StimulationParadigm
13
13
  from qlsdk.rsc.parser.base import TcpMessageParser
14
14
 
15
+
16
+ def intersection_positions(A, B):
17
+ setB = set(B)
18
+ seen = set()
19
+ return [idx for idx, elem in enumerate(A)
20
+ if elem in setB and elem not in seen and not seen.add(elem)]
21
+
15
22
  class QLBaseDevice(IDevice):
23
+
24
+ __TRIGGER_MAPPING = {
25
+ 0x3E8: "Start of stimulation",
26
+ 0x3E9: "End of stimulation",
27
+ 0x3EA: "Ascending end of stimulation",
28
+ 0x3EB: "Descending start of stimulation ",
29
+ 0x3EC: "刺激参数有误",
30
+ 0x3ED: "End of stimulation (by force)",
31
+ }
16
32
  def __init__(self, socket):
17
33
  self.socket = socket
18
34
 
@@ -62,35 +78,9 @@ class QLBaseDevice(IDevice):
62
78
  "channels": [],
63
79
  }
64
80
 
65
- self._stim_param = {
66
- "stim_type": 0, # 刺激类型:0-所有通道参数相同, 1: 通道参数不同
67
- "channels": [],
68
- "param": [{
69
- "channel_id": 0, #通道号 从0开始 -- 必填
70
- "waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
71
- "current": 1, #电流强度(mA) -- 必填
72
- "duration": 30, #平稳阶段持续时间(s) -- 必填
73
- "ramp_up": 5, #上升时间(s) 默认0
74
- "ramp_down": 5, #下降时间(s) 默认0
75
- "frequency": 500, #频率(Hz) -- 非直流必填
76
- "phase_position": 0, #相位 -- 默认0
77
- "duration_delay": "0", #延迟启动时间(s) -- 默认0
78
- "pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
79
- },
80
- {
81
- "channel_id": 1, #通道号 从0开始 -- 必填
82
- "waveform": 3, #波形类型:0-直流,1-交流 2-方波 3-脉冲 -- 必填
83
- "current": 1, #电流强度(mA) -- 必填
84
- "duration": 30, #平稳阶段持续时间(s) -- 必填
85
- "ramp_up": 5, #上升时间(s) 默认0
86
- "ramp_down": 5, #下降时间(s) 默认0
87
- "frequency": 500, #频率(Hz) -- 非直流必填
88
- "phase_position": 0, #相位 -- 默认0
89
- "duration_delay": "0", #延迟启动时间(s) -- 默认0
90
- "pulse_width": 0, #脉冲宽度(us) -- 仅脉冲类型电流有效, 默认100us
91
- }
92
- ]
93
- }
81
+ self._stim_param = None
82
+
83
+ self._impedance_channels = []
94
84
 
95
85
  self.stim_paradigm: StimulationParadigm = None
96
86
  # 信号采集状态
@@ -116,6 +106,7 @@ class QLBaseDevice(IDevice):
116
106
  self._signal_cache: Queue = None
117
107
  self._recording = False
118
108
 
109
+
119
110
  def parser(self) -> IParser:
120
111
  return self._parser
121
112
 
@@ -132,11 +123,14 @@ class QLBaseDevice(IDevice):
132
123
  self._produce_impedance(body)
133
124
 
134
125
  def _produce_impedance(self, body: bytes):
135
- packet = ImpedancePacket.transfer(body)
136
126
  # 分发阻抗数据包给订阅者
137
127
  if len(self._impedance_consumer) > 0:
128
+ packet = self._impedance_wrapper(body)
138
129
  for topic, q in self._impedance_consumer.items():
139
130
  try:
131
+ # 队列满了就丢弃最早的数据
132
+ if q.full():
133
+ q.get()
140
134
  q.put(packet, timeout=1)
141
135
  except Exception as e:
142
136
  logger.error(f"impedance data put to queue exception: {str(e)}")
@@ -151,7 +145,7 @@ class QLBaseDevice(IDevice):
151
145
  if len(trigger_positions) > 0:
152
146
  # logger.debug(f"Trigger触发点位置: {trigger_positions}, 触发点时间戳: {[data.time_stamp + int(pos * 1000 / data.sample_rate) for pos in trigger_positions]}")
153
147
  for pos in trigger_positions:
154
- self.trigger(trigger_info(data.trigger[pos]))
148
+ self.trigger(self.trigger_info(data.trigger[pos]))
155
149
  # 存储
156
150
  if self.storage_enable:
157
151
  # 确保记录线程启动
@@ -175,7 +169,17 @@ class QLBaseDevice(IDevice):
175
169
  if q.full():
176
170
  q.get()
177
171
 
178
- q.put(data)
172
+ q.put(data)
173
+
174
+ def _impedance_wrapper(self, body: bytes):
175
+ packet = ImpedancePacket().transfer(body)
176
+ if self._impedance_channels is not None and len(self._impedance_channels) > 0:
177
+ # 只保留设置的阻抗通道
178
+ channel_pos = intersection_positions(packet.channels, self._impedance_channels)
179
+ packet.impedance = [packet.impedance[i] for i in channel_pos]
180
+ packet.channels = [packet.channels[i] for i in channel_pos]
181
+
182
+ return packet
179
183
 
180
184
  # 信号数据转换
181
185
  def _signal_wrapper(self, body: bytes):
@@ -249,6 +253,12 @@ class QLBaseDevice(IDevice):
249
253
  def device_type(self) -> int:
250
254
  return self._device_type
251
255
 
256
+ def set_impedance_channels(self, channels):
257
+ self._impedance_channels = channels
258
+
259
+ def get_impedance_channels(self):
260
+ return self._impedance_channels
261
+
252
262
  def start_message_parser(self) -> None:
253
263
  self._parser = TcpMessageParser(self)
254
264
  self._parser.start()
@@ -384,9 +394,17 @@ class QLBaseDevice(IDevice):
384
394
 
385
395
  def start_impedance(self):
386
396
  logger.info(f"[设备-{self.device_no}]启动阻抗测量")
387
- msg = StartImpedanceCommand.build(self).pack()
388
- logger.trace(f"start_impedance message is {msg.hex()}")
389
- self.socket.sendall(msg)
397
+ # 设置数据采集参数
398
+ # set_param_msg = SetImpedanceParamCommand.build(self).pack()
399
+ device_id = bytes.fromhex(self.device_id)[::-1].hex() if self.device_id else '00000000'
400
+ set_param_msg = bytes.fromhex(f'5aa50239{device_id}3f0000001104ffffffffffffffff000000000000000000000000000000000000000000000000e8030000fa00000010000164000000745c5aa50239390045243a00000012040000000000000000000000000000000000000000000000000000000000000000000001000000000000004c2a')
401
+ logger.debug(f"set_param_msg message is {set_param_msg.hex()}")
402
+ self.socket.sendall(set_param_msg)
403
+ sleep(0.5)
404
+
405
+ impedance_start_msg = StartImpedanceCommand.build(self).pack()
406
+ logger.debug(f"start_impedance message is {impedance_start_msg.hex()}")
407
+ self.socket.sendall(impedance_start_msg)
390
408
 
391
409
  def stop_impedance(self):
392
410
  logger.info(f"[设备{self.device_no}]停止阻抗测量")
@@ -490,6 +508,7 @@ class QLBaseDevice(IDevice):
490
508
  else:
491
509
  logger.info("已关闭文件记录,不会记录trigger信息")
492
510
 
511
+ # 设置信号采集参数
493
512
  def gen_set_acquirement_param(self) -> bytes:
494
513
 
495
514
  body = to_bytes(self.acq_channels)
@@ -500,7 +519,19 @@ class QLBaseDevice(IDevice):
500
519
  body += bytes.fromhex('00')
501
520
 
502
521
  return body
503
-
522
+ # 设置阻抗测量参数
523
+ def gen_set_impedance_param(self) -> bytes:
524
+
525
+ # 仅通道生效 32字节,其他不生效-272字节,实际73字节
526
+ body = to_bytes(self._impedance_channels)
527
+ # 100 bytes
528
+ # body += bytes.fromhex('00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
529
+ # # 100 bytes
530
+ # body += bytes.fromhex('00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
531
+ # 73 bytes
532
+ body += bytes.fromhex('00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
533
+
534
+ return bytes.fromhex('ffffffffffffffff000000000000000000000000000000000000000000000000e8030000fa00000010000164000000745c5aa50239390045243a0000001204000000000000000000000000000000000000000000000000000000000000000000000100000000000000')
504
535
  def disconnect(self):
505
536
  logger.info(f"[断开设备-{self.device_no}]的连接...")
506
537
  self._listening = False
@@ -517,6 +548,9 @@ class QLBaseDevice(IDevice):
517
548
 
518
549
  def enable_storage(self, enable: bool = True):
519
550
  self.storage_enable = enable
551
+
552
+ def trigger_info(self, code: int) -> str:
553
+ return QLBaseDevice.__TRIGGER_MAPPING.get(code, hex(code))
520
554
 
521
555
  def __str__(self):
522
556
  return f'''
@@ -537,15 +571,4 @@ class QLBaseDevice(IDevice):
537
571
  return self.device_type == other.device_type and self.device_no == other.device_no
538
572
 
539
573
  def __hash__(self):
540
- return hash((self.device_type, self.device_no))
541
-
542
- __TRIGGER_MAPPING = {
543
- 0x3E8: "Start of stimulation",
544
- 0x3E9: "End of stimulation",
545
- 0x3EA: "Ascending end of stimulation",
546
- 0x3EB: "Descending start of stimulation ",
547
- 0x3EC: "刺激参数有误",
548
- 0x3ED: "End of stimulation (by force)",
549
- }
550
- def trigger_info(code: int) -> str:
551
- return __TRIGGER_MAPPING.get(code, "未知触发事件")
574
+ return hash((self.device_type, self.device_no))
@@ -24,6 +24,10 @@ class IDevice(ABC):
24
24
  @property
25
25
  def device_no(self) -> str:
26
26
  pass
27
+
28
+ def set_impedance_channels(self):
29
+ raise NotImplementedError("Not Supported")
30
+
27
31
 
28
32
  def read_msg(self, size: int) -> bytes:
29
33
  raise NotImplementedError("Not Supported")
@@ -69,4 +73,13 @@ class IDevice(ABC):
69
73
  pass
70
74
 
71
75
  def enable_storage(self, enable: bool = True):
72
- pass
76
+ pass
77
+
78
+ def set_impedance_channel(self):
79
+ raise NotImplementedError("Not Supported")
80
+
81
+ def start_impedance(self):
82
+ raise NotImplementedError("Not Supported")
83
+
84
+ def stop_impedance(self):
85
+ raise NotImplementedError("Not Supported")
@@ -191,7 +191,8 @@ class ACStimulation(StimulationChannel):
191
191
  self.frequency = frequency
192
192
  # self.frequency = frequency
193
193
  self.phase_position = phase_position
194
-
194
+ if current < 0:
195
+ self.phase_position = (phase_position + 180) % 360
195
196
  def to_json(self):
196
197
  return {
197
198
  "channel_id": self.channel_id,
@@ -53,7 +53,7 @@ class TcpMessageParser(IParser):
53
53
  buf_len = get_len(self.buffer)
54
54
 
55
55
  if buf_len < self.header_len:
56
- logger.trace(f"等待数据中...: expect: {self.header_len}, actual: {buf_len}")
56
+ # logger.trace(f"等待数据中...: expect: {self.header_len}, actual: {buf_len}")
57
57
  if not self.__fill_from_cache():
58
58
  time.sleep(0.1)
59
59
  continue
@@ -64,7 +64,7 @@ class TcpMessageParser(IParser):
64
64
  start_pos = self.buffer.tell()
65
65
  head = self.buffer.read(2)
66
66
  if head != self.header:
67
- logger.trace(f"数据包头部不匹配: {head.hex()}, 期望: {self.header.hex()},继续查找...")
67
+ # logger.trace(f"数据包头部不匹配: {head.hex()}, 期望: {self.header.hex()},继续查找...")
68
68
  self.buffer.seek(start_pos + 1) # 移动到下一个字节
69
69
  continue
70
70
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qlsdk2
3
- Version: 0.6.0a9
3
+ Version: 0.6.0a12
4
4
  Summary: SDK for quanlan device
5
5
  Home-page: https://github.com/hehuajun/qlsdk
6
6
  Author: hehuajun
@@ -1,80 +0,0 @@
1
- from loguru import logger
2
- from qlsdk.persist import ARSKindlingEDFHandler
3
- from qlsdk.rsc.interface import IDevice
4
- from qlsdk.rsc.command import *
5
- from qlsdk.rsc.device.base import QLBaseDevice
6
-
7
- class ARSKindling(QLBaseDevice):
8
-
9
- device_type = 0x60 # C64RS设备类型标识符
10
-
11
- def __init__(self, socket):
12
- super().__init__(socket)
13
-
14
- self.channel_mapping = {
15
- "A1": 55, "A2": 56, "A3": 53, "A4": 54, "A5": 51, "A6": 52, "A7": 49, "A8": 50,
16
- "A9": 57, "A10": 58, "A11": 59, "A12": 60, "A13": 61, "A14": 62,
17
-
18
- "B1": 39, "B2": 40, "B3": 37, "B4": 38, "B5": 35, "B6": 36, "B7": 33, "B8": 34,
19
- "B9": 41, "B10": 42, "B11": 43, "B12": 44, "B13": 45, "B14": 46,
20
-
21
- "C1": 23, "C2": 24, "C3": 21, "C4": 22, "C5": 19, "C6": 20, "C7": 17, "C8": 18,
22
- "C9": 25, "C10": 26, "C11": 27, "C12": 28, "C13": 29, "C14": 30,
23
-
24
- "D1": 7, "D2": 8, "D3": 5, "D4": 6, "D5": 3, "D6": 4, "D7": 1, "D8": 2,
25
- "D9": 9, "D10": 10, "D11": 11, "D12": 12, "D13": 13, "D14": 14,
26
- }
27
-
28
- @classmethod
29
- def from_parent(cls, parent:IDevice) -> IDevice:
30
- rlt = cls(parent.socket)
31
- rlt.device_id = parent.device_id
32
- rlt._device_no = parent.device_no
33
- return rlt
34
-
35
- def init_edf_handler(self):
36
- self._edf_handler = ARSKindlingEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
37
- self._edf_handler.set_device_type(self.device_type)
38
- self._edf_handler.set_device_no(self.device_no)
39
- self._edf_handler.set_storage_path(self._storage_path)
40
- self._edf_handler.set_file_prefix(self._file_prefix if self._file_prefix else 'ARS')
41
- logger.debug(f"EDF Handler initialized")
42
-
43
- # 设置采集参数
44
- def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
45
- self._acq_param["original_channels"] = channels
46
-
47
- # 根据映射关系做通道转换
48
- for k in channels.keys():
49
- if isinstance(channels[k], list):
50
- temp = [k + str(i) for i in channels[k]]
51
- channels[k] = [self.channel_mapping.get(c, 1) for c in temp]
52
- else:
53
- channels[k] = [k + str(channels[k])]
54
-
55
-
56
- # 更新采集参数
57
- self._acq_param["channels"] = channels
58
- self._acq_param["sample_rate"] = sample_rate
59
- self._acq_param["sample_range"] = sample_range
60
- self._acq_channels = channels
61
- self._sample_rate = sample_rate
62
- self._sample_range = sample_range
63
-
64
- def gen_set_acquirement_param(self) -> bytes:
65
-
66
- arr = []
67
- for k in self.acq_channels.keys():
68
- arr = list(arr + self.acq_channels[k])
69
-
70
- arr = list(set(arr))
71
- body = to_bytes(arr)
72
- body += self.sample_range.to_bytes(4, byteorder='little')
73
- body += self.sample_rate.to_bytes(4, byteorder='little')
74
- body += self.sample_num.to_bytes(4, byteorder='little')
75
- body += self.resolution.to_bytes(1, byteorder='little')
76
- body += bytes.fromhex('00')
77
-
78
- return body
79
-
80
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes