Compare commits

..

No commits in common. "e945770885a21bb0dc209291ab29c197baa32c21" and "f36b06875ca8a27d318adfc6968e2493c9a92b26" have entirely different histories.

2 changed files with 103 additions and 43147 deletions

File diff suppressed because it is too large Load Diff

View File

@ -5,9 +5,6 @@ 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
@ -392,8 +389,8 @@ 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)
elements.append(element)
bends += self._collect_bends_from_element(element)
result = self._traverse_parts_recursive(element)
elements += result["elements"]
@ -407,7 +404,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),
@ -423,7 +420,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
@ -468,10 +465,11 @@ class KompasDocumentParser:
stats["Welding"]["Total"] = round(total_length, 2)
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 "")
@ -483,28 +481,19 @@ 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 "Профильная труба"
# Листовая оболочка
@ -516,140 +505,103 @@ 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 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
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 traverse_ipart(self, element, depth=0, viewed=None, result=None):
if viewed is None:
viewed = set()
result_all = []
bends = []
welding = []
standard_items = []
profile_tubes = []
sheet_metal_parts = []
custom_parts = []
support_objects = []
obj_id = id(element)
if obj_id in viewed:
return
viewed.add(obj_id)
for item in sub_features:
name = getattr(item, "Name", "---")
ftype = str(type(item))
indent = " " * level
if result is None:
result = {"Standard": {}, "Sheet": {}, "Pipe": {}, "Weld": {}}
classification = self.classify_item(name, item)
property_manager = self.api7_module.IPropertyMng(self.application)
# print(f"{indent}- {name} | {ftype} → [{classification}]")
parts_collection = self.api7_module.IPart7(element).Parts
if not isinstance(parts_collection, tuple):
parts_collection = (parts_collection,)
# Добавляем в общий список
result_all.append((name, ftype))
sub_features = self.api7_module.IFeature7(element).SubFeatures(1, True, True)
if not isinstance(sub_features, tuple):
sub_features = (sub_features,)
# Классифицируем элемент
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})
bodies = self.api7_module.IFeature7(element).ResultBodies
if not isinstance(bodies, tuple):
bodies = (bodies,)
# Рекурсивно обходим дочерние элементы
child_all, child_data = self.traverse_features(item, level + 1)
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)
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", []))
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)
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
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
}
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": []
}
def collect_statistics(self):
"""Сбор статистики по элементам, гибам и сваркам в активном документе"""
logger.info("Начинаем сбор статистики по элементам...")
@ -693,10 +645,16 @@ class KompasDocumentParser:
elements += [top_part]
bends += self._collect_bends_from_element(top_part)
welding += self._collect_welds_from_drawing(top_part)
res = self.traverse_ipart(top_part)
print("@@@")
print(res)
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'])}")
logger.info(
f"Найдено:\n Элементов {len(elements)}\n Гибов {len(bends)}\n"
)