sensor-sdk 0.1.1__tar.gz → 0.1.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.1.1 → sensor_sdk-0.1.2}/PKG-INFO +1 -1
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/bleak_host.py +31 -3
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/bleak_process.py +25 -11
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/gforce.py +29 -18
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/sensor_controller.py +3 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/sensor_data_context.py +8 -2
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/PKG-INFO +1 -1
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/setup.py +1 -1
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/LICENSE.txt +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/README.md +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/__init__.py +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/sdk_log.py +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/sensor_data.py +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/sensor_device.py +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/sensor_profile.py +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor/sensor_utils.py +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/SOURCES.txt +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/dependency_links.txt +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/requires.txt +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/top_level.txt +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/sensor_sdk.egg-info/zip-safe +0 -0
- {sensor_sdk-0.1.1 → sensor_sdk-0.1.2}/setup.cfg +0 -0
|
@@ -51,6 +51,7 @@ class BleakHost:
|
|
|
51
51
|
def stop(self):
|
|
52
52
|
if not self._started:
|
|
53
53
|
return
|
|
54
|
+
self._should_exit = True
|
|
54
55
|
try:
|
|
55
56
|
self._cmd_queue.put({"type": "terminate"})
|
|
56
57
|
except Exception as e:
|
|
@@ -60,9 +61,22 @@ class BleakHost:
|
|
|
60
61
|
self._bleak_process.join(timeout=5)
|
|
61
62
|
if self._bleak_process.is_alive():
|
|
62
63
|
self._bleak_process.terminate()
|
|
64
|
+
self._bleak_process.join(timeout=2)
|
|
63
65
|
|
|
64
66
|
self._stop_result_loop()
|
|
65
67
|
self._stop_cmd_loop()
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
self._cmd_queue.close()
|
|
71
|
+
self._result_queue.close()
|
|
72
|
+
except Exception as e:
|
|
73
|
+
SdkLog.exception(_TAG, "Error closing queues")
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
self._bleak_process.close()
|
|
77
|
+
except Exception as e:
|
|
78
|
+
SdkLog.exception(_TAG, "Error closing bleak process")
|
|
79
|
+
|
|
66
80
|
self._started = False
|
|
67
81
|
|
|
68
82
|
# ------------------------------------------------------------------
|
|
@@ -77,9 +91,23 @@ class BleakHost:
|
|
|
77
91
|
result_thread.start()
|
|
78
92
|
self._result_loop = result_loop
|
|
79
93
|
self._result_thread = result_thread
|
|
80
|
-
|
|
94
|
+
|
|
95
|
+
def _create_consume_task():
|
|
96
|
+
self._consume_task = result_loop.create_task(self._consume_results())
|
|
97
|
+
|
|
98
|
+
result_loop.call_soon_threadsafe(_create_consume_task)
|
|
81
99
|
|
|
82
100
|
def _stop_result_loop(self):
|
|
101
|
+
task = getattr(self, '_consume_task', None)
|
|
102
|
+
if task is not None:
|
|
103
|
+
try:
|
|
104
|
+
loop = task.get_loop()
|
|
105
|
+
if loop is not None and not loop.is_closed():
|
|
106
|
+
loop.call_soon_threadsafe(task.cancel)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
109
|
+
self._consume_task = None
|
|
110
|
+
|
|
83
111
|
loop = getattr(self, '_result_loop', None)
|
|
84
112
|
thread = getattr(self, '_result_thread', None)
|
|
85
113
|
if loop is not None and not loop.is_closed():
|
|
@@ -97,10 +125,10 @@ class BleakHost:
|
|
|
97
125
|
|
|
98
126
|
async def _consume_results(self):
|
|
99
127
|
|
|
100
|
-
while
|
|
128
|
+
while not getattr(self, '_should_exit', False):
|
|
101
129
|
try:
|
|
102
130
|
msg = await asyncio.get_event_loop().run_in_executor(
|
|
103
|
-
None, self._result_queue.get, True, 0.
|
|
131
|
+
None, self._result_queue.get, True, 0.1
|
|
104
132
|
)
|
|
105
133
|
except queue.Empty:
|
|
106
134
|
continue
|
|
@@ -261,6 +261,17 @@ class BleakProcess(multiprocessing.Process):
|
|
|
261
261
|
|
|
262
262
|
def _stop_device_loops(self, device_mac: str):
|
|
263
263
|
|
|
264
|
+
# Cancel battery task if still running in event_loop
|
|
265
|
+
if device_mac in self._battery_tasks:
|
|
266
|
+
task = self._battery_tasks.pop(device_mac, None)
|
|
267
|
+
if task:
|
|
268
|
+
try:
|
|
269
|
+
loop = self._event_loops.get(device_mac)
|
|
270
|
+
if loop and not loop.is_closed():
|
|
271
|
+
loop.call_soon_threadsafe(task.cancel)
|
|
272
|
+
except Exception as e:
|
|
273
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
274
|
+
|
|
264
275
|
# Cancel data task first if still running in data_event_loop
|
|
265
276
|
if device_mac in self._data_tasks:
|
|
266
277
|
task = self._data_tasks.pop(device_mac, None)
|
|
@@ -684,7 +695,12 @@ class BleakProcess(multiprocessing.Process):
|
|
|
684
695
|
|
|
685
696
|
# Cancel battery task
|
|
686
697
|
if device_mac in self._battery_tasks:
|
|
687
|
-
self._battery_tasks.pop(device_mac, None)
|
|
698
|
+
task = self._battery_tasks.pop(device_mac, None)
|
|
699
|
+
if task is not None:
|
|
700
|
+
try:
|
|
701
|
+
task.cancel()
|
|
702
|
+
except Exception as e:
|
|
703
|
+
SdkLog.exception(_TAG, "Unexpected error")
|
|
688
704
|
|
|
689
705
|
# Stop streaming and optionally disconnect client
|
|
690
706
|
if disconnect_client and device_mac in self._gforces:
|
|
@@ -1119,10 +1135,12 @@ class BleakProcess(multiprocessing.Process):
|
|
|
1119
1135
|
if key == "NTF_PPG_RAW":
|
|
1120
1136
|
map_key = "NTF_PPG"
|
|
1121
1137
|
|
|
1122
|
-
# 老版本 EMG 设备上 Gesture 与 EMG
|
|
1138
|
+
# 老版本 EMG 设备上 Gesture 与 EMG 互斥,自动切换
|
|
1123
1139
|
if not ctx.isNewEMG:
|
|
1124
1140
|
if map_key == "NTF_GEST" and value == "ON" and ctx.notify_map.get("NTF_EMG") == "ON":
|
|
1125
|
-
|
|
1141
|
+
ctx.notify_map["NTF_EMG"] = "OFF"
|
|
1142
|
+
ctx.notify_map[map_key] = value
|
|
1143
|
+
result = "OK"
|
|
1126
1144
|
elif map_key == "NTF_EMG" and value == "ON" and ctx.notify_map.get("NTF_GEST") == "ON":
|
|
1127
1145
|
ctx.notify_map["NTF_GEST"] = "OFF"
|
|
1128
1146
|
ctx.notify_map[map_key] = value
|
|
@@ -1165,14 +1183,12 @@ class BleakProcess(multiprocessing.Process):
|
|
|
1165
1183
|
try:
|
|
1166
1184
|
if value == "False" or value == "":
|
|
1167
1185
|
SdkLog.set_log_path("")
|
|
1168
|
-
result = "OK"
|
|
1169
1186
|
elif value == "True":
|
|
1170
1187
|
path = SdkLog.get_default_log_path()
|
|
1171
1188
|
SdkLog.set_log_path(path)
|
|
1172
|
-
result = path
|
|
1173
1189
|
else:
|
|
1174
1190
|
SdkLog.set_log_path(value)
|
|
1175
|
-
|
|
1191
|
+
result = "OK"
|
|
1176
1192
|
except Exception as e:
|
|
1177
1193
|
SdkLog.exception(_TAG, f"_do_set_param DEBUG_LOG_PATH failed: {e}")
|
|
1178
1194
|
result = "ERROR: " + str(e)
|
|
@@ -1182,16 +1198,14 @@ class BleakProcess(multiprocessing.Process):
|
|
|
1182
1198
|
if value == "False" or value == "":
|
|
1183
1199
|
SdkLog.set_data_log_enabled(False)
|
|
1184
1200
|
await ctx.setDebugCSV("")
|
|
1185
|
-
result = "OK"
|
|
1186
1201
|
elif value == "True":
|
|
1187
1202
|
SdkLog.set_data_log_enabled(True)
|
|
1188
1203
|
path = SdkLog.get_default_data_log_path()
|
|
1189
|
-
|
|
1190
|
-
result = path if csv_result == "OK" else csv_result
|
|
1204
|
+
await ctx.setDebugCSV(path)
|
|
1191
1205
|
else:
|
|
1192
1206
|
SdkLog.set_data_log_enabled(True)
|
|
1193
|
-
|
|
1194
|
-
|
|
1207
|
+
await ctx.setDebugCSV(value)
|
|
1208
|
+
result = "OK"
|
|
1195
1209
|
except Exception as e:
|
|
1196
1210
|
SdkLog.exception(_TAG, f"_do_set_param setDebugCSV failed: {device_mac} path={value}")
|
|
1197
1211
|
result = "ERROR: " + str(e)
|
|
@@ -764,6 +764,7 @@ class GForce:
|
|
|
764
764
|
has_res=True,
|
|
765
765
|
)
|
|
766
766
|
)
|
|
767
|
+
return self._check_set_response(ret, "set_motor")
|
|
767
768
|
|
|
768
769
|
async def set_led(self, switchStatus):
|
|
769
770
|
body = [switchStatus == True]
|
|
@@ -775,6 +776,7 @@ class GForce:
|
|
|
775
776
|
has_res=True,
|
|
776
777
|
)
|
|
777
778
|
)
|
|
779
|
+
return self._check_set_response(ret, "set_led")
|
|
778
780
|
|
|
779
781
|
async def set_package_id(self, switchStatus):
|
|
780
782
|
body = [switchStatus == True]
|
|
@@ -786,6 +788,7 @@ class GForce:
|
|
|
786
788
|
has_res=True,
|
|
787
789
|
)
|
|
788
790
|
)
|
|
791
|
+
return self._check_set_response(ret, "set_package_id")
|
|
789
792
|
|
|
790
793
|
async def set_log_level(self, logLevel):
|
|
791
794
|
body = [0xFF & logLevel]
|
|
@@ -797,8 +800,9 @@ class GForce:
|
|
|
797
800
|
has_res=True,
|
|
798
801
|
)
|
|
799
802
|
)
|
|
803
|
+
return self._check_set_response(ret, "set_log_level")
|
|
800
804
|
|
|
801
|
-
async def set_function_switch(self, funcSwitch)
|
|
805
|
+
async def set_function_switch(self, funcSwitch):
|
|
802
806
|
body = [0xFF & funcSwitch]
|
|
803
807
|
body = bytes(body)
|
|
804
808
|
ret = await self._send_request(
|
|
@@ -808,11 +812,9 @@ class GForce:
|
|
|
808
812
|
has_res=True,
|
|
809
813
|
)
|
|
810
814
|
)
|
|
811
|
-
|
|
812
|
-
return True
|
|
813
|
-
return False
|
|
815
|
+
return self._check_set_response(ret, "set_function_switch")
|
|
814
816
|
|
|
815
|
-
async def set_neucir_app_control(self, open, close, stop)
|
|
817
|
+
async def set_neucir_app_control(self, open, close, stop):
|
|
816
818
|
if stop:
|
|
817
819
|
body = [4]
|
|
818
820
|
elif open:
|
|
@@ -828,11 +830,9 @@ class GForce:
|
|
|
828
830
|
has_res=True,
|
|
829
831
|
)
|
|
830
832
|
)
|
|
831
|
-
|
|
832
|
-
return True
|
|
833
|
-
return False
|
|
833
|
+
return self._check_set_response(ret, "set_neucir_app_control")
|
|
834
834
|
|
|
835
|
-
async def set_neucir_mode(self, mode)
|
|
835
|
+
async def set_neucir_mode(self, mode):
|
|
836
836
|
body = [0x90]
|
|
837
837
|
|
|
838
838
|
body = bytes(body)
|
|
@@ -843,14 +843,13 @@ class GForce:
|
|
|
843
843
|
has_res=True,
|
|
844
844
|
)
|
|
845
845
|
)
|
|
846
|
-
|
|
847
|
-
return True
|
|
848
|
-
return False
|
|
846
|
+
return self._check_set_response(ret, "set_neucir_mode")
|
|
849
847
|
|
|
850
848
|
async def set_firmware_filter_switch(self, switchStatus: int):
|
|
851
849
|
body = [0xFF & switchStatus]
|
|
852
850
|
body = bytes(body)
|
|
853
|
-
await self._send_request(Request(cmd=Command.CMD_SET_FRIMWARE_FILTER_SWITCH, body=body, has_res=True))
|
|
851
|
+
ret = await self._send_request(Request(cmd=Command.CMD_SET_FRIMWARE_FILTER_SWITCH, body=body, has_res=True))
|
|
852
|
+
return self._check_set_response(ret, "set_firmware_filter_switch")
|
|
854
853
|
|
|
855
854
|
async def get_firmware_filter_switch(self):
|
|
856
855
|
buf = await self._send_request(Request(cmd=Command.CMD_GET_FRIMWARE_FILTER_SWITCH, has_res=True))
|
|
@@ -867,6 +866,7 @@ class GForce:
|
|
|
867
866
|
)
|
|
868
867
|
|
|
869
868
|
# print('set_emg_raw_data_config returned:', ret)
|
|
869
|
+
return self._check_set_response(ret, "set_emg_raw_data_config")
|
|
870
870
|
|
|
871
871
|
self.resolution = cfg.resolution
|
|
872
872
|
|
|
@@ -925,7 +925,7 @@ class GForce:
|
|
|
925
925
|
)
|
|
926
926
|
return PpgRawDataConfig.from_bytes(buf)
|
|
927
927
|
|
|
928
|
-
async def set_ppg_raw_data_config(self, cfg: PpgRawDataConfig)
|
|
928
|
+
async def set_ppg_raw_data_config(self, cfg: PpgRawDataConfig):
|
|
929
929
|
body = cfg.to_bytes()
|
|
930
930
|
ret = await self._send_request(
|
|
931
931
|
Request(
|
|
@@ -934,7 +934,7 @@ class GForce:
|
|
|
934
934
|
has_res=True,
|
|
935
935
|
)
|
|
936
936
|
)
|
|
937
|
-
return ret
|
|
937
|
+
return self._check_set_response(ret, "set_ppg_raw_data_config")
|
|
938
938
|
|
|
939
939
|
async def get_imu_raw_data_config(self) -> ImuRawDataConfig:
|
|
940
940
|
buf = await self._send_request(
|
|
@@ -945,7 +945,7 @@ class GForce:
|
|
|
945
945
|
)
|
|
946
946
|
return ImuRawDataConfig.from_bytes(buf)
|
|
947
947
|
|
|
948
|
-
async def set_imu_raw_data_config(self, cfg: ImuRawDataConfig)
|
|
948
|
+
async def set_imu_raw_data_config(self, cfg: ImuRawDataConfig):
|
|
949
949
|
body = cfg.to_bytes()
|
|
950
950
|
ret = await self._send_request(
|
|
951
951
|
Request(
|
|
@@ -954,7 +954,7 @@ class GForce:
|
|
|
954
954
|
has_res=True,
|
|
955
955
|
)
|
|
956
956
|
)
|
|
957
|
-
return ret
|
|
957
|
+
return self._check_set_response(ret, "set_imu_raw_data_config")
|
|
958
958
|
|
|
959
959
|
async def get_imu_cap_data_config(self) -> Optional[tuple]:
|
|
960
960
|
"""
|
|
@@ -999,13 +999,24 @@ class GForce:
|
|
|
999
999
|
0xFF & (subscription >> 24),
|
|
1000
1000
|
]
|
|
1001
1001
|
body = bytes(body)
|
|
1002
|
-
await self._send_request(
|
|
1002
|
+
ret = await self._send_request(
|
|
1003
1003
|
Request(
|
|
1004
1004
|
cmd=Command.SET_DATA_NOTIF_SWITCH,
|
|
1005
1005
|
body=body,
|
|
1006
1006
|
has_res=True,
|
|
1007
1007
|
)
|
|
1008
1008
|
)
|
|
1009
|
+
return self._check_set_response(ret, "set_subscription")
|
|
1010
|
+
|
|
1011
|
+
def _check_set_response(self, ret: Optional[bytes], name: str) -> bytes:
|
|
1012
|
+
# """检查 set_xxxx 命令的响应,失败时抛出 RuntimeError。"""
|
|
1013
|
+
# if ret is None:
|
|
1014
|
+
# raise RuntimeError(f"{name} failed: no response")
|
|
1015
|
+
# if len(ret) == 0:
|
|
1016
|
+
# raise RuntimeError(f"{name} failed: empty response")
|
|
1017
|
+
# if ret[0] != 0:
|
|
1018
|
+
# raise RuntimeError(f"{name} failed: error code {ret[0]}")
|
|
1019
|
+
return ret
|
|
1009
1020
|
|
|
1010
1021
|
async def start_streaming(self, q: queue.Queue):
|
|
1011
1022
|
return await self._run_in_gforce_loop(self._do_start_streaming(q))
|
|
@@ -244,7 +244,7 @@ class SensorProfileDataCtx:
|
|
|
244
244
|
data.channelCount = 8
|
|
245
245
|
data.channelMask = config.channel_mask
|
|
246
246
|
data.minPackageSampleCount = packageCount
|
|
247
|
-
data.packageSampleCount =
|
|
247
|
+
data.packageSampleCount = config.batch_len
|
|
248
248
|
|
|
249
249
|
data.clear()
|
|
250
250
|
isNewEMG = True
|
|
@@ -297,6 +297,10 @@ class SensorProfileDataCtx:
|
|
|
297
297
|
self.sensorDatas[SensorDataType.DATA_TYPE_EMG] = data
|
|
298
298
|
self.isNewEMG = isNewEMG
|
|
299
299
|
|
|
300
|
+
# 老 EMG 设备不支持 FILTER
|
|
301
|
+
if not isNewEMG:
|
|
302
|
+
self.filter_map.clear()
|
|
303
|
+
|
|
300
304
|
return data.channelCount
|
|
301
305
|
|
|
302
306
|
|
|
@@ -433,7 +437,7 @@ class SensorProfileDataCtx:
|
|
|
433
437
|
async def initIMU(self, packageCount: int) -> int:
|
|
434
438
|
SdkLog.d(_TAG, "initIMU(...)")
|
|
435
439
|
IMU_TYPE_QAT6 = 0x0004
|
|
436
|
-
min_package_sample_count =
|
|
440
|
+
min_package_sample_count = 1
|
|
437
441
|
self.isContainQAT6 = False
|
|
438
442
|
|
|
439
443
|
if not self.hasIMU():
|
|
@@ -797,6 +801,8 @@ class SensorProfileDataCtx:
|
|
|
797
801
|
return True
|
|
798
802
|
|
|
799
803
|
async def setFilter(self, filter: str, value: str) -> str:
|
|
804
|
+
if not self.filter_map:
|
|
805
|
+
return "ERROR: Filter not supported on this device"
|
|
800
806
|
self.filter_map[filter] = value
|
|
801
807
|
switch = 0
|
|
802
808
|
for filter in self.filter_map.keys():
|
|
@@ -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.1.
|
|
11
|
+
version="0.1.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
|