Будущее КОМПАС API на Linux
Продолжаем рассказывать, как идет портирование КОМПАС-3D на Linux (начало здесь). Сегодня речь про API и с вами Владимир Кузнецов, инженер-программист по прикладным интерфейсам КОМПАС-3D.
До начала разработки нативной Linux-версии КОМПАС-3D у нас был API экспортных функций, API5 и API7 под Windows. Когда возникла необходимость поддерживать КОМПАС на нескольких операционных системах, было принято решение создать кроссплатформенный API, который бы работал как на Windows, так и в ОС Linux. Рабочее название такого API — KsAPI.
Сравнение с API7
На 95% KsAPI состоит из тех же интерфейсов и методов, что и API7. В новом API мы постарались исправить все неточности и синтаксические ошибки, которые были раньше, так что, если пользователь сразу не найдет какой-либо метод из API7, он, немного измененный, обязательно должен быть в KsAPI. Крупными отличиями являются используемые типы аргументов методов и другой подход к реализации событий.
void CreateDocument()
{
if (ksapi::IDocumentsPtr documents = GetKompasApplication().GetDocuments())
{
ksapi::IKompasDocumentPtr doc = documents->AddWithDefaultSettings(ksDocumentAssembly, true);
}
}
(Пример кода на KsAPI)
Для преобразования интерфейсов, как и в API7, используется Ptr-оболочка. Например, IKompasDocumentPtr можно привести к IKompasDocument2DPtr, так же как это делается в API7, обычным присваиванием.
На текущий момент KsAPI написан на С++, используется только в основном потоке приложения.
Хранение данных приложений в документах КОМПАСа остается неизменным. Поэтому то, что ранее было записано в хранилище КОМПАС-документа в Windows, будет читаться и на Linux.
Без изменений остается и создание пользовательских меню и тулбаров с помощью манифеста или из ресурсов. Однако изменится подход к ресурсам, об этом — ниже.
Для написания библиотек, кроме стандартных библиотек и C++, пользователю больше ничего не потребуется, только если он не создает собственные диалоги.
Замена ActiveX
Раньше для встраивания в КОМПАС собственного интерфейса в приложениях использовался ActiveX, который отсутствует под Linux. Для покрытия большинства потребностей мы добавили несколько контролов:
контрол «Таблица с ячейками разного типа» (IPropertyParamGrid)
контрол «Дерево» (IPropertyTree).
аналог IPropertyUserControl — «Пользовательское окно» (IPropertyUserWindow), в который, как и в ActiveX, можно добавлять свои собственные контролы.
Используемые типы данных
В KsAPI типы данных заменены на кроссплатформенные аналоги, а целочисленные типы, у которых их представление в памяти зависит от используемой платформы, заменены на типы с фиксированной длиной.
API7 | KsAPI | |
Строки | BSTR | std: wstring |
Массивы | SAFEARRAY | std: vector |
Типы значения в хранилище | VARIANT | std: variant |
Целочисленные типы | long, _int64, … | int32_t, int64_t, … |
События в KsAPI
Больше всего в KsAPI изменились события: если раньше это был класс, который нужно было наследовать от интерфейса и реализовать все его методы, то сейчас события выглядят как колл-бэк функции. Если у какого-либо объекта, например у процесса, есть метод Еvent (), мы можем получить интерфейс, с помощью которого возможно управлять подпиской на события. Обычно там есть метод для удаления всех обработчиков и несколько методов, которые добавляют отдельные обработчики для конкретного события. Предположим, нужно добавить обработчик ButtonClick, ниже приведен пример, как это можно сделать с помощью лямбда-функции.
void BaseProcess::CreateControls()
{
ksapi::IProcessParamPtr procParam = Settings::app->CreateProcessParam();
ksapi::IPropertiesManagerEvents & events = procParam->Events();
events.AddButtonClickHandler(handlerOwner, [](int32_t buttonId) { return true; });
}
Для этой задачи также можно использовать обычную функцию или метод класса (добавить нужный обработчик с помощью лямбда-функции или bind).
bool OnButtonClick(std::int32_t buttonId)
{
return true;
}
void BaseCheckProcess::CreateControls()
{
ksapi::IProcessParamPtr procParam = Settings::app->CreateProcessParam();
ksapi::IPropertiesManagerEvents & events = procParam->Events();
events.AddButtonClickHandler(handlerOwner, OnButtonClick);
}
Пример использования функции)
bool BaseProcess::OnButtonClick(std::int32_t buttonId)
{
return true;
}
void BaseCheckProcess::CreateControls()
{
ksapi::IProcessParamPtr procParam = Settings::app->CreateProcessParam();
ksapi::IPropertiesManagerEvents & events = procParam->Events();
events.AddButtonClickHandler(handlerOwner, std::bind(OnButtonClick,this, std::placeholders::_1));
}
(Пример использования метода класса)
Ресурсы в KsAPI
Ресурсы в KsAPI выглядят так же, как это сейчас сделано в КОМПАС — с помощью xml-файлов, которые располагаются в определенной файловой иерархии.
Проверка документа
Документ не активизирован или не является графическим
Чтобы обратиться к ресурсам определенного модуля, нужно завести статическую переменную с типом IResourcesModule. Функция GetModuleHandle возвращает указатель на эту переменную. Эту функцию необходимо использовать, чтобы обращаться к ресурсам.
static ksapi::IResourcesModulePtr resourceModule;
void * GetModuleHandle()
{
return &resourceModule;
}
#define MENU_ID(idName) resourceModule->AddMenuId(L"" #idName, idName);
#define STRING_ID(idName) resourceModule->AddStringId(L"" #idName, idName);
void LoadResources(ksapi::IApplication & app)
{
resourceModule = app.CreateResourcesModule(L"MyApplicationName");
MENU_ID(IDR_LIBID)
MENU_ID(IDC_IMPOSITIONS_CHECK)
MENU_ID(IDC_POSITIONS_CHECK)
MENU_ID(IDC_LIBRARY_HELP)
STRING_ID(IDR_LIBID)
STRING_ID(IDS_ERR01)
resourceModule->End();
}
Функция LoadResouces формирует таблицу соответствия между числовым и строковым идентификаторами. При создании ресурсного модуля задается определенное текстовое имя, обычно это идентификатор приложения в манифесте.
Подключение к KsAPI
Раньше, чтобы загрузить библиотеку, она должна была содержать в себе несколько экспортных функций, например LibraryEntry. Сейчас для того чтобы библиотека распознавалась как библиотека KsAPI, в ней должна быть прописана как минимум одна функция LoadKompasLibrary, куда приходит ссылка на главный интерфейс IApplication и вспомогательный интерфейс, который позволяет подключить обработчики — аналоги используемых ранее экспортных функций.
APP_EXP_FUNC(bool) LoadKompasLibrary(ksapi::IApplication & app, ksapi::IKompasLibraryActions & libaryActions)
{
libaryActions.AddRunCommandHandler(RunCommand);
libaryActions.AddGetMenuIdHandler(GetMenuId);
Settings::app = &app;
if (ksapi::ILibraryManagerPtr libraryManager = Settings::app->GetLibraryManager())
{
if (ksapi::IProceduresLibrariesPtr proceduresLibraries = libraryManager->GetProceduresLibraries())
Settings::myLibrary = proceduresLibraries->GetItem(L"MyApplicationId");
}
LoadResources(app);
return true;
}
Например, вместо LibraryEntry можно использовать функцию RunCommand, которая содержит привычный всем разработчикам свитч, реализующий обработку команд приложения.
int32_t GetMenuId()
{
return GetLibraryId();
}
void RunCommand(unsigned int commandId, ksapi::RunCommandMode mode)
{
switch (commandId)
{
case 1:
break;
case 2:
break;
default:
break;
}
}
В функции инициализации можно также обратиться к своей библиотеке, например, для получения словаря. Указывая то же самое имя из манифеста, пользователь получает интерфейс своей библиотеки и может использовать некоторые ее элементы.
В этой же функции LoadKompasLibrary можно и даже нужно вызвать функцию LoadResourses, которая подгрузит таблицу соответствия в хранилище ресурсов КОМПАСа для дальнейшего использования.
Функция UnloadKompasLibrary выполняется при выгрузке библиотеки и позволяет как отписаться от событий, так и выгрузить свои ресурсы и что-либо сохранить.
APP_EXP_FUNC(void) UnloadKompasLibrary()
{
Settings::app->Events().RemoveAllHandlers(handlerOwner);
}
Если пользователь находится на переходном этапе перевода библиотеки, то главный интерфейс можно также получить через функцию GetKompasApplication и некоторые части библиотеки уже начинать переписывать на новом KsAPI.
Переход на KsAPI
Этапы перехода на кроссплатформенность:
замена всех платформозависимых типов данных кроссплатформенными (CString/long/uint/VARIANT);
Замена всех вызовов API5/API7/экспортных функций на новый кроссплатформенный KsAPI;
Переход на новую схему событий в KsAPI;
Перевод интерфейса приложения со связки MFC/WinApi/ActiveX на QT/Панель свойств в KsAPI;
Замена базы данных LOA на SQLite;
Конвертация проекта с sln/vcproj на CMake;
Компиляция приложения clang компилятором под Linux.
В настоящий момент API7 тоже еще дописывается, но то, чего сейчас не хватает в API7, а было в API5, мы добавляем и в API7, и в KsAPI параллельно. Поэтому для облегчения перехода на KsAPI можно предварительно переводить библиотеки на API7. Производительность нового API останется на том же уровне, но он позволит работать на трех российских операционных системах: Astra Linux, РЕД ОС и Альт Рабочая станция.