Skip to content

Bug fixes and new docs#55

Merged
thiagoralves merged 28 commits into
mainfrom
development
Dec 25, 2025
Merged

Bug fixes and new docs#55
thiagoralves merged 28 commits into
mainfrom
development

Conversation

@thiagoralves

Copy link
Copy Markdown
Contributor

This pull request significantly updates the documentation for the OpenPLC Runtime plugin system to reflect full support for both Python and native C/C++ plugins. The changes clarify the architecture, configuration, and development process for both plugin types, provide detailed examples, and introduce new features such as per-plugin Python virtual environments and real-time synchronization hooks for native plugins.

Plugin System Architecture and Support:

  • Updated all references and documentation to state that both Python and native C/C++ plugins are now fully supported and operational. This includes clarifying terminology, updating status sections, and providing accurate directory structures and examples. [1] [2]

Native C/C++ Plugin API and Usage:

  • Provided comprehensive documentation and code examples for implementing native C/C++ plugins, including initialization, lifecycle management, and safe memory handling for runtime arguments. Added details about new per-cycle hooks (cycle_start, cycle_end) for real-time synchronization with the PLC scan cycle. [1] [2] [3]

Python Plugin Enhancements:

  • Updated documentation to reflect the use of the shared module for type safety, buffer access, and logging in Python plugins. Added information about per-plugin virtual environment support and clarified best practices for safe buffer and logging access. [1] [2] [3] [4]

Plugin Configuration and Lifecycle:

  • Enhanced the description of the plugins.conf configuration file format to include the new venv_path field for Python plugins and updated examples for both plugin types. Clarified the plugin lifecycle and memory management requirements for both Python and native plugins. [1] [2] [3] [4]

Other Improvements:

  • Minor update to the pre-commit configuration to disable additional pylint warnings.

These updates provide a clear, accurate, and practical guide for developing, configuring, and integrating both Python and native C/C++ plugins within the OpenPLC Runtime.

devin-ai-integration Bot and others added 28 commits December 7, 2025 20:03
…sion

This commit implements three key optimizations for the mutex protecting
image table access in Python plugins:

1. Priority Inheritance for Mutex (C core)
   - Modified plugin_driver_create() to use pthread_mutexattr_setprotocol()
     with PTHREAD_PRIO_INHERIT
   - Prevents priority inversion when lower-priority plugin threads hold
     the mutex while the real-time PLC scan cycle thread is waiting

2. Move Conversion Logic Outside Critical Section (modbus_master_memory.py)
   - Added new optimized functions that separate data conversion from
     buffer access:
     * convert_modbus_data_to_iec_values() - pre-convert before mutex
     * write_preconverted_iec_values() - write under mutex
     * read_raw_iec_values() - read under mutex
     * convert_raw_iec_to_modbus() - convert after mutex release
   - Legacy functions kept for backward compatibility

3. Batch Write Preparations (modbus_master_plugin.py)
   - READ operations: Pre-convert all Modbus data to IEC values before
     acquiring mutex, then write all pre-converted values under single
     mutex acquisition
   - WRITE operations: Collect all due write points, read all raw IEC
     values under single mutex acquisition, then convert and perform
     Modbus writes outside the mutex

These optimizations reduce mutex hold time by ensuring only actual buffer
access operations occur within the critical section, while CPU-intensive
conversion work happens outside.

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
Addresses Copilot review comment: Log when convert_modbus_data_to_iec_values()
returns None to aid debugging. Uses a concise message with IEC address and
length without dumping the full modbus_data list to avoid log noise.

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
…imizations

Optimize mutex usage to minimize hold time and prevent priority inversion
Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
…-bool-input

Add missing write_bool_input method to SafeBufferAccess
This commit adds runtime support for Python Function Blocks, enabling
PLC programs to include function blocks written in Python that communicate
with the runtime via shared memory.

Changes:
- Add core/src/plc_app/include/iec_python.h: Header declaring Python FB
  loader functions (create_shm_name, python_block_loader)
- Add core/src/plc_app/python_loader.c: Implementation ported from v3,
  adapted to use v4's logging API (log_info, log_error)
- Update scripts/compile.sh: Include Python header in generated code
  compilation and link python_loader with pthread and rt libraries

The Python FB loader creates shared memory regions for input/output data
exchange and spawns Python processes that run the user's function block
code. This matches the behavior of OpenPLC Runtime v3.

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
The python_loader.c was calling log_info() and log_error() directly, but
these functions are defined in plc_main, not in libplc.so. Since plc_main
is not linked with -rdynamic, these symbols would not be resolved at runtime.

This commit implements Option B (function pointers/callbacks) to fix the issue:

- Add static function pointers for logging in python_loader.c
- Add fallback logging to stderr when loggers are not set
- Add python_loader_set_loggers() function to inject logging callbacks
- Update iec_python.h with the setter function declaration
- Update symbols_init() in image_tables.c to wire up logging callbacks
  after loading libplc.so

This approach matches the existing plugin system pattern where logging
functions are passed via callbacks rather than relying on symbol export.

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
Per review feedback, since python_loader.c is always compiled into libplc.so
and symbols_init() always runs before any Python FB code executes, the logging
callbacks will always be set. Therefore, the fallback logging to stderr is
unnecessary.

Changes:
- Remove fallback_log, py_log_info_fallback, py_log_error_fallback functions
- Remove NULL checks in LOG_INFO/LOG_ERROR macros
- Simplify macros to directly call the function pointers
- Remove stdarg.h include (no longer needed)

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
…nction-blocks

Add Python Function Blocks support
- Add lock_memory() function that calls mlockall(MCL_CURRENT | MCL_FUTURE)
- Call lock_memory() after set_realtime_priority() in plc_cycle_thread()
- Add sys/mman.h include for mlockall support

This prevents the kernel from swapping out memory pages during PLC
execution, eliminating unpredictable latency spikes caused by page faults
in the scan cycle. Requires container to have memlock ulimit set to
unlimited (--ulimit memlock=-1).

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
…y-locking

Add memory locking for deterministic PLC execution
- Remove print_stats_thread that was polluting logs every 5 seconds
- Add STATS command to unix socket for fetching timing statistics
- Add thread-safe mutex protection for plc_timing_stats access
- Add get_timing_stats_snapshot() for safe concurrent reads
- Add format_timing_stats_response() to format stats as JSON
- Add stats_plc() method to RuntimeManager
- Modify handle_status() to include timing_stats in response
- Maintain backward compatibility: status field unchanged for old editors
- Fix missing return statement in stop_plc exception handler
- Disable R0902 and R1732 pylint warnings in pre-commit config

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
Use PRId64 from inttypes.h instead of %ld for portable int64_t
formatting. This fixes build failures on 32-bit ARM platforms where
int64_t is 'long long int' rather than 'long int'.

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
…status-response

Move timing stats from logs to status response
This adds an after_request hook to the REST API blueprint that includes
the X-OpenPLC-Runtime-Version header with value 'v4' in all API responses.

This enables the OpenPLC Editor to detect which runtime version it is
connecting to and show an error if there is a version mismatch between
the selected target and the actual runtime.

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
…ersion-header

Add X-OpenPLC-Runtime-Version header to all API responses
- Add optional include_stats parameter to /api/status endpoint (default false)
- Only fetch timing stats when include_stats=true, avoiding mutex acquisition
- Remove verbose command logging from unix_socket.c (only log unrecognized commands)
- This reduces log flooding and avoids unnecessary mutex contention on the
  critical PLC scan cycle when stats are not needed

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
…atus-stats-polling

Add include_stats parameter to status endpoint and reduce log verbosity
- Update README.md to document that native C/C++ plugins are now fully supported
- Fix incorrect type comment in plugin_config.h (0=python, 1=native)
- Update DEVELOPMENT.md plugin section with accurate lifecycle description
- Document native plugin args lifetime (must copy during init)
- Update Python import examples to use correct 'from shared import' syntax
- Add native plugin examples to See Also section

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
…ugin-driver-docs

Update plugin driver documentation to reflect native plugin support
- Add plugin_driver_cycle_start() and plugin_driver_cycle_end() functions
- Integrate cycle hooks into PLC scan cycle in plc_state_manager.c
- Call cycle_start before PLC logic execution, cycle_end after
- Plugins opt-in by implementing cycle_start/cycle_end; opt-out by not implementing
- Only active (enabled + running) native plugins are called
- Update README.md with comprehensive cycle hooks documentation
- Add cycle hook functions to API Reference section

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
…-native-cycle-hooks

Implement native plugin cycle hooks for real-time PLC synchronization
- Fix close_unix_socket() signature mismatch: header declared with no
  parameters but implementation took int server_fd parameter
- Update empty () declarations to (void) in headers and definitions for
  C standards compliance:
  - setup_unix_socket()
  - watchdog_init()
  - scan_cycle_time_start()
  - scan_cycle_time_end()
- Update function pointer typedefs in plugin_driver.h to use (void)
- Make store_on_buffer and retrieve_from_buffer static in log.c

These changes ensure the code compiles on stricter compilers that treat
-Wstrict-prototypes and -Wold-style-definition as errors.

Co-Authored-By: Thiago Alves <thiagoralves@gmail.com>
…-unix-socket-signature

Fix function prototype declarations for cross-platform compatibility
@thiagoralves thiagoralves merged commit eb3ae58 into main Dec 25, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants