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.
Files changed (70) hide show
  1. py61850-0.2.0.dev1/.gitignore +24 -0
  2. py61850-0.2.0.dev1/CLA.md +75 -0
  3. py61850-0.2.0.dev1/COMMERCIAL.md +66 -0
  4. py61850-0.2.0.dev1/CONTRIBUTING.md +67 -0
  5. py61850-0.2.0.dev1/LICENSE +661 -0
  6. py61850-0.2.0.dev1/PKG-INFO +280 -0
  7. py61850-0.2.0.dev1/README.md +253 -0
  8. py61850-0.2.0.dev1/ROADMAP.md +213 -0
  9. py61850-0.2.0.dev1/examples/01_connect_and_scan.py +43 -0
  10. py61850-0.2.0.dev1/examples/02_read_values.py +73 -0
  11. py61850-0.2.0.dev1/examples/03_file_transfer.py +76 -0
  12. py61850-0.2.0.dev1/examples/04_error_handling.py +60 -0
  13. py61850-0.2.0.dev1/examples/05_fleet_inventory.py +59 -0
  14. py61850-0.2.0.dev1/examples/06_find_files.py +114 -0
  15. py61850-0.2.0.dev1/examples/07_read_object_references.py +129 -0
  16. py61850-0.2.0.dev1/examples/README.md +38 -0
  17. py61850-0.2.0.dev1/pyproject.toml +54 -0
  18. py61850-0.2.0.dev1/src/py61850/__init__.py +91 -0
  19. py61850-0.2.0.dev1/src/py61850/__main__.py +18 -0
  20. py61850-0.2.0.dev1/src/py61850/cli/__init__.py +15 -0
  21. py61850-0.2.0.dev1/src/py61850/cli/__main__.py +18 -0
  22. py61850-0.2.0.dev1/src/py61850/cli/files.py +324 -0
  23. py61850-0.2.0.dev1/src/py61850/cli/main.py +56 -0
  24. py61850-0.2.0.dev1/src/py61850/cli/scan.py +239 -0
  25. py61850-0.2.0.dev1/src/py61850/core/__init__.py +22 -0
  26. py61850-0.2.0.dev1/src/py61850/core/ber.py +159 -0
  27. py61850-0.2.0.dev1/src/py61850/core/data.py +158 -0
  28. py61850-0.2.0.dev1/src/py61850/core/quality.py +52 -0
  29. py61850-0.2.0.dev1/src/py61850/core/time.py +53 -0
  30. py61850-0.2.0.dev1/src/py61850/errors.py +53 -0
  31. py61850-0.2.0.dev1/src/py61850/goose/__init__.py +28 -0
  32. py61850-0.2.0.dev1/src/py61850/link/__init__.py +89 -0
  33. py61850-0.2.0.dev1/src/py61850/mms/__init__.py +20 -0
  34. py61850-0.2.0.dev1/src/py61850/mms/client.py +83 -0
  35. py61850-0.2.0.dev1/src/py61850/mms/pdu.py +370 -0
  36. py61850-0.2.0.dev1/src/py61850/mms/service_error.py +44 -0
  37. py61850-0.2.0.dev1/src/py61850/mms/services/__init__.py +19 -0
  38. py61850-0.2.0.dev1/src/py61850/mms/services/directory.py +194 -0
  39. py61850-0.2.0.dev1/src/py61850/mms/services/files.py +256 -0
  40. py61850-0.2.0.dev1/src/py61850/mms/services/read.py +118 -0
  41. py61850-0.2.0.dev1/src/py61850/mms/types.py +72 -0
  42. py61850-0.2.0.dev1/src/py61850/osi/__init__.py +18 -0
  43. py61850-0.2.0.dev1/src/py61850/osi/acse.py +44 -0
  44. py61850-0.2.0.dev1/src/py61850/osi/cotp.py +216 -0
  45. py61850-0.2.0.dev1/src/py61850/osi/oids.py +21 -0
  46. py61850-0.2.0.dev1/src/py61850/osi/presentation.py +86 -0
  47. py61850-0.2.0.dev1/src/py61850/osi/session.py +64 -0
  48. py61850-0.2.0.dev1/src/py61850/osi/stack.py +62 -0
  49. py61850-0.2.0.dev1/src/py61850/osi/tpkt.py +39 -0
  50. py61850-0.2.0.dev1/src/py61850/py.typed +0 -0
  51. py61850-0.2.0.dev1/src/py61850/scl/__init__.py +23 -0
  52. py61850-0.2.0.dev1/src/py61850/sv/__init__.py +23 -0
  53. py61850-0.2.0.dev1/tests/BENCH.md +121 -0
  54. py61850-0.2.0.dev1/tests/README.md +42 -0
  55. py61850-0.2.0.dev1/tests/__init__.py +19 -0
  56. py61850-0.2.0.dev1/tests/fixtures/README.md +30 -0
  57. py61850-0.2.0.dev1/tests/unit/__init__.py +7 -0
  58. py61850-0.2.0.dev1/tests/unit/test_ber.py +134 -0
  59. py61850-0.2.0.dev1/tests/unit/test_client.py +483 -0
  60. py61850-0.2.0.dev1/tests/unit/test_data.py +158 -0
  61. py61850-0.2.0.dev1/tests/unit/test_file_filter.py +86 -0
  62. py61850-0.2.0.dev1/tests/unit/test_logical_nodes.py +102 -0
  63. py61850-0.2.0.dev1/tests/unit/test_osi.py +210 -0
  64. py61850-0.2.0.dev1/tests/unit/test_pdu.py +234 -0
  65. py61850-0.2.0.dev1/tests/unit/test_public_api.py +75 -0
  66. py61850-0.2.0.dev1/tests/unit/test_types.py +102 -0
  67. py61850-0.0.1/PKG-INFO +0 -15
  68. py61850-0.0.1/README.md +0 -5
  69. py61850-0.0.1/pyproject.toml +0 -16
  70. 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.