def initialiseFromConfig(config, send_queues, recv_queues): from collections import defaultdict from ev3sim.robot import initialise_bot, RobotInteractor ev3sim.visual.utils.GLOBAL_COLOURS = config.get("colours", {}) # Keep track of index w.r.t. filename. robot_paths = defaultdict(lambda: 0) for index, robot in enumerate(config.get("robots", [])): robot_path = find_abs(robot, allowed_areas=bot_locations()) initialise_bot(config, robot_path, f"Robot-{index}", robot_paths[robot_path]) robot_paths[robot_path] += 1 ScriptLoader.instance.setRobotQueues(f"Robot-{index}", send_queues[index], recv_queues[index]) for opt in config.get("interactors", []): try: ScriptLoader.instance.addActiveScript(fromOptions(opt)) except Exception as exc: print( f"Failed to load interactor with the following options: {opt}. Got error: {exc}" ) SettingsManager.instance.setMany(config["settings"]) if ScriptLoader.instance.active_scripts: ScriptLoader.instance.startUp() ScriptLoader.instance.loadElements(config.get("elements", [])) for interactor in ScriptLoader.instance.active_scripts: if isinstance(interactor, RobotInteractor): interactor.connectDevices() for interactor in ScriptLoader.instance.active_scripts: interactor.startUp() else: print("No interactors successfully loaded. Quitting...")
def action(): with open( os.path.join( find_abs(sim_config["bots"][index], bot_locations()), fname), "w") as f: f.write("# Put your code here!\n")
def clickBots(self): # Shouldn't happen but lets be safe. if self.batch_index == -1: return from ev3sim.visual.manager import ScreenObjectManager sim_path = self.available_batches[self.batch_index][1] with open(sim_path, "r") as f: sim_config = yaml.safe_load(f) complete_path = find_abs(sim_config["bots"][0], bot_locations()) relative_dir, relative_path = make_relative(complete_path, bot_locations()) return ScreenObjectManager.instance.pushScreen( ScreenObjectManager.SCREEN_BOT_EDIT, bot_file=complete_path, bot_dir_file=(relative_dir, relative_path), )
def run_code(script_name): real = find_abs("examples/robots/default_testing", ["package"]) rel_dir, rel_path = make_relative(real, bot_locations()) with open(join(real, "config.bot"), "r") as f: bot_config = yaml.safe_load(f) bot_config["script"] = script_name bot_config["type"] = "mindstorms" if script_name.endswith( ".ev3") else "python" with open(join(real, "config.bot"), "w") as f: f.write(yaml.dump(bot_config)) sim_path = find_abs("presets/testing.sim", ["package"]) with open(sim_path, "r") as f: test_config = yaml.safe_load(f) test_config["bots"] = [rel_path] with open(sim_path, "w") as f: f.write(yaml.dump(test_config)) return run_sim(sim_path)
def playSim(self, preset): abs_path = find_abs(preset, allowed_areas=preset_locations()) with open(abs_path, "r") as f: preset_config = yaml.safe_load(f) sim_path = find_abs(preset_config["sim_location"], allowed_areas=batch_locations()) with open(sim_path, "r") as f: sim_config = yaml.safe_load(f) to_remove = [] for index in range(len(sim_config["bots"])): # Try loading this bot. try: with open( os.path.join( find_abs(sim_config["bots"][index], bot_locations()), "config.bot"), "r") as f: bot_config = yaml.safe_load(f) if not BotValidator.validate_json(bot_config): to_remove.append(index) if bot_config.get("type", "python") == "python": fname = bot_config.get("script", "code.py") else: fname = bot_config.get("script", "program.ev3") if not os.path.exists( os.path.join( find_abs(sim_config["bots"][index], bot_locations()), fname)): def action(): with open( os.path.join( find_abs(sim_config["bots"][index], bot_locations()), fname), "w") as f: f.write("# Put your code here!\n") ScreenObjectManager.instance.forceCloseError( f"Your bot {sim_config['bots'][index]} does not contain the file {fname}. You may have renamed or deleted it by accident. In order to use this bot, you need to add this file back. Click \"Add {fname}\" to create this file, or do it manually.", (f"Add {fname}", action), ) return except: to_remove.append(index) if to_remove: for index in to_remove[::-1]: del sim_config["bots"][index] with open(sim_path, "w") as f: f.write(yaml.dump(sim_config)) if not sim_config["bots"]: # We cannot play, there no are valid bots. return ScreenObjectManager.instance.pushScreen( ScreenObjectManager.SCREEN_BOTS, batch_file=sim_path, next=ScreenObjectManager.instance.SCREEN_SIM, next_kwargs={"batch": sim_path}, ) return ScreenObjectManager.instance.pushScreen( ScreenObjectManager.instance.SCREEN_SIM, batch=sim_path, )
def generateObjects(self): # First, find all bot files. if not self.in_error: self.available_bots = [] error_bots = [] for rel_dir in bot_locations(): try: actual_dir = find_abs_directory(rel_dir) except: continue for bot in BotValidator.all_valid_in_dir(actual_dir): try: # Show everything except dir and .bot with open(os.path.join(actual_dir, bot, "config.bot"), "r") as f: config = yaml.safe_load(f) # If we are hidden, or in edit mode with hidden_edit, then don't show. if not config.get("hidden", False) and not ( config.get("hidden_edit", False) and len(self.bot_keys) == 0): self.available_bots.append( (bot, os.path.join(actual_dir, bot), rel_dir, bot)) except Exception as e: sentry_sdk.capture_exception(e) error_bots.append(os.path.join(actual_dir, bot)) if self.first_launch and error_bots: self.first_launch = False self.in_error = True self.addErrorDialog( 'A problem occured loading the following bots:<br><br><font color="#cc0000">' + "<br>".join(bot for bot in error_bots) + "</font>") return self.bg = pygame_gui.elements.UIPanel( relative_rect=pygame.Rect(0, 0, *self._size), starting_layer_height=-1, manager=self, object_id=pygame_gui.core.ObjectID("background"), ) self._all_objs.append(self.bg) # Scrolling container old_y = getattr(getattr(self, "scrolling_container", None), "cur_y", 0) self.scrolling_container = CustomScroll( relative_rect=pygame.Rect(0, 0, *self._size), manager=self, object_id=pygame_gui.core.ObjectID("scroll_container"), ) self.scrolling_container.num_elems = len(self.available_bots) scrolling_size = (self._size[0] / 4 + self._size[0] / 5, self._size[1] * 0.9 - min(self._size[1] / 6, 90)) # Setting dimensions and positions on a UIScrollingContainer seems buggy. This works. self.scrolling_container.set_dimensions(scrolling_size) self.scrolling_container.set_position(scrolling_size) self.scrolling_container.cur_y = old_y self.scrolling_container.set_scroll(old_y) self._all_objs.append(self.scrolling_container) button_size = self._size[0] / 4, 60 info_size = self._size[0] / 4 - 20, 15 bot_rect = lambda i: (self._size[0] / 10, self._size[1] / 10 + i * button_size[1] * 1.5) info_rect = lambda b_r: ( b_r[0] + button_size[0] - info_size[0] - 10, b_r[1] + button_size[1] - info_size[1] - 5, ) self.bot_buttons = [] self.bot_descriptions = [] for i, (show, bot, rel_dir, filename) in enumerate(self.available_bots): self.bot_buttons.append( pygame_gui.elements.UIButton( relative_rect=pygame.Rect(*bot_rect(i), *button_size), text=show, manager=self, container=self.scrolling_container, object_id=pygame_gui.core.ObjectID( show + "-" + str(i), "list_button_highlighted" if i == self.bot_index else "list_button"), )) self.addButtonEvent(show + "-" + str(i), self.setBotIndex, i) self.bot_descriptions.append( pygame_gui.elements.UILabel( relative_rect=pygame.Rect(*info_rect(bot_rect(i)), *info_size), text=rel_dir, manager=self, container=self.scrolling_container, object_id=pygame_gui.core.ObjectID( show + "-dir-" + str(i), "button_info_selected" if i == self.bot_index else "button_info"), )) self._all_objs.extend(self.bot_buttons) self._all_objs.extend(self.bot_descriptions) preview_size = self._size[0] / 4, self._size[1] / 4 preview_size = ( min(preview_size[0], (preview_size[1] * 4) // 3), min(preview_size[1], (preview_size[0] * 3) // 4), ) try: if self.bot_index >= len(self.available_bots): self.bot_index = -1 if self.bot_index == -1: image = pygame.Surface(preview_size) image.fill(pygame.Color(self.bg.background_colour)) else: with open( os.path.join(self.available_bots[self.bot_index][1], "config.bot"), "r") as f: config = yaml.safe_load(f) bot_preview = os.path.join( self.available_bots[self.bot_index][1], config.get("preview_path", "preview.png")) image = pygame.image.load(bot_preview) except Exception as e: sentry_sdk.capture_exception(e) self.setBotIndex(-1) self.addErrorDialog( '<font color="#cc0000">The bot you have selected has some internal errors EV3Sim cannot resolve.</font><br><br>' + "If you'd like to fix this, then try manually editing the bot file in a text editor." ) return if image.get_size() != preview_size: image = pygame.transform.smoothscale( image, [int(v) for v in preview_size]) self.preview_image = pygame_gui.elements.UIImage( relative_rect=pygame.Rect(self._size[0] * 0.9 - preview_size[0], self._size[1] * 0.1, *preview_size), image_surface=image, manager=self, object_id=pygame_gui.core.ObjectID("preview-image"), ) self._all_objs.append(self.preview_image) if len(self.bot_keys) == 0: code_size = preview_size[0] * 0.4, preview_size[1] * 0.4 code_button_pos = ( self._size[0] * 0.9 - code_size[0] - 10, self._size[1] * 0.1 + preview_size[1] + 10, ) code_icon_size = code_size[1] * 0.6, code_size[1] * 0.6 self.code_button = pygame_gui.elements.UIButton( relative_rect=pygame.Rect(*code_button_pos, *code_size), text="", manager=self, object_id=pygame_gui.core.ObjectID("bot-code", "settings_buttons"), ) self.addButtonEvent("bot-code", self.clickCode) if not self.code_enable: self.code_button.disable() code_icon_path = find_abs("ui/code.png", allowed_areas=asset_locations()) self.code_icon = pygame_gui.elements.UIImage( relative_rect=pygame.Rect( *self.iconPos(code_button_pos, code_size, code_icon_size), *code_icon_size), image_surface=pygame.image.load(code_icon_path), manager=self, object_id=pygame_gui.core.ObjectID("code-icon"), ) self._all_objs.append(self.code_button) self._all_objs.append(self.code_icon) edit_button_pos = ( self._size[0] * 0.9 - preview_size[0], self._size[1] * 0.1 + preview_size[1] + 10, ) self.edit_button = pygame_gui.elements.UIButton( relative_rect=pygame.Rect(*edit_button_pos, *code_size), text="", manager=self, object_id=pygame_gui.core.ObjectID("bot-edit", "settings_buttons"), ) self.addButtonEvent("bot-edit", self.clickEdit) if not self.edit_enable: self.edit_button.disable() edit_icon_path = find_abs("ui/edit.png", allowed_areas=asset_locations()) self.edit_icon = pygame_gui.elements.UIImage( relative_rect=pygame.Rect( *self.iconPos(edit_button_pos, code_size, code_icon_size), *code_icon_size), image_surface=pygame.image.load(edit_icon_path), manager=self, object_id=pygame_gui.core.ObjectID("edit-icon"), ) self._all_objs.append(self.edit_button) self._all_objs.append(self.edit_icon) new_size = self._size[0] / 8, min(self._size[1] / 6, 90) new_icon_size = new_size[1] * 0.6, new_size[1] * 0.6 new_bot_pos = (bot_rect(0)[0] + button_size[0] - new_size[0], self._size[1] * 0.9 - new_size[1]) self.new_bot = pygame_gui.elements.UIButton( relative_rect=pygame.Rect(*new_bot_pos, *new_size), text="", manager=self, object_id=pygame_gui.core.ObjectID("new_bot", "action_button"), ) self.addButtonEvent("new_bot", self.clickNew) new_bot_path = find_abs("ui/add.png", allowed_areas=asset_locations()) self.new_icon = pygame_gui.elements.UIImage( relative_rect=pygame.Rect( *self.iconPos(new_bot_pos, new_size, new_icon_size), *new_icon_size), image_surface=pygame.image.load(new_bot_path), manager=self, object_id=pygame_gui.core.ObjectID("new_bot-icon"), ) self._all_objs.append(self.new_bot) self._all_objs.append(self.new_icon) remove_bot_pos = (bot_rect(0)[0], self._size[1] * 0.9 - new_size[1]) self.remove_bot = pygame_gui.elements.UIButton( relative_rect=pygame.Rect(*remove_bot_pos, *new_size), text="", manager=self, object_id=pygame_gui.core.ObjectID("remove_bot", "cancel-changes"), ) self.addButtonEvent("remove_bot", self.clickRemove) if not self.remove_enable: self.remove_bot.disable() remove_bot_path = find_abs("ui/bin.png", allowed_areas=asset_locations()) self.remove_icon = pygame_gui.elements.UIImage( relative_rect=pygame.Rect( *self.iconPos(remove_bot_pos, new_size, new_icon_size), *new_icon_size), image_surface=pygame.image.load(remove_bot_path), manager=self, object_id=pygame_gui.core.ObjectID("remove_bot-icon"), ) self._all_objs.append(self.remove_bot) self._all_objs.append(self.remove_icon) super().generateObjects() else: # Bot key locations, for selecting bots in batch files. self.bot_loc_spots = [] for i in range(len(self.bot_keys)): if i == self.key_index: self.bot_loc_spots.append( self.createBotImage(i, bg=pygame.Color("#80b918"))) else: self.bot_loc_spots.append(self.createBotImage(i)) self.sizeBotImage(i, big_mode=len(self.bot_keys) == 1) img = self.preview_images[i] if img is None: continue if img.get_size() != self.bot_loc_spots[i].rect.size: img = pygame.transform.smoothscale( img, (self.bot_loc_spots[i].rect.width, self.bot_loc_spots[i].rect.height)) self.bot_loc_spots[i].set_image(img) self._all_objs.extend(self.bot_loc_spots) select_size = (self._size[0] / 4 - 20) / 2, min( self._size[1] / 4, 120) select_button_pos = (self._size[0] * 0.9 - select_size[0] * 2 - 15, self._size[1] * 0.9 - select_size[1]) self.select_button = pygame_gui.elements.UIButton( relative_rect=pygame.Rect(*select_button_pos, *select_size), text="SELECT", manager=self, object_id=pygame_gui.core.ObjectID("select-bot", "action_button"), ) self.addButtonEvent("select-bot", self.clickSelect) if not self.select_enable: self.select_button.disable() self._all_objs.append(self.select_button) done_size = (self._size[0] / 4 - 20) / 2, min( self._size[1] / 4, 120) done_button_pos = (self._size[0] * 0.9 - select_size[0] - 5, self._size[1] * 0.9 - select_size[1]) self.done_button = pygame_gui.elements.UIButton( relative_rect=pygame.Rect(*done_button_pos, *done_size), text="DONE", manager=self, object_id=pygame_gui.core.ObjectID("select-done", "action_button"), ) self.addButtonEvent("select-done", self.clickDone) if self.key_index == 0: self.done_button.disable() self._all_objs.append(self.done_button) super().generateObjects()
def run_bot(bot_folder, script_name=None, edit=False): from ev3sim.validation.bot_files import BotValidator if not exists(join(bot_folder, "config.bot")): return raise_error( f"The bot {bot_folder} has been messed with, it is missing config.bot. If it has been deleted, please put it back there." ) with open(join(bot_folder, "config.bot"), "r") as f: config = yaml.safe_load(f) if script_name is not None: config["script"] = script_name config["type"] = "mindstorms" if script_name.endswith( ".ev3") else "python" with open(join(bot_folder, "config.bot"), "w") as f: f.write(yaml.dump(config)) if not BotValidator.validate_file(bot_folder): return raise_error( f"There is something wrong with the robot {bot_folder}, and so it cannot be opened or used." ) try: relative_dir, relative_path = make_relative(bot_folder, ["workspace"]) if relative_path.startswith("robots"): raise ValueError() sim_paths = [join(dirname(bot_folder), "sim.sim")] except: relative_dir, relative_path = make_relative(bot_folder, bot_locations()) sim_paths = [ find_abs("presets/soccer.sim", ["package"]), find_abs("presets/rescue.sim", ["package"]), ] if not edit: found = False for sim in sim_paths: with open(sim, "r") as f: config = yaml.safe_load(f) for botpath in config["bots"]: if botpath == relative_path: found = True break if found: return run_sim(sim) # Not in the predefined presets. # Use the testing preset. sim_path = find_abs("presets/testing.sim", ["package"]) with open(sim_path, "r") as f: test_config = yaml.safe_load(f) test_config["bots"] = [relative_path] with open(sim_path, "w") as f: f.write(yaml.dump(test_config)) return run_sim(sim_path) else: return [ScreenObjectManager.SCREEN_BOT_EDIT], [{ "bot_file": bot_folder, "bot_dir_file": (relative_dir, relative_path), }]