Compare commits
21 commits
master
...
solve_opti
Author | SHA1 | Date | |
---|---|---|---|
![]() |
862a61b1e7 | ||
![]() |
deee7b4329 | ||
![]() |
ca0df838a2 | ||
![]() |
a59b3a4775 | ||
ee1afe7869 | |||
0ec56f1eb8 | |||
ff087ffbf8 | |||
![]() |
70004486c7 | ||
![]() |
c2e375f854 | ||
28a8648651 | |||
4477a15909 | |||
2b845403cd | |||
![]() |
0de605f314 | ||
![]() |
7ffa0acee9 | ||
![]() |
ecb43845a6 | ||
![]() |
052dd86d94 | ||
![]() |
8fba62e8a3 | ||
![]() |
a4da4aee68 | ||
![]() |
db4e0d7ac9 | ||
![]() |
b766e36c31 | ||
![]() |
3e7bfba178 |
26 changed files with 653 additions and 173 deletions
|
@ -1,49 +0,0 @@
|
|||
import FreeCAD
|
||||
import FreeCADGui
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
class DatumTool:
|
||||
"""
|
||||
A tool for creating datums in existing models
|
||||
"""
|
||||
def __init__(self):
|
||||
self.active = False
|
||||
|
||||
def activate(self):
|
||||
self.active = True
|
||||
FreeCAD.Console.PrintMessage("Datum tool activatedn")
|
||||
|
||||
def deactivate(self):
|
||||
self.active = False
|
||||
FreeCAD.Console.PrintMessage("Datum tool deactivatedn")
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if self.active:
|
||||
# Create a datum at the position of the mouse click
|
||||
pos = FreeCADGui.ActiveDocument.ActiveView.getCursorPos()
|
||||
point = FreeCADGui.ActiveDocument.ActiveView.getPoint(pos)
|
||||
datum = FreeCAD.ActiveDocument.addObject("Part::Datum", "Datum")
|
||||
datum.Placement.Base = point
|
||||
datum.ViewObject.ShapeColor = (0.0, 1.0, 0.0) # Set the color of the datum to green
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
class DatumCommand:
|
||||
"""
|
||||
A command for activating and deactivating the datum tool
|
||||
"""
|
||||
def __init__(self):
|
||||
self.tool = DatumTool()
|
||||
|
||||
def Activated(self):
|
||||
self.tool.activate()
|
||||
FreeCADGui.ActiveDocument.ActiveView.addEventCallback("SoMouseButtonEvent", self.tool.mousePressEvent)
|
||||
|
||||
def Deactivated(self):
|
||||
self.tool.deactivate()
|
||||
FreeCADGui.ActiveDocument.ActiveView.removeEventCallback("SoMouseButtonEvent", self.tool.mousePressEvent)
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'path/to/icon.png', 'MenuText': 'Datum Tool', 'ToolTip': 'Creates datum elements in existing models'}
|
||||
|
||||
# Add the command to the Draft Workbench
|
||||
FreeCADGui.addCommand('DatumCommand', DatumCommand())
|
11
freecad_workbench/freecad/robossembler/README.md
Normal file
11
freecad_workbench/freecad/robossembler/README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
Общее руководство работы с планировщиком сборки.
|
||||
|
||||
1. Открыть сборку. С помощью команд верстака Robossembler, используя команды "Create Assembly Parameters", "Create fastener set", "Create assembly sequence", произвести разметку элементов сборки. Произвести экспорт настроек с помощью функции
|
||||
export assembly settings. На выходе получим файл assembly_settings.json
|
||||
2. Запустить geometric_feasibility_predicate/main.py, указав в env.json значения cadFilePath (путь к сборке) и outPath (путь вывода). Рекомендуется хранить все файлы в одном месте с проектом На выходе получается файл adjacency_matrix.json
|
||||
|
||||
3. ( опционально) Запустить файл solve_optimizer , получить упрощенную матрицу смежности в виде reduced_adjacency_matrix.json
|
||||
|
||||
4. Произвести расчет последовательности доступным способом, напр. через get_sequences.json или с помощью asp.
|
||||
|
||||
5. (Если применено упрощение) Добавить исключенные компоненты с помощью соотв. функции.
|
|
@ -1,37 +0,0 @@
|
|||
# -*- coding: utf8 -*-
|
||||
|
||||
#***************************************************************************
|
||||
#* *
|
||||
#* Copyright (c) 2020 kbwbe *
|
||||
#* *
|
||||
#* *
|
||||
#* This program is free software; you can redistribute it and/or modify *
|
||||
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
#* as published by the Free Software Foundation; either version 2 of *
|
||||
#* the License, or (at your option) any later version. *
|
||||
#* for detail see the LICENCE text file. *
|
||||
#* *
|
||||
#* This program is distributed in the hope that it will be useful, *
|
||||
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
#* GNU Library General Public License for more details. *
|
||||
#* *
|
||||
#* You should have received a copy of the GNU Library General Public *
|
||||
#* License along with this program; if not, write to the Free Software *
|
||||
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
#* USA *
|
||||
#* *
|
||||
#***************************************************************************
|
||||
|
||||
import FreeCAD
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
from DraftGui import translate
|
||||
else:
|
||||
def QT_TRANSLATE_NOOP(context, text):
|
||||
return text
|
||||
|
||||
def translate(context, text):
|
||||
return text
|
|
@ -37,4 +37,4 @@ def draw_graph_with_thumbnails(G, image_folder):
|
|||
def main():
|
||||
sequences = load_sequences('valid_sequences.json')
|
||||
G = create_graph(sequences)
|
||||
draw_graph_with_thumbnails(G, '/home/markvoltov/GitProjects/framework/test_models/img')
|
||||
draw_graph_with_thumbnails(G, 'path_to_img')
|
||||
|
|
28
freecad_workbench/freecad/robossembler/asm_main.py
Normal file
28
freecad_workbench/freecad/robossembler/asm_main.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
import FreeCAD as App
|
||||
from .geometric_feasibility_predicate.main import main as asm_analysis
|
||||
from get_sequences import process_adjacency_data
|
||||
from valid_sequences import filter_valid_sequences
|
||||
from solve_optimizer import restore_full_sequence # Убедитесь, что эта функция импортирована
|
||||
from constraints_operator import collect_assembly_settings
|
||||
|
||||
def main(flag):
|
||||
# Выполняем анализ сборки и получаем необходимые данные
|
||||
#flag используется для того, чтобы выбирать между обычным и оптимизированным вариантом работы
|
||||
intersection_geometry, sequences, topologyMatrix = asm_analysis()
|
||||
adjacency_matrix = topologyMatrix.matrix
|
||||
assembly_settings = collect_assembly_settings()
|
||||
|
||||
# Упрощаем матрицу смежности
|
||||
if flag:
|
||||
simplified_matrix = simplify_adjacency_matrix(assembly_settings, adjacency_matrix)
|
||||
all_parts, graph, first_detail, leaf_nodes, all_sequences = process_adjacency_data(simplified_matrix)
|
||||
else:
|
||||
all_parts, graph, first_detail, leaf_nodes, all_sequences = process_adjacency_data(adjacency_matrix)
|
||||
|
||||
# Фильтруем допустимые последовательности
|
||||
valid_sequences = filter_valid_sequences(adjacency_matrix, sequences, assembly_settings)
|
||||
full_sequence = restore_full_sequence(assembly_settings, all_sequences)
|
||||
|
||||
return full_sequence
|
||||
|
||||
main()
|
|
@ -35,8 +35,9 @@ def draw_graph(G):
|
|||
plt.show()
|
||||
|
||||
def main():
|
||||
sequence = load_assembly_sequence('assembly_sequence.json')
|
||||
sequence = load_assembly_sequence('path_to_adjacency_matrix.json')
|
||||
assembly_graph = create_assembly_graph(sequence)
|
||||
|
||||
draw_graph(assembly_graph)
|
||||
|
||||
# main()
|
||||
|
|
|
@ -3,7 +3,7 @@ import FreeCADGui as Gui
|
|||
import Draft
|
||||
import json
|
||||
|
||||
sequence_file = '/home/markvoltov/GitProjects/framework/freecad_workbench/freecad/robossembler/sequences.json'
|
||||
sequence_file = 'path_to_sequencesjson'
|
||||
|
||||
def load_assembly_sequence(filepath):
|
||||
with open(filepath, 'r') as file:
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import networkx as nx
|
||||
|
||||
|
||||
class GraphProcessor:
|
||||
graph = None
|
||||
|
||||
def __init__(self, adjacency_matrix):
|
||||
self.adjacency_matrix = adjacency_matrix
|
||||
self.graph = self.load_graph_from_data()
|
||||
|
||||
def load_graph_from_data(self):
|
||||
G = nx.Graph()
|
||||
|
||||
for part1, neighbors in self.adjacency_matrix.items():
|
||||
for neighbor in neighbors:
|
||||
G.add_edge(part1, neighbor)
|
||||
|
||||
return G
|
||||
|
||||
|
||||
class EdgeBetweensClustering:
|
||||
def __init__(self, graph):
|
||||
self.graph = graph.copy()
|
||||
self.clusters = []
|
||||
|
||||
def cluster(self):
|
||||
while self.graph.number_of_edges() > 0:
|
||||
edge_betweens = nx.edge_betweenness_centrality(self.graph)
|
||||
max_betweens_edge = max(edge_betweens, key=edge_betweens.get)
|
||||
self.graph.remove_edge(*max_betweens_edge)
|
||||
components = list(nx.connected_components(self.graph))
|
||||
if components not in self.clusters:
|
||||
self.clusters.append(components)
|
||||
return []
|
||||
|
||||
def get_clusters(self):
|
||||
return self.clusters
|
||||
|
||||
|
||||
class ClusterisationSequenceUseCase:
|
||||
def call(self, adjacency_matrix):
|
||||
graph_processor = GraphProcessor(adjacency_matrix)
|
||||
G = graph_processor.load_graph_from_data()
|
||||
ebc = EdgeBetweensClustering(G)
|
||||
ebc.cluster()
|
||||
clusters = ebc.get_clusters()
|
||||
|
||||
for i in range(len(clusters)):
|
||||
for j in range(len(clusters[i])):
|
||||
clusters[i][j] = list(clusters[i][j])
|
||||
|
||||
# Создание списка последовательностей сборки
|
||||
assembly_sequences = []
|
||||
for cluster in clusters:
|
||||
sequence = []
|
||||
for component in cluster:
|
||||
sequence.extend(component)
|
||||
assembly_sequences.append(sequence)
|
||||
|
||||
return assembly_sequences
|
||||
|
||||
|
||||
# # Вызов функции
|
||||
# adjacency_matrix = {
|
||||
# 'body_down': ['sol_gear', 'planet_gear', 'planet_gear003', 'planet_gear004', 'planet_gear005', 'planet_gear002', 'body_up', 'bolt', 'bolt2', 'bolt3', 'bolt4'],
|
||||
# 'sol_gear': ['body_down', 'output_shaft', 'planet_gear', 'planet_gear003', 'planet_gear004', 'planet_gear005', 'planet_gear002'],
|
||||
# 'output_shaft': ['sol_gear', 'planet_gear', 'planet_gear003', 'planet_gear004', 'planet_gear005', 'planet_gear002', 'body_up'],
|
||||
# 'planet_gear': ['body_down', 'sol_gear', 'output_shaft'],
|
||||
# 'planet_gear003': ['body_down', 'sol_gear', 'output_shaft'],
|
||||
# 'planet_gear004': ['body_down', 'sol_gear', 'output_shaft'],
|
||||
# 'planet_gear005': ['body_down', 'sol_gear', 'output_shaft'],
|
||||
# 'planet_gear002': ['body_down', 'sol_gear', 'output_shaft'],
|
||||
# 'body_up': ['body_down', 'output_shaft', 'bolt', 'bolt2', 'bolt3', 'bolt4'],
|
||||
# 'bolt': ['body_down', 'body_up'],
|
||||
# 'bolt2': ['body_down', 'body_up'],
|
||||
# 'bolt3': ['body_down', 'body_up'],
|
||||
# 'bolt4': ['body_down', 'body_up']
|
||||
# }
|
||||
# print(adjacency_matrix)
|
||||
# use_case = ClusterisationSequenceUseCase()
|
||||
# assembly_sequences = use_case.call(adjacency_matrix)
|
||||
# print(assembly_sequences)
|
|
@ -135,6 +135,50 @@ def export_assembly_settings():
|
|||
with open(save_path, "w") as f:
|
||||
json.dump(data, f, indent=4)
|
||||
|
||||
#для работы с данными как с переменной
|
||||
def collect_assembly_settings():
|
||||
doc = App.activeDocument()
|
||||
if not doc:
|
||||
return None
|
||||
|
||||
assembly_settings_folder = None
|
||||
for obj in doc.Objects:
|
||||
if obj.Name == "Assembly_Settings":
|
||||
assembly_settings_folder = obj
|
||||
break
|
||||
|
||||
if not assembly_settings_folder:
|
||||
return None
|
||||
|
||||
assembly_settings = []
|
||||
for obj in assembly_settings_folder.Group:
|
||||
if hasattr(obj, "Type"):
|
||||
obj_dict = {"Name": obj.Name}
|
||||
if obj.Type == "fastener_set":
|
||||
fasteners = [part.Label for part in obj.Fasteners]
|
||||
obj_dict.update({
|
||||
"Type": "fastener_set",
|
||||
"Parent": obj.Parent.Label,
|
||||
"Child": obj.Child.Label,
|
||||
"Fasteners": fasteners
|
||||
})
|
||||
elif obj.Type == "asm_sequence":
|
||||
obj_dict.update({
|
||||
"Type": "asm_sequence",
|
||||
"Parent": obj.Parent.Label,
|
||||
"Child": obj.Child.Label
|
||||
})
|
||||
elif obj.Type == "clearance":
|
||||
partnames = [part.Label for part in obj.PartName]
|
||||
obj_dict.update({
|
||||
"Type": "clearance",
|
||||
"PartName": partnames,
|
||||
"MaxClearance": obj.MaxClearance
|
||||
})
|
||||
assembly_settings.append(obj_dict)
|
||||
|
||||
return assembly_settings
|
||||
|
||||
# create_fastener_set()
|
||||
# create_assembly_sequence()
|
||||
# create_clearance_constraint()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"cadFilePath": "path/to/file",
|
||||
"outPath": "out/path",
|
||||
"cadFilePath": "path_to_cad_file",
|
||||
"outPath": "out_path",
|
||||
"objectIndentation": 0
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ class AllSequences:
|
|||
for el in v:
|
||||
adj[inc][el - 1] = 1
|
||||
inc += 1
|
||||
return self.find_all_sequences(adj)
|
||||
self.find_all_sequences(adj)
|
||||
|
||||
|
||||
class VectorModel:
|
||||
|
@ -580,8 +580,8 @@ class CadAdjacencyMatrix:
|
|||
collisionResult: int = int(
|
||||
part.Shape.distToShape(nextPart.Shape)[0]
|
||||
)
|
||||
print(collisionResult)
|
||||
print("collisionResult")
|
||||
# print(collisionResult)
|
||||
# print("collisionResult")
|
||||
if collisionResult == 0:
|
||||
matrix[part.Label].append(nextPart.Label)
|
||||
|
||||
|
@ -708,68 +708,82 @@ class ExitFreeCadUseCase:
|
|||
# FreeCadRepository().obj
|
||||
# pass
|
||||
|
||||
#функция, проверяющая, открывается ли программа через консоль или через верстак freecad.
|
||||
def get_paths():
|
||||
if 'FreeCAD' in globals():
|
||||
active_doc = FreeCAD.activeDocument()
|
||||
if active_doc:
|
||||
cadFilePath = active_doc.FileName
|
||||
outPath = os.path.dirname(cadFilePath)
|
||||
else:
|
||||
raise Exception("Нет активного документа в FreeCAD.")
|
||||
else:
|
||||
with open('env.json', 'r', encoding='utf-8') as file:
|
||||
env_data = json.load(file)
|
||||
cadFilePath = env_data.get('cadFilePath')
|
||||
outPath = env_data.get('outPath')
|
||||
if not cadFilePath or not outPath:
|
||||
raise Exception("Не найдены cadFilePath или outPath в env.json.")
|
||||
|
||||
return cadFilePath, outPath
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
env = FS.readJSON("env.json")
|
||||
cadFilePath = str(env["cadFilePath"])
|
||||
outPath = str(env["outPath"])
|
||||
objectIndentation = float(env["objectIndentation"])
|
||||
if 'FreeCAD' in globals():
|
||||
active_doc = FreeCAD.activeDocument()
|
||||
if active_doc:
|
||||
cadFilePath = active_doc.FileName
|
||||
outPath = os.path.dirname(cadFilePath)
|
||||
else:
|
||||
# raise Exception("Нет активного документа в FreeCAD.")
|
||||
# else:
|
||||
env = FS.readJSON("env.json")
|
||||
print(env)
|
||||
cadFilePath = str(env["cadFilePath"])
|
||||
outPath = str(env["outPath"])
|
||||
objectIndentation = float(env["objectIndentation"])
|
||||
|
||||
if cadFilePath == None:
|
||||
return TypeError("CadFile not found env.json")
|
||||
App.open("" + cadFilePath)
|
||||
if cadFilePath == None:
|
||||
return TypeError("CadFile not found env.json")
|
||||
App.open("" + cadFilePath)
|
||||
|
||||
# isAllObjectSolidsCheckUseCase = IsAllObjectSolidsCheckUseCase.call()
|
||||
isAllObjectSolidsCheckUseCase = IsAllObjectSolidsCheckUseCase.call()
|
||||
|
||||
# if isAllObjectSolidsCheckUseCase != None:
|
||||
# FS.writeFile(isAllObjectSolidsCheckUseCase.toString(), outPath, 'error.json')
|
||||
# ExitFreeCadUseCase.call()
|
||||
# return
|
||||
if isAllObjectSolidsCheckUseCase != None:
|
||||
FS.writeFile(isAllObjectSolidsCheckUseCase.toString(), outPath, 'error.json')
|
||||
ExitFreeCadUseCase.call()
|
||||
return
|
||||
|
||||
FreeCAD.open(cadFilePath)
|
||||
|
||||
|
||||
# checkObjectHasTouchesUseCase = CheckObjectHasTouchesUseCase.call(objectIndentation)
|
||||
|
||||
# if checkObjectHasTouchesUseCase != None:
|
||||
# FS.writeFile(checkObjectHasTouchesUseCase.toString(), outPath, 'error.json')
|
||||
# ExitFreeCadUseCase.call()
|
||||
# return
|
||||
|
||||
topologyMatrix = CadAdjacencyMatrix().matrixBySurfaces()
|
||||
import json
|
||||
|
||||
sequences = json.dumps(
|
||||
{"sequences": AllSequences(topologyMatrix.matrix).adj_matrix_names},
|
||||
ensure_ascii=False,
|
||||
indent=4,
|
||||
)
|
||||
sequences = {
|
||||
"sequences": AllSequences(topologyMatrix.matrix).adj_matrix_names
|
||||
}
|
||||
matrix = topologyMatrix.matrix
|
||||
contacts = matrixGetUniqueContact(matrix)
|
||||
intersection_geometry = {"status": True, "recalculations": None}
|
||||
|
||||
for el in contacts:
|
||||
child = App.ActiveDocument.getObjectsByLabel(el.get("child"))[0]
|
||||
parent = App.ActiveDocument.getObjectsByLabel(el.get("parent"))[0]
|
||||
area = IntersectionComputedUseCase.call([child, parent])
|
||||
if area != 0.0:
|
||||
if intersection_geometry.get("recalculations") == None:
|
||||
if intersection_geometry.get("recalculations") is None:
|
||||
intersection_geometry["status"] = False
|
||||
intersection_geometry["recalculations"] = []
|
||||
intersection_geometry["recalculations"].append(
|
||||
{"area": area, "connect": el.get("child") + " " + el.get("parent")}
|
||||
)
|
||||
# print(intersection_geometry, sequences, topologyMatrix.to_dict())
|
||||
return intersection_geometry, sequences, topologyMatrix.to_dict()
|
||||
|
||||
FS.writeFile(
|
||||
json.dumps(intersection_geometry, ensure_ascii=False, indent=4),
|
||||
outPath,
|
||||
"intersection_geometry.json",
|
||||
)
|
||||
FS.writeFile(sequences, outPath, "sequences.json")
|
||||
|
||||
FS.writeFile(
|
||||
json.dumps(topologyMatrix.to_dict(), ensure_ascii=False, indent=4),
|
||||
outPath,
|
||||
"adjacency_matrix.json",
|
||||
)
|
||||
ExitFreeCadUseCase.call()
|
||||
# ExitFreeCadUseCase.call() Сейчас пока не нужна
|
||||
# return intersection_geometry, sequences, topologyMatrix.to_dict()
|
||||
|
||||
|
||||
#main()
|
||||
# main()
|
||||
|
|
|
@ -42,20 +42,36 @@ def save_sequences(sequences, file_path):
|
|||
with open(file_path, 'w') as file:
|
||||
json.dump(sequences, file, indent=4)
|
||||
|
||||
data = load_data('adjacency_matrix.json')
|
||||
constraints = load_constraints('constraints.json')
|
||||
all_parts = data['allParts']
|
||||
graph = create_graph(data)
|
||||
first_detail = data['firstDetail']
|
||||
leaf_nodes = find_leaf_nodes(graph, first_detail)
|
||||
# data = load_data('path_to_adjacency_matrix.json')
|
||||
# # constraints = load_constraints('constraints.json')
|
||||
# all_parts = data['allParts']
|
||||
# print(all_parts)
|
||||
# graph = create_graph(data)
|
||||
# first_detail = data['firstDetail']
|
||||
# leaf_nodes = find_leaf_nodes(graph, first_detail)
|
||||
|
||||
all_sequences = []
|
||||
for leaf in leaf_nodes:
|
||||
paths = find_all_paths(graph, leaf, first_detail)
|
||||
for path in paths:
|
||||
if set(path) == set(all_parts) and is_valid_sequence(path, constraints):
|
||||
all_sequences.append(path)
|
||||
# all_sequences = []
|
||||
# for leaf in leaf_nodes:
|
||||
# paths = find_all_paths(graph, leaf, first_detail)
|
||||
# for path in paths:
|
||||
# if set(path) == set(all_parts) and is_valid_sequence(path, constraints):
|
||||
# all_sequences.append(path)
|
||||
|
||||
save_sequences(all_sequences, 'valid_sequences.json')
|
||||
# save_sequences(all_sequences, 'valid_sequences.json')
|
||||
|
||||
print(f"Найдено {len(all_sequences)} допустимых последовательностей сборки.")
|
||||
# print(f"Найдено {len(all_sequences)} допустимых последовательностей сборки.")
|
||||
|
||||
def process_adjacency_data(topology_matrix):
|
||||
all_parts = topology_matrix['allParts']
|
||||
graph = create_graph(topology_matrix)
|
||||
first_detail = topology_matrix['firstDetail']
|
||||
leaf_nodes = find_leaf_nodes(graph, first_detail)
|
||||
|
||||
all_sequences = []
|
||||
for leaf in leaf_nodes:
|
||||
paths = find_all_paths(graph, leaf, first_detail)
|
||||
for path in paths:
|
||||
if set(path) == set(all_parts) and is_valid_sequence(path, constraints):
|
||||
all_sequences.append(path)
|
||||
|
||||
return all_parts, graph, first_detail, leaf_nodes, all_sequences
|
||||
|
|
|
@ -3,7 +3,7 @@ import networkx as nx
|
|||
import matplotlib.pyplot as plt
|
||||
|
||||
# Загружаем данные из файла
|
||||
with open('adjacency_matrix.json', 'r') as file:
|
||||
with open('path_tp_simplified_adjacency_matrix.json', 'r') as file:
|
||||
data = json.load(file)
|
||||
|
||||
# Создаем пустой граф
|
||||
|
|
|
@ -2,7 +2,6 @@ import os
|
|||
import FreeCADGui as Gui
|
||||
import FreeCAD as App
|
||||
|
||||
from .TranslateUtils import translate
|
||||
from . import ICONPATH, TRANSLATIONSPATH, Frames
|
||||
from .version import __version__
|
||||
|
||||
|
@ -15,6 +14,14 @@ __url__ = ["https://robossembler.org"]
|
|||
__status__ = 'development'
|
||||
|
||||
|
||||
def QT_TRANSLATE_NOOP(ctx, txt):
|
||||
return txt
|
||||
|
||||
|
||||
def translate(ctx, txt):
|
||||
return txt
|
||||
|
||||
|
||||
class Robossembler(Gui.Workbench):
|
||||
"""
|
||||
class which gets initiated at startup of the gui
|
||||
|
|
|
@ -105,7 +105,7 @@ def main():
|
|||
# args.print_helper()
|
||||
# if (aspDir[aspDir.__len__() - 1] != '/'):
|
||||
# aspDir += '/'
|
||||
aspDir = "/home/markvoltov/GitProjects/framework/test_models/"
|
||||
aspDir = "path_to_models/"
|
||||
sequences = FS.readJSON(aspDir + 'sequences.json').get('sequences')
|
||||
|
||||
assemblyDirNormalize = []
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
{
|
||||
"allParts": [
|
||||
"body_down",
|
||||
"body_up",
|
||||
"sol_gear",
|
||||
"output_shaft",
|
||||
"planet_gear",
|
||||
"planet_gear003",
|
||||
"planet_gear004",
|
||||
"planet_gear005",
|
||||
"planet_gear002",
|
||||
"bolt",
|
||||
"bolt2",
|
||||
"bolt3",
|
||||
"bolt4"
|
||||
],
|
||||
"matrix": {
|
||||
"body_down": [
|
||||
"sol_gear",
|
||||
"planet_gear",
|
||||
"planet_gear003",
|
||||
"planet_gear004",
|
||||
"planet_gear005",
|
||||
"planet_gear002",
|
||||
"body_up"
|
||||
],
|
||||
"body_up": [
|
||||
"body_down",
|
||||
"output_shaft"
|
||||
],
|
||||
"sol_gear": [
|
||||
"body_down",
|
||||
"output_shaft",
|
||||
"planet_gear",
|
||||
"planet_gear003",
|
||||
"planet_gear004",
|
||||
"planet_gear005",
|
||||
"planet_gear002"
|
||||
],
|
||||
"output_shaft": [
|
||||
"sol_gear",
|
||||
"planet_gear",
|
||||
"planet_gear003",
|
||||
"planet_gear004",
|
||||
"planet_gear005",
|
||||
"planet_gear002",
|
||||
"body_up"
|
||||
],
|
||||
"planet_gear": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft"
|
||||
],
|
||||
"planet_gear003": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft"
|
||||
],
|
||||
"planet_gear004": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft"
|
||||
],
|
||||
"planet_gear005": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft"
|
||||
],
|
||||
"planet_gear002": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft"
|
||||
]
|
||||
|
||||
}
|
||||
}
|
175
freecad_workbench/freecad/robossembler/solve_optimizer.py
Normal file
175
freecad_workbench/freecad/robossembler/solve_optimizer.py
Normal file
|
@ -0,0 +1,175 @@
|
|||
'''
|
||||
Файл, содержащий скрипт для обработки и упрощения матрицы смежности. Запускается через команду в основном меню верстака freecad
|
||||
'''
|
||||
|
||||
|
||||
import json
|
||||
import FreeCAD as App
|
||||
from geometric_feasibility_predicate.main import main as asm_analysis
|
||||
from constraints_operator import collect_assembly_settings
|
||||
from clusterisation_sequences import ClusterisationSequenceUseCase
|
||||
|
||||
|
||||
# === Для работы с json-файлами. Работает. ===
|
||||
def simplify_adjacency_matrix_json(assembly_file, adjacency_file, output_file):
|
||||
def load_json(file_path):
|
||||
with open(file_path, 'r', encoding='utf-8') as file:
|
||||
return json.load(file)
|
||||
|
||||
def save_json(file_path, data):
|
||||
with open(file_path, 'w', encoding='utf-8') as file:
|
||||
json.dump(data, file, ensure_ascii=False, indent=4)
|
||||
|
||||
assembly_settings = load_json('/assembly_settings.json')
|
||||
adjacency_matrix = load_json('/adjacency_matrix.json')
|
||||
|
||||
fasteners = set()
|
||||
for item in assembly_settings:
|
||||
if item.get("Type") == "fastener_set":
|
||||
fasteners.add(item["Parent"])
|
||||
fasteners.add(item["Child"])
|
||||
|
||||
simplified_matrix = {
|
||||
"allParts": [],
|
||||
"matrix": {}
|
||||
}
|
||||
|
||||
for part in adjacency_matrix["allParts"]:
|
||||
if part not in fasteners:
|
||||
simplified_matrix["allParts"].append(part)
|
||||
neighbors = [
|
||||
neighbor for neighbor in adjacency_matrix["matrix"].get(part, [])
|
||||
if neighbor not in fasteners
|
||||
]
|
||||
if neighbors or part not in fasteners:
|
||||
simplified_matrix["matrix"][part] = neighbors
|
||||
|
||||
save_json(output_file, simplified_matrix)
|
||||
|
||||
|
||||
# simplify_adjacency_matrix('assembly_settings.json', 'adjacency_matrix.json', 'simplified_adjacency_matrix.json')
|
||||
|
||||
|
||||
def restore_full_sequence_json(assembly_file, sequence_file, output_file):
|
||||
def load_json(file_path):
|
||||
with open(file_path, 'r', encoding='utf-8') as file:
|
||||
return json.load(file)
|
||||
|
||||
def save_json(file_path, data):
|
||||
with open(file_path, 'w', encoding='utf-8') as file:
|
||||
json.dump(data, file, ensure_ascii=False, indent=4)
|
||||
|
||||
assembly_settings = load_json(assembly_file)
|
||||
sequence = load_json(sequence_file)
|
||||
|
||||
full_sequence = []
|
||||
sequence_set = set(sequence)
|
||||
|
||||
for item in sequence:
|
||||
full_sequence.append(item)
|
||||
for setting in assembly_settings:
|
||||
if setting.get("Type") == "fastener_set":
|
||||
parent = setting["Parent"]
|
||||
child = setting["Child"]
|
||||
if parent in sequence_set and child in sequence_set:
|
||||
full_sequence.append(setting["Fasteners"])
|
||||
|
||||
save_json(output_file, full_sequence)
|
||||
|
||||
|
||||
# restore_full_sequence('assembly_settings.json', 'sequence.json', 'full_sequence.json')
|
||||
|
||||
|
||||
# ==== Для работы с внутренними переменными
|
||||
|
||||
def simplify_adjacency_matrix(assembly_settings, adjacency_matrix):
|
||||
fasteners = set()
|
||||
for item in assembly_settings:
|
||||
if item.get("Type") == "fastener_set":
|
||||
fasteners.add(item["Parent"])
|
||||
fasteners.add(item["Child"])
|
||||
|
||||
simplified_matrix = {
|
||||
"allParts": [],
|
||||
"matrix": {}
|
||||
}
|
||||
|
||||
for part in adjacency_matrix["allParts"]:
|
||||
# Добавляем все детали, включая крепеж, в simplified_matrix["allParts"]
|
||||
simplified_matrix["allParts"].append(part)
|
||||
|
||||
# Получаем соседей, исключая только крепежные элементы
|
||||
neighbors = [
|
||||
neighbor for neighbor in adjacency_matrix["matrix"].get(part, [])
|
||||
if neighbor not in fasteners
|
||||
]
|
||||
|
||||
# Если у части есть соседи, добавляем их в матрицу
|
||||
if neighbors:
|
||||
simplified_matrix["matrix"][part] = neighbors
|
||||
|
||||
return simplified_matrix
|
||||
|
||||
|
||||
def restore_full_sequence(assembly_settings, sequences):
|
||||
full_sequences = []
|
||||
|
||||
for sequence in sequences:
|
||||
full_sequence = []
|
||||
sequence_set = set(sequence)
|
||||
|
||||
# Сначала добавляем все детали в полную последовательность
|
||||
for item in sequence:
|
||||
full_sequence.append(item)
|
||||
|
||||
# Теперь добавляем крепежные элементы после элемента с наибольшим порядковым номером
|
||||
for setting in assembly_settings:
|
||||
if setting.get("Type") == "fastener_set":
|
||||
parent = setting["Parent"]
|
||||
child = setting["Child"]
|
||||
fasteners = setting["Fasteners"]
|
||||
|
||||
# Проверяем, если родитель и ребенок в последовательности
|
||||
if (parent in sequence_set) and (child in sequence_set):
|
||||
# Находим индексы родителя и ребенка в полной последовательности
|
||||
parent_index = full_sequence.index(parent)
|
||||
child_index = full_sequence.index(child)
|
||||
|
||||
# Находим максимальный индекс между родителем и ребенком
|
||||
max_index = max(parent_index, child_index)
|
||||
|
||||
# Проверяем, содержатся ли крепежные элементы уже в последовательности
|
||||
if not any(fastener in full_sequence for fastener in fasteners):
|
||||
# Добавляем крепежные элементы после элемента child
|
||||
full_sequence[max_index + 1:max_index + 1] = fasteners
|
||||
|
||||
full_sequences.append(full_sequence)
|
||||
|
||||
return full_sequences
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
App.open('/home/markvoltov/GitProjects/framework/test_models/test_reductor.FCStd')
|
||||
if App.ActiveDocument:
|
||||
intersection_geometry, sequences, topologyMatrix = asm_analysis()
|
||||
|
||||
assembly_settings = collect_assembly_settings()
|
||||
print(assembly_settings)
|
||||
simplified_matrix = simplify_adjacency_matrix(assembly_settings, topologyMatrix)
|
||||
|
||||
assembly_sequences = ClusterisationSequenceUseCase().call(topologyMatrix['matrix'])
|
||||
# print('Последовательности 1', assembly_sequences)
|
||||
assembly_sequences_simplified= ClusterisationSequenceUseCase().call(simplified_matrix['matrix'])
|
||||
# print('Последовательности 2',assembly_sequences_simplified)
|
||||
assembly_sequences_restored = restore_full_sequence(assembly_settings, assembly_sequences_simplified)
|
||||
print('Последовательности 3',assembly_sequences_restored)
|
||||
else:
|
||||
print('Ошибка. Нет активного документа!')
|
||||
|
||||
main()
|
6
freecad_workbench/freecad/robossembler/test.py
Normal file
6
freecad_workbench/freecad/robossembler/test.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
adj_matrix = {
|
||||
"A": ["B", "C"],
|
||||
"B": ["C"],
|
||||
"C": []}
|
||||
|
||||
from geometric_feasibility_predicate.main import main as asm_analysis
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -15,22 +15,29 @@ def save_sequences(sequences, file_path):
|
|||
with open(file_path, 'w') as file:
|
||||
json.dump(sequences, file, indent=4)
|
||||
|
||||
# Load data from files
|
||||
adjacency_matrix = load_data('adjacency_matrix.json')
|
||||
constraints = load_data('constraints.json')
|
||||
sequences = load_data('sequences.json')
|
||||
# adjacency_matrix = load_data('adjacency_matrix.json')
|
||||
# constraints = load_data('constraints.json')
|
||||
# sequences = load_data('sequences.json')
|
||||
|
||||
# Get all parts and first detail
|
||||
all_parts = adjacency_matrix['allParts']
|
||||
first_detail = adjacency_matrix['firstDetail']
|
||||
# all_parts = adjacency_matrix['allParts']
|
||||
# first_detail = adjacency_matrix['firstDetail']
|
||||
|
||||
# Filter valid sequences
|
||||
valid_sequences = []
|
||||
for sequence in sequences:
|
||||
if len(set(sequence)) == len(set(all_parts)): #and is_valid_sequence(sequence, constraints):
|
||||
valid_sequences.append(sequence)
|
||||
# valid_sequences = []
|
||||
# for sequence in sequences:
|
||||
# if len(set(sequence)) == len(set(all_parts)): #and is_valid_sequence(sequence, constraints):
|
||||
# valid_sequences.append(sequence)
|
||||
|
||||
# Save valid sequences to file
|
||||
save_sequences(valid_sequences, 'valid_sequences.json')
|
||||
# save_sequences(valid_sequences, 'valid_sequences.json')
|
||||
|
||||
print(f"Найдено {len(valid_sequences)} допустимых последовательностей сборки.")
|
||||
# print(f"Найдено {len(valid_sequences)} допустимых последовательностей сборки.")
|
||||
|
||||
def filter_valid_sequences(adjacency_matrix, sequences, constraints):
|
||||
all_parts = adjacency_matrix['allParts']
|
||||
first_detail = adjacency_matrix['firstDetail']
|
||||
|
||||
valid_sequences = []
|
||||
for sequence in sequences:
|
||||
if len(set(sequence)) == len(set(all_parts)):
|
||||
valid_sequences.append(sequence)
|
||||
|
||||
return valid_sequences
|
101
test_models/adjacency_matrix.json
Normal file
101
test_models/adjacency_matrix.json
Normal file
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"allParts": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft",
|
||||
"planet_gear",
|
||||
"planet_gear003",
|
||||
"planet_gear004",
|
||||
"planet_gear005",
|
||||
"planet_gear002",
|
||||
"body_up",
|
||||
"bolt",
|
||||
"bolt2",
|
||||
"bolt3",
|
||||
"bolt4"
|
||||
],
|
||||
"firstDetail": "body_down",
|
||||
"matrix": {
|
||||
"body_down": [
|
||||
"sol_gear",
|
||||
"planet_gear",
|
||||
"planet_gear003",
|
||||
"planet_gear004",
|
||||
"planet_gear005",
|
||||
"planet_gear002",
|
||||
"body_up",
|
||||
"bolt",
|
||||
"bolt2",
|
||||
"bolt3",
|
||||
"bolt4"
|
||||
],
|
||||
"sol_gear": [
|
||||
"body_down",
|
||||
"output_shaft",
|
||||
"planet_gear",
|
||||
"planet_gear003",
|
||||
"planet_gear004",
|
||||
"planet_gear005",
|
||||
"planet_gear002"
|
||||
],
|
||||
"output_shaft": [
|
||||
"sol_gear",
|
||||
"planet_gear",
|
||||
"planet_gear003",
|
||||
"planet_gear004",
|
||||
"planet_gear005",
|
||||
"planet_gear002",
|
||||
"body_up"
|
||||
],
|
||||
"planet_gear": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft"
|
||||
],
|
||||
"planet_gear003": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft"
|
||||
],
|
||||
"planet_gear004": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft"
|
||||
],
|
||||
"planet_gear005": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft"
|
||||
],
|
||||
"planet_gear002": [
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft"
|
||||
],
|
||||
"body_up": [
|
||||
"body_down",
|
||||
"output_shaft",
|
||||
"bolt",
|
||||
"bolt2",
|
||||
"bolt3",
|
||||
"bolt4"
|
||||
],
|
||||
"bolt": [
|
||||
"body_down",
|
||||
"body_up"
|
||||
],
|
||||
"bolt2": [
|
||||
"body_down",
|
||||
"body_up"
|
||||
],
|
||||
"bolt3": [
|
||||
"body_down",
|
||||
"body_up"
|
||||
],
|
||||
"bolt4": [
|
||||
"body_down",
|
||||
"body_up"
|
||||
]
|
||||
},
|
||||
"matrixError": null
|
||||
}
|
Binary file not shown.
0
test_models/env.json
Normal file
0
test_models/env.json
Normal file
4
test_models/intersection_geometry.json
Normal file
4
test_models/intersection_geometry.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"status": true,
|
||||
"recalculations": null
|
||||
}
|
|
@ -1,10 +1,3 @@
|
|||
{
|
||||
"sequences": [
|
||||
|
||||
"body_down",
|
||||
"sol_gear",
|
||||
"output_shaft",
|
||||
"planet_gear",
|
||||
"planet_gear002"
|
||||
]
|
||||
"sequences": []
|
||||
}
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue