The information in this post can be used with:
- VisualStudio 2013, 2015, 2017
- TE2000-HMI-Engineering 1.10.*.* (Download)
- Sources of this post: GitHub repository
In case you like to update several remote machines with the same HMI project just implement some utilities to support this scenario. Everything you need is on-board. The source of any example in this post is accessible on GitHub in one of my repositories: “twincathmi / UseCases / MassDeployment”.
The solution “TcHmiProject1” contains two projects: (a) a simple HMI project (version 1.10) and (b) a tool to deploy the HMI project to several remote machines.

As stated in a previous post, the MsBuild task “Publish” calls the “Clean” and “Build” first. This will result in a waste of execution time, because during a mass deployment we do not really need several builds for the same project, i.e. a deployment to five remote server just need a single build of the same HMI project.
To reduce the number of “Clean” and “Build” we create a new MsBuild-target, named “PublishWithoutBuild”, see Beckhoff.TwinCAT.HMI.MassDeployment.targets. This MsBuild-target file should be imported into our HMI project. “Import” is the key MsBuild-feature for this.
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="12.0" DefaultTargets="Clean;Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> [..] <PropertyGroup> <TwinCATHmi_Tasks>$(TcHmiDirectory)\MSBuild\Beckhoff.TwinCAT.HMI.tasks</TwinCATHmi_Tasks> <TwinCATHmi_Targets>$(TcHmiDirectory)\MSBuild\Beckhoff.TwinCAT.HMI.targets</TwinCATHmi_Targets> <TwinCATHmiMassDeployment_Targets>..\..\Beckhoff.TwinCAT.HMI.MassDeployment.targets</TwinCATHmiMassDeployment_Targets> </PropertyGroup> <Import Project="$(TwinCATHmi_Tasks)" /> <Import Project="$(TwinCATHmi_Targets)" /> <Import Project="$(TwinCATHmiMassDeployment_Targets)" /> [..]
The target “PublishWithoutBuild” is reduced to a minimum of tasks:
<Target Name="PublishWithoutBuild"> <PropertyGroup> <OutputPath Condition="'$(OutputPath)' == ''">bin\</OutputPath> </PropertyGroup> <TcHmiMSBuild.Publish.TcHmiPublish TaskAction="PublishFiles" ProjectDirectory="$(TcHmi_ProjectDirectory)" ProfilesFilename="$(TcHmi_ProfilesFilename)" ProfileName="$(TcHmi_ProfileName)" OutputPath="$(OutputPath)" SuppressTaskMessages="False"> <Output TaskParameter="Result" PropertyName="TaskResult" /> </TcHmiMSBuild.Publish.TcHmiPublish> <TcHmiMSBuild.Publish.TcHmiPublish TaskAction="PublishExtensions" ProfilesFilename="$(TcHmi_ProfilesFilename)" ProfileName="$(TcHmi_ProfileName)" LocalServer="$(TcHmi_LocalServer)" ExtensionInformation="@(TcHmiReference)"> <Output TaskParameter="Result" PropertyName="TaskResult"/> </TcHmiMSBuild.Publish.TcHmiPublish> <TcHmiMSBuild.Publish.TcHmiPublish TaskAction="PublishConfiguration" ProjectDirectory="$(TcHmi_ProjectDirectory)" ProfilesFilename="$(TcHmi_ProfilesFilename)" ProfileName="$(TcHmi_ProfileName)" LocalServer="$(TcHmi_LocalServer)" ProjectFiles="@(Folder);@(Content)" ExtensionInformation="@(TcHmiReference)"> <Output TaskParameter="Result" PropertyName="TaskResult"/> </TcHmiMSBuild.Publish.TcHmiPublish> <OnError ExecuteTargets="PublishError"/> </Target>
Publish to several remote TwinCAT HMI server programmatically
As stated above we will use our new MsBuild-target “PublishWithoutBuild”. In this post we will call the relevant MsBuild-steps programmatically with C#. The full implementation is provided in MultipleRemoteDeployment/Program.cs.
Extend the dictionary TargetRemoteServer with your remote server addresses:
public static Dictionary<string, int> TargetRemoteServer = new Dictionary<string, int> { { "127.0.0.1", 1010 }, { "172.17.62.179", 1010 } };
At the beginning we will build the HMI-project once. During publish the result of this build is used. The engineering TwinCAT HMI server for the related HMI-project must run, because the local server configuration is queried and transfered to the remote servers.
The following code shows how a build with MsBuild can be triggered programmatically (here the “Build”-target is executed):
var propsBuild = MsBuildInfo.Instance(); propsBuild.ProjectDirectory = Path.GetDirectoryName(PathToHmiProject); var propsDict = propsBuild.GetProperties(); var hmiBuildRequest = new BuildRequestData(PathToHmiProject, propsDict, null, new string[] { "Build" // the HMI-project is built }, null); var pc = new ProjectCollection(); var bp = new BuildParameters(pc) { Loggers = new[] { new TargetLogger() } }; var hmiBuildResult = BuildManager.DefaultBuildManager.Build(bp, hmiBuildRequest); Console.WriteLine("Result: {0}", hmiBuildResult.OverallResult);
Any publish/deployment to a remote machine requires an individual publish profile, i.e. because during publish the target/remote ip adress and port can vary. Here, the key-value-entries of TargetRemoteServer are used. During the iteration the publish profile is modified with the ip/port of the current iteration and “PublishWithoutBuild” is called.
foreach (var it in TargetRemoteServer) { jsonResponse[0].tcHmiServerHost = it.Key; jsonResponse[0].tcHmiServerPort = it.Value; #region msbuild magic for publish var props = MsBuildInfo.Instance(); props.ServerAddress = it.Key; props.ServerPort = it.Value; props.ProfileName = "BasePublishProfile"; props.ProfilePath = PathToHmiPublishProfile; props.LocalServer = "ws://127.0.0.1:3000"; props.ProjectDirectory = Path.GetDirectoryName(PathToHmiProject); props.OutputPath = Path.Combine(props.ProjectDirectory, @"bin\"); var buildRequest = new BuildRequestData(PathToHmiProject, props.GetProperties(), null, new string[] { "PublishWithoutBuild" }, null); var buildResult = BuildManager.DefaultBuildManager.Build(bp, buildRequest); Console.WriteLine("Result: {0}", buildResult.OverallResult); #endregion }
The execution with LoggerVerbosity.Minimal looks like this:

Be First to Comment