Publishing transitions

Generating transitions Most ICS devices have separate tasks for I/O, logic and protocols. RTIMDB exploits this pattern to allow the I/O task (called the "scanning task" in most RTIMDB-related documentation) to generate transitions in the database without blocking.

When the device initializes, it registers the scanning task as a producer with the database, and creates a number of points for which it will produce transitions. Once that is done and the system is ready, the scanning task will start producing transitions.

In order to do that without blocking, the scanning task calls beginTransaction to start a transaction and obtain a transaction object -- which is a writable slice in the lock-free transition queue associated with a time stamp to be applied to the values. It then adds the current value for each of the points. It can do this regardless of whether it knows the previous values of the points -- filtering will be done asynchronously.

Adding a transition to the transaction is a simple question of copying the value and incrementing a counter -- there is no locking involved. Once all the transitions have been put in the transaction, the scanning task calls commit (which, behind the scenes boils down to an atomic write of an integer value) to finish up the transaction.

The following is code for a typical device with 16 binary inputs, 8 analog inputs and 8 binary outputs. Details::Timestamp timestamp(getCurrentTime()); auto transaction(database.beginTransaction(producer, timestamp));

for (unsigned int bi_index(0); bi_index < 16; ++bi_index)
{
    bool const binary_input_value(readBinaryInput(bi_index));
    transaction.add(Transition(bi_index, PointType::binary_input__, binary_input_value));
}
for (unsigned int bo_index(0); bo_index < 8; ++bo_index)
{
    bool const binary_output_value(readBinaryOutput(bo_index)); // read-back may have a few ms delay wrt commands
    transaction.add(Transition(bo_index, PointType::binary_output__, binary_ouput_value));
}
for (unsigned int ai_index(0); ai_index < 8; ++ai_index)
{
    float analog_input_value(readAnalogInput(ai_index)); // A2D converter takes a bunch of cycles to update this value, but we'll let RTIMDB sort out when it changes :0
    transaction.add(Transition(ai_index, PointType::analog_input__, analog_input_value));
}
database.commit(producer, transaction);

At the end of this code, the transaction is queued and is no longer the concern of the scan task.