четверг, 24 ноября 2011 г.

Избавляемся от объектов ProSteel в чертежах. Быстро и надежно.

Начну издалека. Несколько лет назад группа строителей нашего проектного института тестировала программный продукт для проектирования строительной части Bentley ProSteel (в то время он был еще Bentley AutoPLANT Structural). Продукт нам не подошел, но последствия его тестирования мы расхлебываем до сих пор. Я ни в коем случае не хочу делать антирекламу компании Bentley, просто опишу один из способов борьбы с последствиями, на мой взгляд являющийся оптимальным.

Итак, о последствиях.

Некоторое время спустя проектировщики стали жаловаться, что у них часто вылетает AutoCAD, очень сильно тормозит при работе с небольшими чертежами и т.д. Начали смотреть, да, действительно все плохо. Основные симптомы выявленные мной:
  • Во-первых, объем небольшого по графике чертежа становится неприемлемым, чертежи вырастают до 10-15-20-30... МБайт. Самый ужасный чертеж, который я встречал весил 258 МБ.
  • Во-вторых, при копировании стандартных примитивов AutoCAD из чертежа в чертеж, AutoCAD вылетает или жутко тормозит.
Выгрузил из AutoCAD Bentley AutoPLANT Object Enabler. Немного ужаснулся: количество proxy-объектов ProSteel исчисляется миллионами, которые, кстати, не видны на чертеже.

Как боролись раньше:
Вариант 1: В самом ProSteel есть чудесная команда PS_CLEAN_PROXY, которая избавляет от этих объектов.
Плюсы: гарантированное избавление. 
Минусы: Требуется наличие установленного приложения Bentley ProSteel, длительное время выполнения (на чертеже весом в 20 МБ время выполнения 1-2 минуты)

Вариант 2: ARX-утилита от небезызвестного Александра Ривилиса ExplodeProxy, которая добавляет в AutoCAD команду RemoveAllProxy. Отличная штука, когда этих proxy-объектов немного, но в нашей ситуации оказалась практически бесполезной.
Плюсы: Избавляет от proxy-объектов практически полностью (за редким исключением).
Минусы: Длительное время выполнения (на чертежах весом 20 МБ время выполнения 10-15 минут на 64-х битной машинке. На 32-х битной AutoCAD начинает кричать о нехватке памяти уже на 10-15 МБайтных чертежах.)

Вариант 3: Стандартная команда AutoCAD _WBLOCK (ПБЛОК). Тоже хорошее решение, но т.к. у нас используется система электронного архива, а на выходе команды получается новый файл, необходимо каждый раз экспортировать чертежи из архива, прогонять через _WBLOCK, подменять старый чертеж в файловой системе и импортировать обратно в архив.
Плюсы: Время выполнения - несколько секунд. Гарантированное избавление от объектов.
Минусы: Очень большое количество операций с чертежом. Необходимость операций экспорта/импорта из системы электронного архива.

Ни один из вышеперечисленных вариантов не устраивал полностью, поэтому я взял несколько чертежей для анализа. Более глубокий анализ показал наличие в базе чертежа словарей (Dictionaries), в которых и хранятся эти объекты. Перечислю эти словари ниже:
  • Ks_ShapeRefDictionary
  • Ks_DetailStyleDictionary
  • Ks_XRecordDictionary
  • Ks_GroupDataDictionary
Спустя полчаса был готов LSP-файл, избавляющий чертеж от этих объектов. Приведу здесь две функции из файла.
Первая проверяет наличие в базе чертежа объектов ProSteel.
;;; <LISPDOC>
;;; <SUBR>(prosteel-check)</SUBR>
;;; <DESC>Check is there any prosteel dictionaries in drawing database</DESC>
;;; <RET>T or nil</RET>
;;; </LISPDOC>
(defun prosteel-check (/ result)
  (vlax-for dict (vla-get-dictionaries (vla-get-activedocument (vlax-get-acad-object)))
    (if (vlax-property-available-p dict 'Name)
      (if (member (vlax-get dict 'Name) '("Ks_ShapeRefDictionary" "Ks_DetailStyleDictionary" "Ks_XRecordDictionary" "Ks_GroupDataDictionary"))
        (setq result T))))
  result)
Вторая удаляет словари ProSteel из базы данных чертежа.
;;; <LISPDOC>
;;; <SUBR>(prosteel-remove-dicts-vl)</SUBR>
;;; <DESC>Remove all prosteel dictionaries (proxy-objects)</DESC>
;;; <RET>nil</RET>
;;; </LISPDOC>
(defun prosteel-remove-dicts-vl (/ result)
  (vlax-for dict (vla-get-dictionaries (vla-get-activedocument (vlax-get-acad-object)))
    (if (vlax-property-available-p dict 'Name)
      (if (member (vlax-get dict 'Name) '("Ks_ShapeRefDictionary" "Ks_DetailStyleDictionary" "Ks_XRecordDictionary" "Ks_GroupDataDictionary"))
        (vla-delete dict)))))
Я намеренно внедрил все определенные за пределами функции для простоты восприятия.
Полный код можно посмотреть на github.
Вот, пожалуй, и все. Ах да, плюсы и минусы...
Плюсы: Время выполнения 2-3 секунды, на очень больших чертежах 5-6 секунд. Выполняется в текущем чертеже, не требует дополнительных операций с файлом.
Минусы: Пока не обнаружил :)

Update: Обнаружил один существенный минус. На одном и том же чертеже функция может выдать следующую ошибку: "Ошибка Automation. На объект ссылаются другие объекты". А может и корректно отработать. К сожалению, выяснить причину подобного поведения пока не удалось, если есть мысли, подскажите в комментариях. Но поигравшись с чистым AutoLISPом обнаружил, что dictremove удаляет словарь даже если vla-delete спотыкается. Отсюда родилась следующая функция.
;;; <LISPDOC>
;;; <SUBR>(prosteel-remove-dicts-ent)</SUBR>
;;; <DESC>Remove all prosteel dictionaries using dictremove (proxy-objects)</DESC>
;;; <RET>nil</RET>
;;; </LISPDOC>
(defun prosteel-remove-dicts-ent ( / dict)
  (foreach dict '("Ks_ShapeRefDictionary" "Ks_DetailStyleDictionary" "Ks_XRecordDictionary" "Ks_GroupDataDictionary"))
    (dictremove (namedobjdict) dict))
  nil)
Здесь пока проблем не нашел, код на github обновил.
Вот теперь все.

Комментариев нет:

Отправить комментарий