python-snap7 1.3__tar.gz → 1.4__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 (43) hide show
  1. {python-snap7-1.3 → python_snap7-1.4}/MANIFEST.in +0 -1
  2. python_snap7-1.4/PKG-INFO +33 -0
  3. python_snap7-1.4/README.rst +21 -0
  4. python_snap7-1.4/pyproject.toml +69 -0
  5. python_snap7-1.4/python_snap7.egg-info/PKG-INFO +33 -0
  6. {python-snap7-1.3 → python_snap7-1.4}/python_snap7.egg-info/SOURCES.txt +12 -10
  7. {python-snap7-1.3 → python_snap7-1.4}/python_snap7.egg-info/requires.txt +2 -2
  8. python_snap7-1.4/setup.cfg +4 -0
  9. {python-snap7-1.3 → python_snap7-1.4}/snap7/__init__.py +5 -4
  10. {python-snap7-1.3 → python_snap7-1.4}/snap7/client/__init__.py +93 -91
  11. {python-snap7-1.3 → python_snap7-1.4}/snap7/common.py +27 -22
  12. python_snap7-1.4/snap7/error.py +104 -0
  13. {python-snap7-1.3 → python_snap7-1.4}/snap7/exceptions.py +0 -1
  14. {python-snap7-1.3 → python_snap7-1.4}/snap7/logo.py +8 -12
  15. {python-snap7-1.3 → python_snap7-1.4}/snap7/partner.py +9 -13
  16. {python-snap7-1.3 → python_snap7-1.4}/snap7/server/__init__.py +58 -69
  17. {python-snap7-1.3 → python_snap7-1.4}/snap7/server/__main__.py +2 -3
  18. python_snap7-1.4/snap7/types.py +324 -0
  19. python_snap7-1.4/snap7/util/__init__.py +200 -0
  20. python_snap7-1.4/snap7/util/db.py +604 -0
  21. python_snap7-1.4/snap7/util/getters.py +719 -0
  22. python_snap7-1.4/snap7/util/setters.py +536 -0
  23. {python-snap7-1.3/test → python_snap7-1.4/tests}/test_client.py +128 -113
  24. {python-snap7-1.3/test → python_snap7-1.4/tests}/test_common.py +4 -3
  25. {python-snap7-1.3/test → python_snap7-1.4/tests}/test_logo_client.py +15 -8
  26. {python-snap7-1.3/test → python_snap7-1.4/tests}/test_mainloop.py +26 -20
  27. {python-snap7-1.3/test → python_snap7-1.4/tests}/test_partner.py +8 -8
  28. {python-snap7-1.3/test → python_snap7-1.4/tests}/test_server.py +33 -29
  29. python_snap7-1.4/tests/test_util.py +608 -0
  30. python-snap7-1.3/PKG-INFO +0 -68
  31. python-snap7-1.3/README.rst +0 -42
  32. python-snap7-1.3/python_snap7.egg-info/PKG-INFO +0 -68
  33. python-snap7-1.3/setup.cfg +0 -13
  34. python-snap7-1.3/setup.py +0 -66
  35. python-snap7-1.3/snap7/error.py +0 -104
  36. python-snap7-1.3/snap7/types.py +0 -327
  37. python-snap7-1.3/snap7/util.py +0 -1878
  38. python-snap7-1.3/test/test_util.py +0 -567
  39. {python-snap7-1.3 → python_snap7-1.4}/LICENSE +0 -0
  40. {python-snap7-1.3 → python_snap7-1.4}/python_snap7.egg-info/dependency_links.txt +0 -0
  41. {python-snap7-1.3 → python_snap7-1.4}/python_snap7.egg-info/entry_points.txt +0 -0
  42. {python-snap7-1.3 → python_snap7-1.4}/python_snap7.egg-info/top_level.txt +0 -0
  43. {python-snap7-1.3 → python_snap7-1.4}/snap7/py.typed +0 -0
@@ -1,2 +1 @@
1
1
  include README.rst
2
-
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.1
2
+ Name: python-snap7
3
+ Version: 1.4
4
+ Summary: Python wrapper for the snap7 library
5
+ Author-email: Gijs Molenaar <gijsmolenaar@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/gijzelaerr/python-snap7
8
+ Project-URL: Documentation, https://python-snap7.readthedocs.io/en/latest/
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Console
11
+ Classifier: Topic :: System :: Hardware
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Manufacturing
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: POSIX
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Requires-Python: >=3.8
22
+ License-File: LICENSE
23
+ Provides-Extra: test
24
+ Requires-Dist: pytest; extra == "test"
25
+ Requires-Dist: mypy; extra == "test"
26
+ Requires-Dist: types-setuptools; extra == "test"
27
+ Requires-Dist: ruff; extra == "test"
28
+ Provides-Extra: cli
29
+ Requires-Dist: rich; extra == "cli"
30
+ Requires-Dist: click; extra == "cli"
31
+ Provides-Extra: doc
32
+ Requires-Dist: sphinx; extra == "doc"
33
+ Requires-Dist: sphinx_rtd_theme; extra == "doc"
@@ -0,0 +1,21 @@
1
+ About
2
+ =====
3
+
4
+ This is a ctypes-based Python wrapper for snap7. Snap7 is an open-source,
5
+ 32/64 bit, multi-platform Ethernet communication suite for interfacing natively
6
+ with Siemens S7 PLCs.
7
+
8
+ Python-snap7 is tested with Python 3.9+, on Windows, Linux and OS X.
9
+
10
+ The full documentation is available on `Read The Docs <https://python-snap7.readthedocs.io/en/latest/>`_.
11
+
12
+
13
+ Installation
14
+ ============
15
+
16
+ If you are running Windows, Mac OS X or GNU/Linux on an Intel x64 or ARM 64 compatible platform you can use the binary wheel installation::
17
+
18
+ $ pip install python-snap7
19
+
20
+
21
+ Otherwise, please read the `online installation documentation <https://python-snap7.readthedocs.io/en/latest/installation.html>`_.
@@ -0,0 +1,69 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "python-snap7"
7
+ version = "1.4"
8
+ description = "Python wrapper for the snap7 library"
9
+ authors = [
10
+ {name = "Gijs Molenaar", email = "gijsmolenaar@gmail.com"},
11
+ ]
12
+ classifiers = [
13
+ "Development Status :: 5 - Production/Stable",
14
+ "Environment :: Console",
15
+ "Topic :: System :: Hardware",
16
+ "Intended Audience :: Developers",
17
+ "Intended Audience :: Manufacturing",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Operating System :: POSIX",
20
+ "Programming Language :: Python :: 3.8",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ ]
26
+ license = {text = "MIT"}
27
+ requires-python = ">=3.8"
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/gijzelaerr/python-snap7"
31
+ Documentation = "https://python-snap7.readthedocs.io/en/latest/"
32
+
33
+ [project.optional-dependencies]
34
+ test = ["pytest", "mypy", "types-setuptools", "ruff"]
35
+ cli = ["rich", "click" ]
36
+ doc = ["sphinx", "sphinx_rtd_theme"]
37
+
38
+ [tool.setuptools.package-data]
39
+ snap7 = ["py.typed", "lib/libsnap7.so", "lib/snap7.dll", "lib/libsnap7.dylib"]
40
+
41
+ [tool.setuptools.packages.find]
42
+ include = ["snap7*"]
43
+
44
+ [project.scripts]
45
+ snap7-server = "snap7.server.__main__:main"
46
+
47
+ [tool.pytest.ini_options]
48
+ testpaths = ["tests"]
49
+ markers =[
50
+ "client",
51
+ "common",
52
+ "logo",
53
+ "mainloop",
54
+ "partner",
55
+ "server",
56
+ "util"
57
+ ]
58
+
59
+ [tool.mypy]
60
+ ignore_missing_imports = true
61
+
62
+ [tool.ruff]
63
+ output-format = "full"
64
+ line-length = 130
65
+ target-version = "py38"
66
+
67
+ [lint]
68
+ ignore = []
69
+ mccabe.max-complexity = 10
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.1
2
+ Name: python-snap7
3
+ Version: 1.4
4
+ Summary: Python wrapper for the snap7 library
5
+ Author-email: Gijs Molenaar <gijsmolenaar@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/gijzelaerr/python-snap7
8
+ Project-URL: Documentation, https://python-snap7.readthedocs.io/en/latest/
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Console
11
+ Classifier: Topic :: System :: Hardware
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Manufacturing
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: POSIX
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Requires-Python: >=3.8
22
+ License-File: LICENSE
23
+ Provides-Extra: test
24
+ Requires-Dist: pytest; extra == "test"
25
+ Requires-Dist: mypy; extra == "test"
26
+ Requires-Dist: types-setuptools; extra == "test"
27
+ Requires-Dist: ruff; extra == "test"
28
+ Provides-Extra: cli
29
+ Requires-Dist: rich; extra == "cli"
30
+ Requires-Dist: click; extra == "cli"
31
+ Provides-Extra: doc
32
+ Requires-Dist: sphinx; extra == "doc"
33
+ Requires-Dist: sphinx_rtd_theme; extra == "doc"
@@ -1,8 +1,7 @@
1
1
  LICENSE
2
2
  MANIFEST.in
3
3
  README.rst
4
- setup.cfg
5
- setup.py
4
+ pyproject.toml
6
5
  python_snap7.egg-info/PKG-INFO
7
6
  python_snap7.egg-info/SOURCES.txt
8
7
  python_snap7.egg-info/dependency_links.txt
@@ -17,14 +16,17 @@ snap7/logo.py
17
16
  snap7/partner.py
18
17
  snap7/py.typed
19
18
  snap7/types.py
20
- snap7/util.py
21
19
  snap7/client/__init__.py
22
20
  snap7/server/__init__.py
23
21
  snap7/server/__main__.py
24
- test/test_client.py
25
- test/test_common.py
26
- test/test_logo_client.py
27
- test/test_mainloop.py
28
- test/test_partner.py
29
- test/test_server.py
30
- test/test_util.py
22
+ snap7/util/__init__.py
23
+ snap7/util/db.py
24
+ snap7/util/getters.py
25
+ snap7/util/setters.py
26
+ tests/test_client.py
27
+ tests/test_common.py
28
+ tests/test_logo_client.py
29
+ tests/test_mainloop.py
30
+ tests/test_partner.py
31
+ tests/test_server.py
32
+ tests/test_util.py
@@ -1,5 +1,6 @@
1
1
 
2
2
  [cli]
3
+ rich
3
4
  click
4
5
 
5
6
  [doc]
@@ -8,7 +9,6 @@ sphinx_rtd_theme
8
9
 
9
10
  [test]
10
11
  pytest
11
- pytest-asyncio
12
12
  mypy
13
- pycodestyle
14
13
  types-setuptools
14
+ ruff
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -1,7 +1,8 @@
1
1
  """
2
2
  The Snap7 Python library.
3
3
  """
4
- import pkg_resources
4
+
5
+ from importlib.metadata import version, PackageNotFoundError
5
6
 
6
7
  from . import client
7
8
  from . import common
@@ -11,9 +12,9 @@ from . import server
11
12
  from . import types
12
13
  from . import util
13
14
 
14
- __all__ = ['client', 'common', 'error', 'logo', 'server', 'types', 'util']
15
+ __all__ = ["client", "common", "error", "logo", "server", "types", "util"]
15
16
 
16
17
  try:
17
- __version__ = pkg_resources.require("python-snap7")[0].version
18
- except pkg_resources.DistributionNotFound:
18
+ __version__ = version("python-snap7")
19
+ except PackageNotFoundError:
19
20
  __version__ = "0.0rc0"
@@ -1,18 +1,20 @@
1
1
  """
2
2
  Snap7 client used for connection to a siemens 7 server.
3
3
  """
4
+
4
5
  import re
5
6
  import logging
6
- from ctypes import byref, create_string_buffer, sizeof
7
+ from ctypes import CFUNCTYPE, byref, create_string_buffer, sizeof
7
8
  from ctypes import Array, c_byte, c_char_p, c_int, c_int32, c_uint16, c_ulong, c_void_p
8
9
  from datetime import datetime
9
- from typing import List, Optional, Tuple, Union
10
+ from typing import Any, Callable, List, Optional, Tuple, Union
10
11
 
11
12
  from ..common import check_error, ipv4, load_library
12
13
  from ..types import S7SZL, Areas, BlocksList, S7CpInfo, S7CpuInfo, S7DataItem
13
14
  from ..types import S7OrderCode, S7Protection, S7SZLList, TS7BlockInfo, WordLen
14
15
  from ..types import S7Object, buffer_size, buffer_type, cpu_statuses, param_types
15
- from ..types import S7CpuInfo, RemotePort, wordlen_to_ctypes, block_types
16
+ from ..types import RemotePort, wordlen_to_ctypes, block_types
17
+
16
18
  logger = logging.getLogger(__name__)
17
19
 
18
20
 
@@ -68,8 +70,7 @@ class Client:
68
70
  self.destroy()
69
71
 
70
72
  def create(self):
71
- """Creates a SNAP7 client.
72
- """
73
+ """Creates a SNAP7 client."""
73
74
  logger.info("creating snap7 client")
74
75
  self._library.Cli_Create.restype = c_void_p
75
76
  self._pointer = S7Object(self._library.Cli_Create())
@@ -190,9 +191,7 @@ class Client:
190
191
  logger.info(f"connecting to {address}:{tcpport} rack {rack} slot {slot}")
191
192
 
192
193
  self.set_param(RemotePort, tcpport)
193
- return self._library.Cli_ConnectTo(
194
- self._pointer, c_char_p(address.encode()),
195
- c_int(rack), c_int(slot))
194
+ return self._library.Cli_ConnectTo(self._pointer, c_char_p(address.encode()), c_int(rack), c_int(slot))
196
195
 
197
196
  def db_read(self, db_number: int, start: int, size: int) -> bytearray:
198
197
  """Reads a part of a DB from a PLC
@@ -220,9 +219,7 @@ class Client:
220
219
 
221
220
  type_ = wordlen_to_ctypes[WordLen.Byte.value]
222
221
  data = (type_ * size)()
223
- result = (self._library.Cli_DBRead(
224
- self._pointer, db_number, start, size,
225
- byref(data)))
222
+ result = self._library.Cli_DBRead(self._pointer, db_number, start, size, byref(data))
226
223
  check_error(result, context="client")
227
224
  return bytearray(data)
228
225
 
@@ -250,8 +247,7 @@ class Client:
250
247
  size = len(data)
251
248
  cdata = (type_ * size).from_buffer_copy(data)
252
249
  logger.debug(f"db_write db_number:{db_number} start:{start} size:{size} data:{data}")
253
- return self._library.Cli_DBWrite(self._pointer, db_number, start, size,
254
- byref(cdata))
250
+ return self._library.Cli_DBWrite(self._pointer, db_number, start, size, byref(cdata))
255
251
 
256
252
  def delete(self, block_type: str, block_num: int) -> int:
257
253
  """Delete a block into AG.
@@ -283,11 +279,9 @@ class Client:
283
279
  _buffer = buffer_type()
284
280
  size = c_int(sizeof(_buffer))
285
281
  block_type = block_types[_type]
286
- result = self._library.Cli_FullUpload(self._pointer, block_type,
287
- block_num, byref(_buffer),
288
- byref(size))
282
+ result = self._library.Cli_FullUpload(self._pointer, block_type, block_num, byref(_buffer), byref(size))
289
283
  check_error(result, context="client")
290
- return bytearray(_buffer)[:size.value], size.value
284
+ return bytearray(_buffer)[: size.value], size.value
291
285
 
292
286
  def upload(self, block_num: int) -> bytearray:
293
287
  """Uploads a block from AG.
@@ -302,15 +296,14 @@ class Client:
302
296
  Buffer with the uploaded block.
303
297
  """
304
298
  logger.debug(f"db_upload block_num: {block_num}")
305
- block_type = block_types['DB']
299
+ block_type = block_types["DB"]
306
300
  _buffer = buffer_type()
307
301
  size = c_int(sizeof(_buffer))
308
302
 
309
- result = self._library.Cli_Upload(self._pointer, block_type, block_num,
310
- byref(_buffer), byref(size))
303
+ result = self._library.Cli_Upload(self._pointer, block_type, block_num, byref(_buffer), byref(size))
311
304
 
312
305
  check_error(result, context="client")
313
- logger.info(f'received {size} bytes')
306
+ logger.info(f"received {size} bytes")
314
307
  return bytearray(_buffer)
315
308
 
316
309
  @error_wrap
@@ -332,8 +325,7 @@ class Client:
332
325
  type_ = c_byte
333
326
  size = len(data)
334
327
  cdata = (type_ * len(data)).from_buffer_copy(data)
335
- return self._library.Cli_Download(self._pointer, block_num,
336
- byref(cdata), size)
328
+ return self._library.Cli_Download(self._pointer, block_num, byref(cdata), size)
337
329
 
338
330
  def db_get(self, db_number: int) -> bytearray:
339
331
  """Uploads a DB from AG using DBRead.
@@ -357,35 +349,33 @@ class Client:
357
349
  """
358
350
  logger.debug(f"db_get db_number: {db_number}")
359
351
  _buffer = buffer_type()
360
- result = self._library.Cli_DBGet(
361
- self._pointer, db_number, byref(_buffer),
362
- byref(c_int(buffer_size)))
352
+ result = self._library.Cli_DBGet(self._pointer, db_number, byref(_buffer), byref(c_int(buffer_size)))
363
353
  check_error(result, context="client")
364
354
  return bytearray(_buffer)
365
355
 
366
356
  def read_area(self, area: Areas, dbnumber: int, start: int, size: int) -> bytearray:
367
357
  """Reads a data area from a PLC
368
- With it you can read DB, Inputs, Outputs, Merkers, Timers and Counters.
358
+ With it you can read DB, Inputs, Outputs, Merkers, Timers and Counters.
369
359
 
370
- Args:
371
- area: area to be read from.
372
- dbnumber: number of the db to be read from. In case of Inputs, Marks or Outputs, this should be equal to 0.
373
- start: byte index to start reading.
374
- size: number of bytes to read.
360
+ Args:
361
+ area: area to be read from.
362
+ dbnumber: number of the db to be read from. In case of Inputs, Marks or Outputs, this should be equal to 0.
363
+ start: byte index to start reading.
364
+ size: number of bytes to read.
375
365
 
376
- Returns:
377
- Buffer with the data read.
366
+ Returns:
367
+ Buffer with the data read.
378
368
 
379
- Raises:
380
- :obj:`ValueError`: if the area is not defined in the `Areas`
369
+ Raises:
370
+ :obj:`ValueError`: if the area is not defined in the `Areas`
381
371
 
382
- Example:
383
- >>> import snap7
384
- >>> client = snap7.client.Client()
385
- >>> client.connect("192.168.0.1", 0, 0)
386
- >>> buffer = client.read_area(Areas.DB, 1, 10, 4) # Reads the DB number 1 from the byte 10 to the byte 14.
387
- >>> buffer
388
- bytearray(b'\\x00\\x00')
372
+ Example:
373
+ import snap7.util.db >>> import snap7
374
+ >>> client = snap7.client.Client()
375
+ >>> client.connect("192.168.0.1", 0, 0)
376
+ >>> buffer = client.read_area(snap7.util.db.DB, 1, 10, 4) # Reads the DB number 1 from the byte 10 to the byte 14.
377
+ >>> buffer
378
+ bytearray(b'\\x00\\x00')
389
379
  """
390
380
  if area not in Areas:
391
381
  raise ValueError(f"{area} is not implemented in types")
@@ -396,10 +386,12 @@ class Client:
396
386
  else:
397
387
  wordlen = WordLen.Byte
398
388
  type_ = wordlen_to_ctypes[wordlen.value]
399
- logger.debug(f"reading area: {area.name} dbnumber: {dbnumber} start: {start}: amount {size}: wordlen: {wordlen.name}={wordlen.value}")
389
+ logger.debug(
390
+ f"reading area: {area.name} dbnumber: {dbnumber} start: {start} amount: {size} "
391
+ f"wordlen: {wordlen.name}={wordlen.value}"
392
+ )
400
393
  data = (type_ * size)()
401
- result = self._library.Cli_ReadArea(self._pointer, area.value, dbnumber, start,
402
- size, wordlen.value, byref(data))
394
+ result = self._library.Cli_ReadArea(self._pointer, area.value, dbnumber, start, size, wordlen.value, byref(data))
403
395
  check_error(result, context="client")
404
396
  return bytearray(data)
405
397
 
@@ -417,11 +409,13 @@ class Client:
417
409
  Snap7 error code.
418
410
 
419
411
  Exmaple:
412
+ >>> import snap7.util.db
420
413
  >>> import snap7
421
414
  >>> client = snap7.client.Client()
422
415
  >>> client.connect("192.168.0.1", 0, 0)
423
416
  >>> buffer = bytearray([0b00000001])
424
- >>> client.write_area(Areas.DB, 1, 10, buffer) # Writes the bit 0 of the byte 10 from the DB number 1 to TRUE.
417
+ # Writes the bit 0 of the byte 10 from the DB number 1 to TRUE.
418
+ >>> client.write_area(snap7.util.DB, 1, 10, buffer)
425
419
  """
426
420
  if area == Areas.TM:
427
421
  wordlen = WordLen.Timer
@@ -431,11 +425,12 @@ class Client:
431
425
  wordlen = WordLen.Byte
432
426
  type_ = wordlen_to_ctypes[WordLen.Byte.value]
433
427
  size = len(data)
434
- logger.debug(f"writing area: {area.name} dbnumber: {dbnumber} start: {start}: size {size}: "
435
- f"wordlen {wordlen.name}={wordlen.value} type: {type_}")
428
+ logger.debug(
429
+ f"writing area: {area.name} dbnumber: {dbnumber} start: {start}: size {size}: "
430
+ f"wordlen {wordlen.name}={wordlen.value} type: {type_}"
431
+ )
436
432
  cdata = (type_ * len(data)).from_buffer_copy(data)
437
- return self._library.Cli_WriteArea(self._pointer, area.value, dbnumber, start,
438
- size, wordlen.value, byref(cdata))
433
+ return self._library.Cli_WriteArea(self._pointer, area.value, dbnumber, start, size, wordlen.value, byref(cdata))
439
434
 
440
435
  def read_multi_vars(self, items) -> Tuple[int, S7DataItem]:
441
436
  """Reads different kind of variables from a PLC simultaneously.
@@ -446,8 +441,7 @@ class Client:
446
441
  Returns:
447
442
  Tuple with the return code from the snap7 library and the list of items.
448
443
  """
449
- result = self._library.Cli_ReadMultiVars(self._pointer, byref(items),
450
- c_int32(len(items)))
444
+ result = self._library.Cli_ReadMultiVars(self._pointer, byref(items), c_int32(len(items)))
451
445
  check_error(result, context="client")
452
446
  return result, items
453
447
 
@@ -494,10 +488,7 @@ class Client:
494
488
 
495
489
  data = (c_uint16 * size)()
496
490
  count = c_int(size)
497
- result = self._library.Cli_ListBlocksOfType(
498
- self._pointer, _blocktype,
499
- byref(data),
500
- byref(count))
491
+ result = self._library.Cli_ListBlocksOfType(self._pointer, _blocktype, byref(data), byref(count))
501
492
 
502
493
  logger.debug(f"number of items found: {count}")
503
494
 
@@ -563,8 +554,7 @@ class Client:
563
554
  """
564
555
  if len(password) > 8:
565
556
  raise ValueError("Maximum password length is 8")
566
- return self._library.Cli_SetSessionPassword(self._pointer,
567
- c_char_p(password.encode()))
557
+ return self._library.Cli_SetSessionPassword(self._pointer, c_char_p(password.encode()))
568
558
 
569
559
  @error_wrap
570
560
  def clear_session_password(self) -> int:
@@ -593,14 +583,12 @@ class Client:
593
583
  """
594
584
  if not re.match(ipv4, address):
595
585
  raise ValueError(f"{address} is invalid ipv4")
596
- result = self._library.Cli_SetConnectionParams(self._pointer, address,
597
- c_uint16(local_tsap),
598
- c_uint16(remote_tsap))
586
+ result = self._library.Cli_SetConnectionParams(self._pointer, address, c_uint16(local_tsap), c_uint16(remote_tsap))
599
587
  if result != 0:
600
588
  raise ValueError("The parameter was invalid")
601
589
 
602
590
  def set_connection_type(self, connection_type: int):
603
- """ Sets the connection resource type, i.e the way in which the Clients connects to a PLC.
591
+ """Sets the connection resource type, i.e the way in which the Clients connects to a PLC.
604
592
 
605
593
  Args:
606
594
  connection_type: 1 for PG, 2 for OP, 3 to 10 for S7 Basic
@@ -609,8 +597,7 @@ class Client:
609
597
  :obj:`ValueError`: if the result of setting the connection type is
610
598
  different than 0.
611
599
  """
612
- result = self._library.Cli_SetConnectionType(self._pointer,
613
- c_uint16(connection_type))
600
+ result = self._library.Cli_SetConnectionType(self._pointer, c_uint16(connection_type))
614
601
  if result != 0:
615
602
  raise ValueError("The parameter was invalid")
616
603
 
@@ -642,8 +629,7 @@ class Client:
642
629
  type_ = wordlen_to_ctypes[wordlen.value]
643
630
  data = (type_ * size)()
644
631
  logger.debug(f"ab_read: start: {start}: size {size}: ")
645
- result = self._library.Cli_ABRead(self._pointer, start, size,
646
- byref(data))
632
+ result = self._library.Cli_ABRead(self._pointer, start, size, byref(data))
647
633
  check_error(result, context="client")
648
634
  return bytearray(data)
649
635
 
@@ -662,8 +648,7 @@ class Client:
662
648
  size = len(data)
663
649
  cdata = (type_ * size).from_buffer_copy(data)
664
650
  logger.debug(f"ab write: start: {start}: size: {size}: ")
665
- return self._library.Cli_ABWrite(
666
- self._pointer, start, size, byref(cdata))
651
+ return self._library.Cli_ABWrite(self._pointer, start, size, byref(cdata))
667
652
 
668
653
  def as_ab_read(self, start: int, size: int, data) -> int:
669
654
  """Reads a part of IPU area from a PLC asynchronously.
@@ -677,8 +662,7 @@ class Client:
677
662
  Snap7 code.
678
663
  """
679
664
  logger.debug(f"ab_read: start: {start}: size {size}: ")
680
- result = self._library.Cli_AsABRead(self._pointer, start, size,
681
- byref(data))
665
+ result = self._library.Cli_AsABRead(self._pointer, start, size, byref(data))
682
666
  check_error(result, context="client")
683
667
  return result
684
668
 
@@ -697,13 +681,12 @@ class Client:
697
681
  size = len(data)
698
682
  cdata = (type_ * size).from_buffer_copy(data)
699
683
  logger.debug(f"ab write: start: {start}: size: {size}: ")
700
- result = self._library.Cli_AsABWrite(
701
- self._pointer, start, size, byref(cdata))
684
+ result = self._library.Cli_AsABWrite(self._pointer, start, size, byref(cdata))
702
685
  check_error(result, context="client")
703
686
  return result
704
687
 
705
688
  def as_compress(self, time: int) -> int:
706
- """ Performs the Compress action asynchronously.
689
+ """Performs the Compress action asynchronously.
707
690
 
708
691
  Args:
709
692
  time: timeout.
@@ -927,12 +910,7 @@ class Client:
927
910
  check_error(result, context="client")
928
911
 
929
912
  return datetime(
930
- year=buffer[5] + 1900,
931
- month=buffer[4] + 1,
932
- day=buffer[3],
933
- hour=buffer[2],
934
- minute=buffer[1],
935
- second=buffer[0]
913
+ year=buffer[5] + 1900, month=buffer[4] + 1, day=buffer[3], hour=buffer[2], minute=buffer[1], second=buffer[0]
936
914
  )
937
915
 
938
916
  @error_wrap
@@ -969,10 +947,30 @@ class Client:
969
947
  check_error(result, context="client")
970
948
  return result
971
949
 
972
- def set_as_callback(self, pfn_clicompletion, p_usr):
973
- # Cli_SetAsCallback
974
- result = self._library.Cli_SetAsCallback(self._pointer, pfn_clicompletion, p_usr)
975
- check_error(result, context='client')
950
+ def set_as_callback(self, call_back: Callable[..., Any]) -> int:
951
+ logger.info("setting event callback")
952
+ callback_wrap: Callable[..., Any] = CFUNCTYPE(None, c_void_p, c_int, c_int)
953
+
954
+ def wrapper(usrptr: Optional[c_void_p], op_code: int, op_result: int) -> int:
955
+ """Wraps python function into a ctypes function
956
+
957
+ Args:
958
+ usrptr: not used
959
+ op_code:
960
+ op_result:
961
+
962
+ Returns:
963
+ Should return an int
964
+ """
965
+ logger.info(f"callback event: op_code: {op_code} op_result: {op_result}")
966
+ call_back(op_code, op_result)
967
+ return 0
968
+
969
+ self._callback = callback_wrap(wrapper)
970
+ usrPtr = c_void_p()
971
+
972
+ result = self._library.Cli_SetAsCallback(self._pointer, self._callback, usrPtr)
973
+ check_error(result, context="client")
976
974
  return result
977
975
 
978
976
  def wait_as_completion(self, timeout: int) -> int:
@@ -1017,7 +1015,10 @@ class Client:
1017
1015
  Returns:
1018
1016
  Snap7 code.
1019
1017
  """
1020
- logger.debug(f"reading area: {area.name} dbnumber: {dbnumber} start: {start}: amount {size}: wordlen: {wordlen.name}={wordlen.value}")
1018
+ logger.debug(
1019
+ f"reading area: {area.name} dbnumber: {dbnumber} start: {start} amount: {size} "
1020
+ f"wordlen: {wordlen.name}={wordlen.value}"
1021
+ )
1021
1022
  result = self._library.Cli_AsReadArea(self._pointer, area.value, dbnumber, start, size, wordlen.value, pusrdata)
1022
1023
  check_error(result, context="client")
1023
1024
  return result
@@ -1050,8 +1051,9 @@ class Client:
1050
1051
  Snap7 code.
1051
1052
  """
1052
1053
  type_ = wordlen_to_ctypes[WordLen.Byte.value]
1053
- logger.debug(f"writing area: {area.name} dbnumber: {dbnumber} start: {start}: size {size}: "
1054
- f"wordlen {wordlen} type: {type_}")
1054
+ logger.debug(
1055
+ f"writing area: {area.name} dbnumber: {dbnumber} start: {start}: size {size}: " f"wordlen {wordlen} type: {type_}"
1056
+ )
1055
1057
  cdata = (type_ * len(pusrdata)).from_buffer_copy(pusrdata)
1056
1058
  res = self._library.Cli_AsWriteArea(self._pointer, area.value, dbnumber, start, size, wordlen.value, byref(cdata))
1057
1059
  check_error(res, context="client")
@@ -1238,7 +1240,7 @@ class Client:
1238
1240
  Returns:
1239
1241
  Snap7 code.
1240
1242
  """
1241
- block_type = block_types['DB']
1243
+ block_type = block_types["DB"]
1242
1244
  result = self._library.Cli_AsUpload(self._pointer, block_type, block_num, byref(_buffer), byref(size))
1243
1245
  check_error(result, context="client")
1244
1246
  return result
@@ -1350,7 +1352,7 @@ class Client:
1350
1352
  text = create_string_buffer(buffer_size)
1351
1353
  response = self._library.Cli_ErrorText(error_code, byref(text), text_length)
1352
1354
  check_error(response)
1353
- result = bytearray(text)[:text_length.value].decode().strip('\x00')
1355
+ result = bytearray(text)[: text_length.value].decode().strip("\x00")
1354
1356
  return result
1355
1357
 
1356
1358
  def get_cp_info(self) -> S7CpInfo:
@@ -1437,7 +1439,7 @@ class Client:
1437
1439
  cdata = (c_byte * len(data)).from_buffer_copy(data)
1438
1440
  response = self._library.Cli_IsoExchangeBuffer(self._pointer, byref(cdata), byref(size))
1439
1441
  check_error(response)
1440
- result = bytearray(cdata)[:size.value]
1442
+ result = bytearray(cdata)[: size.value]
1441
1443
  return result
1442
1444
 
1443
1445
  def mb_read(self, start: int, size: int) -> bytearray:
@@ -1499,7 +1501,7 @@ class Client:
1499
1501
  items_count = c_int(sizeof(szl_list))
1500
1502
  response = self._library.Cli_ReadSZLList(self._pointer, byref(szl_list), byref(items_count))
1501
1503
  check_error(response, context="client")
1502
- result = bytearray(szl_list.List)[:items_count.value]
1504
+ result = bytearray(szl_list.List)[: items_count.value]
1503
1505
  return result
1504
1506
 
1505
1507
  def set_plc_system_datetime(self) -> int: