sensor-sdk 0.0.18__tar.gz → 0.0.27__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.0.18 → sensor-sdk-0.0.27}/PKG-INFO +1 -1
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor/gforce.py +27 -3
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor/sensor_data_context.py +2 -2
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor/sensor_profile.py +78 -38
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor/sensor_utils.py +13 -7
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/PKG-INFO +1 -1
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/setup.py +1 -1
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/LICENSE.txt +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/README.md +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor/__init__.py +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor/sensor_controller.py +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor/sensor_data.py +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor/sensor_device.py +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/SOURCES.txt +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/dependency_links.txt +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/requires.txt +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/top_level.txt +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/zip-safe +0 -0
- {sensor-sdk-0.0.18 → sensor-sdk-0.0.27}/setup.cfg +0 -0
|
@@ -377,6 +377,7 @@ class GForce:
|
|
|
377
377
|
self.client = None
|
|
378
378
|
self.cmd_char = cmd_char
|
|
379
379
|
self.data_char = data_char
|
|
380
|
+
self.current_request: Request = None
|
|
380
381
|
self.responses: Dict[Command, Queue] = {}
|
|
381
382
|
self.resolution = SampleResolution.BITS_8
|
|
382
383
|
self._num_channels = 8
|
|
@@ -565,11 +566,14 @@ class GForce:
|
|
|
565
566
|
|
|
566
567
|
def _on_cmd_response(self, _: BleakGATTCharacteristic, bs: bytearray):
|
|
567
568
|
try:
|
|
569
|
+
# print(bytes(bs))
|
|
568
570
|
response = self._parse_response(bytes(bs))
|
|
569
|
-
if response.cmd
|
|
571
|
+
if self.responses.get(response.cmd) != None:
|
|
570
572
|
self.responses[response.cmd].put_nowait(
|
|
571
573
|
response.data,
|
|
572
574
|
)
|
|
575
|
+
else:
|
|
576
|
+
print("invalid response:" + bytes(bs))
|
|
573
577
|
except Exception as e:
|
|
574
578
|
raise Exception("Failed to parse response: %s" % e)
|
|
575
579
|
|
|
@@ -884,24 +888,44 @@ class GForce:
|
|
|
884
888
|
pass
|
|
885
889
|
|
|
886
890
|
def _get_response_channel(self, cmd: Command) -> Queue:
|
|
891
|
+
if self.responses.get(cmd) != None:
|
|
892
|
+
return None
|
|
893
|
+
|
|
887
894
|
q = Queue()
|
|
888
895
|
self.responses[cmd] = q
|
|
889
896
|
return q
|
|
890
897
|
|
|
891
898
|
async def _send_request(self, req: Request) -> Optional[bytes]:
|
|
899
|
+
|
|
892
900
|
q = None
|
|
893
901
|
if req.has_res:
|
|
894
902
|
q = self._get_response_channel(req.cmd)
|
|
903
|
+
if q == None:
|
|
904
|
+
# print("duplicate")
|
|
905
|
+
return None
|
|
906
|
+
|
|
907
|
+
while self.current_request != None:
|
|
908
|
+
# print("wait")
|
|
909
|
+
await asyncio.sleep(0.1)
|
|
895
910
|
|
|
911
|
+
self.current_request = req
|
|
896
912
|
bs = bytes([req.cmd])
|
|
897
913
|
if req.body is not None:
|
|
898
914
|
bs += req.body
|
|
899
|
-
|
|
915
|
+
|
|
916
|
+
# print(str(req.cmd) + str(req.body))
|
|
917
|
+
await asyncio.wait_for(self.client.write_gatt_char(self.cmd_char, bs, response=False), 0.1)
|
|
900
918
|
|
|
901
919
|
if not req.has_res:
|
|
920
|
+
self.current_request = None
|
|
902
921
|
return None
|
|
903
922
|
|
|
904
923
|
try:
|
|
905
|
-
|
|
924
|
+
ret = await asyncio.wait_for(q.get(), 0.5)
|
|
925
|
+
self.current_request = None
|
|
926
|
+
self.responses[req.cmd] = None
|
|
927
|
+
return ret
|
|
906
928
|
except Exception as e:
|
|
929
|
+
self.current_request = None
|
|
930
|
+
self.responses[req.cmd] = None
|
|
907
931
|
return None
|
|
@@ -351,7 +351,7 @@ class SensorProfileDataCtx:
|
|
|
351
351
|
switch |= 8
|
|
352
352
|
try:
|
|
353
353
|
await self.gForce.set_firmware_filter_switch(switch)
|
|
354
|
-
await asyncio.sleep(0.1)
|
|
354
|
+
# await asyncio.sleep(0.1)
|
|
355
355
|
return "OK"
|
|
356
356
|
except Exception as e:
|
|
357
357
|
return "ERROR: " + str(e)
|
|
@@ -738,7 +738,7 @@ class SensorProfileDataCtx:
|
|
|
738
738
|
continue
|
|
739
739
|
data_package = bytes(self._concatDataBuffer[index + 2 : index + 2 + n])
|
|
740
740
|
if not sensor_utils._terminated:
|
|
741
|
-
|
|
741
|
+
self.gForce._on_cmd_response(None, data_package)
|
|
742
742
|
last_cut = index = index + 2 + n
|
|
743
743
|
|
|
744
744
|
else:
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# 该枚举类定义了设备的各种状态,用于表示设备在不同操作阶段的状态信息
|
|
3
3
|
from enum import Enum, IntEnum
|
|
4
4
|
from queue import Queue
|
|
5
|
+
import threading
|
|
5
6
|
import time
|
|
6
7
|
from typing import Callable, Optional
|
|
7
8
|
|
|
@@ -60,7 +61,9 @@ class SensorProfile:
|
|
|
60
61
|
self._data_ctx: SensorProfileDataCtx = None
|
|
61
62
|
self._gforce: GForce = None
|
|
62
63
|
self._data_event_loop: asyncio.AbstractEventLoop = None
|
|
64
|
+
self._data_event_thread: threading.Thread = None
|
|
63
65
|
self._event_loop: asyncio.AbstractEventLoop = None
|
|
66
|
+
self._event_thread: threading.Thread = None
|
|
64
67
|
self._is_starting = False
|
|
65
68
|
self._is_setting_param = False
|
|
66
69
|
|
|
@@ -211,15 +214,7 @@ class SensorProfile:
|
|
|
211
214
|
"""
|
|
212
215
|
self._on_power_changed = callback
|
|
213
216
|
|
|
214
|
-
async def
|
|
215
|
-
if sensor_utils._terminated:
|
|
216
|
-
return False
|
|
217
|
-
|
|
218
|
-
if self._event_loop == None:
|
|
219
|
-
self._event_loop = asyncio.new_event_loop()
|
|
220
|
-
self._data_buffer: Queue[SensorData] = Queue()
|
|
221
|
-
self._raw_data_buf: Queue[bytes] = Queue()
|
|
222
|
-
|
|
217
|
+
async def _initGforce(self):
|
|
223
218
|
if self._gforce == None:
|
|
224
219
|
if self._adv.service_data.get(SERVICE_GUID) != None:
|
|
225
220
|
# print("OYM_SERVICE:" + self._detail_device.name)
|
|
@@ -240,25 +235,41 @@ class SensorProfile:
|
|
|
240
235
|
if self._data_ctx == None and self._gforce != None:
|
|
241
236
|
self._data_ctx = SensorProfileDataCtx(self._gforce, self._device.Address, self._raw_data_buf)
|
|
242
237
|
if self._data_ctx.isUniversalStream:
|
|
243
|
-
async_exec(self._process_universal_data())
|
|
238
|
+
async_exec(self._process_universal_data(), self._event_loop)
|
|
244
239
|
else:
|
|
245
|
-
async_exec(self._process_data())
|
|
240
|
+
async_exec(self._process_data(), self._event_loop)
|
|
241
|
+
|
|
242
|
+
async def _connect(self) -> bool:
|
|
243
|
+
if sensor_utils._terminated:
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
if self._event_loop == None:
|
|
247
|
+
self._event_loop = asyncio.new_event_loop()
|
|
248
|
+
self._event_thread = threading.Thread(target=sensor_utils.start_loop, args=(self._event_loop,))
|
|
249
|
+
self._event_thread.daemon = True
|
|
250
|
+
self._event_thread.name = self._detail_device.name + "event"
|
|
251
|
+
self._event_thread.start()
|
|
252
|
+
self._data_buffer: Queue[SensorData] = Queue()
|
|
253
|
+
self._raw_data_buf: Queue[bytes] = Queue()
|
|
246
254
|
|
|
247
255
|
if self.deviceState == DeviceStateEx.Connected or self.deviceState == DeviceStateEx.Ready:
|
|
248
256
|
return True
|
|
257
|
+
|
|
249
258
|
self._set_device_state(DeviceStateEx.Connecting)
|
|
250
259
|
|
|
260
|
+
await async_call(self._initGforce(), runloop=self._event_loop)
|
|
261
|
+
|
|
251
262
|
def handle_disconnect(_: BleakClient):
|
|
252
263
|
if self._data_ctx != None:
|
|
253
264
|
self._data_ctx.close()
|
|
254
|
-
time.sleep(
|
|
265
|
+
time.sleep(0.2)
|
|
255
266
|
self._data_buffer.queue.clear()
|
|
256
267
|
self._data_ctx = None
|
|
257
268
|
self._gforce = None
|
|
258
269
|
self._set_device_state(DeviceStateEx.Disconnected)
|
|
259
270
|
pass
|
|
260
271
|
|
|
261
|
-
await self._gforce.connect(handle_disconnect, self._raw_data_buf)
|
|
272
|
+
await async_call(self._gforce.connect(handle_disconnect, self._raw_data_buf), runloop=self._event_loop)
|
|
262
273
|
|
|
263
274
|
if self._gforce != None and self._gforce.client.is_connected:
|
|
264
275
|
self._set_device_state(DeviceStateEx.Connected)
|
|
@@ -293,7 +304,7 @@ class SensorProfile:
|
|
|
293
304
|
|
|
294
305
|
async def _waitForDisconnect(self) -> bool:
|
|
295
306
|
while not sensor_utils._terminated and self.deviceState != DeviceStateEx.Disconnected:
|
|
296
|
-
await asyncio.sleep(1)
|
|
307
|
+
await asyncio.sleep(0.1)
|
|
297
308
|
return True
|
|
298
309
|
|
|
299
310
|
async def _disconnect(self) -> bool:
|
|
@@ -302,7 +313,7 @@ class SensorProfile:
|
|
|
302
313
|
if self._data_ctx == None:
|
|
303
314
|
return False
|
|
304
315
|
self._set_device_state(DeviceStateEx.Disconnecting)
|
|
305
|
-
|
|
316
|
+
async_exec(self._gforce.disconnect(), self._event_loop)
|
|
306
317
|
await asyncio.wait_for(self._waitForDisconnect(), sensor_utils._TIMEOUT)
|
|
307
318
|
|
|
308
319
|
return True
|
|
@@ -348,10 +359,9 @@ class SensorProfile:
|
|
|
348
359
|
self._raw_data_buf.queue.clear()
|
|
349
360
|
self._data_buffer.queue.clear()
|
|
350
361
|
|
|
351
|
-
result = await self._data_ctx.start_streaming()
|
|
362
|
+
result = await async_call(self._data_ctx.start_streaming(), runloop=self._event_loop)
|
|
352
363
|
await asyncio.sleep(0.2)
|
|
353
364
|
|
|
354
|
-
self._is_starting = False
|
|
355
365
|
return result
|
|
356
366
|
|
|
357
367
|
def startDataNotification(self) -> bool:
|
|
@@ -364,8 +374,14 @@ class SensorProfile:
|
|
|
364
374
|
if self._is_starting:
|
|
365
375
|
return False
|
|
366
376
|
|
|
367
|
-
|
|
368
|
-
|
|
377
|
+
try:
|
|
378
|
+
self._is_starting = True
|
|
379
|
+
ret = sync_call(self._startDataNotification())
|
|
380
|
+
self._is_starting = False
|
|
381
|
+
return ret
|
|
382
|
+
except Exception as e:
|
|
383
|
+
self._is_starting = False
|
|
384
|
+
print(e)
|
|
369
385
|
|
|
370
386
|
async def asyncStartDataNotification(self) -> bool:
|
|
371
387
|
"""
|
|
@@ -377,8 +393,14 @@ class SensorProfile:
|
|
|
377
393
|
if self._is_starting:
|
|
378
394
|
return False
|
|
379
395
|
|
|
380
|
-
|
|
381
|
-
|
|
396
|
+
try:
|
|
397
|
+
self._is_starting = True
|
|
398
|
+
ret = await async_call(self._startDataNotification())
|
|
399
|
+
self._is_starting = False
|
|
400
|
+
return ret
|
|
401
|
+
except Exception as e:
|
|
402
|
+
self._is_starting = False
|
|
403
|
+
print(e)
|
|
382
404
|
|
|
383
405
|
async def _stopDataNotification(self) -> bool:
|
|
384
406
|
if self.deviceState != DeviceStateEx.Ready:
|
|
@@ -391,8 +413,7 @@ class SensorProfile:
|
|
|
391
413
|
if not self._data_ctx.isDataTransfering:
|
|
392
414
|
return True
|
|
393
415
|
|
|
394
|
-
result = await self._data_ctx.stop_streaming()
|
|
395
|
-
self._is_starting = False
|
|
416
|
+
result = await async_call(self._data_ctx.stop_streaming(), runloop=self._event_loop)
|
|
396
417
|
return result
|
|
397
418
|
|
|
398
419
|
def stopDataNotification(self) -> bool:
|
|
@@ -405,8 +426,14 @@ class SensorProfile:
|
|
|
405
426
|
if self._is_starting:
|
|
406
427
|
return False
|
|
407
428
|
|
|
408
|
-
|
|
409
|
-
|
|
429
|
+
try:
|
|
430
|
+
self._is_starting = True
|
|
431
|
+
ret = sync_call(self._stopDataNotification())
|
|
432
|
+
self._is_starting = False
|
|
433
|
+
return ret
|
|
434
|
+
except Exception as e:
|
|
435
|
+
self._is_starting = False
|
|
436
|
+
print(e)
|
|
410
437
|
|
|
411
438
|
async def asyncStopDataNotification(self) -> bool:
|
|
412
439
|
"""
|
|
@@ -418,8 +445,14 @@ class SensorProfile:
|
|
|
418
445
|
if self._is_starting:
|
|
419
446
|
return False
|
|
420
447
|
|
|
421
|
-
|
|
422
|
-
|
|
448
|
+
try:
|
|
449
|
+
self._is_starting = True
|
|
450
|
+
ret = await async_call(self._stopDataNotification())
|
|
451
|
+
self._is_starting = False
|
|
452
|
+
return ret
|
|
453
|
+
except Exception as e:
|
|
454
|
+
self._is_starting = False
|
|
455
|
+
print(e)
|
|
423
456
|
|
|
424
457
|
async def _refresh_power(self):
|
|
425
458
|
while not sensor_utils._terminated and self.deviceState == DeviceStateEx.Ready:
|
|
@@ -443,6 +476,7 @@ class SensorProfile:
|
|
|
443
476
|
|
|
444
477
|
if await self._data_ctx.init(packageSampleCount):
|
|
445
478
|
self._power_interval = powerRefreshInterval
|
|
479
|
+
self._power = await self._gforce.get_battery_level()
|
|
446
480
|
sensor_utils.async_exec(self._refresh_power())
|
|
447
481
|
|
|
448
482
|
return self._data_ctx.hasInit()
|
|
@@ -477,6 +511,23 @@ class SensorProfile:
|
|
|
477
511
|
20,
|
|
478
512
|
)
|
|
479
513
|
|
|
514
|
+
async def _asyncGetBatteryLevel(self) -> int:
|
|
515
|
+
if self.deviceState != DeviceStateEx.Ready:
|
|
516
|
+
return -1
|
|
517
|
+
if self._data_ctx == None:
|
|
518
|
+
return -1
|
|
519
|
+
self._power = await self._gforce.get_battery_level()
|
|
520
|
+
return self._power
|
|
521
|
+
|
|
522
|
+
async def asyncGetBatteryLevel(self) -> int:
|
|
523
|
+
"""
|
|
524
|
+
获取传感器的电池电量。
|
|
525
|
+
|
|
526
|
+
:return: int: 传感器的电池电量。 正常0-100,-1为未知。
|
|
527
|
+
|
|
528
|
+
"""
|
|
529
|
+
return await async_call(self._asyncGetBatteryLevel())
|
|
530
|
+
|
|
480
531
|
def getBatteryLevel(self) -> int:
|
|
481
532
|
"""
|
|
482
533
|
获取传感器的电池电量。
|
|
@@ -509,18 +560,7 @@ class SensorProfile:
|
|
|
509
560
|
|
|
510
561
|
if key in ["FILTER_50Hz", "FILTER_60Hz", "FILTER_HPF", "FILTER_LPF"]:
|
|
511
562
|
if value in ["ON", "OFF"]:
|
|
512
|
-
needPauseTransfer = self.isDataTransfering
|
|
513
|
-
if needPauseTransfer:
|
|
514
|
-
if self._is_starting:
|
|
515
|
-
self._is_setting_param = False
|
|
516
|
-
return "Error: Please pause data transfer first"
|
|
517
|
-
|
|
518
|
-
self._is_starting = True
|
|
519
|
-
await self._stopDataNotification()
|
|
520
563
|
result = await self._data_ctx.setFilter(key, value)
|
|
521
|
-
if needPauseTransfer:
|
|
522
|
-
self._is_starting = True
|
|
523
|
-
await self._startDataNotification()
|
|
524
564
|
|
|
525
565
|
if key == "DEBUG_BLE_DATA_PATH":
|
|
526
566
|
result = await self._data_ctx.setDebugCSV(value)
|
|
@@ -15,7 +15,7 @@ _needCloseRunloop = False
|
|
|
15
15
|
|
|
16
16
|
def checkRunLoop():
|
|
17
17
|
global _runloop, _needCloseRunloop, _event_thread
|
|
18
|
-
if _runloop == None:
|
|
18
|
+
if _runloop == None or not _runloop.is_running():
|
|
19
19
|
try:
|
|
20
20
|
_runloop = asyncio.get_running_loop()
|
|
21
21
|
except Exception as e:
|
|
@@ -45,11 +45,13 @@ def Terminate():
|
|
|
45
45
|
pass
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def async_exec(function):
|
|
48
|
+
def async_exec(function, runloop=None):
|
|
49
49
|
checkRunLoop()
|
|
50
|
+
if runloop == None:
|
|
51
|
+
runloop = _runloop
|
|
50
52
|
task: asyncio.Future = None
|
|
51
53
|
try:
|
|
52
|
-
task = asyncio.run_coroutine_threadsafe(function,
|
|
54
|
+
task = asyncio.run_coroutine_threadsafe(function, runloop)
|
|
53
55
|
running_tasks.add(task)
|
|
54
56
|
task.add_done_callback(lambda t: running_tasks.remove(t))
|
|
55
57
|
except Exception as e:
|
|
@@ -57,11 +59,13 @@ def async_exec(function):
|
|
|
57
59
|
pass
|
|
58
60
|
|
|
59
61
|
|
|
60
|
-
def sync_call(function, _timeout=_TIMEOUT) -> any:
|
|
62
|
+
def sync_call(function, _timeout=_TIMEOUT, runloop=None) -> any:
|
|
61
63
|
checkRunLoop()
|
|
62
64
|
task: asyncio.Future = None
|
|
65
|
+
if runloop == None:
|
|
66
|
+
runloop = _runloop
|
|
63
67
|
try:
|
|
64
|
-
task = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout),
|
|
68
|
+
task = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout), runloop)
|
|
65
69
|
running_tasks.add(task)
|
|
66
70
|
task.add_done_callback(lambda t: running_tasks.remove(t))
|
|
67
71
|
return task.result(timeout=_timeout)
|
|
@@ -70,11 +74,13 @@ def sync_call(function, _timeout=_TIMEOUT) -> any:
|
|
|
70
74
|
pass
|
|
71
75
|
|
|
72
76
|
|
|
73
|
-
async def async_call(function, _timeout=_TIMEOUT) -> any:
|
|
77
|
+
async def async_call(function, _timeout=_TIMEOUT, runloop=None) -> any:
|
|
74
78
|
checkRunLoop()
|
|
75
79
|
task: asyncio.Future = None
|
|
80
|
+
if runloop == None:
|
|
81
|
+
runloop = _runloop
|
|
76
82
|
try:
|
|
77
|
-
task = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout),
|
|
83
|
+
task = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout), runloop)
|
|
78
84
|
running_tasks.add(task)
|
|
79
85
|
task.add_done_callback(lambda t: running_tasks.remove(t))
|
|
80
86
|
except Exception as e:
|
|
@@ -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.0.
|
|
11
|
+
version="0.0.27",
|
|
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
|