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

ADN Club => Revit API => Тема начата: Bojan от 12-03-2018, 12:34:37

Название: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Bojan от 12-03-2018, 12:34:37
Всем доброго времени суток. Столкнулся с проблемой при работе с функцией Face.Triangulate() у одного объекта (файл проекта можно скачать по ссылке https://drive.google.com/open?id=1ZTj1-LvnGwudGE5qJRHa9uheDji796y1, id объекта 2396118).
Функция Face.Triangulate(), выполненная в потоке Revit, нижней (самой сложной) поверхности этого объекта выдает:
•   982 треугольника с параметром 0;
•   4262 треугольника с параметром по умолчанию;
•   10942 треугольника с параметром 1.
Естественно, эта же функция, выполненная в отдельном потоке должна выдавать те же результаты. Это подтверждается на практике для большого количества других объектов. Однако, вот что мы имеем на деле:
•   4759 треугольников с параметром 0;
•   4759 треугольников с параметром по умолчанию;
•   4759 треугольников с параметром 1.
Другими словами, мало того, что количество треугольников не совпадает, так еще и оно не изменяется при изменении параметра.
И самая основная проблема: после завершения работы плагина Revit вообще выдает ошибку и закрывается принудительно.
Код:
Код - C# [Выбрать]
  1. using System;
  2. using Autodesk.Revit.Attributes;
  3. using Autodesk.Revit.DB;
  4. using Autodesk.Revit.UI;
  5. using System.Threading;
  6.  
  7. namespace RevitGeometry
  8. {
  9.     [Transaction(TransactionMode.Manual)]
  10.     public sealed partial class TriangulateGeometry : IExternalCommand
  11.     {
  12.         private static void GetGeometry(object commandData)
  13.         {
  14.             Document document = ((ExternalCommandData)commandData).Application.ActiveUIDocument.Document;
  15.  
  16.             Element element = document.GetElement(new ElementId(2396118));
  17.  
  18.             GeometryElement geometryElement = element.get_Geometry(new Options { DetailLevel = ViewDetailLevel.Fine });
  19.             foreach (GeometryObject geometryObject in geometryElement)
  20.             {
  21.                 Solid solid = geometryObject as Solid;
  22.  
  23.                 Face face = solid.Faces.get_Item(0);
  24.  
  25.                 Mesh mesh = face.Triangulate(0);
  26.             }
  27.         }
  28.  
  29.         Result IExternalCommand.Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
  30.         {
  31.             try
  32.             {
  33. #if DEBUG
  34.                 System.Diagnostics.Debugger.Launch();
  35. #endif
  36.                 //works
  37.                 //GetGeometry(commandData);
  38.  
  39.                 //doesn't work
  40.                 Thread secondThread = new Thread(GetGeometry);
  41.                 secondThread.Start(commandData);
  42.                 secondThread.Join();
  43.             }
  44.             catch (Exception)
  45.             {
  46.                 return Result.Failed;
  47.             }
  48.  
  49.             return Result.Succeeded;
  50.         }
  51.     }
  52. }
  53.  
Название: Re: Не работает функция Face.Triangulate() вызванная в другом потоке.
Отправлено: Александр Пекшев aka Modis от 12-03-2018, 12:44:34
Ревит однопоточный и нельзя обращаться к его базе из другого потока. Точнее, можно обращаться для получения данных, но для какого-либо редактирования - уже нет. Для этого уже нужно реализовать специальный класс IExternalEvenet.
Возможно, для работы метода Triangulate вызываются какие-либо изменения (перерисовка геометрии, например) и поэтому падает Ревит у вас.
Кстати, как падает-то? Ошибку выдает какую? Может стоит для начала обернуть в try{} catch(){}?
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Bojan от 12-03-2018, 12:52:00
Вот так падает:
(https://s9.postimg.org/4psed1b23/Error.png) (https://postimg.org/image/4psed1b23/)
try-catch обертка не помогает, поскольку в процессе работы никаких исключений не возникает, Revit падает уже после "успешного" завершения работы плагина.
Тогда вопрос можно переформулировать по-другому: что такого есть в этом объекте, чего нет во многих других, что заставляет плагин некорректно работать в другом потоке?
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Пекшев aka Modis от 12-03-2018, 12:58:51
1. Я вам уже сказал - Ревит однопоточный и не позволяет с собой работать из другого потока
2. Зачем вообще второй поток?
3. С чего вы взяли, что падает на этом методе?
4. Вы можете сделать тестовый проект и приложить его к теме?
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Bojan от 12-03-2018, 13:03:45
Второй поток нужен, чтобы во время работы большого плагина не зависало ничего и можно было отменить работу;
Потому что если этот метод убрать - все работает;
Тестовый проект приложен (ну я надеюсь по крайней мере), его можно скачать по ссылке во втором предложении в скобках.
https://drive.google.com/file/d/1ZTj1-LvnGwudGE5qJRHa9uheDji796y1/view
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Пекшев aka Modis от 12-03-2018, 13:59:43
Второй поток нужен, чтобы во время работы большого плагина не зависало ничего и можно было отменить работу;
Я даже разбираться не буду с примером и просто спрошу - как вы себе это представляете??? Типа работает команда, а я в этот момент что-то могу делать в Ревите??
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Bojan от 12-03-2018, 14:20:27
Нет конечно. При работе плагина из потока Ревита, он может зависнуть (полоса загрузки остановится), его нельзя отменить (потому что нельзя никуда нажать), Ревит может стать черным (что не очень приятно видеть) и т.п. Второй поток нужен, чтобы я мог в браузере, например, что-то делать и не бояться, что у меня уже давно завис плагин, а я даже отменить его не смогу.
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Ривилис от 12-03-2018, 14:23:15
Bojan
1. Как уже написал Александр Пекшев aka Modis использовать потоки в Revit нельзя. Во всяком случае так, как Вы делаете.
2. Для прерывания выполнения второй поток не нужен. Нужно просто отслеживать нажатие клавиатуры.
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Пекшев aka Modis от 12-03-2018, 14:25:57
Нет конечно. При работе плагина из потока Ревита, он может зависнуть (полоса загрузки остановится), его нельзя отменить (потому что нельзя никуда нажать), Ревит может стать черным (что не очень приятно видеть) и т.п. Второй поток нужен, чтобы я мог в браузере, например, что-то делать и не бояться, что у меня уже давно завис плагин, а я даже отменить его не смогу.
Нет, так нельзя делать. Третий раз говорю - Ревит однопоточный и любые действия с БД проекта требуют работу только в текущем потоке Ревита.
Единственное, что вы можете - это запустить в другом потоке окно с отображением прогресса. А вот чтобы мочь процесс работы еще и отменить из этого окна - это уже нужно помучаться.
Но то, что вы хотите - нельзя сделать
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Ривилис от 12-03-2018, 14:27:29
Тут есть пример: http://thebuildingcoder.typepad.com/blog/2013/01/implement-a-progress-bar-and-abort-a-lengthy-process.html
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Bojan от 12-03-2018, 14:40:02
Слушайте, я просто привел пример, а вы зачем-то начали отвечать не на мой вопрос. Если я правильно понял все вышеизложенное, то по делу:
1. Нельзя понять, почему именно на этому объекте не работает;
2. Корректность работы функции Face.Triangulate() нельзя гарантировать во внешнем потоке. Кроме того, нельзя сказать в каких случаях она будет корректная, а в каких нет.
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Ривилис от 12-03-2018, 14:44:42
Bojan
Ответ на поставленный вопрос: нельзя использовать внешние потоки (Thread) в Revit API. Точка. Дале тебе начали подсказывать как следует выкрутится в данной ситуации. Что не так?
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Bojan от 12-03-2018, 14:56:29
Это не ответ на поставленный вопрос (нельзя использовать внешние потоки (Tread) в Revit API), потому что не было вопроса: "Можно ли использовать внешние потоки (Tread) в Revit API?". Вопрос в явном виде не был поставлен, но по-моему достаточно очевидно, что вопрос в следующем: "Почему функция Face.Triangulate() некорректно работает из внешнего потока?" Ответ на этот вопрос прозвучал почти сразу: "Ревит однопоточный и нельзя обращаться к его базе из другого потока...", но во время этого ответа прозвучали встречные вопросы, на которые я ответил и задал второй вопрос по теме: "Что такого есть в этом объекте, чего нет во многих других, что заставляет плагин некорректно работать в другом потоке?" Ответ на него так и не прозвучал, поэтому я и ответил на него сам, просуммировав все что вы сказали: "Нельзя понять, почему именно на этому объекте не работает". Вот это полный ответ на два основных вопроса, который можно было дать сразу, а не уходить в сторону, поскольку в процессе цепочки вопросов-ответов ничего нового вы и я не узнали.
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Пекшев aka Modis от 12-03-2018, 16:29:47
Bojan, вы вообще ничего не поняли? Этот метод поэтому и не работает, что нельзя в Ревите обращаться к БД из другого потока. Также не будут и сотни других методов работать по той же самой причине! Причем я вам в первом же ответе выдвинул одно из предположений на вопрос Почему конкретно этот метод
Возможно, для работы метода Triangulate вызываются какие-либо изменения (перерисовка геометрии, например) и поэтому падает Ревит у вас.
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: WhiteShark от 12-03-2018, 16:43:35
Bojan
Для начала оберните ваш трэд в try catch. Тогда будет понятно был ли там exception. Ваш имеющийся catch никогда не поймает exception в новом трэде "secondThread"
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Пекшев aka Modis от 12-03-2018, 16:50:53
Кстати, ваш код падает не на строчке
Код - C# [Выбрать]
  1. [s]Mesh mesh = face.Triangulate(0);[/s]
а на строчке
Код - C# [Выбрать]
  1. [s]foreach (GeometryObject geometryObject in geometryElement)[/s]
Потому что geometryElement равен null! А знаете почему? Потому что НЕЛЬЗЯ РАБОТАТЬ С БД ПРОЕКТА ИЗ ДРУГОГО ПОТОКА![/b]

Соврал - метод отрабатывает и Ревит падает. Причина конечна ясна, но конкретно её объяснить сложно
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Пекшев aka Modis от 12-03-2018, 17:19:46
(https://s17.postimg.org/88nw5mwbf/Screenshot_13.png) (https://postimg.org/image/88nw5mwbf/)

Надеюсь официальная справка отобьет желание дальше спорить на глупые темы
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Ривилис от 12-03-2018, 17:28:29
Александр Пекшев aka Modis
Я отметил твой ответ как Решение, так как он в точности соответствует природе ошибки.
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: maksl от 13-03-2018, 17:05:51

2. Для прерывания выполнения второй поток не нужен. Нужно просто отслеживать нажатие клавиатуры.

я, возможно, в лыжи обут, но не работает все так просто. беглое курение msdn показало, что события типа button_click не проходят, пока поток занят в другом цикле. и мои тесты показывают то же самое.

Вот тут я новую тему создал: http://adn-cis.org/forum/index.php?topic=8366.0 (http://adn-cis.org/forum/index.php?topic=8366.0)
Название: Re: Не работает функция Face.Triangulate(), вызванная в другом потоке.
Отправлено: Александр Ривилис от 14-03-2018, 00:38:24
беглое курение msdn показало, что события типа button_click не проходят, пока поток занят в другом цикле. и мои тесты показывают то же самое.
Нужно сделать так, чтобы поток не был "постоянно занят в другом цикле".