sensor-sdk 0.0.71__tar.gz → 0.1.1__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.71/sensor_sdk.egg-info → sensor_sdk-0.1.1}/PKG-INFO +41 -11
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/README.md +40 -10
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor/bleak_host.py +30 -25
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor/bleak_process.py +1228 -1088
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor/gforce.py +1087 -1077
- sensor_sdk-0.1.1/sensor/sdk_log.py +259 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor/sensor_controller.py +40 -14
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor/sensor_data.py +103 -103
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor/sensor_data_context.py +1634 -1488
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor/sensor_device.py +4 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor/sensor_profile.py +630 -540
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor/sensor_utils.py +908 -906
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1/sensor_sdk.egg-info}/PKG-INFO +41 -11
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor_sdk.egg-info/SOURCES.txt +1 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/setup.py +1 -1
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/LICENSE.txt +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor/__init__.py +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor_sdk.egg-info/dependency_links.txt +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor_sdk.egg-info/requires.txt +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor_sdk.egg-info/top_level.txt +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/sensor_sdk.egg-info/zip-safe +0 -0
- {sensor_sdk-0.0.71 → sensor_sdk-0.1.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sensor-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: Python sdk for Synchroni
|
|
5
5
|
Home-page: https://github.com/oymotion/SynchroniSDKPython
|
|
6
6
|
Author: Martin Ye
|
|
@@ -208,6 +208,8 @@ success = sensorProfile.connect()
|
|
|
208
208
|
|
|
209
209
|
Use `def disconnect() -> bool` to disconnect.
|
|
210
210
|
|
|
211
|
+
If data notification is currently active, `disconnect()` will automatically stop it first before closing the BLE connection.
|
|
212
|
+
|
|
211
213
|
```python
|
|
212
214
|
success = sensorProfile.disconnect()
|
|
213
215
|
```
|
|
@@ -369,24 +371,31 @@ Please check async_console.py in examples directory
|
|
|
369
371
|
|
|
370
372
|
Use `def setParam(self, key: str, value: str) -> str` to set parameter of sensor profile. Please call after device in 'Ready' state.
|
|
371
373
|
|
|
374
|
+
The asynchronous variant is `asyncSetParam(self, key: str, value: str) -> str`.
|
|
375
|
+
|
|
376
|
+
If the device is already streaming when you change an `NTF_*` or `FILTER_*` key, the SDK will stop and restart the data notification so the new setting takes effect immediately.
|
|
377
|
+
|
|
372
378
|
Below is available key and value:
|
|
373
379
|
|
|
374
380
|
```python
|
|
381
|
+
# Data stream toggles
|
|
382
|
+
result = sensorProfile.setParam("NTF_GEST", "ON")
|
|
375
383
|
result = sensorProfile.setParam("NTF_EMG", "ON")
|
|
376
|
-
# set EMG data to ON or OFF, result is "OK" if succeed
|
|
377
|
-
|
|
378
384
|
result = sensorProfile.setParam("NTF_EEG", "ON")
|
|
379
|
-
# set EEG data to ON or OFF, result is "OK" if succeed
|
|
380
|
-
|
|
381
385
|
result = sensorProfile.setParam("NTF_ECG", "ON")
|
|
382
|
-
# set ECG data to ON or OFF, result is "OK" if succeed
|
|
383
|
-
|
|
384
386
|
result = sensorProfile.setParam("NTF_IMU", "ON")
|
|
385
|
-
# set IMU data to ON or OFF, result is "OK" if succeed
|
|
386
|
-
|
|
387
387
|
result = sensorProfile.setParam("NTF_BRTH", "ON")
|
|
388
|
-
|
|
389
|
-
|
|
388
|
+
result = sensorProfile.setParam("NTF_IMPEDANCE", "ON")
|
|
389
|
+
result = sensorProfile.setParam("NTF_MAG_ANGLE", "ON")
|
|
390
|
+
result = sensorProfile.setParam("NTF_PPG_RAW", "ON")
|
|
391
|
+
result = sensorProfile.setParam("NTF_GFORCE_EULER", "ON")
|
|
392
|
+
result = sensorProfile.setParam("NTF_GFORCE_QUAT", "ON")
|
|
393
|
+
result = sensorProfile.setParam("NTF_GFORCE_ACC", "ON")
|
|
394
|
+
result = sensorProfile.setParam("NTF_GFORCE_GYRO", "ON")
|
|
395
|
+
# set data stream to ON or OFF, result is "OK" if succeed
|
|
396
|
+
# Note: on legacy (non-new) EMG devices, NTF_GEST and NTF_EMG are mutually exclusive.
|
|
397
|
+
|
|
398
|
+
# Firmware filter toggles
|
|
390
399
|
result = sensorProfile.setParam("FILTER_50HZ", "ON")
|
|
391
400
|
# set 50Hz notch filter to ON or OFF, result is "OK" if succeed
|
|
392
401
|
|
|
@@ -403,3 +412,24 @@ result = sensorProfile.setParam("DEBUG_BLE_DATA_PATH", "d:/temp/test.csv")
|
|
|
403
412
|
# set debug ble data path, result is "OK" if succeed
|
|
404
413
|
# please give an absolute path and make sure it is valid and writeable by yourself
|
|
405
414
|
```
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
### getParam method
|
|
418
|
+
|
|
419
|
+
Use `def getParam(self, key: str) -> str` to query the current parameter state of a sensor profile. Please call after the device reaches the 'Ready' state.
|
|
420
|
+
|
|
421
|
+
The asynchronous variant is `asyncGetParam(self, key: str) -> str`.
|
|
422
|
+
|
|
423
|
+
Supported aggregate query keys:
|
|
424
|
+
|
|
425
|
+
```python
|
|
426
|
+
result = sensorProfile.getParam("FILTER")
|
|
427
|
+
# Returns a pipe-separated string of all filter states, e.g.:
|
|
428
|
+
# "FILTER_50HZ|ON|FILTER_60HZ|ON|FILTER_HPF|ON|FILTER_LPF|ON"
|
|
429
|
+
|
|
430
|
+
result = sensorProfile.getParam("NTF")
|
|
431
|
+
# Returns a pipe-separated string of all notification states, e.g.:
|
|
432
|
+
# "NTF_BRTH|ON|NTF_ECG|ON|NTF_EEG|ON|NTF_EMG|ON|..."
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
If the key is not supported, the result starts with `"Error"`.
|
|
@@ -185,6 +185,8 @@ success = sensorProfile.connect()
|
|
|
185
185
|
|
|
186
186
|
Use `def disconnect() -> bool` to disconnect.
|
|
187
187
|
|
|
188
|
+
If data notification is currently active, `disconnect()` will automatically stop it first before closing the BLE connection.
|
|
189
|
+
|
|
188
190
|
```python
|
|
189
191
|
success = sensorProfile.disconnect()
|
|
190
192
|
```
|
|
@@ -346,24 +348,31 @@ Please check async_console.py in examples directory
|
|
|
346
348
|
|
|
347
349
|
Use `def setParam(self, key: str, value: str) -> str` to set parameter of sensor profile. Please call after device in 'Ready' state.
|
|
348
350
|
|
|
351
|
+
The asynchronous variant is `asyncSetParam(self, key: str, value: str) -> str`.
|
|
352
|
+
|
|
353
|
+
If the device is already streaming when you change an `NTF_*` or `FILTER_*` key, the SDK will stop and restart the data notification so the new setting takes effect immediately.
|
|
354
|
+
|
|
349
355
|
Below is available key and value:
|
|
350
356
|
|
|
351
357
|
```python
|
|
358
|
+
# Data stream toggles
|
|
359
|
+
result = sensorProfile.setParam("NTF_GEST", "ON")
|
|
352
360
|
result = sensorProfile.setParam("NTF_EMG", "ON")
|
|
353
|
-
# set EMG data to ON or OFF, result is "OK" if succeed
|
|
354
|
-
|
|
355
361
|
result = sensorProfile.setParam("NTF_EEG", "ON")
|
|
356
|
-
# set EEG data to ON or OFF, result is "OK" if succeed
|
|
357
|
-
|
|
358
362
|
result = sensorProfile.setParam("NTF_ECG", "ON")
|
|
359
|
-
# set ECG data to ON or OFF, result is "OK" if succeed
|
|
360
|
-
|
|
361
363
|
result = sensorProfile.setParam("NTF_IMU", "ON")
|
|
362
|
-
# set IMU data to ON or OFF, result is "OK" if succeed
|
|
363
|
-
|
|
364
364
|
result = sensorProfile.setParam("NTF_BRTH", "ON")
|
|
365
|
-
|
|
366
|
-
|
|
365
|
+
result = sensorProfile.setParam("NTF_IMPEDANCE", "ON")
|
|
366
|
+
result = sensorProfile.setParam("NTF_MAG_ANGLE", "ON")
|
|
367
|
+
result = sensorProfile.setParam("NTF_PPG_RAW", "ON")
|
|
368
|
+
result = sensorProfile.setParam("NTF_GFORCE_EULER", "ON")
|
|
369
|
+
result = sensorProfile.setParam("NTF_GFORCE_QUAT", "ON")
|
|
370
|
+
result = sensorProfile.setParam("NTF_GFORCE_ACC", "ON")
|
|
371
|
+
result = sensorProfile.setParam("NTF_GFORCE_GYRO", "ON")
|
|
372
|
+
# set data stream to ON or OFF, result is "OK" if succeed
|
|
373
|
+
# Note: on legacy (non-new) EMG devices, NTF_GEST and NTF_EMG are mutually exclusive.
|
|
374
|
+
|
|
375
|
+
# Firmware filter toggles
|
|
367
376
|
result = sensorProfile.setParam("FILTER_50HZ", "ON")
|
|
368
377
|
# set 50Hz notch filter to ON or OFF, result is "OK" if succeed
|
|
369
378
|
|
|
@@ -380,3 +389,24 @@ result = sensorProfile.setParam("DEBUG_BLE_DATA_PATH", "d:/temp/test.csv")
|
|
|
380
389
|
# set debug ble data path, result is "OK" if succeed
|
|
381
390
|
# please give an absolute path and make sure it is valid and writeable by yourself
|
|
382
391
|
```
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
### getParam method
|
|
395
|
+
|
|
396
|
+
Use `def getParam(self, key: str) -> str` to query the current parameter state of a sensor profile. Please call after the device reaches the 'Ready' state.
|
|
397
|
+
|
|
398
|
+
The asynchronous variant is `asyncGetParam(self, key: str) -> str`.
|
|
399
|
+
|
|
400
|
+
Supported aggregate query keys:
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
result = sensorProfile.getParam("FILTER")
|
|
404
|
+
# Returns a pipe-separated string of all filter states, e.g.:
|
|
405
|
+
# "FILTER_50HZ|ON|FILTER_60HZ|ON|FILTER_HPF|ON|FILTER_LPF|ON"
|
|
406
|
+
|
|
407
|
+
result = sensorProfile.getParam("NTF")
|
|
408
|
+
# Returns a pipe-separated string of all notification states, e.g.:
|
|
409
|
+
# "NTF_BRTH|ON|NTF_ECG|ON|NTF_EEG|ON|NTF_EMG|ON|..."
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
If the key is not supported, the result starts with `"Error"`.
|
|
@@ -6,6 +6,9 @@ import uuid
|
|
|
6
6
|
|
|
7
7
|
from sensor.bleak_process import BleakProcess
|
|
8
8
|
from sensor import sensor_utils
|
|
9
|
+
from sensor.sdk_log import SdkLog
|
|
10
|
+
|
|
11
|
+
_TAG = "BleakHost"
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
class BleakHost:
|
|
@@ -14,7 +17,7 @@ class BleakHost:
|
|
|
14
17
|
def __init__(self):
|
|
15
18
|
self._cmd_queue = multiprocessing.Queue(maxsize=50)
|
|
16
19
|
self._result_queue = multiprocessing.Queue(maxsize=sensor_utils.BLEAK_RESULT_QUEUE_MAXSIZE)
|
|
17
|
-
self._bleak_process = BleakProcess(self._cmd_queue, self._result_queue)
|
|
20
|
+
self._bleak_process = BleakProcess(self._cmd_queue, self._result_queue, SdkLog.get_log_path())
|
|
18
21
|
self._started = False
|
|
19
22
|
|
|
20
23
|
|
|
@@ -51,8 +54,7 @@ class BleakHost:
|
|
|
51
54
|
try:
|
|
52
55
|
self._cmd_queue.put({"type": "terminate"})
|
|
53
56
|
except Exception as e:
|
|
54
|
-
|
|
55
|
-
traceback.print_exc()
|
|
57
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
56
58
|
|
|
57
59
|
if self._bleak_process.is_alive():
|
|
58
60
|
self._bleak_process.join(timeout=5)
|
|
@@ -84,14 +86,12 @@ class BleakHost:
|
|
|
84
86
|
try:
|
|
85
87
|
loop.call_soon_threadsafe(loop.stop)
|
|
86
88
|
except Exception as e:
|
|
87
|
-
|
|
88
|
-
traceback.print_exc()
|
|
89
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
89
90
|
if thread is not None and thread.is_alive():
|
|
90
91
|
try:
|
|
91
92
|
thread.join(timeout=2)
|
|
92
93
|
except Exception as e:
|
|
93
|
-
|
|
94
|
-
traceback.print_exc()
|
|
94
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
95
95
|
self._result_loop = None
|
|
96
96
|
self._result_thread = None
|
|
97
97
|
|
|
@@ -113,15 +113,13 @@ class BleakHost:
|
|
|
113
113
|
try:
|
|
114
114
|
self.on_scan_once_result(msg)
|
|
115
115
|
except Exception as e:
|
|
116
|
-
|
|
117
|
-
traceback.print_exc()
|
|
116
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
118
117
|
elif msg_type == "devices":
|
|
119
118
|
if self.on_scan_result is not None:
|
|
120
119
|
try:
|
|
121
120
|
self.on_scan_result(msg)
|
|
122
121
|
except Exception as e:
|
|
123
|
-
|
|
124
|
-
traceback.print_exc()
|
|
122
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
125
123
|
elif msg_type == "command_result":
|
|
126
124
|
self._handle_command_result(msg)
|
|
127
125
|
elif msg_type in ("state_changed", "power_changed", "sensor_data", "error"):
|
|
@@ -130,8 +128,7 @@ class BleakHost:
|
|
|
130
128
|
try:
|
|
131
129
|
self.on_device_message(device_mac, msg)
|
|
132
130
|
except Exception as e:
|
|
133
|
-
|
|
134
|
-
traceback.print_exc()
|
|
131
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
135
132
|
|
|
136
133
|
def _handle_command_result(self, msg):
|
|
137
134
|
|
|
@@ -149,6 +146,15 @@ class BleakHost:
|
|
|
149
146
|
if loop is not None and not loop.is_closed() and not waiter.done():
|
|
150
147
|
loop.call_soon_threadsafe(waiter.set_result, msg)
|
|
151
148
|
|
|
149
|
+
# 将 connect 返回的 chip_type 转发给对应 SensorProfile
|
|
150
|
+
chip_type = msg.get("chip_type")
|
|
151
|
+
device_mac = msg.get("device_mac")
|
|
152
|
+
if chip_type is not None and self.on_device_message is not None and device_mac is not None:
|
|
153
|
+
try:
|
|
154
|
+
self.on_device_message(device_mac, {"type": "chip_type", "chip_type": chip_type})
|
|
155
|
+
except Exception as e:
|
|
156
|
+
SdkLog.exception(_TAG, "Error forwarding chip_type")
|
|
157
|
+
|
|
152
158
|
# ------------------------------------------------------------------
|
|
153
159
|
# Command loop
|
|
154
160
|
# ------------------------------------------------------------------
|
|
@@ -169,14 +175,12 @@ class BleakHost:
|
|
|
169
175
|
try:
|
|
170
176
|
loop.call_soon_threadsafe(loop.stop)
|
|
171
177
|
except Exception as e:
|
|
172
|
-
|
|
173
|
-
traceback.print_exc()
|
|
178
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
174
179
|
if thread is not None and thread.is_alive():
|
|
175
180
|
try:
|
|
176
181
|
thread.join(timeout=2)
|
|
177
182
|
except Exception as e:
|
|
178
|
-
|
|
179
|
-
traceback.print_exc()
|
|
183
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
180
184
|
self._cmd_loop = None
|
|
181
185
|
self._cmd_thread = None
|
|
182
186
|
|
|
@@ -192,32 +196,28 @@ class BleakHost:
|
|
|
192
196
|
try:
|
|
193
197
|
self._cmd_queue.put_nowait({"type": "scan_once", "period": period})
|
|
194
198
|
except queue.Full as e:
|
|
195
|
-
|
|
196
|
-
traceback.print_exc()
|
|
199
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
197
200
|
|
|
198
201
|
def start_scan(self, period):
|
|
199
202
|
|
|
200
203
|
try:
|
|
201
204
|
self._cmd_queue.put_nowait({"type": "start_scan", "period": period})
|
|
202
205
|
except queue.Full as e:
|
|
203
|
-
|
|
204
|
-
traceback.print_exc()
|
|
206
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
205
207
|
|
|
206
208
|
def stop_scan(self):
|
|
207
209
|
|
|
208
210
|
try:
|
|
209
211
|
self._cmd_queue.put_nowait({"type": "stop_scan"})
|
|
210
212
|
except queue.Full as e:
|
|
211
|
-
|
|
212
|
-
traceback.print_exc()
|
|
213
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
213
214
|
|
|
214
215
|
def send_command(self, cmd: dict):
|
|
215
216
|
|
|
216
217
|
try:
|
|
217
218
|
self._cmd_queue.put_nowait(cmd)
|
|
218
219
|
except queue.Full as e:
|
|
219
|
-
|
|
220
|
-
traceback.print_exc()
|
|
220
|
+
SdkLog.exception(_TAG, "Command queue full")
|
|
221
221
|
|
|
222
222
|
def send_command_sync(self, cmd: dict, timeout: float = 10.0) -> dict:
|
|
223
223
|
|
|
@@ -235,12 +235,14 @@ class BleakHost:
|
|
|
235
235
|
try:
|
|
236
236
|
self._cmd_queue.put(cmd, timeout=1.0)
|
|
237
237
|
except queue.Full:
|
|
238
|
+
SdkLog.e(_TAG, f"send_command_sync queue full: {cmd.get('type')}")
|
|
238
239
|
with self._pending_lock:
|
|
239
240
|
self._pending.pop(cmd_id, None)
|
|
240
241
|
return {}
|
|
241
242
|
try:
|
|
242
243
|
await asyncio.wait_for(event.wait(), timeout=timeout)
|
|
243
244
|
except asyncio.TimeoutError:
|
|
245
|
+
SdkLog.e(_TAG, f"send_command_sync timeout: {cmd.get('type')}")
|
|
244
246
|
with self._pending_lock:
|
|
245
247
|
self._pending.pop(cmd_id, None)
|
|
246
248
|
return {}
|
|
@@ -252,6 +254,7 @@ class BleakHost:
|
|
|
252
254
|
try:
|
|
253
255
|
return future.result(timeout=timeout)
|
|
254
256
|
except Exception:
|
|
257
|
+
SdkLog.exception(_TAG, "send_command_sync future failed")
|
|
255
258
|
return {}
|
|
256
259
|
|
|
257
260
|
async def send_command_async(self, cmd: dict, timeout: float = 10.0) -> dict:
|
|
@@ -269,12 +272,14 @@ class BleakHost:
|
|
|
269
272
|
try:
|
|
270
273
|
self._cmd_queue.put(cmd, timeout=1.0)
|
|
271
274
|
except queue.Full:
|
|
275
|
+
SdkLog.e(_TAG, f"send_command_async queue full: {cmd.get('type')}")
|
|
272
276
|
with self._pending_lock:
|
|
273
277
|
self._pending.pop(cmd_id, None)
|
|
274
278
|
return {}
|
|
275
279
|
try:
|
|
276
280
|
return await asyncio.wait_for(future, timeout=timeout)
|
|
277
281
|
except asyncio.TimeoutError:
|
|
282
|
+
SdkLog.e(_TAG, f"send_command_async timeout: {cmd.get('type')}")
|
|
278
283
|
with self._pending_lock:
|
|
279
284
|
self._pending.pop(cmd_id, None)
|
|
280
285
|
return {}
|