sensor-sdk 0.0.8__py3-none-any.whl → 0.0.11__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
@@ -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:
@@ -4,7 +4,9 @@ import platform
4
4
  from queue import Queue
5
5
  import struct
6
6
  from typing import Deque, List
7
+ from concurrent.futures import ThreadPoolExecutor
7
8
 
9
+ from sensor import utils
8
10
  from sensor.gforce import DataSubscription, GForce
9
11
  from sensor.sensor_data import DataType, Sample, SensorData
10
12
 
@@ -55,6 +57,7 @@ class SensorProfileDataCtx:
55
57
  self.sensorDatas.append(SensorData())
56
58
  self.impedanceData: List[float] = list()
57
59
  self.saturationData: List[float] = list()
60
+ self.dataPool = ThreadPoolExecutor(1, "data")
58
61
 
59
62
  def close(self):
60
63
  self._is_running = False
@@ -284,14 +287,32 @@ class SensorProfileDataCtx:
284
287
  await self.gForce.set_subscription(0)
285
288
  return True
286
289
 
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
290
+ async def process_data(self, buf: Queue[SensorData], sensor, callback):
291
+ while self._is_running and self._rawDataBuffer.empty():
292
+ await asyncio.sleep(0.01)
292
293
 
293
- self._processDataPackage(data, buf, sensor)
294
- self._rawDataBuffer.task_done()
294
+ while self._is_running and not self._rawDataBuffer.empty():
295
+ try:
296
+ data: bytes = self._rawDataBuffer.get_nowait()
297
+ except Exception as e:
298
+ continue
299
+
300
+ self._processDataPackage(data, buf, sensor)
301
+ self._rawDataBuffer.task_done()
302
+
303
+ while self._is_running and self.isDataTransfering and not buf.empty():
304
+ sensorData: SensorData = None
305
+ try:
306
+ sensorData = buf.get_nowait()
307
+ except Exception as e:
308
+ break
309
+ if sensorData != None and callback != None:
310
+ try:
311
+ asyncio.get_event_loop().run_in_executor(self.dataPool, callback, sensor, sensorData)
312
+ except Exception as e:
313
+ print(e)
314
+
315
+ buf.task_done()
295
316
 
296
317
  def _processDataPackage(self, data: bytes, buf: Queue[SensorData], sensor):
297
318
  v = data[0]
@@ -370,7 +391,7 @@ class SensorProfileDataCtx:
370
391
  # print(lostLog)
371
392
  if sensor._event_loop != None and sensor._on_error_callback != None:
372
393
  try:
373
- sensor._event_loop.call_soon_threadsafe(sensor._on_error_callback, sensor, lostLog)
394
+ asyncio.get_event_loop().run_in_executor(None, sensor._on_error_callback, sensor, lostLog)
374
395
  except Exception as e:
375
396
  pass
376
397
 
@@ -522,277 +543,7 @@ class SensorProfileDataCtx:
522
543
  for sensorDataResult in sensorDataList:
523
544
  buf.put(sensorDataResult)
524
545
 
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
- ):
546
+ async def processUniversalData(self, buf: Queue[SensorData], sensor, callback):
796
547
 
797
548
  while self._is_running:
798
549
  while self._is_running and self._rawDataBuffer.empty():
@@ -824,7 +575,7 @@ class SensorProfileDataCtx:
824
575
  index += 1
825
576
  continue
826
577
  crc = self._concatDataBuffer[index + 1 + n + 1]
827
- calc_crc = self.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
578
+ calc_crc = utils.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
828
579
  if crc != calc_crc:
829
580
  index += 1
830
581
  continue
@@ -837,9 +588,9 @@ class SensorProfileDataCtx:
837
588
  sensorData = buf.get_nowait()
838
589
  except Exception as e:
839
590
  break
840
- if event_loop != None and sensorData != None and callback != None:
591
+ if sensorData != None and callback != None:
841
592
  try:
842
- event_loop.call_soon_threadsafe(callback, sensor, sensorData)
593
+ asyncio.get_event_loop().run_in_executor(self.dataPool, callback, sensor, sensorData)
843
594
  except Exception as e:
844
595
  print(e)
845
596
 
@@ -855,13 +606,12 @@ class SensorProfileDataCtx:
855
606
  index += 1
856
607
  continue
857
608
  crc = self._concatDataBuffer[index + 1 + n + 1]
858
- calc_crc = self.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
609
+ calc_crc = utils.calc_crc8(self._concatDataBuffer[index + 2 : index + 2 + n])
859
610
  if crc != calc_crc:
860
611
  index += 1
861
612
  continue
862
613
  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)
614
+ asyncio.get_event_loop().run_in_executor(self.dataPool, self.gForce._on_cmd_response, None, data_package)
865
615
  last_cut = index = index + 2 + n
866
616
 
867
617
  else:
sensor/sensor_profile.py CHANGED
@@ -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
  )
sensor/utils.py CHANGED
@@ -2,72 +2,381 @@ import asyncio
2
2
  import platform
3
3
  import queue
4
4
  import signal
5
+ import threading
5
6
  import time
6
7
 
7
8
  _terminated = False
8
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
9
38
 
10
39
 
11
40
  async def delay(_time: float, function) -> any:
12
- await asyncio.sleep(_time)
13
- return await function
41
+ try:
42
+ await asyncio.sleep(_time)
43
+ except Exception as e:
44
+ pass
14
45
 
46
+ try:
47
+ return await function
48
+ except Exception as e:
49
+ pass
15
50
 
16
- def timer(_loop: asyncio.AbstractEventLoop, _delay: float, function):
17
- if _loop == None:
18
- return
51
+
52
+ def timer(_delay: float, function):
53
+ checkRunLoop()
54
+ task: asyncio.Future = None
19
55
  try:
20
- asyncio.run_coroutine_threadsafe(delay(_delay, function), _loop)
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))
21
59
  except Exception as e:
22
60
  print(e)
23
61
  pass
24
62
 
25
63
 
26
- def async_exec(_loop: asyncio.AbstractEventLoop, function):
27
- if _loop == None:
28
- return
64
+ def async_exec(function):
65
+ checkRunLoop()
66
+ task: asyncio.Future = None
29
67
  try:
30
- asyncio.run_coroutine_threadsafe(function, _loop)
68
+ task = asyncio.run_coroutine_threadsafe(function, _runloop)
69
+ running_tasks.add(task)
70
+ task.add_done_callback(lambda t: running_tasks.remove(t))
31
71
  except Exception as e:
32
72
  print(e)
33
73
  pass
34
74
 
35
75
 
36
- def sync_call(_loop: asyncio.AbstractEventLoop, function, _timeout=_TIMEOUT) -> any:
37
- if _loop == None:
38
- return
39
-
76
+ def sync_call(function, _timeout=_TIMEOUT) -> any:
77
+ checkRunLoop()
78
+ task: asyncio.Future = None
40
79
  try:
41
- f = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout), _loop)
42
- return f.result(timeout=_timeout)
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)
43
84
  except Exception as e:
44
85
  print(e)
45
86
  pass
46
87
 
47
88
 
48
- async def async_call(_loop: asyncio.AbstractEventLoop, function, _timeout=_TIMEOUT) -> any:
49
- if _loop == None:
50
- return
51
-
89
+ async def async_call(function, _timeout=_TIMEOUT) -> any:
90
+ checkRunLoop()
91
+ task: asyncio.Future = None
52
92
  try:
53
- f = asyncio.run_coroutine_threadsafe(asyncio.wait_for(function, _timeout), _loop)
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))
54
96
  except Exception as e:
55
97
  print(e)
56
98
  pass
57
99
 
58
- while not _terminated and not f.done():
100
+ while not _terminated and not task.done():
59
101
  await asyncio.sleep(0.1)
60
102
 
61
103
  try:
62
- if not f.cancelled():
63
- return f.result()
104
+ if not task.cancelled():
105
+ return task.result()
64
106
  except Exception as e:
65
107
  print(e)
66
108
  return
67
109
 
68
110
 
69
111
  def start_loop(loop: asyncio.BaseEventLoop):
70
- if platform.system() == "Darwin":
71
- asyncio.get_running_loop = asyncio.get_event_loop
72
112
  asyncio.set_event_loop(loop)
73
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.11
4
4
  Summary: Python sdk for Synchroni
5
5
  Home-page: https://github.com/oymotion/SynchroniSDKPython
6
6
  Author: Martin Ye
@@ -0,0 +1,14 @@
1
+ sensor/__init__.py,sha256=L1VyAP0EDEnJIMeMTzp4iXHSRUUHyHScF_GIl3iYKRI,123
2
+ sensor/gforce.py,sha256=mTw0VVQSxPiTzZmjK7I9knTlTkEeu4PISQdlAkMU6WE,25232
3
+ sensor/sensor_controller.py,sha256=qnM9mVwpJn9PCnxlvbgWFeaLktqVnmaquZgasYGEHqs,9451
4
+ sensor/sensor_data.py,sha256=Hu7Ql0LgQ7V24xYZhaLrKPwU4KWZeWE655v8Gy8xphY,3934
5
+ sensor/sensor_data_context.py,sha256=Mlq0Bflnf6Lmj9cf7ccXYnjKldKD4pM9_x1GcgFynEs,25166
6
+ sensor/sensor_device.py,sha256=eO1vaqjxCc2UCPBoKXqlk6o498uRyWt6IYs7r7wXSD0,3042
7
+ sensor/sensor_profile.py,sha256=64MFNyLJajX6HeGS_tmN9R9wfEW1qjsP_MjH3ZRPq4s,17322
8
+ sensor/utils.py,sha256=FuBEChpJYbXnUaDvlta3aT6dYGAK2fQdGbIB7BYMQp4,7077
9
+ sensor_sdk-0.0.11.dist-info/LICENSE.txt,sha256=8CSivOpub3IuXODTyqBRI91AxouJZk02YrcKuOAkWu8,1111
10
+ sensor_sdk-0.0.11.dist-info/METADATA,sha256=VMpmAjRXMlk8u-UnHu_QrXULi0-YMxSdRSGDwPmCdPs,8375
11
+ sensor_sdk-0.0.11.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
12
+ sensor_sdk-0.0.11.dist-info/top_level.txt,sha256=Ftq49B6bH0Ffdc7c8LkcyakHo6lsg_snlBbpEUoILSk,7
13
+ sensor_sdk-0.0.11.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
14
+ sensor_sdk-0.0.11.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- sensor/__init__.py,sha256=L1VyAP0EDEnJIMeMTzp4iXHSRUUHyHScF_GIl3iYKRI,123
2
- sensor/gforce.py,sha256=wHnZ9fJiqzD9TpHekWsEqp3VIyIBhbXwJSj5k6FjVNw,25049
3
- sensor/sensor_controller.py,sha256=lNi7i3T_aAHEJvv963OvVv_PNAYy_5-BwXD82XNvnGg,10028
4
- sensor/sensor_data.py,sha256=Hu7Ql0LgQ7V24xYZhaLrKPwU4KWZeWE655v8Gy8xphY,3934
5
- sensor/sensor_data_context.py,sha256=KuwdB_l-IC4hxcN_9MMfiRQCLZQBpCn3TgWZioQrSpc,29364
6
- sensor/sensor_device.py,sha256=eO1vaqjxCc2UCPBoKXqlk6o498uRyWt6IYs7r7wXSD0,3042
7
- sensor/sensor_profile.py,sha256=mjqOEZLGJQYY8zhH8TA0HRVxrFC4AjjKAj_zGOQMgHg,19678
8
- sensor/utils.py,sha256=ybmByBldCQ_x-sVtjA2mdXWo0QOafPAupfnWAxLrkV0,1773
9
- sensor_sdk-0.0.8.dist-info/LICENSE.txt,sha256=8CSivOpub3IuXODTyqBRI91AxouJZk02YrcKuOAkWu8,1111
10
- sensor_sdk-0.0.8.dist-info/METADATA,sha256=BfvYDfGxMX44lgFKSXsx6QvxjMaE94xKaG327ww3iYI,8374
11
- sensor_sdk-0.0.8.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
12
- sensor_sdk-0.0.8.dist-info/top_level.txt,sha256=Ftq49B6bH0Ffdc7c8LkcyakHo6lsg_snlBbpEUoILSk,7
13
- sensor_sdk-0.0.8.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
14
- sensor_sdk-0.0.8.dist-info/RECORD,,