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.matches_x_user_name_header( request, simulation.owner): 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: " + error_message + "\n") return 200
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.matches_x_user_name_header( request, simulation.owner): 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'])
def get(self, sim_id): """ Gets the simulation CSV recorders' content :param sim_id: The simulation ID whose transfer functions are retrieved will be saved :> json string file: CSV file name :> json array data: CSV data as a list of strings :status 500: Error when retrieving recorder files :status 404: {0} :status 401: {1} :status 200: Returns a list of recorded csv files and data """ simulation = _get_simulation_or_abort(sim_id) if not UserAuthentication.matches_x_user_name_header( request, simulation.owner): raise NRPServicesWrongUserException() response = [] csv_files = simulation.cle.get_simulation_CSV_recorders_files() if csv_files: for csv_file in csv_files: file_data = [] response.append({"file": csv_file.name, "data": file_data}) with open(csv_file.temporary_path) as csvfile: spamreader = csv.reader(csvfile, delimiter=' ', quotechar='|') for row in spamreader: file_data.append(', '.join(row)) return response, 200
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.matches_x_user_name_header( request, simulation.owner): raise NRPServicesWrongUserException() transfer_function_source = request.data error_message = simulation.cle.edit_simulation_transfer_function( transfer_function_name, transfer_function_source) if error_message and "duplicate" in error_message: raise NRPServicesDuplicateNameException( "Transfer function patch failed: " + error_message + "\n" + "Updated source:\n" + str(transfer_function_source)) elif error_message: raise NRPServicesTransferFunctionException( "Transfer function patch failed: " + error_message + "\n" + "Updated source:\n" + str(transfer_function_source)) return 200
def put(self, sim_id): """ Puts the structured transfer functions :param sim_id: The simulation id :status 500: Simulation in state [STATE]. Can't update transfer function :status 404: {0} :status 401: {1} :status 400: {2} :status 200: Success. The transfer function was successfully patched """ simulation = _get_simulation_or_abort(sim_id) if not UserAuthentication.matches_x_user_name_header( request, simulation.owner): raise NRPServicesWrongUserException() transfer_function = json.loads(request.data, object_hook=JSONObject) error_message = simulation.cle.set_structured_transfer_function( transfer_function) if error_message: raise NRPServicesTransferFunctionException( "Transfer function patch failed: " + error_message + "\n" + "Transfer function:\n" + request.data) return 200
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 401: {1} :status 200: Success. The simulation timeout has been extended """ simulation = _get_simulation_or_abort(sim_id) if not UserAuthentication.matches_x_user_name_header( request, simulation.owner): raise NRPServicesWrongUserException() 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
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.matches_x_user_name_header(request, simulation.owner): 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)
def test_matches_x_user_name_header(self): with self.__app.test_request_context('/test', headers={'X-User-Name': 'Test'}): self.assertTrue( UserAuthentication.matches_x_user_name_header( flask.request, 'Test')) self.assertFalse( UserAuthentication.matches_x_user_name_header( flask.request, 'default-owner')) with self.__app.test_request_context('/test'): self.assertFalse( UserAuthentication.matches_x_user_name_header( flask.request, 'Test')) self.assertTrue( UserAuthentication.matches_x_user_name_header( flask.request, 'default-owner'))
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.matches_x_user_name_header(request, simulation.owner): 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]
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 (given in the field data_type) :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 additional_populations: A dictionary indexed by population names and containing neuron indices :< json int change_population: A flag to select an action on population name change :> 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) if not UserAuthentication.matches_x_user_name_header( request, simulation.owner): raise NRPServicesWrongUserException() body = request.get_json(force=True) result = simulation.cle.set_simulation_brain( body['brain_type'], body['data'], body['data_type'], json.dumps(body['additional_populations']), body['change_population']) if result.error_message is not "": # Error in given brain return { 'error_message': result.error_message, 'error_line': result.error_line, 'error_column': result.error_column, 'handle_population_change': result.handle_population_change }, 400 # Success return {'message': "Success"}, 200
def put(self, sim_id): """ Calls the CLE for resetting a given simulation. :param sim_id: The simulation 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.matches_x_user_name_header( request, sim.owner): raise NRPServicesWrongUserException() body = request.get_json(force=True) for par in SimulationReset.ResetRequest.required: if par not in body: raise NRPServicesClientErrorException('Missing parameter %s' % (par, )) for par in body: if par not in SimulationReset.ResetRequest.resource_fields: raise NRPServicesClientErrorException('Invalid parameter %s' % (par, )) try: reset_type = body.get('resetType') if reset_type == ResetSimulationRequest.RESET_FULL: SimulationReset.reset_brain(sim) SimulationReset.reset_transfer_functions(sim) SimulationReset.reset_state_machines(sim) sim.cle.reset(reset_type) elif reset_type == ResetSimulationRequest.RESET_BRAIN: SimulationReset.reset_brain(sim) else: sim.cle.reset(reset_type) except ROSCLEClientException as e: raise NRPServicesGeneralException(str(e), 'CLE error', 500) return {}, 200
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] :status 500: {0} :status 404: {1} :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.matches_x_user_name_header(request, sim.owner): raise NRPServicesWrongUserException() # validate the 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)
def put(self, sim_id): """ Save the simulation CSV recorders' content to the storage. :param sim_id: The simulation ID :status 500: Error when saving recorder files :status 404: {0} :status 401: {1} :status 200: Success. Files saved into storage """ simulation = _get_simulation_or_abort(sim_id) if not UserAuthentication.matches_x_user_name_header( request, simulation.owner): raise NRPServicesWrongUserException() csv_files = simulation.cle.get_simulation_CSV_recorders_files() # Done here in order to avoid circular dependencies introduced by the # way we __init__ the rest_server module. from hbp_nrp_backend.storage_client_api.StorageClient \ import StorageClient client = StorageClient() time_string = get_date_and_time_string() subfolder_name = string.join(['csv_records', time_string], '_') folder_uuid = client.create_folder( UserAuthentication.get_header_token(request), simulation.experiment_id, subfolder_name)['uuid'] if csv_files: for csv_file in csv_files: with open(csv_file.temporary_path) as csvfile: client.create_or_update( UserAuthentication.get_header_token(request), folder_uuid, csv_file.name, csvfile.read(), 'text/plain') return 200
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.matches_x_user_name_header( request, simulation.owner): raise NRPServicesWrongUserException() response = simulation.cle.delete_simulation_transfer_function( transfer_function_name) if response is False: raise NRPServicesTransferFunctionException( "Transfer function delete failed: " + str(transfer_function_name)) return 200
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.matches_x_user_name_header( request, simulation.owner): 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