s7datablock 0.0.0a0__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.
- s7datablock-0.0.0a0/.coveragerc +28 -0
- s7datablock-0.0.0a0/.github/workflows/ci.yml +91 -0
- s7datablock-0.0.0a0/.gitignore +54 -0
- s7datablock-0.0.0a0/.isort.cfg +3 -0
- s7datablock-0.0.0a0/.pre-commit-config.yaml +54 -0
- s7datablock-0.0.0a0/.readthedocs.yml +27 -0
- s7datablock-0.0.0a0/AUTHORS.md +3 -0
- s7datablock-0.0.0a0/LICENSE.txt +21 -0
- s7datablock-0.0.0a0/PKG-INFO +136 -0
- s7datablock-0.0.0a0/README.md +114 -0
- s7datablock-0.0.0a0/docs/Makefile +29 -0
- s7datablock-0.0.0a0/docs/_static/.gitignore +1 -0
- s7datablock-0.0.0a0/docs/authors.md +4 -0
- s7datablock-0.0.0a0/docs/changelog.md +4 -0
- s7datablock-0.0.0a0/docs/conf.py +299 -0
- s7datablock-0.0.0a0/docs/contributing.md +4 -0
- s7datablock-0.0.0a0/docs/index.md +39 -0
- s7datablock-0.0.0a0/docs/license.md +5 -0
- s7datablock-0.0.0a0/docs/readme.md +4 -0
- s7datablock-0.0.0a0/docs/requirements.txt +6 -0
- s7datablock-0.0.0a0/examples/basic_usage.py +33 -0
- s7datablock-0.0.0a0/examples/dtl_date.py +83 -0
- s7datablock-0.0.0a0/examples/parse_file.py +41 -0
- s7datablock-0.0.0a0/pyproject.toml +65 -0
- s7datablock-0.0.0a0/src/s7datablock/__init__.py +23 -0
- s7datablock-0.0.0a0/src/s7datablock/constants.py +93 -0
- s7datablock-0.0.0a0/src/s7datablock/fields.py +341 -0
- s7datablock-0.0.0a0/src/s7datablock/grammar.py +112 -0
- s7datablock-0.0.0a0/src/s7datablock/mapping.py +207 -0
- s7datablock-0.0.0a0/src/s7datablock/parser.py +83 -0
- s7datablock-0.0.0a0/src/s7datablock/py.typed +0 -0
- s7datablock-0.0.0a0/test_buffer_mapping_extended.py +33 -0
- s7datablock-0.0.0a0/tests/conftest.py +10 -0
- s7datablock-0.0.0a0/tests/definitions/s7_1200_out.db +24 -0
- s7datablock-0.0.0a0/tests/definitions/tester.db +29 -0
- s7datablock-0.0.0a0/tests/test_buffer_mapping.py +43 -0
- s7datablock-0.0.0a0/tests/test_buffer_mapping_extended.py +106 -0
- s7datablock-0.0.0a0/tests/test_dtl_struct.py +58 -0
- s7datablock-0.0.0a0/tests/test_s7_connector.py +52 -0
- s7datablock-0.0.0a0/tests/test_s7_parsing.py +232 -0
- s7datablock-0.0.0a0/tests/test_s7_utils.py +162 -0
- s7datablock-0.0.0a0/uv.lock +284 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# .coveragerc to control coverage.py
|
|
2
|
+
[run]
|
|
3
|
+
branch = True
|
|
4
|
+
source = s7datablock
|
|
5
|
+
# omit = bad_file.py
|
|
6
|
+
|
|
7
|
+
[paths]
|
|
8
|
+
source =
|
|
9
|
+
src/
|
|
10
|
+
*/site-packages/
|
|
11
|
+
|
|
12
|
+
[report]
|
|
13
|
+
# Regexes for lines to exclude from consideration
|
|
14
|
+
exclude_lines =
|
|
15
|
+
# Have to re-enable the standard pragma
|
|
16
|
+
pragma: no cover
|
|
17
|
+
|
|
18
|
+
# Don't complain about missing debug-only code:
|
|
19
|
+
def __repr__
|
|
20
|
+
if self\.debug
|
|
21
|
+
|
|
22
|
+
# Don't complain if tests don't hit defensive assertion code:
|
|
23
|
+
raise AssertionError
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
26
|
+
# Don't complain if non-runnable code isn't run:
|
|
27
|
+
if 0:
|
|
28
|
+
if __name__ == .__main__.:
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# GitHub Actions configuration **EXAMPLE**,
|
|
2
|
+
# MODIFY IT ACCORDING TO YOUR NEEDS!
|
|
3
|
+
# Reference: https://docs.github.com/en/actions
|
|
4
|
+
|
|
5
|
+
name: CI
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
pull_request:
|
|
9
|
+
push:
|
|
10
|
+
branches:
|
|
11
|
+
- "main"
|
|
12
|
+
tags: ['v*'] # Keep tag support for releases
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
pull-requests: write
|
|
16
|
+
contents: write
|
|
17
|
+
id-token: write # Needed for PyPI publishing
|
|
18
|
+
|
|
19
|
+
env:
|
|
20
|
+
UV_CACHE_DIR: /tmp/.uv-cache
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
test:
|
|
24
|
+
strategy:
|
|
25
|
+
matrix:
|
|
26
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
27
|
+
platform: [ubuntu-latest, ]
|
|
28
|
+
# platform: [ubuntu-latest, macos-latest, windows-latest] # Uncomment for cross-platform testing
|
|
29
|
+
runs-on: ${{ matrix.platform }}
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
|
|
33
|
+
- name: Install uv
|
|
34
|
+
uses: astral-sh/setup-uv@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ matrix.python-version }}
|
|
37
|
+
|
|
38
|
+
- name: Restore uv cache
|
|
39
|
+
uses: actions/cache@v4
|
|
40
|
+
with:
|
|
41
|
+
path: ${{ env.UV_CACHE_DIR }}
|
|
42
|
+
key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}
|
|
43
|
+
restore-keys: |
|
|
44
|
+
uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}
|
|
45
|
+
uv-${{ runner.os }}
|
|
46
|
+
|
|
47
|
+
- name: Install dependencies
|
|
48
|
+
run: uv sync --locked --all-extras
|
|
49
|
+
|
|
50
|
+
- name: Run linting
|
|
51
|
+
run: uv run ruff check .
|
|
52
|
+
|
|
53
|
+
- name: Run tests with coverage
|
|
54
|
+
run: uv run pytest --cov s7datablock --cov-report=term-missing
|
|
55
|
+
|
|
56
|
+
- name: Minimize uv cache
|
|
57
|
+
run: uv cache prune --ci
|
|
58
|
+
|
|
59
|
+
build-and-publish:
|
|
60
|
+
needs: test
|
|
61
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
steps:
|
|
64
|
+
- uses: actions/checkout@v4
|
|
65
|
+
|
|
66
|
+
- name: Install uv
|
|
67
|
+
uses: astral-sh/setup-uv@v5
|
|
68
|
+
with:
|
|
69
|
+
python-version: "3.11"
|
|
70
|
+
|
|
71
|
+
- name: Update version from tag
|
|
72
|
+
run: |
|
|
73
|
+
VERSION=${GITHUB_REF#refs/tags/v}
|
|
74
|
+
echo "Setting version to $VERSION"
|
|
75
|
+
sed -i 's/^version = ".*"/version = "'$VERSION'"/' pyproject.toml
|
|
76
|
+
|
|
77
|
+
- name: Build package
|
|
78
|
+
run: |
|
|
79
|
+
uv pip install build
|
|
80
|
+
uv run python -m build
|
|
81
|
+
|
|
82
|
+
- name: Publish to PyPI
|
|
83
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
84
|
+
with:
|
|
85
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
86
|
+
|
|
87
|
+
- name: Create GitHub Release
|
|
88
|
+
uses: softprops/action-gh-release@v2
|
|
89
|
+
with:
|
|
90
|
+
files: dist/*
|
|
91
|
+
prerelease: ${{ contains(github.ref, 'alpha') || contains(github.ref, 'beta') }}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Temporary and binary files
|
|
2
|
+
*~
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.so
|
|
5
|
+
*.cfg
|
|
6
|
+
!.isort.cfg
|
|
7
|
+
!setup.cfg
|
|
8
|
+
*.orig
|
|
9
|
+
*.log
|
|
10
|
+
*.pot
|
|
11
|
+
__pycache__/*
|
|
12
|
+
.cache/*
|
|
13
|
+
.*.swp
|
|
14
|
+
*/.ipynb_checkpoints/*
|
|
15
|
+
.DS_Store
|
|
16
|
+
|
|
17
|
+
# Project files
|
|
18
|
+
.ropeproject
|
|
19
|
+
.project
|
|
20
|
+
.pydevproject
|
|
21
|
+
.settings
|
|
22
|
+
.idea
|
|
23
|
+
.vscode
|
|
24
|
+
tags
|
|
25
|
+
|
|
26
|
+
# Package files
|
|
27
|
+
*.egg
|
|
28
|
+
*.eggs/
|
|
29
|
+
.installed.cfg
|
|
30
|
+
*.egg-info
|
|
31
|
+
|
|
32
|
+
# Unittest and coverage
|
|
33
|
+
htmlcov/*
|
|
34
|
+
.coverage
|
|
35
|
+
.coverage.*
|
|
36
|
+
.tox
|
|
37
|
+
junit*.xml
|
|
38
|
+
coverage.xml
|
|
39
|
+
.pytest_cache/
|
|
40
|
+
|
|
41
|
+
# Build and docs folder/files
|
|
42
|
+
build/*
|
|
43
|
+
dist/*
|
|
44
|
+
sdist/*
|
|
45
|
+
docs/api/*
|
|
46
|
+
docs/_rst/*
|
|
47
|
+
docs/_build/*
|
|
48
|
+
cover/*
|
|
49
|
+
MANIFEST
|
|
50
|
+
|
|
51
|
+
# Per-project virtualenvs
|
|
52
|
+
.venv*/
|
|
53
|
+
.conda*/
|
|
54
|
+
.python-version
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
exclude: |
|
|
2
|
+
(?x)^(
|
|
3
|
+
docs/.*| # exclude docs folder
|
|
4
|
+
tests/definitions/.* # exclude files in the definitions folder
|
|
5
|
+
)$
|
|
6
|
+
|
|
7
|
+
repos:
|
|
8
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
9
|
+
rev: v5.0.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: trailing-whitespace
|
|
12
|
+
- id: check-added-large-files
|
|
13
|
+
- id: check-ast
|
|
14
|
+
- id: check-json
|
|
15
|
+
- id: check-merge-conflict
|
|
16
|
+
- id: check-xml
|
|
17
|
+
- id: check-yaml
|
|
18
|
+
- id: debug-statements
|
|
19
|
+
- id: end-of-file-fixer
|
|
20
|
+
- id: requirements-txt-fixer
|
|
21
|
+
- id: mixed-line-ending
|
|
22
|
+
args: ["--fix=auto"] # replace 'auto' with 'lf' to enforce Linux/Mac line endings or 'crlf' for Windows
|
|
23
|
+
|
|
24
|
+
# If you want to automatically "modernize" your Python code:
|
|
25
|
+
- repo: https://github.com/asottile/pyupgrade
|
|
26
|
+
rev: v3.10.1
|
|
27
|
+
hooks:
|
|
28
|
+
- id: pyupgrade
|
|
29
|
+
args: ["--py311-plus"]
|
|
30
|
+
|
|
31
|
+
- repo: https://github.com/PyCQA/isort
|
|
32
|
+
rev: 5.12.0
|
|
33
|
+
hooks:
|
|
34
|
+
- id: isort
|
|
35
|
+
|
|
36
|
+
- repo: https://github.com/psf/black
|
|
37
|
+
rev: 23.7.0
|
|
38
|
+
hooks:
|
|
39
|
+
- id: black
|
|
40
|
+
language_version: python3
|
|
41
|
+
|
|
42
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
43
|
+
# Ruff version.
|
|
44
|
+
rev: v0.0.284
|
|
45
|
+
hooks:
|
|
46
|
+
- id: ruff
|
|
47
|
+
args: [--fix, --exit-non-zero-on-fix]
|
|
48
|
+
|
|
49
|
+
# Check for misspells in documentation files:
|
|
50
|
+
- repo: https://github.com/codespell-project/codespell
|
|
51
|
+
rev: v2.2.5
|
|
52
|
+
hooks:
|
|
53
|
+
- id: codespell
|
|
54
|
+
args: [-L gud]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Read the Docs configuration file
|
|
2
|
+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
3
|
+
|
|
4
|
+
# Required
|
|
5
|
+
version: 2
|
|
6
|
+
|
|
7
|
+
# Build documentation in the docs/ directory with Sphinx
|
|
8
|
+
sphinx:
|
|
9
|
+
configuration: docs/conf.py
|
|
10
|
+
|
|
11
|
+
# Build documentation with MkDocs
|
|
12
|
+
#mkdocs:
|
|
13
|
+
# configuration: mkdocs.yml
|
|
14
|
+
|
|
15
|
+
# Optionally build your docs in additional formats such as PDF
|
|
16
|
+
formats:
|
|
17
|
+
- pdf
|
|
18
|
+
|
|
19
|
+
build:
|
|
20
|
+
os: ubuntu-22.04
|
|
21
|
+
tools:
|
|
22
|
+
python: "3.11"
|
|
23
|
+
|
|
24
|
+
python:
|
|
25
|
+
install:
|
|
26
|
+
- requirements: docs/requirements.txt
|
|
27
|
+
- {path: ., method: pip}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 CEAD
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: s7datablock
|
|
3
|
+
Version: 0.0.0a0
|
|
4
|
+
Summary: A Python library interacting with Siemens S7 PLC datablocks
|
|
5
|
+
Project-URL: Homepage, https://github.com/pyscaffold/pyscaffold/
|
|
6
|
+
Project-URL: Documentation, https://pyscaffold.org/
|
|
7
|
+
Author-email: Thijs Damsma <thijs@damsma.net>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: AUTHORS.md
|
|
10
|
+
License-File: LICENSE.txt
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Programming Language :: Python
|
|
13
|
+
Requires-Python: >=3.11
|
|
14
|
+
Requires-Dist: pyparsing
|
|
15
|
+
Requires-Dist: python-snap7
|
|
16
|
+
Requires-Dist: rich
|
|
17
|
+
Provides-Extra: testing
|
|
18
|
+
Requires-Dist: pytest; extra == 'testing'
|
|
19
|
+
Requires-Dist: pytest-cov; extra == 'testing'
|
|
20
|
+
Requires-Dist: ruff; extra == 'testing'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# S7DataBlock
|
|
24
|
+
|
|
25
|
+
- [S7DataBlock](#s7datablock)
|
|
26
|
+
- [Introduction](#introduction)
|
|
27
|
+
- [Installation](#installation)
|
|
28
|
+
- [Parsing Data Blocks](#parsing-data-blocks)
|
|
29
|
+
- [Contributing and Issues](#contributing-and-issues)
|
|
30
|
+
|
|
31
|
+
## Introduction
|
|
32
|
+
|
|
33
|
+
> A Python library for parsing Siemens S7 PLC data blocks, in conjunction with Snap7.
|
|
34
|
+
|
|
35
|
+
`s7datablock` is a Python library designed to parse and interpret Siemens S7 PLC data blocks. It works in conjunction with the [python-snap7](https://github.com/gijzelaerr/python-snap7) library, providing a set of tools and utilities to read and write the bytes in a PLC datablock as structured data, using the DB definitions as exported from a TIA Portal project. This makes it easier for developers and engineers to work with Siemens S7 PLCs in their Python applications.
|
|
36
|
+
|
|
37
|
+
Instead of manually mapping bytes, the dataformat defined in Tia Portal can be used to generate the appropriate Python mapping. The configuration file can be kept separate from the code, making it easier to track versions and support complicated data structures.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install s7datablock
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Parsing Data Blocks
|
|
46
|
+
|
|
47
|
+
In this example, we will demonstrate how to use the `s7datablock` library to parse and interpret Siemens S7 PLC data blocks.
|
|
48
|
+
|
|
49
|
+
First, we define a s7_1200_out user-defined type (UDT) and a data block in Structured Control Language (SCL). The UDT, named "s7_1200_out_udt", consists of five boolean variables and one integer variable. The data block, named "s7_1200_out", uses this UDT. This file was defined in and exported from Tia Portal, using the `generate source from blocks, including all dependent blocks` option.
|
|
50
|
+
|
|
51
|
+
```scl
|
|
52
|
+
# s7_1200_out.db
|
|
53
|
+
TYPE "s7_1200_out_udt"
|
|
54
|
+
VERSION : 0.1
|
|
55
|
+
STRUCT
|
|
56
|
+
PLC_DQ_0 : Bool; # Boolean variable
|
|
57
|
+
PLC_DQ_1 : Bool; # Boolean variable
|
|
58
|
+
PLC_DQ_2 : Bool; # Boolean variable
|
|
59
|
+
PLC_DQ_3 : Bool; # Boolean variable
|
|
60
|
+
PLC_DQ_4 : Bool; # Boolean variable
|
|
61
|
+
SB_AQ_0 : Int; # Integer variable
|
|
62
|
+
END_STRUCT;
|
|
63
|
+
END_TYPE
|
|
64
|
+
|
|
65
|
+
DATA_BLOCK "s7_1200_out"
|
|
66
|
+
{ S7_Optimized_Access := 'FALSE' }
|
|
67
|
+
VERSION : 0.1
|
|
68
|
+
NON_RETAIN
|
|
69
|
+
"s7_1200_out_udt"
|
|
70
|
+
|
|
71
|
+
BEGIN
|
|
72
|
+
|
|
73
|
+
END_DATA_BLOCK
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Next, we use the `S7DataBlock` class from the `s7datablock` library to parse the data block definition file. We then display the parsed data block and its initial buffer state. Finally, we modify one of the boolean variables in the data block and display the updated buffer state.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# Import necessary modules
|
|
80
|
+
from s7datablock.mapping import S7DataBlock
|
|
81
|
+
from pathlib import Path
|
|
82
|
+
|
|
83
|
+
# Parse the data block definition file
|
|
84
|
+
db1200 = S7DataBlock.from_definition_file(
|
|
85
|
+
Path("tests/definitions/s7_1200_out.db"),
|
|
86
|
+
db_number=1200
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# The printing the db1200 datasrutcure produces a nicely formatted output
|
|
90
|
+
print(db1200)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```raw
|
|
94
|
+
Data Block Structure
|
|
95
|
+
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┓
|
|
96
|
+
┃ Name ┃ Data type ┃ Offset ┃ Value ┃
|
|
97
|
+
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━┩
|
|
98
|
+
│ PLC_DQ_0 │ Bool │ 0.0 │ False │
|
|
99
|
+
│ PLC_DQ_1 │ Bool │ 0.1 │ False │
|
|
100
|
+
│ PLC_DQ_2 │ Bool │ 0.2 │ False │
|
|
101
|
+
│ PLC_DQ_3 │ Bool │ 0.3 │ False │
|
|
102
|
+
│ PLC_DQ_4 │ Bool │ 0.4 │ False │
|
|
103
|
+
│ SB_AQ_0 │ Int │ 2.0 │ 0 │
|
|
104
|
+
│ TIMEFIELD │ DTL │ 4.0 │ None │
|
|
105
|
+
│ YEAR │ UInt │ 4.0 │ 0 │
|
|
106
|
+
│ MONTH │ USInt │ 6.0 │ 0 │
|
|
107
|
+
│ DAY │ USInt │ 7.0 │ 0 │
|
|
108
|
+
│ WEEKDAY │ USInt │ 8.0 │ 0 │
|
|
109
|
+
│ HOUR │ USInt │ 9.0 │ 0 │
|
|
110
|
+
│ MINUTE │ USInt │ 10.0 │ 0 │
|
|
111
|
+
│ SECOND │ USInt │ 11.0 │ 0 │
|
|
112
|
+
│ NANOSECOND │ UDInt │ 12.0 │ 0 │
|
|
113
|
+
└──────────────────┴─────────────┴──────────┴─────────────┘
|
|
114
|
+
Total size: 16 bytes
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
# db1200 maps the data like a dictionary but the hood it stores the data in a continuous buffer,
|
|
119
|
+
# using the same format and byte ordering as the PLC
|
|
120
|
+
# Initially the buffer is only zeros
|
|
121
|
+
print(db1200.buffer)
|
|
122
|
+
# Output: bytearray(b'\x00\x00\x00\x00')
|
|
123
|
+
|
|
124
|
+
# Modify one of the boolean variables
|
|
125
|
+
db1200["PLC_DQ_3"] = True
|
|
126
|
+
|
|
127
|
+
# Now one bit has changed
|
|
128
|
+
print(db1200.buffer)
|
|
129
|
+
# Output: bytearray(b'\x08\x00\x00\x00')
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
## Contributing and Issues
|
|
135
|
+
|
|
136
|
+
The library is in its early stages, and contributions are welcome. Feel free to submit a pull request or open an issue on GitHub.
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# S7DataBlock
|
|
2
|
+
|
|
3
|
+
- [S7DataBlock](#s7datablock)
|
|
4
|
+
- [Introduction](#introduction)
|
|
5
|
+
- [Installation](#installation)
|
|
6
|
+
- [Parsing Data Blocks](#parsing-data-blocks)
|
|
7
|
+
- [Contributing and Issues](#contributing-and-issues)
|
|
8
|
+
|
|
9
|
+
## Introduction
|
|
10
|
+
|
|
11
|
+
> A Python library for parsing Siemens S7 PLC data blocks, in conjunction with Snap7.
|
|
12
|
+
|
|
13
|
+
`s7datablock` is a Python library designed to parse and interpret Siemens S7 PLC data blocks. It works in conjunction with the [python-snap7](https://github.com/gijzelaerr/python-snap7) library, providing a set of tools and utilities to read and write the bytes in a PLC datablock as structured data, using the DB definitions as exported from a TIA Portal project. This makes it easier for developers and engineers to work with Siemens S7 PLCs in their Python applications.
|
|
14
|
+
|
|
15
|
+
Instead of manually mapping bytes, the dataformat defined in Tia Portal can be used to generate the appropriate Python mapping. The configuration file can be kept separate from the code, making it easier to track versions and support complicated data structures.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install s7datablock
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Parsing Data Blocks
|
|
24
|
+
|
|
25
|
+
In this example, we will demonstrate how to use the `s7datablock` library to parse and interpret Siemens S7 PLC data blocks.
|
|
26
|
+
|
|
27
|
+
First, we define a s7_1200_out user-defined type (UDT) and a data block in Structured Control Language (SCL). The UDT, named "s7_1200_out_udt", consists of five boolean variables and one integer variable. The data block, named "s7_1200_out", uses this UDT. This file was defined in and exported from Tia Portal, using the `generate source from blocks, including all dependent blocks` option.
|
|
28
|
+
|
|
29
|
+
```scl
|
|
30
|
+
# s7_1200_out.db
|
|
31
|
+
TYPE "s7_1200_out_udt"
|
|
32
|
+
VERSION : 0.1
|
|
33
|
+
STRUCT
|
|
34
|
+
PLC_DQ_0 : Bool; # Boolean variable
|
|
35
|
+
PLC_DQ_1 : Bool; # Boolean variable
|
|
36
|
+
PLC_DQ_2 : Bool; # Boolean variable
|
|
37
|
+
PLC_DQ_3 : Bool; # Boolean variable
|
|
38
|
+
PLC_DQ_4 : Bool; # Boolean variable
|
|
39
|
+
SB_AQ_0 : Int; # Integer variable
|
|
40
|
+
END_STRUCT;
|
|
41
|
+
END_TYPE
|
|
42
|
+
|
|
43
|
+
DATA_BLOCK "s7_1200_out"
|
|
44
|
+
{ S7_Optimized_Access := 'FALSE' }
|
|
45
|
+
VERSION : 0.1
|
|
46
|
+
NON_RETAIN
|
|
47
|
+
"s7_1200_out_udt"
|
|
48
|
+
|
|
49
|
+
BEGIN
|
|
50
|
+
|
|
51
|
+
END_DATA_BLOCK
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Next, we use the `S7DataBlock` class from the `s7datablock` library to parse the data block definition file. We then display the parsed data block and its initial buffer state. Finally, we modify one of the boolean variables in the data block and display the updated buffer state.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
# Import necessary modules
|
|
58
|
+
from s7datablock.mapping import S7DataBlock
|
|
59
|
+
from pathlib import Path
|
|
60
|
+
|
|
61
|
+
# Parse the data block definition file
|
|
62
|
+
db1200 = S7DataBlock.from_definition_file(
|
|
63
|
+
Path("tests/definitions/s7_1200_out.db"),
|
|
64
|
+
db_number=1200
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# The printing the db1200 datasrutcure produces a nicely formatted output
|
|
68
|
+
print(db1200)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
```raw
|
|
72
|
+
Data Block Structure
|
|
73
|
+
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┓
|
|
74
|
+
┃ Name ┃ Data type ┃ Offset ┃ Value ┃
|
|
75
|
+
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━┩
|
|
76
|
+
│ PLC_DQ_0 │ Bool │ 0.0 │ False │
|
|
77
|
+
│ PLC_DQ_1 │ Bool │ 0.1 │ False │
|
|
78
|
+
│ PLC_DQ_2 │ Bool │ 0.2 │ False │
|
|
79
|
+
│ PLC_DQ_3 │ Bool │ 0.3 │ False │
|
|
80
|
+
│ PLC_DQ_4 │ Bool │ 0.4 │ False │
|
|
81
|
+
│ SB_AQ_0 │ Int │ 2.0 │ 0 │
|
|
82
|
+
│ TIMEFIELD │ DTL │ 4.0 │ None │
|
|
83
|
+
│ YEAR │ UInt │ 4.0 │ 0 │
|
|
84
|
+
│ MONTH │ USInt │ 6.0 │ 0 │
|
|
85
|
+
│ DAY │ USInt │ 7.0 │ 0 │
|
|
86
|
+
│ WEEKDAY │ USInt │ 8.0 │ 0 │
|
|
87
|
+
│ HOUR │ USInt │ 9.0 │ 0 │
|
|
88
|
+
│ MINUTE │ USInt │ 10.0 │ 0 │
|
|
89
|
+
│ SECOND │ USInt │ 11.0 │ 0 │
|
|
90
|
+
│ NANOSECOND │ UDInt │ 12.0 │ 0 │
|
|
91
|
+
└──────────────────┴─────────────┴──────────┴─────────────┘
|
|
92
|
+
Total size: 16 bytes
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
# db1200 maps the data like a dictionary but the hood it stores the data in a continuous buffer,
|
|
97
|
+
# using the same format and byte ordering as the PLC
|
|
98
|
+
# Initially the buffer is only zeros
|
|
99
|
+
print(db1200.buffer)
|
|
100
|
+
# Output: bytearray(b'\x00\x00\x00\x00')
|
|
101
|
+
|
|
102
|
+
# Modify one of the boolean variables
|
|
103
|
+
db1200["PLC_DQ_3"] = True
|
|
104
|
+
|
|
105
|
+
# Now one bit has changed
|
|
106
|
+
print(db1200.buffer)
|
|
107
|
+
# Output: bytearray(b'\x08\x00\x00\x00')
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## Contributing and Issues
|
|
113
|
+
|
|
114
|
+
The library is in its early stages, and contributions are welcome. Feel free to submit a pull request or open an issue on GitHub.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Makefile for Sphinx documentation
|
|
2
|
+
#
|
|
3
|
+
|
|
4
|
+
# You can set these variables from the command line, and also
|
|
5
|
+
# from the environment for the first two.
|
|
6
|
+
SPHINXOPTS ?=
|
|
7
|
+
SPHINXBUILD ?= sphinx-build
|
|
8
|
+
SOURCEDIR = .
|
|
9
|
+
BUILDDIR = _build
|
|
10
|
+
AUTODOCDIR = api
|
|
11
|
+
|
|
12
|
+
# User-friendly check for sphinx-build
|
|
13
|
+
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $?), 1)
|
|
14
|
+
$(error "The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://sphinx-doc.org/")
|
|
15
|
+
endif
|
|
16
|
+
|
|
17
|
+
.PHONY: help clean Makefile
|
|
18
|
+
|
|
19
|
+
# Put it first so that "make" without argument is like "make help".
|
|
20
|
+
help:
|
|
21
|
+
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
22
|
+
|
|
23
|
+
clean:
|
|
24
|
+
rm -rf $(BUILDDIR)/* $(AUTODOCDIR)
|
|
25
|
+
|
|
26
|
+
# Catch-all target: route all unknown targets to Sphinx using the new
|
|
27
|
+
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
28
|
+
%: Makefile
|
|
29
|
+
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Empty directory
|