sensor-sdk 0.0.4__py3-none-any.whl → 0.0.6__py3-none-any.whl

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.

Potentially problematic release.


This version of sensor-sdk might be problematic. Click here for more details.

sensor/gforce.py CHANGED
@@ -16,6 +16,8 @@ from bleak import (
16
16
  BleakGATTCharacteristic,
17
17
  )
18
18
 
19
+ from sensor import utils
20
+
19
21
 
20
22
  @dataclass
21
23
  class Characteristic:
@@ -146,15 +148,15 @@ class DataSubscription(IntEnum):
146
148
  LOG = (0x00000800,)
147
149
 
148
150
  DNF_EEG = (0x00010000,)
149
-
151
+
150
152
  DNF_ECG = (0x00020000,)
151
-
153
+
152
154
  DNF_IMPEDANCE = (0x00040000,)
153
-
155
+
154
156
  DNF_IMU = (0x00080000,)
155
-
157
+
156
158
  DNF_ADS = (0x00100000,)
157
-
159
+
158
160
  DNF_BRTH = (0x00200000,)
159
161
 
160
162
  DNF_CONCAT_BLE = (0x80000000,)
@@ -181,8 +183,8 @@ class DataType(IntEnum):
181
183
 
182
184
  class SampleResolution(IntEnum):
183
185
  BITS_8 = (8,)
184
- BITS_12 = 12,
185
- BITS_16 = 16,
186
+ BITS_12 = (12,)
187
+ BITS_16 = (16,)
186
188
  BITS_24 = 24
187
189
 
188
190
 
@@ -215,6 +217,7 @@ class EmgRawDataConfig:
215
217
  )
216
218
  return cls(fs, channel_mask, batch_len, resolution)
217
219
 
220
+
218
221
  @dataclass
219
222
  class EegRawDataConfig:
220
223
  fs: SamplingRate = 0
@@ -240,6 +243,7 @@ class EegRawDataConfig:
240
243
  )
241
244
  return cls(fs, channel_mask, batch_len, resolution, K)
242
245
 
246
+
243
247
  @dataclass
244
248
  class EegRawDataCap:
245
249
  fs: SamplingRate = 0
@@ -262,7 +266,8 @@ class EegRawDataCap:
262
266
  data,
263
267
  )
264
268
  return cls(fs, channel_count, batch_len, resolution)
265
-
269
+
270
+
266
271
  @dataclass
267
272
  class EcgRawDataConfig:
268
273
  fs: SamplingRate = SamplingRate.HZ_250
@@ -287,7 +292,8 @@ class EcgRawDataConfig:
287
292
  data,
288
293
  )
289
294
  return cls(fs, channel_mask, batch_len, resolution, K)
290
-
295
+
296
+
291
297
  @dataclass
292
298
  class ImuRawDataConfig:
293
299
  channel_count: int = 0
@@ -295,6 +301,7 @@ class ImuRawDataConfig:
295
301
  batch_len: int = 0
296
302
  accK: float = 0
297
303
  gyroK: float = 0
304
+
298
305
  def to_bytes(self) -> bytes:
299
306
  body = b""
300
307
  body += struct.pack("<i", self.channel_count)
@@ -311,7 +318,8 @@ class ImuRawDataConfig:
311
318
  data,
312
319
  )
313
320
  return cls(channel_count, fs, batch_len, accK, gyroK)
314
-
321
+
322
+
315
323
  @dataclass
316
324
  class BrthRawDataConfig:
317
325
  fs: SamplingRate = 0
@@ -336,7 +344,8 @@ class BrthRawDataConfig:
336
344
  data,
337
345
  )
338
346
  return cls(fs, channel_mask, batch_len, resolution, K)
339
-
347
+
348
+
340
349
  @dataclass
341
350
  class Request:
342
351
  cmd: Command
@@ -361,7 +370,9 @@ class Response:
361
370
 
362
371
 
363
372
  class GForce:
364
- def __init__(self, device: BLEDevice, cmd_char: str, data_char: str, isUniversalStream: bool):
373
+ def __init__(
374
+ self, device: BLEDevice, cmd_char: str, data_char: str, isUniversalStream: bool
375
+ ):
365
376
  self.device_name = ""
366
377
  self.client = None
367
378
  self.cmd_char = cmd_char
@@ -371,21 +382,33 @@ class GForce:
371
382
  self._num_channels = 8
372
383
  self._device = device
373
384
  self._is_universal_stream = isUniversalStream
374
- self._raw_data_buf:queue.Queue[bytes] = None
385
+ self._raw_data_buf: queue.Queue[bytes] = None
375
386
  self.packet_id = 0
376
387
  self.data_packet = []
377
388
 
378
389
  async def connect(self, disconnect_cb, buf: queue.Queue[bytes]):
379
390
  client = BleakClient(self._device, disconnected_callback=disconnect_cb)
380
- await client.connect()
381
-
382
391
  self.client = client
383
392
  self.device_name = self._device.name
384
393
  self._raw_data_buf = buf
385
- if (not self._is_universal_stream):
386
- await client.start_notify(self.cmd_char,self._on_cmd_response)
394
+
395
+ try:
396
+ await asyncio.wait_for(client.connect(), utils._TIMEOUT)
397
+ except Exception as e:
398
+ return
399
+
400
+ if not client.is_connected:
401
+ return
402
+ if not self._is_universal_stream:
403
+ await asyncio.wait_for(
404
+ client.start_notify(self.cmd_char, self._on_cmd_response),
405
+ utils._TIMEOUT,
406
+ )
387
407
  else:
388
- await client.start_notify(self.data_char,self._on_universal_response)
408
+ await asyncio.wait_for(
409
+ client.start_notify(self.data_char, self._on_universal_response),
410
+ utils._TIMEOUT,
411
+ )
389
412
 
390
413
  def _on_data_response(self, q: Queue[bytes], bs: bytearray):
391
414
  bs = bytes(bs)
@@ -537,6 +560,7 @@ class GForce:
537
560
  num_channels = 6
538
561
 
539
562
  return emg_gesture_data.reshape(-1, num_channels)
563
+
540
564
  def _on_universal_response(self, _: BleakGATTCharacteristic, bs: bytearray):
541
565
  self._raw_data_buf.put_nowait(bytes(bs))
542
566
 
@@ -675,7 +699,6 @@ class GForce:
675
699
  )
676
700
  )
677
701
 
678
-
679
702
  async def system_reset(self):
680
703
  await self._send_request(
681
704
  Request(
@@ -684,11 +707,8 @@ class GForce:
684
707
  )
685
708
  )
686
709
 
687
-
688
710
  async def set_motor(self, switchStatus):
689
- body = [
690
- switchStatus == True
691
- ]
711
+ body = [switchStatus == True]
692
712
  body = bytes(body)
693
713
  ret = await self._send_request(
694
714
  Request(
@@ -699,9 +719,7 @@ class GForce:
699
719
  )
700
720
 
701
721
  async def set_led(self, switchStatus):
702
- body = [
703
- switchStatus == True
704
- ]
722
+ body = [switchStatus == True]
705
723
  body = bytes(body)
706
724
  ret = await self._send_request(
707
725
  Request(
@@ -712,9 +730,7 @@ class GForce:
712
730
  )
713
731
 
714
732
  async def set_log_level(self, logLevel):
715
- body = [
716
- 0xFF & logLevel
717
- ]
733
+ body = [0xFF & logLevel]
718
734
  body = bytes(body)
719
735
  ret = await self._send_request(
720
736
  Request(
@@ -724,7 +740,6 @@ class GForce:
724
740
  )
725
741
  )
726
742
 
727
-
728
743
  async def set_emg_raw_data_config(self, cfg=EmgRawDataConfig()):
729
744
  body = cfg.to_bytes()
730
745
  await self._send_request(
@@ -757,7 +772,7 @@ class GForce:
757
772
  )
758
773
  )
759
774
  return EmgRawDataConfig.from_bytes(buf)
760
-
775
+
761
776
  async def get_eeg_raw_data_config(self) -> EegRawDataConfig:
762
777
  buf = await self._send_request(
763
778
  Request(
@@ -766,7 +781,7 @@ class GForce:
766
781
  )
767
782
  )
768
783
  return EegRawDataConfig.from_bytes(buf)
769
-
784
+
770
785
  async def get_eeg_raw_data_cap(self) -> EegRawDataCap:
771
786
  buf = await self._send_request(
772
787
  Request(
@@ -784,7 +799,7 @@ class GForce:
784
799
  )
785
800
  )
786
801
  return EcgRawDataConfig.from_bytes(buf)
787
-
802
+
788
803
  async def get_imu_raw_data_config(self) -> ImuRawDataConfig:
789
804
  buf = await self._send_request(
790
805
  Request(
@@ -792,8 +807,8 @@ class GForce:
792
807
  has_res=True,
793
808
  )
794
809
  )
795
- return ImuRawDataConfig.from_bytes(buf)
796
-
810
+ return ImuRawDataConfig.from_bytes(buf)
811
+
797
812
  async def get_brth_raw_data_config(self) -> BrthRawDataConfig:
798
813
  buf = await self._send_request(
799
814
  Request(
@@ -801,8 +816,8 @@ class GForce:
801
816
  has_res=True,
802
817
  )
803
818
  )
804
- return BrthRawDataConfig.from_bytes(buf)
805
-
819
+ return BrthRawDataConfig.from_bytes(buf)
820
+
806
821
  async def set_subscription(self, subscription: DataSubscription):
807
822
  body = [
808
823
  0xFF & subscription,
@@ -819,20 +834,27 @@ class GForce:
819
834
  )
820
835
  )
821
836
 
822
- async def start_streaming(self, q:queue.Queue):
823
- await self.client.start_notify(
824
- self.data_char,
825
- lambda _, data: self._on_data_response(q, data),
837
+ async def start_streaming(self, q: queue.Queue):
838
+ await asyncio.wait_for(
839
+ self.client.start_notify(
840
+ self.data_char,
841
+ lambda _, data: self._on_data_response(q, data),
842
+ ),
843
+ utils._TIMEOUT,
826
844
  )
827
845
 
828
846
  async def stop_streaming(self):
829
847
  exceptions = []
830
848
  try:
831
- await self.set_subscription(DataSubscription.OFF)
849
+ await asyncio.wait_for(
850
+ self.set_subscription(DataSubscription.OFF), utils._TIMEOUT
851
+ )
832
852
  except Exception as e:
833
853
  exceptions.append(e)
834
854
  try:
835
- await self.client.stop_notify(self.data_char)
855
+ await asyncio.wait_for(
856
+ self.client.stop_notify(self.data_char), utils._TIMEOUT
857
+ )
836
858
  except Exception as e:
837
859
  exceptions.append(e)
838
860
 
@@ -841,7 +863,10 @@ class GForce:
841
863
 
842
864
  async def disconnect(self):
843
865
  with suppress(asyncio.CancelledError):
844
- await self.client.disconnect()
866
+ try:
867
+ await asyncio.wait_for(self.client.disconnect(), utils._TIMEOUT)
868
+ except Exception as e:
869
+ pass
845
870
 
846
871
  def _get_response_channel(self, cmd: Command) -> Queue:
847
872
  q = Queue()
@@ -856,9 +881,11 @@ class GForce:
856
881
  bs = bytes([req.cmd])
857
882
  if req.body is not None:
858
883
  bs += req.body
859
- await self.client.write_gatt_char(self.cmd_char, bs)
884
+ await asyncio.wait_for(
885
+ self.client.write_gatt_char(self.cmd_char, bs), utils._TIMEOUT
886
+ )
860
887
 
861
888
  if not req.has_res:
862
889
  return None
863
890
 
864
- return await asyncio.wait_for(q.get(), 3)
891
+ return await asyncio.wait_for(q.get(), utils._TIMEOUT)
@@ -5,10 +5,11 @@ from typing import Callable, Dict, List, Optional, Tuple
5
5
  import bleak
6
6
 
7
7
  from sensor import sensor_profile
8
+ from sensor import utils
8
9
  from sensor.sensor_profile import DeviceStateEx, SensorProfile
9
10
  import asyncio
10
11
 
11
- from sensor.utils import start_loop, sync_timer, timer
12
+ from sensor.utils import async_call, start_loop, sync_call, async_exec, timer
12
13
  from bleak import (
13
14
  BleakScanner,
14
15
  AdvertisementData,
@@ -37,16 +38,12 @@ class SensorController:
37
38
  初始化 SensorController 实例。
38
39
  """
39
40
  self._event_loop = asyncio.new_event_loop()
40
- self._event_thread = threading.Thread(
41
- target=start_loop, args=(self._event_loop,)
42
- )
41
+ self._event_thread = threading.Thread(target=start_loop, args=(self._event_loop,))
43
42
  self._event_thread.daemon = True
44
43
  self._event_thread.name = "SensorController event"
45
44
  self._event_thread.start()
46
45
  self._gforce_event_loop = asyncio.new_event_loop()
47
- self._gforce_event_thread = threading.Thread(
48
- target=start_loop, args=(self._gforce_event_loop,)
49
- )
46
+ self._gforce_event_thread = threading.Thread(target=start_loop, args=(self._gforce_event_loop,))
50
47
  self._gforce_event_thread.daemon = True
51
48
  self._gforce_event_thread.name = "BLE operation"
52
49
  self._gforce_event_thread.start()
@@ -69,11 +66,9 @@ class SensorController:
69
66
  self._gforce_event_loop.close()
70
67
 
71
68
  def terminate(self):
69
+ utils._terminated = True
72
70
  for sensor in self._sensor_profiles.values():
73
- if (
74
- sensor.deviceState == DeviceStateEx.Connected
75
- or sensor.deviceState == DeviceStateEx.Ready
76
- ):
71
+ if sensor.deviceState == DeviceStateEx.Connected or sensor.deviceState == DeviceStateEx.Ready:
77
72
  sensor._destroy()
78
73
 
79
74
  def _match_device(self, _device: bleak.BLEDevice, adv: AdvertisementData):
@@ -123,9 +118,7 @@ class SensorController:
123
118
  return self._device_callback != None
124
119
 
125
120
  @hasDeviceFoundCallback.setter
126
- def onDeviceFoundCallback(
127
- self, callback: Callable[[List[sensor_profile.BLEDevice]], None]
128
- ):
121
+ def onDeviceFoundCallback(self, callback: Callable[[List[sensor_profile.BLEDevice]], None]):
129
122
  """
130
123
  设置扫描设备回调。
131
124
 
@@ -133,33 +126,77 @@ class SensorController:
133
126
  """
134
127
  self._device_callback = callback
135
128
 
136
- async def _device_scan_callback(
129
+ def _process_ble_devices(
137
130
  self, found_devices: Dict[str, Tuple[bleak.BLEDevice, AdvertisementData]]
138
- ):
131
+ ) -> List[sensor_profile.BLEDevice]:
132
+ devices: List[sensor_profile.BLEDevice] = list()
133
+ deviceMap: Dict[str, SensorProfile] = self._sensor_profiles.copy()
134
+ for uuid in found_devices:
135
+ device = found_devices[uuid][0]
136
+ if device.name == None:
137
+ continue
138
+ adv = found_devices[uuid][1]
139
+ if SERVICE_GUID in adv.service_uuids:
140
+ mac = None
141
+ if adv.service_data.get(SERVICE_GUID) != None:
142
+ bytes_val = adv.service_data[SERVICE_GUID]
143
+ mac = ":".join(f"{byte:02X}" for byte in bytes_val)
144
+ elif adv.service_data.get(RFSTAR_SERVICE_GUID) != None:
145
+ bytes_val = adv.service_data[RFSTAR_SERVICE_GUID]
146
+ mac = ":".join(f"{byte:02X}" for byte in reversed(bytes_val))
147
+
148
+ if mac == None:
149
+ continue
150
+ if deviceMap.get(mac) != None:
151
+ devices.append(self._sensor_profiles[mac].BLEDevice)
152
+ else:
153
+ newSensor = SensorProfile(device, adv, mac, self._gforce_event_loop)
154
+ deviceMap[mac] = newSensor
155
+ devices.append(newSensor.BLEDevice)
156
+
157
+ self._sensor_profiles = deviceMap
158
+ return devices
159
+
160
+ async def _async_scan(self, period):
161
+ self._is_scanning = True
162
+ found_devices = await self._scanner.discover(timeout=period / 1000, return_adv=True)
163
+ self._is_scanning = False
164
+ return self._process_ble_devices(found_devices)
165
+
166
+ def scan(self, period) -> List[sensor_profile.BLEDevice]:
167
+ """
168
+ 扫描一段时间后返回BLEDevice列表。
169
+
170
+ :param periodInMs (int): 扫描时长(毫秒)
171
+
172
+ :return: List[sensor_profile.BLEDevice]: BLEDevice列表
173
+ """
174
+ return sync_call(self._gforce_event_loop, self._async_scan(period))
175
+
176
+ async def asyncScan(self, period) -> List[sensor_profile.BLEDevice]:
177
+ """
178
+ 扫描一段时间后返回BLEDevice列表。
179
+
180
+ :param periodInMs (int): 扫描时长(毫秒)
181
+
182
+ :return: List[sensor_profile.BLEDevice]: BLEDevice列表
183
+ """
184
+ return await async_call(self._gforce_event_loop, self._async_scan(period))
185
+
186
+ async def _device_scan_callback(self, devices: List[sensor_profile.BLEDevice]):
139
187
  if self._device_callback:
140
- devices: List[sensor_profile.BLEDevice] = list()
141
- deviceMap: Dict[str, SensorProfile] = self._sensor_profiles.copy()
142
- for mac in found_devices:
143
- device = found_devices[mac][0]
144
- adv = found_devices[mac][1]
145
- if SERVICE_GUID in adv.service_uuids:
146
- if deviceMap.get(mac) != None:
147
- devices.append(self._sensor_profiles[mac].BLEDevice)
148
- else:
149
- newSensor = SensorProfile(device, adv, self._gforce_event_loop)
150
- deviceMap[mac] = newSensor
151
- devices.append(newSensor.BLEDevice)
152
- self._sensor_profiles = deviceMap
153
- self._device_callback(devices)
188
+ try:
189
+ self._device_callback(devices)
190
+ except Exception as e:
191
+ print(e)
154
192
 
155
193
  if self._is_scanning:
156
- timer(self._gforce_event_loop, 0, self._startScan())
194
+ async_exec(self._gforce_event_loop, self._startScan())
157
195
 
158
196
  async def _startScan(self) -> bool:
159
- devices = await self._scanner.discover(
160
- timeout=self._device_callback_period / 1000, return_adv=True
161
- )
162
- timer(self._event_loop, 0, self._device_scan_callback(devices))
197
+ found_devices = await self._scanner.discover(timeout=self._device_callback_period / 1000, return_adv=True)
198
+ devices = self._process_ble_devices(found_devices)
199
+ async_exec(self._event_loop, self._device_scan_callback(devices))
163
200
 
164
201
  def startScan(self, periodInMs: int) -> bool:
165
202
  """
@@ -175,7 +212,7 @@ class SensorController:
175
212
  self._is_scanning = True
176
213
  self._device_callback_period = periodInMs
177
214
 
178
- timer(self._gforce_event_loop, 0, self._startScan())
215
+ async_exec(self._gforce_event_loop, self._startScan())
179
216
  return True
180
217
 
181
218
  def stopScan(self) -> None:
@@ -187,9 +224,7 @@ class SensorController:
187
224
 
188
225
  self._is_scanning = False
189
226
 
190
- def requireSensor(
191
- self, device: sensor_profile.BLEDevice
192
- ) -> Optional[SensorProfile]:
227
+ def requireSensor(self, device: sensor_profile.BLEDevice) -> Optional[SensorProfile]:
193
228
  """
194
229
  根据设备信息获取或创建SensorProfile。
195
230
 
@@ -221,10 +256,7 @@ class SensorController:
221
256
  """
222
257
  sensors: List[SensorProfile] = list()
223
258
  for sensor in self._sensor_profiles.values():
224
- if (
225
- sensor.deviceState == DeviceStateEx.Connected
226
- or sensor.deviceState == DeviceStateEx.Ready
227
- ):
259
+ if sensor.deviceState == DeviceStateEx.Connected or sensor.deviceState == DeviceStateEx.Ready:
228
260
  sensors.append(sensor)
229
261
 
230
262
  return sensors
@@ -237,10 +269,7 @@ class SensorController:
237
269
  """
238
270
  devices: List[sensor_profile.BLEDevice] = list()
239
271
  for sensor in self._sensor_profiles.values():
240
- if (
241
- sensor.deviceState == DeviceStateEx.Connected
242
- or sensor.deviceState == DeviceStateEx.Ready
243
- ):
272
+ if sensor.deviceState == DeviceStateEx.Connected or sensor.deviceState == DeviceStateEx.Ready:
244
273
  devices.append(sensor.BLEDevice)
245
274
 
246
275
  return devices