Запись в базу OPC HDA архива

<< Click to Display Table of Contents >>

Navigation:  Modbus Universal MasterOPC Server > Руководство по языку Lua 5.1 > Примеры и полезности > Работа с ODBC >

Запись в базу OPC HDA архива

Настройка таблицы для MS SQL

Пример скрипта

Настройка таблицы для PostgreSQL

Рассматривается пример из конфигурации ODBC Example.mpp. Конфигурация состоит из имитационных тегов с включенным режимом HDA, а также отдельным узлом для работы с ODBC. На основе данного примера вы можете разработать собственный скрипт адаптированный под вашу конфигурацию и базу данных.

zapis_v_bazu_opc_hda_arkhiva1

Настройка таблицы для MS SQL

Мы будем опрашивать все три тега, и записывать их в таблицу MS SQL следующей структуры:

zapis_v_bazu_opc_hda_arkhiva

Примечание. Обратите внимание что длина имени Name - 50 символов, если в вашем источнике более длинные имена, то задайте длину имени больше. Также следует иметь ввиду, что данная структура таблицы подходит только для хранения чисел и логических значений - со строковым типом и временем скрипт работать не будет.

При этом ID объявлен как основной ключ, с включенным инкрементом.

zapis_v_bazu_opc_hda_arkhiva3

Имя таблицы - ArchiveValues.

Примечание. По ссылке находится готовая настроенная база данных - можете также использовать ее. Для этого в Management Studio выполните Databases - Restore Database.

 

Пример скрипта

Полный код примера:

local host = "MSSQLConnect"

local login =""

local password = ""

local env;

local CONN=nil;

local PathToDevice="Simulator.script"; -- path to device

local TagTable={}; --table of name tags

local TimeTable={}; --table of time last time HDA-tags values

 

function WriteToBase(NameTag,LastTime)        

 if CONN==nil then

       return true,nil;

   end

   Num,Archive,LastTime=server.ReadHDAFromTag(NameTag,LastTime,false); --read archive value

   if Num==0 then

       return true,nil;

   end;  

   Values=Archive[1];    

   Times=Archive[3];    

   local SQLQuery="INSERT INTO ArchiveValues VALUES "; --header of query  

   for i=1,table.maxn(Values),1 do        

       SQLQuery=SQLQuery..string.format("('%s','%s','%s')",NameTag,Values[i],Times[i]); --concating and formating string query

       if i~=table.maxn(Values) then

           SQLQuery=SQLQuery..", ";

       end;

   end;    

  --server.Message("SQLQuery=",SQLQuery); --summary SQL query

   res,s = odbc.conn_execute(CONN,SQLQuery); --write values in the table

   if res==nil then --error in request

       odbc.conn_close(CONN); --close connection

           odbc.env_close(env) --close ODBC object

       CONN=nil;

       server.Message("Error=",s);

       return true,nil;

   end;

   return false,LastTime;

end;

-- initialize

function OnInit()  

   local CountTag=server.GetCountTags(PathToDevice);

   for i=0,CountTag-1,1 do

       local err,Tab=server.GetAttributeTagByNumber(i,PathToDevice); --get attribute tag from target device

       if err==false and Tab[11]==true then --not error and this is tag is HDA

           local Name=Tab[2]; --full name of tag

           table.insert(TagTable,Name); --adding to table tags

           table.insert(TimeTable,nil); --write nil value to table of times            

       end        

   end

end

-- deinitialize

function OnClose()

end

-- handling

function OnBeforeReading()

if CONN==nil then  

 env = odbc.env_create(); --create ODBC object  

   server.Message("Connection attempt");

   CONN,s = odbc.env_connect(env,host,login,password); --connecting

   if CONN==nil then

       server.Message("Error=",s); -- no connected

       return;

   end;    

end;

for i=1,table.maxn(TagTable),1 do

   local err,LastTime=WriteToBase(TagTable[i],TimeTable[i]) --run function

   if err==false then

       TimeTable[i]=LastTime; --save last time value

   end;

end  

В заголовке скрипта задаются параметры подключения к базе данных - имя ODBC коннектора, логин и пароль. При необходимости данные параметры можно вынести как дополнительные свойства устройства. Также в заголовке задается путь к устройству с тегами, которые будем экспортировать в базу.

В секции OnInit производится получение списка HDA тегов целевого устройства. Для этого можно воспользоваться функцией server.GetAttributeTagByNumber, в которую вторым параметром можно передать путь к нужному узлу или устройству. Получить количество тегов можно с помощью функции server.GetCountTags, в которую также как аргумент передается путь к нужному нам устройству. В цикле перебираем теги устройства, проверяем что тег получен успешно (нет флага ошибки), а также то, что тег является HDA тегом (11 элемент полученной таблицы аргументов). Полный путь тега добавляем в таблицу тегов, а в таблицу времени записываем значение nil - используя значения этой таблицы, мы будем указывать с какого момента делать выборку из HDA архива.

function OnInit()  

   local CountTag=server.GetCountTags(PathToDevice);

   for i=0,CountTag-1,1 do

       local err,Tab=server.GetAttributeTagByNumber(i,PathToDevice); --get attribute tag from target device

       if err==false and Tab[11]==true then --not error and this is tag is HDA

           local Name=Tab[2]; --full name of tag

           table.insert(TagTable,Name); --adding to table tags

           table.insert(TimeTable,nil); --write nil value to table of times            

       end        

   end

end

В секции OnBeforeReading проверяется наличие ODBC объекта, и если его значение равно nil, то происходит создание ODBC объекта с помощью функции odbc.env_create. Затем происходит попытка установления соединения (если оно не было установлено ранее) с помощью функции odbc.env_connect. В случае ошибки происходит выход из скрипта. Затем в цикле происходит перебор всех значений таблицы TagTable, и для каждого тега - с его именем и меткой времени последнего считанного значения, происходит вызов пользовательской функции WriteToBase.

В случае успешности вызова функции метка времени последнего значения сохраняется в таблицу со временем.

function OnBeforeReading()

if CONN==nil then  

   server.Message("Connection attempt");

   CONN,s = odbc.env_connect(env,host,login,password); --connecting

   if CONN==nil then

       server.Message("Error=",s); -- no connected

       return;

   end;    

end;

for i=1,table.maxn(TagTable),1 do

   local err,LastTime=WriteToBase(TagTable[i],TimeTable[i]) --run function

   if err==false then

       TimeTable[i]=LastTime; --save last time value

   end;

end  

Основные действия с базой производятся в теле функции WriteToBase. В качестве аргумента в функцию поступает имя тега и его последняя метка времени. Переменная соединения с ODBC - CONN, является глобальной, поэтому доступна из всех функций скрипта.

С помощью функции server.ReadHDAFromTag производится считывание архива тега. Итогом формируются переменные - количество записей, таблица архива, и метка времени последнего значения. После проверки что у тега есть свежие значения (проверка переменной Num), происходит создание двух таблицы - Values, содержащая таблицу значения и Times - содержащая метки времени (таблица с признаками качества в данном примере не применяется). Затем происходит формирование SQL запроса. Чтобы вставить несколько значений в таблицу за один запрос, запрос должен иметь следующий синтаксис:

INSERT INTO ArchiveValues VALUES (Значение1,Значение2),(Значение2,Значение3),....(ЗначениеN,ЗначениеN)

Сначала происходит инициализация переменной SQLQuery заголовком запроса. Затем начинается перебор всех значений таблицы. С помощью функции string.format происходит соединение трех элементов в строку - имени тега, значения и времени, разделенные запятыми и обрамленные скобками. Если элемент таблицы не является последним то добавляется запятая. Посмотреть форму итогового SQL запроса можно раскомментировав строчку --server.Message("SQLQuery=",SQLQuery);

После того как запрос сформирован происходит его вызов с помощью функции odbc.conn_execute. Если при выполнении запроса произойдет ошибка, то соединение закрывается, и возвращается код ошибки. В противном случае возвращается, что функция выполнена успешно и возвращается метка времени последнего значения.

function WriteToBase(NameTag,LastTime)        

   Num,Archive,LastTime=server.ReadHDAFromTag(NameTag,LastTime,false); --read archive value

   if Num==0 then

       return true,nil;

   end;  

   Values=Archive[1];    

   Times=Archive[3];    

   local SQLQuery="INSERT INTO ArchiveValues VALUES "; --header of query

   for i=1,table.maxn(Values),1 do        

       SQLQuery=SQLQuery..string.format("('%s','%s','%s')",NameTag,Values[i],Times[i]); --concating and formating string query

       if i~=table.maxn(Values) then

           SQLQuery=SQLQuery..", ";

       end;

   end;    

  --server.Message("SQLQuery=",SQLQuery); --summary SQL query

   res,s = odbc.conn_execute(CONN,SQLQuery); --write values in the table

   if res==nil then --error in request

       odbc.conn_close(CONN); --close connection

       CONN=nil;

       server.Message("Error=",s);

       return true,nil;

   end;

   return false,LastTime;

end;

Результат работы конфигурации в режиме исполнения:

zapis_v_bazu_opc_hda_arkhiva2

 

Настройка таблицы для PostgreSQL

В случае с PostgreSQL настройка столбцов таблицы будет выглядеть следующим образом:

zapis_v_bazu_opc_hda_arkhiva4

Обратите внимание на типы данных, а также на то что столбец ID идет последним. Если разместить его первым, то SQL запрос из примера скрипта не будет выполняться.