Author Topic: Multiple AttSync via .Net (synchronize block definitions in many dwg-files)  (Read 5483 times)

0 Members and 1 Guest are viewing this topic.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Hi all.

AutoCAD 2009 SP3 Enu;
Windows XP SP3 Rus;
MS Visual Studio 2010 Premium Rus;
.Net Framework 3.5 SP1;

In English comments of the code I have specified a problem: I can not save a database after change in the same file.
Code: [Select]
public static void MultipleAttSync(string sourceFile, string password, string[] blockNames, string[] targetFiles) {
    if (!File.Exists(sourceFile))
        throw new FileNotFoundException(sourceFile);
    using (Database sourceDb = new Database(false, true)) {
        sourceDb.ReadDwgFile(sourceFile, System.IO.FileShare.Read, true, password);
        ObjectIdCollection blockIds = new ObjectIdCollection();

        using (Transaction t = sourceDb.TransactionManager.StartTransaction()) {
            // Открываем таблицу блоков
            BlockTable bt = (BlockTable) t.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, false);

            // Извлекаем нужные определения блоков
            foreach (BlockTableRecord btr in bt.Cast<ObjectId>().Select(n =>
                (BlockTableRecord) t.GetObject(n, OpenMode.ForRead, false))
                .Where(n => !n.IsAnonymous && !n.IsLayout && blockNames.Contains(n.Name))) {
                blockIds.Add(btr.ObjectId);
                btr.Dispose();
            }
            t.Commit();
        }
        try {
            foreach (FileInfo item in targetFiles.Select(n => new FileInfo(n)).Where(n => n.Exists)) {
                // Копируем определения блоков в нужную нам базу данных
                IdMapping mapping = new IdMapping();
                using (Database targetDb = new Database(false, true)) {
                    //Открываем целевую базу для изменений
                    targetDb.ReadDwgFile(sourceFile, FileShare.None, true, password);
                    sourceDb.WblockCloneObjects(blockIds, targetDb.BlockTableId, mapping, DuplicateRecordCloning.Replace, false);

                    //Теперь нужно выполнить синхронизацию вхождений блоков с их определениями
                    using (Transaction t = targetDb.TransactionManager.StartTransaction()) {
                        BlockTable bt = (BlockTable) t.GetObject(targetDb.BlockTableId, OpenMode.ForRead, false);
                        foreach (ObjectId id in bt) {
                            BlockTableRecord btr = (BlockTableRecord) t.GetObject(id, OpenMode.ForRead);
                            //Синхронизацию выполняем только для обновлённых определений блоков
                            if (blockNames.Select(m => m.ToLower()).Contains(btr.Name.ToLower()))
                                btr.AttSync(false, true, false);
                        }
                        t.Commit();
                    }
                    //targetDb.Save();//I get error: eFileInternalErr
                    //targetDb.SaveAs(targetDb.Filename, DwgVersion.Current);//I get message box: Error writing/closing file
                    targetDb.SaveAs(@"D:\dwg\drawings\Modified.dwg", DwgVersion.Current);//It work Successfully
                }
            }
        }
        catch (Autodesk.AutoCAD.Runtime.Exception ex) {
            throw new System.Exception(string.Format("In the course of import there was an error: {0}", ex.Message));
        }
    }
}

If it is interesting to someone - I show two more methods (and one auxiliary) which can be useful to obtaining of the necessary drawings. I am sorry for Russian comments, but from titles of methods it should be clear that they do.

Code: [Select]
        /// <summary>
        /// Получить базы данных всех чертежей, листы которых входят в состав указанной подшивки
        /// </summary>
        /// <param name="sheetSetFileName">Анализируемая подшивка</param>
        /// <returns>Возвращается перечисление найденных баз данных, листы которых используются в подшивке</returns>
        public static FileInfo[] GetDrawingsFromSheetSet(string sheetSetFileName) {
            //Проверяем наличие указанного файла
            if (!File.Exists(sheetSetFileName))
                throw new FileNotFoundException(sheetSetFileName);
            //Проверяем, что расширение файла соответствует расширению файла подшивки
            FileInfo file = new FileInfo(sheetSetFileName);
            if (file.Extension.ToLower() != ".dst")
                throw new System.Exception("Файл подшивки должен иметь расширение \".dst\"!");
            //Получаем все листы подшивки
            List<FileInfo> x = new List<FileInfo>();
            //********************
            // Получаем менеджера подшивки
            AcSmSheetSetMgr mgr = new AcSmSheetSetMgr();

            // Создаём новый объект базы данных подшивки
            AcSmDatabase db = new AcSmDatabase();

            // Пытаемся загрузить dst-файл
            try {
                db = mgr.OpenDatabase(sheetSetFileName, true);
            }
            catch (System.Exception ex) {
                throw new System.Exception(string.Format(
                    "Не удалось открыть файл подшивки. Убедитесь, что он сейчас никем не используется. Сообщение: {0}", ex.Message));
            }

            try {
                // На время блокируем базу данных подшивки от изменения извне
                db.LockDb(db);

                AcSmSheetSet ss = db.GetSheetSet();
                ProcessEnumerator(ss.GetSheetEnumerator(), ref x);
            }
            catch (System.Exception ex) {
                throw new System.Exception(string.Format(
                    "Не удалось заблокировать файл подшивки. Убедитесь, что он сейчас никем не используется. Сообщение: {0}", ex.Message));
            }
            //Снимаем блокировку базы данных подшивки
            finally {
                db.UnlockDb(db, true);
                mgr.Close(db);
            }
            //Получаем базы данных чертежей, листы которых используются в подшивке
            //Возвращаем результат обработки
            return x.Select(n => n).Distinct().ToArray();
        }

        /// <summary>
        /// Извлечение полных имён файлов, включенных в состав подшивки
        /// </summary>
        /// <param name="iter">Родительский объект</param>
        /// <param name="x">ссылка на объект List<FileInfo>, в котором инкапсулирован результат</param>
        static void ProcessEnumerator(IAcSmEnumComponent iter, ref List<FileInfo> x) {
            IAcSmComponent item = iter.Next();
            while (item != null) {
                string type = item.GetTypeName();
                switch (type) {
                    //Группа листов (рекурсивная обработка, т.к. может содержать вложенные группы листов)
                    case "AcSmSubset":
                        try {
                            AcSmSubset subset = (AcSmSubset) item;
                            string subName = subset.GetName();

                            if (!String.IsNullOrEmpty(subName)) {
                                IAcSmEnumComponent enumerator = (IAcSmEnumComponent) subset.GetSheetEnumerator();
                                ProcessEnumerator(enumerator, ref x);
                            }
                        }
                        catch { }
                        break;

                    //Лист
                    case "AcSmSheet":
                        try {
                            AcSmSheet sh = (AcSmSheet) item;
                            x.Add(new FileInfo(sh.GetLayout().GetFileName()));
                            if (!String.IsNullOrEmpty(sh.GetName())) {
                                IAcSmEnumComponent enumerator = (IAcSmEnumComponent) sh.GetSheetViews();
                                ProcessEnumerator(enumerator, ref x);
                            }
                        }
                        catch { }
                        break;
                }
                item = iter.Next();
            }
        }

        /// <summary>
        /// Получение всех чертежей, хранящихся в определённом каталоге
        /// </summary>
        /// <param name="directoryFullName">Полное имя каталога</param>
        /// <param name="subDirsBrowse">Выполнять ли поиск по подкаталагам (True - выполнять; False - не выполнять)</param>
        /// <returns>Возвращается последовательностьFileInfo</returns>
        public static FileInfo[] GetDrawingsFromDirectory(string directoryFullName, bool subDirsBrowse) {
            DirectoryInfo dir = new DirectoryInfo(directoryFullName);
            if (!dir.Exists) throw new DirectoryNotFoundException();
            return dir.GetFiles("*.dwg", subDirsBrowse ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)
                .AsEnumerable().OrderBy(n => n.DirectoryName).ThenBy(n => n.Name).ThenBy(n => n.Extension).ToArray();
        }

My test code:

Code: [Select]
[CommandMethod("MultipleAttSync", CommandFlags.Modal)]
public void MultipleAttSync() {
    string[] blockNames = new string[] { "blockDef_1", "BlockDef_2", "BlockDef_3"};
    string[] dwgFiles = AcEnvironment.GetDrawingsFromDirectory(@"D:\dwg\drawings", true).Select(n=>n.FullName).ToArray();
    AcEnvironment.MultipleAttSync(@"D:\dwg\block definitions\Block definitions.dwg", "", blockNames, dwgFiles);
}

Why at me it is impossible to save changes in the same file?
« Last Edit: October 14, 2011, 08:32:21 AM by Andrey »

bargool

  • Guest
Re: MultipleAttSync via .Net (for many dwg-files)
« Reply #1 on: October 11, 2011, 08:29:15 AM »
Did you try Autodesk.AutoCAD.DatabaseServices.FileOpenMode except System.IO.FileShare in ReadDwgFile? (this is for Acad 2010, but maybe in 2009 you also have the same?)

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: MultipleAttSync via .Net (for many dwg-files)
« Reply #2 on: October 11, 2011, 08:40:19 AM »
I have tried so:

Code: [Select]
targetDb.ReadDwgFile(sourceFile, FileOpenMode.OpenForReadAndWriteNoShare, true, password);
Has received an error: eFileSharingViolation

It happens in line:

Code: [Select]
targetDb.ReadDwgFile(sourceFile, FileOpenMode.OpenForReadAndWriteNoShare, true, password);
The third variant of this method - only with pointer usage:

Code: [Select]
public void ReadDwgFile (IntPtr drawingFile, bool allowCPConversion, string password);
:(
« Last Edit: October 11, 2011, 08:44:58 AM by Andrey »

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: MultipleAttSync via .Net (for many dwg-files)
« Reply #3 on: October 11, 2011, 09:44:23 AM »
The error is found!!!
Instead of
targetDb. ReadDwgFile (sourceFile, FileOpenMode. OpenForReadAndWriteNoShare, true, password);
It was necessary to write
targetDb. ReadDwgFile (item, FileOpenMode. OpenForReadAndWriteNoShare, true, password);
I had a misprint: sourceFile instead of item!

Now all normally works.
:)

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: MultipleAttSync via .Net (for many dwg-files)
« Reply #4 on: October 14, 2011, 06:38:43 AM »
Plugin for AutoCAD 2009 SP3 x86.
.Net Framework 3.5 SP1

In AutoCAD command line run "netload".
Select BlockSync.dll.
Run "BlockSync" command.



Plugin is attached.
« Last Edit: October 14, 2011, 08:12:00 AM by Andrey »

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: MultipleAttSync via .Net (for many dwg-files)
« Reply #5 on: October 14, 2011, 08:13:06 AM »
I have updated the program distribution kit (in the same link) - have rectified an error in the code.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
If it is interesting to someone...

New version BlockSync is accessible. On the same page are laid out:

- The compiled version of a plug-in for AutoCAD 2009 SP3 x86.
- The full source code of the project (MS a Visual Studio 2010)

That the new:
1. The configuration file PluginSettings.xml which contents explicitly are documented in its comments (the program is added remembers the previous variants of sampling and by default opens the same directories (for each adjustment - the) and as includes/disconnects ticks in additional options).
2. Error messages (the access file/is not present to a file/not isn't found it was possible to read adjustments/etc.) will be deduced with more clear users the description.
3. It is possible to specify target files not only by the local machine, but also in a network.
4. All messages are localized (en-US/ru-RU).
5. It is possible to specify the necessary localization forcedly in a file of adjustments (by default it such what sets it AutoCAD).

The rectified errors:
1. Multiline attributes after synchronization didn't save values
2. For the main determination of the dynamic block (not its anonymous successors) weren't updated attributes in entrances
3. The geometry in dynamic bloks wasn't updated.
4. In entrances of dynamic blocks positioning of some attributes was dropped in a point 0,0.