Metadata-Version: 2.4
Name: ethernetip-emulator
Version: 0.1
Summary: An Ethernet/IP emulation template built on top of cpppo that exposes a set of tags (as attributes) and supports simple side-effect behaviors (like mirroring tags, background incrementing).
Author-email: Stevie Chryn <srcthird@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/srcthird/ethernetip-emulator
Project-URL: Bug Tracker, https://github.com/srcthird/ethernetip-emulator/issues
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cpppo>=5.2.5
Provides-Extra: dev
Requires-Dist: coverage>=7.13.4; extra == "dev"
Requires-Dist: black>=26.1.0; extra == "dev"
Requires-Dist: OPi.GPIO>=0.5.2; extra == "dev"
Requires-Dist: RPi.GPIO>=0.7.1; extra == "dev"
Dynamic: license-file

# EtherNet/IP Emulator (Python)

ethernetip-emulator is a high level binary communications framework built around [`cpppo`](https://github.com/pjkundert/cpppo) (pronounced ‘c’3*’p’‘o’ in Python) that exposes a set of tags and allows for the programatic execution .

## Architecture

```
.
├───example
│   ├───basic_datatypes
│   │   └─── *.py
│   ├───mfg_line
│   │   └─── *.py
│   ├───orange_pi
│   │   └─── *.py
│   └───raspberry_pi
│       └─── *.py
├───src
│   └───ethernetip_emulator
│       ├───client
│       ├───server
│       │   ├───datatypes
│       │   │   ├───templates
│       │   │   │   └─── *.py
│       │   │   └─── *.py
│       │   └─── *.py
│       └─── *.py
└───tests
    └───server
        ├───datatypes
        │   ├───mock
        │   │   └─── test_*.py
        │   ├───templates
        │   │   └─── test_*.py
        │   └─── test_*.py
        └─── test_*.py
        
```

## Requirements

- Python 3.10+
- `cpppo` (for Ethernet/IP attribute/device behavior)
- `black` | Development Dependency (for code formatting)
- `coverage` | Developer Dependency (for coverage reporting)
- `RPi.GPIO` or `OPi.GPIO` | Optional Dependency (for gpio i/o)

## Getting Started

```bash
pip install ethernetip-emulator
```

A minimal project needs three files:

| File | Purpose |
|---|---|
| `tags.py` | Declare tags with `@tag_registry.register` |
| `actions.py` | React to tag changes with `@actions.<type>.on_change` |
| `__main__.py` | Start the server with `device_controller` |

```python
# __main__.py
from ethernetip_emulator.server.device import AttributeDevice, apidict, device_controller
from ethernetip_emulator.server.tag_specs import tag_registry
import tags    # registers all tags (can also just be imported in __init__.py)
import actions # registers all listeners (can also just be imported in __init__.py)

if __name__ == "__main__":
    server_control = apidict(timeout=1.0)
    AttributeDevice.set_server_control(server_control)

    with AttributeDevice._actions.bind(AttributeDevice):
        device_controller(
            argv=tag_registry.build_argv(base_args=["--print"]),
            attribute_class=AttributeDevice,
            server={"control": server_control},
        )
```

## Guides

### Core

| Guide | Description |
|---|---|
| [Tag Registry](wiki/Defining-Tags-with-tag_registry.register) | Define tags with `@tag_registry.register`, use namespaced prefixes, and organise tags across files |
| [Actions](wiki/Working-with-Actions) | Read and write tags at runtime, register `on_change` listeners, and understand `key` and `defer` |
| [Device](wiki/The-Device-Module) | Start and stop the server, understand how tag writes are intercepted, and apply startup defaults |

### Datatypes

| Guide | Description |
|---|---|
| [Datatypes Overview](wiki/datatypes) | All built-in types at a glance, and a step-by-step guide to creating custom datatypes |
| [Basic Datatypes](wiki/basic-datatypes) | Scalar types: `BOOL`, integers, floats, and strings — shared `get_val` / `set_val` / `on_change` API |
| [Bool Array](wiki/bool-array-datatype) | `BOOLARRAY` — bit-level access, list operations, bulk mutations |
| [Numeric Arrays](wiki/numeric-array-datatypes) | `SINTARRAY` · `USINTARRAY` · `INTARRAY` · `UINTARRAY` · `DINTARRAY` · `UDINTARRAY` · `LINTARRAY` · `ULINTARRAY` |
| [Real Arrays](wiki/real-array-datatypes) | `REALARRAY` · `LREALARRAY` — same API as numeric arrays with floating-point zero tolerance (`1e-9`) |
| [String Array](wiki/string-array-datatype) | `SSTRINGARRAY` — list operations using `""` as the empty slot sentinel |

## Typical Workflow 

```
┌─────────────────────────────────────────────────────┐
│                    Your Project                     │
│                                                     │
│  tags.py ──────► tag_registry                       │
│                      │                              │
│                       ▼                             │
│  datatypes.py ─► actions.datatype                   │
│                      │                              │
│                       ▼                             │
│  __main__.py ──► device_controller                  │
│                      │                              │
│                       ▼                             │
│              AttributeDevice                        │
│           (intercepts PLC writes)                   │
│                      │                              │
│                       ▼                             │
│  actions.py ──► on_change listeners                 │
│                      │                              │
│                       ▼                             │
│              actions.<type>.set_val / get_val       │
└─────────────────────────────────────────────────────┘
```

1. **Tags** are declared once at import time via `@tag_registry.register`.
2. **Custom Datatypes** can be declared once at import time via `@actions.datatype`. Although this is completely optional if you like the built-in datatypes
2. **`device_controller`** builds the tag list and starts the EtherNet/IP server.
3. **`AttributeDevice`** intercepts every PLC write and fires the matching `on_change` listeners.
4. **Listeners** use `actions.<type>` helpers to read state, write back to tags, or trigger application logic.

## Project Layout Convention

```
your_project/
├── __main__.py          # server entrypoint
├── tags.py              # tag definitions
├── actions.py           # on_change handlers
└── datatypes/           # optional: custom datatype classes
    ├── __init__.py
    └── mytype.py
```

For larger projects, split tags and actions into sub-modules and import them from a top-level `__init__.py` so all registrations are triggered before the server starts.

## Supporting the Development of EtherNet/IP Emulator
EtherNet/IP Emulator's development depends on your contributions. Right now it is just me working on this, so any contributions are welcome!

## License

Copyright 2026 Merck KGaA, Darmstadt, Germany and/or its affiliates. All rights reserved.

