qlsdk2 0.5.1.1__py3-none-any.whl → 0.6.0__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.
@@ -19,65 +19,197 @@ 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
- def transfer(self, body: bytes) -> 'RscPacket':
25
- self.time_stamp = int.from_bytes(body[0:8], 'little')
26
- self.result = body[8]
27
- self.pkg_id = int.from_bytes(body[9: 13], 'little')
28
- logger.trace(f"pkg_id: {self.pkg_id}")
29
- self.channels = to_channels(body[13: 45])
30
- self.origin_sample_rate = int.from_bytes(body[45: 49], 'little')
31
- self.sample_rate = int.from_bytes(body[49: 53], 'little')
32
- self.sample_num = int.from_bytes(body[53: 57], 'little')
33
- self.resolution = int(int(body[57]) / 8)
34
- self.filter = body[58]
35
- self.data_len = int.from_bytes(body[59: 63], 'little')
25
+ @staticmethod
26
+ def transfer(body: bytes) -> 'RscPacket':
27
+ packet = RscPacket()
28
+ packet.time_stamp = int.from_bytes(body[0:8], 'little')
29
+ packet.result = body[8]
30
+ packet.pkg_id = int.from_bytes(body[9: 13], 'little')
31
+ logger.trace(f"pkg_id: {packet.pkg_id}")
32
+ packet.channels = to_channels(body[13: 45])
33
+ packet.origin_sample_rate = int.from_bytes(body[45: 49], 'little')
34
+ packet.sample_rate = int.from_bytes(body[49: 53], 'little')
35
+ packet.sample_num = int.from_bytes(body[53: 57], 'little')
36
+ packet.resolution = int(int(body[57]) / 8)
37
+ packet.filter = body[58]
38
+ packet.data_len = int.from_bytes(body[59: 63], 'little')
36
39
  # 步径 相同通道的点间隔
37
- step = int(len(self.channels) * self.resolution + 4)
38
- self.trigger = [int.from_bytes(body[i:i+4], 'little') for i in range(63, len(body) - 3, step)]
40
+ step = int(len(packet.channels) * packet.resolution + 4)
39
41
  b_eeg = body[63:]
40
- ch_num = len(self.channels)
41
- self.eeg = [
42
+ ch_num = len(packet.channels)
43
+ # 字节序列(4Cn{channel_size}){sample_num})
44
+ packet.trigger = [int.from_bytes(b_eeg[j * step : j * step + 4], 'little', signed=False) for j in range(packet.sample_num)]
45
+
46
+ packet.eeg = [
42
47
  [
43
- int.from_bytes(b_eeg[i * self.resolution + 4 + j * step:i * self.resolution + 4 + j * step + 3], 'big', signed=True)
44
- for j in range(self.sample_num)
48
+ int.from_bytes(b_eeg[i * packet.resolution + 4 + j * step:i * packet.resolution + 4 + j * step + 3], 'big', signed=True)
49
+ for j in range(packet.sample_num)
45
50
  ]
46
51
  for i in range(ch_num)
47
52
  ]
48
53
 
49
- # logger.trace(self)
50
- return self
54
+ logger.trace(packet)
55
+
56
+ return packet
57
+
58
+ def copy(self) -> 'RscPacket':
59
+ packet = RscPacket()
60
+ packet.time_stamp = self.time_stamp
61
+ packet.pkg_id = self.pkg_id
62
+ packet.result = self.result
63
+ packet.channels = self.channels
64
+ packet.origin_sample_rate = self.origin_sample_rate
65
+ packet.sample_rate = self.sample_rate
66
+ packet.sample_num = self.sample_num
67
+ packet.resolution = self.resolution
68
+ packet.filter = self.filter
69
+ packet.data_len = self.data_len
70
+ packet.trigger = self.trigger
71
+ packet.eeg = self.eeg
72
+ return packet
51
73
 
52
74
  def __str__(self):
53
- return f"""
54
- time_stamp: {self.time_stamp}
55
- pkg_id: {self.pkg_id}
56
- origin_sample_rate: {self.origin_sample_rate}
57
- sample_rate: {self.sample_rate}
58
- sample_num: {self.sample_num}
59
- resolution: {self.resolution}
60
- filter: {self.filter}
61
- channels: {self.channels}
62
- data len: {self.data_len}
63
- trigger: {self.trigger}
64
- eeg: {self.eeg}
65
- """
75
+ return f"""[
76
+ "time_stamp": {self.time_stamp}
77
+ "pkg_id": {self.pkg_id}
78
+ "channels": {self.channels}
79
+ "trigger": {self.trigger}
80
+ "eeg": {self.eeg}
81
+ ]"""
66
82
 
67
83
  class ImpedancePacket(Packet):
68
84
  def __init__(self):
69
85
  super().__init__()
70
- self.data_len = None
71
86
  self.impedance = None
72
87
 
73
- def transfer(self, body:bytes) -> 'ImpedancePacket':
74
- self.time_stamp = int.from_bytes(body[0:8], 'little')
75
- self.result = body[8]
76
- self.pkg_id = int.from_bytes(body[9: 13], 'little')
77
- self.channels = to_channels(body[13: 45])
88
+ @staticmethod
89
+ def transfer(body:bytes) -> 'ImpedancePacket':
90
+ packet = ImpedancePacket()
91
+ packet.time_stamp = int.from_bytes(body[0:8], 'little')
92
+ # packet.result = body[8]
93
+ packet.pkg_id = int.from_bytes(body[9: 13], 'little')
94
+ packet.channels = to_channels(body[13: 45])
95
+ # packet.sample_rate = int.from_bytes(body[45: 49], 'little')
96
+ # packet.sample_len = int.from_bytes(body[49: 53], 'little')
97
+ # packet.resolution = int(int(body[53]) / 8)
98
+ # packet.filter = int(int(body[54]) / 8)
99
+ # packet.wave_type = int(int(body[55]) / 8)
100
+ # packet.wave_freq = int.from_bytes(body[56: 60], 'little')
101
+ # packet.data_len = int.from_bytes(body[60: 64], 'little')
102
+ b_impedance = body[64:]
103
+ packet.impedance = [int.from_bytes(b_impedance[j * 4 : j * 4 + 4], 'little', signed=False) for j in range(len(packet.channels))]
104
+
105
+ logger.trace(f"impedance: {packet}")
78
106
 
79
- logger.debug(f"impedance: {self}")
107
+ return packet
108
+
109
+ def __str__(self):
110
+ return f"""[
111
+ "time_stamp": {self.time_stamp}
112
+ "pkg_id": {self.pkg_id}
113
+ "channels": {self.channels}
114
+ "impedance": {self.impedance}
115
+ ]"""
80
116
 
117
+
118
+ class C256RSPacket(Packet):
119
+ def __init__(self):
120
+ super().__init__()
121
+ self.origin_sample_rate = None
122
+ self.sample_rate = None
123
+ self.sample_num = None
124
+ self.resolution = None
125
+ self.filter = None
126
+ self.data_len = None
127
+ self.trigger = None
128
+ self.eeg = None
129
+
130
+ @staticmethod
131
+ def transfer(body: bytes) -> 'RscPacket':
132
+ packet = RscPacket()
133
+ packet.time_stamp = int.from_bytes(body[0:8], 'little')
134
+ packet.result = body[8]
135
+ packet.pkg_id = int.from_bytes(body[9: 13], 'little')
136
+ packet.channels = to_channels(body[13: 45])
137
+ logger.trace(f"pkg_id: {packet.pkg_id}, channels: {packet.channels}")
138
+
139
+ packet.origin_sample_rate = int.from_bytes(body[45: 49], 'little')
140
+ packet.sample_rate = int.from_bytes(body[49: 53], 'little')
141
+ packet.sample_num = int.from_bytes(body[53: 57], 'little')
142
+ packet.resolution = int(int(body[57]) / 8)
143
+ packet.filter = body[58]
144
+ packet.data_len = int.from_bytes(body[59: 63], 'little')
145
+
146
+ # 数据块
147
+ b_eeg = body[63:]
148
+ # 根据值域分割数组-代表设备的4个模块
149
+ ranges = [(1, 64), (65, 128), (129, 192), (193, 256)]
150
+ sub_channels = [[x for x in packet.channels if low <= x <= high] for low, high in ranges]
151
+ # 只处理选中的模块
152
+ sub_channels = [_ for _ in sub_channels if len(_) > 0]
153
+ # 步径 相同通道的点间隔
154
+ step = int(len(packet.channels) * packet.resolution + 4 * len(sub_channels))
155
+ offset = 0
156
+
157
+ # 分按子模块处理
158
+ for channels in sub_channels:
159
+ logger.trace(f"子数组: {channels} 长度: {len(channels)}")
160
+ channel_size = len(channels)
161
+
162
+ # 模块没有选中通道的,跳过
163
+ if channel_size == 0:
164
+ continue
165
+
166
+ # 只保留第一个有效模块的trigger,其他的模块是冗余信息,无实际含义
167
+ if packet.trigger is None:
168
+ packet.trigger = [int.from_bytes(b_eeg[j * step : j * step + 4], 'little', signed=False) for j in range(packet.sample_num)]
169
+ logger.trace(f"trigger: {packet.trigger}")
170
+ trigger_positions = [index for index, value in enumerate(packet.trigger) if value != 0]
171
+ if len(trigger_positions) > 0:
172
+ logger.debug(f"Trigger触发点位置: {trigger_positions}, 触发点时间戳: {[packet.time_stamp + int(pos * 1000 / packet.sample_rate) for pos in trigger_positions]}")
173
+
174
+ eeg = [
175
+ [
176
+ int.from_bytes(b_eeg[offset + step * j + 4 + k * packet.resolution : offset + step * j + 7 + k * packet.resolution], 'big', signed=True)
177
+ for j in range(packet.sample_num)
178
+ ]
179
+ for k in range(channel_size)
180
+ ]
181
+ packet.eeg = packet.eeg + eeg if packet.eeg else eeg
182
+
183
+ offset += 4 + channel_size * packet.resolution
184
+
185
+ logger.trace(packet)
186
+ return packet
187
+
188
+
189
+ class C256ImpedancePacket(Packet):
190
+ def __init__(self):
191
+ super().__init__()
192
+ self.impedance = None
193
+
194
+ @staticmethod
195
+ def transfer(body:bytes) -> 'ImpedancePacket':
196
+ packet = ImpedancePacket()
197
+ packet.time_stamp = int.from_bytes(body[0:8], 'little')
198
+ # packet.result = body[8]
199
+ packet.pkg_id = int.from_bytes(body[9: 13], 'little')
200
+ packet.channels = to_channels(body[13: 45])
201
+ # packet.sample_rate = int.from_bytes(body[45: 49], 'little')
202
+ # packet.sample_len = int.from_bytes(body[49: 53], 'little')
203
+ # packet.resolution = int(int(body[53]) / 8)
204
+ # packet.filter = int(int(body[54]) / 8)
205
+ # packet.wave_type = int(int(body[55]) / 8)
206
+ # packet.wave_freq = int.from_bytes(body[56: 60], 'little')
207
+ # packet.data_len = int.from_bytes(body[60: 64], 'little')
208
+ b_impedance = body[64:]
209
+ packet.impedance = [int.from_bytes(b_impedance[j : j + 4], 'little', signed=False) for j in range(len(packet.channels))]
210
+
211
+ logger.trace(f"impedance: {packet}")
212
+
81
213
  def __str__(self):
82
214
  return f"""
83
215
  time_stamp: {self.time_stamp}
@@ -86,4 +218,5 @@ class ImpedancePacket(Packet):
86
218
  channels: {self.channels}
87
219
  data len: {self.data_len}
88
220
  impedance: {self.impedance}
89
- """
221
+ """
222
+
qlsdk/core/message/udp.py CHANGED
@@ -2,7 +2,7 @@
2
2
  from enum import Enum
3
3
  from time import timezone
4
4
  from qlsdk.core.crc import check_crc, crc16
5
- from qlsdk.core.local import get_ip
5
+ from qlsdk.core.local import get_ip, get_ips
6
6
 
7
7
  from loguru import logger
8
8
 
@@ -72,6 +72,14 @@ class UDPMessage(object):
72
72
  if server_ip is None:
73
73
  server_ip = get_ip()
74
74
 
75
+ return UDPMessage._search_(device_id, server_ip, server_port)
76
+
77
+ @staticmethod
78
+ def _search_(device_id : str, server_ip : str, server_port : int=19216):
79
+ # 服务端Ip
80
+ if server_ip is None:
81
+ raise ValueError("server_ip is None")
82
+
75
83
  logger.debug(f"search device {device_id} on {server_ip}:{server_port}")
76
84
 
77
85
  message = bytearray(28)
File without changes
File without changes
qlsdk/entity/signal.py ADDED
File without changes
@@ -0,0 +1,10 @@
1
+ from parser import *
2
+
3
+ from device import *
4
+
5
+
6
+ '''
7
+ 设备消息解析器
8
+ '''
9
+ class DeviceMessageParser:
10
+ pass
@@ -0,0 +1,2 @@
1
+ class IAnalyzer:
2
+ pass
@@ -0,0 +1,10 @@
1
+ class ICollector:
2
+ def __init__(self):
3
+ pass
4
+
5
+ def collect_data(self):
6
+ pass
7
+
8
+ class Collector(ICollector):
9
+ def __init__(self):
10
+ super().__init__()
@@ -0,0 +1,2 @@
1
+ class IDevice:
2
+ pass
@@ -0,0 +1,13 @@
1
+ class IParser:
2
+ pass
3
+
4
+ class Parser(IParser):
5
+ def __init__(self):
6
+ super().__init__()
7
+
8
+ class MessageParser(Parser):
9
+ def __init__(self):
10
+ super().__init__()
11
+
12
+ def parse(self, data):
13
+ pass
@@ -0,0 +1,2 @@
1
+ class IStimulator:
2
+ pass
@@ -0,0 +1,2 @@
1
+ class IStore:
2
+ pass
qlsdk/persist/ars_edf.py CHANGED
@@ -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
 
@@ -195,55 +197,32 @@ class ARSKindlingEDFHandler(object):
195
197
  if packet is None:
196
198
  # self._edf_writer_thread.stop_recording()
197
199
  for k in self._edf_handler.keys():
198
- self._edf_handler[k].append(None)
200
+ self._edf_handler[k].write(None)
199
201
  return
200
202
 
201
- #按分区写入数据
202
-
203
+ #按分区写入数据
203
204
  for k in self._channel_spilt.keys():
204
- logger.info(f'分区{k}, {self._channel_spilt[k]}')
205
- p = packet
206
- self.writeA(p, self._channel_spilt[k], k)
207
-
208
- # with self._lock:
209
- # if self.channels is None:
210
- # logger.info(f"开始记录数据到文件...")
211
- # self.channels = packet.channels
212
- # self._first_pkg_id = packet.pkg_id if self._first_pkg_id is None else self._first_pkg_id
213
- # self._first_timestamp = packet.time_stamp if self._first_timestamp is None else self._first_timestamp
214
- # self._start_time = datetime.now()
215
- # logger.info(f"第一个包id: {self._first_pkg_id }, 时间戳:{self._first_timestamp}, 当前时间:{datetime.now().timestamp()} offset: {datetime.now().timestamp() - self._first_timestamp}")
216
-
217
- # if self._last_pkg_id and self._last_pkg_id != packet.pkg_id - 1:
218
- # self._lost_packets += packet.pkg_id - self._last_pkg_id - 1
219
- # logger.warning(f"数据包丢失: {self._last_pkg_id} -> {packet.pkg_id}, 丢包数: {packet.pkg_id - self._last_pkg_id - 1}")
220
-
221
- # self._last_pkg_id = packet.pkg_id
222
- # self._total_packets += 1
223
-
224
- # if self._edf_writer_thread is None:
225
- # self._edf_writer_thread = EDFStreamWriter(self.channels, self.sample_rate, self.physical_max, self.physical_min, self.file_type, self.file_name)
226
- # self._edf_writer_thread.set_start_time(self._start_time)
227
- # self._edf_writer_thread.start()
228
- # logger.info(f"开始写入数据: {self.file_name}")
229
-
230
- # self._edf_writer_thread.append(packet.eeg)
205
+ logger.trace(f'分区{k}写入数据到文件')
206
+ if k in packet.channels.keys():
207
+ p = packet
208
+ self.writeA(p, k)
231
209
 
232
- def writeA(self, packet: RscPacket, channel_filter, name='A'):
210
+ def writeA(self, packet: RscPacket, name='A'):
233
211
  # 参数检查
234
- if packet is None or channel_filter is None:
212
+ if packet is None:
235
213
  logger.warning("空数据,忽略")
236
214
  return
215
+
216
+ channels = packet.channels.get(name, None)
217
+ eeg = packet.eeg.get(name, None)
237
218
 
238
- channel_pos = intersection_positions(packet.channels, channel_filter)
239
-
240
- if channel_pos is None or len(channel_pos) == 0 :
241
- logger.debug(f"没有指定分区{name}的通道,跳过")
242
- pass
219
+ if eeg is None :
220
+ logger.trace(f"分区{name}没有可用的数据,跳过文件写入")
221
+ return
243
222
 
244
223
  # 分区数据包写入
245
224
  if name not in self._edf_handler.keys():
246
- edf_handler = RscEDFHandler(self.sample_rate, self.digital_max , self.digital_min, self.resolution)
225
+ edf_handler = RscEDFHandler(self.sample_rate, self.physical_max , self.physical_min, self.resolution)
247
226
  edf_handler.set_device_type(self._device_type)
248
227
  edf_handler.set_device_no(self._device_no)
249
228
  edf_handler.set_storage_path(self._storage_path)
@@ -251,15 +230,12 @@ class ARSKindlingEDFHandler(object):
251
230
  logger.info(f"开始写入分区{name}的数据到文件")
252
231
  self._edf_handler[name] = edf_handler
253
232
 
254
- # 保留本分区的通道和数据
255
- channels = [packet.channels[p] for p in channel_pos]
256
- eeg = [packet.eeg[p] for p in channel_pos]
257
-
258
233
  # 新建数据包实例
259
234
  data = RscPacket()
260
235
  data.time_stamp = packet.time_stamp
261
236
  data.pkg_id = packet.pkg_id
262
- data.channels = channels
237
+ # data.channels = channels
238
+ data.channels = self.channel_display(channels, name)
263
239
  data.origin_sample_rate = packet.origin_sample_rate
264
240
  data.sample_rate = packet.sample_rate
265
241
  data.sample_num = packet.sample_num
@@ -276,3 +252,6 @@ class ARSKindlingEDFHandler(object):
276
252
  # trigger现在(20250702)多个分区共享, 分发到所有分区文件中标记
277
253
  for k in self._edf_handler.keys():
278
254
  self._edf_handler[k].trigger(desc, cur_time)
255
+
256
+ def channel_display(self, channel, name: str):
257
+ return [f'{name}-{channel_id}' for channel_id in channel]