I’d like to do some testing for for a project/upgrade practice to help hone my skills .
I’ll probably use a VM (idk why Docker scares me)
But not sure how I would simulate tags to verify everything. Or do I even need to? Nothing has to work I just don’t want any overlays during testing and allowing for me to click and play around without any worry of affecting production?
Any tips or ideas would be appreciated. I don’t get to much ignition stuff at work. But I’m trying to keep progressing.
The simplest thing is to just use memory tags. You can even get fancy and write timer events to pretend to be PLC code and drive selected memory tags as if they were live.
If by chance you have an existing AB Logix example you want to play with, you could run my Ethernet/IP module in trial mode to pretend to have the same tags. See this:
If you happen to use Beckhoff PLCs, it’s easy to setup TwinCAT to run and simulate the PLC on your computer. We build simulation logic into our PLC code so we can switch between simulation mode and I/O mode anytime, though this is mostly used for pre-commissioning code testing.
To click through your HMI without a PLC (or simulated PLC), I believe @nminchin mentioned using a script to convert tags from OPC to memory tags (as @pturmel notes above) and vice-versa, which would be a slick way to go back and forth.
This is the function that I use to convert tags to memory and back again. It only handles opc and reference tags at the moment and writes default values to the memory tags. “obj” is your tag json converted to python objects e.g. with json.loads(json_str)
val_source is either “original” or “memory”.
This function only works for v8.
def convert_tag_source(obj, val_source='original'):
this_fn = convert_tag_source
if val_source not in ['original', 'memory']:
return -1
# if the obj is a list, then run through the list and convert any tags contained
if isinstance(obj, list):
for i in range(len(obj)):
obj[i] = this_fn(obj[i], val_source)
# if the obj is a dict, check if it is a tag and convert it if required
if isinstance(obj, dict):
if val_source == 'original':
# convert back 'opc' source tags
if 'opcItemPath' in obj:
obj["valueSource"] = 'opc'
# remove any overridden value from using a memory source
obj.pop('value', None)
# convert back 'reference' source tags
elif 'sourceTagPath' in obj:
obj["valueSource"] = 'reference'
# remove the overridden 'value' from using a memory source
obj.pop('value', None)
# if the val_source arg is memory, then convert opc and reference types to memory
elif val_source == 'memory':
if any(key in obj for key in ['opcItemPath', 'sourceTagPath']):
obj["valueSource"] = 'memory'
dt = obj.get('dataType', None)
# set default values for the memory tags, otherwise they're all set to None which is annoying
if dt in ['Boolean']:
obj['value'] = False
elif dt in ['Float4', 'Int4', 'Float8']:
obj['value'] = 0
elif dt in ['String']:
obj['value'] = 'Dev Test'
else:
raise TypeError('Invalid val_source argument value! ({})'.format(val_source))
for key in obj.keys():
if isinstance(obj[key], dict):
obj[key] = this_fn(obj[key], val_source)
if isinstance(obj[key], list):
for i in range(len(obj[key])):
obj[key][i] = this_fn(obj[key][i], val_source)
# if its just a flat type, ignore it
else:
pass
return obj
V7 isn’t as nice as you can’t reverse the conversion as configuration for different tag types is lost when you convert to other types - only config for the current tag type is held onto, whereas in v8, all changed config is kept regardless if the config doesn’t relate to the given tag’s current config. However it’s fairly simple to convert tags to memory in v7, just export tags to XML and then search for and replace the various sources. Best to export a memory tag to see its config