ottowayi / pycomm3

A Python Ethernet/IP library for communicating with Allen-Bradley PLCs.
MIT License
394 stars 88 forks source link

Find datatype of tag within tag list #81

Closed ccscpanici closed 3 years ago

ccscpanici commented 3 years ago

Is there an easy way of finding out the datatype of a known tag using the tag list? For example, can I give the system a tag "Tag1[40].Temp.Scl" and have it spit back "Float"? Or even the tag in the list? I am not opposed to converting it to a "path" either like "Tag1\40\Temp\Scl"...

Is there anything like that I can use?

CJ

ottowayi commented 3 years ago

Right now that is not built-in, but I like the idea of adding a helper method that would return that. That data is available in the tag list, but you would have handle getting it out.

Something like:

typ = plc.tags['Tag1']['internal_tags']['Temp']['internal_tags']['Scl']['data_type']         

Obviously, a method that loops looks thru each sub-element until it gets to the end would be much nicer. I'm been thinking about adding some utility functions to simplify stuff like this and this is a good example of one. If not included in the library, having them in the examples would good too.

ccscpanici commented 3 years ago

Have a look at this? I've tried to format this correctly, but it will not for some reason...so sorry about that.

def get_data_type(plc_name, a_tag): #

get_data_type - inputs the plc variable name and a tag string

#    plc_name = the string form of the controllogix plc variable
#    a_tag = a valid tag string. This supports arrays and sub-arrays.
#
#    example:
#       get_data_type("plc", "System1.Burner.Temperature1") -> "REAL"
#
# tag pattern
#plc.tags['base_tag']['data_type']['internal_tags']['data_type']

# this line splits the tag up using the dot
# as the delimeter
a_tag_array = a_tag.split('.')

# this chunck of code gets rid of any tag
# indexing.
buffer = []
for i in a_tag_array:
    if i.__contains__("[") and i.__contains__("]"):
        buffer.append(i[0:i.find("[")])
    else:
        buffer.append(i)

# this forms the indexing string
s = plc_name + ".tags" 
for i in range(0, len(buffer)):
    if i < len(buffer) - 1:
        s = s + "['" + buffer[i] + "']['data_type']['internal_tags']"
    else:
        s = s + "['" + buffer[i] + "']['data_type']"

try:
    dt_string = eval(s)
except Exception as ex:
    print("ERROR: Could not get data type for tag %s" % a_tag)
    print(ex)
# end try

return dt_string
ccscpanici commented 3 years ago

Even making a more general utility function:

import pycomm3

def get_tag_indexing_string(a_tag):

this line splits the tag up using the dot

# as the delimeter
a_tag_array = a_tag.split('.')

# this chunck of code gets rid of any tag
# indexing.
buffer = []
for i in a_tag_array:
    if i.__contains__("[") and i.__contains__("]"):
        buffer.append(i[0:i.find("[")])
    else:
        buffer.append(i)

# this forms the indexing string
s = "" 
for i in range(0, len(buffer)):
    if i < len(buffer) - 1:
        s = s + "['" + buffer[i] + "']['data_type']['internal_tags']"
    else:
        s = s + "['" + buffer[i] + "']['data_type']"
return s

if name == 'main':

print(get_tag_indexing_string("SequenceData.COND1[0]"))
print(get_tag_indexing_string("PoolArea.System1.Boiler.Temperature1"))
ottowayi commented 3 years ago

I should have looked closer in my initial reply, there is a private method for LogixDriver that would simplify this a lot.

Here is what I would do:

def get_tag_dataype(plc, tag_name):
    base, *attrs = tag_name.split('.')
    definition = plc._get_tag_info(base, attrs)
    if definition['tag_type'] == 'struct':
        return definition['data_type']['name']
    else:
        return definition['data_type']

A couple just general programming tips as well. You should not be calling the dunder methods yourself, like doing i.__contains__("[") can be replaced with "[" in i. And I would definitely shy away from using eval(), there are much better and safer ways to accomplish what you're trying to do. I understand where you're going with it and how that'd be useful, but I would avoid this method as much as possible.

The private method I used above uses a recursive function to step all the way down to the last item in the request and return that attribute/tag's definition, then from that definition we just need to pick out the data type. This method works examples where the final tag is an atomic type (DINT, REAL, etc) or a structure (UDT, AOI, TIMER, etc). I can add a public method to return this data as well, but for now that function should get you want you need.

ccscpanici commented 3 years ago

Good tips, thanks. The code I pasted was just an example to "get the ball rolling". Thanks for your help. I will use the method that you suggested.