TwinCAT ADS Docker integration introduced some unexpected challenges during the containerization of a .NET application.
The Initial Setup: TwinCAT ADS Communication Outside Docker
One of the interesting projects I had the opportunity to contribute to was the integration with Beckhoff PLCs in the context of a .NET application.
By using the TwinCAT libraries and, in particular, the TwinCAT ADS communication protocol, I was able to interface with and connect to the final machine.
I carried out the implementation and subsequent tests directly on my local PC, with the TwinCAT ADS components installed. Finally, after configuring the Static Routes, I connected to the target, on which TwinCAT XAR was running with the related PLC project.
Up to this point, I did not encounter any particular issues, since the ADS package on my PC included all the basic services strictly necessary for communication. I therefore only had to configure the routes once, in bidirectional mode, and everything was ready to go.
The Challenge: Containerizing the Application
At the end of the activity, the .NET application was functional and essentially completed. The only remaining step was containerization with Docker and the development of the Continuous Integration pipeline. This is where some issues began to arise, as the container had to be isolated and have a single responsibility, without TwinCAT installations or other third-party elements inside it.
Evaluating ADS TCP Router
After some research, my focus shifted directly to a possible solution which, from what I understood, seemed to solve exactly these types of cases. It is Beckhoff’s specific library: TwinCAT.Ads.TcpRouter.
It is a direct and effective way to replace the installation of the local router as software with a lightweight in-process router within the codebase. The trade-off, as you can imagine, was integrating it into my implementation.
I had not yet made the final decision, as I was still skeptical. I first wanted to carry out a Proof of Concept to validate this solution, observe how many modifications would be required, and above all determine whether I could connect to the target PLC from the container without the “router instance missing” error reported by TcAdsClient().
Proof of Concept: Running ADS Communication Inside Docker
The test was not as complicated as I had initially imagined. By configuring a local AMS NetID to be used within the container’s application context, the TcpRouter had to be started before the ADS connection so that it could be detected.
The router started successfully and, after some attempts and reconfigurations, I was finally able to establish the connection with the target AMS NetID, all within the same local subnet.
Once the feasibility had been demonstrated, I carried out the complete integration within the application.
One of the subsequent checks was the complete deactivation of all active TcSysSrv services and their dependencies on my PC. In this way, I wanted to isolate my tests within the container without conflicts with the host components. The check was completed successfully.
The Hidden Issue: Static Route Authorization
A further test involved changing the static IP address of the host to a new one, without using the local router on my PC to perform the bidirectional authorization. The idea was to see what would happen if the TcpRouter inside the container handled everything for the first time.
The router started correctly, but the connection to the target remained unreachable. At first, I thought it was a matter of network isolation between the host and the container, but that was not the case. In fact, the container was running in bridge mode, and the IP addresses were consistent and correctly mapped with the AMS NetIDs.
The issue was something else: TcpRouter creates a connection that requires an authorized response from the target. Therefore, since the destination machine had not defined in its Static Routes the AMS NetID used in the container, it rejected the connection. The solution was to access the target’s Static Routes and add the mapping between the source IP address (the host’s IP address, which the target sees because of NAT) and the AMS NetID used by TcpRouter.
One piece of advice I would give is not to use the “Broadcast Search” option in the dialog for adding a new route, because it does not automatically update the table. I entered everything manually and only saw the changes after refreshing the route list in the underlying window. In my opinion, the user experience of managing Static Routes in TwinCAT could be improved.
Closing Thoughts
This experience was very positive for me. In the end, it was interesting to see the complete communication flow from the container to the destination, managing to configure everything in an isolated, reproducible, and automated way. Containerizing an application is an additional layer of complexity that can sometimes require more effort than expected.
Official References
The following official Beckhoff resources provide additional information about the technologies discussed in this article:
- Beckhoff Information System: Official technical documentation for TwinCAT and ADS technologies.
- TwinCAT: Product overview and ecosystem documentation.
Thank you for reading.
