Friday, August 12, 2016

Import Historical Data from CSV to Ignition's Tag Historian

Ignition's Tag Historian™ makes it easier than ever to store and use historical data. Simply by selecting a check box on a tag, historical data is stored in an efficient format in any SQL database (MySQL, Microsoft SQL Server, Oracle, PostgreSQL, MariaDB, etc). This data is then available for querying through historical bindings, reporting, and scripting.

Are you migrating from a legacy historian package to Ignition? Do you want to import your old historical data into Ignition's Tag Historian (SQL database)?

This is a common question for customers migrating to Ignition. Ignition stores all historical data in a open format in the SQL database. Ignition doesn't log data to a proprietary format. This makes it extremely easy to migrate data from one system to Ignition. The only catch is that you have to be able to export data from your legacy system to a readable format, such as CSV.

In this post, I am going to show you how to take historical data from a CSV file and import it into Ignition's Tag Historian SQL format.

To get started, here is the basic flow to convert data from a legacy system to Ignition:
  1. Export your historical data from the legacy historian into a CSV file. The CSV file must contain the timestamp and the data.
  2. Map tags from the legacy system to tags in Ignition (by their tag paths).
  3. Specify the Ignition Historical Tag Provider (database) to import into.
  4. Specify the Ignition Realtime Tag Provider that the tags exist in.
  5. Specify the date format of the timestamp column in the CSV (to parse the date correctly).
  6. Call the system.tag.storeTagHistory function in Ignition to store the data.
Let's assume you have already exported your historical data to a CSV file. Here is what my CSV file looks like:

t_stamp Amps Temperature HOA
2016-08-12 08:00:00 4 22 0
2016-08-12 08:01:00 18 88 0

I want to import the Amps, Temperature, and HOA tags into Ignition. I have created a utility (an Ignition Project) that is built to perform the steps above. You can download this utility here:

Once downloaded, you can import the .proj file in your Ignition Designer. You need to have Ignition 7.8.3+ in order to import this utility. Once you have the designer open simply click on File > Import... from the menu.

Select the downloaded .proj file and click Open. You only need the "Converter" window so just select that one and click Import.

Open the Converter window in the Ignition Designer or Ignition Client. If you are in the Ignition Designer you can go into preview mode to interact with the window.

The first step is to open your CSV file. Click on the "Browse" button and locate your CSV file. Once located click on the "Parse" button. Now we are going to start the import process.

The utility will try to automatically detect the timestamp column. Now we just need to follow these steps:

Step 1: Map Columns

If the utility doesn't parse your timestamp column automatically, you need to click on the "Select Mapping" column to the right of your timestamp column and select "Timestamp". For me, you will notice that the "t_stamp" column is already mapped to "Timestamp".

If you happen to have a column that represents the quality of the historical values (OPC quality where 192 = Good), than map that column to "Quality". If you don't have a quality column don't worry, it is optional.

Now we need to map each tag we want to import to the Ignition tag using the fully qualified tag path. A fully qualified tag path looks like this:


Ignition comes with a tag provider called "default" and that is typically where these tags exist. I have three tags in Ignition that I want to map to. They are all inside of a folder called "Process". The tags paths look like this:


Hint: You can right click on your tag in the Ignition Designer and click "Copy Tag Path" to get this path.

For each tag you want to import history on, simply set the mapping to "Tag" and enter in the fully qualified tag path. You will need to double click in the "Mapping Value" column to enter in the path. You also need to specify the data type of the tag (that way Ignition knows the correct data type since we can't rely on the CSV file).

Step 2: Select or Enter In Your Historical Tag Provider

The Historical Tag Provider is the system you want to log the history to. Typically this is the name of the database connection you have created in Ignition. The dropdown list on this screen will list all of your database connections. However, if you are using the Tag History Splitter, you will need to enter in the name of that provider into the text box.

My database connection is called "DB".

Step 3: Enter In Your Tag Provider

The Tag Provider is the name of the realtime tag database your mapped tag is stored in. You can find this out by looking under the "All Providers" folder in the tag browser:

You will notice that my tags are in the "default" tag provider. Type that name into the text box.

Step 4: Enter In Your Timestamp String Format

When we parse the CSV file everything comes in as strings, including the date. The system.tag.storeTagHistory function requries an actual date object. So, the utility has to parse the string timestamp into an actual date object. In order to do that it needs to know the date format. The date formats can be wildly different. Some files may leave out the seconds, some can be in 24 hour format, etc. The typical date format is:

yyyy-MM-dd HH:mm:ss

See this page for more details on the timestamp format. Here are a couple of examples:

2016-08-11 14:20:54 = yyyy-MM-dd HH:mm:ss
2016-08-11 02:20:54 PM = yyyy-MM-dd hh:mm:ss a
8/12/2016 8:00 AM = M/d/yyyy h:mm a

My date format is yyyy-MM-dd HH:mm:ss:

Step 5: Import 

The last step is to press the Import button. The utility parses the CSV and creates the necessary arrays to pass into the system.tag.storeTagHistory function. For more information on that function go here:

That's it. You should now be able to view that history in an Easy Chart.

If you have any questions please let me know.

Friday, July 29, 2016

Java and High DPI Displays

Are you running into issues with your Ignition Designer (or runtime) being extremely small on a high DPI display?

Your not alone. A lot of our customers are running into this issue. Ignition's Designer and runtime are Java Swing applications. The problem is that Java Swing claims that they are DPI aware, but they're not, so Windows doesn't scale it.

Unfortunately, this is not something that Oracle will fix, however, there is a way around it by modifying the EXE manifests for java.exe and javaw.exe. 

This fix requires a resource editor and is for Java 8 or higher. I used Resource Hacker to modify the EXE manifests.

Follow these instructions:
  1. Install Resource Hacker
  2. Run Resource Hacker with administrative privileges
  3. Open java.exe from the installed Java 8 JRE (or JDK) path. Example:

    C:\Program Files\Java\jre1.8.0_51\bin\java.exe

    Note: If you have multiple versions of Java 8 installed you will have follow these steps for each one.
  4. Expand folder 24
  5. Expand folder 1
  6. Click on node 0 and you will see XML on the right
  7. Locate the dpiAware setting under application:


    and change true to false:


  8. Press the Compile Script button at the top
  9. Save your changes through File > Save
  10. Follow the same instructions for javaw.exe
That's it. Now you can set your scaling to 150% or 200% in Windows and the Ignition Designer will look much better. You may have to restart your computer for the settings to take affect. 

Note: If you are using the 64-bit JRE make sure you are applying these changes to java.exe in Program Files not Program Files (x86) and vice-versa. 

Also, if you update your JRE to a newer version you must follow these steps for the new java.exe and javaw.exe.

Thursday, July 21, 2016

Template Layout and Resizing

First of all, I want to say sorry for neglecting the blog for a couple of years. Starting this week, I want to post a new blog entry once a week. I have realized that there are a lot of little things that I know that can really make a difference when developing in Ignition. Stay tuned and follow me for new and exciting tips and tricks on Ignition.

This week I want to touch on template's layout and resizing. I find that a lot of people get tripped up with how the layout works and what happens when you resize the template. For this post, I want to explain exactly how it works.

By default, when you drag a template onto the window from the Project Browser, the size of the instance is exactly the same size as the master template (template definition). You can make the size of the template instance on the window larger or smaller but it only changes the size of that instance. Usually people want to make the template instance larger that what was defined.

However, if you modify the size of the master template it will NOT automatically resize the template instance on the window. For that, you must locate the template instance (everywhere it is being used), right click and select "Revert to Master Size."

That will change the size of the template instance on the window to the size of the master template.

Putting a template on the window is the same idea as putting a Vision component on the window. It is very important to note that you are now dealing with 2 different layouts: the layout of the template on the window AND the layout of the components inside of the template. By default, the layout of the template instance on the window will be "Relative - Maintain Aspect Ratio" and the layout of the components inside of the template is "Relative - Stretch."

The layout of the template instance dictates the layout of the components inside of the template. In other words, if the template instance does not resize than the components inside of the template will not resize as well. Also, the template instance can have a different layout than the components inside of the template. For example, you may want the template instance to fill out the entire window using anchored layout but you want the components inside of the template to maintain their aspect ratio.

So here comes the issue that everybody runs into: you set the layout of the components inside of the template to anchored or to maintain the aspect ratio but when you resize the template on the window it ignores that layout and stretches the components.

That happens because a template acts the same way as a "Component Group" by default. That means the layout is stuck to "Relative - Stretch." If you want the template instances to respect your layout settings, you need to set the "Enable Layout" property to true in the template definition.

That little checkbox will allow you to control the layout inside of the template. With that you can now control both layouts: on the window and in the template.

If you have any questions please feel free to email me at Please stay tuned for more tips and tricks.

Thursday, December 12, 2013

How To Filter Alarms on Static Associated Data

First of all, I want to say sorry for neglecting the blog since January. Starting this week, I want to post a new blog entry once a week. So stay tuned for new and exciting tips and tricks for Ignition.

For this post, I want to show how you can filter alarms on the alarm status component, alarm journal component, and in alarm notification pipelines through the use of static associated data.

Ignition 7.6 introduced a host of new alarming features; one such feature is the ability to add "Associated Data" to an alarm configuration. Associated data are custom alarm properties that can be added to any alarm. The values can either be static or dynamic. Dynamic properties will often be bound to other tags that represent associated contextual data that may be related to the alarm. Either way, a snapshot of the values of these properties will be taken when the alarm becomes active. These values will be attached to the alarm event as it moves through the rest of the alarming system, meaning that the values will be available from the alarm status system, the alarm journal system, and in the alarm notification system.

Let's assume you already have a couple of alarms configured. It's easy to go back to the alarms and add associated data. Edit one of your alarm tags and go to the alarm configuration area. Select one of the alarms. Of course you will see all of the alarm properties that you have previously configured. Those properties come with every single alarm. We can add our own properties called "Associated Data."

To add associated data simple click on the green plus icon right above the alarm properties (in the top right).

That will add a new property at the bottom of the list. By default the property will be called "New Data."

To rename the property simple double click on the name and type in a new one. Let's call the property "Group." At this point, we can either type in a static value for the property or make it dynamic by linking it to another tag or an expression. Dynamic associated data is important to snapshot other tag values along with the alarm. That way you can go back in time and see what the temperature of the room was or what happened upstream or downstream when the alarm occurred. If we simply just type in a static value than it will never change, allowing us to use it in filtering.

Go ahead and set the value of the property for the first tag to "Group A" and press OK to save. Do the same thing for other alarm we have configured and set the property to "Group B."

If your tag has more than one alarm configuration make sure you add the associated data to each one. Every alarm configuration can have a different set of values.

At this moment we have 2 alarm tags with one tag set to "Group A" and other tag set to "Group B." Let's look at how we can filter for a particular group on the alarm status component. 

Drag the alarm status component onto a window. You will notice it shows every single alarm that is active right now. So both of our alarms are showing up.

To add the filter, right click on the component and select "Scripting." On the left hand side you will see the component's "Extension Functions." Extension functions allow you to hook into the component providing a way to override/implement parts of the functionality of the component. There is an extension function on the alarm status component called "filterAlarm" which allows you to filter the alarms any way you want through the use of scripting. Go ahead and select the filterAlarm extension function.

Set the "Enabled" checkbox to true and the script will become active. The filterAlarm function will get called for every single alarm that would normally show up in the component. You will return True if you want the alarm to show up and False if you don't. The alarms that would "normally" show up are based on the filters you can put on the alarm status component, such as minimum priority or alarm state (active and unacked, active and acked, clear and unacked, clear and acked). Typically you would allow the component to bring back everything and you would filter them out in the filterAlarm extension function.

The filterAlarm extension function automatically gives you a variable called alarmEvent, which has a function called get to retrieve any property of the alarm such as the name, source, priority, or our associated data. So, if we only want to show alarms for "Group A" we can put in the following script:

group = alarmEvent.get("Group")
if group == "Group A":
return True
return False

Now we only see one alarm.

Typically you won't hardcode the group name in the script. Usually it comes from a property on the window like a drop down list or value passed into the window. If you want to grab a value from the screen you can use the self variable just like the event.source variable in event handlers. The example below gets a value passed into the window on the root container:

group = alarmEvent.get("Group")
groupName = self.parent.groupName
if group == groupName:
return True
return False

Notice the second line that goes to the parent (root container) and grabs the groupName property. We than use that variable in the if statement.

If you want to filter on the alarm journal component, it is exactly the same as the alarm status component. The alarm journal component also has the filterAlarm extension function. 

Lastly, I want to show how you could use the associated data in an alarm notification pipeline. That way you can do something different for each group like notifying a different list of people.

Go ahead and add a new alarm pipeline in the designer. First drag in the "Switch" block into the pipeline. The switch block is very similar to the expression block, except that the result of the expression does not need to be a boolean. Instead, it can be a number or string, and it will be looked up against the defined list of expected results. If a match is found, the alarm will follow that path, otherwise the "catch all" output will be used. You can simply set the expression to:


Below the expression, add two values to the list: one for "Group A" and one for "Group B."

Now you have two paths on the block that can do different logic if the alarm is in "Group A" or "Group B." Here is a pipeline that will send out an email to two different lists based on the group (the pipeline will do nothing if the alarm doesn't have a group):

I hope this gives you a good idea of how to use associated data on alarms. They are extremely powerful and they add a lot of flexibility to the alarming system.

Tuesday, January 29, 2013

Dynamically add SQLTags from Ignition Client

I am finding more and more customers that want to create completely templated projects in Ignition where the configuration comes from a SQL database. They usually want to allow the customer to make configuration changes in the client. In order to achieve that, you need a way to dynamically add/edit/delete devices and tags. Ignition doesn't give you functions to do that out of the box.

Now you can! I have written a special module that exposes scripting functions to manipulate database connections, devices, and tags. It is called the "IA Labs Scripting Module." It is a 100% free module that you can download and try out today.

Simply click on the link below to download the module. You can install it in the Ignition Gateway configuration page under Configuration > Modules.

IA Labs Scripting-module_1.1.0.modl

Right now the only documentation for the module is on our online forum located here:

It lists out all of the functions and how to use them. Here is a list of the functions:

Database Functions

Device Functions
system.device.refreshBrowse - Only for AB controllers

Tag Functions

The most popular function is the system.tag.addTag function that adds new SQLTags to Ignition. I get a lot of questions on how to use the function so I thought it would be best to post a few examples.

The first example is very basic, how to add an OPC tag:

system.tag.addTag(parentPath="Folder", name="TagName", tagType="OPC", dataType="Int4", attributes={"OPCServer":"Ignition OPC-UA", "OPCItemPath":"[DeviceName]N7:0"})

Most of the parameters are self explanatory except for the attributes. The attributes is a dictionary of key/value pairs for all the configuration items on a tag. All of the possible attributes are listed in the forum (use the link above). Let's say that I want to add the same tag but store history on the tag as well, the function would look like this:

system.tag.addTag(parentPath="Folder", name="TagName", tagType="OPC", dataType="Int4", attributes={"OPCServer":"Ignition OPC-UA", "OPCItemPath":"[DeviceName]N7:0", "HistoryEnabled":1, "HistoricalScanclass " : "Default"})

You can see two new attributes have been added, HistoryEnabled and HistoricalScanClass. Not only can you add an OPC tag but you can also add a UDT instance:

system.tag.addTag(parentPath="Folder", name="UDTInstanceName", tagType="UDT_INST", attributes={"UDTParentType":"NameOfUDT"}, parameters={"ParamName":"ParamValue"})

That will add a new UDT instance of the UDT named "NameOfUDT". Since UDTs have parameters that must be filled out you can specify another argument called parameters that is also a dictionary of key/value pairs. My UDT had a property called "ParamName" and I set it to the value "ParamValue." 

Next, let's show how to add a tag with an alarm. We just need to specify all of the alarm properties as attributes, like this:

system.tag.addTag(parentPath="", name="N7_0", tagType="OPC", dataType="Int4", attributes={"OPCServer":"Ignition OPC-UA Server", "OPCItemPath":"[MLX]N7:0", "AlertMode":1, "AlertDisplayPath":"Machine A", "AlertNotes":"Notes for the alert", "AlertAckMode":2}, alarmList="Out of Range;Medium;1.0;1.0;0;;;0.0;SEC$")

The only tricky part is coming up with the alarmList attribute value. If you export a tag in the Ignition designer to a CSV file you will see the "AlarmStates" column. You can copy and paste the alarm states value to the addTag function.

Lastly, let's add an expression tag:

system.tag.addTag(parentPath="Folder", name="ExpressionTagName", tagType="DB", dataType="Int4", attributes={"ExpressionType":1, "Expression":"{Path/To/SomeTag} * {Path/To/AnotherTag}"})

The tag is a DB type with two attributes, ExpressionType and Expression. 

There are many other functions you can play around with in this module. I hope to add new functions soon to manipulate OPC servers and transaction groups dynamically. 

If you have any questions or feedback on the module, feel free to post to our online forum.

Wednesday, January 2, 2013

Ignition Designer: Dependencies and Spotlights

Ignition's designer has a lot of features that can make designing new projects easier. Two such features are dependencies and spotlights. We don't really talk about these features in the documentation so I guess you can call them hidden features or gems.

Designer Dependencies

When you are designing screens in Ignition you are constantly linking (binding) properties to tags and other properties on the same window. Each time you bind a property to something else you are setting up a dependency.

Let's look at a small example. Add a tank component and a slider component to a window. Bind the tank's value property to the slider's value property through a "Property" binding. By doing that, the tank is now dependent on the slider. If you try to delete the slider component Ignition won't let you saying "Cannot Delete. Non-Selected component Root Container.Cylindrical Tank depends on selected component Root Container.Slider."

People always ask, "Is there a way I can see who is linked to who?" Yes, the designer has a built-in feature to show all of the dependencies. Just simply click on View > Dependencies > Show All from the file menu in the designer. It will draw arrows from components to other components to illustrate such dependencies.

The direction of the arrow is important. In our example above, the arrow is drawn from the slider to the tank. The arrow shows how data flows. When the slider changes the tank will update. So, if the arrow is going away from the component it means those components are linked to it.

This is an extremely important feature when designing templates or indirect popup windows. Components inside of templates are always linked in some way to the template parameters. That way the template can dynamically change. With indirect popup windows you pass in a value that is used in all of the component property bindings.

In both cases, if you turn on the dependencies you can easily see if one of the components is not linked to the template parameter or the value passed in. If you don't see line drawn to the center something may be wrong.

Pressure label doesn't have an arrow because it is not bound
using the template parameter. In this case that is expected.


Designer Spotlights

Spotlights are another great feature of the designer. Spotlights allow you to see invisible components, components that have bindings, and components that have scripting on the window by painting a box around the component.

There are a lot of situations where you will have invisible components in the designer. It can be challenging to figure out if there are any and where they are. If you turn on the invisible spotlight, it is easy to pinpoint where they are. Click on View > Spotlights > Invisible to turn the spotlight on. The image below shows that there are two components invisible (the purple outline) in this window.

The binding and scripting spotlights are the same idea but show which components have binding or scripting configured.

If you turn on both features dependencies and spotlights, you will get a good idea of how a window is configured.

Friday, December 28, 2012

Ignition as a chat client?

I am always finding new ways to use Ignition. Recently, I have been getting a lot of requests about whether or not you can send chat messages from one client to another. The answer to that question is simple, YES! The answer is almost always yes with Ignition because it is so versatile.

I think the real key to Ignition is the ability to talk to any SQL database. Every company today is likely using SQL databases and may not even know it. At its simplest form, a SQL database is nothing more than a collection of information. What makes a database so powerful is how easy it is to transfer data back and forth from the database. That is where the SQL language comes into play. SQL is easy to learn and provides a ton of power. The language also follows a standard, making it easy to work with different database vendors such as MySQL, Microsoft SQL Server, and Oracle. Ignition was developed from the beginning to work with SQL databases. Ignition naturally connects and runs SQL queries to any database.

To make a chat client, all we need is a little knowledge of the SQL language and a bit of creativity. Ignition is server based which means a every client communicates back to the central Ignition server. The Ignition server communicates with the SQL database. Because of that, every client looks at the same SQL database information. So, if one client adds information to a database then all of the other clients can immediately see the new information. That is perfect for a chat client. To configure a chat client we need to know who is currently logged into each client and add a table in the database that will store the chat messages.

Let's make our chat client so you can send a message to all users or one particular user. Each client that you login to registers a session on the Ignition Gateway (server). We can easily run a simple scripting function, system.util.getSessionInfo(), to list all of the people currently logged into a client. The function returns a dataset of all the current running client sessions. Each row represents a session with the following columns:
  • username (String)
  • project (String)
  • address (String)
  • isDesigner (Boolean)
  • clientId (String)
  • creationTime (Date)

We just need to pick out the rows where the isDesigner flag is false. Here is an example:

sessions = system.util.getSessionInfo()
users = []
for session in sessions:
if session["isDesigner"] == 0:
print users

Now, let's identify all of the pieces of information we need to store in the database when we the send the message:

user = who sent the message
time = date / time the message was sent
recipient = who to send the message to (if any, NULL when to all users)
message = the actual message

In our database, we just need to create a table with the columns above. Every database table must have a primary key. The primary key of a table uniquely identifies each record in the table. I typically choose an "Auto Incrementing" primary key where the database sets the value for me based on a sequence starting at 1. I am going to use MySQL as the example. You can open the MySQL Workbench tool to graphically build the table. At the end of the day, the tool constructs a SQL query to create the table. It looks like this:

CREATE  TABLE `test`.`chatmessages` (
  `username` VARCHAR(45) NOT NULL ,
  `t_stamp` DATETIME NOT NULL ,
  `message` TEXT NOT NULL ,
  `recipient` VARCHAR(45) NULL ,
  `flags` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) );

You can see that the id column is auto incrementing and is the primary key. I also added another column to the table called flags for different types of messages. The recipient column allows NULLs so  you can send the message to all users. You can run the above query in the MySQL Workbench tool to create the table.

Now that the table is built, we can create a screen in Ignition to view and send messages by running SQL queries to the database. There are 4 main types of SQL queries:

SELECT - retrieve data from the database
INSERT - insert new records
UPDATE - update (edit) records
DELETE - delete records

All we care about is the SELECT and INSERT commands. We use the SELECT command to view the chat messages and the INSERT command to send a new chat message.

We have a lot of options for how we want to view the chat messages. We can use a table, comments panel, or list component. All of them have a dataset property that can be bound (linked) to a SQL query that retrieves the messages from the database. The query looks something like this:

SELECT t_stamp, username, message, recipient FROM chatmessages ORDER BY t_stamp ASC

We can use a text field component to send out a new message. We just need to setup a simple script on the text field that inserts the message into the database when the user presses enter. The script looks something like this:

if event.keyCode == event.VK_ENTER:
message = event.source.text

if message == "":
system.gui.messageBox("Please enter in a message")
username =
system.db.runPrepUpdate("INSERT INTO chatmessages (username, t_stamp, message) VALUES (?,CURRENT_TIMESTAMP,?)", [username, message])

Of course, we can add some more features to the script above. 

Really that is it! All we configured is a single database table and a single Ignition screen to make a chat client.

One of my main goals through this blog is to not only guide you through creating these types of applications but to also give you actual projects that you can download and use. I have created an Ignition screen that demonstrates everything we talked about. It uses a MySQL table and has a few more features built-in. Click on the link below to download.

Ignition chat client

It is a .proj backup. You can import it into your project through the Ignition designer by selecting File > Import... from the file menu.

If you type in a message and press enter it will send the message to all users. If you double click on a logged in user to the right, it will send the message directly to that person. In that case, it will put the following into the text box:

/msg admin here is my message

The /msg admin tells the script who to send the private message to. Only users other than yourself will show up in the logged in list to the right.

Feel free to customize it any way you want. Enjoy!