Any consideration around accessing “document” tag custom property members via UDTi_or_Atomic_tagPath.custom_prop[“member”] like document_tag[“member”] in expressions tag bindings?
I understand they’re different object types and document tag members are nested under a “value” key whereas document custom property members don't… but this would be a HUGE deal and incredibly simplify (or completely remove the need for) expression transforms on tag bindings just to read the value of document custom tag properties.
Example: I store “user friendly” strings that correspond to Boolean tag states in a bool_state document custom property on each Boolean tag. I currently have to read the custom property via tag binding and then extract the members via expression transform. Could be done with a tag binding alone if this was supported.
Of course, please school me if I’m missing something
You are using the tag function outside of expression tags. Even if this feature was implemented, doing it inside the tag() function is crazy talk. You should pitch it as a generic extension to tag paths in general, and then your indirect binding could use it, along with all of the other places in Ignition that accept tag paths.
It’s bad (i know it’s not ideal) to use tag() in perspective property expression bindings/transforms? Is the alternative multiple properties each with tag bindings and an additional property to compse the desired result? That gives me the ick. EDIT: (but i get it now)
is an expression structure binding with multiple tag() functions, then a transform to compose desired result any better? (no)
EDIT: suppose I should’ve just omitted the tag() bit - (but it was useful to pull out a single prop in the middle of a transform)
to be clear, tag('document_tag[“member”]') does indeed work, as does a tag binding to just document_tag[“member”]
I’m definitely all for this, and naively thought it was implemented as such when i discovered it yesterday, only to be disappointed (I have a unique situation where I’m using a real document tag as opposed to document custom tag properties)
I’m all in on document custom tag properties as an effective DB metadata and “static” tag parameter alternative, so if I’m missing something, please let me know
Is my current method of just reading the whole custom tag property and then parsing it via expression transforms really the most performant/effective?
is a tag binding to document_tag[“member”] less performant if I only need one “member”?
Depends on how big the document tag is. If it’s got 1000 keys and you only need to access 2 for your UI, then it’s probably overkill.
But if it’s a reasonably sized object I’d expect this to be the most performant. If you don’t want the whole object to be sent to the client you can always set its access to Private.
somewhat on this same subject - I’m using the results of system.tag.query to populate the instances of a “Label” flex-repeater. I ensure the query includes/returns the basic ’static’ properties from each tag like engUnit, formatString, whatever custom tag properties I need, etc so I can compose the label text with relevant data. Same goes for other “base” view “templates”
I’m a big fan of reusability so I also embed the same “base” view in table cells where applicable (to allow for realtime tag updates). to allow for this, each base view (that might also be embedded in a table cell) has at least two view param objects: view.instanceData (for flex repeater embedding) and view.rowData (for table cell embedding) - and occasionaly custom.urlParams.
I “templataize" the contents of each object to match the expected runtime contents (in replace mode) of each embedding method’s parameters - implicit or otherwise (as well as i can, primarily for debugging and ease of future maintenance/development by other folks), but notably I set each and every value to null (this is often unexpected by other folks, but I develop with an expectation of default nulls/to ensure null cases are appropriately handled).
Where I can, I enable “static” expression structure bindings (transient, with null for each value, and with comments where useful) on these input view parameter objects to ensure debug/testing values are not saved with the view, but this is not always feasible as even default/static null bindings do not always get overridden on view intialization (I still create the bindings for reference but disable them in these cases). I haven’t taken the time to nail down every scenario that causes this, but it’s definitely related to my method of coalescing multiple “identical” parameters from different sources with different embedding methods.
I then create dedicated custom._coalesced parameter objects (with per-member coalesce() expression bindings - or just a top-level expression structure binding with multiple coalesce() expressions) to compose the actual desired values. All other bindings in the views reference these coalesced parameters instead of the view params
here’s the meat of my question:
In the instanceData (flex repeater) case where tag properties like engUnit/formatString/custom_prop are easily sourced (and passed in) by the results of a system.tag.query() pointing at a single site/device parent tagPath, the custom._coalesced function has an easy job, but in the rowData (table cell embedding) case (which is an ‘overview’ displaying a subset of values for every site/device) I don’t readily have that data available so I must do tag reads based on rowData.fullPath (into a custom._tagReads property object with individual member indirect tag bindings) for the custom._coalesced object to reference
few questions:
is the single-universal-“base template”-view method overkill? Should I just create different ‘base’ views for each embedding method?
admittedly it may sometimes take more effort to cordinate between multiple identical ‘base’ params in a single view
i’ve considered doing a recursive tag query for the “overview” table situation, or multiple per-row (site/device) targeted tag queries and stuffing that data into a hidden column for the implicit rowData view param to pass into the nested cell views, but that feels wrong - shouldn’t the underlying queries for a table be as light as possible? Surely a couple tag reads are better than even a tightly filtered recusrive tag query
is this method overly-flexible? Should I just abandon querying/passing in the requisite additional ’static’ tag properties in the instanceData (flex-repeater) scenario in favor of indirect-tag-bindings (only)?
this is admittedly relatively easier, and it’s how I historically developed - assuming only a tagPath would ever get passed in to any nested view, but that was back when we only had system.tag.getConfiguration and system.tag.browse()
You just need my pageVarMap() functions from my Integration Toolkit. Add a change event to the coalesced parameter object and write into the pageVarMap() (and then fire its refresh method). All of your nested views will then be able to use a pageVarMap() expression function to obtain the same structure, and extract any instance-specific values.
just realized my presumed legitimate rationale for using tag() in expression binding:
presumably coalesce({value}, tag(‘tagPath’)) will completely avoid reading the tag if {value} is not null..
if I instead coalesce two distict properties (the latter with a tag binding), there’s an unavoidable read to a (possibly nonexistent) tag
thoughts? Is this unavoidable read to a possibly nonexistent tag preferable to a an expression tag() read only when required? I’d argue the answer would intially be “no” if one was unaware of the performance ramifications, and only “maybe” if one (like me) was only vaguely aware that it’s less than ideal, and assumed the majority of cases would result in the coalescing of an existing value rather than a tag read.
ugly compromise on it’s face IMO. (apologies if this has been hashed out before as I imagine it has)
p.s. just pulled down the Integration Toolkit. Does the modbus driver support Enron/7x registers?
No, it won't. It will still subscribe to the tag, even though the values will be discarded. And updates to the tag will cause the expression to re-execute, even if it will discard again.
Really, do not use the tag() expression function in UI bindings. Ever.
No. Not enough commercial interest for me to spend the time to support Enron format.
just to be clear, if any referenced value in a coalesce function changes, the expression re-executes, but retains the first non-null value’s data - and timestamp?
in other words, there’s no obvious “evidence” that the expression has re-executed but bailed before returning?
i assumed (there i go again) it perhaps subscribed to the parameters in order, and only re-executed if preceding parameters became non-null or if the current value became null
When the expression is first started, before evaluation, the tags() expression gets connected to the tag subsystem. If coalesce's first argument is null at any point, the tag() expression will evaluate its path and subscribe to it. Thereafter, the entire expression will re-execute as that tag updates, even if the coalesce never evaluates that part again.
Since UI properties often start with a null, the odds of this scenario are very high.
another use case - i need to chose from a list of n tags based on their quality - if the first is bad, i need to move on to the next.
without tag() i’d need to hardcode n properites with tagbindings and evaluate their qualities in a giant case statment
i guess i could just script out the — no that’s assuming i would want to pick the first tag that is good and stick with it, unless i set up a timer to re-execute the “pathPicker” script.
wow this is good exercise for alternative (but likely more complex) problem solving
appears tags() dosen’t return a QualifiedValue per-tag, but instead at the object level, with the quality of the last bad tag in the list. Unsure how I would use this to "select 1st where quality is good” inside an expression (but admittedly I just started poking around and prob need some sleep)
additionally, wrapping it in forEach() just returns a null QualifiedValue (with the same quality) if any of the tags are bad/don’t exist.
Hmmm. I haven't anticipated this possible usage. I have recommended wrapping the output of tags() with forceQuality(..., 192) to deal with bad tags, but it turns out that wipes the qualities of nested values, too.
And you can't pass bad quality to my iterators--they bail out. Perhaps I should change that. Hold the line for a few....
Edit: Ugly.
If I modify tags() to ignore the quality of the tag values for its own return quality, it will deliver the nested qualities. And you can iterate over the output of tags(). However, you can't get at those nested qualities in expressions. The square bracket operator discards the quality of the retrieved value. And worse, Perspective itself will discard the nested qualities when it constructs its internal property representation. You can use the dataset form of tags(), and Perspective will hold the dataset with qualities intact, but only scripting can access them.
I will pitch to IA to have the subscript operator deliver nested quality, but that probably wouldn't be acceptable before v8.3, and would need some risk assessment. Perhaps I should implement a quality-aware subscripting function.
Would simply replacing tag() with tags() (in my prior use case) mitigate the problems with tag()?
I’d have to somehow handle copying the quality from the parent object into the [1]… but I feel like I was somehow able to retain the quality of a single tag in an initial test.
(I’ve spent a whole day trying to refactor my tag() usage with indirect bindings, but haven’t been successful without a giant case statement)
More specifically - it’s easy to get the TAG with good quality, but not the tagPath that will result in good quality (without a tag()) function). I need the “good” tagPath to derive the appropriate engUnits/formatString/custom props
Unless there’s some way to derive the path from a tag binding within an expression transform on said tag binding
I haven’t tried coalesce(tags(1), tags(2)…) but I don’t think that would work