parsePath().toString() equivalent? 
How do i
from parsePath()
?
Forgive my lack of familiarity if there’s an easy way - I try to avoid datasets unless absolutely necessary. (e.g. Table props.columns
derivation).
Perhaps a "ds vs object" blurb might be useful if my ignorance is showing.
here's my uuugly method (first try)
{
"type": "expr",
"config": {
"expression": "parsePath(\r\n\tconcat(\r\n\t\t{view.custom.node.meta_node.Tag_Path}, // concat(\u0027[\u0027, {session.custom.project.tag.provider}, \u0027]\u0027), \u0027\u0027),\r\n\t\t\"/Analog Tags/\",\r\n\t\tif(\r\n\t\t\t{view.custom.Type_ID.meta} \u003d \u0027Wl\u0027,\r\n\t\t\t\u0027TEMPERATURE\u0027,\r\n\t\t\t\u0027METER_TEMP\u0027\r\n\t\t)\r\n\t)\r\n)"
},
"transforms": [
{
"expression": "flatten(forEach(\r\n\t{value},\r\n\tasPairs(it())\r\n))",
"type": "expression"
},
{
"expression": "where(\r\n\t{value},\r\n\t!isNull(it()[1])\r\n)",
"type": "expression"
},
{
"expression": "forEach(\r\n\t{value},\r\n\tlower(groupConcat(it(), \u0027:\u0027))\r\n)",
"type": "expression"
},
{
"expression": "groupConcat({value}, \u0027:/\u0027)",
"type": "expression"
}
]
}
TL/DR: Use parsePath()
in conjunction with tags()
reading the storage provider property of the tags. Tags will return a fully de-relativized path, that can then be combined with the value returned from tags
and a separately provided system name to construct the historical path.
Expression
transform(
{path.to.gateway.name},
forEach(
tags(
forEach(
{path.to.list.of.tag.paths},
concat(it(),'.historyProvider')
)
),
transform(
parsePath(it()[0]),
concat(
'histprov:',it()[1], //gets value returned from tags.
':/drv:',coalesce(value()['drv'],value(1)), //if tags is supplied a path with a driver then use that, otherwise use the provided value
':',value()['prov'],
':/tag:',value()['tag']
)
)
)
)
To produce a proper Historical Path given a simple tag path, you need to know a few things.
- The Historical Provider that is configured for the given tag path.
- The name of the gateway the Historical Provider is on. (If a single gateway system, then it's just the gateway name. For remote providers you need the gateway name and the tag provider on the remote gateway.
A historical tag path looks like this:
histprov:hist_prov_name:/drv:gateway_name:tag_provider_name:/tag:tag_path
This then of course begs the question of how you can dynamically retrieve that information.
In a single gateway system it's pretty simple. There is a System Tag SystemName
that will give you the gateway name, and you can actually read the historyProvider
property from the tag, given the tag path. So the first is a simple tag binding, and the second is a simple Indirect Tag Binding.
For a multiple gateway system it is not that simple. For reference see: Remote Tag Provider History Queries
Once you have those pieces of information then it's just a matter of parsing the given tag path into it's parts and reconstructing them into the historical tag path.
So, given the following path [default]_Simulator_/Ramp/Ramp0
, in a single gateway system where the System Name is "Ignition_Gateway", and the historical provider is "History_Provider". This expression will return the correct historical tag path.
Expression for Single Tag Path
transform(
parsePath({this.custom.tagPath}),
lower(
concat(
'histprov:', coalesce(value()['histprov'],{this.custom.histProv}),':/',
'drv:',coalesce(value()['drv'],{this.custom.gatewayName}),':',coalesce(value()['prov'],'default'),':/',
'tag:',value()['tag']
)
)
)
NOTE: I don't believe that paths are case sensitive, but if you want this to match the output from say the Power Chart, then the lower()
does that.
Output
histprov:history_provider:/drv:ignition_gateway:default:/tag:_Simulator_/Ramp/Ramp0
If you provide parsePath
with multiple strings, then it returns a row for each string. Modifying the above expression with forEach
will allow you to return a list of multiple historical tag paths.
Expression for Multiple Tag Paths
transform(
parsePath({this.custom.tagPath},{this.custom.tagPath1}),
forEach(
value(),
lower(
concat(
'histprov:', coalesce(it()['histprov'],{this.custom.histProv}),':/',
'drv:',coalesce(it()['drv'],{this.custom.gatewayName}),':',coalesce(it()['prov'],'default'),':/',
'tag:',it()['tag']
)
)
)
)
Features employed
- Iterables General behavior of all iterators.
- transform: Makes a snapshot of the dataset returned from parsePath.
- parsePath: returns
QualifiedPath
parts as a dataset
- coalesce: returns the first non-null argument
- concat: returns the concatenation of all provided arguments
- lower: returns the provided string in all lower case
- forEach: loops through each row in the dataset.
- it: Delivers the row from this iteration through the dataset.
@lrose - surely the intent of parsePath() was to make this process purely dynamic (as opposed to hardcoding the path part names as your examples do)?
@hunterdg - My examples are dynamic. Given a tag path, this will output a historical tag path. There are a couple of other bindings involved as explained, but it will change dynamically with the path.
The intent of parsePath
is to break a string path down into its constituent parts. A simple tag path by itself does not contain the needed information to build a corresponding historical tag path. Information must be retrieved and inserted. parsePath
on its own does not do any extra look up.
@hunterdg I suppose, maybe this is what you're looking for, although, this will not provide a valid historical tag path, unless it is handed a valid historical tag path.
Expression
transform(
parsePath({this.custom.tagPath},{this.custom.tagPath1}),
forEach(
value(),
groupConcat(
flatten(
forEach(
columnsOf(it()), // columns of dataset returned from parsePath
forEach(
where(
it(1), // rows of dataset returned from parsePath
!isNull(it()[it(1)[0]])
), // all non-null values in this row
concat(it(1)[0],':',it()[it(1)[0]]) // columnName + : + columnValue
)
)
),
':/'
)
)
)
@lrose - This does indeed work for a non-historical (but PowerChart-compatible) QualifiedPath as you point out, but I’m somewhat disappointed as it seems to be basically the consolidated version of my original method (which is still very much appreciated). I assumed there must be an easier way to concat dataset rows interpolated with their column names. Fairly certain QualifiedPath.toString()
performs the equivalent and is much more approachable, despite the overhead (and regardless, I believe a scripting context is needed to build a “proper” historical QualifiedPath object anyway).
EDIT - @pturmel’s original post mentions “(I've been frustrated with the need for scripts to produce proper historical paths from simple tag paths.)”…
I now see that parsePath()
requires passing a string (with histprov
+ drv
) to return a historical path - guess I missed that critical (and obvious in hindsight) piece - so i might as well just pass the historical paths strings straight to PowerChart. It is still useful to derive non-historical stringified qualified paths from a QualifiedPath object though.
Feel free to cleanup this post. I consider my question “mostly” answered - but I’m still uncertain as to the rationale for parsePath()
- as it most certainly does not seem to "produce proper historical paths from simple tag paths” as the original post suggests.
@hunterdg parsePath
is best used when using tags()
to read the Storage Provider property of tags. If you use parsepath on the returned full tag path, you can use its parts with the returned value, and separately provided system name, to construct the history qualified path from a basic tag path. A big concat()
operation.
@pturmel 