aioads 0.1.0.dev3__tar.gz → 0.1.0.dev4__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.dev4/.github/dependabot.yml +20 -0
- aioads-0.1.0.dev4/.github/workflows/ci.yml +51 -0
- aioads-0.1.0.dev4/.github/workflows/publish.yml +62 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/PKG-INFO +1 -1
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/ads_client.py +43 -20
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/commands/ads_add_notification.py +1 -1
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/commands/errors.py +4 -1
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/transport.py +22 -7
- aioads-0.1.0.dev4/docs/unittest_style_guide.html +1739 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/pyproject.toml +6 -1
- aioads-0.1.0.dev4/tests/builders.py +63 -0
- aioads-0.1.0.dev4/tests/unit/__init__.py +0 -0
- aioads-0.1.0.dev4/tests/unit/commands/__init__.py +0 -0
- aioads-0.1.0.dev4/tests/unit/commands/test_ads_add_notification.py +131 -0
- aioads-0.1.0.dev4/tests/unit/commands/test_ads_command.py +63 -0
- aioads-0.1.0.dev4/tests/unit/commands/test_ads_delete_notification.py +83 -0
- aioads-0.1.0.dev4/tests/unit/commands/test_ads_read.py +124 -0
- aioads-0.1.0.dev4/tests/unit/commands/test_ads_read_device_info.py +147 -0
- aioads-0.1.0.dev4/tests/unit/commands/test_ads_read_state.py +137 -0
- aioads-0.1.0.dev4/tests/unit/commands/test_ads_read_write.py +98 -0
- aioads-0.1.0.dev4/tests/unit/commands/test_ads_write.py +117 -0
- aioads-0.1.0.dev4/tests/unit/commands/test_ads_write_state.py +85 -0
- aioads-0.1.0.dev4/tests/unit/commands/test_errors.py +50 -0
- aioads-0.1.0.dev4/tests/unit/functions/__init__.py +0 -0
- aioads-0.1.0.dev4/tests/unit/functions/test_ads_enable_route.py +71 -0
- aioads-0.1.0.dev4/tests/unit/functions/test_ads_function.py +32 -0
- aioads-0.1.0.dev4/tests/unit/functions/test_ads_sum_read.py +102 -0
- aioads-0.1.0.dev4/tests/unit/functions/test_ads_sum_read_write.py +96 -0
- aioads-0.1.0.dev4/tests/unit/functions/test_ads_symbol_datatype_by_name.py +137 -0
- aioads-0.1.0.dev4/tests/unit/functions/test_ads_symbol_datatype_upload.py +82 -0
- aioads-0.1.0.dev4/tests/unit/functions/test_ads_symbol_info_by_name_ex.py +150 -0
- aioads-0.1.0.dev4/tests/unit/functions/test_ads_symbol_table_version.py +74 -0
- aioads-0.1.0.dev4/tests/unit/functions/test_ads_symbol_upload.py +78 -0
- aioads-0.1.0.dev4/tests/unit/functions/test_ads_symbol_upload_info.py +77 -0
- aioads-0.1.0.dev4/tests/unit/test_ads_client.py +184 -0
- aioads-0.1.0.dev4/tests/unit/test_ads_error_codes.py +77 -0
- aioads-0.1.0.dev4/tests/unit/test_ads_notifications.py +161 -0
- aioads-0.1.0.dev4/tests/unit/test_ads_symbol_cache.py +182 -0
- aioads-0.1.0.dev4/tests/unit/test_ads_symbol_parser.py +244 -0
- aioads-0.1.0.dev4/tests/unit/test_ams_address.py +61 -0
- aioads-0.1.0.dev4/tests/unit/test_ams_header.py +84 -0
- aioads-0.1.0.dev4/tests/unit/test_ams_tcp_header.py +62 -0
- aioads-0.1.0.dev4/tests/unit/test_errors.py +51 -0
- aioads-0.1.0.dev4/tests/unit/test_stream.py +133 -0
- aioads-0.1.0.dev4/tests/unit/test_transport.py +220 -0
- aioads-0.1.0.dev4/tests/unit/utils/__init__.py +0 -0
- aioads-0.1.0.dev4/tests/unit/utils/test_local_ip.py +60 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/.gitignore +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/.vscode/settings.json +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/LICENSE +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/README.md +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/ads_error_codes.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/ads_notifications.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/ads_symbol_cache.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/ads_symbol_parser.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/ams_address.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/ams_header.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/ams_tcp_header.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/commands/ads_command.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/commands/ads_delete_notification.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/commands/ads_read.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/commands/ads_read_device_info.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/commands/ads_read_state.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/commands/ads_read_write.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/commands/ads_write.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/commands/ads_write_state.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/errors.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/functions/ads_enable_route.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/functions/ads_function.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/functions/ads_sum_read.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/functions/ads_sum_read_write.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/functions/ads_symbol_datatype_by_name.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/functions/ads_symbol_datatype_upload.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/functions/ads_symbol_info_by_name_ex.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/functions/ads_symbol_table_version.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/functions/ads_symbol_upload.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/functions/ads_symbol_upload_info.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/stream.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/aioads/utils/local_ip.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/docs/transmission_mode.md +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/examples/read_cmd_reuse_mqtt.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/examples/read_cycles.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/examples/read_cycles_mqtt.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/examples/read_multiple.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/examples/read_multiple_mqtt.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/examples/read_single.py +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/pdm.lock +0 -0
- {aioads-0.1.0.dev3 → aioads-0.1.0.dev4}/tests/__init__.py +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# Keep the SHA-pinned GitHub Actions current (Dependabot updates both the
|
|
4
|
+
# pinned SHA and the trailing version comment).
|
|
5
|
+
- package-ecosystem: github-actions
|
|
6
|
+
directory: "/"
|
|
7
|
+
schedule:
|
|
8
|
+
interval: weekly
|
|
9
|
+
groups:
|
|
10
|
+
actions:
|
|
11
|
+
patterns: ["*"]
|
|
12
|
+
|
|
13
|
+
# Python dev/runtime dependencies tracked in pdm.lock / pyproject.toml.
|
|
14
|
+
- package-ecosystem: pip
|
|
15
|
+
directory: "/"
|
|
16
|
+
schedule:
|
|
17
|
+
interval: weekly
|
|
18
|
+
groups:
|
|
19
|
+
python:
|
|
20
|
+
patterns: ["*"]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
test:
|
|
18
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: false
|
|
22
|
+
matrix:
|
|
23
|
+
python-version: ["3.12", "3.13"]
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
|
|
30
|
+
- name: Set up PDM
|
|
31
|
+
uses: pdm-project/setup-pdm@973541a5febeafcfdadf8a51211435be6ecfd90f # v4.5
|
|
32
|
+
with:
|
|
33
|
+
python-version: ${{ matrix.python-version }}
|
|
34
|
+
cache: true
|
|
35
|
+
|
|
36
|
+
- name: Install dependencies
|
|
37
|
+
run: pdm install
|
|
38
|
+
|
|
39
|
+
- name: Lint
|
|
40
|
+
run: pdm run lint
|
|
41
|
+
|
|
42
|
+
- name: Run tests with coverage
|
|
43
|
+
run: pdm run coverage
|
|
44
|
+
|
|
45
|
+
- name: Upload coverage artifact
|
|
46
|
+
if: matrix.python-version == '3.12'
|
|
47
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
48
|
+
with:
|
|
49
|
+
name: coverage-xml
|
|
50
|
+
path: coverage.xml
|
|
51
|
+
if-no-files-found: ignore
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
# Avoid two releases racing to publish the same version.
|
|
12
|
+
concurrency:
|
|
13
|
+
group: publish-${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: false
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
name: Build distribution
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
22
|
+
with:
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
|
|
25
|
+
- name: Set up PDM
|
|
26
|
+
uses: pdm-project/setup-pdm@973541a5febeafcfdadf8a51211435be6ecfd90f # v4.5
|
|
27
|
+
with:
|
|
28
|
+
python-version: "3.12"
|
|
29
|
+
|
|
30
|
+
- name: Build sdist and wheel
|
|
31
|
+
run: pdm build
|
|
32
|
+
|
|
33
|
+
- name: Upload build artifacts
|
|
34
|
+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
35
|
+
with:
|
|
36
|
+
name: dist
|
|
37
|
+
path: dist/
|
|
38
|
+
|
|
39
|
+
publish:
|
|
40
|
+
name: Publish to PyPI
|
|
41
|
+
needs: build
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
# Only publish for real releases, not manual test runs.
|
|
44
|
+
if: github.event_name == 'release'
|
|
45
|
+
environment:
|
|
46
|
+
name: pypi
|
|
47
|
+
url: https://pypi.org/project/aioads/
|
|
48
|
+
permissions:
|
|
49
|
+
# IMPORTANT: required for OIDC trusted publishing.
|
|
50
|
+
id-token: write
|
|
51
|
+
steps:
|
|
52
|
+
- name: Download build artifacts
|
|
53
|
+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
54
|
+
with:
|
|
55
|
+
name: dist
|
|
56
|
+
path: dist/
|
|
57
|
+
|
|
58
|
+
- name: Publish to PyPI
|
|
59
|
+
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
|
|
60
|
+
with:
|
|
61
|
+
# Sign the published distributions with PEP 740 build provenance.
|
|
62
|
+
attestations: true
|
|
@@ -266,36 +266,59 @@ class AdsClient:
|
|
|
266
266
|
) -> dict[str, SymbolReadResult]:
|
|
267
267
|
"""
|
|
268
268
|
Read multiple symbols by their names from the ADS device.
|
|
269
|
+
|
|
270
|
+
The returned dict is keyed by the symbol names exactly as requested,
|
|
271
|
+
preserving their original casing.
|
|
269
272
|
"""
|
|
270
273
|
symbol_infos = await self._cache.read_symbol_infos_by_names(symbol_names)
|
|
271
274
|
if raise_errors:
|
|
272
275
|
self._raise_if_error(symbol_infos)
|
|
273
276
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
277
|
+
output: dict[str, SymbolReadResult] = {}
|
|
278
|
+
|
|
279
|
+
# Only symbols whose info resolved successfully can be read. Symbols
|
|
280
|
+
# with a lookup error are reported as-is; building a read command from
|
|
281
|
+
# their (invalid) index group/offset would misalign the bulk response.
|
|
282
|
+
readable: list[tuple[str, SymbolInfo]] = []
|
|
283
|
+
for requested_name, (error_code, symbol_info) in symbol_infos.items():
|
|
284
|
+
if not error_code.ok:
|
|
285
|
+
output[requested_name] = SymbolReadResult(error_code, None)
|
|
286
|
+
else:
|
|
287
|
+
readable.append((requested_name, symbol_info))
|
|
288
|
+
|
|
289
|
+
if not readable:
|
|
290
|
+
return output
|
|
291
|
+
|
|
284
292
|
function = AdsSumRead(
|
|
285
293
|
transport=self.transport,
|
|
286
294
|
ams_address=self.dst_address,
|
|
287
|
-
commands=
|
|
295
|
+
commands=[
|
|
296
|
+
AdsReadCommand(
|
|
297
|
+
transport=self.transport,
|
|
298
|
+
ams_address=self.dst_address,
|
|
299
|
+
idx_group=symbol_info.idx_group,
|
|
300
|
+
idx_offset=symbol_info.idx_offset,
|
|
301
|
+
length=symbol_info.idx_length,
|
|
302
|
+
)
|
|
303
|
+
for _, symbol_info in readable
|
|
304
|
+
],
|
|
288
305
|
)
|
|
289
|
-
|
|
290
306
|
response = await function.execute()
|
|
291
|
-
output: dict[str, SymbolReadResult] = {}
|
|
292
307
|
|
|
308
|
+
loop = asyncio.get_running_loop()
|
|
309
|
+
parse_names: list[str] = []
|
|
293
310
|
tasks = []
|
|
294
|
-
for (
|
|
295
|
-
|
|
311
|
+
for (requested_name, symbol_info), (read_response, resp_payload) in zip(
|
|
312
|
+
readable, response
|
|
296
313
|
):
|
|
314
|
+
# A failed read yields no usable payload, so skip parsing it.
|
|
315
|
+
if read_response.error_code != 0:
|
|
316
|
+
output[requested_name] = SymbolReadResult(
|
|
317
|
+
read_response.error_code, None)
|
|
318
|
+
continue
|
|
319
|
+
parse_names.append(requested_name)
|
|
297
320
|
tasks.append(
|
|
298
|
-
|
|
321
|
+
loop.run_in_executor(
|
|
299
322
|
self.parser_pool,
|
|
300
323
|
self.parser.parse,
|
|
301
324
|
symbol_info.data_type,
|
|
@@ -303,11 +326,11 @@ class AdsClient:
|
|
|
303
326
|
resp_payload,
|
|
304
327
|
)
|
|
305
328
|
)
|
|
306
|
-
parsed = await asyncio.gather(*tasks)
|
|
307
329
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
330
|
+
parsed = await asyncio.gather(*tasks)
|
|
331
|
+
for requested_name, symbol_data in zip(parse_names, parsed):
|
|
332
|
+
output[requested_name] = SymbolReadResult(
|
|
333
|
+
AdsErrorCode(0), symbol_data)
|
|
311
334
|
|
|
312
335
|
return output
|
|
313
336
|
|
|
@@ -77,7 +77,7 @@ class AdsAddNotificationCommand(ICommand[AdsAddNotificationResponse]):
|
|
|
77
77
|
Ads command to create a ads notification (subscription to a remote resource)
|
|
78
78
|
"""
|
|
79
79
|
|
|
80
|
-
SERIALIZE_STRUCT_DEF: Final[Struct] = Struct("<
|
|
80
|
+
SERIALIZE_STRUCT_DEF: Final[Struct] = Struct("<IIIIII16s")
|
|
81
81
|
|
|
82
82
|
def __init__(
|
|
83
83
|
self,
|
|
@@ -27,4 +27,7 @@ class AdsCommandError(Exception):
|
|
|
27
27
|
|
|
28
28
|
def __repr__(self) -> str:
|
|
29
29
|
"""Returns a detailed representation of the error for debugging."""
|
|
30
|
-
return
|
|
30
|
+
return (
|
|
31
|
+
f"AdsCommandError(error_code={self.error_code!r}, "
|
|
32
|
+
f"error_message:{self.error_code.description} args={self.args!r})"
|
|
33
|
+
)
|
|
@@ -364,7 +364,8 @@ class AdsTcpTransport(BaseTransport, ITransport):
|
|
|
364
364
|
while self._state is not ConnectionState.CLOSED:
|
|
365
365
|
try:
|
|
366
366
|
await self._read_loop()
|
|
367
|
-
except asyncio.CancelledError:
|
|
367
|
+
except asyncio.CancelledError: #pylint: disable=try-except-raise
|
|
368
|
+
# Except everything... except a cancellation event
|
|
368
369
|
raise
|
|
369
370
|
except Exception as e:
|
|
370
371
|
self.logger.info("Connection lost: %s", e)
|
|
@@ -381,7 +382,7 @@ class AdsTcpTransport(BaseTransport, ITransport):
|
|
|
381
382
|
self.logger.info("Reconnected to the remote")
|
|
382
383
|
backoff = self.RECONNECT_INITIAL_BACKOFF
|
|
383
384
|
break
|
|
384
|
-
except asyncio.CancelledError:
|
|
385
|
+
except asyncio.CancelledError: #pylint: disable=try-except-raise
|
|
385
386
|
raise
|
|
386
387
|
except Exception as e: # pylint: disable=broad-exception-caught
|
|
387
388
|
self.logger.info(
|
|
@@ -401,10 +402,23 @@ class AdsTcpTransport(BaseTransport, ITransport):
|
|
|
401
402
|
|
|
402
403
|
reader, _ = stream
|
|
403
404
|
while True:
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
405
|
+
try:
|
|
406
|
+
# Read AmsTcpHeader
|
|
407
|
+
tcp_header_bytes = await reader.readexactly(AmsTcpHeader.FIXED_SIZE)
|
|
408
|
+
tcp_header = AmsTcpHeader.deserialize(tcp_header_bytes)
|
|
409
|
+
payload_bytes = await reader.readexactly(tcp_header.length)
|
|
410
|
+
except asyncio.IncompleteReadError as e:
|
|
411
|
+
# readexactly hit EOF: the peer's recv() returned 0 bytes,
|
|
412
|
+
# i.e. the remote closed the connection. An empty partial is an
|
|
413
|
+
# orderly close on a message boundary; a non-empty partial means
|
|
414
|
+
# we were cut off mid-message. Either way the stream is dead, so
|
|
415
|
+
# raise a clear ConnectionError for the supervisor to reconnect.
|
|
416
|
+
if e.partial:
|
|
417
|
+
raise ConnectionError(
|
|
418
|
+
f"Connection closed mid-message: received "
|
|
419
|
+
f"{len(e.partial)} of {e.expected} expected bytes"
|
|
420
|
+
) from e
|
|
421
|
+
raise ConnectionError("Connection closed by remote") from e
|
|
408
422
|
|
|
409
423
|
ams_message_stream = AdsStream(memoryview(payload_bytes))
|
|
410
424
|
ams_header = AmsHeader.deserialize(ams_message_stream)
|
|
@@ -459,6 +473,7 @@ class AdsOverMqttTopics:
|
|
|
459
473
|
|
|
460
474
|
|
|
461
475
|
class MqttBaseTransport(BaseTransport):
|
|
476
|
+
"""Base transport that tunnels AMS/ADS frames over an MQTT broker."""
|
|
462
477
|
|
|
463
478
|
REQUEST_TIMEOUT = 120.0
|
|
464
479
|
|
|
@@ -771,7 +786,7 @@ class AdsGMqttTransport(MqttBaseTransport, ITransport):
|
|
|
771
786
|
response_future, timeout=self.REQUEST_TIMEOUT
|
|
772
787
|
)
|
|
773
788
|
|
|
774
|
-
async def _on_response(self, _client: gmqtt.Client, topic: str, payload: bytes,
|
|
789
|
+
async def _on_response(self, _client: gmqtt.Client, topic: str, payload: bytes, _qos: int, _properties: dict):
|
|
775
790
|
if self.topics.matches(self.topics.sub_response, topic):
|
|
776
791
|
ams_message_stream = AdsStream(memoryview(payload))
|
|
777
792
|
ams_header = AmsHeader.deserialize(ams_message_stream)
|