diff --git a/README.md b/README.md index 3b23900..e41c564 100644 --- a/README.md +++ b/README.md @@ -45,4 +45,14 @@ name_of_part └── model.config ``` -This packages will placed by default in your FreeCAD Document's folder and could be moved to gazebo model's folder for using them in sumulator. \ No newline at end of file +This packages will placed by default in your FreeCAD Document's folder and could be moved to gazebo model's folder for using them in sumulator. + +# Генератор последовательности сборки (ASP) + +Данный программный модуль(cad_parts_adjacency_matrix.py) предназначен для решения задачи планирования сборки. Он формирует граф сборки состоящий из И/ИЛИ и оценивает полученные там решения по набору из 4-ёх формальных правил для проверки сгенерированного графа, давая обратную связь для инженеров-конструкторов. + +Формальный правила называются предикатами. Модуль ASP предусматривает следующие разновидности предикатов: +- __Предикат геометрической осуществимости__. Верен для тех последовательностей сборки, которые удовлетворяют критерию геометрической осуществимости - когда все части графа сборки могут соприкосаться в определенной последовательности без каких либо столконовений. +- __Предикат механической осуществимость__. Верна для последовательности сборки, когда инструменты сборки могут осуществлять указанную операцию без каких либо коллизий с изделием. +- __Предикат стабильной осуществимости__. Верен для последовательности сборки, когда сборка на каждом из этапов приходит к стабильному состоянию. +- __Предикат степеней свободы__. Формируется на основе уже сгенерированных графов/графа сборки. В каких степенях свободы возможно перемещать деталь. diff --git a/cad_parts_adjacency_matrix.py b/cad_parts_adjacency_matrix.py new file mode 100644 index 0000000..b654b26 --- /dev/null +++ b/cad_parts_adjacency_matrix.py @@ -0,0 +1,142 @@ +import FreeCAD as App +import uuid + +collision_squares_labels = [] + +meshCoordinates = [] +detailSquares = {} + +class ClearWorkSpaceDocument(object): + def call(self): + for key in detailSquares: + for renderPrimitive in detailSquares[key]: + primitivePart = App.ActiveDocument.getObjectsByLabel(renderPrimitive.cadLabel)[0] + App.ActiveDocument.removeObject(primitivePart.Name) + + +class MeshGeometryCoordinateModel(object): + + def __init__(self, x, y, z, label,): + self.x = x + self.y = y + self.z = z + self.label = label + self.cadLabel = '' + + def initializePrimitivesByCoordinate(self): + uuidDoc = str(uuid.uuid1()) + App.ActiveDocument.addObject("Part::Box", "Box") + App.ActiveDocument.ActiveObject.Label = uuidDoc + App.ActiveDocument.recompute() + part = App.ActiveDocument.getObjectsByLabel(uuidDoc)[0] + collision_squares_labels.append(uuidDoc) + part.Width = 2 + part.Height = 2 + part.Length = 2 + part.Placement = App.Placement( + App.Vector(self.x - 1, self.y - 1, self.z - 1), + App.Rotation(App.Vector(0.00, 0.00, 1.00), 0.00)) + if(detailSquares.get(self.label) is None): + detailSquares[self.label] = [] + detailSquares[self.label].append(self) + self.cadLabel = uuidDoc + App.ActiveDocument.recompute() + + + +class FreeCadMetaModel(object): + + def __init__(self, label, vertex) -> None: + self.label = label + self.vertex = vertex + +class InitPartsParseUseCase(): + + def call(self) -> list[FreeCadMetaModel]: + product_details = [] + for part in App.ActiveDocument.Objects: + if part is not None: + model = FreeCadMetaModel(part.Label,part.Shape.Vertexes) + if(model is not None): + product_details.append(model) + return product_details + + + +class GetCollisionAtPrimitives(object): + + def call(self,freeCadMetaModels: list[FreeCadMetaModel] ): + for model in freeCadMetaModels: + activePart = App.ActiveDocument.getObjectsByLabel(model.label)[0] + for key in detailSquares: + if(model.label != key): + for renderPrimitive in detailSquares[key]: + primitivePart = App.ActiveDocument.getObjectsByLabel(renderPrimitive.cadLabel)[0] + collisionResult:int = int(activePart.Shape.distToShape(primitivePart.Shape)[0]) + # print(collisionResult) + if(collisionResult == 0): + print(model.label+ ' ' + renderPrimitive.label) + + + + +class GetPartPrimitiveCoordinatesUseCase(object): + + def call(self,freeCadMetaModels: list[FreeCadMetaModel]) -> list[MeshGeometryCoordinateModel]: + meshCoordinates:list[MeshGeometryCoordinateModel] = [] + for model in freeCadMetaModels: + vertexesDetail = model.vertex + labelDetail = model.label + for coords in vertexesDetail: + detailVertex = MeshGeometryCoordinateModel( + coords.X, + coords.Y, + coords.Z, + labelDetail, + ) + meshCoordinates.append(detailVertex) + + return meshCoordinates + +class RenderPrimitiveUseCase(object): + + def call(self,meshModels :list[MeshGeometryCoordinateModel]) -> None: + for mesh in meshModels: + mesh.initializePrimitivesByCoordinate() + +class RenderPrimitivesScenario(object): + + def __init__( + self, + initPartsParseUseCase: InitPartsParseUseCase, + getPartPrimitiveCoordinatesUseCase:GetPartPrimitiveCoordinatesUseCase, + renderPrimitiveUseCase:RenderPrimitiveUseCase, + getCollisionAtPrimitives:GetCollisionAtPrimitives, + clearWorkSpaceDocument:ClearWorkSpaceDocument, + ) -> None: + self.initPartsParseUseCase = initPartsParseUseCase + self.getPartPrimitiveCoordinatesUseCase = getPartPrimitiveCoordinatesUseCase + self.renderPrimitiveUseCase = renderPrimitiveUseCase + self.getCollisionAtPrimitives = getCollisionAtPrimitives + self.clearWorkSpaceDocument = clearWorkSpaceDocument + + def call(self) -> None: + parts = self.initPartsParseUseCase.call() + meshCoordinates = self.getPartPrimitiveCoordinatesUseCase.call(parts) + self.renderPrimitiveUseCase.call(meshCoordinates) + self.getCollisionAtPrimitives.call(parts) + self.clearWorkSpaceDocument.call() + + + +def main() -> None: + RenderPrimitivesScenario( + InitPartsParseUseCase(), + GetPartPrimitiveCoordinatesUseCase(), + RenderPrimitiveUseCase(), + GetCollisionAtPrimitives(), + ClearWorkSpaceDocument(), + ).call() + + +main()