Модуль обратной связи для анализа последовательностей
|
@ -8,7 +8,11 @@ from .export_entities import export_coordinate_systems
|
||||||
from .utils.freecad_processor import process_file
|
from .utils.freecad_processor import process_file
|
||||||
from .project_validator import validate_project
|
from .project_validator import validate_project
|
||||||
from .usecases.asm4parser_usecase import Asm4StructureParseUseCase
|
from .usecases.asm4parser_usecase import Asm4StructureParseUseCase
|
||||||
|
from .constraints_operator import create_assembly_parameters, create_fastener_set, create_assembly_sequence, create_clearance_constraint, export_assembly_settings
|
||||||
|
from .geometric_feasibility_predicate.main import main as structure_analysis
|
||||||
|
from .asm_graph import main as asm_graph
|
||||||
|
from .autodock_generator import main as asm_layers
|
||||||
|
from .assembly_graph_generation import main as structure_graph
|
||||||
|
|
||||||
if FreeCAD.GuiUp:
|
if FreeCAD.GuiUp:
|
||||||
import FreeCADGui
|
import FreeCADGui
|
||||||
|
@ -331,6 +335,46 @@ spawnClassCommand("Publish_Project",
|
||||||
{"Pixmap": str(os.path.join(ICONPATH, "publish.svg")),
|
{"Pixmap": str(os.path.join(ICONPATH, "publish.svg")),
|
||||||
"MenuText": "Publish Project",
|
"MenuText": "Publish Project",
|
||||||
"ToolTip": "Save and export project files"})
|
"ToolTip": "Save and export project files"})
|
||||||
|
spawnClassCommand("Create Assembly Parameters",
|
||||||
|
create_assembly_parameters,
|
||||||
|
{"Pixmap": str(os.path.join(ICONPATH, ".svg")),
|
||||||
|
"MenuText": "Create Assembly Parameters",
|
||||||
|
"ToolTip": "Create Assembly Parameters"})
|
||||||
|
spawnClassCommand("Create Fastener Set",
|
||||||
|
create_fastener_set,
|
||||||
|
{"Pixmap": str(os.path.join(ICONPATH, ".svg")),
|
||||||
|
"MenuText": "Create Fastener Set",
|
||||||
|
"ToolTip": "Create Fastener Set"})
|
||||||
|
spawnClassCommand("Compute Assembly Sequence",
|
||||||
|
structure_analysis,
|
||||||
|
{"Pixmap": str(os.path.join(ICONPATH, ".svg")),
|
||||||
|
"MenuText": "Create Fastener Set",
|
||||||
|
"ToolTip": "Create Fastener Set"})
|
||||||
|
spawnClassCommand("Create Assembly Sequence",
|
||||||
|
create_assembly_sequence,
|
||||||
|
{"Pixmap": str(os.path.join(ICONPATH, ".svg")),
|
||||||
|
"MenuText": "Create Assembly Sequence",
|
||||||
|
"ToolTip": "Create Assembly Sequence"})
|
||||||
|
spawnClassCommand("Export Assembly Settings",
|
||||||
|
export_assembly_settings,
|
||||||
|
{"Pixmap": str(os.path.join(ICONPATH, ".svg")),
|
||||||
|
"MenuText": "Export Assembly Settings",
|
||||||
|
"ToolTip": "Export Assembly Settings"})
|
||||||
|
spawnClassCommand("Create Assembly Layers",
|
||||||
|
asm_layers,
|
||||||
|
{"Pixmap": str(os.path.join(ICONPATH, ".svg")),
|
||||||
|
"MenuText": "Create Assembly Layers",
|
||||||
|
"ToolTip": "Create Assembly Layers"})
|
||||||
|
spawnClassCommand("Create Structure Graph",
|
||||||
|
structure_graph,
|
||||||
|
{"Pixmap": str(os.path.join(ICONPATH, ".svg")),
|
||||||
|
"MenuText": "Create Structure Graph",
|
||||||
|
"ToolTip": "Create Structure Graph"})
|
||||||
|
spawnClassCommand("Create Assembly Graph",
|
||||||
|
asm_graph,
|
||||||
|
{"Pixmap": str(os.path.join(ICONPATH, ".svg")),
|
||||||
|
"MenuText": "Create Assembly Graph",
|
||||||
|
"ToolTip": "Create Assembly Graph"})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
101
freecad_workbench/freecad/robossembler/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
|
||||||
|
}
|
40
freecad_workbench/freecad/robossembler/asm_graph.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import json
|
||||||
|
import networkx as nx
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import os
|
||||||
|
|
||||||
|
def load_sequences(filename):
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
return data['sequences']
|
||||||
|
|
||||||
|
def create_graph(sequences):
|
||||||
|
G = nx.DiGraph()
|
||||||
|
#for seq in sequences:
|
||||||
|
for i in range(len(sequences) - 1):
|
||||||
|
G.add_edge(sequences[i], sequences[i + 1])
|
||||||
|
return G
|
||||||
|
|
||||||
|
def draw_graph_with_thumbnails(G, image_folder):
|
||||||
|
pos = {}
|
||||||
|
x = 1
|
||||||
|
for node in G.nodes():
|
||||||
|
pos[node] = (1, -x) # Устанавливаем позиции узлов по вертикали
|
||||||
|
x += 1
|
||||||
|
|
||||||
|
plt.figure(figsize=(8, 6))
|
||||||
|
nx.draw(G, pos, with_labels=True, node_size=100, node_color='skyblue', font_size=10, font_weight='bold')
|
||||||
|
|
||||||
|
# for node, (x, y) in pos.items():
|
||||||
|
# image_path = f"{image_folder}/{node}.svg"
|
||||||
|
# if os.path.exists(image_path):
|
||||||
|
# img = plt.imread(image_path)
|
||||||
|
# plt.imshow(img, aspect='auto', extent=(x - 0.5, x + 0.5, y - 0.5, y + 0.5), zorder=0)
|
||||||
|
|
||||||
|
plt.gca().invert_yaxis() # Инвертируем ось y для вертикального отображения
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
sequences = load_sequences('valid_sequences.json')
|
||||||
|
G = create_graph(sequences)
|
||||||
|
draw_graph_with_thumbnails(G, '/home/markvoltov/GitProjects/framework/test_models/img')
|
|
@ -40,5 +40,3 @@ def main():
|
||||||
|
|
||||||
draw_graph(assembly_graph)
|
draw_graph(assembly_graph)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
{
|
||||||
"cadFilePath": "/home/markvoltov/GitProjects/framework/test_models/cubes.FCStd",
|
"cadFilePath": "path/to/file",
|
||||||
"outPath": "/home/markvoltov/GitProjects/framework/test_models/"
|
"outPath": "out/path"
|
||||||
}
|
}
|
||||||
|
|
6
freecad_workbench/freecad/robossembler/constraints.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"Extruder",
|
||||||
|
"Extruder001"
|
||||||
|
]
|
||||||
|
]
|
|
@ -86,6 +86,8 @@ def create_assembly_parameters():
|
||||||
|
|
||||||
assembly_settings_folder.addObject(assembly_parameters)
|
assembly_settings_folder.addObject(assembly_parameters)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#экспорт всех заданных настроек в общий файл json
|
#экспорт всех заданных настроек в общий файл json
|
||||||
def export_assembly_settings():
|
def export_assembly_settings():
|
||||||
doc = App.activeDocument()
|
doc = App.activeDocument()
|
||||||
|
@ -133,7 +135,7 @@ 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)
|
||||||
|
|
||||||
#create_fastener_set()
|
# create_fastener_set()
|
||||||
#create_assembly_sequence()
|
# create_assembly_sequence()
|
||||||
#create_clearance_constraint()
|
# create_clearance_constraint()
|
||||||
export_assembly_settings()
|
# export_assembly_settings()
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"cadFilePath": "/home/markvoltov/GitProjects/framework/test_models/cubes.FCStd",
|
"cadFilePath": "path/to/file",
|
||||||
"outPath": "/home/markvoltov/GitProjects/framework/test_models/",
|
"outPath": "out/path",
|
||||||
"objectIndentation": 0
|
"objectIndentation": 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -771,4 +771,4 @@ def main():
|
||||||
ExitFreeCadUseCase.call()
|
ExitFreeCadUseCase.call()
|
||||||
|
|
||||||
|
|
||||||
main()
|
#main()
|
||||||
|
|
61
freecad_workbench/freecad/robossembler/get_sequences.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import json
|
||||||
|
import networkx as nx
|
||||||
|
|
||||||
|
def load_data(file_path):
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
return json.load(file)
|
||||||
|
|
||||||
|
def create_graph(data):
|
||||||
|
G = nx.Graph()
|
||||||
|
for part in data['allParts']:
|
||||||
|
G.add_node(part)
|
||||||
|
for part, connections in data['matrix'].items():
|
||||||
|
for connected_part in connections:
|
||||||
|
G.add_edge(part, connected_part)
|
||||||
|
return G
|
||||||
|
|
||||||
|
def find_leaf_nodes(graph, central_node):
|
||||||
|
leaf_nodes = []
|
||||||
|
for node in graph.nodes:
|
||||||
|
if node != central_node and graph.degree(node) == 1:
|
||||||
|
leaf_nodes.append(node)
|
||||||
|
return leaf_nodes
|
||||||
|
|
||||||
|
def find_all_paths(graph, start_node, end_node):
|
||||||
|
try:
|
||||||
|
return list(nx.all_simple_paths(graph, start_node, end_node))
|
||||||
|
except nx.NetworkXNoPath:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def load_constraints(file_path):
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
return json.load(file)
|
||||||
|
|
||||||
|
def is_valid_sequence(sequence, constraints):
|
||||||
|
for constraint in constraints:
|
||||||
|
if constraint[0] in sequence and constraint[1] in sequence:
|
||||||
|
if sequence.index(constraint[0]) > sequence.index(constraint[1]):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
print(f"Найдено {len(all_sequences)} допустимых последовательностей сборки.")
|
|
@ -0,0 +1,26 @@
|
||||||
|
import json
|
||||||
|
import networkx as nx
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
# Загружаем данные из файла
|
||||||
|
with open('adjacency_matrix.json', 'r') as file:
|
||||||
|
data = json.load(file)
|
||||||
|
|
||||||
|
# Создаем пустой граф
|
||||||
|
G = nx.Graph()
|
||||||
|
|
||||||
|
# Добавляем узлы
|
||||||
|
for part in data['allParts']:
|
||||||
|
G.add_node(part)
|
||||||
|
|
||||||
|
# Добавляем ребра
|
||||||
|
for part, connections in data['matrix'].items():
|
||||||
|
for connected_part in connections:
|
||||||
|
G.add_edge(part, connected_part)
|
||||||
|
|
||||||
|
# Визуализируем граф
|
||||||
|
plt.figure(figsize=(10, 8))
|
||||||
|
pos = nx.spring_layout(G)
|
||||||
|
nx.draw(G, pos, with_labels=True, node_size=100, node_color='lightblue', font_size=10, font_weight='bold', edge_color='gray')
|
||||||
|
plt.title('Graph of Part Connections')
|
||||||
|
plt.show()
|
|
@ -38,8 +38,7 @@ class Robossembler(Gui.Workbench):
|
||||||
|
|
||||||
self.framecommands = [
|
self.framecommands = [
|
||||||
"BoMGeneration",
|
"BoMGeneration",
|
||||||
"FrameCommand",
|
"FrameCommand",
|
||||||
|
|
||||||
"SelectedPartFrameCommand",
|
"SelectedPartFrameCommand",
|
||||||
"AllPartFramesCommand",
|
"AllPartFramesCommand",
|
||||||
"FeatureFrameCommand"
|
"FeatureFrameCommand"
|
||||||
|
@ -52,8 +51,21 @@ class Robossembler(Gui.Workbench):
|
||||||
"Validate_Project",
|
"Validate_Project",
|
||||||
"Publish_Project"
|
"Publish_Project"
|
||||||
]
|
]
|
||||||
|
self.asmcommands = [
|
||||||
|
"Create Assembly Parameters",
|
||||||
|
"Create Fastener Set",
|
||||||
|
"Create Assembly Sequence",
|
||||||
|
"Export Assembly Settings",
|
||||||
|
"Compute Assembly Sequence",
|
||||||
|
"Create Assembly Layers",
|
||||||
|
"Create Structure Graph",
|
||||||
|
"Create Assembly Graph"
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
self.appendToolbar(f"{__class__.__name__} Frames", self.framecommands)
|
self.appendToolbar(f"{__class__.__name__} Frames", self.framecommands)
|
||||||
self.appendToolbar(f"{__class__.__name__} Tools", self.toolcommands)
|
self.appendToolbar(f"{__class__.__name__} Tools", self.toolcommands)
|
||||||
|
self.appendToolbar(f"{__class__.__name__} Assembly Setup", self.asmcommands)
|
||||||
|
|
||||||
App.Console.PrintMessage(translate("Console",
|
App.Console.PrintMessage(translate("Console",
|
||||||
"Switching to robossembler") + "\n")
|
"Switching to robossembler") + "\n")
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"sequences": [
|
|
||||||
|
|
||||||
"body_down",
|
|
||||||
"sol_gear",
|
|
||||||
"output_shaft",
|
|
||||||
"planet_gear",
|
|
||||||
"planet_gear002"
|
|
||||||
]
|
|
||||||
}
|
|
14
freecad_workbench/freecad/robossembler/valid_sequences.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{"sequences":[
|
||||||
|
"PTFE Tube Nut M6 v1",
|
||||||
|
"PTFE Tube Nut M6 v002",
|
||||||
|
"Extruder002",
|
||||||
|
"Extruder",
|
||||||
|
"Extruder001",
|
||||||
|
"91239A140_Button Head Hex Drive Screw",
|
||||||
|
"Hotend002",
|
||||||
|
"Hotend001",
|
||||||
|
"Hotend003",
|
||||||
|
"Extruder003",
|
||||||
|
"Extruder004",
|
||||||
|
"Hotend"
|
||||||
|
]}
|
36
freecad_workbench/freecad/robossembler/valid_sequences.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
def load_data(file_path):
|
||||||
|
with open(file_path, 'r') as file:
|
||||||
|
return json.load(file)
|
||||||
|
|
||||||
|
def is_valid_sequence(sequence, constraints):
|
||||||
|
for constraint in constraints:
|
||||||
|
if constraint[0] in sequence and constraint[1] in sequence:
|
||||||
|
if sequence.index(constraint[0]) > sequence.index(constraint[1]):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
# Get all parts and first detail
|
||||||
|
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)
|
||||||
|
|
||||||
|
# Save valid sequences to file
|
||||||
|
save_sequences(valid_sequences, 'valid_sequences.json')
|
||||||
|
|
||||||
|
print(f"Найдено {len(valid_sequences)} допустимых последовательностей сборки.")
|
34
test_models/assembly_settings_test_reductor.json
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Name": "Fastener_Set",
|
||||||
|
"Type": "fastener_set",
|
||||||
|
"Parent": "body_down",
|
||||||
|
"Child": "body_up",
|
||||||
|
"Fasteners": [
|
||||||
|
"bolt4",
|
||||||
|
"bolt",
|
||||||
|
"bolt2",
|
||||||
|
"bolt3"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Assembly_Sequence",
|
||||||
|
"Type": "asm_sequence",
|
||||||
|
"Parent": "body_down",
|
||||||
|
"Child": "sol_gear"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Clearance_Constraint",
|
||||||
|
"Type": "clearance",
|
||||||
|
"PartName": [
|
||||||
|
"planet_gear002",
|
||||||
|
"planet_gear005",
|
||||||
|
"planet_gear004",
|
||||||
|
"planet_gear003",
|
||||||
|
"planet_gear",
|
||||||
|
"output_shaft",
|
||||||
|
"sol_gear"
|
||||||
|
],
|
||||||
|
"MaxClearance": 1.0
|
||||||
|
}
|
||||||
|
]
|
Before Width: | Height: | Size: 650 KiB |
Before Width: | Height: | Size: 658 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 728 KiB |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 414 KiB |