Commercial Modbus library by Step Function I/O.
Please see the README for information on the core Rust library.
A user guide and documentation may be found here.
Refer to the CHANGELOG for a release history.
Rust implementation of Modbus with idiomatic bindings for C, C++, .NET, and Java
Home Page: https://stepfunc.io/products/libraries/modbus/
License: Other
Commercial Modbus library by Step Function I/O.
Please see the README for information on the core Rust library.
A user guide and documentation may be found here.
Refer to the CHANGELOG for a release history.
I recently pushed complete docs for C API. It builds nicely w/ doxygen. We can decide if we want to style it, but a biggest question is how/where to host. We could do this on automatak.com or readthedocs.
pros/cons of Automatak:
Currently the server accepts an infinite number of connections. When spawning the server task a maximum number of sessions should be specified. The task accepting connections can maintain a FIFO queue of active connections (how to notify it when a connection closes on its own, e.g. remote termination)?. When a connection is accepted and the maximum number of connections are already running, the oldest connection should be dropped.
Need to set this to the current version and ensure it gets updated for each release.
Tracing can provide additional context, e.g. each connection can be identified in some way, whereas the log crate has no such info and will make it impossible to correlate logs with sessions without injecting identifiers down into the log calls themselves.
net2
crate has been deprecated; usesocket2
instead
Details | |
---|---|
Status | unmaintained |
Package | net2 |
Version | 0.2.34 |
URL | deprecrated/net2-rs@3350e38 |
Date | 2020-05-01 |
The net2
crate has been deprecated
and users are encouraged to considered socket2
instead.
See advisory page for additional details.
https://github.com/GuillaumeGomez/doc-comment
would allow examples in MD and rustdoc w/o repetition.
Since this type isn't thrown by the method itself (it can throw ParamException), but instead gets embedded in the Task<> it doesn't get auto linked by the code generator.
Hey guys!
We are actively using the C++ bindings with your library and are very happy with it.
When we recently expanded our software to communicate with many devices at once, we noticed a significant steady increase in memory usage until the software crashes.
I ran our binary with the valgrind
tool and got this as part of the result:
==31== 219,121,920 bytes in 285,315 blocks are still reachable in loss record 1,022 of 1,022
==31== at 0x488A324: memalign (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==31== by 0x488A497: posix_memalign (in /usr/libexec/valgrind/vgpreload_memcheck-arm64-linux.so)
==31== by 0x4AC2D93: aligned_malloc (alloc.rs:98)
==31== by 0x4AC2D93: alloc (alloc.rs:22)
==31== by 0x4AC2D93: __rdl_alloc (alloc.rs:381)
==31== by 0x494182F: tokio::runtime::task::raw::RawTask::new (in /usr/lib/rodbus/aarch64-unknown-linux-gnu/librodbus_ffi.so)
==31== by 0x4963827: tokio::runtime::scheduler::multi_thread::handle::Handle::bind_new_task (in /usr/lib/rodbus/aarch64-unknown-linux-gnu/librodbus_ffi.so)
==31== by 0x495001B: tokio::runtime::handle::Handle::spawn_named (in /usr/lib/rodbus/aarch64-unknown-linux-gnu/librodbus_ffi.so)
==31== by 0x495ACBB: rodbus_client_channel_read_holding_registers (in /usr/lib/rodbus/aarch64-unknown-linux-gnu/librodbus_ffi.so)
==31== by 0x2BEA77: rodbus::fn::client_channel_read_holding_registers(rodbus::ClientChannel&, rodbus::RequestParam const&, rodbus::AddressRange const&, std::unique_ptr<rodbus::RegisterReadCallback, std::default_delete<rodbus::RegisterReadCallback> >) (rodbus.cpp:1450)
==31== by 0x2C267B: rodbus::ClientChannel::read_holding_registers(rodbus::RequestParam const&, rodbus::AddressRange const&, std::unique_ptr<rodbus::RegisterReadCallback, std::default_delete<rodbus::RegisterReadCallback> >) (rodbus.cpp:2562)
==31== by 0x211073: modbus::modbusMaster::executeJob(int, modbus::unprotectedMbJob, std::vector<unsigned short, std::allocator<unsigned short> >&, modbus::itfMbJobActions*) (modbusMaster.hpp:111)
==31== by 0x211E27: gateway::gateway::modbusJobRun() (gatewayBase.hpp:162)
==31== by 0x211777: gateway::gateway::run() (gatewayBase.hpp:81)
==31==
==31== LEAK SUMMARY:
==31== definitely lost: 0 bytes in 0 blocks
==31== indirectly lost: 0 bytes in 0 blocks
==31== possibly lost: 19,816 bytes in 176 blocks
==31== still reachable: 537,989,313 bytes in 1,994,176 blocks
==31== suppressed: 0 bytes in 0 blocks
A significant amount of memory is still occupied by the rodbus library although it is not needed anymore.
To me it looks like that the tokio runtime tasks never get deleted.
At the time of testing, we sent about 5000 Modbus Requests per second.
We also pass some arguments into the ModbusCallbacks. As those are just pointers or ints, this shouldnt be the cause of the problem. Other than that, I dont see anything we are doing differently than the examples.
Our timeout is set to 1 second and the max_queued_requests is set to 10.
Is this an issue on our end or needs the rodbus library to be adjusted?
Thank you!
create_outstation_serial
panics when called:
Jul 08 13:51:01.176 INFO creating runtime with 4 threads
creating outstation task
thread '<unnamed>' panicked at 'there is no reactor running, must be called from the context of a Tokio 1.x runtime'
This occurs b/c SerialStream::open
must be called from within the runtime so that it can register itself with the I/O driver.
I have enabled the JSON formatting in the rodbus log configuration to extract specific log data and log it with the spdlog library.
I have trouble extracting the IP and Port from this specific log, because the JSON is invalid.
Also, do you have documentation on the general JSON format used in the rodbus lib?
Which data field are always used/ which ones only on server / client log etc..
Thank you very much!
{
"fields": {
"message": "channel enabled"
},
"span": {
"endpoint": "HostAddr { addr: IpAddr(192.168.1.2), port: 502 }",
"name": "Modbus-Client-TCP"
},
"spans": [
{
"endpoint": "HostAddr { addr: IpAddr(192.168.1.2), port: 502 }",
"name": "Modbus-Client-TCP"
}
]
}
Otherwise you can't detect the physical layer closing.
tarpaulin works really well
Hi @jadamcrain ,
I'm actively using your library and facing another issue. When I update the register values from server side by this code:
RuntimeConfig runtimeConfig = new RuntimeConfig().withNumCoreThreads(ushort(4));
Runtime runtime = new Runtime(runtimeConfig);
DeviceMap map = new DeviceMap();
map.addEndpoint(ubyte(1), new ExampleWriteHandler(), db -> {
for(int i = 0; i < 1000; i++) {
db.addHoldingRegister(ushort(i), ushort(1));
db.addInputRegister(ushort(i), ushort(2));
}
});
AddressFilter addressFilter = new AddressFilter("*.*.*.*");
Server server = Server.createTcp(runtime, "0.0.0.0", ushort(8888), addressFilter, ushort(100), map, DecodeLevel.nothing());
server.updateDatabase(ubyte(1), db -> {
db.updateHoldingRegister(ushort(100), ushort(-1));
});
And the error is:
thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: JavaException', /target/aarch64-unknown-linux-gnu/release/build/rodbus-ffi-java-1095f2a2549debe7/out/jni.rs:3439:187
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: failed to initiate panic, error 2216477728
I'm running on ubuntu VM, and cpu info:
CPU op-mode(s): 64-bit
Byte Order: Little Endian
Please advise what can be done to get rid of this issue?
Thanks!
Add SetDecodeLevel
to both the ClientChannel
and the ServerHandle
to dynamically change the decode level.
Will require refactoring the Handler
trait to retrieve one value at a time, returning Option<bool>
or Option<u16>
. The good news is that this would remove the lifetimes from all return values which made using "Service" traits difficult.
valgrind, gperf, etc
The TX logs are currently written in this order: ADU -> PDU -> Phys
, but we expect it to be PDU -> ADU -> Phys
.
Ran into an issue using &[u8]
as the ServerHandler
response type. The compiler insisted that it needed a lifetime but it was unclear where to put it when using an associated type in a trait.
There are two features that are very useful in mbtget
. The first is the ability to output the values in hex (rather than decimal) and the second is to display the TX and RX bytes. The following is an example that shows both.
`
CutSec 2020-09-17 07:38:03
./mbtget -d -hex -a 0 -n 5
Tx
[84 C6 00 00 00 06 01] 03 00 00 00 05
Rx
[84 C6 00 00 00 0D 01] 03 0A 00 00 00 1E 00 00 00 00 00 3C
values:
1 (ad 00000): 0x0000
2 (ad 00001): 0x001e
3 (ad 00002): 0x0000
4 (ad 00003): 0x0000
5 (ad 00004): 0x003c
`
I'm reviewing the code to see if I can implement it, but I'm new to Rust (just installed) and it might take a while for me to do it. Plus, I doubt I would do it in the most effective manner.
Thank you,
cutaway
Would prefer to use tokio-rustls
, but it appears that ATM rustls forces server hostname validation.
I opened up a pair of tickets to track this:
rustls/rustls#331
quininer/tokio-rustls#59
It looks like the native-tls
create on which tokio-tls
depends may also force server hostname validation?
https://docs.rs/native-tls/0.2.3/native_tls/struct.TlsConnector.html#method.connect
The openssl bindings too:
https://docs.rs/openssl/0.10.26/openssl/ssl/struct.SslConnector.html#method.connect
Assuming I'm using the java library correctly, I believe there's either a missing Runtime
reference hold or the documentation may need to be updated to reflect how Runtime
should be treated.
In my multi-threaded use of the library:
final RuntimeConfig runtimeConfig =
new RuntimeConfig()
.withNumCoreThreads(ushort(4));
final Runtime runtime = new Runtime(runtimeConfig);
this.channel =
ClientChannel.createTcp(
runtime,
ip,
ushort(port),
ushort(1),
new RetryStrategy(),
DecodeLevel.nothing(),
state -> LOG.warn("State change: {}", state));
this.channel.enable();
A separate scheduled executor periodically initiates reads from the channel.
With nothing holding the runtime reference, the gc seems to prune it, calling finalize()
, which shuts down the Runtime and "SHUTDOWN" exceptions start to get thrown.
To fix this, holding the reference to Runtime
on the class keeps the gc from deleting it, which keeps things going.
Couldn't find reference to this anywhere, so wanted to share. Unsure if intended or not.
Where to put this? Wiki?
See
rodbus/ffi/rodbus-ffi/src/client.rs
Line 20 in b4b49b9
Hi,
I have the server in Rodbus, and client in other library(sorry have to use that). When client and server are in same process (say same SpringBoot application), client can connect to server successfully:
RodbusLog - Feb 17 01:34:43.756 INFO Modbus-Server-TCP{listen=0.0.0.0:502}: accepted connection from: 172.17.0.2:50384 - assigned session id: 0
However, if client and master are in different process (same machine), I'll get connection refused error. Please suggest what could be go wrong?
Thanks!
In case requests take some time, e.g. database calls.
Currently Frame contains a static buffer, which gets copied. It should point to a slice in another buffer.
I'm trying to experiment with rodbus in C# but I'm getting this error about missing dylib: Unable to load shared library 'rodbus_ffi' or one of its dependencies.
. Below is the full error:
---> System.DllNotFoundException: Unable to load shared library 'rodbus_ffi' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable:
dlopen(/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/rodbus_ffi.dylib, 0x0001): tried: '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/rodbus_ffi.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/rodbus_ffi.dylib' (no such file), '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/rodbus_ffi.dylib' (no such file)
dlopen(/Users/[User]/project/bin/Debug/net7.0/rodbus_ffi.dylib, 0x0001): tried: '/Users/[User]/project/bin/Debug/net7.0/rodbus_ffi.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/[User]/project/bin/Debug/net7.0/rodbus_ffi.dylib' (no such file), '/Users/[User]/project/bin/Debug/net7.0/rodbus_ffi.dylib' (no such file)
dlopen(rodbus_ffi.dylib, 0x0001): tried: 'rodbus_ffi.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSrodbus_ffi.dylib' (no such file), '/usr/lib/rodbus_ffi.dylib' (no such file, not in dyld cache), 'rodbus_ffi.dylib' (no such file), '/usr/local/lib/rodbus_ffi.dylib' (no such file), '/usr/lib/rodbus_ffi.dylib' (no such file, not in dyld cache)
dlopen(/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/librodbus_ffi.dylib, 0x0001): tried: '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/librodbus_ffi.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/librodbus_ffi.dylib' (no such file), '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/librodbus_ffi.dylib' (no such file)
dlopen(/Users/[User]/project/bin/Debug/net7.0/librodbus_ffi.dylib, 0x0001): tried: '/Users/[User]/project/bin/Debug/net7.0/librodbus_ffi.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/[User]/project/bin/Debug/net7.0/librodbus_ffi.dylib' (no such file), '/Users/[User]/project/bin/Debug/net7.0/librodbus_ffi.dylib' (no such file)
dlopen(librodbus_ffi.dylib, 0x0001): tried: 'librodbus_ffi.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibrodbus_ffi.dylib' (no such file), '/usr/lib/librodbus_ffi.dylib' (no such file, not in dyld cache), 'librodbus_ffi.dylib' (no such file), '/usr/local/lib/librodbus_ffi.dylib' (no such file), '/usr/lib/librodbus_ffi.dylib' (no such file, not in dyld cache)
dlopen(/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/rodbus_ffi, 0x0001): tried: '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/rodbus_ffi' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/rodbus_ffi' (no such file), '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/rodbus_ffi' (no such file)
dlopen(/Users/[User]/project/bin/Debug/net7.0/rodbus_ffi, 0x0001): tried: '/Users/[User]/project/bin/Debug/net7.0/rodbus_ffi' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/[User]/project/bin/Debug/net7.0/rodbus_ffi' (no such file), '/Users/[User]/project/bin/Debug/net7.0/rodbus_ffi' (no such file)
dlopen(rodbus_ffi, 0x0001): tried: 'rodbus_ffi' (no such file), '/System/Volumes/Preboot/Cryptexes/OSrodbus_ffi' (no such file), '/usr/lib/rodbus_ffi' (no such file, not in dyld cache), 'rodbus_ffi' (no such file), '/usr/local/lib/rodbus_ffi' (no such file), '/usr/lib/rodbus_ffi' (no such file, not in dyld cache)
dlopen(/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/librodbus_ffi, 0x0001): tried: '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/librodbus_ffi' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/librodbus_ffi' (no such file), '/usr/local/share/dotnet/shared/Microsoft.NETCore.App/7.0.9/librodbus_ffi' (no such file)
dlopen(/Users/[User]/project/bin/Debug/net7.0/librodbus_ffi, 0x0001): tried: '/Users/[User]/project/bin/Debug/net7.0/librodbus_ffi' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/Users/[User]/project/bin/Debug/net7.0/librodbus_ffi' (no such file), '/Users/[User]/project/bin/Debug/net7.0/librodbus_ffi' (no such file)
dlopen(librodbus_ffi, 0x0001): tried: 'librodbus_ffi' (no such file), '/System/Volumes/Preboot/Cryptexes/OSlibrodbus_ffi' (no such file), '/usr/lib/librodbus_ffi' (no such file, not in dyld cache), 'librodbus_ffi' (no such file), '/usr/local/lib/librodbus_ffi' (no such file), '/usr/lib/librodbus_ffi' (no such file, not in dyld cache)
I'm attempting this on an M1 Mac (not sure if this has anything to do with what's wrong)
rodbus/.github/workflows/ci.yml
Line 206 in fdc4983
Task gets stuck in an infinite (asynchronous) loop in wait_for_enabled
I have a project that I am trying to setup. Its a spring boot app using Java 17
When I attempt to create the Runtime object similarly to the example files I run into an issue regarding some native libraries.
I have hopefully formatted the structure below into somewhat orderly manner so that it can be easy to follow my setup and identify any glaring issues.
Thanks!
Exception Stack Trace
2023-12-14 13:47:14.877 [http-nio-591-exec-1] ERROR o.a.c.c.C.[.[localhost].[/].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.ExceptionInInitializerError] with root cause
java.lang.Exception: Unable to load any of the included native libraries
at io.stepfunc.rodbus.NativeFunctions.<clinit>(NativeFunctions.java:53)
at io.stepfunc.rodbus.NativeFunctions$Wrapped.runtime_create(NativeFunctions.java:193)
at io.stepfunc.rodbus.Runtime.<init>(Runtime.java:37)
Method Throwing Error (specifically the second line here is the culprit)
RuntimeConfig runtimeConfig = new RuntimeConfig().withNumCoreThreads(ushort(4));
Runtime runtime = new Runtime(runtimeConfig);
Java Version
java 17.0.6 2023-01-17 LTS
Java(TM) SE Runtime Environment (build 17.0.6+9-LTS-190)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.6+9-LTS-190, mixed mode, sharing)
pom.xml snippet
<dependency>
<groupId>io.stepfunc</groupId>
<artifactId>rodbus</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>joou</artifactId>
<version>0.9.4</version>
</dependency>
class imports
import static org.joou.Unsigned.ubyte;
import static org.joou.Unsigned.ushort;
import io.stepfunc.rodbus.*;
import io.stepfunc.rodbus.Runtime;
Let me know if I am overlooking something in my setup here or if there is any additional information that you need to help me with this issue!
Thanks
Really want to have the count limits and the exception code constants in rodbus.h/rodbus.hpp
Add custom function code like tokio-modbus Custom
Not sure exactly how this will work with the AuthHandler yet.
Similar functionality to DNP3, but is on every inbound connection
The workflow ci.yml is referencing action actions/checkout using references v1. However this reference is missing the commit a6747255bd19d7a757dbdda8c654a9f84db19839 which may contain fix to the some vulnerability.
The vulnerability fix that is missing by actions version could be related to:
(1) CVE fix
(2) upgrade of vulnerable dependency
(3) fix to secret leak and others.
Please consider to update the reference to the action.
Quite possibly failure
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.