Blog

Using XML Templates in TIA Openness

Using XML Templates in TIA Openness

In my previous post, we looked at how Openness formats the XML files that it uses to create objects in TIA Portal. After reviewing some examples, we concluded that creating code in XML can be very complicated and that it is best to use existing blocks to generate XML using Openness.

On the surface, that doesn’t seem very useful, since writing code requires you to have already written code. However, another way to look at it is that by writing your desired code once, you can use Openness to automatically generate the code as much as needed. By creating XML templates, your Openness application can be used to quickly generate new code from an existing codebase.

What is a Template For?

Let’s take a step back and consider when generating code from a template using Openness is more useful than creating the necessary code yourself in TIA Portal. Imagine you are in charge of maintaining the PLC code for your company’s line of modular conveyor systems. When a customer wants to order a conveyor system, they choose from the dozens of available modules and arrange them in the configuration that suits their needs. Since you’re a smart programmer, you’ve created a set of function blocks that can control each module.

When it comes time to develop the final PLC program, you have to copy in your existing modules, link them to the appropriate inputs and outputs, create the handshakes for passing data between each module, collect each module’s fault codes and send them to the master controller, and the list can go on and on. For large systems, this can become very tedious and time-consuming. However, you can take your modules, export them to XML, and with a little tweaking, allow Openness to automatically insert the relevant data and create the code for you.

Creating a Template

The first step of automating your code generation is to create your template files. We’ll start by using Openness to create the XML file, and then we’ll add some modifications that will allow our Openness application to fill in data for us.

To keep things simple, we’ll assume we have 3 modules that have been packaged into function blocks called fbModule1, fbModule2, and fbModule3. Each module has the same block interface. There’s an input for the motor feedback, an interlock signal from the upstream module, a module ID that the block uses for fault messaging, an output to turn on the motor, and an interlock sent to the downstream module.

fb module example

Since the modules are completely self-contained blocks, in this case, there’s no need to create templates for them. As long as they are part of the final TIA Portal project, we just need a template for creating the block calls and assigning the tags. We’ll also need a template for the instance data blocks, a template for creating the I/O tags, and a template for the global data block that will contain the handshake bits.

Let’s start with the I/O tags. I’ve created a tag table called IO and added one input and one output.

<Document>
<SW.Tags.PlcTagTable ID="0">
    <AttributeList>
      <Name>IO</Name>
    </AttributeList>
    <ObjectList>
      <SW.Tags.PlcTag ID="1" CompositionName="Tags">
        <AttributeList>
          <DataTypeName>Bool</DataTypeName>
          <ExternalAccessible>true</ExternalAccessible>
          <ExternalVisible>true</ExternalVisible>
          <ExternalWritable>true</ExternalWritable>
          <LogicalAddress>%I0.0</LogicalAddress>
          <Name>I_Section1Running</Name>
        </AttributeList>
        <ObjectList>
          <MultilingualText ID="2" CompositionName="Comment">
            <ObjectList>
              <MultilingualTextItem ID="3" CompositionName="Items">
                <AttributeList>
                  <Culture>en-US</Culture>
                  <Text>Section 1 motor running</Text>
                </AttributeList>
              </MultilingualTextItem>
            </ObjectList>
          </MultilingualText>
        </ObjectList>
      </SW.Tags.PlcTag>
	  <SW.Tags.PlcTag ID="10" CompositionName="Tags">
        <AttributeList>
          <DataTypeName>Bool</DataTypeName>
          <ExternalAccessible>true</ExternalAccessible>
          <ExternalVisible>true</ExternalVisible>
          <ExternalWritable>true</ExternalWritable>
          <LogicalAddress>%Q0.0</LogicalAddress>
          <Name>Q_Section1Run</Name>
        </AttributeList>
        <ObjectList>
          <MultilingualText ID="11" CompositionName="Comment">
            <ObjectList>
              <MultilingualTextItem ID="12" CompositionName="Items">
                <AttributeList>
                  <Culture>en-US</Culture>
                  <Text>Run section 1 motor</Text>
                </AttributeList>
              </MultilingualTextItem>
            </ObjectList>
          </MultilingualText>
        </ObjectList>
      </SW.Tags.PlcTag>
	 </Document>

This XML needs to be broken into 2 parts: the XML for the tag table, where we will add tags, and the XML for a tag, which we will fill in with the relevant data. To create these templates, delete the data that we will replicate, and add a placeholder that your application will use to perform the replacement. I like the syntax {Placeholder_Name}.

Here are my templates for the tag table and tags.

Tag Table Template:

<Document>
  <SW.Tags.PlcTagTable ID="0">
    <AttributeList>
      <Name>IO</Name>
    </AttributeList>
    <ObjectList>
		{Tags}
	</ObjectList>
   </SW.Tags.PlcTagTable>
 </Document>
 

Tag Template:

<SW.Tags.PlcTag ID="{Iterate}" CompositionName="Tags">
<AttributeList>
       <DataTypeName>Bool</DataTypeName>
       <ExternalAccessible>true</ExternalAccessible>
       <ExternalVisible>true</ExternalVisible>
       <ExternalWritable>true</ExternalWritable>
	   <LogicalAddress>{Address}</LogicalAddress>
	   <Name>{TagName}</Name>
</AttributeList>
		<ObjectList>
          <MultilingualText ID="{Iterate}" CompositionName="Comment">
            <ObjectList>
              <MultilingualTextItem ID="{Iterate}" CompositionName="Items">
                <AttributeList>
                  <Culture>en-US</Culture>
                  <Text>{Tag Comment}</Text>
                </AttributeList>
              </MultilingualTextItem>
            </ObjectList>
          </MultilingualText>
        </ObjectList>
</SW.Tags.PlcTag>

In the Openness application, for each module, we would use the tag template to fill in {Address}, {TagName}, {TagComment}, and {Iterate}. These tags would then be grouped together and inserted into the tag template at the {Tags} placeholder. How you get this data is up to you. An easy solution is to read in a user-created Excel table, such as the following.

Excel data

The application would create the input and output tags for each module and could be set up to give specific tag names depending on the section and module. For example, the tags for Section 1 could be named “I_Section1MotorRunning” and “Q_RunSection1Motor”. The final XML file would contain all the I/O tags, ready to be imported into the project.

A similar process could be done for the global data block. The format is similar to that of the tag table. The resulting templates would look something like the code below.

<Document>
  <SW.Blocks.GlobalDB ID="0">
    <AttributeList>
      <AutoNumber>true</AutoNumber>
      <HeaderAuthor />
      <HeaderFamily />
      <HeaderName />
      <HeaderVersion>0.1</HeaderVersion>
      <Interface>
		 <Sections xmlns="http://www.siemens.com/automation/Openness/SW/Interface/v2">
  			<Section Name="Static">
			{Tags}
			</Section>
		</Sections>
	 <IsOnlyStoredInLoadMemory>false</IsOnlyStoredInLoadMemory>
      <IsRetainMemResEnabled>false</IsRetainMemResEnabled>
      <IsWriteProtectedInAS>false</IsWriteProtectedInAS>
      <MemoryLayout>Optimized</MemoryLayout>
      <MemoryReserve>100</MemoryReserve>
      <Name>dbHandshakes</Name>
      <Number>6</Number>
      <ProgrammingLanguage>DB</ProgrammingLanguage>
    </AttributeList>
    <ObjectList>
      <MultilingualText ID="1" CompositionName="Comment">
        <ObjectList>
          <MultilingualTextItem ID="2" CompositionName="Items">
            <AttributeList>
              <Culture>en-US</Culture>
              <Text />
            </AttributeList>
          </MultilingualTextItem>
        </ObjectList>
      </MultilingualText>
      <MultilingualText ID="3" CompositionName="Title">
        <ObjectList>
          <MultilingualTextItem ID="4" CompositionName="Items">
            <AttributeList>
              <Culture>en-US</Culture>
              <Text />
            </AttributeList>
          </MultilingualTextItem>
        </ObjectList>
      </MultilingualText>
    </ObjectList>
  </SW.Blocks.GlobalDB>
</Document>

The data block tags would use the tag template, and the {TagName} and {TagComment} fields would be filled in. The created tags would be used in the {Tags} placeholder in the data block template. Creating the instance data block template is very simple since each data block can be in a separate XML file. All that needs to be filled in is the function block name, the data block name, and the datablock number.

This assumes that each module has the same block interface. If any module was different, it would need its own instance data block template. Note that I’ve hidden the Interface section in the below template to make it easier to read.

<Document>
<SW.Blocks.InstanceDB ID="0">
    <AttributeList>
      <AutoNumber>false</AutoNumber>
      <HeaderAuthor />
      <HeaderFamily />
      <HeaderName />
      <HeaderVersion>0.1</HeaderVersion>
	  <InstanceOfName>{FunctionBlockName}</InstanceOfName>
      <Interface>
	<MemoryLayout>Optimized</MemoryLayout>
      <Name>{BlockName}</Name>
      <Number>{BlockNumber}</Number>
      <ProgrammingLanguage>DB</ProgrammingLanguage>
    </AttributeList>
	<ObjectList>
      <MultilingualText ID="1" CompositionName="Comment">
        <ObjectList>
          <MultilingualTextItem ID="2" CompositionName="Items">
            <AttributeList>
              <Culture>en-US</Culture>
              <Text />
            </AttributeList>
          </MultilingualTextItem>
        </ObjectList>
      </MultilingualText>
      <MultilingualText ID="3" CompositionName="Title">
        <ObjectList>
          <MultilingualTextItem ID="4" CompositionName="Items">
            <AttributeList>
              <Culture>en-US</Culture>
              <Text />
            </AttributeList>
          </MultilingualTextItem>
        </ObjectList>
      </MultilingualText>
    </ObjectList>
  </SW.Blocks.InstanceDB>
</Document>

Finally, we can create our template for the block calls. In this case, I’ll call all the modules from our PLC’s OB1. We will need 2 templates again; one for the network, and one for the constant that will be linked to the iInModuleID parameter. I’ve hidden a few sections for readability.

<Member Name="{Name}" Datatype="Int" Accessibility="Public">
	<Comment>
	  <MultiLanguageText Lang="en-US">{Comment}</MultiLanguageText>
	</Comment>
	  <StartValue>{Value}</StartValue>
</Member>
<SW.Blocks.CompileUnit ID="{Iterate}" CompositionName="CompileUnits">
      <AttributeList>
        <NetworkSource>
		<FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v1">
  <Parts>
    <Access Scope="GlobalVariable" UId="{Iterate}">
      <Symbol>
        <Component Name="{MotorRunningTag}" />
      </Symbol>
    </Access>
    <Access Scope="GlobalVariable" UId="{Iterate}">
      <Symbol>
        <Component Name="dbHandshakes" />
        <Component Name="{InterlockInputTag}" />
      </Symbol>
    </Access>
    <Access Scope="LocalConstant" UId="{Iterate}">
      <Constant Name="{ModuleIDTag}" />
    </Access>
    <Access Scope="GlobalVariable" UId="{Iterate}">
      <Symbol>
        <Component Name="{MotorRunTag}" />
      </Symbol>
    </Access>
    <Access Scope="GlobalVariable" UId="{Iterate}">
      <Symbol>
        <Component Name="dbHandshakes" />
        <Component Name="bSection1OK" />
      </Symbol>
    </Access>
    <Call UId="26">
      <CallInfo Name="fbModule3" BlockType="FB">
        <Instance UId="27" Scope="GlobalVariable">
          <Component Name="dbIns_Section1" />
        </Instance>
        <Parameter Name="bInMotorRunning" Section="Input" Type="Bool" />
        <Parameter Name="bInOkToRun" Section="Input" Type="Bool" />
        <Parameter Name="iInModuleID" Section="Input" Type="Int" />
        <Parameter Name="bOutRunMotor" Section="Output" Type="Bool" />
        <Parameter Name="bOutModuleOK" Section="Output" Type="Bool" />
      </CallInfo>
    </Call>
  </Parts>
  <Wires>
  </FigNet>
  </NetworkSource>
  <ProgrammingLanguage>LAD</ProgrammingLanguage>
 </AttributeList>
	<ObjectList>
</SW.Blocks.CompileUnit>

The application will use the {Name}, {Comment}, and {Value} placeholders to create the constant tags. The block calls will require filling in all the input and output parameters, as well as the function block and instance data block. Once all the code elements have been generated from templates, they can be imported into the project. If all goes well, your project is now ready to run the system!

While setting up these templates is a difficult and time-consuming process, it can have a lot of benefits in the long run. Once the templates are created, any future projects can be created with a few lines in Excel and the click of a button. It also makes code updates easy. Any changes to a template will be reflected in all projects that are then generated from the template.

By letting Openness handle the low-level, repetitive details of your code, you can quickly layout a project architecture that makes use of clean, reusable code.

Learn more about our PLC Programming and TIA Development Services. Contact us for more information.

Comments

There are currently no comments, be the first to post one.

Post a comment

Name (required)

Email (required)

CAPTCHA image
Enter the code shown above:

Related Blog Posts