Main Page | Features | Central Services | csv-Files | Types | Transfer | Access | API-C | API-.NET | API-Java | Examples | Downloads
page generated on 05.05.2024 - 04:45
Timestamps

Time stamps and other data stamps are used to tag all data elements transfered throughout the control system, and are used for synchronization and correlation.

Standard Timestamps (UTC)

The standard timestamp in use in most control systems is Coordinated Universal Time (UTC),

  • C, C++: traditionally a 4-byte long integer (seconds since Jan. 1, 1970 GMT). In 64-bit compilers the same seconds since Jan. 1, 1970 GMT is represented as an 8-byte long integer (i.e. type time_t is 8 bytes rather than 4).
  • Java: 8-byte long (milliseconds since Jan. 1, 1970 GMT).

Maintaining the timestamp in this format has the following advantages:

  • Most compact -> 4 bytes (or 8 bytes) compared to a 26 character string!
  • Monotonically increasing (no Daylight Savings Time problems).
  • easily accepted in trend plots. In ACOP use GraphStyle TIME_LIN or TIME_LOG.
  • Many tools which render into the local Time Zone in human-readible form:

In C, C++ you might write:

...
time_t timestamp;
char *timestampstring;
timestamp = time();
timestampstring = ctime(timestamp);
printf("the current time is : %s",timestampstring);
...

which gives the following output:

"Mon Jan 10 12:08:01 2005"

In VB you might write:

Dim timestamp As Long
Dim timestampstring As String * 26
timestamp = utils.UNIXTIME
timestampstring = utils.ASCTIME(timestamp)
Label1.Caption = "Timestamp is " + timestampstring

which gives the following output:

"Mon Jan 10 12:08:01 2005"

In Java you might write:

long timestamp = System.currentTimeMillis();
String timestampstring = new Date(timestamp).toString();
System.out.println("the timestamp is : " + timestampstring);

which gives the following output:

"Mon Jan 10 12:08:01 CET 2005"

  • If the Time Zone is included in the string there are no ambiguities

Log files should include the Time Zone in the date format, but the ansi standard calls do not do this (ctime(), asctime()), in contrast to java calls which does. Calls to feclog() will in any event make a full evaluation of the TINE timestamp to include both milliseconds and the TIME ZONE in effect.

This leads to an immediate "international settings" problem, as the TIME ZONE variables themselves are not standardized. By default, TINE uses the Central European Time signature as follows "CET" when standard time is in effect, and "CDT" when daylight time is in effect, i.e. a three letter abbreviation so as to preserve the time string length. For institutes NOT in Western Europe, these time zone strings can be set via environment variable as follows

  • STD_TIME_STR sets the Time Zone string for Standard Time
  • DST_TIME_STR sets the Time Zone string for daylight Time
  • findDaylightHook() can be used to supply a hook function to be used in establishing the local rules for determining whether daylight savings time is in effect or not (for those OSes which do not do this automatically).

TINE Timestamps

All Data have a timestamp.

  • in non-Java applications, TINE uses a modified UTC where an 8-byte double is used to contain the normal UTC seconds since Jan. 1, 1970 plus milliseconds as the decimal part of the double.
  • in Java applications the java 8-byte long variable giving milliseconds since Jan. 1, 1970 can be used, although you might want to continue using the 8-byte double used on the other platforms.
  • The TINE Central archiver and local history subsystems generally use the a TINE timestamp (double) when storing data to disk. TINE formats such as CF_FLTINT can be used to retrieve an array of data-timestamp pairs from the central archive server and suffice in most cases. Typically data can is retrieved as a CF_DBLDBL pair (data + double timestamp). If the data were store with a high resolution timestamp then the returned timestamp will reflect this, otherwise the fractional part of the double timestamp will be zero.

APIs:

  • server-side:
    • C, C++:
      • getDataTimeStamp(); retrieves the current TINE data timestamp (as determined by the local clock plus synchronization offset).
      • setDataTimeStamp(double ts); sets the current TINE data timestamp to the value given in the input parameter. The TINE data timestamp then applies to any data to be returned from the current call into the equipment module.
    • .NET:
      • TDataType.GetDataTimeStamp() retrieves the current TINE data timestamp (as determined by the local clock plus synchronization offset).
      • TDataType.SetDataTimeStamp(double timestamp) sets the current TINE data timestamp to the value given in the input parameter.
    • Java:
      • The static method TDataType.getDataTimeStamp() retrieves the current TINE data timestamp (as determined by the local clock plus synchronization offset).
      • The setDataTimeStamp() method of the TDataType object should be used to assign a timestamp to a data object.
    • C, C++ Examples:
double hwDataTimeStamp = 0;
void srvBackgroundIOLoop(void)
{
// read hardware
...
// set the data timestamp for these data:
hwDataTimeStamp = getDataTimeStamp();
// etc.
}
ini eqm(char *devName,char * devProperty,DTYPE *dout,DTYPE *din, short access)
{
int devnr,prpid;
// ...
devnr = GetDeviceNumberEx(EQPMODNAME,devName,devProperty);
if (devnr < 0) return illegal_equipment_number;
prpid = GetPropertyId(EQPMODNAME,devProperty);
switch (prpid)
{
//...
case PRP_HW_ID:
// return the data timestamp appropriate for these data:
setDataTimeStamp(hwDataTimeStamp);
if (dout->dArrayLength == 0 || dout->dArrayLength > NUMVALUES) return out_of_range;
if (access&CA_WRITE && din == NULL) return illegal_read_write;
if ((cc=putValuesFromFloat(dout,dbuf,NUMVALUES)) != 0) return cc;
return 0;
// ...
}
}
  • Java Examples:
public class SineDevice extends TDevice
{
public static final int DATASIZE = 1024;
public static final double PHASEDIFF = 2 * Math.PI / 100.0;
private double frequency = 1;
private double amplitude = 1;
private double[] myData;
private double phase = 0.0f;
private double noise = 0.05;
private double timestamp = 0;
public double getTimestamp()
{
return timestamp;
}
public void setTimestamp(double timestamp)
{
this.timestamp = timestamp;
}
// .... other methods omitted ....
public void update()
{
clearAlarm(512);
incrementPhase();
for (int i = 0; i < 1024; i++)
myData[i] = amplitude * Math.sin(phase + frequency * 6.28 * ((double) i / 1024.0)) + noise * Math.random();
if (amplitude > 100) setAlarm(512); // amplitude too high !
this.setTimestamp(TDataTime.getDataTimeStamp()); // < record the timestamp !
}
}
public class SineEquipmentModule extends TEquipmentModule
{
// .... other methods omitted ....
private void registerProperties()
{ // all property information (except the handlers) from exports.csv file
getExportInformationFromFile();
// attach the property handlers ...
attachPropertyHandler("Sine",new TPropertyHandler()
{
protected int call(String devName, TDataType dout, TDataType din, TAccess devAccess)
{
return readSine(devName, dout);
}
});
// ... + other property handlers ...
}
private int readSine(String devName, TDataType dout)
{
SineDevice device = findDevice(devName);
if (device == null) return TErrorList.device_not_connected;
info[device.devNumber].numberCalls[0]++;
// set the data timestamp to that when the data was taken!
dout.setDataTimeStamp(device.getTimestamp());
return dout.putData(device.getSine());
}
}
  • client-side:
    • C, C++:
      • getCurrentDataTimeStamp(int LinkId); retrieves the current TINE data timestamp for the link handle give. If the data are obtained via a synchronous call, then the TINE data timestamp should be retrieved directly from the DTYPE object (which is still valid following the call).
    • VB:
      • GetDataTimeStamp() method of the TDataType object returns the current TINE data timestamp associated with the data object.
    • Java:
      • getDataTimeStamp() method of the TDataType object returns the current TINE data timestamp associated with the data object.
    • C, C++ Examples:
int linkId = -1;
float sindata[100];
// asynchronous link:
int startupRoutine(void)
{
DTYPE dout;
// ...
// start a data link here
dout.dArrayLength = 100;
dout.dFormat = CF_FLOAT;
dout.data.fptr = sindata;
linkId = AttachLink("/TEST/SINE/SINE_DEV_0","SINE",&dout,NULL,CA_READ,1000,showlink,CM_POLL);
// etc.
}
void showlink(int id, int cc)
{
// get the data time stamp:
double ts = getCurrentDataTimeStamp(id);
printf("Data valid on : %s",ctime((time_t)ts));
}
// synchronous call:
int getSineCurve(void)
{
DTYPE dout;
float lcldata[100];
double ts;
int cc;
dout.dArrayLength = 100;
dout.dFormat = CF_FLOAT;
dout.data.fptr = lcldata;
cc = ExecLink("/TEST/SINE/SINE_DEV_0","SINE",&dout,NULL,CA_READ);
ts = dout.dTimestamp;
printf("Data valid on : %s",ctime((time_t)ts));
// etc.
return cc;
}
  • Java Examples:
// ...
float[100] val = new float[100];
TDataType dout = new TDataType(val);
TDataType din = new TDataType();
TLink sin = new TLink("/TEST/SINE/SINEDEV_0","SINE",dout,din,TAccess.CA_READ);
int cc = sin.execute();
System.out.println("data timestamp: " + new Date(dout.getDataTimeStamp()).toString());
// etc. ...

TINE Data Stamps

TINE data sets also carry two kinds of integer data stamps with then. The first is a "system" data stamps which is applied systematically. The second is a "user" data stamp supplied at the server side by the server at the time of the call. If never utilized, these data stamps will of course contain only the integer value '0'.

The system data stamp can be set explicitly by calling SetSystemDataStamp() (C code), or by calling the setSystemDataStamp() method of TDataType (java). In practice this value is typically set systematically, for instance by supplying a 'cycle number' from a "CYCLER" server (for those facilities with such a concept) which multicasts the cycle number as an external network global. The system data stamp for a particular data stamp can be obtained, if needed, by calling one of GetSystemDataStamp() or GetSystemDataStampFromCallbackId(), or by calling GetDataFromCallbackId() (C code), in which case a DTYPE object is filled in with all information pertaining to the incoming data set. or by calling the getSystemDataStamp() method of TDataType (java). Systematically applied data stamps (such as a cycle number or pulse number) are a good way to correlate data sets, especially as these extra data stamps can also be archived along with the data.

The device server can tag each data set with an integer value of its own choosing by setting the dStamp field in the DTYPE output object within an equipment modules dispatch handler (C code) or calling the setUserDataStamp() method of the TDataType object in java. A caller can retrieve this values by calling one of GetDataStamp(), GetDataStampFromCallbackId(), or GetDataFromCallbackId() (C code) or by calling the getUserDataStamp() method of TDataType (java).

Synchronization

The servers in the control system generally run in a distributed manner, meaning that they can run stand-alone are not tied to a central server. One the other hand they will make use of central services when they are available and in particular will synchronize themselves to a common time server.

It is assumed that on those operating systems where an NTP service is available (Windows NT/XP, UNIX) that the server is making use of that service and that all servers are sycnrhonizing their clocks to one single time server. Operating systems which do not offer an NTP service (DOS, Win16, VxWorks, NIOS) attempt to synchronize themselves the the central time server using an 'rdate' call built into the TINE library.

Under some circumstances, the central time server might not be available, and to this end the TINE control system offers an additional time server which multicasts the current TINE timestamp at 1 Hz. This 'global' value can be received at the server side and used to establish a data timestamp offset, which is to be appended to all data timestamps used internally. Note that this form of synchronization only synchronizes the data timestamps and does not adjust the system clock.

In addition, all servers will check for the existence of a server named "CYCLER" in its context. If such a server exists and produces a global integer quantity called "CycleNumber" then this value will be received and used to stamp all outgoing data sets, using the system data stamp field of the DTYPE object. In such a way, distributed data can be synchronized and correlated independent of the distributed clock timestamps.

illegal_read_write
@ illegal_read_write
Definition: errors.h:161
DTYPE::data
DUNION data
Definition: tinetype.h:1006
TErrorList
TErrorList
Definition: errors.h:74
GetDeviceNumberEx
TINE_EXPORT int GetDeviceNumberEx(char *eqm, char *devname, char *prpname)
Gives the registered device number for the specified device name and property name.
Definition: srvdbase.c:7706
AttachLink
TINE_EXPORT int AttachLink(const char *devName, const char *devProperty, DTYPE *dout, DTYPE *din, short access, int pollingRate, void(*cbFcn)(int, int), int mode)
Initiates an asynchronous link.
Definition: client.c:9273
illegal_property
@ illegal_property
Definition: errors.h:118
DTYPE::dFormat
short dFormat
Definition: tinetype.h:999
DTYPE
Defines a TINE data object.
Definition: tinetype.h:996
DUNION::fptr
float * fptr
Definition: tinetype.h:987
out_of_range
@ out_of_range
Definition: errors.h:119
illegal_equipment_number
@ illegal_equipment_number
Definition: errors.h:115
GetPropertyId
TINE_EXPORT int GetPropertyId(char *eqm, char *prpName)
Gives the associated property identifier for the given property name.
Definition: srvdbase.c:7784
ExecLink
TINE_EXPORT int ExecLink(const char *devName, const char *devProperty, DTYPE *dout, DTYPE *din, short access)
Executes a synchronous link.
Definition: client.c:9705
DTYPE::dArrayLength
UINT32 dArrayLength
Definition: tinetype.h:998

Impressum   |   Imprint   |   Datenschutzerklaerung   |   Data Privacy Policy   |   Declaration of Accessibility   |   Erklaerung zur Barrierefreiheit
Generated for TINE API by  doxygen 1.5.8