def proto_from_tuple(tup): """Return a bosdyn.api.mission Node from a tuple. EXPERIMENTAL. Example: :: ('do-A-then-B', bosdyn.api.mission.nodes_pb2.Sequence(always_restart=True), [ ('A', foo.bar.Command(...), []), ('B', foo.bar.Command(...), []), ] ) would make a Sequence named 'do-A-then-B' that always restarted, executing some Command named 'A' followed by the Command named 'B'. NOTE: The "List of children tuples" will only work if the parent node has 'child' or 'children' attributes. See tests/test_util.py for a longer example. Args: tup: (Name of node, Instantiated implementation of node protobuf, List of children tuples) """ node = nodes_pb2.Node() name_or_dict, inner_proto, children = tup if isinstance(name_or_dict, dict): node.name = name_or_dict.get('name', '') node.reference_id = name_or_dict.get('reference_id', '') node.node_reference = name_or_dict.get('node_reference', '') if 'user_data' in name_or_dict: node.user_data.CopyFrom(name_or_dict['user_data']) for name, pb_type in six.iteritems(name_or_dict.get('parameters', {})): parameter = util_pb2.VariableDeclaration(name=name, type=pb_type) node.parameters.add().CopyFrom(parameter) for key, value in six.iteritems( name_or_dict.get('parameter_values', {})): parameter_value = util_pb2.KeyValue(key=key) if isinstance(value, six.string_types) and value[0] == '$': parameter_value.value.parameter.name = value[1:] # Leave type unspecified, since we can't look it up easily yet. else: parameter_value.value.constant.CopyFrom( python_var_to_value(value)) node.parameter_values.add().CopyFrom(parameter_value) for key, value in six.iteritems(name_or_dict.get('overrides', {})): # Rather than keep this matching the compiler functionality, just omit the check. # It's not even a CompileError anyway. #if key not in inner_proto.DESCRIPTOR.fields_by_name: # raise CompileError('Override specified for "{}" but no such field in "{}"'.format( # key, inner_proto.DESCRIPTOR.full_name)) override = util_pb2.KeyValue(key=key) override.value.parameter.name = value # We do need the final field for setting the type accurately... #override.value.parameter.type = field_desc_to_pb_type(inner_proto.DESCRIPTOR.fields_by_name[key]) node.overrides.add().CopyFrom(override) else: node.name = name_or_dict if node.node_reference: return node num_children = len(children) inner_type = inner_proto.DESCRIPTOR.name # Do some sanity checking on the children. if hasattr(inner_proto, 'children'): if num_children == 0: raise Error('Proto "{}" of type "{}" has no children!'.format( node.name, inner_type)) for child_tup in children: child_node = proto_from_tuple(child_tup) inner_proto.children.add().CopyFrom(child_node) elif hasattr(inner_proto, 'child'): if isinstance(inner_proto, nodes_pb2.ForDuration) and num_children == 2: inner_proto.child.CopyFrom(proto_from_tuple(children[0])) inner_proto.timeout_child.CopyFrom(proto_from_tuple(children[1])) elif num_children == 1: inner_proto.child.CopyFrom(proto_from_tuple(children[0])) else: raise Error('Proto "{}" of type "{}" has {} children!'.format( node.name, inner_type, num_children)) elif num_children != 0: raise Error( 'Proto "{}" of type "{}" was given {} children, but I do not know how to add' ' them!'.format(node.name, inner_type, num_children)) node.impl.Pack(inner_proto) return node
def main(): parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group(required=True) group.add_argument('--hello-world', action='store_true', help='Target the Hello World remote mission service.') group.add_argument('--power-off', action='store_true', help='Target the Power Off remote mission service.') parser.add_argument( '--user-string', help= 'Specify the user-string input to Tick. Set to the node name in Autowalk missions.' ) subparsers = parser.add_subparsers( help='Select how this service will be accessed.', dest='host_type') # Create the parser for the "local" command. local_parser = subparsers.add_parser( 'local', help='Connect to a locally hosted service.') bosdyn.client.util.add_service_endpoint_arguments(local_parser) # Create the parser for the "robot" command. robot_parser = subparsers.add_parser( 'robot', help='Connect to a service through the robot directory.') bosdyn.client.util.add_common_arguments(robot_parser) options = parser.parse_args() if options.hello_world: directory_name = 'hello-world-callback' elif options.power_off: directory_name = 'power-off-callback' # If attempting to communicate directly to the service. if options.host_type == 'local': # Build a client that can talk directly to the RemoteMissionService implementation. client = bosdyn.mission.remote_client.RemoteClient() # Point the client at the service. We're assuming there is no encryption to set up. client.channel = grpc.insecure_channel('{}:{}'.format( options.host_ip, options.port)) # Else if attempting to communicate through the robot. else: # Register the remote mission client with the SDK instance. sdk = bosdyn.client.create_standard_sdk('RemoteMissionClientExample') sdk.register_service_client(bosdyn.mission.remote_client.RemoteClient, service_name=directory_name) robot = sdk.create_robot(options.hostname) robot.authenticate(options.username, options.password) # Create the remote mission client. client = robot.ensure_client(directory_name) inputs = [] input_values = [] if options.user_string: name = 'user-string' inputs = [ util_pb2.VariableDeclaration( name=name, type=util_pb2.VariableDeclaration.TYPE_STRING) ] input_values = [ util_pb2.KeyValue( key=name, value=util_pb2.Value(constant=util_pb2.ConstantValue( string_value=options.user_string))) ] lease_client = None body_lease = None leases = [] if options.power_off: # If we're using a lease, we'll have to authenticate to the robot to acquire it. lease_client = robot.ensure_client( bosdyn.client.lease.LeaseClient.default_service_name) # Acquire a lease and save it in the list of leases we pass to the servicer. body_lease = lease_client.acquire() leases = [body_lease] # Now run through a typical sequence of calls to the remote servicer. try: # Establish the session, telling the servicer to perform any one-time tasks. try: session_id = client.establish_session(leases=leases, inputs=inputs) except bosdyn.client.UnimplementedError: # EstablishSession is optional, so we can ignore this error. print('EstablishSession is unimplemented.') session_id = None # Begin ticking, and tick until the server indicates something other than RUNNING. response = client.tick(session_id, leases=leases, inputs=input_values) while response.status == remote_pb2.TickResponse.STATUS_RUNNING: time.sleep(0.1) response = client.tick(session_id, leases=leases, inputs=input_values) print('Servicer stopped with status {}'.format( remote_pb2.TickResponse.Status.Name(response.status))) if response.error_message: print('\tError message: {}'.format(response.error_message)) try: # We're done ticking for now -- stop this session. client.stop(session_id) # We don't want to tick with this particular session every again -- tear it down. client.teardown_session(session_id) except bosdyn.client.UnimplementedError as exc: # The exception itself can tell us what's not implemented. print('Either Stop or TeardownSession is unimplemented.') finally: # Try to return the lease, if applicable. if lease_client and body_lease: lease_client.return_lease(body_lease)
parser.add_argument('--add-resources', help=('Resource the remote mission needs, like "body".' ' Can be comma separated for multiple resources.')) parser.add_argument( '--user-string', help='Specify the user-string input to Tick. Set to the node name in Autowalk missions.') options = parser.parse_args() # Build our mission! # Start with the thing we want to do, which is to make a RemoteGrpc call... mission_call = nodes_pb2.RemoteGrpc() # ... to the service whose name matches the name remote_mission_service is using ... mission_call.service_name = remote_mission_service.SERVICE_NAME_IN_DIRECTORY # ... and if we want to provide a lease, ask for the one resource we know about ... if options.add_resources: mission_call.lease_resources[:] = options.add_resources.split(',') # ... and registered to "localhost" (i.e. the robot). mission_call.host = 'localhost' # Optionally add an input set by the RemoteGrpc node. if options.user_string: name = 'user-string' value = util_pb2.Value(constant=util_pb2.ConstantValue(string_value=options.user_string)) mission_call.inputs.add().CopyFrom(util_pb2.KeyValue(key=name, value=value)) # That will be the implementation of our mission. mission = nodes_pb2.Node() mission.impl.Pack(mission_call) with open(options.output_file, 'wb') as output: output.write(mission.SerializeToString())
help= 'Specify the user-string input to Tick. Set to the node name in Autowalk missions.' ) options = parser.parse_args() # Build our mission! # Start with the thing we want to do, which is to make a RemoteGrpc call... mission_call = nodes_pb2.RemoteGrpc() # ... to the service whose name matches the name remote_mission_service is using ... mission_call.service_name = remote_mission_service.SERVICE_NAME_IN_DIRECTORY # ... and if we want to provide a lease, ask for the one resource we know about ... if options.add_resources: mission_call.lease_resources[:] = options.add_resources.split(',') # ... and registered to "localhost" (i.e. the robot). mission_call.host = 'localhost' # Optionally add an input set by the RemoteGrpc node. if options.user_string: name = 'user-string' value = util_pb2.Value(constant=util_pb2.ConstantValue( string_value=options.user_string)) mission_call.inputs.add().CopyFrom( util_pb2.KeyValue(key=name, value=value)) # That will be the implementation of our mission. mission = nodes_pb2.Node() mission.impl.Pack(mission_call) with open(options.output_file, 'wb') as output: output.write(mission.SerializeToString())
def main(): parser = argparse.ArgumentParser() bosdyn.client.util.add_common_arguments(parser) parser.add_argument('--port', help='Port that service is listening on', type=int, required=True) parser.add_argument('--lease-host', help='Host that provides leases. Typically the robot.') parser.add_argument( '--user-string', help='Specify the user-string input to Tick. Set to the node name in Autowalk missions.') options = parser.parse_args() # Build a client that can talk to the RemoteMissionService implementation. client = bosdyn.mission.remote_client.RemoteClient() # Point the client at the service. We're assuming there is no encryption to set up. client.channel = grpc.insecure_channel('{}:{}'.format(options.hostname, options.port)) inputs = [] input_values = [] if options.user_string: name = 'user-string' inputs = [ util_pb2.VariableDeclaration(name=name, type=util_pb2.VariableDeclaration.TYPE_STRING) ] input_values = [ util_pb2.KeyValue(key=name, value=util_pb2.Value( constant=util_pb2.ConstantValue(string_value=options.user_string))) ] lease_client = None body_lease = None leases = [] # If we're using a lease, we'll have to authenticate to the robot to acquire it. if options.lease_host: sdk = bosdyn.client.create_standard_sdk('RemoteMissionClientExample') sdk.load_app_token(options.app_token) robot = sdk.create_robot(options.lease_host) robot.authenticate(options.username, options.password) lease_client = robot.ensure_client(bosdyn.client.lease.LeaseClient.default_service_name) # Acquire a lease and save it in the list of leases we pass to the servicer. body_lease = lease_client.acquire() leases = [body_lease] # Now run through a typical sequence of calls to the remote servicer. try: # Establish the session, telling the servicer to perform any one-time tasks. try: session_id = client.establish_session(leases=leases, inputs=[]) except bosdyn.client.UnimplementedError: # EstablishSession is optional, so we can ignore this error. print('EstablishSession is unimplemented.') session_id = None # Begin ticking, and tick until the server indicates something other than RUNNING. response = client.tick(session_id, leases=leases, inputs=input_values) while response.status == remote_pb2.TickResponse.STATUS_RUNNING: time.sleep(0.1) response = client.tick(session_id, leases=leases, inputs=input_values) print('Servicer stopped with status {}'.format( remote_pb2.TickResponse.Status.Name(response.status))) if response.error_message: print('\tError message: {}'.format(response.error_message)) try: # We're done ticking for now -- stop this session. client.stop(session_id) # We don't want to tick with this particular session every again -- tear it down. client.teardown_session(session_id) except bosdyn.client.UnimplementedError as exc: # The exception itself can tell us what's not implemented. print('Either Stop or TeardownSession is unimplemented.') finally: # Try to return the lease, if applicable. if lease_client and body_lease: lease_client.return_lease(body_lease)