1.4. Процедуры работы с БД ElectroCNF
2. Стандартные компоненты (*.OCX, *.DLL)
2.3. Electro.DLL – набор базовых функций
Программа Electro.dll содержит набор функций и процессов (threads), обеспечивающих доступ и поддержку целостности БД ElectroCNF
- проверка структуры БД ElectroCNF, Electro - VerifyDB();
- идентификация пользователя -GetRights(),AddUser(),CloseUser(),GetUserInfo();
- ведение журнала работы пользователей – Log();
- работа с базой данных - ODBC_Open(), ODBC_Close(), ODBC_Execute(), ODBC_GetData(), ODBC_Fetch(), ODBC_PutLongData(), ODBC_GetLongData(), ODBC_GetError(),ODBC_Add2Trend(),ODBC_FlushTrend();
- работа с метками времени - SystemTime2Second(), Second2SystemTime(), ReduceSecond(), Second2Ind();
- работа с драйвером устройства - DLL_Open(), DLL_Close(), DLL_Control(), DLL_CheckMsg(), DLL_GetError();
- мультиязыковая поддержка – ML();
- визуальная компонента навигации БД – SelectObj(), ElSetEvent().
Перечисленные выше функции могут быть подключены к проектируемому прикладному модулю через библиотеку Electro.LIB
Описание функций Electro.DLL (приведен синтаксис для языка MS Visual C).
2.3.1. Проверка структуры БД
Функция
int __stdcall VerifyDB() ;
Выполняет следующие действия:
- проверяет наличие ODBC-источников данных “ElectroCNF” и “Electro”. При их отсутствии делается попытка создания;
- проверяет наличие таблиц и необходимых полей в каждой таблице.
Функцию следует вызывать в начале работы программы.
2.3.2. Идентификация пользователя
Функции данной группы используются в программе для интерактивной регистрации пользователя и работы с диспетчером пользователей системы Электро.
int __stdcall GetRights(char *titul, int query, int mask);
titul - текст окна запроса пароля;
query - управление запросом пароля;
mask - маска на битовый вектор прав доступа (поле Rigths таблицы TB_User).
Алгоритм:
if (query==0) {
if ( имеется_зарегистрированный_пользователь ) {
if ( (права_пользователя_соответствуют mask) или (mask==0))
return Права_текущего_пользователя;
Вывести_диалоговое_окно;
if ( имеется_зарегистрированный_пользователь ) {
return Права_текущего_пользователя;
return 0;
int __stdcall CloseUser();
Функция деактивирует текущего пользователя.
int __stdcall AddUser(char *name, char *shname, char *password, int rigths);
Функция создает новую учетную запись для пользователя.
2.3.3. Работа с БД
Данная группа функций является надстройкой ODBC API:
DWORD __stdcall ODBC_Open (char *dsn, int *err);
- открывает соединение с ODBC-источником данных dsn (обычно “ElectroCNF” либо “Electro”). В *err возвращается код ошибки (0 при успешном открытии). Возвращается номер открытого соединения (используется в остальных функциях в качестве параметра qb).
BOOL __stdcall ODBC_Close (DWORD qb);
- закрывает ранее открытое соединение.
BOOL __stdcall ODBC_Execute (DWORD qb, char *sql);
- выполнение SQL- оператора.
Short __stdcall ODBC_Fetch (DWORD qb);
- получение следующей строки результата выполнения последней функции ODBC_Execute(). Возвращается значение:
0 – результат получен. Можно использовать ODBC_GetData() для извлечения элементов;
100 – данных нет.
Значение отличное от 0 и 100 – ошибка выполнения.
Short __stdcall ODBC_GetData (DWORD qb, int n, int *len, void *rez);
- получение элемента n из строки результата, полученного последней функцией ODBC_Fetch(). Элемент размещается по адресу rez. Перед вызовом функции переменной *len необходимо присвоить максимальный размер поля rez. В свою очередь, функция записывает в *len действительную длину данных.
Возвращаемое значение: <0 – ошибка.
Аргумент n нумеруется от 1. Специальные значения n используются для получения служебной информации:
n=0 : *(int*)rez присваивается количество элементов строки результата
n=-1,-2, … -i : возвращается информация об элементе i:
- по адресу rez записывается имя элемента
- *len присваивается тип элемента
- возвращается размер элемента.
n=-100 : возвращается количество строк результата.
Int __stdcall ODBC_GetLongData (DWORD qb, char *sql, char *fname);
- перенос бинарных данных из таблицы БД в файл. Например
GetLongData(qb, “SELECT source FROM TB_Reports WHERE repid=5”, “C:\filename”);
Short __stdcall ODBC_PutLongData (DWORD qb, char *sql, char *fname);
- перенос данных из файла в таблицу БД. Например
PutLongData(qb, “UPDATE TB_Reports SET source=? WHERE repid=5”, “C:\filename”);
Short __stdcall ODBC_GetError (DWORD qb, int len, char *errtxt);
- получение текста ошибки
BOOL __stdcall ODBC_Add2Trend (DWORD qb, DWORD oid, DWORD tm, double val, char *flag);
- ускоренное занесение информации в таблицу Trend. Следует пользоваться именно этой функцией (а не ODBC_Execute(“INSERT … INTO Trend ..”).
BOOL __stdcall ODBC_FlushTrend (DWORD qb);
- форсирование выполнения предыдущих функций Add2Trend().
Для Дельфи-программистов разработана компонента Electro.pas, упрощающая работу с функциями семейства ODBC_xxx(). Для С++ имеется класс electro (ElectroDB.cpp + Electro.h)
2.3.4. Работа с метками времени
Данная группа функций облегчает работу программы с метками времени. В системе Электро используются следующие принципы:
- метка времени (mrk) – это беззнаковое 4-х-байтовое целое, секунды с 01.01.1970 00:00:00 по Гринвичу (например, библиотечная функция mrk=time(NULL) возвращает текущее время).
- измерительные параметры, имеющие период (см. описание TB_Objects.Period) снабжаются меткой, соответствующей началу периода. Например, значение месячного параметра для сентября 2003года имеет метку 01.09.2003 00:00:00.
Иногда это может вызвать недоразумения, например когда смысл месячного параметра – показание расхода энергии на КОНЕЦ месяца, на конец сентября, по сути на 01 октября.
void __stdcall Second2SystemTime(DWORD mrk, SYSTEMTIME *STm);
- преобразует метку времени mrk в структуру Win32 SYSTEMTIME (локальное время). Другими словами, данная функция позволяет получить структурированное представление даты-времени.
DWORD __stdcall SystemTime2Second(SYSTEMTIME *STm);
- преобразует содержимое структуры Win32 SYSTEMTIME (локальное время) в метку времени.
Функции Second2SystemTime() и SystemTime2Second() являются взаимообратными.
DWORD __stdcall Second2Ind(DWORD mrk, DWORD period);
- преобразует метку mrk к индексу для заданного периода period.
Индекс – это порядковый номер периода. Например, индекс суточного параметра – это порядковый номер дня.
Индекс вычисляется в контексте локального времени.
DWORD __stdcall ReduceSecond(DWORD mrk, DWORD period, int shift);
Выполняется в два этапа:
- Метка mrk приводится к началу соответствующего локального периода. Например, метка времени, соответствующая локальному времени 08:59:00 17MAY2005, будет приведена к значению, соответствующему локальному времени 00:00:00 17MAY2005.
- после этого метка сдвигается вперед/назад на заданное значение периодов (shift).
Для значения периода указанного в секундах (period >= 10) расчет производится по упрощенному алгоритму
Результат = (mrk / period) * period + period*shift
Пример
ReduceSecond(mrk, 1, 0) вернет значение метки времени mrk, измененное к началу соответствующих локальных суток;
ReduceSecond(mrk, 1, -1) вернет метку предыдущих локальных суток.
Функции визуализации справочника БД
DWORD __stdcall SelectObj(HWND hParent, int RepObj, int EnableEdit, int Selection, char *Server);
- на окно hParent накладивается стандартный фрейм навигации по базе данных Электро. Аргументы:
RepObj: 0 – навигатор параметров; 1 – навигатор отчетов; 2 – навигатор параметров с checkbox.
EnableEdit: разрешить режим редактирования
Selection: установить фокус на указанный элемент
Server: имя сервера БД
int __stdcall ElSetEvent(int mode, void *proc, void *Self, HWND hParent);
mode=1 – указание адреса call-back процедуры для ранее созданного фрейма навигации (идентифицируемого hParent). Electro.dll инициирует вызов этой процедуры по двойному щелчку на элементе. Self используется в качестве параметра call-back процедуры. Синтаксис *proc :
typedef int (CALLBACK* proc)(void *Self, int oid, char *name, char *server);
Демонстрация использования функций – см. Дельфи-компоненту ElectroView.pas
2.3.6. Прочие функции
int __stdcall Log(int category, char *action);
Функция добавляет запись в журнал событий (см. описание ElectroCNF.TB_Log).
Используется идентификатор текущего пользователя.
Приоритет (Prior) сообщения кодируется в начале *action символом ‘@’:
Log(1, “@1Сообщение с Prior=1”);
Log(1, “Сообщение с Prior=0 (по умолчанию)”);
char * __stdcall ML(char *text);
Функция мультиязычной поддержки. Может быть использована в приложениях при для автоматического перевода фразы text на целевой язык. Функция проверяет наличие text в словаре (файлы LANGxxxx.INI) и возвращает указатель на новый текст.
int __stdcall ElectroXML(int action, char *fname1, char *fname2, OnEvent event, char *opt);
Для xml-документа fname1выполняется действие action. Дополнительные опции м.б. заданы в строке opt. Возникающие в процессе работы сообщения м.б. перехвачены call-back функцией event. Результат выполнения помещается в xml-документ fname2.
Определение для call-back функция event -
typedef int (__stdcall * OnEvent)(char*);
Функция ElectroXML() является базовой для оконного приложения ElectroXML.exe и консольного приложения XmlGen.exe.
ACTION | INPUT | OUTPUT | opt |
0 – экспорт данных | queryàselectàdeviceàpar | dataàselectàdeviceàparàv | SERVER=name |
1 – экспорт конфигурации |
| Profileàelectrocnfàtable | SERVER=name |
2 – замена конфигурации | Profileàelectrocnfàtable | Profileàelectrocnfàtable | SERVER=name |
3 – импорт данных | dataàselectàdeviceàparàv | Протокол работы | SERVER=name |
4 - Дамп БД (обратное к ACTION=13) |
|
| DSN=dbname |
5 – опрос устройств | queryàselectàdeviceàpar | Протокол работы | SERVER=name |
6 – обновление данных в БД | queryàselectàdeviceàpar | Протокол работы | SERVER=name |
7 – удаление данных из БД | queryàselectàdeviceàpar | Протокол работы | SERVER=name |
9 – генератор макетов |
|
|
|
10 – новый вариант экспорта данных |
|
|
|
11 – архивирование данных |
|
|
|
12 – УППД-клиент |
|
|
|
13 - замена БД (обратное к ACTION=4) |
|
|
|
|
|
|
|
В опциях FROM, TO метка времени tm может иметь формат
ггггммддTччммсс 20040114T123000 == 2004 Январь,14 12:30:00
ггггммддTччмм 20040114T1230 == 2004 Январь,14 12:30:00
ггггммддTчч 20040114T12 == 2004 Январь,14 12:00:00
ггггммдд 20040114 == 2004 Январь,14 00:00:00
ггггмм 200401 == 2004 Январь,01 00:00:00
гггг 2004 == 2004 Январь,01 00:00:00
CURRENT время на момент выполнения действия
DAY0 начало текущих суток
DAY1, DAY2 и т.д. начало предыдущих суток
MONTH0 начало текущего месяца
MONTH1, MONTH2 .. начало предыдущих месяцев
В опции OUTPUT имя файла fname2 может содержать специальные знаки, обрабатываемые стандартной библиотечной функцией strftime(), в частности
%a Abbreviated weekday name
%A Full weekday name
%b Abbreviated month name
%B Full month name
%d Day of month as decimal number (01 – 31)
%H Hour in 24-hour format (00 – 23)
%I Hour in 12-hour format (01 – 12)
%j Day of year as decimal number (001 – 366)
%m Month as decimal number (01 – 12)
%M Minute as decimal number (00 – 59)
%S Second as decimal number (00 – 59)
%U Week of year as decimal number, with Sunday as first day of week (00 – 51)
%w Weekday as decimal number (0 – 6; Sunday is 0)
%W Week of year as decimal number, with Monday as first day of week (00 – 51)
%y Year without century, as decimal number (00 – 99)
%Y Year with century, as decimal number
Например, вызов ElectroXML(0, “Job.xml”, “Data_%Y_%m_%d.xml”, NULL,NULL); создаст выходной файл Data_2004_02_24.xml (в зависимости от текущей даты).
Также, аргументы fname1, fname2 могут указывать на специальную структуру
typedef struct {
char sign[4]; // "@@@@" - признак mem
DWORD sz; // размер bf[]
DWORD len; // фактический размер данных в bf[]
BYTE *bf; // указ-ль на бинарные данные
char *err; // текст последней ошибки
mem;
В этом случае используется «файл в памяти» (см. mem.cpp). Режим работы «файл в памяти» распознается по наличию символов "@@@@" соответственно по адресам fname1 и/или fname2 с помощью функции MemIs().
Пример.
#include “mem.h”
mem IM, OM;
MemInit(&IM); MemInit(&OM); // создание пустых обьектов mem
// имитируем формирование входного XML-документа
MemPrintf(&IM, “<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<electroxml>\n”);
MemPrintf(&IM, “<query from=\"DAY%d\" to=\"CURRENT\" finfo=\"1\" fsmart=\"1\" fdebug=\"0\">\n”, 2);
MemPrintf(&IM, “<select><device serialnumb=\"CTK3_022022\"><par memind=\"30000\"/></device></select>\n”);
MemPrintf(&IM, “</query></electroxml>”);
int rz = ElectroXML(0, (char*)&IM, (char*)&OM, NULL, NULL);
char *result = (char*)MemPtr(&OM); // ссылка на строку – вых.документ