Работа с ZONT-online.ru

> [HTML Version](zontonline.html)

Важно\! Пример проекта доступен для скачивания [по ссылке](https://simple-scada.com/downloads/ZONT.rar).

Рассмотрим получение данных c сервиса zont-online.ru на примере демонстрационного профиля ZONT доступного [по ссылке](https://lk.zont-online.ru/demo). Допустим, требуется периодически получать данные с сервиса ZONT и выводить их на мнемосхемы или записывать во внутренние переменные для последующей работы с ними. В качестве примера рассмотрим получение данных с устройства ZONT H2000+. Для других устройств ZONT принцип будет аналогичен.

В соответствии с рекомендациями [ZONT API](https://zont-online.ru/api/), предпочтительной является [авторизация с помощью токена](https://zont-online.ru/api/docs/#a1ce3c1a83). Т.е., сначала нужно выполнить запрос и получить от сервера токен авторизации, а затем использовать этот токен в последующих запросах. Для решения задачи удобнее всего создать в скриптах глобальный модуль и описать в нем нужные процедуры, а затем вызывать их в других скриптах. 

[Создадим новый скрипт](via-script-editor.md) с типом события "[Глобальный модуль](globalmodule.md)" и таким кодом (см. комментарии чтобы понять принцип работы):

````
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. Проще всего для этого использовать скрипт с типом события "[Полностью запущен](completely-started.md)". Данный скрипт позволит получить токен сразу после запуска проекта.

````
begin  
  DoAuthOpen;  
end.

````


Далее необходимо создать новый скрипт с типом события ["Выполнен HTTP запрос"](event-types.md), обработать ответ от сервера 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.

````


Так как нам требуется получать данные периодически, [создадим скрипт](via-script-editor.md) с типом события "[Таймер](second-passed.md)" - в параметре скрипта "Интервал" укажем, с какой частотой должен выполняться скрипт, например 60 секунд. Напишем код скрипта:

````
begin  
  DoZontDevices;  // запрашиваем данные ZONT  
end.

````


Далее необходимо создать новый скрипт с типом события ["Выполнен HTTP запрос"](event-types.md) и обработать ответ от сервера 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.

````
