-
Notifications
You must be signed in to change notification settings - Fork 3
Template System
Welcome to the PLCnCLI Template System documentation! This template system is a powerful and flexible tool designed to simplify the generation of files based on user-defined templates containing placeholders. These placeholders are automatically evaluated and filled by the PLCnCLI, streamlining the process of creating files with dynamic content.The template system is ideal for use cases such as code generation, configuration file management, and document templating, where the goal is to produce consistently formatted files with varying content. By leveraging the expressive syntax of the PLCnCLI Template System, users can focus on defining the structure and logic of their templates, while the system takes care of the rest.
In the PLCnCLI Template System, placeholders are denoted using the $(path.to.value) syntax. This notation allows the user to specify the path to the desired value in a hierarchical data structure. The path is evaluated by the PLCnCLI, and the corresponding value is inserted into the output file at the position of the placeholder.
For example, consider a template with the following content:
This is $(file.name)!
It was generated in $(file.path).
The generated file could be:
This is Program.hpp!
It was generated in path/to/Programm.cpp.
This documentation will guide you through the process of creating, managing, and using templates in the PLCnCLI Template System. The following sections are included:
Template Syntax: Learn about the syntax and features supported by the PLCnCLI Template System, including conditionals, loops, and more.
Template Management: Discover how to create, edit, and manage your templates.
Data Sources: Understand how to provide data to the PLCnCLI for use in evaluating placeholders.
The PLCnCLI Template System offers a rich syntax that allows you to create dynamic and complex templates with ease. As mentioned earlier, the general format for accessing values within the template is $(path.to.value). This section provides an in-depth look at the available control sequences and their usage.
Control sequences in the template follow the format: &([control-sequence-name]parameters) Content &([end-control-sequence-name]).
This control sequence removes duplicate lines from the content. It has no parameters. The syntax is:
&([no-duplicate-lines])
Content
&([end-no-duplicate-lines])
This control sequence checks if a value exists in the current context. If the value exists, the content before the
&([if-exist]path.to.value)
Content if value exists
$([else])
Content if value does not exist
&([end-if-exist])
Similar to if-exist, this control sequence evaluates a value as a boolean. If the value evaluates to true, the content before the
&([if]path.to.value)
Content if value is true
$([else])
Content if value is false
&([end-if])
The foreach control sequence iterates over a collection, executing the content for each element in the collection within the context of the current element. The syntax is:
&([foreach]element[in]path.to.collection)
Content for each element
&([end-foreach])
Consider the following example to better understand the template syntax:
#include <iostream>
#include "$(file.name).h"
$([no-duplicate-lines])
$([foreach]struct[in]portStructs)
#include "$(struct.file.format.include)"
$([end-foreach])
#include "$(file.format.include)"
$([end-no-duplicate-lines])
&([if-exist]file.namespace)
namespace $(file.namespace) {
&([end-if-exist])
&([foreach]function[in]file.functions)
void $(function.name)() {
&([if]function.hasImplementation)
$(function.implementation)
&([else])
// TODO: Implement $(function.name)
&([end-if])
}
&([end-foreach])
&([if-exist]file.namespace)
} // namespace $(file.namespace)
&([end-if-exist])
In this example, we use various control sequences to generate a C++ source file based on the provided data. The no-duplicate-lines sequence ensures that each included file is unique, while the if-exist, if, and foreach sequences provide conditional generation and iteration over elements in the context. This is only an example. Not all values do exist.
In the PLCnCLI Template System, templates are managed through template description files. These files define the content, documentation, and arguments required to invoke the template using the PLCnCLI. In this section, we will cover the creation and activation of templates.
To create a template, start by creating a template description file in XML format. This file contains information about the template, including its name, version, arguments, and associated files. Here is an example of a template description file:
<?xml version="1.0" encoding="utf-8"?>
<TemplateDescription name="app" isRoot="true" basedOn="template" supportedFirmwareVersions="19.0.0.16199" requiredCliVersion="19.0.0.660" version="1"
xmlns="http://www.phoenixcontact.com/schema/clitemplates" identifier="ProjectSettingsIdentifier"
deployEngine="AppDeployEngine" buildEngine="NoBuildEngine" generateEngine="NoGenerateEngine">
<Arguments>
<Argument name="name" default="$(output.format.directoryName.format.escapeProjectName)">
<ValueRestriction min-length="2" max-length="128" description="This must be a valid C++ type name. It must start with an upper case letter.">
<Pattern>^[a-zA-Z0-9_]*$</Pattern>
</ValueRestriction>
</Argument>
<Argument name="output" default="$([if-specified]name)$(name)$([else])$(active-directory)$([end-if-specified])"/>
</Arguments>
<File name="plcnext.proj" template=".proj"/>
<File name="app_info.json" template="app_info.json" path="content"/>
<Description>Create an app project. It is intended mainly to pack file into an app format.</Description>
<Example>
<Arguments>
<Argument name="name" value="AppName"/>
</Arguments>
<Description>Creates a new app which will generate an "AppName.app" file.</Description>
</Example>
</TemplateDescription>
The full specification for template description files can be found in the attached documentation.
Additionally, all files associated with the template need to be created using the template syntax described in the previous sections.
To activate a template, add it to a templates repository file. This file lists all the template descriptions you want to make available for use with the PLCnCLI. Here's an example of a templates repository file:
<Templates>
<Template path="/path/to/your/template/description/file.xml"/>
</Templates>
To include your template in the repository file, add a new element with the type attribute set to "Template" and the value set to the path of your template description file.
After updating the templates repository file, add it to the PLCnCLI settings using the following command:
plcncli set setting -a TemplateLocations path/to/Templates.xml
This command adds the path to your templates repository file to the PLCnCLI settings, making all the templates defined in the repository available for use.
In addition to standard templates defined with the TemplateDescription
, the PLCnCLI Template System also supports three types of special templates: TypeTemplates, FieldTemplates, and FormatTemplates. These templates allow for further customization and functionality when working with C++ types, fields, and string transformations.
TypeTemplates are recognized as attributes that can be added to a C++ type. They provide additional metadata or functionality associated with the type. Here's an example of a TypeTemplate:
<?xml version="1.0" encoding="utf-8"?>
<TypeTemplates xmlns="http://www.phoenixcontact.com/schema/clitemplates">
<TypeTemplate name="attributes" has-value="true" multiplicity="OneOrMore" split="|" default-value="None">
<ValueRestriction ignore-case="true">
<Enumeration>Input</Enumeration>
<Enumeration>Output</Enumeration>
<Enumeration>ReadOnly</Enumeration>
<Enumeration>Retain</Enumeration>
<Enumeration>Opc</Enumeration>
<Enumeration>Ehmi</Enumeration>
<Enumeration>ProfiCloud</Enumeration>
<Enumeration>Archive</Enumeration>
<Enumeration>None</Enumeration>
<Enumeration>Hidden</Enumeration>
</ValueRestriction>
</TypeTemplate>
</TypeTemplates>
With this TypeTemplate, you can add attributes like //#attributes(Input|Retain)
to a type. These attributes can be accessed in the template using placeholders like $(type.attributes)
.
FieldTemplates are recognized as attributes that can be added to a C++ field. Similar to TypeTemplates, they provide additional metadata or functionality associated with the field. Here's an example of a FieldTemplate:
<FieldTemplates xmlns="http://www.phoenixcontact.com/schema/clitemplates">
<FieldTemplate name="port"/>
<FieldTemplate name="name" has-value="true" default-value="$(fieldName)">
<ValueRestriction min-length="2" max-length="128" description="An IEC-conform name that does not match any IEC elementary data type.">
<Pattern>(?!((?i)^((SAFE)?(BOOL|BYTE|((D|L)?WORD)|(U?(S|D|L)?INT)|(L?(REAL|TIME|T|DATE|D|DATE_AND_TIME|DT|TIME_OF_DAY|TOD))|(W?(STRING|CHAR))|ANALOG))$))(_|==?|-+|\+\+?|##?|:|&|\/|\*)?([0-9a-zA-Z]+(_|<|>|==?|-+|\+\+?|##?|:|&|\/|\*)?)+</Pattern>
</ValueRestriction>
</FieldTemplate>
</FieldTemplates>
FormatTemplates are used to transform strings into different strings based on defined patterns. For example, consider a file path $(output)
with the value "path\to\file\directory\file.cpp". By applying a FormatTemplate, $(output.format.directoryName)
, the result would be "directory".
Here's an example of how a FormatTemplate is defined:
<FormatTemplates xmlns="http://www.phoenixcontact.com/schema/clitemplates">
<FormatTemplate name="directoryName">
<RegexConversion pattern="^.*?[\/\\](?<directoryName>[^\/\\]*)[\/\\]?\s*$" converted="$(directoryName)"/>
</FormatTemplate>
</FormatTemplates>
In this example, the FormatTemplate named "directoryName" uses a regular expression pattern to extract the directory name from the file path. The RegexConversion
element specifies the pattern and the converted
attribute defines how the matched value should be formatted.
In this case, the directoryName
group in the pattern captures the directory name, and the converted
attribute uses $(directoryName)
to represent the extracted directory name.