def check_override(scene: CachedScene, project: CachedProject, obj_id: str, override: Parameter, add_new_one: bool = False) -> SceneObject: obj = scene.object(obj_id) for par in glob.OBJECT_TYPES[obj.type].meta.settings: if par.name == override.name: if par.type != override.type: raise Arcor2Exception("Override can't change parameter type.") break else: raise Arcor2Exception("Unknown parameter name.") if add_new_one: try: for existing_override in project.overrides[obj.id]: if override.name == existing_override.name: raise Arcor2Exception("Override already exists.") except KeyError: pass else: if obj.id not in project.overrides: raise Arcor2Exception("There are no overrides for the object.") for override in project.overrides[obj.id]: if override.name == override.name: break else: raise Arcor2Exception("Override not found.") return obj
def test_obj_relative_ap_global() -> None: scene = Scene("s1") # object rotated 90° clock-wise so1 = SceneObject( "so1", "WhatEver", Pose(Position(1, 0, 0), Orientation(0, 0, -0.707, 0.707))) scene.objects.append(so1) cached_scene = CachedScene(scene) project = Project("p1", scene.id) ap1 = ActionPoint("ap1", Position(1, 0, 0), parent=so1.id) project.action_points.append(ap1) ap2 = ActionPoint("ap2", Position(1, 0, 0), parent=ap1.id) no1 = NamedOrientation("o1", Orientation()) ap2.orientations.append(no1) project.action_points.append(ap2) cached_project = CachedProject(project) make_relative_ap_global(cached_scene, cached_project, ap2) check_ap(ap2) assert ap2.position == Position(1, -2, 0) assert so1.pose assert no1.orientation == so1.pose.orientation make_global_ap_relative(cached_scene, cached_project, ap2, ap1.id) assert ap2.position == Position(1, 0, 0) assert no1.orientation == Orientation() check_ap(ap2)
def test_constant() -> None: scene = Scene("s1") obj = SceneObject("test_name", Test.__name__) scene.objects.append(obj) project = Project("p1", "s1") ap1 = ActionPoint("ap1", Position()) project.action_points.append(ap1) const_value = 1234 const = ProjectParameter("int_const", "integer", json.dumps(const_value)) project.parameters.append(const) ac1 = Action( "ac1", f"{obj.id}/test_par", flows=[Flow()], parameters=[ ActionParameter("param", ActionParameter.TypeEnum.CONSTANT, json.dumps(const.id)) ], ) ap1.actions.append(ac1) project.logic.append(LogicItem(LogicItem.START, ac1.id)) project.logic.append(LogicItem(ac1.id, LogicItem.END)) src = program_src({Test.__name__: Test}, CachedProject(project), CachedScene(scene)) assert f"{const.name} = {const_value}" in src assert f"test_name.{Test.test_par.__name__}({const.name}, an='ac1')" in src
def test_blind_branch() -> None: scene = Scene("s1") obj = SceneObject("test_name", Test.__name__) scene.objects.append(obj) project = Project("p1", "s1") ap1 = ActionPoint("ap1", Position()) project.action_points.append(ap1) ac1 = Action("ac1", f"{obj.id}/test", flows=[Flow(outputs=["bool_res"])]) ap1.actions.append(ac1) ac2 = Action("ac2", f"{obj.id}/test", flows=[Flow()]) ap1.actions.append(ac2) ac3 = Action("ac3", f"{obj.id}/test", flows=[Flow()]) ap1.actions.append(ac3) ac4 = Action("ac4", f"{obj.id}/test", flows=[Flow()]) ap1.actions.append(ac4) project.logic.append(LogicItem(LogicItem.START, ac1.id)) project.logic.append(LogicItem(ac1.id, ac2.id, ProjectLogicIf(f"{ac1.id}/default/0", json.dumps(True)))) project.logic.append(LogicItem(ac1.id, ac3.id, ProjectLogicIf(f"{ac1.id}/default/0", json.dumps(False)))) project.logic.append(LogicItem(ac2.id, ac4.id)) project.logic.append(LogicItem(ac4.id, LogicItem.END)) with pytest.raises(SourceException, match=f"Action {ac3.name} has no outputs."): program_src({Test.__name__: Test}, CachedProject(project), CachedScene(scene))
async def update_scene(scene: CachedScene) -> datetime: assert scene.id ret = await ps.update_scene(scene.scene) scene.modified = ret if not scene.created: scene.created = scene.modified _scenes_list.listing[scene.id] = IdDesc(scene.id, scene.name, scene.created, scene.modified, scene.description) _scenes[scene.id] = deepcopy(scene) _scenes[scene.id].int_modified = None return ret
def test_prev_result() -> None: scene = Scene("s1") obj = SceneObject("test_name", Test.__name__) scene.objects.append(obj) project = Project("p1", "s1") ap1 = ActionPoint("ap1", Position()) project.action_points.append(ap1) ac1 = Action("ac1", f"{obj.id}/{Test.get_int.__name__}", flows=[Flow(outputs=["res"])]) ap1.actions.append(ac1) ac2 = Action( "ac2", f"{obj.id}/{Test.test_par.__name__}", flows=[Flow()], parameters=[ ActionParameter("param", ActionParameter.TypeEnum.LINK, json.dumps(f"{ac1.id}/default/0")) ], ) ap1.actions.append(ac2) project.logic.append(LogicItem(LogicItem.START, ac1.id)) project.logic.append(LogicItem(ac1.id, ac2.id)) project.logic.append(LogicItem(ac2.id, LogicItem.END)) src = program_src({Test.__name__: Test}, CachedProject(project), CachedScene(scene)) assert f"res = test_name.{Test.get_int.__name__}(an='{ac1.name}')" in src assert f"test_name.{Test.test_par.__name__}(res, an='{ac2.name}')" in src # test wrong order of logic project.logic.clear() project.logic.append(LogicItem(LogicItem.START, ac2.id)) project.logic.append(LogicItem(ac2.id, ac1.id)) project.logic.append(LogicItem(ac1.id, LogicItem.END)) with pytest.raises(SourceException): program_src({Test.__name__: Test}, CachedProject(project), CachedScene(scene))
def test_slots() -> None: """Tests whether classes from cached module uses __slots__.""" s = Scene("") p = Project("", s.id) assert not hasattr(CachedScene(s), "__dict__") assert not hasattr(CachedProject(p), "__dict__") assert not hasattr(UpdateableCachedScene(s), "__dict__") assert not hasattr(UpdateableCachedProject(p), "__dict__")
def check_ap_parent(scene: CachedScene, proj: CachedProject, parent: Optional[str]) -> None: if not parent: return if parent in scene.object_ids: if scene.object(parent).pose is None: raise Arcor2Exception("AP can't have object without pose as parent.") elif parent not in proj.action_points_ids: raise Arcor2Exception("AP has invalid parent ID (not an object or another AP).")
def test_get_value() -> None: p = Pose(Position(1, 2, 3), Orientation(1, 0, 0, 0)) scene = Scene("s1") obj = SceneObject("test_name", TestObject.__name__) scene.objects.append(obj) project = Project("p1", "s1") ap1 = ActionPoint("ap1", Position(1, 0, 0)) project.action_points.append(ap1) ap2 = ActionPoint("ap2", Position(), parent=ap1.id) project.action_points.append(ap2) ori1 = NamedOrientation("ori1", p.orientation) ap2.orientations.append(ori1) invalid_param_name = "invalid_param" ac1 = Action( "ac1", f"{obj.id}/{TestObject.action.__name__}", parameters=[ ActionParameter(param_name, PosePlugin.type_name(), json.dumps(ori1.id)), ActionParameter(invalid_param_name, PosePlugin.type_name(), json.dumps("non_sense")), ], ) ap1.actions.append(ac1) cscene = CachedScene(scene) cproject = CachedProject(project) with pytest.raises(Arcor2Exception): PosePlugin.parameter_value(type_defs, cscene, cproject, ac1.id, "non_sense") with pytest.raises(Arcor2Exception): PosePlugin.parameter_value(type_defs, cscene, cproject, "non_sense", param_name) with pytest.raises(ParameterPluginException): PosePlugin.parameter_value(type_defs, cscene, cproject, ac1.id, invalid_param_name) value = PosePlugin.parameter_value(type_defs, cscene, cproject, ac1.id, param_name) exe_value = PosePlugin.parameter_execution_value(type_defs, cscene, cproject, ac1.id, param_name) assert value == value assert value != exe_value
def project_problems(obj_types: ObjectTypeDict, scene: CachedScene, project: CachedProject) -> list[str]: if project.scene_id != scene.id: return ["Project/scene mismatch."] problems: list[str] = scene_problems(obj_types, scene) for proj_param in project.parameters: try: check_project_parameter(project, proj_param) except Arcor2Exception as e: problems.append(str(e)) for ap in project.action_points: try: check_ap_parent(scene, project, ap.parent) except Arcor2Exception: problems.append(f"Action point {ap.name} has invalid parent: {ap.parent}.") for joints in project.ap_joints(ap.id): if joints.robot_id not in scene.object_ids: problems.append( f"Action point {ap.name} has joints ({joints.name}) for an unknown robot: {joints.robot_id}." ) for action in project.actions: # check if objects have used actions obj_id, action_type = action.parse_type() if obj_id not in scene.object_ids: problems.append(f"Object ID {obj_id} which action is used in {action.name} does not exist in scene.") continue scene_obj = scene.object(obj_id) if action_type not in obj_types[scene_obj.type].actions: problems.append( f"ObjectType {scene_obj.type} does not have action {action_type} used in {action.name}." ) continue action_meta = obj_types[scene_obj.type].actions[action_type] try: check_action_params(obj_types, scene, project, action, action_meta) except Arcor2Exception as e: problems.append(str(e)) try: check_flows(project, action, action_meta) except Arcor2Exception as e: problems.append(str(e)) return problems
def patch_with_action_mapping(type_def: type[Generic], scene: CachedScene, project: CachedProject) -> None: setattr( type_def, ACTION_NAME_ID_MAPPING_ATTR, { act.name: act.id for act in project.actions if scene.object(act.parse_type().obj_id).type == type_def.__name__ }, )
async def get_scene(scene_id: str) -> CachedScene: async with _scenes_list_lock: try: scene = _scenes[scene_id] assert scene.modified except KeyError: scene = CachedScene(await ps.get_scene(scene_id)) _scenes[scene_id] = scene else: await _update_list(ps.get_scenes, _scenes_list, _scenes) if scene_id not in _scenes_list.listing: _scenes.pop(scene_id, None) raise Arcor2Exception("Scene removed externally.") # scene in cache is outdated if scene.modified < _scenes_list.listing[scene_id].modified: scene = CachedScene(await ps.get_scene(scene_id)) _scenes[scene_id] = scene return scene
def test_get_value() -> None: scene = Scene("s1", "s1") obj = SceneObject("test_name", TestObject.__name__) prj = ProjectRobotJoints("name", obj.id, [Joint("name", 0.333)]) scene.objects.append(obj) project = Project("p1", "s1") ap1 = ActionPoint("ap1", Position(1, 0, 0)) ap1.robot_joints.append(prj) project.action_points.append(ap1) invalid_param_name = "invalid_param" act = Action( "ac1", f"{obj.id}/{TestObject.action.__name__}", parameters=[ ActionParameter(param_name, JointsPlugin.type_name(), json.dumps(prj.id)), ActionParameter(invalid_param_name, JointsPlugin.type_name(), json.dumps("non_sense")), ], ) ap1.actions.append(act) cscene = CachedScene(scene) cproject = CachedProject(project) with pytest.raises(Arcor2Exception): JointsPlugin.parameter_value(type_defs, cscene, cproject, act.id, "non_sense") with pytest.raises(Arcor2Exception): JointsPlugin.parameter_value(type_defs, cscene, cproject, "non_sense", param_name) with pytest.raises(ParameterPluginException): JointsPlugin.parameter_value(type_defs, cscene, cproject, act.id, invalid_param_name) value = JointsPlugin.parameter_value(type_defs, cscene, cproject, act.id, param_name) exe_value = JointsPlugin.parameter_execution_value(type_defs, cscene, cproject, act.id, param_name) assert value == value assert value == exe_value
def test_get_value() -> None: img = Image.new("RGB", (320, 240)) scene = Scene("s1", "s1") obj = SceneObject("test_name", TestObject.__name__) scene.objects.append(obj) project = Project("p1", "s1") ap1 = ActionPoint("ap1", Position(1, 0, 0)) project.action_points.append(ap1) invalid_param_name = "invalid_param" ac1 = Action( "ac1", f"{obj.id}/{TestObject.action.__name__}", parameters=[ ActionParameter(param_name, ImagePlugin.type_name(), ImagePlugin.value_to_json(img)), ActionParameter(invalid_param_name, ImagePlugin.type_name(), json.dumps("non_sense")), ], ) ap1.actions.append(ac1) cscene = CachedScene(scene) cproject = CachedProject(project) with pytest.raises(Arcor2Exception): ImagePlugin.parameter_value(type_defs, cscene, cproject, ac1.id, "non_sense") with pytest.raises(Arcor2Exception): ImagePlugin.parameter_value(type_defs, cscene, cproject, "non_sense", param_name) with pytest.raises(ParameterPluginException): ImagePlugin.parameter_value(type_defs, cscene, cproject, ac1.id, invalid_param_name) value = ImagePlugin.parameter_value(type_defs, cscene, cproject, ac1.id, param_name) exe_value = ImagePlugin.parameter_execution_value(type_defs, cscene, cproject, ac1.id, param_name) assert value == value assert value == exe_value
def find_object_action(obj_types: ObjectTypeDict, scene: CachedScene, action: Action) -> ObjectAction: obj_id, action_type = action.parse_type() obj = scene.object(obj_id) obj_type = obj_types[obj.type] try: act = obj_type.actions[action_type] except KeyError: raise Arcor2Exception("Unknown type of action.") if act.disabled: raise Arcor2Exception("Action is disabled.") return act
def test_get_value(self, val: str) -> None: scene = Scene("s1") obj = SceneObject("test_name", TestObject.__name__) scene.objects.append(obj) project = Project("p1", "s1") ap1 = ActionPoint("ap1", Position()) project.action_points.append(ap1) invalid_param_name = "invalid_param" ac1 = Action( "ac1", f"{obj.id}/{TestObject.action.__name__}", parameters=[ ActionParameter(param_name, StringPlugin.type_name(), StringPlugin.value_to_json(val)), ActionParameter(invalid_param_name, StringPlugin.type_name(), json.dumps(666)), ], ) ap1.actions.append(ac1) cscene = CachedScene(scene) cproject = CachedProject(project) with pytest.raises(Arcor2Exception): StringPlugin.parameter_value({}, cscene, cproject, ac1.id, "non_sense") with pytest.raises(Arcor2Exception): StringPlugin.parameter_value({}, cscene, cproject, "non_sense", param_name) with pytest.raises(ParameterPluginException): StringPlugin.parameter_value({}, cscene, cproject, ac1.id, invalid_param_name) value = StringPlugin.parameter_value({}, cscene, cproject, ac1.id, param_name) exe_value = StringPlugin.parameter_execution_value({}, cscene, cproject, ac1.id, param_name) assert value == val assert value == exe_value
def test_make_relative_ap_global_and_relative_again() -> None: scene = Scene("s1") so1 = SceneObject("so1", "WhatEver", Pose(Position(3, 0, 0), Orientation())) scene.objects.append(so1) cached_scene = CachedScene(scene) project = Project("p1", scene.id) ap1 = ActionPoint("ap1", Position(-1, 0, 0), parent=so1.id) project.action_points.append(ap1) ap2 = ActionPoint("ap2", Position(-1, 0, 0), parent=ap1.id) project.action_points.append(ap2) ap3 = ActionPoint( "ap3", Position(-1, 0, 0), parent=ap2.id, orientations=[NamedOrientation("bla", random_orientation())]) project.action_points.append(ap3) cached_project = CachedProject(project) assert ap3.parent ap3_parent = get_parent_pose(cached_scene, cached_project, ap3.parent) assert Pose(ap2.position, Orientation()) == ap3_parent.pose assert ap3_parent.parent_id == ap1.id make_relative_ap_global(cached_scene, cached_project, ap3) check_ap(ap3) assert ap3.parent is None assert ap3.position.x == 0.0 # type: ignore make_global_ap_relative(cached_scene, cached_project, ap3, ap2.id) check_ap(ap3) assert ap3.parent == ap2.id assert ap3.position.x == -1 ap3.parent = "something_unknown" with pytest.raises(Arcor2Exception): make_relative_ap_global(cached_scene, cached_project, ap3)
def find_object_action(scene: CachedScene, action: common.Action) -> ObjectAction: obj_id, action_type = action.parse_type() obj = scene.object(obj_id) try: obj_type = glob.OBJECT_TYPES[obj.type] except KeyError: raise Arcor2Exception("Unknown object type.") try: act = obj_type.actions[action_type] except KeyError: raise Arcor2Exception("Unknown type of action.") if act.disabled: raise Arcor2Exception("Action is disabled.") return act
async def project_info( project_id: str, scenes_lock: asyncio.Lock, scenes: Dict[str, CachedScene]) -> srpc.p.ListProjects.Response.Data: project = await storage.get_project(project_id) assert project.modified is not None pd = srpc.p.ListProjects.Response.Data(id=project.id, desc=project.desc, name=project.name, scene_id=project.scene_id, modified=project.modified) try: cached_project = UpdateableCachedProject(project) except CachedProjectException as e: pd.problems.append(str(e)) return pd try: async with scenes_lock: if project.scene_id not in scenes: scenes[project.scene_id] = CachedScene(await storage.get_scene( project.scene_id)) except storage.ProjectServiceException: pd.problems.append("Scene does not exist.") return pd pd.problems = project_problems(scenes[project.scene_id], cached_project) pd.valid = not pd.problems if not pd.valid: return pd try: # TODO call build service!!! pd.executable = True except Arcor2Exception as e: pd.problems.append(str(e)) return pd
def get_parent_pose(scene: CScene, project: CProject, parent_id: str) -> Parent: """Returns pose of the parent and parent of the parent (if any). :param scene: :param project: :param parent_id: :return: """ if parent_id in scene.object_ids: parent_obj = scene.object(parent_id) if not parent_obj.pose: raise Arcor2Exception("Parent object does not have pose!") # TODO find parent object in the graph (SceneObject has "children" property) return Parent(parent_obj.pose, None) elif parent_id in project.action_points_ids: ap = project.bare_action_point(parent_id) return Parent(Pose(ap.position, Orientation()), ap.parent) else: raise Arcor2Exception(f"Unknown parent_id {parent_id}.")
def check_object(scene: CachedScene, obj: SceneObject, new_one: bool = False) -> None: """Checks if object can be added into the scene.""" assert not obj.children if obj.type not in glob.OBJECT_TYPES: raise Arcor2Exception("Unknown object type.") obj_type = glob.OBJECT_TYPES[obj.type] if obj_type.meta.disabled: raise Arcor2Exception("Object type disabled.") check_object_parameters(obj_type, obj.parameters) # TODO check whether object needs parent and if so, if the parent is in scene and parent_id is set if obj_type.meta.needs_parent_type: pass if obj_type.meta.has_pose and obj.pose is None: raise Arcor2Exception("Object requires pose.") if not obj_type.meta.has_pose and obj.pose is not None: raise Arcor2Exception("Object do not have pose.") if obj_type.meta.abstract: raise Arcor2Exception("Cannot instantiate abstract type.") if new_one: if obj.id in scene.object_ids: raise Arcor2Exception( "Object/service with that id already exists.") if obj.name in scene.object_names(): raise Arcor2Exception("Name is already used.") hlp.is_valid_identifier(obj.name)
def parameter_value(cls, type_defs: TypesDict, scene: CScene, project: CProject, action_id: str, parameter_id: str) -> Enum: action = project.action(action_id) param = action.parameter(parameter_id) obj_id, action_type = action.parse_type() obj_type_name = scene.object(obj_id).type try: obj_type = type_defs[obj_type_name] except KeyError: raise ParameterPluginException( f"Unknown object type {obj_type_name}.") try: method = getattr(obj_type, action_type) except AttributeError: raise ParameterPluginException( f"Object type {obj_type_name} does not have method {action_type}." ) try: ttype = get_type_hints(method)[param.name] except KeyError: raise ParameterPluginException( f"Method {obj_type}/{method.__name__} does not have parameter {param.name}." ) if not issubclass(ttype, cls.type()): raise ParameterPluginException( f"Type {ttype.__name__} is not subclass of {cls.type().__name__}." ) try: return ttype(json.loads(param.value)) except ValueError: raise ParameterPluginException( f"Parameter {parameter_id} of action {action.name} has invalid value." )
def check_object(obj_types: ObjectTypeDict, scene: CachedScene, obj: SceneObject, new_one: bool = False) -> None: """Checks if object can be added into the scene.""" assert not obj.children if obj.type not in obj_types: raise Arcor2Exception(f"Unknown ObjectType {obj.type}.") obj_type = obj_types[obj.type] if obj_type.meta.disabled: raise Arcor2Exception(f"ObjectType {obj.type} is disabled. {obj_type.meta.problem}") check_object_parameters(obj_type, obj.parameters) # TODO check whether object needs parent and if so, if the parent is in scene and parent_id is set if obj_type.meta.needs_parent_type: pass if obj_type.meta.has_pose and obj.pose is None: raise Arcor2Exception(f"Object {obj.name} requires pose.") if not obj_type.meta.has_pose and obj.pose is not None: raise Arcor2Exception(f"Object {obj.name} should not have pose.") if obj_type.meta.abstract: raise Arcor2Exception(f"ObjectType {obj.type} is abstract.") if new_one: if obj.id in scene.object_ids: raise Arcor2Exception(f"Object {obj.name} has duplicate id.") if obj.name in scene.object_names(): raise Arcor2Exception(f"Object name {obj.name} is duplicate.") hlp.is_valid_identifier(obj.name)
def _publish(project_id: str, package_name: str) -> RespT: mem_zip = BytesIO() logger.debug( f"Generating package {package_name} for project_id: {project_id}.") types_dict: TypesDict = {} # restore original environment sys.path = list(original_sys_path) sys.modules = dict(original_sys_modules) with tempfile.TemporaryDirectory() as tmp_dir: prepare_object_types_dir(tmp_dir, OBJECT_TYPE_MODULE) with zipfile.ZipFile(mem_zip, mode="w", compression=zipfile.ZIP_DEFLATED) as zf: try: logger.debug("Getting scene and project.") project = ps.get_project(project_id) cached_project = CachedProject(project) scene = ps.get_scene(project.scene_id) cached_scene = CachedScene(scene) if not package_name: package_name = project.name data_path = "data" ot_path = "object_types" zf.writestr(os.path.join(ot_path, "__init__.py"), "") zf.writestr(os.path.join(data_path, "project.json"), project.to_json()) zf.writestr(os.path.join(data_path, "scene.json"), scene.to_json()) obj_types = set(cached_scene.object_types) obj_types_with_models: set[str] = set() if __debug__: # this should uncover potential problems with order in which ObjectTypes are processed import random random.shuffle(scene.objects) for scene_obj in scene.objects: if scene_obj.type in types_dict: continue logger.debug( f"Getting scene object type {scene_obj.type}.") obj_type = ps.get_object_type(scene_obj.type) if obj_type.model and obj_type.id not in obj_types_with_models: obj_types_with_models.add(obj_type.id) model = ps.get_model(obj_type.model.id, obj_type.model.type) obj_model = ObjectModel( obj_type.model.type, **{model.type().value.lower(): model} # type: ignore ) zf.writestr( os.path.join( data_path, "models", humps.depascalize(obj_type.id) + ".json"), obj_model.to_json(), ) zf.writestr( os.path.join(ot_path, humps.depascalize(obj_type.id)) + ".py", obj_type.source) # handle inheritance get_base_from_project_service(types_dict, tmp_dir, obj_types, obj_type, zf, ot_path, parse(obj_type.source)) types_dict[scene_obj.type] = save_and_import_type_def( obj_type.source, scene_obj.type, Generic, tmp_dir, OBJECT_TYPE_MODULE) except Arcor2Exception as e: logger.exception( f"Failed to prepare package content. {str(e)}") raise FlaskException(str(e), error_code=404) script_path = "script.py" try: if project.has_logic: logger.debug("Generating script from project logic.") zf.writestr( script_path, program_src(types_dict, cached_project, cached_scene, True)) else: try: logger.debug("Getting project sources.") script = ps.get_project_sources(project.id).script # check if it is a valid Python code try: parse(script) except SourceException: logger.exception( "Failed to parse code of the uploaded script.") raise FlaskException("Invalid code.", error_code=501) zf.writestr(script_path, script) except ps.ProjectServiceException: logger.info( "Script not found on project service, creating one from scratch." ) # write script without the main loop zf.writestr( script_path, program_src(types_dict, cached_project, cached_scene, False)) logger.debug("Generating supplementary files.") logger.debug("action_points.py") zf.writestr("action_points.py", global_action_points_class(cached_project)) logger.debug("package.json") zf.writestr( "package.json", PackageMeta(package_name, datetime.now(tz=timezone.utc)).to_json()) except Arcor2Exception as e: logger.exception("Failed to generate script.") raise FlaskException(str(e), error_code=501) logger.info( f"Done with {package_name} (scene {scene.name}, project {project.name})." ) mem_zip.seek(0) return send_file(mem_zip, as_attachment=True, max_age=0, download_name=f"{package_name}_package.zip")
def test_branched_output_2() -> None: scene = Scene("s1") obj = SceneObject("test_name", Test.__name__) scene.objects.append(obj) project = Project("p1", "s1") ap1 = ActionPoint("ap1", Position()) project.action_points.append(ap1) ac1 = Action("ac1", f"{obj.id}/test", flows=[Flow(outputs=["bool_res"])]) ap1.actions.append(ac1) ac2 = Action("ac2", f"{obj.id}/test", flows=[Flow()]) ap1.actions.append(ac2) ac3 = Action("ac3", f"{obj.id}/test", flows=[Flow()]) ap1.actions.append(ac3) ac4 = Action("ac4", f"{obj.id}/test", flows=[Flow(outputs=["bool2_res"])]) ap1.actions.append(ac4) ac5 = Action("ac5", f"{obj.id}/test", flows=[Flow()]) ap1.actions.append(ac5) ac6 = Action("ac6", f"{obj.id}/test", flows=[Flow()]) ap1.actions.append(ac6) project.logic.append(LogicItem(LogicItem.START, ac1.id)) project.logic.append( LogicItem(ac1.id, ac2.id, ProjectLogicIf(f"{ac1.id}/default/0", json.dumps(True)))) project.logic.append( LogicItem(ac1.id, ac4.id, ProjectLogicIf(f"{ac1.id}/default/0", json.dumps(False)))) project.logic.append(LogicItem(ac2.id, ac3.id)) project.logic.append(LogicItem(ac3.id, ac6.id)) project.logic.append( LogicItem(ac4.id, ac5.id, ProjectLogicIf(f"{ac4.id}/default/0", json.dumps(True)))) project.logic.append(LogicItem(ac5.id, ac6.id)) project.logic.append(LogicItem(ac6.id, LogicItem.END)) project.logic.append( LogicItem(ac4.id, LogicItem.END, ProjectLogicIf(f"{ac4.id}/default/0", json.dumps(False)))) src = program_src({Test.__name__: Test}, CachedProject(project), CachedScene(scene)) parse(src) """ bool_res = test_name.test(res.ac1) if (bool_res == False): bool2_res = test_name.test(res.ac4) if (bool2_res == False): continue elif (bool2_res == True): test_name.test(res.ac5) elif (bool_res == True): test_name.test(res.ac2) test_name.test(res.ac3) test_name.test(res.ac6) """ spl = src.splitlines() # it has to be robust against changed order of blocks ac1_idx = subs_index(spl, "bool_res = test_name.test(an='ac1')") if_bool_res_false_idx = subs_index(spl, "if (bool_res == False):") assert if_bool_res_false_idx > ac1_idx assert cntsp(spl[ac1_idx]) == cntsp(spl[if_bool_res_false_idx]) bool2_res_idx = subs_index(spl, "bool2_res = test_name.test(an='ac4')") assert bool2_res_idx > if_bool_res_false_idx assert cntsp(spl[if_bool_res_false_idx]) == cntsp(spl[bool2_res_idx]) - TAB if_bool_2_res_false_idx = subs_index(spl, "if (bool2_res == False):") assert cntsp(spl[if_bool_2_res_false_idx]) == cntsp(spl[bool2_res_idx]) assert if_bool_2_res_false_idx > bool2_res_idx assert "continue" in spl[if_bool_2_res_false_idx + 1] assert cntsp( spl[bool2_res_idx]) == cntsp(spl[if_bool_2_res_false_idx + 1]) - TAB if_bool_2_res_true_idx = subs_index(spl, "if (bool2_res == True):") assert if_bool_2_res_true_idx > bool2_res_idx assert "test_name.test(an='ac5')" in spl[if_bool_2_res_true_idx + 1] assert cntsp(spl[if_bool_2_res_true_idx]) == cntsp( spl[if_bool_2_res_true_idx + 1]) - TAB if_bool_res_true_idx = subs_index(spl, "if (bool_res == True):") assert if_bool_res_true_idx > ac1_idx assert cntsp(spl[ac1_idx]) == cntsp(spl[if_bool_res_true_idx]) assert "test_name.test(an='ac2')" in spl[if_bool_res_true_idx + 1] assert cntsp(spl[if_bool_res_true_idx]) == cntsp( spl[if_bool_res_true_idx + 1]) - TAB assert "test_name.test(an='ac3')" in spl[if_bool_res_true_idx + 2] assert cntsp(spl[if_bool_res_true_idx]) == cntsp( spl[if_bool_res_true_idx + 2]) - TAB ac6_idx = subs_index(spl, "test_name.test(an='ac6')") assert cntsp(spl[ac1_idx]) == cntsp(spl[ac6_idx]) assert ac6_idx > if_bool_2_res_false_idx assert ac6_idx > if_bool_2_res_true_idx
def check_action_params(scene: CachedScene, project: CachedProject, action: common.Action, object_action: ObjectAction) -> None: _, action_type = action.parse_type() assert action_type == object_action.name if len(object_action.parameters) != len(action.parameters): raise Arcor2Exception("Unexpected number of parameters.") for req_param in object_action.parameters: param = action.parameter(req_param.name) if param.type == common.ActionParameter.TypeEnum.PROJECT_PARAMETER: pparam = project.parameter(param.str_from_value()) param_meta = object_action.parameter(param.name) if param_meta.type != pparam.type: raise Arcor2Exception( "Action parameter type does not match project parameter type." ) elif param.type == common.ActionParameter.TypeEnum.LINK: parsed_link = param.parse_link() if parsed_link.action_id == action.id: raise Arcor2Exception("Can't use own result as a parameter.") parent_action = project.action(parsed_link.action_id) source_action_pt = parent_action.parse_type() parent_action_meta = glob.OBJECT_TYPES[scene.object( source_action_pt.obj_id).type].actions[ source_action_pt.action_type] if len(parent_action.flow(parsed_link.flow_name).outputs) != len( parent_action_meta.returns): raise Arcor2Exception( "Source action does not have outputs specified.") param_meta = object_action.parameter(param.name) try: if param_meta.type != parent_action_meta.returns[ parsed_link.output_index]: raise Arcor2Exception( "Param type does not match action output type.") except IndexError: raise Arcor2Exception( f"Index {parsed_link.output_index} is invalid for action {object_action.name}," f" which returns {len(object_action.returns)} values.") else: if param.type not in known_parameter_types(): raise Arcor2Exception( f"Parameter {param.name} of action {action.name} has unknown type: {param.type}." ) try: plugin_from_type_name(param.type).parameter_value( get_types_dict(), scene, project, action.id, param.name) except ParameterPluginException as e: raise Arcor2Exception( f"Parameter {param.name} of action {action.name} has invalid value. {str(e)}" )
async def scenes() -> AsyncIterator[CachedScene]: for scene_id in await storage.get_scene_ids(): yield CachedScene(await storage.get_scene(scene_id))
def check_scene_for_object_type(scene: CachedScene, object_type: str) -> None: for _ in scene.objects_of_type(object_type): raise Arcor2Exception(f"Object type used in scene {scene.name}.")
def get_obj_type_data(scene: CachedScene, object_id: str) -> ObjectTypeData: try: return glob.OBJECT_TYPES[scene.object(object_id).type] except KeyError: raise Arcor2Exception("Unknown object type.")
def get_obj_type_data(scene: CachedScene, object_id: str) -> ObjectTypeData: return glob.OBJECT_TYPES[scene.object(object_id).type]