pyads-agile
pyads-agile is a Python wrapper for the Beckhoff TwinCAT ADS library.
This distribution is maintained by Agile Automation Technologies GmbH and is
based on the excellent upstream pyads project created by Stefan Lehmann:
https://github.com/stlehmann/pyads
pyads-agile intentionally stays drop-in compatible with pyads. The public API,
module name (import pyads), and supported interpreter/OS matrix mirror upstream,
so existing applications can switch distributions without code changes.
Current validated support target is Python 3.13 (CI runs on 3.13).
Attribution
- Original project:
pyadsby Stefan Lehmann - Fork maintainer: Filippo Boido filippo.boido@agileautomation.eu (Agile Automation Technologies GmbH)
- License: MIT
- This repository keeps upstream credit and license notices as required
See ACKNOWLEDGMENTS.md for details.
Installation
Install the distribution:
pip install pyads-agile
Import stays compatible:
import pyads
Versioning
pyads-agile uses its own independent Semantic Versioning (MAJOR.MINOR.PATCH).
It does not mirror upstream pyads version numbers.
Scope
This package provides Python APIs for communicating with TwinCAT devices using:
TcAdsDll.dllon Windowsadslib.soon Linux
Agile-specific enhancements
Beyond compatibility, this fork currently focuses on improved RPC ergonomics:
-
Convenient RPC object proxies.
Connection.get_object()exposes TwinCAT function blocks as Python objects and lets you configure return and parameter types per method:TwinCAT requirement: each callable method must be annotated in PLC code with
{attribute 'TcRpcEnable'}directly above the method declaration.rpc = plc.get_object( "GVL.fbTestRemoteMethodCall", method_return_types={"m_iSimpleCall": pyads.PLCTYPE_INT}, ) result = rpc.m_iSimpleCall()
-
Multi-parameter RPC calls with native syntax. Configure method signatures once and then call methods like normal Python methods:
rpc = plc.get_object( "GVL.fbTestRemoteMethodCall", method_return_types={"m_iSum": pyads.PLCTYPE_INT}, method_parameters={"m_iSum": [pyads.PLCTYPE_INT, pyads.PLCTYPE_INT]}, ) result = rpc.m_iSum(5, 5)
-
Typed RPC interfaces for IntelliSense. Decorate a Python class with
@pyads.ads_path("GVL.fbTestRemoteMethodCall"), annotate method arguments and return types with TwinCAT PLC types, and pass the class intoConnection.get_object. The returned proxy is typed as your class so IDEs can offer completions:@pyads.ads_path("GVL.fbTestRemoteMethodCall") class FB_TestRemoteMethodCall: def m_iSum( self, a: pyads.PLCTYPE_INT, b: pyads.PLCTYPE_INT, ) -> pyads.PLCTYPE_INT: ... rpc = plc.get_object(FB_TestRemoteMethodCall) result = rpc.m_iSum(5, 5)
You can still use low-level direct calls when needed:
result = plc.call_rpc_method( "GVL.fbTestRemoteMethodCall#m_iSimpleCall", return_type=pyads.PLCTYPE_INT, write_value=42, write_type=pyads.PLCTYPE_INT, )
-
Serialized async ADS runtime.
AsyncConnectionexecutes all ADS calls on a dedicated worker thread per connection (in-order, race-safe on connection state), and exposes awaitable helpers and submit-style futures:import asyncio import pyads async def main() -> None: async with pyads.AsyncConnection("127.0.0.1.1.1", pyads.PORT_TC3PLC1) as plc: fut = plc.submit_sum_read(["GVL.int_val", "GVL.bool_val"]) # ... do other work values = await fut await plc.sum_write({"GVL.int_val": int(values["GVL.int_val"]) + 1}) asyncio.run(main())
-
Runtime discovery and notification streams. Version 0.4 exposes immutable symbol/datatype metadata and a cleanup-safe async iterator for online values.
route_policy="existing_only"tells pyads-agile to use the TwinCAT router's current route without creating or deleting one:import asyncio import pyads async def inspect_runtime() -> None: async with pyads.AsyncConnection( "127.0.0.1.1.1", pyads.PORT_TC3PLC1, route_policy="existing_only", ) as plc: upload = await plc.get_upload_info() symbols = await plc.get_all_symbol_info() data_types = await plc.get_all_data_types() print(upload, len(symbols), len(data_types)) async with plc.subscribe( "MAIN.fbConveyor.eState", pyads.PLCTYPE_DINT, cycle_time=0.1, # seconds; length is inferred ) as changes: sample = await anext(changes) print(sample.value, sample.timestamp) asyncio.run(inspect_runtime())
Synchronous callers can use
get_upload_info(),get_symbol_info(),get_all_symbol_info(),get_all_data_types(), andget_symbol_version()onConnection. Upload-info v3 retains the target code page, pointer width, base-type and UTF-8 flags; uploaded names and comments are decoded with that advertised code page. Callclear_symbol_cache()before a manual generation refresh (the complete symbol upload also invalidates it automatically).read_list_by_name_detailed()/write_list_by_name_detailed()and their asyncsum_*_detailed()counterparts retain the ADS result for every item, which lets diagnostic clients distinguish unavailable symbols from transport or datatype failures. Passsymbol_metadata={name: AdsSymbolInfo(...)}to a detailed sum read or write to dispatch against an already approved immutable layout. The complete mapping is validated before any subcommand and bypasses symbol lookup and cache state. Detailed reads additionally acceptraw_data_namesfor values that must remain copied bytes for a metadata-driven caller codec. Zero-size and exported bit-value symbols are reported as unsupported instead of being treated as byte-aligned values. Useget_symbol_version()to bind approval to TwinCAT's symbol-table generation and recheck it immediately before dispatch.subscribe(..., cycle_time=..., max_delay=...)accepts seconds and infers the notification length. A caller-suppliedNotificationAttribremains available for advanced modes and cannot be combined with these timing shortcuts. -
Async wrappers for the synchronous pyads Connection API.
AsyncConnectionnow mirrors the core synchronous read/write surface while keeping single-threaded serialized execution under the hood. For most methods you get both:submit_*returningasyncio.Futureasyncmethod variant that awaits the same operation
Covered wrappers include:
read,write,read_writeread_by_name,write_by_nameread_structure_by_name,write_structure_by_nameread_state,read_device_info,write_controlget_local_address,get_handle,release_handle,set_timeoutsum_read/sum_write(submit_sum_read/submit_sum_write)
import asyncio import pyads async def main() -> None: async with pyads.AsyncConnection("127.0.0.1.1.1", pyads.PORT_TC3PLC1) as plc: # Await-style value = await plc.read_by_name("GVL.int_val", pyads.PLCTYPE_INT) await plc.write_by_name("GVL.int_val", value + 1, pyads.PLCTYPE_INT) # Submit-style fut = plc.submit_read_state() state = await fut print(state) asyncio.run(main())
-
Async typed RPC objects. Use
@pyads.ads_async_path(...)withAsyncConnection.get_async_object(...)for type-safe async RPC interfaces. Method calls returnasyncio.Futureobjects:@pyads.ads_async_path("GVL.fbTestRemoteMethodCall") class FB_TestRemoteMethodCall: def m_iSum( self, a: pyads.PLCTYPE_INT, b: pyads.PLCTYPE_INT, ) -> asyncio.Future[pyads.PLCTYPE_INT]: ... async def main(plc: pyads.AsyncConnection) -> None: rpc = plc.get_async_object(FB_TestRemoteMethodCall) future = rpc.m_iSum(5, 5) result = await future print(result)
-
Native stepchain async RPC interfaces. Use
@pyads.ads_async_path(...)on the interface, inherit frompyads.StepChainRpcInterface, and mark stepchain entry methods with@pyads.stepchain_start. Calls return aStepChainOperationcontaining:accepted: RPC-return phasedone: completion phase based on PLC status fieldsawait op: completion snapshot with the latest ADS status symbol values The generic parameter onStepChainOperation[...]describes the ADS transport return type for the accepted phase.
@pyads.ads_async_path("GVL.fbTestRemoteStepChainMethodCall") class FB_TestRemoteStepChainMethodCall(pyads.StepChainRpcInterface): __stepchain_completion__ = "poll" # or "notify" @pyads.stepchain_start def m_xStartStepChain( self, udiRequestId: pyads.PLCTYPE_UDINT, ) -> pyads.StepChainOperation[pyads.PLCTYPE_BOOL]: ... async def run_stepchain(plc: pyads.AsyncConnection) -> None: rpc = plc.get_async_object(FB_TestRemoteStepChainMethodCall) status_root = rpc.status_symbol() # udiRequestId is auto-generated if omitted. op = rpc.m_xStartStepChain() accepted = await op.accepted if not accepted: raise RuntimeError("Stepchain start rejected by PLC.") # Wait until status reports completion or error and capture snapshot. completion_snapshot = await op request_id_symbol = f"{status_root}.udiRequestId" print("Completed request", completion_snapshot[request_id_symbol]) # Built-in framework status read (predefined structure) status = await rpc.read_status() print(status["udiStep"], status["sStepName"])
Completion backend options:
completion="poll": periodicsum_readchecks (poll_interval/timeout_s)completion="notify": ADS notifications trigger status reads in asyncio
Built-in predefined stepchain status fields:
udiRequestId,xBusy,xDone,xError,diErrorCode,udiStep,sStepName
Repository references:
- TwinCAT PLC project archive:
examples/twincat_reference/project/Test_PyAdsAgile.tpzip - TwinCAT reference source:
examples/twincat_reference/pyads_agile_reference.st - Real test PLC template:
tests/integration_real/plc_symbols_template.st - Detailed stepchain guide:
doc/documentation/stepchain.rst
Features
- connect to remote TwinCAT devices
- create routes on Linux and on remote PLCs
- support for TwinCAT 2 and TwinCAT 3
- read and write values by name or by address
- read and write DUTs (structures)
- notification callbacks
- immutable upload, symbol, and datatype discovery metadata
- cleanup-safe async notification streams
- structured per-item sum-read and sum-write results
- typed RPC interfaces via
@pyads.ads_path(...) - async typed RPC interfaces via
@pyads.ads_async_path(...) - serialized asyncio runtime via
pyads.AsyncConnection - async wrappers for core sync ADS methods (
submit_*+ awaitable variants) - async typed RPC proxies via
get_async_object(...) - native stepchain async RPC flow via
StepChainRpcInterface+@pyads.stepchain_start - stepchain completion backends: polling (
poll) and notification-driven (notify)
Basic usage
import pyads
plc = pyads.Connection("127.0.0.1.1.1", pyads.PORT_TC3PLC1)
plc.open()
i = plc.read_by_name("GVL.int_val")
plc.write_by_name("GVL.int_val", i)
plc.close()
Contribution Policy
This repository is maintained on a best-effort basis for internal and product needs.
At this time, we do not accept unsolicited pull requests, and we may not be able to respond to feature requests or general support issues.
Release files for pyads-agile 0.4.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyads_agile-0.4.2.tar.gz | 324.2 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| pyads_agile-0.4.2-py3-none-win_arm64.whl | Python 3 | none | Windows ARM64 | Details |
| pyads_agile-0.4.2-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| pyads_agile-0.4.2-py3-none-win32.whl | Python 3 | none | Windows x86-32 | Details |
| pyads_agile-0.4.2-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl | Python 3 | none | Linux glibc 2.28+ x86-64, Linux glibc 2.24+ x86-64 | Details |
| pyads_agile-0.4.2-py3-none-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl | Python 3 | none | Linux glibc 2.28+ ARM64, Linux glibc 2.24+ ARM64 | Details |
| pyads_agile-0.4.2-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
| pyads_agile-0.4.2-py3-none-macosx_10_15_x86_64.whl | Python 3 | none | macOS 10.15+ x86-64 | Details |
Total release size: 1.6 MB
Release files / pyads_agile-0.4.2.tar.gz
| Download URL | pyads_agile-0.4.2.tar.gz |
|---|---|
| Size | 324.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8cd40ea1d22d79f6eebc8e527586b0821a8aa637b08114d0cbbba674da9ce866
|
|
BLAKE2b-256 checksum How to use checksums |
21559d212d63c6b9e938b52355c0f8e4c02677e50ccd7e8316fd769daf0c788f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logRelease files / pyads_agile-0.4.2-py3-none-win_arm64.whl
| Download URL | pyads_agile-0.4.2-py3-none-win_arm64.whl |
|---|---|
| Size | 115.6 kB |
| Tags | Python 3 Windows ARM64 |
|
SHA-256 checksum How to use checksums |
1d65787521835eaa5af92d915ea6aa396e661954397801a594927d6faa884b46
|
|
BLAKE2b-256 checksum How to use checksums |
fa3c572679e9f75f8a05ced838223d56c80cb1afc16397c575c5b1a3c082db0e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logRelease files / pyads_agile-0.4.2-py3-none-win_amd64.whl
| Download URL | pyads_agile-0.4.2-py3-none-win_amd64.whl |
|---|---|
| Size | 115.6 kB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
22ced0438d64ff31c4ab49c5fb9a7d2d2fc96680abf78c4f706e512361b70c33
|
|
BLAKE2b-256 checksum How to use checksums |
602cb5c5475ad88f989a0e3e371bd9ad8c35336c4f86fc9c9d8fc842bdcc8def
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logRelease files / pyads_agile-0.4.2-py3-none-win32.whl
| Download URL | pyads_agile-0.4.2-py3-none-win32.whl |
|---|---|
| Size | 115.6 kB |
| Tags | Python 3 Windows x86-32 |
|
SHA-256 checksum How to use checksums |
2dbcda2fd8f5224bb972cf366d87669a5b33325916cae44a758fcfaffd6f0519
|
|
BLAKE2b-256 checksum How to use checksums |
ca0b7dca649409869c166ed569b2a0a650c8da8157b9b49dd62acc2d8ccc125c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logRelease files / pyads_agile-0.4.2-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
| Download URL | pyads_agile-0.4.2-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 280.0 kB |
| Tags | Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
c41b6896c29c0b1c65bbd2d3abe31acff6128f3425f2c99cd582eb777ccb7af3
|
|
BLAKE2b-256 checksum How to use checksums |
8a2f1cdac94e0ebbdead9e89e47a0c57b744b2296b324fd7122a9587d6f5684c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logRelease files / pyads_agile-0.4.2-py3-none-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
| Download URL | pyads_agile-0.4.2-py3-none-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl |
|---|---|
| Size | 258.3 kB |
| Tags | Linux glibc 2.24+ ARM64 Linux glibc 2.28+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
6c900a6904ceb4d4030c63c13878efa884a7c8d64f9da06ae4f4f7dcaded23e5
|
|
BLAKE2b-256 checksum How to use checksums |
cf17e6bdcdaac00f4a54067640d56595c2808854cd3e37b07ac32b3803813200
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logRelease files / pyads_agile-0.4.2-py3-none-macosx_11_0_arm64.whl
| Download URL | pyads_agile-0.4.2-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 208.2 kB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
564eb0b2514d0086f183b8c324f53a14b93155607e6ce3ff57c96e032265373c
|
|
BLAKE2b-256 checksum How to use checksums |
8753daacb54588ed629a55bdc0f85409e8ec2db289d2554f71b92feaaa1c49de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency logRelease files / pyads_agile-0.4.2-py3-none-macosx_10_15_x86_64.whl
| Download URL | pyads_agile-0.4.2-py3-none-macosx_10_15_x86_64.whl |
|---|---|
| Size | 216.3 kB |
| Tags | Python 3 macOS 10.15+ x86-64 |
|
SHA-256 checksum How to use checksums |
1a8d8f885cc4acadaf678cc6f7948e1e9cf5b4f86e6742bfb7e2330c82a38fd3
|
|
BLAKE2b-256 checksum How to use checksums |
109b7cdf8e617a4157a7c051f717bfc7eaf169383db71f2a4d77c8d8025c279c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 31, 2026.
Transparency log