Some time ago I started writing a Java modbus library, not because there aren't any already, but because it's a reasonable way to get more familiar with the protocol. As tends to be the way with these things it was never really finished, but I did learn a fair bit in the process.
I'm in a bit of a tidy up mood, so I've dug out that old code to try to make it somewhat presentable.
tl;dr - https://github.com/ptomli/modbus
It's taken a little bit of exploring to remember what I was thinking, but the API itself seems fairly painless.
var config = new NioTcpClientTransportConfig("127.0.0.1", 502, 1);
var transport = new NioTcpClientTransport(config);
var client = new ModbusClient(transport);
client.connect(); // or try (client.open()) { ... }
var register = 0x1234;
var count = 10;
var response = client.readHoldingRegisters(register, count);
byte[] registers = response.registers();
Clearly the setup could cope with a little sugar, and not too much thought has been spent on actually consuming the response data, but it looks like a decent start.
On the server side it's similarly straightforward
class MyServices implements ModbusServices {
@Override
public CompletionStage<ReadHoldingRegistersResponse> readHoldingRegisters(
ReadHoldingRegistersRequest request) {
var address = request.address();
var quantity = request.quantity();
// for this example, just be synchronous
byte[] bytes = ...
return CompletableFuture.completedStage(new ReadHoldingRegistersResponse(bytes));
}
}
var config = new NioTcpServerTransportConfig("127.0.0.1", 502, Executors.newFixedThreadPool(5));
var transport = new NioTcpServerTransport(config);
var server = new ModbusServer(transport, services);
server.start();
Again, setup is a little verbose, and looking at it now I suspect
ModbusServices might prefer those request properties as individual
arguments. You can also see how the server ends up with a thread pool to
service requests.
The old code includes some "tests", but those scare quotes are doing some
heavy lifting. While I was writing this originally it was mostly exploration,
so @Test was just shorthand for main(String...). There's a bit of work to
be done there to verify the code in place already, besides anything that's
missing.
That's it for now