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

ADN Club => AutoCAD .NET API => Тема начата: SatanaXIII от 12-10-2018, 10:57:00

Название: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: SatanaXIII от 12-10-2018, 10:57:00
Господа, пытаюсь на C# создать в электрикале новый чертеж в текущем проекте.
Чертеж-то создаю, но не могу найти как привязать его к проекту - имя проекта текущего документа и системная переменная с именем текущего проекта пусты.
Полная ж задача сводится к тому, что надо создавать сразу несколько чертежей по заданным шаблонам - аналогично действию, когда в диспетчере проектов, при нажатии правой кнопкой мыши, выбирается пункт "создать чертеж" и открывается окно для заполнения его свойств (имя, шаблон, описание, значения для листа).

Код - C# [Выбрать]
  1. public class CreateNewDrawInProj: IExtensionApplication
  2. {
  3.         [CommandMethod("qwe")]
  4.         public void Qwe()
  5.         {
  6.             Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
  7.             Editor ed = doc.Editor;
  8.             Database db = doc.Database;
  9.  
  10.             String CurrFileName = doc.Name;
  11.  
  12.             String TemplateFileName = @"C:\Template\A3-1.dwt"; // Директория с шаблонами
  13.             NewFileName = System.IO.Path.GetDirectoryName(CurrFileName) + System.IO.Path.DirectorySeparatorChar + "NewFileName.dwg";
  14.  
  15.             if (System.IO.File.Exists(NewFileName))
  16.                 System.IO.File.Delete(NewFileName);
  17.  
  18.             Database NewDB = new Database(false, true);
  19.            
  20.             NewDB.ReadDwgFile(TemplateFileName, FileOpenMode.OpenForReadAndWriteNoShare, true, "");
  21.               NewDB.ProjectName = db.ProjectName; // Empty
  22.               Object ProlName = Application.GetSystemVariable("PROJECTNAME"); // Empty
  23.             NewDB.CloseInput(true);
  24.             NewDB.SaveAs(NewFileName, DwgVersion.Newest);
  25.            
  26.             Application.DocumentManager.ExecuteInApplicationContext(CreateNewDoc, null);
  27.         }
  28.  
  29.         private String NewFileName = string.Empty;
  30.         private void CreateNewDoc(object userdata)
  31.         {
  32.             Document NewDoc = Application.DocumentManager.Open(NewFileName, false);
  33.             Application.DocumentManager.MdiActiveDocument = NewDoc;
  34.         }
  35.  
  36.         public void Initialize()
  37.         {
  38.         }
  39.         public void Terminate()
  40.         {
  41.         }
  42. }
  43.  

Я знаю, что список включенных в проект чертежей хранится в специальном XML-файле имя_проекта.aepx.
Неужели надо вручную туда дописывать добавляемые чертежи. И опять же как все же узнать имя текущего проекта?
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 12-10-2018, 13:17:56
Насколько я в курсе в AutoCAD Electrical всё API на AutoLISP. Например, функция (ace_getactiveproject) возвращает активный проект.
А (c:ace_add_dwg_to_project  dwg2add  paramlst) добавляет чертежи в текущий проект.


 
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: SatanaXIII от 12-10-2018, 13:32:51
Александр Ривилис, ясно, спасибо. Придется копать в сторону AutoLisp.
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 12-10-2018, 13:51:05
Александр Ривилис, ясно, спасибо. Придется копать в сторону AutoLisp.

Можно попытаться вызвать lisp-функции из .NET: P/Invoke для acedEvaluateLisp
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: SatanaXIII от 12-10-2018, 14:02:50
Можно попытаться вызвать lisp-функции из .NET
Спасибо за идею, а то совсем тоска.
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: SatanaXIII от 18-10-2018, 17:22:20
Значит
0) Путем упорного гугления собрал воедино советы уважаемого Александра Ривилиса, касательно данного вопроса, разбросанные в веках.
1) Загрузил и распаковал ObjectARX2018.
2) Подключил из него к проекту три библиотеки (AcCoreMgd.dll, AcDbMgd.dll, AcMgd.dll).
3) С помощью утилиты dumpbin (dumpbin.exe /exports accore.dll >accore.txt), поставляемой вместе с вижуал студией, обнаружил в этих библиотеках точку входа в функцию acedEvaluateLisp (?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z)
4) Используя технологию P/Invoke, подключил к своей библиотеке функцию acedEvaluateLisp.
5) Используя AcadEvalLisp попытался выполнить в контексте автокада ace_getactiveproject, но на этом застопорился.
Не могу обработать возвращаемый результат - (5014,-1). То ли 5014 это код ошибки, то ли нет, но в любом случае -1 это не имя текущего проекта.
Если передать в AcadEvalLisp строковый параметр "(+ 100 50 30 20 10)", то результат (5003,210), где 210 и есть сумма всех слагаемых. При этом 5003 это не 5014.
Прошу подсказать что можно предпринять.

Код - C# [Выбрать]
  1. public class DrawDraw
  2. {
  3.     [System.Security.SuppressUnmanagedCodeSecurity]
  4.     [DllImport("accore.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl,
  5.     EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")] // dumpbin.exe /exports accore.dll >accore.txt
  6.     extern private static int acedEvaluateLisp(string lispLine, out IntPtr result);
  7.  
  8.     static public ResultBuffer AcadEvalLisp(string arg)
  9.     {
  10.         IntPtr rb = IntPtr.Zero;
  11.         acedEvaluateLisp(arg, out rb);
  12.  
  13.         if (rb != IntPtr.Zero)
  14.         {
  15.             try
  16.             {
  17.                 ResultBuffer rbb = DisposableWrapper.Create(typeof(ResultBuffer), rb, true) as ResultBuffer;
  18.                 return rbb;
  19.             }
  20.             catch
  21.             {
  22.                 return null;
  23.             }
  24.         }
  25.         return null;
  26.     }
  27.  
  28.     [CommandMethod("qqqtest")]
  29.     static public void test()
  30.     {
  31.         ResultBuffer rb = AcadEvalLisp("ace_getactiveproject"); // Без проверки на существование функции
  32.         if (rb != null)
  33.         {
  34.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(rb.ToString()); // Результат выполнения: (5014,-1)
  35.         }
  36.         else
  37.         {
  38.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nError in evaluation");
  39.         }
  40.     }
  41. }
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 18-10-2018, 17:26:02
        ResultBuffer rb = AcadEvalLisp("ace_getactiveproject"); // Без проверки на существование функции
А скобки где? Это же функция. Должно быть так:
Код - C# [Выбрать]
  1.        ResultBuffer rb = AcadEvalLisp("(ace_getactiveproject)");
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 18-10-2018, 17:27:26
Заодно проверь что будет если ты в командной строке AutoCAD Electrical введёшь:
Код - Auto/Visual Lisp [Выбрать]
  1. (ace_getactiveproject)
(т.е. просто вызовешь эту lisp-функцию).
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 18-10-2018, 17:29:44
Не могу обработать возвращаемый результат - (5014,-1). То ли 5014 это код ошибки, то ли нет, но в любом случае -1 это не имя текущего проекта.
Код - C++ [Выбрать]
  1. #define RTVOID    5014 /* Blank symbol */
Т.е. 5014 - это "ничего".
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 18-10-2018, 22:49:34
Не поленился и скачал AutoCAD Electrical 2019. В его составе есть файл с документацией ACE_API.chm. В нём описание методов работы с API Electrical. В частности указано как можно работать через .NET. Вместо P/Invoke acedEvaluateLisp используется acedInvoke:

(https://farm2.staticflickr.com/1977/43596701530_c7ffd9f9ae_o.png)



Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: SatanaXIII от 19-10-2018, 09:23:33
А скобки где? Это же функция. Должно быть так
Буду знать, спасибо.
Со скобками все завелось.
Вместо P/Invoke acedEvaluateLisp используется acedInvoke
Пробовал применить acedInvoke, но при вызове получал access violation. Не стал разбираться, вернулся к acedEvaluateLisp. Если сейчас дожму вызов лисповских функций, то вернусь к Invoke.
О результатах отпишусь. Еще раз спасибо, что не ленитесь.  :)
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 19-10-2018, 14:16:43
Пробовал применить acedInvoke, но при вызове получал access violation. Не стал разбираться, вернулся к acedEvaluateLisp.
И правильно сделал.
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: SatanaXIII от 29-10-2018, 12:07:00
О результатах отпишусь.
Код - C# [Выбрать]
  1. public class test : IExtensionApplication
  2. {
  3.     [System.Security.SuppressUnmanagedCodeSecurity]
  4.     [System.Runtime.InteropServices.DllImport("accore.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode, CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")]
  5.     extern public static int acedEvaluateLisp(string lispLine, out IntPtr result);
  6.  
  7.     [CommandMethod("qwe", CommandFlags.Session)]
  8.     public void Qwe()
  9.     {
  10.         Document CurrDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
  11.  
  12.         ResultBuffer rb = DrawDraw.AcadEvalLisp("(ace_getactiveproject)");
  13.         TypedValue tv = rb.AsArray().FirstOrDefault();
  14.         String CurrFileName = tv.Value.ToString();
  15.  
  16.         String TemplateFileName = @"C:\Template\А3 С4 л2.dwt";
  17.         String FileName = "NewFileName.dwg";
  18.         String FullFileName = Path.Combine(Path.GetDirectoryName(CurrFileName), FileName);
  19.  
  20.            
  21.  
  22.         Database NewDB = new Database(false, true);
  23.         NewDB.ReadDwgFile(TemplateFileName, FileOpenMode.OpenForReadAndWriteNoShare, true, "");
  24.         NewDB.CloseInput(true);
  25.            
  26.                    
  27.         if (File.Exists(FullFileName)) // Если есть файл с таким именем, то удалить
  28.         {
  29.             var AllOpenDocs = Application.DocumentManager.Cast<Document>(); // Если открыт, то закрыть
  30.             foreach (Document d in AllOpenDocs)
  31.                 if (d.Name.ToLower().Equals(FullFileName.ToLower()))
  32.                     d.CloseAndDiscard(); // Должен быть установлен флаг CommandFlags.Session
  33.  
  34.             File.Delete(FullFileName);
  35.         }
  36.         NewDB.SaveAs(FullFileName, DwgVersion.Newest);
  37.  
  38.         Application.DocumentManager.ExecuteInApplicationContext(OpenNewTemplateDoc, FullFileName);
  39.         rb = DrawDraw.AcadEvalLisp("(c:ace_add_dwg_to_project nil (list nil nil (list Discr1 Discr2 Discr3) nil nil))"); // Здесь первый nil имеется в виду текущий документ MdiActiveDocument, созданный в OpenNewTemplateDoc
  40.     }
  41.  
  42.     private void OpenNewTemplateDoc(object FileName)
  43.     {
  44.         Document NewDoc = Application.DocumentManager.Open(FileName.ToString(), false);
  45.         Application.DocumentManager.MdiActiveDocument = NewDoc;
  46.     }
  47. }

И опять спотыкаюсь об ровное место. Вроде все верно, но не добавляется созданный чертеж к проекту.
В тридцать девятой строке вызов ace_add_dwg_to_project возвращает единицу - то есть все успешно выполнилось, но в проекте ничего не меняется.
Если руками ввести команду из тридцать девятой строки в консоль автокада, то чертеж добавляется. А вот программно не хочет.
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 29-10-2018, 13:30:08
1. Сомневаюсь, что acedEvaluateLisp работает в контексте приложения, как и lisp воообще. Когда ты запускаешь этот lisp-код из командной строки, то ты работаешь в контексте открытого документа.
2. В lisp-выражении есть Discr1, Discr2, Discr3. Это что-такое? Ты не передаёшь это значение в код.
3. После NewDB.SaveAs(FullFileName, DwgVersion.Newest); должно быть NewDB.Dispose()
4. Если у команды есть флаг CommandFlags.Session, то не нужен вызов ExecuteInApplicationContext
5. Не нужно засовывать код в спойлер, если в нём меньше 300 строк.
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 29-10-2018, 14:11:31
Попробуй для начала вместо первого nil в AcadEvalLisp указать полный путь к добавляемому файлу. Только не забудь про экранирование символов. Например,
Код - C# [Выбрать]
  1. rb = DrawDraw.AcadEvalLisp("(c:ace_add_dwg_to_project \"c:/temp/test.dwg\" nil (list nil nil (list Discr1 Discr2 Discr3) nil nil))");
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: SatanaXIII от 29-10-2018, 14:41:27
1. Сомневаюсь, что acedEvaluateLisp работает в контексте приложения
Тогда б в двенадцатой строке должен был тоже некорректно отрабатывать вызов.
2. В lisp-выражении есть Discr1, Discr2, Discr3. Это что-такое? Ты не передаёшь это значение в код.
Это описание для создаваемого документа. Можно заменить на nil впринципе.
3. После NewDB.SaveAs(FullFileName, DwgVersion.Newest); должно быть NewDB.Dispose()
Сборщик мусора все равно вызовет Dispose при выходе из функции. Но ладно.
4. Если у команды есть флаг CommandFlags.Session, то не нужен вызов ExecuteInApplicationContext
5. Не нужно засовывать код в спойлер, если в нём меньше 300 строк.
Понял.
Попробуй для начала вместо первого nil в AcadEvalLisp указать полный путь к добавляемому файлу
Пробовал. Именно экранирующие слеши, передающиеся в строке в автокад, и не распарсиваются.
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 29-10-2018, 15:35:25
Пробовал. Именно экранирующие слеши, передающиеся в строке в автокад, и не распарсиваются.
Переведи. Замени обратные слэши на прямые, т.е. "\\" на "/"
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: Александр Ривилис от 29-10-2018, 16:32:42
Сборщик мусора все равно вызовет Dispose при выходе из функции.
Во-первых ты путаешь, С# и C++. В C++ действительно при выходе из области видимости для автоматической переменной вызывается деструктор. В C# сборщик мусора может вызвать Dispose() в любой момент времени. Может вообще не вызвать до завершения работы AutoCAD.
Тогда б в двенадцатой строке должен был тоже некорректно отрабатывать вызов.
Не совсем так. Кое-что работает и в контексте приложения, но далеко не всё. Кстати при работе в контексте приложения если меняется Database, то следует её блокировать. Не пишет ли что-то (c:ace_add_dwg_to_project) в сам чертеж я сказать не могу. Так что лучше его на всякий случай заблокировать.
Название: Re: Создать чертеж в текущем проекте AutoCAD Electrical
Отправлено: SatanaXIII от 30-10-2018, 11:59:34
Александр Ривилис, собака не в этом порылась в любом случае.
После выполнения моей команды автокад вообще перестает принимать свои родные команды.
То есть до запуска моей команды в командную строку автокада пишешь (c:ace_add_dwg_to_project nil (list nil nil (list Discr1 Discr2 Discr3) nil nil)) и он добавляет текущий чертеж в проект. После запуска моей команды пишешь в командную строку все тоже самое, но автокад не реагирует.
Я нашел вот эту тему, где решается моя проблема: https://forums.autodesk.com/t5/autocad-electrical-forum/add-drawings-to-active-project/td-p/4672543
Если в трех словах, то надо обновить проект (командой (c:ace_wdp_reread)).
С учетом всех замечаний рабочий код вот такой:
Код - C# [Выбрать]
  1.     public class test : IExtensionApplication
  2.     {
  3.         [System.Security.SuppressUnmanagedCodeSecurity]
  4.         [System.Runtime.InteropServices.DllImport("accore.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode, CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "?acedEvaluateLisp@@YAHPEB_WAEAPEAUresbuf@@@Z")]
  5.         extern public static int acedEvaluateLisp(string lispLine, out IntPtr result);
  6.      
  7.         [CommandMethod("qwe", CommandFlags.Session)]
  8.         public void Qwe()
  9.         {
  10.             Document CurrDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
  11.      
  12.             ResultBuffer rb = DrawDraw.AcadEvalLisp("(ace_getactiveproject)");
  13.             TypedValue tv = rb.AsArray().FirstOrDefault();
  14.             String CurrFileName = tv.Value.ToString();
  15.      
  16.             String TemplateFileName = @"C:\Template\А3 С4 л2.dwt";
  17.             String FileName = "NewFileName.dwg";
  18.             String FullFileName = Path.Combine(Path.GetDirectoryName(CurrFileName), FileName);
  19.      
  20.                
  21.      
  22.             Database NewDB = new Database(false, true);
  23.             NewDB.ReadDwgFile(TemplateFileName, FileOpenMode.OpenForReadAndWriteNoShare, true, "");
  24.             NewDB.CloseInput(true);
  25.                
  26.                        
  27.             if (File.Exists(FullFileName)) // Если есть файл с таким именем, то удалить
  28.             {
  29.                 var AllOpenDocs = Application.DocumentManager.Cast<Document>(); // Если открыт, то закрыть
  30.                 foreach (Document d in AllOpenDocs)
  31.                     if (d.Name.ToLower().Equals(FullFileName.ToLower()))
  32.                         d.CloseAndDiscard(); // Должен быть установлен флаг CommandFlags.Session
  33.      
  34.                 File.Delete(FullFileName);
  35.             }
  36.             NewDB.SaveAs(FullFileName, DwgVersion.Newest);
  37.             NewDB.Dispose();
  38.  
  39.             Document NewDoc = Application.DocumentManager.Open(FullFileName, false);
  40.             Application.DocumentManager.MdiActiveDocument = NewDoc;
  41.             using (NewDoc.LockDocument())
  42.             {
  43.                 rb = DrawDraw.AcadEvalLisp("(c:ace_wdp_reread)");
  44.                 rb = DrawDraw.AcadEvalLisp("(c:ace_add_dwg_to_project nil (list nil nil (list nil nil nil) nil nil))");
  45.             }
  46.         }
  47.     }

Остается только прикрутить это все на формочку.