Ejemplo n.º 1
0
    def test_can_modify(self):
        with self.__app.test_request_context('/test',
                                             headers={'X-User-Name': 'Test'}):
            self.assertTrue(
                UserAuthentication.can_modify(FakeSimulation('Test')))
            self.assertFalse(
                UserAuthentication.can_modify(FakeSimulation('default-owner')))

        with self.__app.test_request_context('/test'):
            self.assertFalse(
                UserAuthentication.can_modify(FakeSimulation('Test')))
            self.assertTrue(
                UserAuthentication.can_modify(FakeSimulation('default-owner')))
Ejemplo n.º 2
0
    def post(self, sim_id, command):
        """
        Issue user commands to the simulator recorder. See parameter description for
        supported query commands.

        :param sim_id: The simulation ID to command.
        :param command: The command to issue, supported: [start, stop, cancel, reset, save]

        :status 500: {0}
        :status 404: {1}
        :status 401: {2}
        :status 400: The command is invalid/refused by recorder - see message returned.
        :status 200: Success. The command was issued.
        """

        # validate simulation id
        sim = _get_simulation_or_abort(sim_id)

        # only the simulation owner can command the recorder
        if not UserAuthentication.can_modify(sim):
            raise NRPServicesWrongUserException()

        # pure local command to save file to storage
        if command == 'save':
            try:
                file_name = sim.lifecycle.save_record_to_user_storage()
                return {'filename': file_name}, 200

            except Exception as e:
                raise NRPServicesClientErrorException(
                    'Cannot copy record to client storage', error_code=404)

        else:

            # validate the remote command type
            valid_commands = {
                'start': SimulationRecorderRequest.START,
                'stop': SimulationRecorderRequest.STOP,
                'cancel': SimulationRecorderRequest.CANCEL,
                'reset': SimulationRecorderRequest.RESET
            }
            if command not in valid_commands:
                raise NRPServicesClientErrorException(
                    'Invalid recorder command: %s' % command, error_code=404)

            # command the recorder, if unsuccessful return the error message
            try:
                resp = sim.cle.command_simulation_recorder(
                    valid_commands[command])

                # check the command success, on failure return status 400 + error
                if not resp.value:
                    raise NRPServicesClientErrorException(resp.message)

                # successful, return status 200
                return 'success', 200

            # internal CLE ROS error if service call fails, notify frontend
            except ROSCLEClientException as e:
                raise NRPServicesGeneralException(str(e), 'CLE error', 500)
Ejemplo n.º 3
0
    def put(self, sim_id):
        """
        Change the material of a given visual. Currently, only the change of screen materials is
        implemented

        :param sim_id: The simulation ID

        :< json string visual_path: The path to the visual for which a change in material is
                                    requested
        :< json string material_name: The name of the material that will be applied

        :status 404: {0}
        :status 401: {1}
        :status 400: The parameters are invalid
        :status 200: Material applied successfully
        """
        simulation = _get_simulation_or_abort(sim_id)

        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        body = request.get_json(force=True)
        if 'visual_path' not in body:
            return "No visual_path given", 400
        if 'material' not in body:
            return "No material given", 400

        return self.__set_material(body['visual_path'], body['material'])
Ejemplo n.º 4
0
    def delete(self, sim_id, state_machine_name):
        """
        Delete a state machine

        :param sim_id: The simulation ID
        :param state_machine_name: The name of the state machine to be deleted

        :status 500: {0}
        :status 404: {1}
        :status 401: {2}
        :status 200: The delete operation was successfully called. This does not imply that the
                     state machine function was correctly deleted though.
        """
        simulation = _get_simulation_or_abort(sim_id)
        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        failure_message = "State machine destruction failed: "
        try:
            ok, response_message = simulation.delete_state_machine(
                state_machine_name)
            if ok:
                return "Success. The state machine was successfully deleted.", 200
        except Exception as e:
            info = exc_info()
            raise NRPServicesGeneralException(
                failure_message +
                " {0}: {1}".format(e.__class__.__name__, e.message),
                "State machine error",
            ), None, info[2]
        raise NRPServicesStateMachineException(
            failure_message + "\n" + response_message, 404)
Ejemplo n.º 5
0
    def put(self, sim_id, transfer_function_name, activate):
        """

        Sets the activation state of the transfer function

        :param sim_id: The simulation ID
        :param transfer_function_name: The name of the transfer function to be modified
        :param activate A boolean denoting the new desired activation status

        :status 404: {0}
        :status 401: {1}
        :status 400: {2}
        :status 200: Success. The activation state of the TF has been successfully changed

        """

        simulation = _get_simulation_or_abort(sim_id)
        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        activate_bool = activate.lower(
        ) == "true"  # convert unicode to boolean

        error_message = simulation.cle.activate_simulation_transfer_function(
            transfer_function_name, activate_bool)

        if error_message:
            raise NRPServicesTransferFunctionException(
                "Transfer function (de-)activation failed: {}\n".format(
                    error_message))

        return 200
Ejemplo n.º 6
0
    def put(self, sim_id, transfer_function_name):
        """
        Applies user changes to transfer function code

        :param sim_id: The simulation ID
        :param transfer_function_name: The name of the transfer function to be modified

        :< string data: The source code of the transfer function

        :status 404: {0}
        :status 403: {1}
        :status 401: {2}
        :status 400: {3}
        :status 200: Success. The transfer function was successfully patched
        """
        simulation = _get_simulation_or_abort(sim_id)
        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        transfer_function_source = request.data
        error_message = simulation.cle.edit_simulation_transfer_function(
            transfer_function_name, transfer_function_source)

        if error_message:
            ex_msg = ("Transfer Function patch failed: {error_msg}\n"
                      "Updated source:\n"
                      "{tf_src}").format(error_msg=error_message,
                                         tf_src=str(transfer_function_source))
            raise NRPServicesDuplicateNameException(ex_msg) if "duplicate" in error_message \
                else NRPServicesTransferFunctionException(ex_msg)

        return 200
Ejemplo n.º 7
0
    def put(self, sim_id):
        """
        Sets the simulation with the given name into a new state. Allowed values are:
        created, initialized, started, paused, stopped

        :param sim_id: The simulation id

        :< json string state: The state of the simulation to set

        :> json string state: The state of the simulation

        :status 404: {0}
        :status 401: {1}
        :status 400: The state transition is invalid
        :status 200: The state of the simulation with the given ID was successfully set
        """
        simulation = _get_simulation_or_abort(sim_id)

        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        body = request.get_json(force=True)
        try:
            simulation.state = body['state']
        except MachineError:
            raise NRPServicesStateException("Invalid transition (" +
                                            simulation.state + "->" +
                                            body['state'] + ")")
        return {'state': str(simulation.state)}, 200
Ejemplo n.º 8
0
    def post(self, sim_id):
        """
        Adds a new transfer function

        :param sim_id: The simulation ID

        :< string data: The source code of the transfer function

        :status 404: {0}
        :status 403: {1}
        :status 401: {2}
        :status 400: {3}
        :status 200: Success. The transfer function was successfully added
        """
        simulation = _get_simulation_or_abort(sim_id)
        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        transfer_function_source = request.data

        error_message = simulation.cle.add_simulation_transfer_function(
            transfer_function_source)
        if error_message:
            ex_msg = ("Adding a new Transfer Function failed: {error_msg}\n"
                      "Updated source:\n"
                      "{tf_src}").format(error_msg=error_message,
                                         tf_src=str(transfer_function_source))
            raise NRPServicesDuplicateNameException(ex_msg) if "duplicate" in error_message \
                else NRPServicesTransferFunctionException(ex_msg)

        return 200
Ejemplo n.º 9
0
    def post(self, sim_id):
        """
        Extends the simulation timeout

        :param sim_id: The simulation id

        :status 404: {0}
        :status 402: Failed to extend the timeout
        :status 412: Failed due to simulation timeout type being 'simulation'
        :status 401: {1}
        :status 200: Success. The simulation timeout has been extended
        """
        simulation = _get_simulation_or_abort(sim_id)

        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        if simulation.timeout_type == TimeoutType.SIMULATION:
            return {}, 412

        new_timeout = simulation.kill_datetime + \
            datetime.timedelta(minutes=SIMULATION_TIMEOUT_EXTEND)

        if not simulation.cle.extend_simulation_timeout(new_timeout):
            return {}, 402

        simulation.kill_datetime = new_timeout

        return {}, 200
Ejemplo n.º 10
0
    def put(self, sim_id):
        """
        Set the populations of the brain in a simulation with the specified simulation id.

        :param sim_id: The simulation ID

        :< json dict brain_populations: A dictionary indexed by population names and containing
                                             neuron indices

        :< json string brain_type: Type of the brain file ('h5' or 'py')
        :< param brain_populations: Contents of the brain file. Encoding given in field data_type
        :< json string data_type: type of the data field ('text' or 'base64')
        :< json string change_population: indicates if it has to be changes
                                        in the transfer functions
        :> json string message: Error Message if there is a syntax error in the code
        :status 404: {0}
        :status 200: Success. The populations of the brain where successfully set
        """

        simulation = _get_simulation_or_abort(sim_id)
        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        body = request.get_json(force=True)

        result = simulation.cle.set_simulation_populations(
            brain_type=body['brain_type'],
            brain_populations=json.dumps(body['brain_populations']),
            data_type=body['data_type'],
            change_population=body['change_population'])

        if result.message:
            return {'error_message': result.message}, 400

        return {'message': 'Success'}, 200
Ejemplo n.º 11
0
    def delete(self, sim_id, robot_id):
        """
        Delete a robot from the simulation.

        :param sim_id: The simulation ID.
        :param robot_id: The robot ID.

        :status 500: {0}
        :status 404: {1}
        :status 401: {2}
        :status 400: Invalid request, the JSON parameters are incorrect
        :status 200: The requested robot was deleted successfully
        """
        # pylint: disable=no-self-use
        sim = _get_simulation_or_abort(sim_id)

        if not UserAuthentication.can_modify(sim):
            raise NRPServicesWrongUserException()
        try:
            res, err = SimulationRobot.__delete_robot(sim, robot_id)

        except ROSCLEClientException as e:
            raise NRPServicesGeneralException(str(e), 'CLE error', 500)
        if not res:
            return {'res': err}, 404
        return {'res': 'success'}, 200
Ejemplo n.º 12
0
    def put(self, sim_id):
        """
        Set brain file of the simulation specified with simulation ID.
        Depending on the type of brain file, it has to be transmitted as text or as base64
        :param sim_id: The simulation ID

        :< json string brain_type: Type of the brain file ('h5' or 'py')
        :< json string data_type: type of the data field ('text' or 'base64')
        :< json string data: Contents of the brain file. Encoding given in field data_type
        :< json dict brain_populations: A dictionary indexed by population names and containing
                                        neuron indices
        :> json string error_message: Error Message if there is a syntax error in the code
        :> json int error_line: Line of code, where error occurred
        :> json int error_column: Column, where error occurred (if available)
        :> json bool handle_population_change: a flag indicating if user wants to change transfer
                                               functions according to population changes.

        :status 500: {0}
        :status 404: {1}
        :status 401: {2}
        :status 400: {3}
        :status 200: Success. The experiment brain file was replaced
        """

        simulation = _get_simulation_or_abort(sim_id)
        storage_client = StorageClient()
        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        body = request.get_json(force=True)

        file_url = body.get('urls', {}).get('fileUrl')
        if file_url:
            # TODO: fix server certificate and remove verify
            with requests.get(file_url, verify=False) as h5:
                filename = os.path.basename(file_url)
                with open(os.path.join(simulation.lifecycle.sim_dir, filename),
                          'w') as f:
                    f.write(h5.content)
                storage_client.create_or_update(simulation.token,
                                                simulation.experiment_id,
                                                filename, h5.content,
                                                "text/plain")

        result = simulation.cle.set_simulation_brain(
            brain_type=body['brain_type'],
            data=body['data'],
            data_type=body['data_type'],
            brain_populations=json.dumps(body['brain_populations']))

        if result.error_message:
            # Error in given brain
            return {
                'error_message': result.error_message,
                'error_line': result.error_line,
                'error_column': result.error_column
            }, 400
        # Success
        return {'message': "Success"}, 200
Ejemplo n.º 13
0
    def put(self, sim_id, experiment_id):
        """
        Calls the CLE for resetting a given simulation to the last saved state in the storage.

        :param sim_id: The simulation ID.
        :param experiment_id: The experiment ID

        :> json resetType: the reset type the user wants to be performed, details about possible
                          values are given in
                          GazeboRosPackages/src/cle_ros_msgs/srv/ResetSimulation.srv

        :status 500: {0}
        :status 404: {1}
        :status 401: {2}
        :status 400: Invalid request, the JSON parameters are incorrect
        :status 200: The requested reset was performed successfully
        """
        sim = _get_simulation_or_abort(sim_id)

        if not UserAuthentication.can_modify(sim):
            raise NRPServicesWrongUserException()

        req_body = request.get_json(force=True)
        context_id = req_body.get('contextId', None)

        for missing_par in (par for par in self.ResetRequest.required if par not in req_body):
            raise NRPServicesClientErrorException('Missing parameter {}'.format(missing_par))

        for invalid_p in (par for par in req_body if par not in self.ResetRequest.resource_fields):
            raise NRPServicesClientErrorException('Invalid parameter {}'.format(invalid_p))

        reset_type = req_body.get('resetType')

        rsr = srv.ResetSimulationRequest

        try:
            if reset_type == rsr.RESET_ROBOT_POSE:
                sim.cle.reset(reset_type)
            elif reset_type == rsr.RESET_WORLD:
                sim.cle.reset(reset_type,
                              world_sdf=self._get_sdf_world_from_storage(experiment_id, context_id))
            elif reset_type == rsr.RESET_FULL:
                brain_path, populations, _ = \
                    self._get_brain_info_from_storage(experiment_id, context_id)

                if sim.playback_path is None:
                    self.reset_from_storage_all(sim, experiment_id, context_id)

                sim.cle.reset(reset_type,
                              world_sdf=self._get_sdf_world_from_storage(experiment_id, context_id),
                              brain_path=brain_path,
                              populations=populations)
            else:
                return {}, 400  # Other reset modes are unsupported

        except ROSCLEClientException as e:
            raise NRPServicesGeneralException(str(e), 'CLE error', 500)

        return {}, 200
Ejemplo n.º 14
0
    def put(self, sim_id, state_machine_name):
        """
        Applies user changes to state machine code.
        If the simulation is paused or started, it will be paused.
        A stopped, created or failed simulation will fail the request with error code 403

        :param sim_id: The simulation ID
        :param state_machine_name: The name of the state machine to be modified

        :< json string data: The source code of the state machine

        :status 404: {0}
        :status 401: {1}
        :status 400: {2}
        :status 200: Success. The code was successfully patched
        """
        simulation = _get_simulation_or_abort(sim_id)
        assert simulation, Simulation
        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        state_machine_source = request.data
        try:
            simulation.set_state_machine_code(state_machine_name,
                                              state_machine_source)
            return "Success. The code was successfully patched.", 200
        except (AttributeError, NameError) as e:
            info = exc_info()
            raise NRPServicesStateMachineException(e.message,
                                                   400), None, info[2]

        except SyntaxError as e:
            info = exc_info()
            args_txt = ""
            for text in e.args:
                args_txt += " {0}".format(text)
            raise NRPServicesStateMachineException(
                "The source code is invalid: "
                "SyntaxError in line {0}{1}.".format(e.lineno, args_txt),
                400), None, info[2]

        except Exception as e:
            info = exc_info()
            raise NRPServicesGeneralException(
                "Update of state machine code failed. "
                "{0}: {1}".format(e.__class__.__name__, e.message),
                "State machine error"), None, info[2]
Ejemplo n.º 15
0
    def post(self, sim_id, robot_id):
        """
        Add a new robot to the simulation.

        :param sim_id: The simulation ID.
        :param robot_id: The robot string ID.

        :status 500: {0}
        :status 404: {1}
        :status 401: {2}
        :status 400: Invalid request, the JSON parameters are incorrect
        :status 200: The requested robot was created successfully
        """
        # pylint: disable=no-self-use
        sim = _get_simulation_or_abort(sim_id)

        if not UserAuthentication.can_modify(sim):
            raise NRPServicesWrongUserException()

        body = request.get_json(force=True)

        missing_parameters = [
            item for item in SimulationRobot.RobotPostRequest.required
            if item not in body
        ]
        if missing_parameters:
            raise NRPServicesClientErrorException('Missing parameter(s): ' +
                                                  ' '.join(missing_parameters))

        try:
            res, err = SimulationRobot.__add_new_robot(
                sim=sim,
                robot_id=robot_id,
                robot_model=body.get(ParamNames.ROBOT_MODEL),
                is_custom=(body.get(ParamNames.IS_CUSTOM, None) == 'True'),
                initial_pose=body.get(ParamNames.ROBOT_POSE, None))
        except ROSCLEClientException as e:
            raise NRPServicesGeneralException(str(e), 'CLE error', 500)

        if not res:
            return {'res': err}, 404
        return {'res': 'success'}, 200
Ejemplo n.º 16
0
    def delete(self, sim_id, transfer_function_name):
        """
        Delete a transfer function

        :param sim_id: The simulation ID
        :param transfer_function_name: The name of the transfer function to be deleted

        :status 404: {0}
        :status 401: {1}
        :status 200: Success. The code was successfully patched
        """
        simulation = _get_simulation_or_abort(sim_id)
        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesWrongUserException()

        response = simulation.cle.delete_simulation_transfer_function(
            transfer_function_name)

        if response is False:
            raise NRPServicesTransferFunctionException(
                "Transfer function delete failed: {}".format(
                    str(transfer_function_name)))
        return 200
Ejemplo n.º 17
0
    def put(self, sim_id):
        """
        Raises a light event

        :param sim_id: The simulation ID

        :< json string name: The light to change
        :< json RGBADescription diffuse: the diffuse color
        :< json float attenuation_constant: the attenuation constant
        :< json float attenuation_linear: the attenuation linear
        :< json float attenuation_quadratic: the attenuation quadratic

        :status 404: {0}
        :status 401: {1}
        :status 400: The parameters are invalid
        :status 200: Successfully raised event
        """
        simulation = _get_simulation_or_abort(sim_id)

        if not UserAuthentication.can_modify(simulation):
            raise NRPServicesClientErrorException(
                "You need to be the simulation owner to trigger interactions", error_code=401)

        body = request.get_json(force=True)
        if 'name' not in body:
            raise NRPServicesClientErrorException("No name given")

        in_name = LightControl.as_ascii(body['name'])
        in_diffuse = body.get('diffuse')

        in_attenuation_constant = LightControl.as_float(body.get('attenuation_constant'))
        in_attenuation_linear = LightControl.as_float(body.get('attenuation_linear'))
        in_attenuation_quadratic = LightControl.as_float(body.get('attenuation_quadratic'))

        diffuse = None

        if in_diffuse is not None:
            diffuse = ColorRGBA(float(in_diffuse['r']), float(in_diffuse['g']),
                                float(in_diffuse['b']), float(in_diffuse['a']))

        if in_diffuse is None or in_attenuation_constant is None \
                or in_attenuation_linear is None or in_attenuation_quadratic is None:
            try:
                rospy.wait_for_service('/gazebo/get_light_properties', 3)
            except rospy.ROSException as exc:
                raise NRPServicesUnavailableROSService(str(exc))

            get_light_properties = rospy.ServiceProxy('/gazebo/get_light_properties',
                                                      GetLightProperties)

            try:
                light_properties = get_light_properties(light_name=in_name)

                if in_attenuation_constant is None:
                    in_attenuation_constant = light_properties.attenuation_constant
                if in_attenuation_linear is None:
                    in_attenuation_linear = light_properties.attenuation_linear
                if in_attenuation_quadratic is None:
                    in_attenuation_quadratic = light_properties.attenuation_quadratic
                if in_diffuse is None:
                    diffuse = light_properties.diffuse

            except rospy.ServiceException as exc:
                raise NRPServicesClientErrorException(
                    "Service did not process request:" + str(exc))

        try:
            rospy.wait_for_service('/gazebo/set_light_properties', 3)
        except rospy.ROSException as exc:
            raise NRPServicesUnavailableROSService(str(exc))

        set_light_properties = rospy.ServiceProxy('/gazebo/set_light_properties',
                                                  SetLightProperties)

        try:
            set_light_properties(light_name=in_name,
                                 diffuse=diffuse,
                                 attenuation_constant=in_attenuation_constant,
                                 attenuation_linear=in_attenuation_linear,
                                 attenuation_quadratic=in_attenuation_quadratic)
        except rospy.ServiceException as exc:
            raise NRPServicesClientErrorException("Service did not process request: " + str(exc))

        return "Changed light intensity", 200