The NewData Module

While it is true that there is more than one method for reading values from an I/O driver, use of the NewData module is recommended for all new tags.

If your tag includes a module named NewData, with the structure similar to that shown in the following example, the I/O driver will use it to send values from the device to the tag.

NewData should have the following five parameters where the last two, Quality and ServiceSync, are optional.

NewData
(
  Addr              { Address we asked for                        };
  TimeStamps        { Single value or 1D array of UTC timestamps  };
  Data              { Single value or 1D array of unscaled data
                      values                                      };
  Quality           { Single value or 1D array of Quality data    };
  ServiceSync       { used to denote that the NewData call was made 
                      as a result of a server synchronization (the 
                      server has just started up and this
                      data has been taken from a running server) };
)

For purposes of comparison, the body of the NewData modules from both the Analog Status tag and the Digital Status tag are reprinted here.

Items to note are:

  • The RawValue is found in the first item of the Data array.
  • The NeedValueUpdate variable scopes to the parent tag module. This is used to alert the tag to the fact that a new value has been received and that the tag's value should be updated.
  • The largest portion of the module provides support for drivers that send history values. These are scaled and logged as required.

NewData() example for reading analog values:

<
{========================== NewData ===============================}
{ Subroutine that receives and processes incoming data from driver }
{ RefreshData in VTSDriver guarantees that TimeStamps is a valid   }
{ time stamp or an array of valid time stamps regardless of the    }
{ validity of Data                                                 }
{==================================================================}
NewData
 (
  Addr               { Address we asked for                        };
  TimeStamps         { Single value or 1D array of UTC timestamps  };
  Data               { Single value or 1D array of unscaled data
                       values                                      };
) 
 [
  I                  { General counting variable                   };
  HistSizeRet        { Size of history list returned               };
  MaxHistTS          { Highest TS in history list                  };
  LastHistPos        { Index of newest data in history list        };
  HistoryValues      { Scaled history array to pass to data logger };
  HistImgTimeStamps  { For history use, 1D array, image of
                       TimeStamps                                  };
  HistImgData        { For history use, 1D array, image of
                      Data                                        };
  NeedValueUpdate = FALSE   { TRUE if the tag needs to recalculate 
                              Value                               };
] 

Main [
  If 1; 
  [
    IfElse (PickValid(Addr == CurrValAddr, 0), Execute(
       { Current value data }
      RawValue = Data[0];
      RawTS    = PickValid(TimeStamps[0], CurrentTime(1)); 
      NeedValueUpdate = TRUE; 
    ); 
    { Else } IfThen (PickValid(Addr == HistoryAddr, 0), 
   { History data might have been Recv'd as single value or as an array }
   { Size of history array }
      IfElse(Valid(HistSizeRet = ArraySize(Data, 0)), Execute(
         { Data is an array }
        HistImgTimeStamps = TimeStamps; 
        HistImgData       = Data; 
      ); 
      { Else } Execute(
         { Data is a single value so copy to a single-value array }
        HistSizeRet          = 1; 
        HistImgTimeStamps    = New(HistSizeRet); 
        HistImgData          = New(HistSizeRet); 
        HistImgTimeStamps[0] = TimeStamps; 
        HistImgData[0]       = Data; 
      )); 

       { If the current tag value comes from history }
      IfThen (ValueFromHist, 
         { Extract RawValue & RawTS from history list if no 
           Current Value address defined }
         { Find the newest history TS }
        MaxHistTS = AMax(HistImgTimeStamps[0], HistSizeRet); 
         { Only use it if greater than current value TS }
        IfThen (MaxHistTS > PickValid(TimeStamp, 0), 
           { Where is this in the array ? }
          LastHistPos = Lookup(HistImgTimeStamps[0], 
                               HistSizeRet, MaxHistTS); 
           { Set our values to the newest history value }
          RawValue = HistImgData[LastHistPos]; 
           { Set the time as well }
          RawTS = HistImgTimeStamps[LastHistPos]; 
          NeedValueUpdate = TRUE; 
        ); 
      ); 

       { Scale the data & log it }
      HistoryValues = New(HistSizeRet); 
      I = 0; 
      WhileLoop(I < HistSizeRet, 
        HistoryValues[I] = Scale(HistImgData[I], UnscaledMin, 
                                 UnscaledMax, ScaledMin, ScaledMax); 
        I++;
      ); 

       { Log history values }
      \HistorianManager\WriteHistory(Root, HistImgTimeStamps, 
                                     HistoryValues); 
    )); 

    IfThen(NeedValueUpdate && !Valid(ManualValue), 
      UpdateValue(ValueFromHist);  
{ The parameter tells UpdateValue to not log Value if it came
           from a history value; this has been logged already }
    ); 

    Return(0); 
  ] 
] 

{ End of AnalogStatus\NewData }
>

NewData() example for reading digital values:

<
{========================= NewData ==================================}
{ Subroutine that receives and processes incoming channel data from  }
{ driver RefreshData in VTSDriver guarantees that TimeStamps is a    }
{ valid time stamp or an array of valid time stamps regardless of    }
{ the validity of Data                                               }
{====================================================================}
NewData
(
  Addr                 { Address we asked for (not used)             };
  TimeStamps           { Single value or 1D array of timestamps      };
  Data                 { Single value or 1D array of unscaled data
                         values                                      };
  Quality              { Single value or 1D array of Quality data    };
)

[
  HistSizeRet          { Size of history list returned               };
  MaxHistTS            { Highest TS in history list                  };
  LastHistPos          { Index of newest data in history list        };
  HistoryValues        { Scaled history array to pass to data logger };
  HistImgTimeStamps    { For history use, 1D array, image of
                         TimeStamps                                  };
  HistImgData          { For history use, 1D array, image of
                         Data                                        };
]

Main [

  If 1;
  [
    IfElse (PickValid(Addr == Bit0Address, 0), Execute(
       { If b0 address }
      RawValue0 = Data[0];
      RawTS     = PickValid(TimeStamps[0] - TimeZone(0), CurrentTime());
    );
    { Else - b1 address } IfElse(PickValid(Addr == b1ValAddr, 0), Execute(
      RawValue1 = Data[0];
      RawTS     = PickValid(TimeStamps[0] - TimeZone(0), CurrentTime());
    );
    { Else } IfThen (PickValid(Addr == HistoryAddr, 0), { History Address }
     { History data might have been Recvd as single value or as an array }
     { Size of history array }
      IfElse(Valid(HistSizeRet = ArraySize(Data, 0)), Execute(
         { Data is an array }
        HistImgTimeStamps = TimeStamps;
        HistImgData       = Data;
      );
      { Else } Execute(
         { Data is a single value so copy to a single-value array }
        HistSizeRet          = 1;
        HistImgTimeStamps    = New(HistSizeRet);
        HistImgData          = New(HistSizeRet);
        HistImgTimeStamps[0] = TimeStamps;
        HistImgData[0]       = Data;
      ));

       { If the current tag value comes from history }
      IfThen (ValueFromHist,
         { Extract RawValue & RawTS from history list if required }
         { Find the highest history TS }
        MaxHistTS = AMax(HistImgTimeStamps[0], HistSizeRet);
         { Only use it if greater than current value TS }
        IfThen (MaxHistTS - TimeZone(0) > PickValid(TimeStamp, 0),
           { Where is this in the array ? }
          LastHistPos = Lookup(HistImgTimeStamps[0], HistSizeRet, 
                               MaxHistTS);
           { Set our value to the newest history value }
          RawValue0 = HistImgData[LastHistPos];
          RawTS     = HistImgTimeStamps[LastHistPos] - TimeZone(0);
        );
      );

       { Invert the data & log it  }
      HistoryValues = New(HistSizeRet);
       { Make a copy of the data array. }
      ArrayOp2(HistoryValues[0], HistImgData[0], HistSizeRet, 0);

       { Some data massage required... }
       { Force all data to 0/1 values. 
          This first step actually returns the inverted data. }
      ArrayOp1(HistoryValues[0], HistSizeRet, 0, 12 {==});
      IfThen (!InvertInput,
         { Invert the data back to normal if not inverted }
        ArrayOp1(HistoryValues[0], HistSizeRet, 0, 12 {==});
      );

       { Log history values }
      \HistorianManager\WriteHistory(Root, HistImgTimeStamps,
                                     HistoryValues);
    )));

    Return(0);
  ]
]
{ End of NewData }
>