Tools

XML Sort

TwinCAT saves its project files somewhat arbitrarily, the order of elements is changed seemingly at random. Use this XML sorter before committing your changes to fix the XML layout and keep your Git history more clean.

Usage

Call with python -m tctools.xml_sort or tc_xml_sort.

usage: tc_xml_sort [-h] [--version] [--dry]
                   [--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--check]
                   [-r] [--filter FILTER [FILTER ...]]
                   [--skip-nodes SKIP_NODES [SKIP_NODES ...]]
                   target [target ...]

Positional Arguments

target

File(s) or folder(s) to target

Named Arguments

--version, -V

show program’s version number and exit

--dry

Do not modify files on disk, only report changes to be made

Default: False

--log-level, -l

Possible choices: DEBUG, INFO, WARNING, ERROR, CRITICAL

Set log level to change verbosity

Default: 'INFO'

--check

Do not modify files on disk, but give a non-zero exit code if there would be changes

Default: False

-r, --recursive

Also target folder (and their files) inside a target folder

Default: False

--filter

Target files only with these patterns

Default: [’*.tsproj’, ‘*.xti’, ‘*.plcproj’]

--skip-nodes, -n

Do not touch the attributes and sub-nodes of nodes with these names

Default: ['Device', 'DataType', 'DeploymentEvents']

Example: tc_xml_sort -r ./MyTwinCATProject

Notes

  • Nodes with the attribute xml:space="preserve" are not touched

Differences with Ruud’s XmlSorter

The precursor of this script is the XmlSorter made by Ruud, written in C#: https://github.com/DEMCON/XmlSorter

There are a couple of difference between this sorter and Ruud’s:

  • The root attribute xmlns:xsi cannot be sorted

    • This is because lxml does not show it as an attribute.

  • This sorter will prefer self-closing tags where content is emtpy, instead of leaving them as they were.

    • This is a consequence of lxml, it cannot identify self-closing tags upon reading.

    • The self-closing tags also do not have a trailing space before the final "/>".

  • Unicode characters are written as #...; instead of literals.

    • Something lxml just seems to do.

None of these appear problematic for TwinCAT. Projects can be opened and built again as expected, and when saved again the file will be as TwinCAT likes it.

Auto Formatter

Use this to make consistent use of whitespace. Visual Studio with PLC doesn’t do a lot of the things that other IDEs do, like removing trailing whitespace and making consistent usage of spaces / tabs. This tool is meant to supplement this.

Specify your preferences with an .editorconfig file, as you would for other projects. An example:

[*.TcPOU]
indent_style = space
indent_size = 4

Usage

Call with python -m tctools.format or tc_format.

usage: __main__.py [-h] [--version] [--dry]
                   [--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--check]
                   [-r] [--filter FILTER [FILTER ...]]
                   target [target ...]

Positional Arguments

target

File(s) or folder(s) to target

Named Arguments

--version, -V

show program’s version number and exit

--dry

Do not modify files on disk, only report changes to be made

Default: False

--log-level, -l

Possible choices: DEBUG, INFO, WARNING, ERROR, CRITICAL

Set log level to change verbosity

Default: 'INFO'

--check

Do not modify files on disk, but give a non-zero exit code if there would be changes

Default: False

-r, --recursive

Also target folder (and their files) inside a target folder

Default: False

--filter

Target files only with these patterns

Default: [’*.tsproj’, ‘*.xti’, ‘*.plcproj’]

Example: tc_format -r ./MyTwinCATProject

Valid options

The following universal .editorconfig fields are considered:

  • indent_style

    • If style is set to space, any tab character will be replaced by tab_width number of spaces

    • If style is set to tab, any tab_width-number of spaces will be replaced by a tab

  • trim_trailing_whitespace

    • If true, whitespace at the end of lines is removed

  • insert_final_newline

    • If true, every code block must end with a newline

And The following unofficial (custom) .editorconfig fields are used:

  • twincat_align_variables

    • If true, variables in declarations are aligned together

  • twincat_parentheses_conditionals

    • If true, parentheses are enforced inside if-statements (IF (condition = 1) THEN...)

    • If false, parentheses inside if-statements are removed (IF condition = 1 THEN...)

When a config property is not set, the formatter will typically take no action. For example, not specifying indent_style (or using unset) will result in no whitespace conversions at all.

Git Info

Use to insert Git version into source file based on a template, to make it available for compilation.

Create a template file (e.g. .TcGVL.template), with {{...}} tags as placeholders for the version info. Then run the info tool (preferably as part of your build) to have it create a new file next to it.

Usage

Call with python -m tctools.git_info or tc_git_info.

usage: __main__.py [-h] [--version] [--dry]
                   [--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
                   [--output OUTPUT] [--repo REPO]
                   [--tolerate-dirty TOLERATE_DIRTY]
                   template

Positional Arguments

template

Template file to be used for newly created file

Named Arguments

--version, -V

show program’s version number and exit

--dry

Do not modify files on disk, only report changes to be made

Default: False

--log-level, -l

Possible choices: DEBUG, INFO, WARNING, ERROR, CRITICAL

Set log level to change verbosity

Default: 'INFO'

--output

File path for the new output file (default: template file with the last extension stripped)

--repo

Path to use for the Git repository (default: use the first repository up from the template file)

--tolerate-dirty, -t

Paths to files that are allowed to be modified without showing the ‘dirty’ flag

Default: []

Example: tc_git_info Version.TcGVL.template

Placeholders

Placeholder

Description

Example

GIT_HASH

Full hash of the last commit

4cc498b3c37375d8d9138fdab553ced012cafc7a

GIT_HASH_SHORT

8-char hash of the last commit

4cc498b3

GIT_DATE

Datetime of the last commit

17-12-2024 12:47:10

GIT_NOW

The current date and time (not a git command at all)

19-12-2024 16:20:35

GIT_TAG

Most relevant tag (result of git tag)

v1.0.0

GIT_VERSION

Guaranteed 3-digit 1.2.3 like-string, based on GIT_TAG

1.0.0

GIT_BRANCH

Current branch name

master

GIT_DESCRIPTION

Most relevant tag + number of commits since then + last commit (result of git describe –tags –always)

v0.0.1-1-g4cc498b

GIT_DESCRIPTION_DIRTY

Same as GIT_DESCRIPTION, except it also adds the –dirty argument to mark if there were uncommitted changes

v0.0.1-1-g4cc498b-dirty

GIT_DIRTY

1 if there are uncommited chances, otherwise 0

0

When using the --tolerate-dirty flag, the -dirty' state can be repressed. The dirty detection itself is always done by Git directly.

You can also call git commands directly using function placeholders, e.g.:

myVar  : STRING := '{{git describe --tags --abrev=4}}';

Notes

  • Requires Git, likely required to be added to PATH.

Release Maker

Use to produce a release archive of compiled PLC code, optionally together with compiled HMI application.

Usage

Call with python -m tctools.make_release or tc_make_release.

usage: __main__.py [-h] [--version] [--dry]
                   [--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
                   [--destination DESTINATION] [--include-hmi]
                   [--add-file ADD_FILE [ADD_FILE ...]] [--platform PLATFORM]
                   [--check-cpu CHECK_CPU CHECK_CPU]
                   [--check-devices CHECK_DEVICES [CHECK_DEVICES ...]]
                   [--check-version-variable CHECK_VERSION_VARIABLE CHECK_VERSION_VARIABLE]
                   [--check-version-hmi CHECK_VERSION_HMI CHECK_VERSION_HMI]
                   plc-source

Positional Arguments

plc-source

Path where to search for compilation directory of PLC project

Named Arguments

--version, -V

show program’s version number and exit

--dry

Do not modify files on disk, only report changes to be made

Default: False

--log-level, -l

Possible choices: DEBUG, INFO, WARNING, ERROR, CRITICAL

Set log level to change verbosity

Default: 'INFO'

--destination

Folder where release output will be saved (default: ./deploy)

Default: 'deploy'

--include-hmi

Also include HMI compilation in release (default: False)

Default: False

--add-file, -a

Add additional file to release package (<filepath> <relative path inside archive>)

--platform

Target platform for PLC to copy (default: x64)

Default: 'x64'

--check-cpu

Validate the CPU configuration in the compiled project (<number of cores> <number of isolated cores>)

--check-devices

Validate devices, only the listed devices by name may be enabled

--check-version-variable

Validate current version in PLC source code in the given variable (<filename> <variable name>)

--check-version-hmi

Validate current version in HMI source code in the given object (<filename> <object id>)

Patch PLC

Adding existing source files to a PLC project can be done through the TwinCAT shell, but it is clunky, slow and prone to outright fail. This tool can discover existing files under a given folder and assert their presence in a plc project file (.plcproj).

The patched PLc project will not be sorted! And the XML formatting will be significantly altered, because the entire XML is saved again. All XML elements are maintained, new files are foldes are merely inserted at the end of the relevant XML nodes. However, the entire file still needs to be saved again, which inevitably cause visible (but non-funcitonal) changes.

After opening, modifying and then saving the PLC project the next time, the original TwinCAT project file formatting will be restored.

In case you track your project file under version control, it is recommended to sort it with XML Sort after patching, before committing changes.

Note that it’s best to close the TwinCAT project while the tool runs. If you still have the project opened, you will be prompted to reload the solution after activating the window again. For smaller projects this will likely be fine, after reloading the modified project should show as expected. But in the case that many, many files were added, this reload will get stuck. Close and re-opening the solution after patch_plc should be faster and more reliable.

Usage

Call with pyton -m tctools.patch_plc or tc_patch_plc.

usage: tc_patch_plc [-h] [--version] [--dry]
                    [--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
                    [--check] [-r] [--filter FILTER [FILTER ...]]
                    [--ignore IGNORE [IGNORE ...]]
                    project {merge,reset,remove} source [source ...]

Positional Arguments

project

Path to the PLC project (typically ‘.plcproj’)

operation

Possible choices: merge, reset, remove

Type of action to take (see description)

source

File(s) or folder(s) with PLC source to add to the project

Named Arguments

--version, -V

show program’s version number and exit

--dry

Do not modify files on disk, only report changes to be made

Default: False

--log-level, -l

Possible choices: DEBUG, INFO, WARNING, ERROR, CRITICAL

Set log level to change verbosity

Default: 'INFO'

--check

Do not modify files on disk, but give a non-zero exit code if there would be changes

Default: False

-r, --recursive

Also target folder (and their files) inside a target folder

Default: False

--filter

Target files only with these patterns

Default: ['*.TcPOU', '*.TcGVL', '*.TcDUT', '*.TcGTLO', '*.TcIO', '*.TcTLEO', '*.TcTTO']

--ignore

File(s) to ignore on the filesystem (filenames are only matched exactly!)

Default: []

Example: tc_patch_plc ./MyPLC.plcproj -r POUs/Generated/