Compare commits

...

10 Commits

Author SHA1 Message Date
Nicolas 2a794d5a51 cff-version updated to 1.2.0 2025-08-04 21:31:16 +02:00
Nicolas 0c86ae95e6 add DOI to CITATION.cff 2025-08-04 21:23:23 +02:00
Nicolas 4e1ac7aa5b updated version to v1.1.2 2025-08-04 21:11:43 +02:00
Nicolas c3a29e0e63 Readme: "Adding new terminals" section added 2025-08-04 21:11:43 +02:00
Nicolas 49f654993e Change docs path and update publishing method 2025-08-04 21:11:43 +02:00
Nicolas 0f5eda6cfc py.typed file added indication package uses type annotations 2025-08-04 21:11:43 +02:00
Nicolas 6cb067ce43 CITATION.cff fixed and CITATION.cff validation added to CI 2025-08-04 21:11:43 +02:00
Nicolas ddec81acaa readme updated 2025-08-04 21:11:43 +02:00
Nicolas 6cb3e216a2 version number updated 2025-08-04 21:11:43 +02:00
Nicolas e160fd2162 Missing docstrings added 2025-08-04 21:11:43 +02:00
13 changed files with 122 additions and 37 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

2
.gitignore vendored
View File

@ -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

View File

@ -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"

View File

@ -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
``` ```

View File

@ -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',

View File

@ -1,7 +1,8 @@
```{toctree} ```{toctree}
:maxdepth: 1 :maxdepth: 1
:hidden: :hidden:
_autogenerated/index api/index
repo
``` ```
```{include} ../../README.md ```{include} ../../README.md

3
docs/source/repo.md Normal file
View File

@ -0,0 +1,3 @@
# Code repository
Code repository is on GitHub: [github.com/Nonannet/pyhoff](https://github.com/Nonannet/pyhoff).

View File

@ -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"

View File

@ -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.

0
src/pyhoff/py.typed Normal file
View File