Time stamps

Time stamps are generally used on events, or to indicate when the latest change to a data point occurred. They are managed by the database as meta-data, associated with a point but not part of its value. A time stamp may therefore be updated independently from the value itself: if a change in the quality flags associated with a point occurs this will usually affect the time stamp of the point as well.

Reading time stamps on events

All events generated by the database have a time stamp associated with them. These time stamps do not require the device to have a clock -- because they are provided by the user's own code -- but they do assume that if one is available, it will usually be used, which warrants the 48-bit overhead of including it in every event.

The time stamp is included in the event object itself, as the following structure:

struct Timestamp
{
    Timestamp()
        : high_(0)
        , low_(0)
    { /* no-op */ }
    Timestamp(uint16_t high, uint32_t low)
        : high_(high)
        , low_(low)
    { /* no-op */ }

    uint16_t high_;
    uint32_t low_;
};

This structure is provided as a convenience (it is never used by the database itself) and is intended to represent the number of milliseconds since January 1, 1970 at midnight UTC. Hence it is, for all intents and purposes, "DNP time".

Generating time-stamped transitions

While time stamps are optional, a device that generates transitions can (and should, if it is able) provide a time stamp for each set of transitions. The transition-generating code might look something like this (for an 8-BI device):

Details::Timestamp timestamp(system__.getTime());
auto transaction(database.beginTransaction(producer, timestamp));

unsigned int index(0);
if (transaction.size() < 8) // not enough space for all of our points - signal an overflow
{
    database.signalOverflow(producer);
}
else
{
    auto values(static_cast< BinaryInputBoard const& >(board).getValues());
    transaction[index] = Transition(index, PointType::binary_input__, (values & 0x01) == 0x01); ++index;
    transaction[index] = Transition(index, PointType::binary_input__, (values & 0x02) == 0x02); ++index;
    transaction[index] = Transition(index, PointType::binary_input__, (values & 0x04) == 0x04); ++index;
    transaction[index] = Transition(index, PointType::binary_input__, (values & 0x08) == 0x08); ++index;
    transaction[index] = Transition(index, PointType::binary_input__, (values & 0x10) == 0x10); ++index;
    transaction[index] = Transition(index, PointType::binary_input__, (values & 0x20) == 0x20); ++index;
    transaction[index] = Transition(index, PointType::binary_input__, (values & 0x40) == 0x40); ++index;
    transaction[index] = Transition(index, PointType::binary_input__, (values & 0x80) == 0x80); ++index;
    database.commit(producer, transaction);
}

In this device, the time is read once, on line 1, and associated with the transaction when it is created on line 2. All the transitions added to the transaction (lines 12 through 19) have the same time stamp.