Perspective Scripting to find a Custom Property on a View


I have a custom UDT tag that is on my view. Within this UDT tag are two tags:


The PB tag needs to be a momentary bit where it's a 1 and goes back to 0. When the PB tag is a 1, a one shot energizes and latches the EnableMode bit. When the PB tag is 1 again, the one shot energizes and unlatches the EnableMode. I am trying to make it so that whenever the user presses a button, it will momentarily turn the tag to a 1 and then back to 0. I am trying to accomplish this through scripting since there is NOT a momentary button in Perspective.

The following works:


It works like a champ, but then when I try to make it dynamic by having it call a tag on the view like below:


Doing this, I get an error in my log saying that it can't find the tag. How else can I reference a custom tag on my view so that it actually finds it??

For the record, I ended up creating a new custom property on the popup. On each custom property I used the following:

concat({view.params.TagPath}, "/HighAlarmEnablePB")

I think system.tag.write has been depreciated (and it's "system", not "System"). You should be using system.tag.writeAsync or system.tag.writeBlocking. Note they expect the tags in list format - even when only one tag is sought (but I think it will tolerate one tag without the [ ].

Can you right-click on custom.UDT, copy and paste it in here so we can check the contents?

Tip: see this before you post:

Putting aside the whole momentary button thing (search the forum for @pturmel's post about this),

How is the custom.UDT property defined/generated ?

For a reason. You are very likely to eventually regret implementing this.

1 Like

Thanks for the tip!

I have tried system.tag.writeAsync and that returned an error as well:

AttributeError: 'com.inductiveautomation.ignition.common.script.Imm' object has no attribute 'tags'

See UDT below:

"AlarmPriority": 0,
"DecimalPlaces": 1,
"DeviceDescription": "Wet Well Level 1",
"DeviceEU": "%",
"DeviceEUAlt1": null,
"DeviceEUAlt2": null,
"DeviceEUPrimary": "%",
"DeviceID": "LIT-111",
"DisplayAlt1Mode": false,
"DisplayAlt1PB": false,
"DisplayAlt2Mode": false,
"DisplayAlt2PB": false,
"DisplayPrimaryMode": false,
"DisplayPrimaryPB": false,
"ForceOnMode": false,
"ForceOnPB": false,
"HAlmColor": "#FF9E9E",
"HighAlarm": false,
"HighAlarmDBValue": 90,
"HighAlarmDelaySP": 5,
"HighAlarmEnableMode": false,
"HighAlarmEnablePB": false,
"HighAlarmEnablePB_MEM": null,
"HighAlarmIn": 95,
"HighAlarmProcessEnable": true,
"HighAlarmResetPB": false,
"HighAlarmResetRequired": false,
"HighWarning": false,
"HighWarningDBValue": 85,
"HighWarningDelaySP": 5,
"HighWarningEnableMode": false,
"HighWarningEnablePB": false,
"HighWarningIn": 90,
"HighWarningProcessEnable": true,
"HistorianPath": "histprov:SQL_Server:/drv:ignition-mnwa3igio01:default:/tag:Wastewater/LIT_111/",
"InputRaw": 0,
"LAlmColor": "#FF9E9E",
"LowAlarm": false,
"LowAlarmDBValue": 10,
"LowAlarmDelaySP": 5,
"LowAlarmEnableMode": false,
"LowAlarmEnablePB": false,
"LowAlarmIn": 5,
"LowAlarmProcessEnable": true,
"LowAlarmResetPB": false,
"LowAlarmResetRequired": false,
"LowWarning": false,
"LowWarningDBValue": 15,
"LowWarningDelaySP": 5,
"LowWarningEnableMode": false,
"LowWarningEnablePB": false,
"LowWarningIn": 10,
"LowWarningProcessEnable": true,
"LowerAlarmDB": 5,
"OOSMode": false,
"OOSPB": false,
"Output": 0,
"OutputAlt1": 0,
"OutputAlt2": 0,
"OutputPrimary": 0,
"PopupWindow": "Templates/Popups/Analogs/SymAnalogInputAlt_Status",
"PopupWindowSetpoints": "None",
"PopupWindowSetpointsTagPath": "None",
"RawInputMax": 20000,
"RawInputMin": 0,
"ScaledMaxAlt1": 100,
"ScaledMaxAlt2": 100,
"ScaledMaxPrimary": 100,
"ScaledMinAlt1": 0,
"ScaledMinAlt2": 0,
"ScaledMinPrimary": 0,
"ScalingFault": false,
"UpperAlarmDB": 5

Are you sure you used system.tag.writeAsync and not system.tags.writeAsync?

Did you?


Sorry, you are correct. I still get an error even with the function written correctly:

TypeError: 'bool' object is not iterable


You are likely not passing your tag value as a list. Try enclosing your tag value in [] like system.tag.writeAsync([self.view.custom.UDT.HighAlarmEnablePB],[1])

Edit: Actually, have you confirmed that self.view.custom.UDT.HighAlarmEnablePB is a tagpath? How are you passing the tag path to view now that you are trying to make it dynamic?

1 Like

It seems like you're passing the actual value of the HighAlarmEnablePB (as hinted at by the error bool is not iterable) it's trying to coerce what you're feeding it into what it expects, a list of tag paths. You need the actual tag path to write to.


When I try with the , it now says, TypeError: expected a str

There's an analog object that has the TagPath initially set by the user. When the user presses the object, that TagPath is then sent to the popup graphic. The popup graphic then has the whole UDT at it's disposal on the view.custom.UDT

Means this:

Use this parameter to build the path to the udt member, something along the line of udtAlarmPBPath = "{}/HighAlarmEnablePB".format(event.view.params.UDTTagPath) then use the created path in the call to system.tag.writeAsync

The other(possibly better) way to build the path to the alarm member would be to create a custom property on the view and bind an expression to it that is along the lines of concat(view.params.UDTTagPath, "/HighAlarmEnablePB"). You'd then reference this custom property to in your script to get the path to the alarm pb member of the udt.

1 Like

EDIT: whoops, didn't notice ryan already beat me to the first part of this post

As for trying to pulse the value high and then low from Perspective, I would recommend you move that logic over to the PLC. If something happens between when you write 1 and when you write 0 (say there's a hiccup in the network), the tag will stay 1 which may just cause an annoying bug or could cause trouble depending on what it's controlling.


That did it. Thanks so much!!

I agree, it's not the best solution, but it's only a "push button" to enable/disable an alarm. Not process critical thankfully.

If the ultimate goal is to simply toggle the EnableMode tag on or off, I don't see the need to have an additional 'momentary' tag. You've already got a custom prop on your view reading the value of your UDT, correct? So you could just check what the mode is and then use that to determine the write value.

currentMode = self.view.custom.UDT.HighAlarmEnableMode
writeVal = not(currentMode)


You are correct. The momentary bit is unnecessary, but the momentary bit is already in the AOI. The logic in the AOI prevents the EnableMode tag to be written to. It would be easy to change, but the ask has been to leave the AOI as is.