sensor-sdk 0.0.16__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.16 → sensor-sdk-0.0.27}/PKG-INFO +3 -3
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/README.md +2 -2
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor/gforce.py +35 -11
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor/sensor_controller.py +7 -7
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor/sensor_data_context.py +11 -8
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor/sensor_profile.py +86 -46
- sensor-sdk-0.0.16/sensor/utils.py → sensor-sdk-0.0.27/sensor/sensor_utils.py +23 -8
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/PKG-INFO +3 -3
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/SOURCES.txt +1 -1
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/setup.py +1 -1
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/LICENSE.txt +0 -0
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor/__init__.py +0 -0
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor/sensor_data.py +0 -0
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor/sensor_device.py +0 -0
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/dependency_links.txt +0 -0
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/requires.txt +0 -0
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/top_level.txt +0 -0
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/sensor_sdk.egg-info/zip-safe +0 -0
- {sensor-sdk-0.0.16 → sensor-sdk-0.0.27}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sensor-sdk
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.27
|
|
4
4
|
Summary: Python sdk for Synchroni
|
|
5
5
|
Home-page: https://github.com/oymotion/SynchroniSDKPython
|
|
6
6
|
Author: Martin Ye
|
|
@@ -92,10 +92,10 @@ isScanning = SensorControllerInstance.isScanning
|
|
|
92
92
|
|
|
93
93
|
### 5. Check if bluetooth is enabled
|
|
94
94
|
|
|
95
|
-
Use `property
|
|
95
|
+
Use `property isEnable: bool` to check if bluetooth is enable
|
|
96
96
|
|
|
97
97
|
```python
|
|
98
|
-
|
|
98
|
+
isEnable = SensorControllerInstance.isEnable
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
### 6. Create SensorProfile
|
|
@@ -81,10 +81,10 @@ isScanning = SensorControllerInstance.isScanning
|
|
|
81
81
|
|
|
82
82
|
### 5. Check if bluetooth is enabled
|
|
83
83
|
|
|
84
|
-
Use `property
|
|
84
|
+
Use `property isEnable: bool` to check if bluetooth is enable
|
|
85
85
|
|
|
86
86
|
```python
|
|
87
|
-
|
|
87
|
+
isEnable = SensorControllerInstance.isEnable
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
### 6. Create SensorProfile
|
|
@@ -16,7 +16,7 @@ from bleak import (
|
|
|
16
16
|
BleakGATTCharacteristic,
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
-
from sensor import
|
|
19
|
+
from sensor import sensor_utils
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@dataclass
|
|
@@ -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
|
|
@@ -393,7 +394,7 @@ class GForce:
|
|
|
393
394
|
self._raw_data_buf = buf
|
|
394
395
|
|
|
395
396
|
try:
|
|
396
|
-
await asyncio.wait_for(client.connect(),
|
|
397
|
+
await asyncio.wait_for(client.connect(), sensor_utils._TIMEOUT)
|
|
397
398
|
except Exception as e:
|
|
398
399
|
return
|
|
399
400
|
|
|
@@ -404,12 +405,12 @@ class GForce:
|
|
|
404
405
|
if not self._is_universal_stream:
|
|
405
406
|
await asyncio.wait_for(
|
|
406
407
|
client.start_notify(self.cmd_char, self._on_cmd_response),
|
|
407
|
-
|
|
408
|
+
sensor_utils._TIMEOUT,
|
|
408
409
|
)
|
|
409
410
|
else:
|
|
410
411
|
await asyncio.wait_for(
|
|
411
412
|
client.start_notify(self.data_char, self._on_universal_response),
|
|
412
|
-
|
|
413
|
+
sensor_utils._TIMEOUT,
|
|
413
414
|
)
|
|
414
415
|
except Exception as e:
|
|
415
416
|
return
|
|
@@ -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
|
|
|
@@ -859,17 +863,17 @@ class GForce:
|
|
|
859
863
|
self.data_char,
|
|
860
864
|
lambda _, data: self._on_data_response(q, data),
|
|
861
865
|
),
|
|
862
|
-
|
|
866
|
+
sensor_utils._TIMEOUT,
|
|
863
867
|
)
|
|
864
868
|
|
|
865
869
|
async def stop_streaming(self):
|
|
866
870
|
exceptions = []
|
|
867
871
|
# try:
|
|
868
|
-
# await asyncio.wait_for(self.set_subscription(DataSubscription.OFF),
|
|
872
|
+
# await asyncio.wait_for(self.set_subscription(DataSubscription.OFF), sensor_utils._TIMEOUT)
|
|
869
873
|
# except Exception as e:
|
|
870
874
|
# exceptions.append(e)
|
|
871
875
|
try:
|
|
872
|
-
await asyncio.wait_for(self.client.stop_notify(self.data_char),
|
|
876
|
+
await asyncio.wait_for(self.client.stop_notify(self.data_char), sensor_utils._TIMEOUT)
|
|
873
877
|
except Exception as e:
|
|
874
878
|
exceptions.append(e)
|
|
875
879
|
|
|
@@ -879,29 +883,49 @@ class GForce:
|
|
|
879
883
|
async def disconnect(self):
|
|
880
884
|
with suppress(asyncio.CancelledError):
|
|
881
885
|
try:
|
|
882
|
-
await asyncio.wait_for(self.client.disconnect(),
|
|
886
|
+
await asyncio.wait_for(self.client.disconnect(), sensor_utils._TIMEOUT)
|
|
883
887
|
except Exception as e:
|
|
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
|
|
@@ -6,10 +6,10 @@ from typing import Callable, Dict, List, Optional, Tuple
|
|
|
6
6
|
import bleak
|
|
7
7
|
|
|
8
8
|
from sensor import sensor_profile
|
|
9
|
-
from sensor import
|
|
9
|
+
from sensor import sensor_utils
|
|
10
10
|
from sensor.sensor_profile import DeviceStateEx, SensorProfile
|
|
11
11
|
|
|
12
|
-
from sensor.
|
|
12
|
+
from sensor.sensor_utils import async_call, sync_call, async_exec
|
|
13
13
|
from bleak import (
|
|
14
14
|
BleakScanner,
|
|
15
15
|
AdvertisementData,
|
|
@@ -52,13 +52,13 @@ class SensorController:
|
|
|
52
52
|
"""
|
|
53
53
|
|
|
54
54
|
def terminate(self):
|
|
55
|
-
|
|
55
|
+
sensor_utils._terminated = True
|
|
56
56
|
|
|
57
57
|
for sensor in self._sensor_profiles.values():
|
|
58
58
|
if sensor.deviceState == DeviceStateEx.Connected or sensor.deviceState == DeviceStateEx.Ready:
|
|
59
59
|
sensor._destroy()
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
sensor_utils.Terminate()
|
|
62
62
|
|
|
63
63
|
def _match_device(self, _device: bleak.BLEDevice, adv: AdvertisementData):
|
|
64
64
|
if _device.name == None:
|
|
@@ -71,7 +71,7 @@ class SensorController:
|
|
|
71
71
|
return False
|
|
72
72
|
|
|
73
73
|
@property
|
|
74
|
-
def
|
|
74
|
+
def isScanning(self) -> bool:
|
|
75
75
|
"""
|
|
76
76
|
检查是否正在扫描。
|
|
77
77
|
|
|
@@ -181,13 +181,13 @@ class SensorController:
|
|
|
181
181
|
return await async_call(self._async_scan(period))
|
|
182
182
|
|
|
183
183
|
async def _device_scan_callback(self, devices: List[sensor_profile.BLEDevice]):
|
|
184
|
-
if self._device_callback:
|
|
184
|
+
if not sensor_utils._terminated and self._device_callback:
|
|
185
185
|
try:
|
|
186
186
|
asyncio.get_event_loop().run_in_executor(None, self._device_callback, devices)
|
|
187
187
|
except Exception as e:
|
|
188
188
|
print(e)
|
|
189
189
|
|
|
190
|
-
if self._is_scanning:
|
|
190
|
+
if not sensor_utils._terminated and self._is_scanning:
|
|
191
191
|
async_exec(self._startScan())
|
|
192
192
|
|
|
193
193
|
async def _startScan(self) -> bool:
|
|
@@ -7,7 +7,7 @@ import struct
|
|
|
7
7
|
from typing import Deque, List
|
|
8
8
|
from concurrent.futures import ThreadPoolExecutor
|
|
9
9
|
import csv
|
|
10
|
-
from sensor import
|
|
10
|
+
from sensor import sensor_utils
|
|
11
11
|
from sensor.gforce import DataSubscription, GForce, SamplingRate
|
|
12
12
|
from sensor.sensor_data import DataType, Sample, SensorData
|
|
13
13
|
|
|
@@ -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)
|
|
@@ -391,7 +391,7 @@ class SensorProfileDataCtx:
|
|
|
391
391
|
sensorData = buf.get_nowait()
|
|
392
392
|
except Exception as e:
|
|
393
393
|
break
|
|
394
|
-
if sensorData != None and callback != None:
|
|
394
|
+
if not sensor_utils._terminated and sensorData != None and callback != None:
|
|
395
395
|
try:
|
|
396
396
|
asyncio.get_event_loop().run_in_executor(self.dataPool, callback, sensor, sensorData)
|
|
397
397
|
except Exception as e:
|
|
@@ -459,6 +459,8 @@ class SensorProfileDataCtx:
|
|
|
459
459
|
offset += 2
|
|
460
460
|
newPackageIndex = packageIndex
|
|
461
461
|
lastPackageIndex = sensorData.lastPackageIndex
|
|
462
|
+
if sensorData.lastPackageCounter == 0 and sensorData.lastPackageIndex == 0 and packageIndex > 1:
|
|
463
|
+
return False
|
|
462
464
|
|
|
463
465
|
if packageIndex < lastPackageIndex:
|
|
464
466
|
packageIndex += 65536 # 包索引是 U16 类型
|
|
@@ -477,7 +479,7 @@ class SensorProfileDataCtx:
|
|
|
477
479
|
+ str(lostSampleCount)
|
|
478
480
|
)
|
|
479
481
|
# print(lostLog)
|
|
480
|
-
if sensor._event_loop != None and sensor._on_error_callback != None:
|
|
482
|
+
if not sensor_utils._terminated and sensor._event_loop != None and sensor._on_error_callback != None:
|
|
481
483
|
try:
|
|
482
484
|
asyncio.get_event_loop().run_in_executor(None, sensor._on_error_callback, sensor, lostLog)
|
|
483
485
|
except Exception as e:
|
|
@@ -699,7 +701,7 @@ class SensorProfileDataCtx:
|
|
|
699
701
|
index += 1
|
|
700
702
|
continue
|
|
701
703
|
crc = self._concatDataBuffer[index + 1 + n + 1]
|
|
702
|
-
calc_crc =
|
|
704
|
+
calc_crc = sensor_utils.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
|
|
703
705
|
if crc != calc_crc:
|
|
704
706
|
index += 1
|
|
705
707
|
continue
|
|
@@ -712,7 +714,7 @@ class SensorProfileDataCtx:
|
|
|
712
714
|
sensorData = buf.get_nowait()
|
|
713
715
|
except Exception as e:
|
|
714
716
|
break
|
|
715
|
-
if sensorData != None and callback != None:
|
|
717
|
+
if not sensor_utils._terminated and sensorData != None and callback != None:
|
|
716
718
|
try:
|
|
717
719
|
asyncio.get_event_loop().run_in_executor(self.dataPool, callback, sensor, sensorData)
|
|
718
720
|
except Exception as e:
|
|
@@ -730,12 +732,13 @@ class SensorProfileDataCtx:
|
|
|
730
732
|
index += 1
|
|
731
733
|
continue
|
|
732
734
|
crc = self._concatDataBuffer[index + 1 + n + 1]
|
|
733
|
-
calc_crc =
|
|
735
|
+
calc_crc = sensor_utils.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
|
|
734
736
|
if crc != calc_crc:
|
|
735
737
|
index += 1
|
|
736
738
|
continue
|
|
737
739
|
data_package = bytes(self._concatDataBuffer[index + 2 : index + 2 + n])
|
|
738
|
-
|
|
740
|
+
if not sensor_utils._terminated:
|
|
741
|
+
self.gForce._on_cmd_response(None, data_package)
|
|
739
742
|
last_cut = index = index + 2 + n
|
|
740
743
|
|
|
741
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
|
|
|
@@ -10,7 +11,7 @@ from bleak import (
|
|
|
10
11
|
BleakClient,
|
|
11
12
|
)
|
|
12
13
|
|
|
13
|
-
from sensor import
|
|
14
|
+
from sensor import sensor_utils
|
|
14
15
|
from sensor.gforce import GForce
|
|
15
16
|
from sensor.sensor_data import SensorData
|
|
16
17
|
import asyncio
|
|
@@ -18,7 +19,7 @@ import asyncio
|
|
|
18
19
|
|
|
19
20
|
from sensor.sensor_data_context import SensorProfileDataCtx
|
|
20
21
|
from sensor.sensor_device import BLEDevice, DeviceInfo, DeviceStateEx
|
|
21
|
-
from sensor.
|
|
22
|
+
from sensor.sensor_utils import async_call, sync_call, async_exec
|
|
22
23
|
|
|
23
24
|
SERVICE_GUID = "0000ffd0-0000-1000-8000-00805f9b34fb"
|
|
24
25
|
OYM_CMD_NOTIFY_CHAR_UUID = "f000ffe1-0451-4000-b000-000000000000"
|
|
@@ -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 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)
|
|
@@ -292,8 +303,8 @@ class SensorProfile:
|
|
|
292
303
|
return await async_call(self._connect())
|
|
293
304
|
|
|
294
305
|
async def _waitForDisconnect(self) -> bool:
|
|
295
|
-
while not
|
|
296
|
-
await asyncio.sleep(1)
|
|
306
|
+
while not sensor_utils._terminated and self.deviceState != DeviceStateEx.Disconnected:
|
|
307
|
+
await asyncio.sleep(0.1)
|
|
297
308
|
return True
|
|
298
309
|
|
|
299
310
|
async def _disconnect(self) -> bool:
|
|
@@ -302,8 +313,8 @@ class SensorProfile:
|
|
|
302
313
|
if self._data_ctx == None:
|
|
303
314
|
return False
|
|
304
315
|
self._set_device_state(DeviceStateEx.Disconnecting)
|
|
305
|
-
|
|
306
|
-
await asyncio.wait_for(self._waitForDisconnect(),
|
|
316
|
+
async_exec(self._gforce.disconnect(), self._event_loop)
|
|
317
|
+
await asyncio.wait_for(self._waitForDisconnect(), sensor_utils._TIMEOUT)
|
|
307
318
|
|
|
308
319
|
return True
|
|
309
320
|
|
|
@@ -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,9 +413,8 @@ 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
|
-
|
|
396
|
-
return not result
|
|
416
|
+
result = await async_call(self._data_ctx.stop_streaming(), runloop=self._event_loop)
|
|
417
|
+
return result
|
|
397
418
|
|
|
398
419
|
def stopDataNotification(self) -> bool:
|
|
399
420
|
"""
|
|
@@ -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,16 +445,22 @@ 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
|
-
while not
|
|
458
|
+
while not sensor_utils._terminated and self.deviceState == DeviceStateEx.Ready:
|
|
426
459
|
await asyncio.sleep(self._power_interval / 1000)
|
|
427
460
|
|
|
428
461
|
self._power = await self._gforce.get_battery_level()
|
|
429
462
|
|
|
430
|
-
if self._event_loop != None and self._on_power_changed != None:
|
|
463
|
+
if not sensor_utils._terminated and self._event_loop != None and self._on_power_changed != None:
|
|
431
464
|
try:
|
|
432
465
|
asyncio.get_event_loop().run_in_executor(None, self._on_power_changed, self, self._power)
|
|
433
466
|
except Exception as e:
|
|
@@ -443,7 +476,8 @@ class SensorProfile:
|
|
|
443
476
|
|
|
444
477
|
if await self._data_ctx.init(packageSampleCount):
|
|
445
478
|
self._power_interval = powerRefreshInterval
|
|
446
|
-
|
|
479
|
+
self._power = await self._gforce.get_battery_level()
|
|
480
|
+
sensor_utils.async_exec(self._refresh_power())
|
|
447
481
|
|
|
448
482
|
return self._data_ctx.hasInit()
|
|
449
483
|
|
|
@@ -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:
|
|
@@ -25,10 +25,18 @@ def checkRunLoop():
|
|
|
25
25
|
_event_thread.daemon = True
|
|
26
26
|
_event_thread.name = "SensorController event"
|
|
27
27
|
_event_thread.start()
|
|
28
|
+
time.sleep(0.1)
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
def Terminate():
|
|
31
|
-
global _runloop, _needCloseRunloop, _event_thread
|
|
32
|
+
global _runloop, _needCloseRunloop, _event_thread, _terminated
|
|
33
|
+
_terminated = True
|
|
34
|
+
try:
|
|
35
|
+
for task in asyncio.all_tasks():
|
|
36
|
+
task.cancel()
|
|
37
|
+
except Exception as e:
|
|
38
|
+
pass
|
|
39
|
+
|
|
32
40
|
if _needCloseRunloop:
|
|
33
41
|
try:
|
|
34
42
|
_runloop.stop()
|
|
@@ -37,11 +45,13 @@ def Terminate():
|
|
|
37
45
|
pass
|
|
38
46
|
|
|
39
47
|
|
|
40
|
-
def async_exec(function):
|
|
48
|
+
def async_exec(function, runloop=None):
|
|
41
49
|
checkRunLoop()
|
|
50
|
+
if runloop == None:
|
|
51
|
+
runloop = _runloop
|
|
42
52
|
task: asyncio.Future = None
|
|
43
53
|
try:
|
|
44
|
-
task = asyncio.run_coroutine_threadsafe(function,
|
|
54
|
+
task = asyncio.run_coroutine_threadsafe(function, runloop)
|
|
45
55
|
running_tasks.add(task)
|
|
46
56
|
task.add_done_callback(lambda t: running_tasks.remove(t))
|
|
47
57
|
except Exception as e:
|
|
@@ -49,11 +59,13 @@ def async_exec(function):
|
|
|
49
59
|
pass
|
|
50
60
|
|
|
51
61
|
|
|
52
|
-
def sync_call(function, _timeout=_TIMEOUT) -> any:
|
|
62
|
+
def sync_call(function, _timeout=_TIMEOUT, runloop=None) -> any:
|
|
53
63
|
checkRunLoop()
|
|
54
64
|
task: asyncio.Future = None
|
|
65
|
+
if runloop == None:
|
|
66
|
+
runloop = _runloop
|
|
55
67
|
try:
|
|
56
|
-
task = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout),
|
|
68
|
+
task = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout), runloop)
|
|
57
69
|
running_tasks.add(task)
|
|
58
70
|
task.add_done_callback(lambda t: running_tasks.remove(t))
|
|
59
71
|
return task.result(timeout=_timeout)
|
|
@@ -62,11 +74,13 @@ def sync_call(function, _timeout=_TIMEOUT) -> any:
|
|
|
62
74
|
pass
|
|
63
75
|
|
|
64
76
|
|
|
65
|
-
async def async_call(function, _timeout=_TIMEOUT) -> any:
|
|
77
|
+
async def async_call(function, _timeout=_TIMEOUT, runloop=None) -> any:
|
|
66
78
|
checkRunLoop()
|
|
67
79
|
task: asyncio.Future = None
|
|
80
|
+
if runloop == None:
|
|
81
|
+
runloop = _runloop
|
|
68
82
|
try:
|
|
69
|
-
task = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout),
|
|
83
|
+
task = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout), runloop)
|
|
70
84
|
running_tasks.add(task)
|
|
71
85
|
task.add_done_callback(lambda t: running_tasks.remove(t))
|
|
72
86
|
except Exception as e:
|
|
@@ -85,6 +99,7 @@ async def async_call(function, _timeout=_TIMEOUT) -> any:
|
|
|
85
99
|
|
|
86
100
|
|
|
87
101
|
def start_loop(loop: asyncio.BaseEventLoop):
|
|
102
|
+
# asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
|
88
103
|
asyncio.set_event_loop(loop)
|
|
89
104
|
loop.run_forever()
|
|
90
105
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sensor-sdk
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.27
|
|
4
4
|
Summary: Python sdk for Synchroni
|
|
5
5
|
Home-page: https://github.com/oymotion/SynchroniSDKPython
|
|
6
6
|
Author: Martin Ye
|
|
@@ -92,10 +92,10 @@ isScanning = SensorControllerInstance.isScanning
|
|
|
92
92
|
|
|
93
93
|
### 5. Check if bluetooth is enabled
|
|
94
94
|
|
|
95
|
-
Use `property
|
|
95
|
+
Use `property isEnable: bool` to check if bluetooth is enable
|
|
96
96
|
|
|
97
97
|
```python
|
|
98
|
-
|
|
98
|
+
isEnable = SensorControllerInstance.isEnable
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
### 6. Create SensorProfile
|
|
@@ -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
|