Writing to OPC HDA Archive Database

<< Click to Display Table of Contents >>

Navigation:  Multi-Protocol MasterOPC Server > Lua 5.1 Reference Manual > Examples and Other Useful Things > Operation via ODBC >

Writing to OPC HDA Archive Database

The example configuration ODBC Example.mpp is described in this section. The configuration includes simulation tags with the HDA mode switched on as well as the separate node for operation via ODBC. Basing on this example, you can develop own script adapted for your configuration and DB.  

zapis_v_bazu_opc_hda_arkhiva1

We will poll 3 tags and write them into a MS SQL table of the following structure:

zapis_v_bazu_opc_hda_arkhiva

Also, the ID is declared as the primary key, with the increment enabled.

zapis_v_bazu_opc_hda_arkhiva3

Name of table - ArchiveValues.

Below is the whole code of the example.

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  

The header of the script specifies the database connection parameters - ODBC connector name, login and password. If necessary, these parameters can be set as additional device properties. The header also sets the path to the device with the tags to be exported to the database.

The OnInit section retrieves the list of HDA tags of the target device. You can use the server.GetAttributeTagByNumber function, where you can pass the path to the desired node or device as the second parameter. To get the number of tags you can use the server.GetCountTags function, which also passes the path to the desired device as an argument. In the loop through the tags of the device, check that the tag was received successfully (no error flag) and that the tag is an HDA tag (the 11th element of the received argument table). The full name of the tag is added to the tag table, and to the time table we write the value nil - using the values of this table, we will specify from which moment to sample from the HDA archive.

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

In the OnBeforeReading section, the presence of the ODBC object is checked and if its value is nil, an ODBC object is created using the odbc.env_connect function. Then an attempt is made to establish a connection (if it was not established earlier) using the odbc.env_connect function. If an error occurs, the script exits. Then all values of the TagTable table are looped through and for each tag - with its name and timestamp of the last value read - the user WriteToBase function is called.

If the function is invoked successfully, a timestamp of the last-time value is saved to the table of timestamps.

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); -- not 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  

Main operations with DB are performed in the function WriteToBase. As an argument, a tag name and a timestamp of a last-time tag value is passed to the function. An ODBC connection variable, CONN, is global and available from all functions of the script.

The function server.ReadHDAFromTag reads out a tag archive. As a result, variables are formed:  number of records, an archive table, and a timestamp of the last-time value. If there are new tag values, (the variable Num is checked), 2 tables are created: Values (values) and Times (timestamps). (A table of quality tags is not used in this example). Next, an SQL query is formed. In order to insert several values to the table using a single query, the following query syntax is used:  

INSERT INTO ArchiveValues VALUES (Value1,Value2),(Value2,Value3),....(ValueN,ValueN)

First, the query header initializes the variable SQLQuery. Next, all table values are searched. The function string.format concatenates 3 elements to a single string (a tag name, a value and a timestamp). The elements are separated by commas and enclosed in parenthesis. If a table element is not a last one, a comma is added. To view a final SQL query to uncomment a line server.Message("SQLQuery=",SQLQuery);

If a query is formed, it is executed using the function odbc.conn_execute. If an error occurs when executing a query, the connection is closed, and an error code is returned. Otherwise, the function returns that it is completed successfully; a timestamp of the last-time value is returned as well.

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]); --concatenate and format string query

       if i~=table.maxn(Values) then

           SQLQuery=SQLQuery..", ";

       end;

   end;    

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

   res,s = odbc.conn_execute(CONN,SQLQuery); --write values to 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;

A result of configuration operation in the run-time mode is shown below:

zapis_v_bazu_opc_hda_arkhiva2