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.
- soemx-0.1.0/MANIFEST.in +11 -0
- soemx-0.1.0/PKG-INFO +109 -0
- soemx-0.1.0/README.md +101 -0
- soemx-0.1.0/pyproject.toml +15 -0
- soemx-0.1.0/setup.cfg +4 -0
- soemx-0.1.0/setup.py +59 -0
- soemx-0.1.0/soemx/__init__.py +70 -0
- soemx-0.1.0/soemx/adapters.py +18 -0
- soemx-0.1.0/soemx/errors.py +75 -0
- soemx-0.1.0/soemx/soemx.c +74965 -0
- soemx-0.1.0/soemx/soemx.pyx +1615 -0
- soemx-0.1.0/soemx/soemx_native.c +265 -0
- soemx-0.1.0/soemx/soemx_native.h +76 -0
- soemx-0.1.0/soemx/status.py +19 -0
- soemx-0.1.0/soemx.egg-info/PKG-INFO +109 -0
- soemx-0.1.0/soemx.egg-info/SOURCES.txt +69 -0
- soemx-0.1.0/soemx.egg-info/dependency_links.txt +1 -0
- soemx-0.1.0/soemx.egg-info/top_level.txt +1 -0
- soemx-0.1.0/tests/test_public_api.py +21 -0
- soemx-0.1.0/tests/test_status.py +66 -0
- soemx-0.1.0/vendor/SOEM/build/include/soem/ec_options.h +128 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_base.h +42 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_coe.h +73 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_config.h +31 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_dc.h +27 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_eoe.h +213 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_foe.h +27 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_main.h +624 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_options.h.in +128 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_print.h +30 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_soe.h +121 -0
- soemx-0.1.0/vendor/SOEM/include/soem/ec_type.h +551 -0
- soemx-0.1.0/vendor/SOEM/include/soem/soem.h +28 -0
- soemx-0.1.0/vendor/SOEM/osal/linux/osal.c +154 -0
- soemx-0.1.0/vendor/SOEM/osal/linux/osal_defs.h +46 -0
- soemx-0.1.0/vendor/SOEM/osal/osal.h +222 -0
- soemx-0.1.0/vendor/SOEM/osal/win32/osal.c +176 -0
- soemx-0.1.0/vendor/SOEM/osal/win32/osal_defs.h +51 -0
- soemx-0.1.0/vendor/SOEM/oshw/linux/nicdrv.c +636 -0
- soemx-0.1.0/vendor/SOEM/oshw/linux/nicdrv.h +108 -0
- soemx-0.1.0/vendor/SOEM/oshw/linux/oshw.c +116 -0
- soemx-0.1.0/vendor/SOEM/oshw/linux/oshw.h +31 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/nicdrv.c +599 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/nicdrv.h +111 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/oshw.c +120 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/oshw.h +31 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/Packet32.h +359 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/Win32-Extensions.h +113 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/bittypes.h +137 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/ip6_misc.h +163 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/bluetooth.h +48 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/bpf.h +934 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/namedb.h +89 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/pcap.h +407 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/sll.h +129 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/usb.h +90 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap/vlan.h +46 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap-bpf.h +47 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap-namedb.h +42 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap-stdinc.h +93 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/pcap.h +45 -0
- soemx-0.1.0/vendor/SOEM/oshw/win32/wpcap/Include/remote-ext.h +444 -0
- soemx-0.1.0/vendor/SOEM/src/ec_base.c +534 -0
- soemx-0.1.0/vendor/SOEM/src/ec_coe.c +1380 -0
- soemx-0.1.0/vendor/SOEM/src/ec_config.c +1653 -0
- soemx-0.1.0/vendor/SOEM/src/ec_dc.c +428 -0
- soemx-0.1.0/vendor/SOEM/src/ec_eoe.c +665 -0
- soemx-0.1.0/vendor/SOEM/src/ec_foe.c +416 -0
- soemx-0.1.0/vendor/SOEM/src/ec_main.c +2705 -0
- soemx-0.1.0/vendor/SOEM/src/ec_print.c +378 -0
- soemx-0.1.0/vendor/SOEM/src/ec_soe.c +379 -0
soemx-0.1.0/MANIFEST.in
ADDED
|
@@ -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
|
+
[](https://github.com/11cookies11/soemx/actions/workflows/ci.yml)
|
|
12
|
+
[](https://pypi.org/project/soemx/)
|
|
13
|
+
[](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
|
+
[](https://github.com/11cookies11/soemx/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/soemx/)
|
|
5
|
+
[](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
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)})")
|