soemx 0.1.0__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 (71) hide show
  1. soemx-0.1.0/MANIFEST.in +11 -0
  2. soemx-0.1.0/PKG-INFO +109 -0
  3. soemx-0.1.0/README.md +101 -0
  4. soemx-0.1.0/pyproject.toml +15 -0
  5. soemx-0.1.0/setup.cfg +4 -0
  6. soemx-0.1.0/setup.py +59 -0
  7. soemx-0.1.0/soemx/__init__.py +70 -0
  8. soemx-0.1.0/soemx/adapters.py +18 -0
  9. soemx-0.1.0/soemx/errors.py +75 -0
  10. soemx-0.1.0/soemx/soemx.c +74965 -0
  11. soemx-0.1.0/soemx/soemx.pyx +1615 -0
  12. soemx-0.1.0/soemx/soemx_native.c +265 -0
  13. soemx-0.1.0/soemx/soemx_native.h +76 -0
  14. soemx-0.1.0/soemx/status.py +19 -0
  15. soemx-0.1.0/soemx.egg-info/PKG-INFO +109 -0
  16. soemx-0.1.0/soemx.egg-info/SOURCES.txt +69 -0
  17. soemx-0.1.0/soemx.egg-info/dependency_links.txt +1 -0
  18. soemx-0.1.0/soemx.egg-info/top_level.txt +1 -0
  19. soemx-0.1.0/tests/test_public_api.py +21 -0
  20. soemx-0.1.0/tests/test_status.py +66 -0
  21. soemx-0.1.0/vendor/SOEM/build/include/soem/ec_options.h +128 -0
  22. soemx-0.1.0/vendor/SOEM/include/soem/ec_base.h +42 -0
  23. soemx-0.1.0/vendor/SOEM/include/soem/ec_coe.h +73 -0
  24. soemx-0.1.0/vendor/SOEM/include/soem/ec_config.h +31 -0
  25. soemx-0.1.0/vendor/SOEM/include/soem/ec_dc.h +27 -0
  26. soemx-0.1.0/vendor/SOEM/include/soem/ec_eoe.h +213 -0
  27. soemx-0.1.0/vendor/SOEM/include/soem/ec_foe.h +27 -0
  28. soemx-0.1.0/vendor/SOEM/include/soem/ec_main.h +624 -0
  29. soemx-0.1.0/vendor/SOEM/include/soem/ec_options.h.in +128 -0
  30. soemx-0.1.0/vendor/SOEM/include/soem/ec_print.h +30 -0
  31. soemx-0.1.0/vendor/SOEM/include/soem/ec_soe.h +121 -0
  32. soemx-0.1.0/vendor/SOEM/include/soem/ec_type.h +551 -0
  33. soemx-0.1.0/vendor/SOEM/include/soem/soem.h +28 -0
  34. soemx-0.1.0/vendor/SOEM/osal/linux/osal.c +154 -0
  35. soemx-0.1.0/vendor/SOEM/osal/linux/osal_defs.h +46 -0
  36. soemx-0.1.0/vendor/SOEM/osal/osal.h +222 -0
  37. soemx-0.1.0/vendor/SOEM/osal/win32/osal.c +176 -0
  38. soemx-0.1.0/vendor/SOEM/osal/win32/osal_defs.h +51 -0
  39. soemx-0.1.0/vendor/SOEM/oshw/linux/nicdrv.c +636 -0
  40. soemx-0.1.0/vendor/SOEM/oshw/linux/nicdrv.h +108 -0
  41. soemx-0.1.0/vendor/SOEM/oshw/linux/oshw.c +116 -0
  42. soemx-0.1.0/vendor/SOEM/oshw/linux/oshw.h +31 -0
  43. soemx-0.1.0/vendor/SOEM/oshw/win32/nicdrv.c +599 -0
  44. soemx-0.1.0/vendor/SOEM/oshw/win32/nicdrv.h +111 -0
  45. soemx-0.1.0/vendor/SOEM/oshw/win32/oshw.c +120 -0
  46. soemx-0.1.0/vendor/SOEM/oshw/win32/oshw.h +31 -0
  47. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/Packet32.h +359 -0
  48. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/Win32-Extensions.h +113 -0
  49. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/bittypes.h +137 -0
  50. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/ip6_misc.h +163 -0
  51. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/bluetooth.h +48 -0
  52. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/bpf.h +934 -0
  53. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/namedb.h +89 -0
  54. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/pcap.h +407 -0
  55. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/sll.h +129 -0
  56. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/usb.h +90 -0
  57. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/vlan.h +46 -0
  58. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap-bpf.h +47 -0
  59. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap-namedb.h +42 -0
  60. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap-stdinc.h +93 -0
  61. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap.h +45 -0
  62. soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/remote-ext.h +444 -0
  63. soemx-0.1.0/vendor/SOEM/src/ec_base.c +534 -0
  64. soemx-0.1.0/vendor/SOEM/src/ec_coe.c +1380 -0
  65. soemx-0.1.0/vendor/SOEM/src/ec_config.c +1653 -0
  66. soemx-0.1.0/vendor/SOEM/src/ec_dc.c +428 -0
  67. soemx-0.1.0/vendor/SOEM/src/ec_eoe.c +665 -0
  68. soemx-0.1.0/vendor/SOEM/src/ec_foe.c +416 -0
  69. soemx-0.1.0/vendor/SOEM/src/ec_main.c +2705 -0
  70. soemx-0.1.0/vendor/SOEM/src/ec_print.c +378 -0
  71. soemx-0.1.0/vendor/SOEM/src/ec_soe.c +379 -0
@@ -0,0 +1,11 @@
1
+ include README.md pyproject.toml setup.py
2
+ recursive-include soemx *.py *.pyx *.c *.h
3
+ recursive-include tests *.py
4
+ recursive-include vendor/SOEM/include *.h *.in
5
+ recursive-include vendor/SOEM/src *.c *.h
6
+ recursive-include vendor/SOEM/osal/linux *.c *.h
7
+ recursive-include vendor/SOEM/osal/win32 *.c *.h
8
+ include vendor/SOEM/osal/osal.h
9
+ recursive-include vendor/SOEM/oshw/linux *.c *.h
10
+ recursive-include vendor/SOEM/oshw/win32 *.c *.h
11
+ recursive-include vendor/SOEM/build/include *.h
soemx-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.4
2
+ Name: soemx
3
+ Version: 0.1.0
4
+ Summary: Cython bindings for SOEM with EoE support
5
+ Author: soemx contributors
6
+ Requires-Python: >=3.9
7
+ Description-Content-Type: text/markdown
8
+
9
+ # soemx
10
+
11
+ [![CI](https://github.com/11cookies11/soemx/actions/workflows/ci.yml/badge.svg)](https://github.com/11cookies11/soemx/actions/workflows/ci.yml)
12
+ [![PyPI](https://img.shields.io/pypi/v/soemx.svg)](https://pypi.org/project/soemx/)
13
+ [![Python](https://img.shields.io/pypi/pyversions/soemx.svg)](https://pypi.org/project/soemx/)
14
+
15
+ `soemx` 是一个面向个人项目和工业自动化实验的 SOEM Python 封装,使用
16
+ Cython 将 SOEM 2.x 的 EtherCAT Master 能力暴露给 Python,并额外提供 EoE
17
+ (Ethernet over EtherCAT)接口。
18
+
19
+ 项目仍在积极开发中,API 可能发生变化。没有 EtherCAT 硬件时,可以运行构建
20
+ 和单元测试;真实的 PDO、Mailbox 和 EoE 通信需要兼容的网卡和从站设备。
21
+
22
+ Python bindings for the [Simple Open EtherCAT Master (SOEM)](https://github.com/OpenEtherCATsociety/SOEM), implemented with Cython.
23
+
24
+ The project is intended to provide a complete, Python-friendly API for EtherCAT master operations, including EoE (Ethernet over EtherCAT), which is not currently exposed by PySOEM.
25
+
26
+ ## Installation
27
+
28
+ ```powershell
29
+ python -m pip install soemx
30
+ ```
31
+
32
+ 当前 PyPI 发布的是开发版本。没有对应平台 wheel 时,pip 会从源码构建扩展,
33
+ 因此 Linux 需要 C 编译器、Cython 和 `libpcap-dev`;Windows 需要 Visual Studio
34
+ C++ 构建工具以及运行时使用的 Npcap(WinPcap API 兼容模式)。
35
+
36
+ ## Current API
37
+
38
+ The Cython extension currently provides:
39
+
40
+ - `Master.open()` / `close()` and context-manager support, including optional redundancy
41
+ - `find_adapters()` returning `Adapter` objects with `name` and `desc` attributes
42
+ - slave discovery, state control, AL diagnostics and EEPROM access
43
+ - mapped process data through `Master.io_map`, `Slave.inputs` and `Slave.outputs`
44
+ - cyclic and grouped process-data exchange, one-shot `exchange_processdata()` /
45
+ `exchange_processdata_group()`, direct `rxpdo()` / `txpdo()` access
46
+ - actual PDO map length through `Master.mapped_size`, plus writable memory views
47
+ - SDO / CoE, SDO information discovery, FoE and SoE
48
+ - configurable default SDO read/write timeouts through `Master`
49
+ - consistent GIL release for blocking native operations through
50
+ `Master.always_release_gil` and `Master.check_release_gil()`
51
+ - EoE frame send/receive and raw mailbox transport
52
+ - distributed clocks, DC sync0, overlap/packed mapping and queued errors
53
+ - parsed slave mailbox parameters (`mailbox_out_address`, `mailbox_out_size`,
54
+ `mailbox_in_address`, `mailbox_in_size`) for diagnostics
55
+ - mailbox layout updates through `Slave.amend_mbx()` before mailbox use
56
+
57
+ PySOEM is used as a practical API reference, but `soemx` is free to improve the
58
+ interface where a clearer or safer design is possible. Raw byte-oriented methods
59
+ are used where Python cannot know a device's object data type. In addition to
60
+ normal slave-indexed EEPROM access, the master exposes direct AP and FP EEPROM
61
+ operations for diagnostics.
62
+
63
+ EoE channels accept any bytes-like frame buffer and expose IPv4 configuration
64
+ through `set_ip()` / `get_ip()`. Emergency mailbox notifications are delivered
65
+ as structured `Emergency` objects through Master-level or per-slave callbacks.
66
+ Raw records from `pop_error()` retain the associated register and auxiliary
67
+ word fields for diagnostics.
68
+
69
+ ## Build and runtime
70
+
71
+ Build the extension on Windows with Visual Studio and Cython:
72
+
73
+ ```powershell
74
+ python setup.py build_ext --inplace
75
+ python -m pytest -q
76
+ ```
77
+
78
+ Linux builds use SOEM's raw-socket backend and require the system `libpcap`
79
+ development package and a compiler.
80
+
81
+ The Windows backend links against Npcap/WinPcap's `wpcap.dll` and `Packet.dll`.
82
+ These DLLs are required when opening a network adapter, but pure Python modules
83
+ and the test suite can be used without EtherCAT hardware or the packet runtime.
84
+
85
+ Physical EtherCAT, PDO, mailbox and EoE behavior still requires a compatible
86
+ network adapter and at least one EtherCAT slave.
87
+
88
+ ## Minimal EoE example
89
+
90
+ ```python
91
+ import soemx
92
+
93
+ for adapter in soemx.find_adapters():
94
+ print(adapter.name, adapter.desc)
95
+
96
+ with soemx.open("YOUR_INTERFACE") as master:
97
+ if master.config_init() <= 0:
98
+ raise RuntimeError("no EtherCAT slaves found")
99
+ channel = master.slaves[0].eoe()
100
+ channel.set_ip(b"192.168.1.20", b"255.255.255.0", b"192.168.1.1")
101
+ ethernet_frame = b"replace with a complete Ethernet frame"
102
+ channel.send(ethernet_frame)
103
+ frame = channel.receive()
104
+ ```
105
+
106
+ The example requires an EoE-capable slave and a WinPcap-compatible Npcap
107
+ installation on Windows (or the Linux packet-capture development package).
108
+
109
+ SOEM is included under `vendor/SOEM` for development and is licensed separately. See its `LICENSE.md`.
soemx-0.1.0/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # soemx
2
+
3
+ [![CI](https://github.com/11cookies11/soemx/actions/workflows/ci.yml/badge.svg)](https://github.com/11cookies11/soemx/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/soemx.svg)](https://pypi.org/project/soemx/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/soemx.svg)](https://pypi.org/project/soemx/)
6
+
7
+ `soemx` 是一个面向个人项目和工业自动化实验的 SOEM Python 封装,使用
8
+ Cython 将 SOEM 2.x 的 EtherCAT Master 能力暴露给 Python,并额外提供 EoE
9
+ (Ethernet over EtherCAT)接口。
10
+
11
+ 项目仍在积极开发中,API 可能发生变化。没有 EtherCAT 硬件时,可以运行构建
12
+ 和单元测试;真实的 PDO、Mailbox 和 EoE 通信需要兼容的网卡和从站设备。
13
+
14
+ Python bindings for the [Simple Open EtherCAT Master (SOEM)](https://github.com/OpenEtherCATsociety/SOEM), implemented with Cython.
15
+
16
+ The project is intended to provide a complete, Python-friendly API for EtherCAT master operations, including EoE (Ethernet over EtherCAT), which is not currently exposed by PySOEM.
17
+
18
+ ## Installation
19
+
20
+ ```powershell
21
+ python -m pip install soemx
22
+ ```
23
+
24
+ 当前 PyPI 发布的是开发版本。没有对应平台 wheel 时,pip 会从源码构建扩展,
25
+ 因此 Linux 需要 C 编译器、Cython 和 `libpcap-dev`;Windows 需要 Visual Studio
26
+ C++ 构建工具以及运行时使用的 Npcap(WinPcap API 兼容模式)。
27
+
28
+ ## Current API
29
+
30
+ The Cython extension currently provides:
31
+
32
+ - `Master.open()` / `close()` and context-manager support, including optional redundancy
33
+ - `find_adapters()` returning `Adapter` objects with `name` and `desc` attributes
34
+ - slave discovery, state control, AL diagnostics and EEPROM access
35
+ - mapped process data through `Master.io_map`, `Slave.inputs` and `Slave.outputs`
36
+ - cyclic and grouped process-data exchange, one-shot `exchange_processdata()` /
37
+ `exchange_processdata_group()`, direct `rxpdo()` / `txpdo()` access
38
+ - actual PDO map length through `Master.mapped_size`, plus writable memory views
39
+ - SDO / CoE, SDO information discovery, FoE and SoE
40
+ - configurable default SDO read/write timeouts through `Master`
41
+ - consistent GIL release for blocking native operations through
42
+ `Master.always_release_gil` and `Master.check_release_gil()`
43
+ - EoE frame send/receive and raw mailbox transport
44
+ - distributed clocks, DC sync0, overlap/packed mapping and queued errors
45
+ - parsed slave mailbox parameters (`mailbox_out_address`, `mailbox_out_size`,
46
+ `mailbox_in_address`, `mailbox_in_size`) for diagnostics
47
+ - mailbox layout updates through `Slave.amend_mbx()` before mailbox use
48
+
49
+ PySOEM is used as a practical API reference, but `soemx` is free to improve the
50
+ interface where a clearer or safer design is possible. Raw byte-oriented methods
51
+ are used where Python cannot know a device's object data type. In addition to
52
+ normal slave-indexed EEPROM access, the master exposes direct AP and FP EEPROM
53
+ operations for diagnostics.
54
+
55
+ EoE channels accept any bytes-like frame buffer and expose IPv4 configuration
56
+ through `set_ip()` / `get_ip()`. Emergency mailbox notifications are delivered
57
+ as structured `Emergency` objects through Master-level or per-slave callbacks.
58
+ Raw records from `pop_error()` retain the associated register and auxiliary
59
+ word fields for diagnostics.
60
+
61
+ ## Build and runtime
62
+
63
+ Build the extension on Windows with Visual Studio and Cython:
64
+
65
+ ```powershell
66
+ python setup.py build_ext --inplace
67
+ python -m pytest -q
68
+ ```
69
+
70
+ Linux builds use SOEM's raw-socket backend and require the system `libpcap`
71
+ development package and a compiler.
72
+
73
+ The Windows backend links against Npcap/WinPcap's `wpcap.dll` and `Packet.dll`.
74
+ These DLLs are required when opening a network adapter, but pure Python modules
75
+ and the test suite can be used without EtherCAT hardware or the packet runtime.
76
+
77
+ Physical EtherCAT, PDO, mailbox and EoE behavior still requires a compatible
78
+ network adapter and at least one EtherCAT slave.
79
+
80
+ ## Minimal EoE example
81
+
82
+ ```python
83
+ import soemx
84
+
85
+ for adapter in soemx.find_adapters():
86
+ print(adapter.name, adapter.desc)
87
+
88
+ with soemx.open("YOUR_INTERFACE") as master:
89
+ if master.config_init() <= 0:
90
+ raise RuntimeError("no EtherCAT slaves found")
91
+ channel = master.slaves[0].eoe()
92
+ channel.set_ip(b"192.168.1.20", b"255.255.255.0", b"192.168.1.1")
93
+ ethernet_frame = b"replace with a complete Ethernet frame"
94
+ channel.send(ethernet_frame)
95
+ frame = channel.receive()
96
+ ```
97
+
98
+ The example requires an EoE-capable slave and a WinPcap-compatible Npcap
99
+ installation on Windows (or the Linux packet-capture development package).
100
+
101
+ SOEM is included under `vendor/SOEM` for development and is licensed separately. See its `LICENSE.md`.
@@ -0,0 +1,15 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel", "Cython>=3.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "soemx"
7
+ version = "0.1.0"
8
+ description = "Cython bindings for SOEM with EoE support"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ authors = [{name = "soemx contributors"}]
12
+ dependencies = []
13
+
14
+ [tool.setuptools]
15
+ packages = ["soemx"]
soemx-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
soemx-0.1.0/setup.py ADDED
@@ -0,0 +1,59 @@
1
+ from pathlib import Path
2
+ import os
3
+ import sys
4
+ from setuptools import Extension, setup
5
+ from Cython.Build import cythonize
6
+
7
+ root = Path(__file__).parent.resolve()
8
+ soem = root / "vendor" / "SOEM"
9
+ sources = ["soemx/soemx.pyx", "soemx/soemx_native.c"]
10
+ sources += [f"vendor/SOEM/src/{name}" for name in [
11
+ "ec_base.c", "ec_coe.c", "ec_config.c", "ec_dc.c", "ec_eoe.c",
12
+ "ec_foe.c", "ec_main.c", "ec_print.c", "ec_soe.c"]]
13
+ if sys.platform == "win32":
14
+ platform_sources = [
15
+ "vendor/SOEM/osal/win32/osal.c",
16
+ "vendor/SOEM/oshw/win32/nicdrv.c",
17
+ "vendor/SOEM/oshw/win32/oshw.c",
18
+ ]
19
+ platform_includes = [
20
+ soem / "osal" / "win32",
21
+ soem / "oshw" / "win32",
22
+ soem / "oshw" / "win32" / "wpcap" / "Include",
23
+ ]
24
+ platform_libraries = ["wpcap", "Packet", "winmm", "ws2_32"]
25
+ platform_library_dirs = [soem / "oshw" / "win32" / "wpcap" / "Lib" / "x64"]
26
+ elif sys.platform.startswith("linux"):
27
+ platform_sources = [
28
+ "vendor/SOEM/osal/linux/osal.c",
29
+ "vendor/SOEM/oshw/linux/nicdrv.c",
30
+ "vendor/SOEM/oshw/linux/oshw.c",
31
+ ]
32
+ platform_includes = [soem / "osal" / "linux", soem / "oshw" / "linux"]
33
+ platform_libraries = ["pcap", "pthread"]
34
+ platform_library_dirs = []
35
+ else:
36
+ raise RuntimeError("soemx currently supports Windows and Linux builds")
37
+ sources += platform_sources
38
+ # setuptools requires source entries to be relative to setup.py. The vendored
39
+ # SOEM paths are normalized to POSIX separators for manifest generation.
40
+ sources = [path.replace("\\", "/") for path in sources]
41
+
42
+ extension = Extension(
43
+ "soemx._soemx",
44
+ sources=sources,
45
+ include_dirs=[str(soem / "include"), str(soem / "build" / "include"), str(soem / "osal"), *(str(path) for path in platform_includes), str(root / "soemx")],
46
+ library_dirs=[str(path) for path in platform_library_dirs],
47
+ libraries=platform_libraries,
48
+ )
49
+
50
+ extensions = cythonize([extension], language_level=3)
51
+ for ext in extensions:
52
+ ext.sources = [os.path.relpath(source, root).replace("\\", "/")
53
+ for source in ext.sources]
54
+
55
+ setup(
56
+ ext_modules=extensions,
57
+ include_package_data=False,
58
+ exclude_package_data={"soemx": ["*.c", "*.h"]},
59
+ )
@@ -0,0 +1,70 @@
1
+ """Python interface for SOEM, under active development."""
2
+
3
+ from .errors import (SoemxError, SdoError, SdoInfoError, MailboxError,
4
+ PacketError, ConfigMapError, EepromError, WkcError,
5
+ NetworkInterfaceNotOpenError, Emergency)
6
+ from .status import al_status_code_to_string
7
+ from .adapters import Adapter
8
+
9
+ NONE_STATE = 0x00
10
+ INIT_STATE = 0x01
11
+ PREOP_STATE = 0x02
12
+ BOOT_STATE = 0x03
13
+ SAFEOP_STATE = 0x04
14
+ OP_STATE = 0x08
15
+ STATE_ACK = 0x10
16
+ STATE_ERROR = 0x10
17
+
18
+ # Common EtherCAT register and CoE capability constants exposed by PySOEM.
19
+ ECT_REG_WD_DIV = 0x0400
20
+ ECT_REG_WD_TIME_PDI = 0x0410
21
+ ECT_REG_WD_TIME_PROCESSDATA = 0x0420
22
+ ECT_REG_SM0 = 0x0800
23
+ ECT_REG_SM1 = ECT_REG_SM0 + 0x08
24
+ ECT_COEDET_SDO = 0x01
25
+ ECT_COEDET_SDOINFO = 0x02
26
+ ECT_COEDET_PDOASSIGN = 0x04
27
+ ECT_COEDET_PDOCONFIG = 0x08
28
+ ECT_COEDET_UPLOAD = 0x10
29
+ ECT_COEDET_SDOCA = 0x20
30
+
31
+ __version__ = "0.1.0"
32
+
33
+
34
+ def _load_native():
35
+ try:
36
+ from . import _soemx
37
+ except ImportError as exc:
38
+ raise RuntimeError(
39
+ "unable to load the soemx native extension; install Npcap "
40
+ "with WinPcap API-compatible mode on Windows"
41
+ ) from exc
42
+ return _soemx
43
+
44
+
45
+ def __getattr__(name):
46
+ if name in ("Master", "find_adapters"):
47
+ native = _load_native()
48
+ return native.Master if name == "Master" else native.find_adapters
49
+ raise AttributeError(name)
50
+
51
+
52
+ def open(interface: str, interface2=None):
53
+ """Open an EtherCAT master and optionally configure a redundant interface."""
54
+ Master = _load_native().Master
55
+ master = Master()
56
+ try:
57
+ master.open(interface, interface2)
58
+ except Exception:
59
+ master.close()
60
+ raise
61
+ return master
62
+
63
+ __all__ = ["Master", "find_adapters", "open", "Adapter", "__version__", "al_status_code_to_string", "SoemxError", "SdoError", "SdoInfoError", "Emergency",
64
+ "MailboxError", "PacketError", "ConfigMapError", "EepromError",
65
+ "WkcError", "NetworkInterfaceNotOpenError", "NONE_STATE", "INIT_STATE",
66
+ "PREOP_STATE", "BOOT_STATE", "SAFEOP_STATE", "OP_STATE",
67
+ "STATE_ACK", "STATE_ERROR", "ECT_REG_WD_DIV", "ECT_REG_WD_TIME_PDI",
68
+ "ECT_REG_WD_TIME_PROCESSDATA", "ECT_REG_SM0", "ECT_REG_SM1",
69
+ "ECT_COEDET_SDO", "ECT_COEDET_SDOINFO", "ECT_COEDET_PDOASSIGN",
70
+ "ECT_COEDET_PDOCONFIG", "ECT_COEDET_UPLOAD", "ECT_COEDET_SDOCA"]
@@ -0,0 +1,18 @@
1
+ """Network adapter discovery helpers."""
2
+
3
+
4
+ class Adapter:
5
+ """Description of a network adapter returned by :func:`find_adapters`."""
6
+
7
+ __slots__ = ("name", "desc")
8
+
9
+ def __init__(self, name, desc):
10
+ self.name = str(name)
11
+ self.desc = str(desc)
12
+
13
+ def __repr__(self):
14
+ return f"Adapter(name={self.name!r}, desc={self.desc!r})"
15
+
16
+
17
+ def _decode(value: bytes) -> str:
18
+ return value.split(b"\0", 1)[0].decode("utf-8", errors="replace")
@@ -0,0 +1,75 @@
1
+ class SoemxError(Exception):
2
+ """Base class for soemx errors."""
3
+
4
+
5
+ class SdoError(SoemxError):
6
+ def __init__(self, slave_pos=None, index=None, subindex=None,
7
+ abort_code=None, desc=""):
8
+ self.slave_pos = slave_pos
9
+ self.index = index
10
+ self.subindex = subindex
11
+ self.abort_code = abort_code
12
+ self.desc = desc
13
+ super().__init__(desc)
14
+
15
+
16
+ class SdoInfoError(SoemxError):
17
+ def __init__(self, message=""):
18
+ self.message = message
19
+ super().__init__(message)
20
+
21
+
22
+ class MailboxError(SoemxError):
23
+ def __init__(self, slave_pos=None, error_code=None, desc=""):
24
+ self.slave_pos = slave_pos
25
+ self.error_code = error_code
26
+ self.desc = desc
27
+ super().__init__(desc)
28
+
29
+
30
+ class PacketError(SoemxError):
31
+ def __init__(self, slave_pos=None, error_code=None, message="", desc=""):
32
+ self.slave_pos = slave_pos
33
+ self.error_code = error_code
34
+ self.message = message
35
+ self.desc = desc
36
+ super().__init__(message or desc)
37
+
38
+
39
+ class ConfigMapError(SoemxError):
40
+ def __init__(self, error_list=None):
41
+ self.error_list = [] if error_list is None else list(error_list)
42
+ super().__init__(self.error_list)
43
+
44
+
45
+ class EepromError(SoemxError):
46
+ def __init__(self, message=""):
47
+ self.message = message
48
+ super().__init__(message)
49
+
50
+
51
+ class WkcError(SoemxError): pass
52
+ class NetworkInterfaceNotOpenError(SoemxError): pass
53
+
54
+
55
+ class Emergency(SoemxError):
56
+ """Emergency mailbox notification compatible with PySOEM."""
57
+
58
+ def __init__(self, slave, error_code, error_reg, b1=0, w1=0, w2=0):
59
+ self.slave_pos = slave
60
+ self.slave = slave
61
+ self.error_code = error_code
62
+ self.error_reg = error_reg
63
+ self.error_register = error_reg
64
+ self.b1 = b1
65
+ self.w1 = w1
66
+ self.w2 = w2
67
+
68
+ def __repr__(self):
69
+ return (f"Emergency(slave_pos={self.slave_pos}, error_code={self.error_code}, "
70
+ f"error_reg={self.error_reg})")
71
+
72
+ def __str__(self):
73
+ payload = bytes([self.b1]) + self.w1.to_bytes(2, "little") + self.w2.to_bytes(2, "little")
74
+ return (f"Slave {self.slave_pos}: {self.error_code:04x}, "
75
+ f"{self.error_reg:02x}, ({','.join(f'{x:02x}' for x in payload)})")