Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
92c8767
feat(ethercat): run the EtherCAT master as EtherDOG, a supervised pro…
thiagoralves Sep 24, 2026
2318921
chore(release): bump VERSION to v4.3.0
thiagoralves Sep 24, 2026
42cfb5c
fix(install): avoid pacman partial upgrades on MSYS2
thiagoralves Sep 24, 2026
906d651
fix(etherdog): point to Npcap when EtherDOG cannot load its DLLs on W…
thiagoralves Sep 24, 2026
835ee31
fix(install): update an existing EtherDOG checkout to ETHERDOG_REF
thiagoralves Sep 24, 2026
0bf1686
fix(etherdog): disable EtherDOG instead of restarting it forever
thiagoralves Sep 24, 2026
ebc998b
fix(etherdog): do not restart EtherDOG on shutdown
thiagoralves Sep 24, 2026
c67ec86
fix(etherdog): shut down an EtherDOG left by a previous webserver
thiagoralves Sep 24, 2026
8960eed
fix(etherdog): keep the control token across restarts
thiagoralves Sep 24, 2026
e4999e7
fix(ethercat): drop the spurious NULL runtime_args warning at init
thiagoralves Sep 24, 2026
c388cde
fix(ethercat): address the PR #205 review
thiagoralves Sep 24, 2026
b5ba9ab
fix(log): escape messages in the JSON log line and bound its length
thiagoralves Sep 24, 2026
2aced75
fix(discovery): accept Npcap device paths as interface names
thiagoralves Sep 24, 2026
d3293e0
fix(webserver): shut down cleanly on SIGTERM and SIGHUP
thiagoralves Sep 24, 2026
b5430ae
chore(license): S7comm plugin sources under LGPLv3+, drop the GPL lin…
thiagoralves Sep 24, 2026
933409a
feat(ethercat): 2048 mapped entries per master; journal capacity 40960
thiagoralves Sep 25, 2026
2360096
fix(webserver): wait for the runtime's socket instead of a fixed 1 s …
thiagoralves Sep 25, 2026
560f83b
fix(ethercat): retry the first EtherDOG link and bind the masters tha…
thiagoralves Sep 25, 2026
c1f4bb0
fix(webserver): stop a running PLC before processing an upload
thiagoralves Sep 25, 2026
d9b9840
test(runtime): count only log records about the test's own socket
thiagoralves Sep 25, 2026
b032da2
fix(ethercat): address the review on the EtherDOG client and supervisor
thiagoralves Oct 5, 2026
a980cac
Merge remote-tracking branch 'origin/development' into RTOP-296-ether…
thiagoralves Oct 5, 2026
ef53a22
fix(ethercat): keep the relay below the dispatcher's new priority
thiagoralves Oct 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,7 @@ core/src/drivers/plugins/python/modbus_slave/modbus_slave_config.json
core/src/drivers/plugins/python/modbus_master/modbus_master.json
core/src/drivers/plugins/python/opcua/opcua.json
core/src/drivers/plugins/native/s7comm/s7comm_config.json

# EtherDOG build trees (the source itself is copied in for the image build)
/etherdog/build*/
/third_party/etherdog/build/
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ core/src/drivers/plugins/native/ethercat/libs/soem/cmake/CYGWIN.cmake
*.o
*.so
.DS_Store

# EtherDOG (EtherCAT master service) source, fetched or copied in by install.sh
/etherdog/
/third_party/
3 changes: 0 additions & 3 deletions .gitmodules

This file was deleted.

5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ State management: `core/src/plc_app/plc_state_manager.cpp`
- **Driver code**: `core/src/drivers/`
- **Plugin examples**: `core/src/drivers/plugins/python/` and `core/src/drivers/plugins/native/`

### EtherCAT
- **Master**: EtherDOG, a separate process (`build/etherdog`) supervised by `webserver/etherdog_manager.py`
- **Client plugin**: `core/src/drivers/plugins/native/ethercat/` joins EtherDOG's layout with `ethercat_iomapping.json`
- Details: `docs/ETHERCAT.md`

### Key Subsystems
- **Scan cycle tracker**: `core/src/plc_app/scan_cycle_manager.c` - scan timing statistics
- **Debug handler**: `core/src/plc_app/debug_handler.c` - STruC++ debugger PDUs (function codes 0x41-0x45);
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ The installation script will:
3. Create Python virtual environment at `venvs/runtime/`
4. Install Python dependencies
5. Compile the PLC runtime core with CMake
6. Build native plugins (e.g., EtherCAT, S7comm)
6. Build native plugins (e.g., EtherCAT client, S7comm) and EtherDOG, the EtherCAT master service
7. Install and start a systemd service (`openplc-runtime`) when systemd is available

### Manual Build
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v4.2.4
v4.3.0
2 changes: 1 addition & 1 deletion core/src/drivers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ void plugin_driver_destroy(plugin_driver_t *driver);

## License

This plugin system is part of the OpenPLC Runtime project and follows the same licensing terms (typically GPLv3 or later).
This plugin system is part of the OpenPLC Runtime and is licensed under the MIT License (see the top-level `LICENSE`). Plugins can carry their own license: the S7comm plugin is LGPLv3 or later, matching the Snap7 library it links.

## Contributing

Expand Down
199 changes: 12 additions & 187 deletions core/src/drivers/plugins/native/ethercat/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# CMakeLists.txt for EtherCAT Plugin
# Builds a self-contained EtherCAT plugin with SOEM library included
# SPDX-License-Identifier: MIT
# Copyright (c) 2026 Autonomy®
#
# EtherCAT client plugin: relays process data between EtherDOG and the image tables.

cmake_minimum_required(VERSION 3.10)
project(ethercat_plugin C)
Expand All @@ -8,166 +10,18 @@ set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# Determine OpenPLC root directory for finding common headers
# When building standalone: calculate from plugin location
# When building from main project: pass -DOPENPLC_ROOT=<path>
if(NOT DEFINED OPENPLC_ROOT)
get_filename_component(OPENPLC_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../" ABSOLUTE)
endif()

message(STATUS "EtherCAT Plugin - OpenPLC root: ${OPENPLC_ROOT}")

# =============================================================================
# SOEM Library (built via add_subdirectory for proper ec_options.h generation)
# =============================================================================

set(SOEM_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/soem)
set(SOEM_BUILD_SAMPLES OFF CACHE BOOL "Disable SOEM samples" FORCE)

# On MSYS2/Cygwin, SOEM ships no platform cmake file (only Linux.cmake,
# Windows.cmake, rt-kernel.cmake exist). Generate one from the Win32
# variant because MSYS2 can use Win32 OSAL/OSHW through w32api headers
# while the Linux OSHW needs AF_PACKET raw sockets which are unavailable.
if(CMAKE_SYSTEM_NAME STREQUAL "MSYS" OR CMAKE_SYSTEM_NAME STREQUAL "CYGWIN")
set(_SOEM_PLATFORM_CMAKE "${SOEM_DIR}/cmake/${CMAKE_SYSTEM_NAME}.cmake")
message(STATUS "EtherCAT Plugin - Generating ${CMAKE_SYSTEM_NAME}.cmake for SOEM (Win32 OSAL/OSHW via w32api)")
file(WRITE "${_SOEM_PLATFORM_CMAKE}" "\
# Auto-generated for ${CMAKE_SYSTEM_NAME} -- based on Windows.cmake\n\
# Uses Win32 OSAL/OSHW accessible through w32api on MSYS2/Cygwin.\n\
\n\
target_sources(soem PRIVATE\n\
osal/win32/osal.c\n\
osal/win32/osal_defs.h\n\
oshw/win32/oshw.c\n\
oshw/win32/oshw.h\n\
oshw/win32/nicdrv.c\n\
oshw/win32/nicdrv.h\n\
)\n\
\n\
target_include_directories(soem PUBLIC\n\
$<BUILD_INTERFACE:\${SOEM_SOURCE_DIR}/osal/win32>\n\
$<BUILD_INTERFACE:\${SOEM_SOURCE_DIR}/oshw/win32>\n\
$<BUILD_INTERFACE:\${SOEM_SOURCE_DIR}/oshw/win32/wpcap/Include>\n\
$<INSTALL_INTERFACE:include/soem>\n\
)\n\
\n\
target_compile_options(soem PUBLIC\n\
$<$<C_COMPILER_ID:GNU>:-std=c11>\n\
)\n\
\n\
# WIN32 is NOT predefined by MSYS2/Cygwin GCC, but the bundled\n\
# wpcap headers (pcap/pcap.h:40) need it to route to pcap-stdinc.h.\n\
# HAVE_U_INT*_T tells bundled bittypes.h to skip its typedefs --\n\
# MSYS2 POSIX headers (sys/types.h) already provide these types and\n\
# use different underlying types on LP64 (long vs long long for 64-bit).\n\
#\n\
# __USE_W32_SOCKETS tells MSYS2 / Cygwin's POSIX headers\n\
# (\`sys/select.h\`, \`sys/unistd.h\`) NOT to declare their own \`select\`\n\
# and \`gethostname\` — the Win32 \`winsock2.h\` declarations (which the\n\
# bundled wpcap headers pull in transitively) are the ones we want\n\
# to use. Without this, any TU that includes both a POSIX header\n\
# (e.g. \`<signal.h>\` in ethercat_plugin.c, which transitively pulls\n\
# in sys/select.h) AND a SOEM/wpcap header (which pulls in\n\
# winsock2.h) gets a redeclaration error because the two libcs\n\
# disagree on the signature ( \`struct timeval *\` vs \`const TIMEVAL *\`,\n\
# \`size_t\` vs \`int\`). Marking the macro PUBLIC propagates it to\n\
# every target linking against soem (notably ethercat_plugin).\n\
target_compile_definitions(soem PUBLIC\n\
WIN32\n\
__USE_W32_SOCKETS\n\
HAVE_U_INT8_T\n\
HAVE_U_INT16_T\n\
HAVE_U_INT32_T\n\
HAVE_U_INT64_T\n\
# pcap-stdinc.h redefines snprintf to _snprintf when _MSC_VER is\n\
# undefined (preprocessor treats it as 0, so 0 < 1500 is true).\n\
# Map _snprintf back to snprintf so MinGW GCC resolves the call.\n\
_snprintf=snprintf\n\
_vsnprintf=vsnprintf\n\
)\n\
\n\
foreach(target IN ITEMS\n\
soem\n\
ec_sample\n\
eepromtool\n\
eni_test\n\
eoe_test\n\
firm_update\n\
simple_ng\n\
slaveinfo)\n\
if (TARGET \${target})\n\
target_compile_options(\${target} PRIVATE\n\
$<$<C_COMPILER_ID:GNU>:\n\
-Wall\n\
-Wextra\n\
-Wno-unused-parameter\n\
>\n\
)\n\
endif()\n\
endforeach()\n\
\n\
if(CMAKE_SIZEOF_VOID_P EQUAL 8)\n\
set(WPCAP_LIB_PATH \${SOEM_SOURCE_DIR}/oshw/win32/wpcap/Lib/x64)\n\
target_link_libraries(soem PUBLIC\n\
\${WPCAP_LIB_PATH}/wpcap.lib\n\
\${WPCAP_LIB_PATH}/Packet.lib\n\
)\n\
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)\n\
set(WPCAP_LIB_PATH \${SOEM_SOURCE_DIR}/oshw/win32/wpcap/Lib)\n\
target_link_libraries(soem PUBLIC\n\
\${WPCAP_LIB_PATH}/libwpcap.a\n\
\${WPCAP_LIB_PATH}/libpacket.a\n\
)\n\
endif()\n\
\n\
target_link_libraries(soem PUBLIC ws2_32 winmm)\n\
\n\
install(FILES\n\
osal/win32/osal_defs.h\n\
oshw/win32/nicdrv.h\n\
DESTINATION include/soem\n\
)\n\
")
unset(_SOEM_PLATFORM_CMAKE)
endif()

add_subdirectory(${SOEM_DIR} ${CMAKE_CURRENT_BINARY_DIR}/soem)

# =============================================================================
# cJSON Library (shared utility for all native plugins)
# =============================================================================

set(CJSON_SOURCES
${OPENPLC_ROOT}/core/src/drivers/plugins/native/cjson/cJSON.c
)

# =============================================================================
# Plugin Source Files
# =============================================================================

set(PLUGIN_SOURCES
add_library(ethercat_plugin SHARED
${CMAKE_CURRENT_SOURCE_DIR}/ethercat_plugin.c
${CMAKE_CURRENT_SOURCE_DIR}/ethercat_config.c
${CMAKE_CURRENT_SOURCE_DIR}/ethercat_master.c
${CMAKE_CURRENT_SOURCE_DIR}/ethercat_io.c
${CMAKE_CURRENT_SOURCE_DIR}/ethercat_proc.c
${CMAKE_CURRENT_SOURCE_DIR}/ethercat_iface_state.c
${CMAKE_CURRENT_SOURCE_DIR}/etherdog_link.c
${CMAKE_CURRENT_SOURCE_DIR}/ethercat_iomap.c
${OPENPLC_ROOT}/core/src/drivers/plugins/native/cjson/cJSON.c
${OPENPLC_ROOT}/core/src/drivers/plugins/native/plugin_logger.c
)

# =============================================================================
# Create Shared Library
# =============================================================================

add_library(ethercat_plugin SHARED
${CJSON_SOURCES}
${PLUGIN_SOURCES}
)

# =============================================================================
# Include Directories
# =============================================================================

target_include_directories(ethercat_plugin PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${OPENPLC_ROOT}/core/src/drivers/plugins/native/cjson
Expand All @@ -176,52 +30,23 @@ target_include_directories(ethercat_plugin PRIVATE
${OPENPLC_ROOT}/core/src/lib
)

# =============================================================================
# Compiler Options
# =============================================================================

target_compile_options(ethercat_plugin PRIVATE
-fPIC
-Wno-unused-parameter
-Wno-sign-compare
)
target_compile_options(ethercat_plugin PRIVATE -Wall -Wextra -Wno-unused-parameter)
target_link_libraries(ethercat_plugin PRIVATE pthread)

# =============================================================================
# Link Libraries
# =============================================================================

target_link_libraries(ethercat_plugin PRIVATE
soem
${CMAKE_DL_LIBS}
)

# On MSYS2/Cygwin, ensure plugin API symbols (init, start_loop, etc.) are
# exported from the shared library so that dlsym() can resolve them.
# MSYS2/Cygwin: export the plugin entry points so dlsym() resolves them.
if(CMAKE_SYSTEM_NAME STREQUAL "MSYS" OR CMAKE_SYSTEM_NAME STREQUAL "CYGWIN")
set_target_properties(ethercat_plugin PROPERTIES
LINK_FLAGS "-Wl,--export-all-symbols"
)
set_target_properties(ethercat_plugin PROPERTIES LINK_FLAGS "-Wl,--export-all-symbols")
endif()

# =============================================================================
# Output Settings
# =============================================================================

set_target_properties(ethercat_plugin PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins
PREFIX "lib"
OUTPUT_NAME "ethercat_plugin"
)

# On Linux, use .so extension
if(UNIX)
set_target_properties(ethercat_plugin PROPERTIES SUFFIX ".so")
endif()

# =============================================================================
# Install Target
# =============================================================================

install(TARGETS ethercat_plugin
LIBRARY DESTINATION lib/openplc/plugins
RUNTIME DESTINATION lib/openplc/plugins
Expand Down
Loading
Loading