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

ADN Club => ObjectARX => Тема начата: Ura от 24-08-2016, 13:35:29

Название: Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016
Отправлено: Ura от 24-08-2016, 13:35:29
Добрый день

В старых версиях AutoCAD использовалась команда acedCommand
Если требовалось вводить переменное кол-во данных, можно было команду разделять на несколько частей

Сейчас команду acedCommand заменили командой acedCommandS (когда имеется фиксированный набор данных)
и acedCommandС (если пользователь в ручную вводит неопределенное кол-во данных)

Вопрос как выполнить команду, в которой имеется переменное кол-во параметров, при этом данные содержатся в отдельном массиве (а не вводятся пользователем)?

Вот пример старого кода, который объединяет в полилинию указанные объекты.


Код - C++ [Выбрать]
  1.                 for (i=0; i < KolObj; i++) {
  2.                         if (AllObj[i].numTypeLine != 1) continue;       //проверка типа
  3.                         if (!newline) { //если это первый объект - выполняем заголовок команды
  4.                                 acedCommand(RTSTR, _T("_.pedit"), RTSTR, _T("_M"), 0);
  5.                                 newline=true;
  6.                         }
  7.                         acedCommand(RTENAME, AllObj[i].Name , 0);       //вводим неопределенный масив данных
  8.                 }
  9.  
  10.                 if (newline) { //если в наборе имеется хотябы один объект, завершаем команду
  11.                         acedCommand(RTSTR, _T(""), RTSTR, _T("_Y"), RTSTR, _T("_J"), RTSTR, _T("0.0"), RTSTR, _T(""), 0);
  12.                 }

Фактически команда делится на 3 части
Начало, ввод данных и завершение команды (если было введено одно или несколько значений)
      
Как мне выполнить команду в AutoCAD2015-2016?
Название: Re: Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016
Отправлено: Александр Ривилис от 24-08-2016, 15:53:39
1. Прочитай у меня в подписи как следует форматировать код на форуме.
2. Прочитай эту статью: http://adndevblog.typepad.com/autocad/2014/04/migration-after-fiber-is-removed-in-autocad-2015.html
Если будет что-то непонятно - продолжим. Хотя конечно лучше не пользоваться командными методами, а переписать всё средствами ObjectARX. Например так: http://adn-cis.org/kak-obedinit-otrezki-i-dugi-v-poliliniyu.html
Название: Re: Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016
Отправлено: Ura от 26-08-2016, 12:20:10
Спасибо за ссылки и код


Сделал тестовый пример для ввода, используя функцию acedCommandC
Функция работает, но только при ручном вводе.

Код - C++ [Выбрать]
  1. void testcmd()
  2. {
  3.         //функция вычерчивания линии
  4.         acedCommandC(&myCallbackFn, NULL,
  5.                                 RTSTR, _T("_Line"),
  6.                                 RTSTR, _T("0,0"),
  7.                                 RTSTR, _T("111,111"),
  8.                                 RTNONE);
  9. }
  10.  
  11. //функция обратного вызова - продолжение ввода данных пользователем
  12. int myCallbackFn(void * pData){
  13.       int nReturn = RTNONE;
  14.       if (isLineActive())  nReturn = acedCommandC(&myCallbackFn, NULL,
  15.                                         RTSTR, PAUSE,   //ввод данных пользователем
  16.                                         RTNONE);
  17.          else { //Если функция успешно завершена
  18.                 acutPrintf(_T("\nВвод данных завершен - выход из процедуры ввода\n"));
  19.          }
  20.    return nReturn;
  21. }
  22.  
  23. //проверка состояния команды - активна или нет
  24. Adesk::Boolean isLineActive()
  25. {
  26.         struct resbuf rb;
  27.         acedGetVar(_T("CMDNAMES"), &rb);
  28.         if (_tcsstr(rb.resval.rstring, _T("LINE")))
  29.                 return Adesk::kTrue;
  30.         return Adesk::kFalse;
  31. }

На сколько я понял, для программного ввода данных, в функцию нужно передавать второй параметр MyData,
acedCommandC(&myCallbackFn, MyData, ...
где MyData - указатель на структуру, содержащую массив данных и текущий индекс массива данных
При этом функция контроля состояния isLineActive() ненужна. Мы сами контролируем ввод внутри функции myCallbackFn()

В функции myCallbackFn() нужно организовать ввод одного элемента массива и увеличить индекс (счетчик)
Функция myCallbackFn() будет вызывать сама себя, пока не введет весь массив данных.

По завершении массива, нужно ввести завершающую часть команды (выход из команды)
nReturn = acedCommandC(&myCallbackFn, NULL, RTSTR, _T(""), RTNONE);

При следующей итерации нужно просто выйти из функции (не вызывая acedCommandC)
вернув значение RTNONE
int nReturn = RTNONE;
...
return nReturn;


Разработчики существенно усложнили создание кода.  :(
Теперь нужно будет создавать множество дополнительных функций обратного вызова
или думать как их можно унифицировать...
Название: Re: Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016
Отправлено: Александр Ривилис от 26-08-2016, 12:59:50
Функция работает, но только при ручном вводе.
Не понял эту фразу. Ты смог сделать то, что хотел или нет? Подумай над использованием acedCmdS или acedCmdC. Для первой из указанных функций ты можешь динамически создать связный список resbuf на основе своих данных, если не требуется во время работы команды взаимодействовать с пользователем. Тут функция обратного вызова не потребуется. Для второй функции аналогично, но можно и взаимодействовать с пользователем в функции обратного вызова по аналогии с acedCommandC.
Название: Re: Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016
Отправлено: Ura от 26-08-2016, 15:18:34
Я сделал упрощенный тестовый пример, как описал, проверил работу программы в отладчике - работает
Полный вариант собирался сделать немного позже, нужно адаптировать приложение для AutoCAD2015-2016, а там таких мест много.
Тест делал упрощенный, поэтому код и не выкладывал, а только описал логику.

Я рассматривал возможность использования команд acedCmdS и acedCmdC
Было бы удобно создать сразу список и потом его выполнить
Но я не нашел какой функцией можно добавлять записи в список resbuf

Для создания списка resbuf используется команда acutBuildList()
Но в команде мы указываем фиксированное кол-во данных.
Какой командой к существующему списку resbuf добавить новые данные я не нашел.
В С# такая команда вроде бы имеется.

Было бы интересно узнать как динамически можно создавать список resbuf
Название: Re: Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016
Отправлено: Александр Ривилис от 26-08-2016, 15:56:54
Для создания списка resbuf используется команда acutBuildList()
Не только. Есть еще функция acutNewRb для создания единичного resbuf, который можно добавить в хвост к существующему списку resbuf.
Название: Re: Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016
Отправлено: Александр Ривилис от 26-08-2016, 17:56:18
Подготовил пример. Думаю что с ним должно быть всё понятно:

Код - C++ [Выбрать]
  1. //-----------------------------------------------------------------------------
  2. //----- acrxEntryPoint.cpp
  3. //-----------------------------------------------------------------------------
  4. #include "StdAfx.h"
  5. #include "resource.h"
  6. #include <acedCmdNF.h>
  7.  
  8. //-----------------------------------------------------------------------------
  9. #define szRDS _RXST("")
  10.  
  11. //-----------------------------------------------------------------------------
  12. //----- ObjectARX EntryPoint
  13. class CArxProject8App : public AcRxArxApp {
  14. public:
  15.         CArxProject8App () : AcRxArxApp () {}
  16.         virtual AcRx::AppRetCode On_kInitAppMsg (void *pkt) {
  17.                 AcRx::AppRetCode retCode = AcRxArxApp::On_kInitAppMsg (pkt) ;
  18.                 return (retCode) ;
  19.         }
  20.  
  21.         virtual AcRx::AppRetCode On_kUnloadAppMsg (void *pkt) {
  22.                 AcRx::AppRetCode retCode = AcRxArxApp::On_kUnloadAppMsg (pkt) ;
  23.                 return (retCode) ;
  24.         }
  25.  
  26.         virtual void RegisterServerComponents () {      }
  27.        
  28.         static void RivilisMyLine () {
  29.                 AcGePoint3dArray pts;
  30.                 ads_point pt;
  31.                 // Запрашиваем поочередно точки для команды
  32.                 while (acedGetPoint((pts.length() == 0) ? NULL : asDblArray(pts.last()),
  33.                         L"\nУкажите очередную точку: ", pt) == RTNORM) {
  34.                         pts.append(asPnt3d(pt));
  35.                 }
  36.                 // Готовим список для команды
  37.                 resbuf *cmdbuf = acutBuildList(RTSTR, L"_LINE", RTNONE);
  38.                 for (int i = 0; i < pts.length(); i++)
  39.                 {
  40.                         AddToResbufList(cmdbuf,
  41.                                 acutBuildList(
  42.                                         RTSTR, L"_none", // Убираем привязки
  43.                                         RT3DPOINT, asDblArray(pts[i]), // Задаём очередную точку
  44.                                         RTNONE)
  45.                                 );
  46.                 }
  47.                 cmdbuf = AddToResbufList(cmdbuf,
  48.                         acutBuildList(RTSTR, L"", RTNONE)); // Завершение команды
  49.                 acedCmdS(cmdbuf);
  50.                 acutRelRb(cmdbuf);
  51.         }
  52.         // Функция добавляет к списку новый хвост
  53.         static resbuf *AddToResbufList(
  54.                 resbuf *head, // Голова списка
  55.                 resbuf *tail  // Новый хвост списка
  56.         )
  57.         {
  58.                 if (head == NULL) return tail;
  59.                 resbuf *rb = head;
  60.                 while (rb->rbnext) rb = rb->rbnext;
  61.                 rb->rbnext = tail;
  62.                 return head;
  63.         }
  64.  
  65. } ;
  66.  
  67. //-----------------------------------------------------------------------------
  68. IMPLEMENT_ARX_ENTRYPOINT(CArxProject8App)
  69. ACED_ARXCOMMAND_ENTRY_AUTO(CArxProject8App, Rivilis, MyLine, MyLine, ACRX_CMD_MODAL, NULL)

Название: Re: Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016
Отправлено: Ura от 27-08-2016, 00:30:58
Спасибо за пример
Это наиболее удобный вариант ввода команды
Использование acutNewRb - это слишком низкоуровневый способ формирования списка, много возни
Использование acutBuildList существенно удобней

Созданную вами функцию AddToResbufList можно сразу размещать в библиотеку, ее будет удобно использовать.
Странно, что этого не сделали разработчики.

Удивительно то, что уже вышло несколько версий AutoCAD 2015-2017, а почти нет ни кокой информации как пользоваться
всеми перечисленными выше функциями - ни в справочной информации ни в интернете, кругом обрывочная информация и никаких примеров...
Я раньше искал информацию по этому вопросу, информации было недостаточно...
Примеры начали появляться только недавно, и то, если хорошо поискать...

Спасибо
Название: Re: Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016
Отправлено: Александр Ривилис от 27-08-2016, 00:39:42
Созданную вами функцию AddToResbufList можно сразу размещать в библиотеку, ее будет удобно использовать.
Странно, что этого не сделали разработчики.
Сделали. Как минимум 17 лет назад. В составе ObjectARX SDK есть пример ARXDBG, в составе которого масса полезных примеров. И в частности класс ArxDbgRbList, который функционально намного мощнее, чем та функция, которую я сделал просто для того, чтобы сократить код.
Примеров по использованию acedCmdC/acedCmdS и acedCommandC/acedCommandS действительно не так много, но подразумевается, то программист знакомый с ObjectARX и функциями acedCommand и acedCmd сам легко эти примеры сделает, основываясь на статьях о появлении этих функций и замене тех, которые были в предыдущих версиях.