logic
This commit is contained in:
parent
875103603c
commit
25ee7e317d
47
bridge.py
47
bridge.py
|
@ -6,12 +6,21 @@ from PyQt6.QtWebChannel import QWebChannel
|
||||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||||
|
|
||||||
from enums import KompasCommand
|
from enums import KompasCommand
|
||||||
|
from logger import logger
|
||||||
from parser.main import KompasDocumentParser
|
from parser.main import KompasDocumentParser
|
||||||
|
|
||||||
|
|
||||||
class PythonEventEmitter(QObject):
|
class PythonEventEmitter(QObject):
|
||||||
onEvent = pyqtSignal(str, "QVariantMap") # событие: тип + данные
|
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):
|
class PythonJSBridge(QObject):
|
||||||
def __init__(self, browser: QWebEngineView):
|
def __init__(self, browser: QWebEngineView):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -29,21 +38,29 @@ class PythonJSBridge(QObject):
|
||||||
|
|
||||||
@pyqtSlot(str, str, result=str)
|
@pyqtSlot(str, str, result=str)
|
||||||
def callFromJS(self, command: str, data_json: str):
|
def callFromJS(self, command: str, data_json: str):
|
||||||
print(f"[Python] Получена команда: {command}")
|
logger.info(f"[Python] Получена команда: {command}")
|
||||||
data = None
|
|
||||||
if command == KompasCommand.OPEN_KOMPAS:
|
try:
|
||||||
data = self.kompas.get_open_documents()
|
if command == KompasCommand.OPEN_KOMPAS:
|
||||||
if command == KompasCommand.GET_AVAILABLE_ACTIONS:
|
data = self.kompas.get_open_documents()
|
||||||
data = self.kompas.get_available_actions()
|
elif command == KompasCommand.GET_AVAILABLE_ACTIONS:
|
||||||
if command == KompasCommand.IGES:
|
data = self.kompas.get_available_actions()
|
||||||
data = self.kompas.save_to_iges()
|
elif command == KompasCommand.IGES:
|
||||||
if command == KompasCommand.EXPORT_RASTER:
|
data = self.kompas.save_to_iges() # <-- твой метод
|
||||||
data = self.kompas.export_to_raster()
|
elif command == KompasCommand.EXPORT_RASTER:
|
||||||
if command == KompasCommand.PROJECT_SUPPORT:
|
data = self.kompas.export_to_raster()
|
||||||
data = self.kompas.create_drawing_for_parts()
|
elif command == KompasCommand.PROJECT_SUPPORT:
|
||||||
if command == KompasCommand.STATS:
|
data = self.kompas.create_drawing_for_parts()
|
||||||
data = self.kompas.collect_statistics()
|
elif command == KompasCommand.STATS:
|
||||||
return json.dumps(data, ensure_ascii=False)
|
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):
|
def send_event_to_js(self, event_type: str, data: dict):
|
||||||
self.emitter.onEvent.emit(event_type, data)
|
self.emitter.onEvent.emit(event_type, data)
|
|
@ -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)
|
|
@ -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
|
124
parser/main.py
124
parser/main.py
|
@ -1,10 +1,11 @@
|
||||||
import os
|
import os
|
||||||
import traceback
|
import traceback
|
||||||
import pythoncom
|
import time
|
||||||
from win32com.client import Dispatch, gencache
|
from win32com.client import Dispatch, gencache
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
|
||||||
from enums import KompasCommand
|
from enums import KompasCommand
|
||||||
|
from logger import logger
|
||||||
|
|
||||||
ids = {
|
ids = {
|
||||||
"api_5": "{0422828C-F174-495E-AC5D-D31014DBBE87}",
|
"api_5": "{0422828C-F174-495E-AC5D-D31014DBBE87}",
|
||||||
|
@ -58,6 +59,23 @@ class KompasDocumentParser:
|
||||||
traceback.print_exc() # <-- Выводит номер строки, файл и стек
|
traceback.print_exc() # <-- Выводит номер строки, файл и стек
|
||||||
raise RuntimeError(f"Ошибка при подключении к КОМПАС: {e}")
|
raise RuntimeError(f"Ошибка при подключении к КОМПАС: {e}")
|
||||||
|
|
||||||
|
def get_export_path(self, doc_path: str, doc_name: str, ext: str) -> str:
|
||||||
|
"""
|
||||||
|
Формирует путь для экспорта файла по шаблону:
|
||||||
|
- Создаёт подпапку `ext` внутри `doc_path`
|
||||||
|
- Возвращает полный путь к файлу `<doc_name>.<ext>`
|
||||||
|
"""
|
||||||
|
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):
|
def get_open_documents(self):
|
||||||
"""Возвращает список информации о всех открытых документах"""
|
"""Возвращает список информации о всех открытых документах"""
|
||||||
|
|
||||||
|
@ -154,7 +172,9 @@ class KompasDocumentParser:
|
||||||
print(f"Создаём чертёж для: {component.Name}")
|
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_layout = c_doc.LayoutSheets
|
||||||
c_sheet = c_layout.Item(0)
|
c_sheet = c_layout.Item(0)
|
||||||
c_sheet.Format.Format = self.constants.ksFormatA3
|
c_sheet.Format.Format = self.constants.ksFormatA3
|
||||||
|
@ -190,7 +210,6 @@ class KompasDocumentParser:
|
||||||
20,
|
20,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
filename = "_".join(
|
filename = "_".join(
|
||||||
filter(
|
filter(
|
||||||
None,
|
None,
|
||||||
|
@ -221,9 +240,12 @@ class KompasDocumentParser:
|
||||||
|
|
||||||
def save_to_iges(self):
|
def save_to_iges(self):
|
||||||
"""Сохраняет открытые 3D-документы (детали/сборки) в формате IGES"""
|
"""Сохраняет открытые 3D-документы (детали/сборки) в формате IGES"""
|
||||||
print("Начинаем сохранение документов в формате IGES...")
|
logger.info("Начинаем сохранение документов в формате IGES...")
|
||||||
|
|
||||||
|
result = {"result": []}
|
||||||
av_actions = self.get_available_actions()
|
av_actions = self.get_available_actions()
|
||||||
docs_collection = self.application.Documents
|
docs_collection = self.application.Documents
|
||||||
|
allowed_types = av_actions[KompasCommand.IGES]["allowed_types"]
|
||||||
|
|
||||||
for i in range(docs_collection.Count):
|
for i in range(docs_collection.Count):
|
||||||
try:
|
try:
|
||||||
|
@ -232,13 +254,13 @@ class KompasDocumentParser:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
doc_type = doc.DocumentType
|
doc_type = doc.DocumentType
|
||||||
if doc_type not in av_actions[KompasCommand.IGES]["allowed_types"]:
|
if doc_type not in allowed_types:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
doc.Active = True
|
doc.Active = True
|
||||||
doc_path = doc.Path
|
doc_path = doc.Path
|
||||||
doc_name = "-".join(doc.Name.split(".")[:-1])
|
doc_name = "-".join(doc.Name.split(".")[:-1])
|
||||||
print(f"Попытка сохранить: {doc_name}")
|
logger.info(f"Попытка сохранить: {doc_name}")
|
||||||
|
|
||||||
# Получаем 3D-документ через API v5
|
# Получаем 3D-документ через API v5
|
||||||
doc_api5 = self.api5.ActiveDocument3D()
|
doc_api5 = self.api5.ActiveDocument3D()
|
||||||
|
@ -252,25 +274,39 @@ class KompasDocumentParser:
|
||||||
save_params.Init()
|
save_params.Init()
|
||||||
save_params.format = self.constants.ksConverterToIGES
|
save_params.format = self.constants.ksConverterToIGES
|
||||||
|
|
||||||
ext = "igs"
|
full_path = self.prepare_export_path(doc_path, doc_name, "igs")
|
||||||
output_dir = os.path.join(doc_path, ext)
|
export_success = doc_api5.SaveAsToAdditionFormat(full_path, save_params)
|
||||||
filename = f"{doc_name}.{ext}"
|
# Добавляем информацию о результате в массив
|
||||||
full_path = os.path.join(output_dir, filename)
|
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 export_success:
|
||||||
if not os.path.exists(output_dir):
|
logger.error(f"Не удалось сохранить: {full_path}")
|
||||||
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}")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")
|
logger.error(
|
||||||
traceback.print_exc()
|
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):
|
def collect_statistics(self):
|
||||||
"""Сбор статистики по элементам, гибам и сваркам в активном документе"""
|
"""Сбор статистики по элементам, гибам и сваркам в активном документе"""
|
||||||
|
@ -329,10 +365,12 @@ class KompasDocumentParser:
|
||||||
macro = self.api7_module.IMacroObject3D(drawing_context)
|
macro = self.api7_module.IMacroObject3D(drawing_context)
|
||||||
sub_features = macro.Owner.SubFeatures(1, True, False) or []
|
sub_features = macro.Owner.SubFeatures(1, True, False) or []
|
||||||
for item in sub_features:
|
for item in sub_features:
|
||||||
if isinstance(item, self.api7_module.IUserDesignationCompObj):
|
if isinstance(
|
||||||
|
item, self.api7_module.IUserDesignationCompObj
|
||||||
|
):
|
||||||
welding.append(item)
|
welding.append(item)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Ошибка в DrawingContext:', e)
|
print("Ошибка в DrawingContext:", e)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
doc_parts = self.api7_module.IParts7(part.Parts)
|
doc_parts = self.api7_module.IParts7(part.Parts)
|
||||||
|
@ -343,7 +381,7 @@ class KompasDocumentParser:
|
||||||
look_features(element)
|
look_features(element)
|
||||||
find_el(element)
|
find_el(element)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print('Ошибка в Parts:', e)
|
print("Ошибка в Parts:", e)
|
||||||
|
|
||||||
if doc_type == self.constants.ksDocumentAssembly:
|
if doc_type == self.constants.ksDocumentAssembly:
|
||||||
find_el(top_part)
|
find_el(top_part)
|
||||||
|
@ -362,43 +400,55 @@ class KompasDocumentParser:
|
||||||
|
|
||||||
for e in elements:
|
for e in elements:
|
||||||
for key in sorted_stats.keys():
|
for key in sorted_stats.keys():
|
||||||
if key == 'Name':
|
if key == "Name":
|
||||||
value = f"{getattr(e, key)}, масса {round(getattr(e, 'Mass'), 3)}"
|
value = f"{getattr(e, key)}, масса {round(getattr(e, 'Mass'), 3)}"
|
||||||
sorted_stats[key][value] = sorted_stats[key].get(value, 0) + 1
|
sorted_stats[key][value] = (
|
||||||
elif key == 'Area':
|
sorted_stats[key].get(value, 0) + 1
|
||||||
mass_inertial_params = self.api7_module.IMassInertiaParam7(e)
|
)
|
||||||
|
elif key == "Area":
|
||||||
|
mass_inertial_params = self.api7_module.IMassInertiaParam7(
|
||||||
|
e
|
||||||
|
)
|
||||||
area = mass_inertial_params.Area * 0.0001 # м²
|
area = mass_inertial_params.Area * 0.0001 # м²
|
||||||
material = getattr(e, 'Material', 'Неизвестно')
|
material = getattr(e, "Material", "Неизвестно")
|
||||||
key_area = f"Площадь {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:
|
else:
|
||||||
value = getattr(e, key)
|
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"
|
welding_key = "Welding"
|
||||||
for w in welding:
|
for w in welding:
|
||||||
if welding_key not in sorted_stats:
|
if welding_key not in sorted_stats:
|
||||||
sorted_stats[welding_key] = {}
|
sorted_stats[welding_key] = {}
|
||||||
w_name_split = w.Name.split('-')
|
w_name_split = w.Name.split("-")
|
||||||
w_len = w_name_split[-1].split('@')[0]
|
w_len = w_name_split[-1].split("@")[0]
|
||||||
sorted_stats[welding_key][w.Name] = w_len
|
sorted_stats[welding_key][w.Name] = w_len
|
||||||
|
|
||||||
if welding_key in sorted_stats:
|
if welding_key in sorted_stats:
|
||||||
|
|
||||||
def float_f(n):
|
def float_f(n):
|
||||||
try:
|
try:
|
||||||
return float(n)
|
return float(n)
|
||||||
except:
|
except:
|
||||||
return 0
|
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
|
sorted_stats[welding_key]["Total"] = total_length
|
||||||
|
|
||||||
for section in sorted_stats:
|
for section in sorted_stats:
|
||||||
print(section)
|
print(section)
|
||||||
for name, value in sorted_stats[section].items():
|
for name, value in sorted_stats[section].items():
|
||||||
print(f"{name} -- {value}")
|
print(f"{name} -- {value}")
|
||||||
print('-----')
|
print("-----")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")
|
print(f"[ERROR] Ошибка при обработке документа #{i + 1}: {e}")
|
||||||
|
|
Loading…
Reference in New Issue