aioads 0.1.0.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aioads-0.1.0.dev1/.gitignore +162 -0
- aioads-0.1.0.dev1/.vscode/settings.json +14 -0
- aioads-0.1.0.dev1/PKG-INFO +302 -0
- aioads-0.1.0.dev1/README.md +292 -0
- aioads-0.1.0.dev1/aioads/ads_client.py +333 -0
- aioads-0.1.0.dev1/aioads/ads_error_codes.py +160 -0
- aioads-0.1.0.dev1/aioads/ads_notifications.py +203 -0
- aioads-0.1.0.dev1/aioads/ads_symbol_cache.py +186 -0
- aioads-0.1.0.dev1/aioads/ads_symbol_parser.py +271 -0
- aioads-0.1.0.dev1/aioads/ams_address.py +53 -0
- aioads-0.1.0.dev1/aioads/ams_header.py +94 -0
- aioads-0.1.0.dev1/aioads/ams_tcp_header.py +54 -0
- aioads-0.1.0.dev1/aioads/commands/ads_add_notification.py +137 -0
- aioads-0.1.0.dev1/aioads/commands/ads_command.py +56 -0
- aioads-0.1.0.dev1/aioads/commands/ads_delete_notification.py +57 -0
- aioads-0.1.0.dev1/aioads/commands/ads_read.py +125 -0
- aioads-0.1.0.dev1/aioads/commands/ads_read_device_info.py +115 -0
- aioads-0.1.0.dev1/aioads/commands/ads_read_state.py +128 -0
- aioads-0.1.0.dev1/aioads/commands/ads_read_write.py +92 -0
- aioads-0.1.0.dev1/aioads/commands/ads_write.py +122 -0
- aioads-0.1.0.dev1/aioads/commands/ads_write_state.py +74 -0
- aioads-0.1.0.dev1/aioads/commands/errors.py +30 -0
- aioads-0.1.0.dev1/aioads/errors.py +32 -0
- aioads-0.1.0.dev1/aioads/functions/ads_enable_route.py +99 -0
- aioads-0.1.0.dev1/aioads/functions/ads_function.py +49 -0
- aioads-0.1.0.dev1/aioads/functions/ads_sum_read.py +79 -0
- aioads-0.1.0.dev1/aioads/functions/ads_sum_read_write.py +131 -0
- aioads-0.1.0.dev1/aioads/functions/ads_symbol_datatype_by_name.py +224 -0
- aioads-0.1.0.dev1/aioads/functions/ads_symbol_datatype_upload.py +56 -0
- aioads-0.1.0.dev1/aioads/functions/ads_symbol_info_by_name_ex.py +219 -0
- aioads-0.1.0.dev1/aioads/functions/ads_symbol_table_version.py +49 -0
- aioads-0.1.0.dev1/aioads/functions/ads_symbol_upload.py +51 -0
- aioads-0.1.0.dev1/aioads/functions/ads_symbol_upload_info.py +89 -0
- aioads-0.1.0.dev1/aioads/stream.py +90 -0
- aioads-0.1.0.dev1/aioads/transport.py +561 -0
- aioads-0.1.0.dev1/aioads/utils/local_ip.py +26 -0
- aioads-0.1.0.dev1/docs/secure_ads.md +489 -0
- aioads-0.1.0.dev1/docs/transmission_mode.md +63 -0
- aioads-0.1.0.dev1/examples/read_cmd_reuse_mqtt.py +66 -0
- aioads-0.1.0.dev1/examples/read_cycles.py +39 -0
- aioads-0.1.0.dev1/examples/read_cycles_mqtt.py +44 -0
- aioads-0.1.0.dev1/examples/read_multiple.py +70 -0
- aioads-0.1.0.dev1/examples/read_multiple_mqtt.py +74 -0
- aioads-0.1.0.dev1/examples/read_single.py +53 -0
- aioads-0.1.0.dev1/pdm.lock +334 -0
- aioads-0.1.0.dev1/pyproject.toml +72 -0
- aioads-0.1.0.dev1/tests/__init__.py +0 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# pdm
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
106
|
+
#pdm.lock
|
|
107
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
108
|
+
# in version control.
|
|
109
|
+
# https://pdm-project.org/#use-with-ide
|
|
110
|
+
.pdm.toml
|
|
111
|
+
.pdm-python
|
|
112
|
+
.pdm-build/
|
|
113
|
+
|
|
114
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
115
|
+
__pypackages__/
|
|
116
|
+
|
|
117
|
+
# Celery stuff
|
|
118
|
+
celerybeat-schedule
|
|
119
|
+
celerybeat.pid
|
|
120
|
+
|
|
121
|
+
# SageMath parsed files
|
|
122
|
+
*.sage.py
|
|
123
|
+
|
|
124
|
+
# Environments
|
|
125
|
+
.env
|
|
126
|
+
.venv
|
|
127
|
+
env/
|
|
128
|
+
venv/
|
|
129
|
+
ENV/
|
|
130
|
+
env.bak/
|
|
131
|
+
venv.bak/
|
|
132
|
+
|
|
133
|
+
# Spyder project settings
|
|
134
|
+
.spyderproject
|
|
135
|
+
.spyproject
|
|
136
|
+
|
|
137
|
+
# Rope project settings
|
|
138
|
+
.ropeproject
|
|
139
|
+
|
|
140
|
+
# mkdocs documentation
|
|
141
|
+
/site
|
|
142
|
+
|
|
143
|
+
# mypy
|
|
144
|
+
.mypy_cache/
|
|
145
|
+
.dmypy.json
|
|
146
|
+
dmypy.json
|
|
147
|
+
|
|
148
|
+
# Pyre type checker
|
|
149
|
+
.pyre/
|
|
150
|
+
|
|
151
|
+
# pytype static type analyzer
|
|
152
|
+
.pytype/
|
|
153
|
+
|
|
154
|
+
# Cython debug symbols
|
|
155
|
+
cython_debug/
|
|
156
|
+
|
|
157
|
+
# PyCharm
|
|
158
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
159
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
160
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
161
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
162
|
+
#.idea/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cSpell.words": [
|
|
3
|
+
"aioads",
|
|
4
|
+
],
|
|
5
|
+
"terminal.integrated.env.osx": {
|
|
6
|
+
"PYTHONPATH": "${workspaceFolder}"
|
|
7
|
+
},
|
|
8
|
+
"terminal.integrated.env.linux": {
|
|
9
|
+
"PYTHONPATH": "${workspaceFolder}"
|
|
10
|
+
},
|
|
11
|
+
"terminal.integrated.env.windows": {
|
|
12
|
+
"PYTHONPATH": "${workspaceFolder}"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aioads
|
|
3
|
+
Version: 0.1.0.dev1
|
|
4
|
+
Summary: An asynchronous Python library for communicating with Beckhoff TwinCAT PLCs
|
|
5
|
+
Author-email: MkKiefer <102972583+MkKiefer@users.noreply.github.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.12
|
|
8
|
+
Requires-Dist: aiomqtt>=v3.0.0-alpha.1
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# AIOADS - Asynchronous ADS Library for Python
|
|
12
|
+
|
|
13
|
+
An asynchronous Python library for communicating with Beckhoff TwinCAT PLCs using the ADS (Automation Device Specification) protocol. This library provides high-performance, async/await-based communication with PLCs for reading symbols, data types, notifications, and more.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- ✨ **Fully asynchronous** - Built from the ground up with asyncio
|
|
18
|
+
- 🚀 **High performance** - Efficient batch operations and smart caching
|
|
19
|
+
- 📡 **ADS over TCP** - Direct communication with TwinCAT systems
|
|
20
|
+
- 🔍 **Symbol resolution** - Automatic symbol table parsing and caching
|
|
21
|
+
- 📊 **Data type support** - Complete TwinCAT data type parsing
|
|
22
|
+
- 🔔 **Notifications** - Event-driven data change monitoring
|
|
23
|
+
- 🛠️ **Type safety** - Full typing support for better development experience
|
|
24
|
+
- ⚡ **Smart caching** - Intelligent symbol and data type caching for performance
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pdm add aioads
|
|
30
|
+
|
|
31
|
+
pip install aioads
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import asyncio
|
|
38
|
+
from aioads import AdsClient, AmsAddress
|
|
39
|
+
|
|
40
|
+
async def main():
|
|
41
|
+
# Create client
|
|
42
|
+
client = AdsClient.create_tcp(
|
|
43
|
+
src=AmsAddress(net_id="192.168.1.100.1.1", port=1234),
|
|
44
|
+
dst=AmsAddress(net_id="192.168.1.200.1.1", port=851),
|
|
45
|
+
ip="192.168.1.200",
|
|
46
|
+
port=48898,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
# Connect to PLC
|
|
51
|
+
await client.connect()
|
|
52
|
+
|
|
53
|
+
# Read device state
|
|
54
|
+
state = await client.read_state()
|
|
55
|
+
print(f"PLC State: {state.ads_state.name}")
|
|
56
|
+
|
|
57
|
+
# Read single symbol
|
|
58
|
+
value = await client.read_symbol_by_name("MAIN.MyVariable")
|
|
59
|
+
print(f"Value: {value}")
|
|
60
|
+
|
|
61
|
+
# Read multiple symbols efficiently
|
|
62
|
+
symbols = ["MAIN.Var1", "MAIN.Var2", "MAIN.Var3"]
|
|
63
|
+
values = await client.read_symbols_by_names(symbols)
|
|
64
|
+
print(f"Values: {values}")
|
|
65
|
+
|
|
66
|
+
finally:
|
|
67
|
+
await client.disconnect()
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
asyncio.run(main())
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Core Components
|
|
74
|
+
|
|
75
|
+
### AdsClient
|
|
76
|
+
|
|
77
|
+
The main interface for ADS communication:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from aioads import AdsClient, AmsAddress
|
|
81
|
+
|
|
82
|
+
# Create TCP client
|
|
83
|
+
client = AdsClient.create_tcp(
|
|
84
|
+
src=AmsAddress(net_id="source.net.id.1.1", port=1234),
|
|
85
|
+
dst=AmsAddress(net_id="target.net.id.1.1", port=851),
|
|
86
|
+
ip="192.168.1.100"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Connect
|
|
90
|
+
await client.connect()
|
|
91
|
+
|
|
92
|
+
# Read operations
|
|
93
|
+
state = await client.read_state()
|
|
94
|
+
symbols = await client.get_symbols()
|
|
95
|
+
data_types = await client.get_symbol_datatypes()
|
|
96
|
+
value = await client.read_symbol_by_name("MAIN.Variable")
|
|
97
|
+
values = await client.read_symbols_by_names(["Var1", "Var2"])
|
|
98
|
+
|
|
99
|
+
# Disconnect
|
|
100
|
+
await client.disconnect()
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Batch Operations
|
|
104
|
+
|
|
105
|
+
Efficiently read multiple symbols in a single operation:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# Prepare symbol list
|
|
109
|
+
symbols_to_read = {
|
|
110
|
+
"MAIN.Temperature",
|
|
111
|
+
"MAIN.Pressure",
|
|
112
|
+
"MAIN.FlowRate",
|
|
113
|
+
"MAIN.Status.Running",
|
|
114
|
+
"MAIN.Recipe.CurrentStep"
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Read all symbols in one batch operation
|
|
118
|
+
values = await client.read_symbols_by_names(symbols_to_read)
|
|
119
|
+
|
|
120
|
+
for symbol_name, value in values.items():
|
|
121
|
+
print(f"{symbol_name}: {value}")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Advanced Usage
|
|
125
|
+
|
|
126
|
+
### Custom Transport
|
|
127
|
+
|
|
128
|
+
You can provide your own transport implementation:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from aioads.tcp_transport import AdsTcpTransport
|
|
132
|
+
from aioads.ads_symbol_cache import AdsSymbolCache
|
|
133
|
+
|
|
134
|
+
transport = AdsTcpTransport(
|
|
135
|
+
src_address=src_address,
|
|
136
|
+
ip="192.168.1.100",
|
|
137
|
+
port=48898
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
cache = AdsSymbolCache(transport=transport, dst_address=dst_address)
|
|
141
|
+
client = AdsClient(transport=transport, dst_address=dst_address, cache=cache)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Performance Tuning
|
|
145
|
+
|
|
146
|
+
For high-performance applications:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
# Pre-load symbol information to cache
|
|
150
|
+
await client.read_symbol_infos_by_names(large_symbol_list)
|
|
151
|
+
|
|
152
|
+
# Use batch reads for efficiency
|
|
153
|
+
start = time.perf_counter()
|
|
154
|
+
values = await client.read_symbols_by_names(symbols)
|
|
155
|
+
duration = (time.perf_counter() - start) * 1000
|
|
156
|
+
print(f"Read {len(values)} symbols in {duration:.2f}ms")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Concurrent Operations
|
|
160
|
+
|
|
161
|
+
Handle multiple tasks concurrently:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
import asyncio
|
|
165
|
+
|
|
166
|
+
async def read_process_data(client, process_symbols):
|
|
167
|
+
while True:
|
|
168
|
+
data = await client.read_symbols_by_names(process_symbols)
|
|
169
|
+
# Process data...
|
|
170
|
+
await asyncio.sleep(0.1)
|
|
171
|
+
|
|
172
|
+
async def read_diagnostics(client, diag_symbols):
|
|
173
|
+
while True:
|
|
174
|
+
data = await client.read_symbols_by_names(diag_symbols)
|
|
175
|
+
# Process diagnostics...
|
|
176
|
+
await asyncio.sleep(1.0)
|
|
177
|
+
|
|
178
|
+
async def main():
|
|
179
|
+
client = AdsClient.create_tcp(...)
|
|
180
|
+
await client.connect()
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
# Run multiple concurrent tasks
|
|
184
|
+
async with asyncio.TaskGroup() as tg:
|
|
185
|
+
tg.create_task(read_process_data(client, process_symbols))
|
|
186
|
+
tg.create_task(read_diagnostics(client, diagnostic_symbols))
|
|
187
|
+
finally:
|
|
188
|
+
await client.disconnect()
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Protocol Support
|
|
192
|
+
|
|
193
|
+
### ADS Commands
|
|
194
|
+
|
|
195
|
+
The library supports all major ADS commands:
|
|
196
|
+
|
|
197
|
+
- **Read/Write Operations**: Single and batch symbol access
|
|
198
|
+
- **Device Information**: State, version, and device info
|
|
199
|
+
- **Symbol Management**: Symbol table upload and parsing
|
|
200
|
+
- **Data Types**: Complete data type information and parsing
|
|
201
|
+
- **Notifications**: Change and cyclic notifications
|
|
202
|
+
- **Sum Commands**: Efficient bulk operations
|
|
203
|
+
|
|
204
|
+
### Data Types
|
|
205
|
+
|
|
206
|
+
Full support for TwinCAT data types:
|
|
207
|
+
|
|
208
|
+
- **Basic Types**: BOOL, BYTE, WORD, DWORD, INT, REAL, STRING, etc.
|
|
209
|
+
- **Structured Types**: STRUCT, ARRAY, UNION
|
|
210
|
+
- **Complex Types**: Custom user-defined types
|
|
211
|
+
- **Arrays**: Multi-dimensional arrays with proper indexing
|
|
212
|
+
|
|
213
|
+
### Notification Modes
|
|
214
|
+
|
|
215
|
+
Multiple notification transmission modes supported:
|
|
216
|
+
|
|
217
|
+
| Mode | Description | Use Case |
|
|
218
|
+
| ------------------- | --------------------------- | ----------------------- |
|
|
219
|
+
| `ClientCycle` | Client-triggered periodic | Reduced server load |
|
|
220
|
+
| `ClientOnChange` | Client-triggered on change | Reduced server load |
|
|
221
|
+
| `Cyclic` | Server-side periodic | Real-time performance |
|
|
222
|
+
| `OnChange` | Server-side on change | Immediate notifications |
|
|
223
|
+
| `CyclicInContext` | Task-synchronized periodic | Task coordination |
|
|
224
|
+
| `OnChangeInContext` | Task-synchronized on change | Task coordination |
|
|
225
|
+
|
|
226
|
+
## Project Structure
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
aioads/
|
|
230
|
+
├── ads_client.py # Main client interface
|
|
231
|
+
├── tcp_transport.py # TCP transport layer
|
|
232
|
+
├── ads_symbol_cache.py # Symbol caching system
|
|
233
|
+
├── ads_symbol_parser.py # Symbol data parsing
|
|
234
|
+
├── ams_address.py # AMS addressing
|
|
235
|
+
├── ams_header.py # AMS protocol headers
|
|
236
|
+
├── commands/ # ADS command implementations
|
|
237
|
+
│ ├── ads_read.py
|
|
238
|
+
│ ├── ads_write.py
|
|
239
|
+
│ ├── ads_read_state.py
|
|
240
|
+
│ └── ...
|
|
241
|
+
├── functions/ # ADS function implementations
|
|
242
|
+
│ ├── ads_sum_read.py
|
|
243
|
+
│ ├── ads_symbol_upload.py
|
|
244
|
+
│ └── ...
|
|
245
|
+
└── utils/ # Utility functions
|
|
246
|
+
└── async_zip.py
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Examples
|
|
250
|
+
|
|
251
|
+
Check the `examples/` directory for complete usage examples:
|
|
252
|
+
|
|
253
|
+
- `read_cycles.py` - Basic single symbol reading
|
|
254
|
+
- `read_multiple.py` - Batch reading
|
|
255
|
+
|
|
256
|
+
## Requirements
|
|
257
|
+
|
|
258
|
+
- Python 3.11+
|
|
259
|
+
- asyncio
|
|
260
|
+
- No external dependencies (pure Python implementation)
|
|
261
|
+
|
|
262
|
+
## Performance
|
|
263
|
+
|
|
264
|
+
The library is optimized for high-performance scenarios:
|
|
265
|
+
|
|
266
|
+
- **Batch Operations**: Read multiple symbols in single ADS calls
|
|
267
|
+
- **Smart Caching**: Symbol information cached to avoid repeated lookups
|
|
268
|
+
- **Async Architecture**: Non-blocking I/O for maximum throughput
|
|
269
|
+
|
|
270
|
+
Typical performance:
|
|
271
|
+
- Single symbol read: ~1-5ms
|
|
272
|
+
- Batch reads (100+ symbols): ~10-50ms
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
277
|
+
|
|
278
|
+
## Contributing
|
|
279
|
+
|
|
280
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
281
|
+
|
|
282
|
+
## Troubleshooting
|
|
283
|
+
|
|
284
|
+
### Connection Issues
|
|
285
|
+
|
|
286
|
+
1. **Firewall**: Ensure port 48898 (ADS) is open
|
|
287
|
+
2. **AMS Routes**: Verify AMS routes are configured on target system
|
|
288
|
+
3. **Network**: Check IP connectivity between systems
|
|
289
|
+
|
|
290
|
+
### Symbol Access
|
|
291
|
+
|
|
292
|
+
1. **PLC State**: Ensure PLC is in RUN mode for symbol access
|
|
293
|
+
|
|
294
|
+
### Performance Issues
|
|
295
|
+
|
|
296
|
+
1. **Batch Reads**: Use batch operations instead of individual reads
|
|
297
|
+
2. **Caching**: Pre-load symbol info with `read_symbol_infos_by_names()`
|
|
298
|
+
3. **Concurrent Tasks**: Be mindful of PLC load with multiple tasks
|
|
299
|
+
|
|
300
|
+
## Support
|
|
301
|
+
|
|
302
|
+
For questions, issues, or contributions, please refer to the project's GitHub repository.
|