Синхронизация множества чертежей с определениями блоков, хранящимися во внешнем файле

Опубликовано 11.10.2011 21:35
Отредактировано 18.10.2011 14:44

Пишем метод, с помощью которого можно было бы синхронизировать определения блоков, хранящихся во внешних файлах, с определениями блоков, хранящимися в рабочих чертежах. Помимо синхронизации определений блоков, должна происходить автоматическая синхронизация и вхождений блоков. Причём эта операция должна выполняться пакетно: либо на файлах нужного каталога, либо на файлах определённой подшивки, либо на произвольно указанном наборе файлов - чтобы не приходилось вручную открывать каждый чертёж и выполнять синхронизацию его содержимого.  Этот метод запишем в статический класс AcEnvironment, созданный ранее здесь (там ему и самое место).

Дополнительные ссылки, относящиеся к данной теме:

Имея на руках то, что содержится по указанным выше ссылкам - пишем нужный код...

Код C#

   1:          /// <summary>
   2:          /// Для всех целевых чертежей синхронизировать указанные определения блоков (если они есть) с
   3:          /// определением блоков из внешнего файла. По завершению синхронизации определений блоков выполнить синхронизацию
   4:          /// вхождений блоков с их обновлёнными определениями во всех обрабатываемых чертежах.
   5:          /// </summary>
   6:          /// <param name="sourceFile">Чертёж, определения блоков которого взяты за эталон</param>
   7:          /// <param name="password">Пароль к чертежу, определения блоков которого приняты за эталон</param>
   8:          /// <param name="blockNames">Имена определений блоков, синхронизацию которых следует выполнить</param>
   9:          /// <param name="targetFiles">Целевые файлы, в составе которых следует обновить определения блоков и их вхождения</param>
  10:          /// <param name="directOnly">Следует ли искать только на верхнем уровне, или же нужно 
  11:          /// анализировать и вложенные вхождения, т.е. следует ли рекурсивно обрабатывать блок в блоке:
  12:          /// true - только верхний; false - рекурсивно проверять вложенные блоки.</param>
  13:          /// <param name="removeSuperfluous">
  14:          /// Следует ли во вхождениях блока удалять лишние атрибуты (те, которых нет в определении блока).</param>
  15:          /// <param name="setAttDefValues">
  16:          /// Следует ли всем атрибутам, во вхождениях блока, назначить текущим значением значение по умолчанию.</param>
  17:          public static void BlockSync(string sourceFile, string password, string[] blockNames, string[] targetFiles,
  18:              bool directOnly, bool removeSuperfluous, bool setAttDefValues) {
  19:              if (!File.Exists(sourceFile))
  20:                  throw new FileNotFoundException(string.Format(AutoCAD_Resources.FileNotFound, sourceFile));
  21:              using (Database sourceDb = new Database(false, true)) {
  22:                  try {
  23:                      sourceDb.ReadDwgFile(sourceFile, System.IO.FileShare.Read, true, password);
  24:                  }
  25:                  catch (System.Exception ex) {
  26:                      throw new System.Exception(string.Format(AutoCAD_Resources.FileOpeningError, sourceFile), ex);
  27:                  }
  28:                  
  29:                  ObjectIdCollection blockIds = new ObjectIdCollection();
  30:   
  31:                  using (Transaction t = sourceDb.TransactionManager.StartTransaction()) {
  32:                      // Открываем таблицу блоков
  33:                      BlockTable bt = (BlockTable) t.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, false);
  34:   
  35:                      // Извлекаем нужные определения блоков
  36:                      foreach (BlockTableRecord btr in bt.Cast<ObjectId>().Select(n =>
  37:                          (BlockTableRecord) t.GetObject(n, OpenMode.ForRead, false))
  38:                          .Where(n => !n.IsAnonymous && !n.IsLayout && blockNames.Contains(n.Name))) {
  39:                          blockIds.Add(btr.ObjectId);
  40:                          btr.Dispose();
  41:                      }
  42:                      t.Commit();
  43:                  }
  44:                  try {
  45:                      foreach (FileInfo item in targetFiles.Select(n => new FileInfo(n)).Where(n => n.Exists)) {
  46:                          // Копируем определения блоков в нужную нам базу данных
  47:                          IdMapping mapping = new IdMapping();
  48:                          using (Database targetDb = new Database(false, true)) {
  49:                              //Открываем целевую базу для изменений
  50:                              try {
  51:                                  targetDb.ReadDwgFile(item.FullName, FileOpenMode.OpenForReadAndWriteNoShare, true, password);
  52:                              }
  53:                              catch (System.Exception ex) {
  54:                                  throw new System.Exception(string.Format(AutoCAD_Resources.FileOpeningError, item.FullName), ex);
  55:                              }                            
  56:                              sourceDb.WblockCloneObjects(blockIds, targetDb.BlockTableId, mapping, DuplicateRecordCloning.Replace, false);
  57:   
  58:                              //Теперь нужно выполнить синхронизацию вхождений блоков с их определениями
  59:                              using (Transaction t = targetDb.TransactionManager.StartTransaction()) {
  60:                                  BlockTable bt = (BlockTable) t.GetObject(targetDb.BlockTableId, OpenMode.ForRead, false);
  61:                                  foreach (ObjectId id in bt) {
  62:                                      BlockTableRecord btr = (BlockTableRecord) t.GetObject(id, OpenMode.ForRead);
  63:                                      //Синхронизацию выполняем только для обновлённых определений блоков
  64:                                      if (blockNames.Select(m => m.ToLower()).Contains(btr.Name.ToLower()))
  65:                                          btr.AttSync(directOnly, removeSuperfluous, setAttDefValues);
  66:                                  }
  67:                                  t.Commit();
  68:                              }
  69:                              targetDb.SaveAs(targetDb.Filename, DwgVersion.Current);
  70:                          }
  71:                      }
  72:                  }
  73:                  catch (Autodesk.AutoCAD.Runtime.Exception ex) {
  74:                      throw new System.Exception(string.Format(AutoCAD_Resources.ErrorOfImport, ex.Message));                    
  75:                  }
  76:              }

В данном коде я вызываю метод AttSync (см. строку 54 кода) - этот метод написан здесь. Теперь создадим тестовые чертежи с блоками и напишем тестовый пример кода, дабы проверить работоспособность метода:

Тестовый пример C#

   1:  [CommandMethod("BlockSync", CommandFlags.Modal)]
   2:  public void BlockSync() {
   3:      string[] blockNames = new string[] { "blockDef_1", "BlockDef_2", "BlockDef_3"};
   4:      string[] dwgFiles = AcEnvironment.GetDrawingsFromDirectory(@"D:\dwg\drawings", true)
   5:          .Select(n=>n.FullName).ToArray();
   6:      AcEnvironment.BlockSync(@"D:\dwg\block definitions\Block definitions.dwg", "", 
   7:          blockNames, dwgFiles, false, true, false);
   8:  }

Обратите внимание на строки 6 и 7 кода: мы вызываем метод написанного ранее класса. Этот метод рекурсивно выбирает чертежи из каталога. Можно было указать файл подшивки - произошла бы выборка всех чертежей, листы которых включены в состав этой подшивки.

Собственно пример отработал как нужно - произошла полная синхронизация. Теперь, имея на руках метод BlockSync, можно легко написать команду с GUI (графическим интерфейсом), через который пользователь смог бы задавать нужный набор параметров для этого метода. Я написал и эту команду, выглядит сие так:


Откомпилированную библиотеку для AutoCAD 2009 SP3 можно скачать отсюда. По умолчанию локализация интерфейса используется та же, какая у AutoCAD. Однако при желании можно принудительно указать нужную (английскую или русскую), задав нужное значение в файле настроек (PluginSettings.xml).

Исходный код проекта (MS VS 2010) выложен здесь - желающие могут перекомпилировать его под другие версии AutoCAD (2010-2012).

Comments