Welcome to our new forum
All users of the legacy CODESYS Forums, please create a new account at account.codesys.com. But make sure to use the same E-Mail address as in the old Forum. Then your posts will be matched. Close

Change date range of Trend

ryusoup
2023-02-16
2024-01-17
  • ryusoup - 2023-02-16

    Hi,
    I know showing range of the trend visualization can be controlled with the date range picker and the time range picker.

    Is there any way to control the range of the trend graph through variables? (like with start/end DATE_AND_TIME vars)
    I want to change the range with date/time picker to show historical data.

    Best regards,

     
    πŸ‘
    3
  • ryusoup - 2023-02-17

    I guess the range can be managed using VisuElemsDateTime_Interfaces.IDateRangeSelectorClient.SetCurrentRange() method.
    But I have no idea how to get the instance of the Trend element.
    Does anyone know any way to get the instance by element ID, name or something?

     
    πŸ‘
    1
  • mqi2r

    mqi2r - 2023-02-26

    I too wish to know a way to do this. There is frustratingly no information on setting sane defaults for the date/time range.

     
  • ben1 - 2023-03-03

    I also would love a solution to this. The date range picker is ugly!
    It would be great to be able to input a date range through a text field instead.

     
  • ryusoup - 2023-05-29

    I resolved it myself.
    To achieve this, create a FB which implements VisuElems.IValueChangedListener and Implement the method "ValueChanged".

    METHOD ValueChanged : BOOL
    VAR_INPUT
        itfVisualization    : VisuElems.IVisualisation;
        ...
        pbNewValue  : POINTER TO BYTE;
        ...
    END_VAR
    VAR
        instIDateRangeSelectorClient:VisuElemTrace.IDateRangeSelectorClient;
        instITimeSelectorClient:VisuElemTrace.ITimeSelectorClient;
        i:INT;
        pArrElems:POINTER TO ARRAY[0..10] OF VisuElems.IVisualElement;
        icount:INT;
        liTimestampFrom:LINT;
        liTimestampTo:LINT;
        liTime:LINT;
        str:STRING;
        pDate:POINTER TO DATE;
        dTemp:DATE;
        uiYear:UINT;
        uiMonth:UINT;
        uiDay:UINT;
        std:VisuElemTrace.SYSTIMEDATE;
        stTemp:VisuElemTrace.SYSTIME;
    END_VAR
    
    str := itfVisualization.GetName(FALSE);
    IF str = 'MainVisu' THEN
        // Get Elements in the visu
        pArrElems:=itfVisualization.GetElementArray(nElementCount => icount);
        FOR i:=0 TO MIN(icount - 1, TO_INT(UPPER_BOUND(pArrElems^, 1))) BY 1 DO
            // Get the trend instance
            // The trend instance should be queryable by ITimeSelectorClient and IDateRangeSelectorClient.
            __QUERYINTERFACE(pArrElems^[i], instITimeSelectorClient);
            __QUERYINTERFACE(pArrElems^[i], instIDateRangeSelectorClient);
            IF instITimeSelectorClient <> 0 THEN
                EXIT;
            END_IF
        END_FOR
    
        IF instITimeSelectorClient <> 0 AND instIDateRangeSelectorClient <> 0 THEN
            // get date and span
            // this part can be better
            pDate := pbNewValue;
            dTemp := pDate^;
            DTU.DateSplit(datDate:=dTemp, puiYear:=ADR(uiYear), puiMonth:=ADR(uiMonth), puiDay:=ADR(uiDay));
            std.wYear := uiYear;
            std.wMonth := uiMonth;
            std.wDay := uiDay;
            std.wHour := 0;
            std.wMinute := 0;
            std.wSecond := 0;
            std.wMilliseconds := 0;
            VisuElemTrace.SysTimeRtcConvertLocalToHighRes(pDate:=std, pTimestamp:=stTemp);
            VisuElemTrace.SysTimeRtcConvertHighResToDate(pTimestamp:=stTemp, pDate:=std);
            liTimestampFrom:=VisuElemTrace.TrendStorageConvertToTimestamp(timeDate:=std, uiMicroseconds:=0);
    
            dTemp := dTemp + TIME#1D;
            DTU.DateSplit(datDate:=dTemp, puiYear:=ADR(uiYear), puiMonth:=ADR(uiMonth), puiDay:=ADR(uiDay));
            std.wYear := uiYear;
            std.wMonth := uiMonth;
            std.wDay := uiDay;
            std.wHour := 0;
            std.wMinute := 0;
            VisuElemTrace.SysTimeRtcConvertLocalToHighRes(pDate:=std, pTimestamp:=stTemp);
            VisuElemTrace.SysTimeRtcConvertHighResToDate(pTimestamp:=stTemp, pDate:=std);
            liTimestampTo:=VisuElemTrace.TrendStorageConvertToTimestamp(timeDate:=std, uiMicroseconds:=0);
    
            liTime:=liTimestampTo - liTimestampFrom;
    
            // set date/time range
            instITimeSelectorClient.TimeChanged(liTime:=liTime, xAll:=FALSE);
            instIDateRangeSelectorClient.SetCurrentRange(liFrom:=liTimestampFrom, liTo:=liTimestampTo);
        END_IF
    END_IF
    

    This is my code to change showing date using Date/Time Picker element.
    You can improve above as you like, for example, to listen event from Date Range Picker, to filter the event source element, or to specify one from multiple trend elements.

    Call VisuElems.g_itfValueChangedListenerManager.AddValueChangedListener() to enable the listener.

    Please tell me if you know any better or more simple way.

     
    πŸ‘
    2

    Last edit: ryusoup 2023-05-30
    • mqi2r

      mqi2r - 2023-05-30

      This is excellent. Thank you.

      I have been able to implement it (I modified it from Date to DT as I wish to be able to select the start date and time) except for the part where the pbNewValue is cast into the pDate (pDT in my case) and dereferenced.

      I seem to only get the element in the DT that has changed (so pbNewValue is a pointer to the byte which has changed, if I set the minutes to 45 in the Date/Time Picker I get 45 as the pbNewValue).

      I have had success with having the Date/Time Picker writing out to a global DT variable and changing the dTemp := pDate^ line to dTemp := GVL.StartDateTime. I know this is not good practice referencing global variables from a method like this but cannot work out how the changed DT variable can be referenced from the input parameters to the method.

      VAR_INPUT
          ...
      END_VAR
      
      VAR
          instIDateRangeSelectorClient:VisuElemTrace.IDateRangeSelectorClient;
          instITimeSelectorClient:VisuElemTrace.ITimeSelectorClient;
          pinstVisuFbFrame:POINTER TO VisuElems.VisuFbFrame;
          instVisu:VisuElems.IVisualisation;
          elementInfo:VisuElems.Visu_StructElementInfo;
          i:INT;
          pArrElems:POINTER TO ARRAY[0..10] OF VisuElems.IVisualElement;
          icount:INT;
          liTimestampFrom:LINT;
          liTimestampTo:LINT;
          liTime:LINT;
          str:STRING;
          // pDate:POINTER TO DATE;
          pDate:POINTER TO DT;
          // dTemp:DATE;
          dTemp:DT;
          uiYear:UINT;
          uiMonth:UINT;
          uiDay:UINT;
          uiHour:UINT;
          uiMinute:UINT;
          uiSecond:UINT;
          std:VisuElemTrace.SYSTIMEDATE;
          stTemp:VisuElemTrace.SYSTIME;
      END_VAR
      
      str := itfVisualization.GetName(FALSE);
      IF str = 'TrendyMcTrendface' THEN
          // Get Elements in the visu
          pArrElems:=itfVisualization.GetElementArray(nElementCount => icount);
          FOR i:=0 TO MIN(icount - 1, TO_INT(UPPER_BOUND(pArrElems^, 1))) BY 1 DO
              // Get the trend instance
              // The trend instance should be queryable by ITimeSelectorClient and IDateRangeSelectorClient.
              __QUERYINTERFACE(pArrElems^[i], instITimeSelectorClient);
              __QUERYINTERFACE(pArrElems^[i], instIDateRangeSelectorClient);
              IF instITimeSelectorClient <> 0 THEN
                  EXIT;
              END_IF
          END_FOR
      
          IF instITimeSelectorClient <> 0 AND instIDateRangeSelectorClient <> 0 THEN
              // get date and span
              // this part can be better
              // pDate := pbNewValue;
              // dTemp := pDate^;
              dTemp := GVL.StartDateTime; // I know this is bad practice
              // DTU.DateSplit(datDate:=dTemp, puiYear:=ADR(uiYear), puiMonth:=ADR(uiMonth), puiDay:=ADR(uiDay));
              DTU.DTSplit(dtDateAndTime:=dTemp, puiYear:=ADR(uiYear), puiMonth:=ADR(uiMonth), puiDay:=ADR(uiDay), puiHour:=ADR(uiHour), puiMinute:=ADR(uiMinute), puiSecond:=ADR(uiSecond));
              std.wYear := uiYear;
              std.wMonth := uiMonth;
              std.wDay := uiDay;
              std.wHour := uiHour;
              std.wMinute := uiMinute;
              std.wSecond := uiSecond;
              std.wMilliseconds := 0;
              VisuElemTrace.SysTimeRtcConvertLocalToHighRes(pDate:=std, pTimestamp:=stTemp);
              VisuElemTrace.SysTimeRtcConvertHighResToDate(pTimestamp:=stTemp, pDate:=std);
              liTimestampFrom:=VisuElemTrace.TrendStorageConvertToTimestamp(timeDate:=std, uiMicroseconds:=0);
      
              dTemp := dTemp + TIME#15M;
              // DTU.DateSplit(datDate:=dTemp, puiYear:=ADR(uiYear), puiMonth:=ADR(uiMonth), puiDay:=ADR(uiDay));
              DTU.DTSplit(dtDateAndTime:=dTemp, puiYear:=ADR(uiYear), puiMonth:=ADR(uiMonth), puiDay:=ADR(uiDay), puiHour:=ADR(uiHour), puiMinute:=ADR(uiMinute), puiSecond:=ADR(uiSecond));
              std.wYear := uiYear;
              std.wMonth := uiMonth;
              std.wDay := uiDay;
              std.wHour := uiHour;
              std.wMinute := uiMinute;
              VisuElemTrace.SysTimeRtcConvertLocalToHighRes(pDate:=std, pTimestamp:=stTemp);
              VisuElemTrace.SysTimeRtcConvertHighResToDate(pTimestamp:=stTemp, pDate:=std);
              liTimestampTo:=VisuElemTrace.TrendStorageConvertToTimestamp(timeDate:=std, uiMicroseconds:=0);
      
              liTime:=liTimestampTo - liTimestampFrom;
      
              // set date/time range
              instITimeSelectorClient.TimeChanged(liTime:=liTime, xAll:=FALSE);
              instIDateRangeSelectorClient.SetCurrentRange(liFrom:=liTimestampFrom, liTo:=liTimestampTo);
          END_IF
      END_IF
      
       
      • ryusoup - 2023-05-30

        You firstly tried as followings but it doesn't work, right?

        pDT := pbNewValue;
        dTemp := pDT^;
        

        It worked in my case.
        CODESYS 3.5.17.30 and CODESYS Visualization 4.2.0.0 is my environment.

        Actually, a date time picker element calls the event handler for several times while changing operation, but it triggers the event
        with desired value on the last call. Doesn't it?

         
        • mqi2r

          mqi2r - 2023-05-30

          Yes the element seems to call the handler a number of times but it seems to work fine (when I use a global variable for the DT).

          Yes still no luck for me. The event is called and the trend updates but it just shows the last X minutes rather than from what I select in the picker.

          I'm currently using CODESYS Control Win 3 x64 3.5.18.40 and CODESYS Visualisation 4.3.0.0 although my project/development environment is 3.5.19

          My complete method is below along with some screenshots:

          METHOD ValueChanged : BOOL
          VAR_INPUT
          (* A pointer to the client structure were the event occurred.
           pClient can be 0 when eType = ConditionVariable
           CHECKED_OMIT*)
              pClient : POINTER TO VisuElems.VisuStructClientData;
              (* The visualization from which the value changed event is called.
           Is always set when pClient <> 0
           CHECKED_OMIT*)
              itfVisualization    : VisuElems.IVisualisation;
              (* If the value changed event is called from a dialog this parameter is set (<> 0).
           CHECKED_OMIT*)
              itfVisualizationDialog  : VisuElems.IVisualisationDialog;
              (* The input position in the visualization is a list of element ids.
           A list is necessary because of element in frames. -1 means not set.
           Example: 41,23,-1,-1,…
           Frame element with id 41 in visualization itfVisualization.GetName(TRUE);
           Element with id 23 in the referenced visualization of the frame
           CHECKED_OMIT*)
              paiInputPosition    : POINTER TO ARRAY [0..9] OF INT;
              (* A list of frame indices for the input position. This information is necessary
           to know the referenced visualization.
           Example: 1,-1,-1,…
           In the frame element the second visualization in the list of frame selection was set.
           CHECKED_OMIT*)
              paiInputFrameIndices    : POINTER TO ARRAY [0..9] OF INT;
              (* A pointer to the old value. The pointer is not necessarily the same than the pbVarPointer.
           If a pbVarPointer with a size > 2004 byte is used the old value cannot be stored
           In this case pbOldValue is NULL
           CHECKED_OMIT*)
              pbOldValue  : POINTER TO BYTE;
              (* A pointer to the new value. The pointer is not necessarily the same than the pbVarPointer.
           CHECKED_OMIT*)
              pbNewValue  : POINTER TO BYTE;
              (* A pointer to the variable which was changed. Can be null if pPropertyInfo is set.
           CHECKED_OMIT*)
              pbVarPointer    : POINTER TO BYTE;
              (* A pointer to the property info of the variable which was changed. The value is only 
           set when the value changed event comes from a property.
           CHECKED_OMIT*)
              pPropertyInfo   : POINTER TO VisuElems.PropertyInfo;
              (* The size of the variable which was changed.
           CHECKED_OMIT*)
              dwVarSize   : DWORD;
              (* The type of the variable which was changed.
           CHECKED_OMIT*)
              eTypeClass  : __SYSTEM.TYPE_CLASS;
              (* The type of the value changed event. This type can be used to filter events.
           CHECKED_OMIT*)
              eType   : VisuElems.VisuEnumValueChangedType;
              (* If the value changed event comes from a dialog the dialog id is necessary to
           know the context from which element the dialog was opened. This can be necessary
           to know the corresponding variables of the dialog.
           Normally the following events occur:
           Event with type OpenDialogPositionInfo - To know the element where the dialog was opened.
           Event with type Default - ValueChange event for the changed variables
           Event with type CloseDialogPositionInfo - To know that the dialog was really closed.
           CHECKED_OMIT*)
              dwDialogId  : DWORD;
              (* If the value changed event comes from a key event dwParam1 contains the key code.
           CHECKED_OMIT*)
              dwParam1    : DWORD;
              (* If the value changed event comes from a key event dwParam2 contains the modifier code.
           CHECKED_OMIT*)
              dwParam2    : DWORD;
          END_VAR
          
          VAR
              instIDateRangeSelectorClient:VisuElemTrace.IDateRangeSelectorClient;
              instITimeSelectorClient:VisuElemTrace.ITimeSelectorClient;
              pinstVisuFbFrame:POINTER TO VisuElems.VisuFbFrame;
              instVisu:VisuElems.IVisualisation;
              elementInfo:VisuElems.Visu_StructElementInfo;
              i:INT;
              pArrElems:POINTER TO ARRAY[0..10] OF VisuElems.IVisualElement;
              icount:INT;
              liTimestampFrom:LINT;
              liTimestampTo:LINT;
              liTime:LINT;
              str:STRING;
             // pDate:POINTER TO DATE;
              pDT:POINTER TO DT;
              //dTemp:DATE;
              dTemp:DT;
              uiYear:UINT;
              uiMonth:UINT;
              uiDay:UINT;
              uiHour:UINT;
              uiMinute:UINT;
              uiSecond:UINT;
              std:VisuElemTrace.SYSTIMEDATE;
              stTemp:VisuElemTrace.SYSTIME;
          END_VAR
          
          str := itfVisualization.GetName(FALSE);
          IF str = 'TrendyMcTrendface' THEN
              // Get Elements in the visu
              pArrElems:=itfVisualization.GetElementArray(nElementCount => icount);
              FOR i:=0 TO MIN(icount - 1, TO_INT(UPPER_BOUND(pArrElems^, 1))) BY 1 DO
                  // Get the trend instance
                  // The trend instance should be queryable by ITimeSelectorClient and IDateRangeSelectorClient.
                  __QUERYINTERFACE(pArrElems^[i], instITimeSelectorClient);
                  __QUERYINTERFACE(pArrElems^[i], instIDateRangeSelectorClient);
                  IF instITimeSelectorClient <> 0 THEN
                      EXIT;
                  END_IF
              END_FOR
          
              IF instITimeSelectorClient <> 0 AND instIDateRangeSelectorClient <> 0 THEN
                  // get date and span
                  // this part can be better
                  pDT := pbNewValue;
                  dTemp := pDT^;
                  //dTemp := GVL.StartDateTime;
                  //DTU.DateSplit(datDate:=dTemp, puiYear:=ADR(uiYear), puiMonth:=ADR(uiMonth), puiDay:=ADR(uiDay));
                  DTU.DTSplit(dtDateAndTime:=dTemp, puiYear:=ADR(uiYear), puiMonth:=ADR(uiMonth), puiDay:=ADR(uiDay), puiHour:=ADR(uiHour), puiMinute:=ADR(uiMinute), puiSecond:=ADR(uiSecond));
                  std.wYear := uiYear;
                  std.wMonth := uiMonth;
                  std.wDay := uiDay;
                  std.wHour := uiHour;
                  std.wMinute := uiMinute;
                  std.wSecond := uiSecond;
                  std.wMilliseconds := 0;
                  VisuElemTrace.SysTimeRtcConvertLocalToHighRes(pDate:=std, pTimestamp:=stTemp);
                  VisuElemTrace.SysTimeRtcConvertHighResToDate(pTimestamp:=stTemp, pDate:=std);
                  liTimestampFrom:=VisuElemTrace.TrendStorageConvertToTimestamp(timeDate:=std, uiMicroseconds:=0);
          
                  dTemp := dTemp + TIME#15M;
                  //DTU.DateSplit(datDate:=dTemp, puiYear:=ADR(uiYear), puiMonth:=ADR(uiMonth), puiDay:=ADR(uiDay));
                  DTU.DTSplit(dtDateAndTime:=dTemp, puiYear:=ADR(uiYear), puiMonth:=ADR(uiMonth), puiDay:=ADR(uiDay), puiHour:=ADR(uiHour), puiMinute:=ADR(uiMinute), puiSecond:=ADR(uiSecond));
                  std.wYear := uiYear;
                  std.wMonth := uiMonth;
                  std.wDay := uiDay;
                  std.wHour := uiHour;
                  std.wMinute := uiMinute;
                  VisuElemTrace.SysTimeRtcConvertLocalToHighRes(pDate:=std, pTimestamp:=stTemp);
                  VisuElemTrace.SysTimeRtcConvertHighResToDate(pTimestamp:=stTemp, pDate:=std);
                  liTimestampTo:=VisuElemTrace.TrendStorageConvertToTimestamp(timeDate:=std, uiMicroseconds:=0);
          
                  liTime:=liTimestampTo - liTimestampFrom;
          
                  // set date/time range
                  instITimeSelectorClient.TimeChanged(liTime:=liTime, xAll:=FALSE);
                  instIDateRangeSelectorClient.SetCurrentRange(liFrom:=liTimestampFrom, liTo:=liTimestampTo);
              END_IF
          END_IF
          
           
          • ryusoup - 2023-05-30

            Your issue seems to be the same as mine: the display range does not exceed the recording trend range.
            This appears to be a limitation of the current method...

            In my case, I verified the date range in the trend storage and compared it with the 'timestampfrom' and 'timestampto'. If the requested range falls outside the recorded data, I activated a message to notify the user.

             
  • ben1 - 2023-07-05

    Hi Guru's,

    Could you guys possibly send a sample code or some snips to help with this?
    I am either barking up the wrong tree expecting a different result here or I am missing something that this simple mind can't work out, but I cannot get this to work at all.
    Everything you have shown and all else I believe is needed is there, but changing dates/times changes nothing...

     
    πŸ‘
    1
  • ignat - 2024-01-17

    Hi ryusoup,

    looking for the same functionality.

    I tried to use your code but couldn't figure out all the details.

    • to enable Listener should it be called once or cyclically?
    • the listener should be called with the intance of FB which you described?
    • should it be this instance to be called cyclically too?

    I tried to make it simpler, just force the uiYear/uiMonth etc. but couldn't make it work.

    Could you please share some details how the whole struckture looks like?
    Would be great if you share an example

    UPD: found the issue. Your code works. Thanks for the posts

     

    Last edit: ignat 2024-01-17

Log in to post a comment.