mirror of https://github.com/Nonannet/pyhoff.git
Compare commits
10 Commits
418bcb59e8
...
2a794d5a51
Author | SHA1 | Date |
---|---|---|
|
2a794d5a51 | |
|
0c86ae95e6 | |
|
4e1ac7aa5b | |
|
c3a29e0e63 | |
|
49f654993e | |
|
0f5eda6cfc | |
|
6cb067ce43 | |
|
ddec81acaa | |
|
6cb3e216a2 | |
|
e160fd2162 |
7
.flake8
7
.flake8
|
@ -12,9 +12,10 @@ exclude =
|
||||||
__pycache__,
|
__pycache__,
|
||||||
build,
|
build,
|
||||||
dist,
|
dist,
|
||||||
.conda
|
.conda,
|
||||||
.venv
|
.venv,
|
||||||
venv
|
venv,
|
||||||
|
docs/source/api
|
||||||
|
|
||||||
# Enable specific plugins or options
|
# Enable specific plugins or options
|
||||||
# Example: Enabling flake8-docstrings
|
# Example: Enabling flake8-docstrings
|
||||||
|
|
|
@ -27,6 +27,13 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
python -m pip install -e .[dev]
|
python -m pip install -e .[dev]
|
||||||
|
if [ "${{ matrix.python-version }}" = "3.13" ]; then
|
||||||
|
python -m pip install cffconvert
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Validate CITATION.cff
|
||||||
|
if: ${{ matrix.python-version == '3.13' }}
|
||||||
|
run: cffconvert --validate
|
||||||
|
|
||||||
- name: Lint code with flake8
|
- name: Lint code with flake8
|
||||||
run: flake8
|
run: flake8
|
||||||
|
|
|
@ -9,7 +9,7 @@ permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-deploy:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@ -29,8 +29,24 @@ jobs:
|
||||||
rm ./source/*.rst
|
rm ./source/*.rst
|
||||||
make html
|
make html
|
||||||
touch ./build/html/.nojekyll
|
touch ./build/html/.nojekyll
|
||||||
- name: Deploy to GitHub Pages
|
mkdir -p ./build/html/_autogenerated
|
||||||
uses: JamesIves/github-pages-deploy-action@v4
|
cp ./build/html/api/* ./build/html/_autogenerated/
|
||||||
|
- name: Upload artifact
|
||||||
|
uses: actions/upload-pages-artifact@v3
|
||||||
with:
|
with:
|
||||||
branch: gh-pages
|
path: docs/build/html
|
||||||
folder: docs/build/html
|
|
||||||
|
deploy:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
steps:
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v4
|
|
@ -10,6 +10,10 @@ jobs:
|
||||||
name: Build and publish
|
name: Build and publish
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
environment:
|
||||||
|
name: pypi
|
||||||
|
url: https://pypi.org/project/${{ github.event.repository.name }}/
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
|
|
@ -70,7 +70,7 @@ instance/
|
||||||
|
|
||||||
# Sphinx documentation
|
# Sphinx documentation
|
||||||
docs/_build/
|
docs/_build/
|
||||||
docs/source/_autogenerated/
|
docs/source/api/
|
||||||
|
|
||||||
# Autogenerated documentation
|
# Autogenerated documentation
|
||||||
docs/source/modules.md
|
docs/source/modules.md
|
||||||
|
|
15
CITATION.cff
15
CITATION.cff
|
@ -1,15 +1,16 @@
|
||||||
cff-version: 1.2.0
|
cff-version: 1.2.0
|
||||||
|
message: "If you use this software, please cite it as below."
|
||||||
title: pyhoff
|
title: pyhoff
|
||||||
abstract: The pyhoff package allows easy accessing of Beckhoff and Wago terminals with python over ModBus TCP
|
abstract: The pyhoff package allows easy accessing of Beckhoff and Wago terminals with python over ModBus TCP
|
||||||
authors:
|
authors:
|
||||||
- family-names: Kruse
|
- family-names: Kruse
|
||||||
given-names: Nicolas
|
given-names: Nicolas
|
||||||
orcid: "https://orcid.org/0000-0001-6758-2269"
|
orcid: "https://orcid.org/0000-0001-6758-2269"
|
||||||
version: 1.1.0
|
version: v1.1.2
|
||||||
#date-released: "2025-04-01"
|
date-released: "2025-08-04"
|
||||||
#identifiers:
|
identifiers:
|
||||||
# - description: This is the collection of archived snapshots of all versions of My Research Software
|
- description: This is the collection of archived snapshots of all versions of pyhoff
|
||||||
# type: doi
|
type: doi
|
||||||
# value: "10.5281/"
|
value: "10.5281/zenodo.16740202"
|
||||||
license: MIT License
|
license: MIT
|
||||||
repository-code: "https://github.com/Nonannet/pyhoff"
|
repository-code: "https://github.com/Nonannet/pyhoff"
|
77
README.md
77
README.md
|
@ -1,6 +1,5 @@
|
||||||
# Pyhoff
|
# Pyhoff
|
||||||
|
|
||||||
## Description
|
|
||||||
The pyhoff package allows you to read and write the most common
|
The pyhoff package allows you to read and write the most common
|
||||||
Beckhoff and WAGO bus terminals ("Busklemmen") using the Ethernet bus
|
Beckhoff and WAGO bus terminals ("Busklemmen") using the Ethernet bus
|
||||||
coupler ("Busskoppler") BK9000, BK9050, BK9100, or WAGO 750_352
|
coupler ("Busskoppler") BK9000, BK9050, BK9100, or WAGO 750_352
|
||||||
|
@ -55,6 +54,62 @@ bk.select(KL4002, 0).set_voltage(1, 4.2)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Adding new terminals
|
||||||
|
The package comes with automatic generated code stubs for nearly all
|
||||||
|
terminals. These stubs are not tested with hardware but for most
|
||||||
|
digital IO terminals the code should be fully functional.
|
||||||
|
Such a stub looks like this:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# From ./src/pyhoff/devices.py:
|
||||||
|
class KL2442(DigitalOutputTerminal):
|
||||||
|
"""
|
||||||
|
KL2442: 2-channel digital output, 24 V DC, 2 x 4 A/1 x 8 A
|
||||||
|
(Automatic generated stub)
|
||||||
|
"""
|
||||||
|
parameters = {'output_bit_width': 2, 'input_bit_width': 0}
|
||||||
|
```
|
||||||
|
|
||||||
|
For analog IO terminals the stubs are functional as well,
|
||||||
|
but they provide only a generic `read_channel_word` and
|
||||||
|
`read_normalized` function (for inputs) without scaling the
|
||||||
|
values to voltages, currents or temperatures. For better usability
|
||||||
|
they might be extended with functions. Based on the stub the
|
||||||
|
extension could look like this:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from pyhoff.devices import KL3054 as KL3054_stub
|
||||||
|
|
||||||
|
class KL3054(KL3054_stub):
|
||||||
|
def read_current(self, channel: int) -> float:
|
||||||
|
return self.read_normalized(channel) * 16.0 + 4.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Or for contributing to the pyhoff package, the existing stub
|
||||||
|
code can be updated like this:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# From ./src/pyhoff/devices.py:
|
||||||
|
class KL3054(AnalogInputTerminal):
|
||||||
|
"""
|
||||||
|
KL3054: 4x analog input 4...20 mA 12 Bit single-ended
|
||||||
|
"""
|
||||||
|
# Input: 4 x 16 Bit Daten (optional 4x 8 Bit Control/Status)
|
||||||
|
parameters = {'input_word_width': 4}
|
||||||
|
|
||||||
|
def read_current(self, channel: int) -> float:
|
||||||
|
"""
|
||||||
|
Read the current value from a specific channel.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
channel: The channel number to read from.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The current value in mA.
|
||||||
|
"""
|
||||||
|
return self.read_normalized(channel) * 16.0 + 4.0
|
||||||
|
```
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
Other analog and digital IO terminals are easy to complement. Contributions are welcome!
|
Other analog and digital IO terminals are easy to complement. Contributions are welcome!
|
||||||
Please open an issue or submit a pull request on GitHub.
|
Please open an issue or submit a pull request on GitHub.
|
||||||
|
@ -62,32 +117,24 @@ Please open an issue or submit a pull request on GitHub.
|
||||||
## Developer Guide
|
## Developer Guide
|
||||||
To get started with developing the `pyhoff` package, follow these steps:
|
To get started with developing the `pyhoff` package, follow these steps:
|
||||||
|
|
||||||
1. **Clone the Repository**
|
1. First, clone the repository to your local machine using Git:
|
||||||
First, clone the repository to your local machine using Git:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/Nonannet/pyhoff.git
|
git clone https://github.com/Nonannet/pyhoff.git
|
||||||
cd pyhoff
|
cd pyhoff
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Set Up a Virtual Environment**
|
2. It is recommended to use a virtual environment:
|
||||||
It is recommended to use a virtual environment to manage dependencies. You can create one using `venv`:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -m venv venv
|
python -m venv .venv
|
||||||
source venv/bin/activate # On Windows use `venv\Scripts\activate`
|
source .venv/bin/activate # On Windows/Powershell use `.\venv\Scripts\Activate.ps1`
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Install Dev Dependencies**
|
3. Install pyhoff from source plus the development dependencies:
|
||||||
Install pyhoff from source plus the dependencies required for development using `pip`:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install -e .[dev]
|
pip install -e .[dev]
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Run Tests**
|
4. Ensure that everything is set up correctly by running the tests:
|
||||||
Ensure that everything is set up correctly by running the tests:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pytest
|
pytest
|
||||||
```
|
```
|
||||||
|
|
|
@ -24,7 +24,7 @@ def write_classes(f: TextIOWrapper, patterns: list[str], module_name: str, title
|
||||||
write_dochtree(f, title, classes)
|
write_dochtree(f, title, classes)
|
||||||
|
|
||||||
for cls in classes:
|
for cls in classes:
|
||||||
with open(f'docs/source/_autogenerated/{cls}.md', 'w') as f2:
|
with open(f'docs/source/api/{cls}.md', 'w') as f2:
|
||||||
f2.write(f'# {module_name}.{cls}\n')
|
f2.write(f'# {module_name}.{cls}\n')
|
||||||
f2.write('```{eval-rst}\n')
|
f2.write('```{eval-rst}\n')
|
||||||
f2.write(f'.. autoclass:: {module_name}.{cls}\n')
|
f2.write(f'.. autoclass:: {module_name}.{cls}\n')
|
||||||
|
@ -52,9 +52,9 @@ def write_dochtree(f: TextIOWrapper, title: str, items: list[str]):
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Ensure the output directory exists
|
# Ensure the output directory exists
|
||||||
os.makedirs('docs/source/_autogenerated', exist_ok=True)
|
os.makedirs('docs/source/api', exist_ok=True)
|
||||||
|
|
||||||
with open('docs/source/_autogenerated/index.md', 'w') as f:
|
with open('docs/source/api/index.md', 'w') as f:
|
||||||
f.write('# Classes and Modules\n\n')
|
f.write('# Classes and Modules\n\n')
|
||||||
|
|
||||||
write_classes(f, ['BK*', 'WAGO_750_352'], 'pyhoff.devices', title='Bus coupler',
|
write_classes(f, ['BK*', 'WAGO_750_352'], 'pyhoff.devices', title='Bus coupler',
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
```{toctree}
|
```{toctree}
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
:hidden:
|
:hidden:
|
||||||
_autogenerated/index
|
api/index
|
||||||
|
repo
|
||||||
```
|
```
|
||||||
|
|
||||||
```{include} ../../README.md
|
```{include} ../../README.md
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Code repository
|
||||||
|
|
||||||
|
Code repository is on GitHub: [github.com/Nonannet/pyhoff](https://github.com/Nonannet/pyhoff).
|
|
@ -1,6 +1,6 @@
|
||||||
[project]
|
[project]
|
||||||
name = "pyhoff"
|
name = "pyhoff"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
authors = [
|
authors = [
|
||||||
{ name="Nicolas Kruse", email="nicolas.kruse@nonan.net" },
|
{ name="Nicolas Kruse", email="nicolas.kruse@nonan.net" },
|
||||||
]
|
]
|
||||||
|
@ -25,6 +25,9 @@ build-backend = "setuptools.build_meta"
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["src"]
|
where = ["src"]
|
||||||
|
|
||||||
|
[tool.setuptools.package-data]
|
||||||
|
pyhoff = ["py.typed"]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"pytest", "flake8", "mypy"
|
"pytest", "flake8", "mypy"
|
||||||
|
|
|
@ -137,9 +137,10 @@ class AnalogInputTerminal(BusTerminal):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
channel: The channel number (1 based index) to read from.
|
channel: The channel number (1 based index) to read from.
|
||||||
|
error_value: Value that is returned in case the modbus read command fails.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The read word value.
|
The read word value or provided error_value if read failed.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
Exception: If the word offset or count is out of range.
|
Exception: If the word offset or count is out of range.
|
||||||
|
@ -174,6 +175,7 @@ class AnalogOutputTerminal(BusTerminal):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
channel: The channel number (1 based index) to read from.
|
channel: The channel number (1 based index) to read from.
|
||||||
|
error_value: Value that is returned in case the modbus read command fails.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The read word value or provided error_value if read failed.
|
The read word value or provided error_value if read failed.
|
||||||
|
|
Loading…
Reference in New Issue