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