Database
You can use the Database
class to manipulate the point values that the server exposes to the clients. Note that while it's called a "database", it's really just
a thread-safe data structure in memory.
For maximum versatility, the Rust interface does not provide a database implementation. The generic RequestHandler
trait is used to get the values to return to the client.
However, in most cases, you would just store the values in a Vec
or HashMap
.
Transactions
All modifications to the internal database are done inside a transaction. All the changes are applied atomically at the end of the transaction. This makes it impossible for clients to read inconsistent state in a single request.
A transaction can be started on a running server with the update
method. Inside the transaction, any operations can be performed.
They will be executed in sequence.
Because the transaction mechanism acquires a lock on a mutex, it is important to keep each transaction as short as possible. Never perform a blocking operation inside a database transaction.
Database Initialization
When adding a device to the DeviceMap
, an initialization transaction must be specified. This is usually used to add the points
to the database so that when the server is actually created, it can immediately report valid values.
- C
- C++
- Java
- C#
// initialize 10 of every point type
void configure_db(rodbus_database_t *db, void *ctx)
{
for (uint16_t i = 0; i < 10; ++i) {
rodbus_database_add_coil(db, i, false);
rodbus_database_add_discrete_input(db, i, false);
rodbus_database_add_holding_register(db, i, 0);
rodbus_database_add_input_register(db, i, 0);
}
}
rodbus_write_handler_t write_handler = {
.write_single_coil = on_write_single_coil,
.write_single_register = on_write_single_register,
.write_multiple_coils = on_write_multiple_coils,
.write_multiple_registers = on_write_multiple_registers,
};
rodbus_device_map_t* map = rodbus_device_map_create();
rodbus_device_map_add_endpoint(map,
1, // Unit ID
write_handler, // Handler for write requests
(rodbus_database_callback_t){.callback = configure_db } // Callback for the initial state of the database
);
// initialize 10 of every point type
auto device_map = rodbus::DeviceMap();
auto init_transaction = rodbus::functional::database_callback([](rodbus::Database& db) {
for (uint16_t i = 0; i < 10; ++i)
{
db.add_coil(i, false);
db.add_discrete_input(i, false);
db.add_holding_register(i, 0);
db.add_input_register(i, 0);
}
});
device_map.add_endpoint(1, std::make_unique<WriterHandler>(), init_transaction);
DeviceMap map = new DeviceMap();
map.addEndpoint(ubyte(1), new ExampleWriteHandler(), db -> {
for(int i = 0; i < 10; i++) {
db.addCoil(ushort(i), false);
db.addDiscreteInput(ushort(i), false);
db.addHoldingRegister(ushort(i), ushort(0));
db.addInputRegister(ushort(i), ushort(0));
}
});
var map = new DeviceMap();
map.AddEndpoint(1, new WriteHandler(), db =>
{
for (ushort i = 0; i < 10; ++i)
{
db.AddCoil(i, false);
db.AddDiscreteInput(i, false);
db.AddHoldingRegister(i, 0);
db.AddInputRegister(i, 0);
}
});
Updating Points
You can update a point value by calling one of the update_xxx
method on the Database
object inside a transaction. The returned boolean
indicates if the update was successful (i.e. the point existed).
- C
- C++
- Java
- C#
void update_coil(rodbus_database_t *db, void *ctx)
{
state_t *state = (state_t *)ctx;
state->coil_value = !state->coil_value;
for (uint16_t i = 0; i < 10; ++i) {
rodbus_database_update_coil(db, i, state->coil_value);
}
}
rodbus_server_update_database(server, 1, (rodbus_database_callback_t) {
.callback = update_coil,
.ctx = &state,
});
auto transaction = rodbus::functional::database_callback([&](rodbus::Database& db) {
coil_value = !coil_value;
for (uint16_t i = 0; i < 10; ++i) {
db.update_coil(i, coil_value);
}
});
server.update_database(1, transaction);
coilValue = !coilValue;
final boolean pointValue = coilValue;
server.updateDatabase(ubyte(1), db -> {
for(int i = 0; i < 10; i++) {
db.updateCoil(ushort(i), pointValue);
}
});
server.UpdateDatabase(1, db =>
{
coilValue = !coilValue;
for (ushort i = 0; i < 10; ++i)
{
db.UpdateCoil(i, coilValue);
}
});
Getting Point Values
You may also use the Database
as a cache of the most recent value if desired. Each type has a getter method to retrieve the most recently assigned value.
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 server. There is a type-specific function for removing every point type given its index. The returned boolean indicates if the point was defined prior to the call to remove it.