diff --git a/README.md b/README.md new file mode 100644 index 0000000..611cb2d --- /dev/null +++ b/README.md @@ -0,0 +1,198 @@ +# Инструменты СПИС + +Комплект инструментов для работы с внешними ссылками в СПИС. +AutoCAD 2020 и новее. + +--- + +## Состав + +| Файл | Назначение | +|---|---| +| `SPIS_TOOLS.lsp` | Загрузчик. Загружать его — остальные подтянутся сами | +| `SPISSETDIR` (в загрузчике) | Указать папку СПИС вручную, если автопоиск не сработал | +| `SPIS_RUN_SETS.lsp` | `SPISRUNSETS` — собрать комплект дисциплин | +| `SPIS_SYSTEM_SETS.lsp` | `SPISATTACHSETS` — подключить комплект в СПИС | +| `XRBIND.lsp` | Шесть команд обслуживания ссылок | +| `run_sets_launcher.py` | Запуск сборки: bat в нужной кодировке, старт в фоне | +| `build_discipline_sets.py` | Рабочий скрипт сборки комплекта | +| `.venv/` | Python с pywin32 — нужен для работы через COM | + +Все файлы должны лежать **в одной папке**. Загрузчик определяет своё +расположение сам, при переносе папки путь править не нужно. + +--- + +## Требования + +**AutoCAD** запущен — сборка работает через COM. + +**Папка скриптов** определяется автоматически (реестр → пути поддержки → +поиск в доверенных путях). Найденная папка запоминается в реестре +(`HKCU\Software\GENPRO\SPIS`) и добавляется в `TRUSTEDPATHS` — диалог +безопасности не появляется. Если автопоиск не сработал — команда `SPISSETDIR`. + +**Python с pywin32.** В системном PATH может не быть Python, поэтому +рядом лежит собственное окружение `.venv`. Если папку переносят на +другую машину, окружение нужно создать заново: + +``` +python -m venv .venv +.venv\Scripts\python.exe -m pip install pywin32 +``` + +Путь к интерпретатору LISP ищет сам: сначала `.venv\Scripts\python.exe` +рядом со скриптами, затем запасной вариант. Если ни один не найден — +команда сообщит об этом. + +--- + +## Команды + +### Создание комплекта + +**`SPISRUNSETS`** — создаёт файлы дисциплин ЭТР/СС/ВК/ОВ рядом с АР +и подключает в каждый ссылки по схеме. + +Ожидает корневую папку проекта с пятью подпапками: + +``` +Проект\ + АР\ АР К9.dwg основа, ничего не подключает + ЭТР\ ЭТР К9.dwg содержит АР + ВК + ОВ + СС + СС\ СС К9.dwg содержит АР + ВК + ОВ + ЭТР + ВК\ ВК К9.dwg содержит АР + ОВ + СС + ЭТР + ОВ\ ОВ К9.dwg содержит АР + ВК + СС + ЭТР +``` + +Достаточно, чтобы существовал только файл АР — остальные команда создаст. + +> **Почему через Python.** Операции с документами (создать, открыть, +> сохранить) из LISP блокируют AutoCAD: `vla-Open`, `_.OPEN` и `_.SAVEAS` +> внутри LISP вешают сессию — проверено на AutoCAD 2020. Поэтому команда +> запускает внешний скрипт, а AutoCAD остаётся свободен. + +### Сборка СПИС + +**`SPISATTACHSETS`** — подключает в открытый СПИС внешние ссылки на АР +и четыре дисциплины. Показывает список найденных файлов и запрашивает +подтверждение. При ответе, отличном от `Yes`, чертёж не изменяется. + +### Работа со ссылками + +| Команда | Действие | +|---|---| +| `XRLIST` | Список ссылок со статусом. Чертёж не изменяется | +| `XRBIND` | Внедрить все разрешённые ссылки | +| `XRBIND1` | Внедрить одну ссылку по имени | +| `XRPATH` | Сменить путь «переехавшей» ссылки | +| `XRUNBIND` | Отсоединить только битые (неразрешённые) ссылки | +| `XRDETACH` | Отсоединить все ссылки, с подтверждением | + +Все команды работают по **активному** чертежу. Перед `XRBIND` и `XRDETACH` +проверьте, какое окно активно. + +--- + +## Порядок работы + +| Шаг | Действие | +|---|---| +| 1. Собрать комплект | `SPISRUNSETS` — указать корень проекта, ответить `Yes`. Сборка идёт в фоне | +| 2. Дождаться файлов | Следить за `%TEMP%\spis_sets_log.txt` | +| 3. Собрать СПИС | `SPISATTACHSETS` — указать тот же корень, ответить `Yes` | +| 4. Проверить | `XRLIST` — все ссылки должны быть «РАЗРЕШЕНА» | +| 5. Внедрить (если надо) | `XRBIND` — если по заданию ссылки внедряются перед сдачей | + +--- + +## Загрузка + +В командной строке AutoCAD: + +``` +APPLOAD +``` + +Выбрать `SPIS_TOOLS.lsp`, нажать «Загрузить». + +### Автозагрузка при старте + +`APPLOAD` → Содержимое → Добавить → `SPIS_TOOLS.lsp`. + +Либо строка в `acaddoc.lsp`: + +```lisp +(load "G:/Мой диск/lisp-code/SPIS/SPIS_TOOLS.lsp") +``` + +--- + +## Параметры подключения + +| Параметр | Значение | +|---|---| +| Тип ссылки | Overlay (наложение) | +| Точка вставки | 0, 0, 0 | +| Масштаб | 1 : 1 по всем осям | +| Угол поворота | 0° | +| Единицы | миллиметры (INSUNITS = 4) | +| Повторное подключение | ссылка с таким именем пропускается | + +**Почему Overlay, а не Attach.** Дисциплины ссылаются друг на друга, +образуя цикл СС ↔ ВК. При `Attach` циклические ссылки не загружаются, +при `Overlay` — отображаются корректно. + +--- + +## Кодировка файлов + +LISP-файлы хранятся в **Windows-1251**. AutoCAD 2020 не читает UTF-8: +кириллица превращается в мусор, загрузка падает с ошибкой +«команда не найдена». + +**AutoCAD 2026** читает и cp1251, и UTF-8 — проверено живьём (кириллица +печатается верно в обоих вариантах). Для совместимости с 2020 файлы +по-прежнему держим в cp1251; `.lsp.utf8` — версия для правки. + +Рядом с каждым `.lsp` лежит копия `.lsp.utf8` — та же версия для правки. +После правки перекодировать обратно: + +```python +from pathlib import Path +p = Path("SPIS_TOOLS.lsp.utf8") +Path("SPIS_TOOLS.lsp").write_bytes(p.read_text(encoding="utf-8").encode("cp1251")) +``` + +**Отдельно про bat-файл.** Скрипт сборки запускается через `.bat`, который +пишется в **cp866 без `chcp`**. Если добавить `chcp 65001`, русские пути +внутри bat бьются и Python не находит файлы. + +--- + +## Типовые проблемы + +| Симптом | Причина и решение | +|---|---| +| «Команда не найдена» | Файл не загружен либо сохранён в UTF-8. Загрузить через `APPLOAD`, проверить кодировку. Если загрузка прошла, но подкоманд нет — выполнить `SPISSETDIR` и указать папку | +| `[НЕТ]` при загрузке | Файл не найден рядом с `SPIS_TOOLS.lsp` | +| `Не найден скрипт` | `build_discipline_sets.py` должен лежать рядом с LISP | +| Не найдено DWG | В подпапках АР, ЭТР, СС, ВК, ОВ нет файлов `.dwg` | +| Пропущено | Ссылка уже подключена, дубликат не создаётся | +| `Вызов был отклонен` | AutoCAD занят или завис. Перезапустить AutoCAD | +| Ссылка не видна | Проверить координаты и слои, выполнить `Z` → `E` | + +--- + +## Правила работы со ссылками + +- Не переименовывать и не перемещать DWG после подключения. +- Хранить папку проекта целиком, без разрыва структуры подпапок. +- Для обновления исходников — «Перезагрузить» в палитре XREF. +- Перед заменой состава ссылок сохранить резервную копию СПИС. +- Существующие DWG команда сборки не перезаписывает. Пересобрать комплект + можно, только удалив файлы дисциплин вручную. АР не удаляется никогда. + +--- + +GENPRO · Группа ЭТР · 2026 diff --git a/SPISHELP.lsp b/SPISHELP.lsp new file mode 100644 index 0000000..974af99 --- /dev/null +++ b/SPISHELP.lsp @@ -0,0 +1,124 @@ +;;; ============================================================ +;;; SPISHELP.lsp � ������� �� �������� ���� +;;; +;;; ������� SPISHELP � ��������� ������ AutoCAD. +;;; ���������� �������� ���� ������, ����� ���������, +;;; �������� ���� � ������� ������� �������. +;;; ============================================================ + +(defun c:SPISHELP ( / ) + (princ "\n") + (princ "\n============================================================") + (princ "\n ����������� ���� � ������ �������") + (princ "\n============================================================") + + ;; --- ����� ��������� --- + (princ "\n") + (princ "\n ����� ��������� (������ Overlay, ������� 0,0,0, ������� 1:1):") + (princ "\n") + (princ "\n �� � ������, ���� ������ �� ����������") + (princ "\n ��� = �� + �� + �� + ��") + (princ "\n �� = �� + �� + �� + ���") + (princ "\n �� = �� + �� + �� + ���") + (princ "\n �� = �� + �� + �� + ���") + + ;; --- ������� ������ --- + (princ "\n") + (princ "\n ������� ������:") + (princ "\n") + (princ "\n 1. SPISRUNSETS � ������� ����� ��������� (����� ����� Python)") + (princ "\n 2. ��������� ���������� (���: %TEMP%\\spis_sets_log.txt)") + (princ "\n 3. SPISATTACHSETS � ���������� ����� � �������� ����") + + ;; --- �������: ������ --- + (princ "\n") + (princ "\n ������ ���������:") + (princ "\n") + (princ "\n SPISRUNSETS") + (princ "\n ������� ����� ��������� (���, ��, ��, ��) �� �������� �� �����.") + (princ "\n ���������� ������ ������� (�����, ��� ����� ��, ���, ��, ��, ��).") + (princ "\n ���������� ������ �� ����� ����� �� ��� �����.") + (princ "\n ��������� Python-������ � ���� � AutoCAD ������� ��������.") + (princ "\n ���: %TEMP%\\spis_sets_log.txt") + (princ "\n") + (princ "\n SPISATTACHSETS") + (princ "\n ���������� �� � ��� ���������� � �������� ���� (Overlay).") + (princ "\n ���������� ������ �������.") + (princ "\n ���������� ������ ������ � ���������� �������������.") + (princ "\n ��� ������������ ������ ������������.") + + ;; --- �������: ������ --- + (princ "\n") + (princ "\n ������ �� �������� (XREF):") + (princ "\n") + (princ "\n XRLIST") + (princ "\n �������� ��� ������� ������ � �� ��������.") + (princ "\n ������ �� ��������. �����: %TEMP%\\xrbind_report.txt") + (princ "\n") + (princ "\n XRBIND") + (princ "\n �������� (BIND) ��� ����������� ������ � �����.") + (princ "\n ����� ������ ������������. �����: %TEMP%\\xrbind_report.txt") + (princ "\n") + (princ "\n XRBIND1") + (princ "\n �������� ���� ������ �� ����� (������� ���).") + (princ "\n") + (princ "\n XRPATH") + (princ "\n ������� ���� ������������ ������.") + (princ "\n ������� ��� ������ � ����� ���� � DWG-�����.") + (princ "\n �������, ����� ����� moved �� ������ ����/�����.") + (princ "\n") + (princ "\n XRUNBIND") + (princ "\n ����������� ������ ����� (�������������) ������.") + (princ "\n �� ������� �������� ������. ����� �����, ������� ��") + (princ "\n ����������� ��-�� ����� ��������� ������.") + (princ "\n") + (princ "\n XRDETACH") + (princ "\n ����������� ��� ������ (������� �������������).") + (princ "\n ��������: ��� ���������! ������� XRBIND, ����� XRDETACH.") + + ;; --- �������� ���� --- + (princ "\n") + (princ "\n �������� ����:") + (princ "\n") + (princ (strcat "\n �����������: " *SPIS-DIR*)) + (princ (strcat "\n ��� ������: " (getenv "TEMP") "\\spis_sets_log.txt")) + (princ (strcat "\n ��� ������: " (getenv "TEMP") "\\xrbind_report.txt")) + (princ "\n Python: .venv\\Scripts\\pythonw.exe (����� � �������������)") + + ;; --- ������� ������� --- + (princ "\n") + (princ "\n ������� ������� �������:") + (princ "\n") + (princ "\n FILEDIA �������� (��� �������� ��������/����������):") + (princ "\n (setvar \"FILEDIA\" 1) � ��������� � ��������� ������") + (princ "\n") + (princ "\n ������� ��� ���������� ��������� ��� ������:") + (princ "\n � AutoCAD ����� ���������� ��������� �� ������� ��������.") + (princ "\n �������� ��� ��������� (����� ����) � ��������� �����.") + (princ "\n ��� �������� � �������� AutoCAD ������.") + (princ "\n") + (princ "\n ������ �� ������������ (SPISATTACHSETS):") + (princ "\n ���������, ��� ����� ����� � ��������� ��, ���, ��, ��, ��.") + (princ "\n ��������� XRLIST � ����� ������ ��� ���� � �� ������.") + (princ "\n") + (princ "\n ������ ������� / �� �����������:") + (princ "\n ���������� ���: %TEMP%\\spis_sets_log.txt") + (princ "\n ���������, ��� .venv ���������� ����� � �������������.") + + ;; --- ������������ --- + (princ "\n") + (princ "\n ������������:") + (princ "\n") + (princ "\n APPLOAD -> ���������� -> �������� -> SPIS_TOOLS.lsp") + (princ "\n ��� � acaddoc.lsp:") + (princ "\n (load \"G:/��� ����/lisp-code/SPIS/SPIS_TOOLS.lsp\")") + + (princ "\n") + (princ "\n============================================================") + (princ "\n ������� SPISHELP ��� ��� ��� ���������� ������ �������.") + (princ "\n============================================================") + (princ) +) + +(princ "\nSPISHELP.lsp ��������. �������: SPISHELP") +(princ) diff --git a/SPISHELP.lsp.utf8 b/SPISHELP.lsp.utf8 new file mode 100644 index 0000000..dc11dd7 --- /dev/null +++ b/SPISHELP.lsp.utf8 @@ -0,0 +1,124 @@ +;;; ============================================================ +;;; SPISHELP.lsp — справка по командам СПИС +;;; +;;; Введите SPISHELP в командной строке AutoCAD. +;;; Показывает описание всех команд, схему комплекта, +;;; полезные пути и решение типовых проблем. +;;; ============================================================ + +(defun c:SPISHELP ( / ) + (princ "\n") + (princ "\n============================================================") + (princ "\n ИНСТРУМЕНТЫ СПИС — полная справка") + (princ "\n============================================================") + + ;; --- СХЕМА КОМПЛЕКТА --- + (princ "\n") + (princ "\n СХЕМА КОМПЛЕКТА (ссылки Overlay, вставка 0,0,0, масштаб 1:1):") + (princ "\n") + (princ "\n АР — основа, сама ничего не подключает") + (princ "\n ЭТР = АР + СС + ВК + ОВ") + (princ "\n СС = АР + ВК + ОВ + ЭТР") + (princ "\n ВК = АР + СС + ОВ + ЭТР") + (princ "\n ОВ = АР + СС + ВК + ЭТР") + + ;; --- ПОРЯДОК РАБОТЫ --- + (princ "\n") + (princ "\n ПОРЯДОК РАБОТЫ:") + (princ "\n") + (princ "\n 1. SPISRUNSETS — собрать файлы дисциплин (фоном через Python)") + (princ "\n 2. Дождаться завершения (лог: %TEMP%\\spis_sets_log.txt)") + (princ "\n 3. SPISATTACHSETS — подключить файлы в открытый СПИС") + + ;; --- КОМАНДЫ: СБОРКА --- + (princ "\n") + (princ "\n СБОРКА КОМПЛЕКТА:") + (princ "\n") + (princ "\n SPISRUNSETS") + (princ "\n Создать файлы дисциплин (ЭТР, СС, ВК, ОВ) со ссылками по схеме.") + (princ "\n Спрашивает корень проекта (папка, где лежат АР, ЭТР, СС, ВК, ОВ).") + (princ "\n Определяет корпус из имени файла АР или папки.") + (princ "\n Запускает Python-скрипт в фоне — AutoCAD остаётся свободен.") + (princ "\n Лог: %TEMP%\\spis_sets_log.txt") + (princ "\n") + (princ "\n SPISATTACHSETS") + (princ "\n Подключить АР и все дисциплины в открытый СПИС (Overlay).") + (princ "\n Спрашивает корень проекта.") + (princ "\n Показывает список файлов и спрашивает подтверждение.") + (princ "\n Уже подключённые ссылки пропускаются.") + + ;; --- КОМАНДЫ: ССЫЛКИ --- + (princ "\n") + (princ "\n РАБОТА СО ССЫЛКАМИ (XREF):") + (princ "\n") + (princ "\n XRLIST") + (princ "\n Показать все внешние ссылки с их статусом.") + (princ "\n Ничего не изменяет. Отчёт: %TEMP%\\xrbind_report.txt") + (princ "\n") + (princ "\n XRBIND") + (princ "\n Внедрить (BIND) ВСЕ разрешённые ссылки в чертёж.") + (princ "\n Битые ссылки пропускаются. Отчёт: %TEMP%\\xrbind_report.txt") + (princ "\n") + (princ "\n XRBIND1") + (princ "\n Внедрить ОДНУ ссылку по имени (спросит имя).") + (princ "\n") + (princ "\n XRPATH") + (princ "\n Сменить путь «переехавшей» ссылки.") + (princ "\n Спросит имя ссылки и новый путь к DWG-файлу.") + (princ "\n Полезно, когда файлы moved на другой диск/папку.") + (princ "\n") + (princ "\n XRUNBIND") + (princ "\n Отсоединить ТОЛЬКО битые (неразрешённые) ссылки.") + (princ "\n Не требует исходных файлов. Лечит чертёж, который не") + (princ "\n сохраняется из-за битых вложенных ссылок.") + (princ "\n") + (princ "\n XRDETACH") + (princ "\n Отсоединить ВСЕ ссылки (спросит подтверждение).") + (princ "\n ВНИМАНИЕ: без внедрения! Сначала XRBIND, потом XRDETACH.") + + ;; --- ПОЛЕЗНЫЕ ПУТИ --- + (princ "\n") + (princ "\n ПОЛЕЗНЫЕ ПУТИ:") + (princ "\n") + (princ (strcat "\n Инструменты: " *SPIS-DIR*)) + (princ (strcat "\n Лог сборки: " (getenv "TEMP") "\\spis_sets_log.txt")) + (princ (strcat "\n Лог ссылок: " (getenv "TEMP") "\\xrbind_report.txt")) + (princ "\n Python: .venv\\Scripts\\pythonw.exe (рядом с инструментами)") + + ;; --- РЕШЕНИЕ ПРОБЛЕМ --- + (princ "\n") + (princ "\n РЕШЕНИЕ ТИПОВЫХ ПРОБЛЕМ:") + (princ "\n") + (princ "\n FILEDIA сломался (нет диалогов открытия/сохранения):") + (princ "\n (setvar \"FILEDIA\" 1) — выполнить в командной строке") + (princ "\n") + (princ "\n «Ошибка при сохранении документа» при сборке:") + (princ "\n В AutoCAD висят незакрытые документы от прошлых запусков.") + (princ "\n Закройте все документы (кроме СПИС) и запустите снова.") + (princ "\n Или закройте и откройте AutoCAD заново.") + (princ "\n") + (princ "\n Ссылки не подключаются (SPISATTACHSETS):") + (princ "\n Проверьте, что файлы лежат в подпапках АР, ЭТР, СС, ВК, ОВ.") + (princ "\n Проверьте XRLIST — какие ссылки уже есть и их статус.") + (princ "\n") + (princ "\n Сборка зависла / не запускается:") + (princ "\n Посмотрите лог: %TEMP%\\spis_sets_log.txt") + (princ "\n Убедитесь, что .venv существует рядом с инструментами.") + + ;; --- АВТОЗАГРУЗКА --- + (princ "\n") + (princ "\n АВТОЗАГРУЗКА:") + (princ "\n") + (princ "\n APPLOAD -> Содержимое -> Добавить -> SPIS_TOOLS.lsp") + (princ "\n Или в acaddoc.lsp:") + (princ "\n (load \"G:/Мой диск/lisp-code/SPIS/SPIS_TOOLS.lsp\")") + + (princ "\n") + (princ "\n============================================================") + (princ "\n Введите SPISHELP ещё раз для повторного показа справки.") + (princ "\n============================================================") + (princ) +) + +(princ "\nSPISHELP.lsp загружен. Команда: SPISHELP") +(princ) diff --git a/SPIS_RUN_SETS.lsp b/SPIS_RUN_SETS.lsp new file mode 100644 index 0000000..58df0fc --- /dev/null +++ b/SPIS_RUN_SETS.lsp @@ -0,0 +1,167 @@ +;;; SPIS_RUN_SETS.lsp +;;; SPISRUNSETS � ��������� ������ ��������� ��������� �� AutoCAD. +;;; +;;; ������� �� ������ ����� ����: �������� � ����������� (�������, +;;; �������, ���������) �� LISP ��������� AutoCAD � ��������� �� +;;; AutoCAD 2020: vla-Open, _.OPEN � _.SAVEAS ������ LISP ������ ������. +;;; +;;; ������� ������� ��������� ������� Python-������, ������� �������� +;;; � AutoCAD ����� COM. AutoCAD ������� ��������. +;;; +;;; ��� �������� (����� �����������): +;;; ��� = �� + �� + �� + �� +;;; �� = �� + �� + �� + ��� +;;; �� = �� + �� + �� + ��� +;;; �� = �� + �� + �� + ��� +;;; �� � ������, ���� ������ �� ���������� +;;; +;;; ������ ��� ����� run_sets_launcher.py: �� ������ bat-���� � +;;; OEM-��������� (cp866) � �������� ������ ������. AutoLISP ��� �� +;;; ����� � (open ... "w") ����� � ��������� ���������, � ������� +;;; ���� � bat ������. + +(vl-load-com) + +;; ����� � ������������� ���� (���� ������ � �������� ������). +(defun srs:tools-dir (/ f r) + (cond + ;; 1) ���� ������ � ����� ��������� + ((setq f (findfile "SPIS_RUN_SETS.lsp")) + (vl-string-translate "/" "\\" (vl-filename-directory f))) + ;; 2) �����, ����������� ����������� SPIS_TOOLS / �������� SPISSETDIR + ((and (setq r (vl-catch-all-apply 'vl-registry-read + (list "HKEY_CURRENT_USER\\Software\\GENPRO\\SPIS" "ToolsDir"))) + (not (vl-catch-all-error-p r)) r (> (strlen r) 0)) + (vl-string-translate "/" "\\" r)) + ;; 3) ���������� ����������, ������������ ����������� + ((and *SPIS-DIR* (> (strlen *SPIS-DIR*) 0)) + (vl-string-translate "/" "\\" *SPIS-DIR*)) + ;; 4) ��������� ����� + ((setq f (findfile "SPIS_TOOLS.lsp")) + (vl-string-translate "/" "\\" (vl-filename-directory f))) + ;; 5) ��������� ������� + (T "G:\\��� ����\\lisp-code\\SPIS") + ) +) + +;; ������������� Python: ���� �� ���������� ��������� ����� �� ���������. +;; ������������ pythonw.exe � �� �� ������ ����������� ����. +(defun srs:python (/ tools local) + (setq tools (srs:tools-dir)) + (setq local (strcat (vl-string-right-trim "\\/" tools) "\\.venv\\Scripts\\pythonw.exe")) + (if (findfile local) + local + (progn + (setq local (strcat (vl-string-right-trim "\\/" tools) "\\.venv\\Scripts\\python.exe")) + (if (findfile local) local "python") + ) + ) +) + +(defun srs:norm (p) + (vl-string-right-trim "\\" (vl-string-translate "/" "\\" p)) +) + +(defun srs:split (str sep / pos res) + (setq res nil) + (while (setq pos (vl-string-search sep str)) + (setq res (cons (substr str 1 pos) res) + str (substr str (+ pos 1 (strlen sep)))) + ) + (reverse (cons str res)) +) + +;; ������ ��� ��� ������ ���������. +;; "�� �9.dwg" -> "�9" (������ � ����� �����) +;; "��.dwg" � "�����" -> "�����" (��� ����� ��� ������� � ���� �����) +(defun srs:corpus (root / dir files base parts parent) + (setq dir (srs:norm (strcat (srs:norm root) "\\��"))) + (if (vl-file-directory-p dir) + (setq files (vl-directory-files dir "*.dwg" 1)) + ) + (if files + (progn + (setq base (vl-filename-base (car files))) + (setq parts (srs:split base " ")) + (if (> (length parts) 1) + ;; ��� �� ���������� ����: ������ � ��������� ����� + (last parts) + ;; ��� �� ������ �����: ������ ������ � �������� ����� + (progn + (setq parent (vl-filename-base (srs:norm root))) + (if (or (not parent) (= parent "")) base parent) + ) + ) + ) + nil + ) +) + +;; �������� ������� ------------------------------------------------- +(defun c:SPISRUNSETS (/ root corpus tools launcher vpython shell cmd real-res) + (setq root (srs:norm (getstring T "\n������ ������� (����� ��, ���, ��, ��, ��): "))) + (cond + ((not (vl-file-directory-p root)) + (prompt (strcat "\n����� �� �������: " root)) + ) + ((not (setq corpus (srs:corpus root))) + (prompt "\n� ����� �� ��� ������ DWG � �� ���������� ������.") + ) + (T + (setq tools (srs:tools-dir)) + (setq launcher (strcat tools "/run_sets_launcher.py")) + (cond + ((not (findfile launcher)) + (prompt (strcat "\n�� ������ ���� �������: " launcher)) + (prompt "\n�� ������ ������ ����� � ���� LISP-������.") + ) + (T + (prompt (strcat "\n������: " root)) + (prompt (strcat "\n������: " corpus)) + (prompt "\n�����: ������ ������ = �� + ������� ����������") + (initget "Yes No") + (cond + ((/= (getkword "\n������� ��������? [Yes/No] : ") "Yes") + (prompt "\n��������. ������ �� ��������.") + ) + (T + (setq vpython (srs:python)) + ;; �����������: ���� + (prompt (strcat "\n[�������] python: " vpython)) + (prompt (strcat "\n[�������] ������: " launcher)) + (prompt (strcat "\n[�������] root: " root)) + (prompt (strcat "\n[�������] corpus: " corpus)) + ;; ������ ����� WScript.Shell.Run � startapp � AutoCAD 2020 + ;; �� �������� (���������� NIL ���� ��� cmd.exe). + (setq shell (vl-catch-all-apply 'vlax-create-object (list "WScript.Shell"))) + (if (vl-catch-all-error-p shell) + (prompt (strcat "\n[�������] WScript.Shell: ������ " (vl-catch-all-error-message shell))) + (progn + (prompt "\n[�������] WScript.Shell: ������ ������") + (setq cmd (strcat "\"" vpython "\" \"" launcher "\" \"" + root "\" \"" corpus "\"")) + (prompt (strcat "\n[�������] �������: " cmd)) + (setq real-res (vl-catch-all-apply 'vlax-invoke + (list shell "Run" cmd 0 0))) + (if (vl-catch-all-error-p real-res) + (prompt (strcat "\n[�������] ������ Run: " (vl-catch-all-error-message real-res))) + (prompt (strcat "\n[�������] Run: OK, ���=" (vl-princ-to-string real-res))) + ) + (vlax-release-object shell) + ) + ) + (prompt "\n=== �������� � ���� ===") + (prompt (strcat "\n��� ����������: " (getenv "TEMP") "\\spis_sets_log.txt")) + (prompt "\nAutoCAD �������� � ������ ��� ��������.") + (prompt "\n����� ����� ��������, ��������� SPISATTACHSETS � ����.") + ) + ) + ) + ) + ) + ) + (princ) +) + +(prompt "\nSPIS_RUN_SETS ��������. �������: SPISRUNSETS") +(princ) diff --git a/SPIS_RUN_SETS.lsp.utf8 b/SPIS_RUN_SETS.lsp.utf8 new file mode 100644 index 0000000..2f45ba4 --- /dev/null +++ b/SPIS_RUN_SETS.lsp.utf8 @@ -0,0 +1,167 @@ +;;; SPIS_RUN_SETS.lsp +;;; SPISRUNSETS — запускает сборку комплекта дисциплин из AutoCAD. +;;; +;;; Команда НЕ создаёт файлы сама: операции с документами (создать, +;;; открыть, сохранить) из LISP блокируют AutoCAD — проверено на +;;; AutoCAD 2020: vla-Open, _.OPEN и _.SAVEAS внутри LISP вешают сессию. +;;; +;;; Поэтому команда запускает ВНЕШНИЙ Python-скрипт, который работает +;;; с AutoCAD через COM. AutoCAD остаётся свободен. +;;; +;;; Что создаётся (схема согласована): +;;; ЭТР = АР + СС + ВК + ОВ +;;; СС = АР + ВК + ОВ + ЭТР +;;; ВК = АР + СС + ОВ + ЭТР +;;; ОВ = АР + СС + ВК + ЭТР +;;; АР — основа, сама ничего не подключает +;;; +;;; ЗАПУСК идёт через run_sets_launcher.py: он создаёт bat-файл в +;;; OEM-кодировке (cp866) и стартует сборку скрыто. AutoLISP так не +;;; умеет — (open ... "w") пишет в системной кодировке, и русские +;;; пути в bat бьются. + +(vl-load-com) + +;; Папка с инструментами СПИС (пути всегда с обратным слэшем). +(defun srs:tools-dir (/ f r) + (cond + ;; 1) файл найден в путях поддержки + ((setq f (findfile "SPIS_RUN_SETS.lsp")) + (vl-string-translate "/" "\\" (vl-filename-directory f))) + ;; 2) папка, запомненная загрузчиком SPIS_TOOLS / командой SPISSETDIR + ((and (setq r (vl-catch-all-apply 'vl-registry-read + (list "HKEY_CURRENT_USER\\Software\\GENPRO\\SPIS" "ToolsDir"))) + (not (vl-catch-all-error-p r)) r (> (strlen r) 0)) + (vl-string-translate "/" "\\" r)) + ;; 3) глобальная переменная, выставленная загрузчиком + ((and *SPIS-DIR* (> (strlen *SPIS-DIR*) 0)) + (vl-string-translate "/" "\\" *SPIS-DIR*)) + ;; 4) загрузчик рядом + ((setq f (findfile "SPIS_TOOLS.lsp")) + (vl-string-translate "/" "\\" (vl-filename-directory f))) + ;; 5) аварийный вариант + (T "G:\\Мой диск\\lisp-code\\SPIS") + ) +) + +;; Интерпретатор Python: берём из локального окружения рядом со скриптами. +;; Предпочитаем pythonw.exe — он не создаёт консольного окна. +(defun srs:python (/ tools local) + (setq tools (srs:tools-dir)) + (setq local (strcat (vl-string-right-trim "\\/" tools) "\\.venv\\Scripts\\pythonw.exe")) + (if (findfile local) + local + (progn + (setq local (strcat (vl-string-right-trim "\\/" tools) "\\.venv\\Scripts\\python.exe")) + (if (findfile local) local "python") + ) + ) +) + +(defun srs:norm (p) + (vl-string-right-trim "\\" (vl-string-translate "/" "\\" p)) +) + +(defun srs:split (str sep / pos res) + (setq res nil) + (while (setq pos (vl-string-search sep str)) + (setq res (cons (substr str 1 pos) res) + str (substr str (+ pos 1 (strlen sep)))) + ) + (reverse (cons str res)) +) + +;; Корпус для имён файлов дисциплин. +;; "АР К9.dwg" -> "К9" (корпус в имени файла) +;; "АР.dwg" в "Ларус" -> "Ларус" (имя файла без корпуса — берём папку) +(defun srs:corpus (root / dir files base parts parent) + (setq dir (srs:norm (strcat (srs:norm root) "\\АР"))) + (if (vl-file-directory-p dir) + (setq files (vl-directory-files dir "*.dwg" 1)) + ) + (if files + (progn + (setq base (vl-filename-base (car files))) + (setq parts (srs:split base " ")) + (if (> (length parts) 1) + ;; имя из нескольких слов: корпус — последнее слово + (last parts) + ;; имя из одного слова: значит корпус в названии папки + (progn + (setq parent (vl-filename-base (srs:norm root))) + (if (or (not parent) (= parent "")) base parent) + ) + ) + ) + nil + ) +) + +;; Основная команда ------------------------------------------------- +(defun c:SPISRUNSETS (/ root corpus tools launcher vpython shell cmd real-res) + (setq root (srs:norm (getstring T "\nКорень проекта (папки АР, ЭТР, СС, ВК, ОВ): "))) + (cond + ((not (vl-file-directory-p root)) + (prompt (strcat "\nПапка не найдена: " root)) + ) + ((not (setq corpus (srs:corpus root))) + (prompt "\nВ папке АР нет файлов DWG — не определить корпус.") + ) + (T + (setq tools (srs:tools-dir)) + (setq launcher (strcat tools "/run_sets_launcher.py")) + (cond + ((not (findfile launcher)) + (prompt (strcat "\nНе найден файл запуска: " launcher)) + (prompt "\nОн должен лежать рядом с этим LISP-файлом.") + ) + (T + (prompt (strcat "\nКорень: " root)) + (prompt (strcat "\nКорпус: " corpus)) + (prompt "\nСхема: каждый раздел = АР + смежные дисциплины") + (initget "Yes No") + (cond + ((/= (getkword "\nСоздать комплект? [Yes/No] : ") "Yes") + (prompt "\nОтменено. Ничего не изменено.") + ) + (T + (setq vpython (srs:python)) + ;; Диагностика: пути + (prompt (strcat "\n[отладка] python: " vpython)) + (prompt (strcat "\n[отладка] скрипт: " launcher)) + (prompt (strcat "\n[отладка] root: " root)) + (prompt (strcat "\n[отладка] corpus: " corpus)) + ;; Запуск через WScript.Shell.Run — startapp в AutoCAD 2020 + ;; не работает (возвращает NIL даже для cmd.exe). + (setq shell (vl-catch-all-apply 'vlax-create-object (list "WScript.Shell"))) + (if (vl-catch-all-error-p shell) + (prompt (strcat "\n[отладка] WScript.Shell: ошибка " (vl-catch-all-error-message shell))) + (progn + (prompt "\n[отладка] WScript.Shell: объект создан") + (setq cmd (strcat "\"" vpython "\" \"" launcher "\" \"" + root "\" \"" corpus "\"")) + (prompt (strcat "\n[отладка] команда: " cmd)) + (setq real-res (vl-catch-all-apply 'vlax-invoke + (list shell "Run" cmd 0 0))) + (if (vl-catch-all-error-p real-res) + (prompt (strcat "\n[отладка] ошибка Run: " (vl-catch-all-error-message real-res))) + (prompt (strcat "\n[отладка] Run: OK, код=" (vl-princ-to-string real-res))) + ) + (vlax-release-object shell) + ) + ) + (prompt "\n=== Запущено в фоне ===") + (prompt (strcat "\nХод выполнения: " (getenv "TEMP") "\\spis_sets_log.txt")) + (prompt "\nAutoCAD свободен — сборка идёт отдельно.") + (prompt "\nКогда файлы появятся, выполните SPISATTACHSETS в СПИС.") + ) + ) + ) + ) + ) + ) + (princ) +) + +(prompt "\nSPIS_RUN_SETS загружен. Команда: SPISRUNSETS") +(princ) diff --git a/SPIS_SYSTEM_SETS.lsp b/SPIS_SYSTEM_SETS.lsp new file mode 100644 index 0000000..47be1ec --- /dev/null +++ b/SPIS_SYSTEM_SETS.lsp @@ -0,0 +1,116 @@ +;;; SPIS_SYSTEM_SETS.lsp +;;; SPISATTACHSETS � ���������� �� � ��� ���������� ��������� � �������� ����. +;;; +;;; ��������� ��������� (��� DWG ����� �� ����� ������): +;;; <������>\��\<...>.dwg � ������, ������ �� ���������� +;;; <������>\���\<...>.dwg � �������� �� + ������� ���������� +;;; <������>\��\<...>.dwg +;;; <������>\��\<...>.dwg +;;; <������>\��\<...>.dwg +;;; +;;; � ���� ������������ �� � ��� ������ ����������. +;;; ��� ������ � Overlay, ����� ������� 0,0,0, ������� 1:1, ������� 0. +;;; ��� ������������ ������ ������������, ������ �� ����������������. +;;; +;;; �����: AttachExternalReference ����������� ModelSpace, � �� ��������� +;;; Blocks. ��������� �� AutoCAD 2020: � Blocks ������ ������ ���, +;;; ����� � Blocks ��� ������������ ���: AttachExternalReference�. + +(vl-load-com) + +(defun sxs:blocks ( / doc) + (vla-get-Blocks (setq doc (vla-get-ActiveDocument (vlax-get-acad-object)))) +) + +(defun sxs:modelspace () + (vla-get-ModelSpace (vla-get-ActiveDocument (vlax-get-acad-object))) +) + +;; ���������� �� ��� ����-������ � ����� ������. +(defun sxs:exists-xref (name / blk) + (setq blk (vl-catch-all-apply 'vla-Item (list (sxs:blocks) name))) + (and (not (vl-catch-all-error-p blk)) (= :vlax-true (vla-get-IsXRef blk))) +) + +;; ���������� ���� ����. ���������� (STATUS name [message]). +;; �������� ���� ���������� ��� �� ������: ����� ��������� attach +;; ���� ��� ��������� xref-�������, � �������� ���� �� ������ SKIP. +(defun sxs:attach-one (file / name res) + (setq name (vl-filename-base file)) + (cond + ((sxs:exists-xref name) (list "SKIP" name)) + (T + ;; ����� ���������� � ModelSpace � ��. ���������� � ��������� + (setq res (vl-catch-all-apply 'vla-AttachExternalReference + (list (sxs:modelspace) file name + (vlax-3d-point 0.0 0.0 0.0) + 1.0 1.0 1.0 0.0 :vlax-true))) + (cond + ((vl-catch-all-error-p res) + (list "ERROR" name (vl-catch-all-error-message res))) + (T + ;; ������������ ���������: ���� ���� xref-�������? + (if (sxs:exists-xref name) + (list "OK" name) + (list "ERROR" name "������ �� ��������� � �������") + ) + ) + ) + ) + ) +) + +(defun c:SPISATTACHSETS (/ root sections section folder files file res ok skip err) + (setq root (vl-string-right-trim "\\/" + (getstring T "\n������ ������� (����� ��, ���, ��, ��, ��): "))) + (cond + ((not (vl-file-directory-p root)) + (prompt "\n����� �� �������. ������ �� ��������.") + ) + (T + ;; �� ������������ � ���� ������, ����� ��� ���������� ���������. + (setq sections (list "��" "���" "��" "��" "��")) + (setq files nil) + (foreach section sections + (setq folder (strcat root "\\" section)) + (if (vl-file-directory-p folder) + (foreach file (vl-directory-files folder "*.dwg" 1) + (setq files (cons (strcat folder "\\" file) files)) + ) + ) + ) + (setq files (reverse files)) + (cond + ((not files) + (prompt "\n� ������ ��������� �� ������� DWG. ������ �� ��������.") + ) + (T + (prompt (strcat "\n����� ���������� ������: " (itoa (length files)))) + (foreach file files (prompt (strcat "\n " file))) + (initget "Yes No") + (if (/= (getkword "\n���������� ��� �����? [Yes/No] : ") "Yes") + (prompt "\n��������. ������ �� ��������.") + (progn + (foreach file files + (setq res (sxs:attach-one file)) + (cond + ((= (car res) "OK") (setq ok (1+ (if ok ok 0)))) + ((= (car res) "SKIP") (setq skip (1+ (if skip skip 0)))) + (T (setq err (1+ (if err err 0))) + (prompt (strcat "\n������: " (cadr res) " - " (caddr res)))) + ) + ) + (prompt (strcat "\n������. ����������=" (itoa (if ok ok 0)) + ", ���������=" (itoa (if skip skip 0)) + ", ������=" (itoa (if err err 0)))) + ) + ) + ) + ) + ) + ) + (princ) +) + +(prompt "\nSPIS_SYSTEM_SETS ��������. �������: SPISATTACHSETS") +(princ) diff --git a/SPIS_SYSTEM_SETS.lsp.utf8 b/SPIS_SYSTEM_SETS.lsp.utf8 new file mode 100644 index 0000000..374a87e --- /dev/null +++ b/SPIS_SYSTEM_SETS.lsp.utf8 @@ -0,0 +1,116 @@ +;;; SPIS_SYSTEM_SETS.lsp +;;; SPISATTACHSETS — подключает АР и все дисциплины комплекта в открытый СПИС. +;;; +;;; Ожидаемая структура (все DWG лежат по своим папкам): +;;; <корень>\АР\<...>.dwg — основа, ничего не подключает +;;; <корень>\ЭТР\<...>.dwg — содержит АР + смежные дисциплины +;;; <корень>\СС\<...>.dwg +;;; <корень>\ВК\<...>.dwg +;;; <корень>\ОВ\<...>.dwg +;;; +;;; В СПИС подключаются АР и все четыре дисциплины. +;;; Тип ссылки — Overlay, точка вставки 0,0,0, масштаб 1:1, поворот 0. +;;; Уже подключённые ссылки пропускаются, ничего не перезаписывается. +;;; +;;; ВАЖНО: AttachExternalReference принадлежит ModelSpace, а НЕ коллекции +;;; Blocks. Проверено на AutoCAD 2020: у Blocks такого метода нет, +;;; вызов у Blocks даёт «неизвестное имя: AttachExternalReference». + +(vl-load-com) + +(defun sxs:blocks ( / doc) + (vla-get-Blocks (setq doc (vla-get-ActiveDocument (vlax-get-acad-object)))) +) + +(defun sxs:modelspace () + (vla-get-ModelSpace (vla-get-ActiveDocument (vlax-get-acad-object))) +) + +;; Существует ли уже блок-ссылка с таким именем. +(defun sxs:exists-xref (name / blk) + (setq blk (vl-catch-all-apply 'vla-Item (list (sxs:blocks) name))) + (and (not (vl-catch-all-error-p blk)) (= :vlax-true (vla-get-IsXRef blk))) +) + +;; Подключить один файл. Возвращает (STATUS name [message]). +;; Проверка «уже подключено» идёт ДО вызова: после успешного attach +;; блок уже считается xref-ссылкой, и проверка дала бы ложный SKIP. +(defun sxs:attach-one (file / name res) + (setq name (vl-filename-base file)) + (cond + ((sxs:exists-xref name) (list "SKIP" name)) + (T + ;; метод вызывается у ModelSpace — см. примечание в заголовке + (setq res (vl-catch-all-apply 'vla-AttachExternalReference + (list (sxs:modelspace) file name + (vlax-3d-point 0.0 0.0 0.0) + 1.0 1.0 1.0 0.0 :vlax-true))) + (cond + ((vl-catch-all-error-p res) + (list "ERROR" name (vl-catch-all-error-message res))) + (T + ;; Подтверждаем результат: блок стал xref-ссылкой? + (if (sxs:exists-xref name) + (list "OK" name) + (list "ERROR" name "ссылка не появилась в чертеже") + ) + ) + ) + ) + ) +) + +(defun c:SPISATTACHSETS (/ root sections section folder files file res ok skip err) + (setq root (vl-string-right-trim "\\/" + (getstring T "\nКорень проекта (папки АР, ЭТР, СС, ВК, ОВ): "))) + (cond + ((not (vl-file-directory-p root)) + (prompt "\nПапка не найдена. Ничего не изменено.") + ) + (T + ;; АР подключается в СПИС первым, затем все дисциплины комплекта. + (setq sections (list "АР" "ЭТР" "СС" "ВК" "ОВ")) + (setq files nil) + (foreach section sections + (setq folder (strcat root "\\" section)) + (if (vl-file-directory-p folder) + (foreach file (vl-directory-files folder "*.dwg" 1) + (setq files (cons (strcat folder "\\" file) files)) + ) + ) + ) + (setq files (reverse files)) + (cond + ((not files) + (prompt "\nВ папках дисциплин не найдено DWG. Ничего не изменено.") + ) + (T + (prompt (strcat "\nБудет подключено файлов: " (itoa (length files)))) + (foreach file files (prompt (strcat "\n " file))) + (initget "Yes No") + (if (/= (getkword "\nПодключить эти файлы? [Yes/No] : ") "Yes") + (prompt "\nОтменено. Ничего не изменено.") + (progn + (foreach file files + (setq res (sxs:attach-one file)) + (cond + ((= (car res) "OK") (setq ok (1+ (if ok ok 0)))) + ((= (car res) "SKIP") (setq skip (1+ (if skip skip 0)))) + (T (setq err (1+ (if err err 0))) + (prompt (strcat "\nОшибка: " (cadr res) " - " (caddr res)))) + ) + ) + (prompt (strcat "\nГотово. Подключено=" (itoa (if ok ok 0)) + ", пропущено=" (itoa (if skip skip 0)) + ", ошибок=" (itoa (if err err 0)))) + ) + ) + ) + ) + ) + ) + (princ) +) + +(prompt "\nSPIS_SYSTEM_SETS загружен. Команда: SPISATTACHSETS") +(princ) diff --git a/SPIS_TOOLS.lsp b/SPIS_TOOLS.lsp new file mode 100644 index 0000000..aea59e7 --- /dev/null +++ b/SPIS_TOOLS.lsp @@ -0,0 +1,94 @@ +;;; ============================================================ +;;; SPIS_TOOLS.lsp � ������ ��������� ������������ ���� +;;; +;;; ���������� ��� ������ ������: +;;; +;;; 1) �������� ��������� (SPIS_RUN_SETS.lsp) +;;; SPISRUNSETS � ������� ����� ��������� �� �������� �� ����� +;;; (��������� ������� Python-������) +;;; +;;; 2) ������ ���� (SPIS_SYSTEM_SETS.lsp) +;;; SPISATTACHSETS � ���������� �� � ���������� ���/��/��/�� +;;; � �������� ���� ��� ������� ������ (Overlay) +;;; +;;; 3) ������ �� �������� (XRBIND.lsp) +;;; XRLIST � ������ ������ �� ��������, ��� ��������� ������� +;;; XRBIND � �������� ��� ����������� ������ +;;; XRBIND1 � �������� ���� ������ �� ����� +;;; XRPATH � ������� ���� ������������ ������ +;;; XRUNBIND � ����������� ������ ����� (�������������) ������ +;;; XRDETACH � ����������� ��� ������ (� ��������������) +;;; +;;; 4) ������� (SPISHELP.lsp) +;;; SPISHELP � ������ �������: �����, �������, ����, �������� +;;; +;;; �����: ��� ����� ������ ������ � ����� �����. +;;; ��������� ���������� � ���; ��� ������������� ������� �������: +;;; (setq *SPIS-DIR* "G:/��� ����/lisp-code/SPIS") +;;; +;;; ����� ������ ���� � ��������� Windows-1251: AutoCAD 2020 +;;; �� ������ UTF-8 � LISP, ��������� ������������ � �����. +;;; ============================================================ + +;; �����, ���� ����������� �����. ������� ����� ��� ��������. +(setq *SPIS-DIR* "G:/��� ����/lisp-code/SPIS") + +;; ���������� ����� �� ������������ ����� �����. +(defun spis:detect-dir (/ f) + (cond + ((setq f (findfile "SPIS_TOOLS.lsp")) (vl-filename-directory f)) + ((setq f (findfile "SPIS_TOOLS")) (vl-filename-directory f)) + (T nil) + ) +) + +(defun spis:load-one (fname / full) + (setq full (strcat (vl-string-right-trim "\\/" *SPIS-DIR*) "\\" fname)) + (cond + ((findfile full) + (load full) + (princ (strcat "\n [OK] " fname))) + (T + (princ (strcat "\n [���] " fname " � �� ������ � " *SPIS-DIR*))) + ) + (princ) +) + +(princ "\n==============================================") +(princ "\n �������� ������������ ����") +(princ "\n==============================================") + +(if (setq *spis-detected* (spis:detect-dir)) + (progn + (setq *SPIS-DIR* *spis-detected*) + (princ (strcat "\n �����: " *SPIS-DIR*))) + (princ (strcat "\n ����� (�� ���������): " *SPIS-DIR*)) +) + +(spis:load-one "SPIS_RUN_SETS.lsp") +(spis:load-one "SPIS_SYSTEM_SETS.lsp") +(spis:load-one "XRBIND.lsp") +(spis:load-one "XRCOPYVISIBLE.lsp") +(spis:load-one "SPISHELP.lsp") + +(princ "\n\n �������:") +(princ "\n �������� ���������:") +(princ "\n SPISRUNSETS � ������� ����� ��������� �� ��������") +(princ "\n ������ ����:") +(princ "\n SPISATTACHSETS � ���������� �� � ���������� � ����") +(princ "\n ������ �� ��������:") +(princ "\n XRLIST � ������ ������ �� ��������") +(princ "\n XRBIND � �������� ����������� ������") +(princ "\n XRBIND1 � �������� ���� ������") +(princ "\n XRPATH � ������� ���� ������") +(princ "\n XRUNBIND � ����������� ����� ������") +(princ "\n XRDETACH � ����������� ��� ������") +(princ "\n XRCOPYVISIBLE � ����������� ��������� ��������� ������� ������") +(princ "\n �������:") +(princ "\n SPISHELP � ������ ������� �� ���� ��������") +(princ "\n==============================================") +(princ) + +;;; ��� ������������: �������� SPIS_TOOLS.lsp � Startup Suite +;;; (APPLOAD -> ���������� -> ��������) ���� ������ � acaddoc.lsp: +;;; (load "G:/��� ����/lisp-code/SPIS/SPIS_TOOLS.lsp") diff --git a/SPIS_TOOLS.lsp.utf8 b/SPIS_TOOLS.lsp.utf8 new file mode 100644 index 0000000..e7d9190 --- /dev/null +++ b/SPIS_TOOLS.lsp.utf8 @@ -0,0 +1,159 @@ +;;; ============================================================ +;;; SPIS_TOOLS.lsp — единый загрузчик инструментов СПИС +;;; +;;; AutoCAD 2020 и 2026. Кодировка Windows-1251. +;;; +;;; Папка скриптов определяется АВТОМАТИЧЕСКИ и запоминается: +;;; 1) реестр HKCU\Software\GENPRO\SPIS\ToolsDir +;;; 2) пути поддержки AutoCAD (findfile) +;;; 3) поиск SPIS_TOOLS.lsp в доверенных путях (до 3 уровней) +;;; Найденная папка добавляется в TRUSTEDPATHS (снимает диалог +;;; безопасности) и сохраняется в реестр — второй раз мгновенно. +;;; +;;; Если автопоиск не сработал — команда SPISSETDIR. +;;; +;;; Команды: +;;; SPISSETDIR — указать папку СПИС вручную +;;; SPISRUNSETS — собрать файлы дисциплин (фоном, Python) +;;; SPISATTACHSETS — подключить АР и дисциплины в СПИС +;;; XRLIST XRBIND XRBIND1 XRPATH XRUNBIND XRDETACH +;;; SPISHELP — справка +;;; ============================================================ + +(vl-load-com) + +(setq *SPIS-REG* "HKEY_CURRENT_USER\\Software\\GENPRO\\SPIS") +(setq *SPIS-SUBS* (list "SPIS_RUN_SETS.lsp" "SPIS_SYSTEM_SETS.lsp" + "XRBIND.lsp" "SPISHELP.lsp")) +(setq *SPIS-DIR* nil) + +;; ---------- мелкие утилиты ---------- +(defun spis:trim (s) (vl-string-right-trim "\\/" s)) + +(defun spis:split (str sep / pos res) + (setq res nil) + (while (setq pos (vl-string-search sep str)) + (setq res (cons (substr str 1 pos) res) + str (substr str (+ pos 1 (strlen sep))))) + (reverse (cons str res))) + +;; Есть ли в папке загрузчик? +(defun spis:here-p (dir / full) + (if (and dir (> (strlen dir) 0)) + (progn + (setq full (strcat (spis:trim dir) "\\SPIS_TOOLS.lsp")) + (if (findfile full) T nil)) + nil)) + +;; ---------- реестр: запомнить/прочитать папку ---------- +(defun spis:reg-get ( / r) + (setq r (vl-catch-all-apply 'vl-registry-read (list *SPIS-REG* "ToolsDir"))) + (if (vl-catch-all-error-p r) nil r)) + +(defun spis:reg-set (dir) + (vl-catch-all-apply 'vl-registry-write (list *SPIS-REG* "ToolsDir" dir))) + +;; ---------- добавить папку в TRUSTEDPATHS ---------- +(defun spis:ensure-trusted (dir / tp full) + (setq full (strcase (spis:trim dir))) + (setq tp (getvar "TRUSTEDPATHS")) + (if (vl-string-search full (strcase tp)) + (princ) + (progn + (setvar "TRUSTEDPATHS" (strcat tp ";" (spis:trim dir))) + (princ (strcat "\n Доверенный путь добавлен: " (spis:trim dir)))))) + +;; ---------- рекурсивный поиск папки с загрузчиком ---------- +(defun spis:scan (dir depth / subs s found r) + (setq found nil) + (if (spis:here-p dir) + (setq found dir) + (if (> depth 0) + (progn + (setq r (vl-catch-all-apply 'vl-directory-files (list dir "*" -1))) + (if (not (vl-catch-all-error-p r)) + (foreach s r + (if (and (null found) + (/= s ".") (/= s "..") + (vl-file-directory-p (strcat dir "\\" s))) + (setq found (spis:scan (strcat dir "\\" s) (1- depth))))))))) + found) + +;; ---------- корни для поиска: пути поддержки + доверенные ---------- +(defun spis:candidate-roots ( / acc) + (setq acc (spis:split (getvar "ACADPREFIX") ";")) + (setq acc (append acc (spis:split (getvar "TRUSTEDPATHS") ";"))) + acc) + +;; ---------- определить папку ---------- +(defun spis:detect ( / d f roots) + (setq d (spis:reg-get)) ;; 1) реестр + (if (not (spis:here-p d)) (setq d nil)) + (if (null d) ;; 2) пути поддержки + (if (setq f (findfile "SPIS_TOOLS.lsp")) + (setq d (vl-filename-directory f)))) + (if (null d) ;; 3) скан доверенных/поддержки + (progn + (setq roots (spis:candidate-roots)) + (foreach r roots + (if (and (null d) (vl-file-directory-p r)) + (setq d (spis:scan (spis:trim r) 3)))))) + d) + +;; ---------- загрузка одного файла по полному пути ---------- +(defun spis:load-one (dir fname / full r) + (setq full (strcat (spis:trim dir) "\\" fname)) + (if (findfile full) + (progn + (setq r (vl-catch-all-apply 'load (list full))) + (if (vl-catch-all-error-p r) + (princ (strcat "\n [ОШИБКА] " fname ": " (vl-catch-all-error-message r))) + (princ (strcat "\n [OK] " fname)))) + (princ (strcat "\n [НЕТ] " fname " — не найден в " dir))) + (princ)) + +;; ---------- ручная установка папки ---------- +(defun c:SPISSETDIR ( / dir) + (setq dir (getstring T "\nПапка с файлами СПИС (например Y:/BIM/Autocad PLUGINS/SPIS): ")) + (setq dir (spis:trim (vl-string-translate "/" "\\" dir))) + (cond + ((= dir "") (princ "\n Пусто — ничего не изменено.")) + ((not (spis:here-p dir)) (princ "\n В этой папке нет SPIS_TOOLS.lsp.")) + (T + (setq *SPIS-DIR* dir) + (spis:reg-set dir) + (spis:ensure-trusted dir) + (princ (strcat "\n Папка сохранена: " dir)) + (princ "\n Перезагрузите SPIS_TOOLS.lsp (APPLOAD) для подключения команд."))) + (princ)) + +;; ============================================================ +;; ЗАГРУЗКА +;; ============================================================ +(princ "\n==============================================") +(princ "\n Загрузка инструментов СПИС") +(princ "\n==============================================") + +(if (setq *SPIS-DIR* (spis:detect)) + (progn + (princ (strcat "\n Папка: " *SPIS-DIR*)) + (spis:reg-set *SPIS-DIR*) + (spis:ensure-trusted *SPIS-DIR*) + (foreach f *SPIS-SUBS* (spis:load-one *SPIS-DIR* f)) + (princ "\n\n Команды:") + (princ "\n SPISRUNSETS — собрать файлы дисциплин") + (princ "\n SPISATTACHSETS — подключить дисциплины в СПИС") + (princ "\n XRLIST XRBIND XRBIND1 XRPATH XRUNBIND XRDETACH") + (princ "\n SPISHELP — справка") + (princ "\n SPISSETDIR — указать папку вручную")) + (progn + (princ "\n Папка с инструментами не найдена автоматически.") + (princ "\n Выполните команду SPISSETDIR и укажите путь, например:") + (princ "\n Y:/BIM/Autocad PLUGINS/SPIS"))) +(princ "\n==============================================") +(princ) + +;;; Автозагрузка: APPLOAD -> Содержимое -> Добавить -> SPIS_TOOLS.lsp +;;; либо строка в acaddoc.lsp (путь подставьте свой): +;;; (setvar "TRUSTEDPATHS" (strcat (getvar "TRUSTEDPATHS") ";Y:\\BIM\\Autocad PLUGINS\\SPIS")) +;;; (load "Y:/BIM/Autocad PLUGINS/SPIS/SPIS_TOOLS.lsp") diff --git a/XRBIND.lsp b/XRBIND.lsp new file mode 100644 index 0000000..3fede54 --- /dev/null +++ b/XRBIND.lsp @@ -0,0 +1,233 @@ +;;; ============================================================ +;;; XRBIND.lsp � ������� ������ AutoCAD: ��������� / ������ +;;; AutoCAD 2025/2026. Dionis / ����. +;;; �������: +;;; XRBIND � �������� ��� �����ب���� ������ � �������� ������� +;;; XRBIND1 � �������� ���� ������ (������� ���) +;;; XRLIST � ������ ������ �� ��������, ��� ��������� ������� +;;; XRPATH � ������� ���� ����� ������ (�������, ��� ����� ����) +;;; XRUNBIND � ����������� ������ ������������� (�����) ������ +;;; XRDETACH � ����������� ��� ������ (� ��������������) +;;; �����: %TEMP%\xrbind_report.txt +;;; ============================================================ + +(setq *xrb-log* (strcat (getenv "TEMP") "\\xrbind_report.txt")) + +;; ---- ������ ��� ������� ������ (������� ��� � �������) ---- +(defun xrb-xref-names (doc / lst) + (setq lst nil) + (vlax-for b (vla-get-Blocks doc) + (if (= (vla-get-IsXRef b) :vlax-true) + (setq lst (cons (vla-get-Name b) lst)))) + (reverse lst)) + +;; ---- ��������� �� ������: ��� 32 � ������ ����� ---- +(defun xrb-resolved-p (nm / e fl) + (setq e (entget (tblobjname "BLOCK" nm))) + (if e + (progn (setq fl (cdr (assoc 70 e))) + (if (= 32 (logand 32 fl)) T nil)) + nil)) + +;; ---- ��� ������ xref-����? (��� 4) ---- +(defun xrb-isxref-p (nm / e fl) + (setq e (entget (tblobjname "BLOCK" nm))) + (if e + (progn (setq fl (cdr (assoc 70 e))) + (if (= 4 (logand 4 fl)) T nil)) + nil)) + +(defun xrb-w (f s) (write-line s f)) +(defun xrb-pr (s) (princ (strcat "\n" s))) + +;; ============================================================ +;; XRLIST � ����� �� ������� (��� ��������� �������) +;; ============================================================ +(defun c:XRLIST ( / doc names nm f n res tot) + (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) + (setq f (open *xrb-log* "w")) + (xrb-w f (strcat "����� �� ������� ������� " (vla-get-Name doc))) + (xrb-w f (strcat "������ ����: " (vla-get-FullName doc))) + (xrb-w f "-----------------------------------------------") + (setq names (xrb-xref-names doc)) + (setq tot (length names) n 0) + (foreach nm names + (setq n (1+ n)) + (setq res (if (xrb-resolved-p nm) "���������" "�� ���������")) + (xrb-w f (strcat (itoa n) ".\t" res "\t" nm))) + (xrb-w f "-----------------------------------------------") + (xrb-w f (strcat "����� ������: " (itoa tot))) + (close f) + (xrb-pr (strcat "XRLIST: ������ " (itoa tot) " -> " *xrb-log*)) + (xrb-pr "XRLIST_DONE")) + +;; ============================================================ +;; XRBIND � �������� ��� ����������� ������ +;; ============================================================ +(defun c:XRBIND ( / doc f names nm before after ok fail skip oldecho oldfiledia) + (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) + (setq f (open *xrb-log* "w")) + (xrb-w f (strcat "XBIND (��������� ������) " (vla-get-Name doc))) + (xrb-w f "-----------------------------------------------") + (setq oldecho (getvar "CMDECHO")) + (setq oldfiledia (getvar "FILEDIA")) + (setvar "CMDECHO" 0) + (setvar "FILEDIA" 0) + + (setq names (xrb-xref-names doc)) + (setq before (length names)) + (xrb-w f (strcat "������ �� ��������: " (itoa before))) + + (setq ok 0 fail 0 skip 0) + (foreach nm names + (if (xrb-resolved-p nm) + (progn + ;; ���������: ��� + ������ Enter (��� ���� ������� ������ �� ��������� + ;; ������� ������ ���). ��������� �� �������� ��������� ����. + (command "_.-XREF" "_B" nm "") + (if (xrb-resolved-p nm) + (progn (setq fail (1+ fail)) + (xrb-w f (strcat "�� �������\t" nm))) + (progn (setq ok (1+ ok)) + (xrb-w f (strcat "��������\t" nm))))) + (progn (setq skip (1+ skip)) + (xrb-w f (strcat "��������� (�� ���������)\t" nm))))) + + (setq after (length (xrb-xref-names doc))) + (xrb-w f "-----------------------------------------------") + (xrb-w f (strcat "��������: " (itoa ok))) + (xrb-w f (strcat "�� �������: " (itoa fail))) + (xrb-w f (strcat "��������� (�����): " (itoa skip))) + (xrb-w f (strcat "������ ��������: " (itoa after) " (���� " (itoa before) ")")) + (close f) + + (setvar "CMDECHO" oldecho) + (setvar "FILEDIA" oldfiledia) + (xrb-pr (strcat "XRBIND: �������� " (itoa ok) ", �� ������� " (itoa fail) + ", ��������� " (itoa skip) ", �������� " (itoa after) + " -> " *xrb-log*)) + (xrb-pr "XRBIND_DONE")) + +;; ============================================================ +;; XRBIND1 � �������� ���� ������ �� ����� +;; ============================================================ +(defun c:XRBIND1 ( / nm oldfiledia) + (setq nm (getstring T "\n��� ������� ������ ��� ���������: ")) + (if (and nm (/= nm "")) + (if (xrb-resolved-p nm) + (progn + (setq oldfiledia (getvar "FILEDIA")) + (setvar "FILEDIA" 0) + (command "_.-XREF" "_B" nm "") + (setvar "FILEDIA" oldfiledia) + (if (xrb-resolved-p nm) + (xrb-pr (strcat "XRBIND1: �� ������� �������� " nm)) + (xrb-pr (strcat "XRBIND1: �������� " nm)))) + (xrb-pr (strcat "XRBIND1: ������ �� ��������� ��� �� �������: " nm))) + (xrb-pr "XRBIND1: ��� �� ������")) + (xrb-pr "XRBIND1_DONE")) + +;; ============================================================ +;; XRPATH � ������� ���� ������ (������� ������������� �����) +;; ============================================================ +(defun c:XRPATH ( / nm newpath oldfiledia) + (setq nm (getstring T "\n��� ������, ������� ������ ����: ")) + (cond + ((= nm "") (xrb-pr "XRPATH: ��� ������")) + ((not (xrb-isxref-p nm)) (xrb-pr "XRPATH: ��� �� ������� ������")) + (T + (setq newpath (getstring T "\n����� ������ ���� � dwg: ")) + (cond + ((= newpath "") (xrb-pr "XRPATH: ���� ������ � �� ������")) + ((not (findfile newpath)) (xrb-pr "XRPATH: ���� �� ������ � ���� �� ������")) + (T + (setq oldfiledia (getvar "FILEDIA")) + (setvar "FILEDIA" 0) + (command "_.-XREF" "_P" nm newpath "") + (setvar "FILEDIA" oldfiledia) + (if (xrb-resolved-p nm) + (xrb-pr (strcat "XRPATH: ������ �� ��� �� ���������: " nm)) + (xrb-pr (strcat "XRPATH: ���� �������, ������ ���������: " nm)) + ) + ) + ) + ) + ) + (xrb-pr "XRPATH_DONE") +) + +;; ============================================================ +;; XRUNBIND � ����������� ������ ������������� (�����) ������. +;; ������� ������� �� Bind: Detach �� ������� ��������� �����, +;; ������� ���� � ��, ���� �� ����� ���. ������ ���� ������� +;; �����, ������� �� ������������� ��-�� ����� ��������� ������. +;; ============================================================ +(defun c:XRUNBIND ( / doc blks names nm f e ok fail) + (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) + (setq blks (vla-get-Blocks doc)) + (setq f (open *xrb-log* "w")) + (xrb-w f (strcat "XRUNBIND (������������ ����� ������) " (vla-get-Name doc))) + (xrb-w f "-----------------------------------------------") + + ;; ������ 1 � ������ ������: ������� ����� ������������� ������. + ;; ������ ������� �������� ���������, ���� �� ��� ��� vlax-for. + (setq names nil) + (vlax-for b blks + (if (= (vla-get-IsXRef b) :vlax-true) + (progn + (setq nm (vla-get-Name b)) + (if (not (xrb-resolved-p nm)) + (setq names (cons nm names)))))) + (setq names (reverse names)) + (xrb-w f (strcat "����� ������ �������: " (itoa (length names)))) + + ;; ������ 2 � �������� �� ������ + (setq ok 0 fail 0) + (foreach nm names + (setq e (vl-catch-all-apply 'vla-Detach (list (vla-item blks nm)))) + (if (vl-catch-all-error-p e) + (progn (setq fail (1+ fail)) + (xrb-w f (strcat "������\t" nm "\t" (vl-catch-all-error-message e)))) + (progn (setq ok (1+ ok)) + (xrb-w f (strcat "�����������\t" nm))))) + + (xrb-w f "-----------------------------------------------") + (xrb-w f (strcat "�����������: " (itoa ok) ", ������: " (itoa fail))) + (close f) + (xrb-pr (strcat "XRUNBIND: ����������� " (itoa ok) ", ������ " (itoa fail) + " -> " *xrb-log*)) + (xrb-pr "XRUNBIND_DONE")) + +;; ============================================================ +;; XRDETACH � ����������� ��� ������ (� ��������������) +;; ============================================================ +(defun c:XRDETACH ( / doc blks names nm f e ok fail ans) + (initget "�� ���") + (setq ans (getkword "\n����������� ��� ������� ������ ��� ���������? [��/���] <���>: ")) + (if (= ans "��") + (progn + (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) + (setq blks (vla-get-Blocks doc)) + (setq f (open *xrb-log* "w")) + (xrb-w f (strcat "XRDETACH (������������ ���� ������) " (vla-get-Name doc))) + (xrb-w f "-----------------------------------------------") + + (setq names (xrb-xref-names doc)) ; ������ 1: ������ + (setq ok 0 fail 0) + (foreach nm names ; ������ 2: �������� + (setq e (vl-catch-all-apply 'vla-Detach (list (vla-item blks nm)))) + (if (vl-catch-all-error-p e) + (progn (setq fail (1+ fail)) + (xrb-w f (strcat "������\t" nm))) + (progn (setq ok (1+ ok)) + (xrb-w f (strcat "�����������\t" nm))))) + (xrb-w f "-----------------------------------------------") + (xrb-w f (strcat "�����������: " (itoa ok) ", ������: " (itoa fail))) + (close f) + (xrb-pr (strcat "XRDETACH: ����������� " (itoa ok) ", ������ " (itoa fail) + " -> " *xrb-log*))) + (xrb-pr "XRDETACH: ��������")) + (xrb-pr "XRDETACH_DONE")) + +(princ "\nXRBIND.lsp ��������. �������: XRBIND XRBIND1 XRLIST XRPATH XRUNBIND XRDETACH") +(princ) diff --git a/XRBIND.lsp.utf8 b/XRBIND.lsp.utf8 new file mode 100644 index 0000000..9871e70 --- /dev/null +++ b/XRBIND.lsp.utf8 @@ -0,0 +1,233 @@ +;;; ============================================================ +;;; XRBIND.lsp — Внешние ссылки AutoCAD: внедрение / ремонт +;;; AutoCAD 2025/2026. Dionis / Олег. +;;; Команды: +;;; XRBIND — внедрить ВСЕ РАЗРЕШЁННЫЕ ссылки в активном чертеже +;;; XRBIND1 — внедрить одну ссылку (спросит имя) +;;; XRLIST — список ссылок со статусом, без изменений чертежа +;;; XRPATH — сменить путь битой ссылки (указать, где лежит файл) +;;; XRUNBIND — ОТСОЕДИНИТЬ только неразрешённые (битые) ссылки +;;; XRDETACH — отсоединить ВСЕ ссылки (с подтверждением) +;;; Отчёт: %TEMP%\xrbind_report.txt +;;; ============================================================ + +(setq *xrb-log* (strcat (getenv "TEMP") "\\xrbind_report.txt")) + +;; ---- список имён внешних ссылок (порядок как в чертеже) ---- +(defun xrb-xref-names (doc / lst) + (setq lst nil) + (vlax-for b (vla-get-Blocks doc) + (if (= (vla-get-IsXRef b) :vlax-true) + (setq lst (cons (vla-get-Name b) lst)))) + (reverse lst)) + +;; ---- разрешена ли ссылка: бит 32 в флагах блока ---- +(defun xrb-resolved-p (nm / e fl) + (setq e (entget (tblobjname "BLOCK" nm))) + (if e + (progn (setq fl (cdr (assoc 70 e))) + (if (= 32 (logand 32 fl)) T nil)) + nil)) + +;; ---- это вообще xref-блок? (бит 4) ---- +(defun xrb-isxref-p (nm / e fl) + (setq e (entget (tblobjname "BLOCK" nm))) + (if e + (progn (setq fl (cdr (assoc 70 e))) + (if (= 4 (logand 4 fl)) T nil)) + nil)) + +(defun xrb-w (f s) (write-line s f)) +(defun xrb-pr (s) (princ (strcat "\n" s))) + +;; ============================================================ +;; XRLIST — отчёт по ссылкам (без изменений чертежа) +;; ============================================================ +(defun c:XRLIST ( / doc names nm f n res tot) + (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) + (setq f (open *xrb-log* "w")) + (xrb-w f (strcat "Отчёт по внешним ссылкам " (vla-get-Name doc))) + (xrb-w f (strcat "Полный путь: " (vla-get-FullName doc))) + (xrb-w f "-----------------------------------------------") + (setq names (xrb-xref-names doc)) + (setq tot (length names) n 0) + (foreach nm names + (setq n (1+ n)) + (setq res (if (xrb-resolved-p nm) "РАЗРЕШЕНА" "НЕ РАЗРЕШЕНА")) + (xrb-w f (strcat (itoa n) ".\t" res "\t" nm))) + (xrb-w f "-----------------------------------------------") + (xrb-w f (strcat "Всего ссылок: " (itoa tot))) + (close f) + (xrb-pr (strcat "XRLIST: ссылок " (itoa tot) " -> " *xrb-log*)) + (xrb-pr "XRLIST_DONE")) + +;; ============================================================ +;; XRBIND — внедрить все разрешённые ссылки +;; ============================================================ +(defun c:XRBIND ( / doc f names nm before after ok fail skip oldecho oldfiledia) + (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) + (setq f (open *xrb-log* "w")) + (xrb-w f (strcat "XBIND (внедрение ссылок) " (vla-get-Name doc))) + (xrb-w f "-----------------------------------------------") + (setq oldecho (getvar "CMDECHO")) + (setq oldfiledia (getvar "FILEDIA")) + (setvar "CMDECHO" 0) + (setvar "FILEDIA" 0) + + (setq names (xrb-xref-names doc)) + (setq before (length names)) + (xrb-w f (strcat "Ссылок до операции: " (itoa before))) + + (setq ok 0 fail 0 skip 0) + (foreach nm names + (if (xrb-resolved-p nm) + (progn + ;; ВНЕДРЕНИЕ: имя + пустой Enter (без него команда виснет на повторном + ;; запросе списка имён). Проверено на реальном комплекте СПИС. + (command "_.-XREF" "_B" nm "") + (if (xrb-resolved-p nm) + (progn (setq fail (1+ fail)) + (xrb-w f (strcat "НЕ УДАЛОСЬ\t" nm))) + (progn (setq ok (1+ ok)) + (xrb-w f (strcat "ВНЕДРЕНО\t" nm))))) + (progn (setq skip (1+ skip)) + (xrb-w f (strcat "ПРОПУЩЕНА (не разрешена)\t" nm))))) + + (setq after (length (xrb-xref-names doc))) + (xrb-w f "-----------------------------------------------") + (xrb-w f (strcat "Внедрено: " (itoa ok))) + (xrb-w f (strcat "Не удалось: " (itoa fail))) + (xrb-w f (strcat "Пропущено (битые): " (itoa skip))) + (xrb-w f (strcat "Ссылок осталось: " (itoa after) " (было " (itoa before) ")")) + (close f) + + (setvar "CMDECHO" oldecho) + (setvar "FILEDIA" oldfiledia) + (xrb-pr (strcat "XRBIND: внедрено " (itoa ok) ", не удалось " (itoa fail) + ", пропущено " (itoa skip) ", осталось " (itoa after) + " -> " *xrb-log*)) + (xrb-pr "XRBIND_DONE")) + +;; ============================================================ +;; XRBIND1 — внедрить одну ссылку по имени +;; ============================================================ +(defun c:XRBIND1 ( / nm oldfiledia) + (setq nm (getstring T "\nИмя внешней ссылки для внедрения: ")) + (if (and nm (/= nm "")) + (if (xrb-resolved-p nm) + (progn + (setq oldfiledia (getvar "FILEDIA")) + (setvar "FILEDIA" 0) + (command "_.-XREF" "_B" nm "") + (setvar "FILEDIA" oldfiledia) + (if (xrb-resolved-p nm) + (xrb-pr (strcat "XRBIND1: не удалось внедрить " nm)) + (xrb-pr (strcat "XRBIND1: внедрено " nm)))) + (xrb-pr (strcat "XRBIND1: ссылка не разрешена или не найдена: " nm))) + (xrb-pr "XRBIND1: имя не задано")) + (xrb-pr "XRBIND1_DONE")) + +;; ============================================================ +;; XRPATH — сменить путь ссылки (починка «переехавшего» файла) +;; ============================================================ +(defun c:XRPATH ( / nm newpath oldfiledia) + (setq nm (getstring T "\nИмя ссылки, которой меняем путь: ")) + (cond + ((= nm "") (xrb-pr "XRPATH: имя пустое")) + ((not (xrb-isxref-p nm)) (xrb-pr "XRPATH: это не внешняя ссылка")) + (T + (setq newpath (getstring T "\nНовый полный путь к dwg: ")) + (cond + ((= newpath "") (xrb-pr "XRPATH: путь пустой — не изменён")) + ((not (findfile newpath)) (xrb-pr "XRPATH: файл не найден — путь не изменён")) + (T + (setq oldfiledia (getvar "FILEDIA")) + (setvar "FILEDIA" 0) + (command "_.-XREF" "_P" nm newpath "") + (setvar "FILEDIA" oldfiledia) + (if (xrb-resolved-p nm) + (xrb-pr (strcat "XRPATH: ссылка всё ещё не разрешена: " nm)) + (xrb-pr (strcat "XRPATH: путь обновлён, ссылка разрешена: " nm)) + ) + ) + ) + ) + ) + (xrb-pr "XRPATH_DONE") +) + +;; ============================================================ +;; XRUNBIND — ОТСОЕДИНИТЬ только неразрешённые (битые) ссылки. +;; Главное отличие от Bind: Detach НЕ требует исходного файла, +;; поэтому берёт и то, чего на диске нет. Именно этим лечится +;; чертёж, который не привязывается из-за битых вложенных ссылок. +;; ============================================================ +(defun c:XRUNBIND ( / doc blks names nm f e ok fail) + (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) + (setq blks (vla-get-Blocks doc)) + (setq f (open *xrb-log* "w")) + (xrb-w f (strcat "XRUNBIND (отсоединение битых ссылок) " (vla-get-Name doc))) + (xrb-w f "-----------------------------------------------") + + ;; ПРОХОД 1 — только чтение: собрать имена НЕразрешённых ссылок. + ;; Нельзя удалять элементы коллекции, пока по ней идёт vlax-for. + (setq names nil) + (vlax-for b blks + (if (= (vla-get-IsXRef b) :vlax-true) + (progn + (setq nm (vla-get-Name b)) + (if (not (xrb-resolved-p nm)) + (setq names (cons nm names)))))) + (setq names (reverse names)) + (xrb-w f (strcat "Битых ссылок найдено: " (itoa (length names)))) + + ;; ПРОХОД 2 — удаление по именам + (setq ok 0 fail 0) + (foreach nm names + (setq e (vl-catch-all-apply 'vla-Detach (list (vla-item blks nm)))) + (if (vl-catch-all-error-p e) + (progn (setq fail (1+ fail)) + (xrb-w f (strcat "ОШИБКА\t" nm "\t" (vl-catch-all-error-message e)))) + (progn (setq ok (1+ ok)) + (xrb-w f (strcat "ОТСОЕДИНЕНО\t" nm))))) + + (xrb-w f "-----------------------------------------------") + (xrb-w f (strcat "Отсоединено: " (itoa ok) ", ошибок: " (itoa fail))) + (close f) + (xrb-pr (strcat "XRUNBIND: отсоединено " (itoa ok) ", ошибок " (itoa fail) + " -> " *xrb-log*)) + (xrb-pr "XRUNBIND_DONE")) + +;; ============================================================ +;; XRDETACH — отсоединить ВСЕ ссылки (с подтверждением) +;; ============================================================ +(defun c:XRDETACH ( / doc blks names nm f e ok fail ans) + (initget "Да Нет") + (setq ans (getkword "\nОтсоединить ВСЕ внешние ссылки без внедрения? [Да/Нет] <Нет>: ")) + (if (= ans "Да") + (progn + (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) + (setq blks (vla-get-Blocks doc)) + (setq f (open *xrb-log* "w")) + (xrb-w f (strcat "XRDETACH (отсоединение всех ссылок) " (vla-get-Name doc))) + (xrb-w f "-----------------------------------------------") + + (setq names (xrb-xref-names doc)) ; проход 1: чтение + (setq ok 0 fail 0) + (foreach nm names ; проход 2: удаление + (setq e (vl-catch-all-apply 'vla-Detach (list (vla-item blks nm)))) + (if (vl-catch-all-error-p e) + (progn (setq fail (1+ fail)) + (xrb-w f (strcat "ОШИБКА\t" nm))) + (progn (setq ok (1+ ok)) + (xrb-w f (strcat "ОТСОЕДИНЕНО\t" nm))))) + (xrb-w f "-----------------------------------------------") + (xrb-w f (strcat "Отсоединено: " (itoa ok) ", ошибок: " (itoa fail))) + (close f) + (xrb-pr (strcat "XRDETACH: отсоединено " (itoa ok) ", ошибок " (itoa fail) + " -> " *xrb-log*))) + (xrb-pr "XRDETACH: отменено")) + (xrb-pr "XRDETACH_DONE")) + +(princ "\nXRBIND.lsp загружен. Команды: XRBIND XRBIND1 XRLIST XRPATH XRUNBIND XRDETACH") +(princ) diff --git a/XRCOPYVISIBLE.lsp b/XRCOPYVISIBLE.lsp new file mode 100644 index 0000000..7998ae9 --- /dev/null +++ b/XRCOPYVISIBLE.lsp @@ -0,0 +1,133 @@ +;;; ============================================================ +;;; XRCOPYVISIBLE.lsp � ����������� ����������� ��������� XREF +;;; +;;; ������� XRCOPYVISIBLE �������� ������� ������� ������ � ������� +;;; ������� � ��������� ��������� ������ ������� � DWG � ������� ����. +;;; ��������� XREF �� �������: ����� �� ������ ��������. +;;; +;;; �����: +;;; - ������� �� ����������� � �� �������� �������� ������; +;;; - ������� DWG ������ ���� ������� �� �������; +;;; - ����� ���������� ���� ��������� ��������� ��������� � ��������� DWG; +;;; - ���������, ������� ���� ������ � ����� ��������� XREF, �� ����������. +;;; ============================================================ + +(vl-load-com) + +(defun xrcv:tools-dir (/ f) + (if (setq f (findfile "XRCOPYVISIBLE.lsp")) + (vl-string-translate "/" "\\" (vl-filename-directory f)) + "G:\\��� ����\\lisp-code\\SPIS" + ) +) + +(defun xrcv:python (/ tools local) + (setq tools (xrcv:tools-dir)) + (setq local (strcat (vl-string-right-trim "\\/" tools) "\\.venv\\Scripts\\pythonw.exe")) + (if (findfile local) + local + (progn + (setq local (strcat (vl-string-right-trim "\\/" tools) "\\.venv\\Scripts\\python.exe")) + (if (findfile local) local "python") + ) + ) +) + +(defun xrcv:xref-object-p (obj / name blk) + (and (= (vla-get-ObjectName obj) "AcDbBlockReference") + (not (vl-catch-all-error-p + (setq blk (vl-catch-all-apply 'vla-Item + (list (vla-get-Blocks (vla-get-ActiveDocument (vlax-get-acad-object))) + (vla-get-Name obj)))))) + (= (vla-get-IsXRef blk) :vlax-true) + ) +) + +(defun xrcv:pt-value (pt i) + (rtos (nth i (vlax-safearray->list (vlax-variant-value pt))) 2 10) +) + +(defun c:XRCOPYVISIBLE (/ doc pick en obj blk source target ip sx sy sz rot tools script vpython cmd shell res) + (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) + (setq target (vla-get-FullName doc)) + (cond + ((or (not target) (= target "")) + (prompt "\nXRCOPYVISIBLE: ������� ��������� ������� DWG (SAVEAS).") + ) + (T + (prompt "\n�������� ������� ������� ������ � ������: ") + (setq pick (entsel)) + (cond + ((not pick) + (prompt "\nXRCOPYVISIBLE: ������ �� ������.") + ) + (T + (setq en (car pick) + obj (vlax-ename->vla-object en)) + (cond + ((not (xrcv:xref-object-p obj)) + (prompt "\nXRCOPYVISIBLE: ��������� ������ �� �������� ������� ������� �������.") + ) + (T + (setq blk (vla-Item (vla-get-Blocks doc) (vla-get-Name obj))) + (setq source (vla-get-Path blk)) + (cond + ((or (not source) (= source "") (not (findfile source))) + (prompt "\nXRCOPYVISIBLE: �������� DWG ������� ������ �� ������. ���� �� ������.") + ) + (T + (setq ip (vla-get-InsertionPoint obj) + sx (rtos (vla-get-XScaleFactor obj) 2 10) + sy (rtos (vla-get-YScaleFactor obj) 2 10) + sz (rtos (vla-get-ZScaleFactor obj) 2 10) + rot (rtos (vla-get-Rotation obj) 2 10) + tools (xrcv:tools-dir) + script (strcat tools "\\xr_copy_visible.py") + vpython (xrcv:python)) + (cond + ((not (findfile script)) + (prompt (strcat "\nXRCOPYVISIBLE: �� ������ ������: " script)) + ) + (T + (prompt "\n����� ����������� ����������� ��������� ������� ������.") + (prompt "\n��������� ������ (������� �����) ����� ���������.") + (initget "Yes No") + (if (/= (getkword "\n��������� �����������? [Yes/No] : ") "Yes") + (prompt "\nXRCOPYVISIBLE: ��������. ������ �� ��������.") + (progn + (setq cmd (strcat "\"" vpython "\" \"" script "\" \"" + source "\" \"" target "\" " + (xrcv:pt-value ip 0) " " (xrcv:pt-value ip 1) " " (xrcv:pt-value ip 2) " " + sx " " sy " " sz " " rot)) + (setq shell (vl-catch-all-apply 'vlax-create-object (list "WScript.Shell"))) + (if (vl-catch-all-error-p shell) + (prompt (strcat "\nXRCOPYVISIBLE: ������ WScript.Shell: " (vl-catch-all-error-message shell))) + (progn + (setq res (vl-catch-all-apply 'vlax-invoke (list shell "Run" cmd 0 0))) + (vlax-release-object shell) + (if (vl-catch-all-error-p res) + (prompt (strcat "\nXRCOPYVISIBLE: �� ��������: " (vl-catch-all-error-message res))) + (progn + (prompt "\nXRCOPYVISIBLE: �������� � ����.") + (prompt (strcat "\n���: " (getenv "TEMP") "\\xr_copy_visible_log.txt")) + (prompt "\n����� ���������� ��������� ��������� ���� � ��������� DWG.")) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + (princ) +) + +(princ "\nXRCOPYVISIBLE.lsp ��������. �������: XRCOPYVISIBLE") +(princ) diff --git a/XRCOPYVISIBLE.lsp.utf8 b/XRCOPYVISIBLE.lsp.utf8 new file mode 100644 index 0000000..9b6c8d3 --- /dev/null +++ b/XRCOPYVISIBLE.lsp.utf8 @@ -0,0 +1,133 @@ +;;; ============================================================ +;;; XRCOPYVISIBLE.lsp — копирование собственной геометрии XREF +;;; +;;; Команда XRCOPYVISIBLE выбирает ВЕРХНЮЮ внешнюю ссылку в текущем +;;; чертеже и переносит доступные прямые объекты её DWG в текущий файл. +;;; Вложенные XREF не трогает: битые не мешают операции. +;;; +;;; ВАЖНО: +;;; - команда не отсоединяет и не внедряет исходную ссылку; +;;; - текущий DWG должен быть сохранён до запуска; +;;; - после выполнения надо визуально проверить результат и сохранить DWG; +;;; - геометрия, которая была только в любой вложенной XREF, не копируется. +;;; ============================================================ + +(vl-load-com) + +(defun xrcv:tools-dir (/ f) + (if (setq f (findfile "XRCOPYVISIBLE.lsp")) + (vl-string-translate "/" "\\" (vl-filename-directory f)) + "G:\\Мой диск\\lisp-code\\SPIS" + ) +) + +(defun xrcv:python (/ tools local) + (setq tools (xrcv:tools-dir)) + (setq local (strcat (vl-string-right-trim "\\/" tools) "\\.venv\\Scripts\\pythonw.exe")) + (if (findfile local) + local + (progn + (setq local (strcat (vl-string-right-trim "\\/" tools) "\\.venv\\Scripts\\python.exe")) + (if (findfile local) local "python") + ) + ) +) + +(defun xrcv:xref-object-p (obj / name blk) + (and (= (vla-get-ObjectName obj) "AcDbBlockReference") + (not (vl-catch-all-error-p + (setq blk (vl-catch-all-apply 'vla-Item + (list (vla-get-Blocks (vla-get-ActiveDocument (vlax-get-acad-object))) + (vla-get-Name obj)))))) + (= (vla-get-IsXRef blk) :vlax-true) + ) +) + +(defun xrcv:pt-value (pt i) + (rtos (nth i (vlax-safearray->list (vlax-variant-value pt))) 2 10) +) + +(defun c:XRCOPYVISIBLE (/ doc pick en obj blk source target ip sx sy sz rot tools script vpython cmd shell res) + (setq doc (vla-get-ActiveDocument (vlax-get-acad-object))) + (setq target (vla-get-FullName doc)) + (cond + ((or (not target) (= target "")) + (prompt "\nXRCOPYVISIBLE: сначала сохраните текущий DWG (SAVEAS).") + ) + (T + (prompt "\nВыберите ВЕРХНЮЮ внешнюю ссылку с планом: ") + (setq pick (entsel)) + (cond + ((not pick) + (prompt "\nXRCOPYVISIBLE: объект не выбран.") + ) + (T + (setq en (car pick) + obj (vlax-ename->vla-object en)) + (cond + ((not (xrcv:xref-object-p obj)) + (prompt "\nXRCOPYVISIBLE: выбранный объект не является верхней внешней ссылкой.") + ) + (T + (setq blk (vla-Item (vla-get-Blocks doc) (vla-get-Name obj))) + (setq source (vla-get-Path blk)) + (cond + ((or (not source) (= source "") (not (findfile source))) + (prompt "\nXRCOPYVISIBLE: исходный DWG верхней ссылки не найден. Путь не изменён.") + ) + (T + (setq ip (vla-get-InsertionPoint obj) + sx (rtos (vla-get-XScaleFactor obj) 2 10) + sy (rtos (vla-get-YScaleFactor obj) 2 10) + sz (rtos (vla-get-ZScaleFactor obj) 2 10) + rot (rtos (vla-get-Rotation obj) 2 10) + tools (xrcv:tools-dir) + script (strcat tools "\\xr_copy_visible.py") + vpython (xrcv:python)) + (cond + ((not (findfile script)) + (prompt (strcat "\nXRCOPYVISIBLE: не найден скрипт: " script)) + ) + (T + (prompt "\nБудет скопирована собственная геометрия верхней ссылки.") + (prompt "\nВложенные ссылки (включая битые) будут пропущены.") + (initget "Yes No") + (if (/= (getkword "\nЗапустить копирование? [Yes/No] : ") "Yes") + (prompt "\nXRCOPYVISIBLE: отменено. Ничего не изменено.") + (progn + (setq cmd (strcat "\"" vpython "\" \"" script "\" \"" + source "\" \"" target "\" " + (xrcv:pt-value ip 0) " " (xrcv:pt-value ip 1) " " (xrcv:pt-value ip 2) " " + sx " " sy " " sz " " rot)) + (setq shell (vl-catch-all-apply 'vlax-create-object (list "WScript.Shell"))) + (if (vl-catch-all-error-p shell) + (prompt (strcat "\nXRCOPYVISIBLE: ошибка WScript.Shell: " (vl-catch-all-error-message shell))) + (progn + (setq res (vl-catch-all-apply 'vlax-invoke (list shell "Run" cmd 0 0))) + (vlax-release-object shell) + (if (vl-catch-all-error-p res) + (prompt (strcat "\nXRCOPYVISIBLE: не запущено: " (vl-catch-all-error-message res))) + (progn + (prompt "\nXRCOPYVISIBLE: запущено в фоне.") + (prompt (strcat "\nЛог: " (getenv "TEMP") "\\xr_copy_visible_log.txt")) + (prompt "\nПосле завершения визуально проверьте план и сохраните DWG.")) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + (princ) +) + +(princ "\nXRCOPYVISIBLE.lsp загружен. Команда: XRCOPYVISIBLE") +(princ) diff --git a/__pycache__-20261006T160044Z-1-001.zip b/__pycache__-20261006T160044Z-1-001.zip new file mode 100644 index 0000000..ad3e9fc --- /dev/null +++ b/__pycache__-20261006T160044Z-1-001.zip Binary files differ diff --git a/build_discipline_sets.py b/build_discipline_sets.py new file mode 100644 index 0000000..1e23b77 --- /dev/null +++ b/build_discipline_sets.py @@ -0,0 +1,303 @@ +"""Создание комплектов дисциплин с взаимными внешними ссылками. + +Итоговая схема (все ссылки Overlay, вставка 0,0,0, масштаб 1:1): + + ЭТР = АР + СС + ВК + ОВ + СС = АР + ВК + ОВ + ЭТР + ВК = АР + СС + ОВ + ЭТР + ОВ = АР + СС + ВК + ЭТР + +АР — основа. Она ни на что не ссылается, но подключается во все разделы. +Каждый раздел не ссылается сам на себя — только на 3 смежных + АР. + +Только Overlay — безопасно, циклы не блокируют отображение. + +Запуск из AutoCAD: + команда SPISRUNSETS (см. SPIS_RUN_SETS.lsp) +Запуск вручную: + python build_discipline_sets.py "путь\к\корню" "Корпус" +""" + +import sys +import time +from pathlib import Path + +import pythoncom +from win32com.client import VARIANT, gencache + +AP = "АР" +SECTIONS = ["ЭТР", "СС", "ВК", "ОВ"] + +SCHEME = { + "ЭТР": [AP, "СС", "ВК", "ОВ"], + "СС": [AP, "ВК", "ОВ", "ЭТР"], + "ВК": [AP, "СС", "ОВ", "ЭТР"], + "ОВ": [AP, "СС", "ВК", "ЭТР"], +} + +X, Y, Z = 0.0, 0.0, 0.0 +SCALE = 1.0 + + +def log(msg: str) -> None: + print(msg, flush=True) + + +def retry(fn, *args, **kwargs): + """Повтор COM-вызова при отклонении (RPC_E_CALL_REJECTED). + + Точная копия из fix_larus2.py — проверено, работает. + """ + for _ in range(30): + try: + return fn(*args, **kwargs) + except Exception as e: + if "-2147418111" in str(e) or "отклонен" in str(e): + time.sleep(4.0) + continue + raise + raise RuntimeError("retry: не удалось") + + +def attach_until_ok(doc, path, name, pt, attempts=30): + """AttachExternalReference с повтором при отклонении.""" + for _ in range(attempts): + try: + doc.ModelSpace.AttachExternalReference( + path, name, pt, SCALE, SCALE, SCALE, 0.0, True) + return True + except Exception as e: + s = str(e) + if "-2147418111" in s or "отклонен" in s or "rejected" in s.lower(): + time.sleep(4.0) + continue + raise + raise RuntimeError(f"attach_until_ok: не удалось {name} после {attempts} попыток") + + +def point(): + return VARIANT(pythoncom.VT_ARRAY | pythoncom.VT_R8, [X, Y, Z]) + + +def detach_all_xrefs(doc): + """Отключить все внешние ссылки (очистка от дубликатов прошлых запусков).""" + try: + for blk in list(doc.Blocks): + if blk.IsXRef: + try: + blk.Detach() + except Exception: + pass + except Exception: + pass + + +def current_refs(doc) -> set: + result = set() + try: + for blk in doc.Blocks: + if blk.IsXRef: + result.add(blk.Name.upper()) + except Exception: + pass + return result + + +def find_ap_file(root: Path) -> Path | None: + ap_dir = root / AP + if not ap_dir.is_dir(): + return None + for f in ap_dir.glob(f"{AP} *.dwg"): + return f + p = ap_dir / f"{AP}.dwg" + if p.is_file(): + return p + for f in ap_dir.glob("*.dwg"): + return f + return None + + +def source_file(section: str, corpus: str, root: Path) -> Path | None: + if section == AP: + return find_ap_file(root) + p = root / section / f"{section} {corpus}.dwg" + return p if p.is_file() else None + + +def target_path(root: Path, section: str, corpus: str) -> Path: + return root / section / f"{section} {corpus}.dwg" + + +def main() -> int: + root = Path(sys.argv[1]) if len(sys.argv) > 1 else Path( + r"C:\Users\o.mashkancev\Desktop\Гранель Дубровка\TEST\Ларус Капитал") + corpus = sys.argv[2] if len(sys.argv) > 2 else "Ларус Капитал" + + if not root.is_dir(): + log(f"ERROR: папка не найдена: {root}") + return 1 + + ap_file = find_ap_file(root) + if ap_file is None: + log(f"ERROR: не найден базовый файл АР в {root / AP}") + return 1 + + log(f"Архитектура: {ap_file.name}") + log(f"Корпус: {corpus}") + log(f"Корень: {root}") + + pythoncom.CoInitialize() + try: + acad = gencache.EnsureDispatch("AutoCAD.Application") + except Exception as e: + log(f"ERROR: AutoCAD недоступен: {e}") + return 1 + + log(f"Connected: AutoCAD {acad.Version}") + + # Закрываем ВСЕ открытые документы — иначе «висящие» от прошлых + # запусков блокируют сохранение (файлы .dwl/.dwl2). + try: + for doc in list(acad.Documents): + try: + doc.Close(False) + except Exception: + pass + time.sleep(2) + log(f" закрыто открытых документов: {len(acad.Documents)}") + except Exception: + pass + + # FILEDIA: сохраняем и гасим диалоги + try: + saved_filedia = acad.ActiveDocument.GetVariable("FILEDIA") + except Exception: + saved_filedia = 1 + try: + acad.ActiveDocument.SetVariable("FILEDIA", 0) + except Exception: + pass + + # Шаг 1. Создаём пустые файлы разделов (если нет). + for section in SECTIONS: + target = target_path(root, section, corpus) + target.parent.mkdir(parents=True, exist_ok=True) + if target.exists(): + log(f" файл уже существует: {target.name}") + continue + try: + doc = retry(acad.Documents.Add) + except Exception as e: + log(f" Documents.Add for {section}: {e}") + return 1 + try: + doc.SetVariable("INSUNITS", 4) + doc.SetVariable("MEASUREMENT", 1) + except Exception: + pass + try: + doc.SaveAs(str(target), 24) + log(f" создан: {target.name}") + except Exception as e: + log(f" SaveAs failed for {section}: {e}") + try: + retry(doc.Close, False) + except Exception: + pass + return 1 + try: + retry(doc.Close, False) + except Exception: + pass + + # Шаг 2. Подключаем ссылки по схеме. + # Точный алгоритм fix_larus2.py: открыть → attach + sleep(10) → Close(True) → sleep(5) + for section in SECTIONS: + path = target_path(root, section, corpus) + try: + doc = retry(acad.Documents.Open, str(path)) + except Exception as e: + log(f" Open failed for {path.name}: {e}") + return 1 + + time.sleep(3) + # Отвязываем ВСЕ старые xref (дубликаты от прошлых запусков + # с именами типа "СС Ларус Капитал" вместо "СС"). + # Без очистки AutoCAD создаёт дубли и не может сохранить файл. + detach_all_xrefs(doc) + time.sleep(2) + have = current_refs(doc) + log(f" {path.name}: после очистки ссылок {len(have)}") + + for other in SCHEME[section]: + if other == section: + continue + if other.upper() in have: + log(f" пропущен (уже есть): {other}") + continue + src = source_file(other, corpus, root) + if not src: + log(f" пропущен (нет файла): {other}") + continue + try: + pt = point() + attach_until_ok(doc, str(src), other, pt) + log(f" {path.name} += {other}") + have.add(other.upper()) + time.sleep(10.0) + except Exception as e: + log(f" {path.name} += {other}: {e}") + + try: + retry(doc.Close, True) + log(f" сохранён: {path.name}") + except Exception as e: + log(f" ОШИБКА сохранения {path.name}: {e}") + try: + retry(doc.Close, False) + except Exception: + pass + + time.sleep(5.0) + + # Шаг 3. Проверяем результат. + ok = 0 + for section in SECTIONS: + path = target_path(root, section, corpus) + try: + doc = retry(acad.Documents.Open, str(path)) + except Exception: + continue + time.sleep(1) + have = current_refs(doc) + want = set(s.upper() for s in SCHEME[section]) + want.discard(section.upper()) + missing = want - have + if not missing: + ok += 1 + log(f" {path.name}: {len(have)} ссылок — OK") + else: + log(f" {path.name}: {len(have)} ссылок, не хватает {sorted(missing)}") + try: + retry(doc.Close, False) + except Exception: + pass + + # Восстанавливаем FILEDIA + try: + acad.ActiveDocument.SetVariable("FILEDIA", saved_filedia) + except Exception: + pass + + log("") + if ok == len(SECTIONS): + log(f"ГОТОВО: {ok}/{len(SECTIONS)} файлов со всеми ссылками.") + else: + log(f"ГОТОВО: {ok}/{len(SECTIONS)} файлов со всеми ссылками.") + log("Запустите повторно для добивки недостающих.") + log("Не забудьте: SPISATTACHSETS — подключить АР и разделы в СПИС.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/run_sets_launcher.py b/run_sets_launcher.py new file mode 100644 index 0000000..7cfb8f7 --- /dev/null +++ b/run_sets_launcher.py @@ -0,0 +1,61 @@ +"""Запуск сборки комплекта дисциплин из AutoCAD. + +Вызывается командой SPISRUNSETS через WScript.Shell.Run. +Запускает рабочий скрипт через pythonw.exe (без консольного окна) +с перенаправлением вывода в лог-файл. +""" +import os +import subprocess +import sys +from pathlib import Path + +TOOLS_DIR = Path(__file__).resolve().parent +WORKER = TOOLS_DIR / "build_discipline_sets.py" +VENV_PY = TOOLS_DIR / ".venv" / "Scripts" / "python.exe" +LOG = Path(os.environ.get("TEMP", ".")) / "spis_sets_log.txt" + + +def main() -> int: + if len(sys.argv) < 3: + print("Использование: python run_sets_launcher.py <корень> <корпус>") + return 2 + + root = sys.argv[1].strip('"') + corpus = sys.argv[2].strip('"') + + if not WORKER.exists(): + with open(LOG, "w", encoding="utf-8") as f: + f.write(f"ОШИБКА: не найден рабочий скрипт:\n{WORKER}\n") + return 1 + + # pythonw.exe — без консольного окна. Если нет — берём python.exe. + pythonw = VENV_PY.with_name("pythonw.exe") + python = pythonw if pythonw.exists() else VENV_PY + + if not python.exists(): + python = "python" + + try: + log = open(LOG, "w", encoding="utf-8") + subprocess.Popen( + [str(python), str(WORKER), root, corpus], + creationflags=subprocess.CREATE_NO_WINDOW, + close_fds=True, + cwd=str(TOOLS_DIR), + stdout=log, + stderr=subprocess.STDOUT, + ) + log.close() + except Exception as exc: + try: + log.close() + except Exception: + pass + with open(LOG, "a", encoding="utf-8") as f: + f.write(f"\nОШИБКА запуска: {exc}\n") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/venv-20261006T160045Z-1-001.zip b/venv-20261006T160045Z-1-001.zip new file mode 100644 index 0000000..ae3e295 --- /dev/null +++ b/venv-20261006T160045Z-1-001.zip Binary files differ diff --git "a/\320\230\320\275\321\201\321\202\321\200\321\203\320\272\321\206\320\270\321\217_\320\230\320\275\321\201\321\202\321\200\321\203\320\274\320\265\320\275\321\202\321\213_\320\241\320\237\320\230\320\241.docx" "b/\320\230\320\275\321\201\321\202\321\200\321\203\320\272\321\206\320\270\321\217_\320\230\320\275\321\201\321\202\321\200\321\203\320\274\320\265\320\275\321\202\321\213_\320\241\320\237\320\230\320\241.docx" new file mode 100644 index 0000000..24384ca --- /dev/null +++ "b/\320\230\320\275\321\201\321\202\321\200\321\203\320\272\321\206\320\270\321\217_\320\230\320\275\321\201\321\202\321\200\321\203\320\274\320\265\320\275\321\202\321\213_\320\241\320\237\320\230\320\241.docx" Binary files differ