The purpose of this sample code is to showcase how you can very simply access Modbus TCP Programmable Logic Controllers (PLC) from a Python 3 environment and run it on Cisco IOx.
Our aim in this case is simply to trigger a few Modbus coil registers on and off and have some fun doing the visual effect of Kitt in Knight Rider, on a PLC!
Cisco IOx is an execution and hosting environment and in this case we will build an application package for the Cisco IR1101 which is a ARM-based platform. For this reason we will need to include QEMU for cross-compilation on an Intel x86_64 platform. For more information on how to set-up your machine check "Build and Deploy a Docker IOx Package for the IR1101 ARM Architecture".
This sample is using Python 3 along with the pyModbusTCP library which is an extremely compact implementation.
In this code sample we are going to simulate Kitt, with the back and forth light flashing on a PLC. This is not very useful per se, however the purpose is to show how to set this up so you can embark on much more useful use cases.
Although covered in the Cisco IOS-XE documentation, here's a quick run down on how to set up IOx networking. On the IR1101 all IOx containers will be presented as being connected to interface VirtualPortGroup0
. We will use a dynamic DHCP pool to assign containers an IP address and Network Address Translation (NAT) to masquerade the IP traffic.
You need to get access to the router console to enter those commands.
The external, public interface will be the main GigabitEthernet0/0/0
port. It will take it's IP address from an external DHCP server, but of course you can also assign a static IP address. Here is an example on what to do on the router console for DHCP:
interface GigabitEthernet0/0/0
ip address dhcp
ip nat outside
Then configure the VirtualPortGroup0
interface with an IP address and as NAT inside. All containers will appear as being connected to that network:
interface VirtualPortGroup0
ip address 192.168.1.1 255.255.255.0
ip nat inside
For our containers to receive an IP address, we then need to also configure a DHCP pool:
ip dhcp pool iox
network 192.168.1.0 255.255.255.0
default-router 192.168.1.1
dns-server 8.8.8.8
You can also use a static IP address for the container during the activation phase, and skip the DHCP server configuration. This will allow the container to always come back with the same IP address which may be interesting when doing port forwarding. When not doing port forwarding the DHCP method is the most simple one.
Since this container also need access to the external network where our PLC is configured, we also need to configure Network Address Translation (NAT) to "hide" our private IP address as if it was coming from the router's public interface. Here's how to do that:
ip access-list extended 100
10 permit ip 192.168.1.0 0.0.0.255 any
ip nat inside source list 100 interface GigabitEthernet0/0/0 overload
On your build machine start by cloning the repo with:
git clone https://github.com/etychon/iox-ir1101-modbustcp-br-py
In the start.py
main program, change the base address on your Coil address. This information is coming from the Modbus Exchange Table which describes the type, size and address of the registers.
In my case the table was generated by the B&R "Automation Studio" software and the output was like so:
My Digital Output module in slot 5 has a base address of 0x0008 and six output, with one bit per output. We will then configure the register and number of output in the start.py
program by changing to the base address r_addr_hex
to 0x0008 and the maximum register number to 5 (6 registers from 0 to 5, so maximum is 5).
Edit the start.py
script to customise it for your specific PLC configuration:
r_addr_hex = "0008"
max_registers = 5;
As an exercise you can make those parameters configurable through the package_config.ini
configuration file. Currently it is being used only to configure the PLC IP address after the application activation and before execution, so it will be used to configure the target IP address of your PLC.
This process of using a package_config.ini
file to configure parameters is called "bootstrapping" in Cisco IOx allows application parameters to be changed without touching the container itself.
For example in this case the package_config.ini
file contains an IP address that can be changed to match your PLC IP address. You can edit the package_config.ini
directly or through IOx activation:
[PLC]
IP_Address:192.168.2.167
After the IP address has been changed and before going further you can check if this runs properly on your PLC with:
You can now proceed with building your Docker image:
docker build -t iox-ir1101-modbustcp-br-py .
Similarly you can validate that the image runs well as a Docker container with:
docker run iox-ir1101-modbustcp-br-py
Note that when you run the start.py
script it runs with your local x86 Python interpreter, whereas in the Docker container it runs as an emulated ARM Python interpreter. This abstraction is provided in the container by QEMU.
Now is the time to package this image as an IOx application using Cisco ioxclient utility, which takes additional information from the resource descriptor file package.yaml
. This file describes the target architecture, network and resource requirements, description and version.
ioxclient docker package iox-ir1101-modbustcp-br-py . -n iox-ir1101-modbustcp-br-py --use-targz
Your Cisco IOx application is now in the current directory and called iox-ir1101-modbustcp-br-py.tar.gz
.
Log in to the IR1101 Local Manager WebUI by logging in to the main router WebUI, then navigate to Configuration > Services > IOx to log in to the Cisco IOx Local Manager which is using the same credentials as the main router WebUI.
Deploy the application like so:
Then activate and optionally configure IP address like so:
Your application is now running in Cisco IOx and you should see your PLC running Kitt's Knight Rider effect. Here's how it looks like on mine:
If you have questions, concerns, bug reports, etc., please create an issue against this repository.
This section should detail why people should get involved and describe key areas you are currently focusing on; e.g., trying to get feedback on features, fixing certain bugs, building important pieces, etc. Include information on how to setup a development environment if different from general installation instructions.
General instructions on how to contribute should be stated with a link to CONTRIBUTING file.
Copyright (c) 2020 Cisco Systems, Inc. and/or its affiliates
Code Exchange Community
Get help, share code, and collaborate with other developers in the Code Exchange community.View Community