sensor-sdk 0.0.8__tar.gz → 0.0.10__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.

Potentially problematic release.


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

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sensor-sdk
3
- Version: 0.0.8
3
+ Version: 0.0.10
4
4
  Summary: Python sdk for Synchroni
5
5
  Home-page: https://github.com/oymotion/SynchroniSDKPython
6
6
  Author: Martin Ye
@@ -397,16 +397,20 @@ class GForce:
397
397
 
398
398
  if not client.is_connected:
399
399
  return
400
- if not self._is_universal_stream:
401
- await asyncio.wait_for(
402
- client.start_notify(self.cmd_char, self._on_cmd_response),
403
- utils._TIMEOUT,
404
- )
405
- else:
406
- await asyncio.wait_for(
407
- client.start_notify(self.data_char, self._on_universal_response),
408
- utils._TIMEOUT,
409
- )
400
+
401
+ try:
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
+ )
407
+ else:
408
+ await asyncio.wait_for(
409
+ client.start_notify(self.data_char, self._on_universal_response),
410
+ utils._TIMEOUT,
411
+ )
412
+ except Exception as e:
413
+ return
410
414
 
411
415
  def _on_data_response(self, q: Queue[bytes], bs: bytearray):
412
416
  bs = bytes(bs)
@@ -875,4 +879,7 @@ class GForce:
875
879
  if not req.has_res:
876
880
  return None
877
881
 
878
- return await asyncio.wait_for(q.get(), utils._TIMEOUT)
882
+ try:
883
+ return await asyncio.wait_for(q.get(), utils._TIMEOUT)
884
+ except Exception as e:
885
+ return None
@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  from concurrent.futures import ThreadPoolExecutor
2
3
  import threading
3
4
  from typing import Callable, Dict, List, Optional, Tuple
@@ -7,9 +8,8 @@ import bleak
7
8
  from sensor import sensor_profile
8
9
  from sensor import utils
9
10
  from sensor.sensor_profile import DeviceStateEx, SensorProfile
10
- import asyncio
11
11
 
12
- from sensor.utils import async_call, start_loop, sync_call, async_exec, timer
12
+ from sensor.utils import async_call, sync_call, async_exec
13
13
  from bleak import (
14
14
  BleakScanner,
15
15
  AdvertisementData,
@@ -27,6 +27,7 @@ class SensorController:
27
27
  with SensorController._instance_lock:
28
28
  if not hasattr(SensorController, "_instance"):
29
29
  SensorController._instance = object.__new__(cls)
30
+
30
31
  return SensorController._instance
31
32
 
32
33
  """
@@ -37,21 +38,8 @@ class SensorController:
37
38
  """
38
39
  初始化 SensorController 实例。
39
40
  """
40
- self._event_loop = asyncio.new_event_loop()
41
- self._event_thread = threading.Thread(target=start_loop, args=(self._event_loop,))
42
- self._event_thread.daemon = True
43
- self._event_thread.name = "SensorController event"
44
- self._event_thread.start()
45
- self._gforce_event_loop = asyncio.new_event_loop()
46
- self._gforce_event_thread = threading.Thread(target=start_loop, args=(self._gforce_event_loop,))
47
- self._gforce_event_thread.daemon = True
48
- self._gforce_event_thread.name = "BLE operation"
49
- self._gforce_event_thread.start()
50
- self._scanner = BleakScanner(
51
- detection_callback=self._match_device,
52
- service_uuids=[SERVICE_GUID, RFSTAR_SERVICE_GUID],
53
- )
54
41
  self._is_scanning = False
42
+ self._scanner: BleakScanner = None
55
43
  self._device_callback: Callable[[List[sensor_profile.BLEDevice]], None] = None
56
44
  self._device_callback_period = 0
57
45
  self._enable_callback: Callable[[bool], None] = None
@@ -62,15 +50,16 @@ class SensorController:
62
50
  反初始化 SensorController 类的实例。
63
51
 
64
52
  """
65
- self._event_loop.close()
66
- self._gforce_event_loop.close()
67
53
 
68
54
  def terminate(self):
69
55
  utils._terminated = True
56
+
70
57
  for sensor in self._sensor_profiles.values():
71
58
  if sensor.deviceState == DeviceStateEx.Connected or sensor.deviceState == DeviceStateEx.Ready:
72
59
  sensor._destroy()
73
60
 
61
+ utils.Terminate()
62
+
74
63
  def _match_device(self, _device: bleak.BLEDevice, adv: AdvertisementData):
75
64
  if _device.name == None:
76
65
  return False
@@ -150,15 +139,23 @@ class SensorController:
150
139
  if deviceMap.get(mac) != None:
151
140
  devices.append(self._sensor_profiles[mac].BLEDevice)
152
141
  else:
153
- newSensor = SensorProfile(device, adv, mac, self._gforce_event_loop)
142
+ newSensor = SensorProfile(device, adv, mac)
154
143
  deviceMap[mac] = newSensor
155
144
  devices.append(newSensor.BLEDevice)
156
145
 
157
146
  self._sensor_profiles = deviceMap
158
147
  return devices
159
148
 
149
+ def _init_scan(self):
150
+ if self._scanner == None:
151
+ self._scanner = BleakScanner(
152
+ detection_callback=self._match_device,
153
+ service_uuids=[SERVICE_GUID, RFSTAR_SERVICE_GUID],
154
+ )
155
+
160
156
  async def _async_scan(self, period):
161
157
  self._is_scanning = True
158
+ self._init_scan()
162
159
  found_devices = await self._scanner.discover(timeout=period / 1000, return_adv=True)
163
160
  self._is_scanning = False
164
161
  return self._process_ble_devices(found_devices)
@@ -171,7 +168,7 @@ class SensorController:
171
168
 
172
169
  :return: List[sensor_profile.BLEDevice]: BLEDevice列表
173
170
  """
174
- return sync_call(self._gforce_event_loop, self._async_scan(period))
171
+ return sync_call(self._async_scan(period))
175
172
 
176
173
  async def asyncScan(self, period) -> List[sensor_profile.BLEDevice]:
177
174
  """
@@ -181,22 +178,23 @@ class SensorController:
181
178
 
182
179
  :return: List[sensor_profile.BLEDevice]: BLEDevice列表
183
180
  """
184
- return await async_call(self._gforce_event_loop, self._async_scan(period))
181
+ return await async_call(self._async_scan(period))
185
182
 
186
183
  async def _device_scan_callback(self, devices: List[sensor_profile.BLEDevice]):
187
184
  if self._device_callback:
188
185
  try:
189
- self._device_callback(devices)
186
+ asyncio.get_event_loop().run_in_executor(None, self._device_callback, devices)
190
187
  except Exception as e:
191
188
  print(e)
192
189
 
193
190
  if self._is_scanning:
194
- async_exec(self._gforce_event_loop, self._startScan())
191
+ async_exec(self._startScan())
195
192
 
196
193
  async def _startScan(self) -> bool:
194
+ self._init_scan()
197
195
  found_devices = await self._scanner.discover(timeout=self._device_callback_period / 1000, return_adv=True)
198
196
  devices = self._process_ble_devices(found_devices)
199
- async_exec(self._event_loop, self._device_scan_callback(devices))
197
+ async_exec(self._device_scan_callback(devices))
200
198
 
201
199
  def startScan(self, periodInMs: int) -> bool:
202
200
  """
@@ -212,7 +210,7 @@ class SensorController:
212
210
  self._is_scanning = True
213
211
  self._device_callback_period = periodInMs
214
212
 
215
- async_exec(self._gforce_event_loop, self._startScan())
213
+ async_exec(self._startScan())
216
214
  return True
217
215
 
218
216
  def stopScan(self) -> None:
@@ -5,6 +5,7 @@ from queue import Queue
5
5
  import struct
6
6
  from typing import Deque, List
7
7
 
8
+ from sensor import utils
8
9
  from sensor.gforce import DataSubscription, GForce
9
10
  from sensor.sensor_data import DataType, Sample, SensorData
10
11
 
@@ -284,14 +285,32 @@ class SensorProfileDataCtx:
284
285
  await self.gForce.set_subscription(0)
285
286
  return True
286
287
 
287
- def process_data(self, buf: Queue[SensorData], sensor):
288
- try:
289
- data: bytes = self._rawDataBuffer.get_nowait()
290
- except Exception as e:
291
- return
288
+ async def process_data(self, buf: Queue[SensorData], sensor, callback):
289
+ while self._is_running and self._rawDataBuffer.empty():
290
+ await asyncio.sleep(0.01)
292
291
 
293
- self._processDataPackage(data, buf, sensor)
294
- self._rawDataBuffer.task_done()
292
+ while self._is_running and not self._rawDataBuffer.empty():
293
+ try:
294
+ data: bytes = self._rawDataBuffer.get_nowait()
295
+ except Exception as e:
296
+ continue
297
+
298
+ self._processDataPackage(data, buf, sensor)
299
+ self._rawDataBuffer.task_done()
300
+
301
+ while self._is_running and self.isDataTransfering and not buf.empty():
302
+ sensorData: SensorData = None
303
+ try:
304
+ sensorData = buf.get_nowait()
305
+ except Exception as e:
306
+ break
307
+ if sensorData != None and callback != None:
308
+ try:
309
+ asyncio.get_event_loop().run_in_executor(None, callback, sensor, sensorData)
310
+ except Exception as e:
311
+ print(e)
312
+
313
+ buf.task_done()
295
314
 
296
315
  def _processDataPackage(self, data: bytes, buf: Queue[SensorData], sensor):
297
316
  v = data[0]
@@ -370,7 +389,7 @@ class SensorProfileDataCtx:
370
389
  # print(lostLog)
371
390
  if sensor._event_loop != None and sensor._on_error_callback != None:
372
391
  try:
373
- sensor._event_loop.call_soon_threadsafe(sensor._on_error_callback, sensor, lostLog)
392
+ asyncio.get_event_loop().run_in_executor(None, sensor._on_error_callback, sensor, lostLog)
374
393
  except Exception as e:
375
394
  pass
376
395
 
@@ -522,277 +541,7 @@ class SensorProfileDataCtx:
522
541
  for sensorDataResult in sensorDataList:
523
542
  buf.put(sensorDataResult)
524
543
 
525
- def calc_crc8(self, data):
526
- crc8Table = [
527
- 0x00,
528
- 0x07,
529
- 0x0E,
530
- 0x09,
531
- 0x1C,
532
- 0x1B,
533
- 0x12,
534
- 0x15,
535
- 0x38,
536
- 0x3F,
537
- 0x36,
538
- 0x31,
539
- 0x24,
540
- 0x23,
541
- 0x2A,
542
- 0x2D,
543
- 0x70,
544
- 0x77,
545
- 0x7E,
546
- 0x79,
547
- 0x6C,
548
- 0x6B,
549
- 0x62,
550
- 0x65,
551
- 0x48,
552
- 0x4F,
553
- 0x46,
554
- 0x41,
555
- 0x54,
556
- 0x53,
557
- 0x5A,
558
- 0x5D,
559
- 0xE0,
560
- 0xE7,
561
- 0xEE,
562
- 0xE9,
563
- 0xFC,
564
- 0xFB,
565
- 0xF2,
566
- 0xF5,
567
- 0xD8,
568
- 0xDF,
569
- 0xD6,
570
- 0xD1,
571
- 0xC4,
572
- 0xC3,
573
- 0xCA,
574
- 0xCD,
575
- 0x90,
576
- 0x97,
577
- 0x9E,
578
- 0x99,
579
- 0x8C,
580
- 0x8B,
581
- 0x82,
582
- 0x85,
583
- 0xA8,
584
- 0xAF,
585
- 0xA6,
586
- 0xA1,
587
- 0xB4,
588
- 0xB3,
589
- 0xBA,
590
- 0xBD,
591
- 0xC7,
592
- 0xC0,
593
- 0xC9,
594
- 0xCE,
595
- 0xDB,
596
- 0xDC,
597
- 0xD5,
598
- 0xD2,
599
- 0xFF,
600
- 0xF8,
601
- 0xF1,
602
- 0xF6,
603
- 0xE3,
604
- 0xE4,
605
- 0xED,
606
- 0xEA,
607
- 0xB7,
608
- 0xB0,
609
- 0xB9,
610
- 0xBE,
611
- 0xAB,
612
- 0xAC,
613
- 0xA5,
614
- 0xA2,
615
- 0x8F,
616
- 0x88,
617
- 0x81,
618
- 0x86,
619
- 0x93,
620
- 0x94,
621
- 0x9D,
622
- 0x9A,
623
- 0x27,
624
- 0x20,
625
- 0x29,
626
- 0x2E,
627
- 0x3B,
628
- 0x3C,
629
- 0x35,
630
- 0x32,
631
- 0x1F,
632
- 0x18,
633
- 0x11,
634
- 0x16,
635
- 0x03,
636
- 0x04,
637
- 0x0D,
638
- 0x0A,
639
- 0x57,
640
- 0x50,
641
- 0x59,
642
- 0x5E,
643
- 0x4B,
644
- 0x4C,
645
- 0x45,
646
- 0x42,
647
- 0x6F,
648
- 0x68,
649
- 0x61,
650
- 0x66,
651
- 0x73,
652
- 0x74,
653
- 0x7D,
654
- 0x7A,
655
- 0x89,
656
- 0x8E,
657
- 0x87,
658
- 0x80,
659
- 0x95,
660
- 0x92,
661
- 0x9B,
662
- 0x9C,
663
- 0xB1,
664
- 0xB6,
665
- 0xBF,
666
- 0xB8,
667
- 0xAD,
668
- 0xAA,
669
- 0xA3,
670
- 0xA4,
671
- 0xF9,
672
- 0xFE,
673
- 0xF7,
674
- 0xF0,
675
- 0xE5,
676
- 0xE2,
677
- 0xEB,
678
- 0xEC,
679
- 0xC1,
680
- 0xC6,
681
- 0xCF,
682
- 0xC8,
683
- 0xDD,
684
- 0xDA,
685
- 0xD3,
686
- 0xD4,
687
- 0x69,
688
- 0x6E,
689
- 0x67,
690
- 0x60,
691
- 0x75,
692
- 0x72,
693
- 0x7B,
694
- 0x7C,
695
- 0x51,
696
- 0x56,
697
- 0x5F,
698
- 0x58,
699
- 0x4D,
700
- 0x4A,
701
- 0x43,
702
- 0x44,
703
- 0x19,
704
- 0x1E,
705
- 0x17,
706
- 0x10,
707
- 0x05,
708
- 0x02,
709
- 0x0B,
710
- 0x0C,
711
- 0x21,
712
- 0x26,
713
- 0x2F,
714
- 0x28,
715
- 0x3D,
716
- 0x3A,
717
- 0x33,
718
- 0x34,
719
- 0x4E,
720
- 0x49,
721
- 0x40,
722
- 0x47,
723
- 0x52,
724
- 0x55,
725
- 0x5C,
726
- 0x5B,
727
- 0x76,
728
- 0x71,
729
- 0x78,
730
- 0x7F,
731
- 0x6A,
732
- 0x6D,
733
- 0x64,
734
- 0x63,
735
- 0x3E,
736
- 0x39,
737
- 0x30,
738
- 0x37,
739
- 0x22,
740
- 0x25,
741
- 0x2C,
742
- 0x2B,
743
- 0x06,
744
- 0x01,
745
- 0x08,
746
- 0x0F,
747
- 0x1A,
748
- 0x1D,
749
- 0x14,
750
- 0x13,
751
- 0xAE,
752
- 0xA9,
753
- 0xA0,
754
- 0xA7,
755
- 0xB2,
756
- 0xB5,
757
- 0xBC,
758
- 0xBB,
759
- 0x96,
760
- 0x91,
761
- 0x98,
762
- 0x9F,
763
- 0x8A,
764
- 0x8D,
765
- 0x84,
766
- 0x83,
767
- 0xDE,
768
- 0xD9,
769
- 0xD0,
770
- 0xD7,
771
- 0xC2,
772
- 0xC5,
773
- 0xCC,
774
- 0xCB,
775
- 0xE6,
776
- 0xE1,
777
- 0xE8,
778
- 0xEF,
779
- 0xFA,
780
- 0xFD,
781
- 0xF4,
782
- 0xF3,
783
- ]
784
- crc8 = 0
785
- len_data = len(data)
786
-
787
- for i in range(len_data):
788
- crc8 ^= data[i]
789
- crc8 = crc8Table[crc8]
790
-
791
- return crc8
792
-
793
- async def processUniversalData(
794
- self, buf: Queue[SensorData], event_loop: asyncio.AbstractEventLoop, cmd_loop: asyncio.AbstractEventLoop, sensor, callback
795
- ):
544
+ async def processUniversalData(self, buf: Queue[SensorData], sensor, callback):
796
545
 
797
546
  while self._is_running:
798
547
  while self._is_running and self._rawDataBuffer.empty():
@@ -824,7 +573,7 @@ class SensorProfileDataCtx:
824
573
  index += 1
825
574
  continue
826
575
  crc = self._concatDataBuffer[index + 1 + n + 1]
827
- calc_crc = self.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
576
+ calc_crc = utils.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
828
577
  if crc != calc_crc:
829
578
  index += 1
830
579
  continue
@@ -837,9 +586,9 @@ class SensorProfileDataCtx:
837
586
  sensorData = buf.get_nowait()
838
587
  except Exception as e:
839
588
  break
840
- if event_loop != None and sensorData != None and callback != None:
589
+ if sensorData != None and callback != None:
841
590
  try:
842
- event_loop.call_soon_threadsafe(callback, sensor, sensorData)
591
+ asyncio.get_event_loop().run_in_executor(None, callback, sensor, sensorData)
843
592
  except Exception as e:
844
593
  print(e)
845
594
 
@@ -855,13 +604,12 @@ class SensorProfileDataCtx:
855
604
  index += 1
856
605
  continue
857
606
  crc = self._concatDataBuffer[index + 1 + n + 1]
858
- calc_crc = self.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
607
+ calc_crc = utils.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
859
608
  if crc != calc_crc:
860
609
  index += 1
861
610
  continue
862
611
  data_package = bytes(self._concatDataBuffer[index + 2 : index + 2 + n])
863
- if cmd_loop != None:
864
- cmd_loop.call_soon_threadsafe(self.gForce._on_cmd_response, None, data_package)
612
+ asyncio.get_event_loop().run_in_executor(None, self.gForce._on_cmd_response, None, data_package)
865
613
  last_cut = index = index + 2 + n
866
614
 
867
615
  else:
@@ -2,28 +2,23 @@
2
2
  # 该枚举类定义了设备的各种状态,用于表示设备在不同操作阶段的状态信息
3
3
  from enum import Enum, IntEnum
4
4
  from queue import Queue
5
- import struct
6
5
  import time
7
- from typing import Callable, Dict, List, Optional
6
+ from typing import Callable, Optional
8
7
 
9
8
  import bleak
10
9
  from bleak import (
11
10
  BleakClient,
12
- BleakGATTCharacteristic,
13
11
  )
14
12
 
15
- import sensor
16
13
  from sensor import utils
17
14
  from sensor.gforce import GForce
18
- from sensor.sensor_data import DataType, Sample, SensorData
15
+ from sensor.sensor_data import SensorData
19
16
  import asyncio
20
- import threading
17
+
21
18
 
22
19
  from sensor.sensor_data_context import SensorProfileDataCtx
23
20
  from sensor.sensor_device import BLEDevice, DeviceInfo, DeviceStateEx
24
- from sensor.utils import async_call, start_loop, sync_call, async_exec, timer
25
- from contextlib import suppress
26
- from dataclasses import dataclass
21
+ from sensor.utils import async_call, sync_call, async_exec, timer
27
22
 
28
23
  SERVICE_GUID = "0000ffd0-0000-1000-8000-00805f9b34fb"
29
24
  OYM_CMD_NOTIFY_CHAR_UUID = "f000ffe1-0451-4000-b000-000000000000"
@@ -46,7 +41,6 @@ class SensorProfile:
46
41
  device: bleak.BLEDevice,
47
42
  adv: bleak.AdvertisementData,
48
43
  mac: str,
49
- gforce_event_loop: asyncio.AbstractEventLoop,
50
44
  ):
51
45
  """
52
46
  初始化 SensorProfile 类的实例。
@@ -66,9 +60,7 @@ class SensorProfile:
66
60
  self._data_ctx: SensorProfileDataCtx = None
67
61
  self._gforce: GForce = None
68
62
  self._data_event_loop: asyncio.AbstractEventLoop = None
69
- self._gforce_event_loop: asyncio.AbstractEventLoop = gforce_event_loop
70
63
  self._event_loop: asyncio.AbstractEventLoop = None
71
- self._event_thread = None
72
64
 
73
65
  def __del__(self) -> None:
74
66
  """
@@ -85,7 +77,6 @@ class SensorProfile:
85
77
  self._data_event_loop.stop()
86
78
  self._data_event_loop.close()
87
79
  self._data_event_loop = None
88
- self._data_event_thread.join()
89
80
  except Exception as e:
90
81
  pass
91
82
  if self._event_loop != None:
@@ -93,7 +84,6 @@ class SensorProfile:
93
84
  self._event_loop.stop()
94
85
  self._event_loop.close()
95
86
  self._event_loop = None
96
- self._event_thread.join()
97
87
  except Exception as e:
98
88
  pass
99
89
 
@@ -111,7 +101,7 @@ class SensorProfile:
111
101
  self._device_state = newState
112
102
  if self._event_loop != None and self._on_state_changed != None:
113
103
  try:
114
- self._event_loop.call_soon_threadsafe(self._on_state_changed, self, newState)
104
+ asyncio.get_event_loop().run_in_executor(None, self._on_state_changed, self, newState)
115
105
  except Exception as e:
116
106
  print(e)
117
107
  pass
@@ -220,12 +210,11 @@ class SensorProfile:
220
210
  self._on_power_changed = callback
221
211
 
222
212
  async def _connect(self) -> bool:
223
- if self._event_thread == None:
213
+ if utils._terminated:
214
+ return False
215
+
216
+ if self._event_loop == None:
224
217
  self._event_loop = asyncio.new_event_loop()
225
- self._event_thread = threading.Thread(target=start_loop, args=(self._event_loop,))
226
- self._event_thread.daemon = True
227
- self._event_thread.name = self._device.Name + " event"
228
- self._event_thread.start()
229
218
  self._data_buffer: Queue[SensorData] = Queue()
230
219
  self._raw_data_buf: Queue[bytes] = Queue()
231
220
 
@@ -242,10 +231,6 @@ class SensorProfile:
242
231
  # print("RFSTAR_SERVICE:" + self._detail_device.name)
243
232
  self._gforce = GForce(self._detail_device, RFSTAR_CMD_UUID, RFSTAR_DATA_UUID, True)
244
233
  self._data_event_loop = asyncio.new_event_loop()
245
- self._data_event_thread = threading.Thread(target=start_loop, args=(self._data_event_loop,))
246
- self._data_event_thread.daemon = True
247
- self._data_event_thread.name = self._detail_device.name + " data"
248
- self._data_event_thread.start()
249
234
  else:
250
235
  print("Invalid device service uuid:" + self._detail_device.name + str(self._adv))
251
236
  return False
@@ -253,24 +238,25 @@ class SensorProfile:
253
238
  if self._data_ctx == None and self._gforce != None:
254
239
  self._data_ctx = SensorProfileDataCtx(self._gforce, self._device.Address, self._raw_data_buf)
255
240
  if self._data_ctx.isUniversalStream:
256
- async_exec(self._data_event_loop, self._process_universal_data())
241
+ async_exec(self._process_universal_data())
257
242
 
258
243
  if self.deviceState == DeviceStateEx.Connected or self.deviceState == DeviceStateEx.Ready:
259
244
  return True
260
245
  self._set_device_state(DeviceStateEx.Connecting)
261
246
 
262
247
  def handle_disconnect(_: BleakClient):
263
- self._data_ctx.close()
264
- time.sleep(0.1)
265
- self._data_buffer.queue.clear()
266
- self._data_ctx = None
267
- self._gforce = None
248
+ if self._data_ctx != None:
249
+ self._data_ctx.close()
250
+ time.sleep(1)
251
+ self._data_buffer.queue.clear()
252
+ self._data_ctx = None
253
+ self._gforce = None
268
254
  self._set_device_state(DeviceStateEx.Disconnected)
269
255
  pass
270
256
 
271
257
  await self._gforce.connect(handle_disconnect, self._raw_data_buf)
272
258
 
273
- if self._gforce.client.is_connected:
259
+ if self._gforce != None and self._gforce.client.is_connected:
274
260
  self._set_device_state(DeviceStateEx.Connected)
275
261
  self._set_device_state(DeviceStateEx.Ready)
276
262
  # if self._gforce.client.mtu_size >= 80:
@@ -289,7 +275,7 @@ class SensorProfile:
289
275
  :return: bool: 如果连接成功,返回 True;否则返回 False。
290
276
 
291
277
  """
292
- result = sync_call(self._gforce_event_loop, self._connect())
278
+ result = sync_call(self._connect())
293
279
  return result
294
280
 
295
281
  async def asyncConnect(self) -> bool:
@@ -299,7 +285,7 @@ class SensorProfile:
299
285
  :return: bool: 如果连接成功,返回 True;否则返回 False。
300
286
 
301
287
  """
302
- return await async_call(self._gforce_event_loop, self._connect())
288
+ return await async_call(self._connect())
303
289
 
304
290
  async def _waitForDisconnect(self) -> bool:
305
291
  while self.deviceState != DeviceStateEx.Disconnected:
@@ -314,6 +300,7 @@ class SensorProfile:
314
300
  self._set_device_state(DeviceStateEx.Disconnecting)
315
301
  await self._gforce.disconnect()
316
302
  await asyncio.wait_for(self._waitForDisconnect(), utils._TIMEOUT)
303
+
317
304
  return True
318
305
 
319
306
  def disconnect(self) -> bool:
@@ -323,7 +310,7 @@ class SensorProfile:
323
310
  :return: bool: 如果断开连接成功,返回 True;否则返回 False。
324
311
 
325
312
  """
326
- return sync_call(self._gforce_event_loop, self._disconnect())
313
+ return sync_call(self._disconnect())
327
314
 
328
315
  async def asyncDisconnect(self) -> bool:
329
316
  """
@@ -332,28 +319,13 @@ class SensorProfile:
332
319
  :return: bool: 如果断开连接成功,返回 True;否则返回 False。
333
320
 
334
321
  """
335
- return await async_call(self._gforce_event_loop, self._disconnect())
322
+ return await async_call(self._disconnect())
336
323
 
337
324
  async def _process_data(self):
338
- while self._data_ctx._is_running and self._data_ctx.isDataTransfering:
339
- self._data_ctx.process_data(self._data_buffer, self)
340
- while self._data_ctx._is_running and self._data_ctx.isDataTransfering:
341
- sensorData: SensorData = None
342
- try:
343
- sensorData = self._data_buffer.get_nowait()
344
- except Exception as e:
345
- break
346
- if self._event_loop != None and sensorData != None and self._on_data_callback != None:
347
- try:
348
- self._event_loop.call_soon_threadsafe(self._on_data_callback, self, sensorData)
349
- except Exception as e:
350
- print(e)
351
- self._data_buffer.task_done()
325
+ await self._data_ctx.process_data(self._data_buffer, self, self._on_data_callback)
352
326
 
353
327
  async def _process_universal_data(self):
354
- await self._data_ctx.processUniversalData(
355
- self._data_buffer, self._event_loop, self._gforce_event_loop, self, self._on_data_callback
356
- )
328
+ await self._data_ctx.processUniversalData(self._data_buffer, self, self._on_data_callback)
357
329
 
358
330
  async def _startDataNotification(self) -> bool:
359
331
  if self.deviceState != DeviceStateEx.Ready:
@@ -368,16 +340,12 @@ class SensorProfile:
368
340
 
369
341
  if self._data_event_loop == None:
370
342
  self._data_event_loop = asyncio.new_event_loop()
371
- self._data_event_thread = threading.Thread(target=start_loop, args=(self._data_event_loop,))
372
- self._data_event_thread.daemon = True
373
- self._data_event_thread.name = self.BLEDevice.Name + " data"
374
- self._data_event_thread.start()
375
343
 
376
344
  result = await self._data_ctx.start_streaming()
377
345
  self._data_buffer.queue.clear()
378
346
  self._data_ctx.clear()
379
347
  if not self._data_ctx.isUniversalStream:
380
- async_exec(self._data_event_loop, self._process_data())
348
+ async_exec(self._process_data())
381
349
  return result
382
350
 
383
351
  def startDataNotification(self) -> bool:
@@ -387,7 +355,7 @@ class SensorProfile:
387
355
  :return: bool: 如果开始数据通知成功,返回 True;否则返回 False。
388
356
 
389
357
  """
390
- return sync_call(self._gforce_event_loop, self._startDataNotification())
358
+ return sync_call(self._startDataNotification())
391
359
 
392
360
  async def asyncStartDataNotification(self) -> bool:
393
361
  """
@@ -396,7 +364,7 @@ class SensorProfile:
396
364
  :return: bool: 如果开始数据通知成功,返回 True;否则返回 False。
397
365
 
398
366
  """
399
- return await async_call(self._gforce_event_loop, self._startDataNotification())
367
+ return await async_call(self._startDataNotification())
400
368
 
401
369
  async def _stopDataNotification(self) -> bool:
402
370
  if self.deviceState != DeviceStateEx.Ready:
@@ -418,7 +386,7 @@ class SensorProfile:
418
386
  :return: bool: 如果停止数据通知成功,返回 True;否则返回 False。
419
387
 
420
388
  """
421
- return sync_call(self._gforce_event_loop, self._stopDataNotification())
389
+ return sync_call(self._stopDataNotification())
422
390
 
423
391
  async def asyncStopDataNotification(self) -> bool:
424
392
  """
@@ -427,23 +395,19 @@ class SensorProfile:
427
395
  :return: bool: 如果停止数据通知成功,返回 True;否则返回 False。
428
396
 
429
397
  """
430
- return await async_call(self._gforce_event_loop, self._stopDataNotification())
398
+ return await async_call(self._stopDataNotification())
431
399
 
432
400
  async def _refresh_power(self):
433
- self._power = await self._gforce.get_battery_level()
401
+ while not utils._terminated and self.deviceState == DeviceStateEx.Ready:
402
+ await asyncio.sleep(self._power_interval / 1000)
434
403
 
435
- if self._event_loop != None and self._on_power_changed != None:
436
- try:
437
- self._event_loop.call_soon_threadsafe(self._on_power_changed, self, self._power)
438
- except Exception as e:
439
- print(e)
404
+ self._power = await self._gforce.get_battery_level()
440
405
 
441
- if self.deviceState == DeviceStateEx.Ready:
442
- timer(
443
- self._gforce_event_loop,
444
- self._power_interval / 1000,
445
- self._refresh_power(),
446
- )
406
+ if self._event_loop != None and self._on_power_changed != None:
407
+ try:
408
+ asyncio.get_event_loop().run_in_executor(None, self._on_power_changed, self, self._power)
409
+ except Exception as e:
410
+ print(e)
447
411
 
448
412
  async def _init(self, packageSampleCount: int, powerRefreshInterval: int) -> bool:
449
413
  if self.deviceState != DeviceStateEx.Ready:
@@ -455,11 +419,7 @@ class SensorProfile:
455
419
 
456
420
  if await self._data_ctx.init(packageSampleCount):
457
421
  self._power_interval = powerRefreshInterval
458
- timer(
459
- self._gforce_event_loop,
460
- self._power_interval / 1000,
461
- self._refresh_power(),
462
- )
422
+ utils.async_exec(self._refresh_power())
463
423
 
464
424
  return self._data_ctx.hasInit()
465
425
 
@@ -474,7 +434,6 @@ class SensorProfile:
474
434
 
475
435
  """
476
436
  return sync_call(
477
- self._gforce_event_loop,
478
437
  self._init(packageSampleCount, powerRefreshInterval),
479
438
  20,
480
439
  )
@@ -490,7 +449,6 @@ class SensorProfile:
490
449
 
491
450
  """
492
451
  return await async_call(
493
- self._gforce_event_loop,
494
452
  self._init(packageSampleCount, powerRefreshInterval),
495
453
  20,
496
454
  )
@@ -0,0 +1,382 @@
1
+ import asyncio
2
+ import platform
3
+ import queue
4
+ import signal
5
+ import threading
6
+ import time
7
+
8
+ _terminated = False
9
+ _TIMEOUT = 10
10
+ running_tasks = set()
11
+ _runloop: asyncio.AbstractEventLoop = None
12
+ _event_thread: threading.Thread = None
13
+ _needCloseRunloop = False
14
+
15
+
16
+ def checkRunLoop():
17
+ global _runloop, _needCloseRunloop, _event_thread
18
+ if _runloop == None:
19
+ try:
20
+ _runloop = asyncio.get_running_loop()
21
+ except Exception as e:
22
+ _needCloseRunloop = True
23
+ _runloop = asyncio.new_event_loop()
24
+ _event_thread = threading.Thread(target=start_loop, args=(_runloop,))
25
+ _event_thread.daemon = True
26
+ _event_thread.name = "SensorController event"
27
+ _event_thread.start()
28
+
29
+
30
+ def Terminate():
31
+ global _runloop, _needCloseRunloop, _event_thread
32
+ if _needCloseRunloop:
33
+ try:
34
+ _runloop.stop()
35
+ _runloop.close()
36
+ except Exception as e:
37
+ pass
38
+
39
+
40
+ async def delay(_time: float, function) -> any:
41
+ try:
42
+ await asyncio.sleep(_time)
43
+ except Exception as e:
44
+ pass
45
+
46
+ try:
47
+ return await function
48
+ except Exception as e:
49
+ pass
50
+
51
+
52
+ def timer(_delay: float, function):
53
+ checkRunLoop()
54
+ task: asyncio.Future = None
55
+ try:
56
+ task = asyncio.run_coroutine_threadsafe(delay(_delay, function), _runloop)
57
+ running_tasks.add(task)
58
+ task.add_done_callback(lambda t: running_tasks.remove(t))
59
+ except Exception as e:
60
+ print(e)
61
+ pass
62
+
63
+
64
+ def async_exec(function):
65
+ checkRunLoop()
66
+ task: asyncio.Future = None
67
+ try:
68
+ task = asyncio.run_coroutine_threadsafe(function, _runloop)
69
+ running_tasks.add(task)
70
+ task.add_done_callback(lambda t: running_tasks.remove(t))
71
+ except Exception as e:
72
+ print(e)
73
+ pass
74
+
75
+
76
+ def sync_call(function, _timeout=_TIMEOUT) -> any:
77
+ checkRunLoop()
78
+ task: asyncio.Future = None
79
+ try:
80
+ task = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout), _runloop)
81
+ running_tasks.add(task)
82
+ task.add_done_callback(lambda t: running_tasks.remove(t))
83
+ return task.result(timeout=_timeout)
84
+ except Exception as e:
85
+ print(e)
86
+ pass
87
+
88
+
89
+ async def async_call(function, _timeout=_TIMEOUT) -> any:
90
+ checkRunLoop()
91
+ task: asyncio.Future = None
92
+ try:
93
+ task = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout), _runloop)
94
+ running_tasks.add(task)
95
+ task.add_done_callback(lambda t: running_tasks.remove(t))
96
+ except Exception as e:
97
+ print(e)
98
+ pass
99
+
100
+ while not _terminated and not task.done():
101
+ await asyncio.sleep(0.1)
102
+
103
+ try:
104
+ if not task.cancelled():
105
+ return task.result()
106
+ except Exception as e:
107
+ print(e)
108
+ return
109
+
110
+
111
+ def start_loop(loop: asyncio.BaseEventLoop):
112
+ asyncio.set_event_loop(loop)
113
+ loop.run_forever()
114
+
115
+
116
+ def calc_crc8(data):
117
+ crc8Table = [
118
+ 0x00,
119
+ 0x07,
120
+ 0x0E,
121
+ 0x09,
122
+ 0x1C,
123
+ 0x1B,
124
+ 0x12,
125
+ 0x15,
126
+ 0x38,
127
+ 0x3F,
128
+ 0x36,
129
+ 0x31,
130
+ 0x24,
131
+ 0x23,
132
+ 0x2A,
133
+ 0x2D,
134
+ 0x70,
135
+ 0x77,
136
+ 0x7E,
137
+ 0x79,
138
+ 0x6C,
139
+ 0x6B,
140
+ 0x62,
141
+ 0x65,
142
+ 0x48,
143
+ 0x4F,
144
+ 0x46,
145
+ 0x41,
146
+ 0x54,
147
+ 0x53,
148
+ 0x5A,
149
+ 0x5D,
150
+ 0xE0,
151
+ 0xE7,
152
+ 0xEE,
153
+ 0xE9,
154
+ 0xFC,
155
+ 0xFB,
156
+ 0xF2,
157
+ 0xF5,
158
+ 0xD8,
159
+ 0xDF,
160
+ 0xD6,
161
+ 0xD1,
162
+ 0xC4,
163
+ 0xC3,
164
+ 0xCA,
165
+ 0xCD,
166
+ 0x90,
167
+ 0x97,
168
+ 0x9E,
169
+ 0x99,
170
+ 0x8C,
171
+ 0x8B,
172
+ 0x82,
173
+ 0x85,
174
+ 0xA8,
175
+ 0xAF,
176
+ 0xA6,
177
+ 0xA1,
178
+ 0xB4,
179
+ 0xB3,
180
+ 0xBA,
181
+ 0xBD,
182
+ 0xC7,
183
+ 0xC0,
184
+ 0xC9,
185
+ 0xCE,
186
+ 0xDB,
187
+ 0xDC,
188
+ 0xD5,
189
+ 0xD2,
190
+ 0xFF,
191
+ 0xF8,
192
+ 0xF1,
193
+ 0xF6,
194
+ 0xE3,
195
+ 0xE4,
196
+ 0xED,
197
+ 0xEA,
198
+ 0xB7,
199
+ 0xB0,
200
+ 0xB9,
201
+ 0xBE,
202
+ 0xAB,
203
+ 0xAC,
204
+ 0xA5,
205
+ 0xA2,
206
+ 0x8F,
207
+ 0x88,
208
+ 0x81,
209
+ 0x86,
210
+ 0x93,
211
+ 0x94,
212
+ 0x9D,
213
+ 0x9A,
214
+ 0x27,
215
+ 0x20,
216
+ 0x29,
217
+ 0x2E,
218
+ 0x3B,
219
+ 0x3C,
220
+ 0x35,
221
+ 0x32,
222
+ 0x1F,
223
+ 0x18,
224
+ 0x11,
225
+ 0x16,
226
+ 0x03,
227
+ 0x04,
228
+ 0x0D,
229
+ 0x0A,
230
+ 0x57,
231
+ 0x50,
232
+ 0x59,
233
+ 0x5E,
234
+ 0x4B,
235
+ 0x4C,
236
+ 0x45,
237
+ 0x42,
238
+ 0x6F,
239
+ 0x68,
240
+ 0x61,
241
+ 0x66,
242
+ 0x73,
243
+ 0x74,
244
+ 0x7D,
245
+ 0x7A,
246
+ 0x89,
247
+ 0x8E,
248
+ 0x87,
249
+ 0x80,
250
+ 0x95,
251
+ 0x92,
252
+ 0x9B,
253
+ 0x9C,
254
+ 0xB1,
255
+ 0xB6,
256
+ 0xBF,
257
+ 0xB8,
258
+ 0xAD,
259
+ 0xAA,
260
+ 0xA3,
261
+ 0xA4,
262
+ 0xF9,
263
+ 0xFE,
264
+ 0xF7,
265
+ 0xF0,
266
+ 0xE5,
267
+ 0xE2,
268
+ 0xEB,
269
+ 0xEC,
270
+ 0xC1,
271
+ 0xC6,
272
+ 0xCF,
273
+ 0xC8,
274
+ 0xDD,
275
+ 0xDA,
276
+ 0xD3,
277
+ 0xD4,
278
+ 0x69,
279
+ 0x6E,
280
+ 0x67,
281
+ 0x60,
282
+ 0x75,
283
+ 0x72,
284
+ 0x7B,
285
+ 0x7C,
286
+ 0x51,
287
+ 0x56,
288
+ 0x5F,
289
+ 0x58,
290
+ 0x4D,
291
+ 0x4A,
292
+ 0x43,
293
+ 0x44,
294
+ 0x19,
295
+ 0x1E,
296
+ 0x17,
297
+ 0x10,
298
+ 0x05,
299
+ 0x02,
300
+ 0x0B,
301
+ 0x0C,
302
+ 0x21,
303
+ 0x26,
304
+ 0x2F,
305
+ 0x28,
306
+ 0x3D,
307
+ 0x3A,
308
+ 0x33,
309
+ 0x34,
310
+ 0x4E,
311
+ 0x49,
312
+ 0x40,
313
+ 0x47,
314
+ 0x52,
315
+ 0x55,
316
+ 0x5C,
317
+ 0x5B,
318
+ 0x76,
319
+ 0x71,
320
+ 0x78,
321
+ 0x7F,
322
+ 0x6A,
323
+ 0x6D,
324
+ 0x64,
325
+ 0x63,
326
+ 0x3E,
327
+ 0x39,
328
+ 0x30,
329
+ 0x37,
330
+ 0x22,
331
+ 0x25,
332
+ 0x2C,
333
+ 0x2B,
334
+ 0x06,
335
+ 0x01,
336
+ 0x08,
337
+ 0x0F,
338
+ 0x1A,
339
+ 0x1D,
340
+ 0x14,
341
+ 0x13,
342
+ 0xAE,
343
+ 0xA9,
344
+ 0xA0,
345
+ 0xA7,
346
+ 0xB2,
347
+ 0xB5,
348
+ 0xBC,
349
+ 0xBB,
350
+ 0x96,
351
+ 0x91,
352
+ 0x98,
353
+ 0x9F,
354
+ 0x8A,
355
+ 0x8D,
356
+ 0x84,
357
+ 0x83,
358
+ 0xDE,
359
+ 0xD9,
360
+ 0xD0,
361
+ 0xD7,
362
+ 0xC2,
363
+ 0xC5,
364
+ 0xCC,
365
+ 0xCB,
366
+ 0xE6,
367
+ 0xE1,
368
+ 0xE8,
369
+ 0xEF,
370
+ 0xFA,
371
+ 0xFD,
372
+ 0xF4,
373
+ 0xF3,
374
+ ]
375
+ crc8 = 0
376
+ len_data = len(data)
377
+
378
+ for i in range(len_data):
379
+ crc8 ^= data[i]
380
+ crc8 = crc8Table[crc8]
381
+
382
+ return crc8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sensor-sdk
3
- Version: 0.0.8
3
+ Version: 0.0.10
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.8",
11
+ version="0.0.10",
12
12
  description="Python sdk for Synchroni",
13
13
  long_description=long_description,
14
14
  long_description_content_type="text/markdown",
@@ -1,73 +0,0 @@
1
- import asyncio
2
- import platform
3
- import queue
4
- import signal
5
- import time
6
-
7
- _terminated = False
8
- _TIMEOUT = 10
9
-
10
-
11
- async def delay(_time: float, function) -> any:
12
- await asyncio.sleep(_time)
13
- return await function
14
-
15
-
16
- def timer(_loop: asyncio.AbstractEventLoop, _delay: float, function):
17
- if _loop == None:
18
- return
19
- try:
20
- asyncio.run_coroutine_threadsafe(delay(_delay, function), _loop)
21
- except Exception as e:
22
- print(e)
23
- pass
24
-
25
-
26
- def async_exec(_loop: asyncio.AbstractEventLoop, function):
27
- if _loop == None:
28
- return
29
- try:
30
- asyncio.run_coroutine_threadsafe(function, _loop)
31
- except Exception as e:
32
- print(e)
33
- pass
34
-
35
-
36
- def sync_call(_loop: asyncio.AbstractEventLoop, function, _timeout=_TIMEOUT) -> any:
37
- if _loop == None:
38
- return
39
-
40
- try:
41
- f = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout), _loop)
42
- return f.result(timeout=_timeout)
43
- except Exception as e:
44
- print(e)
45
- pass
46
-
47
-
48
- async def async_call(_loop: asyncio.AbstractEventLoop, function, _timeout=_TIMEOUT) -> any:
49
- if _loop == None:
50
- return
51
-
52
- try:
53
- f = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout), _loop)
54
- except Exception as e:
55
- print(e)
56
- pass
57
-
58
- while not _terminated and not f.done():
59
- await asyncio.sleep(0.1)
60
-
61
- try:
62
- if not f.cancelled():
63
- return f.result()
64
- except Exception as e:
65
- print(e)
66
- return
67
-
68
-
69
- def start_loop(loop: asyncio.BaseEventLoop):
70
- if platform.system() == "Darwin":
71
- asyncio.get_running_loop = asyncio.get_event_loop
72
- asyncio.set_event_loop(loop)
73
- loop.run_forever()
File without changes
File without changes
File without changes