17 Replies Latest reply on Oct 15, 2018 11:04 AM by gregor

    How to Locate a Specific PointID in a PISDK.PointList Object

    pauljohnson

      Hi there, I'm new to writing interfaces using the PISDK (Note, due to corporate desktop model we do not have access to the PI AF SDK).  I have written a simple interface which retrieves all tags for a specific PointSource into a PointList object.  The interface reads data from a device specific CSV file which is then written to the appropriate tags in PI.  I am having difficulty dealing with underlying COM architecture of the PISDK, specifically locating the InstrumentTag for the tag I need within the PointList.  For want of a better solution, I currently iterate through the PointList comparing the InstrumentTag in the data against each InstrumentTag contained in the PointList.  I am sure there is a more efficient way of doing this, but I can't for the life of me find it within the documentation, or from looking at the methods available via the PointList object.  Assistance would be much appreciated.

       

      Kind Regards

      Paul.

        • Re: How to Locate a Specific PointID in a PISDK.PointList Object
          Rick Davin

          What version of PI Data Archive are you using?  What is your PI SDK version?

            • Re: How to Locate a Specific PointID in a PISDK.PointList Object
              pauljohnson

              Rick

                   Versions are:

                        Data Archive - 3.4.390.18

                        PISDK - 1.4.0.416

                • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                  Rick Davin

                  Hi Paul,

                   

                  Your PI Data Archive is about 8 versions out-of-date.  The PI SDK is 5 versions out-of-date.  There have been a LOT of security and performance enhancements released in the past 6 years.  We do not recommend any new development be done with PI SDK.  It would be irresponsible for us not to bring this to your attention, and let you expend a lot of effort in a dying technology.  Since you admittedly seem new to PI SDK, here's some information we've released in the past 3+ years. 

                   

                  2018-10-11 10_07_14-Developer Technologies.png

                   

                  I would think your corporate desktop model is based on very old, and poor information.  Applications targeting .NET 4.x can use AF SDK to completely migrate your tag-based application.  Forget the fact that from a developer perspective that AF SDK applications (managed .NET) run faster than PI SDK (heavier COM) and are quicker to develop than with PI SDK.  PI SDK has been in maintenance mode for years, meaning no new features or performance enhancements, just bug fixes and security patches.  Instead from an IT/Security perspective, I would rather use the technology (AF SDK) that offers the better security - within my application, on the box hosting my application, and to the PI System that I network with.  Better security is usually an easy sell to your IT department.  No one is suggesting you go around to 100 desktops and install the AF client.  You only need to install it on the one box hosting the custom interface.  Typically such boxes have a different set of rules than desktop users, such as the box is not expected to have a user sitting at the keyboard, or that the interface will run under a dedicated services account, which is exempt from changing the password regularly such as with human users.  So why can't this one box be exempt from an antiquated desktop model?

                   

                  Live Library AF SDK PI Help

                   

                  PIPoint Query Syntax Overview

                   

                  PIPoint Search Query in AF SDK

                   

                  And for interfaces you may want to look into PIDataPipe Class

                  2 of 2 people found this helpful
              • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                pauljohnson

                Rick

                     This development is a simple proof of concept  that as a contractor I have been pressed into providing using the existing resources on our corporate desktop.  I am not in a position at the moment to change to using the AFSDK.  If and when we come to developing this for a full production implementation it will incorporate a move to PI Vision and a full update of our client based PI software.  I appreciate the antiquated nature of our current architecture, no software developer in their right mind would consider developing a solution against a COM object model, when there is a pure .NET equivalent, but as I say I'm stuck with what we have at the moment, so any help you can provide relevant to the old PISDK would be much appreciated.  I have a deadline for delivery of this 'proof of concept' for Wednesday next week.

                  • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                    Rick Davin

                    Hi Paul,

                     

                    Some people refer to "InstrumentTag" to mean the tag name they use in the field.  Others may loosely use it to refer to the PIPoint, or tag or tag name.  However, in the PI SDK, InstrumentTag has another meaning.  It is a PointAttribute associated with a given PIPoint object.  I am going to assume you want to work the the InstrumentTag attribute.

                     

                    Here is a link to the online Programming Reference.   The examples are given in VBA.  Are you using C# or VB.NET?

                     

                    The InstrumentTag is called a PointAttribute. For each PIPoint, you will need to get GetAttributes or GetSelectedAttributes in order to get the setting for InstrumentTag.  If working with VBA, this is done with NamedValues.  If I was using .NET, I would rather work with dictionaries.  As I would be building a PointList, I would likewise build out a dictionary keyed on InstrumentTag (string) mapped to the PIPoint.  If you have a PointList collection, you should be able to quickly search for a tag name using the default Item indexer.  Working with PointAttributes can be confusing and frustrating at first, because they are not like properties that are always known per object (thank you, Intellisense).  Instead you must take extra care to load into the NamedValues collection, which is like an appendage to the PIPoint.

                     

                    UPDATE:

                     

                    If you are interested more than just InstrumentTag, such as PointID or the tag name, then read this help for PointList.Item.  If you request a name, it must be "\\servername\tagname".  If you want to search by PointID, it would be a string starting with "?" followed by the id number.

                    2 of 2 people found this helpful
                      • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                        pauljohnson

                        Rick

                         

                             I am using vb.net.  Up to this point the only ways I can determine for getting the Point information for the data that I want to write is to either:

                         

                        1/  Load a PointList object with the points relevant to the specific PointSource, and iterate through them comparing the instrument identifier from the data file with the InstrumentTag attribute of the PIPoint

                        OR

                        2/  Load the PointList object with a single PiPoint (using a datapoint specific filter).

                         

                        Neither approach seems appropriately efficient.  Approach 1 starts out pretty fast but slows down to an unacceptable level seemingly proportionate to the number of PIPoints in the PointList object.  Approach two whilst eliminating the degradation over time still is not acceptable as it takes about a second to retrieve each Point from the server and update the associated data.  Haven't yet looked at the links from your last post, but will look at them this afternoon.  In the meantime here is an extract of the relevant code elements from my app:

                         

                        ...

                                    colCnt = _dataPointCol
                                    Do Until topsDevice.Data(_titleRow, colCnt) = ""
                                        'Get a new TOPSPoint object to store tag attributes outside the PIPoint object (requires iterating through all elements in order to read attributes for a single point this object allows me to 
                          'store this information outside of the PIPoint object)
                                        _topsPnt = New TopsPoint
                                        'Lookup the device datapoint in the instrumenttag attribute of the pi point list
                                        _dataPoint = deviceName & plantId & topsDevice.Data(_titleRow, colCnt).ToString.Replace(plantId, "")
                                        'Strip all spaces from the datapoint
                                        _dataPoint = _dataPoint.Replace(" ", "")
                                        '@@ Refresh tag list for the current file.  Tags are filtered by devicename & plantid
                                        'RefreshTags("InstrumentTag = '" & _dataPoint & "'")
                                        'Return the PiPoint using the instrumenttag as filter criteria
                          'GetPoint(_piPoints, _dataPoint, PISDK.CommonAttributeConstants.cmnatrInstrumentTag)
                                        _logger.SendLog("Processing datapoint - " & _dataPoint, NLog.LogLevel.Trace)
                                        _logger.SendLog("Started at - " & DateTime.Now.ToString("hh:mm:ss"), NLog.LogLevel.Trace)
                                        If Not _pnt Is Nothing Then
                                            'Build the array of data values for writing to the archive
                                            _logger.SendLog("Instrument Tag '" & _dataPoint & "' FOUND in tag database as PointID " & _topsPnt.PointID.ToString & "/" & _topsPnt.TagName, NLog.LogLevel.Trace, Nothing)
                                            piValCnt = 0
                                            For rowCnt = _dataPointRow To topsDevice.Data.GetUpperBound(0) - 1
                                                timeStamp = CDate(topsDevice.Data(rowCnt, _timeStampCol))
                                                topsValue = New TOPSPIValue
                                                topsValue.TimeStamp = timeStamp
                                                topsValue.PiValue = topsDevice.Data(rowCnt, colCnt).ToString.Trim
                                                piValues.Add(topsValue.TimeStamp, topsValue.PiValue, Nothing)
                                            Next
                                            _piServer.PIPoints(_pnt.Name).Data.UpdateValues(piValues, PISDK.DataMergeConstants.dmReplaceDuplicates)
                                            _logger.SendLog("Tag " & _pnt.Name & " succesfully updated", NLog.LogLevel.Trace)
                                        Else
                                            _logger.SendLog("Instrument Tag '" & _dataPoint & "' NOT found in tag database.", NLog.LogLevel.Info, Nothing)
                                        End If
                                        colCnt = colCnt + 1
                                    Loop 'for the next datapoint
                        ...
                            'Find the pi point for the specified datapoint by iterating through the PointList object
                            Private Sub FindPoint(ByVal pointList As PISDK.PointList, ByVal dataPoint As String, _
                                                      ByVal searchAttr As PISDK.CommonAttributeConstants)
                                Dim ptAttr As String = ""
                                'Iterate through the pi points list until we locate the required pipoint
                                For Each pnt In pointList
                                    '_logger.SendLog(pnt.name.ToString, NLog.LogLevel.Trace)
                                    ptAttr = pnt.pointattributes.CommonAttribute(searchAttr).Value
                                    If ptAttr = dataPoint Then
                                        'return the specified return attribute value
                                        _pnt = pnt
                                        _topsPnt.PointID = pnt.pointattributes.CommonAttribute(PISDK.CommonAttributeConstants.cmnatrPointID).Value
                                        _topsPnt.TagName = pnt.pointattributes.CommonAttribute(PISDK.CommonAttributeConstants.cmnatrTag).Value
                                        _topsPnt.InstrumentTag = pnt.pointattributes.CommonAttribute(PISDK.CommonAttributeConstants.cmnatrInstrumentTag).Value
                                        Exit For
                                    End If
                                Next pnt
                            End Sub
                        

                         

                        Kind Regards

                        Paul.

                          • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                            pauljohnson

                            Rick

                             

                                 I just noted an error in the code extract

                             

                            'GetPoint(_piPoints, _dataPoint, PISDK.CommonAttributeConstants.cmnatrInstrumentTag)

                             

                            should not be commented out and should actually read:

                             

                            FimdPoint(_piPoints, _dataPoint, PISDK.CommonAttributeConstants.cmnatrInstrumentTag)

                             

                            Paul.

                            • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                              gregor

                              Hi Paul,

                               

                              The GetPoints2 method offered through the IGetPoints2 Interface allows you to load multiple points together with selected point attributes in a single shot. Patrice has shared a C# sample as answer to Efficient way to retrieve Huge number of Points & its attributes using PI SDK some time ago.

                               

                              It is difficult to understand what an application is doing from looking at just some code snippets but my feeling is that there is more potential for improvement. I understand you are reading time series values from a comma separated file. Together with each row, there is information referring the PI Point this information is supposed to be written to but it doesn't mention the PI Point's name but rather its Instrumenttag Attribute. It appears you are looking up the destination PI Point for each row in the comma separated file and instead of using cached information, you are doing a trip to the server each time. At least this is what I understand from your code.

                              Point Attributes are considered relative static, meaning they shouldn't change but for sure they can. The way PI Interfaces address this is through a sign-up with PI Update Manager for point updates. The sign-up allows to detect point table changes periodically and to just re-load required Point Attributes for those points which changes were reported for. This way Interfaces are able to load required point attributes only at startup and to use the information cached in memory until a change is occurring.

                               

                              While talking about PI Interfaces, have you looked into using PI Connector for UFL or PI Interface for Universal File and Stream Loading (UFL)? PI Connectors are working asset centric and hence a connector doesn't appear to be the right choice but the PI UFL Interface is likely capable of doing what's needed to transfer time series data from comma separated files to PI Points.

                          • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                            gregor

                            Hi Paul,

                             

                            Our mission is to support developers to be successful with their development projects and please be ensured that we take this mission serious and the success of community users respectively their development project is what we love about the job.

                             

                            A concept considering PI SDK for a recent .NET project is a bad one. I cannot think of a single category the PI SDK would score better than AF SDK.

                             

                            • The RDA version of AF SDK offers bulk operations which perform way better especially in high latency networks.
                            • It is way easier and more natural to deal with .NET objects rather than with COM objects and I consider the impact to the development cycle huge.
                            • AF SDK offers to develop asset but also PI Point centric.
                            • The documentation has C# and VB.NET samples.
                            • There is no difference with regards to license costs since all Developer Technologies are freely available - except those deprecated.
                            • The requirement for a runtime license (PSA) exist independently from the chosen technology.
                            • PI SDK is announced for deprecation while there is active development efforts spend on the AF SDK.

                             

                            Can you share any details about this POC and the defined success criteria?

                             

                            Because time is a limiting factor with your POC too, when seeing a dead end sign, is it necessary to drive until the road really proofs being a dead end?

                            1 of 1 people found this helpful
                              • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                                pauljohnson

                                Gregory

                                 

                                     As already noted in the thread, I am aware of the all the benefits available from the AFSDK over the PISDK, but as I have already stated this is beyond my control, I would much prefer to be developing against a native .NET architecture rather than COM :-)    Regarding success criteria for the POC, the customer would be happy I suspect with the current response times, however from an impressionistic point of view I would like to deliver as efficient a POC as possible.  The final solution if this goes to production will be part of a wider project incorporating a rollout of PI Vision and the interface will logically be developed against the AFSDK.  I should also note that the machines the POC will run on are restricted to the PISDK as well, so although I could quite easily install the AFSDK on my development platform it would be a pointless exercise as it would not run on the destination POC platform.

                                 

                                Kind Regards

                                Paul.

                                  • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                                    Rick Davin

                                    Hi Paul,

                                     

                                    This next tip is not just for PI SDK but applies to any COM-based interop.  If using managed .NET objects, For Each or LINQ smartly know what data type each item should be.  But COM objects are not that smart.  Therefore it is up to you to declare them.

                                     

                                    For example, in the snippet below, variable pnt is an Object:

                                     

                                    For Each pnt As PISDK.PIPoint In pointList

                                     

                                    So you can't use Intellisense.  But below, pnt is a PIPoint and Intellisense helps you:

                                     

                                    For Each pnt As PISDK.PIPoint In pointList

                                     

                                    With that said, you do not need to extract the Tagname as a PointAttribute.  Rather you may use the simpler, easier, and more efficient Name property:

                                     

                                    _topsPnt.TagName = pnt.Name

                                • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                                  Rick Davin

                                  Hi Paul,

                                   

                                  How many tags do you expect to be supported by your custom interface?  How frequently do you expect data to be updated?

                                   

                                  If you have a reasonable number of tags, let's say a few thousand, and they update fairly slowly, let's say every 5 minutes or longer, then you may want to consider using the PI Interface for Universal File and Streaming Loading (UFL).  Your VB.NET application would then be concerned about reading values from the source and formatting a text file that is sent to the UFL interface.  You let the UFL interface worry about updating values into PI.

                                   

                                  If you have a higher number of tags OR you demand very frequent updates, then you most likely will need to write a Windows Service.  For a POC, I would write a Console app that justs runs unattended because writing and debugging Windows Services takes a little more code and caution.  By the very nature of the word "interface", I would expect that your application sits between some data source and the PI System.  The data source communicates via Instrument Tag name, and PI will expect a PIPoint.  Your interface therefore needs a mapping between the two (again, I suggest a dictionary for fast lookups).

                                   

                                  If I were building such an interface, here is a big picture of what I would code:

                                   

                                  App Start Up

                                  • On starting up, I would read some file sitting data source side that has a list of Instrument Tags.
                                  • I would find all PIPoints related to those Instrument Tags.
                                  • I would have a dictionary that stays alive for entire life of application that maps each Instrument Tag to a PIPoint.  When the data source sends me an update for an Instrument Tag, I can quickly lookup the PIPoint.
                                  • Long term, I would consider having a more robust class that includes the dictionary.  This class would contain additional information, perhaps exception deviation specifications or the snapshot current value.  Granted this maybe too refined for a simple POC.

                                   

                                  App Listening Mode

                                  • Once I've established what tags I care about, I would then transition to listening mode to listen for updates.  Whether this is periodic or streaming depends on the capabilities of the data source.
                                  • Updated values come in.  Read the source Instrument Tag associated with the value, and lookup the associated PIPoint.  Read the value's Value and Timestamp accordingly.  Then you may update the PIPoint.
                                    • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                                      pauljohnson

                                      Rick

                                       

                                      Thanks again, appreciate your feedback.  I sort of pre-empted what you were saying in your last post.  My issue was with the time that it took to lookup up a tag reference in the PointsList object.  In order to resolve my issue, I have supplemented my code with an object called TopsPoints which is ListOf TopsPoint.  A TopsPoint comprises three properties PointId, InstrumentTag and TagName.

                                      Each time I refresh my PointsList (this is accomplished via an asynch process on a threading.timer object), I automatically update my list of TopsPoints as part of this process, I then use the TopsPoints object to search for the datapoint to be updated, get the TagName and update the server with the data accordingly.  Using this technique a file which was taking 2.5 minutes to update now takes 44 secs.  This approach also allows me to utilise the IRefresh interface to pick changes in tag attributes on the database.  Not sure how scaleable this would be against a large tag database but for the purposes of the POC it more than fits the bill.

                                       

                                      Thanks again everybody for all your help.

                                       

                                      Kind Regards

                                      Paul.

                                        • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                                          gregor

                                          Hi Paul,

                                           

                                          We are happy when you are.

                                           

                                          I am just curious because I am by nature. What about the IGetPoints2 interface? Have you checked if this is an option?

                                          • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                                            Rick Davin

                                            Hi Paul,

                                             

                                            You don't mention how many tags are used in your list or how many Find calls you make to fetch PIPoint objects.  Makes it hard to say that 44 seconds is too long or really fast.  It would also help if you can break the time out into how long it took to create the PISDK.PointList versus how long it takes to build your TOPS points.

                                             

                                            Gregor's link on IGetPoints2 interface is quite intriguing.  I have also mentioned creating a dictionary to help you quick lookups.  One inefficiency I see in your code is you crawl over the point list searching for a given tag, and this could be repeated by as many tags as you have.  It would be more efficient to crawl over the point list once to create a dictionary.  Here is some sample code towards that end, including the IGetPoints2 interface:

                                             

                                            First, let's start with a basic TOPSPoint

                                             

                                            Public Class TOPSPoint
                                            
                                                Public ReadOnly Property InstrumentTag As String = Nothing
                                                Public ReadOnly Property PointID As UInt32 = 0
                                                Public ReadOnly Property TagName As String = Nothing
                                                Public ReadOnly Property PIPoint As PISDK.PIPoint = Nothing
                                            
                                                Public Sub New(ByVal pipt As PISDK.PIPoint)
                                                    PIPoint = pipt
                                                    TagName = pipt.Name
                                                    Try
                                                        ' Not every point class has an InstrumentTag
                                                        InstrumentTag = CType(pipt.PointAttributes("instrumenttag").Value, String)
                                                    Catch ex As Exception
                                                        ' NOP
                                                    End Try
                                                    PointID = CType(pipt.PointAttributes("pointid").Value, UInt32)
                                                End Sub
                                            
                                            End Class
                                            

                                             

                                            Very few tags on my test server have an InstrumentTag, so I needed to wrap that call inside a Try Block.  The above is for a solitary TOPSPoint.  Now let's put them in a list.  But not just a simple List(Of TOPSPoint).  Let's make it a smarter list with a new custom class that inherits from List(Of TOPSPoint).  We will give it the creative name TOPSPointList.

                                             

                                            Public Class TOPSPointList
                                                Inherits List(Of TOPSPoint)
                                            
                                                Private _instrumentTagMap As Dictionary(Of String, TOPSPoint) = New Dictionary(Of String, TOPSPoint)(comparer:=StringComparer.InvariantCultureIgnoreCase)
                                                Private _tagNameMap As Dictionary(Of String, TOPSPoint) = New Dictionary(Of String, TOPSPoint)(comparer:=StringComparer.InvariantCultureIgnoreCase)
                                                Private _pointIdMap As Dictionary(Of UInt32, TOPSPoint) = New Dictionary(Of UInt32, TOPSPoint)()
                                            
                                                Public Shared Function CreateFromPIPoints(ByVal pipts As PISDK.PointList) As TOPSPointList
                                                    Dim list As New TOPSPointList()
                                                    For Each pipt As PISDK.PIPoint In pipts
                                                        list.Add(pipt)
                                                    Next
                                                    Return list
                                                End Function
                                            
                                                Public Shadows Sub Add(ByVal pipoint As PISDK.PIPoint)
                                                    Add(New TOPSPoint(pipoint))
                                                End Sub
                                            
                                                Public Shadows Sub Add(ByVal topsPoint As TOPSPoint)
                                            
                                                    MyBase.Add(topsPoint)
                                            
                                                    If Not String.IsNullOrWhiteSpace(topsPoint.InstrumentTag) AndAlso Not _instrumentTagMap.ContainsKey(topsPoint.InstrumentTag) Then
                                                        _instrumentTagMap.Add(topsPoint.InstrumentTag, topsPoint)
                                                    End If
                                            
                                                    If Not String.IsNullOrWhiteSpace(topsPoint.TagName) AndAlso Not _tagNameMap.ContainsKey(topsPoint.TagName) Then
                                                        _tagNameMap.Add(topsPoint.TagName, topsPoint)
                                                    End If
                                            
                                                    If topsPoint.PointID <> 0 AndAlso Not _pointIdMap.ContainsKey(topsPoint.PointID) Then
                                                        _pointIdMap.Add(topsPoint.PointID, topsPoint)
                                                    End If
                                                End Sub
                                            
                                                Public Function FindByTagName(ByVal key As String) As TOPSPoint
                                                    Dim value As TOPSPoint = Nothing
                                                    _tagNameMap.TryGetValue(key, value)
                                                    Return value
                                                End Function
                                            
                                                Public Function FindByInstrumentTag(ByVal key As String) As TOPSPoint
                                                    Dim value As TOPSPoint = Nothing
                                                    _instrumentTagMap.TryGetValue(key, value)
                                                    Return value
                                                End Function
                                            
                                                Public Function FindByPointID(ByVal key As UInt32) As TOPSPoint
                                                    Dim value As TOPSPoint = Nothing
                                                    _pointIdMap.TryGetValue(key, value)
                                                    Return value
                                                End Function
                                            
                                            End Class
                                            

                                             

                                            Note we keep some internal dictionaries to aid later for faster lookups.  Any dictionary keyed by String uses a case insensitive comparer.  In a more robust system, I would need to account for Remove, and consider duplicates.  There is also a handy CreateFromPIPoints function that accepts a PISDK.PointList as input.

                                             

                                            Here's a sample Sub to test out the above:

                                             

                                                Public Sub Test()
                                                    Dim sdk = New PISDK.PISDK()
                                                    Dim pida = sdk.Servers.DefaultServer
                                            
                                                    Dim namedValues = New PISDKCommon.NamedValues()
                                                    namedValues.Add("instrumenttag", Nothing)
                                                    namedValues.Add("pointid", 0UI)
                                            
                                                    ' CAUTION: this query asks for ALL tags on the PI Data Archive.  For my test PIDA, I have less than 100 tags.
                                                    Dim piptList = CType(pida, PISDK.IGetPoints2).GetPoints2("tag='*'", namedValues, PISDK.GetPointsRetrievalTypes.useGetPoints)
                                            
                                                    Dim topsPtList = TOPSPointList.CreateFromPIPoints(piptList)
                                            
                                                    Dim lookup1 = topsPtList.FindByTagName("sinuSOID") ' Let's see if its really case insensitive
                                                    Dim lookup2 = topsPtList.FindByPointID(9)
                                            
                                                    EchoPoint(lookup1)
                                                    EchoPoint(lookup2)
                                            
                                                End Sub
                                            
                                                Private Sub EchoPoint(ByVal point As TOPSPoint)
                                            
                                                    If point Is Nothing Then
                                                        Console.WriteLine("TOPSPoint is Nothing")
                                                        Return
                                                    End If
                                            
                                                    Console.WriteLine("TOPSPoint Info:")
                                                    Console.WriteLine($"   PIPoint: {point.PIPoint?.PathName}")
                                                    Console.WriteLine($"   TagName: {point.TagName}")
                                                    Console.WriteLine($"   PointID: {point.PointID}")
                                                    Console.WriteLine($"   InstrumentTag: {point.InstrumentTag}")
                                            
                                                End Sub
                                            

                                             

                                            And here's the sample output to my Console:

                                             

                                            TOPSPoint Info:

                                               PIPoint: \\OBFUSCATED\SINUSOID

                                               TagName: SINUSOID

                                               PointID: 1

                                               InstrumentTag:

                                            TOPSPoint Info:

                                               PIPoint: \\OBFUSCATED\BA:ACTIVE.1

                                               TagName: BA:ACTIVE.1

                                               PointID: 9

                                               InstrumentTag:


                                            UPDATE:

                                             

                                            I should point out that this works only for tags belonging to the same PI Data Archive.  If your PointList refers to tags on 2 different PIDA's, they could have the same TagName or PointID, which would not correctly be mapped in the dictionaries.

                                            1 of 1 people found this helpful
                                              • Re: How to Locate a Specific PointID in a PISDK.PointList Object
                                                pauljohnson

                                                Rick

                                                 

                                                     Thanks for your efforts, much appreciated.  Nothing like some sample code to clarify usage.  I will definitely use this technique if/when we go to production, but bearing in mind time constrictions (Wednesday delivery) and my current workload I think I will go with the simple ListOf solution that I currently have, it proves the concept with decent performance which is all the customer really needs at the moment.   As ever when learning new coding techniques there's always that temptation to 'refactor, refactor, refactor...' :-)   but need to get onto other stuff this week.  Thanks again for your assistance in this matter.

                                                 

                                                Kind Regards

                                                Paul.