Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
96 changes: 95 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,101 @@ Python-snap7 is a Python wrapper for the Snap7 library, providing Ethernet commu
- **snap7/common.py**: Common utilities including library loading
- **snap7/error.py**: Error handling and exceptions

The library uses ctypes to interface with the native Snap7 C library (libsnap7.so/snap7.dll/libsnap7.dylib).
The library traditionally uses ctypes to interface with the native Snap7 C library (libsnap7.so/snap7.dll/libsnap7.dylib), but now also includes a **pure Python implementation** that removes the dependency on the C library.

## Pure Python Implementation

### Overview

The project includes a complete pure Python implementation of the S7 protocol that eliminates the need for the Snap7 C library. This implementation provides:

- **Zero dependencies** on external C libraries
- **Cross-platform compatibility** without platform-specific binaries
- **Full S7 protocol support** for basic operations (read/write/connect)
- **Drop-in replacement** API compatibility with the ctypes version

### Architecture

**snap7/native/**: Pure Python S7 protocol implementation
- **snap7/native/client.py**: Core S7Client class with connection management
- **snap7/native/connection.py**: ISO on TCP implementation (TPKT/COTP layers)
- **snap7/native/protocol.py**: S7 PDU encoding/decoding
- **snap7/native/datatypes.py**: S7 data types and address encoding
- **snap7/native/errors.py**: S7-specific error handling
- **snap7/native/__init__.py**: Package initialization

**snap7/native_client.py**: Drop-in replacement Client class that wraps the pure Python implementation

### Usage

```python
import snap7

# Option 1: Use get_client() function to choose backend
client = snap7.get_client(pure_python=True) # Pure Python
client = snap7.get_client(pure_python=False) # Ctypes (default)

# Option 2: Import directly
from snap7 import PureClient
client = PureClient()

# Option 3: Traditional way (uses ctypes)
from snap7 import Client
client = Client()

# All clients have the same API
client.connect("192.168.1.10", 0, 1)
data = client.db_read(1, 0, 4)
client.db_write(1, 0, bytearray([1, 2, 3, 4]))
client.disconnect()
```

### Implementation Status

**✅ Implemented:**
- TCP connection management
- ISO on TCP (TPKT/COTP) transport layers
- S7 protocol PDU encoding/decoding
- Read/write operations for all memory areas (DB, M, I, Q, T, C)
- Error handling and connection management
- Data type conversions (BYTE, WORD, DWORD, INT, DINT, REAL, BIT)
- Multi-variable operations
- API compatibility with ctypes version

**🚧 Not Yet Implemented:**
- Block operations (upload/download)
- PLC control functions (start/stop)
- CPU information retrieval
- Authentication/password handling
- Advanced S7 userdata functions
- Time/date operations

### Testing

```bash
# Test pure Python implementation specifically
pytest tests/test_native_client.py tests/test_native_datatypes.py

# Test integration between backends
pytest tests/test_integration.py

# Run all tests (includes pure Python tests)
pytest tests/
```

### Performance Considerations

- **Pure Python**: No C library dependencies, easier deployment, potentially slower
- **Ctypes**: Uses optimized C library, faster execution, requires platform-specific binaries
- **Use case**: Pure Python ideal for cloud/container deployments where C dependencies are problematic

### Development Notes

- The pure Python implementation is designed as a learning reference and dependency-free alternative
- Protocol implementation follows the official Siemens S7 specification
- Socket-level programming uses standard Python libraries only
- All S7 protocol constants and structures are faithfully reproduced
- Error codes and messages match the original Snap7 library

## Essential Commands

Expand Down
82 changes: 82 additions & 0 deletions snap7/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,90 @@
from .util.db import Row, DB
from .type import Area, Block, WordLen, SrvEvent, SrvArea

# Pure Python client and server implementation
try:
from .native_client import Client as PureClient
from .native_server import Server as PureServer
_PURE_PYTHON_AVAILABLE = True
except ImportError:
_PURE_PYTHON_AVAILABLE = False
PureClient = None # type: ignore
PureServer = None # type: ignore

__all__ = ["Client", "Server", "Logo", "Partner", "Row", "DB", "Area", "Block", "WordLen", "SrvEvent", "SrvArea"]

# Add pure Python implementations to exports if available
if _PURE_PYTHON_AVAILABLE:
__all__.extend(["PureClient", "PureServer"])


def get_client(pure_python: bool = False):
"""
Get a client instance using the specified backend.

Args:
pure_python: If True, use pure Python implementation.
If False (default), use ctypes wrapper around Snap7 C library.

Returns:
Client instance using the requested backend.

Raises:
ImportError: If pure Python backend is requested but not available.

Examples:
>>> # Use default ctypes backend
>>> client = snap7.get_client()

>>> # Use pure Python backend
>>> client = snap7.get_client(pure_python=True)
"""
if pure_python:
if not _PURE_PYTHON_AVAILABLE:
raise ImportError(
"Pure Python client is not available. "
"This may be due to missing dependencies in the native module."
)
return PureClient()
else:
return Client()


def get_server(pure_python: bool = False):
"""
Get a server instance using the specified backend.

Args:
pure_python: If True, use pure Python implementation.
If False (default), use ctypes wrapper around Snap7 C library.

Returns:
Server instance using the requested backend.

Raises:
ImportError: If pure Python backend is requested but not available.

Examples:
>>> # Use default ctypes backend
>>> server = snap7.get_server()

>>> # Use pure Python backend
>>> server = snap7.get_server(pure_python=True)
"""
if pure_python:
if not _PURE_PYTHON_AVAILABLE:
raise ImportError(
"Pure Python server is not available. "
"This may be due to missing dependencies in the native module."
)
return PureServer()
else:
return Server()


# Add to exports
__all__.extend(["get_client", "get_server"])

try:
__version__ = version("python-snap7")
except PackageNotFoundError:
Expand Down
38 changes: 38 additions & 0 deletions snap7/native/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
Pure Python implementation of Snap7 S7 protocol.

This module provides a complete Python implementation of the Siemens S7 protocol,
eliminating the need for the native Snap7 C library and DLL dependencies.

Architecture:
- Application Layer: High-level S7 client API
- S7 Protocol Layer: S7 PDU encoding/decoding and operations
- ISO on TCP Layer: TPKT/COTP frame handling (RFC 1006)
- Socket Layer: TCP socket connection management
- Platform Layer: Cross-platform compatibility

Components:
- S7Client: Main client interface (drop-in replacement for ctypes version)
- S7Protocol: S7 PDU message encoding/decoding
- ISOTCPConnection: ISO on TCP connection management
- S7DataTypes: S7 data type definitions and conversions
- S7Errors: Error handling and exception mapping
"""

from .client import S7Client
from .protocol import S7Protocol
from .connection import ISOTCPConnection
from .datatypes import S7DataTypes
from .errors import S7Error, S7ConnectionError, S7ProtocolError
from .server import S7Server

__all__ = [
'S7Client',
'S7Server',
'S7Protocol',
'ISOTCPConnection',
'S7DataTypes',
'S7Error',
'S7ConnectionError',
'S7ProtocolError'
]
Loading
Loading