Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: Александр Пекшев aka Modis от 20-11-2018, 22:57:16

Название: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Пекшев aka Modis от 20-11-2018, 22:57:16
Всем привет. Как-то давно были у меня вопросы (вот (http://adn-cis.org/forum/index.php?topic=7813.msg26266#msg26266), вот (http://adn-cis.org/forum/index.php?topic=7834.msg26451#msg26451)) касаемо ObjectOverrule. Те проблемы были решены и все работает. Конечно, возможно не идеально, но вроде без фаталов ошибок и прочего такого. По крайней мере пользователи не сообщали и у меня при тестах хорошо. О результатах я пишу понемногу в этой теме (http://adn-cis.org/forum/index.php?topic=7771.0). Скоро, кстати, будет новый комментарий, если это конечно кому-то интересно.
Проект был надолго заброшен, но недавно я прям загорелся им снова. На данный момент занимаюсь рефакторингом и оптимизацией. Переделал практически весь проект, упростив создание новых примитивов до максимума. Настолько, что даже палитра строится сама на основе выбранного примитива ))
Так вот - есть такой код - его не получается обобщить и приходится делать для каждого типа моих примитивов:
Код - C# [Выбрать]
  1. namespace mpESKD.Functions.mpBreakLine.Overrules
  2. {
  3.     using Autodesk.AutoCAD.DatabaseServices;
  4.     using Autodesk.AutoCAD.Runtime;
  5.     using ModPlusAPI.Windows;
  6.     using Base.Helpers;
  7.     using Base;
  8.  
  9.     public class BreakLineObjectOverrule : ObjectOverrule
  10.     {
  11.         protected static BreakLineObjectOverrule _breakLineObjectOverrule;
  12.  
  13.         public static BreakLineObjectOverrule Instance()
  14.         {
  15.             if (_breakLineObjectOverrule != null) return _breakLineObjectOverrule;
  16.             _breakLineObjectOverrule = new BreakLineObjectOverrule();
  17.             // Фильтр "отлова" примитива по расширенным данным. Работает лучше, чем проверка вручную!
  18.             _breakLineObjectOverrule.SetXDataFilter(BreakLineInterface.Name);
  19.             return _breakLineObjectOverrule;
  20.         }
  21.  
  22.         public override void Close(DBObject dbObject)
  23.         {
  24.             // Проверка дополнительных условий
  25.             if (IsApplicable(dbObject))
  26.             {
  27.                 AcadHelpers.WriteMessageInDebug("Close in ObjectOverrule");
  28.                 try
  29.                 {
  30.                     if (AcadHelpers.Document != null)
  31.                         if (dbObject != null && dbObject.IsNewObject & dbObject.Database == AcadHelpers.Database ||
  32.                             dbObject != null && dbObject.IsUndoing & dbObject.IsModifiedXData)
  33.                         {
  34.                             var breakLine = EntityReaderFactory.Instance.GetFromEntity<BreakLine>((Entity)dbObject);
  35.                             if (breakLine != null)
  36.                             {
  37.                                 breakLine.UpdateEntities();
  38.                                 breakLine.GetBlockTableRecordForUndo((BlockReference)dbObject).UpdateAnonymousBlocks();
  39.                             }
  40.                         }
  41.                 }
  42.                 catch (Exception exception)
  43.                 {
  44.                     ExceptionBox.Show(exception);
  45.                 }
  46.             }
  47.             base.Close(dbObject);
  48.         }
  49.  
  50.         public override bool IsApplicable(RXObject overruledSubject)
  51.         {
  52.             return ExtendedDataHelpers.IsApplicable(overruledSubject, BreakLineInterface.Name);
  53.         }
  54.     }
  55. }

Весь код в вопросе не важен. Важны только некоторые строки:
18 - согласно этой статье (https://www.keanw.com/2009/04/optimized-overruling-in-autocad-2010-using-net.html) использование SetXDataFilter позволяет убрать ручную проверку XData, перекладывая эту работу на сам автокад
51-53 - стандартная реализация метода IsApplicable где я проверяю что в примитиве имеется нужная мне XData с кодом 1001
И вот самое интересное - в 27 строке - я просто вывожу сообщение в командную строку. А интересно то, что после создания моего примитива в чертеже, эту строчку в командной строке я вижу ПОСТОЯННО. Независимо от того, делаю я что-то со своим объектом или с любым другим примитивов автокада. А самое главное - эту строчку я вижу при каждом движении мышкой в чертеже!

Получается как-то совсем не оптимизировано. Хотелось бы понять почему так происходит и как лучше поступить в этой ситуации
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Ривилис от 20-11-2018, 23:25:04
 ;D ;D ;D
Для того, чтобы AutoCAD мог определить есть или нет у примитива XData с соответствующим именем приложения, примитив нужно открыть, ну а потом закрыть, т.е. вызвать его метод Close. Так что тут всё очевидно. Способа оптимизации я не вижу, так как понимаю, что во всех случаях у тебя примитив - BlockReference, так что отфильтровать не открывая его не получится.
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Пекшев aka Modis от 20-11-2018, 23:36:26
Для того, чтобы AutoCAD мог определить есть или нет у примитива XData с соответствующим именем приложения, примитив нужно открыть, ну а потом закрыть, т.е. вызвать его метод Close
Блин, и точно)) Очевидно же

Но вот что мне не совсем очевидно - что происходит, когда я просто мышкой двигаю в пустом пространстве? Что при этом постоянно открывается?
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Ривилис от 20-11-2018, 23:39:56
Что при этом постоянно открывается?
Запусти под отладчиком и посмотри. Возможно какой-то другой Overrule или PointMonitor/PointFilter шалит.
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Пекшев aka Modis от 20-11-2018, 23:53:44
Что при этом постоянно открывается?
Запусти под отладчиком и посмотри. Возможно какой-то другой Overrule или PointMonitor/PointFilter шалит.
Понятия не имею как это сделать) Я знаю, что срабатывает мой конкретный метод Close(), знаю, что прилетает в него всегда BlockReference, а вот как узнать кто является причиной - я так не умею )
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Ривилис от 21-11-2018, 00:04:27
Александр Пекшев aka Modis,
1. Что будет если твой метод IsApplicable будет всегда возвращать false?
2. Что делает ExtendedDataHelpers.IsApplicable(overruledSubject, BreakLineInterface.Name)? Нужен код.
3. Я бы в этой ситуации использовал бы не фильтр SetXDataFilter, а SetIdFilter, предварительно отобрав бы ObjectId своих примитивов и создав из них коллекцию для каждого открытого документа.
Не забывай, что Close вызывается не только для примитивов, но и для любых объектов (блоки/слои/типы линий/виды и т.д. и т.п.), и соответственно при фильтре SetXDataFilter каждый из них открывается/закрывается и это рекурсивно приводит к вызову Close.
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Пекшев aka Modis от 21-11-2018, 00:27:27
Я бы в этой ситуации использовал бы не фильтр SetXDataFilter, а SetIdFilter, предварительно отобрав бы ObjectId своих примитивов и создав из них коллекцию для каждого документа.
Идея очень даже хорошая, но вот реализация объёмная как мне кажется. Я ведь за этой коллекцией должен следить и многое могу не усмотреть сразу. Это я так рассуждаю, потому-что еще ни разу не использовал словари автокадовские (даже не помню как их там зовут). Надо будет поизучать и посмотреть примеры.

Сейчас уже проводить тесты поздно, так что пока лишь приведу метод:
Код - C# [Выбрать]
  1. /// <summary>
  2. /// Проверка поддерживаемости примитива для Overrule
  3. /// </summary>
  4. /// <param name="rxObject"></param>
  5. /// <param name="appName"></param>
  6. public static bool IsApplicable(RXObject rxObject, string appName)
  7. {
  8.     DBObject dbObject = rxObject as DBObject;
  9.     if (dbObject == null)
  10.         return false;
  11.     // Всегда нужно проверять по наличию расширенных данных
  12.     // иначе может привести к фаталам при работе с динамическими блоками
  13.     return IsIntellectualEntity(dbObject, appName);
  14. }
  15. public static bool IsIntellectualEntity(DBObject dbObject, string appName)
  16. {
  17.     ResultBuffer rb = dbObject.GetXDataForApplication(appName);
  18.     return rb != null;
  19. }
Тут все просто
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Ривилис от 21-11-2018, 00:31:30
Это я так рассуждаю, потому-что еще ни разу не использовал словари автокадовские (даже не помню как их там зовут).
Причем здесь "AutoCAD'овские словари"? Я не про AutoCAD'овские, а про .NET-овские.
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Пекшев aka Modis от 21-11-2018, 00:35:44
Это я так рассуждаю, потому-что еще ни разу не использовал словари автокадовские (даже не помню как их там зовут).
Причем здесь "AutoCAD'овские словари"? Я не про AutoCAD'овские, а про .NET-овские.
Ну мне же эти данные нужно будет сохранять в документе, чтобы при следующем открытии документа все заработало. А значит либо XData, либо DBDictionary. При этом первый вариант, по идее, подвержен переполнению
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Ривилис от 21-11-2018, 00:40:09
Ну мне же эти данные нужно будет сохранять в документе, чтобы при следующем открытии документа все заработало.
Можно и так. Тогда XRecord в Named Object Dictionary (NOD) или в ExtensionDictionary для BlockTable. А можно и налету при открытии файла. Но в любом случае Close будет срабатывать и для них. Вот что самое печальное.
Кстати, если у тебя это только BlockReference, то почему ты сразу не проверяешь в IsApplicable на BlockReference? Кроме того я проверил бы еще и ObjectId на null и в этом случае возвращал сразу false.
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Владимир Шу от 21-11-2018, 08:20:01
Скоро, кстати, будет новый комментарий, если это конечно кому-то интересно.
Лично мне эта тема интересна и последние несколько месяцев ей и занимаюсь по мере сил, результаты экспериментов есть на youtube (если интересно), код пока не выкладываю, так как там именно эксперименты и показать пока нечего. Про оптимизацию пока не думал, но с интересом читаю твои посты.
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Пекшев aka Modis от 21-11-2018, 10:01:42
Но в любом случае Close будет срабатывать и для них
Тогда, пожалуй, пока отложу такую фичу. На данный момент не вижу в ней острой необходимости. Возможно, когда моих "кастомных" примитивов перевалит штук за 10, тогда можно будет задуматься об этом еще раз
Кстати, если у тебя это только BlockReference, то почему ты сразу не проверяешь в IsApplicable на BlockReference? Кроме того я проверил бы еще и ObjectId на null и в этом случае возвращал сразу false
Хорошие замечания. Добавлю в проверку. Все-таки свежий взгляд полезен. Особенно, когда проект уже перевалил за 5К строк исполняемого кода (согласно метрикам VS)
результаты экспериментов есть на youtube (если интересно)
Только вот нет ссылки на ютуб =))
Про оптимизацию пока не думал, но с интересом читаю твои посты
Кстати, рассматриваю вариант делать проект OpenSource. Правда не совсем в этом уверен, так как он будет не "отвязанным" - т.е. все-равно завязан на ModPlus и работать только в нём. Вот и думаю - нужно ли?
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Владимир Шу от 21-11-2018, 10:10:53
Off-Topic: показать
Только вот нет ссылки на ютуб =))
Отвлекся, думал что добавил... =(
Извините, вам запрещён просмотр содержимого спойлеров.

Open Source... если код завязан на закрытую библиотеку кода, то какой смысл? Меня посещала мылсЯ, что-то типа "движка" сделать... т.е. разделить код в соответствии с МVC и упростить создание модели до предела, что бы можно было очень просто и легко клепать свои "кустомные" объекты. Но чувствую опыта и знаний пока маловато для этого...
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Пекшев aka Modis от 21-11-2018, 10:15:20
Отвлекся, думал что добавил
Какая-то специфическая штуковина во всех видосах =)) Что это за объект такой? От какой специальности?
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Владимир Шу от 21-11-2018, 10:22:50
Какая-то специфическая штуковина во всех видосах =)) Что это за объект такой? От какой специальности?
Сейчас придет Александр Ривилис и даст нам по голове, за такой оффтоп...
Это из КЖ, зона раскладки арматуры. Проблема в том, что классические дин. блоки плохо зеркалятся (там с атрибутами заморочка, как пример, попробуй это сделать с этим блоком https://dwg.ru/dnl/14692)
А такой кустомный объект такого недостатка лишён, плюс часто требуется не совсем прямые арматурины, а с некоторой формой или отгибами или условными обозначениями на концах, плюс растягивать можно в соответствии с шагами по ширине и кратностью 11700 по длине, в общем плюшек очень много ... вот и экспериментирую...
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Пекшев aka Modis от 21-11-2018, 10:25:12
Сейчас придет Александр Ривилис и даст нам по голове, за такой оффтоп
Тогда уйдем в ту тему ;)
Постараюсь сегодня новый комментарий написать, рассказав немного чего я добился за последние две недели, переделав практически ВСЕ =))
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Пекшев aka Modis от 21-11-2018, 14:18:14
Кроме того я проверил бы еще и ObjectId на null и в этом случае возвращал сразу false
Вот это, кстати, не стоит делать. Конечно, идея правильная в том ключе, что мы обсуждаем только лишь ObjectOverrule. Но у меня метод IsApplicable работает со всеми overrule и в GripOverrule это вызывает отрицательный эффект. Дело в том, что при работе метода MoveGripPointsAt автокад создает копию блока, которую мы и видим на экране в момент изменения. И вот у этой копии ObjectId.Null как-раз.
Как вариант, просто в параметр метода IsApplicable можно добавить bool checkForNullId = false и в ObjectOverrule уже ставить этот параметр в true
Название: Re: Почему метод ObjectOverrule.Close() вызывается постоянно?
Отправлено: Александр Ривилис от 21-11-2018, 18:27:21
Но у меня метод IsApplicable работает со всеми overrule и в GripOverrule это вызывает отрицательный эффект.
И кто тебе виноват? ;)