From b22c762af253da43c6f0edf54640f6e79c596d24 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 16:27:44 -0800 Subject: [PATCH 1/2] fix grammatical errors in documentation --- guide/docs/about/guide.mdx | 2 +- guide/docs/about/modbus.mdx | 3 +-- guide/docs/api/server/rtu_server.mdx | 2 +- guide/docs/api/server/tcp_server.mdx | 2 +- guide/docs/api/server/tls_server.mdx | 2 +- guide/docs/api/tls.mdx | 4 ++-- guide/docs/languages/cpp_lang.mdx | 6 +++--- guide/docs/languages/csharp.mdx | 2 +- guide/docs/languages/java.mdx | 2 +- 9 files changed, 12 insertions(+), 13 deletions(-) diff --git a/guide/docs/about/guide.mdx b/guide/docs/about/guide.mdx index a814392c..d952d82c 100644 --- a/guide/docs/about/guide.mdx +++ b/guide/docs/about/guide.mdx @@ -28,7 +28,7 @@ In addition to this documentation, you can find example programs in the source r ## Modbus Standard -Please note that while this guide covers many Modbus concepts, it cannot replicate the Modbus standard itself. If you plan to develop a product that uses Modbus, +While this guide covers many Modbus concepts, it does not replicate the Modbus standard. If you plan to develop a product that uses Modbus, you can get a free copy from the [Modbus Organization website](https://modbus.org/).
diff --git a/guide/docs/about/modbus.mdx b/guide/docs/about/modbus.mdx index 5e13d020..1c7b56f7 100644 --- a/guide/docs/about/modbus.mdx +++ b/guide/docs/about/modbus.mdx @@ -85,8 +85,7 @@ The following table lists the functions currently supported by our library: | 0x15 | Write multiple coils | ✔️ | | 0x16 | Write multiple registers | ✔️ | -These functions are all that is required for the vast majority of applications using Modbus. We would consider adding support for additional -functions codes if requested by a potential customer. +These functions are all that is required for the vast majority of applications using Modbus. Additional function codes can be added upon request. Modbus also allows user-defined function codes in the range 65 to 72 and 100 to 110. This library does not support adding user defined functions at this time, but could be added if desired. diff --git a/guide/docs/api/server/rtu_server.mdx b/guide/docs/api/server/rtu_server.mdx index dca9c718..29552515 100644 --- a/guide/docs/api/server/rtu_server.mdx +++ b/guide/docs/api/server/rtu_server.mdx @@ -28,7 +28,7 @@ enforce this requirement, but a warning message is generated as a reminder that ## Creating a server -To create a RTU server, first build a `DeviceMap` for each unit ID to which the server will respond. The is similar to how it's done in the [TCP server](./tcp_server). +To create a RTU server, first build a `DeviceMap` for each unit ID to which the server will respond. This is similar to how it is done in the [TCP server](./tcp_server). Then use the `Server.CreateRtu` factory method to create the background task. The `Server.CreateRtu` method takes the following arguments: diff --git a/guide/docs/api/server/tcp_server.mdx b/guide/docs/api/server/tcp_server.mdx index 787ee2da..655694af 100644 --- a/guide/docs/api/server/tcp_server.mdx +++ b/guide/docs/api/server/tcp_server.mdx @@ -31,7 +31,7 @@ The created server will start listening on the port immediately. The `Server.CreateTcp` method takes the following arguments: - `runtime`: tokio runtime used to drive the async process. See [Runtime](../runtime.mdx) for more details. -- `address`: IP address of the adapter on which to listen. It may be any specified as any valid IPv4 or IPv6 local endpoint, such as: +- `address`: IP address of the adapter on which to listen. Specify any valid IPv4 or IPv6 local endpoint, such as: - `127.0.0.1` for localhost only - `0.0.0.0` for all adapters - The IP address for a particular adapter diff --git a/guide/docs/api/server/tls_server.mdx b/guide/docs/api/server/tls_server.mdx index 0f19f0e0..01a8427f 100644 --- a/guide/docs/api/server/tls_server.mdx +++ b/guide/docs/api/server/tls_server.mdx @@ -15,7 +15,7 @@ support and the configuration options, check the [TLS general information](../tl :::tip A server mode of operation is also supported which does not require the client certificate to contain the role extension. -The example only demonstrate the `Server.CreateTlsWithAuthz` method, but there is a `Server.CreateTls` which does NOT take an `AuthorizationHandler` parameter +The example only demonstrates the `Server.CreateTlsWithAuthz` method, but there is a `Server.CreateTls` which does NOT take an `AuthorizationHandler` parameter and allows an authenticated client to perform any Modbus operation. ::: diff --git a/guide/docs/api/tls.mdx b/guide/docs/api/tls.mdx index 1893da08..55d82850 100644 --- a/guide/docs/api/tls.mdx +++ b/guide/docs/api/tls.mdx @@ -76,7 +76,7 @@ Rustls has been [reviewed](https://github.com/rustls/rustls/raw/main/audit/TLS-0 TLS configuration is configured using the `TlsClientConfig` or `TlsServerConfig` structures. They are very similar and contain the following fields: - `name` (client only): - * The client verifies that the certificate presented by the server is valid for this name. Check out the next section for the gory details on + * The client verifies that the certificate presented by the server is valid for this name. Check out the next section for the technical details on name validation. * The client will advertise this name through a Server Name Indication (SNI) extension in the `Client Hello`. - `peer_cert_path`: @@ -188,7 +188,7 @@ Name validation is performed *indirectly* in self-signed mode, since the byte-fo ### `AuthorizationHandler` When creating a Modbus Security server, you also need to provide an `AuthorizationHandler`. -It is use to validate that the client has the authorization to perform the operation. There's +It is used to validate that the client has the authorization to perform the operation. There is a callback for each function code. You get the unit ID of the destination, information about what points are requested (either as an `AddressRange` or a 16-bit unsigned integer) as well as the Role ID that was extracted from the presented certificate. diff --git a/guide/docs/languages/cpp_lang.mdx b/guide/docs/languages/cpp_lang.mdx index ac38c6f7..7ed544fa 100644 --- a/guide/docs/languages/cpp_lang.mdx +++ b/guide/docs/languages/cpp_lang.mdx @@ -75,7 +75,7 @@ All C API errors are transformed into a C++ exceptions containing the error enum Other validations (e.g. checking that a moved class isn't used after the move) also throw `std::logic_error`. :::warning -Uncaught exceptions thrown in callbacks will terminate the program. Always wrap your callback logic using `try/catch` syntax if there's a possibility the callback will throw. +Uncaught exceptions thrown in callbacks will terminate the program. Always wrap your callback logic using `try/catch` syntax if there is a possibility the callback will throw. ::: ### Iterators @@ -102,8 +102,8 @@ Collections are taken through a constant reference to a `std::vector`. The eleme ### Classes -Classes have a opaque pointer inside and therefore cannot be copied. They can be moved around with `std::move`. If a method -is called on a moved class it throw a `std::logic_error`. +Classes have an opaque pointer inside and therefore cannot be copied. They can be moved around with `std::move`. If a method +is called on a moved class it throws a `std::logic_error`. The class destructor will call the underlying C destructor automatically. diff --git a/guide/docs/languages/csharp.mdx b/guide/docs/languages/csharp.mdx index 36c7d599..2a88a5d3 100644 --- a/guide/docs/languages/csharp.mdx +++ b/guide/docs/languages/csharp.mdx @@ -19,7 +19,7 @@ C# is an object-oriented language that supports all the abstract patterns modele C API errors are transformed into exceptions that contain the error `enum`. :::warning -Uncaught exceptions thrown in callbacks will terminate the program. Your code should always wrap callback logic with `try/catch` syntax if there's a chance that the callback will throw. +Uncaught exceptions thrown in callbacks will terminate the program. Your code should always wrap callback logic with `try/catch` syntax if there is a chance that the callback will throw. ::: ### Iterators diff --git a/guide/docs/languages/java.mdx b/guide/docs/languages/java.mdx index a11c8551..b65f0003 100644 --- a/guide/docs/languages/java.mdx +++ b/guide/docs/languages/java.mdx @@ -57,7 +57,7 @@ Java is an object-oriented language that supports all the abstract patterns mode C API errors are transformed into exceptions containing the error enum. The exception class inherits from `RuntimeException`. :::warning -Uncaught exceptions thrown in callbacks will terminate the program. Always wrap your callback logic using `try/catch` syntax if there's a possibility the callback will throw. +Uncaught exceptions thrown in callbacks will terminate the program. Always wrap your callback logic using `try/catch` syntax if there is a possibility the callback will throw. ::: From 59757fa541873bd11e7e4f8b1c1ced8a7c82f0db Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 16:44:32 -0800 Subject: [PATCH 2/2] improve documentation tone: more direct, less verbose --- guide/docs/about/dependencies.mdx | 13 ++++++------- guide/docs/about/modbus.mdx | 10 +++++----- guide/docs/api/client/requests.mdx | 10 ++++------ guide/docs/api/client/rtu_client.mdx | 5 ++--- guide/docs/api/client/tcp_client.mdx | 8 +++----- guide/docs/api/logging.mdx | 9 ++++----- guide/docs/api/runtime.mdx | 18 ++++++++---------- guide/docs/api/server/database.mdx | 10 ++++------ guide/docs/api/server/rtu_server.mdx | 8 +++----- guide/docs/api/server/tcp_server.mdx | 9 ++++----- guide/docs/api/server/write_handler.mdx | 11 ++++------- guide/docs/api/tls.mdx | 13 +++++-------- guide/docs/examples/summary.mdx | 4 ++-- guide/docs/languages/bindings.mdx | 6 ++---- guide/docs/languages/c_lang.mdx | 14 +++++++------- guide/docs/languages/cpp_lang.mdx | 16 +++++++--------- guide/docs/languages/csharp.mdx | 4 ++-- guide/docs/languages/java.mdx | 6 +++--- 18 files changed, 75 insertions(+), 99 deletions(-) diff --git a/guide/docs/about/dependencies.mdx b/guide/docs/about/dependencies.mdx index 00f1eb07..4e976151 100644 --- a/guide/docs/about/dependencies.mdx +++ b/guide/docs/about/dependencies.mdx @@ -8,21 +8,20 @@ slug: /dependencies import useBaseUrl from '@docusaurus/useBaseUrl'; import sitedata from '../../sitedata.json' -Rust's package manager (`cargo`) makes it easy to work with external dependencies. This is wonderful for development, but means that Rust applications +Rust's package manager (`cargo`) makes it easy to work with external dependencies. While this benefits development, Rust applications and libraries tend to have many dependencies. -Our library only depends directly on a handful of third-party libraries; however, those libraries pull in dozens of their own dependencies. Here's how we manage -our direct and indirect dependencies. +Our library directly depends on only a few third-party libraries, though those libraries have many transitive dependencies. ## Dependency Whitelisting -We use a dependency whitelist to ensure that we never incorporate dependencies into our builds unless they are manually approved. During each CI build, the following -checks are performed: +We use a dependency whitelist to ensure that we never incorporate unapproved dependencies. During each CI build, we perform the following +checks: * Check every dependency against the whitelist. Our CI packaging will fail if add a dependency is added with a license that has not been pre-approved. * Produce a license report called `third-party-licenses.txt` that consolidates all the dependency and license information. We include this document in all of our binary distributions. -* Ignore projects that are 100% copyrighted by Step Function I/O, e.g. the library itself and some dependencies we share between with our other protocol libraries. +* Ignore projects wholly owned by Step Function I/O, e.g. the library itself and dependencies shared with our other protocol libraries. :::note The license report file differs slightly for the Java library as it incorporates some additional components for the JNI functionality. @@ -36,6 +35,6 @@ incorporation of strong copyleft licenses such as the GPL. You can see a complet ## Disclaimer -We've included this information because we take open source license compliance seriously. That said, this information and the `third-party-licenses.txt` file are provided for your reference and do not constitute legal advice. Treat this information as a starting point so you can perform your own due diligence. +We include this information because we take open source license compliance seriously. However, this information and the `third-party-licenses.txt` file are provided for reference and do not constitute legal advice. Treat this information as a starting point so you can perform your own due diligence. diff --git a/guide/docs/about/modbus.mdx b/guide/docs/about/modbus.mdx index 1c7b56f7..99ae8465 100644 --- a/guide/docs/about/modbus.mdx +++ b/guide/docs/about/modbus.mdx @@ -7,8 +7,8 @@ slug: /modbus import useBaseUrl from '@docusaurus/useBaseUrl'; -Modbus is a ubiquitous communication protocol used to communicate with various industrial devices, most notably PLCs. Its simplicity and versatility makes -it very popular in numerous applications. The standard itself is freely available from the [Modbus Organization](https://modbus.org/). It supports both TCP/IP +Modbus is a ubiquitous communication protocol used to communicate with various industrial devices, most notably PLCs. Its simplicity and versatility make +it popular in numerous applications. The standard itself is freely available from the [Modbus Organization](https://modbus.org/). It supports both TCP/IP and serial communication, and more recently, has been defined for secure operation over TLS. Modbus is sometimes referred to as "Modicon Modbus" or just "Modicon" for historical reasons. @@ -55,7 +55,7 @@ remove the leading entity type and subtract 1. ### Point interpretation -Due to the limited point types available in Modbus, the data model is often extended to represent more complex values. You will need to read device +Because Modbus has limited point types, applications often extend the data model to represent more complex values. You will need to read device documentation to interpret the data appropriately. For example, applications often use multiple registers to represent a single physical value. Here are some examples of common point interpretations: @@ -87,8 +87,8 @@ The following table lists the functions currently supported by our library: These functions are all that is required for the vast majority of applications using Modbus. Additional function codes can be added upon request. -Modbus also allows user-defined function codes in the range 65 to 72 and 100 to 110. This library does not support adding user defined functions at this time, -but could be added if desired. +Modbus also allows user-defined function codes in the range 65 to 72 and 100 to 110. This library does not currently support user-defined functions, +but support can be added upon request. ## Exceptions diff --git a/guide/docs/api/client/requests.mdx b/guide/docs/api/client/requests.mdx index 18094c9d..f45bd2ae 100644 --- a/guide/docs/api/client/requests.mdx +++ b/guide/docs/api/client/requests.mdx @@ -33,7 +33,7 @@ The library supports four read requests: | `read_holding_registers` | 16-bit registers | | `read_input_registers` | 16-bit registers | -Each read request takes an `AddressRange` parameter to specify what values are requested. +Each read request takes an `AddressRange` parameter specifying which values to retrieve. It has a 16-bit `start` index and a 16-bit `count` value. :::warning @@ -41,7 +41,7 @@ The sum of the `start` and `count` fields **cannot exceed 65,536**. The library returning an error. ::: -The asynchronous response will contain an iterator with the received values. If an error occurs, the iterator will be empty. +The asynchronous response contains an iterator over the retrieved values. If an error occurs, the iterator is empty. The following example demonstrates reading the first 5 coils from a device: @@ -127,12 +127,10 @@ The registers that are written using the `write_single_register` and `write_mult are *holding registers*. ::: -The "single" requests, as their name suggests, writes a single point at a time. +Single requests write one point at a time. The "multiple" requests write multiple **contiguous** points of the same type in a single message. -In order to write a set of discontinuous points, you must perform multiple requests. Modbus does -not have a transaction mechanism. Therefore, you must keep in mind that the device state -might change in between requests. +To write discontinuous points, perform multiple requests. Modbus lacks a transaction mechanism, so device state may change between requests. The following example demonstrates how to write a coil: diff --git a/guide/docs/api/client/rtu_client.mdx b/guide/docs/api/client/rtu_client.mdx index 81ec15a1..6671ef20 100644 --- a/guide/docs/api/client/rtu_client.mdx +++ b/guide/docs/api/client/rtu_client.mdx @@ -8,7 +8,7 @@ slug: /api/client/rtu_client import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -You can create a RTU client channel using the `create_rtu_client` method. It will immediately try to open the serial port. +Create an RTU client channel using the `create_rtu_client` method. The client immediately attempts to open the serial port. :::note In Rust, you can use the `spawn_rtu_client_task` to create a channel and spawn the async task in the context of the current runtime. @@ -88,8 +88,7 @@ Each channel sends one request at a time and has a fixed-length buffer of reques ## Retry Delay -A serial channel tries to open the serial port as soon as it is created. If the serial port cannot be opened, the library -automatically waits `retry_delay` before retrying to open the port. +The serial channel attempts to open the port immediately upon creation. If the port cannot be opened, the library waits `retry_delay` before retrying. ## Decode Level diff --git a/guide/docs/api/client/tcp_client.mdx b/guide/docs/api/client/tcp_client.mdx index 869a93b4..bca0f219 100644 --- a/guide/docs/api/client/tcp_client.mdx +++ b/guide/docs/api/client/tcp_client.mdx @@ -8,8 +8,7 @@ slug: /api/client/tcp_client import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -The `ClientChannel` class represents a communication channel on which you can make requests to server device. `Channel` presents the same interface once you create it, -regardless of the underlying transport. You can create a TCP client channel using `create_tcp` method. +The `ClientChannel` class represents a communication channel for making requests to server devices. The channel presents the same interface regardless of underlying transport. Create a TCP client channel using the `create_tcp` method. :::note In Rust, you can use the `spawn_tcp_client_task` to create a channel and spawn the runner task in the current runtime. @@ -77,10 +76,9 @@ The argument for the remote endpoint is a string in format the `:` w ## Retry Strategy -A TCP channel tries to establish and maintain a connection as soon as it is created. To avoid flooding, reconnection delays are applied. +A TCP channel establishes and maintains a connection immediately upon creation. The client applies reconnection delays to avoid flooding. -The `RetryStrategy` controls the rate at which the client retries failed connection attempts. The client uses exponential backoff when attempting to establish -a connection. The delay between attempts doubles from `min_delay` up to `max_delay`. +The `RetryStrategy` determines how quickly the client retries failed connections. The client uses exponential backoff: the delay between attempts doubles from `min_delay` up to `max_delay`. ## Decode Level diff --git a/guide/docs/api/logging.mdx b/guide/docs/api/logging.mdx index 0c84b719..6a972fa2 100644 --- a/guide/docs/api/logging.mdx +++ b/guide/docs/api/logging.mdx @@ -11,14 +11,14 @@ import TabItem from '@theme/TabItem'; The library provides highly-contextual logging using the [tracing](https://crates.io/crates/tracing) crate. If you're using Rust, refer to the tracing documentation for details. -In comparison, the bindings use a rigid logging interface with a single callback method to record a message. Configurable options include: +The bindings provide a single callback method for logging messages. Configurable options include: * `LogLevel` that controls which messages are generated * How and if to print the time as part of the message * Line or JSON based output :::note -The LogLevel is set to Info by default. This will record Info, Warn, and Error messages. The Debug and Trace levels are generally only useful if debugging an issue with the underlying runtime. +The LogLevel defaults to Info, recording Info, Warn, and Error messages. Use Debug and Trace levels only when debugging runtime issues. Protocol decoding is always logged at the Info level and is configured separately on a per channel basis. ::: @@ -87,7 +87,7 @@ use. ## Example Output -The logs provide a wealth of contextual metadata so you can: +The logs provide rich contextual metadata, allowing you to: * Determine which communication session produced the message * Understand what state the software was in when the event occurred @@ -122,7 +122,6 @@ protocol stack, including: Refer to the language-specific API documentation for the meaning of each enumeration value. :::note -Protocol decoding is always output at the *Info* log level. If left enabled, it can be too verbose in a production system. When you're debugging a communication issue, -try adjusting the application-layer decoding first to gain visibility into the messages being exchanged on one channel at a time. +Protocol decoding outputs at the Info log level and can generate excessive output in production systems. When debugging communication issues, adjust application-layer decoding first to gain visibility into messages on one channel at a time. ::: diff --git a/guide/docs/api/runtime.mdx b/guide/docs/api/runtime.mdx index bd26dbad..64796e86 100644 --- a/guide/docs/api/runtime.mdx +++ b/guide/docs/api/runtime.mdx @@ -8,17 +8,17 @@ slug: /api/runtime import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -Rust's asynchronous programming model is a form of [cooperative multitasking](https://docs.rs/tokio/1.0.2/tokio/task/index.html). Once a task gets to a point where it would typically block, such as reading from a socket, execution is instead released back to an executor so that another task may run. This lets a pool of worker threads inside the executor efficiently execute thousands of asynchronous tasks concurrently without the overhead of per-task call stacks or thread context-switching. +Rust's asynchronous programming model is a form of [cooperative multitasking](https://docs.rs/tokio/1.0.2/tokio/task/index.html). When a task encounters blocking operations like socket reads, execution transfers to an executor, allowing worker threads to efficiently execute thousands of concurrent tasks without per-task call stacks or thread context-switching overhead. Rust supports asynchronous programming using 'async' functions and 'async/await' syntax. The Rust compiler transforms synchronous-looking code into state machines that are just as efficient as what can be written by hand. Although Rust has this capability built into the compiler, it doesn't include a default runtime to execute the asynchronous programs. Instead, you are free to pick the runtime as an external library. The rodbus library runs on top of the [Tokio](https://tokio.rs/) runtime, providing a state-of-the-art scheduler and platform-agnostic networking APIs. The OS-specific mechanisms vary by platform, for example, *epoll* on Linux and *IOCP* on Windows. -Tokio is a modern evolution of libraries like [libuv (C)](https://libuv.org/) and [ASIO (C++)](https://think-async.com/Asio/). It leverages Rust's thread and memory safety to deliver asynchronous programs that are not only incredibly fast, but also correct. This is extremely important since it is quite difficult to write correct asynchronous software in C/C++ due to the need to manually reason object lifetimes in callbacks. +Tokio is a modern evolution of libraries like [libuv (C)](https://libuv.org/) and [ASIO (C++)](https://think-async.com/Asio/). It leverages Rust's thread and memory safety to deliver asynchronous programs that are both fast and correct. Writing correct asynchronous C/C++ code requires careful manual management of object lifetimes in callbacks. ## Lifetime -You must create a `Runtime` before any communication can take place. It is a shared resource for multiple communication sessions that is typically created just after initializing logging. It is also the last component to shut down; see below for more details about runtime shutdown. +Create a `Runtime` before any communication can take place. The runtime is a shared resource for multiple communication sessions, typically created just after initializing logging. It should be the last component to shut down; see below for details. :::note Rust users can share the runtime with other libraries that also use Tokio. The bindings don't currently support sharing a runtime, but this will be possible in a future release. @@ -81,12 +81,11 @@ Set the number of runtime threads to `0` to default to the number of system core ## Callbacks -The runtime's thread pool invokes callbacks from the library to user code. If you block during a callback, an entire thread is made unavailable for task execution. If all threads in a thread pool are blocked, no communication sessions will execute until -a thread becomes unblocked. +The runtime's thread pool invokes callbacks from the library to user code. Blocking during a callback removes an entire thread from task execution. If all threads in a thread pool are blocked, no communication sessions execute until a thread unblocks. For example, when you receive a log message via a callback, a synchronous call to write the message to a file will block a thread. If this frequently occurs on all your pool threads, it can cause poor throughput or even task starvation. -For best results, avoid blocking whenever possible in your applications. Instead, you should defer blocking calls to dedicated worker threads, such as a user-managed thread that write log messages to file. +Avoid blocking in callbacks. Defer blocking calls to dedicated worker threads, such as a user-managed thread that writes log messages to file. :::tip If you have a case where some blocking is unavoidable, set the number of worker threads to a multiple of the number of system cores, such as 2x or 3x. @@ -102,11 +101,10 @@ worker threads have been joined. A blocked worker thread can cause shutdown to deadlock. For example, if a communication channel makes a callback to user code that permanently blocks, `shutdown` will cause a deadlock. -If you cannot ensure a clean shutdown, you can use `Runtime.set_shutdown_timeout(..)` to put an upper -time limit on the eventual shut down. You would call this method immediately after creating the Runtime. +If you cannot ensure a clean shutdown, use `Runtime.set_shutdown_timeout(..)` to set an upper +time limit on shutdown. Call this method immediately after creating the Runtime. -**Shutting down the runtime using a timeout can leak memory as worker threads are be aborted if the timeout occurs. Only -use this method if you are exiting the process anyway.** +**A shutdown timeout can leak memory because the system aborts worker threads. Only use this method when exiting the process.** ::: ### Logging diff --git a/guide/docs/api/server/database.mdx b/guide/docs/api/server/database.mdx index 7d2389ba..2a83da9f 100644 --- a/guide/docs/api/server/database.mdx +++ b/guide/docs/api/server/database.mdx @@ -8,8 +8,7 @@ slug: /api/outstation/database import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -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. +Use the `Database` class to manipulate the point values that the server exposes to clients. Although called a "database", this is a thread-safe in-memory data structure. :::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. @@ -25,8 +24,7 @@ A transaction can be started on a running server with the `update` method. Insid They will be executed in sequence. :::warning -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. +The transaction mechanism acquires a mutex lock, so keep transactions short. Never perform blocking operations inside a database transaction. ::: ## Database Initialization @@ -125,7 +123,7 @@ values={[ ## 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. +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. :::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. @@ -133,6 +131,6 @@ Since the point may not be defined, the getters can fail. If you try to retrieve ## 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. +Few applications need to remove points, but this capability exists for dynamic scenarios. 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. diff --git a/guide/docs/api/server/rtu_server.mdx b/guide/docs/api/server/rtu_server.mdx index 29552515..ce458104 100644 --- a/guide/docs/api/server/rtu_server.mdx +++ b/guide/docs/api/server/rtu_server.mdx @@ -11,8 +11,7 @@ import TabItem from '@theme/TabItem'; The library supports serial communication using the RTU transmission mode. RTU uses a similar Modbus addressing scheme but adds a CRC to each transmitted frame. -Multi-drop communications may be implemented by having a single client communicating with multiple servers sharing the same serial channel. Broadcasts -messages may be used to write requests simultaneously to all server on a channel. +Multi-drop communications allow a single client to communicate with multiple servers sharing the same serial channel. Use broadcast messages to write requests simultaneously to all servers on a channel. ## Special addresses @@ -23,8 +22,7 @@ handlers. No response will be returned. ### Reserved addresses -Addresses 248 (`0xF8`) to 255 (`0xFF`) (inclusive) are reserved in the specification and **should not be used**. The library does not -enforce this requirement, but a warning message is generated as a reminder that it may not be interoperable. +Do not use addresses 248 (`0xF8`) to 255 (`0xFF`), which are reserved by the specification. The library does not enforce this requirement but generates a warning as a reminder of potential interoperability issues. ## Creating a server @@ -49,7 +47,7 @@ The `Server.CreateRtu` method takes the following arguments: - `level`: Initial decoding level for the port which can be adjusted later via the returned Channel. :::tip -The task handling the port is tolerant to the hardware device being added and removed from the system as might occur with USB to serial adapters. +The task handles dynamic hardware changes, such as USB-to-serial adapter insertion and removal. ::: >Client: Illegal function code exception end ``` -This library uses [rustls](https://github.com/rustls/rustls), a modern TLS library written in safe Rust. It does -**not** depend on OpenSSL or other system libraries, but will interoperate seamlessly with other implementations. +This library uses [rustls](https://github.com/rustls/rustls), a modern TLS library written in safe Rust. It does **not** depend on OpenSSL or other system libraries and interoperates seamlessly with other implementations. Rustls has been [reviewed](https://github.com/rustls/rustls/raw/main/audit/TLS-01-report.pdf) by a third party and is production ready. It [outperforms](https://jbp.io/2019/07/01/rustls-vs-openssl-performance.html) OpenSSL in almost every aspect and has the backing of the @@ -76,8 +75,7 @@ Rustls has been [reviewed](https://github.com/rustls/rustls/raw/main/audit/TLS-0 TLS configuration is configured using the `TlsClientConfig` or `TlsServerConfig` structures. They are very similar and contain the following fields: - `name` (client only): - * The client verifies that the certificate presented by the server is valid for this name. Check out the next section for the technical details on - name validation. + * The client verifies that the certificate presented by the server is valid for this name. See the next section for technical details on name validation. * The client will advertise this name through a Server Name Indication (SNI) extension in the `Client Hello`. - `peer_cert_path`: * Path to the unencrypted PEM file containing the trusted root certificate(s) or the peer self-signed certificate. @@ -117,7 +115,7 @@ If the SAN extension is present, the name is validated against it. The SAN may c The comparison is case-insensitive. If the SAN is absent, then the `Common Name` from the certificate's `Subject` is extracted and compared. The Common Name cannot contain a wildcard character -and the comparison is case-sensitive. It is effectively compared byte-for-byte with the expected name. +and the comparison is case-sensitive. The comparison is byte-for-byte with the expected name. :::tip New certificates should always include the SAN extension. Performing name verification using the `Common Name` @@ -157,8 +155,7 @@ The OpenSSL [pkcs8 command](https://www.openssl.org/docs/man1.1.1/man1/openssl-p encrypt keys or convert private key formats. :::tip -Encrypting private keys for use with TLS rarely addresses a meaningful threat model. Encrypting a private key and storing the password in a configuration file -is equivalent to locking your doors and leaving the key under the doormat. +Encrypting private keys provides minimal security benefit. Storing the password in a configuration file is equivalent to locking your doors and leaving the key under the doormat. ::: ### Certificate Mode @@ -182,7 +179,7 @@ The `SelfSigned` mode validates that: * `NotBefore` and `NotAfter` time fields are valid for the current time :::note -Name validation is performed *indirectly* in self-signed mode, since the byte-for-byte comparison also compares the internal name fields. +In self-signed mode, name validation occurs indirectly through byte-for-byte comparison of the entire certificate, including internal name fields. ::: ### `AuthorizationHandler` diff --git a/guide/docs/examples/summary.mdx b/guide/docs/examples/summary.mdx index 3c2810a5..fe54aaf5 100644 --- a/guide/docs/examples/summary.mdx +++ b/guide/docs/examples/summary.mdx @@ -7,7 +7,7 @@ slug: /examples/summary import sitedata from '../../sitedata.json' -The source repository contains example programs that you can compile and run interactively. Each program demonstrates the usage of various API features by reading console input from the user. +The source repository contains example programs you can compile and run interactively. Each program demonstrates various API features using interactive console input. As you read through the examples, you may notice blocks of code surrounded by `ANCHOR` tags. @@ -19,7 +19,7 @@ code that configures the logging ... // ANCHOR_END: logging ``` -We pull these code blocks from the example programs when we generate this guide. This ensures that all the snippets you encounter when reading the documentation are syntactically valid and up to date. +We extract these code blocks from example programs during guide generation, ensuring all documentation snippets are syntactically valid and current. ## Links diff --git a/guide/docs/languages/bindings.mdx b/guide/docs/languages/bindings.mdx index ceda5d32..0d94f0cc 100644 --- a/guide/docs/languages/bindings.mdx +++ b/guide/docs/languages/bindings.mdx @@ -7,13 +7,11 @@ slug: /bindings import useBaseUrl from '@docusaurus/useBaseUrl'; -We wrote our core library in idiomatic Rust without any concern for how it would be bound into other languages. Consequently, keep in mind that the Rust API won't always -correlate 100% with the various binding APIs. This guide is still helpful for Rust users, but you should be aware that the guide is primarily written from -the perspective of the bindings. +We developed the core library in idiomatic Rust without considering how to bind it to other languages. The Rust API may not directly correspond to the binding APIs. This guide is still helpful for Rust users but is primarily written from the perspective of the bindings. ## Generic Terminology -The guide use generic terms like `struct`, `interface`, and `class`. Because these terms don't map directly to all languages, please refer to our language-specific pages for how these abstract concepts map to language-specific implementations. +This guide uses generic terms like `struct`, `interface`, and `class`. Because these terms don't map directly to all languages, refer to the language-specific pages for how these abstract concepts map to language-specific implementations. :::note For example, Rust doesn't support inheritance, which means it lacks an interface concept like C# and Java. Rust users can assume that we're talking about a boxed trait object whenever we talk about an interface. diff --git a/guide/docs/languages/c_lang.mdx b/guide/docs/languages/c_lang.mdx index 28dd2177..9d703095 100644 --- a/guide/docs/languages/c_lang.mdx +++ b/guide/docs/languages/c_lang.mdx @@ -7,7 +7,7 @@ slug: /c_bindings import useBaseUrl from '@docusaurus/useBaseUrl'; -The C bindings are a simple single header/library distribution which makes application integration extremely straightforward: +The C bindings feature a simple single-header design that simplifies application integration: ```mermaid graph TD @@ -26,8 +26,8 @@ The distribution contains: * CMake find package script in `/cmake/` :::tip -The CMake script is just a recipe for how you might integrate the library. You don't have to use it. Our distribution contains a single header file -and a shared library that can be easily linked. Assuming that you've installed the shared library in /usr/lib, linking a program could be as simple +The CMake script shows how to integrate the library; you don't have to use it. The distribution contains a single header file +and a shared library that link easily. Assuming that you've installed the shared library in /usr/lib, linking a program could be as simple as: ``` @@ -138,7 +138,7 @@ typedef struct dnp3_logger_t * `on_destroy` is the destructor that cleans up the `ctx`. * `on_message` is a function pointer used to dispatch a log message. -Keep in mind that this example only contains a single method. Other interfaces will contain several methods. +This example contains only a single method; other interfaces contain several methods. :::tip If your implementation of an interface is stateless, you can initialize `ctx` and `on_destroy` to NULL. C99 struct initialization syntax @@ -147,7 +147,7 @@ will do this by default if you don't specify a value for these fields. ### Iterators -Collections in the C bindings are always implemented as an opaque iterator type. Think of them as a class with a single "next" method. For example, let's look at an iterator over bool values: +The C bindings implement collections as opaque iterator types. Think of them as a class with a single "next" method. For example, let's look at an iterator over bool values: ```c // opaque iterator @@ -170,12 +170,12 @@ void my_callback(bool_iterator_t* iter, void* ctx) ``` :::warning -Never use an iterator outside the callback. Frequently, the iterator points to memory on the stack and will result in undefined behavior if it is used after the callback is complete. +Never use an iterator outside its callback. Iterators typically reference stack memory, causing undefined behavior if used after callback completion. ::: ### Error Handling -C API error handling is performed using error codes. An error code is always an enum value where the first value is equal to zero, indicating success. +The C API uses error codes for error handling. Error codes are enum values where the first value equals zero, indicating success. Consider an error enum and a function that parses a string as an `int` that can fail: diff --git a/guide/docs/languages/cpp_lang.mdx b/guide/docs/languages/cpp_lang.mdx index 7ed544fa..81268f1e 100644 --- a/guide/docs/languages/cpp_lang.mdx +++ b/guide/docs/languages/cpp_lang.mdx @@ -70,12 +70,12 @@ Most of the abstract concepts in the binding generator map directly to C++. ### Errors -All C API errors are transformed into a C++ exceptions containing the error enum. All exceptions derive from `std::logic_error`. +The code generator transforms all C API errors into C++ exceptions containing the error enum. All exceptions derive from `std::logic_error`. -Other validations (e.g. checking that a moved class isn't used after the move) also throw `std::logic_error`. +Additional validations, such as detecting use-after-move, throw `std::logic_error`. :::warning -Uncaught exceptions thrown in callbacks will terminate the program. Always wrap your callback logic using `try/catch` syntax if there is a possibility the callback will throw. +Uncaught callback exceptions terminate the program. Always wrap callback logic with `try/catch` syntax to prevent uncaught exceptions. ::: ### Iterators @@ -88,17 +88,16 @@ while(iter.next()) { } ``` -The `next()` advances the iterator and returns `true` if a value is available. The `get()` returns the current value pointed, or throws +The `next()` method advances the iterator and returns `true` if a value exists. The `get()` method returns the current value, or throws `std::logic_error` if the end was reached. :::warning -The iterator wrapper does **not** copy and accumulate the values like in C# or Java. Therefore, an iterator should **never** be used outside of the callback. -Frequently, the iterator points to memory on the stack and will result in undefined behavior if it is used after the callback is complete. +The iterator wrapper does **not** copy and accumulate values like in C# or Java. Never use an iterator outside its callback. The iterator typically references stack memory, causing undefined behavior if used after callback completion. ::: ### Collections -Collections are taken through a constant reference to a `std::vector`. The elements will be copied internally. +Collections pass by constant reference to `std::vector`. Elements are copied internally. ### Classes @@ -118,8 +117,7 @@ function as an argument available in the `dnp3::functional` namespace. ### Async methods -C++ doesn't have a robust model for asynchronous computations in the standard library (yet). You can only extract a value -from a C++ `std::future` using the blocking `get` method and there is no way to chain asynchronously chain futures. +The C++ standard library lacks comprehensive asynchronous computation support. You can only extract a value from `std::future` using the blocking `get` method, with no way to chain futures asynchronously. Asynchronous methods are mapped to callback interfaces with two methods: diff --git a/guide/docs/languages/csharp.mdx b/guide/docs/languages/csharp.mdx index 2a88a5d3..f16f735e 100644 --- a/guide/docs/languages/csharp.mdx +++ b/guide/docs/languages/csharp.mdx @@ -12,14 +12,14 @@ The bindings are .NET Standard 2.0 compatible and include platform-specific shar ## Mapping -C# is an object-oriented language that supports all the abstract patterns modeled in the code generator. Here's what you need to know. +C# is an object-oriented language supporting all abstract patterns the code generator defines. ### Errors C API errors are transformed into exceptions that contain the error `enum`. :::warning -Uncaught exceptions thrown in callbacks will terminate the program. Your code should always wrap callback logic with `try/catch` syntax if there is a chance that the callback will throw. +Uncaught callback exceptions terminate the program. Always wrap callback logic with `try/catch` syntax to prevent uncaught exceptions. ::: ### Iterators diff --git a/guide/docs/languages/java.mdx b/guide/docs/languages/java.mdx index b65f0003..cf078748 100644 --- a/guide/docs/languages/java.mdx +++ b/guide/docs/languages/java.mdx @@ -25,7 +25,7 @@ Release artifacts are published to Maven central. Add this dependency to incorpo ## Dependencies -In addition to the Rust dependencies, the Java bindings depend on this open source project: +Besides Rust dependencies, the Java bindings require this open source project: * [joou-java-6](https://github.com/jOOQ/jOOU) - Apache 2.0 - Java Object Oriented Unsigned (JOOU) integer classes @@ -33,7 +33,7 @@ This library is not distributed by Step Function I/O directly. It is only declar ## Unsigned Integers -Java doesn't support unsigned integers as part of the core language. Instead, the Java code generator uses classes from the `JOOU` library. This ensures that numeric types crossing the Java/JNI boundary are pre-validated within the correct range. User code that creates unsigned integers will need to import symbols from the `JOOU` library. +Java lacks native unsigned integer types. Instead, the Java code generator uses classes from the `JOOU` library. This ensures that numeric types crossing the Java/JNI boundary are pre-validated within the correct range. User code that creates unsigned integers will need to import symbols from the `JOOU` library. It is particularly helpful to statically import the factory methods on `Unsigned` class: @@ -57,7 +57,7 @@ Java is an object-oriented language that supports all the abstract patterns mode C API errors are transformed into exceptions containing the error enum. The exception class inherits from `RuntimeException`. :::warning -Uncaught exceptions thrown in callbacks will terminate the program. Always wrap your callback logic using `try/catch` syntax if there is a possibility the callback will throw. +Uncaught callback exceptions terminate the program. Always wrap callback logic with `try/catch` syntax to prevent uncaught exceptions. :::