From 25ee7e317d0929a3e4b3f44e1a09c94a84b0316d Mon Sep 17 00:00:00 2001 From: ksenia_mikhailova Date: Tue, 24 Jun 2025 17:04:22 +0300 Subject: [PATCH] logic --- bridge.py | 53 ++++++++++++------- logger.py | 29 +++++++++++ logs/parser.log | 24 +++++++++ parser/main.py | 136 +++++++++++++++++++++++++++++++++--------------- 4 files changed, 181 insertions(+), 61 deletions(-) create mode 100644 logger.py create mode 100644 logs/parser.log diff --git a/bridge.py b/bridge.py index 0facb36..4cf481b 100644 --- a/bridge.py +++ b/bridge.py @@ -6,12 +6,21 @@ from PyQt6.QtWebChannel import QWebChannel from PyQt6.QtWebEngineWidgets import QWebEngineView from enums import KompasCommand +from logger import logger from parser.main import KompasDocumentParser + class PythonEventEmitter(QObject): onEvent = pyqtSignal(str, "QVariantMap") # событие: тип + данные +def format_response(success: bool, data=None, error: str = None): + return json.dumps( + {"status": "success" if success else "error", "data": data, "error": error}, + ensure_ascii=False, + ) + + class PythonJSBridge(QObject): def __init__(self, browser: QWebEngineView): super().__init__() @@ -24,26 +33,34 @@ class PythonJSBridge(QObject): self.channel.registerObject("pyjs", self) self.channel.registerObject("pyjs_events", self.emitter) - + self.kompas = KompasDocumentParser() @pyqtSlot(str, str, result=str) def callFromJS(self, command: str, data_json: str): - print(f"[Python] Получена команда: {command}") - data = None - if command == KompasCommand.OPEN_KOMPAS: - data = self.kompas.get_open_documents() - if command == KompasCommand.GET_AVAILABLE_ACTIONS: - data = self.kompas.get_available_actions() - if command == KompasCommand.IGES: - data = self.kompas.save_to_iges() - if command == KompasCommand.EXPORT_RASTER: - data = self.kompas.export_to_raster() - if command == KompasCommand.PROJECT_SUPPORT: - data = self.kompas.create_drawing_for_parts() - if command == KompasCommand.STATS: - data = self.kompas.collect_statistics() - return json.dumps(data, ensure_ascii=False) - + logger.info(f"[Python] Получена команда: {command}") + + try: + if command == KompasCommand.OPEN_KOMPAS: + data = self.kompas.get_open_documents() + elif command == KompasCommand.GET_AVAILABLE_ACTIONS: + data = self.kompas.get_available_actions() + elif command == KompasCommand.IGES: + data = self.kompas.save_to_iges() # <-- твой метод + elif command == KompasCommand.EXPORT_RASTER: + data = self.kompas.export_to_raster() + elif command == KompasCommand.PROJECT_SUPPORT: + data = self.kompas.create_drawing_for_parts() + elif command == KompasCommand.STATS: + data = self.kompas.collect_statistics() + else: + return format_response(False, error=f"Неизвестная команда: {command}") + + return format_response(True, data=data) + + except Exception as e: + logger.error(f"Ошибка при выполнении команды {command}: {e}", exc_info=True) + return format_response(False, error=str(e)) + def send_event_to_js(self, event_type: str, data: dict): - self.emitter.onEvent.emit(event_type, data) \ No newline at end of file + self.emitter.onEvent.emit(event_type, data) diff --git a/logger.py b/logger.py new file mode 100644 index 0000000..cccbbb3 --- /dev/null +++ b/logger.py @@ -0,0 +1,29 @@ +# logger.py +import logging +import os + +# Создаем корневой логгер +logger = logging.getLogger('parser') +logger.setLevel(logging.DEBUG) # Уровень по умолчанию: DEBUG и выше + +# Проверяем, существует ли папка logs +logs_dir = "logs" +if not os.path.exists(logs_dir): + os.makedirs(logs_dir) + +# Форматтер для записей в логе +formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(funcName)s - %(lineno)d - %(message)s' +) + +# Обработчик для вывода в консоль +console_handler = logging.StreamHandler() +console_handler.setLevel(logging.INFO) +console_handler.setFormatter(formatter) +logger.addHandler(console_handler) + +# Обработчик для записи в файл +file_handler = logging.FileHandler(os.path.join(logs_dir, 'parser.log'), encoding='utf-8') +file_handler.setLevel(logging.DEBUG) +file_handler.setFormatter(formatter) +logger.addHandler(file_handler) \ No newline at end of file diff --git a/logs/parser.log b/logs/parser.log new file mode 100644 index 0000000..51299f7 --- /dev/null +++ b/logs/parser.log @@ -0,0 +1,24 @@ +2025-06-24 16:48:58,547 - parser - INFO - main - save_to_iges - 244 - Начинаем сохранение документов в формате IGES... +2025-06-24 16:48:59,394 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Проф труба +2025-06-24 16:49:00,892 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Сборка изделий +2025-06-24 16:49:02,333 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Список деталей +2025-06-24 16:49:03,686 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Nema 17 +2025-06-24 16:49:04,310 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Квадрат +2025-06-24 16:49:04,842 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Цилиндр +2025-06-24 16:49:48,697 - parser - INFO - main - save_to_iges - 244 - Начинаем сохранение документов в формате IGES... +2025-06-24 16:49:49,345 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Проф труба +2025-06-24 16:49:49,600 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Проф труба.igs +2025-06-24 16:49:50,167 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Сборка изделий +2025-06-24 16:49:50,554 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Сборка изделий.igs +2025-06-24 16:49:50,947 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Список деталей +2025-06-24 16:49:51,482 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Список деталей.igs +2025-06-24 16:49:52,346 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Nema 17 +2025-06-24 16:49:52,485 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Nema 17.igs +2025-06-24 16:49:52,986 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Квадрат +2025-06-24 16:49:53,124 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Квадрат.igs +2025-06-24 16:49:53,781 - parser - INFO - main - save_to_iges - 264 - Попытка сохранить: Цилиндр +2025-06-24 16:49:53,923 - parser - INFO - main - save_to_iges - 281 - Успешно сохранено: C:\Users\user\Desktop\ЗН 999 test\3D Модель\igs\Цилиндр.igs +2025-06-24 17:00:05,175 - parser - INFO - bridge - callFromJS - 41 - [Python] Получена команда: open_kompas +2025-06-24 17:00:09,976 - parser - INFO - bridge - callFromJS - 41 - [Python] Получена команда: open_kompas +2025-06-24 17:01:37,671 - parser - INFO - bridge - callFromJS - 41 - [Python] Получена команда: open_kompas +2025-06-24 17:01:40,023 - parser - INFO - bridge - callFromJS - 41 - [Python] Получена команда: open_kompas diff --git a/parser/main.py b/parser/main.py index 617ef8b..fe1bd0e 100644 --- a/parser/main.py +++ b/parser/main.py @@ -1,10 +1,11 @@ import os import traceback -import pythoncom +import time from win32com.client import Dispatch, gencache from PIL import Image, ImageDraw, ImageFont from enums import KompasCommand +from logger import logger ids = { "api_5": "{0422828C-F174-495E-AC5D-D31014DBBE87}", @@ -58,6 +59,23 @@ class KompasDocumentParser: traceback.print_exc() # <-- Выводит номер строки, файл и стек raise RuntimeError(f"Ошибка при подключении к КОМПАС: {e}") + def get_export_path(self, doc_path: str, doc_name: str, ext: str) -> str: + """ + Формирует путь для экспорта файла по шаблону: + - Создаёт подпапку `ext` внутри `doc_path` + - Возвращает полный путь к файлу `.` + """ + output_dir = os.path.join(doc_path, ext) + filename = f"{doc_name}.{ext}" + full_path = os.path.join(output_dir, filename) + + return full_path + + def prepare_export_path(self, doc_path: str, doc_name: str, ext: str) -> str: + full_path = self.get_export_path(doc_path, doc_name, ext) + os.makedirs(os.path.dirname(full_path), exist_ok=True) + return full_path + def get_open_documents(self): """Возвращает список информации о всех открытых документах""" @@ -82,7 +100,7 @@ class KompasDocumentParser: print(f"Ошибка при обработке документа #{i + 1}: {e}") return documents - + def create_drawing_for_parts(self): """ Создание чертежей для всех уникальных деталей из открытых деталей и сборок. @@ -104,13 +122,13 @@ class KompasDocumentParser: continue doc_path = doc.Path - + # Сохранение чертежа output_dir = os.path.join(doc_path, saving_path.strip("/")) if not os.path.exists(output_dir): os.makedirs(output_dir) print(f"Создана папка: {output_dir}") - + doc_type = doc.DocumentType if doc_type not in allowed_types: continue @@ -124,7 +142,7 @@ class KompasDocumentParser: if not os.path.exists(output_dir): os.makedirs(output_dir) print(f"Создана папка: {output_dir}") - + doc_3d = self.api7_module.IKompasDocument3D(doc) doc_components = [] @@ -154,7 +172,9 @@ class KompasDocumentParser: print(f"Создаём чертёж для: {component.Name}") # Создаём новый чертёж - c_doc = self.application.Documents.Add(self.constants.ksDocumentDrawing) + c_doc = self.application.Documents.Add( + self.constants.ksDocumentDrawing + ) c_layout = c_doc.LayoutSheets c_sheet = c_layout.Item(0) c_sheet.Format.Format = self.constants.ksFormatA3 @@ -190,7 +210,6 @@ class KompasDocumentParser: 20, ) - filename = "_".join( filter( None, @@ -221,9 +240,12 @@ class KompasDocumentParser: def save_to_iges(self): """Сохраняет открытые 3D-документы (детали/сборки) в формате IGES""" - print("Начинаем сохранение документов в формате IGES...") + logger.info("Начинаем сохранение документов в формате IGES...") + + result = {"result": []} av_actions = self.get_available_actions() docs_collection = self.application.Documents + allowed_types = av_actions[KompasCommand.IGES]["allowed_types"] for i in range(docs_collection.Count): try: @@ -232,13 +254,13 @@ class KompasDocumentParser: continue doc_type = doc.DocumentType - if doc_type not in av_actions[KompasCommand.IGES]["allowed_types"]: + if doc_type not in allowed_types: continue doc.Active = True doc_path = doc.Path doc_name = "-".join(doc.Name.split(".")[:-1]) - print(f"Попытка сохранить: {doc_name}") + logger.info(f"Попытка сохранить: {doc_name}") # Получаем 3D-документ через API v5 doc_api5 = self.api5.ActiveDocument3D() @@ -252,34 +274,48 @@ class KompasDocumentParser: save_params.Init() save_params.format = self.constants.ksConverterToIGES - ext = "igs" - output_dir = os.path.join(doc_path, ext) - filename = f"{doc_name}.{ext}" - full_path = os.path.join(output_dir, filename) + full_path = self.prepare_export_path(doc_path, doc_name, "igs") + export_success = doc_api5.SaveAsToAdditionFormat(full_path, save_params) + # Добавляем информацию о результате в массив + logger.info(f"Успешно сохранено: {full_path}") + result["result"].append( + { + "file": full_path, + "success": bool(export_success), + "document_name": doc_name, + "document_type": doc_type, + "timestamp": time.time(), + } + ) - # Создаём директорию, если её нет - if not os.path.exists(output_dir): - os.makedirs(output_dir) - - # Сохраняем файл - result = doc_api5.SaveAsToAdditionFormat(full_path, save_params) - if result: - print(f"[OK] Сохранено: {full_path}") - else: - print(f"[ERROR] Не удалось сохранить: {full_path}") + if not export_success: + logger.error(f"Не удалось сохранить: {full_path}") except Exception as e: - print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}") - traceback.print_exc() + logger.error( + f"Ошибка при обработке документа #{i + 1}: {e}", + exc_info=True, + ) + result["result"].append( + { + "file": None, + "success": False, + "error": str(e), + "document_index": i + 1, + "timestamp": time.time(), + } + ) + + return result def collect_statistics(self): """Сбор статистики по элементам, гибам и сваркам в активном документе""" print("Начинаем сбор статистики по элементам...") - + # Получаем доступные типы из разрешённых действий av_actions = self.get_available_actions() allowed_types = av_actions[KompasCommand.STATS]["allowed_types"] - + docs_collection = self.application.Documents for i in range(docs_collection.Count): @@ -329,10 +365,12 @@ class KompasDocumentParser: macro = self.api7_module.IMacroObject3D(drawing_context) sub_features = macro.Owner.SubFeatures(1, True, False) or [] for item in sub_features: - if isinstance(item, self.api7_module.IUserDesignationCompObj): + if isinstance( + item, self.api7_module.IUserDesignationCompObj + ): welding.append(item) except Exception as e: - print('Ошибка в DrawingContext:', e) + print("Ошибка в DrawingContext:", e) try: doc_parts = self.api7_module.IParts7(part.Parts) @@ -343,7 +381,7 @@ class KompasDocumentParser: look_features(element) find_el(element) except Exception as e: - print('Ошибка в Parts:', e) + print("Ошибка в Parts:", e) if doc_type == self.constants.ksDocumentAssembly: find_el(top_part) @@ -362,43 +400,55 @@ class KompasDocumentParser: for e in elements: for key in sorted_stats.keys(): - if key == 'Name': + if key == "Name": value = f"{getattr(e, key)}, масса {round(getattr(e, 'Mass'), 3)}" - sorted_stats[key][value] = sorted_stats[key].get(value, 0) + 1 - elif key == 'Area': - mass_inertial_params = self.api7_module.IMassInertiaParam7(e) + sorted_stats[key][value] = ( + sorted_stats[key].get(value, 0) + 1 + ) + elif key == "Area": + mass_inertial_params = self.api7_module.IMassInertiaParam7( + e + ) area = mass_inertial_params.Area * 0.0001 # м² - material = getattr(e, 'Material', 'Неизвестно') + material = getattr(e, "Material", "Неизвестно") key_area = f"Площадь {material}, м²:" - sorted_stats[key][key_area] = round(sorted_stats[key].get(key_area, 0) + area, 6) + sorted_stats[key][key_area] = round( + sorted_stats[key].get(key_area, 0) + area, 6 + ) else: value = getattr(e, key) - sorted_stats[key][value] = sorted_stats[key].get(value, 0) + 1 + sorted_stats[key][value] = ( + sorted_stats[key].get(value, 0) + 1 + ) - sorted_stats['Area']['Total'] = sum(sorted_stats['Area'].values()) + sorted_stats["Area"]["Total"] = sum(sorted_stats["Area"].values()) welding_key = "Welding" for w in welding: if welding_key not in sorted_stats: sorted_stats[welding_key] = {} - w_name_split = w.Name.split('-') - w_len = w_name_split[-1].split('@')[0] + w_name_split = w.Name.split("-") + w_len = w_name_split[-1].split("@")[0] sorted_stats[welding_key][w.Name] = w_len if welding_key in sorted_stats: + def float_f(n): try: return float(n) except: return 0 - total_length = sum(float_f(v) for v in sorted_stats[welding_key].values()) + + total_length = sum( + float_f(v) for v in sorted_stats[welding_key].values() + ) sorted_stats[welding_key]["Total"] = total_length for section in sorted_stats: print(section) for name, value in sorted_stats[section].items(): print(f"{name} -- {value}") - print('-----') + print("-----") except Exception as e: print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")