Geo SCADA Knowledge Base
Access vast amounts of technical know-how and pro tips from our community of Geo SCADA experts.
Link copied. Please paste this link to share this article on your social media post.
Originally published on Geo SCADA Knowledge Base by Anonymous user | June 10, 2021 01:02 AM
📖 Home Back
In a structured text program you can define simple variables using %M, %I and %Q, however useful these are they are static.
There is a more dynamic way of reading data from ClearSCADA to use in a structured text program that returns the data in the form of a "resultset" (similar to ADO's RecordSet). This is defined using %S and a new TYPE declaration. It can only access data from within ClearSCADA, that is, you cannot use %S to run a query against an external database.
Due to the way that SQL commands within logic programs are handled, it is critical that sensible queries are used. Inappropriate queries can severely impact performance of the system and potentially delay other system operations.
The first step is to define a suitable data type to store each row returned from the query. This is achieved using the TYPE keyword and derived using DATABASE_OBJECT or STRUCT keywords.
You would use DATABASE_OBJECT when accessing information from table derived from CDBObject Database Table. It allows read-write access to properties and the execution of methods (without parameters).
For example,
TYPE
Point : DATABASE_OBJECT(CPointAlgManual)
FullName : STRING;
CurrentValue : LREAL;
END_DATABASE_OBJECT;
END_TYPE
When using DATABASE_OBJECT it automatically includes the Id field as a column, so if you will be looking at a table without an Id Metadata Columns then the DATABASE_OBJECT cannot be used. The table defined (CPointAlgManual in the example) must be the table referred to in the SQL later. Anything derived from the base class CDBObject (i.e. all database objects) has an Id metadata column, however creating a column in a datagrid called Id does not count.
Also, if you plan on using a JOIN you'll not be able to use DATABASE_OBJECT. Instead you have to use STRUCT.
For example,
TYPE
Point :
STRUCT
Id : DINT;
FullName : STRING;
CurrentValue : LREAL;
END_STRUCT;
END_TYPE
Structurally, DATABASE_OBJECT and STRUCT are the same, however you cannot use methods in a STRUCT. A STRUCT does not assume anything, such as the Id column is included, and the STRUCT may point to any table within ClearSCADA, or multiple tables (JOINs). The order of the columns is important as they must be the same as in the SQL SELECT query.
Once you have your new TYPE defined you can declare the SQL to retrieve the resultset.
The declaration of the resultset is similar to normal variables, it must exist within the VAR/END_VAR blocks only containing other external variables (%M, %I, %Q and %D).
For example,
VAR
Points AT %S(SELECT Id, FullName, CurrentValue FROM CPointAlgManual) : RESULTSET OF Point
END_VAR
The above declaration will work for both DATABASE_OBJECT and STRUCT examples of TYPE and will return all Internal Analogues and their current value.
You can also pass in parameters declared in the VAR_INPUT block.
For example,
VAR
Points AT %S(SELECT Id, FullName, CurrentValue FROM CPointAlgManual WHERE FullName LIKE '%' || ? OR FullName LIKE ? || '%') : RESULTSET OF Point WITH_PARAMS Name, Name;
END_VAR
The || is there to concatenate the % onto the start or end of the condition. If you just wanted a simple comparison then the following is suitable, even for strings:
VAR
Points AT %S(SELECT Id, FullName, CurrentValue FROM CPointAlgManual WHERE FullName LIKE ?) : RESULTSET OF Point WITH_PARAMS Name;
END_VAR
Once you have a resultset you can access its information. The following properties are available:
The following methods are available to navigate the resultset:
For example,
WHILE Points.Valid DO
Count := Count + Points.Value.CurrentValue;
Points.Next();
END_WHILE;
The .Last() method moves the cursor to the last record, not after the end of the last record. This means that .Valid will still return True and if you were using .Last to break out of a WHILE loop then if the condition of the break happens on the last record, you could get into an infinite loop. You may have to call .Next() after a .Last() to go beyond the bounds of the resultset and .Valid() to become false. For example,
WHILE Points.Valid DO
IF Count > 100 THEN
Points.Last();
Points.Next(); (* Required else could cause an infinite loop after a few points had be summed up in the Count value*)
ELSE
Count := Count + Points.Value.CurrentValue;
Points.Next();
END_IF;
END_WHILE;
Link copied. Please paste this link to share this article on your social media post.
Create your free account or log in to subscribe to the board - and gain access to more than 10,000+ support articles along with insights from experts and peers.