11 Replies Latest reply on Apr 21, 2010 4:02 PM by ken

    PISDK behavior: exe vs windows service

    ygalipeau

      good morning everyone,

       

      I am working on a multithread windows service application that use SDK to generate some events in my Ekho application suite. here is the case: i am using the event pipe to subscribe to pi tags (it's currently working with 974 subscriptions for wind turbines and that is great!!!)

       

      when i receive event from the pipe, i need to do some action (including gathering additional pi tag values with the arcvalue method). what i am experiencing is that when i am requesting a pi tag and that pi tag was one of my tag subscribed in the event pipe, i am not able to retreive the value. the pointvalue object is frozen or something else. To add more strangeness to that, this is the case only when the code is running as a windows service. if it's a regular exe, no problem.

       

      i am attaching a very simple project that reproduce this behavior. it's constitued of 3 vb.net project and you can run it as exe and compile and install it as service as well. in the project i am subscribing to the tag BA:Active.1 and when i receive and event for it, i go back to the archive to request it's value. so i you start the UI project and use SMT to insert a new value into ba:active.1 and you open UI\bin\Debug\Log\eeg.log you will see

       

      116 DEBUG 9 [Root] Starting Windows App
      894 INFO 9 [TestPI.PIEventListener] PI Event Pipe was successfully initialize with BA:Active.1
      15967 DEBUG 10 [TestPI.PIEventListener] BA:ACTIVE.1 = Inactive    timestamp : 1271511499
      15970 INFO 6 [TestPI.PIEventListener] Starting thread
      16024 DEBUG 6 [TestPI.PIPointReader] Points read : 1
      16030 DEBUG 6 [TestPI.PIPointReader] BA:ACTIVE.1 = Inactive ********* reading OK
      16030 INFO 6 [TestPI.PIEventListener] Ending thread

       

      which means everything is ok.

       

      now simply run the BuildAndInstall.cmd to compile the same code as a service and install it under the name _Test_Read_PI_. now start it and put a new value for the ba.active.1 tag and the log under WindowsService1\bin\Debug\Log\eeg.log. will show:

       

      90 INFO 4 [Root] Starting service
      869 INFO 4 [TestPI.PIEventListener] PI Event Pipe was successfully initialize with BA:Active.1
      10960 DEBUG 5 [TestPI.PIEventListener] BA:ACTIVE.1 = Active    timestamp : 1271511546
      10963 INFO 4 [TestPI.PIEventListener] Starting thread
      10979 DEBUG 4 [TestPI.PIPointReader] Points read : 1

       

      and its never able to give me the value. and it's the same code. also this is a very simplified version of the real one, but it can easily reproduce the problems. also i know that i have already the value in the event pipe so why reading it again, but it could be 2 different process that don't know each other and one trigger on the TagA and the other one trigger on Tag b, but need the value of Tag A at the trigger time.

       

      do not hesitate if you have question as i know it's hard to explain everything clearly by text.

       

      and also Go Habs Go!

       

      have a nice weekend

       

      yannick galipeau

       

       

       

       

        • Re: PISDK behavior: exe vs windows service
          hanyong

          Hi Yannick,

           

          I changed the codes to create a thread object with Single-Threaded COM Apartment (STA) instead of Multi-Thread COM Apartment (MTA) when using a Threadpool and the service seems to work after that.

           

          Basically the changes are made in PIEventListener.vb (line 105 if I remember correctly. The original line is:

          Threading.ThreadPool.QueueUserWorkItem(New Threading.WaitCallback(AddressOf ReadValueFromPI), ptToread)

           

          And my changes are as follows:

          Dim th as Threading.Thread = new Threading.Thread(Addressof ReadValueFromPI)
          th.SetApartmentState(ApartmentState.STA)
          th.Start(ptToread)

           

          As Steve mentioned in this other post, PI SDK in general is STA (single-threaded COM apartment) based, you would need to take some extra care in working with PI SDK in a MTA threading environment.

            • Re: PISDK behavior: exe vs windows service
              ygalipeau

              Han,

               

              do you know why the exe vs the service behave differently? i am not sure that i can change my real application as everything is base on the threadpool object. i'll have to check that. but i am very curious about the difference in the behavior as it works fine if the requested tag is a different one than the one which in the event pipe. This tend me to think that it's not related to MTA vs. STA.

               

              yannick

                • Re: PISDK behavior: exe vs windows service
                  hanyong

                  Hi Yannick,

                   

                  I probably can't explain the mechanics of Threading working in STA and MTA too well here, because I am still trying to figure my way out for some of these stuff myself. I would still think that this is related to MTA vs STA, as there is a mixture of STA and MTA threads running in the program here.

                   

                  I am actually using the Windows Form that you included in the solution to test the not-Windows Service scenario. The difference in that and running a Windows Service is that the Windows Form's main thread defaults to STA while Windows Service defaults to MTA. If you print out the thread information at 3 points

                  1. when the main thread is creating the Listener
                  2. when new value is received in eventpipe (PIValueHasChanged)
                  3. when Reader is reading a value (Read)

                  We can see that these 3 points are running in different threads point 1 = main thread, point 2 = EventPipeHandler and point 3 is worker thread. Running the program using the Windows Form project or as a Windows service puts the main thread in different compartment state at point 1. At point 2, the thread is always in STA, whereas point 3 is in MTA when you use Threadpool.

                   

                  The problem seems to appear when the SDK objects are shared across threads with different apartment states. An alternative to creating new threads in STA to read value (what I suggested the my earlier post), you can also make the Windows Service's main thread run in STA and you will see that the problem goes away as well.

                   

                  In Service1.vb:

                  <STAThread()> _
                  Protected Overrides Sub OnStart(ByVal args() As String)

                   

                   

                    • Re: PISDK behavior: exe vs windows service
                      MichaelvdV@Atos

                      I recently ran into an issue regarding PISDK and the whole MTA/STA debacle.

                       

                      Being more from the .NET/Java generation, I didn't had much experience with COM exceptions.

                       

                      I found this StackOverflow thread very helpfull:

                       

                      http://stackoverflow.com/questions/127188/could-you-explain-sta-and-mta

                       

                      and also this wikipedia article:

                       

                      http://en.wikipedia.org/wiki/Component_Object_Model#Threading_in_COM

                      • Re: PISDK behavior: exe vs windows service
                        ygalipeau

                        Han

                         

                        what make me think that it is not related to STA vs. MTA is that if the tag that is read when a new value is received in the eventpipe is different than the event pipe tag, it works. i set the eventpipe tag to BA:Active.1 and i request Sinusoid, this work.

                         

                        if i try to read BA:Active.1 when BA:Active.1 change it does not work.

                         

                        thanks

                         

                        yannick

                          • Re: PISDK behavior: exe vs windows service
                            ken

                            Yannick,

                             

                            I pretty sure you have a MTA/STA marshalling problem. When the program runs interacatvely there is an STA UI thread that can handle the marshalling because it has a message pump. When the program runs as a service there is no STA UI thread so no marshalling is going on. When the event comes back in the event pipe, it has a reference to a PIPoint that was created on another thread and needs to be marshalled. That doesn't work in the service. When you say you can request data for another tag, that is consistent since you are instantiating that other tag's PIPoint object on the same thread you are using to request data so no marshalling is needed.

                             

                            One solution would to make your own thread pool of STA threads. If you use the .NET thread pool, my understanding is that the threads are MTA and you can't change it.

                             

                            Ken___

                              • Re: PISDK behavior: exe vs windows service
                                ygalipeau

                                hi ken,

                                 

                                when i mention i can request data for another tag, i am doing exactly the same thing whether it's my event pipe tag or not. the code i am going through is exactly the same in both case. in my reading function, i am creating another pi point object no matter which tag i am using and then calling the arcvalue method with this. i don't know if you took a look at the code i supply but here is the reading value:

                                Dim piSDK As PISDK.PISDK = New PISDK.PISDK()
                                Dim piServer As Server = Nothing
                                piServer = piSDK.Servers(serverName)
                                piServer.Open()

                                Try
                                    Dim pointList As PointList = piServer.GetPoints("tag = '" + tagname + "'")
                                    Dim err As New PISDKCommon.NamedValues

                                    Dim retreivalMode As PISDK.RetrievalTypeConstants = RetrievalTypeConstants.rtInterpolated
                                    Dim piPointValues As PISDK.PointValues = pointList.Data.ArcValue(timestamp, retreivalMode, err)

                                    Dim pointValue As Object

                                    For Each item As PISDK.PointValue In piPointValues
                                       If item.PIPoint.PointType = Global.PISDK.PointTypeConstants.pttypDigital Then
                                           Dim ds As PISDK.DigitalState = DirectCast(item.PIValue.Value, PISDK.DigitalState)
                                           pointValue = ds.Name
                                       Else
                                           pointValue = item.PIValue.Value
                                       End If

                                       _logger.DebugFormat("{0} = {1} ********* reading OK", item.PIPoint.Name, pointValue.ToString)
                                   Next

                                Catch ex As Exception
                                     _logger.Error(String.Format("Unable to get values from PI. Server = {0}   --||--   Point = {1}   --||--   Timestamp = {2}", _
                                                                 tagname, serverName, timestamp), _
                                                  ex)
                                End Try

                                 

                                 

                                as you see, i am creating everything in this routine but when my tagname is the same as one subscribe in the event pipe it don't work. don't hesitate if you have questions,

                                 

                                thanks,

                                 

                                yannick

                                  • Re: PISDK behavior: exe vs windows service
                                    ken

                                    Thanks Yannick,

                                     

                                     My guess is that the original PIPoint is already instantiated in another thread and you are picking it up from the SDK point cache. You have a proxy to the object instead of the object itself.

                                     

                                    I think the solution to your problem (with the least amount of change to your code) is the one that Han recommended on Apr 19 2010 7:46 AM

                                     

                                    Instead of using .NET thread pool

                                     

                                    Threading.ThreadPool.

                                     

                                    create your own STA thread pool.

                                     

                                    Ken___

                                     

                                     

                                      • Re: PISDK behavior: exe vs windows service
                                        hanyong

                                        Ken Coates

                                        My guess is that the original PIPoint is already instantiated in another thread and you are picking it up from the SDK point cache. You have a proxy to the object instead of the object itself.

                                        I would agree with Ken on this, since this problem goes away if either the main thread or worker thread if configured to run in STA. It does seems to that the problem is caused by when the PIPoint is instantiated in one MTA thread and another MTA thread accesses the same object.

                                         

                                        @Yannick, I phrased my statement wrongly in my own previous post:

                                        Han Yong

                                        The problem seems to appear when the SDK objects are shared across threads with different apartment states.

                                        What I meant is above. Sorry if that have caused some confusion.

                                          • Re: PISDK behavior: exe vs windows service
                                            ygalipeau

                                            Ken and Han,

                                             

                                            thanks for your help and feedback. Now just for me to be sure that i understand what is going on:

                                             

                                            when i am adding a point to monitor on the event pipe, i create a point list with BA:Active.1 as the tag and i use this point list for the event pipe:

                                            Dim piPointList As PISDK.PointList = _piServer.GetPoints("tag = '" + tagname + "'")

                                            ' Reference the EventPipe
                                            _piEventPipe = DirectCast(piPointList.Data.EventPipe, PISDK.IEventPipe3)

                                            then when i receive event from the event pipe, even if i create a new point list for the BA:Active.1 when i read my values

                                            Dim pointList As PointList = piServer.GetPoints("tag = '" + tagname + "'")
                                            Dim err As New PISDKCommon.NamedValues

                                            Dim retreivalMode As PISDK.RetrievalTypeConstants = RetrievalTypeConstants.rtInterpolated
                                            Dim piPointValues As PISDK.PointValues = pointList.Data.ArcValue(timestamp, retreivalMode, err)

                                            the sdk will use the pipoint object that i have created at the time I create the Event Pipe and will not use the one instantiated in the reading function?

                                             

                                            And from what you mention Ken, there is no way to prevent this cache re-use mechanism right?

                                             

                                            thanks

                                             

                                            yannick

                                              • Re: PISDK behavior: exe vs windows service
                                                ken

                                                Yannick,

                                                 

                                                The SDK caches PIPoint objects to improve performance. It is expensive to get the point attribute information from the server. There is no way to prevent this caching mechanism, and you wouldn't want to because your application would run much slower if it had to get the point information from the server each time you needed it.

                                                 

                                                When you call PIPoints::Item with a tag name, it will first look to see if already has that point in its cache, if so, it will return a COM pointer to it. If not, it will fetch the point from the server, put the PIPoint object in its cache and return a COM pointer to it.

                                                 

                                                Ken___