Skip to main content

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.

note

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.

danger

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.

// 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
);

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).

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,
});

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.

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 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.