How do you simulate tags for testing?

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.

Thank you!

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'

            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
    return obj
1 Like

@nimrodh thanks. I’m playing around with a 7.8 copy i have access to. any tips for that?

@pturmel would it work with rslogix500 as well? or only 5000?

5000 only.

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

1 Like