Важно! Пример проекта доступен для скачивания по ссылке.
Рассмотрим получение данных c сервиса zont-online.ru на примере демонстрационного профиля ZONT доступного по ссылке. Допустим, требуется периодически получать данные с сервиса ZONT и выводить их на мнемосхемы или записывать во внутренние переменные для последующей работы с ними. В качестве примера рассмотрим получение данных с устройства ZONT H2000+. Для других устройств ZONT принцип будет аналогичен.
В соответствии с рекомендациями ZONT API, предпочтительной является авторизация с помощью токена. Т.е., сначала нужно выполнить запрос и получить от сервера токен авторизации, а затем использовать этот токен в последующих запросах. Для решения задачи удобнее всего создать в скриптах глобальный модуль и описать в нем нужные процедуры, а затем вызывать их в других скриптах.
Создадим новый скрипт с типом события "Глобальный модуль" и таким кодом (см. комментарии чтобы понять принцип работы):
interface
var
AuthToken: string = ''; // в эту глобальную переменную будет получен токен
procedure DoAuthOpen;
procedure DoZontDevices;
implementation
// Процедура аутентификации. После её выполнения сервер вернёт
// токен. Значение токена будет записано в переменную AuthToken
procedure DoAuthOpen;
var
aHTTP: TM_HTTP;
aPOST: TM_HTTPPost;
begin
AuthToken := '';
aPost := TM_HTTPPost.Create(hptJSON);
aPost.Add('{"client_name": "Simple-Scada"}'); // произвольное имя
aHTTP := RequestHTTP;
aHTTP.SetSSL(stSSLv23);
aHTTP.SetHeader('ContentType', 'application/json');
aHTTP.SetHeader('Authorization', 'Basic ' + Base64Encode('demo:demo')); // логин/пароль профиля ZONT, закодированные в Base64
aHTTP.SetHeader('X-ZONT-Client', 'ilya@microline.ru'); // e-mail профиля ZONT
aHTTP.Post('https://zont-online.ru/api/get_authtoken', aPost, 1); // отправляем запрос и отмечаем его тегом = 1
end;
// эта процедура отправляет запрос используя полученный ранее токен авторизации
procedure DoZontDevices;
var
aHTTP: TM_HTTP;
aPOST: TM_HTTPPost;
begin
// прерываем выполнение, если токен ещё не был получен
if AuthToken = '' then
Exit;
// создаём данные для отправки серверу
aPost := TM_HTTPPost.Create(hptJSON);
aPost.Add('{"load_io": true}'); // получить данные по датчикам
aHTTP := RequestHTTP;
aHTTP.SetSSL(stSSLv23);
aHTTP.SetHeader('ContentType', 'application/json');
aHTTP.SetHeader('X-ZONT-Client', 'ilya@microline.ru'); // e-mail профиля ZONT
aHTTP.SetHeader('X-ZONT-Token', AuthToken); // авторизуемся с помощью токена
aHTTP.Post('https://zont-online.ru/api/devices', aPost, 2); // отправляем запрос и отмечаем его тегом = 2
end;
end.
Теперь можно вызвать процедуру аутентификации DoAuthOpen. Проще всего для этого использовать скрипт с типом события "Полностью запущен". Данный скрипт позволит получить токен сразу после запуска проекта.
begin
DoAuthOpen;
end.
Далее необходимо создать новый скрипт с типом события "Выполнен HTTP запрос", обработать ответ от сервера ZONT и получить токен аутентификации:
var
aNode: TM_JSONNode;
begin
if Response.Tag = 1 then // тег = 1 (выполнена процедура аутентификации)
if Response.Code = 200 then // код ответа сервера равен 200 (ОК)
begin
aNode := Response.NodesStr['token']; // извлекаем поле "token"
if Assigned(aNode) then // если такое поле существует
begin
AuthToken := aNode.AsStr; // то сохраняем значение токена в глоб. переменную AuthToken
DoZontDevices; // запрашиваем данные ZONT сразу после получения токена
end;
end else
AddMessage(Now, mkAlarm, 'Запрос на аутентификацию вернул ошибку!' + ' Код = ' + IntToStr(Response.Code), True, False);
end.
Так как нам требуется получать данные периодически, создадим скрипт с типом события "Таймер" - в параметре скрипта "Интервал" укажем, с какой частотой должен выполняться скрипт, например 60 секунд. Напишем код скрипта:
begin
DoZontDevices; // запрашиваем данные ZONT
end.
Далее необходимо создать новый скрипт с типом события "Выполнен HTTP запрос" и обработать ответ от сервера ZONT, содержащий данные по устройствам. В скрипте ниже рассмотрен пример записи полученных данных в ячейки таблицы, пример получения данных из вложенного массива, а также пример записи текущих значений во внутренние переменные.
var
I, J: Integer;
aDeviceArray, aThermArray, aTherm, aThermCurr, aDevice: TM_JSONNode;
aVar: TM_Variable;
begin
// прерываем выполнение, если тег не равен 2
if Response.Tag <> 2 then
Exit;
// если код ответа сервера не равен 200 (ОК), то выдаём ошибку
if (Response.Code <> 200) then
begin
AddMessage(Now, mkAlarm, 'Ошибка выполнения HTTP-запроса, Тег = ' + IntToStr(Response.Tag) + '. Код = ' + IntToStr(Response.Code), TRUE, FALSE);
Exit; // прерываем выполнение
end;
// получаем массив устройств
aDeviceArray := Response['devices'];
for I := 0 to aDeviceArray.Count - 1 do
begin
aDevice := aDeviceArray.Nodes[I]; // извлекаем очередное устройство из массива в aDeviceArray
// ищем устройство ZONT H2000+ (ID 202303)
// ID устройства можно посмотреть в личном кабинете ZONT
if aDevice['id'].AsInt = 202303 then
begin
// пример записи свойств в ячейки таблицы
tblDevice.GetCell(1, 0).Text := aDevice['name'].AsStr; // имя устройства
tblDevice.GetCell(1, 1).Text := aDevice['id'].AsStr; // ID устройства
tblDevice.GetCell(1, 2).Text := aDevice['online'].AsStr; // статус устройства
tblDevice.GetCell(1, 3).Text := aDevice['serial'].AsStr; // серийный номер
// некоторые данные могут находиться во вложенных массивах
// для примера рассмотрим получение данных из вложенного массива термометров (wired_temperature_sensors)
aThermArray := aDevice['z3k_config']['wired_temperature_sensors']; // получаем массив термометров
for J := 0 to aThermArray.Count - 1 do
begin
// поочередно извлекаем термометры из массива aThermArray
// и записываем нужные данные в строки таблицы tblSensors
aTherm := aThermArray.Nodes[J];
tblSensors.GetCell(0, J + 1).Text := aTherm['name'].AsStr;
tblSensors.GetCell(1, J + 1).Text := aTherm['id'].AsStr;
tblSensors.GetCell(2, J + 1).Text := aTherm['serial'].AsStr;
// ниже рассмотрен пример получения данных из списка текущих значений (z3k-state)
// и запись полученных значений во внутренние переменные Simple-Scada
aThermCurr := aDevice['io']['z3k-state']; // получаем список текущих значений
aVar := GetVariableByName('vrTemp' + IntToStr(J + 1)); // поиск внутренней переменной для записи значения
if aVar <> nil then // проверка существования переменной
// значения температур в списке z3k-state хранятся в соответствии с ID термометра из массива wired_temperature_sensors
// поэтому, для получения значения используем ID термометра из массива wired_temperature_sensors
aVar.Value := aThermCurr[aTherm['id'].AsStr]['curr_temp'].AsFloat;
end;
end;
end;
end.
Если требуется получать только текущие значения по ID датчиков, то можно использовать следующий скрипт по событию "Выполнен HTTP запрос":
// текущие значения датчиков ZONT H2000+ хранятся в списке z3k-state в соответствии с ID датчика
// при необходимости, можно брать значения напрямую из списка z3k-state по ID датчика как это описано ниже
var
I: Integer;
aDeviceArray, aTherm, aDevice: TM_JSONNode;
begin
// прерываем выполнение, если тег не равен 2
if Response.Tag <> 2 then
Exit;
// если код ответа сервера не равен 200 (ОК), то выдаём ошибку
if (Response.Code <> 200) then
begin
AddMessage(Now, mkAlarm, 'Ошибка выполнения HTTP-запроса, Тег = ' + IntToStr(Response.Tag) + '. Код = ' + IntToStr(Response.Code), TRUE, FALSE);
Exit; // прерываем выполнение
end;
// получаем массив устройств
aDeviceArray := Response['devices'];
for I := 0 to aDeviceArray.Count - 1 do
begin
aDevice := aDeviceArray.Nodes[I]; // извлекаем очередное устройство из массива в aDeviceArray
// ищем устройство ZONT H2000+ (ID 202303)
// ID устройства можно посмотреть в личном кабинете ZONT
if aDevice['id'].AsInt = 202303 then
begin
aTherm := aDevice['io']['z3k-state']; // получаем список текущих значений
// получаем значение по ID датчика и записываем его во внутреннюю переменную
vrTemp25.Value := aTherm['4276']['curr_temp'].AsFloat; // улица
vrTemp26.Value := aTherm['4264']['curr_temp'].AsFloat; // стрелка
vrTemp27.Value := aTherm['4263']['curr_temp'].AsFloat; // 1контур(1эт.бат.)
vrTemp28.Value := aTherm['4265']['curr_temp'].AsFloat; // 2контур(пол)
vrTemp29.Value := aTherm['4266']['curr_temp'].AsFloat; // 3контур(2эт.бат.)
vrTemp30.Value := aTherm['4281']['curr_temp'].AsFloat; // 4 контур
vrTemp31.Value := aTherm['4288']['curr_temp'].AsFloat; // 1эт.кухня
vrTemp32.Value := aTherm['4290']['curr_temp'].AsFloat; // 1эт.зал
vrTemp33.Value := aTherm['4278']['curr_temp'].AsFloat; // 1эт.гостевая
end;
end;
end.