Cleanud up

This commit is contained in:
2026-09-30 22:41:23 -06:00
parent ba3729a4a1
commit 96ed822ebb
14 changed files with 169 additions and 566 deletions
+9
View File
@@ -0,0 +1,9 @@
* text=auto
*.py text eol=lf
*.c text eol=lf
*.h text eol=lf
*.m4 text eol=lf
*.md text eol=lf
*.so binary
*.precompilecache binary
*.library binary
+16
View File
@@ -0,0 +1,16 @@
# Native build outputs
component-source/component/out/
component-source/component/*.o
component-source/tests/driver
component-source/tests/bridge_driver
component-source/tests/test_bridge
__pycache__/
*.pyc
# CODESYS local working caches
*.precompilecache
*.compileinfo
*.bootinfo
*.bak
*.opt
# Keep the supplied cache as a reference artifact
!artifacts/arm64/CmpAtomModbus_library.precompilecache
+41 -62
View File
@@ -1,41 +1,37 @@
# atMb — documented working library # Atom Modbus Library (`atMb`)
Start with `library/atMb-Documented.export`: comments-only documentation of your CODESYS library with a native C engine for Modbus TCP client, RTU client and TCP
working CmpAtomModbus export, retaining its embedded C library object. It uses slave operation. Public namespace: `atMb`. No ModbusFB dependency is required.
`ClientTCP`, matching the uploaded working interface.
Read `docs/USER-GUIDE.md` for setup and examples and `docs/API-REFERENCE.md` for ## Use the library
all 95 code objects, parameters, outputs, methods and properties.
`docs/DOCUMENTATION-VALIDATION.md` records code/payload preservation checks.
`reference/Working-CmpAtomModbus.export` is the exact uploaded original.
The earlier development export and generator are retained for source history. Open `library/CmpAtomModbus.library` in CODESYS, or install it using Library
`library/build_native_export.py` generates the earlier migration export; it does Repository and reference it from your application. Keep its project title and
not generate or supersede the newly documented working export. Do not overwrite external C interface settings intact. The development target is Raspberry Pi
working IDE changes by regenerating that migration export. The documentation 64 SL (ARM64), using CODESYS 3.5 SP22; runtime/target compatibility must be checked
adds no C changes; the source-to-uploaded-binary match remains unverified. when changing platforms. Read [the user guide](docs/USER-GUIDE.md) for examples
and [the API reference](docs/API-REFERENCE.md) for members and methods.
The rest of the source/build kit remains below. ## Repository contents
# atMb — native Modbus for CODESYS | Path | Purpose |
|---|---|
| `library/CmpAtomModbus.library` | Working CODESYS library file; edit IEC code here |
| `component-source/src/` | C protocol, client, serial and server implementation |
| `component-source/component/` | Active component frame, ABI interface and makefile |
| `component-source/tests/` | Host protocol and bridge tests |
| `ExtensionSDK/` | Supplied SDK required for component builds |
| `artifacts/arm64/libCmpAtomModbus.so` | Supplied ARM64 native binary |
| `docs/BUILD-AND-USAGE.md` | Native build and deployment instructions |
| `docs/USER-GUIDE.md` | Calling patterns, configuration and diagnostics |
| `docs/API-REFERENCE.md` | Detailed IEC reference based on the last working export |
| `docs/CLEANUP.md` | Removed files, preservation checks and update workflow |
| `docs/VALIDATION.md` | Validation scope |
| `SHA256SUMS.txt` | Current working-file snapshot checksums, excluding Git metadata |
Git-ready source snapshot, revision 0.9.1. Public namespace `atMb`. ## Build on the Pi
C implementation supports TCP client, RTU client and TCP slave; IEC code has
no ModbusFB dependency. Online-change support remains future work.
## Files Run from the repository root:
- `component-source/`: C engine, CODESYS component frame/interface and host tests.
- `ExtensionSDK/`: supplied SDK used by the component build.
- `library/atMb-Native.export`: corrected IEC import; includes public status declarations.
- `library/atMb-Native-Review.st`: readable generated IEC source.
- `library/build_native_export.py`: reproducible export generator.
- `reference/CmpAtomModbus-AllInOne.export`: required generator baseline, not the final import.
- `artifacts/arm64/`: your uploaded shared object and precompile cache, preserved unchanged.
- `docs/BUILD-AND-USAGE.md`: import, deployment, API, diagnostics and limitations.
- `SHA256SUMS.txt`: snapshot file checksums.
## Build and test on the Pi
```sh ```sh
sudo apt install build-essential m4 dos2unix python3 sudo apt install build-essential m4 dos2unix python3
@@ -44,38 +40,21 @@ make -C component-source/component SDKDIR="$(realpath ExtensionSDK)" all
make -C component-source test make -C component-source test
``` ```
Build natively for each Linux target architecture; the uploaded ARM64 `.so` Native output: `component-source/component/out/libCmpAtomModbus.so`.
cannot run on ARM32 or x86-64. Runtime/SDK compatibility must also be checked See the build guide before updating the embedded component. An ARM64 binary
for each CODESYS target. The host tests exercise protocol code independently cannot run on ARM32 or x86-64; build for each architecture and verify the SDK/runtime
of the CODESYS runtime. combination. The supplied binary has not been proven byte-equivalent to this source.
## IEC export ## Update and review
```sh Edit the IEC library in CODESYS. After significant IEC changes, export the current
python3 library/build_native_export.py objects and update the API reference against that export. Binary `.library` files
``` cannot be reviewed as ordinary text diffs. This snapshot intentionally contains no
old migration exports or generators that overwrite current IDE changes.
Import `library/atMb-Native.export` into a fresh CODESYS library project, This ZIP retains the existing Git history and remote metadata. Cleanup changes
namespace `atMb`, and embed the newly built `.so` in a C Implemented Library are uncommitted and unstaged. Review locally with `git status` and `git diff`,
object. See the full instructions in `docs/BUILD-AND-USAGE.md`. then stage/commit when ready. No push is performed by this package.
The export is source, not a compiled `.library`. The supplied precompile cache
is an opaque reference artifact and is not a substitute for the library project.
Your uploaded `.so` is ARM64 and contains RTU/server symbols. Its exact match Online-change qualification remains future work. Keep all native calls owned by
to this source snapshot and its operation in the runtime have not been verified; one IEC task. No new license is assigned to the supplied SDK/source files.
rebuild from source before making a release. The older development instructions
say no prebuilt binary is included: that applies to the earlier kit; this repo
snapshot adds your uploaded binary under `artifacts/arm64/`.
## Start a repository
```sh
cd atMb-repo
git init
git add .
git commit -m "Initial atMb native Modbus source"
```
No Git history or remote is included. The SDK and baseline came from supplied
files; review their redistribution terms before publishing a public repository.
No new license is assigned by this snapshot.
+8 -18
View File
@@ -1,5 +1,5 @@
d1192772047955638e8bb171080ed9b37e10683de935e4e0503c93b6d26f03fc .gitattributes d1192772047955638e8bb171080ed9b37e10683de935e4e0503c93b6d26f03fc .gitattributes
675d431f63e2c212360b692b18de50a344aed5664d17b3683f0b687b96479f24 .gitignore 02f0eff1a80c9a8f2fe037d1b5980eb391b65e4dc07dba6fe5d109b08c180c1a .gitignore
0ac6e4fe82a82d1bd27ff20dec2f18820c0e0a5057cd0cb1a8020f6dc506603f ExtensionSDK/README 0ac6e4fe82a82d1bd27ff20dec2f18820c0e0a5057cd0cb1a8020f6dc506603f ExtensionSDK/README
1cc355e875e572de3829d28bff9c100a4eecbfd741d938654be97881023c84d5 ExtensionSDK/include/CmpErrors.h 1cc355e875e572de3829d28bff9c100a4eecbfd741d938654be97881023c84d5 ExtensionSDK/include/CmpErrors.h
2010310fbeb8b4734e638610d2f6dc01b600fe42b8b104155806ef0784e95f2d ExtensionSDK/include/CmpItf.h 2010310fbeb8b4734e638610d2f6dc01b600fe42b8b104155806ef0784e95f2d ExtensionSDK/include/CmpItf.h
@@ -10,16 +10,13 @@ a8de018bfde8d10d3f281bb45de03c4b4f3cd28466eb0fa0981086bc4033a69c ExtensionSDK/i
814d06ca83d775f3bcb93f80859602b6c1debe9511b3d42ef337948bd2e2b5d0 ExtensionSDK/makefile 814d06ca83d775f3bcb93f80859602b6c1debe9511b3d42ef337948bd2e2b5d0 ExtensionSDK/makefile
4fc8013238169e924ee2eb5dd6c2b39c9d274f4c7c2219dc0b9a530687097519 ExtensionSDK/makefile_template 4fc8013238169e924ee2eb5dd6c2b39c9d274f4c7c2219dc0b9a530687097519 ExtensionSDK/makefile_template
aa7ccad11508ccbf4f9b24aa8db8f22392f9d723ae3af5cbfa67e38271cfbac3 ExtensionSDK/src/CmpFrame.c aa7ccad11508ccbf4f9b24aa8db8f22392f9d723ae3af5cbfa67e38271cfbac3 ExtensionSDK/src/CmpFrame.c
67076180ec906d9f27f5b63406d8a2701faa58771f76e0f4487ae750660ce7e4 README.md 11aee0c6ff01eaff1aac5aa46c470ea8367d5eae655068320869a1bcdce1ef53 README.md
f0279c023b82eeda388cdc1b9c9f358b7794869360a06684df670cd34d5cab56 artifacts/arm64/CmpAtomModbus_library.precompilecache
07527262a8efcf9add5554cfcaed22cf927af63b985e431ab5341078a6864667 artifacts/arm64/libCmpAtomModbus.so 07527262a8efcf9add5554cfcaed22cf927af63b985e431ab5341078a6864667 artifacts/arm64/libCmpAtomModbus.so
14db5bb96e65494eed63da0139b37734dfc08e312a3cb0924ad10b7d9476d442 component-source/Makefile 14db5bb96e65494eed63da0139b37734dfc08e312a3cb0924ad10b7d9476d442 component-source/Makefile
d91cffa9751bca79f13ae80552d654519333536c894bb13b41a1182c179c53b7 component-source/component/CmpAtomModbus.c d91cffa9751bca79f13ae80552d654519333536c894bb13b41a1182c179c53b7 component-source/component/CmpAtomModbus.c
30a8d7d5dfd372d171d4acdafd0b77cedca684e0b6e661ae1ab67e2627d35708 component-source/component/CmpAtomModbusItf.m4 30a8d7d5dfd372d171d4acdafd0b77cedca684e0b6e661ae1ab67e2627d35708 component-source/component/CmpAtomModbusItf.m4
513586eed183bcc53cdf008c483da03feff6b5cb0f3ed3eff17de1462def12c3 component-source/component/CmpFrame.c 513586eed183bcc53cdf008c483da03feff6b5cb0f3ed3eff17de1462def12c3 component-source/component/CmpFrame.c
0972948a5cc88892421433fb01875f4f51609cda3b0df57ff58f05c61390e1c2 component-source/component/makefile 0972948a5cc88892421433fb01875f4f51609cda3b0df57ff58f05c61390e1c2 component-source/component/makefile
c362c04636cb2cfc00eec8800f0aa9631709cd9eba76b66332f61a988823a055 component-source/generated/CmpAtomModbus.c
fa9b00a24ff9f24b63c9a08b8eba4d585a23f4fd6e19f990ec36dcc70f745427 component-source/generated/CmpAtomModbusItf.m4
78240595fa115d3706cbc7316bdcd4e0101a3269e5faacce98826bc32143e9d9 component-source/src/atom_bridge.c 78240595fa115d3706cbc7316bdcd4e0101a3269e5faacce98826bc32143e9d9 component-source/src/atom_bridge.c
4a3eaca066d0c06ac7fa95d802ff78b270a1ccdc3fc67c6a072b7e09efba08c0 component-source/src/atom_bridge.h 4a3eaca066d0c06ac7fa95d802ff78b270a1ccdc3fc67c6a072b7e09efba08c0 component-source/src/atom_bridge.h
4f772a0dab94053bfa2f073f6877ec63a8c6b7999d8e3415160705db441f6068 component-source/src/atom_mb.c 4f772a0dab94053bfa2f073f6877ec63a8c6b7999d8e3415160705db441f6068 component-source/src/atom_mb.c
@@ -33,16 +30,9 @@ d770393632f9d3d1acddf229f562148648792398a39d90851aa529d2923f8382 component-sour
3af1a5f71d69bae2abda3de9dba31740ad254d7157d702847fb921fc2b64df3d component-source/tests/test_bridge.c 3af1a5f71d69bae2abda3de9dba31740ad254d7157d702847fb921fc2b64df3d component-source/tests/test_bridge.c
2a4b582bed0404f865f4fa2c905dd251c3dccfc4961f20467723c7349793a7f2 component-source/tests/test_native.py 2a4b582bed0404f865f4fa2c905dd251c3dccfc4961f20467723c7349793a7f2 component-source/tests/test_native.py
0cf8f8fefda6376cacf0c56729d24aaa76241e807b0dee0b36001b3ad3cea509 component-source/tests/test_tcp.py 0cf8f8fefda6376cacf0c56729d24aaa76241e807b0dee0b36001b3ad3cea509 component-source/tests/test_tcp.py
c1f09f7498453182f7ed88c1cb343b637ca6c6507e728ac71591a826588c0d3a docs/API-REFERENCE.md ca8eca5c351bcc426676213c162606d1be9ef6a07d017dad74aa10c198e4ea91 docs/API-REFERENCE.md
a3cd41084413b1f7c2045e6a7a647b793c8df1c9fa338e146439031b269a1dcb docs/BUILD-AND-USAGE.md 43eb385327aa81deb28a55b08068a9b6e565ad290257d2cbd5819d1ab7558ae3 docs/BUILD-AND-USAGE.md
17d84b44d288b46012d9e52d7c8a247592ad28f3d5c3032fffbab701a8478418 docs/DOCUMENTATION-VALIDATION.md a0931348b32a336798b7d93a5956fd14bd481597626bc1d40b1597ee6ed5e6a3 docs/CLEANUP.md
3a2d04af7a1fc7f2e39fea1401d12653fda16864cf4cb7d8edfe2d9988091453 docs/USER-GUIDE.md ef14901695852012e92ae7dde304244e441c17f2bc17abd4fb7e7a2e30f2e9df docs/USER-GUIDE.md
fe7df15839ff4bcf3272cbec71cd67a026e514c79df4bd0be829c8a373391f4d docs/VALIDATION.md b35e03e6cd3e23719794802a65663ddc22986731dd52f6713ab71b94d1798f53 docs/VALIDATION.md
8aa5a11a22263ea04902ce883b14fd31c3de270c711deca13f57889a05b689a0 docs/document_export.py be0edea894f02a6f525307da69fd52498d4ff76e4b95728608ff6db8f6877e39 library/CmpAtomModbus.library
87e2f904f08d7ee8c37ba8362989d2453b2860e91336f21623d0ab91eb8a178b library/atMb-Documented-Review.st
19a979f94b437fc6c523fdfbd24fd8f6038860cb107b45dea2466020e2a319fe library/atMb-Documented.export
0efb6d1760ea5104331cd0fb13b7324fd468c5ab2ec415f0dd9e38db74961e70 library/atMb-Native-Review.st
e2223cf6cef8cecb71cd41093c67f095559764eaa52e22f0f31347e3faec7238 library/atMb-Native.export
1d66a3b8cefa95e39815b12d1e802192b83d416ad281aaca5b8efa8c90ed4f77 library/build_native_export.py
c3be3f6ee2e065633f8e8e04482c95fe5c30f61e34cf6223d6454500a756dbb2 reference/CmpAtomModbus-AllInOne.export
5a07da11853e955633a977bdd59a7d1f8f292c594c2116bc220bd63c8edc9329 reference/Working-CmpAtomModbus.export
@@ -1,10 +0,0 @@
#include "CmpStd.h"
#include "CmpErrors.h"
#include "CmpItf.h"
#include "CmpAtomModbusDep.h"
void CDECL CDECL_EXT atmbcall_cext(atmbcall_cext_struct *p)
{
}
@@ -1,70 +0,0 @@
/**
* <interfacename>CmpAtomModbus</interfacename>
* <description></description>
*
* <copyright></copyright>
*/
SET_INTERFACE_NAME(`CmpAtomModbus')
/** EXTERN LIB SECTION BEGIN **/
/* Comments are ignored for m4 compiler so restructured text can be used. changecom(`/*', `*/') */
#ifdef __cplusplus
extern "C" {
#endif
/**
* <description>
* Create FUNCTION atMbCall_cext returning UDINT, paste this declaration.
* Properties > Build > External implementation must be selected.
* Library filename and Project Information title: CmpAtomModbus.
* This declaration is the SINGLE external entry point for the C engine.
* </description>
* <element name="atMbCall_cext" type=OUT></element>
* <element name="command" type=IN></element>
* <element name="handle" type=IN></element>
* <element name="ipAddress" type=IN></element>
* <element name="port" type=IN></element>
* <element name="unitId" type=IN></element>
* <element name="functionCode" type=IN></element>
* <element name="startAddress" type=IN></element>
* <element name="quantity" type=IN></element>
* <element name="pData" type=IN></element>
* <element name="dataCapacity" type=IN></element>
* <element name="pStatus" type=IN></element>
* <element name="statusCapacity" type=IN></element>
* <element name="nowMs" type=IN></element>
* <element name="connectTimeoutMs" type=IN></element>
* <element name="responseTimeoutMs" type=IN></element>
* <element name="retryDelayMs" type=IN></element>
*/
typedef struct tagatmbcall_cext_struct
{
RTS_IEC_UDINT command;
RTS_IEC_UDINT handle;
RTS_IEC_DWORD ipAddress;
RTS_IEC_UINT port;
RTS_IEC_BYTE unitId;
RTS_IEC_BYTE functionCode;
RTS_IEC_UINT startAddress;
RTS_IEC_UINT quantity;
RTS_IEC_WORD *pData;
RTS_IEC_UDINT dataCapacity;
RTS_IEC_UDINT *pStatus;
RTS_IEC_UDINT statusCapacity;
RTS_IEC_ULINT nowMs;
RTS_IEC_UDINT connectTimeoutMs;
RTS_IEC_UDINT responseTimeoutMs;
RTS_IEC_UDINT retryDelayMs;
RTS_IEC_UDINT atMbCall_cext;
} atmbcall_cext_struct;
DEF_API(`void',`CDECL',`atmbcall_cext',`(atmbcall_cext_struct *p)',1,0x709EE65B,0x00020000)
#ifdef __cplusplus
}
#endif
/** EXTERN LIB SECTION END **/
+1 -1
View File
@@ -1,6 +1,6 @@
# atMb API reference # atMb API reference
Generated from your working `CmpAtomModbus(1).export`. Documents every code object, including internal methods and property accessors. Runtime implementations, identifiers, declarations and embedded native payloads are preserved in the documented export; additions are comments only. Reference based on the last supplied working `CmpAtomModbus(1).export`, with 95 code objects. The current editable/installable library is `library/CmpAtomModbus.library`. This reference includes internal methods and property accessors. The binary library was preserved unchanged but not decoded to independently confirm its current declarations. Update this document against a fresh export when IEC code changes.
## atMbCall_cext ## atMbCall_cext
+51 -186
View File
@@ -1,203 +1,68 @@
# atMb native TCP + RTU + TCP slave — development revision 0.9 # Build and deployment
The IEC export and C implementation have no ModbusFB dependency. All socket/ ## Build native C
serial/frame processing runs in the native component. Public namespace: atMb.
Standard and SysTime are still required for IEC timers and time measurement.
This is a tested host development build, not a compiled CODESYS library or a From the repository root on the target Linux machine:
hardware-qualified Pi release. Full CODESYS compilation and physical RTU testing
remain required. Online-change ownership/pointer relocation is future work.
## Rebuild on the Pi (required)
Extract this bundle on the Pi, preserving component-source and ExtensionSDK
as siblings. Install build-essential, m4, dos2unix, make and python3 if absent.
```sh ```sh
cd component-source sudo apt install build-essential m4 dos2unix python3
make -C component SDKDIR="$(realpath ../ExtensionSDK)" clean make -C component-source/component SDKDIR="$(realpath ExtensionSDK)" clean
make -C component SDKDIR="$(realpath ../ExtensionSDK)" all make -C component-source/component SDKDIR="$(realpath ExtensionSDK)" all
file component/out/libCmpAtomModbus.so file component-source/component/out/libCmpAtomModbus.so
make test make -C component-source test
``` ```
The component registration version is now 0.3.0.0. The external atMbCall_cext `component-source/component/` is the active SDK build directory. Its
function ABI, signature 0x709EE65B and generated API version 0.2.0.0 are unchanged. `CmpAtomModbus.c` includes the protocol source files as one translation unit.
No regeneration of runtime interface files is required for these extra commands. The removed `generated/` directory contained original unimplemented stubs;
Keep the supplied generated interface; do not regenerate blindly from a changed it was not used by this makefile. Preserve the active `CmpAtomModbusItf.m4` and
library title/version. No prebuilt Pi binary is included. A strict host compile `CmpFrame.c`; they define ABI registration and cleanup.
of the component function used the supplied SDK headers and original real
parameter struct; the full SDK/M4/component frame build must run on your Pi.
## Build the IEC library without legacy objects The makefile registers component version 0.3.0.0. The supplied external
`atMbCall_cext` API retains signature 0x709EE65B and API version 0.2.0.0.
Do not change the external parameter layout or regenerate it just because the
library title/version was edited. Full SDK/M4 builds must be checked on the target.
1. Create a fresh library project saved as CmpAtomModbus.library (or a backup copy ## Update the CODESYS library
with the old Modbus master/slave folders removed). Import atMb-Native.export.
A fresh project is preferred because import does not delete omitted old methods.
2. Keep Project Information title CmpAtomModbus and default namespace atMb, as
supplied in the export. This is one combined library, not a wrapper referencing
a second CmpAtomModbus/atMb library. Remove those self/wrapper references.
3. Add only Standard and SysTime references needed by this IEC code. Remove the
ModbusFB reference. Resolve their ordinary dependencies if the IDE requests it.
4. Add a C Implemented Library object and embed your NEWLY BUILT ARM64
component/out/libCmpAtomModbus.so for the exact Raspberry Pi 64 SL target.
Use CmpAtomModbus for the component name. The old TCP-only embedded object was
deliberately omitted from this export; do not reuse its binary.
5. Compile, save/install and update references in the Atom library/application.
6. Perform a full download. Verify runtime loads the new component version.
For automatic deployment, stop using the old manually preloaded component 1. Back up `library/CmpAtomModbus.library` and open it in CODESYS.
before testing the embedded one: remove only its ComponentManager entry, then 2. Retain Project Information title `CmpAtomModbus` and namespace `atMb`.
restart the runtime during maintenance. Alternatively, for a manual bench test, 3. Retain the Standard and SysTime references required by the IEC code.
stop the runtime and replace /usr/lib/libCmpAtomModbus.so with the new binary. 4. In its C Implemented Library object, update the native binary for the exact
Inspect /var/log/codesyscontrol.log. No production install is performed by this kit. device target with the newly built `libCmpAtomModbus.so`. Use the component
naming/settings already present in the working project.
5. Compile the library, save/install it in Library Repository and update application
references. Test with a full download for native component changes.
## TCP client The working `.library` is preserved unchanged in this cleanup. Updating a separate
`.so` file does not automatically update the binary embedded inside the library.
The standalone artifact is retained for deployment/build comparison; rebuilding it
does not imply it is identical to the currently embedded implementation.
Existing atMb.Client and ReqReadHolding/ReqReadInput/ReqReadCoil/ReqReadDiscrete/ ## Runtime deployment
ReqWriteHolding/ReqWriteCoil calls remain. Supported C function codes are
1,2,3,4,5,6,15,16. Automatic retry continues while enabled; normal Modbus
exception replies do not force reconnects. The default retry delay is 100 ms.
## Native RTU Use the established embedded-library deployment or a manual bench installation;
avoid loading two versions of the same component simultaneously. The earlier Pi
setup manually loaded `/usr/lib/libCmpAtomModbus.so` with
`Component.1=CmpAtomModbus` under `[ComponentManager]` in
`/etc/CODESYSControl_User.cfg`. When switching to embedded deployment, remove only
that manual preload entry during maintenance and restart the runtime as required.
```iecst For a manual installation, stop the runtime before replacing its component and
VAR restart afterwards. Check `/var/log/codesyscontrol.log` for the loaded component
bus : atMb.ClientRTU; and version; `/var/opt/codesys/codesyscontrol.log` was stale on the development Pi.
read : atMb.ReqReadHolding; No deployment or runtime restart is performed by this cleanup.
registers : ARRAY[0..9] OF UINT;
END_VAR
```
```iecst ## Different Linux targets
bus(
in_device := '/dev/ttyUSB0',
in_baudRate := 9600,
in_parity := 0,
in_dataBits := 8,
in_stopBits := 1,
in_timeout := T#500MS,
in_reconnectDelay := T#100MS
);
read(client := bus, in_unitId := 1, in_startAddr := 0,
in_quantity := 10, in_dataOffset := 0, in_minDelay := T#100MS,
data := registers);
```
Call client/requests every scan in ONE task. One RTU client owns each serial bus; Build natively on the desired CPU or use a matching cross-toolchain. ARM64,
register different unit IDs on that same client, rather than opening multiple ARM32 and x86-64 outputs are distinct. SDK, CODESYS runtime interface and libc
clients on the same tty. The native open uses exclusive serial access. compatibility also matter. A successful host protocol test does not validate native
component loading into every runtime version. RTU support uses Linux termios/ioctl;
verify serial permissions, driver behavior and physical timing on the target.
Use /dev/serial/by-id/... when it fits the 127-byte path limit, or a stable ## Further documentation
short udev symlink. The runtime OS user must have access to the device; check the
actual service user and device group on your Pi. Stop other serial consumers.
The old numeric in_port selection was removed; a numeric index is not a portable
Linux device mapping. in_baudRate is now UDINT so 115200 fits.
Supported baud rates: 1200,2400,4800,9600,19200,38400,57600,115200. - [User guide](USER-GUIDE.md): examples, polling, RTU, slave, timeout and recovery.
RTU uses 8 data bits; parity 0 none/1 odd/2 even; stop bits 1 or 2. - [API reference](API-REFERENCE.md): all objects from the last supplied working export.
Unit IDs 1..247 are accepted. Broadcast unit 0 is explicitly rejected because - [Validation](VALIDATION.md): exact scope of checks performed.
broadcast completion cannot be reported as an acknowledged write.
For automatic-direction USB RS485 adapters leave in_rs485 FALSE. TRUE requests
Linux TIOCSRS485 with RTS during transmit; unsupported drivers report an OS error.
No software GPIO direction control or adapter-specific configuration is included.
CRC, unit/function, length and write echo are validated before accepting replies.
Faults close/reopen the tty after the configured retry delay. No raw write packet
is replayed by the engine. The existing scheduler can issue fresh cyclic writes;
use in_holdUncertainWrites TRUE for commands requiring an application decision.
Delayed serial replies have no transaction ID: use suitable timeout/retry settings
for slow devices. Serial silence is conservatively scheduled in milliseconds;
physical adapter/baud/parity/line-turnaround tests are still necessary. PTY tests
cannot certify electrical timing, parity generation or RS485 direction behavior.
out_connected on RTU means the tty is open/ready, not proof that a remote unit
responds. Use per-request online/stale/error status. out_socketError carries the
OS errno for tty failures too. All existing client status counters are shared.
## Native TCP slave
atMb.ModbusSlave and atMb.SlaveTCP both run the C server (no ModbusFB server).
Map four BOOL/UINT arrays using aDiscreteInputsMemory/aCoilsMemory/
aInputRegistersMemory/aHoldingRegistersMemory. Their actual LOWER_BOUND is their
zero-based protocol starting address; each table is limited to 2000 items and
must fit within addresses 0..65535. A full 65536-item table is not supported.
Use in_ipAddress DWORD = 0 to bind all addresses, or numeric IPv4 in human octet
order (16#7F000001 for 127.0.0.1). port defaults to 502. The legacy interfaceName
field is retained but must be empty; numeric bind IP replaces interface-name
binding. Nonempty names are reported as parameter errors rather than ignored.
Example: `slave : atMb.ModbusSlave;` with all four arrays passed every scan:
```iecst
slave(in_enable := TRUE, in_ipAddress := DWORD#0, port := 502,
aDiscreteInputsMemory := discreteInputs,
aCoilsMemory := coils,
aInputRegistersMemory := inputRegisters,
aHoldingRegistersMemory := holdingRegisters);
```
FC1/2/3/4/5/6/15/16 are implemented, with exception replies for unsupported
functions, invalid quantities or unmapped addresses. Four server contexts,
eight simultaneous peers per server. Requests are bounded and processed in
order per connection. A server tick accepts at most one peer and performs at
most one nonblocking I/O action per peer per IEC call. Incomplete frames/send
stall timeout after 5 seconds; idle peers close after 60 seconds.
The wrapper pushes PLC table snapshots, ticks the engine, then pulls completed
remote writes in the same scan. C retains no PLC pointers between calls. Application
writes between calls become the next snapshot. Arrange application order accordingly.
The fixed server limits and scan-bounded throughput differ from the old ModbusFB
server; load test your actual client count/polling rates. No RTU slave is provided.
in_enable controls listening, in_reset recreates the context, endpoint changes
recreate it, and listener failures retry automatically after in_reconnectDelay.
Outputs: out_running/out_error/out_errorId/out_parameterError/out_socketError,
out_connectedClients/out_requestsReceived/out_repliesSent/out_exceptionReplies.
## Native lifecycle and API
All bridge/server contexts are fixed-pool, generation-checked and single-task.
No RETAIN/PERSISTENT handles. release() disables client enable and closes its
context. STOP alone does not guarantee FB_exit: disable and scan before STOP.
New full download required; online-change safety is not claimed in this revision.
Own Error and ExceptionCodes DUTs replace the ModbusFB enums. Error retains the
named compatibility labels used by request statistics. Legacy dynamically
allocated request methods are removed or become native recreateRequest BOOL.
Old allocation-alarm outputs/legacy request reset fields were removed because
these contexts use fixed native pools. A pool-full result is native error 257.
Command 20: open RTU. data contains one device-path ASCII byte per WORD, quantity
is path length; ipAddress=baud; unitId=parity; functionCode=bits;
startAddress=stop bits; connectTimeoutMs=kernel RS485 flag (0/1).
The existing commands 2..11 configure/tick/submit/close this client normally.
Commands 30..36: server open/close/enable/disable/tick/push/pull. Push/pull use
functionCode as table 0 coils/1 discrete/2 holding/3 input, startAddress as base
and quantity as length. Server handles use distinct low-byte tags from clients.
Server status indices: 0 handle,1 running,2 error,3 result,4 OS errno,5 peers,
6 requests,7 replies,8 exception replies. Client status layout is unchanged.
## Tests and remaining checks
Run make test on Linux. Includes existing nine TCP scenarios directly and through
the bridge, pool/handle tests, PTY RTU read/fragment/CRC/timeout/recovery/exception/
register-write/coil-write/invalid-write-echo recovery tests and native slave FC1/2/3/4/5/6/15/16, nonzero mapping,
exception/listener-recovery/stale-handle tests. All passed on the development host.
The CODESYS XML has no ModbusFB identifiers and passes structural checks. It has
not been compiled in the IDE; hardware Pi/serial and embedded deployment tests
remain. Do not interpret host tests as target or online-change qualification.
v0.9.1 export correction
------------------------
Restores ClientBase public diagnostics: msgLastSecondCnt, msgLastSecondTime,
numMsgSent, numMsgReply, allRequestsOnline and reqStats. The generator now
checks these declarations and balanced VAR sections. Replace matching objects
when importing the corrected export. C sources are unchanged from v0.9; this
correction alone does not require rebuilding an already installed v0.9 component.
The full native migration still requires the C build and embedding described above.
Validated XML and declaration checks locally; CODESYS compilation must be checked
in the IDE.
+25
View File
@@ -0,0 +1,25 @@
# Repository cleanup
Removed unused files:
- `component-source/generated/`: original empty C stubs; active makefile builds
from `component-source/component/`.
- Both `CmpAtomModbus_library.precompilecache` files: local CODESYS caches,
regenerated by the IDE; older duplicate was under artifacts.
- `docs/document_export.py`: depended on an export already absent from this ZIP.
- `docs/DOCUMENTATION-VALIDATION.md`: report for an export absent from this ZIP;
current validation is consolidated in `VALIDATION.md`.
Rewrote README/build instructions to use the actual working `.library`, removing
references to deleted exports, migration scripts and first-time Git initialization.
Retained API/usage details and stated that their baseline is the last supplied export.
Updated ignore rules and file checksums.
Preserved exact bytes of the `.library`, ARM64 `.so`, active C sources, SDK, tests
and all existing Git metadata. Git changes are not staged, committed or pushed.
Old files remain recoverable from Git history where previously committed.
To apply to an existing checkout, replacing files alone will not remove obsolete
files. Prefer extracting this full snapshot into a separate directory and reviewing
its existing Git status; alternatively remove the listed obsolete paths explicitly
while applying updated files, keeping your checkout's own `.git` directory.
-11
View File
@@ -1,11 +0,0 @@
# Documentation validation
Documented 95 code objects.
- All interface executable tokens unchanged after removing comments/whitespace.
- All XML content outside Interface text identical after parse/serialization.
- Implementations, GUIDs, native payloads, project information and references unchanged.
- Documented XML parses successfully.
- No CODESYS IDE compile performed; this is a comments-only change.
Input SHA256: `5a07da11853e955633a977bdd59a7d1f8f292c594c2116bc220bd63c8edc9329`
+6 -2
View File
@@ -1,6 +1,10 @@
# atMb usage and behavior # atMb usage and behavior
This guide describes the supplied working export, including its renamed `ClientTCP`. The documented export changes comments only and retains the embedded C Implemented Library object. Import with replacement of matching objects into your existing library project. No C rebuild is needed for these documentation changes. This guide describes the last supplied working IEC export, including `ClientTCP`.
The current repository stores the working file at `library/CmpAtomModbus.library`.
Use that file in CODESYS. No IEC or C implementation is changed by repository cleanup.
The binary library format was not decoded to reverify its member declarations;
when changing IEC code, update this guide against a fresh CODESYS export.
## Choosing the block ## Choosing the block
@@ -211,6 +215,6 @@ States: 0 disabled, 1 retry wait, 2 connecting, 3 ready, 4 sending, 5 receiving.
## Maintenance and scope ## Maintenance and scope
The documented export preserves the working implementations, metadata and embedded payloads. Internal native fields are sometimes exposed as VAR_INPUT for compatibility; leave them under library control. Several legacy BOOL methods do not assign a return value; the API reference identifies them so callers rely on their side effects. The cleanup preserves the supplied working library and standalone native binary. Internal native fields are sometimes exposed as VAR_INPUT for compatibility; leave them under library control. Several legacy BOOL methods do not assign a return value; the API reference identifies them so callers rely on their side effects.
Lifecycle hooks exist, but reliable online-change ownership and pointer relocation remain future work. Documentation adds no behavior changes or online-change guarantee. Use the API reference for every method/property, including internal accessors. Lifecycle hooks exist, but reliable online-change ownership and pointer relocation remain future work. Documentation adds no behavior changes or online-change guarantee. Use the API reference for every method/property, including internal accessors.
+12 -12
View File
@@ -1,14 +1,14 @@
# Snapshot validation # Cleanup validation
- Export generator completed: 99 objects, no ModbusFB references. - Working `.library`, standalone ARM64 `.so`, active C/component sources, tests,
- Public diagnostics and balanced VAR declaration checks passed. SDK and all Git metadata compared byte-for-byte with the uploaded ZIP: unchanged.
- Strict host C compilation passed. - Removed precompile caches, unused generated stubs and broken generator script.
- Bridge pool/stale handle/configuration/shutdown tests passed. - README/build instructions no longer refer to absent migration exports/scripts.
- Nine TCP scenarios passed directly and through the native bridge. - Strict host compilation and bridge tests passed after cleanup.
- RTU pseudo-terminal tests passed, including CRC/timeout recovery, partial frames, - Nine direct TCP and nine bridge TCP scenarios passed.
writes, exceptions and invalid write echo recovery. - RTU frame/write/error/recovery tests passed.
- TCP slave FC 1/2/3/4/5/6/15/16, exception, nonzero base, listener recovery - TCP slave FC1/2/3/4/5/6/15/16, exception/address and recovery tests passed.
and stale handle tests passed.
No CODESYS IDE compile, full ARM64 SDK build, physical serial hardware test, No CODESYS compile or full ARM64 SDK build was run. Physical RTU operation and
or online-change qualification was performed for this repository package. online-change behavior were not qualified by these checks. API documentation is
based on the last supplied working IEC export, not a decoded binary `.library`.
-194
View File
@@ -1,194 +0,0 @@
from pathlib import Path
import xml.etree.ElementTree as E
import re,hashlib,json,zipfile
B=Path(__file__).resolve().parent
src=B.parent/'reference/Working-CmpAtomModbus.export'
r=E.parse(src).getroot();entries=list(r.find('.//List2[@Name="EntryList"]'))
def meta(e,n):return e.find('./Single[@Name="MetaObject"]/Single[@Name="'+n+'"]')
names={meta(e,'Guid').text:meta(e,'Name').text for e in entries}
def path(e):
n=meta(e,'Name').text;p=e.find('./Single[@Name="ParentSVNodeGuid"]').text
return names.get(p,''),n
def blob(e,p):return e.find('./Single[@Name="Object"]/Single[@Name="'+p+'"]//Single[@Name="TextBlobForSerialisation"]')
summary={
'ClientBase':'Abstract shared client scheduler and native transport owner. Use ClientTCP or ClientRTU. Holds up to 100 registered requests and runs one transaction at a time. Call the concrete client once per scan, from the same task as its requests.',
'ClientTCP':'Modbus TCP client with automatic retry after connection or transaction failure. Call every scan. Endpoint or timeout/retry configuration changes close the current context and reopen it on subsequent scans. Requests share this connection and execute sequentially.',
'ClientRTU':'Modbus RTU client using a Linux serial device. Call every scan. Supports 8 data bits, parity 0 none/1 odd/2 even, and 1 or 2 stop bits. Uses unit IDs 1..247; broadcast 0 is unsupported. Serial-setting changes recreate the context. Automatic recovery proceeds while enabled and scanned.',
'RequestBase':'Abstract request binding, scheduling and diagnostics. Derived requests register automatically when called with a valid initialized client. Call each instance every scan with stable array storage. Quantity zero disables this request. Actual length is MIN(in_quantity, remaining array capacity).',
'RequestStats':'Request counters, completion properties, online/offline tracking and 100 ms indicator lights. Normally maintained by RequestBase. Online means a valid response was received, including a Modbus exception; it does not guarantee successful data. Several fields are declared VAR_INPUT for compatibility but are maintained internally.',
'To_IPAddress':'Returns the four supplied bytes as ARRAY[0..3] OF BYTE. Example: atMb.To_IPAddress(192,168,50,10). This is the ClientTCP address format; the slave bind address instead uses a packed DWORD.',
'ModbusSlave':'Native Modbus TCP server. Same implementation and interface as SlaveTCP. Call once per scan with all four memory arrays. Array bounds are protocol addresses; each table is limited to 2000 elements and addresses 0..65535. Pushes application memory, performs bounded network processing, then copies native memory back. This is TCP only, not an RTU slave.',
'SlaveTCP':'Native Modbus TCP server with four separate memory tables. Same implementation and interface as ModbusSlave. FC 1/2/3/4 read the tables; FC 5/6/15/16 write coils/holding registers. Array lower bounds define Modbus addresses. Call every scan and retain array storage. This is TCP only.',
'ClientStatus':'Aggregate request status used by the RTU monitoring property: noneOk=0, someOk=1, allOk=2. This describes request online flags, not the physical connection. The current toString getter compares onlineCnt with all registered requests and reports allOk for an empty registry.',
'Error':'IEC error classification. TCP-named socket/timeout members are also used for RTU failures. For the precise native reason inspect out_nativeError/out_lastNativeError; for an OS failure inspect out_socketError.',
'ExceptionCodes':'Modbus exception response values plus local TIMEOUT=-1. A valid Modbus exception proves communication, but the requested operation failed. In current statistics, non-exception errors can also be classified as TIMEOUT; use native diagnostics to distinguish their actual cause.',
'atMbCall_cext':'External C ABI entry point used by the wrappers. Application code should normally call the function blocks. Do not change parameter order/types or external implementation settings. Pointers are accessed synchronously by C; provide distinct valid data/status buffers and truthful capacities. Client status requires 24 UDINT elements. All calls must be owned by one IEC task.',
'addRequest':'Register a non-null RequestBase pointer once. Duplicate pointers are ignored. Capacity is 100 requests per client; overflow sets out_registrationFull. No removal API is provided. Normally called automatically by the request FB.',
'nativeCall':'Internal adapter to atMbCall_cext. Command 20 packs the ASCII serial path into WORD storage and passes serial settings in ABI fields; other commands use normal client arguments and status buffers. Return is the native command result, 0 for success.',
'nativeCycle':'Internal per-scan engine: acquire context, tick nonblocking I/O, update diagnostics, copy completed read data, process registered requests, acknowledge completion, select the next request, and publish statistics. Invalid native handles are discarded and reopened on a later scan.',
'nativeEnsureContext':'Acquire/configure/enable a native context if absent. Returns TRUE when a handle exists or initialization succeeds. A handle does not mean connected; check out_connected. Failure records a native error and closes partially initialized contexts.',
'nativeRelease':'Close the native context, complete any active request as cancelled, clear selection, set registered requests offline and invalidate cached read data. Returns TRUE. It does not set in_enable FALSE; an enabled scanned client can reopen.',
'nativeSelectNextRequest':'Select an active, unheld request that meets in_minDelay and was not just completed this scan. Lowest in_priorityCurrent wins; oldest sinceLastExec breaks ties. Positive priorities are decremented when the best selected priority is positive. The selected request is submitted immediately; zero priority is not a prerequisite for execution.',
'nativeUpdateDiagnostics':'Publish native connection state, last error, errno and reconnect count. States 3..5 count as connected. A connected-to-disconnected transition marks registered requests offline.',
'nativeUpdateStatistics':'Publish IEC send/reply counters, registered statistics pointers and active-request aggregate online flags. Every elapsed window of at least 1000 ms publishes submitted-request count and successful transaction-duration sum, then restarts the window.',
'processAllRequests':'Internal loop over registered non-null requests, dispatching processRequest. Returns immediately for an empty or invalid registry. Usually invoked by nativeCycle rather than application code.',
'recreateClient':'Close/release the current context so the next enabled scan creates a fresh one with current settings. Cancels an active transaction and invalidates cached read data. Abstract on ClientBase; implemented by both concrete clients.',
'release':'Stop this client by setting in_enable FALSE, then release its native context. Returns the release result. To resume, set in_enable TRUE and keep calling the client. A call that supplies in_enable := TRUE every scan will re-enable it.',
'setAllRequestsOffline':'Internal helper marking every registered request offline via its statistics FB. Returns TRUE after processing a nonempty valid registry; early return does not explicitly assign the return value.',
'setupClient':'Returns whether nativeHandle is nonzero. It does not create a context or guarantee transport connectivity. Normal FB scanning performs initialization.',
'activeIndex':'Get the current active request registry index, or -1 when idle. Setting a valid index sets that request stats.execute TRUE; it does not bypass native scheduler selection or directly submit a transaction.',
'activeReqCnt':'Private count of registered requests whose stats.active is TRUE. Zero quantity/usable length requests are inactive.',
'allRequestsComplete':'Private legacy completion check. TRUE only if none of the registered requests has execute, reqDone, reqBusy or reqError set. Empty registry returns FALSE. This is not a network completion pulse.',
'onlineCnt':'Count registered requests with stats.online TRUE. Includes any registered request with that flag; used for aggregate reporting.',
'requestExecuting':'Private property returning activeIndex <> -1. Represents selected/active request state maintained by the scheduler.',
'toString':'ClientRTU monitoring property returning ClientStatus based on onlineCnt versus requestCnt. Reports allOk for an empty registry. RequestStats also has an internal STRING field of this name; these are different values.',
'busy':'Assign stats.reqBusy from input in. Internal request helper. Although declared BOOL, its implementation does not assign a return value; use the side effect rather than the return.',
'done':'Set stats.reqDone. When in is TRUE, restore in_priorityCurrent to in_prioritySetpoint for the next scheduler cycle. Successful completion counters are maintained by the stats property setter.',
'err':'Set stats.reqError and, on TRUE, record errorId/exceptionCode. Set next priority to 10 * MIN(consecutive errors,3)^2. Lower numeric priority wins, so this backs off repeated failures relative to lower-priority-number requests.',
'invalidateNativeData':'Mark cached data invalid and stale. Does not erase the application array. Called during client release; applications should gate use of cached values with validity and age flags.',
'nativeFinish':'Complete a request from native result. Map errors, copy errno/write uncertainty, clear busy, update response/counters and successful-read freshness. Modbus exceptions count as replies and communication-online evidence. If enabled, uncertain writes are held pending writeNow().',
'nativeProcess':'Shared request processing called by derived processRequest. Completes native results or submits a selected request, sets its timeout and records busy/send counters. Updates data age and stale flags during ordinary processing. Completion branches can return before age refresh, so allow a subsequent scan for freshness diagnostics.',
'replyTimeout':'Protected timeout property expressed internally in microseconds. Request in_timeout is milliseconds and takes precedence, then client TIME in_timeout, then a dataRate formula, then 1 second fallback. Submission rounds up to milliseconds and clamps to at least 1 ms.',
'setClientOffline':'Clear nativeResponseSeen and consecutive-success count, force online FALSE/offline TRUE. Does not clear data arrays. Declared BOOL but does not assign a return value.',
'setToString':'Private statistics helper formatting OK:<doneCnt>/Er:<errorCnt> into STRING(20). Declared BOOL but does not assign a return value.',
'execute':'Statistics execution property. Setting TRUE starts the execution state only when reqDone and reqError are FALSE; otherwise the setter clears execution. Usually controlled by the scheduler.',
'reqBusy':'Get/set the underlying request-busy flag. Normally managed by the native wrapper. The scan-held reqBusyLight provides a more visible monitoring indicator.',
'reqDone':'Get/set completion flag. When TRUE during execution, record duration, increment total/consecutive successes, clear consecutive errors and last error, clear execution and update the monitor string. Do not repeatedly drive this manually.',
'reqError':'Get/set error flag. When TRUE during execution, increment total/consecutive errors, clear consecutive successes and execution. If lastExceptionCode is success, increment timeoutCnt and set TIMEOUT even for some non-timeout native errors.',
'recreateRequest':'Clear this request nativeInFlight. If it is the client active request, recreate the whole client connection first. Returns TRUE. May affect other requests sharing the same connection; this is not an independent transport.',
'writeNow':'Clear uncertain-write hold and out_writeUncertain, then set in_priorityCurrent to 0. Returns TRUE. Requests a prompt opportunity at the next scheduler selection; does not guarantee transmission in this call and does not bypass connection/minimum-delay requirements.',
'serverCall':'Private adapter for native server commands. Uses separate server status and WORD staging buffers. Returns the command result; 0 means command accepted, not necessarily that the listener has bound.',
'FB_exit':'CODESYS lifecycle hook releasing owned native resources. Applications should use release/recreateClient rather than call lifecycle hooks manually. Server hooks close the server context; the client hook calls nativeRelease.',
'FB_reinit':'CODESYS lifecycle hook clearing native handles and in-flight state. ClientBase also clears the request registry for re-registration; RequestBase clears execution/completion flags; servers clear their handle. These hooks alone do not establish qualified online-change support.'}
for name,fc,kind,limit in [('ReqReadCoil',1,'BOOL coils',2000),('ReqReadDiscrete',2,'BOOL discrete inputs',2000),('ReqReadHolding',3,'UINT holding registers',125),('ReqReadInput',4,'UINT input registers',125),('ReqWriteCoil',15,'BOOL coils',1968),('ReqWriteHolding',16,'UINT holding registers',123)]:
summary[name]=f'Modbus FC {fc}: '+('write ' if fc>=15 else 'read ')+f'{kind}. Call every scan with a client reference and data array. Protocol start address is zero-based; in_dataOffset is the actual array index. Maximum transaction length is {limit}. Usable length is clamped to array capacity. '+('Values are copied into native storage at submission. This FB uses the multiple-write FC even for one element. Use writeNow() to release a held uncertain write.' if fc>=15 else 'Only a successful validated reply updates the destination slice. Old values remain after failures; check out_validData and out_stale.')
fields={
'in_enable':'Enable processing; TRUE by default. FALSE releases transport. Continue scanning to refresh state.',
'in_timeout':'Reply timeout override. ClientBase: TIME; RequestBase: SYSTIME numeric milliseconds (not microseconds). Zero selects inherited/calculated timeout.',
'in_resetStats':'Reset request consumed and cleared by nativeCycle. Supplying TRUE on every call resets statistics every scan; pulse once.',
'in_ipAddress':'ClientTCP: four IPv4 bytes. Slave: packed numeric DWORD, 0 binds all interfaces; e.g. DWORD#16#C0A8321F for 192.168.50.31.',
'in_port':'Remote TCP port, default 502.', 'in_baudrate':'TCP gateway serial baud-rate hint for calculated reply timeout; 0 uses internal dataRate 105200. Does not configure a serial port.',
'in_connectTimeout':'TCP connection-attempt deadline, default 2 s; clamped to at least 1 ms.',
'in_reconnectDelay':'Delay before native recovery attempt, default 100 ms; clamped to at least 1 ms. Effective timing also depends on scan interval and failure detection.',
'in_holdUncertainWrites':'FALSE allows normal retries after uncertain writes. TRUE holds affected write requests until writeNow(); read requests can continue.',
'in_device':'Linux serial device path, default /dev/ttyUSB0, at most 127 ASCII characters. Runtime account needs access.',
'in_baudRate':'Serial baud rate, default 9600. Supplied C source accepts 1200,2400,4800,9600,19200,38400,57600,115200.',
'in_parity':'0 none, 1 odd, 2 even. Must match the slave.', 'in_dataBits':'Only 8 supported by supplied native implementation.', 'in_stopBits':'1 or 2; must match the slave.',
'in_rs485':'Request Linux kernel RS485 direction control. Leave FALSE for USB adapters handling direction automatically. Requires driver support.',
'client':'Reference to the owning ClientBase concrete instance. Initialize/call client before first registration; retain a stable instance.',
'in_startAddr':'Zero-based Modbus protocol address, not a 40001/30001 display reference. Must fit the transaction into address space.',
'in_quantity':'Requested item count; actual stats.reqLength is MIN(this, available destination/source capacity). Zero makes the request inactive.',
'in_dataOffset':'Actual first index in the application array. For ARRAY[1..10], use 1 to start at the first element; not a relative offset.',
'in_unitId':'Modbus unit/slave ID, default 0. RTU requires explicit 1..247; TCP/gateway values depend on the target.',
'in_minDelay':'Minimum start-to-start time before scheduler eligibility, default 10 ms. Not a fixed polling period.',
'in_prioritySetpoint':'Priority restored on success, default 50. Lower values win. Error backoff can replace current priority.',
'in_priorityCurrent':'Mutable scheduler priority. Lower values win, equal values use age. WriteNow sets 0. Application need not maintain it each scan.',
'in_staleAfter':'Read data age threshold, default 2 s. out_stale becomes TRUE when age is strictly greater; does not stop polling.',
'data':'Open array: BOOL for coils/discretes; UINT for registers. Stable VAR_IN_OUT storage. Writes snapshot values at submission; reads replace only their successful slice.',
'stats':'Per-request RequestStats instance maintained by request processing; inspect rather than overwrite.',
'out_connected':'Transport is in ready/sending/receiving state. Does not prove a request succeeded or data is fresh.',
'out_connectionState':'0 disabled, 1 retry wait, 2 TCP connecting, 3 ready, 4 sending, 5 receiving.',
'out_lastNativeError':'Native last transport error; may persist after recovery. Check current connection and request diagnostics too.',
'out_nativeError':'Result of the last completed/failed request; 0 success. Precise native code, distinct from IEC Error enum.',
'out_socketError':'Native Linux errno snapshot as UDINT; may be historical. Used for both serial and sockets.',
'out_reconnectCount':'Native successful reconnections after the first connection. Context recreation resets its native lifetime counters.',
'out_registrationFull':'Sticky request registry overflow indicator; max 100 requests/client. Client reinitialization clears it.',
'out_writeUncertain':'Write may have reached target without a confirmed response. It is not proof of failure or success. Relevant for write requests.',
'out_validData':'TRUE after a successful read; subsequent read failures do not necessarily clear it. Client release or invalidateNativeData clears it.',
'out_dataAgeMs':'Age since last successful read in milliseconds. Updated by normal processing; completion early returns can defer refresh by one scan.',
'out_stale':'TRUE before valid read data or when age exceeds in_staleAfter. Array contents are retained.',
'out_parameterError':'Array-offset validation for requests; full transaction validation can instead appear in out_nativeError/stats.lastErrorId. Slave: invalid port/interfaceName/table bounds or capacity.',
'msgLastSecondCnt':'Submitted request count in the last completed window of at least 1000 ms, not necessarily an exact sliding second.',
'msgLastSecondTime':'Sum of successful transaction durations in that completed window (ms); excludes failed durations.',
'numMsgSent':'IEC accepted submission count since resetStats; not a count of physical packets.',
'numMsgReply':'IEC successful or Modbus-exception reply count since resetStats.',
'allRequestsOnline':'TRUE when at least one active request exists and onlineCnt equals activeReqCnt. Online can include exception replies.',
'reqStats':'Pointers to registered request statistics by registry index, populated on client scans. Null entries are unused; validate before dereference.',
'in_reset':'Slave one-shot recreation input. FB clears it internally; supplying TRUE every scan recreates every scan.',
'interfaceName':'Legacy slave field. Must be empty; nonempty is rejected. Use packed in_ipAddress instead.',
'port':'Slave listening port, default 502, must be nonzero.',
'aCoilsMemory':'Read/write BOOL coil table. Array bounds are protocol addresses; FC1 reads, FC5/15 write.',
'aDiscreteInputsMemory':'Read-only from network BOOL input table; FC2 reads. Application updates it.',
'aHoldingRegistersMemory':'Read/write UINT holding table; FC3 reads, FC6/16 write.',
'aInputRegistersMemory':'Read-only from network UINT input table; FC4 reads. Application updates it.',
'out_running':'Native listener is bound and enabled. Does not mean a peer is connected.',
'out_error':'Current server error flag when outputs are refreshed; some early returns leave other diagnostics unchanged.',
'out_errorId':'IEC server error classification: parameter/internal/native socket class. Consult errno for OS detail.',
'out_connectedClients':'Number of native TCP peers connected (max 8 per server in supplied C source).',
'out_requestsReceived':'Native request count for current server context.',
'out_repliesSent':'Native fully transmitted reply count for current context, including exception replies.',
'out_exceptionReplies':'Number of Modbus exception replies for current server context.',
'lastErrorId':'Last mapped IEC error; successful completion resets to OK.', 'lastExceptionCode':'Last Modbus exception, success=0; stats may substitute TIMEOUT=-1 for non-exception failures.',
'lastValidDuration':'Last successful request duration (ms).','longestValidDuration':'Maximum successful duration recorded; resetStats does not clear it.',
'interval':'Start-to-start execution interval (ms).','offlineCnt':'Count of NOT online rising edges; can include initial not-online state, not only a physical disconnect.',
'doneCnt':'Total successful completions; resetStats clears.','errorCnt':'Total failed completions; resetStats clears.',
'doneCntRst':'Consecutive successes; reset on error/offline/resetStats.','errorCntRst':'Consecutive errors; reset on successful completion/resetStats.',
'sinceLastExec':'Age since last execution start (ms), capped around 60000 by the statistics clock logic.',
'timeoutCnt':'Statistics non-exception error count; can include other native failures. Not exclusively reply timeouts; resetStats does not clear it.',
'reqLength':'Effective transaction length; zero means inactive.', 'online':'Communication status; valid normal/exception response sets evidence. Distinct from valid data.',
'offline':'Offline status; initialized FALSE, explicitly set on client-offline or repeated failures.', 'active':'TRUE when reqLength is nonzero.',
'executeLight':'Execution indicator with 100 ms off-delay.','reqDoneLight':'Completion indicator with 100 ms off-delay.','reqErrorLight':'Error indicator with 100 ms off-delay.','reqBusyLight':'Busy indicator with 100 ms off-delay.',
'executeMbFb':'Legacy retained output; no assignment in current native implementation. Do not use as a trigger.',
'nativeResponseSeen':'Internal communication-response evidence, maintained by nativeFinish and offline handling.', 'nativeLastSuccessMs':'Internal successful-read timestamp in monotonic milliseconds.',
'command':'Native command selector. Use wrappers in application code.', 'handle':'Generation-checked native context handle; 0 for open.',
'ipAddress':'Packed IPv4 for TCP commands; command 20 overloads this field with RTU baud rate.',
'unitId':'Transaction unit ID; command 20 overloads it with serial parity.', 'functionCode':'Modbus FC; command 20 uses data bits, server push/pull use table ID.',
'startAddress':'Protocol start; command 20 uses stop bits.', 'quantity':'Item count; command 20 uses serial path byte count.',
'pData':'WORD buffer; client reads/writes, serial ASCII path, or server table staging.', 'dataCapacity':'Available WORD elements, not bytes.',
'pStatus':'UDINT status buffer; requires at least 24 elements.', 'statusCapacity':'Available UDINT elements, not bytes.',
'nowMs':'Monotonic time in milliseconds; wrappers use SysTimeGetUs / 1000.', 'connectTimeoutMs':'TCP connection timeout; command 20 uses kernel RS485 flag.',
'responseTimeoutMs':'Reply deadline in milliseconds.', 'retryDelayMs':'Native recovery wait in milliseconds.',
'byte1':'First IPv4 octet.','byte2':'Second IPv4 octet.','byte3':'Third IPv4 octet.','byte4':'Fourth IPv4 octet.',
'bInCopyCode':'Runtime lifecycle copy-code indicator; current implementation does not branch on it.',
'in':'New state to assign. Normally used internally by completion helpers.', 'errorId':'Mapped IEC error supplied to the error helper.', 'exceptionCode':'Modbus exception classification supplied to the error helper.', 'result':'Native completion result; 0 success.'}
original={}
sections=[];count=0
for e in entries:
parent,name=path(e);t=blob(e,'Interface');impl=blob(e,'Implementation')
if t is None:continue
old=t.text or '';original[meta(e,'Guid').text]=old
if name=='resetStats':
desc=('Reset IEC send/reply/window counters and call each registered stats.resetStats. Does not clear native errno/reconnect counters or all per-request history. No transport recreation.' if parent=='ClientBase' else 'Reset only doneCnt, doneCntRst, errorCnt and errorCntRst. Retains durations, offlineCnt, timeoutCnt and timestamps.')
elif name=='processRequest':desc=('Derived request dispatch: performs '+parent+' transaction using shared nativeProcess, validates copy bounds and transfers the configured slice. Called by client scheduler; application calls the request FB body to register/bind its data.' if parent!='RequestBase' else 'Abstract internal request dispatch implemented by each concrete read/write block.')
elif name in ['Get','Set']:desc=f'{name} accessor for {parent}; see the owning property reference for side effects. Runtime/property syntax normally invokes this accessor.'
else:desc=summary.get(name)
assert desc,(parent,name)
header=f'(* atMb API reference: {parent+"." if parent else ""}{name}\n{desc}\n*)\n'
# Add comments only to declaration lines, never change code or types.
lines=[];scope=''
for line in old.splitlines(keepends=True):
sm=re.match(r'\s*(VAR(?:_INPUT|_OUTPUT|_IN_OUT|_TEMP)?)(?:\s+CONSTANT)?\s*$',line)
if sm:scope=sm.group(1)
if re.match(r'\s*END_VAR',line):scope=''
m=re.match(r'\s*(\w+)\s*:',line)
if m and m.group(1) in fields:
lines.append(' // '+fields[m.group(1)]+'\n')
lines.append(line)
t.text=header+''.join(lines);count+=1
sections.append('## '+(parent+'.' if parent else '')+name+'\n\n'+desc+'\n\n```iecst\n'+old.strip()+'\n```\n\n')
# Parameter table scoped to input/output/open-array sections.
rows=[];scope=''
for line in old.splitlines():
sm=re.match(r'\s*(VAR(?:_INPUT|_OUTPUT|_IN_OUT)?)(?:\s+CONSTANT)?\s*$',line)
if sm:scope=sm.group(1)
if re.match(r'\s*END_VAR',line):scope=''
m=re.match(r'\s*(\w+)\s*:\s*(.*?);',line)
if m and scope in ['VAR_INPUT','VAR_OUTPUT','VAR_IN_OUT']:
fn=m.group(1); explanation=fields.get(fn,'Internal wrapper state exposed for compatibility; normally leave under library control.')
rows.append('| `'+fn+'` | '+scope+' | `'+m.group(2).replace('|','\\|')+'` | '+explanation+' |')
if rows:sections.append('| Member | Direction | Declaration | Meaning |\n|---|---|---|---|\n'+'\n'.join(rows)+'\n\n')
# Verify executable tokens and all non-Interface XML content unchanged.
def without_comments(s):return re.sub(r'\s+','',re.sub(r'//[^\n]*','',re.sub(r'\(\*.*?\*\)','',s,flags=re.S)))
for e in entries:
guid=meta(e,'Guid').text
if guid in original:assert without_comments(original[guid])==without_comments(blob(e,'Interface').text),path(e)
output=B/'atMb-Documented.export';E.ElementTree(r).write(output,encoding='utf-8',xml_declaration=True)
check=E.parse(output).getroot();orig=E.parse(src).getroot()
for root in [check,orig]:
for e in root.find('.//List2[@Name="EntryList"]'):
t=blob(e,'Interface')
if t is not None:t.text='DOCUMENTATION_ONLY'
assert E.tostring(check)==E.tostring(orig),'Non-documentation content changed'
(B/'API-REFERENCE.md').write_text('# atMb API reference\n\nGenerated from your working `CmpAtomModbus(1).export`. Documents every code object, including internal methods and property accessors. Runtime implementations, identifiers, declarations and embedded native payloads are preserved in the documented export; additions are comments only.\n\n'+''.join(sections))
(B/'VALIDATION.md').write_text(f'# Documentation validation\n\nDocumented {count} code objects.\n\n- All interface executable tokens unchanged after removing comments/whitespace.\n- All XML content outside Interface text identical after parse/serialization.\n- Implementations, GUIDs, native payloads, project information and references unchanged.\n- Documented XML parses successfully.\n- No CODESYS IDE compile performed; this is a comments-only change.\n\nInput SHA256: `{hashlib.sha256(src.read_bytes()).hexdigest()}`\n')
print('Documented',count,'objects; comment-only and native-payload preservation checks passed')