13 Replies Latest reply on Sep 2, 2013 12:48 PM by Gregor

    Trend max time markers

      Let's say I know the pixel width of a trend symbol, and I know the time range of the trend, and I selected "partial timestamp" on the trend symbol configuration...is the algorithm for where the max grid times are placed known - or can it be shared by OSIsoft? For each timestamp shown I want to show some other reference data alongside the timestamp, but not on the trend itself. I can handle the VBA side of things, just need a reliable way of placing the reference data next to the timestamps no matter what the time range of the trend.

        • Re: Trend max time markers
          ldieffenbach

          Hi Rhys, I've asked some of our developers familiar with the inner workings of trends to weigh in. As you would imagine, the algorithms are complex and I don't know them well enough to respond directly.

           

          Regards,

           

          Laurie

            • Re: Trend max time markers
              dhollebeek

              The answer is, yes there is an algorithm, and no I don't think it can't be predicted without access to properties not exposed by automation.  The best approximation would be (assuming the legend is there) to take the longest tag name or tag value or UOM and use the legend font (main trend font) and size to calculate the width of the legend, and then there are 2 pixels on each side of the legend.  Coming from the right edge, that should get you close to the "now" line.  (all this is in logical pixels of course, but so are the display coordinates).  If there's no legend, it should be either 2 or 4 pixels in (I forget how many border paddings there are there).

                • Re: Trend max time markers

                  Thanks Laurie!

                   

                  David, thanks for the input. So I'm only interested right now in a stripped down Trend, vertical axis inside, no legend or eng units or tag name or value. The trend plot fills the symbol minus the padding that you mention. However, what I am angling for is how you determine the number of max grid lines (and corresponding time stamps) you show for different time ranges. For example, on 1 hour I see 12 where each major grid line represents 5 minutes. For 2 hours I see 4 major markers (30 min interval), 3 hours = 6 markers @ 30 min interval up to and including 6 hours that uses 30 minute intervals. 7 hours+ switches to 1 hour intervals, and then it depends on the width of the trend + font to which of the grid timestamps actually show a timestamp (switches to every other one when too small). So if you have those intervals to time range ratio details it will save me some time figuring it out.

                    • Re: Trend max time markers
                      dhollebeek

                      Ah, beware ... PB 2012 does that slightly differently than previous versions.  Beyond that, the algorithm is more than 500 lines and may well be proprietary.  Basically, it depends on how many "round numbers" of "smallest meaningful intervals" taking into account that the number of intervals and/or scale of the intervals may be decreased by the width of each time label (which may vary from one label to the next).

                       

                      *** Abandon all hope all ye who try to reproduce this in VBA ***

                       

                      :D

                        • Re: Trend max time markers

                          David Hollebeek

                          *** Abandon all hope all ye who try to reproduce this in VBA ***

                           

                          Now that sounds like a challenge!....must....not....take....on....challenge....too....busy....with....other...work....

                           

                           

                           

                          ....oh dear, I may have to have a crack at this at some point this week. No sleep penciled in for Friday.

                            • Re: Trend max time markers
                              ldieffenbach

                              Thanks for your typical helpfulness, David. I had asked our internal trend experts to weigh in, but I see you dove right in.

                               

                              Rhys, try to remember you have a family :-)

                               

                              They probably like to see you every once in a while...awake.

                                • Re: Trend max time markers
                                  Lonnie Bowling

                                  What, you mean we are not Rhys' family!!??

                                    • Re: Trend max time markers
                                      mlemley

                                      Although you can accomplish your goal of seeing data placed next to each timestamp of a trend using the partial timestamp layout, we believe you’ll run into many problems.

                                       

                                      The ProcessBook time gridline placement algorithm hasn’t changed in several years (10+?). But, you cannot depend on the interval layout remaining the same between versions of ProcessBook.

                                       

                                      There is some inconsistency with time label object avoidance – the trend has logic to skip a time label (but not the gridline) if a label overlaps other text. The object avoidance logic has changed with different versions of PB. I have no details on the inconstancies of the time label object avoidance. The size of the time labels may change based on culture, font and time range.

                                       

                                      ProcessBook tries very hard to maintain layout of a trend between versions. However, layout changes do occur resulting in portions of the trend being a pixel or two off between versions. Pixel location of a grid may change between ProcessBook versions.

                                       

                                      Below, I’ve provided the algorithm for gridline placement:

                                       

                                      PB 2012

                                      Plot   Duration

                                      Major interval

                                      Minor   interval

                                      <   .00000001 seconds 

                                        .0000000025   seconds                        

                                       .000000001   seconds

                                      <   .0000001  seconds 

                                        .000000025   seconds                         

                                       .00000001   seconds

                                      <   .000001   seconds 

                                        .0000025   seconds                           

                                       .0000001   seconds

                                      <   .00001    seconds 

                                        .0000025   seconds                           

                                       .000001   seconds

                                      <   .0001     seconds 

                                        .000025   seconds                            

                                       .00001   seconds

                                      <   .001      seconds 

                                        .00025   seconds                             

                                       .0001   seconds

                                      <   .01       seconds 

                                        .0025   seconds                              

                                       .001   seconds

                                      <   .1        seconds 

                                        .025   seconds                               

                                       .01   seconds

                                      <   1         seconds 

                                        .25   seconds                                

                                       .01   seconds

                                      <   10        seconds 

                                        5   seconds                                  

                                       .1   seconds

                                      <   30 seconds

                                        5   seconds                        

                                       1   seconds

                                      <   1 minutes 

                                       30   seconds                        

                                       5   seconds

                                      <   5 minutes 

                                        1   minutes                        

                                       30   seconds

                                      <   15 minutes

                                        5   minutes                        

                                       1   minute

                                      <   1 hour    

                                       30   minutes                        

                                       5   minutes

                                      <   6 hours   

                                        1   hour                            

                                       30   minutes

                                      <   1 day     

                                        4   hours                          

                                        1   hour

                                      <   2 days    

                                        6   hours                          

                                        2   hour

                                      <   4 day     

                                       12   hours                          

                                        4   hour

                                      <   4 weeks   

                                        1   week (7 days)                  

                                        1   day

                                      <   12 weeks  

                                        4   weeks (28   days)                

                                        1   week (7 days)

                                      <   2 years   

                                       12   weeks (84   days)                

                                        4   weeks (28 days)

                                      <   5 years   

                                        1   year                           

                                       12   weeks (84 days)

                                      >   5 years   

                                        4   years                           

                                        1   year

                                       

                                        • Re: Trend max time markers
                                          ldieffenbach

                                          Thanks for providing the detail, Mike.

                                           

                                          Lonnie...we ARE Rhys' family, but not the ones who live with him. :-)

                                            • Re: Trend max time markers

                                              Mike...that is perfect, exactly what I needed! I owe you a pitcher of beer! (Laurie can cash in a drink coupon too for giving you a nudge.)

                                               

                                              I've noticed the skipping time labels, also the missing time labels where where the zoom controls are placed. However, I think I can get it to 90% completeness which will do what I wanted for now. The culture, font & size are all under my control for the reference data so I can apply a similar yet simpler avoidance logic.

                                               

                                              I'll post up the result when I'm done to show it in action.

                                                • Re: Trend max time markers

                                                  Happy to report I'm not far away now, just looking at a primitive avoidance algorithm, although I may not need it because I don't intend to allow the trends to be resized.

                                                   

                                                  I can successfully clamp reference data to a trend of any orientation (horizontal, vertical end time up & vertical end time down) for relative time from 0.00000001 to 2 years trend duration (I didn't bother testing beyond that). For ProcessBook 2012 some of the interval checks had to be equal to the bands and not just less than, but I have that all working fine and matching what the trend produces. Mike that table you provided was the key.

                                                   

                                                  The reference data even follows the time labels as they move if you have a snapshot trend .

                                                   

                                                  Code and screenshots later when I get a chance...

                                                    • Re: Trend max time markers

                                                      Okay so I avoided the avoidance algorithm for now.

                                                       

                                                      In case anyone wants to have a play with this the code is below and here are some screenshots of it in action. All you need is a display, add reference to PI SDK Type Libraries (Timeserver and Common), add a trend to the display removing all properties like "Tag name", "Server Name", "Description", "Engineering Units", "Value" etc and name it "Trend1". Set the timestamp to "partial" or "relative". Add the below code to the display and away you go. Change the time range of the trend, change the size, change the type (horizontal/vertical), etc. Hopefully works as smooth for you as my test does. It is far from a finished thing (code is messy, my OCD doesn't like it!), just some Friday fun with ProcessBook. I'll concentrate on modifying it to do what I need next, you'll not see that part though...

                                                       

                                                      Who said you can't do it with VBA  Means I can sleep tonight now

                                                       

                                                      pbtrendref1.jpgpbtrendref2.jpgpbtrendref3.jpgpbtrendref4.jpgpbtrendref5.jpg

                                                       

                                                       

                                                       

                                                       

                                                       

                                                       

                                                       

                                                       

                                                       
                                                      Private intervalsAlreadyLoaded As Integer
                                                      
                                                      Sub RemoveClampedText(ByVal sTrend As String)
                                                      On Error Resume Next
                                                      Dim i As Integer
                                                      For i = 1 To 100
                                                          Symbols.Remove Symbols(sTrend & "_" & i).Name
                                                          If Err.Number <> 0 Then Exit For
                                                      Next i
                                                      End Sub
                                                      Sub AddClampedText(ByVal sTrend As String, ByVal iIntervals As Integer)
                                                      Dim i As Integer
                                                      For i = 1 To iIntervals
                                                          Dim t As Text
                                                          Set t = Symbols.Add(pbSymbolText)
                                                              t.Name = sTrend & "_" & i
                                                              t.Contents = "REF" & i 'Obviously go get external data if you want, need to calculate the time on the trend based on the interval & trend end time.
                                                              t.BackgroundColor = -1
                                                              t.LineColor = pbBlack
                                                          Set t = Nothing
                                                      Next i
                                                      End Sub
                                                      
                                                      Sub ClampReferenceDataToTrend()
                                                      
                                                      Dim t As Trend
                                                      Set t = ThisDisplay.Symbols("Trend1")
                                                      
                                                      Dim trendSeconds As Double: trendSeconds = TotalSeconds(t.StartTime, t.EndTime)
                                                      Dim intervals As Integer: intervals = NumberOfIntervals(trendSeconds, t.PlotTimeStyle)
                                                      Dim trendSecondsInterval As Double: trendSecondsInterval = trendSeconds / intervals
                                                      Dim absTime As New DynamicTime: absTime.InputString = t.EndTime
                                                      
                                                      If intervals <> intervalsAlreadyLoaded Then
                                                          RemoveClampedText t.Name
                                                          AddClampedText t.Name, intervals
                                                          intervalsAlreadyLoaded = intervals
                                                      End If
                                                      
                                                      Dim offset As Double: offset = 0
                                                      Dim pixelInterval As Double: pixelInterval = IIf(t.Orientation = pbTrendEndTimeRight, t.Width / intervals, t.Height / intervals)
                                                      
                                                      Select Case trendSecondsInterval
                                                          Case Is < 1: offset = 0
                                                          Case 1 To 60: offset = (1 - (CDbl(Second(absTime.LocalDate) Mod trendSecondsInterval) / CDbl(trendSecondsInterval))) * pixelInterval - 4
                                                          Case Is <= 1800: offset = (1 - (((Minute(absTime.LocalDate) * 60 + Second(absTime.LocalDate)) Mod trendSecondsInterval) / trendSecondsInterval)) * pixelInterval - 4
                                                          Case Is <= 3600: offset = (1 - (((Hour(absTime.LocalDate) * 3600 + Minute(absTime.LocalDate) * 60 + Second(absTime.LocalDate)) Mod trendSecondsInterval) / trendSecondsInterval)) * pixelInterval - 4
                                                          Case Is <= 7200: offset = (1 - (((3600 + Hour(absTime.LocalDate) * 3600 + Minute(absTime.LocalDate) * 60 + Second(absTime.LocalDate)) Mod trendSecondsInterval) / trendSecondsInterval)) * pixelInterval - 4
                                                          Case Is <= 14400: offset = (1 - (((3600 + Hour(absTime.LocalDate) * 3600 + Minute(absTime.LocalDate) * 60 + Second(absTime.LocalDate)) Mod trendSecondsInterval) / trendSecondsInterval)) * pixelInterval - 4
                                                          Case Is <= 86400: offset = (1 - (((3600 + Hour(absTime.LocalDate) * 3600 + Minute(absTime.LocalDate) * 60 + Second(absTime.LocalDate)) Mod trendSecondsInterval) / trendSecondsInterval)) * pixelInterval - 4
                                                          Case Is <= 604800: offset = (1 - (((3600 + Hour(absTime.LocalDate) * 3600 + Minute(absTime.LocalDate) * 60 + Second(absTime.LocalDate)) Mod trendSecondsInterval) / trendSecondsInterval)) * pixelInterval - 4
                                                          Case Is <= 2419000: offset = 0 'Not too interested just yet in large date ranges, should finish it for completeness...
                                                      End Select
                                                      
                                                      Dim txt As Text
                                                      Dim iInterval As Integer
                                                      If t.Orientation = pbTrendEndTimeRight Then
                                                          ' Horizontal trend, clamp to the bottom...
                                                              For iInterval = 1 To intervals
                                                                  Set txt = Symbols(t.Name & "_" & iInterval)
                                                                      txt.Top = t.Top - t.Height - 2
                                                                      If iInterval = 1 Then
                                                                          txt.Left = t.Left + t.Width - pixelInterval - (txt.Width / 2) + offset - 2
                                                                      Else
                                                                          txt.Left = Symbols(t.Name & "_" & (iInterval - 1)).Left - pixelInterval
                                                                      End If
                                                                  Set txt = Nothing
                                                              Next iInterval
                                                      Else
                                                          ' Vertical trend, clamp to the left.
                                                          ' Top or Bottom end time have same intervals, just different chronological orientation
                                                          For iInterval = 1 To intervals
                                                                  Set txt = Symbols(t.Name & "_" & iInterval)
                                                                      txt.Left = t.Left - txt.Width - 5
                                                                      If iInterval = 1 Then
                                                                          If t.Orientation = pbTrendEndTimeBottom Then
                                                                              txt.Top = t.Top - t.Height + pixelInterval + (txt.Height / 2) - offset + 2
                                                                          ElseIf t.Orientation = pbTrendEndTimeTop Then
                                                                              txt.Top = t.Top - pixelInterval + (txt.Height / 2) + offset + 2
                                                                          End If
                                                                      Else
                                                                          If t.Orientation = pbTrendEndTimeBottom Then
                                                                              txt.Top = Symbols(t.Name & "_" & (iInterval - 1)).Top + pixelInterval
                                                                          ElseIf t.Orientation = pbTrendEndTimeTop Then
                                                                              txt.Top = Symbols(t.Name & "_" & (iInterval - 1)).Top - pixelInterval
                                                                          End If
                                                                          
                                                                      End If
                                                                  Set txt = Nothing
                                                          Next iInterval
                                                      End If
                                                      
                                                      Set t = Nothing
                                                      
                                                      End Sub
                                                      
                                                      Private Function TotalSeconds(ByVal StartTime As String, ByVal EndTime As String) As Double
                                                      
                                                      Dim time As New DynamicTime
                                                      
                                                      time.InputString = StartTime
                                                      Dim startD As Date: startD = time.LocalDate
                                                      
                                                      time.InputString = EndTime
                                                      Dim endD As Date: endD = time.LocalDate
                                                      
                                                      TotalSeconds = DateDiff("s", startD, endD)
                                                      
                                                      End Function
                                                      
                                                      Private Function NumberOfIntervals(ByVal TotalSeconds As Double, ByVal timeStyle As pbPlotTimeStyle) As Integer
                                                      
                                                      Select Case TotalSeconds
                                                          Case Is <= 0.00000001: NumberOfIntervals = (0.00000001 / IIf(timeStyle = pbPTSFull, 0.0000000025, 0.000000001))
                                                          Case Is <= 0.0000001: NumberOfIntervals = (0.0000001 / IIf(timeStyle = pbPTSFull, 0.000000025, 0.00000001))
                                                          Case Is <= 0.000001: NumberOfIntervals = (0.000001 / IIf(timeStyle = pbPTSFull, 0.00000025, 0.0000001))
                                                          Case Is <= 0.00001: NumberOfIntervals = (0.00001 / IIf(timeStyle = pbPTSFull, 0.0000025, 0.000001))
                                                          Case Is <= 0.0001: NumberOfIntervals = (0.0001 / IIf(timeStyle = pbPTSFull, 0.000025, 0.00001))
                                                          Case Is <= 0.001: NumberOfIntervals = (0.001 / IIf(timeStyle = pbPTSFull, 0.00025, 0.0001))
                                                          Case Is <= 0.01: NumberOfIntervals = (0.01 / IIf(timeStyle = pbPTSFull, 0.0025, 0.001))
                                                          Case Is <= 0.1: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 0.025, 0.01))
                                                          Case Is < 1: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 0.25, 0.01))
                                                          Case Is < 10: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 5, 0.1))
                                                          Case Is <= 30: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 5, 1))
                                                          ' 1 Minute
                                                          Case Is <= 60: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 30, 5))
                                                          ' 5 Minutes
                                                          Case Is <= 300: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 60, 30))
                                                          ' 15 Minutes
                                                          Case Is <= 900: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 300, 60))
                                                          ' 1 Hour
                                                          Case Is <= 3600: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 1800, 300))
                                                          ' 6 Hours
                                                          Case Is <= 21600: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 3600, 1800))
                                                          ' 1 Day
                                                          Case Is <= 86400: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 14400, 3600))
                                                          ' 2 Days
                                                          Case Is <= 172800: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 21600, 7200))
                                                          ' 4 Days
                                                          Case Is < 345600: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 43200, 14400))
                                                          ' 4 Weeks
                                                          Case Is <= 2419200: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 604800, 86400))
                                                          ' 12 Weeks
                                                          Case Is <= 7258000: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 2419000, 604800))
                                                          ' 2 Years
                                                          Case Is <= 63110000: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 7258000, 2419000))
                                                          ' 5 Years
                                                          Case Is <= 157800000: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 31560000, 7258000))
                                                          ' > 5 Years
                                                          Case Is > 157800000: NumberOfIntervals = (TotalSeconds / IIf(timeStyle = pbPTSFull, 126200000, 31560000))
                                                      End Select
                                                      
                                                      End Function
                                                      
                                                      Private Sub Display_DataUpdate()
                                                          ClampReferenceDataToTrend
                                                      End Sub
                                                      
                                                      Private Sub Trend1_DataRefresh()
                                                      ClampReferenceDataToTrend
                                                      End Sub
                                                      
                                                      Private Sub Trend1_DataUpdate(ByVal ntrace As Integer)
                                                      ClampReferenceDataToTrend
                                                      End Sub
                                                      
                                                      Private Sub Trend1_Move()
                                                      ClampReferenceDataToTrend
                                                      End Sub
                                                      
                                                      Private Sub Trend1_TimeRangeChange(ByVal StartTime As String, ByVal EndTime As String)
                                                      ClampReferenceDataToTrend
                                                      End Sub
                                                      
                                                      Private Sub Trend1_Zoom()
                                                      ClampReferenceDataToTrend
                                                      End Sub