yostlabs 2025.1.16__py3-none-any.whl → 2025.2.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.
- yostlabs/communication/base.py +3 -2
- yostlabs/tss3/api.py +694 -674
- yostlabs/tss3/consts.py +40 -1
- {yostlabs-2025.1.16.dist-info → yostlabs-2025.2.11.dist-info}/METADATA +1 -1
- {yostlabs-2025.1.16.dist-info → yostlabs-2025.2.11.dist-info}/RECORD +7 -7
- {yostlabs-2025.1.16.dist-info → yostlabs-2025.2.11.dist-info}/WHEEL +0 -0
- {yostlabs-2025.1.16.dist-info → yostlabs-2025.2.11.dist-info}/licenses/LICENSE +0 -0
yostlabs/tss3/api.py
CHANGED
|
@@ -5,6 +5,7 @@ from yostlabs.communication.serial import ThreespaceSerialComClass
|
|
|
5
5
|
from enum import Enum
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
7
|
from typing import TypeVar, Generic
|
|
8
|
+
from collections.abc import Callable
|
|
8
9
|
import struct
|
|
9
10
|
import types
|
|
10
11
|
import inspect
|
|
@@ -62,10 +63,11 @@ class ThreespaceCommand:
|
|
|
62
63
|
BINARY_START_BYTE = 0xf7
|
|
63
64
|
BINARY_START_BYTE_HEADER = 0xf9
|
|
64
65
|
|
|
65
|
-
def __init__(self, name: str, num: int, in_format: str, out_format: str):
|
|
66
|
+
def __init__(self, name: str, num: int, in_format: str, out_format: str, custom_func: Callable = None):
|
|
66
67
|
self.info = ThreespaceCommandInfo(name, num, in_format, out_format)
|
|
67
68
|
self.in_format = _3space_format_to_external(self.info.in_format)
|
|
68
69
|
self.out_format = _3space_format_to_external(self.info.out_format)
|
|
70
|
+
self.custom_func = custom_func
|
|
69
71
|
|
|
70
72
|
def format_cmd(self, *args, header_enabled=False):
|
|
71
73
|
cmd_data = struct.pack("<B", self.info.num)
|
|
@@ -109,7 +111,7 @@ class ThreespaceCommand:
|
|
|
109
111
|
return output
|
|
110
112
|
|
|
111
113
|
#Read the command dynamically from an input stream
|
|
112
|
-
def read_command(self, com: ThreespaceInputStream):
|
|
114
|
+
def read_command(self, com: ThreespaceInputStream, verbose=False):
|
|
113
115
|
raw = bytearray([])
|
|
114
116
|
if self.info.num_out_params == 0: return None, raw
|
|
115
117
|
output = []
|
|
@@ -120,14 +122,16 @@ class ThreespaceCommand:
|
|
|
120
122
|
response = com.read(size)
|
|
121
123
|
raw += response
|
|
122
124
|
if len(response) != size:
|
|
123
|
-
|
|
125
|
+
if verbose:
|
|
126
|
+
print(f"Failed to read {c} type. Aborting...")
|
|
124
127
|
return None
|
|
125
128
|
output.append(struct.unpack(format_str, response)[0])
|
|
126
129
|
else: #Strings are special, find the null terminator
|
|
127
130
|
response = com.read(1)
|
|
128
131
|
raw += response
|
|
129
132
|
if len(response) != 1:
|
|
130
|
-
|
|
133
|
+
if verbose:
|
|
134
|
+
print(f"Failed to read string. Aborting...")
|
|
131
135
|
return None
|
|
132
136
|
byte = chr(response[0])
|
|
133
137
|
string = ""
|
|
@@ -137,7 +141,8 @@ class ThreespaceCommand:
|
|
|
137
141
|
response = com.read(1)
|
|
138
142
|
raw += response
|
|
139
143
|
if len(response) != 1:
|
|
140
|
-
|
|
144
|
+
if verbose:
|
|
145
|
+
print(f"Failed to read string. Aborting...")
|
|
141
146
|
return None
|
|
142
147
|
byte = chr(response[0])
|
|
143
148
|
output.append(string)
|
|
@@ -157,6 +162,7 @@ class ThreespaceGetStreamingBatchCommand(ThreespaceCommand):
|
|
|
157
162
|
def set_stream_slots(self, streaming_slots: list[ThreespaceCommand]):
|
|
158
163
|
self.commands = streaming_slots
|
|
159
164
|
self.out_format = ''.join(slot.out_format for slot in streaming_slots if slot is not None)
|
|
165
|
+
self.info.out_size = struct.calcsize(f"<{self.out_format}")
|
|
160
166
|
|
|
161
167
|
def parse_response(self, response: bytes):
|
|
162
168
|
data = []
|
|
@@ -168,7 +174,7 @@ class ThreespaceGetStreamingBatchCommand(ThreespaceCommand):
|
|
|
168
174
|
|
|
169
175
|
return data
|
|
170
176
|
|
|
171
|
-
def read_command(self, com: ThreespaceInputStream):
|
|
177
|
+
def read_command(self, com: ThreespaceInputStream, verbose=False):
|
|
172
178
|
#Get the response to all the streaming commands
|
|
173
179
|
response = []
|
|
174
180
|
raw_response = bytearray([])
|
|
@@ -411,6 +417,7 @@ class StreamableCommands(Enum):
|
|
|
411
417
|
|
|
412
418
|
THREESPACE_AWAIT_COMMAND_FOUND = 0
|
|
413
419
|
THREESPACE_AWAIT_COMMAND_TIMEOUT = 1
|
|
420
|
+
THREESPACE_AWAIT_BOOTLOADER = 2
|
|
414
421
|
|
|
415
422
|
T = TypeVar('T')
|
|
416
423
|
|
|
@@ -457,7 +464,7 @@ class ThreespaceBootloaderInfo:
|
|
|
457
464
|
THREESPACE_REQUIRED_HEADER = THREESPACE_HEADER_ECHO_BIT | THREESPACE_HEADER_CHECKSUM_BIT | THREESPACE_HEADER_LENGTH_BIT
|
|
458
465
|
class ThreespaceSensor:
|
|
459
466
|
|
|
460
|
-
def __init__(self, com = None, timeout=2):
|
|
467
|
+
def __init__(self, com = None, timeout=2, verbose=False):
|
|
461
468
|
if com is None: #Default to attempting to use the serial com class if none is provided
|
|
462
469
|
com = ThreespaceSerialComClass
|
|
463
470
|
|
|
@@ -480,22 +487,15 @@ class ThreespaceSensor:
|
|
|
480
487
|
except:
|
|
481
488
|
raise ValueError("Failed to create default ThreespaceSerialComClass from parameter:", type(com), com)
|
|
482
489
|
|
|
483
|
-
self.
|
|
484
|
-
|
|
485
|
-
self.
|
|
486
|
-
|
|
487
|
-
self.
|
|
488
|
-
|
|
489
|
-
#Some commands are special and need added specially
|
|
490
|
-
if command.info.num == THREESPACE_GET_STREAMING_BATCH_COMMAND_NUM:
|
|
491
|
-
self.getStreamingBatchCommand = ThreespaceGetStreamingBatchCommand([])
|
|
492
|
-
command = self.getStreamingBatchCommand
|
|
493
|
-
|
|
494
|
-
self.__add_command(command)
|
|
495
|
-
|
|
496
|
-
self.immediate_debug = False
|
|
490
|
+
self.immediate_debug = True #Assume it is on from the start. May cause it to take slightly longer to initialize, but prevents breaking if it is on
|
|
491
|
+
#Callback gives the debug message and sensor object that caused it
|
|
492
|
+
self.__debug_cache: list[str] = [] #Used for storing startup debug messages until sensor state is confirmed
|
|
493
|
+
|
|
494
|
+
self.verbose = verbose
|
|
495
|
+
self.debug_callback: Callable[[str, ThreespaceSensor],None] = self.__default_debug_callback
|
|
497
496
|
self.misaligned = False
|
|
498
497
|
self.dirty_cache = False
|
|
498
|
+
self.header_info = ThreespaceHeaderInfo()
|
|
499
499
|
self.header_enabled = True
|
|
500
500
|
|
|
501
501
|
#All the different streaming options
|
|
@@ -506,12 +506,27 @@ class ThreespaceSensor:
|
|
|
506
506
|
|
|
507
507
|
#Used to ensure connecting to the correct sensor when reconnecting
|
|
508
508
|
self.serial_number = None
|
|
509
|
+
self.short_serial_number = None
|
|
510
|
+
self.sensor_family = None
|
|
511
|
+
self.firmware_version = None
|
|
512
|
+
|
|
513
|
+
self.commands: list[ThreespaceCommand] = [None] * 256
|
|
514
|
+
self.getStreamingBatchCommand: ThreespaceGetStreamingBatchCommand = None
|
|
515
|
+
self.funcs = {}
|
|
509
516
|
|
|
510
517
|
self.__cached_in_bootloader = self.__check_bootloader_status()
|
|
511
518
|
if not self.in_bootloader:
|
|
512
519
|
self.__firmware_init()
|
|
513
520
|
else:
|
|
514
|
-
self.
|
|
521
|
+
self.__cache_serial_number(self.bootloader_get_sn())
|
|
522
|
+
self.__empty_debug_cache()
|
|
523
|
+
|
|
524
|
+
#Just a helper for outputting information
|
|
525
|
+
def log(self, *args):
|
|
526
|
+
if self.verbose:
|
|
527
|
+
print(*args)
|
|
528
|
+
|
|
529
|
+
#-----------------------INITIALIZIATION & REINITIALIZATION-----------------------------------
|
|
515
530
|
|
|
516
531
|
def __firmware_init(self):
|
|
517
532
|
"""
|
|
@@ -519,9 +534,13 @@ class ThreespaceSensor:
|
|
|
519
534
|
Called for powerup events when booting into firmware
|
|
520
535
|
"""
|
|
521
536
|
self.dirty_cache = False #No longer dirty cause initializing
|
|
522
|
-
|
|
523
|
-
self.com.read_all() #Clear anything that may be there
|
|
524
537
|
|
|
538
|
+
#Only reinitialize settings if detected firmware version changed (Or on startup)
|
|
539
|
+
version = self.get_settings("version_firmware")
|
|
540
|
+
if version != self.firmware_version:
|
|
541
|
+
self.firmware_version = version
|
|
542
|
+
self.__initialize_commands()
|
|
543
|
+
|
|
525
544
|
self.__reinit_firmware()
|
|
526
545
|
|
|
527
546
|
self.valid_mags = self.__get_valid_components("valid_mags")
|
|
@@ -529,16 +548,10 @@ class ThreespaceSensor:
|
|
|
529
548
|
self.valid_gyros = self.__get_valid_components("valid_gyros")
|
|
530
549
|
self.valid_baros = self.__get_valid_components("valid_baros")
|
|
531
550
|
|
|
532
|
-
def __get_valid_components(self, key: str):
|
|
533
|
-
valid = self.get_settings(key)
|
|
534
|
-
if len(valid) == 0: return []
|
|
535
|
-
return [int(v) for v in valid.split(',')]
|
|
536
|
-
|
|
537
551
|
def __reinit_firmware(self):
|
|
538
552
|
"""
|
|
539
553
|
Called when settings may have changed but a full reboot did not occur
|
|
540
554
|
"""
|
|
541
|
-
self.com.read_all() #Clear anything that may be there
|
|
542
555
|
self.dirty_cache = False #No longer dirty cause initializing
|
|
543
556
|
|
|
544
557
|
self.header_info = ThreespaceHeaderInfo()
|
|
@@ -550,26 +563,68 @@ class ThreespaceSensor:
|
|
|
550
563
|
self.file_stream_length = 0
|
|
551
564
|
|
|
552
565
|
self.streaming_packet_size = 0
|
|
553
|
-
self.header_enabled = True
|
|
554
566
|
self._force_stop_streaming()
|
|
555
567
|
|
|
556
568
|
#Now reinitialize the cached settings
|
|
557
569
|
self.__cache_header_settings()
|
|
558
|
-
self.
|
|
570
|
+
self.__cache_streaming_settings()
|
|
559
571
|
|
|
560
|
-
self.
|
|
572
|
+
self.__cache_serial_number(int(self.get_settings("serial_number"), 16))
|
|
573
|
+
self.__empty_debug_cache()
|
|
561
574
|
self.immediate_debug = int(self.get_settings("debug_mode")) == 1 #Needed for some startup processes when restarting
|
|
562
575
|
|
|
576
|
+
def __initialize_commands(self):
|
|
577
|
+
self.commands: list[ThreespaceCommand] = [None] * 256
|
|
578
|
+
self.getStreamingBatchCommand: ThreespaceGetStreamingBatchCommand = None
|
|
579
|
+
self.funcs = {}
|
|
580
|
+
|
|
581
|
+
valid_commands = self.get_settings("valid_commands")
|
|
582
|
+
if valid_commands == THREESPACE_GET_SETTINGS_ERROR_RESPONSE:
|
|
583
|
+
#Treat all commands as valid because firmware is too old to have this setting
|
|
584
|
+
valid_commands = list(range(256))
|
|
585
|
+
self.log("Please update firmware to a version that contains ?valid_commands")
|
|
586
|
+
else:
|
|
587
|
+
valid_commands = list(int(v) for v in valid_commands.split(','))
|
|
588
|
+
|
|
589
|
+
for command in _threespace_commands:
|
|
590
|
+
#Skip commands that are not valid for this sensor
|
|
591
|
+
if command.info.num not in valid_commands:
|
|
592
|
+
#Register as invalid.
|
|
593
|
+
setattr(self, command.info.name, self.__invalid_command)
|
|
594
|
+
continue
|
|
595
|
+
|
|
596
|
+
#Some commands are special and need added specially
|
|
597
|
+
if command.info.num == THREESPACE_GET_STREAMING_BATCH_COMMAND_NUM:
|
|
598
|
+
self.getStreamingBatchCommand = ThreespaceGetStreamingBatchCommand([])
|
|
599
|
+
command = self.getStreamingBatchCommand
|
|
600
|
+
|
|
601
|
+
self.__add_command(command)
|
|
602
|
+
|
|
603
|
+
#------------------------------INITIALIZATION HELPERS--------------------------------------------
|
|
604
|
+
|
|
605
|
+
def __get_valid_components(self, key: str):
|
|
606
|
+
valid = self.get_settings(key)
|
|
607
|
+
if len(valid) == 0: return []
|
|
608
|
+
return [int(v) for v in valid.split(',')]
|
|
609
|
+
|
|
563
610
|
def __add_command(self, command: ThreespaceCommand):
|
|
564
611
|
if self.commands[command.info.num] != None:
|
|
565
|
-
|
|
612
|
+
self.log(f"Registering duplicate command: {command.info.num} {self.commands[command.info.num].info.name} {command.info.name}")
|
|
566
613
|
self.commands[command.info.num] = command
|
|
567
614
|
|
|
568
|
-
#
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
615
|
+
#This command type has special logic that requires its own function.
|
|
616
|
+
#Make that function be called instead of using the generic execute that gets built
|
|
617
|
+
method = None
|
|
618
|
+
if command.custom_func is not None:
|
|
619
|
+
method = types.MethodType(command.custom_func, self)
|
|
620
|
+
else:
|
|
621
|
+
#Build the actual method for executing the command
|
|
622
|
+
code = f"def {command.info.name}(self, *args):\n"
|
|
623
|
+
code += f" return self.execute_command(self.commands[{command.info.num}], *args)"
|
|
624
|
+
exec(code, globals(), self.funcs)
|
|
625
|
+
method = types.MethodType(self.funcs[command.info.name], self)
|
|
626
|
+
|
|
627
|
+
setattr(self, command.info.name, method)
|
|
573
628
|
|
|
574
629
|
def __get_command(self, command_name: str):
|
|
575
630
|
for command in self.commands:
|
|
@@ -577,10 +632,107 @@ class ThreespaceSensor:
|
|
|
577
632
|
if command.info.name == command_name:
|
|
578
633
|
return command
|
|
579
634
|
return None
|
|
635
|
+
|
|
636
|
+
def __attempt_rediscover_self(self):
|
|
637
|
+
"""
|
|
638
|
+
Trys to change the com class currently being used to be a detected
|
|
639
|
+
com class with the same serial number. Useful for re-enumeration, such as when
|
|
640
|
+
entering bootloader and using USB.
|
|
641
|
+
"""
|
|
642
|
+
for potential_com in self.com.auto_detect():
|
|
643
|
+
potential_com.open()
|
|
644
|
+
sensor = ThreespaceSensor(potential_com)
|
|
645
|
+
if sensor.serial_number == self.serial_number:
|
|
646
|
+
self.com = potential_com
|
|
647
|
+
return True
|
|
648
|
+
sensor.cleanup() #Handles closing the potential_com
|
|
649
|
+
return False
|
|
580
650
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
651
|
+
def __cache_header_settings(self):
|
|
652
|
+
"""
|
|
653
|
+
Should be called any time changes are made to the header. Will normally be called via the check_dirty/reinit
|
|
654
|
+
"""
|
|
655
|
+
result = self.get_settings("header")
|
|
656
|
+
header = int(result)
|
|
657
|
+
#API requires these bits to be enabled, so don't let them be disabled
|
|
658
|
+
required_header = header | THREESPACE_REQUIRED_HEADER
|
|
659
|
+
if header == self.header_info.bitfield and header == required_header: return #Nothing to update
|
|
660
|
+
|
|
661
|
+
#Don't allow the header to change while streaming
|
|
662
|
+
#This is to prevent a situation where the header for streaming and commands are different
|
|
663
|
+
#since streaming caches the header. This would cause an issue where the echo byte could be in seperate
|
|
664
|
+
#positions, causing a situation where parsing a command and streaming at the same time breaks since it thinks both are valid cmd echoes.
|
|
665
|
+
if self.is_streaming:
|
|
666
|
+
self.log("Preventing header change due to currently streaming")
|
|
667
|
+
self.set_settings(header=self.header_info.bitfield)
|
|
668
|
+
return
|
|
669
|
+
|
|
670
|
+
if required_header != header:
|
|
671
|
+
self.log(f"Forcing header checksum, echo, and length enabled")
|
|
672
|
+
self.set_settings(header=required_header)
|
|
673
|
+
return
|
|
674
|
+
|
|
675
|
+
#Current/New header is valid, so can cache it
|
|
676
|
+
self.header_info.bitfield = header
|
|
677
|
+
self.cmd_echo_byte_index = self.header_info.get_start_byte(THREESPACE_HEADER_ECHO_BIT) #Needed for cmd validation while streaming
|
|
678
|
+
|
|
679
|
+
def __cache_serial_number(self, serial_number: int):
|
|
680
|
+
"""
|
|
681
|
+
Doesn't actually retrieve the serial number, rather sets various properties based on the serial number
|
|
682
|
+
"""
|
|
683
|
+
self.serial_number = serial_number
|
|
684
|
+
|
|
685
|
+
#Short SN is the 32 bit version of the u64 serial number
|
|
686
|
+
#It is defined as the FamilyVersion (byte) << 24 | Incrementor (24 bits)
|
|
687
|
+
family = (self.serial_number & THREESPACE_SN_FAMILY_MSK) >> THREESPACE_SN_FAMILY_POS
|
|
688
|
+
incrementor = (self.serial_number & THREESPACE_SN_INCREMENTOR_MSK) >> THREESPACE_SN_INCREMENTOR_POS
|
|
689
|
+
self.short_serial_number = family << 24 | incrementor
|
|
690
|
+
self.sensor_family = THREESPACE_SN_FAMILY_TO_NAME.get(family)
|
|
691
|
+
if self.sensor_family is None:
|
|
692
|
+
self.log(f"Unknown Sensor Family detected, {family}")
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
#--------------------------------REINIT/DIRTY Helpers-----------------------------------------------
|
|
696
|
+
def set_cached_settings_dirty(self):
|
|
697
|
+
"""
|
|
698
|
+
Could be streaming settings, header settings...
|
|
699
|
+
Basically the sensor needs reinitialized
|
|
700
|
+
"""
|
|
701
|
+
self.dirty_cache = True
|
|
702
|
+
|
|
703
|
+
def check_dirty(self):
|
|
704
|
+
if not self.dirty_cache: return
|
|
705
|
+
if self.com.reenumerates and not self.com.check_open(): #Must check this, as could have transitioned from bootloader to firmware or vice versa and just needs re-opened/detected
|
|
706
|
+
success = self.__attempt_rediscover_self()
|
|
707
|
+
if not success:
|
|
708
|
+
raise RuntimeError("Sensor connection lost")
|
|
709
|
+
|
|
710
|
+
self._force_stop_streaming() #Can't be streaming when checking the dirty cache. If you want to stream, don't do things that cause the object to go dirty.
|
|
711
|
+
was_in_bootloader = self.__cached_in_bootloader
|
|
712
|
+
self.__cached_in_bootloader = self.__check_bootloader_status()
|
|
713
|
+
|
|
714
|
+
if was_in_bootloader and not self.__cached_in_bootloader: #Just Exited bootloader, need to fully reinit
|
|
715
|
+
self.__firmware_init()
|
|
716
|
+
elif not self.__cached_in_bootloader: #Was already in firmware, so only need to partially reinit
|
|
717
|
+
self.__reinit_firmware() #Partially init when just naturally dirty
|
|
718
|
+
self.dirty_cache = False
|
|
719
|
+
|
|
720
|
+
#-----------------------------------DEBUG COMMANDS---------------------------------------------------
|
|
721
|
+
def __default_debug_callback(self, msg: str, sensor: "ThreespaceSensor"):
|
|
722
|
+
if self.serial_number is None:
|
|
723
|
+
self.__debug_cache.append(msg.strip())
|
|
724
|
+
else:
|
|
725
|
+
print(f"DEBUG {hex(self.serial_number)}:", msg.strip())
|
|
726
|
+
|
|
727
|
+
def __empty_debug_cache(self):
|
|
728
|
+
for msg in self.__debug_cache:
|
|
729
|
+
print(f"DEBUG {hex(self.serial_number)}:", msg)
|
|
730
|
+
self.__debug_cache.clear()
|
|
731
|
+
|
|
732
|
+
def set_debug_callback(self, callback: Callable[[str, "ThreespaceSensor"], None]):
|
|
733
|
+
self.debug_callback = callback
|
|
734
|
+
|
|
735
|
+
#-----------------------------------------------BASE SETTINGS PROTOCOL------------------------------------------------
|
|
584
736
|
|
|
585
737
|
#Can't just do if "header" in string because log_header_enabled exists and doesn't actually require cacheing the header
|
|
586
738
|
HEADER_KEYS = ["header", "header_status", "header_timestamp", "header_echo", "header_checksum", "header_serial", "header_length"]
|
|
@@ -600,10 +752,18 @@ class ThreespaceSensor:
|
|
|
600
752
|
params.append(f"{key}={value}")
|
|
601
753
|
cmd = f"!{';'.join(params)}\n"
|
|
602
754
|
|
|
755
|
+
if len(cmd) > 2048:
|
|
756
|
+
self.log("Too many settings in one set_settings call. Max str length is 2048 but got", len(cmd))
|
|
757
|
+
return 0xFF, 0xFF
|
|
758
|
+
|
|
603
759
|
#For dirty check
|
|
604
760
|
keys = cmd[1:-1].split(';')
|
|
605
761
|
keys = [v.split('=')[0] for v in keys]
|
|
606
762
|
|
|
763
|
+
#Must enable this before sending the set so can properly handle reading the response
|
|
764
|
+
if "debug_mode=1" in cmd:
|
|
765
|
+
self.immediate_debug = True
|
|
766
|
+
|
|
607
767
|
#Send cmd
|
|
608
768
|
self.com.write(cmd.encode())
|
|
609
769
|
|
|
@@ -611,50 +771,18 @@ class ThreespaceSensor:
|
|
|
611
771
|
err = 3
|
|
612
772
|
num_successes = 0
|
|
613
773
|
|
|
614
|
-
|
|
615
|
-
if
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
if b'\n' not in line:
|
|
627
|
-
break
|
|
628
|
-
|
|
629
|
-
try:
|
|
630
|
-
values = line.decode().strip()
|
|
631
|
-
values = values.split(',')
|
|
632
|
-
if len(values) != 2: break
|
|
633
|
-
err = int(values[0])
|
|
634
|
-
num_successes = int(values[1])
|
|
635
|
-
except: break
|
|
636
|
-
if err > 255 or num_successes > 255:
|
|
637
|
-
break
|
|
638
|
-
|
|
639
|
-
#Successfully got pass all the checks!
|
|
640
|
-
#Consume the buffer and continue
|
|
641
|
-
found_response = True
|
|
642
|
-
self.com.readline()
|
|
643
|
-
break
|
|
644
|
-
if found_response: break
|
|
645
|
-
while not self.updateStreaming(max_checks=1): pass #Wait for streaming to parse something!
|
|
646
|
-
else:
|
|
647
|
-
#When not streaming, way more straight forward
|
|
648
|
-
try:
|
|
649
|
-
response = self.com.readline()
|
|
650
|
-
response = response.decode().strip()
|
|
651
|
-
err, num_successes = response.split(',')
|
|
652
|
-
err = int(err)
|
|
653
|
-
num_successes = int(num_successes)
|
|
654
|
-
except:
|
|
655
|
-
print("Failed to parse set response:", response)
|
|
656
|
-
return err, num_successes
|
|
657
|
-
|
|
774
|
+
response = self.__await_set_settings(self.com.timeout)
|
|
775
|
+
if response == THREESPACE_AWAIT_COMMAND_TIMEOUT:
|
|
776
|
+
self.log("Failed to get set_settings response")
|
|
777
|
+
return err, num_successes
|
|
778
|
+
|
|
779
|
+
#Decode response
|
|
780
|
+
response = self.com.readline()
|
|
781
|
+
response = response.decode().strip()
|
|
782
|
+
err, num_successes = response.split(',')
|
|
783
|
+
err = int(err)
|
|
784
|
+
num_successes = int(num_successes)
|
|
785
|
+
|
|
658
786
|
#Handle updating state variables based on settings
|
|
659
787
|
#If the user modified the header, need to cache the settings so the API knows how to interpret responses
|
|
660
788
|
if "header" in cmd.lower(): #First do a quick check
|
|
@@ -662,13 +790,14 @@ class ThreespaceSensor:
|
|
|
662
790
|
self.__cache_header_settings()
|
|
663
791
|
|
|
664
792
|
if "stream_slots" in cmd.lower():
|
|
665
|
-
self.
|
|
793
|
+
self.__cache_streaming_settings()
|
|
666
794
|
|
|
667
|
-
|
|
795
|
+
#All the settings changed, just need to mark dirty
|
|
796
|
+
if any(v in keys for v in ("default", "reboot")):
|
|
668
797
|
self.set_cached_settings_dirty()
|
|
669
798
|
|
|
670
799
|
if err:
|
|
671
|
-
|
|
800
|
+
self.log(f"Err setting {cmd}: {err=} {num_successes=}")
|
|
672
801
|
return err, num_successes
|
|
673
802
|
|
|
674
803
|
def get_settings(self, *args: str) -> dict[str, str] | str:
|
|
@@ -679,91 +808,165 @@ class ThreespaceSensor:
|
|
|
679
808
|
self.com.write(cmd.encode())
|
|
680
809
|
|
|
681
810
|
keys = cmd[1:-1].split(';')
|
|
682
|
-
|
|
811
|
+
error_response_len = len(THREESPACE_GET_SETTINGS_ERROR_RESPONSE)
|
|
683
812
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
#the sensor to determine where the ascii data actually starts.
|
|
688
|
-
#Ex: get_settings("header", "all") would work
|
|
689
|
-
if self.is_streaming:
|
|
690
|
-
first_key = bytes(keys[0] + "=", 'ascii') #Add on the equals sign to try and make this less likely to conflict with binary data
|
|
691
|
-
possible_outputs = [(len(error_response), bytes(error_response, 'ascii')), (len(first_key), first_key)]
|
|
692
|
-
possible_outputs.sort() #Must try the smallest one first because if streaming is slow, may take a while for the data to fill pass the largest possible value
|
|
693
|
-
start_time = time.time()
|
|
694
|
-
while True:
|
|
695
|
-
if time.time() - start_time > self.com.timeout:
|
|
696
|
-
print("Timeout parsing get response")
|
|
697
|
-
return {}
|
|
698
|
-
found_response = False
|
|
699
|
-
for length, key in possible_outputs:
|
|
700
|
-
possible_response = self.com.peek(length)
|
|
701
|
-
if possible_response == key: #This the response, so break and parse
|
|
702
|
-
found_response = True
|
|
703
|
-
break
|
|
704
|
-
if found_response: break
|
|
705
|
-
while not self.updateStreaming(max_checks=1): pass #Wait for streaming to process something. May just advance due to invalid
|
|
813
|
+
min_resp_length = 0
|
|
814
|
+
for key in keys:
|
|
815
|
+
min_resp_length += min(len(key) + 1, error_response_len)
|
|
706
816
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
817
|
+
|
|
818
|
+
response = self.__await_get_settings(min_resp_length, timeout=self.com.timeout)
|
|
819
|
+
if response == THREESPACE_AWAIT_COMMAND_TIMEOUT:
|
|
820
|
+
self.log("Requested:", cmd)
|
|
821
|
+
self.log("Potential response:", self.com.peekline())
|
|
822
|
+
raise RuntimeError("Failed to receive get_settings response")
|
|
823
|
+
|
|
824
|
+
response = self.com.readline()
|
|
825
|
+
response = response.decode().strip().split(';')
|
|
715
826
|
|
|
716
827
|
#Build the response dict
|
|
717
828
|
response_dict = {}
|
|
718
829
|
for i, v in enumerate(response):
|
|
719
|
-
if v ==
|
|
720
|
-
response_dict[keys[i]] =
|
|
830
|
+
if v == THREESPACE_GET_SETTINGS_ERROR_RESPONSE:
|
|
831
|
+
response_dict[keys[i]] = THREESPACE_GET_SETTINGS_ERROR_RESPONSE
|
|
721
832
|
continue
|
|
722
833
|
try:
|
|
723
834
|
key, value = v.split('=')
|
|
724
835
|
response_dict[key] = value
|
|
725
836
|
except:
|
|
726
|
-
|
|
837
|
+
self.log("Failed to parse get value:", i, v, len(v))
|
|
727
838
|
|
|
728
839
|
#Format response
|
|
729
840
|
if len(response_dict) == 1:
|
|
730
841
|
return list(response_dict.values())[0]
|
|
731
842
|
return response_dict
|
|
732
843
|
|
|
733
|
-
|
|
734
|
-
self.check_dirty()
|
|
844
|
+
#-----------Base Settings Parsing----------------
|
|
735
845
|
|
|
736
|
-
|
|
737
|
-
|
|
846
|
+
def __await_set_settings(self, timeout=2):
|
|
847
|
+
start_time = time.time()
|
|
848
|
+
MINIMUM_LENGTH = len("0,0\r\n")
|
|
849
|
+
MAXIMUM_LENGTH = len("255,255\r\n")
|
|
738
850
|
|
|
739
|
-
while
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
raise RuntimeError(f"Failed to get response to command {cmd.info.name}")
|
|
851
|
+
while True:
|
|
852
|
+
remaining_time = timeout - (time.time() - start_time)
|
|
853
|
+
if remaining_time <= 0:
|
|
854
|
+
return THREESPACE_AWAIT_COMMAND_TIMEOUT
|
|
855
|
+
if self.com.length < MINIMUM_LENGTH: continue
|
|
856
|
+
|
|
857
|
+
possible_response = self.com.peekline()
|
|
858
|
+
if b'\r\n' not in possible_response: continue
|
|
748
859
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
860
|
+
if len(possible_response) < MINIMUM_LENGTH:
|
|
861
|
+
self.__internal_update(self.__try_peek_header())
|
|
862
|
+
continue
|
|
863
|
+
|
|
864
|
+
#Attempt to parse the line
|
|
865
|
+
values = possible_response.split(b',')
|
|
866
|
+
if len(values) != 2:
|
|
867
|
+
self.__internal_update(self.__try_peek_header())
|
|
868
|
+
continue
|
|
869
|
+
|
|
870
|
+
v1 = 0
|
|
871
|
+
v2 = 0
|
|
872
|
+
try:
|
|
873
|
+
v1 = int(values[0].decode())
|
|
874
|
+
v2 = int(values[0].decode())
|
|
875
|
+
except:
|
|
876
|
+
self.__internal_update(self.__try_peek_header())
|
|
877
|
+
continue
|
|
878
|
+
|
|
879
|
+
if v1 < 0 or v1 > 255 or v2 < 0 or v2 > 255:
|
|
880
|
+
self.__internal_update(self.__try_peek_header())
|
|
881
|
+
continue
|
|
882
|
+
|
|
883
|
+
self.misaligned = False
|
|
884
|
+
return THREESPACE_AWAIT_COMMAND_FOUND
|
|
885
|
+
|
|
886
|
+
def __await_get_settings(self, min_resp_length: int, timeout=2, check_bootloader=False):
|
|
887
|
+
start_time = time.time()
|
|
888
|
+
|
|
889
|
+
while True:
|
|
890
|
+
remaining_time = timeout - (time.time() - start_time)
|
|
891
|
+
if remaining_time <= 0:
|
|
892
|
+
return THREESPACE_AWAIT_COMMAND_TIMEOUT
|
|
893
|
+
|
|
894
|
+
if self.com.length < min_resp_length: continue
|
|
895
|
+
if check_bootloader and self.com.peek(2) == b'OK':
|
|
896
|
+
return THREESPACE_AWAIT_BOOTLOADER
|
|
897
|
+
|
|
898
|
+
possible_response = self.com.peekline()
|
|
899
|
+
if b'\r\n' not in possible_response: #failed to get newline
|
|
900
|
+
continue
|
|
901
|
+
|
|
902
|
+
if len(possible_response) < min_resp_length:
|
|
903
|
+
self.__internal_update(self.__try_peek_header())
|
|
904
|
+
continue
|
|
905
|
+
|
|
906
|
+
#Make sure the line is all ascii data
|
|
907
|
+
if not possible_response.isascii():
|
|
908
|
+
self.__internal_update(self.__try_peek_header())
|
|
909
|
+
continue
|
|
910
|
+
|
|
911
|
+
#Check to make sure each potential key conforms to the standard
|
|
912
|
+
key_value_pairs = possible_response.decode().split(';')
|
|
913
|
+
err = False
|
|
914
|
+
for kvp in key_value_pairs:
|
|
915
|
+
if kvp.strip() == THREESPACE_GET_SETTINGS_ERROR_RESPONSE: continue
|
|
916
|
+
split = kvp.split('=')
|
|
917
|
+
if len(split) != 2:
|
|
918
|
+
err = True
|
|
919
|
+
break
|
|
920
|
+
k, v = split
|
|
921
|
+
if any(c in k for c in THREESPACE_SETTING_KEY_INVALID_CHARS):
|
|
922
|
+
err = True
|
|
923
|
+
break
|
|
924
|
+
if err:
|
|
925
|
+
self.__internal_update(self.__try_peek_header())
|
|
926
|
+
continue
|
|
927
|
+
|
|
928
|
+
self.misaligned = False
|
|
929
|
+
return THREESPACE_AWAIT_COMMAND_FOUND
|
|
930
|
+
|
|
931
|
+
#---------------------------------BASE COMMAND PARSING--------------------------------------
|
|
932
|
+
def __try_peek_header(self):
|
|
933
|
+
"""
|
|
934
|
+
Attempts to retrieve a header from the com class immediately.
|
|
935
|
+
|
|
936
|
+
Returns
|
|
937
|
+
-------
|
|
938
|
+
The header retrieved, or None
|
|
939
|
+
"""
|
|
940
|
+
if not self.header_enabled: return None
|
|
941
|
+
if self.com.length < self.header_info.size: return None
|
|
942
|
+
header = self.com.peek(self.header_info.size)
|
|
943
|
+
if len(header) != self.header_info.size: return None
|
|
944
|
+
header = ThreespaceHeader.from_bytes(header, self.header_info)
|
|
945
|
+
return header
|
|
946
|
+
|
|
947
|
+
def __peek_checksum(self, header: ThreespaceHeader, max_data_length=4096):
|
|
948
|
+
"""
|
|
949
|
+
Using a header that contains the checksum and data length, calculate the checksum of the expected
|
|
950
|
+
data and verify if with the checksum in the header.
|
|
758
951
|
|
|
759
|
-
|
|
952
|
+
Params
|
|
953
|
+
------
|
|
954
|
+
header : The header to verify
|
|
955
|
+
max_data_length : The maximum size to allow from header_length. This should be set to avoid a corrupted header with an extremely large length causing a lockup/timeout
|
|
956
|
+
"""
|
|
760
957
|
header_len = len(header.raw_binary)
|
|
958
|
+
if header.length > max_data_length:
|
|
959
|
+
self.log("DATA TOO BIG:", header.length)
|
|
960
|
+
return False
|
|
761
961
|
data = self.com.peek(header_len + header.length)[header_len:]
|
|
762
962
|
if len(data) != header.length: return False
|
|
763
963
|
checksum = sum(data) % 256
|
|
764
964
|
return checksum == header.checksum
|
|
765
965
|
|
|
766
966
|
def __await_command(self, cmd: ThreespaceCommand, timeout=2):
|
|
967
|
+
#Header isn't enabled, nothing can do. Just pretend we found it
|
|
968
|
+
if not self.header_enabled: return THREESPACE_AWAIT_COMMAND_FOUND
|
|
969
|
+
|
|
767
970
|
start_time = time.time()
|
|
768
971
|
|
|
769
972
|
#Update the streaming until the result for this command is next in the buffer
|
|
@@ -772,179 +975,137 @@ class ThreespaceSensor:
|
|
|
772
975
|
return THREESPACE_AWAIT_COMMAND_TIMEOUT
|
|
773
976
|
|
|
774
977
|
#Get potential header
|
|
775
|
-
header = self.
|
|
776
|
-
if
|
|
978
|
+
header = self.__try_peek_header()
|
|
979
|
+
if header is None:
|
|
777
980
|
continue
|
|
778
981
|
|
|
779
|
-
#Check to see what this packet is a response to
|
|
780
|
-
header = ThreespaceHeader.from_bytes(header, self.header_info)
|
|
781
982
|
echo = header.echo
|
|
782
983
|
|
|
783
984
|
if echo == cmd.info.num: #Cmd matches
|
|
784
|
-
if self.__peek_checksum(header):
|
|
985
|
+
if self.__peek_checksum(header, max_data_length=cmd.info.out_size):
|
|
986
|
+
self.misaligned = False
|
|
785
987
|
return THREESPACE_AWAIT_COMMAND_FOUND
|
|
786
988
|
|
|
787
989
|
#Error in packet, go start realigning
|
|
788
990
|
if not self.misaligned:
|
|
789
|
-
|
|
991
|
+
self.log(f"Checksum mismatch for command {cmd.info.num}")
|
|
790
992
|
self.misaligned = True
|
|
791
993
|
self.com.read(1)
|
|
792
994
|
else:
|
|
793
995
|
#It wasn't a response to the command, so may be a response to some internal system
|
|
794
|
-
self.__internal_update(header)
|
|
996
|
+
self.__internal_update(header)
|
|
795
997
|
|
|
796
|
-
|
|
998
|
+
#------------------------------BASE INPUT PARSING--------------------------------------------
|
|
999
|
+
|
|
1000
|
+
def __internal_update(self, header: ThreespaceHeader = None):
|
|
797
1001
|
"""
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
1002
|
+
Manages checking the datastream for asynchronous responses (Streaming, Immediate Debug Messages).
|
|
1003
|
+
If no data is found to match these responses, the data buffer will be considered corrupted/misaligned
|
|
1004
|
+
and start advancing 1 byte at a time until a message is retrieved.
|
|
1005
|
+
For this reason, if waiting for a synchronous command response, this should be only checked after confirming the data
|
|
1006
|
+
is not in response to any synchronously queued commands to avoid removing actual data bytes from the com class.
|
|
1007
|
+
|
|
1008
|
+
Parameters
|
|
1009
|
+
----------
|
|
1010
|
+
header : ThreespaceHeader
|
|
1011
|
+
The header to use for checking if streaming results exist. Can optionally leave None if don't want to check streaming responses.
|
|
1012
|
+
|
|
1013
|
+
Returns
|
|
1014
|
+
--------
|
|
1015
|
+
False : Misalignment
|
|
1016
|
+
True : Internal Data Found/Parsed
|
|
801
1017
|
"""
|
|
802
1018
|
checksum_match = False #Just for debugging
|
|
803
1019
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
if
|
|
808
|
-
self.
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
1020
|
+
if header is not None:
|
|
1021
|
+
#NOTE: FOR THIS TO WORK IT IS REQUIRED THAT THE HEADER DOES NOT CHANGE WHILE STREAMING ANY FORM OF DATA.
|
|
1022
|
+
#IT IS UP TO THE API TO ENFORCE NOT ALLOWING HEADER CHANGES WHILE ANY OF THOSE THINGS ARE HAPPENING
|
|
1023
|
+
if self.is_data_streaming and header.echo == THREESPACE_GET_STREAMING_BATCH_COMMAND_NUM: #Its a streaming packet, so update streaming
|
|
1024
|
+
if checksum_match := self.__peek_checksum(header, max_data_length=self.getStreamingBatchCommand.info.out_size):
|
|
1025
|
+
self.__update_base_streaming()
|
|
1026
|
+
self.misaligned = False
|
|
1027
|
+
return True
|
|
1028
|
+
elif self.is_log_streaming and header.echo == THREESPACE_FILE_READ_BYTES_COMMAND_NUM:
|
|
1029
|
+
if checksum_match := self.__peek_checksum(header, max_data_length=2560): #TODO: Confirm this number can be 2048 and then pound define it. Currently set to 2560 because I know that is big enough, but not optimal
|
|
1030
|
+
self.__update_log_streaming()
|
|
1031
|
+
self.misaligned = False
|
|
1032
|
+
return True
|
|
1033
|
+
elif self.is_file_streaming and header.echo == THREESPACE_FILE_READ_BYTES_COMMAND_NUM:
|
|
1034
|
+
if checksum_match := self.__peek_checksum(header, max_data_length=THREESPACE_FILE_STREAMING_MAX_PACKET_SIZE):
|
|
1035
|
+
self.__update_file_streaming()
|
|
1036
|
+
self.misaligned = False
|
|
1037
|
+
return True
|
|
1038
|
+
|
|
1039
|
+
#Debug messages are possible and there is enough data to potentially be a debug message
|
|
1040
|
+
#NOTE: Firmware should avoid putting more then one \r\n in a debug message as they will be treated as unprocessed/misaligned characters
|
|
1041
|
+
if self.immediate_debug and self.com.length >= 7:
|
|
1042
|
+
#This peek can't be blocking so peekline can't be used
|
|
1043
|
+
potential_message = self.com.peek(min(self.com.length, 27)) #27 is 20 digit timestamp + " Level:"
|
|
1044
|
+
if b"Level:" in potential_message: #There is a debug message somewhere in the data, must validate it is the next item
|
|
1045
|
+
level_index = potential_message.index(b" Level:")
|
|
1046
|
+
partial = potential_message[:level_index]
|
|
1047
|
+
#There should not be a newline until the end of the message, so it shouldn't be in partial
|
|
1048
|
+
if partial.isascii() and partial.decode('ascii').isnumeric() and b'\r\n' not in partial:
|
|
1049
|
+
message = self.com.readline() #Read out the whole message!
|
|
1050
|
+
self.debug_callback(message.decode('ascii'), self)
|
|
1051
|
+
self.misaligned = False
|
|
1052
|
+
return True
|
|
818
1053
|
|
|
819
1054
|
#The response didn't match any of the expected asynchronous streaming API responses, so assume a misalignment
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
self.
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
"""
|
|
830
|
-
if not self.is_streaming: return False
|
|
831
|
-
|
|
832
|
-
#I may need to make this have a max num bytes it will process before exiting to prevent locking up on slower machines
|
|
833
|
-
#due to streaming faster then the program runs
|
|
834
|
-
num_checks = 0
|
|
835
|
-
data_processed = False
|
|
836
|
-
while num_checks < max_checks:
|
|
837
|
-
if self.com.length < self.header_info.size:
|
|
838
|
-
return data_processed
|
|
839
|
-
|
|
840
|
-
#Get header
|
|
841
|
-
header = self.com.peek(self.header_info.size)
|
|
1055
|
+
if header is not None:
|
|
1056
|
+
msg = f"Possible Misalignment or corruption/debug message, header {header} raw {header.raw_binary} {[hex(v) for v in header.raw_binary]}" \
|
|
1057
|
+
f" Checksum match? {checksum_match}"
|
|
1058
|
+
#f"{self.com.peek(min(self.com.length, 10))}"
|
|
1059
|
+
else:
|
|
1060
|
+
msg = "Possible Misalignment or corruption/debug message"
|
|
1061
|
+
#self.log("Misaligned:", self.com.peek(1))
|
|
1062
|
+
self.__handle_misalignment(msg)
|
|
1063
|
+
return False
|
|
842
1064
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
self.
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
return data_processed
|
|
1065
|
+
def __handle_misalignment(self, message: str = None):
|
|
1066
|
+
if not self.misaligned and message is not None:
|
|
1067
|
+
self.log(message)
|
|
1068
|
+
self.misaligned = True
|
|
1069
|
+
self.com.read(1) #Because of expected misalignment, go through buffer 1 by 1 until realigned
|
|
850
1070
|
|
|
1071
|
+
#-----------------------------BASE COMMAND EXECUTION-------------------------------------
|
|
851
1072
|
|
|
852
|
-
def
|
|
853
|
-
if self.is_data_streaming: return
|
|
1073
|
+
def execute_command(self, cmd: ThreespaceCommand, *args):
|
|
854
1074
|
self.check_dirty()
|
|
855
|
-
self.streaming_packets.clear()
|
|
856
1075
|
|
|
857
|
-
|
|
1076
|
+
retries = 0
|
|
1077
|
+
MAX_RETRIES = 3
|
|
1078
|
+
|
|
1079
|
+
while retries < MAX_RETRIES:
|
|
1080
|
+
cmd.send_command(self.com, *args, header_enabled=self.header_enabled)
|
|
1081
|
+
result = self.__await_command(cmd)
|
|
1082
|
+
if result == THREESPACE_AWAIT_COMMAND_FOUND:
|
|
1083
|
+
break
|
|
1084
|
+
retries += 1
|
|
1085
|
+
|
|
1086
|
+
if retries == MAX_RETRIES:
|
|
1087
|
+
raise RuntimeError(f"Failed to get response to command {cmd.info.name}")
|
|
858
1088
|
|
|
859
|
-
self.
|
|
1089
|
+
return self.read_and_parse_command(cmd)
|
|
1090
|
+
|
|
1091
|
+
def __invalid_command(self, *args):
|
|
1092
|
+
raise NotImplementedError("This method is not available.")
|
|
860
1093
|
|
|
861
|
-
|
|
862
|
-
cmd.send_command(self.com, header_enabled=self.header_enabled)
|
|
1094
|
+
def read_and_parse_command(self, cmd: ThreespaceCommand):
|
|
863
1095
|
if self.header_enabled:
|
|
864
|
-
self.__await_command(cmd)
|
|
865
1096
|
header = ThreespaceHeader.from_bytes(self.com.read(self.header_info.size), self.header_info)
|
|
866
1097
|
else:
|
|
867
1098
|
header = ThreespaceHeader()
|
|
868
|
-
self.
|
|
869
|
-
return ThreespaceCmdResult(
|
|
1099
|
+
result, raw = cmd.read_command(self.com, verbose=self.verbose)
|
|
1100
|
+
return ThreespaceCmdResult(result, header, data_raw_binary=raw)
|
|
870
1101
|
|
|
871
|
-
|
|
872
|
-
"""
|
|
873
|
-
This function is used to stop streaming without validating it was streaming and ignoring any output of the
|
|
874
|
-
communication line. This is a destructive call that will lose data, but will gurantee stopping streaming
|
|
875
|
-
and leave the communication line in a clean state
|
|
876
|
-
"""
|
|
877
|
-
cached_header_enabled = self.header_enabled
|
|
878
|
-
cahched_dirty = self.dirty_cache
|
|
879
|
-
|
|
880
|
-
#Must set these to gurantee it doesn't try and parse a response from anything
|
|
881
|
-
self.dirty_cache = False
|
|
882
|
-
self.header_enabled = False #Keep off for the attempt at stop streaming since if in an invalid state, won't be able to get response
|
|
883
|
-
self.stopStreaming() #Just in case was streaming
|
|
884
|
-
self.fileStopStream()
|
|
885
|
-
|
|
886
|
-
#TODO: Change this to pause the data logging instead, then check the state and update
|
|
887
|
-
self.stopDataLogging()
|
|
888
|
-
|
|
889
|
-
#Restore
|
|
890
|
-
self.header_enabled = cached_header_enabled
|
|
891
|
-
self.dirty_cache = cahched_dirty
|
|
892
|
-
|
|
893
|
-
def stopStreaming(self):
|
|
894
|
-
self.check_dirty()
|
|
895
|
-
cmd = self.commands[86]
|
|
896
|
-
cmd.send_command(self.com, header_enabled=self.header_enabled)
|
|
897
|
-
if self.header_enabled: #Header will be enabled while streaming, but this is useful for startup
|
|
898
|
-
self.__await_command(cmd)
|
|
899
|
-
header = ThreespaceHeader.from_bytes(self.com.read(self.header_info.size), self.header_info)
|
|
900
|
-
else:
|
|
901
|
-
header = ThreespaceHeader()
|
|
902
|
-
time.sleep(0.05)
|
|
903
|
-
while self.com.length:
|
|
904
|
-
self.com.read_all()
|
|
905
|
-
self.is_data_streaming = False
|
|
906
|
-
return ThreespaceCmdResult(None, header)
|
|
907
|
-
|
|
908
|
-
def set_cached_settings_dirty(self):
|
|
909
|
-
"""
|
|
910
|
-
Could be streaming settings, header settings...
|
|
911
|
-
Basically the sensor needs reinitialized
|
|
912
|
-
"""
|
|
913
|
-
self.dirty_cache = True
|
|
1102
|
+
#-----------------------------------BASE STREAMING COMMMANDS----------------------------------------------
|
|
914
1103
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
com class with the same serial number. Useful for re-enumeration, such as when
|
|
919
|
-
entering bootloader and using USB
|
|
920
|
-
"""
|
|
921
|
-
for potential_com in self.com.auto_detect():
|
|
922
|
-
potential_com.open()
|
|
923
|
-
sensor = ThreespaceSensor(potential_com)
|
|
924
|
-
if sensor.serial_number == self.serial_number:
|
|
925
|
-
self.com = potential_com
|
|
926
|
-
return True
|
|
927
|
-
sensor.cleanup() #Handles closing the potential_com
|
|
928
|
-
return False
|
|
929
|
-
|
|
930
|
-
def check_dirty(self):
|
|
931
|
-
if not self.dirty_cache: return
|
|
932
|
-
if self.com.reenumerates and not self.com.check_open(): #Must check this, as could have transitioned from bootloader to firmware or vice versa and just needs re-opened/detected
|
|
933
|
-
success = self.__attempt_rediscover_self()
|
|
934
|
-
if not success:
|
|
935
|
-
raise RuntimeError("Sensor connection lost")
|
|
936
|
-
|
|
937
|
-
self._force_stop_streaming() #Can't be streaming when checking the dirty cache. If you want to stream, don't do things that cause the object to go dirty.
|
|
938
|
-
was_in_bootloader = self.__cached_in_bootloader
|
|
939
|
-
self.__cached_in_bootloader = self.__check_bootloader_status()
|
|
940
|
-
|
|
941
|
-
if was_in_bootloader and not self.__cached_in_bootloader: #Just Exited bootloader, need to fully reinit
|
|
942
|
-
self.__firmware_init()
|
|
943
|
-
elif not self.__cached_in_bootloader: #Was already in firmware, so only need to partially reinit
|
|
944
|
-
self.__reinit_firmware() #Partially init when just naturally dirty
|
|
945
|
-
self.dirty_cache = False
|
|
1104
|
+
@property
|
|
1105
|
+
def is_streaming(self):
|
|
1106
|
+
return self.is_data_streaming or self.is_log_streaming or self.is_file_streaming
|
|
946
1107
|
|
|
947
|
-
def
|
|
1108
|
+
def __cache_streaming_settings(self):
|
|
948
1109
|
cached_slots: list[ThreespaceCommand] = []
|
|
949
1110
|
slots: str = self.get_settings("stream_slots")
|
|
950
1111
|
slots = slots.split(',')
|
|
@@ -961,32 +1122,21 @@ class ThreespaceSensor:
|
|
|
961
1122
|
if command == None: continue
|
|
962
1123
|
self.streaming_packet_size += command.info.out_size
|
|
963
1124
|
|
|
964
|
-
def
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
self.set_settings(header=self.header_info.bitfield)
|
|
980
|
-
return
|
|
981
|
-
|
|
982
|
-
if required_header != header:
|
|
983
|
-
print(f"Forcing header checksum, echo, and length enabled")
|
|
984
|
-
self.set_settings(header=required_header)
|
|
985
|
-
return
|
|
986
|
-
|
|
987
|
-
#Current/New header is valid, so can cache it
|
|
988
|
-
self.header_info.bitfield = header
|
|
989
|
-
self.cmd_echo_byte_index = self.header_info.get_start_byte(THREESPACE_HEADER_ECHO_BIT) #Needed for cmd validation while streaming
|
|
1125
|
+
def startStreaming(self) -> ThreespaceCmdResult[None]: ...
|
|
1126
|
+
def __startStreaming(self) -> ThreespaceCmdResult[None]:
|
|
1127
|
+
if self.is_data_streaming: return
|
|
1128
|
+
self.streaming_packets.clear()
|
|
1129
|
+
self.__cache_streaming_settings()
|
|
1130
|
+
|
|
1131
|
+
result = self.execute_command(self.commands[THREESPACE_START_STREAMING_COMMAND_NUM])
|
|
1132
|
+
self.is_data_streaming = True
|
|
1133
|
+
return result
|
|
1134
|
+
|
|
1135
|
+
def stopStreaming(self) -> ThreespaceCmdResult[None]: ...
|
|
1136
|
+
def __stopStreaming(self) -> ThreespaceCmdResult[None]:
|
|
1137
|
+
result = self.execute_command(self.commands[THREESPACE_STOP_STREAMING_COMMAND_NUM])
|
|
1138
|
+
self.is_data_streaming = False
|
|
1139
|
+
return result
|
|
990
1140
|
|
|
991
1141
|
def __update_base_streaming(self):
|
|
992
1142
|
"""
|
|
@@ -1007,44 +1157,86 @@ class ThreespaceSensor:
|
|
|
1007
1157
|
def clearStreamingPackets(self):
|
|
1008
1158
|
self.streaming_packets.clear()
|
|
1009
1159
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1160
|
+
#This is called for all streaming types
|
|
1161
|
+
def updateStreaming(self, max_checks=float('inf')):
|
|
1162
|
+
"""
|
|
1163
|
+
Returns true if any amount of data was processed whether valid or not. This is called for all streaming types.
|
|
1164
|
+
"""
|
|
1165
|
+
if not self.is_streaming: return False
|
|
1013
1166
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1167
|
+
#I may need to make this have a max num bytes it will process before exiting to prevent locking up on slower machines
|
|
1168
|
+
#due to streaming faster then the program runs
|
|
1169
|
+
num_checks = 0
|
|
1170
|
+
data_processed = False
|
|
1171
|
+
while num_checks < max_checks:
|
|
1172
|
+
if self.com.length < self.header_info.size:
|
|
1173
|
+
return data_processed
|
|
1174
|
+
|
|
1175
|
+
#Get header
|
|
1176
|
+
header = self.com.peek(self.header_info.size)
|
|
1177
|
+
|
|
1178
|
+
#Get the header and send it to the internal update
|
|
1179
|
+
header = ThreespaceHeader.from_bytes(header, self.header_info)
|
|
1180
|
+
self.__internal_update(header)
|
|
1181
|
+
data_processed = True #Internal update always processes data. Either reads a streaming message, or advances buffer due to misalignment
|
|
1182
|
+
num_checks += 1
|
|
1017
1183
|
|
|
1018
|
-
|
|
1019
|
-
header = ThreespaceHeader.from_bytes(self.com.read(self.header_info.size), self.header_info)
|
|
1020
|
-
else:
|
|
1021
|
-
header = ThreespaceHeader()
|
|
1184
|
+
return data_processed
|
|
1022
1185
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1186
|
+
#This is more so used for initialization. Its a way of stopping streaming without having to worry about parsing.
|
|
1187
|
+
#That way it can clean up the data stream that won't match the expected state if not already configured.
|
|
1188
|
+
def _force_stop_streaming(self):
|
|
1189
|
+
"""
|
|
1190
|
+
This function attempts to stop all possible streaming without knowing anything about the state of the sensor.
|
|
1191
|
+
This includes trying to stop before any commands are even registered as valid. This is to ensure the sensor can properly
|
|
1192
|
+
start and recover from error conditions.
|
|
1026
1193
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1194
|
+
This will stop streaming without validating it was streaming and ignoring any output of the
|
|
1195
|
+
communication line. This is a destructive call that will lose data, but will gurantee stopping streaming
|
|
1196
|
+
and leave the communication line in a clean state.
|
|
1197
|
+
"""
|
|
1198
|
+
cached_header_enabled = self.header_enabled
|
|
1199
|
+
cahched_dirty = self.dirty_cache
|
|
1031
1200
|
|
|
1032
|
-
|
|
1033
|
-
|
|
1201
|
+
#Must set these to gurantee it doesn't try and parse a response from anything since don't know the state of header
|
|
1202
|
+
self.dirty_cache = False
|
|
1203
|
+
self.header_enabled = False #Keep off for the attempt at stop streaming since if in an invalid state, won't be able to get response
|
|
1034
1204
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1205
|
+
#NOTE that commands are accessed directly from the global table instead of commands registered to this sensor object
|
|
1206
|
+
#since this sensor object may have yet to register these commands when calling force_stop_streaming
|
|
1207
|
+
|
|
1208
|
+
#Stop base Streaming
|
|
1209
|
+
self.execute_command(threespaceCommandGetByName("stopStreaming"))
|
|
1210
|
+
self.is_data_streaming = False
|
|
1211
|
+
|
|
1212
|
+
#Stop file streaming
|
|
1213
|
+
self.execute_command(threespaceCommandGetByName("fileStopStream"))
|
|
1214
|
+
self.is_file_streaming = False
|
|
1215
|
+
|
|
1216
|
+
#Stop logging streaming
|
|
1217
|
+
# #TODO: Change this to pause the data logging instead, then check the state and update
|
|
1218
|
+
self.execute_command(threespaceCommandGetByName("stopDataLogging"))
|
|
1219
|
+
self.is_log_streaming = False
|
|
1040
1220
|
|
|
1041
|
-
#
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1221
|
+
#Restore
|
|
1222
|
+
self.header_enabled = cached_header_enabled
|
|
1223
|
+
self.dirty_cache = cahched_dirty
|
|
1224
|
+
|
|
1225
|
+
#-------------------------------------FILE STREAMING----------------------------------------------
|
|
1045
1226
|
|
|
1227
|
+
def fileStartStream(self) -> ThreespaceCmdResult[int]: ...
|
|
1228
|
+
def __fileStartStream(self) -> ThreespaceCmdResult[int]:
|
|
1229
|
+
result = self.execute_command(self.__get_command("fileStartStream"))
|
|
1230
|
+
self.file_stream_length = result.data
|
|
1231
|
+
if self.file_stream_length > 0:
|
|
1232
|
+
self.is_file_streaming = True
|
|
1233
|
+
return result
|
|
1234
|
+
|
|
1235
|
+
def fileStopStream(self) -> ThreespaceCmdResult[None]: ...
|
|
1236
|
+
def __fileStopStream(self) -> ThreespaceCmdResult[None]:
|
|
1237
|
+
result = self.execute_command(self.__get_command("fileStopStream"))
|
|
1046
1238
|
self.is_file_streaming = False
|
|
1047
|
-
return
|
|
1239
|
+
return result
|
|
1048
1240
|
|
|
1049
1241
|
def getFileStreamData(self):
|
|
1050
1242
|
to_return = self.file_stream_data.copy()
|
|
@@ -1062,52 +1254,32 @@ class ThreespaceSensor:
|
|
|
1062
1254
|
data = self.com.read(header.length)
|
|
1063
1255
|
self.file_stream_data += data
|
|
1064
1256
|
self.file_stream_length -= header.length
|
|
1065
|
-
if header.length <
|
|
1257
|
+
if header.length < THREESPACE_FILE_STREAMING_MAX_PACKET_SIZE or self.file_stream_length == 0: #File streaming sends in chunks of 512. If not 512, it must be the last packet
|
|
1066
1258
|
self.is_file_streaming = False
|
|
1067
1259
|
if self.file_stream_length != 0:
|
|
1068
|
-
|
|
1260
|
+
self.log(f"File streaming stopped due to last packet. However still expected {self.file_stream_length} more bytes.")
|
|
1069
1261
|
|
|
1070
|
-
|
|
1071
|
-
self.check_dirty()
|
|
1262
|
+
#----------------------------DATA LOGGING--------------------------------------
|
|
1072
1263
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1264
|
+
def startDataLogging(self) -> ThreespaceCmdResult[None]: ...
|
|
1265
|
+
def __startDataLogging(self) -> ThreespaceCmdResult[None]:
|
|
1266
|
+
self.__cache_streaming_settings()
|
|
1075
1267
|
|
|
1076
1268
|
#Must check whether streaming is being done alongside logging or not. Also configure required settings if it is
|
|
1077
1269
|
streaming = bool(int(self.get_settings("log_immediate_output")))
|
|
1078
1270
|
if streaming:
|
|
1079
1271
|
self.set_settings(log_immediate_output_header_enabled=1,
|
|
1080
1272
|
log_immediate_output_header_mode=THREESPACE_OUTPUT_MODE_BINARY) #Must have header enabled in the log messages for this to work and must use binary for the header
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
if self.header_enabled:
|
|
1084
|
-
self.__await_command(cmd)
|
|
1085
|
-
header = ThreespaceHeader.from_bytes(self.com.read(self.header_info.size), self.header_info)
|
|
1086
|
-
else:
|
|
1087
|
-
header = ThreespaceHeader()
|
|
1088
|
-
|
|
1273
|
+
|
|
1274
|
+
result = self.execute_command(self.__get_command("startDataLogging"))
|
|
1089
1275
|
self.is_log_streaming = streaming
|
|
1090
|
-
return
|
|
1091
|
-
|
|
1092
|
-
def stopDataLogging(self) -> ThreespaceCmdResult[None]:
|
|
1093
|
-
self.check_dirty()
|
|
1094
|
-
|
|
1095
|
-
cmd = self.__get_command("__stopDataLogging")
|
|
1096
|
-
cmd.send_command(self.com, header_enabled=self.header_enabled)
|
|
1097
|
-
|
|
1098
|
-
if self.header_enabled: #Header will be enabled while streaming, but this is useful for startup
|
|
1099
|
-
self.__await_command(cmd)
|
|
1100
|
-
header = ThreespaceHeader.from_bytes(self.com.read(self.header_info.size), self.header_info)
|
|
1101
|
-
else:
|
|
1102
|
-
header = ThreespaceHeader()
|
|
1103
|
-
#TODO: Remove me now that realignment exists and multiple things can be streaming at once
|
|
1104
|
-
if self.is_log_streaming:
|
|
1105
|
-
time.sleep(0.05)
|
|
1106
|
-
while self.com.length:
|
|
1107
|
-
self.com.read_all()
|
|
1276
|
+
return result
|
|
1108
1277
|
|
|
1278
|
+
def stopDataLogging(self) -> ThreespaceCmdResult[None]: ...
|
|
1279
|
+
def __stopDataLogging(self) -> ThreespaceCmdResult[None]:
|
|
1280
|
+
result = self.execute_command(self.__get_command("stopDataLogging"))
|
|
1109
1281
|
self.is_log_streaming = False
|
|
1110
|
-
return
|
|
1282
|
+
return result
|
|
1111
1283
|
|
|
1112
1284
|
def __update_log_streaming(self):
|
|
1113
1285
|
"""
|
|
@@ -1119,23 +1291,26 @@ class ThreespaceSensor:
|
|
|
1119
1291
|
data = self.com.read(header.length)
|
|
1120
1292
|
self.file_stream_data += data
|
|
1121
1293
|
|
|
1122
|
-
|
|
1294
|
+
#---------------------------------POWER STATE CHANGING COMMANDS & BOOTLOADER------------------------------------
|
|
1295
|
+
|
|
1296
|
+
def softwareReset(self): ...
|
|
1297
|
+
def __softwareReset(self):
|
|
1123
1298
|
self.check_dirty()
|
|
1124
|
-
cmd = self.commands[
|
|
1299
|
+
cmd = self.commands[THREESPACE_SOFTWARE_RESET_COMMAND_NUM]
|
|
1125
1300
|
cmd.send_command(self.com)
|
|
1126
1301
|
self.com.close()
|
|
1302
|
+
#TODO: Make this actually wait instead of an arbitrary sleep length
|
|
1127
1303
|
time.sleep(0.5) #Give it time to restart
|
|
1128
1304
|
self.com.open()
|
|
1129
|
-
if self.immediate_debug:
|
|
1130
|
-
time.sleep(2) #An additional 2 seconds to ensure can clear all debug messages
|
|
1131
|
-
self.com.read_all()
|
|
1132
1305
|
self.__firmware_init()
|
|
1133
1306
|
|
|
1134
|
-
def enterBootloader(self):
|
|
1307
|
+
def enterBootloader(self): ...
|
|
1308
|
+
def __enterBootloader(self):
|
|
1135
1309
|
if self.in_bootloader: return
|
|
1136
1310
|
|
|
1137
|
-
cmd = self.commands[
|
|
1311
|
+
cmd = self.commands[THREESPACE_ENTER_BOOTLOADER_COMMAND_NUM]
|
|
1138
1312
|
cmd.send_command(self.com)
|
|
1313
|
+
#TODO: Make this actually wait instead of an arbitrary sleep length
|
|
1139
1314
|
time.sleep(0.5) #Give it time to boot into bootloader
|
|
1140
1315
|
if self.com.reenumerates:
|
|
1141
1316
|
self.com.close()
|
|
@@ -1173,12 +1348,20 @@ class ThreespaceSensor:
|
|
|
1173
1348
|
#By then adding a ?UUU, that will trigger a <KEY_ERROR> if in firmware. So, can tell if in bootloader or firmware by checking for OK or <KEY_ERROR>
|
|
1174
1349
|
bootloader = False
|
|
1175
1350
|
self.com.write("UUU?UUU\n".encode())
|
|
1176
|
-
response = self.
|
|
1177
|
-
if
|
|
1178
|
-
|
|
1179
|
-
|
|
1351
|
+
response = self.__await_get_settings(2, check_bootloader=True)
|
|
1352
|
+
if response == THREESPACE_AWAIT_COMMAND_TIMEOUT:
|
|
1353
|
+
self.log("Requested Bootloader, Got:")
|
|
1354
|
+
self.log(self.com.peek(self.com.length))
|
|
1355
|
+
raise RuntimeError("Failed to discover bootloader or firmware.")
|
|
1356
|
+
if response == THREESPACE_AWAIT_BOOTLOADER:
|
|
1180
1357
|
bootloader = True
|
|
1181
|
-
|
|
1358
|
+
time.sleep(0.1) #Give time for all the OK responses to come in
|
|
1359
|
+
self.com.read_all() #Remove the rest of the OK responses or the rest of the <KEY_ERROR> response
|
|
1360
|
+
elif response == THREESPACE_AWAIT_COMMAND_FOUND:
|
|
1361
|
+
bootloader = False
|
|
1362
|
+
self.com.readline() #Clear the setting, no need to parse
|
|
1363
|
+
else:
|
|
1364
|
+
raise Exception("Failed to detect if in bootloader or firmware")
|
|
1182
1365
|
return bootloader
|
|
1183
1366
|
|
|
1184
1367
|
def bootloader_get_sn(self):
|
|
@@ -1198,11 +1381,6 @@ class ThreespaceSensor:
|
|
|
1198
1381
|
success = self.__attempt_rediscover_self()
|
|
1199
1382
|
if not success:
|
|
1200
1383
|
raise RuntimeError("Failed to reconnect to sensor in firmware")
|
|
1201
|
-
self.com.read_all() #If debug_mode=1, might be debug messages waiting
|
|
1202
|
-
if self.immediate_debug:
|
|
1203
|
-
print("Waiting longer before booting into firmware because immediate debug was enabled.")
|
|
1204
|
-
time.sleep(2)
|
|
1205
|
-
self.com.read_all()
|
|
1206
1384
|
in_bootloader = self.__check_bootloader_status()
|
|
1207
1385
|
if in_bootloader:
|
|
1208
1386
|
raise RuntimeError("Failed to exit bootloader")
|
|
@@ -1256,299 +1434,134 @@ class ThreespaceSensor:
|
|
|
1256
1434
|
self.fileStopStream()
|
|
1257
1435
|
if self.is_log_streaming:
|
|
1258
1436
|
self.stopDataLogging()
|
|
1259
|
-
|
|
1437
|
+
|
|
1438
|
+
#The sensor may or may not have this command registered. So just try it
|
|
1439
|
+
try:
|
|
1440
|
+
#May not be opened, but also not cacheing that so just attempt to close.
|
|
1441
|
+
self.closeFile()
|
|
1442
|
+
except: pass
|
|
1260
1443
|
self.com.close()
|
|
1261
1444
|
|
|
1262
1445
|
#-------------------------START ALL PROTOTYPES------------------------------------
|
|
1263
1446
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
def eeptsGetOldestStep(self) -> ThreespaceCmdResult[list]:
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
def
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
def
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
def
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
def
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
def
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
def
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
def
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
def
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
def
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
def
|
|
1301
|
-
raise NotImplementedError("This method is not available.")
|
|
1302
|
-
|
|
1303
|
-
def getTaredOrientationAsAxisAngles(self) -> ThreespaceCmdResult[list[float]]:
|
|
1304
|
-
raise NotImplementedError("This method is not available.")
|
|
1305
|
-
|
|
1306
|
-
def getTaredOrientationAsTwoVector(self) -> ThreespaceCmdResult[list[float]]:
|
|
1307
|
-
raise NotImplementedError("This method is not available.")
|
|
1308
|
-
|
|
1309
|
-
def getDifferenceQuaternion(self) -> ThreespaceCmdResult[list[float]]:
|
|
1310
|
-
raise NotImplementedError("This method is not available.")
|
|
1311
|
-
|
|
1312
|
-
def getUntaredOrientation(self) -> ThreespaceCmdResult[list[float]]:
|
|
1313
|
-
raise NotImplementedError("This method is not available.")
|
|
1314
|
-
|
|
1315
|
-
def getUntaredOrientationAsEulerAngles(self) -> ThreespaceCmdResult[list[float]]:
|
|
1316
|
-
raise NotImplementedError("This method is not available.")
|
|
1317
|
-
|
|
1318
|
-
def getUntaredOrientationAsRotationMatrix(self) -> ThreespaceCmdResult[list[float]]:
|
|
1319
|
-
raise NotImplementedError("This method is not available.")
|
|
1320
|
-
|
|
1321
|
-
def getUntaredOrientationAsAxisAngles(self) -> ThreespaceCmdResult[list[float]]:
|
|
1322
|
-
raise NotImplementedError("This method is not available.")
|
|
1323
|
-
|
|
1324
|
-
def getUntaredOrientationAsTwoVector(self) -> ThreespaceCmdResult[list[float]]:
|
|
1325
|
-
raise NotImplementedError("This method is not available.")
|
|
1326
|
-
|
|
1327
|
-
def commitSettings(self) -> ThreespaceCmdResult[None]:
|
|
1328
|
-
raise NotImplementedError("This method is not available.")
|
|
1329
|
-
|
|
1330
|
-
def getMotionlessConfidenceFactor(self) -> ThreespaceCmdResult[float]:
|
|
1331
|
-
raise NotImplementedError("This method is not available.")
|
|
1332
|
-
|
|
1333
|
-
def enableMSC(self) -> ThreespaceCmdResult[None]:
|
|
1334
|
-
raise NotImplementedError("This method is not available.")
|
|
1335
|
-
|
|
1336
|
-
def disableMSC(self) -> ThreespaceCmdResult[None]:
|
|
1337
|
-
raise NotImplementedError("This method is not available.")
|
|
1338
|
-
|
|
1339
|
-
def getNextDirectoryItem(self) -> ThreespaceCmdResult[list[int,str,int]]:
|
|
1340
|
-
raise NotImplementedError("This method is not available.")
|
|
1341
|
-
|
|
1342
|
-
def changeDirectory(self, path: str) -> ThreespaceCmdResult[None]:
|
|
1343
|
-
raise NotImplementedError("This method is not available.")
|
|
1344
|
-
|
|
1345
|
-
def openFile(self, path: str) -> ThreespaceCmdResult[None]:
|
|
1346
|
-
raise NotImplementedError("This method is not available.")
|
|
1347
|
-
|
|
1348
|
-
def closeFile(self) -> ThreespaceCmdResult[None]:
|
|
1349
|
-
raise NotImplementedError("This method is not available.")
|
|
1350
|
-
|
|
1351
|
-
def fileGetRemainingSize(self) -> ThreespaceCmdResult[int]:
|
|
1352
|
-
raise NotImplementedError("This method is not available.")
|
|
1353
|
-
|
|
1354
|
-
def fileReadLine(self) -> ThreespaceCmdResult[str]:
|
|
1355
|
-
raise NotImplementedError("This method is not available.")
|
|
1356
|
-
|
|
1357
|
-
def fileReadBytes(self, num_bytes: int) -> ThreespaceCmdResult[bytes]:
|
|
1447
|
+
#To actually see how commands work, look at __initialize_commands and __add_command
|
|
1448
|
+
#But basically, these are all just prototypes. Information about the commands is in the table
|
|
1449
|
+
#beneath here, and the API simply calls its execute_command function on the Command information objects defined.
|
|
1450
|
+
|
|
1451
|
+
def eeptsStart(self) -> ThreespaceCmdResult[None]: ...
|
|
1452
|
+
def eeptsStop(self) -> ThreespaceCmdResult[None]: ...
|
|
1453
|
+
def eeptsGetOldestStep(self) -> ThreespaceCmdResult[list]: ...
|
|
1454
|
+
def eeptsGetNewestStep(self) -> ThreespaceCmdResult[list]: ...
|
|
1455
|
+
def eeptsGetNumStepsAvailable(self) -> ThreespaceCmdResult[int]: ...
|
|
1456
|
+
def eeptsInsertGPS(self, latitude: float, longitude: float) -> ThreespaceCmdResult[None]: ...
|
|
1457
|
+
def eeptsAutoOffset(self) -> ThreespaceCmdResult[None]: ...
|
|
1458
|
+
def getRawGyroRate(self, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1459
|
+
def getRawAccelVec(self, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1460
|
+
def getRawMagVec(self, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1461
|
+
def getTaredOrientation(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1462
|
+
def getTaredOrientationAsEulerAngles(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1463
|
+
def getTaredOrientationAsRotationMatrix(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1464
|
+
def getTaredOrientationAsAxisAngles(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1465
|
+
def getTaredOrientationAsTwoVector(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1466
|
+
def getDifferenceQuaternion(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1467
|
+
def getUntaredOrientation(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1468
|
+
def getUntaredOrientationAsEulerAngles(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1469
|
+
def getUntaredOrientationAsRotationMatrix(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1470
|
+
def getUntaredOrientationAsAxisAngles(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1471
|
+
def getUntaredOrientationAsTwoVector(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1472
|
+
def commitSettings(self) -> ThreespaceCmdResult[None]: ...
|
|
1473
|
+
def getMotionlessConfidenceFactor(self) -> ThreespaceCmdResult[float]: ...
|
|
1474
|
+
def enableMSC(self) -> ThreespaceCmdResult[None]: ...
|
|
1475
|
+
def disableMSC(self) -> ThreespaceCmdResult[None]: ...
|
|
1476
|
+
def getNextDirectoryItem(self) -> ThreespaceCmdResult[list[int,str,int]]: ...
|
|
1477
|
+
def changeDirectory(self, path: str) -> ThreespaceCmdResult[None]: ...
|
|
1478
|
+
def openFile(self, path: str) -> ThreespaceCmdResult[None]: ...
|
|
1479
|
+
def closeFile(self) -> ThreespaceCmdResult[None]: ...
|
|
1480
|
+
def fileGetRemainingSize(self) -> ThreespaceCmdResult[int]: ...
|
|
1481
|
+
def fileReadLine(self) -> ThreespaceCmdResult[str]: ...
|
|
1482
|
+
def fileReadBytes(self, num_bytes: int) -> ThreespaceCmdResult[bytes]: ...
|
|
1483
|
+
def __fileReadBytes(self, num_bytes: int) -> ThreespaceCmdResult[bytes]:
|
|
1358
1484
|
self.check_dirty()
|
|
1359
1485
|
cmd = self.commands[THREESPACE_FILE_READ_BYTES_COMMAND_NUM]
|
|
1360
1486
|
cmd.send_command(self.com, num_bytes, header_enabled=self.header_enabled)
|
|
1361
1487
|
self.__await_command(cmd)
|
|
1362
1488
|
if self.header_enabled:
|
|
1363
1489
|
header = ThreespaceHeader.from_bytes(self.com.read(self.header_info.size), self.header_info)
|
|
1490
|
+
num_bytes = min(num_bytes, header.length) #Its possible for less bytes to be returned when an error occurs (EX: Reading from unopened file)
|
|
1364
1491
|
else:
|
|
1365
1492
|
header = ThreespaceHeader()
|
|
1366
1493
|
|
|
1367
1494
|
response = self.com.read(num_bytes)
|
|
1368
1495
|
return ThreespaceCmdResult(response, header, data_raw_binary=response)
|
|
1369
1496
|
|
|
1370
|
-
def deleteFile(self, path: str) -> ThreespaceCmdResult[None]:
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
def
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
def
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
def
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
def
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
def
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
def
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
def
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
def
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
def
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
def
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
def
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
def
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
def
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
def
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
def
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
def
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
def
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
def
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
def
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
def
|
|
1431
|
-
raise NotImplementedError("This method is not available.")
|
|
1432
|
-
|
|
1433
|
-
def getTemperatureCelsius(self) -> ThreespaceCmdResult[float]:
|
|
1434
|
-
raise NotImplementedError("This method is not available.")
|
|
1435
|
-
|
|
1436
|
-
def getTemperatureFahrenheit(self) -> ThreespaceCmdResult[float]:
|
|
1437
|
-
raise NotImplementedError("This method is not available.")
|
|
1438
|
-
|
|
1439
|
-
def getNormalizedGyroRate(self, id: int) -> ThreespaceCmdResult[list[float]]:
|
|
1440
|
-
raise NotImplementedError("This method is not available.")
|
|
1441
|
-
|
|
1442
|
-
def getNormalizedAccelVec(self, id: int) -> ThreespaceCmdResult[list[float]]:
|
|
1443
|
-
raise NotImplementedError("This method is not available.")
|
|
1444
|
-
|
|
1445
|
-
def getNormalizedMagVec(self, id: int) -> ThreespaceCmdResult[list[float]]:
|
|
1446
|
-
raise NotImplementedError("This method is not available.")
|
|
1447
|
-
|
|
1448
|
-
def getCorrectedGyroRate(self, id: int) -> ThreespaceCmdResult[list[float]]:
|
|
1449
|
-
raise NotImplementedError("This method is not available.")
|
|
1450
|
-
|
|
1451
|
-
def getCorrectedAccelVec(self, id: int) -> ThreespaceCmdResult[list[float]]:
|
|
1452
|
-
raise NotImplementedError("This method is not available.")
|
|
1453
|
-
|
|
1454
|
-
def getCorrectedMagVec(self, id: int) -> ThreespaceCmdResult[list[float]]:
|
|
1455
|
-
raise NotImplementedError("This method is not available.")
|
|
1456
|
-
|
|
1457
|
-
def enableMSC(self) -> ThreespaceCmdResult[None]:
|
|
1458
|
-
raise NotImplementedError("This method is not available.")
|
|
1459
|
-
|
|
1460
|
-
def disableMSC(self) -> ThreespaceCmdResult[None]:
|
|
1461
|
-
raise NotImplementedError("This method is not available.")
|
|
1462
|
-
|
|
1463
|
-
def getTimestamp(self) -> ThreespaceCmdResult[int]:
|
|
1464
|
-
raise NotImplementedError("This method is not available.")
|
|
1465
|
-
|
|
1466
|
-
def getBatteryVoltage(self) -> ThreespaceCmdResult[float]:
|
|
1467
|
-
raise NotImplementedError("This method is not available.")
|
|
1468
|
-
|
|
1469
|
-
def getBatteryPercent(self) -> ThreespaceCmdResult[int]:
|
|
1470
|
-
raise NotImplementedError("This method is not available.")
|
|
1471
|
-
|
|
1472
|
-
def getBatteryStatus(self) -> ThreespaceCmdResult[int]:
|
|
1473
|
-
raise NotImplementedError("This method is not available.")
|
|
1474
|
-
|
|
1475
|
-
def getGpsCoord(self) -> ThreespaceCmdResult[list[float]]:
|
|
1476
|
-
raise NotImplementedError("This method is not available.")
|
|
1477
|
-
|
|
1478
|
-
def getGpsAltitude(self) -> ThreespaceCmdResult[float]:
|
|
1479
|
-
raise NotImplementedError("This method is not available.")
|
|
1480
|
-
|
|
1481
|
-
def getGpsFixState(self) -> ThreespaceCmdResult[int]:
|
|
1482
|
-
raise NotImplementedError("This method is not available.")
|
|
1483
|
-
|
|
1484
|
-
def getGpsHdop(self) -> ThreespaceCmdResult[float]:
|
|
1485
|
-
raise NotImplementedError("This method is not available.")
|
|
1486
|
-
|
|
1487
|
-
def getGpsSatellites(self) -> ThreespaceCmdResult[int]:
|
|
1488
|
-
raise NotImplementedError("This method is not available.")
|
|
1489
|
-
|
|
1490
|
-
def getButtonState(self) -> ThreespaceCmdResult[int]:
|
|
1491
|
-
raise NotImplementedError("This method is not available.")
|
|
1492
|
-
|
|
1493
|
-
def correctRawGyroData(self, x: float, y: float, z: float, id: int) -> ThreespaceCmdResult[list[float]]:
|
|
1494
|
-
raise NotImplementedError("This method is not available.")
|
|
1495
|
-
|
|
1496
|
-
def correctRawAccelData(self, x: float, y: float, z: float, id: int) -> ThreespaceCmdResult[list[float]]:
|
|
1497
|
-
raise NotImplementedError("This method is not available.")
|
|
1498
|
-
|
|
1499
|
-
def correctRawMagData(self, x: float, y: float, z: float, id: int) -> ThreespaceCmdResult[list[float]]:
|
|
1500
|
-
raise NotImplementedError("This method is not available.")
|
|
1501
|
-
|
|
1502
|
-
def formatSd(self) -> ThreespaceCmdResult[None]:
|
|
1503
|
-
raise NotImplementedError("This method is not available.")
|
|
1504
|
-
|
|
1505
|
-
def setDateTime(self, year: int, month: int, day: int, hour: int, minute: int, second: int) -> ThreespaceCmdResult[None]:
|
|
1506
|
-
raise NotImplementedError("This method is not available.")
|
|
1507
|
-
|
|
1508
|
-
def getDateTime(self) -> ThreespaceCmdResult[list[int]]:
|
|
1509
|
-
raise NotImplementedError("This method is not available.")
|
|
1510
|
-
|
|
1511
|
-
def tareWithCurrentOrientation(self) -> ThreespaceCmdResult[None]:
|
|
1512
|
-
raise NotImplementedError("This method is not available.")
|
|
1513
|
-
|
|
1514
|
-
def setBaseTareWithCurrentOrientation(self) -> ThreespaceCmdResult[None]:
|
|
1515
|
-
raise NotImplementedError("This method is not available.")
|
|
1516
|
-
|
|
1517
|
-
def resetFilter(self) -> ThreespaceCmdResult[None]:
|
|
1518
|
-
raise NotImplementedError("This method is not available.")
|
|
1519
|
-
|
|
1520
|
-
def getNumDebugMessages(self) -> ThreespaceCmdResult[int]:
|
|
1521
|
-
raise NotImplementedError("This method is not available.")
|
|
1522
|
-
|
|
1523
|
-
def getOldestDebugMessage(self) -> ThreespaceCmdResult[str]:
|
|
1524
|
-
raise NotImplementedError("This method is not available.")
|
|
1525
|
-
|
|
1526
|
-
def beginPassiveAutoCalibration(self, enabled_bitfield: int) -> ThreespaceCmdResult[None]:
|
|
1527
|
-
raise NotImplementedError("This method is not available.")
|
|
1528
|
-
|
|
1529
|
-
def getActivePassiveAutoCalibration(self) -> ThreespaceCmdResult[int]:
|
|
1530
|
-
raise NotImplementedError("This method is not available.")
|
|
1531
|
-
|
|
1532
|
-
def beginActiveAutoCalibration(self) -> ThreespaceCmdResult[None]:
|
|
1533
|
-
raise NotImplementedError("This method is not available.")
|
|
1534
|
-
|
|
1535
|
-
def isActiveAutoCalibrationActive(self) -> ThreespaceCmdResult[int]:
|
|
1536
|
-
raise NotImplementedError("This method is not available.")
|
|
1537
|
-
|
|
1538
|
-
def getStreamingLabel(self, cmd_num: int) -> ThreespaceCmdResult[str]:
|
|
1539
|
-
raise NotImplementedError("This method is not available.")
|
|
1540
|
-
|
|
1541
|
-
def setCursor(self, cursor_index: int) -> ThreespaceCmdResult[None]:
|
|
1542
|
-
raise NotImplementedError("This method is not available.")
|
|
1543
|
-
|
|
1544
|
-
def getLastLogCursorInfo(self) -> ThreespaceCmdResult[tuple[int,str]]:
|
|
1545
|
-
raise NotImplementedError("This method is not available.")
|
|
1546
|
-
|
|
1547
|
-
def pauseLogStreaming(self, pause: bool) -> ThreespaceCmdResult[None]:
|
|
1548
|
-
raise NotImplementedError("This method is not available.")
|
|
1497
|
+
def deleteFile(self, path: str) -> ThreespaceCmdResult[None]: ...
|
|
1498
|
+
def getStreamingBatch(self) -> ThreespaceCmdResult[list]: ...
|
|
1499
|
+
def setOffsetWithCurrentOrientation(self) -> ThreespaceCmdResult[None]: ...
|
|
1500
|
+
def resetBaseOffset(self) -> ThreespaceCmdResult[None]: ...
|
|
1501
|
+
def setBaseOffsetWithCurrentOrientation(self) -> ThreespaceCmdResult[None]: ...
|
|
1502
|
+
def getTaredTwoVectorInSensorFrame(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1503
|
+
def getUntaredTwoVectorInSensorFrame(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1504
|
+
def getPrimaryBarometerPressure(self) -> ThreespaceCmdResult[float]: ...
|
|
1505
|
+
def getPrimaryBarometerAltitude(self) -> ThreespaceCmdResult[float]: ...
|
|
1506
|
+
def getBarometerAltitude(self, id: int) -> ThreespaceCmdResult[float]: ...
|
|
1507
|
+
def getBarometerPressure(self, id: int) -> ThreespaceCmdResult[float]: ...
|
|
1508
|
+
def getAllPrimaryNormalizedData(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1509
|
+
def getPrimaryNormalizedGyroRate(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1510
|
+
def getPrimaryNormalizedAccelVec(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1511
|
+
def getPrimaryNormalizedMagVec(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1512
|
+
def getAllPrimaryCorrectedData(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1513
|
+
def getPrimaryCorrectedGyroRate(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1514
|
+
def getPrimaryCorrectedAccelVec(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1515
|
+
def getPrimaryCorrectedMagVec(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1516
|
+
def getPrimaryGlobalLinearAccel(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1517
|
+
def getPrimaryLocalLinearAccel(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1518
|
+
def getTemperatureCelsius(self) -> ThreespaceCmdResult[float]: ...
|
|
1519
|
+
def getTemperatureFahrenheit(self) -> ThreespaceCmdResult[float]: ...
|
|
1520
|
+
def getNormalizedGyroRate(self, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1521
|
+
def getNormalizedAccelVec(self, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1522
|
+
def getNormalizedMagVec(self, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1523
|
+
def getCorrectedGyroRate(self, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1524
|
+
def getCorrectedAccelVec(self, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1525
|
+
def getCorrectedMagVec(self, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1526
|
+
def enableMSC(self) -> ThreespaceCmdResult[None]: ...
|
|
1527
|
+
def disableMSC(self) -> ThreespaceCmdResult[None]: ...
|
|
1528
|
+
def getTimestamp(self) -> ThreespaceCmdResult[int]: ...
|
|
1529
|
+
def getBatteryVoltage(self) -> ThreespaceCmdResult[float]: ...
|
|
1530
|
+
def getBatteryPercent(self) -> ThreespaceCmdResult[int]: ...
|
|
1531
|
+
def getBatteryStatus(self) -> ThreespaceCmdResult[int]: ...
|
|
1532
|
+
def getGpsCoord(self) -> ThreespaceCmdResult[list[float]]: ...
|
|
1533
|
+
def getGpsAltitude(self) -> ThreespaceCmdResult[float]: ...
|
|
1534
|
+
def getGpsFixState(self) -> ThreespaceCmdResult[int]: ...
|
|
1535
|
+
def getGpsHdop(self) -> ThreespaceCmdResult[float]: ...
|
|
1536
|
+
def getGpsSatellites(self) -> ThreespaceCmdResult[int]: ...
|
|
1537
|
+
def getButtonState(self) -> ThreespaceCmdResult[int]: ...
|
|
1538
|
+
def correctRawGyroData(self, x: float, y: float, z: float, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1539
|
+
def correctRawAccelData(self, x: float, y: float, z: float, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1540
|
+
def correctRawMagData(self, x: float, y: float, z: float, id: int) -> ThreespaceCmdResult[list[float]]: ...
|
|
1541
|
+
def formatSd(self) -> ThreespaceCmdResult[None]: ...
|
|
1542
|
+
def setDateTime(self, year: int, month: int, day: int, hour: int, minute: int, second: int) -> ThreespaceCmdResult[None]: ...
|
|
1543
|
+
def getDateTime(self) -> ThreespaceCmdResult[list[int]]: ...
|
|
1544
|
+
def tareWithCurrentOrientation(self) -> ThreespaceCmdResult[None]: ...
|
|
1545
|
+
def setBaseTareWithCurrentOrientation(self) -> ThreespaceCmdResult[None]: ...
|
|
1546
|
+
def resetFilter(self) -> ThreespaceCmdResult[None]: ...
|
|
1547
|
+
def getNumDebugMessages(self) -> ThreespaceCmdResult[int]: ...
|
|
1548
|
+
def getOldestDebugMessage(self) -> ThreespaceCmdResult[str]: ...
|
|
1549
|
+
def selfTest(self) -> ThreespaceCmdResult[int]: ...
|
|
1550
|
+
def beginPassiveAutoCalibration(self, enabled_bitfield: int) -> ThreespaceCmdResult[None]: ...
|
|
1551
|
+
def getActivePassiveAutoCalibration(self) -> ThreespaceCmdResult[int]: ...
|
|
1552
|
+
def beginActiveAutoCalibration(self) -> ThreespaceCmdResult[None]: ...
|
|
1553
|
+
def isActiveAutoCalibrationActive(self) -> ThreespaceCmdResult[int]: ...
|
|
1554
|
+
def getStreamingLabel(self, cmd_num: int) -> ThreespaceCmdResult[str]: ...
|
|
1555
|
+
def setCursor(self, cursor_index: int) -> ThreespaceCmdResult[None]: ...
|
|
1556
|
+
def getLastLogCursorInfo(self) -> ThreespaceCmdResult[tuple[int,str]]: ...
|
|
1557
|
+
def pauseLogStreaming(self, pause: bool) -> ThreespaceCmdResult[None]: ...
|
|
1549
1558
|
|
|
1550
1559
|
THREESPACE_GET_STREAMING_BATCH_COMMAND_NUM = 84
|
|
1560
|
+
THREESPACE_START_STREAMING_COMMAND_NUM = 85
|
|
1561
|
+
THREESPACE_STOP_STREAMING_COMMAND_NUM = 86
|
|
1551
1562
|
THREESPACE_FILE_READ_BYTES_COMMAND_NUM = 177
|
|
1563
|
+
THREESPACE_SOFTWARE_RESET_COMMAND_NUM = 226
|
|
1564
|
+
THREESPACE_ENTER_BOOTLOADER_COMMAND_NUM = 229
|
|
1552
1565
|
|
|
1553
1566
|
#Acutal command definitions
|
|
1554
1567
|
_threespace_commands: list[ThreespaceCommand] = [
|
|
@@ -1616,8 +1629,8 @@ _threespace_commands: list[ThreespaceCommand] = [
|
|
|
1616
1629
|
ThreespaceCommand("disableMSC", 58, "", ""),
|
|
1617
1630
|
|
|
1618
1631
|
ThreespaceCommand("formatSd", 59, "", ""),
|
|
1619
|
-
ThreespaceCommand("
|
|
1620
|
-
ThreespaceCommand("
|
|
1632
|
+
ThreespaceCommand("startDataLogging", 60, "", "", custom_func=ThreespaceSensor._ThreespaceSensor__startDataLogging),
|
|
1633
|
+
ThreespaceCommand("stopDataLogging", 61, "", "", custom_func=ThreespaceSensor._ThreespaceSensor__stopDataLogging),
|
|
1621
1634
|
|
|
1622
1635
|
ThreespaceCommand("setDateTime", 62, "Bbbbbb", ""),
|
|
1623
1636
|
ThreespaceCommand("getDateTime", 63, "", "Bbbbbb"),
|
|
@@ -1635,9 +1648,9 @@ _threespace_commands: list[ThreespaceCommand] = [
|
|
|
1635
1648
|
ThreespaceCommand("eeptsAutoOffset", 74, "", ""),
|
|
1636
1649
|
|
|
1637
1650
|
ThreespaceCommand("getStreamingLabel", 83, "b", "S"),
|
|
1638
|
-
ThreespaceCommand("
|
|
1639
|
-
ThreespaceCommand("
|
|
1640
|
-
ThreespaceCommand("
|
|
1651
|
+
ThreespaceCommand("getStreamingBatch", THREESPACE_GET_STREAMING_BATCH_COMMAND_NUM, "", "S"),
|
|
1652
|
+
ThreespaceCommand("startStreaming", THREESPACE_START_STREAMING_COMMAND_NUM, "", "", custom_func=ThreespaceSensor._ThreespaceSensor__startStreaming),
|
|
1653
|
+
ThreespaceCommand("stopStreaming", THREESPACE_STOP_STREAMING_COMMAND_NUM, "", "", custom_func=ThreespaceSensor._ThreespaceSensor__stopStreaming),
|
|
1641
1654
|
ThreespaceCommand("pauseLogStreaming", 87, "b", ""),
|
|
1642
1655
|
|
|
1643
1656
|
ThreespaceCommand("getTimestamp", 94, "", "U"),
|
|
@@ -1648,6 +1661,7 @@ _threespace_commands: list[ThreespaceCommand] = [
|
|
|
1648
1661
|
ThreespaceCommand("resetFilter", 120, "", ""),
|
|
1649
1662
|
ThreespaceCommand("getNumDebugMessages", 126, "", "B"),
|
|
1650
1663
|
ThreespaceCommand("getOldestDebugMessage", 127, "", "S"),
|
|
1664
|
+
ThreespaceCommand("selfTest", 128, "", "u"),
|
|
1651
1665
|
|
|
1652
1666
|
ThreespaceCommand("beginPassiveAutoCalibration", 165, "b", ""),
|
|
1653
1667
|
ThreespaceCommand("getActivePassiveAutoCalibration", 166, "", "b"),
|
|
@@ -1661,11 +1675,11 @@ _threespace_commands: list[ThreespaceCommand] = [
|
|
|
1661
1675
|
ThreespaceCommand("closeFile", 174, "", ""),
|
|
1662
1676
|
ThreespaceCommand("fileGetRemainingSize", 175, "", "U"),
|
|
1663
1677
|
ThreespaceCommand("fileReadLine", 176, "", "S"),
|
|
1664
|
-
ThreespaceCommand("
|
|
1678
|
+
ThreespaceCommand("fileReadBytes", THREESPACE_FILE_READ_BYTES_COMMAND_NUM, "B", "S", custom_func=ThreespaceSensor._ThreespaceSensor__fileReadBytes), #This has to be handled specially as the output is variable length BYTES not STRING
|
|
1665
1679
|
ThreespaceCommand("deleteFile", 178, "S", ""),
|
|
1666
1680
|
ThreespaceCommand("setCursor", 179, "U", ""),
|
|
1667
|
-
ThreespaceCommand("
|
|
1668
|
-
ThreespaceCommand("
|
|
1681
|
+
ThreespaceCommand("fileStartStream", 180, "", "U", custom_func=ThreespaceSensor._ThreespaceSensor__fileStartStream),
|
|
1682
|
+
ThreespaceCommand("fileStopStream", 181, "", "", custom_func=ThreespaceSensor._ThreespaceSensor__fileStopStream),
|
|
1669
1683
|
|
|
1670
1684
|
ThreespaceCommand("getBatteryVoltage", 201, "", "f"),
|
|
1671
1685
|
ThreespaceCommand("getBatteryPercent", 202, "", "b"),
|
|
@@ -1678,8 +1692,8 @@ _threespace_commands: list[ThreespaceCommand] = [
|
|
|
1678
1692
|
ThreespaceCommand("getGpsSatellites", 219, "", "b"),
|
|
1679
1693
|
|
|
1680
1694
|
ThreespaceCommand("commitSettings", 225, "", ""),
|
|
1681
|
-
ThreespaceCommand("
|
|
1682
|
-
ThreespaceCommand("
|
|
1695
|
+
ThreespaceCommand("softwareReset", THREESPACE_SOFTWARE_RESET_COMMAND_NUM, "", "", custom_func=ThreespaceSensor._ThreespaceSensor__softwareReset),
|
|
1696
|
+
ThreespaceCommand("enterBootloader", THREESPACE_ENTER_BOOTLOADER_COMMAND_NUM, "", "", custom_func=ThreespaceSensor._ThreespaceSensor__enterBootloader),
|
|
1683
1697
|
|
|
1684
1698
|
ThreespaceCommand("getButtonState", 250, "", "b"),
|
|
1685
1699
|
]
|
|
@@ -1690,6 +1704,12 @@ def threespaceCommandGet(cmd_num: int):
|
|
|
1690
1704
|
return command
|
|
1691
1705
|
return None
|
|
1692
1706
|
|
|
1707
|
+
def threespaceCommandGetByName(name: str):
|
|
1708
|
+
for command in _threespace_commands:
|
|
1709
|
+
if command.info.name == name:
|
|
1710
|
+
return command
|
|
1711
|
+
return None
|
|
1712
|
+
|
|
1693
1713
|
def threespaceCommandGetInfo(cmd_num: int):
|
|
1694
1714
|
command = threespaceCommandGet(cmd_num)
|
|
1695
1715
|
if command is None: return None
|