From e21254cd97d4478657bda2db3f54d5a8f633faf1 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 12:41:36 -0800 Subject: [PATCH 01/11] fix grammatical errors in documentation --- guide/docs/api/master/attributes.mdx | 2 +- guide/docs/api/master/channel_config.mdx | 2 +- guide/docs/api/master/file_transfer.mdx | 2 +- guide/docs/api/outstation/control_handler.mdx | 2 +- guide/docs/api/outstation/database.mdx | 2 +- guide/docs/api/outstation/outstation_application.mdx | 2 +- guide/docs/api/outstation/serial.mdx | 2 +- guide/docs/api/runtime.mdx | 2 +- guide/docs/api/tls.mdx | 4 ++-- guide/docs/languages/cpp_lang.mdx | 6 +++--- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/guide/docs/api/master/attributes.mdx b/guide/docs/api/master/attributes.mdx index 4b651b996..81a808964 100644 --- a/guide/docs/api/master/attributes.mdx +++ b/guide/docs/api/master/attributes.mdx @@ -66,7 +66,7 @@ values={[ ## Writing Attributes -Attributes can be written one at a time or in batches by constructing a request header send it the outstation. The Rust +Attributes can be written one at a time or in batches by constructing a request header and sending it to the outstation. The Rust API uses a single method to add an attribute to the header, whereas the bindings use a separate method for each type of attribute. diff --git a/guide/docs/api/master/channel_config.mdx b/guide/docs/api/master/channel_config.mdx index 2b2734419..a03e00c40 100644 --- a/guide/docs/api/master/channel_config.mdx +++ b/guide/docs/api/master/channel_config.mdx @@ -15,7 +15,7 @@ The `MasterChannelConfig` struct contains the following generic settings that ar * Transmit and receive buffer sizes :::note -The timeout for responses is configured for each association individually. This is useful if the outstation is a gateway services with varying latency. +The timeout for responses is configured for each association individually. This is useful if the outstation is a gateway service with varying latency. ::: The constructor takes the master address and has reasonable defaults for all other settings. The default response timeout is 5 seconds; you may need to increase it for high-latency communication links. You should only need to adjust the default buffer size of 2,048 bytes if an outstation has a non-standard configuration. diff --git a/guide/docs/api/master/file_transfer.mdx b/guide/docs/api/master/file_transfer.mdx index 688a2421f..fbac14e86 100644 --- a/guide/docs/api/master/file_transfer.mdx +++ b/guide/docs/api/master/file_transfer.mdx @@ -160,7 +160,7 @@ a background file transfer, accumulating the file data over possibly multiple bl of `FileInfo` instances. :::warning -The default limit for the size of directory file is 2048 bytes. This is a reasonable and safe limit, however, if its too +The default limit for the size of directory file is 2048 bytes. This is a reasonable and safe limit, however, if it's too small in your application you can raise this limit using the provided parameter in `DirReadConfig`. ::: diff --git a/guide/docs/api/outstation/control_handler.mdx b/guide/docs/api/outstation/control_handler.mdx index 6ea10d9ea..a7d0e1345 100644 --- a/guide/docs/api/outstation/control_handler.mdx +++ b/guide/docs/api/outstation/control_handler.mdx @@ -12,7 +12,7 @@ The `ControlHandler` interface lets application code receive control requests. E with a call to `endFragment`. These callbacks are helpful if you want the application to handle operations atomically or in batches of some sort. Each `select` or `operate` callback returns a `CommandStatus` enum. The outstation response to a control message echoes the request data and this -status code is returned for every control in the message. The library actually builds the response message while making these callbacks. +status code is returned for every control in the message. The library actually builds the response message while making these callbacks. ## Handling SELECT diff --git a/guide/docs/api/outstation/database.mdx b/guide/docs/api/outstation/database.mdx index f624b10d2..cccd57ae0 100644 --- a/guide/docs/api/outstation/database.mdx +++ b/guide/docs/api/outstation/database.mdx @@ -169,7 +169,7 @@ will also enforce that only certain variations are writable. Writable variations ### Private Sets -Private sets are sets with any index other than 0. User may assign any attribute values to variations within these sets +Private sets are sets with any index other than 0. Users may assign any attribute values to variations within these sets other than the reserved variation numbers 0, 254, and 255. diff --git a/guide/docs/api/outstation/outstation_application.mdx b/guide/docs/api/outstation/outstation_application.mdx index f31427593..a09a17527 100644 --- a/guide/docs/api/outstation/outstation_application.mdx +++ b/guide/docs/api/outstation/outstation_application.mdx @@ -65,7 +65,7 @@ A provided reference to a `DatabaseHandle` may be used to copy counters to froze When defining an attribute, you may specify that it is writable. When the outstation receives a WRITE request containing an attribute it follows this procedure: -1. If the attribute is has not been defined the outstation returns `IIN2.NO_FUNC_CODE_SUPPORT`. +1. If the attribute has not been defined the outstation returns `IIN2.NO_FUNC_CODE_SUPPORT`. 2. If the attribute is defined, but the value is not of the correct type the outstation returns `IIN.PARAMETER_ERROR`. 3. The outstation then calls the appropriate handler to ask user code to validate and persist the new value. Rust has a single callback method whereas the bindings have a callback method for each type of attribute. These callbacks may return false to reject the WRITE. For example, diff --git a/guide/docs/api/outstation/serial.mdx b/guide/docs/api/outstation/serial.mdx index b46566e37..b808c78f0 100644 --- a/guide/docs/api/outstation/serial.mdx +++ b/guide/docs/api/outstation/serial.mdx @@ -18,7 +18,7 @@ the following serial port parameters: * Stop bits * Parity * Flow control -* Period of time for retrying the serial port if it cannot be opened or is fails because it is removed from the OS. +* Period of time for retrying the serial port if it cannot be opened or fails because it is removed from the OS. The method will then either open the port or fail if the port doesn't exist or is already in use. The returned `Outstation` class behaves identically to other transport types. diff --git a/guide/docs/api/runtime.mdx b/guide/docs/api/runtime.mdx index bf43e42b4..3ce0a54b4 100644 --- a/guide/docs/api/runtime.mdx +++ b/guide/docs/api/runtime.mdx @@ -88,7 +88,7 @@ a thread becomes unblocked. 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. +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 writes log messages to file. diff --git a/guide/docs/api/tls.mdx b/guide/docs/api/tls.mdx index b70e282b0..47d2f3188 100644 --- a/guide/docs/api/tls.mdx +++ b/guide/docs/api/tls.mdx @@ -94,7 +94,7 @@ definition of a valid name includes: - One or more labels separated by a single period - Labels are made of alphanumeric characters `[A-Za-z0-9]`, hyphens `-`, and underscores `_` -- A label cannot start or end with an hyphen `-` +- A label cannot start or end with a hyphen `-` - A label cannot be all numeric - A label cannot be empty - Maximum of 63 characters per label @@ -115,7 +115,7 @@ is still secure, but it is deprecated for new use cases. Unencrypted private keys may be stored in PKCS#1 or PKCS#8 formats: -* `----BEGIN RSA PRIVATE KEY-----` +* `-----BEGIN RSA PRIVATE KEY-----` * `-----BEGIN PRIVATE KEY-----` diff --git a/guide/docs/languages/cpp_lang.mdx b/guide/docs/languages/cpp_lang.mdx index c33c59efa..bb89124ae 100644 --- a/guide/docs/languages/cpp_lang.mdx +++ b/guide/docs/languages/cpp_lang.mdx @@ -93,8 +93,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 will throw a `std::logic_error`. The class destructor will call the underlying C destructor automatically. @@ -110,7 +110,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. +from a C++ `std::future` using the blocking `get` method and there is no way to asynchronously chain futures. Asynchronous methods are mapped to callback interfaces with two methods: From 01a746430a096f5b29c991a07b96f1a2aca061d3 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 12:43:23 -0800 Subject: [PATCH 02/11] replace placeholder text in C file transfer examples --- guide/docs/api/master/file_transfer.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/guide/docs/api/master/file_transfer.mdx b/guide/docs/api/master/file_transfer.mdx index fbac14e86..0e3df02e6 100644 --- a/guide/docs/api/master/file_transfer.mdx +++ b/guide/docs/api/master/file_transfer.mdx @@ -39,7 +39,7 @@ values={[ ```c -This is possible, but not part of the C example! +// Not demonstrated in this example. ``` @@ -111,7 +111,7 @@ values={[ ```c -This is possible, but not part of the C example! +// Not demonstrated in this example. ``` @@ -184,7 +184,7 @@ values={[ ```c -This is possible, but not part of the C example! +// Not demonstrated in this example. ``` From 8214715b2fc851425d387353ccafedf09577f812 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 12:45:03 -0800 Subject: [PATCH 03/11] fix incorrect cert path in TLS self-signed example --- guide/docs/api/tls.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/guide/docs/api/tls.mdx b/guide/docs/api/tls.mdx index 47d2f3188..e06bd13ba 100644 --- a/guide/docs/api/tls.mdx +++ b/guide/docs/api/tls.mdx @@ -208,5 +208,5 @@ openssl req -x509 -newkey rsa:4096 -keyout ./master_key.pem -out ./master_cert.p ``` - Generate the outstation certificate: ``` -openssl req -x509 -newkey rsa:4096 -keyout ./outstation_key.pem -out ./outstation/entity2_cert.pem -subj "/C=US/ST=Oregon/L=Bend/O=Test/CN=DO NOT USE" -addext "subjectAltName=DNS:test.com" -nodes -days 365 +openssl req -x509 -newkey rsa:4096 -keyout ./outstation_key.pem -out ./outstation_cert.pem -subj "/C=US/ST=Oregon/L=Bend/O=Test/CN=DO NOT USE" -addext "subjectAltName=DNS:test.com" -nodes -days 365 ``` From 01a6b64c902ea4d8320db2f71e7cd7900530c72f Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 12:45:35 -0800 Subject: [PATCH 04/11] remove empty tip block from runtime docs --- guide/docs/api/runtime.mdx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/guide/docs/api/runtime.mdx b/guide/docs/api/runtime.mdx index 3ce0a54b4..c8f90f68a 100644 --- a/guide/docs/api/runtime.mdx +++ b/guide/docs/api/runtime.mdx @@ -181,8 +181,4 @@ If you don't call `Shutdown` before exiting, the .NET runtime will shut down the ::: - - -:::tip - -::: \ No newline at end of file + \ No newline at end of file From a557d4371275eaa502433edbd9c261d070fb0002 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 15:30:44 -0800 Subject: [PATCH 05/11] improve documentation tone: more direct, less verbose --- guide/docs/api/logging.mdx | 10 ++++------ guide/docs/api/master/read_handler.mdx | 7 ++----- guide/docs/api/outstation/database.mdx | 19 +++++++------------ guide/docs/api/runtime.mdx | 11 +++++------ 4 files changed, 18 insertions(+), 29 deletions(-) diff --git a/guide/docs/api/logging.mdx b/guide/docs/api/logging.mdx index 75e54ef45..f6bd8d7fc 100644 --- a/guide/docs/api/logging.mdx +++ b/guide/docs/api/logging.mdx @@ -8,7 +8,7 @@ slug: /api/logging import Tabs from '@theme/Tabs'; 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 +The library uses the [tracing](https://crates.io/crates/tracing) crate for contextual logging. Rust users should 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: @@ -87,7 +87,7 @@ use. ## Example Output -The logs provide a wealth of contextual metadata so you can: +Logs include contextual metadata so you can: * Determine which communication session produced the message * Understand what state the software was in when the event occurred @@ -103,16 +103,14 @@ INFO DNP3-Master-TCP{endpoint="127.0.0.1:20000"}:Task{type=Function(DisableUnsol ## Protocol Decoding -Protocol decoding is configurable on a per-communication channel basis, such as all traffic on a TCP socket or a serial port. You can specify the -`DecodeLevel` when you create a master or outstation or adjust it at runtime. This struct controls the level of decoding (including none) that takes place for each layer of the -protocol stack, including: +Configure protocol decoding per communication channel (TCP socket, serial port, etc.) by setting `DecodeLevel` when creating a master or outstation, or adjust it at runtime. This controls decoding for each layer of the protocol stack: * Application-layer headers, object headers, and object values * Transport-layer header and payload * Link-layer header and payload * Physical-layer length and data bytes -Refer to the language-specific API documentation for the meaning of each enumeration value. The decoding can even show the value of individual fields within objects: +See the language-specific API documentation for enumeration values. Decoding can show individual field values within objects: ``` APP TX - ctrl: [fir: true fin: true con: false uns: false seq: 1] func: Write ... (len = 6) diff --git a/guide/docs/api/master/read_handler.mdx b/guide/docs/api/master/read_handler.mdx index ab184bb6b..01cff725c 100644 --- a/guide/docs/api/master/read_handler.mdx +++ b/guide/docs/api/master/read_handler.mdx @@ -8,8 +8,7 @@ slug: /api/master/read_handler import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -The `ReadHandler` interface is used to receive values from the outstation as it is received by the master. This callback interface is specified -for each association you create and is invoked for both unsolicited responses and responses to READ requests. +The `ReadHandler` interface receives measurement values from the outstation. Specify it for each association; it's invoked for both unsolicited responses and READ responses. When a response is parsed and determined to carry measurement data, `ReadHandler` callbacks are executed in the following order: @@ -35,9 +34,7 @@ Since responses may consist of multiple fragments, you can inspect the `Response in a response series. ::: -It is possible to send a read request with a specific `ReadHandler` by using the `MasterChannel::read_with_handler()` method. -All the response data associated with the request will be forwarded to the `ReadHandler` specified as an argument instead of -going to the handler specified at the creation of the association. +Use `MasterChannel::read_with_handler()` to send a read request with a specific `ReadHandler`. Response data goes to the specified handler instead of the association's default handler. ## Device Attributes diff --git a/guide/docs/api/outstation/database.mdx b/guide/docs/api/outstation/database.mdx index cccd57ae0..5493dd116 100644 --- a/guide/docs/api/outstation/database.mdx +++ b/guide/docs/api/outstation/database.mdx @@ -8,20 +8,17 @@ slug: /api/outstation/database import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -You can use the `Database` class to manipulate the measurement types and values that the outstation exposes to the master. Note that while it's called a "database", it's really just -an in-memory data structure protected by a mutex. +Use the `Database` class to manage measurements the outstation exposes to the master. Despite the name, it's just an in-memory data structure protected by a mutex. All database operations are executed inside a transaction protected by a mutex. Operations within a transaction are applied to the database and the event buffers atomically. If unsolicited responses are enabled, the outstation will automatically decide if an unsolicited response should be sent at the end of the transaction. -The database may be accessed in a transaction in two different ways: +Access the database in a transaction two ways: -1. When measurement values need to be updated due to external changes, the user can call `Outstation.transaction` to acquire -a locked reference to the database and make changes. +1. Call `Outstation.transaction` to acquire a locked reference and make changes. -2. Callbacks on the `ControlHandler` provide a `DatabaseHandle` which also has an identical `transaction method`. Similarly, the freeze operations on -`OutstationApplication` provide this handle as well. +2. `ControlHandler` callbacks and `OutstationApplication` freeze operations provide a `DatabaseHandle` with an identical `transaction` method. :::tip Structure any common update code to operate on the `Database` type and not depend on `Outstation` or `DatabaseHandle`. @@ -29,7 +26,7 @@ Structure any common update code to operate on the `Database` type and not depen ## Adding Points -You must initialize the points before the outstation exposes any measurement data. While you should do this when you create the outstation, you can add points to a running outstation as well. Each measurement type has unique configuration including: +Initialize points before the outstation exposes measurement data. This is typically done at creation, but you can add points to a running outstation. Each measurement type has unique configuration: * An optional event class assignment for the point * Default static and event variations for the type @@ -40,7 +37,7 @@ The default static and event variations for each point type may not do what you some DNP3 event variations don't carry a timestamp. This can be confusing to new users who expect to see a timestamp value that changes as the outstation receives events. -Refer to the documentation for each variation to understand the data associated with it. +See the documentation for each variation to understand its data. ::: When you add a point, it is assigned the following default value with `RESTART` flags: @@ -53,9 +50,7 @@ When you add a point, it is assigned the following default value with `RESTART` Update the value after adding the point if you don't want a connecting master to see the points with a `RESTART` flag set. :::tip -The example code below only shows the definition of contiguous ranges of points, however, the library efficiently supports -using discontiguous ranges as well. In fact, no matter how you define the indices, the library always uses a [BTreeMap](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html) -to store static values and their configuration. +The examples show contiguous point ranges, but discontiguous ranges work efficiently too. The library uses a [BTreeMap](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html) internally regardless of how you define indices. ::: @@ -177,7 +176,7 @@ If you don't call `Shutdown` before exiting, the JVM will shutdown the runtime i ``` :::note -If you don't call `Shutdown` before exiting, the .NET runtime will shut down the runtime in its finalizer (also called the destructor). Again, you should explicitly shut down the runtime before exiting instead of relying on this behavior. +If you don't call `Shutdown` before exiting, the .NET runtime will shut down the runtime in its finalizer. Always explicitly shut down the runtime instead of relying on this behavior. ::: From a3add43cd0c4c32fd1d11fbeb8c88e81dd7b1014 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 15:35:42 -0800 Subject: [PATCH 06/11] continue tonal improvements: cut filler phrases, use imperatives --- guide/docs/api/master/association_config.mdx | 11 +++++------ guide/docs/api/outstation/configuration.mdx | 10 +++++----- guide/docs/api/outstation/tcp_server.mdx | 6 ++---- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/guide/docs/api/master/association_config.mdx b/guide/docs/api/master/association_config.mdx index a3e8f6656..d242e34b5 100644 --- a/guide/docs/api/master/association_config.mdx +++ b/guide/docs/api/master/association_config.mdx @@ -8,14 +8,14 @@ slug: /api/master/association/config import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -The `AssociationConfig` struct provides the configuration information you need to communicate with a particular outstation on the communication channel, including: +`AssociationConfig` configures communication with a particular outstation: * Timeout for responses on this association * Startup handshaking * If and how to perform time synchronization * Per-association task queue size -Note that the outstation address is provided as a separate argument when adding an association to a channel. +The outstation address is provided as a separate argument when adding an association to a channel. >Master: Response; ``` -If your outstation doesn't support unsolicited reporting, you can turn the DISABLE/ENABLE unsolicited requests off by setting -`AssociationConfig.DisableUnsolClasses` and `AssociationConfig.EnableUnsolClasses` to `EventClasses.None()`. The master will then skip unsolicited configuration during initialization, only performing the integrity poll. +If your outstation doesn't support unsolicited reporting, set `AssociationConfig.DisableUnsolClasses` and `AssociationConfig.EnableUnsolClasses` to `EventClasses.None()`. The master will skip unsolicited configuration and only perform the integrity poll. ## Scheduling -The following algorithm is used to schedule requests within an association: +Requests within an association are scheduled as follows: 1. User requests such as control operations get top priority. 2. The association then considers automatic and initialization tasks in this order: @@ -92,7 +91,7 @@ The following algorithm is used to schedule requests within an association: 3. Perform an integrity scan if configured to do so during initialization. 4. Perform automatic time synchronization if enabled, and the master observes the 'NEED_TIME IIN' bit in a response. 5. Enable unsolicited reporting if configured to do so during initialization. -3. Finally, the system will then execute periodic polls. +3. Periodic polls execute last. :::note The standard does not specify how requests should be scheduled for multiple associations on a channel. Our implementation uses a per-association task queue. The channel scheduler round-robins through the associations to ensure fair access. diff --git a/guide/docs/api/outstation/configuration.mdx b/guide/docs/api/outstation/configuration.mdx index e0233761d..9cb03b6c6 100644 --- a/guide/docs/api/outstation/configuration.mdx +++ b/guide/docs/api/outstation/configuration.mdx @@ -14,11 +14,11 @@ You create outstations using three types of components: * Callback interfaces * Transport-specific configuration (e.g., TCP vs. serial parameters) -Let's look at the static configuration required to create an outstation. +The following static configuration is required to create an outstation. ### OutstationConfig -The `OutstationConfig` structure contains the parameters that modify the behavior of the outstation. The generated API documentation provides a definition for each field. All fields have default values except for the master and outstation link addresses. Keep in mind that we didn't optimize these default values for every use case; for example, you may need to adjust the timeout parameters for higher latency links. +`OutstationConfig` contains parameters that modify outstation behavior. See the generated API documentation for field definitions. All fields have defaults except the master and outstation link addresses. Defaults aren't optimized for every use case—you may need to adjust timeouts for higher latency links. :::note -Setting the number of events for a particular type to `0` ensures that you will never record or report events for that type. +Setting the event count to `0` for a type disables event recording and reporting for that type. ::: diff --git a/guide/docs/api/outstation/tcp_server.mdx b/guide/docs/api/outstation/tcp_server.mdx index 7a4026468..306901cd0 100644 --- a/guide/docs/api/outstation/tcp_server.mdx +++ b/guide/docs/api/outstation/tcp_server.mdx @@ -8,9 +8,7 @@ slug: /api/outstation/tcp_server import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -You can bind one or more outstation instances to a TCP server using an `AddressFilter`. Unlike most server protocols, each outstation instance can only -communicate with one client at a time. This is because DNP3 is stateful and maintains event data for a specific master. If you need to support more than -one master connection on the same port, you must preconfigure each master IP address and associate it with a specific outstation instance. +Bind one or more outstation instances to a TCP server using an `AddressFilter`. Unlike most server protocols, each outstation can only communicate with one client at a time because DNP3 maintains event data for a specific master. To support multiple masters on the same port, preconfigure each master IP address and associate it with a specific outstation instance. ```mermaid graph TD @@ -24,7 +22,7 @@ end ## Creating a server -Begin by creating a `TCPServer` instance. Keep in mind that creating a server doesn't initiate `bind/listen` immediately. Endpoints are specified in the format `
:`. Addresses may be specified as any valid IPv4 or IPv6 local endpoint, such as: +Create a `TCPServer` instance. Creating a server doesn't initiate `bind/listen` immediately. Specify endpoints as `
:` where address is any valid IPv4 or IPv6 local endpoint: * `127.0.0.1` for localhost only * `0.0.0.0` for all adapters From 4d53bf9f190910609c5fd399c96798f2b68dc556 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 15:37:44 -0800 Subject: [PATCH 07/11] tonal improvements to outstation interface docs --- guide/docs/api/outstation/control_handler.mdx | 10 +++------- guide/docs/api/outstation/outstation_application.mdx | 11 +++++------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/guide/docs/api/outstation/control_handler.mdx b/guide/docs/api/outstation/control_handler.mdx index a7d0e1345..e787f48b2 100644 --- a/guide/docs/api/outstation/control_handler.mdx +++ b/guide/docs/api/outstation/control_handler.mdx @@ -8,8 +8,7 @@ slug: /api/outstation/controls import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -The `ControlHandler` interface lets application code receive control requests. Each request begins with a call to `beginFragment` and ends -with a call to `endFragment`. These callbacks are helpful if you want the application to handle operations atomically or in batches of some sort. +The `ControlHandler` interface receives control requests. Each request begins with `beginFragment` and ends with `endFragment`, useful for handling operations atomically or in batches. Each `select` or `operate` callback returns a `CommandStatus` enum. The outstation response to a control message echoes the request data and this status code is returned for every control in the message. The library actually builds the response message while making these callbacks. @@ -27,8 +26,7 @@ sequenceDiagram Outstation->>Master: response; ``` -DNP3 masters can also use a "select-before-operate" strategy to execute controls. The rules for how outstations process these -two-pass control messages are complex. The library will handle all these rules for you automatically. +DNP3 masters can also use a "select-before-operate" strategy. The rules for processing these two-pass control messages are complex, but the library handles them automatically. ## Handling OPERATE @@ -38,9 +36,7 @@ The `operate*` methods of `ControlHandler` are called when the outstation receiv * `DIRECT_OPERATE` function code (single-pass control with response). * `DIRECT_OPERATE_NO_RESPONSE` function code (single-pass control without a response). -A reference to a `DatabaseHandle` also lets you update point values in response to a control request. While you would typically use it to update -`BinaryOutputStatus` and `AnalogOutputStatus` values, you can also use it to update other types if required by the application. For example, you might -maintain the number of control operations performed in a DNP3 counter value. +A `DatabaseHandle` reference lets you update point values in response to controls. Typically used for `BinaryOutputStatus` and `AnalogOutputStatus`, but you can update other types too—for example, tracking control operations in a counter. :::tip Every call to `DatabaseHandle.transaction` locks and unlocks a mutex. Since DNP3 can carry multiple controls in a single message, the most diff --git a/guide/docs/api/outstation/outstation_application.mdx b/guide/docs/api/outstation/outstation_application.mdx index a09a17527..5e4745795 100644 --- a/guide/docs/api/outstation/outstation_application.mdx +++ b/guide/docs/api/outstation/outstation_application.mdx @@ -8,7 +8,7 @@ slug: /api/outstation/application import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -The `OutstationApplication` interface provides callbacks for dynamic events and information that the outstation requires from your application: +`OutstationApplication` provides callbacks for dynamic events and information the outstation requires: * Internal Indication (IIN) bits * Processing requests to WRITE the time @@ -19,8 +19,7 @@ The `OutstationApplication` interface provides callbacks for dynamic events and ## ApplicationIin -The outstation will call `OutstationApplication::getApplicationIin()` whenever the system performs a solicited or unsolicited response. It will merge -private IIN bits with the bits returned by the `ApplicationIin` structure, which contains four internal indication bits that you can control: +The outstation calls `OutstationApplication::getApplicationIin()` for every solicited or unsolicited response, merging your `ApplicationIin` bits with internal IIN bits. You control four bits: * `needTime`: Outstation requires time synchronization * `localControl`: Some output points are in local mode @@ -52,10 +51,10 @@ If you must implement this functionality to pass conformance tests or due to cus `OutstationApplication` contains two methods related to freezing counters: -* `freezeCountersAll`: This is called when a counter freeze operation is received using the All Points (0x06) qualifier. -* `freezeCountersRange`: This is called when a counter freeze operation is received using 8-bit (0x00) or 16-bit (0x01) range qualifiers. +* `freezeCountersAll`: Called for freeze operations using the All Points (0x06) qualifier. +* `freezeCountersRange`: Called for freeze operations using 8-bit (0x00) or 16-bit (0x01) range qualifiers. -A provided reference to a `DatabaseHandle` may be used to copy counters to frozen counters. The `FreezeType` parameter lets you choose between two types of supported freeze operations: +Use the provided `DatabaseHandle` to copy counters to frozen counters. `FreezeType` specifies the operation: * `IMMEDIATE_FREEZE`: Copy the current value of a counter to the associated frozen counter point. * `FREEZE_AND_CLEAR`: Copy the current value of a counter to the associated frozen counter point and clear the current value to 0. From 45da324a6d7a5ea88d9c36e20552dad5b054fee5 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 15:39:30 -0800 Subject: [PATCH 08/11] tonal improvements to TLS and DNP3 overview docs --- guide/docs/about/dnp3.mdx | 5 ++--- guide/docs/api/tls.mdx | 9 +++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/guide/docs/about/dnp3.mdx b/guide/docs/about/dnp3.mdx index 18880f7e6..e95fe4828 100644 --- a/guide/docs/about/dnp3.mdx +++ b/guide/docs/about/dnp3.mdx @@ -24,7 +24,7 @@ A DNP3 outstation is a field device that provides measurement data and controls * Capacitor banks * Voltage-regulators -If you use DNP3 over TCP/IP, the outstation typically acts as a TCP server. This lets it accept connections from one or more masters acting as a TCP client. +Over TCP/IP, the outstation typically acts as a server, accepting connections from one or more masters acting as TCP clients. :::note Unlike typical client/server architecture where multiple clients can access the same server, DNP3 statefully buffers event data for a specific master. @@ -33,8 +33,7 @@ If you plan to connect multiple masters to the same TCP endpoint, you must preco ### Virtual RTUs -You can also use a DNP3 outstation to transfer measurement and control data from a utility SCADA system to a historian or automation process. This is also known as a virtual remote terminal unit (RTU) since you implement it as a software -processor within the SCADA server without separate physical hardware. +A DNP3 outstation can transfer measurement and control data from a SCADA system to a historian or automation process. This is called a virtual RTU—a software processor within the SCADA server with no separate physical hardware. ```mermaid graph LR diff --git a/guide/docs/api/tls.mdx b/guide/docs/api/tls.mdx index e06bd13ba..69ea56ff0 100644 --- a/guide/docs/api/tls.mdx +++ b/guide/docs/api/tls.mdx @@ -8,9 +8,7 @@ slug: /api/tls import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -Masters and outstations may optionally use TLS which protects DNP3 communication by adding encryption and authentication. The DNP3 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. +Masters and outstations can use TLS for encryption and authentication. The 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 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 @@ -62,11 +60,10 @@ We recommend using TLS to secure DNP3 and caution the industry to avoid home-gro ## Configuration -TLS configuration is configured using the `TlsClientConfig` or `TlsServerConfig` structures. They are very similar and contain the following fields: +Configure TLS using `TlsClientConfig` or `TlsServerConfig`. Both contain these fields: - `name`: - * The client and the server both verify that the certificate presented by the peer is valid for this name. Check out the next section for the gory details on - name validation. + * Both client and server verify the peer's certificate is valid for this name. See the next section for name validation details. * 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. From 8dde8f6f6a86eb725c483bd4ed85ad3b3524aa61 Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 15:43:04 -0800 Subject: [PATCH 09/11] tonal improvements across multiple docs --- guide/docs/about/guide.mdx | 3 +-- guide/docs/api/master/association_create.mdx | 5 ++--- guide/docs/api/master/association_handler.mdx | 5 ++--- guide/docs/api/master/association_polls.mdx | 3 +-- guide/docs/api/master/controls.mdx | 2 +- guide/docs/api/master/terminology.mdx | 5 ++--- guide/docs/languages/bindings.mdx | 4 +--- 7 files changed, 10 insertions(+), 17 deletions(-) diff --git a/guide/docs/about/guide.mdx b/guide/docs/about/guide.mdx index 297e187e9..36391cbf4 100644 --- a/guide/docs/about/guide.mdx +++ b/guide/docs/about/guide.mdx @@ -28,8 +28,7 @@ Language-specific API documentation is available via the links below: ## IEEE-1815 -While this guide covers many DNP3 concepts, it is not a replacement for the DNP3 standard. If you plan to develop a product that uses DNP3, a copy of the -standard is indispensable. Purchase your copy from the [DNP3 Users Group](https://dnp.org) or [IEEE](https://standards.ieee.org/standard/1815-2012.html). +This guide covers many DNP3 concepts but is not a replacement for the standard. If you're developing a DNP3 product, the standard is indispensable. Purchase it from the [DNP3 Users Group](https://dnp.org) or [IEEE](https://standards.ieee.org/standard/1815-2012.html).
DNP3 logo diff --git a/guide/docs/api/master/association_create.mdx b/guide/docs/api/master/association_create.mdx index 82fc033c2..3729fb561 100644 --- a/guide/docs/api/master/association_create.mdx +++ b/guide/docs/api/master/association_create.mdx @@ -8,7 +8,7 @@ slug: /api/master/association/create import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -You can create associations on a `MasterChannel` by specifying configuration and callback interfaces. +Create associations on a `MasterChannel` by specifying configuration and callback interfaces. -The `AssociationId` struct that is returned when adding an association is just a token. While it's required to perform operations on a particular outstation on the channel, -you don't need to keep a reference to it unless you need it to perform operations later. Allowing the `AssociationId` to be garbage collected won't remove the association from the channel. +The returned `AssociationId` is just a token. It's required for operations on a particular outstation, but you don't need to keep a reference unless you need it later. Garbage collecting the `AssociationId` won't remove the association from the channel. ## Removing diff --git a/guide/docs/api/master/association_handler.mdx b/guide/docs/api/master/association_handler.mdx index a189cfd88..a1b7f0596 100644 --- a/guide/docs/api/master/association_handler.mdx +++ b/guide/docs/api/master/association_handler.mdx @@ -8,10 +8,9 @@ slug: /api/master/association_handler import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -The `AssociationHandler` interface handles all the per-association callbacks that are unrelated to measurement data. It contains a single method (`get_current_time`) -to retrieve a UTC timestamp used to synchronize the time on the outstation. +`AssociationHandler` handles per-association callbacks unrelated to measurement data. It contains a single method (`get_current_time`) to retrieve a UTC timestamp for time synchronization. -Typically, you'd return the system time in `get_current_time` which is kept synchronized via NTP. However, you can use a different clock if desired. For example, you might want a data concentrator to receive time from an upstream DNP3 master while maintaining an offset from an internal clock. The examples below demonstrate how to implement an `AssociationHandler` that retrieves the system time. +Typically, return the system time (synchronized via NTP) in `get_current_time`. You can use a different clock if desired—for example, a data concentrator receiving time from an upstream DNP3 master. The examples below retrieve the system time. Date: Sun, 21 Dec 2025 15:43:54 -0800 Subject: [PATCH 10/11] tonal improvements to tcp_client, outstation, c_lang docs --- guide/docs/api/master/tcp_client.mdx | 8 +++----- guide/docs/api/outstation/outstation.mdx | 5 ++--- guide/docs/languages/c_lang.mdx | 4 ++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/guide/docs/api/master/tcp_client.mdx b/guide/docs/api/master/tcp_client.mdx index a469f38af..a9ed90b0c 100644 --- a/guide/docs/api/master/tcp_client.mdx +++ b/guide/docs/api/master/tcp_client.mdx @@ -8,8 +8,7 @@ slug: /api/master/tcp_client import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -A `MasterChannel` presents the same interface once you create it, regardless of the underlying transport. You can create a TCP client channel using the runtime, -configuration data, and a callback to observe the status of the connection. +A `MasterChannel` presents the same interface regardless of transport. Create a TCP client channel using the runtime, configuration data, and a connection status callback. :` tuple where "host" is one of the following: +An `EndpointList` contains remote endpoints the master will try to connect to. It must have at least one entry; additional entries serve as backups. The channel round-robins through the list until connected. Each endpoint is a `:` tuple where "host" is: * IPv4 address * IPv6 address diff --git a/guide/docs/api/outstation/outstation.mdx b/guide/docs/api/outstation/outstation.mdx index 2517aeb49..78b0efd0d 100644 --- a/guide/docs/api/outstation/outstation.mdx +++ b/guide/docs/api/outstation/outstation.mdx @@ -8,10 +8,9 @@ slug: /api/outstation/outstation_handle import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -The `Outstation` class corresponds to an asynchronous task that implements the outstation state machine and responds to requests from a single master. In addition, it provides transactional access to the `Database` for configuring and modifying the values -sent to the master. We'll cover database interaction in the next section. +The `Outstation` class is an asynchronous task that implements the outstation state machine and responds to requests from a single master. It provides transactional access to the `Database` for configuring and modifying values sent to the master (covered in the next section). -In addition to accessing the Database, this class lets you modify the `DecodeLevel` of the running outstation. This can be extremely useful in production environments for debugging communications between a master and the outstation. +This class also lets you modify the `DecodeLevel` of the running outstation, useful for debugging communications in production. :::tip The Outstation class has the same interface regardless of which transport-specific method you used to create it. This allows you to treat an Outstation operating diff --git a/guide/docs/languages/c_lang.mdx b/guide/docs/languages/c_lang.mdx index 855b1a195..ed09d0567 100644 --- a/guide/docs/languages/c_lang.mdx +++ b/guide/docs/languages/c_lang.mdx @@ -95,7 +95,7 @@ target_link_libraries(my_awesome_project PRIVATE dnp3) ## Mapping -Many of the concepts built into higher-level languages are just design patterns or idioms in C. When you see these higher levels patterns discussed in this guide, you can use the idioms to understand how they map to C. +Many concepts built into higher-level languages are just design patterns or idioms in C. Use the idioms below to understand how they map to C. ### Classes @@ -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 only contains a single method. Other interfaces contain several methods. :::warning Always use [C99 struct initialization](https://en.cppreference.com/w/c/language/struct_initialization) when defining interfaces From 3abdd9cb92aa512d380b3ccf3f98a14391b211ea Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Sun, 21 Dec 2025 15:44:38 -0800 Subject: [PATCH 11/11] tonal improvements to interoperability and dependencies docs --- guide/docs/about/dependencies.mdx | 6 ++---- guide/docs/about/interoperability.mdx | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/guide/docs/about/dependencies.mdx b/guide/docs/about/dependencies.mdx index a285fc9eb..484956f33 100644 --- a/guide/docs/about/dependencies.mdx +++ b/guide/docs/about/dependencies.mdx @@ -8,11 +8,9 @@ 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 -and libraries tend to have many dependencies. +Rust's package manager (`cargo`) makes it easy to work with external dependencies. This is great for development, but means 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 depends directly on a handful of third-party libraries, but those pull in dozens of their own. Here's how we manage dependencies. ## Dependency Allowlisting diff --git a/guide/docs/about/interoperability.mdx b/guide/docs/about/interoperability.mdx index 3c8f3075c..7c38170a4 100644 --- a/guide/docs/about/interoperability.mdx +++ b/guide/docs/about/interoperability.mdx @@ -11,12 +11,12 @@ DNP3 defines four subset levels of interoperability: * `Level 2`: This adds several function codes and objects in order to support a simple RTU or data concentrator. * `Level 3`: This adds additional function codes and objects, most notably enable/disable of unsolicited reporting, in order to support complex RTUs/concentrators. * `Level 4`: This adds rarely-used features, such as floating-point analog variations, double-bit binary inputs, and device attributes. -* Please note that features like file transfer, datasets, and secure authentication do not belong to any subset level. +Features like file transfer, datasets, and secure authentication do not belong to any subset level. ### Current Support: Level 3 -Our library is currently `Level 3` compliant for both master and outstation. We will make it `Level 4` compliant in a future release. Please note that we already support a number of `Level 4` features, along with features that do not belong to a subset level including: +Our library is currently `Level 3` compliant for both master and outstation. `Level 4` compliance is planned for a future release. We already support a number of `Level 4` features, along with features outside subset levels: | Feature | Objects | Level | |---------------------------|---------------------------------------------------------|:----------:|