Compare commits

...

3 Commits

Author SHA1 Message Date
ksenia_mikhailova e945770885 use property keeper 2025-06-30 11:31:04 +03:00
ksenia_mikhailova 6d37e2d04e collect data 2025-06-30 11:15:19 +03:00
ksenia_mikhailova 9b880eb91f weld test 2025-06-27 17:02:17 +03:00
2 changed files with 43147 additions and 103 deletions

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,9 @@ import re
from collections import defaultdict
from win32com.client import Dispatch, gencache
from PIL import Image, ImageDraw, ImageFont
import xmltodict
import pythoncom
from win32com.client import Dispatch, VARIANT
from enums import KompasCommand
from logger import logger
@ -389,7 +392,7 @@ class KompasDocumentParser:
for j in range(doc_parts.Count):
element = doc_parts.Part(j)
# if element.Parts.Count == 0:
# elements.append(element)
# elements.append(element)
elements.append(element)
bends += self._collect_bends_from_element(element)
result = self._traverse_parts_recursive(element)
@ -404,7 +407,7 @@ class KompasDocumentParser:
def _build_statistics_data(self, elements, welding):
stats = {"Name": {}, "Material": {}, "Area": {}}
item_template = lambda: {"quantity": 0, "area": 0, "mass":0, "material": ""}
item_template = lambda: {"quantity": 0, "area": 0, "mass": 0, "material": ""}
detail_stats = {
"standard": defaultdict(item_template),
"custom": defaultdict(item_template),
@ -420,7 +423,7 @@ class KompasDocumentParser:
mass_inertial_params = self.api7_module.IMassInertiaParam7(e)
area = mass_inertial_params.Area * 0.0001
mass = round(getattr(e, 'Mass'), 3)
mass = round(getattr(e, "Mass"), 3)
quantity = 1
detail_stats[type_key][element_key]["material"] = material
@ -466,10 +469,9 @@ class KompasDocumentParser:
return stats
def extract_section(name):
"""Извлечение сечения из имени профиля"""
match = re.search(r'(\d+)х(\d+)(?:х(\d+))?', name)
match = re.search(r"(\d+)х(\d+)(?:х(\d+))?", name)
if match:
w, h, t = match.groups()
return f"{w}x{h}" + (f", толщ. {t}" if t else "")
@ -481,19 +483,28 @@ class KompasDocumentParser:
cls_name = str(type(obj))
# Гиб листовой детали
if isinstance(obj, (self.api7_module.ISheetMetalBend, self.api7_module.ISheetMetalLineBend)):
if isinstance(
obj,
(self.api7_module.ISheetMetalBend, self.api7_module.ISheetMetalLineBend),
):
return "Гиб"
# Сварной шов
elif "IUserDesignationCompObj" in cls_name or ("IUserObject3D" in cls_name and "свар" in name_lower):
elif "IUserDesignationCompObj" in cls_name or (
"IUserObject3D" in cls_name and "свар" in name_lower
):
return "Сварной шов"
# Стандартное изделие
elif "IMacroObject3D" in cls_name or any(kw in name_lower for kw in ["болт", "гайка", "шайба"]):
elif "IMacroObject3D" in cls_name or any(
kw in name_lower for kw in ["болт", "гайка", "шайба"]
):
return "Стандартное изделие"
# Профильная труба
elif "IUserObject3D" in cls_name and any(kw in name_lower for kw in ["труба", "профиль"]):
elif "IUserObject3D" in cls_name and any(
kw in name_lower for kw in ["труба", "профиль"]
):
return "Профильная труба"
# Листовая оболочка
@ -505,102 +516,139 @@ class KompasDocumentParser:
return "Кастомная деталь"
# Вспомогательные объекты
elif any(kw in cls_name for kw in ["IPlane3D", "IAxis3D", "IModelObject", "IMateConstraint3D", "ISketch"]):
elif any(
kw in cls_name
for kw in [
"IPlane3D",
"IAxis3D",
"IModelObject",
"IMateConstraint3D",
"ISketch",
]
):
return "Вспомогательный объект"
else:
return "Неизвестный тип"
def traverse_features(self, feature, level=0):
"""
Рекурсивный обход дерева построения модели.
Возвращает:
result_all: список всех элементов
classified_data: словарь с категориями
"""
try:
sub_features = self.api7_module.IFeature7(feature).SubFeatures(1, True, False)
if not sub_features:
return [], {
"bends": [],
"welding": [],
"standard_items": [],
"profile_tubes": [],
"sheet_metal_parts": [],
"custom_parts": [],
"support_objects": []
}
def merge_results(self, target, source):
for category in ["Standard", "Sheet", "Pipe"]:
for key, items in source.get(category, {}).items():
target[category].setdefault(key, []).extend(items)
return target
result_all = []
bends = []
welding = []
standard_items = []
profile_tubes = []
sheet_metal_parts = []
custom_parts = []
support_objects = []
def traverse_ipart(self, element, depth=0, viewed=None, result=None):
if viewed is None:
viewed = set()
for item in sub_features:
name = getattr(item, "Name", "---")
ftype = str(type(item))
indent = " " * level
obj_id = id(element)
if obj_id in viewed:
return
viewed.add(obj_id)
classification = self.classify_item(name, item)
if result is None:
result = {"Standard": {}, "Sheet": {}, "Pipe": {}, "Weld": {}}
# print(f"{indent}- {name} | {ftype} → [{classification}]")
property_manager = self.api7_module.IPropertyMng(self.application)
# Добавляем в общий список
result_all.append((name, ftype))
parts_collection = self.api7_module.IPart7(element).Parts
if not isinstance(parts_collection, tuple):
parts_collection = (parts_collection,)
# Классифицируем элемент
if classification == "Гиб":
bends.append({"name": name, "type": ftype})
elif classification == "Сварной шов":
welding.append({"name": name, "type": ftype})
elif classification == "Стандартное изделие":
standard_items.append({"name": name, "type": ftype})
elif classification == "Профильная труба":
profile_tubes.append({"name": name, "type": ftype, "section": extract_section(name)})
elif classification == "Листовая деталь":
sheet_metal_parts.append({"name": name, "type": ftype})
elif classification == "Кастомная деталь":
custom_parts.append({"name": name, "type": ftype})
elif classification == "Вспомогательный объект":
support_objects.append({"name": name, "type": ftype})
sub_features = self.api7_module.IFeature7(element).SubFeatures(1, True, True)
if not isinstance(sub_features, tuple):
sub_features = (sub_features,)
# Рекурсивно обходим дочерние элементы
child_all, child_data = self.traverse_features(item, level + 1)
bodies = self.api7_module.IFeature7(element).ResultBodies
if not isinstance(bodies, tuple):
bodies = (bodies,)
result_all.extend(child_all)
bends.extend(child_data.get("bends", []))
welding.extend(child_data.get("welding", []))
standard_items.extend(child_data.get("standard_items", []))
profile_tubes.extend(child_data.get("profile_tubes", []))
sheet_metal_parts.extend(child_data.get("sheet_metal_parts", []))
custom_parts.extend(child_data.get("custom_parts", []))
support_objects.extend(child_data.get("support_objects", []))
for f in parts_collection + sub_features + bodies:
print(f"Обрабатываем: {type(f)} - {getattr(f, 'Name', 'Без имени')}")
if getattr(f, "Standard", False):
key = (f.Name, getattr(f, "Material", "Без материала"))
result["Standard"].setdefault(key, []).append(f.Name)
return result_all, {
"bends": bends,
"welding": welding,
"standard_items": standard_items,
"profile_tubes": profile_tubes,
"sheet_metal_parts": sheet_metal_parts,
"custom_parts": custom_parts,
"support_objects": support_objects
}
elif getattr(f, "FileName", None):
param = f.GetOpenDocumentParam()
param.Visible = True
param.ReadOnly = True
new_doc3d = f.OpenSourceDocument(param)
new_doc = self.api7_module.IKompasDocument(new_doc3d)
new_doc.Active = True
new_doc3d = self.api7_module.IKompasDocument3D(new_doc)
new_result = self.traverse_ipart(new_doc3d.TopPart)
except Exception as ex:
logger.error(f"Ошибка при обходе SubFeatures: {ex}", exc_info=True)
return [], {
"bends": [],
"welding": [],
"standard_items": [],
"profile_tubes": [],
"sheet_metal_parts": [],
"custom_parts": [],
"support_objects": []
}
self.merge_results(result, new_result)
new_doc.Close(self.constants.kdDoNotSaveChanges)
else:
if isinstance(f, self.api7_module.IBody7):
# Получаем интерфейс IPropertyKeeper для текущего тела
property_keeper = self.api7_module.IPropertyKeeper(f)
# Получаем свойства "Длина профиля" и "Сечение"
prop_length = property_manager.GetProperty(
self.application.ActiveDocument, "Длина профиля"
)
prop_section = property_manager.GetProperty(
self.application.ActiveDocument, "Сечение"
)
# Извлекаем значения свойств
success_length, value_length, _ = property_keeper.GetPropertyValue(
prop_length, "", True, True
)
success_section, value_section, _ = property_keeper.GetPropertyValue(
prop_section, "", True, True
)
# Если оба свойства успешно извлечены — сохраняем в результат
if success_section and success_length:
result["Pipe"].setdefault(value_section, []).append(value_length)
if isinstance(f, self.api7_module.IUserDesignationCompObj):
weldes = self.api7_module.IFeature7(f).SubFeatures(1, True, True)
user_params = self.api7_module.IUserParameters(f)
lib_filename = user_params.LibraryName
if lib_filename == "Welding3D":
prop = property_manager.GetProperty(
self.application.ActiveDocument, "Длина сварного шва"
)
property_manager = self.api7_module.IPropertyMng(
self.application
)
for w in weldes:
property_keeper = self.api7_module.IPropertyKeeper(w)
res, value, from_source = property_keeper.GetPropertyValue(
prop, "", True, True
)
if res:
key = w.Name
value = value
result["Weld"].setdefault(key, []).append(value)
if isinstance(f, self.api7_module.ISheetMetalRuledShell):
f = self.api7_module.ISheetMetalBody(f)
sheets = self.api7_module.IFeature7(f).SubFeatures(1, True, True)
key = (getattr(f, "Thickness", None), getattr(f, "Radius", None))
for s in sheets:
area = self.api7_module.IMassInertiaParam7(s.Part).Area * 0.001
value = (area)
result["Sheet"].setdefault(key, []).append(value)
if isinstance(
f,
(
self.api7_module.IPart7,
self.api7_module.IBody7,
self.api7_module.IFeature7,
),
):
self.traverse_ipart(f, depth + 1, viewed, result)
return result
def collect_statistics(self):
"""Сбор статистики по элементам, гибам и сваркам в активном документе"""
@ -646,15 +694,9 @@ class KompasDocumentParser:
bends += self._collect_bends_from_element(top_part)
welding += self._collect_welds_from_drawing(top_part)
result_all, categorized = self.traverse_features(top_part)
# Пример вывода
print(f"Гибы: {len(categorized['bends'])}")
print(f"Сварные швы: {len(categorized['welding'])}")
print(f"Стандартные изделия: {len(categorized['standard_items'])}")
print(f"Профильные трубы: {len(categorized['profile_tubes'])}")
print(f"Листовые детали: {len(categorized['sheet_metal_parts'])}")
print(f"Кастомные детали: {len(categorized['custom_parts'])}")
res = self.traverse_ipart(top_part)
print("@@@")
print(res)
logger.info(
f"Найдено:\n Элементов {len(elements)}\n Гибов {len(bends)}\n"
)