Reading PLC tags offline

Hi there,

I’ve been wondering this for a while. But is there a way the I can either import the tags offline into Ignition or emulate the PLC so I can read the tags?

Basically I am always given just the PLC file and when it comes time to tag my stuff on Ignition I have to manually type everything in and everything just looks red because it cant find those tags and when I get on site I end up having typos or things not tagged. So I am wondering what other people do on overcoming this issue.

No mention of what PLC type you're using, but you may find this of interest.

1 Like

If I don't have a PLC but I have the project, most of our tags are within UDTs which are already developed, so these are fairly easy to create manually or using a tool that takes in a list of instance names and parameters and creates the udt instances for me.

Without a PLC, in v8, you can convert tags between opc and memory without any information being removed (this wasn't in the case in v7).

I use this script, just copied into the script console, to convert tag json copied into the clipboard, between OPC & memory types. Actual memory tags or any other tags won't be affected, only those that were originally OPC will be affected.

Just comment in/out the bottom two lines, then paste back into the tag browser (need to right click, paste - Ctrl+V doesn't work for tags). It works for tags as well as UDT definitions and their tags.

EDIT 2024-07-01

from java.awt.datatransfer import StringSelection
from java.awt.datatransfer import Clipboard
from java.awt import Toolkit
from java.awt.datatransfer import DataFlavor

### clipboard functions ###
def setup():
    global toolkit
    global clipboard
    toolkit = Toolkit.getDefaultToolkit()
    clipboard = toolkit.getSystemClipboard()
def writeClipboard(text):
    setup()
    clipboard.setContents(StringSelection(text), None)
def readClipboard():
    setup()
    contents = clipboard.getContents(None)
    return contents.getTransferData(DataFlavor.stringFlavor)
### end clipboard functions ###


def convertTagSource(obj, valSource='original', insideUdtInstance=False):
    """
    Converts tag JSON between memory and their original value source.
    This works by looking at the tag's valueSource and identifying property values for other value sources to determine
    what the original value source was.
    Note: UDT instance tags do not have a lot of this information, so some of these tags are unnecessarily converted
    to memory tags such as expression tags. This is unavoidable without reading the tag's config using
    system.tag.getConfiguration. But... lazy...
    """
    this_fn = convertTagSource
    if valSource not in ['original', 'memory']:
        raise ValueError('Invalid `valSource` value passed into function. Valid values are "original" or "memory".')
    # 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], valSource, insideUdtInstance=insideUdtInstance)

    # if the obj is a dict, check if it is a tag and convert it if required
    if isinstance(obj, dict):
        if 'tagType' in obj and obj['tagType'] == 'UdtInstance':
            insideUdtInstance = True

        if valSource == 'original':
            if obj.get('valueSource', None) != 'expr':
                # convert back 'opc' source tags
                if 'opcItemPath' in obj and obj['opcItemPath'] != '':
                    obj["valueSource"] = 'opc'
                    # remove the 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 insideUdtInstance and 'valueSource' in obj:
                obj.pop('valueSource', None)
                obj.pop('value', None)

        # if the val_source arg is memory, then convert opc and reference types to memory
        elif valSource == 'memory':
            if (any(key in obj for key in ['opcItemPath', 'sourceTagPath']) and
                    obj.get('valueSource', None) != 'expr') or \
                    (insideUdtInstance and 'tagType' in obj and obj['tagType'] == 'AtomicTag'):
                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'

                elif dt is None:
                    obj['value'] = 0

        else:
            raise TypeError('Invalid val_source argument value! ({})'.format(valSource))

        for key in obj.keys():
            if isinstance(obj[key], dict):
                obj[key] = this_fn(obj[key], valSource, insideUdtInstance=insideUdtInstance)
            if isinstance(obj[key], list):
                for i in range(len(obj[key])):
                    obj[key][i] = this_fn(obj[key][i], valSource, insideUdtInstance=insideUdtInstance)

    # if it's just a flat type, ignore it
    else:
        pass
    return obj

def convertTagJsonSourceFromClipboard(convertTo):
    """ Converts tag json from """
    # make sure you copy some tag json first to the clipboard!
    tagsJson = readClipboard()
    tags = system.util.jsonDecode(tagsJson)

    if convertTo in ['original', 'memory']:
        for item in tags:
            tags[item] = convertTagSource(tags[item], convertTo)

        tags = system.util.jsonEncode(tags)
        tags = tags.replace(":True", ":true").replace(":False",
                                                      ":false")  # json technically doesn't capitalise first letter of bool vals.. compare tools whinge
        writeClipboard(tags)
    else:
        system.gui.errorBox("You failed at setting the value source correctly... use 'original' or 'memory' idiot", 'Idiot')

convertTagJsonSourceFromClipboard('memory')
#convertTagJsonSourceFromClipboard('original')

For Vision as well, to get rid of the red overlays, you can disable overlays in the designer:
image

3 Likes

Sorry I should have mentioned it is Rockwell L82. I used the module you linked there. And it manage to grab the information but when I go to the OPC Browser on the designer it does not show there. I am using version 7.9.16. Any idea why?

Let’s ping @pturmel on this one.

I’ll need to know what you’ve tried, any errors reported, a look at the resulting XML in my module, and perhaps a copy of the L5X you used. “It does not show there” isn’t really enough information.