- •Введение
- •Создание функциональных блоков Создание новой библиотеки функциональных блоков
- •Последовательность разработки функциональных блоков
- •Использование групп, набор входов которых определяется пользователем.
- •Определение группы free_pingroup_def в фб.
- •Инструкция по внедрению шаблона cfbPropPageImpl в страничку свойств функционального блока.
- •Атрибуты входов, находящихся в группе free_pingroup_def.
- •Получение событий от таймера
- •Получение текущего времени
- •Проверка правильности в страничках свойств.
- •Использование таймера в функциональных блоках.
- •Признаки качества в Функциональных блоках Коды признака качества значения
- •Установка признаков качества выходов фб
- •Получение признака качества
- •Передача признаков качества входа выходу
- •Сообщения в фб.
- •Вывод информация об ошибках в фб в протокол работы.
- •Восстановление состояния фб при горячем рестарте.
- •Страницы свойств фб
- •Справка к фб
- •Справка к страницам свойств фб
- •Разработка визуальных фб на основе существующей библиотеки фб
- •Создание общего h-файла (подкл в файл описания фб и файл описания контрола)
- •Изменения в создании фб
- •Создание ActiveX контрола
- •Чтобы создать свойства нужно:
Последовательность разработки функциональных блоков
Вставьте в проект новый COM- объект Simple Object , Задайте его имя, например, NewObj, на страничке “Attributes” поставьте флажки: Free, Dual, Yes, Free Threaded Marshaler. Можно еще в поле Type задать русское имя, которое будет отображаться в палитре. В idl- файле проекта в конструкцию coclass нового объекта вставьте следующую строку:
[source] dispinterface _IFunkBlockEvents;
Скомпилируйте idl- файл.
В список наследования класса ФБ (CNewObj) добавить шаблон, аналогичный
public CFBImpl< CNewObj>
Имплементируйте Connection Point контрола. Для этого щелчком правой клавиши мыши по классу контрола вызвать локальное меню Класс-Инспектора, и в нем выбрать пункт Implement Connection Point. В появляющемся диалоге Connection Point Wizard’а отметить интерфейс _IFunkBlockEvents. Нажать кнопку ОК, в результате создаются Proxy- классы, реализующие оповещение о событиях контрола.После этого в список наследования класса ФБ добавится строка, аналогичная следующей:
public CProxy_IFuncBlockEvents< CNewObj >
В h-файл блока добавьте строку #include "FBImpl.h"
В разделе BEGIN_COM_MAP(CNewObj) добавить макрос
COM_FB_ENTRY
Поскольку блок реализует два dual-интерфейса, необходимо разрешить неоднозначность, возникающую для декларации в COM MAP интерфейса IDispatch. Это можно сделать, заменив вводящий его макрос COM_INTERFACE_ENTRY(IDispatch) на макрос COM_INTERFACE_ENTRY2, наподобие следующего:
COM_INTERFACE_ENTRY2(IDispatch, INewObj)
В декларацию класса контрола вставить (или заменить существующие) декларации:
public:
HRESULT FinalConstruct() {InitPinMap();return S_OK;} void FinalRelease() {ReleasePinMap();}
HRESULT UpdatePoutData( long lWave);
Декларация имен входов и выходов
BEGIN_PIN_NAMES_DEF
PinName1,
PinName2,
…..
PinNameN,
END_PIN_NAMES_DEF
Декларация имен групп входов и групп выходов
BEGIN_GROUP_NAMES_DEF
GroupName1,
GroupName2,
…..
GroupNameN,
END_GROUP_NAMES_DEF
Замечание: избегайте использования идентификаторов IN и OUT, так как они уже определены в системном h-файле.
Далее описывается структура ФБ внутри определения класса в следующем виде:
BEGIN_FB_MAP
PIN_DEF( DUMMY, "Dummy", VT_R8, 12.45)
POUT_DEF( INTVAL, "IntVal", VT_I4)
POUTGROUP_DEF( SINGROUP, "SinGroup", "Sin", NSin, VT_R8)
FREE_PINGROUP_DEF (GROUP_ID, "Group Name")
END_FB_MAP
Макрос PIN_DEF(id, strName, vt, value) определяет вход.
id – идентификатор входа, определенный ранее в разделе BEGIN_PIN_NAMES_DEF. Этот идентификатор передается в функцию GetPinData.
strName – имя входа, которое отображается в дереве MasterSCADA. (Нельзя давать одинаковые имена входам и выходам одного уровня!)
vt – тип переменной (VT_I4, VT_R8, …). Если указать VT_EMPTY, то вход будет наследовать тип связанного с ним выхода.
value – значение входа по умолчанию (назначается входу при старте runtime).
Макрос POUT_DEF(id, strName, vt) определяет выход.
id – идентификатор выхода, определенный ранее в разделе BEGIN_PIN_NAMES_DEF. Этот идентификатор передается в функцию SetPoutData.
strName – имя выхода, которое отображается в дереве MasterSCADA
vt – тип переменной (VT_I4, VT_R8, …).
Макрос PINGROUP_DEF(id, strName, strPinName, vt, Prop, value)
id – идентификатор группы входов, определенный ранее в разделе BEGIN_GROUP_NAMES_DEF. Этот идентификатор передается в функцию GetPinData.
strName – имя группы, которое отображается в дереве MasterSCADA
strPinName – строка, на основе которой строится имена входов этой группы по формату strPinNameN, где N – номер входа ( 0, 1, 2, 3 ...). Если указать в качестве strPinName пустую строчку, то для формирования имени входа будет использоваться функция GetCustomPinName.
vt – тип переменных в этой группе (VT_I4, VT_R8, …). Если указать VT_EMPTY, то входы будут наследовать типы связанных с ними выходов. Если указать в качестве типа VT_USERDEFINED, то для получения типа входа будет использоваться функция GetCustomPinVarType.
Prop – название свойства объекта FB, которое определяет количество входов в группе. После изменения этого свойства, для того, чтобы в дереве MasterSCADA изменилось число входов, необходимо вызвать Fire_PinSpaceChanged().
value – значение по умолчанию для входов этой группы (назначается входам при старте runtime).
Макрос POUTGROUP_DEF(id, strName, strPoutName, vt, Prop)
id – идентификатор группы выходов, определенный ранее в разделе BEGIN_GROUP_NAMES_DEF. Этот идентификатор передается в функцию SetPoutData.
strName – имя группы, которое отображается в дереве MasterSCADA
strPoutName – строка, на основе которой строится имена выходов этой группы по формату strPoutNameN, где N – номер выхода (0, 1, 2, 3 ...). Если указать в качестве strPinName пустую строчку, то для формирования имени выхода будет использоваться функция GetCustomPinName.
vt – тип переменных в этой группе (VT_I4, VT_R8, …). Если указать в качестве типа VT_USERDEFINED, то для получения типа входа будет использоваться функция GetCustomPinVarType.
Prop – название свойства объекта FB, которое определяет количество выходов в группе. После изменения этого свойства, для того, чтобы в дереве MasterSCADA изменилось число выходов, необходимо вызвать Fire_PinSpaceChanged().
Макрос FREE_PINGROUP_DEF описан в разделе «Использование групп, набор входов которых определяется пользователем».
Если необходимо, чтобы вход функционального блока имел такой же тип, как у присоединенного к нему входа, то в качестве vt необходимо прописать значение VT_EMPTY. При этом, если вход ни с чем не связан, то он при переходе в Runtime получит значение value (как и в случае входов, имеющих свой тип). При этом тип константы определится автоматически исходя из значения value.
В .cpp- файле класса контрола реализовать функцию UpdatePoutData. Эта функция выполняет преобразование входных значений в выходные.
UpdatePoutData(long lWave)
Эта функция должна пересчитывать значения на всех выходах ФБ.
Для установки значений на выходах можно использовать функции
HRESULT SetPoutData(long lPoutIndex, VARIANT varVal, long lWave = -1, FILETIME ftTimeStamp = DEFAULT_TIME, DWORD dwValidity = DEFAULT_VALIDITY) – если выход не находится в группе.
HRESULT SetPoutData(long lGroupIndex, long lGroupPoutIndex, VARIANT varVal, long lWave = -1, FILETIME ftTimeStamp = DEFAULT_TIME, DWORD dwValidity = DEFAULT_VALIDITY) – если выход находится в группе.
lGroupIndex – идентификатор группы выходов
lGroupPoutIndex – номер выхода в группе (0, 1, 2...)
lPoutIndex – идентификатор выхода
lWave – номер волны, который был передан в UpdateData. Если значение на выходе устанавливатся не из функции UpdateData, то нужно использовать значение по умолчанию - -1.
ftTimeStamp – метка времени. Если DEFAULT_TIME, то на выходе устанавливается метка времени входа, который не обновлялся дольше всего. Если CURRENT_TIME, то устанавливается текущее время.
dwValidity – признак недостоверности. Список признаков недостоверности приводится в разделе «Признаки качества в Функциональных блоках».
Для получения значений входов можно использовать функции
HRESULT GetPinData(long lPinIndex, VARIANT *pvarVal, FILETIME* pftTimeStamp = NULL, DWORD* pdwValidity = NULL) – если вход не находится в группе.
HRESULT GetPinData(long lGroupIndex, long lGroupPinIndex, VARIANT *pvarVal, FILETIME* pftTimeStamp = NULL, DWORD* pdwValidity = NULL) - если вход находится в группе.
lGroupIndex – идентификатор группы входов
lGroupPinIndex – номер входа в группе (0, 1, 2...)
lPinIndex – идентификатор входа
pvarVal – текущее значение на входе, pftTimeStamp – его метка времени, pdwValidity – его достоверность.
Для работы с входами группы, описанной как FREE_PINGROUP_DEF, имеется функция get_ItemCount (GROUP_ID, &nCount), которая возвращает количество входов в группе. Чтение осуществляется как у обычных групп через GetPinData.
Также для получения значений входов можно пользоваться функциями
bool GetPinBool (long nPinIndex)
bool GetPinBool (long nGroupIndex, long nPinIndex)
long GetPinLong (long nPinIndex)
long GetPinLong (long nGroupIndex, long nPinIndex)
double GetPinDouble (long nPinIndex)
double GetPinDouble (long nGroupIndex, long nPinIndex)
ФБ может выполнять некоторые инициализационные операции при переходе в Runtime, и возврате из него. Например, он может производить инициализацию таймеров и их уничтожение.
Для этого нужно переопределить следующие функции (одну, обе, или ни одной):
BOOL ToRuntime() – вызывается при переходе в режим runtime
void ToDesign() – вызывается при возврате из режима runtime
Если в карте BEGIN_FB_MAP была объявлена хотя бы одна группа с пустым шаблоном имени переменной, то в классе ФБ необходимо переопределить следующую функцию -
CString GetCustomPinName(long lGroup, long lPin)
Она должна возвращать действительное имя переменной с индексом lPin в группе с идентификатором lGroup
Если в карте BEGIN_FB_MAP была объявлена хотя бы одна группа с типом значений VT_USERDEFINED, то в классе ФБ необходимо переопределить следующую функцию -
VARTYPE GetCustomPinVarType(long lGroup, long lPin, CComVariant& varDefValue)
Она должна возвращать действительный тип переменной с индексом lPin в группе с идентификатором lGroup. В varDefValue можно установить значение входа по умолчанию.
Добавьте в декларацию класса CATEGORY_MAP, в которой укажите категории, имплементируемые данным объектом. В перечень категорий обязательно должна включаться категория CATID_FB. Кроме того, для дополнительных функциональных блоков необходимо указать категорию CATID_OTHER.
BEGIN_CATEGORY_MAP(CNewObj)
IMPLEMENTED_CATEGORY(CATID_FB)
IMPLEMENTED_CATEGORY(CATID_OTHER)
END_CATEGORY_MAP()
В ресурсы проекта добавить два растровых изображения (Bitmap), отображающих объект в палитре и в дереве проекта, одна иконка- для нормального, вторая для выбранного состояния элемента. Обе иконки имеют размер 16 * 16.
В rgs- файл контрола добавить следующие записи. Значения, проставляемые в этих записях, равны идентификаторам соответствующих ресурсов.
'Images'
{
val 'MainImage' = d '234'
val 'SelectedImage' = d '235'
}
В rgs- файл контрола в строчке, аналогичной
ForceRemove {BEE9D142-396D-11d4-A0A7-006008677772} = s 'Switch Class'
заменить Switch Class на полное русское имя блока. (Если уже само не заменилось).
Страницы свойств.
В системе MasterSCADA для настройки функционального блока есть стандартная страничка свойств, на которой отображаются все свойства ФБ(определенные в его интерфейсе) с возможностью редактирования (если это get-put свойство). Кроме того можно разработать собственные странички свойств для специфичной настройки. Для этого надо:
Разработать объекты-странички свойств для настройки свойств функционального блока. (New ATL Object Controls Property Page, на закладке Strings в поле Title задать русское имя закладки)
Добавить в список наследования ФБ ISpecifyPropertyPagesImpl<CnewObj>
В COM_MAP класса добавить COM_INTERFACE_ENTRY(ISpecifyPropertyPages)
В раздел BEGIN_PROP_MAP функционального блока добавить (для каждой странички свойств): PROP_PAGE(CLSID_CnewObjPP) где CLSID_CnewObjPP – CLSID странички свойств
Обеспечьте персистентность свойств объекта. Например, следующим образом:
В список наследования добавить public IPersistStreamInitImpl<CNewObj>
В COM_MAP класса добавить COM_INTERFACE_ENTRY(IPersistStreamInit)
Добавить переменную-член класса BOOL m_bRequiresSave;
И, наконец добавить макросы BEGIN_PROP_MAP(CNewObj) END_PROP_MAP()
Если вы аккуратно выполнили все рекомендации, функциональный блок готов!
