commit 07244fc761240e9d127eb2be33c4786dc3862044 Author: Adam Date: Wed Sep 30 21:17:41 2026 -0600 first commit diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a1a1d82 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changes + +## Development bundle 0.5 (component/ABI 0.2.0.0) +- Latest compatibility export includes empty registry, bounds and null dispatch checks. +- Related registry loop subtraction uses signed counts. +- Shared mutable scheduler fields use CODESYS input fields; offline method protected. +- C TCP core, status bridge and per-request timeout command 11 included. +- Supplied SDK, original generated C/M4, original export and tests preserved. +- Linux build script, packaging instructions and build metadata included. + +Earlier exports are retained for comparison; use AtomModbus-Compatible.export. diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md new file mode 100644 index 0000000..39c91a6 --- /dev/null +++ b/COMPATIBILITY.md @@ -0,0 +1,15 @@ +# Compatibility record + +| Item | Known evidence | +|---|---| +| IDE | User reports CODESYS 3.5 SP22 | +| Pi | aarch64, 64-bit Linux | +| Installed packages | codesyscontrol 4.16.0.0; codesysedge 4.16.0.0 arm64 | +| Component | User-built ARM64 component 0.2.0.0 loaded successfully | +| Latest wrapper | Dispatch guards added; target verification outstanding | +| Native C tests | Passed on development Linux host | +| Other architectures | Build source provided; not validated | +| Embedded deployment | Official procedure documented; not validated here | + +A previous /var/opt/codesys log was stale and is not a current version record. +Record actual OS/libc, runtime, SDK hashes and test results for each release. diff --git a/LICENSE-NOTES.md b/LICENSE-NOTES.md new file mode 100644 index 0000000..94c46f5 --- /dev/null +++ b/LICENSE-NOTES.md @@ -0,0 +1,7 @@ +# Ownership and third-party licensing + +No license is assigned to Atom's source or original exports by this bundle. +Choose your desired distribution terms before publishing. +The supplied ExtensionSDK remains subject to its CODESYS copyright and applicable +license terms. Preserve notices; verify redistribution rights before distributing +the SDK outside your organization. This file is not a license grant. diff --git a/MANIFEST.sha256 b/MANIFEST.sha256 new file mode 100644 index 0000000..4aea9eb --- /dev/null +++ b/MANIFEST.sha256 @@ -0,0 +1,55 @@ +badefa63129a93dfe5aa2fc25e8ff72fb256655d44141199e68a9b6db40a8a98 .gitignore +0bf50be5921642c5fded901891709b227c9f53e5f598023379b2fb0e7d3ec123 CHANGELOG.md +3325c8766682bccf1de680f5cd75c8e64670ed12f15ea2388f4926da567caaf9 COMPATIBILITY.md +7e78eb0048fb62a249151dac1d8d0cc37df5eba5f020f844ee2c9465c88764a8 LICENSE-NOTES.md +0b4a11e0fa640bc39f8352b45110ceaba951033dbdd8e53438d770081e62cc58 Makefile +293b1752acad04ae5e24b8a5e646ccf9f2e26c89b74cee8a46f163cb1e122051 README.md +0f705c0e574b205918d14b0346a4b1f359f92606c18fd3b3c4bd16d93bbf2fa7 build/README.md +650346532973f9a6b526d318a5de513e1c8c099c00b41238e0ce8cb5de6e0ffd codesys/BUILD_ON_PI.md +b39ded5506bfdcb1aea7c8c97161cb8ea23d10a1f734df82bcc4fb96f7d87319 codesys/INTEGRATION.md +098db0f8c4609bf3f09b44a0012459f0ff7933938c5c9b79a5e289a5cedc8377 codesys/PI_SETUP.md +f426ebafcdd8e19b24a90354049b6b8559d9be597b2f9cac9f7649b2cd022c8d codesys/PLC_PRG_declaration.st +661c39cf7c5cb1900394753a91f8e53f806cf7e1ed5244a1e3562fa14a70bfce codesys/PLC_PRG_implementation.st +c334109ec949d2342bdee60cc18c025377a0d32397a96dbc2b1ccd9a29f7b8a7 codesys/PROJECTS-TO-ADD.md +d46196b1e67d8318bee443afa7ea7f4820680775219c519e6d3600e78e70cf12 codesys/STATUS.md +69570458de6762e5b63908adec323619ea7ccfea2a33b45613ead860c06fc171 codesys/atMbCall_cext.st +19fb0ff04eb6742f5e21cbc2956fd1adce9601422846e2bed19704ffd090f79c compat/All_Source_Review.st +4cb458ed8376857dfe644c506046183c8d6ebaac8ee23414a54cdbf2e576917d compat/AtomModbus-Compatible-DispatchFix.export +c16529ebd161704feb6806a0262fdbbc0d2b2da6a1aaaf2fc87ec555460f40fd compat/AtomModbus-Compatible-Fixed.export +4cb458ed8376857dfe644c506046183c8d6ebaac8ee23414a54cdbf2e576917d compat/AtomModbus-Compatible.export +8f5e02ffbc24f38f28b4f4a5bb6b6cee6289bff03aecb0fe7ed76749f0ed93cf compat/Original_HomeCodesys.export +be920ad07708256ff86c7651e8558899c65e9ff2c4378a8fca7d3e4f56c17952 compat/README.md +7eb25c89d948a565a7a5d6941df07945f7d70239d64f5a4e3f2f76692b11b1a8 component/CmpAtomModbus.c +30a8d7d5dfd372d171d4acdafd0b77cedca684e0b6e661ae1ab67e2627d35708 component/CmpAtomModbusItf.m4 +513586eed183bcc53cdf008c483da03feff6b5cb0f3ed3eff17de1462def12c3 component/CmpFrame.c +51ac804780cf85318d8d75b42d97838afcc24428f6ddcc9e68f554aca139d2a5 component/makefile +941271e770b5dfb4367b752aef1af6b6d7a7b7245ea82627c1151a8090c7099c dist/README.md +d46196b1e67d8318bee443afa7ea7f4820680775219c519e6d3600e78e70cf12 docs/API.md +1edd488b4480ebe7d320f4c09b945aff55ce58897783b31efd891635158c644a docs/BUILD.md +5b7ffb0075cd4eb663b9d30414dd92ed41b3da28e472a053930804227ddd5ccc docs/PACKAGING.md +aaa2d2335812fc15897b7d2ed861f87a75c0fd8420b89681aedc131310eaf3fd docs/TESTING.md +5630f6f9eca82ff55e3b9515f22b6a9a00d3df616fc0594d331892df01d09e27 docs/TROUBLESHOOTING.md +c362c04636cb2cfc00eec8800f0aa9631709cd9eba76b66332f61a988823a055 generated/CmpAtomModbus.c +fa9b00a24ff9f24b63c9a08b8eba4d585a23f4fd6e19f990ec36dcc70f745427 generated/CmpAtomModbusItf.m4 +0ac6e4fe82a82d1bd27ff20dec2f18820c0e0a5057cd0cb1a8020f6dc506603f sdk/ExtensionSDK/README +1cc355e875e572de3829d28bff9c100a4eecbfd741d938654be97881023c84d5 sdk/ExtensionSDK/include/CmpErrors.h +2010310fbeb8b4734e638610d2f6dc01b600fe42b8b104155806ef0784e95f2d sdk/ExtensionSDK/include/CmpItf.h +a8de018bfde8d10d3f281bb45de03c4b4f3cd28466eb0fa0981086bc4033a69c sdk/ExtensionSDK/include/CmpStd.h +212fcd438a0ad38712d575521c34ed875ac86353e543c737aec3770386503b14 sdk/ExtensionSDK/m4/Exports.m4 +06b55c74620a6247bf4ddcf69f7373255f4677b2c2b7ef1ed0ac02ba0407f94e sdk/ExtensionSDK/m4/Implementation.m4 +8fb9f4e77e2f2c4df494b6199bfd16ab2ed4257d76c735e46460f837d6e22163 sdk/ExtensionSDK/m4/Util.m4 +814d06ca83d775f3bcb93f80859602b6c1debe9511b3d42ef337948bd2e2b5d0 sdk/ExtensionSDK/makefile +4fc8013238169e924ee2eb5dd6c2b39c9d274f4c7c2219dc0b9a530687097519 sdk/ExtensionSDK/makefile_template +aa7ccad11508ccbf4f9b24aa8db8f22392f9d723ae3af5cbfa67e38271cfbac3 sdk/ExtensionSDK/src/CmpFrame.c +151c7eed44289b30dc60121937846172e54abda6af19c643cdbe5afc887904f4 sdk/ExtensionSDK-original.zip +4cb1f22eb90c8ed8485132b5273f8e189ed142a66d03e73b5b296d6a3f9fdebf src/atom_bridge.c +a65faab8dd4ca545df89d0a756637c8630d67a3a5ed3bc75a0de3b3b8fb08a43 src/atom_bridge.h +b90ed7d3a8c2b5b38654b68d07e3a5f0b02ed39f0cef2b45068d8c1f654b4e39 src/atom_mb.c +db2e5789ececf935a2a8968982792efaafebc8ab4bb53b6f9374138e528c2cec src/atom_mb.h +d770393632f9d3d1acddf229f562148648792398a39d90851aa529d2923f8382 tests/bridge_driver.c +50bb7854c3a966c46a583fba951c1d3a605bf9d1739ac83685322d9a7b740c4d tests/driver.c +3af1a5f71d69bae2abda3de9dba31740ad254d7157d702847fb921fc2b64df3d tests/test_bridge.c +0cf8f8fefda6376cacf0c56729d24aaa76241e807b0dee0b36001b3ad3cea509 tests/test_tcp.py +dac1df481f09c954e0ff7af793238f54d11156e8b5060605979d300dde47dcf4 tools/build-linux.sh +a38bcadbe1ca1cd047ce2c54cc8e52428196f6552e4b20c62eba90c2bc823309 tools/build_compat_export.py +691eb3acc3dd58d0114ae2f0cb9de611be50d364d312ab5383a797d3ecadbeab tools/prepare_component.py diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..718e302 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +CC ?= cc +CFLAGS = -std=c11 -Wall -Wextra -Werror -O2 +.PHONY: test clean +test: tests/driver tests/test_bridge tests/bridge_driver + ./tests/test_bridge + python3 tests/test_tcp.py + python3 tests/test_tcp.py bridge_driver +tests/bridge_driver: tests/bridge_driver.c src/atom_bridge.c src/atom_bridge.h src/atom_mb.c src/atom_mb.h + $(CC) $(CFLAGS) -Isrc tests/bridge_driver.c src/atom_bridge.c src/atom_mb.c -o $@ +tests/test_bridge: tests/test_bridge.c src/atom_bridge.c src/atom_bridge.h src/atom_mb.c src/atom_mb.h + $(CC) $(CFLAGS) -Isrc tests/test_bridge.c src/atom_bridge.c src/atom_mb.c -o $@ +tests/driver: tests/driver.c src/atom_mb.c src/atom_mb.h + $(CC) $(CFLAGS) -Isrc tests/driver.c src/atom_mb.c -o $@ +clean: + rm -f tests/driver tests/test_bridge tests/bridge_driver diff --git a/README.md b/README.md new file mode 100644 index 0000000..ab6e336 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# Atom Modbus Library development kit + +Start here. This folder contains the C TCP engine, CODESYS runtime component, +original generated ABI files, compatibility export, supplied ExtensionSDK, +regression tests and build documentation. + +## Build on the Pi + +Install build-essential, make, m4, sed, dos2unix and python3. In this folder run: + +```sh +./tools/build-linux.sh linux-aarch64 +``` + +The output and build metadata are placed in dist/0.2.0/linux-aarch64/. +To run the native socket regression tests: `make test`. + +## CODESYS + +Import compat/AtomModbus-Compatible.export into a copy of your existing Atom +library. This is the latest dispatch-fixed export. Read compat/README.md for +interfaces, dependencies and ownership rules. Keep CmpAtomModbus as the external +function library and add it with namespace CmpAtomModbus. + +No editable .library projects, compiled libraries or ARM64 .so were uploaded +in this session, so none are included. Save your actual CmpAtomModbus.library +and Atom library projects in codesys/ when you have them. Exports are backups, +not substitutes for the complete library projects and their metadata. + +Read docs/BUILD.md, docs/PACKAGING.md and docs/TROUBLESHOOTING.md. +This bundle is development revision 0.5; ABI/component version remains 0.2.0.0. +The CODESYS changes still require compilation and target testing. diff --git a/build/README.md b/build/README.md new file mode 100644 index 0000000..1269d03 --- /dev/null +++ b/build/README.md @@ -0,0 +1 @@ +Temporary build workspace. Do not store authoritative source here. diff --git a/codesys/BUILD_ON_PI.md b/codesys/BUILD_ON_PI.md new file mode 100644 index 0000000..8cf8320 --- /dev/null +++ b/codesys/BUILD_ON_PI.md @@ -0,0 +1,87 @@ +# Ready-to-build component, generated interface validated + +The supplied CmpAtomModbus.c and CmpAtomModbusItf.m4 were inspected and the C +stub implemented. The M4 file is unchanged. External registration metadata: +signature 0x709EE65B, version 0x00020000 (0.2.0.0). + +This source bundle is revision 0.3; the IEC library/component version remains +0.2.0.0 to match your generated interface. Do not change it independently. + +## Build + +Copy/extract this source folder and your ExtensionSDK onto the Pi: + +```text +~/modbus-build/atom-modbus/ +~/modbus-build/ExtensionSDK/ +``` + +Then: + +```sh +sudo apt-get install build-essential m4 sed dos2unix make +cd ~/modbus-build/atom-modbus +make -C component SDKDIR="$(realpath sdk/ExtensionSDK)" all +file component/out/libCmpAtomModbus.so +nm -D component/out/libCmpAtomModbus.so | sed -n '/ComponentEntry/p; /atmbcall_cext/p' +``` + +Expected: AArch64 shared object; ComponentEntry and atmbcall_cext exported. +You do NOT need to run the preparation script: component/ is already prepared. +The original generated files are retained in generated/ for reference. + +## Install during a bench-test maintenance window + +Stop your PLC application safely first. Keep a backup of the configuration. + +```sh +sudo cp /etc/CODESYSControl_User.cfg /etc/CODESYSControl_User.cfg.before-atom-modbus +sudo /etc/init.d/codesyscontrol stop +sudo cp component/out/libCmpAtomModbus.so /usr/lib/libCmpAtomModbus.so +sudo nano /etc/CODESYSControl_User.cfg +``` + +Under the existing [ComponentManager] section add: + +```ini +Component.N=CmpAtomModbus +``` + +Replace N with an unused numeric component index. Do not add a duplicate section +or overwrite another component. Development component ID is 0xffff2001; ensure +it does not collide with another custom component. + +```sh +sudo /etc/init.d/codesyscontrol start +sudo tail -n 80 /var/log/codesyscontrol.log +``` + +Verify the component loads with no interface/signature/version errors. Save and +install your CmpAtomModbus IEC library in CODESYS' Library Repository, then add +it to a separate Raspberry Pi test project's Library Manager. + +## Read-only PLC test + +Create an ST PROGRAM PLC_PRG. Add the SysTime library. Paste +PLC_PRG_declaration.st in its declaration pane and +PLC_PRG_implementation.st in its implementation pane. Ensure PLC_PRG is called +by one cyclic task, e.g. 10 ms. Namespace-qualify atMbCall_cext if required. + +Set the IP, unit ID and zero-based start address for a device with at least ten +readable holding registers. Download, start and set run=TRUE in the watch window. +Inspect status[1] (state), callResult, registers, readDone, successfulReadCount, +dataStale and requestErrorCount. Unplug and restore Ethernet or restart the +device to exercise recovery. status[11] counts additional connection attempts. + +Set run=FALSE and let the program scan until handle=0 before stopping, +downloading or making online changes. Automatic application lifecycle cleanup +and the original atMb scheduler migration are still pending; this is a bench +program, not yet a production replacement. RTU is not implemented. + +## Verification scope + +Native engine/bridge tests passed locally. The real generated parameter struct +and function implementation were compiled against the supplied SDK headers +on the x86-64 development host. Full SDK/M4 registration build and execution +on the ARM64 Pi remain to be verified. No precompiled Pi binary is included. +The ST example has not been compiled in the CODESYS IDE here. diff --git a/codesys/CmpAtomModbus.library b/codesys/CmpAtomModbus.library new file mode 100644 index 0000000..69b4a1e Binary files /dev/null and b/codesys/CmpAtomModbus.library differ diff --git a/codesys/CmpAtomModbus.~u b/codesys/CmpAtomModbus.~u new file mode 100644 index 0000000..d137df2 --- /dev/null +++ b/codesys/CmpAtomModbus.~u @@ -0,0 +1,4 @@ +atom +ADAM-CODESYS +5068 +639263998361642755 diff --git a/codesys/INTEGRATION.md b/codesys/INTEGRATION.md new file mode 100644 index 0000000..c2b952d --- /dev/null +++ b/codesys/INTEGRATION.md @@ -0,0 +1,88 @@ +# Integration into atomLibrary + +This package contains a working C TCP core and native C bridge, not a compiled +CODESYS library. Target supplied: Raspberry Pi ARM64, runtime 4.16.0.0, +development environment SP22. ExtensionSDK inspected. See PI_SETUP.md for +the original generated-interface step. Real generated C/M4 files are now +supplied and the stub is implemented; use BUILD_ON_PI.md for the prepared build. +Do not map `am_client` directly to an IEC STRUCT: C int, size_t, alignment and +pointer sizes vary by target. Use SDK-generated external function signatures. + +## Implemented bridge API + +The separate operations below are implemented as commands through ONE external +function, `atMbCall_cext`. See STATUS.md for command IDs and the status array. +The table describes operations rather than separate exported function names. + +The bridge uses a fixed pool of contexts and returns generation-checked UDINT handles. +Zero is invalid. Use functions named with `_cext` for the Control SL Extension SDK: + +| Function | Purpose | +|---|---| +| atMbOpen_cext | Allocate a fixed context slot; configure numeric IPv4 and port | +| atMbConfigure_cext | Set connect, response and reconnect times in milliseconds | +| atMbEnable_cext | Enable/disable recovery | +| atMbTick_cext | Advance one nonblocking step using monotonic time | +| atMbSubmit_cext | Validate and copy request/write data into the context | +| atMbGetStatus_cext | Copy explicitly sized status fields to IEC outputs | +| atMbCopyRead_cext | Copy validated read result with destination capacity check | +| atMbReconnect_cext | Cancel active transaction and create a fresh connection | +| atMbClose_cext | Close socket and release pool slot | + +Only pass pointers during a synchronous call. Never store pointers into IEC +application memory in C across scans or online changes. All operations for one +handle must run from one task. Add target-specific lifecycle handling for stop, +reset, download and online change; close or invalidate old handles before reuse. + +## Map existing function blocks + +Keep the existing names and data interfaces, with these internal changes: + +- `atMbClientTCP`: own the C handle and call Tick once per scan. Keep + `in_ipAddress`, `in_port`, request registration and priority scheduling. + Changing the endpoint cancels the transaction and opens the new endpoint. +- `atMbClient`: schedule only while Ready and no request is pending. Complete + the prior request and copy its read data BEFORE submitting the next request. + Preserve priority ageing/minimum delay, adding explicit enabled eligibility + and starvation checks. Enforce the existing 100-request registration limit. +- `atMbReqBase`: snapshot parameters when submitted; validate offset plus + quantity against destination capacity. Reject overflow rather than silently + truncate. Quantity zero means disabled at the wrapper level. +- `atMbReadCoil` / `atMbReadDiscrete`: FC01 / FC02; convert unpacked C uint16_t + values to the actual IEC BOOL array type when copying. +- `atMbReadHolding` / `atMbReadInput`: FC03 / FC04, WORD values. +- `atMbWriteCoil` / `atMbWriteHolding`: FC15 / FC16; preserve `writeNow` semantics. + Snapshot write values on submit. A failed write is not automatically replayed; + the application must explicitly decide whether to reissue it. +- `atMbRequestStats`: replace `ModbusFB.Error` with your own transport-error enum; + keep Modbus exception separately. Keep counts, intervals and HMI indicators. +- `atMbClientRTU`: retain as a separate future transport; this C core does not + implement serial RTU framing, CRC, timing or serial-port control. + +## Status additions + +Client: state, connection attempt count, reconnect count, last transport error, +OS socket error, active request index and last successful response time. +Request: Busy/Done/Error, result, exception, consecutive successes/failures, +last successful read time, hasValidData, dataAgeMs, stale and writeUncertain. + +Use connection state for transport readiness. Use per-request successful read +timestamps for freshness; another successful request must not refresh stale +data. A valid exception proves the peer responded but does not validate data. +Keep startup/never-read distinct from stale. The C core exposes a CLIENT-wide +last_success timestamp; per-request timestamps belong in the scheduler wrapper. + +## Deployment gate + +1. Confirm PLC model, CODESYS runtime version, operating system and architecture. +2. Confirm vendor support for custom C components; Control SL SDK support must + not be assumed on a vendor-supplied PLC runtime. +3. Generate the external declarations/stubs in the matching SDK, implement the + bridge, compile for the actual target and package its component. +4. Compile the IEC wrapper in CODESYS and exercise stop/start and online change. +5. Bench-test unplug/replug, device restart, long outage, endpoint change, full + request list, priority fairness and uncertain writes before site deployment. + +References: +- https://content.helpme-codesys.com/en/CODESYS%20Control/_rtsl_extension_extension_sdk.html +- https://www.modbus.org/modbus-specifications diff --git a/codesys/PI_SETUP.md b/codesys/PI_SETUP.md new file mode 100644 index 0000000..55cbefe --- /dev/null +++ b/codesys/PI_SETUP.md @@ -0,0 +1,72 @@ +# Raspberry Pi integration — known target + +UPDATE: Real generated C/M4 files have been supplied and component/ is prepared. +Start with BUILD_ON_PI.md; the interface-generation steps below are historical +reference and do not need to be repeated. + +User-reported: aarch64, 64-bit userland, codesyscontrol 4.16.0.0; +development environment CODESYS 3.5 SP22. Supplied ExtensionSDK inspected. +The runtime package reports `all`, so verify the actual component/runtime +binary architecture on the Pi after building. No ARM binary is included here. + +## Generate the official CODESYS interface + +1. Create an **Empty library** named `CmpAtomModbus.library`. In Project + Information set Title = `CmpAtomModbus`, Version = `0.2.0.0`. +2. Add one Function named `atMbCall_cext`, return type UDINT. Paste the matching + declaration from atMbCall_cext.st (the FUNCTION line may already exist in + the declaration editor; do not duplicate it). +3. Right-click the function > Properties > Build > **External implementation**. +4. Build > **Generate Runtime System Files**. Select **M4 interface file** and + **C stub file**. Component name must be `CmpAtomModbus`. +5. Send the generated `CmpAtomModbus.c` and `CmpAtomModbusItf.m4` back for actual + interface validation. The generated signature/checksum must be preserved; + a plain `.so` exported symbol alone is insufficient for the IEC call. + +## Prepare and build on Pi after generating those files + +Keep your ExtensionSDK directory outside this source folder. Install host build +tools on the Pi if missing: + +```sh +sudo apt-get install build-essential m4 sed dos2unix make +``` + +From the atom-modbus source folder, with the generated files in `generated/`: + +```sh +python3 tools/prepare_component.py generated/CmpAtomModbus.c generated/CmpAtomModbusItf.m4 --sdk ../ExtensionSDK +make -C component SDKDIR="$(realpath sdk/ExtensionSDK)" all +file component/out/libCmpAtomModbus.so +``` + +The last output must identify an AArch64 ELF shared object. Native compilation +on the Pi avoids guessing a cross-toolchain or glibc version. The preparation +script validates field names from the real generated M4 struct and preserves +the generated interface. Its parser has only been exercised with a synthetic +fixture until the real generated files are supplied. + +The generated component adds cleanup at `CH_EXIT`, after runtime tasks have +stopped. This does not yet handle application-only lifecycle changes. + +## Install only after final review/bench test + +The SDK's documented method is to place the component in `/usr/lib`, stop the +runtime and add `Component.N=CmpAtomModbus` under `[ComponentManager]` in +`/etc/CODESYSControl_User.cfg`, choosing an UNUSED component index. Restart the +runtime and inspect `/var/log/codesyscontrol.log`. Use a maintenance +window: a runtime restart stops the PLC application. + +Alternatively, package it using the C-Implemented Library object for the exact +Raspberry Pi device target. That packaging still requires CODESYS IDE work. + +## First bench exercise + +Use a separate test application/task. Open -> configure -> enable -> tick every +scan until Ready. Submit an FC03 read. Tick until Done/Error. On Done, CopyRead, +then Acknowledge. On Error, record diagnostics, Acknowledge and allow recovery. +Do not submit writes during initial connection testing. Use a monotonic +millisecond clock (e.g. SysTimeGetUs value divided by 1000). + +Preserve your existing production atomLibrary until the IEC wrapper and +stop/start/download/online-change handling have passed target tests. diff --git a/codesys/PLC_PRG_declaration.st b/codesys/PLC_PRG_declaration.st new file mode 100644 index 0000000..b424426 --- /dev/null +++ b/codesys/PLC_PRG_declaration.st @@ -0,0 +1,30 @@ +(* Bench test: create PROGRAM PLC_PRG, ST language. Add the SysTime library. + Add CmpAtomModbus as a library; qualify atMbCall_cext with its namespace if + the Library Manager requires it. Paste this in the DECLARATION pane. *) +PROGRAM PLC_PRG +VAR + run : BOOL := FALSE; (* Set TRUE in watch window to start. FALSE closes. *) + ipAddress : DWORD := 16#C0A80132; (* EDIT: 192.168.1.50 *) + tcpPort : UINT := 502; + unitId : BYTE := 1; + startAddress : UINT := 0; (* Zero-based wire address, not 40001. *) + registers : ARRAY[0..9] OF WORD; + status : ARRAY[0..23] OF UDINT; + handle : UDINT := 0; (* Do NOT retain/persist this. *) + command : UDINT := 0; + callResult : UDINT; + timeResult : UDINT; + nowUs : SYSTIME; + nowMs : ULINT; + lastReadMs : ULINT; + readDone : BOOL; + requestError : BOOL; + hasValidData : BOOL; + dataStale : BOOL := TRUE; + lastRequestResult : UDINT; + lastException : UDINT; + lastSocketError : UDINT; + requestErrorCount : UDINT; + successfulReadCount : UDINT; + pollDelay : TON; +END_VAR diff --git a/codesys/PLC_PRG_implementation.st b/codesys/PLC_PRG_implementation.st new file mode 100644 index 0000000..68c4269 --- /dev/null +++ b/codesys/PLC_PRG_implementation.st @@ -0,0 +1,93 @@ +(* Paste in IMPLEMENTATION pane. This only issues FC03 reads. + Leave run FALSE until the component is loaded and target is configured. + Set run FALSE and allow scans to execute before stopping/downloading. + This bench program does not implement automatic disposal on online change. *) +readDone := FALSE; +requestError := FALSE; +timeResult := SysTimeGetUs(nowUs); +IF timeResult <> 0 THEN + RETURN; +END_IF; +nowMs := nowUs / 1000; +dataStale := NOT hasValidData; +IF hasValidData THEN + dataStale := (nowMs - lastReadMs) > 2000; +END_IF; + +pollDelay(IN := (command = 5) AND (status[1] = 3) AND (status[2] = 0), + PT := T#200MS); + +IF NOT run THEN + IF handle <> 0 THEN + command := 2; (* Close *) + ELSE + command := 0; + END_IF; +ELSIF (handle = 0) AND (command = 0) THEN + command := 1; (* Open *) +END_IF; + +IF command = 0 THEN + RETURN; +END_IF; + +callResult := atMbCall_cext( + command := command, + handle := handle, + ipAddress := ipAddress, + port := tcpPort, + unitId := unitId, + functionCode := 3, + startAddress := startAddress, + quantity := 10, + pData := ADR(registers[0]), + dataCapacity := 10, + pStatus := ADR(status[0]), + statusCapacity := 24, + nowMs := nowMs, + connectTimeoutMs := 2000, + responseTimeoutMs := 1000, + retryDelayMs := 1000 +); + +IF callResult <> 0 THEN + IF callResult = 256 THEN (* Stale handle: obtain a fresh context. *) + handle := 0; + command := 0; + ELSIF (command = 6) AND ((callResult = 2) OR (callResult = 3)) THEN + command := 5; (* Busy/offline: return to tick. *) + ELSE + (* Preserve callResult for diagnostics. Turning run off closes context. *) + run := FALSE; + END_IF; + RETURN; +END_IF; + +CASE command OF + 1: handle := status[0]; command := 9; (* Configure *) + 9: command := 3; (* Enable *) + 3: command := 5; (* Tick *) + 5: + IF status[3] <> 0 THEN + command := 7; (* Copy validated read result before acknowledge. *) + ELSIF status[4] <> 0 THEN + lastRequestResult := status[5]; + lastException := status[8]; + lastSocketError := status[7]; + requestErrorCount := requestErrorCount + 1; + requestError := TRUE; + command := 10; + ELSIF (status[1] = 3) AND pollDelay.Q THEN + command := 6; (* Submit *) + END_IF; + 6: command := 5; + 7: + lastReadMs := nowMs; + hasValidData := TRUE; + dataStale := FALSE; + readDone := TRUE; + successfulReadCount := successfulReadCount + 1; + command := 10; (* Acknowledge *) + 10: command := 5; + 2: handle := 0; command := 0; hasValidData := FALSE; dataStale := TRUE; +END_CASE; diff --git a/codesys/PROJECTS-TO-ADD.md b/codesys/PROJECTS-TO-ADD.md new file mode 100644 index 0000000..71e8192 --- /dev/null +++ b/codesys/PROJECTS-TO-ADD.md @@ -0,0 +1 @@ +Save your editable CmpAtomModbus.library and Atom library projects here, including project information and references. diff --git a/codesys/STATUS.md b/codesys/STATUS.md new file mode 100644 index 0000000..372dfb3 --- /dev/null +++ b/codesys/STATUS.md @@ -0,0 +1,54 @@ +# Bridge contract + +Return: 0 = accepted, 1..10 = am_error values in atom_mb.h. +256 = invalid/stale handle, 257 = pool full, 258 = insufficient/null buffer, +259 = unknown command. Always inspect return codes. A rejected call is not a +completed Modbus request. + +Commands: 1 Open, 2 Close, 3 Enable, 4 Disable, 5 Tick, 6 Submit, +7 CopyRead, 8 Reconnect, 9 Configure, 10 Acknowledge, +11 Set request timeout (responseTimeoutMs only; permitted while not busy). +Configure only while disabled; all three timeouts must be nonzero. +Open receives an IPv4 DWORD in human octet order: 16#C0A80132 = 192.168.1.50. +Open returns its handle in status[0], NOT in the function return value. +Close returns status[0]=0; old handles fail even when their slot is reused. +Capacity values count WORD/UDINT elements, not bytes. Pass separate buffers. +CopyRead copies `quantity` WORDs; coils/discretes are unpacked WORD 0/1. + +| Status index | Meaning | +|---|---| +| 0 | Handle | +| 1 | State: 0 Disabled, 1 Retry, 2 Connecting, 3 Ready, 4 Sending, 5 Receiving | +| 2 | Busy | +| 3 | Done | +| 4 | Error | +| 5 | Latest transaction result | +| 6 | Last failure, retained after success | +| 7 | OS socket error | +| 8 | Modbus exception | +| 9 | Write outcome uncertain | +| 10 | Connection attempts | +| 11 | Attempts after first attempt | +| 12 | Completed successful requests | +| 13 | Failed requests | +| 14 | Has successful transaction | +| 15 | Last success timestamp, low 32 bits | +| 16 | Last success timestamp, high 32 bits | +| 17 | Quantity of latest request | +| 18 | Function code | +| 19 | Unit ID | +| 20 | Enabled | +| 21 | Connect timeout milliseconds | +| 22 | Whole-request timeout milliseconds | +| 23 | Retry delay milliseconds | + +The PLC wrapper consumes Done/Error then Acknowledge before submitting a new +request. For Done reads, call CopyRead BEFORE Acknowledge. Completed read data +is retained internally but CopyRead requires Done. For writes, do not replay +an uncertain result without an application decision. + +All calls to the entire bridge pool must originate from ONE IEC task. Contexts +and the pool are not thread-safe. Handles must not be RETAIN/PERSISTENT. Closing +on program disposal and invalidating on application reset/download remain PLC +wrapper responsibilities. Component unload cleanup alone does not cover an IEC +application stop or download. diff --git a/codesys/atMbCall_cext.st b/codesys/atMbCall_cext.st new file mode 100644 index 0000000..37caf1c --- /dev/null +++ b/codesys/atMbCall_cext.st @@ -0,0 +1,23 @@ +(* 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. *) +FUNCTION atMbCall_cext : UDINT +VAR_INPUT + command : UDINT; + handle : UDINT; + ipAddress : DWORD; + port : UINT; + unitId : BYTE; + functionCode : BYTE; + startAddress : UINT; + quantity : UINT; + pData : POINTER TO WORD; + dataCapacity : UDINT; + pStatus : POINTER TO UDINT; + statusCapacity : UDINT; + nowMs : ULINT; + connectTimeoutMs : UDINT; + responseTimeoutMs : UDINT; + retryDelayMs : UDINT; +END_VAR diff --git a/compat/All_Source_Review.st b/compat/All_Source_Review.st new file mode 100644 index 0000000..a32f71f --- /dev/null +++ b/compat/All_Source_Review.st @@ -0,0 +1,2296 @@ + +(* ModbusMaster/atMbClientToString -- Interface *) +{attribute 'qualified_only'} +{attribute 'strict'} +TYPE atMbClientToString : +( + noneOk := 0, + someOk := 1, + allOk := 2 +); +END_TYPE + + +(* ModbusMaster/atMBExceptionCodes -- Interface *) +{attribute 'qualified_only'} +// {attribute 'strict'} NOT strict so can set with modbusFB.ExceptionCodes. +TYPE atMBExceptionCodes : +( + // Local / transport-level (non-Modbus) result + TIMEOUT := -1, // Communication timeout / no response + + // Modbus exception codes (returned in exception response PDU) + RESPONSE_SUCCESS := 16#00, // Everything OK (no exception) + + ILLEGAL_FUNCTION := 16#01, // Function code not supported by slave + ILLEGAL_DATA_ADDRESS := 16#02, // Data address not valid for slave (out of range / not mapped) + ILLEGAL_DATA_VALUE := 16#03, // Data value not valid (bad length/value/etc.) + SLAVE_DEVICE_FAILURE := 16#04, // Unrecoverable error while performing the requested action + + ACKNOWLEDGE := 16#05, // Request accepted; processing started (use to avoid master timeout) + SLAVE_DEVICE_BUSY := 16#06, // Slave busy (e.g. long operation in progress); retry later + + MEMORY_PARITY_ERROR := 16#08, // Memory parity error (special case; historically for FC 20/21) + + GATEWAY_PATH_UNAVAILABLE := 16#0A, // Gateway problem: path unavailable / misconfigured / overloaded + GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND := 16#0B // Gateway problem: target device did not respond +); +END_TYPE + + +(* ModbusMaster/atMbClient -- Interface *) +{attribute 'monitoring_display' := 'toString'} +{attribute 'no_assign'} +FUNCTION_BLOCK ABSTRACT atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_timeout: TIME := T#0MS; // If this is set, this will be used on requests, otherwise it will be calculated from in_dataRate. + {attribute 'symbol' := 'none'} + in_resetStats: BOOL; // Set this bool to true to reset statistics. +END_VAR +VAR_OUTPUT + {attribute 'symbol' := 'none'} + msgLastSecondCnt: UDINT; //Messages in the last second + {attribute 'symbol' := 'none'} + msgLastSecondTime: UDINT; //Total amount of time for all messages sent last second in ms + {attribute 'symbol' := 'none'} + numMsgSent: UDINT; + {attribute 'symbol' := 'none'} + numMsgReply: UDINT; + {attribute 'symbol' := 'none'} + allRequestsOnline: BOOL; //All requests are working + //{attribute 'symbol' := 'none'} + reqStats: ARRAY[0..99] OF POINTER TO atMbRequestStats; // For debugging modbus requests +END_VAR +VAR + {attribute 'symbol' := 'none'} + datarate: DINT := 0; //Bits per second data rate. Used to calculate reply timeout. Based on amount of time to transmit holding register read for max amount of registers. Default 154705 for Modbus TCP = 17ms to transmit 2650 bits + {attribute 'symbol' := 'none'} + client: REFERENCE TO ModbusFB.Client; // This is generated internally, no need to set. + {attribute 'symbol' := 'none'} + connectCnt: DINT; //Number of connection attempts + {attribute 'symbol' := 'none'} + initDone: BOOL; + {attribute 'symbol' := 'none'} + disconnectTimer: TON; //Connecting but not connected delay + {attribute 'symbol' := 'none'} + reconnectTimer: TON; //Not connecting and not connected delay + {attribute 'symbol' := 'none'} + msgCountLast: UDINT; //Last message count + {attribute 'symbol' := 'none'} + msgCountSum: UDINT; //Message count accumulator + {attribute 'symbol' := 'none'} + msgTimeSum: UDINT; //Amount of time for messsages accumulator + {attribute 'symbol' := 'none'} + pulse: atPulse; //Pulse generator + {attribute 'symbol' := 'none'} + requests: ARRAY[0..99] OF POINTER TO atMbReqBase; //list of request to direct + {attribute 'symbol' := 'none'} + requestCnt: UINT; //Number of active requests + {attribute 'symbol' := 'none'} + reqDoneSum: UDINT; //Count of done result on all requests + {attribute 'symbol' := 'none'} + reqErrSum: UDINT; //Count of error result on all requests + {attribute 'symbol' := 'none'} + i: DINT; //Loop variable + {attribute 'symbol' := 'none'} + nextIdx: DINT := -1; //Index of the active request + {attribute 'symbol' := 'none'} + priorityLowest: INT; //lowest request priority + {attribute 'symbol' := 'none'} + highestTimeSinceRun: UDINT; //lowest request priority + {attribute 'symbol' := 'none'} + allRequestsOfflineTON: TON; //All requests are offline delay + {attribute 'symbol' := 'none'} + requestNotExecutingTON: TON; //Delay after there are no requests execting + {attribute 'symbol' := 'none'} + recreateClientDly: TON; //Delay to make new client after force fail + {attribute 'symbol' := 'none'} + allRequestsOffline: BOOL; //All requests are offline + {attribute 'symbol' := 'none'} + connect: BOOL; // this is more of a "Request to connect, Requet to disconnect", will drop xConnect once all requests are done, wont start requests unless this is true. + {attribute 'symbol' := 'none'} + forceConnect: TON; //If after 5 seconds, all requests are still ofline, just force a reconnect. + test1: REAL; + test2: REAL; + destroyClient: BOOL; + recreateTest: BOOL; +END_VAR +VAR CONSTANT + {attribute 'symbol' := 'none'} + NEXT_REQUEST_DELAY: TIME := T#0MS; // Dont seem to need a delay + {attribute 'symbol' := 'none'} + RECONNECT_DELAY: TIME := T#500MS; + {attribute 'symbol' := 'none'} + DISCONNECT_DELAY: TIME := T#500MS; + {attribute 'symbol' := 'none'} + RECREATE_CLIENT_DELAY: TIME := T#1000MS; + {attribute 'symbol' := 'none'} + ALL_OFFLINE_DELAY: TIME := T#2S; + {attribute 'symbol' := 'none'} + FORCE_RECONNECT_DELAY: TIME := T#1S; + {attribute 'symbol' := 'none'} + PRIORITY_NONE: INT := 32767; +END_VAR + +VAR +nativeMode : BOOL; +nativeHandle : UDINT; +nativeIP : DWORD; +nativePort : UINT; +nativeNowUs : SYSTIME; +nativeNowMs : ULINT; +nativeStatus : ARRAY[0..23] OF UDINT; +nativeActiveIndex : DINT := -1; +nativeSelected : POINTER TO atMbReqBase; +nativeResultReady : BOOL; +nativeFinishedIndex : DINT := -1; +nativeCallResult : UDINT; +nativeWindowStart : ULINT; +nativePrevConnected : BOOL; +nativeEnable : BOOL := TRUE; +nativeConnectTimeout : UDINT := 2000; +nativeRetryDelay : UDINT := 1000; +nativeHoldUncertainWrites : BOOL := FALSE; +END_VAR +VAR_INPUT +{attribute 'symbol' := 'none'} +nativeIO : ARRAY[0..1999] OF WORD; +{attribute 'symbol' := 'none'} +nativeActiveRequest : POINTER TO atMbReqBase; +{attribute 'symbol' := 'none'} +nativeArgUnit : BYTE; +{attribute 'symbol' := 'none'} +nativeArgFunction : BYTE; +{attribute 'symbol' := 'none'} +nativeArgAddress : UINT; +{attribute 'symbol' := 'none'} +nativeArgQuantity : UINT; +{attribute 'symbol' := 'none'} +nativeArgTimeout : UDINT := 1000; +{attribute 'symbol' := 'none'} +nativeSent : UDINT; +{attribute 'symbol' := 'none'} +nativeReplied : UDINT; +{attribute 'symbol' := 'none'} +nativeWindowCount : UDINT; +{attribute 'symbol' := 'none'} +nativeWindowTime : UDINT; +END_VAR + +VAR_OUTPUT +out_connected : BOOL; +out_connectionState : UDINT; +out_lastNativeError : UDINT; +out_socketError : UDINT; +out_reconnectCount : UDINT; +out_registrationFull : BOOL; +END_VAR + + +(* ModbusMaster/atMbClient -- Implementation *) +IF nativeMode THEN + nativeCycle(); + RETURN; +END_IF; +//Modbus connection +//Connects to a modbus slave +//2024-11-23 ATM Cleaned up code +//2024-12-23 CWD Add statistics +//2025-06-18 CWD Add Get priorities and interval working together +//2026-03-26 ATM Clean up code, more testing +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +// ===== Main function call ===== + +IF (in_resetStats) THEN + resetStats(); + in_resetStats := FALSE; +END_IF + +// ===== Connect and disconnect logic ===== + +//Timer to trigger a connection if not connected for to long +reconnectTimer( + IN := NOT client.xConnect AND NOT client.xConnected AND NOT client.xError, + PT := RECONNECT_DELAY +); + +//Timer to trigger a disconnection if connect fails +disconnectTimer( + IN:=client.xConnect AND NOT client.xConnected, + PT:=DISCONNECT_DELAY +); + +//All requests offline timer +allRequestsOfflineTON( + IN := allRequestsOffline AND client.xConnected, + PT := ALL_OFFLINE_DELAY +); + +//Destroy Client delay timer +recreateClientDly( + IN:=destroyClient, + PT:=RECREATE_CLIENT_DELAY +); + +IF disconnectTimer.Q OR allRequestsOfflineTON.Q OR client.xError OR recreateTest THEN + //All requests are offline + //Connection attempt failed so disconnect + //If Client has error then disconnect + connect := FALSE; + recreateTest := FALSE; + destroyClient := TRUE; + setAllRequestsOffline(); +ELSIF reconnectTimer.Q AND NOT destroyClient THEN +//If not connected then attempt connect + connect := TRUE; + connectCnt := connectCnt + 1; //for debugging +END_IF; + + +IF recreateClientDly.Q THEN + destroyClient := FALSE; + recreateClient(); +END_IF; + +// If some reason gets stuck at busy, (This shouldnt happen, but force a reconnect). +forceConnect( + in := connect <> client.xConnect, + pt := FORCE_RECONNECT_DELAY +); + +// Only flip status of xConnect if no current request is active. +IF (connect <> client.xConnect) AND_THEN (allRequestsComplete OR forceConnect.Q) THEN + client.xConnect := connect; +END_IF + +// Moved this lower down, so xConnect happens one scan sooner. +client(); + +// If there are no requests, dont neeed to even do anything. +IF requestCnt = 0 THEN + numMsgSent := 0; + numMsgReply := 0; + allRequestsOnline := 0; + allRequestsOffline := 0; + RETURN; +END_IF + + +requestNotExecutingTON(IN := NOT requestExecuting, PT := NEXT_REQUEST_DELAY); //Only delaying 1 ms. This may not do much, was put in during testing, but now it almost seems unnecesary + +// ===== Execute next request ===== +IF connect AND client.xConnect AND client.xConnected AND (requestNotExecutingTON.Q OR NEXT_REQUEST_DELAY = T#0MS) THEN + + //Find the lowest priority of overdue requests + nextIdx := -1; + priorityLowest := PRIORITY_NONE; + highestTimeSinceRun := 0; + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF (requests[i]^.in_priorityCurrent < priorityLowest + OR (requests[i]^.in_priorityCurrent = priorityLowest AND requests[i]^.stats.sinceLastExec > highestTimeSinceRun)) + AND requests[i]^.stats.sinceLastExec > TIME_TO_UDINT(requests[i]^.in_minDelay) THEN + priorityLowest := requests[i]^.in_priorityCurrent; + highestTimeSinceRun := requests[i]^.stats.sinceLastExec; + nextIdx := i; + END_IF + END_FOR + + //Decriment all request priorities by one. + IF priorityLowest < PRIORITY_NONE AND priorityLowest > 0 THEN //Only if priority + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.in_priorityCurrent := requests[i]^.in_priorityCurrent - 1; + IF requests[i]^.in_priorityCurrent < 0 THEN + requests[i]^.in_priorityCurrent := 0; + END_IF + END_FOR + END_IF + + IF nextIdx >= 0 THEN + activeIndex := nextIdx; + END_IF + END_IF + +processAllRequests(); + +allRequestsOnline := (activeReqCnt > 0) AND (onlineCnt = activeReqCnt); +allRequestsOffline := (activeReqCnt > 0) AND (onlineCnt = 0); + +// ===== Client statistics ===== +//Get a sum of done and error counts from all requests +reqErrSum := 0; +reqDoneSum := 0; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + reqDoneSum := reqDoneSum + requests[i]^.stats.doneCnt; + reqErrSum := reqErrSum + requests[i]^.stats.errorCnt; + + //copy stats to array for debugging + reqStats[i] := ADR(requests[i]^.stats); +END_FOR + + + +//Every time the tx message counter changes increment msg count and add last transaction time +IF client.udiNumMsgSent <> msgCountLast THEN + msgCountLast := client.udiNumMsgSent; + msgTimeSum := msgTimeSum + client.udiLastTransactionTime; + msgCountSum := msgCountSum + 1; +END_IF + +//Every 1 second compute statistics +pulse(); +IF pulse.T_Sec_OS THEN + msgLastSecondCnt := msgCountSum; + msgCountSum := 0; + msgLastSecondTime := msgTimeSum; + msgTimeSum := 0; +END_IF + +numMsgSent := client.udiNumMsgSent; +numMsgReply := client.udiNumMsgReply; + + +(* ModbusMaster/atMbClientRTU -- Interface *) +{attribute 'no_assign'} +FUNCTION_BLOCK atMbClientRTU EXTENDS atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_port: BYTE := 0; //0=None, 1,2,3,4 + {attribute 'symbol' := 'none'} + in_baudRate: UINT := 9600; //4800, 9600, 19200, 38400, 57600, 115200 + {attribute 'symbol' := 'none'} + in_parity: BYTE := 0; //0=None, 1=Odd, 2=Even + {attribute 'symbol' := 'none'} + in_dataBits: BYTE := 8; //8=8 + {attribute 'symbol' := 'none'} + in_stopBits: BYTE := 1; //1=OneStopBit, 2=One5StopBits, 3=TwoStopBits +END_VAR +VAR + {attribute 'symbol' := 'none'} + _client: POINTER TO ModbusFB.ClientSerial; +END_VAR + +(* ModbusMaster/atMbClientRTU -- Implementation *) +//Modbus connection +//Connects to a modbus slave +//2024-06-27 CWD Initial create +//2024-11-13 CWD Fix problem with not connecting properly on power up +//2024-11-15 ATM Move from structure to defaults. +//2024-11-23 ATM Cleaned up code +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +IF NOT setupClient() THEN + RETURN; +END_IF + +//Set dataRate +//This is based on an 8 byte request and a 255 byte reply +dataRate := in_baudRate; + + +// ===== Initialization ===== +// if the param changes, force a reinit +IF _client^.iPort <> in_port + OR _client^.dwBaudrate <> in_baudRate + OR _client^.byDataBits <> in_dataBits + OR _client^.eParity <> in_parity + OR _client^.eStopBits <> in_stopBits THEN + initDone := FALSE; +END_IF; + +IF NOT initDone THEN + initDone := TRUE; + + //Other configuration parameters + _client^.iPort := in_port; + _client^.dwBaudrate := in_baudRate; + _client^.byDataBits := in_dataBits; + _client^.eParity := in_parity; + _client^.eStopBits := in_stopBits; + _client^.eDTRcontrol := 0; + _client^.eRTScontrol := 0; + _client^.eRTUASCII := 0; + _client^.udiLogOptions := ModbusFB.LoggingOptions.All; + _client^.xConnect:=FALSE; + +END_IF + +SUPER^(); + +(* ModbusMaster/atMbClientTCP -- Interface *) +{attribute 'no_assign'} +FUNCTION_BLOCK atMbClientTCP EXTENDS atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_ipAddress: ARRAY[0..3] OF BYTE; // TCP IP Address + {attribute 'symbol' := 'none'} + in_port: UINT := 502; // TCP port + {attribute 'symbol' := 'none'} + in_baudrate: UINT := 0; // Set this if using RS485 to TCP Converter, used to calculate timeout. +END_VAR +VAR + {attribute 'symbol' := 'none'} + _client: POINTER TO ModbusFB.ClientTCP; +END_VAR +VAR_INPUT +in_enable : BOOL := TRUE; +in_connectTimeout : TIME := T#2S; +in_reconnectDelay : TIME := T#1S; +in_holdUncertainWrites : BOOL := FALSE; +END_VAR + +VAR +nativeEndpointIP : DWORD; +nativeEndpointPort : UINT; +nativeConfigConnect : UDINT; +nativeConfigRetry : UDINT; +END_VAR + + +(* ModbusMaster/atMbClientTCP -- Implementation *) +nativeMode := TRUE; +nativeIP := SHL(BYTE_TO_DWORD(in_ipAddress[0]),24) OR + SHL(BYTE_TO_DWORD(in_ipAddress[1]),16) OR + SHL(BYTE_TO_DWORD(in_ipAddress[2]),8) OR BYTE_TO_DWORD(in_ipAddress[3]); +nativePort := in_port; +nativeConnectTimeout := MAX(UDINT#1,TIME_TO_UDINT(in_connectTimeout)); +nativeRetryDelay := MAX(UDINT#1,TIME_TO_UDINT(in_reconnectDelay)); +nativeHoldUncertainWrites := in_holdUncertainWrites; +IF nativeHandle <> 0 AND ((nativeEndpointIP <> nativeIP) OR + (nativeEndpointPort <> nativePort) OR (nativeConfigConnect <> nativeConnectTimeout) OR + (nativeConfigRetry <> nativeRetryDelay) OR NOT in_enable) THEN + nativeRelease(); +END_IF; +nativeEndpointIP := nativeIP; nativeEndpointPort := nativePort; +nativeConfigConnect := nativeConnectTimeout; nativeConfigRetry := nativeRetryDelay; +nativeEnable := in_enable; +IF in_baudrate <> 0 THEN dataRate := in_baudrate; ELSE dataRate := 105200; END_IF; +SUPER^(); + +(* ModbusMaster/atMbReqBase -- Interface *) +{attribute 'no_assign'} +FUNCTION_BLOCK ABSTRACT atMbReqBase +VAR_INPUT + {attribute 'symbol' := 'none'} + client: REFERENCE TO atMbClient; // Instance of atMBClient + {attribute 'symbol' := 'none'} + in_startAddr: UINT; + {attribute 'symbol' := 'none'} + in_quantity: UINT; + {attribute 'symbol' := 'none'} + in_dataOffset: UINT; // Offset position for data into array. + {attribute 'symbol' := 'none'} + in_unitId: BYTE := 0; + {attribute 'symbol' := 'none'} + in_timeout: SYSTIME := 0; // timeout in ms, if not set, will be 1ms less than interval. + {attribute 'symbol' := 'none'} + in_minDelay: TIME := T#10MS; // This is the minimum delay between queries, only relevent if high priority. + {attribute 'symbol' := 'none'} + in_prioritySetpoint: INT := 50; // this requests priority compared to other requests. Lower value is higher priority. + {attribute 'symbol' := 'none'} + in_priorityCurrent: INT; //Current priority. This is decrimented and the request is executed when at zero. + {attribute 'symbol' := 'none'} + stats: atMbRequestStats; //Request stats + +END_VAR +VAR_OUTPUT + //{attribute 'symbol' := 'none'} + //out_fault: BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + dataSize: UINT; + //{attribute 'symbol' := 'none'} + //quantUsed: UINT; + //{attribute 'symbol' := 'none'} + //doneTrig: R_TRIG; //Succesful request trigger + //{attribute 'symbol' := 'none'} + //errTrig: R_TRIG; //Error request result trigger + + {attribute 'symbol' := 'none'} + request: REFERENCE TO ModbusFB.ClientRequest;; +END_VAR + + +VAR +nativeFunction : BYTE; +nativeCount : UINT; +nativeOffset : UINT; +nativeInFlight : BOOL; +nativeReadComplete : BOOL; +nativeCopyAllowed : BOOL; +nativeWriteHold : BOOL; +nativeStartMs : ULINT; +nativeLoop : DINT; +nativeRC : UDINT; +END_VAR + +VAR_OUTPUT +out_nativeError : UDINT; +out_socketError : UDINT; +out_writeUncertain : BOOL; +out_validData : BOOL; +out_dataAgeMs : ULINT; +out_stale : BOOL := TRUE; +out_parameterError : BOOL; +END_VAR + +VAR_INPUT +in_staleAfter : TIME := T#2S; +END_VAR + + +(* ModbusMaster/atMbReqBase -- Implementation *) +//Modbus request +//Makes a request using the start and quantity, puts the result into data +//2024-06-27 CWD Initial create +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage + +IF NOT __ISVALIDREF(client) THEN + RETURN; +END_IF + + +//Add this request to the clients request array +client.addRequest(THIS); + +//fault logic +//Fault if error +//errTrig(CLK := stats.reqError); +//IF errTrig.Q THEN +// out_fault := TRUE; +//END_IF +//not faulted if a succesfull request +//doneTrig(CLK := stats.reqDone); +// also clear fault if not active +//IF doneTrig.Q OR NOT stats.active THEN +// out_fault := FALSE; +//END_IF + +IF in_quantity <= dataSize THEN + stats.reqLength := in_quantity; +ELSE + stats.reqLength := dataSize; +END_IF; + +(* ModbusMaster/atMbReqReadCoil -- Interface *) +{attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadCoil EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readCoilReq: POINTER TO ModbusFB.ClientRequestReadCoils; +END_VAR + + +(* ModbusMaster/atMbReqReadCoil -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-11-16 CWD Added read coil +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + + + +(* ModbusMaster/atMbReqReadDiscrete -- Interface *) +{attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadDiscrete EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readDiscreteReq: POINTER TO ModbusFB.ClientRequestReadDiscreteInputs; +END_VAR + + +(* ModbusMaster/atMbReqReadDiscrete -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + +(* ModbusMaster/atMbReqReadHolding -- Interface *) +{attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadHolding EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readHoldReq: POINTER TO ModbusFB.ClientRequestReadHoldingRegisters; +END_VAR + + +(* ModbusMaster/atMbReqReadHolding -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + +(* ModbusMaster/atMbReqReadInput -- Interface *) +{attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadInput EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readInputReq: POINTER TO ModbusFB.ClientRequestReadInputRegisters; +END_VAR + + +(* ModbusMaster/atMbReqReadInput -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + +(* ModbusMaster/atMbRequestStats -- Interface *) +{attribute 'monitoring_display' := 'toString'} +{attribute 'no_assign'} +FUNCTION_BLOCK atMbRequestStats +VAR_INPUT + lastErrorId: modbusFB.Error; //Last error id + lastExceptionCode: atMBExceptionCodes; //Last modbus exception code. These are the standard modbus exceptions. + lastValidDuration: UDINT; // How long last transaction took (ms) + longestValidDuration: UDINT; //Longest valid duration (ms) + interval: UDINT; // Start-to-start interval (ms) + offlineCnt: UINT; //Count of how many times this request has gone offline + doneCnt: UDINT; // Count of successful request + errorCnt: UDINT; // Count of failed requests + doneCntRst: UDINT; // Count of succesful requests, resets on error result + errorCntRst: UDINT; // Count of failed requests, resets on succesful request + sinceLastExec: UDINT; // Time since the last start (ms) + timeoutCnt: UDINT; + reqLength: UINT; //Number of registers requested in request, used to calc active. +END_VAR +VAR_OUTPUT + executeLight: BOOL; //Execute that stays on momentarily + reqDoneLight: BOOL; //Request done that stays on momentarily + reqErrorLight: BOOL; //Request error that stays on momentarily + reqBusyLight: BOOL; //Request busy that stays on momentarily + online: BOOL; //Online means the requests are being replied to normally or with modbus exceptions. Communications are ok. + offline: BOOL; //Opposite of online, but starts out as false on program start. + active: BOOL; // This is true if its actually being used, and not having a count of 0 on the request. + executeMbFb: BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + inDoneDeb: atDebounce; + {attribute 'symbol' := 'none'} + inErrDeb: atDebounce; + {attribute 'symbol' := 'none'} + inBusyDeb: atDebounce; + {attribute 'symbol' := 'none'} + executeDeb: atDebounce; + toString: STRING(20); + {attribute 'symbol' := 'none'} + executeTrig: R_TRIG; //Execute trigger + {attribute 'symbol' := 'none'} + offlineTrig: R_TRIG; //Offline trigger + {attribute 'symbol' := 'none'} + lastReqStart: SYSTIME; //time of start of last request + {attribute 'symbol' := 'none'} + TSSus: SYSTIME; + _execute: BOOL; //Execute trigger and status. Turn on to trigger request. Will turn off when request is complete + _reqDone: BOOL; //Request done + _reqBusy: BOOL; //Request busy + _reqError: BOOL; //Request error +END_VAR + +VAR CONSTANT + {attribute 'symbol' := 'none'} + lightOn: TIME:= T#100MS; //Light on time for debug indicators +END_VAR +VAR_INPUT +nativeResponseSeen : BOOL; +nativeLastSuccessMs : ULINT; +END_VAR + + +(* ModbusMaster/atMbRequestStats -- Implementation *) +SysTimeGetUs(TSSus); + +IF (reqLength = 0) THEN + active := FALSE; + doneCnt := 0; + errorCnt := 0; + lastErrorId := modbusFB.Error.OK; + lastExceptionCode := atMBExceptionCodes.RESPONSE_SUCCESS; + execute := FALSE; + online := FALSE; + offline := FALSE; + reqDone := FALSE; + reqBusy := FALSE; + reqError := FALSE; + sinceLastExec := 0; + RETURN; +END_IF +active := TRUE; + +inDoneDeb(in_raw := reqDone, in_delOn := T#0S, in_DelOff := lightOn); +reqDoneLight := inDoneDeb.out_val; + +inBusyDeb(in_raw := reqBusy, in_delOn := T#0S, in_DelOff := lightOn); +reqBusyLight := inBusyDeb.out_val; + +inErrDeb(in_raw := reqError, in_delOn := T#0S, in_DelOff := lightOn); +reqErrorLight := inErrDeb.out_val; + +//Debounced execute indicator for debbugging +executeDeb(in_raw := _execute, in_delOn := T#0S, in_DelOff := lightOn); +executeLight := executeDeb.out_val; + + +//Set online status +IF nativeResponseSeen OR doneCntRst >=3 THEN + online := TRUE; + offline := FALSE; +END_IF + +//Set online status +IF NOT nativeResponseSeen AND errorCntRst >= 3 THEN + online := FALSE; + offline := TRUE; +END_IF + +//Record start time in ms on execute rising edge +executeTrig(CLK := execute); +IF executeTrig.Q THEN + interval := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + lastReqStart := TSSus; +END_IF + +//Offline count +offlineTrig(CLK := NOT online); +IF offlineTrig.Q THEN + offlineCnt := offlineCnt + 1; +END_IF + +//Calculate ms since last execute +//If the last request start is more then 1 minute ago then set it for 1 minute +IF TSSus >= lastReqStart THEN + IF TSSus - lastReqStart > 60000000 THEN lastReqStart := TSSus - 60000000; END_IF; +ELSE lastReqStart := TSSus; +END_IF; +sinceLastExec := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + + +(* ModbusMaster/atMbReqWriteCoil -- Interface *) +{attribute 'no_assign'} +FUNCTION_BLOCK atMbReqWriteCoil EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + writeCoilsReq: POINTER TO ModbusFB.ClientRequestWriteMultipleCoils; +END_VAR + + +(* ModbusMaster/atMbReqWriteCoil -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + +(* ModbusMaster/atMbReqWriteHolding -- Interface *) +{attribute 'no_assign'} +FUNCTION_BLOCK atMbReqWriteHolding EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + writeHoldReq: POINTER TO ModbusFB.ClientRequestWriteMultipleRegisters; +END_VAR + + +(* ModbusMaster/atMbReqWriteHolding -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + +(* ModbusMaster/To_IPAddress -- Interface *) +FUNCTION To_IPAddress : ARRAY[0..3] OF BYTE +VAR_INPUT + byte1: BYTE; + byte2: BYTE; + byte3: BYTE; + byte4: BYTE; +END_VAR + + +(* ModbusMaster/To_IPAddress -- Implementation *) + //Convert parameters to array +To_IPAddress[0] := byte1; +To_IPAddress[1] := byte2; +To_IPAddress[2] := byte3; +To_IPAddress[3] := byte4; + + + + +(* atMbClient/addRequest -- Interface *) +//Adds the specified request to the global list +METHOD PUBLIC addRequest +VAR_INPUT + request: POINTER TO atMbReqBase; +END_VAR +VAR + i: UINT := 0; + +END_VAR + + +(* atMbClient/addRequest -- Implementation *) +IF request = 0 THEN RETURN; END_IF; +IF requestCnt > 0 THEN + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF request = requests[i] THEN RETURN; END_IF; + END_FOR; +END_IF; +IF requestCnt >= 100 THEN + out_registrationFull := TRUE; + RETURN; +END_IF; +requests[requestCnt] := request; +requestCnt := requestCnt + 1; + +(* atMbClient/processAllRequests -- Interface *) +METHOD processAllRequests +VAR_INPUT +END_VAR +VAR + i: DINT; // Loop bstisnlr +END_VAR + +(* atMbClient/processAllRequests -- Implementation *) +IF requestCnt = 0 OR requestCnt > 100 THEN RETURN; END_IF; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i] <> 0 THEN + requests[i]^.processRequest(); + END_IF; +END_FOR; + +(* atMbClient/recreateAllRequests -- Interface *) +METHOD PROTECTED recreateAllRequests +VAR_INPUT +END_VAR + +VAR + i : DINT; +END_VAR + + +(* atMbClient/recreateAllRequests -- Implementation *) +IF requestCnt = 0 OR requestCnt > 100 THEN RETURN; END_IF; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i] <> 0 THEN + requests[i]^.recreateRequest(); + END_IF; +END_FOR; + +(* atMbClient/recreateClient -- Interface *) +METHOD ABSTRACT recreateClient +VAR_INPUT +END_VAR + +(* atMbClient/recreateClient -- Implementation *) + + +(* atMbClient/resetStats -- Interface *) +METHOD resetStats +VAR_INPUT +END_VAR +VAR + i: DINT; + val: POINTER TO UDINT; +END_VAR + + + + + +(* atMbClient/resetStats -- Implementation *) +IF nativeMode THEN + nativeSent := 0; nativeReplied := 0; + nativeWindowCount := 0; nativeWindowTime := 0; + msgLastSecondCnt := 0; msgLastSecondTime := 0; + numMsgSent := 0; numMsgReply := 0; + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.stats.resetStats(); + END_FOR; + RETURN; +END_IF; +connectCnt := 0; +val := ADR(client.udiNumMsgSent); +val^ := 0; +val := ADR(client.udiNumMsgReply); +val^ := 0; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.stats.resetStats(); +END_FOR; + +(* atMbClient/setAllRequestsOffline -- Interface *) +METHOD PROTECTED setAllRequestsOffline : BOOL +VAR_INPUT +END_VAR +VAR + i: DINT; +END_VAR + + + + + +(* atMbClient/setAllRequestsOffline -- Implementation *) +IF requestCnt = 0 OR requestCnt > 100 THEN RETURN; END_IF; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i] <> 0 THEN + requests[i]^.stats.setClientOffline(); + END_IF; +END_FOR; + +(* atMbClient/setupClient -- Interface *) +METHOD setupClient : BOOL +VAR_INPUT +END_VAR + + +(* atMbClient/setupClient -- Implementation *) +IF nativeMode THEN + setupClient := nativeHandle <> 0; + RETURN; +END_IF; +// Only create request if doesnt exist +IF NOT __ISVALIDREF(client) THEN + recreateClient(); + IF NOT __ISVALIDREF(client) THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; + setupClient := FALSE; + RETURN; + END_IF +END_IF + +setupClient := TRUE; + +(* atMbClient/activeIndex -- Interface *) +PROPERTY activeIndex : DINT + +(* atMbClient/activeReqCnt -- Interface *) +PROPERTY PRIVATE activeReqCnt : UINT + +(* atMbClient/allRequestsComplete -- Interface *) +{attribute 'symbol' := 'none'} +PROPERTY PRIVATE allRequestsComplete : BOOL + +(* atMbClient/onlineCnt -- Interface *) +{attribute 'symbol' := 'none'} +PROPERTY onlineCnt : DINT//Count of requests that are online + +(* atMbClient/requestExecuting -- Interface *) +{attribute 'symbol' := 'none'} +PROPERTY PRIVATE requestExecuting : BOOL //There is at least one request executing + +(* atMbClientRTU/recreateClient -- Interface *) +METHOD recreateClient +VAR_INPUT + old_client_id: POINTER TO ModbusFB.ClientSerial; +END_VAR + + +(* atMbClientRTU/recreateClient -- Implementation *) +old_client_id := _client; +_client := __NEW(ModbusFB.ClientSerial); +IF _client = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; +END_IF; +client REF= _client^; +connect := FALSE; + +IF old_client_id <> 0 THEN + __DELETE(old_client_id); + old_client_id := 0; +END_IF + +recreateAllRequests(); + +(* atMbClientRTU/toString -- Interface *) +{attribute 'symbol' := 'none'} +PROPERTY toString : atMbClientToString //Client request status noneOk = no requests working, someOk = some requests are working some are failing, allOk = all requests are working + +(* atMbClientTCP/recreateClient -- Interface *) +METHOD recreateClient +VAR +END_VAR + +(* atMbClientTCP/recreateClient -- Implementation *) +nativeRelease(); +// Next scan creates a fresh native context with current endpoint settings. + +(* atMbReqBase/busy -- Interface *) +METHOD busy : BOOL +VAR_INPUT + in: BOOL; +END_VAR +VAR + executeTime: ULINT; + replyTimeoutTime: ULINT; +END_VAR + + + +(* atMbReqBase/busy -- Implementation *) +stats.reqBusy := in; + +IF NOT in THEN + RETURN; +END_IF; + +(* atMbReqBase/completeRequest -- Interface *) +METHOD completeRequest +VAR_INPUT +END_VAR + + +(* atMbReqBase/completeRequest -- Implementation *) +done(request.xDone); +err(in := request.xError, errorId := request.eErrorId, exceptionCode := request.eException); +busy(in := request.xBusy); +stats(); + +IF (atGVL.reset) THEN + // request.AbortModel(); + request.ResetModel(TRUE); +END_IF + +(* atMbReqBase/done -- Interface *) +METHOD done +VAR_INPUT + // Done state + in: BOOL; +END_VAR + + +(* atMbReqBase/done -- Implementation *) +stats.reqDone := in; + +IF NOT in THEN + RETURN; +END_IF + +//Set priority for next request +in_priorityCurrent := in_prioritySetpoint; + +(* atMbReqBase/err -- Interface *) +METHOD err +VAR_INPUT + // Error state + in: BOOL; + errorId: modbusFB.Error; //modbusFB error ID + exceptionCode: modbusFB.ExceptionCodes; //ModbusFB exception code +END_VAR +VAR + priorityMultiplier: INT; //Raise priority when not response +END_VAR + + +(* atMbReqBase/err -- Implementation *) +stats.reqError := in; + +IF NOT in THEN + RETURN; +END_IF + + +//Update error info +stats.lastErrorId := errorId; +stats.lastExceptionCode := ANY_TO_BYTE(exceptionCode); + +//Since we are here there was an exception. If the modbus response is 0, that means no modbus communication happened, so we are offline. If we would have had a succesful communication we would have got a actual modbus error. +//IF stats.lastExceptionCode = atMBExceptionCodes.RESPONSE_SUCCESS THEN + //Since there was no response from the device set the priority to higher number priority +// in_priorityCurrent := in_prioritySetPoint * 5; +//END_IF + +//Set priority for next request, priority gets higher after each error result +priorityMultiplier := UDINT_TO_INT(stats.errorCntRst); +IF priorityMultiplier > 3 THEN + priorityMultiplier := 3; +END_IF +// set multiplier to exponential retry. +in_priorityCurrent := 10 * priorityMultiplier * priorityMultiplier; +//in_priorityCurrent := in_prioritySetpoint * priorityMultiplier; + +// NO idea what Reset Model does, hoping it fixes comms issues +// request.ResetModel(TRUE); // I dont think this helped + +(* atMbReqBase/processRequest -- Interface *) +METHOD ABSTRACT processRequest +VAR_INPUT +END_VAR + + +(* atMbReqBase/processRequest -- Implementation *) + + +(* atMbReqBase/recreateRequest -- Interface *) +METHOD ABSTRACT recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + +(* atMbReqBase/recreateRequest -- Implementation *) + + +(* atMbReqBase/setupClient -- Interface *) +METHOD setupClient : BOOL +VAR_INPUT + in_request: POINTER TO ModbusFB.ClientRequest; +END_VAR + + +(* atMbReqBase/setupClient -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + setupClient := FALSE; // Native path has no ModbusFB request object. + RETURN; +END_IF; +// Only create request if doesnt exist +IF in_request = 0 THEN + in_request := recreateRequest(); + IF in_request = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; + setupClient := FALSE; + RETURN; + END_IF +END_IF + +IF NOT __ISVALIDREF(mbsClient) THEN + setupClient := FALSE; + RETURN; +END_IF + +request REF= in_request^; + +setupClient := TRUE; +RETURN; + +(* atMbReqBase/mbsClient -- Interface *) +PROPERTY PROTECTED mbsClient : REFERENCE TO ModbusFB.Client + +(* atMbReqBase/replyTimeout -- Interface *) +//Calculates the reply timout setting +PROPERTY PROTECTED replyTimeout : UDINT + +(* atMbReqReadCoil/processRequest -- Interface *) +METHOD processRequest + + +(* atMbReqReadCoil/processRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(1); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := client.nativeIO[nativeLoop] <> 0; + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readCoilReq) THEN + RETURN; +END_IF + +//Do request processing +readCoilReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + +(* atMbReqReadCoil/recreateRequest -- Interface *) +METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + +(* atMbReqReadCoil/recreateRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readCoilReq <> 0 THEN + __DELETE(readCoilReq); + readCoilReq := 0; +END_IF +readCoilReq := __NEW(ModbusFB.ClientRequestReadCoils); +recreateRequest := readCoilReq; + +(* atMbReqReadDiscrete/processRequest -- Interface *) +METHOD processRequest + + +(* atMbReqReadDiscrete/processRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(2); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := client.nativeIO[nativeLoop] <> 0; + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readDiscreteReq) THEN + RETURN; +END_IF + +//Do request processing +readDiscreteReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + +(* atMbReqReadDiscrete/recreateRequest -- Interface *) +METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + +(* atMbReqReadDiscrete/recreateRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readDiscreteReq <> 0 THEN + __DELETE(readDiscreteReq); + readDiscreteReq := 0; +END_IF +readDiscreteReq := __NEW(ModbusFB.ClientRequestReadDiscreteInputs); +recreateRequest := readDiscreteReq; + +(* atMbReqReadHolding/processRequest -- Interface *) +METHOD processRequest + + +(* atMbReqReadHolding/processRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(3); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := WORD_TO_UINT(client.nativeIO[nativeLoop]); + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readHoldReq) THEN + RETURN; +END_IF + +//Do request processing +readHoldReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + // udiReplyTimeout := 1 * 1000 * 1000, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + +(* atMbReqReadHolding/recreateRequest -- Interface *) +METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + +(* atMbReqReadHolding/recreateRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readHoldReq <> 0 THEN + __DELETE(readHoldReq); + readHoldReq := 0; +END_IF +readHoldReq := __NEW(ModbusFB.ClientRequestReadHoldingRegisters); +recreateRequest := readHoldReq; + +(* atMbReqReadInput/processRequest -- Interface *) +METHOD processRequest + + +(* atMbReqReadInput/processRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(4); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := WORD_TO_UINT(client.nativeIO[nativeLoop]); + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readInputReq) THEN + RETURN; +END_IF + +//Do request processing +readInputReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + +(* atMbReqReadInput/recreateRequest -- Interface *) +METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + +(* atMbReqReadInput/recreateRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readInputReq <> 0 THEN + __DELETE(readInputReq); + readInputReq := 0; +END_IF +readInputReq := __NEW(ModbusFB.ClientRequestReadInputRegisters); +recreateRequest := readInputReq; + +(* atMbRequestStats/resetStats -- Interface *) +METHOD resetStats +VAR_INPUT +END_VAR + + +(* atMbRequestStats/resetStats -- Implementation *) +doneCnt := 0; +doneCntRst := 0; +errorCnt := 0; +errorCntRst := 0; + +(* atMbRequestStats/setClientOffline -- Interface *) +METHOD setClientOffline : BOOL +VAR_INPUT +END_VAR + + +(* atMbRequestStats/setClientOffline -- Implementation *) +nativeResponseSeen := FALSE; +doneCntRst := 0; +online := FALSE; +offline := TRUE; + +(* atMbRequestStats/setToString -- Interface *) +METHOD PRIVATE setToString : BOOL +VAR_INPUT +END_VAR + + +(* atMbRequestStats/setToString -- Implementation *) +toString := CONCAT('OK:', CONCAT(TO_STRING(doneCnt), CONCAT('/Er:', TO_STRING(errorCnt)))); + +(* atMbRequestStats/execute -- Interface *) +PROPERTY execute : BOOL + +(* atMbRequestStats/reqBusy -- Interface *) +PROPERTY reqBusy : BOOL + +(* atMbRequestStats/reqDone -- Interface *) +PROPERTY reqDone : BOOL + +(* atMbRequestStats/reqError -- Interface *) +PROPERTY reqError : BOOL + +(* atMbReqWriteCoil/processRequest -- Interface *) +METHOD processRequest + + +(* atMbReqWriteCoil/processRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS AND NOT out_parameterError THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(stats.reqLength)-1 DO + client.nativeIO[nativeLoop] := BOOL_TO_WORD(data[UINT_TO_DINT(in_dataOffset)+nativeLoop]); + END_FOR; + END_IF; + nativeProcess(15); + RETURN; +END_IF; +IF NOT setupClient(writeCoilsReq) THEN + RETURN; +END_IF + +//Do request processing +writeCoilsReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + +(* atMbReqWriteCoil/recreateRequest -- Interface *) +METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + +(* atMbReqWriteCoil/recreateRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF writeCoilsReq <> 0 THEN + __DELETE(writeCoilsReq); + writeCoilsReq := 0; +END_IF +writeCoilsReq := __NEW(ModbusFB.ClientRequestWriteMultipleCoils); +recreateRequest := writeCoilsReq; + +(* atMbReqWriteCoil/writeNow -- Interface *) +METHOD writeNow : BOOL +VAR_INPUT +END_VAR + + +(* atMbReqWriteCoil/writeNow -- Implementation *) +nativeWriteHold := FALSE; +out_writeUncertain := FALSE; +in_priorityCurrent := 0; + +(* atMbReqWriteHolding/processRequest -- Interface *) +METHOD processRequest + + +(* atMbReqWriteHolding/processRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS AND NOT out_parameterError THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(stats.reqLength)-1 DO + client.nativeIO[nativeLoop] := UINT_TO_WORD(data[UINT_TO_DINT(in_dataOffset)+nativeLoop]); + END_FOR; + END_IF; + nativeProcess(16); + RETURN; +END_IF; +IF NOT setupClient(writeHoldReq) THEN + RETURN; +END_IF + +//Do request processing +writeHoldReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + +(* atMbReqWriteHolding/recreateRequest -- Interface *) +METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + +(* atMbReqWriteHolding/recreateRequest -- Implementation *) +IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF writeHoldReq <> 0 THEN + __DELETE(writeHoldReq); + writeHoldReq := 0; +END_IF +writeHoldReq := __NEW(ModbusFB.ClientRequestWriteMultipleRegisters); +recreateRequest := writeHoldReq; + +(* atMbReqWriteHolding/writeNow -- Interface *) +METHOD writeNow : BOOL +VAR_INPUT +END_VAR + + +(* atMbReqWriteHolding/writeNow -- Implementation *) +nativeWriteHold := FALSE; +out_writeUncertain := FALSE; +in_priorityCurrent := 0; + +(* activeIndex/Get -- Interface *) +VAR + i: DINT; //Loop variable +END_VAR + + +(* activeIndex/Get -- Implementation *) +IF nativeMode THEN + activeIndex := nativeActiveIndex; + RETURN; +END_IF; +// ===== Test for any requests executing ===== +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i]^.stats.execute OR requests[i]^.stats.reqBusy THEN + activeIndex := i; + RETURN; + END_IF +END_FOR +activeIndex := -1; + + +(* activeIndex/Set -- Interface *) +VAR +END_VAR + + +(* activeIndex/Set -- Implementation *) +// set request to active, which is how the active index gets calculated. +requests[activeIndex]^.stats.execute := TRUE; + +(* activeReqCnt/Get -- Interface *) +VAR + i: UINT; //Loop variable + cnt: UINT; +END_VAR + + +(* activeReqCnt/Get -- Implementation *) +cnt := 0; +IF requestCnt = 0 THEN + activeReqCnt := 0; + RETURN; +END_IF +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + //Test for any offline request + IF requests[i]^.stats.active THEN + cnt := cnt + 1; + END_IF +END_FOR; +activeReqCnt := cnt; + +(* allRequestsComplete/Get -- Interface *) +VAR + i: UINT; +END_VAR + + + + + +(* allRequestsComplete/Get -- Implementation *) +IF requestCnt = 0 THEN + allRequestsComplete := 0; + RETURN; +END_IF +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + //Test for any offline request + IF requests[i]^.stats.execute OR requests[i]^.stats.reqDone OR requests[i]^.stats.reqBusy OR requests[i]^.stats.reqError THEN + allRequestsComplete := FALSE; + RETURN; + END_IF +END_FOR; +allRequestsComplete := TRUE; + +(* onlineCnt/Get -- Interface *) +VAR + i: UINT; //Loop variable + cnt: UINT; +END_VAR + + +(* onlineCnt/Get -- Implementation *) +cnt := 0; +IF requestCnt = 0 THEN + onlineCnt := 0; + RETURN; +END_IF +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + //Test for any offline request + IF requests[i]^.stats.online THEN + cnt := cnt + 1; + END_IF +END_FOR; +onlineCnt := cnt; + +(* requestExecuting/Get -- Interface *) +VAR + i: DINT; //Loop variable +END_VAR + + +(* requestExecuting/Get -- Implementation *) +// ===== Test for any requests executing ===== +requestExecuting := activeIndex <> -1; + +(* toString/Get -- Interface *) + + +(* toString/Get -- Implementation *) +//Calculation for tostring +IF onlineCnt = requestCnt THEN + toString := atMbClientToString.allOk; +ELSIF onlineCnt = 0 THEN + toString := atMbClientToString.noneOk; +ELSE + toString := atMbClientToString.someOk; +END_IF + +(* mbsClient/Get -- Interface *) +VAR +END_VAR + + +(* mbsClient/Get -- Implementation *) +IF NOT __ISVALIDREF(client) OR_ELSE NOT __ISVALIDREF(client.client) THEN + RETURN; +END_IF + +mbsClient REF= client.client; + +(* replyTimeout/Get -- Interface *) +VAR +END_VAR + + +(* replyTimeout/Get -- Implementation *) + +IF in_timeout > 0 THEN + //Explicitly set timout + replyTimeout := ANY_TO_UDINT((in_timeout) * 1000); +ELSE + // If client timeout is set, use that. + IF client.in_timeout <> T#0MS THEN + replyTimeout := TIME_TO_UDINT(client.in_timeout) * 1000; + ELSIF client.dataRate <> 0 THEN + //Data transmision time * 1.5 to get the timeout. Result is in ms + //8 byte request, then 255 byte reply = 2630 bits. + replyTimeout := REAL_TO_UDINT(2630 / DINT_TO_REAL(client.dataRate) * 1.5 * 1000000); + ELSE + //In case client datarate = zero + replyTimeout := 1000000; //1 second? + END_IF + + +END_IF + + + +(* execute/Get -- Interface *) +VAR +END_VAR + + +(* execute/Get -- Implementation *) +execute := _execute; + +(* execute/Set -- Interface *) +VAR +END_VAR + + +(* execute/Set -- Implementation *) +IF reqDone OR reqError THEN + _execute := FALSE; + RETURN; +END_IF +_execute := execute; + +(* reqBusy/Get -- Interface *) +VAR +END_VAR + + +(* reqBusy/Get -- Implementation *) +reqBusy := _reqBusy; + +(* reqBusy/Set -- Interface *) +VAR +END_VAR + + +(* reqBusy/Set -- Implementation *) +_reqBusy := reqBusy; + +(* reqDone/Get -- Interface *) +VAR +END_VAR + + +(* reqDone/Get -- Implementation *) +reqDone := _reqDone; + +(* reqDone/Set -- Interface *) +VAR +END_VAR + + +(* reqDone/Set -- Implementation *) +_reqDone := reqDone; + +IF (execute AND reqDone) THEN + // ===== Request has just completed succesfully ===== + //Update last valid duration + lastValidDuration := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + IF lastValidDuration > longestValidDuration THEN + longestValidDuration := lastValidDuration; + END_IF + //Counters + doneCnt := doneCnt + 1; + doneCntRst := doneCntRst + 1; + errorCntRst := 0; + + //Update error info + lastErrorId := modbusFB.Error.OK; + lastExceptionCode := atMBExceptionCodes.RESPONSE_SUCCESS; + + execute := FALSE; + setToString(); +END_IF + +(* reqError/Get -- Interface *) +VAR +END_VAR + + +(* reqError/Get -- Implementation *) +reqError := _reqError; + +(* reqError/Set -- Interface *) +VAR +END_VAR + + +(* reqError/Set -- Implementation *) +_reqError := reqError; + +IF (execute AND reqError) THEN + // ===== Request has just completed with error ===== + // Counters + errorCnt := errorCnt + 1; + errorCntRst := errorCntRst + 1; + doneCntRst := 0; + //Turn off execute + execute := FALSE; + + //Since we are here there was an exception. If the modbus response is 0, that means no modbus communication happened, so we are offline. If we would have had a succesful communication we would have got a actual modbus error. + IF lastExceptionCode = atMBExceptionCodes.RESPONSE_SUCCESS THEN + timeoutCnt := timeoutCnt + 1; + lastExceptionCode := atMBExceptionCodes.TIMEOUT; + END_IF + setToString(); +END_IF + +(* atMbClient/nativeCall -- Interface *) +METHOD nativeCall : UDINT +VAR_INPUT + command : UDINT; +END_VAR + +(* atMbClient/nativeCall -- Implementation *) +nativeCall := CmpAtomModbus.atMbCall_cext( + command := command, handle := nativeHandle, ipAddress := nativeIP, + port := nativePort, unitId := nativeArgUnit, functionCode := nativeArgFunction, + startAddress := nativeArgAddress, quantity := nativeArgQuantity, + pData := ADR(nativeIO[0]), dataCapacity := 2000, + pStatus := ADR(nativeStatus[0]), statusCapacity := 24, nowMs := nativeNowMs, + connectTimeoutMs := nativeConnectTimeout, responseTimeoutMs := nativeArgTimeout, + retryDelayMs := nativeRetryDelay); + +(* atMbClient/nativeCycle -- Interface *) +METHOD nativeCycle +VAR + j : DINT; + best : DINT; + priority : INT; + age : UDINT; + rc : UDINT; +END_VAR + +(* atMbClient/nativeCycle -- Implementation *) +SysTimeGetUs(nativeNowUs); +nativeNowMs := nativeNowUs / 1000; +IF in_resetStats THEN + resetStats(); + in_resetStats := FALSE; +END_IF; +IF nativeHandle = 0 AND nativeEnable THEN + rc := nativeCall(1); + IF rc <> 0 THEN + out_lastNativeError := rc; + RETURN; + END_IF; + nativeHandle := nativeStatus[0]; + nativeArgTimeout := 1000; + rc := nativeCall(9); + IF rc <> 0 THEN + out_lastNativeError := rc; + nativeCall(2); nativeHandle := 0; + RETURN; + END_IF; + nativeCall(3); +END_IF; +IF nativeHandle = 0 THEN + out_connected := FALSE; + out_connectionState := 0; + RETURN; +END_IF; +rc := nativeCall(5); +IF rc = 256 THEN + nativeHandle := 0; + IF nativeActiveRequest <> 0 THEN + nativeActiveRequest^.nativeFinish(10); + END_IF; + nativeActiveRequest := 0; nativeActiveIndex := -1; + RETURN; +ELSIF rc <> 0 THEN + out_lastNativeError := rc; + RETURN; +END_IF; +out_connectionState := nativeStatus[1]; +out_connected := (nativeStatus[1] >= 3) AND (nativeStatus[1] <= 5); +out_lastNativeError := nativeStatus[6]; +out_socketError := nativeStatus[7]; +out_reconnectCount := nativeStatus[11]; +IF nativePrevConnected AND NOT out_connected THEN + setAllRequestsOffline(); +END_IF; +nativePrevConnected := out_connected; +nativeFinishedIndex := -1; +nativeResultReady := (nativeActiveRequest <> 0) AND + ((nativeStatus[3] <> 0) OR (nativeStatus[4] <> 0)); +IF nativeResultReady AND (nativeStatus[3] <> 0) AND (nativeStatus[18] <= 4) THEN + rc := nativeCall(7); + IF rc <> 0 THEN + nativeStatus[3] := 0; + nativeStatus[4] := 1; + nativeStatus[5] := rc; + END_IF; +END_IF; +processAllRequests(); +IF nativeResultReady THEN + nativeFinishedIndex := nativeActiveIndex; + nativeCall(10); + nativeActiveRequest := 0; nativeActiveIndex := -1; + nativeSelected := 0; nativeResultReady := FALSE; +END_IF; +IF nativeEnable AND nativeStatus[1] = 3 AND nativeActiveRequest = 0 THEN + best := -1; priority := 32767; age := 0; + FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[j] <> 0 AND_THEN requests[j]^.stats.active + AND NOT requests[j]^.nativeWriteHold AND j <> nativeFinishedIndex THEN + IF requests[j]^.stats.sinceLastExec >= TIME_TO_UDINT(requests[j]^.in_minDelay) THEN + IF (requests[j]^.in_priorityCurrent < priority) OR + ((requests[j]^.in_priorityCurrent = priority) AND + (requests[j]^.stats.sinceLastExec > age)) THEN + best := j; priority := requests[j]^.in_priorityCurrent; + age := requests[j]^.stats.sinceLastExec; + END_IF; + END_IF; + END_IF; + END_FOR; + IF best >= 0 THEN + IF priority > 0 THEN + FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[j]^.in_priorityCurrent > 0 THEN + requests[j]^.in_priorityCurrent := requests[j]^.in_priorityCurrent - 1; + END_IF; + END_FOR; + END_IF; + nativeSelected := requests[best]; nativeActiveIndex := best; + nativeSelected^.stats.execute := TRUE; + nativeSelected^.processRequest(); + IF nativeActiveRequest = 0 THEN + nativeActiveIndex := -1; nativeSelected := 0; + END_IF; + END_IF; +END_IF; +allRequestsOnline := (activeReqCnt > 0) AND (onlineCnt = activeReqCnt); +allRequestsOffline := (activeReqCnt > 0) AND (onlineCnt = 0); +numMsgSent := nativeSent; numMsgReply := nativeReplied; +FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + reqStats[j] := ADR(requests[j]^.stats); +END_FOR; +IF nativeWindowStart = 0 THEN nativeWindowStart := nativeNowMs; END_IF; +IF nativeNowMs - nativeWindowStart >= 1000 THEN + msgLastSecondCnt := nativeWindowCount; msgLastSecondTime := nativeWindowTime; + nativeWindowCount := 0; nativeWindowTime := 0; nativeWindowStart := nativeNowMs; +END_IF; + +(* atMbClientTCP/nativeRelease -- Interface *) +METHOD nativeRelease : BOOL +VAR + j : DINT; +END_VAR + +(* atMbClientTCP/nativeRelease -- Implementation *) +IF nativeHandle <> 0 THEN + nativeCall(2); + IF nativeActiveRequest <> 0 THEN + nativeActiveRequest^.nativeFinish(10); + END_IF; + nativeHandle := 0; +END_IF; +nativeActiveRequest := 0; nativeSelected := 0; nativeActiveIndex := -1; +nativeResultReady := FALSE; +out_connected := FALSE; out_connectionState := 0; +setAllRequestsOffline(); +FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[j]^.invalidateNativeData(); +END_FOR; +nativeRelease := TRUE; + +(* atMbClientTCP/release -- Interface *) +METHOD release : BOOL + +(* atMbClientTCP/release -- Implementation *) +in_enable := FALSE; +release := nativeRelease(); + +(* atMbClientTCP/FB_exit -- Interface *) +METHOD FB_exit : BOOL +VAR_INPUT + bInCopyCode : BOOL; +END_VAR + +(* atMbClientTCP/FB_exit -- Implementation *) +FB_exit := nativeRelease(); + +(* atMbClientTCP/FB_reinit -- Interface *) +METHOD FB_reinit : BOOL +VAR + j : DINT; +END_VAR + +(* atMbClientTCP/FB_reinit -- Implementation *) +nativeHandle := 0; +nativeActiveRequest := 0; nativeSelected := 0; nativeActiveIndex := -1; +nativeResultReady := FALSE; nativePrevConnected := FALSE; +requestCnt := 0; out_registrationFull := FALSE; +FOR j := 0 TO 99 DO requests[j] := 0; reqStats[j] := 0; END_FOR; +FB_reinit := TRUE; + +(* atMbReqBase/invalidateNativeData -- Interface *) +METHOD PUBLIC invalidateNativeData + +(* atMbReqBase/invalidateNativeData -- Implementation *) +out_validData := FALSE; +out_stale := TRUE; + +(* atMbReqBase/nativeFinish -- Interface *) +METHOD nativeFinish +VAR_INPUT + result : UDINT; +END_VAR +VAR + mapped : ModbusFB.Error; + ex : ModbusFB.ExceptionCodes; + duration : UDINT; +END_VAR + +(* atMbReqBase/nativeFinish -- Implementation *) +out_nativeError := result; +out_socketError := client.nativeStatus[7]; +out_writeUncertain := nativeInFlight AND (client.nativeStatus[9] <> 0); +IF (result = 10) AND nativeInFlight AND (nativeFunction >= 15) THEN + out_writeUncertain := TRUE; +END_IF; +IF out_writeUncertain AND client.nativeHoldUncertainWrites THEN + nativeWriteHold := TRUE; +END_IF; +stats.reqBusy := FALSE; +IF result = 0 THEN + duration := ULINT_TO_UDINT(client.nativeNowMs - nativeStartMs); + stats.lastValidDuration := duration; + IF duration > stats.longestValidDuration THEN stats.longestValidDuration := duration; END_IF; + client.nativeReplied := client.nativeReplied + 1; + client.nativeWindowTime := client.nativeWindowTime + duration; + stats.nativeResponseSeen := TRUE; + IF nativeFunction <= 4 THEN + nativeReadComplete := TRUE; + stats.nativeLastSuccessMs := client.nativeNowMs; + out_validData := TRUE; + END_IF; + done(TRUE); +ELSE + CASE result OF + 1,258: mapped := ModbusFB.Error.RequestParameterError; + 4,7: mapped := ModbusFB.Error.TcpSocketError; + 5: mapped := ModbusFB.Error.TcpConnectTimeout; + 6: mapped := ModbusFB.Error.ReplyTimeout; + 8: mapped := ModbusFB.Error.InvalidReply; + 9: mapped := ModbusFB.Error.RequestException; + 10: mapped := ModbusFB.Error.RequestCancelled; + ELSE mapped := ModbusFB.Error.InternalError; + END_CASE; + ex := ModbusFB.ExceptionCodes.RESPONSE_SUCCESS; + IF result = 9 THEN + CASE client.nativeStatus[8] OF + 1: ex := ModbusFB.ExceptionCodes.ILLEGAL_FUNCTION; + 2: ex := ModbusFB.ExceptionCodes.ILLEGAL_DATA_ADDRESS; + 3: ex := ModbusFB.ExceptionCodes.ILLEGAL_DATA_VALUE; + 4: ex := ModbusFB.ExceptionCodes.SLAVE_DEVICE_FAILURE; + 5: ex := ModbusFB.ExceptionCodes.ACKNOWLEDGE; + 6: ex := ModbusFB.ExceptionCodes.SLAVE_DEVICE_BUSY; + 8: ex := ModbusFB.ExceptionCodes.MEMORY_PARITY_ERROR; + 10: ex := ModbusFB.ExceptionCodes.GATEWAY_PATH_UNAVAILABLE; + 11: ex := ModbusFB.ExceptionCodes.GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND; + END_CASE; + client.nativeReplied := client.nativeReplied + 1; + stats.nativeResponseSeen := TRUE; + ELSE + stats.nativeResponseSeen := FALSE; + END_IF; + err(in := TRUE,errorId := mapped,exceptionCode := ex); +END_IF; +nativeInFlight := FALSE; +stats(); + +(* atMbReqBase/nativeProcess -- Interface *) +METHOD nativeProcess +VAR_INPUT + functionCode : BYTE; +END_VAR + +(* atMbReqBase/nativeProcess -- Implementation *) +nativeReadComplete := FALSE; +stats.reqDone := FALSE; stats.reqError := FALSE; +stats(); +IF NOT client.out_connected THEN stats.setClientOffline(); END_IF; +IF client.nativeActiveRequest = THIS AND client.nativeResultReady THEN + IF (client.nativeStatus[3] <> 0) AND (nativeFunction <= 4) AND NOT nativeCopyAllowed THEN + nativeFinish(1); + ELSE + nativeFinish(client.nativeStatus[5]); + END_IF; + RETURN; +END_IF; +IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS THEN + nativeFunction := functionCode; + nativeCount := stats.reqLength; nativeOffset := in_dataOffset; + nativeStartMs := client.nativeNowMs; + IF out_parameterError OR nativeCount = 0 THEN nativeFinish(1); RETURN; END_IF; + client.nativeArgUnit := in_unitId; + client.nativeArgFunction := functionCode; + client.nativeArgAddress := in_startAddr; + client.nativeArgQuantity := nativeCount; + client.nativeArgTimeout := MAX(UDINT#1,(replyTimeout + 999)/1000); + nativeRC := client.nativeCall(11); + IF nativeRC = 0 THEN nativeRC := client.nativeCall(6); END_IF; + IF nativeRC = 0 THEN + client.nativeActiveRequest := THIS; + nativeInFlight := TRUE; + stats.reqBusy := TRUE; + client.nativeSent := client.nativeSent + 1; + client.nativeWindowCount := client.nativeWindowCount + 1; + ELSE nativeFinish(nativeRC); + END_IF; + stats(); +END_IF; +IF out_validData THEN + out_dataAgeMs := client.nativeNowMs - stats.nativeLastSuccessMs; + out_stale := out_dataAgeMs > TIME_TO_UDINT(in_staleAfter); +ELSE out_stale := TRUE; +END_IF; + +(* atMbReqBase/FB_reinit -- Interface *) +METHOD FB_reinit : BOOL + +(* atMbReqBase/FB_reinit -- Implementation *) +nativeInFlight := FALSE; +stats.execute := FALSE; +stats.reqBusy := FALSE; +stats.reqDone := FALSE; +stats.reqError := FALSE; +FB_reinit := TRUE; diff --git a/compat/AtomModbus-Compatible-DispatchFix.export b/compat/AtomModbus-Compatible-DispatchFix.export new file mode 100644 index 0000000..cd8367c --- /dev/null +++ b/compat/AtomModbus-Compatible-DispatchFix.export @@ -0,0 +1,5531 @@ + + + + + + + True + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + 00000000-0000-0000-0000-000000000000 + 6-Comms + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + e59dc300-e37a-4c37-997b-021563304d7e + + + + + + + + 738bea1e-99bb-4f04-90bb-a7a567e74e3a + + 639034841590379670 + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + e59dc300-e37a-4c37-997b-021563304d7e + + atomLibrary + + -1 + + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + 00000000-0000-0000-0000-000000000000 + ModbusMaster + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + + + + + + + + 738bea1e-99bb-4f04-90bb-a7a567e74e3a + + 639034841590449813 + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + + atomLibrary + 6-Comms + + -1 + + + False + + f228f401-6c74-4359-8ff1-b15b675180a4 + 00000000-0000-0000-0000-000000000000 + atMbClientToString + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 2db5746d-d284-4425-9f7f-2663a34b0ebc + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841591030946 + + + + + {attribute 'qualified_only'} +{attribute 'strict'} +TYPE atMbClientToString : +( + noneOk := 0, + someOk := 1, + allOk := 2 +); +END_TYPE + + f228f401-6c74-4359-8ff1-b15b675180a4_Decl_LineIds + + + 11 + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 43f52a7a-cd45-4c0a-8652-9b18c69324d7 + 00000000-0000-0000-0000-000000000000 + atMBExceptionCodes + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 2db5746d-d284-4425-9f7f-2663a34b0ebc + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590519807 + + + + + {attribute 'qualified_only'} +// {attribute 'strict'} NOT strict so can set with modbusFB.ExceptionCodes. +TYPE atMBExceptionCodes : +( + // Local / transport-level (non-Modbus) result + TIMEOUT := -1, // Communication timeout / no response + + // Modbus exception codes (returned in exception response PDU) + RESPONSE_SUCCESS := 16#00, // Everything OK (no exception) + + ILLEGAL_FUNCTION := 16#01, // Function code not supported by slave + ILLEGAL_DATA_ADDRESS := 16#02, // Data address not valid for slave (out of range / not mapped) + ILLEGAL_DATA_VALUE := 16#03, // Data value not valid (bad length/value/etc.) + SLAVE_DEVICE_FAILURE := 16#04, // Unrecoverable error while performing the requested action + + ACKNOWLEDGE := 16#05, // Request accepted; processing started (use to avoid master timeout) + SLAVE_DEVICE_BUSY := 16#06, // Slave busy (e.g. long operation in progress); retry later + + MEMORY_PARITY_ERROR := 16#08, // Memory parity error (special case; historically for FC 20/21) + + GATEWAY_PATH_UNAVAILABLE := 16#0A, // Gateway problem: path unavailable / misconfigured / overloaded + GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND := 16#0B // Gateway problem: target device did not respond +); +END_TYPE + + 43f52a7a-cd45-4c0a-8652-9b18c69324d7_Decl_LineIds + + + 78 + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + 00000000-0000-0000-0000-000000000000 + atMbClient + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639251309599709549 + + + None + + + IF nativeMode THEN + nativeCycle(); + RETURN; +END_IF; +//Modbus connection +//Connects to a modbus slave +//2024-11-23 ATM Cleaned up code +//2024-12-23 CWD Add statistics +//2025-06-18 CWD Add Get priorities and interval working together +//2026-03-26 ATM Clean up code, more testing +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +// ===== Main function call ===== + +IF (in_resetStats) THEN + resetStats(); + in_resetStats := FALSE; +END_IF + +// ===== Connect and disconnect logic ===== + +//Timer to trigger a connection if not connected for to long +reconnectTimer( + IN := NOT client.xConnect AND NOT client.xConnected AND NOT client.xError, + PT := RECONNECT_DELAY +); + +//Timer to trigger a disconnection if connect fails +disconnectTimer( + IN:=client.xConnect AND NOT client.xConnected, + PT:=DISCONNECT_DELAY +); + +//All requests offline timer +allRequestsOfflineTON( + IN := allRequestsOffline AND client.xConnected, + PT := ALL_OFFLINE_DELAY +); + +//Destroy Client delay timer +recreateClientDly( + IN:=destroyClient, + PT:=RECREATE_CLIENT_DELAY +); + +IF disconnectTimer.Q OR allRequestsOfflineTON.Q OR client.xError OR recreateTest THEN + //All requests are offline + //Connection attempt failed so disconnect + //If Client has error then disconnect + connect := FALSE; + recreateTest := FALSE; + destroyClient := TRUE; + setAllRequestsOffline(); +ELSIF reconnectTimer.Q AND NOT destroyClient THEN +//If not connected then attempt connect + connect := TRUE; + connectCnt := connectCnt + 1; //for debugging +END_IF; + + +IF recreateClientDly.Q THEN + destroyClient := FALSE; + recreateClient(); +END_IF; + +// If some reason gets stuck at busy, (This shouldnt happen, but force a reconnect). +forceConnect( + in := connect <> client.xConnect, + pt := FORCE_RECONNECT_DELAY +); + +// Only flip status of xConnect if no current request is active. +IF (connect <> client.xConnect) AND_THEN (allRequestsComplete OR forceConnect.Q) THEN + client.xConnect := connect; +END_IF + +// Moved this lower down, so xConnect happens one scan sooner. +client(); + +// If there are no requests, dont neeed to even do anything. +IF requestCnt = 0 THEN + numMsgSent := 0; + numMsgReply := 0; + allRequestsOnline := 0; + allRequestsOffline := 0; + RETURN; +END_IF + + +requestNotExecutingTON(IN := NOT requestExecuting, PT := NEXT_REQUEST_DELAY); //Only delaying 1 ms. This may not do much, was put in during testing, but now it almost seems unnecesary + +// ===== Execute next request ===== +IF connect AND client.xConnect AND client.xConnected AND (requestNotExecutingTON.Q OR NEXT_REQUEST_DELAY = T#0MS) THEN + + //Find the lowest priority of overdue requests + nextIdx := -1; + priorityLowest := PRIORITY_NONE; + highestTimeSinceRun := 0; + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF (requests[i]^.in_priorityCurrent < priorityLowest + OR (requests[i]^.in_priorityCurrent = priorityLowest AND requests[i]^.stats.sinceLastExec > highestTimeSinceRun)) + AND requests[i]^.stats.sinceLastExec > TIME_TO_UDINT(requests[i]^.in_minDelay) THEN + priorityLowest := requests[i]^.in_priorityCurrent; + highestTimeSinceRun := requests[i]^.stats.sinceLastExec; + nextIdx := i; + END_IF + END_FOR + + //Decriment all request priorities by one. + IF priorityLowest < PRIORITY_NONE AND priorityLowest > 0 THEN //Only if priority + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.in_priorityCurrent := requests[i]^.in_priorityCurrent - 1; + IF requests[i]^.in_priorityCurrent < 0 THEN + requests[i]^.in_priorityCurrent := 0; + END_IF + END_FOR + END_IF + + IF nextIdx >= 0 THEN + activeIndex := nextIdx; + END_IF + END_IF + +processAllRequests(); + +allRequestsOnline := (activeReqCnt > 0) AND (onlineCnt = activeReqCnt); +allRequestsOffline := (activeReqCnt > 0) AND (onlineCnt = 0); + +// ===== Client statistics ===== +//Get a sum of done and error counts from all requests +reqErrSum := 0; +reqDoneSum := 0; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + reqDoneSum := reqDoneSum + requests[i]^.stats.doneCnt; + reqErrSum := reqErrSum + requests[i]^.stats.errorCnt; + + //copy stats to array for debugging + reqStats[i] := ADR(requests[i]^.stats); +END_FOR + + + +//Every time the tx message counter changes increment msg count and add last transaction time +IF client.udiNumMsgSent <> msgCountLast THEN + msgCountLast := client.udiNumMsgSent; + msgTimeSum := msgTimeSum + client.udiLastTransactionTime; + msgCountSum := msgCountSum + 1; +END_IF + +//Every 1 second compute statistics +pulse(); +IF pulse.T_Sec_OS THEN + msgLastSecondCnt := msgCountSum; + msgCountSum := 0; + msgLastSecondTime := msgTimeSum; + msgTimeSum := 0; +END_IF + +numMsgSent := client.udiNumMsgSent; +numMsgReply := client.udiNumMsgReply; + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa_Impl_LineIds + + + + + {attribute 'monitoring_display' := 'toString'} +{attribute 'no_assign'} +FUNCTION_BLOCK ABSTRACT atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_timeout: TIME := T#0MS; // If this is set, this will be used on requests, otherwise it will be calculated from in_dataRate. + {attribute 'symbol' := 'none'} + in_resetStats: BOOL; // Set this bool to true to reset statistics. +END_VAR +VAR_OUTPUT + {attribute 'symbol' := 'none'} + msgLastSecondCnt: UDINT; //Messages in the last second + {attribute 'symbol' := 'none'} + msgLastSecondTime: UDINT; //Total amount of time for all messages sent last second in ms + {attribute 'symbol' := 'none'} + numMsgSent: UDINT; + {attribute 'symbol' := 'none'} + numMsgReply: UDINT; + {attribute 'symbol' := 'none'} + allRequestsOnline: BOOL; //All requests are working + //{attribute 'symbol' := 'none'} + reqStats: ARRAY[0..99] OF POINTER TO atMbRequestStats; // For debugging modbus requests +END_VAR +VAR + {attribute 'symbol' := 'none'} + datarate: DINT := 0; //Bits per second data rate. Used to calculate reply timeout. Based on amount of time to transmit holding register read for max amount of registers. Default 154705 for Modbus TCP = 17ms to transmit 2650 bits + {attribute 'symbol' := 'none'} + client: REFERENCE TO ModbusFB.Client; // This is generated internally, no need to set. + {attribute 'symbol' := 'none'} + connectCnt: DINT; //Number of connection attempts + {attribute 'symbol' := 'none'} + initDone: BOOL; + {attribute 'symbol' := 'none'} + disconnectTimer: TON; //Connecting but not connected delay + {attribute 'symbol' := 'none'} + reconnectTimer: TON; //Not connecting and not connected delay + {attribute 'symbol' := 'none'} + msgCountLast: UDINT; //Last message count + {attribute 'symbol' := 'none'} + msgCountSum: UDINT; //Message count accumulator + {attribute 'symbol' := 'none'} + msgTimeSum: UDINT; //Amount of time for messsages accumulator + {attribute 'symbol' := 'none'} + pulse: atPulse; //Pulse generator + {attribute 'symbol' := 'none'} + requests: ARRAY[0..99] OF POINTER TO atMbReqBase; //list of request to direct + {attribute 'symbol' := 'none'} + requestCnt: UINT; //Number of active requests + {attribute 'symbol' := 'none'} + reqDoneSum: UDINT; //Count of done result on all requests + {attribute 'symbol' := 'none'} + reqErrSum: UDINT; //Count of error result on all requests + {attribute 'symbol' := 'none'} + i: DINT; //Loop variable + {attribute 'symbol' := 'none'} + nextIdx: DINT := -1; //Index of the active request + {attribute 'symbol' := 'none'} + priorityLowest: INT; //lowest request priority + {attribute 'symbol' := 'none'} + highestTimeSinceRun: UDINT; //lowest request priority + {attribute 'symbol' := 'none'} + allRequestsOfflineTON: TON; //All requests are offline delay + {attribute 'symbol' := 'none'} + requestNotExecutingTON: TON; //Delay after there are no requests execting + {attribute 'symbol' := 'none'} + recreateClientDly: TON; //Delay to make new client after force fail + {attribute 'symbol' := 'none'} + allRequestsOffline: BOOL; //All requests are offline + {attribute 'symbol' := 'none'} + connect: BOOL; // this is more of a "Request to connect, Requet to disconnect", will drop xConnect once all requests are done, wont start requests unless this is true. + {attribute 'symbol' := 'none'} + forceConnect: TON; //If after 5 seconds, all requests are still ofline, just force a reconnect. + test1: REAL; + test2: REAL; + destroyClient: BOOL; + recreateTest: BOOL; +END_VAR +VAR CONSTANT + {attribute 'symbol' := 'none'} + NEXT_REQUEST_DELAY: TIME := T#0MS; // Dont seem to need a delay + {attribute 'symbol' := 'none'} + RECONNECT_DELAY: TIME := T#500MS; + {attribute 'symbol' := 'none'} + DISCONNECT_DELAY: TIME := T#500MS; + {attribute 'symbol' := 'none'} + RECREATE_CLIENT_DELAY: TIME := T#1000MS; + {attribute 'symbol' := 'none'} + ALL_OFFLINE_DELAY: TIME := T#2S; + {attribute 'symbol' := 'none'} + FORCE_RECONNECT_DELAY: TIME := T#1S; + {attribute 'symbol' := 'none'} + PRIORITY_NONE: INT := 32767; +END_VAR + +VAR +nativeMode : BOOL; +nativeHandle : UDINT; +nativeIP : DWORD; +nativePort : UINT; +nativeNowUs : SYSTIME; +nativeNowMs : ULINT; +nativeStatus : ARRAY[0..23] OF UDINT; +nativeActiveIndex : DINT := -1; +nativeSelected : POINTER TO atMbReqBase; +nativeResultReady : BOOL; +nativeFinishedIndex : DINT := -1; +nativeCallResult : UDINT; +nativeWindowStart : ULINT; +nativePrevConnected : BOOL; +nativeEnable : BOOL := TRUE; +nativeConnectTimeout : UDINT := 2000; +nativeRetryDelay : UDINT := 1000; +nativeHoldUncertainWrites : BOOL := FALSE; +END_VAR +VAR_INPUT +{attribute 'symbol' := 'none'} +nativeIO : ARRAY[0..1999] OF WORD; +{attribute 'symbol' := 'none'} +nativeActiveRequest : POINTER TO atMbReqBase; +{attribute 'symbol' := 'none'} +nativeArgUnit : BYTE; +{attribute 'symbol' := 'none'} +nativeArgFunction : BYTE; +{attribute 'symbol' := 'none'} +nativeArgAddress : UINT; +{attribute 'symbol' := 'none'} +nativeArgQuantity : UINT; +{attribute 'symbol' := 'none'} +nativeArgTimeout : UDINT := 1000; +{attribute 'symbol' := 'none'} +nativeSent : UDINT; +{attribute 'symbol' := 'none'} +nativeReplied : UDINT; +{attribute 'symbol' := 'none'} +nativeWindowCount : UDINT; +{attribute 'symbol' := 'none'} +nativeWindowTime : UDINT; +END_VAR + +VAR_OUTPUT +out_connected : BOOL; +out_connectionState : UDINT; +out_lastNativeError : UDINT; +out_socketError : UDINT; +out_reconnectCount : UDINT; +out_registrationFull : BOOL; +END_VAR + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa_Decl_LineIds + + + 3369 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + 00000000-0000-0000-0000-000000000000 + atMbClientRTU + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649591897 + + + None + + + //Modbus connection +//Connects to a modbus slave +//2024-06-27 CWD Initial create +//2024-11-13 CWD Fix problem with not connecting properly on power up +//2024-11-15 ATM Move from structure to defaults. +//2024-11-23 ATM Cleaned up code +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +IF NOT setupClient() THEN + RETURN; +END_IF + +//Set dataRate +//This is based on an 8 byte request and a 255 byte reply +dataRate := in_baudRate; + + +// ===== Initialization ===== +// if the param changes, force a reinit +IF _client^.iPort <> in_port + OR _client^.dwBaudrate <> in_baudRate + OR _client^.byDataBits <> in_dataBits + OR _client^.eParity <> in_parity + OR _client^.eStopBits <> in_stopBits THEN + initDone := FALSE; +END_IF; + +IF NOT initDone THEN + initDone := TRUE; + + //Other configuration parameters + _client^.iPort := in_port; + _client^.dwBaudrate := in_baudRate; + _client^.byDataBits := in_dataBits; + _client^.eParity := in_parity; + _client^.eStopBits := in_stopBits; + _client^.eDTRcontrol := 0; + _client^.eRTScontrol := 0; + _client^.eRTUASCII := 0; + _client^.udiLogOptions := ModbusFB.LoggingOptions.All; + _client^.xConnect:=FALSE; + +END_IF + +SUPER^(); + 17c2ab7a-4d60-48ec-966f-339ec83a75e9_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbClientRTU EXTENDS atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_port: BYTE := 0; //0=None, 1,2,3,4 + {attribute 'symbol' := 'none'} + in_baudRate: UINT := 9600; //4800, 9600, 19200, 38400, 57600, 115200 + {attribute 'symbol' := 'none'} + in_parity: BYTE := 0; //0=None, 1=Odd, 2=Even + {attribute 'symbol' := 'none'} + in_dataBits: BYTE := 8; //8=8 + {attribute 'symbol' := 'none'} + in_stopBits: BYTE := 1; //1=OneStopBit, 2=One5StopBits, 3=TwoStopBits +END_VAR +VAR + {attribute 'symbol' := 'none'} + _client: POINTER TO ModbusFB.ClientSerial; +END_VAR + 17c2ab7a-4d60-48ec-966f-339ec83a75e9_Decl_LineIds + + + 607 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + 00000000-0000-0000-0000-000000000000 + atMbClientTCP + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649747183 + + + None + + + nativeMode := TRUE; +nativeIP := SHL(BYTE_TO_DWORD(in_ipAddress[0]),24) OR + SHL(BYTE_TO_DWORD(in_ipAddress[1]),16) OR + SHL(BYTE_TO_DWORD(in_ipAddress[2]),8) OR BYTE_TO_DWORD(in_ipAddress[3]); +nativePort := in_port; +nativeConnectTimeout := MAX(UDINT#1,TIME_TO_UDINT(in_connectTimeout)); +nativeRetryDelay := MAX(UDINT#1,TIME_TO_UDINT(in_reconnectDelay)); +nativeHoldUncertainWrites := in_holdUncertainWrites; +IF nativeHandle <> 0 AND ((nativeEndpointIP <> nativeIP) OR + (nativeEndpointPort <> nativePort) OR (nativeConfigConnect <> nativeConnectTimeout) OR + (nativeConfigRetry <> nativeRetryDelay) OR NOT in_enable) THEN + nativeRelease(); +END_IF; +nativeEndpointIP := nativeIP; nativeEndpointPort := nativePort; +nativeConfigConnect := nativeConnectTimeout; nativeConfigRetry := nativeRetryDelay; +nativeEnable := in_enable; +IF in_baudrate <> 0 THEN dataRate := in_baudrate; ELSE dataRate := 105200; END_IF; +SUPER^(); + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbClientTCP EXTENDS atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_ipAddress: ARRAY[0..3] OF BYTE; // TCP IP Address + {attribute 'symbol' := 'none'} + in_port: UINT := 502; // TCP port + {attribute 'symbol' := 'none'} + in_baudrate: UINT := 0; // Set this if using RS485 to TCP Converter, used to calculate timeout. +END_VAR +VAR + {attribute 'symbol' := 'none'} + _client: POINTER TO ModbusFB.ClientTCP; +END_VAR +VAR_INPUT +in_enable : BOOL := TRUE; +in_connectTimeout : TIME := T#2S; +in_reconnectDelay : TIME := T#1S; +in_holdUncertainWrites : BOOL := FALSE; +END_VAR + +VAR +nativeEndpointIP : DWORD; +nativeEndpointPort : UINT; +nativeConfigConnect : UDINT; +nativeConfigRetry : UDINT; +END_VAR + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570_Decl_LineIds + + + 1168 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + 00000000-0000-0000-0000-000000000000 + atMbReqBase + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649837310 + + + None + + + //Modbus request +//Makes a request using the start and quantity, puts the result into data +//2024-06-27 CWD Initial create +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage + +IF NOT __ISVALIDREF(client) THEN + RETURN; +END_IF + + +//Add this request to the clients request array +client.addRequest(THIS); + +//fault logic +//Fault if error +//errTrig(CLK := stats.reqError); +//IF errTrig.Q THEN +// out_fault := TRUE; +//END_IF +//not faulted if a succesfull request +//doneTrig(CLK := stats.reqDone); +// also clear fault if not active +//IF doneTrig.Q OR NOT stats.active THEN +// out_fault := FALSE; +//END_IF + +IF in_quantity <= dataSize THEN + stats.reqLength := in_quantity; +ELSE + stats.reqLength := dataSize; +END_IF; + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK ABSTRACT atMbReqBase +VAR_INPUT + {attribute 'symbol' := 'none'} + client: REFERENCE TO atMbClient; // Instance of atMBClient + {attribute 'symbol' := 'none'} + in_startAddr: UINT; + {attribute 'symbol' := 'none'} + in_quantity: UINT; + {attribute 'symbol' := 'none'} + in_dataOffset: UINT; // Offset position for data into array. + {attribute 'symbol' := 'none'} + in_unitId: BYTE := 0; + {attribute 'symbol' := 'none'} + in_timeout: SYSTIME := 0; // timeout in ms, if not set, will be 1ms less than interval. + {attribute 'symbol' := 'none'} + in_minDelay: TIME := T#10MS; // This is the minimum delay between queries, only relevent if high priority. + {attribute 'symbol' := 'none'} + in_prioritySetpoint: INT := 50; // this requests priority compared to other requests. Lower value is higher priority. + {attribute 'symbol' := 'none'} + in_priorityCurrent: INT; //Current priority. This is decrimented and the request is executed when at zero. + {attribute 'symbol' := 'none'} + stats: atMbRequestStats; //Request stats + +END_VAR +VAR_OUTPUT + //{attribute 'symbol' := 'none'} + //out_fault: BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + dataSize: UINT; + //{attribute 'symbol' := 'none'} + //quantUsed: UINT; + //{attribute 'symbol' := 'none'} + //doneTrig: R_TRIG; //Succesful request trigger + //{attribute 'symbol' := 'none'} + //errTrig: R_TRIG; //Error request result trigger + + {attribute 'symbol' := 'none'} + request: REFERENCE TO ModbusFB.ClientRequest;; +END_VAR + + +VAR +nativeFunction : BYTE; +nativeCount : UINT; +nativeOffset : UINT; +nativeInFlight : BOOL; +nativeReadComplete : BOOL; +nativeCopyAllowed : BOOL; +nativeWriteHold : BOOL; +nativeStartMs : ULINT; +nativeLoop : DINT; +nativeRC : UDINT; +END_VAR + +VAR_OUTPUT +out_nativeError : UDINT; +out_socketError : UDINT; +out_writeUncertain : BOOL; +out_validData : BOOL; +out_dataAgeMs : ULINT; +out_stale : BOOL := TRUE; +out_parameterError : BOOL; +END_VAR + +VAR_INPUT +in_staleAfter : TIME := T#2S; +END_VAR + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37_Decl_LineIds + + + 3210 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + 00000000-0000-0000-0000-000000000000 + atMbReqReadCoil + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146913881 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-11-16 CWD Added read coil +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + + + a77a90fd-541d-4ce9-ad97-7024224bbab4_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadCoil EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readCoilReq: POINTER TO ModbusFB.ClientRequestReadCoils; +END_VAR + + a77a90fd-541d-4ce9-ad97-7024224bbab4_Decl_LineIds + + + 193 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 1a3278f4-a658-407c-968a-21aa53361562 + 00000000-0000-0000-0000-000000000000 + atMbReqReadDiscrete + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147014015 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + 1a3278f4-a658-407c-968a-21aa53361562_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadDiscrete EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readDiscreteReq: POINTER TO ModbusFB.ClientRequestReadDiscreteInputs; +END_VAR + + 1a3278f4-a658-407c-968a-21aa53361562_Decl_LineIds + + + 184 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 73b10819-9112-4811-9583-e2735d2b13ea + 00000000-0000-0000-0000-000000000000 + atMbReqReadHolding + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147099228 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + 73b10819-9112-4811-9583-e2735d2b13ea_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadHolding EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readHoldReq: POINTER TO ModbusFB.ClientRequestReadHoldingRegisters; +END_VAR + + 73b10819-9112-4811-9583-e2735d2b13ea_Decl_LineIds + + + 173 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + 00000000-0000-0000-0000-000000000000 + atMbReqReadInput + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147179217 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + a5dbc9b0-a1a1-4281-a685-ec85b35bb866_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadInput EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readInputReq: POINTER TO ModbusFB.ClientRequestReadInputRegisters; +END_VAR + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866_Decl_LineIds + + + 167 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + 00000000-0000-0000-0000-000000000000 + atMbRequestStats + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147425423 + + + None + + + SysTimeGetUs(TSSus); + +IF (reqLength = 0) THEN + active := FALSE; + doneCnt := 0; + errorCnt := 0; + lastErrorId := modbusFB.Error.OK; + lastExceptionCode := atMBExceptionCodes.RESPONSE_SUCCESS; + execute := FALSE; + online := FALSE; + offline := FALSE; + reqDone := FALSE; + reqBusy := FALSE; + reqError := FALSE; + sinceLastExec := 0; + RETURN; +END_IF +active := TRUE; + +inDoneDeb(in_raw := reqDone, in_delOn := T#0S, in_DelOff := lightOn); +reqDoneLight := inDoneDeb.out_val; + +inBusyDeb(in_raw := reqBusy, in_delOn := T#0S, in_DelOff := lightOn); +reqBusyLight := inBusyDeb.out_val; + +inErrDeb(in_raw := reqError, in_delOn := T#0S, in_DelOff := lightOn); +reqErrorLight := inErrDeb.out_val; + +//Debounced execute indicator for debbugging +executeDeb(in_raw := _execute, in_delOn := T#0S, in_DelOff := lightOn); +executeLight := executeDeb.out_val; + + +//Set online status +IF nativeResponseSeen OR doneCntRst >=3 THEN + online := TRUE; + offline := FALSE; +END_IF + +//Set online status +IF NOT nativeResponseSeen AND errorCntRst >= 3 THEN + online := FALSE; + offline := TRUE; +END_IF + +//Record start time in ms on execute rising edge +executeTrig(CLK := execute); +IF executeTrig.Q THEN + interval := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + lastReqStart := TSSus; +END_IF + +//Offline count +offlineTrig(CLK := NOT online); +IF offlineTrig.Q THEN + offlineCnt := offlineCnt + 1; +END_IF + +//Calculate ms since last execute +//If the last request start is more then 1 minute ago then set it for 1 minute +IF TSSus >= lastReqStart THEN + IF TSSus - lastReqStart > 60000000 THEN lastReqStart := TSSus - 60000000; END_IF; +ELSE lastReqStart := TSSus; +END_IF; +sinceLastExec := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + + 48b508fd-2131-484c-ba4a-20e61e66a5b2_Impl_LineIds + + + + + {attribute 'monitoring_display' := 'toString'} +{attribute 'no_assign'} +FUNCTION_BLOCK atMbRequestStats +VAR_INPUT + lastErrorId: modbusFB.Error; //Last error id + lastExceptionCode: atMBExceptionCodes; //Last modbus exception code. These are the standard modbus exceptions. + lastValidDuration: UDINT; // How long last transaction took (ms) + longestValidDuration: UDINT; //Longest valid duration (ms) + interval: UDINT; // Start-to-start interval (ms) + offlineCnt: UINT; //Count of how many times this request has gone offline + doneCnt: UDINT; // Count of successful request + errorCnt: UDINT; // Count of failed requests + doneCntRst: UDINT; // Count of succesful requests, resets on error result + errorCntRst: UDINT; // Count of failed requests, resets on succesful request + sinceLastExec: UDINT; // Time since the last start (ms) + timeoutCnt: UDINT; + reqLength: UINT; //Number of registers requested in request, used to calc active. +END_VAR +VAR_OUTPUT + executeLight: BOOL; //Execute that stays on momentarily + reqDoneLight: BOOL; //Request done that stays on momentarily + reqErrorLight: BOOL; //Request error that stays on momentarily + reqBusyLight: BOOL; //Request busy that stays on momentarily + online: BOOL; //Online means the requests are being replied to normally or with modbus exceptions. Communications are ok. + offline: BOOL; //Opposite of online, but starts out as false on program start. + active: BOOL; // This is true if its actually being used, and not having a count of 0 on the request. + executeMbFb: BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + inDoneDeb: atDebounce; + {attribute 'symbol' := 'none'} + inErrDeb: atDebounce; + {attribute 'symbol' := 'none'} + inBusyDeb: atDebounce; + {attribute 'symbol' := 'none'} + executeDeb: atDebounce; + toString: STRING(20); + {attribute 'symbol' := 'none'} + executeTrig: R_TRIG; //Execute trigger + {attribute 'symbol' := 'none'} + offlineTrig: R_TRIG; //Offline trigger + {attribute 'symbol' := 'none'} + lastReqStart: SYSTIME; //time of start of last request + {attribute 'symbol' := 'none'} + TSSus: SYSTIME; + _execute: BOOL; //Execute trigger and status. Turn on to trigger request. Will turn off when request is complete + _reqDone: BOOL; //Request done + _reqBusy: BOOL; //Request busy + _reqError: BOOL; //Request error +END_VAR + +VAR CONSTANT + {attribute 'symbol' := 'none'} + lightOn: TIME:= T#100MS; //Light on time for debug indicators +END_VAR +VAR_INPUT +nativeResponseSeen : BOOL; +nativeLastSuccessMs : ULINT; +END_VAR + + 48b508fd-2131-484c-ba4a-20e61e66a5b2_Decl_LineIds + + + 286 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + 00000000-0000-0000-0000-000000000000 + atMbReqWriteCoil + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147259331 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + f6ee860b-0951-41d0-93d8-6436c681d8f1_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqWriteCoil EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + writeCoilsReq: POINTER TO ModbusFB.ClientRequestWriteMultipleCoils; +END_VAR + + f6ee860b-0951-41d0-93d8-6436c681d8f1_Decl_LineIds + + + 206 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + bb7e18ba-2018-4681-875e-de1f291019ef + 00000000-0000-0000-0000-000000000000 + atMbReqWriteHolding + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147339348 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + bb7e18ba-2018-4681-875e-de1f291019ef_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqWriteHolding EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + writeHoldReq: POINTER TO ModbusFB.ClientRequestWriteMultipleRegisters; +END_VAR + + bb7e18ba-2018-4681-875e-de1f291019ef_Decl_LineIds + + + 164 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 9499c60e-03a2-49d0-b086-43990a018e20 + 00000000-0000-0000-0000-000000000000 + To_IPAddress + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590489804 + + + None + + + //Convert parameters to array +To_IPAddress[0] := byte1; +To_IPAddress[1] := byte2; +To_IPAddress[2] := byte3; +To_IPAddress[3] := byte4; + + + + 9499c60e-03a2-49d0-b086-43990a018e20_Impl_LineIds + + + + + FUNCTION To_IPAddress : ARRAY[0..3] OF BYTE +VAR_INPUT + byte1: BYTE; + byte2: BYTE; + byte3: BYTE; + byte4: BYTE; +END_VAR + + 9499c60e-03a2-49d0-b086-43990a018e20_Decl_LineIds + + + 168 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 9e99e368-853f-485b-911f-d9a2a4e46434 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + addRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146468437 + + + + + IF request = 0 THEN RETURN; END_IF; +IF requestCnt > 0 THEN + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF request = requests[i] THEN RETURN; END_IF; + END_FOR; +END_IF; +IF requestCnt >= 100 THEN + out_registrationFull := TRUE; + RETURN; +END_IF; +requests[requestCnt] := request; +requestCnt := requestCnt + 1; + 9e99e368-853f-485b-911f-d9a2a4e46434_Impl_LineIds + + + + + //Adds the specified request to the global list +METHOD PUBLIC addRequest +VAR_INPUT + request: POINTER TO atMbReqBase; +END_VAR +VAR + i: UINT := 0; + +END_VAR + + 9e99e368-853f-485b-911f-d9a2a4e46434_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 685f7c69-c46a-4b34-b5e2-fb848c593f3e + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + processAllRequests + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148036583 + + + + + IF requestCnt = 0 OR requestCnt > 100 THEN RETURN; END_IF; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i] <> 0 THEN + requests[i]^.processRequest(); + END_IF; +END_FOR; + 685f7c69-c46a-4b34-b5e2-fb848c593f3e_Impl_LineIds + + + + + METHOD processAllRequests +VAR_INPUT +END_VAR +VAR + i: DINT; // Loop bstisnlr +END_VAR + 685f7c69-c46a-4b34-b5e2-fb848c593f3e_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 39769d9e-08c1-472d-82ef-25c66891a065 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + recreateAllRequests + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148056585 + + + + + IF requestCnt = 0 OR requestCnt > 100 THEN RETURN; END_IF; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i] <> 0 THEN + requests[i]^.recreateRequest(); + END_IF; +END_FOR; + 39769d9e-08c1-472d-82ef-25c66891a065_Impl_LineIds + + + + + METHOD PROTECTED recreateAllRequests +VAR_INPUT +END_VAR + +VAR + i : DINT; +END_VAR + + 39769d9e-08c1-472d-82ef-25c66891a065_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574648119824 + + + + + + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc_Impl_LineIds + + + + + METHOD ABSTRACT recreateClient +VAR_INPUT +END_VAR + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + a7e27967-573f-4f9f-86e7-dd569b8cd943 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + resetStats + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + IF nativeMode THEN + nativeSent := 0; nativeReplied := 0; + nativeWindowCount := 0; nativeWindowTime := 0; + msgLastSecondCnt := 0; msgLastSecondTime := 0; + numMsgSent := 0; numMsgReply := 0; + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.stats.resetStats(); + END_FOR; + RETURN; +END_IF; +connectCnt := 0; +val := ADR(client.udiNumMsgSent); +val^ := 0; +val := ADR(client.udiNumMsgReply); +val^ := 0; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.stats.resetStats(); +END_FOR; + a7e27967-573f-4f9f-86e7-dd569b8cd943_Impl_LineIds + + + + + METHOD resetStats +VAR_INPUT +END_VAR +VAR + i: DINT; + val: POINTER TO UDINT; +END_VAR + + + + + a7e27967-573f-4f9f-86e7-dd569b8cd943_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + ca1dfc70-ad93-43de-886d-39e56897aa6d + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + setAllRequestsOffline + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148106585 + + + + + IF requestCnt = 0 OR requestCnt > 100 THEN RETURN; END_IF; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i] <> 0 THEN + requests[i]^.stats.setClientOffline(); + END_IF; +END_FOR; + ca1dfc70-ad93-43de-886d-39e56897aa6d_Impl_LineIds + + + + + METHOD PROTECTED setAllRequestsOffline : BOOL +VAR_INPUT +END_VAR +VAR + i: DINT; +END_VAR + + + + + ca1dfc70-ad93-43de-886d-39e56897aa6d_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + d12c9bf2-320f-4e95-badb-68a2357c5783 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + setupClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574648239962 + + + + + IF nativeMode THEN + setupClient := nativeHandle <> 0; + RETURN; +END_IF; +// Only create request if doesnt exist +IF NOT __ISVALIDREF(client) THEN + recreateClient(); + IF NOT __ISVALIDREF(client) THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; + setupClient := FALSE; + RETURN; + END_IF +END_IF + +setupClient := TRUE; + d12c9bf2-320f-4e95-badb-68a2357c5783_Impl_LineIds + + + + + METHOD setupClient : BOOL +VAR_INPUT +END_VAR + + d12c9bf2-320f-4e95-badb-68a2357c5783_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + activeIndex + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590625089 + + + + + PROPERTY activeIndex : DINT + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32_Decl_LineIds + + + 1 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + activeReqCnt + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639108502146293051 + + + + + PROPERTY PRIVATE activeReqCnt : UINT + 456b4953-57c9-4f76-ab5b-0db456dd9b58_Decl_LineIds + + + 1 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 4d9d378a-f256-4f23-b835-539ee9e287c3 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + allRequestsComplete + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639108502147921016 + + + + + {attribute 'symbol' := 'none'} +PROPERTY PRIVATE allRequestsComplete : BOOL + 4d9d378a-f256-4f23-b835-539ee9e287c3_Decl_LineIds + + + 2 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 89742962-f9b6-46c2-b9a5-b7e298f8495d + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + onlineCnt + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590755417 + + + + + {attribute 'symbol' := 'none'} +PROPERTY onlineCnt : DINT//Count of requests that are online + 89742962-f9b6-46c2-b9a5-b7e298f8495d_Decl_LineIds + + + 3 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 27292866-0bc0-4848-a965-64c51a0bf960 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + requestExecuting + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590845466 + + + + + {attribute 'symbol' := 'none'} +PROPERTY PRIVATE requestExecuting : BOOL //There is at least one request executing + 27292866-0bc0-4848-a965-64c51a0bf960_Decl_LineIds + + + 3 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 4950a3a1-f54f-434d-9252-b61a6cb4663d + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649617058 + + + + + old_client_id := _client; +_client := __NEW(ModbusFB.ClientSerial); +IF _client = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; +END_IF; +client REF= _client^; +connect := FALSE; + +IF old_client_id <> 0 THEN + __DELETE(old_client_id); + old_client_id := 0; +END_IF + +recreateAllRequests(); + 4950a3a1-f54f-434d-9252-b61a6cb4663d_Impl_LineIds + + + + + METHOD recreateClient +VAR_INPUT + old_client_id: POINTER TO ModbusFB.ClientSerial; +END_VAR + + 4950a3a1-f54f-434d-9252-b61a6cb4663d_Decl_LineIds + + + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + + -1 + + + False + + e8c6c80b-7bdb-4108-987e-4af107de4377 + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + toString + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639207574650142720 + + + + + {attribute 'symbol' := 'none'} +PROPERTY toString : atMbClientToString //Client request status noneOk = no requests working, someOk = some requests are working some are failing, allOk = all requests are working + e8c6c80b-7bdb-4108-987e-4af107de4377_Decl_LineIds + + + 3 + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + + -1 + + + False + + 79dfe91d-6dbc-46b0-866c-4b880e44e02b + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649757233 + + + + + nativeRelease(); +// Next scan creates a fresh native context with current endpoint settings. + 79dfe91d-6dbc-46b0-866c-4b880e44e02b_Impl_LineIds + + + + + METHOD recreateClient +VAR +END_VAR + 79dfe91d-6dbc-46b0-866c-4b880e44e02b_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + a77f3143-e2cf-48a4-bafd-a0c18a11faeb + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + busy + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148176670 + + + + + stats.reqBusy := in; + +IF NOT in THEN + RETURN; +END_IF; + a77f3143-e2cf-48a4-bafd-a0c18a11faeb_Impl_LineIds + + + + + METHOD busy : BOOL +VAR_INPUT + in: BOOL; +END_VAR +VAR + executeTime: ULINT; + replyTimeoutTime: ULINT; +END_VAR + + + a77f3143-e2cf-48a4-bafd-a0c18a11faeb_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 5350a8fa-8297-42db-b185-fcb966e45684 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + completeRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148196661 + + + + + done(request.xDone); +err(in := request.xError, errorId := request.eErrorId, exceptionCode := request.eException); +busy(in := request.xBusy); +stats(); + +IF (atGVL.reset) THEN + // request.AbortModel(); + request.ResetModel(TRUE); +END_IF + 5350a8fa-8297-42db-b185-fcb966e45684_Impl_LineIds + + + + + METHOD completeRequest +VAR_INPUT +END_VAR + + 5350a8fa-8297-42db-b185-fcb966e45684_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + done + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148206667 + + + + + stats.reqDone := in; + +IF NOT in THEN + RETURN; +END_IF + +//Set priority for next request +in_priorityCurrent := in_prioritySetpoint; + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac_Impl_LineIds + + + + + METHOD done +VAR_INPUT + // Done state + in: BOOL; +END_VAR + + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + err + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148226656 + + + + + stats.reqError := in; + +IF NOT in THEN + RETURN; +END_IF + + +//Update error info +stats.lastErrorId := errorId; +stats.lastExceptionCode := ANY_TO_BYTE(exceptionCode); + +//Since we are here there was an exception. If the modbus response is 0, that means no modbus communication happened, so we are offline. If we would have had a succesful communication we would have got a actual modbus error. +//IF stats.lastExceptionCode = atMBExceptionCodes.RESPONSE_SUCCESS THEN + //Since there was no response from the device set the priority to higher number priority +// in_priorityCurrent := in_prioritySetPoint * 5; +//END_IF + +//Set priority for next request, priority gets higher after each error result +priorityMultiplier := UDINT_TO_INT(stats.errorCntRst); +IF priorityMultiplier > 3 THEN + priorityMultiplier := 3; +END_IF +// set multiplier to exponential retry. +in_priorityCurrent := 10 * priorityMultiplier * priorityMultiplier; +//in_priorityCurrent := in_prioritySetpoint * priorityMultiplier; + +// NO idea what Reset Model does, hoping it fixes comms issues +// request.ResetModel(TRUE); // I dont think this helped + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c_Impl_LineIds + + + + + METHOD err +VAR_INPUT + // Error state + in: BOOL; + errorId: modbusFB.Error; //modbusFB error ID + exceptionCode: modbusFB.ExceptionCodes; //ModbusFB exception code +END_VAR +VAR + priorityMultiplier: INT; //Raise priority when not response +END_VAR + + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 109d8e00-bf23-4306-a8d5-764aa6a03235 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148246723 + + + + + + 109d8e00-bf23-4306-a8d5-764aa6a03235_Impl_LineIds + + + + + METHOD ABSTRACT processRequest +VAR_INPUT +END_VAR + + 109d8e00-bf23-4306-a8d5-764aa6a03235_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148266663 + + + + + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8_Impl_LineIds + + + + + METHOD ABSTRACT recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + db40c392-b461-4198-851d-85857ed98ccf + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + setupClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148276644 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + setupClient := FALSE; // Native path has no ModbusFB request object. + RETURN; +END_IF; +// Only create request if doesnt exist +IF in_request = 0 THEN + in_request := recreateRequest(); + IF in_request = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; + setupClient := FALSE; + RETURN; + END_IF +END_IF + +IF NOT __ISVALIDREF(mbsClient) THEN + setupClient := FALSE; + RETURN; +END_IF + +request REF= in_request^; + +setupClient := TRUE; +RETURN; + db40c392-b461-4198-851d-85857ed98ccf_Impl_LineIds + + + + + METHOD setupClient : BOOL +VAR_INPUT + in_request: POINTER TO ModbusFB.ClientRequest; +END_VAR + + db40c392-b461-4198-851d-85857ed98ccf_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 6159c037-e399-466d-8108-0c4f6570f9b2 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + mbsClient + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639207574650298004 + + + + + PROPERTY PROTECTED mbsClient : REFERENCE TO ModbusFB.Client + 6159c037-e399-466d-8108-0c4f6570f9b2_Decl_LineIds + + + 1 + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + replyTimeout + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841591196333 + + + + + //Calculates the reply timout setting +PROPERTY PROTECTED replyTimeout : UDINT + 4f361885-f95c-4244-84d6-8c3b6b9b8a18_Decl_LineIds + + + 2 + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + ea5277e9-2ae8-417f-a88d-b571902c7d71 + a77a90fd-541d-4ce9-ad97-7024224bbab4 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639163341699321592 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(1); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := client.nativeIO[nativeLoop] <> 0; + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readCoilReq) THEN + RETURN; +END_IF + +//Do request processing +readCoilReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + ea5277e9-2ae8-417f-a88d-b571902c7d71_Impl_LineIds + + + + + METHOD processRequest + + ea5277e9-2ae8-417f-a88d-b571902c7d71_Decl_LineIds + + + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadCoil + + -1 + + + False + + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3 + a77a90fd-541d-4ce9-ad97-7024224bbab4 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148331930 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readCoilReq <> 0 THEN + __DELETE(readCoilReq); + readCoilReq := 0; +END_IF +readCoilReq := __NEW(ModbusFB.ClientRequestReadCoils); +recreateRequest := readCoilReq; + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3_Decl_LineIds + + + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadCoil + + -1 + + + False + + 9df094f9-a173-4520-816f-7badcbb1bd34 + 1a3278f4-a658-407c-968a-21aa53361562 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639163341699461941 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(2); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := client.nativeIO[nativeLoop] <> 0; + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readDiscreteReq) THEN + RETURN; +END_IF + +//Do request processing +readDiscreteReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 9df094f9-a173-4520-816f-7badcbb1bd34_Impl_LineIds + + + + + METHOD processRequest + + 9df094f9-a173-4520-816f-7badcbb1bd34_Decl_LineIds + + + + 1a3278f4-a658-407c-968a-21aa53361562 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadDiscrete + + -1 + + + False + + d34866d4-1134-4650-b3d6-2e19d2fd09d1 + 1a3278f4-a658-407c-968a-21aa53361562 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148362063 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readDiscreteReq <> 0 THEN + __DELETE(readDiscreteReq); + readDiscreteReq := 0; +END_IF +readDiscreteReq := __NEW(ModbusFB.ClientRequestReadDiscreteInputs); +recreateRequest := readDiscreteReq; + d34866d4-1134-4650-b3d6-2e19d2fd09d1_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + d34866d4-1134-4650-b3d6-2e19d2fd09d1_Decl_LineIds + + + + 1a3278f4-a658-407c-968a-21aa53361562 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadDiscrete + + -1 + + + False + + 7b978176-d206-4509-b9d8-7908d33a9a74 + 73b10819-9112-4811-9583-e2735d2b13ea + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148402056 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(3); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := WORD_TO_UINT(client.nativeIO[nativeLoop]); + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readHoldReq) THEN + RETURN; +END_IF + +//Do request processing +readHoldReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + // udiReplyTimeout := 1 * 1000 * 1000, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 7b978176-d206-4509-b9d8-7908d33a9a74_Impl_LineIds + + + + + METHOD processRequest + + 7b978176-d206-4509-b9d8-7908d33a9a74_Decl_LineIds + + + + 73b10819-9112-4811-9583-e2735d2b13ea + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadHolding + + -1 + + + False + + 52ffdba7-bf4a-4746-924c-8b86df6d9a83 + 73b10819-9112-4811-9583-e2735d2b13ea + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148422060 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readHoldReq <> 0 THEN + __DELETE(readHoldReq); + readHoldReq := 0; +END_IF +readHoldReq := __NEW(ModbusFB.ClientRequestReadHoldingRegisters); +recreateRequest := readHoldReq; + 52ffdba7-bf4a-4746-924c-8b86df6d9a83_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 52ffdba7-bf4a-4746-924c-8b86df6d9a83_Decl_LineIds + + + + 73b10819-9112-4811-9583-e2735d2b13ea + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadHolding + + -1 + + + False + + 1004c837-fbef-45ee-986d-977343f4bd8f + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148442038 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(4); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := WORD_TO_UINT(client.nativeIO[nativeLoop]); + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readInputReq) THEN + RETURN; +END_IF + +//Do request processing +readInputReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 1004c837-fbef-45ee-986d-977343f4bd8f_Impl_LineIds + + + + + METHOD processRequest + + 1004c837-fbef-45ee-986d-977343f4bd8f_Decl_LineIds + + + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadInput + + -1 + + + False + + 9903b949-e1b9-4e0b-a98f-b6f745ad3605 + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148462074 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readInputReq <> 0 THEN + __DELETE(readInputReq); + readInputReq := 0; +END_IF +readInputReq := __NEW(ModbusFB.ClientRequestReadInputRegisters); +recreateRequest := readInputReq; + 9903b949-e1b9-4e0b-a98f-b6f745ad3605_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 9903b949-e1b9-4e0b-a98f-b6f745ad3605_Decl_LineIds + + + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadInput + + -1 + + + False + + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + resetStats + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148542250 + + + + + doneCnt := 0; +doneCntRst := 0; +errorCnt := 0; +errorCntRst := 0; + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec_Impl_LineIds + + + + + METHOD resetStats +VAR_INPUT +END_VAR + + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + b1508d1f-b7a7-40b5-b74f-3091afc5b834 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + setClientOffline + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148552305 + + + + + nativeResponseSeen := FALSE; +doneCntRst := 0; +online := FALSE; +offline := TRUE; + b1508d1f-b7a7-40b5-b74f-3091afc5b834_Impl_LineIds + + + + + METHOD setClientOffline : BOOL +VAR_INPUT +END_VAR + + b1508d1f-b7a7-40b5-b74f-3091afc5b834_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 1c6ab99e-dc71-45a2-a024-45cf65b9af99 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + setToString + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148572279 + + + + + toString := CONCAT('OK:', CONCAT(TO_STRING(doneCnt), CONCAT('/Er:', TO_STRING(errorCnt)))); + 1c6ab99e-dc71-45a2-a024-45cf65b9af99_Impl_LineIds + + + + + METHOD PRIVATE setToString : BOOL +VAR_INPUT +END_VAR + + 1c6ab99e-dc71-45a2-a024-45cf65b9af99_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + execute + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043399503 + + + + + PROPERTY execute : BOOL + 89434b6a-7e3b-4095-9220-0a6fc3789a25_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqBusy + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043449644 + + + + + PROPERTY reqBusy : BOOL + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqDone + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043519638 + + + + + PROPERTY reqDone : BOOL + 80965ce1-9b6a-44d3-b272-a5c739ccbc70_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqError + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043604994 + + + + + PROPERTY reqError : BOOL + 30645d91-91c6-42eb-8399-c20f91ca0d3e_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + fbb41e61-f245-4f4e-bcd0-218ede01fcda + f6ee860b-0951-41d0-93d8-6436c681d8f1 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148472030 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS AND NOT out_parameterError THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(stats.reqLength)-1 DO + client.nativeIO[nativeLoop] := BOOL_TO_WORD(data[UINT_TO_DINT(in_dataOffset)+nativeLoop]); + END_FOR; + END_IF; + nativeProcess(15); + RETURN; +END_IF; +IF NOT setupClient(writeCoilsReq) THEN + RETURN; +END_IF + +//Do request processing +writeCoilsReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + fbb41e61-f245-4f4e-bcd0-218ede01fcda_Impl_LineIds + + + + + METHOD processRequest + + fbb41e61-f245-4f4e-bcd0-218ede01fcda_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + a861d6e2-2501-43dc-bb37-34509923450c + f6ee860b-0951-41d0-93d8-6436c681d8f1 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148502269 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF writeCoilsReq <> 0 THEN + __DELETE(writeCoilsReq); + writeCoilsReq := 0; +END_IF +writeCoilsReq := __NEW(ModbusFB.ClientRequestWriteMultipleCoils); +recreateRequest := writeCoilsReq; + a861d6e2-2501-43dc-bb37-34509923450c_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + a861d6e2-2501-43dc-bb37-34509923450c_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + df9b4fd1-9d38-4a54-a7e5-264a94a756ac + f6ee860b-0951-41d0-93d8-6436c681d8f1 + writeNow + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591456658 + + + + + nativeWriteHold := FALSE; +out_writeUncertain := FALSE; +in_priorityCurrent := 0; + df9b4fd1-9d38-4a54-a7e5-264a94a756ac_Impl_LineIds + + + + + METHOD writeNow : BOOL +VAR_INPUT +END_VAR + + df9b4fd1-9d38-4a54-a7e5-264a94a756ac_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + 76621df2-a77c-4e0a-aba0-e3e4a53bd278 + bb7e18ba-2018-4681-875e-de1f291019ef + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148512269 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS AND NOT out_parameterError THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(stats.reqLength)-1 DO + client.nativeIO[nativeLoop] := UINT_TO_WORD(data[UINT_TO_DINT(in_dataOffset)+nativeLoop]); + END_FOR; + END_IF; + nativeProcess(16); + RETURN; +END_IF; +IF NOT setupClient(writeHoldReq) THEN + RETURN; +END_IF + +//Do request processing +writeHoldReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 76621df2-a77c-4e0a-aba0-e3e4a53bd278_Impl_LineIds + + + + + METHOD processRequest + + 76621df2-a77c-4e0a-aba0-e3e4a53bd278_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + bd7ff184-1747-404c-9fa0-1b728376c927 + bb7e18ba-2018-4681-875e-de1f291019ef + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148532258 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF writeHoldReq <> 0 THEN + __DELETE(writeHoldReq); + writeHoldReq := 0; +END_IF +writeHoldReq := __NEW(ModbusFB.ClientRequestWriteMultipleRegisters); +recreateRequest := writeHoldReq; + bd7ff184-1747-404c-9fa0-1b728376c927_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + bd7ff184-1747-404c-9fa0-1b728376c927_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + 5f9160f9-c4a4-4f65-a74c-2838020397f3 + bb7e18ba-2018-4681-875e-de1f291019ef + writeNow + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591496600 + + + + + nativeWriteHold := FALSE; +out_writeUncertain := FALSE; +in_priorityCurrent := 0; + 5f9160f9-c4a4-4f65-a74c-2838020397f3_Impl_LineIds + + + + + METHOD writeNow : BOOL +VAR_INPUT +END_VAR + + 5f9160f9-c4a4-4f65-a74c-2838020397f3_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590675071 + + + + + IF nativeMode THEN + activeIndex := nativeActiveIndex; + RETURN; +END_IF; +// ===== Test for any requests executing ===== +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i]^.stats.execute OR requests[i]^.stats.reqBusy THEN + activeIndex := i; + RETURN; + END_IF +END_FOR +activeIndex := -1; + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c_Impl_LineIds + + + + + VAR + i: DINT; //Loop variable +END_VAR + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c_Decl_LineIds + + + True + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeIndex + + -1 + + + False + + 3b82f264-e41e-461d-9beb-263305a22a8d + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590705101 + + + + + // set request to active, which is how the active index gets calculated. +requests[activeIndex]^.stats.execute := TRUE; + 3b82f264-e41e-461d-9beb-263305a22a8d_Impl_LineIds + + + + + VAR +END_VAR + + 3b82f264-e41e-461d-9beb-263305a22a8d_Decl_LineIds + + + False + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeIndex + + -1 + + + False + + cc024b23-e5fe-4958-ad80-513feea6aeab + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146383170 + + + + + cnt := 0; +IF requestCnt = 0 THEN + activeReqCnt := 0; + RETURN; +END_IF +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + //Test for any offline request + IF requests[i]^.stats.active THEN + cnt := cnt + 1; + END_IF +END_FOR; +activeReqCnt := cnt; + cc024b23-e5fe-4958-ad80-513feea6aeab_Impl_LineIds + + + + + VAR + i: UINT; //Loop variable + cnt: UINT; +END_VAR + + cc024b23-e5fe-4958-ad80-513feea6aeab_Decl_LineIds + + + True + + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeReqCnt + + -1 + + + False + + d59f404d-315d-4ea4-b16b-c8f0e8342d2e + 4d9d378a-f256-4f23-b835-539ee9e287c3 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147990941 + + + + + IF requestCnt = 0 THEN + allRequestsComplete := 0; + RETURN; +END_IF +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + //Test for any offline request + IF requests[i]^.stats.execute OR requests[i]^.stats.reqDone OR requests[i]^.stats.reqBusy OR requests[i]^.stats.reqError THEN + allRequestsComplete := FALSE; + RETURN; + END_IF +END_FOR; +allRequestsComplete := TRUE; + d59f404d-315d-4ea4-b16b-c8f0e8342d2e_Impl_LineIds + + + + + VAR + i: UINT; +END_VAR + + + + + d59f404d-315d-4ea4-b16b-c8f0e8342d2e_Decl_LineIds + + + True + + 4d9d378a-f256-4f23-b835-539ee9e287c3 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + allRequestsComplete + + -1 + + + False + + dceb6b17-86bf-4543-9652-2fd2b943953c + 89742962-f9b6-46c2-b9a5-b7e298f8495d + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146538436 + + + + + cnt := 0; +IF requestCnt = 0 THEN + onlineCnt := 0; + RETURN; +END_IF +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + //Test for any offline request + IF requests[i]^.stats.online THEN + cnt := cnt + 1; + END_IF +END_FOR; +onlineCnt := cnt; + dceb6b17-86bf-4543-9652-2fd2b943953c_Impl_LineIds + + + + + VAR + i: UINT; //Loop variable + cnt: UINT; +END_VAR + + dceb6b17-86bf-4543-9652-2fd2b943953c_Decl_LineIds + + + True + + 89742962-f9b6-46c2-b9a5-b7e298f8495d + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + onlineCnt + + -1 + + + False + + 0006696f-29e2-441e-a325-e3481d025cec + 27292866-0bc0-4848-a965-64c51a0bf960 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590890993 + + + + + // ===== Test for any requests executing ===== +requestExecuting := activeIndex <> -1; + 0006696f-29e2-441e-a325-e3481d025cec_Impl_LineIds + + + + + VAR + i: DINT; //Loop variable +END_VAR + + 0006696f-29e2-441e-a325-e3481d025cec_Decl_LineIds + + + True + + 27292866-0bc0-4848-a965-64c51a0bf960 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + requestExecuting + + -1 + + + False + + 3d4da579-672d-4758-9b0f-e055a08e0da3 + e8c6c80b-7bdb-4108-987e-4af107de4377 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574650278010 + + + + + //Calculation for tostring +IF onlineCnt = requestCnt THEN + toString := atMbClientToString.allOk; +ELSIF onlineCnt = 0 THEN + toString := atMbClientToString.noneOk; +ELSE + toString := atMbClientToString.someOk; +END_IF + 3d4da579-672d-4758-9b0f-e055a08e0da3_Impl_LineIds + + + + + + 3d4da579-672d-4758-9b0f-e055a08e0da3_Decl_LineIds + + + True + + e8c6c80b-7bdb-4108-987e-4af107de4377 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + toString + + -1 + + + False + + 4895a5bf-c631-4f8c-bee7-addb0b769548 + 6159c037-e399-466d-8108-0c4f6570f9b2 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574650328003 + + + + + IF NOT __ISVALIDREF(client) OR_ELSE NOT __ISVALIDREF(client.client) THEN + RETURN; +END_IF + +mbsClient REF= client.client; + 4895a5bf-c631-4f8c-bee7-addb0b769548_Impl_LineIds + + + + + VAR +END_VAR + + 4895a5bf-c631-4f8c-bee7-addb0b769548_Decl_LineIds + + + True + + 6159c037-e399-466d-8108-0c4f6570f9b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + mbsClient + + -1 + + + False + + f54a4cd6-8540-4892-a914-4aac4f8d9018 + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591226460 + + + + + +IF in_timeout > 0 THEN + //Explicitly set timout + replyTimeout := ANY_TO_UDINT((in_timeout) * 1000); +ELSE + // If client timeout is set, use that. + IF client.in_timeout <> T#0MS THEN + replyTimeout := TIME_TO_UDINT(client.in_timeout) * 1000; + ELSIF client.dataRate <> 0 THEN + //Data transmision time * 1.5 to get the timeout. Result is in ms + //8 byte request, then 255 byte reply = 2630 bits. + replyTimeout := REAL_TO_UDINT(2630 / DINT_TO_REAL(client.dataRate) * 1.5 * 1000000); + ELSE + //In case client datarate = zero + replyTimeout := 1000000; //1 second? + END_IF + + +END_IF + + + f54a4cd6-8540-4892-a914-4aac4f8d9018_Impl_LineIds + + + + + VAR +END_VAR + + f54a4cd6-8540-4892-a914-4aac4f8d9018_Decl_LineIds + + + True + + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + replyTimeout + + -1 + + + False + + 465bf80a-0d6e-4363-ba1e-dd04aef379f7 + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043419500 + + + + + execute := _execute; + 465bf80a-0d6e-4363-ba1e-dd04aef379f7_Impl_LineIds + + + + + VAR +END_VAR + + 465bf80a-0d6e-4363-ba1e-dd04aef379f7_Decl_LineIds + + + True + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + execute + + -1 + + + False + + 8ff5fbe7-c047-436d-923c-942d0d66494c + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043439650 + + + + + IF reqDone OR reqError THEN + _execute := FALSE; + RETURN; +END_IF +_execute := execute; + 8ff5fbe7-c047-436d-923c-942d0d66494c_Impl_LineIds + + + + + VAR +END_VAR + + 8ff5fbe7-c047-436d-923c-942d0d66494c_Decl_LineIds + + + False + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + execute + + -1 + + + False + + db10f23e-d96b-4fd5-aec0-8b530ad288e1 + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043479636 + + + + + reqBusy := _reqBusy; + db10f23e-d96b-4fd5-aec0-8b530ad288e1_Impl_LineIds + + + + + VAR +END_VAR + + db10f23e-d96b-4fd5-aec0-8b530ad288e1_Decl_LineIds + + + True + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqBusy + + -1 + + + False + + a239da3a-8f75-41da-a264-f06ca07143f4 + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043499661 + + + + + _reqBusy := reqBusy; + a239da3a-8f75-41da-a264-f06ca07143f4_Impl_LineIds + + + + + VAR +END_VAR + + a239da3a-8f75-41da-a264-f06ca07143f4_Decl_LineIds + + + False + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqBusy + + -1 + + + False + + 61db378f-6029-4712-b562-fea707e5698b + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043539625 + + + + + reqDone := _reqDone; + 61db378f-6029-4712-b562-fea707e5698b_Impl_LineIds + + + + + VAR +END_VAR + + 61db378f-6029-4712-b562-fea707e5698b_Decl_LineIds + + + True + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqDone + + -1 + + + False + + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3 + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147505410 + + + + + _reqDone := reqDone; + +IF (execute AND reqDone) THEN + // ===== Request has just completed succesfully ===== + //Update last valid duration + lastValidDuration := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + IF lastValidDuration > longestValidDuration THEN + longestValidDuration := lastValidDuration; + END_IF + //Counters + doneCnt := doneCnt + 1; + doneCntRst := doneCntRst + 1; + errorCntRst := 0; + + //Update error info + lastErrorId := modbusFB.Error.OK; + lastExceptionCode := atMBExceptionCodes.RESPONSE_SUCCESS; + + execute := FALSE; + setToString(); +END_IF + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3_Impl_LineIds + + + + + VAR +END_VAR + + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3_Decl_LineIds + + + False + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqDone + + -1 + + + False + + e44e83bd-8f84-4202-9ab4-784d09cf5506 + 30645d91-91c6-42eb-8399-c20f91ca0d3e + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043645000 + + + + + reqError := _reqError; + e44e83bd-8f84-4202-9ab4-784d09cf5506_Impl_LineIds + + + + + VAR +END_VAR + + e44e83bd-8f84-4202-9ab4-784d09cf5506_Decl_LineIds + + + True + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqError + + -1 + + + False + + 89b5f7bc-0c37-43f3-8984-14034b000d89 + 30645d91-91c6-42eb-8399-c20f91ca0d3e + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147595513 + + + + + _reqError := reqError; + +IF (execute AND reqError) THEN + // ===== Request has just completed with error ===== + // Counters + errorCnt := errorCnt + 1; + errorCntRst := errorCntRst + 1; + doneCntRst := 0; + //Turn off execute + execute := FALSE; + + //Since we are here there was an exception. If the modbus response is 0, that means no modbus communication happened, so we are offline. If we would have had a succesful communication we would have got a actual modbus error. + IF lastExceptionCode = atMBExceptionCodes.RESPONSE_SUCCESS THEN + timeoutCnt := timeoutCnt + 1; + lastExceptionCode := atMBExceptionCodes.TIMEOUT; + END_IF + setToString(); +END_IF + 89b5f7bc-0c37-43f3-8984-14034b000d89_Impl_LineIds + + + + + VAR +END_VAR + + 89b5f7bc-0c37-43f3-8984-14034b000d89_Decl_LineIds + + + False + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqError + + -1 + + + False + + f0be8440-a307-5b71-82c8-af27d8852e42 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + nativeCall + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeCall := CmpAtomModbus.atMbCall_cext( + command := command, handle := nativeHandle, ipAddress := nativeIP, + port := nativePort, unitId := nativeArgUnit, functionCode := nativeArgFunction, + startAddress := nativeArgAddress, quantity := nativeArgQuantity, + pData := ADR(nativeIO[0]), dataCapacity := 2000, + pStatus := ADR(nativeStatus[0]), statusCapacity := 24, nowMs := nativeNowMs, + connectTimeoutMs := nativeConnectTimeout, responseTimeoutMs := nativeArgTimeout, + retryDelayMs := nativeRetryDelay); + f0be8440-a307-5b71-82c8-af27d8852e42_Impl_LineIds + + + + + METHOD nativeCall : UDINT +VAR_INPUT + command : UDINT; +END_VAR + f0be8440-a307-5b71-82c8-af27d8852e42_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 1233447d-8796-56cf-9291-c959801d2a63 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + nativeCycle + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + SysTimeGetUs(nativeNowUs); +nativeNowMs := nativeNowUs / 1000; +IF in_resetStats THEN + resetStats(); + in_resetStats := FALSE; +END_IF; +IF nativeHandle = 0 AND nativeEnable THEN + rc := nativeCall(1); + IF rc <> 0 THEN + out_lastNativeError := rc; + RETURN; + END_IF; + nativeHandle := nativeStatus[0]; + nativeArgTimeout := 1000; + rc := nativeCall(9); + IF rc <> 0 THEN + out_lastNativeError := rc; + nativeCall(2); nativeHandle := 0; + RETURN; + END_IF; + nativeCall(3); +END_IF; +IF nativeHandle = 0 THEN + out_connected := FALSE; + out_connectionState := 0; + RETURN; +END_IF; +rc := nativeCall(5); +IF rc = 256 THEN + nativeHandle := 0; + IF nativeActiveRequest <> 0 THEN + nativeActiveRequest^.nativeFinish(10); + END_IF; + nativeActiveRequest := 0; nativeActiveIndex := -1; + RETURN; +ELSIF rc <> 0 THEN + out_lastNativeError := rc; + RETURN; +END_IF; +out_connectionState := nativeStatus[1]; +out_connected := (nativeStatus[1] >= 3) AND (nativeStatus[1] <= 5); +out_lastNativeError := nativeStatus[6]; +out_socketError := nativeStatus[7]; +out_reconnectCount := nativeStatus[11]; +IF nativePrevConnected AND NOT out_connected THEN + setAllRequestsOffline(); +END_IF; +nativePrevConnected := out_connected; +nativeFinishedIndex := -1; +nativeResultReady := (nativeActiveRequest <> 0) AND + ((nativeStatus[3] <> 0) OR (nativeStatus[4] <> 0)); +IF nativeResultReady AND (nativeStatus[3] <> 0) AND (nativeStatus[18] <= 4) THEN + rc := nativeCall(7); + IF rc <> 0 THEN + nativeStatus[3] := 0; + nativeStatus[4] := 1; + nativeStatus[5] := rc; + END_IF; +END_IF; +processAllRequests(); +IF nativeResultReady THEN + nativeFinishedIndex := nativeActiveIndex; + nativeCall(10); + nativeActiveRequest := 0; nativeActiveIndex := -1; + nativeSelected := 0; nativeResultReady := FALSE; +END_IF; +IF nativeEnable AND nativeStatus[1] = 3 AND nativeActiveRequest = 0 THEN + best := -1; priority := 32767; age := 0; + FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[j] <> 0 AND_THEN requests[j]^.stats.active + AND NOT requests[j]^.nativeWriteHold AND j <> nativeFinishedIndex THEN + IF requests[j]^.stats.sinceLastExec >= TIME_TO_UDINT(requests[j]^.in_minDelay) THEN + IF (requests[j]^.in_priorityCurrent < priority) OR + ((requests[j]^.in_priorityCurrent = priority) AND + (requests[j]^.stats.sinceLastExec > age)) THEN + best := j; priority := requests[j]^.in_priorityCurrent; + age := requests[j]^.stats.sinceLastExec; + END_IF; + END_IF; + END_IF; + END_FOR; + IF best >= 0 THEN + IF priority > 0 THEN + FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[j]^.in_priorityCurrent > 0 THEN + requests[j]^.in_priorityCurrent := requests[j]^.in_priorityCurrent - 1; + END_IF; + END_FOR; + END_IF; + nativeSelected := requests[best]; nativeActiveIndex := best; + nativeSelected^.stats.execute := TRUE; + nativeSelected^.processRequest(); + IF nativeActiveRequest = 0 THEN + nativeActiveIndex := -1; nativeSelected := 0; + END_IF; + END_IF; +END_IF; +allRequestsOnline := (activeReqCnt > 0) AND (onlineCnt = activeReqCnt); +allRequestsOffline := (activeReqCnt > 0) AND (onlineCnt = 0); +numMsgSent := nativeSent; numMsgReply := nativeReplied; +FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + reqStats[j] := ADR(requests[j]^.stats); +END_FOR; +IF nativeWindowStart = 0 THEN nativeWindowStart := nativeNowMs; END_IF; +IF nativeNowMs - nativeWindowStart >= 1000 THEN + msgLastSecondCnt := nativeWindowCount; msgLastSecondTime := nativeWindowTime; + nativeWindowCount := 0; nativeWindowTime := 0; nativeWindowStart := nativeNowMs; +END_IF; + 1233447d-8796-56cf-9291-c959801d2a63_Impl_LineIds + + + + + METHOD nativeCycle +VAR + j : DINT; + best : DINT; + priority : INT; + age : UDINT; + rc : UDINT; +END_VAR + 1233447d-8796-56cf-9291-c959801d2a63_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + c08712ba-3cf5-54e9-89a1-76a1c3183975 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + nativeRelease + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + IF nativeHandle <> 0 THEN + nativeCall(2); + IF nativeActiveRequest <> 0 THEN + nativeActiveRequest^.nativeFinish(10); + END_IF; + nativeHandle := 0; +END_IF; +nativeActiveRequest := 0; nativeSelected := 0; nativeActiveIndex := -1; +nativeResultReady := FALSE; +out_connected := FALSE; out_connectionState := 0; +setAllRequestsOffline(); +FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[j]^.invalidateNativeData(); +END_FOR; +nativeRelease := TRUE; + c08712ba-3cf5-54e9-89a1-76a1c3183975_Impl_LineIds + + + + + METHOD nativeRelease : BOOL +VAR + j : DINT; +END_VAR + c08712ba-3cf5-54e9-89a1-76a1c3183975_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + 2a6d360f-01c4-5b27-81fa-337b6b33a1b6 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + release + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + in_enable := FALSE; +release := nativeRelease(); + 2a6d360f-01c4-5b27-81fa-337b6b33a1b6_Impl_LineIds + + + + + METHOD release : BOOL + 2a6d360f-01c4-5b27-81fa-337b6b33a1b6_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + b2fda51f-6f2a-5b12-bc9c-2985dd3a44b4 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + FB_exit + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + FB_exit := nativeRelease(); + b2fda51f-6f2a-5b12-bc9c-2985dd3a44b4_Impl_LineIds + + + + + METHOD FB_exit : BOOL +VAR_INPUT + bInCopyCode : BOOL; +END_VAR + b2fda51f-6f2a-5b12-bc9c-2985dd3a44b4_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + 7568f58a-e795-5d22-a622-3f070deb32b0 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + FB_reinit + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeHandle := 0; +nativeActiveRequest := 0; nativeSelected := 0; nativeActiveIndex := -1; +nativeResultReady := FALSE; nativePrevConnected := FALSE; +requestCnt := 0; out_registrationFull := FALSE; +FOR j := 0 TO 99 DO requests[j] := 0; reqStats[j] := 0; END_FOR; +FB_reinit := TRUE; + 7568f58a-e795-5d22-a622-3f070deb32b0_Impl_LineIds + + + + + METHOD FB_reinit : BOOL +VAR + j : DINT; +END_VAR + 7568f58a-e795-5d22-a622-3f070deb32b0_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + 28ccddd0-4531-5168-aa3e-ac56dddcb68d + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + invalidateNativeData + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + out_validData := FALSE; +out_stale := TRUE; + 28ccddd0-4531-5168-aa3e-ac56dddcb68d_Impl_LineIds + + + + + METHOD PUBLIC invalidateNativeData + 28ccddd0-4531-5168-aa3e-ac56dddcb68d_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 1c14b96c-a77e-5cd3-b853-c18bf9b5ea87 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + nativeFinish + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + out_nativeError := result; +out_socketError := client.nativeStatus[7]; +out_writeUncertain := nativeInFlight AND (client.nativeStatus[9] <> 0); +IF (result = 10) AND nativeInFlight AND (nativeFunction >= 15) THEN + out_writeUncertain := TRUE; +END_IF; +IF out_writeUncertain AND client.nativeHoldUncertainWrites THEN + nativeWriteHold := TRUE; +END_IF; +stats.reqBusy := FALSE; +IF result = 0 THEN + duration := ULINT_TO_UDINT(client.nativeNowMs - nativeStartMs); + stats.lastValidDuration := duration; + IF duration > stats.longestValidDuration THEN stats.longestValidDuration := duration; END_IF; + client.nativeReplied := client.nativeReplied + 1; + client.nativeWindowTime := client.nativeWindowTime + duration; + stats.nativeResponseSeen := TRUE; + IF nativeFunction <= 4 THEN + nativeReadComplete := TRUE; + stats.nativeLastSuccessMs := client.nativeNowMs; + out_validData := TRUE; + END_IF; + done(TRUE); +ELSE + CASE result OF + 1,258: mapped := ModbusFB.Error.RequestParameterError; + 4,7: mapped := ModbusFB.Error.TcpSocketError; + 5: mapped := ModbusFB.Error.TcpConnectTimeout; + 6: mapped := ModbusFB.Error.ReplyTimeout; + 8: mapped := ModbusFB.Error.InvalidReply; + 9: mapped := ModbusFB.Error.RequestException; + 10: mapped := ModbusFB.Error.RequestCancelled; + ELSE mapped := ModbusFB.Error.InternalError; + END_CASE; + ex := ModbusFB.ExceptionCodes.RESPONSE_SUCCESS; + IF result = 9 THEN + CASE client.nativeStatus[8] OF + 1: ex := ModbusFB.ExceptionCodes.ILLEGAL_FUNCTION; + 2: ex := ModbusFB.ExceptionCodes.ILLEGAL_DATA_ADDRESS; + 3: ex := ModbusFB.ExceptionCodes.ILLEGAL_DATA_VALUE; + 4: ex := ModbusFB.ExceptionCodes.SLAVE_DEVICE_FAILURE; + 5: ex := ModbusFB.ExceptionCodes.ACKNOWLEDGE; + 6: ex := ModbusFB.ExceptionCodes.SLAVE_DEVICE_BUSY; + 8: ex := ModbusFB.ExceptionCodes.MEMORY_PARITY_ERROR; + 10: ex := ModbusFB.ExceptionCodes.GATEWAY_PATH_UNAVAILABLE; + 11: ex := ModbusFB.ExceptionCodes.GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND; + END_CASE; + client.nativeReplied := client.nativeReplied + 1; + stats.nativeResponseSeen := TRUE; + ELSE + stats.nativeResponseSeen := FALSE; + END_IF; + err(in := TRUE,errorId := mapped,exceptionCode := ex); +END_IF; +nativeInFlight := FALSE; +stats(); + 1c14b96c-a77e-5cd3-b853-c18bf9b5ea87_Impl_LineIds + + + + + METHOD nativeFinish +VAR_INPUT + result : UDINT; +END_VAR +VAR + mapped : ModbusFB.Error; + ex : ModbusFB.ExceptionCodes; + duration : UDINT; +END_VAR + 1c14b96c-a77e-5cd3-b853-c18bf9b5ea87_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + fd4b7cdb-3c7f-5110-af32-341f71321132 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + nativeProcess + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeReadComplete := FALSE; +stats.reqDone := FALSE; stats.reqError := FALSE; +stats(); +IF NOT client.out_connected THEN stats.setClientOffline(); END_IF; +IF client.nativeActiveRequest = THIS AND client.nativeResultReady THEN + IF (client.nativeStatus[3] <> 0) AND (nativeFunction <= 4) AND NOT nativeCopyAllowed THEN + nativeFinish(1); + ELSE + nativeFinish(client.nativeStatus[5]); + END_IF; + RETURN; +END_IF; +IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS THEN + nativeFunction := functionCode; + nativeCount := stats.reqLength; nativeOffset := in_dataOffset; + nativeStartMs := client.nativeNowMs; + IF out_parameterError OR nativeCount = 0 THEN nativeFinish(1); RETURN; END_IF; + client.nativeArgUnit := in_unitId; + client.nativeArgFunction := functionCode; + client.nativeArgAddress := in_startAddr; + client.nativeArgQuantity := nativeCount; + client.nativeArgTimeout := MAX(UDINT#1,(replyTimeout + 999)/1000); + nativeRC := client.nativeCall(11); + IF nativeRC = 0 THEN nativeRC := client.nativeCall(6); END_IF; + IF nativeRC = 0 THEN + client.nativeActiveRequest := THIS; + nativeInFlight := TRUE; + stats.reqBusy := TRUE; + client.nativeSent := client.nativeSent + 1; + client.nativeWindowCount := client.nativeWindowCount + 1; + ELSE nativeFinish(nativeRC); + END_IF; + stats(); +END_IF; +IF out_validData THEN + out_dataAgeMs := client.nativeNowMs - stats.nativeLastSuccessMs; + out_stale := out_dataAgeMs > TIME_TO_UDINT(in_staleAfter); +ELSE out_stale := TRUE; +END_IF; + fd4b7cdb-3c7f-5110-af32-341f71321132_Impl_LineIds + + + + + METHOD nativeProcess +VAR_INPUT + functionCode : BYTE; +END_VAR + fd4b7cdb-3c7f-5110-af32-341f71321132_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 9ee1081f-903d-595c-ab0c-17c64f4fbe75 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + FB_reinit + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeInFlight := FALSE; +stats.execute := FALSE; +stats.reqBusy := FALSE; +stats.reqDone := FALSE; +stats.reqError := FALSE; +FB_reinit := TRUE; + 9ee1081f-903d-595c-ab0c-17c64f4fbe75_Impl_LineIds + + + + + METHOD FB_reinit : BOOL + 9ee1081f-903d-595c-ab0c-17c64f4fbe75_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + CODESYS V3.5 SP21 Patch 4 + + + \ No newline at end of file diff --git a/compat/AtomModbus-Compatible-Fixed.export b/compat/AtomModbus-Compatible-Fixed.export new file mode 100644 index 0000000..16494ef --- /dev/null +++ b/compat/AtomModbus-Compatible-Fixed.export @@ -0,0 +1,5519 @@ + + + + + + + True + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + 00000000-0000-0000-0000-000000000000 + 6-Comms + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + e59dc300-e37a-4c37-997b-021563304d7e + + + + + + + + 738bea1e-99bb-4f04-90bb-a7a567e74e3a + + 639034841590379670 + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + e59dc300-e37a-4c37-997b-021563304d7e + + atomLibrary + + -1 + + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + 00000000-0000-0000-0000-000000000000 + ModbusMaster + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + + + + + + + + 738bea1e-99bb-4f04-90bb-a7a567e74e3a + + 639034841590449813 + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + + atomLibrary + 6-Comms + + -1 + + + False + + f228f401-6c74-4359-8ff1-b15b675180a4 + 00000000-0000-0000-0000-000000000000 + atMbClientToString + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 2db5746d-d284-4425-9f7f-2663a34b0ebc + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841591030946 + + + + + {attribute 'qualified_only'} +{attribute 'strict'} +TYPE atMbClientToString : +( + noneOk := 0, + someOk := 1, + allOk := 2 +); +END_TYPE + + f228f401-6c74-4359-8ff1-b15b675180a4_Decl_LineIds + + + 11 + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 43f52a7a-cd45-4c0a-8652-9b18c69324d7 + 00000000-0000-0000-0000-000000000000 + atMBExceptionCodes + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 2db5746d-d284-4425-9f7f-2663a34b0ebc + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590519807 + + + + + {attribute 'qualified_only'} +// {attribute 'strict'} NOT strict so can set with modbusFB.ExceptionCodes. +TYPE atMBExceptionCodes : +( + // Local / transport-level (non-Modbus) result + TIMEOUT := -1, // Communication timeout / no response + + // Modbus exception codes (returned in exception response PDU) + RESPONSE_SUCCESS := 16#00, // Everything OK (no exception) + + ILLEGAL_FUNCTION := 16#01, // Function code not supported by slave + ILLEGAL_DATA_ADDRESS := 16#02, // Data address not valid for slave (out of range / not mapped) + ILLEGAL_DATA_VALUE := 16#03, // Data value not valid (bad length/value/etc.) + SLAVE_DEVICE_FAILURE := 16#04, // Unrecoverable error while performing the requested action + + ACKNOWLEDGE := 16#05, // Request accepted; processing started (use to avoid master timeout) + SLAVE_DEVICE_BUSY := 16#06, // Slave busy (e.g. long operation in progress); retry later + + MEMORY_PARITY_ERROR := 16#08, // Memory parity error (special case; historically for FC 20/21) + + GATEWAY_PATH_UNAVAILABLE := 16#0A, // Gateway problem: path unavailable / misconfigured / overloaded + GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND := 16#0B // Gateway problem: target device did not respond +); +END_TYPE + + 43f52a7a-cd45-4c0a-8652-9b18c69324d7_Decl_LineIds + + + 78 + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + 00000000-0000-0000-0000-000000000000 + atMbClient + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639251309599709549 + + + None + + + IF nativeMode THEN + nativeCycle(); + RETURN; +END_IF; +//Modbus connection +//Connects to a modbus slave +//2024-11-23 ATM Cleaned up code +//2024-12-23 CWD Add statistics +//2025-06-18 CWD Add Get priorities and interval working together +//2026-03-26 ATM Clean up code, more testing +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +// ===== Main function call ===== + +IF (in_resetStats) THEN + resetStats(); + in_resetStats := FALSE; +END_IF + +// ===== Connect and disconnect logic ===== + +//Timer to trigger a connection if not connected for to long +reconnectTimer( + IN := NOT client.xConnect AND NOT client.xConnected AND NOT client.xError, + PT := RECONNECT_DELAY +); + +//Timer to trigger a disconnection if connect fails +disconnectTimer( + IN:=client.xConnect AND NOT client.xConnected, + PT:=DISCONNECT_DELAY +); + +//All requests offline timer +allRequestsOfflineTON( + IN := allRequestsOffline AND client.xConnected, + PT := ALL_OFFLINE_DELAY +); + +//Destroy Client delay timer +recreateClientDly( + IN:=destroyClient, + PT:=RECREATE_CLIENT_DELAY +); + +IF disconnectTimer.Q OR allRequestsOfflineTON.Q OR client.xError OR recreateTest THEN + //All requests are offline + //Connection attempt failed so disconnect + //If Client has error then disconnect + connect := FALSE; + recreateTest := FALSE; + destroyClient := TRUE; + setAllRequestsOffline(); +ELSIF reconnectTimer.Q AND NOT destroyClient THEN +//If not connected then attempt connect + connect := TRUE; + connectCnt := connectCnt + 1; //for debugging +END_IF; + + +IF recreateClientDly.Q THEN + destroyClient := FALSE; + recreateClient(); +END_IF; + +// If some reason gets stuck at busy, (This shouldnt happen, but force a reconnect). +forceConnect( + in := connect <> client.xConnect, + pt := FORCE_RECONNECT_DELAY +); + +// Only flip status of xConnect if no current request is active. +IF (connect <> client.xConnect) AND_THEN (allRequestsComplete OR forceConnect.Q) THEN + client.xConnect := connect; +END_IF + +// Moved this lower down, so xConnect happens one scan sooner. +client(); + +// If there are no requests, dont neeed to even do anything. +IF requestCnt = 0 THEN + numMsgSent := 0; + numMsgReply := 0; + allRequestsOnline := 0; + allRequestsOffline := 0; + RETURN; +END_IF + + +requestNotExecutingTON(IN := NOT requestExecuting, PT := NEXT_REQUEST_DELAY); //Only delaying 1 ms. This may not do much, was put in during testing, but now it almost seems unnecesary + +// ===== Execute next request ===== +IF connect AND client.xConnect AND client.xConnected AND (requestNotExecutingTON.Q OR NEXT_REQUEST_DELAY = T#0MS) THEN + + //Find the lowest priority of overdue requests + nextIdx := -1; + priorityLowest := PRIORITY_NONE; + highestTimeSinceRun := 0; + FOR i := 0 TO requestCnt - 1 DO + IF (requests[i]^.in_priorityCurrent < priorityLowest + OR (requests[i]^.in_priorityCurrent = priorityLowest AND requests[i]^.stats.sinceLastExec > highestTimeSinceRun)) + AND requests[i]^.stats.sinceLastExec > TIME_TO_UDINT(requests[i]^.in_minDelay) THEN + priorityLowest := requests[i]^.in_priorityCurrent; + highestTimeSinceRun := requests[i]^.stats.sinceLastExec; + nextIdx := i; + END_IF + END_FOR + + //Decriment all request priorities by one. + IF priorityLowest < PRIORITY_NONE AND priorityLowest > 0 THEN //Only if priority + FOR i := 0 TO requestCnt - 1 DO + requests[i]^.in_priorityCurrent := requests[i]^.in_priorityCurrent - 1; + IF requests[i]^.in_priorityCurrent < 0 THEN + requests[i]^.in_priorityCurrent := 0; + END_IF + END_FOR + END_IF + + IF nextIdx >= 0 THEN + activeIndex := nextIdx; + END_IF + END_IF + +processAllRequests(); + +allRequestsOnline := (activeReqCnt > 0) AND (onlineCnt = activeReqCnt); +allRequestsOffline := (activeReqCnt > 0) AND (onlineCnt = 0); + +// ===== Client statistics ===== +//Get a sum of done and error counts from all requests +reqErrSum := 0; +reqDoneSum := 0; +FOR i := 0 TO requestCnt -1 DO + reqDoneSum := reqDoneSum + requests[i]^.stats.doneCnt; + reqErrSum := reqErrSum + requests[i]^.stats.errorCnt; + + //copy stats to array for debugging + reqStats[i] := ADR(requests[i]^.stats); +END_FOR + + + +//Every time the tx message counter changes increment msg count and add last transaction time +IF client.udiNumMsgSent <> msgCountLast THEN + msgCountLast := client.udiNumMsgSent; + msgTimeSum := msgTimeSum + client.udiLastTransactionTime; + msgCountSum := msgCountSum + 1; +END_IF + +//Every 1 second compute statistics +pulse(); +IF pulse.T_Sec_OS THEN + msgLastSecondCnt := msgCountSum; + msgCountSum := 0; + msgLastSecondTime := msgTimeSum; + msgTimeSum := 0; +END_IF + +numMsgSent := client.udiNumMsgSent; +numMsgReply := client.udiNumMsgReply; + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa_Impl_LineIds + + + + + {attribute 'monitoring_display' := 'toString'} +{attribute 'no_assign'} +FUNCTION_BLOCK ABSTRACT atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_timeout: TIME := T#0MS; // If this is set, this will be used on requests, otherwise it will be calculated from in_dataRate. + {attribute 'symbol' := 'none'} + in_resetStats: BOOL; // Set this bool to true to reset statistics. +END_VAR +VAR_OUTPUT + {attribute 'symbol' := 'none'} + msgLastSecondCnt: UDINT; //Messages in the last second + {attribute 'symbol' := 'none'} + msgLastSecondTime: UDINT; //Total amount of time for all messages sent last second in ms + {attribute 'symbol' := 'none'} + numMsgSent: UDINT; + {attribute 'symbol' := 'none'} + numMsgReply: UDINT; + {attribute 'symbol' := 'none'} + allRequestsOnline: BOOL; //All requests are working + //{attribute 'symbol' := 'none'} + reqStats: ARRAY[0..99] OF POINTER TO atMbRequestStats; // For debugging modbus requests +END_VAR +VAR + {attribute 'symbol' := 'none'} + datarate: DINT := 0; //Bits per second data rate. Used to calculate reply timeout. Based on amount of time to transmit holding register read for max amount of registers. Default 154705 for Modbus TCP = 17ms to transmit 2650 bits + {attribute 'symbol' := 'none'} + client: REFERENCE TO ModbusFB.Client; // This is generated internally, no need to set. + {attribute 'symbol' := 'none'} + connectCnt: DINT; //Number of connection attempts + {attribute 'symbol' := 'none'} + initDone: BOOL; + {attribute 'symbol' := 'none'} + disconnectTimer: TON; //Connecting but not connected delay + {attribute 'symbol' := 'none'} + reconnectTimer: TON; //Not connecting and not connected delay + {attribute 'symbol' := 'none'} + msgCountLast: UDINT; //Last message count + {attribute 'symbol' := 'none'} + msgCountSum: UDINT; //Message count accumulator + {attribute 'symbol' := 'none'} + msgTimeSum: UDINT; //Amount of time for messsages accumulator + {attribute 'symbol' := 'none'} + pulse: atPulse; //Pulse generator + {attribute 'symbol' := 'none'} + requests: ARRAY[0..99] OF POINTER TO atMbReqBase; //list of request to direct + {attribute 'symbol' := 'none'} + requestCnt: UINT; //Number of active requests + {attribute 'symbol' := 'none'} + reqDoneSum: UDINT; //Count of done result on all requests + {attribute 'symbol' := 'none'} + reqErrSum: UDINT; //Count of error result on all requests + {attribute 'symbol' := 'none'} + i: DINT; //Loop variable + {attribute 'symbol' := 'none'} + nextIdx: DINT := -1; //Index of the active request + {attribute 'symbol' := 'none'} + priorityLowest: INT; //lowest request priority + {attribute 'symbol' := 'none'} + highestTimeSinceRun: UDINT; //lowest request priority + {attribute 'symbol' := 'none'} + allRequestsOfflineTON: TON; //All requests are offline delay + {attribute 'symbol' := 'none'} + requestNotExecutingTON: TON; //Delay after there are no requests execting + {attribute 'symbol' := 'none'} + recreateClientDly: TON; //Delay to make new client after force fail + {attribute 'symbol' := 'none'} + allRequestsOffline: BOOL; //All requests are offline + {attribute 'symbol' := 'none'} + connect: BOOL; // this is more of a "Request to connect, Requet to disconnect", will drop xConnect once all requests are done, wont start requests unless this is true. + {attribute 'symbol' := 'none'} + forceConnect: TON; //If after 5 seconds, all requests are still ofline, just force a reconnect. + test1: REAL; + test2: REAL; + destroyClient: BOOL; + recreateTest: BOOL; +END_VAR +VAR CONSTANT + {attribute 'symbol' := 'none'} + NEXT_REQUEST_DELAY: TIME := T#0MS; // Dont seem to need a delay + {attribute 'symbol' := 'none'} + RECONNECT_DELAY: TIME := T#500MS; + {attribute 'symbol' := 'none'} + DISCONNECT_DELAY: TIME := T#500MS; + {attribute 'symbol' := 'none'} + RECREATE_CLIENT_DELAY: TIME := T#1000MS; + {attribute 'symbol' := 'none'} + ALL_OFFLINE_DELAY: TIME := T#2S; + {attribute 'symbol' := 'none'} + FORCE_RECONNECT_DELAY: TIME := T#1S; + {attribute 'symbol' := 'none'} + PRIORITY_NONE: INT := 32767; +END_VAR + +VAR +nativeMode : BOOL; +nativeHandle : UDINT; +nativeIP : DWORD; +nativePort : UINT; +nativeNowUs : SYSTIME; +nativeNowMs : ULINT; +nativeStatus : ARRAY[0..23] OF UDINT; +nativeActiveIndex : DINT := -1; +nativeSelected : POINTER TO atMbReqBase; +nativeResultReady : BOOL; +nativeFinishedIndex : DINT := -1; +nativeCallResult : UDINT; +nativeWindowStart : ULINT; +nativePrevConnected : BOOL; +nativeEnable : BOOL := TRUE; +nativeConnectTimeout : UDINT := 2000; +nativeRetryDelay : UDINT := 1000; +nativeHoldUncertainWrites : BOOL := FALSE; +END_VAR +VAR_INPUT +{attribute 'symbol' := 'none'} +nativeIO : ARRAY[0..1999] OF WORD; +{attribute 'symbol' := 'none'} +nativeActiveRequest : POINTER TO atMbReqBase; +{attribute 'symbol' := 'none'} +nativeArgUnit : BYTE; +{attribute 'symbol' := 'none'} +nativeArgFunction : BYTE; +{attribute 'symbol' := 'none'} +nativeArgAddress : UINT; +{attribute 'symbol' := 'none'} +nativeArgQuantity : UINT; +{attribute 'symbol' := 'none'} +nativeArgTimeout : UDINT := 1000; +{attribute 'symbol' := 'none'} +nativeSent : UDINT; +{attribute 'symbol' := 'none'} +nativeReplied : UDINT; +{attribute 'symbol' := 'none'} +nativeWindowCount : UDINT; +{attribute 'symbol' := 'none'} +nativeWindowTime : UDINT; +END_VAR + +VAR_OUTPUT +out_connected : BOOL; +out_connectionState : UDINT; +out_lastNativeError : UDINT; +out_socketError : UDINT; +out_reconnectCount : UDINT; +out_registrationFull : BOOL; +END_VAR + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa_Decl_LineIds + + + 3369 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + 00000000-0000-0000-0000-000000000000 + atMbClientRTU + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649591897 + + + None + + + //Modbus connection +//Connects to a modbus slave +//2024-06-27 CWD Initial create +//2024-11-13 CWD Fix problem with not connecting properly on power up +//2024-11-15 ATM Move from structure to defaults. +//2024-11-23 ATM Cleaned up code +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +IF NOT setupClient() THEN + RETURN; +END_IF + +//Set dataRate +//This is based on an 8 byte request and a 255 byte reply +dataRate := in_baudRate; + + +// ===== Initialization ===== +// if the param changes, force a reinit +IF _client^.iPort <> in_port + OR _client^.dwBaudrate <> in_baudRate + OR _client^.byDataBits <> in_dataBits + OR _client^.eParity <> in_parity + OR _client^.eStopBits <> in_stopBits THEN + initDone := FALSE; +END_IF; + +IF NOT initDone THEN + initDone := TRUE; + + //Other configuration parameters + _client^.iPort := in_port; + _client^.dwBaudrate := in_baudRate; + _client^.byDataBits := in_dataBits; + _client^.eParity := in_parity; + _client^.eStopBits := in_stopBits; + _client^.eDTRcontrol := 0; + _client^.eRTScontrol := 0; + _client^.eRTUASCII := 0; + _client^.udiLogOptions := ModbusFB.LoggingOptions.All; + _client^.xConnect:=FALSE; + +END_IF + +SUPER^(); + 17c2ab7a-4d60-48ec-966f-339ec83a75e9_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbClientRTU EXTENDS atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_port: BYTE := 0; //0=None, 1,2,3,4 + {attribute 'symbol' := 'none'} + in_baudRate: UINT := 9600; //4800, 9600, 19200, 38400, 57600, 115200 + {attribute 'symbol' := 'none'} + in_parity: BYTE := 0; //0=None, 1=Odd, 2=Even + {attribute 'symbol' := 'none'} + in_dataBits: BYTE := 8; //8=8 + {attribute 'symbol' := 'none'} + in_stopBits: BYTE := 1; //1=OneStopBit, 2=One5StopBits, 3=TwoStopBits +END_VAR +VAR + {attribute 'symbol' := 'none'} + _client: POINTER TO ModbusFB.ClientSerial; +END_VAR + 17c2ab7a-4d60-48ec-966f-339ec83a75e9_Decl_LineIds + + + 607 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + 00000000-0000-0000-0000-000000000000 + atMbClientTCP + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649747183 + + + None + + + nativeMode := TRUE; +nativeIP := SHL(BYTE_TO_DWORD(in_ipAddress[0]),24) OR + SHL(BYTE_TO_DWORD(in_ipAddress[1]),16) OR + SHL(BYTE_TO_DWORD(in_ipAddress[2]),8) OR BYTE_TO_DWORD(in_ipAddress[3]); +nativePort := in_port; +nativeConnectTimeout := MAX(UDINT#1,TIME_TO_UDINT(in_connectTimeout)); +nativeRetryDelay := MAX(UDINT#1,TIME_TO_UDINT(in_reconnectDelay)); +nativeHoldUncertainWrites := in_holdUncertainWrites; +IF nativeHandle <> 0 AND ((nativeEndpointIP <> nativeIP) OR + (nativeEndpointPort <> nativePort) OR (nativeConfigConnect <> nativeConnectTimeout) OR + (nativeConfigRetry <> nativeRetryDelay) OR NOT in_enable) THEN + nativeRelease(); +END_IF; +nativeEndpointIP := nativeIP; nativeEndpointPort := nativePort; +nativeConfigConnect := nativeConnectTimeout; nativeConfigRetry := nativeRetryDelay; +nativeEnable := in_enable; +IF in_baudrate <> 0 THEN dataRate := in_baudrate; ELSE dataRate := 105200; END_IF; +SUPER^(); + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbClientTCP EXTENDS atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_ipAddress: ARRAY[0..3] OF BYTE; // TCP IP Address + {attribute 'symbol' := 'none'} + in_port: UINT := 502; // TCP port + {attribute 'symbol' := 'none'} + in_baudrate: UINT := 0; // Set this if using RS485 to TCP Converter, used to calculate timeout. +END_VAR +VAR + {attribute 'symbol' := 'none'} + _client: POINTER TO ModbusFB.ClientTCP; +END_VAR +VAR_INPUT +in_enable : BOOL := TRUE; +in_connectTimeout : TIME := T#2S; +in_reconnectDelay : TIME := T#1S; +in_holdUncertainWrites : BOOL := FALSE; +END_VAR + +VAR +nativeEndpointIP : DWORD; +nativeEndpointPort : UINT; +nativeConfigConnect : UDINT; +nativeConfigRetry : UDINT; +END_VAR + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570_Decl_LineIds + + + 1168 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + 00000000-0000-0000-0000-000000000000 + atMbReqBase + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649837310 + + + None + + + //Modbus request +//Makes a request using the start and quantity, puts the result into data +//2024-06-27 CWD Initial create +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage + +IF NOT __ISVALIDREF(client) THEN + RETURN; +END_IF + + +//Add this request to the clients request array +client.addRequest(THIS); + +//fault logic +//Fault if error +//errTrig(CLK := stats.reqError); +//IF errTrig.Q THEN +// out_fault := TRUE; +//END_IF +//not faulted if a succesfull request +//doneTrig(CLK := stats.reqDone); +// also clear fault if not active +//IF doneTrig.Q OR NOT stats.active THEN +// out_fault := FALSE; +//END_IF + +IF in_quantity <= dataSize THEN + stats.reqLength := in_quantity; +ELSE + stats.reqLength := dataSize; +END_IF; + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK ABSTRACT atMbReqBase +VAR_INPUT + {attribute 'symbol' := 'none'} + client: REFERENCE TO atMbClient; // Instance of atMBClient + {attribute 'symbol' := 'none'} + in_startAddr: UINT; + {attribute 'symbol' := 'none'} + in_quantity: UINT; + {attribute 'symbol' := 'none'} + in_dataOffset: UINT; // Offset position for data into array. + {attribute 'symbol' := 'none'} + in_unitId: BYTE := 0; + {attribute 'symbol' := 'none'} + in_timeout: SYSTIME := 0; // timeout in ms, if not set, will be 1ms less than interval. + {attribute 'symbol' := 'none'} + in_minDelay: TIME := T#10MS; // This is the minimum delay between queries, only relevent if high priority. + {attribute 'symbol' := 'none'} + in_prioritySetpoint: INT := 50; // this requests priority compared to other requests. Lower value is higher priority. + {attribute 'symbol' := 'none'} + in_priorityCurrent: INT; //Current priority. This is decrimented and the request is executed when at zero. + {attribute 'symbol' := 'none'} + stats: atMbRequestStats; //Request stats + +END_VAR +VAR_OUTPUT + //{attribute 'symbol' := 'none'} + //out_fault: BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + dataSize: UINT; + //{attribute 'symbol' := 'none'} + //quantUsed: UINT; + //{attribute 'symbol' := 'none'} + //doneTrig: R_TRIG; //Succesful request trigger + //{attribute 'symbol' := 'none'} + //errTrig: R_TRIG; //Error request result trigger + + {attribute 'symbol' := 'none'} + request: REFERENCE TO ModbusFB.ClientRequest;; +END_VAR + + +VAR +nativeFunction : BYTE; +nativeCount : UINT; +nativeOffset : UINT; +nativeInFlight : BOOL; +nativeReadComplete : BOOL; +nativeCopyAllowed : BOOL; +nativeWriteHold : BOOL; +nativeStartMs : ULINT; +nativeLoop : DINT; +nativeRC : UDINT; +END_VAR + +VAR_OUTPUT +out_nativeError : UDINT; +out_socketError : UDINT; +out_writeUncertain : BOOL; +out_validData : BOOL; +out_dataAgeMs : ULINT; +out_stale : BOOL := TRUE; +out_parameterError : BOOL; +END_VAR + +VAR_INPUT +in_staleAfter : TIME := T#2S; +END_VAR + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37_Decl_LineIds + + + 3210 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + 00000000-0000-0000-0000-000000000000 + atMbReqReadCoil + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146913881 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-11-16 CWD Added read coil +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + + + a77a90fd-541d-4ce9-ad97-7024224bbab4_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadCoil EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readCoilReq: POINTER TO ModbusFB.ClientRequestReadCoils; +END_VAR + + a77a90fd-541d-4ce9-ad97-7024224bbab4_Decl_LineIds + + + 193 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 1a3278f4-a658-407c-968a-21aa53361562 + 00000000-0000-0000-0000-000000000000 + atMbReqReadDiscrete + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147014015 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + 1a3278f4-a658-407c-968a-21aa53361562_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadDiscrete EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readDiscreteReq: POINTER TO ModbusFB.ClientRequestReadDiscreteInputs; +END_VAR + + 1a3278f4-a658-407c-968a-21aa53361562_Decl_LineIds + + + 184 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 73b10819-9112-4811-9583-e2735d2b13ea + 00000000-0000-0000-0000-000000000000 + atMbReqReadHolding + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147099228 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + 73b10819-9112-4811-9583-e2735d2b13ea_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadHolding EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readHoldReq: POINTER TO ModbusFB.ClientRequestReadHoldingRegisters; +END_VAR + + 73b10819-9112-4811-9583-e2735d2b13ea_Decl_LineIds + + + 173 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + 00000000-0000-0000-0000-000000000000 + atMbReqReadInput + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147179217 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + a5dbc9b0-a1a1-4281-a685-ec85b35bb866_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadInput EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readInputReq: POINTER TO ModbusFB.ClientRequestReadInputRegisters; +END_VAR + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866_Decl_LineIds + + + 167 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + 00000000-0000-0000-0000-000000000000 + atMbRequestStats + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147425423 + + + None + + + SysTimeGetUs(TSSus); + +IF (reqLength = 0) THEN + active := FALSE; + doneCnt := 0; + errorCnt := 0; + lastErrorId := modbusFB.Error.OK; + lastExceptionCode := atMBExceptionCodes.RESPONSE_SUCCESS; + execute := FALSE; + online := FALSE; + offline := FALSE; + reqDone := FALSE; + reqBusy := FALSE; + reqError := FALSE; + sinceLastExec := 0; + RETURN; +END_IF +active := TRUE; + +inDoneDeb(in_raw := reqDone, in_delOn := T#0S, in_DelOff := lightOn); +reqDoneLight := inDoneDeb.out_val; + +inBusyDeb(in_raw := reqBusy, in_delOn := T#0S, in_DelOff := lightOn); +reqBusyLight := inBusyDeb.out_val; + +inErrDeb(in_raw := reqError, in_delOn := T#0S, in_DelOff := lightOn); +reqErrorLight := inErrDeb.out_val; + +//Debounced execute indicator for debbugging +executeDeb(in_raw := _execute, in_delOn := T#0S, in_DelOff := lightOn); +executeLight := executeDeb.out_val; + + +//Set online status +IF nativeResponseSeen OR doneCntRst >=3 THEN + online := TRUE; + offline := FALSE; +END_IF + +//Set online status +IF NOT nativeResponseSeen AND errorCntRst >= 3 THEN + online := FALSE; + offline := TRUE; +END_IF + +//Record start time in ms on execute rising edge +executeTrig(CLK := execute); +IF executeTrig.Q THEN + interval := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + lastReqStart := TSSus; +END_IF + +//Offline count +offlineTrig(CLK := NOT online); +IF offlineTrig.Q THEN + offlineCnt := offlineCnt + 1; +END_IF + +//Calculate ms since last execute +//If the last request start is more then 1 minute ago then set it for 1 minute +IF TSSus >= lastReqStart THEN + IF TSSus - lastReqStart > 60000000 THEN lastReqStart := TSSus - 60000000; END_IF; +ELSE lastReqStart := TSSus; +END_IF; +sinceLastExec := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + + 48b508fd-2131-484c-ba4a-20e61e66a5b2_Impl_LineIds + + + + + {attribute 'monitoring_display' := 'toString'} +{attribute 'no_assign'} +FUNCTION_BLOCK atMbRequestStats +VAR_INPUT + lastErrorId: modbusFB.Error; //Last error id + lastExceptionCode: atMBExceptionCodes; //Last modbus exception code. These are the standard modbus exceptions. + lastValidDuration: UDINT; // How long last transaction took (ms) + longestValidDuration: UDINT; //Longest valid duration (ms) + interval: UDINT; // Start-to-start interval (ms) + offlineCnt: UINT; //Count of how many times this request has gone offline + doneCnt: UDINT; // Count of successful request + errorCnt: UDINT; // Count of failed requests + doneCntRst: UDINT; // Count of succesful requests, resets on error result + errorCntRst: UDINT; // Count of failed requests, resets on succesful request + sinceLastExec: UDINT; // Time since the last start (ms) + timeoutCnt: UDINT; + reqLength: UINT; //Number of registers requested in request, used to calc active. +END_VAR +VAR_OUTPUT + executeLight: BOOL; //Execute that stays on momentarily + reqDoneLight: BOOL; //Request done that stays on momentarily + reqErrorLight: BOOL; //Request error that stays on momentarily + reqBusyLight: BOOL; //Request busy that stays on momentarily + online: BOOL; //Online means the requests are being replied to normally or with modbus exceptions. Communications are ok. + offline: BOOL; //Opposite of online, but starts out as false on program start. + active: BOOL; // This is true if its actually being used, and not having a count of 0 on the request. + executeMbFb: BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + inDoneDeb: atDebounce; + {attribute 'symbol' := 'none'} + inErrDeb: atDebounce; + {attribute 'symbol' := 'none'} + inBusyDeb: atDebounce; + {attribute 'symbol' := 'none'} + executeDeb: atDebounce; + toString: STRING(20); + {attribute 'symbol' := 'none'} + executeTrig: R_TRIG; //Execute trigger + {attribute 'symbol' := 'none'} + offlineTrig: R_TRIG; //Offline trigger + {attribute 'symbol' := 'none'} + lastReqStart: SYSTIME; //time of start of last request + {attribute 'symbol' := 'none'} + TSSus: SYSTIME; + _execute: BOOL; //Execute trigger and status. Turn on to trigger request. Will turn off when request is complete + _reqDone: BOOL; //Request done + _reqBusy: BOOL; //Request busy + _reqError: BOOL; //Request error +END_VAR + +VAR CONSTANT + {attribute 'symbol' := 'none'} + lightOn: TIME:= T#100MS; //Light on time for debug indicators +END_VAR +VAR_INPUT +nativeResponseSeen : BOOL; +nativeLastSuccessMs : ULINT; +END_VAR + + 48b508fd-2131-484c-ba4a-20e61e66a5b2_Decl_LineIds + + + 286 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + 00000000-0000-0000-0000-000000000000 + atMbReqWriteCoil + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147259331 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + f6ee860b-0951-41d0-93d8-6436c681d8f1_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqWriteCoil EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + writeCoilsReq: POINTER TO ModbusFB.ClientRequestWriteMultipleCoils; +END_VAR + + f6ee860b-0951-41d0-93d8-6436c681d8f1_Decl_LineIds + + + 206 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + bb7e18ba-2018-4681-875e-de1f291019ef + 00000000-0000-0000-0000-000000000000 + atMbReqWriteHolding + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147339348 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + bb7e18ba-2018-4681-875e-de1f291019ef_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqWriteHolding EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + writeHoldReq: POINTER TO ModbusFB.ClientRequestWriteMultipleRegisters; +END_VAR + + bb7e18ba-2018-4681-875e-de1f291019ef_Decl_LineIds + + + 164 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 9499c60e-03a2-49d0-b086-43990a018e20 + 00000000-0000-0000-0000-000000000000 + To_IPAddress + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590489804 + + + None + + + //Convert parameters to array +To_IPAddress[0] := byte1; +To_IPAddress[1] := byte2; +To_IPAddress[2] := byte3; +To_IPAddress[3] := byte4; + + + + 9499c60e-03a2-49d0-b086-43990a018e20_Impl_LineIds + + + + + FUNCTION To_IPAddress : ARRAY[0..3] OF BYTE +VAR_INPUT + byte1: BYTE; + byte2: BYTE; + byte3: BYTE; + byte4: BYTE; +END_VAR + + 9499c60e-03a2-49d0-b086-43990a018e20_Decl_LineIds + + + 168 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 9e99e368-853f-485b-911f-d9a2a4e46434 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + addRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146468437 + + + + + IF request = 0 THEN RETURN; END_IF; +IF requestCnt > 0 THEN + FOR i := 0 TO requestCnt-1 DO + IF request = requests[i] THEN RETURN; END_IF; + END_FOR; +END_IF; +IF requestCnt >= 100 THEN + out_registrationFull := TRUE; + RETURN; +END_IF; +requests[requestCnt] := request; +requestCnt := requestCnt + 1; + 9e99e368-853f-485b-911f-d9a2a4e46434_Impl_LineIds + + + + + //Adds the specified request to the global list +METHOD PUBLIC addRequest +VAR_INPUT + request: POINTER TO atMbReqBase; +END_VAR +VAR + i: UINT := 0; + +END_VAR + + 9e99e368-853f-485b-911f-d9a2a4e46434_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 685f7c69-c46a-4b34-b5e2-fb848c593f3e + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + processAllRequests + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148036583 + + + + + FOR i := 0 TO requestCnt -1 DO + requests[i]^.processRequest(); +END_FOR; + 685f7c69-c46a-4b34-b5e2-fb848c593f3e_Impl_LineIds + + + + + METHOD processAllRequests +VAR_INPUT +END_VAR +VAR + i: UINT; // Loop bstisnlr +END_VAR + 685f7c69-c46a-4b34-b5e2-fb848c593f3e_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 39769d9e-08c1-472d-82ef-25c66891a065 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + recreateAllRequests + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148056585 + + + + + FOR i := 0 TO requestCnt -1 DO + //Test for any offline request + requests[i]^.recreateRequest(); +END_FOR; + 39769d9e-08c1-472d-82ef-25c66891a065_Impl_LineIds + + + + + METHOD PROTECTED recreateAllRequests +VAR_INPUT +END_VAR + + 39769d9e-08c1-472d-82ef-25c66891a065_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574648119824 + + + + + + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc_Impl_LineIds + + + + + METHOD ABSTRACT recreateClient +VAR_INPUT +END_VAR + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + a7e27967-573f-4f9f-86e7-dd569b8cd943 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + resetStats + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + IF nativeMode THEN + nativeSent := 0; nativeReplied := 0; + nativeWindowCount := 0; nativeWindowTime := 0; + msgLastSecondCnt := 0; msgLastSecondTime := 0; + numMsgSent := 0; numMsgReply := 0; + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.stats.resetStats(); + END_FOR; + RETURN; +END_IF; +connectCnt := 0; +val := ADR(client.udiNumMsgSent); +val^ := 0; +val := ADR(client.udiNumMsgReply); +val^ := 0; +FOR i := 0 TO requestCnt -1 DO + requests[i]^.stats.resetStats(); +END_FOR; + a7e27967-573f-4f9f-86e7-dd569b8cd943_Impl_LineIds + + + + + METHOD resetStats +VAR_INPUT +END_VAR +VAR + i: DINT; + val: POINTER TO UDINT; +END_VAR + + + + + a7e27967-573f-4f9f-86e7-dd569b8cd943_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + ca1dfc70-ad93-43de-886d-39e56897aa6d + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + setAllRequestsOffline + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148106585 + + + + + FOR i := 0 TO requestCnt -1 DO + requests[i]^.stats.setClientOffline(); +END_FOR; + ca1dfc70-ad93-43de-886d-39e56897aa6d_Impl_LineIds + + + + + METHOD PROTECTED setAllRequestsOffline : BOOL +VAR_INPUT +END_VAR +VAR + i: DINT; +END_VAR + + + + + ca1dfc70-ad93-43de-886d-39e56897aa6d_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + d12c9bf2-320f-4e95-badb-68a2357c5783 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + setupClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574648239962 + + + + + IF nativeMode THEN + setupClient := nativeHandle <> 0; + RETURN; +END_IF; +// Only create request if doesnt exist +IF NOT __ISVALIDREF(client) THEN + recreateClient(); + IF NOT __ISVALIDREF(client) THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; + setupClient := FALSE; + RETURN; + END_IF +END_IF + +setupClient := TRUE; + d12c9bf2-320f-4e95-badb-68a2357c5783_Impl_LineIds + + + + + METHOD setupClient : BOOL +VAR_INPUT +END_VAR + + d12c9bf2-320f-4e95-badb-68a2357c5783_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + activeIndex + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590625089 + + + + + PROPERTY activeIndex : DINT + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32_Decl_LineIds + + + 1 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + activeReqCnt + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639108502146293051 + + + + + PROPERTY PRIVATE activeReqCnt : UINT + 456b4953-57c9-4f76-ab5b-0db456dd9b58_Decl_LineIds + + + 1 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 4d9d378a-f256-4f23-b835-539ee9e287c3 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + allRequestsComplete + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639108502147921016 + + + + + {attribute 'symbol' := 'none'} +PROPERTY PRIVATE allRequestsComplete : BOOL + 4d9d378a-f256-4f23-b835-539ee9e287c3_Decl_LineIds + + + 2 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 89742962-f9b6-46c2-b9a5-b7e298f8495d + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + onlineCnt + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590755417 + + + + + {attribute 'symbol' := 'none'} +PROPERTY onlineCnt : DINT//Count of requests that are online + 89742962-f9b6-46c2-b9a5-b7e298f8495d_Decl_LineIds + + + 3 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 27292866-0bc0-4848-a965-64c51a0bf960 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + requestExecuting + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590845466 + + + + + {attribute 'symbol' := 'none'} +PROPERTY PRIVATE requestExecuting : BOOL //There is at least one request executing + 27292866-0bc0-4848-a965-64c51a0bf960_Decl_LineIds + + + 3 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 4950a3a1-f54f-434d-9252-b61a6cb4663d + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649617058 + + + + + old_client_id := _client; +_client := __NEW(ModbusFB.ClientSerial); +IF _client = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; +END_IF; +client REF= _client^; +connect := FALSE; + +IF old_client_id <> 0 THEN + __DELETE(old_client_id); + old_client_id := 0; +END_IF + +recreateAllRequests(); + 4950a3a1-f54f-434d-9252-b61a6cb4663d_Impl_LineIds + + + + + METHOD recreateClient +VAR_INPUT + old_client_id: POINTER TO ModbusFB.ClientSerial; +END_VAR + + 4950a3a1-f54f-434d-9252-b61a6cb4663d_Decl_LineIds + + + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + + -1 + + + False + + e8c6c80b-7bdb-4108-987e-4af107de4377 + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + toString + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639207574650142720 + + + + + {attribute 'symbol' := 'none'} +PROPERTY toString : atMbClientToString //Client request status noneOk = no requests working, someOk = some requests are working some are failing, allOk = all requests are working + e8c6c80b-7bdb-4108-987e-4af107de4377_Decl_LineIds + + + 3 + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + + -1 + + + False + + 79dfe91d-6dbc-46b0-866c-4b880e44e02b + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649757233 + + + + + nativeRelease(); +// Next scan creates a fresh native context with current endpoint settings. + 79dfe91d-6dbc-46b0-866c-4b880e44e02b_Impl_LineIds + + + + + METHOD recreateClient +VAR +END_VAR + 79dfe91d-6dbc-46b0-866c-4b880e44e02b_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + a77f3143-e2cf-48a4-bafd-a0c18a11faeb + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + busy + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148176670 + + + + + stats.reqBusy := in; + +IF NOT in THEN + RETURN; +END_IF; + a77f3143-e2cf-48a4-bafd-a0c18a11faeb_Impl_LineIds + + + + + METHOD busy : BOOL +VAR_INPUT + in: BOOL; +END_VAR +VAR + executeTime: ULINT; + replyTimeoutTime: ULINT; +END_VAR + + + a77f3143-e2cf-48a4-bafd-a0c18a11faeb_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 5350a8fa-8297-42db-b185-fcb966e45684 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + completeRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148196661 + + + + + done(request.xDone); +err(in := request.xError, errorId := request.eErrorId, exceptionCode := request.eException); +busy(in := request.xBusy); +stats(); + +IF (atGVL.reset) THEN + // request.AbortModel(); + request.ResetModel(TRUE); +END_IF + 5350a8fa-8297-42db-b185-fcb966e45684_Impl_LineIds + + + + + METHOD completeRequest +VAR_INPUT +END_VAR + + 5350a8fa-8297-42db-b185-fcb966e45684_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + done + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148206667 + + + + + stats.reqDone := in; + +IF NOT in THEN + RETURN; +END_IF + +//Set priority for next request +in_priorityCurrent := in_prioritySetpoint; + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac_Impl_LineIds + + + + + METHOD done +VAR_INPUT + // Done state + in: BOOL; +END_VAR + + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + err + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148226656 + + + + + stats.reqError := in; + +IF NOT in THEN + RETURN; +END_IF + + +//Update error info +stats.lastErrorId := errorId; +stats.lastExceptionCode := ANY_TO_BYTE(exceptionCode); + +//Since we are here there was an exception. If the modbus response is 0, that means no modbus communication happened, so we are offline. If we would have had a succesful communication we would have got a actual modbus error. +//IF stats.lastExceptionCode = atMBExceptionCodes.RESPONSE_SUCCESS THEN + //Since there was no response from the device set the priority to higher number priority +// in_priorityCurrent := in_prioritySetPoint * 5; +//END_IF + +//Set priority for next request, priority gets higher after each error result +priorityMultiplier := UDINT_TO_INT(stats.errorCntRst); +IF priorityMultiplier > 3 THEN + priorityMultiplier := 3; +END_IF +// set multiplier to exponential retry. +in_priorityCurrent := 10 * priorityMultiplier * priorityMultiplier; +//in_priorityCurrent := in_prioritySetpoint * priorityMultiplier; + +// NO idea what Reset Model does, hoping it fixes comms issues +// request.ResetModel(TRUE); // I dont think this helped + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c_Impl_LineIds + + + + + METHOD err +VAR_INPUT + // Error state + in: BOOL; + errorId: modbusFB.Error; //modbusFB error ID + exceptionCode: modbusFB.ExceptionCodes; //ModbusFB exception code +END_VAR +VAR + priorityMultiplier: INT; //Raise priority when not response +END_VAR + + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 109d8e00-bf23-4306-a8d5-764aa6a03235 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148246723 + + + + + + 109d8e00-bf23-4306-a8d5-764aa6a03235_Impl_LineIds + + + + + METHOD ABSTRACT processRequest +VAR_INPUT +END_VAR + + 109d8e00-bf23-4306-a8d5-764aa6a03235_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148266663 + + + + + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8_Impl_LineIds + + + + + METHOD ABSTRACT recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + db40c392-b461-4198-851d-85857ed98ccf + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + setupClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148276644 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + setupClient := FALSE; // Native path has no ModbusFB request object. + RETURN; +END_IF; +// Only create request if doesnt exist +IF in_request = 0 THEN + in_request := recreateRequest(); + IF in_request = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; + setupClient := FALSE; + RETURN; + END_IF +END_IF + +IF NOT __ISVALIDREF(mbsClient) THEN + setupClient := FALSE; + RETURN; +END_IF + +request REF= in_request^; + +setupClient := TRUE; +RETURN; + db40c392-b461-4198-851d-85857ed98ccf_Impl_LineIds + + + + + METHOD setupClient : BOOL +VAR_INPUT + in_request: POINTER TO ModbusFB.ClientRequest; +END_VAR + + db40c392-b461-4198-851d-85857ed98ccf_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 6159c037-e399-466d-8108-0c4f6570f9b2 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + mbsClient + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639207574650298004 + + + + + PROPERTY PROTECTED mbsClient : REFERENCE TO ModbusFB.Client + 6159c037-e399-466d-8108-0c4f6570f9b2_Decl_LineIds + + + 1 + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + replyTimeout + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841591196333 + + + + + //Calculates the reply timout setting +PROPERTY PROTECTED replyTimeout : UDINT + 4f361885-f95c-4244-84d6-8c3b6b9b8a18_Decl_LineIds + + + 2 + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + ea5277e9-2ae8-417f-a88d-b571902c7d71 + a77a90fd-541d-4ce9-ad97-7024224bbab4 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639163341699321592 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(1); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := client.nativeIO[nativeLoop] <> 0; + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readCoilReq) THEN + RETURN; +END_IF + +//Do request processing +readCoilReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + ea5277e9-2ae8-417f-a88d-b571902c7d71_Impl_LineIds + + + + + METHOD processRequest + + ea5277e9-2ae8-417f-a88d-b571902c7d71_Decl_LineIds + + + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadCoil + + -1 + + + False + + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3 + a77a90fd-541d-4ce9-ad97-7024224bbab4 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148331930 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readCoilReq <> 0 THEN + __DELETE(readCoilReq); + readCoilReq := 0; +END_IF +readCoilReq := __NEW(ModbusFB.ClientRequestReadCoils); +recreateRequest := readCoilReq; + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3_Decl_LineIds + + + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadCoil + + -1 + + + False + + 9df094f9-a173-4520-816f-7badcbb1bd34 + 1a3278f4-a658-407c-968a-21aa53361562 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639163341699461941 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(2); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := client.nativeIO[nativeLoop] <> 0; + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readDiscreteReq) THEN + RETURN; +END_IF + +//Do request processing +readDiscreteReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 9df094f9-a173-4520-816f-7badcbb1bd34_Impl_LineIds + + + + + METHOD processRequest + + 9df094f9-a173-4520-816f-7badcbb1bd34_Decl_LineIds + + + + 1a3278f4-a658-407c-968a-21aa53361562 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadDiscrete + + -1 + + + False + + d34866d4-1134-4650-b3d6-2e19d2fd09d1 + 1a3278f4-a658-407c-968a-21aa53361562 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148362063 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readDiscreteReq <> 0 THEN + __DELETE(readDiscreteReq); + readDiscreteReq := 0; +END_IF +readDiscreteReq := __NEW(ModbusFB.ClientRequestReadDiscreteInputs); +recreateRequest := readDiscreteReq; + d34866d4-1134-4650-b3d6-2e19d2fd09d1_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + d34866d4-1134-4650-b3d6-2e19d2fd09d1_Decl_LineIds + + + + 1a3278f4-a658-407c-968a-21aa53361562 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadDiscrete + + -1 + + + False + + 7b978176-d206-4509-b9d8-7908d33a9a74 + 73b10819-9112-4811-9583-e2735d2b13ea + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148402056 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(3); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := WORD_TO_UINT(client.nativeIO[nativeLoop]); + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readHoldReq) THEN + RETURN; +END_IF + +//Do request processing +readHoldReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + // udiReplyTimeout := 1 * 1000 * 1000, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 7b978176-d206-4509-b9d8-7908d33a9a74_Impl_LineIds + + + + + METHOD processRequest + + 7b978176-d206-4509-b9d8-7908d33a9a74_Decl_LineIds + + + + 73b10819-9112-4811-9583-e2735d2b13ea + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadHolding + + -1 + + + False + + 52ffdba7-bf4a-4746-924c-8b86df6d9a83 + 73b10819-9112-4811-9583-e2735d2b13ea + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148422060 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readHoldReq <> 0 THEN + __DELETE(readHoldReq); + readHoldReq := 0; +END_IF +readHoldReq := __NEW(ModbusFB.ClientRequestReadHoldingRegisters); +recreateRequest := readHoldReq; + 52ffdba7-bf4a-4746-924c-8b86df6d9a83_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 52ffdba7-bf4a-4746-924c-8b86df6d9a83_Decl_LineIds + + + + 73b10819-9112-4811-9583-e2735d2b13ea + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadHolding + + -1 + + + False + + 1004c837-fbef-45ee-986d-977343f4bd8f + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148442038 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(4); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := WORD_TO_UINT(client.nativeIO[nativeLoop]); + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readInputReq) THEN + RETURN; +END_IF + +//Do request processing +readInputReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 1004c837-fbef-45ee-986d-977343f4bd8f_Impl_LineIds + + + + + METHOD processRequest + + 1004c837-fbef-45ee-986d-977343f4bd8f_Decl_LineIds + + + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadInput + + -1 + + + False + + 9903b949-e1b9-4e0b-a98f-b6f745ad3605 + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148462074 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readInputReq <> 0 THEN + __DELETE(readInputReq); + readInputReq := 0; +END_IF +readInputReq := __NEW(ModbusFB.ClientRequestReadInputRegisters); +recreateRequest := readInputReq; + 9903b949-e1b9-4e0b-a98f-b6f745ad3605_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 9903b949-e1b9-4e0b-a98f-b6f745ad3605_Decl_LineIds + + + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadInput + + -1 + + + False + + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + resetStats + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148542250 + + + + + doneCnt := 0; +doneCntRst := 0; +errorCnt := 0; +errorCntRst := 0; + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec_Impl_LineIds + + + + + METHOD resetStats +VAR_INPUT +END_VAR + + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + b1508d1f-b7a7-40b5-b74f-3091afc5b834 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + setClientOffline + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148552305 + + + + + nativeResponseSeen := FALSE; +doneCntRst := 0; +online := FALSE; +offline := TRUE; + b1508d1f-b7a7-40b5-b74f-3091afc5b834_Impl_LineIds + + + + + METHOD setClientOffline : BOOL +VAR_INPUT +END_VAR + + b1508d1f-b7a7-40b5-b74f-3091afc5b834_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 1c6ab99e-dc71-45a2-a024-45cf65b9af99 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + setToString + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148572279 + + + + + toString := CONCAT('OK:', CONCAT(TO_STRING(doneCnt), CONCAT('/Er:', TO_STRING(errorCnt)))); + 1c6ab99e-dc71-45a2-a024-45cf65b9af99_Impl_LineIds + + + + + METHOD PRIVATE setToString : BOOL +VAR_INPUT +END_VAR + + 1c6ab99e-dc71-45a2-a024-45cf65b9af99_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + execute + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043399503 + + + + + PROPERTY execute : BOOL + 89434b6a-7e3b-4095-9220-0a6fc3789a25_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqBusy + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043449644 + + + + + PROPERTY reqBusy : BOOL + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqDone + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043519638 + + + + + PROPERTY reqDone : BOOL + 80965ce1-9b6a-44d3-b272-a5c739ccbc70_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqError + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043604994 + + + + + PROPERTY reqError : BOOL + 30645d91-91c6-42eb-8399-c20f91ca0d3e_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + fbb41e61-f245-4f4e-bcd0-218ede01fcda + f6ee860b-0951-41d0-93d8-6436c681d8f1 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148472030 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS AND NOT out_parameterError THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(stats.reqLength)-1 DO + client.nativeIO[nativeLoop] := BOOL_TO_WORD(data[UINT_TO_DINT(in_dataOffset)+nativeLoop]); + END_FOR; + END_IF; + nativeProcess(15); + RETURN; +END_IF; +IF NOT setupClient(writeCoilsReq) THEN + RETURN; +END_IF + +//Do request processing +writeCoilsReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + fbb41e61-f245-4f4e-bcd0-218ede01fcda_Impl_LineIds + + + + + METHOD processRequest + + fbb41e61-f245-4f4e-bcd0-218ede01fcda_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + a861d6e2-2501-43dc-bb37-34509923450c + f6ee860b-0951-41d0-93d8-6436c681d8f1 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148502269 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF writeCoilsReq <> 0 THEN + __DELETE(writeCoilsReq); + writeCoilsReq := 0; +END_IF +writeCoilsReq := __NEW(ModbusFB.ClientRequestWriteMultipleCoils); +recreateRequest := writeCoilsReq; + a861d6e2-2501-43dc-bb37-34509923450c_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + a861d6e2-2501-43dc-bb37-34509923450c_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + df9b4fd1-9d38-4a54-a7e5-264a94a756ac + f6ee860b-0951-41d0-93d8-6436c681d8f1 + writeNow + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591456658 + + + + + nativeWriteHold := FALSE; +out_writeUncertain := FALSE; +in_priorityCurrent := 0; + df9b4fd1-9d38-4a54-a7e5-264a94a756ac_Impl_LineIds + + + + + METHOD writeNow : BOOL +VAR_INPUT +END_VAR + + df9b4fd1-9d38-4a54-a7e5-264a94a756ac_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + 76621df2-a77c-4e0a-aba0-e3e4a53bd278 + bb7e18ba-2018-4681-875e-de1f291019ef + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148512269 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS AND NOT out_parameterError THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(stats.reqLength)-1 DO + client.nativeIO[nativeLoop] := UINT_TO_WORD(data[UINT_TO_DINT(in_dataOffset)+nativeLoop]); + END_FOR; + END_IF; + nativeProcess(16); + RETURN; +END_IF; +IF NOT setupClient(writeHoldReq) THEN + RETURN; +END_IF + +//Do request processing +writeHoldReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 76621df2-a77c-4e0a-aba0-e3e4a53bd278_Impl_LineIds + + + + + METHOD processRequest + + 76621df2-a77c-4e0a-aba0-e3e4a53bd278_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + bd7ff184-1747-404c-9fa0-1b728376c927 + bb7e18ba-2018-4681-875e-de1f291019ef + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148532258 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF writeHoldReq <> 0 THEN + __DELETE(writeHoldReq); + writeHoldReq := 0; +END_IF +writeHoldReq := __NEW(ModbusFB.ClientRequestWriteMultipleRegisters); +recreateRequest := writeHoldReq; + bd7ff184-1747-404c-9fa0-1b728376c927_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + bd7ff184-1747-404c-9fa0-1b728376c927_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + 5f9160f9-c4a4-4f65-a74c-2838020397f3 + bb7e18ba-2018-4681-875e-de1f291019ef + writeNow + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591496600 + + + + + nativeWriteHold := FALSE; +out_writeUncertain := FALSE; +in_priorityCurrent := 0; + 5f9160f9-c4a4-4f65-a74c-2838020397f3_Impl_LineIds + + + + + METHOD writeNow : BOOL +VAR_INPUT +END_VAR + + 5f9160f9-c4a4-4f65-a74c-2838020397f3_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590675071 + + + + + IF nativeMode THEN + activeIndex := nativeActiveIndex; + RETURN; +END_IF; +// ===== Test for any requests executing ===== +FOR i := 0 TO requestCnt -1 DO + IF requests[i]^.stats.execute OR requests[i]^.stats.reqBusy THEN + activeIndex := i; + RETURN; + END_IF +END_FOR +activeIndex := -1; + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c_Impl_LineIds + + + + + VAR + i: DINT; //Loop variable +END_VAR + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c_Decl_LineIds + + + True + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeIndex + + -1 + + + False + + 3b82f264-e41e-461d-9beb-263305a22a8d + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590705101 + + + + + // set request to active, which is how the active index gets calculated. +requests[activeIndex]^.stats.execute := TRUE; + 3b82f264-e41e-461d-9beb-263305a22a8d_Impl_LineIds + + + + + VAR +END_VAR + + 3b82f264-e41e-461d-9beb-263305a22a8d_Decl_LineIds + + + False + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeIndex + + -1 + + + False + + cc024b23-e5fe-4958-ad80-513feea6aeab + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146383170 + + + + + cnt := 0; +IF requestCnt = 0 THEN + activeReqCnt := 0; + RETURN; +END_IF +FOR i := 0 TO requestCnt -1 DO + //Test for any offline request + IF requests[i]^.stats.active THEN + cnt := cnt + 1; + END_IF +END_FOR; +activeReqCnt := cnt; + cc024b23-e5fe-4958-ad80-513feea6aeab_Impl_LineIds + + + + + VAR + i: UINT; //Loop variable + cnt: UINT; +END_VAR + + cc024b23-e5fe-4958-ad80-513feea6aeab_Decl_LineIds + + + True + + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeReqCnt + + -1 + + + False + + d59f404d-315d-4ea4-b16b-c8f0e8342d2e + 4d9d378a-f256-4f23-b835-539ee9e287c3 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147990941 + + + + + IF requestCnt = 0 THEN + allRequestsComplete := 0; + RETURN; +END_IF +FOR i := 0 TO requestCnt -1 DO + //Test for any offline request + IF requests[i]^.stats.execute OR requests[i]^.stats.reqDone OR requests[i]^.stats.reqBusy OR requests[i]^.stats.reqError THEN + allRequestsComplete := FALSE; + RETURN; + END_IF +END_FOR; +allRequestsComplete := TRUE; + d59f404d-315d-4ea4-b16b-c8f0e8342d2e_Impl_LineIds + + + + + VAR + i: UINT; +END_VAR + + + + + d59f404d-315d-4ea4-b16b-c8f0e8342d2e_Decl_LineIds + + + True + + 4d9d378a-f256-4f23-b835-539ee9e287c3 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + allRequestsComplete + + -1 + + + False + + dceb6b17-86bf-4543-9652-2fd2b943953c + 89742962-f9b6-46c2-b9a5-b7e298f8495d + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146538436 + + + + + cnt := 0; +IF requestCnt = 0 THEN + onlineCnt := 0; + RETURN; +END_IF +FOR i := 0 TO requestCnt -1 DO + //Test for any offline request + IF requests[i]^.stats.online THEN + cnt := cnt + 1; + END_IF +END_FOR; +onlineCnt := cnt; + dceb6b17-86bf-4543-9652-2fd2b943953c_Impl_LineIds + + + + + VAR + i: UINT; //Loop variable + cnt: UINT; +END_VAR + + dceb6b17-86bf-4543-9652-2fd2b943953c_Decl_LineIds + + + True + + 89742962-f9b6-46c2-b9a5-b7e298f8495d + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + onlineCnt + + -1 + + + False + + 0006696f-29e2-441e-a325-e3481d025cec + 27292866-0bc0-4848-a965-64c51a0bf960 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590890993 + + + + + // ===== Test for any requests executing ===== +requestExecuting := activeIndex <> -1; + 0006696f-29e2-441e-a325-e3481d025cec_Impl_LineIds + + + + + VAR + i: DINT; //Loop variable +END_VAR + + 0006696f-29e2-441e-a325-e3481d025cec_Decl_LineIds + + + True + + 27292866-0bc0-4848-a965-64c51a0bf960 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + requestExecuting + + -1 + + + False + + 3d4da579-672d-4758-9b0f-e055a08e0da3 + e8c6c80b-7bdb-4108-987e-4af107de4377 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574650278010 + + + + + //Calculation for tostring +IF onlineCnt = requestCnt THEN + toString := atMbClientToString.allOk; +ELSIF onlineCnt = 0 THEN + toString := atMbClientToString.noneOk; +ELSE + toString := atMbClientToString.someOk; +END_IF + 3d4da579-672d-4758-9b0f-e055a08e0da3_Impl_LineIds + + + + + + 3d4da579-672d-4758-9b0f-e055a08e0da3_Decl_LineIds + + + True + + e8c6c80b-7bdb-4108-987e-4af107de4377 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + toString + + -1 + + + False + + 4895a5bf-c631-4f8c-bee7-addb0b769548 + 6159c037-e399-466d-8108-0c4f6570f9b2 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574650328003 + + + + + IF NOT __ISVALIDREF(client) OR_ELSE NOT __ISVALIDREF(client.client) THEN + RETURN; +END_IF + +mbsClient REF= client.client; + 4895a5bf-c631-4f8c-bee7-addb0b769548_Impl_LineIds + + + + + VAR +END_VAR + + 4895a5bf-c631-4f8c-bee7-addb0b769548_Decl_LineIds + + + True + + 6159c037-e399-466d-8108-0c4f6570f9b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + mbsClient + + -1 + + + False + + f54a4cd6-8540-4892-a914-4aac4f8d9018 + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591226460 + + + + + +IF in_timeout > 0 THEN + //Explicitly set timout + replyTimeout := ANY_TO_UDINT((in_timeout) * 1000); +ELSE + // If client timeout is set, use that. + IF client.in_timeout <> T#0MS THEN + replyTimeout := TIME_TO_UDINT(client.in_timeout) * 1000; + ELSIF client.dataRate <> 0 THEN + //Data transmision time * 1.5 to get the timeout. Result is in ms + //8 byte request, then 255 byte reply = 2630 bits. + replyTimeout := REAL_TO_UDINT(2630 / DINT_TO_REAL(client.dataRate) * 1.5 * 1000000); + ELSE + //In case client datarate = zero + replyTimeout := 1000000; //1 second? + END_IF + + +END_IF + + + f54a4cd6-8540-4892-a914-4aac4f8d9018_Impl_LineIds + + + + + VAR +END_VAR + + f54a4cd6-8540-4892-a914-4aac4f8d9018_Decl_LineIds + + + True + + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + replyTimeout + + -1 + + + False + + 465bf80a-0d6e-4363-ba1e-dd04aef379f7 + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043419500 + + + + + execute := _execute; + 465bf80a-0d6e-4363-ba1e-dd04aef379f7_Impl_LineIds + + + + + VAR +END_VAR + + 465bf80a-0d6e-4363-ba1e-dd04aef379f7_Decl_LineIds + + + True + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + execute + + -1 + + + False + + 8ff5fbe7-c047-436d-923c-942d0d66494c + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043439650 + + + + + IF reqDone OR reqError THEN + _execute := FALSE; + RETURN; +END_IF +_execute := execute; + 8ff5fbe7-c047-436d-923c-942d0d66494c_Impl_LineIds + + + + + VAR +END_VAR + + 8ff5fbe7-c047-436d-923c-942d0d66494c_Decl_LineIds + + + False + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + execute + + -1 + + + False + + db10f23e-d96b-4fd5-aec0-8b530ad288e1 + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043479636 + + + + + reqBusy := _reqBusy; + db10f23e-d96b-4fd5-aec0-8b530ad288e1_Impl_LineIds + + + + + VAR +END_VAR + + db10f23e-d96b-4fd5-aec0-8b530ad288e1_Decl_LineIds + + + True + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqBusy + + -1 + + + False + + a239da3a-8f75-41da-a264-f06ca07143f4 + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043499661 + + + + + _reqBusy := reqBusy; + a239da3a-8f75-41da-a264-f06ca07143f4_Impl_LineIds + + + + + VAR +END_VAR + + a239da3a-8f75-41da-a264-f06ca07143f4_Decl_LineIds + + + False + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqBusy + + -1 + + + False + + 61db378f-6029-4712-b562-fea707e5698b + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043539625 + + + + + reqDone := _reqDone; + 61db378f-6029-4712-b562-fea707e5698b_Impl_LineIds + + + + + VAR +END_VAR + + 61db378f-6029-4712-b562-fea707e5698b_Decl_LineIds + + + True + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqDone + + -1 + + + False + + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3 + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147505410 + + + + + _reqDone := reqDone; + +IF (execute AND reqDone) THEN + // ===== Request has just completed succesfully ===== + //Update last valid duration + lastValidDuration := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + IF lastValidDuration > longestValidDuration THEN + longestValidDuration := lastValidDuration; + END_IF + //Counters + doneCnt := doneCnt + 1; + doneCntRst := doneCntRst + 1; + errorCntRst := 0; + + //Update error info + lastErrorId := modbusFB.Error.OK; + lastExceptionCode := atMBExceptionCodes.RESPONSE_SUCCESS; + + execute := FALSE; + setToString(); +END_IF + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3_Impl_LineIds + + + + + VAR +END_VAR + + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3_Decl_LineIds + + + False + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqDone + + -1 + + + False + + e44e83bd-8f84-4202-9ab4-784d09cf5506 + 30645d91-91c6-42eb-8399-c20f91ca0d3e + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043645000 + + + + + reqError := _reqError; + e44e83bd-8f84-4202-9ab4-784d09cf5506_Impl_LineIds + + + + + VAR +END_VAR + + e44e83bd-8f84-4202-9ab4-784d09cf5506_Decl_LineIds + + + True + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqError + + -1 + + + False + + 89b5f7bc-0c37-43f3-8984-14034b000d89 + 30645d91-91c6-42eb-8399-c20f91ca0d3e + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147595513 + + + + + _reqError := reqError; + +IF (execute AND reqError) THEN + // ===== Request has just completed with error ===== + // Counters + errorCnt := errorCnt + 1; + errorCntRst := errorCntRst + 1; + doneCntRst := 0; + //Turn off execute + execute := FALSE; + + //Since we are here there was an exception. If the modbus response is 0, that means no modbus communication happened, so we are offline. If we would have had a succesful communication we would have got a actual modbus error. + IF lastExceptionCode = atMBExceptionCodes.RESPONSE_SUCCESS THEN + timeoutCnt := timeoutCnt + 1; + lastExceptionCode := atMBExceptionCodes.TIMEOUT; + END_IF + setToString(); +END_IF + 89b5f7bc-0c37-43f3-8984-14034b000d89_Impl_LineIds + + + + + VAR +END_VAR + + 89b5f7bc-0c37-43f3-8984-14034b000d89_Decl_LineIds + + + False + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqError + + -1 + + + False + + f0be8440-a307-5b71-82c8-af27d8852e42 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + nativeCall + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeCall := CmpAtomModbus.atMbCall_cext( + command := command, handle := nativeHandle, ipAddress := nativeIP, + port := nativePort, unitId := nativeArgUnit, functionCode := nativeArgFunction, + startAddress := nativeArgAddress, quantity := nativeArgQuantity, + pData := ADR(nativeIO[0]), dataCapacity := 2000, + pStatus := ADR(nativeStatus[0]), statusCapacity := 24, nowMs := nativeNowMs, + connectTimeoutMs := nativeConnectTimeout, responseTimeoutMs := nativeArgTimeout, + retryDelayMs := nativeRetryDelay); + f0be8440-a307-5b71-82c8-af27d8852e42_Impl_LineIds + + + + + METHOD nativeCall : UDINT +VAR_INPUT + command : UDINT; +END_VAR + f0be8440-a307-5b71-82c8-af27d8852e42_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 1233447d-8796-56cf-9291-c959801d2a63 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + nativeCycle + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + SysTimeGetUs(nativeNowUs); +nativeNowMs := nativeNowUs / 1000; +IF in_resetStats THEN + resetStats(); + in_resetStats := FALSE; +END_IF; +IF nativeHandle = 0 AND nativeEnable THEN + rc := nativeCall(1); + IF rc <> 0 THEN + out_lastNativeError := rc; + RETURN; + END_IF; + nativeHandle := nativeStatus[0]; + nativeArgTimeout := 1000; + rc := nativeCall(9); + IF rc <> 0 THEN + out_lastNativeError := rc; + nativeCall(2); nativeHandle := 0; + RETURN; + END_IF; + nativeCall(3); +END_IF; +IF nativeHandle = 0 THEN + out_connected := FALSE; + out_connectionState := 0; + RETURN; +END_IF; +rc := nativeCall(5); +IF rc = 256 THEN + nativeHandle := 0; + IF nativeActiveRequest <> 0 THEN + nativeActiveRequest^.nativeFinish(10); + END_IF; + nativeActiveRequest := 0; nativeActiveIndex := -1; + RETURN; +ELSIF rc <> 0 THEN + out_lastNativeError := rc; + RETURN; +END_IF; +out_connectionState := nativeStatus[1]; +out_connected := (nativeStatus[1] >= 3) AND (nativeStatus[1] <= 5); +out_lastNativeError := nativeStatus[6]; +out_socketError := nativeStatus[7]; +out_reconnectCount := nativeStatus[11]; +IF nativePrevConnected AND NOT out_connected THEN + setAllRequestsOffline(); +END_IF; +nativePrevConnected := out_connected; +nativeFinishedIndex := -1; +nativeResultReady := (nativeActiveRequest <> 0) AND + ((nativeStatus[3] <> 0) OR (nativeStatus[4] <> 0)); +IF nativeResultReady AND (nativeStatus[3] <> 0) AND (nativeStatus[18] <= 4) THEN + rc := nativeCall(7); + IF rc <> 0 THEN + nativeStatus[3] := 0; + nativeStatus[4] := 1; + nativeStatus[5] := rc; + END_IF; +END_IF; +processAllRequests(); +IF nativeResultReady THEN + nativeFinishedIndex := nativeActiveIndex; + nativeCall(10); + nativeActiveRequest := 0; nativeActiveIndex := -1; + nativeSelected := 0; nativeResultReady := FALSE; +END_IF; +IF nativeEnable AND nativeStatus[1] = 3 AND nativeActiveRequest = 0 THEN + best := -1; priority := 32767; age := 0; + FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[j] <> 0 AND_THEN requests[j]^.stats.active + AND NOT requests[j]^.nativeWriteHold AND j <> nativeFinishedIndex THEN + IF requests[j]^.stats.sinceLastExec >= TIME_TO_UDINT(requests[j]^.in_minDelay) THEN + IF (requests[j]^.in_priorityCurrent < priority) OR + ((requests[j]^.in_priorityCurrent = priority) AND + (requests[j]^.stats.sinceLastExec > age)) THEN + best := j; priority := requests[j]^.in_priorityCurrent; + age := requests[j]^.stats.sinceLastExec; + END_IF; + END_IF; + END_IF; + END_FOR; + IF best >= 0 THEN + IF priority > 0 THEN + FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[j]^.in_priorityCurrent > 0 THEN + requests[j]^.in_priorityCurrent := requests[j]^.in_priorityCurrent - 1; + END_IF; + END_FOR; + END_IF; + nativeSelected := requests[best]; nativeActiveIndex := best; + nativeSelected^.stats.execute := TRUE; + nativeSelected^.processRequest(); + IF nativeActiveRequest = 0 THEN + nativeActiveIndex := -1; nativeSelected := 0; + END_IF; + END_IF; +END_IF; +allRequestsOnline := (activeReqCnt > 0) AND (onlineCnt = activeReqCnt); +allRequestsOffline := (activeReqCnt > 0) AND (onlineCnt = 0); +numMsgSent := nativeSent; numMsgReply := nativeReplied; +FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + reqStats[j] := ADR(requests[j]^.stats); +END_FOR; +IF nativeWindowStart = 0 THEN nativeWindowStart := nativeNowMs; END_IF; +IF nativeNowMs - nativeWindowStart >= 1000 THEN + msgLastSecondCnt := nativeWindowCount; msgLastSecondTime := nativeWindowTime; + nativeWindowCount := 0; nativeWindowTime := 0; nativeWindowStart := nativeNowMs; +END_IF; + 1233447d-8796-56cf-9291-c959801d2a63_Impl_LineIds + + + + + METHOD nativeCycle +VAR + j : DINT; + best : DINT; + priority : INT; + age : UDINT; + rc : UDINT; +END_VAR + 1233447d-8796-56cf-9291-c959801d2a63_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + c08712ba-3cf5-54e9-89a1-76a1c3183975 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + nativeRelease + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + IF nativeHandle <> 0 THEN + nativeCall(2); + IF nativeActiveRequest <> 0 THEN + nativeActiveRequest^.nativeFinish(10); + END_IF; + nativeHandle := 0; +END_IF; +nativeActiveRequest := 0; nativeSelected := 0; nativeActiveIndex := -1; +nativeResultReady := FALSE; +out_connected := FALSE; out_connectionState := 0; +setAllRequestsOffline(); +FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[j]^.invalidateNativeData(); +END_FOR; +nativeRelease := TRUE; + c08712ba-3cf5-54e9-89a1-76a1c3183975_Impl_LineIds + + + + + METHOD nativeRelease : BOOL +VAR + j : DINT; +END_VAR + c08712ba-3cf5-54e9-89a1-76a1c3183975_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + 2a6d360f-01c4-5b27-81fa-337b6b33a1b6 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + release + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + in_enable := FALSE; +release := nativeRelease(); + 2a6d360f-01c4-5b27-81fa-337b6b33a1b6_Impl_LineIds + + + + + METHOD release : BOOL + 2a6d360f-01c4-5b27-81fa-337b6b33a1b6_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + b2fda51f-6f2a-5b12-bc9c-2985dd3a44b4 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + FB_exit + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + FB_exit := nativeRelease(); + b2fda51f-6f2a-5b12-bc9c-2985dd3a44b4_Impl_LineIds + + + + + METHOD FB_exit : BOOL +VAR_INPUT + bInCopyCode : BOOL; +END_VAR + b2fda51f-6f2a-5b12-bc9c-2985dd3a44b4_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + 7568f58a-e795-5d22-a622-3f070deb32b0 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + FB_reinit + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeHandle := 0; +nativeActiveRequest := 0; nativeSelected := 0; nativeActiveIndex := -1; +nativeResultReady := FALSE; nativePrevConnected := FALSE; +requestCnt := 0; out_registrationFull := FALSE; +FOR j := 0 TO 99 DO requests[j] := 0; reqStats[j] := 0; END_FOR; +FB_reinit := TRUE; + 7568f58a-e795-5d22-a622-3f070deb32b0_Impl_LineIds + + + + + METHOD FB_reinit : BOOL +VAR + j : DINT; +END_VAR + 7568f58a-e795-5d22-a622-3f070deb32b0_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + 28ccddd0-4531-5168-aa3e-ac56dddcb68d + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + invalidateNativeData + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + out_validData := FALSE; +out_stale := TRUE; + 28ccddd0-4531-5168-aa3e-ac56dddcb68d_Impl_LineIds + + + + + METHOD PUBLIC invalidateNativeData + 28ccddd0-4531-5168-aa3e-ac56dddcb68d_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 1c14b96c-a77e-5cd3-b853-c18bf9b5ea87 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + nativeFinish + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + out_nativeError := result; +out_socketError := client.nativeStatus[7]; +out_writeUncertain := nativeInFlight AND (client.nativeStatus[9] <> 0); +IF (result = 10) AND nativeInFlight AND (nativeFunction >= 15) THEN + out_writeUncertain := TRUE; +END_IF; +IF out_writeUncertain AND client.nativeHoldUncertainWrites THEN + nativeWriteHold := TRUE; +END_IF; +stats.reqBusy := FALSE; +IF result = 0 THEN + duration := ULINT_TO_UDINT(client.nativeNowMs - nativeStartMs); + stats.lastValidDuration := duration; + IF duration > stats.longestValidDuration THEN stats.longestValidDuration := duration; END_IF; + client.nativeReplied := client.nativeReplied + 1; + client.nativeWindowTime := client.nativeWindowTime + duration; + stats.nativeResponseSeen := TRUE; + IF nativeFunction <= 4 THEN + nativeReadComplete := TRUE; + stats.nativeLastSuccessMs := client.nativeNowMs; + out_validData := TRUE; + END_IF; + done(TRUE); +ELSE + CASE result OF + 1,258: mapped := ModbusFB.Error.RequestParameterError; + 4,7: mapped := ModbusFB.Error.TcpSocketError; + 5: mapped := ModbusFB.Error.TcpConnectTimeout; + 6: mapped := ModbusFB.Error.ReplyTimeout; + 8: mapped := ModbusFB.Error.InvalidReply; + 9: mapped := ModbusFB.Error.RequestException; + 10: mapped := ModbusFB.Error.RequestCancelled; + ELSE mapped := ModbusFB.Error.InternalError; + END_CASE; + ex := ModbusFB.ExceptionCodes.RESPONSE_SUCCESS; + IF result = 9 THEN + CASE client.nativeStatus[8] OF + 1: ex := ModbusFB.ExceptionCodes.ILLEGAL_FUNCTION; + 2: ex := ModbusFB.ExceptionCodes.ILLEGAL_DATA_ADDRESS; + 3: ex := ModbusFB.ExceptionCodes.ILLEGAL_DATA_VALUE; + 4: ex := ModbusFB.ExceptionCodes.SLAVE_DEVICE_FAILURE; + 5: ex := ModbusFB.ExceptionCodes.ACKNOWLEDGE; + 6: ex := ModbusFB.ExceptionCodes.SLAVE_DEVICE_BUSY; + 8: ex := ModbusFB.ExceptionCodes.MEMORY_PARITY_ERROR; + 10: ex := ModbusFB.ExceptionCodes.GATEWAY_PATH_UNAVAILABLE; + 11: ex := ModbusFB.ExceptionCodes.GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND; + END_CASE; + client.nativeReplied := client.nativeReplied + 1; + stats.nativeResponseSeen := TRUE; + ELSE + stats.nativeResponseSeen := FALSE; + END_IF; + err(in := TRUE,errorId := mapped,exceptionCode := ex); +END_IF; +nativeInFlight := FALSE; +stats(); + 1c14b96c-a77e-5cd3-b853-c18bf9b5ea87_Impl_LineIds + + + + + METHOD nativeFinish +VAR_INPUT + result : UDINT; +END_VAR +VAR + mapped : ModbusFB.Error; + ex : ModbusFB.ExceptionCodes; + duration : UDINT; +END_VAR + 1c14b96c-a77e-5cd3-b853-c18bf9b5ea87_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + fd4b7cdb-3c7f-5110-af32-341f71321132 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + nativeProcess + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeReadComplete := FALSE; +stats.reqDone := FALSE; stats.reqError := FALSE; +stats(); +IF NOT client.out_connected THEN stats.setClientOffline(); END_IF; +IF client.nativeActiveRequest = THIS AND client.nativeResultReady THEN + IF (client.nativeStatus[3] <> 0) AND (nativeFunction <= 4) AND NOT nativeCopyAllowed THEN + nativeFinish(1); + ELSE + nativeFinish(client.nativeStatus[5]); + END_IF; + RETURN; +END_IF; +IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS THEN + nativeFunction := functionCode; + nativeCount := stats.reqLength; nativeOffset := in_dataOffset; + nativeStartMs := client.nativeNowMs; + IF out_parameterError OR nativeCount = 0 THEN nativeFinish(1); RETURN; END_IF; + client.nativeArgUnit := in_unitId; + client.nativeArgFunction := functionCode; + client.nativeArgAddress := in_startAddr; + client.nativeArgQuantity := nativeCount; + client.nativeArgTimeout := MAX(UDINT#1,(replyTimeout + 999)/1000); + nativeRC := client.nativeCall(11); + IF nativeRC = 0 THEN nativeRC := client.nativeCall(6); END_IF; + IF nativeRC = 0 THEN + client.nativeActiveRequest := THIS; + nativeInFlight := TRUE; + stats.reqBusy := TRUE; + client.nativeSent := client.nativeSent + 1; + client.nativeWindowCount := client.nativeWindowCount + 1; + ELSE nativeFinish(nativeRC); + END_IF; + stats(); +END_IF; +IF out_validData THEN + out_dataAgeMs := client.nativeNowMs - stats.nativeLastSuccessMs; + out_stale := out_dataAgeMs > TIME_TO_UDINT(in_staleAfter); +ELSE out_stale := TRUE; +END_IF; + fd4b7cdb-3c7f-5110-af32-341f71321132_Impl_LineIds + + + + + METHOD nativeProcess +VAR_INPUT + functionCode : BYTE; +END_VAR + fd4b7cdb-3c7f-5110-af32-341f71321132_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 9ee1081f-903d-595c-ab0c-17c64f4fbe75 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + FB_reinit + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeInFlight := FALSE; +stats.execute := FALSE; +stats.reqBusy := FALSE; +stats.reqDone := FALSE; +stats.reqError := FALSE; +FB_reinit := TRUE; + 9ee1081f-903d-595c-ab0c-17c64f4fbe75_Impl_LineIds + + + + + METHOD FB_reinit : BOOL + 9ee1081f-903d-595c-ab0c-17c64f4fbe75_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + CODESYS V3.5 SP21 Patch 4 + + + \ No newline at end of file diff --git a/compat/AtomModbus-Compatible.export b/compat/AtomModbus-Compatible.export new file mode 100644 index 0000000..cd8367c --- /dev/null +++ b/compat/AtomModbus-Compatible.export @@ -0,0 +1,5531 @@ + + + + + + + True + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + 00000000-0000-0000-0000-000000000000 + 6-Comms + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + e59dc300-e37a-4c37-997b-021563304d7e + + + + + + + + 738bea1e-99bb-4f04-90bb-a7a567e74e3a + + 639034841590379670 + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + e59dc300-e37a-4c37-997b-021563304d7e + + atomLibrary + + -1 + + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + 00000000-0000-0000-0000-000000000000 + ModbusMaster + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + + + + + + + + 738bea1e-99bb-4f04-90bb-a7a567e74e3a + + 639034841590449813 + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + + atomLibrary + 6-Comms + + -1 + + + False + + f228f401-6c74-4359-8ff1-b15b675180a4 + 00000000-0000-0000-0000-000000000000 + atMbClientToString + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 2db5746d-d284-4425-9f7f-2663a34b0ebc + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841591030946 + + + + + {attribute 'qualified_only'} +{attribute 'strict'} +TYPE atMbClientToString : +( + noneOk := 0, + someOk := 1, + allOk := 2 +); +END_TYPE + + f228f401-6c74-4359-8ff1-b15b675180a4_Decl_LineIds + + + 11 + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 43f52a7a-cd45-4c0a-8652-9b18c69324d7 + 00000000-0000-0000-0000-000000000000 + atMBExceptionCodes + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 2db5746d-d284-4425-9f7f-2663a34b0ebc + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590519807 + + + + + {attribute 'qualified_only'} +// {attribute 'strict'} NOT strict so can set with modbusFB.ExceptionCodes. +TYPE atMBExceptionCodes : +( + // Local / transport-level (non-Modbus) result + TIMEOUT := -1, // Communication timeout / no response + + // Modbus exception codes (returned in exception response PDU) + RESPONSE_SUCCESS := 16#00, // Everything OK (no exception) + + ILLEGAL_FUNCTION := 16#01, // Function code not supported by slave + ILLEGAL_DATA_ADDRESS := 16#02, // Data address not valid for slave (out of range / not mapped) + ILLEGAL_DATA_VALUE := 16#03, // Data value not valid (bad length/value/etc.) + SLAVE_DEVICE_FAILURE := 16#04, // Unrecoverable error while performing the requested action + + ACKNOWLEDGE := 16#05, // Request accepted; processing started (use to avoid master timeout) + SLAVE_DEVICE_BUSY := 16#06, // Slave busy (e.g. long operation in progress); retry later + + MEMORY_PARITY_ERROR := 16#08, // Memory parity error (special case; historically for FC 20/21) + + GATEWAY_PATH_UNAVAILABLE := 16#0A, // Gateway problem: path unavailable / misconfigured / overloaded + GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND := 16#0B // Gateway problem: target device did not respond +); +END_TYPE + + 43f52a7a-cd45-4c0a-8652-9b18c69324d7_Decl_LineIds + + + 78 + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + 00000000-0000-0000-0000-000000000000 + atMbClient + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639251309599709549 + + + None + + + IF nativeMode THEN + nativeCycle(); + RETURN; +END_IF; +//Modbus connection +//Connects to a modbus slave +//2024-11-23 ATM Cleaned up code +//2024-12-23 CWD Add statistics +//2025-06-18 CWD Add Get priorities and interval working together +//2026-03-26 ATM Clean up code, more testing +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +// ===== Main function call ===== + +IF (in_resetStats) THEN + resetStats(); + in_resetStats := FALSE; +END_IF + +// ===== Connect and disconnect logic ===== + +//Timer to trigger a connection if not connected for to long +reconnectTimer( + IN := NOT client.xConnect AND NOT client.xConnected AND NOT client.xError, + PT := RECONNECT_DELAY +); + +//Timer to trigger a disconnection if connect fails +disconnectTimer( + IN:=client.xConnect AND NOT client.xConnected, + PT:=DISCONNECT_DELAY +); + +//All requests offline timer +allRequestsOfflineTON( + IN := allRequestsOffline AND client.xConnected, + PT := ALL_OFFLINE_DELAY +); + +//Destroy Client delay timer +recreateClientDly( + IN:=destroyClient, + PT:=RECREATE_CLIENT_DELAY +); + +IF disconnectTimer.Q OR allRequestsOfflineTON.Q OR client.xError OR recreateTest THEN + //All requests are offline + //Connection attempt failed so disconnect + //If Client has error then disconnect + connect := FALSE; + recreateTest := FALSE; + destroyClient := TRUE; + setAllRequestsOffline(); +ELSIF reconnectTimer.Q AND NOT destroyClient THEN +//If not connected then attempt connect + connect := TRUE; + connectCnt := connectCnt + 1; //for debugging +END_IF; + + +IF recreateClientDly.Q THEN + destroyClient := FALSE; + recreateClient(); +END_IF; + +// If some reason gets stuck at busy, (This shouldnt happen, but force a reconnect). +forceConnect( + in := connect <> client.xConnect, + pt := FORCE_RECONNECT_DELAY +); + +// Only flip status of xConnect if no current request is active. +IF (connect <> client.xConnect) AND_THEN (allRequestsComplete OR forceConnect.Q) THEN + client.xConnect := connect; +END_IF + +// Moved this lower down, so xConnect happens one scan sooner. +client(); + +// If there are no requests, dont neeed to even do anything. +IF requestCnt = 0 THEN + numMsgSent := 0; + numMsgReply := 0; + allRequestsOnline := 0; + allRequestsOffline := 0; + RETURN; +END_IF + + +requestNotExecutingTON(IN := NOT requestExecuting, PT := NEXT_REQUEST_DELAY); //Only delaying 1 ms. This may not do much, was put in during testing, but now it almost seems unnecesary + +// ===== Execute next request ===== +IF connect AND client.xConnect AND client.xConnected AND (requestNotExecutingTON.Q OR NEXT_REQUEST_DELAY = T#0MS) THEN + + //Find the lowest priority of overdue requests + nextIdx := -1; + priorityLowest := PRIORITY_NONE; + highestTimeSinceRun := 0; + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF (requests[i]^.in_priorityCurrent < priorityLowest + OR (requests[i]^.in_priorityCurrent = priorityLowest AND requests[i]^.stats.sinceLastExec > highestTimeSinceRun)) + AND requests[i]^.stats.sinceLastExec > TIME_TO_UDINT(requests[i]^.in_minDelay) THEN + priorityLowest := requests[i]^.in_priorityCurrent; + highestTimeSinceRun := requests[i]^.stats.sinceLastExec; + nextIdx := i; + END_IF + END_FOR + + //Decriment all request priorities by one. + IF priorityLowest < PRIORITY_NONE AND priorityLowest > 0 THEN //Only if priority + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.in_priorityCurrent := requests[i]^.in_priorityCurrent - 1; + IF requests[i]^.in_priorityCurrent < 0 THEN + requests[i]^.in_priorityCurrent := 0; + END_IF + END_FOR + END_IF + + IF nextIdx >= 0 THEN + activeIndex := nextIdx; + END_IF + END_IF + +processAllRequests(); + +allRequestsOnline := (activeReqCnt > 0) AND (onlineCnt = activeReqCnt); +allRequestsOffline := (activeReqCnt > 0) AND (onlineCnt = 0); + +// ===== Client statistics ===== +//Get a sum of done and error counts from all requests +reqErrSum := 0; +reqDoneSum := 0; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + reqDoneSum := reqDoneSum + requests[i]^.stats.doneCnt; + reqErrSum := reqErrSum + requests[i]^.stats.errorCnt; + + //copy stats to array for debugging + reqStats[i] := ADR(requests[i]^.stats); +END_FOR + + + +//Every time the tx message counter changes increment msg count and add last transaction time +IF client.udiNumMsgSent <> msgCountLast THEN + msgCountLast := client.udiNumMsgSent; + msgTimeSum := msgTimeSum + client.udiLastTransactionTime; + msgCountSum := msgCountSum + 1; +END_IF + +//Every 1 second compute statistics +pulse(); +IF pulse.T_Sec_OS THEN + msgLastSecondCnt := msgCountSum; + msgCountSum := 0; + msgLastSecondTime := msgTimeSum; + msgTimeSum := 0; +END_IF + +numMsgSent := client.udiNumMsgSent; +numMsgReply := client.udiNumMsgReply; + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa_Impl_LineIds + + + + + {attribute 'monitoring_display' := 'toString'} +{attribute 'no_assign'} +FUNCTION_BLOCK ABSTRACT atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_timeout: TIME := T#0MS; // If this is set, this will be used on requests, otherwise it will be calculated from in_dataRate. + {attribute 'symbol' := 'none'} + in_resetStats: BOOL; // Set this bool to true to reset statistics. +END_VAR +VAR_OUTPUT + {attribute 'symbol' := 'none'} + msgLastSecondCnt: UDINT; //Messages in the last second + {attribute 'symbol' := 'none'} + msgLastSecondTime: UDINT; //Total amount of time for all messages sent last second in ms + {attribute 'symbol' := 'none'} + numMsgSent: UDINT; + {attribute 'symbol' := 'none'} + numMsgReply: UDINT; + {attribute 'symbol' := 'none'} + allRequestsOnline: BOOL; //All requests are working + //{attribute 'symbol' := 'none'} + reqStats: ARRAY[0..99] OF POINTER TO atMbRequestStats; // For debugging modbus requests +END_VAR +VAR + {attribute 'symbol' := 'none'} + datarate: DINT := 0; //Bits per second data rate. Used to calculate reply timeout. Based on amount of time to transmit holding register read for max amount of registers. Default 154705 for Modbus TCP = 17ms to transmit 2650 bits + {attribute 'symbol' := 'none'} + client: REFERENCE TO ModbusFB.Client; // This is generated internally, no need to set. + {attribute 'symbol' := 'none'} + connectCnt: DINT; //Number of connection attempts + {attribute 'symbol' := 'none'} + initDone: BOOL; + {attribute 'symbol' := 'none'} + disconnectTimer: TON; //Connecting but not connected delay + {attribute 'symbol' := 'none'} + reconnectTimer: TON; //Not connecting and not connected delay + {attribute 'symbol' := 'none'} + msgCountLast: UDINT; //Last message count + {attribute 'symbol' := 'none'} + msgCountSum: UDINT; //Message count accumulator + {attribute 'symbol' := 'none'} + msgTimeSum: UDINT; //Amount of time for messsages accumulator + {attribute 'symbol' := 'none'} + pulse: atPulse; //Pulse generator + {attribute 'symbol' := 'none'} + requests: ARRAY[0..99] OF POINTER TO atMbReqBase; //list of request to direct + {attribute 'symbol' := 'none'} + requestCnt: UINT; //Number of active requests + {attribute 'symbol' := 'none'} + reqDoneSum: UDINT; //Count of done result on all requests + {attribute 'symbol' := 'none'} + reqErrSum: UDINT; //Count of error result on all requests + {attribute 'symbol' := 'none'} + i: DINT; //Loop variable + {attribute 'symbol' := 'none'} + nextIdx: DINT := -1; //Index of the active request + {attribute 'symbol' := 'none'} + priorityLowest: INT; //lowest request priority + {attribute 'symbol' := 'none'} + highestTimeSinceRun: UDINT; //lowest request priority + {attribute 'symbol' := 'none'} + allRequestsOfflineTON: TON; //All requests are offline delay + {attribute 'symbol' := 'none'} + requestNotExecutingTON: TON; //Delay after there are no requests execting + {attribute 'symbol' := 'none'} + recreateClientDly: TON; //Delay to make new client after force fail + {attribute 'symbol' := 'none'} + allRequestsOffline: BOOL; //All requests are offline + {attribute 'symbol' := 'none'} + connect: BOOL; // this is more of a "Request to connect, Requet to disconnect", will drop xConnect once all requests are done, wont start requests unless this is true. + {attribute 'symbol' := 'none'} + forceConnect: TON; //If after 5 seconds, all requests are still ofline, just force a reconnect. + test1: REAL; + test2: REAL; + destroyClient: BOOL; + recreateTest: BOOL; +END_VAR +VAR CONSTANT + {attribute 'symbol' := 'none'} + NEXT_REQUEST_DELAY: TIME := T#0MS; // Dont seem to need a delay + {attribute 'symbol' := 'none'} + RECONNECT_DELAY: TIME := T#500MS; + {attribute 'symbol' := 'none'} + DISCONNECT_DELAY: TIME := T#500MS; + {attribute 'symbol' := 'none'} + RECREATE_CLIENT_DELAY: TIME := T#1000MS; + {attribute 'symbol' := 'none'} + ALL_OFFLINE_DELAY: TIME := T#2S; + {attribute 'symbol' := 'none'} + FORCE_RECONNECT_DELAY: TIME := T#1S; + {attribute 'symbol' := 'none'} + PRIORITY_NONE: INT := 32767; +END_VAR + +VAR +nativeMode : BOOL; +nativeHandle : UDINT; +nativeIP : DWORD; +nativePort : UINT; +nativeNowUs : SYSTIME; +nativeNowMs : ULINT; +nativeStatus : ARRAY[0..23] OF UDINT; +nativeActiveIndex : DINT := -1; +nativeSelected : POINTER TO atMbReqBase; +nativeResultReady : BOOL; +nativeFinishedIndex : DINT := -1; +nativeCallResult : UDINT; +nativeWindowStart : ULINT; +nativePrevConnected : BOOL; +nativeEnable : BOOL := TRUE; +nativeConnectTimeout : UDINT := 2000; +nativeRetryDelay : UDINT := 1000; +nativeHoldUncertainWrites : BOOL := FALSE; +END_VAR +VAR_INPUT +{attribute 'symbol' := 'none'} +nativeIO : ARRAY[0..1999] OF WORD; +{attribute 'symbol' := 'none'} +nativeActiveRequest : POINTER TO atMbReqBase; +{attribute 'symbol' := 'none'} +nativeArgUnit : BYTE; +{attribute 'symbol' := 'none'} +nativeArgFunction : BYTE; +{attribute 'symbol' := 'none'} +nativeArgAddress : UINT; +{attribute 'symbol' := 'none'} +nativeArgQuantity : UINT; +{attribute 'symbol' := 'none'} +nativeArgTimeout : UDINT := 1000; +{attribute 'symbol' := 'none'} +nativeSent : UDINT; +{attribute 'symbol' := 'none'} +nativeReplied : UDINT; +{attribute 'symbol' := 'none'} +nativeWindowCount : UDINT; +{attribute 'symbol' := 'none'} +nativeWindowTime : UDINT; +END_VAR + +VAR_OUTPUT +out_connected : BOOL; +out_connectionState : UDINT; +out_lastNativeError : UDINT; +out_socketError : UDINT; +out_reconnectCount : UDINT; +out_registrationFull : BOOL; +END_VAR + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa_Decl_LineIds + + + 3369 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + 00000000-0000-0000-0000-000000000000 + atMbClientRTU + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649591897 + + + None + + + //Modbus connection +//Connects to a modbus slave +//2024-06-27 CWD Initial create +//2024-11-13 CWD Fix problem with not connecting properly on power up +//2024-11-15 ATM Move from structure to defaults. +//2024-11-23 ATM Cleaned up code +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +IF NOT setupClient() THEN + RETURN; +END_IF + +//Set dataRate +//This is based on an 8 byte request and a 255 byte reply +dataRate := in_baudRate; + + +// ===== Initialization ===== +// if the param changes, force a reinit +IF _client^.iPort <> in_port + OR _client^.dwBaudrate <> in_baudRate + OR _client^.byDataBits <> in_dataBits + OR _client^.eParity <> in_parity + OR _client^.eStopBits <> in_stopBits THEN + initDone := FALSE; +END_IF; + +IF NOT initDone THEN + initDone := TRUE; + + //Other configuration parameters + _client^.iPort := in_port; + _client^.dwBaudrate := in_baudRate; + _client^.byDataBits := in_dataBits; + _client^.eParity := in_parity; + _client^.eStopBits := in_stopBits; + _client^.eDTRcontrol := 0; + _client^.eRTScontrol := 0; + _client^.eRTUASCII := 0; + _client^.udiLogOptions := ModbusFB.LoggingOptions.All; + _client^.xConnect:=FALSE; + +END_IF + +SUPER^(); + 17c2ab7a-4d60-48ec-966f-339ec83a75e9_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbClientRTU EXTENDS atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_port: BYTE := 0; //0=None, 1,2,3,4 + {attribute 'symbol' := 'none'} + in_baudRate: UINT := 9600; //4800, 9600, 19200, 38400, 57600, 115200 + {attribute 'symbol' := 'none'} + in_parity: BYTE := 0; //0=None, 1=Odd, 2=Even + {attribute 'symbol' := 'none'} + in_dataBits: BYTE := 8; //8=8 + {attribute 'symbol' := 'none'} + in_stopBits: BYTE := 1; //1=OneStopBit, 2=One5StopBits, 3=TwoStopBits +END_VAR +VAR + {attribute 'symbol' := 'none'} + _client: POINTER TO ModbusFB.ClientSerial; +END_VAR + 17c2ab7a-4d60-48ec-966f-339ec83a75e9_Decl_LineIds + + + 607 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + 00000000-0000-0000-0000-000000000000 + atMbClientTCP + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649747183 + + + None + + + nativeMode := TRUE; +nativeIP := SHL(BYTE_TO_DWORD(in_ipAddress[0]),24) OR + SHL(BYTE_TO_DWORD(in_ipAddress[1]),16) OR + SHL(BYTE_TO_DWORD(in_ipAddress[2]),8) OR BYTE_TO_DWORD(in_ipAddress[3]); +nativePort := in_port; +nativeConnectTimeout := MAX(UDINT#1,TIME_TO_UDINT(in_connectTimeout)); +nativeRetryDelay := MAX(UDINT#1,TIME_TO_UDINT(in_reconnectDelay)); +nativeHoldUncertainWrites := in_holdUncertainWrites; +IF nativeHandle <> 0 AND ((nativeEndpointIP <> nativeIP) OR + (nativeEndpointPort <> nativePort) OR (nativeConfigConnect <> nativeConnectTimeout) OR + (nativeConfigRetry <> nativeRetryDelay) OR NOT in_enable) THEN + nativeRelease(); +END_IF; +nativeEndpointIP := nativeIP; nativeEndpointPort := nativePort; +nativeConfigConnect := nativeConnectTimeout; nativeConfigRetry := nativeRetryDelay; +nativeEnable := in_enable; +IF in_baudrate <> 0 THEN dataRate := in_baudrate; ELSE dataRate := 105200; END_IF; +SUPER^(); + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbClientTCP EXTENDS atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_ipAddress: ARRAY[0..3] OF BYTE; // TCP IP Address + {attribute 'symbol' := 'none'} + in_port: UINT := 502; // TCP port + {attribute 'symbol' := 'none'} + in_baudrate: UINT := 0; // Set this if using RS485 to TCP Converter, used to calculate timeout. +END_VAR +VAR + {attribute 'symbol' := 'none'} + _client: POINTER TO ModbusFB.ClientTCP; +END_VAR +VAR_INPUT +in_enable : BOOL := TRUE; +in_connectTimeout : TIME := T#2S; +in_reconnectDelay : TIME := T#1S; +in_holdUncertainWrites : BOOL := FALSE; +END_VAR + +VAR +nativeEndpointIP : DWORD; +nativeEndpointPort : UINT; +nativeConfigConnect : UDINT; +nativeConfigRetry : UDINT; +END_VAR + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570_Decl_LineIds + + + 1168 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + 00000000-0000-0000-0000-000000000000 + atMbReqBase + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649837310 + + + None + + + //Modbus request +//Makes a request using the start and quantity, puts the result into data +//2024-06-27 CWD Initial create +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage + +IF NOT __ISVALIDREF(client) THEN + RETURN; +END_IF + + +//Add this request to the clients request array +client.addRequest(THIS); + +//fault logic +//Fault if error +//errTrig(CLK := stats.reqError); +//IF errTrig.Q THEN +// out_fault := TRUE; +//END_IF +//not faulted if a succesfull request +//doneTrig(CLK := stats.reqDone); +// also clear fault if not active +//IF doneTrig.Q OR NOT stats.active THEN +// out_fault := FALSE; +//END_IF + +IF in_quantity <= dataSize THEN + stats.reqLength := in_quantity; +ELSE + stats.reqLength := dataSize; +END_IF; + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK ABSTRACT atMbReqBase +VAR_INPUT + {attribute 'symbol' := 'none'} + client: REFERENCE TO atMbClient; // Instance of atMBClient + {attribute 'symbol' := 'none'} + in_startAddr: UINT; + {attribute 'symbol' := 'none'} + in_quantity: UINT; + {attribute 'symbol' := 'none'} + in_dataOffset: UINT; // Offset position for data into array. + {attribute 'symbol' := 'none'} + in_unitId: BYTE := 0; + {attribute 'symbol' := 'none'} + in_timeout: SYSTIME := 0; // timeout in ms, if not set, will be 1ms less than interval. + {attribute 'symbol' := 'none'} + in_minDelay: TIME := T#10MS; // This is the minimum delay between queries, only relevent if high priority. + {attribute 'symbol' := 'none'} + in_prioritySetpoint: INT := 50; // this requests priority compared to other requests. Lower value is higher priority. + {attribute 'symbol' := 'none'} + in_priorityCurrent: INT; //Current priority. This is decrimented and the request is executed when at zero. + {attribute 'symbol' := 'none'} + stats: atMbRequestStats; //Request stats + +END_VAR +VAR_OUTPUT + //{attribute 'symbol' := 'none'} + //out_fault: BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + dataSize: UINT; + //{attribute 'symbol' := 'none'} + //quantUsed: UINT; + //{attribute 'symbol' := 'none'} + //doneTrig: R_TRIG; //Succesful request trigger + //{attribute 'symbol' := 'none'} + //errTrig: R_TRIG; //Error request result trigger + + {attribute 'symbol' := 'none'} + request: REFERENCE TO ModbusFB.ClientRequest;; +END_VAR + + +VAR +nativeFunction : BYTE; +nativeCount : UINT; +nativeOffset : UINT; +nativeInFlight : BOOL; +nativeReadComplete : BOOL; +nativeCopyAllowed : BOOL; +nativeWriteHold : BOOL; +nativeStartMs : ULINT; +nativeLoop : DINT; +nativeRC : UDINT; +END_VAR + +VAR_OUTPUT +out_nativeError : UDINT; +out_socketError : UDINT; +out_writeUncertain : BOOL; +out_validData : BOOL; +out_dataAgeMs : ULINT; +out_stale : BOOL := TRUE; +out_parameterError : BOOL; +END_VAR + +VAR_INPUT +in_staleAfter : TIME := T#2S; +END_VAR + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37_Decl_LineIds + + + 3210 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + 00000000-0000-0000-0000-000000000000 + atMbReqReadCoil + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146913881 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-11-16 CWD Added read coil +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + + + a77a90fd-541d-4ce9-ad97-7024224bbab4_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadCoil EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readCoilReq: POINTER TO ModbusFB.ClientRequestReadCoils; +END_VAR + + a77a90fd-541d-4ce9-ad97-7024224bbab4_Decl_LineIds + + + 193 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 1a3278f4-a658-407c-968a-21aa53361562 + 00000000-0000-0000-0000-000000000000 + atMbReqReadDiscrete + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147014015 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + 1a3278f4-a658-407c-968a-21aa53361562_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadDiscrete EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readDiscreteReq: POINTER TO ModbusFB.ClientRequestReadDiscreteInputs; +END_VAR + + 1a3278f4-a658-407c-968a-21aa53361562_Decl_LineIds + + + 184 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 73b10819-9112-4811-9583-e2735d2b13ea + 00000000-0000-0000-0000-000000000000 + atMbReqReadHolding + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147099228 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + 73b10819-9112-4811-9583-e2735d2b13ea_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadHolding EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readHoldReq: POINTER TO ModbusFB.ClientRequestReadHoldingRegisters; +END_VAR + + 73b10819-9112-4811-9583-e2735d2b13ea_Decl_LineIds + + + 173 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + 00000000-0000-0000-0000-000000000000 + atMbReqReadInput + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147179217 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + a5dbc9b0-a1a1-4281-a685-ec85b35bb866_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadInput EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readInputReq: POINTER TO ModbusFB.ClientRequestReadInputRegisters; +END_VAR + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866_Decl_LineIds + + + 167 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + 00000000-0000-0000-0000-000000000000 + atMbRequestStats + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147425423 + + + None + + + SysTimeGetUs(TSSus); + +IF (reqLength = 0) THEN + active := FALSE; + doneCnt := 0; + errorCnt := 0; + lastErrorId := modbusFB.Error.OK; + lastExceptionCode := atMBExceptionCodes.RESPONSE_SUCCESS; + execute := FALSE; + online := FALSE; + offline := FALSE; + reqDone := FALSE; + reqBusy := FALSE; + reqError := FALSE; + sinceLastExec := 0; + RETURN; +END_IF +active := TRUE; + +inDoneDeb(in_raw := reqDone, in_delOn := T#0S, in_DelOff := lightOn); +reqDoneLight := inDoneDeb.out_val; + +inBusyDeb(in_raw := reqBusy, in_delOn := T#0S, in_DelOff := lightOn); +reqBusyLight := inBusyDeb.out_val; + +inErrDeb(in_raw := reqError, in_delOn := T#0S, in_DelOff := lightOn); +reqErrorLight := inErrDeb.out_val; + +//Debounced execute indicator for debbugging +executeDeb(in_raw := _execute, in_delOn := T#0S, in_DelOff := lightOn); +executeLight := executeDeb.out_val; + + +//Set online status +IF nativeResponseSeen OR doneCntRst >=3 THEN + online := TRUE; + offline := FALSE; +END_IF + +//Set online status +IF NOT nativeResponseSeen AND errorCntRst >= 3 THEN + online := FALSE; + offline := TRUE; +END_IF + +//Record start time in ms on execute rising edge +executeTrig(CLK := execute); +IF executeTrig.Q THEN + interval := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + lastReqStart := TSSus; +END_IF + +//Offline count +offlineTrig(CLK := NOT online); +IF offlineTrig.Q THEN + offlineCnt := offlineCnt + 1; +END_IF + +//Calculate ms since last execute +//If the last request start is more then 1 minute ago then set it for 1 minute +IF TSSus >= lastReqStart THEN + IF TSSus - lastReqStart > 60000000 THEN lastReqStart := TSSus - 60000000; END_IF; +ELSE lastReqStart := TSSus; +END_IF; +sinceLastExec := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + + 48b508fd-2131-484c-ba4a-20e61e66a5b2_Impl_LineIds + + + + + {attribute 'monitoring_display' := 'toString'} +{attribute 'no_assign'} +FUNCTION_BLOCK atMbRequestStats +VAR_INPUT + lastErrorId: modbusFB.Error; //Last error id + lastExceptionCode: atMBExceptionCodes; //Last modbus exception code. These are the standard modbus exceptions. + lastValidDuration: UDINT; // How long last transaction took (ms) + longestValidDuration: UDINT; //Longest valid duration (ms) + interval: UDINT; // Start-to-start interval (ms) + offlineCnt: UINT; //Count of how many times this request has gone offline + doneCnt: UDINT; // Count of successful request + errorCnt: UDINT; // Count of failed requests + doneCntRst: UDINT; // Count of succesful requests, resets on error result + errorCntRst: UDINT; // Count of failed requests, resets on succesful request + sinceLastExec: UDINT; // Time since the last start (ms) + timeoutCnt: UDINT; + reqLength: UINT; //Number of registers requested in request, used to calc active. +END_VAR +VAR_OUTPUT + executeLight: BOOL; //Execute that stays on momentarily + reqDoneLight: BOOL; //Request done that stays on momentarily + reqErrorLight: BOOL; //Request error that stays on momentarily + reqBusyLight: BOOL; //Request busy that stays on momentarily + online: BOOL; //Online means the requests are being replied to normally or with modbus exceptions. Communications are ok. + offline: BOOL; //Opposite of online, but starts out as false on program start. + active: BOOL; // This is true if its actually being used, and not having a count of 0 on the request. + executeMbFb: BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + inDoneDeb: atDebounce; + {attribute 'symbol' := 'none'} + inErrDeb: atDebounce; + {attribute 'symbol' := 'none'} + inBusyDeb: atDebounce; + {attribute 'symbol' := 'none'} + executeDeb: atDebounce; + toString: STRING(20); + {attribute 'symbol' := 'none'} + executeTrig: R_TRIG; //Execute trigger + {attribute 'symbol' := 'none'} + offlineTrig: R_TRIG; //Offline trigger + {attribute 'symbol' := 'none'} + lastReqStart: SYSTIME; //time of start of last request + {attribute 'symbol' := 'none'} + TSSus: SYSTIME; + _execute: BOOL; //Execute trigger and status. Turn on to trigger request. Will turn off when request is complete + _reqDone: BOOL; //Request done + _reqBusy: BOOL; //Request busy + _reqError: BOOL; //Request error +END_VAR + +VAR CONSTANT + {attribute 'symbol' := 'none'} + lightOn: TIME:= T#100MS; //Light on time for debug indicators +END_VAR +VAR_INPUT +nativeResponseSeen : BOOL; +nativeLastSuccessMs : ULINT; +END_VAR + + 48b508fd-2131-484c-ba4a-20e61e66a5b2_Decl_LineIds + + + 286 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + 00000000-0000-0000-0000-000000000000 + atMbReqWriteCoil + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147259331 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + f6ee860b-0951-41d0-93d8-6436c681d8f1_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqWriteCoil EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + writeCoilsReq: POINTER TO ModbusFB.ClientRequestWriteMultipleCoils; +END_VAR + + f6ee860b-0951-41d0-93d8-6436c681d8f1_Decl_LineIds + + + 206 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + bb7e18ba-2018-4681-875e-de1f291019ef + 00000000-0000-0000-0000-000000000000 + atMbReqWriteHolding + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147339348 + + + None + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +//Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + bb7e18ba-2018-4681-875e-de1f291019ef_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqWriteHolding EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + writeHoldReq: POINTER TO ModbusFB.ClientRequestWriteMultipleRegisters; +END_VAR + + bb7e18ba-2018-4681-875e-de1f291019ef_Decl_LineIds + + + 164 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 9499c60e-03a2-49d0-b086-43990a018e20 + 00000000-0000-0000-0000-000000000000 + To_IPAddress + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590489804 + + + None + + + //Convert parameters to array +To_IPAddress[0] := byte1; +To_IPAddress[1] := byte2; +To_IPAddress[2] := byte3; +To_IPAddress[3] := byte4; + + + + 9499c60e-03a2-49d0-b086-43990a018e20_Impl_LineIds + + + + + FUNCTION To_IPAddress : ARRAY[0..3] OF BYTE +VAR_INPUT + byte1: BYTE; + byte2: BYTE; + byte3: BYTE; + byte4: BYTE; +END_VAR + + 9499c60e-03a2-49d0-b086-43990a018e20_Decl_LineIds + + + 168 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 9e99e368-853f-485b-911f-d9a2a4e46434 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + addRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146468437 + + + + + IF request = 0 THEN RETURN; END_IF; +IF requestCnt > 0 THEN + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF request = requests[i] THEN RETURN; END_IF; + END_FOR; +END_IF; +IF requestCnt >= 100 THEN + out_registrationFull := TRUE; + RETURN; +END_IF; +requests[requestCnt] := request; +requestCnt := requestCnt + 1; + 9e99e368-853f-485b-911f-d9a2a4e46434_Impl_LineIds + + + + + //Adds the specified request to the global list +METHOD PUBLIC addRequest +VAR_INPUT + request: POINTER TO atMbReqBase; +END_VAR +VAR + i: UINT := 0; + +END_VAR + + 9e99e368-853f-485b-911f-d9a2a4e46434_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 685f7c69-c46a-4b34-b5e2-fb848c593f3e + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + processAllRequests + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148036583 + + + + + IF requestCnt = 0 OR requestCnt > 100 THEN RETURN; END_IF; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i] <> 0 THEN + requests[i]^.processRequest(); + END_IF; +END_FOR; + 685f7c69-c46a-4b34-b5e2-fb848c593f3e_Impl_LineIds + + + + + METHOD processAllRequests +VAR_INPUT +END_VAR +VAR + i: DINT; // Loop bstisnlr +END_VAR + 685f7c69-c46a-4b34-b5e2-fb848c593f3e_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 39769d9e-08c1-472d-82ef-25c66891a065 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + recreateAllRequests + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148056585 + + + + + IF requestCnt = 0 OR requestCnt > 100 THEN RETURN; END_IF; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i] <> 0 THEN + requests[i]^.recreateRequest(); + END_IF; +END_FOR; + 39769d9e-08c1-472d-82ef-25c66891a065_Impl_LineIds + + + + + METHOD PROTECTED recreateAllRequests +VAR_INPUT +END_VAR + +VAR + i : DINT; +END_VAR + + 39769d9e-08c1-472d-82ef-25c66891a065_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574648119824 + + + + + + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc_Impl_LineIds + + + + + METHOD ABSTRACT recreateClient +VAR_INPUT +END_VAR + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + a7e27967-573f-4f9f-86e7-dd569b8cd943 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + resetStats + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + IF nativeMode THEN + nativeSent := 0; nativeReplied := 0; + nativeWindowCount := 0; nativeWindowTime := 0; + msgLastSecondCnt := 0; msgLastSecondTime := 0; + numMsgSent := 0; numMsgReply := 0; + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.stats.resetStats(); + END_FOR; + RETURN; +END_IF; +connectCnt := 0; +val := ADR(client.udiNumMsgSent); +val^ := 0; +val := ADR(client.udiNumMsgReply); +val^ := 0; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.stats.resetStats(); +END_FOR; + a7e27967-573f-4f9f-86e7-dd569b8cd943_Impl_LineIds + + + + + METHOD resetStats +VAR_INPUT +END_VAR +VAR + i: DINT; + val: POINTER TO UDINT; +END_VAR + + + + + a7e27967-573f-4f9f-86e7-dd569b8cd943_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + ca1dfc70-ad93-43de-886d-39e56897aa6d + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + setAllRequestsOffline + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148106585 + + + + + IF requestCnt = 0 OR requestCnt > 100 THEN RETURN; END_IF; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i] <> 0 THEN + requests[i]^.stats.setClientOffline(); + END_IF; +END_FOR; + ca1dfc70-ad93-43de-886d-39e56897aa6d_Impl_LineIds + + + + + METHOD PROTECTED setAllRequestsOffline : BOOL +VAR_INPUT +END_VAR +VAR + i: DINT; +END_VAR + + + + + ca1dfc70-ad93-43de-886d-39e56897aa6d_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + d12c9bf2-320f-4e95-badb-68a2357c5783 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + setupClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574648239962 + + + + + IF nativeMode THEN + setupClient := nativeHandle <> 0; + RETURN; +END_IF; +// Only create request if doesnt exist +IF NOT __ISVALIDREF(client) THEN + recreateClient(); + IF NOT __ISVALIDREF(client) THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; + setupClient := FALSE; + RETURN; + END_IF +END_IF + +setupClient := TRUE; + d12c9bf2-320f-4e95-badb-68a2357c5783_Impl_LineIds + + + + + METHOD setupClient : BOOL +VAR_INPUT +END_VAR + + d12c9bf2-320f-4e95-badb-68a2357c5783_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + activeIndex + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590625089 + + + + + PROPERTY activeIndex : DINT + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32_Decl_LineIds + + + 1 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + activeReqCnt + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639108502146293051 + + + + + PROPERTY PRIVATE activeReqCnt : UINT + 456b4953-57c9-4f76-ab5b-0db456dd9b58_Decl_LineIds + + + 1 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 4d9d378a-f256-4f23-b835-539ee9e287c3 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + allRequestsComplete + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639108502147921016 + + + + + {attribute 'symbol' := 'none'} +PROPERTY PRIVATE allRequestsComplete : BOOL + 4d9d378a-f256-4f23-b835-539ee9e287c3_Decl_LineIds + + + 2 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 89742962-f9b6-46c2-b9a5-b7e298f8495d + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + onlineCnt + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590755417 + + + + + {attribute 'symbol' := 'none'} +PROPERTY onlineCnt : DINT//Count of requests that are online + 89742962-f9b6-46c2-b9a5-b7e298f8495d_Decl_LineIds + + + 3 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 27292866-0bc0-4848-a965-64c51a0bf960 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + requestExecuting + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590845466 + + + + + {attribute 'symbol' := 'none'} +PROPERTY PRIVATE requestExecuting : BOOL //There is at least one request executing + 27292866-0bc0-4848-a965-64c51a0bf960_Decl_LineIds + + + 3 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 4950a3a1-f54f-434d-9252-b61a6cb4663d + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649617058 + + + + + old_client_id := _client; +_client := __NEW(ModbusFB.ClientSerial); +IF _client = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; +END_IF; +client REF= _client^; +connect := FALSE; + +IF old_client_id <> 0 THEN + __DELETE(old_client_id); + old_client_id := 0; +END_IF + +recreateAllRequests(); + 4950a3a1-f54f-434d-9252-b61a6cb4663d_Impl_LineIds + + + + + METHOD recreateClient +VAR_INPUT + old_client_id: POINTER TO ModbusFB.ClientSerial; +END_VAR + + 4950a3a1-f54f-434d-9252-b61a6cb4663d_Decl_LineIds + + + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + + -1 + + + False + + e8c6c80b-7bdb-4108-987e-4af107de4377 + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + toString + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639207574650142720 + + + + + {attribute 'symbol' := 'none'} +PROPERTY toString : atMbClientToString //Client request status noneOk = no requests working, someOk = some requests are working some are failing, allOk = all requests are working + e8c6c80b-7bdb-4108-987e-4af107de4377_Decl_LineIds + + + 3 + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + + -1 + + + False + + 79dfe91d-6dbc-46b0-866c-4b880e44e02b + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649757233 + + + + + nativeRelease(); +// Next scan creates a fresh native context with current endpoint settings. + 79dfe91d-6dbc-46b0-866c-4b880e44e02b_Impl_LineIds + + + + + METHOD recreateClient +VAR +END_VAR + 79dfe91d-6dbc-46b0-866c-4b880e44e02b_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + a77f3143-e2cf-48a4-bafd-a0c18a11faeb + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + busy + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148176670 + + + + + stats.reqBusy := in; + +IF NOT in THEN + RETURN; +END_IF; + a77f3143-e2cf-48a4-bafd-a0c18a11faeb_Impl_LineIds + + + + + METHOD busy : BOOL +VAR_INPUT + in: BOOL; +END_VAR +VAR + executeTime: ULINT; + replyTimeoutTime: ULINT; +END_VAR + + + a77f3143-e2cf-48a4-bafd-a0c18a11faeb_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 5350a8fa-8297-42db-b185-fcb966e45684 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + completeRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148196661 + + + + + done(request.xDone); +err(in := request.xError, errorId := request.eErrorId, exceptionCode := request.eException); +busy(in := request.xBusy); +stats(); + +IF (atGVL.reset) THEN + // request.AbortModel(); + request.ResetModel(TRUE); +END_IF + 5350a8fa-8297-42db-b185-fcb966e45684_Impl_LineIds + + + + + METHOD completeRequest +VAR_INPUT +END_VAR + + 5350a8fa-8297-42db-b185-fcb966e45684_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + done + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148206667 + + + + + stats.reqDone := in; + +IF NOT in THEN + RETURN; +END_IF + +//Set priority for next request +in_priorityCurrent := in_prioritySetpoint; + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac_Impl_LineIds + + + + + METHOD done +VAR_INPUT + // Done state + in: BOOL; +END_VAR + + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + err + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148226656 + + + + + stats.reqError := in; + +IF NOT in THEN + RETURN; +END_IF + + +//Update error info +stats.lastErrorId := errorId; +stats.lastExceptionCode := ANY_TO_BYTE(exceptionCode); + +//Since we are here there was an exception. If the modbus response is 0, that means no modbus communication happened, so we are offline. If we would have had a succesful communication we would have got a actual modbus error. +//IF stats.lastExceptionCode = atMBExceptionCodes.RESPONSE_SUCCESS THEN + //Since there was no response from the device set the priority to higher number priority +// in_priorityCurrent := in_prioritySetPoint * 5; +//END_IF + +//Set priority for next request, priority gets higher after each error result +priorityMultiplier := UDINT_TO_INT(stats.errorCntRst); +IF priorityMultiplier > 3 THEN + priorityMultiplier := 3; +END_IF +// set multiplier to exponential retry. +in_priorityCurrent := 10 * priorityMultiplier * priorityMultiplier; +//in_priorityCurrent := in_prioritySetpoint * priorityMultiplier; + +// NO idea what Reset Model does, hoping it fixes comms issues +// request.ResetModel(TRUE); // I dont think this helped + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c_Impl_LineIds + + + + + METHOD err +VAR_INPUT + // Error state + in: BOOL; + errorId: modbusFB.Error; //modbusFB error ID + exceptionCode: modbusFB.ExceptionCodes; //ModbusFB exception code +END_VAR +VAR + priorityMultiplier: INT; //Raise priority when not response +END_VAR + + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 109d8e00-bf23-4306-a8d5-764aa6a03235 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148246723 + + + + + + 109d8e00-bf23-4306-a8d5-764aa6a03235_Impl_LineIds + + + + + METHOD ABSTRACT processRequest +VAR_INPUT +END_VAR + + 109d8e00-bf23-4306-a8d5-764aa6a03235_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148266663 + + + + + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8_Impl_LineIds + + + + + METHOD ABSTRACT recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + db40c392-b461-4198-851d-85857ed98ccf + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + setupClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148276644 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + setupClient := FALSE; // Native path has no ModbusFB request object. + RETURN; +END_IF; +// Only create request if doesnt exist +IF in_request = 0 THEN + in_request := recreateRequest(); + IF in_request = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; + setupClient := FALSE; + RETURN; + END_IF +END_IF + +IF NOT __ISVALIDREF(mbsClient) THEN + setupClient := FALSE; + RETURN; +END_IF + +request REF= in_request^; + +setupClient := TRUE; +RETURN; + db40c392-b461-4198-851d-85857ed98ccf_Impl_LineIds + + + + + METHOD setupClient : BOOL +VAR_INPUT + in_request: POINTER TO ModbusFB.ClientRequest; +END_VAR + + db40c392-b461-4198-851d-85857ed98ccf_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 6159c037-e399-466d-8108-0c4f6570f9b2 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + mbsClient + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639207574650298004 + + + + + PROPERTY PROTECTED mbsClient : REFERENCE TO ModbusFB.Client + 6159c037-e399-466d-8108-0c4f6570f9b2_Decl_LineIds + + + 1 + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + replyTimeout + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841591196333 + + + + + //Calculates the reply timout setting +PROPERTY PROTECTED replyTimeout : UDINT + 4f361885-f95c-4244-84d6-8c3b6b9b8a18_Decl_LineIds + + + 2 + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + ea5277e9-2ae8-417f-a88d-b571902c7d71 + a77a90fd-541d-4ce9-ad97-7024224bbab4 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639163341699321592 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(1); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := client.nativeIO[nativeLoop] <> 0; + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readCoilReq) THEN + RETURN; +END_IF + +//Do request processing +readCoilReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + ea5277e9-2ae8-417f-a88d-b571902c7d71_Impl_LineIds + + + + + METHOD processRequest + + ea5277e9-2ae8-417f-a88d-b571902c7d71_Decl_LineIds + + + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadCoil + + -1 + + + False + + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3 + a77a90fd-541d-4ce9-ad97-7024224bbab4 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148331930 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readCoilReq <> 0 THEN + __DELETE(readCoilReq); + readCoilReq := 0; +END_IF +readCoilReq := __NEW(ModbusFB.ClientRequestReadCoils); +recreateRequest := readCoilReq; + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3_Decl_LineIds + + + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadCoil + + -1 + + + False + + 9df094f9-a173-4520-816f-7badcbb1bd34 + 1a3278f4-a658-407c-968a-21aa53361562 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639163341699461941 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(2); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := client.nativeIO[nativeLoop] <> 0; + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readDiscreteReq) THEN + RETURN; +END_IF + +//Do request processing +readDiscreteReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 9df094f9-a173-4520-816f-7badcbb1bd34_Impl_LineIds + + + + + METHOD processRequest + + 9df094f9-a173-4520-816f-7badcbb1bd34_Decl_LineIds + + + + 1a3278f4-a658-407c-968a-21aa53361562 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadDiscrete + + -1 + + + False + + d34866d4-1134-4650-b3d6-2e19d2fd09d1 + 1a3278f4-a658-407c-968a-21aa53361562 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148362063 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readDiscreteReq <> 0 THEN + __DELETE(readDiscreteReq); + readDiscreteReq := 0; +END_IF +readDiscreteReq := __NEW(ModbusFB.ClientRequestReadDiscreteInputs); +recreateRequest := readDiscreteReq; + d34866d4-1134-4650-b3d6-2e19d2fd09d1_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + d34866d4-1134-4650-b3d6-2e19d2fd09d1_Decl_LineIds + + + + 1a3278f4-a658-407c-968a-21aa53361562 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadDiscrete + + -1 + + + False + + 7b978176-d206-4509-b9d8-7908d33a9a74 + 73b10819-9112-4811-9583-e2735d2b13ea + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148402056 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(3); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := WORD_TO_UINT(client.nativeIO[nativeLoop]); + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readHoldReq) THEN + RETURN; +END_IF + +//Do request processing +readHoldReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + // udiReplyTimeout := 1 * 1000 * 1000, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 7b978176-d206-4509-b9d8-7908d33a9a74_Impl_LineIds + + + + + METHOD processRequest + + 7b978176-d206-4509-b9d8-7908d33a9a74_Decl_LineIds + + + + 73b10819-9112-4811-9583-e2735d2b13ea + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadHolding + + -1 + + + False + + 52ffdba7-bf4a-4746-924c-8b86df6d9a83 + 73b10819-9112-4811-9583-e2735d2b13ea + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148422060 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readHoldReq <> 0 THEN + __DELETE(readHoldReq); + readHoldReq := 0; +END_IF +readHoldReq := __NEW(ModbusFB.ClientRequestReadHoldingRegisters); +recreateRequest := readHoldReq; + 52ffdba7-bf4a-4746-924c-8b86df6d9a83_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 52ffdba7-bf4a-4746-924c-8b86df6d9a83_Decl_LineIds + + + + 73b10819-9112-4811-9583-e2735d2b13ea + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadHolding + + -1 + + + False + + 1004c837-fbef-45ee-986d-977343f4bd8f + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148442038 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + nativeProcess(4); + IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO + data[UINT_TO_DINT(nativeOffset)+nativeLoop] := WORD_TO_UINT(client.nativeIO[nativeLoop]); + END_FOR; + END_IF; + RETURN; +END_IF; +IF NOT setupClient(readInputReq) THEN + RETURN; +END_IF + +//Do request processing +readInputReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 1004c837-fbef-45ee-986d-977343f4bd8f_Impl_LineIds + + + + + METHOD processRequest + + 1004c837-fbef-45ee-986d-977343f4bd8f_Decl_LineIds + + + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadInput + + -1 + + + False + + 9903b949-e1b9-4e0b-a98f-b6f745ad3605 + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148462074 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF readInputReq <> 0 THEN + __DELETE(readInputReq); + readInputReq := 0; +END_IF +readInputReq := __NEW(ModbusFB.ClientRequestReadInputRegisters); +recreateRequest := readInputReq; + 9903b949-e1b9-4e0b-a98f-b6f745ad3605_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 9903b949-e1b9-4e0b-a98f-b6f745ad3605_Decl_LineIds + + + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadInput + + -1 + + + False + + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + resetStats + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148542250 + + + + + doneCnt := 0; +doneCntRst := 0; +errorCnt := 0; +errorCntRst := 0; + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec_Impl_LineIds + + + + + METHOD resetStats +VAR_INPUT +END_VAR + + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + b1508d1f-b7a7-40b5-b74f-3091afc5b834 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + setClientOffline + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148552305 + + + + + nativeResponseSeen := FALSE; +doneCntRst := 0; +online := FALSE; +offline := TRUE; + b1508d1f-b7a7-40b5-b74f-3091afc5b834_Impl_LineIds + + + + + METHOD setClientOffline : BOOL +VAR_INPUT +END_VAR + + b1508d1f-b7a7-40b5-b74f-3091afc5b834_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 1c6ab99e-dc71-45a2-a024-45cf65b9af99 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + setToString + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148572279 + + + + + toString := CONCAT('OK:', CONCAT(TO_STRING(doneCnt), CONCAT('/Er:', TO_STRING(errorCnt)))); + 1c6ab99e-dc71-45a2-a024-45cf65b9af99_Impl_LineIds + + + + + METHOD PRIVATE setToString : BOOL +VAR_INPUT +END_VAR + + 1c6ab99e-dc71-45a2-a024-45cf65b9af99_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + execute + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043399503 + + + + + PROPERTY execute : BOOL + 89434b6a-7e3b-4095-9220-0a6fc3789a25_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqBusy + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043449644 + + + + + PROPERTY reqBusy : BOOL + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqDone + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043519638 + + + + + PROPERTY reqDone : BOOL + 80965ce1-9b6a-44d3-b272-a5c739ccbc70_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqError + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043604994 + + + + + PROPERTY reqError : BOOL + 30645d91-91c6-42eb-8399-c20f91ca0d3e_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + fbb41e61-f245-4f4e-bcd0-218ede01fcda + f6ee860b-0951-41d0-93d8-6436c681d8f1 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148472030 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS AND NOT out_parameterError THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(stats.reqLength)-1 DO + client.nativeIO[nativeLoop] := BOOL_TO_WORD(data[UINT_TO_DINT(in_dataOffset)+nativeLoop]); + END_FOR; + END_IF; + nativeProcess(15); + RETURN; +END_IF; +IF NOT setupClient(writeCoilsReq) THEN + RETURN; +END_IF + +//Do request processing +writeCoilsReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + fbb41e61-f245-4f4e-bcd0-218ede01fcda_Impl_LineIds + + + + + METHOD processRequest + + fbb41e61-f245-4f4e-bcd0-218ede01fcda_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + a861d6e2-2501-43dc-bb37-34509923450c + f6ee860b-0951-41d0-93d8-6436c681d8f1 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148502269 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF writeCoilsReq <> 0 THEN + __DELETE(writeCoilsReq); + writeCoilsReq := 0; +END_IF +writeCoilsReq := __NEW(ModbusFB.ClientRequestWriteMultipleCoils); +recreateRequest := writeCoilsReq; + a861d6e2-2501-43dc-bb37-34509923450c_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + a861d6e2-2501-43dc-bb37-34509923450c_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + df9b4fd1-9d38-4a54-a7e5-264a94a756ac + f6ee860b-0951-41d0-93d8-6436c681d8f1 + writeNow + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591456658 + + + + + nativeWriteHold := FALSE; +out_writeUncertain := FALSE; +in_priorityCurrent := 0; + df9b4fd1-9d38-4a54-a7e5-264a94a756ac_Impl_LineIds + + + + + METHOD writeNow : BOOL +VAR_INPUT +END_VAR + + df9b4fd1-9d38-4a54-a7e5-264a94a756ac_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + 76621df2-a77c-4e0a-aba0-e3e4a53bd278 + bb7e18ba-2018-4681-875e-de1f291019ef + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148512269 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); + IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS AND NOT out_parameterError THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(stats.reqLength)-1 DO + client.nativeIO[nativeLoop] := UINT_TO_WORD(data[UINT_TO_DINT(in_dataOffset)+nativeLoop]); + END_FOR; + END_IF; + nativeProcess(16); + RETURN; +END_IF; +IF NOT setupClient(writeHoldReq) THEN + RETURN; +END_IF + +//Do request processing +writeHoldReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 76621df2-a77c-4e0a-aba0-e3e4a53bd278_Impl_LineIds + + + + + METHOD processRequest + + 76621df2-a77c-4e0a-aba0-e3e4a53bd278_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + bd7ff184-1747-404c-9fa0-1b728376c927 + bb7e18ba-2018-4681-875e-de1f291019ef + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148532258 + + + + + IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +IF writeHoldReq <> 0 THEN + __DELETE(writeHoldReq); + writeHoldReq := 0; +END_IF +writeHoldReq := __NEW(ModbusFB.ClientRequestWriteMultipleRegisters); +recreateRequest := writeHoldReq; + bd7ff184-1747-404c-9fa0-1b728376c927_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + bd7ff184-1747-404c-9fa0-1b728376c927_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + 5f9160f9-c4a4-4f65-a74c-2838020397f3 + bb7e18ba-2018-4681-875e-de1f291019ef + writeNow + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591496600 + + + + + nativeWriteHold := FALSE; +out_writeUncertain := FALSE; +in_priorityCurrent := 0; + 5f9160f9-c4a4-4f65-a74c-2838020397f3_Impl_LineIds + + + + + METHOD writeNow : BOOL +VAR_INPUT +END_VAR + + 5f9160f9-c4a4-4f65-a74c-2838020397f3_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590675071 + + + + + IF nativeMode THEN + activeIndex := nativeActiveIndex; + RETURN; +END_IF; +// ===== Test for any requests executing ===== +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i]^.stats.execute OR requests[i]^.stats.reqBusy THEN + activeIndex := i; + RETURN; + END_IF +END_FOR +activeIndex := -1; + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c_Impl_LineIds + + + + + VAR + i: DINT; //Loop variable +END_VAR + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c_Decl_LineIds + + + True + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeIndex + + -1 + + + False + + 3b82f264-e41e-461d-9beb-263305a22a8d + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590705101 + + + + + // set request to active, which is how the active index gets calculated. +requests[activeIndex]^.stats.execute := TRUE; + 3b82f264-e41e-461d-9beb-263305a22a8d_Impl_LineIds + + + + + VAR +END_VAR + + 3b82f264-e41e-461d-9beb-263305a22a8d_Decl_LineIds + + + False + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeIndex + + -1 + + + False + + cc024b23-e5fe-4958-ad80-513feea6aeab + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146383170 + + + + + cnt := 0; +IF requestCnt = 0 THEN + activeReqCnt := 0; + RETURN; +END_IF +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + //Test for any offline request + IF requests[i]^.stats.active THEN + cnt := cnt + 1; + END_IF +END_FOR; +activeReqCnt := cnt; + cc024b23-e5fe-4958-ad80-513feea6aeab_Impl_LineIds + + + + + VAR + i: UINT; //Loop variable + cnt: UINT; +END_VAR + + cc024b23-e5fe-4958-ad80-513feea6aeab_Decl_LineIds + + + True + + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeReqCnt + + -1 + + + False + + d59f404d-315d-4ea4-b16b-c8f0e8342d2e + 4d9d378a-f256-4f23-b835-539ee9e287c3 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147990941 + + + + + IF requestCnt = 0 THEN + allRequestsComplete := 0; + RETURN; +END_IF +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + //Test for any offline request + IF requests[i]^.stats.execute OR requests[i]^.stats.reqDone OR requests[i]^.stats.reqBusy OR requests[i]^.stats.reqError THEN + allRequestsComplete := FALSE; + RETURN; + END_IF +END_FOR; +allRequestsComplete := TRUE; + d59f404d-315d-4ea4-b16b-c8f0e8342d2e_Impl_LineIds + + + + + VAR + i: UINT; +END_VAR + + + + + d59f404d-315d-4ea4-b16b-c8f0e8342d2e_Decl_LineIds + + + True + + 4d9d378a-f256-4f23-b835-539ee9e287c3 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + allRequestsComplete + + -1 + + + False + + dceb6b17-86bf-4543-9652-2fd2b943953c + 89742962-f9b6-46c2-b9a5-b7e298f8495d + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146538436 + + + + + cnt := 0; +IF requestCnt = 0 THEN + onlineCnt := 0; + RETURN; +END_IF +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + //Test for any offline request + IF requests[i]^.stats.online THEN + cnt := cnt + 1; + END_IF +END_FOR; +onlineCnt := cnt; + dceb6b17-86bf-4543-9652-2fd2b943953c_Impl_LineIds + + + + + VAR + i: UINT; //Loop variable + cnt: UINT; +END_VAR + + dceb6b17-86bf-4543-9652-2fd2b943953c_Decl_LineIds + + + True + + 89742962-f9b6-46c2-b9a5-b7e298f8495d + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + onlineCnt + + -1 + + + False + + 0006696f-29e2-441e-a325-e3481d025cec + 27292866-0bc0-4848-a965-64c51a0bf960 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590890993 + + + + + // ===== Test for any requests executing ===== +requestExecuting := activeIndex <> -1; + 0006696f-29e2-441e-a325-e3481d025cec_Impl_LineIds + + + + + VAR + i: DINT; //Loop variable +END_VAR + + 0006696f-29e2-441e-a325-e3481d025cec_Decl_LineIds + + + True + + 27292866-0bc0-4848-a965-64c51a0bf960 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + requestExecuting + + -1 + + + False + + 3d4da579-672d-4758-9b0f-e055a08e0da3 + e8c6c80b-7bdb-4108-987e-4af107de4377 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574650278010 + + + + + //Calculation for tostring +IF onlineCnt = requestCnt THEN + toString := atMbClientToString.allOk; +ELSIF onlineCnt = 0 THEN + toString := atMbClientToString.noneOk; +ELSE + toString := atMbClientToString.someOk; +END_IF + 3d4da579-672d-4758-9b0f-e055a08e0da3_Impl_LineIds + + + + + + 3d4da579-672d-4758-9b0f-e055a08e0da3_Decl_LineIds + + + True + + e8c6c80b-7bdb-4108-987e-4af107de4377 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + toString + + -1 + + + False + + 4895a5bf-c631-4f8c-bee7-addb0b769548 + 6159c037-e399-466d-8108-0c4f6570f9b2 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574650328003 + + + + + IF NOT __ISVALIDREF(client) OR_ELSE NOT __ISVALIDREF(client.client) THEN + RETURN; +END_IF + +mbsClient REF= client.client; + 4895a5bf-c631-4f8c-bee7-addb0b769548_Impl_LineIds + + + + + VAR +END_VAR + + 4895a5bf-c631-4f8c-bee7-addb0b769548_Decl_LineIds + + + True + + 6159c037-e399-466d-8108-0c4f6570f9b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + mbsClient + + -1 + + + False + + f54a4cd6-8540-4892-a914-4aac4f8d9018 + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591226460 + + + + + +IF in_timeout > 0 THEN + //Explicitly set timout + replyTimeout := ANY_TO_UDINT((in_timeout) * 1000); +ELSE + // If client timeout is set, use that. + IF client.in_timeout <> T#0MS THEN + replyTimeout := TIME_TO_UDINT(client.in_timeout) * 1000; + ELSIF client.dataRate <> 0 THEN + //Data transmision time * 1.5 to get the timeout. Result is in ms + //8 byte request, then 255 byte reply = 2630 bits. + replyTimeout := REAL_TO_UDINT(2630 / DINT_TO_REAL(client.dataRate) * 1.5 * 1000000); + ELSE + //In case client datarate = zero + replyTimeout := 1000000; //1 second? + END_IF + + +END_IF + + + f54a4cd6-8540-4892-a914-4aac4f8d9018_Impl_LineIds + + + + + VAR +END_VAR + + f54a4cd6-8540-4892-a914-4aac4f8d9018_Decl_LineIds + + + True + + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + replyTimeout + + -1 + + + False + + 465bf80a-0d6e-4363-ba1e-dd04aef379f7 + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043419500 + + + + + execute := _execute; + 465bf80a-0d6e-4363-ba1e-dd04aef379f7_Impl_LineIds + + + + + VAR +END_VAR + + 465bf80a-0d6e-4363-ba1e-dd04aef379f7_Decl_LineIds + + + True + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + execute + + -1 + + + False + + 8ff5fbe7-c047-436d-923c-942d0d66494c + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043439650 + + + + + IF reqDone OR reqError THEN + _execute := FALSE; + RETURN; +END_IF +_execute := execute; + 8ff5fbe7-c047-436d-923c-942d0d66494c_Impl_LineIds + + + + + VAR +END_VAR + + 8ff5fbe7-c047-436d-923c-942d0d66494c_Decl_LineIds + + + False + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + execute + + -1 + + + False + + db10f23e-d96b-4fd5-aec0-8b530ad288e1 + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043479636 + + + + + reqBusy := _reqBusy; + db10f23e-d96b-4fd5-aec0-8b530ad288e1_Impl_LineIds + + + + + VAR +END_VAR + + db10f23e-d96b-4fd5-aec0-8b530ad288e1_Decl_LineIds + + + True + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqBusy + + -1 + + + False + + a239da3a-8f75-41da-a264-f06ca07143f4 + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043499661 + + + + + _reqBusy := reqBusy; + a239da3a-8f75-41da-a264-f06ca07143f4_Impl_LineIds + + + + + VAR +END_VAR + + a239da3a-8f75-41da-a264-f06ca07143f4_Decl_LineIds + + + False + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqBusy + + -1 + + + False + + 61db378f-6029-4712-b562-fea707e5698b + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043539625 + + + + + reqDone := _reqDone; + 61db378f-6029-4712-b562-fea707e5698b_Impl_LineIds + + + + + VAR +END_VAR + + 61db378f-6029-4712-b562-fea707e5698b_Decl_LineIds + + + True + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqDone + + -1 + + + False + + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3 + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147505410 + + + + + _reqDone := reqDone; + +IF (execute AND reqDone) THEN + // ===== Request has just completed succesfully ===== + //Update last valid duration + lastValidDuration := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + IF lastValidDuration > longestValidDuration THEN + longestValidDuration := lastValidDuration; + END_IF + //Counters + doneCnt := doneCnt + 1; + doneCntRst := doneCntRst + 1; + errorCntRst := 0; + + //Update error info + lastErrorId := modbusFB.Error.OK; + lastExceptionCode := atMBExceptionCodes.RESPONSE_SUCCESS; + + execute := FALSE; + setToString(); +END_IF + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3_Impl_LineIds + + + + + VAR +END_VAR + + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3_Decl_LineIds + + + False + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqDone + + -1 + + + False + + e44e83bd-8f84-4202-9ab4-784d09cf5506 + 30645d91-91c6-42eb-8399-c20f91ca0d3e + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043645000 + + + + + reqError := _reqError; + e44e83bd-8f84-4202-9ab4-784d09cf5506_Impl_LineIds + + + + + VAR +END_VAR + + e44e83bd-8f84-4202-9ab4-784d09cf5506_Decl_LineIds + + + True + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqError + + -1 + + + False + + 89b5f7bc-0c37-43f3-8984-14034b000d89 + 30645d91-91c6-42eb-8399-c20f91ca0d3e + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147595513 + + + + + _reqError := reqError; + +IF (execute AND reqError) THEN + // ===== Request has just completed with error ===== + // Counters + errorCnt := errorCnt + 1; + errorCntRst := errorCntRst + 1; + doneCntRst := 0; + //Turn off execute + execute := FALSE; + + //Since we are here there was an exception. If the modbus response is 0, that means no modbus communication happened, so we are offline. If we would have had a succesful communication we would have got a actual modbus error. + IF lastExceptionCode = atMBExceptionCodes.RESPONSE_SUCCESS THEN + timeoutCnt := timeoutCnt + 1; + lastExceptionCode := atMBExceptionCodes.TIMEOUT; + END_IF + setToString(); +END_IF + 89b5f7bc-0c37-43f3-8984-14034b000d89_Impl_LineIds + + + + + VAR +END_VAR + + 89b5f7bc-0c37-43f3-8984-14034b000d89_Decl_LineIds + + + False + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqError + + -1 + + + False + + f0be8440-a307-5b71-82c8-af27d8852e42 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + nativeCall + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeCall := CmpAtomModbus.atMbCall_cext( + command := command, handle := nativeHandle, ipAddress := nativeIP, + port := nativePort, unitId := nativeArgUnit, functionCode := nativeArgFunction, + startAddress := nativeArgAddress, quantity := nativeArgQuantity, + pData := ADR(nativeIO[0]), dataCapacity := 2000, + pStatus := ADR(nativeStatus[0]), statusCapacity := 24, nowMs := nativeNowMs, + connectTimeoutMs := nativeConnectTimeout, responseTimeoutMs := nativeArgTimeout, + retryDelayMs := nativeRetryDelay); + f0be8440-a307-5b71-82c8-af27d8852e42_Impl_LineIds + + + + + METHOD nativeCall : UDINT +VAR_INPUT + command : UDINT; +END_VAR + f0be8440-a307-5b71-82c8-af27d8852e42_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 1233447d-8796-56cf-9291-c959801d2a63 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + nativeCycle + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + SysTimeGetUs(nativeNowUs); +nativeNowMs := nativeNowUs / 1000; +IF in_resetStats THEN + resetStats(); + in_resetStats := FALSE; +END_IF; +IF nativeHandle = 0 AND nativeEnable THEN + rc := nativeCall(1); + IF rc <> 0 THEN + out_lastNativeError := rc; + RETURN; + END_IF; + nativeHandle := nativeStatus[0]; + nativeArgTimeout := 1000; + rc := nativeCall(9); + IF rc <> 0 THEN + out_lastNativeError := rc; + nativeCall(2); nativeHandle := 0; + RETURN; + END_IF; + nativeCall(3); +END_IF; +IF nativeHandle = 0 THEN + out_connected := FALSE; + out_connectionState := 0; + RETURN; +END_IF; +rc := nativeCall(5); +IF rc = 256 THEN + nativeHandle := 0; + IF nativeActiveRequest <> 0 THEN + nativeActiveRequest^.nativeFinish(10); + END_IF; + nativeActiveRequest := 0; nativeActiveIndex := -1; + RETURN; +ELSIF rc <> 0 THEN + out_lastNativeError := rc; + RETURN; +END_IF; +out_connectionState := nativeStatus[1]; +out_connected := (nativeStatus[1] >= 3) AND (nativeStatus[1] <= 5); +out_lastNativeError := nativeStatus[6]; +out_socketError := nativeStatus[7]; +out_reconnectCount := nativeStatus[11]; +IF nativePrevConnected AND NOT out_connected THEN + setAllRequestsOffline(); +END_IF; +nativePrevConnected := out_connected; +nativeFinishedIndex := -1; +nativeResultReady := (nativeActiveRequest <> 0) AND + ((nativeStatus[3] <> 0) OR (nativeStatus[4] <> 0)); +IF nativeResultReady AND (nativeStatus[3] <> 0) AND (nativeStatus[18] <= 4) THEN + rc := nativeCall(7); + IF rc <> 0 THEN + nativeStatus[3] := 0; + nativeStatus[4] := 1; + nativeStatus[5] := rc; + END_IF; +END_IF; +processAllRequests(); +IF nativeResultReady THEN + nativeFinishedIndex := nativeActiveIndex; + nativeCall(10); + nativeActiveRequest := 0; nativeActiveIndex := -1; + nativeSelected := 0; nativeResultReady := FALSE; +END_IF; +IF nativeEnable AND nativeStatus[1] = 3 AND nativeActiveRequest = 0 THEN + best := -1; priority := 32767; age := 0; + FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[j] <> 0 AND_THEN requests[j]^.stats.active + AND NOT requests[j]^.nativeWriteHold AND j <> nativeFinishedIndex THEN + IF requests[j]^.stats.sinceLastExec >= TIME_TO_UDINT(requests[j]^.in_minDelay) THEN + IF (requests[j]^.in_priorityCurrent < priority) OR + ((requests[j]^.in_priorityCurrent = priority) AND + (requests[j]^.stats.sinceLastExec > age)) THEN + best := j; priority := requests[j]^.in_priorityCurrent; + age := requests[j]^.stats.sinceLastExec; + END_IF; + END_IF; + END_IF; + END_FOR; + IF best >= 0 THEN + IF priority > 0 THEN + FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[j]^.in_priorityCurrent > 0 THEN + requests[j]^.in_priorityCurrent := requests[j]^.in_priorityCurrent - 1; + END_IF; + END_FOR; + END_IF; + nativeSelected := requests[best]; nativeActiveIndex := best; + nativeSelected^.stats.execute := TRUE; + nativeSelected^.processRequest(); + IF nativeActiveRequest = 0 THEN + nativeActiveIndex := -1; nativeSelected := 0; + END_IF; + END_IF; +END_IF; +allRequestsOnline := (activeReqCnt > 0) AND (onlineCnt = activeReqCnt); +allRequestsOffline := (activeReqCnt > 0) AND (onlineCnt = 0); +numMsgSent := nativeSent; numMsgReply := nativeReplied; +FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + reqStats[j] := ADR(requests[j]^.stats); +END_FOR; +IF nativeWindowStart = 0 THEN nativeWindowStart := nativeNowMs; END_IF; +IF nativeNowMs - nativeWindowStart >= 1000 THEN + msgLastSecondCnt := nativeWindowCount; msgLastSecondTime := nativeWindowTime; + nativeWindowCount := 0; nativeWindowTime := 0; nativeWindowStart := nativeNowMs; +END_IF; + 1233447d-8796-56cf-9291-c959801d2a63_Impl_LineIds + + + + + METHOD nativeCycle +VAR + j : DINT; + best : DINT; + priority : INT; + age : UDINT; + rc : UDINT; +END_VAR + 1233447d-8796-56cf-9291-c959801d2a63_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + c08712ba-3cf5-54e9-89a1-76a1c3183975 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + nativeRelease + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + IF nativeHandle <> 0 THEN + nativeCall(2); + IF nativeActiveRequest <> 0 THEN + nativeActiveRequest^.nativeFinish(10); + END_IF; + nativeHandle := 0; +END_IF; +nativeActiveRequest := 0; nativeSelected := 0; nativeActiveIndex := -1; +nativeResultReady := FALSE; +out_connected := FALSE; out_connectionState := 0; +setAllRequestsOffline(); +FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[j]^.invalidateNativeData(); +END_FOR; +nativeRelease := TRUE; + c08712ba-3cf5-54e9-89a1-76a1c3183975_Impl_LineIds + + + + + METHOD nativeRelease : BOOL +VAR + j : DINT; +END_VAR + c08712ba-3cf5-54e9-89a1-76a1c3183975_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + 2a6d360f-01c4-5b27-81fa-337b6b33a1b6 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + release + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + in_enable := FALSE; +release := nativeRelease(); + 2a6d360f-01c4-5b27-81fa-337b6b33a1b6_Impl_LineIds + + + + + METHOD release : BOOL + 2a6d360f-01c4-5b27-81fa-337b6b33a1b6_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + b2fda51f-6f2a-5b12-bc9c-2985dd3a44b4 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + FB_exit + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + FB_exit := nativeRelease(); + b2fda51f-6f2a-5b12-bc9c-2985dd3a44b4_Impl_LineIds + + + + + METHOD FB_exit : BOOL +VAR_INPUT + bInCopyCode : BOOL; +END_VAR + b2fda51f-6f2a-5b12-bc9c-2985dd3a44b4_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + 7568f58a-e795-5d22-a622-3f070deb32b0 + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + FB_reinit + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeHandle := 0; +nativeActiveRequest := 0; nativeSelected := 0; nativeActiveIndex := -1; +nativeResultReady := FALSE; nativePrevConnected := FALSE; +requestCnt := 0; out_registrationFull := FALSE; +FOR j := 0 TO 99 DO requests[j] := 0; reqStats[j] := 0; END_FOR; +FB_reinit := TRUE; + 7568f58a-e795-5d22-a622-3f070deb32b0_Impl_LineIds + + + + + METHOD FB_reinit : BOOL +VAR + j : DINT; +END_VAR + 7568f58a-e795-5d22-a622-3f070deb32b0_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + 28ccddd0-4531-5168-aa3e-ac56dddcb68d + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + invalidateNativeData + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + out_validData := FALSE; +out_stale := TRUE; + 28ccddd0-4531-5168-aa3e-ac56dddcb68d_Impl_LineIds + + + + + METHOD PUBLIC invalidateNativeData + 28ccddd0-4531-5168-aa3e-ac56dddcb68d_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 1c14b96c-a77e-5cd3-b853-c18bf9b5ea87 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + nativeFinish + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + out_nativeError := result; +out_socketError := client.nativeStatus[7]; +out_writeUncertain := nativeInFlight AND (client.nativeStatus[9] <> 0); +IF (result = 10) AND nativeInFlight AND (nativeFunction >= 15) THEN + out_writeUncertain := TRUE; +END_IF; +IF out_writeUncertain AND client.nativeHoldUncertainWrites THEN + nativeWriteHold := TRUE; +END_IF; +stats.reqBusy := FALSE; +IF result = 0 THEN + duration := ULINT_TO_UDINT(client.nativeNowMs - nativeStartMs); + stats.lastValidDuration := duration; + IF duration > stats.longestValidDuration THEN stats.longestValidDuration := duration; END_IF; + client.nativeReplied := client.nativeReplied + 1; + client.nativeWindowTime := client.nativeWindowTime + duration; + stats.nativeResponseSeen := TRUE; + IF nativeFunction <= 4 THEN + nativeReadComplete := TRUE; + stats.nativeLastSuccessMs := client.nativeNowMs; + out_validData := TRUE; + END_IF; + done(TRUE); +ELSE + CASE result OF + 1,258: mapped := ModbusFB.Error.RequestParameterError; + 4,7: mapped := ModbusFB.Error.TcpSocketError; + 5: mapped := ModbusFB.Error.TcpConnectTimeout; + 6: mapped := ModbusFB.Error.ReplyTimeout; + 8: mapped := ModbusFB.Error.InvalidReply; + 9: mapped := ModbusFB.Error.RequestException; + 10: mapped := ModbusFB.Error.RequestCancelled; + ELSE mapped := ModbusFB.Error.InternalError; + END_CASE; + ex := ModbusFB.ExceptionCodes.RESPONSE_SUCCESS; + IF result = 9 THEN + CASE client.nativeStatus[8] OF + 1: ex := ModbusFB.ExceptionCodes.ILLEGAL_FUNCTION; + 2: ex := ModbusFB.ExceptionCodes.ILLEGAL_DATA_ADDRESS; + 3: ex := ModbusFB.ExceptionCodes.ILLEGAL_DATA_VALUE; + 4: ex := ModbusFB.ExceptionCodes.SLAVE_DEVICE_FAILURE; + 5: ex := ModbusFB.ExceptionCodes.ACKNOWLEDGE; + 6: ex := ModbusFB.ExceptionCodes.SLAVE_DEVICE_BUSY; + 8: ex := ModbusFB.ExceptionCodes.MEMORY_PARITY_ERROR; + 10: ex := ModbusFB.ExceptionCodes.GATEWAY_PATH_UNAVAILABLE; + 11: ex := ModbusFB.ExceptionCodes.GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND; + END_CASE; + client.nativeReplied := client.nativeReplied + 1; + stats.nativeResponseSeen := TRUE; + ELSE + stats.nativeResponseSeen := FALSE; + END_IF; + err(in := TRUE,errorId := mapped,exceptionCode := ex); +END_IF; +nativeInFlight := FALSE; +stats(); + 1c14b96c-a77e-5cd3-b853-c18bf9b5ea87_Impl_LineIds + + + + + METHOD nativeFinish +VAR_INPUT + result : UDINT; +END_VAR +VAR + mapped : ModbusFB.Error; + ex : ModbusFB.ExceptionCodes; + duration : UDINT; +END_VAR + 1c14b96c-a77e-5cd3-b853-c18bf9b5ea87_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + fd4b7cdb-3c7f-5110-af32-341f71321132 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + nativeProcess + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeReadComplete := FALSE; +stats.reqDone := FALSE; stats.reqError := FALSE; +stats(); +IF NOT client.out_connected THEN stats.setClientOffline(); END_IF; +IF client.nativeActiveRequest = THIS AND client.nativeResultReady THEN + IF (client.nativeStatus[3] <> 0) AND (nativeFunction <= 4) AND NOT nativeCopyAllowed THEN + nativeFinish(1); + ELSE + nativeFinish(client.nativeStatus[5]); + END_IF; + RETURN; +END_IF; +IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS THEN + nativeFunction := functionCode; + nativeCount := stats.reqLength; nativeOffset := in_dataOffset; + nativeStartMs := client.nativeNowMs; + IF out_parameterError OR nativeCount = 0 THEN nativeFinish(1); RETURN; END_IF; + client.nativeArgUnit := in_unitId; + client.nativeArgFunction := functionCode; + client.nativeArgAddress := in_startAddr; + client.nativeArgQuantity := nativeCount; + client.nativeArgTimeout := MAX(UDINT#1,(replyTimeout + 999)/1000); + nativeRC := client.nativeCall(11); + IF nativeRC = 0 THEN nativeRC := client.nativeCall(6); END_IF; + IF nativeRC = 0 THEN + client.nativeActiveRequest := THIS; + nativeInFlight := TRUE; + stats.reqBusy := TRUE; + client.nativeSent := client.nativeSent + 1; + client.nativeWindowCount := client.nativeWindowCount + 1; + ELSE nativeFinish(nativeRC); + END_IF; + stats(); +END_IF; +IF out_validData THEN + out_dataAgeMs := client.nativeNowMs - stats.nativeLastSuccessMs; + out_stale := out_dataAgeMs > TIME_TO_UDINT(in_staleAfter); +ELSE out_stale := TRUE; +END_IF; + fd4b7cdb-3c7f-5110-af32-341f71321132_Impl_LineIds + + + + + METHOD nativeProcess +VAR_INPUT + functionCode : BYTE; +END_VAR + fd4b7cdb-3c7f-5110-af32-341f71321132_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 9ee1081f-903d-595c-ab0c-17c64f4fbe75 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + FB_reinit + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + nativeInFlight := FALSE; +stats.execute := FALSE; +stats.reqBusy := FALSE; +stats.reqDone := FALSE; +stats.reqError := FALSE; +FB_reinit := TRUE; + 9ee1081f-903d-595c-ab0c-17c64f4fbe75_Impl_LineIds + + + + + METHOD FB_reinit : BOOL + 9ee1081f-903d-595c-ab0c-17c64f4fbe75_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + CODESYS V3.5 SP21 Patch 4 + + + \ No newline at end of file diff --git a/compat/Original_HomeCodesys.export b/compat/Original_HomeCodesys.export new file mode 100644 index 0000000..0d3afae --- /dev/null +++ b/compat/Original_HomeCodesys.export @@ -0,0 +1,4646 @@ + + + + + + + True + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + 00000000-0000-0000-0000-000000000000 + 6-Comms + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + e59dc300-e37a-4c37-997b-021563304d7e + + + + + + + + 738bea1e-99bb-4f04-90bb-a7a567e74e3a + + 639034841590379670 + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + e59dc300-e37a-4c37-997b-021563304d7e + + atomLibrary + + -1 + + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + 00000000-0000-0000-0000-000000000000 + ModbusMaster + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + + + + + + + + 738bea1e-99bb-4f04-90bb-a7a567e74e3a + + 639034841590449813 + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + 98ed6c72-1d8a-444d-b9fb-44a679624f78 + + atomLibrary + 6-Comms + + -1 + + + False + + f228f401-6c74-4359-8ff1-b15b675180a4 + 00000000-0000-0000-0000-000000000000 + atMbClientToString + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 2db5746d-d284-4425-9f7f-2663a34b0ebc + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841591030946 + + + + + {attribute 'qualified_only'} +{attribute 'strict'} +TYPE atMbClientToString : +( + noneOk := 0, + someOk := 1, + allOk := 2 +); +END_TYPE + + f228f401-6c74-4359-8ff1-b15b675180a4_Decl_LineIds + + + 11 + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 43f52a7a-cd45-4c0a-8652-9b18c69324d7 + 00000000-0000-0000-0000-000000000000 + atMBExceptionCodes + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 2db5746d-d284-4425-9f7f-2663a34b0ebc + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590519807 + + + + + {attribute 'qualified_only'} +// {attribute 'strict'} NOT strict so can set with modbusFB.ExceptionCodes. +TYPE atMBExceptionCodes : +( + // Local / transport-level (non-Modbus) result + TIMEOUT := -1, // Communication timeout / no response + + // Modbus exception codes (returned in exception response PDU) + RESPONSE_SUCCESS := 16#00, // Everything OK (no exception) + + ILLEGAL_FUNCTION := 16#01, // Function code not supported by slave + ILLEGAL_DATA_ADDRESS := 16#02, // Data address not valid for slave (out of range / not mapped) + ILLEGAL_DATA_VALUE := 16#03, // Data value not valid (bad length/value/etc.) + SLAVE_DEVICE_FAILURE := 16#04, // Unrecoverable error while performing the requested action + + ACKNOWLEDGE := 16#05, // Request accepted; processing started (use to avoid master timeout) + SLAVE_DEVICE_BUSY := 16#06, // Slave busy (e.g. long operation in progress); retry later + + MEMORY_PARITY_ERROR := 16#08, // Memory parity error (special case; historically for FC 20/21) + + GATEWAY_PATH_UNAVAILABLE := 16#0A, // Gateway problem: path unavailable / misconfigured / overloaded + GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND := 16#0B // Gateway problem: target device did not respond +); +END_TYPE + + 43f52a7a-cd45-4c0a-8652-9b18c69324d7_Decl_LineIds + + + 78 + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + 00000000-0000-0000-0000-000000000000 + atMbClient + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639251309599709549 + + + None + + + //Modbus connection +//Connects to a modbus slave +//2024-11-23 ATM Cleaned up code +//2024-12-23 CWD Add statistics +//2025-06-18 CWD Add Get priorities and interval working together +//2026-03-26 ATM Clean up code, more testing +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +// ===== Main function call ===== + +IF (in_resetStats) THEN + resetStats(); + in_resetStats := FALSE; +END_IF + +// ===== Connect and disconnect logic ===== + +//Timer to trigger a connection if not connected for to long +reconnectTimer( + IN := NOT client.xConnect AND NOT client.xConnected AND NOT client.xError, + PT := RECONNECT_DELAY +); + +//Timer to trigger a disconnection if connect fails +disconnectTimer( + IN:=client.xConnect AND NOT client.xConnected, + PT:=DISCONNECT_DELAY +); + +//All requests offline timer +allRequestsOfflineTON( + IN := allRequestsOffline AND client.xConnected, + PT := ALL_OFFLINE_DELAY +); + +//Destroy Client delay timer +recreateClientDly( + IN:=destroyClient, + PT:=RECREATE_CLIENT_DELAY +); + +IF disconnectTimer.Q OR allRequestsOfflineTON.Q OR client.xError OR recreateTest THEN + //All requests are offline + //Connection attempt failed so disconnect + //If Client has error then disconnect + connect := FALSE; + recreateTest := FALSE; + destroyClient := TRUE; + setAllRequestsOffline(); +ELSIF reconnectTimer.Q AND NOT destroyClient THEN +//If not connected then attempt connect + connect := TRUE; + connectCnt := connectCnt + 1; //for debugging +END_IF; + + +IF recreateClientDly.Q THEN + destroyClient := FALSE; + recreateClient(); +END_IF; + +// If some reason gets stuck at busy, (This shouldnt happen, but force a reconnect). +forceConnect( + in := connect <> client.xConnect, + pt := FORCE_RECONNECT_DELAY +); + +// Only flip status of xConnect if no current request is active. +IF (connect <> client.xConnect) AND_THEN (allRequestsComplete OR forceConnect.Q) THEN + client.xConnect := connect; +END_IF + +// Moved this lower down, so xConnect happens one scan sooner. +client(); + +// If there are no requests, dont neeed to even do anything. +IF requestCnt = 0 THEN + numMsgSent := 0; + numMsgReply := 0; + allRequestsOnline := 0; + allRequestsOffline := 0; + RETURN; +END_IF + + +requestNotExecutingTON(IN := NOT requestExecuting, PT := NEXT_REQUEST_DELAY); //Only delaying 1 ms. This may not do much, was put in during testing, but now it almost seems unnecesary + +// ===== Execute next request ===== +IF connect AND client.xConnect AND client.xConnected AND (requestNotExecutingTON.Q OR NEXT_REQUEST_DELAY = T#0MS) THEN + + //Find the lowest priority of overdue requests + nextIdx := -1; + priorityLowest := PRIORITY_NONE; + highestTimeSinceRun := 0; + FOR i := 0 TO requestCnt - 1 DO + IF (requests[i]^.in_priorityCurrent < priorityLowest + OR (requests[i]^.in_priorityCurrent = priorityLowest AND requests[i]^.stats.sinceLastExec > highestTimeSinceRun)) + AND requests[i]^.stats.sinceLastExec > TIME_TO_UDINT(requests[i]^.in_minDelay) THEN + priorityLowest := requests[i]^.in_priorityCurrent; + highestTimeSinceRun := requests[i]^.stats.sinceLastExec; + nextIdx := i; + END_IF + END_FOR + + //Decriment all request priorities by one. + IF priorityLowest < PRIORITY_NONE AND priorityLowest > 0 THEN //Only if priority + FOR i := 0 TO requestCnt - 1 DO + requests[i]^.in_priorityCurrent := requests[i]^.in_priorityCurrent - 1; + IF requests[i]^.in_priorityCurrent < 0 THEN + requests[i]^.in_priorityCurrent := 0; + END_IF + END_FOR + END_IF + + IF nextIdx >= 0 THEN + activeIndex := nextIdx; + END_IF + END_IF + +processAllRequests(); + +allRequestsOnline := (activeReqCnt > 0) AND (onlineCnt = activeReqCnt); +allRequestsOffline := (activeReqCnt > 0) AND (onlineCnt = 0); + +// ===== Client statistics ===== +//Get a sum of done and error counts from all requests +reqErrSum := 0; +reqDoneSum := 0; +FOR i := 0 TO requestCnt -1 DO + reqDoneSum := reqDoneSum + requests[i]^.stats.doneCnt; + reqErrSum := reqErrSum + requests[i]^.stats.errorCnt; + + //copy stats to array for debugging + reqStats[i] := ADR(requests[i]^.stats); +END_FOR + + + +//Every time the tx message counter changes increment msg count and add last transaction time +IF client.udiNumMsgSent <> msgCountLast THEN + msgCountLast := client.udiNumMsgSent; + msgTimeSum := msgTimeSum + client.udiLastTransactionTime; + msgCountSum := msgCountSum + 1; +END_IF + +//Every 1 second compute statistics +pulse(); +IF pulse.T_Sec_OS THEN + msgLastSecondCnt := msgCountSum; + msgCountSum := 0; + msgLastSecondTime := msgTimeSum; + msgTimeSum := 0; +END_IF + +numMsgSent := client.udiNumMsgSent; +numMsgReply := client.udiNumMsgReply; + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa_Impl_LineIds + + + + + {attribute 'monitoring_display' := 'toString'} +{attribute 'no_assign'} +FUNCTION_BLOCK ABSTRACT atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_timeout: TIME := T#0MS; // If this is set, this will be used on requests, otherwise it will be calculated from in_dataRate. + {attribute 'symbol' := 'none'} + in_resetStats: BOOL; // Set this bool to true to reset statistics. +END_VAR +VAR_OUTPUT + {attribute 'symbol' := 'none'} + msgLastSecondCnt: UDINT; //Messages in the last second + {attribute 'symbol' := 'none'} + msgLastSecondTime: UDINT; //Total amount of time for all messages sent last second in ms + {attribute 'symbol' := 'none'} + numMsgSent: UDINT; + {attribute 'symbol' := 'none'} + numMsgReply: UDINT; + {attribute 'symbol' := 'none'} + allRequestsOnline: BOOL; //All requests are working + //{attribute 'symbol' := 'none'} + reqStats: ARRAY[0..99] OF POINTER TO atMbRequestStats; // For debugging modbus requests +END_VAR +VAR + {attribute 'symbol' := 'none'} + datarate: DINT := 0; //Bits per second data rate. Used to calculate reply timeout. Based on amount of time to transmit holding register read for max amount of registers. Default 154705 for Modbus TCP = 17ms to transmit 2650 bits + {attribute 'symbol' := 'none'} + client: REFERENCE TO ModbusFB.Client; // This is generated internally, no need to set. + {attribute 'symbol' := 'none'} + connectCnt: DINT; //Number of connection attempts + {attribute 'symbol' := 'none'} + initDone: BOOL; + {attribute 'symbol' := 'none'} + disconnectTimer: TON; //Connecting but not connected delay + {attribute 'symbol' := 'none'} + reconnectTimer: TON; //Not connecting and not connected delay + {attribute 'symbol' := 'none'} + msgCountLast: UDINT; //Last message count + {attribute 'symbol' := 'none'} + msgCountSum: UDINT; //Message count accumulator + {attribute 'symbol' := 'none'} + msgTimeSum: UDINT; //Amount of time for messsages accumulator + {attribute 'symbol' := 'none'} + pulse: atPulse; //Pulse generator + {attribute 'symbol' := 'none'} + requests: ARRAY[0..99] OF POINTER TO atMbReqBase; //list of request to direct + {attribute 'symbol' := 'none'} + requestCnt: UINT; //Number of active requests + {attribute 'symbol' := 'none'} + reqDoneSum: UDINT; //Count of done result on all requests + {attribute 'symbol' := 'none'} + reqErrSum: UDINT; //Count of error result on all requests + {attribute 'symbol' := 'none'} + i: DINT; //Loop variable + {attribute 'symbol' := 'none'} + nextIdx: DINT := -1; //Index of the active request + {attribute 'symbol' := 'none'} + priorityLowest: INT; //lowest request priority + {attribute 'symbol' := 'none'} + highestTimeSinceRun: UDINT; //lowest request priority + {attribute 'symbol' := 'none'} + allRequestsOfflineTON: TON; //All requests are offline delay + {attribute 'symbol' := 'none'} + requestNotExecutingTON: TON; //Delay after there are no requests execting + {attribute 'symbol' := 'none'} + recreateClientDly: TON; //Delay to make new client after force fail + {attribute 'symbol' := 'none'} + allRequestsOffline: BOOL; //All requests are offline + {attribute 'symbol' := 'none'} + connect: BOOL; // this is more of a "Request to connect, Requet to disconnect", will drop xConnect once all requests are done, wont start requests unless this is true. + {attribute 'symbol' := 'none'} + forceConnect: TON; //If after 5 seconds, all requests are still ofline, just force a reconnect. + test1: REAL; + test2: REAL; + destroyClient: BOOL; + recreateTest: BOOL; +END_VAR +VAR CONSTANT + {attribute 'symbol' := 'none'} + NEXT_REQUEST_DELAY: TIME := T#0MS; // Dont seem to need a delay + {attribute 'symbol' := 'none'} + RECONNECT_DELAY: TIME := T#500MS; + {attribute 'symbol' := 'none'} + DISCONNECT_DELAY: TIME := T#500MS; + {attribute 'symbol' := 'none'} + RECREATE_CLIENT_DELAY: TIME := T#1000MS; + {attribute 'symbol' := 'none'} + ALL_OFFLINE_DELAY: TIME := T#2S; + {attribute 'symbol' := 'none'} + FORCE_RECONNECT_DELAY: TIME := T#1S; + {attribute 'symbol' := 'none'} + PRIORITY_NONE: INT := 32767; +END_VAR + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa_Decl_LineIds + + + 3369 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + 00000000-0000-0000-0000-000000000000 + atMbClientRTU + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649591897 + + + None + + + //Modbus connection +//Connects to a modbus slave +//2024-06-27 CWD Initial create +//2024-11-13 CWD Fix problem with not connecting properly on power up +//2024-11-15 ATM Move from structure to defaults. +//2024-11-23 ATM Cleaned up code +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +IF NOT setupClient() THEN + RETURN; +END_IF + +//Set dataRate +//This is based on an 8 byte request and a 255 byte reply +dataRate := in_baudRate; + + +// ===== Initialization ===== +// if the param changes, force a reinit +IF _client^.iPort <> in_port + OR _client^.dwBaudrate <> in_baudRate + OR _client^.byDataBits <> in_dataBits + OR _client^.eParity <> in_parity + OR _client^.eStopBits <> in_stopBits THEN + initDone := FALSE; +END_IF; + +IF NOT initDone THEN + initDone := TRUE; + + //Other configuration parameters + _client^.iPort := in_port; + _client^.dwBaudrate := in_baudRate; + _client^.byDataBits := in_dataBits; + _client^.eParity := in_parity; + _client^.eStopBits := in_stopBits; + _client^.eDTRcontrol := 0; + _client^.eRTScontrol := 0; + _client^.eRTUASCII := 0; + _client^.udiLogOptions := ModbusFB.LoggingOptions.All; + _client^.xConnect:=FALSE; + +END_IF + +SUPER^(); + 17c2ab7a-4d60-48ec-966f-339ec83a75e9_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbClientRTU EXTENDS atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_port: BYTE := 0; //0=None, 1,2,3,4 + {attribute 'symbol' := 'none'} + in_baudRate: UINT := 9600; //4800, 9600, 19200, 38400, 57600, 115200 + {attribute 'symbol' := 'none'} + in_parity: BYTE := 0; //0=None, 1=Odd, 2=Even + {attribute 'symbol' := 'none'} + in_dataBits: BYTE := 8; //8=8 + {attribute 'symbol' := 'none'} + in_stopBits: BYTE := 1; //1=OneStopBit, 2=One5StopBits, 3=TwoStopBits +END_VAR +VAR + {attribute 'symbol' := 'none'} + _client: POINTER TO ModbusFB.ClientSerial; +END_VAR + 17c2ab7a-4d60-48ec-966f-339ec83a75e9_Decl_LineIds + + + 607 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + 00000000-0000-0000-0000-000000000000 + atMbClientTCP + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649747183 + + + None + + + //Modbus connection +//Connects to a modbus slave +//2024-06-27 CWD Initial create +//2024-11-15 ATM Cleanup +//2024-11-18 CWD Fix problem with not connecting properly on power up +//2024-11-19 CWD More fixing on maintaining a connection +//2024-11-23 ATM Cleaned up code +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +IF NOT setupClient() THEN + RETURN; +END_IF + +//Set dataRate +//This is based on an 8 byte request and a 255 byte reply +IF (in_baudRate <> 0) THEN + dataRate := in_baudRate; +ELSE + dataRate := 105200; +END_IF + + +// ===== Initialization ===== +// if the ip address or port changes, force a reinit +IF _client^.aIPaddr[0] <> in_ipAddress[0] + OR _client^.aIPaddr[1] <> in_ipAddress[1] + OR _client^.aIPaddr[2] <> in_ipAddress[2] + OR _client^.aIPaddr[3] <> in_ipAddress[3] + OR _client^.uiPort <> in_port THEN + initDone := FALSE; +END_IF; + + +// These are outside init so that the get updated on reconnect. +IF NOT initDone THEN + initDone := TRUE; + + // Setting the values, instead of copying the array, making the above compare possible. + _client^.aIPaddr[0] := in_ipAddress[0]; + _client^.aIPaddr[1] := in_ipAddress[1]; + _client^.aIPaddr[2] := in_ipAddress[2]; + _client^.aIPaddr[3] := in_ipAddress[3]; + _client^.uiPort := in_port; + + _client^.udiLogOptions := ModbusFB.LoggingOptions.None; + _client^.tConnnectTimeOut := T#100H; + _client^.xConnect := FALSE; +END_IF; + +client REF= _client^; + +SUPER^(); + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbClientTCP EXTENDS atMbClient +VAR_INPUT + {attribute 'symbol' := 'none'} + in_ipAddress: ARRAY[0..3] OF BYTE; // TCP IP Address + {attribute 'symbol' := 'none'} + in_port: UINT := 502; // TCP port + {attribute 'symbol' := 'none'} + in_baudrate: UINT := 0; // Set this if using RS485 to TCP Converter, used to calculate timeout. +END_VAR +VAR + {attribute 'symbol' := 'none'} + _client: POINTER TO ModbusFB.ClientTCP; +END_VAR + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570_Decl_LineIds + + + 1168 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + 00000000-0000-0000-0000-000000000000 + atMbReqBase + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649837310 + + + None + + + //Modbus request +//Makes a request using the start and quantity, puts the result into data +//2024-06-27 CWD Initial create +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage + +IF NOT __ISVALIDREF(client) THEN + RETURN; +END_IF + + +//Add this request to the clients request array +client.addRequest(THIS); + +//fault logic +//Fault if error +//errTrig(CLK := stats.reqError); +//IF errTrig.Q THEN +// out_fault := TRUE; +//END_IF +//not faulted if a succesfull request +//doneTrig(CLK := stats.reqDone); +// also clear fault if not active +//IF doneTrig.Q OR NOT stats.active THEN +// out_fault := FALSE; +//END_IF + +IF in_quantity <= dataSize THEN + stats.reqLength := in_quantity; +ELSE + stats.reqLength := dataSize; +END_IF; + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK ABSTRACT atMbReqBase +VAR_INPUT + {attribute 'symbol' := 'none'} + client: REFERENCE TO atMbClient; // Instance of atMBClient + {attribute 'symbol' := 'none'} + in_startAddr: UINT; + {attribute 'symbol' := 'none'} + in_quantity: UINT; + {attribute 'symbol' := 'none'} + in_dataOffset: UINT; // Offset position for data into array. + {attribute 'symbol' := 'none'} + in_unitId: BYTE := 0; + {attribute 'symbol' := 'none'} + in_timeout: SYSTIME := 0; // timeout in ms, if not set, will be 1ms less than interval. + {attribute 'symbol' := 'none'} + in_minDelay: TIME := T#10MS; // This is the minimum delay between queries, only relevent if high priority. + {attribute 'symbol' := 'none'} + in_prioritySetpoint: INT := 50; // this requests priority compared to other requests. Lower value is higher priority. + {attribute 'symbol' := 'none'} + in_priorityCurrent: INT; //Current priority. This is decrimented and the request is executed when at zero. + {attribute 'symbol' := 'none'} + stats: atMbRequestStats; //Request stats + +END_VAR +VAR_OUTPUT + //{attribute 'symbol' := 'none'} + //out_fault: BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + dataSize: UINT; + //{attribute 'symbol' := 'none'} + //quantUsed: UINT; + //{attribute 'symbol' := 'none'} + //doneTrig: R_TRIG; //Succesful request trigger + //{attribute 'symbol' := 'none'} + //errTrig: R_TRIG; //Error request result trigger + + {attribute 'symbol' := 'none'} + request: REFERENCE TO ModbusFB.ClientRequest;; +END_VAR + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37_Decl_LineIds + + + 3210 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + 00000000-0000-0000-0000-000000000000 + atMbReqReadCoil + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146913881 + + + None + + + //Modbus request input registers +//2024-11-16 CWD Added read coil +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + + + a77a90fd-541d-4ce9-ad97-7024224bbab4_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadCoil EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readCoilReq: POINTER TO ModbusFB.ClientRequestReadCoils; +END_VAR + + a77a90fd-541d-4ce9-ad97-7024224bbab4_Decl_LineIds + + + 193 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 1a3278f4-a658-407c-968a-21aa53361562 + 00000000-0000-0000-0000-000000000000 + atMbReqReadDiscrete + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147014015 + + + None + + + //Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + 1a3278f4-a658-407c-968a-21aa53361562_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadDiscrete EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readDiscreteReq: POINTER TO ModbusFB.ClientRequestReadDiscreteInputs; +END_VAR + + 1a3278f4-a658-407c-968a-21aa53361562_Decl_LineIds + + + 184 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 73b10819-9112-4811-9583-e2735d2b13ea + 00000000-0000-0000-0000-000000000000 + atMbReqReadHolding + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147099228 + + + None + + + //Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + 73b10819-9112-4811-9583-e2735d2b13ea_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadHolding EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readHoldReq: POINTER TO ModbusFB.ClientRequestReadHoldingRegisters; +END_VAR + + 73b10819-9112-4811-9583-e2735d2b13ea_Decl_LineIds + + + 173 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + 00000000-0000-0000-0000-000000000000 + atMbReqReadInput + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147179217 + + + None + + + //Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + a5dbc9b0-a1a1-4281-a685-ec85b35bb866_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqReadInput EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + readInputReq: POINTER TO ModbusFB.ClientRequestReadInputRegisters; +END_VAR + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866_Decl_LineIds + + + 167 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + 00000000-0000-0000-0000-000000000000 + atMbRequestStats + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147425423 + + + None + + + SysTimeGetUs(TSSus); + +IF (reqLength = 0) THEN + active := FALSE; + doneCnt := 0; + errorCnt := 0; + lastErrorId := modbusFB.Error.OK; + lastExceptionCode := atMBExceptionCodes.RESPONSE_SUCCESS; + execute := FALSE; + online := FALSE; + offline := FALSE; + reqDone := FALSE; + reqBusy := FALSE; + reqError := FALSE; + sinceLastExec := 0; + RETURN; +END_IF +active := TRUE; + +inDoneDeb(in_raw := reqDone, in_delOn := T#0S, in_DelOff := lightOn); +reqDoneLight := inDoneDeb.out_val; + +inBusyDeb(in_raw := reqBusy, in_delOn := T#0S, in_DelOff := lightOn); +reqBusyLight := inBusyDeb.out_val; + +inErrDeb(in_raw := reqError, in_delOn := T#0S, in_DelOff := lightOn); +reqErrorLight := inErrDeb.out_val; + +//Debounced execute indicator for debbugging +executeDeb(in_raw := _execute, in_delOn := T#0S, in_DelOff := lightOn); +executeLight := executeDeb.out_val; + + +//Set online status +IF doneCntRst >=3 THEN + online := TRUE; + offline := FALSE; +END_IF + +//Set online status +IF errorCntRst >= 3 THEN + online := FALSE; + offline := TRUE; +END_IF + +//Record start time in ms on execute rising edge +executeTrig(CLK := execute); +IF executeTrig.Q THEN + interval := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + lastReqStart := TSSus; +END_IF + +//Offline count +offlineTrig(CLK := NOT online); +IF offlineTrig.Q THEN + offlineCnt := offlineCnt + 1; +END_IF + +//Calculate ms since last execute +//If the last request start is more then 1 minute ago then set it for 1 minute +IF lastReqStart < TSSus - 60000000 THEN + lastReqStart := TSSus - 60000000; +END_IF +sinceLastExec := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + + 48b508fd-2131-484c-ba4a-20e61e66a5b2_Impl_LineIds + + + + + {attribute 'monitoring_display' := 'toString'} +{attribute 'no_assign'} +FUNCTION_BLOCK atMbRequestStats +VAR_INPUT + lastErrorId: modbusFB.Error; //Last error id + lastExceptionCode: atMBExceptionCodes; //Last modbus exception code. These are the standard modbus exceptions. + lastValidDuration: UDINT; // How long last transaction took (ms) + longestValidDuration: UDINT; //Longest valid duration (ms) + interval: UDINT; // Start-to-start interval (ms) + offlineCnt: UINT; //Count of how many times this request has gone offline + doneCnt: UDINT; // Count of successful request + errorCnt: UDINT; // Count of failed requests + doneCntRst: UDINT; // Count of succesful requests, resets on error result + errorCntRst: UDINT; // Count of failed requests, resets on succesful request + sinceLastExec: UDINT; // Time since the last start (ms) + timeoutCnt: UDINT; + reqLength: UINT; //Number of registers requested in request, used to calc active. +END_VAR +VAR_OUTPUT + executeLight: BOOL; //Execute that stays on momentarily + reqDoneLight: BOOL; //Request done that stays on momentarily + reqErrorLight: BOOL; //Request error that stays on momentarily + reqBusyLight: BOOL; //Request busy that stays on momentarily + online: BOOL; //Online means the requests are being replied to normally or with modbus exceptions. Communications are ok. + offline: BOOL; //Opposite of online, but starts out as false on program start. + active: BOOL; // This is true if its actually being used, and not having a count of 0 on the request. + executeMbFb: BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + inDoneDeb: atDebounce; + {attribute 'symbol' := 'none'} + inErrDeb: atDebounce; + {attribute 'symbol' := 'none'} + inBusyDeb: atDebounce; + {attribute 'symbol' := 'none'} + executeDeb: atDebounce; + toString: STRING(20); + {attribute 'symbol' := 'none'} + executeTrig: R_TRIG; //Execute trigger + {attribute 'symbol' := 'none'} + offlineTrig: R_TRIG; //Offline trigger + {attribute 'symbol' := 'none'} + lastReqStart: SYSTIME; //time of start of last request + {attribute 'symbol' := 'none'} + TSSus: SYSTIME; + _execute: BOOL; //Execute trigger and status. Turn on to trigger request. Will turn off when request is complete + _reqDone: BOOL; //Request done + _reqBusy: BOOL; //Request busy + _reqError: BOOL; //Request error +END_VAR + +VAR CONSTANT + {attribute 'symbol' := 'none'} + lightOn: TIME:= T#100MS; //Light on time for debug indicators +END_VAR + 48b508fd-2131-484c-ba4a-20e61e66a5b2_Decl_LineIds + + + 286 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + 00000000-0000-0000-0000-000000000000 + atMbReqWriteCoil + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147259331 + + + None + + + //Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + f6ee860b-0951-41d0-93d8-6436c681d8f1_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqWriteCoil EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF BOOL to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF BOOL; +END_VAR +VAR + {attribute 'symbol' := 'none'} + writeCoilsReq: POINTER TO ModbusFB.ClientRequestWriteMultipleCoils; +END_VAR + + f6ee860b-0951-41d0-93d8-6436c681d8f1_Decl_LineIds + + + 206 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + bb7e18ba-2018-4681-875e-de1f291019ef + 00000000-0000-0000-0000-000000000000 + atMbReqWriteHolding + + + + 24568a24-c491-472c-a21f-ee5d33859fab + + + + 1024 + False + False + False + + False + + + + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147339348 + + + None + + + //Modbus request input registers +//2024-06-27 CWD Initial Create +//2024-10-21 CWD Set udiTimeout to 0 as this was causing problems with detecting an actual no reply timeout from the slave +//2024-11-15 ATM Moved from Work to main for easier debugging +//2024-11-23 ATM Cleaned up code, added start array position +//2025-10-28 CWD Updated to reduce symbol usage +//2026-04-03 ATM Now Client makes the requests, calling this just passes data. Dynamically allocated requests + + +//Verify data size is large enough, if not then only get as many registers that will fit. +dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - LOWER_BOUND(data,1) + 1); + +SUPER^(); + bb7e18ba-2018-4681-875e-de1f291019ef_Impl_LineIds + + + + + {attribute 'no_assign'} +FUNCTION_BLOCK atMbReqWriteHolding EXTENDS atMbReqBase +VAR_IN_OUT + // ARRAY[] OF UINT to hold received data + {attribute 'symbol' := 'none'} + data: ARRAY[*] OF UINT; +END_VAR +VAR_OUTPUT +END_VAR +VAR + {attribute 'symbol' := 'none'} + writeHoldReq: POINTER TO ModbusFB.ClientRequestWriteMultipleRegisters; +END_VAR + + bb7e18ba-2018-4681-875e-de1f291019ef_Decl_LineIds + + + 164 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 9499c60e-03a2-49d0-b086-43990a018e20 + 00000000-0000-0000-0000-000000000000 + To_IPAddress + + + + 829a18f2-c514-4f6e-9634-1df173429203 + + + + + + + 21af5390-2942-461a-bf89-951aaf6999f1 + + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + + + + + + + 6f9dac99-8de1-4efc-8465-68ac443b7d08 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590489804 + + + None + + + //Convert parameters to array +To_IPAddress[0] := byte1; +To_IPAddress[1] := byte2; +To_IPAddress[2] := byte3; +To_IPAddress[3] := byte4; + + + + 9499c60e-03a2-49d0-b086-43990a018e20_Impl_LineIds + + + + + FUNCTION To_IPAddress : ARRAY[0..3] OF BYTE +VAR_INPUT + byte1: BYTE; + byte2: BYTE; + byte3: BYTE; + byte4: BYTE; +END_VAR + + 9499c60e-03a2-49d0-b086-43990a018e20_Decl_LineIds + + + 168 + Standard + + False + + 8fc52b24-7436-4d4a-8b03-aa43d42302d0 + + atomLibrary + 6-Comms + ModbusMaster + + -1 + + + False + + 9e99e368-853f-485b-911f-d9a2a4e46434 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + addRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146468437 + + + + + //Need a way to only add the request 1 time. Add a value to the requestbase to indicate its been added to the director +IF requestCnt >= UPPER_BOUND(requests,1) THEN + RETURN; +END_IF + +FOR i := 0 TO requestCnt DO + IF request = requests[i] THEN + RETURN; + END_IF +END_FOR + +requests[requestCnt] := request; +requestCnt := requestCnt + 1; + 9e99e368-853f-485b-911f-d9a2a4e46434_Impl_LineIds + + + + + //Adds the specified request to the global list +METHOD PUBLIC addRequest +VAR_INPUT + request: POINTER TO atMbReqBase; +END_VAR +VAR + i: UINT := 0; + +END_VAR + + 9e99e368-853f-485b-911f-d9a2a4e46434_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 685f7c69-c46a-4b34-b5e2-fb848c593f3e + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + processAllRequests + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148036583 + + + + + FOR i := 0 TO requestCnt -1 DO + requests[i]^.processRequest(); +END_FOR; + 685f7c69-c46a-4b34-b5e2-fb848c593f3e_Impl_LineIds + + + + + METHOD processAllRequests +VAR_INPUT +END_VAR +VAR + i: UINT; // Loop bstisnlr +END_VAR + 685f7c69-c46a-4b34-b5e2-fb848c593f3e_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 39769d9e-08c1-472d-82ef-25c66891a065 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + recreateAllRequests + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148056585 + + + + + FOR i := 0 TO requestCnt -1 DO + //Test for any offline request + requests[i]^.recreateRequest(); +END_FOR; + 39769d9e-08c1-472d-82ef-25c66891a065_Impl_LineIds + + + + + METHOD PROTECTED recreateAllRequests +VAR_INPUT +END_VAR + + 39769d9e-08c1-472d-82ef-25c66891a065_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574648119824 + + + + + + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc_Impl_LineIds + + + + + METHOD ABSTRACT recreateClient +VAR_INPUT +END_VAR + 60fd3bf3-849f-4af3-a1a5-18a73fd7b7fc_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + a7e27967-573f-4f9f-86e7-dd569b8cd943 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + resetStats + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148096636 + + + + + connectCnt := 0; +val := ADR(client.udiNumMsgSent); +val^ := 0; +val := ADR(client.udiNumMsgReply); +val^ := 0; +FOR i := 0 TO requestCnt -1 DO + requests[i]^.stats.resetStats(); +END_FOR; + a7e27967-573f-4f9f-86e7-dd569b8cd943_Impl_LineIds + + + + + METHOD resetStats +VAR_INPUT +END_VAR +VAR + i: DINT; + val: POINTER TO UDINT; +END_VAR + + + + + a7e27967-573f-4f9f-86e7-dd569b8cd943_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + ca1dfc70-ad93-43de-886d-39e56897aa6d + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + setAllRequestsOffline + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148106585 + + + + + FOR i := 0 TO requestCnt -1 DO + requests[i]^.stats.setClientOffline(); +END_FOR; + ca1dfc70-ad93-43de-886d-39e56897aa6d_Impl_LineIds + + + + + METHOD PRIVATE setAllRequestsOffline : BOOL +VAR_INPUT +END_VAR +VAR + i: DINT; +END_VAR + + + + + ca1dfc70-ad93-43de-886d-39e56897aa6d_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + d12c9bf2-320f-4e95-badb-68a2357c5783 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + setupClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574648239962 + + + + + // Only create request if doesnt exist +IF NOT __ISVALIDREF(client) THEN + recreateClient(); + IF NOT __ISVALIDREF(client) THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; + setupClient := FALSE; + RETURN; + END_IF +END_IF + +setupClient := TRUE; + d12c9bf2-320f-4e95-badb-68a2357c5783_Impl_LineIds + + + + + METHOD setupClient : BOOL +VAR_INPUT +END_VAR + + d12c9bf2-320f-4e95-badb-68a2357c5783_Decl_LineIds + + + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + activeIndex + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590625089 + + + + + PROPERTY activeIndex : DINT + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32_Decl_LineIds + + + 1 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + activeReqCnt + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639108502146293051 + + + + + PROPERTY PRIVATE activeReqCnt : UINT + 456b4953-57c9-4f76-ab5b-0db456dd9b58_Decl_LineIds + + + 1 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 4d9d378a-f256-4f23-b835-539ee9e287c3 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + allRequestsComplete + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639108502147921016 + + + + + {attribute 'symbol' := 'none'} +PROPERTY PRIVATE allRequestsComplete : BOOL + 4d9d378a-f256-4f23-b835-539ee9e287c3_Decl_LineIds + + + 2 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 89742962-f9b6-46c2-b9a5-b7e298f8495d + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + onlineCnt + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590755417 + + + + + {attribute 'symbol' := 'none'} +PROPERTY onlineCnt : DINT//Count of requests that are online + 89742962-f9b6-46c2-b9a5-b7e298f8495d_Decl_LineIds + + + 3 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 27292866-0bc0-4848-a965-64c51a0bf960 + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + requestExecuting + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841590845466 + + + + + {attribute 'symbol' := 'none'} +PROPERTY PRIVATE requestExecuting : BOOL //There is at least one request executing + 27292866-0bc0-4848-a965-64c51a0bf960_Decl_LineIds + + + 3 + + bc7fa96e-8ba3-4e3e-9cec-4d50ab57e6fa + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + + -1 + + + False + + 4950a3a1-f54f-434d-9252-b61a6cb4663d + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649617058 + + + + + old_client_id := _client; +_client := __NEW(ModbusFB.ClientSerial); +IF _client = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; +END_IF; +client REF= _client^; +connect := FALSE; + +IF old_client_id <> 0 THEN + __DELETE(old_client_id); + old_client_id := 0; +END_IF + +recreateAllRequests(); + 4950a3a1-f54f-434d-9252-b61a6cb4663d_Impl_LineIds + + + + + METHOD recreateClient +VAR_INPUT + old_client_id: POINTER TO ModbusFB.ClientSerial; +END_VAR + + 4950a3a1-f54f-434d-9252-b61a6cb4663d_Decl_LineIds + + + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + + -1 + + + False + + e8c6c80b-7bdb-4108-987e-4af107de4377 + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + toString + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639207574650142720 + + + + + {attribute 'symbol' := 'none'} +PROPERTY toString : atMbClientToString //Client request status noneOk = no requests working, someOk = some requests are working some are failing, allOk = all requests are working + e8c6c80b-7bdb-4108-987e-4af107de4377_Decl_LineIds + + + 3 + + 17c2ab7a-4d60-48ec-966f-339ec83a75e9 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + + -1 + + + False + + 79dfe91d-6dbc-46b0-866c-4b880e44e02b + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + recreateClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574649757233 + + + + + old_client_id := _client; +_client := __NEW(ModbusFB.ClientTCP); +IF _client = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; +END_IF; +client REF= _client^; +connect := FALSE; + +IF old_client_id <> 0 THEN + __DELETE(old_client_id); + old_client_id := 0; +END_IF + +recreateAllRequests(); + 79dfe91d-6dbc-46b0-866c-4b880e44e02b_Impl_LineIds + + + + + METHOD recreateClient +VAR + old_client_id: POINTER TO ModbusFB.ClientTCP; +END_VAR + + 79dfe91d-6dbc-46b0-866c-4b880e44e02b_Decl_LineIds + + + + 269ad87c-8ffe-4a28-9e12-2f2da3f7c570 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientTCP + + -1 + + + False + + a77f3143-e2cf-48a4-bafd-a0c18a11faeb + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + busy + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148176670 + + + + + stats.reqBusy := in; + +IF NOT in THEN + RETURN; +END_IF; + a77f3143-e2cf-48a4-bafd-a0c18a11faeb_Impl_LineIds + + + + + METHOD busy : BOOL +VAR_INPUT + in: BOOL; +END_VAR +VAR + executeTime: ULINT; + replyTimeoutTime: ULINT; +END_VAR + + + a77f3143-e2cf-48a4-bafd-a0c18a11faeb_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 5350a8fa-8297-42db-b185-fcb966e45684 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + completeRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148196661 + + + + + done(request.xDone); +err(in := request.xError, errorId := request.eErrorId, exceptionCode := request.eException); +busy(in := request.xBusy); +stats(); + +IF (atGVL.reset) THEN + // request.AbortModel(); + request.ResetModel(TRUE); +END_IF + 5350a8fa-8297-42db-b185-fcb966e45684_Impl_LineIds + + + + + METHOD completeRequest +VAR_INPUT +END_VAR + + 5350a8fa-8297-42db-b185-fcb966e45684_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + done + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148206667 + + + + + stats.reqDone := in; + +IF NOT in THEN + RETURN; +END_IF + +//Set priority for next request +in_priorityCurrent := in_prioritySetpoint; + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac_Impl_LineIds + + + + + METHOD done +VAR_INPUT + // Done state + in: BOOL; +END_VAR + + 3bfc4f72-6ee6-4e83-9bf3-99339b388eac_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + err + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148226656 + + + + + stats.reqError := in; + +IF NOT in THEN + RETURN; +END_IF + + +//Update error info +stats.lastErrorId := errorId; +stats.lastExceptionCode := ANY_TO_BYTE(exceptionCode); + +//Since we are here there was an exception. If the modbus response is 0, that means no modbus communication happened, so we are offline. If we would have had a succesful communication we would have got a actual modbus error. +//IF stats.lastExceptionCode = atMBExceptionCodes.RESPONSE_SUCCESS THEN + //Since there was no response from the device set the priority to higher number priority +// in_priorityCurrent := in_prioritySetPoint * 5; +//END_IF + +//Set priority for next request, priority gets higher after each error result +priorityMultiplier := UDINT_TO_INT(stats.errorCntRst); +IF priorityMultiplier > 3 THEN + priorityMultiplier := 3; +END_IF +// set multiplier to exponential retry. +in_priorityCurrent := 10 * priorityMultiplier * priorityMultiplier; +//in_priorityCurrent := in_prioritySetpoint * priorityMultiplier; + +// NO idea what Reset Model does, hoping it fixes comms issues +// request.ResetModel(TRUE); // I dont think this helped + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c_Impl_LineIds + + + + + METHOD err +VAR_INPUT + // Error state + in: BOOL; + errorId: modbusFB.Error; //modbusFB error ID + exceptionCode: modbusFB.ExceptionCodes; //ModbusFB exception code +END_VAR +VAR + priorityMultiplier: INT; //Raise priority when not response +END_VAR + + d7c69d5d-5192-4bf1-96fc-9ef0da66a62c_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 109d8e00-bf23-4306-a8d5-764aa6a03235 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148246723 + + + + + + 109d8e00-bf23-4306-a8d5-764aa6a03235_Impl_LineIds + + + + + METHOD ABSTRACT processRequest +VAR_INPUT +END_VAR + + 109d8e00-bf23-4306-a8d5-764aa6a03235_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148266663 + + + + + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8_Impl_LineIds + + + + + METHOD ABSTRACT recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + f64e9d26-0b0d-4e83-b6b3-1cfb43d906f8_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + db40c392-b461-4198-851d-85857ed98ccf + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + setupClient + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148276644 + + + + + // Only create request if doesnt exist +IF in_request = 0 THEN + in_request := recreateRequest(); + IF in_request = 0 THEN + atGVL.dynamicAllocationFail.in_raw := TRUE; + setupClient := FALSE; + RETURN; + END_IF +END_IF + +IF NOT __ISVALIDREF(mbsClient) THEN + setupClient := FALSE; + RETURN; +END_IF + +request REF= in_request^; + +setupClient := TRUE; +RETURN; + db40c392-b461-4198-851d-85857ed98ccf_Impl_LineIds + + + + + METHOD setupClient : BOOL +VAR_INPUT + in_request: POINTER TO ModbusFB.ClientRequest; +END_VAR + + db40c392-b461-4198-851d-85857ed98ccf_Decl_LineIds + + + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 6159c037-e399-466d-8108-0c4f6570f9b2 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + mbsClient + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639207574650298004 + + + + + PROPERTY PROTECTED mbsClient : REFERENCE TO ModbusFB.Client + 6159c037-e399-466d-8108-0c4f6570f9b2_Decl_LineIds + + + 1 + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + replyTimeout + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639034841591196333 + + + + + //Calculates the reply timout setting +PROPERTY PROTECTED replyTimeout : UDINT + 4f361885-f95c-4244-84d6-8c3b6b9b8a18_Decl_LineIds + + + 2 + + 3ad7cda5-e8b7-44c8-b264-165eb4cd7d37 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + + -1 + + + False + + ea5277e9-2ae8-417f-a88d-b571902c7d71 + a77a90fd-541d-4ce9-ad97-7024224bbab4 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639163341699321592 + + + + + IF NOT setupClient(readCoilReq) THEN + RETURN; +END_IF + +//Do request processing +readCoilReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + ea5277e9-2ae8-417f-a88d-b571902c7d71_Impl_LineIds + + + + + METHOD processRequest + + ea5277e9-2ae8-417f-a88d-b571902c7d71_Decl_LineIds + + + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadCoil + + -1 + + + False + + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3 + a77a90fd-541d-4ce9-ad97-7024224bbab4 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148331930 + + + + + IF readCoilReq <> 0 THEN + __DELETE(readCoilReq); + readCoilReq := 0; +END_IF +readCoilReq := __NEW(ModbusFB.ClientRequestReadCoils); +recreateRequest := readCoilReq; + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 2c4e80c5-f4e7-4d01-97ec-5317c7e780b3_Decl_LineIds + + + + a77a90fd-541d-4ce9-ad97-7024224bbab4 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadCoil + + -1 + + + False + + 9df094f9-a173-4520-816f-7badcbb1bd34 + 1a3278f4-a658-407c-968a-21aa53361562 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639163341699461941 + + + + + IF NOT setupClient(readDiscreteReq) THEN + RETURN; +END_IF + +//Do request processing +readDiscreteReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 9df094f9-a173-4520-816f-7badcbb1bd34_Impl_LineIds + + + + + METHOD processRequest + + 9df094f9-a173-4520-816f-7badcbb1bd34_Decl_LineIds + + + + 1a3278f4-a658-407c-968a-21aa53361562 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadDiscrete + + -1 + + + False + + d34866d4-1134-4650-b3d6-2e19d2fd09d1 + 1a3278f4-a658-407c-968a-21aa53361562 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148362063 + + + + + IF readDiscreteReq <> 0 THEN + __DELETE(readDiscreteReq); + readDiscreteReq := 0; +END_IF +readDiscreteReq := __NEW(ModbusFB.ClientRequestReadDiscreteInputs); +recreateRequest := readDiscreteReq; + d34866d4-1134-4650-b3d6-2e19d2fd09d1_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + d34866d4-1134-4650-b3d6-2e19d2fd09d1_Decl_LineIds + + + + 1a3278f4-a658-407c-968a-21aa53361562 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadDiscrete + + -1 + + + False + + 7b978176-d206-4509-b9d8-7908d33a9a74 + 73b10819-9112-4811-9583-e2735d2b13ea + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148402056 + + + + + IF NOT setupClient(readHoldReq) THEN + RETURN; +END_IF + +//Do request processing +readHoldReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + // udiReplyTimeout := 1 * 1000 * 1000, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 7b978176-d206-4509-b9d8-7908d33a9a74_Impl_LineIds + + + + + METHOD processRequest + + 7b978176-d206-4509-b9d8-7908d33a9a74_Decl_LineIds + + + + 73b10819-9112-4811-9583-e2735d2b13ea + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadHolding + + -1 + + + False + + 52ffdba7-bf4a-4746-924c-8b86df6d9a83 + 73b10819-9112-4811-9583-e2735d2b13ea + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148422060 + + + + + IF readHoldReq <> 0 THEN + __DELETE(readHoldReq); + readHoldReq := 0; +END_IF +readHoldReq := __NEW(ModbusFB.ClientRequestReadHoldingRegisters); +recreateRequest := readHoldReq; + 52ffdba7-bf4a-4746-924c-8b86df6d9a83_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 52ffdba7-bf4a-4746-924c-8b86df6d9a83_Decl_LineIds + + + + 73b10819-9112-4811-9583-e2735d2b13ea + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadHolding + + -1 + + + False + + 1004c837-fbef-45ee-986d-977343f4bd8f + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148442038 + + + + + IF NOT setupClient(readInputReq) THEN + RETURN; +END_IF + +//Do request processing +readInputReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 1004c837-fbef-45ee-986d-977343f4bd8f_Impl_LineIds + + + + + METHOD processRequest + + 1004c837-fbef-45ee-986d-977343f4bd8f_Decl_LineIds + + + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadInput + + -1 + + + False + + 9903b949-e1b9-4e0b-a98f-b6f745ad3605 + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148462074 + + + + + IF readInputReq <> 0 THEN + __DELETE(readInputReq); + readInputReq := 0; +END_IF +readInputReq := __NEW(ModbusFB.ClientRequestReadInputRegisters); +recreateRequest := readInputReq; + 9903b949-e1b9-4e0b-a98f-b6f745ad3605_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + 9903b949-e1b9-4e0b-a98f-b6f745ad3605_Decl_LineIds + + + + a5dbc9b0-a1a1-4281-a685-ec85b35bb866 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqReadInput + + -1 + + + False + + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + resetStats + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148542250 + + + + + doneCnt := 0; +doneCntRst := 0; +errorCnt := 0; +errorCntRst := 0; + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec_Impl_LineIds + + + + + METHOD resetStats +VAR_INPUT +END_VAR + + 94cb9a26-304d-46fc-a18d-a8ce0f1931ec_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + b1508d1f-b7a7-40b5-b74f-3091afc5b834 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + setClientOffline + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148552305 + + + + + doneCntRst := 0; +online := FALSE; +offline := TRUE; + b1508d1f-b7a7-40b5-b74f-3091afc5b834_Impl_LineIds + + + + + METHOD setClientOffline : BOOL +VAR_INPUT +END_VAR + + b1508d1f-b7a7-40b5-b74f-3091afc5b834_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 1c6ab99e-dc71-45a2-a024-45cf65b9af99 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + setToString + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148572279 + + + + + toString := CONCAT('OK:', CONCAT(TO_STRING(doneCnt), CONCAT('/Er:', TO_STRING(errorCnt)))); + 1c6ab99e-dc71-45a2-a024-45cf65b9af99_Impl_LineIds + + + + + METHOD PRIVATE setToString : BOOL +VAR_INPUT +END_VAR + + 1c6ab99e-dc71-45a2-a024-45cf65b9af99_Decl_LineIds + + + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + execute + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043399503 + + + + + PROPERTY execute : BOOL + 89434b6a-7e3b-4095-9220-0a6fc3789a25_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqBusy + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043449644 + + + + + PROPERTY reqBusy : BOOL + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqDone + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043519638 + + + + + PROPERTY reqDone : BOOL + 80965ce1-9b6a-44d3-b272-a5c739ccbc70_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + reqError + + 5a3b8626-d3e9-4f37-98b5-66420063d91e + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + + 639085160043604994 + + + + + PROPERTY reqError : BOOL + 30645d91-91c6-42eb-8399-c20f91ca0d3e_Decl_LineIds + + + 1 + + 48b508fd-2131-484c-ba4a-20e61e66a5b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + + -1 + + + False + + fbb41e61-f245-4f4e-bcd0-218ede01fcda + f6ee860b-0951-41d0-93d8-6436c681d8f1 + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148472030 + + + + + IF NOT setupClient(writeCoilsReq) THEN + RETURN; +END_IF + +//Do request processing +writeCoilsReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + fbb41e61-f245-4f4e-bcd0-218ede01fcda_Impl_LineIds + + + + + METHOD processRequest + + fbb41e61-f245-4f4e-bcd0-218ede01fcda_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + a861d6e2-2501-43dc-bb37-34509923450c + f6ee860b-0951-41d0-93d8-6436c681d8f1 + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148502269 + + + + + IF writeCoilsReq <> 0 THEN + __DELETE(writeCoilsReq); + writeCoilsReq := 0; +END_IF +writeCoilsReq := __NEW(ModbusFB.ClientRequestWriteMultipleCoils); +recreateRequest := writeCoilsReq; + a861d6e2-2501-43dc-bb37-34509923450c_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + a861d6e2-2501-43dc-bb37-34509923450c_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + df9b4fd1-9d38-4a54-a7e5-264a94a756ac + f6ee860b-0951-41d0-93d8-6436c681d8f1 + writeNow + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591456658 + + + + + in_priorityCurrent := 0; + df9b4fd1-9d38-4a54-a7e5-264a94a756ac_Impl_LineIds + + + + + METHOD writeNow : BOOL +VAR_INPUT +END_VAR + + df9b4fd1-9d38-4a54-a7e5-264a94a756ac_Decl_LineIds + + + + f6ee860b-0951-41d0-93d8-6436c681d8f1 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteCoil + + -1 + + + False + + 76621df2-a77c-4e0a-aba0-e3e4a53bd278 + bb7e18ba-2018-4681-875e-de1f291019ef + processRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148512269 + + + + + IF NOT setupClient(writeHoldReq) THEN + RETURN; +END_IF + +//Do request processing +writeHoldReq^( + rClient := mbsClient, + xExecute := stats.execute, + udiTimeOut := 0, + uiUnitId := in_unitId, + udiReplyTimeout := replyTimeout, + uiStartItem := in_startAddr, + uiQuantity := stats.reqLength, + pData := ADR(data[in_dataOffset]) +); + +completeRequest(); + 76621df2-a77c-4e0a-aba0-e3e4a53bd278_Impl_LineIds + + + + + METHOD processRequest + + 76621df2-a77c-4e0a-aba0-e3e4a53bd278_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + bd7ff184-1747-404c-9fa0-1b728376c927 + bb7e18ba-2018-4681-875e-de1f291019ef + recreateRequest + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502148532258 + + + + + IF writeHoldReq <> 0 THEN + __DELETE(writeHoldReq); + writeHoldReq := 0; +END_IF +writeHoldReq := __NEW(ModbusFB.ClientRequestWriteMultipleRegisters); +recreateRequest := writeHoldReq; + bd7ff184-1747-404c-9fa0-1b728376c927_Impl_LineIds + + + + + METHOD recreateRequest : POINTER TO ModbusFB.ClientRequest +VAR_INPUT +END_VAR + + bd7ff184-1747-404c-9fa0-1b728376c927_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + 5f9160f9-c4a4-4f65-a74c-2838020397f3 + bb7e18ba-2018-4681-875e-de1f291019ef + writeNow + + f8a58466-d7f6-439f-bbb8-d4600e41d099 + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591496600 + + + + + in_priorityCurrent := 0; + 5f9160f9-c4a4-4f65-a74c-2838020397f3_Impl_LineIds + + + + + METHOD writeNow : BOOL +VAR_INPUT +END_VAR + + 5f9160f9-c4a4-4f65-a74c-2838020397f3_Decl_LineIds + + + + bb7e18ba-2018-4681-875e-de1f291019ef + + atomLibrary + 6-Comms + ModbusMaster + atMbReqWriteHolding + + -1 + + + False + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590675071 + + + + + // ===== Test for any requests executing ===== +FOR i := 0 TO requestCnt -1 DO + IF requests[i]^.stats.execute OR requests[i]^.stats.reqBusy THEN + activeIndex := i; + RETURN; + END_IF +END_FOR +activeIndex := -1; + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c_Impl_LineIds + + + + + VAR + i: DINT; //Loop variable +END_VAR + + 0803920e-cc13-4b93-a08c-4d27afc1ff3c_Decl_LineIds + + + True + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeIndex + + -1 + + + False + + 3b82f264-e41e-461d-9beb-263305a22a8d + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590705101 + + + + + // set request to active, which is how the active index gets calculated. +requests[activeIndex]^.stats.execute := TRUE; + 3b82f264-e41e-461d-9beb-263305a22a8d_Impl_LineIds + + + + + VAR +END_VAR + + 3b82f264-e41e-461d-9beb-263305a22a8d_Decl_LineIds + + + False + + 5cc2fe1e-bdb2-429a-84e1-63e783ea3b32 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeIndex + + -1 + + + False + + cc024b23-e5fe-4958-ad80-513feea6aeab + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146383170 + + + + + cnt := 0; +IF requestCnt = 0 THEN + activeReqCnt := 0; + RETURN; +END_IF +FOR i := 0 TO requestCnt -1 DO + //Test for any offline request + IF requests[i]^.stats.active THEN + cnt := cnt + 1; + END_IF +END_FOR; +activeReqCnt := cnt; + cc024b23-e5fe-4958-ad80-513feea6aeab_Impl_LineIds + + + + + VAR + i: UINT; //Loop variable + cnt: UINT; +END_VAR + + cc024b23-e5fe-4958-ad80-513feea6aeab_Decl_LineIds + + + True + + 456b4953-57c9-4f76-ab5b-0db456dd9b58 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + activeReqCnt + + -1 + + + False + + d59f404d-315d-4ea4-b16b-c8f0e8342d2e + 4d9d378a-f256-4f23-b835-539ee9e287c3 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147990941 + + + + + IF requestCnt = 0 THEN + allRequestsComplete := 0; + RETURN; +END_IF +FOR i := 0 TO requestCnt -1 DO + //Test for any offline request + IF requests[i]^.stats.execute OR requests[i]^.stats.reqDone OR requests[i]^.stats.reqBusy OR requests[i]^.stats.reqError THEN + allRequestsComplete := FALSE; + RETURN; + END_IF +END_FOR; +allRequestsComplete := TRUE; + d59f404d-315d-4ea4-b16b-c8f0e8342d2e_Impl_LineIds + + + + + VAR + i: UINT; +END_VAR + + + + + d59f404d-315d-4ea4-b16b-c8f0e8342d2e_Decl_LineIds + + + True + + 4d9d378a-f256-4f23-b835-539ee9e287c3 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + allRequestsComplete + + -1 + + + False + + dceb6b17-86bf-4543-9652-2fd2b943953c + 89742962-f9b6-46c2-b9a5-b7e298f8495d + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502146538436 + + + + + cnt := 0; +IF requestCnt = 0 THEN + onlineCnt := 0; + RETURN; +END_IF +FOR i := 0 TO requestCnt -1 DO + //Test for any offline request + IF requests[i]^.stats.online THEN + cnt := cnt + 1; + END_IF +END_FOR; +onlineCnt := cnt; + dceb6b17-86bf-4543-9652-2fd2b943953c_Impl_LineIds + + + + + VAR + i: UINT; //Loop variable + cnt: UINT; +END_VAR + + dceb6b17-86bf-4543-9652-2fd2b943953c_Decl_LineIds + + + True + + 89742962-f9b6-46c2-b9a5-b7e298f8495d + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + onlineCnt + + -1 + + + False + + 0006696f-29e2-441e-a325-e3481d025cec + 27292866-0bc0-4848-a965-64c51a0bf960 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841590890993 + + + + + // ===== Test for any requests executing ===== +requestExecuting := activeIndex <> -1; + 0006696f-29e2-441e-a325-e3481d025cec_Impl_LineIds + + + + + VAR + i: DINT; //Loop variable +END_VAR + + 0006696f-29e2-441e-a325-e3481d025cec_Decl_LineIds + + + True + + 27292866-0bc0-4848-a965-64c51a0bf960 + + atomLibrary + 6-Comms + ModbusMaster + atMbClient + requestExecuting + + -1 + + + False + + 3d4da579-672d-4758-9b0f-e055a08e0da3 + e8c6c80b-7bdb-4108-987e-4af107de4377 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574650278010 + + + + + //Calculation for tostring +IF onlineCnt = requestCnt THEN + toString := atMbClientToString.allOk; +ELSIF onlineCnt = 0 THEN + toString := atMbClientToString.noneOk; +ELSE + toString := atMbClientToString.someOk; +END_IF + 3d4da579-672d-4758-9b0f-e055a08e0da3_Impl_LineIds + + + + + + 3d4da579-672d-4758-9b0f-e055a08e0da3_Decl_LineIds + + + True + + e8c6c80b-7bdb-4108-987e-4af107de4377 + + atomLibrary + 6-Comms + ModbusMaster + atMbClientRTU + toString + + -1 + + + False + + 4895a5bf-c631-4f8c-bee7-addb0b769548 + 6159c037-e399-466d-8108-0c4f6570f9b2 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639207574650328003 + + + + + IF NOT __ISVALIDREF(client) OR_ELSE NOT __ISVALIDREF(client.client) THEN + RETURN; +END_IF + +mbsClient REF= client.client; + 4895a5bf-c631-4f8c-bee7-addb0b769548_Impl_LineIds + + + + + VAR +END_VAR + + 4895a5bf-c631-4f8c-bee7-addb0b769548_Decl_LineIds + + + True + + 6159c037-e399-466d-8108-0c4f6570f9b2 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + mbsClient + + -1 + + + False + + f54a4cd6-8540-4892-a914-4aac4f8d9018 + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639034841591226460 + + + + + +IF in_timeout > 0 THEN + //Explicitly set timout + replyTimeout := ANY_TO_UDINT((in_timeout) * 1000); +ELSE + // If client timeout is set, use that. + IF client.in_timeout <> T#0MS THEN + replyTimeout := TIME_TO_UDINT(client.in_timeout) * 1000; + ELSIF client.dataRate <> 0 THEN + //Data transmision time * 1.5 to get the timeout. Result is in ms + //8 byte request, then 255 byte reply = 2630 bits. + replyTimeout := REAL_TO_UDINT(2630 / DINT_TO_REAL(client.dataRate) * 1.5 * 1000000); + ELSE + //In case client datarate = zero + replyTimeout := 1000000; //1 second? + END_IF + + +END_IF + + + f54a4cd6-8540-4892-a914-4aac4f8d9018_Impl_LineIds + + + + + VAR +END_VAR + + f54a4cd6-8540-4892-a914-4aac4f8d9018_Decl_LineIds + + + True + + 4f361885-f95c-4244-84d6-8c3b6b9b8a18 + + atomLibrary + 6-Comms + ModbusMaster + atMbReqBase + replyTimeout + + -1 + + + False + + 465bf80a-0d6e-4363-ba1e-dd04aef379f7 + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043419500 + + + + + execute := _execute; + 465bf80a-0d6e-4363-ba1e-dd04aef379f7_Impl_LineIds + + + + + VAR +END_VAR + + 465bf80a-0d6e-4363-ba1e-dd04aef379f7_Decl_LineIds + + + True + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + execute + + -1 + + + False + + 8ff5fbe7-c047-436d-923c-942d0d66494c + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043439650 + + + + + IF reqDone OR reqError THEN + _execute := FALSE; + RETURN; +END_IF +_execute := execute; + 8ff5fbe7-c047-436d-923c-942d0d66494c_Impl_LineIds + + + + + VAR +END_VAR + + 8ff5fbe7-c047-436d-923c-942d0d66494c_Decl_LineIds + + + False + + 89434b6a-7e3b-4095-9220-0a6fc3789a25 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + execute + + -1 + + + False + + db10f23e-d96b-4fd5-aec0-8b530ad288e1 + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043479636 + + + + + reqBusy := _reqBusy; + db10f23e-d96b-4fd5-aec0-8b530ad288e1_Impl_LineIds + + + + + VAR +END_VAR + + db10f23e-d96b-4fd5-aec0-8b530ad288e1_Decl_LineIds + + + True + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqBusy + + -1 + + + False + + a239da3a-8f75-41da-a264-f06ca07143f4 + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043499661 + + + + + _reqBusy := reqBusy; + a239da3a-8f75-41da-a264-f06ca07143f4_Impl_LineIds + + + + + VAR +END_VAR + + a239da3a-8f75-41da-a264-f06ca07143f4_Decl_LineIds + + + False + + 13e3ae42-bdd9-49ee-b8c0-74b8339469cb + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqBusy + + -1 + + + False + + 61db378f-6029-4712-b562-fea707e5698b + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043539625 + + + + + reqDone := _reqDone; + 61db378f-6029-4712-b562-fea707e5698b_Impl_LineIds + + + + + VAR +END_VAR + + 61db378f-6029-4712-b562-fea707e5698b_Decl_LineIds + + + True + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqDone + + -1 + + + False + + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3 + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147505410 + + + + + _reqDone := reqDone; + +IF (execute AND reqDone) THEN + // ===== Request has just completed succesfully ===== + //Update last valid duration + lastValidDuration := ULINT_TO_UDINT((TSSus - lastReqStart)/1000); + IF lastValidDuration > longestValidDuration THEN + longestValidDuration := lastValidDuration; + END_IF + //Counters + doneCnt := doneCnt + 1; + doneCntRst := doneCntRst + 1; + errorCntRst := 0; + + //Update error info + lastErrorId := modbusFB.Error.OK; + lastExceptionCode := atMBExceptionCodes.RESPONSE_SUCCESS; + + execute := FALSE; + setToString(); +END_IF + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3_Impl_LineIds + + + + + VAR +END_VAR + + 858ae7ab-7bd2-4c3e-9c4b-69064e0620a3_Decl_LineIds + + + False + + 80965ce1-9b6a-44d3-b272-a5c739ccbc70 + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqDone + + -1 + + + False + + e44e83bd-8f84-4202-9ab4-784d09cf5506 + 30645d91-91c6-42eb-8399-c20f91ca0d3e + Get + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639085160043645000 + + + + + reqError := _reqError; + e44e83bd-8f84-4202-9ab4-784d09cf5506_Impl_LineIds + + + + + VAR +END_VAR + + e44e83bd-8f84-4202-9ab4-784d09cf5506_Decl_LineIds + + + True + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqError + + -1 + + + False + + 89b5f7bc-0c37-43f3-8984-14034b000d89 + 30645d91-91c6-42eb-8399-c20f91ca0d3e + Set + + 792f2eb6-721e-4e64-ba20-bc98351056db + + a9ed5b7e-75c5-4651-af16-d2c27e98cb94 + 3b83b776-fb25-43b8-99f2-3c507c9143fc + + 639108502147595513 + + + + + _reqError := reqError; + +IF (execute AND reqError) THEN + // ===== Request has just completed with error ===== + // Counters + errorCnt := errorCnt + 1; + errorCntRst := errorCntRst + 1; + doneCntRst := 0; + //Turn off execute + execute := FALSE; + + //Since we are here there was an exception. If the modbus response is 0, that means no modbus communication happened, so we are offline. If we would have had a succesful communication we would have got a actual modbus error. + IF lastExceptionCode = atMBExceptionCodes.RESPONSE_SUCCESS THEN + timeoutCnt := timeoutCnt + 1; + lastExceptionCode := atMBExceptionCodes.TIMEOUT; + END_IF + setToString(); +END_IF + 89b5f7bc-0c37-43f3-8984-14034b000d89_Impl_LineIds + + + + + VAR +END_VAR + + 89b5f7bc-0c37-43f3-8984-14034b000d89_Decl_LineIds + + + False + + 30645d91-91c6-42eb-8399-c20f91ca0d3e + + atomLibrary + 6-Comms + ModbusMaster + atMbRequestStats + reqError + + -1 + + + CODESYS V3.5 SP21 Patch 4 + + diff --git a/compat/README.md b/compat/README.md new file mode 100644 index 0000000..50247b8 --- /dev/null +++ b/compat/README.md @@ -0,0 +1,137 @@ +# Existing atMb interface backed by the C TCP engine + +Import AtomModbus-Compatible.export into a BACKUP COPY of your existing +atomLibrary, accepting replacement of the matching ModbusMaster objects. +This preserves the other Atom helpers referenced by the original code. +Do not import it into your small CmpAtomModbus external-function library. + +In the copied atomLibrary's Library Manager add your CmpAtomModbus 0.2.0.0 +library. Ensure its namespace/alias is exactly `CmpAtomModbus`, as the wrapper +calls `CmpAtomModbus.atMbCall_cext`. Keep existing dependencies including +ModbusFB, SysTime, the standard timer library and your other Atom helpers. +ModbusFB remains for legacy enum types and RTU; TCP no longer creates or calls +its client/request objects. + +The native export preserves all 78 original object IDs and adds 10 methods. +Review source is in All_Source_Review.st. Original_HomeCodesys.export is the +unchanged supplied original. The export's XML/schema structure and public +declaration preservation have been checked, but the revised IEC code has NOT +been compiled in CODESYS here. Compile the copied atomLibrary and test before +replacing the installed production version. + +## Same application calls + +Your export's exact request names include `Req`: + +```iecst +VAR + mbClient : atMbClientTCP; + mbRead : atMbReqReadHolding; + mbWrite : atMbReqWriteHolding; + holding : ARRAY[0..9] OF UINT; + setpoints : ARRAY[0..1] OF UINT; +END_VAR +``` + +```iecst +mbClient( + in_ipAddress := To_IPAddress(192,168,1,50), + in_port := 502, + in_timeout := T#1S +); + +mbRead( + client := mbClient, + in_unitId := 1, + in_startAddr := 0, + in_quantity := 10, + in_dataOffset := 0, + in_minDelay := T#200MS, + data := holding +); + +mbWrite( + client := mbClient, + in_unitId := 1, + in_startAddr := 100, + in_quantity := 2, + in_dataOffset := 0, + in_minDelay := T#1S, + data := setpoints +); +// mbWrite.writeNow(); // Existing method boosts priority, respecting min delay. +``` + +Only use mapped register ranges for your device; the write example is an API +example, not part of the read-only bench test. Existing names, input defaults, +BOOL/UINT open arrays, client reference, stats properties and writeNow are kept. +Use your existing library namespace prefix if required by Library Manager. + +`in_dataOffset` remains an ACTUAL ARRAY INDEX as in the old implementation, +not a relative offset from LOWER_BOUND. Native capacity clipping now considers +that starting index. Bad indices set out_parameterError and disable scheduling +until corrected. Quantity zero disables a request. Protocol-excessive quantities +are rejected with a request parameter error. Don't change the client reference +of a registered request while running; request registrations are persistent. + +## Added diagnostics/options (old call sites need no new arguments) + +Client: out_connected, out_connectionState, out_lastNativeError, +out_socketError, out_reconnectCount, out_registrationFull. +State: 0 disabled, 1 retry, 2 connecting, 3 ready, 4 sending, 5 receiving. +Request: out_nativeError, out_socketError, out_writeUncertain, out_validData, +out_dataAgeMs, out_stale, out_parameterError. Existing stats remain available. + +Extra TCP inputs: in_enable (TRUE), in_connectTimeout (2s), +in_reconnectDelay (1s), in_holdUncertainWrites (FALSE). +Extra request input: in_staleAfter (2s). + +Priorities/minimum polling delays and cyclic writes are preserved. The C engine +never automatically replays a failed packet, but the IEC scheduler submits a +fresh snapshot on the next eligible write cycle, as the original code does. +For command writes where that is inappropriate, set in_holdUncertainWrites=TRUE: +an uncertain write holds that request until its existing writeNow() is called. +A valid Modbus exception counts as responsive communication, but does not +refresh read-data freshness. A client endpoint change closes the old context, +cancels its transaction and invalidates its old read data. + +The client processes its registered requests, as before. Request FB calls bind +their arrays/register the requests; they must still run every scan. After an +online change registrations are rebuilt by those calls. The native base's +FB_reinit resets in-flight state and TCP FB_exit closes the C context when +disposed. These lifecycle paths require target testing. An application STOP +alone does not guarantee FB_exit; set in_enable=FALSE and run one scan (or call +release()) before STOP/download. Re-enable afterwards. + +All native clients/requests must be owned by ONE IEC task because the C pool +is not thread-safe. Up to 16 native clients and 100 registered requests/client. +Do not retain/persist client handles. Arrays/instances must live for the client +lifetime. No IEC data pointer is held in the C engine between calls. + +## Rebuild the Pi component once + +This wrapper uses new command 11 for per-request timeouts. Rebuild the included +component; do not use the previous .so. No external function declaration, +checksum or library interface regeneration is needed. Version stays 0.2.0.0. + +```sh +cd ~/modbus-build/atom-modbus +make -C component SDKDIR="$(realpath sdk/ExtensionSDK)" clean +make -C component SDKDIR="$(realpath sdk/ExtensionSDK)" all +sudo /etc/init.d/codesyscontrol stop +sudo cp component/out/libCmpAtomModbus.so /usr/lib/ +sudo /etc/init.d/codesyscontrol start +``` + +The stop/start requires the same safe bench maintenance conditions as before. +The existing ComponentManager entry stays valid. Add the copied/compiled Atom +library to a test application, use only the read block initially and verify +disconnect/reconnect, per-request timeouts, priorities and lifecycle handling. + +## Validation + +Public input/output/in-out declarations from the original export are retained. +The C engine and bridge regression tests cover TCP recovery, framing and +exceptions. The new per-request timeout command is exercised by the TCP bridge +driver. This does not substitute for CODESYS compile, target scheduler tests or +production commissioning. diff --git a/component/CmpAtomModbus.c b/component/CmpAtomModbus.c new file mode 100644 index 0000000..b465ab0 --- /dev/null +++ b/component/CmpAtomModbus.c @@ -0,0 +1,30 @@ +#include "../src/atom_bridge.h" +#include "CmpStd.h" +#include "CmpErrors.h" +#include "CmpItf.h" +#include "CmpAtomModbusDep.h" + +void CDECL CDECL_EXT atmbcall_cext(atmbcall_cext_struct *p) +{ + p->atMbCall_cext = ab_call( + p->command, + p->handle, + p->ipAddress, + p->port, + p->unitId, + p->functionCode, + p->startAddress, + p->quantity, + p->pData, + p->dataCapacity, + p->pStatus, + p->statusCapacity, + p->nowMs, + p->connectTimeoutMs, + p->responseTimeoutMs, + p->retryDelayMs); +} + + +#include "../src/atom_mb.c" +#include "../src/atom_bridge.c" diff --git a/component/CmpAtomModbusItf.m4 b/component/CmpAtomModbusItf.m4 new file mode 100644 index 0000000..0f01230 --- /dev/null +++ b/component/CmpAtomModbusItf.m4 @@ -0,0 +1,70 @@ +/** + * 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/component/CmpFrame.c b/component/CmpFrame.c new file mode 100644 index 0000000..7c1f10f --- /dev/null +++ b/component/CmpFrame.c @@ -0,0 +1,81 @@ +#include "../src/atom_bridge.h" +/***************************************************************************** +* Copyright: Copyright CODESYS Development GmbH +* Program: Extension API for the Linux SL products +******************************************************************************/ + +#include "CmpStd.h" + +#include DEP_H + +USE_STMT + +DLL_DECL int CDECL ComponentEntry(INIT_STRUCT *pInitStruct) +/* Used to exchange function pointers between component manager and components. + Called at startup for each component. + pInitStruct: IN Pointer to structure with: + pfExportFunctions OUT Pointer to function that exports component functions + pfImportFunctions OUT Pointer to function that imports functions from other components + pfGetVersion OUT Pointer to function to get component version + pfRegisterAPI IN Pointer to component manager function to register a API function + pfGetAPI IN Pointer to component manager function to get a API function + pfCallHook IN Pointer to component manager function to call a hook function + Return ERR_OK if library could be initialized, else error code +*/ +{ + pInitStruct->CmpId = COMPONENT_ID; + pInitStruct->pfExportFunctions = ExportFunctions; + pInitStruct->pfImportFunctions = ImportFunctions; + pInitStruct->pfGetVersion = CmpGetVersion; + pInitStruct->pfHookFunction = HookFunction; + pInitStruct->pfCreateInstance = NULL; + pInitStruct->pfDeleteInstance = NULL; + + s_pfCMRegisterAPI = pInitStruct->pfCMRegisterAPI; + s_pfCMRegisterAPI2 = pInitStruct->pfCMRegisterAPI2; + s_pfCMGetAPI = pInitStruct->pfCMGetAPI; + s_pfCMGetAPI2 = pInitStruct->pfCMGetAPI2; + s_pfCMCallHook = pInitStruct->pfCMCallHook; + s_pfCMRegisterClass = pInitStruct->pfCMRegisterClass; + s_pfCMCreateInstance = pInitStruct->pfCMCreateInstance; + return ERR_OK; +} + +/** + * Publish exported functions to the component manager, to make them available for other components. + * Use generated macro EXPORT_STMT. + * ERR_OK or error code in case of error + */ +static int CDECL ExportFunctions(void) +{ + /* Macro to export functions */ + EXPORT_STMT; + return ERR_OK; +} + +/** + * Import function pointers from other components. Use generated macro IMPORT_STMT. + * ERR_OK or error code if function name could not be resolved + */ +static int CDECL ImportFunctions(void) +{ + /* Macro to import functions */ + IMPORT_STMT; + return ERR_OK; +} + +/** + * Return the version of our component. + * Version as defined in dep file + */ +static RTS_UI32 CDECL CmpGetVersion(void) +{ + return CMP_VERSION; +} + +static RTS_RESULT CDECL HookFunction(RTS_UI32 ulHook, RTS_UINTPTR ulParam1, RTS_UINTPTR ulParam2) +{ + if (ulHook == CH_EXIT) ab_shutdown(); + + return ERR_OK; +} diff --git a/component/makefile b/component/makefile new file mode 100644 index 0000000..d7f6b45 --- /dev/null +++ b/component/makefile @@ -0,0 +1,13 @@ +# Generated from supplied ExtensionSDK; native build on ARM64 Pi. +SDKDIR ?= ../sdk/ExtensionSDK +MAJORVER=0 +MINORVER=2 +CMPVERSION=0x00020000 +# Local development ID; check no other component uses this ID. +CMPID=0x2001 +DOS2UNIX=dos2unix +M4=m4 +CC ?= gcc +# The SDK otherwise includes CmpFrame in its component identity wildcard. +override CSRC := CmpAtomModbus.c +include $(SDKDIR)/makefile diff --git a/dist/README.md b/dist/README.md new file mode 100644 index 0000000..264e9f5 --- /dev/null +++ b/dist/README.md @@ -0,0 +1 @@ +Release outputs are created by tools/build-linux.sh. No precompiled .so is included. diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..372dfb3 --- /dev/null +++ b/docs/API.md @@ -0,0 +1,54 @@ +# Bridge contract + +Return: 0 = accepted, 1..10 = am_error values in atom_mb.h. +256 = invalid/stale handle, 257 = pool full, 258 = insufficient/null buffer, +259 = unknown command. Always inspect return codes. A rejected call is not a +completed Modbus request. + +Commands: 1 Open, 2 Close, 3 Enable, 4 Disable, 5 Tick, 6 Submit, +7 CopyRead, 8 Reconnect, 9 Configure, 10 Acknowledge, +11 Set request timeout (responseTimeoutMs only; permitted while not busy). +Configure only while disabled; all three timeouts must be nonzero. +Open receives an IPv4 DWORD in human octet order: 16#C0A80132 = 192.168.1.50. +Open returns its handle in status[0], NOT in the function return value. +Close returns status[0]=0; old handles fail even when their slot is reused. +Capacity values count WORD/UDINT elements, not bytes. Pass separate buffers. +CopyRead copies `quantity` WORDs; coils/discretes are unpacked WORD 0/1. + +| Status index | Meaning | +|---|---| +| 0 | Handle | +| 1 | State: 0 Disabled, 1 Retry, 2 Connecting, 3 Ready, 4 Sending, 5 Receiving | +| 2 | Busy | +| 3 | Done | +| 4 | Error | +| 5 | Latest transaction result | +| 6 | Last failure, retained after success | +| 7 | OS socket error | +| 8 | Modbus exception | +| 9 | Write outcome uncertain | +| 10 | Connection attempts | +| 11 | Attempts after first attempt | +| 12 | Completed successful requests | +| 13 | Failed requests | +| 14 | Has successful transaction | +| 15 | Last success timestamp, low 32 bits | +| 16 | Last success timestamp, high 32 bits | +| 17 | Quantity of latest request | +| 18 | Function code | +| 19 | Unit ID | +| 20 | Enabled | +| 21 | Connect timeout milliseconds | +| 22 | Whole-request timeout milliseconds | +| 23 | Retry delay milliseconds | + +The PLC wrapper consumes Done/Error then Acknowledge before submitting a new +request. For Done reads, call CopyRead BEFORE Acknowledge. Completed read data +is retained internally but CopyRead requires Done. For writes, do not replay +an uncertain result without an application decision. + +All calls to the entire bridge pool must originate from ONE IEC task. Contexts +and the pool are not thread-safe. Handles must not be RETAIN/PERSISTENT. Closing +on program disposal and invalidating on application reset/download remain PLC +wrapper responsibilities. Component unload cleanup alone does not cover an IEC +application stop or download. diff --git a/docs/BUILD.md b/docs/BUILD.md new file mode 100644 index 0000000..333f494 --- /dev/null +++ b/docs/BUILD.md @@ -0,0 +1,37 @@ +# Linux builds + +A binary must match CPU architecture, libc and the CODESYS component interface. +One .so cannot cover every Linux version. Build on the oldest supported target +OS within each architecture, or use its sysroot in a matching cross toolchain. + +Native Pi build, from Library: + +```sh +sudo apt-get install build-essential make m4 sed dos2unix python3 +./tools/build-linux.sh linux-aarch64 +``` + +Native x86-64 Linux build: `./tools/build-linux.sh linux-x86_64`. +32-bit ARM requires a matching ARM hard-float compiler/OS; do not use the ARM64 +binary. The target argument labels output; it does not choose a compiler. + +Cross-build example, provided a matching compiler and libc sysroot are installed: + +```sh +CC=aarch64-linux-gnu-gcc ./tools/build-linux.sh linux-aarch64 +``` + +For a custom compiler/sysroot use a compiler wrapper as CC. Cross builds have +not been validated in this bundle. The supplied SDK's makefile controls linking; +check its options before adding toolchain flags. CC must be one executable path. +Build targets sequentially because component/out is shared and cleaned each run. + +The build script accepts an SDKDIR environment override. Preserve the supplied +SDK unchanged and use separate folders for replacement SDK versions. Record +and test compatibility before changing the generated external function ABI. +For ABI changes, regenerate C/M4 from the external CODESYS library and review +tools/prepare_component.py; do not overwrite component implementations blindly. + +Build metadata includes compiler, host, SDK file hashes and output architecture. +Host OS/glibc describes the build host, not necessarily a cross-compiler sysroot. +Add the actual sysroot and tested target runtime to build-info.txt before release. diff --git a/docs/PACKAGING.md b/docs/PACKAGING.md new file mode 100644 index 0000000..6a18a95 --- /dev/null +++ b/docs/PACKAGING.md @@ -0,0 +1,19 @@ +# Embed the shared object in the IEC library + +1. Copy the built libCmpAtomModbus.so to the development PC. +2. Open the CmpAtomModbus library containing atMbCall_cext. +3. In POUs add a C Implemented Library object (C Code Integration functionality). +4. Add an Object file for devices: choose the .so and the exact Raspberry Pi + 64 SL device/version. If a component name is requested, use CmpAtomModbus. +5. Save/install the library and update the application's library reference. +6. Perform a full download and verify loading and function resolution. + +Add separate object files for other device/architecture targets. CODESYS chooses +the matching device entry; do not assign ARM64 binaries to 32-bit devices. +For automatic deployment testing, remove only the old manually added +CmpAtomModbus ComponentManager entry and restart the runtime before download. +Do this while the PLC application is stopped for maintenance. + +Official instructions: +https://content.helpme-codesys.com/en/CODESYS%20Control/_rtsl_extension_extension_sdk.html +https://content.helpme-codesys.com/en/CODESYS%20Development%20System/_cds_obj_c_implemented_library.html diff --git a/docs/TESTING.md b/docs/TESTING.md new file mode 100644 index 0000000..4c922e2 --- /dev/null +++ b/docs/TESTING.md @@ -0,0 +1,14 @@ +# Validation + +Run `make test` on Linux with a C compiler and Python 3. Tests use a local +socket server and cover normal/fragmented replies, close/reconnect, timeout, +bad transaction IDs, bad MBAP length, Modbus exceptions, uncertain writes and +coils. They run through the direct engine and bridge. Pool/handle tests are +included. These tests do not compile or execute Structured Text. + +In CODESYS compile both libraries and start a read-only bench application. +Verify first scan with zero registered requests, then registered requests, +reply timeouts, priorities, data freshness, disconnect/reconnect and disposal. +Test full download/reset and online-change lifecycle separately. Only then test +writes with appropriate device registers. Validate embedded deployment on a +target without the manually configured component. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md new file mode 100644 index 0000000..6fb0fbc --- /dev/null +++ b/docs/TROUBLESHOOTING.md @@ -0,0 +1,27 @@ +# Troubleshooting + +On the user's Pi the active runtime log is /var/log/codesyscontrol.log. +/var/opt/codesys/codesyscontrol.log contained old logs. Check timestamps. + +```sh +sudo /etc/init.d/codesyscontrol status +sudo tail -n 100 /var/log/codesyscontrol.log +file component/out/libCmpAtomModbus.so +nm -D component/out/libCmpAtomModbus.so +``` + +Look for CmpAtomModbus, undefined symbol, signature mismatch or load errors. +ComponentEntry and atmbcall_cext must be exported. The observed manual load was +component ID 0xffff2001, version 0.2.0.0. Ensure custom component IDs do not collide. + +External function signature: 0x709EE65B, API version: 0x00020000. +A valid component load does not prove the request scheduler or device I/O works. + +AccessViolation in processAllRequests: use the current export with zero-count, +bounds and null checks and do a full download. Nonzero stale pointers still need +lifecycle investigation. Keep requests and their data arrays alive for the client +lifetime, and do not change a registered request's client while running. + +Use one IEC task for all native clients. Handles must not be retained/persistent. +For STOP/download, disable the client and let a scan run, or call release(). +See compat/README.md for lifecycle limitations and uncertain-write handling. diff --git a/generated/CmpAtomModbus.c b/generated/CmpAtomModbus.c new file mode 100644 index 0000000..cb364d6 --- /dev/null +++ b/generated/CmpAtomModbus.c @@ -0,0 +1,10 @@ +#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/generated/CmpAtomModbusItf.m4 b/generated/CmpAtomModbusItf.m4 new file mode 100644 index 0000000..ac59f30 --- /dev/null +++ b/generated/CmpAtomModbusItf.m4 @@ -0,0 +1,70 @@ +/** + * 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/sdk/ExtensionSDK-original.zip b/sdk/ExtensionSDK-original.zip new file mode 100644 index 0000000..cc463b1 Binary files /dev/null and b/sdk/ExtensionSDK-original.zip differ diff --git a/sdk/ExtensionSDK/README b/sdk/ExtensionSDK/README new file mode 100644 index 0000000..d0789cb --- /dev/null +++ b/sdk/ExtensionSDK/README @@ -0,0 +1,149 @@ +############################################################################## +# Copyright: Copyright CODESYS Development GmbH +# Program: Extension API for the Linux SL products +############################################################################## + +########################## +This is the SDK of the Linux SL products to create an own additional CODESYS runtime component +whose functions that can be called from a CODESYS IEC library. +########################## + +########################## +Prerequisites: +- A CODESYS Installation to create an IEC library on a windows machine +- Some buildtools to create a shared object (linux dynamic library), that can be run with the CODESYS Control SL runtimes + - some kind of GCC (for X64 / ARM / AARCH64, depending on your target system) + - make + - m4 + - sed + - dos2unix +########################## + +########################## +If you want to start a new project, follow these steps: + +On the linux system (where you want to build the component) +1) Install the required tools (see prerequisites), e.g. with + sudo apt-get install build-essential m4 sed dos2unix make + +2) Copy the SDK to a place of your choice, e.g. in your home directory. + Later we call this directory the "", so please remember this location! + Hint: As we are using the tool "make": dont use paths with whitespaces. Make does not like that at all! + +3) Create a folder next to the sdk where you want to start your project, e.g. "CmpFirstTest" + mkdir ~/CmpFirstTest + Later we call this directory the "", so please remember this location! + +4) Now create a new project with the following command, and use the makefile from the : + make -f /makefile newproject + e.g.: + make -f ../sdk/makefile newproject + This creates a makefile within your folder, you might want to take a look at it later ;-) + Your workspace now might look like this: + | + |- SKD + | |- include (dir) + | |- m4 (dir) + | |- src (dir) + | |- makefile + | |- makefile_template + | |- README + | + |- CmpFirstTest + |- makefile + + +5) Now take a look at the generated makefile. + If you are compiling for X64, you dont need to change anything + If you are compiling for another architecture, you need to set a proper cross compiler via the variable CC (and probably more CFLAGS, INCLUDES and LDLIBS) + + +On the Windows system (where CODESYS is installed) +1) Open your CODESYS IDE and create a new library unsing the "Empty library" template (do not use the "External CODESYS library" template). + Set the title in the project information to e.g.: "CmpFirstTest" + and store it under the same filename, e.g. "CmpFirstTest.library". + These two must match, otherwise it wont be possible to generate the shared object! + +2) Add "externally linked" functions to your library as you wish. You can make a Function "externally linked" by right clicking + on the new function ->Properties->Build and select "External implementation". + This function must include "_cext" in its name, otherwise it wont be loaded by the runtime! + E.g.: TestFunction_cext + +3) Now create the runtime system files by selecting Build->"Generate runtime system files". + Choose a suitable path as output directory. + Enable the checkboxes for "M4 interface file" and "C stub file" as you need to generate both: C and M4 files. + Also check that the component name is equal to the name of your library, e.g. "CmpFirstTest" + +4) Save or transfer these generated files (*.c and *Itf.m4 file) to the on your linux system + + +On the linux system (at ) +1) Go to your and try to build your component with + make all + +2) Now a shared object, with the same name as your IEC library should have been generated in the "out" folder + +3) Now you can transfer, copy or symbolically link that to a location on your target system where libraries are normally found (e.g. /usr/lib ) + e.g. if you are on the target system: + sudo cp out/libCmpFirstTest.so /usr/lib + +4) Stop the runtime on your target e.g. with + sudo /etc/init.d/codesyscontrol stop + +5) Open the configuration file CODESYSControl_User.cfg of the runtime, e.g. with nano + and add an entry at the section [ComponentManager], where the components are listed: + [ComponentManager] + Component.1=CmpFirstTest + Component.2=CmpSomeOtherComponent + Component.3=CmpSomeMoreComponent + ... + + hints: + - the number or index in this setting needs to be incrementing, it might not collide with already existing settings/component indices + - if the line starts with ; it is a comment and the setting is not active + +6) Then restart your runtime again, e.g. with + sudo /etc/init.d/codesyscontrol start + +7) and check that your component is properly loaded, e.g. check the logfile (via CODESYS IDE), or under codesyscontrol.log + cat /var/opt/codesys/codesyscontrol.log + you should see a message like this: + ... Dynamic: CmpFirstTest, 0xffff2000 1.0.0.0 + +-> Congratulations! you made your first component and it runs from the CODESYS Control runtime +-> if not: check the FAQs section at the end of this file + + +On the Windows system (where CODESYS is installed) +1) go to the CODESYS project where you want to use your newly created IEC library, and use your library + +2) make a download and check if your code is called (e.g. make a printf there) + +########################## + + + +########################## +FAQs: + 1) My component does not build, what is going wrong? + - this might have very different reasons. Check what compiler (CC), flags (CFLAGS/LDFLAGS), include paths (INCLUDES) and libraries (LDLIBS) you are setting in your makefile + - check if the code you are writing does compile without this SDK stuff + - check if the functions in the component are named with "_cext" + + 2) My component is not loaded from the runtime? + - if you see a message in the log, like: + **** ERROR: libCmpFirstTest.so: cannot open shared object file: No such file or directory + or + **** ERROR: *** Error: Component CmpFirstTest NOT loaded + -> then your shared object is not found. Please double check that you put it somewhere on your linuxsystem, where it can be found (e.g. /usr/lib/) + + 3) Can I output some messages from my code? Where is this output? + - just use the normal printf() function in your code. This will be output at the "debug logger". To enable the debug logger, just change the file /etc/defaults/codesyscontrol: + set these variables: + DEBUGOUTPUT=1 + and + DEBUGLOGFILE=/tmp/codesyscontrol_debug.log + then restart the runtime. Then you can see the output at this logfile + + +########################## \ No newline at end of file diff --git a/sdk/ExtensionSDK/include/CmpErrors.h b/sdk/ExtensionSDK/include/CmpErrors.h new file mode 100644 index 0000000..93f6a72 --- /dev/null +++ b/sdk/ExtensionSDK/include/CmpErrors.h @@ -0,0 +1,16 @@ +/* + * this is a special header for the SL extension API!!! + * + * Copyright (c) 2017-2020 CODESYS Development GmbH, Copyright (c) 1994-2016 3S-Smart Software Solutions GmbH. All rights reserved. + * + */ + + +#ifndef __CMPERRORS_H__ +#define __CMPERRORS_H__ + + +#define ERR_OK 0 +#define ERR_NOTINITIALIZED 0x0003 + +#endif \ No newline at end of file diff --git a/sdk/ExtensionSDK/include/CmpItf.h b/sdk/ExtensionSDK/include/CmpItf.h new file mode 100644 index 0000000..4ae6f97 --- /dev/null +++ b/sdk/ExtensionSDK/include/CmpItf.h @@ -0,0 +1,711 @@ +/* + * this is a special header for the SL extension API!!! + * + * Copyright (c) 2017-2020 CODESYS Development GmbH, Copyright (c) 1994-2016 3S-Smart Software Solutions GmbH. All rights reserved. + * + */ + +#ifndef __CMPITF_H__ +#define __CMPITF_H__ + +#include + +#if defined(WIN32) || defined(_WIN32_WCE) + #ifdef _USRDLL + #define DLL_DECL _declspec(dllexport) + #endif +#endif +#if defined(LINUX) || defined(QNX) + #ifdef _USRDLL + #define DLL_DECL __attribute__ ((visibility ("default"))) + #endif +#endif + +#if !defined(DLL_DECL) + #define DLL_DECL +#endif + +#ifndef CDECL + #define CDECL +#endif + +#ifndef CDECL_EXT + #define CDECL_EXT +#endif + +#ifndef STDCALL + #define STDCALL __stdcall +#endif + +#if defined(__cplusplus) + extern "C" { +#endif + + +typedef RTS_UI32 CMPID; /* Component ID to specify a component. Has the vendor ID as a prefix! */ +typedef RTS_UI32 CLASSID; /* Class ID to specify a specific class (C or C++) */ +typedef RTS_UI32 OBJID; /* Object ID, typically the instance index starting with 0 */ +typedef RTS_UI32 ITFID; /* Interface ID to specify a specific interface (C or C++) */ + +#define RTS_VENDORID_3S 0x0000 +#define RTS_ADDVENDORID(VendorID, ID) (((RTS_UI32)(VendorID) << 16 & UINT32_C(0xFFFF0000)) + (ID)) +#define ADDVENDORID RTS_ADDVENDORID +#define RTS_GETVENDORID(ID) ((RTS_UI32)(ID) >> 16 & UINT32_C(0x0000FFFF)) +#ifdef TRG_64BIT + #define RTSITF_GET_SIGNATURE(sign32bit, sign64bit) sign64bit +#else + #define RTSITF_GET_SIGNATURE(sign32bit, sign64bit) sign32bit +#endif + + +/* Function that can be used for all CAL_ macros to return ERR_NOTIMPLEMENTED */ +RTS_RESULT CDECL FUNCTION_NOTIMPLEMENTED(unsigned long dummy,...); +RTS_RESULT CDECL FUNCTION_NOTIMPLEMENTED2(void *dummy,...); + +/* Structure to handle a mapping of an ID and a name */ +typedef struct RTS_ID_TO_NAME_ +{ + RTS_UI32 id; + char *pszName; +} RTS_ID_TO_NAME; + + +struct tagIBase; + +typedef struct +{ + /*pointer to virtual function table*/ + void* __VFTABLEPOINTER; +} instance_struct; + +typedef struct +{ + instance_struct *pInstance; + RTS_IEC_DWORD dwCount; +} addref_struct; + +typedef struct +{ + instance_struct *pInstance; + RTS_IEC_DWORD dwCount; +} release_struct; + +typedef struct +{ + instance_struct *pInstance; + RTS_IEC_DWORD iid; + RTS_IEC_RESULT *pResult; + instance_struct **ppInterface; +} queryinterface_struct; + +typedef unsigned long (CDECL *PF_ADDREF)(struct tagIBase *pIBase); +typedef unsigned long (CDECL *PF_RELEASE)(struct tagIBase *pIBase); +typedef void *(CDECL *PF_QUERYINTERFACE)(struct tagIBase *pIBase, ITFID iid, RTS_RESULT *pResult); + +typedef void (CDECL *PF_IEC_ADDREF)(void); +typedef void (CDECL *PF_IEC_RELEASE)(void); +typedef void (CDECL *PF_IEC_QUERYINTERFACE)(void); + +/* */ +typedef struct tagIBase +{ + int bIEC; + int iRefCount; + RTS_HANDLE hInstance; + PF_ADDREF AddRef; + PF_RELEASE Release; + PF_QUERYINTERFACE QueryInterface; + CLASSID ClassId; +} IBase_C; + +typedef struct tagIBase_IEC +{ + int bIEC; + int iRefCount; + RTS_HANDLE hInstance; + PF_IEC_ADDREF *AddRef; + PF_IEC_RELEASE *Release; + PF_IEC_QUERYINTERFACE *QueryInterface; +} IBase_IEC; + + +#ifndef STATICITF + #define STATICITF static +#endif + +#ifndef STATICITF_DEF + #define STATICITF_DEF +#endif + +#if defined(CPLUSPLUS) && defined(__cplusplus) + class IBase + { + public: + virtual unsigned long AddRef(IBase *pIBase = NULL) =0; + virtual unsigned long Release(IBase *pIBase = NULL) =0; + virtual void *QueryInterface(IBase *pIBase = NULL, ITFID iid = 0, RTS_RESULT *pResult = NULL) =0; + virtual ~IBase() { }; + }; + + #define DECLARE_ADDREF()\ + virtual unsigned long AddRef(IBase *pIBase = NULL); + + #define IMPLEMENT_ADDREF()\ + virtual unsigned long AddRef(IBase *pIBase = NULL)\ + {\ + iRefCount++;\ + return iRefCount;\ + } + + #define DECLARE_RELEASE()\ + virtual unsigned long Release(IBase *pIBase = NULL); + + #define IMPLEMENT_RELEASE()\ + virtual unsigned long Release(IBase *pIBase = NULL)\ + {\ + iRefCount--;\ + if (iRefCount == 0)\ + {\ + delete this;\ + return 0;\ + }\ + return iRefCount;\ + } + + #define IMPLEMENT_RELEASE_NODELETE()\ + virtual unsigned long Release(IBase *pIBase = NULL)\ + {\ + iRefCount--;\ + if (iRefCount == 0)\ + {\ + return 0;\ + }\ + return iRefCount;\ + } +#else + #define DECLARE_ADDREF\ + static unsigned long CDECL AddRef(struct tagIBase *pIBase); + + #define IMPLEMENT_ADDREF\ + static unsigned long CDECL AddRef(struct tagIBase *pIBase)\ + {\ + return (pIBase ? ++pIBase->iRefCount : 0);\ + } + + #define DECLARE_RELEASE\ + static unsigned long CDECL Release(struct tagIBase *pIBase); + + #define IMPLEMENT_RELEASE\ + static unsigned long CDECL Release(struct tagIBase *pIBase)\ + {\ + return (pIBase ? --pIBase->iRefCount : 0);\ + } + + #define DECLARE_QUERYINTERFACE\ + static void *CDECL QueryInterface(IBase *pBase, ITFID iid, RTS_RESULT *pResult); + + typedef IBase_C IBase; +#endif + +/* Global function pointer declarations */ +typedef int (CDECL *PF_EXPORT_FUNCTIONS)(void); +typedef int (CDECL *PF_IMPORT_FUNCTIONS)(void); +typedef RTS_UI32 (CDECL *PF_GET_VERSION)(void); +typedef RTS_RESULT (CDECL *PF_HOOK_FUNCTION)(RTS_UI32 ulHook, RTS_UINTPTR ulParam1, RTS_UINTPTR ulParam2); +/* typedef RTS_RESULT (CDECL *PF_REGISTER_API)(const char *pszAPIName, RTS_VOID_FCTPTR pfAPIFunction, int bExternalLibrary, RTS_UI32 ulSignatureID); */ +typedef RTS_RESULT (CDECL *PF_REGISTER_API)(const CMP_EXT_FUNCTION_REF *pExpTable, RTS_UINTPTR dummy, int bExternalLibrary, RTS_UI32 cmpId); +typedef RTS_RESULT (CDECL *PF_GET_API)(char *pszAPIName, RTS_VOID_FCTPTR *ppfAPIFunction, RTS_UI32 ulSignatureID); +typedef RTS_RESULT (CDECL *PF_REGISTER_API2)(const char *pszAPIName, RTS_VOID_FCTPTR pfAPIFunction, int bExternalLibrary, RTS_UI32 ulSignatureID, RTS_UI32 ulVersion); +typedef RTS_RESULT (CDECL *PF_GET_API2)(char *pszAPIName, RTS_VOID_FCTPTR *ppfAPIFunction, int bExternalLibrary, RTS_UI32 ulSignatureID, RTS_UI32 ulVersion); +typedef RTS_RESULT (CDECL *PF_CALL_HOOK)(RTS_UI32 ulHook, RTS_UINTPTR ulParam1, RTS_UINTPTR ulParam2, int bReverse); +typedef IBase* (CDECL *PF_CREATEINSTANCE)(CLASSID cid, RTS_RESULT *pResult); +typedef RTS_RESULT (CDECL *PF_DELETEINSTANCE)(IBase *pIBase); +typedef RTS_HANDLE (CDECL *PF_REGISTER_CLASS)(CMPID CmpId, CLASSID ClassId); +typedef RTS_RESULT (CDECL *PF_DELETEINSTANCE2)(CLASSID ClassId, IBase *pIBase); + +typedef struct tagINIT_STRUCT +{ + /* Exported by Component */ + CMPID CmpId; +#ifdef TRG_64BIT + RTS_UI32 aligndummy; +#endif + PF_EXPORT_FUNCTIONS pfExportFunctions; + PF_IMPORT_FUNCTIONS pfImportFunctions; + PF_GET_VERSION pfGetVersion; + PF_CREATEINSTANCE pfCreateInstance; + PF_DELETEINSTANCE pfDeleteInstance; + PF_HOOK_FUNCTION pfHookFunction; + + /* Import from CM */ + PF_REGISTER_API pfCMRegisterAPI; + PF_GET_API pfCMGetAPI; + PF_CALL_HOOK pfCMCallHook; + PF_REGISTER_CLASS pfCMRegisterClass; + PF_CREATEINSTANCE pfCMCreateInstance; + PF_REGISTER_API2 pfCMRegisterAPI2; + PF_GET_API2 pfCMGetAPI2; + PF_DELETEINSTANCE2 pfCMDeleteInstance2; +} INIT_STRUCT; + +#define COMPONENT_ENTRY_NAME "ComponentEntry" + +#if defined(CPLUSPLUS) && defined(__cplusplus) +extern "C" { +#endif + +#if !defined(STATIC_LINK) && !defined(MIXED_LINK) && !defined(DYNAMIC_LINK) && !defined(CPLUSPLUS_STATIC_LINK) + DLL_DECL int CDECL ComponentEntry(INIT_STRUCT *pInitStruct); +#endif +typedef int (CDECL *PF_COMPONENT_ENTRY)(INIT_STRUCT *pInitStruct); + +#if defined(CPLUSPLUS) && defined(__cplusplus) +} +#endif + + +/* Functions exported from main */ +RTS_RESULT CDECL MainLoadComponent(char *pszName, char *pszPath, RTS_HANDLE *phModule, PF_COMPONENT_ENTRY* ppfComponentEntry); +RTS_RESULT CDECL MainUnloadComponent(char *pszName, RTS_HANDLE hModule); + + +/* The following functions are always offered by the component manager */ +int CDECL MainExitLoop(RTS_UINTPTR ulPar); +typedef int (CDECL *PFMAINEXITLOOP)(RTS_UINTPTR ulPar); +#ifdef STATIC_LINK + #define USE_CMExitLoop extern int CDECL MainExitLoop(RTS_UINTPTR ulPar); + #define EXT_CMExitLoop extern int CDECL MainExitLoop(RTS_UINTPTR ulPar); + #define GET_CMExitLoop ERR_OK + #define CAL_CMExitLoop MainExitLoop + #define CHK_CMExitLoop TRUE + #define EXP_CMExitLoop TRUE +#else /* DYNAMIC_LINK */ + #define USE_CMExitLoop PFMAINEXITLOOP pfMainExitLoop; + #define EXT_CMExitLoop extern PFMAINEXITLOOP pfMainExitLoop; + #define GET_CMExitLoop s_pfGetAPI("CMExitLoop", (void **)&pfMainExitLoop, 0) + #define CAL_CMExitLoop pfMainExitLoop + #define CHK_CMExitLoop (pfMainExitLoop != NULL) + #define EXP_CMExitLoop s_pfRegisterAPI("CMExitLoop", MainExitLoop, 1, 0) +#endif + +/** + * Hook definitions to inform all components with a broadcast via HookFunction() ([0..10000] reserved for core components): + *
    + *
  • CH_INIT_XXX Hooks: Used for initialization of all components. + * Future range, not yet defined! = [1..1000]
  • + *
  • CH_EXIT_XXX Hooks: Used for deinitialization of all components. + * Future range, not yet defined! = [2000..3000]
  • + *
  • CH_XXX Hooks: Used for broadcast commands (e.g. cyclic operations). + * Future range, not yet defined! = [4000..5000]
  • + *
+ * IMPLEMENTATION NOTE: + * Future hooks should be defined in the appropriate range above! + *
+ */ + +/* + CH_INIT_XXX Hooks: Used for initialization of all components. + */ +#define CH_INIT_SYSTEM 1 +/* First call at system startup to initialize all system components. + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_INIT_SYSTEM2 8 +/* Second call at system startup to initialize all system components. + (used to resolve dependencies between core components) + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_INIT 2 +/* First call at system startup. Compontents should initialize all local variables. + ulParam1: Pointer to int. Parameter name=pbDontCallInitHooksFromCMInit. Content can be set as followed: + 0 [Default]: CMInitEnd() is called at the end of CMInit(). + 1: CMInitEnd (calling hooks from CH_INIT2 to CH_INIT_COMM) can be called asynchronously by your own + component and is not called in the context of CMInit()! + ulParam2: Not used +*/ + +#define CH_POST_INIT 200 +/* Called right after CH_INIT. Use this hook to run initialization between + highly related components (e.g. Register backends or providers to the corresponding components.). + + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_PRE_INIT2 250 +/* Called right before CH_INIT2. Use this hook to finish initialization of a component that is highly + related to an other components. E.g. finish initialization after the providers have registered. + + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_INIT2 3 +/* Called after CH_INIT. All components are initialized. Other components can be used. + + NOTE: + Events must be created of the provider in this init step! + + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_INIT_DONE CH_INIT2 +/* Backward compatibility */ + +#define CH_PRE_INIT3 7 +/* Called after CH_INIT2 and before CH_INIT3. All components are initialized. Other components can be used. + ulParam1: Not used + ulParam2: Not used +*/ +#define CH_INIT201 CH_PRE_INIT3 +/* Backward compatibility */ + +#define CH_INIT3 4 +/* Called after CH_INIT201. All components are initialized. Other components can be used. + + NOTE: + Event callbacks must be registered by the consumer in this init step! Events are created in CH_INIT2. + + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_INIT_SYSTEM_TASKS 500 +/* Called after CH_INIT3. Tasks can be started that are relevant for the IEC applications. + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_INIT_TASKS 5 +/* Called after CH_INIT_SYSTEM_TASKS. All components should start their tasks. + + NOTE: + The IEC bootprojects are loaded here! + + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_INIT_COMM 6 +/* Called after CH_INIT_TASKS. Communication can be started at this point, because all + components are ready and running. + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_INIT_FINISHED 1000 +/* Called at the end of the complete initialization sequence. This is called after CH_INIT_COMM and right before the runtime system + fall into the cyclic CH_COMM_CYCLE state. + + NOTE: + The IEC bootprojects are started here! + + ulParam1: Not used + ulParam2: Not used +*/ + +/* + CH_EXIT_XXX Hooks: Used for deinitialization of all components. +*/ + +#define CH_EXIT_COMM 10 +/* Called at the beginning of the shutdown sequence to disable all communication channels! + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_EXIT_TASKS 11 +/* Called before CH_EXIT_SYSTEM_TASKS to stop and remove all tasks that are not needed by the IEC application. + + NOTE: + IEC applications are unloaded here. + + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_EXIT_SYSTEM_TASKS 2200 +/* Called before CH_EXIT3 to stop and remove all running tasks, because at CH_EXIT3 all objects are released and + so no task must run after this point. + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_EXIT3 12 +/* Called before CH_EXIT2 to store data (e.g. retain data). + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_EXIT2 13 +/* Called before CH_EXIT to store data (e.g. retain data). + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_PRE_EXIT CH_EXIT2 +/* Backward compatibility */ + +#define CH_EXIT 14 +/* Here all memory of the components should be released. + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_EXIT_SYSTEM2 17 +/* Counter part to CH_INIT_SYSTEM2. + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_EXIT_SYSTEM 15 +/* Called at the end of the shutdown process for the system components to release memory. + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_EXIT_GARBAGEMEMORY 16 +/* This is the final call for the garbage collector to check, if heap memory was not released. + ulParam1: Not used + ulParam2: Not used +*/ + +/* + CH_XXX Hooks: Used for broadcast commands (e.g. cyclic operations). + */ +#define CH_COMM_CYCLE 20 +/* Called every main cycle period. Can be used for background executions. + ulParam1: Not used + ulParam2: Not used +*/ + +#define CH_ON_IMPORT 23 +/* Called for components to import new functions from a new loaded component + ulParam1: char *: Pointer to component name + ulParam2: Not used +*/ + +#define CH_ON_IMPORT_RELEASE 24 +/* Called for components to release imported functions from a component that will be unloaded + ulParam1: char *: Pointer to component name + ulParam2: Not used +*/ + +#define CH_ON_LOAD_COMPONENT 25 +/* Called after loading a new component + ulParam1: char *: Pointer to component name + ulParam2: Not used +*/ + +#define CH_ON_UNLOAD_COMPONENT 26 +/* Called before unloading a component + ulParam1: char *: Pointer to component name + ulParam2: Not used +*/ + +#define CH_ON_REGISTER_INSTANCE 27 +/* Called after register a new instance + ulParam1: CLASSID: ClassId of instance + ulParam2: IBase *: Pointer to IBase +*/ + +#define CH_ON_UNREGISTER_INSTANCE 28 +/* Called before unregister a new instance + ulParam1: CLASSID: ClassId of instance + ulParam2: IBase *: Pointer to IBase +*/ + +#define CH_ON_LICENSE_FCTS_REGISTERED 29 +/* + Called after the functionpointers from the license-library + have been registered to the runtime. +*/ + +#define CH_ON_LICENSE_FCTS_REGISTERED2 290 +/* + Called after CH_ON_LICENSE_FCTS_REGISTERED. +*/ + +#define CH_ON_LICENSE_FCTS_LEGACY_REGISTERED 30 +/* + Called after the functionpointers from the license-library + have been registered to the runtime. +*/ + +#define CH_SAFEMODE 4000 +/* + Called when a component fails to resolve its required import functions during startup of the runtime system. In this case, the system is not stable any more. + Every component should check safety. + After entering the safemode, at least the communication should work and so for example a service engineer can investigate the reason for this error. + The logger should work too. + + IMPORTANT NOTE: + It is important, that the IEC-application(s) are not loaded or at least are not started after entering the safemode! This is still implemented in the CmpApp component. + + ulParam1: Not used + ulParam2: Not used +*/ + +/** +* Hook table +* +* This is the table of all hooks with its corresponding name (see CM_HOOK_TABLE). +* +* NOTE: +* The init and exit hooks are defined here in the order they are executed during startup and shutdown! +* +* +*/ +#define CM_INIT_HOOK_TABLE { CH_INIT_SYSTEM, "CH_INIT_SYSTEM" },\ + { CH_INIT_SYSTEM2, "CH_INIT_SYSTEM2" },\ + { CH_INIT, "CH_INIT" },\ + { CH_POST_INIT, "CH_POST_INIT" },\ + { CH_PRE_INIT2, "CH_PRE_INIT2" },\ + { CH_INIT2, "CH_INIT2" },\ + { CH_PRE_INIT3, "CH_PRE_INIT3" },\ + { CH_INIT3, "CH_INIT3" },\ + { CH_INIT_SYSTEM_TASKS, "CH_INIT_SYSTEM_TASKS" },\ + { CH_INIT_TASKS, "CH_INIT_TASKS" },\ + { CH_INIT_COMM, "CH_INIT_COMM" },\ + { CH_INIT_FINISHED, "CH_INIT_FINISHED" } + +#define CM_COMM_CYCLE_HOOK { CH_COMM_CYCLE, "CH_COMM_CYCLE" } + +#define CM_EXIT_HOOK_TABLE { CH_EXIT_COMM, "CH_EXIT_COMM" },\ + { CH_EXIT_TASKS, "CH_EXIT_TASKS" },\ + { CH_EXIT_SYSTEM_TASKS, "CH_EXIT_SYSTEM_TASKS" },\ + { CH_EXIT3, "CH_EXIT3" },\ + { CH_EXIT2, "CH_EXIT2" },\ + { CH_EXIT, "CH_EXIT" },\ + { CH_EXIT_SYSTEM2, "CH_EXIT_SYSTEM2" },\ + { CH_EXIT_SYSTEM, "CH_EXIT_SYSTEM" },\ + { CH_EXIT_GARBAGEMEMORY, "CH_EXIT_GARBAGEMEMORY" } + +#define CM_EXTENDED_HOOK_TABLE { CH_ON_IMPORT, "CH_ON_IMPORT" }, \ + { CH_ON_IMPORT_RELEASE, "CH_ON_IMPORT_RELEASE" }, \ + { CH_ON_LOAD_COMPONENT, "CH_ON_LOAD_COMPONENT" }, \ + { CH_ON_UNLOAD_COMPONENT, "CH_ON_UNLOAD_COMPONENT" }, \ + { CH_ON_REGISTER_INSTANCE, "CH_ON_REGISTER_INSTANCE" }, \ + { CH_ON_UNREGISTER_INSTANCE, "CH_ON_UNREGISTER_INSTANCE" }, \ + { CH_ON_LICENSE_FCTS_REGISTERED, "CH_ON_LICENSE_FCTS_REGISTERED" }, \ + { CH_ON_LICENSE_FCTS_REGISTERED2, "CH_ON_LICENSE_FCTS_REGISTERED2" }, \ + { CH_ON_LICENSE_FCTS_LEGACY_REGISTERED, "CH_ON_LICENSE_FCTS_LEGACY_REGISTERED" }, \ + { CH_SAFEMODE, "CH_SAFEMODE" } \ + +#define CM_HOOK_TABLE {\ + CM_INIT_HOOK_TABLE,\ + CM_COMM_CYCLE_HOOK,\ + CM_EXIT_HOOK_TABLE,\ + CM_EXTENDED_HOOK_TABLE\ + } + +/** + * Single import of interface functions + * + * These macros are used by the System-Components to import single interface functions without modifying + * the global SysXXXDep.m4 files. + * + * ATTENTION: + * With usage of these macros, the RtsConfiguration cannot resolve this dependency anymore!!! + * So use these macros only in Sys-Components and not in standard or customer components! + * + * Usage: + * #include "Itf.h" + * USE_SINGLE_ITF() or USE_SINGLE_ITF(, ) + * USE_SINGLE_FUNCTION() + * + * static int CDECL ImportFunctions(void) + * { + * IMPORT_SINGLE_ITF(); + * IMPORT_SINGLE_FUNCTION(); + * return ERR_OK; + * } + * + */ +#ifdef CPLUSPLUS + #define USE_SINGLE_ITF(Interface_) ITF_##Interface_ + + #define IMPORT_SINGLE_ITF(Interface_) \ + {\ + IBase *pIBase_;\ + RTS_RESULT Result_;\ + if (pI##Interface_ == NULL && s_pfCMCreateInstance != NULL) \ + { \ + pIBase_ = (IBase *)s_pfCMCreateInstance(CLASSID_C##Interface_, &Result_); \ + if (pIBase_ != NULL) \ + {\ + pI##Interface_ = (I##Interface_ *)pIBase_->QueryInterface(pIBase_, ITFID_I##Interface_, &Result_); \ + pIBase_->Release(pIBase_); \ + }\ + } \ + } + + #define EXIT_SINGLE_ITF(Interface_) \ + {\ + IBase *pIBase_;\ + RTS_RESULT exitResult_;\ + if (pI##Interface_ != NULL) \ + { \ + pIBase_ = (IBase *)pI##Interface_->QueryInterface(pI##Interface_, ITFID_IBase, &exitResult_); \ + if (pIBase_ != NULL && exitResult_ == ERR_OK) \ + { \ + pIBase_->Release(pIBase_); \ + if (pIBase_->Release(pIBase_) == 0) /* The object will be deleted here! */ \ + pI##Interface_ = NULL; \ + } \ + } \ + } + + #define IMPORT_SINGLE_ITF2(Class_, Interface_) \ + {\ + IBase *pIBase_;\ + RTS_RESULT Result_;\ + if (pI##Interface_ == NULL && s_pfCMCreateInstance != NULL) \ + { \ + pIBase_ = (IBase *)s_pfCMCreateInstance(CLASSID_C##Class_, &Result_); \ + if (pIBase_ != NULL) \ + {\ + pI##Interface_ = (I##Interface_ *)pIBase_->QueryInterface(pIBase_, ITFID_I##Interface_, &Result_); \ + pIBase_->Release(pIBase_); \ + }\ + } \ + } + + #define EXIT_SINGLE_ITF2(Class_, Interface_) \ + {\ + IBase *pIBase_;\ + RTS_RESULT exitResult_;\ + if (pI##Interface_ != NULL) \ + { \ + pIBase_ = (IBase *)pI##Interface_->QueryInterface(pI##Interface_, ITFID_IBase, &exitResult_); \ + if (pIBase_ != NULL && exitResult_ == ERR_OK) \ + { \ + pIBase_->Release(pIBase_); \ + if (pIBase_->Release(pIBase_) == 0) /* The object will be deleted here! */ \ + pI##Interface_ = NULL; \ + } \ + } \ + } +#else + #define USE_SINGLE_ITF(Interface_) + #define IMPORT_SINGLE_ITF(Interface_) + #define EXIT_SINGLE_ITF(Interface_) + #define IMPORT_SINGLE_ITF2(Class_, Interface_) + #define EXIT_SINGLE_ITF2(Class_, Interface_) +#endif + +#define USE_SINGLE_FUNCTION(Function) USE_##Function +#define IMPORT_SINGLE_FUNCTION(Function) GET_##Function(0) +#define EXIT_SINGLE_FUNCTION(Function) + + +#if defined(__cplusplus) +} +#endif + +#endif /*__CMPITF_H__*/ diff --git a/sdk/ExtensionSDK/include/CmpStd.h b/sdk/ExtensionSDK/include/CmpStd.h new file mode 100644 index 0000000..a23e477 --- /dev/null +++ b/sdk/ExtensionSDK/include/CmpStd.h @@ -0,0 +1,641 @@ +/* + * this is a special header for the SL extension API!!! + * + * Copyright (c) 2017-2020 CODESYS Development GmbH, Copyright (c) 1994-2016 3S-Smart Software Solutions GmbH. All rights reserved. + * + */ + +#ifndef __RTSSTD_H__ +#define __RTSSTD_H__ + + +#if defined(__amd64__) + #define TRG_X64 + #define TRG_64BIT +#elif defined(__arm__) + #define TRG_ARM +#elif defined(__aarch64__) + #define TRG_ARM + #define TRG_64BIT +#elif defined(i386) + #error "32 bit X86 is not supported!" +#else + #error "Unknown architecture!" +#endif + +#define LINUX + +#define CDECL +#define __cdecl CDECL /*necessary for sqlite in CmpCAAStorage*/ +#define STDCALL +#define HUGEPTR + +/****** OS *****/ +#define SYSTARGET_OS_LINUX + +/*** types ***/ +#ifndef STDINT_H_UINTPTR_T_DEFINED + #define STDINT_H_UINTPTR_T_DEFINED +#endif +typedef unsigned short RTS_WCHAR; +#define RTS_WCHAR_DEFINED + +#define INTEL_BYTE_ORDER + + +#include +#include +#include +#include +#include +#include +#include + + + +/** + * set default register width + */ +#ifndef RTS_REGISTER_WIDTH + #if defined(TRG_C16X) || defined(TRG_DSP) + #define RTS_REGISTER_WIDTH 16 + #elif defined(TRG_64BIT) || defined(TRG_IA64) + #define RTS_REGISTER_WIDTH 64 + #else + #define RTS_REGISTER_WIDTH 32 + #endif +#endif + +#if RTS_REGISTER_WIDTH == 64 && !defined (SIZEOF_LONG) + #if defined (SYSTARGET_OS_WINDOWS) || defined (SYSTARGET_OS_WINDOWS_RTE) || defined (SYSTARGET_OS_WINDOWS_CE) + #define SIZEOF_LONG 4 + #else + #define SIZEOF_LONG 8 + #endif +#endif + +#include + +/** + * Redefine UINT32_C(), INT16_C, ..., + * as many compilers are defining this in a bad way. They + * are using type-casts to get the correct data-width. This + * makes it impossible to use for some pedantic C-Compilers. + */ +#ifdef INT64_C + #undef INT64_C +#endif +#ifdef UINT64_C + #undef UINT64_C +#endif +#ifdef INT32_C + #undef INT32_C +#endif +#ifdef UINT32_C + #undef UINT32_C +#endif +#ifdef INT16_C + #undef INT16_C +#endif +#ifdef UINT16_C + #undef UINT16_C +#endif +#ifdef INT8_C + #undef INT8_C +#endif +#ifdef UINT8_C + #undef UINT8_C +#endif + +#define INT64_C(x) (RTS_I64_CONST(x)) +#define UINT64_C(x) (RTS_UI64_CONST(x)) +#define INT32_C(x) (x ## L) +#define UINT32_C(x) (x ## UL) +#define INT16_C(x) (x) +#define UINT16_C(x) (x ## U) +#define INT8_C(x) (x) +#define UINT8_C(x) (x ## U) + +#define RTS_IEC_BOOL_C(x) (x) +#define RTS_IEC_BYTE_C(x) (UINT8_C(x)) +#define RTS_IEC_SINT_C(x) (INT8_C(x)) +#define RTS_IEC_USINT_C(x) (UINT8_C(x)) +#define RTS_IEC_INT_C(x) (INT16_C(x)) +#define RTS_IEC_UINT_C(x) (UINT16_C(x)) +#define RTS_IEC_DINT_C(x) (INT32_C(x)) +#define RTS_IEC_UDINT_C(x) (UINT32_C(x)) +#define RTS_IEC_LINT_C(x) (INT64_C(x)) +#define RTS_IEC_ULINT_C(x) (UINT64_C(x)) +#define RTS_IEC_WORD_C(x) (UINT16_C(x)) +#define RTS_IEC_DWORD_C(x) (UINT32_C(x)) +#define RTS_IEC_LWORD_C(x) (UINT64_C(x)) +#define RTS_IEC_REAL_C(x) (x) +#define RTS_IEC_LREAL_C(x) (x) +#ifdef TRG_64BIT +#define RTS_IEC_XINT_C(x) (INT64_C(x)) +#define RTS_IEC_UXINT_C(x) (UINT64_C(x)) +#define RTS_IEC_XWORD_C(x) (UINT64_C(x)) +#else +#define RTS_IEC_XINT_C(x) (INT32_C(x)) +#define RTS_IEC_UXINT_C(x) (UINT32_C(x)) +#define RTS_IEC_XWORD_C(x) (UINT32_C(x)) +#endif + +/** + * Define some more exotic types, which are missing + * in many versions of stdint.h. + */ +#if !defined(STDINT_H_UINTPTR_T_DEFINED) +/* Note: If your compiler complains about a "redefinition" + * or s.th. similar here, you may set the defines + * _UINTPTR_T, _PTRDIFF_T, ... in your sysdefines.h. + */ + #ifndef _UINTPTR_T + typedef uint32_t uintptr_t; + #define _UINTPTR_T + #endif + #ifndef _PTRDIFF_T + typedef int32_t ptrdiff_t; + #define _PTRDIFF_T + #endif + #ifndef _INTPTR_T + typedef ptrdiff_t intptr_t; + #define _INTPTR_T + #endif + #define STDINT_H_UINTPTR_T_DEFINED +#endif /*!defined(STDINT_H_UINTPTR_T_DEFINED)*/ + +/* Format modifiers for integral types */ +#ifndef ULONG_MAX +# include +# ifndef ULONG_MAX +# error ULONG_MAX does not exist in limits.h! +# endif +#endif + +/* + * Some basic definitions + */ + +#ifndef NULL +# define NULL 0L +#endif + +#ifndef TRUE +# define TRUE 1 +#endif + +#ifndef FALSE +# define FALSE 0 +#endif + +/*lint -e666 -e506 */ +#ifndef MIN +# define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX +# define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +#ifndef RTS_SETRESULT +# define RTS_SETRESULT(p,r) do { if(p) *(p)=r; } while(0) +#endif + +#ifndef RTS_GETRESULT +# define RTS_GETRESULT(Return,Result) (Result == ERR_OK ? (Return) : ((Return) ? Result : Result)) +#endif + +#ifndef LOWWORD +# define LOWWORD(p) ((uint16_t)((uint32_t)(p) & 0x0000FFFF)) +#endif + +#ifndef HIGHWORD +# define HIGHWORD(p) ((uint16_t)(((uint32_t)(p)>>16) & 0x0000FFFF)) +#endif + + +/* Operations on flags */ +#ifndef RTS_SETFLAGS + #define RTS_SETFLAGS(value, flags) (value |= (flags)) +#endif +#ifndef RTS_SETFLAG + #define RTS_SETFLAG(value, flag) RTS_SETFLAGS(value, flag) +#endif + +#ifndef RTS_RESETFLAGS + #define RTS_RESETFLAGS(value, flags) (value &= ~(flags)) +#endif +#ifndef RTS_RESETFLAG + #define RTS_RESETFLAG(value, flag) RTS_RESETFLAGS(value, flag) +#endif + +#ifndef RTS_HASFLAGS + #define RTS_HASFLAGS(value, flags) ((value & (flags)) == (flags)) +#endif +#ifndef RTS_HASFLAG + #define RTS_HASFLAG(value, flag) (value & (flag)) +#endif + + + +#ifndef ALIGN_MEMORY +# define ALIGN_MEMORY(pMemory) ((void *)(((((uintptr_t)(pMemory) + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*)))) +#endif + +#ifndef ALIGN_MEMORY_SIZE +# define ALIGN_MEMORY_SIZE(pMemory, iSize) ((iSize) - (((((uintptr_t)(pMemory) + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*)) - (uintptr_t)(pMemory))) +#endif + +#ifndef ALIGN_MEMORY2 +# define ALIGN_MEMORY2(pMemory, Modulo) ((void *)((((uintptr_t)(pMemory) + (Modulo) - 1) / (uintptr_t)(Modulo)) * (uintptr_t)(Modulo))) +#endif + +#ifndef ALIGN_MEMORY2_BACK +# define ALIGN_MEMORY2_BACK(pMemory, Modulo) ((void *)(((uintptr_t)(pMemory) / (uintptr_t)(Modulo)) * (uintptr_t)(Modulo))) +#endif + +#ifndef ALIGN_MEMORY_SIZE2 +# define ALIGN_MEMORY_SIZE2(pMemory, iSize, Modulo) ((iSize) - (((((uintptr_t)(pMemory) + (Modulo) - 1) / sizeof(void*)) * (Modulo)) - (uintptr_t)(pMemory))) +#endif + +#ifndef ALIGN_SIZE +/* NOTE: it seems like this operation can be made without a comparison: */ +/* #define ALIGN_SIZE(Size, Modulo) (( (Size) + ((Modulo) - 1) ) / (Modulo)) * (Modulo) */ +# define ALIGN_SIZE(Size, Modulo) /*lint -e{506} */(((Size) % (Modulo)) ? ((Size) + ((Modulo) - ((Size) % (Modulo)))) : (Size)) +#endif + +#ifndef RTS_VSNPRINTF + #if defined(RTS_VSNPRINTF_NOT_AVAILABLE) + #define RTS_VSNPRINTF(s,len,format,arglist) vsprintf(s,format,arglist) + #else + #define RTS_VSNPRINTF(s,len,format,arglist) vsnprintf(s,len,format,arglist) + #endif +#endif + +#ifndef va_copy + #define va_copy(destination, source) ((destination) = (source)) +#endif + + +#define CONCAT(x,y) x##y + +/* Some preprocessors do not allow to place such a macro within other macros */ +/* and/or pragmas. */ +#define ____QUOTE____(x) #x +#define QUOTE(x) ____QUOTE____(x) + +#ifdef INTEL_BYTE_ORDER +# define SWAP16u(x) (x) +# define SWAP16s(x) (x) +# define SWAP32u(x) (x) +# define SWAP32s(x) (x) +#elif defined(MOTOROLA_BYTE_ORDER) +# define SWAP16u(x) ( (uint16_t)((((x) & 0xFF)<<8) | (((x) & 0xFF00) >> 8)) ) +# define SWAP16s(x) ( (int16_t)((((x) & 0xFF)<<8) | (((x) & 0xFF00) >> 8)) ) +# define SWAP32u(x) ( (uint32_t)((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) ) +# define SWAP32s(x) ((int32_t)((((uint32_t)(x)) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) +#else + #error "No byte order defined for the current target" +#endif + + + +#include "CmpErrors.h" + +#define CAL_CMGETAPI(name) ERR_OK +#define CAL_CMEXPAPI(name) ERR_OK + +typedef void HUGEPTR(CDECL *RTS_VOID_FCTPTR)(void); + + +/* + * Runtime system basic typedefinitions. + * Defines some types with special meaning. Systems not using standard type sizes + * must redefine this types in sysdefines.h and set BASETYPES_DEFINED + */ + +#ifndef RTS_I64_CONST + #define RTS_I64_CONST(a) (a##LL) +#endif +#ifndef RTS_UI64_CONST + #define RTS_UI64_CONST(a) (a##ULL) +#endif + +#ifndef BASETYPES_DEFINED +# define BASETYPES_DEFINED + + typedef int8_t RTS_I8; /* Signed 8 bit value */ + typedef int16_t RTS_I16; /* Signed 16 bit value */ +#ifndef RTS_I32_DEFINED + #define RTS_I32_DEFINED + typedef int32_t RTS_I32; /* Signed 32 bit value */ +#endif + + typedef uint8_t RTS_UI8; /* Unsigned 8 bit value */ +# define RTS_UI8_MAX (RTS_UI8)(~((RTS_UI8)0)) + + typedef uint16_t RTS_UI16; /* Unsigned 16 bit value */ +# define RTS_UI16_MAX (RTS_UI16)(~((RTS_UI16)0)) + +#ifndef RTS_UI32_DEFINED + #define RTS_UI32_DEFINED + typedef uint32_t RTS_UI32; /* Unsigned 32 bit value */ +#endif +# define RTS_UI32_MAX (RTS_UI32)(~((RTS_UI32)0)) + +#ifndef IEEE754TYPES_DEFINED +# define IEEE754TYPES_DEFINED + typedef float RTS_REAL32; /* 32 bit floating point value */ + typedef double RTS_REAL64; /* 64 bit floating point value */ + #define RTS_REAL32_MAX 3.4028234e+38 + #define RTS_REAL64_MAX 1.7976931348623157e+308 +#endif + + typedef void* RTS_HANDLE; /* Handle for files, semaphores, ... Usually the native integer type of the platform*/ + #define RTS_INVALID_HANDLE ((RTS_HANDLE)(~((uintptr_t)0))) + + typedef RTS_UI32 RTS_RESULT; /* Result type for most functions. At least 16 bit. Should be the natural integer of a platform. */ + + typedef uintptr_t RTS_UINTPTR; /* Unsigned integral type that can hold a pointer */ + #define RTS_UINTPTR_MAX (RTS_UINTPTR)(~((RTS_UINTPTR)0)) + + typedef intptr_t RTS_INTPTR; /* Signed integral type that can hold a pointer */ + #define RTS_INTPTR_MAX (RTS_INTPTR)(~((RTS_INTPTR)0)) + + typedef ptrdiff_t RTS_PTRDIFF; /* Signed integral type that can hold an array index */ + #define RTS_PTRDIFF_MAX (RTS_PTRDIFF)(~((RTS_PTRDIFF)0)) + +#ifndef RTS_SIZE_DEFINED + #define RTS_SIZE_DEFINED + typedef size_t RTS_SIZE; /* Unsigned integral type that can hold a buffer offset */ +#endif + #define RTS_SIZE_MAX (RTS_SIZE)(~((RTS_SIZE)0)) + +/* Type definition of RTS_SSIZE should be changed to standard datatype ssize_t, if available on any target! */ +#ifndef RTS_SSIZE_DEFINED + #define RTS_SSIZE_DEFINED + typedef intptr_t RTS_SSIZE; /* integral type that can hold a buffer offset */ +#endif + #define RTS_SSIZE_MAX (RTS_SSIZE)(~((RTS_SSIZE)0)) + + typedef RTS_UINTPTR RTS_TIME; /* Coordinated Universal Time (UTC) */ + + typedef int RTS_INT; /* Data type has no constant size, so be careful in sharing shuch datatypes with IEC! */ + typedef int RTS_BOOL; /* Boolean value (TRUE or FALSE). For best performance use platform specific int. + Data type has no constant size, so be carful in sharing shuch datatypes with IEC! */ +#if defined(RTS_DISABLE_64BIT_SUPPORT) && !defined(SYSINTERNAL_DISABLE_64BIT) + #define SYSINTERNAL_DISABLE_64BIT +#endif + +#if !defined(BASE64BITTYPES_DEFINED) && !defined(SYSINTERNAL_DISABLE_64BIT) +# define BASE64BITTYPES_DEFINED + typedef uint64_t RTS_UI64; + typedef int64_t RTS_I64; + #define RTS_UI64_MAX (RTS_UI64)(~((RTS_UI64)0)) + #define RTS_I64_MAX (RTS_I64)(~((RTS_I64)0)) +#endif + + typedef char RTS_CSTRING; + typedef char RTS_UTF8STRING; + +# ifndef RTS_WCHAR_DEFINED +# define RTS_WCHAR_DEFINED + typedef wchar_t RTS_WCHAR; /* wide character value. We expect 2 bytes unicode here! */ +# endif /*RTS_WCHAR_DEFINED*/ + +# ifndef RTS_CWCHAR_DEFINED +# define RTS_CWCHAR_DEFINED +# ifdef RTS_UNICODE + typedef RTS_WCHAR RTS_CWCHAR; +# else + typedef RTS_CSTRING RTS_CWCHAR; +# endif +# endif /*RTS_CWCHAR_DEFINED*/ + +#ifdef RTS_DEFINE_MODE_T + typedef unsigned short mode_t; +#endif +#ifdef RTS_DEFINE_GID_T + typedef short gid_t; +#endif +#ifdef RTS_DEFINE_UID_T + typedef short uid_t; +#endif +#ifdef RTS_DEFINE_ID_T + typedef intptr_t id_t; +#endif +#ifdef RTS_DEFINE_PID_T + typedef uintptr_t pid_t; +#endif +#ifdef RTS_DEFINE_OFF_T + typedef uintptr_t off_t; +#endif +#ifdef RTS_DEFINE_NLINK_T + typedef short nlink_t; +#endif + + + +#endif /*BASETYPES_DEFINED*/ + +typedef enum +{ + RTSTYPE_I8, + RTSTYPE_I16, + RTSTYPE_I32, + RTSTYPE_UI8, + RTSTYPE_UI16, + RTSTYPE_UI32, + RTSTYPE_REAL32, + RTSTYPE_REAL64, + RTSTYPE_HANDLE, + RTSTYPE_RESULT, + RTSTYPE_UINTPTR, + RTSTYPE_INTPTR, + RTSTYPE_SIZE, + RTSTYPE_PTRDIFF, + RTSTYPE_INT, + RTSTYPE_BOOL, + RTSTYPE_UI64, + RTSTYPE_I64, + RTSTYPE_CSTRING, + RTSTYPE_WCHAR, + RTSTYPE_CWCHAR +}RTS_TYPECLASS; + +typedef union +{ + RTS_I8 i8Value; + RTS_I16 i16Value; + RTS_I32 i32Value; + RTS_UI8 ui8Value; + RTS_UI16 ui16Value; + RTS_UI32 ui32Value; + RTS_REAL32 real32Value; + RTS_REAL64 real64Value; + RTS_HANDLE handleValue; + RTS_RESULT resultValue; + RTS_UINTPTR uintptrValue; + RTS_INTPTR intptrValue; + RTS_SIZE sizeValue; + RTS_PTRDIFF ptrdiffValue; + RTS_INT intValue; + RTS_BOOL boolValue; + RTS_UI64 ui64Value; + RTS_I64 i64Value; + RTS_CSTRING *cstringValue; + RTS_WCHAR *wstringValue; + RTS_CWCHAR *cwstringValue; +}RTS_VALUE; + +typedef struct +{ + RTS_TYPECLASS typeClass; + RTS_VALUE value; +}RTS_TYPEDVALUE; + + +/* + * IEC basic typedefinitions. + * Defines all datatypes used in IEC programs. Datatypes must match to CoDeSys compiler. + * Systems that overloads standard types must redefine this types in sysdefines.h and set IECBASETYPES_DEFINED + */ + +typedef void HUGEPTR(CDECL *RTS_VOID_FCTPTR)(void); + +#ifndef IECBASETYPES_DEFINED +#define IECBASETYPES_DEFINED + +# define RTS_IEC_VOID_FCTPTR RTS_VOID_FCTPTR + typedef void* RTS_IEC_VOIDPTR; + + typedef RTS_I8 RTS_IEC_BOOL; + typedef RTS_I8 RTS_IEC_SINT; + typedef RTS_UI8 RTS_IEC_USINT; + typedef RTS_UI8 RTS_IEC_BYTE; + typedef RTS_I16 RTS_IEC_INT; + typedef RTS_UI16 RTS_IEC_UINT; + typedef RTS_UI16 RTS_IEC_WORD; + typedef RTS_I32 RTS_IEC_DINT; + typedef RTS_UI32 RTS_IEC_UDINT; + typedef RTS_UI32 RTS_IEC_DWORD; + + typedef RTS_UI32 RTS_IEC_TIME; + typedef RTS_UI32 RTS_IEC_TIME_OF_DAY; + typedef RTS_UI32 RTS_IEC_TOD; + typedef RTS_UI32 RTS_IEC_DATE_AND_TIME; + typedef RTS_UI32 RTS_IEC_DT; + typedef RTS_UI32 RTS_IEC_DATE; + + typedef RTS_I64 RTS_IEC_LINT; + typedef RTS_UI64 RTS_IEC_ULINT; + typedef RTS_UI64 RTS_IEC_LWORD; + typedef RTS_UI64 RTS_IEC_LTIME; + + typedef RTS_SIZE RTS_IEC_XWORD; + typedef RTS_UINTPTR RTS_IEC_UXINT; + typedef RTS_PTRDIFF RTS_IEC_XINT; + + typedef RTS_REAL32 RTS_IEC_REAL; + typedef RTS_REAL64 RTS_IEC_LREAL; + + typedef RTS_CSTRING RTS_IEC_STRING; +# ifdef RTS_UNICODE + typedef RTS_WCHAR RTS_IEC_WSTRING; +# else + typedef RTS_UI16 RTS_IEC_WSTRING; +# endif + typedef RTS_CWCHAR RTS_IEC_CWCHAR; + + typedef RTS_SIZE RTS_IEC_SIZE; + typedef RTS_HANDLE RTS_IEC_HANDLE; + typedef RTS_IEC_UDINT RTS_IEC_RESULT; +#endif /*IECBASETYPES_DEFINED*/ + + +#ifndef RTS_MEM_REGION_DEFINED +#define RTS_MEM_REGION_DEFINED + +/** + * + * Abstract Memory Region. + * + */ +typedef struct tagRTS_MEM_REGION +{ + /* base address of the region */ + RTS_UINTPTR base; + + /* region size */ + RTS_SIZE size; + +} RTS_MEM_REGION; +#endif + +#ifndef EXT_FUNCT_REF_DEFINED +#define EXT_FUNCT_REF_DEFINED +/** + * Exported function descriptor + */ +typedef struct tagCMP_EXT_FUNCTION_REF +{ + /* pointer to an exported function */ + RTS_VOID_FCTPTR pfExtCall; + + /* name of an exported function */ + const char* pszExtCallName; + + /* signature */ + RTS_UI32 signature; + + /* version */ + RTS_UI32 version; + +} CMP_EXT_FUNCTION_REF; +#endif + +/* The CMGetAPI2/CMGetAPI3 importOptions parameter flag which specifies an import */ +/* as a required function (error if it is unresolved) */ +#define CM_IMPORT_REQUIRED_FUNCTION 0 + +/* The CMGetAPI2/CMGetAPI3 importOptions parameter flag which specifies an import */ +/* as an external libray function */ +#define CM_IMPORT_EXTERNAL_LIB_FUNCTION 1 + +/* The CMGetAPI2/CMGetAPI3 importOptions parameter flag which specifies an import */ +/* as an optional function */ +#define CM_IMPORT_OPTIONAL_FUNCTION 2 + +/* Imported function belongs to an external libray */ +#define ImportIsExternalLibFunction(par) (0 != ((par) & CM_IMPORT_EXTERNAL_LIB_FUNCTION)) + +/* Imported function is optional */ +#define ImportIsOptionalFunction(par) (0 != ((par) & CM_IMPORT_OPTIONAL_FUNCTION)) + +#define RTS_SIZEOF sizeof +#define RTS_GET_WORD_OFFSET(typesize) (typesize) +#define RTS_GET_WORD_SIZE RTS_GET_WORD_OFFSET +#define RTS_GET_BYTE_OFFSET(offset) (offset) +#define RTS_GET_BYTE_SIZE(size) (size) +#define RTS_MEMSET memset +#define RTS_ALIGN_4BYTES 4 +#define RTS_BITS_IN_BYTE 8 + +#define RTS_EMPTY_STRING "" +#define RTS_IS_EMPTY_STRING(s) ((char *)s == NULL || strcmp((char *)s, RTS_EMPTY_STRING) == 0) + +#define RTS_TIMEOUT_INFINITE (~(RTS_UI32)0) +#define RTS_TIMEOUT_DEFAULT (RTS_UI32)(-2) +#define RTS_TIMEOUT_NO_WAIT (0) + +#define RTS_INVALID_FILETRANSFER (RTS_UI32)(-1) + +#include "CmpItf.h" + + +#ifndef COMPONENT_NAME_POSTFIX + #define COMPONENT_NAME_POSTFIX +#endif + +#endif /*__RTSSTD_H__*/ diff --git a/sdk/ExtensionSDK/m4/Exports.m4 b/sdk/ExtensionSDK/m4/Exports.m4 new file mode 100644 index 0000000..3747d00 --- /dev/null +++ b/sdk/ExtensionSDK/m4/Exports.m4 @@ -0,0 +1,1514 @@ +include(`Util.m4') dnl +divert(-1) +% ----------------------------------- +% Copyright: Copyright CODESYS Development GmbH +% ----------------------------------- +changecom(`%') +% ----------------------------- +% Define some diverts that gather information about some special features +% and initialize those diversions +% ----------------------------- +define(DIVERT_INTERFACEDEF_C,3) +define(DIVERT_ENDINTERFACEDEF_C,4) +define(DIVERT_INTERFACEDEF,5) +define(DIVERT_ENDINTERFACEDEF,6) +define(DIVERT_ENTRYFUN,7) +define(DIVERT_ENDFILE,100) + + +% ----------------------------- +% The SET_PLACEHOLDER_NAME macro is to be ignored. It +% is used by the delivery manager only. +% ----------------------------- +define(`SET_PLACEHOLDER_NAME',`dnl') +') + +define(`GEN_INCLUDE', dnl +`ifelse(`-1',regexp(`$1',`\.m4$'), dnl + `error(`m4:'__file__`('__line__`):The first parameter "'$1`" is not a .m4 file + ')',dnl +`#include "patsubst(`$1',`\.m4$',`\.h')"')') + +define(`REF_ITF', `GEN_INCLUDE($1)') + +% Mark safety relevant component, from which the IBase is used +% in a safety critical manner. +define(`INTERFACEDEF', +`divert(DIVERT_INTERFACEDEF) +#ifdef CPLUSPLUS +class `I'$1 : public IBase +{ + public: +divert +divert(DIVERT_INTERFACEDEF_C) + +ifelse($1,`CmpIoDrv',`/* */')dnl +ifelse($1,`CmpIoDrvC',`/* */')dnl +ifelse($1,`CmpIoDrvIec',`/* */') +typedef struct +{ + IBase_C *pBase; +divert') + +define(`ENDINTERFACEDEF', +`divert(DIVERT_ENDINTERFACEDEF)dnl +}; + #ifndef `ITF_'$1 + #define `ITF_'$1 static `I'$1 *`pI'$1 = NULL; + #endif + #define `EXTITF_'$1 +#else /*CPLUSPLUS*/ + typedef `I'$1_C `I'$1; + #ifndef `ITF_'$1 + #define `ITF_'$1 + #endif + #define `EXTITF_'$1 +#endif +divert +divert(DIVERT_ENDINTERFACEDEF_C)dnl +} `I'$1_C; +divert') + +define(`SET_INTERFACE_NAME', + `define(CMP_NAME, $1)' + `define(ITFNOTDEFINED,_UPPERCASE(_TRIM($1))_NOTIMPLEMENTED)' + `define(ITFEXTERNAL,_UPPERCASE(_TRIM($1))_EXTERNAL) +`#ifndef _'_UPPERCASE(_TRIM($1))`ITF_H_' +`#define _'_UPPERCASE(_TRIM($1))`ITF_H_' +` +#include "CmpStd.h"' +define(`_INTERFACE_NAME',`I$1') +INTERFACEDEF($1) +ENDINTERFACEDEF($1) +divert(DIVERT_ENDFILE) +`#ifdef CPLUSPLUS_ONLY' +` #undef CPLUSPLUS_ONLY' +`#endif' + +`#endif /*_'_UPPERCASE(_TRIM($1))`ITF_H_*/' +divert +') + + +% +% Call the DEF_API Makro with the following parameters: +% DEF_API(, , , ) +% +% Example: +% DEF_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`REGISTER_API2', `s_pfCMRegisterAPI2( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), ifelse($6,1,_CHKSUM($1, $2, $4),$6), ifelse(eval($# < 7),1,0,$7))' )'dnl +`define(`GET_API', `s_pfCMGetAPI( "$3", (RTS_VOID_FCTPTR *)&pf$3, _CHKSUM($1, $2, $4))' )'dnl +`define(`GET_API2', `s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, ifelse(EXTERNAL,1,1,0), ifelse($6,0,_CHKSUM($1, $2, $4),$6), ifelse(eval($# < 7),1,0,$7))' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6),`1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($5, `1',`typedef $1 ($2 CDECL_EXT* _FUN_PTR_IEC($3)) $4;',`typedef $1 ($2 * _FUN_PTR($3)) $4;'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) ifelse(_TRIM($1),`void',`',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,ifelse(eval($# < 7),`1',REGISTER_API,REGISTER_API2),CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(eval($# < 7),`1',REGISTER_API,REGISTER_API2)' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3 $3' + `#define CHK_`'CMP_NAME`'$3 TRUE' + `#define EXP_`'CMP_NAME`'$3 ifelse(EXTERNAL,1,ifelse(eval($# < 7),`1',REGISTER_API,REGISTER_API2),ERR_OK)' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,ifelse(eval($# < 7),`1',REGISTER_API,REGISTER_API2),CAL_EXPAPI)' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6),`1',`_FUN_PTR($3)',`_FUN_PTR_IEC($3)') pf$3;' + `#define EXT_'$3 `extern ifelse(eval($# < 6),`1',`_FUN_PTR($3)',`_FUN_PTR_IEC($3)') pf$3'; + `#define GET_$3(fl) ifelse(eval($# < 7),1, `s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), ifelse(eval($# < 6),1,0,`ifelse($6,0,_CHKSUM($1, $2, $4),$6)'), ifelse(eval($# < 7),1,0,$7))',`s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl) | CM_IMPORT_EXTERNAL_LIB_FUNCTION, ifelse($6,0,_CHKSUM($1, $2, $4),$6), ifelse(eval($# < 7),1,0,$7))')' + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 ifelse(eval($# < 7),`1',REGISTER_API, REGISTER_API2)' +`#endif' +) + +% +% Call the DEF_STATIC_API Makro with the following parameters: +% DEF_STATIC_API(, , , ) +% +% Example: +% DEF_STATIC_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_STATIC_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6),`1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3 $3' + `#define CHK_`'CMP_NAME`'$3 TRUE' + `#define EXP_`'CMP_NAME`'$3 ifelse(EXTERNAL,1,REGISTER_API,ERR_OK)' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `_FUN_PTR($3) pf$3;' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4),0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +) + +% +% Call the DEF_CREATEITF_API Makro with the following parameters: +% DEF_CREATEITF_API(, , , ) +% +% Example: +% DEF_CREATEITF_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_CREATEITF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`''$3 ``((I'CMP_NAME`*)s_pfCMCreateInstance(CLASSID_C'CMP_NAME'`, NULL))->I'$3 + `#define CHK_`'CMP_NAME`'$3 (s_pfCMCreateInstance != NULL && pI`'CMP_NAME != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_'$3 ``((I'CMP_NAME`*)s_pfCMCreateInstance(CLASSID_C'CMP_NAME'`, NULL))->I'$3 + `#define CHK_$3 (s_pfCMCreateInstance != NULL && pI`'CMP_NAME != NULL)' + `#define EXP_$3 CAL_EXPAPI' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6), `1',`_FUN_PTR($3) pf$3;', ifelse($6, `1', `_FUN_PTR($3) pf$3;'))' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4), 0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_CREATEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_DELETEITF_API Makro with the following parameters: +% DEF_DELETEITF_API(, , , ) +% +% Example: +% DEF_DELETEITF_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_DELETEITF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) ifelse(_TRIM($1),`void',`',``(((RTS_HANDLE)p0 == NULL || (RTS_HANDLE)p0 == RTS_INVALID_HANDLE) ? ERR_PARAMETER : ((I'CMP_NAME`*)p0)'->I'$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4'))))' + `#define CHK_`'CMP_NAME`'$3 TRUE' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) ifelse(_TRIM($1),`void',`',``(((RTS_HANDLE)p0 == NULL || (RTS_HANDLE)p0 == RTS_INVALID_HANDLE) ? ERR_PARAMETER : ((I'CMP_NAME`*)p0)'->I'$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4'))))' + `#define CHK_$3 TRUE' + `#define EXP_$3 CAL_EXPAPI' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6), `1',`_FUN_PTR($3) pf$3;', ifelse($6, `1', `_FUN_PTR($3) pf$3;'))' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4), 0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_HANDLEITF_API Makro with the following parameters: +% DEF_HANDLEITF_API(, , , ) +% +% Example: +% DEF_HANDLEITF_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_HANDLEITF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? pI`'CMP_NAME->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')) : ((I`'CMP_NAME*)p0)->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_`'CMP_NAME`'$3 (pI`'CMP_NAME != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? pI`'CMP_NAME->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')) : ((I`'CMP_NAME*)p0)->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 (pI`'CMP_NAME != NULL)' + `#define EXP_$3 CAL_EXPAPI' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6), `1',`_FUN_PTR($3) pf$3;', ifelse($6, `1', `_FUN_PTR($3) pf$3;'))' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4), 0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_HANDLEITF_API2 Makro with the following parameters: +% DEF_HANDLEITF_API2(, , , ) +% +% Example: +% DEF_HANDLEITF_API2(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_HANDLEITF_API2', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? pI`'CMP_NAME->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')) : ((I`'CMP_NAME*)(((IBase*)p0)->QueryInterface((IBase *)p0, ITFID_I`'CMP_NAME, (RTS_RESULT *)1)))->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_`'CMP_NAME`'$3 (pI`'CMP_NAME != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? pI`'CMP_NAME->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')) : ((I`'CMP_NAME*)(((IBase*)p0)->QueryInterface((IBase *)p0, ITFID_I`'CMP_NAME, (RTS_RESULT *)1)))->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 (pI`'CMP_NAME != NULL)' + `#define EXP_$3 CAL_EXPAPI' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6), `1',`_FUN_PTR($3) pf$3;', ifelse($6, `1', `_FUN_PTR($3) pf$3;'))' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4), 0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_HANDLEITF_API_OWNCPP Makro with the following parameters: +% DEF_HANDLEITF_API_OWNCPP(, , , ) +% +% NOTE: +% Identical to DEF_HANDLEITF_API except, that in the C++ wrapper you can use your own implementation. +% +% Example: +% DEF_HANDLEITF_API_OWNCPP(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_HANDLEITF_API_OWNCPP', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? pI`'CMP_NAME->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')) : ((I`'CMP_NAME*)p0)->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_`'CMP_NAME`'$3 (pI`'CMP_NAME != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? pI`'CMP_NAME->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')) : ((I`'CMP_NAME*)p0)->I'`$3(_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 (pI`'CMP_NAME != NULL)' + `#define EXP_$3 CAL_EXPAPI' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6), `1',`_FUN_PTR($3) pf$3;', ifelse($6, `1', `_FUN_PTR($3) pf$3;'))' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4), 0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + + +% +% Call the DEF_CREATECLASSITF_API Makro with the following parameters: +% DEF_CREATECLASSITF_API(, , , ) +% +% Example: +% DEF_CREATECLASSITF_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_CREATECLASSITF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`STATICITF_DEF $1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `p0 = (IBase *)s_pfCMCreateInstance(p1, NULL);\ + ((IBase *)p0)->hInstance = ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, NULL)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `p0 = (IBase *)s_pfCMCreateInstance(p1, NULL);\ + ((IBase *)p0)->hInstance = ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, NULL)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) `p0 = (IBase *)s_pfCMCreateInstance(p1, NULL);\ + ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, (RTS_RESULT *)1)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))' + `#define CHK_`'CMP_NAME`'$3 (s_pfCMCreateInstance != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `p0 = (IBase *)s_pfCMCreateInstance(p1, NULL);\ + ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, (RTS_RESULT *)1)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))' + `#define CHK_$3 (s_pfCMCreateInstance != NULL)' + `#define EXP_$3 ERR_OK' +`#else /* DYNAMIC_LINK */' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `p0 = (IBase *)s_pfCMCreateInstance(p1, NULL);\ + ((IBase *)p0)->hInstance = ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, NULL)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_CREATEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_CREATECLASSITF2_API Makro with the following parameters: +% DEF_CREATECLASSITF2_API(, , , ) +% +% Example: +% DEF_CREATECLASSITF2_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_CREATECLASSITF2_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`STATICITF_DEF $1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + (((IBase *)p0)->ClassId = p1) == CLASSID_INVALID ? RTS_INVALID_HANDLE :\ + (\ + (((IBase *)p0)->hInstance = ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, NULL)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))) == RTS_INVALID_HANDLE ? (s_pfCMDeleteInstance2(p1, (IBase *)p0) ? RTS_INVALID_HANDLE : RTS_INVALID_HANDLE) : \ + (\ + ((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_`'_INTERFACE_NAME`, NULL)'\ + )\ + )\ + )\ + )' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + (((IBase *)p0)->ClassId = p1) == CLASSID_INVALID ? RTS_INVALID_HANDLE :\ + (\ + (((IBase *)p0)->hInstance = ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, NULL)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))) == RTS_INVALID_HANDLE ? (s_pfCMDeleteInstance2(p1, (IBase *)p0) ? RTS_INVALID_HANDLE : RTS_INVALID_HANDLE) : \ + (\ + ((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_`'_INTERFACE_NAME`, NULL)'\ + )\ + )\ + )\ + )' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, (RTS_RESULT *)1)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))\ + )\ + )' + `#define CHK_`'CMP_NAME`'$3 (s_pfCMCreateInstance != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4'))`\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, (RTS_RESULT *)1)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))\ + )\ + )' + `#define CHK_$3 (s_pfCMCreateInstance != NULL)' + `#define EXP_$3 ERR_OK' +`#else /* DYNAMIC_LINK */' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + (((IBase *)p0)->ClassId = p1) == CLASSID_INVALID ? RTS_INVALID_HANDLE :\ + (\ + (((IBase *)p0)->hInstance = ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, NULL)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))) == RTS_INVALID_HANDLE ? (s_pfCMDeleteInstance2(p1, (IBase *)p0) ? RTS_INVALID_HANDLE : RTS_INVALID_HANDLE) : \ + (\ + ((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_`'_INTERFACE_NAME`, NULL)'\ + )\ + )\ + )\ + )' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_CREATEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +define(`NUMPARAMS_xxx',`$#') +define(`NUMPARAMS',`NUMPARAMS_xxx(_EXTRACTPARAMS($1))') +define(`MINPARAMS',`eval(NUMPARAMS(`$1') >= $2)') + +% +% Call the DEF_PRODUCECLASSITF_API Makro with the following parameters: +% DEF_PRODUCECLASSITF_API(, , , ) +% +% Example: +% DEF_PRODUCECLASSITF_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_PRODUCECLASSITF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`STATICITF_DEF $1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + (((IBase *)p0)->ClassId = p1) == 0 ? RTS_INVALID_HANDLE :\ + (\ + (((IBase *)p0)->hInstance = ((I'CMP_NAME`*)p2)'->I$3(p2, p1, ((I`'CMP_NAME*)p2)->pBase->hInstance, _MAKE_HANDLE_DUMMYPARAMLIST2(`$4', 3))) == RTS_INVALID_HANDLE ? (s_pfCMDeleteInstance2(p1, (IBase *)p0) ? RTS_INVALID_HANDLE : RTS_INVALID_HANDLE) : \ + (\ + ((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_`'_INTERFACE_NAME`, NULL)'\ + )\ + )\ + )\ + )' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + (((IBase *)p0)->ClassId = p1) == 0 ? RTS_INVALID_HANDLE :\ + (\ + (((IBase *)p0)->hInstance = ((I'CMP_NAME`*)p2)'->I$3(p2, p1, ((I`'CMP_NAME*)p2)->pBase->hInstance, _MAKE_HANDLE_DUMMYPARAMLIST2(`$4', 3))) == RTS_INVALID_HANDLE ? (s_pfCMDeleteInstance2(p1, (IBase *)p0) ? RTS_INVALID_HANDLE : RTS_INVALID_HANDLE) : \ + (\ + ((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_`'_INTERFACE_NAME`, NULL)'\ + )\ + )\ + )\ + )' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, (RTS_RESULT *)1)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))\ + )\ + )' + `#define CHK_`'CMP_NAME`'$3 (s_pfCMCreateInstance != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, (RTS_RESULT *)1)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))\ + )\ + )' + `#define CHK_$3 (s_pfCMCreateInstance != NULL)' + `#define EXP_$3 ERR_OK' +`#else /* DYNAMIC_LINK */' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + (((IBase *)p0)->ClassId = p1) == 0 ? RTS_INVALID_HANDLE :\ + (\ + (((IBase *)p0)->hInstance = ((I'CMP_NAME`*)p2)'->I$3(p2, p1, ((I`'CMP_NAME*)p2)->pBase->hInstance, _MAKE_HANDLE_DUMMYPARAMLIST2(`$4', 3))) == RTS_INVALID_HANDLE ? (s_pfCMDeleteInstance2(p1, (IBase *)p0) ? RTS_INVALID_HANDLE : RTS_INVALID_HANDLE) : \ + (\ + ((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_`'_INTERFACE_NAME`, NULL)'\ + )\ + )\ + )\ + )' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_CREATEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_PRODUCECLASSITF_API_OWNCPP Makro with the following parameters: +% DEF_PRODUCECLASSITF_API_OWNCPP(, , , ) +% +% Example: +% DEF_PRODUCECLASSITF_API_OWNCPP(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_PRODUCECLASSITF_API_OWNCPP', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`STATICITF_DEF $1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + (((IBase *)p0)->ClassId = p1) == 0 ? RTS_INVALID_HANDLE :\ + (\ + (((IBase *)p0)->hInstance = ((I'CMP_NAME`*)p2)'->I$3(p2, p1, ((I`'CMP_NAME*)p2)->pBase->hInstance, _MAKE_HANDLE_DUMMYPARAMLIST2(`$4', 3))) == RTS_INVALID_HANDLE ? (s_pfCMDeleteInstance2(p1, (IBase *)p0) ? RTS_INVALID_HANDLE : RTS_INVALID_HANDLE) : \ + (\ + ((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_`'_INTERFACE_NAME`, NULL)'\ + )\ + )\ + )\ + )' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + (((IBase *)p0)->ClassId = p1) == 0 ? RTS_INVALID_HANDLE :\ + (\ + (((IBase *)p0)->hInstance = ((I'CMP_NAME`*)p2)'->I$3(p2, p1, ((I`'CMP_NAME*)p2)->pBase->hInstance, _MAKE_HANDLE_DUMMYPARAMLIST2(`$4', 3))) == RTS_INVALID_HANDLE ? (s_pfCMDeleteInstance2(p1, (IBase *)p0) ? RTS_INVALID_HANDLE : RTS_INVALID_HANDLE) : \ + (\ + ((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_`'_INTERFACE_NAME`, NULL)'\ + )\ + )\ + )\ + )' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, (RTS_RESULT *)1)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))\ + )\ + )' + `#define CHK_`'CMP_NAME`'$3 (s_pfCMCreateInstance != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + ((I'CMP_NAME`*)((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_'_INTERFACE_NAME`, (RTS_RESULT *)1)')->I$3(_MAKE_DUMMYPARAMLIST(`$4'))\ + )\ + )' + `#define CHK_$3 (s_pfCMCreateInstance != NULL)' + `#define EXP_$3 ERR_OK' +`#else /* DYNAMIC_LINK */' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) `\ + (\ + (p0 = (IBase *)s_pfCMCreateInstance(p1, NULL)) == NULL ? RTS_INVALID_HANDLE : \ + (\ + (((IBase *)p0)->ClassId = p1) == 0 ? RTS_INVALID_HANDLE :\ + (\ + (((IBase *)p0)->hInstance = ((I'CMP_NAME`*)p2)'->I$3(p2, p1, ((I`'CMP_NAME*)p2)->pBase->hInstance, _MAKE_HANDLE_DUMMYPARAMLIST2(`$4', 3))) == RTS_INVALID_HANDLE ? (s_pfCMDeleteInstance2(p1, (IBase *)p0) ? RTS_INVALID_HANDLE : RTS_INVALID_HANDLE) : \ + (\ + ((IBase *)p0)->QueryInterface((IBase *)p0, ITFID_`'_INTERFACE_NAME`, NULL)'\ + )\ + )\ + )\ + )' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_CREATEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_DELETECLASSITF_API Makro with the following parameters: +% DEF_DELETECLASSITF_API(, , , ) +% +% Example: +% DEF_DELETECLASSITF_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_DELETECLASSITF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`STATICITF_DEF $1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_`'CMP_NAME`'$3 TRUE' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#else /* DYNAMIC_LINK */' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_DELETECLASSITF2_API Makro with the following parameters: +% DEF_DELETECLASSITF2_API(, , , ) +% +% Example: +% DEF_DELETECLASSITF2_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_DELETECLASSITF2_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`STATICITF_DEF $1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')) != ERR_OK ? \ + ERR_FAILED :\ + s_pfCMDeleteInstance2(((I`'CMP_NAME*)p0)->pBase->ClassId, ((I`'CMP_NAME*)p0)->pBase))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')) != ERR_OK ? \ + ERR_FAILED :\ + s_pfCMDeleteInstance2(((I`'CMP_NAME*)p0)->pBase->ClassId, ((I`'CMP_NAME*)p0)->pBase))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_`'CMP_NAME`'$3 TRUE' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#else /* DYNAMIC_LINK */' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')) != ERR_OK ? \ + ERR_FAILED :\ + s_pfCMDeleteInstance2(((I`'CMP_NAME*)p0)->pBase->ClassId, ((I`'CMP_NAME*)p0)->pBase))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_CLASSITF_API Makro with the following parameters: +% DEF_CLASSITF_API(, , , ) +% +% Example: +% DEF_CLASSITF_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_CLASSITF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`STATICITF_DEF $1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_`'CMP_NAME`'$3 TRUE' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#else /* DYNAMIC_LINK */' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_RETURNHANDLECLASSITF_API Makro with the following parameters: +% DEF_RETURNHANDLECLASSITF_API(, , , ) +% +% Example: +% DEF_RETURNHANDLECLASSITF_API(`RTS_HANDLE',`CDECL', `MyApiFunction', `(RTS_HANDLE hObject, int nX, void *pData)') +% ==> ((IMyInterface*)p0)->IMyInterfaceFunction(((IMyInterface*)p0)->pBase->hInstance,p1,p2,p3)) +% +define(`DEF_RETURNHANDLECLASSITF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`STATICITF_DEF $1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : ((('I`'CMP_NAME*)p0)->pBase->hInstance = ((I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4'))))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : ((('I`'CMP_NAME*)p0)->pBase->hInstance = ((I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4'))))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`'$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_`'CMP_NAME`'$3 TRUE' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : (('I`'CMP_NAME*)p0)->I$3(`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4')))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#else /* DYNAMIC_LINK */' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_OK' + `#define CAL_$3(_MAKE_DUMMYPARAMLIST(`$4')) (p0 == RTS_INVALID_HANDLE || p0 == NULL ? `'ifelse(_TRIM($1),`RTS_RESULT',`($1)ERR_PARAMETER',ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`0'))` : ((('I`'CMP_NAME*)p0)->pBase->hInstance = ((I`'CMP_NAME*)p0)->I$3(((I`'CMP_NAME*)p0)->pBase->hInstance`'ifelse(MINPARAMS(`$4',2),`1',`,',`')`'_MAKE_HANDLE_DUMMYPARAMLIST(`$4'))))' + `#define CHK_$3 TRUE' + `#define EXP_$3 ERR_OK' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_STATICITF_API Makro with the following parameters: +% DEF_STATICITF_API(, , , ) +% +% Example: +% DEF_STATICITF_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_STATICITF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`''$3 ``C'CMP_NAME::I'$3 + `#define CHK_`'CMP_NAME`'$3 TRUE' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_'$3 ``C'CMP_NAME::I'$3 + `#define CHK_$3 TRUE' + `#define EXP_$3 CAL_EXPAPI' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6), `1',`_FUN_PTR($3) pf$3;', ifelse($6, `1', `_FUN_PTR($3) pf$3;'))' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4), 0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +`divert(DIVERT_INTERFACEDEF) static $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_ITF_API Makro with the following parameters: +% DEF_ITF_API(, , , ) +% +% Example: +% DEF_ITF_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_ITF_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',ifelse(_TRIM($1),`void *',`($1)(RTS_SIZE)ERR_NOTIMPLEMENTED',`($1)ERR_NOTIMPLEMENTED')))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`''$3 ``pI'CMP_NAME->I'$3 + `#define CHK_`'CMP_NAME`''$3 ``(pI'CMP_NAME != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_'$3 ``pI'CMP_NAME->I'$3 + `#define CHK_'$3 ``(pI'CMP_NAME != NULL)' + `#define EXP_$3 CAL_EXPAPI' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6), `1',`_FUN_PTR($3) pf$3;', ifelse($6, `1', `_FUN_PTR($3) pf$3;'))' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4), 0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_ITF_API Makro with the following parameters: +% DEF_ITF_API(, , , ) +% +% Example: +% DEF_ITF_API_OWNCPP(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_ITF_API_OWNCPP', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',ifelse(_TRIM($1),`void *',`($1)(RTS_SIZE)ERR_NOTIMPLEMENTED',`($1)ERR_NOTIMPLEMENTED')))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`''$3 ``pI'CMP_NAME->I'$3 + `#define CHK_`'CMP_NAME`''$3 ``(pI'CMP_NAME != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_'$3 ``pI'CMP_NAME->I'$3 + `#define CHK_'$3 ``(pI'CMP_NAME != NULL)' + `#define EXP_$3 CAL_EXPAPI' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6), `1',`_FUN_PTR($3) pf$3;', ifelse($6, `1', `_FUN_PTR($3) pf$3;'))' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4), 0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_ITF_API Makro with the following parameters: +% DEF_ITF_API(, , , ) +% +% Example: +% DEF_ITF_API2(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_ITF_API2', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`$1 $2 CDECL_EXT $3$4;',`$1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',ifelse(_TRIM($1),`void *',`($1)(RTS_SIZE)ERR_NOTIMPLEMENTED',`($1)ERR_NOTIMPLEMENTED')))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`''$3 ``pI'CMP_NAME->I'$3 + `#define CHK_`'CMP_NAME`''$3 ``(pI'CMP_NAME != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_'$3 ``pI'CMP_NAME->I'$3 + `#define CHK_'$3 ``(pI'CMP_NAME != NULL)' + `#define EXP_$3 CAL_EXPAPI' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6), `1',`_FUN_PTR($3) pf$3;', ifelse($6, `1', `_FUN_PTR($3) pf$3;'))' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4), 0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) + +% +% Call the DEF_ITF_GLOBAL_API Makro with the following parameters: +% DEF_ITF_GLOBAL_API(, , , ) +% +% Example: +% DEF_ITF_GLOBAL_API(`int',`CDECL', `MyApiFunction', `(int nX, void *pData)') +% +define(`DEF_ITF_GLOBAL_API', +`ifelse(eval($# < 5),`0',`define(`EXTERNAL',_TRIM(`$5'))',`define(`EXTERNAL',0)')'dnl +`define(`REGISTER_API', `s_pfCMRegisterAPI( (const CMP_EXT_FUNCTION_REF*)"$3", (RTS_UINTPTR)$3, ifelse(EXTERNAL,1,1,0), _CHKSUM($1, $2, $4))' )'dnl +`define(`FCT_NOTIMPLEMENTED', `ifelse(_EXTRACTFIRSTPARAMTYPE(`$4'), `RTS_HANDLE', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPEPOINTER(`$4'), `*', `FUNCTION_NOTIMPLEMENTED2',`ifelse(_EXTRACTFIRSTPARAMTYPERTS(`$4'), `RTS_', `FUNCTION_NOTIMPLEMENTED',`FUNCTION_NOTIMPLEMENTED2')')')' )'dnl +`define(`CAL_GETAPI', `CAL_CMGETAPI( "$3" )' )'dnl +`define(`CAL_EXPAPI', `CAL_CMEXPAPI( "$3" )' )'dnl +`ifelse(_TRIM(`$5'),1,`DLL_DECL $1 $2 CDECL_EXT $3$4;',`DLL_DECL $1 $2 $3$4;')' +`ifelse(eval($# < 6), `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;', ifelse($6, `1',`typedef $1 ($2 * _FUN_PTR($3)) $4;',`'))' +`#if ifdef(`ITFNOTDEFINED',`defined(ITFNOTDEFINED) || ')defined(_UPPERCASE(_TRIM(`$3'))_NOTIMPLEMENTED)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) ERR_NOTIMPLEMENTED' + `#define CAL_$3`'ifelse(_TRIMVARPARAMLIST($4),1,`',`(_MAKE_DUMMYPARAMLIST(`$4'))') ifelse(_TRIM($1),`void',`', ifelse(_TRIM($4), `void',`($1)ERR_NOTIMPLEMENTED',ifelse(_TRIMVARPARAMLIST($4),1,FCT_NOTIMPLEMENTED,ifelse(_TRIM($1),`RTS_HANDLE',`($1)RTS_INVALID_HANDLE',`($1)ERR_NOTIMPLEMENTED'))))' + `#define CHK_$3 FALSE' + `#define EXP_$3 ERR_OK' +`#elif defined(STATIC_LINK)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 ifelse(EXTERNAL,1,REGISTER_API,CAL_EXPAPI)' +`#elif defined(MIXED_LINK) && !defined(ITFEXTERNAL)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_$3 $3' + `#define CHK_$3 TRUE' + `#define EXP_$3 REGISTER_API' +`#elif defined(CPLUSPLUS_ONLY)' + `#define USE_`'CMP_NAME`'$3' + `#define EXT_`'CMP_NAME`'$3' + `#define GET_`'CMP_NAME`'$3 ERR_OK' + `#define CAL_`'CMP_NAME`''$3 ``pI'CMP_NAME->I'$3 + `#define CHK_`'CMP_NAME`''$3 ``(pI'CMP_NAME != NULL)' + `#define EXP_`'CMP_NAME`'$3 ERR_OK' +`#elif defined(CPLUSPLUS)' + `#define USE_$3' + `#define EXT_$3' + `#define GET_$3(fl) CAL_GETAPI' + `#define CAL_'$3 ``pI'CMP_NAME->I'$3 + `#define CHK_'$3 ``(pI'CMP_NAME != NULL)' + `#define EXP_$3 CAL_EXPAPI' +`#else /* DYNAMIC_LINK */' + `#define USE_'$3 `ifelse(eval($# < 6), `1',`_FUN_PTR($3) pf$3;', ifelse($6, `1', `_FUN_PTR($3) pf$3;'))' + `#define EXT_'$3 `extern _FUN_PTR($3) pf$3'; + `#define GET_'$3(fl) s_pfCMGetAPI2( "$3", (RTS_VOID_FCTPTR *)&pf$3, (fl), _CHKSUM($1, $2, $4), 0) + `#define CAL_'$3 pf$3 + `#define CHK_'$3 (pf$3 != NULL) + `#define EXP_$3 REGISTER_API' +`#endif' +`divert(DIVERT_INTERFACEDEF) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))) =0; +divert' +`divert(DIVERT_INTERFACEDEF_C) _FUN_PTR($3) `I'$3; + divert' +) +divert \ No newline at end of file diff --git a/sdk/ExtensionSDK/m4/Implementation.m4 b/sdk/ExtensionSDK/m4/Implementation.m4 new file mode 100644 index 0000000..7c3ea0e --- /dev/null +++ b/sdk/ExtensionSDK/m4/Implementation.m4 @@ -0,0 +1,897 @@ +divert(-1) +% ----------------------------------- +% Copyright: Copyright CODESYS Development GmbH +% ----------------------------------- +# The following lines ("DO NOT MODIFY...") generate a message in the destination file. +# This comment is not aimed at this file. +divert`'dnl +/*********************************************************************** + * DO NOT MODIFY! + * This is a generated file. Do not modify it's contents directly + ***********************************************************************/ +include(`Util.m4') +divert(-1) + +changecom(`%') +% ----------------------------- +% Define some diverts that gather information about some special features +% and initialize those diversions +% ----------------------------- +define(DIVERT_USE,3) +define(DIVERT_USEIMPORT,4) +define(DIVERT_USEEXTERN,5) +define(DIVERT_INIT,6) +define(DIVERT_RENAME_ENTRYFUN,9) +define(DIVERT_BEGINCLASSDEF,10) +define(DIVERT_CLASSDEF,11) +define(DIVERT_CLASSMEMBERS,12) +define(DIVERT_ENDCLASSDEF,13) +define(DIVERT_ENDMETHODDEF,15) +define(DIVERT_URTS_DEFS, 20) +define(DIVERT_ENDFILE,100) + +define(`REF_ITF', `GEN_INCLUDE($1)') + +define(`COMPONENT_VERSION',`#define CMP_VERSION UINT32_C(`$1') +#define CMP_VERSION_STRING "_VERSION_MAJOR(`$1')._VERSION_SUB0(`$1')._VERSION_SUB1(`$1')._VERSION_SUB2(`$1')" +#define CMP_VERSION_RC _VERSION_MAJOR(`$1'),_VERSION_SUB0(`$1'),_VERSION_SUB1(`$1'),_VERSION_SUB2(`$1')') +define(`COMPONENT_VENDORID',`#define CMP_VENDORID $1 + +#ifndef WIN32_RESOURCES') + +divert(DIVERT_USE)dnl +#define USE_STMT \ + /*lint -save --e{528} --e{551} */ \ + static volatile PF_REGISTER_API s_pfCMRegisterAPI; \ + static volatile PF_REGISTER_API2 s_pfCMRegisterAPI2; \ + static volatile PF_GET_API s_pfCMGetAPI; \ + static volatile PF_GET_API2 s_pfCMGetAPI2; \ + static volatile PF_REGISTER_CLASS s_pfCMRegisterClass; \ + static volatile PF_CREATEINSTANCE s_pfCMCreateInstance; \ + static volatile PF_CALL_HOOK s_pfCMCallHook; \ + static const CMP_EXT_FUNCTION_REF s_ExternalsTable[] =\ + {\ + EXPORT_EXTREF_STMT\ + EXPORT_EXTREF2_STMT\ + { ((RTS_VOID_FCTPTR)(void *)0), "", 0, 0 }\ + };\ + static const CMP_EXT_FUNCTION_REF s_ItfTable[] = EXPORT_CMPITF_STMT; \ + /*lint -restore */ \ + static int CDECL ExportFunctions(void); \ + static int CDECL ImportFunctions(void); \ + static IBase* CDECL CreateInstance(CLASSID cid, RTS_RESULT *pResult); \ + static RTS_RESULT CDECL DeleteInstance(IBase *pIBase); \ + static RTS_UI32 CDECL CmpGetVersion(void); \ + static RTS_RESULT CDECL HookFunction(RTS_UI32 ulHook, RTS_UINTPTR ulParam1, RTS_UINTPTR ulParam2); \ + dnl +divert(-1) + + +divert(DIVERT_USEIMPORT) +#define USEIMPORT_STMT \ + /*lint -save --e{551} */ \ + static volatile PF_REGISTER_API s_pfCMRegisterAPI; \ + static volatile PF_REGISTER_API2 s_pfCMRegisterAPI2; \ + static volatile PF_GET_API s_pfCMGetAPI; \ + static volatile PF_GET_API2 s_pfCMGetAPI2; \ + static volatile PF_REGISTER_CLASS s_pfCMRegisterClass; \ + static volatile PF_CREATEINSTANCE s_pfCMCreateInstance; \ + static volatile PF_CALL_HOOK s_pfCMCallHook; \ + /*lint -restore */ \ + dnl +divert(-1) + + + +divert(DIVERT_USEEXTERN) +#define USEEXTERN_STMT \ + dnl +divert(-1) + +divert(DIVERT_RENAME_ENTRYFUN) +#ifndef COMPONENT_NAME + #error COMPONENT_NAME is not defined. This prevents the component from being linked statically. Use `SET_COMPONENT_NAME'() to set the name of the component in your .m4 component description. +#endif +divert(-1) + +divert(DIVERT_BEGINCLASSDEF) +#ifdef CPLUSPLUS +divert(-1) + +divert(DIVERT_ENDMETHODDEF) +#endif /*CPLUSPLUS*/ +divert(-1) + +divert(DIVERT_ENDFILE) +`#endif /*WIN32_RESOURCES*/' +`#endif /*_DEP_H_*/' +divert(-1)') + + +% --------------------------- +% The SET_COMPONENT_NAME directive should reside at the top of each "Itf" file. +% --------------------------- + +define(`SET_COMPONENT_NAME', +`define(_COMPONENT_NAME,`$1')dnl +`#ifndef _'_UPPERCASE(_COMPONENT_NAME)`DEP_H_' +`#define _'_UPPERCASE(_COMPONENT_NAME)`DEP_H_' + +#define COMPONENT_NAME "$1" COMPONENT_NAME_POSTFIX +#define COMPONENT_ID ADDVENDORID(CMP_VENDORID, CMPID_$1) +#define COMPONENT_NAME_UNQUOTED $1 + +divert(DIVERT_RENAME_ENTRYFUN) + +divert(DIVERT_RENAME_ENTRYFUN) + +#if defined(STATIC_LINK) || defined(MIXED_LINK) || defined(DYNAMIC_LINK) || defined(CPLUSPLUS_STATIC_LINK) + #define ComponentEntry $1`'__Entry +#endif + +divert + +divert(DIVERT_URTS_DEFS)dnl + +divert +') + +% ----------------------------- +% The COMPONENT_SOURCES and COMPONENT_SYSSOURCES macros are to be ignored. They +% are used by the configuration tool only. +% ----------------------------- +define(`COMPONENT_SOURCES',`dnl') +define(`EXTERNAL_SOURCES',`dnl') +define(`EXTERNAL_INCLUDES',`dnl') +define(`COMPONENT_INCLUDES',`dnl') +define(`COMPONENT_SYSSOURCES',`dnl') +define(`CONTRIB_SOURCES',`dnl') +define(`CONTRIB_INCLUDES',`dnl') +') + +% ----------------------------- +% The CATEGORY macro is to be ignored. It +% is used by the configuration tool only. +% ----------------------------- +define(`CATEGORY',`dnl') +') + +% ----------------------------- +% After all input is read, some macros have to be created +% ----------------------------- +define(`ADD_IMPORTS',`ifdef(`__IMPORTS',`__IMPORTS popdef(`__IMPORTS') ADD_IMPORTS')') +define(`CREATE_IMPORT_STMT', dnl +`ifdef(`__IMPORTS',`#if defined(STATIC_LINK) + #define IMPORT_STMT +#else + #define IMPORT_STMT \ + {\ + RTS_RESULT importResult = ERR_OK;\ + RTS_RESULT TempResult = ERR_OK;\ + INIT_STMT \ + TempResult = GET_LogAdd(CM_IMPORT_OPTIONAL_FUNCTION); \ + TempResult = GET_CMUtlMemCpy(CM_IMPORT_OPTIONAL_FUNCTION); \ + ADD_IMPORTS \ + /* To make LINT happy */\ + TempResult = TempResult;\ + if (ERR_OK != importResult) return importResult;\ + } +#endif +',`#define IMPORT_STMT INIT_STMT') +') + +% ----------------------------- +% This function emits all export statements inside the macro EXPORT_STMT (one after another) +% ----------------------------- +define(`ADD_EXPORTS',`ifdef(`__EXPORTS',`__EXPORTS popdef(`__EXPORTS') ADD_EXPORTS')') +define(`ADD_EXTREF_EXPORTS',`ifdef(`__EXPORTS_EXTREF',`__EXPORTS_EXTREF popdef(`__EXPORTS_EXTREF') ADD_EXTREF_EXPORTS')') +define(`ADD_EXTREF_EXPORTS2',`ifdef(`__EXPORTS_EXTREF2',`__EXPORTS_EXTREF2 popdef(`__EXPORTS_EXTREF2') ADD_EXTREF_EXPORTS2')') +define(`ADD_EXPORTS_CPLUSPLUS_ONLY',`ifdef(`__EXPORTS_CPLUSPLUS_ONLY',`__EXPORTS_CPLUSPLUS_ONLY popdef(`__EXPORTS_CPLUSPLUS_ONLY') ADD_EXPORTS_CPLUSPLUS_ONLY')') + +% ----------------------------- +% Define the EXPORT_STMT macro. the content is filled out by ADD_EXPORTS. +% ----------------------------- +define(`CREATE_EXPORT_STMT',dnl +`ifdef(`__EXPORTS_EXTREF',dnl +`#ifndef _UPPERCASE(_COMPONENT_NAME)_DISABLE_EXTREF +#define EXPORT_EXTREF_STMT \ + ADD_EXTREF_EXPORTS +#else +#define EXPORT_EXTREF_STMT +#endif',`#define EXPORT_EXTREF_STMT') +'dnl +`ifdef(`__EXPORTS_EXTREF2',dnl +`#ifndef _UPPERCASE(_COMPONENT_NAME)_DISABLE_EXTREF2 +#define EXPORT_EXTREF2_STMT \ + ADD_EXTREF_EXPORTS2 +#else +#define EXPORT_EXTREF2_STMT +#endif',`#define EXPORT_EXTREF2_STMT') +'dnl +`ifdef(`__EXPORTS',dnl +`#if !defined(_UPPERCASE(_COMPONENT_NAME)_DISABLE_CMPITF) && !defined(STATIC_LINK) && !defined(CPLUSPLUS) && !defined(CPLUSPLUS_ONLY) +#define EXPORT_CMPITF_STMT \ + {\ + ADD_EXPORTS\ + { ((RTS_VOID_FCTPTR)(void *)0), "", 0, 0 }\ + } +#else +#define EXPORT_CMPITF_STMT \ + {\ + { ((RTS_VOID_FCTPTR)(void *)0), "", 0, 0 }\ + } +#endif',`#define EXPORT_CMPITF_STMT {{ ((RTS_VOID_FCTPTR)(void *)0), "", 0, 0 }}') +'dnl +`ifdef(`__EXPORTS_CPLUSPLUS_ONLY', ` +#define EXPORT_CPP_STMT \ + {\ + ADD_EXPORTS_CPLUSPLUS_ONLY \ + } +',`#define EXPORT_CPP_STMT') +'dnl +) + +% ----------------------------- +% This function emits all init statements inside the macro INIT_STMT (one after another) +% ----------------------------- +define(`ADD_INITS',`ifdef(`__INITS',`__INITS popdef(`__INITS') ADD_INITS')') +define(`ADD_INIT_LOCALS',`ifdef(`__INIT_LOCALS',`__INIT_LOCALS popdef(`__INIT_LOCALS') ADD_INIT_LOCALS')') +define(`ADD_EXITS',`ifdef(`__EXITS',`__EXITS popdef(`__EXITS') ADD_EXITS')') + +% ----------------------------- +% Define the INIT_STMT macro. +% ----------------------------- +define(`CREATE_INIT_STMT', dnl +`ifdef(`__INITS',`` +#ifdef CPLUSPLUS + #define INIT_STMT' \ + {\ + IBase *pIBase;\ + RTS_RESULT initResult;\ + if (pICmpLog == NULL && s_pfCMCreateInstance != NULL) \ + { \ + pIBase = (IBase *)s_pfCMCreateInstance(CLASSID_CCmpLog, &initResult); \ + if (pIBase != NULL) \ + { \ + pICmpLog = (ICmpLog *)pIBase->QueryInterface(pIBase, ITFID_ICmpLog, &initResult); \ + pIBase->Release(pIBase); \ + } \ + } \ + if (pICMUtils == NULL && s_pfCMCreateInstance != NULL) \ + { \ + pIBase = (IBase *)s_pfCMCreateInstance(CLASSID_CCMUtils, &initResult); \ + if (pIBase != NULL) \ + { \ + pICMUtils = (ICMUtils *)pIBase->QueryInterface(pIBase, ITFID_ICMUtils, &initResult); \ + pIBase->Release(pIBase); \ + } \ + } \ + ADD_INITS \ + } + #define INIT_LOCALS_STMT \ + {\ + pICmpLog = NULL; \ + pICMUtils = NULL; \ + ADD_INIT_LOCALS \ + } + #define EXIT_STMT \ + {\ + IBase *pIBase;\ + RTS_RESULT exitResult;\ + if (pICmpLog != NULL) \ + { \ + pIBase = (IBase *)pICmpLog->QueryInterface(pICmpLog, ITFID_IBase, &exitResult); \ + if (pIBase != NULL) \ + { \ + pIBase->Release(pIBase); \ + if (pIBase->Release(pIBase) == 0) /* The object will be deleted here! */ \ + pICmpLog = NULL; \ + } \ + } \ + if (pICMUtils != NULL) \ + { \ + pIBase = (IBase *)pICMUtils->QueryInterface(pICMUtils, ITFID_IBase, &exitResult); \ + if (pIBase != NULL) \ + { \ + pIBase->Release(pIBase); \ + if (pIBase->Release(pIBase) == 0) /* The object will be deleted here! */ \ + pICMUtils = NULL; \ + } \ + } \ + ADD_EXITS \ + } +#else + #define INIT_STMT + #define INIT_LOCALS_STMT + #define EXIT_STMT +#endif +',`#ifdef CPLUSPLUS + #define INIT_STMT \ + {\ + IBase *pIBase;\ + RTS_RESULT initResult;\ + if (pICmpLog == NULL && s_pfCMCreateInstance != NULL) \ + { \ + pIBase = (IBase *)s_pfCMCreateInstance(CLASSID_CCmpLog, &initResult); \ + if (pIBase != NULL) \ + { \ + pICmpLog = (ICmpLog *)pIBase->QueryInterface(pIBase, ITFID_ICmpLog, &initResult); \ + pIBase->Release(pIBase); \ + } \ + } \ + if (pICMUtils == NULL && s_pfCMCreateInstance != NULL) \ + { \ + pIBase = (IBase *)s_pfCMCreateInstance(CLASSID_CCMUtils, &initResult); \ + if (pIBase != NULL) \ + { \ + pICMUtils = (ICMUtils *)pIBase->QueryInterface(pIBase, ITFID_ICMUtils, &initResult); \ + pIBase->Release(pIBase); \ + } \ + } \ + } + #define INIT_LOCALS_STMT \ + {\ + pICmpLog = NULL; \ + pICMUtils = NULL; \ + ADD_INIT_LOCALS \ + } + #define EXIT_STMT \ + {\ + IBase *pIBase;\ + RTS_RESULT exitResult;\ + if (pICmpLog != NULL) \ + { \ + pIBase = (IBase *)pICmpLog->QueryInterface(pICmpLog, ITFID_IBase, &exitResult); \ + if (pIBase != NULL) \ + { \ + pIBase->Release(pIBase); \ + if (pIBase->Release(pIBase) == 0) /* The object will be deleted here! */ \ + pICmpLog = NULL; \ + } \ + } \ + if (pICMUtils != NULL) \ + { \ + pIBase = (IBase *)pICMUtils->QueryInterface(pICMUtils, ITFID_IBase, &exitResult); \ + if (pIBase != NULL) \ + { \ + pIBase->Release(pIBase); \ + if (pIBase->Release(pIBase) == 0) /* The object will be deleted here! */ \ + pICMUtils = NULL; \ + } \ + } \ + } +#else + #define INIT_STMT + #define INIT_LOCALS_STMT + #define EXIT_STMT +#endif') +') + + +% ----------------------------- +% The next lines causes the CREATE_IMPORT_STMT, CREATE_EXPORT_STMT +% and CREATE_INIT_STMT function to be called after all input has been read +% ----------------------------- +m4wrap(` +CREATE_EXPORT_STMT + +#if defined(STATIC_LINK) + #define EXPORT_STMT\ + {\ + RTS_RESULT ExpResult;\ + if (NULL == s_pfCMRegisterAPI)\ + return ERR_NOTINITIALIZED;\ + ExpResult = s_pfCMRegisterAPI(s_ExternalsTable, 0, 1, COMPONENT_ID);\ + if (ERR_OK != ExpResult)\ + return ExpResult;\ + } +#else + #define EXPORT_STMT\ + {\ + RTS_RESULT ExpResult;\ + if (NULL == s_pfCMRegisterAPI)\ + return ERR_NOTINITIALIZED;\ + ExpResult = s_pfCMRegisterAPI(s_ExternalsTable, 0, 1, COMPONENT_ID);\ + if (ERR_OK != ExpResult)\ + return ExpResult;\ + ExpResult = s_pfCMRegisterAPI(s_ItfTable, 0, 0, COMPONENT_ID);\ + if (ERR_OK != ExpResult)\ + return ExpResult;\ + } +#endif + +') +m4wrap(` +CREATE_IMPORT_STMT +') +m4wrap(` +CREATE_INIT_STMT +') + + +% ----------------------------- +% Generate an "#include " directive, +% replacing the extension ".m4" with ".h" +% ----------------------------- +define(`GET_INCLUDE_FILE', `ifelse(`-1',regexp(`$1',`\.m4$'),`error(`m4:'__file__`('__line__`):The first parameter "'$1`" is not a .m4 file')',dnl +`/** + * \file 'patsubst(`$1',`\.m4$',`\.h')` + */')') + +define(`GEN_INCLUDE', dnl +`ifelse(`-1',regexp(`$1',`\.m4$'), dnl + `error(`m4:'__file__`('__line__`):The first parameter "'$1`" is not a .m4 file + ')',dnl +$1,`CmpLogItf.m4', `/*Obsolete include: CmpLogItf.m4*/',dnl +$1,`CMUtilsItf.m4', `/*Obsolete include: CMUtilsItf.m4*/',dnl +`#include "patsubst(`$1',`\.m4$',`\.h')"')') + +define(`GEN_INIT_STMT', `pushdef(`__INITS',ifelse($1,`CMUtils',`/*Obsolete include CMUtils*/ \ + ', + $1,`CmpLog',`/*Obsolete include CmpLog*/ \ + ', +`if (pI$1 == NULL && s_pfCMCreateInstance != NULL) \ + { \ + pIBase = (IBase *)s_pfCMCreateInstance(CLASSID_C$1, &initResult); \ + if (pIBase != NULL) \ + { \ + pI$1 = (I$1 *)pIBase->QueryInterface(pIBase, ITFID_I$1, &initResult); \ + pIBase->Release(pIBase); \ + } \ + } \ + '))') + +define(`GEN_INIT_LOCALS_STMT', `pushdef(`__INIT_LOCALS',`ifelse($1,`CMUtils',`/*Obsolete include CMUtils*/ \ + ', +$1,`CmpLog',`/*Obsolete include CmpLog*/ \ + ', +`pI$1 = NULL; \ + ')')') + +define(`GEN_EXIT_STMT', `pushdef(`__EXITS',`ifelse($1,`CMUtils',`/*Obsolete include CMUtils*/ \ + ', +$1,`CmpLog',`/*Obsolete include CmpLog*/ \ + ', +`if (pI$1 != NULL) \ + { \ + pIBase = (IBase *)pI$1->QueryInterface(pI$1, ITFID_IBase, &exitResult); \ + if (pIBase != NULL) \ + { \ + pIBase->Release(pIBase); \ + if (pIBase->Release(pIBase) == 0) /* The object will be deleted here! */ \ + pI$1 = NULL; \ + } \ + } \ + ')')') + +define(`GEN_USED_INTERFACES', dnl +`ifelse(`-1',regexp(`$1',`\.m4$'), dnl + `error(`m4:'__file__`('__line__`):The first parameter "'$1`" is not a .m4 file + ')',dnl +`GEN_INCLUDE($1)'dnl +`GEN_INIT_STMT(`'patsubst(`$1',`\Itf.m4$',`'))'dnl +`GEN_INIT_LOCALS_STMT(`'patsubst(`$1',`\Itf.m4$',`'))'dnl +`GEN_EXIT_STMT(`'patsubst(`$1',`\Itf.m4$',`'))')dnl +') + +% ----------------------------- +% All variations of DEF_*_API +% Depending on the Variable DEF_CPP, +% we are adding them to __EXPORTS or __EXPORTS_CPP. +% ----------------------------- +define(`PUSH_ITF', dnl +`ifdef(`DEF_CPP', dnl +`pushdef(`__EXPORTS_CPLUSPLUS_ONLY',` \ + if (ERR_OK != EXP_`'_COMPONENT_NAME`'$3) \ + {\ + if (CHK_LogAdd)\ + (void)CAL_LogAdd(STD_LOGGER, COMPONENT_ID, LOG_ERROR, ERR_FAILED, 0, "Export of ApiFunction [$3] failed\n"); \ + return ERR_FAILED;\ + }')' +, dnl +`pushdef(`__EXPORTS_EXTREF',`ifelse(`$5',1,`ifelse(`$8',1,`', `{ (RTS_VOID_FCTPTR)$3, "$3", ifelse(eval($#>5),1,$6,0), ifelse(eval($#>6),1,$7,0) },\ + ')',`')')' dnl +`pushdef(`__EXPORTS_EXTREF2',`ifelse(`$8',1,`{ (RTS_VOID_FCTPTR)$3, "$3", $6, $7 }, \ + ',`')')' dnl +`pushdef(`__EXPORTS',`ifelse(`$5',1,`',`{ (RTS_VOID_FCTPTR)$3, "$3", 0, 0 },\ + ')')' dnl +)' dnl +) + +define(`PUSH_CLASS_ITF', dnl +`ifdef(`DEF_CPP', dnl +`pushdef(`__EXPORTS_CPLUSPLUS_ONLY',` \ + if (ERR_OK != EXP_`'_COMPONENT_NAME`'$3) \ + {\ + if (CHK_LogAdd)\ + (void)CAL_LogAdd(STD_LOGGER, COMPONENT_ID, LOG_ERROR, ERR_FAILED, 0, "Export of ApiFunction [$3] failed\n"); \ + return ERR_FAILED;\ + }')' +, dnl +`pushdef(`__EXPORTS_EXTREF',`ifelse(`$5',1,`ifelse(`$8',1,`',`{ (RTS_VOID_FCTPTR)$3, "$3",ifelse(eval($#>5),1,$6,0),ifelse(eval($#>6),1,$7,0)},\ + ')',`')')' dnl +`pushdef(`__EXPORTS_EXTREF2',`ifelse(`$8',1,`{ (RTS_VOID_FCTPTR)$3, "$3", $6, $7 }, \ + ',`')')' dnl +`pushdef(`__EXPORTS',`')' dnl +)' dnl +) + + + +define(`DEF_API', dnl +`PUSH_ITF($@)' dnl +) + +define(`DEF_STATIC_API', +`PUSH_ITF($@)' dnl +) + +define(`DEF_CREATEITF_API', +`PUSH_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_DELETEITF_API', +`PUSH_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_HANDLEITF_API', +`PUSH_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_HANDLEITF_API2', +`PUSH_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_HANDLEITF_API_OWNCPP', +`PUSH_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_CREATECLASSITF_API', +`PUSH_CLASS_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_CREATECLASSITF2_API', +`PUSH_CLASS_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_PRODUCECLASSITF_API', +`PUSH_CLASS_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_PRODUCECLASSITF_API_OWNCPP', +`PUSH_CLASS_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_DELETECLASSITF_API', +`PUSH_CLASS_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_DELETECLASSITF2_API', +`PUSH_CLASS_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_CLASSITF_API', +`PUSH_CLASS_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_RETURNHANDLECLASSITF_API', +`PUSH_CLASS_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_HANDLEITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_STATICITF_API', +`PUSH_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) static $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_ITF_API', +`PUSH_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_ITF_API_OWNCPP', +`PUSH_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_ITF_API2', +`PUSH_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + +define(`DEF_ITF_GLOBAL_API', +`PUSH_ITF($@)' dnl +`divert(DIVERT_CLASSMEMBERS) virtual $1 $2 `I'$3(_CONCAT(`_MAKE_ITF_PARAMLIST',_TRIM(`$4'))); +divert(-1)' +) + + + +define(`SET_INTERFACE_NAME',` + ifdef(`_IN_IMPLEMENT',` + pushdef(`_INTERFACE_LIST', `$1') + pushdef(`_INTERFACECAST_LIST',` + else if (iid == ITFID_`I'$1) + pItf = dynamic_cast<`I'$1 *>(this);')', + `ifelse($1,`CMUtils',`divert(DIVERT_USE)\ + /*obsolete entry ITF_$1*/ dnl divert(-1)', + $1,`CmpLog',`divert(DIVERT_USE)\ + /*obsolete entry ITF_$1*/ dnl divert(-1)', + `divert(DIVERT_USE)\ + `ITF_'`$1'dnl') + ifelse($1,`CMUtils',`divert(DIVERT_USEIMPORT)\ + /*obsolete entry ITF_$1*/ dnl divert(-1)', + $1,`CmpLog',`divert(DIVERT_USEIMPORT)\ + /*obsolete entry ITF_$1*/ dnl divert(-1)', + `divert(DIVERT_USEIMPORT)\ + `ITF_'`$1'dnl') + ifelse($1,`CMUtils',`divert(DIVERT_USEEXTERN)\ + /*obsolete entry EXTITF_$1*/ dnl divert(-1)', + $1,`CmpLog',`divert(DIVERT_USEEXTERN)\ + /*obsolete entry EXTITF_$1*/ dnl divert(-1)', + `divert(DIVERT_USEEXTERN)\ + `EXTITF_'`$1'dnl') + divert(-1)') +') + +define(`WRITE_INTERFACE_LIST2',`ifdef(`_INTERFACE_LIST',`, public `I'_INTERFACE_LIST popdef(`_INTERFACE_LIST')WRITE_INTERFACE_LIST2')') +define(`WRITE_INTERFACE_LIST',`ifdef(`_INTERFACE_LIST',`define(`_FIRST_INTERFACE_NAME',_INTERFACE_LIST)public `I'_INTERFACE_LIST popdef(`_INTERFACE_LIST')WRITE_INTERFACE_LIST2')') + +define(`WRITE_INTERFACE_CASTS',`ifdef(`_INTERFACECAST_LIST',`_INTERFACECAST_LIST popdef(`_INTERFACECAST_LIST')WRITE_INTERFACE_CASTS')') + + +define(`TASK',`#ifndef RTS_TASKNAME_`'_UPPERCASE($1) + #define RTS_TASKNAME_`'_UPPERCASE($1) "$1" +#endif +#ifndef RTS_TASKPRIO_`'_UPPERCASE($1) + #define RTS_TASKPRIO_`'_UPPERCASE($1) $2 +#endif +ifelse($3,`',`', `#ifndef RTS_TASKGROUP_`'_UPPERCASE($1) + #define RTS_TASKGROUP_`'_UPPERCASE($1) $3 +#endif') +') + +define(`TASKPREFIX',`#ifndef RTS_TASKPREFIX_`'_UPPERCASE($1) + #define RTS_TASKPREFIX_`'_UPPERCASE($1) "$1" +#endif +#ifndef RTS_TASKPRIO_`'_UPPERCASE($1) + #define RTS_TASKPRIO_`'_UPPERCASE($1) $2 +#endif +ifelse($3,`',`', `#ifndef RTS_TASKGROUP_`'_UPPERCASE($1) + #define RTS_TASKGROUP_`'_UPPERCASE($1) $3 +#endif') +') + +define(`TASKPLACEHOLDER',`#ifndef RTS_TASKPLACEHOLDER_`'_UPPERCASE($1) + #define RTS_TASKPLACEHOLDER_`'_UPPERCASE($1) "" +#endif +#ifndef RTS_TASKPRIO_`'_UPPERCASE($1) + #define RTS_TASKPRIO_`'_UPPERCASE($1) $2 +#endif +ifelse($3,`',`', `#ifndef RTS_TASKGROUP_`'_UPPERCASE($1) + #define RTS_TASKGROUP_`'_UPPERCASE($1) $3 +#endif') +') + + +% ----------------------------- +% Get all interfaces, which are implemented by this component. +% Differentiate between C++ and C Interfaces. +% ----------------------------- +define(`DO_IMPLEMENT_ITF',` +GET_INCLUDE_FILE($1) +GEN_INCLUDE($1) +ifelse(`$#',1,`',`DO_IMPLEMENT_ITF(shift($@))') +divert(-1) +include($1) +divert +') + + +define(`IMPLEMENT_QUERY_INTERFACE',`define(`QUERY_INTERFACE', `$1')') + +define(`IMPLEMENT_ITF',dnl +`undefine(`DEF_CPP')'dnl +`IMPLEMENT_ITF_AS(``C'_COMPONENT_NAME', $@)' +) +define(`IMPLEMENT_CPP_ITF', +`define(`DEF_CPP')' +`undefine(`_INTERFACE_LIST')undefine(`_INTERFACECAST_LIST')undefine(`_FIRST_INTERFACE_NAME')define(`_IN_IMPLEMENT')define(`_INTERFACE_LIST_INIT')' +`DO_IMPLEMENT_ITF($@)' +) +define(`IMPLEMENT_ITF_AS', +`ifdef(`_INTERFACE_LIST_INIT', `', +`undefine(`_INTERFACE_LIST')undefine(`_INTERFACECAST_LIST')undefine(`_FIRST_INTERFACE_NAME')' +)' +`define(`_IN_IMPLEMENT')define(`_INTERFACE_LIST_INIT')' +` +DO_IMPLEMENT_ITF(shift($@)) +divert(DIVERT_CLASSDEF) +class $1 : WRITE_INTERFACE_LIST +{ + public: + $1`() : h'_COMPONENT_NAME`'(RTS_INVALID_HANDLE), iRefCount(0) + { + } + virtual ~$1`()' + { + } + virtual unsigned long AddRef(IBase *pIBase = NULL) + { + iRefCount++; + return iRefCount; + } + virtual unsigned long Release(IBase *pIBase = NULL) + { + iRefCount--; + if (iRefCount == 0) + { + delete this; + return 0; + } + return iRefCount; + } + + ifdef(`QUERY_INTERFACE', `QUERY_INTERFACE', ` + virtual void* QueryInterface(IBase *pIBase, ITFID iid, RTS_RESULT *pResult) + { + void *pItf; + if (iid == ITFID_IBase) + pItf = dynamic_cast((`I'_FIRST_INTERFACE_NAME *)this);dnl + WRITE_INTERFACE_CASTS + else + { + if (pResult != NULL) + *pResult = ERR_NOTIMPLEMENTED; + return NULL; + } + if (pResult != (RTS_RESULT *)1) + (reinterpret_cast(pItf))->AddRef(); + if (pResult != NULL && pResult != (RTS_RESULT *)1) + *pResult = ERR_OK; + return pItf; + }') +divert(DIVERT_ENDCLASSDEF) + public: + RTS_HANDLE h`'_COMPONENT_NAME; + int iRefCount; +}; +divert(-1) +define(`DIVERT_CLASSDEF',eval(DIVERT_CLASSDEF+3)) +define(`DIVERT_CLASSMEMBERS',eval(DIVERT_CLASSMEMBERS+3)) +define(`DIVERT_ENDCLASSDEF',eval(DIVERT_ENDCLASSDEF+3)) +undefine(`_IN_IMPLEMENT') +divert +') + +%define(`IMPLEMENT_ITF', ` +%divert(-1) +%include($1) +%divert') + +define(`GENERATE_ITF_MACRO', `divert(DIVERT_USE)\ + `ITF_'`$1' dnl +divert(-1)') + +define(`USE_ITF', `GEN_USED_INTERFACES($1) +divert(-1) +pushdef(`DEF_API',`') +pushdef(`DEF_STATIC_API',`') +pushdef(`DEF_CREATEITF_API',`') +pushdef(`DEF_DELETEITF_API',`') +pushdef(`DEF_HANDLEITF_API',`') +pushdef(`DEF_HANDLEITF_API2',`') +pushdef(`DEF_HANDLEITF_API_OWNCPP',`') +pushdef(`DEF_CREATECLASSITF_API',`') +pushdef(`DEF_CREATECLASSITF2_API',`') +pushdef(`DEF_PRODUCECLASSITF_API',`') +pushdef(`DEF_PRODUCECLASSITF_API_OWNCPP',`') +pushdef(`DEF_DELETECLASSITF_API',`') +pushdef(`DEF_DELETECLASSITF2_API',`') +pushdef(`DEF_CLASSITF_API',`') +pushdef(`DEF_RETURNHANDLECLASSITF_API',`') +pushdef(`DEF_STATICITF_API',`') +pushdef(`DEF_ITF_API',`') +pushdef(`DEF_ITF_API_OWNCPP',`') +pushdef(`DEF_ITF_API2',`') +pushdef(`DEF_ITF_GLOBAL_API',`') +include($1) +popdef(`DEF_API') +popdef(`DEF_STATIC_API') +popdef(`DEF_CREATEITF_API') +popdef(`DEF_DELETEITF_API') +popdef(`DEF_HANDLEITF_API') +popdef(`DEF_HANDLEITF_API_OWNCPP') +popdef(`DEF_HANDLEITF_API2') +popdef(`DEF_CREATECLASSITF_API') +popdef(`DEF_CREATECLASSITF2_API') +popdef(`DEF_PRODUCECLASSITF_API') +popdef(`DEF_PRODUCECLASSITF_API_OWNCPP') +popdef(`DEF_DELETECLASSITF_API') +popdef(`DEF_DELETECLASSITF2_API') +popdef(`DEF_RETURNHANDLECLASSITF_API') +popdef(`DEF_CLASSITF_API') +popdef(`DEF_STATICITF_API') +popdef(`DEF_ITF_API2') +popdef(`DEF_ITF_API') +popdef(`DEF_ITF_API_OWNCPP') +popdef(`DEF_ITF_GLOBAL_API') +divert +') + +define(`DOUSE', `divert(DIVERT_USE) \ + ifelse($1,`CMUtlMemCpy',`/*obsolete USE_$1*/', + $1,`LogAdd',`/*obsolete USE_$1*/', + USE_$1) dnl + divert(DIVERT_USEIMPORT) \ + ifelse($1,`CMUtlMemCpy',`/*obsolete USE_$1*/', + $1,`LogAdd',`/*obsolete USE_$1*/', + USE_$1) dnl + divert(DIVERT_USEEXTERN) \ + ifelse($1,`CMUtlMemCpy',`/*obsolete USE_$1*/', + $1,`LogAdd',`/*obsolete EXT_$1*/', + EXT_$1) dnl +divert') + + +define(`DOIMPORT_REQ', `pushdef(`__IMPORTS',`ifelse($1,`LogAdd', `/*Obsolete required include LogAdd*/ \ + ',dnl +$1,`CMUtlMemCpy', `/*Obsolete required include CMUtlMemCpy*/ \ + ', +`if (ERR_OK == importResult ) importResult = GET_$1(0);\ + ')')') + + +define(`DOIMPORT_OPT', `pushdef(`__IMPORTS',`ifelse($1,`LogAdd', `/*Obsolete optional include LogAdd*/ \ + ',dnl +$1,`CMUtlMemCpy', `/*Obsolete optional include CMUtlMemCpy*/ \ + ', +`if (ERR_OK == importResult ) TempResult = GET_$1(CM_IMPORT_OPTIONAL_FUNCTION);\ + ')')') + + + +define(`REQUIRED_IMPORTS', dnl + `ifelse($1,`', `', dnl + `_FOREACH(`DOIMPORT_REQ', $@)dnl + _FOREACH(`DOUSE', $@)dnl + ') +') + +define(`OPTIONAL_IMPORTS', dnl + `ifelse($1,`', `', dnl + `_FOREACH(`DOIMPORT_OPT', $@) dnl + _FOREACH(`DOUSE', $@) dnl + ') +') +changecom(`/*', `*/') +divert \ No newline at end of file diff --git a/sdk/ExtensionSDK/m4/Util.m4 b/sdk/ExtensionSDK/m4/Util.m4 new file mode 100644 index 0000000..63f00a3 --- /dev/null +++ b/sdk/ExtensionSDK/m4/Util.m4 @@ -0,0 +1,113 @@ +divert(-1) +changecom(`%') +% +% Copyright: Copyright CODESYS Development GmbH +% + +define(`_CONCAT',`ifelse($#,0,`',$#,1,`$1',$#,2,`$1$2',`_CONCAT(`$1$2',`_CONCAT(shift(shift($@)))')')') +define(`_UPPERCASE', `translit($1, `a-z', `A-Z')') +define(`_TRIM',`regexp(`$1',`^[ ]*\(.*[^ ]\)[ ]*$',`\1')') +define(`_TRIMCMPPREFIX',`regexp(`$1',`...\(.*\)$',`\1')') +define(`_TRIMPOINTERPARAM', `ifelse(regexp(`$1',`[^,]\*',`RTS_HANDLE'),-1,0,1)') +define(`_TRIMVARPARAMLIST', `ifelse(regexp(`$1',`\.\.\.'),-1,0,1)') + +define(`_FUN_PTR', _FUN_PTR1(`_UPPERCASE(`$1')')) +define(`_FUN_PTR_IEC', _FUN_PTR1(`_UPPERCASE(`$1')'_IEC)) +define(`_FUN_PTR1', `PF'$1) + +define( _CHKSUM, 0) + +define(`_EXTRACTFIRSTPARAMTYPE',`_EXTRACTTYPE(_EXTRACTPARAMS(`$1'))') +define(`_EXTRACTTYPE',`regexp(`$1',`\([\w_]*[^ ]*\)',`\1')') +define(`_EXTRACTFIRSTPARAMTYPEPOINTER',`_EXTRACTTYPEPOINTER(_EXTRACTPARAMS(`$1'))') +define(`_EXTRACTTYPEPOINTER',`regexp(`$1',`\([\*]\)',`\1')') +define(`_EXTRACTFIRSTPARAMTYPERTS',`_EXTRACTTYPERTS(_EXTRACTPARAMS(`$1'))') +define(`_EXTRACTTYPERTS',`regexp(`$1',`\(RTS_\)',`\1')') + +define(`_EXTRACTPARAMS',`regexp(`$1',`^[ ]*(\(.*\))[ ]*$',`\1')') +define(`_EXTRACTPARAMNAME',`regexp(`$1',`\([a-zA-Z0-9_]+\)$',`\1')') +define(`_HAS_VARIABLEPARAMETER',`regexp(`$1',`\.\.\.')') +define(`_EXTRACT_VARIABLEPARAMETER_FORMAT',`regexp(`$1',` \*?\([a-zA-Z0-9_]+\), \.\.\.',`\1')') + +% +% Extract the parameter out of a parameter list. +% +% _EXTRACT_PARAM_LIST(`(int nCount, char *pBuf)') => `nCount,pBuf' +% _EXTRACT_PARAM_LIST(`(void)') => `' +% +define(_EXTRACT_PARAM_LIST_RECURSIVE,`ifelse($#,1,`_EXTRACTPARAMNAME(`$1')',`_EXTRACTPARAMNAME(`$1')`',_EXTRACT_PARAM_LIST_RECURSIVE(shift($@))')') +define(_EXTRACT_PARAM_LIST_,`ifelse(_TRIM(`$1'),`',`',_TRIM(`$1'),`void',`',`_EXTRACT_PARAM_LIST_RECURSIVE($@)')') +define(_EXTRACT_PARAM_LIST,`_EXTRACT_PARAM_LIST_(_EXTRACTPARAMS(`$1'))') + +define(_EXTRACT_PARAM_LIST_HANDLE_RECURSIVE,`ifelse($#,1,`,_EXTRACTPARAMNAME(`$1')',`,_EXTRACTPARAMNAME(`$1')`'_EXTRACT_PARAM_LIST_HANDLE_RECURSIVE(shift($@))')') +define(_EXTRACT_PARAM_LIST_HANDLE_,`ifelse(_TRIM(`$1'),`',`',_TRIM(`$1'),`void',`',`_EXTRACT_PARAM_LIST_HANDLE_RECURSIVE($@)')') +define(_EXTRACT_PARAM_LIST_HANDLE,`_EXTRACT_PARAM_LIST_HANDLE_(shift(_EXTRACTPARAMS(`$1')))') + +% +% Creates a parameter list for a macro call "(p1, p2, p3)", with one parameter for each parameter +% in the c parameterlist provided as parameter. If the provided parameterlist contains only `void' +% then it is treated as if it was empty. +% +% _MAKE_DUMMYPARAMLIST(`(int nCount, char *pBuf)') => `p0,p1' +% _MAKE_DUMMYPARAMLIST(`(void)') => `' +% +define(_MAKE_DUMMYPARAMLIST1,`ifelse($#,1,`',$#,2,`p$1',`p$1,_MAKE_DUMMYPARAMLIST1(incr($1),shift(shift($@)))')') +define(_MAKE_DUMMYPARAMLIST_,`ifelse(_TRIM(`$1'),`',`',_TRIM(`$1'),`void',`',`_MAKE_DUMMYPARAMLIST1(0,$@)')') +define(_MAKE_DUMMYPARAMLIST_WITHVOID_,`ifelse(_TRIM(`$1'),`',`',`_MAKE_DUMMYPARAMLIST1(0,$@)')') +define(_MAKE_DUMMYPARAMLIST,`_MAKE_DUMMYPARAMLIST_(_EXTRACTPARAMS(`$1'))') + +% +% Creates a parameter list for a macro call "(p1, p2, p3)", with one parameter for each parameter +% in the c parameterlist provided as parameter. If the provided parameterlist contains only `void' +% then it is treated as if it had exactly one parameter. +% +% _MAKE_DUMMYPARAMLIST(`(int nCount, char *pBuf)') => `p0,p1' +% _MAKE_DUMMYPARAMLIST(`(void)') => `p0' +% +define(_MAKE_DUMMYPARAMLIST_WITHVOID,`_MAKE_DUMMYPARAMLIST_WITHVOID_(_EXTRACTPARAMS(`$1'))') + +% +% Same as _MAKE_DUMMYPARAMLIST, but drops the first parameter +% +% _MAKE_HANDLE_DUMMYPARAMLIST(`(RTS_HANDLE, hHandle, int nCount, char *pBuf)') => `p1,p2' +% +define(_MAKE_HANDLE_DUMMYPARAMLIST,`shift(_MAKE_DUMMYPARAMLIST(`$1'))') + +% +% Same as _MAKE_DUMMYPARAMLIST, but drops the first n parameter +% +% _MAKE_HANDLE_DUMMYPARAMLIST2(`(RTS_HANDLE hHandle, int nCount, char *pBuf, int nValue)', 2) => `p2,p3' +% +define(nshift, `ifelse($2, 0, `$1', `nshift(`shift($1)', decr($2))')') +define(_MAKE_HANDLE_DUMMYPARAMLIST2,`nshift(`_MAKE_DUMMYPARAMLIST(`$1')', $2)') + + +define(_MAKE_PARAMLIST2,`ifelse(_TRIM($@),`',`',`, $1`'_MAKE_PARAMLIST2(shift($@))')') +define(_MAKE_PARAMLIST1,`ifelse(_TRIM($@),`',`',`$1`'_MAKE_PARAMLIST2(shift($@))')') +define(_MAKE_HANDLEITF_PARAMLIST,`ifelse(_TRIM(shift($@)),`',`void',_TRIM(shift($@)),`void',`void',`_MAKE_PARAMLIST1(shift($@))')') +define(_MAKE_ITF_PARAMLIST, `ifelse(_TRIM($@),`',`void',_TRIM($@),`void',`void',`_MAKE_PARAMLIST1($@)')') +define(_MAKE_CREATEITF_PARAMLIST,`ifelse(_TRIM($@),`',`void',_TRIM($@),`void',`void',`_MAKE_PARAMLIST1($@)')') + +% +% Ieration over some string elements +% +define(`_FOREACH', `ifelse(1,$#,` ',`$1($2) +ifelse(2,$#, ,`_FOREACH(`$1',shift(shift($@)))')')') + +% +% Split component version number +% +% Example: +% $1 = "0x03020104" +% _VERSION_MAJOR => 3 +% _VERSION_SUB0 => 2 +% _VERSION_SUB1 => 1 +% _VERSION_SUB2 => 4 +% +define(`_TRIMLEFT_ZERO', `regexp(`$1', `^0*\(.+\)', `\1')') +define(`_VERSION_MAJOR',`_TRIMLEFT_ZERO(regexp(`$1',`^0x\([0-9a-fA-F][0-9a-fA-F]\)', `eval(0x\1)'))') +define(`_VERSION_SUB0',`_TRIMLEFT_ZERO(regexp(`$1',`^0x[0-9a-fA-F][0-9a-fA-F]\([0-9a-fA-F][0-9a-fA-F]\)', `eval(0x\1)'))') +define(`_VERSION_SUB1',`_TRIMLEFT_ZERO(regexp(`$1',`^0x[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]\([0-9a-fA-F][0-9a-fA-F]\)', `eval(0x\1)'))') +define(`_VERSION_SUB2',`_TRIMLEFT_ZERO(regexp(`$1',`^0x[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]\([0-9a-fA-F][0-9a-fA-F]\)', `eval(0x\1)'))') + +divert`'dnl diff --git a/sdk/ExtensionSDK/makefile b/sdk/ExtensionSDK/makefile new file mode 100644 index 0000000..9edd426 --- /dev/null +++ b/sdk/ExtensionSDK/makefile @@ -0,0 +1,183 @@ +############################################################################## +# Copyright: Copyright CODESYS Development GmbH +# Program: Extension API for the Linux SL products +############################################################################## + +ifeq ($(dir $(lastword $(MAKEFILE_LIST))), ./) +# +# MAKEFILE is used wrong! Never call it from this folder directly +# +.PHONY:help +help: + @echo "**********************" + @echo "Dont work inside this folder! Please go to your library folder and call this makefile e.g.:" + @echo "make -f ../sdk/makefile newproject" + @echo + @echo "**********************" + +.PHONY:all +all: help + +.PHONY:newproject +newproject: help + +.PHONY:makefile +makefile: help + +else +# +# NORMAL RULES +# + +# get some info from generated files +ITFM4 = $(wildcard *Itf.m4) +CSRC = $(wildcard *.c) +CMPBASENAME=$(CSRC:.c=) + +# set some variables +OUTDIR=./out +ITF = $(basename ${ITFM4}) +TEMPLATEDEP = $(SDKDIR)/m4/TemplateDep.m4 +DEP = ${CMPBASENAME}Dep + + +# set name of shared object +SONAME= lib${CMPBASENAME}.so + +# set sources of component +SRC += ${CSRC} CmpFrame.c + +# objects: all sources +OBJS = $(addprefix ${OUTDIR}/,$(SRC:.c=.o)) + +# set default includes +INCLUDES += -I${SDKDIR}/include/ -I${OUTDIR}/ -I. +CFLAGS += -fPIC -g -O0 -Wall -Wextra -Wno-unused +CFLAGS += -DDEP_H=\"${DEP}.h\" + +# set a vendorID +CMPVENDORID=0xFFFF + +vpath %.m4 ${SDKDIR}/cmpstub/ +vpath %.c ${SDKDIR}/src/ + +.PHONY:help +help: + @echo "**********************" + @echo "help / usage / available make targets:" + @echo "-all : just do everything needed" + @echo "-clean : cleanup all generated files" + @echo "-newproject : create a new project" + @echo "-showenv : show intern variables (for debugging)" + @echo "**********************" + + +.PHONY:newproject +newproject: makefile + +.PHONY:all +all: ${OUTDIR}/$(SONAME) + +SDK_BASE=$(dir $(lastword $(MAKEFILE_LIST))) + +.PHONY:showenv +showenv: + @echo "**********************" + @echo "ITFM4: ${ITFM4}" + @echo "CSRC: ${CSRC}" + @echo "SONAME: ${SONAME}" + @echo "ITF: ${ITF}" + @echo "DEP: ${DEP}" + @echo "OBJS: ${OBJS}" + @echo "CC: $(CC)" + @echo "CFLAGS: ${CFLAGS}" + @echo "SDKDIR: ${SDKDIR}" + @echo "CMPBASENAME: ${CMPBASENAME}" + @echo "MAKEFILE_LIST: $(MAKEFILE_LIST)" + @echo "**********************" + @echo "" + +# check if there are whitespaces in the SDK BASE DIR +# this causes horrible things in make, so we detect/avoid it directly at creating a new project +ifeq (1,$(words $(MAKEFILE_LIST))) +makefile: + @echo "**********************" + @echo "creating makefile for your project " + cp ${SDK_BASE}/makefile_template $@ + @echo "# include of the SDK makefiles" >> $@ + @echo "SDKDIR="$(abspath ${SDK_BASE}) >> $@ + @echo 'include $${SDKDIR}/makefile' >> $@ + @echo "" >> $@ + @echo "now check your makefile here" + @echo "**********************" + @echo "" + +else +makefile: + @echo "**********************" + @echo "creating makefile for your project FAILED:" + @echo "there are whitespaces in the SDK path" + @echo "this does not work with 'make'!" + @echo "Please put your SDK to a path without whitespaces" + @echo "**********************" + @echo "" + +endif + +${OUTDIR}/%.o: %.c + @echo "**********************" + @echo "compile: $@ from $<" + @mkdir -p ${OUTDIR} + $(CC) -c $(CFLAGS) $(INCLUDES) -o $@ $< + @echo "**********************" + @echo "" + +${OUTDIR}/${SONAME}: ${OUTDIR}/$(DEP).h ${OUTDIR}/$(ITF).h $(OBJS) + @echo "**********************" + @echo "link: $@" + @mkdir -p ${OUTDIR} + $(CC) $(LDFLAGS) -shared $(OBJS) -Wl,-soname,$(SONAME) -o $@ $(LDLIBS) + @echo "**********************" + @echo "" + +${OUTDIR}/$(DEP).m4: + @echo "**********************" + @echo "create $@ from template" + @mkdir -p ${OUTDIR} + echo 'SET_COMPONENT_NAME(`'${CMPBASENAME}"')" > $@ + echo 'COMPONENT_VERSION(`'${CMPVERSION}"')" >> $@ + echo 'COMPONENT_VENDORID(`'${CMPVENDORID}"')" >> $@ + echo "#define CMPID_${CMPBASENAME} ${CMPID}" >> $@ + echo "IMPLEMENT_ITF("'`'"${CMPBASENAME}Itf.m4')" >> $@ + @echo "**********************" + @echo "" + +${OUTDIR}/$(DEP).h: ${OUTDIR}/$(DEP).m4 $(ITF).m4 + @echo "**********************" + @echo "build dependency header: $@ from $<" + @mkdir -p ${OUTDIR} + $(DOS2UNIX) $< + $(M4) -I$(SDKDIR)/m4 -I. Implementation.m4 - < $< > $@ + @echo "**********************" + @echo "" + +${OUTDIR}/$(ITF).h: $(ITF).m4 + @echo "**********************" + @echo "build interface header: $@ from $<" + @sed -n '/DEF_API/ !d; /_cext/ ! { s/^/ERROR: This functionname does not contain _cext (this is mandatory, see help for details):\n/p;q 1}' $< + @mkdir -p ${OUTDIR} + $(DOS2UNIX) $< + $(M4) -I$(SDKDIR)/m4 Exports.m4 - < $< > $@ + @echo "**********************" + @echo "" + +.PHONY:clean +clean: + @echo "" + @echo "**********************" + @echo "clean up" + rm -rf ${OUTDIR}; + @echo "**********************" + @echo "" + +endif diff --git a/sdk/ExtensionSDK/makefile_template b/sdk/ExtensionSDK/makefile_template new file mode 100644 index 0000000..fc510d9 --- /dev/null +++ b/sdk/ExtensionSDK/makefile_template @@ -0,0 +1,48 @@ +############################################################################## +# Copyright: Copyright CODESYS Development GmbH +# Program: Extension API for the Linux SL products +############################################################################## + +# +# with this makefile you can influence the build of your component +# +# +# Hints: +# - the name of the *Itf.m4 file in this folder will be used as name of the shared object (and component name) +# - all C files in this folder will be compiled by default +# + +# set versions of your shared object +# must be single digit, decimal +MAJORVER=0 +MINORVER=1 + +# set a component version (must match your IEC library version) +# must be 4 bytes, hexadecimal +CMPVERSION=0x01000000 + +# set a component ID +# must be 2 bytes >= 0x2000 and hexadecimal and can be used to differentiate different components +CMPID=0x2000 + +# set your tools +DOS2UNIX = dos2unix +M4 = m4 + +# set a compiler +CC ?= gcc + +# add some compiler flags (with += ) +#CFLAGS += -g + +# add some include paths (with +=) +#INCLUDES += -I. + +# add some linker flags (with += ) +#LDFLAGS += + +# add some libraries that you might need (with += ) +#LDLIBS += -lc + + + diff --git a/sdk/ExtensionSDK/src/CmpFrame.c b/sdk/ExtensionSDK/src/CmpFrame.c new file mode 100644 index 0000000..ae31e13 --- /dev/null +++ b/sdk/ExtensionSDK/src/CmpFrame.c @@ -0,0 +1,78 @@ +/***************************************************************************** +* Copyright: Copyright CODESYS Development GmbH +* Program: Extension API for the Linux SL products +******************************************************************************/ + +#include "CmpStd.h" + +#include DEP_H + +USE_STMT + +DLL_DECL int CDECL ComponentEntry(INIT_STRUCT *pInitStruct) +/* Used to exchange function pointers between component manager and components. + Called at startup for each component. + pInitStruct: IN Pointer to structure with: + pfExportFunctions OUT Pointer to function that exports component functions + pfImportFunctions OUT Pointer to function that imports functions from other components + pfGetVersion OUT Pointer to function to get component version + pfRegisterAPI IN Pointer to component manager function to register a API function + pfGetAPI IN Pointer to component manager function to get a API function + pfCallHook IN Pointer to component manager function to call a hook function + Return ERR_OK if library could be initialized, else error code +*/ +{ + pInitStruct->CmpId = COMPONENT_ID; + pInitStruct->pfExportFunctions = ExportFunctions; + pInitStruct->pfImportFunctions = ImportFunctions; + pInitStruct->pfGetVersion = CmpGetVersion; + pInitStruct->pfHookFunction = HookFunction; + pInitStruct->pfCreateInstance = NULL; + pInitStruct->pfDeleteInstance = NULL; + + s_pfCMRegisterAPI = pInitStruct->pfCMRegisterAPI; + s_pfCMRegisterAPI2 = pInitStruct->pfCMRegisterAPI2; + s_pfCMGetAPI = pInitStruct->pfCMGetAPI; + s_pfCMGetAPI2 = pInitStruct->pfCMGetAPI2; + s_pfCMCallHook = pInitStruct->pfCMCallHook; + s_pfCMRegisterClass = pInitStruct->pfCMRegisterClass; + s_pfCMCreateInstance = pInitStruct->pfCMCreateInstance; + return ERR_OK; +} + +/** + * Publish exported functions to the component manager, to make them available for other components. + * Use generated macro EXPORT_STMT. + * ERR_OK or error code in case of error + */ +static int CDECL ExportFunctions(void) +{ + /* Macro to export functions */ + EXPORT_STMT; + return ERR_OK; +} + +/** + * Import function pointers from other components. Use generated macro IMPORT_STMT. + * ERR_OK or error code if function name could not be resolved + */ +static int CDECL ImportFunctions(void) +{ + /* Macro to import functions */ + IMPORT_STMT; + return ERR_OK; +} + +/** + * Return the version of our component. + * Version as defined in dep file + */ +static RTS_UI32 CDECL CmpGetVersion(void) +{ + return CMP_VERSION; +} + +static RTS_RESULT CDECL HookFunction(RTS_UI32 ulHook, RTS_UINTPTR ulParam1, RTS_UINTPTR ulParam2) +{ + return ERR_OK; +} diff --git a/src/atom_bridge.c b/src/atom_bridge.c new file mode 100644 index 0000000..694610f --- /dev/null +++ b/src/atom_bridge.c @@ -0,0 +1,86 @@ +#include "atom_bridge.h" +#include "atom_mb.h" +#include +#include +typedef struct { am_client c; uint32_t generation; int used, retired; } slot; +static slot slots[AM_POOL_SIZE]; +static uint32_t token(unsigned i) { return (slots[i].generation<<8)|(i+1u); } +static slot *lookup(uint32_t h) { + unsigned n=h&255u; + if(!n || n>AM_POOL_SIZE) return NULL; + slot *s=&slots[n-1]; + return s->used && s->generation==(h>>8)?s:NULL; +} +static void snapshot(const am_client *c,uint32_t h,uint32_t *s) { + s[0]=h; s[1]=(uint32_t)c->state; s[2]=(uint32_t)c->busy; + s[3]=(uint32_t)c->done; s[4]=(uint32_t)c->error; s[5]=(uint32_t)c->result; + s[6]=(uint32_t)c->last_error; s[7]=(uint32_t)c->socket_error; + s[8]=c->exception; s[9]=(uint32_t)c->write_uncertain; + s[10]=c->connect_attempts; s[11]=c->reconnects; s[12]=c->completed; s[13]=c->failed; + s[14]=(uint32_t)c->has_success; s[15]=(uint32_t)c->last_success_ms; + s[16]=(uint32_t)(c->last_success_ms>>32); s[17]=c->quantity; + s[18]=c->function; s[19]=c->unit; s[20]=(uint32_t)c->enabled; + s[21]=c->connect_timeout_ms; s[22]=c->response_timeout_ms; s[23]=c->retry_delay_ms; +} +uint32_t ab_call(uint32_t cmd,uint32_t h,uint32_t ip,uint16_t port, + uint8_t unit,uint8_t f,uint16_t addr,uint16_t q,uint16_t *data,uint32_t cap, + uint32_t *status,uint32_t sc,uint64_t now,uint32_t ct,uint32_t rt,uint32_t retry) { + slot *s; unsigned i; uint32_t result=0; + if(!status || sc>24),(unsigned)((ip>>16)&255u), + (unsigned)((ip>>8)&255u),(unsigned)(ip&255u)); + if(!am_init(&s->c,text,port)) return AM_ARGUMENT; + s->generation++; s->used=1; h=token(i); + snapshot(&s->c,h,status); return 0; + } + s=lookup(h); + if(!s) { memset(status,0,AM_STATUS_WORDS*sizeof(*status)); return AB_BAD_HANDLE; } + switch(cmd) { + case AB_CLOSE: + am_close(&s->c); s->used=0; + /* Never recycle a generation after wrap: stale handles stay invalid. */ + if(s->generation==0xffffffu) s->retired=1; + snapshot(&s->c,0,status); return 0; + case AB_ENABLE: am_enable(&s->c,1,now); break; + case AB_DISABLE: am_enable(&s->c,0,now); break; + case AB_TICK: am_tick(&s->c,now); break; + case AB_RECONNECT: am_reconnect(&s->c,now); break; + case AB_CONFIGURE: + if(s->c.enabled) result=AM_BUSY; + else if(!ct || !rt || !retry) result=AM_ARGUMENT; + else { s->c.connect_timeout_ms=ct; s->c.response_timeout_ms=rt; s->c.retry_delay_ms=retry; } + break; + case AB_SUBMIT: + if((f==5 || f==6 || f==15 || f==16) && (!data || capc,unit,f,addr,q,data,now); + break; + case AB_COPY_READ: + if(!s->c.done || s->c.function>4 || s->c.function<1) result=AM_ARGUMENT; + else if(!data || capc.quantity) result=AB_BAD_CAPACITY; + else memcpy(data,s->c.data,s->c.quantity*sizeof(*data)); + break; + case AB_ACK: s->c.done=0; s->c.error=0; break; + case AB_REQUEST_TIMEOUT: + if(s->c.busy) result=AM_BUSY; + else if(!rt) result=AM_ARGUMENT; + else s->c.response_timeout_ms=rt; + break; + default: result=AB_BAD_COMMAND; break; + } + snapshot(&s->c,h,status); return result; +} +void ab_shutdown(void) { + unsigned i; + for(i=0;i +/* Fixed pool, one IEC task owns ALL calls. Do not share across tasks. + * Only synchronous pointer access; generation-checked handles, no heap. */ +#define AM_STATUS_WORDS 24 +#define AM_POOL_SIZE 16 +enum { AB_OPEN=1, AB_CLOSE, AB_ENABLE, AB_DISABLE, AB_TICK, AB_SUBMIT, + AB_COPY_READ, AB_RECONNECT, AB_CONFIGURE, AB_ACK, AB_REQUEST_TIMEOUT }; +enum { AB_BAD_HANDLE=256, AB_POOL_FULL, AB_BAD_CAPACITY, AB_BAD_COMMAND }; +uint32_t ab_call(uint32_t command,uint32_t handle,uint32_t ip,uint16_t port, + uint8_t unit,uint8_t function,uint16_t address,uint16_t quantity, + uint16_t *data,uint32_t capacity,uint32_t *status,uint32_t status_capacity, + uint64_t now,uint32_t connect_ms,uint32_t response_ms,uint32_t retry_ms); +/* Call on component unload; invalidate handles and close all sockets. */ +void ab_shutdown(void); +#endif diff --git a/src/atom_mb.c b/src/atom_mb.c new file mode 100644 index 0000000..0e2107b --- /dev/null +++ b/src/atom_mb.c @@ -0,0 +1,146 @@ +#include "atom_mb.h" +#include +#include +#include +#include +#include +#include +#include +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif +static uint16_t get16(const uint8_t *p) { return (uint16_t)((p[0]<<8)|p[1]); } +static void put16(uint8_t *p,uint16_t v) { p[0]=(uint8_t)(v>>8); p[1]=(uint8_t)v; } +static int writing(uint8_t f) { return f==5 || f==6 || f==15 || f==16; } +static void drop(am_client *c) { if(c->fd>=0) close(c->fd); c->fd=-1; } +static void finish(am_client *c,am_error e) { + c->busy=0; c->done=e==AM_OK; c->error=e!=AM_OK; c->result=e; + if(e==AM_OK) c->completed++; else { c->failed++; c->last_error=e; } +} +static void fault(am_client *c,am_error e,uint64_t now) { + if(c->busy) { c->write_uncertain=writing(c->function) && c->tx_pos>0; finish(c,e); } + c->last_error=e; drop(c); c->state=c->enabled?AM_RETRY:AM_DISABLED; + c->deadline_ms=now+c->retry_delay_ms; +} +int am_init(am_client *c,const char *ip,uint16_t port) { + struct in_addr a; + if(!c || !ip || strlen(ip)>=16 || !port || inet_pton(AF_INET,ip,&a)!=1) return 0; + memset(c,0,sizeof(*c)); c->fd=-1; strcpy(c->ip,ip); c->port=port; + c->connect_timeout_ms=2000; c->response_timeout_ms=1000; c->retry_delay_ms=1000; + return 1; +} +void am_close(am_client *c) { c->enabled=0; fault(c,AM_CANCELLED,0); } +void am_enable(am_client *c,int enabled,uint64_t now) { + if(!enabled) { if(c->enabled) am_close(c); return; } + if(!c->enabled) { c->enabled=1; c->state=AM_RETRY; c->deadline_ms=now; } +} +void am_reconnect(am_client *c,uint64_t now) { fault(c,AM_CANCELLED,now); c->deadline_ms=now; } +am_error am_request(am_client *c,uint8_t unit,uint8_t f,uint16_t addr,uint16_t q, + const uint16_t *v,uint64_t now) { + size_t n=12, bytes=0; unsigned i; + if(c->busy) return AM_BUSY; + if(c->state!=AM_READY) return AM_OFFLINE; + if(!q || (uint32_t)addr+q>65536u) return AM_ARGUMENT; + switch(f) { + case 1: case 2: if(q>2000) return AM_ARGUMENT; break; + case 3: case 4: if(q>125) return AM_ARGUMENT; break; + case 5: case 6: if(q!=1 || !v || (f==5 && v[0]>1)) return AM_ARGUMENT; break; + case 15: if(q>1968 || !v) return AM_ARGUMENT; + for(i=0;i1) return AM_ARGUMENT; + bytes=(q+7u)/8u; n=13+bytes; break; + case 16: if(q>123 || !v) return AM_ARGUMENT; bytes=2u*q; n=13+bytes; break; + default: return AM_ARGUMENT; + } + memset(c->tx,0,sizeof(c->tx)); c->transaction++; + put16(c->tx,c->transaction); put16(c->tx+4,(uint16_t)(n-6)); + c->tx[6]=unit; c->tx[7]=f; put16(c->tx+8,addr); put16(c->tx+10,q); + if(f==5 || f==6) put16(c->tx+10,f==5?(v[0]?0xff00:0):v[0]); + if(f==15 || f==16) { + c->tx[12]=(uint8_t)bytes; + for(i=0;itx+13+2*i,v[i]); + else if(v[i]) c->tx[13+i/8]|=(uint8_t)(1u<<(i%8)); + } + } + c->unit=unit; c->function=f; c->address=addr; c->quantity=q; + c->tx_len=n; c->tx_pos=0; c->rx_len=0; c->rx_need=7; + c->busy=1; c->done=0; c->error=0; c->exception=0; c->write_uncertain=0; + c->result=AM_OK; c->state=AM_SENDING; c->deadline_ms=now+c->response_timeout_ms; + return AM_OK; +} +static void response(am_client *c,uint64_t now) { + unsigned i; size_t bytes; + if(c->rx[7]==(uint8_t)(c->function|0x80)) { + if(c->rx_need!=9 || !c->rx[8]) { fault(c,AM_PROTOCOL,now); return; } + c->exception=c->rx[8]; finish(c,AM_EXCEPTION); c->state=AM_READY; return; + } + if(c->rx[7]!=c->function) { fault(c,AM_PROTOCOL,now); return; } + if(writing(c->function)) { + if(c->rx_need!=12 || memcmp(c->rx+8,c->tx+8,4)) { fault(c,AM_PROTOCOL,now); return; } + } else { + bytes=c->function<=2?(c->quantity+7u)/8u:2u*c->quantity; + if(c->rx_need!=9+bytes || c->rx[8]!=bytes) { fault(c,AM_PROTOCOL,now); return; } + for(i=0;iquantity;i++) c->data[i]=c->function<=2? + (uint16_t)((c->rx[9+i/8]>>(i%8))&1):get16(c->rx+9+2*i); + } + c->last_success_ms=now; c->has_success=1; finish(c,AM_OK); c->state=AM_READY; +} +void am_tick(am_client *c,uint64_t now) { + struct sockaddr_in a; struct pollfd p; int e,r; socklen_t len; ssize_t n; + if(!c->enabled) return; + if(c->state==AM_RETRY) { + if(nowdeadline_ms) return; + c->connect_attempts++; if(c->connect_attempts>1) c->reconnects++; + c->socket_error=0; c->fd=socket(AF_INET,SOCK_STREAM,0); + if(c->fd<0) { c->socket_error=errno; fault(c,AM_SOCKET,now); return; } + r=fcntl(c->fd,F_GETFL,0); + if(r<0 || fcntl(c->fd,F_SETFL,r|O_NONBLOCK)<0) { c->socket_error=errno; fault(c,AM_SOCKET,now); return; } + memset(&a,0,sizeof(a)); a.sin_family=AF_INET; a.sin_port=htons(c->port); + if(inet_pton(AF_INET,c->ip,&a.sin_addr)!=1) { fault(c,AM_ARGUMENT,now); return; } + r=connect(c->fd,(struct sockaddr*)&a,sizeof(a)); + if(r==0) c->state=AM_READY; + else if(errno==EINPROGRESS) { c->state=AM_CONNECTING; c->deadline_ms=now+c->connect_timeout_ms; } + else { c->socket_error=errno; fault(c,AM_SOCKET,now); } + return; + } + if(c->state==AM_CONNECTING) { + if(now>=c->deadline_ms) { fault(c,AM_CONNECT_TIMEOUT,now); return; } + p.fd=c->fd; p.events=POLLOUT; p.revents=0; r=poll(&p,1,0); + if(r<0) { if(errno!=EINTR) { c->socket_error=errno; fault(c,AM_SOCKET,now); } return; } + if(!r) return; + len=sizeof(e); e=0; + if(getsockopt(c->fd,SOL_SOCKET,SO_ERROR,&e,&len)<0) e=errno; + if(e) { c->socket_error=e; fault(c,AM_SOCKET,now); } else c->state=AM_READY; + return; + } + if(c->state==AM_READY) { + /* Detect idle peer closure; unsolicited bytes cannot belong to a request. */ + uint8_t b; n=recv(c->fd,&b,1,MSG_PEEK); + if(n==0) fault(c,AM_PEER_CLOSED,now); + else if(n>0) fault(c,AM_PROTOCOL,now); + else if(errno!=EAGAIN && errno!=EWOULDBLOCK && errno!=EINTR) { c->socket_error=errno; fault(c,AM_SOCKET,now); } + return; + } + if(now>=c->deadline_ms) { fault(c,AM_RESPONSE_TIMEOUT,now); return; } + if(c->state==AM_SENDING) { + n=send(c->fd,c->tx+c->tx_pos,c->tx_len-c->tx_pos,MSG_NOSIGNAL); + if(n>0) { c->tx_pos+=(size_t)n; if(c->tx_pos==c->tx_len) c->state=AM_RECEIVING; } + else if(n==0) fault(c,AM_PEER_CLOSED,now); + else if(errno!=EAGAIN && errno!=EWOULDBLOCK && errno!=EINTR) { c->socket_error=errno; fault(c,AM_SOCKET,now); } + return; + } + if(c->state!=AM_RECEIVING) return; + n=recv(c->fd,c->rx+c->rx_len,c->rx_need-c->rx_len,0); + if(n==0) { fault(c,AM_PEER_CLOSED,now); return; } + if(n<0) { if(errno!=EAGAIN && errno!=EWOULDBLOCK && errno!=EINTR) { c->socket_error=errno; fault(c,AM_SOCKET,now); } return; } + c->rx_len+=(size_t)n; + if(c->rx_len!=c->rx_need) return; + if(c->rx_need==7) { + uint16_t l=get16(c->rx+4); + if(get16(c->rx)!=c->transaction || get16(c->rx+2)!=0 || c->rx[6]!=c->unit || l<3 || l>254) { + fault(c,AM_PROTOCOL,now); return; + } + c->rx_need=6u+l; return; + } + response(c,now); +} diff --git a/src/atom_mb.h b/src/atom_mb.h new file mode 100644 index 0000000..f0c50d7 --- /dev/null +++ b/src/atom_mb.h @@ -0,0 +1,38 @@ +#ifndef ATOM_MB_H +#define ATOM_MB_H +#include +#include +/* Single owner/task per context. Init once, close before disposal. No heap allocation. + * tick() time must be monotonic milliseconds. POSIX IPv4 transport, numeric IP only. */ +typedef enum { AM_DISABLED, AM_RETRY, AM_CONNECTING, AM_READY, AM_SENDING, AM_RECEIVING } am_state; +typedef enum { AM_OK, AM_ARGUMENT, AM_BUSY, AM_OFFLINE, AM_SOCKET, AM_CONNECT_TIMEOUT, + AM_RESPONSE_TIMEOUT, AM_PEER_CLOSED, AM_PROTOCOL, AM_EXCEPTION, AM_CANCELLED } am_error; +typedef struct { + int fd, enabled; + am_state state; + am_error last_error, result; + int socket_error; + uint8_t exception, unit, function; + uint16_t transaction, address, quantity; + uint32_t connect_timeout_ms, response_timeout_ms, retry_delay_ms; + uint64_t deadline_ms, last_success_ms; + uint32_t connect_attempts, reconnects, completed, failed; + int busy, done, error, write_uncertain, has_success; + char ip[16]; uint16_t port; + uint8_t tx[260], rx[260]; + size_t tx_len, tx_pos, rx_len, rx_need; + /* Reads: coils/discretes unpacked as 0/1; registers as host-order uint16_t. + * Result is copied only after the entire response validates. */ + uint16_t data[2000]; +} am_client; +int am_init(am_client *c, const char *ip, uint16_t port); +void am_enable(am_client *c, int enabled, uint64_t now_ms); +void am_reconnect(am_client *c, uint64_t now_ms); +void am_close(am_client *c); +void am_tick(am_client *c, uint64_t now_ms); +/* Supported functions: 1,2,3,4,5,6,15,16. Zero-based protocol addresses. + * Writes copy input immediately. No automatic replay of a failed request. + * Completion flags persist until next accepted request; result is per-request. */ +am_error am_request(am_client *c, uint8_t unit, uint8_t function, + uint16_t address, uint16_t quantity, const uint16_t *values, uint64_t now_ms); +#endif diff --git a/tests/bridge_driver.c b/tests/bridge_driver.c new file mode 100644 index 0000000..da2349f --- /dev/null +++ b/tests/bridge_driver.c @@ -0,0 +1,29 @@ +#define _POSIX_C_SOURCE 200809L +#include "atom_bridge.h" +#include +#include +#include +static uint64_t now(void) { struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return (uint64_t)t.tv_sec*1000+(uint64_t)t.tv_nsec/1000000; } +int main(int argc,char **argv) { + uint32_t s[24],h=0; uint16_t data[2]={42,43}; unsigned ok=0,err=0; + int uncertain=0,last=0; uint64_t start=now(); struct timespec pause={0,1000000}; + if(argc!=4) return 2; + uint16_t port=(uint16_t)atoi(argv[1]); uint8_t f=(uint8_t)atoi(argv[2]); +#define CALL(cmd) ab_call(cmd,h,0x7f000001u,port,1,f,0,2,data,2,s,24,now(),100,80,20) + if(CALL(AB_OPEN)) return 2; + h=s[0]; if(CALL(AB_CONFIGURE) || CALL(AB_ENABLE)) return 2; + while(now()-start<1500 && ok<(unsigned)atoi(argv[3])) { + if(CALL(AB_TICK)) return 2; + if(s[3]) { + if(f<=4 && CALL(AB_COPY_READ)) return 2; + ok++; if(CALL(AB_ACK)) return 2; + } + if(s[4]) { err++; last=(int)s[5]; uncertain|=(int)s[9]; if(CALL(AB_ACK)) return 2; } + if(s[1]==3 && ok<(unsigned)atoi(argv[3])) { + if(CALL(AB_REQUEST_TIMEOUT) || CALL(AB_SUBMIT)) return 2; + } + nanosleep(&pause,0); + } + printf("%u %u %u %d %d %u %u\n",ok,err,s[11],uncertain,last,data[0],data[1]); + CALL(AB_CLOSE); return ok==(unsigned)atoi(argv[3])?0:1; +} diff --git a/tests/driver.c b/tests/driver.c new file mode 100644 index 0000000..92f55d7 --- /dev/null +++ b/tests/driver.c @@ -0,0 +1,24 @@ +#define _POSIX_C_SOURCE 200809L +#include "atom_mb.h" +#include +#include +#include +static uint64_t now(void) { struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return (uint64_t)t.tv_sec*1000+(uint64_t)t.tv_nsec/1000000; } +int main(int argc,char **argv) { + am_client c; uint64_t start=now(); unsigned successes=0,errors=0; int uncertain=0,last=0; + struct timespec pause={0,1000000}; uint16_t values[2]={42,43}; + if(argc!=4 || !am_init(&c,"127.0.0.1",(uint16_t)atoi(argv[1]))) return 2; + c.retry_delay_ms=20; c.response_timeout_ms=80; c.connect_timeout_ms=100; + am_enable(&c,1,start); + while(now()-start<1500 && successes<(unsigned)atoi(argv[3])) { + uint64_t t=now(); am_tick(&c,t); + if(c.done) { successes++; c.done=0; } + if(c.error) { errors++; last=c.result; uncertain|=c.write_uncertain; c.error=0; } + if(c.state==AM_READY && successes<(unsigned)atoi(argv[3])) { + int f=atoi(argv[2]); am_request(&c,1,(uint8_t)f,0,2,values,t); + } + nanosleep(&pause,0); + } + printf("%u %u %u %d %d %u %u\n",successes,errors,c.reconnects,uncertain,last,c.data[0],c.data[1]); + am_close(&c); return successes==(unsigned)atoi(argv[3])?0:1; +} diff --git a/tests/test_bridge.c b/tests/test_bridge.c new file mode 100644 index 0000000..45cc4aa --- /dev/null +++ b/tests/test_bridge.c @@ -0,0 +1,19 @@ +#include "atom_bridge.h" +#include +#include +#define CALL(cmd,h) ab_call(cmd,h,0x7f000001u,502,1,3,0,2,NULL,0,status,24,100,2000,1000,1000) +int main(void) { + uint32_t status[24],handles[16],old,newh; unsigned i; + assert(ab_call(AB_OPEN,0,0,502,0,0,0,0,NULL,0,status,23,0,0,0,0)==AB_BAD_CAPACITY); + for(i=0;i<16;i++) { assert(CALL(AB_OPEN,0)==0); handles[i]=status[0]; assert(handles[i]); } + assert(CALL(AB_OPEN,0)==AB_POOL_FULL); + old=handles[0]; assert(CALL(AB_CLOSE,old)==0); assert(CALL(AB_TICK,old)==AB_BAD_HANDLE); + assert(CALL(AB_OPEN,0)==0); newh=status[0]; assert(newh!=old); + assert(CALL(AB_CONFIGURE,newh)==0); assert(CALL(AB_ENABLE,newh)==0); + assert(CALL(AB_REQUEST_TIMEOUT,newh)==0); assert(status[22]==1000); + assert(CALL(AB_CONFIGURE,newh)==2); assert(CALL(AB_SUBMIT,newh)==3); + assert(CALL(AB_DISABLE,newh)==0); assert(CALL(AB_COPY_READ,newh)==1); + ab_shutdown(); assert(CALL(AB_TICK,newh)==AB_BAD_HANDLE); + puts("PASS bridge: capacity, pool limit, stale handles, configure, offline submission, shutdown"); + return 0; +} diff --git a/tests/test_tcp.py b/tests/test_tcp.py new file mode 100644 index 0000000..bcdcd36 --- /dev/null +++ b/tests/test_tcp.py @@ -0,0 +1,59 @@ +import socket, struct, subprocess, threading, time, sys +from pathlib import Path +ROOT=Path(__file__).resolve().parents[1] +DRIVER=ROOT/'tests'/(sys.argv[1] if len(sys.argv)>1 else 'driver') +def exact(s,n): + b=b'' + while len(b)H',h[4:6])[0]-1) + except (EOFError,ConnectionError,socket.timeout): break + if first and mode in ('close','timeout','bad_tid','bad_length','write_drop'): + first=False + if mode=='timeout': time.sleep(.12) + elif mode=='bad_tid': s.sendall(b'\xff\xff'+h[2:4]+b'\x00\x07'+h[6:7]+b'\x03\x04\x00\x2a\x00\x2b') + elif mode=='bad_length': s.sendall(h[:4]+b'\xff\xff'+h[6:7]) + break + if first and mode=='exception': reply=bytes([p[0]|128,2]); first=False + elif function==16: reply=p[:5] + elif function==1: reply=b'\x01\x01\x03' + else: reply=b'\x03\x04\x00\x2a\x00\x2b' + packet=h[:4]+struct.pack('>H',len(reply)+1)+h[6:7]+reply + if mode=='fragment': + for b in packet: s.sendall(bytes([b])); time.sleep(.001) + else: s.sendall(packet) + except Exception as e: issues.append(e) + worker=threading.Thread(target=server); worker.start() + try: + p=subprocess.run([str(DRIVER),str(listener.getsockname()[1]),str(function),'2'],capture_output=True,text=True,timeout=4) + assert p.returncode==0,(mode,p.stdout,p.stderr) + ok,err,reconnect,uncertain,last,a,b=map(int,p.stdout.split()) + assert ok==2 + if function==3: assert (a,b)==(42,43) + if function==1: assert (a,b)==(1,1) + if mode in ('close','timeout','bad_tid','bad_length','write_drop'): assert err>=1 and reconnect>=1 + if mode=='exception': assert err==1 and reconnect==0 and last==9 + if mode=='write_drop': assert uncertain==1 + print('PASS',mode,p.stdout.strip()) + finally: + listener.close(); worker.join(3) + assert not issues,issues +for mode in ('normal','fragment','close','timeout','bad_tid','bad_length','exception'): run(mode) +run('write_drop',16) +run('coils',1) diff --git a/tools/build-linux.sh b/tools/build-linux.sh new file mode 100755 index 0000000..f3745ea --- /dev/null +++ b/tools/build-linux.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail +root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +target="${1:?Usage: build-linux.sh linux-aarch64|linux-armhf|linux-x86_64}" +case "$target" in linux-aarch64|linux-armhf|linux-x86_64) ;; *) echo "Unsupported target label" >&2; exit 2;; esac +sdk="${SDKDIR:-$root/sdk/ExtensionSDK}" +compiler="${CC:-gcc}" +for tool in "$compiler" make m4 dos2unix python3 file; do command -v "$tool" >/dev/null; done +sdk="$(realpath "$sdk")" +make -C "$root/component" SDKDIR="$sdk" CC="$compiler" clean +make -C "$root/component" SDKDIR="$sdk" CC="$compiler" all +binary="$root/component/out/libCmpAtomModbus.so" +identity="$(file -b "$binary")" +case "$target" in + linux-aarch64) [[ "$identity" == *aarch64* ]] ;; + linux-armhf) [[ "$identity" == *"ELF 32-bit"* && "$identity" == *ARM* ]] ;; + linux-x86_64) [[ "$identity" == *x86-64* ]] ;; +esac || { echo "Binary architecture does not match $target: $identity" >&2; exit 3; } +destination="$root/dist/0.2.0/$target" +mkdir -p "$destination" +cp "$binary" "$destination/" +{ + date -u '+Build UTC: %Y-%m-%dT%H:%M:%SZ' + uname -a + "$compiler" --version + file "$binary" + if command -v getconf >/dev/null; then getconf GNU_LIBC_VERSION || true; fi + if command -v git >/dev/null; then git -C "$root" rev-parse HEAD 2>/dev/null || true; fi + printf 'SDK file hashes:\n' + find "$sdk" -type f -exec sha256sum {} + + printf 'Record tested runtime, target OS/libc and cross sysroot here before release.\n' +} > "$destination/build-info.txt" +sha256sum "$destination/libCmpAtomModbus.so" > "$destination/SHA256SUMS.txt" +printf 'Built: %s\n' "$destination/libCmpAtomModbus.so" diff --git a/tools/build_compat_export.py b/tools/build_compat_export.py new file mode 100644 index 0000000..6ef1d8c --- /dev/null +++ b/tools/build_compat_export.py @@ -0,0 +1,542 @@ +"""Patch original native CODESYS export; keep existing public declarations/IDs.""" +from pathlib import Path +import xml.etree.ElementTree as E +import copy, uuid +ROOT=Path(__file__).resolve().parents[1] +r=E.parse(ROOT/'compat/Original_HomeCodesys.export').getroot() +listing=r.find('.//List2[@Name="EntryList"]') +entries=list(listing) +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 key(e): + p=e.find('./Single[@Name="ParentSVNodeGuid"]') + return (names.get(p.text,''),meta(e,'Name').text) +by={key(e):e for e in entries} +def node(parent,name): return by[(parent,name)] +def textnode(e,part): return e.find('./Single[@Name="Object"]/Single[@Name="'+part+'"]//Single[@Name="TextBlobForSerialisation"]') +def get(parent,name,part='Implementation'): return textnode(node(parent,name),part).text or '' +def put(parent,name,body,part='Implementation'): textnode(node(parent,name),part).text=body + +def append_vars(parent,name,body,kind='VAR'): + put(parent,name,get(parent,name,'Interface')+'\n'+kind+'\n'+body+'\nEND_VAR\n','Interface') +def method(parent,name,decl,body): + e=copy.deepcopy(node('atMbClient','resetStats')) + old=meta(e,'Guid').text; new=str(uuid.uuid5(uuid.NAMESPACE_URL,'atom-modbus/'+parent+'/'+name)) + pg=meta(node('ModbusMaster',parent),'Guid').text + for v in e.iter(): + if v.text and old in v.text:v.text=v.text.replace(old,new) + meta(e,'Name').text=name; meta(e,'ParentGuid').text=pg + e.find('./Single[@Name="ParentSVNodeGuid"]').text=pg + path=e.find('./Array[@Name="Path"]'); path[-1].text=parent + textnode(e,'Interface').text=decl; textnode(e,'Implementation').text=body + listing.append(e); by[(parent,name)]=e + +append_vars('ModbusMaster','atMbClient','''nativeMode : BOOL; +nativeHandle : UDINT; +nativeIP : DWORD; +nativePort : UINT; +nativeNowUs : SYSTIME; +nativeNowMs : ULINT; +nativeStatus : ARRAY[0..23] OF UDINT; +nativeIO : ARRAY[0..1999] OF WORD; +nativeActiveRequest : POINTER TO atMbReqBase; +nativeActiveIndex : DINT := -1; +nativeSelected : POINTER TO atMbReqBase; +nativeResultReady : BOOL; +nativeFinishedIndex : DINT := -1; +nativeArgUnit : BYTE; +nativeArgFunction : BYTE; +nativeArgAddress : UINT; +nativeArgQuantity : UINT; +nativeArgTimeout : UDINT := 1000; +nativeCallResult : UDINT; +nativeSent : UDINT; +nativeReplied : UDINT; +nativeWindowStart : ULINT; +nativeWindowCount : UDINT; +nativeWindowTime : UDINT; +nativePrevConnected : BOOL; +nativeEnable : BOOL := TRUE; +nativeConnectTimeout : UDINT := 2000; +nativeRetryDelay : UDINT := 1000; +nativeHoldUncertainWrites : BOOL := FALSE;''') +# CODESYS permits another FB to assign inputs, but not local VAR members. +# Match the original stats pattern for the shared scheduler working fields. +e=node('ModbusMaster','atMbClient') +decl=textnode(e,'Interface').text +shared=['nativeActiveRequest','nativeIO','nativeArgUnit','nativeArgFunction', + 'nativeArgAddress','nativeArgQuantity','nativeArgTimeout','nativeSent', + 'nativeReplied','nativeWindowCount','nativeWindowTime'] +lines=decl.splitlines(); moved=[]; kept=[] +for line in lines: + if any(line.strip().startswith(n+' :') for n in shared): moved.append(line) + else: kept.append(line) +textnode(e,'Interface').text='\n'.join(kept)+'\nVAR_INPUT\n'+''.join( + "{attribute 'symbol' := 'none'}\n"+line+'\n' for line in moved)+'END_VAR\n' +put('atMbClient','setAllRequestsOffline', + get('atMbClient','setAllRequestsOffline','Interface').replace('METHOD PRIVATE','METHOD PROTECTED'), + 'Interface') +append_vars('ModbusMaster','atMbClient','''out_connected : BOOL; +out_connectionState : UDINT; +out_lastNativeError : UDINT; +out_socketError : UDINT; +out_reconnectCount : UDINT; +out_registrationFull : BOOL;''','VAR_OUTPUT') +method('atMbClient','nativeCall','''METHOD nativeCall : UDINT +VAR_INPUT + command : UDINT; +END_VAR''','''nativeCall := CmpAtomModbus.atMbCall_cext( + command := command, handle := nativeHandle, ipAddress := nativeIP, + port := nativePort, unitId := nativeArgUnit, functionCode := nativeArgFunction, + startAddress := nativeArgAddress, quantity := nativeArgQuantity, + pData := ADR(nativeIO[0]), dataCapacity := 2000, + pStatus := ADR(nativeStatus[0]), statusCapacity := 24, nowMs := nativeNowMs, + connectTimeoutMs := nativeConnectTimeout, responseTimeoutMs := nativeArgTimeout, + retryDelayMs := nativeRetryDelay);''') +method('atMbClient','nativeCycle','''METHOD nativeCycle +VAR + j : DINT; + best : DINT; + priority : INT; + age : UDINT; + rc : UDINT; +END_VAR''','''SysTimeGetUs(nativeNowUs); +nativeNowMs := nativeNowUs / 1000; +IF in_resetStats THEN + resetStats(); + in_resetStats := FALSE; +END_IF; +IF nativeHandle = 0 AND nativeEnable THEN + rc := nativeCall(1); + IF rc <> 0 THEN + out_lastNativeError := rc; + RETURN; + END_IF; + nativeHandle := nativeStatus[0]; + nativeArgTimeout := 1000; + rc := nativeCall(9); + IF rc <> 0 THEN + out_lastNativeError := rc; + nativeCall(2); nativeHandle := 0; + RETURN; + END_IF; + nativeCall(3); +END_IF; +IF nativeHandle = 0 THEN + out_connected := FALSE; + out_connectionState := 0; + RETURN; +END_IF; +rc := nativeCall(5); +IF rc = 256 THEN + nativeHandle := 0; + IF nativeActiveRequest <> 0 THEN + nativeActiveRequest^.nativeFinish(10); + END_IF; + nativeActiveRequest := 0; nativeActiveIndex := -1; + RETURN; +ELSIF rc <> 0 THEN + out_lastNativeError := rc; + RETURN; +END_IF; +out_connectionState := nativeStatus[1]; +out_connected := (nativeStatus[1] >= 3) AND (nativeStatus[1] <= 5); +out_lastNativeError := nativeStatus[6]; +out_socketError := nativeStatus[7]; +out_reconnectCount := nativeStatus[11]; +IF nativePrevConnected AND NOT out_connected THEN + setAllRequestsOffline(); +END_IF; +nativePrevConnected := out_connected; +nativeFinishedIndex := -1; +nativeResultReady := (nativeActiveRequest <> 0) AND + ((nativeStatus[3] <> 0) OR (nativeStatus[4] <> 0)); +IF nativeResultReady AND (nativeStatus[3] <> 0) AND (nativeStatus[18] <= 4) THEN + rc := nativeCall(7); + IF rc <> 0 THEN + nativeStatus[3] := 0; + nativeStatus[4] := 1; + nativeStatus[5] := rc; + END_IF; +END_IF; +processAllRequests(); +IF nativeResultReady THEN + nativeFinishedIndex := nativeActiveIndex; + nativeCall(10); + nativeActiveRequest := 0; nativeActiveIndex := -1; + nativeSelected := 0; nativeResultReady := FALSE; +END_IF; +IF nativeEnable AND nativeStatus[1] = 3 AND nativeActiveRequest = 0 THEN + best := -1; priority := 32767; age := 0; + FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[j] <> 0 AND_THEN requests[j]^.stats.active + AND NOT requests[j]^.nativeWriteHold AND j <> nativeFinishedIndex THEN + IF requests[j]^.stats.sinceLastExec >= TIME_TO_UDINT(requests[j]^.in_minDelay) THEN + IF (requests[j]^.in_priorityCurrent < priority) OR + ((requests[j]^.in_priorityCurrent = priority) AND + (requests[j]^.stats.sinceLastExec > age)) THEN + best := j; priority := requests[j]^.in_priorityCurrent; + age := requests[j]^.stats.sinceLastExec; + END_IF; + END_IF; + END_IF; + END_FOR; + IF best >= 0 THEN + IF priority > 0 THEN + FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[j]^.in_priorityCurrent > 0 THEN + requests[j]^.in_priorityCurrent := requests[j]^.in_priorityCurrent - 1; + END_IF; + END_FOR; + END_IF; + nativeSelected := requests[best]; nativeActiveIndex := best; + nativeSelected^.stats.execute := TRUE; + nativeSelected^.processRequest(); + IF nativeActiveRequest = 0 THEN + nativeActiveIndex := -1; nativeSelected := 0; + END_IF; + END_IF; +END_IF; +allRequestsOnline := (activeReqCnt > 0) AND (onlineCnt = activeReqCnt); +allRequestsOffline := (activeReqCnt > 0) AND (onlineCnt = 0); +numMsgSent := nativeSent; numMsgReply := nativeReplied; +FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + reqStats[j] := ADR(requests[j]^.stats); +END_FOR; +IF nativeWindowStart = 0 THEN nativeWindowStart := nativeNowMs; END_IF; +IF nativeNowMs - nativeWindowStart >= 1000 THEN + msgLastSecondCnt := nativeWindowCount; msgLastSecondTime := nativeWindowTime; + nativeWindowCount := 0; nativeWindowTime := 0; nativeWindowStart := nativeNowMs; +END_IF;''') +put('ModbusMaster','atMbClient','''IF nativeMode THEN + nativeCycle(); + RETURN; +END_IF; +'''+get('ModbusMaster','atMbClient')) +# RTU retains old code and public compatibility methods. +put('atMbClient','resetStats','''IF nativeMode THEN + nativeSent := 0; nativeReplied := 0; + nativeWindowCount := 0; nativeWindowTime := 0; + msgLastSecondCnt := 0; msgLastSecondTime := 0; + numMsgSent := 0; numMsgReply := 0; + FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[i]^.stats.resetStats(); + END_FOR; + RETURN; +END_IF; +'''+get('atMbClient','resetStats')) +put('atMbClient','addRequest','''IF request = 0 THEN RETURN; END_IF; +IF requestCnt > 0 THEN + FOR i := 0 TO requestCnt-1 DO + IF request = requests[i] THEN RETURN; END_IF; + END_FOR; +END_IF; +IF requestCnt >= 100 THEN + out_registrationFull := TRUE; + RETURN; +END_IF; +requests[requestCnt] := request; +requestCnt := requestCnt + 1;''') +put('activeIndex','Get','''IF nativeMode THEN + activeIndex := nativeActiveIndex; + RETURN; +END_IF; +'''+get('activeIndex','Get')) +# TCP declarations: existing inputs stay unchanged, extra diagnostics inherited. +append_vars('ModbusMaster','atMbClientTCP','''in_enable : BOOL := TRUE; +in_connectTimeout : TIME := T#2S; +in_reconnectDelay : TIME := T#1S; +in_holdUncertainWrites : BOOL := FALSE;''','VAR_INPUT') +append_vars('ModbusMaster','atMbClientTCP','''nativeEndpointIP : DWORD; +nativeEndpointPort : UINT; +nativeConfigConnect : UDINT; +nativeConfigRetry : UDINT;''') +put('ModbusMaster','atMbClientTCP','''nativeMode := TRUE; +nativeIP := SHL(BYTE_TO_DWORD(in_ipAddress[0]),24) OR + SHL(BYTE_TO_DWORD(in_ipAddress[1]),16) OR + SHL(BYTE_TO_DWORD(in_ipAddress[2]),8) OR BYTE_TO_DWORD(in_ipAddress[3]); +nativePort := in_port; +nativeConnectTimeout := MAX(UDINT#1,TIME_TO_UDINT(in_connectTimeout)); +nativeRetryDelay := MAX(UDINT#1,TIME_TO_UDINT(in_reconnectDelay)); +nativeHoldUncertainWrites := in_holdUncertainWrites; +IF nativeHandle <> 0 AND ((nativeEndpointIP <> nativeIP) OR + (nativeEndpointPort <> nativePort) OR (nativeConfigConnect <> nativeConnectTimeout) OR + (nativeConfigRetry <> nativeRetryDelay) OR NOT in_enable) THEN + nativeRelease(); +END_IF; +nativeEndpointIP := nativeIP; nativeEndpointPort := nativePort; +nativeConfigConnect := nativeConnectTimeout; nativeConfigRetry := nativeRetryDelay; +nativeEnable := in_enable; +IF in_baudrate <> 0 THEN dataRate := in_baudrate; ELSE dataRate := 105200; END_IF; +SUPER^();''') +put('atMbClientTCP','recreateClient','''nativeRelease(); +// Next scan creates a fresh native context with current endpoint settings.''') +# Remove the old local pointer used by the recreated built-in TCP client. +put('atMbClientTCP','recreateClient','METHOD recreateClient\nVAR\nEND_VAR','Interface') +method('atMbClientTCP','nativeRelease','''METHOD nativeRelease : BOOL +VAR + j : DINT; +END_VAR''','''IF nativeHandle <> 0 THEN + nativeCall(2); + IF nativeActiveRequest <> 0 THEN + nativeActiveRequest^.nativeFinish(10); + END_IF; + nativeHandle := 0; +END_IF; +nativeActiveRequest := 0; nativeSelected := 0; nativeActiveIndex := -1; +nativeResultReady := FALSE; +out_connected := FALSE; out_connectionState := 0; +setAllRequestsOffline(); +FOR j := 0 TO UINT_TO_DINT(requestCnt)-1 DO + requests[j]^.invalidateNativeData(); +END_FOR; +nativeRelease := TRUE;''') +method('atMbClientTCP','release','''METHOD release : BOOL''','''in_enable := FALSE; +release := nativeRelease();''') +method('atMbClientTCP','FB_exit','''METHOD FB_exit : BOOL +VAR_INPUT + bInCopyCode : BOOL; +END_VAR''','''FB_exit := nativeRelease();''') +method('atMbClientTCP','FB_reinit','''METHOD FB_reinit : BOOL +VAR + j : DINT; +END_VAR''','''nativeHandle := 0; +nativeActiveRequest := 0; nativeSelected := 0; nativeActiveIndex := -1; +nativeResultReady := FALSE; nativePrevConnected := FALSE; +requestCnt := 0; out_registrationFull := FALSE; +FOR j := 0 TO 99 DO requests[j] := 0; reqStats[j] := 0; END_FOR; +FB_reinit := TRUE;''') + +append_vars('ModbusMaster','atMbReqBase','''nativeFunction : BYTE; +nativeCount : UINT; +nativeOffset : UINT; +nativeInFlight : BOOL; +nativeReadComplete : BOOL; +nativeCopyAllowed : BOOL; +nativeWriteHold : BOOL; +nativeStartMs : ULINT; +nativeLoop : DINT; +nativeRC : UDINT;''') +append_vars('ModbusMaster','atMbReqBase','''out_nativeError : UDINT; +out_socketError : UDINT; +out_writeUncertain : BOOL; +out_validData : BOOL; +out_dataAgeMs : ULINT; +out_stale : BOOL := TRUE; +out_parameterError : BOOL;''','VAR_OUTPUT') +append_vars('ModbusMaster','atMbReqBase','''in_staleAfter : TIME := T#2S;''','VAR_INPUT') +method('atMbReqBase','invalidateNativeData','''METHOD PUBLIC invalidateNativeData''','''out_validData := FALSE; +out_stale := TRUE;''') +method('atMbReqBase','nativeFinish','''METHOD nativeFinish +VAR_INPUT + result : UDINT; +END_VAR +VAR + mapped : ModbusFB.Error; + ex : ModbusFB.ExceptionCodes; + duration : UDINT; +END_VAR''','''out_nativeError := result; +out_socketError := client.nativeStatus[7]; +out_writeUncertain := nativeInFlight AND (client.nativeStatus[9] <> 0); +IF (result = 10) AND nativeInFlight AND (nativeFunction >= 15) THEN + out_writeUncertain := TRUE; +END_IF; +IF out_writeUncertain AND client.nativeHoldUncertainWrites THEN + nativeWriteHold := TRUE; +END_IF; +stats.reqBusy := FALSE; +IF result = 0 THEN + duration := ULINT_TO_UDINT(client.nativeNowMs - nativeStartMs); + stats.lastValidDuration := duration; + IF duration > stats.longestValidDuration THEN stats.longestValidDuration := duration; END_IF; + client.nativeReplied := client.nativeReplied + 1; + client.nativeWindowTime := client.nativeWindowTime + duration; + stats.nativeResponseSeen := TRUE; + IF nativeFunction <= 4 THEN + nativeReadComplete := TRUE; + stats.nativeLastSuccessMs := client.nativeNowMs; + out_validData := TRUE; + END_IF; + done(TRUE); +ELSE + CASE result OF + 1,258: mapped := ModbusFB.Error.RequestParameterError; + 4,7: mapped := ModbusFB.Error.TcpSocketError; + 5: mapped := ModbusFB.Error.TcpConnectTimeout; + 6: mapped := ModbusFB.Error.ReplyTimeout; + 8: mapped := ModbusFB.Error.InvalidReply; + 9: mapped := ModbusFB.Error.RequestException; + 10: mapped := ModbusFB.Error.RequestCancelled; + ELSE mapped := ModbusFB.Error.InternalError; + END_CASE; + ex := ModbusFB.ExceptionCodes.RESPONSE_SUCCESS; + IF result = 9 THEN + CASE client.nativeStatus[8] OF + 1: ex := ModbusFB.ExceptionCodes.ILLEGAL_FUNCTION; + 2: ex := ModbusFB.ExceptionCodes.ILLEGAL_DATA_ADDRESS; + 3: ex := ModbusFB.ExceptionCodes.ILLEGAL_DATA_VALUE; + 4: ex := ModbusFB.ExceptionCodes.SLAVE_DEVICE_FAILURE; + 5: ex := ModbusFB.ExceptionCodes.ACKNOWLEDGE; + 6: ex := ModbusFB.ExceptionCodes.SLAVE_DEVICE_BUSY; + 8: ex := ModbusFB.ExceptionCodes.MEMORY_PARITY_ERROR; + 10: ex := ModbusFB.ExceptionCodes.GATEWAY_PATH_UNAVAILABLE; + 11: ex := ModbusFB.ExceptionCodes.GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND; + END_CASE; + client.nativeReplied := client.nativeReplied + 1; + stats.nativeResponseSeen := TRUE; + ELSE + stats.nativeResponseSeen := FALSE; + END_IF; + err(in := TRUE,errorId := mapped,exceptionCode := ex); +END_IF; +nativeInFlight := FALSE; +stats();''') +method('atMbReqBase','nativeProcess','''METHOD nativeProcess +VAR_INPUT + functionCode : BYTE; +END_VAR''','''nativeReadComplete := FALSE; +stats.reqDone := FALSE; stats.reqError := FALSE; +stats(); +IF NOT client.out_connected THEN stats.setClientOffline(); END_IF; +IF client.nativeActiveRequest = THIS AND client.nativeResultReady THEN + IF (client.nativeStatus[3] <> 0) AND (nativeFunction <= 4) AND NOT nativeCopyAllowed THEN + nativeFinish(1); + ELSE + nativeFinish(client.nativeStatus[5]); + END_IF; + RETURN; +END_IF; +IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS THEN + nativeFunction := functionCode; + nativeCount := stats.reqLength; nativeOffset := in_dataOffset; + nativeStartMs := client.nativeNowMs; + IF out_parameterError OR nativeCount = 0 THEN nativeFinish(1); RETURN; END_IF; + client.nativeArgUnit := in_unitId; + client.nativeArgFunction := functionCode; + client.nativeArgAddress := in_startAddr; + client.nativeArgQuantity := nativeCount; + client.nativeArgTimeout := MAX(UDINT#1,(replyTimeout + 999)/1000); + nativeRC := client.nativeCall(11); + IF nativeRC = 0 THEN nativeRC := client.nativeCall(6); END_IF; + IF nativeRC = 0 THEN + client.nativeActiveRequest := THIS; + nativeInFlight := TRUE; + stats.reqBusy := TRUE; + client.nativeSent := client.nativeSent + 1; + client.nativeWindowCount := client.nativeWindowCount + 1; + ELSE nativeFinish(nativeRC); + END_IF; + stats(); +END_IF; +IF out_validData THEN + out_dataAgeMs := client.nativeNowMs - stats.nativeLastSuccessMs; + out_stale := out_dataAgeMs > TIME_TO_UDINT(in_staleAfter); +ELSE out_stale := TRUE; +END_IF;''') +# Request body remains registration/clipping; fix remaining array capacity in native mode. +for name,f,bit,write in [('atMbReqReadCoil',1,True,False),('atMbReqReadDiscrete',2,True,False),('atMbReqReadHolding',3,False,False),('atMbReqReadInput',4,False,False),('atMbReqWriteCoil',15,True,True),('atMbReqWriteHolding',16,False,True)]: + original=get('ModbusMaster',name) + put('ModbusMaster',name,'''IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + out_parameterError := (UINT_TO_DINT(in_dataOffset) < LOWER_BOUND(data,1)) OR + (UINT_TO_DINT(in_dataOffset) > UPPER_BOUND(data,1)); + IF out_parameterError THEN dataSize := 0; + ELSE dataSize := DINT_TO_UINT(UPPER_BOUND(data,1) - UINT_TO_DINT(in_dataOffset) + 1); + END_IF; + SUPER^(); + RETURN; +END_IF; +'''+original) + native='''IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + nativeCopyAllowed := (UINT_TO_DINT(nativeOffset) >= LOWER_BOUND(data,1)) AND + (UINT_TO_DINT(nativeOffset) + UINT_TO_DINT(nativeCount) - 1 <= UPPER_BOUND(data,1)); +''' + if write: + native+=''' IF stats.execute AND NOT nativeInFlight AND client.nativeSelected = THIS AND NOT out_parameterError THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(stats.reqLength)-1 DO +''' + native+=(' client.nativeIO[nativeLoop] := BOOL_TO_WORD(data[UINT_TO_DINT(in_dataOffset)+nativeLoop]);\n' if bit else ' client.nativeIO[nativeLoop] := UINT_TO_WORD(data[UINT_TO_DINT(in_dataOffset)+nativeLoop]);\n') + native+=' END_FOR;\n END_IF;\n' + native+=f' nativeProcess({f});\n' + if not write: + native+=''' IF nativeReadComplete AND nativeCopyAllowed THEN + FOR nativeLoop := 0 TO UINT_TO_DINT(nativeCount)-1 DO +''' + native+=(' data[UINT_TO_DINT(nativeOffset)+nativeLoop] := client.nativeIO[nativeLoop] <> 0;\n' if bit else ' data[UINT_TO_DINT(nativeOffset)+nativeLoop] := WORD_TO_UINT(client.nativeIO[nativeLoop]);\n') + native+=' END_FOR;\n END_IF;\n' + native+=' RETURN;\nEND_IF;\n' + put(name,'processRequest',native+get(name,'processRequest')) + put(name,'recreateRequest','''IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + IF client.nativeActiveRequest = THIS THEN client.recreateClient(); END_IF; + nativeInFlight := FALSE; + recreateRequest := 0; + RETURN; +END_IF; +'''+get(name,'recreateRequest')) + if write:put(name,'writeNow','''nativeWriteHold := FALSE; +out_writeUncertain := FALSE; +'''+get(name,'writeNow')) +method('atMbReqBase','FB_reinit','''METHOD FB_reinit : BOOL''','''nativeInFlight := FALSE; +stats.execute := FALSE; +stats.reqBusy := FALSE; +stats.reqDone := FALSE; +stats.reqError := FALSE; +FB_reinit := TRUE;''') +put('atMbClient','setupClient','''IF nativeMode THEN + setupClient := nativeHandle <> 0; + RETURN; +END_IF; +'''+get('atMbClient','setupClient')) +put('atMbReqBase','setupClient','''IF __ISVALIDREF(client) AND_THEN client.nativeMode THEN + setupClient := FALSE; // Native path has no ModbusFB request object. + RETURN; +END_IF; +'''+get('atMbReqBase','setupClient')) +# Stats type names remain unchanged; add native health/freshness fields. +append_vars('ModbusMaster','atMbRequestStats','''nativeResponseSeen : BOOL; +nativeLastSuccessMs : ULINT;''','VAR_INPUT') +s=get('ModbusMaster','atMbRequestStats') +s=s.replace('IF doneCntRst >=3 THEN','IF nativeResponseSeen OR doneCntRst >=3 THEN') +s=s.replace('IF errorCntRst >= 3 THEN','IF NOT nativeResponseSeen AND errorCntRst >= 3 THEN') +a=s.index('IF lastReqStart < TSSus - 60000000 THEN');b=s.index('sinceLastExec :=',a) +s=s[:a]+'''IF TSSus >= lastReqStart THEN + IF TSSus - lastReqStart > 60000000 THEN lastReqStart := TSSus - 60000000; END_IF; +ELSE lastReqStart := TSSus; +END_IF; +'''+s[b:] +put('ModbusMaster','atMbRequestStats',s) +put('atMbRequestStats','setClientOffline','nativeResponseSeen := FALSE;\n'+get('atMbRequestStats','setClientOffline')) +# Dispatch may run before any request FB has registered on the first scan. +for name, call in [('processAllRequests', 'processRequest()'), + ('recreateAllRequests', 'recreateRequest()'), + ('setAllRequestsOffline', 'stats.setClientOffline()')]: + decl = get('atMbClient', name, 'Interface') + if 'i:' not in decl and 'i :' not in decl: + decl += '\nVAR\n i : DINT;\nEND_VAR\n' + decl = decl.replace('i: UINT;', 'i: DINT;') + put('atMbClient', name, decl, 'Interface') + put('atMbClient', name, f'''IF requestCnt = 0 OR requestCnt > 100 THEN RETURN; END_IF; +FOR i := 0 TO UINT_TO_DINT(requestCnt)-1 DO + IF requests[i] <> 0 THEN + requests[i]^.{call}; + END_IF; +END_FOR;''') +# Keep subtraction signed in every other registry consumer too. +import re +for e in listing: + t = textnode(e, 'Implementation') + if t is not None and t.text and 'requestCnt' in t.text: + t.text = re.sub(r'(?'+lookup['atmbcall_cext']+' = ab_call(\n ' + body+=',\n '.join(param+'->'+lookup[n] for n in args)+');\n}' + c=c[:start]+body+c[end:] + # Use one component C translation unit because the supplied SDK derives + # component identity from the top-level C filename list. + c='#include "../src/atom_bridge.h"\n'+c + c+='\n#include "../src/atom_mb.c"\n#include "../src/atom_bridge.c"\n' + frame=(sdk/'src/CmpFrame.c').read_text() + hook=re.search(r'(static\s+RTS_RESULT\s+CDECL\s+HookFunction\([^)]*\)\s*\{)',frame) + if not hook: raise ValueError('Unexpected SDK HookFunction; inspect manually') + frame=frame[:hook.end()]+'\n if (ulHook == CH_EXIT) ab_shutdown();\n'+frame[hook.end():] + frame='#include "../src/atom_bridge.h"\n'+frame + destination.mkdir(parents=True,exist_ok=True) + if any(destination.iterdir()): raise ValueError('Destination must be empty; preserve existing build work') + (destination/'CmpAtomModbus.c').write_text(c) + (destination/interface.name).write_text(m4) + (destination/'CmpFrame.c').write_text(frame) + (destination/'makefile').write_text('''# Generated from supplied ExtensionSDK; native build on ARM64 Pi. +SDKDIR ?= ../../ExtensionSDK +MAJORVER=0 +MINORVER=2 +CMPVERSION=0x00020000 +# Local development ID; check no other component uses this ID. +CMPID=0x2001 +DOS2UNIX=dos2unix +M4=m4 +CC ?= gcc +# The SDK otherwise includes CmpFrame in its component identity wildcard. +override CSRC := CmpAtomModbus.c +include $(SDKDIR)/makefile +''') + +if __name__=='__main__': + p=argparse.ArgumentParser(description=__doc__) + p.add_argument('source',type=Path); p.add_argument('interface',type=Path) + p.add_argument('--sdk',required=True,type=Path) + p.add_argument('--out',type=Path,default=Path('component')) + a=p.parse_args() + prepare(a.source,a.interface,a.sdk,a.out) + print('Prepared component; compile and validate the generated interface on target.')