sensor-sdk 0.2.1__tar.gz → 0.2.2__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.
- {sensor_sdk-0.2.1/sensor_sdk.egg-info → sensor_sdk-0.2.2}/PKG-INFO +1 -1
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/bleak_process.py +26 -4
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/sensor_data_context.py +11 -1
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/sensor_profile.py +34 -1
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2/sensor_sdk.egg-info}/PKG-INFO +1 -1
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/setup.py +1 -1
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/LICENSE.txt +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/README.md +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/__init__.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/bleak_host.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/bleak_no_ack_patch.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/fb/DataType.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/fb/Sample.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/fb/SensorData.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/fb/__init__.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/gforce.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/sdk_log.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/sensor_controller.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/sensor_data.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/sensor_data_pool.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/sensor_device.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/sensor_utils.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor/winrt_high_throughput.py +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor_sdk.egg-info/SOURCES.txt +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor_sdk.egg-info/dependency_links.txt +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor_sdk.egg-info/requires.txt +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor_sdk.egg-info/top_level.txt +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/sensor_sdk.egg-info/zip-safe +0 -0
- {sensor_sdk-0.2.1 → sensor_sdk-0.2.2}/setup.cfg +0 -0
|
@@ -30,6 +30,16 @@ _MAX_RECONNECT_ATTEMPTS = 5
|
|
|
30
30
|
_RECONNECT_DELAY_SECONDS = 1.0
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def _is_core_bluetooth(client) -> bool:
|
|
34
|
+
"""判断 bleak 当前是否使用 macOS CoreBluetooth 后端。"""
|
|
35
|
+
if client is None:
|
|
36
|
+
return False
|
|
37
|
+
backend = getattr(client, "_backend", None)
|
|
38
|
+
if backend is None:
|
|
39
|
+
return False
|
|
40
|
+
return "corebluetooth" in type(backend).__module__.lower()
|
|
41
|
+
|
|
42
|
+
|
|
33
43
|
def _extract_mac(_device: bleak.BLEDevice, adv: AdvertisementData) -> str:
|
|
34
44
|
|
|
35
45
|
mac = None
|
|
@@ -98,6 +108,7 @@ class BleakProcess(multiprocessing.Process):
|
|
|
98
108
|
self._data_tasks: dict = {} # mac -> asyncio.Task
|
|
99
109
|
self._battery_tasks: dict = {} # mac -> asyncio.Task
|
|
100
110
|
self._reconnect_info: dict = {} # mac -> {cmd, attempts, task, normal_disconnect}
|
|
111
|
+
self._restore_info: dict = {} # mac -> {package_sample_count, power_refresh_interval, streaming}
|
|
101
112
|
self._main_event_loop = None # BleakProcess 主事件循环
|
|
102
113
|
|
|
103
114
|
# Per-device event loops and threads
|
|
@@ -1341,15 +1352,26 @@ class BleakProcess(multiprocessing.Process):
|
|
|
1341
1352
|
|
|
1342
1353
|
if needs_restart or oym_needs_subscribe:
|
|
1343
1354
|
try:
|
|
1344
|
-
|
|
1355
|
+
is_oym = ctx.getChipType() == BLEChipType.OYM
|
|
1356
|
+
# macOS CoreBluetooth 在流传输中 stop_notify 容易导致设备断开,
|
|
1357
|
+
# 因此 OYM 设备在 CoreBluetooth 后端上直接更新订阅掩码,不启停通知。
|
|
1358
|
+
oym_skip_stop_start = is_oym and _is_core_bluetooth(ctx.gForce.client)
|
|
1359
|
+
if oym_skip_stop_start:
|
|
1360
|
+
SdkLog.d(_TAG, f"OYM CoreBluetooth: skip stop/start, set subscription directly for {device_mac}")
|
|
1361
|
+
if was_streaming and not oym_skip_stop_start:
|
|
1345
1362
|
await ctx.stop_streaming()
|
|
1346
|
-
if oym_needs_subscribe and
|
|
1363
|
+
if oym_needs_subscribe and is_oym:
|
|
1347
1364
|
await ctx.gForce.set_subscription(ctx.notifyDataFlag)
|
|
1348
|
-
if was_streaming:
|
|
1365
|
+
if was_streaming and not oym_skip_stop_start:
|
|
1349
1366
|
await ctx.start_streaming()
|
|
1350
1367
|
except Exception as e:
|
|
1368
|
+
err_msg = str(e)
|
|
1351
1369
|
SdkLog.exception(_TAG, f"_do_set_param restart stream failed: {device_mac}")
|
|
1352
|
-
result = "ERROR: restart stream fail: " +
|
|
1370
|
+
result = "ERROR: restart stream fail: " + err_msg
|
|
1371
|
+
# 如果是因为设备已经断开,直接触发自动重连
|
|
1372
|
+
if "disconnected" in err_msg.lower() or "not connected" in err_msg.lower():
|
|
1373
|
+
SdkLog.w(_TAG, f"Device disconnected during setParam restart, scheduling reconnect for {device_mac}")
|
|
1374
|
+
self._schedule_reconnect(device_mac)
|
|
1353
1375
|
|
|
1354
1376
|
if key == "DEBUG_LOG_PATH":
|
|
1355
1377
|
try:
|
|
@@ -714,7 +714,9 @@ class SensorProfileDataCtx:
|
|
|
714
714
|
data.clear()
|
|
715
715
|
self.sensorDatas[SensorDataType.DATA_TYPE_EULER] = data
|
|
716
716
|
|
|
717
|
-
|
|
717
|
+
# EEG + OYM 设备默认不订阅 IMU
|
|
718
|
+
if not (self._chip_type == BLEChipType.OYM and self.hasEEG()):
|
|
719
|
+
self.notifyDataFlag |= DataSubscription.DNF_IMU
|
|
718
720
|
if self._device_info is not None:
|
|
719
721
|
self._device_info.AccChannelCount = 3
|
|
720
722
|
self._device_info.GyroChannelCount = 3
|
|
@@ -870,6 +872,14 @@ class SensorProfileDataCtx:
|
|
|
870
872
|
self._device_info = info
|
|
871
873
|
await self.initDataTransfer(True)
|
|
872
874
|
|
|
875
|
+
# EEG + OYM 设备默认关闭 IMU
|
|
876
|
+
if self._chip_type == BLEChipType.OYM and self.hasEEG():
|
|
877
|
+
self.notify_map["NTF_IMU"] = "OFF"
|
|
878
|
+
self.notify_map["NTF_GFORCE_ACC"] = "OFF"
|
|
879
|
+
self.notify_map["NTF_GFORCE_GYRO"] = "OFF"
|
|
880
|
+
self.notify_map["NTF_GFORCE_QUAT"] = "OFF"
|
|
881
|
+
self.notify_map["NTF_GFORCE_EULER"] = "OFF"
|
|
882
|
+
|
|
873
883
|
if self.hasConcatBLE():
|
|
874
884
|
self.notifyDataFlag |= DataSubscription.DNF_CONCAT_BLE
|
|
875
885
|
|
|
@@ -93,6 +93,11 @@ class SensorProfile:
|
|
|
93
93
|
self._device_info: Optional[DeviceInfo] = None
|
|
94
94
|
self._chip_type: BLEChipType = BLEChipType.Unknown
|
|
95
95
|
|
|
96
|
+
# 异常断开后自动恢复流状态所需信息
|
|
97
|
+
self._last_init_args: Optional[tuple] = None # (packageSampleCount, powerRefreshInterval)
|
|
98
|
+
self._was_streaming_before_disconnect = False
|
|
99
|
+
self._auto_restoring = False
|
|
100
|
+
|
|
96
101
|
# 用于在独立线程中执行用户回调,避免阻塞 BLE/数据解析线程
|
|
97
102
|
# onDataCallback 使用单线程池,保证数据回调严格按到达顺序执行
|
|
98
103
|
self._callback_executor = ThreadPoolExecutor(max_workers=4)
|
|
@@ -222,7 +227,8 @@ class SensorProfile:
|
|
|
222
227
|
return self._device_state
|
|
223
228
|
|
|
224
229
|
def _set_device_state(self, newState: DeviceStateEx):
|
|
225
|
-
|
|
230
|
+
old_state = self._device_state
|
|
231
|
+
if old_state != newState:
|
|
226
232
|
self._device_state = newState
|
|
227
233
|
if newState == DeviceStateEx.Disconnected:
|
|
228
234
|
self._has_inited = False
|
|
@@ -235,6 +241,33 @@ class SensorProfile:
|
|
|
235
241
|
SdkLog.e(_TAG, f"Error occurred while processing state change: {e}")
|
|
236
242
|
raise RuntimeError("Set device state %s fail: %s" % (self.BLEDevice.Name , e))
|
|
237
243
|
|
|
244
|
+
# 异常断开后重连成功,自动恢复 init + 数据流
|
|
245
|
+
if (newState == DeviceStateEx.Connected
|
|
246
|
+
and old_state == DeviceStateEx.Disconnected
|
|
247
|
+
and self._last_init_args is not None
|
|
248
|
+
and self._was_streaming_before_disconnect
|
|
249
|
+
and not self._auto_restoring):
|
|
250
|
+
SdkLog.i(_TAG, f"Auto-restoring stream state for {self._device_mac}")
|
|
251
|
+
threading.Thread(target=self._auto_restore, daemon=True).start()
|
|
252
|
+
|
|
253
|
+
def _auto_restore(self):
|
|
254
|
+
"""在异常断开后自动恢复 init 和数据流。"""
|
|
255
|
+
if self._auto_restoring:
|
|
256
|
+
return
|
|
257
|
+
self._auto_restoring = True
|
|
258
|
+
try:
|
|
259
|
+
package_sample_count, power_refresh_interval = self._last_init_args
|
|
260
|
+
SdkLog.i(_TAG, f"Auto-restore init for {self._device_mac}")
|
|
261
|
+
if self.init(package_sample_count, power_refresh_interval):
|
|
262
|
+
SdkLog.i(_TAG, f"Auto-restore start data notification for {self._device_mac}")
|
|
263
|
+
self.startDataNotification()
|
|
264
|
+
else:
|
|
265
|
+
SdkLog.w(_TAG, f"Auto-restore init failed for {self._device_mac}")
|
|
266
|
+
except Exception as e:
|
|
267
|
+
SdkLog.exception(_TAG, f"Auto-restore failed for {self._device_mac}: {e}")
|
|
268
|
+
finally:
|
|
269
|
+
self._auto_restoring = False
|
|
270
|
+
|
|
238
271
|
@property
|
|
239
272
|
def hasInited(self) -> bool:
|
|
240
273
|
|
|
@@ -8,7 +8,7 @@ with open(os.path.join(this_directory, "README.md"), "r", encoding="utf-8") as f
|
|
|
8
8
|
|
|
9
9
|
setup(
|
|
10
10
|
name="sensor-sdk",
|
|
11
|
-
version="0.2.
|
|
11
|
+
version="0.2.2",
|
|
12
12
|
description="Python sdk for Synchroni",
|
|
13
13
|
long_description=long_description,
|
|
14
14
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|