Example #1
0
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)
Example #3
0
    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())
Example #4
0
        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())
Example #5
0
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)