Skip to content

Commit 72ef47b

Browse files
authored
Merge pull request #75 from Autonomy-Logic/development
Add atomic journaled write to plugins; Add Siemens S7Comm
2 parents 0f5afa0 + ce8bfde commit 72ef47b

65 files changed

Lines changed: 26529 additions & 73 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.DS_Store

6 KB
Binary file not shown.

CLAUDE.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Build Commands
6+
7+
```bash
8+
# Full installation (installs deps, creates venv, compiles runtime)
9+
sudo ./install.sh
10+
11+
# Manual build (C/C++ runtime core)
12+
mkdir -p build && cd build && cmake .. && make -j$(nproc)
13+
14+
# Start the runtime (requires root for real-time scheduling)
15+
sudo ./start_openplc.sh
16+
17+
# Run web server only (for development)
18+
source venvs/runtime/bin/activate
19+
sudo python3 -m webserver.app
20+
21+
# Run PLC runtime only
22+
sudo ./build/plc_main --print-logs
23+
```
24+
25+
## Testing
26+
27+
```bash
28+
# Setup test environment and run tests
29+
sudo bash scripts/setup-tests-env.sh
30+
pytest tests/
31+
32+
# Or use the test script
33+
bash scripts/run-pytest.sh
34+
```
35+
36+
## Linting and Formatting
37+
38+
Pre-commit hooks handle formatting. Install with:
39+
```bash
40+
pip install pre-commit
41+
pre-commit install
42+
pre-commit run --all-files
43+
```
44+
45+
- **C/C++**: Clang-Format (LLVM style, 4-space indent, 100 char limit)
46+
- **Python**: Black + isort + Ruff (100 char line length)
47+
48+
## Architecture Overview
49+
50+
OpenPLC Runtime v4 is a **dual-process industrial PLC runtime**:
51+
52+
### Process 1: REST API Server (Python/Flask)
53+
- **Location**: `webserver/`
54+
- **Port**: 8443 (HTTPS with self-signed TLS)
55+
- **Purpose**: REST API for OpenPLC Editor, WebSocket debug interface, compilation orchestration
56+
- **Entry point**: `webserver/app.py`
57+
58+
### Process 2: PLC Runtime Core (C/C++)
59+
- **Location**: `core/src/plc_app/`
60+
- **Executable**: `build/plc_main`
61+
- **Purpose**: Real-time PLC execution with SCHED_FIFO priority
62+
- **Entry point**: `core/src/plc_app/plc_main.c`
63+
64+
### Inter-Process Communication
65+
- **Command socket**: `/run/runtime/plc_runtime.socket` (text protocol for start/stop/status)
66+
- **Log socket**: `/run/runtime/log_runtime.socket` (real-time log streaming)
67+
- **Client**: `webserver/unixclient.py`
68+
- **Server**: `core/src/plc_app/unix_socket.c`
69+
70+
### PLC State Machine
71+
```
72+
EMPTY -> INIT -> RUNNING <-> STOPPED -> ERROR
73+
```
74+
State management: `core/src/plc_app/plc_state_manager.c`
75+
76+
### Plugin System
77+
- **Config**: `plugins.conf`
78+
- **Types**: Python (type=0) and Native C/C++ (type=1)
79+
- **Driver code**: `core/src/drivers/`
80+
- **Plugin examples**: `core/src/drivers/plugins/python/` and `core/src/drivers/plugins/native/`
81+
82+
### Key Subsystems
83+
- **Scan cycle manager**: `core/src/plc_app/scan_cycle_manager.c` - deterministic timing
84+
- **Debug handler**: `core/src/plc_app/debug_handler.c` - WebSocket debug protocol
85+
- **Watchdog**: `core/src/plc_app/utils/watchdog.c` - health monitoring
86+
- **Image tables**: `core/src/plc_app/image_tables.c` - I/O buffer management
87+
88+
## Code Style
89+
90+
- **C/C++**: 4-space indent, no tabs, `snake_case` functions, `snake_case_t` types, `UPPER_CASE` macros
91+
- **Python**: PEP 8, type hints, 100 char lines
92+
- **No emojis** anywhere in code, comments, or documentation (project standard)
93+
94+
## Key Directories
95+
96+
- `webserver/` - Flask REST API and WebSocket debug interface
97+
- `core/src/plc_app/` - C/C++ real-time PLC runtime
98+
- `core/src/drivers/` - Plugin driver system
99+
- `core/generated/` - Generated PLC code from uploaded programs
100+
- `scripts/` - Build, compile, and management scripts
101+
- `build/` - CMake output (`plc_main` executable, `libplc_*.so` libraries)
102+
- `venvs/` - Python virtual environments (runtime + per-plugin)
103+
- `docs/` - Detailed documentation
104+
105+
## Compilation Flow
106+
107+
1. OpenPLC Editor uploads `program.zip` to `/api/upload-file`
108+
2. Runtime validates, extracts to `core/generated/`
109+
3. `scripts/compile.sh` compiles to `build/libplc_*.so`
110+
4. Runtime loads shared library dynamically via `plcapp_manager.c`

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ RUN rm -rf build/ venvs/ .venv/ 2>/dev/null || true
1717
# Run installation script
1818
RUN ./install.sh
1919

20+
# Clean up apt cache to reduce image size (Docker-specific optimization)
21+
RUN rm -rf /var/lib/apt/lists/*
22+
2023
# Expose webserver port
2124
EXPOSE 8443
2225

Dockerfile.dev

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ RUN rm -rf build/ venvs/ 2>/dev/null || true
1414
RUN chmod +x install.sh scripts/* start_openplc.sh
1515
RUN ./install.sh
1616

17+
# Clean up apt cache to reduce image size (Docker-specific optimization)
18+
RUN rm -rf /var/lib/apt/lists/*
19+
1720
EXPOSE 8443
1821

1922
CMD ["bash", "./scripts/run-pytest.sh"]

core/.DS_Store

6 KB
Binary file not shown.

core/src/.DS_Store

6 KB
Binary file not shown.

core/src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ add_executable(plc_main
3434
${CMAKE_SOURCE_DIR}/core/src/plc_app/utils/utils.c
3535
${CMAKE_SOURCE_DIR}/core/src/plc_app/utils/watchdog.c
3636
${CMAKE_SOURCE_DIR}/core/src/plc_app/image_tables.c
37+
${CMAKE_SOURCE_DIR}/core/src/plc_app/journal_buffer.c
3738
${CMAKE_SOURCE_DIR}/core/src/plc_app/plc_state_manager.c
3839
${CMAKE_SOURCE_DIR}/core/src/plc_app/plcapp_manager.c
3940
${CMAKE_SOURCE_DIR}/core/src/plc_app/scan_cycle_manager.c

core/src/drivers/.DS_Store

6 KB
Binary file not shown.

core/src/drivers/plugin_driver.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#endif
1515

1616
#include "../plc_app/image_tables.h"
17+
#include "../plc_app/journal_buffer.h"
1718
#include "../plc_app/utils/log.h"
1819
#include "plugin_config.h"
1920
#include "plugin_driver.h"
@@ -105,6 +106,36 @@ int plugin_mutex_give(pthread_mutex_t *mutex)
105106
return pthread_mutex_unlock(mutex);
106107
}
107108

109+
// Journal write wrapper functions for plugins
110+
// These match the function pointer signatures in plugin_types.h and delegate
111+
// to the journal_buffer.h API with proper type casting.
112+
113+
static int plugin_journal_write_bool(int type, int index, int bit, int value)
114+
{
115+
return journal_write_bool((journal_buffer_type_t)type, (uint16_t)index, (uint8_t)bit,
116+
value != 0);
117+
}
118+
119+
static int plugin_journal_write_byte(int type, int index, int value)
120+
{
121+
return journal_write_byte((journal_buffer_type_t)type, (uint16_t)index, (uint8_t)value);
122+
}
123+
124+
static int plugin_journal_write_int(int type, int index, int value)
125+
{
126+
return journal_write_int((journal_buffer_type_t)type, (uint16_t)index, (uint16_t)value);
127+
}
128+
129+
static int plugin_journal_write_dint(int type, int index, unsigned int value)
130+
{
131+
return journal_write_dint((journal_buffer_type_t)type, (uint16_t)index, (uint32_t)value);
132+
}
133+
134+
static int plugin_journal_write_lint(int type, int index, unsigned long long value)
135+
{
136+
return journal_write_lint((journal_buffer_type_t)type, (uint16_t)index, (uint64_t)value);
137+
}
138+
108139
// Python capsule destructor for runtime args
109140
// Breakpoint here to debug capsule issues
110141
static void plugin_runtime_args_capsule_destructor(PyObject *capsule)
@@ -670,6 +701,13 @@ void *generate_structured_args_with_driver(plugin_type_t type, plugin_driver_t *
670701
args->log_warn = log_warn;
671702
args->log_error = log_error;
672703

704+
// Initialize journal write functions for race-condition-free buffer writes
705+
args->journal_write_bool = plugin_journal_write_bool;
706+
args->journal_write_byte = plugin_journal_write_byte;
707+
args->journal_write_int = plugin_journal_write_int;
708+
args->journal_write_dint = plugin_journal_write_dint;
709+
args->journal_write_lint = plugin_journal_write_lint;
710+
673711
// printf("[PLUGIN]: Runtime args initialized:\n");
674712
// printf("[PLUGIN]: buffer_size = %d\n", args->buffer_size);
675713
// printf("[PLUGIN]: bits_per_buffer = %d\n", args->bits_per_buffer);

core/src/drivers/plugin_types.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,26 @@ typedef void (*plugin_log_debug_func_t)(const char *fmt, ...);
3030
typedef void (*plugin_log_warn_func_t)(const char *fmt, ...);
3131
typedef void (*plugin_log_error_func_t)(const char *fmt, ...);
3232

33+
/**
34+
* @brief Journal write function pointer types
35+
*
36+
* These function pointers allow plugins to write to I/O buffers through
37+
* the journal buffer system, ensuring race-condition-free writes.
38+
* All writes are applied atomically at the start of the next PLC scan cycle.
39+
*
40+
* Buffer type values (matching journal_buffer_type_t):
41+
* 0=BOOL_INPUT, 1=BOOL_OUTPUT, 2=BOOL_MEMORY
42+
* 3=BYTE_INPUT, 4=BYTE_OUTPUT
43+
* 5=INT_INPUT, 6=INT_OUTPUT, 7=INT_MEMORY
44+
* 8=DINT_INPUT, 9=DINT_OUTPUT, 10=DINT_MEMORY
45+
* 11=LINT_INPUT, 12=LINT_OUTPUT, 13=LINT_MEMORY
46+
*/
47+
typedef int (*plugin_journal_write_bool_func_t)(int type, int index, int bit, int value);
48+
typedef int (*plugin_journal_write_byte_func_t)(int type, int index, int value);
49+
typedef int (*plugin_journal_write_int_func_t)(int type, int index, int value);
50+
typedef int (*plugin_journal_write_dint_func_t)(int type, int index, unsigned int value);
51+
typedef int (*plugin_journal_write_lint_func_t)(int type, int index, unsigned long long value);
52+
3353
/**
3454
* @brief Runtime buffer access structure for plugins
3555
*
@@ -79,6 +99,13 @@ typedef struct
7999
plugin_log_debug_func_t log_debug;
80100
plugin_log_warn_func_t log_warn;
81101
plugin_log_error_func_t log_error;
102+
103+
/* Journal write functions - race-condition-free buffer writes */
104+
plugin_journal_write_bool_func_t journal_write_bool;
105+
plugin_journal_write_byte_func_t journal_write_byte;
106+
plugin_journal_write_int_func_t journal_write_int;
107+
plugin_journal_write_dint_func_t journal_write_dint;
108+
plugin_journal_write_lint_func_t journal_write_lint;
82109
} plugin_runtime_args_t;
83110

84111
#endif /* PLUGIN_TYPES_H */

0 commit comments

Comments
 (0)