Scripting: "Can't Convert 'x' to double"

Greetings,

I am working on a project that displays how long until a PM needs to be performed on a robot. The PLC has an "Hours" and "Minutes" that it receives from each robot. This has worked fine to simply display the amount of time left in "LED Displays", but I am trying to take it up a notch and write script that will look at the amount of time left on all of the robots in a zone, decide what robot has the least amount of time left, and display it in LED Displays.

I am very new to python and have limited programming knowledge, but think this should be pretty simple to do.

The problem I am having is any time the code executes, I get an error:

Traceback (most recent call last):
File "event:propertyChange", line 7, in
TypeError: can't convert '[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST30LR1_Hours_Left' to double

at org.python.core.Py.TypeError(Py.java:235)
at org.python.core.Py.tojava(Py.java:515)
at org.python.core.PyBeanProperty.doset(PyBeanProperty.java:63)
at org.python.core.PyObject.set(PyObject.java:3678)
at org.python.core.PyObject.object___setattr
_(PyObject.java:3742)
at org.python.core.PyObject.object___setattr__(PyObject.java:3732)
at org.python.core.PyObject$object___setattr___exposer.call(Unknown Source)
at org.python.core.PyObjectDerived.setattr(PyObjectDerived.java:990)
at com.inductiveautomation.factorypmi.application.script.PyComponentWrapper.setattr(PyComponentWrapper.java:169)
at org.python.pycode._pyx171.f$0(event:propertyChange:20)
at org.python.pycode._pyx171.call_function(event:propertyChange)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyCode.call(PyCode.java:18)
at org.python.core.Py.runCode(Py.java:1275)
at com.inductiveautomation.ignition.common.script.ScriptManager.runCode(ScriptManager.java:657)
at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.runActions(ActionAdapter.java:183)
at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.access$000(ActionAdapter.java:42)
at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter$ActionRunner.run(ActionAdapter.java:299)
at java.awt.event.InvocationEvent.dispatch(Unknown Source)
at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.awt.EventQueue.access$500(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.awt.EventQueue$3.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

Ignition v7.9.3 (b2017060210)
Java: Oracle Corporation 1.8.0_131

There my be logical errors in my script, but I can't really troubleshoot until I get past this error. The tags that I am trying to display in the LED Displays are Integers and I don't have any issues directly binding the tags to the displays, but it seems to not like them being integers when setting the value through the script.

Here is the script:

st30lr1Ttime = ("[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST30LR1_Hours_Left"*60)+"[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST30LR1_Minutes_Left"
st30lr2Ttime = ("[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST30LR2_Hours_Left"*60)+"[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST30LR2_Minutes_Left"
st40lr1Ttime = ("[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST40LR1_Hours_Left"*60)+"[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST40LR1_Minutes_Left"
st60lr1Ttime = ("[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST60LR1_Hours_Left"*60)+"[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST60LR1_Minutes_Left"

if st30lr1Ttime < st30lr2Ttime and st40lr1Ttime and st60lr1Ttime:
event.source.getComponent('TipChange_Hr').value="[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST30LR1_Hours_Left"
event.source.getComponent('TipChange_Min').value="[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST30LR1_Minutes_Left"
elif st30lr2Ttime < st30lr1Ttime and st40lr1Ttime and st60lr1Ttime:
event.source.getComponent('TipChange_Hr').value="[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST30LR2_Hours_Left"
event.source.getComponent('TipChange_Min').value="[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST30LR2_Minutes_Left"
elif st40lr1Ttime < st30lr2Ttime and st30lr1Ttime and st60lr1Ttime:
event.source.getComponent('TipChange_Hr').value="[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST40LR1_Hours_Left"
event.source.getComponent('TipChange_Min').value="[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST40LR1_Minutes_Left"
elif st60lr1Ttime < st30lr2Ttime and st40lr1Ttime and st30lr1Ttime:
event.source.getComponent('TipChange_Hr').value="[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST60LR1_Hours_Left"
event.source.getComponent('TipChange_Min').value="[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/ST60LR1_Minutes_Left"
else:
event.source.getComponent('TipChange_Group').getComponent('TipChange_Hr').value=00
event.source.getComponent('TipChange_Group').getComponent('TipChange_Min').value=00

What would be the best way to go about this?

Thanks in advance,
Steven

You are trying to assign a string to a property that requires a floating point value. The string is the tag path for the values you want. You need to pass the tag path to one of the system.tag.read\*() functions that will get the qualified values you need. For efficiency, you probably want to read all of them at once, using system.tag.readAll().

3 Likes

Hi Steven,

Expanding on what Phil stated, the below (completely untested) example creates a list of tags to read using system.tag.readAll(). This returns a list of “qualified tag values”. This just means that you get the value, quality, and last-changed time of the tag. This also means you need to grab just the values later in your script.

The loop takes all the values and makes another list with the total minute values. If you want to explain deeper what xrange does, I’ll be happy to.

After we make our list with the individual robot minutes, it’s a snap to find the minimum value. A little bit o’ math to get our hour / minutes values back out, and we’re done. At least hopefully. Like I said, it’s completely untested. :slight_smile:

# Set common tag path
cellPath = "[default]STOLNAI_Tags/D2LC_H-Pillar_Main_Tags/"

# Create tag list to read
tagList=[cellPath + "ST30LR1_Hours_Left", cellPath + "ST30LR1_Minutes_Left", cellPath + "ST30LR2_Hours_Left", cellPath + "ST30LR2_Minutes_Left", cellPath + "ST40LR1_Hours_Left", cellPath + "ST40LR1_Minutes_Left", cellPath + "ST60LR1_Hours_Left",cellPath+"ST60LR1_Minutes_Left"]

#read tags
tagValues = system.tag.readAll(tagList)

minuteList =[]

# Create list of minute values.
# This loops though the range with a step value of 2
for i in xrange(0, len(tagValues), 2):
  minuteList.append(tagValues[i].value * 60 + tagValues[i+1].value)

# Get minimum value from list
minimumValue = min(minuteList)

# get hours and minutes from minimumValue
# // is an integer divide, % is modulo
hourValue = minimumValue // 60
minuteValue = minimumValue % 60

# Write values to component
event.source.getComponent('TipChange_Group').getComponent('TipChange_Hr').value = hourValue
event.source.getComponent('TipChange_Group').getComponent('TipChange_Min').value = minuteValue
1 Like

Thank you Phil and Jordan, this is a huge help.

Jordan, I appreciate the example program and am hoping you can elaborate a little more on what is happening in the loop with xrange. This looks a lot simpler than my series of if statements.

I had a half day at work today and won’t be able to test anything until tomorrow but I’m excited to try this out.

Thanks again,
Steven

Jordan,

Thanks again for the help! I was able to just copy and paste your script and it worked perfectly (just had to change the component addresses a little bit in the last section.

I did some Googling on xrange to try to figure out exactly what is happening in the code.

This was a great learning experience.

Thanks again Jordan and Phil

Hi Steven.

Sorry been busy trying to finish thing up over before the Thanksgiving holiday. I can write something up over the weekend, if you still want it.

No problem! I think I have a basic understanding of what is going on now, but it may be helpful for other users if you still wanted to go into more detail.

range() and xrange() are very similar. The difference is in how they set themselves up. We’ll use range() in our examples for clarity.

range() creates a list.
range(5) – a list of integers up to, but not including 5. If no start value is specified, it assumes zero.
range(2,5) – a list of integers starting at 2
range(0,5,2) – list of integers, staring at 0, with a step of 2
range(1,5,2) – list of integers, staring at 1, with a step of 2

If you need something with a step value, you must use a start value.

xrange() works the same way, but it doesn’t create a list. It returns a value ‘on demand’.

When to use one over the other? Python purists will say that for large ranges, xrange() is faster because there’s no overhead. It uses the same amount of memory, regardless of the range it represents. On the other hand, the list generated by range() can be used over and over again, sliced, diced, and modified to your heart’s content.

It should be noted that in Python 3, range() works like xrange(), and xrange() went away. It’ll be a while before Jython (and then Ignition) catches up to this, so hopefully we’ll retired before that happens. :wink: