diff --git a/pddl/helper/fs.py b/pddl/helper/fs.py new file mode 100644 index 0000000..2030e73 --- /dev/null +++ b/pddl/helper/fs.py @@ -0,0 +1,15 @@ +import os +import json + +class FS: + def readJSON(path:str): + return json.loads((open(path)).read()) + + def writeFile(data, filePath, fileName): + + file_to_open = filePath + fileName + + f = open(file_to_open, 'w', ) + + f.write(data) + diff --git a/pddl/main.py b/pddl/main.py new file mode 100644 index 0000000..68ff3a9 --- /dev/null +++ b/pddl/main.py @@ -0,0 +1,27 @@ +import argparse +from helper.fs import FS +from src.model.asm4_structure import Asm4Structure + +from src.usecases.asm4_to_assembly_use_case import Asm4ToAssemblyUseCase +from src.usecases.assembly_to_pddl_use_case import AssemblyToPddlUseCase + + + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--asm4Path', help='asm4 json FreeCad') + parser.add_argument('--outPath', help='save pddl path') + args = parser.parse_args() + + if args.asm4Path == None or args.outPath == None: + parser.print_help() + + data = FS.readJSON(args.asm4Path) + asm4 = Asm4Structure.parse(data) + asm4usecase = Asm4ToAssemblyUseCase().call(asm4) + assemblyToPddlUseCase = AssemblyToPddlUseCase.call(assembly=asm4usecase['asm'],rootLabel=asm4usecase['rootLabel']) + FS.writeFile(assemblyToPddlUseCase['problem'] ,args.outPath, 'problem.pddl') + FS.writeFile(assemblyToPddlUseCase['domain'] ,args.outPath, 'domain.pddl') + + \ No newline at end of file diff --git a/pddl/mocks/asm4-mock.json b/pddl/mocks/asm4-mock.json new file mode 100644 index 0000000..3178ba2 --- /dev/null +++ b/pddl/mocks/asm4-mock.json @@ -0,0 +1,86 @@ +[ + { + "level": 1, + "attachedTo": "Parent Assembly", + "label": "Model", + "axis": [ + "LCS_Origin" + ] + }, + { + "level": 1, + "attachedTo": [ + "Parent Assembly", + "LCS_Origin" + ], + "label": "CubePart001", + "axis": [ + "LCS_0" + ] + }, + { + "level": 1, + "attachedTo": [ + "CubePart001", + "LCS_0" + ], + "label": "CubePart002", + "axis": [ + "LCS_0" + ] + }, + { + "level": 1, + "attachedTo": [ + "CubePart001", + "LCS_0" + ], + "label": "CubePart003", + "axis": [ + "LCS_0" + ] + }, + { + "level": 1, + "attachedTo": [ + "" + ], + "label": "CubePart004", + "axis": [ + "LCS_0" + ] + }, + { + "level": 1, + "attachedTo": [ + "CubePart001", + "LCS_0" + ], + "label": "CubePart005", + "axis": [ + "LCS_0" + ] + }, + { + "level": 1, + "attachedTo": [ + "CubePart005", + "LCS_0" + ], + "label": "CubePart006", + "axis": [ + "LCS_0" + ] + }, + { + "level": 1, + "attachedTo": [ + "CubePart006", + "LCS_0" + ], + "label": "CubePart007", + "axis": [ + "LCS_0" + ] + } +] \ No newline at end of file diff --git a/pddl/requirements.txt b/pddl/requirements.txt new file mode 100644 index 0000000..586ed6d --- /dev/null +++ b/pddl/requirements.txt @@ -0,0 +1,2 @@ +argparse +unified_planning \ No newline at end of file diff --git a/pddl/src/model/asm4_structure.py b/pddl/src/model/asm4_structure.py new file mode 100644 index 0000000..308cd77 --- /dev/null +++ b/pddl/src/model/asm4_structure.py @@ -0,0 +1,90 @@ +from typing import Optional, List, Union, Any, TypeVar, Callable, Type, cast + + +T = TypeVar("T") + + +def from_int(x: Any) -> int: + assert isinstance(x, int) and not isinstance(x, bool) + return x + + +def from_none(x: Any) -> Any: + assert x is None + return x + + +def from_union(fs, x): + for f in fs: + try: + return f(x) + except: + pass + assert False + + +def from_list(f: Callable[[Any], T], x: Any) -> List[T]: + assert isinstance(x, list) + return [f(y) for y in x] + + +def from_str(x: Any) -> str: + assert isinstance(x, str) + return x + + +def to_class(c: Type[T], x: Any) -> dict: + assert isinstance(x, c) + return cast(Any, x).to_dict() + + +class Asm4Structure: + level: Optional[int] + attached_to: Optional[Union[List[str], str]] + label: Optional[str] + axis: Optional[List[str]] + + def __init__(self, level: Optional[int], attached_to: Optional[Union[List[str], str]], label: Optional[str], axis: Optional[List[str]]) -> None: + self.level = level + self.attached_to = attached_to + self.label = label + self.axis = axis + + @staticmethod + def from_dict(obj: Any) -> 'Asm4Structure': + assert isinstance(obj, dict) + level = from_union([from_int, from_none], obj.get("level")) + attached_to = from_union([lambda x: from_list(from_str, x), from_str, from_none], obj.get("attachedTo")) + label = from_union([from_str, from_none], obj.get("label")) + axis = from_union([lambda x: from_list(from_str, x), from_none], obj.get("axis")) + return Asm4Structure(level, attached_to, label, axis) + + def to_dict(self) -> dict: + result: dict = {} + if self.level is not None: + result["level"] = from_union([from_int, from_none], self.level) + if self.attached_to is not None: + result["attachedTo"] = from_union([lambda x: from_list(from_str, x), from_str, from_none], self.attached_to) + if self.label is not None: + result["label"] = from_union([from_str, from_none], self.label) + if self.axis is not None: + result["axis"] = from_union([lambda x: from_list(from_str, x), from_none], self.axis) + return result + def parse(data) -> List['Asm4Structure']: + structure:list = [] + + for i in data: + structure.append(Asm4Structure( + label=i['label'], + level=i['level'], + attached_to=i['attachedTo'], + axis=i['axis'] + )) + return structure + +def asm4_structures_from_dict(s: Any) -> List[Asm4Structure]: + return from_list(Asm4Structure.from_dict, s) + + +def asm4_structures_to_dict(x: List[Asm4Structure]) -> Any: + return from_list(lambda x: to_class(Asm4Structure, x), x) diff --git a/pddl/src/usecases/asm4_to_assembly_use_case.py b/pddl/src/usecases/asm4_to_assembly_use_case.py new file mode 100644 index 0000000..a1b0ecd --- /dev/null +++ b/pddl/src/usecases/asm4_to_assembly_use_case.py @@ -0,0 +1,19 @@ +from src.model.asm4_structure import Asm4Structure +from typing import List + + + +class Asm4ToAssemblyUseCase: + _asm4Assembly = [] + def call(self,asm4:List[Asm4Structure] ): + rootPart = asm4[0] + self._asm4Assembly.append(rootPart.label) + for el in asm4[1:asm4.__len__()]: + self.parse(lead=el) + return {'asm':self._asm4Assembly[1:self._asm4Assembly.__len__()],'rootLabel':rootPart.label} + def parse(self,lead:Asm4Structure ): + if lead.attached_to[0] == 'Parent Assembly': + self._asm4Assembly.append(lead.label) + for i in self._asm4Assembly: + if i == lead.attached_to[0]: + self._asm4Assembly.append(lead.label) \ No newline at end of file diff --git a/pddl/src/usecases/assembly_to_pddl_use_case.py b/pddl/src/usecases/assembly_to_pddl_use_case.py new file mode 100644 index 0000000..f1bfe9b --- /dev/null +++ b/pddl/src/usecases/assembly_to_pddl_use_case.py @@ -0,0 +1,37 @@ +from typing import List +from unified_planning.shortcuts import * +from unified_planning import * + + +class AssemblyToPddlUseCase: + def call(assembly: List[str], rootLabel: str): + partType = UserType("part") + assemblyType = UserType('assembly') + + objectsPartPddl = [] + objectsAsmToPddl = [] + + i = 0 + for el in assembly: + objectsPartPddl.append(Object(el, partType)) + + problem = Problem(rootLabel) + + for el in objectsPartPddl: + problem.add_object(el) + i = 0 + for el in objectsPartPddl: + problem.add_object(Object('subasm' + str(i), assemblyType)) + objectsAsmToPddl.append(Object('subasm' + str(i), assemblyType)) + i = i+1 + + connected = Fluent('part-of', BoolType(), + l_from=partType, l_to=assemblyType) + i = 0 + for el in objectsPartPddl: + problem.set_initial_value(connected(el, objectsAsmToPddl[i]), True) + i = i+1 + goal = Fluent(rootLabel) + problem.add_goal(connected(objectsPartPddl[objectsPartPddl.__len__( + ) - 1], objectsAsmToPddl[objectsAsmToPddl.__len__() - 1]),) + return {"problem": unified_planning.io.PDDLWriter(problem).get_problem(), 'domain': unified_planning.io.PDDLWriter(problem).get_domain()} diff --git a/pddl/unit.test.py b/pddl/unit.test.py new file mode 100644 index 0000000..c883c48 --- /dev/null +++ b/pddl/unit.test.py @@ -0,0 +1,28 @@ +import unittest +from src.usecases.asm4_to_assembly_use_case import Asm4ToAssemblyUseCase +from src.usecases.assembly_to_pddl_use_case import AssemblyToPddlUseCase +from src.model.asm4_structure import Asm4Structure + +from helper.fs import FS +import os + + +mock = FS.readJSON(os.path.dirname(os.path.realpath(__file__)) + '/mocks/asm4-mock.json') + +asm4 = Asm4Structure.parse(mock) + + +asm4usecase = Asm4ToAssemblyUseCase().call(asm4) +assemblyToPddl = AssemblyToPddlUseCase.call(assembly=asm4usecase['asm'],rootLabel=asm4usecase['rootLabel']) + + +class TestStringMethods(unittest.TestCase): + def test_problem(self): + self.assertIsInstance(assemblyToPddl["problem"], str) + def test_domain(self): + self.assertIsInstance(assemblyToPddl["domain"], str) + + + +if __name__ == '__main__': + unittest.main()