solve optimizer
This commit is contained in:
parent
a59b3a4775
commit
ca0df838a2
17 changed files with 491 additions and 236 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())
|
|
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()
|
plt.show()
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
sequence = load_assembly_sequence('assembly_sequence.json')
|
sequence = load_assembly_sequence('/home/markvoltov/GitProjects/framework/test_models/adjacency_matrix.json')
|
||||||
assembly_graph = create_assembly_graph(sequence)
|
assembly_graph = create_assembly_graph(sequence)
|
||||||
|
|
||||||
draw_graph(assembly_graph)
|
draw_graph(assembly_graph)
|
||||||
|
|
||||||
|
# main()
|
||||||
|
|
|
@ -135,6 +135,50 @@ def export_assembly_settings():
|
||||||
with open(save_path, "w") as f:
|
with open(save_path, "w") as f:
|
||||||
json.dump(data, f, indent=4)
|
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_fastener_set()
|
||||||
# create_assembly_sequence()
|
# create_assembly_sequence()
|
||||||
# create_clearance_constraint()
|
# create_clearance_constraint()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"cadFilePath": "path/to/file",
|
"cadFilePath": "/home/markvoltov/GitProjects/framework/test_models/test_reductor.FCStd",
|
||||||
"outPath": "out/path",
|
"outPath": "/home/markvoltov/GitProjects/framework/test_models/",
|
||||||
"objectIndentation": 0
|
"objectIndentation": 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ class AllSequences:
|
||||||
for el in v:
|
for el in v:
|
||||||
adj[inc][el - 1] = 1
|
adj[inc][el - 1] = 1
|
||||||
inc += 1
|
inc += 1
|
||||||
return self.find_all_sequences(adj)
|
self.find_all_sequences(adj)
|
||||||
|
|
||||||
|
|
||||||
class VectorModel:
|
class VectorModel:
|
||||||
|
@ -580,8 +580,8 @@ class CadAdjacencyMatrix:
|
||||||
collisionResult: int = int(
|
collisionResult: int = int(
|
||||||
part.Shape.distToShape(nextPart.Shape)[0]
|
part.Shape.distToShape(nextPart.Shape)[0]
|
||||||
)
|
)
|
||||||
print(collisionResult)
|
# print(collisionResult)
|
||||||
print("collisionResult")
|
# print("collisionResult")
|
||||||
if collisionResult == 0:
|
if collisionResult == 0:
|
||||||
matrix[part.Label].append(nextPart.Label)
|
matrix[part.Label].append(nextPart.Label)
|
||||||
|
|
||||||
|
@ -708,68 +708,82 @@ class ExitFreeCadUseCase:
|
||||||
# FreeCadRepository().obj
|
# FreeCadRepository().obj
|
||||||
# pass
|
# 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():
|
def main():
|
||||||
env = FS.readJSON("env.json")
|
if 'FreeCAD' in globals():
|
||||||
cadFilePath = str(env["cadFilePath"])
|
active_doc = FreeCAD.activeDocument()
|
||||||
outPath = str(env["outPath"])
|
if active_doc:
|
||||||
objectIndentation = float(env["objectIndentation"])
|
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:
|
if cadFilePath == None:
|
||||||
return TypeError("CadFile not found env.json")
|
return TypeError("CadFile not found env.json")
|
||||||
App.open("" + cadFilePath)
|
App.open("" + cadFilePath)
|
||||||
|
|
||||||
# isAllObjectSolidsCheckUseCase = IsAllObjectSolidsCheckUseCase.call()
|
isAllObjectSolidsCheckUseCase = IsAllObjectSolidsCheckUseCase.call()
|
||||||
|
|
||||||
# if isAllObjectSolidsCheckUseCase != None:
|
if isAllObjectSolidsCheckUseCase != None:
|
||||||
# FS.writeFile(isAllObjectSolidsCheckUseCase.toString(), outPath, 'error.json')
|
FS.writeFile(isAllObjectSolidsCheckUseCase.toString(), outPath, 'error.json')
|
||||||
# ExitFreeCadUseCase.call()
|
ExitFreeCadUseCase.call()
|
||||||
# return
|
return
|
||||||
|
|
||||||
|
FreeCAD.open(cadFilePath)
|
||||||
|
|
||||||
|
|
||||||
# checkObjectHasTouchesUseCase = CheckObjectHasTouchesUseCase.call(objectIndentation)
|
|
||||||
|
|
||||||
# if checkObjectHasTouchesUseCase != None:
|
|
||||||
# FS.writeFile(checkObjectHasTouchesUseCase.toString(), outPath, 'error.json')
|
|
||||||
# ExitFreeCadUseCase.call()
|
|
||||||
# return
|
|
||||||
|
|
||||||
topologyMatrix = CadAdjacencyMatrix().matrixBySurfaces()
|
topologyMatrix = CadAdjacencyMatrix().matrixBySurfaces()
|
||||||
import json
|
|
||||||
|
|
||||||
sequences = json.dumps(
|
sequences = {
|
||||||
{"sequences": AllSequences(topologyMatrix.matrix).adj_matrix_names},
|
"sequences": AllSequences(topologyMatrix.matrix).adj_matrix_names
|
||||||
ensure_ascii=False,
|
}
|
||||||
indent=4,
|
|
||||||
)
|
|
||||||
matrix = topologyMatrix.matrix
|
matrix = topologyMatrix.matrix
|
||||||
contacts = matrixGetUniqueContact(matrix)
|
contacts = matrixGetUniqueContact(matrix)
|
||||||
intersection_geometry = {"status": True, "recalculations": None}
|
intersection_geometry = {"status": True, "recalculations": None}
|
||||||
|
|
||||||
for el in contacts:
|
for el in contacts:
|
||||||
child = App.ActiveDocument.getObjectsByLabel(el.get("child"))[0]
|
child = App.ActiveDocument.getObjectsByLabel(el.get("child"))[0]
|
||||||
parent = App.ActiveDocument.getObjectsByLabel(el.get("parent"))[0]
|
parent = App.ActiveDocument.getObjectsByLabel(el.get("parent"))[0]
|
||||||
area = IntersectionComputedUseCase.call([child, parent])
|
area = IntersectionComputedUseCase.call([child, parent])
|
||||||
if area != 0.0:
|
if area != 0.0:
|
||||||
if intersection_geometry.get("recalculations") == None:
|
if intersection_geometry.get("recalculations") is None:
|
||||||
intersection_geometry["status"] = False
|
intersection_geometry["status"] = False
|
||||||
intersection_geometry["recalculations"] = []
|
intersection_geometry["recalculations"] = []
|
||||||
intersection_geometry["recalculations"].append(
|
intersection_geometry["recalculations"].append(
|
||||||
{"area": area, "connect": el.get("child") + " " + el.get("parent")}
|
{"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(
|
# ExitFreeCadUseCase.call() Сейчас пока не нужна
|
||||||
json.dumps(intersection_geometry, ensure_ascii=False, indent=4),
|
# return intersection_geometry, sequences, topologyMatrix.to_dict()
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
#main()
|
# main()
|
||||||
|
|
|
@ -42,20 +42,36 @@ def save_sequences(sequences, file_path):
|
||||||
with open(file_path, 'w') as file:
|
with open(file_path, 'w') as file:
|
||||||
json.dump(sequences, file, indent=4)
|
json.dump(sequences, file, indent=4)
|
||||||
|
|
||||||
data = load_data('adjacency_matrix.json')
|
# data = load_data('/home/markvoltov/GitProjects/framework/test_models/adjacency_matrix.json')
|
||||||
constraints = load_constraints('constraints.json')
|
# # constraints = load_constraints('constraints.json')
|
||||||
all_parts = data['allParts']
|
# all_parts = data['allParts']
|
||||||
graph = create_graph(data)
|
# print(all_parts)
|
||||||
first_detail = data['firstDetail']
|
# graph = create_graph(data)
|
||||||
leaf_nodes = find_leaf_nodes(graph, first_detail)
|
# first_detail = data['firstDetail']
|
||||||
|
# leaf_nodes = find_leaf_nodes(graph, first_detail)
|
||||||
|
|
||||||
all_sequences = []
|
# all_sequences = []
|
||||||
for leaf in leaf_nodes:
|
# for leaf in leaf_nodes:
|
||||||
paths = find_all_paths(graph, leaf, first_detail)
|
# paths = find_all_paths(graph, leaf, first_detail)
|
||||||
for path in paths:
|
# for path in paths:
|
||||||
if set(path) == set(all_parts) and is_valid_sequence(path, constraints):
|
# if set(path) == set(all_parts) and is_valid_sequence(path, constraints):
|
||||||
all_sequences.append(path)
|
# 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
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
# Загружаем данные из файла
|
# Загружаем данные из файла
|
||||||
with open('adjacency_matrix.json', 'r') as file:
|
with open('/home/markvoltov/GitProjects/framework/freecad_workbench/freecad/robossembler/simplified_adjacency_matrix.json', 'r') as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
|
|
||||||
# Создаем пустой граф
|
# Создаем пустой граф
|
||||||
|
|
|
@ -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"
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,112 +1,136 @@
|
||||||
import FreeCAD
|
'''
|
||||||
import numpy as np
|
Файл, содержащий скрипт для обработки и упрощения матрицы смежности. Запускается через команду в основном меню верстака freecad
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import FreeCAD as App
|
||||||
|
from geometric_feasibility_predicate.main import main as asm_analysis
|
||||||
|
from constraints_operator import collect_assembly_settings
|
||||||
|
|
||||||
|
|
||||||
#Запускается после генерации матрицы смежности и разметки изделия
|
# === Для работы с 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 get_adjacency_matrix_from_file(file_path):
|
def save_json(file_path, data):
|
||||||
with open(file_path, 'r') as f:
|
with open(file_path, 'w', encoding='utf-8') as file:
|
||||||
adjacency_matrix = np.array(json.load(f))
|
json.dump(data, file, ensure_ascii=False, indent=4)
|
||||||
#print(adjacency_matrix)
|
|
||||||
return adjacency_matrix
|
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)
|
||||||
|
|
||||||
def get_asm_settings(asm_settings_path):
|
|
||||||
with open(asm_settings_path), 'r') as f:
|
|
||||||
asm_settings = np.array(json.load(f))
|
|
||||||
#print(asm_settings)
|
|
||||||
return asm_settings
|
|
||||||
|
|
||||||
#def get_parts_with_fasteners(asm_settings):
|
# restore_full_sequence('assembly_settings.json', 'sequence.json', 'full_sequence.json')
|
||||||
'''
|
|
||||||
"Type": "fastener_set",
|
|
||||||
"Parent": obj.Parent.Label,
|
|
||||||
"Child": obj.Child.Label,
|
|
||||||
"Fasteners": fasteners '''
|
|
||||||
#вероятно, тут нужно создавать список объектов
|
|
||||||
|
|
||||||
# fastener_set = []
|
|
||||||
#чтобы это работало, нужно работать с позициями элементов в дереве
|
|
||||||
#возможно, отработает и через лейблы
|
|
||||||
|
|
||||||
|
|
||||||
#return part_names, fasteners
|
|
||||||
|
|
||||||
def remove_fasteners_from_matrix(adjacency_matrix, part_names, fasteners):
|
# ==== Для работы с внутренними переменными
|
||||||
fastener_connections = {}
|
|
||||||
indices_to_remove = []
|
|
||||||
|
|
||||||
for fastener in fasteners:
|
def simplify_adjacency_matrix(assembly_settings, adjacency_matrix):
|
||||||
idx = part_names.index(fastener)
|
fasteners = set()
|
||||||
indices_to_remove.append(idx)
|
for item in assembly_settings:
|
||||||
|
if item.get("Type") == "fastener_set":
|
||||||
|
fasteners.add(item["Parent"])
|
||||||
|
fasteners.add(item["Child"])
|
||||||
|
|
||||||
connected_parts = []
|
simplified_matrix = {
|
||||||
for i in range(len(adjacency_matrix)):
|
"allParts": [],
|
||||||
if adjacency_matrix[i, idx] == 1:
|
"matrix": {}
|
||||||
connected_parts.append(part_names[i])
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
return simplified_matrix
|
||||||
|
|
||||||
|
def restore_full_sequence(assembly_settings, sequence):
|
||||||
|
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"])
|
||||||
|
|
||||||
|
return full_sequence
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
App.open("/home/markvoltov/GitProjects/framework/test_models/desk_table.FCStd")
|
||||||
|
|
||||||
|
if App.ActiveDocument:
|
||||||
|
intersection_geometry, sequences, topologyMatrix = asm_analysis()
|
||||||
|
print(sequences)
|
||||||
|
assembly_settings = collect_assembly_settings()
|
||||||
|
simplified_matrix = simplify_adjacency_matrix(assembly_settings, topologyMatrix)
|
||||||
|
|
||||||
fastener_connections[fastener] = connected_parts
|
else:
|
||||||
|
print('Ошибка. Нет активного документа!')
|
||||||
|
|
||||||
reduced_matrix = np.delete(adjacency_matrix, indices_to_remove, axis=0)
|
main()
|
||||||
reduced_matrix = np.delete(reduced_matrix, indices_to_remove, axis=1)
|
|
||||||
reduced_part_names = [part for part in part_names if part not in fasteners]
|
|
||||||
|
|
||||||
return reduced_matrix, reduced_part_names, fastener_connections
|
|
||||||
|
|
||||||
def save_to_json(data, file_path):
|
|
||||||
with open(file_path, 'w') as f:
|
|
||||||
json.dump(data, f, indent=4)
|
|
||||||
|
|
||||||
|
|
||||||
doc = FreeCAD.ActiveDocument
|
|
||||||
|
|
||||||
|
|
||||||
adjacency_matrix = get_adjacency_matrix_from_file('adjacency_matrix.json')
|
|
||||||
part_names, fasteners = get_parts_with_fasteners(doc)
|
|
||||||
|
|
||||||
# Построение сокращенной матрицы смежности
|
|
||||||
reduced_matrix, reduced_part_names, fastener_connections = remove_fasteners_from_matrix(adjacency_matrix, part_names, fasteners)
|
|
||||||
|
|
||||||
# Сохранение результатов в файлы JSON
|
|
||||||
save_to_json(reduced_matrix.tolist(), 'reduced_adjacency_matrix.json')
|
|
||||||
save_to_json(reduced_part_names, 'reduced_part_names.json')
|
|
||||||
save_to_json(fastener_connections, 'fastener_connections.json')
|
|
||||||
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
with open('adjacency_matrix.json') as f:
|
|
||||||
adjacency_matrix = json.load(f)
|
|
||||||
with open('assembly_settings.json') as f:
|
|
||||||
assembly_settings = json.load(f)
|
|
||||||
|
|
||||||
|
|
||||||
fasteners_to_exclude = set()
|
|
||||||
for setting in assembly_settings:
|
|
||||||
if setting.get('Type') == 'fastener_set':
|
|
||||||
fasteners_to_exclude.update(setting.get('Fasteners'))
|
|
||||||
|
|
||||||
|
|
||||||
#for fastener in fasteners_to_exclude:
|
|
||||||
# del adjacency_matrix[fastener]
|
|
||||||
|
|
||||||
#здесь должен запуститься файл генерации последовательности сборки
|
|
||||||
#на вход поступает упрощенная матрица смежности
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
with open('assembly_sequence.json') as f:
|
|
||||||
assembly_sequence = json.load(f)
|
|
||||||
#TODO: сделать в виде функции, вызываемой отдельно
|
|
||||||
#Добавляем крепежи в посл. сборки
|
|
||||||
for setting in assembly_settings:
|
|
||||||
if setting.get('Type') == 'fastener_set':
|
|
||||||
parent = setting.get('Parent')
|
|
||||||
child = setting.get('Child')
|
|
||||||
fasteners = set(setting.get('Fasteners'))
|
|
||||||
for step in assembly_sequence:
|
|
||||||
if step.get('Parent') == parent and step.get('Child') == child:
|
|
||||||
step['Fasteners'] = list(fasteners.intersection(step.get('Fasteners', [])))
|
|
||||||
break
|
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
|
@ -15,22 +15,29 @@ def save_sequences(sequences, file_path):
|
||||||
with open(file_path, 'w') as file:
|
with open(file_path, 'w') as file:
|
||||||
json.dump(sequences, file, indent=4)
|
json.dump(sequences, file, indent=4)
|
||||||
|
|
||||||
# Load data from files
|
# adjacency_matrix = load_data('adjacency_matrix.json')
|
||||||
adjacency_matrix = load_data('adjacency_matrix.json')
|
# constraints = load_data('constraints.json')
|
||||||
constraints = load_data('constraints.json')
|
# sequences = load_data('sequences.json')
|
||||||
sequences = load_data('sequences.json')
|
|
||||||
|
|
||||||
# Get all parts and first detail
|
# all_parts = adjacency_matrix['allParts']
|
||||||
all_parts = adjacency_matrix['allParts']
|
# first_detail = adjacency_matrix['firstDetail']
|
||||||
first_detail = adjacency_matrix['firstDetail']
|
|
||||||
|
|
||||||
# Filter valid sequences
|
# valid_sequences = []
|
||||||
valid_sequences = []
|
# for sequence in sequences:
|
||||||
for sequence in sequences:
|
# if len(set(sequence)) == len(set(all_parts)): #and is_valid_sequence(sequence, constraints):
|
||||||
if len(set(sequence)) == len(set(all_parts)): #and is_valid_sequence(sequence, constraints):
|
# valid_sequences.append(sequence)
|
||||||
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
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"body_down":["body_up","bolt1", "bolt2", "bolt3", "bolt4","sol_gear","planet_gear"],
|
|
||||||
"body_up":["body_down","bolt1", "bolt2", "bolt3", "bolt4","sol_gear"],
|
|
||||||
"body_down":["body_up","bolt1", "bolt2", "bolt3", "bolt4","sol_gear","planet_gear"]
|
|
||||||
}
|
|
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
|
||||||
|
}
|
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": [
|
"sequences": []
|
||||||
|
|
||||||
"body_down",
|
|
||||||
"sol_gear",
|
|
||||||
"output_shaft",
|
|
||||||
"planet_gear",
|
|
||||||
"planet_gear002"
|
|
||||||
]
|
|
||||||
}
|
}
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue