diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8761e98 --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82d6b34 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/README.md b/README.md index 1d2fce1..a3ee7b3 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,37 @@ -# atMb — documented working library +# Atom Modbus Library (`atMb`) -Start with `library/atMb-Documented.export`: comments-only documentation of your -working CmpAtomModbus export, retaining its embedded C library object. It uses -`ClientTCP`, matching the uploaded working interface. +CODESYS library with a native C engine for Modbus TCP client, RTU client and TCP +slave operation. Public namespace: `atMb`. No ModbusFB dependency is required. -Read `docs/USER-GUIDE.md` for setup and examples and `docs/API-REFERENCE.md` for -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. +## Use the library -The earlier development export and generator are retained for source history. -`library/build_native_export.py` generates the earlier migration export; it does -not generate or supersede the newly documented working export. Do not overwrite -working IDE changes by regenerating that migration export. The documentation -adds no C changes; the source-to-uploaded-binary match remains unverified. +Open `library/CmpAtomModbus.library` in CODESYS, or install it using Library +Repository and reference it from your application. Keep its project title and +external C interface settings intact. The development target is Raspberry Pi +64 SL (ARM64), using CODESYS 3.5 SP22; runtime/target compatibility must be checked +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`. -C implementation supports TCP client, RTU client and TCP slave; IEC code has -no ModbusFB dependency. Online-change support remains future work. +## Build on the Pi -## Files - -- `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 +Run from the repository root: ```sh 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 ``` -Build natively for each Linux target architecture; the uploaded ARM64 `.so` -cannot run on ARM32 or x86-64. Runtime/SDK compatibility must also be checked -for each CODESYS target. The host tests exercise protocol code independently -of the CODESYS runtime. +Native output: `component-source/component/out/libCmpAtomModbus.so`. +See the build guide before updating the embedded component. An ARM64 binary +cannot run on ARM32 or x86-64; build for each architecture and verify the SDK/runtime +combination. The supplied binary has not been proven byte-equivalent to this source. -## IEC export +## Update and review -```sh -python3 library/build_native_export.py -``` +Edit the IEC library in CODESYS. After significant IEC changes, export the current +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, -namespace `atMb`, and embed the newly built `.so` in a C Implemented Library -object. See the full instructions in `docs/BUILD-AND-USAGE.md`. -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. +This ZIP retains the existing Git history and remote metadata. Cleanup changes +are uncommitted and unstaged. Review locally with `git status` and `git diff`, +then stage/commit when ready. No push is performed by this package. -Your uploaded `.so` is ARM64 and contains RTU/server symbols. Its exact match -to this source snapshot and its operation in the runtime have not been verified; -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. +Online-change qualification remains future work. Keep all native calls owned by +one IEC task. No new license is assigned to the supplied SDK/source files. diff --git a/SHA256SUMS.txt b/SHA256SUMS.txt index 977275c..c99521e 100644 --- a/SHA256SUMS.txt +++ b/SHA256SUMS.txt @@ -1,5 +1,5 @@ d1192772047955638e8bb171080ed9b37e10683de935e4e0503c93b6d26f03fc .gitattributes -675d431f63e2c212360b692b18de50a344aed5664d17b3683f0b687b96479f24 .gitignore +02f0eff1a80c9a8f2fe037d1b5980eb391b65e4dc07dba6fe5d109b08c180c1a .gitignore 0ac6e4fe82a82d1bd27ff20dec2f18820c0e0a5057cd0cb1a8020f6dc506603f ExtensionSDK/README 1cc355e875e572de3829d28bff9c100a4eecbfd741d938654be97881023c84d5 ExtensionSDK/include/CmpErrors.h 2010310fbeb8b4734e638610d2f6dc01b600fe42b8b104155806ef0784e95f2d ExtensionSDK/include/CmpItf.h @@ -10,16 +10,13 @@ a8de018bfde8d10d3f281bb45de03c4b4f3cd28466eb0fa0981086bc4033a69c ExtensionSDK/i 814d06ca83d775f3bcb93f80859602b6c1debe9511b3d42ef337948bd2e2b5d0 ExtensionSDK/makefile 4fc8013238169e924ee2eb5dd6c2b39c9d274f4c7c2219dc0b9a530687097519 ExtensionSDK/makefile_template aa7ccad11508ccbf4f9b24aa8db8f22392f9d723ae3af5cbfa67e38271cfbac3 ExtensionSDK/src/CmpFrame.c -67076180ec906d9f27f5b63406d8a2701faa58771f76e0f4487ae750660ce7e4 README.md -f0279c023b82eeda388cdc1b9c9f358b7794869360a06684df670cd34d5cab56 artifacts/arm64/CmpAtomModbus_library.precompilecache +11aee0c6ff01eaff1aac5aa46c470ea8367d5eae655068320869a1bcdce1ef53 README.md 07527262a8efcf9add5554cfcaed22cf927af63b985e431ab5341078a6864667 artifacts/arm64/libCmpAtomModbus.so 14db5bb96e65494eed63da0139b37734dfc08e312a3cb0924ad10b7d9476d442 component-source/Makefile d91cffa9751bca79f13ae80552d654519333536c894bb13b41a1182c179c53b7 component-source/component/CmpAtomModbus.c 30a8d7d5dfd372d171d4acdafd0b77cedca684e0b6e661ae1ab67e2627d35708 component-source/component/CmpAtomModbusItf.m4 513586eed183bcc53cdf008c483da03feff6b5cb0f3ed3eff17de1462def12c3 component-source/component/CmpFrame.c 0972948a5cc88892421433fb01875f4f51609cda3b0df57ff58f05c61390e1c2 component-source/component/makefile -c362c04636cb2cfc00eec8800f0aa9631709cd9eba76b66332f61a988823a055 component-source/generated/CmpAtomModbus.c -fa9b00a24ff9f24b63c9a08b8eba4d585a23f4fd6e19f990ec36dcc70f745427 component-source/generated/CmpAtomModbusItf.m4 78240595fa115d3706cbc7316bdcd4e0101a3269e5faacce98826bc32143e9d9 component-source/src/atom_bridge.c 4a3eaca066d0c06ac7fa95d802ff78b270a1ccdc3fc67c6a072b7e09efba08c0 component-source/src/atom_bridge.h 4f772a0dab94053bfa2f073f6877ec63a8c6b7999d8e3415160705db441f6068 component-source/src/atom_mb.c @@ -33,16 +30,9 @@ d770393632f9d3d1acddf229f562148648792398a39d90851aa529d2923f8382 component-sour 3af1a5f71d69bae2abda3de9dba31740ad254d7157d702847fb921fc2b64df3d component-source/tests/test_bridge.c 2a4b582bed0404f865f4fa2c905dd251c3dccfc4961f20467723c7349793a7f2 component-source/tests/test_native.py 0cf8f8fefda6376cacf0c56729d24aaa76241e807b0dee0b36001b3ad3cea509 component-source/tests/test_tcp.py -c1f09f7498453182f7ed88c1cb343b637ca6c6507e728ac71591a826588c0d3a docs/API-REFERENCE.md -a3cd41084413b1f7c2045e6a7a647b793c8df1c9fa338e146439031b269a1dcb docs/BUILD-AND-USAGE.md -17d84b44d288b46012d9e52d7c8a247592ad28f3d5c3032fffbab701a8478418 docs/DOCUMENTATION-VALIDATION.md -3a2d04af7a1fc7f2e39fea1401d12653fda16864cf4cb7d8edfe2d9988091453 docs/USER-GUIDE.md -fe7df15839ff4bcf3272cbec71cd67a026e514c79df4bd0be829c8a373391f4d docs/VALIDATION.md -8aa5a11a22263ea04902ce883b14fd31c3de270c711deca13f57889a05b689a0 docs/document_export.py -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 +ca8eca5c351bcc426676213c162606d1be9ef6a07d017dad74aa10c198e4ea91 docs/API-REFERENCE.md +43eb385327aa81deb28a55b08068a9b6e565ad290257d2cbd5819d1ab7558ae3 docs/BUILD-AND-USAGE.md +a0931348b32a336798b7d93a5956fd14bd481597626bc1d40b1597ee6ed5e6a3 docs/CLEANUP.md +ef14901695852012e92ae7dde304244e441c17f2bc17abd4fb7e7a2e30f2e9df docs/USER-GUIDE.md +b35e03e6cd3e23719794802a65663ddc22986731dd52f6713ab71b94d1798f53 docs/VALIDATION.md +be0edea894f02a6f525307da69fd52498d4ff76e4b95728608ff6db8f6877e39 library/CmpAtomModbus.library diff --git a/artifacts/arm64/CmpAtomModbus_library.precompilecache b/artifacts/arm64/CmpAtomModbus_library.precompilecache deleted file mode 100644 index 06722cd..0000000 Binary files a/artifacts/arm64/CmpAtomModbus_library.precompilecache and /dev/null differ diff --git a/component-source/generated/CmpAtomModbus.c b/component-source/generated/CmpAtomModbus.c deleted file mode 100644 index be3373b..0000000 --- a/component-source/generated/CmpAtomModbus.c +++ /dev/null @@ -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) -{ - -} - diff --git a/component-source/generated/CmpAtomModbusItf.m4 b/component-source/generated/CmpAtomModbusItf.m4 deleted file mode 100644 index 0f01230..0000000 --- a/component-source/generated/CmpAtomModbusItf.m4 +++ /dev/null @@ -1,70 +0,0 @@ -/** - * CmpAtomModbus - * - * - * - */ - -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 - -/** - * - * 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. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * -*/ -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 **/ - diff --git a/docs/API-REFERENCE.md b/docs/API-REFERENCE.md index 34ee4c8..8599947 100644 --- a/docs/API-REFERENCE.md +++ b/docs/API-REFERENCE.md @@ -1,6 +1,6 @@ # 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 diff --git a/docs/BUILD-AND-USAGE.md b/docs/BUILD-AND-USAGE.md index 43929eb..21009e0 100644 --- a/docs/BUILD-AND-USAGE.md +++ b/docs/BUILD-AND-USAGE.md @@ -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/ -serial/frame processing runs in the native component. Public namespace: atMb. -Standard and SysTime are still required for IEC timers and time measurement. +## Build native C -This is a tested host development build, not a compiled CODESYS library or a -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. +From the repository root on the target Linux machine: ```sh -cd component-source -make -C component SDKDIR="$(realpath ../ExtensionSDK)" clean -make -C component SDKDIR="$(realpath ../ExtensionSDK)" all -file component/out/libCmpAtomModbus.so -make test +sudo apt install build-essential m4 dos2unix python3 +make -C component-source/component SDKDIR="$(realpath ExtensionSDK)" clean +make -C component-source/component SDKDIR="$(realpath ExtensionSDK)" all +file component-source/component/out/libCmpAtomModbus.so +make -C component-source test ``` -The component registration version is now 0.3.0.0. The external atMbCall_cext -function ABI, signature 0x709EE65B and generated API version 0.2.0.0 are unchanged. -No regeneration of runtime interface files is required for these extra commands. -Keep the supplied generated interface; do not regenerate blindly from a changed -library title/version. No prebuilt Pi binary is included. A strict host compile -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. +`component-source/component/` is the active SDK build directory. Its +`CmpAtomModbus.c` includes the protocol source files as one translation unit. +The removed `generated/` directory contained original unimplemented stubs; +it was not used by this makefile. Preserve the active `CmpAtomModbusItf.m4` and +`CmpFrame.c`; they define ABI registration and cleanup. -## 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 - 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. +## Update the CODESYS library -For automatic deployment, stop using the old manually preloaded component -before testing the embedded one: remove only its ComponentManager entry, then -restart the runtime during maintenance. Alternatively, for a manual bench test, -stop the runtime and replace /usr/lib/libCmpAtomModbus.so with the new binary. -Inspect /var/log/codesyscontrol.log. No production install is performed by this kit. +1. Back up `library/CmpAtomModbus.library` and open it in CODESYS. +2. Retain Project Information title `CmpAtomModbus` and namespace `atMb`. +3. Retain the Standard and SysTime references required by the IEC code. +4. In its C Implemented Library object, update the native binary for the exact + 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/ -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. +## Runtime deployment -## 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 -VAR - bus : atMb.ClientRTU; - read : atMb.ReqReadHolding; - registers : ARRAY[0..9] OF UINT; -END_VAR -``` +For a manual installation, stop the runtime before replacing its component and +restart afterwards. Check `/var/log/codesyscontrol.log` for the loaded component +and version; `/var/opt/codesys/codesyscontrol.log` was stale on the development Pi. +No deployment or runtime restart is performed by this cleanup. -```iecst -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); -``` +## Different Linux targets -Call client/requests every scan in ONE task. One RTU client owns each serial bus; -register different unit IDs on that same client, rather than opening multiple -clients on the same tty. The native open uses exclusive serial access. +Build natively on the desired CPU or use a matching cross-toolchain. ARM64, +ARM32 and x86-64 outputs are distinct. SDK, CODESYS runtime interface and libc +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 -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. +## Further documentation -Supported baud rates: 1200,2400,4800,9600,19200,38400,57600,115200. -RTU uses 8 data bits; parity 0 none/1 odd/2 even; stop bits 1 or 2. -Unit IDs 1..247 are accepted. Broadcast unit 0 is explicitly rejected because -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. +- [User guide](USER-GUIDE.md): examples, polling, RTU, slave, timeout and recovery. +- [API reference](API-REFERENCE.md): all objects from the last supplied working export. +- [Validation](VALIDATION.md): exact scope of checks performed. diff --git a/docs/CLEANUP.md b/docs/CLEANUP.md new file mode 100644 index 0000000..9d2a035 --- /dev/null +++ b/docs/CLEANUP.md @@ -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. diff --git a/docs/DOCUMENTATION-VALIDATION.md b/docs/DOCUMENTATION-VALIDATION.md deleted file mode 100644 index 8a636a7..0000000 --- a/docs/DOCUMENTATION-VALIDATION.md +++ /dev/null @@ -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` diff --git a/docs/USER-GUIDE.md b/docs/USER-GUIDE.md index 806fdb0..c0eb333 100644 --- a/docs/USER-GUIDE.md +++ b/docs/USER-GUIDE.md @@ -1,6 +1,10 @@ # 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 @@ -211,6 +215,6 @@ States: 0 disabled, 1 retry wait, 2 connecting, 3 ready, 4 sending, 5 receiving. ## 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. diff --git a/docs/VALIDATION.md b/docs/VALIDATION.md index 322a86b..1c1476d 100644 --- a/docs/VALIDATION.md +++ b/docs/VALIDATION.md @@ -1,14 +1,14 @@ -# Snapshot validation +# Cleanup validation -- Export generator completed: 99 objects, no ModbusFB references. -- Public diagnostics and balanced VAR declaration checks passed. -- Strict host C compilation passed. -- Bridge pool/stale handle/configuration/shutdown tests passed. -- Nine TCP scenarios passed directly and through the native bridge. -- RTU pseudo-terminal tests passed, including CRC/timeout recovery, partial frames, - writes, exceptions and invalid write echo recovery. -- TCP slave FC 1/2/3/4/5/6/15/16, exception, nonzero base, listener recovery - and stale handle tests passed. +- Working `.library`, standalone ARM64 `.so`, active C/component sources, tests, + SDK and all Git metadata compared byte-for-byte with the uploaded ZIP: unchanged. +- Removed precompile caches, unused generated stubs and broken generator script. +- README/build instructions no longer refer to absent migration exports/scripts. +- Strict host compilation and bridge tests passed after cleanup. +- Nine direct TCP and nine bridge TCP scenarios passed. +- RTU frame/write/error/recovery tests passed. +- TCP slave FC1/2/3/4/5/6/15/16, exception/address and recovery tests passed. -No CODESYS IDE compile, full ARM64 SDK build, physical serial hardware test, -or online-change qualification was performed for this repository package. +No CODESYS compile or full ARM64 SDK build was run. Physical RTU operation and +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`. diff --git a/docs/document_export.py b/docs/document_export.py deleted file mode 100644 index 92b6be8..0000000 --- a/docs/document_export.py +++ /dev/null @@ -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:/Er: 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')