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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sensor-sdk
3
- Version: 0.0.18
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
@@ -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 in self.responses:
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
- await asyncio.wait_for(self.client.write_gatt_char(self.cmd_char, bs), sensor_utils._TIMEOUT)
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
- return await asyncio.wait_for(q.get(), sensor_utils._TIMEOUT)
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
- asyncio.get_event_loop().run_in_executor(None, self.gForce._on_cmd_response, None, data_package)
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 _connect(self) -> bool:
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(1)
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
- await self._gforce.disconnect()
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
- self._is_starting = True
368
- return sync_call(self._startDataNotification())
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
- self._is_starting = True
381
- return await async_call(self._startDataNotification())
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
- self._is_starting = True
409
- return sync_call(self._stopDataNotification())
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
- self._is_starting = True
422
- return await async_call(self._stopDataNotification())
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, _runloop)
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), _runloop)
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), _runloop)
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sensor-sdk
3
- Version: 0.0.18
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
@@ -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.18",
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