File size: 3,928 Bytes
ce82348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# src/layout_generator/exporter.py

"""

Шаг пайплайна для экспорта 3D-сцены и Draco-компрессии.

"""
import logging
import os
import subprocess
from pathlib import Path

from .base import BaseStep, LayoutContext

logger: logging.Logger = logging.getLogger(__name__)

class ExportAndCompressStep(BaseStep):
    """Сохраняет сцену на жесткий диск и сжимает для веб-рендера."""
    
    def process(self, context: LayoutContext) -> LayoutContext:
        if not context.final_scene:
            return context
            
        project_dir_path: Path = Path(context.project_dir)
        data_dir: Path = project_dir_path / "data"
        data_dir.mkdir(parents=True, exist_ok=True)

        output_filename: str = str(data_dir / f"inventory_layout_{context.size_n}x{context.size_m}.glb")
        uncompressed_filename: str = str(data_dir / f"uncompressed_layout_{context.size_n}x{context.size_m}_{os.getpid()}.glb")
        
        context.final_scene.export(uncompressed_filename)

        logger.info("🗜️ Сжимаем геометрию с помощью Draco (gltf-pipeline)...")
        try:
            subprocess.run(
                ["gltf-pipeline", "-i", uncompressed_filename, "-o", output_filename, "-d"],
                check=True,
                capture_output=True
            )
            old_size: float = os.path.getsize(uncompressed_filename) / (1024 * 1024)
            new_size: float = os.path.getsize(output_filename) / (1024 * 1024)
            logger.info(f"✅ Draco-компрессия завершена! Размер уменьшен с {old_size:.1f} MB до {new_size:.1f} MB.")
            
            if os.path.exists(uncompressed_filename):
                os.remove(uncompressed_filename)
                
        except FileNotFoundError:
            logger.warning("⚠️ Утилита gltf-pipeline не найдена. Сохраняем тяжелый несжатый файл.")
            os.rename(uncompressed_filename, output_filename)
        except Exception as e:
            logger.warning(f"⚠️ Ошибка при сжатии Draco ({e}). Сохраняем несжатый файл как запасной вариант.")
            if os.path.exists(output_filename):
                os.remove(output_filename)
            os.rename(uncompressed_filename, output_filename)

        context.output_filename = output_filename
        
        # --- Генерация финального лог-отчета ---
        failed_count: int = len(context.tracker.failed_items) if context.tracker else 0
        
        # Безопасный подсчет запланированных товаров (устойчив к изменениям формата отчета)
        scheduled_total: int = 0
        for profile in context.report_matrix.values():
            if isinstance(profile, dict):
                scheduled_total += profile.get("total", 0)
            elif isinstance(profile, int):
                scheduled_total += profile

        logger.info("=" * 60)
        logger.info("📋 ОТЧЕТ: АНАЛИЗ ПЛАНОГРАММЫ И ВМЕСТИМОСТИ")
        logger.info(f"Помещение: {context.size_n}x{context.size_m}. Успешно построено стеллажей: {context.actual_shelf_idx} шт.")
        logger.info(f"Вместилось на полки (запланировано): {scheduled_total} SKU")
        logger.info(f"Физически размещено без коллизий: {scheduled_total - failed_count} SKU")
        if failed_count > 0:
            logger.warning(f"⚠️ Упало с полок (ошибка физики движка): {failed_count} SKU")
        logger.info("=" * 60)

        return context