aioads 0.1.0.dev5__tar.gz → 0.1.0.dev6__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.
Files changed (98) hide show
  1. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/PKG-INFO +1 -1
  2. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/functions/ads_sum_read.py +21 -16
  3. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/functions/ads_sum_read_write.py +15 -13
  4. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/functions/ads_symbol_datatype_by_name.py +8 -3
  5. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/functions/ads_symbol_datatype_upload.py +4 -1
  6. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/functions/ads_symbol_info_by_name_ex.py +30 -12
  7. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/functions/ads_symbol_table_version.py +6 -5
  8. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/functions/ads_symbol_upload.py +10 -4
  9. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/functions/ads_symbol_upload_info.py +10 -8
  10. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/pyproject.toml +1 -1
  11. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/.github/dependabot.yml +0 -0
  12. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/.github/workflows/ci.yml +0 -0
  13. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/.github/workflows/publish.yml +0 -0
  14. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/.gitignore +0 -0
  15. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/.vscode/settings.json +0 -0
  16. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/LICENSE +0 -0
  17. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/README.md +0 -0
  18. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/ads_client.py +0 -0
  19. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/ads_error_codes.py +0 -0
  20. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/ads_notifications.py +0 -0
  21. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/ads_symbol_cache.py +0 -0
  22. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/ads_symbol_parser.py +0 -0
  23. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/ams_address.py +0 -0
  24. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/ams_header.py +0 -0
  25. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/ams_service_port.py +0 -0
  26. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/ams_tcp_header.py +0 -0
  27. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/commands/ads_add_notification.py +0 -0
  28. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/commands/ads_command.py +0 -0
  29. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/commands/ads_delete_notification.py +0 -0
  30. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/commands/ads_read.py +0 -0
  31. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/commands/ads_read_device_info.py +0 -0
  32. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/commands/ads_read_state.py +0 -0
  33. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/commands/ads_read_write.py +0 -0
  34. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/commands/ads_write.py +0 -0
  35. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/commands/ads_write_state.py +0 -0
  36. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/commands/errors.py +0 -0
  37. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/errors.py +0 -0
  38. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/functions/ads_enable_route.py +0 -0
  39. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/functions/ads_function.py +0 -0
  40. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/py.typed +0 -0
  41. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/stream.py +0 -0
  42. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/transport.py +0 -0
  43. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/aioads/utils/local_ip.py +0 -0
  44. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/docs/transmission_mode.md +0 -0
  45. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/docs/unittest_style_guide.html +0 -0
  46. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/examples/read_cmd_reuse_mqtt.py +0 -0
  47. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/examples/read_cycles.py +0 -0
  48. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/examples/read_cycles_mqtt.py +0 -0
  49. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/examples/read_multiple.py +0 -0
  50. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/examples/read_multiple_mqtt.py +0 -0
  51. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/examples/read_single.py +0 -0
  52. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/pdm.lock +0 -0
  53. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/__init__.py +0 -0
  54. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/builders.py +0 -0
  55. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/integration/README.md +0 -0
  56. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/integration/__init__.py +0 -0
  57. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/integration/base.py +0 -0
  58. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/integration/config.example.toml +0 -0
  59. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/integration/config.py +0 -0
  60. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/integration/test_connection.py +0 -0
  61. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/integration/test_performance.py +0 -0
  62. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/integration/test_read_symbols.py +0 -0
  63. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/__init__.py +0 -0
  64. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/__init__.py +0 -0
  65. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/test_ads_add_notification.py +0 -0
  66. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/test_ads_command.py +0 -0
  67. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/test_ads_delete_notification.py +0 -0
  68. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/test_ads_read.py +0 -0
  69. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/test_ads_read_device_info.py +0 -0
  70. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/test_ads_read_state.py +0 -0
  71. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/test_ads_read_write.py +0 -0
  72. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/test_ads_write.py +0 -0
  73. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/test_ads_write_state.py +0 -0
  74. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/commands/test_errors.py +0 -0
  75. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/__init__.py +0 -0
  76. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/test_ads_enable_route.py +0 -0
  77. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/test_ads_function.py +0 -0
  78. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/test_ads_sum_read.py +0 -0
  79. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/test_ads_sum_read_write.py +0 -0
  80. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/test_ads_symbol_datatype_by_name.py +0 -0
  81. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/test_ads_symbol_datatype_upload.py +0 -0
  82. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/test_ads_symbol_info_by_name_ex.py +0 -0
  83. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/test_ads_symbol_table_version.py +0 -0
  84. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/test_ads_symbol_upload.py +0 -0
  85. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/functions/test_ads_symbol_upload_info.py +0 -0
  86. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_ads_client.py +0 -0
  87. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_ads_error_codes.py +0 -0
  88. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_ads_notifications.py +0 -0
  89. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_ads_symbol_cache.py +0 -0
  90. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_ads_symbol_parser.py +0 -0
  91. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_ams_address.py +0 -0
  92. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_ams_header.py +0 -0
  93. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_ams_tcp_header.py +0 -0
  94. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_errors.py +0 -0
  95. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_stream.py +0 -0
  96. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/test_transport.py +0 -0
  97. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/utils/__init__.py +0 -0
  98. {aioads-0.1.0.dev5 → aioads-0.1.0.dev6}/tests/unit/utils/test_local_ip.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aioads
3
- Version: 0.1.0.dev5
3
+ Version: 0.1.0.dev6
4
4
  Summary: An asynchronous Python library for communicating with Beckhoff TwinCAT PLCs
5
5
  Author-email: MkKiefer <102972583+MkKiefer@users.noreply.github.com>
6
6
  License: MIT
@@ -5,13 +5,13 @@ sending multiple `AdsReadCommand` in a single command
5
5
  https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/124830987.html&id=
6
6
  """
7
7
 
8
-
9
8
  from struct import Struct
10
9
  from typing import Final
11
10
  from aioads.ads_error_codes import AdsErrorCode
12
11
  from aioads.ams_address import AmsAddress
13
12
  from aioads.commands.ads_read import AdsReadCommand, AdsReadResponse
14
13
  from aioads.commands.ads_read_write import AdsReadWriteCommand
14
+ from aioads.commands.errors import AdsCommandError
15
15
  from aioads.functions.ads_function import AdsFunctionSymbolGroup, IAdsFunction
16
16
  from aioads.stream import AdsStream
17
17
  from aioads.transport import ITransport
@@ -19,7 +19,7 @@ from aioads.transport import ITransport
19
19
 
20
20
  class AdsSumRead(IAdsFunction[list[tuple[AdsReadResponse, AdsStream]]]):
21
21
  """
22
- Ads sum read command that allows batching up to 500 `AdsReadCommand` in a single call
22
+ Ads sum read command that allows batching up to 500 `AdsReadCommand` in a single call
23
23
  """
24
24
 
25
25
  ERROR_CODE_STRUCT_DEF: Final[Struct] = Struct("<I")
@@ -36,17 +36,15 @@ class AdsSumRead(IAdsFunction[list[tuple[AdsReadResponse, AdsStream]]]):
36
36
 
37
37
  def serialize(self) -> bytes:
38
38
  """
39
- serialize all commands to bytes
39
+ serialize all commands to bytes
40
40
  """
41
41
  return b"".join(command.serialize() for command in self.commands)
42
42
 
43
43
  async def execute(self) -> list[tuple[AdsReadResponse, AdsStream]]:
44
44
  if len(self.commands) == 0:
45
- raise ValueError(
46
- "At least one command is required for ADS Sum Read")
45
+ raise ValueError("At least one command is required for ADS Sum Read")
47
46
  if len(self.commands) > 500:
48
- raise ValueError(
49
- "Too many commands for ADS Sum Read, maximum is 500")
47
+ raise ValueError("Too many commands for ADS Sum Read, maximum is 500")
50
48
 
51
49
  payload = self.serialize()
52
50
  command = AdsReadWriteCommand(
@@ -60,20 +58,27 @@ class AdsSumRead(IAdsFunction[list[tuple[AdsReadResponse, AdsStream]]]):
60
58
  write_length=len(payload),
61
59
  write_data=payload,
62
60
  )
63
- _, read_payload = await command.request()
61
+ header, read_payload = await command.request()
62
+ if not header.error_code.ok:
63
+ raise AdsCommandError(header.error_code, "Failed to execute ADS Sum Read")
64
64
  error_stream = read_payload.sub_stream(
65
65
  # 4 bytes per error code
66
- len(self.commands) * self.ERROR_CODE_STRUCT_DEF.size)
66
+ len(self.commands)
67
+ * self.ERROR_CODE_STRUCT_DEF.size
68
+ )
67
69
 
68
70
  response: list[tuple[AdsReadResponse, AdsStream]] = []
69
71
  for cmd in self.commands:
70
72
  error_code = AdsErrorCode(
71
- error_stream.read_struct(self.ERROR_CODE_STRUCT_DEF)[0])
72
- response.append((
73
- AdsReadResponse(
74
- error_code=error_code,
75
- length=cmd.length,
76
- ),
77
- read_payload.sub_stream(cmd.length))
73
+ error_stream.read_struct(self.ERROR_CODE_STRUCT_DEF)[0]
74
+ )
75
+ response.append(
76
+ (
77
+ AdsReadResponse(
78
+ error_code=error_code,
79
+ length=cmd.length,
80
+ ),
81
+ read_payload.sub_stream(cmd.length),
82
+ )
78
83
  )
79
84
  return response
@@ -49,17 +49,17 @@ packet
49
49
  ```
50
50
 
51
51
  """
52
+
52
53
  from aioads.ams_address import AmsAddress
53
54
  from aioads.commands.ads_read import AdsReadResponse
54
55
  from aioads.commands.ads_read_write import AdsReadWriteCommand
56
+ from aioads.commands.errors import AdsCommandError
55
57
  from aioads.functions.ads_function import AdsFunctionSymbolGroup, IAdsFunction
56
58
  from aioads.stream import AdsStream
57
59
  from aioads.transport import ITransport
58
60
 
59
61
 
60
- class AdsSumReadWrite(
61
- IAdsFunction[list[tuple[AdsReadResponse, AdsStream]]]
62
- ):
62
+ class AdsSumReadWrite(IAdsFunction[list[tuple[AdsReadResponse, AdsStream]]]):
63
63
  """
64
64
  ADS Sum Read/Write function to send a READ/WRITE Command in batch.
65
65
  This function allows multiple read/write commands to be sent in a single request,
@@ -92,11 +92,9 @@ class AdsSumReadWrite(
92
92
  :return: An async generator yielding tuples of (AdsReadResponse, AdsStream) for each command.
93
93
  """
94
94
  if len(self.commands) == 0:
95
- raise ValueError(
96
- "At least one command is required for ADS Sum Read/Write")
95
+ raise ValueError("At least one command is required for ADS Sum Read/Write")
97
96
  if len(self.commands) > 500:
98
- raise ValueError(
99
- "Too many commands for ADS Sum Read/Write, maximum is 500")
97
+ raise ValueError("Too many commands for ADS Sum Read/Write, maximum is 500")
100
98
 
101
99
  payload = self.serialize()
102
100
  total_read_length = sum(
@@ -117,15 +115,19 @@ class AdsSumReadWrite(
117
115
  write_length=total_write_length,
118
116
  write_data=payload,
119
117
  )
120
- _, read_payload = await command.request()
118
+ header, read_payload = await command.request()
119
+ if not header.error_code.ok:
120
+ raise AdsCommandError(
121
+ header.error_code, "Failed to execute ADS Sum Read/Write"
122
+ )
121
123
  read_response_stream = read_payload.sub_stream(
122
- len(self.commands) * AdsReadResponse.STRUCT_DEF.size)
124
+ len(self.commands) * AdsReadResponse.STRUCT_DEF.size
125
+ )
123
126
 
124
127
  response: list[tuple[AdsReadResponse, AdsStream]] = []
125
128
  for _ in self.commands:
126
129
  read_response = AdsReadResponse.deserialize(read_response_stream)
127
- response.append((
128
- read_response,
129
- read_payload.sub_stream(read_response.length)
130
- ))
130
+ response.append(
131
+ (read_response, read_payload.sub_stream(read_response.length))
132
+ )
131
133
  return response
@@ -36,11 +36,13 @@ packet
36
36
 
37
37
 
38
38
  """
39
+
39
40
  from dataclasses import dataclass
40
41
  from struct import Struct
41
42
  from typing import ClassVar
42
43
  from aioads.ams_address import AmsAddress
43
44
  from aioads.commands.ads_read_write import AdsReadWriteCommand
45
+ from aioads.commands.errors import AdsCommandError
44
46
  from aioads.functions.ads_function import AdsFunctionSymbolGroup, IAdsFunction
45
47
  from aioads.functions.ads_symbol_info_by_name_ex import (
46
48
  AdsSymbolDataType,
@@ -75,8 +77,7 @@ class AdsDatatypeArrayInfo:
75
77
  """
76
78
  Create `AdsDatatypeArrayInfo` from the `AdsStream`
77
79
  """
78
- l_bound, e_elements = data.read_struct(
79
- AdsDatatypeArrayInfo.STRUCT_DEF)
80
+ l_bound, e_elements = data.read_struct(AdsDatatypeArrayInfo.STRUCT_DEF)
80
81
  return AdsDatatypeArrayInfo(
81
82
  l_bound=l_bound,
82
83
  e_elements=e_elements,
@@ -227,6 +228,10 @@ class AdsSymbolDataTypeByName(IAdsFunction[SymbolDataTypeResponse]):
227
228
  write_length=len(payload),
228
229
  read_length=0xFFFF, # Max read length
229
230
  )
230
- _, read_payload = await command.request()
231
+ header, read_payload = await command.request()
232
+ if not header.error_code.ok:
233
+ raise AdsCommandError(
234
+ header.error_code, "Failed to read symbol datatype by name"
235
+ )
231
236
  symbol_datatype = SymbolDataTypeResponse.deserialize(read_payload)
232
237
  return symbol_datatype
@@ -6,6 +6,7 @@ https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/124
6
6
 
7
7
  from aioads.ams_address import AmsAddress
8
8
  from aioads.commands.ads_read import AdsReadCommand
9
+ from aioads.commands.errors import AdsCommandError
9
10
  from aioads.functions.ads_function import AdsFunctionSymbolGroup, IAdsFunction
10
11
  from aioads.functions.ads_symbol_datatype_by_name import SymbolDataTypeResponse
11
12
  from aioads.transport import ITransport
@@ -38,7 +39,9 @@ class AdsSymbolDataTypeUpload(IAdsFunction[list[SymbolDataTypeResponse]]):
38
39
  idx_offset=0,
39
40
  length=self.dt_size,
40
41
  )
41
- _, read_payload = await command.request()
42
+ header, read_payload = await command.request()
43
+ if not header.error_code.ok:
44
+ raise AdsCommandError(header.error_code, "Failed to read datatype info")
42
45
 
43
46
  symbol_datatypes = list[SymbolDataTypeResponse]()
44
47
  start = read_payload.tell()
@@ -1,5 +1,5 @@
1
1
  """
2
- This module provides models and ads function to get the symbol info (symbol metadata) by the variable / symbol name.
2
+ This module provides models and ads function to get the symbol info (symbol metadata) by the variable / symbol name.
3
3
  """
4
4
 
5
5
  from dataclasses import dataclass
@@ -9,6 +9,7 @@ from typing import ClassVar
9
9
  from aioads.ads_error_codes import AdsErrorCode
10
10
  from aioads.ams_address import AmsAddress
11
11
  from aioads.commands.ads_read_write import AdsReadWriteCommand
12
+ from aioads.commands.errors import AdsCommandError
12
13
  from aioads.functions.ads_function import AdsFunctionSymbolGroup, IAdsFunction
13
14
  from aioads.functions.ads_sum_read_write import AdsSumReadWrite
14
15
  from aioads.stream import AdsStream
@@ -113,10 +114,8 @@ class SymbolInfo:
113
114
  type_name_length,
114
115
  comment_length,
115
116
  ) = data.read_struct(SymbolInfo.FIXED_STRUCT)
116
- symbol_name = data.read(symbol_name_length + 1).rstrip(
117
- b"\x00").decode("cp1252")
118
- type_name = data.read(type_name_length + 1).rstrip(
119
- b"\x00").decode("cp1252")
117
+ symbol_name = data.read(symbol_name_length + 1).rstrip(b"\x00").decode("cp1252")
118
+ type_name = data.read(type_name_length + 1).rstrip(b"\x00").decode("cp1252")
120
119
  comment = data.read(comment_length + 1).rstrip(b"\x00").decode("cp1252")
121
120
 
122
121
  # Move the stream position to the end of the entry
@@ -133,6 +132,20 @@ class SymbolInfo:
133
132
  comment=comment,
134
133
  )
135
134
 
135
+ @classmethod
136
+ def void(cls) -> "SymbolInfo":
137
+ """A void symbol info that can be used as a placeholder"""
138
+ return cls(
139
+ idx_group=0,
140
+ idx_offset=0,
141
+ idx_length=0,
142
+ data_type=AdsSymbolDataType.VOID,
143
+ symbol_flags=AdsSymbolFlags(0),
144
+ symbol_name="",
145
+ type_name="",
146
+ comment="",
147
+ )
148
+
136
149
 
137
150
  class SymbolInfoByNameEx(IAdsFunction[SymbolInfo]):
138
151
  """
@@ -165,14 +178,14 @@ class SymbolInfoByNameEx(IAdsFunction[SymbolInfo]):
165
178
  read_length=0xFFFF, # Max read length
166
179
  write_data=payload,
167
180
  )
168
- _, read_payload = await command.request()
181
+ header, read_payload = await command.request()
182
+ if not header.error_code.ok:
183
+ raise AdsCommandError(header.error_code, "Failed to read symbol info")
169
184
  symbol_info = SymbolInfo.deserialize(read_payload)
170
185
  return symbol_info
171
186
 
172
187
 
173
- class SymbolInfoByNameExSumRead(
174
- IAdsFunction[list[tuple[AdsErrorCode, SymbolInfo]]]
175
- ):
188
+ class SymbolInfoByNameExSumRead(IAdsFunction[list[tuple[AdsErrorCode, SymbolInfo]]]):
176
189
  """
177
190
  This function utilizes the `AdsSumReadWrite` function to
178
191
  send multiple `SymbolInfoByNameEx` function calls in a batch.
@@ -219,9 +232,14 @@ class SymbolInfoByNameExSumRead(
219
232
  command = AdsSumReadWrite(
220
233
  transport=self.transport,
221
234
  ams_address=self.ams_address,
222
- commands=commands[batch_start:batch_start + self.BATCH_SIZE],
235
+ commands=commands[batch_start : batch_start + self.BATCH_SIZE],
223
236
  )
224
- for read_header, read_payload in await command.execute():
237
+ for header, read_payload in await command.execute():
238
+ # If we have an error and no payload we return a void symbol info
239
+ # we raise no exception to let the client handle the error for each symbol individually
240
+ if not header.error_code.ok and read_payload.length == 0:
241
+ response.append((header.error_code, SymbolInfo.void()))
242
+ continue
225
243
  symbol_info = SymbolInfo.deserialize(read_payload)
226
- response.append((read_header.error_code, symbol_info))
244
+ response.append((header.error_code, symbol_info))
227
245
  return response
@@ -2,7 +2,7 @@
2
2
  This module contains the ads function call for requesting the `SymbolTableVersion`.
3
3
 
4
4
 
5
- This can be used to monitoring, on change the index offsets in symbol metadata can change
5
+ This can be used to monitoring, on change the index offsets in symbol metadata can change
6
6
  and we need to re resolve the new address where we can read this symbol, if we don't do this
7
7
  we read a random memory array we most likely can't parse.
8
8
  https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/124830987.html&id=
@@ -42,8 +42,9 @@ class SymbolTableVersion(IAdsFunction[int]):
42
42
  idx_offset=0,
43
43
  length=1,
44
44
  )
45
- read_resp, read_stream = await command.request()
46
- if not read_resp.error_code.ok:
47
- raise AdsCommandError(read_resp.error_code,
48
- "Failed to read symbol table version")
45
+ header, read_stream = await command.request()
46
+ if not header.error_code.ok:
47
+ raise AdsCommandError(
48
+ header.error_code, "Failed to read symbol table version"
49
+ )
49
50
  return read_stream.read_struct(self.VERSION_STRUCT_DEF)[0]
@@ -1,9 +1,10 @@
1
1
  """
2
- This module contains a ads function to acquire the symbol infos (metadata) of the global / root symbols.
2
+ This module contains a ads function to acquire the symbol infos (metadata) of the global / root symbols.
3
3
  """
4
4
 
5
5
  from aioads.ams_address import AmsAddress
6
6
  from aioads.commands.ads_read import AdsReadCommand
7
+ from aioads.commands.errors import AdsCommandError
7
8
  from aioads.functions.ads_function import AdsFunctionSymbolGroup, IAdsFunction
8
9
  from aioads.functions.ads_symbol_info_by_name_ex import SymbolInfo
9
10
  from aioads.transport import ITransport
@@ -11,8 +12,8 @@ from aioads.transport import ITransport
11
12
 
12
13
  class AdsSymbolUpload(IAdsFunction[list[SymbolInfo]]):
13
14
  """
14
- Ads Function to read all global symbols.
15
- Hint:
15
+ Ads Function to read all global symbols.
16
+ Hint:
16
17
  Only the top most symbol information are returned, to get the full ads tree
17
18
  you need to resolve it from this over the datatypes that can be requested with `AdsSymbolDataTypeUpload`
18
19
  """
@@ -36,7 +37,12 @@ class AdsSymbolUpload(IAdsFunction[list[SymbolInfo]]):
36
37
  idx_offset=0,
37
38
  length=self.tree_size,
38
39
  )
39
- _, read_payload = await command.request()
40
+ header, read_payload = await command.request()
41
+ if not header.error_code.ok:
42
+ raise AdsCommandError(
43
+ header.error_code, "Failed to request symbol upload info"
44
+ )
45
+
40
46
  symbol_infos = list[SymbolInfo]()
41
47
 
42
48
  start = read_payload.tell()
@@ -10,6 +10,7 @@ from typing import ClassVar
10
10
 
11
11
  from aioads.ams_address import AmsAddress
12
12
  from aioads.commands.ads_read import AdsReadCommand
13
+ from aioads.commands.errors import AdsCommandError
13
14
  from aioads.functions.ads_function import AdsFunctionSymbolGroup, IAdsFunction
14
15
 
15
16
  from aioads.stream import AdsStream
@@ -46,11 +47,9 @@ class SymbolUploadInfo2Response:
46
47
  @classmethod
47
48
  def deserialize(cls, data: AdsStream) -> "SymbolUploadInfo2Response":
48
49
  """
49
- Create `SymbolUploadInfo2Response` from the `AdsStream`
50
+ Create `SymbolUploadInfo2Response` from the `AdsStream`
50
51
  """
51
- parsed: tuple[
52
- int, int, int, int, int, int
53
- ] = data.read_struct(cls.STRUCT_DEF)
52
+ parsed: tuple[int, int, int, int, int, int] = data.read_struct(cls.STRUCT_DEF)
54
53
  return cls(
55
54
  symbol_cnt=parsed[0],
56
55
  symbol_size=parsed[1],
@@ -63,7 +62,7 @@ class SymbolUploadInfo2Response:
63
62
 
64
63
  class AdsSymbolUploadInfo2(IAdsFunction[SymbolUploadInfo2Response]):
65
64
  """
66
- Function to acquire basic symbol information
65
+ Function to acquire basic symbol information
67
66
  """
68
67
 
69
68
  def __init__(self, transport: ITransport, ams_address: AmsAddress) -> None:
@@ -83,7 +82,10 @@ class AdsSymbolUploadInfo2(IAdsFunction[SymbolUploadInfo2Response]):
83
82
  # 6 x 4 bytes (We expect a struct with 6x UINT32)
84
83
  length=SymbolUploadInfo2Response.STRUCT_DEF.size,
85
84
  )
86
- _, read_payload = await command.request()
87
- symbol_upload_info = SymbolUploadInfo2Response.deserialize(
88
- read_payload)
85
+ header, read_payload = await command.request()
86
+ if not header.error_code.ok:
87
+ raise AdsCommandError(
88
+ header.error_code, "Failed to request symbol upload info"
89
+ )
90
+ symbol_upload_info = SymbolUploadInfo2Response.deserialize(read_payload)
89
91
  return symbol_upload_info
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aioads"
3
- version = "0.1.0.dev5"
3
+ version = "0.1.0.dev6"
4
4
  description = "An asynchronous Python library for communicating with Beckhoff TwinCAT PLCs"
5
5
  authors = [
6
6
  { name = "MkKiefer", email = "102972583+MkKiefer@users.noreply.github.com" },
File without changes
File without changes
File without changes
File without changes
File without changes