py61850 0.0.1__tar.gz → 0.2.0.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py61850-0.2.0.dev1/.gitignore +24 -0
- py61850-0.2.0.dev1/CLA.md +75 -0
- py61850-0.2.0.dev1/COMMERCIAL.md +66 -0
- py61850-0.2.0.dev1/CONTRIBUTING.md +67 -0
- py61850-0.2.0.dev1/LICENSE +661 -0
- py61850-0.2.0.dev1/PKG-INFO +280 -0
- py61850-0.2.0.dev1/README.md +253 -0
- py61850-0.2.0.dev1/ROADMAP.md +213 -0
- py61850-0.2.0.dev1/examples/01_connect_and_scan.py +43 -0
- py61850-0.2.0.dev1/examples/02_read_values.py +73 -0
- py61850-0.2.0.dev1/examples/03_file_transfer.py +76 -0
- py61850-0.2.0.dev1/examples/04_error_handling.py +60 -0
- py61850-0.2.0.dev1/examples/05_fleet_inventory.py +59 -0
- py61850-0.2.0.dev1/examples/06_find_files.py +114 -0
- py61850-0.2.0.dev1/examples/07_read_object_references.py +129 -0
- py61850-0.2.0.dev1/examples/README.md +38 -0
- py61850-0.2.0.dev1/pyproject.toml +54 -0
- py61850-0.2.0.dev1/src/py61850/__init__.py +91 -0
- py61850-0.2.0.dev1/src/py61850/__main__.py +18 -0
- py61850-0.2.0.dev1/src/py61850/cli/__init__.py +15 -0
- py61850-0.2.0.dev1/src/py61850/cli/__main__.py +18 -0
- py61850-0.2.0.dev1/src/py61850/cli/files.py +324 -0
- py61850-0.2.0.dev1/src/py61850/cli/main.py +56 -0
- py61850-0.2.0.dev1/src/py61850/cli/scan.py +239 -0
- py61850-0.2.0.dev1/src/py61850/core/__init__.py +22 -0
- py61850-0.2.0.dev1/src/py61850/core/ber.py +159 -0
- py61850-0.2.0.dev1/src/py61850/core/data.py +158 -0
- py61850-0.2.0.dev1/src/py61850/core/quality.py +52 -0
- py61850-0.2.0.dev1/src/py61850/core/time.py +53 -0
- py61850-0.2.0.dev1/src/py61850/errors.py +53 -0
- py61850-0.2.0.dev1/src/py61850/goose/__init__.py +28 -0
- py61850-0.2.0.dev1/src/py61850/link/__init__.py +89 -0
- py61850-0.2.0.dev1/src/py61850/mms/__init__.py +20 -0
- py61850-0.2.0.dev1/src/py61850/mms/client.py +83 -0
- py61850-0.2.0.dev1/src/py61850/mms/pdu.py +370 -0
- py61850-0.2.0.dev1/src/py61850/mms/service_error.py +44 -0
- py61850-0.2.0.dev1/src/py61850/mms/services/__init__.py +19 -0
- py61850-0.2.0.dev1/src/py61850/mms/services/directory.py +194 -0
- py61850-0.2.0.dev1/src/py61850/mms/services/files.py +256 -0
- py61850-0.2.0.dev1/src/py61850/mms/services/read.py +118 -0
- py61850-0.2.0.dev1/src/py61850/mms/types.py +72 -0
- py61850-0.2.0.dev1/src/py61850/osi/__init__.py +18 -0
- py61850-0.2.0.dev1/src/py61850/osi/acse.py +44 -0
- py61850-0.2.0.dev1/src/py61850/osi/cotp.py +216 -0
- py61850-0.2.0.dev1/src/py61850/osi/oids.py +21 -0
- py61850-0.2.0.dev1/src/py61850/osi/presentation.py +86 -0
- py61850-0.2.0.dev1/src/py61850/osi/session.py +64 -0
- py61850-0.2.0.dev1/src/py61850/osi/stack.py +62 -0
- py61850-0.2.0.dev1/src/py61850/osi/tpkt.py +39 -0
- py61850-0.2.0.dev1/src/py61850/py.typed +0 -0
- py61850-0.2.0.dev1/src/py61850/scl/__init__.py +23 -0
- py61850-0.2.0.dev1/src/py61850/sv/__init__.py +23 -0
- py61850-0.2.0.dev1/tests/BENCH.md +121 -0
- py61850-0.2.0.dev1/tests/README.md +42 -0
- py61850-0.2.0.dev1/tests/__init__.py +19 -0
- py61850-0.2.0.dev1/tests/fixtures/README.md +30 -0
- py61850-0.2.0.dev1/tests/unit/__init__.py +7 -0
- py61850-0.2.0.dev1/tests/unit/test_ber.py +134 -0
- py61850-0.2.0.dev1/tests/unit/test_client.py +483 -0
- py61850-0.2.0.dev1/tests/unit/test_data.py +158 -0
- py61850-0.2.0.dev1/tests/unit/test_file_filter.py +86 -0
- py61850-0.2.0.dev1/tests/unit/test_logical_nodes.py +102 -0
- py61850-0.2.0.dev1/tests/unit/test_osi.py +210 -0
- py61850-0.2.0.dev1/tests/unit/test_pdu.py +234 -0
- py61850-0.2.0.dev1/tests/unit/test_public_api.py +75 -0
- py61850-0.2.0.dev1/tests/unit/test_types.py +102 -0
- py61850-0.0.1/PKG-INFO +0 -15
- py61850-0.0.1/README.md +0 -5
- py61850-0.0.1/pyproject.toml +0 -16
- py61850-0.0.1/src/py61850/__init__.py +0 -5
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
*.egg-info/
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
.venv/
|
|
7
|
+
venv/
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
downloads/
|
|
10
|
+
relay_files/
|
|
11
|
+
inventory.csv
|
|
12
|
+
|
|
13
|
+
# SCL / IED capability files (vendor-supplied, large, not redistributable)
|
|
14
|
+
*.icd
|
|
15
|
+
*.ICD
|
|
16
|
+
*.cid
|
|
17
|
+
*.CID
|
|
18
|
+
*.scd
|
|
19
|
+
*.SCD
|
|
20
|
+
*.iid
|
|
21
|
+
*.IID
|
|
22
|
+
*.sed
|
|
23
|
+
*.SED
|
|
24
|
+
fixtures/ICD/
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Contributor Licence Agreement
|
|
2
|
+
|
|
3
|
+
py61850 is dual-licensed: AGPL-3.0-or-later for everyone, plus commercial
|
|
4
|
+
licences sold by the copyright holder (see [COMMERCIAL.md](COMMERCIAL.md)).
|
|
5
|
+
|
|
6
|
+
That second half only works if the project owns, or is permitted to relicense,
|
|
7
|
+
**every line** in it. A patch contributed under the AGPL alone could never be
|
|
8
|
+
included in a commercially-licensed build — it would have to be stripped out, or
|
|
9
|
+
the whole arrangement collapses. So contributions are accepted only under this
|
|
10
|
+
agreement.
|
|
11
|
+
|
|
12
|
+
> **This document has not been reviewed by a lawyer.** It follows the shape of
|
|
13
|
+
> the Apache Individual Contributor Licence Agreement, which is widely used and
|
|
14
|
+
> widely understood, but if you are contributing on behalf of an employer, or
|
|
15
|
+
> anything substantial is at stake, have your own counsel read it first.
|
|
16
|
+
|
|
17
|
+
## Agreement
|
|
18
|
+
|
|
19
|
+
By submitting a contribution to py61850 (a pull request, a patch, a code
|
|
20
|
+
suggestion in an issue), you agree to the following, for that contribution and
|
|
21
|
+
every future one, unless you say otherwise in writing:
|
|
22
|
+
|
|
23
|
+
1. **You own what you are giving.** The contribution is your original work, and
|
|
24
|
+
you have the right to submit it. If your employer has rights to work you
|
|
25
|
+
produce, you have their permission to contribute it, or they have waived
|
|
26
|
+
those rights in writing.
|
|
27
|
+
|
|
28
|
+
2. **Copyright licence.** You grant Guilherme Marini, and recipients of software
|
|
29
|
+
distributed by him, a perpetual, worldwide, non-exclusive, royalty-free,
|
|
30
|
+
irrevocable copyright licence to reproduce, prepare derivative works of,
|
|
31
|
+
publicly display, publicly perform, sublicense and distribute your
|
|
32
|
+
contribution and such derivative works.
|
|
33
|
+
|
|
34
|
+
3. **Right to relicense.** You agree that your contribution may be licensed by
|
|
35
|
+
the copyright holder under **any terms, including proprietary commercial
|
|
36
|
+
terms**, alongside its release under the AGPL. This is the clause that keeps
|
|
37
|
+
the commercial channel legally possible.
|
|
38
|
+
|
|
39
|
+
4. **Patent licence.** You grant the same parties a perpetual, worldwide,
|
|
40
|
+
non-exclusive, royalty-free, irrevocable patent licence to make, use, sell,
|
|
41
|
+
offer to sell, import and otherwise transfer your contribution, covering only
|
|
42
|
+
those patent claims you can license that are necessarily infringed by your
|
|
43
|
+
contribution alone or by its combination with py61850. If you bring patent
|
|
44
|
+
litigation alleging that py61850 or a contribution to it infringes a patent,
|
|
45
|
+
the patent licences granted to you under this agreement end on the day you
|
|
46
|
+
file.
|
|
47
|
+
|
|
48
|
+
5. **You keep your copyright.** This agreement grants licences; it does not
|
|
49
|
+
assign ownership. Your name stays on your work and you remain free to use
|
|
50
|
+
your own contribution however you like, elsewhere.
|
|
51
|
+
|
|
52
|
+
6. **No warranty.** You provide your contribution "as is", without warranty of
|
|
53
|
+
any kind, and you are not obliged to provide support for it.
|
|
54
|
+
|
|
55
|
+
7. **Third-party material.** If a contribution includes work you do not own —
|
|
56
|
+
an algorithm transcribed from a paper, code from another project, anything
|
|
57
|
+
under a separate licence — say so plainly in the pull request, with its
|
|
58
|
+
source and its licence, marked `[third-party]`. It will be evaluated
|
|
59
|
+
separately. Do not fold it in silently: a single AGPL-incompatible or
|
|
60
|
+
unattributed fragment is the kind of thing that has to be found and removed
|
|
61
|
+
years later, from every release.
|
|
62
|
+
|
|
63
|
+
## Signing it
|
|
64
|
+
|
|
65
|
+
Add this line to a commit message in your pull request, with your real name and
|
|
66
|
+
the e-mail address on the commit:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
py61850-CLA-1.0 signed-off-by: Your Full Name <you@example.com>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
One signature covers all your future contributions to py61850. If you cannot
|
|
73
|
+
agree to these terms, you are still welcome to open issues, report bugs, send
|
|
74
|
+
recorded PDU captures for `tests/fixtures/`, and discuss design — none of that
|
|
75
|
+
requires a CLA.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Commercial licensing
|
|
2
|
+
|
|
3
|
+
py61850 is dual-licensed. You may use it under **either**:
|
|
4
|
+
|
|
5
|
+
1. the **GNU Affero General Public License v3 or later** ([LICENSE](LICENSE)) —
|
|
6
|
+
free of charge, for any purpose, including commercially; or
|
|
7
|
+
2. a **commercial licence** from the copyright holder, on negotiated terms.
|
|
8
|
+
|
|
9
|
+
Everything here describes option 2. Nothing on this page changes the AGPL: if
|
|
10
|
+
the AGPL suits you, take it and go — you owe nothing and need not ask.
|
|
11
|
+
|
|
12
|
+
## Which one do you need?
|
|
13
|
+
|
|
14
|
+
You can use py61850 under the **AGPL, for free**, if:
|
|
15
|
+
|
|
16
|
+
- you use it **inside your own organisation** and publish nothing. Internal
|
|
17
|
+
commissioning tools, engineering scripts, a substation HMI your own staff use
|
|
18
|
+
— the AGPL's obligations are triggered by conveying software to others, not
|
|
19
|
+
by using it. A utility running py61850 on its own network owes nothing.
|
|
20
|
+
- you **distribute** software built on it and are willing to release that whole
|
|
21
|
+
work under the AGPL, source included, to the people you distribute it to.
|
|
22
|
+
- you **host** a service built on it and are willing to offer the complete
|
|
23
|
+
source of that service to its users (AGPL section 13).
|
|
24
|
+
|
|
25
|
+
You need a **commercial licence** if:
|
|
26
|
+
|
|
27
|
+
- you want to **ship a closed-source product** that embeds or imports py61850 —
|
|
28
|
+
an HMI, a test set, a gateway, a configuration tool, a vendor SCADA package;
|
|
29
|
+
- you want to **run a hosted or SaaS service** built on py61850 without
|
|
30
|
+
publishing its source to your users;
|
|
31
|
+
- you need it under terms the AGPL does not offer — a warranty, an indemnity,
|
|
32
|
+
liability limits your own contracts require, or the ability to sublicense.
|
|
33
|
+
|
|
34
|
+
Selling is allowed under the AGPL. What the AGPL does not allow is keeping the
|
|
35
|
+
source closed. That is the line the commercial licence crosses.
|
|
36
|
+
|
|
37
|
+
## What a commercial licence covers
|
|
38
|
+
|
|
39
|
+
Terms are negotiated per case; they typically settle:
|
|
40
|
+
|
|
41
|
+
- **Scope** — which products, which sites, how many installations or devices.
|
|
42
|
+
- **Fee** — a one-off, an annual subscription, per-installation, or a share of
|
|
43
|
+
the revenue of the product py61850 goes into.
|
|
44
|
+
- **Duration and updates** — which versions the licence covers, and for how long.
|
|
45
|
+
- **Support** — whether any is included, and at what response time.
|
|
46
|
+
- **Warranty and liability** — the AGPL disclaims both; a commercial agreement
|
|
47
|
+
can allocate them.
|
|
48
|
+
|
|
49
|
+
## Getting one
|
|
50
|
+
|
|
51
|
+
Open an issue at
|
|
52
|
+
<https://github.com/GuilhermeMarini/py61850/issues> saying what you want to
|
|
53
|
+
build, or write to the copyright holder directly:
|
|
54
|
+
|
|
55
|
+
> **Contact:** _add your licensing e-mail address here before publishing._
|
|
56
|
+
|
|
57
|
+
Please describe the product, the deployment shape (shipped software, hosted
|
|
58
|
+
service, embedded device) and the rough scale. That is enough for a first
|
|
59
|
+
answer on terms.
|
|
60
|
+
|
|
61
|
+
## Ownership
|
|
62
|
+
|
|
63
|
+
The copyright holder is Guilherme Marini, who owns all of py61850 and is
|
|
64
|
+
therefore able to license it under terms other than the AGPL. Contributions are
|
|
65
|
+
accepted under the [CLA](CLA.md) for exactly this reason: code the project does
|
|
66
|
+
not own cannot be included in a commercial licence.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Contributing to py61850
|
|
2
|
+
|
|
3
|
+
Bug reports, recorded PDU captures from real IEDs, and design discussion are
|
|
4
|
+
welcome with no paperwork at all. Code needs one extra step — see *The CLA*
|
|
5
|
+
below, and please read it before you write the patch rather than after.
|
|
6
|
+
|
|
7
|
+
## Before you open a pull request
|
|
8
|
+
|
|
9
|
+
py61850 is **dual-licensed** (AGPL-3.0-or-later, plus commercial licences sold
|
|
10
|
+
by the copyright holder — see [COMMERCIAL.md](COMMERCIAL.md)). For that to hold,
|
|
11
|
+
the project has to be able to relicense every line it ships, so **every code
|
|
12
|
+
contribution must be signed off under the [CLA](CLA.md)**:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
py61850-CLA-1.0 signed-off-by: Your Full Name <you@example.com>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
in a commit message on the pull request. One signature covers everything you
|
|
19
|
+
contribute afterwards. A PR without it cannot be merged, however good the patch
|
|
20
|
+
— not a judgement on the code, just a licence the project cannot grant onward.
|
|
21
|
+
|
|
22
|
+
Contributions that need **no** CLA: issues, bug reports, feature requests,
|
|
23
|
+
questions, and recorded PDUs for `tests/fixtures/` (data captured from a device,
|
|
24
|
+
not authored code).
|
|
25
|
+
|
|
26
|
+
## What makes a good patch here
|
|
27
|
+
|
|
28
|
+
The repository conventions worth knowing before you start; the ones that come
|
|
29
|
+
up most:
|
|
30
|
+
|
|
31
|
+
- **Standard library only.** No runtime dependencies, ever. That is the reason
|
|
32
|
+
this library installs on a locked-down engineering laptop, and it is not
|
|
33
|
+
negotiable without discussion first.
|
|
34
|
+
- **`core/` and `mms/pdu.py` do no I/O.** No sockets, no `open()`. The codec is
|
|
35
|
+
the same code an MMS *server* runs in the other direction, and code without
|
|
36
|
+
sockets is testable offline. `mms/client.py` and `osi/cotp.py` are the only
|
|
37
|
+
places a socket belongs.
|
|
38
|
+
- **`link/`, `goose/`, `sv/` stay out of the `py61850` import graph.** Raw
|
|
39
|
+
Ethernet needs `CAP_NET_RAW` and Linux; the MMS client must keep installing
|
|
40
|
+
and running unprivileged on any OS. `tests/unit/test_public_api.py` asserts it.
|
|
41
|
+
- **New service groups are mixins** in `mms/services/`, composed in
|
|
42
|
+
`mms/client.py` — not subclasses of `MmsClient`, which does not compose past
|
|
43
|
+
one group.
|
|
44
|
+
- **Errors derive from `Iec61850Error`**, so a caller can catch the whole family
|
|
45
|
+
at once. Do not raise bare `OSError`/`ValueError` from a new failure path.
|
|
46
|
+
- **The library never prints.** User-facing output belongs in `cli/`.
|
|
47
|
+
- **Tests come with the code.** A codec change belongs in `tests/unit/`; a
|
|
48
|
+
client change can be driven through the `FakeTransport` in
|
|
49
|
+
`tests/unit/test_client.py`. The suite opens no sockets and must stay that way.
|
|
50
|
+
- **New source files carry the SPDX header** the existing files carry.
|
|
51
|
+
|
|
52
|
+
## Verifying before you push
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
python -m unittest discover -s tests -t . # offline; no relay needed
|
|
56
|
+
python -m compileall -q src tests
|
|
57
|
+
python -c "import py61850 as m; print(m.__all__)"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
If a change can only really be confirmed against hardware, add an entry to
|
|
61
|
+
[tests/BENCH.md](tests/BENCH.md) saying what to run and what a pass looks like,
|
|
62
|
+
rather than leaving it implicit.
|
|
63
|
+
|
|
64
|
+
## Commit messages
|
|
65
|
+
|
|
66
|
+
Imperative mood ("Add file write service", not "Added"). Concise subject line;
|
|
67
|
+
use the body for the *why* when it is not obvious.
|