qlsdk2 0.4.0a2__py3-none-any.whl → 0.4.1__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.
- qlsdk/ar4/__init__.py +9 -5
- qlsdk/core/__init__.py +2 -1
- qlsdk/core/device.py +10 -1
- qlsdk/core/entity/__init__.py +1 -1
- qlsdk/core/message/command.py +93 -53
- qlsdk/core/network/__init__.py +34 -0
- qlsdk/core/network/monitor.py +55 -0
- qlsdk/core/utils.py +2 -0
- qlsdk/persist/edf.py +21 -2
- qlsdk/persist/rsc_edf.py +190 -126
- qlsdk/rsc/__init__.py +4 -2
- qlsdk/rsc/command/__init__.py +183 -61
- qlsdk/rsc/command/message.py +171 -74
- qlsdk/rsc/device/__init__.py +2 -0
- qlsdk/rsc/device/base.py +388 -0
- qlsdk/rsc/device/c64_rs.py +364 -0
- qlsdk/rsc/device/device_factory.py +29 -0
- qlsdk/rsc/device_manager.py +2 -0
- qlsdk/rsc/entity.py +95 -179
- qlsdk/rsc/interface/__init__.py +3 -0
- qlsdk/rsc/interface/command.py +10 -0
- qlsdk/rsc/interface/device.py +107 -0
- qlsdk/rsc/interface/handler.py +9 -0
- qlsdk/rsc/interface/parser.py +9 -0
- qlsdk/rsc/manager/__init__.py +2 -0
- qlsdk/rsc/manager/container.py +121 -0
- qlsdk/rsc/manager/search.py +0 -0
- qlsdk/rsc/network/__init__.py +1 -0
- qlsdk/rsc/network/discover.py +87 -0
- qlsdk/rsc/paradigm.py +4 -3
- qlsdk/rsc/parser/__init__.py +1 -0
- qlsdk/rsc/parser/base.py +66 -0
- qlsdk/sdk/ar4sdk.py +13 -4
- qlsdk/x8/__init__.py +4 -0
- {qlsdk2-0.4.0a2.dist-info → qlsdk2-0.4.1.dist-info}/METADATA +2 -2
- qlsdk2-0.4.1.dist-info/RECORD +58 -0
- qlsdk2-0.4.0a2.dist-info/RECORD +0 -40
- {qlsdk2-0.4.0a2.dist-info → qlsdk2-0.4.1.dist-info}/WHEEL +0 -0
- {qlsdk2-0.4.0a2.dist-info → qlsdk2-0.4.1.dist-info}/top_level.txt +0 -0
qlsdk/ar4/__init__.py
CHANGED
|
@@ -39,12 +39,12 @@ class AR4(LMDevice):
|
|
|
39
39
|
consumer.put(packet)
|
|
40
40
|
|
|
41
41
|
if len(self._phy_subscriber) > 0:
|
|
42
|
-
logger.
|
|
43
|
-
logger.
|
|
42
|
+
logger.debug(f"dig data eeg: {packet.eeg}")
|
|
43
|
+
logger.debug(f"dig data acc: {packet.acc}")
|
|
44
44
|
packet.eeg = self.eeg2phy(np.array(packet.eeg))
|
|
45
45
|
packet.acc = self.acc2phy(np.array(packet.acc))
|
|
46
|
-
logger.
|
|
47
|
-
logger.
|
|
46
|
+
logger.debug(f"phy data eeg: {packet.eeg}")
|
|
47
|
+
logger.debug(f"phy data acc: {packet.acc}")
|
|
48
48
|
for consumer2 in self._phy_subscriber.values():
|
|
49
49
|
consumer2.put(packet)
|
|
50
50
|
|
|
@@ -74,7 +74,11 @@ class AR4(LMDevice):
|
|
|
74
74
|
self._edf_handler = None
|
|
75
75
|
self._recording = False
|
|
76
76
|
logger.info(f"停止记录数据: {self.box_mac}")
|
|
77
|
-
|
|
77
|
+
|
|
78
|
+
def trigger(self, desc):
|
|
79
|
+
if self._edf_handler:
|
|
80
|
+
self._edf_handler.trigger(desc)
|
|
81
|
+
|
|
78
82
|
# 订阅推送消息
|
|
79
83
|
def subscribe(self, topic: str = None, q: Queue = Queue(), value_type: Literal['phy', 'dig'] = 'phy') -> tuple[str, Queue]:
|
|
80
84
|
if topic is None:
|
qlsdk/core/__init__.py
CHANGED
qlsdk/core/device.py
CHANGED
|
@@ -4,9 +4,18 @@ class BaseDevice(object):
|
|
|
4
4
|
def __init__(self, socket = None):
|
|
5
5
|
self.socket = socket
|
|
6
6
|
self.device_name = None
|
|
7
|
-
self.
|
|
7
|
+
self._device_type = None
|
|
8
8
|
self.device_id = None
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def device_type(self) -> int:
|
|
13
|
+
return self._device_type
|
|
14
|
+
|
|
15
|
+
@device_type.setter
|
|
16
|
+
def device_type(self, value: int):
|
|
17
|
+
self._device_type = value
|
|
18
|
+
|
|
10
19
|
@property
|
|
11
20
|
def acq_channels(self) :
|
|
12
21
|
return None
|
qlsdk/core/entity/__init__.py
CHANGED
qlsdk/core/message/command.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import numpy as np
|
|
3
|
+
from time import time_ns
|
|
2
4
|
from typing import Dict, Type
|
|
3
|
-
from enum import Enum
|
|
4
5
|
from loguru import logger
|
|
6
|
+
|
|
7
|
+
|
|
5
8
|
from qlsdk.core.crc import crc16
|
|
6
9
|
from qlsdk.core.device import BaseDevice
|
|
7
10
|
from qlsdk.core.entity import RscPacket, ImpedancePacket
|
|
@@ -26,6 +29,10 @@ class DeviceCommand(abc.ABC):
|
|
|
26
29
|
@abc.abstractmethod
|
|
27
30
|
def cmd_code(self) -> int:
|
|
28
31
|
pass
|
|
32
|
+
@property
|
|
33
|
+
@abc.abstractmethod
|
|
34
|
+
def cmd_desc(self) -> str:
|
|
35
|
+
pass
|
|
29
36
|
|
|
30
37
|
@staticmethod
|
|
31
38
|
def checksum(data: bytes) -> bytes:
|
|
@@ -59,6 +66,13 @@ class DeviceCommand(abc.ABC):
|
|
|
59
66
|
# 解析消息体
|
|
60
67
|
body = payload[self.HEADER_LEN:-2]
|
|
61
68
|
|
|
69
|
+
def parse_body(self, body: bytes):
|
|
70
|
+
time = int.from_bytes(body[0:8], 'little')
|
|
71
|
+
# result - 1B
|
|
72
|
+
result = body[8]
|
|
73
|
+
logger.info(f"[{time}]{self.cmd_desc}{'成功' if result == 0 else '失败'}")
|
|
74
|
+
|
|
75
|
+
|
|
62
76
|
|
|
63
77
|
class CommandFactory:
|
|
64
78
|
"""Registry for command implementations"""
|
|
@@ -70,15 +84,16 @@ class CommandFactory:
|
|
|
70
84
|
|
|
71
85
|
@classmethod
|
|
72
86
|
def create_command(cls, code: int) -> Type[DeviceCommand]:
|
|
73
|
-
logger.
|
|
87
|
+
logger.trace(f"Creating command for code: {hex(code)}")
|
|
74
88
|
if code not in cls._commands:
|
|
75
|
-
logger.warning(f"
|
|
89
|
+
logger.warning(f"不支持的设备指令: {hex(code)}")
|
|
76
90
|
return cls._commands[DefaultCommand.cmd_code]
|
|
77
91
|
return cls._commands[code]
|
|
78
92
|
|
|
79
93
|
# =============================================================================
|
|
80
94
|
class DefaultCommand(DeviceCommand):
|
|
81
95
|
cmd_code = 0x00
|
|
96
|
+
cmd_desc = "未定义"
|
|
82
97
|
|
|
83
98
|
def parse_body(self, body: bytes):
|
|
84
99
|
# Response parsing example: 2 bytes version + 4 bytes serial
|
|
@@ -86,9 +101,9 @@ class DefaultCommand(DeviceCommand):
|
|
|
86
101
|
|
|
87
102
|
class GetDeviceInfoCommand(DeviceCommand):
|
|
88
103
|
cmd_code = 0x17
|
|
104
|
+
cmd_desc = "设备信息"
|
|
89
105
|
|
|
90
106
|
def parse_body(self, body: bytes):
|
|
91
|
-
logger.info(f"Received GetDeviceInfoCommand body len: {len(body)}")
|
|
92
107
|
# time - 8B
|
|
93
108
|
self.device.connect_time = int.from_bytes(body[0:8], 'little')
|
|
94
109
|
self.device.current_time = self.device.connect_time
|
|
@@ -108,10 +123,13 @@ class GetDeviceInfoCommand(DeviceCommand):
|
|
|
108
123
|
flag = int.from_bytes(body[41:45], 'little')
|
|
109
124
|
logger.debug(f"Received device info: {result}, {flag}, {self.device}")
|
|
110
125
|
|
|
126
|
+
# 创建设备对象
|
|
127
|
+
|
|
111
128
|
|
|
112
129
|
# 握手
|
|
113
130
|
class HandshakeCommand(DeviceCommand):
|
|
114
131
|
cmd_code = 0x01
|
|
132
|
+
cmd_desc = "握手"
|
|
115
133
|
|
|
116
134
|
def parse_body(self, body: bytes):
|
|
117
135
|
logger.info(f"Received handshake response: {body.hex()}")
|
|
@@ -119,8 +137,8 @@ class HandshakeCommand(DeviceCommand):
|
|
|
119
137
|
# 查询电量
|
|
120
138
|
class QueryBatteryCommand(DeviceCommand):
|
|
121
139
|
cmd_code = 0x16
|
|
140
|
+
cmd_desc = "电量信息"
|
|
122
141
|
def parse_body(self, body: bytes):
|
|
123
|
-
logger.info(f"Received QueryBatteryCommand body len: {len(body)}")
|
|
124
142
|
# time - 8b
|
|
125
143
|
self.device.current_time = int.from_bytes(body[0:8], 'little')
|
|
126
144
|
# result - 1b
|
|
@@ -135,12 +153,15 @@ class QueryBatteryCommand(DeviceCommand):
|
|
|
135
153
|
self.device.battery_total = body[12]
|
|
136
154
|
# state - 1b
|
|
137
155
|
# state = body[13]
|
|
156
|
+
logger.debug(f"电量更新: {self.device}")
|
|
138
157
|
else:
|
|
139
158
|
logger.warning(f"QueryBatteryCommand message received but result is failed.")
|
|
140
159
|
|
|
141
160
|
# 设置采集参数
|
|
142
161
|
class SetAcquisitionParamCommand(DeviceCommand):
|
|
143
162
|
cmd_code = 0x451
|
|
163
|
+
cmd_desc = "设置信号采集参数"
|
|
164
|
+
|
|
144
165
|
def pack_body(self):
|
|
145
166
|
body = to_bytes(self.device.acq_channels)
|
|
146
167
|
body += self.device.sample_range.to_bytes(4, byteorder='little')
|
|
@@ -150,76 +171,56 @@ class SetAcquisitionParamCommand(DeviceCommand):
|
|
|
150
171
|
body += bytes.fromhex('00')
|
|
151
172
|
|
|
152
173
|
return body
|
|
153
|
-
def parse_body(self, body: bytes):
|
|
154
|
-
logger.info(f"Received SetAcquisitionParam response: {body.hex()}")
|
|
155
174
|
|
|
156
175
|
# 启动采集
|
|
157
176
|
class StartAcquisitionCommand(DeviceCommand):
|
|
158
177
|
cmd_code = 0x452
|
|
178
|
+
cmd_desc = "启动信号采集"
|
|
159
179
|
|
|
160
180
|
def pack_body(self):
|
|
161
181
|
return bytes.fromhex('0000')
|
|
162
|
-
def parse_body(self, body: bytes):
|
|
163
|
-
logger.info(f"Received acquisition start response: {body.hex()}")
|
|
164
182
|
|
|
165
183
|
# 停止采集
|
|
166
184
|
class StopAcquisitionCommand(DeviceCommand):
|
|
167
185
|
cmd_code = 0x453
|
|
186
|
+
cmd_desc = "停止信号采集"
|
|
168
187
|
|
|
169
188
|
def pack_body(self):
|
|
170
189
|
return b''
|
|
171
|
-
|
|
172
|
-
|
|
190
|
+
|
|
191
|
+
|
|
173
192
|
# 设置阻抗采集参数
|
|
174
193
|
class SetImpedanceParamCommand(DeviceCommand):
|
|
175
194
|
cmd_code = 0x411
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
#
|
|
195
|
+
cmd_desc = "设置阻抗测量参数"
|
|
196
|
+
|
|
197
|
+
# 启动阻抗测量
|
|
179
198
|
class StartImpedanceCommand(DeviceCommand):
|
|
180
199
|
cmd_code = 0x412
|
|
200
|
+
cmd_desc = "启动阻抗测量"
|
|
181
201
|
def pack_body(self):
|
|
182
202
|
body = bytes.fromhex('0000')
|
|
183
203
|
body += to_bytes(self.device.acq_channels)
|
|
184
204
|
body += bytes.fromhex('0000000000000000') # 8字节占位符
|
|
185
205
|
return body
|
|
186
206
|
|
|
187
|
-
def parse_body(self, body: bytes):
|
|
188
|
-
logger.info(f"Received StartImpedanceCommand response: {body.hex()}")
|
|
189
207
|
|
|
190
|
-
#
|
|
208
|
+
# 停止阻抗测量
|
|
191
209
|
class StopImpedanceCommand(DeviceCommand):
|
|
192
210
|
cmd_code = 0x413
|
|
211
|
+
cmd_desc = "停止阻抗测量"
|
|
193
212
|
|
|
194
213
|
def pack_body(self):
|
|
195
214
|
return b''
|
|
196
|
-
|
|
197
|
-
def parse_body(self, body: bytes):
|
|
198
|
-
logger.info(f"Received StopImpedanceCommand response: {body.hex()}")
|
|
199
|
-
|
|
200
|
-
# 设置采集参数
|
|
201
|
-
class SetStimulationParamCommand(DeviceCommand):
|
|
202
|
-
cmd_code = 0x451
|
|
203
|
-
def pack_body(self):
|
|
204
|
-
body = to_bytes(self.device.acq_channels)
|
|
205
|
-
body += self.device.sample_range.to_bytes(4, byteorder='little')
|
|
206
|
-
body += self.device.sample_rate.to_bytes(4, byteorder='little')
|
|
207
|
-
body += self.device.sample_num.to_bytes(4, byteorder='little')
|
|
208
|
-
body += self.device.resolution.to_bytes(1, byteorder='little')
|
|
209
|
-
body += bytes.fromhex('00')
|
|
210
|
-
|
|
211
|
-
return body
|
|
212
|
-
def parse_body(self, body: bytes):
|
|
213
|
-
logger.info(f"Received SetAcquisitionParam response: {body.hex()}")
|
|
214
215
|
|
|
215
|
-
#
|
|
216
|
+
# 启动刺激
|
|
216
217
|
class StartStimulationCommand(DeviceCommand):
|
|
217
218
|
cmd_code = 0x48C
|
|
219
|
+
cmd_desc = "启动刺激"
|
|
218
220
|
def pack_body(self):
|
|
219
221
|
return self.device.stim_paradigm.to_bytes()
|
|
220
222
|
# return bytes.fromhex('01000000000000008813000000000000010000000000000000000140420f00640064000000803f0000010000000000000000000000000000000000000000008813000000000000')
|
|
221
223
|
def parse_body(self, body: bytes):
|
|
222
|
-
logger.info(f"Received stimulation start response: {body.hex()}")
|
|
223
224
|
# time - 8B
|
|
224
225
|
time = int.from_bytes(body[0:8], 'little')
|
|
225
226
|
# result - 1B
|
|
@@ -227,28 +228,58 @@ class StartStimulationCommand(DeviceCommand):
|
|
|
227
228
|
# error_channel - 8B
|
|
228
229
|
# error_channel= int.from_bytes(body[9:17], 'big')
|
|
229
230
|
channels = to_channels(body[9:17])
|
|
230
|
-
logger.
|
|
231
|
+
logger.success(f"通道 {channels} 刺激开始")
|
|
232
|
+
self.device.trigger(f"通道 {channels} 刺激开始")
|
|
231
233
|
# error_type - 1B
|
|
232
234
|
error_type = body[17]
|
|
233
235
|
|
|
234
|
-
#
|
|
236
|
+
# 停止刺激
|
|
235
237
|
class StopStimulationCommand(DeviceCommand):
|
|
236
238
|
cmd_code = 0x488
|
|
237
|
-
|
|
239
|
+
cmd_desc = "停止刺激"
|
|
240
|
+
|
|
241
|
+
# 启动刺激
|
|
242
|
+
class StopStimulationNotifyCommand(DeviceCommand):
|
|
243
|
+
cmd_code = 0x48D
|
|
244
|
+
cmd_desc = "停止刺激通知"
|
|
245
|
+
def pack_body(self):
|
|
246
|
+
return self.device.stim_paradigm.to_bytes()
|
|
247
|
+
# return bytes.fromhex('01000000000000008813000000000000010000000000000000000140420f00640064000000803f0000010000000000000000000000000000000000000000008813000000000000')
|
|
238
248
|
def parse_body(self, body: bytes):
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
#
|
|
249
|
+
# time - 8B
|
|
250
|
+
time = int.from_bytes(body[0:8], 'little')
|
|
251
|
+
# result - 1B
|
|
252
|
+
result = body[8]
|
|
253
|
+
# error_channel - 8B
|
|
254
|
+
# error_channel= int.from_bytes(body[9:17], 'big')
|
|
255
|
+
channels = to_channels(body[9:17])
|
|
256
|
+
logger.success(f"通道 {channels} 刺激结束")
|
|
257
|
+
self.device.trigger(f"通道 {channels} 刺激结束", time)
|
|
258
|
+
# error_type - 1B
|
|
259
|
+
error_type = body[17]
|
|
260
|
+
# 刺激信息
|
|
242
261
|
class StimulationInfoCommand(DeviceCommand):
|
|
243
262
|
cmd_code = 0x48e
|
|
263
|
+
cmd_desc = "刺激告警信息"
|
|
244
264
|
|
|
245
265
|
def parse_body(self, body: bytes):
|
|
246
|
-
|
|
266
|
+
time = int.from_bytes(body[0:8], 'little')
|
|
267
|
+
# result - 1B
|
|
268
|
+
result = body[8]
|
|
269
|
+
# error_channel - 8B
|
|
270
|
+
channels = to_channels(body[9:17])
|
|
271
|
+
# 保留位-8B
|
|
272
|
+
# error_type - 1B
|
|
273
|
+
err_type = body[17]
|
|
274
|
+
# 特征位-4B
|
|
275
|
+
# errType = int.from_bytes(body[25:29], 'little')
|
|
276
|
+
logger.warning(f"刺激告警信息[{err_type}],通道 {channels} 刺激驱动不足")
|
|
247
277
|
|
|
248
278
|
|
|
249
279
|
# 阻抗数据
|
|
250
280
|
class ImpedanceDataCommand(DeviceCommand):
|
|
251
281
|
cmd_code = 0x415
|
|
282
|
+
cmd_desc = "阻抗数据"
|
|
252
283
|
|
|
253
284
|
def parse_body(self, body: bytes):
|
|
254
285
|
logger.info(f"Received impedance data: {body.hex()}")
|
|
@@ -257,24 +288,33 @@ class ImpedanceDataCommand(DeviceCommand):
|
|
|
257
288
|
# 信号数据
|
|
258
289
|
class SignalDataCommand(DeviceCommand):
|
|
259
290
|
cmd_code = 0x455
|
|
291
|
+
cmd_desc = "信号数据"
|
|
260
292
|
|
|
261
293
|
def unpack(self, payload):
|
|
262
294
|
return super().unpack(payload)
|
|
263
295
|
|
|
264
|
-
def parse_body(self, body: bytes):
|
|
265
|
-
#
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
296
|
+
def parse_body(self, body: bytes):
|
|
297
|
+
# 解析数据包
|
|
298
|
+
packet = RscPacket()
|
|
299
|
+
packet.transfer(body)
|
|
300
|
+
|
|
301
|
+
# 文件写入到edf
|
|
302
|
+
if self.device.edf_handler:
|
|
303
|
+
self.device.edf_handler.write(packet)
|
|
304
|
+
|
|
305
|
+
if len(self.device.signal_consumers) > 0 :
|
|
306
|
+
# 信号数字值转物理值
|
|
307
|
+
packet.eeg = self.device.eeg2phy(np.array(packet.eeg))
|
|
308
|
+
|
|
270
309
|
# 发送数据包到订阅者
|
|
271
310
|
for q in list(self.device.signal_consumers.values()):
|
|
272
|
-
q.put(
|
|
311
|
+
q.put(packet)
|
|
312
|
+
|
|
313
|
+
|
|
273
314
|
|
|
274
315
|
# =============================================================================
|
|
275
|
-
#
|
|
316
|
+
# 指令实现类注册到指令工厂
|
|
276
317
|
# =============================================================================
|
|
277
|
-
|
|
278
318
|
CommandFactory.register_command(DefaultCommand.cmd_code, DefaultCommand)
|
|
279
319
|
CommandFactory.register_command(GetDeviceInfoCommand.cmd_code, GetDeviceInfoCommand)
|
|
280
320
|
CommandFactory.register_command(HandshakeCommand.cmd_code, HandshakeCommand)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import psutil
|
|
2
|
+
import socket
|
|
3
|
+
|
|
4
|
+
def get_active_interfaces():
|
|
5
|
+
interfaces = {}
|
|
6
|
+
# 获取所有接口地址信息
|
|
7
|
+
all_addrs = psutil.net_if_addrs()
|
|
8
|
+
# 获取接口状态(是否处于UP状态)
|
|
9
|
+
stats = psutil.net_if_stats()
|
|
10
|
+
|
|
11
|
+
for name, addrs in all_addrs.items():
|
|
12
|
+
# 检查接口是否启用
|
|
13
|
+
if stats[name].isup:
|
|
14
|
+
ips = []
|
|
15
|
+
for addr in addrs:
|
|
16
|
+
# 提取IPv4和IPv6地址
|
|
17
|
+
if addr.family == socket.AF_INET:
|
|
18
|
+
ips.append(f"IPv4: {addr.address}")
|
|
19
|
+
elif addr.family == socket.AF_INET6:
|
|
20
|
+
ips.append(f"IPv6: {addr.address}")
|
|
21
|
+
# 过滤无IP的接口(可选)
|
|
22
|
+
if ips:
|
|
23
|
+
interfaces[name] = {
|
|
24
|
+
"status": "UP",
|
|
25
|
+
"IPs": ips
|
|
26
|
+
}
|
|
27
|
+
return interfaces
|
|
28
|
+
|
|
29
|
+
# 调用并打印结果
|
|
30
|
+
active_ifs = get_active_interfaces()
|
|
31
|
+
for iface, info in active_ifs.items():
|
|
32
|
+
print(f"接口: {iface}")
|
|
33
|
+
print(f"状态: {info['status']}")
|
|
34
|
+
print(f"IP地址: {info['IPs']}\n")
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from threading import Thread
|
|
2
|
+
import psutil
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
def get_active_ipv4():
|
|
6
|
+
ips = []
|
|
7
|
+
# 获取所有接口地址信息
|
|
8
|
+
all_addrs = psutil.net_if_addrs()
|
|
9
|
+
# 获取接口状态(是否处于UP状态)
|
|
10
|
+
stats = psutil.net_if_stats()
|
|
11
|
+
|
|
12
|
+
for name, addrs in all_addrs.items():
|
|
13
|
+
# 检查接口是否启用
|
|
14
|
+
if stats[name].isup:
|
|
15
|
+
for addr in addrs:
|
|
16
|
+
# 提取IPv4地址
|
|
17
|
+
if addr.family == socket.AF_INET:
|
|
18
|
+
ips.append(addr.address)
|
|
19
|
+
return ips
|
|
20
|
+
def monitor_up_interfaces(interval=2, callback=None):
|
|
21
|
+
prev_status = {iface: psutil.net_if_stats()[iface].isup
|
|
22
|
+
for iface in psutil.net_if_stats()}
|
|
23
|
+
|
|
24
|
+
while True:
|
|
25
|
+
current_stats = psutil.net_if_stats()
|
|
26
|
+
for iface, stats in current_stats.items():
|
|
27
|
+
current_up = stats.isup
|
|
28
|
+
# 检测状态变化
|
|
29
|
+
if current_up != prev_status.get(iface, None):
|
|
30
|
+
if current_up:
|
|
31
|
+
print(f"[UP] 接口 {iface} 激活")
|
|
32
|
+
if callback: callback(iface, "UP")
|
|
33
|
+
else:
|
|
34
|
+
print(f"[DOWN] 接口 {iface} 断开")
|
|
35
|
+
if callback: callback(iface, "DOWN")
|
|
36
|
+
prev_status[iface] = current_up
|
|
37
|
+
time.sleep(interval)
|
|
38
|
+
|
|
39
|
+
# 自定义回调函数示例
|
|
40
|
+
def notify(iface, status):
|
|
41
|
+
if status == "UP":
|
|
42
|
+
print(f"接口 {iface} 已激活")
|
|
43
|
+
|
|
44
|
+
# 启动监听
|
|
45
|
+
# monitor_up_interfaces(callback=notify)
|
|
46
|
+
|
|
47
|
+
monitor = Thread(target=monitor_up_interfaces, name="Network Status Monitor", args=(2, notify))
|
|
48
|
+
monitor.start()
|
|
49
|
+
|
|
50
|
+
import socket
|
|
51
|
+
def is_port_in_use(ip, port):
|
|
52
|
+
with socket.socket() as s:
|
|
53
|
+
return s.connect_ex((ip, port)) == 0
|
|
54
|
+
|
|
55
|
+
print(get_active_ipv4())
|
qlsdk/core/utils.py
CHANGED
qlsdk/persist/edf.py
CHANGED
|
@@ -34,6 +34,7 @@ class EdfHandler(object):
|
|
|
34
34
|
self._first_pkg_id = None
|
|
35
35
|
self._last_pkg_id = None
|
|
36
36
|
self._first_timestamp = None
|
|
37
|
+
self._first_time = None
|
|
37
38
|
self._end_time = None
|
|
38
39
|
self._patient_code = "patient_code"
|
|
39
40
|
self._patient_name = "patient_name"
|
|
@@ -77,6 +78,7 @@ class EdfHandler(object):
|
|
|
77
78
|
self.acc_channels = data.acc_ch_count
|
|
78
79
|
self._first_pkg_id = data.pkg_id
|
|
79
80
|
self._first_timestamp = data.time_stamp
|
|
81
|
+
self._first_time = datetime.now()
|
|
80
82
|
|
|
81
83
|
if self._last_pkg_id and self._last_pkg_id != data.pkg_id - 1:
|
|
82
84
|
self._lost_packets += data.pkg_id - self._last_pkg_id - 1
|
|
@@ -90,8 +92,25 @@ class EdfHandler(object):
|
|
|
90
92
|
if not self._recording:
|
|
91
93
|
self.start()
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
# trigger标记
|
|
96
|
+
# desc: 标记内容
|
|
97
|
+
# cur_time: 设备时间时间戳,非设备发出的trigger不要设置
|
|
98
|
+
def trigger(self, desc, cur_time=None):
|
|
99
|
+
if self._edf_writer is None:
|
|
100
|
+
logger.warning("EDF writer未初始化,无法写入trigger标记")
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
if cur_time is None:
|
|
104
|
+
onset = datetime.now().timestamp() - self._first_time.timestamp()
|
|
105
|
+
else:
|
|
106
|
+
onset = cur_time - self._first_timestamp
|
|
107
|
+
|
|
108
|
+
self._edf_writer.writeAnnotation(onset, 1, desc)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# def trigger(self, desc):
|
|
112
|
+
# if self._edf_writer:
|
|
113
|
+
# self._edf_writer.writeAnnotation(0, 1, desc)
|
|
95
114
|
|
|
96
115
|
def start(self):
|
|
97
116
|
self._recording = True
|