You are on page 1of 38

Ignition by Inductive Automation

Copyright 2011
All rights reserved
Inductive Automation
2110 21st St, Ste. 500
Sacramento, CA 95818

Table of Contents
Part I Databases

1 Ignition
...................................................................................................................................
Database Connections
5
2 What is
...................................................................................................................................
a Database?
6
3 SQL

................................................................................................................................... 8

Part II Database Manipulation - Create Database


Table

13

Part III Database Manipulation - View Data

16

Part IV Database Manipulation - Insert Data

22

Part V Database Manipulation - Update Data

27

Part VI Database Manipulation - Delete Data

35

Part VII Working with Datasets in Scripting

37

Index

2012 Inductive Automation

Databases
Part I

Databases

Databases

1.1

Ignition Database Connections

Connects to a SQL Database through JDBC


JDBC or Java Database Connectivity is an application program interface (API) specification for
connecting programs written in Java to the data in popular databases. All major database systems, such
as MySQL and SQL Server, provide the JDBC connector / driver. Ignition ships will all of the popular
connectors.
Configuration:
Connect URL
Username / Password
Extra Connection Properties
Failover
Connection Pooling
Store & Forward

Ignition Connection Failover


Database connections support "failover", by which objects that use that connection will use a different
connection if it is unavailable. The failover datasource determines which connection will be used, and the
failover mode determines when, if ever, the connection will switch back to the primary connection.
Standard Mode - Dictates that the secondary connection will be used only until the primary
connection is available again.
Sticky Mode - Instead, will continue to use the secondary connection until that connection fails, or
until the system is restarted.

Ignition Connection Pooling


A connection pool is a cache of database connections maintained by the database so that the
connections can be reused when the database receives future requests for data. Connection pools are
used to enhance the performance of executing commands on a database. Opening and maintaining a
database connection for each user, especially requests made to a dynamic database-driven website
application, is costly and wastes resources. In connection pooling, after a connection is created, it is
placed in the pool and it is used over again so that a new connection does not have to be established. If
all the connections are being used, a new connection is made and is added to the pool. Connection
pooling also cuts down on the amount of time a user must wait to establish a connection to the
database.

2012 Inductive Automation

Databases

Ignition Store & Forward


The store and forward system provides a reliable way for Ignition to store historical data to the database.
Systems such as SQLTags Historian, Transaction Groups, etc. use the store and forward system in
order to ensure that data reaches its destination in the database, and is stored in an efficient manner.
The store and forward system can be configured in a number of ways, offering both memory buffering for
performance and local disk caching for safe storage.
Primary Features and Benefits
Data Loss Prevention
Guaranteed Ordering
Enhanced Performance

1.2

What is a Database?
A structured collection of records or data that is stored in a computer system.

Types of Databases
There are several common types of databases; each type of database has its own data model (how the
data is structured).
Flat Model
Hierarchical Model
Relational Model

What is a Relational Database?


A database consisting of separate tables, having explicitly defined relationships, and whose elements
may be selectively combined as the results of queries.

2012 Inductive Automation

Databases

What is a RDBMS?
A SQL Database System or Relational Database Management System (RDBMS) stores data in the form
of related tables, organizes and provides access to the data through a language called SQL.
Common Systems
MySQL
Microsoft SQL Server
Oracle
PostgreSQL
DB2

RDBMS Architecture

2012 Inductive Automation

Databases

1.3

SQL
What is SQL?
SQL or Structured Query Language is a standard computer language for accessing and manipulating
database systems. SQL statements are used to create, maintain and query relational databases.
Examples:
SELECT
INSERT
UPDATE
DELETE

Firstname FROM Contacts WHERE Lastname = Smith


INTO Contacts VALUES (Joe, Smith)
Contacts SET Lastname = Howard WHERE ID = 1
FROM Contacts WHERE Lastname = Smith

Tables
The foundation of every database system is a table. Every database consists of one or more tables,
which store the databases data / information. Each table is identified by a name (e.g. "Customers" or
"Orders") and consists of columns and rows.
The database table columns have their own unique names and have a pre-defined data types. Table
columns can have various attributes defining the column functionality (the column is a primary key, there
is an index defined on the column, the column has certain default value, etc.).
While table columns describe the data types, the table rows contain the actual data for the columns.

Primary Key
A primary key is a way to uniquely identify each row in a table. Every table must have a primary key. A
primary key comprises of a single column or set of columns.
No two distinct rows in a table can have the same value (or combination of values) in those columns.
Can be:
Auto-incrementing
Statically defined

Index
Indexes speed up the querying process by providing swift access to rows in the data tables, similarly to

2012 Inductive Automation

Databases

the way a books index helps you find information quickly within that book.
Indexes are extremely important when querying large sets of data. You should create an index for each
set of columns you specify in a where clause. For example, you should add an index on the timestamp
column of a historical table when querying the table by a start and end date.

Foreign Key
A foreign key is a referential constraint between two tables. The foreign key identifies a column or a set
of columns in one (referencing) table that refers to a column or set of columns in another (referenced)
table.
The columns in the referencing table must be the primary key in the referenced table.
Example:
Supplier ( SupplierNumber, Name, Address, Type )
Invoices ( InvoiceNumber, SupplierNumber, Text )

NULL Value
NULL is a special marker used in SQL to indicate that a data value does not exist in the database.

SELECT Command
The SELECT statement is used to select data from a database. The result is stored in a result table,
called the result-set.
Examples:
SELECT * FROM Customers
SELECT Name FROM Customers
SELECT Name, Address FROM Customers

UPDATE Command
The UPDATE statement is used to update records in a table.
Examples:
UPDATE Customers SET Name = Inductive Automation
UPDATE Customers SET Address = 2110 21st Street WHERE ID = 1

INSERT Command
The INSERT statement is used to insert a new row in a table.
Example:
INSERT INTO Customers (Name, Address, City, State, Zip, Country, Phone)
VALUES (Inductive Automation, 2110 21st Street, Sacramento, CA,
95818, NULL, 800-266-7798)
INSERT INTO Customers SELECT * FROM OtherCustomers

DELETE Command
The DELETE statement is used to delete records in a table.
2012 Inductive Automation

Databases

10

Examples:
DELETE FROM Customers
DELETE FROM Customers WHERE Name = Inductive Automation

WHERE Clause
The WHERE clause is used to extract only those records that fulfill a specified criterion. Operators
Allowed in the WHERE Clause:
= Equal
<> Not equal
> Greater than
< Less than
>= Greater than or equal
<= Less than or equal
BETWEEN Between an inclusive range
LIKE Search for a pattern
IN If you know the exact value you want to return for at least one of the columns
The AND & OR operators are used to filter records based on more than one condition.
Examples:
SELECT * FROM Customers WHERE State = CA
SELECT Name FROM Customers WHERE Address LIKE %St% AND State = CA

ORDER BY Clause
The ORDER BY keyword is used to sort the result-set by a specified column. The ORDER BY keyword
sort the records in ascending order by default. If you want to sort the records in a descending order, you
can use the DESC keyword.
Examples:
SELECT * FROM Customers ORDER BY Name ASC
SELECT * FROM Customers ORDER BY State ASC, Name DESC

Joins
The JOIN keyword is used in an SQL statement to query data from two or more tables, based on a
relationship between certain columns in these tables. Tables in a database are often related to each
other with keys. A primary key is a column (or a combination of columns) with a unique value for each
row. Each primary key value must be unique within the table. The purpose is to bind data together,
across tables, without repeating all of the data in every table.
JOIN: Return rows when there is at least one match in both tables
LEFT JOIN: Return all rows from the left table, even if there are no matches in the right table
RIGHT JOIN: Return all rows from the right table, even if there are no matches in the left table
FULL JOIN: Return rows when there is a match in one of the tables
Example:
SELECT * FROM Contacts ct JOIN Customers cn ON cn.ID = ct.CustomerID
2012 Inductive Automation

Databases

11

GROUP BY Clause
The GROUP BY statement is used in conjunction with the aggregate functions to group the result-set by
one or more columns.
Example:
SELECT SUM(Duration) FROM Downtime GROUP BY CauseID

2012 Inductive Automation

Database Manipulation - Create Database Table


Part II

Database Manipulation - Create Database Table

13

Database Manipulation - Create Database Table


Adding, editing and deleting data is vital for most HMI / SCADA systems. Every project in some way
needs to interact with the database. This example walks you through creating a table in the database
and interacting with it in Ignition by adding, editing and deleting data from it.
Let's create a simple inventory table. Open up the MySQL Workbench and connect to your database.
Underneath your database schema, right click on Tables and select Create Table

Name the table inventory (press enter name to take affect).


Select the Columns tab to add new columns. Just click in the first cell to edit that column. Here we need
five columns like the following:
Column Name
id
name
description
location
quantity

2012 Inductive Automation

Datatype
INT
VARCHAR(45)
TEXT
VARCHAR(255)
FLOAT

PK
Y
N
N
N
N

NN
Y
Y
N
Y
Y

AI
Y
N
N
N
N

Database Manipulation - Create Database Table

14

Press Apply to save.


Lets add at least one row to start with. Right click on the Tables and select Refresh All. Once you see
the recipes table, right click on it and select Edit Table Data.
Double click in the cells to change the values. Let's add a row with the following values:
name
description
location
quantity

ControlLogix Processor
Processor in charge of the plant
Plant Floor
1

Once completed click on the green check mark to save. Press "Apply".
The table is now ready to use.

2012 Inductive Automation

Database Manipulation - View Data


Part III

Database Manipulation - View Data

16

Database Manipulation - View Data


Now that we have the database table we need to create a window to display all of the inventory items in
that table.
Open or create a new window for this example. Call the window "Inventory". Make sure you put the
window in the correct day folder.
A table is the best way to display the inventory data. Drag a Table component from the Tables tab of the
component palette.
Next, click on the bind icon to the right of the data property and bind it to a SQL query type. We want to
display all of the inventory so the query is:
SELECT
*
FROM
inventory
ORDER BY
name ASC
We want the query to poll so set the polling options to relative rate.

Press "OK" to save. We should have one row of data.

2012 Inductive Automation

Database Manipulation - View Data

17

Eventually we will have a lot of rows of data. We need some way to filter the data. Let's add a search
text box above the table so the user can search for specific items. Drag in a Text Field component from
the Input tab of the component palette and place it above the table.
To make the table dynamic and use the search field we need to add to the SQL query. We are going to
make the query dynamic by adding a WHERE clause.
Click on the bind icon to the right of the data property and change the query to the following:
SELECT
*
FROM
inventory
WHERE
name LIKE '%{Root Container.Text Field.text}%'
ORDER BY
name ASC

2012 Inductive Automation

Database Manipulation - View Data

18

Now when the user types into the text field the data in the table will change. Go into preview mode and
check it out.

2012 Inductive Automation

Database Manipulation - View Data

19

You can see no row in the table has a name with the text "Test" inside of it. Let's make the table look
nicer. First, change the following properties:
Row Height
Background Mode
Odd Row Background

20
Alternating
255,255,204,255

Secondly, let's change the header names. Right click on the table and select Customizers > Table
Customizer. Here we can alter the way the table looks by customizing each column.
The user doesn't want to see the id column so check the "Hide" checkbox under the id column.
We want the quantity column to be centered so change the "Horiz Align" to "Center" under the quantity
column.
Lastly, change the "Header" name of each column:
name
description
location
quantity

Press "OK".
2012 Inductive Automation

Name
Description
Location
Qty

Database Manipulation - View Data

20

Almost there. Let's change the width of each column. Go into Preview mode and alter the widths of each
column to your liking. Once done leave preview mode and in order to save the widths you must go back
into the Table Customizer and press "OK". That makes the widths of each column stick.
Lastly, add a nice inventory label at the top and make the table fit the window. Remember to set the
layout mode correctly.

2012 Inductive Automation

Database Manipulation - Insert Data


Part IV

Database Manipulation - Insert Data

22

Database Manipulation - Insert Data


Now we need the ability to add a new inventory item.
Let's add a button that opens a popup window where the user can enter in a new inventory item and add
it to the table. Drag in a Button component from the Buttons tab of the palette. Place it to the right of the
table.
Change the following properties on the button:
Text
Image Path
Mouseover Text

Empty String
Builtin/icons/24/add2.png
Add a new inventory item

In order to get the button to work correctly we need a new popup window. Let's now create the "Add
Inventory" popup window.
Create a new window by selecting File > New > Popup Window from the file menu. Call the window "Add
Inventory" and put it in the Popups folder.
The popup window will allow users to type in a name, description, location and quantity. It will have a
button to add the data and a button to cancel or close the window. We are going to create a simple form
using various input components such as a text field, numeric text field and a text area.
Drag in a Text Field component from the Input tab of the component palette. Name the component
"Name" by setting the Name property. Add a Label component to the left of the text field that says
"Name".

Do the same thing for the description, location and quantity using the following components:
Description
Location
Quantity

Text Area
Text Field
Numeric Text Field

Remember to name each component appropriately. I named the rest "Description", "Location" and
"Quantity".
Also change the Number Type property of the Quantity numeric text field to Float instead of Integer.
Next, add two buttons at the bottom of the window (from the Buttons tab of the palette) called "Add" and
"Cancel". You should have a window that looks like this:

2012 Inductive Automation

Database Manipulation - Insert Data

23

All that is left is to configure the events on the add and cancel button. The cancel button is the easiest,
you just want it to close the window. Right click on the cancel button and select "Event Handlers...".
Select the actionPerformed event under action. On the right hand side choose the Navigation tab
and select the Close > This Window radio button.

2012 Inductive Automation

Database Manipulation - Insert Data

24

Ok, now for the add button. Again right click on the button and select Event Handlers... Choose the
actionPerformed event under action. This time select the Script Editor tab on the right hand side and
enter in the following script and press "OK":
name = event.source.parent.getComponent('Name').text
desc = event.source.parent.getComponent('Description').text
loc = event.source.parent.getComponent('Location').text
qty = event.source.parent.getComponent('Quantity').floatValue
if name == "":
system.gui.messageBox("Please enter in a name")
elif qty <= 0:
system.gui.messageBox("Please enter in a quantity greater than 0")
else:
system.db.runPrepUpdate("INSERT INTO inventory (name, description,
location, quantity) VALUES (?,?,?,?)", [name, desc, loc, qty])
system.nav.closeParentWindow(event)
Notice the script validates the name and quantity fields making sure the name is not empty and the
quantity is greater than 0.
2012 Inductive Automation

Database Manipulation - Insert Data

25

That's it. The add window is done. Go back to the Inventory window. The add button now needs to open
this new popup window.
Right click on the add button and select Event Handlers... You will notice we are doing that a lot
(Control-J is faster). Select the actionPerformed event under action. Select the Navigation tab.
Choose the "Open" radio button and find the "Add Inventory" window.

Done. Try it out in the client.

2012 Inductive Automation

Database Manipulation - Update Data


Part V

Database Manipulation - Update Data

27

Database Manipulation - Update Data


Now let's create the "Edit Inventory" popup window. The window is going to look just like the add window
except for a couple of small details. First, right click on the "Add Inventory" window in the Project
Browser and select "Duplicate Window".

Right click on the new window and rename it to "Edit Inventory". Again place it in the "Popups" folder.

Open the Edit Inventory window by double clicking on it. This window is going to edit one of the inventory

2012 Inductive Automation

Database Manipulation - Update Data

28

items in the database. We want to pre-fill the data in each input field. In order to do that we need to pass
in the id of the inventory item from the database (the value of the first column).
To pass parameters we need a custom property on the root container. Right click on the Root Container
and select Customizers > Custom Properties.
Press the green + icon to add a new Property. Name the property "id" and give it an Integer data type.
Press OK.
We have the id now and can use that to get the rest of the data from the table. Instead of binding each
input field to a SQL query resulting in 4 queries, we want to create a dataset to hold all of the data. It will
just be one row since we are only editing one inventory item at a time.
Right click on the Root Container and select Customizers > Custom Properties.
Press the green + icon to add a new Property. Name the property "data" and give it an Dataset data
type. Press OK.
Click on the bind icon to the right of the data property and select SQL Query. Type in the following SQL
query:
SELECT
*
FROM
inventory
WHERE
id = {Root Container.id}
The query references the id custom property on the root container. To add this property simply click on
the property link icon to the right and select the property.
Make sure the query is "Polling Off" so it only runs once. If you set the query to poll more than once it
will overwrite information that the user inputs.

2012 Inductive Automation

Database Manipulation - Update Data

29

Making progress. Now we need each input field to show the correct value from the dataset. Let's do the
name field first.
Select the name text field on the window. Bind the "Text" property of the component to an expression
binding. Enter in the following expression:
try({Root Container.data}["name"], "")
The expression goes inside of the dataset and gets out the name column's value. We didn't reference a
row because the first row is implied and we will only have one row of data.
Do the same thing for the rest of the components.
Description - "Text" property
try({Root Container.data}["description"], "")
Location - "Text" property
try({Root Container.data}["location"], "")
Quantity - "Value (Float)" property
try({Root Container.data}["quantity"], 0.0)
Notice the property and expression is a little bit different for the quantity field since it is a floating point
number. To test it out set the id column on the root container to 1. The components should get values.

2012 Inductive Automation

Database Manipulation - Update Data

30

All that is left is to alter the former add button to now be an edit button. Change the labels and buttons to
say edit inventory and save.
Right click on the Save button (add button previously) and select Event Handlers... There should already
be a script there replace it with the following:
id = event.source.parent.id
name = event.source.parent.getComponent('Name').text
desc = event.source.parent.getComponent('Description').text
loc = event.source.parent.getComponent('Location').text
qty = event.source.parent.getComponent('Quantity').floatValue
if name == "":
system.gui.messageBox("Please enter in a name")
elif qty <= 0:

2012 Inductive Automation

Database Manipulation - Update Data

31

system.gui.messageBox("Please enter in a quantity greater than 0")


else:
system.db.runPrepUpdate("UPDATE inventory SET name = ?, description =
?, location = ?, quantity = ? WHERE id = ?", [name, desc, loc, qty, id])
Now the query is an UPDATE rather than an INSERT using the id custom property on the root container.
Done. Go back to the inventory window. Again we need the ability to open the edit window when a user
selects a row from the table. Select the Table component on the inventory window. The table component
does have a "Selected Row" property that changes when you select a row.

The problem is that the value is the index into the dataset of the selected row. So if you select the first
row the value is 0. The second is 1 and so on. This is not the id value we need in order to open the edit
window. Don't worry there is a nice trick to get the id column. We will go inside of the table's data
dataset and get out the value of the id column for the selected row.
Right click on the Table and select Customizers > Custom Properties.
Press the green + icon to add a new Property. Name the property "selectedID" and give it an Integer
data type. Press OK.
Click on the bind icon to the right of the selectedID property and bind it to the following expression:
try({Root Container.Table.data}[{Root Container.Table.selectedRow},
"id"], -1)

2012 Inductive Automation

Database Manipulation - Update Data

32

The expression goes inside of the dataset getting out the value at the selectedRow row and the "id"
column. Since I have the first row selected the value should be 1 when I press "OK".

Now add an edit button to the right of the table just like the add button.

We only want the edit button enabled if the user has selected a row. So bind the "Enabled" property of
the edit button to the following expression:
{Root Container.Table.selectedID} != -1
We need the button to do something so right click on it and select Event Handlers... Again select the
actionPerformed event under action. Choose the Navigation tab.
Select the "Open" radio button and find the "Edit Inventory" window.
We need to pass in the selectedID value from the table. Check the "Pass Parameters" checkbox. Click
on the green + to add a new parameter. Name the parameter "id". Link the row to the selectedID
property on the table.

2012 Inductive Automation

Database Manipulation - Update Data

33

That's it! Now you can edit an inventory item.


Before we create the delete button I want to show you another trick. You can double click on the row in
the table to open the popup window as well.
Right click on the table and choose "Event Handlers...". Select the mouseClicked event under mouse.
Select the Script Editor tab and enter in the following script and press "OK":
if event.clickCount == 2:
event.source.parent.getComponent('Edit').doClick()
Essentially, if the user performs a double click on the table it will programmatically click on the edit
button. Try it out in the client.

2012 Inductive Automation

Database Manipulation - Delete Data


Part VI

Database Manipulation - Delete Data

35

Database Manipulation - Delete Data


Last but not least we need a delete button on the inventory window to delete an inventory item. We want
to ask the user if they are sure they want to delete the item as well. We want a button very similar to the
edit button.
Select the edit button and press Ctrl-D to duplicate the component. Move it right below the edit button.
Put the following script on the actionPerformed event:
id = event.source.parent.getComponent('Table').selectedID
if system.gui.confirm("Are you sure you want to delete the selected
inventory item?", "Really Delete?"):
system.db.runPrepUpdate("DELETE FROM inventory WHERE id = ?", [id])
system.gui.messageBox("Inventory item deleted")
That's it. We have completed the example. Launch the client and test it all out.

2012 Inductive Automation

Working with Datasets in Scripting


Part VII

Working with Datasets in Scripting

37

Working with Datasets in Scripting


Datasets are an important datatype in Ignition. They store advanced configuration for a lot of components
and are used to display a large amount of data. There are several ways to work with datasets in
scripting.
Open your project in the designer.
Open or create a window to design the objects.
For this we need to create a dataset and we will use the Test Data from a Table. Drag a Table
component from the Tables tab of the Component Palette into the Root Container.
Set the Property Filter in the Property Editor pane to All.
Set the value of the TestData property to true. You should see data populate the table component.
Next, drag a Button component from the Buttons tab of the Component Palette into the Root Container.
Right click on the button and select Event Handlers... For this example, we are going to use the action
performed event. This is different than mouse clicked because it can be triggered by a mouse click or by
a user tabbing to it and pressing enter. Expand out action and select actionPerformed.
This script will pull in the dataset from the table component, step through the rows and add a column
that displays whether the value is within a set range. The first step is to pull the dataset into a variable.
Add the following to your script. You can get the path to the table by clicking on the Insert Property
Reference icon in the upper right. Once you have the component dataset, you need to convert it to a
Python data set. Add the following code and click OK to save the script.
table = event.source.parent.getComponent(Table)
rawData = system.dataset.toPyDataSet(table.data)
print rawData
Now check that this code works properly. Turn on Preview Mode by clicking on Project > Preview Mode
(or by pressing F5).
Click on Tools > Console to open the output console so you can see what our print command is doing.
Click on the button and the following should be entered into the console.
Dataset [150R ? 3C]
Next, open the Event Handlers for the button. We need to create a two new arrays that will eventually
hold our modified data.
header = [col1, col2, col3, level]
newData = []
Next, we have to step through each row of the raw data with a for loop. Add the following script to step
through the old dataset and print the data.
for row in rawData:
print row

2012 Inductive Automation

Working with Datasets in Scripting

38

Check that this print statement is working right. Open the console again and test the button. You should
see 150 entries of the following.
<Row:3 columns>
You can access data in the rows using []. Change the print statement to in the for loop to the following
and check your results with the console. You should see the data from the table in the console when
you press the button.
for row in rawData:
print row[0], row[1], row[2]
Open the event handler again. Under the for loop, add the following code to test if the third column in the
dataset is over 75 or under 25. You can stop any line from running by putting a # in front of it. Comment
out the first print command in the for loop.
for row in rawData:
if row[2] >= 75:
level = high
elif row[2] <= 25:
level = low
else:
level =
print row[0], row[1], row[2], level
Open the console and test out your button. You should see something like this in the console for each
line (actual values will vary).
34 Test Row 6 83.2627920171505 high
To store this data in the newData array, use the python append() function on your new dataset. This line
should be tabbed in to the same level as your If statements.
newData.append([row[0], row[1], row[2], level])
Finally, convert the newData and header arrays to a dataset and store it in the tables data property.
Enter these lines of code and press the OK button.
finalData = system.dataset.toDataSet(header, newData)
event.source.parent.getComponent(Table).data = finalData
Click on your button and you should see the table component change from three to four columns with
the words high and low added to any column that is greater than 75 or less than 25.
The following is the full script with comments added for reference.
#get the table data (this is a comment using the # symbol)
table = event.source.parent.getComponent(Table)
rawData = system.dataset.toPyDataSet(table.data)
#create a new dataset
header = [col1, col2, col3, level]
2012 Inductive Automation

Working with Datasets in Scripting

newData = []
#step through the old data
for row in rawData:
#test if the third column is too high or too low
if row[2] >= 75:
level = high
elif row[2] <= 25:
level = low
else:
level =
print row[0], row[1], row[2], level
#store the data in the new data set
newData.append([row[0], row[1], row[2], level])
#convert the new data into a dataset
finalData = system.dataset.toDataSet(header, newData)
event.source.parent.getComponent(Table).data = finalData

2012 Inductive Automation

39

You might also like