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

22/05/2015

Список всех импортированных САПР файлов

Вы когда-нибудь беспокоились о том, есть ли у вас дубликаты импортов из САПР форматов?

Мой коллега Николай Шульга (Nikolay Shulga) из команды разработчиков Revit реализовал маленькую полезную утилиту для ответа на этот вопрос.

Вот что он пишет:

Когда-то я написал прототип приложения для вывода всех импортированных объектов в проект Revit. Идея состояла в том, чтобы вывести дублирующийся экземпляры. Люди часто импортируют одни и те же данные несколько раз.

Я не думаю, что я смогу до конца доработать это приложение. Может быть опубликовав этот прототип на своем блоге, и кто-нибудь подхватит эту идею. Или еще лучше – сделать это приложение open-source.

  • Мотивация – Люди часто импортируют чертежи. Обычно DWG. При этом несколько раз один и тот же чертеж на разные виды. Из-за этого размер документа увеличивается и снижается производительность. Мы обнаружили, что люде не часто знают, что за данные они импортируют.
  • Спецификация – Вывести список импортированных данных и вывести их в полезном для анализа виде.
  • Реализация – смотрите ниже
  • Как это можно улучшить – одной из идеей была возможность создания диспетчера импортированных файлов, подобно тому, как отображается список видов.
  • Подходящая модель для тестирования – любой проект с импортированным несколько раз одним и тем же DWG файлом.

Вот текущая реализация.

Код - C#: [Выделить]
  1. #region Namespaces
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Collections.Specialized;
  5. using System.Diagnostics;
  6. using Autodesk.Revit.ApplicationServices;
  7. using Autodesk.Revit.Attributes;
  8. using Autodesk.Revit.DB;
  9. using Autodesk.Revit.UI;
  10. using Autodesk.Revit.UI.Selection;
  11. using System.IO;
  12. #endregion
  13.  
  14. namespace ListImportInstances
  15. {
  16.     /// <summary>
  17.     /// Интерфейс для генерации отчета по имортированным файлам
  18.     /// в конкретном проекте   
  19.     /// </summary>
  20.     interface IReportImportData
  21.     {
  22.         bool init(string projectName);
  23.         void startReportSection(string sectionName);
  24.         void logItem(string item);
  25.         void setWarning();
  26.         void done();
  27.         string getLogFileName();
  28.     }
  29.  
  30.     class SimpleTextFileBasedReporter : IReportImportData
  31.     {
  32.         public SimpleTextFileBasedReporter()
  33.         {
  34.         }
  35.  
  36.         public bool init(string projectFileName)
  37.         {
  38.             bool outcome = false;
  39.             m_currentSection = null;
  40.             m_warnUser = false;
  41.  
  42.             if (0 != projectFileName.Length)
  43.             {
  44.                 m_projectFileName = projectFileName;
  45.             }
  46.             else
  47.             {
  48.                 m_projectFileName = "Default";
  49.             }
  50.  
  51.             m_logFileName = System.IO.Path.Combine(
  52.               System.IO.Path.GetDirectoryName(m_projectFileName),
  53.               System.IO.Path.GetFileNameWithoutExtension(
  54.                 m_projectFileName)) + "-ListOfImportedData.txt";
  55.  
  56.             // Construct log file name from projectFileName
  57.             // and try to open file. Project file name is
  58.             // assumed to be valid (expected to be called
  59.             // on an open doc).
  60.  
  61.             // Создаем название лог файла исходя из названия проекта и
  62.             // пробуем открыть файл.
  63.             // Предполагаем, что имя файла проекта валидное
  64.  
  65.             try
  66.             {
  67.                 m_outputFile = new StreamWriter(m_logFileName);
  68.                 m_outputFile.WriteLine("Список импортированных САПР файлов"
  69.                   + projectFileName);
  70.                 outcome = true;
  71.             }
  72.             catch (System.UnauthorizedAccessException)
  73.             {
  74.                 TaskDialog.Show("FindImports",
  75.                   "You are not authorized to create "
  76.                     + m_logFileName);
  77.             }
  78.             catch (System.ArgumentNullException) // oh, come on.
  79.             {
  80.                 TaskDialog.Show("FindImports",
  81.                   "Так не честно. Null argument for StreamWriter()");
  82.             }
  83.             catch (System.ArgumentException)
  84.             {
  85.                 TaskDialog.Show("FindImports",
  86.                   "Не удалось создать " + m_logFileName);
  87.             }
  88.             catch (System.IO.DirectoryNotFoundException)
  89.             {
  90.                 TaskDialog.Show("FindImports",
  91.                   "Такого не должно было случиться. ДИректория не найдена: "
  92.                   + System.IO.Path.GetDirectoryName(m_projectFileName));
  93.             }
  94.             catch (System.IO.PathTooLongException)
  95.             {
  96.                 TaskDialog.Show("FindImports",
  97.                   "КАжеется название файла " + m_logFileName
  98.                   + " Слишком длинное");
  99.             }
  100.             catch (System.IO.IOException)
  101.             {
  102.                 TaskDialog.Show("FindImports",
  103.                   "Ошибка ввода вывода в файле "
  104.                   + m_logFileName);
  105.             }
  106.             catch (System.Security.SecurityException)
  107.             {
  108.                 TaskDialog.Show("FindImports",
  109.                   "Нет прав доступа для записи файла "
  110.                   + System.IO.Path.GetDirectoryName(m_projectFileName)
  111.                   + "");
  112.             }
  113.             return outcome;
  114.         }
  115.  
  116.         public void startReportSection(string sectionName)
  117.         {
  118.             endReportSection();
  119.             m_outputFile.WriteLine();
  120.             m_outputFile.WriteLine(sectionName);
  121.             m_outputFile.WriteLine();
  122.  
  123.             m_currentSection = sectionName;
  124.         }
  125.  
  126.         public void logItem(string item)
  127.         {
  128.             m_outputFile.WriteLine(item);
  129.         }
  130.  
  131.         public void setWarning()
  132.         {
  133.             m_warnUser = true;
  134.         }
  135.  
  136.         public void done()
  137.         {
  138.             endReportSection();
  139.             m_outputFile.WriteLine();
  140.             m_outputFile.WriteLine("The End");
  141.             m_outputFile.WriteLine();
  142.             m_outputFile.Close();
  143.  
  144.             // Display "done" dialog, potentially open log file
  145.             // Диалог завершения и открытия лог-файла
  146.  
  147.             TaskDialog doneMsg = null;
  148.  
  149.             if (m_warnUser)
  150.             {
  151.                 doneMsg = new TaskDialog(
  152.                   "Найдены возможные проблемы. Смотрите лог файл");
  153.             }
  154.             else
  155.             {
  156.                 doneMsg = new TaskDialog(
  157.                   "Поиск импортированных САПР файлов завершен");
  158.             }
  159.  
  160.             doneMsg.AddCommandLink(
  161.               TaskDialogCommandLinkId.CommandLink1,
  162.               "Просмотр файла " + m_logFileName);
  163.  
  164.             switch (doneMsg.Show())
  165.             {
  166.                 default:
  167.                     break;
  168.  
  169.                 case TaskDialogResult.CommandLink1:
  170.                     // Display the log file
  171.                     Process.Start("notepad.exe", m_logFileName);
  172.                     break;
  173.             }
  174.         }
  175.  
  176.         public string getLogFileName()
  177.         {
  178.             return m_logFileName;
  179.         }
  180.  
  181.         private void endReportSection()
  182.         {
  183.             if (null != m_currentSection)
  184.             {
  185.                 m_outputFile.WriteLine();
  186.                 m_outputFile.WriteLine("Конец "
  187.                   + m_currentSection);
  188.                 m_outputFile.WriteLine();
  189.             }
  190.         }
  191.  
  192.         private string m_projectFileName;
  193.         private string m_logFileName;
  194.         private StreamWriter m_outputFile;
  195.         private string m_currentSection;
  196.  
  197.         /// <summary>
  198.         /// говорим пользователю что нужно просмотреть лог
  199.         /// </summary>
  200.         private bool m_warnUser;
  201.     }
  202.  
  203.     [Transaction(TransactionMode.ReadOnly)]
  204.     public class Command : IExternalCommand
  205.     {
  206.         private void listImports(Document doc)
  207.         {
  208.             FilteredElementCollector col
  209.               = new FilteredElementCollector(doc)
  210.                 .OfClass(typeof(ImportInstance));
  211.  
  212.             NameValueCollection listOfViewSpecificImports
  213.               = new NameValueCollection();
  214.  
  215.             NameValueCollection listOfModelImports
  216.               = new NameValueCollection();
  217.  
  218.             NameValueCollection listOfUnidentifiedImports
  219.               = new NameValueCollection();
  220.  
  221.             foreach (Element e in col)
  222.             {
  223.                 // Collect all view-specific names.
  224.                 // собираем все видоориентированные имена
  225.  
  226.                 if (e.ViewSpecific)
  227.                 {
  228.                     string viewName = null;
  229.  
  230.                     try
  231.                     {
  232.                         Element viewElement = doc.GetElement(
  233.                           e.OwnerViewId);
  234.                         viewName = viewElement.Name;
  235.                     }
  236.                     catch (Autodesk.Revit.Exceptions
  237.                       .ArgumentNullException) // на всякий случай проверим
  238.                     {
  239.                         viewName = String.Concat(
  240.                           "Invalid View ID: ",
  241.                           e.OwnerViewId.ToString());
  242.                     }
  243.  
  244.                     if (null != e.Category)
  245.                     {
  246.                         listOfViewSpecificImports.Add(
  247.                           importCategoryNameToFileName(
  248.                             e.Category.Name), viewName);
  249.                     }
  250.                     else
  251.                     {
  252.                         listOfUnidentifiedImports.Add(
  253.                           e.Id.ToString(), viewName);
  254.                     }
  255.                 }
  256.                 else
  257.                 {
  258.                     listOfModelImports.Add(
  259.                       importCategoryNameToFileName(
  260.                         e.Category.Name), e.Name);
  261.                 }
  262.             }
  263.  
  264.             IReportImportData logOutput
  265.               = new SimpleTextFileBasedReporter();
  266.  
  267.             if (!logOutput.init(doc.PathName))
  268.             {
  269.                 TaskDialog.Show("FindImports",
  270.                   "Не удалось создать файла отчета");
  271.             }
  272.             else
  273.             {
  274.                 if (listOfViewSpecificImports.HasKeys())
  275.                 {
  276.                     logOutput.startReportSection(
  277.                       "Видоориентированные САПР файлы");
  278.  
  279.                     listResults(listOfViewSpecificImports,
  280.                       logOutput);
  281.                 }
  282.  
  283.                 if (listOfModelImports.HasKeys())
  284.                 {
  285.                     logOutput.startReportSection("Импортированные модели");
  286.                     listResults(listOfModelImports, logOutput);
  287.                 }
  288.  
  289.                 if (listOfUnidentifiedImports.HasKeys())
  290.                 {
  291.                     logOutput.startReportSection(
  292.                       "Неизвестные импортированные файлы");
  293.                     listResults(listOfUnidentifiedImports,
  294.                       logOutput);
  295.                 }
  296.  
  297.                 if (!sanityCheckViewSpecific(
  298.                   listOfViewSpecificImports, logOutput))
  299.                 {
  300.                     logOutput.setWarning();
  301.                     //TaskDialog.Show("FindImportedData",
  302.                     //"Possible issues found. Please review the log file");
  303.                 }
  304.  
  305.                 logOutput.done();
  306.             }
  307.         }
  308.  
  309.         /// <summary>
  310.         /// Импортированные категории. Они создаются из имени
  311.         /// САПР файла + соответствующий индексю
  312.         /// Мы хотим использовать имя файла в качестве
  313.         /// первичного ключа в нашем списке импортированных файловю
  314.      
  315.         /// </summary>       
  316.         private string importCategoryNameToFileName(
  317.           string catName)
  318.         {
  319.             string fileName = catName;
  320.             fileName = fileName.Trim();
  321.  
  322.             if (fileName.EndsWith(")"))
  323.             {
  324.                 int lastLeftBracket = fileName.LastIndexOf("(");
  325.  
  326.                 if (-1 != lastLeftBracket)
  327.                     fileName = fileName.Remove(lastLeftBracket); // Удаляем левую скобку
  328.             }
  329.  
  330.             return fileName.Trim();
  331.         }
  332.  
  333.         private void listResults(
  334.           NameValueCollection listOfImports,
  335.           IReportImportData logFile)
  336.         {
  337.  
  338.             foreach (String key in listOfImports.AllKeys)
  339.             {
  340.                 logFile.logItem(key + ": "
  341.                   + listOfImports.Get(key));
  342.             }
  343.         }
  344.  
  345.         /// <summary>
  346.         /// Запускаем проверку списка видоориентированных импортов.
  347.         /// Видориентированная проверка это не то же самое
  348.         /// что и проверка по все модели.       
  349.         /// </summary>
  350.         private bool sanityCheckViewSpecific(
  351.           NameValueCollection listOfImports,
  352.           IReportImportData logFile)
  353.         {
  354.             logFile.startReportSection(
  355.               "Проверка на видоориентированные импорты");
  356.  
  357.             bool status = true;
  358.  
  359.             //Считаем количество
  360.  
  361.             foreach (String key in listOfImports.AllKeys)
  362.             {
  363.                 string[] levels = listOfImports.GetValues(key);
  364.                 if (levels != null && levels.GetLength(0) > 1)
  365.                 {
  366.                     logFile.logItem("САПР файл " + key
  367.                       + " были импортированы в "
  368.                       + "в режиме Ориентировать по виду несколько раз. "
  369.                       + "Он находится в следующих видах "
  370.                       + listOfImports.Get(key));
  371.                     status = false;
  372.                 }
  373.             }
  374.             return status;
  375.         }
  376.  
  377.         public Result Execute(
  378.           ExternalCommandData commandData,
  379.           ref string message,
  380.           ElementSet elements)
  381.         {
  382.             UIApplication uiapp = commandData.Application;
  383.             UIDocument uidoc = uiapp.ActiveUIDocument;
  384.             Document doc = uidoc.Document;
  385.  
  386.             listImports(doc);
  387.  
  388.             return Result.Succeeded;
  389.         }
  390.     }
  391. }

Спасибо Николаю за реализацию. Я последовал инструкции Николая и создал небольшой проект и импортировал три раза один DWG файл.

 

Запустив команду, будет сгенерирован отчет в текстовый файл.

 

Как обычно, самую последнюю версию можно найти на GitHub.

Жду от вас предложений как можно улучшить приложение.

Не стесняйтесь делать форки и пул-реквесты.

Источник: http://thebuildingcoder.typepad.com/blog/2015/04/list-all-import-instances.html

Автор перевода: Виктор Чекалин

Обсуждение: http://adn-cis.org/forum/index.php?topic=2731

Опубликовано 22.05.2015