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.