Docker PLC Dev Simulation

To my Docker/Kubernetes users out there, I am recently grasping a better understanding of docker-compose and how simple it is to rapidly recreate entire projects locally with multiple gateways, all connected, databases, DNS, AD, etc. However the one item that I am still missing is PLC comms.

I am the least PLC informed Ignition guy you will ever meet, my focus is much more on the app side of things, I can barely spell PLC :wink:. However if I need to spin up rapid quick dev mockups of OPC connections through a container what have you guys used before?

I have essentially glanced at the Microsoft IoT Edge OPC UA PLC image and am thinking that is my best bet, but I am curious on other peoples experiences.

@Kevin.Collins? @PGriffith?

As an update if anyones curious,

I did end up using the Microsoft IoT Edge OPC UA PLC image that I mentioned before, and it works fairly well. For rapid reuse I find it best to provide it a server certificate instead of letting it generate one so that you dont have to go back and re-trust it on your gateway every time you recreate the container from a backup. I also planned on making an adjustment to kcollinsโ€™ ignition dockerfile to automatically put the certificate you give the OPC server into the \Ignition\data\opcua\client\security\pki\trusted\certs folder

I also created a script that will go through any of your tags and accordingly change them to point to the default fake simulated tags

import random

ints = ["ns=2;s=PositiveTrendData", "ns=2;s=NegativeTrendData", "ns=2;s=SpikeData", "ns=2;s=StepUp", "ns=2;s=DipData"]
bools = ["ns=2;s=AlternatingBoolean"]

def updateConfig(udtPath, config):
	for item in config['tags']:
			if str(item["tagType"]) in ["UdtType", "Folder", "UdtInstance"]:
				updateConfig(udtPath + "/" +  str(item["path"]), item)
				itemPath = udtPath + "/" + str(item["path"])
				if str(item["valueSource"]) == "opc":
					dataType = str( + ".dataType").value)
					if dataType in ["Int2", "Int4", "Int6"]:
						demoPath = ints[random.randint(0,len(ints) - 1)]
					elif dataType in ["Boolean"]:
						demoPath = ints[random.randint(0,len(ints) - 1)]
						demoPath = ints[random.randint(0,len(ints) - 1)]
					system.tag.write(itemPath + ".OpcItemPath", demoPath)
					system.tag.write(itemPath + ".OpcServer", "OPC_Demo")
			nil = 1
defaultPath = "[default]_types_"
config = system.tag.getConfiguration(defaultPath, True)[0]
updateConfig(defaultPath, config)

But in the future I think it would be better if I could write a script to not only iterate through all of my OPC tags, but define a json document like the following that will allow me to simulate all of the OPC tags with the same OpcItemPaths that they are in the production environment. If I can determine where tags are stored in the gateway backups, then I could probably create these tags on startup for the OPC server dockerfile and provide it a copy of the gateway backup as a source.

  "Folder": "MyOpcFolder",
  "NodeList": [
      "NodeId": 1023,
      "Name": "ActualSpeed",
      "Description": "Rotational speed"
      "NodeId": "aRMS"
      "NodeId": "1025",
      "Name": "DKW",
      "DataType": "Float",
      "ValueRank": -1,
      "AccessLevel": "CurrentReadOrWrite",
      "Description": "Diagnostic characteristic value"

This is definitely some big-nerd stuff, but I can see it making it much simpler to create a digital twin of your production environment for development purposes.

1 Like