Metadata-Version: 2.4
Name: SELlib
Version: 2.0.0
Summary: Read and write the file formats an SEL protective relay project is made of: RDB, SET_*.TXT, DNP3 maps, GLE logic diagrams, SELOGIC and IEC 61850 SCL
Author: Guilherme Marini
License-Expression: AGPL-3.0-or-later
Project-URL: Homepage, https://github.com/GuilhermeMarini/sellib
Project-URL: Source, https://github.com/GuilhermeMarini/sellib
Keywords: sel,acselerator,rdb,selogic,dnp3,iec61850,scl,protection,relay,commissioning
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Manufacturing
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: olefile>=0.47
Requires-Dist: openpyxl>=3.1
Requires-Dist: cfbwrite>=1.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Requires-Dist: mypy>=1.11; extra == "dev"
Dynamic: license-file

# SELlib

Read and write the file formats an SEL protective relay project is made of —
the ones AcSELerator QuickSet produces.

Nothing here talks to a relay, opens a socket, or knows what a web request is.

> **Renamed.** This library was published as `selfiles` for 1.x. From 2.0.0 it
> is `SELlib`, and the import moved with it — `import sellib`, not
> `import selfiles`. Nothing else changed in 2.0.0; the major is the rename.
> `selfiles` 1.1.1 stays on PyPI and is not maintained.

```python
import sellib
from sellib.rdb import process_upload
from sellib.dnp_map import parse, discover

sellib.configure(user_data_dir="~/.pacct/data", cache_dir="/var/cache/rdb")

info = process_upload(open("substation.rdb", "rb").read(), "substation.rdb")
for relay in info.relays:
    print(relay.name, relay.model, relay.ip)
```

| module | what it reads |
|---|---|
| `sellib.rdb`, `.rdb_cache` | the RDB (an OLE compound database): relays, models, addresses, extracted into a content-addressed cache |
| `sellib.settings` | `SET_*.TXT` relay settings |
| `sellib.dnp_map` | `SET_D<n>.TXT`, the DNP3 point map |
| `sellib.gle` | QuickSet logic diagrams — parse, and render a page to SVG |
| `sellib.selogic` | SELOGIC equations: parse, compare by equivalence, normalise settings |
| `sellib.models` | per-model registries: block conventions, and valid Relay Word names |
| `sellib.scl` | IEC 61850 SCL/SCD: IEDs, GOOSE, VLANs, ExtRefs, functional constraints, SEL `sAddr` |
| `sellib.match` | cross-match an RDB's relays against an SCD's IEDs |
| `sellib.dnp_profile` | SEL DNP3 device profile bundles |

## The one contract to know about

`sellib.dnp_map` guarantees `parse(b).serialize() == b`, byte for byte, for
every `SET_D` in the reference corpus. These bytes go back into a protection
relay: a settings file that round-trips imperfectly is a relay that behaves in
a way nobody asked for. The `0x1C` field separator lives *inside* the line,
before the CRLF, and is preserved literally, along with each model's own
index padding (`BI_1` on a 411L, `BI_00` on a 751).

Writing a Compound File back out — needed whenever an edit grows a stream — is
[`cfbwrite`](https://github.com/GuilhermeMarini/cfbwrite), a separate library.

## Data

The per-model registries ship with the package. `configure(user_data_dir=...)`
adds an overlay searched **first**, per model, so a host can add a relay model
at runtime without shipping a new release and without repeating the rest.

## Install

```bash
pip install sellib
```

Python 3.10+. Extracted from
[PAC CT](https://github.com/GuilhermeMarini/pac-ct), where all of it is
exercised against a real substation corpus.

## Licence

AGPL-3.0-or-later — see [LICENSE](LICENSE).
