Подключиться к Revit API из внешнего приложения

Автор Тема: Подключиться к Revit API из внешнего приложения  (Прочитано 20710 раз)

0 Пользователей и 2 Гостей просматривают эту тему.

Оффлайн ДамирАвтор темы

  • ADN Club
  • **
  • Сообщений: 94
  • Карма: 1
Всем привет!


Стоит несложная задача подключиться к Revit API из внешней программы, как например к AutoCAD - Set AcadApp = CreateObject("AutoCAD.Application")
Мои варианты:
Код - Visual Basic [Выбрать]
  1. Set RevtApp = CreateObject("Autodesk.Revit.Application")
  2. Set RevtApp = CreateObject(,"Autodesk.Revit.Application")
  3. Set RevtApp = CreateObject("Revit.Application")
  4. Set RevtApp = CreateObject(,"Revit.Application")
  5. Set RevtApp = CreateObject("Autodesk.Revit.Application.15")


Всюду получаю сообщения об ошибках о невозможности создания объекта контейнером ActiveX. Версия Revit 2015


Заранее премного благодарен
Сколько голов, столько умов. Но голов больше

Оффлайн Виктор Чекалин

  • Administrator
  • *****
  • Сообщений: 694
  • Карма: 111
  • Skype: chekalin-v
Дамир, на самом деле задача все-таки сложная. Обратиться к Revit также как к AutoCAD не получится.

Я писал статью по этому поводу с примером как можно обращаться к Revit API из внешнего приложения. Доступ к Revit из внешнего приложения.

Конкретная реализация зависит от ваших задач.

Оффлайн ДамирАвтор темы

  • ADN Club
  • **
  • Сообщений: 94
  • Карма: 1
Виктор Чекалин, благодарю Вас за быстрый ответ и ссылку на статью.
Позвольте опишу задачу. Требуется из внешнего приложения посчитать кол-во листов внутри чертежа. Внешнее приложение допускает программирование на VBScript. Для меня весьма важно понять как подключаться к Revit API извне, поскольку планируются иные задачи.


Внутри Revit я создаю сервис, который будет получать запросы из внешнего приложения
Как я понял из статьи, за само подключение к API отвечает созданный WCF сервис.  Он представляет собой написанное отдельно приложение (написанного на Visual Studio к примеру)? - судя по видео в статье

Достаточно ли будет мне вашего WCF сервиса для подсчета кол-ва листов? То есть вариант наподобие (в качестве бреда):

[ServiceContract]
    public interface IRevitExternalService
    {
        [OperationContract]
        string ПолучитьВсеЛистыЧертежа();

    }

Получается в самом приложении (WCF сервисе) можно поместить подсчет кол-ва листов. Прошу Вашей помощи в реализации моей задачи.
« Последнее редактирование: 27-10-2014, 20:33:26 от Дамир »
Сколько голов, столько умов. Но голов больше

Оффлайн Виктор Чекалин

  • Administrator
  • *****
  • Сообщений: 694
  • Карма: 111
  • Skype: chekalin-v
Дамир, в целом вы правы. В Revit вы создаете WCF сервис на подобие того, что вы описали.
А вот дальше сложнее. Как работать с WCF сервисом из VBScript - я честно не знаю. Вот нашел пример, но сам я ничего подобного не пробовал. C VBScript последний раз работал лет 7 назад.

Загвоздка еще заключается в том, что в моем примере доступ к WCF сервису осуществляется через net.tcp. Это может так же послужить проблемой. Хотя вот тут[url] пишут что проблема решаема.

Я бы вам посоветовал создать простое консольное приложение на C#, создать в нем простенький WCF сервис и уже затем тестировать обращение к этому сервису из VBScript. После этого уже можно будет внедрить этот код Revit.

Оффлайн ДамирАвтор темы

  • ADN Club
  • **
  • Сообщений: 94
  • Карма: 1
Виктор Чекалин, спасибо.
Я обдумаю ваши рекомендации.
Я понимаю, что есть следующие варианты:
1. Создать WCF внутри VBScipt'a (как в приведенной ссылке) и там же обратиться к нужным методам Revit API. - Возможно ли это?
2. Если не получится создать WCF внутри VBScipt'a, то можно создать консольное приложение (как Вы и написали), которое будет обращаться к чертежу Revit, считывать кол-во истов, сохранять в некий лог, откуда я его считаю силами VBScript.

3. На сколько обязательно использовать net.tcp в WCF? (наверно потому что обращаемся к службам)

Спасибо за помощь
Сколько голов, столько умов. Но голов больше

Оффлайн Виктор Чекалин

  • Administrator
  • *****
  • Сообщений: 694
  • Карма: 111
  • Skype: chekalin-v
Дамир,
1. Тут возможно мы в понятиях немного путаемся. У WCF есть серверная часть, есть клиентская. В Revit вам нужно создать именно сервверную часть WCF сервиса, для того, чтобы к нему можно было обратиться из внешнего приложения. На VBScript нужно использовать клиентскую часть, т.е. из VBScript вызвать метод на серверной строне сервиса и получить ответ. Судя по приведенным ссылкам это возможно. Но результат будет в виде XML. Возможно сделать, чтобы результат был в виде JSON.
2. По идее можно сделать так. На C# вы пишете консольное приложение, в котором создать WCF клиента очень просто и обращаться к WCF сервису тоже очень просто. При запуске это консольное приолжение сразу же обращатется к WCF сервису внутри Revit. Полученный результат сохраняем в файл (название файла можно передать параметром). В VBScript вы запускаете это консольное приложение, затем читаете результат из файла. Но честно это уже совсем извращение:)
3. можно и через http. Только в этом случае Revit нужно будет запускать с правами администратора. С net.tcp таких проблем нет.

Оффлайн ДамирАвтор темы

  • ADN Club
  • **
  • Сообщений: 94
  • Карма: 1
я не понимаю что значит
Цитировать
В Revit вам нужно создать именно серверную часть WCF сервиса
. В самом файле чертежа?
В моем понимании создание WCF осуществляется кодом в приложении. Например в консольном на C#. При этом не понимаю, а что нужно сделать в Revit'e? То есть включить там серверную часть WCF - как кодом в этом же консольном приложении?
Сколько голов, столько умов. Но голов больше

Оффлайн Виктор Чекалин

  • Administrator
  • *****
  • Сообщений: 694
  • Карма: 111
  • Skype: chekalin-v
Дамир, В статье есть пример кода работающего приложения. Я рекомендую вам запустить его и посмотреть как он работает.

WCF Service создается в вашей надстройке при старте Revit. Метода OnStartup. После этого Rуvit готов к обращению из внешнего приложения.  Сам сервис не связан с конкретной моделью.
Вот код из примера:
Код - C# [Выбрать]
  1. class App : IExternalApplication
  2.     {
  3.         private const string serviceUrl =
  4.             "net.pipe://localhost/";
  5.  
  6.  
  7.         private ServiceHost serviceHost;
  8.  
  9.         public Result OnStartup(UIControlledApplication a)
  10.         {
  11.             a.Idling += OnIdling;
  12.  
  13.             Uri uri = new Uri(serviceUrl);
  14.  
  15.             serviceHost =
  16.                 new ServiceHost(typeof(RevitExternalService), uri);
  17.  
  18.  
  19.             try
  20.             {
  21.                 serviceHost.AddServiceEndpoint(typeof (IRevitExternalService),
  22.                                                new NetNamedPipeBinding(),
  23.                                                "RevitExternalService");
  24.  
  25.                 //ServiceMetadataBehavior smb =
  26.                 //    new ServiceMetadataBehavior();
  27.                 //smb.HttpGetEnabled = true;
  28.                 //serviceHost.Description.Behaviors.Add(smb);
  29.  
  30.                 serviceHost.Open();
  31.             }
  32.             catch (Exception ex)
  33.             {                
  34.                 a.ControlledApplication
  35.                     .WriteJournalComment(string.Format("{0}.\r\n{1}",  
  36.                         Resources.CouldNotStartWCFService,
  37.                         ex.ToString()),
  38.                     true);
  39.                
  40.             }
  41.            
  42.             return Result.Succeeded;
  43.         }
  44.  
  45.         private void OnIdling(object sender, IdlingEventArgs e)
  46.         {
  47.             var uiApp = sender as UIApplication;
  48.  
  49.             Debug.Print("OnIdling: {0}", DateTime.Now.ToString("HH:mm:ss.fff"));
  50.  
  51.             // be carefull. It loads CPU
  52.             e.SetRaiseWithoutDelay();
  53.  
  54.             if (!TaskContainer.Instance.HasTaskToPerform)
  55.                 return;
  56.  
  57.             try
  58.             {
  59.                 Debug.Print("{0}: {1}", Resources.StartExecuteTask, DateTime.Now.ToString("HH:mm:ss.fff"));
  60.  
  61.                 var task = TaskContainer.Instance.DequeueTask();
  62.                 task(uiApp);
  63.  
  64.                 Debug.Print("{0}: {1}", Resources.EndExecuteTask, DateTime.Now.ToString("HH:mm:ss.fff"));
  65.             }
  66.             catch (Exception ex)
  67.             {
  68.                 uiApp.Application.WriteJournalComment(
  69.                     string.Format("RevitExternalService. {0}:\r\n{2}",
  70.                     Resources.AnErrorOccuredWhileExecutingTheOnIdlingEvent,
  71.                     ex.ToString()), true);
  72.  
  73.                 Debug.WriteLine(ex);
  74.             }
  75.  
  76.             //e.SetRaiseWithoutDelay();
  77.         }
  78.  
  79.         public Result OnShutdown(UIControlledApplication a)
  80.         {
  81.             a.Idling -= OnIdling;
  82.  
  83.             if (serviceHost != null)
  84.             {
  85.                 serviceHost.Close();
  86.             }
  87.  
  88.             return Result.Succeeded;
  89.         }
  90.     }
  91.  

Оффлайн ДамирАвтор темы

  • ADN Club
  • **
  • Сообщений: 94
  • Карма: 1
Цитировать
WCF Service создается в вашей надстройке при старте Revit
вот теперь я понял. Получается Revit должен содержать Add-In c WCF сервисом внутри, ждущим обращения. Вот такой как в вашей статье:
Код - C# [Выбрать]
  1.     public interface IRevitExternalService
  2.  
  3.     {
  4.  
  5.         [OperationContract]
  6.  
  7.         string GetCurrentDocumentPath();
  8.  
  9.  
  10.  
  11.         [OperationContract]
  12.  
  13.         bool CreateWall(XYZ startPoint, XYZ endPoint);
  14.  
  15.     }
    И сам Revit должен быть запущен, к сожалению.
    Потом в консольном app мы это обращение строим и получаем ответ куда - надо подумать.


    Спасибо! Мне к сожалению не хватает знаний C#. Сейчас занялся изучением. [/li]
« Последнее редактирование: 28-10-2014, 12:32:14 от Виктор Чекалин »
Сколько голов, столько умов. Но голов больше

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
И сам Revit должен быть запущен, к сожалению.
Ну с этим то вроде в VBScript проблем быть не должно. Разве что нужно знать путь доступа к exe-файлу.
В этой статье описано как это сделать: http://thebuildingcoder.typepad.com/blog/2009/09/revit-install-location.html
Насчет актуальности этой статьи для последних версий Revit я думаю Виктор Чекалин просветит.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Стоит несложная задача подключиться к Revit API из внешней программы
С определением "несложная" кажется ты поторопился. ;)
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Виктор Чекалин

  • Administrator
  • *****
  • Сообщений: 694
  • Карма: 111
  • Skype: chekalin-v
Дамир, совершенно верно. Внутри Revit создаете серввис, затем из VBScript к нему обращаететсь.
Работает естественно при запущенном Revit. Но, как подметил Александр, запустить Revit и открыть файл модели из VBScript не проблема.
Хотя проблема будет, если при открытии файла Revit, появится диалог с ошибкой. Но это тоже решаемо.

По поводу того как получить путь к установленным на компьютере версиям Revit, я когда то писал вот такой код:
Код - C# [Выбрать]
  1.         private IEnumerable<RevitProductInfo> GetProducts(int verion)
  2.         {
  3.             var revitKey =
  4.                 Registry.LocalMachine.OpenSubKey(RevitRegistryKeyName, false);
  5.             if (revitKey == null)
  6.                 yield break;
  7.  
  8.             var revitVersionNumberKey =
  9.                 revitKey.OpenSubKey(verion.ToString(), false);
  10.  
  11.             if (revitVersionNumberKey == null)
  12.                 yield break;
  13.  
  14.             var revitVersionNumberKeyPattern = @"^REVIT-\d{2}:\d{4}$";
  15.             var regex = new Regex(revitVersionNumberKeyPattern);
  16.  
  17.             foreach (var keyName in revitVersionNumberKey.GetSubKeyNames())
  18.             {
  19.                 if (regex.IsMatch(keyName))
  20.                 {
  21.  
  22.                     var revitVersionKey =
  23.                         revitVersionNumberKey.OpenSubKey(keyName, false);
  24.                     RevitProductInfo revitProductInfo =
  25.                         RevitProductInfo.Create(verion);
  26.                     revitProductInfo.Language =
  27.                         revitVersionKey.GetValue("Language", string.Empty).ToString();
  28.                     revitProductInfo.Location =
  29.                         revitVersionKey.GetValue("InstallationLocation", string.Empty).ToString();
  30.                     revitProductInfo.ProductName =
  31.                         revitVersionKey.GetValue("ProductName", string.Empty).ToString();
  32.                     revitProductInfo.Version =
  33.                         revitVersionKey.GetValue("Version", string.Empty).ToString();
  34.  
  35.  
  36.                     yield return revitProductInfo;
  37.                 }
  38.             }
  39.         }
  40.  
  41.         public abstract class RevitProductInfo : ProductInfo
  42.     {
  43.         public static RevitProductInfo Create(int version)
  44.         {
  45.             switch (version)
  46.             {
  47.                 case 2012:
  48.                     return new Revit2012ProductInfo();
  49.                 case 2013:
  50.                     return new Revit2013ProductInfo();
  51.                 case 2014:
  52.                     return new Revit2014ProductInfo();
  53.                 default:
  54.                     throw new NotSupportedException
  55.                         (string.Format("Revit {0} is not supported", version));
  56.             }
  57.  
  58.         }
  59.  
  60.              
  61.         public override string ProgramPath
  62.         {
  63.             get { return Path.Combine(Location, "Program", "Revit.exe"); }
  64.         }
  65.     }
  66.  
  67.     public class Revit2012ProductInfo : RevitProductInfo
  68.     {
  69.        
  70.     }
  71.  
  72.     public class Revit2013ProductInfo : RevitProductInfo
  73.     {
  74.  
  75.     }
  76.  
  77.     public class Revit2014ProductInfo : RevitProductInfo
  78.     {
  79.         public override string ProgramPath
  80.         {
  81.             get { return Path.Combine(Location, "Revit.exe"); }
  82.  
  83.         }
  84.     }
  85.  
  86.  
Передаете в качестве параметра версию Revit - 2014, 2015 и т.п.. На выходе получаете список установленных редакций Revit указанной версии.

Оффлайн ДамирАвтор темы

  • ADN Club
  • **
  • Сообщений: 94
  • Карма: 1
Цитировать
С определением "несложная" кажется ты поторопился.

согласен!


Цитировать
Ну с этим то вроде в VBScript проблем быть не должно. Разве что нужно знать путь доступа к exe-файлу.

да, по exe запустить не проблема. Так сказать не главная беда всей задачи.


По поводу приведенной статьи: автор пишет на VB, я же использую VBScript. То есть вот такая конструкция не сработает
Using rgk1 As RegistryKey _     = Registry.LocalMachine.OpenSubKey(key)

Для этого мне надо определять rgk1 как объект, правильно к нему обращаться и т.д. ну это отдельная тема.
Как я понял из приведенной статьи автор обращается к регистрации Revit, его пути установки.
Сколько голов, столько умов. Но голов больше

Оффлайн Виктор Чекалин

  • Administrator
  • *****
  • Сообщений: 694
  • Карма: 111
  • Skype: chekalin-v
В статье используется VB.NET. А VB.NET и VBScript - это совершенно разные языки, несмотря на похожее название.

Вот первая статья, что мне попалась по поводу того как работать с реестром в VBScript - http://www.askit.ru/custom/progr_admin/m11/11_registry.htm

Хотя на мой взгляд, сначала лучше решить главную проблему - получить нужные данные из Revit в стороннем приложении. Путь к Revit для начала можно задать явно в коде.

Оффлайн ДамирАвтор темы

  • ADN Club
  • **
  • Сообщений: 94
  • Карма: 1
Прошу прощения. Версия  в качестве бреда , а то если обратиться к Revit так:
Код - Visual Basic [Выбрать]
  1. Set RevtApp = CreateObject("Revit.Application.R19.1")
  2.  
  3. Set RevtApp = CreateObject("Autodesck.Revit.Application.R19.1")


то есть с учетом версии приложения.
Сколько голов, столько умов. Но голов больше