Интеграция внешних модулей с системой 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 – Восстановление связи;
    В разделе “События” для объекта DEMO_DEVICE определим события:
      ON – Включено; OFF – Выключено;
    В разделе “Реакции” для объекта DEMO_DEVICE определим действия:
      ON – Включить; OFF – Выключить;
    В разделе “Значки ” для объекта DEMO и DEMO_DEVICE определим имена объектов: “demo” и “demo_device”, соответственно. В разделе “Состояния” для объекта DEMO определим состояния и соответствующие состояниям значки:
      DETACHED – Потеря связи (с изображением “detach”); NORMAL – Восстановление связи (с изображением “norm”);
    В разделе “Состояния” для объекта DEMO_DEVICE определим состояния и соответствующие состояниям значки:
      ON – Включено (с изображением “on”); OFF – Выключено (с изображением “off”);
    В разделе “Правила перехода” для объекта DEMO добавим правила:
      DETACHED –> NORMAL; NORMAL –> DETACHED;
    В разделе “Правила перехода” для объекта DEMO_DEVICE добавим правила:
      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.