Overview:
Rockwell’s does not natively give a method to export controller tags from the PLC and import the tags as alarms to FactoryTalk. This can be problematic as some projects can contain thousands of alarms with mostly repetitive information. This article will serve as a guide on how to automate the creation of trigger tags and alarm messages.
Prerequisites:
- Errors should be mapped into a user-defined variable that contains symbolic and representative names for the error
- Usually, each error data type corresponds with a particular type of device or process. (For example, the same alarm UDT can be used for all analog inputs)
- All error tags should have a prefix to isolate them as error tags
- All errors should be of the Boolean type
- An example of an error-tag would be “ERROR_Valve_01.NoHomeFeedback”
- You will need to install a Python test environment to run the script
- PyCharm
- Spyder – Used in this example
- Visual Studio
Exporting Error Tags from Logix Designer:
Logix Designer does give the option to export controller tags as a comma-separate-value file, but this file will not include the user-defined variable definitions. The best way to create an export of all the error tags with the type definitions is to first open the “Controller Tags” window, expand all error tags, select them, and copy them into a text document.
Steps:
- Open Controller Tags Window
- Copy alarm variables
- Paste into an empty text file
You should now be able to save this text file as something relevant. In this example, the file name will be, “ErrorTags.txt”.
Formatting the data with Python:
FactoryTalk alarms can be imported using a specific XML format. The following script will show how to open the text file, parse the date, create string messages, and write to an XML file.
The function below will perform the following tasks in order:
- Open the text file
- Map the text file to a matrix of data
- Map the first index of the data into an array with only tag names
- Remove the header tags of the UDT instance
def parse_text_to_matrix(file_path):
matrix_temp = [] # initialize matrix
matrix = [] # initialize matrix
with open(file_path, 'r') as file: # Open the file
for line in file:
row = line.strip().split() # Split into array elements
matrix_temp.append(row) # Write to temp matrix
for i in range(len(matrix_temp)): # Isolate relevant data
if '.' in matrix_temp[i][0]: # Remove header tags
matrix.append(matrix_temp[i][0])
return matrix # Output final arrray
Next, we will call the function to format the alarm messages using the tag names. The function will iterate through the alarm messages and format the message. This function block may need to be changed to fit your specific formatting needs.
The code snippet will perform the following transformation:
ERROR_Control_Valve_Tank_1_HPNV01.InvalidFeedback –> Control Valve Tank 1 – HPNV01 – Invalid Feedback
def FormatString(input_TagArray):
message_array = []
for i in range(len(tags_array)):
sMessage = input_TagArray[i].replace('ERROR_', '') # Remove Error_
sMessage = sMessage.replace('_', ' ') # Replace underscores with spaces
sMessage = sMessage.replace('.', ' - ') # Replace periods with dashes
sMessage = re.sub(r'(?<!^)(?=[A-Z])', ' ', sMessage) # Add spaces where there is a case change
sMessage = sMessage.replace(' ', ' ') # Remove double spaces
sMessage = sMessage.replace('V F D', 'VFD') # correct the term VFD
sMessage = sMessage.replace('M F', '- MF.') # correct the term MF
sMessage = sMessage.replace('P T', '- PT.') # Correct the term PT
sMessage = sMessage.replace('H P N V', '- HPNV.') # Correct the term HPNV
sMessage = sMessage.replace('Pump0', '- Pump.0') # Correct the term Pump
message_array.append(sMessage)
return message_array
Next, we will create a function to create our XML file with our trigger tags and our error messages. The header information can be found by exporting an empty alarm configuration from FactoryTalk.
def WriteOutput(tags_array, message_array):
with open('output.xml', 'w') as file:
# Print the header
file.write('\n')
file.write('\n')
file.write(' \n')
# Print Triggers
file.write(' \n')
for i in range(len(tags_array)):
file.write(' \n')
file.write(' \n')
# Print Messages
file.write(' \n')
for i in range(len(tags_array)):
file.write(' \n')
file.write(' \n')
# Closer
file.write(' \n')
file.write('\n')
Finally, we can call our functions together and declare the name of our input file.
file_path = 'ErrorTags.txt' # Input File Name
tags_array = parse_text_to_matrix(file_path) # Create array of tag names
message_array = FormatString(tags_array) # Create array of alarm messages
WriteOutput(tags_array, message_array) # Generate the final XML file
Importing to Factory Talk:
From the script above we should now have a file called, “output.XML” that we can import into FactoryTalk. Below are instructions on how to import this file.
- Open your FactoyTalk ME project
- Right-click on Alarm Setup (ProjectName/ProjectName/ProjectName/Alarms/Alarm Setup)
- Select “Import and Export”
- Complete the import wizard
After completing the setup wizard, Factory Talk will open Notepad and display a message to indicate whether the import was successful or not. See the section labeled, “Troubleshooting common issues” if the import fails.
Troubleshooting Common issues:
Messages do not look as expected
The message text may take a few tries to get right. A common tool is to add a space whenever the case changes in a tag name. (for example, LowLowAlarm à Low Low Alarm) This can be helpful but may also create some problems with project-specific acronyms. The code attached to this article should give a few examples of how to create exceptions to these rules. An example of this can be seen on line 30 where V F D is formatted back into VFD.
Another common issue is creating double spaces while formatting the text. An easy solution for this is to use the replace method as seen in line 29. This line will check every message and replace any double space with a single space.
FactoryTalk failed to import the XML
The formatting of the XML file may vary slightly between FactoryTalk versions and projects. An easy method to ensure that the XML will match is to create a few alarms manually and then export the alarm configuration. With this, you can compare the header information between the file you created and the file that was generated by the software and adjust accordingly.
Problems installing Python:
Although Python can be installed independently directly from the command prompt with the command “python”. It is generally preferred to install an entire Python environment. An environment such as Spyder with Anacona will give you access to features such as:
- a variable explorer
- A kernel interface
- Program error descriptions
Instructions for installing and general use can be found at Spyder | Anaconda.org
Closing:
Although creating and using a tool like this may not be practical for every project, the time invested in troubleshooting the script can be negligible should the amount of needed error messages grow long. The main advantage of using an iterative script such as this rather than creating the alarms manually is the ability to quickly change the verbiage/wording across all alarms to a particular user-defined data type. Traditionally, the end user does not get much input into the alarm text due to the amount of time required to make modifications to large systems. With the use of scripting, we can generate and modify alarm texts with little effort.
Full Code:
# -*- coding: utf-8 -*-
"""
Created on Wed Nov 6 13:41:40 2024
@author: JerimaeWilliams
"""
import re
def parse_text_to_matrix(file_path):
matrix_temp = [] # initialize matrix
matrix = [] # initialize matrix
with open(file_path, 'r') as file: # Open the file
for line in file:
row = line.strip().split() # Split into array elements
matrix_temp.append(row) # Write to temp matrix
for i in range(len(matrix_temp)): # Isolate relevant data
if '.' in matrix_temp[i][0]: # Remove header tags
matrix.append(matrix_temp[i][0])
return matrix # Output final arrray
def FormatString(input_TagArray):
message_array = []
for i in range(len(tags_array)):
sMessage = input_TagArray[i].replace('ERROR_', '') # Remove Error_
sMessage = sMessage.replace('_', ' ') # Replace underscores with spaces
sMessage = sMessage.replace('.', ' - ') # Replace periods with dashes
sMessage = re.sub(r'(?<!^)(?=[A-Z])', ' ', sMessage) # Add spaces where there is a case change
sMessage = sMessage.replace(' ', ' ') # Remove double spaces
sMessage = sMessage.replace('V F D', 'VFD') # correct the term VFD
sMessage = sMessage.replace('M F', '- MF.') # correct the term MF
sMessage = sMessage.replace('P T', '- PT.') # Correct the term PT
sMessage = sMessage.replace('H P N V', '- HPNV.') # Correct the term HPNV
sMessage = sMessage.replace('Pump0', '- Pump.0') # Correct the term Pump
message_array.append(sMessage)
return message_array
def WriteOutput(tags_array, message_array):
with open('output.xml', 'w') as file:
# Print the header
file.write('\n')
file.write('\n')
file.write(' \n')
# Print Triggers
file.write(' \n')
for i in range(len(tags_array)):
file.write(' \n')
file.write(' \n')
# Print Messages
file.write(' \n')
for i in range(len(tags_array)):
file.write(' \n')
file.write(' \n')
# Closer
file.write(' \n')
file.write('\n')
# Call functions
file_path = 'ErrorTags.txt' # Input File Name
tags_array = parse_text_to_matrix(file_path) # Create array of tag names
message_array = FormatString(tags_array) # Create array of alarm messages
WriteOutput(tags_array, message_array) # Generate the final XML file