bosdyn-client 3.3.2__py3-none-any.whl → 4.0.1__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.
- bosdyn/client/__init__.py +5 -6
- bosdyn/client/area_callback_region_handler_base.py +19 -4
- bosdyn/client/area_callback_service_servicer.py +29 -1
- bosdyn/client/area_callback_service_utils.py +45 -51
- bosdyn/client/auth.py +13 -28
- bosdyn/client/autowalk.py +3 -0
- bosdyn/client/channel.py +23 -26
- bosdyn/client/command_line.py +64 -13
- bosdyn/client/common.py +4 -4
- bosdyn/client/data_acquisition.py +47 -6
- bosdyn/client/data_acquisition_plugin.py +12 -2
- bosdyn/client/data_acquisition_plugin_service.py +33 -2
- bosdyn/client/data_acquisition_store.py +38 -0
- bosdyn/client/data_buffer.py +22 -8
- bosdyn/client/data_chunk.py +1 -0
- bosdyn/client/directory_registration.py +1 -14
- bosdyn/client/exceptions.py +0 -4
- bosdyn/client/frame_helpers.py +3 -1
- bosdyn/client/gps/NMEAParser.py +189 -0
- bosdyn/client/gps/__init__.py +6 -0
- bosdyn/client/gps/aggregator_client.py +56 -0
- bosdyn/client/gps/gps_listener.py +153 -0
- bosdyn/client/gps/registration_client.py +48 -0
- bosdyn/client/graph_nav.py +50 -20
- bosdyn/client/image.py +20 -7
- bosdyn/client/image_service_helpers.py +14 -14
- bosdyn/client/lease.py +27 -22
- bosdyn/client/lease_validator.py +5 -5
- bosdyn/client/manipulation_api_client.py +1 -1
- bosdyn/client/map_processing.py +10 -5
- bosdyn/client/math_helpers.py +21 -11
- bosdyn/client/metrics_logging.py +147 -0
- bosdyn/client/network_compute_bridge_client.py +6 -0
- bosdyn/client/power.py +40 -0
- bosdyn/client/recording.py +3 -3
- bosdyn/client/robot.py +15 -16
- bosdyn/client/robot_command.py +341 -203
- bosdyn/client/robot_id.py +6 -5
- bosdyn/client/robot_state.py +6 -0
- bosdyn/client/sdk.py +5 -11
- bosdyn/client/server_util.py +11 -11
- bosdyn/client/service_customization_helpers.py +776 -64
- bosdyn/client/signals_helpers.py +105 -0
- bosdyn/client/spot_cam/compositor.py +6 -2
- bosdyn/client/spot_cam/ptz.py +24 -14
- bosdyn/client/spot_check.py +160 -0
- bosdyn/client/time_sync.py +5 -5
- bosdyn/client/units_helpers.py +39 -0
- bosdyn/client/util.py +100 -64
- bosdyn/client/world_object.py +5 -5
- {bosdyn_client-3.3.2.dist-info → bosdyn_client-4.0.1.dist-info}/METADATA +4 -3
- bosdyn_client-4.0.1.dist-info/RECORD +97 -0
- {bosdyn_client-3.3.2.dist-info → bosdyn_client-4.0.1.dist-info}/WHEEL +1 -1
- bosdyn/client/log_annotation.py +0 -359
- bosdyn_client-3.3.2.dist-info/RECORD +0 -90
- {bosdyn_client-3.3.2.dist-info → bosdyn_client-4.0.1.dist-info}/top_level.txt +0 -0
bosdyn/client/__init__.py
CHANGED
|
@@ -8,18 +8,17 @@
|
|
|
8
8
|
Sets up some convenience imports for commonly used classes.
|
|
9
9
|
"""
|
|
10
10
|
# yapf: enable
|
|
11
|
-
from .auth import
|
|
12
|
-
InvalidLoginError, InvalidTokenError)
|
|
11
|
+
from .auth import AuthClient, InvalidLoginError, InvalidTokenError
|
|
13
12
|
from .common import BaseClient
|
|
14
13
|
# yapf: disable
|
|
15
14
|
from .exceptions import (ClientCancelledOperationError, CustomParamError, Error,
|
|
16
|
-
InternalServerError,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
ProxyConnectionError, ResponseError, RetryableRpcError,
|
|
15
|
+
InternalServerError, InvalidClientCertificateError, InvalidRequestError,
|
|
16
|
+
LeaseUseError, LicenseError, NonexistentAuthorityError, NotFoundError,
|
|
17
|
+
PersistentRpcError, ProxyConnectionError, ResponseError, RetryableRpcError,
|
|
20
18
|
RetryableUnavailableError, RpcError, ServerError,
|
|
21
19
|
ServiceFailedDuringExecutionError, ServiceUnavailableError, TimedOutError,
|
|
22
20
|
TooManyRequestsError, UnableToConnectToRobotError, UnauthenticatedError,
|
|
23
21
|
UnimplementedError, UnknownDnsNameError, UnsetStatusError)
|
|
24
22
|
from .robot import Robot
|
|
25
23
|
from .sdk import BOSDYN_RESOURCE_ROOT, Sdk, create_standard_sdk
|
|
24
|
+
|
|
@@ -50,6 +50,15 @@ class CallbackTimedOutError(HandlerError):
|
|
|
50
50
|
re-raised to make sure the response is set correctly."""
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
class RouteChangedResult:
|
|
54
|
+
"""Options for how the helper class should respond to a route change."""
|
|
55
|
+
|
|
56
|
+
def __init__(self):
|
|
57
|
+
# Specify that if the callback has stopped (returned or raised from run()) that run()
|
|
58
|
+
# should be called again.
|
|
59
|
+
self.rerun_if_stopped = False
|
|
60
|
+
|
|
61
|
+
|
|
53
62
|
class AreaCallbackRegionHandlerBase:
|
|
54
63
|
"""Base class for implementing a AreaCallbackRegionHandler.
|
|
55
64
|
|
|
@@ -100,6 +109,12 @@ class AreaCallbackRegionHandlerBase:
|
|
|
100
109
|
"""This function is called after run thread has finished and client calls EndCallback."""
|
|
101
110
|
raise NotImplementedError("Derived class must implement this function.")
|
|
102
111
|
|
|
112
|
+
def route_changed(self, request: area_callback_pb2.RouteChangeRequest) -> RouteChangedResult:
|
|
113
|
+
"""This function is called when Graph Nav re-routes inside the callback region.
|
|
114
|
+
In most cases, the callback does not need to do anything for this case and can leave the
|
|
115
|
+
default implementation"""
|
|
116
|
+
return RouteChangedResult()
|
|
117
|
+
|
|
103
118
|
@property
|
|
104
119
|
def area_callback_information(self) -> area_callback_pb2.AreaCallbackInformation:
|
|
105
120
|
"""Get area_callback_pb2.AreaCallbackInformation."""
|
|
@@ -155,10 +170,10 @@ class AreaCallbackRegionHandlerBase:
|
|
|
155
170
|
self._update_response.complete.SetInParent()
|
|
156
171
|
|
|
157
172
|
def set_localization_at_end(self):
|
|
158
|
-
"""Set the localization hint to the end of the callback region, indicating that graph nav
|
|
173
|
+
"""Set the localization hint to the end of the callback region, indicating that graph nav
|
|
159
174
|
that navigation should continue from this point.
|
|
160
175
|
Robot control is required to set this. It should be called after walking to the end of
|
|
161
|
-
the region, but before ceding control.
|
|
176
|
+
the region, but before ceding control.
|
|
162
177
|
|
|
163
178
|
Raises:
|
|
164
179
|
IncorrectUsage: When called without robot control.
|
|
@@ -243,7 +258,7 @@ class AreaCallbackRegionHandlerBase:
|
|
|
243
258
|
sleep_time_secs (float): Time to sleep, in seconds.
|
|
244
259
|
|
|
245
260
|
Raises:
|
|
246
|
-
HandlerError: When a shutdown is requested during the sleep time
|
|
261
|
+
HandlerError: When a shutdown is requested during the sleep time.
|
|
247
262
|
"""
|
|
248
263
|
if self.robot.time_sec() > self._end_time:
|
|
249
264
|
raise CallbackTimedOutError()
|
|
@@ -318,7 +333,7 @@ class AreaCallbackRegionHandlerBase:
|
|
|
318
333
|
"""Wrapper around the run function which catches exceptions and set update response.
|
|
319
334
|
|
|
320
335
|
Args:
|
|
321
|
-
shutdown_event (Event): Event that signals the run thread to
|
|
336
|
+
shutdown_event (Event): Event that signals the run thread to shut down.
|
|
322
337
|
"""
|
|
323
338
|
self._shutdown_event = shutdown_event
|
|
324
339
|
_LOGGER.info('Beginning callback')
|
|
@@ -159,6 +159,34 @@ class AreaCallbackServiceServicer(area_callback_service_pb2_grpc.AreaCallbackSer
|
|
|
159
159
|
self.area_callback_region_handler = None
|
|
160
160
|
return response
|
|
161
161
|
|
|
162
|
+
def RouteChange(self, request, context):
|
|
163
|
+
"""Called when we re-route within the callback. Most callbacks do not need to know about
|
|
164
|
+
changes in the route, and can ignore this."""
|
|
165
|
+
_LOGGER.info('Received RouteChange for command %d', request.command_id)
|
|
166
|
+
response = area_callback_pb2.RouteChangeResponse()
|
|
167
|
+
with ResponseContext(response, request, self._rpc_logger):
|
|
168
|
+
if (not self.area_callback_region_handler or
|
|
169
|
+
not self._is_active_command_id(request.command_id)):
|
|
170
|
+
response.status = area_callback_pb2.EndCallbackResponse.STATUS_INVALID_COMMAND_ID
|
|
171
|
+
return response
|
|
172
|
+
try:
|
|
173
|
+
route_changed_result = self.area_callback_region_handler.route_changed(request)
|
|
174
|
+
if (route_changed_result.rerun_if_stopped and
|
|
175
|
+
not self.area_callback_active_thread.is_alive()):
|
|
176
|
+
self.area_callback_active_thread_event = Event()
|
|
177
|
+
self.area_callback_active_thread = Thread(
|
|
178
|
+
target=self.area_callback_region_handler.internal_run_wrapper,
|
|
179
|
+
args=[self.area_callback_active_thread_event])
|
|
180
|
+
self.area_callback_active_thread.start()
|
|
181
|
+
_LOGGER.info('Re-created thread for command id %d', self._active_command_id)
|
|
182
|
+
response.status = response.STATUS_OK
|
|
183
|
+
except Exception as exc:
|
|
184
|
+
_LOGGER.exception("Failed route_changed_call")
|
|
185
|
+
response.header.error.code = response.header.error.CODE_INTERNAL_SERVER_ERROR
|
|
186
|
+
response.header.error.message = str(exc)
|
|
187
|
+
|
|
188
|
+
return response
|
|
189
|
+
|
|
162
190
|
def _is_expired(self, end_time):
|
|
163
191
|
current_robot_time_secs = self.robot.time_sec()
|
|
164
192
|
end_time_secs = timestamp_to_sec(end_time)
|
|
@@ -213,7 +241,7 @@ class AreaCallbackServiceServicer(area_callback_service_pb2_grpc.AreaCallbackSer
|
|
|
213
241
|
"""Call to force run thread to terminate.
|
|
214
242
|
|
|
215
243
|
Args:
|
|
216
|
-
timeout (float, optional): Time allowed to run thread to
|
|
244
|
+
timeout (float, optional): Time allowed to run thread to shut down. Defaults to 5.
|
|
217
245
|
|
|
218
246
|
Returns:
|
|
219
247
|
bool: True if the thread correctly shut down within the allowed time.
|
|
@@ -8,13 +8,13 @@ import logging
|
|
|
8
8
|
import time
|
|
9
9
|
from typing import List
|
|
10
10
|
|
|
11
|
+
import bosdyn.client
|
|
11
12
|
from bosdyn.api import service_fault_pb2
|
|
12
13
|
from bosdyn.api.graph_nav import area_callback_pb2
|
|
13
14
|
from bosdyn.api.service_customization_pb2 import DictParam
|
|
14
15
|
from bosdyn.client.directory import NonexistentServiceError
|
|
15
|
-
from bosdyn.client.
|
|
16
|
-
from bosdyn.client.
|
|
17
|
-
ServiceFaultDoesNotExistError)
|
|
16
|
+
from bosdyn.client.fault import ServiceFaultAlreadyExistsError, ServiceFaultDoesNotExistError
|
|
17
|
+
from bosdyn.client.service_customization_helpers import dict_params_to_dict
|
|
18
18
|
|
|
19
19
|
_LOGGER = logging.getLogger(__name__)
|
|
20
20
|
|
|
@@ -43,49 +43,18 @@ class AreaCallbackServiceConfig:
|
|
|
43
43
|
|
|
44
44
|
def parse_params(self, params: DictParam):
|
|
45
45
|
""" Parse params and validate they agree with the spec stored in area_callback_information.
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
Args:
|
|
48
48
|
params (DictParam): The parameters being validated.
|
|
49
49
|
"""
|
|
50
|
-
|
|
51
|
-
spec = self.area_callback_information.custom_params
|
|
52
|
-
values = {}
|
|
53
|
-
for (key, param) in params.values.items():
|
|
54
|
-
if key not in spec.specs:
|
|
55
|
-
raise ValueError(f"No spec provided for parameter {key}.")
|
|
56
|
-
|
|
57
|
-
param_spec = spec.specs[key].spec
|
|
58
|
-
spec_field = param_spec.WhichOneof("spec")
|
|
59
|
-
value_field = spec_field.replace("spec", "value")
|
|
60
|
-
if not param.HasField(value_field):
|
|
61
|
-
raise ValueError(f"Param {key} has a {spec_field}, but has no {value_field}.")
|
|
62
|
-
|
|
63
|
-
type_spec = getattr(param_spec, spec_field)
|
|
64
|
-
param_value = getattr(param, value_field)
|
|
65
|
-
|
|
66
|
-
# For numerical values, check bounds.
|
|
67
|
-
if hasattr(type_spec, "min_value") and \
|
|
68
|
-
type_spec.HasField("min_value") and \
|
|
69
|
-
param_value.value < type_spec.min_value.value:
|
|
70
|
-
raise ValueError(f"Value for {key} too low; minimum is {type_spec.min_value.value} " \
|
|
71
|
-
f"but passed value of {param_value.value}")
|
|
72
|
-
|
|
73
|
-
if hasattr(type_spec, "max_value") and \
|
|
74
|
-
type_spec.HasField("max_value") and \
|
|
75
|
-
param_value.value > type_spec.max_value.value:
|
|
76
|
-
raise ValueError(f"Value for {key} too high; maximum is {type_spec.max_value.value} " \
|
|
77
|
-
f"but passed value of {param_value.value}")
|
|
78
|
-
|
|
79
|
-
values[key] = param_value.value
|
|
80
|
-
|
|
81
|
-
return values
|
|
50
|
+
return dict_params_to_dict(params, self.area_callback_information.custom_params)
|
|
82
51
|
|
|
83
52
|
|
|
84
53
|
# Helper to raise service faults when other services are unavailable.
|
|
85
54
|
def handle_service_faults(fault_client, robot_state_client, directory_client, service_name,
|
|
86
55
|
prereq_services):
|
|
87
56
|
service_fault = service_fault_pb2.ServiceFault()
|
|
88
|
-
service_fault.fault_id.fault_name =
|
|
57
|
+
service_fault.fault_id.fault_name = service_name
|
|
89
58
|
service_fault.fault_id.service_name = service_name
|
|
90
59
|
service_fault.severity = service_fault_pb2.ServiceFault.SEVERITY_CRITICAL
|
|
91
60
|
check_period = 0.5 # seconds.
|
|
@@ -98,43 +67,68 @@ def handle_service_faults(fault_client, robot_state_client, directory_client, se
|
|
|
98
67
|
registered_service = directory_client.get_entry(service_name)
|
|
99
68
|
except NonexistentServiceError as exc:
|
|
100
69
|
continue
|
|
70
|
+
except bosdyn.client.Error as exc:
|
|
71
|
+
_LOGGER.error("Failed to check if %s exists during fault handling: %s", service_name,
|
|
72
|
+
exc)
|
|
73
|
+
continue
|
|
101
74
|
|
|
102
75
|
set_fault = False
|
|
76
|
+
fault_exists = False
|
|
103
77
|
unavailable_services = []
|
|
104
|
-
|
|
78
|
+
|
|
79
|
+
# Need robot state to check existing faults.
|
|
80
|
+
try:
|
|
81
|
+
state = robot_state_client.get_robot_state()
|
|
82
|
+
except bosdyn.client.Error as exc:
|
|
83
|
+
_LOGGER.error("Failed to get robot state during fault handling: %s", exc)
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
for prereq_service in prereq_services:
|
|
105
87
|
# Make sure the prereq service exists.
|
|
106
88
|
try:
|
|
107
|
-
registered_service = directory_client.get_entry(
|
|
89
|
+
registered_service = directory_client.get_entry(prereq_service)
|
|
108
90
|
except NonexistentServiceError as exc:
|
|
109
91
|
set_fault = True
|
|
110
|
-
unavailable_services.append(
|
|
92
|
+
unavailable_services.append(prereq_service)
|
|
93
|
+
continue
|
|
94
|
+
except bosdyn.client.Error as exc:
|
|
95
|
+
_LOGGER.error("Failed to check if %s exists during fault handling: %s",
|
|
96
|
+
prereq_service, exc)
|
|
97
|
+
set_fault = True
|
|
98
|
+
unavailable_services.append(prereq_service)
|
|
111
99
|
continue
|
|
112
100
|
|
|
113
101
|
# Make sure the prereq service isn't faulted.
|
|
114
|
-
state = robot_state_client.get_robot_state()
|
|
115
102
|
for fault in state.service_fault_state.faults:
|
|
116
|
-
if fault.fault_id.service_name ==
|
|
103
|
+
if fault.fault_id.service_name == prereq_service:
|
|
117
104
|
set_fault = True
|
|
118
|
-
unavailable_services.append(
|
|
105
|
+
unavailable_services.append(prereq_service)
|
|
119
106
|
break
|
|
120
107
|
|
|
121
|
-
#
|
|
122
|
-
|
|
108
|
+
# Check if the service is already faulted.
|
|
109
|
+
for fault in state.service_fault_state.faults:
|
|
110
|
+
if fault.fault_id.service_name == service_name:
|
|
111
|
+
fault_exists = True
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
# Fault the service if there isn't an existing fault.
|
|
115
|
+
if set_fault and not fault_exists:
|
|
123
116
|
service_fault.error_message = 'Faulted due to issues with ' + ','.join(
|
|
124
117
|
unavailable_services)
|
|
125
118
|
try:
|
|
126
119
|
fault_client.trigger_service_fault(service_fault)
|
|
120
|
+
_LOGGER.info("Triggered fault on %s", service_name)
|
|
127
121
|
except ServiceFaultAlreadyExistsError:
|
|
128
122
|
pass
|
|
129
|
-
except
|
|
130
|
-
_LOGGER.error(
|
|
123
|
+
except bosdyn.client.Error as exc:
|
|
124
|
+
_LOGGER.error("Failed to set %s fault: %s", service_name, exc)
|
|
131
125
|
|
|
132
126
|
# Otherwise, clear the fault if it exists.
|
|
133
|
-
|
|
127
|
+
elif not set_fault and fault_exists:
|
|
134
128
|
try:
|
|
135
129
|
fault_client.clear_service_fault(service_fault.fault_id)
|
|
136
|
-
|
|
130
|
+
_LOGGER.info("Cleared fault on %s", service_name)
|
|
137
131
|
except ServiceFaultDoesNotExistError:
|
|
138
132
|
pass
|
|
139
|
-
except
|
|
140
|
-
_LOGGER.error(
|
|
133
|
+
except bosdyn.client.Error as exc:
|
|
134
|
+
_LOGGER.error("Failed to clear %s fault: %s", service_name, exc)
|
bosdyn/client/auth.py
CHANGED
|
@@ -39,14 +39,6 @@ class TemporarilyLockedOutError(AuthResponseError):
|
|
|
39
39
|
"""User is temporarily locked out of authentication."""
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
class ExpiredApplicationTokenError(AuthResponseError):
|
|
43
|
-
"""Application token has expired. Please contact support@bostondynamics.com to receive a new one."""
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class InvalidApplicationTokenError(AuthResponseError):
|
|
47
|
-
"""The Application Token is invalid."""
|
|
48
|
-
|
|
49
|
-
|
|
50
42
|
_STATUS_TO_ERROR = collections.defaultdict(lambda: (ResponseError, None))
|
|
51
43
|
_STATUS_TO_ERROR.update({
|
|
52
44
|
auth_pb2.GetAuthTokenResponse.STATUS_OK: (None, None),
|
|
@@ -55,11 +47,7 @@ _STATUS_TO_ERROR.update({
|
|
|
55
47
|
auth_pb2.GetAuthTokenResponse.STATUS_INVALID_TOKEN:
|
|
56
48
|
(InvalidTokenError, InvalidTokenError.__doc__),
|
|
57
49
|
auth_pb2.GetAuthTokenResponse.STATUS_TEMPORARILY_LOCKED_OUT:
|
|
58
|
-
(TemporarilyLockedOutError, TemporarilyLockedOutError.__doc__)
|
|
59
|
-
auth_pb2.GetAuthTokenResponse.STATUS_INVALID_APPLICATION_TOKEN:
|
|
60
|
-
(InvalidApplicationTokenError, InvalidApplicationTokenError.__doc__),
|
|
61
|
-
auth_pb2.GetAuthTokenResponse.STATUS_EXPIRED_APPLICATION_TOKEN:
|
|
62
|
-
(ExpiredApplicationTokenError, ExpiredApplicationTokenError.__doc__)
|
|
50
|
+
(TemporarilyLockedOutError, TemporarilyLockedOutError.__doc__)
|
|
63
51
|
})
|
|
64
52
|
|
|
65
53
|
|
|
@@ -76,13 +64,12 @@ def _token_from_response(response):
|
|
|
76
64
|
return response.token
|
|
77
65
|
|
|
78
66
|
|
|
79
|
-
def _build_auth_request(username, password
|
|
80
|
-
return auth_pb2.GetAuthTokenRequest(username=username, password=password
|
|
81
|
-
application_token=app_token)
|
|
67
|
+
def _build_auth_request(username, password):
|
|
68
|
+
return auth_pb2.GetAuthTokenRequest(username=username, password=password)
|
|
82
69
|
|
|
83
70
|
|
|
84
|
-
def _build_auth_token_request(token
|
|
85
|
-
return auth_pb2.GetAuthTokenRequest(token=token
|
|
71
|
+
def _build_auth_token_request(token):
|
|
72
|
+
return auth_pb2.GetAuthTokenRequest(token=token)
|
|
86
73
|
|
|
87
74
|
|
|
88
75
|
class AuthClient(BaseClient):
|
|
@@ -96,13 +83,12 @@ class AuthClient(BaseClient):
|
|
|
96
83
|
def __init__(self, name=None):
|
|
97
84
|
super(AuthClient, self).__init__(auth_service_pb2_grpc.AuthServiceStub, name=name)
|
|
98
85
|
|
|
99
|
-
def auth(self, username, password,
|
|
86
|
+
def auth(self, username, password, **kwargs):
|
|
100
87
|
"""Authenticate to the robot with a username/password combo.
|
|
101
88
|
|
|
102
89
|
Args:
|
|
103
90
|
username: username on the robot.
|
|
104
91
|
password: password for the username on the robot.
|
|
105
|
-
app_token: Deprecated. Only include for robots with old software.
|
|
106
92
|
kwargs: extra arguments for controlling RPC details.
|
|
107
93
|
|
|
108
94
|
Returns:
|
|
@@ -111,25 +97,24 @@ class AuthClient(BaseClient):
|
|
|
111
97
|
Raises:
|
|
112
98
|
InvalidLoginError: If username and/or password are not valid.
|
|
113
99
|
"""
|
|
114
|
-
req = _build_auth_request(username, password
|
|
100
|
+
req = _build_auth_request(username, password)
|
|
115
101
|
return self.call(self._stub.GetAuthToken, req, _token_from_response, _error_from_response,
|
|
116
102
|
copy_request=False, **kwargs)
|
|
117
103
|
|
|
118
|
-
def auth_async(self, username, password,
|
|
104
|
+
def auth_async(self, username, password, **kwargs):
|
|
119
105
|
"""Asynchronously authenticate to the robot with a username/password combo.
|
|
120
106
|
|
|
121
107
|
See auth documentation for more details.
|
|
122
108
|
"""
|
|
123
|
-
req = _build_auth_request(username, password
|
|
109
|
+
req = _build_auth_request(username, password)
|
|
124
110
|
return self.call_async(self._stub.GetAuthToken, req, _token_from_response,
|
|
125
111
|
_error_from_response, copy_request=False, **kwargs)
|
|
126
112
|
|
|
127
|
-
def auth_with_token(self, token,
|
|
113
|
+
def auth_with_token(self, token, **kwargs):
|
|
128
114
|
"""Authenticate to the robot using a previously created user token.
|
|
129
115
|
|
|
130
116
|
Args:
|
|
131
117
|
token: a user token previously issued by the robot.
|
|
132
|
-
app_token: Deprecated. Only include for robots with old software.
|
|
133
118
|
kwargs: extra arguments for controlling RPC details.
|
|
134
119
|
|
|
135
120
|
Returns:
|
|
@@ -140,15 +125,15 @@ class AuthClient(BaseClient):
|
|
|
140
125
|
Raises:
|
|
141
126
|
InvalidTokenError: If the token was incorrectly formed, for the wrong robot, or expired.
|
|
142
127
|
"""
|
|
143
|
-
req = _build_auth_token_request(token
|
|
128
|
+
req = _build_auth_token_request(token)
|
|
144
129
|
return self.call(self._stub.GetAuthToken, req, _token_from_response, _error_from_response,
|
|
145
130
|
copy_request=False, **kwargs)
|
|
146
131
|
|
|
147
|
-
def auth_with_token_async(self, token,
|
|
132
|
+
def auth_with_token_async(self, token, **kwargs):
|
|
148
133
|
"""Authenticate to the robot using a previously created user token.
|
|
149
134
|
|
|
150
135
|
See auth_with_token documentation for more details.
|
|
151
136
|
"""
|
|
152
|
-
req = _build_auth_token_request(token
|
|
137
|
+
req = _build_auth_token_request(token)
|
|
153
138
|
return self.call_async(self._stub.GetAuthToken, req, _token_from_response,
|
|
154
139
|
_error_from_response, copy_request=False, **kwargs)
|
bosdyn/client/autowalk.py
CHANGED
|
@@ -41,6 +41,7 @@ class AutowalkClient(BaseClient):
|
|
|
41
41
|
|
|
42
42
|
def update_from(self, other):
|
|
43
43
|
"""Update instance from another object.
|
|
44
|
+
|
|
44
45
|
Args:
|
|
45
46
|
other: The object where to copy from.
|
|
46
47
|
"""
|
|
@@ -50,6 +51,7 @@ class AutowalkClient(BaseClient):
|
|
|
50
51
|
|
|
51
52
|
def compile_autowalk(self, walk, data_chunk_byte_size=1000 * 1000, **kwargs):
|
|
52
53
|
"""Send the input walk file to the autowalk service for compilation.
|
|
54
|
+
|
|
53
55
|
Args:
|
|
54
56
|
walk: a walks_pb2.Walk input to be compiled by the autowalk service
|
|
55
57
|
data_chunk_byte_size: max size of each streamed message
|
|
@@ -69,6 +71,7 @@ class AutowalkClient(BaseClient):
|
|
|
69
71
|
def load_autowalk(self, walk, leases=[], data_chunk_byte_size=1000 * 1000, **kwargs):
|
|
70
72
|
"""Send the input walk file to the autowalk service for compilation and
|
|
71
73
|
load resulting mission to the Mission Service on the robot.
|
|
74
|
+
|
|
72
75
|
Args:
|
|
73
76
|
walk: a walks_pb2.Walk input to be loaded onto the robot by the autowalk service
|
|
74
77
|
leases: Leases the autowalk service will need to use. Unlike other clients, these MUST
|
bosdyn/client/channel.py
CHANGED
|
@@ -9,53 +9,47 @@ import warnings
|
|
|
9
9
|
|
|
10
10
|
import grpc
|
|
11
11
|
|
|
12
|
-
from .exceptions import (ClientCancelledOperationError,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
from .exceptions import (ClientCancelledOperationError, InvalidClientCertificateError,
|
|
13
|
+
NonexistentAuthorityError, NotFoundError, PermissionDeniedError,
|
|
14
|
+
ProxyConnectionError, ResponseTooLargeError, RetryableUnavailableError,
|
|
15
|
+
RpcError, ServiceFailedDuringExecutionError, ServiceUnavailableError,
|
|
16
|
+
TimedOutError, TooManyRequestsError, TransientFailureError,
|
|
17
|
+
UnableToConnectToRobotError, UnauthenticatedError, UnimplementedError,
|
|
18
|
+
UnknownDnsNameError)
|
|
19
19
|
|
|
20
20
|
TransportError = grpc.RpcError
|
|
21
21
|
|
|
22
22
|
_LOGGER = logging.getLogger(__name__)
|
|
23
23
|
|
|
24
|
-
# Set default max message length for sending and receiving
|
|
24
|
+
# Set default max message length for sending and receiving. This value is used when
|
|
25
25
|
# creating channels in the bosdyn.client.Robot class.
|
|
26
26
|
DEFAULT_MAX_MESSAGE_LENGTH = 100 * (1024**2)
|
|
27
27
|
|
|
28
|
+
# Period in milliseconds after which a keepalive ping is sent on the transport.
|
|
29
|
+
DEFAULT_KEEP_ALIVE_TIME_MS = 5000
|
|
30
|
+
|
|
28
31
|
|
|
29
32
|
class RefreshingAccessTokenAuthMetadataPlugin(grpc.AuthMetadataPlugin):
|
|
30
33
|
"""Plugin to refresh access token.
|
|
31
34
|
|
|
32
35
|
Args:
|
|
33
|
-
token_cb:
|
|
34
|
-
add_app_token (bool): Deprecated
|
|
36
|
+
token_cb: a callback to provide a valid user token.
|
|
35
37
|
"""
|
|
36
38
|
|
|
37
|
-
def __init__(self, token_cb
|
|
39
|
+
def __init__(self, token_cb):
|
|
38
40
|
self._token_cb = token_cb
|
|
39
|
-
if add_app_token is not None:
|
|
40
|
-
warnings.warn(
|
|
41
|
-
'add_app_token is deprecated for RefreshingAccessTokenAuthMetadataPlugin. '
|
|
42
|
-
'Do not set it', DeprecationWarning)
|
|
43
41
|
|
|
44
42
|
def __call__(self, context, callback):
|
|
45
|
-
|
|
46
|
-
user_token_metadata = ('authorization', 'Bearer {}'.format(user_token))
|
|
43
|
+
user_token_metadata = ('authorization', 'Bearer {}'.format(self._token_cb()))
|
|
47
44
|
metadata = (user_token_metadata,)
|
|
48
45
|
error = None
|
|
49
46
|
callback(metadata, error)
|
|
50
47
|
|
|
51
48
|
|
|
52
|
-
def create_secure_channel_creds(cert, token_cb
|
|
49
|
+
def create_secure_channel_creds(cert, token_cb):
|
|
53
50
|
"""Returns credentials for establishing a secure channel.
|
|
54
51
|
Uses previously set values on the linked Sdk and self.
|
|
55
52
|
"""
|
|
56
|
-
if add_app_token is not None:
|
|
57
|
-
warnings.warn('add_app_token is deprecated for create_secure_channel_creds. Do not set it.',
|
|
58
|
-
DeprecationWarning)
|
|
59
53
|
|
|
60
54
|
transport_creds = grpc.ssl_channel_credentials(root_certificates=cert)
|
|
61
55
|
plugin = RefreshingAccessTokenAuthMetadataPlugin(token_cb)
|
|
@@ -126,8 +120,6 @@ def translate_exception(rpc_error):
|
|
|
126
120
|
if code is grpc.StatusCode.CANCELLED:
|
|
127
121
|
if str(401) in details:
|
|
128
122
|
return UnauthenticatedError(rpc_error, UnauthenticatedError.__doc__)
|
|
129
|
-
elif str(403) in details:
|
|
130
|
-
return InvalidAppTokenError(rpc_error, InvalidAppTokenError.__doc__)
|
|
131
123
|
elif str(404) in details:
|
|
132
124
|
return NotFoundError(rpc_error, NotFoundError.__doc__)
|
|
133
125
|
elif str(429) in details:
|
|
@@ -183,15 +175,19 @@ def translate_exception(rpc_error):
|
|
|
183
175
|
return RpcError(rpc_error, RpcError.__doc__)
|
|
184
176
|
|
|
185
177
|
|
|
186
|
-
def generate_channel_options(max_send_message_length=None, max_receive_message_length=None
|
|
178
|
+
def generate_channel_options(max_send_message_length=None, max_receive_message_length=None,
|
|
179
|
+
keep_alive_ping_time_ms=None):
|
|
187
180
|
"""Generate the array of options to specify in the creation of a client channel or server.
|
|
188
181
|
|
|
189
182
|
The list contains the values for max allowed message length for both sending and
|
|
190
|
-
receiving. If no values are provided, the default values of 100 MB are used.
|
|
183
|
+
receiving. If no values are provided, the default values of 100 MB are used. It also contains a
|
|
184
|
+
value for time between keep_alive pings, which default to 5s.
|
|
191
185
|
|
|
192
186
|
Args:
|
|
193
187
|
max_send_message_length (int): Max message length allowed for message to send.
|
|
194
188
|
max_receive_message_length (int): Max message length allowed for message to receive.
|
|
189
|
+
keep_alive_ping_time_ms (int): Period in milliseconds after which a keepalive ping is sent
|
|
190
|
+
on the transport.
|
|
195
191
|
|
|
196
192
|
Returns:
|
|
197
193
|
Array with values for channel options.
|
|
@@ -199,4 +195,5 @@ def generate_channel_options(max_send_message_length=None, max_receive_message_l
|
|
|
199
195
|
|
|
200
196
|
return [('grpc.max_send_message_length', max_send_message_length or DEFAULT_MAX_MESSAGE_LENGTH),
|
|
201
197
|
('grpc.max_receive_message_length', max_receive_message_length or
|
|
202
|
-
DEFAULT_MAX_MESSAGE_LENGTH)
|
|
198
|
+
DEFAULT_MAX_MESSAGE_LENGTH),
|
|
199
|
+
('grpc.keepalive_time_ms', keep_alive_ping_time_ms or DEFAULT_KEEP_ALIVE_TIME_MS)]
|