qlsdk2 0.5.1.1__py3-none-any.whl → 0.6.0a1__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.
@@ -21,33 +21,37 @@ class RscPacket(Packet):
21
21
  self.trigger = None
22
22
  self.eeg = None
23
23
 
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')
24
+ @staticmethod
25
+ def transfer(body: bytes) -> 'RscPacket':
26
+ packet = RscPacket()
27
+ packet.time_stamp = int.from_bytes(body[0:8], 'little')
28
+ packet.result = body[8]
29
+ packet.pkg_id = int.from_bytes(body[9: 13], 'little')
30
+ logger.trace(f"pkg_id: {packet.pkg_id}")
31
+ packet.channels = to_channels(body[13: 45])
32
+ packet.origin_sample_rate = int.from_bytes(body[45: 49], 'little')
33
+ packet.sample_rate = int.from_bytes(body[49: 53], 'little')
34
+ packet.sample_num = int.from_bytes(body[53: 57], 'little')
35
+ packet.resolution = int(int(body[57]) / 8)
36
+ packet.filter = body[58]
37
+ packet.data_len = int.from_bytes(body[59: 63], 'little')
36
38
  # 步径 相同通道的点间隔
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)]
39
+ step = int(len(packet.channels) * packet.resolution + 4)
39
40
  b_eeg = body[63:]
40
- ch_num = len(self.channels)
41
- self.eeg = [
41
+ ch_num = len(packet.channels)
42
+ # 字节序列(4Cn{channel_size}){sample_num})
43
+ packet.trigger = [int.from_bytes(b_eeg[j * step : j * step + 4], 'little', signed=False) for j in range(packet.sample_num)]
44
+
45
+ packet.eeg = [
42
46
  [
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)
47
+ int.from_bytes(b_eeg[i * packet.resolution + 4 + j * step:i * packet.resolution + 4 + j * step + 3], 'big', signed=True)
48
+ for j in range(packet.sample_num)
45
49
  ]
46
50
  for i in range(ch_num)
47
51
  ]
48
52
 
49
53
  # logger.trace(self)
50
- return self
54
+ return packet
51
55
 
52
56
  def __str__(self):
53
57
  return f"""
@@ -86,4 +90,75 @@ class ImpedancePacket(Packet):
86
90
  channels: {self.channels}
87
91
  data len: {self.data_len}
88
92
  impedance: {self.impedance}
89
- """
93
+ """
94
+
95
+
96
+ class C256RSPacket(Packet):
97
+ def __init__(self):
98
+ super().__init__()
99
+ self.origin_sample_rate = None
100
+ self.sample_rate = None
101
+ self.sample_num = None
102
+ self.resolution = None
103
+ self.filter = None
104
+ self.data_len = None
105
+ self.trigger = None
106
+ self.eeg = None
107
+
108
+ @staticmethod
109
+ def transfer(body: bytes) -> 'RscPacket':
110
+ packet = RscPacket()
111
+ packet.time_stamp = int.from_bytes(body[0:8], 'little')
112
+ packet.result = body[8]
113
+ packet.pkg_id = int.from_bytes(body[9: 13], 'little')
114
+ packet.channels = to_channels(body[13: 45])
115
+ logger.trace(f"pkg_id: {packet.pkg_id}, channels: {packet.channels}")
116
+
117
+ packet.origin_sample_rate = int.from_bytes(body[45: 49], 'little')
118
+ packet.sample_rate = int.from_bytes(body[49: 53], 'little')
119
+ packet.sample_num = int.from_bytes(body[53: 57], 'little')
120
+ packet.resolution = int(int(body[57]) / 8)
121
+ packet.filter = body[58]
122
+ packet.data_len = int.from_bytes(body[59: 63], 'little')
123
+
124
+ # 数据块
125
+ b_eeg = body[63:]
126
+ # 根据值域分割数组-代表设备的4个模块
127
+ ranges = [(1, 64), (65, 128), (129, 192), (193, 256)]
128
+ sub_channels = [[x for x in packet.channels if low <= x <= high] for low, high in ranges]
129
+ # 只处理选中的模块
130
+ sub_channels = [_ for _ in sub_channels if len(_) > 0]
131
+ # 步径 相同通道的点间隔
132
+ step = int(len(packet.channels) * packet.resolution + 4 * len(sub_channels))
133
+ offset = 0
134
+
135
+ # 分按子模块处理
136
+ for channels in sub_channels:
137
+ logger.trace(f"子数组: {channels} 长度: {len(channels)}")
138
+ channel_size = len(channels)
139
+
140
+ # 模块没有选中通道的,跳过
141
+ if channel_size == 0:
142
+ continue
143
+
144
+ # 只保留第一个有效模块的trigger,其他的模块是冗余信息,无实际含义
145
+ if packet.trigger is None:
146
+ packet.trigger = [int.from_bytes(b_eeg[j * step : j * step + 4], 'little', signed=False) for j in range(packet.sample_num)]
147
+ logger.trace(f"trigger: {packet.trigger}")
148
+ trigger_positions = [index for index, value in enumerate(packet.trigger) if value != 0]
149
+ if len(trigger_positions) > 0:
150
+ logger.debug(f"Trigger触发点位置: {trigger_positions}, 触发点时间戳: {[packet.time_stamp + int(pos * 1000 / packet.sample_rate) for pos in trigger_positions]}")
151
+
152
+ eeg = [
153
+ [
154
+ int.from_bytes(b_eeg[offset + step * j + 4 + k * packet.resolution : offset + step * j + 7 + k * packet.resolution], 'big', signed=True)
155
+ for j in range(packet.sample_num)
156
+ ]
157
+ for k in range(channel_size)
158
+ ]
159
+ packet.eeg = packet.eeg + eeg if packet.eeg else eeg
160
+
161
+ offset += 4 + channel_size * packet.resolution
162
+
163
+ logger.trace(packet)
164
+ return packet
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/rsc_edf.py CHANGED
@@ -260,6 +260,8 @@ class RscEDFHandler(object):
260
260
  self._device_type = "C64RS"
261
261
  elif device_type == 0x40:
262
262
  self._device_type = "LJ64S1"
263
+ elif device_type == 0x51:
264
+ self._device_type = "C256RS"
263
265
  elif device_type == 0x60:
264
266
  self._device_type = "ARSKindling"
265
267
  elif device_type == 0x339:
@@ -309,11 +309,11 @@ class SignalDataCommand(DeviceCommand):
309
309
  return super().unpack(payload)
310
310
 
311
311
  def parse_body(self, body: bytes):
312
- # 解析数据包
313
- packet = RscPacket()
314
- packet.transfer(body)
312
+ # # 解析数据包
313
+ # packet = RscPacket()
314
+ # packet.transfer(body)
315
315
  # 将数据包传递给设备
316
- self.device.produce(packet)
316
+ self.device.produce(body)
317
317
 
318
318
 
319
319
 
@@ -4,4 +4,5 @@ from .device_factory import DeviceFactory
4
4
  # import devices
5
5
  from .c64_rs import C64RS
6
6
  from .c16_rs import C16RS
7
- from .arskindling import ARSKindling
7
+ from .arskindling import ARSKindling
8
+ from .c256_rs import C256RS
qlsdk/rsc/device/base.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  from multiprocessing import Queue
3
2
  from threading import Thread
4
3
  from time import sleep, time_ns
@@ -8,10 +7,9 @@ from loguru import logger
8
7
  import numpy as np
9
8
  from qlsdk.core.entity import RscPacket
10
9
  from qlsdk.core.utils import to_bytes
11
- from qlsdk.persist import RscEDFHandler
12
10
  from qlsdk.rsc.interface import IDevice, IParser
13
- from qlsdk.rsc.parser import TcpMessageParser
14
11
  from qlsdk.rsc.command import StartImpedanceCommand, StopImpedanceCommand, StartStimulationCommand, StopStimulationCommand, SetAcquisitionParamCommand, StartAcquisitionCommand, StopAcquisitionCommand
12
+ from qlsdk.rsc.parser.base import TcpMessageParser
15
13
 
16
14
  class QLBaseDevice(IDevice):
17
15
  def __init__(self, socket):
@@ -137,12 +135,18 @@ class QLBaseDevice(IDevice):
137
135
  self._record_duration = record_duration
138
136
 
139
137
  # 数据包处理
140
- def produce(self, data: RscPacket, type:Literal['signal', 'impedance']="signal"):
141
- if data is None: return
138
+ def produce(self, body: bytes, type:Literal['signal', 'impedance']="signal"):
139
+ if body is None: return
142
140
 
143
141
  # 处理信号数据
144
- self._signal_wrapper(data)
145
-
142
+ data = self._signal_wrapper(body)
143
+ # logger.debug("pkg_id: {}, eeg len: {}".format(data.pkg_id, len(data.eeg)))
144
+ #
145
+ trigger_positions = [index for index, value in enumerate(data.trigger) if value != 0]
146
+ if len(trigger_positions) > 0:
147
+ # logger.debug(f"Trigger触发点位置: {trigger_positions}, 触发点时间戳: {[data.time_stamp + int(pos * 1000 / data.sample_rate) for pos in trigger_positions]}")
148
+ for pos in trigger_positions:
149
+ self.trigger(data.trigger[pos])
146
150
  # 存储
147
151
  if self.storage_enable:
148
152
  self._write_signal(data)
@@ -159,9 +163,9 @@ class QLBaseDevice(IDevice):
159
163
 
160
164
  q.put(data)
161
165
 
162
- # 信号数据转换(默认不处理)
163
- def _signal_wrapper(self, data: RscPacket):
164
- pass
166
+ # 信号数据转换
167
+ def _signal_wrapper(self, body: bytes):
168
+ return RscPacket().transfer(body)
165
169
 
166
170
  def _write_signal(self, data: RscPacket):
167
171
  # 文件写入到edf
@@ -175,8 +179,10 @@ class QLBaseDevice(IDevice):
175
179
  def start_listening(self):
176
180
 
177
181
  try:
182
+ # 启动消息解析器
178
183
  self.start_message_parser()
179
184
 
185
+ # 启动消息监听器
180
186
  self.start_message_listening()
181
187
  except Exception as e:
182
188
  logger.error(f"设备{self.device_no}准备失败: {str(e)}")
@@ -188,7 +194,22 @@ class QLBaseDevice(IDevice):
188
194
  logger.trace(f"设备{self.device_no}停止socket监听")
189
195
  self._listening = False
190
196
  self._parser.stop()
191
-
197
+
198
+ def read_msg(self, size: int) -> bytes:
199
+ try:
200
+ self.socket.settimeout(2.0)
201
+ return self.socket.recv(size)
202
+ except Exception as e:
203
+ logger.error(f"read_msg exception: {str(e)}")
204
+ raise ValueError("read_msg exception") from e
205
+
206
+ @classmethod
207
+ def from_parent(cls, parent:IDevice) -> IDevice:
208
+ rlt = cls(parent.socket)
209
+ rlt.device_id = parent.device_id
210
+ rlt._device_no = parent.device_no
211
+ return rlt
212
+
192
213
  @property
193
214
  def device_type(self) -> int:
194
215
  return self._device_type
@@ -196,18 +217,21 @@ class QLBaseDevice(IDevice):
196
217
  def start_message_parser(self) -> None:
197
218
  self._parser = TcpMessageParser(self)
198
219
  self._parser.start()
199
- logger.debug("TCP消息解析器已启动")
220
+ logger.debug("RSC消息解析器已启动")
200
221
 
201
222
  def start_message_listening(self) -> None:
202
223
  def _accept():
203
224
  while self._listening:
204
- # 缓冲区4M
205
- data = self.socket.recv(4096*1024)
206
- if not data:
207
- logger.warning(f"设备[{self.device_name}]连接结束")
225
+ try:
226
+ # 缓冲区4M
227
+ data = self.socket.recv(4096*1024)
228
+ if not data:
229
+ logger.warning(f"设备[{self.device_name}]连接结束")
230
+ break
231
+ self._parser.append(data)
232
+ except Exception as e:
233
+ logger.debug(f"设备[{self.device_name}]接收数据异常: {str(e)}")
208
234
  break
209
-
210
- self._parser.append(data)
211
235
 
212
236
  # 启动数据接收线程
213
237
  self._listening = True
@@ -252,11 +276,6 @@ class QLBaseDevice(IDevice):
252
276
  def init_edf_handler(self):
253
277
  logger.warning("init_edf_handler not implemented in base class, should be overridden in subclass")
254
278
  pass
255
- # self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
256
- # self._edf_handler.set_device_type(self.device_type)
257
- # self._edf_handler.set_device_no(self.device_no)
258
- # self._edf_handler.set_storage_path(self._storage_path)
259
- # self._edf_handler.set_file_prefix(self._file_prefix)
260
279
 
261
280
  # eeg数字值转物理值
262
281
  def eeg2phy(self, digital:int):
@@ -444,12 +463,26 @@ class QLBaseDevice(IDevice):
444
463
 
445
464
  return body
446
465
 
466
+ def disconnect(self):
467
+ logger.info(f"[断开设备-{self.device_no}]的连接...")
468
+ self._listening = False
469
+ try:
470
+ sleep(0.1)
471
+ self.socket.shutdown(2)
472
+ self.socket.close()
473
+ logger.info(f"[设备-{self.device_no}]设备连接已断开")
474
+ except Exception as e:
475
+ logger.error(f"断开设备连接异常: {str(e)}")
476
+
477
+ # 关闭解析器
478
+ self._parser.stop()
479
+
447
480
  def __str__(self):
448
481
  return f'''
449
482
  Device:
450
483
  Name: {self.device_name},
451
484
  Type: {hex(self.device_type) if self.device_type else None},
452
- ID: {self.device_id if self.device_id else None},
485
+ ID: {self.device_no if self.device_no else None},
453
486
  Software: {self.software_version},
454
487
  Hardware: {self.hardware_version},
455
488
  Connect Time: {self.connect_time},
@@ -459,8 +492,8 @@ class QLBaseDevice(IDevice):
459
492
  Battery Total: {str(self.battery_total) + "%" if self.battery_total else None}
460
493
  '''
461
494
 
462
- def __eq__(self, other):
463
- return self.device_name == other.device_name and self.device_type == other.device_type and self.device_id == other.device_id
495
+ def __eq__(self, other:IDevice):
496
+ return self.device_type == other.device_type and self.device_no == other.device_no
464
497
 
465
498
  def __hash__(self):
466
- return hash((self.device_name, self.device_type, self.device_id))
499
+ return hash((self.device_type, self.device_no))
@@ -5,15 +5,15 @@ from time import sleep, time_ns
5
5
  from typing import Any, Dict, Literal
6
6
 
7
7
  from loguru import logger
8
+ from qlsdk.core.entity import C256RSPacket
8
9
  from qlsdk.persist import RscEDFHandler
9
- from qlsdk.rsc.interface import IDevice, IParser
10
+ from qlsdk.rsc.device.device_factory import DeviceFactory
10
11
  from qlsdk.rsc.command import *
11
- from qlsdk.rsc.parser.base import TcpMessageParser
12
12
  from qlsdk.rsc.device.base import QLBaseDevice
13
13
 
14
14
  class C256RS(QLBaseDevice):
15
15
 
16
- device_type = 0x40 # C64RS设备类型标识符
16
+ device_type = 0x51 # C64RS设备类型标识符
17
17
 
18
18
  def __init__(self, socket):
19
19
  super().__init__(socket)
@@ -125,13 +125,13 @@ class C256RS(QLBaseDevice):
125
125
  self._edf_handler = None
126
126
  self.storage_enable = True
127
127
 
128
- self._parser: IParser = TcpMessageParser(self)
129
- self._parser.start()
128
+ # self._parser: IParser = RSCMessageParser(self)
129
+ # self._parser.start()
130
130
 
131
131
  # 启动数据接收线程
132
- self._accept = Thread(target=self.accept)
133
- self._accept.daemon = True
134
- self._accept.start()
132
+ # self._accept = Thread(target=self.accept)
133
+ # self._accept.daemon = True
134
+ # self._accept.start()
135
135
 
136
136
  @property
137
137
  def device_no(self):
@@ -140,21 +140,14 @@ class C256RS(QLBaseDevice):
140
140
  @device_no.setter
141
141
  def device_no(self, value: str):
142
142
  self._device_no = value
143
-
144
- @classmethod
145
- def from_parent(cls, parent:IDevice) -> IDevice:
146
- rlt = cls(parent.socket)
147
- rlt.device_id = parent.device_id
148
- rlt._device_no = parent.device_no
149
- return rlt
150
-
143
+
151
144
 
152
145
  def init_edf_handler(self):
153
146
  self._edf_handler = RscEDFHandler(self.sample_rate, self.sample_range * 1000 , - self.sample_range * 1000, self.resolution)
154
147
  self._edf_handler.set_device_type(self.device_type)
155
- self._edf_handler.set_device_no(self.device_name)
148
+ self._edf_handler.set_device_no(self.device_no)
156
149
  self._edf_handler.set_storage_path(self._storage_path)
157
- self._edf_handler.set_file_prefix(self._file_prefix)
150
+ self._edf_handler.set_file_prefix(self._file_prefix if self._file_prefix else 'C256RS')
158
151
 
159
152
  @property
160
153
  def edf_handler(self):
@@ -169,7 +162,7 @@ class C256RS(QLBaseDevice):
169
162
  @property
170
163
  def acq_channels(self):
171
164
  if self._acq_channels is None:
172
- self._acq_channels = [i for i in range(1, 63)]
165
+ self._acq_channels = [i for i in range(1, 256)]
173
166
  return self._acq_channels
174
167
  @property
175
168
  def sample_range(self):
@@ -199,15 +192,15 @@ class C256RS(QLBaseDevice):
199
192
 
200
193
  # 接收socket消息
201
194
  def accept(self):
202
- while True:
203
- # 缓冲去4M
204
- data = self.socket.recv(4096*1024)
205
- if not data:
206
- logger.warning(f"设备{self.device_name}连接结束")
207
- break
208
-
209
- self._parser.append(data)
195
+ pass
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
210
202
 
203
+ # self._parser.append(data)
211
204
 
212
205
  # socket发送数据
213
206
  def send(self, data):
@@ -226,9 +219,8 @@ class C256RS(QLBaseDevice):
226
219
  self._sample_rate = sample_rate
227
220
  self._sample_range = sample_range
228
221
 
229
- # 通用配置-TODO
230
- def set_config(self, key:str, val: str):
231
- pass
222
+ def _signal_wrapper(self, body: bytes):
223
+ return C256RSPacket().transfer(body)
232
224
 
233
225
  def start_impedance(self):
234
226
  logger.info("启动阻抗测量")
@@ -297,7 +289,7 @@ class C256RS(QLBaseDevice):
297
289
 
298
290
  # 数据队列
299
291
  if q is None:
300
- q = Queue(maxsize=1000)
292
+ q = Queue(maxsize=10240000) # 10M缓存
301
293
 
302
294
  # 队列名称
303
295
  if topic is None:
@@ -362,4 +354,5 @@ class C256RS(QLBaseDevice):
362
354
  def __hash__(self):
363
355
  return hash((self.device_name, self.device_type, self.device_id))
364
356
 
365
-
357
+
358
+ DeviceFactory.register(C256RS.device_type, C256RS)
@@ -30,5 +30,6 @@ DeviceFactory.register(C64RS.device_type, C64RS)
30
30
  DeviceFactory.register(ARSKindling.device_type, ARSKindling)
31
31
 
32
32
 
33
+
33
34
 
34
35
 
@@ -10,118 +10,55 @@ class IDevice(ABC):
10
10
  pass
11
11
 
12
12
  def set_device_type(self, value: int):
13
- pass
13
+ raise NotImplementedError("Not Supported")
14
14
 
15
15
  def set_storage_path(self, path: str):
16
- pass
16
+ raise NotImplementedError("Not Supported")
17
17
 
18
18
  def set_file_prefix(self, pre: str):
19
- pass
19
+ raise NotImplementedError("Not Supported")
20
20
 
21
21
  def set_acq_param(self, channels, sample_rate = 500, sample_range = 188):
22
- pass
22
+ raise NotImplementedError("Not Supported")
23
23
 
24
24
  @property
25
25
  def device_no(self) -> str:
26
26
  pass
27
27
 
28
- def produce(self, data, type:Literal['signal', 'impedance']="signal") -> None:
29
- pass
28
+ def read_msg(self, size: int) -> bytes:
29
+ raise NotImplementedError("Not Supported")
30
+
31
+ def produce(self, body: bytes, type:Literal['signal', 'impedance']="signal") -> None:
32
+ raise NotImplementedError("Not Supported")
30
33
 
31
34
  def start_listening(self):
32
- pass
35
+ raise NotImplementedError("Not Supported")
33
36
 
34
37
  def stop_listening(self):
35
- pass
38
+ raise NotImplementedError("Not Supported")
36
39
 
37
40
  def set_device_no(self, value: str):
38
- pass
41
+ raise NotImplementedError("Not Supported")
39
42
 
40
43
  def from_parent(cls, parent) :
41
- pass
42
-
43
- def start_implementation(self) -> None:
44
- pass
45
-
46
- def stop_implementation(self) -> None:
47
- pass
44
+ raise NotImplementedError("Not Supported")
48
45
 
49
46
  def start_acquisition(self) -> None:
50
- pass
47
+ raise NotImplementedError("Not Supported")
51
48
 
52
49
  def stop_acquisition(self) -> None:
53
- pass
50
+ raise NotImplementedError("Not Supported")
54
51
 
55
52
  def subscribe(self, type="signal") -> None:
56
- pass
57
-
53
+ raise NotImplementedError("Not Supported")
58
54
  def unsubscribe(self, topic) -> None:
59
- pass
55
+ raise NotImplementedError("Not Supported")
60
56
 
61
57
  def start_stimulation(self, type="signal", duration=0) -> None:
62
- pass
58
+ raise NotImplementedError("Not Supported")
63
59
 
64
60
  def stop_stimulation(self) -> None:
65
- pass
66
-
67
- def gen_set_acquirement_param(self) -> bytes:
68
- pass
69
-
70
-
71
-
72
- class RscDevice(IDevice):
73
- def __init__(self, socket):
74
- super().__init__(socket)
75
-
76
- def start_implementation(self):
77
- """
78
- Start the device implementation
79
- """
80
- raise NotImplementedError("This method should be overridden by subclasses")
81
-
82
- def stop_implementation(self):
83
- """
84
- Stop the device implementation
85
- """
86
- raise NotImplementedError("This method should be overridden by subclasses")
87
-
88
- def start_acquisition(self):
89
- """
90
- Start data acquisition
91
- """
92
- raise NotImplementedError("This method should be overridden by subclasses")
93
-
94
- def stop_acquisition(self):
95
- """
96
- Stop data acquisition
97
- """
98
- raise NotImplementedError("This method should be overridden by subclasses")
99
-
100
- def subscribe(self, type="signal"):
101
- """
102
- Subscribe to data of a specific type (default is "signal")
103
- """
104
- raise NotImplementedError("This method should be overridden by subclasses")
105
-
106
- def unsubscribe(self, topic):
107
- """
108
- Unsubscribe from a specific topic
109
- """
110
- raise NotImplementedError("This method should be overridden by subclasses")
111
-
112
- def start_stimulation(self, type="signal", duration=0):
113
- """
114
- Start stimulation of a specific type (default is "signal") for a given duration
115
- """
116
- raise NotImplementedError("This method should be overridden by subclasses")
117
-
118
- def stop_stimulation(self):
119
- """
120
- Stop stimulation
121
- """
122
- raise NotImplementedError("This method should be overridden by subclasses")
123
-
124
- class ProxyDevice(IDevice):
125
- def __init__(self, socket, proxy_socket):
126
- super().__init__(socket)
127
- self.proxy_socket = proxy_socket
61
+ raise NotImplementedError("Not Supported")
62
+ def disconnect(self) -> None:
63
+ raise NotImplementedError("Not Supported")
64
+
@@ -9,4 +9,10 @@ class IParser(ABC):
9
9
  pass
10
10
 
11
11
  def set_device(self, device):
12
+ pass
13
+
14
+ def start(self):
15
+ pass
16
+
17
+ def stop(self):
12
18
  pass
@@ -90,11 +90,11 @@ class DeviceContainer(object):
90
90
  device.start_listening()
91
91
  # GET_DEVICE_INFO
92
92
  msg = GetDeviceInfoCommand.build(device).pack()
93
- logger.trace(f"发送获取设备信息命令: {msg.hex()}")
93
+ logger.debug(f"发送获取设备信息命令: {msg.hex()}")
94
94
  device.send(msg)
95
95
  # 添加设备
96
96
  while True:
97
- if device.device_name:
97
+ if device.device_no:
98
98
  real_device = DeviceFactory.create_device(device)
99
99
  device.stop_listening()
100
100
  real_device.start_listening()
@@ -117,6 +117,14 @@ class DeviceContainer(object):
117
117
  # 标记设备为已连接
118
118
  self._broadcaster.mark_device_as_connected(device.device_no)
119
119
 
120
+ def remove_device(self, device_no:str=None):
121
+ if device_no is None:
122
+ return
123
+
124
+ if device_no in self._devices:
125
+ del self._devices[device_no]
126
+ logger.info(f"从设备列表移除设备[{device_no}],已连接设备数量:{len(self._devices)}")
127
+
120
128
  def get_device(self, device_no:str=None)->IDevice:
121
129
  logger.info(f"已连接设备数量:{len(self._devices)}")
122
130
  if len(self._devices) == 0:
@@ -4,6 +4,7 @@ from threading import Thread, Lock
4
4
  from loguru import logger
5
5
 
6
6
  from qlsdk.core.message import UDPMessage
7
+ from qlsdk.core.local import get_ips
7
8
 
8
9
  '''
9
10
  广播器类,用于发送和接收设备广播消息
@@ -55,6 +56,7 @@ class UdpBroadcaster:
55
56
  def broadcast_devices(self):
56
57
 
57
58
  while self.running:
59
+ ips = get_ips()
58
60
  with self.lock:
59
61
  for device_id in self.devices_to_broadcast:
60
62
  message = UDPMessage.search(device_id)
@@ -1 +1,2 @@
1
- from .base import TcpMessageParser
1
+ from .base import TcpMessageParser
2
+ from .rsc import RSCMessageParser
qlsdk/rsc/parser/base.py CHANGED
@@ -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
@@ -0,0 +1,130 @@
1
+ from multiprocessing import Lock
2
+ from qlsdk.rsc.interface import IDevice, IParser
3
+
4
+ from loguru import logger
5
+ from threading import Thread
6
+ from time import time_ns
7
+ from qlsdk.rsc.command import CommandFactory
8
+
9
+ class RSCMessageParser(IParser):
10
+ def __init__(self, device : IDevice):
11
+ # 待解析的数据来源于该设备
12
+ self.device = device
13
+ self.running = False
14
+
15
+ # 缓冲区-用于处理数据
16
+ self.buffer:bytearray = bytearray()
17
+ # 读写锁-用于临时缓冲区(避免读写冲突)
18
+ self._lock = Lock()
19
+
20
+ @property
21
+ def header(self):
22
+ return b'\x5A\xA5'
23
+
24
+ @property
25
+ def header_len(self):
26
+ return 14
27
+
28
+ @property
29
+ def cmd_pos(self):
30
+ return 12
31
+
32
+ def set_device(self, device):
33
+ self.device = device
34
+
35
+ def append(self, value):
36
+ print(f"append value len: {len(value)}")
37
+ def __parser__(self):
38
+ logger.info("数据解析开始")
39
+
40
+ try:
41
+ while self.running:
42
+
43
+ # 查找消息头
44
+ while len(self.buffer) < len(self.header):
45
+ read_data = self.device.read_msg(1)
46
+ if read_data is None:
47
+ if self.running:
48
+ continue
49
+ else:
50
+ break
51
+ if read_data[0] == self.header[len(self.buffer)]:
52
+ self.buffer.extend(read_data)
53
+ else:
54
+ self.buffer.clear()
55
+ break
56
+
57
+ if len(self.buffer) < len(self.header):
58
+ continue
59
+
60
+
61
+ # 判断消息头
62
+ while len(self.buffer) < self.header_len:
63
+ read_data = self.device.read_msg(self.header_len - len(self.buffer))
64
+ if read_data is None:
65
+ if self.running:
66
+ continue
67
+ else:
68
+ break
69
+ self.buffer.extend(read_data)
70
+
71
+ # # 移动下标(指向包长度的位置)
72
+ # self.buffer.seek(self.start_pos + 8)
73
+ # 包总长度
74
+ pkg_len = int.from_bytes(self.buffer[8:12], 'little')
75
+ # 256*32K = 8388608
76
+ # 太长2M
77
+ if pkg_len > 2048000:
78
+ print(f"error message pkg_len={pkg_len} > 1000000")
79
+ self.buffer.clear()
80
+ continue
81
+ # 太短
82
+ if pkg_len < self.header_len:
83
+ print(f"error message pkg_len={pkg_len} < {self.header_len}")
84
+ self.buffer.clear()
85
+ continue
86
+
87
+ # 读取消息体
88
+ while len(self.buffer) < pkg_len:
89
+ read_data = self.device.read_msg(pkg_len - len(self.buffer))
90
+ if read_data is None:
91
+ if self.running:
92
+ continue
93
+ else:
94
+ break
95
+ self.buffer.extend(read_data)
96
+
97
+ if len(self.buffer) < pkg_len:
98
+ continue
99
+
100
+ # msg_crc = self.buffer[-2:]
101
+ # cal_crc = (bytes_buffer, len(bytes_buffer) - 2)
102
+ # if cal_crc != msg_crc:
103
+ # print(f'crc fail {cal_crc} {msg_crc}')
104
+ # if cal_crc != msg_crc:
105
+ # print(f'crc fail {cal_crc} {msg_crc}')
106
+
107
+ self.unpack(self.buffer)
108
+ self.buffer.clear()
109
+
110
+ except Exception as e:
111
+ logger.error(f"数据解析异常: {e}")
112
+
113
+ logger.info(f"数据解析结束:{self.running}")
114
+
115
+ def unpack(self, packet):
116
+ # 提取指令码
117
+ cmd_code = int.from_bytes(packet[self.cmd_pos : self.cmd_pos + 2], 'little')
118
+ cmd_class = CommandFactory.create_command(cmd_code)
119
+ instance = cmd_class(self.device)
120
+ instance.parse_body(packet[self.header_len:-2])
121
+ return instance
122
+
123
+ def start(self):
124
+ self.running = True
125
+ parser = Thread(target=self.__parser__, daemon=True)
126
+ parser.start()
127
+
128
+ def stop(self):
129
+ self.running = False
130
+
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: qlsdk2
3
- Version: 0.5.1.1
3
+ Version: 0.6.0a1
4
4
  Summary: SDK for quanlan device
5
5
  Home-page: https://github.com/hehuajun/qlsdk
6
6
  Author: hehuajun
@@ -10,12 +10,21 @@ Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: Microsoft :: Windows :: Windows 10
11
11
  Requires-Python: >=3.9
12
12
  Description-Content-Type: text/markdown
13
- Requires-Dist: loguru >=0.6.0
14
- Requires-Dist: numpy >=1.23.5
15
- Requires-Dist: bitarray >=1.5.3
13
+ Requires-Dist: loguru>=0.6.0
14
+ Requires-Dist: numpy>=1.23.5
15
+ Requires-Dist: bitarray>=1.5.3
16
16
  Provides-Extra: dev
17
- Requires-Dist: pytest >=6.0 ; extra == 'dev'
18
- Requires-Dist: twine >=3.0 ; extra == 'dev'
17
+ Requires-Dist: pytest>=6.0; extra == "dev"
18
+ Requires-Dist: twine>=3.0; extra == "dev"
19
+ Dynamic: author
20
+ Dynamic: author-email
21
+ Dynamic: classifier
22
+ Dynamic: description
23
+ Dynamic: description-content-type
24
+ Dynamic: home-page
25
+ Dynamic: requires-dist
26
+ Dynamic: requires-python
27
+ Dynamic: summary
19
28
 
20
29
  ## **v0.5.1.1** (2025-08-24)
21
30
  #### 🐛 Bug Fixed
@@ -8,19 +8,29 @@ qlsdk/core/local.py,sha256=vbison4XZtS4SNYLJ9CqBhetEcukdviTWmvtdA1efkQ,811
8
8
  qlsdk/core/utils.py,sha256=yfCiLpufO96I68MLs6Drc6IECRjcQ-If8sXn7RaRHrk,4241
9
9
  qlsdk/core/crc/__init__.py,sha256=kaYSr6KN5g4U49xlxAvT2lnEeGtwX4Dz1ArwKDvUIIY,143
10
10
  qlsdk/core/crc/crctools.py,sha256=sDeE6CMccQX2cRAyMQK0SZUk1fa50XMuwqXau5UX5C8,4242
11
- qlsdk/core/entity/__init__.py,sha256=1BBbL41zqb0_UsxgoiuyG5zM0CKVzT5VUA_BXSKZmAs,3177
11
+ qlsdk/core/entity/__init__.py,sha256=LOS5k_cIA-NYnvE94KUXCBUU_uu1v8FcXf7L399j8Yg,6610
12
12
  qlsdk/core/filter/__init__.py,sha256=YIWIzDUKN30mq2JTr53ZGblggZfC_rLUp2FSRrsQFgU,36
13
13
  qlsdk/core/filter/norch.py,sha256=5RdIBX5eqs5w5nmVAnCB3ESSuAT_vVBZ2g-dg6HMZdY,1858
14
14
  qlsdk/core/message/__init__.py,sha256=sHuavOyHf4bhH6VdDpTA1EsCh7Q-XsPHcFiItpVz3Rs,51
15
15
  qlsdk/core/message/command.py,sha256=94AyM6qmgVHQ8qzNLczdL8iY6N-2uEP5oBU6kD6pOG8,12102
16
16
  qlsdk/core/message/tcp.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- qlsdk/core/message/udp.py,sha256=KS_pKZcCEFhNGABCOZTOHiVFkfPNZl_0K5RXppwMIvA,3085
17
+ qlsdk/core/message/udp.py,sha256=e_w35NSlgmhptzlMNGee7rE7KVgeLDO2WhE6FEnuy-0,3387
18
18
  qlsdk/core/network/__init__.py,sha256=9Ww0cGgBEU_TVrwmgiDz20zA2fMFgepI_kOF4ggHcS0,1111
19
19
  qlsdk/core/network/monitor.py,sha256=QqjjPwSr1kgqDTTySp5bpalZmsBQTaAWSxrfPLdROZo,1810
20
+ qlsdk/entity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ qlsdk/entity/message.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ qlsdk/entity/signal.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ qlsdk/interface/__init__.py,sha256=zvXbyjbMWlPBt7AhD7fF4JwTrDqwkdyH8rH2xkmslGE,123
24
+ qlsdk/interface/analyzer.py,sha256=-muX0wWUn2e-PvUJicSstCGXHJZw2yP7Er1soJsWybw,26
25
+ qlsdk/interface/collector.py,sha256=ye9dVjRVYfCjeJsGV8Jc4ppwp58qKW5_u5e8IIcvL4U,190
26
+ qlsdk/interface/device.py,sha256=09Z2KQ1dj6gP-7CdPXfMvT8YhyadgqIfo4rf6PAyXog,24
27
+ qlsdk/interface/parser.py,sha256=00Ds01Xp9eUPwx7okL4B1Bl0_fRbgaxfm_2GOeWoZiM,248
28
+ qlsdk/interface/stimulator.py,sha256=MJbgiI-4qTMjbdhcBAZe8zb10hV9x8yhFCPL3Iq_QyI,28
29
+ qlsdk/interface/store.py,sha256=694WQnnrtXThP44JkQlBXzghvqA0PwHKK3uA640aiUo,23
20
30
  qlsdk/persist/__init__.py,sha256=b8qk1aOU6snEMCQNYDl1ijV3-2gwBmMt76fiAzNk1E8,107
21
31
  qlsdk/persist/ars_edf.py,sha256=_pYtHqucB-utMw-xUXZc9IB8_8ThbLFpTl_-WBQR-Sc,10555
22
32
  qlsdk/persist/edf.py,sha256=ETngb86CfkIUJYWmw86QR445MvTFC7Edk_CH9nyNgtY,7857
23
- qlsdk/persist/rsc_edf.py,sha256=WRuVZ8bvji8ztByukp0tkQiaM_Na4ezgfwsJU3PEdS8,13315
33
+ qlsdk/persist/rsc_edf.py,sha256=-L8Qv_d39rog6vrIEBnO5yYnxCr6yfL4qUlM72AaFJg,13392
24
34
  qlsdk/persist/stream.py,sha256=TCVF1sqDrHiYBsJC27At66AaCs-_blXeXA_WXdJiIVA,5828
25
35
  qlsdk/rsc/__init__.py,sha256=hOMiN0eYn4jYo7O4_0IPlQT0hD15SqqCQUihOVlTZvs,269
26
36
  qlsdk/rsc/device_manager.py,sha256=1ucd-lzHkNeQPKPzXV6OBkAMqPp_vOcsLyS-9TJ7wRc,4448
@@ -29,29 +39,30 @@ qlsdk/rsc/eegion.py,sha256=lxrktO-3Z_MYdFIwc4NxvgLM5AL5kU3UItjH6tsKmHY,11670
29
39
  qlsdk/rsc/entity.py,sha256=-fRWFkVWp9d8Y1uh6GiacXC5scdeEKNiNFf3aziGdCE,17751
30
40
  qlsdk/rsc/paradigm.py,sha256=DGfwY36sMdPIMRjbGo661GvUTEwsRRi3jrmG405mSTk,12840
31
41
  qlsdk/rsc/proxy.py,sha256=9CPdGNGWremwBUh4GvlXAykYB-x_BEPPLqsNvwuwIDE,2736
32
- qlsdk/rsc/command/__init__.py,sha256=2rqWM23fFqvFfgN7K3PLT7rkTyW9mawK4OGCo63hnJg,12292
42
+ qlsdk/rsc/command/__init__.py,sha256=DiuXvKQepVTc5hkHSB_YAAZPxobN2a7tYXpI7kZ3GQM,12296
33
43
  qlsdk/rsc/command/message.py,sha256=nTdG-Vp4MBnltyrgedAWiKD6kzOaPrg58Z_hq6yjhys,12220
34
- qlsdk/rsc/device/__init__.py,sha256=xtTXLT9QFKtb-qS-A8-ewSxJ3zXgImFCX0OoAPw6hHE,185
44
+ qlsdk/rsc/device/__init__.py,sha256=BzY9lRfssGPUlJ1ys-v3CWNgGihg7mPa2T4X0tl0Vg4,214
35
45
  qlsdk/rsc/device/arskindling.py,sha256=owci6MEGjyWqohEXzPdKj_ESeVIZKgO53StVj6Tmi18,15002
36
- qlsdk/rsc/device/base.py,sha256=osLToxNrb745OzCpgXZU5AH72emMh4OxATeqy4fCxsg,17856
46
+ qlsdk/rsc/device/base.py,sha256=oQ4Ev0LkDiJZXTqqTd6zqzzsn4ijYXkTLDUJKF0mn5U,19157
37
47
  qlsdk/rsc/device/c16_rs.py,sha256=BHQRHOnsTMAKgqSXaAS2RjPIklZQAl2CVfe6i_iX-i4,5928
38
- qlsdk/rsc/device/c256_rs.py,sha256=K1XmLqZpvHTAfCm_dr2VsGxHc67aJQVDV1cI41a1WTI,13955
48
+ qlsdk/rsc/device/c256_rs.py,sha256=CVnxxegyTNHQAfFVda6lQITQGGB-A958TEaqz7RjLq8,13883
39
49
  qlsdk/rsc/device/c64_rs.py,sha256=cZIioIRGgd4Ub0ieho4_XujBNo8AQgJEjXcqgcEkyFQ,13644
40
50
  qlsdk/rsc/device/c64s1.py,sha256=L7nKmsoMCGj6GMjHYfYkKgkBtrGfP516kQHQ5I1FAUE,13986
41
- qlsdk/rsc/device/device_factory.py,sha256=AL_dtjx6ThcyWTHxGSrLKjEDaCt1Y9gClK4HQ5FGjFI,1315
51
+ qlsdk/rsc/device/device_factory.py,sha256=6cPhm3pPGrVXA1s1HePFLjZqmhNI1vOAucFI0VRD_Y0,1317
42
52
  qlsdk/rsc/interface/__init__.py,sha256=xeRzIlQSB7ZSf4r5kLfH5cDQLzCyWeJAReG8Xq5nOE0,70
43
53
  qlsdk/rsc/interface/command.py,sha256=1s5Lxb_ejsd-JNvKMqU2aFSnOoW-_cx01VSD3czxmQI,199
44
- qlsdk/rsc/interface/device.py,sha256=VEV-Ige8tjvASdddP6SPRolTqPuIuHrIZP8wiX-Fhu8,3391
54
+ qlsdk/rsc/interface/device.py,sha256=__ap7aJFaaktzMaZ253MMeZy9vgvJmX0Hha1dISGkOk,2064
45
55
  qlsdk/rsc/interface/handler.py,sha256=ADDe_a2RAxGMuooLyivH0JBPTGBcFP2JaTVX41R1A4w,198
46
- qlsdk/rsc/interface/parser.py,sha256=DxuFZiprJJbG4pfFbbZPaG8MlBiBRe0S0lJrvc2Iees,251
56
+ qlsdk/rsc/interface/parser.py,sha256=Z4PND5LXcJ_8CQ-OIq3KlOEVOceU1hKUuZkoFSIGGLM,334
47
57
  qlsdk/rsc/manager/__init__.py,sha256=4ljT3mR8YPBDQur46B5xPqK5tjLKlsWfgCJVuA0gs-8,40
48
- qlsdk/rsc/manager/container.py,sha256=mowoFJNVDSEhqsz-EDzPVDcMRiuu_oakdGLZbJrPvlM,5071
58
+ qlsdk/rsc/manager/container.py,sha256=S6E0_ZXejC_YM_kzMQQHr7daPJPbVwZINBbYhreFW5w,5389
49
59
  qlsdk/rsc/manager/search.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
60
  qlsdk/rsc/network/__init__.py,sha256=PfYiqXS2pZV__uegQ1TjaeYhY1pefZ_shwE_X5HNVbs,23
51
- qlsdk/rsc/network/discover.py,sha256=4aojzRFInTC3d8K2TYGbnP1Ji5fOFEi31ekghj7ce5k,2977
52
- qlsdk/rsc/parser/__init__.py,sha256=8RgwbKCINu3eTsxVLF9cMoBXJnVrDocOEFP6NGP_atk,34
61
+ qlsdk/rsc/network/discover.py,sha256=GRXP0WxxIorDZWXq1X5CPAV60raSRvNiVwMQE8647XA,3044
62
+ qlsdk/rsc/parser/__init__.py,sha256=cVKk06bRYOnwE3XMoksGlatKJSaZE1GVrYypQm-9aro,69
53
63
  qlsdk/rsc/parser/base-new.py,sha256=cAOy1V_1fAJyGq7bm7uLxpW41DbkllWOprnfWKpjtsQ,5116
54
- qlsdk/rsc/parser/base.py,sha256=Cqel02BA_AH-deSmzTyUvqecIGoYVBre5UuFlG1eNGA,5728
64
+ qlsdk/rsc/parser/base.py,sha256=VoVYoy2B583EohRRLPX3GcRiuQxT7RG7-y24WgICP2k,5702
65
+ qlsdk/rsc/parser/rsc.py,sha256=RuBqsg5KZNio7mTyM14svQEeU0_0CLhnRmfbP0NBM2o,4724
55
66
  qlsdk/sdk/__init__.py,sha256=v9LKP-5qXCqnAsCkiRE9LDb5Tagvl_Qd_fqrw7y9yd4,68
56
67
  qlsdk/sdk/ar4sdk.py,sha256=tugH3UUeNebdka78AzLyrtAXbYQQE3iFJ227zUit6tY,27261
57
68
  qlsdk/sdk/hub.py,sha256=uEOGZBZtMDCWlV8G2TZe6FAo6eTPcwHAW8zdqr1eq_0,1571
@@ -59,7 +70,7 @@ qlsdk/sdk/libs/libAr4SDK.dll,sha256=kZp9_DRwPdAJ5OgTFQSqS8tEETxUs7YmmETuBP2g60U,
59
70
  qlsdk/sdk/libs/libwinpthread-1.dll,sha256=W77ySaDQDi0yxpnQu-ifcU6-uHKzmQpcvsyx2J9j5eg,52224
60
71
  qlsdk/x8/__init__.py,sha256=FDpDK7GAYL-g3vzfU9U_V03QzoYoxH9YLm93PjMlANg,4870
61
72
  qlsdk/x8m/__init__.py,sha256=cLeUqEEj65qXw4Qa4REyxoLh6T24anSqPaKe9_lR340,634
62
- qlsdk2-0.5.1.1.dist-info/METADATA,sha256=jeill3HEYJtHhFF2MVR_AIfm8LJAJcIdYOfbVnP3Xjk,1820
63
- qlsdk2-0.5.1.1.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
64
- qlsdk2-0.5.1.1.dist-info/top_level.txt,sha256=2CHzn0SY-NIBVyBl07Suh-Eo8oBAQfyNPtqQ_aDatBg,6
65
- qlsdk2-0.5.1.1.dist-info/RECORD,,
73
+ qlsdk2-0.6.0a1.dist-info/METADATA,sha256=noX9rMQ0g9zxwyRCgDOQct_z1PBF-S5hEauk3d4x9Sk,2019
74
+ qlsdk2-0.6.0a1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
75
+ qlsdk2-0.6.0a1.dist-info/top_level.txt,sha256=2CHzn0SY-NIBVyBl07Suh-Eo8oBAQfyNPtqQ_aDatBg,6
76
+ qlsdk2-0.6.0a1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.3.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5