Database
In the bindings, use the Database class to manage the point values that the server exposes to clients. Despite the name, it's
just an in-memory data structure protected by a mutex. The server answers reads from it, and passes writes to your
WriteHandler.
The Rust interface does not provide a database. Your RequestHandler returns the values to read, which you would usually
store in a Vec or HashMap.
Transactions
All changes to the database are made inside a transaction, so a client request never sees a partially applied one. Start one
on a running server with the update_database method, giving it the unit ID.
A transaction holds the database's lock, which client requests also need, so keep transactions short and never block inside one.
Database initialization
When adding a device to the DeviceMap, you also give it an initialization transaction. Use it to add the points, so that the server
reports valid values as soon as it starts. A client that reads an address you haven't added gets an Illegal Data Address exception.
- C
- C++
- Java
- C#
// POINT_COUNT points of each type, starting at index 0
void configure_db(rodbus_database_t *db, void *ctx)
{
for (uint16_t i = 0; i < POINT_COUNT; ++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_device_map_t *map = rodbus_device_map_create();
rodbus_device_map_add_endpoint(map,
UNIT_ID, // unit id
get_write_handler(), // handler for write requests
(rodbus_database_callback_t){.callback = &configure_db} // callback for the initial state of the database
);
// POINT_COUNT points of each type, starting at index 0
auto init_transaction = rodbus::functional::database_callback([](rodbus::Database &db) {
for (uint16_t i = 0; i < POINT_COUNT; ++i) {
db.add_coil(i, false);
db.add_discrete_input(i, false);
db.add_holding_register(i, 0);
db.add_input_register(i, 0);
}
});
auto map = rodbus::DeviceMap();
map.add_endpoint(UNIT_ID, create_write_handler(), init_transaction);
DeviceMap map = new DeviceMap();
map.addEndpoint(ubyte(UNIT_ID), new ExampleWriteHandler(), db -> {
for (int i = 0; i < POINT_COUNT; 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(UnitId, new WriteHandler(), db =>
{
for (ushort i = 0; i < PointCount; ++i)
{
db.AddCoil(i, false);
db.AddDiscreteInput(i, false);
db.AddHoldingRegister(i, 0);
db.AddInputRegister(i, 0);
}
});
Updating points
Update a point value by calling one of the update_xxx methods on the Database inside a transaction. The returned boolean
indicates whether the point exists.
- C
- C++
- Java
- C#
// runs while the database is locked, so client writes in between aren't lost
void toggle_coils(rodbus_database_t *db, void *ctx)
{
for (uint16_t i = 0; i < POINT_COUNT; ++i) {
bool value = false;
if (rodbus_database_get_coil(db, i, &value) == RODBUS_PARAM_ERROR_OK) {
rodbus_database_update_coil(db, i, !value);
}
}
}
rodbus_database_callback_t transaction = {.callback = &toggle_coils};
rodbus_param_error_t err = rodbus_server_update_database(server, UNIT_ID, transaction);
auto transaction = rodbus::functional::database_callback([](rodbus::Database &db) {
for (uint16_t i = 0; i < POINT_COUNT; ++i) {
db.update_coil(i, !db.get_coil(i));
}
});
server.update_database(UNIT_ID, transaction);
// toggle every coil, reading the current value inside the transaction
server.updateDatabase(ubyte(UNIT_ID), db -> {
for (int i = 0; i < POINT_COUNT; i++) {
db.updateCoil(ushort(i), !db.getCoil(ushort(i)));
}
});
server.UpdateDatabase(UnitId, db =>
{
for (ushort i = 0; i < PointCount; ++i)
{
db.UpdateCoil(i, !db.GetCoil(i));
}
});
Getting point values
You can also use the Database as a cache of the most recent values. 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 C++, Java or .NET, an exception is thrown.
Removing points
Few applications need to remove points, but you can remove one from a running server. There is a type-specific function for removing every point type given its index. The returned boolean indicates whether the point existed.