Интеграция внешних модулей с системой Inspector+
25.11.2002
Процесс интеграции внешних модулей с системой Inspector+ состоит из следующих этапов:
- Внесение необходимых изменений в файл “niss. dbi”; Внесение необходимых изменений в файл “niss_eng. ddi” или “niss_rus. ddi” (в зависимости от версии системы Inspector+); Предварительная подготовка DLL-файла с именем “niss_module.dll” и добавление его в корневой каталог Inspector’a; Предварительная подготовка исполнительного модуля с именем “module.exe” и добавление его в корневой каталог Inspector’a;
Все это подробно описаны ниже. Введем заранее во избежание недоразумений следующие обозначения:
DBI-файл - “niss. dbi”
DDI-файл - “niss_eng. ddi”
DLL-файл - “niss_module. dll”
EXE-модуль - “module. exe”
Замечание: Вместо имени “module” используется имя конкретного модуля (в данном случае, используется имя “demo”).
Ниже приведена диаграмма, наглядно демонстрирующая процесс интеграции и взаимодействия модулей в системе Inspector+:
![]() |
Диаграмма взаимосвязей модулей в системе Inspector+
Поясним назначение каждого объекта и смысл связей в данной диаграмме. DBI и DDI файлы содержат необходимую информацию по объектам системы Inspector+ (формат этих файлов описан ниже). DLL-файл отвечает за изменение параметров объектов и за сохранение их в базе данных. Также он обеспечивает работу с объектами: создание, изменение, удаление и некоторые другие специфические операции с объектами в дереве системы Inspector+. Позволяет пересылать параметры созданных или измененных объектов исполнительному модулю, информирует его об определенных действиях с объектами. Также в DLL-файле хранятся настроечные панели объектов. EXE-модуль отвечает за работу с оборудованием. Он выполняет опрос всех устройств и извещает ядро Inspector’а обо всех возникающих событиях, а также позволяет выполнять определенные действия с устройствами из системы Inspector+. Таким образом, осуществляется своего рода двусторонний обмен между ядром и модулем. Все события и действия должны быть обязательно зарегистрированы в системе (включены в DDI-файл). Остальные объекты из диаграммы в пояснении не нуждаются.
Примечание: DLL файл должен быть написан ТОЛЬКО на VC++ 6.0 c использованием MFC. Исполняемый файл может быть написан на чем угодно, в любой среде программирования (например, CBuilder или Delphi).
Пример реализации
К этой документации прилагаются два подробных примера. Первый из них создает объект SIMPLEDEMO, а второй DEMO. Первый представляет собой каркас без учета особенностей реализации с конкрентным оборудованием. Второй объект предусматривает наличие некого виртуального устройства, подключенного к COM-порту компьютера, и описывает взаимодействие системы Inspector+ с этим оборудованием. Ниже описывается создание второго варианта.
Примечание: Все необходимые модули и исходные файлы прилагаются к данному документу.
Допустим, в систему требуется интегрировать некое оборудование под названием “Demo”. Созданный модуль должен эмулировать работу с неким несуществующим оборудованием, которое включает в себя устройства с уникальными адресами, по которым будет осуществляться обращение к этим устройствам и происходить их опрос. Таким образом, в системе будет существовать конфигурация, состоящая из 2 основных объектов: родительского объекта DEMO с параметром COM-port и дочерних объектов DEMO_DEVICE с параметром Address. В системе будет существовать возможность для выполнения определенного набора действий с устройствами и передачи всех происходящих в них событий ядру системы Inspector+.
Этап 1
Добавим в DBI-файл 2 объекта с описанием всех параметров:
[OBJ_DEMO]
id, CHAR, 16
name, CHAR, 60
parent_id, CHAR, 16
port, CHAR, 5
flags, INTEGER
[OBJ_DEMO_DEVICE]
id, CHAR, 16
name, CHAR, 60
parent_id, CHAR, 16
address, INTEGER
flags, INTEGER
Каждый объект обязательно должен обладать 3 параметрами:
id – уникальный идентификатор объекта;
name – имя объекта;
parent_id – идентификатор родительского объекта;
Дополнительный параметр для объекта DEMO:
port - адрес COM-порта;
Дополнительный параметр для объекта DEMO_DEVICE:
address - адрес устройства;
Параметр flags используется для внутренних нужд системы и не может использоваться внешними приложениями.
Теперь запустите программу idb.exe (она находится в корневой папке Инспектора) и нажмите кнопку “Выбрать базу”.

Далее укажите путь к базе niss.mbd и нажмите кнопку “Обновить”.
Вся информация по объектам и значения всех их параметров сохраняется в базе данных Inspector’a.
Этап 2
Приступим теперь к редактированию DDI-файла. Для каждого объекта следует внести следующую информацию: имя объекта, события, действия и состояния объекта, правила переходов из одного состояния в другое по определенным событиям, а также присвоить каждому состоянию соответствующее имя bmp-файла без расширения. При этом берется та часть имени файла, которая описывает состояние объекта (см. замечание ниже). Изображения используются для отображения объектов на карте Inspector’a в зависимости от состояния объекта. BMP-файлы должны храниться в папке “\Bmp” в корневом каталоге Inspector’a. Итак, выполним следующее:
- Добавим объекты DEMO и DEMO_DEVICE; В разделе “Имена” определим названия объектов: “Demo” и “Demo Device”, соответственно; В разделе “События” для объекта DEMO определим события:
- LOST – Потеря связи; RESTORE – Восстановление связи;
- ON – Включено; OFF – Выключено;
- ON – Включить; OFF – Выключить;
- DETACHED – Потеря связи (с изображением “detach”); NORMAL – Восстановление связи (с изображением “norm”);
- ON – Включено (с изображением “on”); OFF – Выключено (с изображением “off”);
- DETACHED –> NORMAL; NORMAL –> DETACHED;
- ON –> OFF; OFF –> ON;
Для работы с DDI-файлом рекомендуется пользоваться программой “DDI Editor”, специально предназначенной для этой цели.
Замечание: Имя bmp-файла формируется следующим образом: “name_state.bmp”, где “name” - имя объекта, а “state” - соответствующее состояние объекта.
Этап 3
В прилагающихся к документации исходных файлах наглядно представлен пример DLL-файла. Ниже, кратко описан процесс создания DLL-файла и его состав.
Все объекты наследуются от класса NissObjectDLLExt с переопределением виртуальных методов этого класса.
Описание класса NissObjectDLLEx:
class NissObjectDLLExt
{
protected:
CoreInterface* m_pCore;
public:
NissObjectDLLExt(CoreInterface* core) { m_pCore = core; }
virtual CString GetObjectType() = 0;
virtual CString GetParentType() = 0;
virtual int GetPos() { return -1; }
virtual CString GetPort() { return CString(); }
virtual CString GetProcessName() { return CString(); }
virtual CString GetDeviceType() { return CString(); }
virtual BOOL HasChild() { return FALSE; }
virtual UINT HasSetupPanel() { return FALSE; }
virtual void OnPanelInit(CWnd*) {}
virtual void OnPanelLoad(CWnd*,Msg&) {}
virtual void OnPanelSave(CWnd*,Msg&) {}
virtual void OnPanelExit(CWnd*) {}
virtual void OnPanelButtonPressed(CWnd*,UINT) {}
virtual BOOL IsRegionObject() { return FALSE; }
virtual BOOL IsProcessObject() { return FALSE; }
virtual void OnCreate(Msg&) {}
virtual void OnChange(Msg&,Msg&) {}
virtual void OnDelete(Msg&) {}
virtual void OnInit(Msg&) {}
virtual void OnEnable(Msg&) {}
virtual void OnDisable(Msg&) {}
virtual BOOL OnEvent(Event&) { return FALSE; }
virtual BOOL OnReact(React&) { return FALSE; }
virtual BOOL IsIncludeParentId() { return 0; }
virtual BOOL IsWantAllEvents() { return 0; }
virtual CString DescribeSubscribeObjectsList() { return CString(); }
virtual CString GetIncludeIdParentType(){ return CString(); }
virtual CString DescribeParamLists(){ return CString(); }
};
CoreInterface* m_pCore – это указатель на интерфейс ядра. Интерфейс используется для обращения к ядру и отсылки ему сообщений, а также для получения параметров других объектов через методы CoreInterface.
Описание класса CoreInterface:
class CoreInterface
{
public:
virtual BOOL DoReact(React&) = 0;
virtual BOOL NotifyEvent(Event&) = 0;
virtual void SetupACDevice(LPCTSTR objtype, LPCTSTR objid, LPCTSTR objtype_reader) = 0;
virtual BOOL IsObjectExist(LPCTSTR objtype, LPCTSTR id) = 0;
virtual BOOL IsObjectDisabled(LPCTSTR objtype, LPCTSTR id) = 0;
virtual Msg FindPersonInfoByCard(LPCTSTR facility_code, LPCTSTR card) = 0;
virtual Cstring GetObjectName(LPCTSTR objtype, LPCTSTR id) = 0;
virtual Cstring GetObjectState(LPCTSTR objtype, LPCTSTR id) = 0;
virtual BOOL IsObjectState(LPCTSTR objtype, LPCTSTR id, CString state) = 0;
virtual Cstring GetObjectParam(LPCTSTR objtype, LPCTSTR id, LPCTSTR param) = 0;
virtual int GetObjectParamInt(LPCTSTR objtype, LPCTSTR id, LPCTSTR param) = 0;
virtual CMapStringToStringArray* GetObjectParamList(LPCTSTR objtype, LPCTSTR id, LPCTSTR param) = 0;
virtual CStringArray* GetObjectParamList(LPCTSTR objtype, LPCTSTR id, LPCTSTR param, LPCTSTR name) = 0;
virtual void GetObjectParams(LPCTSTR objtype, LPCTSTR id, Msg& msg) = 0;
virtual void SetObjectParamInt(LPCTSTR objtype, LPCTSTR id, LPCTSTR param, int val) = 0;
virtual Cstring GetObjectParentId(LPCTSTR objtype, LPCTSTR id, LPCTSTR parent) = 0;
virtual int GetObjectIds(LPCTSTR objtype, CStringArray& list, LPCTSTR main_id = NULL) = 0;
virtual int GetObjectChildIds(LPCTSTR objtype, LPCTSTR objid, LPCTSTR childtype, CStringArray& list) = 0;
};
Описание методов класса NissObjectDLLEx:
virtual BOOL IsWantAllEvents() – возвращает TRUE, если необходимо в функции OnEvent получать события от всех объектов, FALSE - если только от своего объекта.
virtual CString DescribeSubscribeObjectsList() – подписаться на объекты. Через запятую указываются типы объектов, при изменении которых (при нажатии на кнопке “Настройка”) произойдет уведомление об этом для текущего объекта и в модуле можно будет получить соответствующее событие. Например, если вернуть “CAM, GRABBER”, то при изменении настроек этих объектов для объекта “DEMO” придет сообщение типа:
“DEMO|1|UPDATE_CAM|параметры камеры”
и
“DEMO|1|UPDATE_GRABBER|параметры платы видеоввода”
virtual CString GetObjectType() – определяет тип объекта.
Пример:
virtual CString GetObjectType() { return "DEMO"; }
virtual CString GetParentType() – определяет тип родительского объекта.
Пример:
virtual CString GetParentType() { return "SLAVE"; }
virtual int GetPos() – возвращает позицию объекта в ключевом файле “key.iss”. Этот параметр должен быть согласован с компанией ISS.
Пример:
virtual int GetPos() { return 100; }
Если этот параметр вам еще не известен – отладку модуля осуществляйте в демо-режиме.
virtual CString GetPort() – возвращает номер порта, по которому будет происходить соединение и обмен между объектом и ядром. Этот параметр должен быть согласован с фирмой ISS.
Пример:
virtual CString GetPort() { return "1100"; }
virtual CString GetProcessName() – определяет имя процесса и используется ядром для поиска и автоматичского запуска исполнительного модуля при старте системы и инициализации модуля.
Пример:
virtual CString GetProcessName() { return "demo"; }
virtual CString GetDeviceType() – определяет тип объекта и характер его поведения. В зависимости от этого, ядро выбирает способ взаимодействия с объектом. Существует 3 известных типа объектов: “ACD”, “ACD2” и “ACR”. Тип “ACD” означает, что объект в дальнейшем будет получать все события, связанные с созданием, изменением и удалением следующих объектов: Пользователи, Временные зоны и Уровни доступа. Тип “ACD2” аналогичен типу “ACD”, только кроме этого, он еще указывает на то, что ядро обеспечит по мере необходимости автоматическое удаление временных карточек (карточек с ограниченным сроком действия). Тип “ACR” указывает на то, что объект является считывателем. Этот параметр будет непосредственно влиять на объект уровня доступа, точнее, это отразиться в его настройках: в выпадающем списке раздела “точка прохода” будет содержаться перечень всех созданных и определенных в системе объектов типа “ACR”.
virtual BOOL HasChild() – используется для определения, существует ли у данного объекта дочерний объект. Если у объекта имеется дочерний объект, метод возвращает TRUE, в противном случае, FALSE.
Пример:
virtual BOOL HasChild() { return TRUE; }
virtual UINT HasSetupPanel() – используется для определения, существует ли у данного объекта панель настроек (см. замечание ниже). Если у объекта имеется имеется панель настроек, метод возвращает TRUE, в противном случае, FALSE.
Пример:
virtual UINT HasSetupPanel() { return TRUE; }
virtual void OnPanelInit(CWnd*) – используется при инициализации панели настроек объекта. В качестве параметра передается указатель на окно панели настроек.
virtual void OnPanelLoad(CWnd*,Msg&) – используется при загрузке панели настроек для получения параметров объекта. В качестве параметра передается указатель на окно панели настроек и ссылка на сообщение, через которое осуществляется передача параметров объекта и заполнения ими необходимых полей в панели настроек.
Пример:
virtual void OnPanelLoad(CWnd* pwnd, Msg& params)
{
CString s;
s = params. GetParam("port");
pwnd->GetDlgItem(IDC_PORT)->SetWindowText(s);
}
virtual void OnPanelSave(CWnd*,Msg&) – используется при выгрузке панели настроек для сохранения параметров объекта. В качестве параметра передается указатель на окно панели настроек и ссылка на сообщение, через которое осуществляется передача параметров объекта и сохранение их в БД.
Пример:
virtual void OnPanelSave(CWnd* pwnd, Msg& params)
{
CString s;
pwnd->GetDlgItem(IDC_PORT)->GetWindowText(s);
params. SetParam("port",s);
}
virtual void OnPanelExit(CWnd*) – используется при закрытии панели настроек объекта. В качестве параметра передается указатель на окно панели настроек.
virtual void OnPanelButtonPressed(CWnd*,UINT) – предназначен для отработки нажатий кнопок на панели настроек объекта. В качестве параметра передается указатель на окно панели настроек и идентификатор кнопки. На идентификаторы всех кнопок накладываются определенные условия (см. замечание ниже).
Пример:
virtual void OnPanelButtonPressed(CWnd* pwnd, UINT id)
{
if(id==IDC_TEST)
{
React react("DEMO",Id,"TEST");
m_pCore->DoReact(react);
}
}
Если вы хотите по нажатию кнопки вывести какое-нибудь собственное диалоговое окно, созданное в этом же dll-файле, то необходимо предварительно использовать следующий код:
HINSTANCE prev_hinst = AfxGetResourceHandle();
HMODULE hRes = GetModuleHandle(“niss_demo. dll”);
if(hRes) AfxSetResourceHandle(hRes);
a затем уже показать это диалоговое окно:
СXXXDialog dlg;
dlg. DoModal();
и в заключение:
AfxSetResourceHandle(prev_hinst);
virtual BOOL IsRegionObject() – указывает на то, что объект будет поддерживать разделы Inspector’a. Разделы применяются для группирования объектов. Также они могут использоваться в системе отчетов.
virtual BOOL IsProcessObject() – указывает на то, что объект будет поддерживать возможность одновременного запуска и параллельной работы нескольких исполнительных модулей. Например, это может использоваться для запуска отдельного модуля непосредственно для каждого COM-порта. Старайтесь по возможности использовать всегда один рабочий модуль. Это упростит отладку и модификацию модуля.
virtual void OnCreate(Msg&) – используется при создании объекта. В качестве параметра передается ссылка на сообщение, содержащая информацию по объекту. Здесь же указывются параметры по умолчанию.
Пример:
virtual void OnCreate(Msg& msg)
{
msg. SetParam("port","COM1");
}
virtual void OnInit(Msg&) – используется при инициализации объекта. В качестве параметра передается ссылка на сообщение, содержащая информацию по объекту.
Пример:
virtual void OnInit(Msg& msg)
{
OnChange(msg, msg);
}
virtual void OnChange(Msg&,Msg&) – используется при изменении объекта. В качестве параметра передаются ссылки на сообщения, содержащие информацию по объекту до и после изменения, соответственно.
Пример:
virtual void OnChange(Msg& msg, Msg& prev)
{
React react(msg. GetSourceType(),msg. GetSourceId(),"INIT");
react. SetParam("port",msg. GetParam("port"));
m_pCore->DoReact(react);
}
virtual void OnDelete(Msg&) – используется при удалении объекта. В качестве параметра передается ссылка на сообщение, содержащая информацию по объекту.
Пример:
virtual void OnDelete(Msg& msg)
{
React react(msg. GetSourceType(),msg. GetSourceId(),"EXIT");
m_pCore->DoReact(react);
}
virtual void OnEnable(Msg&) – предназначен для отработки нажатия кнопки “Disable”(“Выключить”) на панели Inspector’a при включении объекта. В качестве параметра передается ссылка на сообщение, содержащая информацию по объекту.
virtual void OnDisable(Msg&) – предназначен для отработки нажатия кнопки “Disable”(“Выключить”) на панели Inspector’a при выключении объекта. В качестве параметра передается ссылка на сообщение, содержащая информацию по объекту.
virtual BOOL OnEvent(Event&) – предназначен для обработки событий, передаваемых в качестве параметра.
Пример:
virtual BOOL OnEvent(Event& event)
{
if(event. GetAction() == "ACCESS_IN" ||
event. GetAction() == "ACCESS_OUT")
{
Msg per = m_pCore->FindPersonInfoByCard(event. GetParam("facility_code"),event. GetParam("card"));
event. SetParam("param0", !per. GetSourceId().IsEmpty() ?
per. GetParam("name") : event. GetParam("facility_code") + event. GetParam("card"));
event. SetParam("param1", per. GetSourceId() );
}
else
if(event. GetAction() == "NOACCESS_CARD")
{
event. SetParam("param0",event. GetParam("facility_code") + event. GetParam("card"));
}
return TRUE;
}
virtual BOOL OnReact(React&) – предназначен для обработки реакций, передаваемых в качестве параметра.
Замечание 1: Все панели настроек объектов хранятся в ресурсах в виде диалогов. Причем, идентификаторы диалогов строятся по схеме IDD_object_SETUP, где object - это имя соответствующего объекта. Например, для объекта DEMO – это IDD_DEMO_SETUP, а для объекта DEMO_DEVICE – это IDD_DEMO_DEVICE _SETUP.
Замечание 2: Числовые значения идентификаторов кнопок должны начинаться с номера 1151. Например, для кнопки “Test” идентификатор в файле “Resource. h” определяется так:
#define IDC_TEST 1151
Замечание 3: Для того, чтобы в дереве настроек у объекта отображался свой собственный значок, необходимо в ресурсах DLL-файла создать BITMAP размером 14x14 с именем объекта. В данном случае “DEMO” и “DEMO_DEVICE”, соответственно.
В глобальной функции CreateNissObject(CoreInterface* core) необходимо создать экзепляры описанных объектов, поместить их в массив CNissObjectDLLExtArray и возвратить указатель на объект этого массива. Через эту же функцию получаем указатель на интерфейс ядра, который в дальнейшем используется в объектах (в данном случае в объектах DEMO и DEMO_DEVICE) для обращения к методам данного интерфейса. В дальнейшем, после загрузки DLL-файла ядро вызывает функцию CreateNissObject и получает указатели на все используемые объекты.
CNissObjectDLLExtArray* APIENTRY CreateNissObject(CoreInterface* core)
{
CNissObjectDLLExtArray* ar = new CNissObjectDLLExtArray;
ar->Add(new NissObjectDemo(core));
ar->Add(new NissObjectDemoDevice(core));
return ar;
}
Этап 4
Процесс создания модулей, взаимодействующих с ядром, подробно рассмотрен в документации по IIDK. Необходимую информацию можно также почерпнуть из примера, исходные файлы которого прилагаются к данной документации. Ниже, кратко изложены основные моменты, отражающие основную суть реализации модуля и его взаимодействия с ядром.
В начале происходит установление связи с ядром:
BOOL Connect(LPCTSTR ip, LPCTSTR port, LPCTSTR id, void (_stdcall *func)(LPCTSTR msg)) – осуществляет подсоединение к ядру.
Параметры функции:
LPCTSTR ip - ip адрес машины с ядром
LPCTSTR port - порт протокола TCP/IP, через которое происходит подключение.
900 для видеоподсистемы, 1030 для ActiveX.
LPCTSTR id - идентификатор подключения, для видео >1 , для Activex = id ActiveX в дереве
настроек.
void (_stdcall *func)(LPCTSTR msg)) – callback функция, которая принимает сообщения от Инспектора.
В конце, соответственно, происходит разрыв связи:
void Disconnect(LPCTSTR id) – осуществляет отключение от ядра.
Параметры функции:
LPCTSTR id – идентификатор подключения, указанный ранее при вызове функции Connect.
Все сообщения от ядра отрабатываются в специальной call-back функции:
void (_stdcall *func)(LPCTSTR msg)) – callback функция, принимающая сообщения от Inspector'а.
Пример объявления функции:
void _stdcall myfunc(LPCTSTR str)
{
printf("\r\nReceived:%s\r\n\r\n",str);
}
Сообщения для ядра передаются следующим образом:
BOOL SendMsg(LPCTSTR id, LPCTSTR msg) – осуществляет отправку сообщения Inspector'у.
Параметры:
LPCTSTR id - идентификатор подключения, указанный ранее при вызове функции Connect
LPCTSTR msg – текст сообщения.
Возвращаемое значение:
TRUE в случае удачной посылки сообщения,
FALSE в случае неудачи.
Примеры передачи сообщений:
SendMsg(id,”CAM|1|REC”); // поставить камеру 1 на запись
SendMsg(id, "DEMO|1|RESTORE"); // восстановление связи объектом DEMO
SendMsg(id,"DEMO_DEVICE|1|ON|params<1>,param0_name<address>,param0_val<1>"); // включить устройство DEMO_DEVICE с адресом 1
Для реализации взаимодействия модуля и ядра используются следующие файлы:
Состав IIDK
Iidk. dll – библиотека функций для интеграции с Inspector’ом.
Iidk. h - заголовочный файл с объявлениями импортируемых функций
Iidk. lib - lib файл для работы с iidk. dll (lib для MS Visual C++)
Msg. h - файл ,содержащий класс для извлечения параметров объектов
К документации прилагаются следующие файлы:
Niss. dbi
Niss_eng. ddi
Niss_demo. dll с исходными файлами проекта
Demo. exe с исходными файлами проекта
Ddi. exe - программа DDI Editor
Замечание: В отличие от стандартного подключения к ядру Inspector’a в данном случае IIDK Interface в системе не создается. В качестве идентификатора подключения передается пустая строка, то есть id равен “”.
При выгрузке модуля ему посылается сообщение WM_EXIT:
#define WM_EXIT (WM_USER+2000)
при помощи функции WinAPI PostThreadMessage.
Необходимо перехватить это сообщение и обеспечить “правильную” выгрузку своего модуля. Например в VC++ и MFC, это событие отлавливается в классе, наследуемым от CWinApp, в Delphi и СBuilder - TApplication.



