Database
You can use the Database
class to manipulate the measurement types and values that the outstation exposes to the master. Note that while it's called a "database", it's really just
a thread-safe data structure in memory.
Adding Points
You must initialize the points before the outstation exposes any measurement data. While you should do this when you create the outstation, you can add points to a running outstation as well. Each measurement type has unique configuration including:
- An optional event class assignment for the point
- Default static and event variations for the type
- Type-specific dead-bands that default to zero (Binary points have no deadband)
When you add a point, it is assigned the following default value with RESTART
flags:
- Binary points are set to
false
- Numeric values are set to
0
- Double-bit Binary points set to
Indeterminate
- Octet Strings are set to the value of
[0x00]
Update the value after adding the point if you don't want a connecting master to see RESTART
.
- Rust
- C
- Java
- C#
outstation.transaction(|db| {
for i in 0..10 {
db.add(i, Some(EventClass::Class1), AnalogConfig::default());
db.update(
i,
&Analog::new(10.0, Flags::ONLINE, Time::synchronized(0)),
UpdateOptions::initialize(),
);
}
});
// initialize 10 of every point type
void outstation_transaction_startup(dnp3_database_t *db, void *context)
{
for (uint16_t i = 0; i < 10; ++i) {
// initialize each point with default configuration
dnp3_database_add_binary(db, i, DNP3_EVENT_CLASS_CLASS1, dnp3_binary_config_init());
dnp3_database_add_double_bit_binary(db, i, DNP3_EVENT_CLASS_CLASS1, dnp3_double_bit_binary_config_init());
dnp3_database_add_binary_output_status(db, i, DNP3_EVENT_CLASS_CLASS1, dnp3_binary_output_status_config_init());
dnp3_database_add_counter(db, i, DNP3_EVENT_CLASS_CLASS1, dnp3_counter_config_init());
dnp3_database_add_frozen_counter(db, i, DNP3_EVENT_CLASS_CLASS1, dnp3_frozen_counter_config_init());
dnp3_database_add_analog(db, i, DNP3_EVENT_CLASS_CLASS1, dnp3_analog_config_init());
dnp3_database_add_analog_output_status(db, i, DNP3_EVENT_CLASS_CLASS1, dnp3_analog_output_status_config_init());
dnp3_database_add_octet_string(db, i, DNP3_EVENT_CLASS_CLASS1);
}
}
// during program initialization - "outstation" already created
dnp3_outstation_transaction_t startup_transaction = {
.execute = &outstation_transaction_startup,
.on_destroy = NULL,
.ctx = NULL,
};
dnp3_outstation_transaction(outstation, startup_transaction);
// you can use a separate method or just initialize directly in the lambda expression
public static void initializeDatabase(Database db) {
for (int i = 0; i < 10; i++) {
db.addBinary(ushort(i), EventClass.CLASS1, new BinaryConfig());
db.addDoubleBitBinary(ushort(i), EventClass.CLASS1, new DoubleBitBinaryConfig());
db.addBinaryOutputStatus(ushort(i), EventClass.CLASS1, new BinaryOutputStatusConfig());
db.addCounter(ushort(i), EventClass.CLASS1, new CounterConfig());
db.addFrozenCounter(ushort(i), EventClass.CLASS1, new FrozenCounterConfig());
db.addAnalog(ushort(i), EventClass.CLASS1, new AnalogConfig());
db.addAnalogOutputStatus(ushort(i), EventClass.CLASS1, new AnalogOutputStatusConfig());
db.addOctetString(ushort(i), EventClass.CLASS1);
}
}
// during program initialization - "outstation" already created
outstation.transaction((db) -> initializeDatabase(db));
outstation.Transaction(new OutstationTransaction((db) =>
{
for (ushort i = 0; i < 10; i++)
{
// add points with default values
db.AddBinary(i, EventClass.Class1, new BinaryConfig());
db.AddDoubleBitBinary(i, EventClass.Class1, new DoubleBitBinaryConfig());
db.AddBinaryOutputStatus(i, EventClass.Class1, new BinaryOutputStatusConfig());
db.AddCounter(i, EventClass.Class1, new CounterConfig());
db.AddFrozenCounter(i, EventClass.Class1, new FrozenCounterConfig());
db.AddAnalog(i, EventClass.Class1, new AnalogConfig());
db.AddAnalogOutputStatus(i, EventClass.Class1, new AnalogOutputStatusConfig());
db.AddOctetString(i, EventClass.Class1);
}
}));
Updating Points
You can update a point value in a new transaction or in the same transaction you used to initialize it. This is useful if the outstation has local access to values at startup, such as via a local ADC.
The UpdateOptions
struct lets you precisely control how a point update is processed. Use the default constructor to:
- Update the static value
- Produce an event if the point value changes in a way that exceeds the deadband or if the flags change
tip
Use the UpdateOptions to ignore event creation during startup initialization if you don't want to create events for the initial values.
Getting Point Values
Some applications may wish to use the Database
as a cache of the most recent value. Each type has a getter method to retrieve the most recently assigned value.
note
Since the point may not be defined, the getters can fail. If you try to retrieve a point that doesn't exist using Java and C#, an exception will be thrown.
Removing Points
Most applications don't need to remove points, but the option is there in case you want to remove points from a running outstation. There is a type-specific function for removing every point type given its index.
note
Removing a point stops the outstation from reporting static data for that point. However, it does NOT remove any queued events for that point from the event buffer. Those events will remain in the event buffer until they are reported and cleared by confirmation.